Skip to contentSkip to navigationSkip to topbar
Rate this page:
On this page

Track Subscriptions


(warning)

Warning

This page is for reference only. We are no longer onboarding new customers to Programmable Video. Existing customers can continue to use the product until December 5, 2024(link takes you to an external page).
We recommend migrating your application to the API provided by our preferred video partner, Zoom. We've prepared this migration guide(link takes you to an external page) to assist you in minimizing any service disruption.


Overview

overview page anchor

By default clients connecting to a Group Room subscribe to all tracks published and as such all participants can see and hear all other participants. The Track Subscription API allows you to change this behavior and manage the communication topology of a Group Room. There are many possibilities, for example one participant could be set up as a silent observer, or the Group Room could be arranged so that all participants are only subscribed to the video and audio of a single presenter. Specifically, the API allows you to:

  • Define a Participant's Track subscribe rules.
  • Enumerate a Participant's subscribed Tracks.

The Track Subscription API is only available in Group Rooms. Check the SDK Compatibility section below for further compatibility information.



The Track Subscription API is a REST API. However, it fires subscription events that must be managed client side, which is only available in the following SDK versions:

Twilio SDKTrack Subscriptions Support
JavaScriptSDK 1.15.0+
iOSSDK 2.4.0+
AndroidSDK 3.0.0+

For further information on how to manage client side subscription events and errors check the Managing Track Subscriptions with Twilio Client SDKs section below.


Communication Topologies in Group Rooms

topologies-gr page anchor

Twilio Group Rooms media communications work though a publish/subscribe model that relies on the following principles:

  • When a participant publishes an audio, video or data track such track is assigned a SID and the client SDK starts sending the media bitstream to Twilio.
  • When a participant subscribes to a track, Twilio clones and forwards the track bitstream to that participant.

This document describes how the Track Subscription API can be used for setting which participants subscribe to which tracks. Some possible subscription models developers can implement using this API are the following:

  • Subscribe-to-all: each participant subscribes to all the published tracks. This is the default in Group Rooms. It is commonly used in collaboration applications where participants communicate with all the rest.
  • Subscribe-to-one: participants subscribe only to a presenter's tracks. This is often used in single-presenter webinars where viewers cannot contribute to the presentation.
  • Subscribe-to-some: where participants subscribe to the tracks published by a set of speakers. It is common in collaborative presentations or in presentations where viewers can temporarily become presenters.
  • Whisperers: some participants (i.e. the whisperers) subscribe to all, but can only be listened/viewed by some. Whisperers are common to train and supervise video contact center agents.
  • etc.
Track Subscription Models.

By default Group Rooms use a "subscribe-to-all" model. Hence, each participant subscribes to all the tracks published to the room by the rest of participants.


For the sake of simplicity, we define the Participant Resource URI as:


_10
PARTICIPANT_URI = https://video.twilio.com/v1/Rooms/{RoomNameOrSid}/Participants/{ParticipantIdentityOrSid}

As children, the resources of the Track Subscription API are:

  • PARTICIPANT_URI/SubscribedTracks
    • GET : Lists the Tracks subscribed by the specified Participant.
  • PARTICIPANT_URI/SubscribedTracks/{TrackSid}/
    • GET : Retrieves the SubscribedTrack instance resource with information about the subscription of the specified Track by the specified Participant.
  • PARTICIPANT_URI/SubscribeRules
    • GET : Retrieves the subscribe rules for the specified Participant.
    • POST : Updates the subscribe rules of the specified Participant

The SubscribedTrack Instance Resource

st-resource page anchor

A SubscribedTrack instance is a Participant's resource that represents the subscription such Participant has for a given Track.

Resource URI

st-uri page anchor

PARTICIPANT_URI/SubscribedTracks/{TrackSid}/

Resource Properties

st-properties page anchor

This resource has the following properties:

Resource properties
sidtype: SID<MT>Not PII

The unique string that we created to identify the RoomParticipantSubscribedTrack resource.


participant_sidtype: SID<PA>Not PII

The SID of the participant that subscribes to the track.


publisher_sidtype: SID<PA>Not PII

The SID of the participant that publishes the track.


room_sidtype: SID<RM>Not PII

The SID of the room where the track is published.


nametype: stringNot PII

The track name. Must have no more than 128 characters and be unique among the participant's published tracks.


date_createdtype: string<DATE TIME>Not PII

The date and time in GMT when the resource was created specified in ISO 8601(link takes you to an external page) format.


date_updatedtype: string<DATE TIME>Not PII

The date and time in GMT when the resource was last updated specified in ISO 8601(link takes you to an external page) format.


enabledtype: booleanNot PII

Whether the track is enabled.


kindtype: enum<STRING>Not PII

The track type. Can be: audio, video or data.

Possible values:
audiovideodata

urltype: string<URI>Not PII

The absolute URL of the resource.

Get a Subscribed Track (HTTP GET)

get-st page anchor

Retrieves the SubscribedTrack instance resource associated to the specified Participant and TrackSid.

This resource does not support POST, PUT or DELETE. For modifying the track subscriptions of a participant see the SubscribeRules Resource section below.


The SubscribedTracks List Resource

stl-resource page anchor

The SubscribedTracks list resource represents the subscriptions of a given Participant in a Group Room.

PARTICIPANT_URI/SubscribedTracks

Get a Participant's Subscribed Track List (HTTP GET)

get-stl page anchor

Retrieves the list of subscribed tracks associated to the specified Participant with paging data.

This resource does not support POST, PUT or DELETE. For modifying the track subscriptions of a participant see the SubscribeRules Resource section below.


The SubscribeRules Resource

sl-resource page anchor

The SubscribeRules resource is a singleton resource that represents the subscribe rules that are enforced on a given Participant.

Resource URI

sr-uri page anchor

PARTICIPANT_URI/SubscribeRules

Resource Properties

sr-properties page anchor

This resource has the following properties:

Resource properties
participant_sidtype: SID<PA>Not PII

The SID of the Participant resource for the Subscribe Rules.


room_sidtype: SID<RM>Not PII

The SID of the Room resource for the Subscribe Rules


rulestype: object[]Not PII

A collection of Subscribe Rules that describe how to include or exclude matching tracks. See the Specifying Subscribe Rules(link takes you to an external page) section for further information.


date_createdtype: string<DATE TIME>Not PII

The date and time in GMT when the resource was created specified in ISO 8601(link takes you to an external page) format.


date_updatedtype: string<DATE TIME>Not PII

The date and time in GMT when the resource was last updated specified in ISO 8601(link takes you to an external page) format.

Get a Participant's Subscribe Rules (HTTP GET)

sr-get page anchor

Returns the subscribe rules of the specified Participant.

Modify a Participant's Subscribe Rules (HTTP POST)

sr-post page anchor

Updates the subscribe rules of the specified Participant. The following parameters are supported:

URI parameters
RoomSidtype: stringNot PII
Path Parameter

The SID of the Room resource where the subscribe rules to update apply.


ParticipantSidtype: stringNot PII
Path Parameter

The SID of the Participant resource to update the Subscribe Rules.


Request body parameters
Rulestype: objectNot PII

A JSON-encoded array of subscribe rules. See the Specifying Subscribe Rules(link takes you to an external page) section for further information.

For a deeper understanding on how subscribe rules work, see the Understanding Subscribe Rules section below.

This resource does not support PUT or DELETE.


Understanding Subscribe Rules

understanding-sr page anchor

Subscribe rules can be set:

Specifying Subscribe Rules

specifying-sr page anchor

A subscription rule instance is a JSON with the following structure:


_10
{"type": rule_type, filter_name: filter_value, filter_name: filter_value, ...}

Where:

FieldDescription
rule_typeAn identifier specifying the type of rule. Can be one of the following:-include: includes the tracks that match the filters into the subscription.-exclude: excludes the tracks that match the filters from the subscription.
filter_name and filter_valueThe filter_name must be one of the following:-all: the filter affects all tracks. Accepts only one value: true (matches all tracks)-kind: matches tracks of a given type. Accepts video, audio and data as values.-publisher: matches all tracks published by the Participant with the identity (case sensitive) or SID specified as value-track: matches tracks with the name (case sensitive) or SID specified as value. A rule containing multiple filters matches the set of tracks that comply with all of them. In other words, filters combine in a rule through a logical AND. Note also that the tracks published by a participant are never considered when evaluating such participant's filters. As a consequence, a participant cannot subscribe to her own tracks.

Based on this, subscribe rules are specified as a JSON array containing up to 20 rules. For example:


_10
Rules = [
_10
{"type": "include", "all": true}, //rule_1
_10
{"type": "exclude", "kind": "video"}, //rule_2
_10
{"type": "include", "publisher": "Bob", "track": "screen"}, //rule_3
_10
{"type": "include", "track": "MTXXXXXXXXXXXXXXXXXXXXXXXXXXX"}, //rule_4
_10
...
_10
rule_20
_10
]

Remember that valid subscription rules comply with the following:

  • The maximum number of rules that can be specified is 20.
  • An empty set of rules (i.e. [] ) is not allowed.
  • A rule containing the "all" filter must have a value of true . Notice that this means false is not allowed.
  • A rule having an “all” filter cannot have any other filters.
  • A rule must contain a "type" field.
  • If the "kind" filter is used, its value must be one of audio , video or data .
  • A rule cannot contain several times the same filter (e.g. specify two different kind s or publisher s, etc.)

When invalid rules are specified, the currently active rules will not be updated and the POST will be answered with an HTTP 400 error response like the following:


_10
{
_10
"code": 53215,
_10
"message": "Invalid Subscribe Rule(s)",
_10
"more_info": "https://www.twilio.com/docs/errors/53215",
_10
"status": 400
_10
}

For example, the following requests are invalid:


_10
//Invalid because it's using an empty set
_10
Rules = []


_10
//Invalid because it uses false as value of "all"
_10
Rules = [
_10
{"type":"include", "all": false}
_10
]


_10
//Invalid because it specifies a non supported kind
_10
Rules = [
_10
{"type": "include", "all": true},
_10
{"type": "exclude", "kind": "wideo"}
_10
]


_10
//Invalid because it repeats the "kind" filter twice
_10
Rules = [
_10
{"type": "include", "kind": "audio", "kind": "data"},
_10
]


_10
//Invalid because an "all" filter is not compatible with any other filter
_10
Rules = [
_10
{"type": "include", "all": "true", "kind": "data"},
_10
]

Subscribe Rules Semantics

sr-semantics page anchor

SubscribeRules are enforced by Twilio using four main principles:

  1. Subscribe Rules semantics are based on Algebra of Sets(link takes you to an external page) .
  2. Subscribe Rules are stateless.
  3. Subscribe Rules are enforced declaratively.
  4. Subscribe Rules are initialized with a "subscribe-to-all" rule.

1. Subscribe Rules Semantics are based on Algebra of Sets

sr-algebra page anchor

We define the Set of Subscribed Tracks (SetST) as the set of tracks a given participant should be subscribed to at any time. The SetST is computed by Twilio using Algebra of Sets based on the following algorithm:

  • The SetST is initialized to the empty set.
  • The subscribe rules are applied to the SetST using Algebra of Sets in the order provided such that:
    • For any include rule we perform the union of the SetST with the set of tracks matching the rule filters.
    • For any exclude rule we perform the set difference of the SetST with the set of tracks matching by the rule filters.
  • Participants are subscribed to all the tracks remaining in SetST after all the rules have been evaluated.

Let's illustrate this using an example. Imagine a Group Room with three participants named: Alice, Bob and Carl, who publish the tacks specified in the following table:

Alice (PTA)Bob (PTB)Carl (PTC)
Audio TrackMTA_A alice-audioMTB_A bob-audioMTC_A carl-audio
Video Track (cam)MTA_C alice-camMTB_C bob-camMTC_C carl-cam
Video Track (screen)MTA_S screenMTB_S screen
Data TrackMTC_D carl-data

Notice that, for the sake of simplicity, we have assumed the following conventions:

  • Track SIDs have the prefix MT followed by the participant initial and by a letter identifying the track nature ( _A for audio, _C for webcam, _S for screenshare and _D for data).
  • Track names are specified under their corresponding SIDs.

In this context, imagine that we POST the following rules to Alice's /SubscribeRules resource:


_10
Rules = [
_10
{"type": "include", "all": true},
_10
{"type": "exclude", "kind": "video"},
_10
{"type": "include", "publisher": "Bob", "track": "screen"}
_10
]

Then, the algorithm will compute the tracks Alice should subscribe to in the following way:

Alice's rules (applied in the specified order)Tracks in the SetST
SetST is initialized to the empty set.[]
{"type": "include", "all": true} Includes all tracks (except Alice's ones.)[MTB_A,MTB_C,MTB_S,MTC_A,MTC_C,MTC_D]
{"type": "exclude", "kind": "video"} Excludes all video tracks.[MTB_A,MTC_A,MTC_D]
{"type": "include", "publisher": "Bob", "track": "screen"} Includes Bob's tracks named screen.[MTB_A,MTC_A,MTC_D,MTB_S] Alice is finally subscribed to these Tracks.

Still assuming the above specified Group Room scenario, consider the following examples that might be interesting to understand how the subscription algorithm works:

Example: Subscribe-to-all
example-subscribe-to-all page anchor

When POSTing to Alice's subscribe rules the following:


_10
Rules = [
_10
{"type": "include", "all": true}
_10
]

Alice will subscribe to all the tracks (except for her own). Hence, the SetST will be:


_10
[MTB_A,MTB_C,MTB_S,MTC_A,MTC_C,MTC_D]

This happens because:

  • The SetST is initialized to the empty set []
  • The rule includes all the published tracks (except for her own)
Example: Subscribe-to-none
example-subscribe-to-none page anchor

When POSTing to Alice's subscribe rules the following:


_10
Rules = [
_10
{"type": "exclude", "all": true}
_10
]

Alice will subscribe to no tracks:


_10
[] //The empty set

This happens because:

  • The SetST is initialized to the empty set []
  • The rules excludes all the tracks from the empty set, which is still the empty set.
Example: Single exclude rules
example-single-exclude-rules page anchor

When POSTing to Alice's subscribe rules the following:


_10
Rules = [
_10
{"type": "exclude", "kind": "video"}
_10
]

Alice's subscribed tracks will be:


_10
[] //The empty set

This happens because:

  • The SetST is initialized to the empty set [] .
  • The single rule excludes the video tracks from the empty set, which is still an empty set.
Example: include own tracks
example-include-own-tracks page anchor

When POSTing to Alice's subscribe rules the following:


_10
Rules = [
_10
{"type": "include", "track": "alice-audio"}
_10
]

Alice's subscribed tracks will be:


_10
[] //The empty set

This happens because:

  • The SetST is initialized to the empty set [] .
  • Including a participant's own tracks will have no effect as rule filters never consider own published tracks. Hence the rule filter only matches the empty set and the union of two empty sets is still the empty set.
Example: Including tracks by name
example-including-tracks-by-name page anchor

When POSTing to Alice's subscribe rules the following:


_10
Rules = [
_10
{"type": "include", "track": "screen"}
_10
]

Alice's subscribed tracks will be:


_10
[MTB_S] //Bob's screen track

This happens because:

  • The SetST is initialized to the empty set [] .
  • The rule includes all tracks named screen . There are two of them, one owned by Alice. As filters do not consider the participant's own tracks, then Alice subscribes to the union of [MTB_S] and the empty set. Filters containing participant or track names are case sensitive.
Example: Working with multiple filters
example-working-with-multiple-filters page anchor

When POSTing to Carl's subscribe rules the following:


_10
Rules = [
_10
{"type": "include", "all": true},
_10
{"type": "exclude", "publisher": "PTB", "kind": "audio"},
_10
{"type": "exclude", "kind": "video", "track": "screen"}
_10
]

Carl's subscribed tracks will be:


_10
[MTA_A, MTA_C, MTB_C]

This happens because:

  • The SetST is initialized to the empty set [] .
  • The first rule includes all tracks, except Carl's ones: [MTA_A,MTA_C,MTA_S,MTB_A,MTB_C,MTB_S]
  • Given that filters intersect (i.e. you can read them as being connected through a logical AND), the second rule excludes all audio tracks published by Bob (we assume PTB is Bob's SID). That is, MTB_A is excluded.
  • The third rule further excludes all video tracks named screen . Hence, MTA_S and MTB_S are also excluded.
Example: Considering rule order
example-considering-rule-order page anchor

When POSTing to Alice's subscribe rules the following:


_10
Rules = [
_10
{"type": "include", "kind": "data"},
_10
{"type": "exclude", "publisher": "Carl"}
_10
]

Alice's subscribed tracks will be:


_10
[] //The empty set

This happens because:

  • The SetST is initialized to the empty set [] .
  • The first rule includes MTC_D
  • The second rule excludes all of Carl's tracks, which removes MTC_D and results in the empty set.

However, when POSTing


_10
Rules = [
_10
{"type": "exclude", "publisher": "Carl"},
_10
{"type": "include", "kind": "data"}
_10
]

Alice's subscribed tracks will be:


_10
[MTC_D]

This happens because:

  • The SetST is initialized to the empty set [] .
  • The first excludes Carl's tracks from the empty set, which is still the empty set.
  • The third rule adds MTC_D to the SetST.

2. Subscribe Rules are stateless

sr-stateless page anchor

The Track Subscription API is stateless. This means that it has no memory of rules or subscribed tracks. Every time a developer POSTs a set of new subscription rules, the previous rules are fully erased and replaced with new rules, which are then enforced using the algorithm described in the section above.

To illustrate this, and assuming the Group Room scenario described above, let's imagine that Carl wants to subscribe to all audio tracks and exclude the rest. POSTing the following rules will do it:


_10
Rules = [
_10
{"type": "include", "kind": "audio"}
_10
]

Imagine now that Carl, while keeping his current audio subscriptions, also wants to subscribe to Alice's screenshare. Carl could do it POSTing the following:


_10
Rules = [
_10
{"type": "include", "kind": "audio"},
_10
{"type": "include", "track": "MTA_S"}
_10
]

Observe that if the first rule is omitted, given the stateless nature of the Track Subscription API, Carl would be subscribed only to MTA_S but not to Alice's and Bob's audios.

If now Carl wants to stop receiving Alice's screen, but instead wants to see Bob's webcam while still receiving all the audios, the POST should contain something like this:


_10
Rules = [
_10
{"type": "include", "kind": "audio"},
_10
{"type": "include", "track": "MTB_C"}
_10
]

As you can see, each POST "resets" the subscribe rules making it necessary to specify what should be included and excluded at all requests.

3. Subscribe Rules are enforced declaratively

sr-declarative page anchor

When developers POST subscribe rules to Twilio, those rules are enforced in a dynamic way. That means that the algorithm does not only execute at POST time, but it is executed every time there is change in the Room's available tracks. Hence, once the rules have been POSTed, Twilio guarantees that they are enforced at any time without requiring further developer intervention.

To illustrate this, let's imagine that the sequence of actions in our example room is the following:

  1. First Alice joins the room and publishes her 3 tracks.
  2. Second Bob joins publishing his 3 tracks
  3. Third Carl joins also publishing his audio and video tracks.
  4. Fourth Carl publishes his data track
  5. Then Bob leaves unpublishing his tracks.
  6. Finally Carl also leaves.

In this scenario, imagine that the following subscribe rules are POSTed to Alice's /SubscribedTracks just after she joins:


_10
Rules=[
_10
{"type": "include", "kind": "audio"},
_10
{"type": "include", "track": "screen"},
_10
{"type": "exclude", "publisher": "Carl"},
_10
{"type": "include", "kind": "data"}
_10
]

Just after the POST, Alice's subscribed tracks are:


_10
[] //The empty set

This happens because Alice is alone in the room and cannot subscribe to her own published tracks, no matter what the subscribe rules are.

Following the sequence above, now Bob connects to the Room publishing his 3 tracks. At that moment, given the declarative nature of subscribe rules, Alice's subscribed tracks are re-evaluated to be:


_10
[MTB_A, MTB_S]

Observe the above rules avoid using SIDs in the filters, we can create rules affecting Alice's subscriptions to Bob's tracks even before Bob connects to the room.

Next Carl joins and publishes audio and video tracks. Given that the third rule excludes Carl's tracks, Alice's subscribed tracks stay the same:


_10
[MTB_A, MTB_S]

After that, Carl publishes his Data Track. As the fourth rule is including all tracks with kind equal to data, Alice's subscribed tracks evolve to:


_10
[MTB_A, MTB_S, MTC_D]

At the fifth step, Bob leaves the room. Alice's subscriptions will be modified to be:


_10
[MTC_D]

Last, when Carl leaves, Alice's will no be subscribed to any track any longer.


_10
[] //The empty set

4. Subscribe Rules are initialized with a "subscribe-to-all" rule

sb-default-rule page anchor

When a participant joins a Group Room, Twilio automatically feeds a subscription rule to that participant. We call it the default_rule:


_10
{"type": "include", "all": true}

This happens automatically and only once at the time the participant connects. Twilio does this for enforcing a default "subscribe-to-all" model. This means that, if no further rules are provided, participants will connect to all the published tracks, which is consistent with Group Rooms default behavior prior to the existence of the Track Subscription API.

Thanks to this, Twilio guarantees backwards compatibility of all Group Room applications. However, this has a drawback. To understand why, let's use an example. Imagine that a developer wants Carl to just publish to the room but not to subscribe to anything. In that case, the developer may POST the following to Carl's rules:


_10
Rules=[{"type":"exclude", "all": true}]

Indeed, this will remove the default rule with a new one enforcing a "subscribe-to-none" model. However this POST can only be sent after Carl connects. Hence, there is a race condition between the default rule, which tries to subscribe Carl to all the tracks, and the POST that tries to remove all subscriptions. This may degrade Carl's experience with some annoying subscriptions that appear and disappear.

To avoid this, Twilio client SDK APIs allow to override the default rule at connect time. Section Overriding defaults below is devoted to explain how.


Managing Track Subscriptions with Twilio Client SDKs

mts-sdk page anchor

Overriding the Default Rule

override-defaults page anchor

Twilio Video SDKs allows overriding the "subscribe-to-all" default_rule at connect time and replace it with a "subscribe-to-none" one:


_10
{"type": "exclude", "all": true}

This is achieved through the automaticSubscription parameter as the following code snippets illustrate.

JavaScript SDK (v2.0.0-beta12+ required)


_10
const { connect } = require('twilio-video');
_10
connect(token, {
_10
automaticSubscription: false,
_10
name: 'my-room'
_10
});

iOS SDK (v.2.10.0+ required)


_10
let connectOptions = TVIConnectOptions(token: accessToken) { (builder) in
_10
builder.isAutomaticSubscriptionEnabled = false
_10
builder.roomName = "my-room"
_10
}
_10
self.room = TwilioVideo.connect(with: connectOptions, delegate: self)

Android SDK (v.4.1.0+ required)

Java


_10
ConnectOptions connectOptions = new ConnectOptions.Builder(accessToken)
_10
.enableAutomaticSubscription(false)
_10
.roomName("my-room")
_10
.build();
_10
_10
room = Video.connect(context, connectOptions, roomListener);

Kotlin


_10
val connectOptions = ConnectOptions.Builder(accessToken)
_10
.enableAutomaticSubscription(false)
_10
.roomName("my-room")
_10
.build()
_10
_10
room = Video.connect(context, connectOptions, roomListener)

Managing SDK Subscribe Events

su-events page anchor

Every time subscriptions change Twilio sends events to the corresponding participant SDK. Developers must manage such events as part of their client application logic. There are three types of events:

Event typeEvent description
subscribedFired when the Participant starts receiving media from a newly subscribed Track. Handling this event typically consists on rendering the received media (e.g. attaching to a video tag, etc.)
unsubscribedFired when the Participant unsubscribes from a previously subscribed Track. Handling this event typically consists on adapting the GUI to stop rendering the track media (e.g. detaching from a video tag, etc.)
errorFired when the Participant should have subscribed to a Track but could not do it. This happens, for example, when the Participant does not support codecs of Tracks included by her subscribe rules. Handling this event typically requires informing the end-user and, eventually, taking the appropriate actions for minimizing the error impact.

Managing Subscribe Events in JavaScript (v.1.18.0+ required)

se-javascript page anchor

The following code snippet illustrates how to manage subscribe events in Twilio Video JavaScript SDK:


_37
_37
const { connect } = require('twilio-video');
_37
_37
function subscribed(track) {
_37
console.log('Subscribed to RemoteTrack:', track.sid);
_37
//Code for starting track rendering goes here.
_37
}
_37
_37
function unsubscribed(track) {
_37
console.log('Unsubscribed to RemoteTrack:', track.sid);
_37
//Code for stopping track rendering goes here.
_37
}
_37
_37
function subscriptionFailed(error, publication) {
_37
console.log('Failed to subscribe to RemoteTrack ${publication.trackSid}:', error);
_37
//Code for managing subscribe errors goes here.
_37
}
_37
_37
function listenToSubscriptionEvents(publication) {
_37
publication.on('subscribed', subscribed);
_37
publication.on('unsubscribed', unsubscribed);
_37
publication.on('subscriptionFailed', subscriptionFailed);
_37
}
_37
_37
function participantConnected(participant) {
_37
// Listen to subscription events of already published Tracks.
_37
participant.tracks.forEach(listenToSubscriptionEvents);
_37
// Listen to subscription events of Tracks that are published later.
_37
room.on('trackPublished', listenToSubscriptionEvents);
_37
}
_37
_37
connect('$token', { name: 'my-room' }).then(room => {
_37
// Listen to events from RemoteParticipants currently in the Room.
_37
room.participants.forEach(participantConnected);
_37
// Listen to events from RemoteParticipants that will join the Room.
_37
room.on('participantConnected', participantConnected);
_37
});

Managing Subscribe Events in iOS (v.2.4.0+ required)

se-ios page anchor

The following code snippet illustrates how to manage subscribe events in Twilio Video iOS SDK. For the sake of simplicity, we have only considered subscription event from video tracks. Handlers for audio and data tracks are equivalent but using their corresponding data types.


_35
let options = TVIConnectOptions(token: accessToken)
_35
room = TwilioVideo.connect(with: options, delegate: self)
_35
_35
func didConnect(to room: TVIRoom) {
_35
// Handle subscription events for connected Participants
_35
for remoteParticipant in room.remoteParticipants {
_35
remoteParticipant.delegate = self
_35
}
_35
}
_35
_35
func room(_ room: TVIRoom, participantDidConnect participant: TVIRemoteParticipant) {
_35
participant.delegate = self
_35
}
_35
_35
func subscribed(to videoTrack: TVIRemoteVideoTrack,
_35
publication: TVIRemoteVideoTrackPublication,
_35
for participant: TVIRemoteParticipant) {
_35
print("Subscribed to video track: \(publication.trackName)")
_35
//Code for starting track rendering goes here.
_35
}
_35
_35
func unsubscribed(from videoTrack: TVIRemoteVideoTrack,
_35
publication: TVIRemoteVideoTrackPublication,
_35
for participant: TVIRemoteParticipant) {
_35
print("Unsubscribed from video track: \(publication.trackName)")
_35
//Code for stopping track rendering goes here.
_35
}
_35
_35
func failedToSubscribe(toVideoTrack publication: TVIRemoteVideoTrackPublication,
_35
error: Error,
_35
for participant: TVIRemoteParticipant) {
_35
let errorText = String(describing: error)
_35
print("Failed to subscribe to: \(publication.trackName), error: \(errorText)")
_35
//Code for managing subscribe errors goes here.
_35
}

Managing Subscribe Events in Android (v.3.0.0+ required)

se-android page anchor

The following code snippet illustrates how to manage subscribe events in Twilio Video Android SDK. For the sake of simplicity, we have only considered subscription event from video tracks. Callbacks for audio and data tracks are similar but using their corresponding data types.

Java


_42
ConnectOptions connectOptions = new ConnectOptions.Builder(accessToken).build();
_42
_42
Room room = Video.connect(context, connectOptions, this);
_42
_42
@Override
_42
public void onConnected(@NonNull Room room) {
_42
for (RemoteParticipant remoteParticipant : room.getRemoteParticipants()) {
_42
remoteParticipant.setListener(this);
_42
}
_42
}
_42
_42
@Override
_42
public void onParticipantConnected(@NonNull Room room,
_42
@NonNull RemoteParticipant remoteParticipant) {
_42
remoteParticipant.setListener(this);
_42
}
_42
_42
@Override
_42
public void onVideoTrackSubscribed(@NonNull RemoteParticipant remoteParticipant,
_42
@NonNull RemoteVideoTrackPublication remoteVideoTrackPublication,
_42
@NonNull RemoteVideoTrack remoteVideoTrack) {
_42
Log.i("Listener", "Subscribed to video track: " + remoteVideoTrack.getName());
_42
// Code for starting track rendering goes here
_42
}
_42
_42
@Override
_42
public void onVideoTrackUnsubscribed(@NonNull RemoteParticipant remoteParticipant,
_42
@NonNull RemoteVideoTrackPublication remoteVideoTrackPublication,
_42
@NonNull RemoteVideoTrack remoteVideoTrack) {
_42
Log.i("Listener", "Unsubscribed from video track: " + remoteVideoTrack.getName());
_42
// Code for stopping track rendering goes here
_42
}
_42
_42
@Override
_42
public void onVideoTrackSubscriptionFailed(@NonNull RemoteParticipant remoteParticipant,
_42
@NonNull RemoteVideoTrackPublication remoteVideoTrackPublication,
_42
@NonNull TwilioException twilioException) {
_42
Log.e("Listener", "Failed to subscribe to: "
_42
+ remoteVideoTrackPublication.getTrackName() + ", error: " +
_42
twilioException.getMessage());
_42
// Code for managing subscribe errors goes here.
_42
}

Kotlin


_36
val connectOptions = ConnectOptions.Builder(accessToken).build()
_36
_36
val room = Video.connect(context, connectOptions, this)
_36
_36
override fun onConnected(room: Room) {
_36
for (remoteParticipant in room.remoteParticipants) {
_36
remoteParticipant.setListener(this)
_36
}
_36
}
_36
_36
override fun onParticipantConnected(room: Room,
_36
remoteParticipant: RemoteParticipant) {
_36
remoteParticipant.setListener(this)
_36
}
_36
_36
override fun onVideoTrackSubscribed(remoteParticipant: RemoteParticipant,
_36
remoteVideoTrackPublication: RemoteVideoTrackPublication,
_36
remoteVideoTrack: RemoteVideoTrack) {
_36
Log.i("Listener", "Subscribed to video track: ${remoteVideoTrack.name}")
_36
// Code for starting track rendering goes here
_36
}
_36
_36
override fun onVideoTrackUnsubscribed(remoteParticipant: RemoteParticipant,
_36
remoteVideoTrackPublication: RemoteVideoTrackPublication,
_36
remoteVideoTrack: RemoteVideoTrack) {
_36
Log.i("Listener", "Unsubscribed from video track: ${remoteVideoTrack.name}")
_36
// Code for stopping track rendering goes here
_36
}
_36
_36
override fun onVideoTrackSubscriptionFailed(remoteParticipant: RemoteParticipant,
_36
remoteVideoTrackPublication: RemoteVideoTrackPublication,
_36
twilioException: TwilioException) {
_36
Log.e("Listener", "Failed to subscribe to:" +
_36
"${remoteVideoTrackPublication.trackName}, error: ${twilioException.message}")
_36
// Code for managing subscribe errors goes here.
_36
}


Video Contact Center: Setting Static Subscribe Rules

static-example page anchor

Video contact centers are gaining popularity as an evolution of voice ones. We use this use-case as an example on how to implement a Group Application with static subscribe rules: that is, rules that do change over time. A typical contact center video room may involve the following roles:

  • Agent, who assists contact center customers.
  • Customer, who video calls asking for support.
  • Supervisor, who trains Agents. In traditional voice contact centers this type of role is sometimes called a "whisperer": a participant who listens to all but can only be listened by the agent.

The Group Room communication topology for this use-case is:

  • The agent should subscribe to all tracks both the one of customer and the ones of the supervisor.
  • The customer should subscribe to the agent's audio and video. However, supervisor should be hidden for her.
  • The supervisor should subscribe to all the tracks.
Video Contact Center Whisperer.

Representation of a video contact center where an agent is in helping a customer while being at the same time whispered by a supervisor.

In these types of scenarios where there are clearly established roles and topologies, we recommend the following implementation approach:

  • Developers should follow a fixed naming convention for their tracks. For example, the agent webcam track could be named agent-video and the customer audio track could be named customer-audio . The specific chosen names are not relevant. The important point is to comply with the naming convention in all the video calls.
  • The application developer can use the above-mentioned naming convention for setting the appropriate subscribe rules at connect time.

Based on this recommendation, tracks published in this application will look like this:

AgentCustomerSupervisor
Audio TrackMTA_A agent-audioMTC_A customer-audioMTS_A whisperer-audio
Video Track (cam)MTA_C agent-videoMTC_C customer-video
Video Track (screen)MTC_S customer-screen

For the sake of simplicity, we assume the following:

  • Track SIDs have the prefix MT followed by the participant initial and by a letter identifying the track nature ( _A for audio, _C for webcam, _S for screenshare).
  • Track names are specified under their corresponding SIDs. Due to be above-mentioned naming convention, all room instances keep those track names.

Agent's Subscribe Rules

agents-subscribe-rules page anchor

Agents subscribe to all the tracks published by the rest of participants. Due to this, the default "subscribe-to-all" model is enough. Hence, the application developer does not need add any additional rule:


_10
Use default (i.e. "subscribe-to-all").

Supervisor's Subscribe Rules

supervisors-subscribe-rules page anchor

Supervisors also subscribe to all the published tracks and the default rule is also enough for them.


_10
Use default (i.e. "subscribe-to-all").

Customer's Subscribe Rules

customers-subscribe-rules page anchor

Customers should just subscribe to the agent tracks. We recommend to do it in two steps:

Step 1: override default rule with "subscribe-to-none"

This step avoids race conditions between the default "subscribe-to-all" rule and POST setting the appropriate subscribe rules. See section Overriding the Default Rule for further information.

Step 2: POST the appropriate subscribe rules

Use the following code snippet where we assume that:

  • The Twilio Access Key SID and Secret are SKXXXX:your_api_key_secret
  • The Room SID is RMXXXX

Subscribe Rules for Video Contact Center Customer

subscribe-rules-for-video-contact-center-customer page anchor
Node.js
Python
C#
Java
PHP
Ruby
curl

_27
// NOTE: This example uses the next generation Twilio helper library - for more
_27
// information on how to download and install this version, visit
_27
// https://www.twilio.com/docs/libraries/node
_27
_27
// Find your credentials at twilio.com/console
_27
// To set up environmental variables, see http://twil.io/secure
_27
const API_KEY_SID = process.env.TWILIO_API_KEY;
_27
const API_KEY_SECRET = process.env.TWILIO_API_KEY_SECRET;
_27
const ACCOUNT_SID = process.env.TWILIO_ACCOUNT_SID;
_27
_27
const Twilio = require('twilio');
_27
_27
const client = new Twilio(API_KEY_SID, API_KEY_SECRET, {accountSid: ACCOUNT_SID});
_27
_27
client.video.rooms('RMXXXX').participants.get('Customer')
_27
.subscribeRules.update({
_27
rules: [
_27
{"type": "include", "all": true},
_27
{"type": "exclude", "publisher": "Supervisor"}
_27
]
_27
})
_27
.then(result => {
_27
console.log('Subscribe Rules updated successfully')
_27
})
_27
.catch(error => {
_27
console.log('Error updating rules ' + error)
_27
});

Output

_10
{
_10
"room_sid": "RMXXXX",
_10
"participant_sid": "PAXXXX",
_10
"rules": [
_10
{"type": "include", "all": true},
_10
{"type": "exclude", "publisher": "Supervisor"}
_10
],
_10
"date_updated": "2019-04-30T20:28:00Z",
_10
"date_created": "2019-04-30T20:15:49Z"
_10
}

Collaborative Video: Updating Subscribe Rules Dynamically

dynamic-example page anchor

The example above relies on some a prior knowledge about track names and participant roles for setting the subscribe rules at connect time. However, this is not always possible as there might be use-cases where the number of participants, their roles and their communication topology may dynamically change. For example, imagine a collaboration application where participants have full freedom to select the tracks they want to subscribe to at runtime. In this context, imagine a participant named Adam who connects with the following preferences:

  1. At connect time Adam wants to receive all the tracks (default behavior).
  2. After a while, Adam notices his bandwidth consumption is too high and decides to unsubscribe from all video tracks.
  3. Later, a video screenshare track with SID MTXXXX is published to the room and Adam subscribes to it.
  4. John, another participant, is in a noisy place and his audio track is annoying. Adam decides to unsubscribe from it.

The way in which the application signals the different events is out of the scope of this document. However, concentrating only on Track Subscription API calls, this is a possible solution. Notice that we assume that:

  • The Twilio credentials are SKXXXX:your_api_key_secret .
  • The Room SID is RMXXXX .

Subscribe Rules for Collaborative Dynamic Participant

subscribe-rules-for-collaborative-dynamic-participant page anchor
Node.js
Python
C#
Java
PHP
Ruby
curl

_72
// NOTE: This example uses the next generation Twilio helper library - for more
_72
// information on how to download and install this version, visit
_72
// https://www.twilio.com/docs/libraries/node
_72
_72
// Find your credentials at twilio.com/console
_72
// To set up environmental variables, see http://twil.io/secure
_72
const API_KEY_SID = process.env.TWILIO_API_KEY;
_72
const API_KEY_SECRET = process.env.TWILIO_API_KEY_SECRET;
_72
const ACCOUNT_SID = process.env.TWILIO_ACCOUNT_SID;
_72
_72
const Twilio = require('twilio');
_72
_72
const client = new Twilio(API_KEY_SID, API_KEY_SECRET, {accountSid: ACCOUNT_SID});
_72
_72
//-------------------------------------------------------------------------------
_72
//1. At connect time Adam wants to receive all the tracks.
_72
// Done by default rule. No further actions required.
_72
_72
_72
//-------------------------------------------------------------------------------
_72
//2. After a while, Adam notices his bandwidth consumption is too high and
_72
// decides to unsubscribe from all video tracks
_72
_72
client.video.rooms('RMXXXX').participants.get('Adam')
_72
.subscribeRules.update({
_72
rules: [
_72
{"type": "include", "kind": "audio"}
_72
]
_72
})
_72
.then(result => {
_72
console.log('Subscribe Rules updated successfully')
_72
})
_72
.catch(error => {
_72
console.log('Error updating rules ' + error)
_72
});
_72
_72
//-------------------------------------------------------------------------------
_72
//3. Later, a video screenshare track with SID MTXXXX is published to the room
_72
// and Adam subscribes to it.
_72
_72
client.video.rooms('RMXXXX').participants.get('Adam')
_72
.subscribeRules.update({
_72
rules: [
_72
{"type": "include", "kind": "audio"},
_72
{"type": "include", "track": "MTXXXX"}
_72
]
_72
})
_72
.then(result => {
_72
console.log('Subscribe Rules updated successfully')
_72
})
_72
.catch(error => {
_72
console.log('Error updating rules ' + error)
_72
});
_72
_72
//-------------------------------------------------------------------------------
_72
//4. John, another participant, is in a noisy place and his audio track is
_72
// annoying. Adam decides to unsubscribe from it.
_72
_72
client.video.rooms('RMXXXX').participants.get('Adam')
_72
.subscribeRules.update({
_72
rules: [
_72
{"type": "include", "kind": "audio"},
_72
{"type": "include", "track": "MTXXXX"},
_72
{"type": "exclude", "publisher": "John", "kind": "audio"}
_72
]
_72
})
_72
.then(result => {
_72
console.log('Subscribe Rules updated successfully')
_72
})
_72
.catch(error => {
_72
console.log('Error updating rules ' + error)
_72
});

Output

_50
_50
//-------------------------------------------------------------------------------
_50
//1. At connect time Adam wants to receive all the tracks.
_50
// Done by default rule. No further actions required.
_50
_50
_50
//-------------------------------------------------------------------------------
_50
//2. After a while, Adam notices his bandwidth consumption is too high and
_50
// decides to unsubscribe from all video tracks
_50
_50
{
_50
"room_sid": "RMXXXX",
_50
"participant_sid": "PAXXXX",
_50
"rules": [
_50
{"type": "include", "kind": "audio"}
_50
],
_50
"date_updated": "2019-04-30T20:28:00Z",
_50
"date_created": "2019-04-30T20:15:49Z"
_50
}
_50
_50
//-------------------------------------------------------------------------------
_50
//3. Later, a video screenshare track with SID MTXXXX is published to the room
_50
// and Adam subscribes to it.
_50
_50
{
_50
"room_sid": "RMXXXX",
_50
"participant_sid": "PAXXXX",
_50
"rules": [
_50
{"type": "include", "kind": "audio"},
_50
{"type": "include", "track": "MTXXXX"}
_50
],
_50
"date_updated": "2019-04-30T20:28:00Z",
_50
"date_created": "2019-04-30T20:15:49Z"
_50
}
_50
_50
//-------------------------------------------------------------------------------
_50
//4. John, another participant, is in a noisy place and his audio track is
_50
// annoying. Adam decides to unsubscribe from it.
_50
_50
{
_50
"room_sid": "RMXXXX",
_50
"participant_sid": "PAXXXX",
_50
"rules": [
_50
{"type": "include", "kind": "audio"},
_50
{"type": "include", "track": "MTXXXX"},
_50
{"type": "exclude", "publisher": "John", "kind": "audio"}
_50
],
_50
"date_updated": "2019-04-30T20:28:00Z",
_50
"date_created": "2019-04-30T20:15:49Z"
_50
}

Fetching a Participant's Subscribe Rules

fetch-sr-example page anchor

For executing this example the following is required:

  • The Twilio Access Key SID and Secret ( SKXXXX:your_api_key_secret )
  • The Room SID or name ( RMXXXX )
  • The Participant SID or identity ( PAXXXX )

Fetching a Participant's Subscribe Rules

fetching-a-participants-subscribe-rules page anchor
Node.js
Python
C#
Java
PHP
Ruby
curl

_24
// NOTE: This example uses the next generation Twilio helper library - for more
_24
// information on how to download and install this version, visit
_24
// https://www.twilio.com/docs/libraries/node
_24
_24
// Find your credentials at twilio.com/console
_24
// To set up environmental variables, see http://twil.io/secure
_24
const API_KEY_SID = process.env.TWILIO_API_KEY;
_24
const API_KEY_SECRET = process.env.TWILIO_API_KEY_SECRET;
_24
const ACCOUNT_SID = process.env.TWILIO_ACCOUNT_SID;
_24
_24
const Twilio = require('twilio');
_24
_24
const client = new Twilio(API_KEY_SID, API_KEY_SECRET, {accountSid: ACCOUNT_SID});
_24
_24
client.video.rooms('RMXXXX').participants.get('PAXXXX')
_24
.subscribeRules.fetch()
_24
.then(subscribeRules => {
_24
subscribeRules.rules.forEach(rule => {
_24
console.log('Read rule with type = ' + rule.type);
_24
});
_24
})
_24
.catch(error => {
_24
console.log('Error fetching subscribed rules ' + error)
_24
});

Output

_16
{
_16
"room_sid": "RMXXXX",
_16
"participant_sid": "PAXXXX",
_16
"rules": [
_16
{
_16
"all": true,
_16
"type": "include"
_16
},
_16
{
_16
"publisher": "Carl",
_16
"type": "exclude"
_16
}
_16
],
_16
"date_updated": "2019-04-30T12:38:10Z",
_16
"date_created": "2019-04-30T12:25:21Z"
_16
}

Fetching a Participant's Subscribed Track List

fetch-stl-example page anchor

For executing this example the following is required:

  • The Twilio Access Key SID and Secret ( SKXXXX:your_api_key_secret )
  • The Room SID or name ( RMXXXX )
  • The Participant SID or identity ( PAXXXX )

Fetching a Participant's Subscribed Track List

fetching-a-participants-subscribed-track-list page anchor
Node.js
Python
C#
Java
PHP
Ruby
curl

_24
// NOTE: This example uses the next generation Twilio helper library - for more
_24
// information on how to download and install this version, visit
_24
// https://www.twilio.com/docs/libraries/node
_24
_24
// Find your credentials at twilio.com/console
_24
// To set up environmental variables, see http://twil.io/secure
_24
const API_KEY_SID = process.env.TWILIO_API_KEY;
_24
const API_KEY_SECRET = process.env.TWILIO_API_KEY_SECRET;
_24
const ACCOUNT_SID = process.env.TWILIO_ACCOUNT_SID;
_24
_24
const Twilio = require('twilio');
_24
_24
const client = new Twilio(API_KEY_SID, API_KEY_SECRET, {accountSid: ACCOUNT_SID});
_24
_24
client.video.rooms('RMXXXX').participants.get('PAXXXX')
_24
.subscribedTracks.list()
_24
.then(subscribedTracks => {
_24
subscribedTracks.rules.forEach(subscribedTrack => {
_24
console.log('Read subscribed track with sid = ' + subscribedTrack.sid);
_24
});
_24
})
_24
.catch(error => {
_24
console.log('Error fetching subscribed tracks' + error)
_24
});

Output

_61
{
_61
"subscribed_tracks": [
_61
{
_61
"kind": "video",
_61
"name": "1f6f07c7-7d02-4f4c-9caa-c7fdd6817d5a",
_61
"date_updated": null,
_61
"sid": "MTXXXX",
_61
"enabled": true,
_61
"room_sid": "RMXXXX",
_61
"url": "https://video.twilio.com/v1/Rooms/RMXXXX/Participants/PAXXXX/SubscribedTracks/MTXXXX",
_61
"date_created": "2019-04-30T12:26:07Z",
_61
"publisher_sid": "PAXYXY",
_61
"participant_sid": "PAXXXX"
_61
},
_61
{
_61
"kind": "audio",
_61
"name": "4ff26a60-3447-4dd3-8281-2f83568bff50",
_61
"date_updated": null,
_61
"sid": "MTYYYY",
_61
"enabled": true,
_61
"room_sid": "RMXXXX",
_61
"url": "https://video.twilio.com/v1/Rooms/RMXXXX/Participants/PAXXXX/SubscribedTracks/MTYYYY",
_61
"date_created": "2019-04-30T12:26:07Z",
_61
"publisher_sid": "PAXYXY",
_61
"participant_sid": "PAXXXX"
_61
},
_61
{
_61
"kind": "video",
_61
"name": "e124298c-f90f-468a-ad33-d7071c058231",
_61
"date_updated": null,
_61
"sid": "MTZZZZ",
_61
"enabled": true,
_61
"room_sid": "RMXXXX",
_61
"url": "https://video.twilio.com/v1/Rooms/RMXXXX/Participants/PAXXXX/SubscribedTracks/MTZZZZ",
_61
"date_created": "2019-04-30T12:25:44Z",
_61
"publisher_sid": "PAYXYX",
_61
"participant_sid": "PAXXXX"
_61
},
_61
{
_61
"kind": "audio",
_61
"name": "ec40db11-aeb6-4d88-879a-0870aaa6c994",
_61
"date_updated": null,
_61
"sid": "MTKKKK",
_61
"enabled": true,
_61
"room_sid": "RMXXXX",
_61
"url": "https://video.twilio.com/v1/Rooms/RMXXXX/Participants/PAXXXX/SubscribedTracks/MTKKKK",
_61
"date_created": "2019-04-30T12:25:44Z",
_61
"publisher_sid": "PAYXYX",
_61
"participant_sid": "PAXXXX"
_61
}
_61
],
_61
"meta": {
_61
"page": 0,
_61
"page_size": 50,
_61
"first_page_url": "https://video.twilio.com/v1/Rooms/RMXXXX/Participants/PAXXXX/SubscribedTracks?PageSize=50&Page=0",
_61
"previous_page_url": null,
_61
"url": "https://video.twilio.com/v1/Rooms/RMXXXX/Participants/PAXXXX/SubscribedTracks?PageSize=50&Page=0",
_61
"next_page_url": null,
_61
"key": "subscribed_tracks"
_61
}
_61
}

Fetching a Participant's Subscribed Track Instance

fetch-st-example page anchor

For executing this example the following is required:

  • The Twilio Access Key SID and Secret ( SKXXXX:your_api_key_secret )
  • The Room SID or name ( RMXXXX )
  • The Participant SID or identity ( PAXXXX )
  • The subscribed Track SID ( MTXXXX )

Fetching a Participant's Subscribed Track Instance

fetching-a-participants-subscribed-track-instance page anchor
Node.js
Python
C#
Java
PHP
Ruby
curl

_23
// NOTE: This example uses the next generation Twilio helper library - for more
_23
// information on how to download and install this version, visit
_23
// https://www.twilio.com/docs/libraries/node
_23
_23
// Find your credentials at twilio.com/console
_23
// To set up environmental variables, see http://twil.io/secure
_23
const API_KEY_SID = process.env.TWILIO_API_KEY;
_23
const API_KEY_SECRET = process.env.TWILIO_API_KEY_SECRET;
_23
const ACCOUNT_SID = process.env.TWILIO_ACCOUNT_SID;
_23
_23
const Twilio = require('twilio');
_23
_23
const client = new Twilio(API_KEY_SID, API_KEY_SECRET, {accountSid: ACCOUNT_SID});
_23
_23
client.video.rooms('RMXXXX').participants.get('PAXXXX')
_23
.subscribedTracks.get('MTXXXX')
_23
.fetch()
_23
.then(subscription => {
_23
console.log('Read track subscription with sid = ' + subscription.sid);
_23
})
_23
.catch(error => {
_23
console.log('Error fetching subscribed track' + error)
_23
});

Output

_12
{
_12
"kind": "video",
_12
"name": "1f6f07c7-7d02-4f4c-9caa-c7fdd6817d5a",
_12
"date_updated": null,
_12
"sid": "MTXXXX",
_12
"enabled": true,
_12
"room_sid": "RMXXXX",
_12
"url": "https://video.twilio.com/v1/Rooms/RMXXXX/Participants/PAXXXX/SubscribedTracks/MTXXXX",
_12
"date_created": "2019-04-30T12:26:07Z",
_12
"publisher_sid": "PAXYXY",
_12
"participant_sid": "PAXXXX"
_12
}


There are no current known issues.


Rate this page: