shaka-player 4.2.3 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/sync-labels.yaml +1 -1
- package/.github/workflows/update-issues.yaml +1 -1
- package/AUTHORS +5 -0
- package/CHANGELOG.md +85 -39
- package/CONTRIBUTORS +5 -0
- package/README.md +14 -0
- package/demo/common/assets.js +58 -0
- package/demo/common/message_ids.js +22 -1
- package/demo/config.js +89 -5
- package/demo/demo.less +7 -1
- package/demo/index.html +8 -1
- package/demo/locales/en.json +21 -1
- package/demo/locales/source.json +82 -2
- package/demo/main.js +57 -1
- package/demo/search.js +2 -0
- package/demo/visualizer.js +337 -0
- package/demo/visualizer_button.js +103 -0
- package/dist/controls.css +1 -1
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +83 -58
- package/dist/demo.compiled.debug.map +4 -4
- package/dist/demo.compiled.js +83 -58
- package/dist/demo.compiled.map +4 -4
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +18 -11
- package/dist/locales.js +32 -32
- package/dist/receiver.compiled.debug.js +23 -19
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +23 -19
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +178 -27
- package/dist/shaka-player.compiled.debug.d.ts +178 -27
- package/dist/shaka-player.compiled.debug.externs.js +163 -32
- package/dist/shaka-player.compiled.debug.js +488 -422
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +163 -32
- package/dist/shaka-player.compiled.js +895 -857
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +178 -27
- package/dist/shaka-player.ui.debug.d.ts +178 -27
- package/dist/shaka-player.ui.debug.externs.js +163 -32
- package/dist/shaka-player.ui.debug.js +519 -450
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +163 -32
- package/dist/shaka-player.ui.js +1053 -1015
- package/dist/shaka-player.ui.map +4 -4
- package/docs/design/lcevc-architecture.png +0 -0
- package/docs/design/lcevc-demo.png +0 -0
- package/docs/design/lcevc-integration.md +72 -0
- package/docs/tutorials/fairplay.md +7 -0
- package/docs/tutorials/faq.md +1 -1
- package/docs/tutorials/ui.md +7 -7
- package/docs/tutorials/upgrade.md +6 -0
- package/externs/ima.js +13 -1
- package/externs/lcevc.js +81 -0
- package/externs/mux.js +4 -42
- package/externs/shaka/ads.js +11 -1
- package/externs/shaka/manifest_parser.js +5 -1
- package/externs/shaka/player.js +178 -15
- package/lib/abr/simple_abr_manager.js +7 -0
- package/lib/ads/ad_manager.js +0 -8
- package/lib/ads/client_side_ad.js +15 -0
- package/lib/ads/client_side_ad_manager.js +21 -8
- package/lib/ads/server_side_ad.js +15 -0
- package/lib/ads/server_side_ad_manager.js +3 -3
- package/lib/cast/cast_proxy.js +2 -1
- package/lib/cast/cast_receiver.js +19 -5
- package/lib/cast/cast_sender.js +2 -1
- package/lib/cast/cast_utils.js +18 -6
- package/lib/cea/dummy_cea_parser.js +28 -0
- package/lib/cea/i_cea_parser.js +14 -2
- package/lib/cea/mp4_cea_parser.js +87 -5
- package/lib/config/auto_show_text.js +33 -0
- package/lib/dash/dash_parser.js +1 -1
- package/lib/hls/hls_parser.js +799 -376
- package/lib/lcevc/lcevc_dil.js +152 -0
- package/lib/media/closed_caption_parser.js +9 -6
- package/lib/media/drm_engine.js +14 -6
- package/lib/media/media_source_engine.js +112 -26
- package/lib/media/playhead.js +23 -1
- package/lib/media/presentation_timeline.js +26 -11
- package/lib/media/segment_reference.js +49 -1
- package/lib/media/streaming_engine.js +145 -24
- package/lib/media/transmuxer.js +79 -18
- package/lib/offline/storage.js +1 -0
- package/lib/player.js +477 -85
- package/lib/polyfill/patchedmediakeys_apple.js +55 -1
- package/lib/text/ui_text_displayer.js +5 -5
- package/lib/text/vtt_text_parser.js +35 -1
- package/lib/util/dom_utils.js +0 -1
- package/lib/util/error.js +17 -0
- package/lib/util/fake_event.js +2 -0
- package/lib/util/id3_utils.js +300 -0
- package/lib/util/manifest_parser_utils.js +4 -0
- package/lib/util/mime_utils.js +3 -6
- package/lib/util/mp4_box_parsers.js +15 -5
- package/lib/util/platform.js +9 -1
- package/lib/util/player_configuration.js +23 -1
- package/lib/util/ts_parser.js +499 -0
- package/maintained-branches.md +2 -2
- package/package.json +4 -1
- package/roadmap.md +27 -7
- package/test/ads/ad_manager_unit.js +180 -29
- package/test/cast/cast_receiver_integration.js +97 -16
- package/test/cast/cast_receiver_unit.js +17 -12
- package/test/cast/cast_utils_unit.js +4 -1
- package/test/dash/dash_parser_content_protection_unit.js +1 -0
- package/test/dash/dash_parser_live_unit.js +1 -0
- package/test/dash/dash_parser_manifest_unit.js +93 -1
- package/test/dash/dash_parser_segment_base_unit.js +1 -0
- package/test/dash/dash_parser_segment_list_unit.js +1 -0
- package/test/dash/dash_parser_segment_template_unit.js +1 -0
- package/test/demo/demo_unit.js +2 -1
- package/test/hls/hls_live_unit.js +253 -19
- package/test/hls/hls_parser_unit.js +500 -12
- package/test/media/closed_caption_parser_unit.js +2 -1
- package/test/media/drm_engine_integration.js +3 -1
- package/test/media/media_source_engine_integration.js +102 -6
- package/test/media/media_source_engine_unit.js +10 -5
- package/test/media/playhead_unit.js +41 -0
- package/test/media/streaming_engine_integration.js +17 -1
- package/test/media/streaming_engine_unit.js +180 -24
- package/test/media/transmuxer_integration.js +21 -10
- package/test/player_integration.js +224 -82
- package/test/player_unit.js +92 -2
- package/test/polyfill/patchedmediakeys_apple_unit.js +124 -0
- package/test/test/assets/id3-metadata.aac +0 -0
- package/test/test/assets/id3-metadata.ts +0 -0
- package/test/test/assets/thumbnails-sprites.vtt +10 -0
- package/test/test/assets/thumbnails.vtt +10 -0
- package/test/test/util/dash_parser_util.js +2 -0
- package/test/test/util/fake_ad.js +21 -0
- package/test/test/util/fake_media_source_engine.js +7 -0
- package/test/test/util/id3_generator.js +100 -0
- package/test/test/util/manifest_generator.js +3 -6
- package/test/test/util/simple_fakes.js +7 -0
- package/test/test/util/stream_generator.js +38 -0
- package/test/test/util/test_scheme.js +43 -14
- package/test/text/vtt_text_parser_unit.js +11 -0
- package/test/ui/text_displayer_layout_unit.js +14 -0
- package/test/util/id3_utils_unit.js +116 -0
- package/test/util/ts_parser_unit.js +77 -0
- package/ui/controls.js +4 -1
- package/ui/less/containers.less +7 -0
- package/ui/settings_menu.js +11 -2
- package/ui/ui.js +43 -4
|
@@ -25,45 +25,156 @@ describe('Ad manager', () => {
|
|
|
25
25
|
/** @type {!HTMLElement} */ (document.createElement('div'));
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
describe('client side', () => {
|
|
29
|
+
it('doesn\'t init if CS IMA is missing', () => {
|
|
30
|
+
const error = createError(
|
|
31
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
32
|
+
shaka.util.Error.Code.CS_IMA_SDK_MISSING);
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
expect(() => adManager.initClientSide(
|
|
35
|
+
adContainer, mockVideo)).toThrow(error);
|
|
36
|
+
});
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
it('doesn\'t request ads until CS is initialized', () => {
|
|
39
|
+
setupFakeIMA();
|
|
40
|
+
const error = createError(
|
|
41
|
+
shaka.util.Error.Severity.RECOVERABLE,
|
|
42
|
+
shaka.util.Error.Code.CS_AD_MANAGER_NOT_INITIALIZED);
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
const request = new google.ima.AdsRequest();
|
|
45
|
+
request.adTagUrl = 'fakeTag';
|
|
46
|
+
|
|
47
|
+
expect(() => adManager.requestClientSideAds(request)).toThrow(error);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('doesn\'t request ads if CS events return no ad', () => {
|
|
51
|
+
setupFakeIMA();
|
|
52
|
+
|
|
53
|
+
const request = new google.ima.AdsRequest();
|
|
54
|
+
request.adTagUrl = 'fakeTag';
|
|
55
|
+
|
|
56
|
+
/** @type {google.ima.AdsLoader} */
|
|
57
|
+
let mockAdsLoaderInstance;
|
|
58
|
+
let numAdsRequested = 0;
|
|
59
|
+
/** @type {google.ima.AdsManager} */
|
|
60
|
+
let mockAdsManagerInstance;
|
|
61
|
+
/** @type {Event} */
|
|
62
|
+
let loadEvent;
|
|
63
|
+
/** @type {Event} */
|
|
64
|
+
let startedEventA;
|
|
65
|
+
/** @type {Event} */
|
|
66
|
+
let startedEventB;
|
|
67
|
+
|
|
68
|
+
/** @suppress {invalidCasts} */
|
|
69
|
+
function makeMocks() {
|
|
70
|
+
const mockAdsLoader = class extends shaka.util.FakeEventTarget {
|
|
71
|
+
constructor(container) {
|
|
72
|
+
super();
|
|
73
|
+
mockAdsLoaderInstance = /** @type {!google.ima.AdsLoader} */ (this);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
getSettings() {
|
|
77
|
+
return {
|
|
78
|
+
setPlayerType: (type) => {},
|
|
79
|
+
setPlayerVersion: (version) => {},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
45
82
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
83
|
+
requestAds(imaRequest) {
|
|
84
|
+
numAdsRequested += 1;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
window['google'].ima.AdsLoader = mockAdsLoader;
|
|
51
88
|
|
|
52
|
-
|
|
53
|
-
|
|
89
|
+
loadEvent = /** @type {!google.ima.AdsManagerLoadedEvent} */ (
|
|
90
|
+
new shaka.util.FakeEvent(
|
|
91
|
+
google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED));
|
|
92
|
+
loadEvent.getAdsManager = () => {
|
|
93
|
+
const MockAdManager = class extends shaka.util.FakeEventTarget {
|
|
94
|
+
constructor() {
|
|
95
|
+
super();
|
|
96
|
+
mockAdsManagerInstance =
|
|
97
|
+
/** @type {!google.ima.AdsManager} */ (this);
|
|
98
|
+
}
|
|
54
99
|
|
|
55
|
-
|
|
100
|
+
getCuePoints() {
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
getVolume() {
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
return new MockAdManager();
|
|
109
|
+
};
|
|
110
|
+
startedEventA = /** @type {!google.ima.AdEvent} */ (
|
|
111
|
+
new shaka.util.FakeEvent(google.ima.AdEvent.Type.STARTED));
|
|
112
|
+
startedEventA.getAd = () => {
|
|
113
|
+
return null;
|
|
114
|
+
};
|
|
115
|
+
startedEventB = /** @type {!google.ima.AdEvent} */ (
|
|
116
|
+
new shaka.util.FakeEvent(google.ima.AdEvent.Type.STARTED));
|
|
117
|
+
startedEventB.getAd = () => {
|
|
118
|
+
return {};
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
makeMocks();
|
|
122
|
+
|
|
123
|
+
// Set up event listeners.
|
|
124
|
+
const eventManager = new shaka.util.EventManager();
|
|
125
|
+
let loaded = false;
|
|
126
|
+
let numAdStarted = 0;
|
|
127
|
+
const AdManager = shaka.ads.AdManager;
|
|
128
|
+
eventManager.listen(adManager, AdManager.IMA_AD_MANAGER_LOADED, () => {
|
|
129
|
+
loaded = true;
|
|
130
|
+
});
|
|
131
|
+
eventManager.listen(adManager, AdManager.AD_STARTED, () => {
|
|
132
|
+
numAdStarted += 1;
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Set up the ad manager.
|
|
136
|
+
adManager.initClientSide(adContainer, mockVideo);
|
|
137
|
+
goog.asserts.assert(loadEvent != null, 'loadEvent exists');
|
|
138
|
+
mockAdsLoaderInstance.dispatchEvent(/** @type {!Event} */ (loadEvent));
|
|
139
|
+
expect(loaded).toBe(true);
|
|
140
|
+
|
|
141
|
+
// Request an ad, but create an event with no ad.
|
|
142
|
+
adManager.requestClientSideAds(request);
|
|
143
|
+
expect(numAdsRequested).toBe(1);
|
|
144
|
+
mockAdsManagerInstance.dispatchEvent(/** @type {!Event} */ (
|
|
145
|
+
startedEventA));
|
|
146
|
+
expect(numAdStarted).toBe(0);
|
|
147
|
+
|
|
148
|
+
// Request another ad. This time, the IMA event has an ad, so the ad
|
|
149
|
+
// manager should fire a started event.
|
|
150
|
+
adManager.requestClientSideAds(request);
|
|
151
|
+
expect(numAdsRequested).toBe(2);
|
|
152
|
+
mockAdsManagerInstance.dispatchEvent(/** @type {!Event} */ (
|
|
153
|
+
startedEventB));
|
|
154
|
+
expect(numAdStarted).toBe(1);
|
|
155
|
+
});
|
|
56
156
|
});
|
|
57
157
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
158
|
+
describe('server side', () => {
|
|
159
|
+
it('doesn\'t init if SS IMA is missing', () => {
|
|
160
|
+
const error = createError(
|
|
161
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
162
|
+
shaka.util.Error.Code.SS_IMA_SDK_MISSING);
|
|
163
|
+
|
|
164
|
+
expect(() => adManager.initServerSide(
|
|
165
|
+
adContainer, mockVideo)).toThrow(error);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('doesn\'t request streams until SS is initialized', () => {
|
|
169
|
+
setupFakeIMA();
|
|
170
|
+
const error = createError(
|
|
171
|
+
shaka.util.Error.Severity.RECOVERABLE,
|
|
172
|
+
shaka.util.Error.Code.SS_AD_MANAGER_NOT_INITIALIZED);
|
|
63
173
|
|
|
64
|
-
|
|
174
|
+
const request = new google.ima.dai.api.StreamRequest();
|
|
65
175
|
|
|
66
|
-
|
|
176
|
+
expect(() => adManager.requestServerSideStream(request)).toThrow(error);
|
|
177
|
+
});
|
|
67
178
|
});
|
|
68
179
|
|
|
69
180
|
/**
|
|
@@ -86,5 +197,45 @@ describe('Ad manager', () => {
|
|
|
86
197
|
window['google'].ima.AdsRequest = class {};
|
|
87
198
|
window['google'].ima.dai.api = {};
|
|
88
199
|
window['google'].ima.dai.api.StreamRequest = class {};
|
|
200
|
+
window['google'].ima.settings = {};
|
|
201
|
+
window['google'].ima.settings.setLocale = (locale) => {};
|
|
202
|
+
window['google'].ima.AdsManagerLoadedEvent = {};
|
|
203
|
+
window['google'].ima.AdsManagerLoadedEvent.Type = {
|
|
204
|
+
ADS_MANAGER_LOADED: 'ADS_MANAGER_LOADED',
|
|
205
|
+
};
|
|
206
|
+
window['google'].ima.AdErrorEvent = {};
|
|
207
|
+
window['google'].ima.AdErrorEvent.Type = {
|
|
208
|
+
AD_ERROR: 'AD_ERROR',
|
|
209
|
+
};
|
|
210
|
+
window['google'].ima.AdEvent = {};
|
|
211
|
+
window['google'].ima.AdEvent.Type = {
|
|
212
|
+
CONTENT_PAUSE_REQUESTED: 'CONTENT_PAUSE_REQUESTED',
|
|
213
|
+
STARTED: 'STARTED',
|
|
214
|
+
FIRST_QUARTILE: 'FIRST_QUARTILE',
|
|
215
|
+
MIDPOINT: 'MIDPOINT',
|
|
216
|
+
THIRD_QUARTILE: 'THIRD_QUARTILE',
|
|
217
|
+
COMPLETE: 'COMPLETE',
|
|
218
|
+
CONTENT_RESUME_REQUESTED: 'CONTENT_RESUME_REQUESTED',
|
|
219
|
+
ALL_ADS_COMPLETED: 'ALL_ADS_COMPLETED',
|
|
220
|
+
SKIPPED: 'SKIPPED',
|
|
221
|
+
VOLUME_CHANGED: 'VOLUME_CHANGED',
|
|
222
|
+
VOLUME_MUTED: 'VOLUME_MUTED',
|
|
223
|
+
PAUSED: 'PAUSED',
|
|
224
|
+
RESUMED: 'RESUMED',
|
|
225
|
+
SKIPPABLE_STATE_CHANGED: 'SKIPPABLE_STATE_CHANGED',
|
|
226
|
+
CLICK: 'CLICK',
|
|
227
|
+
AD_PROGRESS: 'AD_PROGRESS',
|
|
228
|
+
AD_BUFFERING: 'AD_BUFFERING',
|
|
229
|
+
IMPRESSION: 'IMPRESSION',
|
|
230
|
+
DURATION_CHANGE: 'DURATION_CHANGE',
|
|
231
|
+
USER_CLOSE: 'USER_CLOSE',
|
|
232
|
+
LOADED: 'LOADED',
|
|
233
|
+
LINEAR_CHANGED: 'LINEAR_CHANGED',
|
|
234
|
+
AD_METADATA: 'AD_METADATA',
|
|
235
|
+
LOG: 'LOG',
|
|
236
|
+
AD_BREAK_READY: 'AD_BREAK_READY',
|
|
237
|
+
INTERACTION: 'INTERACTION',
|
|
238
|
+
};
|
|
239
|
+
window['google'].ima.AdDisplayContainer = class { initialize() {} };
|
|
89
240
|
}
|
|
90
241
|
});
|
|
@@ -34,6 +34,8 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
34
34
|
|
|
35
35
|
/** @type {shaka.util.PublicPromise} */
|
|
36
36
|
let messageWaitPromise;
|
|
37
|
+
/** @type {Array.<string>} */
|
|
38
|
+
let pendingMessages = null;
|
|
37
39
|
|
|
38
40
|
/** @type {!Array.<function()>} */
|
|
39
41
|
let toRestore;
|
|
@@ -91,6 +93,9 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
91
93
|
toRestore = [];
|
|
92
94
|
pendingWaitWrapperCalls = 0;
|
|
93
95
|
|
|
96
|
+
messageWaitPromise = null;
|
|
97
|
+
pendingMessages = null;
|
|
98
|
+
|
|
94
99
|
fakeInitState = {
|
|
95
100
|
player: {
|
|
96
101
|
configure: {},
|
|
@@ -118,6 +123,12 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
118
123
|
player = null;
|
|
119
124
|
video = null;
|
|
120
125
|
receiver = null;
|
|
126
|
+
|
|
127
|
+
if (messageWaitPromise) {
|
|
128
|
+
messageWaitPromise.resolve([]);
|
|
129
|
+
}
|
|
130
|
+
messageWaitPromise = null;
|
|
131
|
+
pendingMessages = null;
|
|
121
132
|
});
|
|
122
133
|
|
|
123
134
|
afterAll(() => {
|
|
@@ -128,6 +139,66 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
128
139
|
}
|
|
129
140
|
});
|
|
130
141
|
|
|
142
|
+
describe('without drm', () => {
|
|
143
|
+
it('sends reasonably-sized updates', async () => {
|
|
144
|
+
// Use an unencrypted asset.
|
|
145
|
+
fakeInitState.manifest = 'test:sintel';
|
|
146
|
+
|
|
147
|
+
const p = waitForLoadedData();
|
|
148
|
+
|
|
149
|
+
// Start the process of loading by sending a fake init message.
|
|
150
|
+
fakeConnectedSenders(1);
|
|
151
|
+
fakeIncomingMessage({
|
|
152
|
+
type: 'init',
|
|
153
|
+
initState: fakeInitState,
|
|
154
|
+
appData: {},
|
|
155
|
+
}, mockShakaMessageBus);
|
|
156
|
+
|
|
157
|
+
await p;
|
|
158
|
+
// Wait for an update message.
|
|
159
|
+
const messages = await waitForUpdateMessages();
|
|
160
|
+
for (const message of messages) {
|
|
161
|
+
// Check that the update message is of a reasonable size. From previous
|
|
162
|
+
// testing we found that the socket would silently reject data that got
|
|
163
|
+
// too big. 6KB is safely below the limit.
|
|
164
|
+
expect(message.length).toBeLessThan(6000);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('has reasonable average message size', async () => {
|
|
169
|
+
// Use an unencrypted asset.
|
|
170
|
+
fakeInitState.manifest = 'test:sintel';
|
|
171
|
+
|
|
172
|
+
const p = waitForLoadedData();
|
|
173
|
+
|
|
174
|
+
// Start the process of loading by sending a fake init message.
|
|
175
|
+
fakeConnectedSenders(1);
|
|
176
|
+
fakeIncomingMessage({
|
|
177
|
+
type: 'init',
|
|
178
|
+
initState: fakeInitState,
|
|
179
|
+
appData: {},
|
|
180
|
+
}, mockShakaMessageBus);
|
|
181
|
+
|
|
182
|
+
await p;
|
|
183
|
+
|
|
184
|
+
// Collect messages over 50 update cycles, and average their length.
|
|
185
|
+
// Not all properties are passed along on every update message, so
|
|
186
|
+
// the average length is expected to be lower than the length of the first
|
|
187
|
+
// update message.
|
|
188
|
+
let totalLength = 0;
|
|
189
|
+
let totalMessages = 0;
|
|
190
|
+
for (let i = 0; i < 50; i++) {
|
|
191
|
+
// eslint-disable-next-line no-await-in-loop
|
|
192
|
+
const messages = await waitForUpdateMessages();
|
|
193
|
+
for (const message of messages) {
|
|
194
|
+
totalLength += message.length;
|
|
195
|
+
totalMessages += 1;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
expect(totalLength / totalMessages).toBeLessThan(2000);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
131
202
|
filterDescribe('with drm', () => support['com.widevine.alpha'], () => {
|
|
132
203
|
drmIt('sends reasonably-sized updates', async () => {
|
|
133
204
|
// Use an encrypted asset, to make sure DRM info doesn't balloon the size.
|
|
@@ -150,11 +221,13 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
150
221
|
|
|
151
222
|
await p;
|
|
152
223
|
// Wait for an update message.
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
224
|
+
const messages = await waitForUpdateMessages();
|
|
225
|
+
for (const message of messages) {
|
|
226
|
+
// Check that the update message is of a reasonable size. From previous
|
|
227
|
+
// testing we found that the socket would silently reject data that got
|
|
228
|
+
// too big. 6KB is safely below the limit.
|
|
229
|
+
expect(message.length).toBeLessThan(6000);
|
|
230
|
+
}
|
|
158
231
|
});
|
|
159
232
|
|
|
160
233
|
drmIt('has reasonable average message size', async () => {
|
|
@@ -177,17 +250,22 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
177
250
|
}, mockShakaMessageBus);
|
|
178
251
|
|
|
179
252
|
await p;
|
|
180
|
-
|
|
253
|
+
|
|
254
|
+
// Collect messages over 50 update cycles, and average their length.
|
|
181
255
|
// Not all properties are passed along on every update message, so
|
|
182
256
|
// the average length is expected to be lower than the length of the first
|
|
183
257
|
// update message.
|
|
184
258
|
let totalLength = 0;
|
|
259
|
+
let totalMessages = 0;
|
|
185
260
|
for (let i = 0; i < 50; i++) {
|
|
186
261
|
// eslint-disable-next-line no-await-in-loop
|
|
187
|
-
const
|
|
188
|
-
|
|
262
|
+
const messages = await waitForUpdateMessages();
|
|
263
|
+
for (const message of messages) {
|
|
264
|
+
totalLength += message.length;
|
|
265
|
+
totalMessages += 1;
|
|
266
|
+
}
|
|
189
267
|
}
|
|
190
|
-
expect(totalLength /
|
|
268
|
+
expect(totalLength / totalMessages).toBeLessThan(2000);
|
|
191
269
|
});
|
|
192
270
|
});
|
|
193
271
|
|
|
@@ -227,12 +305,12 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
227
305
|
expect(pendingWaitWrapperCalls).toBe(0);
|
|
228
306
|
|
|
229
307
|
// Wait for a final update message before proceeding.
|
|
230
|
-
await
|
|
308
|
+
await waitForUpdateMessages();
|
|
231
309
|
});
|
|
232
310
|
|
|
233
311
|
/**
|
|
234
312
|
* Creates a wrapper around a method on a given prototype, which makes it
|
|
235
|
-
* wait on
|
|
313
|
+
* wait on waitForUpdateMessages before returning, and registers that wrapper
|
|
236
314
|
* to be uninstalled afterwards.
|
|
237
315
|
* The replaced method is expected to be a method that returns a promise.
|
|
238
316
|
* @param {!Object} prototype
|
|
@@ -249,7 +327,7 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
249
327
|
'Waiting for update message before calling ' +
|
|
250
328
|
name + '.' + methodName + '...');
|
|
251
329
|
const originalArguments = Array.from(arguments);
|
|
252
|
-
await
|
|
330
|
+
await waitForUpdateMessages();
|
|
253
331
|
// eslint-disable-next-line no-restricted-syntax
|
|
254
332
|
return original.apply(this, originalArguments);
|
|
255
333
|
};
|
|
@@ -265,7 +343,8 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
265
343
|
});
|
|
266
344
|
}
|
|
267
345
|
|
|
268
|
-
function
|
|
346
|
+
function waitForUpdateMessages() {
|
|
347
|
+
pendingMessages = [];
|
|
269
348
|
messageWaitPromise = new shaka.util.PublicPromise();
|
|
270
349
|
return messageWaitPromise;
|
|
271
350
|
}
|
|
@@ -316,10 +395,12 @@ filterDescribe('CastReceiver', castReceiverIntegrationSupport, () => {
|
|
|
316
395
|
bus.messages.push(CastUtils.deserialize(message));
|
|
317
396
|
// Check to see if it's an update message.
|
|
318
397
|
const parsed = CastUtils.deserialize(message);
|
|
319
|
-
if (parsed.type == 'update' &&
|
|
398
|
+
if (parsed.type == 'update' && pendingMessages) {
|
|
320
399
|
shaka.log.debug('Received update message. Proceeding...');
|
|
321
|
-
|
|
322
|
-
|
|
400
|
+
// The code waiting on this Promise will get an array of all of the
|
|
401
|
+
// messages processed within this tick.
|
|
402
|
+
pendingMessages.push(message);
|
|
403
|
+
messageWaitPromise.resolve(pendingMessages);
|
|
323
404
|
}
|
|
324
405
|
});
|
|
325
406
|
const channel = {
|
|
@@ -245,18 +245,20 @@ filterDescribe('CastReceiver', castReceiverSupport, () => {
|
|
|
245
245
|
const fakeEvent = {type: 'timeupdate'};
|
|
246
246
|
mockVideo.on['timeupdate'](fakeEvent);
|
|
247
247
|
|
|
248
|
-
// There are now "update" and "event" messages, in that
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
248
|
+
// There are now some number of "update" and "event" messages, in that
|
|
249
|
+
// order.
|
|
250
|
+
expect(mockShakaMessageBus.messages).toContain({
|
|
251
|
+
type: 'update',
|
|
252
|
+
update: jasmine.any(Object),
|
|
253
|
+
});
|
|
254
|
+
expect(mockShakaMessageBus.messages).toContain({
|
|
255
|
+
type: 'event',
|
|
256
|
+
targetName: 'video',
|
|
257
|
+
event: jasmine.objectContaining(fakeEvent),
|
|
258
|
+
});
|
|
259
|
+
const eventIndex = mockShakaMessageBus.messages.findIndex(
|
|
260
|
+
(message) => message.type == 'event');
|
|
261
|
+
expect(eventIndex).toBe(mockShakaMessageBus.messages.length - 1);
|
|
260
262
|
});
|
|
261
263
|
});
|
|
262
264
|
|
|
@@ -1105,6 +1107,9 @@ filterDescribe('CastReceiver', castReceiverSupport, () => {
|
|
|
1105
1107
|
for (const name in CastUtils.PlayerGetterMethods) {
|
|
1106
1108
|
player[name] = jasmine.createSpy(name);
|
|
1107
1109
|
}
|
|
1110
|
+
for (const name in CastUtils.LargePlayerGetterMethods) {
|
|
1111
|
+
player[name] = jasmine.createSpy(name);
|
|
1112
|
+
}
|
|
1108
1113
|
for (const name in CastUtils.PlayerGetterMethodsThatRequireLive) {
|
|
1109
1114
|
player[name] = jasmine.createSpy(name);
|
|
1110
1115
|
}
|
|
@@ -34,6 +34,7 @@ describe('CastUtils', () => {
|
|
|
34
34
|
const castMembers = CastUtils.PlayerVoidMethods
|
|
35
35
|
.concat(CastUtils.PlayerPromiseMethods)
|
|
36
36
|
.concat(Object.keys(CastUtils.PlayerGetterMethods))
|
|
37
|
+
.concat(Object.keys(CastUtils.LargePlayerGetterMethods))
|
|
37
38
|
.concat(Object.keys(CastUtils.PlayerGetterMethodsThatRequireLive));
|
|
38
39
|
// eslint-disable-next-line no-restricted-syntax
|
|
39
40
|
const allPlayerMembers = Object.getOwnPropertyNames(shaka.Player.prototype);
|
|
@@ -205,8 +206,10 @@ describe('CastUtils', () => {
|
|
|
205
206
|
|
|
206
207
|
mediaSourceEngine = new shaka.media.MediaSourceEngine(
|
|
207
208
|
video,
|
|
208
|
-
new shaka.test.FakeClosedCaptionParser(),
|
|
209
209
|
new shaka.test.FakeTextDisplayer());
|
|
210
|
+
const config =
|
|
211
|
+
shaka.util.PlayerConfiguration.createDefault().mediaSource;
|
|
212
|
+
mediaSourceEngine.configure(config);
|
|
210
213
|
|
|
211
214
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
212
215
|
const initObject = new Map();
|
|
@@ -53,6 +53,7 @@ describe('DashParser Manifest', () => {
|
|
|
53
53
|
isAutoLowLatencyMode: () => false,
|
|
54
54
|
enableLowLatencyMode: () => {},
|
|
55
55
|
updateDuration: () => {},
|
|
56
|
+
newDrmInfo: (stream) => {},
|
|
56
57
|
};
|
|
57
58
|
});
|
|
58
59
|
|
|
@@ -119,7 +120,97 @@ describe('DashParser Manifest', () => {
|
|
|
119
120
|
});
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
describe('parses and inherits attributes', () => {
|
|
123
|
+
describe('parses and inherits attributes with sequenceMode', () => {
|
|
124
|
+
beforeEach(() => {
|
|
125
|
+
const config = shaka.util.PlayerConfiguration.createDefault().manifest;
|
|
126
|
+
config.dash.sequenceMode = true;
|
|
127
|
+
parser.configure(config);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
makeTestsForEach(
|
|
131
|
+
[
|
|
132
|
+
'<MPD minBufferTime="PT75S">',
|
|
133
|
+
' <Period id="1" duration="PT30S">',
|
|
134
|
+
' <BaseURL>http://example.com</BaseURL>',
|
|
135
|
+
],
|
|
136
|
+
[
|
|
137
|
+
' <AdaptationSet contentType="video" mimeType="video/mp4"',
|
|
138
|
+
' codecs="avc1.4d401f" frameRate="1000000/42000">',
|
|
139
|
+
' <Representation bandwidth="100" width="768" height="576" />',
|
|
140
|
+
' <Representation bandwidth="50" width="576" height="432" />',
|
|
141
|
+
' </AdaptationSet>',
|
|
142
|
+
' <AdaptationSet mimeType="text/vtt"',
|
|
143
|
+
' lang="es" label="spanish">',
|
|
144
|
+
' <Role value="caption" />',
|
|
145
|
+
' <Role value="main" />',
|
|
146
|
+
' <Representation bandwidth="100" />',
|
|
147
|
+
' </AdaptationSet>',
|
|
148
|
+
' <AdaptationSet mimeType="audio/mp4" lang="en" ',
|
|
149
|
+
' codecs="mp4a.40.29">',
|
|
150
|
+
' <Role value="main" />',
|
|
151
|
+
' <Representation bandwidth="100" />',
|
|
152
|
+
' </AdaptationSet>',
|
|
153
|
+
' </Period>',
|
|
154
|
+
'</MPD>',
|
|
155
|
+
],
|
|
156
|
+
shaka.test.ManifestGenerator.generate((manifest) => {
|
|
157
|
+
manifest.sequenceMode = true;
|
|
158
|
+
manifest.anyTimeline();
|
|
159
|
+
manifest.minBufferTime = 75;
|
|
160
|
+
manifest.addPartialVariant((variant) => {
|
|
161
|
+
variant.language = 'en';
|
|
162
|
+
variant.bandwidth = 200;
|
|
163
|
+
variant.primary = true;
|
|
164
|
+
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
165
|
+
stream.bandwidth = 100;
|
|
166
|
+
stream.frameRate = 1000000 / 42000;
|
|
167
|
+
stream.size(768, 576);
|
|
168
|
+
stream.mime('video/mp4', 'avc1.4d401f');
|
|
169
|
+
});
|
|
170
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
171
|
+
stream.bandwidth = 100;
|
|
172
|
+
stream.primary = true;
|
|
173
|
+
stream.roles = ['main'];
|
|
174
|
+
stream.mime('audio/mp4', 'mp4a.40.29');
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
manifest.addPartialVariant((variant) => {
|
|
178
|
+
variant.language = 'en';
|
|
179
|
+
variant.bandwidth = 150;
|
|
180
|
+
variant.primary = true;
|
|
181
|
+
variant.addPartialStream(ContentType.VIDEO, (stream) => {
|
|
182
|
+
stream.bandwidth = 50;
|
|
183
|
+
stream.frameRate = 1000000 / 42000;
|
|
184
|
+
stream.size(576, 432);
|
|
185
|
+
stream.mime('video/mp4', 'avc1.4d401f');
|
|
186
|
+
});
|
|
187
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
188
|
+
stream.bandwidth = 100;
|
|
189
|
+
stream.primary = true;
|
|
190
|
+
stream.roles = ['main'];
|
|
191
|
+
stream.mime('audio/mp4', 'mp4a.40.29');
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
manifest.addPartialTextStream((stream) => {
|
|
195
|
+
stream.language = 'es';
|
|
196
|
+
stream.label = 'spanish';
|
|
197
|
+
stream.primary = true;
|
|
198
|
+
stream.mimeType = 'text/vtt';
|
|
199
|
+
stream.bandwidth = 100;
|
|
200
|
+
stream.kind = 'caption';
|
|
201
|
+
stream.roles = ['caption', 'main'];
|
|
202
|
+
});
|
|
203
|
+
}));
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe('parses and inherits attributes without sequenceMode', () => {
|
|
207
|
+
beforeEach(() => {
|
|
208
|
+
const config = shaka.util.PlayerConfiguration.createDefault().manifest;
|
|
209
|
+
config.dash.sequenceMode = false;
|
|
210
|
+
parser.configure(config);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
|
|
123
214
|
makeTestsForEach(
|
|
124
215
|
[
|
|
125
216
|
'<MPD minBufferTime="PT75S">',
|
|
@@ -147,6 +238,7 @@ describe('DashParser Manifest', () => {
|
|
|
147
238
|
'</MPD>',
|
|
148
239
|
],
|
|
149
240
|
shaka.test.ManifestGenerator.generate((manifest) => {
|
|
241
|
+
manifest.sequenceMode = false;
|
|
150
242
|
manifest.anyTimeline();
|
|
151
243
|
manifest.minBufferTime = 75;
|
|
152
244
|
manifest.addPartialVariant((variant) => {
|
|
@@ -349,6 +349,7 @@ describe('DashParser SegmentList', () => {
|
|
|
349
349
|
isAutoLowLatencyMode: () => false,
|
|
350
350
|
enableLowLatencyMode: () => {},
|
|
351
351
|
updateDuration: () => {},
|
|
352
|
+
newDrmInfo: (stream) => {},
|
|
352
353
|
};
|
|
353
354
|
const manifest = await dashParser.start('dummy://foo', playerInterface);
|
|
354
355
|
const stream = manifest.variants[0].video;
|
package/test/demo/demo_unit.js
CHANGED
|
@@ -95,7 +95,8 @@ describe('Demo', () => {
|
|
|
95
95
|
.add('playRangeEnd')
|
|
96
96
|
.add('manifest.dash.keySystemsByURI')
|
|
97
97
|
.add('manifest.hls.mediaPlaylistFullMimeType')
|
|
98
|
-
.add('drm.keySystemsMapping')
|
|
98
|
+
.add('drm.keySystemsMapping')
|
|
99
|
+
.add('streaming.parsePrftBox');
|
|
99
100
|
|
|
100
101
|
/**
|
|
101
102
|
* @param {!Object} section
|