shaka-player 4.3.6 → 4.3.7
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/demo-version-index.yaml +3 -3
- package/CHANGELOG.md +21 -0
- package/README.md +3 -0
- package/demo/demo.less +1 -0
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/locales.js +31 -31
- package/dist/shaka-player.compiled.d.ts +8 -0
- package/dist/shaka-player.compiled.debug.d.ts +8 -0
- package/dist/shaka-player.compiled.debug.externs.js +9 -0
- package/dist/shaka-player.compiled.debug.js +130 -125
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +9 -0
- package/dist/shaka-player.compiled.js +754 -751
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +8 -0
- package/dist/shaka-player.ui.debug.d.ts +8 -0
- package/dist/shaka-player.ui.debug.externs.js +9 -0
- package/dist/shaka-player.ui.debug.js +236 -231
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +9 -0
- package/dist/shaka-player.ui.js +578 -575
- package/dist/shaka-player.ui.map +3 -3
- package/docs/tutorials/welcome.md +1 -1
- package/lib/cea/mp4_cea_parser.js +5 -2
- package/lib/hls/hls_parser.js +43 -2
- package/lib/hls/manifest_text_parser.js +10 -1
- package/lib/media/drm_engine.js +9 -6
- package/lib/media/media_source_engine.js +6 -4
- package/lib/media/segment_reference.js +22 -0
- package/lib/media/streaming_engine.js +9 -4
- package/lib/player.js +5 -1
- package/package.json +8 -3
- package/test/cea/mp4_cea_parser_unit.js +19 -0
- package/test/hls/hls_live_unit.js +3 -1
- package/test/media/media_source_engine_unit.js +30 -0
- package/test/test/assets/h265-cea-init.mp4 +0 -0
- package/test/test/assets/h265-cea-segment.mp4 +0 -0
- package/test/test/util/layout_tests.js +4 -3
|
@@ -29,7 +29,7 @@ You can build Shaka on Linux, Windows, or Mac.
|
|
|
29
29
|
To get the sources and compile the library, you will need:
|
|
30
30
|
* {@link https://git-scm.com/downloads Git v1.9+}
|
|
31
31
|
* {@link https://www.python.org/downloads/ Python v2.7 or v3.5+}
|
|
32
|
-
* {@link https://
|
|
32
|
+
* {@link https://learn.microsoft.com/en-us/java/openjdk/download Java Runtime Environment v14+}
|
|
33
33
|
* {@link https://nodejs.org/en/download/ NodeJS v14+}
|
|
34
34
|
* A local web server, such as {@link https://httpd.apache.org/ Apache}
|
|
35
35
|
* _NOTE: A local web server is required because browsers place restrictions
|
|
@@ -275,6 +275,7 @@ shaka.cea.Mp4CeaParser = class {
|
|
|
275
275
|
const naluHeader = reader.readUint8();
|
|
276
276
|
let naluType = null;
|
|
277
277
|
let isSeiMessage = false;
|
|
278
|
+
let naluHeaderSize = 1;
|
|
278
279
|
|
|
279
280
|
goog.asserts.assert(this.bitstreamFormat_ != BitstreamFormat.UNKNOWN,
|
|
280
281
|
'Bitstream format should have been checked before now!');
|
|
@@ -285,6 +286,8 @@ shaka.cea.Mp4CeaParser = class {
|
|
|
285
286
|
break;
|
|
286
287
|
|
|
287
288
|
case BitstreamFormat.H265:
|
|
289
|
+
naluHeaderSize = 2;
|
|
290
|
+
reader.skip(1);
|
|
288
291
|
naluType = (naluHeader >> 1) & 0x3f;
|
|
289
292
|
isSeiMessage =
|
|
290
293
|
naluType == ICeaParser.H265_PREFIX_NALU_TYPE_SEI ||
|
|
@@ -304,7 +307,7 @@ shaka.cea.Mp4CeaParser = class {
|
|
|
304
307
|
|
|
305
308
|
const pts = (time + timeOffset)/timescale;
|
|
306
309
|
for (const packet of this.seiProcessor_
|
|
307
|
-
.process(reader.readBytes(naluSize -
|
|
310
|
+
.process(reader.readBytes(naluSize - naluHeaderSize))) {
|
|
308
311
|
captionPackets.push({
|
|
309
312
|
packet,
|
|
310
313
|
pts,
|
|
@@ -312,7 +315,7 @@ shaka.cea.Mp4CeaParser = class {
|
|
|
312
315
|
}
|
|
313
316
|
} else {
|
|
314
317
|
try {
|
|
315
|
-
reader.skip(naluSize -
|
|
318
|
+
reader.skip(naluSize - naluHeaderSize);
|
|
316
319
|
} catch (e) {
|
|
317
320
|
// It is necessary to ignore this error because it can break the start
|
|
318
321
|
// of playback even if the user does not want to see the subtitles.
|
package/lib/hls/hls_parser.js
CHANGED
|
@@ -319,6 +319,9 @@ shaka.hls.HlsParser = class {
|
|
|
319
319
|
this.presentationTimeline_.setDuration(Math.min(...maxTimestamps));
|
|
320
320
|
this.playerInterface_.updateDuration();
|
|
321
321
|
}
|
|
322
|
+
if (stillLive) {
|
|
323
|
+
this.determineDuration_();
|
|
324
|
+
}
|
|
322
325
|
}
|
|
323
326
|
|
|
324
327
|
/**
|
|
@@ -400,9 +403,16 @@ shaka.hls.HlsParser = class {
|
|
|
400
403
|
mediaSequenceNumber);
|
|
401
404
|
stream.segmentIndex.evict(playlistStartTime);
|
|
402
405
|
}
|
|
406
|
+
const oldSegment = segments[0];
|
|
407
|
+
goog.asserts.assert(oldSegment, 'Should have segments!');
|
|
408
|
+
|
|
409
|
+
streamInfo.minTimestamp = oldSegment.startTime;
|
|
410
|
+
|
|
403
411
|
const newestSegment = segments[segments.length - 1];
|
|
404
412
|
goog.asserts.assert(newestSegment, 'Should have segments!');
|
|
405
413
|
|
|
414
|
+
streamInfo.maxTimestamp = newestSegment.endTime;
|
|
415
|
+
|
|
406
416
|
// Once the last segment has been added to the playlist,
|
|
407
417
|
// #EXT-X-ENDLIST tag will be appended.
|
|
408
418
|
// If that happened, treat the rest of the EVENT presentation as VOD.
|
|
@@ -413,7 +423,6 @@ shaka.hls.HlsParser = class {
|
|
|
413
423
|
// Flag this for later. We don't convert the whole presentation into VOD
|
|
414
424
|
// until we've seen the ENDLIST tag for all active playlists.
|
|
415
425
|
streamInfo.hasEndList = true;
|
|
416
|
-
streamInfo.maxTimestamp = newestSegment.endTime;
|
|
417
426
|
}
|
|
418
427
|
}
|
|
419
428
|
|
|
@@ -576,6 +585,9 @@ shaka.hls.HlsParser = class {
|
|
|
576
585
|
* @private
|
|
577
586
|
*/
|
|
578
587
|
offsetStreamInfo_(streamInfo, offset) {
|
|
588
|
+
// Adjust our accounting of the minimum timestamp.
|
|
589
|
+
streamInfo.minTimestamp += offset;
|
|
590
|
+
|
|
579
591
|
// Adjust our accounting of the maximum timestamp.
|
|
580
592
|
streamInfo.maxTimestamp += offset;
|
|
581
593
|
goog.asserts.assert(streamInfo.maxTimestamp >= 0,
|
|
@@ -804,7 +816,7 @@ shaka.hls.HlsParser = class {
|
|
|
804
816
|
const PresentationType = shaka.hls.HlsParser.PresentationType_;
|
|
805
817
|
|
|
806
818
|
if (this.presentationType_ == PresentationType.LIVE) {
|
|
807
|
-
let segmentAvailabilityDuration = this.
|
|
819
|
+
let segmentAvailabilityDuration = this.getLiveDuration_();
|
|
808
820
|
|
|
809
821
|
// This defaults to the presentation delay, which has the effect of
|
|
810
822
|
// making the live stream unseekable. This is consistent with Apple's
|
|
@@ -1655,6 +1667,7 @@ shaka.hls.HlsParser = class {
|
|
|
1655
1667
|
verbatimMediaPlaylistUri,
|
|
1656
1668
|
// These values are filled out or updated after lazy-loading:
|
|
1657
1669
|
absoluteMediaPlaylistUri: initialMediaPlaylistUri,
|
|
1670
|
+
minTimestamp: 0,
|
|
1658
1671
|
maxTimestamp: 0,
|
|
1659
1672
|
mediaSequenceToStartTime: new Map(),
|
|
1660
1673
|
canSkipSegments: false,
|
|
@@ -1700,6 +1713,7 @@ shaka.hls.HlsParser = class {
|
|
|
1700
1713
|
|
|
1701
1714
|
// Copy values from the real stream info to our initial one.
|
|
1702
1715
|
streamInfo.absoluteMediaPlaylistUri = absoluteMediaPlaylistUri;
|
|
1716
|
+
streamInfo.minTimestamp = realStreamInfo.minTimestamp;
|
|
1703
1717
|
streamInfo.maxTimestamp = realStreamInfo.maxTimestamp;
|
|
1704
1718
|
streamInfo.canSkipSegments = realStreamInfo.canSkipSegments;
|
|
1705
1719
|
streamInfo.hasEndList = realStreamInfo.hasEndList;
|
|
@@ -1833,6 +1847,22 @@ shaka.hls.HlsParser = class {
|
|
|
1833
1847
|
return minDuration;
|
|
1834
1848
|
}
|
|
1835
1849
|
|
|
1850
|
+
/**
|
|
1851
|
+
* @return {number}
|
|
1852
|
+
* @private
|
|
1853
|
+
*/
|
|
1854
|
+
getLiveDuration_() {
|
|
1855
|
+
let maxTimestamp = Infinity;
|
|
1856
|
+
let minTimestamp = Infinity;
|
|
1857
|
+
for (const streamInfo of this.uriToStreamInfosMap_.values()) {
|
|
1858
|
+
if (streamInfo.stream.segmentIndex && streamInfo.stream.type != 'text') {
|
|
1859
|
+
maxTimestamp = Math.min(maxTimestamp, streamInfo.maxTimestamp);
|
|
1860
|
+
minTimestamp = Math.min(minTimestamp, streamInfo.minTimestamp);
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
return maxTimestamp - minTimestamp;
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1836
1866
|
/**
|
|
1837
1867
|
* @param {!Array.<!shaka.extern.Stream>} streams
|
|
1838
1868
|
* @private
|
|
@@ -1978,6 +2008,7 @@ shaka.hls.HlsParser = class {
|
|
|
1978
2008
|
this.determinePresentationType_(playlist);
|
|
1979
2009
|
}
|
|
1980
2010
|
|
|
2011
|
+
const firstStartTime = segments[0].startTime;
|
|
1981
2012
|
const lastEndTime = segments[segments.length - 1].endTime;
|
|
1982
2013
|
/** @type {!shaka.media.SegmentIndex} */
|
|
1983
2014
|
const segmentIndex = new shaka.media.SegmentIndex(segments);
|
|
@@ -2000,6 +2031,7 @@ shaka.hls.HlsParser = class {
|
|
|
2000
2031
|
type,
|
|
2001
2032
|
verbatimMediaPlaylistUri,
|
|
2002
2033
|
absoluteMediaPlaylistUri,
|
|
2034
|
+
minTimestamp: firstStartTime,
|
|
2003
2035
|
maxTimestamp: lastEndTime,
|
|
2004
2036
|
canSkipSegments,
|
|
2005
2037
|
hasEndList: false,
|
|
@@ -2498,6 +2530,9 @@ shaka.hls.HlsParser = class {
|
|
|
2498
2530
|
/* tileDuration= */ null,
|
|
2499
2531
|
/* syncTime= */ null,
|
|
2500
2532
|
partialStatus);
|
|
2533
|
+
if (item.name == 'EXT-X-PRELOAD-HINT') {
|
|
2534
|
+
partial.markAsPreload();
|
|
2535
|
+
}
|
|
2501
2536
|
partialSegmentRefs.push(partial);
|
|
2502
2537
|
} // for-loop of hlsSegment.partialSegments
|
|
2503
2538
|
}
|
|
@@ -2807,6 +2842,9 @@ shaka.hls.HlsParser = class {
|
|
|
2807
2842
|
* @private
|
|
2808
2843
|
*/
|
|
2809
2844
|
variableSubstitution_(uri, variables) {
|
|
2845
|
+
if (!variables.size) {
|
|
2846
|
+
return uri;
|
|
2847
|
+
}
|
|
2810
2848
|
let newUri = String(uri).replace(/%7B/g, '{').replace(/%7D/g, '}');
|
|
2811
2849
|
|
|
2812
2850
|
const uriVariables = newUri.match(/{\$\w*}/g);
|
|
@@ -3252,6 +3290,7 @@ shaka.hls.HlsParser = class {
|
|
|
3252
3290
|
* type: string,
|
|
3253
3291
|
* verbatimMediaPlaylistUri: string,
|
|
3254
3292
|
* absoluteMediaPlaylistUri: string,
|
|
3293
|
+
* minTimestamp: number,
|
|
3255
3294
|
* maxTimestamp: number,
|
|
3256
3295
|
* mediaSequenceToStartTime: !Map.<number, number>,
|
|
3257
3296
|
* canSkipSegments: boolean,
|
|
@@ -3275,6 +3314,8 @@ shaka.hls.HlsParser = class {
|
|
|
3275
3314
|
* @property {string} absoluteMediaPlaylistUri
|
|
3276
3315
|
* The absolute media playlist URI, resolved relative to the master playlist
|
|
3277
3316
|
* and updated to reflect any redirects.
|
|
3317
|
+
* @property {number} minTimestamp
|
|
3318
|
+
* The minimum timestamp found in the stream.
|
|
3278
3319
|
* @property {number} maxTimestamp
|
|
3279
3320
|
* The maximum timestamp found in the stream.
|
|
3280
3321
|
* @property {!Map.<number, number>} mediaSequenceToStartTime
|
|
@@ -158,7 +158,15 @@ shaka.hls.ManifestTextParser = class {
|
|
|
158
158
|
partialSegmentTags.push(tag);
|
|
159
159
|
} else if (tag.name == 'EXT-X-PRELOAD-HINT') {
|
|
160
160
|
if (tag.getAttributeValue('TYPE') == 'PART') {
|
|
161
|
-
|
|
161
|
+
// Note: BYTERANGE-START without BYTERANGE-LENGTH is being
|
|
162
|
+
// ignored.
|
|
163
|
+
if (tag.getAttributeValue('BYTERANGE-START') != null) {
|
|
164
|
+
if (tag.getAttributeValue('BYTERANGE-LENGTH') != null) {
|
|
165
|
+
partialSegmentTags.push(tag);
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
partialSegmentTags.push(tag);
|
|
169
|
+
}
|
|
162
170
|
} else if (tag.getAttributeValue('TYPE') == 'MAP') {
|
|
163
171
|
// Rename the Preload Hint tag to be a Map tag.
|
|
164
172
|
tag.setName('EXT-X-MAP');
|
|
@@ -300,6 +308,7 @@ shaka.hls.ManifestTextParser.MEDIA_PLAYLIST_TAGS = [
|
|
|
300
308
|
'EXT-X-ENDLIST',
|
|
301
309
|
'EXT-X-SERVER-CONTROL',
|
|
302
310
|
'EXT-X-SKIP',
|
|
311
|
+
'EXT-X-PART-INF',
|
|
303
312
|
];
|
|
304
313
|
|
|
305
314
|
|
package/lib/media/drm_engine.js
CHANGED
|
@@ -133,6 +133,9 @@ shaka.media.DrmEngine = class {
|
|
|
133
133
|
|
|
134
134
|
/** @private {Promise} */
|
|
135
135
|
this.mediaKeysAttached_ = null;
|
|
136
|
+
|
|
137
|
+
/** @private {?shaka.extern.InitDataOverride} */
|
|
138
|
+
this.manifestInitData_ = null;
|
|
136
139
|
}
|
|
137
140
|
|
|
138
141
|
/** @override */
|
|
@@ -480,10 +483,10 @@ shaka.media.DrmEngine = class {
|
|
|
480
483
|
() => this.closeOpenSessions_());
|
|
481
484
|
}
|
|
482
485
|
|
|
483
|
-
|
|
484
|
-
this.currentDrmInfo_.initData.find(
|
|
486
|
+
this.manifestInitData_ = this.currentDrmInfo_ ?
|
|
487
|
+
(this.currentDrmInfo_.initData.find(
|
|
485
488
|
(initDataOverride) => initDataOverride.initData.length > 0,
|
|
486
|
-
) : null;
|
|
489
|
+
) || null) : null;
|
|
487
490
|
|
|
488
491
|
/**
|
|
489
492
|
* We can attach media keys before the playback actually begins when:
|
|
@@ -491,7 +494,7 @@ shaka.media.DrmEngine = class {
|
|
|
491
494
|
* - Some initData already has been generated (through the manifest)
|
|
492
495
|
* - In case of an offline session
|
|
493
496
|
*/
|
|
494
|
-
if (
|
|
497
|
+
if (this.manifestInitData_ ||
|
|
495
498
|
this.currentDrmInfo_.keySystem !== 'com.apple.fps' ||
|
|
496
499
|
this.offlineSessionIds_.length) {
|
|
497
500
|
await this.attachMediaKeys_();
|
|
@@ -504,7 +507,7 @@ shaka.media.DrmEngine = class {
|
|
|
504
507
|
// Also suppress 'encrypted' events when parsing in-band ppsh
|
|
505
508
|
// from media segments because that serves the same purpose as the
|
|
506
509
|
// 'encrypted' events.
|
|
507
|
-
if (!
|
|
510
|
+
if (!this.manifestInitData_ && !this.offlineSessionIds_.length &&
|
|
508
511
|
!this.config_.parseInbandPsshEnabled) {
|
|
509
512
|
this.eventManager_.listen(
|
|
510
513
|
this.video_, 'encrypted', (e) => this.onEncryptedEvent_(e));
|
|
@@ -2361,7 +2364,7 @@ shaka.media.DrmEngine = class {
|
|
|
2361
2364
|
* @return {!Promise<void>}
|
|
2362
2365
|
*/
|
|
2363
2366
|
parseInbandPssh(contentType, mediaSegment) {
|
|
2364
|
-
if (!this.config_.parseInbandPsshEnabled) {
|
|
2367
|
+
if (!this.config_.parseInbandPsshEnabled || this.manifestInitData_) {
|
|
2365
2368
|
return Promise.resolve();
|
|
2366
2369
|
}
|
|
2367
2370
|
|
|
@@ -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
|
|
@@ -791,7 +793,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
791
793
|
this.append_(contentType, data);
|
|
792
794
|
});
|
|
793
795
|
|
|
794
|
-
if (goog.DEBUG && reference) {
|
|
796
|
+
if (goog.DEBUG && reference && !reference.isPreload()) {
|
|
795
797
|
const bufferedAfter = this.getBuffered_(contentType);
|
|
796
798
|
const newBuffered = shaka.media.TimeRangesUtils.computeAddedRange(
|
|
797
799
|
bufferedBefore, bufferedAfter);
|
|
@@ -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 =
|
package/lib/player.js
CHANGED
|
@@ -6066,6 +6066,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6066
6066
|
error.category != shaka.util.Error.Category.NETWORK) {
|
|
6067
6067
|
return false;
|
|
6068
6068
|
}
|
|
6069
|
+
if (this.loadMode_ != shaka.Player.LoadMode.MEDIA_SOURCE) {
|
|
6070
|
+
return false;
|
|
6071
|
+
}
|
|
6072
|
+
goog.asserts.assert(this.manifest_, 'Should have manifest!');
|
|
6069
6073
|
|
|
6070
6074
|
if (!navigator.onLine) {
|
|
6071
6075
|
// Don't restrict variants if we're completely offline, or else we end up
|
|
@@ -7005,7 +7009,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
7005
7009
|
* @define {string} A version number taken from git at compile time.
|
|
7006
7010
|
* @export
|
|
7007
7011
|
*/
|
|
7008
|
-
shaka.Player.version = 'v4.3.
|
|
7012
|
+
shaka.Player.version = 'v4.3.7-uncompiled';
|
|
7009
7013
|
|
|
7010
7014
|
// Initialize the deprecation system using the version string we just set
|
|
7011
7015
|
// on the player.
|
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.7",
|
|
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",
|
|
@@ -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,30 @@
|
|
|
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';
|
|
10
12
|
const Util = shaka.test.Util;
|
|
11
13
|
|
|
12
14
|
/** @type {!ArrayBuffer} */
|
|
13
15
|
let ceaInitSegment;
|
|
14
16
|
/** @type {!ArrayBuffer} */
|
|
15
17
|
let ceaSegment;
|
|
18
|
+
/** @type {!ArrayBuffer} */
|
|
19
|
+
let h265ceaInitSegment;
|
|
20
|
+
/** @type {!ArrayBuffer} */
|
|
21
|
+
let h265ceaSegment;
|
|
16
22
|
|
|
17
23
|
beforeAll(async () => {
|
|
18
24
|
const responses = await Promise.all([
|
|
19
25
|
shaka.test.Util.fetch(ceaInitSegmentUri),
|
|
20
26
|
shaka.test.Util.fetch(ceaSegmentUri),
|
|
27
|
+
shaka.test.Util.fetch(h265ceaInitSegmentUri),
|
|
28
|
+
shaka.test.Util.fetch(h265ceaSegmentUri),
|
|
21
29
|
]);
|
|
22
30
|
ceaInitSegment = responses[0];
|
|
23
31
|
ceaSegment = responses[1];
|
|
32
|
+
h265ceaInitSegment = responses[2];
|
|
33
|
+
h265ceaSegment = responses[3];
|
|
24
34
|
});
|
|
25
35
|
|
|
26
36
|
/**
|
|
@@ -70,6 +80,15 @@ describe('Mp4CeaParser', () => {
|
|
|
70
80
|
.toEqual(expectedCea708Packet);
|
|
71
81
|
});
|
|
72
82
|
|
|
83
|
+
it('parses cea data from an h265 mp4 stream', () => {
|
|
84
|
+
const ceaParser = new shaka.cea.Mp4CeaParser();
|
|
85
|
+
|
|
86
|
+
ceaParser.init(h265ceaInitSegment);
|
|
87
|
+
const ceaPackets = ceaParser.parse(h265ceaSegment);
|
|
88
|
+
expect(ceaPackets).toBeDefined();
|
|
89
|
+
expect(ceaPackets.length).toBe(60);
|
|
90
|
+
});
|
|
91
|
+
|
|
73
92
|
it('parses an invalid init segment', () => {
|
|
74
93
|
const cea708Parser = new shaka.cea.Mp4CeaParser();
|
|
75
94
|
|
|
@@ -590,7 +590,8 @@ describe('HlsParser live', () => {
|
|
|
590
590
|
// partialRef3
|
|
591
591
|
'#EXT-X-PART:DURATION=2,URI="partial.mp4",BYTERANGE=210@0\n',
|
|
592
592
|
// preloadRef
|
|
593
|
-
'#EXT-X-PRELOAD-HINT:TYPE=PART,URI="partial.mp4",BYTERANGE-START=210
|
|
593
|
+
'#EXT-X-PRELOAD-HINT:TYPE=PART,URI="partial.mp4",BYTERANGE-START=210,',
|
|
594
|
+
'BYTERANGE-LENGTH=210\n',
|
|
594
595
|
].join('');
|
|
595
596
|
|
|
596
597
|
const partialRef = makeReference(
|
|
@@ -613,6 +614,7 @@ describe('HlsParser live', () => {
|
|
|
613
614
|
const preloadRef = makeReference(
|
|
614
615
|
'test:/partial.mp4', 6, 7.5, /* syncTime= */ null,
|
|
615
616
|
/* baseUri= */ '', /* startByte= */ 210, /* endByte= */ null);
|
|
617
|
+
preloadRef.markAsPreload();
|
|
616
618
|
|
|
617
619
|
// ref2 is not fully published yet, so it doesn't have a segment uri.
|
|
618
620
|
const ref2 = makeReference(
|
|
@@ -171,9 +171,12 @@ describe('MediaSourceEngine', () => {
|
|
|
171
171
|
describe('constructor', () => {
|
|
172
172
|
const originalCreateObjectURL =
|
|
173
173
|
shaka.media.MediaSourceEngine.createObjectURL;
|
|
174
|
+
const originalRevokeObjectURL = window.URL.revokeObjectURL;
|
|
174
175
|
const originalMediaSource = window.MediaSource;
|
|
175
176
|
/** @type {jasmine.Spy} */
|
|
176
177
|
let createObjectURLSpy;
|
|
178
|
+
/** @type {jasmine.Spy} */
|
|
179
|
+
let revokeObjectURLSpy;
|
|
177
180
|
|
|
178
181
|
beforeEach(async () => {
|
|
179
182
|
// Mock out MediaSource so we can test the production version of
|
|
@@ -188,6 +191,9 @@ describe('MediaSourceEngine', () => {
|
|
|
188
191
|
shaka.media.MediaSourceEngine.createObjectURL =
|
|
189
192
|
Util.spyFunc(createObjectURLSpy);
|
|
190
193
|
|
|
194
|
+
revokeObjectURLSpy = jasmine.createSpy('revokeObjectURL');
|
|
195
|
+
window.URL.revokeObjectURL = Util.spyFunc(revokeObjectURLSpy);
|
|
196
|
+
|
|
191
197
|
const mediaSourceSpy = jasmine.createSpy('MediaSource');
|
|
192
198
|
// Because this is a fake constructor, it must be callable with "new".
|
|
193
199
|
// This will cause jasmine to invoke the callback with "new" as well, so
|
|
@@ -205,6 +211,7 @@ describe('MediaSourceEngine', () => {
|
|
|
205
211
|
afterAll(() => {
|
|
206
212
|
shaka.media.MediaSourceEngine.createObjectURL = originalCreateObjectURL;
|
|
207
213
|
window.MediaSource = originalMediaSource;
|
|
214
|
+
window.URL.revokeObjectURL = originalRevokeObjectURL;
|
|
208
215
|
});
|
|
209
216
|
|
|
210
217
|
it('creates a MediaSource object and sets video.src', () => {
|
|
@@ -216,6 +223,29 @@ describe('MediaSourceEngine', () => {
|
|
|
216
223
|
expect(createObjectURLSpy).toHaveBeenCalled();
|
|
217
224
|
expect(mockVideo.src).toBe('blob:foo');
|
|
218
225
|
});
|
|
226
|
+
|
|
227
|
+
it('revokes object URL after MediaSource opens', () => {
|
|
228
|
+
let onSourceOpenListener;
|
|
229
|
+
|
|
230
|
+
mockMediaSource.addEventListener.and.callFake((event, callback, _) => {
|
|
231
|
+
if (event == 'sourceopen') {
|
|
232
|
+
onSourceOpenListener = callback;
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
mediaSourceEngine = new shaka.media.MediaSourceEngine(
|
|
237
|
+
video,
|
|
238
|
+
new shaka.test.FakeTextDisplayer());
|
|
239
|
+
|
|
240
|
+
expect(mockMediaSource.addEventListener).toHaveBeenCalledTimes(1);
|
|
241
|
+
expect(mockMediaSource.addEventListener.calls.mostRecent().args[0])
|
|
242
|
+
.toBe('sourceopen');
|
|
243
|
+
expect(typeof onSourceOpenListener).toBe(typeof Function);
|
|
244
|
+
|
|
245
|
+
onSourceOpenListener();
|
|
246
|
+
|
|
247
|
+
expect(revokeObjectURLSpy).toHaveBeenCalledWith('blob:foo');
|
|
248
|
+
});
|
|
219
249
|
});
|
|
220
250
|
|
|
221
251
|
describe('init', () => {
|
|
Binary file
|
|
Binary file
|
|
@@ -43,9 +43,10 @@ shaka.test.LayoutTests = class {
|
|
|
43
43
|
});
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
// Wait one extra
|
|
47
|
-
// updated. Without this, we saw some rare test flake in Firefox on
|
|
48
|
-
|
|
46
|
+
// Wait one extra second to make sure the font rendering on the page has
|
|
47
|
+
// been updated. Without this, we saw some rare test flake in Firefox on
|
|
48
|
+
// Mac.
|
|
49
|
+
await shaka.test.Util.delay(1);
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
/**
|