shaka-player 3.2.2 → 3.3.2
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/ISSUE_TEMPLATE/bug_report.md +1 -1
- package/.github/ISSUE_TEMPLATE/feature_request.md +1 -1
- package/.github/ISSUE_TEMPLATE/question.md +1 -1
- package/.github/workflows/build_and_test.yaml +73 -0
- package/.github/workflows/release.yaml +53 -0
- package/AUTHORS +6 -5
- package/CHANGELOG.md +314 -1
- package/CONTRIBUTORS +4 -2
- package/build/checkversion.py +1 -1
- package/build/conformance.textproto +183 -169
- package/build/test.py +6 -0
- package/build/types/core +3 -0
- package/build/types/offline +1 -0
- package/build/types/polyfill +1 -0
- package/build/types/ui +2 -0
- package/demo/common/assets.js +18 -0
- package/demo/common/message_ids.js +13 -1
- package/demo/config.js +31 -1
- package/demo/index.html +1 -1
- package/demo/locales/en.json +12 -0
- package/demo/locales/source.json +48 -0
- package/dist/controls.css +26 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +19 -16
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +19 -16
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +21 -14
- package/dist/locales.js +31 -29
- package/dist/receiver.compiled.debug.js +11 -9
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +11 -9
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +232 -18
- package/dist/shaka-player.compiled.debug.d.ts +232 -18
- package/dist/shaka-player.compiled.debug.externs.js +293 -17
- package/dist/shaka-player.compiled.debug.js +481 -422
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +293 -17
- package/dist/shaka-player.compiled.js +846 -817
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +250 -30
- package/dist/shaka-player.ui.debug.d.ts +250 -30
- package/dist/shaka-player.ui.debug.externs.js +322 -28
- package/dist/shaka-player.ui.debug.js +630 -553
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +322 -28
- package/dist/shaka-player.ui.js +1048 -1006
- package/dist/shaka-player.ui.map +4 -4
- package/docs/design/bg-fetch-after.gv +29 -0
- package/docs/design/bg-fetch-after.gv.png +0 -0
- package/docs/design/bg-fetch-before.gv +18 -0
- package/docs/design/bg-fetch-before.gv.png +0 -0
- package/docs/design/bg-fetch.md +134 -0
- package/docs/tutorials/blob-url.md +15 -0
- package/docs/tutorials/index.json +1 -0
- package/docs/tutorials/ui-customization.md +66 -0
- package/docs/tutorials/ui.md +5 -5
- package/externs/cmcd.js +186 -0
- package/externs/ima.js +3 -0
- package/externs/shaka/ads.js +5 -0
- package/externs/shaka/manifest.js +12 -1
- package/externs/shaka/manifest_parser.js +10 -1
- package/externs/shaka/net.js +20 -4
- package/externs/shaka/offline.js +31 -2
- package/externs/shaka/player.js +138 -5
- package/externs/shaka/text.js +24 -0
- package/externs/webcrypto.js +18 -0
- package/karma.conf.js +19 -15
- package/lib/abr/ewma.js +14 -0
- package/lib/abr/ewma_bandwidth_estimator.js +16 -2
- package/lib/abr/simple_abr_manager.js +6 -0
- package/lib/ads/ad_manager.js +1 -0
- package/lib/ads/client_side_ad.js +8 -0
- package/lib/ads/client_side_ad_manager.js +8 -4
- package/lib/ads/server_side_ad.js +7 -0
- package/lib/cast/cast_proxy.js +3 -0
- package/lib/cast/cast_receiver.js +3 -0
- package/lib/cast/cast_utils.js +1 -0
- package/lib/dash/dash_parser.js +79 -33
- package/lib/dash/segment_base.js +25 -1
- package/lib/dash/segment_list.js +18 -9
- package/lib/dash/segment_template.js +23 -10
- package/lib/hls/hls_parser.js +116 -23
- package/lib/hls/manifest_text_parser.js +1 -0
- package/lib/media/quality_observer.js +274 -0
- package/lib/media/region_observer.js +39 -33
- package/lib/media/region_timeline.js +17 -19
- package/lib/media/segment_index.js +8 -1
- package/lib/media/segment_reference.js +37 -4
- package/lib/media/streaming_engine.js +47 -6
- package/lib/net/http_fetch_plugin.js +31 -11
- package/lib/net/http_plugin_utils.js +1 -0
- package/lib/net/http_xhr_plugin.js +40 -14
- package/lib/net/networking_engine.js +85 -18
- package/lib/offline/download_info.js +68 -0
- package/lib/offline/download_manager.js +13 -5
- package/lib/offline/indexeddb/base_storage_cell.js +35 -0
- package/lib/offline/indexeddb/storage_mechanism.js +1 -1
- package/lib/offline/indexeddb/v5_storage_cell.js +5 -0
- package/lib/offline/storage.js +401 -293
- package/lib/offline/stored_content_utils.js +2 -0
- package/lib/player.js +254 -27
- package/lib/polyfill/media_capabilities.js +30 -15
- package/lib/polyfill/random_uuid.js +52 -0
- package/lib/text/cue.js +18 -0
- package/lib/text/ttml_text_parser.js +109 -60
- package/lib/text/ui_text_displayer.js +19 -13
- package/lib/util/cmcd_manager.js +672 -0
- package/lib/util/error.js +6 -0
- package/lib/util/fake_event_target.js +21 -1
- package/lib/util/language_utils.js +12 -0
- package/lib/util/mutex.js +52 -0
- package/lib/util/periods.js +98 -64
- package/lib/util/player_configuration.js +18 -0
- package/lib/util/stream_utils.js +86 -10
- package/lib/util/xml_utils.js +19 -0
- package/package.json +3 -6
- package/shaka-player.uncompiled.js +3 -0
- package/test/dash/dash_parser_content_protection_unit.js +2 -0
- package/test/dash/dash_parser_live_unit.js +144 -1
- package/test/dash/dash_parser_manifest_unit.js +37 -2
- package/test/dash/dash_parser_segment_base_unit.js +2 -0
- package/test/dash/dash_parser_segment_list_unit.js +2 -1
- package/test/dash/dash_parser_segment_template_unit.js +2 -1
- package/test/hls/hls_live_unit.js +2 -0
- package/test/hls/hls_parser_unit.js +76 -0
- package/test/media/drm_engine_unit.js +3 -3
- package/test/media/quality_observer_unit.js +142 -0
- package/test/media/region_observer_unit.js +40 -5
- package/test/media/region_timeline_unit.js +40 -22
- package/test/media/segment_reference_unit.js +16 -1
- package/test/media/streaming_engine_integration.js +4 -2
- package/test/media/streaming_engine_unit.js +147 -0
- package/test/net/http_plugin_unit.js +20 -15
- package/test/offline/storage_integration.js +108 -4
- package/test/player_integration.js +140 -8
- package/test/player_src_equals_integration.js +41 -8
- package/test/player_unit.js +19 -9
- package/test/test/assets/chapters.vtt +2 -2
- package/test/test/assets/chapters2.vtt +10 -0
- package/test/test/util/dash_parser_util.js +4 -0
- package/test/test/util/fake_ad.js +7 -0
- package/test/test/util/fake_drm_engine.js +9 -2
- package/test/test/util/manifest_generator.js +3 -2
- package/test/test/util/test_scheme.js +14 -1
- package/test/test/util/ttml_utils.js +81 -0
- package/test/test/util/util.js +10 -7
- package/test/text/mp4_ttml_parser_unit.js +11 -18
- package/test/text/ttml_text_parser_unit.js +544 -228
- package/test/ui/text_displayer_layout_unit.js +8 -7
- package/test/ui/ui_unit.js +250 -0
- package/test/util/cmcd_manager_unit.js +291 -0
- package/test/util/stream_utils_unit.js +36 -30
- package/third_party/closure-uri/uri.js +21 -0
- package/ui/airplay_button.js +1 -0
- package/ui/audio_language_selection.js +7 -0
- package/ui/cast_button.js +1 -0
- package/ui/context_menu.js +109 -0
- package/ui/controls.js +44 -7
- package/ui/controls.less +1 -0
- package/ui/enums.js +4 -0
- package/ui/externs/ui.js +28 -1
- package/ui/fast_forward_button.js +14 -5
- package/ui/fullscreen_button.js +1 -0
- package/ui/less/buttons.less +0 -4
- package/ui/less/containers.less +74 -0
- package/ui/less/overflow_menu.less +0 -10
- package/ui/less/tooltip.less +97 -0
- package/ui/locales/en.json +1 -0
- package/ui/locales/source.json +4 -0
- package/ui/localization.js +12 -36
- package/ui/loop_button.js +5 -0
- package/ui/mute_button.js +2 -1
- package/ui/overflow_menu.js +5 -25
- package/ui/pip_button.js +5 -0
- package/ui/play_button.js +2 -2
- package/ui/playback_rate_selection.js +8 -9
- package/ui/resolution_selection.js +7 -0
- package/ui/rewind_button.js +15 -6
- package/ui/seek_bar.js +4 -2
- package/ui/settings_menu.js +36 -4
- package/ui/small_play_button.js +1 -0
- package/ui/statistics_button.js +253 -0
- package/ui/text_selection.js +12 -4
- package/ui/ui.js +30 -0
- package/ui/volume_bar.js +1 -1
- package/externs/media_capabilities.js +0 -16
|
@@ -87,6 +87,41 @@ describe('Player', () => {
|
|
|
87
87
|
});
|
|
88
88
|
}); // describe('attach')
|
|
89
89
|
|
|
90
|
+
describe('updateStartTime() in manifestparsed event handler', () => {
|
|
91
|
+
it('does not get segments prior to startTime', async () => {
|
|
92
|
+
player.addEventListener('manifestparsed', () => {
|
|
93
|
+
player.updateStartTime(24);
|
|
94
|
+
});
|
|
95
|
+
const results = {
|
|
96
|
+
requestedVideoSegment0: false,
|
|
97
|
+
requestedVideoSegment1: false,
|
|
98
|
+
requestedVideoSegment2: false,
|
|
99
|
+
};
|
|
100
|
+
const expected = {
|
|
101
|
+
requestedVideoSegment0: false,
|
|
102
|
+
requestedVideoSegment1: false,
|
|
103
|
+
requestedVideoSegment2: true,
|
|
104
|
+
};
|
|
105
|
+
player.getNetworkingEngine().registerRequestFilter(
|
|
106
|
+
(type, request) => {
|
|
107
|
+
if (request.uris[0] == 'test:sintel/video/0') {
|
|
108
|
+
results.requestedVideoSegment0 = true;
|
|
109
|
+
}
|
|
110
|
+
if (request.uris[0] == 'test:sintel/video/1') {
|
|
111
|
+
results.requestedVideoSegment1 = true;
|
|
112
|
+
}
|
|
113
|
+
if (request.uris[0] == 'test:sintel/video/2') {
|
|
114
|
+
results.requestedVideoSegment2 = true;
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
await player.load('test:sintel_compiled');
|
|
118
|
+
video.play();
|
|
119
|
+
await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 25, 30);
|
|
120
|
+
expect(results).toEqual(expected);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
|
|
90
125
|
describe('getStats', () => {
|
|
91
126
|
it('gives stats about current stream', async () => {
|
|
92
127
|
// This is tested more in player_unit.js. This is here to test the public
|
|
@@ -652,6 +687,70 @@ describe('Player', () => {
|
|
|
652
687
|
});
|
|
653
688
|
});
|
|
654
689
|
|
|
690
|
+
describe('mediaQualityChanges', () => {
|
|
691
|
+
/** @type {!jasmine.Spy} */
|
|
692
|
+
let onQualityChange;
|
|
693
|
+
/** @type {!shaka.test.Waiter} */
|
|
694
|
+
let waiter;
|
|
695
|
+
|
|
696
|
+
const qualityChange1 = jasmine.objectContaining({
|
|
697
|
+
mediaQuality: jasmine.objectContaining({
|
|
698
|
+
bandwidth: 1,
|
|
699
|
+
codecs: 'mp4a.40.2',
|
|
700
|
+
contentType: 'audio',
|
|
701
|
+
mimeType: 'audio/mp4',
|
|
702
|
+
}),
|
|
703
|
+
type: 'mediaqualitychanged',
|
|
704
|
+
position: jasmine.any(Number),
|
|
705
|
+
});
|
|
706
|
+
const qualityChange2 = jasmine.objectContaining({
|
|
707
|
+
mediaQuality: jasmine.objectContaining({
|
|
708
|
+
bandwidth: 1,
|
|
709
|
+
codecs: 'avc1.42c01e',
|
|
710
|
+
contentType: 'video',
|
|
711
|
+
mimeType: 'video/mp4',
|
|
712
|
+
}),
|
|
713
|
+
type: 'mediaqualitychanged',
|
|
714
|
+
position: jasmine.any(Number),
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
beforeEach(() => {
|
|
718
|
+
onQualityChange = jasmine.createSpy('onQualityChange');
|
|
719
|
+
// const spyFunc = Util.spyFunc(onQualityChange);
|
|
720
|
+
player.addEventListener('mediaqualitychanged',
|
|
721
|
+
Util.spyFunc(onQualityChange));
|
|
722
|
+
waiter = new shaka.test.Waiter(eventManager)
|
|
723
|
+
.timeoutAfter(10)
|
|
724
|
+
.failOnTimeout(true);
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
it('emits audio/video quality changes at start when enabled', async () => {
|
|
728
|
+
player.configure('streaming.observeQualityChanges', true);
|
|
729
|
+
|
|
730
|
+
await player.load('test:sintel_compiled');
|
|
731
|
+
video.play();
|
|
732
|
+
|
|
733
|
+
// Wait for the video to start playback. If it takes longer than 10
|
|
734
|
+
// seconds, fail the test.
|
|
735
|
+
await waiter.waitForMovementOrFailOnTimeout(video, 10);
|
|
736
|
+
expect(onQualityChange).toHaveBeenCalledTimes(2);
|
|
737
|
+
expect(onQualityChange).toHaveBeenCalledWith(qualityChange1);
|
|
738
|
+
expect(onQualityChange).toHaveBeenCalledWith(qualityChange2);
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
it('does not emit quality changes at start when disabled', async () => {
|
|
742
|
+
player.configure('streaming.observeQualityChanges', false);
|
|
743
|
+
|
|
744
|
+
await player.load('test:sintel_compiled');
|
|
745
|
+
video.play();
|
|
746
|
+
|
|
747
|
+
// Wait for the video to start playback. If it takes longer than 10
|
|
748
|
+
// seconds, fail the test.
|
|
749
|
+
await waiter.waitForMovementOrFailOnTimeout(video, 10);
|
|
750
|
+
expect(onQualityChange).not.toHaveBeenCalled();
|
|
751
|
+
});
|
|
752
|
+
});
|
|
753
|
+
|
|
655
754
|
describe('seek range', () => {
|
|
656
755
|
// Regression test for Issue #3675.
|
|
657
756
|
it('has a reasonable seek range after going from live to VOD', async () => {
|
|
@@ -964,9 +1063,9 @@ describe('Player', () => {
|
|
|
964
1063
|
it('add external chapters in vtt format', async () => {
|
|
965
1064
|
await player.load('test:sintel_no_text_compiled');
|
|
966
1065
|
const locationUri = new goog.Uri(location.href);
|
|
967
|
-
const
|
|
968
|
-
const
|
|
969
|
-
await player.addChaptersTrack(
|
|
1066
|
+
const partialUri1 = new goog.Uri('/base/test/test/assets/chapters.vtt');
|
|
1067
|
+
const absoluteUri1 = locationUri.resolve(partialUri1);
|
|
1068
|
+
await player.addChaptersTrack(absoluteUri1.toString(), 'en');
|
|
970
1069
|
|
|
971
1070
|
await shaka.test.Util.delay(1.5);
|
|
972
1071
|
|
|
@@ -979,11 +1078,44 @@ describe('Player', () => {
|
|
|
979
1078
|
const chapter2 = chapters[1];
|
|
980
1079
|
expect(chapter2.title).toBe('Chapter 2');
|
|
981
1080
|
expect(chapter2.startTime).toBe(5);
|
|
982
|
-
expect(chapter2.endTime).toBe(
|
|
1081
|
+
expect(chapter2.endTime).toBe(10);
|
|
983
1082
|
const chapter3 = chapters[2];
|
|
984
1083
|
expect(chapter3.title).toBe('Chapter 3');
|
|
985
|
-
expect(chapter3.startTime).toBe(
|
|
986
|
-
expect(chapter3.endTime).toBe(
|
|
1084
|
+
expect(chapter3.startTime).toBe(10);
|
|
1085
|
+
expect(chapter3.endTime).toBe(20);
|
|
1086
|
+
|
|
1087
|
+
const partialUri2 = new goog.Uri('/base/test/test/assets/chapters2.vtt');
|
|
1088
|
+
const absoluteUri2 = locationUri.resolve(partialUri2);
|
|
1089
|
+
await player.addChaptersTrack(absoluteUri2.toString(), 'en');
|
|
1090
|
+
|
|
1091
|
+
await shaka.test.Util.delay(1.5);
|
|
1092
|
+
|
|
1093
|
+
const chaptersUpdated = player.getChapters('en');
|
|
1094
|
+
expect(chaptersUpdated.length).toBe(6);
|
|
1095
|
+
const chapterUpdated1 = chaptersUpdated[0];
|
|
1096
|
+
expect(chapterUpdated1.title).toBe('Chapter 1');
|
|
1097
|
+
expect(chapterUpdated1.startTime).toBe(0);
|
|
1098
|
+
expect(chapterUpdated1.endTime).toBe(5);
|
|
1099
|
+
const chapterUpdated2 = chaptersUpdated[1];
|
|
1100
|
+
expect(chapterUpdated2.title).toBe('Chapter 2');
|
|
1101
|
+
expect(chapterUpdated2.startTime).toBe(5);
|
|
1102
|
+
expect(chapterUpdated2.endTime).toBe(10);
|
|
1103
|
+
const chapterUpdated3 = chaptersUpdated[2];
|
|
1104
|
+
expect(chapterUpdated3.title).toBe('Chapter 3');
|
|
1105
|
+
expect(chapterUpdated3.startTime).toBe(10);
|
|
1106
|
+
expect(chapterUpdated3.endTime).toBe(20);
|
|
1107
|
+
const chapterUpdated4 = chaptersUpdated[3];
|
|
1108
|
+
expect(chapterUpdated4.title).toBe('Chapter 4');
|
|
1109
|
+
expect(chapterUpdated4.startTime).toBe(20);
|
|
1110
|
+
expect(chapterUpdated4.endTime).toBe(30);
|
|
1111
|
+
const chapterUpdated5 = chaptersUpdated[4];
|
|
1112
|
+
expect(chapterUpdated5.title).toBe('Chapter 5');
|
|
1113
|
+
expect(chapterUpdated5.startTime).toBe(30);
|
|
1114
|
+
expect(chapterUpdated5.endTime).toBe(40);
|
|
1115
|
+
const chapterUpdated6 = chaptersUpdated[5];
|
|
1116
|
+
expect(chapterUpdated6.title).toBe('Chapter 6');
|
|
1117
|
+
expect(chapterUpdated6.startTime).toBe(40);
|
|
1118
|
+
expect(chapterUpdated6.endTime).toBe(61.349);
|
|
987
1119
|
});
|
|
988
1120
|
|
|
989
1121
|
it('add external chapters in srt format', async () => {
|
|
@@ -991,11 +1123,11 @@ describe('Player', () => {
|
|
|
991
1123
|
const locationUri = new goog.Uri(location.href);
|
|
992
1124
|
const partialUri = new goog.Uri('/base/test/test/assets/chapters.srt');
|
|
993
1125
|
const absoluteUri = locationUri.resolve(partialUri);
|
|
994
|
-
await player.addChaptersTrack(absoluteUri.toString(), '
|
|
1126
|
+
await player.addChaptersTrack(absoluteUri.toString(), 'es');
|
|
995
1127
|
|
|
996
1128
|
await shaka.test.Util.delay(1.5);
|
|
997
1129
|
|
|
998
|
-
const chapters = player.getChapters('
|
|
1130
|
+
const chapters = player.getChapters('es');
|
|
999
1131
|
expect(chapters.length).toBe(3);
|
|
1000
1132
|
const chapter1 = chapters[0];
|
|
1001
1133
|
expect(chapter1.title).toBe('Chapter 1');
|
|
@@ -330,9 +330,9 @@ describe('Player Src Equals', () => {
|
|
|
330
330
|
await loadWithSrcEquals(SMALL_MP4_CONTENT_URI, /* startTime= */ null);
|
|
331
331
|
|
|
332
332
|
const locationUri = new goog.Uri(location.href);
|
|
333
|
-
const
|
|
334
|
-
const
|
|
335
|
-
await player.addChaptersTrack(
|
|
333
|
+
const partialUri1 = new goog.Uri('/base/test/test/assets/chapters.vtt');
|
|
334
|
+
const absoluteUri1 = locationUri.resolve(partialUri1);
|
|
335
|
+
await player.addChaptersTrack(absoluteUri1.toString(), 'en');
|
|
336
336
|
|
|
337
337
|
await shaka.test.Util.delay(1.5);
|
|
338
338
|
|
|
@@ -345,11 +345,44 @@ describe('Player Src Equals', () => {
|
|
|
345
345
|
const chapter2 = chapters[1];
|
|
346
346
|
expect(chapter2.title).toBe('Chapter 2');
|
|
347
347
|
expect(chapter2.startTime).toBe(5);
|
|
348
|
-
expect(chapter2.endTime).toBe(
|
|
348
|
+
expect(chapter2.endTime).toBe(10);
|
|
349
349
|
const chapter3 = chapters[2];
|
|
350
350
|
expect(chapter3.title).toBe('Chapter 3');
|
|
351
|
-
expect(chapter3.startTime).toBe(
|
|
352
|
-
expect(chapter3.endTime).toBe(
|
|
351
|
+
expect(chapter3.startTime).toBe(10);
|
|
352
|
+
expect(chapter3.endTime).toBe(20);
|
|
353
|
+
|
|
354
|
+
const partialUri2 = new goog.Uri('/base/test/test/assets/chapters2.vtt');
|
|
355
|
+
const absoluteUri2 = locationUri.resolve(partialUri2);
|
|
356
|
+
await player.addChaptersTrack(absoluteUri2.toString(), 'en');
|
|
357
|
+
|
|
358
|
+
await shaka.test.Util.delay(1.5);
|
|
359
|
+
|
|
360
|
+
const chaptersUpdated = player.getChapters('en');
|
|
361
|
+
expect(chaptersUpdated.length).toBe(6);
|
|
362
|
+
const chapterUpdated1 = chaptersUpdated[0];
|
|
363
|
+
expect(chapterUpdated1.title).toBe('Chapter 1');
|
|
364
|
+
expect(chapterUpdated1.startTime).toBe(0);
|
|
365
|
+
expect(chapterUpdated1.endTime).toBe(5);
|
|
366
|
+
const chapterUpdated2 = chaptersUpdated[1];
|
|
367
|
+
expect(chapterUpdated2.title).toBe('Chapter 2');
|
|
368
|
+
expect(chapterUpdated2.startTime).toBe(5);
|
|
369
|
+
expect(chapterUpdated2.endTime).toBe(10);
|
|
370
|
+
const chapterUpdated3 = chaptersUpdated[2];
|
|
371
|
+
expect(chapterUpdated3.title).toBe('Chapter 3');
|
|
372
|
+
expect(chapterUpdated3.startTime).toBe(10);
|
|
373
|
+
expect(chapterUpdated3.endTime).toBe(20);
|
|
374
|
+
const chapterUpdated4 = chaptersUpdated[3];
|
|
375
|
+
expect(chapterUpdated4.title).toBe('Chapter 4');
|
|
376
|
+
expect(chapterUpdated4.startTime).toBe(20);
|
|
377
|
+
expect(chapterUpdated4.endTime).toBe(30);
|
|
378
|
+
const chapterUpdated5 = chaptersUpdated[4];
|
|
379
|
+
expect(chapterUpdated5.title).toBe('Chapter 5');
|
|
380
|
+
expect(chapterUpdated5.startTime).toBe(30);
|
|
381
|
+
expect(chapterUpdated5.endTime).toBe(40);
|
|
382
|
+
const chapterUpdated6 = chaptersUpdated[5];
|
|
383
|
+
expect(chapterUpdated6.title).toBe('Chapter 6');
|
|
384
|
+
expect(chapterUpdated6.startTime).toBe(40);
|
|
385
|
+
expect(chapterUpdated6.endTime).toBe(61.349);
|
|
353
386
|
});
|
|
354
387
|
|
|
355
388
|
it('add external chapters in srt format', async () => {
|
|
@@ -358,11 +391,11 @@ describe('Player Src Equals', () => {
|
|
|
358
391
|
const locationUri = new goog.Uri(location.href);
|
|
359
392
|
const partialUri = new goog.Uri('/base/test/test/assets/chapters.srt');
|
|
360
393
|
const absoluteUri = locationUri.resolve(partialUri);
|
|
361
|
-
await player.addChaptersTrack(absoluteUri.toString(), '
|
|
394
|
+
await player.addChaptersTrack(absoluteUri.toString(), 'es');
|
|
362
395
|
|
|
363
396
|
await shaka.test.Util.delay(1.5);
|
|
364
397
|
|
|
365
|
-
const chapters = player.getChapters('
|
|
398
|
+
const chapters = player.getChapters('es');
|
|
366
399
|
expect(chapters.length).toBe(3);
|
|
367
400
|
const chapter1 = chapters[0];
|
|
368
401
|
expect(chapter1.title).toBe('Chapter 1');
|
package/test/player_unit.js
CHANGED
|
@@ -36,7 +36,7 @@ describe('Player', () => {
|
|
|
36
36
|
const originalLogWarn = shaka.log.warning;
|
|
37
37
|
const originalLogAlwaysWarn = shaka.log.alwaysWarn;
|
|
38
38
|
const originalIsTypeSupported = window.MediaSource.isTypeSupported;
|
|
39
|
-
const originalDecodingInfo =
|
|
39
|
+
const originalDecodingInfo = navigator.mediaCapabilities.decodingInfo;
|
|
40
40
|
|
|
41
41
|
const fakeManifestUri = 'fake-manifest-uri';
|
|
42
42
|
const fakeMimeType = 'application/test';
|
|
@@ -93,7 +93,7 @@ describe('Player', () => {
|
|
|
93
93
|
// Since this is not an integration test, we don't want MediaCapabilities to
|
|
94
94
|
// fail assertions based on browser support for types. Pretend that all
|
|
95
95
|
// video and audio types are supported.
|
|
96
|
-
|
|
96
|
+
navigator.mediaCapabilities.decodingInfo = async (config) => {
|
|
97
97
|
await Promise.resolve();
|
|
98
98
|
const videoType = config['video'] ?
|
|
99
99
|
config['video'].contentType.split('/')[0] : null;
|
|
@@ -177,7 +177,7 @@ describe('Player', () => {
|
|
|
177
177
|
shaka.log.alwaysWarn = originalLogAlwaysWarn;
|
|
178
178
|
window.MediaSource.isTypeSupported = originalIsTypeSupported;
|
|
179
179
|
shaka.media.ManifestParser.unregisterParserByMime(fakeMimeType);
|
|
180
|
-
|
|
180
|
+
navigator.mediaCapabilities.decodingInfo = originalDecodingInfo;
|
|
181
181
|
}
|
|
182
182
|
});
|
|
183
183
|
|
|
@@ -1092,8 +1092,8 @@ describe('Player', () => {
|
|
|
1092
1092
|
// Image tracks
|
|
1093
1093
|
manifest.addImageStream(53, (stream) => {
|
|
1094
1094
|
stream.originalId = 'thumbnail';
|
|
1095
|
-
stream.width =
|
|
1096
|
-
stream.height =
|
|
1095
|
+
stream.width = 200;
|
|
1096
|
+
stream.height = 400;
|
|
1097
1097
|
stream.bandwidth = 10;
|
|
1098
1098
|
stream.mimeType = 'image/jpeg';
|
|
1099
1099
|
stream.tilesLayout = '1x1';
|
|
@@ -1503,8 +1503,8 @@ describe('Player', () => {
|
|
|
1503
1503
|
audioBandwidth: null,
|
|
1504
1504
|
videoBandwidth: null,
|
|
1505
1505
|
bandwidth: 10,
|
|
1506
|
-
width:
|
|
1507
|
-
height:
|
|
1506
|
+
width: 200,
|
|
1507
|
+
height: 400,
|
|
1508
1508
|
frameRate: null,
|
|
1509
1509
|
pixelAspectRatio: null,
|
|
1510
1510
|
hdr: null,
|
|
@@ -2463,7 +2463,7 @@ describe('Player', () => {
|
|
|
2463
2463
|
it('throws CONTENT_UNSUPPORTED_BY_BROWSER', async () => {
|
|
2464
2464
|
window.MediaSource.isTypeSupported = (mimeType) => false;
|
|
2465
2465
|
|
|
2466
|
-
|
|
2466
|
+
navigator.mediaCapabilities.decodingInfo = async (config) => {
|
|
2467
2467
|
await Promise.resolve();
|
|
2468
2468
|
return {supported: false};
|
|
2469
2469
|
};
|
|
@@ -2827,7 +2827,7 @@ describe('Player', () => {
|
|
|
2827
2827
|
});
|
|
2828
2828
|
});
|
|
2829
2829
|
|
|
2830
|
-
|
|
2830
|
+
navigator.mediaCapabilities.decodingInfo = async (config) => {
|
|
2831
2831
|
await Promise.resolve();
|
|
2832
2832
|
const videoType = config['video'] ? config['video'].contentType : '';
|
|
2833
2833
|
if (videoType.includes('video') &&
|
|
@@ -3537,29 +3537,39 @@ describe('Player', () => {
|
|
|
3537
3537
|
|
|
3538
3538
|
await player.load(fakeManifestUri, 0, fakeMimeType);
|
|
3539
3539
|
|
|
3540
|
+
expect(player.getImageTracks()[0].width).toBe(100);
|
|
3541
|
+
expect(player.getImageTracks()[0].height).toBe(50);
|
|
3540
3542
|
const thumbnail0 = await player.getThumbnails(5, 0);
|
|
3541
3543
|
const thumbnail1 = await player.getThumbnails(5, 11);
|
|
3542
3544
|
const thumbnail2 = await player.getThumbnails(5, 21);
|
|
3543
3545
|
const thumbnail5 = await player.getThumbnails(5, 51);
|
|
3544
3546
|
expect(thumbnail0).toEqual(jasmine.objectContaining({
|
|
3547
|
+
imageHeight: 150,
|
|
3548
|
+
imageWidth: 200,
|
|
3545
3549
|
positionX: 0,
|
|
3546
3550
|
positionY: 0,
|
|
3547
3551
|
width: 100,
|
|
3548
3552
|
height: 50,
|
|
3549
3553
|
}));
|
|
3550
3554
|
expect(thumbnail1).toEqual(jasmine.objectContaining({
|
|
3555
|
+
imageHeight: 150,
|
|
3556
|
+
imageWidth: 200,
|
|
3551
3557
|
positionX: 100,
|
|
3552
3558
|
positionY: 0,
|
|
3553
3559
|
width: 100,
|
|
3554
3560
|
height: 50,
|
|
3555
3561
|
}));
|
|
3556
3562
|
expect(thumbnail2).toEqual(jasmine.objectContaining({
|
|
3563
|
+
imageHeight: 150,
|
|
3564
|
+
imageWidth: 200,
|
|
3557
3565
|
positionX: 0,
|
|
3558
3566
|
positionY: 50,
|
|
3559
3567
|
width: 100,
|
|
3560
3568
|
height: 50,
|
|
3561
3569
|
}));
|
|
3562
3570
|
expect(thumbnail5).toEqual(jasmine.objectContaining({
|
|
3571
|
+
imageHeight: 150,
|
|
3572
|
+
imageWidth: 200,
|
|
3563
3573
|
positionX: 100,
|
|
3564
3574
|
positionY: 100,
|
|
3565
3575
|
width: 100,
|
|
@@ -46,6 +46,8 @@ shaka.test.Dash = class {
|
|
|
46
46
|
|
|
47
47
|
const playerInterface = {
|
|
48
48
|
networkingEngine: networkingEngine,
|
|
49
|
+
modifyManifestRequest: (request, manifestInfo) => {},
|
|
50
|
+
modifySegmentRequest: (request, segmentInfo) => {},
|
|
49
51
|
filter: () => {},
|
|
50
52
|
makeTextStreamsForClosedCaptions: (manifest) => {},
|
|
51
53
|
onTimelineRegionAdded: fail, // Should not have any EventStream elements.
|
|
@@ -78,6 +80,8 @@ shaka.test.Dash = class {
|
|
|
78
80
|
|
|
79
81
|
const playerInterface = {
|
|
80
82
|
networkingEngine: networkingEngine,
|
|
83
|
+
modifyManifestRequest: (request, manifestInfo) => {},
|
|
84
|
+
modifySegmentRequest: (request, segmentInfo) => {},
|
|
81
85
|
filter: () => {},
|
|
82
86
|
makeTextStreamsForClosedCaptions: (manifest) => {},
|
|
83
87
|
onTimelineRegionAdded: fail, // Should not have any EventStream elements.
|
|
@@ -17,7 +17,7 @@ goog.require('shaka.test.Util');
|
|
|
17
17
|
*/
|
|
18
18
|
shaka.test.FakeDrmEngine = class {
|
|
19
19
|
constructor() {
|
|
20
|
-
/** @private {!Array.<
|
|
20
|
+
/** @private {!Array.<string>} */
|
|
21
21
|
this.offlineSessions_ = [];
|
|
22
22
|
/** @private {?shaka.extern.DrmInfo} */
|
|
23
23
|
this.drmInfo_ = null;
|
|
@@ -45,6 +45,13 @@ shaka.test.FakeDrmEngine = class {
|
|
|
45
45
|
// be returned.
|
|
46
46
|
this.getDrmInfo.and.callFake(() => this.drmInfo_);
|
|
47
47
|
|
|
48
|
+
/** @type {!jasmine.Spy} */
|
|
49
|
+
this.newInitData = jasmine.createSpy('newInitData');
|
|
50
|
+
this.newInitData.and.callFake((initDataType, initData) => {
|
|
51
|
+
const num = 1 + this.offlineSessions_.length;
|
|
52
|
+
this.offlineSessions_.push('session-' + num);
|
|
53
|
+
});
|
|
54
|
+
|
|
48
55
|
/** @type {!jasmine.Spy} */
|
|
49
56
|
this.getExpiration = jasmine.createSpy('getExpiration');
|
|
50
57
|
this.getExpiration.and.returnValue(Infinity);
|
|
@@ -90,7 +97,7 @@ shaka.test.FakeDrmEngine = class {
|
|
|
90
97
|
}
|
|
91
98
|
|
|
92
99
|
/**
|
|
93
|
-
* @param {!Array.<
|
|
100
|
+
* @param {!Array.<string>} sessions
|
|
94
101
|
*/
|
|
95
102
|
setSessionIds(sessions) {
|
|
96
103
|
// Copy the values to break the reference to the input value.
|
|
@@ -653,15 +653,16 @@ shaka.test.ManifestGenerator.Stream = class {
|
|
|
653
653
|
* @param {!Array.<string>} uris
|
|
654
654
|
* @param {number} startByte
|
|
655
655
|
* @param {?number} endByte
|
|
656
|
+
* @param {null|shaka.extern.MediaQualityInfo=} mediaQuality
|
|
656
657
|
*/
|
|
657
|
-
setInitSegmentReference(uris, startByte, endByte) {
|
|
658
|
+
setInitSegmentReference(uris, startByte, endByte, mediaQuality) {
|
|
658
659
|
goog.asserts.assert(this.manifest_,
|
|
659
660
|
'A top-level generated Manifest is required to use this method!');
|
|
660
661
|
|
|
661
662
|
const getUris = () => uris;
|
|
662
663
|
this.initSegmentReference_ =
|
|
663
664
|
new this.manifest_.shaka_.media.InitSegmentReference(
|
|
664
|
-
getUris, startByte, endByte);
|
|
665
|
+
getUris, startByte, endByte, mediaQuality);
|
|
665
666
|
}
|
|
666
667
|
|
|
667
668
|
/**
|
|
@@ -206,10 +206,23 @@ shaka.test.TestScheme = class {
|
|
|
206
206
|
* @param {string} name
|
|
207
207
|
*/
|
|
208
208
|
function addStreamInfo(stream, variant, data, contentType, name) {
|
|
209
|
+
const mediaQualityInfo = {
|
|
210
|
+
bandwidth: 1,
|
|
211
|
+
codecs: data[contentType].codecs || 'unknown',
|
|
212
|
+
contentType: contentType,
|
|
213
|
+
mimeType: data[contentType].mimeType,
|
|
214
|
+
audioSamplingRate: null,
|
|
215
|
+
frameRate: null,
|
|
216
|
+
height: null,
|
|
217
|
+
channelsCount: null,
|
|
218
|
+
pixelAspectRatio: null,
|
|
219
|
+
width: null,
|
|
220
|
+
};
|
|
209
221
|
stream.mimeType = data[contentType].mimeType;
|
|
210
222
|
stream.codecs = data[contentType].codecs;
|
|
211
223
|
stream.setInitSegmentReference(
|
|
212
|
-
['test:' + name + '/' + contentType + '/init'], 0, null
|
|
224
|
+
['test:' + name + '/' + contentType + '/init'], 0, null,
|
|
225
|
+
mediaQualityInfo);
|
|
213
226
|
stream.useSegmentTemplate(
|
|
214
227
|
'test:' + name + '/' + contentType + '/%d',
|
|
215
228
|
data[contentType].segmentDuration);
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
goog.provide('shaka.test.TtmlUtils');
|
|
8
|
+
|
|
9
|
+
shaka.test.TtmlUtils = class {
|
|
10
|
+
/**
|
|
11
|
+
* @param {!Array} expectedCues
|
|
12
|
+
* @param {!Array} actualCues
|
|
13
|
+
* @param {!Object} bodyProperties
|
|
14
|
+
* @param {Object=} divProperties
|
|
15
|
+
*/
|
|
16
|
+
static verifyHelper(expectedCues, actualCues, bodyProperties, divProperties) {
|
|
17
|
+
const mapExpected = (cue) => {
|
|
18
|
+
if (cue.region) {
|
|
19
|
+
cue.region = jasmine.objectContaining(cue.region);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (cue.nestedCues && (cue.nestedCues instanceof Array)) {
|
|
23
|
+
cue.nestedCues = cue.nestedCues.map(mapExpected);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (cue.isContainer == undefined) {
|
|
27
|
+
// If not specified to be true, check for isContainer to be false.
|
|
28
|
+
cue.isContainer = false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return jasmine.objectContaining(cue);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {!Object} properties
|
|
36
|
+
* @return {!shaka.extern.Cue}
|
|
37
|
+
*/
|
|
38
|
+
const makeContainer = (properties) => {
|
|
39
|
+
const region = {
|
|
40
|
+
id: '',
|
|
41
|
+
viewportAnchorX: 0,
|
|
42
|
+
viewportAnchorY: 0,
|
|
43
|
+
regionAnchorX: 0,
|
|
44
|
+
regionAnchorY: 0,
|
|
45
|
+
width: 100,
|
|
46
|
+
height: 100,
|
|
47
|
+
widthUnits: shaka.text.CueRegion.units.PERCENTAGE,
|
|
48
|
+
heightUnits: shaka.text.CueRegion.units.PERCENTAGE,
|
|
49
|
+
viewportAnchorUnits: shaka.text.CueRegion.units.PERCENTAGE,
|
|
50
|
+
scroll: '',
|
|
51
|
+
};
|
|
52
|
+
const containerCue = /** @type {!shaka.extern.Cue} */ ({
|
|
53
|
+
region,
|
|
54
|
+
nestedCues: jasmine.any(Object),
|
|
55
|
+
payload: '',
|
|
56
|
+
startTime: 0,
|
|
57
|
+
endTime: Infinity,
|
|
58
|
+
isContainer: true,
|
|
59
|
+
});
|
|
60
|
+
Object.assign(containerCue, properties);
|
|
61
|
+
return mapExpected(containerCue);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
if (expectedCues.length == 0 && !divProperties) {
|
|
65
|
+
expect(actualCues.length).toBe(0);
|
|
66
|
+
} else {
|
|
67
|
+
// Body.
|
|
68
|
+
expect(actualCues.length).toBe(1);
|
|
69
|
+
const body = actualCues[0];
|
|
70
|
+
expect(body).toEqual(makeContainer(bodyProperties));
|
|
71
|
+
|
|
72
|
+
// Div.
|
|
73
|
+
expect(body.nestedCues.length).toBe(1);
|
|
74
|
+
const div = body.nestedCues[0];
|
|
75
|
+
expect(div).toEqual(makeContainer(divProperties || bodyProperties));
|
|
76
|
+
|
|
77
|
+
// Cues.
|
|
78
|
+
expect(div.nestedCues).toEqual(expectedCues.map(mapExpected));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
package/test/test/util/util.js
CHANGED
|
@@ -401,10 +401,10 @@ shaka.test.Util = class {
|
|
|
401
401
|
* within the bounds of the viewport.
|
|
402
402
|
* @param {string} name An identifier for the screenshot. Use alphanumeric
|
|
403
403
|
* plus dash and underscore only.
|
|
404
|
-
* @param {number}
|
|
404
|
+
* @param {number} minSimilarity A minimum similarity score between 0 and 1.
|
|
405
405
|
* @return {!Promise}
|
|
406
406
|
*/
|
|
407
|
-
static async checkScreenshot(element, name,
|
|
407
|
+
static async checkScreenshot(element, name, minSimilarity=1) {
|
|
408
408
|
// Make sure the DOM is up-to-date and layout has settled before continuing.
|
|
409
409
|
// Without this delay, or with a shorter delay, we sometimes get missing
|
|
410
410
|
// elements in our UITextDisplayer tests on some platforms.
|
|
@@ -448,13 +448,16 @@ shaka.test.Util = class {
|
|
|
448
448
|
const buffer = await shaka.test.Util.fetch(
|
|
449
449
|
'/screenshot/diff' + parentUrlParams + paramsString);
|
|
450
450
|
const json = shaka.util.StringUtils.fromUTF8(buffer);
|
|
451
|
-
const
|
|
451
|
+
const similarity = /** @type {number} */(JSON.parse(json));
|
|
452
452
|
|
|
453
|
-
// If the
|
|
453
|
+
// If the minimum similarity is not met, you can review the new screenshot
|
|
454
454
|
// and the diff image in the screenshots folder. Look for images that end
|
|
455
|
-
// with "-new" and "-diff".
|
|
456
|
-
//
|
|
457
|
-
|
|
455
|
+
// with "-new" and "-diff". (NOTE: The diff is a pixel-wise diff for human
|
|
456
|
+
// review, and is not produced with the same structural similarity
|
|
457
|
+
// algorithm used to detect changes in the test.) If cropping doesn't work
|
|
458
|
+
// right, you can view the full-page screenshot in the image that ends with
|
|
459
|
+
// "-full".
|
|
460
|
+
expect(similarity).withContext(name).not.toBeLessThan(minSimilarity);
|
|
458
461
|
}
|
|
459
462
|
};
|
|
460
463
|
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
goog.require('shaka.test.TtmlUtils');
|
|
7
8
|
goog.require('shaka.test.Util');
|
|
8
9
|
goog.require('shaka.text.Mp4TtmlParser');
|
|
9
10
|
goog.require('shaka.util.BufferUtils');
|
|
@@ -48,7 +49,14 @@ describe('Mp4TtmlParser', () => {
|
|
|
48
49
|
parser.parseInit(ttmlInitSegment);
|
|
49
50
|
const time = {periodStart: 0, segmentStart: 0, segmentEnd: 0};
|
|
50
51
|
const ret = parser.parseMedia(ttmlSegmentMultipleMDAT, time);
|
|
51
|
-
|
|
52
|
+
// Bodies.
|
|
53
|
+
expect(ret.length).toBe(2);
|
|
54
|
+
// Divs.
|
|
55
|
+
expect(ret[0].nestedCues.length).toBe(1);
|
|
56
|
+
expect(ret[1].nestedCues.length).toBe(1);
|
|
57
|
+
// Cues.
|
|
58
|
+
expect(ret[0].nestedCues[0].nestedCues.length).toBe(10);
|
|
59
|
+
expect(ret[1].nestedCues[0].nestedCues.length).toBe(10);
|
|
52
60
|
});
|
|
53
61
|
|
|
54
62
|
it('accounts for offset', () => {
|
|
@@ -159,22 +167,7 @@ describe('Mp4TtmlParser', () => {
|
|
|
159
167
|
parser.parseInit(ttmlInitSegment);
|
|
160
168
|
const time = {periodStart: 0, segmentStart: 0, segmentEnd: 0};
|
|
161
169
|
const result = parser.parseMedia(ttmlSegment, time);
|
|
162
|
-
verifyHelper(
|
|
170
|
+
shaka.test.TtmlUtils.verifyHelper(
|
|
171
|
+
cues, result, {startTime: 23, endTime: 53.5});
|
|
163
172
|
});
|
|
164
|
-
|
|
165
|
-
function verifyHelper(/** !Array */ expected, /** !Array */ actual) {
|
|
166
|
-
const mapExpected = (cue) => {
|
|
167
|
-
if (cue.region) {
|
|
168
|
-
cue.region = jasmine.objectContaining(cue.region);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (cue.nestedCues) {
|
|
172
|
-
cue.nestedCues = cue.nestedCues.map(mapExpected);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return jasmine.objectContaining(cue);
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
expect(actual).toEqual(expected.map(mapExpected));
|
|
179
|
-
}
|
|
180
173
|
});
|