shaka-player 3.2.2 → 3.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.
- 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/AUTHORS +6 -5
- package/CHANGELOG.md +247 -0
- package/CONTRIBUTORS +4 -2
- 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 +18 -11
- package/dist/locales.js +28 -26
- 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 +205 -15
- package/dist/shaka-player.compiled.debug.d.ts +205 -15
- package/dist/shaka-player.compiled.debug.externs.js +256 -6
- package/dist/shaka-player.compiled.debug.js +406 -354
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +256 -6
- package/dist/shaka-player.compiled.js +826 -800
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +221 -16
- package/dist/shaka-player.ui.debug.d.ts +221 -16
- package/dist/shaka-player.ui.debug.externs.js +283 -6
- package/dist/shaka-player.ui.debug.js +547 -476
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +283 -6
- package/dist/shaka-player.ui.js +1019 -980
- 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/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 +130 -5
- package/externs/shaka/text.js +14 -0
- package/externs/webcrypto.js +18 -0
- 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/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_utils.js +1 -0
- package/lib/dash/dash_parser.js +48 -14
- 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 +86 -23
- package/lib/media/quality_observer.js +291 -0
- package/lib/media/segment_index.js +6 -0
- package/lib/media/segment_reference.js +18 -3
- package/lib/media/streaming_engine.js +42 -4
- 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 +81 -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 +393 -284
- package/lib/offline/stored_content_utils.js +2 -0
- package/lib/player.js +206 -7
- package/lib/polyfill/random_uuid.js +52 -0
- package/lib/text/cue.js +12 -0
- package/lib/text/ttml_text_parser.js +25 -2
- package/lib/text/ui_text_displayer.js +2 -2
- package/lib/util/cmcd_manager.js +826 -0
- package/lib/util/error.js +6 -0
- 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/package.json +1 -1
- 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 +101 -1
- package/test/dash/dash_parser_manifest_unit.js +35 -1
- 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 +2 -0
- package/test/media/quality_observer_unit.js +139 -0
- package/test/media/segment_reference_unit.js +16 -1
- package/test/media/streaming_engine_integration.js +2 -1
- package/test/media/streaming_engine_unit.js +2 -0
- package/test/net/http_plugin_unit.js +20 -15
- package/test/offline/storage_integration.js +108 -4
- package/test/player_integration.js +99 -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/text/ttml_text_parser_unit.js +47 -0
- package/test/ui/ui_unit.js +250 -0
- package/test/util/cmcd_manager_unit.js +277 -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 +41 -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/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 +11 -4
- package/ui/ui.js +30 -0
- package/ui/volume_bar.js +1 -1
|
@@ -57,6 +57,8 @@ describe('DashParser Manifest', () => {
|
|
|
57
57
|
onEventSpy = jasmine.createSpy('onEvent');
|
|
58
58
|
playerInterface = {
|
|
59
59
|
networkingEngine: fakeNetEngine,
|
|
60
|
+
modifyManifestRequest: (request, manifestInfo) => {},
|
|
61
|
+
modifySegmentRequest: (request, segmentInfo) => {},
|
|
60
62
|
filter: (manifest) => Promise.resolve(),
|
|
61
63
|
makeTextStreamsForClosedCaptions: (manifest) => {},
|
|
62
64
|
onTimelineRegionAdded: fail, // Should not have any EventStream elements.
|
|
@@ -1872,7 +1874,7 @@ describe('DashParser Manifest', () => {
|
|
|
1872
1874
|
expect(textStream.kind).toBe('caption');
|
|
1873
1875
|
});
|
|
1874
1876
|
|
|
1875
|
-
it('converts Roles element to "forced"', async () => {
|
|
1877
|
+
it('converts Roles element to "forced" (old role)', async () => {
|
|
1876
1878
|
const manifestText = [
|
|
1877
1879
|
'<MPD minBufferTime="PT75S">',
|
|
1878
1880
|
' <Period id="1" duration="PT30S">',
|
|
@@ -1904,6 +1906,38 @@ describe('DashParser Manifest', () => {
|
|
|
1904
1906
|
expect(textStream.forced).toBe(true);
|
|
1905
1907
|
});
|
|
1906
1908
|
|
|
1909
|
+
it('converts Roles element to "forced"', async () => {
|
|
1910
|
+
const manifestText = [
|
|
1911
|
+
'<MPD minBufferTime="PT75S">',
|
|
1912
|
+
' <Period id="1" duration="PT30S">',
|
|
1913
|
+
' <AdaptationSet id="1" contentType="text">',
|
|
1914
|
+
' <Accessibility schemeIdUri="urn:mpeg:dash:role:2011" ',
|
|
1915
|
+
' value="captions" />',
|
|
1916
|
+
' <Accessibility schemeIdUri="urn:mpeg:dash:role:2011" ',
|
|
1917
|
+
' value="forced-subtitle" />',
|
|
1918
|
+
' <Accessibility schemeIdUri="foobar" value="bar" />',
|
|
1919
|
+
' <Representation id="text-en" mimeType="text/webvtt">',
|
|
1920
|
+
' <BaseURL>t-en.vtt</BaseURL>',
|
|
1921
|
+
' </Representation>',
|
|
1922
|
+
' </AdaptationSet>',
|
|
1923
|
+
' <AdaptationSet id="1" mimeType="video/mp4">',
|
|
1924
|
+
' <Representation id="video-sd" width="640" height="480">',
|
|
1925
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
1926
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1927
|
+
' </Representation>',
|
|
1928
|
+
' </AdaptationSet>',
|
|
1929
|
+
' </Period>',
|
|
1930
|
+
'</MPD>',
|
|
1931
|
+
].join('\n');
|
|
1932
|
+
|
|
1933
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
1934
|
+
/** @type {shaka.extern.Manifest} */
|
|
1935
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
1936
|
+
const textStream = manifest.textStreams[0];
|
|
1937
|
+
expect(textStream.roles).toEqual(['captions', 'forced-subtitle']);
|
|
1938
|
+
expect(textStream.forced).toBe(true);
|
|
1939
|
+
});
|
|
1940
|
+
|
|
1907
1941
|
it('supports HDR signaling via profiles', async () => {
|
|
1908
1942
|
// (DASH-IF IOP v4.3 10.3.3.)
|
|
1909
1943
|
const hdrProfile =
|
|
@@ -35,6 +35,8 @@ describe('DashParser SegmentBase', () => {
|
|
|
35
35
|
|
|
36
36
|
playerInterface = {
|
|
37
37
|
networkingEngine: fakeNetEngine,
|
|
38
|
+
modifyManifestRequest: (request, manifestInfo) => {},
|
|
39
|
+
modifySegmentRequest: (request, segmentInfo) => {},
|
|
38
40
|
filter: (manifest) => Promise.resolve(),
|
|
39
41
|
makeTextStreamsForClosedCaptions: (manifest) => {},
|
|
40
42
|
onTimelineRegionAdded: fail, // Should not have any EventStream elements.
|
|
@@ -344,6 +344,8 @@ describe('DashParser SegmentList', () => {
|
|
|
344
344
|
|
|
345
345
|
const playerInterface = {
|
|
346
346
|
networkingEngine: networkingEngine,
|
|
347
|
+
modifyManifestRequest: (request, manifestInfo) => {},
|
|
348
|
+
modifySegmentRequest: (request, segmentInfo) => {},
|
|
347
349
|
filter: () => {},
|
|
348
350
|
makeTextStreamsForClosedCaptions: (manifest) => {},
|
|
349
351
|
onTimelineRegionAdded: fail, // Should not have any EventStream elements.
|
|
@@ -374,4 +376,3 @@ describe('DashParser SegmentList', () => {
|
|
|
374
376
|
expect(actual).toEqual(expected);
|
|
375
377
|
});
|
|
376
378
|
});
|
|
377
|
-
|
|
@@ -46,6 +46,8 @@ describe('DashParser SegmentTemplate', () => {
|
|
|
46
46
|
|
|
47
47
|
playerInterface = {
|
|
48
48
|
networkingEngine: fakeNetEngine,
|
|
49
|
+
modifyManifestRequest: (request, manifestInfo) => {},
|
|
50
|
+
modifySegmentRequest: (request, segmentInfo) => {},
|
|
49
51
|
filter: (manifest) => Promise.resolve(),
|
|
50
52
|
makeTextStreamsForClosedCaptions: (manifest) => {},
|
|
51
53
|
onTimelineRegionAdded: fail, // Should not have any EventStream elements.
|
|
@@ -585,4 +587,3 @@ describe('DashParser SegmentTemplate', () => {
|
|
|
585
587
|
});
|
|
586
588
|
});
|
|
587
589
|
});
|
|
588
|
-
|
|
@@ -116,6 +116,8 @@ describe('HlsParser live', () => {
|
|
|
116
116
|
|
|
117
117
|
config = shaka.util.PlayerConfiguration.createDefault().manifest;
|
|
118
118
|
playerInterface = {
|
|
119
|
+
modifyManifestRequest: (request, manifestInfo) => {},
|
|
120
|
+
modifySegmentRequest: (request, segmentInfo) => {},
|
|
119
121
|
filter: () => Promise.resolve(),
|
|
120
122
|
makeTextStreamsForClosedCaptions: (manifest) => {},
|
|
121
123
|
networkingEngine: fakeNetEngine,
|
|
@@ -94,6 +94,8 @@ describe('HlsParser', () => {
|
|
|
94
94
|
config = shaka.util.PlayerConfiguration.createDefault().manifest;
|
|
95
95
|
onEventSpy = jasmine.createSpy('onEvent');
|
|
96
96
|
playerInterface = {
|
|
97
|
+
modifyManifestRequest: (request, manifestInfo) => {},
|
|
98
|
+
modifySegmentRequest: (request, segmentInfo) => {},
|
|
97
99
|
filter: () => Promise.resolve(),
|
|
98
100
|
makeTextStreamsForClosedCaptions: (manifest) => {},
|
|
99
101
|
networkingEngine: fakeNetEngine,
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
goog.require('shaka.media.QualityObserver');
|
|
8
|
+
goog.require('shaka.test.Util');
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
describe('QualityObserver', () => {
|
|
12
|
+
/** @type {!shaka.media.QualityObserver} */
|
|
13
|
+
let observer;
|
|
14
|
+
|
|
15
|
+
/** @type {!jasmine.Spy} */
|
|
16
|
+
let onQualityChange;
|
|
17
|
+
|
|
18
|
+
const createQualityInfo = (contentType, bandwidth) => {
|
|
19
|
+
return {
|
|
20
|
+
bandwidth: bandwidth,
|
|
21
|
+
audioSamplingRate: 444000,
|
|
22
|
+
codecs: 'my codec',
|
|
23
|
+
contentType: contentType,
|
|
24
|
+
frameRate: 30,
|
|
25
|
+
height: 720,
|
|
26
|
+
mimeType: 'mime type',
|
|
27
|
+
channelsCount: 2,
|
|
28
|
+
pixelAspectRatio: '1:1',
|
|
29
|
+
width: 1280,
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
let emptyBuffer = true;
|
|
33
|
+
let bufferStart = 0;
|
|
34
|
+
let bufferEnd = 0;
|
|
35
|
+
|
|
36
|
+
const quality1 = createQualityInfo('video', 1);
|
|
37
|
+
const quality2 = createQualityInfo('video', 2);
|
|
38
|
+
|
|
39
|
+
const getBufferedInfo = () => {
|
|
40
|
+
if (emptyBuffer) {
|
|
41
|
+
return {
|
|
42
|
+
video: [],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
video: [{start: bufferStart, end: bufferEnd}],
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
onQualityChange = jasmine.createSpy('onQualityChange');
|
|
52
|
+
observer = new shaka.media.QualityObserver(getBufferedInfo);
|
|
53
|
+
observer.setListeners(shaka.test.Util.spyFunc(onQualityChange));
|
|
54
|
+
emptyBuffer = true;
|
|
55
|
+
bufferStart = 0;
|
|
56
|
+
bufferEnd = 0;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('does not call onQualityChange when there are no quality changes', () => {
|
|
60
|
+
observer.poll(10, false);
|
|
61
|
+
expect(onQualityChange).not.toHaveBeenCalled();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('calls onQualityChange when position is after 1st quality change', () => {
|
|
65
|
+
observer.addMediaQualityChange(quality1, 10);
|
|
66
|
+
emptyBuffer = false;
|
|
67
|
+
bufferStart = 10;
|
|
68
|
+
bufferEnd = 20;
|
|
69
|
+
observer.poll(10, false);
|
|
70
|
+
expect(onQualityChange).toHaveBeenCalledWith(quality1, 10);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('does not call onQualityChange when pos advances with no change', () => {
|
|
74
|
+
observer.addMediaQualityChange(quality1, 10);
|
|
75
|
+
emptyBuffer = false;
|
|
76
|
+
bufferStart = 10;
|
|
77
|
+
bufferEnd = 20;
|
|
78
|
+
observer.poll(10, false);
|
|
79
|
+
expect(onQualityChange).toHaveBeenCalledWith(quality1, 10);
|
|
80
|
+
observer.poll(11, false);
|
|
81
|
+
expect(onQualityChange).toHaveBeenCalledTimes(1);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('does not call onQualityChange on seek to unbuffered position', () => {
|
|
85
|
+
observer.addMediaQualityChange(quality1, 10);
|
|
86
|
+
emptyBuffer = false;
|
|
87
|
+
bufferStart = 10;
|
|
88
|
+
bufferEnd = 20;
|
|
89
|
+
observer.poll(15, false);
|
|
90
|
+
expect(onQualityChange).toHaveBeenCalledOnceMoreWith([quality1, 15]);
|
|
91
|
+
observer.addMediaQualityChange(quality2, 20);
|
|
92
|
+
observer.poll(25, true);
|
|
93
|
+
expect(onQualityChange).not.toHaveBeenCalledOnceMore();
|
|
94
|
+
bufferEnd = 30;
|
|
95
|
+
observer.poll(26, false);
|
|
96
|
+
expect(onQualityChange).toHaveBeenCalledOnceMoreWith([quality2, 26]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('calls onQualityChange when position advances over 2nd quality change',
|
|
100
|
+
() => {
|
|
101
|
+
observer.addMediaQualityChange(quality1, 10);
|
|
102
|
+
emptyBuffer = false;
|
|
103
|
+
bufferStart = 10;
|
|
104
|
+
bufferEnd = 20;
|
|
105
|
+
observer.poll(10, false);
|
|
106
|
+
expect(onQualityChange).toHaveBeenCalledOnceMoreWith([quality1, 10]);
|
|
107
|
+
observer.addMediaQualityChange(quality2, 20);
|
|
108
|
+
bufferStart = 10;
|
|
109
|
+
bufferEnd = 30;
|
|
110
|
+
observer.poll(20, false);
|
|
111
|
+
expect(onQualityChange).toHaveBeenCalledOnceMoreWith([quality2, 20]);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('calls onQualityChange when position moves back over a quality chanage',
|
|
115
|
+
() => {
|
|
116
|
+
observer.addMediaQualityChange(quality1, 10);
|
|
117
|
+
emptyBuffer = false;
|
|
118
|
+
bufferStart = 10;
|
|
119
|
+
bufferEnd = 20;
|
|
120
|
+
observer.addMediaQualityChange(quality2, 20);
|
|
121
|
+
bufferStart = 10;
|
|
122
|
+
bufferEnd = 30;
|
|
123
|
+
observer.poll(25, false);
|
|
124
|
+
expect(onQualityChange).toHaveBeenCalledOnceMoreWith([quality2, 25]);
|
|
125
|
+
observer.poll(15, false);
|
|
126
|
+
expect(onQualityChange).toHaveBeenCalledOnceMoreWith([quality1, 15]);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('uses last applied quality when there are two at the same position',
|
|
130
|
+
() => {
|
|
131
|
+
observer.addMediaQualityChange(quality1, 10);
|
|
132
|
+
observer.addMediaQualityChange(quality2, 10);
|
|
133
|
+
emptyBuffer = false;
|
|
134
|
+
bufferStart = 10;
|
|
135
|
+
bufferEnd = 20;
|
|
136
|
+
observer.poll(15, false);
|
|
137
|
+
expect(onQualityChange).toHaveBeenCalledWith(quality2, 15);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
@@ -38,14 +38,29 @@ describe('SegmentReference', () => {
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
describe('InitSegmentReference', () => {
|
|
41
|
+
const mediaQuality = {
|
|
42
|
+
bandwidth: 1,
|
|
43
|
+
audioSamplingRate: 444000,
|
|
44
|
+
codecs: 'my codec',
|
|
45
|
+
contentType: 'video',
|
|
46
|
+
frameRate: 30,
|
|
47
|
+
height: 720,
|
|
48
|
+
mimeType: 'mime type',
|
|
49
|
+
channelsCount: 2,
|
|
50
|
+
pixelAspectRatio: '1:1',
|
|
51
|
+
width: 1280,
|
|
52
|
+
};
|
|
53
|
+
|
|
41
54
|
it('returns in getters values from constructor parameters', () => {
|
|
42
55
|
const reference = new shaka.media.InitSegmentReference(
|
|
43
56
|
/* getUris= */ () => ['x', 'y'],
|
|
44
57
|
/* startByte= */ 4,
|
|
45
|
-
/* endByte= */ 5
|
|
58
|
+
/* endByte= */ 5,
|
|
59
|
+
mediaQuality);
|
|
46
60
|
|
|
47
61
|
expect(reference.getUris()).toEqual(['x', 'y']);
|
|
48
62
|
expect(reference.getStartByte()).toBe(4);
|
|
49
63
|
expect(reference.getEndByte()).toBe(5);
|
|
64
|
+
expect(reference.getMediaQuality()).toBe(mediaQuality);
|
|
50
65
|
});
|
|
51
66
|
});
|
|
@@ -60,7 +60,6 @@ describe('StreamingEngine', () => {
|
|
|
60
60
|
/** @type {!shaka.media.StreamingEngine} */
|
|
61
61
|
let streamingEngine;
|
|
62
62
|
|
|
63
|
-
|
|
64
63
|
/** @type {shaka.extern.Variant} */
|
|
65
64
|
let variant;
|
|
66
65
|
|
|
@@ -262,6 +261,7 @@ describe('StreamingEngine', () => {
|
|
|
262
261
|
|
|
263
262
|
function createStreamingEngine() {
|
|
264
263
|
const playerInterface = {
|
|
264
|
+
modifySegmentRequest: (request, segmentInfo) => {},
|
|
265
265
|
getPresentationTime: () => playhead.getTime(),
|
|
266
266
|
getBandwidthEstimate: () => 1e6,
|
|
267
267
|
mediaSourceEngine: mediaSourceEngine,
|
|
@@ -270,6 +270,7 @@ describe('StreamingEngine', () => {
|
|
|
270
270
|
onEvent: Util.spyFunc(onEvent),
|
|
271
271
|
onManifestUpdate: () => {},
|
|
272
272
|
onSegmentAppended: () => playhead.notifyOfBufferingChange(),
|
|
273
|
+
onInitSegmentAppended: () => {},
|
|
273
274
|
};
|
|
274
275
|
streamingEngine = new shaka.media.StreamingEngine(
|
|
275
276
|
/** @type {shaka.extern.Manifest} */(manifest), playerInterface);
|
|
@@ -451,6 +451,7 @@ describe('StreamingEngine', () => {
|
|
|
451
451
|
presentationTimeInSeconds != undefined,
|
|
452
452
|
'All tests should have defined an initial presentation time by now!');
|
|
453
453
|
const playerInterface = {
|
|
454
|
+
modifySegmentRequest: (request, segmentInfo) => {},
|
|
454
455
|
getPresentationTime: () => presentationTimeInSeconds,
|
|
455
456
|
getBandwidthEstimate: Util.spyFunc(getBandwidthEstimate),
|
|
456
457
|
mediaSourceEngine: mediaSourceEngine,
|
|
@@ -459,6 +460,7 @@ describe('StreamingEngine', () => {
|
|
|
459
460
|
onEvent: Util.spyFunc(onEvent),
|
|
460
461
|
onManifestUpdate: Util.spyFunc(onManifestUpdate),
|
|
461
462
|
onSegmentAppended: Util.spyFunc(onSegmentAppended),
|
|
463
|
+
onInitSegmentAppended: () => {},
|
|
462
464
|
};
|
|
463
465
|
streamingEngine = new shaka.media.StreamingEngine(
|
|
464
466
|
/** @type {shaka.extern.Manifest} */(manifest), playerInterface);
|
|
@@ -26,6 +26,9 @@ function httpPluginTests(usingFetch) {
|
|
|
26
26
|
// A dummy progress callback.
|
|
27
27
|
const progressUpdated = (elapsedMs, bytes, bytesRemaining) => {};
|
|
28
28
|
|
|
29
|
+
// A dummy headers callback.
|
|
30
|
+
const headersReceived = (headers) => {};
|
|
31
|
+
|
|
29
32
|
/** @type {shaka.extern.RetryParameters} */
|
|
30
33
|
let retryParameters;
|
|
31
34
|
|
|
@@ -168,7 +171,8 @@ function httpPluginTests(usingFetch) {
|
|
|
168
171
|
request.method = 'POST';
|
|
169
172
|
request.headers['BAZ'] = '123';
|
|
170
173
|
|
|
171
|
-
await plugin(
|
|
174
|
+
await plugin(
|
|
175
|
+
request.uris[0], request, requestType, progressUpdated, headersReceived)
|
|
172
176
|
.promise;
|
|
173
177
|
|
|
174
178
|
const actual = mostRecentRequest();
|
|
@@ -189,8 +193,8 @@ function httpPluginTests(usingFetch) {
|
|
|
189
193
|
request.body = null;
|
|
190
194
|
request.method = 'GET';
|
|
191
195
|
|
|
192
|
-
await plugin(request.uris[0], request, requestType, progressUpdated
|
|
193
|
-
.promise;
|
|
196
|
+
await plugin(request.uris[0], request, requestType, progressUpdated,
|
|
197
|
+
headersReceived).promise;
|
|
194
198
|
|
|
195
199
|
const actual = jasmine.Fetch.requests.mostRecent();
|
|
196
200
|
expect(actual).toBeTruthy();
|
|
@@ -205,8 +209,8 @@ function httpPluginTests(usingFetch) {
|
|
|
205
209
|
|
|
206
210
|
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
207
211
|
[uri], retryParameters, Util.spyFunc(streamDataCallback));
|
|
208
|
-
const response =
|
|
209
|
-
|
|
212
|
+
const response = await plugin(
|
|
213
|
+
uri, request, requestType, progressUpdated, headersReceived).promise;
|
|
210
214
|
|
|
211
215
|
expect(mostRecentRequest().url).toBe(uri);
|
|
212
216
|
expect(response).toBeTruthy();
|
|
@@ -297,9 +301,9 @@ function httpPluginTests(usingFetch) {
|
|
|
297
301
|
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
298
302
|
['https://foo.bar/cache'], retryParameters);
|
|
299
303
|
|
|
300
|
-
const response =
|
|
301
|
-
|
|
302
|
-
|
|
304
|
+
const response = await plugin(
|
|
305
|
+
request.uris[0], request, requestType, progressUpdated, headersReceived)
|
|
306
|
+
.promise;
|
|
303
307
|
expect(response).toBeTruthy();
|
|
304
308
|
expect(response.fromCache).toBe(true);
|
|
305
309
|
});
|
|
@@ -312,8 +316,8 @@ function httpPluginTests(usingFetch) {
|
|
|
312
316
|
uri = 'https://foo.bar/timeout';
|
|
313
317
|
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
314
318
|
[uri], retryParameters);
|
|
315
|
-
const operation = plugin(
|
|
316
|
-
|
|
319
|
+
const operation = plugin(request.uris[0], request, requestType,
|
|
320
|
+
progressUpdated, headersReceived);
|
|
317
321
|
|
|
318
322
|
/** @type {jasmine.Fetch.RequestStub} */
|
|
319
323
|
const actual = jasmine.Fetch.requests.mostRecent();
|
|
@@ -354,8 +358,8 @@ function httpPluginTests(usingFetch) {
|
|
|
354
358
|
uri = 'https://foo.bar/';
|
|
355
359
|
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
356
360
|
[uri], retryParameters);
|
|
357
|
-
operation = plugin(
|
|
358
|
-
|
|
361
|
+
operation = plugin(request.uris[0], request, requestType, progressUpdated,
|
|
362
|
+
headersReceived);
|
|
359
363
|
requestPromise = operation.promise;
|
|
360
364
|
}
|
|
361
365
|
|
|
@@ -388,8 +392,8 @@ function httpPluginTests(usingFetch) {
|
|
|
388
392
|
async function testSucceeds(uri, overrideUri) {
|
|
389
393
|
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
390
394
|
[uri], retryParameters);
|
|
391
|
-
const response =
|
|
392
|
-
|
|
395
|
+
const response = await plugin(
|
|
396
|
+
uri, request, requestType, progressUpdated, headersReceived).promise;
|
|
393
397
|
|
|
394
398
|
expect(mostRecentRequest().url).toBe(uri);
|
|
395
399
|
expect(response).toBeTruthy();
|
|
@@ -410,7 +414,8 @@ function httpPluginTests(usingFetch) {
|
|
|
410
414
|
const request = shaka.net.NetworkingEngine.makeRequest(
|
|
411
415
|
[uri], retryParameters);
|
|
412
416
|
|
|
413
|
-
const p = plugin(
|
|
417
|
+
const p = plugin(
|
|
418
|
+
uri, request, requestType, progressUpdated, headersReceived).promise;
|
|
414
419
|
if (expected.code == shaka.util.Error.Code.TIMEOUT) {
|
|
415
420
|
jasmine.clock().tick(5000);
|
|
416
421
|
}
|
|
@@ -22,10 +22,12 @@ goog.require('shaka.test.ManifestGenerator');
|
|
|
22
22
|
goog.require('shaka.test.TestScheme');
|
|
23
23
|
goog.require('shaka.test.Util');
|
|
24
24
|
goog.require('shaka.util.AbortableOperation');
|
|
25
|
+
goog.require('shaka.util.BufferUtils');
|
|
25
26
|
goog.require('shaka.util.Error');
|
|
26
27
|
goog.require('shaka.util.EventManager');
|
|
27
28
|
goog.require('shaka.util.PlayerConfiguration');
|
|
28
29
|
goog.require('shaka.util.PublicPromise');
|
|
30
|
+
goog.require('shaka.util.Uint8ArrayUtils');
|
|
29
31
|
goog.requireType('shaka.media.SegmentReference');
|
|
30
32
|
|
|
31
33
|
/** @return {boolean} */
|
|
@@ -58,12 +60,16 @@ filterDescribe('Storage', storageSupport, () => {
|
|
|
58
60
|
const manifestWithNonZeroStartUri = 'fake:manifest-with-non-zero-start';
|
|
59
61
|
const manifestWithLiveTimelineUri = 'fake:manifest-with-live-timeline';
|
|
60
62
|
const manifestWithAlternateSegmentsUri = 'fake:manifest-with-alt-segments';
|
|
63
|
+
const manifestWithVideoInitSegmentsUri =
|
|
64
|
+
'fake:manifest-with-video-init-segments';
|
|
61
65
|
|
|
66
|
+
const initSegmentUri = 'fake:init-segment';
|
|
62
67
|
const segment1Uri = 'fake:segment-1';
|
|
63
68
|
const segment2Uri = 'fake:segment-2';
|
|
64
69
|
const segment3Uri = 'fake:segment-3';
|
|
65
70
|
const segment4Uri = 'fake:segment-4';
|
|
66
71
|
|
|
72
|
+
const alternateInitSegmentUri = 'fake:alt-init-segment';
|
|
67
73
|
const alternateSegment1Uri = 'fake:alt-segment-1';
|
|
68
74
|
const alternateSegment2Uri = 'fake:alt-segment-2';
|
|
69
75
|
const alternateSegment3Uri = 'fake:alt-segment-3';
|
|
@@ -945,6 +951,46 @@ filterDescribe('Storage', storageSupport, () => {
|
|
|
945
951
|
}
|
|
946
952
|
});
|
|
947
953
|
|
|
954
|
+
it('can extract DRM info from segments', async () => {
|
|
955
|
+
const pssh1 =
|
|
956
|
+
'00000028' + // atom size
|
|
957
|
+
'70737368' + // atom type='pssh'
|
|
958
|
+
'00000000' + // v0, flags=0
|
|
959
|
+
'edef8ba979d64acea3c827dcd51d21ed' + // system id (Widevine)
|
|
960
|
+
'00000008' + // data size
|
|
961
|
+
'0102030405060708'; // data
|
|
962
|
+
const psshData1 = shaka.util.Uint8ArrayUtils.fromHex(pssh1);
|
|
963
|
+
const pssh2 =
|
|
964
|
+
'00000028' + // atom size
|
|
965
|
+
'70737368' + // atom type='pssh'
|
|
966
|
+
'00000000' + // v0, flags=0
|
|
967
|
+
'edef8ba979d64acea3c827dcd51d21ed' + // system id (Widevine)
|
|
968
|
+
'00000008' + // data size
|
|
969
|
+
'1337420123456789'; // data
|
|
970
|
+
const psshData2 = shaka.util.Uint8ArrayUtils.fromHex(pssh2);
|
|
971
|
+
netEngine.setResponseValue(initSegmentUri,
|
|
972
|
+
shaka.util.BufferUtils.toArrayBuffer(psshData1));
|
|
973
|
+
netEngine.setResponseValue(alternateInitSegmentUri,
|
|
974
|
+
shaka.util.BufferUtils.toArrayBuffer(psshData2));
|
|
975
|
+
|
|
976
|
+
const drm = new shaka.test.FakeDrmEngine();
|
|
977
|
+
const drmInfo = makeDrmInfo();
|
|
978
|
+
drmInfo.keySystem = 'com.widevine.alpha';
|
|
979
|
+
drm.setDrmInfo(drmInfo);
|
|
980
|
+
overrideDrmAndManifest(
|
|
981
|
+
storage,
|
|
982
|
+
drm,
|
|
983
|
+
makeManifestWithVideoInitSegments());
|
|
984
|
+
|
|
985
|
+
const stored = await storage.store(
|
|
986
|
+
manifestWithVideoInitSegmentsUri, noMetadata, fakeMimeType).promise;
|
|
987
|
+
goog.asserts.assert(stored.offlineUri != null, 'URI should not be null!');
|
|
988
|
+
|
|
989
|
+
// The manifest chooses the alternate stream, so expect only the alt init
|
|
990
|
+
// segment.
|
|
991
|
+
expect(drm.newInitData).toHaveBeenCalledWith('cenc', psshData2);
|
|
992
|
+
});
|
|
993
|
+
|
|
948
994
|
it('can store multiple assets at once', async () => {
|
|
949
995
|
// Block the network so that we won't finish the first store command.
|
|
950
996
|
/** @type {!shaka.util.PublicPromise} */
|
|
@@ -1234,11 +1280,11 @@ filterDescribe('Storage', storageSupport, () => {
|
|
|
1234
1280
|
goog.asserts.assert(
|
|
1235
1281
|
content.offlineUri != null, 'URI should not be null!');
|
|
1236
1282
|
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1283
|
+
// We expect 5 progress events because there are 4 unique segments, plus
|
|
1284
|
+
// the manifest.
|
|
1285
|
+
/** @type {!Array.<number>}*/
|
|
1240
1286
|
const progressSteps = [
|
|
1241
|
-
0.
|
|
1287
|
+
0.2, 0.4, 0.6, 0.8, 1,
|
|
1242
1288
|
];
|
|
1243
1289
|
|
|
1244
1290
|
const progressCallback = (content, progress) => {
|
|
@@ -1399,6 +1445,62 @@ filterDescribe('Storage', storageSupport, () => {
|
|
|
1399
1445
|
};
|
|
1400
1446
|
}
|
|
1401
1447
|
|
|
1448
|
+
/** @return {shaka.extern.Manifest} */
|
|
1449
|
+
function makeManifestWithVideoInitSegments() {
|
|
1450
|
+
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
1451
|
+
manifest.presentationTimeline.setDuration(20);
|
|
1452
|
+
|
|
1453
|
+
manifest.addVariant(0, (variant) => {
|
|
1454
|
+
variant.bandwidth = kbps(13);
|
|
1455
|
+
variant.addVideo(1, (stream) => {
|
|
1456
|
+
stream.bandwidth = kbps(13);
|
|
1457
|
+
stream.size(100, 200);
|
|
1458
|
+
});
|
|
1459
|
+
});
|
|
1460
|
+
manifest.addVariant(2, (variant) => {
|
|
1461
|
+
variant.bandwidth = kbps(20);
|
|
1462
|
+
variant.addVideo(3, (stream) => {
|
|
1463
|
+
stream.bandwidth = kbps(20);
|
|
1464
|
+
stream.size(200, 400);
|
|
1465
|
+
});
|
|
1466
|
+
});
|
|
1467
|
+
});
|
|
1468
|
+
|
|
1469
|
+
const stream = manifest.variants[0].video;
|
|
1470
|
+
goog.asserts.assert(stream, 'The first stream should exist');
|
|
1471
|
+
stream.encrypted = true;
|
|
1472
|
+
const init = new shaka.media.InitSegmentReference(
|
|
1473
|
+
() => [initSegmentUri], 0, null);
|
|
1474
|
+
const refs = [
|
|
1475
|
+
makeReference(segment1Uri, 0, 1),
|
|
1476
|
+
makeReference(segment2Uri, 1, 2),
|
|
1477
|
+
makeReference(segment3Uri, 2, 3),
|
|
1478
|
+
makeReference(segment4Uri, 3, 4),
|
|
1479
|
+
];
|
|
1480
|
+
for (const ref of refs) {
|
|
1481
|
+
ref.initSegmentReference = init;
|
|
1482
|
+
}
|
|
1483
|
+
overrideSegmentIndex(stream, refs);
|
|
1484
|
+
|
|
1485
|
+
const streamAlt = manifest.variants[1].video;
|
|
1486
|
+
goog.asserts.assert(streamAlt, 'The second stream should exist');
|
|
1487
|
+
streamAlt.encrypted = true;
|
|
1488
|
+
const initAlt = new shaka.media.InitSegmentReference(
|
|
1489
|
+
() => [alternateInitSegmentUri], 0, null);
|
|
1490
|
+
const refsAlt = [
|
|
1491
|
+
makeReference(alternateSegment1Uri, 0, 1),
|
|
1492
|
+
makeReference(alternateSegment2Uri, 1, 2),
|
|
1493
|
+
makeReference(alternateSegment3Uri, 2, 3),
|
|
1494
|
+
makeReference(alternateSegment4Uri, 3, 4),
|
|
1495
|
+
];
|
|
1496
|
+
for (const ref of refsAlt) {
|
|
1497
|
+
ref.initSegmentReference = initAlt;
|
|
1498
|
+
}
|
|
1499
|
+
overrideSegmentIndex(streamAlt, refsAlt);
|
|
1500
|
+
|
|
1501
|
+
return manifest;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1402
1504
|
/** @return {shaka.extern.Manifest} */
|
|
1403
1505
|
function makeManifestWithPerStreamBandwidth() {
|
|
1404
1506
|
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
@@ -1612,6 +1714,8 @@ filterDescribe('Storage', storageSupport, () => {
|
|
|
1612
1714
|
makeManifestWithLiveTimeline();
|
|
1613
1715
|
this.map_[manifestWithAlternateSegmentsUri] =
|
|
1614
1716
|
makeManifestWithAlternateSegments();
|
|
1717
|
+
this.map_[manifestWithVideoInitSegmentsUri] =
|
|
1718
|
+
makeManifestWithVideoInitSegments();
|
|
1615
1719
|
}
|
|
1616
1720
|
|
|
1617
1721
|
/** @override */
|