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.
Files changed (181) hide show
  1. package/AUTHORS +8 -2
  2. package/CHANGELOG.md +209 -0
  3. package/CONTRIBUTORS +10 -3
  4. package/README.md +3 -0
  5. package/build/apps.py +2 -2
  6. package/build/check.py +2 -2
  7. package/build/conformance.textproto +0 -9
  8. package/build/generateExterns.js +1 -1
  9. package/build/misspellings.txt +1 -0
  10. package/build/shakaBuildHelpers.py +6 -5
  11. package/build/subprocessWindowsPatch.py +103 -0
  12. package/demo/common/asset.js +11 -0
  13. package/demo/common/assets.js +1 -1
  14. package/demo/common/message_ids.js +5 -0
  15. package/demo/config.js +2 -1
  16. package/demo/custom.js +84 -3
  17. package/demo/demo.less +13 -1
  18. package/demo/demo_utils.js +1 -0
  19. package/demo/input.js +31 -12
  20. package/demo/input_container.js +24 -1
  21. package/demo/locales/en.json +6 -1
  22. package/demo/locales/source.json +20 -0
  23. package/demo/main.js +5 -1
  24. package/dist/controls.css +2 -2
  25. package/dist/controls.css.map +1 -1
  26. package/dist/demo.compiled.debug.js +99 -91
  27. package/dist/demo.compiled.debug.map +3 -3
  28. package/dist/demo.compiled.js +99 -91
  29. package/dist/demo.compiled.map +3 -3
  30. package/dist/demo.css +1 -1
  31. package/dist/demo.css.map +1 -1
  32. package/dist/deps.js +9 -9
  33. package/dist/locales.js +36 -34
  34. package/dist/receiver.compiled.debug.js +74 -70
  35. package/dist/receiver.compiled.debug.map +3 -3
  36. package/dist/receiver.compiled.js +74 -70
  37. package/dist/receiver.compiled.map +3 -3
  38. package/dist/shaka-player.compiled.d.ts +165 -9
  39. package/dist/shaka-player.compiled.debug.d.ts +165 -9
  40. package/dist/shaka-player.compiled.debug.externs.js +179 -11
  41. package/dist/shaka-player.compiled.debug.js +367 -339
  42. package/dist/shaka-player.compiled.debug.map +4 -4
  43. package/dist/shaka-player.compiled.externs.js +179 -11
  44. package/dist/shaka-player.compiled.js +818 -807
  45. package/dist/shaka-player.compiled.map +4 -4
  46. package/dist/shaka-player.ui.d.ts +165 -9
  47. package/dist/shaka-player.ui.debug.d.ts +165 -9
  48. package/dist/shaka-player.ui.debug.externs.js +181 -13
  49. package/dist/shaka-player.ui.debug.js +459 -429
  50. package/dist/shaka-player.ui.debug.map +4 -4
  51. package/dist/shaka-player.ui.externs.js +181 -13
  52. package/dist/shaka-player.ui.js +973 -961
  53. package/dist/shaka-player.ui.map +4 -4
  54. package/docs/tutorials/fairplay.md +1 -1
  55. package/docs/tutorials/faq.md +25 -0
  56. package/docs/tutorials/index.json +2 -2
  57. package/docs/tutorials/upgrade-v3.md +38 -0
  58. package/docs/tutorials/upgrade.md +3 -3
  59. package/externs/ima.js +4 -0
  60. package/externs/media_capabilities.js +16 -0
  61. package/externs/shaka/ads.js +2 -0
  62. package/externs/shaka/net.js +2 -2
  63. package/externs/shaka/player.js +6 -0
  64. package/karma.conf.js +1 -0
  65. package/lib/abr/simple_abr_manager.js +1 -1
  66. package/lib/ads/ad_manager.js +29 -11
  67. package/lib/ads/client_side_ad_manager.js +76 -86
  68. package/lib/ads/server_side_ad_manager.js +26 -11
  69. package/lib/cast/cast_proxy.js +3 -2
  70. package/lib/cast/cast_receiver.js +2 -1
  71. package/lib/cast/cast_sender.js +2 -2
  72. package/lib/cea/sei_processor.js +24 -17
  73. package/lib/dash/dash_parser.js +107 -4
  74. package/lib/dash/segment_list.js +1 -1
  75. package/lib/dash/segment_template.js +45 -15
  76. package/lib/dependencies/all.js +15 -15
  77. package/lib/hls/hls_parser.js +46 -18
  78. package/lib/media/closed_caption_parser.js +3 -1
  79. package/lib/media/content_workarounds.js +18 -0
  80. package/lib/media/drm_engine.js +20 -1
  81. package/lib/media/gap_jumping_controller.js +8 -4
  82. package/lib/media/media_source_engine.js +1 -2
  83. package/lib/media/playhead.js +1 -1
  84. package/lib/media/presentation_timeline.js +3 -1
  85. package/lib/media/segment_index.js +38 -12
  86. package/lib/media/segment_reference.js +8 -0
  87. package/lib/media/streaming_engine.js +30 -21
  88. package/lib/net/http_fetch_plugin.js +2 -2
  89. package/lib/net/networking_engine.js +3 -2
  90. package/lib/offline/storage.js +4 -0
  91. package/lib/player.js +113 -56
  92. package/lib/polyfill/encryption_scheme.js +2 -0
  93. package/lib/polyfill/fullscreen.js +2 -0
  94. package/lib/polyfill/mathround.js +2 -0
  95. package/lib/polyfill/media_capabilities.js +62 -23
  96. package/lib/polyfill/mediasource.js +8 -0
  97. package/lib/polyfill/orientation.js +3 -1
  98. package/lib/polyfill/patchedmediakeys_apple.js +6 -4
  99. package/lib/polyfill/patchedmediakeys_ms.js +6 -4
  100. package/lib/polyfill/patchedmediakeys_nop.js +2 -0
  101. package/lib/polyfill/patchedmediakeys_webkit.js +6 -4
  102. package/lib/polyfill/pip_webkit.js +2 -0
  103. package/lib/polyfill/storage_estimate.js +2 -0
  104. package/lib/polyfill/video_play_promise.js +2 -0
  105. package/lib/polyfill/videoplaybackquality.js +2 -0
  106. package/lib/polyfill/vttcue.js +2 -0
  107. package/lib/text/cue.js +11 -0
  108. package/lib/text/mp4_ttml_parser.js +3 -0
  109. package/lib/text/text_engine.js +14 -2
  110. package/lib/text/ttml_text_parser.js +52 -30
  111. package/lib/text/ui_text_displayer.js +255 -82
  112. package/lib/text/vtt_text_parser.js +4 -2
  113. package/lib/util/error.js +1 -4
  114. package/lib/util/fake_event.js +53 -7
  115. package/lib/util/mime_utils.js +33 -0
  116. package/lib/util/mp4_box_parsers.js +1 -1
  117. package/lib/util/mp4_parser.js +14 -0
  118. package/lib/util/networking.js +1 -1
  119. package/lib/util/periods.js +132 -37
  120. package/lib/util/platform.js +13 -1
  121. package/lib/util/stream_utils.js +46 -7
  122. package/package.json +3 -3
  123. package/test/cast/cast_proxy_unit.js +12 -6
  124. package/test/cast/cast_receiver_integration.js +1 -1
  125. package/test/cast/cast_utils_unit.js +1 -1
  126. package/test/cea/mp4_cea_parser_unit.js +26 -0
  127. package/test/dash/dash_parser_live_unit.js +1 -0
  128. package/test/dash/dash_parser_manifest_unit.js +187 -1
  129. package/test/dash/dash_parser_segment_template_unit.js +3 -3
  130. package/test/demo/demo_unit.js +0 -1
  131. package/test/hls/hls_parser_unit.js +45 -1
  132. package/test/media/closed_caption_parser_unit.js +20 -0
  133. package/test/media/drm_engine_unit.js +59 -3
  134. package/test/media/playhead_unit.js +27 -1
  135. package/test/media/presentation_timeline_unit.js +19 -0
  136. package/test/media/segment_index_unit.js +76 -3
  137. package/test/media/streaming_engine_unit.js +3 -3
  138. package/test/player_integration.js +38 -0
  139. package/test/player_unit.js +125 -6
  140. package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
  141. package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
  142. package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
  143. package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
  144. package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
  145. package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
  146. package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
  147. package/test/test/assets/3675/dash_0.mpd +30 -0
  148. package/test/test/assets/3675/dash_1.mpd +30 -0
  149. package/test/test/assets/3675/dash_2.mpd +30 -0
  150. package/test/test/assets/3675/dash_3.mpd +30 -0
  151. package/test/test/assets/3675/dash_4.mpd +30 -0
  152. package/test/test/assets/3675/dash_5.mpd +26 -0
  153. package/test/test/assets/3675/init-stream0.m4s +0 -0
  154. package/test/test/assets/empty_caption_video_init.mp4 +0 -0
  155. package/test/test/assets/empty_caption_video_segment.mp4 +0 -0
  156. package/test/test/util/fake_ad_manager.js +4 -1
  157. package/test/test/util/simple_fakes.js +5 -3
  158. package/test/test/util/waiter.js +36 -2
  159. package/test/text/ttml_text_parser_unit.js +55 -0
  160. package/test/text/ui_text_displayer_unit.js +95 -18
  161. package/test/text/vtt_text_parser_unit.js +17 -1
  162. package/test/ui/localization_unit.js +15 -14
  163. package/test/ui/text_displayer_layout_unit.js +52 -1
  164. package/test/ui/ui_unit.js +4 -0
  165. package/test/util/mp4_box_parsers_unit.js +28 -0
  166. package/test/util/periods_unit.js +190 -2
  167. package/test/util/stream_utils_unit.js +56 -5
  168. package/ui/airplay_button.js +5 -0
  169. package/ui/big_play_button.js +10 -0
  170. package/ui/cast_button.js +2 -3
  171. package/ui/controls.js +9 -7
  172. package/ui/less/containers.less +10 -5
  173. package/ui/locales/en.json +1 -0
  174. package/ui/locales/ru.json +6 -6
  175. package/ui/locales/source.json +4 -0
  176. package/ui/localization.js +28 -51
  177. package/ui/pip_button.js +2 -3
  178. package/ui/play_button.js +10 -10
  179. package/ui/small_play_button.js +12 -0
  180. package/ui/ui.js +1 -1
  181. package/docs/tutorials/upgrade-v2-4.md +0 -481
@@ -80,7 +80,10 @@ shaka.util.PeriodCombiner = class {
80
80
 
81
81
  /** @return {!Array.<shaka.extern.Stream>} */
82
82
  getTextStreams() {
83
- return this.textStreams_;
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 fails on any
146
- // period without matching streams. So we add dummy text streams to each
147
- // period. Since we combine text streams by language, we might need a
148
- // dummy even in periods with text streams already.
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.dummyTextStream_());
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
- // It's okay to have a period with no text, but our algorithm fails on any
373
- // period without matching streams. So we add dummy text streams to each
374
- // period. Since we combine text streams by language, we might need a
375
- // dummy even in periods with text streams already.
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.dummyTextStreamDB_());
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
- if (stream.type == ContentType.TEXT && !stream.language) {
510
- // This is one of our dummy text streams, so ignore it. We may not
511
- // use them all, and that's fine.
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 Promise.all(matches.map((match) => match.createSegmentIndex()));
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 Promise.all(matches.map((match) => match.createSegmentIndex()));
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 detecrmine the best
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 detecrmine the best
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 detecrmine the best
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
- // If the candidate has the same MIME type, upgrade to the
1326
- // candidate. It's not required that image streams use the same format
1327
- // across periods, but it's a helpful signal.
1328
- if (candidate.mimeType == outputStream.mimeType) {
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 text StreamDB to fill in periods with no text, to avoid
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 dummyTextStreamDB_() {
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: ContentType.TEXT,
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 text Stream to fill in periods with no text, to avoid
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 dummyTextStream_() {
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: ContentType.TEXT,
1486
+ type,
1392
1487
  primary: false,
1393
1488
  trickModeVideo: null,
1394
1489
  emsgSchemeIdUris: null,
@@ -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
  *
@@ -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(navigator.mediaCapabilities,
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 = navigator.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.0",
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": "^20201006.0.0",
32
- "google-closure-library": "^20200830.0.0",
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('timeupdate', {detail: 8675309});
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('timeupdate', {detail: 8675309});
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('timeupdate', {detail: 8675309});
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('buffering', {detail: 8675309});
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('buffering', {detail: 8675309});
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('buffering', {detail: 8675309});
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(6 * 1024);
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 = new FakeEvent(deserialized['type'], deserialized);
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);