shaka-player 3.3.4 → 4.0.1
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/build-and-test.yaml +0 -1
- package/.github/workflows/demo-version-index.yaml +8 -0
- package/.github/workflows/release-please.yaml +19 -3
- package/.github/workflows/selenium-lab-tests.yaml +0 -1
- package/.github/workflows/sync-labels.yaml +33 -0
- package/.github/workflows/update-issues.yaml +29 -0
- package/.github/workflows/validate-pr-title.yaml +35 -0
- package/AUTHORS +1 -0
- package/CHANGELOG.md +209 -28
- package/CONTRIBUTORS +1 -0
- package/README.md +10 -7
- package/app-engine/demo-version-index/README.md +3 -3
- package/app-engine/demo-version-index/app.yaml +27 -1
- package/app-engine/demo-version-index/generate.py +164 -0
- package/app-engine/demo-version-index/main.py +4 -155
- package/app-engine/demo-version-index/requirements.txt +0 -1
- package/build/README.md +2 -2
- package/build/compiler.py +1 -1
- package/build/generateTsDefs.py +0 -2
- package/build/misspellings.txt +1 -0
- package/build/shaka-lab.yaml +0 -2
- package/build/test.py +7 -1
- package/build/types/core +0 -1
- package/build/types/polyfill +0 -3
- package/demo/common/asset.js +17 -1
- package/demo/common/assets.js +29 -0
- package/demo/common/message_ids.js +6 -3
- package/demo/config.js +10 -11
- package/demo/custom.js +39 -0
- package/demo/demo_utils.js +0 -1
- package/demo/locales/en.json +6 -3
- package/demo/locales/source.json +22 -10
- package/demo/main.js +9 -6
- package/demo/search.js +2 -1
- package/dist/controls.css +1 -1
- package/dist/demo.compiled.debug.js +71 -64
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +71 -64
- package/dist/demo.compiled.map +3 -3
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +16 -20
- package/dist/locales.js +33 -31
- package/dist/receiver.compiled.debug.js +32 -28
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +32 -28
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +78 -82
- package/dist/shaka-player.compiled.debug.d.ts +78 -82
- package/dist/shaka-player.compiled.debug.externs.js +127 -126
- package/dist/shaka-player.compiled.debug.js +471 -523
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +127 -126
- package/dist/shaka-player.compiled.js +844 -858
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +80 -82
- package/dist/shaka-player.ui.debug.d.ts +80 -82
- package/dist/shaka-player.ui.debug.externs.js +135 -126
- package/dist/shaka-player.ui.debug.js +531 -581
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +135 -126
- package/dist/shaka-player.ui.js +991 -1001
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/config.md +0 -2
- package/docs/tutorials/drm-config.md +5 -0
- package/docs/tutorials/fairplay.md +7 -22
- package/docs/tutorials/faq.md +0 -11
- package/docs/tutorials/index.json +2 -8
- package/docs/tutorials/plugins.md +1 -3
- package/docs/tutorials/upgrade.md +85 -11
- package/externs/polyfill.js +15 -0
- package/externs/shaka/manifest.js +5 -1
- package/externs/shaka/net.js +12 -0
- package/externs/shaka/offline.js +5 -1
- package/externs/shaka/player.js +39 -21
- package/externs/shaka/text.js +12 -12
- package/externs/sourcebuffer.js +15 -0
- package/karma.conf.js +4 -0
- package/lib/ads/client_side_ad.js +13 -2
- package/lib/ads/client_side_ad_manager.js +22 -4
- package/lib/cast/cast_utils.js +0 -1
- package/lib/dash/content_protection.js +85 -5
- package/lib/dash/dash_parser.js +3 -3
- package/lib/hls/hls_parser.js +391 -747
- package/lib/hls/manifest_text_parser.js +2 -3
- package/lib/media/drm_engine.js +102 -37
- package/lib/media/gap_jumping_controller.js +10 -45
- package/lib/media/media_source_engine.js +157 -31
- package/lib/media/playhead.js +2 -5
- package/lib/media/presentation_timeline.js +1 -2
- package/lib/media/segment_index.js +13 -46
- package/lib/media/segment_reference.js +47 -1
- package/lib/media/streaming_engine.js +28 -11
- package/lib/media/time_ranges_utils.js +4 -9
- package/lib/net/networking_engine.js +3 -0
- package/lib/offline/indexeddb/storage_mechanism.js +4 -2
- package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
- package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
- package/lib/offline/manifest_converter.js +1 -0
- package/lib/offline/storage.js +100 -132
- package/lib/player.js +181 -307
- package/lib/polyfill/media_capabilities.js +12 -6
- package/lib/polyfill/mediasource.js +0 -28
- package/lib/polyfill/patchedmediakeys_nop.js +2 -0
- package/lib/polyfill/patchedmediakeys_webkit.js +2 -0
- package/lib/text/cue.js +0 -6
- package/lib/text/lrc_text_parser.js +8 -0
- package/lib/text/mp4_ttml_parser.js +8 -0
- package/lib/text/mp4_vtt_parser.js +8 -0
- package/lib/text/sbv_text_parser.js +8 -0
- package/lib/text/simple_text_displayer.js +1 -7
- package/lib/text/srt_text_parser.js +8 -0
- package/lib/text/ssa_text_parser.js +8 -0
- package/lib/text/text_engine.js +19 -3
- package/lib/text/ttml_text_parser.js +8 -0
- package/lib/text/ui_text_displayer.js +1 -7
- package/lib/text/vtt_text_parser.js +27 -5
- package/lib/text/web_vtt_generator.js +1 -7
- package/lib/util/config_utils.js +25 -0
- package/lib/util/error.js +16 -13
- package/lib/util/fairplay_utils.js +109 -0
- package/lib/util/functional.js +0 -42
- package/lib/util/manifest_parser_utils.js +1 -1
- package/lib/util/mime_utils.js +50 -1
- package/lib/util/periods.js +5 -4
- package/lib/util/platform.js +45 -0
- package/lib/util/player_configuration.js +26 -20
- package/lib/util/pssh.js +23 -4
- package/lib/util/stream_utils.js +96 -24
- package/lib/util/uint8array_utils.js +0 -16
- package/lib/util/xml_utils.js +1 -1
- package/package.json +4 -1
- package/roadmap.md +19 -19
- package/shaka-player.uncompiled.js +0 -3
- package/test/cast/cast_utils_unit.js +1 -0
- package/test/dash/dash_parser_content_protection_unit.js +44 -0
- package/test/dash/dash_parser_manifest_unit.js +0 -24
- package/test/dash/dash_parser_segment_template_unit.js +3 -4
- package/test/demo/demo_unit.js +2 -1
- package/test/hls/hls_live_unit.js +121 -220
- package/test/hls/hls_parser_unit.js +515 -432
- package/test/hls/manifest_text_parser_unit.js +19 -0
- package/test/media/drm_engine_unit.js +86 -120
- package/test/media/media_source_engine_integration.js +26 -3
- package/test/media/media_source_engine_unit.js +7 -6
- package/test/media/playhead_unit.js +27 -191
- package/test/media/segment_index_unit.js +15 -25
- package/test/media/streaming_engine_integration.js +4 -39
- package/test/media/streaming_engine_unit.js +3 -2
- package/test/media/time_ranges_utils_unit.js +0 -5
- package/test/offline/indexeddb_storage_unit.js +1 -1
- package/test/offline/manifest_convert_unit.js +34 -4
- package/test/offline/offline_manifest_parser_unit.js +1 -0
- package/test/offline/storage_compatibility_unit.js +1 -1
- package/test/offline/storage_integration.js +5 -4
- package/test/player_external.js +7 -7
- package/test/player_integration.js +22 -10
- package/test/player_src_equals_integration.js +81 -83
- package/test/player_unit.js +124 -1
- package/test/test/boot.js +9 -0
- package/test/test/externs/filters.js +4 -0
- package/test/test/util/fake_media_source_engine.js +11 -4
- package/test/test/util/manifest_generator.js +3 -1
- package/test/test/util/manifest_parser_util.js +4 -1
- package/test/test/util/offline_utils.js +1 -0
- package/test/test/util/simple_fakes.js +31 -2
- package/test/test/util/streaming_engine_util.js +1 -0
- package/test/test/util/test_scheme.js +24 -1
- package/test/test/util/util.js +4 -4
- package/test/text/cue_integration.js +2 -2
- package/test/text/lrc_text_parser_unit.js +6 -7
- package/test/text/mp4_ttml_parser_unit.js +6 -4
- package/test/text/mp4_vtt_parser_unit.js +6 -5
- package/test/text/sbv_text_parser_unit.js +5 -6
- package/test/text/srt_text_parser_unit.js +5 -6
- package/test/text/ssa_text_parser_unit.js +17 -29
- package/test/text/text_engine_unit.js +45 -5
- package/test/text/ttml_text_parser_unit.js +77 -77
- package/test/text/vtt_text_parser_unit.js +116 -59
- package/test/ui/ui_integration.js +0 -16
- package/test/ui/ui_unit.js +27 -0
- package/test/util/cmcd_manager_unit.js +3 -0
- package/test/util/mime_utils_unit.js +54 -0
- package/test/util/periods_unit.js +79 -0
- package/test/util/stream_utils_unit.js +23 -7
- package/ui/controls.js +65 -40
- package/ui/fullscreen_button.js +26 -6
- package/ui/less/containers.less +3 -3
- package/ui/locales/en.json +1 -0
- package/ui/locales/source.json +5 -0
- package/ui/resolution_selection.js +31 -33
- package/ui/ui.js +0 -17
- package/docs/tutorials/gsoc-ideas-list.md +0 -3
- package/docs/tutorials/upgrade-v2-5.md +0 -441
- package/docs/tutorials/upgrade-v3.md +0 -38
- package/lib/polyfill/mathround.js +0 -49
- package/lib/polyfill/patchedmediakeys_apple.js +0 -744
- package/lib/polyfill/patchedmediakeys_ms.js +0 -698
- package/lib/util/mutex.js +0 -52
- package/test/util/functional_unit.js +0 -82
|
@@ -63,9 +63,17 @@ shaka.media.MediaSourceEngine = class {
|
|
|
63
63
|
string>} */
|
|
64
64
|
this.sourceBufferTypes_ = {};
|
|
65
65
|
|
|
66
|
+
|
|
67
|
+
/** @private {!Object.<shaka.util.ManifestParserUtils.ContentType,
|
|
68
|
+
boolean>} */
|
|
69
|
+
this.expectedEncryption_ = {};
|
|
70
|
+
|
|
66
71
|
/** @private {shaka.text.TextEngine} */
|
|
67
72
|
this.textEngine_ = null;
|
|
68
73
|
|
|
74
|
+
/** @private {boolean} */
|
|
75
|
+
this.segmentRelativeVttTiming_ = false;
|
|
76
|
+
|
|
69
77
|
const onMetadataNoOp = (metadata, timestampOffset, segmentEnd) => {};
|
|
70
78
|
|
|
71
79
|
/** @private {!function(!Array.<shaka.extern.ID3Metadata>,
|
|
@@ -98,6 +106,12 @@ shaka.media.MediaSourceEngine = class {
|
|
|
98
106
|
|
|
99
107
|
/** @private {string} */
|
|
100
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();
|
|
101
115
|
}
|
|
102
116
|
|
|
103
117
|
/**
|
|
@@ -196,6 +210,8 @@ shaka.media.MediaSourceEngine = class {
|
|
|
196
210
|
// TTML types
|
|
197
211
|
'application/ttml+xml',
|
|
198
212
|
'application/mp4; codecs="stpp"',
|
|
213
|
+
// Containerless types
|
|
214
|
+
...shaka.media.MediaSourceEngine.RAW_FORMATS,
|
|
199
215
|
];
|
|
200
216
|
|
|
201
217
|
const support = {};
|
|
@@ -310,14 +326,19 @@ shaka.media.MediaSourceEngine = class {
|
|
|
310
326
|
* according to MediaSourceEngine.isStreamSupported.
|
|
311
327
|
* @param {boolean} forceTransmuxTS
|
|
312
328
|
* If true, this will transmux TS content even if it is natively supported.
|
|
329
|
+
* @param {boolean=} sequenceMode
|
|
330
|
+
* If true, the media segments are appended to the SourceBuffer in strict
|
|
331
|
+
* sequence.
|
|
313
332
|
*
|
|
314
333
|
* @return {!Promise}
|
|
315
334
|
*/
|
|
316
|
-
async init(streamsByType, forceTransmuxTS) {
|
|
335
|
+
async init(streamsByType, forceTransmuxTS, sequenceMode=false) {
|
|
317
336
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
318
337
|
|
|
319
338
|
await this.mediaSourceOpen_;
|
|
320
339
|
|
|
340
|
+
this.sequenceMode_ = sequenceMode;
|
|
341
|
+
|
|
321
342
|
for (const contentType of streamsByType.keys()) {
|
|
322
343
|
const stream = streamsByType.get(contentType);
|
|
323
344
|
goog.asserts.assert(
|
|
@@ -327,7 +348,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
327
348
|
let mimeType = shaka.util.MimeUtils.getFullType(
|
|
328
349
|
stream.mimeType, stream.codecs);
|
|
329
350
|
if (contentType == ContentType.TEXT) {
|
|
330
|
-
this.reinitText(mimeType);
|
|
351
|
+
this.reinitText(mimeType, sequenceMode);
|
|
331
352
|
} else {
|
|
332
353
|
if ((forceTransmuxTS || !MediaSource.isTypeSupported(mimeType)) &&
|
|
333
354
|
shaka.media.Transmuxer.isSupported(mimeType, contentType)) {
|
|
@@ -335,7 +356,9 @@ shaka.media.MediaSourceEngine = class {
|
|
|
335
356
|
mimeType =
|
|
336
357
|
shaka.media.Transmuxer.convertTsCodecs(contentType, mimeType);
|
|
337
358
|
}
|
|
359
|
+
|
|
338
360
|
const sourceBuffer = this.mediaSource_.addSourceBuffer(mimeType);
|
|
361
|
+
|
|
339
362
|
this.eventManager_.listen(
|
|
340
363
|
sourceBuffer, 'error',
|
|
341
364
|
() => this.onError_(contentType));
|
|
@@ -345,6 +368,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
345
368
|
this.sourceBuffers_[contentType] = sourceBuffer;
|
|
346
369
|
this.sourceBufferTypes_[contentType] = mimeType;
|
|
347
370
|
this.queues_[contentType] = [];
|
|
371
|
+
this.expectedEncryption_[contentType] = !!stream.drmInfos.length;
|
|
348
372
|
}
|
|
349
373
|
}
|
|
350
374
|
}
|
|
@@ -352,12 +376,14 @@ shaka.media.MediaSourceEngine = class {
|
|
|
352
376
|
/**
|
|
353
377
|
* Reinitialize the TextEngine for a new text type.
|
|
354
378
|
* @param {string} mimeType
|
|
379
|
+
* @param {boolean} sequenceMode
|
|
355
380
|
*/
|
|
356
|
-
reinitText(mimeType) {
|
|
381
|
+
reinitText(mimeType, sequenceMode) {
|
|
357
382
|
if (!this.textEngine_) {
|
|
358
383
|
this.textEngine_ = new shaka.text.TextEngine(this.textDisplayer_);
|
|
359
384
|
}
|
|
360
|
-
this.textEngine_.initParser(mimeType
|
|
385
|
+
this.textEngine_.initParser(mimeType, sequenceMode,
|
|
386
|
+
this.segmentRelativeVttTiming_);
|
|
361
387
|
}
|
|
362
388
|
|
|
363
389
|
/**
|
|
@@ -404,17 +430,15 @@ shaka.media.MediaSourceEngine = class {
|
|
|
404
430
|
*
|
|
405
431
|
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
|
|
406
432
|
* @param {number} time Playhead time
|
|
407
|
-
* @param {number=} smallGapLimit
|
|
408
433
|
* @return {boolean}
|
|
409
434
|
*/
|
|
410
|
-
isBuffered(contentType, time
|
|
435
|
+
isBuffered(contentType, time) {
|
|
411
436
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
412
437
|
if (contentType == ContentType.TEXT) {
|
|
413
438
|
return this.textEngine_.isBuffered(time);
|
|
414
439
|
} else {
|
|
415
440
|
const buffered = this.getBuffered_(contentType);
|
|
416
|
-
return shaka.media.TimeRangesUtils.isBuffered(
|
|
417
|
-
buffered, time, smallGapLimit);
|
|
441
|
+
return shaka.media.TimeRangesUtils.isBuffered(buffered, time);
|
|
418
442
|
}
|
|
419
443
|
}
|
|
420
444
|
|
|
@@ -496,20 +520,30 @@ shaka.media.MediaSourceEngine = class {
|
|
|
496
520
|
* @param {?number} endTime relative to the start of the presentation
|
|
497
521
|
* @param {?boolean} hasClosedCaptions True if the buffer contains CEA closed
|
|
498
522
|
* captions
|
|
523
|
+
* @param {boolean=} seeked True if we just seeked
|
|
499
524
|
* @return {!Promise}
|
|
500
525
|
*/
|
|
501
|
-
async appendBuffer(
|
|
526
|
+
async appendBuffer(
|
|
527
|
+
contentType, data, startTime, endTime, hasClosedCaptions, seeked) {
|
|
502
528
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
503
529
|
|
|
504
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
|
+
}
|
|
505
536
|
await this.textEngine_.appendBuffer(data, startTime, endTime);
|
|
506
|
-
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (this.transmuxers_[contentType]) {
|
|
507
541
|
const transmuxedData =
|
|
508
542
|
await this.transmuxers_[contentType].transmux(data);
|
|
509
543
|
// For HLS CEA-608/708 CLOSED-CAPTIONS, text data is embedded in
|
|
510
544
|
// the video stream, so textEngine may not have been initialized.
|
|
511
545
|
if (!this.textEngine_) {
|
|
512
|
-
this.reinitText('text/vtt');
|
|
546
|
+
this.reinitText('text/vtt', this.sequenceMode_);
|
|
513
547
|
}
|
|
514
548
|
|
|
515
549
|
if (transmuxedData.metadata) {
|
|
@@ -528,15 +562,10 @@ shaka.media.MediaSourceEngine = class {
|
|
|
528
562
|
closedCaptions, startTime, endTime, videoOffset);
|
|
529
563
|
}
|
|
530
564
|
|
|
531
|
-
|
|
532
|
-
transmuxedSegment = this.workAroundBrokenPlatforms_(
|
|
533
|
-
transmuxedSegment, startTime, contentType);
|
|
534
|
-
|
|
535
|
-
await this.enqueueOperation_(
|
|
536
|
-
contentType, () => this.append_(contentType, transmuxedSegment));
|
|
565
|
+
data = transmuxedData.data;
|
|
537
566
|
} else if (hasClosedCaptions) {
|
|
538
567
|
if (!this.textEngine_) {
|
|
539
|
-
this.reinitText('text/vtt');
|
|
568
|
+
this.reinitText('text/vtt', this.sequenceMode_);
|
|
540
569
|
}
|
|
541
570
|
// If it is the init segment for closed captions, initialize the closed
|
|
542
571
|
// caption parser.
|
|
@@ -551,19 +580,78 @@ shaka.media.MediaSourceEngine = class {
|
|
|
551
580
|
closedCaptions, startTime, endTime, videoOffset);
|
|
552
581
|
}
|
|
553
582
|
}
|
|
583
|
+
}
|
|
554
584
|
|
|
555
|
-
|
|
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
|
+
}
|
|
556
634
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
} else {
|
|
561
|
-
data = this.workAroundBrokenPlatforms_(data, startTime, contentType);
|
|
635
|
+
// Now switch to sequence mode and fall through to our normal operations.
|
|
636
|
+
sourceBuffer.mode = SEQUENCE;
|
|
637
|
+
}
|
|
562
638
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
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
|
+
}
|
|
566
650
|
}
|
|
651
|
+
|
|
652
|
+
await this.enqueueOperation_(
|
|
653
|
+
contentType,
|
|
654
|
+
() => this.append_(contentType, data));
|
|
567
655
|
}
|
|
568
656
|
|
|
569
657
|
/**
|
|
@@ -664,13 +752,18 @@ shaka.media.MediaSourceEngine = class {
|
|
|
664
752
|
* @param {number} appendWindowEnd The timestamp to set the append window end
|
|
665
753
|
* to. For future appends, frames/samples with timestamps greater than this
|
|
666
754
|
* value will be dropped.
|
|
755
|
+
* @param {boolean} sequenceMode If true, the timestampOffset will not be
|
|
756
|
+
* applied in this step.
|
|
667
757
|
* @return {!Promise}
|
|
668
758
|
*/
|
|
669
759
|
async setStreamProperties(
|
|
670
|
-
contentType, timestampOffset, appendWindowStart, appendWindowEnd
|
|
760
|
+
contentType, timestampOffset, appendWindowStart, appendWindowEnd,
|
|
761
|
+
sequenceMode) {
|
|
671
762
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
672
763
|
if (contentType == ContentType.TEXT) {
|
|
673
|
-
|
|
764
|
+
if (!sequenceMode) {
|
|
765
|
+
this.textEngine_.setTimestampOffset(timestampOffset);
|
|
766
|
+
}
|
|
674
767
|
this.textEngine_.setAppendWindow(appendWindowStart, appendWindowEnd);
|
|
675
768
|
return;
|
|
676
769
|
}
|
|
@@ -687,7 +780,10 @@ shaka.media.MediaSourceEngine = class {
|
|
|
687
780
|
this.enqueueOperation_(
|
|
688
781
|
contentType,
|
|
689
782
|
() => this.abort_(contentType)),
|
|
690
|
-
|
|
783
|
+
// Don't set the timestampOffset here when in sequenceMode, since we
|
|
784
|
+
// use timestampOffset for a different purpose in that mode (e.g. to
|
|
785
|
+
// indicate where the current segment is).
|
|
786
|
+
sequenceMode ? Promise.resolve() : this.enqueueOperation_(
|
|
691
787
|
contentType,
|
|
692
788
|
() => this.setTimestampOffset_(contentType, timestampOffset)),
|
|
693
789
|
this.enqueueOperation_(
|
|
@@ -1079,6 +1175,13 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1079
1175
|
}
|
|
1080
1176
|
}
|
|
1081
1177
|
|
|
1178
|
+
/**
|
|
1179
|
+
* @param {boolean} segmentRelativeVttTiming
|
|
1180
|
+
*/
|
|
1181
|
+
setSegmentRelativeVttTiming(segmentRelativeVttTiming) {
|
|
1182
|
+
this.segmentRelativeVttTiming_ = segmentRelativeVttTiming;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1082
1185
|
/**
|
|
1083
1186
|
* Apply platform-specific transformations to this segment to work around
|
|
1084
1187
|
* issues in the platform.
|
|
@@ -1091,7 +1194,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1091
1194
|
*/
|
|
1092
1195
|
workAroundBrokenPlatforms_(segment, startTime, contentType) {
|
|
1093
1196
|
const isInitSegment = startTime == null;
|
|
1094
|
-
const encryptionExpected = this.
|
|
1197
|
+
const encryptionExpected = this.expectedEncryption_[contentType];
|
|
1095
1198
|
|
|
1096
1199
|
// If:
|
|
1097
1200
|
// 1. this is an init segment,
|
|
@@ -1139,3 +1242,26 @@ shaka.media.MediaSourceEngine.createObjectURL = window.URL.createObjectURL;
|
|
|
1139
1242
|
* The PublicPromise which is associated with this operation.
|
|
1140
1243
|
*/
|
|
1141
1244
|
shaka.media.MediaSourceEngine.Operation;
|
|
1245
|
+
|
|
1246
|
+
|
|
1247
|
+
/**
|
|
1248
|
+
* @enum {string}
|
|
1249
|
+
* @private
|
|
1250
|
+
*/
|
|
1251
|
+
shaka.media.MediaSourceEngine.SourceBufferMode_ = {
|
|
1252
|
+
SEQUENCE: 'sequence',
|
|
1253
|
+
SEGMENTS: 'segments',
|
|
1254
|
+
};
|
|
1255
|
+
|
|
1256
|
+
|
|
1257
|
+
/**
|
|
1258
|
+
* MIME types of raw formats.
|
|
1259
|
+
*
|
|
1260
|
+
* @const {!Array.<string>}
|
|
1261
|
+
*/
|
|
1262
|
+
shaka.media.MediaSourceEngine.RAW_FORMATS = [
|
|
1263
|
+
'audio/aac',
|
|
1264
|
+
'audio/ac3',
|
|
1265
|
+
'audio/ec3',
|
|
1266
|
+
'audio/mpeg',
|
|
1267
|
+
];
|
package/lib/media/playhead.js
CHANGED
|
@@ -160,10 +160,8 @@ shaka.media.MediaSourcePlayhead = class {
|
|
|
160
160
|
* @param {function()} onSeek
|
|
161
161
|
* Called when the user agent seeks to a time within the presentation
|
|
162
162
|
* timeline.
|
|
163
|
-
* @param {function(!Event)} onEvent
|
|
164
|
-
* Called when an event is raised to be sent to the application.
|
|
165
163
|
*/
|
|
166
|
-
constructor(mediaElement, manifest, config, startTime, onSeek
|
|
164
|
+
constructor(mediaElement, manifest, config, startTime, onSeek) {
|
|
167
165
|
/**
|
|
168
166
|
* The seek range must be at least this number of seconds long. If it is
|
|
169
167
|
* smaller than this, change it to be this big so we don't repeatedly seek
|
|
@@ -199,8 +197,7 @@ shaka.media.MediaSourcePlayhead = class {
|
|
|
199
197
|
mediaElement,
|
|
200
198
|
manifest.presentationTimeline,
|
|
201
199
|
config,
|
|
202
|
-
this.createStallDetector_(mediaElement, config)
|
|
203
|
-
onEvent);
|
|
200
|
+
this.createStallDetector_(mediaElement, config));
|
|
204
201
|
|
|
205
202
|
/** @private {shaka.media.VideoWrapper} */
|
|
206
203
|
this.videoWrapper_ = new shaka.media.VideoWrapper(
|
|
@@ -249,8 +249,7 @@ shaka.media.PresentationTimeline = class {
|
|
|
249
249
|
* @param {number} startTime
|
|
250
250
|
* @export
|
|
251
251
|
*/
|
|
252
|
-
notifyMinSegmentStartTime(
|
|
253
|
-
startTime) {
|
|
252
|
+
notifyMinSegmentStartTime(startTime) {
|
|
254
253
|
if (this.minSegmentStartTime_ == null) {
|
|
255
254
|
// No data yet, and Math.min(null, startTime) is always 0. So just store
|
|
256
255
|
// startTime.
|
|
@@ -9,7 +9,6 @@ goog.provide('shaka.media.SegmentIndex');
|
|
|
9
9
|
goog.provide('shaka.media.SegmentIterator');
|
|
10
10
|
|
|
11
11
|
goog.require('goog.asserts');
|
|
12
|
-
goog.require('shaka.Deprecate');
|
|
13
12
|
goog.require('shaka.media.SegmentReference');
|
|
14
13
|
goog.require('shaka.util.IReleasable');
|
|
15
14
|
goog.require('shaka.util.Timer');
|
|
@@ -52,23 +51,6 @@ shaka.media.SegmentIndex = class {
|
|
|
52
51
|
}
|
|
53
52
|
|
|
54
53
|
|
|
55
|
-
/**
|
|
56
|
-
* SegmentIndex used to be an IDestroyable. Now it is an IReleasable.
|
|
57
|
-
* This method is provided for backward compatibility.
|
|
58
|
-
*
|
|
59
|
-
* @deprecated
|
|
60
|
-
* @return {!Promise}
|
|
61
|
-
* @export
|
|
62
|
-
*/
|
|
63
|
-
destroy() {
|
|
64
|
-
shaka.Deprecate.deprecateFeature(4,
|
|
65
|
-
'shaka.media.SegmentIndex',
|
|
66
|
-
'Please use release() instead of destroy().');
|
|
67
|
-
this.release();
|
|
68
|
-
return Promise.resolve();
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
54
|
/**
|
|
73
55
|
* @override
|
|
74
56
|
* @export
|
|
@@ -101,6 +83,17 @@ shaka.media.SegmentIndex = class {
|
|
|
101
83
|
}
|
|
102
84
|
|
|
103
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Iterates over all top-level segment references in this segment index.
|
|
88
|
+
* @param {function(!shaka.media.SegmentReference)} fn
|
|
89
|
+
*/
|
|
90
|
+
forEachTopLevelReference(fn) {
|
|
91
|
+
for (const reference of this.references) {
|
|
92
|
+
fn(reference);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
104
97
|
/**
|
|
105
98
|
* Finds the position of the segment for the given time, in seconds, relative
|
|
106
99
|
* to the start of the presentation. Returns the position of the segment
|
|
@@ -191,9 +184,6 @@ shaka.media.SegmentIndex = class {
|
|
|
191
184
|
* @param {!Array.<!shaka.media.SegmentReference>} references The list of
|
|
192
185
|
* SegmentReferences, which must be sorted first by their start times
|
|
193
186
|
* (ascending) and second by their end times (ascending).
|
|
194
|
-
* @deprecated Not used directly by our own parsers, so will become private in
|
|
195
|
-
* v4. Use mergeAndEvict() instead.
|
|
196
|
-
* @export
|
|
197
187
|
*/
|
|
198
188
|
merge(references) {
|
|
199
189
|
if (goog.DEBUG) {
|
|
@@ -348,7 +338,8 @@ shaka.media.SegmentIndex = class {
|
|
|
348
338
|
lastReference.appendWindowEnd,
|
|
349
339
|
lastReference.partialReferences,
|
|
350
340
|
lastReference.tilesLayout,
|
|
351
|
-
lastReference.tileDuration
|
|
341
|
+
lastReference.tileDuration,
|
|
342
|
+
lastReference.syncTime);
|
|
352
343
|
}
|
|
353
344
|
|
|
354
345
|
|
|
@@ -513,30 +504,6 @@ shaka.media.SegmentIterator = class {
|
|
|
513
504
|
this.currentPartialPosition_ = partialSegmentIndex;
|
|
514
505
|
}
|
|
515
506
|
|
|
516
|
-
/**
|
|
517
|
-
* Move the iterator to a given timestamp in the underlying SegmentIndex.
|
|
518
|
-
*
|
|
519
|
-
* @param {number} time
|
|
520
|
-
* @return {shaka.media.SegmentReference}
|
|
521
|
-
* @deprecated Use SegmentIndex.getIteratorForTime instead
|
|
522
|
-
* @export
|
|
523
|
-
*/
|
|
524
|
-
seek(time) {
|
|
525
|
-
shaka.Deprecate.deprecateFeature(
|
|
526
|
-
4, 'shaka.media.SegmentIterator',
|
|
527
|
-
'Please use SegmentIndex.getIteratorForTime instead of seek().');
|
|
528
|
-
|
|
529
|
-
const iter = this.segmentIndex_.getIteratorForTime(time);
|
|
530
|
-
if (iter) {
|
|
531
|
-
this.currentPosition_ = iter.currentPosition_;
|
|
532
|
-
this.currentPartialPosition_ = iter.currentPartialPosition_;
|
|
533
|
-
} else {
|
|
534
|
-
this.currentPosition_ = Number.MAX_VALUE;
|
|
535
|
-
this.currentPartialPosition_ = 0;
|
|
536
|
-
}
|
|
537
|
-
return this.next().value;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
507
|
/**
|
|
541
508
|
* @return {shaka.media.SegmentReference}
|
|
542
509
|
* @export
|
|
@@ -159,11 +159,18 @@ shaka.media.SegmentReference = class {
|
|
|
159
159
|
* The explicit duration of an individual tile within the tiles grid.
|
|
160
160
|
* If not provided, the duration should be automatically calculated based on
|
|
161
161
|
* the duration of the reference.
|
|
162
|
+
* @param {?number=} syncTime
|
|
163
|
+
* A time value, expressed in the same scale as the start and end time, which
|
|
164
|
+
* is used to synchronize between streams.
|
|
165
|
+
* @param {shaka.media.SegmentReference.Status=} status
|
|
166
|
+
* The segment status is used to indicate that a segment does not exist or is
|
|
167
|
+
* not available.
|
|
162
168
|
*/
|
|
163
169
|
constructor(
|
|
164
170
|
startTime, endTime, uris, startByte, endByte, initSegmentReference,
|
|
165
171
|
timestampOffset, appendWindowStart, appendWindowEnd,
|
|
166
|
-
partialReferences = [], tilesLayout = '', tileDuration = null
|
|
172
|
+
partialReferences = [], tilesLayout = '', tileDuration = null,
|
|
173
|
+
syncTime = null, status = shaka.media.SegmentReference.Status.AVAILABLE) {
|
|
167
174
|
// A preload hinted Partial Segment has the same startTime and endTime.
|
|
168
175
|
goog.asserts.assert(startTime <= endTime,
|
|
169
176
|
'startTime must be less than or equal to endTime');
|
|
@@ -213,6 +220,12 @@ shaka.media.SegmentReference = class {
|
|
|
213
220
|
|
|
214
221
|
/** @type {?number} */
|
|
215
222
|
this.tileDuration = tileDuration;
|
|
223
|
+
|
|
224
|
+
/** @type {?number} */
|
|
225
|
+
this.syncTime = syncTime;
|
|
226
|
+
|
|
227
|
+
/** @type {shaka.media.SegmentReference.Status} */
|
|
228
|
+
this.status = status;
|
|
216
229
|
}
|
|
217
230
|
|
|
218
231
|
/**
|
|
@@ -308,6 +321,39 @@ shaka.media.SegmentReference = class {
|
|
|
308
321
|
getTileDuration() {
|
|
309
322
|
return this.tileDuration;
|
|
310
323
|
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Returns the segment's status.
|
|
327
|
+
*
|
|
328
|
+
* @return {shaka.media.SegmentReference.Status}
|
|
329
|
+
* @export
|
|
330
|
+
*/
|
|
331
|
+
getStatus() {
|
|
332
|
+
return this.status;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Mark the reference as unavailable.
|
|
337
|
+
*
|
|
338
|
+
* @export
|
|
339
|
+
*/
|
|
340
|
+
markAsUnavailable() {
|
|
341
|
+
this.status = shaka.media.SegmentReference.Status.UNAVAILABLE;
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Rather than using booleans to communicate what the state of the reference,
|
|
348
|
+
* we have this enum.
|
|
349
|
+
*
|
|
350
|
+
* @enum {number}
|
|
351
|
+
* @export
|
|
352
|
+
*/
|
|
353
|
+
shaka.media.SegmentReference.Status = {
|
|
354
|
+
AVAILABLE: 0,
|
|
355
|
+
UNAVAILABLE: 1,
|
|
356
|
+
MISSING: 2,
|
|
311
357
|
};
|
|
312
358
|
|
|
313
359
|
|
|
@@ -234,7 +234,8 @@ shaka.media.StreamingEngine = class {
|
|
|
234
234
|
|
|
235
235
|
const mimeType = shaka.util.MimeUtils.getFullType(
|
|
236
236
|
stream.mimeType, stream.codecs);
|
|
237
|
-
this.playerInterface_.mediaSourceEngine.reinitText(
|
|
237
|
+
this.playerInterface_.mediaSourceEngine.reinitText(
|
|
238
|
+
mimeType, this.manifest_.sequenceMode);
|
|
238
239
|
|
|
239
240
|
const textDisplayer =
|
|
240
241
|
this.playerInterface_.mediaSourceEngine.getTextDisplayer();
|
|
@@ -428,7 +429,8 @@ shaka.media.StreamingEngine = class {
|
|
|
428
429
|
// init segment again.
|
|
429
430
|
const fullMimeType = shaka.util.MimeUtils.getFullType(
|
|
430
431
|
stream.mimeType, stream.codecs);
|
|
431
|
-
this.playerInterface_.mediaSourceEngine.reinitText(
|
|
432
|
+
this.playerInterface_.mediaSourceEngine.reinitText(
|
|
433
|
+
fullMimeType, this.manifest_.sequenceMode);
|
|
432
434
|
}
|
|
433
435
|
|
|
434
436
|
// Releases the segmentIndex of the old stream.
|
|
@@ -604,11 +606,10 @@ shaka.media.StreamingEngine = class {
|
|
|
604
606
|
*/
|
|
605
607
|
seeked() {
|
|
606
608
|
const presentationTime = this.playerInterface_.getPresentationTime();
|
|
607
|
-
const smallGapLimit = this.config_.smallGapLimit;
|
|
608
609
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
609
610
|
const newTimeIsBuffered = (type) => {
|
|
610
611
|
return this.playerInterface_.mediaSourceEngine.isBuffered(
|
|
611
|
-
type, presentationTime
|
|
612
|
+
type, presentationTime);
|
|
612
613
|
};
|
|
613
614
|
|
|
614
615
|
let streamCleared = false;
|
|
@@ -645,6 +646,10 @@ shaka.media.StreamingEngine = class {
|
|
|
645
646
|
if (type === ContentType.TEXT) {
|
|
646
647
|
this.playerInterface_.mediaSourceEngine.resetCaptionParser();
|
|
647
648
|
}
|
|
649
|
+
|
|
650
|
+
// Mark the media state as having seeked, so that the new buffers know
|
|
651
|
+
// that they will need to be at a new position (for sequence mode).
|
|
652
|
+
mediaState.seeked = true;
|
|
648
653
|
}
|
|
649
654
|
}
|
|
650
655
|
|
|
@@ -764,7 +769,9 @@ shaka.media.StreamingEngine = class {
|
|
|
764
769
|
const mediaSourceEngine = this.playerInterface_.mediaSourceEngine;
|
|
765
770
|
const forceTransmuxTS = this.config_.forceTransmuxTS;
|
|
766
771
|
|
|
767
|
-
|
|
772
|
+
|
|
773
|
+
await mediaSourceEngine.init(streamsByType, forceTransmuxTS,
|
|
774
|
+
this.manifest_.sequenceMode);
|
|
768
775
|
this.destroyer_.ensureNotDestroyed();
|
|
769
776
|
|
|
770
777
|
this.setDuration_();
|
|
@@ -805,6 +812,9 @@ shaka.media.StreamingEngine = class {
|
|
|
805
812
|
clearBufferSafeMargin: 0,
|
|
806
813
|
waitingToFlushBuffer: false,
|
|
807
814
|
clearingBuffer: false,
|
|
815
|
+
// The playhead might be seeking on startup, if a start time is set, so
|
|
816
|
+
// start "seeked" as true.
|
|
817
|
+
seeked: true,
|
|
808
818
|
recovering: false,
|
|
809
819
|
hasError: false,
|
|
810
820
|
operation: null,
|
|
@@ -1272,6 +1282,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1272
1282
|
this.scheduleUpdate_(mediaState, 0);
|
|
1273
1283
|
return;
|
|
1274
1284
|
}
|
|
1285
|
+
|
|
1275
1286
|
await this.append_(
|
|
1276
1287
|
mediaState, presentationTime, stream, reference, result);
|
|
1277
1288
|
}
|
|
@@ -1508,7 +1519,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1508
1519
|
|
|
1509
1520
|
await this.playerInterface_.mediaSourceEngine.setStreamProperties(
|
|
1510
1521
|
mediaState.type, timestampOffset, appendWindowStart,
|
|
1511
|
-
appendWindowEnd);
|
|
1522
|
+
appendWindowEnd, this.manifest_.sequenceMode);
|
|
1512
1523
|
} catch (error) {
|
|
1513
1524
|
mediaState.lastAppendWindowStart = null;
|
|
1514
1525
|
mediaState.lastAppendWindowEnd = null;
|
|
@@ -1565,8 +1576,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1565
1576
|
* @return {!Promise}
|
|
1566
1577
|
* @private
|
|
1567
1578
|
*/
|
|
1568
|
-
async append_(mediaState, presentationTime, stream, reference,
|
|
1569
|
-
segment) {
|
|
1579
|
+
async append_(mediaState, presentationTime, stream, reference, segment) {
|
|
1570
1580
|
const logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);
|
|
1571
1581
|
|
|
1572
1582
|
const hasClosedCaptions = stream.closedCaptions &&
|
|
@@ -1584,14 +1594,18 @@ shaka.media.StreamingEngine = class {
|
|
|
1584
1594
|
|
|
1585
1595
|
await this.evict_(mediaState, presentationTime);
|
|
1586
1596
|
this.destroyer_.ensureNotDestroyed();
|
|
1587
|
-
shaka.log.v1(logPrefix, 'appending media segment'
|
|
1597
|
+
shaka.log.v1(logPrefix, 'appending media segment at',
|
|
1598
|
+
(reference.syncTime == null ? 'unknown' : reference.syncTime));
|
|
1588
1599
|
|
|
1600
|
+
const seeked = mediaState.seeked;
|
|
1601
|
+
mediaState.seeked = false;
|
|
1589
1602
|
await this.playerInterface_.mediaSourceEngine.appendBuffer(
|
|
1590
1603
|
mediaState.type,
|
|
1591
1604
|
segment,
|
|
1592
|
-
reference.startTime,
|
|
1605
|
+
reference.syncTime == null ? reference.startTime : reference.syncTime,
|
|
1593
1606
|
reference.endTime,
|
|
1594
|
-
hasClosedCaptions
|
|
1607
|
+
hasClosedCaptions,
|
|
1608
|
+
seeked);
|
|
1595
1609
|
this.destroyer_.ensureNotDestroyed();
|
|
1596
1610
|
shaka.log.v2(logPrefix, 'appended media segment');
|
|
1597
1611
|
}
|
|
@@ -2017,6 +2031,7 @@ shaka.media.StreamingEngine.PlayerInterface;
|
|
|
2017
2031
|
* waitingToFlushBuffer: boolean,
|
|
2018
2032
|
* clearBufferSafeMargin: number,
|
|
2019
2033
|
* clearingBuffer: boolean,
|
|
2034
|
+
* seeked: boolean,
|
|
2020
2035
|
* recovering: boolean,
|
|
2021
2036
|
* hasError: boolean,
|
|
2022
2037
|
* operation: shaka.net.NetworkingEngine.PendingRequest
|
|
@@ -2061,6 +2076,8 @@ shaka.media.StreamingEngine.PlayerInterface;
|
|
|
2061
2076
|
* The amount of buffer to retain when clearing the buffer after the update.
|
|
2062
2077
|
* @property {boolean} clearingBuffer
|
|
2063
2078
|
* True indicates that the buffer is being cleared.
|
|
2079
|
+
* @property {boolean} seeked
|
|
2080
|
+
* True indicates that the presentation just seeked.
|
|
2064
2081
|
* @property {boolean} recovering
|
|
2065
2082
|
* True indicates that the last segment was not appended because it could not
|
|
2066
2083
|
* fit in the buffer.
|