mirror of
https://github.com/DanielnetoDotCom/YouPHPTube
synced 2025-10-03 09:49:28 +02:00
This commit is contained in:
parent
746e163d01
commit
1c7ea28b46
808 changed files with 316395 additions and 381162 deletions
167
node_modules/@videojs/http-streaming/CHANGELOG.md
generated
vendored
167
node_modules/@videojs/http-streaming/CHANGELOG.md
generated
vendored
|
@ -1,46 +1,165 @@
|
|||
<a name="2.16.0"></a>
|
||||
# [2.16.0](https://github.com/videojs/http-streaming/compare/v2.15.1...v2.16.0) (2023-01-30)
|
||||
<a name="3.0.2"></a>
|
||||
## [3.0.2](https://github.com/videojs/http-streaming/compare/v3.0.1...v3.0.2) (2023-02-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* in-manifest VTT iOS MSE issue ([#1364](https://github.com/videojs/http-streaming/issues/1364)) ([e735188](https://github.com/videojs/http-streaming/commit/e735188))
|
||||
|
||||
<a name="2.15.1"></a>
|
||||
## [2.15.1](https://github.com/videojs/http-streaming/compare/v2.15.0...v2.15.1) (2022-11-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Restart masterPlaylistLoader after media change ([#1339](https://github.com/videojs/http-streaming/issues/1339)) ([66707b4](https://github.com/videojs/http-streaming/commit/66707b4))
|
||||
* resume loading on segment timeout for `experimentalBufferBasedABR` ([#1333](https://github.com/videojs/http-streaming/issues/1333)) ([5666562](https://github.com/videojs/http-streaming/commit/5666562))
|
||||
* CMAF HLS. Source buffer change type is called with wrong codecs sometimes when append segment without init data because of a race condition. ([#1375](https://github.com/videojs/http-streaming/issues/1375)) ([7c3e08e](https://github.com/videojs/http-streaming/commit/7c3e08e))
|
||||
|
||||
### Chores
|
||||
|
||||
* update mpd-parser (main) ([#1336](https://github.com/videojs/http-streaming/issues/1336)) ([404ba76](https://github.com/videojs/http-streaming/commit/404ba76))
|
||||
* update video.js for the example page ([#1340](https://github.com/videojs/http-streaming/issues/1340)) ([8a8b111](https://github.com/videojs/http-streaming/commit/8a8b111))
|
||||
* **changelog:** add missing bug fix ([#1362](https://github.com/videojs/http-streaming/issues/1362)) ([343f682](https://github.com/videojs/http-streaming/commit/343f682))
|
||||
* update mux.js ([#1372](https://github.com/videojs/http-streaming/issues/1372)) ([1bd22c9](https://github.com/videojs/http-streaming/commit/1bd22c9))
|
||||
|
||||
<a name="2.15.0"></a>
|
||||
# [2.15.0](https://github.com/videojs/http-streaming/compare/v2.14.3...v2.15.0) (2022-09-14)
|
||||
<a name="3.0.1"></a>
|
||||
## [3.0.1](https://github.com/videojs/http-streaming/compare/v3.0.0...v3.0.1) (2023-01-24)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Linear DASH multiperiod label issue ([#1352](https://github.com/videojs/http-streaming/issues/1352)) ([d7e8713](https://github.com/videojs/http-streaming/commit/d7e8713))
|
||||
* In-manifest VTT iOS MSE issue ([#1360](https://github.com/videojs/http-streaming/issues/1360)) ([6ba70e0](https://github.com/videojs/http-streaming/commit/6ba70e0))
|
||||
|
||||
<a name="3.0.0"></a>
|
||||
# [3.0.0](https://github.com/videojs/http-streaming/compare/v2.14.2...v3.0.0) (2022-11-21)
|
||||
|
||||
### Features
|
||||
|
||||
* add frameRate property to the representation class. ([#1289](https://github.com/videojs/http-streaming/issues/1289)) ([27a970c](https://github.com/videojs/http-streaming/commit/27a970c))
|
||||
|
||||
<a name="2.14.3"></a>
|
||||
## [2.14.3](https://github.com/videojs/http-streaming/compare/v2.14.2...v2.14.3) (2022-08-31)
|
||||
* add compatibility layer for video.js 7 and 8 ([#1322](https://github.com/videojs/http-streaming/issues/1322)) ([b9d26e5](https://github.com/videojs/http-streaming/commit/b9d26e5))
|
||||
* add frameRate property to the representation class. ([#1289](https://github.com/videojs/http-streaming/issues/1289)) ([fd2898f](https://github.com/videojs/http-streaming/commit/fd2898f))
|
||||
* enable LLHLS support by default and remove experimental prefix on options ([#1301](https://github.com/videojs/http-streaming/issues/1301)) ([02c3c77](https://github.com/videojs/http-streaming/commit/02c3c77))
|
||||
* remove handleManifestRedirects and always use XHR.responseURL if available ([#1226](https://github.com/videojs/http-streaming/issues/1226)) ([3ad3120](https://github.com/videojs/http-streaming/commit/3ad3120))
|
||||
* rename many things to `main` ([#1309](https://github.com/videojs/http-streaming/issues/1309)) ([54cbab3](https://github.com/videojs/http-streaming/commit/54cbab3))
|
||||
* Skip gaps immediately ([#1267](https://github.com/videojs/http-streaming/issues/1267)) ([f85c153](https://github.com/videojs/http-streaming/commit/f85c153))
|
||||
* update tooling to remove ie 11 transpiling, update tests ([#1306](https://github.com/videojs/http-streaming/issues/1306)) ([206f099](https://github.com/videojs/http-streaming/commit/206f099))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* cache aes keys for text tracks ([#973](https://github.com/videojs/http-streaming/issues/973)) ([#1228](https://github.com/videojs/http-streaming/issues/1228)) ([721e1bf](https://github.com/videojs/http-streaming/commit/721e1bf))
|
||||
* output-restricted event handling for unplayable streams ([#1305](https://github.com/videojs/http-streaming/issues/1305)) ([23bbf84](https://github.com/videojs/http-streaming/commit/23bbf84))
|
||||
* add Video.js 8 to the dep version range ([#1307](https://github.com/videojs/http-streaming/issues/1307)) ([325a98e](https://github.com/videojs/http-streaming/commit/325a98e))
|
||||
* cache aes keys for text tracks ([#973](https://github.com/videojs/http-streaming/issues/973)) ([#1228](https://github.com/videojs/http-streaming/issues/1228)) ([66a5b17](https://github.com/videojs/http-streaming/commit/66a5b17))
|
||||
* output-restricted event handling for unplayable streams ([#1305](https://github.com/videojs/http-streaming/issues/1305)) ([1c62a98](https://github.com/videojs/http-streaming/commit/1c62a98))
|
||||
* remove deprecation hls options, properties, and events; add migration guide ([#1229](https://github.com/videojs/http-streaming/issues/1229)) ([43fce26](https://github.com/videojs/http-streaming/commit/43fce26))
|
||||
* Restart mainPlaylistLoader after media change ([#1339](https://github.com/videojs/http-streaming/issues/1339)) ([cf340f2](https://github.com/videojs/http-streaming/commit/cf340f2))
|
||||
* resume loading on segment timeout for `bufferBasedABR` ([#1333](https://github.com/videojs/http-streaming/issues/1333)) ([969589e](https://github.com/videojs/http-streaming/commit/969589e))
|
||||
|
||||
### Chores
|
||||
|
||||
* **docs:** Remove outdated information in collaborators' guide ([#1271](https://github.com/videojs/http-streaming/issues/1271)) ([5223427](https://github.com/videojs/http-streaming/commit/5223427))
|
||||
* **docs:** Remove outdated information in collaborators' guide ([#1271](https://github.com/videojs/http-streaming/issues/1271)) ([6100750](https://github.com/videojs/http-streaming/commit/6100750))
|
||||
* **package:** update dependencies to use new ES6 builds ([#1320](https://github.com/videojs/http-streaming/issues/1320)) ([9ae6695](https://github.com/videojs/http-streaming/commit/9ae6695))
|
||||
* **package:** update m3u8-parser to v6.0.0 ([#1330](https://github.com/videojs/http-streaming/issues/1330)) ([fe15751](https://github.com/videojs/http-streaming/commit/fe15751))
|
||||
* remove old-index since IE is no longer supported ([#1308](https://github.com/videojs/http-streaming/issues/1308)) ([5ba3a77](https://github.com/videojs/http-streaming/commit/5ba3a77))
|
||||
* update karma-config to 8 to drop ie11 and older browsers ([#1227](https://github.com/videojs/http-streaming/issues/1227)) ([44c12ea](https://github.com/videojs/http-streaming/commit/44c12ea))
|
||||
* update mpd-parser ([#1337](https://github.com/videojs/http-streaming/issues/1337)) ([7ff95b9](https://github.com/videojs/http-streaming/commit/7ff95b9))
|
||||
* update package-lock ([1806b46](https://github.com/videojs/http-streaming/commit/1806b46))
|
||||
* update package-lock.json ([#1319](https://github.com/videojs/http-streaming/issues/1319)) ([c7aa9c1](https://github.com/videojs/http-streaming/commit/c7aa9c1))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* clean up parameters of excludePlaylist ([#1304](https://github.com/videojs/http-streaming/issues/1304)) ([ca3162b](https://github.com/videojs/http-streaming/commit/ca3162b))
|
||||
* Remove deprecated smooth quality change ([#1268](https://github.com/videojs/http-streaming/issues/1268)) ([6041014](https://github.com/videojs/http-streaming/commit/6041014))
|
||||
* rename 'blacklist' to 'exclude' ([#1274](https://github.com/videojs/http-streaming/issues/1274)) ([d79d783](https://github.com/videojs/http-streaming/commit/d79d783))
|
||||
|
||||
### Tests
|
||||
|
||||
* change source for live DASH playback test to fix test failures ([#1303](https://github.com/videojs/http-streaming/issues/1303)) ([e39e27d](https://github.com/videojs/http-streaming/commit/e39e27d))
|
||||
* fix IE11 encrypted VTT tests by using an actual encrypted VTT segment ([#1291](https://github.com/videojs/http-streaming/issues/1291)) ([97e02fb](https://github.com/videojs/http-streaming/commit/97e02fb))
|
||||
* change source for live DASH playback test to fix test failures ([#1303](https://github.com/videojs/http-streaming/issues/1303)) ([128b3d7](https://github.com/videojs/http-streaming/commit/128b3d7))
|
||||
* fix IE11 encrypted VTT tests by using an actual encrypted VTT segment ([#1291](https://github.com/videojs/http-streaming/issues/1291)) ([57c0e72](https://github.com/videojs/http-streaming/commit/57c0e72))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* **package:** manifests with tags lacking colons (:) are no longer supported
|
||||
* **package:** This updates bundled libraries to no longer be transpiled to ES5, which means IE will no longer be supported.
|
||||
* This changes the arguments for the `PlaylistController#excludePlaylist` method to take a single object instead of multiple arguments.
|
||||
* This renames four experimental options to no longer be experimental and enables Low Latency HLS support by default (`llhls: false` will still disable it, if desired).
|
||||
* rename PlaylistController
|
||||
* rename HAVE_MASTER to HAVE_MAIN_MANIFEST
|
||||
* playlist loaders updateMain and .main prop rename
|
||||
* manifest.js exports mainForMedia and addPropertiesToMain
|
||||
* rename media groups prop to isMainPlaylist
|
||||
* rename property to mainPlaylistLoader_
|
||||
* rename to PlaylistController#main()
|
||||
* This removes support entirely for IE11 (and older) as well as any other platforms that do not support ES6.
|
||||
* remove ^6 from the dependency version ranges.
|
||||
* Skips detected gaps immediately instead of waiting the duration of the gap before skipping
|
||||
* Removes deprecated `smoothQualityChange` option
|
||||
* remove deprecated options, properties, events.
|
||||
* remove handleManifestRedirects option. Now XHR.responseURL will always be used when available.
|
||||
|
||||
<a name="3.0.0-2"></a>
|
||||
# [3.0.0-2](https://github.com/videojs/http-streaming/compare/v3.0.0-1...v3.0.0-2) (2022-09-30)
|
||||
|
||||
<a name="3.0.0-1"></a>
|
||||
# [3.0.0-1](https://github.com/videojs/http-streaming/compare/v3.0.0-0...v3.0.0-1) (2022-09-30)
|
||||
|
||||
### Features
|
||||
|
||||
* add compatibility layer for video.js 7 and 8 ([#1322](https://github.com/videojs/http-streaming/issues/1322)) ([b9d26e5](https://github.com/videojs/http-streaming/commit/b9d26e5))
|
||||
* add frameRate property to the representation class. ([#1289](https://github.com/videojs/http-streaming/issues/1289)) ([fd2898f](https://github.com/videojs/http-streaming/commit/fd2898f))
|
||||
|
||||
### Chores
|
||||
|
||||
* **package:** update m3u8-parser to v6.0.0 ([#1330](https://github.com/videojs/http-streaming/issues/1330)) ([fe15751](https://github.com/videojs/http-streaming/commit/fe15751))
|
||||
* update package-lock ([1806b46](https://github.com/videojs/http-streaming/commit/1806b46))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* **package:** manifests with tags lacking colons (:) are no longer supported
|
||||
|
||||
<a name="3.0.0-0"></a>
|
||||
# [3.0.0-0](https://github.com/videojs/http-streaming/compare/v2.14.2...v3.0.0-0) (2022-08-19)
|
||||
|
||||
### Features
|
||||
|
||||
* enable LLHLS support by default and remove experimental prefix on options ([#1301](https://github.com/videojs/http-streaming/issues/1301)) ([02c3c77](https://github.com/videojs/http-streaming/commit/02c3c77))
|
||||
* remove handleManifestRedirects and always use XHR.responseURL if available ([#1226](https://github.com/videojs/http-streaming/issues/1226)) ([3ad3120](https://github.com/videojs/http-streaming/commit/3ad3120))
|
||||
* rename many things to `main` ([#1309](https://github.com/videojs/http-streaming/issues/1309)) ([54cbab3](https://github.com/videojs/http-streaming/commit/54cbab3))
|
||||
* Skip gaps immediately ([#1267](https://github.com/videojs/http-streaming/issues/1267)) ([f85c153](https://github.com/videojs/http-streaming/commit/f85c153))
|
||||
* update tooling to remove ie 11 transpiling, update tests ([#1306](https://github.com/videojs/http-streaming/issues/1306)) ([206f099](https://github.com/videojs/http-streaming/commit/206f099))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add Video.js 8 to the dep version range ([#1307](https://github.com/videojs/http-streaming/issues/1307)) ([325a98e](https://github.com/videojs/http-streaming/commit/325a98e))
|
||||
* cache aes keys for text tracks ([#973](https://github.com/videojs/http-streaming/issues/973)) ([#1228](https://github.com/videojs/http-streaming/issues/1228)) ([66a5b17](https://github.com/videojs/http-streaming/commit/66a5b17))
|
||||
* output-restricted event handling for unplayable streams ([#1305](https://github.com/videojs/http-streaming/issues/1305)) ([1c62a98](https://github.com/videojs/http-streaming/commit/1c62a98))
|
||||
* remove deprecation hls options, properties, and events; add migration guide ([#1229](https://github.com/videojs/http-streaming/issues/1229)) ([43fce26](https://github.com/videojs/http-streaming/commit/43fce26))
|
||||
|
||||
### Chores
|
||||
|
||||
* **docs:** Remove outdated information in collaborators' guide ([#1271](https://github.com/videojs/http-streaming/issues/1271)) ([6100750](https://github.com/videojs/http-streaming/commit/6100750))
|
||||
* **package:** update dependencies to use new ES6 builds ([#1320](https://github.com/videojs/http-streaming/issues/1320)) ([9ae6695](https://github.com/videojs/http-streaming/commit/9ae6695))
|
||||
* remove old-index since IE is no longer supported ([#1308](https://github.com/videojs/http-streaming/issues/1308)) ([5ba3a77](https://github.com/videojs/http-streaming/commit/5ba3a77))
|
||||
* update karma-config to 8 to drop ie11 and older browsers ([#1227](https://github.com/videojs/http-streaming/issues/1227)) ([44c12ea](https://github.com/videojs/http-streaming/commit/44c12ea))
|
||||
* update package-lock.json ([#1319](https://github.com/videojs/http-streaming/issues/1319)) ([c7aa9c1](https://github.com/videojs/http-streaming/commit/c7aa9c1))
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* clean up parameters of excludePlaylist ([#1304](https://github.com/videojs/http-streaming/issues/1304)) ([ca3162b](https://github.com/videojs/http-streaming/commit/ca3162b))
|
||||
* Remove deprecated smooth quality change ([#1268](https://github.com/videojs/http-streaming/issues/1268)) ([6041014](https://github.com/videojs/http-streaming/commit/6041014))
|
||||
* rename 'blacklist' to 'exclude' ([#1274](https://github.com/videojs/http-streaming/issues/1274)) ([d79d783](https://github.com/videojs/http-streaming/commit/d79d783))
|
||||
|
||||
### Tests
|
||||
|
||||
* change source for live DASH playback test to fix test failures ([#1303](https://github.com/videojs/http-streaming/issues/1303)) ([128b3d7](https://github.com/videojs/http-streaming/commit/128b3d7))
|
||||
* fix IE11 encrypted VTT tests by using an actual encrypted VTT segment ([#1291](https://github.com/videojs/http-streaming/issues/1291)) ([57c0e72](https://github.com/videojs/http-streaming/commit/57c0e72))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* **package:** This updates bundled libraries to no longer be transpiled to ES5, which means IE will no longer be supported.
|
||||
* This changes the arguments for the `PlaylistController#excludePlaylist` method to take a single object instead of multiple arguments.
|
||||
* This renames four experimental options to no longer be experimental and enables Low Latency HLS support by default (`llhls: false` will still disable it, if desired).
|
||||
* rename PlaylistController
|
||||
* rename HAVE_MASTER to HAVE_MAIN_MANIFEST
|
||||
* playlist loaders updateMain and .main prop rename
|
||||
* manifest.js exports mainForMedia and addPropertiesToMain
|
||||
* rename media groups prop to isMainPlaylist
|
||||
* rename property to mainPlaylistLoader_
|
||||
* rename to PlaylistController#main()
|
||||
* This removes support entirely for IE11 (and older) as well as any other platforms that do not support ES6.
|
||||
* remove ^6 from the dependency version ranges.
|
||||
* Skips detected gaps immediately instead of waiting the duration of the gap before skipping
|
||||
* Removes deprecated `smoothQualityChange` option
|
||||
* remove deprecated options, properties, events.
|
||||
* remove handleManifestRedirects option. Now XHR.responseURL will always be used when available.
|
||||
|
||||
<a name="2.14.2"></a>
|
||||
## [2.14.2](https://github.com/videojs/http-streaming/compare/v2.14.1...v2.14.2) (2022-04-13)
|
||||
|
|
2
node_modules/@videojs/http-streaming/CONTRIBUTING.md
generated
vendored
2
node_modules/@videojs/http-streaming/CONTRIBUTING.md
generated
vendored
|
@ -27,4 +27,4 @@ Testing is a crucial part of any software project. For all but the most trivial
|
|||
|
||||
[karma]: http://karma-runner.github.io/
|
||||
[local]: http://localhost:9999/test/
|
||||
[conventions]: https://github.com/videojs/generator-videojs-plugin/blob/master/docs/conventions.md
|
||||
[conventions]: https://github.com/videojs/generator-videojs-plugin/blob/main/docs/conventions.md
|
||||
|
|
60
node_modules/@videojs/http-streaming/README.md
generated
vendored
60
node_modules/@videojs/http-streaming/README.md
generated
vendored
|
@ -13,7 +13,7 @@ Included in video.js 7 by default! See the [video.js 7 blog post](https://blog.v
|
|||
|
||||
Maintenance Status: Stable
|
||||
|
||||
Video.js Compatibility: 6.0, 7.0
|
||||
Video.js Compatibility: 7.x, 8.x
|
||||
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
|
@ -39,18 +39,16 @@ Video.js Compatibility: 6.0, 7.0
|
|||
- [Source](#source)
|
||||
- [List](#list)
|
||||
- [withCredentials](#withcredentials)
|
||||
- [handleManifestRedirects](#handlemanifestredirects)
|
||||
- [useCueTags](#usecuetags)
|
||||
- [parse708captions](#parse708captions)
|
||||
- [overrideNative](#overridenative)
|
||||
- [blacklistDuration](#blacklistduration)
|
||||
- [playlistExclusionDuration](#playlistexclusionduration)
|
||||
- [maxPlaylistRetries](#maxplaylistretries)
|
||||
- [bandwidth](#bandwidth)
|
||||
- [useBandwidthFromLocalStorage](#usebandwidthfromlocalstorage)
|
||||
- [enableLowInitialPlaylist](#enablelowinitialplaylist)
|
||||
- [limitRenditionByPlayerDimensions](#limitrenditionbyplayerdimensions)
|
||||
- [useDevicePixelRatio](#usedevicepixelratio)
|
||||
- [smoothQualityChange](#smoothqualitychange)
|
||||
- [allowSeeksWithinUnsafeLiveWindow](#allowseekswithinunsafelivewindow)
|
||||
- [customTagParsers](#customtagparsers)
|
||||
- [customTagMappers](#customtagmappers)
|
||||
|
@ -63,7 +61,7 @@ Video.js Compatibility: 6.0, 7.0
|
|||
- [Format](#format)
|
||||
- [Example](#example)
|
||||
- [Runtime Properties](#runtime-properties)
|
||||
- [vhs.playlists.master](#vhsplaylistsmaster)
|
||||
- [vhs.playlists.main](#vhsplaylistsmain)
|
||||
- [vhs.playlists.media](#vhsplaylistsmedia)
|
||||
- [vhs.systemBandwidth](#vhssystembandwidth)
|
||||
- [vhs.bandwidth](#vhsbandwidth)
|
||||
|
@ -174,7 +172,7 @@ This plugin does not support Flash playback. Instead, it is recommended that use
|
|||
|
||||
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).
|
||||
|
||||
Detailed option information can be found in the [videojs-contrib-eme README](https://github.com/videojs/videojs-contrib-eme/blob/master/README.md).
|
||||
Detailed option information can be found in the [videojs-contrib-eme README](https://github.com/videojs/videojs-contrib-eme/blob/main/README.md).
|
||||
|
||||
## Documentation
|
||||
[HTTP Live Streaming](https://developer.apple.com/streaming/) (HLS) has
|
||||
|
@ -283,16 +281,6 @@ is set to `true`.
|
|||
See html5rocks's [article](http://www.html5rocks.com/en/tutorials/cors/)
|
||||
for more info.
|
||||
|
||||
##### handleManifestRedirects
|
||||
* Type: `boolean`
|
||||
* Default: `false`
|
||||
* can be used as a source option
|
||||
* can be used as an initialization option
|
||||
|
||||
When the `handleManifestRedirects` property is set to `true`, manifest requests
|
||||
which are redirected will have their URL updated to the new URL for future
|
||||
requests.
|
||||
|
||||
##### useCueTags
|
||||
* Type: `boolean`
|
||||
* can be used as an initialization option
|
||||
|
@ -358,13 +346,13 @@ var player = videojs('playerId', {
|
|||
|
||||
Since MSE playback may be desirable on all browsers with some native support other than Safari, `overrideNative: !videojs.browser.IS_SAFARI` could be used.
|
||||
|
||||
##### blacklistDuration
|
||||
##### playlistExclusionDuration
|
||||
* Type: `number`
|
||||
* can be used as an initialization option
|
||||
|
||||
When the `blacklistDuration` property is set to a time duration in seconds,
|
||||
if a playlist is blacklisted, it will be blacklisted for a period of that
|
||||
customized duration. This enables the blacklist duration to be configured
|
||||
When the `playlistExclusionDuration` property is set to a time duration in seconds,
|
||||
if a playlist is excluded, it will be excluded for a period of that
|
||||
customized duration. This enables the exclusion duration to be configured
|
||||
by the user.
|
||||
|
||||
##### maxPlaylistRetries
|
||||
|
@ -414,18 +402,6 @@ This setting is `true` by default.
|
|||
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.
|
||||
This setting is `false` by default.
|
||||
|
||||
##### smoothQualityChange
|
||||
* NOTE: DEPRECATED
|
||||
* Type: `boolean`
|
||||
* can be used as a source option
|
||||
* can be used as an initialization option
|
||||
|
||||
smoothQualityChange is deprecated and will be removed in the next major version of VHS.
|
||||
|
||||
Instead of its prior behavior, smoothQualityChange will now call fastQualityChange, which
|
||||
clears the buffer, chooses a new rendition, and starts loading content from the current
|
||||
playhead position.
|
||||
|
||||
##### allowSeeksWithinUnsafeLiveWindow
|
||||
* Type: `boolean`
|
||||
* can be used as a source option
|
||||
|
@ -546,11 +522,11 @@ work across all the media types that video.js supports. If you're
|
|||
deploying videojs-http-streaming on your own website and want to make a
|
||||
couple tweaks though, go for it!
|
||||
|
||||
#### vhs.playlists.master
|
||||
#### vhs.playlists.main
|
||||
Type: `object`
|
||||
|
||||
An object representing the parsed master playlist. If a media playlist
|
||||
is loaded directly, a master playlist with only one entry will be
|
||||
An object representing the parsed main playlist. If a media playlist
|
||||
is loaded directly, a main playlist with only one entry will be
|
||||
created.
|
||||
|
||||
#### vhs.playlists.media
|
||||
|
@ -561,7 +537,7 @@ media playlist. The active media playlist is referred to when
|
|||
additional video data needs to be downloaded. Calling this function
|
||||
with no arguments returns the parsed playlist object for the active
|
||||
media playlist. Calling this function with a playlist object from the
|
||||
master playlist or a URI string as specified in the master playlist
|
||||
main playlist or a URI string as specified in the main playlist
|
||||
will kick off an asynchronous load of the specified media
|
||||
playlist. Once it has been retreived, it will become the active media
|
||||
playlist.
|
||||
|
@ -601,7 +577,7 @@ A function that returns the media playlist object to use to download
|
|||
the next segment. It is invoked by the tech immediately before a new
|
||||
segment is downloaded. You can override this function to provide your
|
||||
adaptive streaming logic. You must, however, be sure to return a valid
|
||||
media playlist object that is present in `player.tech().vhs.master`.
|
||||
media playlist object that is present in `player.tech().vhs.main`.
|
||||
|
||||
Overridding this function with your own is very powerful but is overkill
|
||||
for many purposes. Most of the time, you should use the much simpler
|
||||
|
@ -715,7 +691,7 @@ This object contains a summary of HLS and player related stats.
|
|||
| currentSource | object | The source object. Has the structure `{src: 'url', type: 'mimetype'}` |
|
||||
| currentTech | string | The name of the tech in use |
|
||||
| duration | number | Duration of the video in seconds |
|
||||
| master | object | The [master playlist object](#vhsplaylistsmaster) |
|
||||
| main | object | The [main playlist object](#vhsplaylistsmain) |
|
||||
| playerDimensions | object | Contains the width and height of the player |
|
||||
| seekable | array | List of time ranges that the player can seek to |
|
||||
| timestamp | number | Timestamp of when `vhs.stats` was accessed |
|
||||
|
@ -756,7 +732,7 @@ player.on('ready', () => {
|
|||
```
|
||||
|
||||
Note that these events are triggered as soon as a case is encountered, and often only
|
||||
once. For example, the `vhs-demuxed` usage event will be triggered as soon as the master
|
||||
once. For example, the `vhs-demuxed` usage event will be triggered as soon as the main
|
||||
manifest is downloaded and parsed, and will not be triggered again.
|
||||
|
||||
#### Presence Stats
|
||||
|
@ -765,11 +741,11 @@ Each of the following usage events are fired once per source if (and when) detec
|
|||
|
||||
| Name | Description |
|
||||
| ------------- | ------------- |
|
||||
| vhs-webvtt | master manifest has at least one segmented WebVTT playlist |
|
||||
| vhs-webvtt | main manifest has at least one segmented WebVTT playlist |
|
||||
| vhs-aes | a playlist is AES encrypted |
|
||||
| vhs-fmp4 | a playlist used fMP4 segments |
|
||||
| vhs-demuxed | audio and video are demuxed by default |
|
||||
| vhs-alternate-audio | alternate audio available in the master manifest |
|
||||
| vhs-alternate-audio | alternate audio available in the main manifest |
|
||||
| vhs-playlist-cue-tags | a playlist used cue tags (see useCueTags(#usecuetags) for details) |
|
||||
| vhs-bandwidth-from-local-storage | starting bandwidth was retrieved from local storage (see useBandwidthFromLocalStorage(#useBandwidthFromLocalStorage) for details) |
|
||||
| vhs-throughput-from-local-storage | starting throughput was retrieved from local storage (see useBandwidthFromLocalStorage(#useBandwidthFromLocalStorage) for details) |
|
||||
|
@ -785,7 +761,7 @@ Each of the following usage events are fired per use:
|
|||
| vhs-audio-change | a user selected an alternate audio stream |
|
||||
| vhs-rendition-disabled | a rendition was disabled |
|
||||
| vhs-rendition-enabled | a rendition was enabled |
|
||||
| vhs-rendition-blacklisted | a rendition was blacklisted |
|
||||
| vhs-rendition-excluded| a rendition was excluded |
|
||||
| vhs-timestamp-offset | a timestamp offset was set in HLS (can identify discontinuities) |
|
||||
| vhs-unknown-waiting | the player stopped for an unknown reason and we seeked to current time try to address it |
|
||||
| vhs-live-resync | playback fell off the back of a live playlist and we resynced to the live point |
|
||||
|
|
64731
node_modules/@videojs/http-streaming/dist/videojs-http-streaming-sync-workers.js
generated
vendored
64731
node_modules/@videojs/http-streaming/dist/videojs-http-streaming-sync-workers.js
generated
vendored
File diff suppressed because it is too large
Load diff
10975
node_modules/@videojs/http-streaming/dist/videojs-http-streaming.cjs.js
generated
vendored
10975
node_modules/@videojs/http-streaming/dist/videojs-http-streaming.cjs.js
generated
vendored
File diff suppressed because it is too large
Load diff
10972
node_modules/@videojs/http-streaming/dist/videojs-http-streaming.es.js
generated
vendored
10972
node_modules/@videojs/http-streaming/dist/videojs-http-streaming.es.js
generated
vendored
File diff suppressed because it is too large
Load diff
64628
node_modules/@videojs/http-streaming/dist/videojs-http-streaming.js
generated
vendored
64628
node_modules/@videojs/http-streaming/dist/videojs-http-streaming.js
generated
vendored
File diff suppressed because it is too large
Load diff
11
node_modules/@videojs/http-streaming/dist/videojs-http-streaming.min.js
generated
vendored
11
node_modules/@videojs/http-streaming/dist/videojs-http-streaming.min.js
generated
vendored
File diff suppressed because one or more lines are too long
15
node_modules/@videojs/http-streaming/docs/README.md
generated
vendored
15
node_modules/@videojs/http-streaming/docs/README.md
generated
vendored
|
@ -1,3 +1,14 @@
|
|||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
|
||||
- [Overview](#overview)
|
||||
- [HTTP Live Streaming](#http-live-streaming)
|
||||
- [Dynamic Adaptive Streaming over HTTP](#dynamic-adaptive-streaming-over-http)
|
||||
- [Further Documentation](#further-documentation)
|
||||
- [Helpful Tools](#helpful-tools)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
# Overview
|
||||
This project supports both [HLS][hls] and [MPEG-DASH][dash] playback in the video.js player. This document is intended as a primer for anyone interested in contributing or just better understanding how bits from a server get turned into video on their display.
|
||||
|
||||
|
@ -7,11 +18,11 @@ This project supports both [HLS][hls] and [MPEG-DASH][dash] playback in the vide
|
|||
- Delivered over HTTP(S): it uses the standard application protocol of the web to deliver all its data
|
||||
- Segmented: longer videos are broken up into smaller chunks which can be downloaded independently and switched between at runtime
|
||||
|
||||
A standard HLS stream consists of a *Master Playlist* which references one or more *Media Playlists*. Each Media Playlist contains one or more sequential video segments. All these components form a logical hierarchy that informs the player of the different quality levels of the video available and how to address the individual segments of video at each of those levels:
|
||||
A standard HLS stream consists of a *Main Playlist* which references one or more *Media Playlists*. Each Media Playlist contains one or more sequential video segments. All these components form a logical hierarchy that informs the player of the different quality levels of the video available and how to address the individual segments of video at each of those levels:
|
||||
|
||||

|
||||
|
||||
HLS streams can be delivered in two different modes: a "static" mode for videos that can be played back from any point, often referred to as video-on-demand (VOD); or a "live" mode where later portions of the video become available as time goes by. In the static mode, the Master and Media playlists are fixed. The player is guaranteed that the set of video segments referenced by those playlists will not change over time.
|
||||
HLS streams can be delivered in two different modes: a "static" mode for videos that can be played back from any point, often referred to as video-on-demand (VOD); or a "live" mode where later portions of the video become available as time goes by. In the static mode, the Main and Media playlists are fixed. The player is guaranteed that the set of video segments referenced by those playlists will not change over time.
|
||||
|
||||
Live mode can work in one of two ways. For truly live events, the most common configuration is for each individual Media Playlist to only include the latest video segment and a small number of consecutive previous segments. In this mode, the player may be able to seek backwards a short time in the video but probably not all the way back to the beginning. In the other live configuration, new video segments can be appended to the Media Playlists but older segments are never removed. This configuration allows the player to seek back to the beginning of the stream at any time during the broadcast and transitions seamlessly to the static stream type when the event finishes.
|
||||
|
||||
|
|
84
node_modules/@videojs/http-streaming/docs/a-walk-through-vhs.md
generated
vendored
84
node_modules/@videojs/http-streaming/docs/a-walk-through-vhs.md
generated
vendored
|
@ -58,25 +58,23 @@ flowchart TD
|
|||
> * most EME handling for DRM and setup of the [videojs-contrib-eme plugin](https://github.com/videojs/videojs-contrib-eme)
|
||||
> * mapping/handling of options passed down via Video.js
|
||||
|
||||
## MasterPlaylistController
|
||||
## PlaylistController
|
||||
|
||||
One critical object that `VhsHandler`'s constructor creates is a new `MasterPlaylistController`.
|
||||
One critical object that `VhsHandler`'s constructor creates is a new `PlaylistController`.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
VhsSourceHandler --> VhsHandler
|
||||
VhsHandler --> MasterPlaylistController
|
||||
VhsHandler --> PlaylistController
|
||||
```
|
||||
|
||||
`MasterPlaylistController` is not a great name, and has grown in size to be a bit unwieldy, but it's the hub of VHS. Eventually, it should be broken into smaller pieces, but for now, it handles the creation and management of most of the other VHS modules. Its code can be found in [src/master-playlist-controller.js](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/master-playlist-controller.js).
|
||||
`PlaylistController` is not a great name, and has grown in size to be a bit unwieldy, but it's the hub of VHS. Eventually, it should be broken into smaller pieces, but for now, it handles the creation and management of most of the other VHS modules. Its code can be found in [src/playlist-controller.js](/src/playlist-controller.js).
|
||||
|
||||
The best way to think of `MasterPlaylistController` is as Tron's Master Control Program, though hopefully it isn't as evil.
|
||||
`PlaylistController` is a lot to say. So we often refer to it as PC.
|
||||
|
||||
`MasterPlaylistController` is a lot to say. So we often refer to it as MPC.
|
||||
If you need to find a place where different modules communicate, you will probably end up in PC. Just about all of `VhsHandler` that doesn't interface with Video.js or other plugins, interfaces with PC.
|
||||
|
||||
If you need to find a place where different modules communicate, you will probably end up in MPC. Just about all of `VhsHandler` that doesn't interface with Video.js or other plugins, interfaces with MPC.
|
||||
|
||||
MPC's [constructor](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/master-playlist-controller.js#L148) does a lot. Instead of listing all of the things it does, let's go step-by-step through the main ones, passing the source we had above.
|
||||
PC's [constructor](/src/playlist-controller.js#L148) does a lot. Instead of listing all of the things it does, let's go step-by-step through the main ones, passing the source we had above.
|
||||
|
||||
```html
|
||||
<video-js id="myPlayer" class="video-js" data-setup='{}'>
|
||||
|
@ -84,18 +82,18 @@ MPC's [constructor](https://github.com/videojs/http-streaming/blob/0964cb4827d9e
|
|||
</video-js>
|
||||
```
|
||||
|
||||
Looking at the `<source>` tag, `VhsSourceHandler` already used the "type" to tell Video.js that it could handle the source. `VhsHandler` took the manifest URL, in this case "manifest.m3u8" and provided it to the constructor of MPC.
|
||||
Looking at the `<source>` tag, `VhsSourceHandler` already used the "type" to tell Video.js that it could handle the source. `VhsHandler` took the manifest URL, in this case "manifest.m3u8" and provided it to the constructor of PC.
|
||||
|
||||
The first thing that MPC must do is download that source, but it doesn't make the request itself. Instead, it creates [this.masterPlaylistLoader_](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/master-playlist-controller.js#L264-L266).
|
||||
The first thing that PC must do is download that source, but it doesn't make the request itself. Instead, it creates [this.mainPlaylistLoader_](/src/laylist-controller.js#L263-L265).
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
VhsSourceHandler --> VhsHandler
|
||||
VhsHandler --> MasterPlaylistController
|
||||
MasterPlaylistController --> PlaylistLoader
|
||||
VhsHandler --> PlaylistController
|
||||
PlaylistController --> PlaylistLoader
|
||||
```
|
||||
|
||||
`masterPlaylistLoader_` is an instance of either the [HLS PlaylistLoader](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/playlist-loader.js#L379) or the [DashPlaylistLoader](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/dash-playlist-loader.js#L259).
|
||||
`mainPlaylistLoader_` is an instance of either the [HLS PlaylistLoader](/src/playlist-loader.js#L379) or the [DashPlaylistLoader](/src/dash-playlist-loader.js#L259).
|
||||
|
||||
The names betray their use. They load the playlist. The URL ("manifest.m3u8" here) is given, and the manifest/playlist is downloaded and parsed. If the content is live, the playlist loader also handles refreshing the manifest. For HLS, where manifests point to other manifests, the playlist loader requests those as well.
|
||||
|
||||
|
@ -117,9 +115,9 @@ Manifest {
|
|||
}
|
||||
```
|
||||
|
||||
Many properties are removed for simplicity. This is a top level manifest (often referred to as a master or main manifest), and within it there are playlists, each playlist being a Manifest itself. Since the JSON "schema" for main and media playlists is the same, you will see irrelevant properties within any given manifest object. For instance, you might see a `targetDuration` property on the main manifest object, though a main manifest doesn't have a target duration. You can ignore irrelevant properties. Eventually they should be cleaned up, and a proper schema defined for manifest objects.
|
||||
Many properties are removed for simplicity. This is a top level manifest (often referred to as a main manifest or a multivariant manifest [from the HLS spec]), and within it there are playlists, each playlist being a Manifest itself. Since the JSON "schema" for main and media playlists is the same, you will see irrelevant properties within any given manifest object. For instance, you might see a `targetDuration` property on the main manifest object, though a main manifest doesn't have a target duration. You can ignore irrelevant properties. Eventually they should be cleaned up, and a proper schema defined for manifest objects.
|
||||
|
||||
MPC will also use `masterPlaylistLoader_` to select which media playlist is active (e.g., the 720p rendition or the 480p rendition), so that `masterPlaylistLoader_` will only need to refresh that individual playlist if the stream is live.
|
||||
PC will also use `mainPlaylistLoader_` to select which media playlist is active (e.g., the 720p rendition or the 480p rendition), so that `mainPlaylistLoader_` will only need to refresh that individual playlist if the stream is live.
|
||||
|
||||
> :information_source: **Future Work**
|
||||
>
|
||||
|
@ -129,26 +127,26 @@ MPC will also use `masterPlaylistLoader_` to select which media playlist is acti
|
|||
|
||||
### Media Source Extensions
|
||||
|
||||
The next thing MPC needs to do is set up a media source for [Media Source Extensions](https://www.w3.org/TR/media-source/). Specifically, it needs to create [this.mediaSource](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/master-playlist-controller.js#L210) and its associated [source buffers](https://github.com/videojs/http-streaming/blob/main/src/master-playlist-controller.js#L1818). These are where audio and video data will be appended, so that the browser has content to play. But those aren't used directly. Because source buffers can only handle one operation at a time, [this.sourceUpdater_](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/master-playlist-controller.js#L234) is created. `sourceUpdater_` is a queue for operations performed on the source buffers. That's pretty much it. So all of the MSE pieces for appending get wrapped up in `sourceUpdater_`.
|
||||
The next thing PC needs to do is set up a media source for [Media Source Extensions](https://www.w3.org/TR/media-source/). Specifically, it needs to create [this.mediaSource](/src/playlist-controller.js#L208) and its associated [source buffers](/src/playlist-controller.js#L1814). These are where audio and video data will be appended, so that the browser has content to play. But those aren't used directly. Because source buffers can only handle one operation at a time, [this.sourceUpdater_](/src/playlist-controller.js#L232) is created. `sourceUpdater_` is a queue for operations performed on the source buffers. That's pretty much it. So all of the MSE pieces for appending get wrapped up in `sourceUpdater_`.
|
||||
|
||||
## Segment Loaders
|
||||
|
||||
The SourceUpdater created for MSE above is passed to the segment loaders.
|
||||
|
||||
[this.mainSegmentLoader_](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/master-playlist-controller.js#L271-L275) is used for muxed content (audio and video in one segment) and for audio or video only streams.
|
||||
[this.mainSegmentLoader_](/src/playlist-controller.js#L270-L274) is used for muxed content (audio and video in one segment) and for audio or video only streams.
|
||||
|
||||
[this.audioSegmentLoader_](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/master-playlist-controller.js#L278-L281) is used when the content is demuxed (audio and video in separate playlists).
|
||||
[this.audioSegmentLoader_](/src/playlist-controller.js#L277-L280) is used when the content is demuxed (audio and video in separate playlists).
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
VhsSourceHandler --> VhsHandler
|
||||
VhsHandler --> MasterPlaylistController
|
||||
MasterPlaylistController --> PlaylistLoader
|
||||
MasterPlaylistController --> SourceUpdater
|
||||
MasterPlaylistController --> SegmentLoader
|
||||
VhsHandler --> PlaylistController
|
||||
PlaylistController --> PlaylistLoader
|
||||
PlaylistController --> SourceUpdater
|
||||
PlaylistController --> SegmentLoader
|
||||
```
|
||||
|
||||
Besides options and the `sourceUpdater_` from MPC, the segment loaders are given a [playlist](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/segment-loader.js#L988). This playlist is a media playlist from the `masterPlaylistLoader_`. So looking back at our parsed manifest object:
|
||||
Besides options and the `sourceUpdater_` from PC, the segment loaders are given a [playlist](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/segment-loader.js#L988). This playlist is a media playlist from the `mainPlaylistLoader_`. So looking back at our parsed manifest object:
|
||||
|
||||
```
|
||||
Manifest {
|
||||
|
@ -174,19 +172,19 @@ VHS uses a strategy called `mediaIndex++` for choosing the next segment, see [he
|
|||
|
||||
If there are no seeks or rendition changes, `chooseNextRequest_` will rely on the `mediaIndex++` strategy.
|
||||
|
||||
If there are seeks or rendition changes, then `chooseNextRequest_` will look at segment timing values via the `SyncController` (created previously in MPC), the current time, and the buffer, to determine what the next segment should be, and what it's start time should be (to position it on the timeline).
|
||||
If there are seeks or rendition changes, then `chooseNextRequest_` will look at segment timing values via the `SyncController` (created previously in PC), the current time, and the buffer, to determine what the next segment should be, and what it's start time should be (to position it on the timeline).
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
VhsSourceHandler --> VhsHandler
|
||||
VhsHandler --> MasterPlaylistController
|
||||
MasterPlaylistController --> PlaylistLoader
|
||||
MasterPlaylistController --> SourceUpdater
|
||||
MasterPlaylistController --> SegmentLoader
|
||||
VhsHandler --> PlaylistController
|
||||
PlaylistController --> PlaylistLoader
|
||||
PlaylistController --> SourceUpdater
|
||||
PlaylistController --> SegmentLoader
|
||||
SegmentLoader --> SyncController
|
||||
```
|
||||
|
||||
The `SyncController` has various [strategies](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/sync-controller.js#L16) for ensuring that different renditions, which can have different media sequence and segment timing values, can be positioned on the playback timeline successfully. (It is also be [used by MPC](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/master-playlist-controller.js#L1477) to establish a `seekable` range.)
|
||||
The `SyncController` has various [strategies](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/sync-controller.js#L16) for ensuring that different renditions, which can have different media sequence and segment timing values, can be positioned on the playback timeline successfully. (It is also be [used by PC](/src/playlist-controller.js#L1472) to establish a `seekable` range.)
|
||||
|
||||
### Downloading and Appending Segments
|
||||
|
||||
|
@ -196,10 +194,10 @@ If the buffer is not full, and a segment was chosen, then `SegmentLoader` will d
|
|||
```mermaid
|
||||
flowchart TD
|
||||
VhsSourceHandler --> VhsHandler
|
||||
VhsHandler --> MasterPlaylistController
|
||||
MasterPlaylistController --> PlaylistLoader
|
||||
MasterPlaylistController --> SourceUpdater
|
||||
MasterPlaylistController --> SegmentLoader
|
||||
VhsHandler --> PlaylistController
|
||||
PlaylistController --> PlaylistLoader
|
||||
PlaylistController --> SourceUpdater
|
||||
PlaylistController --> SegmentLoader
|
||||
SegmentLoader --> SyncController
|
||||
SegmentLoader --> mediaSegmentRequest
|
||||
```
|
||||
|
@ -213,10 +211,10 @@ When the `SegmentLoader` receives segment data events, it can append the data to
|
|||
```mermaid
|
||||
flowchart TD
|
||||
VhsSourceHandler --> VhsHandler
|
||||
VhsHandler --> MasterPlaylistController
|
||||
MasterPlaylistController --> PlaylistLoader
|
||||
MasterPlaylistController --> SourceUpdater
|
||||
MasterPlaylistController --> SegmentLoader
|
||||
VhsHandler --> PlaylistController
|
||||
PlaylistController --> PlaylistLoader
|
||||
PlaylistController --> SourceUpdater
|
||||
PlaylistController --> SegmentLoader
|
||||
SegmentLoader --> SyncController
|
||||
SegmentLoader --> mediaSegmentRequest
|
||||
SegmentLoader --> SourceUpdater
|
||||
|
@ -231,10 +229,10 @@ Besides downloading segments, `mediaSegmentRequest` [decrypts](https://github.co
|
|||
```mermaid
|
||||
flowchart TD
|
||||
VhsSourceHandler --> VhsHandler
|
||||
VhsHandler --> MasterPlaylistController
|
||||
MasterPlaylistController --> PlaylistLoader
|
||||
MasterPlaylistController --> SourceUpdater
|
||||
MasterPlaylistController --> SegmentLoader
|
||||
VhsHandler --> PlaylistController
|
||||
PlaylistController --> PlaylistLoader
|
||||
PlaylistController --> SourceUpdater
|
||||
PlaylistController --> SegmentLoader
|
||||
SegmentLoader --> SyncController
|
||||
SegmentLoader --> mediaSegmentRequest
|
||||
SegmentLoader --> SourceUpdater
|
||||
|
@ -247,4 +245,4 @@ The video can start playing as soon as there's enough audio and video (for muxed
|
|||
|
||||
But once `SegmentLoader` does finish, it starts the process again, looking for new content.
|
||||
|
||||
There are other modules, and other functions of the code (e.g., blacklisting logic, ABR, etc.), but this is the most critical path of VHS, the one that allows video to play in the browser.
|
||||
There are other modules, and other functions of the code (e.g., excluding logic, ABR, etc.), but this is the most critical path of VHS, the one that allows video to play in the browser.
|
||||
|
|
2
node_modules/@videojs/http-streaming/docs/arch.md
generated
vendored
2
node_modules/@videojs/http-streaming/docs/arch.md
generated
vendored
|
@ -13,7 +13,7 @@ The [playlist loader](../src/playlist-loader.js) handles all of the details of r
|
|||
During VOD playback, the loader will move quickly to the HAVE_METADATA state and then stay there unless a quality switch request sends it to SWITCHING_MEDIA while it fetches an alternate playlist. The loader enters the HAVE_CURRENT_METADATA when a live stream is detected and it's time to refresh the current media playlist to find out about new video segments.
|
||||
|
||||
### HLS Tech
|
||||
Currently, the HLS project integrates with [video.js](http://www.videojs.com/) as a [tech](https://github.com/videojs/video.js/blob/master/docs/guides/tech.md). That means it's responsible for providing an interface that closely mirrors the `<video>` element. You can see that implementation in [videojs-http-streaming.js](../src/videojs-http-streaming.js), the primary entry point of the project.
|
||||
Currently, the HLS project integrates with [video.js](http://www.videojs.com/) as a [tech](https://github.com/videojs/video.js/blob/main/docs/guides/tech.md). That means it's responsible for providing an interface that closely mirrors the `<video>` element. You can see that implementation in [videojs-http-streaming.js](../src/videojs-http-streaming.js), the primary entry point of the project.
|
||||
|
||||
### Transmuxing
|
||||
Most browsers don't have support for the file type that HLS video segments are stored in. To get HLS playing back on those browsers, contrib-hls strings together a number of technologies:
|
||||
|
|
2
node_modules/@videojs/http-streaming/docs/bitrate-switching.md
generated
vendored
2
node_modules/@videojs/http-streaming/docs/bitrate-switching.md
generated
vendored
|
@ -34,7 +34,7 @@ gets used:
|
|||

|
||||
|
||||
If it turns out no rendition is acceptable based on the filtering
|
||||
described above, the first encoding listed in the master playlist will
|
||||
described above, the first encoding listed in the main playlist will
|
||||
be used.
|
||||
|
||||
If you'd like your player to use a different set of priorities, it's
|
||||
|
|
20
node_modules/@videojs/http-streaming/docs/dash-playlist-loader.md
generated
vendored
20
node_modules/@videojs/http-streaming/docs/dash-playlist-loader.md
generated
vendored
|
@ -22,19 +22,19 @@ The [DPL] is written to be as similar as possible to the [PlaylistLoader][pl]. T
|
|||

|
||||
|
||||
- `HAVE_NOTHING` the state before the MPD is received and parsed.
|
||||
- `HAVE_MASTER` the state before a media stream is setup but the MPD has been parsed.
|
||||
- `HAVE_MAIN_MANIFEST` the state before a media stream is setup but the MPD has been parsed.
|
||||
- `HAVE_METADATA` the state after a media stream is setup.
|
||||
|
||||
### API
|
||||
|
||||
- `load()` this will either start or kick the loader during playback.
|
||||
- `start()` this will start the [DPL] and request the MPD.
|
||||
- `parseMasterXml()` this will parse the MPD manifest and return the result.
|
||||
- `parseMainXml()` this will parse the MPD manifest and return the result.
|
||||
- `media()` this will return the currently active media stream or set a new active media stream.
|
||||
|
||||
### Events
|
||||
|
||||
- `loadedplaylist` signals the setup of a master playlist, representing the DASH source as a whole, from the MPD; or a media playlist, representing a media stream.
|
||||
- `loadedplaylist` signals the setup of a main playlist, representing the DASH source as a whole, from the MPD; or a media playlist, representing a media stream.
|
||||
- `loadedmetadata` signals initial setup of a media stream.
|
||||
- `minimumUpdatePeriod` signals that a update period has ended and the MPD must be requested again.
|
||||
- `playlistunchanged` signals that no changes have been made to a MPD.
|
||||
|
@ -44,7 +44,7 @@ The [DPL] is written to be as similar as possible to the [PlaylistLoader][pl]. T
|
|||
|
||||
### Interaction with Other Modules
|
||||
|
||||

|
||||

|
||||
|
||||
### Special Features
|
||||
|
||||
|
@ -64,17 +64,17 @@ To be filled out.
|
|||
|
||||
### Previous Behavior
|
||||
|
||||
Until version 1.9.0 of [VHS], we thought that [DPL] could skip the `HAVE_NOTHING` and `HAVE_MASTER` states, as no other XHR requests are needed once the MPD has been downloaded and parsed. However, this is incorrect as there are some Presentations that signal the use of a "Segment Index box" or `sidx`. This `sidx` references specific byte ranges in a file that could contain media or potentially other `sidx` boxes.
|
||||
Until version 1.9.0 of [VHS], we thought that [DPL] could skip the `HAVE_NOTHING` and `HAVE_MAIN_MANIFEST` states, as no other XHR requests are needed once the MPD has been downloaded and parsed. However, this is incorrect as there are some Presentations that signal the use of a "Segment Index box" or `sidx`. This `sidx` references specific byte ranges in a file that could contain media or potentially other `sidx` boxes.
|
||||
|
||||
A DASH MPD that describes a `sidx` is therefore similar to an HLS master manifest, in that the MPD contains references to something that must be requested and parsed first before references to media segments can be obtained. With this in mind, it was necessary to update the initialization and state transitions of [DPL] to allow further XHR requests to be made after the initial request for the MPD.
|
||||
A DASH MPD that describes a `sidx` is therefore similar to an HLS main manifest, in that the MPD contains references to something that must be requested and parsed first before references to media segments can be obtained. With this in mind, it was necessary to update the initialization and state transitions of [DPL] to allow further XHR requests to be made after the initial request for the MPD.
|
||||
|
||||
### Current Behavior
|
||||
|
||||
In [this PR](https://github.com/videojs/http-streaming/pull/386), the [DPL] was updated to go through the `HAVE_NOTHING` and `HAVE_MASTER` states before arriving at `HAVE_METADATA`. If the MPD does not contain `sidx` boxes, then this transition happens quickly after `load()` is called, spending little time in the `HAVE_MASTER` state.
|
||||
In [this PR](https://github.com/videojs/http-streaming/pull/386), the [DPL] was updated to go through the `HAVE_NOTHING` and `HAVE_MAIN_MANIFEST` states before arriving at `HAVE_METADATA`. If the MPD does not contain `sidx` boxes, then this transition happens quickly after `load()` is called, spending little time in the `HAVE_MAIN_MANIFEST` state.
|
||||
|
||||
The initial media selection for `masterPlaylistLoader` is made in the `loadedplaylist` handler located in [MasterPlaylistController][mpc]. We now use `hasPendingRequest` to determine whether to automatically select a media playlist for the `masterPlaylistLoader` as a fallback in case one is not selected by [MPC]. The child [DPL]s are created with a media playlist passed in as an argument, so this fallback is not necessary for them. Instead, that media playlist is saved and auto-selected once we enter the `HAVE_MASTER` state.
|
||||
The initial media selection for `mainPlaylistLoader` is made in the `loadedplaylist` handler located in [PlaylistController][pc]. We now use `hasPendingRequest` to determine whether to automatically select a media playlist for the `mainPlaylistLoader` as a fallback in case one is not selected by [PC]. The child [DPL]s are created with a media playlist passed in as an argument, so this fallback is not necessary for them. Instead, that media playlist is saved and auto-selected once we enter the `HAVE_MAIN_MANIFEST` state.
|
||||
|
||||
The `updateMaster` method will return `null` if no updates are found.
|
||||
The `updateMain` method will return `null` if no updates are found.
|
||||
|
||||
The `selectinitialmedia` event is not triggered until an audioPlaylistLoader (which for DASH is always a child [DPL]) has a media playlist. This is signaled by triggering `loadedmetadata` on the respective [DPL]. This event is used to initialize the [Representations API][representations] and setup EME (see [contrib-eme]).
|
||||
|
||||
|
@ -82,6 +82,6 @@ The `selectinitialmedia` event is not triggered until an audioPlaylistLoader (wh
|
|||
[sl]: ../src/segment-loader.js
|
||||
[vhs]: intro.md
|
||||
[pl]: ../src/playlist-loader.js
|
||||
[mpc]: ../src/master-playlist-controller.js
|
||||
[pc]: ../src/playlist-controller.js
|
||||
[representations]: ../README.md#hlsrepresentations
|
||||
[contrib-eme]: https://github.com/videojs/videojs-contrib-eme
|
||||
|
|
4
node_modules/@videojs/http-streaming/docs/glossary.md
generated
vendored
4
node_modules/@videojs/http-streaming/docs/glossary.md
generated
vendored
|
@ -4,9 +4,9 @@
|
|||
|
||||
**Media Playlist**: This is a manifest that represents a single rendition or media stream of the source.
|
||||
|
||||
**Master Playlist Controller**: This acts as the main controller for the playback engine. It interacts with the SegmentLoaders, PlaylistLoaders, PlaybackWatcher, etc.
|
||||
**Playlist Controller**: This acts as the main controller for the playback engine. It interacts with the SegmentLoaders, PlaylistLoaders, PlaybackWatcher, etc.
|
||||
|
||||
**Playlist Loader**: This will request the source and load the master manifest. It is also instructed by the ABR algorithm to load a media playlist or wraps a media playlist if it is provided as the source. There are more details about the playlist loader [here](./arch.md).
|
||||
**Playlist Loader**: This will request the source and load the main manifest. It is also instructed by the ABR algorithm to load a media playlist or wraps a media playlist if it is provided as the source. There are more details about the playlist loader [here](./arch.md).
|
||||
|
||||
**DASH Playlist Loader**: This will do as the PlaylistLoader does, but for DASH sources. It also handles DASH specific functionaltiy, such as refreshing the MPD according to the minimumRefreshPeriod and synchronizing to a server clock.
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 169 KiB |
24
node_modules/@videojs/http-streaming/docs/images/dash-playlist-loader-states.nomnoml.svg
generated
vendored
24
node_modules/@videojs/http-streaming/docs/images/dash-playlist-loader-states.nomnoml.svg
generated
vendored
|
@ -1,12 +1,12 @@
|
|||
<svg width="228" height="310" version="1.1" baseProfile="full" viewbox="0 0 228 310" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events" style="font-weight:bold; font-size:10pt; font-family:'Arial', Helvetica, sans-serif;;stroke-width:2;stroke-linejoin:round;stroke-linecap:round"><text x="134" y="111" style="font-weight:normal;">load()</text>
|
||||
<path d="M114 81 L114 105 L114 129 L114 129 " style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<path d="M110.8 121 L114 125 L117.2 121 L114 129 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
|
||||
<text x="134" y="211" style="font-weight:normal;">media()</text>
|
||||
<path d="M114 181 L114 205 L114 229 L114 229 " style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<path d="M110.8 221 L114 225 L117.2 221 L114 229 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
|
||||
<rect x="37" y="30" height="50" width="154" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="57" y="60" style="">HAVE_NOTHING</text>
|
||||
<rect x="40" y="130" height="50" width="149" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="60" y="160" style="">HAVE_MASTER</text>
|
||||
<rect x="30" y="230" height="50" width="168" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="50" y="260" style="">HAVE_METADATA</text></svg>
|
||||
<svg width="280" height="310" version="1.1" baseProfile="full" viewbox="0 0 280 310" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events" style="font-weight:bold; font-size:10pt; font-family:'Arial', Helvetica, sans-serif;;stroke-width:2;stroke-linejoin:round;stroke-linecap:round"><text x="160" y="111" style="font-weight:normal;">load()</text>
|
||||
<path d="M140 81 L140 105 L140 129 L140 129 " style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<path d="M136.8 121 L140 125 L143.2 121 L140 129 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
|
||||
<text x="160" y="211" style="font-weight:normal;">media()</text>
|
||||
<path d="M140 181 L140 205 L140 229 L140 229 " style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<path d="M136.8 221 L140 225 L143.2 221 L140 229 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
|
||||
<rect x="63" y="30" height="50" width="154" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="83" y="60" style="">HAVE_NOTHING</text>
|
||||
<rect x="30" y="130" height="50" width="220" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="50" y="160" style="">HAVE_MAIN_MANIFEST</text>
|
||||
<rect x="56" y="230" height="50" width="168" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="76" y="260" style="">HAVE_METADATA</text></svg>
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 162 KiB |
4
node_modules/@videojs/http-streaming/docs/images/playlist-loader-states.nomnoml.svg
generated
vendored
4
node_modules/@videojs/http-streaming/docs/images/playlist-loader-states.nomnoml.svg
generated
vendored
|
@ -15,8 +15,8 @@
|
|||
<path d="M155.2 489 L152 485 L148.8 489 L152 481 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
|
||||
<rect x="75" y="30" height="50" width="154" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="95" y="60" style="">HAVE_NOTHING</text>
|
||||
<rect x="78" y="130" height="50" width="149" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="98" y="160" style="">HAVE_MASTER</text>
|
||||
<rect x="42" y="130" height="50" width="220" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="62" y="160" style="">HAVE_MAIN_MANIFEST</text>
|
||||
<rect x="56" y="230" height="50" width="192" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="76.3" y="260" style="">SWITCHING_MEDIA</text>
|
||||
<rect x="68" y="330" height="50" width="168" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
|
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
@ -1,119 +0,0 @@
|
|||
@startuml
|
||||
|
||||
header DashPlaylistLoader sequences
|
||||
title DashPlaylistLoader sequences: Master Manifest with Alternate Audio
|
||||
|
||||
Participant "MasterPlaylistController" as MPC #red
|
||||
Participant "MasterDashPlaylistLoader" as MPL #blue
|
||||
Participant "mainSegmentLoader" as SL #blue
|
||||
Participant "AudioDashPlaylistLoader" as APL #green
|
||||
Participant "audioSegmentLoader" as ASL #green
|
||||
Participant "external server" as ext #brown
|
||||
Participant "mpdParser" as parser #orange
|
||||
Participant "mediaGroups" as MG #purple
|
||||
Participant Tech #lightblue
|
||||
|
||||
== Initialization ==
|
||||
|
||||
MPC -> MPL : construct MasterPlaylistLoader
|
||||
MPC -> MPL: load()
|
||||
|
||||
== Requesting Master Manifest ==
|
||||
|
||||
MPL -> MPL : start()
|
||||
|
||||
MPL -> ext: xhr request for master manifest
|
||||
ext -> MPL : response with master manifest
|
||||
|
||||
MPL -> parser: parse manifest
|
||||
parser -> MPL: object representing manifest
|
||||
|
||||
note over MPL #lightblue: trigger 'loadedplaylist'
|
||||
|
||||
== Requesting Video Manifest ==
|
||||
|
||||
note over MPL #lightblue: handling loadedplaylist
|
||||
MPL -> MPL: media(x)
|
||||
|
||||
alt if no sidx
|
||||
note over MPL #lightgray: zero delay to fake network request
|
||||
else if sidx
|
||||
break
|
||||
MPL -> ext: request sidx
|
||||
end
|
||||
end
|
||||
|
||||
note over MPL #lightblue: trigger 'loadedmetadata' on master loader [T1]
|
||||
|
||||
note over MPL #lightblue: handling 'loadedmetadata'
|
||||
|
||||
opt vod and preload !== 'none'
|
||||
|
||||
MPL -> SL: playlist()
|
||||
MPL -> SL: load()
|
||||
|
||||
end
|
||||
|
||||
== Initializing Media Groups, Choosing Active Tracks ==
|
||||
|
||||
MPL -> MG: setupMediaGroups()
|
||||
MG -> MG: initialize()
|
||||
|
||||
== Initializing Alternate Audio Loader ==
|
||||
|
||||
MG -> APL: create child playlist loader for alt audio
|
||||
|
||||
MG -> MG: activeGroup and audio variant selected
|
||||
MG -> MG: enable activeTrack, onTrackChanged()
|
||||
MG -> ASL: reset audio segment loader
|
||||
|
||||
== Requesting Alternate Audio Manifest ==
|
||||
|
||||
MG -> MG: startLoaders()
|
||||
|
||||
MG -> APL: load()
|
||||
APL -> APL: start()
|
||||
APL -> APL: zero delay to fake network request
|
||||
|
||||
break finish pending tasks
|
||||
MG -> Tech: add audioTrack
|
||||
MPL -> MPC: setupSourceBuffers_()
|
||||
MPL -> MPC: setupFirstPlay()
|
||||
|
||||
loop mainSegmentLoader.monitorBufferTick_()
|
||||
SL -> ext: requests media segments
|
||||
ext -> SL: response with media segment bytes
|
||||
end
|
||||
end
|
||||
|
||||
APL -> APL: zero delay over
|
||||
APL -> APL: media(x)
|
||||
|
||||
alt if no sidx
|
||||
note over APL #lightgray: zero delay to fake network request
|
||||
else if sidx
|
||||
break
|
||||
MPL -> ext: request sidx
|
||||
end
|
||||
end
|
||||
|
||||
== Requesting Alternate Audio Segments ==
|
||||
|
||||
note over APL #lightblue: trigger 'loadedplaylist'
|
||||
note over APL #lightblue: handling 'loadedplaylist'
|
||||
APL -> ASL: playlist()
|
||||
|
||||
|
||||
note over ASL #lightblue: trigger 'loadedmetadata' [T2]
|
||||
note over APL #lightblue: handling 'loadedmetadata'
|
||||
APL -> ASL: playlist()
|
||||
APL -> ASL: load()
|
||||
|
||||
loop audioSegmentLoader.monitorBufferTick_()
|
||||
|
||||
ASL -> ext: requests media segments
|
||||
ext -> ASL: response with media segment bytes
|
||||
|
||||
end
|
||||
|
||||
@enduml
|
|
@ -17,5 +17,5 @@
|
|||
|
||||
#.label: align=center visual=none italic
|
||||
|
||||
[HAVE_NOTHING] load()-> [HAVE_MASTER]
|
||||
[HAVE_MASTER] media()-> [HAVE_METADATA]
|
||||
[HAVE_NOTHING] load()-> [HAVE_MAIN_MANIFEST]
|
||||
[HAVE_MAIN_MANIFEST] media()-> [HAVE_METADATA]
|
||||
|
|
|
@ -1,246 +0,0 @@
|
|||
@startuml
|
||||
|
||||
header PlaylistLoader sequences
|
||||
title PlaylistLoader sequences: Master Manifest and Alternate Audio
|
||||
|
||||
Participant "MasterPlaylistController" as MPC #red
|
||||
Participant "MasterPlaylistLoader" as MPL #blue
|
||||
Participant "mainSegmentLoader" as SL #blue
|
||||
Participant "AudioPlaylistLoader" as APL #green
|
||||
Participant "audioSegmentLoader" as ASL #green
|
||||
Participant "external server" as ext #brown
|
||||
Participant "m3u8Parser" as parser #orange
|
||||
Participant "mediaGroups" as MG #purple
|
||||
Participant Tech #lightblue
|
||||
|
||||
== Initialization ==
|
||||
|
||||
group MasterPlaylistController.constructor()
|
||||
|
||||
MPC -> MPL : setting up MasterPlaylistLoader
|
||||
note left #lightyellow
|
||||
sets up mediaupdatetimeout
|
||||
handler for live playlist staleness
|
||||
end note
|
||||
note over MPL #lightgray: state = 'HAVE_NOTHING'
|
||||
|
||||
MPC -> MPL: load()
|
||||
|
||||
end
|
||||
|
||||
group MasterPlaylistLoader.load()
|
||||
|
||||
MPL -> MPL : start()
|
||||
note left #lightyellow: not started yet
|
||||
|
||||
== Requesting Master Manifest ==
|
||||
|
||||
group start()
|
||||
|
||||
note over MPL #lightgray: started = true
|
||||
|
||||
MPL -> ext: xhr request for master manifest
|
||||
ext -> MPL : response with master manifest
|
||||
|
||||
MPL -> parser: parse master manifest
|
||||
parser -> MPL: object representing manifest
|
||||
MPL -> MPL: set loader's master playlist
|
||||
note over MPL #lightgray: state = 'HAVE_MASTER'
|
||||
note over MPL #lightblue: trigger 'loadedplaylist' on master loader
|
||||
|
||||
== Requesting Video Manifest ==
|
||||
|
||||
group 'loadedplaylist' handler
|
||||
|
||||
note over MPL #lightblue: handling loadedplaylist
|
||||
MPL -> MPL : media()
|
||||
note left #lightgray: select initial (video) playlist
|
||||
note over MPL #lightyellow: state = 'SWITCHING_MEDIA'
|
||||
|
||||
group media()
|
||||
|
||||
MPL -> ext : request child manifest
|
||||
ext -> MPL: child manifest returned
|
||||
MPL -> MPL: haveMetadata()
|
||||
note over MPL #lightyellow: state = 'HAVE_METADATA'
|
||||
|
||||
group haveMetadata()
|
||||
|
||||
MPL -> parser: parse child manifest
|
||||
parser -> MPL: object representing the child manifest
|
||||
note over MPL #lightyellow
|
||||
update master and media playlists
|
||||
end note
|
||||
|
||||
opt live
|
||||
MPL -> MPL: setup mediaupdatetimeout
|
||||
end
|
||||
|
||||
note over MPL #lightblue
|
||||
trigger 'loadedplaylist' on master loader.
|
||||
This does not end up requesting segments
|
||||
at this point.
|
||||
end note
|
||||
|
||||
group MasterPlaylistLoader 'loadedplaylist' handler
|
||||
|
||||
MPL -> MPL : setup durationchange handler
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
== Requesting Video Segments ==
|
||||
|
||||
note over MPL #lightblue: trigger 'loadedmetadata'
|
||||
|
||||
group 'loadedmetadata' handler
|
||||
|
||||
note over MPL #lightblue: handling 'loadedmetadata'
|
||||
|
||||
opt vod and preload !== 'none'
|
||||
|
||||
MPL -> SL: playlist()
|
||||
note over SL #lightyellow: updates playlist
|
||||
|
||||
MPL -> SL: load()
|
||||
note right #lightgray
|
||||
This does nothing as mimeTypes
|
||||
have not been set yet.
|
||||
end note
|
||||
|
||||
end
|
||||
|
||||
MPL -> MG: setupMediaGroups()
|
||||
|
||||
== Initializing Media Groups, Choosing Active Tracks ==
|
||||
|
||||
group MediaGroups.setupMediaGroups()
|
||||
|
||||
group initialize()
|
||||
|
||||
MG -> APL: create child playlist loader for alt audio
|
||||
note over APL #lightyellow: state = 'HAVE_NOTHING'
|
||||
|
||||
note left #lightgray
|
||||
setup 'loadedmetadata' and 'loadedplaylist' listeners
|
||||
on child alt audio playlist loader
|
||||
end note
|
||||
|
||||
MG -> Tech: add audioTracks
|
||||
|
||||
end
|
||||
|
||||
MG -> MG: activeGroup and audio variant selected
|
||||
MG -> MG: enable activeTrack, onTrackChanged()
|
||||
note left #lightgray
|
||||
There is no activePlaylistLoader at this point,
|
||||
but there is an audio playlistLoader
|
||||
end note
|
||||
|
||||
group onTrackChanged()
|
||||
|
||||
MG -> SL: reset mainSegmentLoader
|
||||
note left #lightgray: Clears buffer, aborts all inflight requests
|
||||
|
||||
== Requesting Alternate Audio Manifest ==
|
||||
|
||||
MG -> MG: startLoaders()
|
||||
|
||||
group startLoaders()
|
||||
note over MG #lightyellow
|
||||
activePlaylistLoader = AudioPlaylistLoader
|
||||
end note
|
||||
|
||||
MG -> APL: load()
|
||||
end
|
||||
|
||||
group AudioPlaylistLoader.load()
|
||||
APL -> APL: start()
|
||||
|
||||
group alt start()
|
||||
|
||||
note over APL #lightyellow: started = true
|
||||
APL -> ext: request alt audio media manifest
|
||||
|
||||
break MasterPlaylistLoader 'loadedmetadata' handler
|
||||
|
||||
MPL -> MPC: setupSourceBuffers()
|
||||
note left #lightgray
|
||||
This will set mimeType.
|
||||
Segments can be loaded from now on.
|
||||
end note
|
||||
|
||||
MPL -> MPC: setupFirstPlay()
|
||||
note left #lightgray
|
||||
Immediate exit since the player
|
||||
is paused
|
||||
end note
|
||||
|
||||
end
|
||||
|
||||
ext -> APL: responds with child manifest
|
||||
APL -> parser: parse child manifest
|
||||
parser -> APL: object representing child manifest returned
|
||||
note over APL #lightyellow: state = 'HAVE_MASTER'
|
||||
note left #lightgray: Infer a master playlist
|
||||
|
||||
APL -> APL: haveMetadata()
|
||||
note over APL #lightyellow: state = 'HAVE_METADATA'
|
||||
|
||||
group haveMetadata()
|
||||
|
||||
APL -> parser: parsing the child manifest again
|
||||
parser -> APL: returning object representing child manifest
|
||||
note over APL #lightyellow
|
||||
update master and media references
|
||||
end note
|
||||
|
||||
== Requesting Alternate Audio Segments ==
|
||||
|
||||
note over APL #lightblue: trigger 'loadedplaylist'
|
||||
|
||||
group 'loadedplaylist' handler
|
||||
|
||||
note over APL #lightblue: handling 'loadedplaylist'
|
||||
APL -> ASL: playlist()
|
||||
note over ASL #lightyellow: set playlist
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
note over APL #lightblue: trigger 'loadedmetadata'
|
||||
|
||||
group 'loadedmetadata' handler
|
||||
|
||||
note over APL #lightblue: handling 'loadedmetadata'
|
||||
|
||||
APL -> ASL: playlist()
|
||||
APL -> ASL: load()
|
||||
|
||||
loop audioSegmentLoader.load()
|
||||
|
||||
ASL -> ext: requests media segments
|
||||
ext -> ASL: response with media segment bytes
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@enduml
|
|
@ -1,114 +0,0 @@
|
|||
@startuml
|
||||
|
||||
header PlaylistLoader sequences
|
||||
title PlaylistLoader sequences: Master Manifest and Alternate Audio
|
||||
|
||||
Participant "MasterPlaylistController" as MPC #red
|
||||
Participant "MasterPlaylistLoader" as MPL #blue
|
||||
Participant "mainSegmentLoader" as SL #blue
|
||||
Participant "AudioPlaylistLoader" as APL #green
|
||||
Participant "audioSegmentLoader" as ASL #green
|
||||
Participant "external server" as ext #brown
|
||||
Participant "m3u8Parser" as parser #orange
|
||||
Participant "mediaGroups" as MG #purple
|
||||
Participant Tech #lightblue
|
||||
|
||||
== Initialization ==
|
||||
|
||||
MPC -> MPL : construct MasterPlaylistLoader
|
||||
MPC -> MPL: load()
|
||||
|
||||
MPL -> MPL : start()
|
||||
|
||||
== Requesting Master Manifest ==
|
||||
|
||||
MPL -> ext: xhr request for master manifest
|
||||
ext -> MPL : response with master manifest
|
||||
|
||||
MPL -> parser: parse master manifest
|
||||
parser -> MPL: object representing manifest
|
||||
|
||||
note over MPL #lightblue: trigger 'loadedplaylist'
|
||||
|
||||
== Requesting Video Manifest ==
|
||||
|
||||
note over MPL #lightblue: handling loadedplaylist
|
||||
MPL -> MPL : media()
|
||||
|
||||
MPL -> ext : request child manifest
|
||||
ext -> MPL: child manifest returned
|
||||
|
||||
MPL -> parser: parse child manifest
|
||||
parser -> MPL: object representing the child manifest
|
||||
|
||||
note over MPL #lightblue: trigger 'loadedplaylist'
|
||||
note over MPL #lightblue: handleing 'loadedplaylist'
|
||||
|
||||
MPL -> SL: playlist()
|
||||
MPL -> SL: load()
|
||||
|
||||
== Requesting Video Segments ==
|
||||
|
||||
note over MPL #lightblue: trigger 'loadedmetadata'
|
||||
note over MPL #lightblue: handling 'loadedmetadata'
|
||||
|
||||
opt vod and preload !== 'none'
|
||||
|
||||
MPL -> SL: playlist()
|
||||
MPL -> SL: load()
|
||||
|
||||
end
|
||||
|
||||
MPL -> MG: setupMediaGroups()
|
||||
|
||||
== Initializing Media Groups, Choosing Active Tracks ==
|
||||
|
||||
MG -> APL: create child playlist loader for alt audio
|
||||
|
||||
MG -> MG: activeGroup and audio variant selected
|
||||
MG -> MG: enable activeTrack, onTrackChanged()
|
||||
MG -> SL: reset mainSegmentLoader
|
||||
|
||||
== Requesting Alternate Audio Manifest ==
|
||||
|
||||
MG -> MG: startLoaders()
|
||||
|
||||
MG -> APL: load()
|
||||
APL -> APL: start()
|
||||
|
||||
APL -> ext: request alt audio media manifest
|
||||
|
||||
break finish pending tasks
|
||||
MG -> Tech: add audioTracks
|
||||
MPL -> MPC: setupSourceBuffers()
|
||||
MPL -> MPC: setupFirstPlay()
|
||||
|
||||
loop on monitorBufferTick
|
||||
SL -> ext: requests media segments
|
||||
ext -> SL: response with media segment bytes
|
||||
end
|
||||
end
|
||||
|
||||
ext -> APL: responds with child manifest
|
||||
|
||||
APL -> parser: parse child manifest
|
||||
parser -> APL: object representing child manifest returned
|
||||
|
||||
== Requesting Alternate Audio Segments ==
|
||||
|
||||
note over APL #lightblue: trigger 'loadedplaylist'
|
||||
note over APL #lightblue: handling 'loadedplaylist'
|
||||
APL -> ASL: playlist()
|
||||
|
||||
note over APL #lightblue: trigger 'loadedmetadata'
|
||||
note over APL #lightblue: handling 'loadedmetadata'
|
||||
APL -> ASL: playlist()
|
||||
APL -> ASL: load()
|
||||
|
||||
loop audioSegmentLoader.load()
|
||||
|
||||
ASL -> ext: requests media segments
|
||||
ext -> ASL: response with media segment bytes
|
||||
end
|
||||
|
||||
@enduml
|
|
@ -17,8 +17,8 @@
|
|||
|
||||
#.label: align=center visual=none italic
|
||||
|
||||
[HAVE_NOTHING] load()-> [HAVE_MASTER]
|
||||
[HAVE_MASTER] media()-> [SWITCHING_MEDIA]
|
||||
[HAVE_NOTHING] load()-> [HAVE_MAIN_MANIFEST]
|
||||
[HAVE_MAIN_MANIFEST] media()-> [SWITCHING_MEDIA]
|
||||
[SWITCHING_MEDIA] media()/ start()-> [HAVE_METADATA]
|
||||
|
||||
[HAVE_METADATA] <--> [<label> mediaupdatetimeout]
|
||||
|
|
4
node_modules/@videojs/http-streaming/docs/lhls/index.md
generated
vendored
4
node_modules/@videojs/http-streaming/docs/lhls/index.md
generated
vendored
|
@ -53,7 +53,7 @@ The first change was to request pieces of a segment. There are a few approaches
|
|||
|
||||
*Plain text MIME type* was chosen because of its wide support. It provides a mechanism to access progressive bytes downloaded on [XMLHttpRequest progress events](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestEventTarget/onprogress).
|
||||
|
||||
This change was made in [media-segment-request](https://github.com/videojs/http-streaming/blob/master/src/media-segment-request.js).
|
||||
This change was made in [media-segment-request](/src/media-segment-request.js).
|
||||
|
||||
### Transmux and Append Segment Pieces
|
||||
|
||||
|
@ -93,7 +93,7 @@ The transmuxing logic moved to a new module called segment-transmuxer, which wra
|
|||
|
||||
##### mux.js
|
||||
|
||||
While most of the [mux.js pipeline](https://github.com/videojs/mux.js/blob/master/docs/diagram.png) supports pushing pieces of data (and should support LHLS by default), its "flushes" to send transmuxed data back to the caller expected full segments.
|
||||
While most of the [mux.js pipeline](/docs/diagram.png) supports pushing pieces of data (and should support LHLS by default), its "flushes" to send transmuxed data back to the caller expected full segments.
|
||||
|
||||
Much of the pipeline was reused, however, the top level audio and video segment streams, as well as the entry point, were rewritten so that instead of providing a full segment on flushes, each frame of video was provided individually (audio frames still flush as a group). The new concept of partial flushes was added into the pipeline to handle this case.
|
||||
|
||||
|
|
2
node_modules/@videojs/http-streaming/docs/lhls/transmux-before-append-changes.md
generated
vendored
2
node_modules/@videojs/http-streaming/docs/lhls/transmux-before-append-changes.md
generated
vendored
|
@ -33,4 +33,4 @@ With the transmux before append and LHLS changes, we only append video init segm
|
|||
|
||||
### Test Changes
|
||||
|
||||
Some tests were removed because they were no longer relevant after the change to creating source buffers later. For instance, `waits for both main and audio loaders to finish before calling endOfStream if main loader starting media is unknown` no longer can be tested by waiting for an audio loader response and checking for end of stream, as the test will time out since MasterPlaylistController will wait for track info from the main loader before the source buffers are created. That condition is checked elsewhere.
|
||||
Some tests were removed because they were no longer relevant after the change to creating source buffers later. For instance, `waits for both main and audio loaders to finish before calling endOfStream if main loader starting media is unknown` no longer can be tested by waiting for an audio loader response and checking for end of stream, as the test will time out since PlaylistController will wait for track info from the main loader before the source buffers are created. That condition is checked elsewhere.
|
||||
|
|
30
node_modules/@videojs/http-streaming/docs/multiple-alternative-audio-tracks.md
generated
vendored
30
node_modules/@videojs/http-streaming/docs/multiple-alternative-audio-tracks.md
generated
vendored
|
@ -65,19 +65,19 @@ Corresponding AudioTrackList when media-group-1 is used (before any tracks have
|
|||
## Startup (how tracks are added and used)
|
||||
> AudioTrack & AudioTrackList live in video.js
|
||||
|
||||
1. `HLS` creates a `MasterPlaylistController` and watches for the `loadedmetadata` event
|
||||
1. `HLS` parses the m3u8 using the `MasterPlaylistController`
|
||||
1. `MasterPlaylistController` creates a `PlaylistLoader` for the master m3u8
|
||||
1. `MasterPlaylistController` creates `PlaylistLoader`s for every audio playlist
|
||||
1. `MasterPlaylistController` creates a `SegmentLoader` for the main m3u8
|
||||
1. `MasterPlaylistController` creates a `SegmentLoader` for a potential audio playlist
|
||||
1. `HLS` creates a `PlaylistController` and watches for the `loadedmetadata` event
|
||||
1. `HLS` parses the m3u8 using the `PlaylistController`
|
||||
1. `PlaylistController` creates a `PlaylistLoader` for the main m3u8
|
||||
1. `PlaylistController` creates `PlaylistLoader`s for every audio playlist
|
||||
1. `PlaylistController` creates a `SegmentLoader` for the main m3u8
|
||||
1. `PlaylistController` creates a `SegmentLoader` for a potential audio playlist
|
||||
1. `HLS` sees the `loadedmetadata` and finds the currently selected MediaGroup and all the metadata
|
||||
1. `HLS` removes all `AudioTrack`s from the `AudioTrackList`
|
||||
1. `HLS` created `AudioTrack`s for the MediaGroup and adds them to the `AudioTrackList`
|
||||
1. `HLS` calls `MasterPlaylistController`s `useAudio` with no arguments (causes it to use the currently enabled audio)
|
||||
1. `MasterPlaylistController` turns off the current audio `PlaylistLoader` if it is on
|
||||
1. `MasterPlaylistController` maps the `label` to the `PlaylistLoader` containing the audio
|
||||
1. `MasterPlaylistController` turns on that `PlaylistLoader` and the Corresponding `SegmentLoader` (master or audio only)
|
||||
1. `HLS` calls `PlaylistController`s `useAudio` with no arguments (causes it to use the currently enabled audio)
|
||||
1. `PlaylistController` turns off the current audio `PlaylistLoader` if it is on
|
||||
1. `PlaylistController` maps the `label` to the `PlaylistLoader` containing the audio
|
||||
1. `PlaylistController` turns on that `PlaylistLoader` and the Corresponding `SegmentLoader` (main or audio only)
|
||||
1. `MediaSource`/`mux.js` determine how to mux
|
||||
|
||||
## How tracks are switched
|
||||
|
@ -88,9 +88,9 @@ Corresponding AudioTrackList when media-group-1 is used (before any tracks have
|
|||
1. `AudioTrackList` enables the new `Audiotrack` and disables all others
|
||||
1. `AudioTrackList` triggers a `changed` event
|
||||
1. `HLS` sees the `changed` event and finds the newly enabled `AudioTrack`
|
||||
1. `HLS` sends the `label` for the new `AudioTrack` to `MasterPlaylistController`s `useAudio` function
|
||||
1. `MasterPlaylistController` turns off the current audio `PlaylistLoader` if it is on
|
||||
1. `MasterPlaylistController` maps the `label` to the `PlaylistLoader` containing the audio
|
||||
1. `MasterPlaylistController` maps the `label` to the `PlaylistLoader` containing the audio
|
||||
1. `MasterPlaylistController` turns on that `PlaylistLoader` and the Corresponding `SegmentLoader` (master or audio only)
|
||||
1. `HLS` sends the `label` for the new `AudioTrack` to `PlaylistController`s `useAudio` function
|
||||
1. `PlaylistController` turns off the current audio `PlaylistLoader` if it is on
|
||||
1. `PlaylistController` maps the `label` to the `PlaylistLoader` containing the audio
|
||||
1. `PlaylistController` maps the `label` to the `PlaylistLoader` containing the audio
|
||||
1. `PlaylistController` turns on that `PlaylistLoader` and the Corresponding `SegmentLoader` (main or audio only)
|
||||
1. `MediaSource`/`mux.js` determine how to mux
|
||||
|
|
8
node_modules/@videojs/http-streaming/docs/playlist-loader.md
generated
vendored
8
node_modules/@videojs/http-streaming/docs/playlist-loader.md
generated
vendored
|
@ -9,7 +9,7 @@ The [PlaylistLoader][pl] (PL) is responsible for requesting m3u8s, parsing them
|
|||
1. To request an m3u8.
|
||||
2. To parse a m3u8 into a format [videojs-http-streaming][vhs] can understand.
|
||||
3. To allow selection of a specific media stream.
|
||||
4. To refresh a live master m3u8 for changes.
|
||||
4. To refresh a live m3u8 for changes.
|
||||
|
||||
## Design
|
||||
|
||||
|
@ -18,7 +18,7 @@ The [PlaylistLoader][pl] (PL) is responsible for requesting m3u8s, parsing them
|
|||

|
||||
|
||||
- `HAVE_NOTHING` the state before the m3u8 is received and parsed.
|
||||
- `HAVE_MASTER` the state before a media manifest is parsed and setup but after the master manifest has been parsed and setup.
|
||||
- `HAVE_MAIN_MANIFEST` the state before a media manifest is parsed and setup but after the main manifest has been parsed and setup.
|
||||
- `HAVE_METADATA` the state after a media stream is setup.
|
||||
- `SWITCHING_MEDIA` the intermediary state we go though while changing to a newly selected media playlist
|
||||
- `HAVE_CURRENT_METADATA` a temporary state after requesting a refresh of the live manifest and before receiving the update
|
||||
|
@ -31,7 +31,7 @@ The [PlaylistLoader][pl] (PL) is responsible for requesting m3u8s, parsing them
|
|||
|
||||
### Events
|
||||
|
||||
- `loadedplaylist` signals the setup of a master playlist, representing the HLS source as a whole, from the m3u8; or a media playlist, representing a media stream.
|
||||
- `loadedplaylist` signals the setup of a main playlist, representing the HLS source as a whole, from the m3u8; or a media playlist, representing a media stream.
|
||||
- `loadedmetadata` signals initial setup of a media stream.
|
||||
- `playlistunchanged` signals that no changes have been made to a m3u8.
|
||||
- `mediaupdatetimeout` signals that a live m3u8 and media stream must be refreshed.
|
||||
|
@ -40,7 +40,7 @@ The [PlaylistLoader][pl] (PL) is responsible for requesting m3u8s, parsing them
|
|||
|
||||
### Interaction with Other Modules
|
||||
|
||||

|
||||

|
||||
|
||||
[pl]: ../src/playlist-loader.js
|
||||
[sl]: ../src/segment-loader.js
|
||||
|
|
2
node_modules/@videojs/http-streaming/docs/troubleshooting.md
generated
vendored
2
node_modules/@videojs/http-streaming/docs/troubleshooting.md
generated
vendored
|
@ -2,7 +2,7 @@
|
|||
|
||||
## Other troubleshooting guides
|
||||
|
||||
For issues around data embedded into media segments (e.g., 608 captions), see the [mux.js troubleshooting guide](https://github.com/videojs/mux.js/blob/master/docs/troubleshooting.md).
|
||||
For issues around data embedded into media segments (e.g., 608 captions), see the [mux.js troubleshooting guide](https://github.com/videojs/mux.js/blob/main/docs/troubleshooting.md).
|
||||
|
||||
## Tools
|
||||
|
||||
|
|
15
node_modules/@videojs/http-streaming/index.html
generated
vendored
15
node_modules/@videojs/http-streaming/index.html
generated
vendored
|
@ -27,21 +27,6 @@
|
|||
</style>
|
||||
</head>
|
||||
<body class="m-4">
|
||||
<script>
|
||||
// if we're on IE, load up the load index page
|
||||
var result = (/MSIE\s(\d+)\.\d/).exec(navigator.userAgent);
|
||||
var version = result && parseFloat(result[1]);
|
||||
|
||||
if (!version && (/Trident\/7.0/i).test(navigator.userAgent) && (/rv:11.0/).test(navigator.userAgent)) {
|
||||
// IE 11 has a different user agent string than other IE versions
|
||||
version = 11.0;
|
||||
}
|
||||
|
||||
if (version) {
|
||||
window.location.href = './old-index.html';
|
||||
}
|
||||
</script>
|
||||
|
||||
<header class="container-fluid">
|
||||
<a href="https://github.com/videojs/http-streaming" class="d-flex align-items-center pb-3 mb-5 border-bottom" style="height: 4em">
|
||||
<img src="./logo.svg" alt="VHS logo showcasing a VHS tape with the Video.js logo on the label" class="rounded mh-100">
|
||||
|
|
45
node_modules/@videojs/http-streaming/package.json
generated
vendored
45
node_modules/@videojs/http-streaming/package.json
generated
vendored
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@videojs/http-streaming",
|
||||
"version": "2.16.0",
|
||||
"version": "3.0.2",
|
||||
"description": "Play back HLS and DASH with Video.js, even where it's not natively supported",
|
||||
"main": "dist/videojs-http-streaming.cjs.js",
|
||||
"module": "dist/videojs-http-streaming.es.js",
|
||||
|
@ -18,10 +18,12 @@
|
|||
"docs": "npm-run-all docs:*",
|
||||
"docs:api": "jsdoc src -g plugins/markdown -r -d docs/api",
|
||||
"docs:toc": "doctoc --notitle README.md",
|
||||
"docs:images": "node ./scripts/create-docs-images.js",
|
||||
"docs:images:puml": "for i in docs/images/sources/*.puml; do npx water-uml export $i -f png -o \"docs/images/$(echo $i | cut -d '/' -f 4)\"; done",
|
||||
"docs:images:nomnoml": "node ./scripts/create-docs-images.js",
|
||||
"docs:images": "npm-run-all -p docs:images:puml docs:images:nomnoml",
|
||||
"clean": "shx rm -rf ./dist ./test/dist && shx mkdir -p ./dist ./test/dist",
|
||||
"lint": "vjsstandard",
|
||||
"prepublishOnly": "npm-run-all build-prod && vjsverify --verbose",
|
||||
"prepublishOnly": "npm-run-all build-prod && vjsverify --verbose --skip-es-check",
|
||||
"start": "npm-run-all -p server watch",
|
||||
"server": "karma start scripts/karma.conf.js --singleRun=false --auto-watch",
|
||||
"test": "npm-run-all lint build-test && karma start scripts/karma.conf.js",
|
||||
|
@ -56,16 +58,16 @@
|
|||
],
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@videojs/vhs-utils": "3.0.5",
|
||||
"aes-decrypter": "3.1.3",
|
||||
"@videojs/vhs-utils": "4.0.0",
|
||||
"aes-decrypter": "4.0.1",
|
||||
"global": "^4.4.0",
|
||||
"m3u8-parser": "4.8.0",
|
||||
"mpd-parser": "^0.22.1",
|
||||
"mux.js": "6.0.1",
|
||||
"video.js": "^6 || ^7"
|
||||
"m3u8-parser": "^6.0.0",
|
||||
"mpd-parser": "^1.0.1",
|
||||
"mux.js": "6.3.0",
|
||||
"video.js": "^7 || ^8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"video.js": "^6 || ^7"
|
||||
"video.js": "^7 || ^8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-replace": "^2.3.4",
|
||||
|
@ -73,33 +75,28 @@
|
|||
"@videojs/generator-helpers": "~3.1.0",
|
||||
"bootstrap": "^5.1.0",
|
||||
"d3": "^3.4.8",
|
||||
"es5-shim": "^4.5.13",
|
||||
"es6-shim": "^0.35.5",
|
||||
"jsdoc": "~3.6.6",
|
||||
"karma": "^5.2.3",
|
||||
"jsdoc": "^3.6.11",
|
||||
"karma": "^6.4.0",
|
||||
"lodash": "^4.17.4",
|
||||
"lodash-compat": "^3.10.0",
|
||||
"nomnoml": "^0.3.0",
|
||||
"nomnoml": "^1.5.2",
|
||||
"rollup": "^2.36.1",
|
||||
"rollup-plugin-worker-factory": "0.5.7",
|
||||
"shelljs": "^0.8.4",
|
||||
"shelljs": "^0.8.5",
|
||||
"sinon": "^8.1.1",
|
||||
"url-toolkit": "^2.2.1",
|
||||
"videojs-contrib-eme": "^3.8.1",
|
||||
"videojs-contrib-eme": "^5.0.1",
|
||||
"videojs-contrib-quality-levels": "^2.0.4",
|
||||
"videojs-generate-karma-config": "^7.1.0",
|
||||
"videojs-generate-rollup-config": "^6.2.2",
|
||||
"videojs-generate-karma-config": "^8.0.1",
|
||||
"videojs-generate-rollup-config": "^7.0.0",
|
||||
"videojs-generator-verify": "~3.0.1",
|
||||
"videojs-http-source-selector": "^1.1.6",
|
||||
"videojs-standard": "^9.0.0"
|
||||
"videojs-standard": "^9.0.0",
|
||||
"water-plant-uml": "^2.0.2"
|
||||
},
|
||||
"generator-videojs-plugin": {
|
||||
"version": "7.6.3"
|
||||
},
|
||||
"browserslist": [
|
||||
"defaults",
|
||||
"ie 11"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
|
|
56
node_modules/@videojs/http-streaming/scripts/index.js
generated
vendored
56
node_modules/@videojs/http-streaming/scripts/index.js
generated
vendored
|
@ -21,7 +21,7 @@
|
|||
rep.playlist.disabled = rep.id !== id;
|
||||
});
|
||||
|
||||
window.mpc.fastQualityChange_();
|
||||
window.pc.fastQualityChange_();
|
||||
});
|
||||
var isManifestObjectType = function(url) {
|
||||
return (/application\/vnd\.videojs\.vhs\+json/).test(url);
|
||||
|
@ -270,7 +270,7 @@
|
|||
width: rep.width
|
||||
});
|
||||
|
||||
if (window.mpc.media().id === rep.id) {
|
||||
if (window.pc.media().id === rep.id) {
|
||||
selectedIndex = i;
|
||||
}
|
||||
|
||||
|
@ -390,29 +390,29 @@
|
|||
return;
|
||||
}
|
||||
|
||||
videoBufferedStat.textContent = getBuffered(player.tech(true).vhs.masterPlaylistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer &&
|
||||
player.tech(true).vhs.masterPlaylistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer.buffered);
|
||||
videoBufferedStat.textContent = getBuffered(player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer &&
|
||||
player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer.buffered);
|
||||
|
||||
// demuxed audio
|
||||
var audioBuffer = getBuffered(player.tech(true).vhs.masterPlaylistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer &&
|
||||
player.tech(true).vhs.masterPlaylistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer.buffered);
|
||||
var audioBuffer = getBuffered(player.tech(true).vhs.playlistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer &&
|
||||
player.tech(true).vhs.playlistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer.buffered);
|
||||
|
||||
// muxed audio
|
||||
if (!audioBuffer) {
|
||||
audioBuffer = getBuffered(player.tech(true).vhs.masterPlaylistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer &&
|
||||
player.tech(true).vhs.masterPlaylistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer.buffered);
|
||||
audioBuffer = getBuffered(player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer &&
|
||||
player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer.buffered);
|
||||
}
|
||||
audioBufferedStat.textContent = audioBuffer;
|
||||
|
||||
if (player.tech(true).vhs.masterPlaylistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer) {
|
||||
audioTimestampOffset.textContent = player.tech(true).vhs.masterPlaylistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer.timestampOffset;
|
||||
if (player.tech(true).vhs.playlistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer) {
|
||||
audioTimestampOffset.textContent = player.tech(true).vhs.playlistController_.audioSegmentLoader_.sourceUpdater_.audioBuffer.timestampOffset;
|
||||
|
||||
} else if (player.tech(true).vhs.masterPlaylistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer) {
|
||||
audioTimestampOffset.textContent = player.tech(true).vhs.masterPlaylistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer.timestampOffset;
|
||||
} else if (player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer) {
|
||||
audioTimestampOffset.textContent = player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.audioBuffer.timestampOffset;
|
||||
}
|
||||
|
||||
if (player.tech(true).vhs.masterPlaylistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer) {
|
||||
videoTimestampOffset.textContent = player.tech(true).vhs.masterPlaylistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer.timestampOffset;
|
||||
if (player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer) {
|
||||
videoTimestampOffset.textContent = player.tech(true).vhs.playlistController_.mainSegmentLoader_.sourceUpdater_.videoBuffer.timestampOffset;
|
||||
}
|
||||
|
||||
// bitrates
|
||||
|
@ -512,6 +512,20 @@
|
|||
});
|
||||
});
|
||||
|
||||
[
|
||||
'llhls'
|
||||
].forEach(function(name) {
|
||||
stateEls[name].checked = true;
|
||||
});
|
||||
|
||||
[
|
||||
'exact-manifest-timings',
|
||||
'pixel-diff-selector',
|
||||
'buffer-water'
|
||||
].forEach(function(name) {
|
||||
stateEls[name].checked = false;
|
||||
});
|
||||
|
||||
stateEls.debug.addEventListener('change', function(event) {
|
||||
saveState();
|
||||
window.videojs.log.level(event.target.checked ? 'debug' : 'info');
|
||||
|
@ -566,10 +580,10 @@
|
|||
html5: {
|
||||
vhs: {
|
||||
overrideNative: getInputValue(stateEls['override-native']),
|
||||
experimentalBufferBasedABR: getInputValue(stateEls['buffer-water']),
|
||||
experimentalLLHLS: getInputValue(stateEls.llhls),
|
||||
experimentalExactManifestTimings: getInputValue(stateEls['exact-manifest-timings']),
|
||||
experimentalLeastPixelDiffSelector: getInputValue(stateEls['pixel-diff-selector']),
|
||||
bufferBasedABR: getInputValue(stateEls['buffer-water']),
|
||||
llhls: getInputValue(stateEls.llhls),
|
||||
exactManifestTimings: getInputValue(stateEls['exact-manifest-timings']),
|
||||
leastPixelDiffSelector: getInputValue(stateEls['pixel-diff-selector']),
|
||||
useNetworkInformationApi: getInputValue(stateEls['network-info']),
|
||||
useDtsForTimestampOffset: getInputValue(stateEls['dts-offset'])
|
||||
}
|
||||
|
@ -635,13 +649,13 @@
|
|||
player.on('loadedmetadata', function() {
|
||||
if (player.tech_.vhs) {
|
||||
window.vhs = player.tech_.vhs;
|
||||
window.mpc = player.tech_.vhs.masterPlaylistController_;
|
||||
window.mpc.masterPlaylistLoader_.on('mediachange', regenerateRepresentations);
|
||||
window.pc = player.tech_.vhs.playlistController_;
|
||||
window.pc.mainPlaylistLoader_.on('mediachange', regenerateRepresentations);
|
||||
regenerateRepresentations();
|
||||
|
||||
} else {
|
||||
window.vhs = null;
|
||||
window.mpc = null;
|
||||
window.pc = null;
|
||||
}
|
||||
});
|
||||
cb(player);
|
||||
|
|
8
node_modules/@videojs/http-streaming/scripts/karma.conf.js
generated
vendored
8
node_modules/@videojs/http-streaming/scripts/karma.conf.js
generated
vendored
|
@ -10,13 +10,10 @@ module.exports = function(config) {
|
|||
preferHeadless: false,
|
||||
browsers(aboutToRun) {
|
||||
return aboutToRun.filter(function(launcherName) {
|
||||
return !(/^(Safari|Chromium)/).test(launcherName);
|
||||
return !(/(Safari|Chromium)/).test(launcherName);
|
||||
});
|
||||
},
|
||||
files(defaults) {
|
||||
defaults.unshift('node_modules/es5-shim/es5-shim.js');
|
||||
defaults.unshift('node_modules/es6-shim/es6-shim.js');
|
||||
|
||||
defaults.splice(
|
||||
defaults.indexOf('node_modules/video.js/dist/video.js'),
|
||||
1,
|
||||
|
@ -26,9 +23,6 @@ module.exports = function(config) {
|
|||
return defaults;
|
||||
},
|
||||
browserstackLaunchers(defaults) {
|
||||
delete defaults.bsSafariMojave;
|
||||
delete defaults.bsSafariElCapitan;
|
||||
|
||||
// do not run on browserstack for coverage
|
||||
if (CI_TEST_TYPE === 'coverage') {
|
||||
defaults = {};
|
||||
|
|
550
node_modules/@videojs/http-streaming/scripts/old-index.js
generated
vendored
550
node_modules/@videojs/http-streaming/scripts/old-index.js
generated
vendored
|
@ -1,550 +0,0 @@
|
|||
/* global window document */
|
||||
/* eslint-disable vars-on-top, no-var, object-shorthand, no-console */
|
||||
(function(window) {
|
||||
var representationsEl = document.getElementById('representations');
|
||||
|
||||
representationsEl.addEventListener('change', function() {
|
||||
var selectedIndex = representationsEl.selectedIndex;
|
||||
|
||||
if (!selectedIndex || selectedIndex < 1 || !window.vhs) {
|
||||
return;
|
||||
}
|
||||
var selectedOption = representationsEl.options[representationsEl.selectedIndex];
|
||||
|
||||
if (!selectedOption) {
|
||||
return;
|
||||
}
|
||||
|
||||
var id = selectedOption.value;
|
||||
|
||||
window.vhs.representations().forEach(function(rep) {
|
||||
rep.playlist.disabled = rep.id !== id;
|
||||
});
|
||||
|
||||
window.mpc.fastQualityChange_();
|
||||
});
|
||||
var isManifestObjectType = function(url) {
|
||||
return (/application\/vnd\.videojs\.vhs\+json/).test(url);
|
||||
};
|
||||
var hlsOptGroup = document.querySelector('[label="hls"]');
|
||||
var dashOptGroup = document.querySelector('[label="dash"]');
|
||||
var drmOptGroup = document.querySelector('[label="drm"]');
|
||||
var liveOptGroup = document.querySelector('[label="live"]');
|
||||
var llliveOptGroup = document.querySelector('[label="low latency live"]');
|
||||
var manifestOptGroup = document.querySelector('[label="json manifest object"]');
|
||||
|
||||
var sourceList;
|
||||
var hlsDataManifest;
|
||||
var dashDataManifest;
|
||||
|
||||
var addSourcesToDom = function() {
|
||||
if (!sourceList || !hlsDataManifest || !dashDataManifest) {
|
||||
return;
|
||||
}
|
||||
|
||||
sourceList.forEach(function(source) {
|
||||
var option = document.createElement('option');
|
||||
|
||||
option.innerText = source.name;
|
||||
option.value = source.uri;
|
||||
|
||||
if (source.keySystems) {
|
||||
option.setAttribute('data-key-systems', JSON.stringify(source.keySystems, null, 2));
|
||||
}
|
||||
|
||||
if (source.mimetype) {
|
||||
option.setAttribute('data-mimetype', source.mimetype);
|
||||
}
|
||||
|
||||
if (source.features.indexOf('low-latency') !== -1) {
|
||||
llliveOptGroup.appendChild(option);
|
||||
} else if (source.features.indexOf('live') !== -1) {
|
||||
liveOptGroup.appendChild(option);
|
||||
} else if (source.keySystems) {
|
||||
drmOptGroup.appendChild(option);
|
||||
} else if (source.mimetype === 'application/x-mpegurl') {
|
||||
hlsOptGroup.appendChild(option);
|
||||
} else if (source.mimetype === 'application/dash+xml') {
|
||||
dashOptGroup.appendChild(option);
|
||||
}
|
||||
});
|
||||
|
||||
var hlsOption = document.createElement('option');
|
||||
var dashOption = document.createElement('option');
|
||||
|
||||
dashOption.innerText = 'Dash Manifest Object Test, does not survive page reload';
|
||||
dashOption.value = 'data:application/vnd.videojs.vhs+json,' + dashDataManifest;
|
||||
hlsOption.innerText = 'HLS Manifest Object Test, does not survive page reload';
|
||||
hlsOption.value = 'data:application/vnd.videojs.vhs+json,' + hlsDataManifest;
|
||||
|
||||
manifestOptGroup.appendChild(hlsOption);
|
||||
manifestOptGroup.appendChild(dashOption);
|
||||
};
|
||||
|
||||
var sourcesXhr = new window.XMLHttpRequest();
|
||||
|
||||
sourcesXhr.addEventListener('load', function() {
|
||||
sourceList = JSON.parse(sourcesXhr.responseText);
|
||||
addSourcesToDom();
|
||||
});
|
||||
sourcesXhr.open('GET', './scripts/sources.json');
|
||||
sourcesXhr.send();
|
||||
|
||||
var hlsManifestXhr = new window.XMLHttpRequest();
|
||||
|
||||
hlsManifestXhr.addEventListener('load', function() {
|
||||
hlsDataManifest = hlsManifestXhr.responseText;
|
||||
addSourcesToDom();
|
||||
|
||||
});
|
||||
hlsManifestXhr.open('GET', './scripts/hls-manifest-object.json');
|
||||
hlsManifestXhr.send();
|
||||
|
||||
var dashManifestXhr = new window.XMLHttpRequest();
|
||||
|
||||
dashManifestXhr.addEventListener('load', function() {
|
||||
dashDataManifest = dashManifestXhr.responseText;
|
||||
addSourcesToDom();
|
||||
});
|
||||
dashManifestXhr.open('GET', './scripts/dash-manifest-object.json');
|
||||
dashManifestXhr.send();
|
||||
|
||||
// all relevant elements
|
||||
var urlButton = document.getElementById('load-url');
|
||||
var sources = document.getElementById('load-source');
|
||||
var stateEls = {};
|
||||
|
||||
var getInputValue = function(el) {
|
||||
if (el.type === 'url' || el.type === 'text' || el.nodeName.toLowerCase() === 'textarea') {
|
||||
if (isManifestObjectType(el.value)) {
|
||||
return '';
|
||||
}
|
||||
return encodeURIComponent(el.value);
|
||||
} else if (el.type === 'select-one') {
|
||||
return el.options[el.selectedIndex].value;
|
||||
} else if (el.type === 'checkbox') {
|
||||
return el.checked;
|
||||
}
|
||||
|
||||
console.warn('unhandled input type ' + el.type);
|
||||
return '';
|
||||
};
|
||||
|
||||
var setInputValue = function(el, value) {
|
||||
if (el.type === 'url' || el.type === 'text' || el.nodeName.toLowerCase() === 'textarea') {
|
||||
el.value = decodeURIComponent(value);
|
||||
} else if (el.type === 'select-one') {
|
||||
for (var i = 0; i < el.options.length; i++) {
|
||||
if (el.options[i].value === value) {
|
||||
el.options[i].selected = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// get the `value` into a Boolean.
|
||||
el.checked = JSON.parse(value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var newEvent = function(name) {
|
||||
var event;
|
||||
|
||||
if (typeof window.Event === 'function') {
|
||||
event = new window.Event(name);
|
||||
} else {
|
||||
event = document.createEvent('Event');
|
||||
event.initEvent(name, true, true);
|
||||
}
|
||||
|
||||
return event;
|
||||
};
|
||||
|
||||
// taken from video.js
|
||||
var getFileExtension = function(path) {
|
||||
var splitPathRe;
|
||||
var pathParts;
|
||||
|
||||
if (typeof path === 'string') {
|
||||
splitPathRe = /^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]*?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/i;
|
||||
pathParts = splitPathRe.exec(path);
|
||||
|
||||
if (pathParts) {
|
||||
return pathParts.pop().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
var saveState = function() {
|
||||
var query = '';
|
||||
|
||||
if (!window.history.replaceState) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(stateEls).forEach(function(elName) {
|
||||
var symbol = query.length ? '&' : '?';
|
||||
|
||||
query += symbol + elName + '=' + getInputValue(stateEls[elName]);
|
||||
});
|
||||
|
||||
window.history.replaceState({}, 'vhs demo', query);
|
||||
};
|
||||
|
||||
window.URLSearchParams = window.URLSearchParams || function(locationSearch) {
|
||||
this.get = function(name) {
|
||||
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(locationSearch);
|
||||
|
||||
return results ? decodeURIComponent(results[1]) : null;
|
||||
};
|
||||
};
|
||||
|
||||
// eslint-disable-next-line
|
||||
var loadState = function() {
|
||||
var params = new window.URLSearchParams(window.location.search);
|
||||
|
||||
return Object.keys(stateEls).reduce(function(acc, elName) {
|
||||
acc[elName] = typeof params.get(elName) !== 'object' ? params.get(elName) : getInputValue(stateEls[elName]);
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
|
||||
// eslint-disable-next-line
|
||||
var reloadScripts = function(urls, cb) {
|
||||
var el = document.getElementById('reload-scripts');
|
||||
|
||||
if (!el) {
|
||||
el = document.createElement('div');
|
||||
el.id = 'reload-scripts';
|
||||
document.body.appendChild(el);
|
||||
}
|
||||
|
||||
while (el.firstChild) {
|
||||
el.removeChild(el.firstChild);
|
||||
}
|
||||
|
||||
var loaded = [];
|
||||
|
||||
var checkDone = function() {
|
||||
if (loaded.length === urls.length) {
|
||||
cb();
|
||||
}
|
||||
};
|
||||
|
||||
urls.forEach(function(url) {
|
||||
var script = document.createElement('script');
|
||||
|
||||
// scripts marked as defer will be loaded asynchronously but will be executed in the order they are in the DOM
|
||||
script.defer = true;
|
||||
// dynamically created scripts are async by default unless otherwise specified
|
||||
// async scripts are loaded asynchronously but also executed as soon as they are loaded
|
||||
// we want to load them in the order they are added therefore we want to turn off async
|
||||
script.async = false;
|
||||
script.src = url;
|
||||
script.onload = function() {
|
||||
loaded.push(url);
|
||||
checkDone();
|
||||
};
|
||||
el.appendChild(script);
|
||||
});
|
||||
};
|
||||
|
||||
var regenerateRepresentations = function() {
|
||||
while (representationsEl.firstChild) {
|
||||
representationsEl.removeChild(representationsEl.firstChild);
|
||||
}
|
||||
|
||||
var selectedIndex;
|
||||
|
||||
window.vhs.representations().forEach(function(rep, i) {
|
||||
var option = document.createElement('option');
|
||||
|
||||
option.value = rep.id;
|
||||
option.innerText = JSON.stringify({
|
||||
id: rep.id,
|
||||
videoCodec: rep.codecs.video,
|
||||
audioCodec: rep.codecs.audio,
|
||||
bandwidth: rep.bandwidth,
|
||||
heigth: rep.heigth,
|
||||
width: rep.width
|
||||
});
|
||||
|
||||
if (window.mpc.media().id === rep.id) {
|
||||
selectedIndex = i;
|
||||
}
|
||||
|
||||
representationsEl.appendChild(option);
|
||||
});
|
||||
|
||||
representationsEl.selectedIndex = selectedIndex;
|
||||
};
|
||||
|
||||
[
|
||||
'debug',
|
||||
'autoplay',
|
||||
'muted',
|
||||
'minified',
|
||||
'sync-workers',
|
||||
'liveui',
|
||||
'llhls',
|
||||
'url',
|
||||
'type',
|
||||
'keysystems',
|
||||
'buffer-water',
|
||||
'exact-manifest-timings',
|
||||
'pixel-diff-selector',
|
||||
'override-native',
|
||||
'preload',
|
||||
'mirror-source'
|
||||
].forEach(function(name) {
|
||||
stateEls[name] = document.getElementById(name);
|
||||
});
|
||||
|
||||
window.startDemo = function(cb) {
|
||||
var state = loadState();
|
||||
|
||||
Object.keys(state).forEach(function(elName) {
|
||||
setInputValue(stateEls[elName], state[elName]);
|
||||
});
|
||||
|
||||
Array.prototype.forEach.call(sources.options, function(s, i) {
|
||||
if (s.value === state.url) {
|
||||
sources.selectedIndex = i;
|
||||
}
|
||||
});
|
||||
|
||||
stateEls.muted.addEventListener('change', function(event) {
|
||||
saveState();
|
||||
window.player.muted(event.target.checked);
|
||||
});
|
||||
|
||||
stateEls.autoplay.addEventListener('change', function(event) {
|
||||
saveState();
|
||||
window.player.autoplay(event.target.checked);
|
||||
});
|
||||
|
||||
// stateEls that reload the player and scripts
|
||||
[
|
||||
'mirror-source',
|
||||
'sync-workers',
|
||||
'preload',
|
||||
'llhls',
|
||||
'buffer-water',
|
||||
'override-native',
|
||||
'liveui',
|
||||
'pixel-diff-selector',
|
||||
'exact-manifest-timings'
|
||||
].forEach(function(name) {
|
||||
stateEls[name].addEventListener('change', function(event) {
|
||||
saveState();
|
||||
|
||||
stateEls.minified.dispatchEvent(newEvent('change'));
|
||||
});
|
||||
});
|
||||
|
||||
stateEls.debug.addEventListener('change', function(event) {
|
||||
saveState();
|
||||
window.videojs.log.level(event.target.checked ? 'debug' : 'info');
|
||||
});
|
||||
|
||||
stateEls.minified.addEventListener('change', function(event) {
|
||||
var urls = [
|
||||
'node_modules/video.js/dist/alt/video.core',
|
||||
'node_modules/videojs-contrib-eme/dist/videojs-contrib-eme',
|
||||
'node_modules/videojs-contrib-quality-levels/dist/videojs-contrib-quality-levels',
|
||||
'node_modules/videojs-http-source-selector/dist/videojs-http-source-selector'
|
||||
].map(function(url) {
|
||||
return url + (event.target.checked ? '.min' : '') + '.js';
|
||||
});
|
||||
|
||||
if (stateEls['sync-workers'].checked) {
|
||||
urls.push('dist/videojs-http-streaming-sync-workers.js');
|
||||
} else {
|
||||
urls.push('dist/videojs-http-streaming' + (event.target.checked ? '.min' : '') + '.js');
|
||||
}
|
||||
|
||||
saveState();
|
||||
|
||||
if (window.player) {
|
||||
window.player.dispose();
|
||||
delete window.player;
|
||||
}
|
||||
if (window.videojs) {
|
||||
delete window.videojs;
|
||||
}
|
||||
|
||||
reloadScripts(urls, function() {
|
||||
var player;
|
||||
var fixture = document.getElementById('player-fixture');
|
||||
var videoEl = document.createElement('video-js');
|
||||
|
||||
videoEl.setAttribute('controls', '');
|
||||
videoEl.setAttribute('preload', stateEls.preload.options[stateEls.preload.selectedIndex].value || 'auto');
|
||||
videoEl.className = 'vjs-default-skin';
|
||||
fixture.appendChild(videoEl);
|
||||
|
||||
var mirrorSource = getInputValue(stateEls['mirror-source']);
|
||||
|
||||
player = window.player = window.videojs(videoEl, {
|
||||
plugins: {
|
||||
httpSourceSelector: {
|
||||
default: 'auto'
|
||||
}
|
||||
},
|
||||
liveui: stateEls.liveui.checked,
|
||||
enableSourceset: mirrorSource,
|
||||
html5: {
|
||||
vhs: {
|
||||
overrideNative: getInputValue(stateEls['override-native']),
|
||||
experimentalBufferBasedABR: getInputValue(stateEls['buffer-water']),
|
||||
experimentalLLHLS: getInputValue(stateEls.llhls),
|
||||
experimentalExactManifestTimings: getInputValue(stateEls['exact-manifest-timings']),
|
||||
experimentalLeastPixelDiffSelector: getInputValue(stateEls['pixel-diff-selector'])
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
player.on('sourceset', function() {
|
||||
var source = player.currentSource();
|
||||
|
||||
if (source.keySystems) {
|
||||
var copy = JSON.parse(JSON.stringify(source.keySystems));
|
||||
|
||||
// have to delete pssh as it will often make keySystems too big
|
||||
// for a uri
|
||||
Object.keys(copy).forEach(function(key) {
|
||||
if (copy[key].hasOwnProperty('pssh')) {
|
||||
delete copy[key].pssh;
|
||||
}
|
||||
});
|
||||
|
||||
stateEls.keysystems.value = JSON.stringify(copy, null, 2);
|
||||
}
|
||||
|
||||
if (source.src) {
|
||||
stateEls.url.value = encodeURI(source.src);
|
||||
}
|
||||
|
||||
if (source.type) {
|
||||
stateEls.type.value = source.type;
|
||||
}
|
||||
|
||||
saveState();
|
||||
});
|
||||
|
||||
player.width(640);
|
||||
player.height(264);
|
||||
|
||||
// configure videojs-contrib-eme
|
||||
player.eme();
|
||||
|
||||
stateEls.debug.dispatchEvent(newEvent('change'));
|
||||
stateEls.muted.dispatchEvent(newEvent('change'));
|
||||
stateEls.autoplay.dispatchEvent(newEvent('change'));
|
||||
|
||||
// run the load url handler for the intial source
|
||||
if (stateEls.url.value) {
|
||||
urlButton.dispatchEvent(newEvent('click'));
|
||||
} else {
|
||||
sources.dispatchEvent(newEvent('change'));
|
||||
}
|
||||
player.on('loadedmetadata', function() {
|
||||
if (player.tech_.vhs) {
|
||||
window.vhs = player.tech_.vhs;
|
||||
window.mpc = player.tech_.vhs.masterPlaylistController_;
|
||||
window.mpc.masterPlaylistLoader_.on('mediachange', regenerateRepresentations);
|
||||
regenerateRepresentations();
|
||||
|
||||
} else {
|
||||
window.vhs = null;
|
||||
window.mpc = null;
|
||||
}
|
||||
});
|
||||
cb(player);
|
||||
});
|
||||
});
|
||||
|
||||
var urlButtonClick = function(event) {
|
||||
var ext;
|
||||
var type = stateEls.type.value;
|
||||
|
||||
// reset type if it's a manifest object's type
|
||||
if (type === 'application/vnd.videojs.vhs+json') {
|
||||
type = '';
|
||||
}
|
||||
|
||||
if (isManifestObjectType(stateEls.url.value)) {
|
||||
type = 'application/vnd.videojs.vhs+json';
|
||||
}
|
||||
|
||||
if (!type.trim()) {
|
||||
ext = getFileExtension(stateEls.url.value);
|
||||
|
||||
if (ext === 'mpd') {
|
||||
type = 'application/dash+xml';
|
||||
} else if (ext === 'm3u8') {
|
||||
type = 'application/x-mpegURL';
|
||||
}
|
||||
}
|
||||
|
||||
saveState();
|
||||
|
||||
var source = {
|
||||
src: stateEls.url.value,
|
||||
type: type
|
||||
};
|
||||
|
||||
if (stateEls.keysystems.value) {
|
||||
source.keySystems = JSON.parse(stateEls.keysystems.value);
|
||||
}
|
||||
|
||||
sources.selectedIndex = -1;
|
||||
|
||||
Array.prototype.forEach.call(sources.options, function(s, i) {
|
||||
if (s.value === stateEls.url.value) {
|
||||
sources.selectedIndex = i;
|
||||
}
|
||||
});
|
||||
|
||||
window.player.src(source);
|
||||
};
|
||||
|
||||
urlButton.addEventListener('click', urlButtonClick);
|
||||
urlButton.addEventListener('tap', urlButtonClick);
|
||||
|
||||
sources.addEventListener('change', function(event) {
|
||||
var selectedOption = sources.options[sources.selectedIndex];
|
||||
|
||||
if (!selectedOption) {
|
||||
return;
|
||||
}
|
||||
var src = selectedOption.value;
|
||||
|
||||
stateEls.url.value = src;
|
||||
stateEls.type.value = selectedOption.getAttribute('data-mimetype');
|
||||
stateEls.keysystems.value = selectedOption.getAttribute('data-key-systems');
|
||||
|
||||
urlButton.dispatchEvent(newEvent('click'));
|
||||
});
|
||||
|
||||
stateEls.url.addEventListener('keyup', function(event) {
|
||||
if (event.key === 'Enter') {
|
||||
urlButton.click();
|
||||
}
|
||||
});
|
||||
stateEls.url.addEventListener('input', function(event) {
|
||||
if (stateEls.type.value.length) {
|
||||
stateEls.type.value = '';
|
||||
}
|
||||
});
|
||||
stateEls.type.addEventListener('keyup', function(event) {
|
||||
if (event.key === 'Enter') {
|
||||
urlButton.click();
|
||||
}
|
||||
});
|
||||
|
||||
// run the change handler for the first time
|
||||
stateEls.minified.dispatchEvent(newEvent('change'));
|
||||
};
|
||||
}(window));
|
283
node_modules/@videojs/http-streaming/src/dash-playlist-loader.js
generated
vendored
283
node_modules/@videojs/http-streaming/src/dash-playlist-loader.js
generated
vendored
|
@ -7,7 +7,7 @@ import {
|
|||
} from 'mpd-parser';
|
||||
import {
|
||||
refreshDelay,
|
||||
updateMaster as updatePlaylist,
|
||||
updateMain as updatePlaylist,
|
||||
isPlaylistUnchanged
|
||||
} from './playlist-loader';
|
||||
import { resolveUrl, resolveManifestRedirect } from './resolve-url';
|
||||
|
@ -16,13 +16,14 @@ import { segmentXhrHeaders } from './xhr';
|
|||
import window from 'global/window';
|
||||
import {
|
||||
forEachMediaGroup,
|
||||
addPropertiesToMaster
|
||||
addPropertiesToMain
|
||||
} from './manifest';
|
||||
import containerRequest from './util/container-request.js';
|
||||
import {toUint8} from '@videojs/vhs-utils/es/byte-helpers';
|
||||
import logger from './util/logger';
|
||||
import {merge} from './util/vjs-compat';
|
||||
|
||||
const { EventTarget, mergeOptions } = videojs;
|
||||
const { EventTarget } = videojs;
|
||||
|
||||
const dashPlaylistUnchanged = function(a, b) {
|
||||
if (!isPlaylistUnchanged(a, b)) {
|
||||
|
@ -86,11 +87,24 @@ const dashPlaylistUnchanged = function(a, b) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Parses the master XML string and updates playlist URI references.
|
||||
* Use the representation IDs from the mpd object to create groupIDs, the NAME is set to mandatory representation
|
||||
* ID in the parser. This allows for continuous playout across periods with the same representation IDs
|
||||
* (continuous periods as defined in DASH-IF 3.2.12). This is assumed in the mpd-parser as well. If we want to support
|
||||
* periods without continuous playback this function may need modification as well as the parser.
|
||||
*/
|
||||
const dashGroupId = (type, group, label, playlist) => {
|
||||
// If the manifest somehow does not have an ID (non-dash compliant), use the label.
|
||||
const playlistId = playlist.attributes.NAME || label;
|
||||
|
||||
return `placeholder-uri-${type}-${group}-${playlistId}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses the main XML string and updates playlist URI references.
|
||||
*
|
||||
* @param {Object} config
|
||||
* Object of arguments
|
||||
* @param {string} config.masterXml
|
||||
* @param {string} config.mainXml
|
||||
* The mpd XML
|
||||
* @param {string} config.srcUrl
|
||||
* The mpd URL
|
||||
|
@ -101,49 +115,65 @@ const dashPlaylistUnchanged = function(a, b) {
|
|||
* @return {Object}
|
||||
* The parsed mpd manifest object
|
||||
*/
|
||||
export const parseMasterXml = ({
|
||||
masterXml,
|
||||
export const parseMainXml = ({
|
||||
mainXml,
|
||||
srcUrl,
|
||||
clientOffset,
|
||||
sidxMapping,
|
||||
previousManifest
|
||||
}) => {
|
||||
const manifest = parseMpd(masterXml, {
|
||||
const manifest = parseMpd(mainXml, {
|
||||
manifestUri: srcUrl,
|
||||
clientOffset,
|
||||
sidxMapping,
|
||||
previousManifest
|
||||
});
|
||||
|
||||
addPropertiesToMaster(manifest, srcUrl);
|
||||
addPropertiesToMain(manifest, srcUrl, dashGroupId);
|
||||
|
||||
return manifest;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a new master manifest that is the result of merging an updated master manifest
|
||||
* Removes any mediaGroup labels that no longer exist in the newMain
|
||||
*
|
||||
* @param {Object} update
|
||||
* The previous mpd object being updated
|
||||
* @param {Object} newMain
|
||||
* The new mpd object
|
||||
*/
|
||||
const removeOldMediaGroupLabels = (update, newMain) => {
|
||||
forEachMediaGroup(update, (properties, type, group, label) => {
|
||||
if (!(label in newMain.mediaGroups[type][group])) {
|
||||
delete update.mediaGroups[type][group][label];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a new main manifest that is the result of merging an updated main manifest
|
||||
* into the original version.
|
||||
*
|
||||
* @param {Object} oldMaster
|
||||
* @param {Object} oldMain
|
||||
* The old parsed mpd object
|
||||
* @param {Object} newMaster
|
||||
* @param {Object} newMain
|
||||
* The updated parsed mpd object
|
||||
* @return {Object}
|
||||
* A new object representing the original master manifest with the updated media
|
||||
* A new object representing the original main manifest with the updated media
|
||||
* playlists merged in
|
||||
*/
|
||||
export const updateMaster = (oldMaster, newMaster, sidxMapping) => {
|
||||
export const updateMain = (oldMain, newMain, sidxMapping) => {
|
||||
let noChanges = true;
|
||||
let update = mergeOptions(oldMaster, {
|
||||
let update = merge(oldMain, {
|
||||
// These are top level properties that can be updated
|
||||
duration: newMaster.duration,
|
||||
minimumUpdatePeriod: newMaster.minimumUpdatePeriod,
|
||||
timelineStarts: newMaster.timelineStarts
|
||||
duration: newMain.duration,
|
||||
minimumUpdatePeriod: newMain.minimumUpdatePeriod,
|
||||
timelineStarts: newMain.timelineStarts
|
||||
});
|
||||
|
||||
// First update the playlists in playlist list
|
||||
for (let i = 0; i < newMaster.playlists.length; i++) {
|
||||
const playlist = newMaster.playlists[i];
|
||||
for (let i = 0; i < newMain.playlists.length; i++) {
|
||||
const playlist = newMain.playlists[i];
|
||||
|
||||
if (playlist.sidx) {
|
||||
const sidxKey = generateSidxKey(playlist.sidx);
|
||||
|
@ -162,21 +192,31 @@ export const updateMaster = (oldMaster, newMaster, sidxMapping) => {
|
|||
}
|
||||
|
||||
// Then update media group playlists
|
||||
forEachMediaGroup(newMaster, (properties, type, group, label) => {
|
||||
forEachMediaGroup(newMain, (properties, type, group, label) => {
|
||||
if (properties.playlists && properties.playlists.length) {
|
||||
const id = properties.playlists[0].id;
|
||||
const playlistUpdate = updatePlaylist(update, properties.playlists[0], dashPlaylistUnchanged);
|
||||
|
||||
if (playlistUpdate) {
|
||||
update = playlistUpdate;
|
||||
|
||||
// add new mediaGroup label if it doesn't exist and assign the new mediaGroup.
|
||||
if (!(label in update.mediaGroups[type][group])) {
|
||||
update.mediaGroups[type][group][label] = properties;
|
||||
}
|
||||
|
||||
// update the playlist reference within media groups
|
||||
update.mediaGroups[type][group][label].playlists[0] = update.playlists[id];
|
||||
|
||||
noChanges = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (newMaster.minimumUpdatePeriod !== oldMaster.minimumUpdatePeriod) {
|
||||
// remove mediaGroup labels and references that no longer exist in the newMain
|
||||
removeOldMediaGroupLabels(update, newMain);
|
||||
|
||||
if (newMain.minimumUpdatePeriod !== oldMain.minimumUpdatePeriod) {
|
||||
noChanges = false;
|
||||
}
|
||||
|
||||
|
@ -235,18 +275,18 @@ export const compareSidxEntry = (playlists, oldSidxMapping) => {
|
|||
*
|
||||
* The method is exported for testing
|
||||
*
|
||||
* @param {Object} master the parsed mpd XML returned via mpd-parser
|
||||
* @param {Object} main the parsed mpd XML returned via mpd-parser
|
||||
* @param {Object} oldSidxMapping the SIDX to compare against
|
||||
*/
|
||||
export const filterChangedSidxMappings = (master, oldSidxMapping) => {
|
||||
const videoSidx = compareSidxEntry(master.playlists, oldSidxMapping);
|
||||
export const filterChangedSidxMappings = (main, oldSidxMapping) => {
|
||||
const videoSidx = compareSidxEntry(main.playlists, oldSidxMapping);
|
||||
let mediaGroupSidx = videoSidx;
|
||||
|
||||
forEachMediaGroup(master, (properties, mediaType, groupKey, labelKey) => {
|
||||
forEachMediaGroup(main, (properties, mediaType, groupKey, labelKey) => {
|
||||
if (properties.playlists && properties.playlists.length) {
|
||||
const playlists = properties.playlists;
|
||||
|
||||
mediaGroupSidx = mergeOptions(
|
||||
mediaGroupSidx = merge(
|
||||
mediaGroupSidx,
|
||||
compareSidxEntry(playlists, oldSidxMapping)
|
||||
);
|
||||
|
@ -260,19 +300,18 @@ export default class DashPlaylistLoader extends EventTarget {
|
|||
// DashPlaylistLoader must accept either a src url or a playlist because subsequent
|
||||
// playlist loader setups from media groups will expect to be able to pass a playlist
|
||||
// (since there aren't external URLs to media playlists with DASH)
|
||||
constructor(srcUrlOrPlaylist, vhs, options = { }, masterPlaylistLoader) {
|
||||
constructor(srcUrlOrPlaylist, vhs, options = { }, mainPlaylistLoader) {
|
||||
super();
|
||||
|
||||
this.masterPlaylistLoader_ = masterPlaylistLoader || this;
|
||||
if (!masterPlaylistLoader) {
|
||||
this.isMaster_ = true;
|
||||
this.mainPlaylistLoader_ = mainPlaylistLoader || this;
|
||||
if (!mainPlaylistLoader) {
|
||||
this.isMain_ = true;
|
||||
}
|
||||
|
||||
const { withCredentials = false, handleManifestRedirects = false } = options;
|
||||
const { withCredentials = false } = options;
|
||||
|
||||
this.vhs_ = vhs;
|
||||
this.withCredentials = withCredentials;
|
||||
this.handleManifestRedirects = handleManifestRedirects;
|
||||
|
||||
if (!srcUrlOrPlaylist) {
|
||||
throw new Error('A non-empty playlist URL or object is required');
|
||||
|
@ -293,12 +332,12 @@ export default class DashPlaylistLoader extends EventTarget {
|
|||
this.logger_ = logger('DashPlaylistLoader');
|
||||
|
||||
// initialize the loader state
|
||||
// The masterPlaylistLoader will be created with a string
|
||||
if (this.isMaster_) {
|
||||
this.masterPlaylistLoader_.srcUrl = srcUrlOrPlaylist;
|
||||
// The mainPlaylistLoader will be created with a string
|
||||
if (this.isMain_) {
|
||||
this.mainPlaylistLoader_.srcUrl = srcUrlOrPlaylist;
|
||||
// TODO: reset sidxMapping between period changes
|
||||
// once multi-period is refactored
|
||||
this.masterPlaylistLoader_.sidxMapping_ = {};
|
||||
this.mainPlaylistLoader_.sidxMapping_ = {};
|
||||
} else {
|
||||
this.childPlaylist_ = srcUrlOrPlaylist;
|
||||
}
|
||||
|
@ -340,21 +379,21 @@ export default class DashPlaylistLoader extends EventTarget {
|
|||
const sidxKey = playlist.sidx && generateSidxKey(playlist.sidx);
|
||||
|
||||
// playlist lacks sidx or sidx segments were added to this playlist already.
|
||||
if (!playlist.sidx || !sidxKey || this.masterPlaylistLoader_.sidxMapping_[sidxKey]) {
|
||||
if (!playlist.sidx || !sidxKey || this.mainPlaylistLoader_.sidxMapping_[sidxKey]) {
|
||||
// keep this function async
|
||||
this.mediaRequest_ = window.setTimeout(() => cb(false), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// resolve the segment URL relative to the playlist
|
||||
const uri = resolveManifestRedirect(this.handleManifestRedirects, playlist.sidx.resolvedUri);
|
||||
const uri = resolveManifestRedirect(playlist.sidx.resolvedUri);
|
||||
|
||||
const fin = (err, request) => {
|
||||
if (this.requestErrored_(err, request, startingState)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sidxMapping = this.masterPlaylistLoader_.sidxMapping_;
|
||||
const sidxMapping = this.mainPlaylistLoader_.sidxMapping_;
|
||||
let sidx;
|
||||
|
||||
try {
|
||||
|
@ -389,7 +428,7 @@ export default class DashPlaylistLoader extends EventTarget {
|
|||
response: '',
|
||||
playlist,
|
||||
internal: true,
|
||||
blacklistDuration: Infinity,
|
||||
playlistExclusionDuration: Infinity,
|
||||
// MEDIA_ERR_NETWORK
|
||||
code: 2
|
||||
}, request);
|
||||
|
@ -426,9 +465,9 @@ export default class DashPlaylistLoader extends EventTarget {
|
|||
this.mediaRequest_ = null;
|
||||
this.minimumUpdatePeriodTimeout_ = null;
|
||||
|
||||
if (this.masterPlaylistLoader_.createMupOnMedia_) {
|
||||
this.off('loadedmetadata', this.masterPlaylistLoader_.createMupOnMedia_);
|
||||
this.masterPlaylistLoader_.createMupOnMedia_ = null;
|
||||
if (this.mainPlaylistLoader_.createMupOnMedia_) {
|
||||
this.off('loadedmetadata', this.mainPlaylistLoader_.createMupOnMedia_);
|
||||
this.mainPlaylistLoader_.createMupOnMedia_ = null;
|
||||
}
|
||||
|
||||
this.off();
|
||||
|
@ -463,10 +502,10 @@ export default class DashPlaylistLoader extends EventTarget {
|
|||
|
||||
// find the playlist object if the target playlist has been specified by URI
|
||||
if (typeof playlist === 'string') {
|
||||
if (!this.masterPlaylistLoader_.master.playlists[playlist]) {
|
||||
if (!this.mainPlaylistLoader_.main.playlists[playlist]) {
|
||||
throw new Error('Unknown playlist URI: ' + playlist);
|
||||
}
|
||||
playlist = this.masterPlaylistLoader_.master.playlists[playlist];
|
||||
playlist = this.mainPlaylistLoader_.main.playlists[playlist];
|
||||
}
|
||||
|
||||
const mediaChange = !this.media_ || playlist.id !== this.media_.id;
|
||||
|
@ -511,7 +550,7 @@ export default class DashPlaylistLoader extends EventTarget {
|
|||
|
||||
// fire loadedmetadata the first time a media playlist is loaded
|
||||
// to resolve setup of media groups
|
||||
if (startingState === 'HAVE_MASTER') {
|
||||
if (startingState === 'HAVE_MAIN_MANIFEST') {
|
||||
this.trigger('loadedmetadata');
|
||||
} else {
|
||||
// trigger media change if the active media has been updated
|
||||
|
@ -520,16 +559,16 @@ export default class DashPlaylistLoader extends EventTarget {
|
|||
}
|
||||
|
||||
pause() {
|
||||
if (this.masterPlaylistLoader_.createMupOnMedia_) {
|
||||
this.off('loadedmetadata', this.masterPlaylistLoader_.createMupOnMedia_);
|
||||
this.masterPlaylistLoader_.createMupOnMedia_ = null;
|
||||
if (this.mainPlaylistLoader_.createMupOnMedia_) {
|
||||
this.off('loadedmetadata', this.mainPlaylistLoader_.createMupOnMedia_);
|
||||
this.mainPlaylistLoader_.createMupOnMedia_ = null;
|
||||
}
|
||||
this.stopRequest();
|
||||
window.clearTimeout(this.mediaUpdateTimeout);
|
||||
this.mediaUpdateTimeout = null;
|
||||
if (this.isMaster_) {
|
||||
window.clearTimeout(this.masterPlaylistLoader_.minimumUpdatePeriodTimeout_);
|
||||
this.masterPlaylistLoader_.minimumUpdatePeriodTimeout_ = null;
|
||||
if (this.isMain_) {
|
||||
window.clearTimeout(this.mainPlaylistLoader_.minimumUpdatePeriodTimeout_);
|
||||
this.mainPlaylistLoader_.minimumUpdatePeriodTimeout_ = null;
|
||||
}
|
||||
if (this.state === 'HAVE_NOTHING') {
|
||||
// If we pause the loader before any data has been retrieved, its as if we never
|
||||
|
@ -559,11 +598,11 @@ export default class DashPlaylistLoader extends EventTarget {
|
|||
}
|
||||
|
||||
if (media && !media.endList) {
|
||||
// Check to see if this is the master loader and the MUP was cleared (this happens
|
||||
// Check to see if this is the main loader and the MUP was cleared (this happens
|
||||
// when the loader was paused). `media` should be set at this point since one is always
|
||||
// set during `start()`.
|
||||
if (this.isMaster_ && !this.minimumUpdatePeriodTimeout_) {
|
||||
// Trigger minimumUpdatePeriod to refresh the master manifest
|
||||
if (this.isMain_ && !this.minimumUpdatePeriodTimeout_) {
|
||||
// Trigger minimumUpdatePeriod to refresh the main manifest
|
||||
this.trigger('minimumUpdatePeriod');
|
||||
// Since there was no prior minimumUpdatePeriodTimeout it should be recreated
|
||||
this.updateMinimumUpdatePeriodTimeout_();
|
||||
|
@ -577,25 +616,25 @@ export default class DashPlaylistLoader extends EventTarget {
|
|||
start() {
|
||||
this.started = true;
|
||||
|
||||
// We don't need to request the master manifest again
|
||||
// We don't need to request the main manifest again
|
||||
// Call this asynchronously to match the xhr request behavior below
|
||||
if (!this.isMaster_) {
|
||||
this.mediaRequest_ = window.setTimeout(() => this.haveMaster_(), 0);
|
||||
if (!this.isMain_) {
|
||||
this.mediaRequest_ = window.setTimeout(() => this.haveMain_(), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
this.requestMaster_((req, masterChanged) => {
|
||||
this.haveMaster_();
|
||||
this.requestMain_((req, mainChanged) => {
|
||||
this.haveMain_();
|
||||
|
||||
if (!this.hasPendingRequest() && !this.media_) {
|
||||
this.media(this.masterPlaylistLoader_.master.playlists[0]);
|
||||
this.media(this.mainPlaylistLoader_.main.playlists[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
requestMaster_(cb) {
|
||||
requestMain_(cb) {
|
||||
this.request = this.vhs_.xhr({
|
||||
uri: this.masterPlaylistLoader_.srcUrl,
|
||||
uri: this.mainPlaylistLoader_.srcUrl,
|
||||
withCredentials: this.withCredentials
|
||||
}, (error, req) => {
|
||||
if (this.requestErrored_(error, req)) {
|
||||
|
@ -605,55 +644,55 @@ export default class DashPlaylistLoader extends EventTarget {
|
|||
return;
|
||||
}
|
||||
|
||||
const masterChanged = req.responseText !== this.masterPlaylistLoader_.masterXml_;
|
||||
const mainChanged = req.responseText !== this.mainPlaylistLoader_.mainXml_;
|
||||
|
||||
this.masterPlaylistLoader_.masterXml_ = req.responseText;
|
||||
this.mainPlaylistLoader_.mainXml_ = req.responseText;
|
||||
|
||||
if (req.responseHeaders && req.responseHeaders.date) {
|
||||
this.masterLoaded_ = Date.parse(req.responseHeaders.date);
|
||||
this.mainLoaded_ = Date.parse(req.responseHeaders.date);
|
||||
} else {
|
||||
this.masterLoaded_ = Date.now();
|
||||
this.mainLoaded_ = Date.now();
|
||||
}
|
||||
|
||||
this.masterPlaylistLoader_.srcUrl = resolveManifestRedirect(this.handleManifestRedirects, this.masterPlaylistLoader_.srcUrl, req);
|
||||
this.mainPlaylistLoader_.srcUrl = resolveManifestRedirect(this.mainPlaylistLoader_.srcUrl, req);
|
||||
|
||||
if (masterChanged) {
|
||||
this.handleMaster_();
|
||||
if (mainChanged) {
|
||||
this.handleMain_();
|
||||
this.syncClientServerClock_(() => {
|
||||
return cb(req, masterChanged);
|
||||
return cb(req, mainChanged);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
return cb(req, masterChanged);
|
||||
return cb(req, mainChanged);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the master xml for UTCTiming node to sync the client clock to the server
|
||||
* Parses the main xml for UTCTiming node to sync the client clock to the server
|
||||
* clock. If the UTCTiming node requires a HEAD or GET request, that request is made.
|
||||
*
|
||||
* @param {Function} done
|
||||
* Function to call when clock sync has completed
|
||||
*/
|
||||
syncClientServerClock_(done) {
|
||||
const utcTiming = parseUTCTiming(this.masterPlaylistLoader_.masterXml_);
|
||||
const utcTiming = parseUTCTiming(this.mainPlaylistLoader_.mainXml_);
|
||||
|
||||
// No UTCTiming element found in the mpd. Use Date header from mpd request as the
|
||||
// server clock
|
||||
if (utcTiming === null) {
|
||||
this.masterPlaylistLoader_.clientOffset_ = this.masterLoaded_ - Date.now();
|
||||
this.mainPlaylistLoader_.clientOffset_ = this.mainLoaded_ - Date.now();
|
||||
return done();
|
||||
}
|
||||
|
||||
if (utcTiming.method === 'DIRECT') {
|
||||
this.masterPlaylistLoader_.clientOffset_ = utcTiming.value - Date.now();
|
||||
this.mainPlaylistLoader_.clientOffset_ = utcTiming.value - Date.now();
|
||||
return done();
|
||||
}
|
||||
|
||||
this.request = this.vhs_.xhr({
|
||||
uri: resolveUrl(this.masterPlaylistLoader_.srcUrl, utcTiming.value),
|
||||
uri: resolveUrl(this.mainPlaylistLoader_.srcUrl, utcTiming.value),
|
||||
method: utcTiming.method,
|
||||
withCredentials: this.withCredentials
|
||||
}, (error, req) => {
|
||||
|
@ -665,7 +704,7 @@ export default class DashPlaylistLoader extends EventTarget {
|
|||
if (error) {
|
||||
// sync request failed, fall back to using date header from mpd
|
||||
// TODO: log warning
|
||||
this.masterPlaylistLoader_.clientOffset_ = this.masterLoaded_ - Date.now();
|
||||
this.mainPlaylistLoader_.clientOffset_ = this.mainLoaded_ - Date.now();
|
||||
return done();
|
||||
}
|
||||
|
||||
|
@ -675,7 +714,7 @@ export default class DashPlaylistLoader extends EventTarget {
|
|||
if (!req.responseHeaders || !req.responseHeaders.date) {
|
||||
// expected date header not preset, fall back to using date header from mpd
|
||||
// TODO: log warning
|
||||
serverTime = this.masterLoaded_;
|
||||
serverTime = this.mainLoaded_;
|
||||
} else {
|
||||
serverTime = Date.parse(req.responseHeaders.date);
|
||||
}
|
||||
|
@ -683,17 +722,17 @@ export default class DashPlaylistLoader extends EventTarget {
|
|||
serverTime = Date.parse(req.responseText);
|
||||
}
|
||||
|
||||
this.masterPlaylistLoader_.clientOffset_ = serverTime - Date.now();
|
||||
this.mainPlaylistLoader_.clientOffset_ = serverTime - Date.now();
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
haveMaster_() {
|
||||
this.state = 'HAVE_MASTER';
|
||||
if (this.isMaster_) {
|
||||
// We have the master playlist at this point, so
|
||||
// trigger this to allow MasterPlaylistController
|
||||
haveMain_() {
|
||||
this.state = 'HAVE_MAIN_MANIFEST';
|
||||
if (this.isMain_) {
|
||||
// We have the main playlist at this point, so
|
||||
// trigger this to allow PlaylistController
|
||||
// to make an initial playlist selection
|
||||
this.trigger('loadedplaylist');
|
||||
} else if (!this.media_) {
|
||||
|
@ -703,42 +742,42 @@ export default class DashPlaylistLoader extends EventTarget {
|
|||
}
|
||||
}
|
||||
|
||||
handleMaster_() {
|
||||
handleMain_() {
|
||||
// clear media request
|
||||
this.mediaRequest_ = null;
|
||||
|
||||
const oldMaster = this.masterPlaylistLoader_.master;
|
||||
const oldMain = this.mainPlaylistLoader_.main;
|
||||
|
||||
let newMaster = parseMasterXml({
|
||||
masterXml: this.masterPlaylistLoader_.masterXml_,
|
||||
srcUrl: this.masterPlaylistLoader_.srcUrl,
|
||||
clientOffset: this.masterPlaylistLoader_.clientOffset_,
|
||||
sidxMapping: this.masterPlaylistLoader_.sidxMapping_,
|
||||
previousManifest: oldMaster
|
||||
let newMain = parseMainXml({
|
||||
mainXml: this.mainPlaylistLoader_.mainXml_,
|
||||
srcUrl: this.mainPlaylistLoader_.srcUrl,
|
||||
clientOffset: this.mainPlaylistLoader_.clientOffset_,
|
||||
sidxMapping: this.mainPlaylistLoader_.sidxMapping_,
|
||||
previousManifest: oldMain
|
||||
});
|
||||
|
||||
// if we have an old master to compare the new master against
|
||||
if (oldMaster) {
|
||||
newMaster = updateMaster(oldMaster, newMaster, this.masterPlaylistLoader_.sidxMapping_);
|
||||
// if we have an old main to compare the new main against
|
||||
if (oldMain) {
|
||||
newMain = updateMain(oldMain, newMain, this.mainPlaylistLoader_.sidxMapping_);
|
||||
}
|
||||
|
||||
// only update master if we have a new master
|
||||
this.masterPlaylistLoader_.master = newMaster ? newMaster : oldMaster;
|
||||
const location = this.masterPlaylistLoader_.master.locations && this.masterPlaylistLoader_.master.locations[0];
|
||||
// only update main if we have a new main
|
||||
this.mainPlaylistLoader_.main = newMain ? newMain : oldMain;
|
||||
const location = this.mainPlaylistLoader_.main.locations && this.mainPlaylistLoader_.main.locations[0];
|
||||
|
||||
if (location && location !== this.masterPlaylistLoader_.srcUrl) {
|
||||
this.masterPlaylistLoader_.srcUrl = location;
|
||||
if (location && location !== this.mainPlaylistLoader_.srcUrl) {
|
||||
this.mainPlaylistLoader_.srcUrl = location;
|
||||
}
|
||||
|
||||
if (!oldMaster || (newMaster && newMaster.minimumUpdatePeriod !== oldMaster.minimumUpdatePeriod)) {
|
||||
if (!oldMain || (newMain && newMain.minimumUpdatePeriod !== oldMain.minimumUpdatePeriod)) {
|
||||
this.updateMinimumUpdatePeriodTimeout_();
|
||||
}
|
||||
|
||||
return Boolean(newMaster);
|
||||
return Boolean(newMain);
|
||||
}
|
||||
|
||||
updateMinimumUpdatePeriodTimeout_() {
|
||||
const mpl = this.masterPlaylistLoader_;
|
||||
const mpl = this.mainPlaylistLoader_;
|
||||
|
||||
// cancel any pending creation of mup on media
|
||||
// a new one will be added if needed.
|
||||
|
@ -753,7 +792,7 @@ export default class DashPlaylistLoader extends EventTarget {
|
|||
mpl.minimumUpdatePeriodTimeout_ = null;
|
||||
}
|
||||
|
||||
let mup = mpl.master && mpl.master.minimumUpdatePeriod;
|
||||
let mup = mpl.main && mpl.main.minimumUpdatePeriod;
|
||||
|
||||
// If the minimumUpdatePeriod has a value of 0, that indicates that the current
|
||||
// MPD has no future validity, so a new one will need to be acquired when new
|
||||
|
@ -782,7 +821,7 @@ export default class DashPlaylistLoader extends EventTarget {
|
|||
}
|
||||
|
||||
createMUPTimeout_(mup) {
|
||||
const mpl = this.masterPlaylistLoader_;
|
||||
const mpl = this.mainPlaylistLoader_;
|
||||
|
||||
mpl.minimumUpdatePeriodTimeout_ = window.setTimeout(() => {
|
||||
mpl.minimumUpdatePeriodTimeout_ = null;
|
||||
|
@ -792,22 +831,22 @@ export default class DashPlaylistLoader extends EventTarget {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sends request to refresh the master xml and updates the parsed master manifest
|
||||
* Sends request to refresh the main xml and updates the parsed main manifest
|
||||
*/
|
||||
refreshXml_() {
|
||||
this.requestMaster_((req, masterChanged) => {
|
||||
if (!masterChanged) {
|
||||
this.requestMain_((req, mainChanged) => {
|
||||
if (!mainChanged) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.media_) {
|
||||
this.media_ = this.masterPlaylistLoader_.master.playlists[this.media_.id];
|
||||
this.media_ = this.mainPlaylistLoader_.main.playlists[this.media_.id];
|
||||
}
|
||||
|
||||
// This will filter out updated sidx info from the mapping
|
||||
this.masterPlaylistLoader_.sidxMapping_ = filterChangedSidxMappings(
|
||||
this.masterPlaylistLoader_.master,
|
||||
this.masterPlaylistLoader_.sidxMapping_
|
||||
this.mainPlaylistLoader_.sidxMapping_ = filterChangedSidxMappings(
|
||||
this.mainPlaylistLoader_.main,
|
||||
this.mainPlaylistLoader_.sidxMapping_
|
||||
);
|
||||
|
||||
this.addSidxSegments_(this.media(), this.state, (sidxChanged) => {
|
||||
|
@ -818,25 +857,25 @@ export default class DashPlaylistLoader extends EventTarget {
|
|||
}
|
||||
|
||||
/**
|
||||
* Refreshes the media playlist by re-parsing the master xml and updating playlist
|
||||
* Refreshes the media playlist by re-parsing the main xml and updating playlist
|
||||
* references. If this is an alternate loader, the updated parsed manifest is retrieved
|
||||
* from the master loader.
|
||||
* from the main loader.
|
||||
*/
|
||||
refreshMedia_(mediaID) {
|
||||
if (!mediaID) {
|
||||
throw new Error('refreshMedia_ must take a media id');
|
||||
}
|
||||
|
||||
// for master we have to reparse the master xml
|
||||
// for main we have to reparse the main xml
|
||||
// to re-create segments based on current timing values
|
||||
// which may change media. We only skip updating master
|
||||
// which may change media. We only skip updating the main manifest
|
||||
// if this is the first time this.media_ is being set.
|
||||
// as master was just parsed in that case.
|
||||
if (this.media_ && this.isMaster_) {
|
||||
this.handleMaster_();
|
||||
// as main was just parsed in that case.
|
||||
if (this.media_ && this.isMain_) {
|
||||
this.handleMain_();
|
||||
}
|
||||
|
||||
const playlists = this.masterPlaylistLoader_.master.playlists;
|
||||
const playlists = this.mainPlaylistLoader_.main.playlists;
|
||||
const mediaChanged = !this.media_ || this.media_ !== playlists[mediaID];
|
||||
|
||||
if (mediaChanged) {
|
||||
|
|
116
node_modules/@videojs/http-streaming/src/manifest.js
generated
vendored
116
node_modules/@videojs/http-streaming/src/manifest.js
generated
vendored
|
@ -10,6 +10,11 @@ export const createPlaylistID = (index, uri) => {
|
|||
return `${index}-${uri}`;
|
||||
};
|
||||
|
||||
// default function for creating a group id
|
||||
const groupID = (type, group, label) => {
|
||||
return `placeholder-uri-${type}-${group}-${label}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a given m3u8 playlist
|
||||
*
|
||||
|
@ -23,7 +28,7 @@ export const createPlaylistID = (index, uri) => {
|
|||
* An array of custom tag parsers for the m3u8-parser instance
|
||||
* @param {Object[]} [customTagMappers]
|
||||
* An array of custom tag mappers for the m3u8-parser instance
|
||||
* @param {boolean} [experimentalLLHLS=false]
|
||||
* @param {boolean} [llhls]
|
||||
* Whether to keep ll-hls features in the manifest after parsing.
|
||||
* @return {Object}
|
||||
* The manifest object
|
||||
|
@ -34,7 +39,7 @@ export const parseManifest = ({
|
|||
manifestString,
|
||||
customTagParsers = [],
|
||||
customTagMappers = [],
|
||||
experimentalLLHLS
|
||||
llhls
|
||||
}) => {
|
||||
const parser = new M3u8Parser();
|
||||
|
||||
|
@ -55,7 +60,7 @@ export const parseManifest = ({
|
|||
|
||||
// remove llhls features from the parsed manifest
|
||||
// if we don't want llhls support.
|
||||
if (!experimentalLLHLS) {
|
||||
if (!llhls) {
|
||||
[
|
||||
'preloadSegment',
|
||||
'skip',
|
||||
|
@ -109,25 +114,25 @@ export const parseManifest = ({
|
|||
};
|
||||
|
||||
/**
|
||||
* Loops through all supported media groups in master and calls the provided
|
||||
* Loops through all supported media groups in main and calls the provided
|
||||
* callback for each group
|
||||
*
|
||||
* @param {Object} master
|
||||
* The parsed master manifest object
|
||||
* @param {Object} main
|
||||
* The parsed main manifest object
|
||||
* @param {Function} callback
|
||||
* Callback to call for each media group
|
||||
*/
|
||||
export const forEachMediaGroup = (master, callback) => {
|
||||
if (!master.mediaGroups) {
|
||||
export const forEachMediaGroup = (main, callback) => {
|
||||
if (!main.mediaGroups) {
|
||||
return;
|
||||
}
|
||||
['AUDIO', 'SUBTITLES'].forEach((mediaType) => {
|
||||
if (!master.mediaGroups[mediaType]) {
|
||||
if (!main.mediaGroups[mediaType]) {
|
||||
return;
|
||||
}
|
||||
for (const groupKey in master.mediaGroups[mediaType]) {
|
||||
for (const labelKey in master.mediaGroups[mediaType][groupKey]) {
|
||||
const mediaProperties = master.mediaGroups[mediaType][groupKey][labelKey];
|
||||
for (const groupKey in main.mediaGroups[mediaType]) {
|
||||
for (const labelKey in main.mediaGroups[mediaType][groupKey]) {
|
||||
const mediaProperties = main.mediaGroups[mediaType][groupKey][labelKey];
|
||||
|
||||
callback(mediaProperties, mediaType, groupKey, labelKey);
|
||||
}
|
||||
|
@ -144,7 +149,7 @@ export const forEachMediaGroup = (master, callback) => {
|
|||
* @param {Object} config.playlist
|
||||
* The media playlist
|
||||
* @param {string} [config.uri]
|
||||
* The uri to the media playlist (if media playlist is not from within a master
|
||||
* The uri to the media playlist (if media playlist is not from within a main
|
||||
* playlist)
|
||||
* @param {string} id
|
||||
* ID to use for the playlist
|
||||
|
@ -160,7 +165,7 @@ export const setupMediaPlaylist = ({ playlist, uri, id }) => {
|
|||
playlist.uri = uri;
|
||||
}
|
||||
|
||||
// For HLS master playlists, even though certain attributes MUST be defined, the
|
||||
// For HLS main playlists, even though certain attributes MUST be defined, the
|
||||
// stream may still be played without them.
|
||||
// For HLS media playlists, m3u8-parser does not attach an attributes object to the
|
||||
// manifest.
|
||||
|
@ -171,27 +176,27 @@ export const setupMediaPlaylist = ({ playlist, uri, id }) => {
|
|||
};
|
||||
|
||||
/**
|
||||
* Adds ID, resolvedUri, and attributes properties to each playlist of the master, where
|
||||
* Adds ID, resolvedUri, and attributes properties to each playlist of the main, where
|
||||
* necessary. In addition, creates playlist IDs for each playlist and adds playlist ID to
|
||||
* playlist references to the playlists array.
|
||||
*
|
||||
* @param {Object} master
|
||||
* The master playlist
|
||||
* @param {Object} main
|
||||
* The main playlist
|
||||
*/
|
||||
export const setupMediaPlaylists = (master) => {
|
||||
let i = master.playlists.length;
|
||||
export const setupMediaPlaylists = (main) => {
|
||||
let i = main.playlists.length;
|
||||
|
||||
while (i--) {
|
||||
const playlist = master.playlists[i];
|
||||
const playlist = main.playlists[i];
|
||||
|
||||
setupMediaPlaylist({
|
||||
playlist,
|
||||
id: createPlaylistID(i, playlist.uri)
|
||||
});
|
||||
playlist.resolvedUri = resolveUrl(master.uri, playlist.uri);
|
||||
master.playlists[playlist.id] = playlist;
|
||||
playlist.resolvedUri = resolveUrl(main.uri, playlist.uri);
|
||||
main.playlists[playlist.id] = playlist;
|
||||
// URI reference added for backwards compatibility
|
||||
master.playlists[playlist.uri] = playlist;
|
||||
main.playlists[playlist.uri] = playlist;
|
||||
|
||||
// Although the spec states an #EXT-X-STREAM-INF tag MUST have a BANDWIDTH attribute,
|
||||
// the stream can be played without it. Although an attributes property may have been
|
||||
|
@ -206,19 +211,19 @@ export const setupMediaPlaylists = (master) => {
|
|||
/**
|
||||
* Adds resolvedUri properties to each media group.
|
||||
*
|
||||
* @param {Object} master
|
||||
* The master playlist
|
||||
* @param {Object} main
|
||||
* The main playlist
|
||||
*/
|
||||
export const resolveMediaGroupUris = (master) => {
|
||||
forEachMediaGroup(master, (properties) => {
|
||||
export const resolveMediaGroupUris = (main) => {
|
||||
forEachMediaGroup(main, (properties) => {
|
||||
if (properties.uri) {
|
||||
properties.resolvedUri = resolveUrl(master.uri, properties.uri);
|
||||
properties.resolvedUri = resolveUrl(main.uri, properties.uri);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a master playlist wrapper to insert a sole media playlist into.
|
||||
* Creates a main playlist wrapper to insert a sole media playlist into.
|
||||
*
|
||||
* @param {Object} media
|
||||
* Media playlist
|
||||
|
@ -226,11 +231,11 @@ export const resolveMediaGroupUris = (master) => {
|
|||
* The media URI
|
||||
*
|
||||
* @return {Object}
|
||||
* Master playlist
|
||||
* main playlist
|
||||
*/
|
||||
export const masterForMedia = (media, uri) => {
|
||||
export const mainForMedia = (media, uri) => {
|
||||
const id = createPlaylistID(0, uri);
|
||||
const master = {
|
||||
const main = {
|
||||
mediaGroups: {
|
||||
'AUDIO': {},
|
||||
'VIDEO': {},
|
||||
|
@ -250,48 +255,48 @@ export const masterForMedia = (media, uri) => {
|
|||
};
|
||||
|
||||
// set up ID reference
|
||||
master.playlists[id] = master.playlists[0];
|
||||
main.playlists[id] = main.playlists[0];
|
||||
// URI reference added for backwards compatibility
|
||||
master.playlists[uri] = master.playlists[0];
|
||||
main.playlists[uri] = main.playlists[0];
|
||||
|
||||
return master;
|
||||
return main;
|
||||
};
|
||||
|
||||
/**
|
||||
* Does an in-place update of the master manifest to add updated playlist URI references
|
||||
* Does an in-place update of the main manifest to add updated playlist URI references
|
||||
* as well as other properties needed by VHS that aren't included by the parser.
|
||||
*
|
||||
* @param {Object} master
|
||||
* Master manifest object
|
||||
* @param {Object} main
|
||||
* main manifest object
|
||||
* @param {string} uri
|
||||
* The source URI
|
||||
* @param {function} createGroupID
|
||||
* A function to determine how to create the groupID for mediaGroups
|
||||
*/
|
||||
export const addPropertiesToMaster = (master, uri) => {
|
||||
master.uri = uri;
|
||||
export const addPropertiesToMain = (main, uri, createGroupID = groupID) => {
|
||||
main.uri = uri;
|
||||
|
||||
for (let i = 0; i < master.playlists.length; i++) {
|
||||
if (!master.playlists[i].uri) {
|
||||
for (let i = 0; i < main.playlists.length; i++) {
|
||||
if (!main.playlists[i].uri) {
|
||||
// Set up phony URIs for the playlists since playlists are referenced by their URIs
|
||||
// throughout VHS, but some formats (e.g., DASH) don't have external URIs
|
||||
// TODO: consider adding dummy URIs in mpd-parser
|
||||
const phonyUri = `placeholder-uri-${i}`;
|
||||
|
||||
master.playlists[i].uri = phonyUri;
|
||||
main.playlists[i].uri = phonyUri;
|
||||
}
|
||||
}
|
||||
const audioOnlyMaster = isAudioOnly(master);
|
||||
|
||||
forEachMediaGroup(master, (properties, mediaType, groupKey, labelKey) => {
|
||||
const groupId = `placeholder-uri-${mediaType}-${groupKey}-${labelKey}`;
|
||||
const audioOnlyMain = isAudioOnly(main);
|
||||
|
||||
forEachMediaGroup(main, (properties, mediaType, groupKey, labelKey) => {
|
||||
// add a playlist array under properties
|
||||
if (!properties.playlists || !properties.playlists.length) {
|
||||
// If the manifest is audio only and this media group does not have a uri, check
|
||||
// if the media group is located in the main list of playlists. If it is, don't add
|
||||
// placeholder properties as it shouldn't be considered an alternate audio track.
|
||||
if (audioOnlyMaster && mediaType === 'AUDIO' && !properties.uri) {
|
||||
for (let i = 0; i < master.playlists.length; i++) {
|
||||
const p = master.playlists[i];
|
||||
if (audioOnlyMain && mediaType === 'AUDIO' && !properties.uri) {
|
||||
for (let i = 0; i < main.playlists.length; i++) {
|
||||
const p = main.playlists[i];
|
||||
|
||||
if (p.attributes && p.attributes.AUDIO && p.attributes.AUDIO === groupKey) {
|
||||
return;
|
||||
|
@ -303,10 +308,11 @@ export const addPropertiesToMaster = (master, uri) => {
|
|||
}
|
||||
|
||||
properties.playlists.forEach(function(p, i) {
|
||||
const groupId = createGroupID(mediaType, groupKey, labelKey, p);
|
||||
const id = createPlaylistID(i, groupId);
|
||||
|
||||
if (p.uri) {
|
||||
p.resolvedUri = p.resolvedUri || resolveUrl(master.uri, p.uri);
|
||||
p.resolvedUri = p.resolvedUri || resolveUrl(main.uri, p.uri);
|
||||
} else {
|
||||
// DEPRECATED, this has been added to prevent a breaking change.
|
||||
// previously we only ever had a single media group playlist, so
|
||||
|
@ -326,12 +332,12 @@ export const addPropertiesToMaster = (master, uri) => {
|
|||
p.attributes = p.attributes || {};
|
||||
|
||||
// setup ID and URI references (URI for backwards compatibility)
|
||||
master.playlists[p.id] = p;
|
||||
master.playlists[p.uri] = p;
|
||||
main.playlists[p.id] = p;
|
||||
main.playlists[p.uri] = p;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
setupMediaPlaylists(master);
|
||||
resolveMediaGroupUris(master);
|
||||
setupMediaPlaylists(main);
|
||||
resolveMediaGroupUris(main);
|
||||
};
|
||||
|
|
2035
node_modules/@videojs/http-streaming/src/master-playlist-controller.js
generated
vendored
2035
node_modules/@videojs/http-streaming/src/master-playlist-controller.js
generated
vendored
File diff suppressed because it is too large
Load diff
92
node_modules/@videojs/http-streaming/src/media-groups.js
generated
vendored
92
node_modules/@videojs/http-streaming/src/media-groups.js
generated
vendored
|
@ -4,6 +4,7 @@ import DashPlaylistLoader from './dash-playlist-loader';
|
|||
import noop from './util/noop';
|
||||
import {isAudioOnly, playlistMatch} from './playlist.js';
|
||||
import logger from './util/logger';
|
||||
import {merge} from './util/vjs-compat';
|
||||
|
||||
/**
|
||||
* Convert the properties of an HLS track into an audioTrackKind.
|
||||
|
@ -94,7 +95,7 @@ export const onGroupChanged = (type, settings) => () => {
|
|||
|
||||
stopLoaders(segmentLoader, mediaType);
|
||||
|
||||
if (!activeGroup || activeGroup.isMasterPlaylist) {
|
||||
if (!activeGroup || activeGroup.isMainPlaylist) {
|
||||
// there is no group active or active group is a main playlist and won't change
|
||||
return;
|
||||
}
|
||||
|
@ -146,7 +147,7 @@ export const onGroupChanging = (type, settings) => () => {
|
|||
*/
|
||||
export const onTrackChanged = (type, settings) => () => {
|
||||
const {
|
||||
masterPlaylistLoader,
|
||||
mainPlaylistLoader,
|
||||
segmentLoaders: {
|
||||
[type]: segmentLoader,
|
||||
main: mainSegmentLoader
|
||||
|
@ -173,24 +174,24 @@ export const onTrackChanged = (type, settings) => () => {
|
|||
return;
|
||||
}
|
||||
|
||||
if (activeGroup.isMasterPlaylist) {
|
||||
if (activeGroup.isMainPlaylist) {
|
||||
// track did not change, do nothing
|
||||
if (!activeTrack || !lastTrack || activeTrack.id === lastTrack.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mpc = settings.vhs.masterPlaylistController_;
|
||||
const newPlaylist = mpc.selectPlaylist();
|
||||
const pc = settings.vhs.playlistController_;
|
||||
const newPlaylist = pc.selectPlaylist();
|
||||
|
||||
// media will not change do nothing
|
||||
if (mpc.media() === newPlaylist) {
|
||||
if (pc.media() === newPlaylist) {
|
||||
return;
|
||||
}
|
||||
|
||||
mediaType.logger_(`track change. Switching master audio from ${lastTrack.id} to ${activeTrack.id}`);
|
||||
masterPlaylistLoader.pause();
|
||||
mediaType.logger_(`track change. Switching main audio from ${lastTrack.id} to ${activeTrack.id}`);
|
||||
mainPlaylistLoader.pause();
|
||||
mainSegmentLoader.resetEverything();
|
||||
mpc.fastQualityChange_(newPlaylist);
|
||||
pc.fastQualityChange_(newPlaylist);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -243,7 +244,7 @@ export const onError = {
|
|||
* @param {Object} settings
|
||||
* Object containing required information for media groups
|
||||
* @return {Function}
|
||||
* Error handler. Logs warning (or error if the playlist is blacklisted) to
|
||||
* Error handler. Logs warning (or error if the playlist is excluded) to
|
||||
* console and switches back to default audio track.
|
||||
* @function onError.AUDIO
|
||||
*/
|
||||
|
@ -251,7 +252,7 @@ export const onError = {
|
|||
const {
|
||||
segmentLoaders: { [type]: segmentLoader},
|
||||
mediaTypes: { [type]: mediaType },
|
||||
blacklistCurrentPlaylist
|
||||
excludePlaylist
|
||||
} = settings;
|
||||
|
||||
stopLoaders(segmentLoader, mediaType);
|
||||
|
@ -263,10 +264,10 @@ export const onError = {
|
|||
const defaultTrack = mediaType.tracks[id];
|
||||
|
||||
if (activeTrack === defaultTrack) {
|
||||
// Default track encountered an error. All we can do now is blacklist the current
|
||||
// Default track encountered an error. All we can do now is exclude the current
|
||||
// rendition and hope another will switch audio groups
|
||||
blacklistCurrentPlaylist({
|
||||
message: 'Problem encountered loading the default audio track.'
|
||||
excludePlaylist({
|
||||
error: { message: 'Problem encountered loading the default audio track.' }
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -421,7 +422,7 @@ export const initialize = {
|
|||
sourceType,
|
||||
segmentLoaders: { [type]: segmentLoader },
|
||||
requestOptions,
|
||||
master: {mediaGroups},
|
||||
main: {mediaGroups},
|
||||
mediaTypes: {
|
||||
[type]: {
|
||||
groups,
|
||||
|
@ -429,17 +430,17 @@ export const initialize = {
|
|||
logger_
|
||||
}
|
||||
},
|
||||
masterPlaylistLoader
|
||||
mainPlaylistLoader
|
||||
} = settings;
|
||||
|
||||
const audioOnlyMaster = isAudioOnly(masterPlaylistLoader.master);
|
||||
const audioOnlyMain = isAudioOnly(mainPlaylistLoader.main);
|
||||
|
||||
// force a default if we have none
|
||||
if (!mediaGroups[type] ||
|
||||
Object.keys(mediaGroups[type]).length === 0) {
|
||||
mediaGroups[type] = { main: { default: { default: true } } };
|
||||
if (audioOnlyMaster) {
|
||||
mediaGroups[type].main.default.playlists = masterPlaylistLoader.master.playlists;
|
||||
if (audioOnlyMain) {
|
||||
mediaGroups[type].main.default.playlists = mainPlaylistLoader.main.playlists;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -452,9 +453,9 @@ export const initialize = {
|
|||
|
||||
let playlistLoader;
|
||||
|
||||
if (audioOnlyMaster) {
|
||||
logger_(`AUDIO group '${groupId}' label '${variantLabel}' is a master playlist`);
|
||||
properties.isMasterPlaylist = true;
|
||||
if (audioOnlyMain) {
|
||||
logger_(`AUDIO group '${groupId}' label '${variantLabel}' is a main playlist`);
|
||||
properties.isMainPlaylist = true;
|
||||
playlistLoader = null;
|
||||
|
||||
// if vhs-json was provided as the source, and the media playlist was resolved,
|
||||
|
@ -478,7 +479,7 @@ export const initialize = {
|
|||
properties.playlists[0],
|
||||
vhs,
|
||||
requestOptions,
|
||||
masterPlaylistLoader
|
||||
mainPlaylistLoader
|
||||
);
|
||||
} else {
|
||||
// no resolvedUri means the audio is muxed with the video when using this
|
||||
|
@ -486,7 +487,7 @@ export const initialize = {
|
|||
playlistLoader = null;
|
||||
}
|
||||
|
||||
properties = videojs.mergeOptions(
|
||||
properties = merge(
|
||||
{ id: variantLabel, playlistLoader },
|
||||
properties
|
||||
);
|
||||
|
@ -529,14 +530,14 @@ export const initialize = {
|
|||
sourceType,
|
||||
segmentLoaders: { [type]: segmentLoader },
|
||||
requestOptions,
|
||||
master: { mediaGroups },
|
||||
main: { mediaGroups },
|
||||
mediaTypes: {
|
||||
[type]: {
|
||||
groups,
|
||||
tracks
|
||||
}
|
||||
},
|
||||
masterPlaylistLoader
|
||||
mainPlaylistLoader
|
||||
} = settings;
|
||||
|
||||
for (const groupId in mediaGroups[type]) {
|
||||
|
@ -574,7 +575,7 @@ export const initialize = {
|
|||
properties.playlists[0],
|
||||
vhs,
|
||||
requestOptions,
|
||||
masterPlaylistLoader
|
||||
mainPlaylistLoader
|
||||
);
|
||||
} else if (sourceType === 'vhs-json') {
|
||||
playlistLoader = new PlaylistLoader(
|
||||
|
@ -586,7 +587,7 @@ export const initialize = {
|
|||
);
|
||||
}
|
||||
|
||||
properties = videojs.mergeOptions({
|
||||
properties = merge({
|
||||
id: variantLabel,
|
||||
playlistLoader
|
||||
}, properties);
|
||||
|
@ -624,7 +625,7 @@ export const initialize = {
|
|||
'CLOSED-CAPTIONS': (type, settings) => {
|
||||
const {
|
||||
tech,
|
||||
master: { mediaGroups },
|
||||
main: { mediaGroups },
|
||||
mediaTypes: {
|
||||
[type]: {
|
||||
groups,
|
||||
|
@ -656,7 +657,7 @@ export const initialize = {
|
|||
};
|
||||
|
||||
if (captionServices[newProps.instreamId]) {
|
||||
newProps = videojs.mergeOptions(newProps, captionServices[newProps.instreamId]);
|
||||
newProps = merge(newProps, captionServices[newProps.instreamId]);
|
||||
}
|
||||
|
||||
if (newProps.default === undefined) {
|
||||
|
@ -665,7 +666,7 @@ export const initialize = {
|
|||
|
||||
// No PlaylistLoader is required for Closed-Captions because the captions are
|
||||
// embedded within the video stream
|
||||
groups[groupId].push(videojs.mergeOptions({ id: variantLabel }, properties));
|
||||
groups[groupId].push(merge({ id: variantLabel }, properties));
|
||||
|
||||
if (typeof tracks[variantLabel] === 'undefined') {
|
||||
const track = tech.addRemoteTextTrack({
|
||||
|
@ -713,11 +714,11 @@ const groupMatch = (list, media) => {
|
|||
*/
|
||||
export const activeGroup = (type, settings) => (track) => {
|
||||
const {
|
||||
masterPlaylistLoader,
|
||||
mainPlaylistLoader,
|
||||
mediaTypes: { [type]: { groups } }
|
||||
} = settings;
|
||||
|
||||
const media = masterPlaylistLoader.media();
|
||||
const media = mainPlaylistLoader.media();
|
||||
|
||||
if (!media) {
|
||||
return null;
|
||||
|
@ -733,10 +734,10 @@ export const activeGroup = (type, settings) => (track) => {
|
|||
const groupKeys = Object.keys(groups);
|
||||
|
||||
if (!variants) {
|
||||
// find the masterPlaylistLoader media
|
||||
// find the mainPlaylistLoader media
|
||||
// that is in a media group if we are dealing
|
||||
// with audio only
|
||||
if (type === 'AUDIO' && groupKeys.length > 1 && isAudioOnly(settings.master)) {
|
||||
if (type === 'AUDIO' && groupKeys.length > 1 && isAudioOnly(settings.main)) {
|
||||
for (let i = 0; i < groupKeys.length; i++) {
|
||||
const groupPropertyList = groups[groupKeys[i]];
|
||||
|
||||
|
@ -828,7 +829,7 @@ export const getActiveGroup = (type, {mediaTypes}) => () => {
|
|||
|
||||
/**
|
||||
* Setup PlaylistLoaders and Tracks for media groups (Audio, Subtitles,
|
||||
* Closed-Captions) specified in the master manifest.
|
||||
* Closed-Captions) specified in the main manifest.
|
||||
*
|
||||
* @param {Object} settings
|
||||
* Object containing required information for setting up the media groups
|
||||
|
@ -836,16 +837,16 @@ export const getActiveGroup = (type, {mediaTypes}) => () => {
|
|||
* The tech of the player
|
||||
* @param {Object} settings.requestOptions
|
||||
* XHR request options used by the segment loaders
|
||||
* @param {PlaylistLoader} settings.masterPlaylistLoader
|
||||
* PlaylistLoader for the master source
|
||||
* @param {PlaylistLoader} settings.mainPlaylistLoader
|
||||
* PlaylistLoader for the main source
|
||||
* @param {VhsHandler} settings.vhs
|
||||
* VHS SourceHandler
|
||||
* @param {Object} settings.master
|
||||
* The parsed master manifest
|
||||
* @param {Object} settings.main
|
||||
* The parsed main manifest
|
||||
* @param {Object} settings.mediaTypes
|
||||
* Object to store the loaders, tracks, and utility methods for each media type
|
||||
* @param {Function} settings.blacklistCurrentPlaylist
|
||||
* Blacklists the current rendition and forces a rendition switch.
|
||||
* @param {Function} settings.excludePlaylist
|
||||
* Excludes the current rendition and forces a rendition switch.
|
||||
* @function setupMediaGroups
|
||||
*/
|
||||
export const setupMediaGroups = (settings) => {
|
||||
|
@ -855,7 +856,7 @@ export const setupMediaGroups = (settings) => {
|
|||
|
||||
const {
|
||||
mediaTypes,
|
||||
masterPlaylistLoader,
|
||||
mainPlaylistLoader,
|
||||
tech,
|
||||
vhs,
|
||||
segmentLoaders: {
|
||||
|
@ -900,11 +901,11 @@ export const setupMediaGroups = (settings) => {
|
|||
}
|
||||
}
|
||||
|
||||
masterPlaylistLoader.on('mediachange', () => {
|
||||
mainPlaylistLoader.on('mediachange', () => {
|
||||
['AUDIO', 'SUBTITLES'].forEach(type => mediaTypes[type].onGroupChanged());
|
||||
});
|
||||
|
||||
masterPlaylistLoader.on('mediachanging', () => {
|
||||
mainPlaylistLoader.on('mediachanging', () => {
|
||||
['AUDIO', 'SUBTITLES'].forEach(type => mediaTypes[type].onGroupChanging());
|
||||
});
|
||||
|
||||
|
@ -912,7 +913,6 @@ export const setupMediaGroups = (settings) => {
|
|||
const onAudioTrackChanged = () => {
|
||||
mediaTypes.AUDIO.onTrackChanged();
|
||||
tech.trigger({ type: 'usage', name: 'vhs-audio-change' });
|
||||
tech.trigger({ type: 'usage', name: 'hls-audio-change' });
|
||||
};
|
||||
|
||||
tech.audioTracks().addEventListener('change', onAudioTrackChanged);
|
||||
|
|
16
node_modules/@videojs/http-streaming/src/media-segment-request.js
generated
vendored
16
node_modules/@videojs/http-streaming/src/media-segment-request.js
generated
vendored
|
@ -1,4 +1,3 @@
|
|||
import videojs from 'video.js';
|
||||
import { createTransferableMessage } from './bin-utils';
|
||||
import { stringToArrayBuffer } from './util/string-to-array-buffer';
|
||||
import { transmux } from './segment-transmuxer';
|
||||
|
@ -8,6 +7,7 @@ import {
|
|||
detectContainerForBytes,
|
||||
isLikelyFmp4MediaSegment
|
||||
} from '@videojs/vhs-utils/es/containers';
|
||||
import {merge} from './util/vjs-compat';
|
||||
|
||||
export const REQUEST_ERRORS = {
|
||||
FAILURE: 2,
|
||||
|
@ -425,7 +425,7 @@ const handleSegmentBytes = ({
|
|||
// TODO:
|
||||
// We should have a handler that fetches the number of bytes required
|
||||
// to check if something is fmp4. This will allow us to save bandwidth
|
||||
// because we can only blacklist a playlist and abort requests
|
||||
// because we can only exclude a playlist and abort requests
|
||||
// by codec after trackinfo triggers.
|
||||
if (isLikelyFmp4MediaSegment(bytesAsUint8Array)) {
|
||||
segment.isFmp4 = true;
|
||||
|
@ -514,7 +514,7 @@ const handleSegmentBytes = ({
|
|||
bytes = message.data.buffer;
|
||||
segment.bytes = bytesAsUint8Array = message.data;
|
||||
message.logs.forEach(function(log) {
|
||||
onTransmuxerLog(videojs.mergeOptions(log, {stream: 'mp4CaptionParser'}));
|
||||
onTransmuxerLog(merge(log, {stream: 'mp4CaptionParser'}));
|
||||
});
|
||||
finishLoading(message.captions);
|
||||
}
|
||||
|
@ -860,7 +860,7 @@ const handleProgress = ({
|
|||
return;
|
||||
}
|
||||
|
||||
segment.stats = videojs.mergeOptions(segment.stats, getProgressStats(event));
|
||||
segment.stats = merge(segment.stats, getProgressStats(event));
|
||||
|
||||
// record the time that we receive the first byte of data
|
||||
if (!segment.stats.firstBytesReceivedAt && segment.stats.bytesReceived) {
|
||||
|
@ -981,7 +981,7 @@ export const mediaSegmentRequest = ({
|
|||
if (segment.map && !segment.map.bytes && segment.map.key && segment.map.key.resolvedUri === segment.key.resolvedUri) {
|
||||
objects.push(segment.map.key);
|
||||
}
|
||||
const keyRequestOptions = videojs.mergeOptions(xhrOptions, {
|
||||
const keyRequestOptions = merge(xhrOptions, {
|
||||
uri: segment.key.resolvedUri,
|
||||
responseType: 'arraybuffer'
|
||||
});
|
||||
|
@ -996,7 +996,7 @@ export const mediaSegmentRequest = ({
|
|||
const differentMapKey = segment.map.key && (!segment.key || segment.key.resolvedUri !== segment.map.key.resolvedUri);
|
||||
|
||||
if (differentMapKey) {
|
||||
const mapKeyRequestOptions = videojs.mergeOptions(xhrOptions, {
|
||||
const mapKeyRequestOptions = merge(xhrOptions, {
|
||||
uri: segment.map.key.resolvedUri,
|
||||
responseType: 'arraybuffer'
|
||||
});
|
||||
|
@ -1005,7 +1005,7 @@ export const mediaSegmentRequest = ({
|
|||
|
||||
activeXhrs.push(mapKeyXhr);
|
||||
}
|
||||
const initSegmentOptions = videojs.mergeOptions(xhrOptions, {
|
||||
const initSegmentOptions = merge(xhrOptions, {
|
||||
uri: segment.map.resolvedUri,
|
||||
responseType: 'arraybuffer',
|
||||
headers: segmentXhrHeaders(segment.map)
|
||||
|
@ -1016,7 +1016,7 @@ export const mediaSegmentRequest = ({
|
|||
activeXhrs.push(initSegmentXhr);
|
||||
}
|
||||
|
||||
const segmentRequestOptions = videojs.mergeOptions(xhrOptions, {
|
||||
const segmentRequestOptions = merge(xhrOptions, {
|
||||
uri: segment.part && segment.part.resolvedUri || segment.resolvedUri,
|
||||
responseType: 'arraybuffer',
|
||||
headers: segmentXhrHeaders(segment)
|
||||
|
|
78
node_modules/@videojs/http-streaming/src/playback-watcher.js
generated
vendored
78
node_modules/@videojs/http-streaming/src/playback-watcher.js
generated
vendored
|
@ -32,7 +32,7 @@ export default class PlaybackWatcher {
|
|||
* @param {Object} options an object that includes the tech and settings
|
||||
*/
|
||||
constructor(options) {
|
||||
this.masterPlaylistController_ = options.masterPlaylistController;
|
||||
this.playlistController_ = options.playlistController;
|
||||
this.tech_ = options.tech;
|
||||
this.seekable = options.seekable;
|
||||
this.allowSeeksWithinUnsafeLiveWindow = options.allowSeeksWithinUnsafeLiveWindow;
|
||||
|
@ -41,7 +41,6 @@ export default class PlaybackWatcher {
|
|||
|
||||
this.consecutiveUpdates = 0;
|
||||
this.lastRecordedTime = null;
|
||||
this.timer_ = null;
|
||||
this.checkCurrentTimeTimeout_ = null;
|
||||
this.logger_ = logger('PlaybackWatcher');
|
||||
|
||||
|
@ -50,9 +49,9 @@ export default class PlaybackWatcher {
|
|||
const playHandler = () => this.monitorCurrentTime_();
|
||||
const canPlayHandler = () => this.monitorCurrentTime_();
|
||||
const waitingHandler = () => this.techWaiting_();
|
||||
const cancelTimerHandler = () => this.cancelTimer_();
|
||||
const cancelTimerHandler = () => this.resetTimeUpdate_();
|
||||
|
||||
const mpc = this.masterPlaylistController_;
|
||||
const pc = this.playlistController_;
|
||||
|
||||
const loaderTypes = ['main', 'subtitle', 'audio'];
|
||||
const loaderChecks = {};
|
||||
|
@ -63,11 +62,11 @@ export default class PlaybackWatcher {
|
|||
updateend: () => this.checkSegmentDownloads_(type)
|
||||
};
|
||||
|
||||
mpc[`${type}SegmentLoader_`].on('appendsdone', loaderChecks[type].updateend);
|
||||
pc[`${type}SegmentLoader_`].on('appendsdone', loaderChecks[type].updateend);
|
||||
// If a rendition switch happens during a playback stall where the buffer
|
||||
// isn't changing we want to reset. We cannot assume that the new rendition
|
||||
// will also be stalled, until after new appends.
|
||||
mpc[`${type}SegmentLoader_`].on('playlistupdate', loaderChecks[type].reset);
|
||||
pc[`${type}SegmentLoader_`].on('playlistupdate', loaderChecks[type].reset);
|
||||
// Playback stalls should not be detected right after seeking.
|
||||
// This prevents one segment playlists (single vtt or single segment content)
|
||||
// from being detected as stalling. As the buffer will not change in those cases, since
|
||||
|
@ -85,7 +84,7 @@ export default class PlaybackWatcher {
|
|||
*/
|
||||
const setSeekingHandlers = (fn) => {
|
||||
['main', 'audio'].forEach((type) => {
|
||||
mpc[`${type}SegmentLoader_`][fn]('appended', this.seekingAppendCheck_);
|
||||
pc[`${type}SegmentLoader_`][fn]('appended', this.seekingAppendCheck_);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -136,14 +135,14 @@ export default class PlaybackWatcher {
|
|||
this.tech_.off('seeked', this.clearSeekingAppendCheck_);
|
||||
|
||||
loaderTypes.forEach((type) => {
|
||||
mpc[`${type}SegmentLoader_`].off('appendsdone', loaderChecks[type].updateend);
|
||||
mpc[`${type}SegmentLoader_`].off('playlistupdate', loaderChecks[type].reset);
|
||||
pc[`${type}SegmentLoader_`].off('appendsdone', loaderChecks[type].updateend);
|
||||
pc[`${type}SegmentLoader_`].off('playlistupdate', loaderChecks[type].reset);
|
||||
this.tech_.off(['seeked', 'seeking'], loaderChecks[type].reset);
|
||||
});
|
||||
if (this.checkCurrentTimeTimeout_) {
|
||||
window.clearTimeout(this.checkCurrentTimeTimeout_);
|
||||
}
|
||||
this.cancelTimer_();
|
||||
this.resetTimeUpdate_();
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -175,7 +174,7 @@ export default class PlaybackWatcher {
|
|||
* @listens Tech#seeked
|
||||
*/
|
||||
resetSegmentDownloads_(type) {
|
||||
const loader = this.masterPlaylistController_[`${type}SegmentLoader_`];
|
||||
const loader = this.playlistController_[`${type}SegmentLoader_`];
|
||||
|
||||
if (this[`${type}StalledDownloads_`] > 0) {
|
||||
this.logger_(`resetting possible stalled download count for ${type} loader`);
|
||||
|
@ -187,7 +186,7 @@ export default class PlaybackWatcher {
|
|||
/**
|
||||
* Checks on every segment `appendsdone` to see
|
||||
* if segment appends are making progress. If they are not
|
||||
* and we are still downloading bytes. We blacklist the playlist.
|
||||
* and we are still downloading bytes. We exclude the playlist.
|
||||
*
|
||||
* @param {string} type
|
||||
* The segment loader type to check.
|
||||
|
@ -195,8 +194,8 @@ export default class PlaybackWatcher {
|
|||
* @listens SegmentLoader#appendsdone
|
||||
*/
|
||||
checkSegmentDownloads_(type) {
|
||||
const mpc = this.masterPlaylistController_;
|
||||
const loader = mpc[`${type}SegmentLoader_`];
|
||||
const pc = this.playlistController_;
|
||||
const loader = pc[`${type}SegmentLoader_`];
|
||||
const buffered = loader.buffered_();
|
||||
const isBufferedDifferent = Ranges.isRangeDifferent(this[`${type}Buffered_`], buffered);
|
||||
|
||||
|
@ -233,9 +232,10 @@ export default class PlaybackWatcher {
|
|||
|
||||
// TODO: should we exclude audio tracks rather than main tracks
|
||||
// when type is audio?
|
||||
mpc.blacklistCurrentPlaylist({
|
||||
message: `Excessive ${type} segment downloading detected.`
|
||||
}, Infinity);
|
||||
pc.excludePlaylist({
|
||||
error: { message: `Excessive ${type} segment downloading detected.` },
|
||||
playlistExclusionDuration: Infinity
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -277,20 +277,12 @@ export default class PlaybackWatcher {
|
|||
}
|
||||
|
||||
/**
|
||||
* Cancels any pending timers and resets the 'timeupdate' mechanism
|
||||
* designed to detect that we are stalled
|
||||
* Resets the 'timeupdate' mechanism designed to detect that we are stalled
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
cancelTimer_() {
|
||||
resetTimeUpdate_() {
|
||||
this.consecutiveUpdates = 0;
|
||||
|
||||
if (this.timer_) {
|
||||
this.logger_('cancelTimer_');
|
||||
clearTimeout(this.timer_);
|
||||
}
|
||||
|
||||
this.timer_ = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -347,7 +339,7 @@ export default class PlaybackWatcher {
|
|||
return true;
|
||||
}
|
||||
|
||||
const sourceUpdater = this.masterPlaylistController_.sourceUpdater_;
|
||||
const sourceUpdater = this.playlistController_.sourceUpdater_;
|
||||
const buffered = this.tech_.buffered();
|
||||
const audioBuffered = sourceUpdater.audioBuffer ? sourceUpdater.audioBuffered() : null;
|
||||
const videoBuffered = sourceUpdater.videoBuffer ? sourceUpdater.videoBuffered() : null;
|
||||
|
@ -419,7 +411,7 @@ export default class PlaybackWatcher {
|
|||
// make sure there is ~3 seconds of forward buffer before taking any corrective action
|
||||
// to avoid triggering an `unknownwaiting` event when the network is slow.
|
||||
if (currentRange.length && currentTime + 3 <= currentRange.end(0)) {
|
||||
this.cancelTimer_();
|
||||
this.resetTimeUpdate_();
|
||||
this.tech_.setCurrentTime(currentTime);
|
||||
|
||||
this.logger_(`Stopped at ${currentTime} while inside a buffered region ` +
|
||||
|
@ -428,7 +420,6 @@ export default class PlaybackWatcher {
|
|||
|
||||
// unknown waiting corrections may be useful for monitoring QoS
|
||||
this.tech_.trigger({type: 'usage', name: 'vhs-unknown-waiting'});
|
||||
this.tech_.trigger({type: 'usage', name: 'hls-unknown-waiting'});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -445,7 +436,7 @@ export default class PlaybackWatcher {
|
|||
const seekable = this.seekable();
|
||||
const currentTime = this.tech_.currentTime();
|
||||
|
||||
if (this.tech_.seeking() || this.timer_ !== null) {
|
||||
if (this.tech_.seeking()) {
|
||||
// Tech is seeking or already waiting on another action, no action needed
|
||||
return true;
|
||||
}
|
||||
|
@ -455,16 +446,15 @@ export default class PlaybackWatcher {
|
|||
|
||||
this.logger_(`Fell out of live window at time ${currentTime}. Seeking to ` +
|
||||
`live point (seekable end) ${livePoint}`);
|
||||
this.cancelTimer_();
|
||||
this.resetTimeUpdate_();
|
||||
this.tech_.setCurrentTime(livePoint);
|
||||
|
||||
// live window resyncs may be useful for monitoring QoS
|
||||
this.tech_.trigger({type: 'usage', name: 'vhs-live-resync'});
|
||||
this.tech_.trigger({type: 'usage', name: 'hls-live-resync'});
|
||||
return true;
|
||||
}
|
||||
|
||||
const sourceUpdater = this.tech_.vhs.masterPlaylistController_.sourceUpdater_;
|
||||
const sourceUpdater = this.tech_.vhs.playlistController_.sourceUpdater_;
|
||||
const buffered = this.tech_.buffered();
|
||||
const videoUnderflow = this.videoUnderflow_({
|
||||
audioBuffered: sourceUpdater.audioBuffered(),
|
||||
|
@ -477,30 +467,21 @@ export default class PlaybackWatcher {
|
|||
// the gap, leading currentTime into a buffered range. Seeking to currentTime
|
||||
// allows the video to catch up to the audio position without losing any audio
|
||||
// (only suffering ~3 seconds of frozen video and a pause in audio playback).
|
||||
this.cancelTimer_();
|
||||
this.resetTimeUpdate_();
|
||||
this.tech_.setCurrentTime(currentTime);
|
||||
|
||||
// video underflow may be useful for monitoring QoS
|
||||
this.tech_.trigger({type: 'usage', name: 'vhs-video-underflow'});
|
||||
this.tech_.trigger({type: 'usage', name: 'hls-video-underflow'});
|
||||
return true;
|
||||
}
|
||||
const nextRange = Ranges.findNextRange(buffered, currentTime);
|
||||
|
||||
// check for gap
|
||||
if (nextRange.length > 0) {
|
||||
const difference = nextRange.start(0) - currentTime;
|
||||
this.logger_(`Stopped at ${currentTime} and seeking to ${nextRange.start(0)}`);
|
||||
|
||||
this.logger_(`Stopped at ${currentTime}, setting timer for ${difference}, seeking ` +
|
||||
`to ${nextRange.start(0)}`);
|
||||
|
||||
this.cancelTimer_();
|
||||
|
||||
this.timer_ = setTimeout(
|
||||
this.skipTheGap_.bind(this),
|
||||
difference * 1000,
|
||||
currentTime
|
||||
);
|
||||
this.resetTimeUpdate_();
|
||||
this.skipTheGap_(currentTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -591,7 +572,7 @@ export default class PlaybackWatcher {
|
|||
const currentTime = this.tech_.currentTime();
|
||||
const nextRange = Ranges.findNextRange(buffered, currentTime);
|
||||
|
||||
this.cancelTimer_();
|
||||
this.resetTimeUpdate_();
|
||||
|
||||
if (nextRange.length === 0 ||
|
||||
currentTime !== scheduledCurrentTime) {
|
||||
|
@ -609,7 +590,6 @@ export default class PlaybackWatcher {
|
|||
this.tech_.setCurrentTime(nextRange.start(0) + Ranges.TIME_FUDGE_FACTOR);
|
||||
|
||||
this.tech_.trigger({type: 'usage', name: 'vhs-gap-skip'});
|
||||
this.tech_.trigger({type: 'usage', name: 'hls-gap-skip'});
|
||||
}
|
||||
|
||||
gapFromVideoUnderflow_(buffered, currentTime) {
|
||||
|
|
97
node_modules/@videojs/http-streaming/src/playlist-loader.js
generated
vendored
97
node_modules/@videojs/http-streaming/src/playlist-loader.js
generated
vendored
|
@ -11,14 +11,15 @@ import window from 'global/window';
|
|||
import logger from './util/logger';
|
||||
import {
|
||||
parseManifest,
|
||||
addPropertiesToMaster,
|
||||
masterForMedia,
|
||||
addPropertiesToMain,
|
||||
mainForMedia,
|
||||
setupMediaPlaylist,
|
||||
forEachMediaGroup
|
||||
} from './manifest';
|
||||
import {getKnownPartCount} from './playlist.js';
|
||||
import {merge} from './util/vjs-compat';
|
||||
|
||||
const { mergeOptions, EventTarget } = videojs;
|
||||
const { EventTarget } = videojs;
|
||||
|
||||
const addLLHLSQueryDirectives = (uri, media) => {
|
||||
if (media.endList || !media.serverControl) {
|
||||
|
@ -105,7 +106,7 @@ export const updateSegment = (a, b) => {
|
|||
return b;
|
||||
}
|
||||
|
||||
const result = mergeOptions(a, b);
|
||||
const result = merge(a, b);
|
||||
|
||||
// if only the old segment has preload hints
|
||||
// and the new one does not, remove preload hints.
|
||||
|
@ -123,7 +124,7 @@ export const updateSegment = (a, b) => {
|
|||
} else if (a.parts && b.parts) {
|
||||
for (let i = 0; i < b.parts.length; i++) {
|
||||
if (a.parts && a.parts[i]) {
|
||||
result.parts[i] = mergeOptions(a.parts[i], b.parts[i]);
|
||||
result.parts[i] = merge(a.parts[i], b.parts[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -261,19 +262,19 @@ export const isPlaylistUnchanged = (a, b) => a === b ||
|
|||
a.preloadSegment === b.preloadSegment);
|
||||
|
||||
/**
|
||||
* Returns a new master playlist that is the result of merging an
|
||||
* Returns a new main playlist that is the result of merging an
|
||||
* updated media playlist into the original version. If the
|
||||
* updated media playlist does not match any of the playlist
|
||||
* entries in the original master playlist, null is returned.
|
||||
* entries in the original main playlist, null is returned.
|
||||
*
|
||||
* @param {Object} master a parsed master M3U8 object
|
||||
* @param {Object} main a parsed main M3U8 object
|
||||
* @param {Object} media a parsed media M3U8 object
|
||||
* @return {Object} a new object that represents the original
|
||||
* master playlist with the updated media playlist merged in, or
|
||||
* main playlist with the updated media playlist merged in, or
|
||||
* null if the merge produced no change.
|
||||
*/
|
||||
export const updateMaster = (master, newMedia, unchangedCheck = isPlaylistUnchanged) => {
|
||||
const result = mergeOptions(master, {});
|
||||
export const updateMain = (main, newMedia, unchangedCheck = isPlaylistUnchanged) => {
|
||||
const result = merge(main, {});
|
||||
const oldMedia = result.playlists[newMedia.id];
|
||||
|
||||
if (!oldMedia) {
|
||||
|
@ -286,7 +287,7 @@ export const updateMaster = (master, newMedia, unchangedCheck = isPlaylistUnchan
|
|||
|
||||
newMedia.segments = getAllSegments(newMedia);
|
||||
|
||||
const mergedPlaylist = mergeOptions(oldMedia, newMedia);
|
||||
const mergedPlaylist = merge(oldMedia, newMedia);
|
||||
|
||||
// always use the new media's preload segment
|
||||
if (mergedPlaylist.preloadSegment && !newMedia.preloadSegment) {
|
||||
|
@ -328,7 +329,7 @@ export const updateMaster = (master, newMedia, unchangedCheck = isPlaylistUnchan
|
|||
result.playlists[newMedia.uri] = mergedPlaylist;
|
||||
|
||||
// update media group playlist references.
|
||||
forEachMediaGroup(master, (properties, mediaType, groupKey, labelKey) => {
|
||||
forEachMediaGroup(main, (properties, mediaType, groupKey, labelKey) => {
|
||||
if (!properties.playlists) {
|
||||
return;
|
||||
}
|
||||
|
@ -385,23 +386,17 @@ export default class PlaylistLoader extends EventTarget {
|
|||
}
|
||||
this.logger_ = logger('PlaylistLoader');
|
||||
|
||||
const { withCredentials = false, handleManifestRedirects = false } = options;
|
||||
const { withCredentials = false} = options;
|
||||
|
||||
this.src = src;
|
||||
this.vhs_ = vhs;
|
||||
this.withCredentials = withCredentials;
|
||||
this.handleManifestRedirects = handleManifestRedirects;
|
||||
|
||||
const vhsOptions = vhs.options_;
|
||||
|
||||
this.customTagParsers = (vhsOptions && vhsOptions.customTagParsers) || [];
|
||||
this.customTagMappers = (vhsOptions && vhsOptions.customTagMappers) || [];
|
||||
this.experimentalLLHLS = (vhsOptions && vhsOptions.experimentalLLHLS) || false;
|
||||
|
||||
// force experimentalLLHLS for IE 11
|
||||
if (videojs.browser.IE_VERSION) {
|
||||
this.experimentalLLHLS = false;
|
||||
}
|
||||
this.llhls = vhsOptions && vhsOptions.llhls;
|
||||
|
||||
// initialize the loader state
|
||||
this.state = 'HAVE_NOTHING';
|
||||
|
@ -418,9 +413,9 @@ export default class PlaylistLoader extends EventTarget {
|
|||
}
|
||||
const media = this.media();
|
||||
|
||||
let uri = resolveUrl(this.master.uri, media.uri);
|
||||
let uri = resolveUrl(this.main.uri, media.uri);
|
||||
|
||||
if (this.experimentalLLHLS) {
|
||||
if (this.llhls) {
|
||||
uri = addLLHLSQueryDirectives(uri, media);
|
||||
}
|
||||
this.state = 'HAVE_CURRENT_METADATA';
|
||||
|
@ -461,7 +456,7 @@ export default class PlaylistLoader extends EventTarget {
|
|||
}
|
||||
|
||||
this.error = {
|
||||
playlist: this.master.playlists[id],
|
||||
playlist: this.main.playlists[id],
|
||||
status: xhr.status,
|
||||
message: `HLS playlist request error at URL: ${uri}.`,
|
||||
responseText: xhr.responseText,
|
||||
|
@ -478,7 +473,7 @@ export default class PlaylistLoader extends EventTarget {
|
|||
manifestString,
|
||||
customTagParsers: this.customTagParsers,
|
||||
customTagMappers: this.customTagMappers,
|
||||
experimentalLLHLS: this.experimentalLLHLS
|
||||
llhls: this.llhls
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -512,16 +507,16 @@ export default class PlaylistLoader extends EventTarget {
|
|||
id
|
||||
});
|
||||
|
||||
// merge this playlist into the master
|
||||
const update = updateMaster(this.master, playlist);
|
||||
// merge this playlist into the main manifest
|
||||
const update = updateMain(this.main, playlist);
|
||||
|
||||
this.targetDuration = playlist.partTargetDuration || playlist.targetDuration;
|
||||
|
||||
this.pendingMedia_ = null;
|
||||
|
||||
if (update) {
|
||||
this.master = update;
|
||||
this.media_ = this.master.playlists[id];
|
||||
this.main = update;
|
||||
this.media_ = this.main.playlists[id];
|
||||
} else {
|
||||
this.trigger('playlistunchanged');
|
||||
}
|
||||
|
@ -581,10 +576,10 @@ export default class PlaylistLoader extends EventTarget {
|
|||
// find the playlist object if the target playlist has been
|
||||
// specified by URI
|
||||
if (typeof playlist === 'string') {
|
||||
if (!this.master.playlists[playlist]) {
|
||||
if (!this.main.playlists[playlist]) {
|
||||
throw new Error('Unknown playlist URI: ' + playlist);
|
||||
}
|
||||
playlist = this.master.playlists[playlist];
|
||||
playlist = this.main.playlists[playlist];
|
||||
}
|
||||
|
||||
window.clearTimeout(this.finalRenditionTimeout);
|
||||
|
@ -599,10 +594,10 @@ export default class PlaylistLoader extends EventTarget {
|
|||
|
||||
const startingState = this.state;
|
||||
const mediaChange = !this.media_ || playlist.id !== this.media_.id;
|
||||
const masterPlaylistRef = this.master.playlists[playlist.id];
|
||||
const mainPlaylistRef = this.main.playlists[playlist.id];
|
||||
|
||||
// switch to fully loaded playlists immediately
|
||||
if (masterPlaylistRef && masterPlaylistRef.endList ||
|
||||
if (mainPlaylistRef && mainPlaylistRef.endList ||
|
||||
// handle the case of a playlist object (e.g., if using vhs-json with a resolved
|
||||
// media playlist or, for the case of demuxed audio, a resolved audio media group)
|
||||
(playlist.endList && playlist.segments.length)) {
|
||||
|
@ -620,8 +615,8 @@ export default class PlaylistLoader extends EventTarget {
|
|||
if (mediaChange) {
|
||||
this.trigger('mediachanging');
|
||||
|
||||
if (startingState === 'HAVE_MASTER') {
|
||||
// The initial playlist was a master manifest, and the first media selected was
|
||||
if (startingState === 'HAVE_MAIN_MANIFEST') {
|
||||
// The initial playlist was a main manifest, and the first media selected was
|
||||
// also provided (in the form of a resolved playlist object) as part of the
|
||||
// source object (rather than just a URL). Therefore, since the media playlist
|
||||
// doesn't need to be requested, loadedmetadata won't trigger as part of the
|
||||
|
@ -678,7 +673,7 @@ export default class PlaylistLoader extends EventTarget {
|
|||
|
||||
playlist.lastRequest = Date.now();
|
||||
|
||||
playlist.resolvedUri = resolveManifestRedirect(this.handleManifestRedirects, playlist.resolvedUri, req);
|
||||
playlist.resolvedUri = resolveManifestRedirect(playlist.resolvedUri, req);
|
||||
|
||||
if (error) {
|
||||
return this.playlistRequestError(this.request, playlist, startingState);
|
||||
|
@ -691,7 +686,7 @@ export default class PlaylistLoader extends EventTarget {
|
|||
});
|
||||
|
||||
// fire loadedmetadata the first time a media playlist is loaded
|
||||
if (startingState === 'HAVE_MASTER') {
|
||||
if (startingState === 'HAVE_MAIN_MANIFEST') {
|
||||
this.trigger('loadedmetadata');
|
||||
} else {
|
||||
this.trigger('mediachange');
|
||||
|
@ -717,12 +712,12 @@ export default class PlaylistLoader extends EventTarget {
|
|||
// Need to restore state now that no activity is happening
|
||||
if (this.state === 'SWITCHING_MEDIA') {
|
||||
// if the loader was in the process of switching media, it should either return to
|
||||
// HAVE_MASTER or HAVE_METADATA depending on if the loader has loaded a media
|
||||
// HAVE_MAIN_MANIFEST or HAVE_METADATA depending on if the loader has loaded a media
|
||||
// playlist yet. This is determined by the existence of loader.media_
|
||||
if (this.media_) {
|
||||
this.state = 'HAVE_METADATA';
|
||||
} else {
|
||||
this.state = 'HAVE_MASTER';
|
||||
this.state = 'HAVE_MAIN_MANIFEST';
|
||||
}
|
||||
} else if (this.state === 'HAVE_CURRENT_METADATA') {
|
||||
this.state = 'HAVE_METADATA';
|
||||
|
@ -800,7 +795,7 @@ export default class PlaylistLoader extends EventTarget {
|
|||
// Since a manifest object was passed in as the source (instead of a URL), the first
|
||||
// request can be skipped (since the top level of the manifest, at a minimum, is
|
||||
// already available as a parsed manifest object). However, if the manifest object
|
||||
// represents a master playlist, some media playlists may need to be resolved before
|
||||
// represents a main playlist, some media playlists may need to be resolved before
|
||||
// the starting segment list is available. Therefore, go directly to setup of the
|
||||
// initial playlist, and let the normal flow continue from there.
|
||||
//
|
||||
|
@ -839,7 +834,7 @@ export default class PlaylistLoader extends EventTarget {
|
|||
return this.trigger('error');
|
||||
}
|
||||
|
||||
this.src = resolveManifestRedirect(this.handleManifestRedirects, this.src, req);
|
||||
this.src = resolveManifestRedirect(this.src, req);
|
||||
|
||||
const manifest = this.parseManifest_({
|
||||
manifestString: req.responseText,
|
||||
|
@ -855,17 +850,17 @@ export default class PlaylistLoader extends EventTarget {
|
|||
}
|
||||
|
||||
/**
|
||||
* Given a manifest object that's either a master or media playlist, trigger the proper
|
||||
* Given a manifest object that's either a main or media playlist, trigger the proper
|
||||
* events and set the state of the playlist loader.
|
||||
*
|
||||
* If the manifest object represents a master playlist, `loadedplaylist` will be
|
||||
* If the manifest object represents a main playlist, `loadedplaylist` will be
|
||||
* triggered to allow listeners to select a playlist. If none is selected, the loader
|
||||
* will default to the first one in the playlists array.
|
||||
*
|
||||
* If the manifest object represents a media playlist, `loadedplaylist` will be
|
||||
* triggered followed by `loadedmetadata`, as the only available playlist is loaded.
|
||||
*
|
||||
* In the case of a media playlist, a master playlist object wrapper with one playlist
|
||||
* In the case of a media playlist, a main playlist object wrapper with one playlist
|
||||
* will be created so that all logic can handle playlists in the same fashion (as an
|
||||
* assumed manifest object schema).
|
||||
*
|
||||
|
@ -873,12 +868,12 @@ export default class PlaylistLoader extends EventTarget {
|
|||
* The parsed manifest object
|
||||
*/
|
||||
setupInitialPlaylist(manifest) {
|
||||
this.state = 'HAVE_MASTER';
|
||||
this.state = 'HAVE_MAIN_MANIFEST';
|
||||
|
||||
if (manifest.playlists) {
|
||||
this.master = manifest;
|
||||
addPropertiesToMaster(this.master, this.srcUri());
|
||||
// If the initial master playlist has playlists wtih segments already resolved,
|
||||
this.main = manifest;
|
||||
addPropertiesToMain(this.main, this.srcUri());
|
||||
// If the initial main playlist has playlists wtih segments already resolved,
|
||||
// then resolve URIs in advance, as they are usually done after a playlist request,
|
||||
// which may not happen if the playlist is resolved.
|
||||
manifest.playlists.forEach((playlist) => {
|
||||
|
@ -892,7 +887,7 @@ export default class PlaylistLoader extends EventTarget {
|
|||
if (!this.request) {
|
||||
// no media playlist was specifically selected so start
|
||||
// from the first listed one
|
||||
this.media(this.master.playlists[0]);
|
||||
this.media(this.main.playlists[0]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -902,11 +897,11 @@ export default class PlaylistLoader extends EventTarget {
|
|||
// default used.
|
||||
const uri = this.srcUri() || window.location.href;
|
||||
|
||||
this.master = masterForMedia(manifest, uri);
|
||||
this.main = mainForMedia(manifest, uri);
|
||||
this.haveMetadata({
|
||||
playlistObject: manifest,
|
||||
url: uri,
|
||||
id: this.master.playlists[0].id
|
||||
id: this.main.playlists[0].id
|
||||
});
|
||||
this.trigger('loadedmetadata');
|
||||
}
|
||||
|
|
56
node_modules/@videojs/http-streaming/src/playlist-selectors.js
generated
vendored
56
node_modules/@videojs/http-streaming/src/playlist-selectors.js
generated
vendored
|
@ -133,8 +133,8 @@ export const comparePlaylistResolution = function(left, right) {
|
|||
/**
|
||||
* Chooses the appropriate media playlist based on bandwidth and player size
|
||||
*
|
||||
* @param {Object} master
|
||||
* Object representation of the master manifest
|
||||
* @param {Object} main
|
||||
* Object representation of the main manifest
|
||||
* @param {number} playerBandwidth
|
||||
* Current calculated bandwidth of the player
|
||||
* @param {number} playerWidth
|
||||
|
@ -143,23 +143,23 @@ export const comparePlaylistResolution = function(left, right) {
|
|||
* Current height of the player element (should account for the device pixel ratio)
|
||||
* @param {boolean} limitRenditionByPlayerDimensions
|
||||
* True if the player width and height should be used during the selection, false otherwise
|
||||
* @param {Object} masterPlaylistController
|
||||
* the current masterPlaylistController object
|
||||
* @param {Object} playlistController
|
||||
* the current playlistController object
|
||||
* @return {Playlist} the highest bitrate playlist less than the
|
||||
* currently detected bandwidth, accounting for some amount of
|
||||
* bandwidth variance
|
||||
*/
|
||||
export let simpleSelector = function(
|
||||
master,
|
||||
main,
|
||||
playerBandwidth,
|
||||
playerWidth,
|
||||
playerHeight,
|
||||
limitRenditionByPlayerDimensions,
|
||||
masterPlaylistController
|
||||
playlistController
|
||||
) {
|
||||
|
||||
// If we end up getting called before `master` is available, exit early
|
||||
if (!master) {
|
||||
// If we end up getting called before `main` is available, exit early
|
||||
if (!main) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -170,11 +170,11 @@ export let simpleSelector = function(
|
|||
limitRenditionByPlayerDimensions
|
||||
};
|
||||
|
||||
let playlists = master.playlists;
|
||||
let playlists = main.playlists;
|
||||
|
||||
// if playlist is audio only, select between currently active audio group playlists.
|
||||
if (Playlist.isAudioOnly(master)) {
|
||||
playlists = masterPlaylistController.getAudioTrackPlaylists_();
|
||||
if (Playlist.isAudioOnly(main)) {
|
||||
playlists = playlistController.getAudioTrackPlaylists_();
|
||||
// add audioOnly to options so that we log audioOnly: true
|
||||
// at the buttom of this function for debugging.
|
||||
options.audioOnly = true;
|
||||
|
@ -204,12 +204,12 @@ export let simpleSelector = function(
|
|||
sortedPlaylistReps = sortedPlaylistReps.filter((rep) => !Playlist.isIncompatible(rep.playlist));
|
||||
|
||||
// filter out any playlists that have been disabled manually through the representations
|
||||
// api or blacklisted temporarily due to playback errors.
|
||||
// api or excluded temporarily due to playback errors.
|
||||
let enabledPlaylistReps = sortedPlaylistReps.filter((rep) => Playlist.isEnabled(rep.playlist));
|
||||
|
||||
if (!enabledPlaylistReps.length) {
|
||||
// if there are no enabled playlists, then they have all been blacklisted or disabled
|
||||
// by the user through the representations api. In this case, ignore blacklisting and
|
||||
// if there are no enabled playlists, then they have all been excluded or disabled
|
||||
// by the user through the representations api. In this case, ignore exclusion and
|
||||
// fallback to what the user wants by using playlists the user has not disabled.
|
||||
enabledPlaylistReps = sortedPlaylistReps.filter((rep) => !Playlist.isDisabled(rep.playlist));
|
||||
}
|
||||
|
@ -289,7 +289,7 @@ export let simpleSelector = function(
|
|||
// If this selector proves to be better than others,
|
||||
// resolutionPlusOneRep and resolutionBestRep and all
|
||||
// the code involving them should be removed.
|
||||
if (masterPlaylistController.experimentalLeastPixelDiffSelector) {
|
||||
if (playlistController.leastPixelDiffSelector) {
|
||||
// find the variant that is closest to the player's pixel size
|
||||
const leastPixelDiffList = haveResolution.map((rep) => {
|
||||
rep.pixelDiff = Math.abs(rep.width - playerWidth) + Math.abs(rep.height - playerHeight);
|
||||
|
@ -367,12 +367,12 @@ export const lastBandwidthSelector = function() {
|
|||
const pixelRatio = this.useDevicePixelRatio ? window.devicePixelRatio || 1 : 1;
|
||||
|
||||
return simpleSelector(
|
||||
this.playlists.master,
|
||||
this.playlists.main,
|
||||
this.systemBandwidth,
|
||||
parseInt(safeGetComputedStyle(this.tech_.el(), 'width'), 10) * pixelRatio,
|
||||
parseInt(safeGetComputedStyle(this.tech_.el(), 'height'), 10) * pixelRatio,
|
||||
this.limitRenditionByPlayerDimensions,
|
||||
this.masterPlaylistController_
|
||||
this.playlistController_
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -418,12 +418,12 @@ export const movingAverageBandwidthSelector = function(decay) {
|
|||
}
|
||||
|
||||
return simpleSelector(
|
||||
this.playlists.master,
|
||||
this.playlists.main,
|
||||
average,
|
||||
parseInt(safeGetComputedStyle(this.tech_.el(), 'width'), 10) * pixelRatio,
|
||||
parseInt(safeGetComputedStyle(this.tech_.el(), 'height'), 10) * pixelRatio,
|
||||
this.limitRenditionByPlayerDimensions,
|
||||
this.masterPlaylistController_
|
||||
this.playlistController_
|
||||
);
|
||||
};
|
||||
};
|
||||
|
@ -433,8 +433,8 @@ export const movingAverageBandwidthSelector = function(decay) {
|
|||
*
|
||||
* @param {Object} settings
|
||||
* Object of information required to use this selector
|
||||
* @param {Object} settings.master
|
||||
* Object representation of the master manifest
|
||||
* @param {Object} settings.main
|
||||
* Object representation of the main manifest
|
||||
* @param {number} settings.currentTime
|
||||
* The current time of the player
|
||||
* @param {number} settings.bandwidth
|
||||
|
@ -458,7 +458,7 @@ export const movingAverageBandwidthSelector = function(decay) {
|
|||
*/
|
||||
export const minRebufferMaxBandwidthSelector = function(settings) {
|
||||
const {
|
||||
master,
|
||||
main,
|
||||
currentTime,
|
||||
bandwidth,
|
||||
duration,
|
||||
|
@ -470,15 +470,15 @@ export const minRebufferMaxBandwidthSelector = function(settings) {
|
|||
|
||||
// filter out any playlists that have been excluded due to
|
||||
// incompatible configurations
|
||||
const compatiblePlaylists = master.playlists.filter(playlist => !Playlist.isIncompatible(playlist));
|
||||
const compatiblePlaylists = main.playlists.filter(playlist => !Playlist.isIncompatible(playlist));
|
||||
|
||||
// filter out any playlists that have been disabled manually through the representations
|
||||
// api or blacklisted temporarily due to playback errors.
|
||||
// api or excluded temporarily due to playback errors.
|
||||
let enabledPlaylists = compatiblePlaylists.filter(Playlist.isEnabled);
|
||||
|
||||
if (!enabledPlaylists.length) {
|
||||
// if there are no enabled playlists, then they have all been blacklisted or disabled
|
||||
// by the user through the representations api. In this case, ignore blacklisting and
|
||||
// if there are no enabled playlists, then they have all been excluded or disabled
|
||||
// by the user through the representations api. In this case, ignore exclusion and
|
||||
// fallback to what the user wants by using playlists the user has not disabled.
|
||||
enabledPlaylists = compatiblePlaylists.filter(playlist => !Playlist.isDisabled(playlist));
|
||||
}
|
||||
|
@ -540,7 +540,7 @@ export const minRebufferMaxBandwidthSelector = function(settings) {
|
|||
export const lowestBitrateCompatibleVariantSelector = function() {
|
||||
// filter out any playlists that have been excluded due to
|
||||
// incompatible configurations or playback errors
|
||||
const playlists = this.playlists.master.playlists.filter(Playlist.isEnabled);
|
||||
const playlists = this.playlists.main.playlists.filter(Playlist.isEnabled);
|
||||
|
||||
// Sort ascending by bitrate
|
||||
stableSort(
|
||||
|
@ -553,7 +553,7 @@ export const lowestBitrateCompatibleVariantSelector = function() {
|
|||
//
|
||||
// If an entire manifest has no valid videos everything will get filtered
|
||||
// out.
|
||||
const playlistsWithVideo = playlists.filter(playlist => !!codecsForPlaylist(this.playlists.master, playlist).video);
|
||||
const playlistsWithVideo = playlists.filter(playlist => !!codecsForPlaylist(this.playlists.main, playlist).video);
|
||||
|
||||
return playlistsWithVideo[0] || null;
|
||||
};
|
||||
|
|
62
node_modules/@videojs/http-streaming/src/playlist.js
generated
vendored
62
node_modules/@videojs/http-streaming/src/playlist.js
generated
vendored
|
@ -3,12 +3,10 @@
|
|||
*
|
||||
* Playlist related utilities.
|
||||
*/
|
||||
import videojs from 'video.js';
|
||||
import window from 'global/window';
|
||||
import {isAudioCodec} from '@videojs/vhs-utils/es/codecs.js';
|
||||
import {TIME_FUDGE_FACTOR} from './ranges.js';
|
||||
|
||||
const {createTimeRange} = videojs;
|
||||
import {createTimeRanges} from './util/vjs-compat';
|
||||
|
||||
/**
|
||||
* Get the duration of a segment, with special cases for
|
||||
|
@ -88,18 +86,18 @@ export const getKnownPartCount = ({preloadSegment}) => {
|
|||
* Get the number of seconds to delay from the end of a
|
||||
* live playlist.
|
||||
*
|
||||
* @param {Playlist} master the master playlist
|
||||
* @param {Playlist} main the main playlist
|
||||
* @param {Playlist} media the media playlist
|
||||
* @return {number} the hold back in seconds.
|
||||
*/
|
||||
export const liveEdgeDelay = (master, media) => {
|
||||
export const liveEdgeDelay = (main, media) => {
|
||||
if (media.endList) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// dash suggestedPresentationDelay trumps everything
|
||||
if (master && master.suggestedPresentationDelay) {
|
||||
return master.suggestedPresentationDelay;
|
||||
if (main && main.suggestedPresentationDelay) {
|
||||
return main.suggestedPresentationDelay;
|
||||
}
|
||||
|
||||
const hasParts = getLastParts(media).length > 0;
|
||||
|
@ -396,9 +394,9 @@ export const seekable = function(playlist, expired, liveEdgePadding) {
|
|||
const seekableEnd = playlistEnd(playlist, expired, useSafeLiveEnd, liveEdgePadding);
|
||||
|
||||
if (seekableEnd === null) {
|
||||
return createTimeRange();
|
||||
return createTimeRanges();
|
||||
}
|
||||
return createTimeRange(seekableStart, seekableEnd);
|
||||
return createTimeRanges(seekableStart, seekableEnd);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -420,7 +418,7 @@ export const getMediaInfoForTime = function({
|
|||
startingSegmentIndex,
|
||||
startingPartIndex,
|
||||
startTime,
|
||||
experimentalExactManifestTimings
|
||||
exactManifestTimings
|
||||
}) {
|
||||
|
||||
let time = currentTime - startTime;
|
||||
|
@ -453,7 +451,7 @@ export const getMediaInfoForTime = function({
|
|||
|
||||
time += partAndSegment.duration;
|
||||
|
||||
if (experimentalExactManifestTimings) {
|
||||
if (exactManifestTimings) {
|
||||
if (time < 0) {
|
||||
continue;
|
||||
}
|
||||
|
@ -507,7 +505,7 @@ export const getMediaInfoForTime = function({
|
|||
|
||||
time -= partAndSegment.duration;
|
||||
|
||||
if (experimentalExactManifestTimings) {
|
||||
if (exactManifestTimings) {
|
||||
if (time > 0) {
|
||||
continue;
|
||||
}
|
||||
|
@ -536,19 +534,19 @@ export const getMediaInfoForTime = function({
|
|||
};
|
||||
|
||||
/**
|
||||
* Check whether the playlist is blacklisted or not.
|
||||
* Check whether the playlist is excluded or not.
|
||||
*
|
||||
* @param {Object} playlist the media playlist object
|
||||
* @return {boolean} whether the playlist is blacklisted or not
|
||||
* @function isBlacklisted
|
||||
* @return {boolean} whether the playlist is excluded or not
|
||||
* @function isExcluded
|
||||
*/
|
||||
export const isBlacklisted = function(playlist) {
|
||||
export const isExcluded = function(playlist) {
|
||||
return playlist.excludeUntil && playlist.excludeUntil > Date.now();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether the playlist is compatible with current playback configuration or has
|
||||
* been blacklisted permanently for being incompatible.
|
||||
* been excluded permanently for being incompatible.
|
||||
*
|
||||
* @param {Object} playlist the media playlist object
|
||||
* @return {boolean} whether the playlist is incompatible or not
|
||||
|
@ -566,9 +564,9 @@ export const isIncompatible = function(playlist) {
|
|||
* @function isEnabled
|
||||
*/
|
||||
export const isEnabled = function(playlist) {
|
||||
const blacklisted = isBlacklisted(playlist);
|
||||
const excluded = isExcluded(playlist);
|
||||
|
||||
return (!playlist.disabled && !blacklisted);
|
||||
return (!playlist.disabled && !excluded);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -647,14 +645,14 @@ export const estimateSegmentRequestTime = function(
|
|||
*
|
||||
* @return {Boolean} true if on lowest rendition
|
||||
*/
|
||||
export const isLowestEnabledRendition = (master, media) => {
|
||||
if (master.playlists.length === 1) {
|
||||
export const isLowestEnabledRendition = (main, media) => {
|
||||
if (main.playlists.length === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const currentBandwidth = media.attributes.BANDWIDTH || Number.MAX_VALUE;
|
||||
|
||||
return (master.playlists.filter((playlist) => {
|
||||
return (main.playlists.filter((playlist) => {
|
||||
if (!isEnabled(playlist)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -698,8 +696,8 @@ export const playlistMatch = (a, b) => {
|
|||
return false;
|
||||
};
|
||||
|
||||
const someAudioVariant = function(master, callback) {
|
||||
const AUDIO = master && master.mediaGroups && master.mediaGroups.AUDIO || {};
|
||||
const someAudioVariant = function(main, callback) {
|
||||
const AUDIO = main && main.mediaGroups && main.mediaGroups.AUDIO || {};
|
||||
let found = false;
|
||||
|
||||
for (const groupName in AUDIO) {
|
||||
|
@ -719,21 +717,21 @@ const someAudioVariant = function(master, callback) {
|
|||
return !!found;
|
||||
};
|
||||
|
||||
export const isAudioOnly = (master) => {
|
||||
export const isAudioOnly = (main) => {
|
||||
// we are audio only if we have no main playlists but do
|
||||
// have media group playlists.
|
||||
if (!master || !master.playlists || !master.playlists.length) {
|
||||
if (!main || !main.playlists || !main.playlists.length) {
|
||||
// without audio variants or playlists this
|
||||
// is not an audio only master.
|
||||
const found = someAudioVariant(master, (variant) =>
|
||||
// is not an audio only main.
|
||||
const found = someAudioVariant(main, (variant) =>
|
||||
(variant.playlists && variant.playlists.length) || variant.uri);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
// if every playlist has only an audio codec it is audio only
|
||||
for (let i = 0; i < master.playlists.length; i++) {
|
||||
const playlist = master.playlists[i];
|
||||
for (let i = 0; i < main.playlists.length; i++) {
|
||||
const playlist = main.playlists[i];
|
||||
const CODECS = playlist.attributes && playlist.attributes.CODECS;
|
||||
|
||||
// all codecs are audio, this is an audio playlist.
|
||||
|
@ -742,7 +740,7 @@ export const isAudioOnly = (master) => {
|
|||
}
|
||||
|
||||
// playlist is in an audio group it is audio only
|
||||
const found = someAudioVariant(master, (variant) => playlistMatch(playlist, variant));
|
||||
const found = someAudioVariant(main, (variant) => playlistMatch(playlist, variant));
|
||||
|
||||
if (found) {
|
||||
continue;
|
||||
|
@ -766,7 +764,7 @@ export default {
|
|||
getMediaInfoForTime,
|
||||
isEnabled,
|
||||
isDisabled,
|
||||
isBlacklisted,
|
||||
isExcluded,
|
||||
isIncompatible,
|
||||
playlistEnd,
|
||||
isAes,
|
||||
|
|
17
node_modules/@videojs/http-streaming/src/ranges.js
generated
vendored
17
node_modules/@videojs/http-streaming/src/ranges.js
generated
vendored
|
@ -4,8 +4,7 @@
|
|||
* Utilities for working with TimeRanges.
|
||||
*
|
||||
*/
|
||||
|
||||
import videojs from 'video.js';
|
||||
import {createTimeRanges} from './util/vjs-compat';
|
||||
|
||||
// Fudge factor to account for TimeRanges rounding
|
||||
export const TIME_FUDGE_FACTOR = 1 / 30;
|
||||
|
@ -40,7 +39,7 @@ const filterRanges = function(timeRanges, predicate) {
|
|||
}
|
||||
}
|
||||
|
||||
return videojs.createTimeRanges(results);
|
||||
return createTimeRanges(results);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -79,7 +78,7 @@ export const findNextRange = function(timeRanges, time) {
|
|||
*/
|
||||
export const findGaps = function(buffered) {
|
||||
if (buffered.length < 2) {
|
||||
return videojs.createTimeRanges();
|
||||
return createTimeRanges();
|
||||
}
|
||||
|
||||
const ranges = [];
|
||||
|
@ -91,7 +90,7 @@ export const findGaps = function(buffered) {
|
|||
ranges.push([start, end]);
|
||||
}
|
||||
|
||||
return videojs.createTimeRanges(ranges);
|
||||
return createTimeRanges(ranges);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -170,7 +169,7 @@ export const bufferIntersection = function(bufferA, bufferB) {
|
|||
const ranges = [];
|
||||
|
||||
if (!bufferA || !bufferA.length || !bufferB || !bufferB.length) {
|
||||
return videojs.createTimeRange();
|
||||
return createTimeRanges();
|
||||
}
|
||||
|
||||
// Handle the case where we have both buffers and create an
|
||||
|
@ -221,7 +220,7 @@ export const bufferIntersection = function(bufferA, bufferB) {
|
|||
}
|
||||
}
|
||||
|
||||
return videojs.createTimeRanges(ranges);
|
||||
return createTimeRanges(ranges);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -300,7 +299,7 @@ export const getSegmentBufferedPercent = function(
|
|||
const endOfSegment = startOfSegment + segmentDuration;
|
||||
|
||||
// The entire time range of the segment
|
||||
const originalSegmentRange = videojs.createTimeRanges([[
|
||||
const originalSegmentRange = createTimeRanges([[
|
||||
startOfSegment,
|
||||
endOfSegment
|
||||
]]);
|
||||
|
@ -311,7 +310,7 @@ export const getSegmentBufferedPercent = function(
|
|||
// for that and the function will still return 100% if a only half of a
|
||||
// segment is actually in the buffer as long as the currentTime is also
|
||||
// half-way through the segment
|
||||
const adjustedSegmentRange = videojs.createTimeRanges([[
|
||||
const adjustedSegmentRange = createTimeRanges([[
|
||||
clamp(startOfSegment, [currentTime, endOfSegment]),
|
||||
endOfSegment
|
||||
]]);
|
||||
|
|
6
node_modules/@videojs/http-streaming/src/reload-source-on-error.js
generated
vendored
6
node_modules/@videojs/http-streaming/src/reload-source-on-error.js
generated
vendored
|
@ -1,4 +1,5 @@
|
|||
import videojs from 'video.js';
|
||||
import {merge} from './util/vjs-compat';
|
||||
|
||||
const defaultOptions = {
|
||||
errorInterval: 30,
|
||||
|
@ -20,11 +21,10 @@ const defaultOptions = {
|
|||
const initPlugin = function(player, options) {
|
||||
let lastCalled = 0;
|
||||
let seekTo = 0;
|
||||
const localOptions = videojs.mergeOptions(defaultOptions, options);
|
||||
const localOptions = merge(defaultOptions, options);
|
||||
|
||||
player.ready(() => {
|
||||
player.trigger({type: 'usage', name: 'vhs-error-reload-initialized'});
|
||||
player.trigger({type: 'usage', name: 'hls-error-reload-initialized'});
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -55,7 +55,6 @@ const initPlugin = function(player, options) {
|
|||
|
||||
player.src(sourceObj);
|
||||
player.trigger({type: 'usage', name: 'vhs-error-reload'});
|
||||
player.trigger({type: 'usage', name: 'hls-error-reload'});
|
||||
player.play();
|
||||
};
|
||||
|
||||
|
@ -70,7 +69,6 @@ const initPlugin = function(player, options) {
|
|||
// 'errorInterval' time has elapsed since the last source-reload
|
||||
if (Date.now() - lastCalled < localOptions.errorInterval * 1000) {
|
||||
player.trigger({type: 'usage', name: 'vhs-error-reload-canceled'});
|
||||
player.trigger({type: 'usage', name: 'hls-error-reload-canceled'});
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
23
node_modules/@videojs/http-streaming/src/rendition-mixin.js
generated
vendored
23
node_modules/@videojs/http-streaming/src/rendition-mixin.js
generated
vendored
|
@ -4,7 +4,7 @@ import { codecsForPlaylist } from './util/codecs.js';
|
|||
/**
|
||||
* Returns a function that acts as the Enable/disable playlist function.
|
||||
*
|
||||
* @param {PlaylistLoader} loader - The master playlist loader
|
||||
* @param {PlaylistLoader} loader - The main playlist loader
|
||||
* @param {string} playlistID - id of the playlist
|
||||
* @param {Function} changePlaylistFn - A function to be called after a
|
||||
* playlist's enabled-state has been changed. Will NOT be called if a
|
||||
|
@ -14,7 +14,7 @@ import { codecsForPlaylist } from './util/codecs.js';
|
|||
* @return {Function} Function for setting/getting enabled
|
||||
*/
|
||||
const enableFunction = (loader, playlistID, changePlaylistFn) => (enable) => {
|
||||
const playlist = loader.master.playlists[playlistID];
|
||||
const playlist = loader.main.playlists[playlistID];
|
||||
const incompatible = isIncompatible(playlist);
|
||||
const currentlyEnabled = isEnabled(playlist);
|
||||
|
||||
|
@ -50,12 +50,9 @@ const enableFunction = (loader, playlistID, changePlaylistFn) => (enable) => {
|
|||
class Representation {
|
||||
constructor(vhsHandler, playlist, id) {
|
||||
const {
|
||||
masterPlaylistController_: mpc,
|
||||
options_: { smoothQualityChange }
|
||||
playlistController_: pc
|
||||
} = vhsHandler;
|
||||
// Get a reference to a bound version of the quality change function
|
||||
const changeType = smoothQualityChange ? 'smooth' : 'fast';
|
||||
const qualityChangeFunction = mpc[`${changeType}QualityChange_`].bind(mpc);
|
||||
const qualityChangeFunction = pc.fastQualityChange_.bind(pc);
|
||||
|
||||
// some playlist attributes are optional
|
||||
if (playlist.attributes) {
|
||||
|
@ -68,12 +65,12 @@ class Representation {
|
|||
this.frameRate = playlist.attributes['FRAME-RATE'];
|
||||
}
|
||||
|
||||
this.codecs = codecsForPlaylist(mpc.master(), playlist);
|
||||
this.codecs = codecsForPlaylist(pc.main(), playlist);
|
||||
|
||||
this.playlist = playlist;
|
||||
|
||||
// The id is simply the ordinality of the media playlist
|
||||
// within the master playlist
|
||||
// within the main playlist
|
||||
this.id = id;
|
||||
|
||||
// Partially-apply the enableFunction to create a playlist-
|
||||
|
@ -97,10 +94,10 @@ const renditionSelectionMixin = function(vhsHandler) {
|
|||
|
||||
// Add a single API-specific function to the VhsHandler instance
|
||||
vhsHandler.representations = () => {
|
||||
const master = vhsHandler.masterPlaylistController_.master();
|
||||
const playlists = isAudioOnly(master) ?
|
||||
vhsHandler.masterPlaylistController_.getAudioTrackPlaylists_() :
|
||||
master.playlists;
|
||||
const main = vhsHandler.playlistController_.main();
|
||||
const playlists = isAudioOnly(main) ?
|
||||
vhsHandler.playlistController_.getAudioTrackPlaylists_() :
|
||||
main.playlists;
|
||||
|
||||
if (!playlists) {
|
||||
return [];
|
||||
|
|
7
node_modules/@videojs/http-streaming/src/resolve-url.js
generated
vendored
7
node_modules/@videojs/http-streaming/src/resolve-url.js
generated
vendored
|
@ -7,8 +7,8 @@ import _resolveUrl from '@videojs/vhs-utils/es/resolve-url.js';
|
|||
export const resolveUrl = _resolveUrl;
|
||||
|
||||
/**
|
||||
* Checks whether xhr request was redirected and returns correct url depending
|
||||
* on `handleManifestRedirects` option
|
||||
* If the xhr request was redirected, return the responseURL, otherwise,
|
||||
* return the original url.
|
||||
*
|
||||
* @api private
|
||||
*
|
||||
|
@ -17,12 +17,11 @@ export const resolveUrl = _resolveUrl;
|
|||
*
|
||||
* @return {string}
|
||||
*/
|
||||
export const resolveManifestRedirect = (handleManifestRedirect, url, req) => {
|
||||
export const resolveManifestRedirect = (url, req) => {
|
||||
// To understand how the responseURL below is set and generated:
|
||||
// - https://fetch.spec.whatwg.org/#concept-response-url
|
||||
// - https://fetch.spec.whatwg.org/#atomic-http-redirect-handling
|
||||
if (
|
||||
handleManifestRedirect &&
|
||||
req &&
|
||||
req.responseURL &&
|
||||
url !== req.responseURL
|
||||
|
|
20
node_modules/@videojs/http-streaming/src/segment-loader.js
generated
vendored
20
node_modules/@videojs/http-streaming/src/segment-loader.js
generated
vendored
|
@ -24,6 +24,7 @@ import shallowEqual from './util/shallow-equal.js';
|
|||
import { QUOTA_EXCEEDED_ERR } from './error-codes';
|
||||
import {timeRangesToArray, lastBufferedEnd, timeAheadOf} from './ranges.js';
|
||||
import {getKnownPartCount} from './playlist.js';
|
||||
import {createTimeRanges} from './util/vjs-compat';
|
||||
|
||||
/**
|
||||
* The segment loader has no recourse except to fetch a segment in the
|
||||
|
@ -561,7 +562,7 @@ export default class SegmentLoader extends videojs.EventTarget {
|
|||
this.parse708captions_ = settings.parse708captions;
|
||||
this.useDtsForTimestampOffset_ = settings.useDtsForTimestampOffset;
|
||||
this.captionServices_ = settings.captionServices;
|
||||
this.experimentalExactManifestTimings = settings.experimentalExactManifestTimings;
|
||||
this.exactManifestTimings = settings.exactManifestTimings;
|
||||
|
||||
// private instance variables
|
||||
this.checkBufferTimeout_ = null;
|
||||
|
@ -838,7 +839,7 @@ export default class SegmentLoader extends videojs.EventTarget {
|
|||
const trackInfo = this.getMediaInfo_();
|
||||
|
||||
if (!this.sourceUpdater_ || !trackInfo) {
|
||||
return videojs.createTimeRanges();
|
||||
return createTimeRanges();
|
||||
}
|
||||
|
||||
if (this.loaderType_ === 'main') {
|
||||
|
@ -1162,6 +1163,7 @@ export default class SegmentLoader extends videojs.EventTarget {
|
|||
*/
|
||||
resetEverything(done) {
|
||||
this.ended_ = false;
|
||||
this.activeInitSegmentId_ = null;
|
||||
this.appendInitSegment_ = {
|
||||
audio: true,
|
||||
video: true
|
||||
|
@ -1445,7 +1447,7 @@ export default class SegmentLoader extends videojs.EventTarget {
|
|||
} else {
|
||||
// Find the segment containing the end of the buffer or current time.
|
||||
const {segmentIndex, startTime, partIndex} = Playlist.getMediaInfoForTime({
|
||||
experimentalExactManifestTimings: this.experimentalExactManifestTimings,
|
||||
exactManifestTimings: this.exactManifestTimings,
|
||||
playlist: this.playlist_,
|
||||
currentTime: this.fetchAtBuffer_ ? bufferedEnd : this.currentTime_(),
|
||||
startingPartIndex: this.syncPoint_.partIndex,
|
||||
|
@ -1653,7 +1655,7 @@ export default class SegmentLoader extends videojs.EventTarget {
|
|||
}
|
||||
|
||||
const switchCandidate = minRebufferMaxBandwidthSelector({
|
||||
master: this.vhs_.playlists.master,
|
||||
main: this.vhs_.playlists.main,
|
||||
currentTime,
|
||||
bandwidth: measuredBandwidth,
|
||||
duration: this.duration_(),
|
||||
|
@ -1982,6 +1984,10 @@ export default class SegmentLoader extends videojs.EventTarget {
|
|||
return this.getCurrentMediaInfo_(segmentInfo) || this.startingMediaInfo_;
|
||||
}
|
||||
|
||||
getPendingSegmentPlaylist() {
|
||||
return this.pendingSegment_ ? this.pendingSegment_.playlist : null;
|
||||
}
|
||||
|
||||
hasEnoughInfoToAppend_() {
|
||||
if (!this.sourceUpdater_.ready()) {
|
||||
return false;
|
||||
|
@ -2690,7 +2696,7 @@ export default class SegmentLoader extends videojs.EventTarget {
|
|||
}
|
||||
|
||||
// if control-flow has arrived here, then the error is real
|
||||
// emit an error event to blacklist the current playlist
|
||||
// emit an error event to exclude the current playlist
|
||||
this.mediaRequestsErrored += 1;
|
||||
this.error(error);
|
||||
this.trigger('error');
|
||||
|
@ -2799,7 +2805,7 @@ export default class SegmentLoader extends videojs.EventTarget {
|
|||
if (!trackInfo) {
|
||||
this.error({
|
||||
message: 'No starting media returned, likely due to an unsupported media format.',
|
||||
blacklistDuration: Infinity
|
||||
playlistExclusionDuration: Infinity
|
||||
});
|
||||
this.trigger('error');
|
||||
return;
|
||||
|
@ -2880,7 +2886,7 @@ export default class SegmentLoader extends videojs.EventTarget {
|
|||
if (illegalMediaSwitchError) {
|
||||
this.error({
|
||||
message: illegalMediaSwitchError,
|
||||
blacklistDuration: Infinity
|
||||
playlistExclusionDuration: Infinity
|
||||
});
|
||||
this.trigger('error');
|
||||
return true;
|
||||
|
|
9
node_modules/@videojs/http-streaming/src/source-updater.js
generated
vendored
9
node_modules/@videojs/http-streaming/src/source-updater.js
generated
vendored
|
@ -9,6 +9,7 @@ import {getMimeForCodec} from '@videojs/vhs-utils/es/codecs.js';
|
|||
import window from 'global/window';
|
||||
import toTitleCase from './util/to-title-case.js';
|
||||
import { QUOTA_EXCEEDED_ERR } from './error-codes';
|
||||
import {createTimeRanges} from './util/vjs-compat';
|
||||
|
||||
const bufferTypes = [
|
||||
'video',
|
||||
|
@ -605,11 +606,11 @@ export default class SourceUpdater extends videojs.EventTarget {
|
|||
// no media source/source buffer or it isn't in the media sources
|
||||
// source buffer list
|
||||
if (!inSourceBuffers(this.mediaSource, this.audioBuffer)) {
|
||||
return videojs.createTimeRange();
|
||||
return createTimeRanges();
|
||||
}
|
||||
|
||||
return this.audioBuffer.buffered ? this.audioBuffer.buffered :
|
||||
videojs.createTimeRange();
|
||||
createTimeRanges();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -622,10 +623,10 @@ export default class SourceUpdater extends videojs.EventTarget {
|
|||
// no media source/source buffer or it isn't in the media sources
|
||||
// source buffer list
|
||||
if (!inSourceBuffers(this.mediaSource, this.videoBuffer)) {
|
||||
return videojs.createTimeRange();
|
||||
return createTimeRanges();
|
||||
}
|
||||
return this.videoBuffer.buffered ? this.videoBuffer.buffered :
|
||||
videojs.createTimeRange();
|
||||
createTimeRanges();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
24
node_modules/@videojs/http-streaming/src/util/codecs.js
generated
vendored
24
node_modules/@videojs/http-streaming/src/util/codecs.js
generated
vendored
|
@ -29,21 +29,21 @@ const getCodecs = function(media) {
|
|||
}
|
||||
};
|
||||
|
||||
export const isMaat = (master, media) => {
|
||||
export const isMaat = (main, media) => {
|
||||
const mediaAttributes = media.attributes || {};
|
||||
|
||||
return master && master.mediaGroups && master.mediaGroups.AUDIO &&
|
||||
return main && main.mediaGroups && main.mediaGroups.AUDIO &&
|
||||
mediaAttributes.AUDIO &&
|
||||
master.mediaGroups.AUDIO[mediaAttributes.AUDIO];
|
||||
main.mediaGroups.AUDIO[mediaAttributes.AUDIO];
|
||||
};
|
||||
|
||||
export const isMuxed = (master, media) => {
|
||||
if (!isMaat(master, media)) {
|
||||
export const isMuxed = (main, media) => {
|
||||
if (!isMaat(main, media)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const mediaAttributes = media.attributes || {};
|
||||
const audioGroup = master.mediaGroups.AUDIO[mediaAttributes.AUDIO];
|
||||
const audioGroup = main.mediaGroups.AUDIO[mediaAttributes.AUDIO];
|
||||
|
||||
for (const groupId in audioGroup) {
|
||||
// If an audio group has a URI (the case for HLS, as HLS will use external playlists),
|
||||
|
@ -95,28 +95,28 @@ export const codecCount = function(codecObj) {
|
|||
|
||||
/**
|
||||
* Calculates the codec strings for a working configuration of
|
||||
* SourceBuffers to play variant streams in a master playlist. If
|
||||
* SourceBuffers to play variant streams in a main playlist. If
|
||||
* there is no possible working configuration, an empty object will be
|
||||
* returned.
|
||||
*
|
||||
* @param master {Object} the m3u8 object for the master playlist
|
||||
* @param main {Object} the m3u8 object for the main playlist
|
||||
* @param media {Object} the m3u8 object for the variant playlist
|
||||
* @return {Object} the codec strings.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
export const codecsForPlaylist = function(master, media) {
|
||||
export const codecsForPlaylist = function(main, media) {
|
||||
const mediaAttributes = media.attributes || {};
|
||||
const codecInfo = unwrapCodecList(getCodecs(media) || []);
|
||||
|
||||
// HLS with multiple-audio tracks must always get an audio codec.
|
||||
// Put another way, there is no way to have a video-only multiple-audio HLS!
|
||||
if (isMaat(master, media) && !codecInfo.audio) {
|
||||
if (!isMuxed(master, media)) {
|
||||
if (isMaat(main, media) && !codecInfo.audio) {
|
||||
if (!isMuxed(main, media)) {
|
||||
// It is possible for codecs to be specified on the audio media group playlist but
|
||||
// not on the rendition playlist. This is mostly the case for DASH, where audio and
|
||||
// video are always separate (and separately specified).
|
||||
const defaultCodecs = unwrapCodecList(codecsFromDefault(master, mediaAttributes.AUDIO) || []);
|
||||
const defaultCodecs = unwrapCodecList(codecsFromDefault(main, mediaAttributes.AUDIO) || []);
|
||||
|
||||
if (defaultCodecs.audio) {
|
||||
codecInfo.audio = defaultCodecs.audio;
|
||||
|
|
1
node_modules/@videojs/http-streaming/src/util/text-tracks.js
generated
vendored
1
node_modules/@videojs/http-streaming/src/util/text-tracks.js
generated
vendored
|
@ -15,7 +15,6 @@ import videojs from 'video.js';
|
|||
export const createCaptionsTrackIfNotExists = function(inbandTextTracks, tech, captionStream) {
|
||||
if (!inbandTextTracks[captionStream]) {
|
||||
tech.trigger({type: 'usage', name: 'vhs-608'});
|
||||
tech.trigger({type: 'usage', name: 'hls-608'});
|
||||
|
||||
let instreamId = captionStream;
|
||||
|
||||
|
|
286
node_modules/@videojs/http-streaming/src/videojs-http-streaming.js
generated
vendored
286
node_modules/@videojs/http-streaming/src/videojs-http-streaming.js
generated
vendored
|
@ -1,8 +1,8 @@
|
|||
/**
|
||||
* @file videojs-http-streaming.js
|
||||
*
|
||||
* The main file for the HLS project.
|
||||
* License: https://github.com/videojs/videojs-http-streaming/blob/master/LICENSE
|
||||
* The main file for the VHS project.
|
||||
* License: https://github.com/videojs/videojs-http-streaming/blob/main/LICENSE
|
||||
*/
|
||||
import document from 'global/document';
|
||||
import window from 'global/window';
|
||||
|
@ -17,7 +17,7 @@ import {
|
|||
} from './util/time';
|
||||
import { timeRangesToArray } from './ranges';
|
||||
import videojs from 'video.js';
|
||||
import { MasterPlaylistController } from './master-playlist-controller';
|
||||
import { PlaylistController } from './playlist-controller';
|
||||
import Config from './config';
|
||||
import renditionSelectionMixin from './rendition-mixin';
|
||||
import PlaybackWatcher from './playback-watcher';
|
||||
|
@ -38,6 +38,7 @@ import {
|
|||
import { unwrapCodecList } from './util/codecs.js';
|
||||
import logger from './util/logger';
|
||||
import {SAFE_TIME_DELTA} from './ranges';
|
||||
import {merge} from './util/vjs-compat';
|
||||
|
||||
// IMPORTANT:
|
||||
// keep these at the bottom they are replaced at build time
|
||||
|
@ -125,10 +126,10 @@ const handleVhsLoadedMetadata = function(qualityLevels, vhs) {
|
|||
handleVhsMediaChange(qualityLevels, vhs.playlists);
|
||||
};
|
||||
|
||||
// HLS is a source handler, not a tech. Make sure attempts to use it
|
||||
// VHS is a source handler, not a tech. Make sure attempts to use it
|
||||
// as one do not cause exceptions.
|
||||
Vhs.canPlaySource = function() {
|
||||
return videojs.log.warn('HLS is no longer a tech. Please remove it from ' +
|
||||
return videojs.log.warn('VHS is no longer a tech. Please remove it from ' +
|
||||
'your player\'s techOrder.');
|
||||
};
|
||||
|
||||
|
@ -183,7 +184,7 @@ const emeKeySystems = (keySystemOptions, mainPlaylist, audioPlaylist) => {
|
|||
}
|
||||
}
|
||||
|
||||
return videojs.mergeOptions(keySystemOptions, keySystemContentTypes);
|
||||
return merge(keySystemOptions, keySystemContentTypes);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -252,7 +253,7 @@ const getAllPsshKeySystemsOptions = (playlists, keySystems) => {
|
|||
* @param {Object} [audioMedia]
|
||||
* The active audio media playlist (optional)
|
||||
* @param {Object[]} mainPlaylists
|
||||
* The playlists found on the master playlist object
|
||||
* The playlists found on the main playlist object
|
||||
*
|
||||
* @return {Object}
|
||||
* Promise that resolves when the key session has been created
|
||||
|
@ -391,7 +392,7 @@ const updateVhsLocalStorage = (options) => {
|
|||
|
||||
let objectToStore = getVhsLocalStorage();
|
||||
|
||||
objectToStore = objectToStore ? videojs.mergeOptions(objectToStore, options) : options;
|
||||
objectToStore = objectToStore ? merge(objectToStore, options) : options;
|
||||
|
||||
try {
|
||||
window.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(objectToStore));
|
||||
|
@ -483,11 +484,11 @@ Vhs.supportsTypeNatively = (type) => {
|
|||
};
|
||||
|
||||
/**
|
||||
* HLS is a source handler, not a tech. Make sure attempts to use it
|
||||
* VHS is a source handler, not a tech. Make sure attempts to use it
|
||||
* as one do not cause exceptions.
|
||||
*/
|
||||
Vhs.isSupported = function() {
|
||||
return videojs.log.warn('HLS is no longer a tech. Please remove it from ' +
|
||||
return videojs.log.warn('VHS is no longer a tech. Please remove it from ' +
|
||||
'your player\'s techOrder.');
|
||||
};
|
||||
|
||||
|
@ -495,7 +496,7 @@ const Component = videojs.getComponent('Component');
|
|||
|
||||
/**
|
||||
* The Vhs Handler object, where we orchestrate all of the parts
|
||||
* of HLS to interact with video.js
|
||||
* of VHS to interact with video.js
|
||||
*
|
||||
* @class VhsHandler
|
||||
* @extends videojs.Component
|
||||
|
@ -505,11 +506,7 @@ const Component = videojs.getComponent('Component');
|
|||
*/
|
||||
class VhsHandler extends Component {
|
||||
constructor(source, tech, options) {
|
||||
super(tech, videojs.mergeOptions(options.hls, options.vhs));
|
||||
|
||||
if (options.hls && Object.keys(options.hls).length) {
|
||||
videojs.log.warn('Using hls options is deprecated. Please rename `hls` to `vhs` in your options object.');
|
||||
}
|
||||
super(tech, options.vhs);
|
||||
|
||||
// if a tech level `initialBandwidth` option was passed
|
||||
// use that over the VHS level `bandwidth` option
|
||||
|
@ -519,42 +516,10 @@ class VhsHandler extends Component {
|
|||
|
||||
this.logger_ = logger('VhsHandler');
|
||||
|
||||
// tech.player() is deprecated but setup a reference to HLS for
|
||||
// backwards-compatibility
|
||||
// we need access to the player in some cases,
|
||||
// so, get it from Video.js via the `playerId`
|
||||
if (tech.options_ && tech.options_.playerId) {
|
||||
const _player = videojs(tech.options_.playerId);
|
||||
|
||||
if (!_player.hasOwnProperty('hls')) {
|
||||
Object.defineProperty(_player, 'hls', {
|
||||
get: () => {
|
||||
videojs.log.warn('player.hls is deprecated. Use player.tech().vhs instead.');
|
||||
tech.trigger({ type: 'usage', name: 'hls-player-access' });
|
||||
return this;
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
|
||||
if (!_player.hasOwnProperty('vhs')) {
|
||||
Object.defineProperty(_player, 'vhs', {
|
||||
get: () => {
|
||||
videojs.log.warn('player.vhs is deprecated. Use player.tech().vhs instead.');
|
||||
tech.trigger({ type: 'usage', name: 'vhs-player-access' });
|
||||
return this;
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
|
||||
if (!_player.hasOwnProperty('dash')) {
|
||||
Object.defineProperty(_player, 'dash', {
|
||||
get: () => {
|
||||
videojs.log.warn('player.dash is deprecated. Use player.tech().vhs instead.');
|
||||
return this;
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
const _player = videojs.getPlayer(tech.options_.playerId);
|
||||
|
||||
this.player_ = _player;
|
||||
}
|
||||
|
@ -572,9 +537,9 @@ class VhsHandler extends Component {
|
|||
tech.overrideNativeVideoTracks(true);
|
||||
} else if (this.options_.overrideNative &&
|
||||
(tech.featuresNativeVideoTracks || tech.featuresNativeAudioTracks)) {
|
||||
// overriding native HLS only works if audio tracks have been emulated
|
||||
// overriding native VHS only works if audio tracks have been emulated
|
||||
// error early if we're misconfigured
|
||||
throw new Error('Overriding native HLS requires emulated tracks. ' +
|
||||
throw new Error('Overriding native VHS requires emulated tracks. ' +
|
||||
'See https://git.io/vMpjB');
|
||||
}
|
||||
|
||||
|
@ -590,12 +555,12 @@ class VhsHandler extends Component {
|
|||
document.msFullscreenElement;
|
||||
|
||||
if (fullscreenElement && fullscreenElement.contains(this.tech_.el())) {
|
||||
this.masterPlaylistController_.fastQualityChange_();
|
||||
this.playlistController_.fastQualityChange_();
|
||||
} else {
|
||||
// When leaving fullscreen, since the in page pixel dimensions should be smaller
|
||||
// than full screen, see if there should be a rendition switch down to preserve
|
||||
// bandwidth.
|
||||
this.masterPlaylistController_.checkABR_();
|
||||
this.playlistController_.checkABR_();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -610,9 +575,9 @@ class VhsHandler extends Component {
|
|||
|
||||
this.on(this.tech_, 'error', function() {
|
||||
// verify that the error was real and we are loaded
|
||||
// enough to have mpc loaded.
|
||||
if (this.tech_.error() && this.masterPlaylistController_) {
|
||||
this.masterPlaylistController_.pauseLoading();
|
||||
// enough to have pc loaded.
|
||||
if (this.tech_.error() && this.playlistController_) {
|
||||
this.playlistController_.pauseLoading();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -622,10 +587,8 @@ class VhsHandler extends Component {
|
|||
setOptions_() {
|
||||
// defaults
|
||||
this.options_.withCredentials = this.options_.withCredentials || false;
|
||||
this.options_.handleManifestRedirects = this.options_.handleManifestRedirects === false ? false : true;
|
||||
this.options_.limitRenditionByPlayerDimensions = this.options_.limitRenditionByPlayerDimensions === false ? false : true;
|
||||
this.options_.useDevicePixelRatio = this.options_.useDevicePixelRatio || false;
|
||||
this.options_.smoothQualityChange = this.options_.smoothQualityChange || false;
|
||||
this.options_.useBandwidthFromLocalStorage =
|
||||
typeof this.source_.useBandwidthFromLocalStorage !== 'undefined' ?
|
||||
this.source_.useBandwidthFromLocalStorage :
|
||||
|
@ -635,9 +598,11 @@ class VhsHandler extends Component {
|
|||
this.options_.customTagParsers = this.options_.customTagParsers || [];
|
||||
this.options_.customTagMappers = this.options_.customTagMappers || [];
|
||||
this.options_.cacheEncryptionKeys = this.options_.cacheEncryptionKeys || false;
|
||||
this.options_.llhls = this.options_.llhls === false ? false : true;
|
||||
this.options_.bufferBasedABR = this.options_.bufferBasedABR || false;
|
||||
|
||||
if (typeof this.options_.blacklistDuration !== 'number') {
|
||||
this.options_.blacklistDuration = 5 * 60;
|
||||
if (typeof this.options_.playlistExclusionDuration !== 'number') {
|
||||
this.options_.playlistExclusionDuration = 5 * 60;
|
||||
}
|
||||
|
||||
if (typeof this.options_.bandwidth !== 'number') {
|
||||
|
@ -647,12 +612,10 @@ class VhsHandler extends Component {
|
|||
if (storedObject && storedObject.bandwidth) {
|
||||
this.options_.bandwidth = storedObject.bandwidth;
|
||||
this.tech_.trigger({type: 'usage', name: 'vhs-bandwidth-from-local-storage'});
|
||||
this.tech_.trigger({type: 'usage', name: 'hls-bandwidth-from-local-storage'});
|
||||
}
|
||||
if (storedObject && storedObject.throughput) {
|
||||
this.options_.throughput = storedObject.throughput;
|
||||
this.tech_.trigger({type: 'usage', name: 'vhs-throughput-from-local-storage'});
|
||||
this.tech_.trigger({type: 'usage', name: 'hls-throughput-from-local-storage'});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -674,20 +637,18 @@ class VhsHandler extends Component {
|
|||
'useDevicePixelRatio',
|
||||
'limitRenditionByPlayerDimensions',
|
||||
'bandwidth',
|
||||
'smoothQualityChange',
|
||||
'customTagParsers',
|
||||
'customTagMappers',
|
||||
'handleManifestRedirects',
|
||||
'cacheEncryptionKeys',
|
||||
'playlistSelector',
|
||||
'initialPlaylistSelector',
|
||||
'experimentalBufferBasedABR',
|
||||
'bufferBasedABR',
|
||||
'liveRangeSafeTimeDelta',
|
||||
'experimentalLLHLS',
|
||||
'llhls',
|
||||
'useNetworkInformationApi',
|
||||
'useDtsForTimestampOffset',
|
||||
'experimentalExactManifestTimings',
|
||||
'experimentalLeastPixelDiffSelector'
|
||||
'exactManifestTimings',
|
||||
'leastPixelDiffSelector'
|
||||
].forEach((option) => {
|
||||
if (typeof this.source_[option] !== 'undefined') {
|
||||
this.options_[option] = this.source_[option];
|
||||
|
@ -708,7 +669,7 @@ class VhsHandler extends Component {
|
|||
return;
|
||||
}
|
||||
this.setOptions_();
|
||||
// add master playlist controller options
|
||||
// add main playlist controller options
|
||||
this.options_.src = expandDataUri(this.source_.src);
|
||||
this.options_.tech = this.tech_;
|
||||
this.options_.externVhs = Vhs;
|
||||
|
@ -719,29 +680,25 @@ class VhsHandler extends Component {
|
|||
this.tech_.setCurrentTime(time);
|
||||
};
|
||||
|
||||
if (this.options_.smoothQualityChange) {
|
||||
videojs.log.warn('smoothQualityChange is deprecated and will be removed in the next major version');
|
||||
}
|
||||
this.playlistController_ = new PlaylistController(this.options_);
|
||||
|
||||
this.masterPlaylistController_ = new MasterPlaylistController(this.options_);
|
||||
|
||||
const playbackWatcherOptions = videojs.mergeOptions(
|
||||
const playbackWatcherOptions = merge(
|
||||
{
|
||||
liveRangeSafeTimeDelta: SAFE_TIME_DELTA
|
||||
},
|
||||
this.options_,
|
||||
{
|
||||
seekable: () => this.seekable(),
|
||||
media: () => this.masterPlaylistController_.media(),
|
||||
masterPlaylistController: this.masterPlaylistController_
|
||||
media: () => this.playlistController_.media(),
|
||||
playlistController: this.playlistController_
|
||||
}
|
||||
);
|
||||
|
||||
this.playbackWatcher_ = new PlaybackWatcher(playbackWatcherOptions);
|
||||
|
||||
this.masterPlaylistController_.on('error', () => {
|
||||
this.playlistController_.on('error', () => {
|
||||
const player = videojs.players[this.tech_.options_.playerId];
|
||||
let error = this.masterPlaylistController_.error;
|
||||
let error = this.playlistController_.error;
|
||||
|
||||
if (typeof error === 'object' && !error.code) {
|
||||
error.code = 3;
|
||||
|
@ -752,48 +709,48 @@ class VhsHandler extends Component {
|
|||
player.error(error);
|
||||
});
|
||||
|
||||
const defaultSelector = this.options_.experimentalBufferBasedABR ?
|
||||
const defaultSelector = this.options_.bufferBasedABR ?
|
||||
Vhs.movingAverageBandwidthSelector(0.55) : Vhs.STANDARD_PLAYLIST_SELECTOR;
|
||||
|
||||
// `this` in selectPlaylist should be the VhsHandler for backwards
|
||||
// compatibility with < v2
|
||||
this.masterPlaylistController_.selectPlaylist = this.selectPlaylist ?
|
||||
this.playlistController_.selectPlaylist = this.selectPlaylist ?
|
||||
this.selectPlaylist.bind(this) :
|
||||
defaultSelector.bind(this);
|
||||
|
||||
this.masterPlaylistController_.selectInitialPlaylist =
|
||||
this.playlistController_.selectInitialPlaylist =
|
||||
Vhs.INITIAL_PLAYLIST_SELECTOR.bind(this);
|
||||
|
||||
// re-expose some internal objects for backwards compatibility with < v2
|
||||
this.playlists = this.masterPlaylistController_.masterPlaylistLoader_;
|
||||
this.mediaSource = this.masterPlaylistController_.mediaSource;
|
||||
this.playlists = this.playlistController_.mainPlaylistLoader_;
|
||||
this.mediaSource = this.playlistController_.mediaSource;
|
||||
|
||||
// Proxy assignment of some properties to the master playlist
|
||||
// Proxy assignment of some properties to the main playlist
|
||||
// controller. Using a custom property for backwards compatibility
|
||||
// with < v2
|
||||
Object.defineProperties(this, {
|
||||
selectPlaylist: {
|
||||
get() {
|
||||
return this.masterPlaylistController_.selectPlaylist;
|
||||
return this.playlistController_.selectPlaylist;
|
||||
},
|
||||
set(selectPlaylist) {
|
||||
this.masterPlaylistController_.selectPlaylist = selectPlaylist.bind(this);
|
||||
this.playlistController_.selectPlaylist = selectPlaylist.bind(this);
|
||||
}
|
||||
},
|
||||
throughput: {
|
||||
get() {
|
||||
return this.masterPlaylistController_.mainSegmentLoader_.throughput.rate;
|
||||
return this.playlistController_.mainSegmentLoader_.throughput.rate;
|
||||
},
|
||||
set(throughput) {
|
||||
this.masterPlaylistController_.mainSegmentLoader_.throughput.rate = throughput;
|
||||
this.playlistController_.mainSegmentLoader_.throughput.rate = throughput;
|
||||
// By setting `count` to 1 the throughput value becomes the starting value
|
||||
// for the cumulative average
|
||||
this.masterPlaylistController_.mainSegmentLoader_.throughput.count = 1;
|
||||
this.playlistController_.mainSegmentLoader_.throughput.count = 1;
|
||||
}
|
||||
},
|
||||
bandwidth: {
|
||||
get() {
|
||||
let playerBandwidthEst = this.masterPlaylistController_.mainSegmentLoader_.bandwidth;
|
||||
let playerBandwidthEst = this.playlistController_.mainSegmentLoader_.bandwidth;
|
||||
|
||||
const networkInformation = window.navigator.connection || window.navigator.mozConnection || window.navigator.webkitConnection;
|
||||
const tenMbpsAsBitsPerSecond = 10e6;
|
||||
|
@ -816,11 +773,11 @@ class VhsHandler extends Component {
|
|||
return playerBandwidthEst;
|
||||
},
|
||||
set(bandwidth) {
|
||||
this.masterPlaylistController_.mainSegmentLoader_.bandwidth = bandwidth;
|
||||
this.playlistController_.mainSegmentLoader_.bandwidth = bandwidth;
|
||||
// setting the bandwidth manually resets the throughput counter
|
||||
// `count` is set to zero that current value of `rate` isn't included
|
||||
// in the cumulative average
|
||||
this.masterPlaylistController_.mainSegmentLoader_.throughput = {
|
||||
this.playlistController_.mainSegmentLoader_.throughput = {
|
||||
rate: 0,
|
||||
count: 0
|
||||
};
|
||||
|
@ -869,51 +826,51 @@ class VhsHandler extends Component {
|
|||
enumerable: true
|
||||
},
|
||||
mediaRequests: {
|
||||
get: () => this.masterPlaylistController_.mediaRequests_() || 0,
|
||||
get: () => this.playlistController_.mediaRequests_() || 0,
|
||||
enumerable: true
|
||||
},
|
||||
mediaRequestsAborted: {
|
||||
get: () => this.masterPlaylistController_.mediaRequestsAborted_() || 0,
|
||||
get: () => this.playlistController_.mediaRequestsAborted_() || 0,
|
||||
enumerable: true
|
||||
},
|
||||
mediaRequestsTimedout: {
|
||||
get: () => this.masterPlaylistController_.mediaRequestsTimedout_() || 0,
|
||||
get: () => this.playlistController_.mediaRequestsTimedout_() || 0,
|
||||
enumerable: true
|
||||
},
|
||||
mediaRequestsErrored: {
|
||||
get: () => this.masterPlaylistController_.mediaRequestsErrored_() || 0,
|
||||
get: () => this.playlistController_.mediaRequestsErrored_() || 0,
|
||||
enumerable: true
|
||||
},
|
||||
mediaTransferDuration: {
|
||||
get: () => this.masterPlaylistController_.mediaTransferDuration_() || 0,
|
||||
get: () => this.playlistController_.mediaTransferDuration_() || 0,
|
||||
enumerable: true
|
||||
},
|
||||
mediaBytesTransferred: {
|
||||
get: () => this.masterPlaylistController_.mediaBytesTransferred_() || 0,
|
||||
get: () => this.playlistController_.mediaBytesTransferred_() || 0,
|
||||
enumerable: true
|
||||
},
|
||||
mediaSecondsLoaded: {
|
||||
get: () => this.masterPlaylistController_.mediaSecondsLoaded_() || 0,
|
||||
get: () => this.playlistController_.mediaSecondsLoaded_() || 0,
|
||||
enumerable: true
|
||||
},
|
||||
mediaAppends: {
|
||||
get: () => this.masterPlaylistController_.mediaAppends_() || 0,
|
||||
get: () => this.playlistController_.mediaAppends_() || 0,
|
||||
enumerable: true
|
||||
},
|
||||
mainAppendsToLoadedData: {
|
||||
get: () => this.masterPlaylistController_.mainAppendsToLoadedData_() || 0,
|
||||
get: () => this.playlistController_.mainAppendsToLoadedData_() || 0,
|
||||
enumerable: true
|
||||
},
|
||||
audioAppendsToLoadedData: {
|
||||
get: () => this.masterPlaylistController_.audioAppendsToLoadedData_() || 0,
|
||||
get: () => this.playlistController_.audioAppendsToLoadedData_() || 0,
|
||||
enumerable: true
|
||||
},
|
||||
appendsToLoadedData: {
|
||||
get: () => this.masterPlaylistController_.appendsToLoadedData_() || 0,
|
||||
get: () => this.playlistController_.appendsToLoadedData_() || 0,
|
||||
enumerable: true
|
||||
},
|
||||
timeToLoadedData: {
|
||||
get: () => this.masterPlaylistController_.timeToLoadedData_() || 0,
|
||||
get: () => this.playlistController_.timeToLoadedData_() || 0,
|
||||
enumerable: true
|
||||
},
|
||||
buffered: {
|
||||
|
@ -936,8 +893,8 @@ class VhsHandler extends Component {
|
|||
get: () => this.tech_.duration(),
|
||||
enumerable: true
|
||||
},
|
||||
master: {
|
||||
get: () => this.playlists.master,
|
||||
main: {
|
||||
get: () => this.playlists.main,
|
||||
enumerable: true
|
||||
},
|
||||
playerDimensions: {
|
||||
|
@ -960,7 +917,7 @@ class VhsHandler extends Component {
|
|||
|
||||
this.tech_.one(
|
||||
'canplay',
|
||||
this.masterPlaylistController_.setupFirstPlay.bind(this.masterPlaylistController_)
|
||||
this.playlistController_.setupFirstPlay.bind(this.playlistController_)
|
||||
);
|
||||
|
||||
this.tech_.on('bandwidthupdate', () => {
|
||||
|
@ -972,24 +929,24 @@ class VhsHandler extends Component {
|
|||
}
|
||||
});
|
||||
|
||||
this.masterPlaylistController_.on('selectedinitialmedia', () => {
|
||||
this.playlistController_.on('selectedinitialmedia', () => {
|
||||
// Add the manual rendition mix-in to VhsHandler
|
||||
renditionSelectionMixin(this);
|
||||
});
|
||||
|
||||
this.masterPlaylistController_.sourceUpdater_.on('createdsourcebuffers', () => {
|
||||
this.playlistController_.sourceUpdater_.on('createdsourcebuffers', () => {
|
||||
this.setupEme_();
|
||||
});
|
||||
|
||||
// the bandwidth of the primary segment loader is our best
|
||||
// estimate of overall bandwidth
|
||||
this.on(this.masterPlaylistController_, 'progress', function() {
|
||||
this.on(this.playlistController_, 'progress', function() {
|
||||
this.tech_.trigger('progress');
|
||||
});
|
||||
|
||||
// In the live case, we need to ignore the very first `seeking` event since
|
||||
// that will be the result of the seek-to-live behavior
|
||||
this.on(this.masterPlaylistController_, 'firstplay', function() {
|
||||
this.on(this.playlistController_, 'firstplay', function() {
|
||||
this.ignoreNextSeekingEvent_ = true;
|
||||
});
|
||||
|
||||
|
@ -1001,24 +958,24 @@ class VhsHandler extends Component {
|
|||
return;
|
||||
}
|
||||
|
||||
this.mediaSourceUrl_ = window.URL.createObjectURL(this.masterPlaylistController_.mediaSource);
|
||||
this.mediaSourceUrl_ = window.URL.createObjectURL(this.playlistController_.mediaSource);
|
||||
|
||||
this.tech_.src(this.mediaSourceUrl_);
|
||||
}
|
||||
|
||||
createKeySessions_() {
|
||||
const audioPlaylistLoader =
|
||||
this.masterPlaylistController_.mediaTypes_.AUDIO.activePlaylistLoader;
|
||||
this.playlistController_.mediaTypes_.AUDIO.activePlaylistLoader;
|
||||
|
||||
this.logger_('waiting for EME key session creation');
|
||||
waitForKeySessionCreation({
|
||||
player: this.player_,
|
||||
sourceKeySystems: this.source_.keySystems,
|
||||
audioMedia: audioPlaylistLoader && audioPlaylistLoader.media(),
|
||||
mainPlaylists: this.playlists.master.playlists
|
||||
mainPlaylists: this.playlists.main.playlists
|
||||
}).then(() => {
|
||||
this.logger_('created EME key session');
|
||||
this.masterPlaylistController_.sourceUpdater_.initializedEme();
|
||||
this.playlistController_.sourceUpdater_.initializedEme();
|
||||
}).catch((err) => {
|
||||
this.logger_('error while creating EME key session', err);
|
||||
this.player_.error({
|
||||
|
@ -1051,7 +1008,7 @@ class VhsHandler extends Component {
|
|||
*/
|
||||
setupEme_() {
|
||||
const audioPlaylistLoader =
|
||||
this.masterPlaylistController_.mediaTypes_.AUDIO.activePlaylistLoader;
|
||||
this.playlistController_.mediaTypes_.AUDIO.activePlaylistLoader;
|
||||
|
||||
const didSetupEmeOptions = setupEmeOptions({
|
||||
player: this.player_,
|
||||
|
@ -1065,16 +1022,16 @@ class VhsHandler extends Component {
|
|||
return;
|
||||
}
|
||||
|
||||
const masterPlaylist = this.masterPlaylistController_.master();
|
||||
const mainPlaylist = this.playlistController_.main();
|
||||
|
||||
if (!masterPlaylist || !masterPlaylist.playlists) {
|
||||
if (!mainPlaylist || !mainPlaylist.playlists) {
|
||||
return;
|
||||
}
|
||||
|
||||
const excludedHDPlaylists = [];
|
||||
|
||||
// Assume all HD streams are unplayable and exclude them from ABR selection
|
||||
masterPlaylist.playlists.forEach(playlist => {
|
||||
mainPlaylist.playlists.forEach(playlist => {
|
||||
if (playlist && playlist.attributes && playlist.attributes.RESOLUTION &&
|
||||
playlist.attributes.RESOLUTION.height >= 720) {
|
||||
if (!playlist.excludeUntil || playlist.excludeUntil < Infinity) {
|
||||
|
@ -1094,7 +1051,7 @@ class VhsHandler extends Component {
|
|||
);
|
||||
|
||||
// Clear the buffer before switching playlists, since it may already contain unplayable segments
|
||||
this.masterPlaylistController_.fastQualityChange_();
|
||||
this.playlistController_.fastQualityChange_();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1105,7 +1062,7 @@ class VhsHandler extends Component {
|
|||
// promises.
|
||||
if (videojs.browser.IE_VERSION === 11 || !didSetupEmeOptions) {
|
||||
// If EME options were not set up, we've done all we could to initialize EME.
|
||||
this.masterPlaylistController_.sourceUpdater_.initializedEme();
|
||||
this.playlistController_.sourceUpdater_.initializedEme();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1129,7 +1086,7 @@ class VhsHandler extends Component {
|
|||
|
||||
this.qualityLevels_ = player.qualityLevels();
|
||||
|
||||
this.masterPlaylistController_.on('selectedinitialmedia', () => {
|
||||
this.playlistController_.on('selectedinitialmedia', () => {
|
||||
handleVhsLoadedMetadata(this.qualityLevels_, this);
|
||||
});
|
||||
|
||||
|
@ -1166,28 +1123,28 @@ class VhsHandler extends Component {
|
|||
* Begin playing the video.
|
||||
*/
|
||||
play() {
|
||||
this.masterPlaylistController_.play();
|
||||
this.playlistController_.play();
|
||||
}
|
||||
|
||||
/**
|
||||
* a wrapper around the function in MasterPlaylistController
|
||||
* a wrapper around the function in PlaylistController
|
||||
*/
|
||||
setCurrentTime(currentTime) {
|
||||
this.masterPlaylistController_.setCurrentTime(currentTime);
|
||||
this.playlistController_.setCurrentTime(currentTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* a wrapper around the function in MasterPlaylistController
|
||||
* a wrapper around the function in PlaylistController
|
||||
*/
|
||||
duration() {
|
||||
return this.masterPlaylistController_.duration();
|
||||
return this.playlistController_.duration();
|
||||
}
|
||||
|
||||
/**
|
||||
* a wrapper around the function in MasterPlaylistController
|
||||
* a wrapper around the function in PlaylistController
|
||||
*/
|
||||
seekable() {
|
||||
return this.masterPlaylistController_.seekable();
|
||||
return this.playlistController_.seekable();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1197,28 +1154,17 @@ class VhsHandler extends Component {
|
|||
if (this.playbackWatcher_) {
|
||||
this.playbackWatcher_.dispose();
|
||||
}
|
||||
if (this.masterPlaylistController_) {
|
||||
this.masterPlaylistController_.dispose();
|
||||
if (this.playlistController_) {
|
||||
this.playlistController_.dispose();
|
||||
}
|
||||
if (this.qualityLevels_) {
|
||||
this.qualityLevels_.dispose();
|
||||
}
|
||||
|
||||
if (this.player_) {
|
||||
delete this.player_.vhs;
|
||||
delete this.player_.dash;
|
||||
delete this.player_.hls;
|
||||
}
|
||||
|
||||
if (this.tech_ && this.tech_.vhs) {
|
||||
delete this.tech_.vhs;
|
||||
}
|
||||
|
||||
// don't check this.tech_.hls as it will log a deprecated warning
|
||||
if (this.tech_) {
|
||||
delete this.tech_.hls;
|
||||
}
|
||||
|
||||
if (this.mediaSourceUrl_ && window.URL.revokeObjectURL) {
|
||||
window.URL.revokeObjectURL(this.mediaSourceUrl_);
|
||||
this.mediaSourceUrl_ = null;
|
||||
|
@ -1233,7 +1179,7 @@ class VhsHandler extends Component {
|
|||
|
||||
convertToProgramTime(time, callback) {
|
||||
return getProgramTime({
|
||||
playlist: this.masterPlaylistController_.media(),
|
||||
playlist: this.playlistController_.media(),
|
||||
time,
|
||||
callback
|
||||
});
|
||||
|
@ -1243,7 +1189,7 @@ class VhsHandler extends Component {
|
|||
seekToProgramTime(programTime, callback, pauseAfterSeek = true, retryCount = 2) {
|
||||
return seekToProgramTime({
|
||||
programTime,
|
||||
playlist: this.masterPlaylistController_.media(),
|
||||
playlist: this.playlistController_.media(),
|
||||
retryCount,
|
||||
pauseAfterSeek,
|
||||
seekTo: this.options_.seekTo,
|
||||
|
@ -1264,23 +1210,14 @@ const VhsSourceHandler = {
|
|||
name: 'videojs-http-streaming',
|
||||
VERSION: vhsVersion,
|
||||
canHandleSource(srcObj, options = {}) {
|
||||
const localOptions = videojs.mergeOptions(videojs.options, options);
|
||||
const localOptions = merge(videojs.options, options);
|
||||
|
||||
return VhsSourceHandler.canPlayType(srcObj.type, localOptions);
|
||||
},
|
||||
handleSource(source, tech, options = {}) {
|
||||
const localOptions = videojs.mergeOptions(videojs.options, options);
|
||||
const localOptions = merge(videojs.options, options);
|
||||
|
||||
tech.vhs = new VhsHandler(source, tech, localOptions);
|
||||
if (!videojs.hasOwnProperty('hls')) {
|
||||
Object.defineProperty(tech, 'hls', {
|
||||
get: () => {
|
||||
videojs.log.warn('player.tech().hls is deprecated. Use player.tech().vhs instead.');
|
||||
return tech.vhs;
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
tech.vhs.xhr = xhrFactory();
|
||||
|
||||
tech.vhs.src(source.src, source.type);
|
||||
|
@ -1300,12 +1237,11 @@ const VhsSourceHandler = {
|
|||
return canUseMsePlayback ? 'maybe' : '';
|
||||
},
|
||||
getOverrideNative(options = {}) {
|
||||
const { vhs = {}, hls = {} } = options;
|
||||
const { vhs = {} } = options;
|
||||
const defaultOverrideNative = !(videojs.browser.IS_ANY_SAFARI || videojs.browser.IS_IOS);
|
||||
const { overrideNative = defaultOverrideNative } = vhs;
|
||||
const { overrideNative: legacyOverrideNative = false } = hls;
|
||||
|
||||
return legacyOverrideNative || overrideNative;
|
||||
return overrideNative;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1325,41 +1261,15 @@ if (supportsNativeMediaSources()) {
|
|||
}
|
||||
|
||||
videojs.VhsHandler = VhsHandler;
|
||||
Object.defineProperty(videojs, 'HlsHandler', {
|
||||
get: () => {
|
||||
videojs.log.warn('videojs.HlsHandler is deprecated. Use videojs.VhsHandler instead.');
|
||||
return VhsHandler;
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
videojs.VhsSourceHandler = VhsSourceHandler;
|
||||
Object.defineProperty(videojs, 'HlsSourceHandler', {
|
||||
get: () => {
|
||||
videojs.log.warn('videojs.HlsSourceHandler is deprecated. ' +
|
||||
'Use videojs.VhsSourceHandler instead.');
|
||||
return VhsSourceHandler;
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
videojs.Vhs = Vhs;
|
||||
Object.defineProperty(videojs, 'Hls', {
|
||||
get: () => {
|
||||
videojs.log.warn('videojs.Hls is deprecated. Use videojs.Vhs instead.');
|
||||
return Vhs;
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
if (!videojs.use) {
|
||||
videojs.registerComponent('Hls', Vhs);
|
||||
videojs.registerComponent('Vhs', Vhs);
|
||||
}
|
||||
videojs.options.vhs = videojs.options.vhs || {};
|
||||
videojs.options.hls = videojs.options.hls || {};
|
||||
|
||||
if (!videojs.getPlugin || !videojs.getPlugin('reloadSourceOnError')) {
|
||||
const registerPlugin = videojs.registerPlugin || videojs.plugin;
|
||||
|
||||
registerPlugin('reloadSourceOnError', reloadSourceOnError);
|
||||
videojs.registerPlugin('reloadSourceOnError', reloadSourceOnError);
|
||||
}
|
||||
|
||||
export {
|
||||
|
|
5
node_modules/@videojs/http-streaming/src/vtt-segment-loader.js
generated
vendored
5
node_modules/@videojs/http-streaming/src/vtt-segment-loader.js
generated
vendored
|
@ -9,6 +9,7 @@ import { initSegmentId } from './bin-utils';
|
|||
import { uint8ToUtf8 } from './util/string';
|
||||
import { REQUEST_ERRORS } from './media-segment-request';
|
||||
import { ONE_SECOND_IN_TS } from 'mux.js/lib/utils/clock';
|
||||
import {createTimeRanges} from './util/vjs-compat';
|
||||
|
||||
const VTT_LINE_TERMINATORS =
|
||||
new Uint8Array('\n\n'.split('').map(char => char.charCodeAt(0)));
|
||||
|
@ -60,14 +61,14 @@ export default class VTTSegmentLoader extends SegmentLoader {
|
|||
*/
|
||||
buffered_() {
|
||||
if (!this.subtitlesTrack_ || !this.subtitlesTrack_.cues || !this.subtitlesTrack_.cues.length) {
|
||||
return videojs.createTimeRanges();
|
||||
return createTimeRanges();
|
||||
}
|
||||
|
||||
const cues = this.subtitlesTrack_.cues;
|
||||
const start = cues[0].startTime;
|
||||
const end = cues[cues.length - 1].startTime;
|
||||
|
||||
return videojs.createTimeRanges([[start, end]]);
|
||||
return createTimeRanges([[start, end]]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
6
node_modules/@videojs/http-streaming/src/xhr.js
generated
vendored
6
node_modules/@videojs/http-streaming/src/xhr.js
generated
vendored
|
@ -11,10 +11,10 @@
|
|||
*/
|
||||
import videojs from 'video.js';
|
||||
import window from 'global/window';
|
||||
import {merge} from './util/vjs-compat';
|
||||
|
||||
const {
|
||||
xhr: videojsXHR,
|
||||
mergeOptions
|
||||
xhr: videojsXHR
|
||||
} = videojs;
|
||||
|
||||
const callbackWrapper = function(request, error, response, callback) {
|
||||
|
@ -59,7 +59,7 @@ const callbackWrapper = function(request, error, response, callback) {
|
|||
const xhrFactory = function() {
|
||||
const xhr = function XhrFunction(options, callback) {
|
||||
// Add a default timeout
|
||||
options = mergeOptions({
|
||||
options = merge({
|
||||
timeout: 45e3
|
||||
}, options);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue