Add spec for MSC2746 (#1511)
* Change version field to a string And add the notes on how the version field works. * Add spec requiring tracks to be within streams. * Put streams spec in its own section * Add 'invitee' field * Add party_id * Remember how JSON works * Add m.call.select_answer * Update examples * Add select_answer to call flow example diagram * Add m.call.reject * Make party_id required in other events * Add possible ways for client to handle an invite * Convert hangup & reject events to YAML So we can have a bulleted list in the description for the values of 'reason'. * Add new reason codes to hangup & reject * Add m.call.negotiate * Add other sections * Revert changes to package lock * Typos * Fix type of other version fields, fix anchor. * Add newsfragment * Fix reason in hangup/reject * Change tense Co-authored-by: Hubert Chathi <hubertc@matrix.org> * Tense, typos & grammar Co-authored-by: Hubert Chathi <hubertc@matrix.org> * Linkify Co-authored-by: Hubert Chathi <hubertc@matrix.org> * Remove unnecessary parts from link Co-authored-by: Hubert Chathi <hubertc@matrix.org> * Capitalise Co-authored-by: Hubert Chathi <hubertc@matrix.org> * Fix hangup reasons * Clarify who can answer Co-authored-by: Hubert Chathi <hubertc@matrix.org> * Linkify Co-authored-by: Hubert Chathi <hubertc@matrix.org> * Remove reference to 'this MSC'. * Move common VoIP fields into a call event type. * Move common voip events to the content, not the actual event * Remove reason from reject event I confused myself, but it's not in the MSC and it shouldn't be. * Failure to YAML * Fix number of room members allowed when sending voip events. Co-authored-by: Hubert Chathi <hubertc@matrix.org> * Add 'added in' version Co-authored-by: Hubert Chathi <hubertc@matrix.org> * Another added-in Co-authored-by: Hubert Chathi <hubertc@matrix.org> * Add missing comma --------- Co-authored-by: Hubert Chathi <hubertc@matrix.org> Co-authored-by: Travis Ralston <travisr@matrix.org>
This commit is contained in:
parent
17ebdf7c86
commit
4fd9f39966
17 changed files with 426 additions and 69 deletions
|
@ -2,7 +2,8 @@
|
|||
"$ref": "core/room_event.json",
|
||||
"type": "m.call.answer",
|
||||
"content": {
|
||||
"version" : 0,
|
||||
"version" : "1",
|
||||
"party_id": "67890",
|
||||
"call_id": "12345",
|
||||
"answer": {
|
||||
"type" : "answer",
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
"$ref": "core/room_event.json",
|
||||
"type": "m.call.candidates",
|
||||
"content": {
|
||||
"version" : 0,
|
||||
"version" : "1",
|
||||
"party_id": "67890",
|
||||
"call_id": "12345",
|
||||
"candidates": [
|
||||
{
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
"$ref": "core/room_event.json",
|
||||
"type": "m.call.hangup",
|
||||
"content": {
|
||||
"version" : 0,
|
||||
"call_id": "12345"
|
||||
"version" : "1",
|
||||
"party_id": "67890",
|
||||
"call_id": "12345",
|
||||
"reason": "user_hangup"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
"$ref": "core/room_event.json",
|
||||
"type": "m.call.invite",
|
||||
"content": {
|
||||
"version" : 0,
|
||||
"version" : "1",
|
||||
"party_id": "67890",
|
||||
"call_id": "12345",
|
||||
"lifetime": 60000,
|
||||
"offer": {
|
||||
|
|
14
data/event-schemas/examples/m.call.negotiate.yaml
Normal file
14
data/event-schemas/examples/m.call.negotiate.yaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"$ref": "core/room_event.json",
|
||||
"type": "m.call.negotiate",
|
||||
"content": {
|
||||
"version" : "1",
|
||||
"party_id": "67890",
|
||||
"call_id": "12345",
|
||||
"lifetime": 10000,
|
||||
"offer": {
|
||||
"type" : "offer",
|
||||
"sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]"
|
||||
}
|
||||
}
|
||||
}
|
9
data/event-schemas/examples/m.call.reject.yaml
Normal file
9
data/event-schemas/examples/m.call.reject.yaml
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"$ref": "core/room_event.json",
|
||||
"type": "m.call.reject",
|
||||
"content": {
|
||||
"version" : "1",
|
||||
"party_id": "67890",
|
||||
"call_id": "12345"
|
||||
}
|
||||
}
|
10
data/event-schemas/examples/m.call.select_answer.yaml
Normal file
10
data/event-schemas/examples/m.call.select_answer.yaml
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"$ref": "core/room_event.json",
|
||||
"type": "m.call.select_answer",
|
||||
"content": {
|
||||
"version" : "1",
|
||||
"call_id": "12345",
|
||||
"party_id": "67890",
|
||||
"selected_party_id": "111213"
|
||||
}
|
||||
}
|
25
data/event-schemas/schema/core-event-schema/call_event.yaml
Normal file
25
data/event-schemas/schema/core-event-schema/call_event.yaml
Normal file
|
@ -0,0 +1,25 @@
|
|||
description: "The content of all call events shares a set of common fields: those
|
||||
of room events and some additional VoIP specific fields."
|
||||
properties:
|
||||
call_id:
|
||||
type: string
|
||||
description: The ID of the call this event relates to.
|
||||
version:
|
||||
type: string
|
||||
description: The version of the VoIP specification this message adheres to.
|
||||
This specification is version 1. This field is a string such that experimental
|
||||
implementations can use non-integer versions. This field was an integer
|
||||
in the previous spec version and implementations must accept an integer
|
||||
0.
|
||||
party_id:
|
||||
type: string
|
||||
description: 'This identifies the party that sent this event. A client may
|
||||
choose to re-use the device ID from end-to-end cryptography for the value
|
||||
of this field.'
|
||||
x-addedInMatrixVersion: "1.7"
|
||||
required:
|
||||
- call_id
|
||||
- version
|
||||
- party_id
|
||||
title: CallEvent
|
||||
type: object
|
|
@ -7,11 +7,10 @@
|
|||
"properties": {
|
||||
"content": {
|
||||
"type": "object",
|
||||
"allOf": [{
|
||||
"$ref": "core-event-schema/call_event.yaml"
|
||||
}],
|
||||
"properties": {
|
||||
"call_id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the call this event relates to."
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
"title": "Answer",
|
||||
|
@ -28,13 +27,9 @@
|
|||
}
|
||||
},
|
||||
"required": ["type", "sdp"]
|
||||
},
|
||||
"version": {
|
||||
"type": "number",
|
||||
"description": "The version of the VoIP specification this messages adheres to. This specification is version 0."
|
||||
}
|
||||
},
|
||||
"required": ["call_id", "answer", "version"]
|
||||
"required": ["answer"]
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
|
|
|
@ -7,11 +7,10 @@
|
|||
"properties": {
|
||||
"content": {
|
||||
"type": "object",
|
||||
"allOf": [{
|
||||
"$ref": "core-event-schema/call_event.yaml"
|
||||
}],
|
||||
"properties": {
|
||||
"call_id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the call this event relates to."
|
||||
},
|
||||
"candidates": {
|
||||
"type": "array",
|
||||
"description": "Array of objects describing the candidates.",
|
||||
|
@ -34,13 +33,9 @@
|
|||
},
|
||||
"required": ["candidate", "sdpMLineIndex", "sdpMid"]
|
||||
}
|
||||
},
|
||||
"version": {
|
||||
"type": "integer",
|
||||
"description": "The version of the VoIP specification this messages adheres to. This specification is version 0."
|
||||
}
|
||||
},
|
||||
"required": ["call_id", "candidates", "version"]
|
||||
"required": ["candidates"]
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
|
|
|
@ -1,35 +1,54 @@
|
|||
{
|
||||
"type": "object",
|
||||
"description": "Sent by either party to signal their termination of the call. This can be sent either once the call has has been established or before to abort the call.",
|
||||
"allOf": [{
|
||||
"$ref": "core-event-schema/room_event.yaml"
|
||||
}],
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"call_id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the call this event relates to."
|
||||
},
|
||||
"version": {
|
||||
"type": "integer",
|
||||
"description": "The version of the VoIP specification this message adheres to. This specification is version 0."
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "Optional error reason for the hangup. This should not be provided when the user naturally ends or rejects the call. When there was an error in the call negotiation, this should be `ice_failed` for when ICE negotiation fails or `invite_timeout` for when the other party did not answer in time.",
|
||||
"enum": [
|
||||
"ice_failed",
|
||||
"invite_timeout"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": ["call_id", "version"]
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["m.call.hangup"]
|
||||
}
|
||||
}
|
||||
}
|
||||
---
|
||||
type: object
|
||||
description: |
|
||||
Sent by either party to signal their termination of the call. This can
|
||||
be sent either once the call has has been established or before to abort the call.
|
||||
|
||||
The meanings of the `reason` field are as follows:
|
||||
* `ice_failed`: ICE negotiation has failed and a media connection could not be established.
|
||||
* `ice_timeout`: The connection failed after some media was exchanged (as opposed to `ice_failed`
|
||||
which means no media connection could be established). Note that, in the case of an ICE
|
||||
renegotiation, a client should be sure to send `ice_timeout` rather than `ice_failed` if media
|
||||
had previously been received successfully, even if the ICE renegotiation itself failed.
|
||||
* `invite_timeout`: The other party did not answer in time.
|
||||
* `user_hangup`: Clients must now send this code when the user chooses to end the call, although
|
||||
for backwards compatibility with version 0, a clients should treat an absence of the `reason`
|
||||
field as `user_hangup`.
|
||||
* `user_media_failed`: The client was unable to start capturing media in such a way that it is unable
|
||||
to continue the call.
|
||||
* `user_busy`: The user is busy. Note that this exists primarily for bridging to other networks such
|
||||
as the PSTN. A Matrix client that receives a call whilst already in a call would not generally reject
|
||||
the new call unless the user had specifically chosen to do so.
|
||||
* `unknown_error`: Some other failure occurred that meant the client was unable to continue the call
|
||||
rather than the user choosing to end it.
|
||||
allOf:
|
||||
- "$ref": core-event-schema/room_event.yaml
|
||||
properties:
|
||||
content:
|
||||
type: object
|
||||
allOf:
|
||||
- "$ref": core-event-schema/call_event.yaml
|
||||
properties:
|
||||
reason:
|
||||
type: string
|
||||
description: Reason for the hangup. Note that this was optional in
|
||||
previous previous versions of the spec, so a missing value should be
|
||||
treated as `user_hangup`.
|
||||
x-changedInMatrixVersion:
|
||||
1.7: |-
|
||||
Additional values were added.
|
||||
enum:
|
||||
- ice_timeout
|
||||
- ice_failed
|
||||
- invite_timeout
|
||||
- user_hangup
|
||||
- user_media_failed
|
||||
- user_busy
|
||||
- unknown_error
|
||||
required:
|
||||
- reason
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- m.call.hangup
|
||||
|
||||
|
|
|
@ -7,11 +7,10 @@
|
|||
"properties": {
|
||||
"content": {
|
||||
"type": "object",
|
||||
"allOf": [{
|
||||
"$ref": "core-event-schema/call_event.yaml"
|
||||
}],
|
||||
"properties": {
|
||||
"call_id": {
|
||||
"type": "string",
|
||||
"description": "A unique identifier for the call."
|
||||
},
|
||||
"offer": {
|
||||
"type": "object",
|
||||
"title": "Offer",
|
||||
|
@ -29,16 +28,17 @@
|
|||
},
|
||||
"required": ["type", "sdp"]
|
||||
},
|
||||
"version": {
|
||||
"type": "integer",
|
||||
"description": "The version of the VoIP specification this message adheres to. This specification is version 0."
|
||||
},
|
||||
"lifetime": {
|
||||
"type": "integer",
|
||||
"description": "The time in milliseconds that the invite is valid for. Once the invite age exceeds this value, clients should discard it. They should also no longer show the call as awaiting an answer in the UI."
|
||||
},
|
||||
"invitee": {
|
||||
"type": "string",
|
||||
"description": "The ID of the user being called. If omitted, any user in the room can answer.",
|
||||
"x-addedInMatrixVersion": "1.7",
|
||||
}
|
||||
},
|
||||
"required": ["call_id", "offer", "version", "lifetime"]
|
||||
"required": ["offer", "lifetime"]
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
|
|
74
data/event-schemas/schema/m.call.negotiate.yaml
Normal file
74
data/event-schemas/schema/m.call.negotiate.yaml
Normal file
|
@ -0,0 +1,74 @@
|
|||
---
|
||||
type: object
|
||||
description: |
|
||||
Provides SDP negotiation semantics for media pause, hold/resume, ICE restarts
|
||||
and voice/video call up/downgrading. Clients should implement and honour hold
|
||||
functionality as per [WebRTC's recommendation](https://www.w3.org/TR/webrtc/#hold-functionality).
|
||||
|
||||
If both the invite event and the accepted answer event have `version` equal
|
||||
to `"1"`, either party may send `m.call.negotiate` with a `description` field
|
||||
to offer new SDP to the other party. This event has `call_id` with the ID of
|
||||
the call and `party_id` equal to the client's party ID for that call. The
|
||||
caller ignores any negotiate events with `party_id` + `user_id` tuple not
|
||||
equal to that of the answer it accepted and the callee ignores any negotiate
|
||||
events with `party_id` + `user_id` tuple not equal to that of the caller.
|
||||
Clients should use the `party_id` field to ignore the remote echo of their
|
||||
own negotiate events.
|
||||
|
||||
This has a `lifetime` field as in `m.call.invite`, after which the sender of
|
||||
the negotiate event should consider the negotiation failed (timed out) and
|
||||
the recipient should ignore it.
|
||||
|
||||
The `description` field is the same as the `offer` field in `m.call.invite`
|
||||
and `answer` field in `m.call.answer` and is an `RTCSessionDescriptionInit`
|
||||
object as per https://www.w3.org/TR/webrtc/#dom-rtcsessiondescriptioninit.
|
||||
|
||||
Once an `m.call.negotiate` event is received, the client must respond with
|
||||
another `m.call.negotiate` event, with the SDP answer (with `"type": "answer"`)
|
||||
in the `description` property.
|
||||
|
||||
In the `m.call.invite` and `m.call.answer` events, the `offer` and `answer`
|
||||
fields respectively are objects of type `RTCSessionDescriptionInit`. Hence
|
||||
the `type` field, whilst redundant in these events, is included for ease of
|
||||
working with the WebRTC API and is mandatory. Receiving clients should not
|
||||
attempt to validate the `type` field, but simply pass the object into the
|
||||
WebRTC API.
|
||||
x-addedInMatrixVersion: "1.7"
|
||||
allOf:
|
||||
- "$ref": core-event-schema/room_event.yaml
|
||||
properties:
|
||||
content:
|
||||
type: object
|
||||
allOf:
|
||||
- "$ref": core-event-schema/call_event.yaml
|
||||
properties:
|
||||
offer:
|
||||
type: object
|
||||
title: Offer
|
||||
description: The session description object
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- offer
|
||||
description: The type of session description.
|
||||
sdp:
|
||||
type: string
|
||||
description: The SDP text of the session description.
|
||||
required:
|
||||
- type
|
||||
- sdp
|
||||
lifetime:
|
||||
type: integer
|
||||
description: The time in milliseconds that the invite is valid for.
|
||||
Once the invite age exceeds this value, clients should discard it.
|
||||
They should also no longer show the call as awaiting an answer in the
|
||||
UI.
|
||||
required:
|
||||
- offer
|
||||
- lifetime
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- m.call.negotiate
|
||||
|
28
data/event-schemas/schema/m.call.reject.yaml
Normal file
28
data/event-schemas/schema/m.call.reject.yaml
Normal file
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
type: object
|
||||
description: |
|
||||
If the `m.call.invite` event has `version` `"1"`, a client wishing to
|
||||
reject the call sends an `m.call.reject` event. This rejects the call on all devices,
|
||||
but if the calling device sees an `answer` before the `reject`, it disregards the
|
||||
reject event and carries on. The reject has a `party_id` just like an answer, and
|
||||
the caller sends a `select_answer` for it just like an answer. If another client
|
||||
had already sent an answer and sees the caller select the reject response instead
|
||||
of its answer, it ends the call. If the `m.call.invite` event has `version` `0`,
|
||||
the callee sends an `m.call.hangup` event. If the calling user chooses to end the
|
||||
call before setup is complete, the client sends `m.call.hangup` as previously.
|
||||
|
||||
Note that, unlike `m.call.hangup`, this event has no `reason` field: the rejection of
|
||||
a call is always implicitly because the user chose not to answer it.
|
||||
x-addedInMatrixVersion: "1.7"
|
||||
allOf:
|
||||
- "$ref": core-event-schema/room_event.yaml
|
||||
properties:
|
||||
content:
|
||||
type: object
|
||||
allOf:
|
||||
- "$ref": core-event-schema/call_event.yaml
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- m.call.reject
|
||||
|
27
data/event-schemas/schema/m.call.select_answer.yaml
Normal file
27
data/event-schemas/schema/m.call.select_answer.yaml
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"type": "object",
|
||||
"description": "This event is sent by the caller's client once it has decided which other client to talk to, by selecting one of multiple possible incoming `m.call.answer` events. Its `selected_party_id` field indicates the answer it's chosen. The `call_id` and `party_id` of the caller is also included. If the callee's client sees a `select_answer` for an answer with party ID other than the one it sent, it ends the call and informs the user the call was answered elsewhere. It does not send any events. Media can start flowing before this event is seen or even sent. Clients that implement previous versions of this specification will ignore this event and behave as they did before.",
|
||||
"x-addedInMatrixVersion": "1.7",
|
||||
"allOf": [{
|
||||
"$ref": "core-event-schema/room_event.yaml"
|
||||
}],
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "object",
|
||||
"allOf": [{
|
||||
"$ref": "core-event-schema/call_event.yaml"
|
||||
}],
|
||||
"properties": {
|
||||
"selected_party_id": {
|
||||
"type": "string",
|
||||
"description": "The `party_id` field from the answer event that the caller chose."
|
||||
},
|
||||
},
|
||||
"required": ["selected_party_id"]
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["m.call.select_answer"]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue