Media Streaming over Websockets

Requesting streaming using Dial Command

The requesting dial command can be extended in the following way to request streaming using WebSockets:

Copy
Copied
curl -X POST \
  --header "Content-Type: application/json" \
  --header "Accept: application/json" \
  --header "Authorization: Bearer YOUR_API_KEY" \
  --data '{"connection_id": "uuid", "to": "+18005550199", "from": "+18005550100", "stream_url": "wss://yourdomain.com", “stream_track”:”inbound_track|outbound_track|both_tracks”}' \
  https://api.telnyx.com/v2/calls

Note: After pasting the above content, Kindly check and remove any new line added

The following additional attributes need to be added to the request:

  • stream_url - the destination address when the stream is going to be delivered
  • stream_track - specifies which track should be streamed, with possible options:
  • inbound_track (default)
  • outbound_track
  • both_tracks

As a response, a regular confirmation is sent:

Copy
Copied
{
  "data": {
    "call_control_id": "v2:T02llQxIyaRkhfRKxgAP8nY511EhFLizdvdUKJiSw8d6A9BborherQ",
    "call_leg_id": "2dc6fc34-f9e0-11ea-b68e-02420a0f7768",
    "call_session_id": "2dc1b3c8-f9e0-11ea-bc5a-02420a0f7768",
    "is_alive": false,
    "record_type": "call"
  }
}

Note: After pasting the above content, Kindly check and remove any new line added

Requesting streaming using Answer Command

Using the same attributes as above, streaming can be requested while answering the call:

Copy
Copied
curl -X POST \
  --header "Content-Type: application/json" \
  --header "Accept: application/json" \
  --header "Authorization: Bearer YOUR_API_KEY" \
  --data '{"client_state":"aGF2ZSBhIG5pY2UgZGF5ID1d","command_id":"891510ac-f3e4-11e8-af5b-de00688a4901","stream_url": "wss://yourdomain.com", “stream_track”:”inbound_track|outbound_track|both_tracks”}' \
  https://api.telnyx.com/v2/calls/{call_control_id}/actions/answer

Note: After pasting the above content, Kindly check and remove any new line added

In this case, confirmation that the call has been answered includes streaming details:

Copy
Copied
{
  "data": {
    "event_type": "call.answered",
    "id": "0ccc7b54-4df3-4bca-a65a-3da1ecc777f0",
    "occurred_at": "2018-02-02T22:25:27.521992Z",
    "payload": {
      "call_control_id":     "v2:T02llQxIyaRkhfRKxgAP8nY511EhFLizdvdUKJiSw8d6A9BborherQ",
      "call_leg_id": "428c31b6-7af4-4bcb-b7f5-5013ef9657c1",
      "call_session_id": "428c31b6-abf3-3bc1-b7f4-5013ef9657c1",
      "client_state": "aGF2ZSBhIG5pY2UgZGF5ID1d",
      "connection_id": "7267xxxxxxxxxxxxxx",
      "from": "+35319605860",
      "state": "answered",
      "stream_url": "wss://yourdomain.com",
      "stream_track”:”inbound_track|outbound_track|both_tracks",
      "to": "+13129457420"
    },
    "record_type": "event"
  }
}

Note: After pasting the above content, Kindly check and remove any new line added

Streaming process flow

When the WebSocket connection is established, the following event is being sent:

Copy
Copied
{ 
  "event": "connected", 
  "version": "1.0.0"
}

Note: After pasting the above content, Kindly check and remove any new line added

Before the stream begins, the streaming.started webhook is sent:

Copy
Copied
{
  "data": {
   "event_type": "streaming.started",
   "id": "0ccc7b54-4df3-4bca-a65a-3da1ecc777f0",
   "occurred_at": "2018-02-02T22:25:27.521992Z",
   "payload": {
     "call_control_id": "v2:T02llQxIyaRkhfRKxgAP8nY511EhFLizdvdUKJiSw8d6A9BborherQ",
     "stream_url": "wss://yourdomain.com"
    },
  "record_type": "event"
  }
}

Note: After pasting the above content, Kindly check and remove any new line added

An event over WebSockets which contains information about the encoding mediaFormat section and stream_id that identifies a particular stream:

Copy
Copied
{ 
 "event": "start",  
 "sequence_number": "1", 
 "start": {
   "user_id": "3E6F995F-85F7-4705-9741-53B116D28237", 
   "call_control_id": "v2:T02llQxIyaRkhfRKxgAP8nY511EhFLizdvdUKJiSw8d6A9BborherQ", 
   "media_format": { 
     "encoding": "PCMU",
     "sample_rate": 8000, 
     "channels": 1 
   } 
 },
 "stream_id": "32DE0DEA-53CB-4B21-89A4-9E1819C043BC"
}

Note: After pasting the above content, Kindly check and remove any new line added

The start event is followed by the following media events:

