shaka-player 4.7.13 → 4.7.15
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/AUTHORS +1 -0
- package/CONTRIBUTORS +2 -0
- package/dist/cast-boot.js +37 -0
- package/dist/deps.js +5 -4
- package/dist/locales.js +31 -31
- package/dist/shaka-player.compiled.d.ts +37 -14
- package/dist/shaka-player.compiled.debug.d.ts +37 -14
- package/dist/shaka-player.compiled.debug.externs.js +14 -0
- package/dist/shaka-player.compiled.debug.js +360 -350
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +14 -0
- package/dist/shaka-player.compiled.js +1107 -1105
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +37 -14
- package/dist/shaka-player.ui.debug.d.ts +37 -14
- package/dist/shaka-player.ui.debug.externs.js +14 -0
- package/dist/shaka-player.ui.debug.js +280 -269
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +14 -0
- package/dist/shaka-player.ui.js +1267 -1265
- package/dist/shaka-player.ui.map +4 -4
- package/externs/cast-namespace.js +14 -0
- package/externs/{chromecast.js → cast-sdk-v2.js} +0 -4
- package/externs/cast-sdk-v3.js +35 -0
- package/externs/ima.js +2 -0
- package/externs/mediatailor.js +0 -0
- package/externs/{cast.js → remote-playback.js} +2 -2
- package/externs/shaka/player.js +5 -2
- package/lib/ads/client_side_ad_manager.js +9 -5
- package/lib/cea/cea608_data_channel.js +9 -0
- package/lib/cea/cea_decoder.js +9 -2
- package/lib/dash/segment_template.js +30 -2
- package/lib/hls/hls_parser.js +19 -10
- package/lib/media/closed_caption_parser.js +16 -4
- package/lib/media/drm_engine.js +17 -3
- package/lib/media/gap_jumping_controller.js +9 -1
- package/lib/media/media_source_engine.js +20 -13
- package/lib/media/play_rate_controller.js +36 -8
- package/lib/media/playhead.js +3 -3
- package/lib/media/segment_index.js +10 -0
- package/lib/media/segment_reference.js +12 -0
- package/lib/media/stall_detector.js +3 -0
- package/lib/media/streaming_engine.js +8 -14
- package/lib/net/http_fetch_plugin.js +3 -1
- package/lib/net/http_xhr_plugin.js +6 -1
- package/lib/player.js +111 -53
- package/lib/polyfill/media_capabilities.js +7 -5
- package/lib/polyfill/patchedmediakeys_cert.js +62 -0
- package/lib/text/ui_text_displayer.js +10 -1
- package/lib/transmuxer/ts_transmuxer.js +6 -24
- package/lib/util/object_utils.js +1 -2
- package/lib/util/platform.js +100 -11
- package/lib/util/stream_utils.js +31 -14
- package/package.json +7 -2
- package/support.html +2 -0
- package/ui/audio_language_selection.js +4 -0
- package/ui/range_element.js +8 -2
- package/ui/resolution_selection.js +5 -0
- package/ui/text_selection.js +4 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2024 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @fileoverview Google Cast namespace definitions, shared by other externs
|
|
9
|
+
* files.
|
|
10
|
+
* @externs
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** @const */
|
|
14
|
+
var cast = {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2024 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @fileoverview Externs for the limited subset of the Cast Application
|
|
9
|
+
* Framework (Receiver SDK v3) that we use in our test infrastructure.
|
|
10
|
+
*
|
|
11
|
+
* @externs
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** @const */
|
|
15
|
+
cast.framework = {};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {{
|
|
19
|
+
* statusText: string,
|
|
20
|
+
* disableIdleTimeout: boolean,
|
|
21
|
+
* skipPlayersLoad: boolean
|
|
22
|
+
* }}
|
|
23
|
+
*/
|
|
24
|
+
cast.framework.CastReceiverOptions;
|
|
25
|
+
|
|
26
|
+
cast.framework.CastReceiverContext = class {
|
|
27
|
+
/** @return {!cast.framework.CastReceiverContext} */
|
|
28
|
+
static getInstance() {}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {!cast.framework.CastReceiverOptions} options
|
|
32
|
+
* @return {!cast.framework.CastReceiverContext}
|
|
33
|
+
*/
|
|
34
|
+
start(options) {}
|
|
35
|
+
};
|
package/externs/ima.js
CHANGED
package/externs/mediatailor.js
CHANGED
|
File without changes
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @fileoverview Externs for HTMLMediaElement related to
|
|
9
|
-
* missing in the Closure compiler.
|
|
8
|
+
* @fileoverview Externs for HTMLMediaElement related to remote playback which
|
|
9
|
+
* were missing in the Closure compiler.
|
|
10
10
|
*
|
|
11
11
|
* @externs
|
|
12
12
|
*/
|
package/externs/shaka/player.js
CHANGED
|
@@ -1376,11 +1376,14 @@ shaka.extern.AdsConfiguration;
|
|
|
1376
1376
|
* @property {boolean} enabled
|
|
1377
1377
|
* If true, enable adaptation by the current AbrManager. Defaults to true.
|
|
1378
1378
|
* @property {boolean} useNetworkInformation
|
|
1379
|
-
* If true, use Network Information API in the current AbrManager
|
|
1379
|
+
* If true, use the Network Information API in the current AbrManager, if it
|
|
1380
|
+
* is available in the browser environment. If the Network Information API is
|
|
1381
|
+
* used, Shaka Player will ignore the defaultBandwidthEstimate config.
|
|
1380
1382
|
* Defaults to true.
|
|
1381
1383
|
* @property {number} defaultBandwidthEstimate
|
|
1382
1384
|
* The default bandwidth estimate to use if there is not enough data, in
|
|
1383
|
-
* bit/sec.
|
|
1385
|
+
* bit/sec. Only used if useNetworkInformation is false, or if the Network
|
|
1386
|
+
* Information API is not available.
|
|
1384
1387
|
* @property {shaka.extern.Restrictions} restrictions
|
|
1385
1388
|
* The restrictions to apply to ABR decisions. These are "soft" restrictions.
|
|
1386
1389
|
* Any track that fails to meet these restrictions will not be selected
|
|
@@ -63,16 +63,17 @@ shaka.ads.ClientSideAdManager = class {
|
|
|
63
63
|
|
|
64
64
|
google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true);
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
/** @private {!google.ima.AdDisplayContainer} */
|
|
67
|
+
this.adDisplayContainer_ = new google.ima.AdDisplayContainer(
|
|
67
68
|
this.adContainer_,
|
|
68
69
|
this.video_);
|
|
69
70
|
|
|
70
71
|
// TODO: IMA: Must be done as the result of a user action on mobile
|
|
71
|
-
|
|
72
|
+
this.adDisplayContainer_.initialize();
|
|
72
73
|
|
|
73
74
|
// IMA: This instance should be re-used for the entire lifecycle of
|
|
74
75
|
// the page.
|
|
75
|
-
this.adsLoader_ = new google.ima.AdsLoader(
|
|
76
|
+
this.adsLoader_ = new google.ima.AdsLoader(this.adDisplayContainer_);
|
|
76
77
|
|
|
77
78
|
this.adsLoader_.getSettings().setPlayerType('shaka-player');
|
|
78
79
|
this.adsLoader_.getSettings().setPlayerVersion(shaka.Player.version);
|
|
@@ -176,6 +177,7 @@ shaka.ads.ClientSideAdManager = class {
|
|
|
176
177
|
this.imaAdsManager_.destroy();
|
|
177
178
|
}
|
|
178
179
|
this.adsLoader_.destroy();
|
|
180
|
+
this.adDisplayContainer_.destroy();
|
|
179
181
|
}
|
|
180
182
|
|
|
181
183
|
/**
|
|
@@ -533,7 +535,9 @@ shaka.ads.ClientSideAdManager = class {
|
|
|
533
535
|
shaka.ads.AdManager.AD_STARTED, data));
|
|
534
536
|
if (this.ad_.isLinear()) {
|
|
535
537
|
this.adContainer_.setAttribute('ad-active', 'true');
|
|
536
|
-
this.
|
|
538
|
+
if (!this.config_.customPlayheadTracker) {
|
|
539
|
+
this.video_.pause();
|
|
540
|
+
}
|
|
537
541
|
if (this.video_.muted) {
|
|
538
542
|
this.ad_.setInitialMuted(this.video_.volume);
|
|
539
543
|
} else {
|
|
@@ -551,7 +555,7 @@ shaka.ads.ClientSideAdManager = class {
|
|
|
551
555
|
(new Map()).set('originalEvent', e)));
|
|
552
556
|
if (this.ad_ && this.ad_.isLinear()) {
|
|
553
557
|
this.adContainer_.removeAttribute('ad-active');
|
|
554
|
-
if (!this.video_.ended) {
|
|
558
|
+
if (!this.config_.customPlayheadTracker && !this.video_.ended) {
|
|
555
559
|
this.video_.play();
|
|
556
560
|
}
|
|
557
561
|
}
|
|
@@ -642,6 +642,15 @@ shaka.cea.Cea608DataChannel = class {
|
|
|
642
642
|
// b1 -> |P|0|0|1|X|X|X|X|
|
|
643
643
|
return (b1 & 0x70) === 0x10;
|
|
644
644
|
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Checks if the data contains a XDS control code.
|
|
648
|
+
* @param {number} b1 Byte 1.
|
|
649
|
+
* @return {boolean}
|
|
650
|
+
*/
|
|
651
|
+
static isXdsControlCode(b1) {
|
|
652
|
+
return b1 >= 0x01 && b1 <= 0x0F;
|
|
653
|
+
}
|
|
645
654
|
};
|
|
646
655
|
|
|
647
656
|
/**
|
package/lib/cea/cea_decoder.js
CHANGED
|
@@ -97,6 +97,7 @@ shaka.cea.CeaDecoder = class {
|
|
|
97
97
|
* @override
|
|
98
98
|
*/
|
|
99
99
|
clear() {
|
|
100
|
+
shaka.log.debug('Clearing CEA decoder');
|
|
100
101
|
this.badFrames_ = 0;
|
|
101
102
|
this.cea608DataArray_ = [];
|
|
102
103
|
this.cea708DataArray_ = [];
|
|
@@ -113,6 +114,7 @@ shaka.cea.CeaDecoder = class {
|
|
|
113
114
|
* Resets the decoder.
|
|
114
115
|
*/
|
|
115
116
|
reset() {
|
|
117
|
+
shaka.log.debug('Resetting CEA decoder');
|
|
116
118
|
this.currentField1Channel_ = 0;
|
|
117
119
|
this.currentField2Channel_ = 0;
|
|
118
120
|
for (const stream of this.cea608ModeToStream_.values()) {
|
|
@@ -128,6 +130,7 @@ shaka.cea.CeaDecoder = class {
|
|
|
128
130
|
*/
|
|
129
131
|
extract(userDataSeiMessage, pts) {
|
|
130
132
|
if (this.waitingForFirstPacket_) {
|
|
133
|
+
shaka.log.debug('Setting first pts value to', pts);
|
|
131
134
|
for (const stream of this.cea608ModeToStream_.values()) {
|
|
132
135
|
stream.firstPts(pts);
|
|
133
136
|
}
|
|
@@ -304,16 +307,20 @@ shaka.cea.CeaDecoder = class {
|
|
|
304
307
|
ccPacket.ccData1 &= 0x7f;
|
|
305
308
|
ccPacket.ccData2 &= 0x7f;
|
|
306
309
|
|
|
310
|
+
// Ignore XDS
|
|
311
|
+
if (shaka.cea.Cea608DataChannel.isXdsControlCode(ccPacket.ccData1)) {
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
|
|
307
315
|
// Check for empty captions and skip them.
|
|
308
316
|
if (!ccPacket.ccData1 && !ccPacket.ccData2) {
|
|
309
317
|
return null;
|
|
310
318
|
}
|
|
311
319
|
|
|
312
|
-
this.streams_.add(selectedMode);
|
|
313
|
-
|
|
314
320
|
// Process the clean CC data pair.
|
|
315
321
|
let parsedClosedCaption = null;
|
|
316
322
|
if (shaka.cea.Cea608DataChannel.isControlCode(ccPacket.ccData1)) {
|
|
323
|
+
this.streams_.add(selectedMode);
|
|
317
324
|
parsedClosedCaption = selectedStream.handleControlCode(ccPacket);
|
|
318
325
|
} else {
|
|
319
326
|
// Handle as a Basic North American Character.
|
|
@@ -394,6 +394,11 @@ shaka.dash.SegmentTemplate = class {
|
|
|
394
394
|
Math.ceil(availablePeriodTimes[1] / segmentDuration) - 1,
|
|
395
395
|
];
|
|
396
396
|
|
|
397
|
+
// For Low Latency we can request the partial current position.
|
|
398
|
+
if (context.representation.availabilityTimeOffset) {
|
|
399
|
+
availablePeriodPositions[1]++;
|
|
400
|
+
}
|
|
401
|
+
|
|
397
402
|
// In absolute positions.
|
|
398
403
|
const availablePresentationPositions =
|
|
399
404
|
availablePeriodPositions.map((x) => x + startNumber);
|
|
@@ -497,7 +502,12 @@ shaka.dash.SegmentTemplate = class {
|
|
|
497
502
|
// references were generated. Otherwise, the update would start creating
|
|
498
503
|
// negative positions for segments in periods which begin in the future.
|
|
499
504
|
let nextPosition = Math.max(minPosition, maxPosition + 1);
|
|
500
|
-
|
|
505
|
+
let updateTime = segmentDuration;
|
|
506
|
+
// For low latency we need to evict very frequently.
|
|
507
|
+
if (context.representation.availabilityTimeOffset) {
|
|
508
|
+
updateTime = 0.1;
|
|
509
|
+
}
|
|
510
|
+
segmentIndex.updateEvery(updateTime, () => {
|
|
501
511
|
// Evict any references outside the window.
|
|
502
512
|
const availabilityStartTime =
|
|
503
513
|
presentationTimeline.getSegmentAvailabilityStart();
|
|
@@ -696,7 +706,7 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
|
|
|
696
706
|
*/
|
|
697
707
|
appendTemplateInfo(info, periodStart, periodEnd, shouldFit,
|
|
698
708
|
initSegmentReference) {
|
|
699
|
-
this.
|
|
709
|
+
this.updateInitSegmentReference(initSegmentReference);
|
|
700
710
|
if (!this.templateInfo_) {
|
|
701
711
|
this.templateInfo_ = info;
|
|
702
712
|
this.periodStart_ = periodStart;
|
|
@@ -727,6 +737,24 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
|
|
|
727
737
|
}
|
|
728
738
|
}
|
|
729
739
|
|
|
740
|
+
/**
|
|
741
|
+
* Updates the init segment reference and propagates the update to all
|
|
742
|
+
* references.
|
|
743
|
+
* @param {shaka.media.InitSegmentReference} initSegmentReference
|
|
744
|
+
*/
|
|
745
|
+
updateInitSegmentReference(initSegmentReference) {
|
|
746
|
+
if (this.initSegmentReference_ === initSegmentReference) {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
this.initSegmentReference_ = initSegmentReference;
|
|
751
|
+
for (const reference of this.references) {
|
|
752
|
+
if (reference) {
|
|
753
|
+
reference.updateInitSegmentReference(initSegmentReference);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
730
758
|
/**
|
|
731
759
|
*
|
|
732
760
|
* @param {number} time
|
package/lib/hls/hls_parser.js
CHANGED
|
@@ -891,13 +891,12 @@ shaka.hls.HlsParser = class {
|
|
|
891
891
|
if (!playlist.segments.length) {
|
|
892
892
|
return defaultBasicInfo;
|
|
893
893
|
}
|
|
894
|
-
const
|
|
895
|
-
const
|
|
896
|
-
const middleSegmentUris = shaka.hls.Utils.constructSegmentUris(
|
|
894
|
+
const firstSegment = playlist.segments[0];
|
|
895
|
+
const firstSegmentUris = shaka.hls.Utils.constructSegmentUris(
|
|
897
896
|
getUris(),
|
|
898
|
-
|
|
899
|
-
const
|
|
900
|
-
const parsedUri = new goog.Uri(
|
|
897
|
+
firstSegment.verbatimSegmentUri);
|
|
898
|
+
const firstSegmentUri = firstSegmentUris[0];
|
|
899
|
+
const parsedUri = new goog.Uri(firstSegmentUri);
|
|
901
900
|
const extension = parsedUri.getPath().split('.').pop();
|
|
902
901
|
const rawMimeType = HlsParser.RAW_FORMATS_TO_MIME_TYPES_[extension];
|
|
903
902
|
if (rawMimeType) {
|
|
@@ -909,7 +908,7 @@ shaka.hls.HlsParser = class {
|
|
|
909
908
|
|
|
910
909
|
let initData = null;
|
|
911
910
|
const initSegmentRef = this.getInitSegmentReference_(
|
|
912
|
-
playlist,
|
|
911
|
+
playlist, firstSegment.tags, getUris);
|
|
913
912
|
this.mapTagToInitSegmentRefMap_.clear();
|
|
914
913
|
if (initSegmentRef) {
|
|
915
914
|
const initSegmentRequest = shaka.util.Networking.createSegmentRequest(
|
|
@@ -924,8 +923,17 @@ shaka.hls.HlsParser = class {
|
|
|
924
923
|
initData = initResponse.data;
|
|
925
924
|
}
|
|
926
925
|
|
|
927
|
-
|
|
928
|
-
|
|
926
|
+
let startByte = 0;
|
|
927
|
+
let endByte = null;
|
|
928
|
+
const byterangeTag = shaka.hls.Utils.getFirstTagWithName(
|
|
929
|
+
firstSegment.tags, 'EXT-X-BYTERANGE');
|
|
930
|
+
if (byterangeTag) {
|
|
931
|
+
[startByte, endByte] = this.parseByteRange_(
|
|
932
|
+
/* previousReference= */ null, byterangeTag.value);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
const segmentRequest = shaka.util.Networking.createSegmentRequest(
|
|
936
|
+
firstSegmentUris, startByte, endByte, this.config_.retryParameters);
|
|
929
937
|
const type = shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_SEGMENT;
|
|
930
938
|
const response = await this.makeNetworkRequest_(
|
|
931
939
|
segmentRequest, requestType, {type});
|
|
@@ -943,7 +951,8 @@ shaka.hls.HlsParser = class {
|
|
|
943
951
|
return basicInfo;
|
|
944
952
|
}
|
|
945
953
|
} else if (extension == 'mp4' || extension == 'cmfv' ||
|
|
946
|
-
extension == 'm4s' ||
|
|
954
|
+
extension == 'm4s' || extension == 'fmp4' ||
|
|
955
|
+
contentMimeType == 'video/mp4' ||
|
|
947
956
|
contentMimeType == 'audio/mp4' ||
|
|
948
957
|
contentMimeType == 'video/iso.segment') {
|
|
949
958
|
const basicInfo = shaka.media.SegmentUtils.getBasicInfoFromMp4(
|
|
@@ -9,6 +9,7 @@ goog.provide('shaka.media.IClosedCaptionParser');
|
|
|
9
9
|
|
|
10
10
|
goog.require('shaka.cea.DummyCaptionDecoder');
|
|
11
11
|
goog.require('shaka.cea.DummyCeaParser');
|
|
12
|
+
goog.require('shaka.log');
|
|
12
13
|
goog.require('shaka.util.BufferUtils');
|
|
13
14
|
|
|
14
15
|
|
|
@@ -22,10 +23,13 @@ goog.require('shaka.util.BufferUtils');
|
|
|
22
23
|
*/
|
|
23
24
|
shaka.media.IClosedCaptionParser = class {
|
|
24
25
|
/**
|
|
25
|
-
* Initialize the caption parser. This should be called
|
|
26
|
+
* Initialize the caption parser. This should be called whenever new init
|
|
27
|
+
* segment arrives.
|
|
26
28
|
* @param {BufferSource} initSegment
|
|
29
|
+
* @param {boolean=} adaptation True if we just automatically switched active
|
|
30
|
+
* variant(s).
|
|
27
31
|
*/
|
|
28
|
-
init(initSegment) {}
|
|
32
|
+
init(initSegment, adaptation = false) {}
|
|
29
33
|
|
|
30
34
|
/**
|
|
31
35
|
* Parses embedded CEA closed captions and interacts with the underlying
|
|
@@ -87,8 +91,16 @@ shaka.media.ClosedCaptionParser = class {
|
|
|
87
91
|
/**
|
|
88
92
|
* @override
|
|
89
93
|
*/
|
|
90
|
-
init(initSegment) {
|
|
91
|
-
|
|
94
|
+
init(initSegment, adaptation = false) {
|
|
95
|
+
shaka.log.debug('Passing new init segment to CEA parser');
|
|
96
|
+
if (!adaptation) {
|
|
97
|
+
// Reset underlying decoder when new init segment arrives
|
|
98
|
+
// to clear stored pts values.
|
|
99
|
+
// This is necessary when a new Period comes in DASH or a discontinuity
|
|
100
|
+
// in HLS.
|
|
101
|
+
this.reset();
|
|
102
|
+
this.ceaParser_.init(initSegment);
|
|
103
|
+
}
|
|
92
104
|
}
|
|
93
105
|
|
|
94
106
|
/**
|
package/lib/media/drm_engine.js
CHANGED
|
@@ -1551,6 +1551,9 @@ shaka.media.DrmEngine = class {
|
|
|
1551
1551
|
const req = this.playerInterface_.netEngine.request(requestType, request);
|
|
1552
1552
|
response = await req.promise;
|
|
1553
1553
|
} catch (error) {
|
|
1554
|
+
if (this.destroyer_.destroyed()) {
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1554
1557
|
// Request failed!
|
|
1555
1558
|
goog.asserts.assert(error instanceof shaka.util.Error,
|
|
1556
1559
|
'Wrong NetworkingEngine error type!');
|
|
@@ -1559,9 +1562,20 @@ shaka.media.DrmEngine = class {
|
|
|
1559
1562
|
shaka.util.Error.Category.DRM,
|
|
1560
1563
|
shaka.util.Error.Code.LICENSE_REQUEST_FAILED,
|
|
1561
1564
|
error);
|
|
1562
|
-
this.
|
|
1563
|
-
|
|
1564
|
-
metadata.updatePromise
|
|
1565
|
+
if (this.activeSessions_.size == 1) {
|
|
1566
|
+
this.onError_(shakaErr);
|
|
1567
|
+
if (metadata && metadata.updatePromise) {
|
|
1568
|
+
metadata.updatePromise.reject(shakaErr);
|
|
1569
|
+
}
|
|
1570
|
+
} else {
|
|
1571
|
+
if (metadata && metadata.updatePromise) {
|
|
1572
|
+
metadata.updatePromise.reject(shakaErr);
|
|
1573
|
+
}
|
|
1574
|
+
this.activeSessions_.delete(session);
|
|
1575
|
+
if (this.areAllSessionsLoaded_()) {
|
|
1576
|
+
this.allSessionsLoaded_.resolve();
|
|
1577
|
+
this.keyStatusTimer_.tickAfter(/* seconds= */ 0.1);
|
|
1578
|
+
}
|
|
1565
1579
|
}
|
|
1566
1580
|
return;
|
|
1567
1581
|
}
|
|
@@ -13,6 +13,7 @@ goog.require('shaka.media.TimeRangesUtils');
|
|
|
13
13
|
goog.require('shaka.util.EventManager');
|
|
14
14
|
goog.require('shaka.util.FakeEvent');
|
|
15
15
|
goog.require('shaka.util.IReleasable');
|
|
16
|
+
goog.require('shaka.util.Platform');
|
|
16
17
|
goog.require('shaka.util.Timer');
|
|
17
18
|
|
|
18
19
|
|
|
@@ -217,7 +218,14 @@ shaka.media.GapJumpingController = class {
|
|
|
217
218
|
|
|
218
219
|
// StreamingEngine can buffer past the seek end, but still don't allow
|
|
219
220
|
// seeking past it.
|
|
220
|
-
|
|
221
|
+
let jumpTo = buffered.start(gapIndex);
|
|
222
|
+
// Workaround for Xbox with Legacy Edge. On this platform video element
|
|
223
|
+
// often rounds value we want to set as currentTime and we are not able
|
|
224
|
+
// to jump over the gap.
|
|
225
|
+
if (shaka.util.Platform.isLegacyEdge() ||
|
|
226
|
+
shaka.util.Platform.isXboxOne()) {
|
|
227
|
+
jumpTo = Math.ceil((jumpTo + 0.01) * 100) / 100;
|
|
228
|
+
}
|
|
221
229
|
const seekEnd = this.timeline_.getSeekRangeEnd();
|
|
222
230
|
if (jumpTo >= seekEnd) {
|
|
223
231
|
return;
|
|
@@ -280,7 +280,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
280
280
|
if (contentType == ContentType.VIDEO) {
|
|
281
281
|
codecs = StreamUtils.getCorrectVideoCodecs(codecs);
|
|
282
282
|
} else if (contentType == ContentType.AUDIO) {
|
|
283
|
-
codecs = StreamUtils.getCorrectAudioCodecs(codecs);
|
|
283
|
+
codecs = StreamUtils.getCorrectAudioCodecs(codecs, mimeType);
|
|
284
284
|
}
|
|
285
285
|
const extendedMimeType = MimeUtils.getExtendedType(
|
|
286
286
|
stream, mimeType, codecs);
|
|
@@ -347,14 +347,11 @@ shaka.media.MediaSourceEngine = class {
|
|
|
347
347
|
|
|
348
348
|
const support = {};
|
|
349
349
|
for (const type of testMimeTypes) {
|
|
350
|
-
if (shaka.
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
support[type] = shaka.media.Capabilities.isTypeSupported(type) ||
|
|
356
|
-
shaka.transmuxer.TransmuxerEngine.isSupported(type);
|
|
357
|
-
}
|
|
350
|
+
if (shaka.text.TextEngine.isTypeSupported(type)) {
|
|
351
|
+
support[type] = true;
|
|
352
|
+
} else if (shaka.util.Platform.supportsMediaSource()) {
|
|
353
|
+
support[type] = shaka.media.Capabilities.isTypeSupported(type) ||
|
|
354
|
+
shaka.transmuxer.TransmuxerEngine.isSupported(type);
|
|
358
355
|
} else {
|
|
359
356
|
support[type] = shaka.util.Platform.supportsMediaType(type);
|
|
360
357
|
}
|
|
@@ -961,7 +958,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
961
958
|
// If it is the init segment for closed captions, initialize the closed
|
|
962
959
|
// caption parser.
|
|
963
960
|
if (!reference) {
|
|
964
|
-
this.captionParser_.init(data);
|
|
961
|
+
this.captionParser_.init(data, adaptation);
|
|
965
962
|
} else {
|
|
966
963
|
const closedCaptions = this.captionParser_.parseFrom(data);
|
|
967
964
|
if (closedCaptions.length) {
|
|
@@ -1839,12 +1836,17 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1839
1836
|
|
|
1840
1837
|
const totalOfBuffers = streamsByType.size;
|
|
1841
1838
|
let numberOfSourceBufferAdded = 0;
|
|
1842
|
-
|
|
1839
|
+
const onSourceBufferAdded = () => {
|
|
1843
1840
|
numberOfSourceBufferAdded++;
|
|
1844
1841
|
if (numberOfSourceBufferAdded === totalOfBuffers) {
|
|
1845
1842
|
sourceBufferAdded.resolve();
|
|
1843
|
+
this.eventManager_.unlisten(sourceBuffers, 'addsourcebuffer',
|
|
1844
|
+
onSourceBufferAdded);
|
|
1846
1845
|
}
|
|
1847
|
-
}
|
|
1846
|
+
};
|
|
1847
|
+
|
|
1848
|
+
this.eventManager_.listen(sourceBuffers, 'addsourcebuffer',
|
|
1849
|
+
onSourceBufferAdded);
|
|
1848
1850
|
|
|
1849
1851
|
for (const contentType of streamsByType.keys()) {
|
|
1850
1852
|
const stream = streamsByType.get(contentType);
|
|
@@ -1868,7 +1870,12 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1868
1870
|
this.destroyer_.ensureNotDestroyed();
|
|
1869
1871
|
|
|
1870
1872
|
this.eventManager_.listenOnce(this.video_, 'canplaythrough', () => {
|
|
1871
|
-
|
|
1873
|
+
// Don't use ensureNotDestroyed() from this event listener, because
|
|
1874
|
+
// that results in an uncaught exception. Instead, just check the
|
|
1875
|
+
// flag.
|
|
1876
|
+
if (this.destroyer_.destroyed()) {
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1872
1879
|
|
|
1873
1880
|
this.video_.autoplay = previousAutoPlayState;
|
|
1874
1881
|
if (!previousPausedState) {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
goog.provide('shaka.media.PlayRateController');
|
|
8
8
|
|
|
9
|
+
goog.require('goog.asserts');
|
|
9
10
|
goog.require('shaka.log');
|
|
10
11
|
goog.require('shaka.util.IReleasable');
|
|
11
12
|
goog.require('shaka.util.Timer');
|
|
@@ -27,6 +28,9 @@ shaka.media.PlayRateController = class {
|
|
|
27
28
|
/** @private {?shaka.media.PlayRateController.Harness} */
|
|
28
29
|
this.harness_ = harness;
|
|
29
30
|
|
|
31
|
+
/** @private {boolean} */
|
|
32
|
+
this.isBuffering_ = false;
|
|
33
|
+
|
|
30
34
|
/** @private {number} */
|
|
31
35
|
this.rate_ = this.harness_.getRate();
|
|
32
36
|
|
|
@@ -51,15 +55,25 @@ shaka.media.PlayRateController = class {
|
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
/**
|
|
54
|
-
*
|
|
58
|
+
* Sets the buffering flag, which controls the effective playback rate.
|
|
59
|
+
*
|
|
60
|
+
* @param {boolean} isBuffering If true, forces playback rate to 0 internally.
|
|
61
|
+
*/
|
|
62
|
+
setBuffering(isBuffering) {
|
|
63
|
+
this.isBuffering_ = isBuffering;
|
|
64
|
+
this.apply_();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Set the playback rate. This rate will only be used as provided when the
|
|
69
|
+
* player is not buffering. You should never set the rate to 0.
|
|
55
70
|
*
|
|
56
71
|
* @param {number} rate
|
|
57
72
|
*/
|
|
58
73
|
set(rate) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
74
|
+
goog.asserts.assert(rate != 0, 'Should never set rate of 0 explicitly!');
|
|
75
|
+
this.rate_ = rate;
|
|
76
|
+
this.apply_();
|
|
63
77
|
}
|
|
64
78
|
|
|
65
79
|
/**
|
|
@@ -93,11 +107,14 @@ shaka.media.PlayRateController = class {
|
|
|
93
107
|
// Always stop the timer. We may not start it again.
|
|
94
108
|
this.timer_.stop();
|
|
95
109
|
|
|
96
|
-
|
|
110
|
+
/** @type {number} */
|
|
111
|
+
const rate = this.calculateCurrentRate_();
|
|
112
|
+
|
|
113
|
+
shaka.log.v1('Changing effective playback rate to', rate);
|
|
97
114
|
|
|
98
|
-
if (
|
|
115
|
+
if (rate >= 0) {
|
|
99
116
|
try {
|
|
100
|
-
this.applyRate_(
|
|
117
|
+
this.applyRate_(rate);
|
|
101
118
|
return;
|
|
102
119
|
} catch (e) {
|
|
103
120
|
// Fall through to the next clause.
|
|
@@ -117,6 +134,17 @@ shaka.media.PlayRateController = class {
|
|
|
117
134
|
this.applyRate_(0);
|
|
118
135
|
}
|
|
119
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Calculate the rate that the controller wants the media element to have
|
|
139
|
+
* based on the current state of the controller.
|
|
140
|
+
*
|
|
141
|
+
* @return {number}
|
|
142
|
+
* @private
|
|
143
|
+
*/
|
|
144
|
+
calculateCurrentRate_() {
|
|
145
|
+
return this.isBuffering_ ? 0 : this.rate_;
|
|
146
|
+
}
|
|
147
|
+
|
|
120
148
|
/**
|
|
121
149
|
* If the new rate is different than the media element's playback rate, this
|
|
122
150
|
* will change the playback rate. If the rate does not need to change, it will
|
package/lib/media/playhead.js
CHANGED
|
@@ -382,10 +382,10 @@ shaka.media.MediaSourcePlayhead = class {
|
|
|
382
382
|
seekStart = seekEnd - this.minSeekRange_;
|
|
383
383
|
}
|
|
384
384
|
|
|
385
|
-
if (currentTime < seekStart
|
|
386
|
-
//
|
|
385
|
+
if (currentTime < seekStart) {
|
|
386
|
+
// The seek range has moved past the playhead. Move ahead to catch up.
|
|
387
387
|
const targetTime = this.reposition_(currentTime);
|
|
388
|
-
shaka.log.info('Jumping
|
|
388
|
+
shaka.log.info('Jumping forward ' + (targetTime - currentTime) +
|
|
389
389
|
' seconds to catch up with the seek range.');
|
|
390
390
|
this.mediaElement_.currentTime = targetTime;
|
|
391
391
|
}
|
|
@@ -694,6 +694,16 @@ shaka.media.MetaSegmentIndex = class extends shaka.media.SegmentIndex {
|
|
|
694
694
|
this.indexes_ = [];
|
|
695
695
|
}
|
|
696
696
|
|
|
697
|
+
/**
|
|
698
|
+
* @override
|
|
699
|
+
* @export
|
|
700
|
+
*/
|
|
701
|
+
forEachTopLevelReference(fn) {
|
|
702
|
+
for (const index of this.indexes_) {
|
|
703
|
+
index.forEachTopLevelReference(fn);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
697
707
|
/**
|
|
698
708
|
* @override
|
|
699
709
|
* @export
|
|
@@ -598,6 +598,18 @@ shaka.media.SegmentReference = class {
|
|
|
598
598
|
this.offset(offset);
|
|
599
599
|
}
|
|
600
600
|
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Updates the init segment reference and propagates the update to all partial
|
|
604
|
+
* references.
|
|
605
|
+
* @param {shaka.media.InitSegmentReference} initSegmentReference
|
|
606
|
+
*/
|
|
607
|
+
updateInitSegmentReference(initSegmentReference) {
|
|
608
|
+
this.initSegmentReference = initSegmentReference;
|
|
609
|
+
for (const partialReference of this.partialReferences) {
|
|
610
|
+
partialReference.updateInitSegmentReference(initSegmentReference);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
601
613
|
};
|
|
602
614
|
|
|
603
615
|
|
|
@@ -174,6 +174,9 @@ shaka.media.StallDetector.MediaElementImplementation = class {
|
|
|
174
174
|
if (this.mediaElement_.paused) {
|
|
175
175
|
return false;
|
|
176
176
|
}
|
|
177
|
+
if (this.mediaElement_.playbackRate == 0) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
177
180
|
|
|
178
181
|
// If we have don't have enough content, we are not stalled, we are
|
|
179
182
|
// buffering.
|