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