HLS Plugin

The HLS plugin is a Brightcove Player plugin that relies on W3C's Media Source Extensions (MSE) to play HTTP Live Streaming (HLS) video on platforms that do not natively support it. The HLS plugin is automatically included in the player.

HLS plugin basics

The following points help you understand and use the HLS plugin:

  • As mentioned in the opening sentence of this document, the plugin relies on W3C's Media Source Extensions (MSE). MSE is a W3C specification that allows JavaScript to send byte streams to media codecs within web browsers that support HTML5 video. Among other possible uses, this allows the implementation of client-side prefetching and buffering code for streaming media entirely in JavaScript.
  • With the plugin you can then use HLS (m3u8) video content in the player. For instance you could create a player using this configuration for the media section:
    "media":{
        "sources": [{
            "src": "http://example.com/video.m3u8",
            "type": "application/x-mpegURL"
        }]
    }
  • Cross-origin resource sharing (CORS) can be an issue when using HLS. For more information about using CORS, see the CORS guide.
  • HLS is not supported on versions of IE earlier than IE11.

Overview

HTTP Live Streaming (HLS) has become a de-facto standard for streaming video on mobile devices thanks to its native support on iOS and Android. Although, there are a number of reasons independent of platform to recommend the format:

  • Supports (client-driven) adaptive bitrate selection
  • Delivered over standard HTTP ports
  • Simple, text-based manifest format
  • No proprietary streaming servers required

Unfortunately, all the major desktop browsers except for Safari are missing HLS support. That leaves web developers in the unfortunate position of having to maintain alternate renditions of the same video and potentially having to forego HTML-based video entirely to provide the best desktop viewing experience.

This plugin addresses the situation by providing a polyfill for HLS on browsers that have Media Source Extensions or Flash support. You can deploy a single HLS stream, code against the regular HTML5 video APIs, and create a fast, high-quality video experience across all the big web device categories.

Currently, at this time there is no support for:

  • Alternate audio and video tracks
  • Segment codecs other than H.264 with AAC audio
  • Internet Explorer < 11

Options

There are several options you can use to configure the HLS plugin.

withCredentials

Type: boolean

Can be used as:

  • a source option
  • an initialization option

When the withCredentials property is set to true, all XHR requests for manifests and segments would have withCredentials set to true as well. This enables storing and passing cookies from the server that the manifests and segments live on. This has some implications on CORS because when set, the Access-Control-Allow-Origin header cannot be set to *, also, the response headers require the addition of Access-Control-Allow-Credentials header which is set to true. See the HTML5Rocks's article for more info.

You can configure the plugin using the Player Management API using an HTTP PATCH method, as shown here:

curl \
    --header "Content-Type: application/json" \
    --user YOUR_EMAIL \
    --request PATCH \
    --data '{ "hls": { "withCredentials": true } }' \
https://players.api.brightcove.com/v2/accounts/YOUR_ACCOUNT_ID/players/YOUR_PLAYER_ID/configuration

You can also set the withCredentials option on a per source basis, rather than on a player basis as just shown. For instance, when setting the source you can include withCredentials, as shown here:

curl \
  --header "Content-Type: application/json" \
  --user $EMAIL \
  --request POST \
  --data '{
      "name": "MySamplePlayer",
      "configuration": {
        "media": {
          "sources": [{
            "src":"http://solutions.brightcove.com/bcls/assets/videos/Tiger.mp4",
            "type":"video/mp4",
            "withCredentials": true
          }]
        }
      }
    }' \
https://players.api.brightcove.com/v2/accounts/$ACCOUNT_ID/players

Runtime configuration

You can configure withCredentials at runtime. You will see below two implementations:

  • Using player.hls.xhr.beforeRequest
  • Using player.src()

In the following code you are using the player.hls.xhr.beforeRequest to assign a function that will be called with an object containing options that will be used to create the xhr request. In this example you see only withCredentials is being configured.

if (videojs.Hls) {
  videojs.Hls.xhr.beforeRequest = function (options) {
    options.withCredentials = true;
  }
}

