shaka-player 4.0.0 → 4.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/config.yml +1 -0
- package/.github/workflows/release-please.yaml +6 -11
- package/AUTHORS +1 -0
- package/CHANGELOG.md +39 -0
- package/CONTRIBUTORS +1 -0
- package/README.md +4 -4
- package/build/misspellings.txt +1 -0
- package/build/test.py +1 -1
- package/demo/common/assets.js +1 -1
- package/demo/demo.less +0 -1
- package/demo/main.js +3 -3
- package/dist/controls.css +3 -3
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +3 -3
- package/dist/demo.compiled.debug.map +2 -2
- package/dist/demo.compiled.js +3 -3
- package/dist/demo.compiled.map +2 -2
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +1 -1
- package/dist/locales.js +33 -33
- package/dist/receiver.compiled.debug.js +1 -1
- package/dist/receiver.compiled.debug.map +1 -1
- package/dist/receiver.compiled.js +1 -1
- package/dist/receiver.compiled.map +1 -1
- package/dist/shaka-player.compiled.d.ts +2 -1
- package/dist/shaka-player.compiled.debug.d.ts +2 -1
- package/dist/shaka-player.compiled.debug.externs.js +4 -2
- package/dist/shaka-player.compiled.debug.js +278 -267
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +4 -2
- package/dist/shaka-player.compiled.js +815 -806
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +2 -1
- package/dist/shaka-player.ui.debug.d.ts +2 -1
- package/dist/shaka-player.ui.debug.externs.js +4 -2
- package/dist/shaka-player.ui.debug.js +305 -293
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +4 -2
- package/dist/shaka-player.ui.js +966 -959
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/upgrade.md +4 -2
- package/externs/shaka/player.js +15 -2
- package/karma.conf.js +4 -0
- package/lib/abr/simple_abr_manager.js +2 -1
- package/lib/dash/dash_parser.js +2 -2
- package/lib/hls/hls_parser.js +211 -82
- package/lib/media/drm_engine.js +19 -0
- package/lib/media/media_source_engine.js +91 -37
- package/lib/media/segment_index.js +26 -1
- package/lib/media/segment_reference.js +10 -3
- package/lib/media/streaming_engine.js +2 -3
- package/lib/offline/indexeddb/storage_mechanism.js +4 -2
- package/lib/player.js +165 -107
- package/lib/text/vtt_text_parser.js +7 -7
- package/lib/util/config_utils.js +25 -0
- package/lib/util/error.js +7 -0
- package/lib/util/manifest_parser_utils.js +1 -1
- package/lib/util/mime_utils.js +62 -6
- package/lib/util/periods.js +5 -4
- package/lib/util/player_configuration.js +14 -14
- package/lib/util/stream_utils.js +5 -2
- package/package.json +6 -3
- package/test/hls/hls_live_unit.js +79 -7
- package/test/hls/hls_parser_unit.js +102 -8
- package/test/hls/manifest_text_parser_unit.js +19 -0
- package/test/offline/indexeddb_storage_unit.js +1 -1
- package/test/offline/storage_compatibility_unit.js +1 -1
- package/test/player_external.js +7 -1
- package/test/player_integration.js +6 -10
- package/test/player_src_equals_integration.js +81 -83
- package/test/player_unit.js +98 -1
- package/test/test/boot.js +9 -0
- package/test/test/externs/filters.js +4 -0
- package/test/test/util/simple_fakes.js +31 -2
- package/test/text/vtt_text_parser_unit.js +64 -5
- package/test/util/mime_utils_unit.js +54 -0
- package/test/util/periods_unit.js +79 -0
- package/ui/less/containers.less +1 -1
- package/ui/less/general.less +1 -1
- package/ui/less/range_elements.less +1 -0
- package/ui/resolution_selection.js +1 -1
- package/.github/pull_request_template.md +0 -10
- package/docs/tutorials/gsoc-ideas-list.md +0 -3
|
@@ -106,6 +106,12 @@ shaka.media.MediaSourceEngine = class {
|
|
|
106
106
|
|
|
107
107
|
/** @private {string} */
|
|
108
108
|
this.url_ = '';
|
|
109
|
+
|
|
110
|
+
/** @private {boolean} */
|
|
111
|
+
this.sequenceMode_ = false;
|
|
112
|
+
|
|
113
|
+
/** @private {!shaka.util.PublicPromise.<number>} */
|
|
114
|
+
this.textSequenceModeOffset_ = new shaka.util.PublicPromise();
|
|
109
115
|
}
|
|
110
116
|
|
|
111
117
|
/**
|
|
@@ -331,6 +337,8 @@ shaka.media.MediaSourceEngine = class {
|
|
|
331
337
|
|
|
332
338
|
await this.mediaSourceOpen_;
|
|
333
339
|
|
|
340
|
+
this.sequenceMode_ = sequenceMode;
|
|
341
|
+
|
|
334
342
|
for (const contentType of streamsByType.keys()) {
|
|
335
343
|
const stream = streamsByType.get(contentType);
|
|
336
344
|
goog.asserts.assert(
|
|
@@ -348,11 +356,9 @@ shaka.media.MediaSourceEngine = class {
|
|
|
348
356
|
mimeType =
|
|
349
357
|
shaka.media.Transmuxer.convertTsCodecs(contentType, mimeType);
|
|
350
358
|
}
|
|
359
|
+
|
|
351
360
|
const sourceBuffer = this.mediaSource_.addSourceBuffer(mimeType);
|
|
352
|
-
|
|
353
|
-
sourceBuffer.mode =
|
|
354
|
-
shaka.media.MediaSourceEngine.SourceBufferMode_.SEQUENCE;
|
|
355
|
-
}
|
|
361
|
+
|
|
356
362
|
this.eventManager_.listen(
|
|
357
363
|
sourceBuffer, 'error',
|
|
358
364
|
() => this.onError_(contentType));
|
|
@@ -515,35 +521,29 @@ shaka.media.MediaSourceEngine = class {
|
|
|
515
521
|
* @param {?boolean} hasClosedCaptions True if the buffer contains CEA closed
|
|
516
522
|
* captions
|
|
517
523
|
* @param {boolean=} seeked True if we just seeked
|
|
518
|
-
* @param {boolean=} sequenceMode True if sequence mode
|
|
519
524
|
* @return {!Promise}
|
|
520
525
|
*/
|
|
521
|
-
async appendBuffer(
|
|
522
|
-
|
|
526
|
+
async appendBuffer(
|
|
527
|
+
contentType, data, startTime, endTime, hasClosedCaptions, seeked) {
|
|
523
528
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
524
529
|
|
|
525
|
-
if (
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
if (seeked) {
|
|
531
|
-
const timestampOffset = /** @type {number} */ (startTime);
|
|
532
|
-
this.enqueueOperation_(
|
|
533
|
-
contentType,
|
|
534
|
-
() => this.setTimestampOffset_(contentType, timestampOffset));
|
|
530
|
+
if (contentType == ContentType.TEXT) {
|
|
531
|
+
if (this.sequenceMode_) {
|
|
532
|
+
// This won't be known until the first video segment is appended.
|
|
533
|
+
const offset = await this.textSequenceModeOffset_;
|
|
534
|
+
this.textEngine_.setTimestampOffset(offset);
|
|
535
535
|
}
|
|
536
|
+
await this.textEngine_.appendBuffer(data, startTime, endTime);
|
|
537
|
+
return;
|
|
536
538
|
}
|
|
537
539
|
|
|
538
|
-
if (contentType
|
|
539
|
-
await this.textEngine_.appendBuffer(data, startTime, endTime);
|
|
540
|
-
} else if (this.transmuxers_[contentType]) {
|
|
540
|
+
if (this.transmuxers_[contentType]) {
|
|
541
541
|
const transmuxedData =
|
|
542
542
|
await this.transmuxers_[contentType].transmux(data);
|
|
543
543
|
// For HLS CEA-608/708 CLOSED-CAPTIONS, text data is embedded in
|
|
544
544
|
// the video stream, so textEngine may not have been initialized.
|
|
545
545
|
if (!this.textEngine_) {
|
|
546
|
-
this.reinitText('text/vtt',
|
|
546
|
+
this.reinitText('text/vtt', this.sequenceMode_);
|
|
547
547
|
}
|
|
548
548
|
|
|
549
549
|
if (transmuxedData.metadata) {
|
|
@@ -562,15 +562,10 @@ shaka.media.MediaSourceEngine = class {
|
|
|
562
562
|
closedCaptions, startTime, endTime, videoOffset);
|
|
563
563
|
}
|
|
564
564
|
|
|
565
|
-
|
|
566
|
-
transmuxedSegment = this.workAroundBrokenPlatforms_(
|
|
567
|
-
transmuxedSegment, startTime, contentType);
|
|
568
|
-
|
|
569
|
-
await this.enqueueOperation_(
|
|
570
|
-
contentType, () => this.append_(contentType, transmuxedSegment));
|
|
565
|
+
data = transmuxedData.data;
|
|
571
566
|
} else if (hasClosedCaptions) {
|
|
572
567
|
if (!this.textEngine_) {
|
|
573
|
-
this.reinitText('text/vtt',
|
|
568
|
+
this.reinitText('text/vtt', this.sequenceMode_);
|
|
574
569
|
}
|
|
575
570
|
// If it is the init segment for closed captions, initialize the closed
|
|
576
571
|
// caption parser.
|
|
@@ -585,19 +580,78 @@ shaka.media.MediaSourceEngine = class {
|
|
|
585
580
|
closedCaptions, startTime, endTime, videoOffset);
|
|
586
581
|
}
|
|
587
582
|
}
|
|
583
|
+
}
|
|
588
584
|
|
|
589
|
-
|
|
585
|
+
data = this.workAroundBrokenPlatforms_(data, startTime, contentType);
|
|
586
|
+
|
|
587
|
+
const sourceBuffer = this.sourceBuffers_[contentType];
|
|
588
|
+
const SEQUENCE = shaka.media.MediaSourceEngine.SourceBufferMode_.SEQUENCE;
|
|
589
|
+
|
|
590
|
+
if (this.sequenceMode_ && sourceBuffer.mode != SEQUENCE &&
|
|
591
|
+
startTime != null) {
|
|
592
|
+
// This is the first media segment to be appended to a SourceBuffer in
|
|
593
|
+
// sequence mode. We set the mode late so that we can trick MediaSource
|
|
594
|
+
// into extracting a timestamp for us to align text segments in sequence
|
|
595
|
+
// mode.
|
|
596
|
+
|
|
597
|
+
// Timestamps can only be reliably extracted from video, not audio.
|
|
598
|
+
// Packed audio formats do not have internal timestamps at all.
|
|
599
|
+
// Prefer video for this when available.
|
|
600
|
+
const isBestSourceBufferForTimestamps =
|
|
601
|
+
contentType == ContentType.VIDEO ||
|
|
602
|
+
!(ContentType.VIDEO in this.sourceBuffers_);
|
|
603
|
+
if (isBestSourceBufferForTimestamps) {
|
|
604
|
+
// Append the segment in segments mode first, with offset of 0 and an
|
|
605
|
+
// open append window.
|
|
606
|
+
const originalRange =
|
|
607
|
+
[sourceBuffer.appendWindowStart, sourceBuffer.appendWindowEnd];
|
|
608
|
+
sourceBuffer.appendWindowStart = 0;
|
|
609
|
+
sourceBuffer.appendWindowEnd = Infinity;
|
|
610
|
+
|
|
611
|
+
const originalOffset = sourceBuffer.timestampOffset;
|
|
612
|
+
sourceBuffer.timestampOffset = 0;
|
|
613
|
+
|
|
614
|
+
await this.enqueueOperation_(
|
|
615
|
+
contentType, () => this.append_(contentType, data));
|
|
616
|
+
|
|
617
|
+
// Reset the offset and append window.
|
|
618
|
+
sourceBuffer.timestampOffset = originalOffset;
|
|
619
|
+
sourceBuffer.appendWindowStart = originalRange[0];
|
|
620
|
+
sourceBuffer.appendWindowEnd = originalRange[1];
|
|
621
|
+
|
|
622
|
+
// Now get the timestamp of the segment and compute the offset for text
|
|
623
|
+
// segments.
|
|
624
|
+
const mediaStartTime = shaka.media.TimeRangesUtils.bufferStart(
|
|
625
|
+
this.getBuffered_(contentType));
|
|
626
|
+
const textOffset = (startTime || 0) - (mediaStartTime || 0);
|
|
627
|
+
this.textSequenceModeOffset_.resolve(textOffset);
|
|
628
|
+
|
|
629
|
+
// Finally, clear the buffer.
|
|
630
|
+
await this.enqueueOperation_(
|
|
631
|
+
contentType,
|
|
632
|
+
() => this.remove_(contentType, 0, this.mediaSource_.duration));
|
|
633
|
+
}
|
|
590
634
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
} else {
|
|
595
|
-
data = this.workAroundBrokenPlatforms_(data, startTime, contentType);
|
|
635
|
+
// Now switch to sequence mode and fall through to our normal operations.
|
|
636
|
+
sourceBuffer.mode = SEQUENCE;
|
|
637
|
+
}
|
|
596
638
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
639
|
+
if (startTime != null && this.sequenceMode_ &&
|
|
640
|
+
contentType != ContentType.TEXT) {
|
|
641
|
+
// In sequence mode, for non-text streams, if we just cleared the buffer
|
|
642
|
+
// and are performing an unbuffered seek, we need to set a new
|
|
643
|
+
// timestampOffset on the sourceBuffer.
|
|
644
|
+
if (seeked) {
|
|
645
|
+
const timestampOffset = /** @type {number} */ (startTime);
|
|
646
|
+
this.enqueueOperation_(
|
|
647
|
+
contentType,
|
|
648
|
+
() => this.setTimestampOffset_(contentType, timestampOffset));
|
|
649
|
+
}
|
|
600
650
|
}
|
|
651
|
+
|
|
652
|
+
await this.enqueueOperation_(
|
|
653
|
+
contentType,
|
|
654
|
+
() => this.append_(contentType, data));
|
|
601
655
|
}
|
|
602
656
|
|
|
603
657
|
/**
|
|
@@ -94,6 +94,25 @@ shaka.media.SegmentIndex = class {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Return the earliest reference, or null if empty.
|
|
99
|
+
* @return {shaka.media.SegmentReference}
|
|
100
|
+
*/
|
|
101
|
+
earliestReference() {
|
|
102
|
+
return this.references[0] || null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Drop the first N references.
|
|
108
|
+
* Used in early HLS synchronization, and does not count as eviction.
|
|
109
|
+
* @param {number} n
|
|
110
|
+
*/
|
|
111
|
+
dropFirstReferences(n) {
|
|
112
|
+
this.references.splice(0, n);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
97
116
|
/**
|
|
98
117
|
* Finds the position of the segment for the given time, in seconds, relative
|
|
99
118
|
* to the start of the presentation. Returns the position of the segment
|
|
@@ -166,7 +185,13 @@ shaka.media.SegmentIndex = class {
|
|
|
166
185
|
for (const ref of this.references) {
|
|
167
186
|
ref.startTime += offset;
|
|
168
187
|
ref.endTime += offset;
|
|
169
|
-
ref.
|
|
188
|
+
ref.trueEndTime += offset;
|
|
189
|
+
|
|
190
|
+
for (const partial of ref.partialReferences) {
|
|
191
|
+
partial.startTime += offset;
|
|
192
|
+
partial.endTime += offset;
|
|
193
|
+
partial.trueEndTime += offset;
|
|
194
|
+
}
|
|
170
195
|
}
|
|
171
196
|
}
|
|
172
197
|
}
|
|
@@ -160,8 +160,9 @@ shaka.media.SegmentReference = class {
|
|
|
160
160
|
* If not provided, the duration should be automatically calculated based on
|
|
161
161
|
* the duration of the reference.
|
|
162
162
|
* @param {?number=} syncTime
|
|
163
|
-
* A time value, expressed in
|
|
164
|
-
*
|
|
163
|
+
* A time value, expressed in seconds since 1970, which is used to
|
|
164
|
+
* synchronize between streams. Both produced and consumed by the HLS
|
|
165
|
+
* parser. Other components should not need this value.
|
|
165
166
|
* @param {shaka.media.SegmentReference.Status=} status
|
|
166
167
|
* The segment status is used to indicate that a segment does not exist or is
|
|
167
168
|
* not available.
|
|
@@ -221,7 +222,13 @@ shaka.media.SegmentReference = class {
|
|
|
221
222
|
/** @type {?number} */
|
|
222
223
|
this.tileDuration = tileDuration;
|
|
223
224
|
|
|
224
|
-
/**
|
|
225
|
+
/**
|
|
226
|
+
* A time value, expressed in seconds since 1970, which is used to
|
|
227
|
+
* synchronize between streams. Both produced and consumed by the HLS
|
|
228
|
+
* parser. Other components should not need this value.
|
|
229
|
+
*
|
|
230
|
+
* @type {?number}
|
|
231
|
+
*/
|
|
225
232
|
this.syncTime = syncTime;
|
|
226
233
|
|
|
227
234
|
/** @type {shaka.media.SegmentReference.Status} */
|
|
@@ -1602,11 +1602,10 @@ shaka.media.StreamingEngine = class {
|
|
|
1602
1602
|
await this.playerInterface_.mediaSourceEngine.appendBuffer(
|
|
1603
1603
|
mediaState.type,
|
|
1604
1604
|
segment,
|
|
1605
|
-
reference.
|
|
1605
|
+
reference.startTime,
|
|
1606
1606
|
reference.endTime,
|
|
1607
1607
|
hasClosedCaptions,
|
|
1608
|
-
seeked
|
|
1609
|
-
this.manifest_.sequenceMode);
|
|
1608
|
+
seeked);
|
|
1610
1609
|
this.destroyer_.ensureNotDestroyed();
|
|
1611
1610
|
shaka.log.v2(logPrefix, 'appended media segment');
|
|
1612
1611
|
}
|
|
@@ -368,8 +368,10 @@ shaka.offline.indexeddb.StorageMechanism.SESSION_ID_STORE = 'session-ids';
|
|
|
368
368
|
shaka.offline.StorageMuxer.register(
|
|
369
369
|
'idb',
|
|
370
370
|
() => {
|
|
371
|
-
// Offline storage is not supported on the Chromecast
|
|
372
|
-
|
|
371
|
+
// Offline storage is not supported on the Chromecast or Xbox One
|
|
372
|
+
// platforms.
|
|
373
|
+
if (shaka.util.Platform.isChromecast() ||
|
|
374
|
+
shaka.util.Platform.isXboxOne()) {
|
|
373
375
|
return null;
|
|
374
376
|
}
|
|
375
377
|
// Offline storage requires the IndexedDB API.
|