shaka-player 4.3.6 → 4.3.8
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/workflows/appspot.yaml +39 -0
- package/.github/workflows/demo-version-index.yaml +3 -3
- package/CHANGELOG.md +46 -0
- package/CONTRIBUTORS +1 -0
- package/README.md +3 -0
- package/demo/custom.js +1 -1
- package/demo/demo.less +1 -0
- 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/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/shaka-player.compiled.d.ts +30 -0
- package/dist/shaka-player.compiled.debug.d.ts +30 -0
- package/dist/shaka-player.compiled.debug.externs.js +9 -0
- package/dist/shaka-player.compiled.debug.js +263 -256
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +9 -0
- package/dist/shaka-player.compiled.js +901 -895
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +30 -0
- package/dist/shaka-player.ui.debug.d.ts +30 -0
- package/dist/shaka-player.ui.debug.externs.js +9 -0
- package/dist/shaka-player.ui.debug.js +268 -262
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +9 -0
- package/dist/shaka-player.ui.js +926 -920
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/drm-config.md +9 -6
- package/docs/tutorials/welcome.md +1 -1
- package/externs/ima.js +34 -0
- package/lib/abr/simple_abr_manager.js +1 -1
- package/lib/cea/mp4_cea_parser.js +29 -20
- package/lib/dash/dash_parser.js +18 -3
- package/lib/hls/hls_parser.js +66 -6
- package/lib/hls/manifest_text_parser.js +10 -1
- package/lib/media/drm_engine.js +23 -14
- package/lib/media/gap_jumping_controller.js +24 -2
- package/lib/media/media_source_engine.js +10 -6
- package/lib/media/playhead.js +11 -0
- package/lib/media/region_observer.js +19 -1
- package/lib/media/segment_reference.js +22 -0
- package/lib/media/streaming_engine.js +48 -23
- package/lib/media/video_wrapper.js +6 -1
- package/lib/player.js +16 -5
- package/lib/polyfill/abort_controller.js +7 -0
- package/lib/text/vtt_text_parser.js +2 -1
- package/lib/util/manifest_parser_utils.js +1 -1
- package/lib/util/stream_utils.js +1 -0
- package/package.json +9 -4
- package/test/cea/mp4_cea_parser_unit.js +45 -3
- package/test/dash/dash_parser_manifest_unit.js +120 -0
- package/test/hls/hls_live_unit.js +3 -1
- package/test/hls/hls_parser_unit.js +144 -0
- package/test/media/media_source_engine_unit.js +30 -0
- package/test/media/region_observer_unit.js +2 -1
- package/test/media/streaming_engine_unit.js +26 -2
- package/test/test/assets/h265-cea-init.mp4 +0 -0
- package/test/test/assets/h265-cea-segment.mp4 +0 -0
- package/test/test/assets/multiple-trun-init.mp4 +0 -0
- package/test/test/assets/multiple-trun-segment.mp4 +0 -0
- package/test/test/util/layout_tests.js +4 -3
- package/ui/resolution_selection.js +7 -1
|
@@ -109,15 +109,15 @@ shaka.media.MediaSourceEngine = class {
|
|
|
109
109
|
/** @private {!shaka.util.PublicPromise} */
|
|
110
110
|
this.mediaSourceOpen_ = new shaka.util.PublicPromise();
|
|
111
111
|
|
|
112
|
+
/** @private {string} */
|
|
113
|
+
this.url_ = '';
|
|
114
|
+
|
|
112
115
|
/** @private {MediaSource} */
|
|
113
116
|
this.mediaSource_ = this.createMediaSource(this.mediaSourceOpen_);
|
|
114
117
|
|
|
115
118
|
/** @type {!shaka.util.Destroyer} */
|
|
116
119
|
this.destroyer_ = new shaka.util.Destroyer(() => this.doDestroy_());
|
|
117
120
|
|
|
118
|
-
/** @private {string} */
|
|
119
|
-
this.url_ = '';
|
|
120
|
-
|
|
121
121
|
/** @private {boolean} */
|
|
122
122
|
this.sequenceMode_ = false;
|
|
123
123
|
|
|
@@ -154,6 +154,8 @@ shaka.media.MediaSourceEngine = class {
|
|
|
154
154
|
* @private
|
|
155
155
|
*/
|
|
156
156
|
onSourceOpen_(p) {
|
|
157
|
+
goog.asserts.assert(this.url_, 'Must have object URL');
|
|
158
|
+
|
|
157
159
|
// Release the object URL that was previously created, to prevent memory
|
|
158
160
|
// leak.
|
|
159
161
|
// createObjectURL creates a strong reference to the MediaSource object
|
|
@@ -580,11 +582,13 @@ shaka.media.MediaSourceEngine = class {
|
|
|
580
582
|
* @param {boolean=} seeked True if we just seeked
|
|
581
583
|
* @param {boolean=} adaptation True if we just automatically switched active
|
|
582
584
|
* variant(s).
|
|
585
|
+
* @param {boolean=} isChunkedData True if we add to the buffer from the
|
|
586
|
+
* partial read of the segment.
|
|
583
587
|
* @return {!Promise}
|
|
584
588
|
*/
|
|
585
589
|
async appendBuffer(
|
|
586
590
|
contentType, data, reference, hasClosedCaptions, seeked = false,
|
|
587
|
-
adaptation = false) {
|
|
591
|
+
adaptation = false, isChunkedData = false) {
|
|
588
592
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
589
593
|
|
|
590
594
|
if (contentType == ContentType.TEXT) {
|
|
@@ -785,13 +789,13 @@ shaka.media.MediaSourceEngine = class {
|
|
|
785
789
|
let bufferedBefore = null;
|
|
786
790
|
|
|
787
791
|
await this.enqueueOperation_(contentType, () => {
|
|
788
|
-
if (goog.DEBUG && reference) {
|
|
792
|
+
if (goog.DEBUG && reference && !reference.isPreload() && !isChunkedData) {
|
|
789
793
|
bufferedBefore = this.getBuffered_(contentType);
|
|
790
794
|
}
|
|
791
795
|
this.append_(contentType, data);
|
|
792
796
|
});
|
|
793
797
|
|
|
794
|
-
if (goog.DEBUG && reference) {
|
|
798
|
+
if (goog.DEBUG && reference && !reference.isPreload() && !isChunkedData) {
|
|
795
799
|
const bufferedAfter = this.getBuffered_(contentType);
|
|
796
800
|
const newBuffered = shaka.media.TimeRangesUtils.computeAddedRange(
|
|
797
801
|
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);
|
|
@@ -243,6 +243,9 @@ shaka.media.SegmentReference = class {
|
|
|
243
243
|
/** @type {shaka.media.SegmentReference.Status} */
|
|
244
244
|
this.status = status;
|
|
245
245
|
|
|
246
|
+
/** @type {boolean} */
|
|
247
|
+
this.preload = false;
|
|
248
|
+
|
|
246
249
|
/** @type {?shaka.extern.HlsAes128Key} */
|
|
247
250
|
this.hlsAes128Key = hlsAes128Key;
|
|
248
251
|
|
|
@@ -366,6 +369,25 @@ shaka.media.SegmentReference = class {
|
|
|
366
369
|
this.status = shaka.media.SegmentReference.Status.UNAVAILABLE;
|
|
367
370
|
}
|
|
368
371
|
|
|
372
|
+
/**
|
|
373
|
+
* Mark the reference as preload.
|
|
374
|
+
*
|
|
375
|
+
* @export
|
|
376
|
+
*/
|
|
377
|
+
markAsPreload() {
|
|
378
|
+
this.preload = true;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Returns true if the segment is preloaded.
|
|
383
|
+
*
|
|
384
|
+
* @return {boolean}
|
|
385
|
+
* @export
|
|
386
|
+
*/
|
|
387
|
+
isPreload() {
|
|
388
|
+
return this.preload;
|
|
389
|
+
}
|
|
390
|
+
|
|
369
391
|
/**
|
|
370
392
|
* Set the segment's thumbnail sprite.
|
|
371
393
|
*
|
|
@@ -651,10 +651,15 @@ shaka.media.StreamingEngine = class {
|
|
|
651
651
|
const mediaState = this.mediaStates_.get(type);
|
|
652
652
|
const logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);
|
|
653
653
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
654
|
+
let segment = null;
|
|
655
|
+
if (mediaState.segmentIterator) {
|
|
656
|
+
segment = mediaState.segmentIterator.current();
|
|
657
|
+
}
|
|
658
|
+
// Only reset the iterator if we seek outside the current segment.
|
|
659
|
+
if (!segment || segment.startTime > presentationTime ||
|
|
660
|
+
segment.endTime < presentationTime) {
|
|
661
|
+
mediaState.segmentIterator = null;
|
|
662
|
+
}
|
|
658
663
|
|
|
659
664
|
if (!newTimeIsBuffered(type)) {
|
|
660
665
|
const bufferEnd =
|
|
@@ -1314,7 +1319,8 @@ shaka.media.StreamingEngine = class {
|
|
|
1314
1319
|
const dataToAppend = remaining.subarray(0, offset);
|
|
1315
1320
|
remaining = remaining.subarray(offset);
|
|
1316
1321
|
await this.append_(
|
|
1317
|
-
mediaState, presentationTime, stream, reference, dataToAppend
|
|
1322
|
+
mediaState, presentationTime, stream, reference, dataToAppend,
|
|
1323
|
+
/* isChunkedData= */ true);
|
|
1318
1324
|
}
|
|
1319
1325
|
};
|
|
1320
1326
|
|
|
@@ -1726,10 +1732,12 @@ shaka.media.StreamingEngine = class {
|
|
|
1726
1732
|
* @param {shaka.extern.Stream} stream
|
|
1727
1733
|
* @param {!shaka.media.SegmentReference} reference
|
|
1728
1734
|
* @param {BufferSource} segment
|
|
1735
|
+
* @param {boolean=} isChunkedData
|
|
1729
1736
|
* @return {!Promise}
|
|
1730
1737
|
* @private
|
|
1731
1738
|
*/
|
|
1732
|
-
async append_(mediaState, presentationTime, stream, reference, segment
|
|
1739
|
+
async append_(mediaState, presentationTime, stream, reference, segment,
|
|
1740
|
+
isChunkedData = false) {
|
|
1733
1741
|
const logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);
|
|
1734
1742
|
|
|
1735
1743
|
const hasClosedCaptions = stream.closedCaptions &&
|
|
@@ -1783,7 +1791,8 @@ shaka.media.StreamingEngine = class {
|
|
|
1783
1791
|
reference,
|
|
1784
1792
|
hasClosedCaptions,
|
|
1785
1793
|
seeked,
|
|
1786
|
-
adaptation
|
|
1794
|
+
adaptation,
|
|
1795
|
+
isChunkedData);
|
|
1787
1796
|
this.destroyer_.ensureNotDestroyed();
|
|
1788
1797
|
shaka.log.v2(logPrefix, 'appended media segment');
|
|
1789
1798
|
}
|
|
@@ -1847,7 +1856,6 @@ shaka.media.StreamingEngine = class {
|
|
|
1847
1856
|
const messageData = box.reader.readBytes(
|
|
1848
1857
|
box.reader.getLength() - box.reader.getPosition());
|
|
1849
1858
|
|
|
1850
|
-
|
|
1851
1859
|
// See DASH sec. 5.10.3.3.1
|
|
1852
1860
|
// If a DASH client detects an event message box with a scheme that is not
|
|
1853
1861
|
// defined in MPD, the client is expected to ignore it.
|
|
@@ -1857,22 +1865,8 @@ shaka.media.StreamingEngine = class {
|
|
|
1857
1865
|
// A special scheme in DASH used to signal manifest updates.
|
|
1858
1866
|
if (schemeId == 'urn:mpeg:dash:event:2012') {
|
|
1859
1867
|
this.playerInterface_.onManifestUpdate();
|
|
1860
|
-
} else if (schemeId == 'https://aomedia.org/emsg/ID3') {
|
|
1861
|
-
// See https://aomediacodec.github.io/id3-emsg/
|
|
1862
|
-
const frames = shaka.util.Id3Utils.getID3Frames(messageData);
|
|
1863
|
-
if (frames.length && reference) {
|
|
1864
|
-
/** @private {shaka.extern.ID3Metadata} */
|
|
1865
|
-
const metadata = {
|
|
1866
|
-
cueTime: reference.startTime,
|
|
1867
|
-
data: messageData,
|
|
1868
|
-
frames: frames,
|
|
1869
|
-
dts: reference.startTime,
|
|
1870
|
-
pts: reference.startTime,
|
|
1871
|
-
};
|
|
1872
|
-
this.playerInterface_.onMetadata(
|
|
1873
|
-
[metadata], /* offset= */ 0, reference.endTime);
|
|
1874
|
-
}
|
|
1875
1868
|
} else {
|
|
1869
|
+
// All other schemes are dispatched as a general 'emsg' event.
|
|
1876
1870
|
/** @type {shaka.extern.EmsgInfo} */
|
|
1877
1871
|
const emsg = {
|
|
1878
1872
|
startTime: startTime,
|
|
@@ -1890,7 +1884,38 @@ shaka.media.StreamingEngine = class {
|
|
|
1890
1884
|
const eventName = shaka.util.FakeEvent.EventName.Emsg;
|
|
1891
1885
|
const data = (new Map()).set('detail', emsg);
|
|
1892
1886
|
const event = new shaka.util.FakeEvent(eventName, data);
|
|
1887
|
+
// A user can call preventDefault() on a cancelable event.
|
|
1888
|
+
event.cancelable = true;
|
|
1889
|
+
|
|
1893
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
|
+
}
|
|
1894
1919
|
}
|
|
1895
1920
|
}
|
|
1896
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/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
|
|
@@ -2191,7 +2193,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2191
2193
|
}
|
|
2192
2194
|
|
|
2193
2195
|
this.playhead_ = this.createPlayhead(has.startTime);
|
|
2194
|
-
this.playheadObservers_ =
|
|
2196
|
+
this.playheadObservers_ =
|
|
2197
|
+
this.createPlayheadObserversForMSE_(has.startTimeOfLoad);
|
|
2195
2198
|
|
|
2196
2199
|
// We need to start the buffer management code near the end because it will
|
|
2197
2200
|
// set the initial buffering state and that depends on other components
|
|
@@ -2918,17 +2921,21 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2918
2921
|
* Create the observers for MSE playback. These observers are responsible for
|
|
2919
2922
|
* notifying the app and player of specific events during MSE playback.
|
|
2920
2923
|
*
|
|
2924
|
+
* @param {number} startTimeOfLoad
|
|
2921
2925
|
* @return {!shaka.media.PlayheadObserverManager}
|
|
2922
2926
|
* @private
|
|
2923
2927
|
*/
|
|
2924
|
-
createPlayheadObserversForMSE_() {
|
|
2928
|
+
createPlayheadObserversForMSE_(startTimeOfLoad) {
|
|
2925
2929
|
goog.asserts.assert(this.manifest_, 'Must have manifest');
|
|
2926
2930
|
goog.asserts.assert(this.regionTimeline_, 'Must have region timeline');
|
|
2927
2931
|
goog.asserts.assert(this.video_, 'Must have video element');
|
|
2928
2932
|
|
|
2933
|
+
const startsPastZero = this.isLive() || startTimeOfLoad > 0;
|
|
2934
|
+
|
|
2929
2935
|
// Create the region observer. This will allow us to notify the app when we
|
|
2930
2936
|
// move in and out of timeline regions.
|
|
2931
|
-
const regionObserver = new shaka.media.RegionObserver(
|
|
2937
|
+
const regionObserver = new shaka.media.RegionObserver(
|
|
2938
|
+
this.regionTimeline_, startsPastZero);
|
|
2932
2939
|
|
|
2933
2940
|
regionObserver.addEventListener('enter', (event) => {
|
|
2934
2941
|
/** @type {shaka.extern.TimelineRegionInfo} */
|
|
@@ -6066,6 +6073,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6066
6073
|
error.category != shaka.util.Error.Category.NETWORK) {
|
|
6067
6074
|
return false;
|
|
6068
6075
|
}
|
|
6076
|
+
if (this.loadMode_ != shaka.Player.LoadMode.MEDIA_SOURCE) {
|
|
6077
|
+
return false;
|
|
6078
|
+
}
|
|
6079
|
+
goog.asserts.assert(this.manifest_, 'Should have manifest!');
|
|
6069
6080
|
|
|
6070
6081
|
if (!navigator.onLine) {
|
|
6071
6082
|
// Don't restrict variants if we're completely offline, or else we end up
|
|
@@ -7005,7 +7016,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
7005
7016
|
* @define {string} A version number taken from git at compile time.
|
|
7006
7017
|
* @export
|
|
7007
7018
|
*/
|
|
7008
|
-
shaka.Player.version = 'v4.3.
|
|
7019
|
+
shaka.Player.version = 'v4.3.8-uncompiled';
|
|
7009
7020
|
|
|
7010
7021
|
// Initialize the deprecation system using the version string we just set
|
|
7011
7022
|
// on the player.
|
|
@@ -785,7 +785,8 @@ shaka.text.VttTextParser = class {
|
|
|
785
785
|
} else if ((results = /^size:([\d.]+)%$/.exec(word))) {
|
|
786
786
|
cue.size = Number(results[1]);
|
|
787
787
|
} else if ((results =
|
|
788
|
-
|
|
788
|
+
// eslint-disable-next-line max-len
|
|
789
|
+
/^position:([\d.]+)%(?:,(line-left|line-right|middle|center|start|end))?$/
|
|
789
790
|
.exec(word))) {
|
|
790
791
|
cue.position = Number(results[1]);
|
|
791
792
|
if (results[2]) {
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -1068,6 +1068,7 @@ shaka.util.StreamUtils = class {
|
|
|
1068
1068
|
track.frameRate = video.frameRate || null;
|
|
1069
1069
|
track.pixelAspectRatio = video.pixelAspectRatio || null;
|
|
1070
1070
|
track.videoBandwidth = video.bandwidth || null;
|
|
1071
|
+
track.hdr = video.hdr || null;
|
|
1071
1072
|
}
|
|
1072
1073
|
|
|
1073
1074
|
if (audio) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shaka-player",
|
|
3
3
|
"description": "DASH/EME video player library",
|
|
4
|
-
"version": "4.3.
|
|
4
|
+
"version": "4.3.8",
|
|
5
5
|
"homepage": "https://github.com/shaka-project/shaka-player",
|
|
6
6
|
"author": "Google",
|
|
7
7
|
"maintainers": [
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"awesomplete": "^1.1.5",
|
|
19
19
|
"babel-plugin-istanbul": "^6.1.1",
|
|
20
20
|
"cajon": "^0.4.4",
|
|
21
|
+
"clean-package": "^2.2.0",
|
|
21
22
|
"code-prettify": "^0.1.0",
|
|
22
23
|
"color-themes-for-google-code-prettify": "^2.0.4",
|
|
23
24
|
"core-js": "^3.21.1",
|
|
@@ -41,7 +42,7 @@
|
|
|
41
42
|
"karma-coverage": "^2.2.0",
|
|
42
43
|
"karma-jasmine": "^4.0.1",
|
|
43
44
|
"karma-jasmine-ajax": "^0.1.13",
|
|
44
|
-
"karma-local-wd-launcher": "^1.6.
|
|
45
|
+
"karma-local-wd-launcher": "^1.6.2",
|
|
45
46
|
"karma-opera-launcher": "^1.0.0",
|
|
46
47
|
"karma-sourcemap-loader": "^0.3.8",
|
|
47
48
|
"karma-spec-reporter": "^0.0.34",
|
|
@@ -94,12 +95,16 @@
|
|
|
94
95
|
},
|
|
95
96
|
"license": "Apache-2.0",
|
|
96
97
|
"scripts": {
|
|
98
|
+
"prepack": "clean-package",
|
|
99
|
+
"postpack": "clean-package restore",
|
|
97
100
|
"prepublishOnly": "python build/checkversion.py && python build/all.py --force"
|
|
98
101
|
},
|
|
99
102
|
"dependencies": {
|
|
100
103
|
"eme-encryption-scheme-polyfill": "^2.1.1"
|
|
101
104
|
},
|
|
102
|
-
"
|
|
103
|
-
"
|
|
105
|
+
"clean-package": {
|
|
106
|
+
"remove": [
|
|
107
|
+
"engines"
|
|
108
|
+
]
|
|
104
109
|
}
|
|
105
110
|
}
|
|
@@ -7,20 +7,43 @@
|
|
|
7
7
|
describe('Mp4CeaParser', () => {
|
|
8
8
|
const ceaInitSegmentUri = '/base/test/test/assets/cea-init.mp4';
|
|
9
9
|
const ceaSegmentUri = '/base/test/test/assets/cea-segment.mp4';
|
|
10
|
+
const h265ceaInitSegmentUri = '/base/test/test/assets/h265-cea-init.mp4';
|
|
11
|
+
const h265ceaSegmentUri = '/base/test/test/assets/h265-cea-segment.mp4';
|
|
12
|
+
const multipleTrunInitSegmentUri =
|
|
13
|
+
'/base/test/test/assets/multiple-trun-init.mp4';
|
|
14
|
+
const multipleTrunSegmentUri =
|
|
15
|
+
'/base/test/test/assets/multiple-trun-segment.mp4';
|
|
10
16
|
const Util = shaka.test.Util;
|
|
11
17
|
|
|
12
18
|
/** @type {!ArrayBuffer} */
|
|
13
19
|
let ceaInitSegment;
|
|
14
20
|
/** @type {!ArrayBuffer} */
|
|
15
21
|
let ceaSegment;
|
|
22
|
+
/** @type {!ArrayBuffer} */
|
|
23
|
+
let h265ceaInitSegment;
|
|
24
|
+
/** @type {!ArrayBuffer} */
|
|
25
|
+
let h265ceaSegment;
|
|
26
|
+
/** @type {!ArrayBuffer} */
|
|
27
|
+
let multipleTrunInitSegment;
|
|
28
|
+
/** @type {!ArrayBuffer} */
|
|
29
|
+
let multipleTrunSegment;
|
|
16
30
|
|
|
17
31
|
beforeAll(async () => {
|
|
18
|
-
|
|
32
|
+
[
|
|
33
|
+
ceaInitSegment,
|
|
34
|
+
ceaSegment,
|
|
35
|
+
h265ceaInitSegment,
|
|
36
|
+
h265ceaSegment,
|
|
37
|
+
multipleTrunInitSegment,
|
|
38
|
+
multipleTrunSegment,
|
|
39
|
+
] = await Promise.all([
|
|
19
40
|
shaka.test.Util.fetch(ceaInitSegmentUri),
|
|
20
41
|
shaka.test.Util.fetch(ceaSegmentUri),
|
|
42
|
+
shaka.test.Util.fetch(h265ceaInitSegmentUri),
|
|
43
|
+
shaka.test.Util.fetch(h265ceaSegmentUri),
|
|
44
|
+
shaka.test.Util.fetch(multipleTrunInitSegmentUri),
|
|
45
|
+
shaka.test.Util.fetch(multipleTrunSegmentUri),
|
|
21
46
|
]);
|
|
22
|
-
ceaInitSegment = responses[0];
|
|
23
|
-
ceaSegment = responses[1];
|
|
24
47
|
});
|
|
25
48
|
|
|
26
49
|
/**
|
|
@@ -70,6 +93,25 @@ describe('Mp4CeaParser', () => {
|
|
|
70
93
|
.toEqual(expectedCea708Packet);
|
|
71
94
|
});
|
|
72
95
|
|
|
96
|
+
it('parses cea data from an h265 mp4 stream', () => {
|
|
97
|
+
const ceaParser = new shaka.cea.Mp4CeaParser();
|
|
98
|
+
|
|
99
|
+
ceaParser.init(h265ceaInitSegment);
|
|
100
|
+
const ceaPackets = ceaParser.parse(h265ceaSegment);
|
|
101
|
+
expect(ceaPackets).toBeDefined();
|
|
102
|
+
expect(ceaPackets.length).toBe(60);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('parses cea data from a segment with multiple trun boxes', () => {
|
|
106
|
+
const ceaParser = new shaka.cea.Mp4CeaParser();
|
|
107
|
+
|
|
108
|
+
ceaParser.init(multipleTrunInitSegment);
|
|
109
|
+
const ceaPackets = ceaParser.parse(multipleTrunSegment);
|
|
110
|
+
// The first trun box references samples with 48 CEA packets.
|
|
111
|
+
// The second trun box references samples with 132 more, for a total of 180.
|
|
112
|
+
expect(ceaPackets.length).toBe(180);
|
|
113
|
+
});
|
|
114
|
+
|
|
73
115
|
it('parses an invalid init segment', () => {
|
|
74
116
|
const cea708Parser = new shaka.cea.Mp4CeaParser();
|
|
75
117
|
|
|
@@ -2023,6 +2023,66 @@ describe('DashParser Manifest', () => {
|
|
|
2023
2023
|
expect(stream.hdr).toBe('PQ');
|
|
2024
2024
|
});
|
|
2025
2025
|
|
|
2026
|
+
it('supports SupplementalProperty MatrixCoefficients', async () => {
|
|
2027
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
2028
|
+
const scheme = cicpScheme('MatrixCoefficients');
|
|
2029
|
+
const manifestText = [
|
|
2030
|
+
'<MPD minBufferTime="PT75S">',
|
|
2031
|
+
' <Period id="1" duration="PT30S">',
|
|
2032
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
2033
|
+
` <SupplementalProperty schemeIdUri="${scheme}" value="9" />`,
|
|
2034
|
+
' <Representation codecs="hvc1.2.4.L153.B0">',
|
|
2035
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
2036
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2037
|
+
' </Representation>',
|
|
2038
|
+
' </AdaptationSet>',
|
|
2039
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
2040
|
+
' <Representation id="audio-en">',
|
|
2041
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
2042
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2043
|
+
' </Representation>',
|
|
2044
|
+
' </AdaptationSet>',
|
|
2045
|
+
' </Period>',
|
|
2046
|
+
'</MPD>',
|
|
2047
|
+
].join('\n');
|
|
2048
|
+
|
|
2049
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
2050
|
+
|
|
2051
|
+
/** @type {shaka.extern.Manifest} */
|
|
2052
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
2053
|
+
expect(manifest.variants.length).toBe(1);
|
|
2054
|
+
});
|
|
2055
|
+
|
|
2056
|
+
it('supports SupplementalProperty ColourPrimaries', async () => {
|
|
2057
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
2058
|
+
const scheme = cicpScheme('ColourPrimaries');
|
|
2059
|
+
const manifestText = [
|
|
2060
|
+
'<MPD minBufferTime="PT75S">',
|
|
2061
|
+
' <Period id="1" duration="PT30S">',
|
|
2062
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
2063
|
+
` <SupplementalProperty schemeIdUri="${scheme}" value="9" />`,
|
|
2064
|
+
' <Representation codecs="hvc1.2.4.L153.B0">',
|
|
2065
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
2066
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2067
|
+
' </Representation>',
|
|
2068
|
+
' </AdaptationSet>',
|
|
2069
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
2070
|
+
' <Representation id="audio-en">',
|
|
2071
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
2072
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2073
|
+
' </Representation>',
|
|
2074
|
+
' </AdaptationSet>',
|
|
2075
|
+
' </Period>',
|
|
2076
|
+
'</MPD>',
|
|
2077
|
+
].join('\n');
|
|
2078
|
+
|
|
2079
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
2080
|
+
|
|
2081
|
+
/** @type {shaka.extern.Manifest} */
|
|
2082
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
2083
|
+
expect(manifest.variants.length).toBe(1);
|
|
2084
|
+
});
|
|
2085
|
+
|
|
2026
2086
|
it('supports HDR signaling via EssentialProperty', async () => {
|
|
2027
2087
|
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
2028
2088
|
const hdrScheme = cicpScheme('TransferCharacteristics');
|
|
@@ -2056,6 +2116,66 @@ describe('DashParser Manifest', () => {
|
|
|
2056
2116
|
expect(stream.hdr).toBe('HLG');
|
|
2057
2117
|
});
|
|
2058
2118
|
|
|
2119
|
+
it('supports EssentialProperty MatrixCoefficients', async () => {
|
|
2120
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
2121
|
+
const hdrScheme = cicpScheme('MatrixCoefficients');
|
|
2122
|
+
const manifestText = [
|
|
2123
|
+
'<MPD minBufferTime="PT75S">',
|
|
2124
|
+
' <Period id="1" duration="PT30S">',
|
|
2125
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
2126
|
+
` <EssentialProperty schemeIdUri="${hdrScheme}" value="9" />`,
|
|
2127
|
+
' <Representation codecs="hvc1.2.4.L153.B0">',
|
|
2128
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
2129
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2130
|
+
' </Representation>',
|
|
2131
|
+
' </AdaptationSet>',
|
|
2132
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
2133
|
+
' <Representation id="audio-en">',
|
|
2134
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
2135
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2136
|
+
' </Representation>',
|
|
2137
|
+
' </AdaptationSet>',
|
|
2138
|
+
' </Period>',
|
|
2139
|
+
'</MPD>',
|
|
2140
|
+
].join('\n');
|
|
2141
|
+
|
|
2142
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
2143
|
+
|
|
2144
|
+
/** @type {shaka.extern.Manifest} */
|
|
2145
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
2146
|
+
expect(manifest.variants.length).toBe(1);
|
|
2147
|
+
});
|
|
2148
|
+
|
|
2149
|
+
it('supports EssentialProperty ColourPrimaries', async () => {
|
|
2150
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
2151
|
+
const hdrScheme = cicpScheme('ColourPrimaries');
|
|
2152
|
+
const manifestText = [
|
|
2153
|
+
'<MPD minBufferTime="PT75S">',
|
|
2154
|
+
' <Period id="1" duration="PT30S">',
|
|
2155
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
2156
|
+
` <EssentialProperty schemeIdUri="${hdrScheme}" value="9" />`,
|
|
2157
|
+
' <Representation codecs="hvc1.2.4.L153.B0">',
|
|
2158
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
2159
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2160
|
+
' </Representation>',
|
|
2161
|
+
' </AdaptationSet>',
|
|
2162
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
2163
|
+
' <Representation id="audio-en">',
|
|
2164
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
2165
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2166
|
+
' </Representation>',
|
|
2167
|
+
' </AdaptationSet>',
|
|
2168
|
+
' </Period>',
|
|
2169
|
+
'</MPD>',
|
|
2170
|
+
].join('\n');
|
|
2171
|
+
|
|
2172
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
2173
|
+
|
|
2174
|
+
/** @type {shaka.extern.Manifest} */
|
|
2175
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
2176
|
+
expect(manifest.variants.length).toBe(1);
|
|
2177
|
+
});
|
|
2178
|
+
|
|
2059
2179
|
it('supports SDR signalling via EssentialProperty', async () => {
|
|
2060
2180
|
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
2061
2181
|
const scheme = cicpScheme('TransferCharacteristics');
|