shaka-player 4.3.7 → 4.3.9
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/ISSUE_TEMPLATE/bug_report.md +11 -0
- package/.github/workflows/appspot.yaml +39 -0
- package/.github/workflows/build-and-test.yaml +8 -0
- package/.github/workflows/selenium-lab-tests.yaml +3 -2
- package/.github/workflows/update-issues.yaml +1 -0
- package/CHANGELOG.md +44 -0
- package/CONTRIBUTORS +1 -0
- package/demo/custom.js +1 -1
- package/dist/demo.compiled.debug.js +2 -2
- package/dist/demo.compiled.debug.map +2 -2
- package/dist/demo.compiled.js +2 -2
- package/dist/demo.compiled.map +2 -2
- package/dist/deps.js +2 -2
- package/dist/locales.js +31 -31
- package/dist/shaka-player.compiled.d.ts +24 -2
- package/dist/shaka-player.compiled.debug.d.ts +24 -2
- package/dist/shaka-player.compiled.debug.js +251 -247
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.js +796 -793
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +24 -2
- package/dist/shaka-player.ui.debug.d.ts +24 -2
- package/dist/shaka-player.ui.debug.js +289 -286
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.js +831 -827
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/drm-config.md +9 -6
- package/docs/tutorials/selenium-grid-config.md +60 -8
- package/externs/ima.js +34 -0
- package/externs/shaka/manifest.js +5 -1
- package/externs/shaka/offline.js +5 -1
- package/karma.conf.js +50 -7
- package/lib/abr/simple_abr_manager.js +6 -6
- package/lib/cea/mp4_cea_parser.js +24 -18
- package/lib/dash/dash_parser.js +19 -3
- package/lib/hls/hls_parser.js +24 -4
- package/lib/media/drm_engine.js +14 -8
- package/lib/media/gap_jumping_controller.js +24 -2
- package/lib/media/media_source_engine.js +11 -8
- package/lib/media/playhead.js +11 -0
- package/lib/media/region_observer.js +19 -1
- package/lib/media/streaming_engine.js +41 -21
- package/lib/media/video_wrapper.js +6 -1
- 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 +1 -0
- package/lib/player.js +20 -9
- package/lib/polyfill/abort_controller.js +7 -0
- package/lib/polyfill/orientation.js +34 -7
- package/lib/text/vtt_text_parser.js +3 -1
- package/lib/util/manifest_parser_utils.js +5 -3
- package/lib/util/periods.js +2 -0
- package/lib/util/platform.js +14 -0
- package/lib/util/stream_utils.js +70 -26
- package/package.json +2 -2
- package/test/cast/cast_utils_unit.js +4 -1
- package/test/cea/mp4_cea_parser_unit.js +28 -5
- package/test/dash/dash_parser_manifest_unit.js +120 -0
- package/test/hls/hls_parser_unit.js +216 -0
- package/test/media/adaptation_set_unit.js +1 -0
- package/test/media/drm_engine_integration.js +2 -2
- package/test/media/media_source_engine_unit.js +1 -1
- package/test/media/region_observer_unit.js +2 -1
- package/test/media/segment_index_unit.js +3 -3
- package/test/media/streaming_engine_integration.js +14 -11
- package/test/media/streaming_engine_unit.js +26 -2
- package/test/offline/manifest_convert_unit.js +5 -0
- package/test/offline/offline_integration.js +6 -3
- package/test/player_external.js +19 -16
- package/test/player_integration.js +35 -48
- package/test/player_src_equals_integration.js +9 -5
- package/test/test/assets/multiple-trun-init.mp4 +0 -0
- package/test/test/assets/multiple-trun-segment.mp4 +0 -0
- package/test/test/util/fake_demo_main.js +7 -3
- package/test/test/util/layout_tests.js +2 -2
- package/test/test/util/manifest_generator.js +2 -0
- package/test/test/util/offline_utils.js +1 -0
- package/test/test/util/streaming_engine_util.js +3 -0
- package/test/test/util/ui_utils.js +9 -2
- package/test/test/util/util.js +6 -27
- package/test/test/util/waiter.js +73 -3
- package/test/ui/ad_ui_unit.js +9 -9
- package/test/util/stream_utils_unit.js +46 -0
- package/ui/range_element.js +5 -0
- package/ui/resolution_selection.js +7 -1
|
@@ -58,6 +58,9 @@ shaka.media.GapJumpingController = class {
|
|
|
58
58
|
/** @private {number} */
|
|
59
59
|
this.prevReadyState_ = video.readyState;
|
|
60
60
|
|
|
61
|
+
/** @private {number} */
|
|
62
|
+
this.startTime_ = 0;
|
|
63
|
+
|
|
61
64
|
/** @private {number} */
|
|
62
65
|
this.gapsJumped_ = 0;
|
|
63
66
|
|
|
@@ -123,6 +126,18 @@ shaka.media.GapJumpingController = class {
|
|
|
123
126
|
this.onPollGapJump_();
|
|
124
127
|
}
|
|
125
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Called when playback has started and the video element is
|
|
131
|
+
* listening for seeks.
|
|
132
|
+
*
|
|
133
|
+
* @param {number} startTime
|
|
134
|
+
*/
|
|
135
|
+
onStarted(startTime) {
|
|
136
|
+
if (this.video_.seeking && !this.seekingEventReceived_) {
|
|
137
|
+
this.seekingEventReceived_ = true;
|
|
138
|
+
this.startTime_ = startTime;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
126
141
|
|
|
127
142
|
/** Called when a seek has started. */
|
|
128
143
|
onSeeking() {
|
|
@@ -164,8 +179,8 @@ shaka.media.GapJumpingController = class {
|
|
|
164
179
|
// while paused on a livestream. We make an exception for time 0, since we
|
|
165
180
|
// may be _required_ to seek on startup before play can begin, but only if
|
|
166
181
|
// autoplay is enabled.
|
|
167
|
-
if (this.video_.paused && (this.video_.currentTime !=
|
|
168
|
-
(!this.video_.autoplay && this.video_.currentTime ==
|
|
182
|
+
if (this.video_.paused && (this.video_.currentTime != this.startTime_ ||
|
|
183
|
+
(!this.video_.autoplay && this.video_.currentTime == this.startTime_))) {
|
|
169
184
|
return;
|
|
170
185
|
}
|
|
171
186
|
|
|
@@ -228,6 +243,13 @@ shaka.media.GapJumpingController = class {
|
|
|
228
243
|
}
|
|
229
244
|
|
|
230
245
|
this.video_.currentTime = jumpTo;
|
|
246
|
+
// This accounts for the possibility that we jump a gap at the start
|
|
247
|
+
// position but we jump _into_ another gap. By setting the start
|
|
248
|
+
// position to the new jumpTo we ensure that the check above will
|
|
249
|
+
// pass even though the video is still paused.
|
|
250
|
+
if (currentTime == this.startTime_) {
|
|
251
|
+
this.startTime_ = jumpTo;
|
|
252
|
+
}
|
|
231
253
|
this.gapsJumped_++;
|
|
232
254
|
this.onEvent_(
|
|
233
255
|
new shaka.util.FakeEvent(shaka.util.FakeEvent.EventName.GapJumped));
|
|
@@ -369,7 +369,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
369
369
|
let mimeType = shaka.util.MimeUtils.getFullType(
|
|
370
370
|
stream.mimeType, stream.codecs);
|
|
371
371
|
if (contentType == ContentType.TEXT) {
|
|
372
|
-
this.reinitText(mimeType, sequenceMode);
|
|
372
|
+
this.reinitText(mimeType, sequenceMode, stream.external);
|
|
373
373
|
} else {
|
|
374
374
|
if ((forceTransmux ||
|
|
375
375
|
!shaka.media.Capabilities.isTypeSupported(mimeType)) &&
|
|
@@ -425,13 +425,14 @@ shaka.media.MediaSourceEngine = class {
|
|
|
425
425
|
* Reinitialize the TextEngine for a new text type.
|
|
426
426
|
* @param {string} mimeType
|
|
427
427
|
* @param {boolean} sequenceMode
|
|
428
|
+
* @param {boolean} external
|
|
428
429
|
*/
|
|
429
|
-
reinitText(mimeType, sequenceMode) {
|
|
430
|
+
reinitText(mimeType, sequenceMode, external) {
|
|
430
431
|
if (!this.textEngine_) {
|
|
431
432
|
this.textEngine_ = new shaka.text.TextEngine(this.textDisplayer_);
|
|
432
433
|
}
|
|
433
434
|
this.textEngine_.initParser(mimeType, sequenceMode,
|
|
434
|
-
this.segmentRelativeVttTiming_);
|
|
435
|
+
external || this.segmentRelativeVttTiming_);
|
|
435
436
|
}
|
|
436
437
|
|
|
437
438
|
/**
|
|
@@ -582,11 +583,13 @@ shaka.media.MediaSourceEngine = class {
|
|
|
582
583
|
* @param {boolean=} seeked True if we just seeked
|
|
583
584
|
* @param {boolean=} adaptation True if we just automatically switched active
|
|
584
585
|
* variant(s).
|
|
586
|
+
* @param {boolean=} isChunkedData True if we add to the buffer from the
|
|
587
|
+
* partial read of the segment.
|
|
585
588
|
* @return {!Promise}
|
|
586
589
|
*/
|
|
587
590
|
async appendBuffer(
|
|
588
591
|
contentType, data, reference, hasClosedCaptions, seeked = false,
|
|
589
|
-
adaptation = false) {
|
|
592
|
+
adaptation = false, isChunkedData = false) {
|
|
590
593
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
591
594
|
|
|
592
595
|
if (contentType == ContentType.TEXT) {
|
|
@@ -649,7 +652,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
649
652
|
// the video stream, so textEngine may not have been initialized.
|
|
650
653
|
if (!this.textEngine_) {
|
|
651
654
|
this.reinitText(shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE,
|
|
652
|
-
this.sequenceMode_);
|
|
655
|
+
this.sequenceMode_, /* external= */ false);
|
|
653
656
|
}
|
|
654
657
|
// This doesn't work for native TS support (ex. Edge/Chromecast),
|
|
655
658
|
// since no transmuxing is needed for native TS.
|
|
@@ -669,7 +672,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
669
672
|
} else if (hasClosedCaptions && contentType == ContentType.VIDEO) {
|
|
670
673
|
if (!this.textEngine_) {
|
|
671
674
|
this.reinitText(shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE,
|
|
672
|
-
this.sequenceMode_);
|
|
675
|
+
this.sequenceMode_, /* external= */ false);
|
|
673
676
|
}
|
|
674
677
|
if (!this.captionParser_) {
|
|
675
678
|
this.captionParser_ = this.getCaptionParser(mimeType);
|
|
@@ -787,13 +790,13 @@ shaka.media.MediaSourceEngine = class {
|
|
|
787
790
|
let bufferedBefore = null;
|
|
788
791
|
|
|
789
792
|
await this.enqueueOperation_(contentType, () => {
|
|
790
|
-
if (goog.DEBUG && reference) {
|
|
793
|
+
if (goog.DEBUG && reference && !reference.isPreload() && !isChunkedData) {
|
|
791
794
|
bufferedBefore = this.getBuffered_(contentType);
|
|
792
795
|
}
|
|
793
796
|
this.append_(contentType, data);
|
|
794
797
|
});
|
|
795
798
|
|
|
796
|
-
if (goog.DEBUG && reference && !reference.isPreload()) {
|
|
799
|
+
if (goog.DEBUG && reference && !reference.isPreload() && !isChunkedData) {
|
|
797
800
|
const bufferedAfter = this.getBuffered_(contentType);
|
|
798
801
|
const newBuffered = shaka.media.TimeRangesUtils.computeAddedRange(
|
|
799
802
|
bufferedBefore, bufferedAfter);
|
package/lib/media/playhead.js
CHANGED
|
@@ -251,6 +251,7 @@ shaka.media.MediaSourcePlayhead = class {
|
|
|
251
251
|
this.videoWrapper_ = new shaka.media.VideoWrapper(
|
|
252
252
|
mediaElement,
|
|
253
253
|
() => this.onSeeking_(),
|
|
254
|
+
(realStartTime) => this.onStarted_(realStartTime),
|
|
254
255
|
this.getStartTime_(startTime));
|
|
255
256
|
|
|
256
257
|
/** @type {shaka.util.Timer} */
|
|
@@ -390,6 +391,16 @@ shaka.media.MediaSourcePlayhead = class {
|
|
|
390
391
|
}
|
|
391
392
|
}
|
|
392
393
|
|
|
394
|
+
/**
|
|
395
|
+
* Called when the video element has started up and is listening for new seeks
|
|
396
|
+
*
|
|
397
|
+
* @param {number} startTime
|
|
398
|
+
* @private
|
|
399
|
+
*/
|
|
400
|
+
onStarted_(startTime) {
|
|
401
|
+
this.gapController_.onStarted(startTime);
|
|
402
|
+
}
|
|
403
|
+
|
|
393
404
|
/**
|
|
394
405
|
* Handles when a seek happens on the video.
|
|
395
406
|
*
|
|
@@ -27,13 +27,24 @@ shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
|
|
|
27
27
|
* destroy the timeline.
|
|
28
28
|
*
|
|
29
29
|
* @param {!shaka.media.RegionTimeline} timeline
|
|
30
|
+
* @param {boolean} startsPastZero
|
|
30
31
|
*/
|
|
31
|
-
constructor(timeline) {
|
|
32
|
+
constructor(timeline, startsPastZero) {
|
|
32
33
|
super();
|
|
33
34
|
|
|
34
35
|
/** @private {shaka.media.RegionTimeline} */
|
|
35
36
|
this.timeline_ = timeline;
|
|
36
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Whether the asset is expected to start at a time beyond 0 seconds.
|
|
40
|
+
* For example, if the asset is a live stream.
|
|
41
|
+
* If true, we will not start polling for regions until the playhead has
|
|
42
|
+
* moved past 0 seconds, to avoid bad behaviors where the current time is
|
|
43
|
+
* briefly 0 before we have enough data to play.
|
|
44
|
+
* @private {boolean}
|
|
45
|
+
*/
|
|
46
|
+
this.startsPastZero_ = startsPastZero;
|
|
47
|
+
|
|
37
48
|
/**
|
|
38
49
|
* A mapping between a region and where we previously were relative to it.
|
|
39
50
|
* When the value here differs from what we calculate, it means we moved and
|
|
@@ -121,6 +132,13 @@ shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
|
|
|
121
132
|
/** @override */
|
|
122
133
|
poll(positionInSeconds, wasSeeking) {
|
|
123
134
|
const RegionObserver = shaka.media.RegionObserver;
|
|
135
|
+
if (this.startsPastZero_ && positionInSeconds == 0) {
|
|
136
|
+
// Don't start checking regions until the timeline has begun moving.
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
// Now that we have seen the playhead go past 0, it's okay if it goes
|
|
140
|
+
// back there (e.g. seeking back to the start).
|
|
141
|
+
this.startsPastZero_ = false;
|
|
124
142
|
|
|
125
143
|
for (const region of this.timeline_.regions()) {
|
|
126
144
|
const previousPosition = this.oldPosition_.get(region);
|
|
@@ -244,7 +244,7 @@ shaka.media.StreamingEngine = class {
|
|
|
244
244
|
const mimeType = shaka.util.MimeUtils.getFullType(
|
|
245
245
|
stream.mimeType, stream.codecs);
|
|
246
246
|
this.playerInterface_.mediaSourceEngine.reinitText(
|
|
247
|
-
mimeType, this.manifest_.sequenceMode);
|
|
247
|
+
mimeType, this.manifest_.sequenceMode, stream.external);
|
|
248
248
|
|
|
249
249
|
const textDisplayer =
|
|
250
250
|
this.playerInterface_.mediaSourceEngine.getTextDisplayer();
|
|
@@ -458,7 +458,7 @@ shaka.media.StreamingEngine = class {
|
|
|
458
458
|
const fullMimeType = shaka.util.MimeUtils.getFullType(
|
|
459
459
|
stream.mimeType, stream.codecs);
|
|
460
460
|
this.playerInterface_.mediaSourceEngine.reinitText(
|
|
461
|
-
fullMimeType, this.manifest_.sequenceMode);
|
|
461
|
+
fullMimeType, this.manifest_.sequenceMode, stream.external);
|
|
462
462
|
}
|
|
463
463
|
|
|
464
464
|
// Releases the segmentIndex of the old stream.
|
|
@@ -1319,7 +1319,8 @@ shaka.media.StreamingEngine = class {
|
|
|
1319
1319
|
const dataToAppend = remaining.subarray(0, offset);
|
|
1320
1320
|
remaining = remaining.subarray(offset);
|
|
1321
1321
|
await this.append_(
|
|
1322
|
-
mediaState, presentationTime, stream, reference, dataToAppend
|
|
1322
|
+
mediaState, presentationTime, stream, reference, dataToAppend,
|
|
1323
|
+
/* isChunkedData= */ true);
|
|
1323
1324
|
}
|
|
1324
1325
|
};
|
|
1325
1326
|
|
|
@@ -1731,10 +1732,12 @@ shaka.media.StreamingEngine = class {
|
|
|
1731
1732
|
* @param {shaka.extern.Stream} stream
|
|
1732
1733
|
* @param {!shaka.media.SegmentReference} reference
|
|
1733
1734
|
* @param {BufferSource} segment
|
|
1735
|
+
* @param {boolean=} isChunkedData
|
|
1734
1736
|
* @return {!Promise}
|
|
1735
1737
|
* @private
|
|
1736
1738
|
*/
|
|
1737
|
-
async append_(mediaState, presentationTime, stream, reference, segment
|
|
1739
|
+
async append_(mediaState, presentationTime, stream, reference, segment,
|
|
1740
|
+
isChunkedData = false) {
|
|
1738
1741
|
const logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);
|
|
1739
1742
|
|
|
1740
1743
|
const hasClosedCaptions = stream.closedCaptions &&
|
|
@@ -1788,7 +1791,8 @@ shaka.media.StreamingEngine = class {
|
|
|
1788
1791
|
reference,
|
|
1789
1792
|
hasClosedCaptions,
|
|
1790
1793
|
seeked,
|
|
1791
|
-
adaptation
|
|
1794
|
+
adaptation,
|
|
1795
|
+
isChunkedData);
|
|
1792
1796
|
this.destroyer_.ensureNotDestroyed();
|
|
1793
1797
|
shaka.log.v2(logPrefix, 'appended media segment');
|
|
1794
1798
|
}
|
|
@@ -1852,7 +1856,6 @@ shaka.media.StreamingEngine = class {
|
|
|
1852
1856
|
const messageData = box.reader.readBytes(
|
|
1853
1857
|
box.reader.getLength() - box.reader.getPosition());
|
|
1854
1858
|
|
|
1855
|
-
|
|
1856
1859
|
// See DASH sec. 5.10.3.3.1
|
|
1857
1860
|
// If a DASH client detects an event message box with a scheme that is not
|
|
1858
1861
|
// defined in MPD, the client is expected to ignore it.
|
|
@@ -1862,22 +1865,8 @@ shaka.media.StreamingEngine = class {
|
|
|
1862
1865
|
// A special scheme in DASH used to signal manifest updates.
|
|
1863
1866
|
if (schemeId == 'urn:mpeg:dash:event:2012') {
|
|
1864
1867
|
this.playerInterface_.onManifestUpdate();
|
|
1865
|
-
} else if (schemeId == 'https://aomedia.org/emsg/ID3') {
|
|
1866
|
-
// See https://aomediacodec.github.io/id3-emsg/
|
|
1867
|
-
const frames = shaka.util.Id3Utils.getID3Frames(messageData);
|
|
1868
|
-
if (frames.length && reference) {
|
|
1869
|
-
/** @private {shaka.extern.ID3Metadata} */
|
|
1870
|
-
const metadata = {
|
|
1871
|
-
cueTime: reference.startTime,
|
|
1872
|
-
data: messageData,
|
|
1873
|
-
frames: frames,
|
|
1874
|
-
dts: reference.startTime,
|
|
1875
|
-
pts: reference.startTime,
|
|
1876
|
-
};
|
|
1877
|
-
this.playerInterface_.onMetadata(
|
|
1878
|
-
[metadata], /* offset= */ 0, reference.endTime);
|
|
1879
|
-
}
|
|
1880
1868
|
} else {
|
|
1869
|
+
// All other schemes are dispatched as a general 'emsg' event.
|
|
1881
1870
|
/** @type {shaka.extern.EmsgInfo} */
|
|
1882
1871
|
const emsg = {
|
|
1883
1872
|
startTime: startTime,
|
|
@@ -1895,7 +1884,38 @@ shaka.media.StreamingEngine = class {
|
|
|
1895
1884
|
const eventName = shaka.util.FakeEvent.EventName.Emsg;
|
|
1896
1885
|
const data = (new Map()).set('detail', emsg);
|
|
1897
1886
|
const event = new shaka.util.FakeEvent(eventName, data);
|
|
1887
|
+
// A user can call preventDefault() on a cancelable event.
|
|
1888
|
+
event.cancelable = true;
|
|
1889
|
+
|
|
1898
1890
|
this.playerInterface_.onEvent(event);
|
|
1891
|
+
|
|
1892
|
+
if (event.defaultPrevented) {
|
|
1893
|
+
// If the caller uses preventDefault() on the 'emsg' event, don't
|
|
1894
|
+
// process any further, and don't generate an ID3 'metadata' event
|
|
1895
|
+
// for the same data.
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
// Additionally, ID3 events generate a 'metadata' event. This is a
|
|
1900
|
+
// pre-parsed version of the metadata blob already dispatched in the
|
|
1901
|
+
// 'emsg' event.
|
|
1902
|
+
if (schemeId == 'https://aomedia.org/emsg/ID3' ||
|
|
1903
|
+
schemeId == 'https://developer.apple.com/streaming/emsg-id3') {
|
|
1904
|
+
// See https://aomediacodec.github.io/id3-emsg/
|
|
1905
|
+
const frames = shaka.util.Id3Utils.getID3Frames(messageData);
|
|
1906
|
+
if (frames.length && reference) {
|
|
1907
|
+
/** @private {shaka.extern.ID3Metadata} */
|
|
1908
|
+
const metadata = {
|
|
1909
|
+
cueTime: reference.startTime,
|
|
1910
|
+
data: messageData,
|
|
1911
|
+
frames: frames,
|
|
1912
|
+
dts: reference.startTime,
|
|
1913
|
+
pts: reference.startTime,
|
|
1914
|
+
};
|
|
1915
|
+
this.playerInterface_.onMetadata(
|
|
1916
|
+
[metadata], /* offset= */ 0, reference.endTime);
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1899
1919
|
}
|
|
1900
1920
|
}
|
|
1901
1921
|
}
|
|
@@ -27,15 +27,19 @@ shaka.media.VideoWrapper = class {
|
|
|
27
27
|
/**
|
|
28
28
|
* @param {!HTMLMediaElement} video
|
|
29
29
|
* @param {function()} onSeek Called when the video seeks.
|
|
30
|
+
* @param {function(number)} onStarted Called when the video has started.
|
|
30
31
|
* @param {number} startTime The time to start at.
|
|
31
32
|
*/
|
|
32
|
-
constructor(video, onSeek, startTime) {
|
|
33
|
+
constructor(video, onSeek, onStarted, startTime) {
|
|
33
34
|
/** @private {HTMLMediaElement} */
|
|
34
35
|
this.video_ = video;
|
|
35
36
|
|
|
36
37
|
/** @private {function()} */
|
|
37
38
|
this.onSeek_ = onSeek;
|
|
38
39
|
|
|
40
|
+
/** @private {function(number)} */
|
|
41
|
+
this.onStarted_ = onStarted;
|
|
42
|
+
|
|
39
43
|
/** @private {number} */
|
|
40
44
|
this.startTime_ = startTime;
|
|
41
45
|
|
|
@@ -165,6 +169,7 @@ shaka.media.VideoWrapper = class {
|
|
|
165
169
|
this.started_ = true;
|
|
166
170
|
|
|
167
171
|
this.eventManager_.listen(this.video_, 'seeking', () => this.onSeek_());
|
|
172
|
+
this.onStarted_(this.video_.currentTime);
|
|
168
173
|
}
|
|
169
174
|
};
|
|
170
175
|
|
package/lib/offline/storage.js
CHANGED
package/lib/player.js
CHANGED
|
@@ -99,7 +99,9 @@ goog.requireType('shaka.routing.Payload');
|
|
|
99
99
|
|
|
100
100
|
/**
|
|
101
101
|
* @event shaka.Player.EmsgEvent
|
|
102
|
-
* @description Fired when
|
|
102
|
+
* @description Fired when an emsg box is found in a segment.
|
|
103
|
+
* If the application calls preventDefault() on this event, further parsing
|
|
104
|
+
* will not happen, and no 'metadata' event will be raised for ID3 payloads.
|
|
103
105
|
* @property {string} type
|
|
104
106
|
* 'emsg'
|
|
105
107
|
* @property {shaka.extern.EmsgInfo} detail
|
|
@@ -2190,8 +2192,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2190
2192
|
await Promise.all(createSegmentIndexPromises);
|
|
2191
2193
|
}
|
|
2192
2194
|
|
|
2195
|
+
shaka.Player.applyPlayRange_(this.manifest_.presentationTimeline,
|
|
2196
|
+
this.config_.playRangeStart,
|
|
2197
|
+
this.config_.playRangeEnd);
|
|
2198
|
+
|
|
2193
2199
|
this.playhead_ = this.createPlayhead(has.startTime);
|
|
2194
|
-
this.playheadObservers_ =
|
|
2200
|
+
this.playheadObservers_ =
|
|
2201
|
+
this.createPlayheadObserversForMSE_(has.startTimeOfLoad);
|
|
2195
2202
|
|
|
2196
2203
|
// We need to start the buffer management code near the end because it will
|
|
2197
2204
|
// set the initial buffering state and that depends on other components
|
|
@@ -2217,10 +2224,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2217
2224
|
|
|
2218
2225
|
this.playhead_.setStartTime(adjustedTime);
|
|
2219
2226
|
}
|
|
2220
|
-
|
|
2221
|
-
// Since the first streams just became active, send an adaptation event.
|
|
2222
|
-
this.onAdaptation_(null,
|
|
2223
|
-
shaka.util.StreamUtils.variantToTrack(initialVariant));
|
|
2224
2227
|
}
|
|
2225
2228
|
|
|
2226
2229
|
this.playhead_.ready();
|
|
@@ -2387,6 +2390,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2387
2390
|
audioSamplingRate: null,
|
|
2388
2391
|
spatialAudio: false,
|
|
2389
2392
|
closedCaptions: null,
|
|
2393
|
+
external: false,
|
|
2390
2394
|
},
|
|
2391
2395
|
bandwidth: 100,
|
|
2392
2396
|
allowedByApplication: true,
|
|
@@ -2918,17 +2922,21 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2918
2922
|
* Create the observers for MSE playback. These observers are responsible for
|
|
2919
2923
|
* notifying the app and player of specific events during MSE playback.
|
|
2920
2924
|
*
|
|
2925
|
+
* @param {number} startTimeOfLoad
|
|
2921
2926
|
* @return {!shaka.media.PlayheadObserverManager}
|
|
2922
2927
|
* @private
|
|
2923
2928
|
*/
|
|
2924
|
-
createPlayheadObserversForMSE_() {
|
|
2929
|
+
createPlayheadObserversForMSE_(startTimeOfLoad) {
|
|
2925
2930
|
goog.asserts.assert(this.manifest_, 'Must have manifest');
|
|
2926
2931
|
goog.asserts.assert(this.regionTimeline_, 'Must have region timeline');
|
|
2927
2932
|
goog.asserts.assert(this.video_, 'Must have video element');
|
|
2928
2933
|
|
|
2934
|
+
const startsPastZero = this.isLive() || startTimeOfLoad > 0;
|
|
2935
|
+
|
|
2929
2936
|
// Create the region observer. This will allow us to notify the app when we
|
|
2930
2937
|
// move in and out of timeline regions.
|
|
2931
|
-
const regionObserver = new shaka.media.RegionObserver(
|
|
2938
|
+
const regionObserver = new shaka.media.RegionObserver(
|
|
2939
|
+
this.regionTimeline_, startsPastZero);
|
|
2932
2940
|
|
|
2933
2941
|
regionObserver.addEventListener('enter', (event) => {
|
|
2934
2942
|
/** @type {shaka.extern.TimelineRegionInfo} */
|
|
@@ -4816,6 +4824,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4816
4824
|
audioSamplingRate: null,
|
|
4817
4825
|
spatialAudio: false,
|
|
4818
4826
|
closedCaptions: null,
|
|
4827
|
+
external: true,
|
|
4819
4828
|
};
|
|
4820
4829
|
|
|
4821
4830
|
const fullMimeType = shaka.util.MimeUtils.getFullType(
|
|
@@ -4967,6 +4976,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4967
4976
|
spatialAudio: false,
|
|
4968
4977
|
closedCaptions: null,
|
|
4969
4978
|
tilesLayout: '1x1',
|
|
4979
|
+
external: true,
|
|
4970
4980
|
};
|
|
4971
4981
|
|
|
4972
4982
|
this.manifest_.imageStreams.push(stream);
|
|
@@ -5386,6 +5396,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5386
5396
|
audioSamplingRate: null,
|
|
5387
5397
|
spatialAudio: false,
|
|
5388
5398
|
closedCaptions: null,
|
|
5399
|
+
external: false,
|
|
5389
5400
|
};
|
|
5390
5401
|
manifest.textStreams.push(textStream);
|
|
5391
5402
|
closedCaptionsSet.add(id);
|
|
@@ -7009,7 +7020,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
7009
7020
|
* @define {string} A version number taken from git at compile time.
|
|
7010
7021
|
* @export
|
|
7011
7022
|
*/
|
|
7012
|
-
shaka.Player.version = 'v4.3.
|
|
7023
|
+
shaka.Player.version = 'v4.3.9-uncompiled';
|
|
7013
7024
|
|
|
7014
7025
|
// Initialize the deprecation system using the version string we just set
|
|
7015
7026
|
// on the player.
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
goog.provide('shaka.polyfill.Orientation');
|
|
8
8
|
|
|
9
|
+
goog.require('shaka.log');
|
|
9
10
|
goog.require('shaka.util.FakeEvent');
|
|
10
11
|
goog.require('shaka.util.FakeEventTarget');
|
|
11
12
|
goog.require('shaka.polyfill');
|
|
@@ -23,21 +24,47 @@ shaka.polyfill.Orientation = class {
|
|
|
23
24
|
* @export
|
|
24
25
|
*/
|
|
25
26
|
static install() {
|
|
26
|
-
if (screen.orientation) {
|
|
27
|
+
if (screen.orientation && screen.orientation.unlock) {
|
|
27
28
|
// Not needed.
|
|
29
|
+
shaka.log.info('Using native screen.orientation');
|
|
28
30
|
return;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (window.orientation != undefined) {
|
|
33
|
+
if (screen.orientation != undefined) {
|
|
34
|
+
// There are some platforms where screen.orientation is defined but
|
|
35
|
+
// incomplete (e.g. Safari).
|
|
36
|
+
// Install a very simple polyfill in that case.
|
|
37
|
+
shaka.polyfill.Orientation.installBasedOnScreenMethods_();
|
|
38
|
+
} else if (window.orientation != undefined) {
|
|
39
|
+
// There is no way to check to see if the 'orientationchange' event exists
|
|
40
|
+
// on window, which could theoretically lead to this making a
|
|
41
|
+
// screen.orientation object that doesn't actually work.
|
|
42
|
+
// However, it looks like all platforms that support the deprecated
|
|
43
|
+
// window.orientation feature also support that event.
|
|
37
44
|
shaka.polyfill.Orientation.installBasedOnWindowMethods_();
|
|
38
45
|
}
|
|
39
46
|
}
|
|
40
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Modifies screen.orientation to add no-ops for missing methods.
|
|
50
|
+
* Meant for cases where screen.orientation is defined, but has missing
|
|
51
|
+
* methods that cannot be properly polyfilled.
|
|
52
|
+
* @private
|
|
53
|
+
*/
|
|
54
|
+
static installBasedOnScreenMethods_() {
|
|
55
|
+
if (screen.orientation.lock === undefined) {
|
|
56
|
+
screen.orientation.lock = (orientation) => {
|
|
57
|
+
shaka.log.info('screen.orientation.lock is a no-op');
|
|
58
|
+
return Promise.resolve();
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (screen.orientation.unlock === undefined) {
|
|
62
|
+
screen.orientation.unlock = () => {
|
|
63
|
+
shaka.log.info('screen.orientation.unlock is a no-op');
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
41
68
|
/**
|
|
42
69
|
* Makes a polyfill for orientation, based on window methods.
|
|
43
70
|
* Note that some of the features this is based on are deprecated, so this
|
|
@@ -674,6 +674,7 @@ shaka.text.VttTextParser = class {
|
|
|
674
674
|
cue.fontWeight = refCue.fontWeight;
|
|
675
675
|
cue.fontStyle = refCue.fontStyle;
|
|
676
676
|
cue.opacity = refCue.opacity;
|
|
677
|
+
cue.textShadow = refCue.textShadow;
|
|
677
678
|
cue.wrapLine = refCue.wrapLine;
|
|
678
679
|
}
|
|
679
680
|
|
|
@@ -785,7 +786,8 @@ shaka.text.VttTextParser = class {
|
|
|
785
786
|
} else if ((results = /^size:([\d.]+)%$/.exec(word))) {
|
|
786
787
|
cue.size = Number(results[1]);
|
|
787
788
|
} else if ((results =
|
|
788
|
-
|
|
789
|
+
// eslint-disable-next-line max-len
|
|
790
|
+
/^position:([\d.]+)%(?:,(line-left|line-right|middle|center|start|end))?$/
|
|
789
791
|
.exec(word))) {
|
|
790
792
|
cue.position = Number(results[1]);
|
|
791
793
|
if (results[2]) {
|
|
@@ -168,7 +168,7 @@ shaka.util.ManifestParserUtils.VIDEO_CODEC_REGEXPS_ = [
|
|
|
168
168
|
/^hvc/,
|
|
169
169
|
/^vp0?[89]/,
|
|
170
170
|
/^av01/,
|
|
171
|
-
/^
|
|
171
|
+
/^dvh/,
|
|
172
172
|
];
|
|
173
173
|
|
|
174
174
|
|
|
@@ -180,8 +180,10 @@ shaka.util.ManifestParserUtils.VIDEO_CODEC_REGEXPS_ = [
|
|
|
180
180
|
*/
|
|
181
181
|
shaka.util.ManifestParserUtils.AUDIO_CODEC_REGEXPS_ = [
|
|
182
182
|
/^vorbis$/,
|
|
183
|
-
/^
|
|
184
|
-
/^
|
|
183
|
+
/^Opus$/, // correct codec string according to RFC 6381 section 3.3
|
|
184
|
+
/^opus$/, // some manifests wrongfully use this
|
|
185
|
+
/^fLaC$/, // correct codec string according to RFC 6381 section 3.3
|
|
186
|
+
/^flac$/, // some manifests wrongfully use this
|
|
185
187
|
/^mp4a/,
|
|
186
188
|
/^[ae]c-3$/,
|
|
187
189
|
/^ac-4$/,
|
package/lib/util/periods.js
CHANGED
|
@@ -1491,6 +1491,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
1491
1491
|
audioSamplingRate: null,
|
|
1492
1492
|
spatialAudio: false,
|
|
1493
1493
|
closedCaptions: null,
|
|
1494
|
+
external: false,
|
|
1494
1495
|
};
|
|
1495
1496
|
}
|
|
1496
1497
|
|
|
@@ -1527,6 +1528,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
1527
1528
|
audioSamplingRate: null,
|
|
1528
1529
|
spatialAudio: false,
|
|
1529
1530
|
closedCaptions: null,
|
|
1531
|
+
external: false,
|
|
1530
1532
|
};
|
|
1531
1533
|
}
|
|
1532
1534
|
|
package/lib/util/platform.js
CHANGED
|
@@ -346,6 +346,20 @@ shaka.util.Platform = class {
|
|
|
346
346
|
return shaka.util.Platform.isApple() && navigator.maxTouchPoints > 1;
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
+
/**
|
|
350
|
+
* Return true if the platform is a Mac, regardless of the browser.
|
|
351
|
+
*
|
|
352
|
+
* @return {boolean}
|
|
353
|
+
*/
|
|
354
|
+
static isMac() {
|
|
355
|
+
// Try the newer standard first.
|
|
356
|
+
if (navigator.userAgentData && navigator.userAgentData.platform) {
|
|
357
|
+
return navigator.userAgentData.platform.toLowerCase() == 'macos';
|
|
358
|
+
}
|
|
359
|
+
// Fall back to the old API, with less strict matching.
|
|
360
|
+
return navigator.platform.toLowerCase().includes('mac');
|
|
361
|
+
}
|
|
362
|
+
|
|
349
363
|
/**
|
|
350
364
|
* Check if the user agent contains a key. This is the best way we know of
|
|
351
365
|
* right now to detect platforms. If there is a better way, please send a
|