UNPKG

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