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.
- package/.github/workflows/build-and-test.yaml +7 -0
- package/.github/workflows/sync-labels.yaml +1 -1
- package/.github/workflows/update-issues.yaml +1 -1
- package/AUTHORS +4 -0
- package/CHANGELOG.md +114 -74
- package/CONTRIBUTORS +4 -0
- package/README.md +14 -0
- package/conditional/dummy_cast_proxy.js +120 -0
- package/demo/common/assets.js +58 -0
- package/demo/common/message_ids.js +22 -1
- package/demo/config.js +89 -5
- package/demo/demo.less +7 -1
- package/demo/index.html +8 -1
- package/demo/locales/en.json +21 -1
- package/demo/locales/source.json +82 -2
- package/demo/main.js +57 -1
- package/demo/search.js +2 -0
- package/demo/visualizer.js +337 -0
- package/demo/visualizer_button.js +103 -0
- package/dist/controls.css +1 -1
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +107 -88
- package/dist/demo.compiled.debug.map +4 -4
- package/dist/demo.compiled.js +107 -88
- package/dist/demo.compiled.map +4 -4
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +21 -13
- package/dist/locales.js +31 -31
- package/dist/receiver.compiled.debug.js +75 -77
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +75 -77
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +181 -27
- package/dist/shaka-player.compiled.debug.d.ts +181 -27
- package/dist/shaka-player.compiled.debug.externs.js +169 -31
- package/dist/shaka-player.compiled.debug.js +510 -450
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +169 -31
- package/dist/shaka-player.compiled.js +912 -874
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +181 -27
- package/dist/shaka-player.ui.debug.d.ts +181 -27
- package/dist/shaka-player.ui.debug.externs.js +169 -31
- package/dist/shaka-player.ui.debug.js +533 -471
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +169 -31
- package/dist/shaka-player.ui.js +1073 -1036
- package/dist/shaka-player.ui.map +4 -4
- package/docs/design/lcevc-architecture.png +0 -0
- package/docs/design/lcevc-demo.png +0 -0
- package/docs/design/lcevc-integration.md +72 -0
- package/docs/tutorials/fairplay.md +7 -0
- package/docs/tutorials/faq.md +1 -1
- package/docs/tutorials/ui.md +7 -7
- package/docs/tutorials/upgrade.md +6 -0
- package/externs/ima.js +13 -1
- package/externs/lcevc.js +81 -0
- package/externs/mux.js +1 -42
- package/externs/shaka/ads.js +11 -1
- package/externs/shaka/manifest_parser.js +5 -1
- package/externs/shaka/mp4_parser.js +3 -0
- package/externs/shaka/player.js +178 -15
- package/lib/abr/simple_abr_manager.js +7 -0
- package/lib/ads/ad_manager.js +0 -8
- package/lib/ads/client_side_ad.js +15 -0
- package/lib/ads/client_side_ad_manager.js +21 -8
- package/lib/ads/server_side_ad.js +15 -0
- package/lib/ads/server_side_ad_manager.js +3 -3
- package/lib/cast/cast_utils.js +2 -0
- package/lib/cea/dummy_cea_parser.js +28 -0
- package/lib/cea/i_cea_parser.js +14 -2
- package/lib/cea/mp4_cea_parser.js +108 -5
- package/lib/config/auto_show_text.js +33 -0
- package/lib/dash/dash_parser.js +1 -1
- package/lib/hls/hls_parser.js +808 -384
- package/lib/lcevc/lcevc_dil.js +152 -0
- package/lib/media/closed_caption_parser.js +9 -6
- package/lib/media/drm_engine.js +14 -6
- package/lib/media/media_source_capabilities.js +36 -0
- package/lib/media/media_source_engine.js +113 -28
- package/lib/media/presentation_timeline.js +26 -11
- package/lib/media/segment_reference.js +49 -1
- package/lib/media/streaming_engine.js +142 -12
- package/lib/media/transmuxer.js +78 -21
- package/lib/offline/storage.js +1 -0
- package/lib/player.js +456 -77
- package/lib/polyfill/media_capabilities.js +9 -5
- package/lib/polyfill/patchedmediakeys_apple.js +63 -4
- package/lib/text/vtt_text_parser.js +35 -1
- package/lib/util/dom_utils.js +0 -1
- package/lib/util/error.js +17 -0
- package/lib/util/fake_event.js +2 -0
- package/lib/util/id3_utils.js +300 -0
- package/lib/util/mime_utils.js +3 -6
- package/lib/util/mp4_box_parsers.js +39 -5
- package/lib/util/mp4_parser.js +35 -0
- package/lib/util/player_configuration.js +23 -1
- package/lib/util/stream_utils.js +5 -3
- package/lib/util/ts_parser.js +499 -0
- package/maintained-branches.md +2 -2
- package/package.json +7 -4
- package/roadmap.md +5 -3
- package/test/ads/ad_manager_unit.js +182 -29
- package/test/cast/cast_utils_unit.js +3 -1
- package/test/dash/dash_parser_content_protection_unit.js +1 -0
- package/test/dash/dash_parser_live_unit.js +1 -0
- package/test/dash/dash_parser_manifest_unit.js +93 -1
- package/test/dash/dash_parser_segment_base_unit.js +1 -0
- package/test/dash/dash_parser_segment_list_unit.js +1 -0
- package/test/dash/dash_parser_segment_template_unit.js +1 -0
- package/test/demo/demo_unit.js +2 -1
- package/test/hls/hls_live_unit.js +253 -19
- package/test/hls/hls_parser_unit.js +631 -12
- package/test/media/closed_caption_parser_unit.js +2 -1
- package/test/media/drm_engine_integration.js +3 -1
- package/test/media/media_source_engine_integration.js +71 -6
- package/test/media/media_source_engine_unit.js +10 -5
- package/test/media/streaming_engine_integration.js +4 -1
- package/test/media/streaming_engine_unit.js +144 -1
- package/test/media/transmuxer_integration.js +21 -10
- package/test/player_integration.js +224 -82
- package/test/player_unit.js +5 -2
- package/test/polyfill/media_capabilities_unit.js +21 -23
- package/test/polyfill/patchedmediakeys_apple_unit.js +130 -0
- package/test/test/assets/id3-metadata.aac +0 -0
- package/test/test/assets/id3-metadata.ts +0 -0
- package/test/test/assets/thumbnails-sprites.vtt +10 -0
- package/test/test/assets/thumbnails.vtt +10 -0
- package/test/test/util/dash_parser_util.js +2 -0
- package/test/test/util/fake_ad.js +21 -0
- package/test/test/util/fake_media_source_engine.js +7 -0
- package/test/test/util/id3_generator.js +100 -0
- package/test/test/util/manifest_generator.js +3 -6
- package/test/test/util/simple_fakes.js +4 -0
- package/test/test/util/stream_generator.js +38 -0
- package/test/test/util/test_scheme.js +43 -14
- package/test/text/vtt_text_parser_unit.js +11 -0
- package/test/util/id3_utils_unit.js +116 -0
- package/test/util/ts_parser_unit.js +77 -0
- package/third_party/closure-uri/uri.js +1 -1
- package/ui/less/containers.less +7 -0
- 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
|
|
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
|
-
|
|
103
|
-
|
|
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
|
|
453
|
+
// Call with forceTransmux = true, so that it will transmux even on
|
|
447
454
|
// platforms with native TS support.
|
|
448
|
-
await mediaSourceEngine.init(initObject, /*
|
|
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, /*
|
|
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, /*
|
|
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.
|
|
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, /*
|
|
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, /*
|
|
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
|
-
/**
|
|
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('
|
|
63
|
-
const
|
|
64
|
-
(type, codecs) => shaka.media.Transmuxer.
|
|
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
|
-
|
|
71
|
+
convertCodecs(ContentType.VIDEO, transportStreamVideoMimeType);
|
|
69
72
|
const convertedAudioCodecs =
|
|
70
|
-
|
|
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
|
-
|
|
86
|
+
convertCodecs(
|
|
80
87
|
ContentType.VIDEO, 'video/mp2t; codecs="avc1.100.42"'))
|
|
81
88
|
.toBe('video/mp4; codecs="avc1.64002a"');
|
|
82
89
|
expect(
|
|
83
|
-
|
|
90
|
+
convertCodecs(ContentType.VIDEO, 'video/mp2t; codecs="avc1.77.80"'))
|
|
84
91
|
.toBe('video/mp4; codecs="avc1.4d0050"');
|
|
85
92
|
expect(
|
|
86
|
-
|
|
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(
|
|
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;
|