Suggestions

close search

Subscribing to streams — Web

Once 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:

Detecting when streams are created in a session

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.

Subscribing to a stream

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:

  • completionHandler— (Optional) A function that is called asymmetrically 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:

    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.

    Unsubscribing from a stream

    To stop playing a stream you are subscribed to, pass the Stream object into the unsubscribe() method of the Session object:

    session.unsubscribe(stream);
    

    The Subscriber object is destroyed, and the stream display is removed from the HTML DOM.

    Detecting when streams leave a session

    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.

    Automatic reconnection

    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.
      }
    );
    

    Restricting the frame rate of a subscribed stream

    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.

    For more information about restricting subscriber bandwidth, see this blog post.

    Detecting when a subscriber's video is disabled

    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 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:

    The Subscriber dispatches a videoEnabled event and 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.
    });
    

    To prevent video from resuming, in the videoEnabled event listener, call subscribeToVideo(false) on the Subscriber object:

    subscriber.on("videoEnabled", function(event) {
      subscriber.subscribeToVideo(false);
    });
    

    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:

    Detecting when a subscriber's stream's video dimensions change

    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.
    });
    

    Getting information about a stream

    The Stream object has the following properties that define the stream:

    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);
    }
    

    Troubleshooting

    Follow the tips in this section to avoid connectivity issues when subscribing. For general information on troubleshooting, see Debugging — Web.

    Handling Errors

    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.');
      }
    });
    

    Losing Connectivity

    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
          }
        }
      }
    });