shaka-player 3.3.3 → 3.3.6
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/workflows/build-and-test.yaml +0 -1
- package/.github/workflows/release-please.yaml +6 -3
- package/.github/workflows/selenium-lab-tests.yaml +0 -1
- package/CHANGELOG.md +38 -0
- package/README.md +4 -4
- package/build/README.md +2 -2
- package/build/test.py +7 -1
- package/build/types/core +0 -1
- package/demo/demo.less +0 -1
- package/dist/controls.css +3 -3
- package/dist/controls.css.map +1 -1
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +2 -3
- package/dist/locales.js +31 -31
- package/dist/shaka-player.compiled.d.ts +1 -0
- package/dist/shaka-player.compiled.debug.d.ts +1 -0
- package/dist/shaka-player.compiled.debug.externs.js +1 -0
- package/dist/shaka-player.compiled.debug.js +152 -150
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +1 -0
- package/dist/shaka-player.compiled.js +844 -842
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +1 -0
- package/dist/shaka-player.ui.debug.d.ts +1 -0
- package/dist/shaka-player.ui.debug.externs.js +1 -0
- package/dist/shaka-player.ui.debug.js +224 -222
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +1 -0
- package/dist/shaka-player.ui.js +952 -950
- package/dist/shaka-player.ui.map +4 -4
- package/karma.conf.js +4 -0
- package/lib/abr/simple_abr_manager.js +2 -1
- package/lib/dash/dash_parser.js +2 -3
- package/lib/hls/hls_parser.js +37 -14
- package/lib/media/drm_engine.js +3 -0
- package/lib/media/media_source_engine.js +7 -1
- package/lib/offline/indexeddb/storage_mechanism.js +4 -2
- package/lib/offline/storage.js +92 -91
- package/lib/player.js +165 -107
- 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 +9 -14
- package/lib/util/stream_utils.js +4 -2
- package/package.json +2 -2
- package/test/cast/cast_utils_unit.js +1 -0
- package/test/dash/dash_parser_manifest_unit.js +0 -24
- package/test/hls/hls_live_unit.js +86 -7
- package/test/hls/manifest_text_parser_unit.js +19 -0
- package/test/media/media_source_engine_integration.js +1 -0
- package/test/media/media_source_engine_unit.js +4 -4
- package/test/media/streaming_engine_integration.js +2 -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/util/mime_utils_unit.js +54 -0
- package/test/util/periods_unit.js +79 -0
- package/ui/less/ad_controls.less +2 -2
- package/ui/less/buttons.less +1 -1
- package/ui/less/containers.less +5 -5
- package/ui/less/general.less +1 -1
- package/ui/less/range_elements.less +3 -2
- package/ui/less/tooltip.less +3 -3
- package/ui/seek_bar.js +2 -1
- package/.github/pull_request_template.md +0 -10
- package/docs/tutorials/gsoc-ideas-list.md +0 -3
- package/lib/util/mutex.js +0 -52
package/lib/player.js
CHANGED
|
@@ -440,8 +440,23 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
440
440
|
*/
|
|
441
441
|
this.isTextVisible_ = false;
|
|
442
442
|
|
|
443
|
-
/**
|
|
444
|
-
|
|
443
|
+
/**
|
|
444
|
+
* For listeners scoped to the lifetime of the Player instance.
|
|
445
|
+
* @private {shaka.util.EventManager}
|
|
446
|
+
*/
|
|
447
|
+
this.globalEventManager_ = new shaka.util.EventManager();
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* For listeners scoped to the lifetime of the media element attachment.
|
|
451
|
+
* @private {shaka.util.EventManager}
|
|
452
|
+
*/
|
|
453
|
+
this.attachEventManager_ = new shaka.util.EventManager();
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* For listeners scoped to the lifetime of the loaded content.
|
|
457
|
+
* @private {shaka.util.EventManager}
|
|
458
|
+
*/
|
|
459
|
+
this.loadEventManager_ = new shaka.util.EventManager();
|
|
445
460
|
|
|
446
461
|
/** @private {shaka.net.NetworkingEngine} */
|
|
447
462
|
this.networkingEngine_ = null;
|
|
@@ -582,7 +597,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
582
597
|
|
|
583
598
|
// If the browser comes back online after being offline, then try to play
|
|
584
599
|
// again.
|
|
585
|
-
this.
|
|
600
|
+
this.globalEventManager_.listen(window, 'online', () => {
|
|
586
601
|
this.retryStreaming();
|
|
587
602
|
});
|
|
588
603
|
|
|
@@ -770,10 +785,18 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
770
785
|
});
|
|
771
786
|
await this.walker_.destroy();
|
|
772
787
|
|
|
773
|
-
// Tear-down the event
|
|
774
|
-
if (this.
|
|
775
|
-
this.
|
|
776
|
-
this.
|
|
788
|
+
// Tear-down the event managers to ensure handlers stop firing.
|
|
789
|
+
if (this.globalEventManager_) {
|
|
790
|
+
this.globalEventManager_.release();
|
|
791
|
+
this.globalEventManager_ = null;
|
|
792
|
+
}
|
|
793
|
+
if (this.attachEventManager_) {
|
|
794
|
+
this.attachEventManager_.release();
|
|
795
|
+
this.attachEventManager_ = null;
|
|
796
|
+
}
|
|
797
|
+
if (this.loadEventManager_) {
|
|
798
|
+
this.loadEventManager_.release();
|
|
799
|
+
this.loadEventManager_ = null;
|
|
777
800
|
}
|
|
778
801
|
|
|
779
802
|
this.abrManagerFactory_ = null;
|
|
@@ -1316,7 +1339,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1316
1339
|
has.mediaElement = wants.mediaElement;
|
|
1317
1340
|
|
|
1318
1341
|
const onError = (error) => this.onVideoError_(error);
|
|
1319
|
-
this.
|
|
1342
|
+
this.attachEventManager_.listen(has.mediaElement, 'error', onError);
|
|
1320
1343
|
}
|
|
1321
1344
|
|
|
1322
1345
|
this.video_ = has.mediaElement;
|
|
@@ -1343,10 +1366,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1343
1366
|
* @private
|
|
1344
1367
|
*/
|
|
1345
1368
|
onDetach_(has, wants) {
|
|
1346
|
-
// If we
|
|
1369
|
+
// If we were going from "detached" to "detached" we wouldn't have
|
|
1347
1370
|
// a media element to detach from.
|
|
1348
1371
|
if (has.mediaElement) {
|
|
1349
|
-
this.
|
|
1372
|
+
this.attachEventManager_.removeAll();
|
|
1350
1373
|
has.mediaElement = null;
|
|
1351
1374
|
}
|
|
1352
1375
|
|
|
@@ -1404,11 +1427,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1404
1427
|
// In most cases we should have a media element. The one exception would
|
|
1405
1428
|
// be if there was an error and we, by chance, did not have a media element.
|
|
1406
1429
|
if (has.mediaElement) {
|
|
1407
|
-
this.
|
|
1408
|
-
this.eventManager_.unlisten(has.mediaElement, 'playing');
|
|
1409
|
-
this.eventManager_.unlisten(has.mediaElement, 'pause');
|
|
1410
|
-
this.eventManager_.unlisten(has.mediaElement, 'ended');
|
|
1411
|
-
this.eventManager_.unlisten(has.mediaElement, 'ratechange');
|
|
1430
|
+
this.loadEventManager_.removeAll();
|
|
1412
1431
|
}
|
|
1413
1432
|
|
|
1414
1433
|
// Some observers use some playback components, shutting down the observers
|
|
@@ -1902,18 +1921,18 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1902
1921
|
this.assetUri_ = assetUri;
|
|
1903
1922
|
|
|
1904
1923
|
this.playRateController_ = new shaka.media.PlayRateController({
|
|
1905
|
-
getRate: () =>
|
|
1906
|
-
getDefaultRate: () =>
|
|
1907
|
-
setRate: (rate) => {
|
|
1908
|
-
movePlayhead: (delta) => {
|
|
1924
|
+
getRate: () => mediaElement.playbackRate,
|
|
1925
|
+
getDefaultRate: () => mediaElement.defaultPlaybackRate,
|
|
1926
|
+
setRate: (rate) => { mediaElement.playbackRate = rate; },
|
|
1927
|
+
movePlayhead: (delta) => { mediaElement.currentTime += delta; },
|
|
1909
1928
|
});
|
|
1910
1929
|
|
|
1911
1930
|
const updateStateHistory = () => this.updateStateHistory_();
|
|
1912
1931
|
const onRateChange = () => this.onRateChange_();
|
|
1913
|
-
this.
|
|
1914
|
-
this.
|
|
1915
|
-
this.
|
|
1916
|
-
this.
|
|
1932
|
+
this.loadEventManager_.listen(mediaElement, 'playing', updateStateHistory);
|
|
1933
|
+
this.loadEventManager_.listen(mediaElement, 'pause', updateStateHistory);
|
|
1934
|
+
this.loadEventManager_.listen(mediaElement, 'ended', updateStateHistory);
|
|
1935
|
+
this.loadEventManager_.listen(mediaElement, 'ratechange', onRateChange);
|
|
1917
1936
|
|
|
1918
1937
|
const abrFactory = this.config_.abrFactory;
|
|
1919
1938
|
if (!this.abrManager_ || this.abrManagerFactory_ != abrFactory) {
|
|
@@ -1976,6 +1995,24 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1976
1995
|
// "streaming" so that they can access internal information.
|
|
1977
1996
|
this.loadMode_ = shaka.Player.LoadMode.MEDIA_SOURCE;
|
|
1978
1997
|
|
|
1998
|
+
if (mediaElement.textTracks) {
|
|
1999
|
+
this.loadEventManager_.listen(
|
|
2000
|
+
mediaElement.textTracks, 'addtrack', (e) => {
|
|
2001
|
+
const trackEvent = /** @type {!TrackEvent} */(e);
|
|
2002
|
+
if (trackEvent.track) {
|
|
2003
|
+
const track = trackEvent.track;
|
|
2004
|
+
goog.asserts.assert(
|
|
2005
|
+
track instanceof TextTrack, 'Wrong track type!');
|
|
2006
|
+
|
|
2007
|
+
switch (track.kind) {
|
|
2008
|
+
case 'chapters':
|
|
2009
|
+
this.activateChaptersTrack_(track);
|
|
2010
|
+
break;
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
});
|
|
2014
|
+
}
|
|
2015
|
+
|
|
1979
2016
|
// The event must be fired after we filter by restrictions but before the
|
|
1980
2017
|
// active stream is picked to allow those listening for the "streaming"
|
|
1981
2018
|
// event to make changes before streaming starts.
|
|
@@ -2062,7 +2099,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2062
2099
|
}
|
|
2063
2100
|
|
|
2064
2101
|
// Wait for the 'loadedmetadata' event to measure load() latency.
|
|
2065
|
-
this.
|
|
2102
|
+
this.loadEventManager_.listenOnce(mediaElement, 'loadedmetadata', () => {
|
|
2066
2103
|
const now = Date.now() / 1000;
|
|
2067
2104
|
const delta = now - wants.startTimeOfLoad;
|
|
2068
2105
|
this.stats_.setLoadLatency(delta);
|
|
@@ -2215,11 +2252,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2215
2252
|
// Save the uri so that it can be used outside of the load-graph.
|
|
2216
2253
|
this.assetUri_ = has.uri;
|
|
2217
2254
|
|
|
2218
|
-
|
|
2255
|
+
const mediaElement = has.mediaElement;
|
|
2256
|
+
|
|
2257
|
+
this.playhead_ = new shaka.media.SrcEqualsPlayhead(mediaElement);
|
|
2219
2258
|
|
|
2220
|
-
// This flag is used below in the language preference setup
|
|
2221
|
-
//
|
|
2222
|
-
// events fired.
|
|
2259
|
+
// This flag is used below in the language preference setup to check if
|
|
2260
|
+
// this load was canceled before the necessary awaits completed.
|
|
2223
2261
|
let unloaded = false;
|
|
2224
2262
|
this.cleanupOnUnload_.push(() => {
|
|
2225
2263
|
unloaded = true;
|
|
@@ -2230,10 +2268,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2230
2268
|
}
|
|
2231
2269
|
|
|
2232
2270
|
this.playRateController_ = new shaka.media.PlayRateController({
|
|
2233
|
-
getRate: () =>
|
|
2234
|
-
getDefaultRate: () =>
|
|
2235
|
-
setRate: (rate) => {
|
|
2236
|
-
movePlayhead: (delta) => {
|
|
2271
|
+
getRate: () => mediaElement.playbackRate,
|
|
2272
|
+
getDefaultRate: () => mediaElement.defaultPlaybackRate,
|
|
2273
|
+
setRate: (rate) => { mediaElement.playbackRate = rate; },
|
|
2274
|
+
movePlayhead: (delta) => { mediaElement.currentTime += delta; },
|
|
2237
2275
|
});
|
|
2238
2276
|
|
|
2239
2277
|
// We need to start the buffer management code near the end because it will
|
|
@@ -2245,17 +2283,17 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2245
2283
|
// Add all media element listeners.
|
|
2246
2284
|
const updateStateHistory = () => this.updateStateHistory_();
|
|
2247
2285
|
const onRateChange = () => this.onRateChange_();
|
|
2248
|
-
this.
|
|
2249
|
-
this.
|
|
2250
|
-
this.
|
|
2251
|
-
this.
|
|
2286
|
+
this.loadEventManager_.listen(mediaElement, 'playing', updateStateHistory);
|
|
2287
|
+
this.loadEventManager_.listen(mediaElement, 'pause', updateStateHistory);
|
|
2288
|
+
this.loadEventManager_.listen(mediaElement, 'ended', updateStateHistory);
|
|
2289
|
+
this.loadEventManager_.listen(mediaElement, 'ratechange', onRateChange);
|
|
2252
2290
|
|
|
2253
2291
|
// Wait for the 'loadedmetadata' event to measure load() latency, but only
|
|
2254
2292
|
// if preload is set in a way that would result in this event firing
|
|
2255
2293
|
// automatically.
|
|
2256
2294
|
// See https://github.com/shaka-project/shaka-player/issues/2483
|
|
2257
|
-
if (
|
|
2258
|
-
this.
|
|
2295
|
+
if (mediaElement.preload != 'none') {
|
|
2296
|
+
this.loadEventManager_.listenOnce(mediaElement, 'loadedmetadata', () => {
|
|
2259
2297
|
const now = Date.now() / 1000;
|
|
2260
2298
|
const delta = now - wants.startTimeOfLoad;
|
|
2261
2299
|
this.stats_.setLoadLatency(delta);
|
|
@@ -2265,44 +2303,47 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2265
2303
|
// The audio tracks are only available on Safari at the moment, but this
|
|
2266
2304
|
// drives the tracks API for Safari's native HLS. So when they change,
|
|
2267
2305
|
// fire the corresponding Shaka Player event.
|
|
2268
|
-
if (
|
|
2269
|
-
this.
|
|
2270
|
-
|
|
2271
|
-
this.
|
|
2272
|
-
|
|
2306
|
+
if (mediaElement.audioTracks) {
|
|
2307
|
+
this.loadEventManager_.listen(
|
|
2308
|
+
mediaElement.audioTracks, 'addtrack', () => this.onTracksChanged_());
|
|
2309
|
+
this.loadEventManager_.listen(
|
|
2310
|
+
mediaElement.audioTracks, 'removetrack',
|
|
2273
2311
|
() => this.onTracksChanged_());
|
|
2274
|
-
this.
|
|
2275
|
-
|
|
2276
|
-
}
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2312
|
+
this.loadEventManager_.listen(
|
|
2313
|
+
mediaElement.audioTracks, 'change', () => this.onTracksChanged_());
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
if (mediaElement.textTracks) {
|
|
2317
|
+
this.loadEventManager_.listen(
|
|
2318
|
+
mediaElement.textTracks, 'addtrack', (e) => {
|
|
2319
|
+
const trackEvent = /** @type {!TrackEvent} */(e);
|
|
2320
|
+
if (trackEvent.track) {
|
|
2321
|
+
const track = trackEvent.track;
|
|
2322
|
+
goog.asserts.assert(
|
|
2323
|
+
track instanceof TextTrack, 'Wrong track type!');
|
|
2324
|
+
|
|
2325
|
+
switch (track.kind) {
|
|
2326
|
+
case 'metadata':
|
|
2327
|
+
this.processTimedMetadataSrcEqls_(track);
|
|
2328
|
+
break;
|
|
2329
|
+
|
|
2330
|
+
case 'chapters':
|
|
2331
|
+
this.activateChaptersTrack_(track);
|
|
2332
|
+
break;
|
|
2333
|
+
|
|
2334
|
+
default:
|
|
2335
|
+
this.onTracksChanged_();
|
|
2336
|
+
break;
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
});
|
|
2284
2340
|
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
this.processTimedMetadataSrcEqls_(track);
|
|
2292
|
-
break;
|
|
2293
|
-
case 'chapters':
|
|
2294
|
-
this.processChaptersTrack_(track);
|
|
2295
|
-
break;
|
|
2296
|
-
default:
|
|
2297
|
-
this.onTracksChanged_();
|
|
2298
|
-
break;
|
|
2299
|
-
}
|
|
2300
|
-
}
|
|
2301
|
-
});
|
|
2302
|
-
this.eventManager_.listen(
|
|
2303
|
-
this.video_.textTracks, 'removetrack', () => this.onTracksChanged_());
|
|
2304
|
-
this.eventManager_.listen(
|
|
2305
|
-
this.video_.textTracks, 'change', () => this.onTracksChanged_());
|
|
2341
|
+
this.loadEventManager_.listen(
|
|
2342
|
+
mediaElement.textTracks, 'removetrack',
|
|
2343
|
+
() => this.onTracksChanged_());
|
|
2344
|
+
this.loadEventManager_.listen(
|
|
2345
|
+
mediaElement.textTracks, 'change',
|
|
2346
|
+
() => this.onTracksChanged_());
|
|
2306
2347
|
}
|
|
2307
2348
|
|
|
2308
2349
|
const extension = shaka.media.ManifestParser.getExtension(has.uri);
|
|
@@ -2311,14 +2352,14 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2311
2352
|
|
|
2312
2353
|
// By setting |src| we are done "loading" with src=. We don't need to set
|
|
2313
2354
|
// the current time because |playhead| will do that for us.
|
|
2314
|
-
|
|
2355
|
+
mediaElement.src = this.cmcdManager_.appendSrcData(has.uri, mimeType);
|
|
2315
2356
|
|
|
2316
2357
|
// Tizen 3 / WebOS won't load anything unless you call load() explicitly,
|
|
2317
2358
|
// no matter the value of the preload attribute. This is harmful on some
|
|
2318
2359
|
// other platforms by triggering unbounded loading of media data, but is
|
|
2319
2360
|
// necessary here.
|
|
2320
2361
|
if (shaka.util.Platform.isTizen() || shaka.util.Platform.isWebOS()) {
|
|
2321
|
-
|
|
2362
|
+
mediaElement.load();
|
|
2322
2363
|
}
|
|
2323
2364
|
|
|
2324
2365
|
// Set the load mode last so that we know that all our components are
|
|
@@ -2334,24 +2375,18 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2334
2375
|
// wait for the full data, that won't happen on Safari until the play button
|
|
2335
2376
|
// is hit.
|
|
2336
2377
|
const fullyLoaded = new shaka.util.PublicPromise();
|
|
2337
|
-
shaka.util.MediaReadyState.waitForReadyState(
|
|
2378
|
+
shaka.util.MediaReadyState.waitForReadyState(mediaElement,
|
|
2338
2379
|
HTMLMediaElement.HAVE_METADATA,
|
|
2339
|
-
this.
|
|
2380
|
+
this.loadEventManager_,
|
|
2340
2381
|
() => {
|
|
2341
2382
|
fullyLoaded.resolve();
|
|
2342
2383
|
});
|
|
2343
2384
|
|
|
2344
2385
|
// We can't switch to preferred languages, though, until the data is loaded.
|
|
2345
|
-
shaka.util.MediaReadyState.waitForReadyState(
|
|
2386
|
+
shaka.util.MediaReadyState.waitForReadyState(mediaElement,
|
|
2346
2387
|
HTMLMediaElement.HAVE_CURRENT_DATA,
|
|
2347
|
-
this.
|
|
2388
|
+
this.loadEventManager_,
|
|
2348
2389
|
async () => {
|
|
2349
|
-
// If we have moved on to another piece of content while waiting for
|
|
2350
|
-
// the above event, we should not change tracks here.
|
|
2351
|
-
if (unloaded) {
|
|
2352
|
-
return;
|
|
2353
|
-
}
|
|
2354
|
-
|
|
2355
2390
|
this.setupPreferredAudioOnSrc_();
|
|
2356
2391
|
|
|
2357
2392
|
// Applying the text preference too soon can result in it being
|
|
@@ -2359,8 +2394,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2359
2394
|
const textTracks = this.getFilteredTextTracks_();
|
|
2360
2395
|
if (!textTracks.find((t) => t.mode != 'disabled')) {
|
|
2361
2396
|
await new Promise((resolve) => {
|
|
2362
|
-
this.
|
|
2363
|
-
|
|
2397
|
+
this.loadEventManager_.listenOnce(
|
|
2398
|
+
mediaElement.textTracks, 'change', resolve);
|
|
2399
|
+
|
|
2364
2400
|
// We expect the event to fire because it does on Safari.
|
|
2365
2401
|
// But in case it doesn't on some other platform or future
|
|
2366
2402
|
// version, move on in 1 second no matter what. This keeps the
|
|
@@ -2379,10 +2415,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2379
2415
|
this.setupPreferredTextOnSrc_();
|
|
2380
2416
|
});
|
|
2381
2417
|
|
|
2382
|
-
if (
|
|
2418
|
+
if (mediaElement.error) {
|
|
2383
2419
|
// Already failed!
|
|
2384
2420
|
fullyLoaded.reject(this.videoErrorToShakaError_());
|
|
2385
|
-
} else if (
|
|
2421
|
+
} else if (mediaElement.preload == 'none') {
|
|
2386
2422
|
shaka.log.alwaysWarn(
|
|
2387
2423
|
'With <video preload="none">, the browser will not load anything ' +
|
|
2388
2424
|
'until play() is called. We are unable to measure load latency in ' +
|
|
@@ -2393,7 +2429,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2393
2429
|
fullyLoaded.resolve();
|
|
2394
2430
|
}
|
|
2395
2431
|
|
|
2396
|
-
this.
|
|
2432
|
+
this.loadEventManager_.listenOnce(mediaElement, 'error', () => {
|
|
2397
2433
|
fullyLoaded.reject(this.videoErrorToShakaError_());
|
|
2398
2434
|
});
|
|
2399
2435
|
|
|
@@ -2477,7 +2513,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2477
2513
|
|
|
2478
2514
|
// Hidden mode is required for the cuechange event to launch correctly
|
|
2479
2515
|
track.mode = 'hidden';
|
|
2480
|
-
this.
|
|
2516
|
+
this.loadEventManager_.listen(track, 'cuechange', () => {
|
|
2481
2517
|
if (!track.activeCues) {
|
|
2482
2518
|
return;
|
|
2483
2519
|
}
|
|
@@ -2555,12 +2591,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2555
2591
|
}
|
|
2556
2592
|
|
|
2557
2593
|
/**
|
|
2558
|
-
*
|
|
2594
|
+
* Set the mode on a chapters track so that it loads.
|
|
2559
2595
|
*
|
|
2560
2596
|
* @param {?TextTrack} track
|
|
2561
2597
|
* @private
|
|
2562
2598
|
*/
|
|
2563
|
-
|
|
2599
|
+
activateChaptersTrack_(track) {
|
|
2564
2600
|
if (!track || track.kind != 'chapters') {
|
|
2565
2601
|
return;
|
|
2566
2602
|
}
|
|
@@ -2573,10 +2609,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2573
2609
|
// this process to be repeated several times to ensure that it has been put
|
|
2574
2610
|
// in the correct mode.
|
|
2575
2611
|
const timer = new shaka.util.Timer(() => {
|
|
2576
|
-
|
|
2577
|
-
for (const chaptersTrack of chaptersTracks) {
|
|
2578
|
-
chaptersTrack.mode = 'hidden';
|
|
2579
|
-
}
|
|
2612
|
+
track.mode = 'hidden';
|
|
2580
2613
|
}).tickNow().tickAfter(0.5);
|
|
2581
2614
|
|
|
2582
2615
|
this.cleanupOnUnload_.push(() => {
|
|
@@ -4721,25 +4754,41 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4721
4754
|
if (!mimeType) {
|
|
4722
4755
|
mimeType = await this.getTextMimetype_(uri);
|
|
4723
4756
|
}
|
|
4757
|
+
|
|
4724
4758
|
let adCuePoints = [];
|
|
4725
4759
|
if (this.adManager_) {
|
|
4726
4760
|
try {
|
|
4727
4761
|
adCuePoints = this.adManager_.getServerSideCuePoints();
|
|
4728
4762
|
} catch (error) {}
|
|
4729
4763
|
}
|
|
4730
|
-
|
|
4731
|
-
|
|
4764
|
+
|
|
4765
|
+
/** @type {!HTMLTrackElement} */
|
|
4766
|
+
const trackElement = await this.addSrcTrackElement_(
|
|
4767
|
+
uri, language, /* kind= */ 'chapters', mimeType, /* label= */ '',
|
|
4768
|
+
adCuePoints);
|
|
4769
|
+
|
|
4732
4770
|
const chaptersTracks = this.getChaptersTracks();
|
|
4733
4771
|
const chaptersTrack = chaptersTracks.find((t) => {
|
|
4734
4772
|
return t.language == language;
|
|
4735
4773
|
});
|
|
4774
|
+
|
|
4736
4775
|
if (chaptersTrack) {
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4776
|
+
await new Promise((resolve, reject) => {
|
|
4777
|
+
// The chapter data isn't available until the 'load' event fires, and
|
|
4778
|
+
// that won't happen until the chapters track is activated by the
|
|
4779
|
+
// activateChaptersTrack_ method.
|
|
4780
|
+
this.loadEventManager_.listenOnce(trackElement, 'load', resolve);
|
|
4781
|
+
this.loadEventManager_.listenOnce(trackElement, 'error', (event) => {
|
|
4782
|
+
reject(new shaka.util.Error(
|
|
4783
|
+
shaka.util.Error.Severity.RECOVERABLE,
|
|
4784
|
+
shaka.util.Error.Category.TEXT,
|
|
4785
|
+
shaka.util.Error.Code.CHAPTERS_TRACK_FAILED));
|
|
4786
|
+
});
|
|
4787
|
+
});
|
|
4788
|
+
|
|
4741
4789
|
return chaptersTrack;
|
|
4742
4790
|
}
|
|
4791
|
+
|
|
4743
4792
|
// This should not happen, but there are browser implementations that may
|
|
4744
4793
|
// not support the Track element.
|
|
4745
4794
|
shaka.log.error('Cannot add this text when loaded with src=');
|
|
@@ -4793,6 +4842,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4793
4842
|
* @param {string} mimeType
|
|
4794
4843
|
* @param {string} label
|
|
4795
4844
|
* @param {!Array.<!shaka.extern.AdCuePoint>} adCuePoints
|
|
4845
|
+
* @return {!Promise.<!HTMLTrackElement>}
|
|
4796
4846
|
* @private
|
|
4797
4847
|
*/
|
|
4798
4848
|
async addSrcTrackElement_(uri, language, kind, mimeType, label,
|
|
@@ -4808,12 +4858,14 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4808
4858
|
uri = shaka.media.MediaSourceEngine.createObjectURL(blob);
|
|
4809
4859
|
mimeType = 'text/vtt';
|
|
4810
4860
|
}
|
|
4861
|
+
|
|
4811
4862
|
const trackElement =
|
|
4812
4863
|
/** @type {!HTMLTrackElement} */(document.createElement('track'));
|
|
4813
4864
|
trackElement.src = this.cmcdManager_.appendTextTrackData(uri);
|
|
4814
4865
|
trackElement.label = label;
|
|
4815
4866
|
trackElement.kind = kind;
|
|
4816
4867
|
trackElement.srclang = language;
|
|
4868
|
+
|
|
4817
4869
|
// Because we're pulling in the text track file via Javascript, the
|
|
4818
4870
|
// same-origin policy applies. If you'd like to have a player served
|
|
4819
4871
|
// from one domain, but the text track served from another, you'll
|
|
@@ -4823,7 +4875,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4823
4875
|
if (!this.video_.getAttribute('crossorigin')) {
|
|
4824
4876
|
this.video_.setAttribute('crossorigin', 'anonymous');
|
|
4825
4877
|
}
|
|
4878
|
+
|
|
4826
4879
|
this.video_.appendChild(trackElement);
|
|
4880
|
+
return trackElement;
|
|
4827
4881
|
}
|
|
4828
4882
|
|
|
4829
4883
|
/**
|
|
@@ -5193,7 +5247,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5193
5247
|
}
|
|
5194
5248
|
|
|
5195
5249
|
await stream.createSegmentIndex();
|
|
5196
|
-
const
|
|
5250
|
+
const iter = stream.segmentIndex.getIteratorForTime(time);
|
|
5251
|
+
const ref = iter ? iter.next().value : null;
|
|
5197
5252
|
if (!ref) {
|
|
5198
5253
|
return null;
|
|
5199
5254
|
}
|
|
@@ -5862,7 +5917,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5862
5917
|
} // if (keyIds.size)
|
|
5863
5918
|
|
|
5864
5919
|
if (tracksChanged) {
|
|
5865
|
-
this.updateAbrManagerVariants_();
|
|
5920
|
+
const variantsUpdated = this.updateAbrManagerVariants_();
|
|
5921
|
+
if (!variantsUpdated) {
|
|
5922
|
+
return;
|
|
5923
|
+
}
|
|
5866
5924
|
}
|
|
5867
5925
|
|
|
5868
5926
|
const currentVariant = this.streamingEngine_.getCurrentVariant();
|
|
@@ -6576,7 +6634,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6576
6634
|
* @define {string} A version number taken from git at compile time.
|
|
6577
6635
|
* @export
|
|
6578
6636
|
*/
|
|
6579
|
-
shaka.Player.version = 'v3.3.
|
|
6637
|
+
shaka.Player.version = 'v3.3.6-uncompiled';
|
|
6580
6638
|
|
|
6581
6639
|
// Initialize the deprecation system using the version string we just set
|
|
6582
6640
|
// on the player.
|
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.
|
package/lib/util/mime_utils.js
CHANGED
|
@@ -8,6 +8,7 @@ goog.provide('shaka.util.MimeUtils');
|
|
|
8
8
|
|
|
9
9
|
goog.require('shaka.dependencies');
|
|
10
10
|
goog.require('shaka.media.Transmuxer');
|
|
11
|
+
goog.require('shaka.util.ManifestParserUtils');
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* @summary A set of utility functions for dealing with MIME types.
|
|
@@ -32,6 +33,8 @@ shaka.util.MimeUtils = class {
|
|
|
32
33
|
/**
|
|
33
34
|
* Takes a MIME type and a codecs string and produces the full MIME
|
|
34
35
|
* type. If it's a transport stream, convert its codecs to MP4 codecs.
|
|
36
|
+
* Otherwise for multiplexed content, convert the video MIME types to
|
|
37
|
+
* their audio equivalents if the content type is audio.
|
|
35
38
|
*
|
|
36
39
|
* @param {string} mimeType
|
|
37
40
|
* @param {string} codecs
|
|
@@ -40,13 +43,19 @@ shaka.util.MimeUtils = class {
|
|
|
40
43
|
*/
|
|
41
44
|
static getFullOrConvertedType(mimeType, codecs, contentType) {
|
|
42
45
|
const fullMimeType = shaka.util.MimeUtils.getFullType(mimeType, codecs);
|
|
46
|
+
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
43
47
|
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
if (shaka.media.Transmuxer.isTsContainer(fullMimeType)) {
|
|
49
|
+
if (shaka.dependencies.muxjs()) {
|
|
50
|
+
return shaka.media.Transmuxer.convertTsCodecs(
|
|
51
|
+
contentType, fullMimeType);
|
|
52
|
+
}
|
|
53
|
+
} else if (contentType == ContentType.AUDIO) {
|
|
54
|
+
// video/mp2t is the correct mime type for TS audio, so only replace the
|
|
55
|
+
// word "video" with "audio" for non-TS audio content.
|
|
56
|
+
return fullMimeType.replace('video', 'audio');
|
|
47
57
|
}
|
|
48
|
-
|
|
49
|
-
return shaka.media.Transmuxer.convertTsCodecs(contentType, fullMimeType);
|
|
58
|
+
return fullMimeType;
|
|
50
59
|
}
|
|
51
60
|
|
|
52
61
|
|
|
@@ -94,6 +103,53 @@ shaka.util.MimeUtils = class {
|
|
|
94
103
|
return codecs.split(',');
|
|
95
104
|
}
|
|
96
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Get the normalized codec from a codec string,
|
|
108
|
+
* independently of their container.
|
|
109
|
+
*
|
|
110
|
+
* @param {string} codecString
|
|
111
|
+
* @return {string}
|
|
112
|
+
*/
|
|
113
|
+
static getNormalizedCodec(codecString) {
|
|
114
|
+
const parts =
|
|
115
|
+
shaka.util.MimeUtils.getCodecParts_(codecString);
|
|
116
|
+
const base = parts[0];
|
|
117
|
+
const profile = parts[1].toLowerCase();
|
|
118
|
+
switch (true) {
|
|
119
|
+
case base === 'mp4a' && profile === '69':
|
|
120
|
+
case base === 'mp4a' && profile === '6b':
|
|
121
|
+
return 'mp3';
|
|
122
|
+
case base === 'mp4a' && profile === '66':
|
|
123
|
+
case base === 'mp4a' && profile === '67':
|
|
124
|
+
case base === 'mp4a' && profile === '68':
|
|
125
|
+
case base === 'mp4a' && profile === '40.2':
|
|
126
|
+
case base === 'mp4a' && profile === '40.02':
|
|
127
|
+
case base === 'mp4a' && profile === '40.5':
|
|
128
|
+
case base === 'mp4a' && profile === '40.05':
|
|
129
|
+
case base === 'mp4a' && profile === '40.29':
|
|
130
|
+
case base === 'mp4a' && profile === '40.42': // Extended HE-AAC
|
|
131
|
+
return 'aac';
|
|
132
|
+
case base === 'mp4a' && profile === 'a5':
|
|
133
|
+
return 'ac-3'; // Dolby Digital
|
|
134
|
+
case base === 'mp4a' && profile === 'a6':
|
|
135
|
+
return 'ec-3'; // Dolby Digital Plus
|
|
136
|
+
case base === 'mp4a' && profile === 'b2':
|
|
137
|
+
return 'dtsx'; // DTS:X
|
|
138
|
+
case base === 'mp4a' && profile === 'a9':
|
|
139
|
+
return 'dtsc'; // DTS Digital Surround
|
|
140
|
+
case base === 'avc1':
|
|
141
|
+
case base === 'avc3':
|
|
142
|
+
return 'avc'; // H264
|
|
143
|
+
case base === 'hvc1':
|
|
144
|
+
case base === 'hev1':
|
|
145
|
+
return 'hevc'; // H265
|
|
146
|
+
case base === 'dvh1':
|
|
147
|
+
case base === 'dvhe':
|
|
148
|
+
return 'dovi'; // Dolby Vision
|
|
149
|
+
}
|
|
150
|
+
return base;
|
|
151
|
+
}
|
|
152
|
+
|
|
97
153
|
/**
|
|
98
154
|
* Get the base codec from a codec string.
|
|
99
155
|
*
|
|
@@ -150,7 +206,7 @@ shaka.util.MimeUtils = class {
|
|
|
150
206
|
|
|
151
207
|
const base = parts[0];
|
|
152
208
|
|
|
153
|
-
parts.
|
|
209
|
+
parts.shift();
|
|
154
210
|
const profile = parts.join('.');
|
|
155
211
|
|
|
156
212
|
// Make sure that we always return a "base" and "profile".
|