shaka-player 3.2.1 → 3.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/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 +9 -5
- package/CHANGELOG.md +358 -0
- package/CONTRIBUTORS +7 -2
- package/build/conformance.textproto +183 -169
- package/build/generateExterns.js +1 -1
- 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 +19 -1
- 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 +20 -17
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +20 -17
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +19 -12
- package/dist/locales.js +39 -37
- package/dist/receiver.compiled.debug.js +12 -10
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +12 -10
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +231 -18
- package/dist/shaka-player.compiled.debug.d.ts +231 -18
- package/dist/shaka-player.compiled.debug.externs.js +279 -8
- package/dist/shaka-player.compiled.debug.js +512 -449
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +279 -8
- package/dist/shaka-player.compiled.js +854 -821
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +247 -19
- package/dist/shaka-player.ui.debug.d.ts +247 -19
- package/dist/shaka-player.ui.debug.externs.js +307 -9
- package/dist/shaka-player.ui.debug.js +662 -580
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +307 -9
- package/dist/shaka-player.ui.js +1047 -1000
- 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/fairplay.md +1 -1
- 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 +1 -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 +54 -90
- package/lib/ads/server_side_ad.js +7 -0
- package/lib/ads/server_side_ad_manager.js +7 -10
- package/lib/cast/cast_proxy.js +3 -2
- package/lib/cast/cast_receiver.js +2 -1
- package/lib/cast/cast_sender.js +2 -2
- package/lib/cast/cast_utils.js +1 -0
- package/lib/dash/dash_parser.js +141 -16
- package/lib/dash/segment_base.js +25 -1
- package/lib/dash/segment_list.js +19 -10
- package/lib/dash/segment_template.js +24 -11
- package/lib/hls/hls_parser.js +146 -40
- package/lib/media/drm_engine.js +7 -0
- package/lib/media/gap_jumping_controller.js +4 -2
- package/lib/media/media_source_engine.js +1 -2
- package/lib/media/quality_observer.js +291 -0
- package/lib/media/segment_index.js +8 -1
- package/lib/media/segment_reference.js +37 -4
- package/lib/media/streaming_engine.js +49 -7
- 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 +83 -19
- 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 +249 -43
- package/lib/polyfill/media_capabilities.js +36 -15
- package/lib/polyfill/mediasource.js +6 -0
- package/lib/polyfill/orientation.js +1 -1
- package/lib/polyfill/patchedmediakeys_apple.js +4 -4
- package/lib/polyfill/patchedmediakeys_ms.js +4 -4
- package/lib/polyfill/patchedmediakeys_webkit.js +4 -4
- package/lib/polyfill/random_uuid.js +52 -0
- package/lib/text/cue.js +18 -0
- package/lib/text/mp4_ttml_parser.js +3 -0
- package/lib/text/text_engine.js +14 -2
- package/lib/text/ttml_text_parser.js +114 -60
- package/lib/text/ui_text_displayer.js +95 -41
- package/lib/util/cmcd_manager.js +672 -0
- package/lib/util/error.js +6 -0
- package/lib/util/fake_event.js +53 -7
- package/lib/util/language_utils.js +12 -0
- package/lib/util/mutex.js +52 -0
- package/lib/util/periods.js +102 -65
- package/lib/util/platform.js +13 -1
- package/lib/util/player_configuration.js +18 -0
- package/lib/util/stream_utils.js +99 -10
- package/lib/util/xml_utils.js +19 -0
- package/package.json +2 -2
- package/shaka-player.uncompiled.js +3 -0
- package/test/cast/cast_proxy_unit.js +12 -6
- package/test/cast/cast_utils_unit.js +1 -1
- 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 +222 -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 +7 -1
- package/test/media/closed_caption_parser_unit.js +6 -1
- package/test/media/drm_engine_unit.js +47 -3
- 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 +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 +178 -8
- package/test/player_src_equals_integration.js +41 -8
- package/test/player_unit.js +19 -9
- package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
- package/test/test/assets/3675/dash_0.mpd +30 -0
- package/test/test/assets/3675/dash_1.mpd +30 -0
- package/test/test/assets/3675/dash_2.mpd +30 -0
- package/test/test/assets/3675/dash_3.mpd +30 -0
- package/test/test/assets/3675/dash_4.mpd +30 -0
- package/test/test/assets/3675/dash_5.mpd +26 -0
- package/test/test/assets/3675/init-stream0.m4s +0 -0
- 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_ad_manager.js +1 -1
- 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/text/mp4_ttml_parser_unit.js +11 -18
- package/test/text/ttml_text_parser_unit.js +552 -227
- package/test/text/ui_text_displayer_unit.js +29 -17
- package/test/ui/localization_unit.js +15 -14
- package/test/ui/text_displayer_layout_unit.js +13 -1
- package/test/ui/ui_unit.js +250 -0
- package/test/util/cmcd_manager_unit.js +291 -0
- package/test/util/stream_utils_unit.js +45 -4
- 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 +3 -3
- package/ui/context_menu.js +109 -0
- package/ui/controls.js +45 -13
- 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 +77 -7
- 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/ru.json +6 -6
- package/ui/locales/source.json +4 -0
- package/ui/localization.js +28 -51
- 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 +7 -3
- 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
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
goog.require('shaka.util.CmcdManager');
|
|
8
|
+
goog.require('shaka.util.ObjectUtils');
|
|
9
|
+
|
|
10
|
+
describe('CmcdManager', () => {
|
|
11
|
+
const CmcdManager = shaka.util.CmcdManager;
|
|
12
|
+
const uuidRegex =
|
|
13
|
+
'[A-F\\d]{8}-[A-F\\d]{4}-4[A-F\\d]{3}-[89AB][A-F\\d]{3}-[A-F\\d]{12}';
|
|
14
|
+
const data = {
|
|
15
|
+
'sid': 'c936730c-031e-4a73-976f-92bc34039c60',
|
|
16
|
+
'cid': 'xyz',
|
|
17
|
+
'su': false,
|
|
18
|
+
'nor': '../testing/3.m4v',
|
|
19
|
+
'nrr': '0-99',
|
|
20
|
+
'd': 6066.66,
|
|
21
|
+
'mtp': 10049,
|
|
22
|
+
'bs': true,
|
|
23
|
+
'br': 52317,
|
|
24
|
+
'v': 1,
|
|
25
|
+
'pr': 1,
|
|
26
|
+
'com.test-hello': 'world',
|
|
27
|
+
'com.test-testing': 1234,
|
|
28
|
+
'com.test-exists': true,
|
|
29
|
+
'com.test-notExists': false,
|
|
30
|
+
'com.test-token': Symbol('s'),
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
describe('Query serialization', () => {
|
|
34
|
+
it('produces correctly serialized data', () => {
|
|
35
|
+
const query = CmcdManager.toQuery(data);
|
|
36
|
+
const result = 'br=52317,bs,cid="xyz",com.test-exists,' +
|
|
37
|
+
'com.test-hello="world",com.test-testing=1234,' +
|
|
38
|
+
'com.test-token=s,d=6067,mtp=10000,' +
|
|
39
|
+
'nor="..%2Ftesting%2F3.m4v",nrr="0-99",' +
|
|
40
|
+
'sid="c936730c-031e-4a73-976f-92bc34039c60"';
|
|
41
|
+
expect(query).toBe(result);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('escapes reserve character in string values', () => {
|
|
45
|
+
const query = CmcdManager.toQuery({
|
|
46
|
+
'com.test-escape': 'Double "Quotes"',
|
|
47
|
+
});
|
|
48
|
+
const result = 'com.test-escape="Double \\"Quotes\\""';
|
|
49
|
+
expect(query).toBe(result);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('Header serialization', () => {
|
|
54
|
+
it('produces all header shards', () => {
|
|
55
|
+
const header = CmcdManager.toHeaders(data);
|
|
56
|
+
expect(header).toEqual({
|
|
57
|
+
'CMCD-Object': 'br=52317,d=6067',
|
|
58
|
+
'CMCD-Request': 'com.test-exists,com.test-hello="world",' +
|
|
59
|
+
'com.test-testing=1234,com.test-token=s,mtp=10000,' +
|
|
60
|
+
'nor="..%2Ftesting%2F3.m4v",nrr="0-99"',
|
|
61
|
+
'CMCD-Session': 'cid="xyz",sid="c936730c-031e-4a73-976f-92bc34039c60"',
|
|
62
|
+
'CMCD-Status': 'bs',
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('ignores empty shards', () => {
|
|
67
|
+
expect(CmcdManager.toHeaders({br: 200})).toEqual({
|
|
68
|
+
'CMCD-Object': 'br=200',
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('CmcdManager instance', () => {
|
|
74
|
+
const ObjectUtils = shaka.util.ObjectUtils;
|
|
75
|
+
|
|
76
|
+
const playerInterface = {
|
|
77
|
+
isLive: () => false,
|
|
78
|
+
getBandwidthEstimate: () => 10000000,
|
|
79
|
+
getBufferedInfo: () => ({
|
|
80
|
+
video: [
|
|
81
|
+
{start: 0, end: 5},
|
|
82
|
+
{start: 6, end: 31.234},
|
|
83
|
+
{start: 35, end: 40},
|
|
84
|
+
],
|
|
85
|
+
}),
|
|
86
|
+
getVariantTracks: () => /** @type {Array.<shaka.extern.Track>} */([
|
|
87
|
+
{
|
|
88
|
+
type: 'variant',
|
|
89
|
+
bandwidth: 50000,
|
|
90
|
+
videoBandwidth: 40000,
|
|
91
|
+
audioBandWidth: 10000,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
type: 'variant',
|
|
95
|
+
bandwidth: 5000000,
|
|
96
|
+
videoBandwidth: 4000000,
|
|
97
|
+
audioBandWidth: 1000000,
|
|
98
|
+
},
|
|
99
|
+
]),
|
|
100
|
+
getPlaybackRate: () => 1,
|
|
101
|
+
getCurrentTime: () => 10,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const sid = '2ed2d1cd-970b-48f2-bfb3-50a79e87cfa3';
|
|
105
|
+
const config = {
|
|
106
|
+
enabled: false,
|
|
107
|
+
sessionId: '',
|
|
108
|
+
contentId: 'testing',
|
|
109
|
+
useHeaders: false,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/** @type shaka.util.CmcdManager */
|
|
113
|
+
let cmcdManager = new CmcdManager(playerInterface, config);
|
|
114
|
+
|
|
115
|
+
const request = {
|
|
116
|
+
uris: ['https://test.com/test.mpd'],
|
|
117
|
+
method: 'GET',
|
|
118
|
+
body: null,
|
|
119
|
+
headers: {
|
|
120
|
+
testing: '1234',
|
|
121
|
+
},
|
|
122
|
+
allowCrossSiteCredentials: false,
|
|
123
|
+
retryParameters: /** @type {shaka.extern.RetryParameters} */({}),
|
|
124
|
+
licenseRequestType: null,
|
|
125
|
+
sessionId: null,
|
|
126
|
+
streamDataCallback: null,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const manifestInfo = {
|
|
130
|
+
format: shaka.util.CmcdManager.StreamingFormat.DASH,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const segmentInfo = {
|
|
134
|
+
type: 'video',
|
|
135
|
+
init: false,
|
|
136
|
+
duration: 3.33,
|
|
137
|
+
mimeType: 'application/mp4',
|
|
138
|
+
codecs: 'avc1.42001e',
|
|
139
|
+
bandwidth: 5234167,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
describe('configuration', () => {
|
|
143
|
+
it('does not modify requests when disabled', () => {
|
|
144
|
+
const r = ObjectUtils.cloneObject(request);
|
|
145
|
+
|
|
146
|
+
cmcdManager.applyManifestData(r, manifestInfo);
|
|
147
|
+
expect(r.uris[0]).toBe(request.uris[0]);
|
|
148
|
+
|
|
149
|
+
cmcdManager.applySegmentData(r, segmentInfo);
|
|
150
|
+
expect(r.uris[0]).toBe(request.uris[0]);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('generates a session id if not provided', () => {
|
|
154
|
+
config.enabled = true;
|
|
155
|
+
cmcdManager = new CmcdManager(playerInterface, config);
|
|
156
|
+
|
|
157
|
+
const r = ObjectUtils.cloneObject(request);
|
|
158
|
+
|
|
159
|
+
cmcdManager.applyManifestData(r, manifestInfo);
|
|
160
|
+
const regex = new RegExp(`sid%3D%22${uuidRegex}%22`, 'i');
|
|
161
|
+
expect(regex.test(r.uris[0])).toBe(true);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe('query mode', () => {
|
|
166
|
+
beforeAll(() => {
|
|
167
|
+
config.sessionId = sid;
|
|
168
|
+
cmcdManager = new CmcdManager(playerInterface, config);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('modifies manifest request uris', () => {
|
|
172
|
+
const r = ObjectUtils.cloneObject(request);
|
|
173
|
+
cmcdManager.applyManifestData(r, manifestInfo);
|
|
174
|
+
const uri = 'https://test.com/test.mpd?CMCD=cid%3D%22testing%22%2C' +
|
|
175
|
+
'mtp%3D10000%2Cot%3Dm%2Csf%3Dd%2C' +
|
|
176
|
+
'sid%3D%222ed2d1cd-970b-48f2-bfb3-50a79e87cfa3%22%2Csu';
|
|
177
|
+
expect(r.uris[0]).toBe(uri);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('modifies segment request uris', () => {
|
|
181
|
+
const r = ObjectUtils.cloneObject(request);
|
|
182
|
+
cmcdManager.applySegmentData(r, segmentInfo);
|
|
183
|
+
const uri = 'https://test.com/test.mpd?CMCD=bl%3D21200%2Cbr%3D5234%2Ccid%3D%22' +
|
|
184
|
+
'testing%22%2Cd%3D3330%2Cmtp%3D10000%2Cot%3Dv%2Csf%3Dd%2C' +
|
|
185
|
+
'sid%3D%222ed2d1cd-970b-48f2-bfb3-50a79e87cfa3%22%2Cst%3Dv%2Csu%2C' +
|
|
186
|
+
'tb%3D4000';
|
|
187
|
+
expect(r.uris[0]).toBe(uri);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('modifies text request uris', () => {
|
|
191
|
+
const r = ObjectUtils.cloneObject(request);
|
|
192
|
+
cmcdManager.applyTextData(r);
|
|
193
|
+
const uri = 'https://test.com/test.mpd?CMCD=cid%3D%22' +
|
|
194
|
+
'testing%22%2Cmtp%3D10000%2Cot%3Dc%2Csf%3Dd%2C' +
|
|
195
|
+
'sid%3D%222ed2d1cd-970b-48f2-bfb3-50a79e87cfa3%22%2Csu';
|
|
196
|
+
expect(r.uris[0]).toBe(uri);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
describe('header mode', () => {
|
|
201
|
+
beforeAll(() => {
|
|
202
|
+
config.useHeaders = true;
|
|
203
|
+
cmcdManager = new CmcdManager(playerInterface, config);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('modifies manifest request headers', () => {
|
|
207
|
+
const r = ObjectUtils.cloneObject(request);
|
|
208
|
+
cmcdManager.applyManifestData(r, manifestInfo);
|
|
209
|
+
expect(r.headers).toEqual({
|
|
210
|
+
'testing': '1234',
|
|
211
|
+
'CMCD-Object': 'ot=m',
|
|
212
|
+
'CMCD-Request': 'mtp=10000,su',
|
|
213
|
+
'CMCD-Session': 'cid="testing",sf=d,' +
|
|
214
|
+
'sid="2ed2d1cd-970b-48f2-bfb3-50a79e87cfa3"',
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('modifies segment request headers', () => {
|
|
219
|
+
const r = ObjectUtils.cloneObject(request);
|
|
220
|
+
cmcdManager.applySegmentData(r, segmentInfo);
|
|
221
|
+
expect(r.headers).toEqual({
|
|
222
|
+
'testing': '1234',
|
|
223
|
+
'CMCD-Object': 'br=5234,d=3330,ot=v,tb=4000',
|
|
224
|
+
'CMCD-Request': 'bl=21200,mtp=10000,su',
|
|
225
|
+
'CMCD-Session': 'cid="testing",sf=d,' +
|
|
226
|
+
'sid="2ed2d1cd-970b-48f2-bfb3-50a79e87cfa3",st=v',
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
describe('src= mode', () => {
|
|
232
|
+
it('modifies media stream uris', () => {
|
|
233
|
+
const r = cmcdManager
|
|
234
|
+
.appendSrcData('https://test.com/test.mp4', 'video/mp4');
|
|
235
|
+
const uri = 'https://test.com/test.mp4?CMCD=cid%3D%22testing%22%2C' +
|
|
236
|
+
'mtp%3D10000%2Cot%3Dav%2Csf%3Dd%2C' +
|
|
237
|
+
'sid%3D%222ed2d1cd-970b-48f2-bfb3-50a79e87cfa3%22%2Csu';
|
|
238
|
+
expect(r).toBe(uri);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('modifies manifest stream uris', () => {
|
|
242
|
+
const r = cmcdManager
|
|
243
|
+
.appendSrcData('https://test.com/test.m3u8', 'application/x-mpegurl');
|
|
244
|
+
const uri = 'https://test.com/test.m3u8?CMCD=cid%3D%22testing%22%2C' +
|
|
245
|
+
'mtp%3D10000%2Cot%3Dm%2Csf%3Dd%2C' +
|
|
246
|
+
'sid%3D%222ed2d1cd-970b-48f2-bfb3-50a79e87cfa3%22%2Csu';
|
|
247
|
+
expect(r).toBe(uri);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('modifies text track uris', () => {
|
|
251
|
+
const r = cmcdManager.appendTextTrackData('https://test.com/test.vtt');
|
|
252
|
+
const uri = 'https://test.com/test.vtt?CMCD=cid%3D%22testing%22%2C' +
|
|
253
|
+
'mtp%3D10000%2Cot%3Dc%2Csf%3Dd%2C' +
|
|
254
|
+
'sid%3D%222ed2d1cd-970b-48f2-bfb3-50a79e87cfa3%22%2Csu';
|
|
255
|
+
expect(r).toBe(uri);
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
describe('adheres to the spec', () => {
|
|
260
|
+
beforeAll(() => {
|
|
261
|
+
cmcdManager.setBuffering(false);
|
|
262
|
+
cmcdManager.setBuffering(true);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('sends bs only once', () => {
|
|
266
|
+
let r = ObjectUtils.cloneObject(request);
|
|
267
|
+
cmcdManager.applySegmentData(r, segmentInfo);
|
|
268
|
+
expect(r.headers['CMCD-Status']).toContain('bs');
|
|
269
|
+
|
|
270
|
+
r = ObjectUtils.cloneObject(request);
|
|
271
|
+
cmcdManager.applySegmentData(r, segmentInfo);
|
|
272
|
+
expect(r.headers['CMCD-Status']).not.toContain('bs');
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('sends su until buffering is complete', () => {
|
|
276
|
+
let r = ObjectUtils.cloneObject(request);
|
|
277
|
+
cmcdManager.applySegmentData(r, segmentInfo);
|
|
278
|
+
expect(r.headers['CMCD-Request']).toContain(',su');
|
|
279
|
+
|
|
280
|
+
r = ObjectUtils.cloneObject(request);
|
|
281
|
+
cmcdManager.applySegmentData(r, segmentInfo);
|
|
282
|
+
expect(r.headers['CMCD-Request']).toContain(',su');
|
|
283
|
+
|
|
284
|
+
cmcdManager.setBuffering(false);
|
|
285
|
+
r = ObjectUtils.cloneObject(request);
|
|
286
|
+
cmcdManager.applySegmentData(r, segmentInfo);
|
|
287
|
+
expect(r.headers['CMCD-Request']).not.toContain(',su');
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
});
|
|
@@ -16,14 +16,14 @@ describe('StreamUtils', () => {
|
|
|
16
16
|
/** @type {!jasmine.Spy} */
|
|
17
17
|
let decodingInfoSpy;
|
|
18
18
|
|
|
19
|
-
const originalDecodingInfo =
|
|
19
|
+
const originalDecodingInfo = navigator.mediaCapabilities.decodingInfo;
|
|
20
20
|
|
|
21
21
|
beforeEach(() => {
|
|
22
22
|
decodingInfoSpy = jasmine.createSpy('decodingInfo');
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
afterEach(() => {
|
|
26
|
-
|
|
26
|
+
navigator.mediaCapabilities.decodingInfo = originalDecodingInfo;
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
describe('filterStreamsByLanguageAndRole', () => {
|
|
@@ -511,7 +511,7 @@ describe('StreamUtils', () => {
|
|
|
511
511
|
});
|
|
512
512
|
|
|
513
513
|
it('handles decodingInfo exception', async () => {
|
|
514
|
-
|
|
514
|
+
navigator.mediaCapabilities.decodingInfo =
|
|
515
515
|
shaka.test.Util.spyFunc(decodingInfoSpy);
|
|
516
516
|
// If decodingInfo() fails, setDecodingInfo should finish without throwing
|
|
517
517
|
// an exception, and the variant should have no decodingInfo result.
|
|
@@ -537,6 +537,47 @@ describe('StreamUtils', () => {
|
|
|
537
537
|
expect(manifest.variants.length).toBe(1);
|
|
538
538
|
expect(manifest.variants[0].decodingInfos.length).toBe(0);
|
|
539
539
|
});
|
|
540
|
+
|
|
541
|
+
it('includes transferFunction in config when hdr', async () => {
|
|
542
|
+
const originalDecodingInfo = navigator.mediaCapabilities.decodingInfo;
|
|
543
|
+
|
|
544
|
+
try {
|
|
545
|
+
navigator.mediaCapabilities.decodingInfo =
|
|
546
|
+
shaka.test.Util.spyFunc(decodingInfoSpy);
|
|
547
|
+
|
|
548
|
+
manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
549
|
+
manifest.addVariant(0, (variant) => {
|
|
550
|
+
variant.addVideo(0, (stream) => {
|
|
551
|
+
stream.mime('video/mp4', 'avc1.640028');
|
|
552
|
+
stream.hdr = 'SDR';
|
|
553
|
+
});
|
|
554
|
+
});
|
|
555
|
+
manifest.addVariant(1, (variant) => {
|
|
556
|
+
variant.addVideo(1, (stream) => {
|
|
557
|
+
stream.mime('video/mp4', 'hvc1.2.4.L150.90');
|
|
558
|
+
stream.hdr = 'PQ';
|
|
559
|
+
});
|
|
560
|
+
});
|
|
561
|
+
manifest.addVariant(2, (variant) => {
|
|
562
|
+
variant.addVideo(2, (stream) => {
|
|
563
|
+
stream.mime('video/mp4', 'hvc1.2.4.L153.B0');
|
|
564
|
+
stream.hdr = 'HLG';
|
|
565
|
+
});
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
await StreamUtils.getDecodingInfosForVariants(manifest.variants,
|
|
570
|
+
/* usePersistentLicenses= */ false, /* srcEquals= */ false);
|
|
571
|
+
expect(decodingInfoSpy.calls.argsFor(0)[0].video.transferFunction)
|
|
572
|
+
.toBe('srgb');
|
|
573
|
+
expect(decodingInfoSpy.calls.argsFor(1)[0].video.transferFunction)
|
|
574
|
+
.toBe('pq');
|
|
575
|
+
expect(decodingInfoSpy.calls.argsFor(2)[0].video.transferFunction)
|
|
576
|
+
.toBe('hlg');
|
|
577
|
+
} finally {
|
|
578
|
+
navigator.mediaCapabilities.decodingInfo = originalDecodingInfo;
|
|
579
|
+
}
|
|
580
|
+
});
|
|
540
581
|
});
|
|
541
582
|
|
|
542
583
|
describe('filterManifest', () => {
|
|
@@ -826,7 +867,7 @@ describe('StreamUtils', () => {
|
|
|
826
867
|
});
|
|
827
868
|
});
|
|
828
869
|
});
|
|
829
|
-
|
|
870
|
+
navigator.mediaCapabilities.decodingInfo =
|
|
830
871
|
shaka.test.Util.spyFunc(decodingInfoSpy);
|
|
831
872
|
decodingInfoSpy.and.callFake((config) => {
|
|
832
873
|
const res = config.video.contentType.includes('notsmooth') ?
|
|
@@ -808,6 +808,27 @@ goog.Uri.QueryData.prototype.add = function(key, value) {
|
|
|
808
808
|
return this;
|
|
809
809
|
};
|
|
810
810
|
|
|
811
|
+
/**
|
|
812
|
+
* Sets a key value pair and removes all other keys with the same value.
|
|
813
|
+
*
|
|
814
|
+
* @param {string} key Name.
|
|
815
|
+
* @param {string} value Value.
|
|
816
|
+
* @return {!goog.Uri.QueryData} Instance of this object.
|
|
817
|
+
*/
|
|
818
|
+
goog.Uri.QueryData.prototype.set = function(key, value) {
|
|
819
|
+
this.ensureKeyMapInitialized_();
|
|
820
|
+
// Invalidate the cache.
|
|
821
|
+
this.encodedQuery_ = null;
|
|
822
|
+
|
|
823
|
+
if (!this.keyMap_.hasOwnProperty(key)) {
|
|
824
|
+
this.add(key, value);
|
|
825
|
+
} else {
|
|
826
|
+
this.keyMap_[key] = [value];
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
return this;
|
|
830
|
+
};
|
|
831
|
+
|
|
811
832
|
|
|
812
833
|
/**
|
|
813
834
|
* @return {string} Encoded query string.
|
package/ui/airplay_button.js
CHANGED
|
@@ -36,6 +36,7 @@ shaka.ui.AirPlayButton = class extends shaka.ui.Element {
|
|
|
36
36
|
/** @private {!HTMLButtonElement} */
|
|
37
37
|
this.airplayButton_ = shaka.util.Dom.createButton();
|
|
38
38
|
this.airplayButton_.classList.add('shaka-airplay-button');
|
|
39
|
+
this.airplayButton_.classList.add('shaka-tooltip');
|
|
39
40
|
this.airplayButton_.ariaPressed = 'false';
|
|
40
41
|
|
|
41
42
|
/** @private {!HTMLElement} */
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
goog.provide('shaka.ui.AudioLanguageSelection');
|
|
9
9
|
|
|
10
|
+
goog.require('shaka.ui.Controls');
|
|
10
11
|
goog.require('shaka.ui.Enums');
|
|
11
12
|
goog.require('shaka.ui.LanguageUtils');
|
|
12
13
|
goog.require('shaka.ui.Locales');
|
|
@@ -31,6 +32,7 @@ shaka.ui.AudioLanguageSelection = class extends shaka.ui.SettingsMenu {
|
|
|
31
32
|
super(parent, controls, shaka.ui.Enums.MaterialDesignIcons.LANGUAGE);
|
|
32
33
|
|
|
33
34
|
this.button.classList.add('shaka-language-button');
|
|
35
|
+
this.button.classList.add('shaka-tooltip-status');
|
|
34
36
|
this.menu.classList.add('shaka-audio-languages');
|
|
35
37
|
|
|
36
38
|
this.eventManager.listen(
|
|
@@ -71,6 +73,8 @@ shaka.ui.AudioLanguageSelection = class extends shaka.ui.SettingsMenu {
|
|
|
71
73
|
|
|
72
74
|
this.controls.dispatchEvent(
|
|
73
75
|
new shaka.util.FakeEvent('languageselectionupdated'));
|
|
76
|
+
|
|
77
|
+
this.button.setAttribute('shaka-status', this.currentSelection.innerText);
|
|
74
78
|
}
|
|
75
79
|
|
|
76
80
|
/** @private */
|
|
@@ -118,3 +122,6 @@ shaka.ui.AudioLanguageSelection.Factory = class {
|
|
|
118
122
|
|
|
119
123
|
shaka.ui.OverflowMenu.registerElement(
|
|
120
124
|
'language', new shaka.ui.AudioLanguageSelection.Factory());
|
|
125
|
+
|
|
126
|
+
shaka.ui.Controls.registerElement(
|
|
127
|
+
'language', new shaka.ui.AudioLanguageSelection.Factory());
|
package/ui/cast_button.js
CHANGED
|
@@ -41,6 +41,7 @@ shaka.ui.CastButton = class extends shaka.ui.Element {
|
|
|
41
41
|
/** @private {!HTMLButtonElement} */
|
|
42
42
|
this.castButton_ = shaka.util.Dom.createButton();
|
|
43
43
|
this.castButton_.classList.add('shaka-cast-button');
|
|
44
|
+
this.castButton_.classList.add('shaka-tooltip');
|
|
44
45
|
this.castButton_.ariaPressed = 'false';
|
|
45
46
|
|
|
46
47
|
/** @private {!HTMLElement} */
|
|
@@ -101,9 +102,8 @@ shaka.ui.CastButton = class extends shaka.ui.Element {
|
|
|
101
102
|
} catch (error) {
|
|
102
103
|
this.castButton_.disabled = false;
|
|
103
104
|
if (error.code != shaka.util.Error.Code.CAST_CANCELED_BY_USER) {
|
|
104
|
-
this.controls.dispatchEvent(new shaka.util.FakeEvent(
|
|
105
|
-
|
|
106
|
-
}));
|
|
105
|
+
this.controls.dispatchEvent(new shaka.util.FakeEvent(
|
|
106
|
+
'error', (new Map()).set('detail', error)));
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
goog.provide('shaka.ui.ContextMenu');
|
|
9
|
+
|
|
10
|
+
goog.require('goog.asserts');
|
|
11
|
+
goog.require('shaka.log');
|
|
12
|
+
goog.require('shaka.ui.Element');
|
|
13
|
+
goog.require('shaka.ui.Utils');
|
|
14
|
+
goog.require('shaka.util.Dom');
|
|
15
|
+
goog.requireType('shaka.ui.Controls');
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @extends {shaka.ui.Element}
|
|
20
|
+
* @final
|
|
21
|
+
* @export
|
|
22
|
+
*/
|
|
23
|
+
shaka.ui.ContextMenu = class extends shaka.ui.Element {
|
|
24
|
+
/**
|
|
25
|
+
* @param {!HTMLElement} parent
|
|
26
|
+
* @param {!shaka.ui.Controls} controls
|
|
27
|
+
*/
|
|
28
|
+
constructor(parent, controls) {
|
|
29
|
+
super(parent, controls);
|
|
30
|
+
|
|
31
|
+
/** @private {!shaka.extern.UIConfiguration} */
|
|
32
|
+
this.config_ = this.controls.getConfig();
|
|
33
|
+
|
|
34
|
+
/** @private {HTMLElement} */
|
|
35
|
+
this.controlsContainer_ = this.controls.getControlsContainer();
|
|
36
|
+
|
|
37
|
+
/** @private {!Array.<shaka.extern.IUIElement>} */
|
|
38
|
+
this.children_ = [];
|
|
39
|
+
|
|
40
|
+
/** @private {!HTMLElement} */
|
|
41
|
+
this.contextMenu_ = shaka.util.Dom.createHTMLElement('div');
|
|
42
|
+
this.contextMenu_.classList.add('shaka-no-propagation');
|
|
43
|
+
this.contextMenu_.classList.add('shaka-context-menu');
|
|
44
|
+
this.contextMenu_.classList.add('shaka-hidden');
|
|
45
|
+
|
|
46
|
+
this.controlsContainer_.appendChild(this.contextMenu_);
|
|
47
|
+
|
|
48
|
+
this.eventManager.listen(this.controlsContainer_, 'contextmenu', (e) => {
|
|
49
|
+
if (this.contextMenu_.classList.contains('shaka-hidden')) {
|
|
50
|
+
e.preventDefault();
|
|
51
|
+
|
|
52
|
+
const controlsLocation =
|
|
53
|
+
this.controlsContainer_.getBoundingClientRect();
|
|
54
|
+
this.contextMenu_.style.left = `${e.clientX - controlsLocation.left}px`;
|
|
55
|
+
this.contextMenu_.style.top = `${e.clientY - controlsLocation.top}px`;
|
|
56
|
+
|
|
57
|
+
shaka.ui.Utils.setDisplay(this.contextMenu_, true);
|
|
58
|
+
} else {
|
|
59
|
+
shaka.ui.Utils.setDisplay(this.contextMenu_, false);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
this.eventManager.listen(window, 'click', () => {
|
|
64
|
+
shaka.ui.Utils.setDisplay(this.contextMenu_, false);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
this.createChildren_();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** @override */
|
|
71
|
+
release() {
|
|
72
|
+
this.controlsContainer_ = null;
|
|
73
|
+
|
|
74
|
+
for (const element of this.children_) {
|
|
75
|
+
element.release();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this.children_ = [];
|
|
79
|
+
super.release();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param {string} name
|
|
84
|
+
* @param {!shaka.extern.IUIElement.Factory} factory
|
|
85
|
+
* @export
|
|
86
|
+
*/
|
|
87
|
+
static registerElement(name, factory) {
|
|
88
|
+
shaka.ui.ContextMenu.elementNamesToFactories_.set(name, factory);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @private
|
|
93
|
+
*/
|
|
94
|
+
createChildren_() {
|
|
95
|
+
for (const name of this.config_.contextMenuElements) {
|
|
96
|
+
const factory =
|
|
97
|
+
shaka.ui.ContextMenu.elementNamesToFactories_.get(name);
|
|
98
|
+
if (factory) {
|
|
99
|
+
goog.asserts.assert(this.controls, 'Controls should not be null!');
|
|
100
|
+
this.children_.push(factory.create(this.contextMenu_, this.controls));
|
|
101
|
+
} else {
|
|
102
|
+
shaka.log.alwaysWarn('Unrecognized context menu element:', name);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/** @private {!Map.<string, !shaka.extern.IUIElement.Factory>} */
|
|
109
|
+
shaka.ui.ContextMenu.elementNamesToFactories_ = new Map();
|