shaka-player 3.2.2 → 3.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
- package/.github/ISSUE_TEMPLATE/feature_request.md +1 -1
- package/.github/ISSUE_TEMPLATE/question.md +1 -1
- package/.github/workflows/build_and_test.yaml +73 -0
- package/.github/workflows/release.yaml +53 -0
- package/AUTHORS +6 -5
- package/CHANGELOG.md +314 -1
- package/CONTRIBUTORS +4 -2
- package/build/checkversion.py +1 -1
- package/build/conformance.textproto +183 -169
- package/build/test.py +6 -0
- package/build/types/core +3 -0
- package/build/types/offline +1 -0
- package/build/types/polyfill +1 -0
- package/build/types/ui +2 -0
- package/demo/common/assets.js +18 -0
- package/demo/common/message_ids.js +13 -1
- package/demo/config.js +31 -1
- package/demo/index.html +1 -1
- package/demo/locales/en.json +12 -0
- package/demo/locales/source.json +48 -0
- package/dist/controls.css +26 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +19 -16
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +19 -16
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +21 -14
- package/dist/locales.js +31 -29
- package/dist/receiver.compiled.debug.js +11 -9
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +11 -9
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +232 -18
- package/dist/shaka-player.compiled.debug.d.ts +232 -18
- package/dist/shaka-player.compiled.debug.externs.js +293 -17
- package/dist/shaka-player.compiled.debug.js +481 -422
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +293 -17
- package/dist/shaka-player.compiled.js +846 -817
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +250 -30
- package/dist/shaka-player.ui.debug.d.ts +250 -30
- package/dist/shaka-player.ui.debug.externs.js +322 -28
- package/dist/shaka-player.ui.debug.js +630 -553
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +322 -28
- package/dist/shaka-player.ui.js +1048 -1006
- package/dist/shaka-player.ui.map +4 -4
- package/docs/design/bg-fetch-after.gv +29 -0
- package/docs/design/bg-fetch-after.gv.png +0 -0
- package/docs/design/bg-fetch-before.gv +18 -0
- package/docs/design/bg-fetch-before.gv.png +0 -0
- package/docs/design/bg-fetch.md +134 -0
- package/docs/tutorials/blob-url.md +15 -0
- package/docs/tutorials/index.json +1 -0
- package/docs/tutorials/ui-customization.md +66 -0
- package/docs/tutorials/ui.md +5 -5
- package/externs/cmcd.js +186 -0
- package/externs/ima.js +3 -0
- package/externs/shaka/ads.js +5 -0
- package/externs/shaka/manifest.js +12 -1
- package/externs/shaka/manifest_parser.js +10 -1
- package/externs/shaka/net.js +20 -4
- package/externs/shaka/offline.js +31 -2
- package/externs/shaka/player.js +138 -5
- package/externs/shaka/text.js +24 -0
- package/externs/webcrypto.js +18 -0
- package/karma.conf.js +19 -15
- package/lib/abr/ewma.js +14 -0
- package/lib/abr/ewma_bandwidth_estimator.js +16 -2
- package/lib/abr/simple_abr_manager.js +6 -0
- package/lib/ads/ad_manager.js +1 -0
- package/lib/ads/client_side_ad.js +8 -0
- package/lib/ads/client_side_ad_manager.js +8 -4
- package/lib/ads/server_side_ad.js +7 -0
- package/lib/cast/cast_proxy.js +3 -0
- package/lib/cast/cast_receiver.js +3 -0
- package/lib/cast/cast_utils.js +1 -0
- package/lib/dash/dash_parser.js +79 -33
- package/lib/dash/segment_base.js +25 -1
- package/lib/dash/segment_list.js +18 -9
- package/lib/dash/segment_template.js +23 -10
- package/lib/hls/hls_parser.js +116 -23
- package/lib/hls/manifest_text_parser.js +1 -0
- package/lib/media/quality_observer.js +274 -0
- package/lib/media/region_observer.js +39 -33
- package/lib/media/region_timeline.js +17 -19
- package/lib/media/segment_index.js +8 -1
- package/lib/media/segment_reference.js +37 -4
- package/lib/media/streaming_engine.js +47 -6
- package/lib/net/http_fetch_plugin.js +31 -11
- package/lib/net/http_plugin_utils.js +1 -0
- package/lib/net/http_xhr_plugin.js +40 -14
- package/lib/net/networking_engine.js +85 -18
- package/lib/offline/download_info.js +68 -0
- package/lib/offline/download_manager.js +13 -5
- package/lib/offline/indexeddb/base_storage_cell.js +35 -0
- package/lib/offline/indexeddb/storage_mechanism.js +1 -1
- package/lib/offline/indexeddb/v5_storage_cell.js +5 -0
- package/lib/offline/storage.js +401 -293
- package/lib/offline/stored_content_utils.js +2 -0
- package/lib/player.js +254 -27
- package/lib/polyfill/media_capabilities.js +30 -15
- package/lib/polyfill/random_uuid.js +52 -0
- package/lib/text/cue.js +18 -0
- package/lib/text/ttml_text_parser.js +109 -60
- package/lib/text/ui_text_displayer.js +19 -13
- package/lib/util/cmcd_manager.js +672 -0
- package/lib/util/error.js +6 -0
- package/lib/util/fake_event_target.js +21 -1
- package/lib/util/language_utils.js +12 -0
- package/lib/util/mutex.js +52 -0
- package/lib/util/periods.js +98 -64
- package/lib/util/player_configuration.js +18 -0
- package/lib/util/stream_utils.js +86 -10
- package/lib/util/xml_utils.js +19 -0
- package/package.json +3 -6
- package/shaka-player.uncompiled.js +3 -0
- package/test/dash/dash_parser_content_protection_unit.js +2 -0
- package/test/dash/dash_parser_live_unit.js +144 -1
- package/test/dash/dash_parser_manifest_unit.js +37 -2
- package/test/dash/dash_parser_segment_base_unit.js +2 -0
- package/test/dash/dash_parser_segment_list_unit.js +2 -1
- package/test/dash/dash_parser_segment_template_unit.js +2 -1
- package/test/hls/hls_live_unit.js +2 -0
- package/test/hls/hls_parser_unit.js +76 -0
- package/test/media/drm_engine_unit.js +3 -3
- package/test/media/quality_observer_unit.js +142 -0
- package/test/media/region_observer_unit.js +40 -5
- package/test/media/region_timeline_unit.js +40 -22
- package/test/media/segment_reference_unit.js +16 -1
- package/test/media/streaming_engine_integration.js +4 -2
- package/test/media/streaming_engine_unit.js +147 -0
- package/test/net/http_plugin_unit.js +20 -15
- package/test/offline/storage_integration.js +108 -4
- package/test/player_integration.js +140 -8
- package/test/player_src_equals_integration.js +41 -8
- package/test/player_unit.js +19 -9
- package/test/test/assets/chapters.vtt +2 -2
- package/test/test/assets/chapters2.vtt +10 -0
- package/test/test/util/dash_parser_util.js +4 -0
- package/test/test/util/fake_ad.js +7 -0
- package/test/test/util/fake_drm_engine.js +9 -2
- package/test/test/util/manifest_generator.js +3 -2
- package/test/test/util/test_scheme.js +14 -1
- package/test/test/util/ttml_utils.js +81 -0
- package/test/test/util/util.js +10 -7
- package/test/text/mp4_ttml_parser_unit.js +11 -18
- package/test/text/ttml_text_parser_unit.js +544 -228
- package/test/ui/text_displayer_layout_unit.js +8 -7
- package/test/ui/ui_unit.js +250 -0
- package/test/util/cmcd_manager_unit.js +291 -0
- package/test/util/stream_utils_unit.js +36 -30
- package/third_party/closure-uri/uri.js +21 -0
- package/ui/airplay_button.js +1 -0
- package/ui/audio_language_selection.js +7 -0
- package/ui/cast_button.js +1 -0
- package/ui/context_menu.js +109 -0
- package/ui/controls.js +44 -7
- package/ui/controls.less +1 -0
- package/ui/enums.js +4 -0
- package/ui/externs/ui.js +28 -1
- package/ui/fast_forward_button.js +14 -5
- package/ui/fullscreen_button.js +1 -0
- package/ui/less/buttons.less +0 -4
- package/ui/less/containers.less +74 -0
- package/ui/less/overflow_menu.less +0 -10
- package/ui/less/tooltip.less +97 -0
- package/ui/locales/en.json +1 -0
- package/ui/locales/source.json +4 -0
- package/ui/localization.js +12 -36
- package/ui/loop_button.js +5 -0
- package/ui/mute_button.js +2 -1
- package/ui/overflow_menu.js +5 -25
- package/ui/pip_button.js +5 -0
- package/ui/play_button.js +2 -2
- package/ui/playback_rate_selection.js +8 -9
- package/ui/resolution_selection.js +7 -0
- package/ui/rewind_button.js +15 -6
- package/ui/seek_bar.js +4 -2
- package/ui/settings_menu.js +36 -4
- package/ui/small_play_button.js +1 -0
- package/ui/statistics_button.js +253 -0
- package/ui/text_selection.js +12 -4
- package/ui/ui.js +30 -0
- package/ui/volume_bar.js +1 -1
- package/externs/media_capabilities.js +0 -16
|
@@ -9,6 +9,7 @@ goog.provide('shaka.util.FakeEventTarget');
|
|
|
9
9
|
goog.require('goog.asserts');
|
|
10
10
|
goog.require('shaka.log');
|
|
11
11
|
goog.require('shaka.util.FakeEvent');
|
|
12
|
+
goog.require('shaka.util.IReleasable');
|
|
12
13
|
goog.require('shaka.util.MultiMap');
|
|
13
14
|
|
|
14
15
|
|
|
@@ -18,13 +19,14 @@ goog.require('shaka.util.MultiMap');
|
|
|
18
19
|
* to non-DOM classes. Only FakeEvents should be dispatched.
|
|
19
20
|
*
|
|
20
21
|
* @implements {EventTarget}
|
|
22
|
+
* @implements {shaka.util.IReleasable}
|
|
21
23
|
* @exportInterface
|
|
22
24
|
*/
|
|
23
25
|
shaka.util.FakeEventTarget = class {
|
|
24
26
|
/** */
|
|
25
27
|
constructor() {
|
|
26
28
|
/**
|
|
27
|
-
* @private {
|
|
29
|
+
* @private {shaka.util.MultiMap.<shaka.util.FakeEventTarget.ListenerType>}
|
|
28
30
|
*/
|
|
29
31
|
this.listeners_ = new shaka.util.MultiMap();
|
|
30
32
|
|
|
@@ -46,6 +48,9 @@ shaka.util.FakeEventTarget = class {
|
|
|
46
48
|
* @exportInterface
|
|
47
49
|
*/
|
|
48
50
|
addEventListener(type, listener, options) {
|
|
51
|
+
if (!this.listeners_) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
49
54
|
this.listeners_.push(type, listener);
|
|
50
55
|
}
|
|
51
56
|
|
|
@@ -73,6 +78,9 @@ shaka.util.FakeEventTarget = class {
|
|
|
73
78
|
* @exportInterface
|
|
74
79
|
*/
|
|
75
80
|
removeEventListener(type, listener, options) {
|
|
81
|
+
if (!this.listeners_) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
76
84
|
this.listeners_.remove(type, listener);
|
|
77
85
|
}
|
|
78
86
|
|
|
@@ -90,6 +98,10 @@ shaka.util.FakeEventTarget = class {
|
|
|
90
98
|
goog.asserts.assert(event instanceof shaka.util.FakeEvent,
|
|
91
99
|
'FakeEventTarget can only dispatch FakeEvents!');
|
|
92
100
|
|
|
101
|
+
if (!this.listeners_) {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
|
|
93
105
|
let listeners = this.listeners_.get(event.type) || [];
|
|
94
106
|
const universalListeners =
|
|
95
107
|
this.listeners_.get(shaka.util.FakeEventTarget.ALL_EVENTS_);
|
|
@@ -129,6 +141,14 @@ shaka.util.FakeEventTarget = class {
|
|
|
129
141
|
|
|
130
142
|
return event.defaultPrevented;
|
|
131
143
|
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @override
|
|
147
|
+
* @exportInterface
|
|
148
|
+
*/
|
|
149
|
+
release() {
|
|
150
|
+
this.listeners_ = null;
|
|
151
|
+
}
|
|
132
152
|
};
|
|
133
153
|
|
|
134
154
|
/**
|
|
@@ -13,6 +13,7 @@ goog.require('shaka.util.ManifestParserUtils');
|
|
|
13
13
|
/**
|
|
14
14
|
* @summary A set of language utility functions.
|
|
15
15
|
* @final
|
|
16
|
+
* @export
|
|
16
17
|
*/
|
|
17
18
|
shaka.util.LanguageUtils = class {
|
|
18
19
|
/**
|
|
@@ -32,6 +33,7 @@ shaka.util.LanguageUtils = class {
|
|
|
32
33
|
* @param {string} locale1
|
|
33
34
|
* @param {string} locale2
|
|
34
35
|
* @return {boolean}
|
|
36
|
+
* @export
|
|
35
37
|
*/
|
|
36
38
|
static areLocaleCompatible(locale1, locale2) {
|
|
37
39
|
const LanguageUtils = shaka.util.LanguageUtils;
|
|
@@ -62,6 +64,7 @@ shaka.util.LanguageUtils = class {
|
|
|
62
64
|
* @param {string} locale1
|
|
63
65
|
* @param {string} locale2
|
|
64
66
|
* @return {boolean}
|
|
67
|
+
* @export
|
|
65
68
|
*/
|
|
66
69
|
static areLanguageCompatible(locale1, locale2) {
|
|
67
70
|
const LanguageUtils = shaka.util.LanguageUtils;
|
|
@@ -102,6 +105,7 @@ shaka.util.LanguageUtils = class {
|
|
|
102
105
|
* @param {string} possibleParent
|
|
103
106
|
* @param {string} possibleChild
|
|
104
107
|
* @return {boolean}
|
|
108
|
+
* @export
|
|
105
109
|
*/
|
|
106
110
|
static isParentOf(possibleParent, possibleChild) {
|
|
107
111
|
const LanguageUtils = shaka.util.LanguageUtils;
|
|
@@ -146,6 +150,7 @@ shaka.util.LanguageUtils = class {
|
|
|
146
150
|
* @param {string} localeA
|
|
147
151
|
* @param {string} localeB
|
|
148
152
|
* @return {boolean}
|
|
153
|
+
* @export
|
|
149
154
|
*/
|
|
150
155
|
static isSiblingOf(localeA, localeB) {
|
|
151
156
|
const LanguageUtils = shaka.util.LanguageUtils;
|
|
@@ -181,6 +186,7 @@ shaka.util.LanguageUtils = class {
|
|
|
181
186
|
*
|
|
182
187
|
* @param {string} locale
|
|
183
188
|
* @return {string}
|
|
189
|
+
* @export
|
|
184
190
|
*/
|
|
185
191
|
static normalize(locale) {
|
|
186
192
|
const LanguageUtils = shaka.util.LanguageUtils;
|
|
@@ -217,6 +223,7 @@ shaka.util.LanguageUtils = class {
|
|
|
217
223
|
* @param {string} a
|
|
218
224
|
* @param {string} b
|
|
219
225
|
* @return {boolean}
|
|
226
|
+
* @export
|
|
220
227
|
*/
|
|
221
228
|
static areSiblings(a, b) {
|
|
222
229
|
const LanguageUtils = shaka.util.LanguageUtils;
|
|
@@ -235,6 +242,7 @@ shaka.util.LanguageUtils = class {
|
|
|
235
242
|
* @param {string} target
|
|
236
243
|
* @param {string} candidate
|
|
237
244
|
* @return {number}
|
|
245
|
+
* @export
|
|
238
246
|
*/
|
|
239
247
|
static relatedness(target, candidate) {
|
|
240
248
|
const LanguageUtils = shaka.util.LanguageUtils;
|
|
@@ -271,6 +279,7 @@ shaka.util.LanguageUtils = class {
|
|
|
271
279
|
*
|
|
272
280
|
* @param {string} lang
|
|
273
281
|
* @return {string}
|
|
282
|
+
* @export
|
|
274
283
|
*/
|
|
275
284
|
static getBase(lang) {
|
|
276
285
|
const LanguageUtils = shaka.util.LanguageUtils;
|
|
@@ -300,6 +309,7 @@ shaka.util.LanguageUtils = class {
|
|
|
300
309
|
*
|
|
301
310
|
* @param {shaka.extern.Stream} stream
|
|
302
311
|
* @return {string}
|
|
312
|
+
* @export
|
|
303
313
|
*/
|
|
304
314
|
static getLocaleForText(stream) {
|
|
305
315
|
const LanguageUtils = shaka.util.LanguageUtils;
|
|
@@ -322,6 +332,7 @@ shaka.util.LanguageUtils = class {
|
|
|
322
332
|
*
|
|
323
333
|
* @param {shaka.extern.Variant} variant
|
|
324
334
|
* @return {string}
|
|
335
|
+
* @export
|
|
325
336
|
*/
|
|
326
337
|
static getLocaleForVariant(variant) {
|
|
327
338
|
const LanguageUtils = shaka.util.LanguageUtils;
|
|
@@ -356,6 +367,7 @@ shaka.util.LanguageUtils = class {
|
|
|
356
367
|
* @param {string} target
|
|
357
368
|
* @param {!Iterable.<string>} searchSpace
|
|
358
369
|
* @return {?string}
|
|
370
|
+
* @export
|
|
359
371
|
*/
|
|
360
372
|
static findClosestLocale(target, searchSpace) {
|
|
361
373
|
const LanguageUtils = shaka.util.LanguageUtils;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
goog.provide('shaka.util.Mutex');
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @summary A simple mutex.
|
|
12
|
+
*/
|
|
13
|
+
shaka.util.Mutex = class {
|
|
14
|
+
/** Creates the mutex. */
|
|
15
|
+
constructor() {
|
|
16
|
+
/** @private {!Array.<function()>} */
|
|
17
|
+
this.waiting_ = [];
|
|
18
|
+
|
|
19
|
+
/** @private {number} */
|
|
20
|
+
this.nextMutexId_ = 0;
|
|
21
|
+
|
|
22
|
+
/** @private {number} */
|
|
23
|
+
this.acquiredMutexId_ = 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** @return {!Promise.<number>} mutexId */
|
|
27
|
+
async acquire() {
|
|
28
|
+
const mutexId = ++this.nextMutexId_;
|
|
29
|
+
if (!this.acquiredMutexId_) {
|
|
30
|
+
this.acquiredMutexId_ = mutexId;
|
|
31
|
+
} else {
|
|
32
|
+
await (new Promise((resolve, reject) => {
|
|
33
|
+
this.waiting_.push(() => {
|
|
34
|
+
this.acquiredMutexId_ = mutexId;
|
|
35
|
+
resolve();
|
|
36
|
+
});
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
return mutexId;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** @param {number} mutexId */
|
|
43
|
+
release(mutexId) {
|
|
44
|
+
if (mutexId == this.acquiredMutexId_) {
|
|
45
|
+
this.acquiredMutexId_ = 0;
|
|
46
|
+
if (this.waiting_.length > 0) {
|
|
47
|
+
const callback = this.waiting_.shift();
|
|
48
|
+
callback();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
package/lib/util/periods.js
CHANGED
|
@@ -547,8 +547,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
547
547
|
for (const stream of unusedStreams) {
|
|
548
548
|
// Create a new output stream which includes this input stream.
|
|
549
549
|
const outputStream =
|
|
550
|
-
|
|
551
|
-
await shaka.util.PeriodCombiner.createNewOutputStream_(
|
|
550
|
+
shaka.util.PeriodCombiner.createNewOutputStream_(
|
|
552
551
|
stream, streamsPerPeriod, clone, concat,
|
|
553
552
|
unusedStreamsPerPeriod);
|
|
554
553
|
if (outputStream) {
|
|
@@ -619,52 +618,69 @@ shaka.util.PeriodCombiner = class {
|
|
|
619
618
|
static async extendExistingOutputStream_(
|
|
620
619
|
outputStream, streamsPerPeriod, firstNewPeriodIndex, concat,
|
|
621
620
|
unusedStreamsPerPeriod) {
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
if (!matches) {
|
|
626
|
-
// We were unable to extend this output stream.
|
|
627
|
-
shaka.log.error('No matches extending output stream!',
|
|
628
|
-
outputStream, streamsPerPeriod);
|
|
629
|
-
return false;
|
|
630
|
-
}
|
|
621
|
+
shaka.util.PeriodCombiner.findMatchesInAllPeriods_(streamsPerPeriod,
|
|
622
|
+
outputStream);
|
|
631
623
|
|
|
632
624
|
// This only exists where T == Stream, and this should only ever be called
|
|
633
625
|
// on Stream types. StreamDB should not have pre-existing output streams.
|
|
634
626
|
goog.asserts.assert(outputStream.createSegmentIndex,
|
|
635
627
|
'outputStream should be a Stream type!');
|
|
636
628
|
|
|
629
|
+
if (!outputStream.matchedStreams) {
|
|
630
|
+
// We were unable to extend this output stream.
|
|
631
|
+
shaka.log.error('No matches extending output stream!',
|
|
632
|
+
outputStream, streamsPerPeriod);
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
637
635
|
// We need to create all the per-period segment indexes and append them to
|
|
638
636
|
// the output's MetaSegmentIndex.
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
goog.asserts.assert(matches, 'Matches should be non-null');
|
|
637
|
+
if (outputStream.segmentIndex) {
|
|
638
|
+
await shaka.util.PeriodCombiner.extendOutputSegmentIndex_(outputStream,
|
|
639
|
+
firstNewPeriodIndex);
|
|
640
|
+
}
|
|
644
641
|
|
|
645
|
-
shaka.util.PeriodCombiner.extendOutputStream_(
|
|
646
|
-
|
|
647
|
-
unusedStreamsPerPeriod);
|
|
642
|
+
shaka.util.PeriodCombiner.extendOutputStream_(outputStream,
|
|
643
|
+
firstNewPeriodIndex, concat, unusedStreamsPerPeriod);
|
|
648
644
|
return true;
|
|
649
645
|
}
|
|
650
646
|
|
|
651
647
|
/**
|
|
652
|
-
* Creates
|
|
653
|
-
* segment index
|
|
648
|
+
* Creates the segment indexes for an array of input streams, and append them
|
|
649
|
+
* to the output stream's segment index.
|
|
654
650
|
*
|
|
655
|
-
* @param {
|
|
656
|
-
* @
|
|
651
|
+
* @param {shaka.extern.Stream} outputStream
|
|
652
|
+
* @param {number} firstNewPeriodIndex An index into streamsPerPeriod which
|
|
653
|
+
* represents the first new period that hasn't been processed yet.
|
|
657
654
|
* @private
|
|
658
655
|
*/
|
|
659
|
-
static
|
|
656
|
+
static async extendOutputSegmentIndex_(outputStream, firstNewPeriodIndex) {
|
|
660
657
|
const operations = [];
|
|
658
|
+
const streams = outputStream.matchedStreams;
|
|
659
|
+
goog.asserts.assert(streams, 'matched streams should be valid');
|
|
660
|
+
|
|
661
661
|
for (const stream of streams) {
|
|
662
662
|
operations.push(stream.createSegmentIndex());
|
|
663
663
|
if (stream.trickModeVideo && !stream.trickModeVideo.segmentIndex) {
|
|
664
664
|
operations.push(stream.trickModeVideo.createSegmentIndex());
|
|
665
665
|
}
|
|
666
666
|
}
|
|
667
|
-
|
|
667
|
+
await Promise.all(operations);
|
|
668
|
+
|
|
669
|
+
// Concatenate the new matches onto the stream, starting at the first new
|
|
670
|
+
// period.
|
|
671
|
+
const Iterables = shaka.util.Iterables;
|
|
672
|
+
// Satisfy the compiler about the type.
|
|
673
|
+
// Also checks if the segmentIndex is still valid after the async
|
|
674
|
+
// operations, to make sure we stop if the active stream has changed.
|
|
675
|
+
if (outputStream.segmentIndex instanceof shaka.media.MetaSegmentIndex) {
|
|
676
|
+
for (const {i, item: match} of Iterables.enumerate(streams)) {
|
|
677
|
+
if (match.segmentIndex && i >= firstNewPeriodIndex) {
|
|
678
|
+
goog.asserts.assert(match.segmentIndex,
|
|
679
|
+
'stream should have a segmentIndex.');
|
|
680
|
+
outputStream.segmentIndex.appendSegmentIndex(match.segmentIndex);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
668
684
|
}
|
|
669
685
|
|
|
670
686
|
/**
|
|
@@ -682,7 +698,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
682
698
|
* @param {!Array.<!Set.<T>>} unusedStreamsPerPeriod An array of sets of
|
|
683
699
|
* unused streams from each period.
|
|
684
700
|
*
|
|
685
|
-
* @return {
|
|
701
|
+
* @return {?T} A newly-created output Stream, or null if matches
|
|
686
702
|
* could not be found.`
|
|
687
703
|
*
|
|
688
704
|
* @template T
|
|
@@ -690,35 +706,36 @@ shaka.util.PeriodCombiner = class {
|
|
|
690
706
|
*
|
|
691
707
|
* @private
|
|
692
708
|
*/
|
|
693
|
-
static
|
|
709
|
+
static createNewOutputStream_(
|
|
694
710
|
stream, streamsPerPeriod, clone, concat, unusedStreamsPerPeriod) {
|
|
695
711
|
// Start by cloning the stream without segments, key IDs, etc.
|
|
696
712
|
const outputStream = clone(stream);
|
|
697
713
|
|
|
698
714
|
// Find best-matching streams in all periods.
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
if (!matches) {
|
|
703
|
-
// This is not a stream we can build output from, but it may become part
|
|
704
|
-
// of another output based on another period's stream.
|
|
705
|
-
return null;
|
|
706
|
-
}
|
|
715
|
+
shaka.util.PeriodCombiner.findMatchesInAllPeriods_(streamsPerPeriod,
|
|
716
|
+
outputStream);
|
|
707
717
|
|
|
708
718
|
// This only exists where T == Stream.
|
|
709
719
|
if (outputStream.createSegmentIndex) {
|
|
720
|
+
// Override the createSegmentIndex function of the outputStream.
|
|
721
|
+
outputStream.createSegmentIndex = async () => {
|
|
722
|
+
if (!outputStream.segmentIndex) {
|
|
723
|
+
outputStream.segmentIndex = new shaka.media.MetaSegmentIndex();
|
|
724
|
+
await shaka.util.PeriodCombiner.extendOutputSegmentIndex_(
|
|
725
|
+
outputStream, /* firstNewPeriodIndex= */ 0);
|
|
726
|
+
}
|
|
727
|
+
};
|
|
710
728
|
// For T == Stream, we need to create all the per-period segment indexes
|
|
711
729
|
// in advance. concat() will add them to the output's MetaSegmentIndex.
|
|
712
|
-
await shaka.util.PeriodCombiner.createSegmentIndexes_(matches);
|
|
713
730
|
}
|
|
714
731
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
unusedStreamsPerPeriod);
|
|
732
|
+
if (!outputStream.matchedStreams) {
|
|
733
|
+
// This is not a stream we can build output from, but it may become part
|
|
734
|
+
// of another output based on another period's stream.
|
|
735
|
+
return null;
|
|
736
|
+
}
|
|
737
|
+
shaka.util.PeriodCombiner.extendOutputStream_(outputStream,
|
|
738
|
+
/* firstNewPeriodIndex= */ 0, concat, unusedStreamsPerPeriod);
|
|
722
739
|
|
|
723
740
|
return outputStream;
|
|
724
741
|
}
|
|
@@ -726,7 +743,6 @@ shaka.util.PeriodCombiner = class {
|
|
|
726
743
|
/**
|
|
727
744
|
* @param {T} outputStream An existing output stream which needs to be
|
|
728
745
|
* extended into new periods.
|
|
729
|
-
* @param {!Array.<T>} matches A list of matching Streams from each period.
|
|
730
746
|
* @param {number} firstNewPeriodIndex An index into streamsPerPeriod which
|
|
731
747
|
* represents the first new period that hasn't been processed yet.
|
|
732
748
|
* @param {function(T, T)} concat Concatenate the second stream onto the end
|
|
@@ -740,10 +756,15 @@ shaka.util.PeriodCombiner = class {
|
|
|
740
756
|
* @private
|
|
741
757
|
*/
|
|
742
758
|
static extendOutputStream_(
|
|
743
|
-
outputStream,
|
|
744
|
-
unusedStreamsPerPeriod) {
|
|
759
|
+
outputStream, firstNewPeriodIndex, concat, unusedStreamsPerPeriod) {
|
|
745
760
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
746
761
|
const LanguageUtils = shaka.util.LanguageUtils;
|
|
762
|
+
const matches = outputStream.matchedStreams;
|
|
763
|
+
|
|
764
|
+
// Assure the compiler that matches didn't become null during the async
|
|
765
|
+
// operation before.
|
|
766
|
+
goog.asserts.assert(outputStream.matchedStreams,
|
|
767
|
+
'matchedStreams should be non-null');
|
|
747
768
|
|
|
748
769
|
// Concatenate the new matches onto the stream, starting at the first new
|
|
749
770
|
// period.
|
|
@@ -787,7 +808,23 @@ shaka.util.PeriodCombiner = class {
|
|
|
787
808
|
// streams that match this output.
|
|
788
809
|
clone.originalId = null;
|
|
789
810
|
clone.createSegmentIndex = () => Promise.resolve();
|
|
790
|
-
clone.
|
|
811
|
+
clone.closeSegmentIndex = () => {
|
|
812
|
+
if (clone.segmentIndex) {
|
|
813
|
+
clone.segmentIndex.release();
|
|
814
|
+
clone.segmentIndex = null;
|
|
815
|
+
}
|
|
816
|
+
// Close the segment index of the matched streams.
|
|
817
|
+
if (clone.matchedStreams) {
|
|
818
|
+
for (const match of clone.matchedStreams) {
|
|
819
|
+
if (match.segmentIndex) {
|
|
820
|
+
match.segmentIndex.release();
|
|
821
|
+
match.segmentIndex = null;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
};
|
|
826
|
+
|
|
827
|
+
clone.segmentIndex = null;
|
|
791
828
|
clone.emsgSchemeIdUris = [];
|
|
792
829
|
clone.keyIds = new Set();
|
|
793
830
|
clone.closedCaptions = null;
|
|
@@ -873,26 +910,24 @@ shaka.util.PeriodCombiner = class {
|
|
|
873
910
|
}
|
|
874
911
|
}
|
|
875
912
|
|
|
876
|
-
// Satisfy the compiler about the type.
|
|
877
|
-
goog.asserts.assert(
|
|
878
|
-
output.segmentIndex instanceof shaka.media.MetaSegmentIndex,
|
|
879
|
-
'Output streams should have a MetaSegmentIndex!');
|
|
880
|
-
// Satisfy the compiler that the input index has been created.
|
|
881
|
-
goog.asserts.assert(
|
|
882
|
-
input.segmentIndex,
|
|
883
|
-
'Input segment index should have been created by now!');
|
|
884
|
-
|
|
885
|
-
output.segmentIndex.appendSegmentIndex(input.segmentIndex);
|
|
886
|
-
|
|
887
913
|
// Combine trick-play video streams, if present.
|
|
888
914
|
if (input.trickModeVideo) {
|
|
889
915
|
if (!output.trickModeVideo) {
|
|
890
916
|
// Create a fresh output stream for trick-mode playback.
|
|
891
917
|
output.trickModeVideo = shaka.util.PeriodCombiner.cloneStream_(
|
|
892
918
|
input.trickModeVideo);
|
|
893
|
-
//
|
|
894
|
-
//
|
|
895
|
-
|
|
919
|
+
// TODO: fix the createSegmentIndex function for trickModeVideo.
|
|
920
|
+
// The trick-mode tracks in multi-period content should have trick-mode
|
|
921
|
+
// segment indexes whenever available, rather than only regular-mode
|
|
922
|
+
// segment indexes.
|
|
923
|
+
output.trickModeVideo.createSegmentIndex = () => {
|
|
924
|
+
// Satisfy the compiler about the type.
|
|
925
|
+
goog.asserts.assert(
|
|
926
|
+
output.segmentIndex instanceof shaka.media.MetaSegmentIndex,
|
|
927
|
+
'The stream should have a MetaSegmentIndex.');
|
|
928
|
+
output.trickModeVideo.segmentIndex = output.segmentIndex.clone();
|
|
929
|
+
return Promise.resolve();
|
|
930
|
+
};
|
|
896
931
|
}
|
|
897
932
|
|
|
898
933
|
// Concatenate the trick mode input onto the trick mode output.
|
|
@@ -943,7 +978,6 @@ shaka.util.PeriodCombiner = class {
|
|
|
943
978
|
*
|
|
944
979
|
* @param {!Array.<!Array.<T>>} streamsPerPeriod
|
|
945
980
|
* @param {T} outputStream
|
|
946
|
-
* @return {Array.<T>}
|
|
947
981
|
*
|
|
948
982
|
* @template T
|
|
949
983
|
* Accepts either a StreamDB or Stream type.
|
|
@@ -956,11 +990,11 @@ shaka.util.PeriodCombiner = class {
|
|
|
956
990
|
const match = shaka.util.PeriodCombiner.findBestMatchInPeriod_(
|
|
957
991
|
streams, outputStream);
|
|
958
992
|
if (!match) {
|
|
959
|
-
return
|
|
993
|
+
return;
|
|
960
994
|
}
|
|
961
995
|
matches.push(match);
|
|
962
996
|
}
|
|
963
|
-
|
|
997
|
+
outputStream.matchedStreams = matches;
|
|
964
998
|
}
|
|
965
999
|
|
|
966
1000
|
/**
|
|
@@ -120,6 +120,8 @@ shaka.util.PlayerConfiguration = class {
|
|
|
120
120
|
ignoreTextStreamFailures: false,
|
|
121
121
|
ignoreImageStreamFailures: false,
|
|
122
122
|
useFullSegmentsForStartTime: false,
|
|
123
|
+
defaultAudioCodec: 'mp4a.40.2',
|
|
124
|
+
defaultVideoCodec: 'avc1.42E01E',
|
|
123
125
|
},
|
|
124
126
|
};
|
|
125
127
|
|
|
@@ -163,6 +165,8 @@ shaka.util.PlayerConfiguration = class {
|
|
|
163
165
|
forceHTTPS: false,
|
|
164
166
|
preferNativeHls: false,
|
|
165
167
|
updateIntervalSeconds: 1,
|
|
168
|
+
dispatchAllEmsgBoxes: false,
|
|
169
|
+
observeQualityChanges: false,
|
|
166
170
|
};
|
|
167
171
|
|
|
168
172
|
// Some browsers will stop earlier than others before a gap (e.g., Edge
|
|
@@ -235,6 +239,19 @@ shaka.util.PlayerConfiguration = class {
|
|
|
235
239
|
minBandwidth: 0,
|
|
236
240
|
maxBandwidth: Infinity,
|
|
237
241
|
},
|
|
242
|
+
advanced: {
|
|
243
|
+
minTotalBytes: 128e3,
|
|
244
|
+
minBytes: 16e3,
|
|
245
|
+
fastHalfLife: 2,
|
|
246
|
+
slowHalfLife: 5,
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const cmcd = {
|
|
251
|
+
enabled: false,
|
|
252
|
+
sessionId: '',
|
|
253
|
+
contentId: '',
|
|
254
|
+
useHeaders: false,
|
|
238
255
|
};
|
|
239
256
|
|
|
240
257
|
/** @type {shaka.extern.PlayerConfiguration} */
|
|
@@ -269,6 +286,7 @@ shaka.util.PlayerConfiguration = class {
|
|
|
269
286
|
playRangeStart: 0,
|
|
270
287
|
playRangeEnd: Infinity,
|
|
271
288
|
textDisplayFactory: () => null,
|
|
289
|
+
cmcd: cmcd,
|
|
272
290
|
};
|
|
273
291
|
|
|
274
292
|
// Add this callback so that we can reference the preferred audio language
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -419,13 +419,48 @@ shaka.util.StreamUtils = class {
|
|
|
419
419
|
*/
|
|
420
420
|
static async filterManifestByMediaCapabilities(
|
|
421
421
|
manifest, usePersistentLicenses) {
|
|
422
|
-
goog.asserts.assert(
|
|
422
|
+
goog.asserts.assert(navigator.mediaCapabilities,
|
|
423
423
|
'MediaCapabilities should be valid.');
|
|
424
424
|
|
|
425
425
|
await shaka.util.StreamUtils.getDecodingInfosForVariants(
|
|
426
426
|
manifest.variants, usePersistentLicenses, /* srcEquals= */ false);
|
|
427
427
|
manifest.variants = manifest.variants.filter((variant) => {
|
|
428
|
+
// See: https://github.com/google/shaka-player/issues/3860
|
|
428
429
|
const video = variant.video;
|
|
430
|
+
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
431
|
+
if (video) {
|
|
432
|
+
let videoCodecs = shaka.util.StreamUtils.patchVp9(video.codecs);
|
|
433
|
+
// For multiplexed streams. Here we must check the audio of the
|
|
434
|
+
// stream to see if it is compatible.
|
|
435
|
+
if (video.codecs.includes(',')) {
|
|
436
|
+
const allCodecs = video.codecs.split(',');
|
|
437
|
+
videoCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
438
|
+
ContentType.VIDEO, allCodecs);
|
|
439
|
+
const audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
440
|
+
ContentType.AUDIO, allCodecs);
|
|
441
|
+
const audioFullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
442
|
+
video.mimeType, audioCodecs, ContentType.AUDIO);
|
|
443
|
+
if (!MediaSource.isTypeSupported(audioFullType)) {
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
448
|
+
video.mimeType, videoCodecs, ContentType.VIDEO);
|
|
449
|
+
if (!MediaSource.isTypeSupported(fullType)) {
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
const audio = variant.audio;
|
|
454
|
+
if (audio) {
|
|
455
|
+
const codecs =
|
|
456
|
+
shaka.util.StreamUtils.getCorrectAudioCodecs_(audio.codecs);
|
|
457
|
+
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
458
|
+
audio.mimeType, codecs, ContentType.AUDIO);
|
|
459
|
+
if (!MediaSource.isTypeSupported(fullType)) {
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
429
464
|
// See: https://github.com/google/shaka-player/issues/3380
|
|
430
465
|
if (shaka.util.Platform.isXboxOne() && video &&
|
|
431
466
|
((video.width && video.width > 1920) ||
|
|
@@ -468,7 +503,7 @@ shaka.util.StreamUtils = class {
|
|
|
468
503
|
return;
|
|
469
504
|
}
|
|
470
505
|
|
|
471
|
-
const mediaCapabilities =
|
|
506
|
+
const mediaCapabilities = navigator.mediaCapabilities;
|
|
472
507
|
|
|
473
508
|
const operations = [];
|
|
474
509
|
const getVariantDecodingInfos = (async (variant, decodingConfig) => {
|
|
@@ -562,12 +597,8 @@ shaka.util.StreamUtils = class {
|
|
|
562
597
|
}
|
|
563
598
|
}
|
|
564
599
|
if (audio) {
|
|
565
|
-
// Some Tizen devices seem to misreport AC-3 support, but correctly
|
|
566
|
-
// report EC-3 support. So query EC-3 as a fallback for AC-3.
|
|
567
|
-
// See https://github.com/google/shaka-player/issues/2989 for details.
|
|
568
600
|
const codecs =
|
|
569
|
-
(audio.codecs
|
|
570
|
-
shaka.util.Platform.isTizen()) ? 'ec-3' : audio.codecs;
|
|
601
|
+
shaka.util.StreamUtils.getCorrectAudioCodecs_(audio.codecs);
|
|
571
602
|
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
572
603
|
audio.mimeType, codecs, ContentType.AUDIO);
|
|
573
604
|
|
|
@@ -678,6 +709,26 @@ shaka.util.StreamUtils = class {
|
|
|
678
709
|
return configs;
|
|
679
710
|
}
|
|
680
711
|
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Generates the correct audio codec for MediaDecodingConfiguration and
|
|
715
|
+
* for MediaSource.isTypeSupported.
|
|
716
|
+
* @param {string} codecs
|
|
717
|
+
* @return {string}
|
|
718
|
+
* @private
|
|
719
|
+
*/
|
|
720
|
+
static getCorrectAudioCodecs_(codecs) {
|
|
721
|
+
// Some Tizen devices seem to misreport AC-3 support, but correctly
|
|
722
|
+
// report EC-3 support. So query EC-3 as a fallback for AC-3.
|
|
723
|
+
// See https://github.com/google/shaka-player/issues/2989 for details.
|
|
724
|
+
if (shaka.util.Platform.isTizen()) {
|
|
725
|
+
return codecs.toLowerCase() == 'ac-3' ? 'ec-3' : codecs;
|
|
726
|
+
} else {
|
|
727
|
+
return codecs;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
|
|
681
732
|
/**
|
|
682
733
|
* MediaCapabilities supports 'vp09...' codecs, but not 'vp9'. Translate vp9
|
|
683
734
|
* codec strings into 'vp09...', to allow such content to play with
|
|
@@ -989,6 +1040,31 @@ shaka.util.StreamUtils = class {
|
|
|
989
1040
|
static imageStreamToTrack(stream) {
|
|
990
1041
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
991
1042
|
|
|
1043
|
+
let width = stream.width || null;
|
|
1044
|
+
let height = stream.height || null;
|
|
1045
|
+
|
|
1046
|
+
// The stream width and height represent the size of the entire thumbnail
|
|
1047
|
+
// sheet, so divide by the layout.
|
|
1048
|
+
let reference = null;
|
|
1049
|
+
// Note: segmentIndex is built by default for HLS, but not for DASH, but
|
|
1050
|
+
// in DASH this information comes at the stream level and not at the
|
|
1051
|
+
// segment level.
|
|
1052
|
+
if (stream.segmentIndex) {
|
|
1053
|
+
reference = stream.segmentIndex.get(0);
|
|
1054
|
+
}
|
|
1055
|
+
let layout = stream.tilesLayout;
|
|
1056
|
+
if (reference) {
|
|
1057
|
+
layout = reference.getTilesLayout() || layout;
|
|
1058
|
+
}
|
|
1059
|
+
if (layout && width != null) {
|
|
1060
|
+
width /= Number(layout.split('x')[0]);
|
|
1061
|
+
}
|
|
1062
|
+
if (layout && height != null) {
|
|
1063
|
+
height /= Number(layout.split('x')[1]);
|
|
1064
|
+
}
|
|
1065
|
+
// TODO: What happens if there are multiple grids, with different
|
|
1066
|
+
// layout sizes, inside this image stream?
|
|
1067
|
+
|
|
992
1068
|
/** @type {shaka.extern.Track} */
|
|
993
1069
|
const track = {
|
|
994
1070
|
id: stream.id,
|
|
@@ -998,8 +1074,8 @@ shaka.util.StreamUtils = class {
|
|
|
998
1074
|
language: '',
|
|
999
1075
|
label: null,
|
|
1000
1076
|
kind: null,
|
|
1001
|
-
width
|
|
1002
|
-
height
|
|
1077
|
+
width,
|
|
1078
|
+
height,
|
|
1003
1079
|
frameRate: null,
|
|
1004
1080
|
pixelAspectRatio: null,
|
|
1005
1081
|
hdr: null,
|
|
@@ -1016,7 +1092,7 @@ shaka.util.StreamUtils = class {
|
|
|
1016
1092
|
channelsCount: null,
|
|
1017
1093
|
audioSamplingRate: null,
|
|
1018
1094
|
spatialAudio: false,
|
|
1019
|
-
tilesLayout:
|
|
1095
|
+
tilesLayout: layout || null,
|
|
1020
1096
|
audioBandwidth: null,
|
|
1021
1097
|
videoBandwidth: null,
|
|
1022
1098
|
originalVideoId: null,
|