shaka-player 3.1.0 → 3.1.4
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/AUTHORS +8 -2
- package/CHANGELOG.md +209 -0
- package/CONTRIBUTORS +10 -3
- package/README.md +3 -0
- package/build/apps.py +2 -2
- package/build/check.py +2 -2
- package/build/conformance.textproto +0 -9
- package/build/generateExterns.js +1 -1
- package/build/misspellings.txt +1 -0
- package/build/shakaBuildHelpers.py +6 -5
- package/build/subprocessWindowsPatch.py +103 -0
- package/demo/common/asset.js +11 -0
- package/demo/common/assets.js +1 -1
- package/demo/common/message_ids.js +5 -0
- package/demo/config.js +2 -1
- package/demo/custom.js +84 -3
- package/demo/demo.less +13 -1
- package/demo/demo_utils.js +1 -0
- package/demo/input.js +31 -12
- package/demo/input_container.js +24 -1
- package/demo/locales/en.json +6 -1
- package/demo/locales/source.json +20 -0
- package/demo/main.js +5 -1
- package/dist/controls.css +2 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +99 -91
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +99 -91
- package/dist/demo.compiled.map +3 -3
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +9 -9
- package/dist/locales.js +36 -34
- package/dist/receiver.compiled.debug.js +74 -70
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +74 -70
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +165 -9
- package/dist/shaka-player.compiled.debug.d.ts +165 -9
- package/dist/shaka-player.compiled.debug.externs.js +179 -11
- package/dist/shaka-player.compiled.debug.js +367 -339
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +179 -11
- package/dist/shaka-player.compiled.js +818 -807
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +165 -9
- package/dist/shaka-player.ui.debug.d.ts +165 -9
- package/dist/shaka-player.ui.debug.externs.js +181 -13
- package/dist/shaka-player.ui.debug.js +459 -429
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +181 -13
- package/dist/shaka-player.ui.js +973 -961
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/fairplay.md +1 -1
- package/docs/tutorials/faq.md +25 -0
- package/docs/tutorials/index.json +2 -2
- package/docs/tutorials/upgrade-v3.md +38 -0
- package/docs/tutorials/upgrade.md +3 -3
- package/externs/ima.js +4 -0
- package/externs/media_capabilities.js +16 -0
- package/externs/shaka/ads.js +2 -0
- package/externs/shaka/net.js +2 -2
- package/externs/shaka/player.js +6 -0
- package/karma.conf.js +1 -0
- package/lib/abr/simple_abr_manager.js +1 -1
- package/lib/ads/ad_manager.js +29 -11
- package/lib/ads/client_side_ad_manager.js +76 -86
- package/lib/ads/server_side_ad_manager.js +26 -11
- package/lib/cast/cast_proxy.js +3 -2
- package/lib/cast/cast_receiver.js +2 -1
- package/lib/cast/cast_sender.js +2 -2
- package/lib/cea/sei_processor.js +24 -17
- package/lib/dash/dash_parser.js +107 -4
- package/lib/dash/segment_list.js +1 -1
- package/lib/dash/segment_template.js +45 -15
- package/lib/dependencies/all.js +15 -15
- package/lib/hls/hls_parser.js +46 -18
- package/lib/media/closed_caption_parser.js +3 -1
- package/lib/media/content_workarounds.js +18 -0
- package/lib/media/drm_engine.js +20 -1
- package/lib/media/gap_jumping_controller.js +8 -4
- package/lib/media/media_source_engine.js +1 -2
- package/lib/media/playhead.js +1 -1
- package/lib/media/presentation_timeline.js +3 -1
- package/lib/media/segment_index.js +38 -12
- package/lib/media/segment_reference.js +8 -0
- package/lib/media/streaming_engine.js +30 -21
- package/lib/net/http_fetch_plugin.js +2 -2
- package/lib/net/networking_engine.js +3 -2
- package/lib/offline/storage.js +4 -0
- package/lib/player.js +113 -56
- package/lib/polyfill/encryption_scheme.js +2 -0
- package/lib/polyfill/fullscreen.js +2 -0
- package/lib/polyfill/mathround.js +2 -0
- package/lib/polyfill/media_capabilities.js +62 -23
- package/lib/polyfill/mediasource.js +8 -0
- package/lib/polyfill/orientation.js +3 -1
- package/lib/polyfill/patchedmediakeys_apple.js +6 -4
- package/lib/polyfill/patchedmediakeys_ms.js +6 -4
- package/lib/polyfill/patchedmediakeys_nop.js +2 -0
- package/lib/polyfill/patchedmediakeys_webkit.js +6 -4
- package/lib/polyfill/pip_webkit.js +2 -0
- package/lib/polyfill/storage_estimate.js +2 -0
- package/lib/polyfill/video_play_promise.js +2 -0
- package/lib/polyfill/videoplaybackquality.js +2 -0
- package/lib/polyfill/vttcue.js +2 -0
- package/lib/text/cue.js +11 -0
- package/lib/text/mp4_ttml_parser.js +3 -0
- package/lib/text/text_engine.js +14 -2
- package/lib/text/ttml_text_parser.js +52 -30
- package/lib/text/ui_text_displayer.js +255 -82
- package/lib/text/vtt_text_parser.js +4 -2
- package/lib/util/error.js +1 -4
- package/lib/util/fake_event.js +53 -7
- package/lib/util/mime_utils.js +33 -0
- package/lib/util/mp4_box_parsers.js +1 -1
- package/lib/util/mp4_parser.js +14 -0
- package/lib/util/networking.js +1 -1
- package/lib/util/periods.js +132 -37
- package/lib/util/platform.js +13 -1
- package/lib/util/stream_utils.js +46 -7
- package/package.json +3 -3
- package/test/cast/cast_proxy_unit.js +12 -6
- package/test/cast/cast_receiver_integration.js +1 -1
- package/test/cast/cast_utils_unit.js +1 -1
- package/test/cea/mp4_cea_parser_unit.js +26 -0
- package/test/dash/dash_parser_live_unit.js +1 -0
- package/test/dash/dash_parser_manifest_unit.js +187 -1
- package/test/dash/dash_parser_segment_template_unit.js +3 -3
- package/test/demo/demo_unit.js +0 -1
- package/test/hls/hls_parser_unit.js +45 -1
- package/test/media/closed_caption_parser_unit.js +20 -0
- package/test/media/drm_engine_unit.js +59 -3
- package/test/media/playhead_unit.js +27 -1
- package/test/media/presentation_timeline_unit.js +19 -0
- package/test/media/segment_index_unit.js +76 -3
- package/test/media/streaming_engine_unit.js +3 -3
- package/test/player_integration.js +38 -0
- package/test/player_unit.js +125 -6
- package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
- package/test/test/assets/3675/dash_0.mpd +30 -0
- package/test/test/assets/3675/dash_1.mpd +30 -0
- package/test/test/assets/3675/dash_2.mpd +30 -0
- package/test/test/assets/3675/dash_3.mpd +30 -0
- package/test/test/assets/3675/dash_4.mpd +30 -0
- package/test/test/assets/3675/dash_5.mpd +26 -0
- package/test/test/assets/3675/init-stream0.m4s +0 -0
- package/test/test/assets/empty_caption_video_init.mp4 +0 -0
- package/test/test/assets/empty_caption_video_segment.mp4 +0 -0
- package/test/test/util/fake_ad_manager.js +4 -1
- package/test/test/util/simple_fakes.js +5 -3
- package/test/test/util/waiter.js +36 -2
- package/test/text/ttml_text_parser_unit.js +55 -0
- package/test/text/ui_text_displayer_unit.js +95 -18
- package/test/text/vtt_text_parser_unit.js +17 -1
- package/test/ui/localization_unit.js +15 -14
- package/test/ui/text_displayer_layout_unit.js +52 -1
- package/test/ui/ui_unit.js +4 -0
- package/test/util/mp4_box_parsers_unit.js +28 -0
- package/test/util/periods_unit.js +190 -2
- package/test/util/stream_utils_unit.js +56 -5
- package/ui/airplay_button.js +5 -0
- package/ui/big_play_button.js +10 -0
- package/ui/cast_button.js +2 -3
- package/ui/controls.js +9 -7
- package/ui/less/containers.less +10 -5
- package/ui/locales/en.json +1 -0
- package/ui/locales/ru.json +6 -6
- package/ui/locales/source.json +4 -0
- package/ui/localization.js +28 -51
- package/ui/pip_button.js +2 -3
- package/ui/play_button.js +10 -10
- package/ui/small_play_button.js +12 -0
- package/ui/ui.js +1 -1
- package/docs/tutorials/upgrade-v2-4.md +0 -481
package/lib/util/periods.js
CHANGED
|
@@ -80,7 +80,10 @@ shaka.util.PeriodCombiner = class {
|
|
|
80
80
|
|
|
81
81
|
/** @return {!Array.<shaka.extern.Stream>} */
|
|
82
82
|
getTextStreams() {
|
|
83
|
-
|
|
83
|
+
// Return a copy of the array because makeTextStreamsForClosedCaptions
|
|
84
|
+
// may make changes to the contents of the array. Those changes should not
|
|
85
|
+
// propagate back to the PeriodCombiner.
|
|
86
|
+
return this.textStreams_.slice();
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
/** @return {!Array.<shaka.extern.Stream>} */
|
|
@@ -100,6 +103,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
100
103
|
shaka.util.PeriodCombiner.filterOutAudioStreamDuplicates_(periods);
|
|
101
104
|
shaka.util.PeriodCombiner.filterOutVideoStreamDuplicates_(periods);
|
|
102
105
|
shaka.util.PeriodCombiner.filterOutTextStreamDuplicates_(periods);
|
|
106
|
+
shaka.util.PeriodCombiner.filterOutImageStreamDuplicates_(periods);
|
|
103
107
|
|
|
104
108
|
// Optimization: for single-period VOD, do nothing. This makes sure
|
|
105
109
|
// single-period DASH content will be 100% accurately represented in the
|
|
@@ -142,12 +146,18 @@ shaka.util.PeriodCombiner = class {
|
|
|
142
146
|
const imageStreamsPerPeriod = periods.map(
|
|
143
147
|
(period) => period.imageStreams);
|
|
144
148
|
|
|
145
|
-
// It's okay to have a period with no text, but our algorithm
|
|
146
|
-
// period without matching streams. So we add dummy
|
|
147
|
-
// period. Since we combine text streams by language
|
|
148
|
-
// dummy even in periods with
|
|
149
|
+
// It's okay to have a period with no text or images, but our algorithm
|
|
150
|
+
// fails on any period without matching streams. So we add dummy streams
|
|
151
|
+
// to each period. Since we combine text streams by language and image
|
|
152
|
+
// streams by resolution, we might need a dummy even in periods with these
|
|
153
|
+
// streams already.
|
|
149
154
|
for (const textStreams of textStreamsPerPeriod) {
|
|
150
|
-
textStreams.push(shaka.util.PeriodCombiner.
|
|
155
|
+
textStreams.push(shaka.util.PeriodCombiner.dummyStream_(
|
|
156
|
+
ContentType.TEXT));
|
|
157
|
+
}
|
|
158
|
+
for (const imageStreams of imageStreamsPerPeriod) {
|
|
159
|
+
imageStreams.push(shaka.util.PeriodCombiner.dummyStream_(
|
|
160
|
+
ContentType.IMAGE));
|
|
151
161
|
}
|
|
152
162
|
|
|
153
163
|
await shaka.util.PeriodCombiner.combine_(
|
|
@@ -342,6 +352,36 @@ shaka.util.PeriodCombiner = class {
|
|
|
342
352
|
}
|
|
343
353
|
}
|
|
344
354
|
|
|
355
|
+
/**
|
|
356
|
+
* @param {!Array.<shaka.util.PeriodCombiner.Period>} periods
|
|
357
|
+
* @private
|
|
358
|
+
*/
|
|
359
|
+
static filterOutImageStreamDuplicates_(periods) {
|
|
360
|
+
// Two image streams are considered to be duplicates of
|
|
361
|
+
// one another if their ids are different, but all the other
|
|
362
|
+
// information is the same.
|
|
363
|
+
for (const period of periods) {
|
|
364
|
+
const filteredImages = [];
|
|
365
|
+
for (const i1 of period.imageStreams) {
|
|
366
|
+
let duplicate = false;
|
|
367
|
+
for (const i2 of filteredImages) {
|
|
368
|
+
if (i1.id != i2.id &&
|
|
369
|
+
i1.width == i2.width &&
|
|
370
|
+
i1.codecs == i2.codecs &&
|
|
371
|
+
i1.mimeType == i2.mimeType) {
|
|
372
|
+
duplicate = true;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (!duplicate) {
|
|
377
|
+
filteredImages.push(i1);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
period.imageStreams = filteredImages;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
345
385
|
/**
|
|
346
386
|
* Stitch together DB streams across periods, taking a mix of stream types.
|
|
347
387
|
* The offline database does not separate these by type.
|
|
@@ -368,13 +408,21 @@ shaka.util.PeriodCombiner = class {
|
|
|
368
408
|
(streams) => streams.filter((s) => s.type == ContentType.VIDEO));
|
|
369
409
|
const textStreamDbsPerPeriod = streamDbsPerPeriod.map(
|
|
370
410
|
(streams) => streams.filter((s) => s.type == ContentType.TEXT));
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
// period
|
|
375
|
-
//
|
|
411
|
+
const imageStreamDbsPerPeriod = streamDbsPerPeriod.map(
|
|
412
|
+
(streams) => streams.filter((s) => s.type == ContentType.IMAGE));
|
|
413
|
+
|
|
414
|
+
// It's okay to have a period with no text or images, but our algorithm
|
|
415
|
+
// fails on any period without matching streams. So we add dummy streams to
|
|
416
|
+
// each period. Since we combine text streams by language and image streams
|
|
417
|
+
// by resolution, we might need a dummy even in periods with these streams
|
|
418
|
+
// already.
|
|
376
419
|
for (const textStreams of textStreamDbsPerPeriod) {
|
|
377
|
-
textStreams.push(shaka.util.PeriodCombiner.
|
|
420
|
+
textStreams.push(shaka.util.PeriodCombiner.dummyStreamDB_(
|
|
421
|
+
ContentType.TEXT));
|
|
422
|
+
}
|
|
423
|
+
for (const imageStreams of imageStreamDbsPerPeriod) {
|
|
424
|
+
imageStreams.push(shaka.util.PeriodCombiner.dummyStreamDB_(
|
|
425
|
+
ContentType.IMAGE));
|
|
378
426
|
}
|
|
379
427
|
|
|
380
428
|
const combinedAudioStreamDbs = await shaka.util.PeriodCombiner.combine_(
|
|
@@ -398,6 +446,13 @@ shaka.util.PeriodCombiner = class {
|
|
|
398
446
|
shaka.util.PeriodCombiner.cloneStreamDB_,
|
|
399
447
|
shaka.util.PeriodCombiner.concatenateStreamDBs_);
|
|
400
448
|
|
|
449
|
+
const combinedImageStreamDbs = await shaka.util.PeriodCombiner.combine_(
|
|
450
|
+
/* outputStreams= */ [],
|
|
451
|
+
imageStreamDbsPerPeriod,
|
|
452
|
+
/* firstNewPeriodIndex= */ 0,
|
|
453
|
+
shaka.util.PeriodCombiner.cloneStreamDB_,
|
|
454
|
+
shaka.util.PeriodCombiner.concatenateStreamDBs_);
|
|
455
|
+
|
|
401
456
|
// Recreate variantIds from scratch in the output.
|
|
402
457
|
// HLS content is always single-period, so the early return at the top of
|
|
403
458
|
// this method would catch all HLS content. DASH content stored with v3.0
|
|
@@ -426,7 +481,8 @@ shaka.util.PeriodCombiner = class {
|
|
|
426
481
|
|
|
427
482
|
return combinedVideoStreamDbs
|
|
428
483
|
.concat(combinedAudioStreamDbs)
|
|
429
|
-
.concat(combinedTextStreamDbs)
|
|
484
|
+
.concat(combinedTextStreamDbs)
|
|
485
|
+
.concat(combinedImageStreamDbs);
|
|
430
486
|
}
|
|
431
487
|
|
|
432
488
|
/**
|
|
@@ -506,9 +562,12 @@ shaka.util.PeriodCombiner = class {
|
|
|
506
562
|
|
|
507
563
|
for (const unusedStreams of unusedStreamsPerPeriod) {
|
|
508
564
|
for (const stream of unusedStreams) {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
565
|
+
const isDummyText = stream.type == ContentType.TEXT && !stream.language;
|
|
566
|
+
const isDummyImage = stream.type == ContentType.IMAGE &&
|
|
567
|
+
!stream.tilesLayout;
|
|
568
|
+
if (isDummyText || isDummyImage) {
|
|
569
|
+
// This is one of our dummy streams, so ignore it. We may not use
|
|
570
|
+
// them all, and that's fine.
|
|
512
571
|
continue;
|
|
513
572
|
}
|
|
514
573
|
// If this stream has a different codec/MIME than any other stream,
|
|
@@ -525,6 +584,8 @@ shaka.util.PeriodCombiner = class {
|
|
|
525
584
|
|
|
526
585
|
// Any other unused stream is likely a bug in our algorithm, so throw
|
|
527
586
|
// an error.
|
|
587
|
+
shaka.log.error('Unused stream in period-flattening!',
|
|
588
|
+
stream, outputStreams);
|
|
528
589
|
throw new shaka.util.Error(
|
|
529
590
|
shaka.util.Error.Severity.CRITICAL,
|
|
530
591
|
shaka.util.Error.Category.MANIFEST,
|
|
@@ -563,6 +624,8 @@ shaka.util.PeriodCombiner = class {
|
|
|
563
624
|
|
|
564
625
|
if (!matches) {
|
|
565
626
|
// We were unable to extend this output stream.
|
|
627
|
+
shaka.log.error('No matches extending output stream!',
|
|
628
|
+
outputStream, streamsPerPeriod);
|
|
566
629
|
return false;
|
|
567
630
|
}
|
|
568
631
|
|
|
@@ -573,7 +636,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
573
636
|
|
|
574
637
|
// We need to create all the per-period segment indexes and append them to
|
|
575
638
|
// the output's MetaSegmentIndex.
|
|
576
|
-
await
|
|
639
|
+
await shaka.util.PeriodCombiner.createSegmentIndexes_(matches);
|
|
577
640
|
|
|
578
641
|
// Assure the compiler that matches didn't become null during the async
|
|
579
642
|
// operation above.
|
|
@@ -585,6 +648,25 @@ shaka.util.PeriodCombiner = class {
|
|
|
585
648
|
return true;
|
|
586
649
|
}
|
|
587
650
|
|
|
651
|
+
/**
|
|
652
|
+
* Creates all segment indexes for an array of streams. Returns once every
|
|
653
|
+
* segment index is created.
|
|
654
|
+
*
|
|
655
|
+
* @param {!Array.<!shaka.extern.Stream>} streams
|
|
656
|
+
* @return {!Promise}
|
|
657
|
+
* @private
|
|
658
|
+
*/
|
|
659
|
+
static createSegmentIndexes_(streams) {
|
|
660
|
+
const operations = [];
|
|
661
|
+
for (const stream of streams) {
|
|
662
|
+
operations.push(stream.createSegmentIndex());
|
|
663
|
+
if (stream.trickModeVideo && !stream.trickModeVideo.segmentIndex) {
|
|
664
|
+
operations.push(stream.trickModeVideo.createSegmentIndex());
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return Promise.all(operations);
|
|
668
|
+
}
|
|
669
|
+
|
|
588
670
|
/**
|
|
589
671
|
* Create a new output Stream based on a particular input Stream. Locates
|
|
590
672
|
* matching Streams in all other periods and combines them into an output
|
|
@@ -627,7 +709,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
627
709
|
if (outputStream.createSegmentIndex) {
|
|
628
710
|
// For T == Stream, we need to create all the per-period segment indexes
|
|
629
711
|
// in advance. concat() will add them to the output's MetaSegmentIndex.
|
|
630
|
-
await
|
|
712
|
+
await shaka.util.PeriodCombiner.createSegmentIndexes_(matches);
|
|
631
713
|
}
|
|
632
714
|
|
|
633
715
|
// Assure the compiler that matches didn't become null during the async
|
|
@@ -1047,7 +1129,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
1047
1129
|
return true;
|
|
1048
1130
|
}
|
|
1049
1131
|
|
|
1050
|
-
// Otherwise, compare the streams' characteristics to
|
|
1132
|
+
// Otherwise, compare the streams' characteristics to determine the best
|
|
1051
1133
|
// match.
|
|
1052
1134
|
|
|
1053
1135
|
// The most important thing is language. In some cases, we will accept a
|
|
@@ -1145,6 +1227,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
1145
1227
|
}
|
|
1146
1228
|
}
|
|
1147
1229
|
|
|
1230
|
+
// If the result of each comparison was inconclusive, default to false.
|
|
1148
1231
|
return false;
|
|
1149
1232
|
}
|
|
1150
1233
|
|
|
@@ -1169,7 +1252,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
1169
1252
|
return true;
|
|
1170
1253
|
}
|
|
1171
1254
|
|
|
1172
|
-
// Otherwise, compare the streams' characteristics to
|
|
1255
|
+
// Otherwise, compare the streams' characteristics to determine the best
|
|
1173
1256
|
// match.
|
|
1174
1257
|
|
|
1175
1258
|
// Take the video with the closest resolution to the output.
|
|
@@ -1215,6 +1298,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
1215
1298
|
}
|
|
1216
1299
|
}
|
|
1217
1300
|
|
|
1301
|
+
// If the result of each comparison was inconclusive, default to false.
|
|
1218
1302
|
return false;
|
|
1219
1303
|
}
|
|
1220
1304
|
|
|
@@ -1239,7 +1323,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
1239
1323
|
return true;
|
|
1240
1324
|
}
|
|
1241
1325
|
|
|
1242
|
-
// Otherwise, compare the streams' characteristics to
|
|
1326
|
+
// Otherwise, compare the streams' characteristics to determine the best
|
|
1243
1327
|
// match.
|
|
1244
1328
|
|
|
1245
1329
|
// The most important thing is language. In some cases, we will accept a
|
|
@@ -1300,6 +1384,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
1300
1384
|
return true;
|
|
1301
1385
|
}
|
|
1302
1386
|
|
|
1387
|
+
// If the result of each comparison was inconclusive, default to false.
|
|
1303
1388
|
return false;
|
|
1304
1389
|
}
|
|
1305
1390
|
|
|
@@ -1315,6 +1400,8 @@ shaka.util.PeriodCombiner = class {
|
|
|
1315
1400
|
* @private
|
|
1316
1401
|
*/
|
|
1317
1402
|
static isImageStreamBetterMatch_(outputStream, best, candidate) {
|
|
1403
|
+
const {BETTER, EQUAL, WORSE} = shaka.util.PeriodCombiner.BetterOrWorse;
|
|
1404
|
+
|
|
1318
1405
|
// If the output stream was based on the candidate stream, the candidate
|
|
1319
1406
|
// stream should be considered a better match. We can check this by
|
|
1320
1407
|
// comparing their ids.
|
|
@@ -1322,31 +1409,38 @@ shaka.util.PeriodCombiner = class {
|
|
|
1322
1409
|
return true;
|
|
1323
1410
|
}
|
|
1324
1411
|
|
|
1325
|
-
//
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1412
|
+
// Take the image with the closest resolution to the output.
|
|
1413
|
+
const resolutionBetterOrWorse =
|
|
1414
|
+
shaka.util.PeriodCombiner.compareClosestPreferLower(
|
|
1415
|
+
outputStream.width * outputStream.height,
|
|
1416
|
+
best.width * best.height,
|
|
1417
|
+
candidate.width * candidate.height);
|
|
1418
|
+
if (resolutionBetterOrWorse == BETTER) {
|
|
1329
1419
|
return true;
|
|
1420
|
+
} else if (resolutionBetterOrWorse == WORSE) {
|
|
1421
|
+
return false;
|
|
1330
1422
|
}
|
|
1331
1423
|
|
|
1424
|
+
// If the result of each comparison was inconclusive, default to false.
|
|
1332
1425
|
return false;
|
|
1333
1426
|
}
|
|
1334
1427
|
|
|
1335
1428
|
/**
|
|
1336
|
-
* Create a dummy
|
|
1337
|
-
* failing the general flattening algorithm.
|
|
1429
|
+
* Create a dummy StreamDB to fill in periods that are missing a certain type,
|
|
1430
|
+
* to avoid failing the general flattening algorithm. This won't be used for
|
|
1431
|
+
* audio or video, since those are strictly required in all periods if they
|
|
1432
|
+
* exist in any period.
|
|
1338
1433
|
*
|
|
1434
|
+
* @param {shaka.util.ManifestParserUtils.ContentType} type
|
|
1339
1435
|
* @return {shaka.extern.StreamDB}
|
|
1340
1436
|
* @private
|
|
1341
1437
|
*/
|
|
1342
|
-
static
|
|
1343
|
-
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
1344
|
-
|
|
1438
|
+
static dummyStreamDB_(type) {
|
|
1345
1439
|
return {
|
|
1346
1440
|
id: 0,
|
|
1347
1441
|
originalId: '',
|
|
1348
1442
|
primary: false,
|
|
1349
|
-
type
|
|
1443
|
+
type,
|
|
1350
1444
|
mimeType: '',
|
|
1351
1445
|
codecs: '',
|
|
1352
1446
|
language: '',
|
|
@@ -1367,15 +1461,16 @@ shaka.util.PeriodCombiner = class {
|
|
|
1367
1461
|
}
|
|
1368
1462
|
|
|
1369
1463
|
/**
|
|
1370
|
-
* Create a dummy
|
|
1371
|
-
* failing the general flattening algorithm.
|
|
1464
|
+
* Create a dummy Stream to fill in periods that are missing a certain type,
|
|
1465
|
+
* to avoid failing the general flattening algorithm. This won't be used for
|
|
1466
|
+
* audio or video, since those are strictly required in all periods if they
|
|
1467
|
+
* exist in any period.
|
|
1372
1468
|
*
|
|
1469
|
+
* @param {shaka.util.ManifestParserUtils.ContentType} type
|
|
1373
1470
|
* @return {shaka.extern.Stream}
|
|
1374
1471
|
* @private
|
|
1375
1472
|
*/
|
|
1376
|
-
static
|
|
1377
|
-
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
1378
|
-
|
|
1473
|
+
static dummyStream_(type) {
|
|
1379
1474
|
return {
|
|
1380
1475
|
id: 0,
|
|
1381
1476
|
originalId: '',
|
|
@@ -1388,7 +1483,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
1388
1483
|
keyIds: new Set(),
|
|
1389
1484
|
language: '',
|
|
1390
1485
|
label: null,
|
|
1391
|
-
type
|
|
1486
|
+
type,
|
|
1392
1487
|
primary: false,
|
|
1393
1488
|
trickModeVideo: null,
|
|
1394
1489
|
emsgSchemeIdUris: null,
|
package/lib/util/platform.js
CHANGED
|
@@ -163,7 +163,6 @@ shaka.util.Platform = class {
|
|
|
163
163
|
!shaka.util.Platform.isEdge();
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
|
|
167
166
|
/**
|
|
168
167
|
* Check if the current platform is from Apple.
|
|
169
168
|
*
|
|
@@ -179,6 +178,19 @@ shaka.util.Platform = class {
|
|
|
179
178
|
&& !shaka.util.Platform.isTizen();
|
|
180
179
|
}
|
|
181
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Check if the current platform is Playstation 5.
|
|
183
|
+
*
|
|
184
|
+
* Returns true on Playstation 5 browsers.
|
|
185
|
+
*
|
|
186
|
+
* Returns false for Playstation 5 browsers
|
|
187
|
+
*
|
|
188
|
+
* @return {boolean}
|
|
189
|
+
*/
|
|
190
|
+
static isPS5() {
|
|
191
|
+
return shaka.util.Platform.userAgentContains_('PlayStation 5');
|
|
192
|
+
}
|
|
193
|
+
|
|
182
194
|
/**
|
|
183
195
|
* Returns a major version number for Safari, or Safari-based iOS browsers.
|
|
184
196
|
*
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -315,7 +315,9 @@ shaka.util.StreamUtils = class {
|
|
|
315
315
|
variant);
|
|
316
316
|
return false;
|
|
317
317
|
}
|
|
318
|
+
return true;
|
|
318
319
|
}
|
|
320
|
+
|
|
319
321
|
shaka.log.debug('DrmEngine is not initialized yet.');
|
|
320
322
|
return true;
|
|
321
323
|
});
|
|
@@ -331,12 +333,23 @@ shaka.util.StreamUtils = class {
|
|
|
331
333
|
*/
|
|
332
334
|
static async filterManifestByMediaCapabilities(
|
|
333
335
|
manifest, usePersistentLicenses) {
|
|
334
|
-
goog.asserts.assert(
|
|
336
|
+
goog.asserts.assert(window.shakaMediaCapabilities,
|
|
335
337
|
'MediaCapabilities should be valid.');
|
|
336
338
|
|
|
337
339
|
await shaka.util.StreamUtils.getDecodingInfosForVariants(
|
|
338
|
-
manifest.variants, usePersistentLicenses);
|
|
340
|
+
manifest.variants, usePersistentLicenses, /* srcEquals= */ false);
|
|
339
341
|
manifest.variants = manifest.variants.filter((variant) => {
|
|
342
|
+
const video = variant.video;
|
|
343
|
+
// See: https://github.com/google/shaka-player/issues/3380
|
|
344
|
+
if (shaka.util.Platform.isXboxOne() && video &&
|
|
345
|
+
((video.width && video.width > 1920) ||
|
|
346
|
+
(video.height && video.height > 1080)) &&
|
|
347
|
+
video.codecs.includes('avc1.')) {
|
|
348
|
+
shaka.log.debug('Dropping variant - not compatible with platform',
|
|
349
|
+
shaka.util.StreamUtils.getVariantSummaryString_(variant));
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
|
|
340
353
|
const supported = variant.decodingInfos.some((decodingInfo) => {
|
|
341
354
|
return decodingInfo.supported;
|
|
342
355
|
});
|
|
@@ -375,6 +388,16 @@ shaka.util.StreamUtils = class {
|
|
|
375
388
|
return false;
|
|
376
389
|
}
|
|
377
390
|
|
|
391
|
+
// See: https://github.com/google/shaka-player/issues/3380
|
|
392
|
+
if (shaka.util.Platform.isXboxOne() && video &&
|
|
393
|
+
((video.width && video.width > 1920) ||
|
|
394
|
+
(video.height && video.height > 1080)) &&
|
|
395
|
+
video.codecs.includes('avc1.')) {
|
|
396
|
+
shaka.log.debug('Dropping variant - video not compatible with platform',
|
|
397
|
+
StreamUtils.getStreamSummaryString_(video));
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
|
|
378
401
|
return true;
|
|
379
402
|
});
|
|
380
403
|
}
|
|
@@ -387,9 +410,11 @@ shaka.util.StreamUtils = class {
|
|
|
387
410
|
*
|
|
388
411
|
* @param {!Array.<shaka.extern.Variant>} variants
|
|
389
412
|
* @param {boolean} usePersistentLicenses
|
|
413
|
+
* @param {boolean} srcEquals
|
|
390
414
|
* @exportDoc
|
|
391
415
|
*/
|
|
392
|
-
static async getDecodingInfosForVariants(variants, usePersistentLicenses
|
|
416
|
+
static async getDecodingInfosForVariants(variants, usePersistentLicenses,
|
|
417
|
+
srcEquals) {
|
|
393
418
|
const gotDecodingInfo = variants.some((variant) =>
|
|
394
419
|
variant.decodingInfos.length);
|
|
395
420
|
if (gotDecodingInfo) {
|
|
@@ -397,7 +422,7 @@ shaka.util.StreamUtils = class {
|
|
|
397
422
|
return;
|
|
398
423
|
}
|
|
399
424
|
|
|
400
|
-
const mediaCapabilities =
|
|
425
|
+
const mediaCapabilities = window.shakaMediaCapabilities;
|
|
401
426
|
|
|
402
427
|
const operations = [];
|
|
403
428
|
const getVariantDecodingInfos = (async (variant, decodingConfig) => {
|
|
@@ -413,7 +438,7 @@ shaka.util.StreamUtils = class {
|
|
|
413
438
|
for (const variant of variants) {
|
|
414
439
|
/** @type {!Array.<!MediaDecodingConfiguration>} */
|
|
415
440
|
const decodingConfigs = shaka.util.StreamUtils.getDecodingConfigs_(
|
|
416
|
-
variant, usePersistentLicenses);
|
|
441
|
+
variant, usePersistentLicenses, srcEquals);
|
|
417
442
|
|
|
418
443
|
for (const config of decodingConfigs) {
|
|
419
444
|
operations.push(getVariantDecodingInfos(variant, config));
|
|
@@ -428,17 +453,18 @@ shaka.util.StreamUtils = class {
|
|
|
428
453
|
* results for each variant.
|
|
429
454
|
* @param {!shaka.extern.Variant} variant
|
|
430
455
|
* @param {boolean} usePersistentLicenses
|
|
456
|
+
* @param {boolean} srcEquals
|
|
431
457
|
* @return {!Array.<!MediaDecodingConfiguration>}
|
|
432
458
|
* @private
|
|
433
459
|
*/
|
|
434
|
-
static getDecodingConfigs_(variant, usePersistentLicenses) {
|
|
460
|
+
static getDecodingConfigs_(variant, usePersistentLicenses, srcEquals) {
|
|
435
461
|
const audio = variant.audio;
|
|
436
462
|
const video = variant.video;
|
|
437
463
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
438
464
|
|
|
439
465
|
/** @type {!MediaDecodingConfiguration} */
|
|
440
466
|
const mediaDecodingConfig = {
|
|
441
|
-
type: 'media-source',
|
|
467
|
+
type: srcEquals ? 'file' : 'media-source',
|
|
442
468
|
};
|
|
443
469
|
|
|
444
470
|
if (video) {
|
|
@@ -475,6 +501,19 @@ shaka.util.StreamUtils = class {
|
|
|
475
501
|
// framerate must be greater than 0, otherwise the config is invalid.
|
|
476
502
|
framerate: video.frameRate || 1,
|
|
477
503
|
};
|
|
504
|
+
if (video.hdr) {
|
|
505
|
+
switch (video.hdr) {
|
|
506
|
+
case 'SDR':
|
|
507
|
+
mediaDecodingConfig.video.transferFunction = 'srgb';
|
|
508
|
+
break;
|
|
509
|
+
case 'PQ':
|
|
510
|
+
mediaDecodingConfig.video.transferFunction = 'pq';
|
|
511
|
+
break;
|
|
512
|
+
case 'HLG':
|
|
513
|
+
mediaDecodingConfig.video.transferFunction = 'hlg';
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
478
517
|
}
|
|
479
518
|
if (audio) {
|
|
480
519
|
// Some Tizen devices seem to misreport AC-3 support, but correctly
|
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": "3.1.
|
|
4
|
+
"version": "3.1.4",
|
|
5
5
|
"homepage": "https://github.com/google/shaka-player",
|
|
6
6
|
"author": "Google",
|
|
7
7
|
"maintainers": [
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"esprima": "^4.0.1",
|
|
29
29
|
"fastestsmallesttextencoderdecoder": "^1.0.22",
|
|
30
30
|
"fontfaceonload": "^1.0.2",
|
|
31
|
-
"google-closure-compiler-java": "^
|
|
32
|
-
"google-closure-library": "^
|
|
31
|
+
"google-closure-compiler-java": "^20210505.0.0",
|
|
32
|
+
"google-closure-library": "^20210406.0.0",
|
|
33
33
|
"htmlhint": "^0.11.0",
|
|
34
34
|
"jasmine-ajax": "^4.0.0",
|
|
35
35
|
"jimp": "^0.11.0",
|
|
@@ -297,7 +297,8 @@ describe('CastProxy', () => {
|
|
|
297
297
|
'timeupdate', Util.spyFunc(proxyListener));
|
|
298
298
|
|
|
299
299
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
300
|
-
const fakeEvent = new FakeEvent(
|
|
300
|
+
const fakeEvent = new FakeEvent(
|
|
301
|
+
'timeupdate', (new Map()).set('detail', 8675309));
|
|
301
302
|
mockVideo.on['timeupdate'](fakeEvent);
|
|
302
303
|
expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
|
|
303
304
|
type: 'timeupdate',
|
|
@@ -315,7 +316,8 @@ describe('CastProxy', () => {
|
|
|
315
316
|
mockSender.hasRemoteProperties.and.returnValue(true);
|
|
316
317
|
|
|
317
318
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
318
|
-
const fakeEvent = new FakeEvent(
|
|
319
|
+
const fakeEvent = new FakeEvent(
|
|
320
|
+
'timeupdate', (new Map()).set('detail', 8675309));
|
|
319
321
|
mockVideo.on['timeupdate'](fakeEvent);
|
|
320
322
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
321
323
|
});
|
|
@@ -332,7 +334,8 @@ describe('CastProxy', () => {
|
|
|
332
334
|
mockSender.hasRemoteProperties.and.returnValue(true);
|
|
333
335
|
|
|
334
336
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
335
|
-
const fakeEvent = new FakeEvent(
|
|
337
|
+
const fakeEvent = new FakeEvent(
|
|
338
|
+
'timeupdate', (new Map()).set('detail', 8675309));
|
|
336
339
|
mockSender.onRemoteEvent('video', fakeEvent);
|
|
337
340
|
expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
|
|
338
341
|
type: 'timeupdate',
|
|
@@ -446,7 +449,8 @@ describe('CastProxy', () => {
|
|
|
446
449
|
'buffering', Util.spyFunc(proxyListener));
|
|
447
450
|
|
|
448
451
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
449
|
-
const fakeEvent = new FakeEvent(
|
|
452
|
+
const fakeEvent = new FakeEvent(
|
|
453
|
+
'buffering', (new Map()).set('detail', 8675309));
|
|
450
454
|
mockPlayer.listeners['buffering'](fakeEvent);
|
|
451
455
|
expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
|
|
452
456
|
type: 'buffering',
|
|
@@ -464,7 +468,8 @@ describe('CastProxy', () => {
|
|
|
464
468
|
mockSender.hasRemoteProperties.and.returnValue(true);
|
|
465
469
|
|
|
466
470
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
467
|
-
const fakeEvent = new FakeEvent(
|
|
471
|
+
const fakeEvent = new FakeEvent(
|
|
472
|
+
'buffering', (new Map()).set('detail', 8675309));
|
|
468
473
|
mockPlayer.listeners['buffering'](fakeEvent);
|
|
469
474
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
470
475
|
});
|
|
@@ -481,7 +486,8 @@ describe('CastProxy', () => {
|
|
|
481
486
|
mockSender.hasRemoteProperties.and.returnValue(true);
|
|
482
487
|
|
|
483
488
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
484
|
-
const fakeEvent = new FakeEvent(
|
|
489
|
+
const fakeEvent = new FakeEvent(
|
|
490
|
+
'buffering', (new Map()).set('detail', 8675309));
|
|
485
491
|
mockSender.onRemoteEvent('player', fakeEvent);
|
|
486
492
|
expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
|
|
487
493
|
type: 'buffering',
|
|
@@ -170,7 +170,7 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
170
170
|
// Check that the update message is of a reasonable size. From previous
|
|
171
171
|
// testing we found that the socket would silently reject data that got
|
|
172
172
|
// too big. 6KB is safely below the limit.
|
|
173
|
-
expect(message.length).toBeLessThan(
|
|
173
|
+
expect(message.length).toBeLessThan(7 * 1024);
|
|
174
174
|
});
|
|
175
175
|
|
|
176
176
|
drmIt('has reasonable average message size', async () => {
|
|
@@ -122,7 +122,7 @@ describe('CastUtils', () => {
|
|
|
122
122
|
expect(typeof deserialized).toBe('object');
|
|
123
123
|
|
|
124
124
|
// The object can be used to construct a FakeEvent.
|
|
125
|
-
const fakeEvent =
|
|
125
|
+
const fakeEvent = FakeEvent.fromRealEvent(deserialized);
|
|
126
126
|
|
|
127
127
|
// The fake event has the same type and properties as the original.
|
|
128
128
|
const asObj = /** @type {!Object} */ (fakeEvent);
|
|
@@ -27,6 +27,32 @@ describe('Mp4CeaParser', () => {
|
|
|
27
27
|
ceaSegment = responses[1];
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Test only the functionality of removing EPB
|
|
32
|
+
* Expect removeEmu() to return the NALU with correct length
|
|
33
|
+
*
|
|
34
|
+
* Chromium VDA has a strict standard on NALU length
|
|
35
|
+
* It will complain about conformance if the array is malformed
|
|
36
|
+
*
|
|
37
|
+
* If EPB is removed by shifting bytes, it will return the original NALU
|
|
38
|
+
* length, which will fail this test
|
|
39
|
+
*
|
|
40
|
+
* Note that the CEA-608 packet in this test is incomplete
|
|
41
|
+
*/
|
|
42
|
+
it('parses CEA-608 SEI data from MP4 H.264 stream', () => {
|
|
43
|
+
const seiProcessor = new shaka.cea.SeiProcessor();
|
|
44
|
+
|
|
45
|
+
const cea608Packet = new Uint8Array([
|
|
46
|
+
0x00, 0x00, 0x03, // Emulation prevention byte
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
const naluData = seiProcessor.removeEmu(cea608Packet);
|
|
50
|
+
expect(naluData).toBeDefined();
|
|
51
|
+
|
|
52
|
+
// EPB should be removed by returning new array, not by shifting bytes
|
|
53
|
+
expect(naluData.length).toBe(2);
|
|
54
|
+
});
|
|
55
|
+
|
|
30
56
|
it('parses cea data from mp4 stream', () => {
|
|
31
57
|
const cea708Parser = new shaka.cea.Mp4CeaParser();
|
|
32
58
|
|
|
@@ -276,6 +276,7 @@ describe('DashParser Live', () => {
|
|
|
276
276
|
ref.timestampOffset = pStart;
|
|
277
277
|
ref.startTime += pStart;
|
|
278
278
|
ref.endTime += pStart;
|
|
279
|
+
ref.trueEndTime += pStart;
|
|
279
280
|
}
|
|
280
281
|
/** @const {!Array.<!shaka.media.SegmentReference>} */
|
|
281
282
|
const allRefs = period1Refs.concat(period2Refs);
|