shaka-player 4.5.0 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
- package/AUTHORS +2 -0
- package/CHANGELOG.md +98 -0
- package/CODE_OF_CONDUCT.md +1 -1
- package/CONTRIBUTORS +2 -0
- package/README.md +38 -32
- package/demo/common/asset.js +11 -17
- package/demo/common/assets.js +117 -11
- package/demo/config.js +28 -1
- package/demo/custom.js +52 -38
- package/demo/main.js +11 -3
- package/demo/search.js +7 -0
- package/dist/controls.css +1 -1
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +133 -124
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +133 -124
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +20 -18
- package/dist/locales.js +35 -31
- package/dist/receiver.compiled.debug.js +58 -50
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +58 -50
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +440 -390
- package/dist/shaka-player.compiled.debug.d.ts +440 -390
- package/dist/shaka-player.compiled.debug.externs.js +162 -77
- package/dist/shaka-player.compiled.debug.js +786 -736
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +162 -77
- package/dist/shaka-player.compiled.js +1154 -1121
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +491 -437
- package/dist/shaka-player.ui.debug.d.ts +491 -437
- package/dist/shaka-player.ui.debug.externs.js +167 -77
- package/dist/shaka-player.ui.debug.js +877 -811
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +167 -77
- package/dist/shaka-player.ui.js +1362 -1318
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/ad_monetization.md +54 -0
- package/docs/tutorials/basic-usage.md +2 -1
- package/docs/tutorials/errors.md +2 -1
- package/docs/tutorials/offline.md +10 -8
- package/docs/tutorials/plugins.md +5 -0
- package/docs/tutorials/ui-customization.md +13 -0
- package/docs/tutorials/ui.md +2 -1
- package/docs/tutorials/upgrade.md +3 -0
- package/externs/managedmediasource.js +24 -0
- package/externs/shaka/abr_manager.js +6 -2
- package/externs/shaka/cea.js +7 -0
- package/externs/shaka/codecs.js +5 -3
- package/externs/shaka/manifest.js +21 -5
- package/externs/shaka/manifest_parser.js +17 -0
- package/externs/shaka/net.js +2 -2
- package/externs/shaka/offline.js +9 -2
- package/externs/shaka/player.js +53 -7
- package/externs/shaka/text.js +3 -1
- package/externs/shaka/transmuxer.js +1 -1
- package/lib/abr/simple_abr_manager.js +25 -8
- package/lib/ads/media_tailor_ad_manager.js +14 -3
- package/lib/cast/cast_receiver.js +1 -1
- package/lib/cea/cea_decoder.js +28 -0
- package/lib/cea/dummy_caption_decoder.js +5 -0
- package/lib/dash/dash_parser.js +249 -83
- package/lib/dash/mpd_utils.js +52 -31
- package/lib/dash/segment_base.js +4 -5
- package/lib/dash/segment_list.js +5 -4
- package/lib/dash/segment_template.js +83 -26
- package/lib/hls/hls_classes.js +28 -43
- package/lib/hls/hls_parser.js +761 -649
- package/lib/hls/hls_utils.js +64 -5
- package/lib/hls/manifest_text_parser.js +7 -11
- package/lib/media/adaptation_set.js +20 -13
- package/lib/media/adaptation_set_criteria.js +70 -18
- package/lib/media/closed_caption_parser.js +13 -0
- package/lib/media/content_workarounds.js +6 -0
- package/lib/media/drm_engine.js +8 -13
- package/lib/media/manifest_parser.js +8 -113
- package/lib/media/media_source_capabilities.js +10 -3
- package/lib/media/media_source_engine.js +75 -21
- package/lib/media/play_rate_controller.js +8 -38
- package/lib/media/presentation_timeline.js +7 -1
- package/lib/media/quality_observer.js +1 -1
- package/lib/media/segment_index.js +5 -5
- package/lib/media/segment_prefetch.js +92 -24
- package/lib/media/segment_reference.js +67 -1
- package/lib/media/segment_utils.js +343 -0
- package/lib/media/stall_detector.js +0 -3
- package/lib/media/streaming_engine.js +41 -21
- package/lib/mss/mss_parser.js +18 -2
- package/lib/net/networking_engine.js +66 -12
- package/lib/net/networking_utils.js +95 -0
- package/lib/offline/indexeddb/v1_storage_cell.js +2 -0
- package/lib/offline/indexeddb/v2_storage_cell.js +2 -0
- package/lib/offline/manifest_converter.js +2 -0
- package/lib/offline/offline_manifest_parser.js +10 -0
- package/lib/offline/storage.js +11 -7
- package/lib/player.js +993 -1502
- package/lib/polyfill/mediasource.js +22 -1
- package/lib/text/mp4_ttml_parser.js +2 -2
- package/lib/text/srt_text_parser.js +2 -2
- package/lib/text/stub_text_displayer.js +51 -0
- package/lib/text/text_engine.js +3 -2
- package/lib/text/ttml_text_parser.js +28 -5
- package/lib/text/ui_text_displayer.js +4 -0
- package/lib/text/vtt_text_parser.js +7 -2
- package/lib/transmuxer/aac_transmuxer.js +1 -1
- package/lib/transmuxer/ac3_transmuxer.js +1 -1
- package/lib/transmuxer/ec3_transmuxer.js +1 -1
- package/lib/transmuxer/h264.js +5 -4
- package/lib/transmuxer/h265.js +5 -4
- package/lib/transmuxer/mp3_transmuxer.js +1 -1
- package/lib/transmuxer/mpeg_ts_transmuxer.js +1 -1
- package/lib/transmuxer/mss_transmuxer.js +1 -1
- package/lib/transmuxer/ts_transmuxer.js +33 -13
- package/lib/util/buffer_utils.js +26 -4
- package/lib/util/content_steering_manager.js +358 -0
- package/lib/util/error.js +2 -6
- package/lib/util/fairplay_utils.js +1 -1
- package/lib/util/fake_event.js +5 -1
- package/lib/util/id3_utils.js +50 -1
- package/lib/util/manifest_parser_utils.js +12 -8
- package/lib/util/mp4_generator.js +2 -4
- package/lib/util/mutex.js +59 -0
- package/lib/util/object_utils.js +1 -1
- package/lib/util/periods.js +154 -179
- package/lib/util/platform.js +4 -3
- package/lib/util/player_configuration.js +35 -6
- package/lib/util/stream_utils.js +105 -98
- package/lib/util/ts_parser.js +124 -73
- package/lib/util/uint8array_utils.js +5 -1
- package/lib/util/xml_utils.js +5 -0
- package/maintained-branches.md +2 -2
- package/package.json +1 -1
- package/roadmap.md +15 -5
- package/shaka-player.uncompiled.js +0 -1
- package/test/abr/simple_abr_manager_unit.js +33 -33
- package/test/ads/ad_manager_unit.js +3 -2
- package/test/cast/cast_receiver_integration.js +3 -4
- package/test/cast/cast_utils_unit.js +2 -0
- package/test/cea/cea_decoder_unit.js +7 -0
- package/test/codec_switching/codec_switching_integration.js +2 -1
- package/test/dash/dash_parser_integration.js +2 -1
- package/test/dash/dash_parser_live_unit.js +0 -19
- package/test/dash/dash_parser_manifest_unit.js +494 -2
- package/test/dash/mpd_utils_unit.js +61 -30
- package/test/hls/hls_live_unit.js +12 -3
- package/test/hls/hls_parser_unit.js +377 -27
- package/test/hls/manifest_text_parser_unit.js +32 -99
- package/test/media/adaptation_set_criteria_unit.js +172 -21
- package/test/media/adaptation_set_unit.js +1 -0
- package/test/media/content_workarounds_unit.js +1 -1
- package/test/media/drm_engine_unit.js +3 -3
- package/test/media/media_source_engine_unit.js +1 -1
- package/test/media/play_rate_controller_unit.js +0 -48
- package/test/media/segment_index_unit.js +2 -1
- package/test/mss/mss_player_integration.js +3 -2
- package/test/net/networking_engine_unit.js +3 -3
- package/test/{media/manifest_parser_unit.js → net/networking_utils_unit.js} +2 -2
- package/test/offline/manifest_convert_unit.js +9 -0
- package/test/offline/offline_integration.js +2 -1
- package/test/offline/storage_integration.js +21 -9
- package/test/player_external.js +4 -2
- package/test/player_integration.js +15 -4
- package/test/player_load_graph_integration.js +47 -151
- package/test/player_src_equals_external.js +1 -0
- package/test/player_src_equals_integration.js +71 -4
- package/test/player_unit.js +80 -27
- package/test/test/util/fake_media_source_engine.js +5 -0
- package/test/test/util/fake_networking_engine.js +9 -1
- package/test/test/util/fake_segment_prefetch.js +3 -0
- package/test/test/util/manifest_generator.js +4 -0
- package/test/test/util/manifest_parser_util.js +14 -2
- package/test/test/util/offline_utils.js +1 -0
- package/test/test/util/simple_fakes.js +2 -2
- package/test/test/util/streaming_engine_util.js +3 -0
- package/test/test/util/test_scheme.js +6 -0
- package/test/test/util/ui_utils.js +4 -3
- package/test/text/mp4_ttml_parser_unit.js +4 -4
- package/test/text/srt_text_parser_unit.js +1 -1
- package/test/text/text_displayer_layout_unit.js +1 -1
- package/test/text/text_engine_unit.js +6 -0
- package/test/text/ttml_text_parser_unit.js +33 -2
- package/test/transmuxer/transmuxer_engine_integration.js +9 -0
- package/test/transmuxer/transmuxer_integration.js +33 -3
- package/test/ui/ad_ui_unit.js +2 -2
- package/test/ui/ui_customization_unit.js +17 -16
- package/test/ui/ui_integration.js +30 -20
- package/test/ui/ui_unit.js +25 -19
- package/test/util/buffer_utils_unit.js +1 -1
- package/test/util/content_steering_manager_unit.js +427 -0
- package/test/util/id3_utils_unit.js +52 -0
- package/test/util/stream_utils_unit.js +17 -113
- package/test/util/ts_parser_unit.js +3 -3
- package/third_party/closure-uri/uri.js +13 -1
- package/ui/audio_language_selection.js +9 -2
- package/ui/externs/ui.js +17 -2
- package/ui/language_utils.js +43 -8
- package/ui/less/range_elements.less +33 -0
- package/ui/locales/en.json +1 -0
- package/ui/locales/es-419.json +1 -0
- package/ui/locales/es.json +1 -0
- package/ui/locales/source.json +4 -0
- package/ui/localization.js +10 -0
- package/ui/remote_button.js +3 -1
- package/ui/resolution_selection.js +47 -10
- package/ui/seek_bar.js +296 -3
- package/ui/text_selection.js +2 -1
- package/ui/ui.js +6 -2
- package/lib/routing/node.js +0 -23
- package/lib/routing/payload.js +0 -47
- package/lib/routing/walker.js +0 -587
- package/test/routing/walker_unit.js +0 -505
|
@@ -4,12 +4,15 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
goog.provide('shaka.media.SegmentPrefetch');
|
|
8
|
+
|
|
7
9
|
goog.require('goog.asserts');
|
|
8
|
-
goog.require('shaka.
|
|
10
|
+
goog.require('shaka.log');
|
|
9
11
|
goog.require('shaka.media.InitSegmentReference');
|
|
10
12
|
goog.require('shaka.media.SegmentReference');
|
|
11
|
-
goog.
|
|
12
|
-
goog.require('shaka.
|
|
13
|
+
goog.require('shaka.net.NetworkingEngine');
|
|
14
|
+
goog.require('shaka.util.Uint8ArrayUtils');
|
|
15
|
+
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* @summary
|
|
@@ -37,8 +40,9 @@ shaka.media.SegmentPrefetch = class {
|
|
|
37
40
|
this.fetchDispatcher_ = fetchDispatcher;
|
|
38
41
|
|
|
39
42
|
/**
|
|
40
|
-
* @private {!Map.<
|
|
41
|
-
*
|
|
43
|
+
* @private {!Map.<
|
|
44
|
+
* !(shaka.media.SegmentReference|shaka.media.InitSegmentReference),
|
|
45
|
+
* !shaka.media.SegmentPrefetchOperation>}
|
|
42
46
|
*/
|
|
43
47
|
this.segmentPrefetchMap_ = new Map();
|
|
44
48
|
}
|
|
@@ -47,9 +51,10 @@ shaka.media.SegmentPrefetch = class {
|
|
|
47
51
|
* Fetch next segments ahead of current segment.
|
|
48
52
|
*
|
|
49
53
|
* @param {(!shaka.media.SegmentReference)} startReference
|
|
54
|
+
* @param {boolean=} skipFirst
|
|
50
55
|
* @public
|
|
51
56
|
*/
|
|
52
|
-
prefetchSegments(startReference) {
|
|
57
|
+
prefetchSegments(startReference, skipFirst = false) {
|
|
53
58
|
goog.asserts.assert(this.prefetchLimit_ > 0,
|
|
54
59
|
'SegmentPrefetch can not be used when prefetchLimit <= 0.');
|
|
55
60
|
|
|
@@ -65,6 +70,14 @@ shaka.media.SegmentPrefetch = class {
|
|
|
65
70
|
return;
|
|
66
71
|
}
|
|
67
72
|
let reference = startReference;
|
|
73
|
+
if (skipFirst) {
|
|
74
|
+
reference = iterator.next().value;
|
|
75
|
+
if (reference &&
|
|
76
|
+
reference.startTime == startReference.startTime &&
|
|
77
|
+
reference.endTime == startReference.endTime) {
|
|
78
|
+
reference = null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
68
81
|
while (this.segmentPrefetchMap_.size < this.prefetchLimit_ &&
|
|
69
82
|
reference != null) {
|
|
70
83
|
// By default doesn't prefech preload partial segments when using
|
|
@@ -88,9 +101,38 @@ shaka.media.SegmentPrefetch = class {
|
|
|
88
101
|
}
|
|
89
102
|
}
|
|
90
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Fetch init segment.
|
|
106
|
+
*
|
|
107
|
+
* @param {!shaka.media.InitSegmentReference} initSegmentReference
|
|
108
|
+
* @public
|
|
109
|
+
*/
|
|
110
|
+
prefetchInitSegment(initSegmentReference) {
|
|
111
|
+
goog.asserts.assert(this.prefetchLimit_ > 0,
|
|
112
|
+
'SegmentPrefetch can not be used when prefetchLimit <= 0.');
|
|
113
|
+
|
|
114
|
+
const logPrefix = shaka.media.SegmentPrefetch.logPrefix_(this.stream_);
|
|
115
|
+
if (!this.stream_.segmentIndex) {
|
|
116
|
+
shaka.log.info(logPrefix, 'missing segmentIndex');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (this.segmentPrefetchMap_.size < this.prefetchLimit_) {
|
|
121
|
+
if (!this.segmentPrefetchMap_.has(initSegmentReference)) {
|
|
122
|
+
const segmentPrefetchOperation =
|
|
123
|
+
new shaka.media.SegmentPrefetchOperation(this.fetchDispatcher_);
|
|
124
|
+
segmentPrefetchOperation.dispatchFetch(
|
|
125
|
+
initSegmentReference, this.stream_);
|
|
126
|
+
this.segmentPrefetchMap_.set(
|
|
127
|
+
initSegmentReference, segmentPrefetchOperation);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
91
132
|
/**
|
|
92
133
|
* Get the result of prefetched segment if already exists.
|
|
93
|
-
* @param {(
|
|
134
|
+
* @param {!(shaka.media.SegmentReference|shaka.media.InitSegmentReference)}
|
|
135
|
+
* reference
|
|
94
136
|
* @param {?function(BufferSource):!Promise=} streamDataCallback
|
|
95
137
|
* @return {?shaka.net.NetworkingEngine.PendingRequest} op
|
|
96
138
|
* @public
|
|
@@ -98,8 +140,6 @@ shaka.media.SegmentPrefetch = class {
|
|
|
98
140
|
getPrefetchedSegment(reference, streamDataCallback) {
|
|
99
141
|
goog.asserts.assert(this.prefetchLimit_ > 0,
|
|
100
142
|
'SegmentPrefetch can not be used when prefetchLimit <= 0.');
|
|
101
|
-
goog.asserts.assert(reference instanceof shaka.media.SegmentReference,
|
|
102
|
-
'getPrefetchedSegment is only used for shaka.media.SegmentReference.');
|
|
103
143
|
|
|
104
144
|
const logPrefix = shaka.media.SegmentPrefetch.logPrefix_(this.stream_);
|
|
105
145
|
|
|
@@ -109,16 +149,30 @@ shaka.media.SegmentPrefetch = class {
|
|
|
109
149
|
segmentPrefetchOperation.setStreamDataCallback(streamDataCallback);
|
|
110
150
|
}
|
|
111
151
|
this.segmentPrefetchMap_.delete(reference);
|
|
112
|
-
shaka.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
152
|
+
if (reference instanceof shaka.media.SegmentReference) {
|
|
153
|
+
shaka.log.debug(
|
|
154
|
+
logPrefix,
|
|
155
|
+
'reused prefetched segment at time:', reference.startTime,
|
|
156
|
+
'mapSize', this.segmentPrefetchMap_.size);
|
|
157
|
+
} else {
|
|
158
|
+
shaka.log.debug(
|
|
159
|
+
logPrefix,
|
|
160
|
+
'reused prefetched init segment at time, mapSize',
|
|
161
|
+
this.segmentPrefetchMap_.size);
|
|
162
|
+
}
|
|
116
163
|
return segmentPrefetchOperation.getOperation();
|
|
117
164
|
} else {
|
|
118
|
-
shaka.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
165
|
+
if (reference instanceof shaka.media.SegmentReference) {
|
|
166
|
+
shaka.log.debug(
|
|
167
|
+
logPrefix,
|
|
168
|
+
'reused prefetched segment at time:', reference.startTime,
|
|
169
|
+
'mapSize', this.segmentPrefetchMap_.size);
|
|
170
|
+
} else {
|
|
171
|
+
shaka.log.debug(
|
|
172
|
+
logPrefix,
|
|
173
|
+
'reused prefetched init segment at time, mapSize',
|
|
174
|
+
this.segmentPrefetchMap_.size);
|
|
175
|
+
}
|
|
122
176
|
return null;
|
|
123
177
|
}
|
|
124
178
|
}
|
|
@@ -174,7 +228,8 @@ shaka.media.SegmentPrefetch = class {
|
|
|
174
228
|
|
|
175
229
|
/**
|
|
176
230
|
* Remove a segment from prefetch map and abort it.
|
|
177
|
-
* @param {(
|
|
231
|
+
* @param {!(shaka.media.SegmentReference|shaka.media.InitSegmentReference)}
|
|
232
|
+
* reference
|
|
178
233
|
* @private
|
|
179
234
|
*/
|
|
180
235
|
abortPrefetchedSegment_(reference) {
|
|
@@ -183,9 +238,13 @@ shaka.media.SegmentPrefetch = class {
|
|
|
183
238
|
this.segmentPrefetchMap_.delete(reference);
|
|
184
239
|
if (segmentPrefetchOperation) {
|
|
185
240
|
segmentPrefetchOperation.abort();
|
|
186
|
-
shaka.
|
|
187
|
-
|
|
188
|
-
|
|
241
|
+
if (reference instanceof shaka.media.SegmentReference) {
|
|
242
|
+
shaka.log.info(
|
|
243
|
+
logPrefix,
|
|
244
|
+
'pop and abort prefetched segment at time:', reference.startTime);
|
|
245
|
+
} else {
|
|
246
|
+
shaka.log.info(logPrefix, 'pop and abort prefetched init segment');
|
|
247
|
+
}
|
|
189
248
|
}
|
|
190
249
|
}
|
|
191
250
|
|
|
@@ -221,16 +280,25 @@ shaka.media.SegmentPrefetchOperation = class {
|
|
|
221
280
|
/**
|
|
222
281
|
* Fetch a segments
|
|
223
282
|
*
|
|
224
|
-
* @param {!shaka.media.SegmentReference}
|
|
225
|
-
*
|
|
283
|
+
* @param {!(shaka.media.SegmentReference|shaka.media.InitSegmentReference)}
|
|
284
|
+
* reference
|
|
226
285
|
* @param {!shaka.extern.Stream} stream
|
|
227
286
|
* @public
|
|
228
287
|
*/
|
|
229
288
|
dispatchFetch(reference, stream) {
|
|
289
|
+
// We need to store the data, because streamDataCallback_ might not be
|
|
290
|
+
// available when you start getting the first data.
|
|
291
|
+
let buffered = new Uint8Array(0);
|
|
230
292
|
this.operation_ = this.fetchDispatcher_(
|
|
231
293
|
reference, stream, async (data) => {
|
|
294
|
+
if (buffered.byteLength > 0) {
|
|
295
|
+
buffered = shaka.util.Uint8ArrayUtils.concat(buffered, data);
|
|
296
|
+
} else {
|
|
297
|
+
buffered = data;
|
|
298
|
+
}
|
|
232
299
|
if (this.streamDataCallback_) {
|
|
233
|
-
await this.streamDataCallback_(
|
|
300
|
+
await this.streamDataCallback_(buffered);
|
|
301
|
+
buffered = new Uint8Array(0);
|
|
234
302
|
}
|
|
235
303
|
});
|
|
236
304
|
}
|
|
@@ -198,13 +198,15 @@ shaka.media.SegmentReference = class {
|
|
|
198
198
|
* not available.
|
|
199
199
|
* @param {?shaka.extern.aes128Key=} aes128Key
|
|
200
200
|
* The segment's AES-128-CBC full segment encryption key and iv.
|
|
201
|
+
* @param {boolean=} allPartialSegments
|
|
202
|
+
* Indicate if the segment has all partial segments
|
|
201
203
|
*/
|
|
202
204
|
constructor(
|
|
203
205
|
startTime, endTime, uris, startByte, endByte, initSegmentReference,
|
|
204
206
|
timestampOffset, appendWindowStart, appendWindowEnd,
|
|
205
207
|
partialReferences = [], tilesLayout = '', tileDuration = null,
|
|
206
208
|
syncTime = null, status = shaka.media.SegmentReference.Status.AVAILABLE,
|
|
207
|
-
aes128Key = null) {
|
|
209
|
+
aes128Key = null, allPartialSegments = false) {
|
|
208
210
|
// A preload hinted Partial Segment has the same startTime and endTime.
|
|
209
211
|
goog.asserts.assert(startTime <= endTime,
|
|
210
212
|
'startTime must be less than or equal to endTime');
|
|
@@ -284,6 +286,24 @@ shaka.media.SegmentReference = class {
|
|
|
284
286
|
|
|
285
287
|
/** @type {number} */
|
|
286
288
|
this.discontinuitySequence = 0;
|
|
289
|
+
|
|
290
|
+
/** @type {boolean} */
|
|
291
|
+
this.allPartialSegments = allPartialSegments;
|
|
292
|
+
|
|
293
|
+
/** @type {boolean} */
|
|
294
|
+
this.partial = false;
|
|
295
|
+
|
|
296
|
+
/** @type {boolean} */
|
|
297
|
+
this.lastPartial = false;
|
|
298
|
+
|
|
299
|
+
for (const partial of this.partialReferences) {
|
|
300
|
+
partial.markAsPartial();
|
|
301
|
+
}
|
|
302
|
+
if (this.allPartialSegments && this.partialReferences.length) {
|
|
303
|
+
const lastPartial =
|
|
304
|
+
this.partialReferences[this.partialReferences.length - 1];
|
|
305
|
+
lastPartial.markAsLastPartial();
|
|
306
|
+
}
|
|
287
307
|
}
|
|
288
308
|
|
|
289
309
|
/**
|
|
@@ -359,6 +379,14 @@ shaka.media.SegmentReference = class {
|
|
|
359
379
|
return this.partialReferences.length > 0;
|
|
360
380
|
}
|
|
361
381
|
|
|
382
|
+
/**
|
|
383
|
+
* Returns true if it contains all partial SegmentReferences.
|
|
384
|
+
* @return {boolean}
|
|
385
|
+
*/
|
|
386
|
+
hasAllPartialSegments() {
|
|
387
|
+
return this.allPartialSegments;
|
|
388
|
+
}
|
|
389
|
+
|
|
362
390
|
/**
|
|
363
391
|
* Returns the segment's tiles layout. Only defined in image segments.
|
|
364
392
|
*
|
|
@@ -437,6 +465,44 @@ shaka.media.SegmentReference = class {
|
|
|
437
465
|
return this.independent;
|
|
438
466
|
}
|
|
439
467
|
|
|
468
|
+
/**
|
|
469
|
+
* Mark the reference as partial.
|
|
470
|
+
*
|
|
471
|
+
* @export
|
|
472
|
+
*/
|
|
473
|
+
markAsPartial() {
|
|
474
|
+
this.partial = true;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Returns true if the segment is partial.
|
|
479
|
+
*
|
|
480
|
+
* @return {boolean}
|
|
481
|
+
* @export
|
|
482
|
+
*/
|
|
483
|
+
isPartial() {
|
|
484
|
+
return this.partial;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Mark the reference as being the last part of the full segment
|
|
489
|
+
*
|
|
490
|
+
* @export
|
|
491
|
+
*/
|
|
492
|
+
markAsLastPartial() {
|
|
493
|
+
this.lastPartial = true;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Returns true if reference as being the last part of the full segment.
|
|
498
|
+
*
|
|
499
|
+
* @return {boolean}
|
|
500
|
+
* @export
|
|
501
|
+
*/
|
|
502
|
+
isLastPartial() {
|
|
503
|
+
return this.lastPartial;
|
|
504
|
+
}
|
|
505
|
+
|
|
440
506
|
/**
|
|
441
507
|
* Mark the reference as byterange optimization.
|
|
442
508
|
*
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
goog.provide('shaka.media.SegmentUtils');
|
|
8
|
+
|
|
9
|
+
goog.require('goog.asserts');
|
|
10
|
+
goog.require('shaka.log');
|
|
11
|
+
goog.require('shaka.media.ClosedCaptionParser');
|
|
12
|
+
goog.require('shaka.util.BufferUtils');
|
|
13
|
+
goog.require('shaka.util.MimeUtils');
|
|
14
|
+
goog.require('shaka.util.Mp4BoxParsers');
|
|
15
|
+
goog.require('shaka.util.Mp4Parser');
|
|
16
|
+
goog.require('shaka.util.TsParser');
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @summary Utility functions for segment parsing.
|
|
21
|
+
*/
|
|
22
|
+
shaka.media.SegmentUtils = class {
|
|
23
|
+
/**
|
|
24
|
+
* @param {string} mimeType
|
|
25
|
+
* @return {shaka.media.SegmentUtils.BasicInfo}
|
|
26
|
+
*/
|
|
27
|
+
static getBasicInfoFromMimeType(mimeType) {
|
|
28
|
+
const baseMimeType = shaka.util.MimeUtils.getBasicType(mimeType);
|
|
29
|
+
const type = baseMimeType.split('/')[0];
|
|
30
|
+
const codecs = shaka.util.MimeUtils.getCodecs(mimeType);
|
|
31
|
+
return {
|
|
32
|
+
type: type,
|
|
33
|
+
mimeType: baseMimeType,
|
|
34
|
+
codecs: codecs,
|
|
35
|
+
language: null,
|
|
36
|
+
height: null,
|
|
37
|
+
width: null,
|
|
38
|
+
channelCount: null,
|
|
39
|
+
sampleRate: null,
|
|
40
|
+
closedCaptions: new Map(),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {!BufferSource} data
|
|
46
|
+
* @return {?shaka.media.SegmentUtils.BasicInfo}
|
|
47
|
+
*/
|
|
48
|
+
static getBasicInfoFromTs(data) {
|
|
49
|
+
const uint8ArrayData = shaka.util.BufferUtils.toUint8(data);
|
|
50
|
+
const tsParser = new shaka.util.TsParser().parse(uint8ArrayData);
|
|
51
|
+
const tsCodecs = tsParser.getCodecs();
|
|
52
|
+
const videoInfo = tsParser.getVideoInfo();
|
|
53
|
+
const codecs = [];
|
|
54
|
+
let hasAudio = false;
|
|
55
|
+
let hasVideo = false;
|
|
56
|
+
switch (tsCodecs.audio) {
|
|
57
|
+
case 'aac':
|
|
58
|
+
codecs.push('mp4a.40.2');
|
|
59
|
+
hasAudio = true;
|
|
60
|
+
break;
|
|
61
|
+
case 'mp3':
|
|
62
|
+
codecs.push('mp4a.40.34');
|
|
63
|
+
hasAudio = true;
|
|
64
|
+
break;
|
|
65
|
+
case 'ac3':
|
|
66
|
+
codecs.push('ac-3');
|
|
67
|
+
hasAudio = true;
|
|
68
|
+
break;
|
|
69
|
+
case 'ec3':
|
|
70
|
+
codecs.push('ec-3');
|
|
71
|
+
hasAudio = true;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
switch (tsCodecs.video) {
|
|
75
|
+
case 'avc':
|
|
76
|
+
if (videoInfo.codec) {
|
|
77
|
+
codecs.push(videoInfo.codec);
|
|
78
|
+
} else {
|
|
79
|
+
codecs.push('avc1.42E01E');
|
|
80
|
+
}
|
|
81
|
+
hasVideo = true;
|
|
82
|
+
break;
|
|
83
|
+
case 'hvc':
|
|
84
|
+
if (videoInfo.codec) {
|
|
85
|
+
codecs.push(videoInfo.codec);
|
|
86
|
+
} else {
|
|
87
|
+
codecs.push('hvc1.1.6.L93.90');
|
|
88
|
+
}
|
|
89
|
+
hasVideo = true;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
if (!codecs.length) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const onlyAudio = hasAudio && !hasVideo;
|
|
96
|
+
const closedCaptions = new Map();
|
|
97
|
+
if (hasVideo) {
|
|
98
|
+
const captionParser = new shaka.media.ClosedCaptionParser('video/mp2t');
|
|
99
|
+
captionParser.parseFrom(data);
|
|
100
|
+
for (const stream of captionParser.getStreams()) {
|
|
101
|
+
closedCaptions.set(stream, stream);
|
|
102
|
+
}
|
|
103
|
+
captionParser.reset();
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
type: onlyAudio ? 'audio' : 'video',
|
|
107
|
+
mimeType: 'video/mp2t',
|
|
108
|
+
codecs: codecs.join(', '),
|
|
109
|
+
language: null,
|
|
110
|
+
height: videoInfo.height,
|
|
111
|
+
width: videoInfo.width,
|
|
112
|
+
channelCount: null,
|
|
113
|
+
sampleRate: null,
|
|
114
|
+
closedCaptions: closedCaptions,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @param {?BufferSource} initData
|
|
120
|
+
* @param {!BufferSource} data
|
|
121
|
+
* @return {?shaka.media.SegmentUtils.BasicInfo}
|
|
122
|
+
*/
|
|
123
|
+
static getBasicInfoFromMp4(initData, data) {
|
|
124
|
+
const Mp4Parser = shaka.util.Mp4Parser;
|
|
125
|
+
const SegmentUtils = shaka.media.SegmentUtils;
|
|
126
|
+
|
|
127
|
+
const codecs = [];
|
|
128
|
+
|
|
129
|
+
let hasAudio = false;
|
|
130
|
+
let hasVideo = false;
|
|
131
|
+
|
|
132
|
+
const addCodec = (codec) => {
|
|
133
|
+
const codecLC = codec.toLowerCase();
|
|
134
|
+
switch (codecLC) {
|
|
135
|
+
case 'avc1':
|
|
136
|
+
case 'avc3':
|
|
137
|
+
codecs.push(codecLC + '.42E01E');
|
|
138
|
+
hasVideo = true;
|
|
139
|
+
break;
|
|
140
|
+
case 'hev1':
|
|
141
|
+
case 'hvc1':
|
|
142
|
+
codecs.push(codecLC + '.1.6.L93.90');
|
|
143
|
+
hasVideo = true;
|
|
144
|
+
break;
|
|
145
|
+
case 'dvh1':
|
|
146
|
+
case 'dvhe':
|
|
147
|
+
codecs.push(codecLC + '.05.04');
|
|
148
|
+
hasVideo = true;
|
|
149
|
+
break;
|
|
150
|
+
case 'vp09':
|
|
151
|
+
codecs.push(codecLC + '.00.10.08');
|
|
152
|
+
hasVideo = true;
|
|
153
|
+
break;
|
|
154
|
+
case 'av01':
|
|
155
|
+
codecs.push(codecLC + '.0.01M.08');
|
|
156
|
+
hasVideo = true;
|
|
157
|
+
break;
|
|
158
|
+
case 'mp4a':
|
|
159
|
+
// We assume AAC, but this can be wrong since mp4a supports
|
|
160
|
+
// others codecs
|
|
161
|
+
codecs.push('mp4a.40.2');
|
|
162
|
+
hasAudio = true;
|
|
163
|
+
break;
|
|
164
|
+
case 'ac-3':
|
|
165
|
+
case 'ec-3':
|
|
166
|
+
case 'opus':
|
|
167
|
+
case 'flac':
|
|
168
|
+
codecs.push(codecLC);
|
|
169
|
+
hasAudio = true;
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const codecBoxParser = (box) => addCodec(box.name);
|
|
175
|
+
|
|
176
|
+
/** @type {?string} */
|
|
177
|
+
let language = null;
|
|
178
|
+
/** @type {?string} */
|
|
179
|
+
let height = null;
|
|
180
|
+
/** @type {?string} */
|
|
181
|
+
let width = null;
|
|
182
|
+
/** @type {?number} */
|
|
183
|
+
let channelCount = null;
|
|
184
|
+
/** @type {?number} */
|
|
185
|
+
let sampleRate = null;
|
|
186
|
+
|
|
187
|
+
new Mp4Parser()
|
|
188
|
+
.box('moov', Mp4Parser.children)
|
|
189
|
+
.box('trak', Mp4Parser.children)
|
|
190
|
+
.fullBox('tkhd', (box) => {
|
|
191
|
+
goog.asserts.assert(
|
|
192
|
+
box.version != null,
|
|
193
|
+
'TKHD is a full box and should have a valid version.');
|
|
194
|
+
const parsedTKHDBox = shaka.util.Mp4BoxParsers.parseTKHD(
|
|
195
|
+
box.reader, box.version);
|
|
196
|
+
height = String(parsedTKHDBox.height);
|
|
197
|
+
width = String(parsedTKHDBox.width);
|
|
198
|
+
})
|
|
199
|
+
.box('mdia', Mp4Parser.children)
|
|
200
|
+
.fullBox('mdhd', (box) => {
|
|
201
|
+
goog.asserts.assert(
|
|
202
|
+
box.version != null,
|
|
203
|
+
'MDHD is a full box and should have a valid version.');
|
|
204
|
+
const parsedMDHDBox = shaka.util.Mp4BoxParsers.parseMDHD(
|
|
205
|
+
box.reader, box.version);
|
|
206
|
+
language = parsedMDHDBox.language;
|
|
207
|
+
})
|
|
208
|
+
.box('minf', Mp4Parser.children)
|
|
209
|
+
.box('stbl', Mp4Parser.children)
|
|
210
|
+
.fullBox('stsd', Mp4Parser.sampleDescription)
|
|
211
|
+
|
|
212
|
+
// AUDIO
|
|
213
|
+
// These are the various boxes that signal a codec.
|
|
214
|
+
.box('mp4a', (box) => {
|
|
215
|
+
const parsedMP4ABox = shaka.util.Mp4BoxParsers.parseMP4A(box.reader);
|
|
216
|
+
channelCount = parsedMP4ABox.channelCount;
|
|
217
|
+
sampleRate = parsedMP4ABox.sampleRate;
|
|
218
|
+
if (box.reader.hasMoreData()) {
|
|
219
|
+
Mp4Parser.children(box);
|
|
220
|
+
} else {
|
|
221
|
+
codecBoxParser(box);
|
|
222
|
+
}
|
|
223
|
+
})
|
|
224
|
+
.box('esds', (box) => {
|
|
225
|
+
const parsedESDSBox = shaka.util.Mp4BoxParsers.parseESDS(box.reader);
|
|
226
|
+
codecs.push(parsedESDSBox.codec);
|
|
227
|
+
hasAudio = true;
|
|
228
|
+
})
|
|
229
|
+
.box('ac-3', codecBoxParser)
|
|
230
|
+
.box('ec-3', codecBoxParser)
|
|
231
|
+
.box('opus', codecBoxParser)
|
|
232
|
+
.box('Opus', codecBoxParser)
|
|
233
|
+
.box('fLaC', codecBoxParser)
|
|
234
|
+
|
|
235
|
+
// VIDEO
|
|
236
|
+
// These are the various boxes that signal a codec.
|
|
237
|
+
.box('avc1', (box) => {
|
|
238
|
+
const parsedAVCBox =
|
|
239
|
+
shaka.util.Mp4BoxParsers.parseAVC(box.reader, box.name);
|
|
240
|
+
codecs.push(parsedAVCBox.codec);
|
|
241
|
+
hasVideo = true;
|
|
242
|
+
})
|
|
243
|
+
.box('avc3', (box) => {
|
|
244
|
+
const parsedAVCBox =
|
|
245
|
+
shaka.util.Mp4BoxParsers.parseAVC(box.reader, box.name);
|
|
246
|
+
codecs.push(parsedAVCBox.codec);
|
|
247
|
+
hasVideo = true;
|
|
248
|
+
})
|
|
249
|
+
.box('hev1', codecBoxParser)
|
|
250
|
+
.box('hvc1', codecBoxParser)
|
|
251
|
+
.box('dvh1', codecBoxParser)
|
|
252
|
+
.box('dvhe', codecBoxParser)
|
|
253
|
+
.box('vp09', codecBoxParser)
|
|
254
|
+
.box('av01', codecBoxParser)
|
|
255
|
+
|
|
256
|
+
// This signals an encrypted sample, which we can go inside of to
|
|
257
|
+
// find the codec used.
|
|
258
|
+
// Note: If encrypted, you can only have audio or video, not both.
|
|
259
|
+
.box('enca', Mp4Parser.visualSampleEntry)
|
|
260
|
+
.box('encv', Mp4Parser.visualSampleEntry)
|
|
261
|
+
.box('sinf', Mp4Parser.children)
|
|
262
|
+
.box('frma', (box) => {
|
|
263
|
+
const {codec} = shaka.util.Mp4BoxParsers.parseFRMA(box.reader);
|
|
264
|
+
addCodec(codec);
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
.parse(initData || data, /* partialOkay= */ true);
|
|
268
|
+
if (!codecs.length) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
const onlyAudio = hasAudio && !hasVideo;
|
|
272
|
+
const closedCaptions = new Map();
|
|
273
|
+
if (hasVideo) {
|
|
274
|
+
const captionParser = new shaka.media.ClosedCaptionParser('video/mp4');
|
|
275
|
+
if (initData) {
|
|
276
|
+
captionParser.init(initData);
|
|
277
|
+
}
|
|
278
|
+
captionParser.parseFrom(data);
|
|
279
|
+
for (const stream of captionParser.getStreams()) {
|
|
280
|
+
closedCaptions.set(stream, stream);
|
|
281
|
+
}
|
|
282
|
+
captionParser.reset();
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
type: onlyAudio ? 'audio' : 'video',
|
|
286
|
+
mimeType: onlyAudio ? 'audio/mp4' : 'video/mp4',
|
|
287
|
+
codecs: SegmentUtils.filterDuplicateCodecs_(codecs).join(', '),
|
|
288
|
+
language: language,
|
|
289
|
+
height: height,
|
|
290
|
+
width: width,
|
|
291
|
+
channelCount: channelCount,
|
|
292
|
+
sampleRate: sampleRate,
|
|
293
|
+
closedCaptions: closedCaptions,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* @param {!Array.<string>} codecs
|
|
299
|
+
* @return {!Array.<string>} codecs
|
|
300
|
+
* @private
|
|
301
|
+
*/
|
|
302
|
+
static filterDuplicateCodecs_(codecs) {
|
|
303
|
+
// Filter out duplicate codecs.
|
|
304
|
+
const seen = new Set();
|
|
305
|
+
const ret = [];
|
|
306
|
+
for (const codec of codecs) {
|
|
307
|
+
const shortCodec = shaka.util.MimeUtils.getCodecBase(codec);
|
|
308
|
+
if (!seen.has(shortCodec)) {
|
|
309
|
+
ret.push(codec);
|
|
310
|
+
seen.add(shortCodec);
|
|
311
|
+
} else {
|
|
312
|
+
shaka.log.debug('Ignoring duplicate codec');
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return ret;
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* @typedef {{
|
|
322
|
+
* type: string,
|
|
323
|
+
* mimeType: string,
|
|
324
|
+
* codecs: string,
|
|
325
|
+
* language: ?string,
|
|
326
|
+
* height: ?string,
|
|
327
|
+
* width: ?string,
|
|
328
|
+
* channelCount: ?number,
|
|
329
|
+
* sampleRate: ?number,
|
|
330
|
+
* closedCaptions: Map.<string, string>
|
|
331
|
+
* }}
|
|
332
|
+
*
|
|
333
|
+
* @property {string} type
|
|
334
|
+
* @property {string} mimeType
|
|
335
|
+
* @property {string} codecs
|
|
336
|
+
* @property {?string} language
|
|
337
|
+
* @property {?string} height
|
|
338
|
+
* @property {?string} width
|
|
339
|
+
* @property {?number} channelCount
|
|
340
|
+
* @property {?number} sampleRate
|
|
341
|
+
* @property {Map.<string, string>} closedCaptions
|
|
342
|
+
*/
|
|
343
|
+
shaka.media.SegmentUtils.BasicInfo;
|
|
@@ -174,9 +174,6 @@ shaka.media.StallDetector.MediaElementImplementation = class {
|
|
|
174
174
|
if (this.mediaElement_.paused) {
|
|
175
175
|
return false;
|
|
176
176
|
}
|
|
177
|
-
if (this.mediaElement_.playbackRate == 0) {
|
|
178
|
-
return false;
|
|
179
|
-
}
|
|
180
177
|
|
|
181
178
|
// If we have don't have enough content, we are not stalled, we are
|
|
182
179
|
// buffering.
|