You can also set the withCredentials options when setting the video source. You use the player.src() method, as shown here:

player.src({
  src: 'https://adomain.com/bipbop_16x9_variant.m3u8',
  type: 'application/x-mpegURL',
  withCredentials: true
});

enableLowInitialPlaylist

Type: boolean

Default: undefined, except when a browser is viewed on an Android device; then it is set to true. You can change this behavior for Android devices, by patching the player, as shown below, with a value of false.

Can be used as:

  • an initialization option

When enableLowInitialPlaylist is set to true, it will be used to select the lowest bitrate playlist initially. This helps to decrease playback start time.

You can configure the plugin using the Player Management API using an HTTP PATCH method, as shown here:

curl \
    --header "Content-Type: application/json" \
    --user YOUR_EMAIL \
    --request PATCH \
    --data '{ "hls": { "enableLowInitialPlaylist": true } }' \
https://players.api.brightcove.com/v2/accounts/YOUR_ACCOUNT_ID/players/YOUR_PLAYER_ID/configuration

Runtime properties

In general, you can access the HLS object this way:

  • Brightcove Player v5: player.hls
  • Brightcove Player v6: player.tech().hls

player.hls.playlists.master

Type: object

An object representing the parsed master playlist. If a media playlist is loaded directly, a master playlist with only one entry will be created.

player.hls.playlists.media

Type: function

A function that can be used to retrieve or modify the currently active media playlist. The active media playlist is referred to when additional video data needs to be downloaded. Calling this function with no arguments returns the parsed playlist object for the active media playlist. Calling this function with a playlist object from the master playlist or a URI string as specified in the master playlist will kick off an asynchronous load of the specified media playlist. Once it has been retrieved, it will become the active media playlist.

player.hls.bandwidth

Type: number

The number of bits downloaded per second in the last segment download. This value is used by the default implementation of selectPlaylist to select an appropriate bitrate to play. Before the first video segment has been downloaded, it's hard to estimate bandwidth accurately. The HLS tech uses a heuristic based on the playlist download times to do this estimation by default. If you have a more accurate source of bandwidth information, you can override this value as soon as the HLS tech has loaded to provide an initial bandwidth estimate.

player.hls.stats.bytesReceived

Type: number

The total number of content bytes downloaded by the HLS tech.

player.hls.selectPlaylist

Type: function

A function that returns the media playlist object to use to download the next segment. It is invoked by the plugin immediately before a new segment is downloaded. You can override this function to provide your adaptive streaming logic. You must, however, be sure to return a valid media playlist object that is present in player.hls.playlists.master.

Events

loadedmetadata

Fired after the first media playlist is downloaded for a stream.

loadedplaylist

Fired immediately after a new master or media playlist has been downloaded. By default, the plugin only downloads playlists as they are needed.

mediachange

Fired when a new playlist becomes the active media playlist. Note that the actual rendering quality change does not occur simultaneously with this event; a new segment must be requested and the existing buffer depleted first.

Reload source on error

When using the HLS plugin there is a method you can call that will reload the source at its current time when an error is emitted by the player. To turn on this feature you need to call the reloadSourceOnError() method. The following short video shows the method in action. All the code shown in the video is described later in this section.

The syntax for the reloadSourceOnError() method is as follows:

reloadSourceOnError(optionsObject)

The optional optionsObject has the following properties:

Property Data Type Default Value Description
errorInterval Number 30 The minimum amount of time (in seconds) that has to pass between two errors for the reload to trigger. For example, if you set the time to 10, each time an error occurs the function will check to see if a reload has happened less than 10 seconds ago. If less than the time interval has passed, it will NOT reload the source. (This is to ensure that content with an error doesn't reload constantly.) If more time than the interval specified has passed, the video is reloaded at the point when the error occurred.
getSource() Function Retrieves current source A function that is called to get a source object to load or reload. By default it gets the current source of the player.

The following details the code used in the video demonstration above:

  • Lines 1-9: Standard in-page embed code with a player id added.
  • Line 11: Button to manually create errors.
  • Lines 22-24: Function called on button click to dispatch an error.
  • Line 19: Create an object in which to place configuration options.
  • Line 20: In the configuration object, create an errorInterval property and assign it a value.
  • Line 21: Call the reloadSourceOnError() method, passing the configuration object as an argument.
<video-js id="myPlayerID"
  data-video-id="4607746980001"
  data-account="1507807800001"
  data-player="HJLp3Hvmg"
  data-embed="default"
  data-application-id=""
  controls=""
></video-js>

<p><button onclick="createError()">createError</button></p>

<script src="https://players.brightcove.net/1507807800001/HJLp3Hvmg_default/index.min.js"></script>

<script type="text/javascript">
  var createError;
  videojs.getPlayer('myPlayerID').ready(function() {
    var myPlayer = this,
      reloadOptions = {};
    reloadOptions.errorInterval = 10;
    myPlayer.reloadSourceOnError(reloadOptions);
    createError = function(){
      myPlayer.error({code:'2'});
    }
  });
</script>

In-Manifest WebVTT

The HLS plugin supports in-manifest WebVTT. There is nothing you need to do to enable this feature as it is standard in the plugin. Videos need to be ingested with in-manifest WebVTT considered. For instance, the Brightcove Dynamic Ingest API can ingest videos and configure captions as in-manifest. See the Overview: Dynamic Ingest API for Dynamic Delivery document for more details.

The player below is playing a video with in-manifest WebVTT captions. You can select the captions via the captions icon, as shown here:

display captions icon

After you start the video you will be able to choose the captions you wish to see.

Simply to see, as this is something you would not build yourself, here is the manifest for the video shown in the player above:

#EXTM3U
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Woofer",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="en",URI="subtitles/en/index.m3u8"
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Woofer (Forced)",DEFAULT=NO,AUTOSELECT=NO,FORCED=YES,LANGUAGE="en",URI="subtitles/en_forced/index.m3u8"
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Spanish",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="es",URI="subtitles/es/index.m3u8"
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Spanish (Forced)",DEFAULT=NO,AUTOSELECT=NO,FORCED=YES,LANGUAGE="es",URI="subtitles/es_forced/index.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=865000,CODECS="mp4a.40.2, avc1.42001e",RESOLUTION=640x360,SUBTITLES="subs"
865/prog_index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=12140000,CODECS="mp4a.40.2, avc1.42001e",RESOLUTION=1280x720,SUBTITLES="subs"
12140/prog_index.m3u8

In it you can see references to the captions file.

Adaptive switching

HLS rendition selection

During playback, the player will switch to a higher or lower rendition based on an algorithm. Inputs to this algorithm are:

  • Available bandwidth
  • Player dimensions

For a complete discussion of rendition selection, please see the Determining Which Rendition Will Play document.

MP4 rendition selection

If for some reason Brightcove Player cannot playback HLS sources, it will fallback to playing MP4. In this case, if viewing a video on a mobile device and playing an MP4, the player will choose the MP4 that has a bitrate closest to .5 MB/s. If on a desktop or laptop device it will choose the MP4 rendition that is closest to 3 MB/s.

In-band metadata

Brightcove Player will recognize certain types of ID3 tag information embedded in an HLS video stream. The ID3 standard was originally used to provide metadata about MP3 audio tracks. (The acronym is from IDentify MP3.) When a stream is encountered with embedded metadata, an in-band metadata text track will automatically be created and populated with cues as they are encountered in the stream. A common use case is the ID3 data would direct when advertisements should be shown in a live stream.

The ID3 standard defines many frame types, but only the following two UTF-8 encoded frames will be mapped to cue points and their values set as cue text:

  • WXXX - User defined URL link frame
  • TXXX - User defined text information frame

Cues are created for all other frame types and the data is attached to the generated cue:

cue.frame.data

For more information about text tracks in general see Getting Started With the Track Element. For information on Brightcove Player and cue points see Displaying Ads Using Ad Cue Points.

Debugging

The information in this section is supplied for you to gather information that can then be passed to Brightcove Support to help resolve any HLS issues. That being said, some of the data reported could be of interest to you.

Two methods and one property will be detailed that assist in HLS debugging.

Method: videojs.log.level()

The videojs.log.level() method gets or sets the current logging level. To turn on debugging you use:

videojs.log.level('debug');

Method: videojs.log.history()

The history() method returns an array containing everything that has been logged to the history.

Any message that gets logged through the videojs.log API will be appended to the history array. What information is placed into that array is dependent on what plugins are in use that use the API, and the state of the player. This means that the history can easily contain non-HLS information. An example display from the console of the history array follows:

console display of history array

If you need to send the history array to support, the best thing to do is at the console type the following:

JSON.stringify(videojs.log.history())

You will get information similar to what is shown here:

console display of history stringified

Copy the JSON that is generated and that can then be sent to support.

Property: player.tech().hls.stats

This object contains a summary of HLS and player related stats. The available properties are shown in the following table:

Property Name Type Description
bandwidth number Rate of the last segment download in bits/second
buffered array List of time ranges of content that are in the SourceBuffer
currentSource object The source object; has the structure {src: 'url', type: 'mimetype'}
currentTech string The name of the tech in use
currentTime number The current position of the player
duration number Duration of the video in seconds
master object The master playlist object
mediaBytesTransferred number Total number of content bytes downloaded
mediaRequests number Total number of media segment requests
mediaRequestsAborted number Total number of aborted media segment requests
mediaRequestsErrored number Total number of errored media segment requests
mediaRequestsTimeout number Total number of timedout media segment requests
mediaSecondsLoaded number Total number of content seconds downloaded
mediaTransferDuration number Total time spent downloading media segments in milliseconds
playerDimensions object Contains the width and height of the player
seekable array List of time ranges to which the player can seek
timestamp number Timestamp of when hls.stats was accessed
videoPlaybackQuality object Media playback quality metrics as specified by the W3C's Media Playback Quality API

An example display from the console of the stats object follows:

console display of stats object

Code example

If you wish to experiment with these debugging features, code based on the following can server as a starting point:

<video-js id="myPlayerID" data-video-id="5622718636001"
  data-account="1507807800001"
  data-player="SkxERgnQM"
  data-embed="default"
  data-application-id=""
  controls=""
  width="640"
  height="360"></video-js>
<script src="https://players.brightcove.net/1507807800001/SkxERgnQM_default/index.min.js"></script>

<script type="text/javascript">
  videojs.getPlayer('myPlayerID').ready(function() {
    var myPlayer = this;

    videojs.log.level('debug');

    myPlayer.on('ended', function(){
      console.log('videojs.log.history(): ', videojs.log.history());
      console.log('videojs.log.level(): ', videojs.log.level());
      console.log('videojs.hls.stats: ', player.tech().hls.stats);
    });
  });
</script>

608 captions

Brightcove's HLS plugin has support for 608 captions. 608 captions, also known as CEA-608 captions, EIA-608 captions and Line 21 captions, are a standard for closed captioning for NTSC TV analog broadcasts in the United States and Canada. The 608 captions can be inserted into a live stream where they are mixed into the HLS' ts (transport stream) files.

Hosting issues

Unlike a native HLS implementation, the HLS plugin has to comply with the browser's security policies. That means that all the files that make up the stream must be served from the same domain as the page hosting the video player or from a server that has appropriate CORS headers configured. Easy instructions are available for popular webservers and most CDNs should have no trouble turning CORS on for your account.

Errors

Errors during HLS playback will be reported using the type APPEND_BUFFER_ERR. The message will be what is retrieved from the browser's native error. For instance, The quota has been exceeded.

Changelog

HLS is now integrated into the player and changes to the plugin functionality will be reported in Brightcove Player release notes.

For historical release notes, see the changelog here.