shaka-player 4.0.0 → 4.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/config.yml +1 -0
- package/.github/workflows/release-please.yaml +6 -11
- package/AUTHORS +1 -0
- package/CHANGELOG.md +39 -0
- package/CONTRIBUTORS +1 -0
- package/README.md +4 -4
- package/build/misspellings.txt +1 -0
- package/build/test.py +1 -1
- package/demo/common/assets.js +1 -1
- package/demo/demo.less +0 -1
- package/demo/main.js +3 -3
- package/dist/controls.css +3 -3
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +3 -3
- package/dist/demo.compiled.debug.map +2 -2
- package/dist/demo.compiled.js +3 -3
- package/dist/demo.compiled.map +2 -2
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +1 -1
- package/dist/locales.js +33 -33
- package/dist/receiver.compiled.debug.js +1 -1
- package/dist/receiver.compiled.debug.map +1 -1
- package/dist/receiver.compiled.js +1 -1
- package/dist/receiver.compiled.map +1 -1
- package/dist/shaka-player.compiled.d.ts +2 -1
- package/dist/shaka-player.compiled.debug.d.ts +2 -1
- package/dist/shaka-player.compiled.debug.externs.js +4 -2
- package/dist/shaka-player.compiled.debug.js +278 -267
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +4 -2
- package/dist/shaka-player.compiled.js +815 -806
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +2 -1
- package/dist/shaka-player.ui.debug.d.ts +2 -1
- package/dist/shaka-player.ui.debug.externs.js +4 -2
- package/dist/shaka-player.ui.debug.js +305 -293
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +4 -2
- package/dist/shaka-player.ui.js +966 -959
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/upgrade.md +4 -2
- package/externs/shaka/player.js +15 -2
- package/karma.conf.js +4 -0
- package/lib/abr/simple_abr_manager.js +2 -1
- package/lib/dash/dash_parser.js +2 -2
- package/lib/hls/hls_parser.js +211 -82
- package/lib/media/drm_engine.js +19 -0
- package/lib/media/media_source_engine.js +91 -37
- package/lib/media/segment_index.js +26 -1
- package/lib/media/segment_reference.js +10 -3
- package/lib/media/streaming_engine.js +2 -3
- package/lib/offline/indexeddb/storage_mechanism.js +4 -2
- package/lib/player.js +165 -107
- package/lib/text/vtt_text_parser.js +7 -7
- package/lib/util/config_utils.js +25 -0
- package/lib/util/error.js +7 -0
- package/lib/util/manifest_parser_utils.js +1 -1
- package/lib/util/mime_utils.js +62 -6
- package/lib/util/periods.js +5 -4
- package/lib/util/player_configuration.js +14 -14
- package/lib/util/stream_utils.js +5 -2
- package/package.json +6 -3
- package/test/hls/hls_live_unit.js +79 -7
- package/test/hls/hls_parser_unit.js +102 -8
- package/test/hls/manifest_text_parser_unit.js +19 -0
- package/test/offline/indexeddb_storage_unit.js +1 -1
- package/test/offline/storage_compatibility_unit.js +1 -1
- package/test/player_external.js +7 -1
- package/test/player_integration.js +6 -10
- package/test/player_src_equals_integration.js +81 -83
- package/test/player_unit.js +98 -1
- package/test/test/boot.js +9 -0
- package/test/test/externs/filters.js +4 -0
- package/test/test/util/simple_fakes.js +31 -2
- package/test/text/vtt_text_parser_unit.js +64 -5
- package/test/util/mime_utils_unit.js +54 -0
- package/test/util/periods_unit.js +79 -0
- package/ui/less/containers.less +1 -1
- package/ui/less/general.less +1 -1
- package/ui/less/range_elements.less +1 -0
- package/ui/resolution_selection.js +1 -1
- package/.github/pull_request_template.md +0 -10
- package/docs/tutorials/gsoc-ideas-list.md +0 -3
package/lib/player.js
CHANGED
|
@@ -423,8 +423,23 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
423
423
|
*/
|
|
424
424
|
this.isTextVisible_ = false;
|
|
425
425
|
|
|
426
|
-
/**
|
|
427
|
-
|
|
426
|
+
/**
|
|
427
|
+
* For listeners scoped to the lifetime of the Player instance.
|
|
428
|
+
* @private {shaka.util.EventManager}
|
|
429
|
+
*/
|
|
430
|
+
this.globalEventManager_ = new shaka.util.EventManager();
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* For listeners scoped to the lifetime of the media element attachment.
|
|
434
|
+
* @private {shaka.util.EventManager}
|
|
435
|
+
*/
|
|
436
|
+
this.attachEventManager_ = new shaka.util.EventManager();
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* For listeners scoped to the lifetime of the loaded content.
|
|
440
|
+
* @private {shaka.util.EventManager}
|
|
441
|
+
*/
|
|
442
|
+
this.loadEventManager_ = new shaka.util.EventManager();
|
|
428
443
|
|
|
429
444
|
/** @private {shaka.net.NetworkingEngine} */
|
|
430
445
|
this.networkingEngine_ = null;
|
|
@@ -564,7 +579,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
564
579
|
|
|
565
580
|
// If the browser comes back online after being offline, then try to play
|
|
566
581
|
// again.
|
|
567
|
-
this.
|
|
582
|
+
this.globalEventManager_.listen(window, 'online', () => {
|
|
568
583
|
this.retryStreaming();
|
|
569
584
|
});
|
|
570
585
|
|
|
@@ -752,10 +767,18 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
752
767
|
});
|
|
753
768
|
await this.walker_.destroy();
|
|
754
769
|
|
|
755
|
-
// Tear-down the event
|
|
756
|
-
if (this.
|
|
757
|
-
this.
|
|
758
|
-
this.
|
|
770
|
+
// Tear-down the event managers to ensure handlers stop firing.
|
|
771
|
+
if (this.globalEventManager_) {
|
|
772
|
+
this.globalEventManager_.release();
|
|
773
|
+
this.globalEventManager_ = null;
|
|
774
|
+
}
|
|
775
|
+
if (this.attachEventManager_) {
|
|
776
|
+
this.attachEventManager_.release();
|
|
777
|
+
this.attachEventManager_ = null;
|
|
778
|
+
}
|
|
779
|
+
if (this.loadEventManager_) {
|
|
780
|
+
this.loadEventManager_.release();
|
|
781
|
+
this.loadEventManager_ = null;
|
|
759
782
|
}
|
|
760
783
|
|
|
761
784
|
this.abrManagerFactory_ = null;
|
|
@@ -1298,7 +1321,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1298
1321
|
has.mediaElement = wants.mediaElement;
|
|
1299
1322
|
|
|
1300
1323
|
const onError = (error) => this.onVideoError_(error);
|
|
1301
|
-
this.
|
|
1324
|
+
this.attachEventManager_.listen(has.mediaElement, 'error', onError);
|
|
1302
1325
|
}
|
|
1303
1326
|
|
|
1304
1327
|
this.video_ = has.mediaElement;
|
|
@@ -1325,10 +1348,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1325
1348
|
* @private
|
|
1326
1349
|
*/
|
|
1327
1350
|
onDetach_(has, wants) {
|
|
1328
|
-
// If we
|
|
1351
|
+
// If we were going from "detached" to "detached" we wouldn't have
|
|
1329
1352
|
// a media element to detach from.
|
|
1330
1353
|
if (has.mediaElement) {
|
|
1331
|
-
this.
|
|
1354
|
+
this.attachEventManager_.removeAll();
|
|
1332
1355
|
has.mediaElement = null;
|
|
1333
1356
|
}
|
|
1334
1357
|
|
|
@@ -1386,11 +1409,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1386
1409
|
// In most cases we should have a media element. The one exception would
|
|
1387
1410
|
// be if there was an error and we, by chance, did not have a media element.
|
|
1388
1411
|
if (has.mediaElement) {
|
|
1389
|
-
this.
|
|
1390
|
-
this.eventManager_.unlisten(has.mediaElement, 'playing');
|
|
1391
|
-
this.eventManager_.unlisten(has.mediaElement, 'pause');
|
|
1392
|
-
this.eventManager_.unlisten(has.mediaElement, 'ended');
|
|
1393
|
-
this.eventManager_.unlisten(has.mediaElement, 'ratechange');
|
|
1412
|
+
this.loadEventManager_.removeAll();
|
|
1394
1413
|
}
|
|
1395
1414
|
|
|
1396
1415
|
// Some observers use some playback components, shutting down the observers
|
|
@@ -1885,18 +1904,18 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1885
1904
|
this.assetUri_ = assetUri;
|
|
1886
1905
|
|
|
1887
1906
|
this.playRateController_ = new shaka.media.PlayRateController({
|
|
1888
|
-
getRate: () =>
|
|
1889
|
-
getDefaultRate: () =>
|
|
1890
|
-
setRate: (rate) => {
|
|
1891
|
-
movePlayhead: (delta) => {
|
|
1907
|
+
getRate: () => mediaElement.playbackRate,
|
|
1908
|
+
getDefaultRate: () => mediaElement.defaultPlaybackRate,
|
|
1909
|
+
setRate: (rate) => { mediaElement.playbackRate = rate; },
|
|
1910
|
+
movePlayhead: (delta) => { mediaElement.currentTime += delta; },
|
|
1892
1911
|
});
|
|
1893
1912
|
|
|
1894
1913
|
const updateStateHistory = () => this.updateStateHistory_();
|
|
1895
1914
|
const onRateChange = () => this.onRateChange_();
|
|
1896
|
-
this.
|
|
1897
|
-
this.
|
|
1898
|
-
this.
|
|
1899
|
-
this.
|
|
1915
|
+
this.loadEventManager_.listen(mediaElement, 'playing', updateStateHistory);
|
|
1916
|
+
this.loadEventManager_.listen(mediaElement, 'pause', updateStateHistory);
|
|
1917
|
+
this.loadEventManager_.listen(mediaElement, 'ended', updateStateHistory);
|
|
1918
|
+
this.loadEventManager_.listen(mediaElement, 'ratechange', onRateChange);
|
|
1900
1919
|
|
|
1901
1920
|
const abrFactory = this.config_.abrFactory;
|
|
1902
1921
|
if (!this.abrManager_ || this.abrManagerFactory_ != abrFactory) {
|
|
@@ -1953,6 +1972,24 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1953
1972
|
// "streaming" so that they can access internal information.
|
|
1954
1973
|
this.loadMode_ = shaka.Player.LoadMode.MEDIA_SOURCE;
|
|
1955
1974
|
|
|
1975
|
+
if (mediaElement.textTracks) {
|
|
1976
|
+
this.loadEventManager_.listen(
|
|
1977
|
+
mediaElement.textTracks, 'addtrack', (e) => {
|
|
1978
|
+
const trackEvent = /** @type {!TrackEvent} */(e);
|
|
1979
|
+
if (trackEvent.track) {
|
|
1980
|
+
const track = trackEvent.track;
|
|
1981
|
+
goog.asserts.assert(
|
|
1982
|
+
track instanceof TextTrack, 'Wrong track type!');
|
|
1983
|
+
|
|
1984
|
+
switch (track.kind) {
|
|
1985
|
+
case 'chapters':
|
|
1986
|
+
this.activateChaptersTrack_(track);
|
|
1987
|
+
break;
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
});
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1956
1993
|
// The event must be fired after we filter by restrictions but before the
|
|
1957
1994
|
// active stream is picked to allow those listening for the "streaming"
|
|
1958
1995
|
// event to make changes before streaming starts.
|
|
@@ -2039,7 +2076,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2039
2076
|
}
|
|
2040
2077
|
|
|
2041
2078
|
// Wait for the 'loadedmetadata' event to measure load() latency.
|
|
2042
|
-
this.
|
|
2079
|
+
this.loadEventManager_.listenOnce(mediaElement, 'loadedmetadata', () => {
|
|
2043
2080
|
const now = Date.now() / 1000;
|
|
2044
2081
|
const delta = now - wants.startTimeOfLoad;
|
|
2045
2082
|
this.stats_.setLoadLatency(delta);
|
|
@@ -2192,11 +2229,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2192
2229
|
// Save the uri so that it can be used outside of the load-graph.
|
|
2193
2230
|
this.assetUri_ = has.uri;
|
|
2194
2231
|
|
|
2195
|
-
|
|
2232
|
+
const mediaElement = has.mediaElement;
|
|
2233
|
+
|
|
2234
|
+
this.playhead_ = new shaka.media.SrcEqualsPlayhead(mediaElement);
|
|
2196
2235
|
|
|
2197
|
-
// This flag is used below in the language preference setup
|
|
2198
|
-
//
|
|
2199
|
-
// events fired.
|
|
2236
|
+
// This flag is used below in the language preference setup to check if
|
|
2237
|
+
// this load was canceled before the necessary awaits completed.
|
|
2200
2238
|
let unloaded = false;
|
|
2201
2239
|
this.cleanupOnUnload_.push(() => {
|
|
2202
2240
|
unloaded = true;
|
|
@@ -2207,10 +2245,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2207
2245
|
}
|
|
2208
2246
|
|
|
2209
2247
|
this.playRateController_ = new shaka.media.PlayRateController({
|
|
2210
|
-
getRate: () =>
|
|
2211
|
-
getDefaultRate: () =>
|
|
2212
|
-
setRate: (rate) => {
|
|
2213
|
-
movePlayhead: (delta) => {
|
|
2248
|
+
getRate: () => mediaElement.playbackRate,
|
|
2249
|
+
getDefaultRate: () => mediaElement.defaultPlaybackRate,
|
|
2250
|
+
setRate: (rate) => { mediaElement.playbackRate = rate; },
|
|
2251
|
+
movePlayhead: (delta) => { mediaElement.currentTime += delta; },
|
|
2214
2252
|
});
|
|
2215
2253
|
|
|
2216
2254
|
// We need to start the buffer management code near the end because it will
|
|
@@ -2222,17 +2260,17 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2222
2260
|
// Add all media element listeners.
|
|
2223
2261
|
const updateStateHistory = () => this.updateStateHistory_();
|
|
2224
2262
|
const onRateChange = () => this.onRateChange_();
|
|
2225
|
-
this.
|
|
2226
|
-
this.
|
|
2227
|
-
this.
|
|
2228
|
-
this.
|
|
2263
|
+
this.loadEventManager_.listen(mediaElement, 'playing', updateStateHistory);
|
|
2264
|
+
this.loadEventManager_.listen(mediaElement, 'pause', updateStateHistory);
|
|
2265
|
+
this.loadEventManager_.listen(mediaElement, 'ended', updateStateHistory);
|
|
2266
|
+
this.loadEventManager_.listen(mediaElement, 'ratechange', onRateChange);
|
|
2229
2267
|
|
|
2230
2268
|
// Wait for the 'loadedmetadata' event to measure load() latency, but only
|
|
2231
2269
|
// if preload is set in a way that would result in this event firing
|
|
2232
2270
|
// automatically.
|
|
2233
2271
|
// See https://github.com/shaka-project/shaka-player/issues/2483
|
|
2234
|
-
if (
|
|
2235
|
-
this.
|
|
2272
|
+
if (mediaElement.preload != 'none') {
|
|
2273
|
+
this.loadEventManager_.listenOnce(mediaElement, 'loadedmetadata', () => {
|
|
2236
2274
|
const now = Date.now() / 1000;
|
|
2237
2275
|
const delta = now - wants.startTimeOfLoad;
|
|
2238
2276
|
this.stats_.setLoadLatency(delta);
|
|
@@ -2242,44 +2280,47 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2242
2280
|
// The audio tracks are only available on Safari at the moment, but this
|
|
2243
2281
|
// drives the tracks API for Safari's native HLS. So when they change,
|
|
2244
2282
|
// fire the corresponding Shaka Player event.
|
|
2245
|
-
if (
|
|
2246
|
-
this.
|
|
2247
|
-
|
|
2248
|
-
this.
|
|
2249
|
-
|
|
2283
|
+
if (mediaElement.audioTracks) {
|
|
2284
|
+
this.loadEventManager_.listen(
|
|
2285
|
+
mediaElement.audioTracks, 'addtrack', () => this.onTracksChanged_());
|
|
2286
|
+
this.loadEventManager_.listen(
|
|
2287
|
+
mediaElement.audioTracks, 'removetrack',
|
|
2250
2288
|
() => this.onTracksChanged_());
|
|
2251
|
-
this.
|
|
2252
|
-
|
|
2253
|
-
}
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2289
|
+
this.loadEventManager_.listen(
|
|
2290
|
+
mediaElement.audioTracks, 'change', () => this.onTracksChanged_());
|
|
2291
|
+
}
|
|
2292
|
+
|
|
2293
|
+
if (mediaElement.textTracks) {
|
|
2294
|
+
this.loadEventManager_.listen(
|
|
2295
|
+
mediaElement.textTracks, 'addtrack', (e) => {
|
|
2296
|
+
const trackEvent = /** @type {!TrackEvent} */(e);
|
|
2297
|
+
if (trackEvent.track) {
|
|
2298
|
+
const track = trackEvent.track;
|
|
2299
|
+
goog.asserts.assert(
|
|
2300
|
+
track instanceof TextTrack, 'Wrong track type!');
|
|
2301
|
+
|
|
2302
|
+
switch (track.kind) {
|
|
2303
|
+
case 'metadata':
|
|
2304
|
+
this.processTimedMetadataSrcEqls_(track);
|
|
2305
|
+
break;
|
|
2306
|
+
|
|
2307
|
+
case 'chapters':
|
|
2308
|
+
this.activateChaptersTrack_(track);
|
|
2309
|
+
break;
|
|
2310
|
+
|
|
2311
|
+
default:
|
|
2312
|
+
this.onTracksChanged_();
|
|
2313
|
+
break;
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
});
|
|
2261
2317
|
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
this.processTimedMetadataSrcEqls_(track);
|
|
2269
|
-
break;
|
|
2270
|
-
case 'chapters':
|
|
2271
|
-
this.processChaptersTrack_(track);
|
|
2272
|
-
break;
|
|
2273
|
-
default:
|
|
2274
|
-
this.onTracksChanged_();
|
|
2275
|
-
break;
|
|
2276
|
-
}
|
|
2277
|
-
}
|
|
2278
|
-
});
|
|
2279
|
-
this.eventManager_.listen(
|
|
2280
|
-
this.video_.textTracks, 'removetrack', () => this.onTracksChanged_());
|
|
2281
|
-
this.eventManager_.listen(
|
|
2282
|
-
this.video_.textTracks, 'change', () => this.onTracksChanged_());
|
|
2318
|
+
this.loadEventManager_.listen(
|
|
2319
|
+
mediaElement.textTracks, 'removetrack',
|
|
2320
|
+
() => this.onTracksChanged_());
|
|
2321
|
+
this.loadEventManager_.listen(
|
|
2322
|
+
mediaElement.textTracks, 'change',
|
|
2323
|
+
() => this.onTracksChanged_());
|
|
2283
2324
|
}
|
|
2284
2325
|
|
|
2285
2326
|
const extension = shaka.media.ManifestParser.getExtension(has.uri);
|
|
@@ -2288,14 +2329,14 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2288
2329
|
|
|
2289
2330
|
// By setting |src| we are done "loading" with src=. We don't need to set
|
|
2290
2331
|
// the current time because |playhead| will do that for us.
|
|
2291
|
-
|
|
2332
|
+
mediaElement.src = this.cmcdManager_.appendSrcData(has.uri, mimeType);
|
|
2292
2333
|
|
|
2293
2334
|
// Tizen 3 / WebOS won't load anything unless you call load() explicitly,
|
|
2294
2335
|
// no matter the value of the preload attribute. This is harmful on some
|
|
2295
2336
|
// other platforms by triggering unbounded loading of media data, but is
|
|
2296
2337
|
// necessary here.
|
|
2297
2338
|
if (shaka.util.Platform.isTizen() || shaka.util.Platform.isWebOS()) {
|
|
2298
|
-
|
|
2339
|
+
mediaElement.load();
|
|
2299
2340
|
}
|
|
2300
2341
|
|
|
2301
2342
|
// Set the load mode last so that we know that all our components are
|
|
@@ -2311,24 +2352,18 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2311
2352
|
// wait for the full data, that won't happen on Safari until the play button
|
|
2312
2353
|
// is hit.
|
|
2313
2354
|
const fullyLoaded = new shaka.util.PublicPromise();
|
|
2314
|
-
shaka.util.MediaReadyState.waitForReadyState(
|
|
2355
|
+
shaka.util.MediaReadyState.waitForReadyState(mediaElement,
|
|
2315
2356
|
HTMLMediaElement.HAVE_METADATA,
|
|
2316
|
-
this.
|
|
2357
|
+
this.loadEventManager_,
|
|
2317
2358
|
() => {
|
|
2318
2359
|
fullyLoaded.resolve();
|
|
2319
2360
|
});
|
|
2320
2361
|
|
|
2321
2362
|
// We can't switch to preferred languages, though, until the data is loaded.
|
|
2322
|
-
shaka.util.MediaReadyState.waitForReadyState(
|
|
2363
|
+
shaka.util.MediaReadyState.waitForReadyState(mediaElement,
|
|
2323
2364
|
HTMLMediaElement.HAVE_CURRENT_DATA,
|
|
2324
|
-
this.
|
|
2365
|
+
this.loadEventManager_,
|
|
2325
2366
|
async () => {
|
|
2326
|
-
// If we have moved on to another piece of content while waiting for
|
|
2327
|
-
// the above event, we should not change tracks here.
|
|
2328
|
-
if (unloaded) {
|
|
2329
|
-
return;
|
|
2330
|
-
}
|
|
2331
|
-
|
|
2332
2367
|
this.setupPreferredAudioOnSrc_();
|
|
2333
2368
|
|
|
2334
2369
|
// Applying the text preference too soon can result in it being
|
|
@@ -2336,8 +2371,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2336
2371
|
const textTracks = this.getFilteredTextTracks_();
|
|
2337
2372
|
if (!textTracks.find((t) => t.mode != 'disabled')) {
|
|
2338
2373
|
await new Promise((resolve) => {
|
|
2339
|
-
this.
|
|
2340
|
-
|
|
2374
|
+
this.loadEventManager_.listenOnce(
|
|
2375
|
+
mediaElement.textTracks, 'change', resolve);
|
|
2376
|
+
|
|
2341
2377
|
// We expect the event to fire because it does on Safari.
|
|
2342
2378
|
// But in case it doesn't on some other platform or future
|
|
2343
2379
|
// version, move on in 1 second no matter what. This keeps the
|
|
@@ -2356,10 +2392,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2356
2392
|
this.setupPreferredTextOnSrc_();
|
|
2357
2393
|
});
|
|
2358
2394
|
|
|
2359
|
-
if (
|
|
2395
|
+
if (mediaElement.error) {
|
|
2360
2396
|
// Already failed!
|
|
2361
2397
|
fullyLoaded.reject(this.videoErrorToShakaError_());
|
|
2362
|
-
} else if (
|
|
2398
|
+
} else if (mediaElement.preload == 'none') {
|
|
2363
2399
|
shaka.log.alwaysWarn(
|
|
2364
2400
|
'With <video preload="none">, the browser will not load anything ' +
|
|
2365
2401
|
'until play() is called. We are unable to measure load latency in ' +
|
|
@@ -2370,7 +2406,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2370
2406
|
fullyLoaded.resolve();
|
|
2371
2407
|
}
|
|
2372
2408
|
|
|
2373
|
-
this.
|
|
2409
|
+
this.loadEventManager_.listenOnce(mediaElement, 'error', () => {
|
|
2374
2410
|
fullyLoaded.reject(this.videoErrorToShakaError_());
|
|
2375
2411
|
});
|
|
2376
2412
|
|
|
@@ -2454,7 +2490,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2454
2490
|
|
|
2455
2491
|
// Hidden mode is required for the cuechange event to launch correctly
|
|
2456
2492
|
track.mode = 'hidden';
|
|
2457
|
-
this.
|
|
2493
|
+
this.loadEventManager_.listen(track, 'cuechange', () => {
|
|
2458
2494
|
if (!track.activeCues) {
|
|
2459
2495
|
return;
|
|
2460
2496
|
}
|
|
@@ -2532,12 +2568,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2532
2568
|
}
|
|
2533
2569
|
|
|
2534
2570
|
/**
|
|
2535
|
-
*
|
|
2571
|
+
* Set the mode on a chapters track so that it loads.
|
|
2536
2572
|
*
|
|
2537
2573
|
* @param {?TextTrack} track
|
|
2538
2574
|
* @private
|
|
2539
2575
|
*/
|
|
2540
|
-
|
|
2576
|
+
activateChaptersTrack_(track) {
|
|
2541
2577
|
if (!track || track.kind != 'chapters') {
|
|
2542
2578
|
return;
|
|
2543
2579
|
}
|
|
@@ -2550,10 +2586,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2550
2586
|
// this process to be repeated several times to ensure that it has been put
|
|
2551
2587
|
// in the correct mode.
|
|
2552
2588
|
const timer = new shaka.util.Timer(() => {
|
|
2553
|
-
|
|
2554
|
-
for (const chaptersTrack of chaptersTracks) {
|
|
2555
|
-
chaptersTrack.mode = 'hidden';
|
|
2556
|
-
}
|
|
2589
|
+
track.mode = 'hidden';
|
|
2557
2590
|
}).tickNow().tickAfter(0.5);
|
|
2558
2591
|
|
|
2559
2592
|
this.cleanupOnUnload_.push(() => {
|
|
@@ -4537,25 +4570,41 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4537
4570
|
if (!mimeType) {
|
|
4538
4571
|
mimeType = await this.getTextMimetype_(uri);
|
|
4539
4572
|
}
|
|
4573
|
+
|
|
4540
4574
|
let adCuePoints = [];
|
|
4541
4575
|
if (this.adManager_) {
|
|
4542
4576
|
try {
|
|
4543
4577
|
adCuePoints = this.adManager_.getServerSideCuePoints();
|
|
4544
4578
|
} catch (error) {}
|
|
4545
4579
|
}
|
|
4546
|
-
|
|
4547
|
-
|
|
4580
|
+
|
|
4581
|
+
/** @type {!HTMLTrackElement} */
|
|
4582
|
+
const trackElement = await this.addSrcTrackElement_(
|
|
4583
|
+
uri, language, /* kind= */ 'chapters', mimeType, /* label= */ '',
|
|
4584
|
+
adCuePoints);
|
|
4585
|
+
|
|
4548
4586
|
const chaptersTracks = this.getChaptersTracks();
|
|
4549
4587
|
const chaptersTrack = chaptersTracks.find((t) => {
|
|
4550
4588
|
return t.language == language;
|
|
4551
4589
|
});
|
|
4590
|
+
|
|
4552
4591
|
if (chaptersTrack) {
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4592
|
+
await new Promise((resolve, reject) => {
|
|
4593
|
+
// The chapter data isn't available until the 'load' event fires, and
|
|
4594
|
+
// that won't happen until the chapters track is activated by the
|
|
4595
|
+
// activateChaptersTrack_ method.
|
|
4596
|
+
this.loadEventManager_.listenOnce(trackElement, 'load', resolve);
|
|
4597
|
+
this.loadEventManager_.listenOnce(trackElement, 'error', (event) => {
|
|
4598
|
+
reject(new shaka.util.Error(
|
|
4599
|
+
shaka.util.Error.Severity.RECOVERABLE,
|
|
4600
|
+
shaka.util.Error.Category.TEXT,
|
|
4601
|
+
shaka.util.Error.Code.CHAPTERS_TRACK_FAILED));
|
|
4602
|
+
});
|
|
4603
|
+
});
|
|
4604
|
+
|
|
4557
4605
|
return chaptersTrack;
|
|
4558
4606
|
}
|
|
4607
|
+
|
|
4559
4608
|
// This should not happen, but there are browser implementations that may
|
|
4560
4609
|
// not support the Track element.
|
|
4561
4610
|
shaka.log.error('Cannot add this text when loaded with src=');
|
|
@@ -4609,6 +4658,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4609
4658
|
* @param {string} mimeType
|
|
4610
4659
|
* @param {string} label
|
|
4611
4660
|
* @param {!Array.<!shaka.extern.AdCuePoint>} adCuePoints
|
|
4661
|
+
* @return {!Promise.<!HTMLTrackElement>}
|
|
4612
4662
|
* @private
|
|
4613
4663
|
*/
|
|
4614
4664
|
async addSrcTrackElement_(uri, language, kind, mimeType, label,
|
|
@@ -4624,12 +4674,14 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4624
4674
|
uri = shaka.media.MediaSourceEngine.createObjectURL(blob);
|
|
4625
4675
|
mimeType = 'text/vtt';
|
|
4626
4676
|
}
|
|
4677
|
+
|
|
4627
4678
|
const trackElement =
|
|
4628
4679
|
/** @type {!HTMLTrackElement} */(document.createElement('track'));
|
|
4629
4680
|
trackElement.src = this.cmcdManager_.appendTextTrackData(uri);
|
|
4630
4681
|
trackElement.label = label;
|
|
4631
4682
|
trackElement.kind = kind;
|
|
4632
4683
|
trackElement.srclang = language;
|
|
4684
|
+
|
|
4633
4685
|
// Because we're pulling in the text track file via Javascript, the
|
|
4634
4686
|
// same-origin policy applies. If you'd like to have a player served
|
|
4635
4687
|
// from one domain, but the text track served from another, you'll
|
|
@@ -4639,7 +4691,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4639
4691
|
if (!this.video_.getAttribute('crossorigin')) {
|
|
4640
4692
|
this.video_.setAttribute('crossorigin', 'anonymous');
|
|
4641
4693
|
}
|
|
4694
|
+
|
|
4642
4695
|
this.video_.appendChild(trackElement);
|
|
4696
|
+
return trackElement;
|
|
4643
4697
|
}
|
|
4644
4698
|
|
|
4645
4699
|
/**
|
|
@@ -5010,7 +5064,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5010
5064
|
}
|
|
5011
5065
|
|
|
5012
5066
|
await stream.createSegmentIndex();
|
|
5013
|
-
const
|
|
5067
|
+
const iter = stream.segmentIndex.getIteratorForTime(time);
|
|
5068
|
+
const ref = iter ? iter.next().value : null;
|
|
5014
5069
|
if (!ref) {
|
|
5015
5070
|
return null;
|
|
5016
5071
|
}
|
|
@@ -5679,7 +5734,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5679
5734
|
} // if (keyIds.size)
|
|
5680
5735
|
|
|
5681
5736
|
if (tracksChanged) {
|
|
5682
|
-
this.updateAbrManagerVariants_();
|
|
5737
|
+
const variantsUpdated = this.updateAbrManagerVariants_();
|
|
5738
|
+
if (!variantsUpdated) {
|
|
5739
|
+
return;
|
|
5740
|
+
}
|
|
5683
5741
|
}
|
|
5684
5742
|
|
|
5685
5743
|
const currentVariant = this.streamingEngine_.getCurrentVariant();
|
|
@@ -6392,7 +6450,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6392
6450
|
* @define {string} A version number taken from git at compile time.
|
|
6393
6451
|
* @export
|
|
6394
6452
|
*/
|
|
6395
|
-
shaka.Player.version = 'v4.0.
|
|
6453
|
+
shaka.Player.version = 'v4.0.3-uncompiled';
|
|
6396
6454
|
|
|
6397
6455
|
// Initialize the deprecation system using the version string we just set
|
|
6398
6456
|
// on the player.
|
|
@@ -68,13 +68,15 @@ shaka.text.VttTextParser = class {
|
|
|
68
68
|
// to the beginning of each segment.
|
|
69
69
|
// NOTE: "periodStart" is the timestamp offset applied via TextEngine.
|
|
70
70
|
// It is no longer closely tied to periods, but the name stuck around.
|
|
71
|
+
// NOTE: This offset and the flag choosing its meaning have no effect on
|
|
72
|
+
// HLS content, which should use X-TIMESTAMP-MAP and periodStart instead.
|
|
71
73
|
let offset = time.vttOffset;
|
|
72
74
|
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
if (blocks[0].includes('X-TIMESTAMP-MAP') &&
|
|
75
|
+
// Only use 'X-TIMESTAMP-MAP' in sequence mode, as that is currently
|
|
76
|
+
// shorthand for HLS. Note that an offset based on the first video
|
|
77
|
+
// timestamp has already been extracted, and appears in periodStart.
|
|
78
|
+
// The relative offset from X-TIMESTAMP-MAP will be added to that for HLS.
|
|
79
|
+
if (blocks[0].includes('X-TIMESTAMP-MAP') && this.sequenceMode_) {
|
|
78
80
|
// https://bit.ly/2K92l7y
|
|
79
81
|
// The 'X-TIMESTAMP-MAP' header is used in HLS to align text with
|
|
80
82
|
// the rest of the media.
|
|
@@ -109,8 +111,6 @@ shaka.text.VttTextParser = class {
|
|
|
109
111
|
mpegTime += shaka.text.VttTextParser.TS_ROLLOVER_;
|
|
110
112
|
}
|
|
111
113
|
|
|
112
|
-
// Apple-encoded HLS content uses absolute timestamps, so assume the
|
|
113
|
-
// presence of the map tag means the content uses absolute timestamps.
|
|
114
114
|
offset = time.periodStart + mpegTime / mpegTimescale - cueTime;
|
|
115
115
|
}
|
|
116
116
|
}
|
package/lib/util/config_utils.js
CHANGED
|
@@ -127,4 +127,29 @@ shaka.util.ConfigUtils = class {
|
|
|
127
127
|
last[fieldName.substring(nameStart).replace(/\\\./g, '.')] = value;
|
|
128
128
|
return configObject;
|
|
129
129
|
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Reference the input parameters so the compiler doesn't remove them from
|
|
133
|
+
* the calling function. Return whatever value is specified.
|
|
134
|
+
*
|
|
135
|
+
* This allows an empty or default implementation of a config callback that
|
|
136
|
+
* still bears the complete function signature even in compiled mode.
|
|
137
|
+
*
|
|
138
|
+
* The caller should look something like this:
|
|
139
|
+
*
|
|
140
|
+
* const callback = (a, b, c, d) => {
|
|
141
|
+
* return referenceParametersAndReturn(
|
|
142
|
+
[a, b, c, d],
|
|
143
|
+
a); // Can be anything, doesn't need to be one of the parameters
|
|
144
|
+
* };
|
|
145
|
+
*
|
|
146
|
+
* @param {!Array.<?>} parameters
|
|
147
|
+
* @param {T} returnValue
|
|
148
|
+
* @return {T}
|
|
149
|
+
* @template T
|
|
150
|
+
* @noinline
|
|
151
|
+
*/
|
|
152
|
+
static referenceParametersAndReturn(parameters, returnValue) {
|
|
153
|
+
return parameters && returnValue;
|
|
154
|
+
}
|
|
130
155
|
};
|
package/lib/util/error.js
CHANGED
|
@@ -345,6 +345,13 @@ shaka.util.Error.Code = {
|
|
|
345
345
|
*/
|
|
346
346
|
'MISSING_TEXT_PLUGIN': 2014,
|
|
347
347
|
|
|
348
|
+
/**
|
|
349
|
+
* The chapters track failed to load. The browser does not provide any
|
|
350
|
+
* information in this case to identify why it failed, but there may be
|
|
351
|
+
* details in the JavaScript console.
|
|
352
|
+
*/
|
|
353
|
+
'CHAPTERS_TRACK_FAILED': 2015,
|
|
354
|
+
|
|
348
355
|
/**
|
|
349
356
|
* Some component tried to read past the end of a buffer. The segment index,
|
|
350
357
|
* init segment, or PSSH may be malformed.
|