Once you have connected to a session, you can subscribe to streams in the session. When you subscribe to a stream, its video stream appears in the client page and its audio is played.
This topic includes the following sections:
The Session object dispatches a streamCreated
event when a new stream (other than
your own) is created in a session. A stream is created when a client
publishes a stream to the session. The
streamCreated
event is also dispatched for each existing stream in the session when
you first connect. This event is defined by the StreamEvent, which has a stream
property, representing stream that was created:
session.on("streamCreated", function (event) {
console.log("New stream in the session: " + event.stream.streamId);
});
// Replace with a valid token:
session.connect(token);
You can subscribe to any stream. See the next section.
To subscribe to a stream, pass the Stream object into the subscribe
method of the Session object:
session.subscribe(stream, replacementElementId);
The subscribe()
method takes the following parameters:
stream
The Stream object.targetElement
(Optional) Defines the DOM element that the Subscriber
video replaces.properties
(Optional) A set of properties that customize the appearance of the Subscriber
view in the HTML page (see Customizing the UI)
and select whether to subscribe to audio and video (see Adjusting
audio and video).completionHandler
(Optional) A function that is called
asynchronously when the call to the subscribe()
method completes successfully
or fails. If the call to the subscribe()
method fails, the completion
handler is passed an error object. This object has a code
and
message
property that describe the error.The following code subscribes to all streams, other than those published by your client:
session.on("streamCreated", function(event) {
session.subscribe(event.stream);
});
// Replace with your API key and token:
session.connect(token, function (error) {
if(error) {
// failed to connect
}
});
The insertMode
property of the properties
parameter of the
Session.subscribe()
method specifies how the Publisher object will be inserted
in the HTML DOM, in relation to the targetElement
parameter. You can set
this parameter to one of the following values:
"replace"
The Subscriber object replaces contents of the
targetElement. This is the default."after"
The Subscriber object is a new element inserted after the
targetElement in the HTML DOM. (Both the Subscriber and targetElement have the same parent
element.)"before"
The Subscriber object is a new element inserted before the
targetElement in the HTML DOM. (Both the Subscriber and targetElement have the same parent
element.)"append"
The Subscriber object is a new element added as a child of
the targetElement. If there are other child elements, the Publisher is appended as the last
child element of the targetElement.
For example, the following code adds a new Subscriber object as a child of a
subscriberContainer
DOM element:
session.on('streamCreated', function(event) {
var subscriberProperties = {insertMode: 'append'};
var subscriber = session.subscribe(event.stream,
'subscriberContainer',
subscriberProperties,
function (error) {
if (error) {
console.log(error);
} else {
console.log('Subscriber added.');
}
});
});
The Subscriber object has an element
property, which is set to the HTML DOM element
containing it.
If you do not want to use the default UI, you access the Video
element for the Subscriber (see this topic). You can also use your own Video
element to display the Subscriber video, and use the Subscriber's MediaStream object as the media source for that Video
element (see this topic).
To stop playing a stream you are subscribed to, pass the Subscriber object
into the unsubscribe()
method of the Session object:
session.unsubscribe(subscriber);
The Subscriber object is destroyed, and the stream display is removed from the HTML DOM.
When a stream, other than your own, leaves a session the Session object dispatches a
streamDestroyed
event:
session.on("streamDestroyed", function (event) {
console.log("Stream stopped. Reason: " + event.reason);
});
When a stream you publish leaves a session the Publisher object dispatches a
streamDestroyed
event:
var publisher = OT.initPublisher();
publisher.on("streamDestroyed", function (event) {
console.log("Stream stopped. Reason: " + event.reason);
});
The streamDestroyed
event is defined by the StreamEvent class. The event includes a reason
property, which details why the stream ended. These reasons include "clientDisconnected"
,
"forceDisconnected"
, "forceUnpublished"
, or "networkDisconnected"
. For details,
see StreamEvent.
By default, when a streamDestroyed
event is dispatched for a stream you are subscribed to, the corresponding
Subscriber objects (there could be more than one) are destroyed and removed from the HTML DOM. You can prevent this
default behavior by calling the preventDefault()
method of the StreamEvent object:
session.on("streamDestroyed", function (event) {
event.preventDefault();
var subscribers = session.getSubscribersForStream(event.stream);
// Now you can adjust the DOM elements around each
// subscriber to the stream, and then delete it yourself.
});
Note that the getSubscribersForStream()
method of a Session object returns all of the Subscriber objects
for a Stream.
You may want to prevent the default behavior, and retain the Subscriber, if you want to adjust related DOM elements before
deleting the Subscriber yourself. You can then delete the Subscriber object (and its DOM element)
by calling the destroy()
method of the Subscriber object.
A Subscriber object dispatches a destroyed
event when the object has been removed
from the HTML DOM. In response to this event, you may choose to adjust (or remove) DOM elements
related to the subscriber that was removed.
If a client drops a connection to a subscribed stream (for example, due to a drop in network
connectivity in either client), it will attempt to automatically reconnect to the stream.
When the stream is dropped and the client tries to reconnect, the Subscriber object dispatches a
disconnected
event. When the stream is restored, the Subscriber object dispatches
a connected
event. If the client cannot restore the stream, the Subscriber object
dispatched a destroyed
event.
In response to these events, your application can (optionally) display user interface notifications indicating the temporary disconnection, reconnection, and destroyed states:
subscriber.on(
disconnected: function() {
// Display a user interface notification.
},
connected: function() {
// Adjust user interface.
},
destroyed: function() {
// Adjust user interface.
}
);
You can also restrict the frame rate of a Subscriber's video stream. To restrict the frame rate
of a subscriber, call the restrictFrameRate()
method of the Subscriber object,
passing in true
:
subscriber.restrictFrameRate(true);
Pass in false
and the frame rate of the video stream is not restricted:
subscriber.restrictFrameRate(false);
When the frame rate is restricted, the Subscriber video frame will update once or less per second.
This feature is only available in sessions that use the OpenTok Media Router (sessions with the media mode set to routed), not in sessions with the media mode set to relayed. In relayed sessions, calling this method has no effect.
Restricting the subscriber frame rate has the following benefits:
Reducing a subscriber's frame rate has no effect on the frame rate of the video in other clients.
Some browsers automatically block audio playback, requiring a click
event before audio playback starts for subscribers. These browsers include Safari,
Firefox 66+, and Chrome 71+.
The Subscriber object displays an audio playback button if audio playback is blocked. You can disable the Subscriber's default audio playback button and display your own UI element that the user will click to start audio playback. See Displaying a custom UI element when Subscriber audio is blocked.
When the subscriber's audio is blocked, the Subscriber object dispatches a
audioBlocked
event, and it dispatches an audioUnblocked
event when the audio is unblocked:
subscriber.on({
audioBlocked: function(event) {
console.log("Subscriber audio is blocked.")
},
audioUnblocked: function(event) {
console.log("Subscriber audio is unblocked.")
}
});
Also, the Subscriber includes an isAudioBlocked()
which returns
true
if the audio is blocked or false
if it is not.
Subscriber audio is unblocked when any of the following occurs:
click
event (if you have disabled the default
audio playback icon)
OT.initPublisher()
).
For more information, see this Mozilla article about autoplay in Firefox and this Google article about autoplay in Chrome.
When the subscriber's video is disabled, the Subscriber object dispatches a
videoDisabled
event:
subscriber.on("videoDisabled", function(event) {
// You may want to hide the subscriber video element:
domElement = document.getElementById(subscriber.id);
domElement.style["visibility"] = "hidden";
// You may want to add or adjust other UI.
});
When the OpenTok Media Router, or a fallback-enabled publisher, disables the video of a subscriber, you may want to adjust the user interface related to the subscriber.
The reason
property of the videoDisabled
event object defines the reason the video was disabled. This can be set to
one of the following values:
"publishVideo"
— The publisher stopped publishing video by calling
publishVideo(false)
."quality"
— The OpenTok Media Router, or the publishing client if
publisher audio fallback
is enabled, stopped sending video
to the subscriber based on stream quality changes. This feature of the OpenTok Media
Router has a subscriber drop the video stream when connectivity degrades. (The subscriber
continues to receive the audio stream, if there is one.) The publisher audio fallback feature
has the publisher stop publishing the video stream when publisher connectivity degrades,
and subsequently the subscriber drops the video stream.
Before sending this event, when the Subscriber's stream quality deteriorates,
or a fallback-enabled publisher's stream quality deteriorates, to a level
that is low enough that the video stream is at risk of being disabled, the Subscriber
dispatches a videoDisableWarning
event.
If connectivity improves to support video again, the Subscriber object dispatches
a videoEnabled
event, and the Subscriber resumes receiving video.
By default, the Subscriber displays a video disabled indicator when a
videoDisabled
event with this reason is dispatched and removes the indicator
when the videoDisabled
event with this reason is dispatched. You can control
the display of this icon by calling the setStyle()
method of the Subscriber,
setting the videoDisabledDisplayMode
property; or you can set the style when
calling the Session.subscribe()
method, setting the style
property
of the properties
parameter.
This feature is only available in sessions that use the OpenTok Media Router (sessions with the media mode set to routed), or in sessions with a fallback-enabled publisher. See the publisher fallback enabled docs.
When you publish a stream, you can prevent it from having its video disabled due to stream
quality. Set audioFallbackEnabled
to false
in the
properties
object passed into the
OT.initPublisher()
method (this feature will be deprecated), or set subscriber
to false
in the audioFallback
object
passed in as the properties
parameter of the
OT.initPublisher()
method.
"subscribeToVideo"
— The subscriber started or stopped subscribing to
video, by calling subscribeToVideo(false)
.
"codecNotSupported"
— The subscriber stopped subscribing to video due
to an incompatible codec (see the Video codecs developer
guide).
The Subscriber dispatches a videoEnabled
event when video resumes:
subscriber.on("videoEnabled", function(event) {
// You may want to display the subscriber video element,
// if it was hidden:
domElement = document.getElementById(subscriber.id);
domElement.style["visibility"] = "visible";
// You may want to add or adjust other UI.
});
The reason
property of the videoEnabled
event object defines the reason the video was enabled. This can be set to
one of the following values:
"publishVideo"
— The publisher started publishing video by calling
publishVideo(true)
."quality"
— The OpenTok Media Router, or the fallback-enabled publisher, resumed sending video
to the subscriber based on stream quality changes. This feature of the OpenTok Media
Router has a subscriber drop the video stream when connectivity degrades and then resume
the video stream if the stream quality improves. The publisher audio fallback feature
has the publisher stop publishing the video stream when publisher connectivity degrades,
and subsequently the subscriber drops the video stream.
This feature is only available in sessions that use the OpenTok Media Router (sessions with the media mode set to routed), or in sessions with a fallback-enabled publisher.
"subscribeToVideo"
— The subscriber started or stopped subscribing to
video, by calling subscribeToVideo(false)
.
"codecChanged"
— The subscriber video was enabled after a codec change
from an incompatible codec (see the Video codecs
developer guide).
The stream of a subscriber's video dimensions can change if a stream published from a mobile
device resizes, based on a change in the device orientation. It can also occur if the video
source is a screen-sharing window and the user publishing the stream resizes the window that
is the source for the stream. When the video dimensions change, the Subscriber object dispatches
a videoDimensionsChanged
event.
The following code resizes a subscriber when the stream's video dimensions change:
subscriber.on('videoDimensionsChanged', function(event) {
subscriber.element.style.width = event.newValue.width + 'px';
subscriber.element.style.height = event.newValue.height + 'px';
// You may want to adjust other UI.
});
The Stream object has the following properties that define the stream:
connection
The Connection object corresponding to the connection that is publishing the stream. You can compare this to the connection
property of the Session object to see if the stream is being published by the local web page.
creationTime
The timestamp (a number) for the creation of the stream. This value is calculated in milliseconds.
You can convert this value to a Date object by calling new Date(stream.creationTime)
.
hasAudio
(Boolean) Whether the stream has audio. This property can change if the publisher turns on or off audio (by calling Publisher.publishAudio()). When this occurs, the Session object dispatches a streamPropertyChanged
event.
hasVideo
(Boolean) Whether the stream has video.
initials
(Boolean) The initials for the stream (if initials were set when the stream's publisher
was initialized).
name
(String) The name of the stream. This is, by default, displayed when the user mouses over the
Subscriber in the HTML DOM. You can, however, customize the UI to hide the name or display it without mousing over.
videoDimensions
This object has two properties: width
and height
. Both are numbers. The width
property is the width of the encoded stream; the height
property is the height of the encoded stream. (These are independent of the actual width of Publisher and Subscriber objects corresponding to the stream.) This property can change if a stream published from an iOS device resizes, based on a change in the device orientation.
videoType
The type of video: either "camera", "screen", "custom", or undefined. A "screen" video
uses screen sharing on the publisher as the video source; a "custom" video uses
a VideoTrack element as the video source on the publisher.
The videoType
is undefined
when a stream is voice-only
(see the Voice-only guide).
This property can change if a stream published from a mobile device changes
from a camera to a screen-sharing video type. For more information, see
Screen sharing — Web.
The hasAudio
, hasVideo
, videoDimensions
, and
videoType
properties can change (for example,
when the publisher turns on or off video). When this occurs, the Session
object dispatches a streamPropertyChanged
event (see
StreamPropertyChangedEvent.)
The getStats()
method of a Subscriber object provides you with information about the
subscriber's stream, including the following:
The following code logs the audio packet loss ratio, the audio bit rate, and the video packet loss ratio, and the video bit rate for a subscriber every second:
var prevStats;
window.setInterval(function() {
subscriber.getStats(function(error, stats) {
if (error) {
console.error('Error getting subscriber stats. ', error.message);
return;
}
if (prevStats) {
var videoPacketLossRatio = stats.video.packetsLost /
(stats.video.packetsLost + stats.video.packetsReceived);
console.log('video packet loss ratio: ', videoPacketLossRatio);
var videoBitRate = 8 * (stats.video.bytesReceived - prevStats.video.bytesReceived);
console.log('video bit rate: ', videoBitRate, 'bps');
var audioPacketLossRatio = stats.audio.packetsLost /
(stats.audio.packetsLost + stats.audio.packetsReceived);
console.log('audio packet loss ratio: ', audioPacketLossRatio);
var audioBitRate = 8 * (stats.audio.bytesReceived - prevStats.audio.bytesReceived);
console.log('audio bit rate: ', audioBitRate, 'bps');
}
prevStats = stats;
});
}, 1000);
To get statistics for a stream published by the local client, you must use a session
that uses the OpenTok Media Router (sessions with the
media mode set to routed), and
you must set the testNetwork
property to true
in the
options
object you pass into the Session.subscribe()
method:
var publisher = OT.initPublisher();
publisher.on('streamCreated', function(event) {
var subscriberOptions = {testNetwork: true};
var subscriber = session.subscribe(event.stream, 'publisher-element', subscriberOptions);
}
To get more detailed stream statics, use the Subscriber.getRtcStatsReport()
method.
It returns a promise that, on success, resolves with an
RtcStatsReport
object for the subscribed stream:
subscriber.getRtcStatsReport()
.then((stats) => stats.forEach(console.log))
.catch(console.log);
When subscribing to a stream that uses the
scalable video feature, you have an option to set preferredResolution
to
"auto"
to automatically manage Subscriber video resolution based on the size being rendered
to optimize the network and CPU usage. For advanced users, you can also manually set the preferred frame rate and
resolution for the stream the subscribing client receives from
the OpenTok Media Router. You can set these as the preferredFrameRate
and preferredResolution
properties of the options
you pass into the
method.
We recommend setting Session.subscribe()
preferredResolution
to "auto"
. With the
"auto"
setting, OpenTok.js selects the preferred resolution based on the
dimensions of the Subscriber video in the browser. You can also set the preferred frame rate
and resolution after subscribing to a stream (see
and
Subscriber.setPreferredFrameRate()
Subscriber.setPreferredResolution()
).
Note: The "auto"
resolution setting only applies when you use the default Subscriber Video element created by the SDK. It does not work if you create your own Video element in response to the videoElementCreated
event (see this topic).
You can apply filters and effects on audio or video tracks for a subscribed stream — see this topic.
Follow the tips in this section to avoid connectivity issues when subscribing. For general information on troubleshooting, see Debugging — Web.
Handling errors when subscribing is a bit easier than when publishing. There is only one way to
subscribe—with the Session.subscribe()
method—and pretty much any error that happens
when subscribing comes down to a network issue. This can happen if, for example, the user is on a really
restrictive network connection that does not allow for WebRTC connections (but the WebSocket
connection worked). If the Subscriber fails to connect it will just display its own error message
inside the Subscriber. It doesn't look particularly nice and isn't very informative to the end user.
We recommend that you handle this case yourself and surface a message to the user indicating that
they failed to subscribe and that they should check their network connection. Handling these errors
looks like this:
session.subscribe(event.stream, 'subscriber', {insertMode: 'append'}, function (err) {
if (err) {
showMessage('Streaming connection failed. This could be due to a restrictive firewall.');
}
});
Your Subscriber can also lose its connection after it has already succeeded in connecting. More
often than not, this will also result in the Session losing its connection, but that's not always
the case. Also, it may be that the Publisher on the other side has lost its connection rather than
the connection being lost locally. You can handle the Subscriber disconnecting by listening for the
streamDestroyed
event on the Session with a reason
property set to "networkDisconnected",
like this:
session.on({
streamDestroyed: function (event) {
if (event.reason === 'networkDisconnected') {
event.preventDefault();
var subscribers = session.getSubscribersForStream(event.stream);
if (subscribers.length > 0) {
var subscriber = document.getElementById(subscribers[0].id);
// Display error message inside the Subscriber
subscriber.innerHTML = 'Lost connection. This could be due to your internet connection '
+ 'or because the other party lost their connection.';
event.preventDefault(); // Prevent the Subscriber from being removed
}
}
}
});