shaka-player 4.2.4 → 4.3.0

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 (137) hide show
  1. package/.github/workflows/sync-labels.yaml +1 -1
  2. package/.github/workflows/update-issues.yaml +1 -1
  3. package/AUTHORS +4 -0
  4. package/CHANGELOG.md +85 -54
  5. package/CONTRIBUTORS +4 -0
  6. package/README.md +14 -0
  7. package/demo/common/assets.js +58 -0
  8. package/demo/common/message_ids.js +22 -1
  9. package/demo/config.js +89 -5
  10. package/demo/demo.less +7 -1
  11. package/demo/index.html +8 -1
  12. package/demo/locales/en.json +21 -1
  13. package/demo/locales/source.json +82 -2
  14. package/demo/main.js +57 -1
  15. package/demo/search.js +2 -0
  16. package/demo/visualizer.js +337 -0
  17. package/demo/visualizer_button.js +103 -0
  18. package/dist/controls.css +1 -1
  19. package/dist/controls.css.map +1 -1
  20. package/dist/demo.compiled.debug.js +83 -58
  21. package/dist/demo.compiled.debug.map +4 -4
  22. package/dist/demo.compiled.js +83 -58
  23. package/dist/demo.compiled.map +4 -4
  24. package/dist/demo.css +1 -1
  25. package/dist/demo.css.map +1 -1
  26. package/dist/deps.js +16 -9
  27. package/dist/locales.js +32 -32
  28. package/dist/receiver.compiled.debug.js +23 -19
  29. package/dist/receiver.compiled.debug.map +3 -3
  30. package/dist/receiver.compiled.js +23 -19
  31. package/dist/receiver.compiled.map +3 -3
  32. package/dist/shaka-player.compiled.d.ts +173 -26
  33. package/dist/shaka-player.compiled.debug.d.ts +173 -26
  34. package/dist/shaka-player.compiled.debug.externs.js +161 -31
  35. package/dist/shaka-player.compiled.debug.js +464 -401
  36. package/dist/shaka-player.compiled.debug.map +4 -4
  37. package/dist/shaka-player.compiled.externs.js +161 -31
  38. package/dist/shaka-player.compiled.js +897 -862
  39. package/dist/shaka-player.compiled.map +4 -4
  40. package/dist/shaka-player.ui.d.ts +173 -26
  41. package/dist/shaka-player.ui.debug.d.ts +173 -26
  42. package/dist/shaka-player.ui.debug.externs.js +161 -31
  43. package/dist/shaka-player.ui.debug.js +488 -422
  44. package/dist/shaka-player.ui.debug.map +4 -4
  45. package/dist/shaka-player.ui.externs.js +161 -31
  46. package/dist/shaka-player.ui.js +1053 -1018
  47. package/dist/shaka-player.ui.map +4 -4
  48. package/docs/design/lcevc-architecture.png +0 -0
  49. package/docs/design/lcevc-demo.png +0 -0
  50. package/docs/design/lcevc-integration.md +72 -0
  51. package/docs/tutorials/fairplay.md +7 -0
  52. package/docs/tutorials/faq.md +1 -1
  53. package/docs/tutorials/ui.md +7 -7
  54. package/docs/tutorials/upgrade.md +6 -0
  55. package/externs/ima.js +13 -1
  56. package/externs/lcevc.js +81 -0
  57. package/externs/mux.js +1 -42
  58. package/externs/shaka/ads.js +11 -1
  59. package/externs/shaka/manifest_parser.js +5 -1
  60. package/externs/shaka/player.js +178 -15
  61. package/lib/abr/simple_abr_manager.js +7 -0
  62. package/lib/ads/ad_manager.js +0 -8
  63. package/lib/ads/client_side_ad.js +15 -0
  64. package/lib/ads/client_side_ad_manager.js +21 -8
  65. package/lib/ads/server_side_ad.js +15 -0
  66. package/lib/ads/server_side_ad_manager.js +3 -3
  67. package/lib/cast/cast_utils.js +2 -0
  68. package/lib/cea/dummy_cea_parser.js +28 -0
  69. package/lib/cea/i_cea_parser.js +14 -2
  70. package/lib/cea/mp4_cea_parser.js +87 -5
  71. package/lib/config/auto_show_text.js +33 -0
  72. package/lib/dash/dash_parser.js +1 -1
  73. package/lib/hls/hls_parser.js +790 -374
  74. package/lib/lcevc/lcevc_dil.js +152 -0
  75. package/lib/media/closed_caption_parser.js +9 -6
  76. package/lib/media/drm_engine.js +14 -6
  77. package/lib/media/media_source_engine.js +106 -26
  78. package/lib/media/presentation_timeline.js +26 -11
  79. package/lib/media/segment_reference.js +49 -1
  80. package/lib/media/streaming_engine.js +142 -12
  81. package/lib/media/transmuxer.js +73 -18
  82. package/lib/offline/storage.js +1 -0
  83. package/lib/player.js +454 -75
  84. package/lib/polyfill/patchedmediakeys_apple.js +55 -1
  85. package/lib/text/ui_text_displayer.js +5 -5
  86. package/lib/text/vtt_text_parser.js +35 -1
  87. package/lib/util/dom_utils.js +0 -1
  88. package/lib/util/error.js +17 -0
  89. package/lib/util/fake_event.js +2 -0
  90. package/lib/util/id3_utils.js +300 -0
  91. package/lib/util/mime_utils.js +3 -6
  92. package/lib/util/mp4_box_parsers.js +15 -5
  93. package/lib/util/platform.js +9 -1
  94. package/lib/util/player_configuration.js +23 -1
  95. package/lib/util/ts_parser.js +499 -0
  96. package/maintained-branches.md +2 -2
  97. package/package.json +4 -1
  98. package/roadmap.md +5 -3
  99. package/test/ads/ad_manager_unit.js +180 -29
  100. package/test/cast/cast_utils_unit.js +3 -1
  101. package/test/dash/dash_parser_content_protection_unit.js +1 -0
  102. package/test/dash/dash_parser_live_unit.js +1 -0
  103. package/test/dash/dash_parser_manifest_unit.js +93 -1
  104. package/test/dash/dash_parser_segment_base_unit.js +1 -0
  105. package/test/dash/dash_parser_segment_list_unit.js +1 -0
  106. package/test/dash/dash_parser_segment_template_unit.js +1 -0
  107. package/test/demo/demo_unit.js +2 -1
  108. package/test/hls/hls_live_unit.js +253 -19
  109. package/test/hls/hls_parser_unit.js +500 -12
  110. package/test/media/closed_caption_parser_unit.js +2 -1
  111. package/test/media/drm_engine_integration.js +3 -1
  112. package/test/media/media_source_engine_integration.js +71 -6
  113. package/test/media/media_source_engine_unit.js +10 -5
  114. package/test/media/streaming_engine_integration.js +4 -1
  115. package/test/media/streaming_engine_unit.js +144 -1
  116. package/test/media/transmuxer_integration.js +21 -10
  117. package/test/player_integration.js +224 -82
  118. package/test/player_unit.js +5 -2
  119. package/test/polyfill/patchedmediakeys_apple_unit.js +124 -0
  120. package/test/test/assets/id3-metadata.aac +0 -0
  121. package/test/test/assets/id3-metadata.ts +0 -0
  122. package/test/test/assets/thumbnails-sprites.vtt +10 -0
  123. package/test/test/assets/thumbnails.vtt +10 -0
  124. package/test/test/util/dash_parser_util.js +2 -0
  125. package/test/test/util/fake_ad.js +21 -0
  126. package/test/test/util/fake_media_source_engine.js +7 -0
  127. package/test/test/util/id3_generator.js +100 -0
  128. package/test/test/util/manifest_generator.js +3 -6
  129. package/test/test/util/simple_fakes.js +4 -0
  130. package/test/test/util/stream_generator.js +38 -0
  131. package/test/test/util/test_scheme.js +43 -14
  132. package/test/text/vtt_text_parser_unit.js +11 -0
  133. package/test/ui/text_displayer_layout_unit.js +14 -0
  134. package/test/util/id3_utils_unit.js +116 -0
  135. package/test/util/ts_parser_unit.js +77 -0
  136. package/ui/less/containers.less +7 -0
  137. 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);
@@ -26,6 +26,9 @@ describe('MediaSourceEngine', () => {
26
26
  */
27
27
  let textDisplayer;
28
28
 
29
+ /** @type {!jasmine.Spy} */
30
+ let onMetadata;
31
+
29
32
  beforeAll(() => {
30
33
  video = shaka.test.UiUtils.createVideoElement();
31
34
  document.body.appendChild(video);
@@ -37,10 +40,14 @@ describe('MediaSourceEngine', () => {
37
40
 
38
41
  textDisplayer = new shaka.test.FakeTextDisplayer();
39
42
 
43
+ onMetadata = jasmine.createSpy('onMetadata');
44
+
40
45
  mediaSourceEngine = new shaka.media.MediaSourceEngine(
41
46
  video,
42
- new shaka.media.ClosedCaptionParser(),
43
- textDisplayer);
47
+ textDisplayer,
48
+ shaka.test.Util.spyFunc(onMetadata));
49
+ const config = shaka.util.PlayerConfiguration.createDefault().mediaSource;
50
+ mediaSourceEngine.configure(config);
44
51
 
45
52
  mediaSource = /** @type {?} */(mediaSourceEngine)['mediaSource_'];
46
53
  expect(video.src).toBeTruthy();
@@ -383,9 +390,9 @@ describe('MediaSourceEngine', () => {
383
390
  const initObject = new Map();
384
391
  initObject.set(ContentType.VIDEO, getFakeStream(metadata.video));
385
392
  initObject.set(ContentType.TEXT, getFakeStream(metadata.text));
386
- // Call with forceTransmuxTS = true, so that it will transmux even on
393
+ // Call with forceTransmux = true, so that it will transmux even on
387
394
  // platforms with native TS support.
388
- await mediaSourceEngine.init(initObject, /* forceTransmuxTS= */ true);
395
+ await mediaSourceEngine.init(initObject, /* forceTransmux= */ true);
389
396
  mediaSourceEngine.setSelectedClosedCaptionId('CC1');
390
397
  await append(ContentType.VIDEO, 0);
391
398
 
@@ -420,7 +427,7 @@ describe('MediaSourceEngine', () => {
420
427
  initObject.set(videoType, getFakeStream(metadata.video));
421
428
 
422
429
  await mediaSourceEngine.init(
423
- initObject, /* forceTransmuxTS= */ false, /* sequenceMode= */ true);
430
+ initObject, /* forceTransmux= */ false, /* sequenceMode= */ true);
424
431
  await mediaSourceEngine.setDuration(presentationDuration);
425
432
  await mediaSourceEngine.setStreamProperties(
426
433
  videoType,
@@ -455,7 +462,7 @@ describe('MediaSourceEngine', () => {
455
462
  const initObject = new Map();
456
463
  initObject.set(ContentType.VIDEO, getFakeStream(metadata.video));
457
464
 
458
- await mediaSourceEngine.init(initObject, /* forceTransmuxTS= */ false);
465
+ await mediaSourceEngine.init(initObject, /* forceTransmux= */ false);
459
466
  await mediaSourceEngine.setDuration(presentationDuration);
460
467
  await appendInitWithClosedCaptions(ContentType.VIDEO);
461
468
  mediaSourceEngine.setSelectedClosedCaptionId('CC1');
@@ -463,4 +470,62 @@ describe('MediaSourceEngine', () => {
463
470
 
464
471
  expect(textDisplayer.appendSpy).toHaveBeenCalled();
465
472
  });
473
+
474
+ it('extracts ID3 metadata from TS', async () => {
475
+ metadata = shaka.test.TestScheme.DATA['id3-metadata_ts'];
476
+ generators = shaka.test.TestScheme.GENERATORS['id3-metadata_ts'];
477
+
478
+ const audioType = ContentType.AUDIO;
479
+ const initObject = new Map();
480
+ initObject.set(audioType, getFakeStream(metadata.audio));
481
+ await mediaSourceEngine.init(initObject, /* forceTransmux= */ false);
482
+ await append(ContentType.AUDIO, 0);
483
+
484
+ expect(onMetadata).toHaveBeenCalled();
485
+ });
486
+
487
+ it('extracts ID3 metadata from TS when transmuxing', async () => {
488
+ metadata = shaka.test.TestScheme.DATA['id3-metadata_ts'];
489
+ generators = shaka.test.TestScheme.GENERATORS['id3-metadata_ts'];
490
+
491
+ const audioType = ContentType.AUDIO;
492
+ const initObject = new Map();
493
+ initObject.set(audioType, getFakeStream(metadata.audio));
494
+ await mediaSourceEngine.init(initObject, /* forceTransmux= */ true);
495
+ await append(ContentType.AUDIO, 0);
496
+
497
+ expect(onMetadata).toHaveBeenCalled();
498
+ });
499
+
500
+ it('extracts ID3 metadata from AAC', async () => {
501
+ if (!MediaSource.isTypeSupported('audio/aac')) {
502
+ return;
503
+ }
504
+ metadata = shaka.test.TestScheme.DATA['id3-metadata_aac'];
505
+ generators = shaka.test.TestScheme.GENERATORS['id3-metadata_aac'];
506
+
507
+ const audioType = ContentType.AUDIO;
508
+ const initObject = new Map();
509
+ initObject.set(audioType, getFakeStream(metadata.audio));
510
+ await mediaSourceEngine.init(initObject, /* forceTransmux= */ false);
511
+ await append(ContentType.AUDIO, 0);
512
+
513
+ expect(onMetadata).toHaveBeenCalled();
514
+ });
515
+
516
+ it('extracts ID3 metadata from AAC when transmuxing', async () => {
517
+ if (!MediaSource.isTypeSupported('audio/aac')) {
518
+ return;
519
+ }
520
+ metadata = shaka.test.TestScheme.DATA['id3-metadata_aac'];
521
+ generators = shaka.test.TestScheme.GENERATORS['id3-metadata_aac'];
522
+
523
+ const audioType = ContentType.AUDIO;
524
+ const initObject = new Map();
525
+ initObject.set(audioType, getFakeStream(metadata.audio));
526
+ await mediaSourceEngine.init(initObject, /* forceTransmux= */ true);
527
+ await append(ContentType.AUDIO, 0);
528
+
529
+ expect(onMetadata).toHaveBeenCalled();
530
+ });
466
531
  });
@@ -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;