shaka-player 3.1.1 → 3.1.5
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 +7 -2
- package/CHANGELOG.md +199 -0
- package/CONTRIBUTORS +9 -3
- 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/assets.js +1 -1
- package/dist/controls.css +2 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +1 -1
- package/dist/demo.compiled.js +1 -1
- package/dist/deps.js +7 -7
- package/dist/locales.js +35 -33
- package/dist/receiver.compiled.debug.js +1 -1
- package/dist/receiver.compiled.js +1 -1
- package/dist/shaka-player.compiled.d.ts +171 -3
- package/dist/shaka-player.compiled.debug.d.ts +171 -3
- package/dist/shaka-player.compiled.debug.externs.js +179 -1
- package/dist/shaka-player.compiled.debug.js +365 -344
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +179 -1
- package/dist/shaka-player.compiled.js +806 -798
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +171 -3
- package/dist/shaka-player.ui.debug.d.ts +171 -3
- package/dist/shaka-player.ui.debug.externs.js +181 -3
- package/dist/shaka-player.ui.debug.js +458 -435
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +181 -3
- package/dist/shaka-player.ui.js +927 -918
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/fairplay.md +1 -1
- package/docs/tutorials/faq.md +14 -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/shaka/ads.js +2 -0
- package/externs/shaka/player.js +6 -0
- package/externs/shaka/text.js +10 -0
- package/karma.conf.js +1 -0
- 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 +100 -5
- package/lib/dash/segment_list.js +1 -1
- package/lib/dash/segment_template.js +7 -4
- package/lib/hls/hls_parser.js +44 -17
- package/lib/media/closed_caption_parser.js +3 -1
- package/lib/media/content_workarounds.js +12 -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_reference.js +8 -0
- package/lib/media/streaming_engine.js +28 -19
- package/lib/net/networking_engine.js +2 -1
- package/lib/player.js +108 -54
- 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 +76 -22
- 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 +17 -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 +135 -87
- package/lib/text/ui_text_displayer.js +233 -83
- package/lib/text/vtt_text_parser.js +4 -2
- package/lib/util/fake_event.js +53 -7
- package/lib/util/mp4_box_parsers.js +1 -1
- package/lib/util/periods.js +111 -35
- package/lib/util/platform.js +13 -1
- package/lib/util/stream_utils.js +79 -10
- package/lib/util/xml_utils.js +19 -0
- package/package.json +2 -2
- 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 +220 -2
- package/test/dash/dash_parser_segment_template_unit.js +3 -3
- package/test/hls/hls_parser_unit.js +5 -1
- package/test/media/closed_caption_parser_unit.js +20 -0
- package/test/media/drm_engine_unit.js +56 -0
- package/test/media/playhead_unit.js +27 -1
- package/test/media/presentation_timeline_unit.js +19 -0
- package/test/media/streaming_engine_integration.js +2 -1
- package/test/media/streaming_engine_unit.js +148 -3
- package/test/player_integration.js +38 -0
- package/test/player_unit.js +101 -0
- 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/ttml_utils.js +81 -0
- package/test/text/mp4_ttml_parser_unit.js +11 -18
- package/test/text/ttml_text_parser_unit.js +542 -220
- 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/util/mp4_box_parsers_unit.js +28 -0
- package/test/util/periods_unit.js +190 -2
- package/test/util/stream_utils_unit.js +59 -2
- 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 +8 -6
- package/ui/less/buttons.less +0 -4
- 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/text_selection.js +1 -0
- 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
|
|
|
@@ -1066,7 +1129,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
1066
1129
|
return true;
|
|
1067
1130
|
}
|
|
1068
1131
|
|
|
1069
|
-
// Otherwise, compare the streams' characteristics to
|
|
1132
|
+
// Otherwise, compare the streams' characteristics to determine the best
|
|
1070
1133
|
// match.
|
|
1071
1134
|
|
|
1072
1135
|
// The most important thing is language. In some cases, we will accept a
|
|
@@ -1164,6 +1227,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
1164
1227
|
}
|
|
1165
1228
|
}
|
|
1166
1229
|
|
|
1230
|
+
// If the result of each comparison was inconclusive, default to false.
|
|
1167
1231
|
return false;
|
|
1168
1232
|
}
|
|
1169
1233
|
|
|
@@ -1188,7 +1252,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
1188
1252
|
return true;
|
|
1189
1253
|
}
|
|
1190
1254
|
|
|
1191
|
-
// Otherwise, compare the streams' characteristics to
|
|
1255
|
+
// Otherwise, compare the streams' characteristics to determine the best
|
|
1192
1256
|
// match.
|
|
1193
1257
|
|
|
1194
1258
|
// Take the video with the closest resolution to the output.
|
|
@@ -1234,6 +1298,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
1234
1298
|
}
|
|
1235
1299
|
}
|
|
1236
1300
|
|
|
1301
|
+
// If the result of each comparison was inconclusive, default to false.
|
|
1237
1302
|
return false;
|
|
1238
1303
|
}
|
|
1239
1304
|
|
|
@@ -1258,7 +1323,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
1258
1323
|
return true;
|
|
1259
1324
|
}
|
|
1260
1325
|
|
|
1261
|
-
// Otherwise, compare the streams' characteristics to
|
|
1326
|
+
// Otherwise, compare the streams' characteristics to determine the best
|
|
1262
1327
|
// match.
|
|
1263
1328
|
|
|
1264
1329
|
// The most important thing is language. In some cases, we will accept a
|
|
@@ -1319,6 +1384,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
1319
1384
|
return true;
|
|
1320
1385
|
}
|
|
1321
1386
|
|
|
1387
|
+
// If the result of each comparison was inconclusive, default to false.
|
|
1322
1388
|
return false;
|
|
1323
1389
|
}
|
|
1324
1390
|
|
|
@@ -1334,6 +1400,8 @@ shaka.util.PeriodCombiner = class {
|
|
|
1334
1400
|
* @private
|
|
1335
1401
|
*/
|
|
1336
1402
|
static isImageStreamBetterMatch_(outputStream, best, candidate) {
|
|
1403
|
+
const {BETTER, EQUAL, WORSE} = shaka.util.PeriodCombiner.BetterOrWorse;
|
|
1404
|
+
|
|
1337
1405
|
// If the output stream was based on the candidate stream, the candidate
|
|
1338
1406
|
// stream should be considered a better match. We can check this by
|
|
1339
1407
|
// comparing their ids.
|
|
@@ -1341,31 +1409,38 @@ shaka.util.PeriodCombiner = class {
|
|
|
1341
1409
|
return true;
|
|
1342
1410
|
}
|
|
1343
1411
|
|
|
1344
|
-
//
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
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) {
|
|
1348
1419
|
return true;
|
|
1420
|
+
} else if (resolutionBetterOrWorse == WORSE) {
|
|
1421
|
+
return false;
|
|
1349
1422
|
}
|
|
1350
1423
|
|
|
1424
|
+
// If the result of each comparison was inconclusive, default to false.
|
|
1351
1425
|
return false;
|
|
1352
1426
|
}
|
|
1353
1427
|
|
|
1354
1428
|
/**
|
|
1355
|
-
* Create a dummy
|
|
1356
|
-
* 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.
|
|
1357
1433
|
*
|
|
1434
|
+
* @param {shaka.util.ManifestParserUtils.ContentType} type
|
|
1358
1435
|
* @return {shaka.extern.StreamDB}
|
|
1359
1436
|
* @private
|
|
1360
1437
|
*/
|
|
1361
|
-
static
|
|
1362
|
-
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
1363
|
-
|
|
1438
|
+
static dummyStreamDB_(type) {
|
|
1364
1439
|
return {
|
|
1365
1440
|
id: 0,
|
|
1366
1441
|
originalId: '',
|
|
1367
1442
|
primary: false,
|
|
1368
|
-
type
|
|
1443
|
+
type,
|
|
1369
1444
|
mimeType: '',
|
|
1370
1445
|
codecs: '',
|
|
1371
1446
|
language: '',
|
|
@@ -1386,15 +1461,16 @@ shaka.util.PeriodCombiner = class {
|
|
|
1386
1461
|
}
|
|
1387
1462
|
|
|
1388
1463
|
/**
|
|
1389
|
-
* Create a dummy
|
|
1390
|
-
* 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.
|
|
1391
1468
|
*
|
|
1469
|
+
* @param {shaka.util.ManifestParserUtils.ContentType} type
|
|
1392
1470
|
* @return {shaka.extern.Stream}
|
|
1393
1471
|
* @private
|
|
1394
1472
|
*/
|
|
1395
|
-
static
|
|
1396
|
-
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
1397
|
-
|
|
1473
|
+
static dummyStream_(type) {
|
|
1398
1474
|
return {
|
|
1399
1475
|
id: 0,
|
|
1400
1476
|
originalId: '',
|
|
@@ -1407,7 +1483,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
1407
1483
|
keyIds: new Set(),
|
|
1408
1484
|
language: '',
|
|
1409
1485
|
label: null,
|
|
1410
|
-
type
|
|
1486
|
+
type,
|
|
1411
1487
|
primary: false,
|
|
1412
1488
|
trickModeVideo: null,
|
|
1413
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
|
});
|
|
@@ -335,9 +337,44 @@ shaka.util.StreamUtils = class {
|
|
|
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
|
+
// See: https://github.com/google/shaka-player/issues/3860
|
|
340
343
|
const video = variant.video;
|
|
344
|
+
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
345
|
+
if (video) {
|
|
346
|
+
let videoCodecs = shaka.util.StreamUtils.patchVp9(video.codecs);
|
|
347
|
+
// For multiplexed streams. Here we must check the audio of the
|
|
348
|
+
// stream to see if it is compatible.
|
|
349
|
+
if (video.codecs.includes(',')) {
|
|
350
|
+
const allCodecs = video.codecs.split(',');
|
|
351
|
+
videoCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
352
|
+
ContentType.VIDEO, allCodecs);
|
|
353
|
+
const audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
354
|
+
ContentType.AUDIO, allCodecs);
|
|
355
|
+
const audioFullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
356
|
+
video.mimeType, audioCodecs, ContentType.AUDIO);
|
|
357
|
+
if (!MediaSource.isTypeSupported(audioFullType)) {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
362
|
+
video.mimeType, videoCodecs, ContentType.VIDEO);
|
|
363
|
+
if (!MediaSource.isTypeSupported(fullType)) {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const audio = variant.audio;
|
|
368
|
+
if (audio) {
|
|
369
|
+
const codecs =
|
|
370
|
+
shaka.util.StreamUtils.getCorrectAudioCodecs_(audio.codecs);
|
|
371
|
+
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
372
|
+
audio.mimeType, codecs, ContentType.AUDIO);
|
|
373
|
+
if (!MediaSource.isTypeSupported(fullType)) {
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
341
378
|
// See: https://github.com/google/shaka-player/issues/3380
|
|
342
379
|
if (shaka.util.Platform.isXboxOne() && video &&
|
|
343
380
|
((video.width && video.width > 1920) ||
|
|
@@ -408,9 +445,11 @@ shaka.util.StreamUtils = class {
|
|
|
408
445
|
*
|
|
409
446
|
* @param {!Array.<shaka.extern.Variant>} variants
|
|
410
447
|
* @param {boolean} usePersistentLicenses
|
|
448
|
+
* @param {boolean} srcEquals
|
|
411
449
|
* @exportDoc
|
|
412
450
|
*/
|
|
413
|
-
static async getDecodingInfosForVariants(variants, usePersistentLicenses
|
|
451
|
+
static async getDecodingInfosForVariants(variants, usePersistentLicenses,
|
|
452
|
+
srcEquals) {
|
|
414
453
|
const gotDecodingInfo = variants.some((variant) =>
|
|
415
454
|
variant.decodingInfos.length);
|
|
416
455
|
if (gotDecodingInfo) {
|
|
@@ -434,7 +473,7 @@ shaka.util.StreamUtils = class {
|
|
|
434
473
|
for (const variant of variants) {
|
|
435
474
|
/** @type {!Array.<!MediaDecodingConfiguration>} */
|
|
436
475
|
const decodingConfigs = shaka.util.StreamUtils.getDecodingConfigs_(
|
|
437
|
-
variant, usePersistentLicenses);
|
|
476
|
+
variant, usePersistentLicenses, srcEquals);
|
|
438
477
|
|
|
439
478
|
for (const config of decodingConfigs) {
|
|
440
479
|
operations.push(getVariantDecodingInfos(variant, config));
|
|
@@ -449,17 +488,18 @@ shaka.util.StreamUtils = class {
|
|
|
449
488
|
* results for each variant.
|
|
450
489
|
* @param {!shaka.extern.Variant} variant
|
|
451
490
|
* @param {boolean} usePersistentLicenses
|
|
491
|
+
* @param {boolean} srcEquals
|
|
452
492
|
* @return {!Array.<!MediaDecodingConfiguration>}
|
|
453
493
|
* @private
|
|
454
494
|
*/
|
|
455
|
-
static getDecodingConfigs_(variant, usePersistentLicenses) {
|
|
495
|
+
static getDecodingConfigs_(variant, usePersistentLicenses, srcEquals) {
|
|
456
496
|
const audio = variant.audio;
|
|
457
497
|
const video = variant.video;
|
|
458
498
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
459
499
|
|
|
460
500
|
/** @type {!MediaDecodingConfiguration} */
|
|
461
501
|
const mediaDecodingConfig = {
|
|
462
|
-
type: 'media-source',
|
|
502
|
+
type: srcEquals ? 'file' : 'media-source',
|
|
463
503
|
};
|
|
464
504
|
|
|
465
505
|
if (video) {
|
|
@@ -496,14 +536,23 @@ shaka.util.StreamUtils = class {
|
|
|
496
536
|
// framerate must be greater than 0, otherwise the config is invalid.
|
|
497
537
|
framerate: video.frameRate || 1,
|
|
498
538
|
};
|
|
539
|
+
if (video.hdr) {
|
|
540
|
+
switch (video.hdr) {
|
|
541
|
+
case 'SDR':
|
|
542
|
+
mediaDecodingConfig.video.transferFunction = 'srgb';
|
|
543
|
+
break;
|
|
544
|
+
case 'PQ':
|
|
545
|
+
mediaDecodingConfig.video.transferFunction = 'pq';
|
|
546
|
+
break;
|
|
547
|
+
case 'HLG':
|
|
548
|
+
mediaDecodingConfig.video.transferFunction = 'hlg';
|
|
549
|
+
break;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
499
552
|
}
|
|
500
553
|
if (audio) {
|
|
501
|
-
// Some Tizen devices seem to misreport AC-3 support, but correctly
|
|
502
|
-
// report EC-3 support. So query EC-3 as a fallback for AC-3.
|
|
503
|
-
// See https://github.com/google/shaka-player/issues/2989 for details.
|
|
504
554
|
const codecs =
|
|
505
|
-
(audio.codecs
|
|
506
|
-
shaka.util.Platform.isTizen()) ? 'ec-3' : audio.codecs;
|
|
555
|
+
shaka.util.StreamUtils.getCorrectAudioCodecs_(audio.codecs);
|
|
507
556
|
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
508
557
|
audio.mimeType, codecs, ContentType.AUDIO);
|
|
509
558
|
|
|
@@ -614,6 +663,26 @@ shaka.util.StreamUtils = class {
|
|
|
614
663
|
return configs;
|
|
615
664
|
}
|
|
616
665
|
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Generates the correct audio codec for MediaDecodingConfiguration and
|
|
669
|
+
* for MediaSource.isTypeSupported.
|
|
670
|
+
* @param {string} codecs
|
|
671
|
+
* @return {string}
|
|
672
|
+
* @private
|
|
673
|
+
*/
|
|
674
|
+
static getCorrectAudioCodecs_(codecs) {
|
|
675
|
+
// Some Tizen devices seem to misreport AC-3 support, but correctly
|
|
676
|
+
// report EC-3 support. So query EC-3 as a fallback for AC-3.
|
|
677
|
+
// See https://github.com/google/shaka-player/issues/2989 for details.
|
|
678
|
+
if (shaka.util.Platform.isTizen()) {
|
|
679
|
+
return codecs.toLowerCase() == 'ac-3' ? 'ec-3' : codecs;
|
|
680
|
+
} else {
|
|
681
|
+
return codecs;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
|
|
617
686
|
/**
|
|
618
687
|
* MediaCapabilities supports 'vp09...' codecs, but not 'vp9'. Translate vp9
|
|
619
688
|
* codec strings into 'vp09...', to allow such content to play with
|
package/lib/util/xml_utils.js
CHANGED
|
@@ -102,6 +102,25 @@ shaka.util.XmlUtils = class {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Gets a namespace-qualified attribute.
|
|
107
|
+
* @param {!Element} elem The element to get from.
|
|
108
|
+
* @param {!Array.<string>} nsList The lis of namespace URIs.
|
|
109
|
+
* @param {string} name The local name of the attribute.
|
|
110
|
+
* @return {?string} The attribute's value, or null if not present.
|
|
111
|
+
*/
|
|
112
|
+
static getAttributeNSList(elem, nsList, name) {
|
|
113
|
+
// Some browsers return the empty string when the attribute is missing,
|
|
114
|
+
// so check if it exists first. See: https://mzl.la/2L7F0UK
|
|
115
|
+
for (const ns of nsList) {
|
|
116
|
+
if (elem.hasAttributeNS(ns, name)) {
|
|
117
|
+
return elem.getAttributeNS(ns, name);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
105
124
|
/**
|
|
106
125
|
* Gets the text contents of a node.
|
|
107
126
|
* @param {!Node} elem The XML element.
|
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.5",
|
|
5
5
|
"homepage": "https://github.com/google/shaka-player",
|
|
6
6
|
"author": "Google",
|
|
7
7
|
"maintainers": [
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
],
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"@chiragrupani/karma-chromium-edge-launcher": "^2.1.0",
|
|
15
|
+
"@onslip/karma-safari-launcher": "^1.0.1",
|
|
15
16
|
"@teppeis/clutz": "^1.0.29-4c95e12.v20190929",
|
|
16
17
|
"awesomplete": "^1.1.4",
|
|
17
18
|
"babel-core": "^6.26.3",
|
|
@@ -44,7 +45,6 @@
|
|
|
44
45
|
"karma-jasmine": "^3.3.1",
|
|
45
46
|
"karma-jasmine-ajax": "^0.1.13",
|
|
46
47
|
"karma-opera-launcher": "^1.0.0",
|
|
47
|
-
"karma-safari-launcher": "^1.0.0",
|
|
48
48
|
"karma-sourcemap-loader": "^0.3.7",
|
|
49
49
|
"karma-spec-reporter": "0.0.32",
|
|
50
50
|
"karma-webdriver-launcher": "^1.0.8",
|
|
@@ -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);
|