shaka-player 4.2.5 → 4.3.1

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 (143) hide show
  1. package/.github/workflows/build-and-test.yaml +7 -0
  2. package/.github/workflows/sync-labels.yaml +1 -1
  3. package/.github/workflows/update-issues.yaml +1 -1
  4. package/AUTHORS +4 -0
  5. package/CHANGELOG.md +114 -74
  6. package/CONTRIBUTORS +4 -0
  7. package/README.md +14 -0
  8. package/conditional/dummy_cast_proxy.js +120 -0
  9. package/demo/common/assets.js +58 -0
  10. package/demo/common/message_ids.js +22 -1
  11. package/demo/config.js +89 -5
  12. package/demo/demo.less +7 -1
  13. package/demo/index.html +8 -1
  14. package/demo/locales/en.json +21 -1
  15. package/demo/locales/source.json +82 -2
  16. package/demo/main.js +57 -1
  17. package/demo/search.js +2 -0
  18. package/demo/visualizer.js +337 -0
  19. package/demo/visualizer_button.js +103 -0
  20. package/dist/controls.css +1 -1
  21. package/dist/controls.css.map +1 -1
  22. package/dist/demo.compiled.debug.js +107 -88
  23. package/dist/demo.compiled.debug.map +4 -4
  24. package/dist/demo.compiled.js +107 -88
  25. package/dist/demo.compiled.map +4 -4
  26. package/dist/demo.css +1 -1
  27. package/dist/demo.css.map +1 -1
  28. package/dist/deps.js +21 -13
  29. package/dist/locales.js +31 -31
  30. package/dist/receiver.compiled.debug.js +75 -77
  31. package/dist/receiver.compiled.debug.map +3 -3
  32. package/dist/receiver.compiled.js +75 -77
  33. package/dist/receiver.compiled.map +3 -3
  34. package/dist/shaka-player.compiled.d.ts +181 -27
  35. package/dist/shaka-player.compiled.debug.d.ts +181 -27
  36. package/dist/shaka-player.compiled.debug.externs.js +169 -31
  37. package/dist/shaka-player.compiled.debug.js +510 -450
  38. package/dist/shaka-player.compiled.debug.map +4 -4
  39. package/dist/shaka-player.compiled.externs.js +169 -31
  40. package/dist/shaka-player.compiled.js +912 -874
  41. package/dist/shaka-player.compiled.map +4 -4
  42. package/dist/shaka-player.ui.d.ts +181 -27
  43. package/dist/shaka-player.ui.debug.d.ts +181 -27
  44. package/dist/shaka-player.ui.debug.externs.js +169 -31
  45. package/dist/shaka-player.ui.debug.js +533 -471
  46. package/dist/shaka-player.ui.debug.map +4 -4
  47. package/dist/shaka-player.ui.externs.js +169 -31
  48. package/dist/shaka-player.ui.js +1073 -1036
  49. package/dist/shaka-player.ui.map +4 -4
  50. package/docs/design/lcevc-architecture.png +0 -0
  51. package/docs/design/lcevc-demo.png +0 -0
  52. package/docs/design/lcevc-integration.md +72 -0
  53. package/docs/tutorials/fairplay.md +7 -0
  54. package/docs/tutorials/faq.md +1 -1
  55. package/docs/tutorials/ui.md +7 -7
  56. package/docs/tutorials/upgrade.md +6 -0
  57. package/externs/ima.js +13 -1
  58. package/externs/lcevc.js +81 -0
  59. package/externs/mux.js +1 -42
  60. package/externs/shaka/ads.js +11 -1
  61. package/externs/shaka/manifest_parser.js +5 -1
  62. package/externs/shaka/mp4_parser.js +3 -0
  63. package/externs/shaka/player.js +178 -15
  64. package/lib/abr/simple_abr_manager.js +7 -0
  65. package/lib/ads/ad_manager.js +0 -8
  66. package/lib/ads/client_side_ad.js +15 -0
  67. package/lib/ads/client_side_ad_manager.js +21 -8
  68. package/lib/ads/server_side_ad.js +15 -0
  69. package/lib/ads/server_side_ad_manager.js +3 -3
  70. package/lib/cast/cast_utils.js +2 -0
  71. package/lib/cea/dummy_cea_parser.js +28 -0
  72. package/lib/cea/i_cea_parser.js +14 -2
  73. package/lib/cea/mp4_cea_parser.js +108 -5
  74. package/lib/config/auto_show_text.js +33 -0
  75. package/lib/dash/dash_parser.js +1 -1
  76. package/lib/hls/hls_parser.js +808 -384
  77. package/lib/lcevc/lcevc_dil.js +152 -0
  78. package/lib/media/closed_caption_parser.js +9 -6
  79. package/lib/media/drm_engine.js +14 -6
  80. package/lib/media/media_source_capabilities.js +36 -0
  81. package/lib/media/media_source_engine.js +113 -28
  82. package/lib/media/presentation_timeline.js +26 -11
  83. package/lib/media/segment_reference.js +49 -1
  84. package/lib/media/streaming_engine.js +142 -12
  85. package/lib/media/transmuxer.js +78 -21
  86. package/lib/offline/storage.js +1 -0
  87. package/lib/player.js +456 -77
  88. package/lib/polyfill/media_capabilities.js +9 -5
  89. package/lib/polyfill/patchedmediakeys_apple.js +63 -4
  90. package/lib/text/vtt_text_parser.js +35 -1
  91. package/lib/util/dom_utils.js +0 -1
  92. package/lib/util/error.js +17 -0
  93. package/lib/util/fake_event.js +2 -0
  94. package/lib/util/id3_utils.js +300 -0
  95. package/lib/util/mime_utils.js +3 -6
  96. package/lib/util/mp4_box_parsers.js +39 -5
  97. package/lib/util/mp4_parser.js +35 -0
  98. package/lib/util/player_configuration.js +23 -1
  99. package/lib/util/stream_utils.js +5 -3
  100. package/lib/util/ts_parser.js +499 -0
  101. package/maintained-branches.md +2 -2
  102. package/package.json +7 -4
  103. package/roadmap.md +5 -3
  104. package/test/ads/ad_manager_unit.js +182 -29
  105. package/test/cast/cast_utils_unit.js +3 -1
  106. package/test/dash/dash_parser_content_protection_unit.js +1 -0
  107. package/test/dash/dash_parser_live_unit.js +1 -0
  108. package/test/dash/dash_parser_manifest_unit.js +93 -1
  109. package/test/dash/dash_parser_segment_base_unit.js +1 -0
  110. package/test/dash/dash_parser_segment_list_unit.js +1 -0
  111. package/test/dash/dash_parser_segment_template_unit.js +1 -0
  112. package/test/demo/demo_unit.js +2 -1
  113. package/test/hls/hls_live_unit.js +253 -19
  114. package/test/hls/hls_parser_unit.js +631 -12
  115. package/test/media/closed_caption_parser_unit.js +2 -1
  116. package/test/media/drm_engine_integration.js +3 -1
  117. package/test/media/media_source_engine_integration.js +71 -6
  118. package/test/media/media_source_engine_unit.js +10 -5
  119. package/test/media/streaming_engine_integration.js +4 -1
  120. package/test/media/streaming_engine_unit.js +144 -1
  121. package/test/media/transmuxer_integration.js +21 -10
  122. package/test/player_integration.js +224 -82
  123. package/test/player_unit.js +5 -2
  124. package/test/polyfill/media_capabilities_unit.js +21 -23
  125. package/test/polyfill/patchedmediakeys_apple_unit.js +130 -0
  126. package/test/test/assets/id3-metadata.aac +0 -0
  127. package/test/test/assets/id3-metadata.ts +0 -0
  128. package/test/test/assets/thumbnails-sprites.vtt +10 -0
  129. package/test/test/assets/thumbnails.vtt +10 -0
  130. package/test/test/util/dash_parser_util.js +2 -0
  131. package/test/test/util/fake_ad.js +21 -0
  132. package/test/test/util/fake_media_source_engine.js +7 -0
  133. package/test/test/util/id3_generator.js +100 -0
  134. package/test/test/util/manifest_generator.js +3 -6
  135. package/test/test/util/simple_fakes.js +4 -0
  136. package/test/test/util/stream_generator.js +38 -0
  137. package/test/test/util/test_scheme.js +43 -14
  138. package/test/text/vtt_text_parser_unit.js +11 -0
  139. package/test/util/id3_utils_unit.js +116 -0
  140. package/test/util/ts_parser_unit.js +77 -0
  141. package/third_party/closure-uri/uri.js +1 -1
  142. package/ui/less/containers.less +7 -0
  143. package/ui/ui.js +43 -4
@@ -10,7 +10,8 @@ describe('ClosedCaptionParser', () => {
10
10
  'base/test/test/assets/empty_caption_video_init.mp4');
11
11
  const videoSegment = await shaka.test.Util.fetch(
12
12
  'base/test/test/assets/empty_caption_video_segment.mp4');
13
- const parser = new shaka.media.ClosedCaptionParser();
13
+ const mimeType = 'video/mp4';
14
+ const parser = new shaka.media.ClosedCaptionParser(mimeType);
14
15
  parser.init(initSegment);
15
16
  parser.parseFrom(videoSegment);
16
17
  });
@@ -126,8 +126,10 @@ describe('DrmEngine', () => {
126
126
 
127
127
  mediaSourceEngine = new shaka.media.MediaSourceEngine(
128
128
  video,
129
- new shaka.test.FakeClosedCaptionParser(),
130
129
  new shaka.test.FakeTextDisplayer());
130
+ const mediaSourceConfig =
131
+ shaka.util.PlayerConfiguration.createDefault().mediaSource;
132
+ mediaSourceEngine.configure(mediaSourceConfig);
131
133
 
132
134
  const expectedObject = new Map();
133
135
  expectedObject.set(ContentType.AUDIO, audioStream);
@@ -86,6 +86,9 @@ describe('MediaSourceEngine', () => {
86
86
  */
87
87
  let textDisplayer;
88
88
 
89
+ /** @type {!jasmine.Spy} */
90
+ let onMetadata;
91
+
89
92
  beforeAll(() => {
90
93
  video = shaka.test.UiUtils.createVideoElement();
91
94
  document.body.appendChild(video);
@@ -97,10 +100,14 @@ describe('MediaSourceEngine', () => {
97
100
 
98
101
  textDisplayer = new shaka.test.FakeTextDisplayer();
99
102
 
103
+ onMetadata = jasmine.createSpy('onMetadata');
104
+
100
105
  mediaSourceEngine = new shaka.media.MediaSourceEngine(
101
106
  video,
102
- new shaka.media.ClosedCaptionParser(),
103
- textDisplayer);
107
+ textDisplayer,
108
+ shaka.test.Util.spyFunc(onMetadata));
109
+ const config = shaka.util.PlayerConfiguration.createDefault().mediaSource;
110
+ mediaSourceEngine.configure(config);
104
111
 
105
112
  mediaSource = /** @type {?} */(mediaSourceEngine)['mediaSource_'];
106
113
  expect(video.src).toBeTruthy();
@@ -443,9 +450,9 @@ describe('MediaSourceEngine', () => {
443
450
  const initObject = new Map();
444
451
  initObject.set(ContentType.VIDEO, getFakeStream(metadata.video));
445
452
  initObject.set(ContentType.TEXT, getFakeStream(metadata.text));
446
- // Call with forceTransmuxTS = true, so that it will transmux even on
453
+ // Call with forceTransmux = true, so that it will transmux even on
447
454
  // platforms with native TS support.
448
- await mediaSourceEngine.init(initObject, /* forceTransmuxTS= */ true);
455
+ await mediaSourceEngine.init(initObject, /* forceTransmux= */ true);
449
456
  mediaSourceEngine.setSelectedClosedCaptionId('CC1');
450
457
 
451
458
  await append(ContentType.VIDEO, 0);
@@ -495,7 +502,7 @@ describe('MediaSourceEngine', () => {
495
502
  initObject.set(videoType, getFakeStream(metadata.video));
496
503
 
497
504
  await mediaSourceEngine.init(
498
- initObject, /* forceTransmuxTS= */ false, /* sequenceMode= */ true);
505
+ initObject, /* forceTransmux= */ false, /* sequenceMode= */ true);
499
506
  await mediaSourceEngine.setDuration(presentationDuration);
500
507
  await mediaSourceEngine.setStreamProperties(
501
508
  videoType,
@@ -530,7 +537,7 @@ describe('MediaSourceEngine', () => {
530
537
  const initObject = new Map();
531
538
  initObject.set(ContentType.VIDEO, getFakeStream(metadata.video));
532
539
 
533
- await mediaSourceEngine.init(initObject, /* forceTransmuxTS= */ false);
540
+ await mediaSourceEngine.init(initObject, /* forceTransmux= */ false);
534
541
  await mediaSourceEngine.setDuration(presentationDuration);
535
542
  await appendInitWithClosedCaptions(ContentType.VIDEO);
536
543
  mediaSourceEngine.setSelectedClosedCaptionId('CC1');
@@ -539,4 +546,62 @@ describe('MediaSourceEngine', () => {
539
546
  expect(textDisplayer.appendSpy).toHaveBeenCalledTimes(1);
540
547
  expect(textDisplayer.appendSpy).toHaveBeenCalledWith([mp4CeaCue0]);
541
548
  });
549
+
550
+ it('extracts ID3 metadata from TS', async () => {
551
+ metadata = shaka.test.TestScheme.DATA['id3-metadata_ts'];
552
+ generators = shaka.test.TestScheme.GENERATORS['id3-metadata_ts'];
553
+
554
+ const audioType = ContentType.AUDIO;
555
+ const initObject = new Map();
556
+ initObject.set(audioType, getFakeStream(metadata.audio));
557
+ await mediaSourceEngine.init(initObject, /* forceTransmux= */ false);
558
+ await append(ContentType.AUDIO, 0);
559
+
560
+ expect(onMetadata).toHaveBeenCalled();
561
+ });
562
+
563
+ it('extracts ID3 metadata from TS when transmuxing', async () => {
564
+ metadata = shaka.test.TestScheme.DATA['id3-metadata_ts'];
565
+ generators = shaka.test.TestScheme.GENERATORS['id3-metadata_ts'];
566
+
567
+ const audioType = ContentType.AUDIO;
568
+ const initObject = new Map();
569
+ initObject.set(audioType, getFakeStream(metadata.audio));
570
+ await mediaSourceEngine.init(initObject, /* forceTransmux= */ true);
571
+ await append(ContentType.AUDIO, 0);
572
+
573
+ expect(onMetadata).toHaveBeenCalled();
574
+ });
575
+
576
+ it('extracts ID3 metadata from AAC', async () => {
577
+ if (!MediaSource.isTypeSupported('audio/aac')) {
578
+ return;
579
+ }
580
+ metadata = shaka.test.TestScheme.DATA['id3-metadata_aac'];
581
+ generators = shaka.test.TestScheme.GENERATORS['id3-metadata_aac'];
582
+
583
+ const audioType = ContentType.AUDIO;
584
+ const initObject = new Map();
585
+ initObject.set(audioType, getFakeStream(metadata.audio));
586
+ await mediaSourceEngine.init(initObject, /* forceTransmux= */ false);
587
+ await append(ContentType.AUDIO, 0);
588
+
589
+ expect(onMetadata).toHaveBeenCalled();
590
+ });
591
+
592
+ it('extracts ID3 metadata from AAC when transmuxing', async () => {
593
+ if (!MediaSource.isTypeSupported('audio/aac')) {
594
+ return;
595
+ }
596
+ metadata = shaka.test.TestScheme.DATA['id3-metadata_aac'];
597
+ generators = shaka.test.TestScheme.GENERATORS['id3-metadata_aac'];
598
+
599
+ const audioType = ContentType.AUDIO;
600
+ const initObject = new Map();
601
+ initObject.set(audioType, getFakeStream(metadata.audio));
602
+ await mediaSourceEngine.init(initObject, /* forceTransmux= */ true);
603
+ await append(ContentType.AUDIO, 0);
604
+
605
+ expect(onMetadata).toHaveBeenCalled();
606
+ });
542
607
  });
@@ -106,7 +106,9 @@ describe('MediaSourceEngine', () => {
106
106
  shaka.media.Transmuxer = /** @type {?} */ (function() {
107
107
  return /** @type {?} */ (mockTransmuxer);
108
108
  });
109
- shaka.media.Transmuxer.convertTsCodecs = originalTransmuxer.convertTsCodecs;
109
+ shaka.media.Transmuxer.convertCodecs = (mimeType, contentType) => {
110
+ return 'video/mp4; codecs="avc1.42E01E"';
111
+ };
110
112
  shaka.media.Transmuxer.isSupported = (mimeType, contentType) => {
111
113
  return mimeType == 'tsMimetype';
112
114
  };
@@ -150,8 +152,12 @@ describe('MediaSourceEngine', () => {
150
152
  mockTextDisplayer = new shaka.test.FakeTextDisplayer();
151
153
  mediaSourceEngine = new shaka.media.MediaSourceEngine(
152
154
  video,
153
- mockClosedCaptionParser,
154
155
  mockTextDisplayer);
156
+ mediaSourceEngine.getCaptionParser = () => {
157
+ return mockClosedCaptionParser;
158
+ };
159
+ const config = shaka.util.PlayerConfiguration.createDefault().mediaSource;
160
+ mediaSourceEngine.configure(config);
155
161
  });
156
162
 
157
163
  afterEach(() => {
@@ -204,7 +210,6 @@ describe('MediaSourceEngine', () => {
204
210
  it('creates a MediaSource object and sets video.src', () => {
205
211
  mediaSourceEngine = new shaka.media.MediaSourceEngine(
206
212
  video,
207
- new shaka.test.FakeClosedCaptionParser(),
208
213
  new shaka.test.FakeTextDisplayer());
209
214
 
210
215
  expect(createMediaSourceSpy).toHaveBeenCalled();
@@ -631,7 +636,7 @@ describe('MediaSourceEngine', () => {
631
636
  videoSourceBuffer.mode = 'sequence';
632
637
 
633
638
  await mediaSourceEngine.init(
634
- initObject, /* forceTransmuxTS= */ false, /* sequenceMode= */ true);
639
+ initObject, /* forceTransmux= */ false, /* sequenceMode= */ true);
635
640
 
636
641
  expect(videoSourceBuffer.timestampOffset).toBe(0);
637
642
 
@@ -657,7 +662,7 @@ describe('MediaSourceEngine', () => {
657
662
  initObject.set(ContentType.VIDEO, fakeVideoStream);
658
663
 
659
664
  await mediaSourceEngine.init(
660
- initObject, /* forceTransmuxTS= */ false, /* sequenceMode= */ true);
665
+ initObject, /* forceTransmux= */ false, /* sequenceMode= */ true);
661
666
 
662
667
  // First, mock the scenario where timestampOffset is set to help align
663
668
  // text segments. In this case, SourceBuffer mode is still 'segments'.
@@ -66,8 +66,10 @@ describe('StreamingEngine', () => {
66
66
 
67
67
  mediaSourceEngine = new shaka.media.MediaSourceEngine(
68
68
  video,
69
- new shaka.test.FakeClosedCaptionParser(),
70
69
  new shaka.test.FakeTextDisplayer());
70
+ const mediaSourceConfig =
71
+ shaka.util.PlayerConfiguration.createDefault().mediaSource;
72
+ mediaSourceEngine.configure(mediaSourceConfig);
71
73
  waiter.setMediaSourceEngine(mediaSourceEngine);
72
74
  });
73
75
 
@@ -261,6 +263,7 @@ describe('StreamingEngine', () => {
261
263
  onSegmentAppended: () => playhead.notifyOfBufferingChange(),
262
264
  onInitSegmentAppended: () => {},
263
265
  beforeAppendSegment: () => Promise.resolve(),
266
+ onMetadata: () => {},
264
267
  };
265
268
  streamingEngine = new shaka.media.StreamingEngine(
266
269
  /** @type {shaka.extern.Manifest} */(manifest), playerInterface);
@@ -74,6 +74,8 @@ describe('StreamingEngine', () => {
74
74
  let streamingEngine;
75
75
  /** @type {!jasmine.Spy} */
76
76
  let beforeAppendSegment;
77
+ /** @type {!jasmine.Spy} */
78
+ let onMetadata;
77
79
 
78
80
  /** @type {function(function(), number)} */
79
81
  let realSetTimeout;
@@ -432,6 +434,7 @@ describe('StreamingEngine', () => {
432
434
  onManifestUpdate = jasmine.createSpy('onManifestUpdate');
433
435
  onSegmentAppended = jasmine.createSpy('onSegmentAppended');
434
436
  beforeAppendSegment = jasmine.createSpy('beforeAppendSegment');
437
+ onMetadata = jasmine.createSpy('onMetadata');
435
438
  getBandwidthEstimate = jasmine.createSpy('getBandwidthEstimate');
436
439
  getBandwidthEstimate.and.returnValue(1e3);
437
440
 
@@ -461,6 +464,7 @@ describe('StreamingEngine', () => {
461
464
  onSegmentAppended: Util.spyFunc(onSegmentAppended),
462
465
  onInitSegmentAppended: () => {},
463
466
  beforeAppendSegment: Util.spyFunc(beforeAppendSegment),
467
+ onMetadata: Util.spyFunc(onMetadata),
464
468
  };
465
469
  streamingEngine = new shaka.media.StreamingEngine(
466
470
  /** @type {shaka.extern.Manifest} */(manifest), playerInterface);
@@ -517,7 +521,7 @@ describe('StreamingEngine', () => {
517
521
  expectedMseInit.set(ContentType.TEXT, textStream);
518
522
 
519
523
  expect(mediaSourceEngine.init).toHaveBeenCalledWith(expectedMseInit,
520
- /** forceTransmuxTS= */ false, /** sequenceMode= */ false);
524
+ /** forceTransmux= */ false, /** sequenceMode= */ false);
521
525
  expect(mediaSourceEngine.init).toHaveBeenCalledTimes(1);
522
526
 
523
527
  expect(mediaSourceEngine.setDuration).toHaveBeenCalledTimes(1);
@@ -2861,6 +2865,52 @@ describe('StreamingEngine', () => {
2861
2865
  expect(onEvent).not.toHaveBeenCalled();
2862
2866
  expect(onManifestUpdate).toHaveBeenCalled();
2863
2867
  });
2868
+
2869
+ it('triggers metadata event', async () => {
2870
+ // This is an 'emsg' box that contains a scheme of
2871
+ // https://aomedia.org/emsg/ID to indicate a ID3 metadata.
2872
+ segmentData[ContentType.VIDEO].segments[0] =
2873
+ Uint8ArrayUtils.fromHex((
2874
+ // 105 bytes emsg box v0, flags 0
2875
+ '00 00 00 69 65 6d 73 67 00 00 00 00' +
2876
+
2877
+ // scheme id uri (13 bytes) 'https://aomedia.org/emsg/ID3'
2878
+ '68 74 74 70 73 3a 2f 2f 61 6f 6d 65 64 69 61 2e' +
2879
+ '6f 72 67 2f 65 6d 73 67 2f 49 44 33 00' +
2880
+
2881
+ // value (1 byte) ''
2882
+ '00' +
2883
+
2884
+ // timescale (4 bytes) 49
2885
+ '00 00 00 31' +
2886
+
2887
+ // presentation time delta (4 bytes) 8
2888
+ '00 00 00 08' +
2889
+
2890
+ // event duration (4 bytes) 255
2891
+ '00 00 00 ff' +
2892
+
2893
+ // id (4 bytes) 51
2894
+ '00 00 00 33' +
2895
+
2896
+ // message data (47 bytes)
2897
+ '49 44 33 03 00 40 00 00 00 1b 00 00 00 06 00 00' +
2898
+ '00 00 00 02 54 58 58 58 00 00 00 07 e0 00 03 00' +
2899
+ '53 68 61 6b 61 33 44 49 03 00 40 00 00 00 1b'
2900
+ ).replace(/\s/g, ''));
2901
+
2902
+ videoStream.emsgSchemeIdUris = ['https://aomedia.org/emsg/ID3'];
2903
+
2904
+ // Here we go!
2905
+ streamingEngine.switchVariant(variant);
2906
+ streamingEngine.switchTextStream(textStream);
2907
+ await streamingEngine.start();
2908
+ playing = true;
2909
+ await runTest();
2910
+
2911
+ expect(onEvent).not.toHaveBeenCalled();
2912
+ expect(onMetadata).toHaveBeenCalled();
2913
+ });
2864
2914
  });
2865
2915
 
2866
2916
  describe('embedded emsg boxes with non zero timestamps', () => {
@@ -2925,6 +2975,99 @@ describe('StreamingEngine', () => {
2925
2975
  });
2926
2976
  });
2927
2977
 
2978
+ describe('embedded prft boxes', () => {
2979
+ const prftSegment = Uint8ArrayUtils.fromHex(
2980
+ '00000020707266740100000000000001E683B62E8E63CC580000001B319D5767');
2981
+ const mdhdSegment = Uint8ArrayUtils.fromHex(
2982
+ '000000446D6F6F760000003C7472616B000000346D6469610000002C6D646864'+
2983
+ '0100000000000000DF22526500000000DF22526500989680FFFFFFFFFFFFFFFF'+
2984
+ '15C70000');
2985
+
2986
+ const prftEventObj = {
2987
+ wallClockTime: 1658402734556,
2988
+ startDate: new Date(1658391054904.7898),
2989
+ };
2990
+
2991
+ beforeEach(() => {
2992
+ setupVod();
2993
+ mediaSourceEngine = new shaka.test.FakeMediaSourceEngine(segmentData);
2994
+ const config = shaka.util.PlayerConfiguration.createDefault().streaming;
2995
+ config.parsePrftBox = true;
2996
+ createStreamingEngine(config);
2997
+ });
2998
+
2999
+ it('raises an event for registered prft v1', async () => {
3000
+ segmentData[ContentType.VIDEO].segments[0] = prftSegment;
3001
+ segmentData[ContentType.VIDEO].initSegments[0] = mdhdSegment;
3002
+
3003
+ streamingEngine.switchVariant(variant);
3004
+ streamingEngine.switchTextStream(textStream);
3005
+ await streamingEngine.start();
3006
+ playing = true;
3007
+ await runTest();
3008
+ expect(onEvent).toHaveBeenCalled();
3009
+
3010
+ const event = onEvent.calls.argsFor(0)[0];
3011
+ expect(event.detail.wallClockTime).toBe(prftEventObj.wallClockTime);
3012
+ expect(event.detail.programStartDate.getUTCMilliseconds()).toBe(
3013
+ prftEventObj.startDate.getUTCMilliseconds());
3014
+ expect(event.detail.programStartDate.toUTCString()).toBe(
3015
+ prftEventObj.startDate.toUTCString());
3016
+ });
3017
+
3018
+ it('raises an event for registered prft v0', async () => {
3019
+ const prftSegment = Uint8ArrayUtils.fromHex(
3020
+ '0000001C707266740000000000000001E683B62E8E63CC5819999999');
3021
+ const expectedStartDate = new Date(1658402691606.3271);
3022
+ segmentData[ContentType.VIDEO].segments[0] = prftSegment;
3023
+ segmentData[ContentType.VIDEO].initSegments[0] = mdhdSegment;
3024
+
3025
+ streamingEngine.switchVariant(variant);
3026
+ streamingEngine.switchTextStream(textStream);
3027
+ await streamingEngine.start();
3028
+ playing = true;
3029
+ await runTest();
3030
+ expect(onEvent).toHaveBeenCalled();
3031
+
3032
+ const event = onEvent.calls.argsFor(0)[0];
3033
+ expect(event.detail.wallClockTime).toBe(prftEventObj.wallClockTime);
3034
+ expect(event.detail.programStartDate.getUTCMilliseconds()).toBe(
3035
+ expectedStartDate.getUTCMilliseconds());
3036
+ expect(event.detail.programStartDate.toUTCString()).toBe(
3037
+ expectedStartDate.toUTCString());
3038
+ });
3039
+
3040
+ it('does not raise event if mediatime exceeds Number.MAX_VALUE',
3041
+ async () => {
3042
+ const prftSegment = Uint8ArrayUtils.fromHex(
3043
+ '00000020707266740100000000000001E683B62E8E63CC58'+
3044
+ 'FFFFFFFFFFFFFFFF');
3045
+ segmentData[ContentType.VIDEO].segments[0] = prftSegment;
3046
+ segmentData[ContentType.VIDEO].initSegments[0] = mdhdSegment;
3047
+
3048
+ streamingEngine.switchVariant(variant);
3049
+ streamingEngine.switchTextStream(textStream);
3050
+ await streamingEngine.start();
3051
+ playing = true;
3052
+ await runTest();
3053
+ expect(onEvent).not.toHaveBeenCalled();
3054
+ });
3055
+
3056
+ it('raises an event once only', async () => {
3057
+ segmentData[ContentType.VIDEO].segments[0] =
3058
+ shaka.util.Uint8ArrayUtils.concat(prftSegment, prftSegment);
3059
+ segmentData[ContentType.VIDEO].segments[1] = prftSegment;
3060
+ segmentData[ContentType.VIDEO].initSegments[0] = mdhdSegment;
3061
+
3062
+ streamingEngine.switchVariant(variant);
3063
+ streamingEngine.switchTextStream(textStream);
3064
+ await streamingEngine.start();
3065
+ playing = true;
3066
+ await runTest();
3067
+ expect(onEvent).toHaveBeenCalledTimes(1);
3068
+ });
3069
+ });
3070
+
2928
3071
  describe('network downgrading', () => {
2929
3072
  /** @type {shaka.extern.Variant} */
2930
3073
  let initialVariant;
@@ -12,6 +12,9 @@ describe('Transmuxer', () => {
12
12
  const mp4MimeType = 'video/mp4; codecs="avc1.42E01E"';
13
13
  const transportStreamVideoMimeType = 'video/mp2t; codecs="avc1.42E01E"';
14
14
  const transportStreamAudioMimeType = 'video/mp2t; codecs="mp4a.40.2"';
15
+ const aacAudioMimeType = 'audio/aac';
16
+
17
+ const transmuxerMimeType = 'fake/mimeType';
15
18
 
16
19
  /** @type {!ArrayBuffer} */
17
20
  let videoSegment;
@@ -35,7 +38,7 @@ describe('Transmuxer', () => {
35
38
  });
36
39
 
37
40
  beforeEach(() => {
38
- transmuxer = new shaka.media.Transmuxer();
41
+ transmuxer = new shaka.media.Transmuxer(transmuxerMimeType);
39
42
  });
40
43
 
41
44
  afterEach(async () => {
@@ -59,42 +62,50 @@ describe('Transmuxer', () => {
59
62
  });
60
63
  });
61
64
 
62
- describe('convertTsCodecs', () => {
63
- const convertTsCodecs =
64
- (type, codecs) => shaka.media.Transmuxer.convertTsCodecs(type, codecs);
65
+ describe('convertCodecs', () => {
66
+ const convertCodecs =
67
+ (type, codecs) => shaka.media.Transmuxer.convertCodecs(type, codecs);
65
68
 
66
69
  it('returns converted codecs', () => {
67
70
  const convertedVideoCodecs =
68
- convertTsCodecs(ContentType.VIDEO, transportStreamVideoMimeType);
71
+ convertCodecs(ContentType.VIDEO, transportStreamVideoMimeType);
69
72
  const convertedAudioCodecs =
70
- convertTsCodecs(ContentType.AUDIO, transportStreamAudioMimeType);
73
+ convertCodecs(ContentType.AUDIO, transportStreamAudioMimeType);
74
+ const convertedAacCodecs =
75
+ convertCodecs(ContentType.AUDIO, aacAudioMimeType);
71
76
  const expectedVideoCodecs = 'video/mp4; codecs="avc1.42E01E"';
72
77
  const expectedAudioCodecs = 'audio/mp4; codecs="mp4a.40.2"';
78
+ const expectedAacCodecs = 'audio/mp4; codecs="mp4a.40.2"';
73
79
  expect(convertedVideoCodecs).toBe(expectedVideoCodecs);
74
80
  expect(convertedAudioCodecs).toBe(expectedAudioCodecs);
81
+ expect(convertedAacCodecs).toBe(expectedAacCodecs);
75
82
  });
76
83
 
77
84
  it('converts legacy avc1 codec strings', () => {
78
85
  expect(
79
- convertTsCodecs(
86
+ convertCodecs(
80
87
  ContentType.VIDEO, 'video/mp2t; codecs="avc1.100.42"'))
81
88
  .toBe('video/mp4; codecs="avc1.64002a"');
82
89
  expect(
83
- convertTsCodecs(ContentType.VIDEO, 'video/mp2t; codecs="avc1.77.80"'))
90
+ convertCodecs(ContentType.VIDEO, 'video/mp2t; codecs="avc1.77.80"'))
84
91
  .toBe('video/mp4; codecs="avc1.4d0050"');
85
92
  expect(
86
- convertTsCodecs(ContentType.VIDEO, 'video/mp2t; codecs="avc1.66.1"'))
93
+ convertCodecs(ContentType.VIDEO, 'video/mp2t; codecs="avc1.66.1"'))
87
94
  .toBe('video/mp4; codecs="avc1.420001"');
88
95
  });
89
96
 
90
97
  // Issue #1991
91
98
  it('handles upper-case MIME types', () => {
92
- expect(convertTsCodecs(
99
+ expect(convertCodecs(
93
100
  ContentType.VIDEO, 'video/MP2T; codecs="avc1.420001"'))
94
101
  .toBe('video/mp4; codecs="avc1.420001"');
95
102
  });
96
103
  });
97
104
 
105
+ it('getOrginalMimeType returns the correct mimeType', () => {
106
+ expect(transmuxer.getOrginalMimeType()).toBe(transmuxerMimeType);
107
+ });
108
+
98
109
  describe('transmuxing', () => {
99
110
  it('transmux video from TS to MP4', async () => {
100
111
  let sawMDAT = false;