shaka-player 4.9.30 → 4.9.32
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/.eslintrc.js +1 -0
- package/AUTHORS +2 -0
- package/CONTRIBUTORS +2 -0
- package/dist/controls.css +1 -1
- package/dist/controls.css.map +1 -1
- package/dist/deps.js +2 -2
- package/dist/locales.js +33 -33
- package/dist/shaka-player.compiled.d.ts +8 -7
- package/dist/shaka-player.compiled.debug.d.ts +8 -7
- package/dist/shaka-player.compiled.debug.externs.js +3 -0
- package/dist/shaka-player.compiled.debug.js +264 -260
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +3 -0
- package/dist/shaka-player.compiled.js +1214 -1212
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +8 -7
- package/dist/shaka-player.ui.debug.d.ts +8 -7
- package/dist/shaka-player.ui.debug.externs.js +3 -0
- package/dist/shaka-player.ui.debug.js +290 -285
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +3 -0
- package/dist/shaka-player.ui.js +1417 -1414
- package/dist/shaka-player.ui.map +3 -3
- package/externs/shaka/abortable.js +36 -36
- package/lib/dash/segment_template.js +19 -2
- package/lib/hls/hls_parser.js +1 -1
- package/lib/media/adaptation_set_criteria.js +8 -1
- package/lib/media/content_workarounds.js +2 -1
- package/lib/media/drm_engine.js +10 -115
- package/lib/media/media_source_engine.js +99 -36
- package/lib/media/preload_manager.js +6 -1
- package/lib/media/segment_index.js +20 -0
- package/lib/media/segment_prefetch.js +7 -0
- package/lib/media/segment_reference.js +1 -1
- package/lib/media/streaming_engine.js +16 -13
- package/lib/mss/mss_parser.js +6 -0
- package/lib/net/networking_engine.js +3 -3
- package/lib/player.js +16 -2
- package/lib/polyfill/media_capabilities.js +48 -9
- package/lib/polyfill/mediasource.js +12 -0
- package/lib/text/ui_text_displayer.js +8 -0
- package/lib/text/vtt_text_parser.js +2 -8
- package/lib/util/abortable_operation.js +7 -5
- package/lib/util/mp4_generator.js +1 -1
- package/lib/util/platform.js +22 -11
- package/package.json +2 -2
- package/ui/controls.js +2 -2
- package/ui/less/containers.less +8 -0
- package/ui/remote_button.js +42 -17
- package/ui/statistics_button.js +0 -2
|
@@ -18,39 +18,39 @@
|
|
|
18
18
|
* @template T
|
|
19
19
|
* @exportDoc
|
|
20
20
|
*/
|
|
21
|
-
shaka.extern.IAbortableOperation =
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
21
|
+
shaka.extern.IAbortableOperation = class {
|
|
22
|
+
constructor() {
|
|
23
|
+
/**
|
|
24
|
+
* A Promise which represents the underlying operation. It is resolved when
|
|
25
|
+
* the operation is complete, and rejected if the operation fails or is
|
|
26
|
+
* aborted. Aborted operations should be rejected with a shaka.util.Error
|
|
27
|
+
* object using the error code OPERATION_ABORTED.
|
|
28
|
+
*
|
|
29
|
+
* @const {!Promise.<T>}
|
|
30
|
+
* @exportDoc
|
|
31
|
+
*/
|
|
32
|
+
this.promise;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Can be called by anyone holding this object to abort the underlying
|
|
37
|
+
* operation. This is not cancelation, and will not necessarily result in
|
|
38
|
+
* any work being undone. abort() should return a Promise which is resolved
|
|
39
|
+
* when the underlying operation has been aborted. The returned Promise
|
|
40
|
+
* should never be rejected.
|
|
41
|
+
*
|
|
42
|
+
* @return {!Promise}
|
|
43
|
+
* @exportDoc
|
|
44
|
+
*/
|
|
45
|
+
abort() {}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {function(boolean)} onFinal A callback to be invoked after the
|
|
50
|
+
* operation succeeds or fails. The boolean argument is true
|
|
51
|
+
* if the operation succeeded and false if it failed.
|
|
52
|
+
* @return {!shaka.extern.IAbortableOperation.<T>} Returns this.
|
|
53
|
+
* @exportDoc
|
|
54
|
+
*/
|
|
55
|
+
finally(onFinal) {}
|
|
56
|
+
};
|
|
@@ -85,7 +85,9 @@ shaka.dash.SegmentTemplate = class {
|
|
|
85
85
|
},
|
|
86
86
|
};
|
|
87
87
|
} else if (info.segmentDuration) {
|
|
88
|
-
if (!isUpdate &&
|
|
88
|
+
if (!isUpdate &&
|
|
89
|
+
context.adaptationSet.contentType !== 'image' &&
|
|
90
|
+
context.adaptationSet.contentType !== 'text') {
|
|
89
91
|
context.presentationTimeline.notifyMaxSegmentDuration(
|
|
90
92
|
info.segmentDuration);
|
|
91
93
|
context.presentationTimeline.notifyMinSegmentStartTime(
|
|
@@ -161,7 +163,9 @@ shaka.dash.SegmentTemplate = class {
|
|
|
161
163
|
tsi.evict(availabilityStart);
|
|
162
164
|
}
|
|
163
165
|
|
|
164
|
-
if (info.timeline &&
|
|
166
|
+
if (info.timeline &&
|
|
167
|
+
context.adaptationSet.contentType !== 'image' &&
|
|
168
|
+
context.adaptationSet.contentType !== 'text') {
|
|
165
169
|
const timeline = info.timeline;
|
|
166
170
|
context.presentationTimeline.notifyTimeRange(
|
|
167
171
|
timeline,
|
|
@@ -1029,6 +1033,19 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
|
|
|
1029
1033
|
return ref;
|
|
1030
1034
|
}
|
|
1031
1035
|
|
|
1036
|
+
/**
|
|
1037
|
+
* @override
|
|
1038
|
+
*/
|
|
1039
|
+
forEachTopLevelReference(fn) {
|
|
1040
|
+
this.fitTimeline();
|
|
1041
|
+
for (let i = 0; i < this.getNumReferences(); i++) {
|
|
1042
|
+
const reference = this.get(i + this.numEvicted_);
|
|
1043
|
+
if (reference) {
|
|
1044
|
+
fn(reference);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1032
1049
|
/**
|
|
1033
1050
|
* Fill in a specific template with values to get the segment uris
|
|
1034
1051
|
*
|
package/lib/hls/hls_parser.js
CHANGED
|
@@ -3705,7 +3705,7 @@ shaka.hls.HlsParser = class {
|
|
|
3705
3705
|
let aesKey = undefined;
|
|
3706
3706
|
|
|
3707
3707
|
let discontinuitySequence = shaka.hls.Utils.getFirstTagWithNameAsNumber(
|
|
3708
|
-
playlist.tags, 'EXT-X-DISCONTINUITY-SEQUENCE',
|
|
3708
|
+
playlist.tags, 'EXT-X-DISCONTINUITY-SEQUENCE', -1);
|
|
3709
3709
|
const mediaSequenceNumber = shaka.hls.Utils.getFirstTagWithNameAsNumber(
|
|
3710
3710
|
playlist.tags, 'EXT-X-MEDIA-SEQUENCE', 0);
|
|
3711
3711
|
const skipTag = shaka.hls.Utils.getFirstTagWithName(
|
|
@@ -356,7 +356,14 @@ shaka.media.PreferenceBasedCriteria = class {
|
|
|
356
356
|
if (hdrLevel == 'AUTO') {
|
|
357
357
|
// Auto detect the ideal HDR level.
|
|
358
358
|
if (window.matchMedia('(color-gamut: p3)').matches) {
|
|
359
|
-
|
|
359
|
+
const someHLG = variants.some((variant) => {
|
|
360
|
+
if (variant.video && variant.video.hdr &&
|
|
361
|
+
variant.video.hdr == 'HLG') {
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
return false;
|
|
365
|
+
});
|
|
366
|
+
hdrLevel = someHLG ? 'HLG' : 'PQ';
|
|
360
367
|
} else {
|
|
361
368
|
hdrLevel = 'SDR';
|
|
362
369
|
}
|
|
@@ -176,7 +176,8 @@ shaka.media.ContentWorkarounds = class {
|
|
|
176
176
|
// patched one, otherwise video element throws following error:
|
|
177
177
|
// CHUNK_DEMUXER_ERROR_APPEND_FAILED: Sample encryption info is not
|
|
178
178
|
// available.
|
|
179
|
-
if (shaka.util.Platform.isEdge() && shaka.util.Platform.isWindows()
|
|
179
|
+
if (shaka.util.Platform.isEdge() && shaka.util.Platform.isWindows() &&
|
|
180
|
+
!shaka.util.Platform.isXboxOne()) {
|
|
180
181
|
const doubleInitSegment = new Uint8Array(initSegment.byteLength +
|
|
181
182
|
modifiedInitSegment.byteLength);
|
|
182
183
|
doubleInitSegment.set(modifiedInitSegment);
|
package/lib/media/drm_engine.js
CHANGED
|
@@ -9,7 +9,6 @@ goog.provide('shaka.media.DrmEngine');
|
|
|
9
9
|
goog.require('goog.asserts');
|
|
10
10
|
goog.require('shaka.log');
|
|
11
11
|
goog.require('shaka.net.NetworkingEngine');
|
|
12
|
-
goog.require('shaka.transmuxer.TransmuxerEngine');
|
|
13
12
|
goog.require('shaka.util.BufferUtils');
|
|
14
13
|
goog.require('shaka.util.Destroyer');
|
|
15
14
|
goog.require('shaka.util.DrmUtils');
|
|
@@ -20,7 +19,6 @@ goog.require('shaka.util.IDestroyable');
|
|
|
20
19
|
goog.require('shaka.util.Iterables');
|
|
21
20
|
goog.require('shaka.util.ManifestParserUtils');
|
|
22
21
|
goog.require('shaka.util.MapUtils');
|
|
23
|
-
goog.require('shaka.util.MimeUtils');
|
|
24
22
|
goog.require('shaka.util.ObjectUtils');
|
|
25
23
|
goog.require('shaka.util.Platform');
|
|
26
24
|
goog.require('shaka.util.Pssh');
|
|
@@ -41,9 +39,6 @@ shaka.media.DrmEngine = class {
|
|
|
41
39
|
/** @private {?shaka.media.DrmEngine.PlayerInterface} */
|
|
42
40
|
this.playerInterface_ = playerInterface;
|
|
43
41
|
|
|
44
|
-
/** @private {!Set.<string>} */
|
|
45
|
-
this.supportedTypes_ = new Set();
|
|
46
|
-
|
|
47
42
|
/** @private {MediaKeys} */
|
|
48
43
|
this.mediaKeys_ = null;
|
|
49
44
|
|
|
@@ -197,7 +192,6 @@ shaka.media.DrmEngine = class {
|
|
|
197
192
|
|
|
198
193
|
// Break references to everything else we hold internally.
|
|
199
194
|
this.currentDrmInfo_ = null;
|
|
200
|
-
this.supportedTypes_.clear();
|
|
201
195
|
this.mediaKeys_ = null;
|
|
202
196
|
this.storedPersistentSessions_ = new Map();
|
|
203
197
|
this.config_ = null;
|
|
@@ -512,7 +506,14 @@ shaka.media.DrmEngine = class {
|
|
|
512
506
|
this.video_ = video;
|
|
513
507
|
|
|
514
508
|
this.eventManager_.listenOnce(this.video_, 'play', () => this.onPlay_());
|
|
515
|
-
if (
|
|
509
|
+
if (this.video_.remote) {
|
|
510
|
+
this.eventManager_.listen(this.video_.remote, 'connect',
|
|
511
|
+
() => this.closeOpenSessions_());
|
|
512
|
+
this.eventManager_.listen(this.video_.remote, 'connecting',
|
|
513
|
+
() => this.closeOpenSessions_());
|
|
514
|
+
this.eventManager_.listen(this.video_.remote, 'disconnect',
|
|
515
|
+
() => this.closeOpenSessions_());
|
|
516
|
+
} else if ('webkitCurrentPlaybackTargetIsWireless' in this.video_) {
|
|
516
517
|
this.eventManager_.listen(this.video_,
|
|
517
518
|
'webkitcurrentplaybacktargetiswirelesschanged',
|
|
518
519
|
() => this.closeOpenSessions_());
|
|
@@ -773,40 +774,6 @@ shaka.media.DrmEngine = class {
|
|
|
773
774
|
return this.initialized_;
|
|
774
775
|
}
|
|
775
776
|
|
|
776
|
-
/**
|
|
777
|
-
* Check if DrmEngine (as initialized) will likely be able to support the
|
|
778
|
-
* given content type.
|
|
779
|
-
*
|
|
780
|
-
* @param {string} contentType
|
|
781
|
-
* @return {boolean}
|
|
782
|
-
*/
|
|
783
|
-
willSupport(contentType) {
|
|
784
|
-
// Edge 14 does not report correct capabilities. It will only report the
|
|
785
|
-
// first MIME type even if the others are supported. To work around this,
|
|
786
|
-
// we say that Edge supports everything.
|
|
787
|
-
//
|
|
788
|
-
// See https://github.com/shaka-project/shaka-player/issues/1495 for details.
|
|
789
|
-
if (shaka.util.Platform.isLegacyEdge()) {
|
|
790
|
-
return true;
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
contentType = contentType.toLowerCase();
|
|
794
|
-
|
|
795
|
-
if (shaka.util.Platform.isTizen() &&
|
|
796
|
-
contentType.includes('codecs="ac-3"')) {
|
|
797
|
-
// Some Tizen devices seem to misreport AC-3 support. This works around
|
|
798
|
-
// the issue, by falling back to EC-3, which seems to be supported on the
|
|
799
|
-
// same devices and be correctly reported in all cases we have observed.
|
|
800
|
-
// See https://github.com/shaka-project/shaka-player/issues/2989 for
|
|
801
|
-
// details.
|
|
802
|
-
const fallback = contentType.replace('ac-3', 'ec-3');
|
|
803
|
-
return this.supportedTypes_.has(contentType) ||
|
|
804
|
-
this.supportedTypes_.has(fallback);
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
return this.supportedTypes_.has(contentType);
|
|
808
|
-
}
|
|
809
|
-
|
|
810
777
|
/**
|
|
811
778
|
* Returns the ID of the sessions currently active.
|
|
812
779
|
*
|
|
@@ -907,25 +874,6 @@ shaka.media.DrmEngine = class {
|
|
|
907
874
|
return Array.from(this.activeSessions_.keys());
|
|
908
875
|
}
|
|
909
876
|
|
|
910
|
-
|
|
911
|
-
/**
|
|
912
|
-
* @param {shaka.extern.Stream} stream
|
|
913
|
-
* @param {string=} codecOverride
|
|
914
|
-
* @return {string}
|
|
915
|
-
* @private
|
|
916
|
-
*/
|
|
917
|
-
static computeMimeType_(stream, codecOverride) {
|
|
918
|
-
const realMimeType = shaka.util.MimeUtils.getFullType(stream.mimeType,
|
|
919
|
-
codecOverride || stream.codecs);
|
|
920
|
-
const TransmuxerEngine = shaka.transmuxer.TransmuxerEngine;
|
|
921
|
-
if (TransmuxerEngine.isSupported(realMimeType, stream.type)) {
|
|
922
|
-
// This will be handled by the Transmuxer, so use the MIME type that the
|
|
923
|
-
// Transmuxer will produce.
|
|
924
|
-
return TransmuxerEngine.convertCodecs(stream.type, realMimeType);
|
|
925
|
-
}
|
|
926
|
-
return realMimeType;
|
|
927
|
-
}
|
|
928
|
-
|
|
929
877
|
/**
|
|
930
878
|
* @param {!Map.<string, MediaKeySystemConfiguration>} configsByKeySystem
|
|
931
879
|
* A dictionary of configs, indexed by key system, with an iteration order
|
|
@@ -950,11 +898,6 @@ shaka.media.DrmEngine = class {
|
|
|
950
898
|
this.destroyer_.ensureNotDestroyed();
|
|
951
899
|
|
|
952
900
|
try {
|
|
953
|
-
// Get the set of supported content types from the audio and video
|
|
954
|
-
// capabilities. Avoid duplicates so that it is easier to read what is
|
|
955
|
-
// supported.
|
|
956
|
-
this.supportedTypes_.clear();
|
|
957
|
-
|
|
958
901
|
// Store the capabilities of the key system.
|
|
959
902
|
const realConfig = mediaKeySystemAccess.getConfiguration();
|
|
960
903
|
|
|
@@ -962,20 +905,6 @@ shaka.media.DrmEngine = class {
|
|
|
962
905
|
'Got MediaKeySystemAccess with configuration',
|
|
963
906
|
realConfig);
|
|
964
907
|
|
|
965
|
-
const audioCaps = realConfig.audioCapabilities || [];
|
|
966
|
-
const videoCaps = realConfig.videoCapabilities || [];
|
|
967
|
-
|
|
968
|
-
for (const cap of audioCaps) {
|
|
969
|
-
this.supportedTypes_.add(cap.contentType.toLowerCase());
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
for (const cap of videoCaps) {
|
|
973
|
-
this.supportedTypes_.add(cap.contentType.toLowerCase());
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
goog.asserts.assert(this.supportedTypes_.size,
|
|
977
|
-
'We should get at least one supported MIME type');
|
|
978
|
-
|
|
979
908
|
if (variants.length) {
|
|
980
909
|
this.currentDrmInfo_ = this.createDrmInfoByInfos_(
|
|
981
910
|
mediaKeySystemAccess.keySystem,
|
|
@@ -1032,7 +961,6 @@ shaka.media.DrmEngine = class {
|
|
|
1032
961
|
|
|
1033
962
|
// Don't rewrap a shaka.util.Error from earlier in the chain:
|
|
1034
963
|
this.currentDrmInfo_ = null;
|
|
1035
|
-
this.supportedTypes_.clear();
|
|
1036
964
|
if (exception instanceof shaka.util.Error) {
|
|
1037
965
|
throw exception;
|
|
1038
966
|
}
|
|
@@ -1779,6 +1707,7 @@ shaka.media.DrmEngine = class {
|
|
|
1779
1707
|
const testKeySystems = [
|
|
1780
1708
|
'org.w3.clearkey',
|
|
1781
1709
|
'com.widevine.alpha',
|
|
1710
|
+
'com.widevine.alpha.experiment', // Widevine L1 in Windows
|
|
1782
1711
|
'com.microsoft.playready',
|
|
1783
1712
|
'com.microsoft.playready.hardware',
|
|
1784
1713
|
'com.microsoft.playready.recommendation',
|
|
@@ -1803,6 +1732,7 @@ shaka.media.DrmEngine = class {
|
|
|
1803
1732
|
|
|
1804
1733
|
const testRobustness = {
|
|
1805
1734
|
'com.widevine.alpha': widevineRobustness,
|
|
1735
|
+
'com.widevine.alpha.experiment': widevineRobustness,
|
|
1806
1736
|
'com.microsoft.playready.recommendation': playreadyRobustness,
|
|
1807
1737
|
};
|
|
1808
1738
|
|
|
@@ -2074,41 +2004,6 @@ shaka.media.DrmEngine = class {
|
|
|
2074
2004
|
}));
|
|
2075
2005
|
}
|
|
2076
2006
|
|
|
2077
|
-
/**
|
|
2078
|
-
* Check if a variant is likely to be supported by DrmEngine. This will err on
|
|
2079
|
-
* the side of being too accepting and may not reject a variant that it will
|
|
2080
|
-
* later fail to play.
|
|
2081
|
-
*
|
|
2082
|
-
* @param {!shaka.extern.Variant} variant
|
|
2083
|
-
* @return {boolean}
|
|
2084
|
-
*/
|
|
2085
|
-
supportsVariant(variant) {
|
|
2086
|
-
/** @type {?shaka.extern.Stream} */
|
|
2087
|
-
const audio = variant.audio;
|
|
2088
|
-
/** @type {?shaka.extern.Stream} */
|
|
2089
|
-
const video = variant.video;
|
|
2090
|
-
|
|
2091
|
-
if (audio && audio.encrypted) {
|
|
2092
|
-
const audioContentType = shaka.media.DrmEngine.computeMimeType_(audio);
|
|
2093
|
-
if (!this.willSupport(audioContentType)) {
|
|
2094
|
-
return false;
|
|
2095
|
-
}
|
|
2096
|
-
}
|
|
2097
|
-
|
|
2098
|
-
if (video && video.encrypted) {
|
|
2099
|
-
const videoContentType = shaka.media.DrmEngine.computeMimeType_(video);
|
|
2100
|
-
if (!this.willSupport(videoContentType)) {
|
|
2101
|
-
return false;
|
|
2102
|
-
}
|
|
2103
|
-
}
|
|
2104
|
-
|
|
2105
|
-
const keySystem = shaka.util.DrmUtils.keySystem(this.currentDrmInfo_);
|
|
2106
|
-
const drmInfos = this.getVariantDrmInfos_(variant);
|
|
2107
|
-
|
|
2108
|
-
return drmInfos.length == 0 ||
|
|
2109
|
-
drmInfos.some((drmInfo) => drmInfo.keySystem == keySystem);
|
|
2110
|
-
}
|
|
2111
|
-
|
|
2112
2007
|
/**
|
|
2113
2008
|
* Concat the audio and video drmInfos in a variant.
|
|
2114
2009
|
* @param {shaka.extern.Variant} variant
|
|
@@ -149,8 +149,9 @@ shaka.media.MediaSourceEngine = class {
|
|
|
149
149
|
/** @private {?number} */
|
|
150
150
|
this.lastDuration_ = null;
|
|
151
151
|
|
|
152
|
-
/** @private {
|
|
153
|
-
|
|
152
|
+
/** @private {!Object.<shaka.util.ManifestParserUtils.ContentType,
|
|
153
|
+
!shaka.util.TsParser>} */
|
|
154
|
+
this.tsParsers_ = {};
|
|
154
155
|
|
|
155
156
|
/** @private {?number} */
|
|
156
157
|
this.firstVideoTimestamp_ = null;
|
|
@@ -178,6 +179,8 @@ shaka.media.MediaSourceEngine = class {
|
|
|
178
179
|
* @return {!MediaSource}
|
|
179
180
|
*/
|
|
180
181
|
createMediaSource(p) {
|
|
182
|
+
this.streamingAllowed_ = true;
|
|
183
|
+
|
|
181
184
|
/** @type {!MediaSource} */
|
|
182
185
|
let mediaSource;
|
|
183
186
|
|
|
@@ -188,11 +191,13 @@ shaka.media.MediaSourceEngine = class {
|
|
|
188
191
|
|
|
189
192
|
this.eventManager_.listen(
|
|
190
193
|
mediaSource, 'startstreaming', () => {
|
|
194
|
+
shaka.log.info('MMS startstreaming');
|
|
191
195
|
this.streamingAllowed_ = true;
|
|
192
196
|
});
|
|
193
197
|
|
|
194
198
|
this.eventManager_.listen(
|
|
195
199
|
mediaSource, 'endstreaming', () => {
|
|
200
|
+
shaka.log.info('MMS endstreaming');
|
|
196
201
|
this.streamingAllowed_ = false;
|
|
197
202
|
});
|
|
198
203
|
} else {
|
|
@@ -437,7 +442,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
437
442
|
// This object is owned by Player
|
|
438
443
|
this.lcevcDec_ = null;
|
|
439
444
|
|
|
440
|
-
this.
|
|
445
|
+
this.tsParsers_ = {};
|
|
441
446
|
this.playerInterface_ = null;
|
|
442
447
|
}
|
|
443
448
|
|
|
@@ -489,7 +494,12 @@ shaka.media.MediaSourceEngine = class {
|
|
|
489
494
|
this.manifestType_ == shaka.media.ManifestParser.HLS &&
|
|
490
495
|
!this.ignoreManifestTimestampsInSegmentsMode_;
|
|
491
496
|
|
|
492
|
-
this.
|
|
497
|
+
this.tsParsers_ = {};
|
|
498
|
+
this.firstVideoTimestamp_ = null;
|
|
499
|
+
this.firstVideoReferenceStartTime_ = null;
|
|
500
|
+
this.firstAudioTimestamp_ = null;
|
|
501
|
+
this.firstAudioReferenceStartTime_ = null;
|
|
502
|
+
this.audioCompensation_ = new shaka.util.PublicPromise();
|
|
493
503
|
|
|
494
504
|
for (const contentType of streamsByType.keys()) {
|
|
495
505
|
const stream = streamsByType.get(contentType);
|
|
@@ -649,8 +659,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
649
659
|
* @return {?number} The timestamp in seconds, or null if nothing is buffered.
|
|
650
660
|
*/
|
|
651
661
|
bufferStart(contentType) {
|
|
652
|
-
if (this.
|
|
653
|
-
!Object.keys(this.sourceBuffers_).length) {
|
|
662
|
+
if (!Object.keys(this.sourceBuffers_).length) {
|
|
654
663
|
return null;
|
|
655
664
|
}
|
|
656
665
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
@@ -668,7 +677,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
668
677
|
* @return {?number} The timestamp in seconds, or null if nothing is buffered.
|
|
669
678
|
*/
|
|
670
679
|
bufferEnd(contentType) {
|
|
671
|
-
if (this.
|
|
680
|
+
if (!Object.keys(this.sourceBuffers_).length) {
|
|
672
681
|
return null;
|
|
673
682
|
}
|
|
674
683
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
@@ -688,9 +697,6 @@ shaka.media.MediaSourceEngine = class {
|
|
|
688
697
|
* @return {boolean}
|
|
689
698
|
*/
|
|
690
699
|
isBuffered(contentType, time) {
|
|
691
|
-
if (this.reloadingMediaSource_) {
|
|
692
|
-
return false;
|
|
693
|
-
}
|
|
694
700
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
695
701
|
if (contentType == ContentType.TEXT) {
|
|
696
702
|
return this.textEngine_.isBuffered(time);
|
|
@@ -709,9 +715,6 @@ shaka.media.MediaSourceEngine = class {
|
|
|
709
715
|
* @return {number} The amount of time buffered ahead in seconds.
|
|
710
716
|
*/
|
|
711
717
|
bufferedAheadOf(contentType, time) {
|
|
712
|
-
if (this.reloadingMediaSource_) {
|
|
713
|
-
return 0;
|
|
714
|
-
}
|
|
715
718
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
716
719
|
if (contentType == ContentType.TEXT) {
|
|
717
720
|
return this.textEngine_.bufferedAheadOf(time);
|
|
@@ -732,9 +735,9 @@ shaka.media.MediaSourceEngine = class {
|
|
|
732
735
|
const info = {
|
|
733
736
|
total: this.reloadingMediaSource_ ? [] :
|
|
734
737
|
TimeRangesUtils.getBufferedInfo(this.video_.buffered),
|
|
735
|
-
audio:
|
|
738
|
+
audio:
|
|
736
739
|
TimeRangesUtils.getBufferedInfo(this.getBuffered_(ContentType.AUDIO)),
|
|
737
|
-
video:
|
|
740
|
+
video:
|
|
738
741
|
TimeRangesUtils.getBufferedInfo(this.getBuffered_(ContentType.VIDEO)),
|
|
739
742
|
text: [],
|
|
740
743
|
};
|
|
@@ -757,6 +760,9 @@ shaka.media.MediaSourceEngine = class {
|
|
|
757
760
|
* @private
|
|
758
761
|
*/
|
|
759
762
|
getBuffered_(contentType) {
|
|
763
|
+
if (this.reloadingMediaSource_) {
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
760
766
|
try {
|
|
761
767
|
return this.sourceBuffers_[contentType].buffered;
|
|
762
768
|
} catch (exception) {
|
|
@@ -787,13 +793,12 @@ shaka.media.MediaSourceEngine = class {
|
|
|
787
793
|
* @param {?shaka.media.SegmentReference} reference The segment reference
|
|
788
794
|
* we are appending, or null for init segments
|
|
789
795
|
* @param {!string} mimeType
|
|
790
|
-
* @
|
|
791
|
-
* @return {?number}
|
|
796
|
+
* @return {{timestamp: ?number, metadata: !Array.<shaka.extern.ID3Metadata>}}
|
|
792
797
|
* @private
|
|
793
798
|
*/
|
|
794
|
-
getTimestampAndDispatchMetadata_(contentType, data, reference, mimeType
|
|
795
|
-
timestampOffset) {
|
|
799
|
+
getTimestampAndDispatchMetadata_(contentType, data, reference, mimeType) {
|
|
796
800
|
let timestamp = null;
|
|
801
|
+
let metadata = [];
|
|
797
802
|
|
|
798
803
|
const uint8ArrayData = shaka.util.BufferUtils.toUint8(data);
|
|
799
804
|
if (shaka.util.MimeUtils.RAW_FORMATS.includes(mimeType)) {
|
|
@@ -807,7 +812,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
807
812
|
timestamp = Math.round(metadataTimestamp.data) / 1000;
|
|
808
813
|
}
|
|
809
814
|
/** @private {shaka.extern.ID3Metadata} */
|
|
810
|
-
const
|
|
815
|
+
const id3Metadata = {
|
|
811
816
|
cueTime: reference.startTime,
|
|
812
817
|
data: uint8ArrayData,
|
|
813
818
|
frames: frames,
|
|
@@ -815,7 +820,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
815
820
|
pts: reference.startTime,
|
|
816
821
|
};
|
|
817
822
|
this.playerInterface_.onMetadata(
|
|
818
|
-
[
|
|
823
|
+
[id3Metadata], /* offset= */ 0, reference.endTime);
|
|
819
824
|
}
|
|
820
825
|
} else if (mimeType.includes('/mp4') &&
|
|
821
826
|
reference && reference.timestampOffset == 0 &&
|
|
@@ -845,23 +850,19 @@ shaka.media.MediaSourceEngine = class {
|
|
|
845
850
|
}
|
|
846
851
|
} else if (!mimeType.includes('/mp4') && !mimeType.includes('/webm') &&
|
|
847
852
|
shaka.util.TsParser.probe(uint8ArrayData)) {
|
|
848
|
-
if (!this.
|
|
849
|
-
this.
|
|
853
|
+
if (!this.tsParsers_[contentType]) {
|
|
854
|
+
this.tsParsers_[contentType] = new shaka.util.TsParser();
|
|
850
855
|
} else {
|
|
851
|
-
this.
|
|
856
|
+
this.tsParsers_[contentType].clearData();
|
|
852
857
|
}
|
|
853
|
-
const tsParser = this.
|
|
858
|
+
const tsParser = this.tsParsers_[contentType].parse(uint8ArrayData);
|
|
854
859
|
const startTime = tsParser.getStartTime(contentType);
|
|
855
860
|
if (startTime != null) {
|
|
856
861
|
timestamp = startTime;
|
|
857
862
|
}
|
|
858
|
-
|
|
859
|
-
if (metadata.length) {
|
|
860
|
-
this.playerInterface_.onMetadata(metadata, timestampOffset,
|
|
861
|
-
reference ? reference.endTime : null);
|
|
862
|
-
}
|
|
863
|
+
metadata = tsParser.getMetadata();
|
|
863
864
|
}
|
|
864
|
-
return timestamp;
|
|
865
|
+
return {timestamp, metadata};
|
|
865
866
|
}
|
|
866
867
|
|
|
867
868
|
/**
|
|
@@ -890,7 +891,8 @@ shaka.media.MediaSourceEngine = class {
|
|
|
890
891
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
891
892
|
|
|
892
893
|
if (contentType == ContentType.TEXT) {
|
|
893
|
-
if (this.
|
|
894
|
+
if (this.manifestType_ == shaka.media.ManifestParser.HLS &&
|
|
895
|
+
reference.discontinuitySequence >= 0) {
|
|
894
896
|
// This won't be known until the first video segment is appended.
|
|
895
897
|
const offset = await this.textSequenceModeOffset_;
|
|
896
898
|
this.textEngine_.setTimestampOffset(offset);
|
|
@@ -925,8 +927,8 @@ shaka.media.MediaSourceEngine = class {
|
|
|
925
927
|
mimeType = this.transmuxers_[contentType].getOriginalMimeType();
|
|
926
928
|
}
|
|
927
929
|
if (reference) {
|
|
928
|
-
const timestamp = this.getTimestampAndDispatchMetadata_(
|
|
929
|
-
contentType, data, reference, mimeType
|
|
930
|
+
const {timestamp, metadata} = this.getTimestampAndDispatchMetadata_(
|
|
931
|
+
contentType, data, reference, mimeType);
|
|
930
932
|
if (timestamp != null) {
|
|
931
933
|
if (this.firstVideoTimestamp_ == null &&
|
|
932
934
|
contentType == ContentType.VIDEO) {
|
|
@@ -984,10 +986,14 @@ shaka.media.MediaSourceEngine = class {
|
|
|
984
986
|
const isBestSourceBufferForTimestamps =
|
|
985
987
|
contentType == ContentType.VIDEO ||
|
|
986
988
|
!(ContentType.VIDEO in this.sourceBuffers_);
|
|
987
|
-
if (
|
|
989
|
+
if (isBestSourceBufferForTimestamps) {
|
|
988
990
|
this.textSequenceModeOffset_.resolve(timestampOffset);
|
|
989
991
|
}
|
|
990
992
|
}
|
|
993
|
+
if (metadata.length) {
|
|
994
|
+
this.playerInterface_.onMetadata(metadata, timestampOffset,
|
|
995
|
+
reference ? reference.endTime : null);
|
|
996
|
+
}
|
|
991
997
|
}
|
|
992
998
|
if (hasClosedCaptions && contentType == ContentType.VIDEO) {
|
|
993
999
|
if (!this.textEngine_) {
|
|
@@ -1309,6 +1315,10 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1309
1315
|
this.textSequenceModeOffset_ = new shaka.util.PublicPromise();
|
|
1310
1316
|
}
|
|
1311
1317
|
|
|
1318
|
+
if (!this.sequenceMode_) {
|
|
1319
|
+
return;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1312
1322
|
// Queue an abort() to help MSE splice together overlapping segments.
|
|
1313
1323
|
// We set appendWindowEnd when we change periods in DASH content, and the
|
|
1314
1324
|
// period transition may result in overlap.
|
|
@@ -1847,6 +1857,48 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1847
1857
|
null);
|
|
1848
1858
|
}
|
|
1849
1859
|
|
|
1860
|
+
/**
|
|
1861
|
+
* Returns the source buffer parameters
|
|
1862
|
+
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
|
|
1863
|
+
* @return {?shaka.media.MediaSourceEngine.SourceBufferParams}
|
|
1864
|
+
* @private
|
|
1865
|
+
*/
|
|
1866
|
+
getSourceBufferParams_(contentType) {
|
|
1867
|
+
if (!this.sourceBuffers_[contentType]) {
|
|
1868
|
+
return null;
|
|
1869
|
+
}
|
|
1870
|
+
return {
|
|
1871
|
+
timestampOffset: this.sourceBuffers_[contentType].timestampOffset,
|
|
1872
|
+
appendWindowStart: this.sourceBuffers_[contentType].appendWindowStart,
|
|
1873
|
+
appendWindowEnd: this.sourceBuffers_[contentType].appendWindowEnd,
|
|
1874
|
+
};
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
/**
|
|
1878
|
+
* Restore source buffer parameters
|
|
1879
|
+
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
|
|
1880
|
+
* @param {?shaka.media.MediaSourceEngine.SourceBufferParams} params
|
|
1881
|
+
* @private
|
|
1882
|
+
*/
|
|
1883
|
+
restoreSourceBufferParams_(contentType, params) {
|
|
1884
|
+
if (!params) {
|
|
1885
|
+
return;
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
if (!this.sourceBuffers_[contentType]) {
|
|
1889
|
+
shaka.log.warning('Attempted to restore a non-existent source buffer');
|
|
1890
|
+
return;
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
this.sourceBuffers_[contentType].timestampOffset =
|
|
1894
|
+
params.timestampOffset;
|
|
1895
|
+
// `end` needs to be set before `start`
|
|
1896
|
+
this.sourceBuffers_[contentType].appendWindowEnd =
|
|
1897
|
+
params.appendWindowEnd;
|
|
1898
|
+
this.sourceBuffers_[contentType].appendWindowStart =
|
|
1899
|
+
params.appendWindowStart;
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1850
1902
|
/**
|
|
1851
1903
|
* Resets the MediaSource and re-adds source buffers due to codec mismatch
|
|
1852
1904
|
*
|
|
@@ -1907,7 +1959,6 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1907
1959
|
}
|
|
1908
1960
|
await Promise.all(cleanup);
|
|
1909
1961
|
this.transmuxers_ = {};
|
|
1910
|
-
this.sourceBuffers_ = {};
|
|
1911
1962
|
|
|
1912
1963
|
const previousDuration = this.mediaSource_.duration;
|
|
1913
1964
|
this.mediaSourceOpen_ = new shaka.util.PublicPromise();
|
|
@@ -1938,6 +1989,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1938
1989
|
onSourceBufferAdded);
|
|
1939
1990
|
|
|
1940
1991
|
for (const contentType of streamsByType.keys()) {
|
|
1992
|
+
const previousParams = this.getSourceBufferParams_(contentType);
|
|
1941
1993
|
const stream = streamsByType.get(contentType);
|
|
1942
1994
|
// eslint-disable-next-line no-await-in-loop
|
|
1943
1995
|
await this.initSourceBuffer_(contentType, stream, stream.codecs);
|
|
@@ -1947,6 +1999,8 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1947
1999
|
} else {
|
|
1948
2000
|
this.queues_[contentType] = [];
|
|
1949
2001
|
}
|
|
2002
|
+
|
|
2003
|
+
this.restoreSourceBufferParams_(contentType, previousParams);
|
|
1950
2004
|
}
|
|
1951
2005
|
|
|
1952
2006
|
// Fake a seek to catchup the playhead.
|
|
@@ -2234,3 +2288,12 @@ shaka.media.MediaSourceEngine.SourceBufferMode_ = {
|
|
|
2234
2288
|
* Callback to use when metadata arrives.
|
|
2235
2289
|
*/
|
|
2236
2290
|
shaka.media.MediaSourceEngine.PlayerInterface;
|
|
2291
|
+
|
|
2292
|
+
/**
|
|
2293
|
+
* @typedef {{
|
|
2294
|
+
* timestampOffset: number,
|
|
2295
|
+
* appendWindowStart: number,
|
|
2296
|
+
* appendWindowEnd: number
|
|
2297
|
+
* }}
|
|
2298
|
+
*/
|
|
2299
|
+
shaka.media.MediaSourceEngine.SourceBufferParams;
|