Copy
Copied
{ 
 "event": "media",
 "sequence_number": "4",
 "media": { 
   "track": "inbound/outbound", 
   "chunk": "2",
   "timestamp": "5",
   "payload": "no+JhoaJjpzSHxAKBgYJDhtEopGKh4aIjZm7JhILBwYIDRg1qZSLh4aIjJevLBUMBwYHDBUsr5eMiIaHi5SpNRgNCAYHCxImu5mNiIaHipGiRBsOCQYGChAf0pyOiYaGiY+e/x4PCQYGCQ4cUp+QioaGiY6bxCIRCgcGCA0ZO6aSi4eGiI2YtSkUCwcGCAwXL6yVjIeGh4yVrC8XDAgGBwsUKbWYjYiGh4uSpjsZDQgGBwoRIsSbjomGhoqQn1IcDgkGBgkPHv+ej4mGhomOnNIfEAoGBgkOG0SikYqHhoiNmbsmEgsHBggNGDWplIuHhoiMl68sFQwHBgcMFSyvl4yIhoeLlKk1GA0IBgcLEia7mY2IhoeKkaJEGw4JBgYKEB/SnI6JhoaJj57/Hg8JBgYJDhxSn5CKhoaJjpvEIhEKBwYIDRk7ppKLh4aIjZi1KRQLBwYIDBcvrJWMh4aHjJWsLxcMCAYHCxQptZiNiIaHi5KmOxkNCAYHChEixJuOiYaGipCfUhwOCQYGCQ8e/56PiYaGiY6c0h8QCgYGCQ4bRKKRioeGiI2ZuyYSCwcGCA0YNamUi4eGiIyXrywVDAcGBwwVLK+XjIiGh4uUqTUYDQgGBwsSJruZjYiGh4qRokQbDgkGBgoQH9KcjomGhomPnv8eDwkGBgkOHFKfkIqGhomOm8QiEQoHBggNGTumkouHhoiNmLUpFAsHBggMFy+slYyHhoeMlawvFwwIBgcLFCm1mI2IhoeLkqY7GQ0IBgcKESLEm46JhoaKkJ9SHA4JBgYJDx7/no+JhoaJjpzSHxAKBgYJDhtEopGKh4aIjZm7JhILBwYIDRg1qZSLh4aIjJevLBUMBwYHDBUsr5eMiIaHi5SpNRgNCAYHCxImu5mNiIaHipGiRBsOCQYGChAf0pyOiYaGiY+e/x4PCQYGCQ4cUp+QioaGiY6bxCIRCgcGCA0ZO6aSi4eGiI2YtSkUCwcGCAwXL6yVjIeGh4yVrC8XDAgGBwsUKbWYjYiGh4uSpjsZDQgGBwoRIsSbjomGhoqQn1Ic"                        
 },
 "stream_id": "32DE0DEA-53CB-4B21-89A4-9E1819C043BC" 
}

Note: After pasting the above content, Kindly check and remove any new line added

The payload contains a base64-encoded RTP packet.

Please be note that the order of events is not guaranteed and the chunk number can be used to reorder the events.

When the call ends, the streaming.stopped webhook is sent:

Copy
Copied
{
  "data": {
    "event_type": "streaming.stopped",
    "id": "0ccc7b54-4df3-4bca-a65a-3da1ecc777f0",
    "occurred_at": "2018-02-02T22:25:27.521992Z",
    "payload": {
      "call_control_id": "v2:T02llQxIyaRkhfRKxgAP8nY511EhFLizdvdUKJiSw8d6A9BborherQ",
      "stream_url": "wss://yourdomain.com"
    },
    "record_type": "event"
  }
}

Note: After pasting the above content, Kindly check and remove any new line added

And the stop event over WebSockets connection is sent:

Copy
Copied
{ 
 "event": "stop",
 "sequence_number": "5",
 "stop": {
  "user_id": "3E6F995F-85F7-4705-9741-53B116D28237",
  "call_control_id": "v2:T02llQxIyaRkhfRKxgAP8nY511EhFLizdvdUKJiSw8d6A9BborherQ"
  },
  "stream_id": "32DE0DEA-53CB-4B21-89A4-9E1819C043BC" 
}

Note: After pasting the above content, Kindly check and remove any new line added

Bidirectional Media Streaming

Media can also be sent back to the call through the websocket. This is done in a similar way to the playback_start command, when using a base64 encoded mp3 file in the payload. Simply send a packet to the websocket connection as follows:

Copy
Copied
{
  "event": "media",
  "media": {
    "payload" : "your base64 encoded mp3 file"
  }
}

Note: After pasting the above content, Kindly check and remove any new line added

The payload, which is a base64-encoded mp3 file, will be played on the call.

Some limitations to be aware of:

  • Media payloads can only be submitted once per second.
  • Media must be base64 encoded mp3

Please note that at the moment only one streaming/fork operation is supported per call. In case of requesting media forking the WebSocket stream will be stopped and replaced by an RTP connection.