UNPKG

24.1 kBMarkdownView Raw
1# video.js HLS Source Handler
2
3[![Build Status][travis-icon]][travis-link]
4[![Slack Status][slack-icon]][slack-link]
5
6
7Play back HLS with video.js, even where it's not natively supported.
8
9Lead Maintainer: Jon-Carlos Rivera [@imbcmdth](https://github.com/imbcmdth)
10
11Maintenance Status: Stable
12
13<!-- START doctoc generated TOC please keep comment here to allow auto update -->
14<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
15**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
16
17- [Installation](#installation)
18 - [NPM](#npm)
19 - [CDN](#cdn)
20 - [Releases](#releases)
21 - [Manual Build](#manual-build)
22- [Contributing](#contributing)
23- [Talk to us](#talk-to-us)
24- [Getting Started](#getting-started)
25 - [Video.js 6](#videojs-6)
26- [Documentation](#documentation)
27 - [Options](#options)
28 - [How to use](#how-to-use)
29 - [Initialization](#initialization)
30 - [Source](#source)
31 - [List](#list)
32 - [withCredentials](#withcredentials)
33 - [useCueTags](#usecuetags)
34 - [overrideNative](#overridenative)
35 - [blacklistDuration](#blacklistduration)
36 - [bandwidth](#bandwidth)
37 - [Runtime Properties](#runtime-properties)
38 - [hls.playlists.master](#hlsplaylistsmaster)
39 - [hls.playlists.media](#hlsplaylistsmedia)
40 - [hls.segmentXhrTime](#hlssegmentxhrtime)
41 - [hls.bandwidth](#hlsbandwidth)
42 - [hls.bytesReceived](#hlsbytesreceived)
43 - [hls.selectPlaylist](#hlsselectplaylist)
44 - [hls.representations](#hlsrepresentations)
45 - [hls.xhr](#hlsxhr)
46 - [Events](#events)
47 - [loadedmetadata](#loadedmetadata)
48 - [HLS Usage Events](#hls-usage-events)
49 - [Presence Stats](#presence-stats)
50 - [Use Stats](#use-stats)
51 - [In-Band Metadata](#in-band-metadata)
52 - [Segment Metadata](#segment-metadata)
53- [Hosting Considerations](#hosting-considerations)
54- [Known Issues](#known-issues)
55 - [IE10 and Below](#ie10-and-below)
56 - [IE11](#ie11)
57 - [Fragmented MP4 Support](#fragmented-mp4-support)
58 - [Testing](#testing)
59- [Release History](#release-history)
60- [Building](#building)
61- [Development](#development)
62 - [Tools](#tools)
63 - [Commands](#commands)
64
65<!-- END doctoc generated TOC please keep comment here to allow auto update -->
66
67## Installation
68### NPM
69To install `videojs-contrib-hls` with npm run
70
71```bash
72npm install --save videojs-contrib-hls
73```
74
75### CDN
76Select a version of HLS from the [CDN](https://cdnjs.com/libraries/videojs-contrib-hls)
77
78### Releases
79Download a release of [videojs-contrib-hls](https://github.com/videojs/videojs-contrib-hls/releases)
80
81### Manual Build
82Download a copy of this git repository and then follow the steps in [Building](#building)
83
84## Contributing
85See [CONTRIBUTING.md](/CONTRIBUTING.md)
86
87## Talk to us
88Drop by our slack channel (#playback) on the [Video.js slack][slack-link].
89
90## Getting Started
91Get a copy of [videojs-contrib-hls](#installation) and include it in your page along with video.js:
92
93```html
94<video id=example-video width=600 height=300 class="video-js vjs-default-skin" controls>
95 <source
96 src="https://example.com/index.m3u8"
97 type="application/x-mpegURL">
98</video>
99<script src="video.js"></script>
100<script src="videojs-contrib-hls.min.js"></script>
101<script>
102var player = videojs('example-video');
103player.play();
104</script>
105```
106
107Check out our [live example](http://jsbin.com/vokipos/8/edit?html,output) if you're having trouble.
108
109### Video.js 6
110With Video.js 6, by default there is no flash support. Instead, flash support is provided
111through the [videojs-flash](https://github.com/videojs/videojs-flash) plugin. If you are
112trying to use Video.js version 6 and want to include flash support, you must include
113[videojs-flash](https://github.com/videojs/videojs-flash) on your page before including
114videojs-contrib-hls
115
116```html
117<script src="https://unpkg.com/videojs-flash/dist/videojs-flash.js"></script>
118<script src="https://unpkg.com/videojs-contrib-hls/dist/videojs-contrib-hls.js"></script>
119```
120
121Flash, and the [videojs-flash](https://github.com/videojs/videojs-flash) plugin, are not
122required, but are recommended as a fallback option for browsers that don't have a native
123HLS player or support for [Media Source Extensions](http://caniuse.com/#feat=mediasource).
124
125## Documentation
126[HTTP Live Streaming](https://developer.apple.com/streaming/) (HLS) has
127become a de-facto standard for streaming video on mobile devices
128thanks to its native support on iOS and Android. There are a number of
129reasons independent of platform to recommend the format, though:
130
131- Supports (client-driven) adaptive bitrate selection
132- Delivered over standard HTTP ports
133- Simple, text-based manifest format
134- No proprietary streaming servers required
135
136Unfortunately, all the major desktop browsers except for Safari are
137missing HLS support. That leaves web developers in the unfortunate
138position of having to maintain alternate renditions of the same video
139and potentially having to forego HTML-based video entirely to provide
140the best desktop viewing experience.
141
142This project addresses that situation by providing a polyfill for HLS
143on browsers that have support for [Media Source
144Extensions](http://caniuse.com/#feat=mediasource), or failing that,
145support Flash. You can deploy a single HLS stream, code against the
146regular HTML5 video APIs, and create a fast, high-quality video
147experience across all the big web device categories.
148
149Check out the [full documentation](docs/) for details on how HLS works
150and advanced configuration. A description of the [adaptive switching
151behavior](docs/bitrate-switching.md) is available, too.
152
153videojs-contrib-hls supports a bunch of HLS features. Here
154are some highlights:
155
156- video-on-demand and live playback modes
157- backup or redundant streams
158- mid-segment quality switching
159- AES-128 segment encryption
160- CEA-608 captions are automatically translated into standard HTML5
161 [caption text tracks][0]
162- In-Manifest WebVTT subtitles are automatically translated into standard HTML5
163 subtitle tracks
164- Timed ID3 Metadata is automatically translated into HTML5 metedata
165 text tracks
166- Highly customizable adaptive bitrate selection
167- Automatic bandwidth tracking
168- Cross-domain credentials support with CORS
169- Tight integration with video.js and a philosophy of exposing as much
170 as possible with standard HTML APIs
171- Stream with multiple audio tracks and switching to those audio tracks
172 (see the docs folder) for info
173- Media content in
174 [fragmented MP4s](https://developer.apple.com/videos/play/wwdc2016/504/)
175 instead of the MPEG2-TS container format.
176
177[0]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/track
178
179### Options
180#### How to use
181
182##### Initialization
183You may pass in an options object to the hls source handler at player
184initialization. You can pass in options just like you would for other
185parts of video.js:
186
187```javascript
188// html5 for html hls
189videojs(video, {html5: {
190 hls: {
191 withCredentials: true
192 }
193}});
194
195// or
196
197// flash for flash hls
198videojs(video, {flash: {
199 hls: {
200 withCredentials: true
201 }
202}});
203
204// or
205
206var options = {hls: {
207 withCredentials: true;
208}};
209
210videojs(video, {flash: options, html5: options});
211
212```
213
214##### Source
215Some options, such as `withCredentials` can be passed in to hls during
216`player.src`
217
218```javascript
219
220var player = videojs('some-video-id');
221
222player.src({
223 src: 'https://d2zihajmogu5jn.cloudfront.net/bipbop-advanced/bipbop_16x9_variant.m3u8',
224 type: 'application/x-mpegURL',
225 withCredentials: true
226});
227```
228
229#### List
230##### withCredentials
231* Type: `boolean`
232* can be used as a source option
233* can be used as an initialization option
234
235When the `withCredentials` property is set to `true`, all XHR requests for
236manifests and segments would have `withCredentials` set to `true` as well. This
237enables storing and passing cookies from the server that the manifests and
238segments live on. This has some implications on CORS because when set, the
239`Access-Control-Allow-Origin` header cannot be set to `*`, also, the response
240headers require the addition of `Access-Control-Allow-Credentials` header which
241is set to `true`.
242See html5rocks's [article](http://www.html5rocks.com/en/tutorials/cors/)
243for more info.
244
245##### useCueTags
246* Type: `boolean`
247* can be used as an initialization option
248
249When the `useCueTags` property is set to `true,` a text track is created with
250label 'ad-cues' and kind 'metadata'. The track is then added to
251`player.textTracks()`. Changes in active cue may be
252tracked by following the Video.js cue points API for text tracks. For example:
253
254```javascript
255let textTracks = player.textTracks();
256let cuesTrack;
257
258for (let i = 0; i < textTracks.length; i++) {
259  if (textTracks[i].label === 'ad-cues') {
260    cuesTrack = textTracks[i];
261  }
262}
263
264cuesTrack.addEventListener('cuechange', function() {
265 let activeCues = cuesTrack.activeCues;
266
267  for (let i = 0; i < activeCues.length; i++) {
268 let activeCue = activeCues[i];
269
270    console.log('Cue runs from ' + activeCue.startTime +
271 ' to ' + activeCue.endTime);
272  }
273});
274```
275
276##### overrideNative
277* Type: `boolean`
278* can be used as an initialization option
279
280Try to use videojs-contrib-hls even on platforms that provide some
281level of HLS support natively. There are a number of platforms that
282*technically* play back HLS content but aren't very reliable or are
283missing features like CEA-608 captions support. When `overrideNative`
284is true, if the platform supports Media Source Extensions
285videojs-contrib-hls will take over HLS playback to provide a more
286consistent experience.
287
288__NOTE__: If you use this option, you must also set
289`videojs.options.html5.nativeAudioTracks` and
290`videojs.options.html5.nativeVideoTracks` to
291`false`. videojs-contrib-hls relies on audio and video tracks to play
292streams with alternate audio and requires additional capabilities only
293supported by non-native tracks in video.js.
294
295##### blacklistDuration
296* Type: `number`
297* can be used as an initialization option
298
299When the `blacklistDuration` property is set to a time duration in seconds,
300if a playlist is blacklisted, it will be blacklisted for a period of that
301customized duration. This enables the blacklist duration to be configured
302by the user.
303
304##### bandwidth
305* Type: `number`
306* can be used as an initialization option
307
308When the `bandwidth` property is set (bits per second), it will be used in
309the calculation for initial playlist selection, before more bandwidth
310information is seen by the player.
311
312### Runtime Properties
313Runtime properties are attached to the tech object when HLS is in
314use. You can get a reference to the HLS source handler like this:
315
316```javascript
317var hls = player.tech({ IWillNotUseThisInPlugins: true }).hls;
318```
319
320If you *were* thinking about modifying runtime properties in a
321video.js plugin, we'd recommend you avoid it. Your plugin won't work
322with videos that don't use videojs-contrib-hls and the best plugins
323work across all the media types that video.js supports. If you're
324deploying videojs-contrib-hls on your own website and want to make a
325couple tweaks though, go for it!
326
327#### hls.playlists.master
328Type: `object`
329
330An object representing the parsed master playlist. If a media playlist
331is loaded directly, a master playlist with only one entry will be
332created.
333
334#### hls.playlists.media
335Type: `function`
336
337A function that can be used to retrieve or modify the currently active
338media playlist. The active media playlist is referred to when
339additional video data needs to be downloaded. Calling this function
340with no arguments returns the parsed playlist object for the active
341media playlist. Calling this function with a playlist object from the
342master playlist or a URI string as specified in the master playlist
343will kick off an asynchronous load of the specified media
344playlist. Once it has been retreived, it will become the active media
345playlist.
346
347#### hls.segmentXhrTime
348Type: `number`
349
350The number of milliseconds it took to download the last media segment.
351This value is updated after each segment download completes.
352
353#### hls.bandwidth
354Type: `number`
355
356The number of bits downloaded per second in the last segment download.
357This value is used by the default implementation of `selectPlaylist`
358to select an appropriate bitrate to play.
359
360Before the first video segment has been downloaded, it's hard to
361estimate bandwidth accurately. The HLS tech uses a heuristic based on
362the playlist download times to do this estimation by default. If you
363have a more accurate source of bandwidth information, you can override
364this value as soon as the HLS tech has loaded to provide an initial
365bandwidth estimate.
366
367#### hls.bytesReceived
368Type: `number`
369
370The total number of content bytes downloaded by the HLS tech.
371
372#### hls.selectPlaylist
373Type: `function`
374
375A function that returns the media playlist object to use to download
376the next segment. It is invoked by the tech immediately before a new
377segment is downloaded. You can override this function to provide your
378adaptive streaming logic. You must, however, be sure to return a valid
379media playlist object that is present in `player.hls.master`.
380
381Overridding this function with your own is very powerful but is overkill
382for many purposes. Most of the time, you should use the much simpler
383function below to selectively enable or disable a playlist from the
384adaptive streaming logic.
385
386#### hls.representations
387Type: `function`
388
389It is recommended to include the [videojs-contrib-quality-levels](https://github.com/videojs/videojs-contrib-quality-levels) plugin to your page so that videojs-contrib-hls will automatically populate the QualityLevelList exposed on the player by the plugin. You can access this list by calling `player.qualityLevels()`. See the [videojs-contrib-quality-levels project page](https://github.com/videojs/videojs-contrib-quality-levels) for more information on how to use the api.
390
391Example, only enabling representations with a width greater than or equal to 720:
392
393```javascript
394var qualityLevels = player.qualityLevels();
395
396for (var i = 0; i < qualityLevels.length; i++) {
397 var quality = qualityLevels[i];
398 if (quality.width >= 720) {
399 quality.enabled = true;
400 } else {
401 quality.enabled = false;
402 }
403}
404```
405
406If including [videojs-contrib-quality-levels](https://github.com/videojs/videojs-contrib-quality-levels) is not an option, you can use the representations api. To get all of the available representations, call the `representations()` method on `player.hls`. This will return a list of plain objects, each with `width`, `height`, `bandwidth`, and `id` properties, and an `enabled()` method.
407
408```javascript
409player.hls.representations();
410```
411
412To see whether the representation is enabled or disabled, call its `enabled()` method with no arguments. To set whether it is enabled/disabled, call its `enabled()` method and pass in a boolean value. Calling `<representation>.enabled(true)` will allow the adaptive bitrate algorithm to select the representation while calling `<representation>.enabled(false)` will disallow any selection of that representation.
413
414Example, only enabling representations with a width greater than or equal to 720:
415
416```javascript
417player.hls.representations().forEach(function(rep) {
418 if (rep.width >= 720) {
419 rep.enabled(true);
420 } else {
421 rep.enabled(false);
422 }
423});
424```
425
426#### hls.xhr
427Type: `function`
428
429The xhr function that is used by HLS internally is exposed on the per-
430player `hls` object. While it is possible, we do not recommend replacing
431the function with your own implementation. Instead, the `xhr` provides
432the ability to specify a `beforeRequest` function that will be called
433with an object containing the options that will be used to create the
434xhr request.
435
436Example:
437```javascript
438player.hls.xhr.beforeRequest = function(options) {
439 options.uri = options.uri.replace('example.com', 'foo.com');
440
441 return options;
442};
443```
444
445The global `videojs.Hls` also exposes an `xhr` property. Specifying a
446`beforeRequest` function on that will allow you to intercept the options
447for *all* requests in every player on a page. For consistency across
448browsers the video source should be set at runtime once the video player
449is ready.
450
451Example
452```javascript
453videojs.Hls.xhr.beforeRequest = function(options) {
454 /*
455 * Modifications to requests that will affect every player.
456 */
457
458 return options;
459};
460
461var player = videojs('video-player-id');
462player.ready(function() {
463 this.src({
464 src: 'https://d2zihajmogu5jn.cloudfront.net/bipbop-advanced/bipbop_16x9_variant.m3u8',
465 type: 'application/x-mpegURL',
466 });
467});
468```
469
470For information on the type of options that you can modify see the
471documentation at [https://github.com/Raynos/xhr](https://github.com/Raynos/xhr).
472
473### Events
474Standard HTML video events are handled by video.js automatically and
475are triggered on the player object.
476
477#### loadedmetadata
478
479Fired after the first segment is downloaded for a playlist. This will not happen
480until playback if video.js's `metadata` setting is `none`
481
482### HLS Usage Events
483
484Usage tracking events are fired when we detect a certain HLS feature, encoding setting,
485or API is used. These can be helpful for analytics, and to pinpoint the cause of HLS errors.
486For instance, if errors are being fired in tandem with a usage event indicating that the
487player was playing an AES encrypted stream, then we have a possible avenue to explore when
488debugging the error.
489
490Note that although these usage events are listed below, they may change at any time without
491a major version change.
492
493HLS usage events are triggered on the tech with the exception of the 3 hls-reload-error
494events, which are triggered on the player.
495
496#### Presence Stats
497
498Each of the following usage events are fired once per source if (and when) detected:
499
500| Name | Description |
501| ------------- | ------------- |
502| hls-webvtt | master manifest has at least one segmented WebVTT playlist |
503| hls-aes | a playlist is AES encrypted |
504| hls-fmp4 | a playlist used fMP4 segments |
505| hls-demuxed | audio and video are demuxed by default |
506| hls-alternate-audio | alternate audio available in the master manifest |
507| hls-playlist-cue-tags | a playlist used cue tags (see useCueTags(#usecuetags) for details) |
508
509#### Use Stats
510
511Each of the following usage events are fired per use:
512
513| Name | Description |
514| ------------- | ------------- |
515| hls-gap-skip | player skipped a gap in the buffer |
516| hls-player-access | player.hls was accessed |
517| hls-audio-change | a user selected an alternate audio stream |
518| hls-rendition-disabled | a rendition was disabled |
519| hls-rendition-enabled | a rendition was enabled |
520| hls-rendition-blacklisted | a rendition was blacklisted |
521| hls-timestamp-offset | a timestamp offset was set in HLS (can identify discontinuities) |
522| hls-unknown-waiting | the player stopped for an unknown reason and we seeked to current time try to address it |
523| hls-live-resync | playback fell off the back of a live playlist and we resynced to the live point |
524| hls-video-underflow | we seeked to current time to address video underflow |
525| hls-error-reload-initialized | the reloadSourceOnError plugin was initialized |
526| hls-error-reload | the reloadSourceOnError plugin reloaded a source |
527| hls-error-reload-canceled | an error occurred too soon after the last reload, so we didn't reload again (to prevent error loops) |
528
529
530### In-Band Metadata
531The HLS tech supports [timed
532metadata](https://developer.apple.com/library/ios/#documentation/AudioVideo/Conceptual/HTTP_Live_Streaming_Metadata_Spec/Introduction/Introduction.html)
533embedded as [ID3 tags](http://id3.org/id3v2.3.0). When a stream is
534encountered with embedded metadata, an [in-band metadata text
535track](https://html.spec.whatwg.org/multipage/embedded-content.html#text-track-in-band-metadata-track-dispatch-type)
536will automatically be created and populated with cues as they are
537encountered in the stream. UTF-8 encoded
538[TXXX](http://id3.org/id3v2.3.0#User_defined_text_information_frame)
539and [WXXX](http://id3.org/id3v2.3.0#User_defined_URL_link_frame) ID3
540frames are mapped to cue points and their values set as the cue
541text. Cues are created for all other frame types and the data is
542attached to the generated cue:
543
544```javascript
545cue.value.data
546```
547
548There are lots of guides and references to using text tracks [around
549the web](http://www.html5rocks.com/en/tutorials/track/basics/).
550
551### Segment Metadata
552You can get metadata about the segments currently in the buffer by using the `segment-metadata`
553text track. You can get the metadata of the currently rendered segment by looking at the
554track's `activeCues` array. The metadata will be attached to the `cue.value` property and
555will have this structure
556
557```javascript
558cue.value = {
559 uri, // The Segment uri
560 timeline, // Timeline of the segment for detecting discontinuities
561 playlist, // The Playlist uri
562 start, // Segment start time
563 end // Segment end time
564};
565```
566
567Example:
568Detect when a change in quality is rendered on screen
569```javascript
570let tracks = player.textTracks();
571let segmentMetadataTrack;
572
573for (let i = 0; i < tracks.length; i++) {
574 if (tracks[i].label === 'segment-metadata') {
575 segmentMetadataTrack = tracks[i];
576 }
577}
578
579let previousPlaylist;
580
581if (segmentMetadataTrack) {
582 segmentMetadataTrack.on('cuechange', function() {
583 let activeCue = segmentMetadataTrack.activeCues[0];
584
585 if (activeCue) {
586 if (previousPlaylist !== activeCue.value.playlist) {
587 console.log('Switched from rendition ' + previousPlaylist +
588 ' to rendition ' + activeCue.value.playlist);
589 }
590 previousPlaylist = activeCue.value.playlist;
591 }
592 });
593}
594```
595
596## Hosting Considerations
597Unlike a native HLS implementation, the HLS tech has to comply with
598the browser's security policies. That means that all the files that
599make up the stream must be served from the same domain as the page
600hosting the video player or from a server that has appropriate [CORS
601headers](https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS)
602configured. Easy [instructions are
603available](http://enable-cors.org/server.html) for popular webservers
604and most CDNs should have no trouble turning CORS on for your account.
605
606
607## Known Issues
608Issues that are currenty know about with workarounds. If you want to
609help find a solution that would be appreciated!
610
611### IE10 and Below
612As of version 5.0.0, IE10 and below are no longer supported.
613
614### IE11
615In some IE11 setups there are issues working with its native HTML
616SourceBuffers functionality. This leads to various issues, such as
617videos stopping playback with media decode errors. The known workaround
618for this issues is to force the player to use flash when running on IE11.
619
620### Fragmented MP4 Support
621Edge has native support for HLS but only in the MPEG2-TS container. If
622you attempt to play an HLS stream with fragmented MP4 segments, Edge
623will stall. Fragmented MP4s are only supported on browser that have
624[Media Source Extensions](http://caniuse.com/#feat=mediasource) available.
625
626### Testing
627
628For testing, you run `npm run test`. This will run tests using any of the
629browsers that karma-detect-browsers detects on your machine.
630
631## Release History
632Check out the [changelog](CHANGELOG.md) for a summary of each release.
633
634## Building
635To build a copy of videojs-contrib-hls run the following commands
636
637```bash
638git clone https://github.com/videojs/videojs-contrib-hls
639cd videojs-contrib-hls
640npm i
641npm run build
642```
643
644videojs-contrib-hls will have created all of the files for using it in a dist folder
645
646## Development
647
648### Tools
649* Download stream locally with the [HLS Fetcher](https://github.com/imbcmdth/hls-fetcher)
650* Simulate errors with [Murphy](https://github.com/mrocajr/murphy)
651
652### Commands
653All commands for development are listed in the `package.json` file and are run using
654```bash
655npm run <command>
656```
657
658[slack-icon]: http://slack.videojs.com/badge.svg
659[slack-link]: http://slack.videojs.com
660[travis-icon]: https://travis-ci.org/videojs/videojs-contrib-hls.svg?branch=master
661[travis-link]: https://travis-ci.org/videojs/videojs-contrib-hls