shaka-player 4.7.11 → 4.7.13
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/README.md +2 -4
- package/dist/deps.js +6 -7
- package/dist/locales.js +34 -34
- package/dist/shaka-player.compiled.d.ts +14 -12
- package/dist/shaka-player.compiled.debug.d.ts +14 -12
- package/dist/shaka-player.compiled.debug.externs.js +78 -64
- package/dist/shaka-player.compiled.debug.js +321 -312
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +78 -64
- package/dist/shaka-player.compiled.js +1167 -1153
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +14 -12
- package/dist/shaka-player.ui.debug.d.ts +14 -12
- package/dist/shaka-player.ui.debug.externs.js +78 -64
- package/dist/shaka-player.ui.debug.js +443 -434
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +78 -64
- package/dist/shaka-player.ui.js +1319 -1305
- package/dist/shaka-player.ui.map +4 -4
- package/externs/shaka/manifest.js +11 -1
- package/externs/shaka/offline.js +7 -1
- package/externs/shaka/player.js +32 -19
- package/lib/abr/simple_abr_manager.js +21 -26
- package/lib/cast/cast_proxy.js +1 -3
- package/lib/cast/cast_sender.js +8 -11
- package/lib/cea/cea608_memory.js +1 -1
- package/lib/cea/ts_cea_parser.js +10 -1
- package/lib/dash/dash_parser.js +13 -3
- package/lib/dash/segment_base.js +8 -1
- package/lib/dash/segment_list.js +24 -18
- package/lib/dash/segment_template.js +21 -2
- package/lib/hls/hls_parser.js +36 -17
- package/lib/media/drm_engine.js +6 -3
- package/lib/media/media_source_engine.js +96 -33
- package/lib/media/playhead.js +3 -3
- package/lib/media/presentation_timeline.js +23 -0
- package/lib/media/segment_index.js +21 -20
- package/lib/media/segment_utils.js +10 -1
- package/lib/media/streaming_engine.js +2 -14
- package/lib/mss/mss_parser.js +4 -0
- package/lib/offline/indexeddb/v1_storage_cell.js +2 -0
- package/lib/offline/indexeddb/v2_storage_cell.js +2 -0
- package/lib/offline/manifest_converter.js +10 -3
- package/lib/offline/storage.js +2 -0
- package/lib/player.js +30 -4
- package/lib/polyfill/media_capabilities.js +20 -1
- package/lib/polyfill/mediasource.js +37 -14
- package/lib/text/vtt_text_parser.js +51 -51
- package/lib/transmuxer/h264.js +3 -3
- package/lib/transmuxer/h265.js +3 -3
- package/lib/transmuxer/ts_transmuxer.js +42 -3
- package/lib/util/manifest_parser_utils.js +1 -0
- package/lib/util/mime_utils.js +10 -5
- package/lib/util/mp4_parser.js +57 -0
- package/lib/util/periods.js +5 -37
- package/lib/util/platform.js +4 -0
- package/lib/util/stream_utils.js +334 -271
- package/lib/util/ts_parser.js +99 -10
- package/package.json +1 -1
- package/lib/polyfill/storage_estimate.js +0 -57
package/lib/hls/hls_parser.js
CHANGED
|
@@ -552,7 +552,7 @@ shaka.hls.HlsParser = class {
|
|
|
552
552
|
if (goog.DEBUG) {
|
|
553
553
|
const firstSequenceStartTime =
|
|
554
554
|
mediaSequenceToStartTime.values().next().value;
|
|
555
|
-
|
|
555
|
+
shaka.log.warning(
|
|
556
556
|
firstSequenceStartTime == segment0.startTime,
|
|
557
557
|
'Sequence number map is not ordered as expected!');
|
|
558
558
|
}
|
|
@@ -586,16 +586,18 @@ shaka.hls.HlsParser = class {
|
|
|
586
586
|
// Drop any earlier references.
|
|
587
587
|
const numSegmentsToDrop =
|
|
588
588
|
this.minSequenceNumber_ - streamInfo.firstSequenceNumber;
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
589
|
+
if (numSegmentsToDrop > 0) {
|
|
590
|
+
segmentIndex.dropFirstReferences(numSegmentsToDrop);
|
|
591
|
+
|
|
592
|
+
// Now adjust timestamps back to begin at 0.
|
|
593
|
+
const segmentN = segmentIndex.earliestReference();
|
|
594
|
+
if (segmentN) {
|
|
595
|
+
const streamOffset = -segmentN.startTime;
|
|
596
|
+
// Modify all SegmentReferences equally.
|
|
597
|
+
streamInfo.stream.segmentIndex.offset(streamOffset);
|
|
598
|
+
// Update other parts of streamInfo the same way.
|
|
599
|
+
this.offsetStreamInfo_(streamInfo, streamOffset);
|
|
600
|
+
}
|
|
599
601
|
}
|
|
600
602
|
}
|
|
601
603
|
}
|
|
@@ -986,17 +988,17 @@ shaka.hls.HlsParser = class {
|
|
|
986
988
|
}
|
|
987
989
|
|
|
988
990
|
if (!this.presentationTimeline_.isStartTimeLocked()) {
|
|
989
|
-
const
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
991
|
+
for (const streamInfo of this.uriToStreamInfosMap_.values()) {
|
|
992
|
+
if (!streamInfo.stream.segmentIndex) {
|
|
993
|
+
continue; // Not active.
|
|
994
|
+
}
|
|
993
995
|
if (streamInfo.type != 'audio' && streamInfo.type != 'video') {
|
|
994
996
|
continue;
|
|
995
997
|
}
|
|
996
998
|
const firstReference = streamInfo.stream.segmentIndex.get(0);
|
|
997
999
|
if (firstReference && firstReference.syncTime) {
|
|
998
1000
|
const syncTime = firstReference.syncTime;
|
|
999
|
-
this.presentationTimeline_.
|
|
1001
|
+
this.presentationTimeline_.setInitialProgramDateTime(syncTime);
|
|
1000
1002
|
}
|
|
1001
1003
|
}
|
|
1002
1004
|
}
|
|
@@ -1222,6 +1224,7 @@ shaka.hls.HlsParser = class {
|
|
|
1222
1224
|
textStreamInfo.stream.mimeType =
|
|
1223
1225
|
this.guessMimeTypeBeforeLoading_(type, codecs) ||
|
|
1224
1226
|
this.guessMimeTypeFallback_(type);
|
|
1227
|
+
this.setFullTypeForStream_(textStreamInfo.stream);
|
|
1225
1228
|
}
|
|
1226
1229
|
}
|
|
1227
1230
|
}
|
|
@@ -1231,6 +1234,15 @@ shaka.hls.HlsParser = class {
|
|
|
1231
1234
|
return textStreams.filter((s) => s);
|
|
1232
1235
|
}
|
|
1233
1236
|
|
|
1237
|
+
/**
|
|
1238
|
+
* @param {!shaka.extern.Stream} stream
|
|
1239
|
+
* @private
|
|
1240
|
+
*/
|
|
1241
|
+
setFullTypeForStream_(stream) {
|
|
1242
|
+
stream.fullMimeTypes = new Set([shaka.util.MimeUtils.getFullType(
|
|
1243
|
+
stream.mimeType, stream.codecs)]);
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1234
1246
|
/**
|
|
1235
1247
|
* @param {!Array.<!shaka.hls.Tag>} imageTags from the playlist.
|
|
1236
1248
|
* @param {!Array.<!shaka.hls.Tag>} iFrameTags from the playlist.
|
|
@@ -2264,6 +2276,7 @@ shaka.hls.HlsParser = class {
|
|
|
2264
2276
|
stream.mimeType = realStream.mimeType;
|
|
2265
2277
|
stream.bandwidth = stream.bandwidth || realStream.bandwidth;
|
|
2266
2278
|
stream.codecs = stream.codecs || realStream.codecs;
|
|
2279
|
+
this.setFullTypeForStream_(stream);
|
|
2267
2280
|
|
|
2268
2281
|
// Since we lazy-loaded this content, the player may need to create new
|
|
2269
2282
|
// sessions for the DRM info in this stream.
|
|
@@ -2278,6 +2291,7 @@ shaka.hls.HlsParser = class {
|
|
|
2278
2291
|
// To aid manifest filtering, assume before loading that all video
|
|
2279
2292
|
// renditions have the same MIME type. (And likewise for audio.)
|
|
2280
2293
|
otherStreamInfo.stream.mimeType = realStream.mimeType;
|
|
2294
|
+
this.setFullTypeForStream_(otherStreamInfo.stream);
|
|
2281
2295
|
}
|
|
2282
2296
|
}
|
|
2283
2297
|
}
|
|
@@ -2286,6 +2300,7 @@ shaka.hls.HlsParser = class {
|
|
|
2286
2300
|
const firstSegment = realStream.segmentIndex.get(0);
|
|
2287
2301
|
if (firstSegment && firstSegment.initSegmentReference) {
|
|
2288
2302
|
stream.mimeType = 'application/mp4';
|
|
2303
|
+
this.setFullTypeForStream_(stream);
|
|
2289
2304
|
}
|
|
2290
2305
|
}
|
|
2291
2306
|
|
|
@@ -2535,6 +2550,7 @@ shaka.hls.HlsParser = class {
|
|
|
2535
2550
|
stream.drmInfos = drmInfos;
|
|
2536
2551
|
stream.keyIds = keyIds;
|
|
2537
2552
|
stream.mimeType = mimeType;
|
|
2553
|
+
this.setFullTypeForStream_(stream);
|
|
2538
2554
|
|
|
2539
2555
|
const mediaSequenceToStartTime = this.isLive_() ?
|
|
2540
2556
|
this.mediaSequenceToStartTimeByType_.get(type) : new Map();
|
|
@@ -2690,7 +2706,7 @@ shaka.hls.HlsParser = class {
|
|
|
2690
2706
|
roles.push(shaka.util.ManifestParserUtils.TextStreamKind.SUBTITLE);
|
|
2691
2707
|
}
|
|
2692
2708
|
|
|
2693
|
-
|
|
2709
|
+
const stream = {
|
|
2694
2710
|
id: this.globalId_++,
|
|
2695
2711
|
originalId: name,
|
|
2696
2712
|
groupId: null,
|
|
@@ -2728,7 +2744,10 @@ shaka.hls.HlsParser = class {
|
|
|
2728
2744
|
accessibilityPurpose: accessibilityPurpose,
|
|
2729
2745
|
external: false,
|
|
2730
2746
|
fastSwitching: false,
|
|
2747
|
+
fullMimeTypes: new Set(),
|
|
2731
2748
|
};
|
|
2749
|
+
this.setFullTypeForStream_(stream);
|
|
2750
|
+
return stream;
|
|
2732
2751
|
}
|
|
2733
2752
|
|
|
2734
2753
|
/**
|
package/lib/media/drm_engine.js
CHANGED
|
@@ -697,9 +697,12 @@ shaka.media.DrmEngine = class {
|
|
|
697
697
|
initDataOverride.initDataType, initDataOverride.initData);
|
|
698
698
|
}
|
|
699
699
|
|
|
700
|
-
// If
|
|
701
|
-
// it will never get resolved.
|
|
702
|
-
|
|
700
|
+
// If there were no sessions to load, we need to resolve the promise right
|
|
701
|
+
// now or else it will never get resolved.
|
|
702
|
+
// We determine this by checking areAllSessionsLoaded_, rather than checking
|
|
703
|
+
// the number of initDatas, since the newInitData method can reject init
|
|
704
|
+
// datas in some circumstances.
|
|
705
|
+
if (this.areAllSessionsLoaded_()) {
|
|
703
706
|
this.allSessionsLoaded_.resolve();
|
|
704
707
|
}
|
|
705
708
|
|
|
@@ -31,6 +31,7 @@ goog.require('shaka.util.Mp4BoxParsers');
|
|
|
31
31
|
goog.require('shaka.util.Mp4Parser');
|
|
32
32
|
goog.require('shaka.util.Platform');
|
|
33
33
|
goog.require('shaka.util.PublicPromise');
|
|
34
|
+
goog.require('shaka.util.StreamUtils');
|
|
34
35
|
goog.require('shaka.util.TsParser');
|
|
35
36
|
goog.require('shaka.lcevc.Dec');
|
|
36
37
|
|
|
@@ -151,6 +152,9 @@ shaka.media.MediaSourceEngine = class {
|
|
|
151
152
|
|
|
152
153
|
/** @private {?number} */
|
|
153
154
|
this.lastDuration_ = null;
|
|
155
|
+
|
|
156
|
+
/** @private {?shaka.util.TsParser} */
|
|
157
|
+
this.tsParser_ = null;
|
|
154
158
|
}
|
|
155
159
|
|
|
156
160
|
/**
|
|
@@ -234,18 +238,61 @@ shaka.media.MediaSourceEngine = class {
|
|
|
234
238
|
* Checks if a certain type is supported.
|
|
235
239
|
*
|
|
236
240
|
* @param {shaka.extern.Stream} stream
|
|
237
|
-
* @
|
|
241
|
+
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
|
|
242
|
+
* @return {!Promise.<boolean>}
|
|
238
243
|
*/
|
|
239
|
-
static isStreamSupported(stream) {
|
|
244
|
+
static async isStreamSupported(stream, contentType) {
|
|
245
|
+
if (stream.createSegmentIndex) {
|
|
246
|
+
await stream.createSegmentIndex();
|
|
247
|
+
}
|
|
248
|
+
if (!stream.segmentIndex) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
if (stream.segmentIndex.isEmpty()) {
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
240
254
|
const MimeUtils = shaka.util.MimeUtils;
|
|
241
|
-
const fullMimeType = MimeUtils.getFullType(stream.mimeType, stream.codecs);
|
|
242
|
-
const extendedMimeType = MimeUtils.getExtendedType(stream);
|
|
243
|
-
const fullMimeTypeWithAllCodecs = MimeUtils.getFullTypeWithAllCodecs(
|
|
244
|
-
stream.mimeType, stream.codecs);
|
|
245
255
|
const TransmuxerEngine = shaka.transmuxer.TransmuxerEngine;
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
256
|
+
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
257
|
+
const StreamUtils = shaka.util.StreamUtils;
|
|
258
|
+
const seenCombos = new Set();
|
|
259
|
+
// Check each combination of mimeType and codecs within the segment index.
|
|
260
|
+
// Unfortunately we cannot use fullMimeTypes, as we ALSO need to check the
|
|
261
|
+
// getFullTypeWithAllCodecs (for the sake of the transmuxer) and we have no
|
|
262
|
+
// way of going from a full mimeType to a full mimeType with all codecs.
|
|
263
|
+
// As this function is only called in debug mode, a little inefficiency is
|
|
264
|
+
// acceptable.
|
|
265
|
+
for (const ref of stream.segmentIndex) {
|
|
266
|
+
const mimeType = ref.mimeType || stream.mimeType || '';
|
|
267
|
+
let codecs = ref.codecs || stream.codecs || '';
|
|
268
|
+
// Don't check the same combination of mimetype + codecs twice.
|
|
269
|
+
const combo = mimeType + ':' + codecs;
|
|
270
|
+
if (seenCombos.has(combo)) {
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
seenCombos.add(combo);
|
|
274
|
+
if (contentType == ContentType.TEXT) {
|
|
275
|
+
const fullMimeType = MimeUtils.getFullType(mimeType, codecs);
|
|
276
|
+
if (!shaka.text.TextEngine.isTypeSupported(fullMimeType)) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
} else {
|
|
280
|
+
if (contentType == ContentType.VIDEO) {
|
|
281
|
+
codecs = StreamUtils.getCorrectVideoCodecs(codecs);
|
|
282
|
+
} else if (contentType == ContentType.AUDIO) {
|
|
283
|
+
codecs = StreamUtils.getCorrectAudioCodecs(codecs);
|
|
284
|
+
}
|
|
285
|
+
const extendedMimeType = MimeUtils.getExtendedType(
|
|
286
|
+
stream, mimeType, codecs);
|
|
287
|
+
const fullMimeType = MimeUtils.getFullTypeWithAllCodecs(
|
|
288
|
+
mimeType, codecs);
|
|
289
|
+
if (!shaka.media.Capabilities.isTypeSupported(extendedMimeType) &&
|
|
290
|
+
!TransmuxerEngine.isSupported(fullMimeType, stream.type)) {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return true;
|
|
249
296
|
}
|
|
250
297
|
|
|
251
298
|
/**
|
|
@@ -392,6 +439,8 @@ shaka.media.MediaSourceEngine = class {
|
|
|
392
439
|
|
|
393
440
|
// This object is owned by Player
|
|
394
441
|
this.lcevcDec_ = null;
|
|
442
|
+
|
|
443
|
+
this.tsParser_ = null;
|
|
395
444
|
}
|
|
396
445
|
|
|
397
446
|
/**
|
|
@@ -442,9 +491,12 @@ shaka.media.MediaSourceEngine = class {
|
|
|
442
491
|
this.manifestType_ == shaka.media.ManifestParser.HLS &&
|
|
443
492
|
!this.ignoreManifestTimestampsInSegmentsMode_;
|
|
444
493
|
|
|
494
|
+
this.tsParser_ = null;
|
|
495
|
+
|
|
445
496
|
for (const contentType of streamsByType.keys()) {
|
|
446
497
|
const stream = streamsByType.get(contentType);
|
|
447
|
-
|
|
498
|
+
// eslint-disable-next-line no-await-in-loop
|
|
499
|
+
await this.initSourceBuffer_(contentType, stream, stream.codecs);
|
|
448
500
|
if (this.needSplitMuxedContent_) {
|
|
449
501
|
this.queues_[ContentType.AUDIO] = [];
|
|
450
502
|
this.queues_[ContentType.VIDEO] = [];
|
|
@@ -460,14 +512,15 @@ shaka.media.MediaSourceEngine = class {
|
|
|
460
512
|
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
|
|
461
513
|
* @param {shaka.extern.Stream} stream
|
|
462
514
|
* @param {string} codecs
|
|
463
|
-
* @
|
|
515
|
+
* @return {!Promise}
|
|
464
516
|
* @private
|
|
465
517
|
*/
|
|
466
|
-
initSourceBuffer_(contentType, stream, codecs
|
|
518
|
+
async initSourceBuffer_(contentType, stream, codecs) {
|
|
467
519
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
468
520
|
|
|
469
521
|
goog.asserts.assert(
|
|
470
|
-
shaka.media.MediaSourceEngine.isStreamSupported(
|
|
522
|
+
await shaka.media.MediaSourceEngine.isStreamSupported(
|
|
523
|
+
stream, contentType),
|
|
471
524
|
'Type negotiation should happen before MediaSourceEngine.init!');
|
|
472
525
|
|
|
473
526
|
let mimeType = shaka.util.MimeUtils.getFullType(
|
|
@@ -475,7 +528,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
475
528
|
if (contentType == ContentType.TEXT) {
|
|
476
529
|
this.reinitText(mimeType, this.sequenceMode_, stream.external);
|
|
477
530
|
} else {
|
|
478
|
-
let needTransmux = this.config_.forceTransmux
|
|
531
|
+
let needTransmux = this.config_.forceTransmux;
|
|
479
532
|
if (!shaka.media.Capabilities.isTypeSupported(mimeType) ||
|
|
480
533
|
(!this.sequenceMode_ &&
|
|
481
534
|
shaka.util.MimeUtils.RAW_FORMATS.includes(mimeType))) {
|
|
@@ -484,7 +537,6 @@ shaka.media.MediaSourceEngine = class {
|
|
|
484
537
|
const mimeTypeWithAllCodecs =
|
|
485
538
|
shaka.util.MimeUtils.getFullTypeWithAllCodecs(
|
|
486
539
|
stream.mimeType, codecs);
|
|
487
|
-
const TransmuxerEngine = shaka.transmuxer.TransmuxerEngine;
|
|
488
540
|
if (needTransmux) {
|
|
489
541
|
const audioCodec = shaka.util.ManifestParserUtils.guessCodecsSafe(
|
|
490
542
|
ContentType.AUDIO, (codecs || '').split(','));
|
|
@@ -492,14 +544,12 @@ shaka.media.MediaSourceEngine = class {
|
|
|
492
544
|
ContentType.VIDEO, (codecs || '').split(','));
|
|
493
545
|
if (audioCodec && videoCodec) {
|
|
494
546
|
this.needSplitMuxedContent_ = true;
|
|
495
|
-
this.initSourceBuffer_(
|
|
496
|
-
|
|
497
|
-
this.initSourceBuffer_(
|
|
498
|
-
ContentType.VIDEO, stream, videoCodec, /* forceTransmux= */ true);
|
|
547
|
+
await this.initSourceBuffer_(ContentType.AUDIO, stream, audioCodec);
|
|
548
|
+
await this.initSourceBuffer_(ContentType.VIDEO, stream, videoCodec);
|
|
499
549
|
return;
|
|
500
550
|
}
|
|
501
|
-
const transmuxerPlugin =
|
|
502
|
-
|
|
551
|
+
const transmuxerPlugin = shaka.transmuxer.TransmuxerEngine
|
|
552
|
+
.findTransmuxer(mimeTypeWithAllCodecs);
|
|
503
553
|
if (transmuxerPlugin) {
|
|
504
554
|
const transmuxer = transmuxerPlugin();
|
|
505
555
|
this.transmuxers_[contentType] = transmuxer;
|
|
@@ -789,7 +839,12 @@ shaka.media.MediaSourceEngine = class {
|
|
|
789
839
|
}
|
|
790
840
|
} else if (!mimeType.includes('/mp4') && !mimeType.includes('/webm') &&
|
|
791
841
|
shaka.util.TsParser.probe(uint8ArrayData)) {
|
|
792
|
-
|
|
842
|
+
if (!this.tsParser_) {
|
|
843
|
+
this.tsParser_ = new shaka.util.TsParser();
|
|
844
|
+
} else {
|
|
845
|
+
this.tsParser_.clearData();
|
|
846
|
+
}
|
|
847
|
+
const tsParser = this.tsParser_.parse(uint8ArrayData);
|
|
793
848
|
const startTime = tsParser.getStartTime(contentType);
|
|
794
849
|
if (startTime != null) {
|
|
795
850
|
timestamp = startTime;
|
|
@@ -1177,6 +1232,12 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1177
1232
|
return;
|
|
1178
1233
|
}
|
|
1179
1234
|
|
|
1235
|
+
// Reset the promise in case the timestamp offset changed during
|
|
1236
|
+
// a period/discontinuity transition.
|
|
1237
|
+
if (contentType == ContentType.VIDEO) {
|
|
1238
|
+
this.textSequenceModeOffset_ = new shaka.util.PublicPromise();
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1180
1241
|
// Queue an abort() to help MSE splice together overlapping segments.
|
|
1181
1242
|
// We set appendWindowEnd when we change periods in DASH content, and the
|
|
1182
1243
|
// period transition may result in overlap.
|
|
@@ -1787,7 +1848,8 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1787
1848
|
|
|
1788
1849
|
for (const contentType of streamsByType.keys()) {
|
|
1789
1850
|
const stream = streamsByType.get(contentType);
|
|
1790
|
-
|
|
1851
|
+
// eslint-disable-next-line no-await-in-loop
|
|
1852
|
+
await this.initSourceBuffer_(contentType, stream, stream.codecs);
|
|
1791
1853
|
if (this.needSplitMuxedContent_) {
|
|
1792
1854
|
this.queues_[ContentType.AUDIO] = [];
|
|
1793
1855
|
this.queues_[ContentType.VIDEO] = [];
|
|
@@ -1857,8 +1919,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1857
1919
|
let needTransmux = this.config_.forceTransmux;
|
|
1858
1920
|
if (!shaka.media.Capabilities.isTypeSupported(newMimeType) ||
|
|
1859
1921
|
(!this.sequenceMode_ &&
|
|
1860
|
-
shaka.util.MimeUtils.RAW_FORMATS.includes(newMimeType))
|
|
1861
|
-
codecs.includes(',')) {
|
|
1922
|
+
shaka.util.MimeUtils.RAW_FORMATS.includes(newMimeType))) {
|
|
1862
1923
|
needTransmux = true;
|
|
1863
1924
|
}
|
|
1864
1925
|
const TransmuxerEngine = shaka.transmuxer.TransmuxerEngine;
|
|
@@ -1921,10 +1982,12 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1921
1982
|
*
|
|
1922
1983
|
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
|
|
1923
1984
|
* @param {shaka.extern.Stream} stream
|
|
1985
|
+
* @param {string} refMimeType
|
|
1986
|
+
* @param {string} refCodecs
|
|
1924
1987
|
* @return {boolean}
|
|
1925
1988
|
* @private
|
|
1926
1989
|
*/
|
|
1927
|
-
isCodecSwitchNecessary_(contentType, stream) {
|
|
1990
|
+
isCodecSwitchNecessary_(contentType, stream, refMimeType, refCodecs) {
|
|
1928
1991
|
if (contentType == shaka.util.ManifestParserUtils.ContentType.TEXT) {
|
|
1929
1992
|
return false;
|
|
1930
1993
|
}
|
|
@@ -1934,18 +1997,16 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1934
1997
|
const currentBasicType = MimeUtils.getBasicType(
|
|
1935
1998
|
this.sourceBufferTypes_[contentType]);
|
|
1936
1999
|
|
|
1937
|
-
let newMimeType = shaka.util.MimeUtils.getFullType(
|
|
1938
|
-
stream.mimeType, stream.codecs);
|
|
2000
|
+
let newMimeType = shaka.util.MimeUtils.getFullType(refMimeType, refCodecs);
|
|
1939
2001
|
let needTransmux = this.config_.forceTransmux;
|
|
1940
2002
|
if (!shaka.media.Capabilities.isTypeSupported(newMimeType) ||
|
|
1941
2003
|
(!this.sequenceMode_ &&
|
|
1942
|
-
shaka.util.MimeUtils.RAW_FORMATS.includes(newMimeType))
|
|
1943
|
-
stream.codecs.includes(',')) {
|
|
2004
|
+
shaka.util.MimeUtils.RAW_FORMATS.includes(newMimeType))) {
|
|
1944
2005
|
needTransmux = true;
|
|
1945
2006
|
}
|
|
1946
2007
|
const newMimeTypeWithAllCodecs =
|
|
1947
2008
|
shaka.util.MimeUtils.getFullTypeWithAllCodecs(
|
|
1948
|
-
|
|
2009
|
+
refMimeType, refCodecs);
|
|
1949
2010
|
const TransmuxerEngine = shaka.transmuxer.TransmuxerEngine;
|
|
1950
2011
|
if (needTransmux) {
|
|
1951
2012
|
const transmuxerPlugin =
|
|
@@ -1971,10 +2032,12 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1971
2032
|
*
|
|
1972
2033
|
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
|
|
1973
2034
|
* @param {shaka.extern.Stream} stream
|
|
2035
|
+
* @param {string} mimeType
|
|
2036
|
+
* @param {string} codecs
|
|
1974
2037
|
* @return {boolean}
|
|
1975
2038
|
*/
|
|
1976
|
-
isResetMediaSourceNecessary(contentType, stream) {
|
|
1977
|
-
if (!this.isCodecSwitchNecessary_(contentType, stream)) {
|
|
2039
|
+
isResetMediaSourceNecessary(contentType, stream, mimeType, codecs) {
|
|
2040
|
+
if (!this.isCodecSwitchNecessary_(contentType, stream, mimeType, codecs)) {
|
|
1978
2041
|
return false;
|
|
1979
2042
|
}
|
|
1980
2043
|
|
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 || currentTime > seekEnd) {
|
|
386
|
+
// playhead is outside of the seekrange. Move playhead to catch up.
|
|
387
387
|
const targetTime = this.reposition_(currentTime);
|
|
388
|
-
shaka.log.info('Jumping
|
|
388
|
+
shaka.log.info('Jumping by ' + (targetTime - currentTime) +
|
|
389
389
|
' seconds to catch up with the seek range.');
|
|
390
390
|
this.mediaElement_.currentTime = targetTime;
|
|
391
391
|
}
|
|
@@ -96,6 +96,9 @@ shaka.media.PresentationTimeline = class {
|
|
|
96
96
|
|
|
97
97
|
/** @private {boolean} */
|
|
98
98
|
this.startTimeLocked_ = false;
|
|
99
|
+
|
|
100
|
+
/** @private {?number} */
|
|
101
|
+
this.initialProgramDateTime_ = null;
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
|
|
@@ -339,6 +342,26 @@ shaka.media.PresentationTimeline = class {
|
|
|
339
342
|
}
|
|
340
343
|
|
|
341
344
|
|
|
345
|
+
/**
|
|
346
|
+
* Sets the initial program date time.
|
|
347
|
+
*
|
|
348
|
+
* @param {number} initialProgramDateTime
|
|
349
|
+
* @export
|
|
350
|
+
*/
|
|
351
|
+
setInitialProgramDateTime(initialProgramDateTime) {
|
|
352
|
+
this.initialProgramDateTime_ = initialProgramDateTime;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* @return {?number} The initial program date time in seconds.
|
|
358
|
+
* @export
|
|
359
|
+
*/
|
|
360
|
+
getInitialProgramDateTime() {
|
|
361
|
+
return this.initialProgramDateTime_;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
|
|
342
365
|
/**
|
|
343
366
|
* Gives PresentationTimeline a Stream's minimum segment start time.
|
|
344
367
|
*
|
|
@@ -363,26 +363,27 @@ shaka.media.SegmentIndex = class {
|
|
|
363
363
|
|
|
364
364
|
// Adjust the last SegmentReference.
|
|
365
365
|
const lastReference = this.references[this.references.length - 1];
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
366
|
+
const newReference = new shaka.media.SegmentReference(
|
|
367
|
+
lastReference.startTime,
|
|
368
|
+
/* endTime= */ windowEnd,
|
|
369
|
+
lastReference.getUrisInner,
|
|
370
|
+
lastReference.startByte,
|
|
371
|
+
lastReference.endByte,
|
|
372
|
+
lastReference.initSegmentReference,
|
|
373
|
+
lastReference.timestampOffset,
|
|
374
|
+
lastReference.appendWindowStart,
|
|
375
|
+
lastReference.appendWindowEnd,
|
|
376
|
+
lastReference.partialReferences,
|
|
377
|
+
lastReference.tilesLayout,
|
|
378
|
+
lastReference.tileDuration,
|
|
379
|
+
lastReference.syncTime,
|
|
380
|
+
lastReference.status,
|
|
381
|
+
lastReference.aes128Key,
|
|
382
|
+
);
|
|
383
|
+
newReference.mimeType = lastReference.mimeType;
|
|
384
|
+
newReference.codecs = lastReference.codecs;
|
|
385
|
+
newReference.discontinuitySequence = lastReference.discontinuitySequence;
|
|
386
|
+
this.references[this.references.length - 1] = newReference;
|
|
386
387
|
}
|
|
387
388
|
|
|
388
389
|
|
|
@@ -55,6 +55,7 @@ shaka.media.SegmentUtils = class {
|
|
|
55
55
|
let hasVideo = false;
|
|
56
56
|
switch (tsCodecs.audio) {
|
|
57
57
|
case 'aac':
|
|
58
|
+
case 'aac-loas':
|
|
58
59
|
codecs.push('mp4a.40.2');
|
|
59
60
|
hasAudio = true;
|
|
60
61
|
break;
|
|
@@ -70,6 +71,10 @@ shaka.media.SegmentUtils = class {
|
|
|
70
71
|
codecs.push('ec-3');
|
|
71
72
|
hasAudio = true;
|
|
72
73
|
break;
|
|
74
|
+
case 'opus':
|
|
75
|
+
codecs.push('opus');
|
|
76
|
+
hasAudio = true;
|
|
77
|
+
break;
|
|
73
78
|
}
|
|
74
79
|
switch (tsCodecs.video) {
|
|
75
80
|
case 'avc':
|
|
@@ -88,6 +93,10 @@ shaka.media.SegmentUtils = class {
|
|
|
88
93
|
}
|
|
89
94
|
hasVideo = true;
|
|
90
95
|
break;
|
|
96
|
+
case 'av1':
|
|
97
|
+
codecs.push('av01.0.01M.08');
|
|
98
|
+
hasVideo = true;
|
|
99
|
+
break;
|
|
91
100
|
}
|
|
92
101
|
if (!codecs.length) {
|
|
93
102
|
return null;
|
|
@@ -256,7 +265,7 @@ shaka.media.SegmentUtils = class {
|
|
|
256
265
|
// This signals an encrypted sample, which we can go inside of to
|
|
257
266
|
// find the codec used.
|
|
258
267
|
// Note: If encrypted, you can only have audio or video, not both.
|
|
259
|
-
.box('enca', Mp4Parser.
|
|
268
|
+
.box('enca', Mp4Parser.audioSampleEntry)
|
|
260
269
|
.box('encv', Mp4Parser.visualSampleEntry)
|
|
261
270
|
.box('sinf', Mp4Parser.children)
|
|
262
271
|
.box('frma', (box) => {
|
|
@@ -881,7 +881,6 @@ shaka.media.StreamingEngine = class {
|
|
|
881
881
|
// The playhead might be seeking on startup, if a start time is set, so
|
|
882
882
|
// start "seeked" as true.
|
|
883
883
|
seeked: true,
|
|
884
|
-
needsResync: false,
|
|
885
884
|
recovering: false,
|
|
886
885
|
hasError: false,
|
|
887
886
|
operation: null,
|
|
@@ -1248,11 +1247,6 @@ shaka.media.StreamingEngine = class {
|
|
|
1248
1247
|
shaka.log.v1(
|
|
1249
1248
|
logPrefix, 'looking up segment from new stream endTime:', time);
|
|
1250
1249
|
|
|
1251
|
-
// Using a new iterator means we need to resync the stream in sequence
|
|
1252
|
-
// mode. The buffered range might not align perfectly with the last
|
|
1253
|
-
// segment end time, so we may end up repeating a segment. Resyncing
|
|
1254
|
-
// makes this safe to do.
|
|
1255
|
-
mediaState.needsResync = true;
|
|
1256
1250
|
mediaState.segmentIterator =
|
|
1257
1251
|
mediaState.stream.segmentIndex.getIteratorForTime(time);
|
|
1258
1252
|
const ref = mediaState.segmentIterator &&
|
|
@@ -1730,7 +1724,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1730
1724
|
const isResetMediaSourceNecessary =
|
|
1731
1725
|
mediaState.lastCodecs && mediaState.lastMimeType &&
|
|
1732
1726
|
this.playerInterface_.mediaSourceEngine.isResetMediaSourceNecessary(
|
|
1733
|
-
mediaState.type, mediaState.stream);
|
|
1727
|
+
mediaState.type, mediaState.stream, mimeType, codecs);
|
|
1734
1728
|
if (isResetMediaSourceNecessary) {
|
|
1735
1729
|
let otherState = null;
|
|
1736
1730
|
if (mediaState.type === ContentType.VIDEO) {
|
|
@@ -1879,9 +1873,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1879
1873
|
// Across discontinuity bounds, we should resync timestamps for
|
|
1880
1874
|
// sequence mode playbacks. The next segment appended should
|
|
1881
1875
|
// land at its theoretical timestamp from the segment index.
|
|
1882
|
-
if (reference.discontinuitySequence != lastDiscontinuitySequence
|
|
1883
|
-
mediaState.needsResync) {
|
|
1884
|
-
mediaState.needsResync = false;
|
|
1876
|
+
if (reference.discontinuitySequence != lastDiscontinuitySequence) {
|
|
1885
1877
|
operations.push(this.playerInterface_.mediaSourceEngine.resync(
|
|
1886
1878
|
mediaState.type, reference.startTime));
|
|
1887
1879
|
}
|
|
@@ -2591,7 +2583,6 @@ shaka.media.StreamingEngine.PlayerInterface;
|
|
|
2591
2583
|
* clearBufferSafeMargin: number,
|
|
2592
2584
|
* clearingBuffer: boolean,
|
|
2593
2585
|
* seeked: boolean,
|
|
2594
|
-
* needsResync: boolean,
|
|
2595
2586
|
* adaptation: boolean,
|
|
2596
2587
|
* recovering: boolean,
|
|
2597
2588
|
* hasError: boolean,
|
|
@@ -2646,9 +2637,6 @@ shaka.media.StreamingEngine.PlayerInterface;
|
|
|
2646
2637
|
* True indicates that the presentation just seeked.
|
|
2647
2638
|
* @property {boolean} adaptation
|
|
2648
2639
|
* True indicates that the presentation just automatically switched variants.
|
|
2649
|
-
* @property {boolean} needsResync
|
|
2650
|
-
* True indicates that the stream needs to be resynced in sequence mode,
|
|
2651
|
-
* regardless of discontinuity sequence.
|
|
2652
2640
|
* @property {boolean} recovering
|
|
2653
2641
|
* True indicates that the last segment was not appended because it could not
|
|
2654
2642
|
* fit in the buffer.
|
package/lib/mss/mss_parser.js
CHANGED
|
@@ -19,6 +19,7 @@ goog.require('shaka.net.NetworkingEngine');
|
|
|
19
19
|
goog.require('shaka.util.Error');
|
|
20
20
|
goog.require('shaka.util.LanguageUtils');
|
|
21
21
|
goog.require('shaka.util.ManifestParserUtils');
|
|
22
|
+
goog.require('shaka.util.MimeUtils');
|
|
22
23
|
goog.require('shaka.util.Mp4Generator');
|
|
23
24
|
goog.require('shaka.util.OperationManager');
|
|
24
25
|
goog.require('shaka.util.Timer');
|
|
@@ -556,6 +557,7 @@ shaka.mss.MssParser = class {
|
|
|
556
557
|
accessibilityPurpose: null,
|
|
557
558
|
external: false,
|
|
558
559
|
fastSwitching: false,
|
|
560
|
+
fullMimeTypes: new Set(),
|
|
559
561
|
};
|
|
560
562
|
|
|
561
563
|
// This is specifically for text tracks.
|
|
@@ -634,6 +636,8 @@ shaka.mss.MssParser = class {
|
|
|
634
636
|
}
|
|
635
637
|
break;
|
|
636
638
|
}
|
|
639
|
+
stream.fullMimeTypes.add(shaka.util.MimeUtils.getFullType(
|
|
640
|
+
stream.mimeType, stream.codecs));
|
|
637
641
|
|
|
638
642
|
// Lazy-Load the segment index to avoid create all init segment at the
|
|
639
643
|
// same time
|