Suggestions

close search

Add Messaging, Voice, and Authentication to your apps with Vonage Communications APIs

Visit the Vonage API Developer Portal

Subscribing to streams — Web

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:

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

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

    Unsubscribing from a stream

    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.

    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.

    Detecting when a subscriber's audio is blocked or unblocked

    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:

    For more information, see this Mozilla article about autoplay in Firefox and this Google article about autoplay in Chrome.

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

    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:

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

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

    Setting the preferred frame rate and resolution

    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 Session.subscribe() method. We recommend setting 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 Subscriber.setPreferredFrameRate() and 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).

    Applying filters and effects to subscribed audio and video

    You can apply filters and effects on audio or video tracks for a subscribed stream — see this topic.

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