Suggestions

close search

Publishing streams — Web

Once you have connected to a session, you can publish a stream that other clients connected to the session can view.

This topic includes the following sections:

Checking whether a client has publish capabilities

Once you have connected to a session, you can check if the client can publish. Check the value of the capabilities.publish property of the Session object. If it is set to 1, the client can publish:

if (session.capabilities.publish == 1) {
    // The client can publish. See the next section.
} else {
    // The client cannot publish.
    // You may want to notify the user.
}

To publish, the client must connect to the session with a token that is assigned a role that supports publishing. There must be a connected camera and microphone. Also, the client environment must support publishing. (Currently, publishing is not supported when running in the browser on Android OS.)

Also, in Google Chrome, publishing is only supported on HTTPS pages. (See this announcement.)

Initializing a Publisher view

The OT.initPublisher() method initializes and returns a Publisher object. The Publisher object represents the view of a video you publish:

var publisher;
var targetElement = 'publisherContainer';

publisher = OT.initPublisher(targetElement, null, function(error) {
  if (error) {
    // The client cannot publish.
    // You may want to notify the user.
  } else {
    console.log('Publisher initialized.');
  }
});

The OT.initPublisher() method takes two parameters:

You can pass this Publisher object into the Session.publish() method to publish a stream to a session. See Publishing a stream.

Before calling Session.publish(), you can use this Publisher object to test the microphone and camera attached to the Publisher.

The insertMode property of the properties parameter of the OT.initPublisher() 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 Publisher object as a child of a publisherContainer DOM element:

// Try setting insertMode to other values: "replace", "after", or "before":
var publisherProperties = {insertMode: "append"};
var publisher = OT.initPublisher('publisherContainer', publisherProperties, function (error) {
  if (error) {
    console.log(error);
  } else {
    console.log("Publisher initialized.");
  }
});

Detecting when a client has granted access to the camera and microphone

Before a Publisher object can access the client's camera and microphone, the user must grant access to them. The Publisher object dispatches events when the user grants or denies access to the camera and microphone:

publisher.on({
  accessAllowed: function (event) {
    // The user has granted access to the camera and mic.
  },
  accessDenied: function accessDeniedHandler(event) {
    // The user has denied access to the camera and mic.
  }
});

Also, a Publisher object dispatches events when the user is presented with the option to allow or deny access to the camera and microphone:

publisher.on({
  accessDialogOpened: function (event) {
    // The Allow/Deny dialog box is opened.
  },
  accessDialogClosed, function (event) {
    // The Allow/Deny dialog box is closed.
  }
});

The Publisher has an accessAllowed property, which indicates whether a client has (true) or has not (false) granted access to the camera and microphone.

Setting the camera and microphone used by the publisher

You can (optionally) specify an audio and video input device for the publisher to use. When you call the OT.initPublisher() method, you can (optionally) set the audioSource and videoSource properties of the properties object passed into the OT.initPublisher() method.

First, use the OT.getDevices() method to enumerate available devices. The array of devices is passed in as the devices parameter of the callback function passed into the OT.getDevices() method. For example, the following code gets a list of audio and video input devices:

var audioInputDevices;
var videoInputDevices;
OT.getDevices(function(error, devices) {
  audioInputDevices = devices.filter(function(element) {
    return element.kind == "audioInput";
  });
  videoInputDevices = devices.filter(function(element) {
    return element.kind == "videoInput";
  });
  for (var i = 0; i < audioInputDevices.length; i++) {
    console.log("audio input device: ", audioInputDevices[i].deviceId);
  }
  for (i = 0; i < videoInputDevices.length; i++) {
    console.log("video input device: ", videoInputDevices[i].deviceId);
  }
});

Each device listed by OT.getDevices() has a unique device ID, set as the deviceId property. You can use these device ID values as the audioSource and videoSource properties of the properties object passed into OT.initPublisher():

var pubOptions =
  {
    audioSource: audioInputDevices[0].deviceId,
    videoSource: videoInputDevices[0].deviceId
  };
var publisher = OT.initPublisher(null, pubOptions, function(error) {
  console.log("OT.initPublisher error: ", error);
});

Set the videoSource property to null or false in a voice-only session (see Publishing in a voice session).

The OpenTok hardware set-up component provides a user interface for clients to select the camera and microphone to use. It is built using the OT.getDevices() method.

