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