UNPKG

38.3 kBMarkdownView Raw
1<img width=300 src="./logo.svg" alt="VHS Logo consisting of a VHS tape, the Video.js logo and the words VHS" />
2
3# videojs-http-streaming (VHS)
4
5[![Build Status][travis-icon]][travis-link]
6[![Slack Status][slack-icon]][slack-link]
7[![Greenkeeper badge][greenkeeper-icon]][greenkeeper-link]
8
9Play HLS, DASH, and future HTTP streaming protocols with video.js, even where they're not
10natively supported.
11
12Included in video.js 7 by default! See the [video.js 7 blog post](https://blog.videojs.com/video-js-7-is-here/)
13
14Maintenance Status: Stable
15
16Video.js Compatibility: 6.0, 7.0
17
18<!-- START doctoc generated TOC please keep comment here to allow auto update -->
19<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
20
21
22- [Installation](#installation)
23 - [NPM](#npm)
24 - [CDN](#cdn)
25 - [Releases](#releases)
26 - [Manual Build](#manual-build)
27- [Contributing](#contributing)
28- [Troubleshooting](#troubleshooting)
29- [Talk to us](#talk-to-us)
30- [Getting Started](#getting-started)
31- [Compatibility](#compatibility)
32 - [Browsers which support MSE](#browsers-which-support-mse)
33 - [Native only](#native-only)
34 - [Flash Support](#flash-support)
35 - [DRM](#drm)
36- [Documentation](#documentation)
37 - [Options](#options)
38 - [How to use](#how-to-use)
39 - [Initialization](#initialization)
40 - [Source](#source)
41 - [List](#list)
42 - [withCredentials](#withcredentials)
43 - [handleManifestRedirects](#handlemanifestredirects)
44 - [useCueTags](#usecuetags)
45 - [parse708captions](#parse708captions)
46 - [overrideNative](#overridenative)
47 - [blacklistDuration](#blacklistduration)
48 - [bandwidth](#bandwidth)
49 - [useBandwidthFromLocalStorage](#usebandwidthfromlocalstorage)
50 - [enableLowInitialPlaylist](#enablelowinitialplaylist)
51 - [limitRenditionByPlayerDimensions](#limitrenditionbyplayerdimensions)
52 - [useDevicePixelRatio](#usedevicepixelratio)
53 - [smoothQualityChange](#smoothqualitychange)
54 - [allowSeeksWithinUnsafeLiveWindow](#allowseekswithinunsafelivewindow)
55 - [customTagParsers](#customtagparsers)
56 - [customTagMappers](#customtagmappers)
57 - [cacheEncryptionKeys](#cacheencryptionkeys)
58 - [handlePartialData](#handlepartialdata)
59 - [liveRangeSafeTimeDelta](#liverangesafetimedelta)
60 - [Runtime Properties](#runtime-properties)
61 - [vhs.playlists.master](#vhsplaylistsmaster)
62 - [vhs.playlists.media](#vhsplaylistsmedia)
63 - [vhs.systemBandwidth](#vhssystembandwidth)
64 - [vhs.bandwidth](#vhsbandwidth)
65 - [vhs.throughput](#vhsthroughput)
66 - [vhs.selectPlaylist](#vhsselectplaylist)
67 - [vhs.representations](#vhsrepresentations)
68 - [vhs.xhr](#vhsxhr)
69 - [vhs.stats](#vhsstats)
70 - [Events](#events)
71 - [loadedmetadata](#loadedmetadata)
72 - [VHS Usage Events](#vhs-usage-events)
73 - [Presence Stats](#presence-stats)
74 - [Use Stats](#use-stats)
75 - [In-Band Metadata](#in-band-metadata)
76 - [Segment Metadata](#segment-metadata)
77 - [Object as Source](#object-as-source)
78- [Hosting Considerations](#hosting-considerations)
79- [Known Issues and Workarounds](#known-issues-and-workarounds)
80 - [Fragmented MP4 Support](#fragmented-mp4-support)
81 - [Assets with an Audio-Only Rate Get Stuck in Audio-Only](#assets-with-an-audio-only-rate-get-stuck-in-audio-only)
82 - [DASH Assets with `$Time` Interpolation and `SegmentTimeline`s with No `t`](#dash-assets-with-time-interpolation-and-segmenttimelines-with-no-t)
83- [Testing](#testing)
84- [Debugging](#debugging)
85- [Release History](#release-history)
86- [Building](#building)
87- [Development](#development)
88 - [Tools](#tools)
89 - [Commands](#commands)
90
91<!-- END doctoc generated TOC please keep comment here to allow auto update -->
92
93## Installation
94### NPM
95To install `videojs-http-streaming` with npm run
96
97```bash
98npm install --save @videojs/http-streaming
99```
100
101### CDN
102Select a version of VHS from the [CDN](https://unpkg.com/@videojs/http-streaming/dist/)
103
104### Releases
105Download a release of [videojs-http-streaming](https://github.com/videojs/http-streaming/releases)
106
107### Manual Build
108Download a copy of this git repository and then follow the steps in [Building](#building)
109
110## Contributing
111See [CONTRIBUTING.md](/CONTRIBUTING.md)
112
113## Troubleshooting
114See [our troubleshooting guide](/docs/troubleshooting.md)
115
116## Talk to us
117Drop by our slack channel (#playback) on the [Video.js slack][slack-link].
118
119## Getting Started
120This library is included in video.js 7 by default, if you are using an older version of video.js then
121get a copy of [videojs-http-streaming](#installation) and include it in your page along with video.js:
122
123```html
124<video-js id=vid1 width=600 height=300 class="vjs-default-skin" controls>
125 <source
126 src="https://example.com/index.m3u8"
127 type="application/x-mpegURL">
128</video-js>
129<script src="video.js"></script>
130<script src="videojs-http-streaming.min.js"></script>
131<script>
132var player = videojs('vid1');
133player.play();
134</script>
135```
136
137Check out our [live example](https://jsbin.com/gejugat/edit?html,output) if you're having trouble.
138
139Is it recommended to use the `<video-js>` element or load a source with `player.src(sourceObject)` in order to prevent the video element from playing the source natively where HLS is supported.
140
141## Compatibility
142
143The [Media Source Extensions](http://caniuse.com/#feat=mediasource) API is required for http-streaming to play HLS or MPEG-DASH.
144
145### Browsers which support MSE
146
147- Chrome
148- Firefox
149- Internet Explorer 11 Windows 10 or 8.1
150
151These browsers have some level of native HLS support, which will be used unless the [overrideNative](#overridenative) option is used:
152
153- Chrome Android
154- Firefox Android
155- Edge
156
157### Native only
158
159- Mac Safari
160- iOS Safari
161
162Mac Safari does have MSE support, but native HLS is recommended
163
164### Flash Support
165This plugin does not support Flash playback. Instead, it is recommended that users use the [videojs-flashls-source-handler](https://github.com/brightcove/videojs-flashls-source-handler) plugin as a fallback option for browsers that don't have a native
166[HLS](https://caniuse.com/#feat=http-live-streaming)/[DASH](https://caniuse.com/#feat=mpeg-dash) player or support for [Media Source Extensions](http://caniuse.com/#feat=mediasource).
167
168### DRM
169
170DRM is supported through [videojs-contrib-eme](https://github.com/videojs/videojs-contrib-eme). In order to use DRM, include the videojs-contrib-eme plug, [initialize it](https://github.com/videojs/videojs-contrib-eme#initialization), and add options to either the [plugin](https://github.com/videojs/videojs-contrib-eme#plugin-options) or the [source](https://github.com/videojs/videojs-contrib-eme#source-options).
171
172Detailed option information can be found in the [videojs-contrib-eme README](https://github.com/videojs/videojs-contrib-eme/blob/master/README.md).
173
174## Documentation
175[HTTP Live Streaming](https://developer.apple.com/streaming/) (HLS) has
176become a de-facto standard for streaming video on mobile devices
177thanks to its native support on iOS and Android. There are a number of
178reasons independent of platform to recommend the format, though:
179
180- Supports (client-driven) adaptive bitrate selection
181- Delivered over standard HTTP ports
182- Simple, text-based manifest format
183- No proprietary streaming servers required
184
185Unfortunately, all the major desktop browsers except for Safari are
186missing HLS support. That leaves web developers in the unfortunate
187position of having to maintain alternate renditions of the same video
188and potentially having to forego HTML-based video entirely to provide
189the best desktop viewing experience.
190
191This project addresses that situation by providing a polyfill for HLS
192on browsers that have support for [Media Source
193Extensions](http://caniuse.com/#feat=mediasource).
194You can deploy a single HLS stream, code against the
195regular HTML5 video APIs, and create a fast, high-quality video
196experience across all the big web device categories.
197
198Check out the [full documentation](docs/README.md) for details on how HLS works
199and advanced configuration. A description of the [adaptive switching
200behavior](docs/bitrate-switching.md) is available, too.
201
202videojs-http-streaming supports a bunch of HLS features. Here
203are some highlights:
204
205- video-on-demand and live playback modes
206- backup or redundant streams
207- mid-segment quality switching
208- AES-128 segment encryption
209- CEA-608 captions are automatically translated into standard HTML5
210 [caption text tracks][0]
211- In-Manifest WebVTT subtitles are automatically translated into standard HTML5
212 subtitle tracks
213- Timed ID3 Metadata is automatically translated into HTML5 metedata
214 text tracks
215- Highly customizable adaptive bitrate selection
216- Automatic bandwidth tracking
217- Cross-domain credentials support with CORS
218- Tight integration with video.js and a philosophy of exposing as much
219 as possible with standard HTML APIs
220- Stream with multiple audio tracks and switching to those audio tracks
221 (see the docs folder) for info
222- Media content in
223 [fragmented MP4s](https://developer.apple.com/videos/play/wwdc2016/504/)
224 instead of the MPEG2-TS container format.
225
226[0]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/track
227
228For a more complete list of supported and missing features, refer to
229[this doc](docs/supported-features.md).
230
231### Options
232#### How to use
233
234##### Initialization
235You may pass in an options object to the hls source handler at player
236initialization. You can pass in options just like you would for other
237parts of video.js:
238
239```javascript
240// html5 for html hls
241videojs(video, {
242 html5: {
243 vhs: {
244 withCredentials: true
245 }
246 }
247});
248```
249
250##### Source
251Some options, such as `withCredentials` can be passed in to vhs during
252`player.src`
253
254```javascript
255
256var player = videojs('some-video-id');
257
258player.src({
259 src: 'https://d2zihajmogu5jn.cloudfront.net/bipbop-advanced/bipbop_16x9_variant.m3u8',
260 type: 'application/x-mpegURL',
261 withCredentials: true
262});
263```
264
265#### List
266##### withCredentials
267* Type: `boolean`
268* can be used as a source option
269* can be used as an initialization option
270
271When the `withCredentials` property is set to `true`, all XHR requests for
272manifests and segments would have `withCredentials` set to `true` as well. This
273enables storing and passing cookies from the server that the manifests and
274segments live on. This has some implications on CORS because when set, the
275`Access-Control-Allow-Origin` header cannot be set to `*`, also, the response
276headers require the addition of `Access-Control-Allow-Credentials` header which
277is set to `true`.
278See html5rocks's [article](http://www.html5rocks.com/en/tutorials/cors/)
279for more info.
280
281##### handleManifestRedirects
282* Type: `boolean`
283* Default: `false`
284* can be used as a source option
285* can be used as an initialization option
286
287When the `handleManifestRedirects` property is set to `true`, manifest requests
288which are redirected will have their URL updated to the new URL for future
289requests.
290
291##### useCueTags
292* Type: `boolean`
293* can be used as an initialization option
294
295When the `useCueTags` property is set to `true,` a text track is created with
296label 'ad-cues' and kind 'metadata'. The track is then added to
297`player.textTracks()`. Changes in active cue may be
298tracked by following the Video.js cue points API for text tracks. For example:
299
300```javascript
301let textTracks = player.textTracks();
302let cuesTrack;
303
304for (let i = 0; i < textTracks.length; i++) {
305  if (textTracks[i].label === 'ad-cues') {
306    cuesTrack = textTracks[i];
307  }
308}
309
310cuesTrack.addEventListener('cuechange', function() {
311 let activeCues = cuesTrack.activeCues;
312
313  for (let i = 0; i < activeCues.length; i++) {
314 let activeCue = activeCues[i];
315
316    console.log('Cue runs from ' + activeCue.startTime +
317 ' to ' + activeCue.endTime);
318  }
319});
320```
321
322##### parse708captions
323* Type: `boolean`
324* Default: `true`
325* can be used as an initialization option
326
327When set to `false`, 708 captions in the stream are not parsed and will not show up in text track lists or the captions menu.
328
329##### overrideNative
330* Type: `boolean`
331* can be used as an initialization option
332
333Try to use videojs-http-streaming even on platforms that provide some
334level of HLS support natively. There are a number of platforms that
335*technically* play back HLS content but aren't very reliable or are
336missing features like CEA-608 captions support. When `overrideNative`
337is true, if the platform supports Media Source Extensions
338videojs-http-streaming will take over HLS playback to provide a more
339consistent experience.
340
341```javascript
342// via the constructor
343var player = videojs('playerId', {
344 html5: {
345 vhs: {
346 overrideNative: true
347 },
348 nativeAudioTracks: false,
349 nativeVideoTracks: false
350 }
351});
352```
353
354Since MSE playback may be desirable on all browsers with some native support other than Safari, `overrideNative: !videojs.browser.IS_SAFARI` could be used.
355
356##### blacklistDuration
357* Type: `number`
358* can be used as an initialization option
359
360When the `blacklistDuration` property is set to a time duration in seconds,
361if a playlist is blacklisted, it will be blacklisted for a period of that
362customized duration. This enables the blacklist duration to be configured
363by the user.
364
365##### bandwidth
366* Type: `number`
367* can be used as an initialization option
368
369When the `bandwidth` property is set (bits per second), it will be used in
370the calculation for initial playlist selection, before more bandwidth
371information is seen by the player.
372
373##### useBandwidthFromLocalStorage
374* Type: `boolean`
375* can be used as an initialization option
376
377If true, `bandwidth` and `throughput` values are stored in and retrieved from local
378storage on startup (for initial rendition selection). This setting is `false` by default.
379
380##### enableLowInitialPlaylist
381* Type: `boolean`
382* can be used as an initialization option
383
384When `enableLowInitialPlaylist` is set to true, it will be used to select
385the lowest bitrate playlist initially. This helps to decrease playback start time.
386This setting is `false` by default.
387
388##### limitRenditionByPlayerDimensions
389* Type: `boolean`
390* can be used as an initialization option
391
392When `limitRenditionByPlayerDimensions` is set to true, rendition
393selection logic will take into account the player size and rendition
394resolutions when making a decision.
395This setting is `true` by default.
396
397##### useDevicePixelRatio
398* Type: `boolean`
399* can be used as an initialization option.
400
401If true, this will take the device pixel ratio into account when doing rendition switching. This means that if you have a player with the width of `540px` in a high density display with a device pixel ratio of 2, a rendition of `1080p` will be allowed.
402This setting is `false` by default.
403
404##### smoothQualityChange
405* Type: `boolean`
406* can be used as a source option
407* can be used as an initialization option
408
409When the `smoothQualityChange` property is set to `true`, a manual quality
410change triggered via the [representations API](#vhsrepresentations) will use
411smooth quality switching rather than the default fast (buffer-ejecting)
412quality switching. Using smooth quality switching will mean no loading spinner
413will appear during quality switches, but will cause quality switches to only
414be visible after a few seconds.
415
416Note that this _only_ affects quality changes triggered via the representations
417API; automatic quality switches based on available bandwidth will always be
418smooth switches.
419
420##### allowSeeksWithinUnsafeLiveWindow
421* Type: `boolean`
422* can be used as a source option
423
424When `allowSeeksWithinUnsafeLiveWindow` is set to `true`, if the active playlist is live
425and a seek is made to a time between the safe live point (end of manifest minus three
426times the target duration,
427see [the hls spec](https://tools.ietf.org/html/draft-pantos-http-live-streaming-23#section-6.3.3)
428for details) and the end of the playlist, the seek is allowed, rather than corrected to
429the safe live point.
430
431This option can help in instances where the live stream's target duration is greater than
432the segment durations, playback ends up in the unsafe live window, and there are gaps in
433the content. In this case the player will attempt to seek past the gaps but end up seeking
434inside of the unsafe range, leading to a correction and seek back into a previously played
435content.
436
437The property defaults to `false`.
438
439##### customTagParsers
440* Type: `Array`
441* can be used as a source option
442
443With `customTagParsers` you can pass an array of custom m3u8 tag parser objects. See https://github.com/videojs/m3u8-parser#custom-parsers
444
445##### customTagMappers
446* Type: `Array`
447* can be used as a source option
448
449Similar to `customTagParsers`, with `customTagMappers` you can pass an array of custom m3u8 tag mapper objects. See https://github.com/videojs/m3u8-parser#custom-parsers
450
451##### cacheEncryptionKeys
452* Type: `boolean`
453* can be used as a source option
454* can be used as an initialization option
455
456This option forces the player to cache AES-128 encryption keys internally instead of requesting the key alongside every segment request.
457This option defaults to `false`.
458
459##### handlePartialData
460* Type: `boolean`,
461* Default: `false`
462* Use partial appends in the transmuxer and segment loader
463
464##### liveRangeSafeTimeDelta
465* Type: `number`,
466* Default: [`SAFE_TIME_DELTA`](https://github.com/videojs/http-streaming/blob/e7cb63af010779108336eddb5c8fd138d6390e95/src/ranges.js#L17)
467* Allow to re-define length (in seconds) of time delta when you compare current time and the end of the buffered range.
468
469### Runtime Properties
470Runtime properties are attached to the tech object when HLS is in
471use. You can get a reference to the VHS source handler like this:
472
473```javascript
474var vhs = player.tech().vhs;
475```
476
477If you *were* thinking about modifying runtime properties in a
478video.js plugin, we'd recommend you avoid it. Your plugin won't work
479with videos that don't use videojs-http-streaming and the best plugins
480work across all the media types that video.js supports. If you're
481deploying videojs-http-streaming on your own website and want to make a
482couple tweaks though, go for it!
483
484#### vhs.playlists.master
485Type: `object`
486
487An object representing the parsed master playlist. If a media playlist
488is loaded directly, a master playlist with only one entry will be
489created.
490
491#### vhs.playlists.media
492Type: `function`
493
494A function that can be used to retrieve or modify the currently active
495media playlist. The active media playlist is referred to when
496additional video data needs to be downloaded. Calling this function
497with no arguments returns the parsed playlist object for the active
498media playlist. Calling this function with a playlist object from the
499master playlist or a URI string as specified in the master playlist
500will kick off an asynchronous load of the specified media
501playlist. Once it has been retreived, it will become the active media
502playlist.
503
504#### vhs.systemBandwidth
505Type: `number`
506
507`systemBandwidth` is a combination of two serial processes' bitrates. The first
508is the network bitrate provided by `bandwidth` and the second is the bitrate of
509the entire process after that (decryption, transmuxing, and appending) provided
510by `throughput`. This value is used by the default implementation of `selectPlaylist`
511to select an appropriate bitrate to play.
512
513Since the two process are serial, the overall system bandwidth is given by:
514`systemBandwidth = 1 / (1 / bandwidth + 1 / throughput)`
515
516#### vhs.bandwidth
517Type: `number`
518
519The number of bits downloaded per second in the last segment download.
520
521Before the first video segment has been downloaded, it's hard to
522estimate bandwidth accurately. The HLS tech uses a starting value of 4194304 or 0.5 MB/s. If you
523have a more accurate source of bandwidth information, you can override
524this value as soon as the HLS tech has loaded to provide an initial
525bandwidth estimate.
526
527#### vhs.throughput
528Type: `number`
529
530The number of bits decrypted, transmuxed, and appended per second as a cumulative average across active processing time.
531
532#### vhs.selectPlaylist
533Type: `function`
534
535A function that returns the media playlist object to use to download
536the next segment. It is invoked by the tech immediately before a new
537segment is downloaded. You can override this function to provide your
538adaptive streaming logic. You must, however, be sure to return a valid
539media playlist object that is present in `player.tech().vhs.master`.
540
541Overridding this function with your own is very powerful but is overkill
542for many purposes. Most of the time, you should use the much simpler
543function below to selectively enable or disable a playlist from the
544adaptive streaming logic.
545
546#### vhs.representations
547Type: `function`
548
549It is recommended to include the [videojs-contrib-quality-levels](https://github.com/videojs/videojs-contrib-quality-levels) plugin to your page so that videojs-http-streaming 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.
550
551Example, only enabling representations with a width greater than or equal to 720:
552
553```javascript
554var qualityLevels = player.qualityLevels();
555
556for (var i = 0; i < qualityLevels.length; i++) {
557 var quality = qualityLevels[i];
558 if (quality.width >= 720) {
559 quality.enabled = true;
560 } else {
561 quality.enabled = false;
562 }
563}
564```
565
566If 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.tech().vhs`. This will return a list of plain objects, each with `width`, `height`, `bandwidth`, and `id` properties, and an `enabled()` method.
567
568```javascript
569player.tech().vhs.representations();
570```
571
572To 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.
573
574Example, only enabling representations with a width greater than or equal to 720:
575
576```javascript
577player.tech().vhs.representations().forEach(function(rep) {
578 if (rep.width >= 720) {
579 rep.enabled(true);
580 } else {
581 rep.enabled(false);
582 }
583});
584```
585
586#### vhs.xhr
587Type: `function`
588
589The xhr function that is used by HLS internally is exposed on the per-
590player `vhs` object. While it is possible, we do not recommend replacing
591the function with your own implementation. Instead, the `xhr` provides
592the ability to specify a `beforeRequest` function that will be called
593with an object containing the options that will be used to create the
594xhr request.
595
596Example:
597```javascript
598player.tech().vhs.xhr.beforeRequest = function(options) {
599 options.uri = options.uri.replace('example.com', 'foo.com');
600
601 return options;
602};
603```
604
605The global `videojs.Vhs` also exposes an `xhr` property. Specifying a
606`beforeRequest` function on that will allow you to intercept the options
607for *all* requests in every player on a page. For consistency across
608browsers the video source should be set at runtime once the video player
609is ready.
610
611Example
612```javascript
613videojs.Vhs.xhr.beforeRequest = function(options) {
614 /*
615 * Modifications to requests that will affect every player.
616 */
617
618 return options;
619};
620
621var player = videojs('video-player-id');
622player.ready(function() {
623 this.src({
624 src: 'https://d2zihajmogu5jn.cloudfront.net/bipbop-advanced/bipbop_16x9_variant.m3u8',
625 type: 'application/x-mpegURL',
626 });
627});
628```
629
630For information on the type of options that you can modify see the
631documentation at [https://github.com/Raynos/xhr](https://github.com/Raynos/xhr).
632
633#### vhs.stats
634Type: `object`
635
636This object contains a summary of HLS and player related stats.
637
638| Property Name | Type | Description |
639| --------------------- | ------ | ----------- |
640| bandwidth | number | Rate of the last segment download in bits/second |
641| mediaRequests | number | Total number of media segment requests |
642| mediaRequestsAborted | number | Total number of aborted media segment requests |
643| mediaRequestsTimedout | number | Total number of timedout media segment requests |
644| mediaRequestsErrored | number | Total number of errored media segment requests |
645| mediaTransferDuration | number | Total time spent downloading media segments in milliseconds |
646| mediaBytesTransferred | number | Total number of content bytes downloaded |
647| mediaSecondsLoaded | number | Total number of content seconds downloaded |
648| buffered | array | List of time ranges of content that are in the SourceBuffer |
649| currentTime | number | The current position of the player |
650| currentSource | object | The source object. Has the structure `{src: 'url', type: 'mimetype'}` |
651| currentTech | string | The name of the tech in use |
652| duration | number | Duration of the video in seconds |
653| master | object | The [master playlist object](#vhsplaylistsmaster) |
654| playerDimensions | object | Contains the width and height of the player |
655| seekable | array | List of time ranges that the player can seek to |
656| timestamp | number | Timestamp of when `vhs.stats` was accessed |
657| videoPlaybackQuality | object | Media playback quality metrics as specified by the [W3C's Media Playback Quality API](https://wicg.github.io/media-playback-quality/) |
658
659
660### Events
661Standard HTML video events are handled by video.js automatically and
662are triggered on the player object.
663
664#### loadedmetadata
665
666Fired after the first segment is downloaded for a playlist. This will not happen
667until playback if video.js's `metadata` setting is `none`
668
669### VHS Usage Events
670
671Usage tracking events are fired when we detect a certain HLS feature, encoding setting,
672or API is used. These can be helpful for analytics, and to pinpoint the cause of HLS errors.
673For instance, if errors are being fired in tandem with a usage event indicating that the
674player was playing an AES encrypted stream, then we have a possible avenue to explore when
675debugging the error.
676
677Note that although these usage events are listed below, they may change at any time without
678a major version change.
679
680VHS usage events are triggered on the tech with the exception of the 3 vhs-reload-error
681events, which are triggered on the player.
682
683To listen for usage events triggered on the tech, listen for the event type of `'usage'`:
684
685```javascript
686player.on('ready', () => {
687 player.tech().on('usage', (e) => {
688 console.log(e.name);
689 });
690});
691```
692
693Note that these events are triggered as soon as a case is encountered, and often only
694once. For example, the `vhs-demuxed` usage event will be triggered as soon as the master
695manifest is downloaded and parsed, and will not be triggered again.
696
697#### Presence Stats
698
699Each of the following usage events are fired once per source if (and when) detected:
700
701| Name | Description |
702| ------------- | ------------- |
703| vhs-webvtt | master manifest has at least one segmented WebVTT playlist |
704| vhs-aes | a playlist is AES encrypted |
705| vhs-fmp4 | a playlist used fMP4 segments |
706| vhs-demuxed | audio and video are demuxed by default |
707| vhs-alternate-audio | alternate audio available in the master manifest |
708| vhs-playlist-cue-tags | a playlist used cue tags (see useCueTags(#usecuetags) for details) |
709| vhs-bandwidth-from-local-storage | starting bandwidth was retrieved from local storage (see useBandwidthFromLocalStorage(#useBandwidthFromLocalStorage) for details) |
710| vhs-throughput-from-local-storage | starting throughput was retrieved from local storage (see useBandwidthFromLocalStorage(#useBandwidthFromLocalStorage) for details) |
711
712#### Use Stats
713
714Each of the following usage events are fired per use:
715
716| Name | Description |
717| ------------- | ------------- |
718| vhs-gap-skip | player skipped a gap in the buffer |
719| vhs-player-access | player.vhs was accessed |
720| vhs-audio-change | a user selected an alternate audio stream |
721| vhs-rendition-disabled | a rendition was disabled |
722| vhs-rendition-enabled | a rendition was enabled |
723| vhs-rendition-blacklisted | a rendition was blacklisted |
724| vhs-timestamp-offset | a timestamp offset was set in HLS (can identify discontinuities) |
725| vhs-unknown-waiting | the player stopped for an unknown reason and we seeked to current time try to address it |
726| vhs-live-resync | playback fell off the back of a live playlist and we resynced to the live point |
727| vhs-video-underflow | we seeked to current time to address video underflow |
728| vhs-error-reload-initialized | the reloadSourceOnError plugin was initialized |
729| vhs-error-reload | the reloadSourceOnError plugin reloaded a source |
730| vhs-error-reload-canceled | an error occurred too soon after the last reload, so we didn't reload again (to prevent error loops) |
731
732
733### In-Band Metadata
734The HLS tech supports [timed
735metadata](https://developer.apple.com/library/ios/#documentation/AudioVideo/Conceptual/HTTP_Live_Streaming_Metadata_Spec/Introduction/Introduction.html)
736embedded as [ID3 tags](http://id3.org/id3v2.3.0). When a stream is
737encountered with embedded metadata, an [in-band metadata text
738track](https://html.spec.whatwg.org/multipage/embedded-content.html#text-track-in-band-metadata-track-dispatch-type)
739will automatically be created and populated with cues as they are
740encountered in the stream. UTF-8 encoded
741[TXXX](http://id3.org/id3v2.3.0#User_defined_text_information_frame)
742and [WXXX](http://id3.org/id3v2.3.0#User_defined_URL_link_frame) ID3
743frames are mapped to cue points and their values set as the cue
744text. Cues are created for all other frame types and the data is
745attached to the generated cue:
746
747```javascript
748cue.value.data
749```
750
751There are lots of guides and references to using text tracks [around
752the web](http://www.html5rocks.com/en/tutorials/track/basics/).
753
754### Segment Metadata
755You can get metadata about the segments currently in the buffer by using the `segment-metadata`
756text track. You can get the metadata of the currently rendered segment by looking at the
757track's `activeCues` array. The metadata will be attached to the `cue.value` property and
758will have this structure
759
760```javascript
761cue.value = {
762 byteLength, // The size of the segment in bytes
763 bandwidth, // The peak bitrate reported by the segment's playlist
764 resolution, // The resolution reported by the segment's playlist
765 codecs, // The codecs reported by the segment's playlist
766 uri, // The Segment uri
767 timeline, // Timeline of the segment for detecting discontinuities
768 playlist, // The Playlist uri
769 start, // Segment start time
770 end // Segment end time
771};
772```
773
774Example:
775Detect when a change in quality is rendered on screen
776```javascript
777let tracks = player.textTracks();
778let segmentMetadataTrack;
779
780for (let i = 0; i < tracks.length; i++) {
781 if (tracks[i].label === 'segment-metadata') {
782 segmentMetadataTrack = tracks[i];
783 }
784}
785
786let previousPlaylist;
787
788if (segmentMetadataTrack) {
789 segmentMetadataTrack.on('cuechange', function() {
790 let activeCue = segmentMetadataTrack.activeCues[0];
791
792 if (activeCue) {
793 if (previousPlaylist !== activeCue.value.playlist) {
794 console.log('Switched from rendition ' + previousPlaylist +
795 ' to rendition ' + activeCue.value.playlist);
796 }
797 previousPlaylist = activeCue.value.playlist;
798 }
799 });
800}
801```
802
803### Object as Source
804
805*Note* that this is an advanced use-case, and may be more fragile for production
806environments, as the schema for a VHS object and how it's used internally are not set in
807stone and may change in future releases.
808
809In normal use, VHS accepts a URL as the source of the video. But VHS also has the ability
810to accept a JSON object as the source.
811
812Passing a JSON object as the source has many uses. A couple of examples include:
813* The manifest has already been downloaded, so there's no need to make another request
814* You want to change some aspect of the manifest, e.g., add a segment, without modifying
815 the manifest itself
816
817In order to pass a JSON object as the source, provide a parsed manifest object in via a
818[data URI](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs),
819and using the "vnd.videojs.vhs+json" media type when setting the source type. For instance:
820
821```
822var player = videojs('some-video-id');
823const parser = new M3u8Parser();
824
825parser.push(manifestString);
826parser.end();
827
828player.src({
829 src: `data:application/vnd.videojs.vhs+json,${JSON.stringify(parser.manifest)}`,
830 type: 'application/vnd.videojs.vhs+json'
831});
832```
833
834The manifest object should follow the "VHS manifest object schema" (a somewhat flexible
835and informally documented structure) provided in the README of
836[m3u8-parser](https://github.com/videojs/m3u8-parser) and
837[mpd-parser](https://github.com/videojs/mpd-parser). This may be referred to in the
838project as `vhs-json`.
839
840## Hosting Considerations
841Unlike a native HLS implementation, the HLS tech has to comply with
842the browser's security policies. That means that all the files that
843make up the stream must be served from the same domain as the page
844hosting the video player or from a server that has appropriate [CORS
845headers](https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS)
846configured. Easy [instructions are
847available](http://enable-cors.org/server.html) for popular webservers
848and most CDNs should have no trouble turning CORS on for your account.
849
850
851## Known Issues and Workarounds
852Issues that are currenty known. If you want to
853help find a solution that would be appreciated!
854
855### Fragmented MP4 Support
856Edge has native support for HLS but only in the MPEG2-TS container. If
857you attempt to play an HLS stream with fragmented MP4 segments (without
858[overriding native playback](#overridenative)), Edge will stall.
859Fragmented MP4s are only supported on browsers that have
860[Media Source Extensions](http://caniuse.com/#feat=mediasource) available.
861
862### Assets with an Audio-Only Rate Get Stuck in Audio-Only
863Some assets which have an audio-only rate and the lowest-bandwidth
864audio + video rate isn't that low get stuck in audio-only mode. This is
865because the initial bandwidth calculation thinks it there's insufficient
866bandwidth for selecting the lowest-quality audio+video playlist, so it picks
867the only-audio one, which unfortunately locks it to being audio-only forever,
868preventing a switch to the audio+video playlist when it gets a better
869estimation of bandwidth.
870
871Until we've implemented a full fix, it is recommended to set the
872[`enableLowInitialPlaylist` option](#enablelowinitialplaylist) for any assets
873that include an audio-only rate; it should always select the lowest-bandwidth
874audio+video playlist for its first playlist.
875
876It's also worth mentioning that Apple no longer requires having an audio-only
877rate; instead, they require a 192kbps audio+video rate (see Apple's current
878[HLS Authoring Specification][]). Removing the audio-only rate would of course
879eliminate this problem since there would be only audio+video playlists to
880choose from.
881
882Follow progress on this in issue [#175](https://github.com/videojs/http-streaming/issues/175).
883
884[HLS Authoring Specification]: https://developer.apple.com/documentation/http_live_streaming/hls_authoring_specification_for_apple_devices
885
886### DASH Assets with `$Time` Interpolation and `SegmentTimeline`s with No `t`
887
888DASH assets which use `$Time` in a `SegmentTemplate`, and also have a
889`SegmentTimeline` where only the first `S` has a `t` and the rest only have a
890`d`, do not load currently.
891
892There is currently no workaround for this, but you can track progress on this
893in issue [#256](https://github.com/videojs/http-streaming/issues/256).
894
895## Testing
896
897For testing, you run `npm run test`. You will need Chrome and Firefox for running the tests.
898
899_videojs-http-streaming uses [BrowserStack](https://browserstack.com) for compatibility testing._
900
901## Debugging
902
903videojs-http-streaming makes use of `videojs.log` for debug logging. You can enable these logs
904by setting the log level to `debug` using `videojs.log.level('debug')`. You can access a complete
905history of the logs using `videojs.log.history()`. This history is maintained even when the
906log level is not set to `debug`.
907
908`vhs.stats` can also be helpful when debugging. Accessing this object will give you
909a snapshot summary of various HLS and player stats. See [vhs.stats](#vhsstats) for details
910about what this object contains.
911
912__NOTE__: The `debug` level is only available in video.js v6.6.0+. With earlier versions of
913video.js, no debug messages will be logged to console.
914
915## Release History
916Check out the [changelog](CHANGELOG.md) for a summary of each release.
917
918## Building
919To build a copy of videojs-http-streaming run the following commands
920
921```bash
922git clone https://github.com/videojs/http-streaming
923cd http-streaming
924npm i
925npm run build
926```
927
928videojs-http-streaming will have created all of the files for using it in a dist folder
929
930## Development
931
932### Tools
933* Download stream locally with the [HLS Fetcher](https://github.com/videojs/hls-fetcher)
934* Simulate errors with [Murphy](https://github.com/videojs/murphy)
935* Inspect content with [Thumbcoil](http://thumb.co.il)
936
937### Commands
938All commands for development are listed in the `package.json` file and are run using
939```bash
940npm run <command>
941```
942
943[slack-icon]: http://slack.videojs.com/badge.svg
944[slack-link]: http://slack.videojs.com
945[travis-icon]: https://travis-ci.org/videojs/http-streaming.svg?branch=main
946[travis-link]: https://travis-ci.org/videojs/http-streaming
947[issue-stats-link]: http://issuestats.com/github/videojs/http-streaming
948[issue-stats-pr-icon]: http://issuestats.com/github/videojs/http-streaming/badge/pr
949[issue-stats-issues-icon]: http://issuestats.com/github/videojs/http-streaming/badge/issue
950[greenkeeper-icon]: https://badges.greenkeeper.io/videojs/http-streaming.svg
951[greenkeeper-link]: https://greenkeeper.io/