Note that you can also publish a screen-sharing stream — one in which the source is the client's screen, not a camera. For details, see Screen sharing.

Remembering the camera and microphone selection

For security in pages loaded over HTTP, all browsers always prompt the user to select the camera and microphone used to publish a stream.

In pages loaded over HTTPS in Chrome, the user's camera and microphone selection is remembered and reused on subsequent visits to a page loaded from the same HTTPS domain.

In pages loaded over HTTPS in Firefox, the user has an option to remember the camera and microphone (in subsequent visits to a page loaded from the same HTTPS domain) when selecting the devices.

In pages loaded over HTTPS in IE, you can use the user's previous camera and microphone selection from previous usage to the same HTTPS domain (if there was any), by setting the usePreviousDeviceSelection property to true in the options you pass into the OT.initPublisher() method:

var pubOptions = {usePreviousDeviceSelection: true};
var publisher = OT.initPublisher(null, pubOptions, function(error) {
  console.log("OT.initPublisher error: ", error);
});

To prompt the user to select the camera and microphone to use in IE (and ignore previous device selections), do not set the usePreviousDevices property in the options you pass into the OT.initPublisher() method (or set it to false, the default).

Publishing a stream

Once you create a Publisher object (See Initializing a publisher view), you can pass it into the publish() method of a Session object to publish a stream to the session:

    publisher = OT.initPublisher('replacementElementId');
    session.publish(publisher, function(error) {
      if (error) {
        console.log(error);
      } else {
        console.log('Publishing a stream.');
      }
    });

The second element is a completion handler function that is passed an error object if publishing fails. Otherwise the completion handler function is called with no error passed in.

This code assumes that session is a Session object, and that the client has connected to the session. For more information, see Joining a Session.

The Publish object dispatches a streamCreated event when it starts streaming to the session:

var publisher = OT.initPublisher();
session.publish(publisher, function(error) {
  if (error) {
    console.log(error);
  } else {
    console.log('Publishing a stream.');
  }
});
publisher.on('streamCreated', function (event) {
    console.log('The publisher started streaming.');
});

The Publisher object has an element property, which is set to the HTML DOM element containing it.

Stopping a publisher from streaming to a session

You can stop publisher from streaming to the session by calling the unpublish() method of the Session object:

    session.unpublish(publisher);

Note that you can individually stop sending video or audio (while still publishing). For more information, see Adjusting audio and video.

Detecting when a published stream leaves a session

The Publisher object dispatches a streamDestroyed event when it stops streaming to the session:

var publisher = OT.initPublisher();
session.publish(publisher);
publisher.on("streamDestroyed", function (event) {
  console.log("The publisher stopped streaming. 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 Publisher dispatches the streamDestroyed event, the Publisher is destroyed and removed from the HTML DOM. You can prevent this default behavior by calling the preventDefault() method of the StreamEvent object:

publisher.on("streamDestroyed", function (event) {
    event.preventDefault();
    console.log("The publisher stopped streaming.");
});

You may want to prevent the default behavior, and retain the Publisher, if you want to reuse the Publisher object to publish again to the session.

The Publisher also 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 publisher that was removed.

Setting the video resolution of a stream

To set a recommended video resolution for a published stream, set the resolution property of the properties parameter you pass into the OT.initPublisher() method:

var publisherProperties = {resolution: '1280x720'};
var publisher = OT.initPublisher(targetElement,
                                 publisherProperties);
publisher.on('streamCreated', function(event) {
   console.log('Stream resolution: ' +
     event.stream.videoDimensions.width +
     'x' + event.stream.videoDimensions.height);
});

This resolution property is a string, defining the desired resolution of the video. The format of the string is "widthxheight", where the width and height are represented in pixels. Valid values are "1280x720", "640x480", and "320x240".

The requested resolution of a video stream is set as the videoDimensions.width and videoDimensions.height properties of the Stream object.

The default resolution for a stream (if you do not specify a resolution) is 640x480 pixels. If the client system cannot support the resolution you requested, the stream will use the next largest setting supported.

The actual resolution used by the Publisher is returned by the videoHeight() and videoWidth() methods of the Publisher object. The actual resolution of a Subscriber video stream is returned by the videoWidth() and videoHeight() methods of the Subscriber object. These may differ from the values of the resolution property passed in as the properties property of the OT.initPublisher() method, if publishing the browser does not support the requested resolution.

Setting the frame rate of a stream

To set a recommended frame rate for a published stream, set the frameRate property of the properties parameter you pass into the OT.initPublisher() method:

var publisherProperties = {frameRate: 7};
var publisher = OT.initPublisher(targetElement,
                                 publisherProperties);
publisher.on('streamCreated', function(event) {
   console.log('Frame rate: ' + event.stream.frameRate);
});

Set the value to the desired frame rate, in frames per second, of the video. Valid values are 30, 15, 7, and 1.

If the publisher specifies a frame rate, the actual frame rate of the video stream is set as the frameRate property of the Stream object, though the actual frame rate will vary based on changing network and system conditions. If you do not specify a frame rate when you call OT.initPublisher, this property is undefined.

For sessions that use the OpenTok Media Router (sessions with the media mode set to routed), lowering the frame rate proportionally reduces the maximum bandwidth the stream can use. However, in session with the media mode set to relayed, lowering the frame rate does not reduce the stream's bandwidth.

You can also restrict the frame rate of a Subscriber's video stream. For more information, see Restricting the frame rate of a subscribed stream.

Deleting a Publisher

You can delete a Publisher by calling its destroy() method:

    publisher.destroy();

Calling the destroy() method deletes the Publisher object and removes it from the HTML DOM.

Testing a publisher's stream

You can publish a test stream and check its audio and video statistics to determine the type of stream (such as high-resolution or audio-only) supported by your connection.

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. You can then use the getStats() method of the Subscriber object to get audio and video statistics for the stream you publish. See this topic for more information.

The opentok-network-test repo includes sample code for showing how to use statistics of a test stream before publishing to a session.

Best practices when publishing

This section includes tips for successfully publishing streams.

Allowing Device Access

It is best practice to let your users know that they are going to be asked to allow access to their camera and microphone. We find that by far the largest number of failures to publish are a result of users clicking the "deny" button or not clicking the allow button at all. We provide you with all of the events you need to be able to guide your users through this process:

publisher.on({
  accessDialogOpened: function (event) {
    // Show allow camera message
    pleaseAllowCamera.style.display = 'block';
  },
  accessDialogClosed: function (event) {
    // Hide allow camera message
    pleaseAllowCamera.style.display = 'none';
  }
});

It is also a good idea to serve your website over SSL. This is because Chrome only requires users to click to allow access to devices once per domain if that domain is served over SSL. This means that your users (if on Chrome) don't have to deal with that inconvenient allow/deny dialog box every time they load the page.

Split OT.initPublisher() and Session.publish()

Another thing we recommend is splitting the OT.initPublisher() and Session.publish() steps. This speeds up the initial connect time because you're connecting to the session while you're waiting for the user to click the allow button. So instead of:

session.connect(token, function (err) {
{... your error handling code ...}
if (!err) {
    var publisher = OT.initPublisher();
    session.publish(publisher);
  }
});

Move the OT.initPublisher() step to before you connect, as in the following:

var publisher = OT.initPublisher();
session.connect(token, function (err) {
{... your error handling code ...}
  if (!err) {
    session.publish(publisher);
  }
});

Resolution and frame rate

You can set the resolution and frame rate of the Publisher when you initialize it:

OT.initPublisher(divId, {
  resolution: '320x240',
  frameRate: 15
});

By default the resolution of a Publisher is 640x480, but you can set it to 1280x720 or 320x240 as well. It is best to try to match the resolution to the size that the video will be displayed. If you are only displaying the video at 320x240 pixels then there is no point in streaming at 1280x720. Reducing the resolution can save bandwidth and reduce congestion and connection drops.

By default the frame rate of the video is 30 frames per second, but you can set it to 15, 7, or 1 as well. Reducing the frame rate can reduce the bandwidth required. Smaller resolution videos can have a lower frame rate without as much of a perceived difference to the user. So if you are using a low resolution, you might also want to think about using a low frame rate.

For more information, see the documentation for OT.initPublisher().

Troubleshooting

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

Handling Errors

There are callback methods for both Session.publish() and OT.initPublisher(). We recommend handling the error responses to both of these methods. As mentioned earlier, it is best to split up these steps and call OT.initPublisher() before you have started connecting to your Session. It also makes error handling easier if you are not calling both of these methods at the same time. This is because both error handlers will fire if there is any error publishing. It is best to wait for OT.initPublisher() to complete and Session.connect() to complete and then call Session.publish(). This way you can handle all hardware related issues in the OT.initPublisher() callback and all network related issues in the Session.publish() callback.

var connected = false,
  publisherInitialized = false;

var publisher = OT.initPublisher(function(err) {
  if (err) {
    // handle error
  } else {
    publisherInitialized = true;
    publish();
  }
});

var publish = function() {
  if (connected && publisherInitialized) {
    session.publish(publisher);
  }
};

session.connect(token, function(err) {
  if (err) {
    // handle error
  } else {
    connected = true;
    publish();
  }
});

Access Denied

The highest number of failures to OT.initPublisher() are a result of the end-user denying access to the camera and microphone. This can either be handled by listening for the accessDenied event or by listening for an error response to the OT.initPublisher() method with a code property set to 1500 and a message property set to "Publisher Access Denied:". We recommend that you handle this case and surface a message to the user indicating that they should try to publish again and allow access to the camera.

publisher.on({
  'accessDenied': function() {
    showMessage('Please allow access to the Camera and Microphone and try publishing again.');
  }
});

Device Access

Another reason for OT.initPublisher() to fail is if OpenTok cannot get access to a camera or microphone. This can happen if there is no camera or microphone attached to the machine, if there is something wrong with the driver for the camera or microphone, or if some other application is using the camera or microphone (this only happens in Windows). You can try to minimize the occurrence of these issues by using our Hardware Setup Component or by calling the OT.getDevices() method directly. However you should also handle any error when calling OT.initPublisher() because something could still go wrong. For example, the user could have denied access to the camera or microphone. In this case, the error.name property is set to "OT_USER_MEDIA_ACCESS_DENIED":

publisher = OT.initPublisher('publisher', {}, function (err) {
  if (err) {
    if (err.name === 'OT_USER_MEDIA_ACCESS_DENIED') {
      // Access denied can also be handled by the accessDenied event
      showMessage('Please allow access to the Camera and Microphone and try publishing again.');
    } else {
      showMessage('Failed to get access to your camera or microphone. Please check that your webcam'
        + ' is connected and not being used by another application and try again.');
    }
    publisher.destroy();
    publisher = null;
  }
});

Network Errors

The other reasons for failures in publishing are usually due to some kind of network failure. We handle these in the callback to Session.publish(). If the user is not connected to the network, the callback function is passed an error object with the name property set to "OT_NOT_CONNECTED". If the user is on a really restrictive network connection that does not allow for WebRTC connections, the Publisher fails to connect, and the Publisher element will just display a spinning wheel. This error has an name property set to "OT_CREATE_PEER_CONNECTION_FAILED". In this case recommend that you surface a message to the user indicating that they failed to publish and that they should check their network connection. Handling these errors looks like this:

session.publish(publisher, function(err) {
  if (err) {
    switch (err.name) {
      case "OT_NOT_CONNECTED":
        showMessage("Publishing your video failed. You are not connected to the internet.");
        break;
      case "OT_CREATE_PEER_CONNECTION_FAILED":
        showMessage("Publishing your video failed. This could be due to a restrictive firewall.");
        break;
      default:
        showMessage("An unknown error occurred while trying to publish your video. Please try again later.");
    }
    publisher.destroy();
    publisher = null;
  }
});

Losing Connectivity

Your Publisher 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. You can handle the Publisher disconnecting by listening for the streamDestroyed event with a reason property set to "networkDisconnected" like so:

publisher.on({
  streamDestroyed: function (event) {
    if (event.reason === 'networkDisconnected') {
      showMessage('Your publisher lost its connection. Please check your internet connection and try publishing again.');
    }
  }
});

Putting it all together

The following code creates a publisher, connects to a session (see Session basics), publishes a stream to the session when the client connects to the session, and detects when the publisher starts and stops streaming:

var session;
var publisher;

// Replace with the replacement element ID:
publisher = OT.initPublisher(replacementElementId);
publisher.on({
  streamCreated: function (event) {
    console.log("Publisher started streaming.");
  },
  streamDestroyed: function (event) {
    console.log("Publisher stopped streaming. Reason: "
      + event.reason);
  }
});

// Replace apiKey and sessionID with your own values:
session = OT.initSession(apiKey, sessionID);
// Replace token with your own value:
session.connect(token, function (error) {
  if (session.capabilities.publish == 1) {
    session.publish(publisher);
  } else {
    console.log("You cannot publish an audio-video stream.");
  }
});