shaka-player 3.1.5 → 3.2.3
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/CHANGELOG.md +129 -6
- package/README.md +4 -4
- package/build/types/polyfill +1 -0
- package/demo/common/asset.js +9 -7
- package/demo/common/assets.js +10 -1
- package/demo/common/message_ids.js +2 -1
- package/demo/config.js +5 -2
- package/demo/index.html +1 -1
- package/demo/locales/en.json +2 -1
- package/demo/locales/source.json +8 -4
- package/demo/main.js +2 -1
- package/dist/demo.compiled.debug.js +14 -14
- package/dist/demo.compiled.debug.map +2 -2
- package/dist/demo.compiled.js +14 -14
- package/dist/demo.compiled.map +2 -2
- package/dist/deps.js +21 -20
- package/dist/locales.js +30 -30
- package/dist/receiver.compiled.debug.js +8 -8
- package/dist/receiver.compiled.debug.map +2 -2
- package/dist/receiver.compiled.js +8 -8
- package/dist/receiver.compiled.map +2 -2
- package/dist/shaka-player.compiled.d.ts +63 -10
- package/dist/shaka-player.compiled.debug.d.ts +63 -10
- package/dist/shaka-player.compiled.debug.externs.js +786 -716
- package/dist/shaka-player.compiled.debug.js +563 -534
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +786 -716
- package/dist/shaka-player.compiled.js +820 -809
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +63 -10
- package/dist/shaka-player.ui.debug.d.ts +63 -10
- package/dist/shaka-player.ui.debug.externs.js +542 -472
- package/dist/shaka-player.ui.debug.js +594 -566
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +542 -472
- package/dist/shaka-player.ui.js +938 -927
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/ui.md +5 -5
- package/externs/aria.js +6 -0
- package/externs/shaka/ads.js +24 -1
- package/externs/shaka/manifest.js +6 -1
- package/externs/shaka/offline.js +5 -0
- package/externs/shaka/player.js +61 -9
- package/externs/shaka/text.js +10 -10
- package/externs/texttrack.js +3 -0
- package/lib/ads/ad_manager.js +16 -15
- package/lib/ads/client_side_ad_manager.js +6 -2
- package/lib/ads/server_side_ad_manager.js +19 -2
- package/lib/cast/cast_proxy.js +8 -0
- package/lib/cast/cast_utils.js +6 -1
- package/lib/cea/cea608_data_channel.js +56 -56
- package/lib/cea/cea608_memory.js +33 -33
- package/lib/cea/cea708_service.js +43 -43
- package/lib/cea/cea708_window.js +38 -38
- package/lib/cea/cea_decoder.js +13 -14
- package/lib/cea/cea_utils.js +23 -23
- package/lib/cea/dtvcc_packet_builder.js +7 -7
- package/lib/cea/i_caption_decoder.js +2 -3
- package/lib/cea/i_cea_parser.js +2 -2
- package/lib/cea/mp4_cea_parser.js +6 -6
- package/lib/dash/dash_parser.js +5 -0
- package/lib/hls/hls_parser.js +159 -12
- package/lib/media/closed_caption_parser.js +1 -2
- package/lib/media/drm_engine.js +162 -192
- package/lib/media/segment_index.js +4 -1
- package/lib/media/segment_reference.js +37 -2
- package/lib/media/streaming_engine.js +6 -6
- package/lib/net/networking_engine.js +1 -0
- package/lib/offline/download_manager.js +1 -2
- package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
- package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
- package/lib/offline/manifest_converter.js +3 -1
- package/lib/offline/storage.js +12 -14
- package/lib/player.js +426 -149
- package/lib/polyfill/aria.js +82 -0
- package/lib/routing/walker.js +2 -3
- package/lib/text/srt_text_parser.js +4 -4
- package/lib/text/text_engine.js +2 -2
- package/lib/text/vtt_text_parser.js +2 -2
- package/lib/text/web_vtt_generator.js +55 -12
- package/lib/util/error.js +6 -0
- package/lib/util/manifest_parser_utils.js +1 -0
- package/lib/util/mp4_box_parsers.js +20 -20
- package/lib/util/multi_map.js +8 -0
- package/lib/util/player_configuration.js +13 -1
- package/lib/util/stream_utils.js +168 -88
- package/lib/util/xml_utils.js +31 -12
- package/package.json +1 -1
- package/roadmap.md +24 -22
- package/shaka-player.uncompiled.js +1 -0
- package/test/cast/cast_utils_unit.js +1 -0
- package/test/cea/cea608_memory_unit.js +1 -1
- package/test/cea/cea708_service_unit.js +8 -8
- package/test/cea/cea708_window_unit.js +6 -6
- package/test/cea/cea_decoder_unit.js +1 -1
- package/test/cea/dtvcc_packet_builder_unit.js +2 -2
- package/test/dash/dash_parser_manifest_unit.js +43 -1
- package/test/hls/hls_parser_unit.js +250 -0
- package/test/media/drm_engine_integration.js +86 -12
- package/test/media/drm_engine_unit.js +310 -450
- package/test/offline/manifest_convert_unit.js +2 -0
- package/test/offline/storage_integration.js +9 -19
- package/test/player_integration.js +85 -0
- package/test/player_src_equals_integration.js +85 -0
- package/test/player_unit.js +25 -37
- package/test/test/assets/chapters.srt +11 -0
- package/test/test/assets/chapters.vtt +10 -0
- package/test/test/assets/chapters2.vtt +10 -0
- package/test/test/util/cea_utils.js +12 -12
- package/test/test/util/fake_ad_manager.js +7 -0
- package/test/test/util/manifest_generator.js +2 -0
- package/test/test/util/manifest_parser_util.js +3 -1
- package/test/text/web_vtt_generator_unit.js +81 -7
- package/test/util/stream_utils_unit.js +118 -10
- package/ui/airplay_button.js +1 -1
- package/ui/audio_language_selection.js +2 -5
- package/ui/cast_button.js +4 -6
- package/ui/constants.js +0 -3
- package/ui/fast_forward_button.js +2 -3
- package/ui/fullscreen_button.js +1 -3
- package/ui/language_utils.js +1 -1
- package/ui/loop_button.js +1 -3
- package/ui/mute_button.js +1 -3
- package/ui/overflow_menu.js +2 -3
- package/ui/pip_button.js +5 -7
- package/ui/playback_rate_selection.js +3 -6
- package/ui/presentation_time.js +1 -3
- package/ui/resolution_selection.js +4 -7
- package/ui/rewind_button.js +2 -3
- package/ui/seek_bar.js +2 -4
- package/ui/spacer.js +1 -1
- package/ui/text_selection.js +8 -11
- package/ui/ui_utils.js +1 -1
- package/ui/volume_bar.js +1 -3
package/lib/cea/cea_utils.js
CHANGED
|
@@ -15,10 +15,10 @@ shaka.cea.CeaUtils = class {
|
|
|
15
15
|
/**
|
|
16
16
|
* Emits a closed caption based on the state of the buffer.
|
|
17
17
|
* @param {!shaka.text.Cue} topLevelCue
|
|
18
|
-
* @param {
|
|
18
|
+
* @param {string} stream
|
|
19
19
|
* @param {!Array<!Array<?shaka.cea.CeaUtils.StyledChar>>} memory
|
|
20
|
-
* @param {
|
|
21
|
-
* @param {
|
|
20
|
+
* @param {number} startTime Start time of the cue.
|
|
21
|
+
* @param {number} endTime End time of the cue.
|
|
22
22
|
* @return {?shaka.cea.ICaptionDecoder.ClosedCaption}
|
|
23
23
|
*/
|
|
24
24
|
static getParsedCaption(topLevelCue, stream, memory, startTime, endTime) {
|
|
@@ -153,12 +153,12 @@ shaka.cea.CeaUtils = class {
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
/**
|
|
156
|
-
* @param {
|
|
157
|
-
* @param {
|
|
158
|
-
* @param {
|
|
159
|
-
* @param {
|
|
160
|
-
* @param {
|
|
161
|
-
* @param {
|
|
156
|
+
* @param {number} startTime
|
|
157
|
+
* @param {number} endTime
|
|
158
|
+
* @param {boolean} underline
|
|
159
|
+
* @param {boolean} italics
|
|
160
|
+
* @param {string} txtColor
|
|
161
|
+
* @param {string} bgColor
|
|
162
162
|
* @return {!shaka.text.Cue}
|
|
163
163
|
*/
|
|
164
164
|
static createStyledCue(startTime, endTime, underline,
|
|
@@ -176,8 +176,8 @@ shaka.cea.CeaUtils = class {
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
/**
|
|
179
|
-
* @param {
|
|
180
|
-
* @param {
|
|
179
|
+
* @param {number} startTime
|
|
180
|
+
* @param {number} endTime
|
|
181
181
|
* @return {!shaka.text.Cue}
|
|
182
182
|
*/
|
|
183
183
|
static createLineBreakCue(startTime, endTime) {
|
|
@@ -198,61 +198,61 @@ shaka.cea.CeaUtils.StyledChar = class {
|
|
|
198
198
|
*/
|
|
199
199
|
constructor(character, underline, italics, backgroundColor, textColor) {
|
|
200
200
|
/**
|
|
201
|
-
* @private {
|
|
201
|
+
* @private {string}
|
|
202
202
|
*/
|
|
203
203
|
this.character_ = character;
|
|
204
204
|
|
|
205
205
|
/**
|
|
206
|
-
* @private {
|
|
206
|
+
* @private {boolean}
|
|
207
207
|
*/
|
|
208
208
|
this.underline_ = underline;
|
|
209
209
|
|
|
210
210
|
/**
|
|
211
|
-
* @private {
|
|
211
|
+
* @private {boolean}
|
|
212
212
|
*/
|
|
213
213
|
this.italics_ = italics;
|
|
214
214
|
|
|
215
215
|
/**
|
|
216
|
-
* @private {
|
|
216
|
+
* @private {string}
|
|
217
217
|
*/
|
|
218
218
|
this.backgroundColor_ = backgroundColor;
|
|
219
219
|
|
|
220
220
|
/**
|
|
221
|
-
* @private {
|
|
221
|
+
* @private {string}
|
|
222
222
|
*/
|
|
223
223
|
this.textColor_ = textColor;
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
/**
|
|
227
|
-
* @return {
|
|
227
|
+
* @return {string}
|
|
228
228
|
*/
|
|
229
229
|
getChar() {
|
|
230
230
|
return this.character_;
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
/**
|
|
234
|
-
* @return {
|
|
234
|
+
* @return {boolean}
|
|
235
235
|
*/
|
|
236
236
|
isUnderlined() {
|
|
237
237
|
return this.underline_;
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
/**
|
|
241
|
-
* @return {
|
|
241
|
+
* @return {boolean}
|
|
242
242
|
*/
|
|
243
243
|
isItalicized() {
|
|
244
244
|
return this.italics_;
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
/**
|
|
248
|
-
* @return {
|
|
248
|
+
* @return {string}
|
|
249
249
|
*/
|
|
250
250
|
getBackgroundColor() {
|
|
251
251
|
return this.backgroundColor_;
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
/**
|
|
255
|
-
* @return {
|
|
255
|
+
* @return {string}
|
|
256
256
|
*/
|
|
257
257
|
getTextColor() {
|
|
258
258
|
return this.textColor_;
|
|
@@ -261,12 +261,12 @@ shaka.cea.CeaUtils.StyledChar = class {
|
|
|
261
261
|
|
|
262
262
|
/**
|
|
263
263
|
* Default background color for text.
|
|
264
|
-
* @const {
|
|
264
|
+
* @const {string}
|
|
265
265
|
*/
|
|
266
266
|
shaka.cea.CeaUtils.DEFAULT_BG_COLOR = 'black';
|
|
267
267
|
|
|
268
268
|
/**
|
|
269
269
|
* Default text color.
|
|
270
|
-
* @const {
|
|
270
|
+
* @const {string}
|
|
271
271
|
*/
|
|
272
272
|
shaka.cea.CeaUtils.DEFAULT_TXT_COLOR = 'white';
|
|
@@ -35,7 +35,7 @@ shaka.cea.DtvccPacketBuilder = class {
|
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Keeps track of the number of bytes left to add in the current packet.
|
|
38
|
-
* @private {
|
|
38
|
+
* @private {number}
|
|
39
39
|
*/
|
|
40
40
|
this.bytesLeftToAddInCurrentPacket_ = 0;
|
|
41
41
|
}
|
|
@@ -107,7 +107,7 @@ shaka.cea.DtvccPacket = class {
|
|
|
107
107
|
constructor(packetData) {
|
|
108
108
|
/**
|
|
109
109
|
* Keeps track of the position to read the next byte from in the packet.
|
|
110
|
-
* @private {
|
|
110
|
+
* @private {number}
|
|
111
111
|
*/
|
|
112
112
|
this.pos_ = 0;
|
|
113
113
|
|
|
@@ -119,14 +119,14 @@ shaka.cea.DtvccPacket = class {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
/**
|
|
122
|
-
* @return {
|
|
122
|
+
* @return {boolean}
|
|
123
123
|
*/
|
|
124
124
|
hasMoreData() {
|
|
125
125
|
return this.pos_ < this.packetData_.length;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
/**
|
|
129
|
-
* @return {
|
|
129
|
+
* @return {number}
|
|
130
130
|
*/
|
|
131
131
|
getPosition() {
|
|
132
132
|
return this.pos_;
|
|
@@ -149,7 +149,7 @@ shaka.cea.DtvccPacket = class {
|
|
|
149
149
|
|
|
150
150
|
/**
|
|
151
151
|
* Skips the provided number of blocks in the buffer.
|
|
152
|
-
* @param {
|
|
152
|
+
* @param {number} numBlocks
|
|
153
153
|
* @throws {!shaka.util.Error}
|
|
154
154
|
*/
|
|
155
155
|
skip(numBlocks) {
|
|
@@ -164,11 +164,11 @@ shaka.cea.DtvccPacket = class {
|
|
|
164
164
|
};
|
|
165
165
|
|
|
166
166
|
/**
|
|
167
|
-
* @const {
|
|
167
|
+
* @const {number}
|
|
168
168
|
*/
|
|
169
169
|
shaka.cea.DtvccPacketBuilder.DTVCC_PACKET_DATA = 2;
|
|
170
170
|
|
|
171
171
|
/**
|
|
172
|
-
* @const {
|
|
172
|
+
* @const {number}
|
|
173
173
|
*/
|
|
174
174
|
shaka.cea.DtvccPacketBuilder.DTVCC_PACKET_START = 3;
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
goog.provide('shaka.cea.ICaptionDecoder');
|
|
8
|
-
goog.provide('shaka.cea.ICaptionDecoder.ClosedCaption');
|
|
9
8
|
|
|
10
9
|
goog.require('shaka.text.Cue');
|
|
11
10
|
|
|
@@ -23,7 +22,7 @@ shaka.cea.ICaptionDecoder = class {
|
|
|
23
22
|
* @param {!Uint8Array} userDataSeiMessage
|
|
24
23
|
* This is a User Data registered by Rec.ITU-T T.35 SEI message.
|
|
25
24
|
* It is described in sections D.1.6 and D.2.6 of Rec. ITU-T H.264 (06/2019).
|
|
26
|
-
* @param {
|
|
25
|
+
* @param {number} pts PTS when this packet was received, in seconds.
|
|
27
26
|
*/
|
|
28
27
|
extract(userDataSeiMessage, pts) {}
|
|
29
28
|
|
|
@@ -46,7 +45,7 @@ shaka.cea.ICaptionDecoder = class {
|
|
|
46
45
|
* Parsed Cue.
|
|
47
46
|
* @typedef {{
|
|
48
47
|
* cue: !shaka.text.Cue,
|
|
49
|
-
* stream:
|
|
48
|
+
* stream: string
|
|
50
49
|
* }}
|
|
51
50
|
*/
|
|
52
51
|
shaka.cea.ICaptionDecoder.ClosedCaption;
|
package/lib/cea/i_cea_parser.js
CHANGED
|
@@ -39,7 +39,7 @@ shaka.cea.ICeaParser.DEFAULT_TIMESCALE_VALUE = 90000;
|
|
|
39
39
|
/**
|
|
40
40
|
* @typedef {{
|
|
41
41
|
* packet: !Uint8Array,
|
|
42
|
-
* pts:
|
|
42
|
+
* pts: number
|
|
43
43
|
* }}
|
|
44
44
|
*
|
|
45
45
|
* @description Parsed Caption Packet.
|
|
@@ -47,7 +47,7 @@ shaka.cea.ICeaParser.DEFAULT_TIMESCALE_VALUE = 90000;
|
|
|
47
47
|
* Caption packet. More specifically, it contains a "User data
|
|
48
48
|
* registered by Recommendation ITU-T T.35 SEI message", from section D.1.6
|
|
49
49
|
* and section D.2.6 of Rec. ITU-T H.264 (06/2019).
|
|
50
|
-
* @property {
|
|
50
|
+
* @property {number} pts
|
|
51
51
|
* The presentation timestamp (pts) at which the ITU-T T.35 data shows up.
|
|
52
52
|
* in seconds.
|
|
53
53
|
* @exportDoc
|
|
@@ -36,13 +36,13 @@ shaka.cea.Mp4CeaParser = class {
|
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* Default sample duration, as specified by the TREX box.
|
|
39
|
-
* @private {
|
|
39
|
+
* @private {number}
|
|
40
40
|
*/
|
|
41
41
|
this.defaultSampleDuration_ = 0;
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* Default sample size, as specified by the TREX box.
|
|
45
|
-
* @private {
|
|
45
|
+
* @private {number}
|
|
46
46
|
*/
|
|
47
47
|
this.defaultSampleSize_ = 0;
|
|
48
48
|
}
|
|
@@ -195,10 +195,10 @@ shaka.cea.Mp4CeaParser = class {
|
|
|
195
195
|
/**
|
|
196
196
|
* Parse MDAT box.
|
|
197
197
|
* @param {!shaka.util.DataViewReader} reader
|
|
198
|
-
* @param {
|
|
199
|
-
* @param {
|
|
200
|
-
* @param {
|
|
201
|
-
* @param {
|
|
198
|
+
* @param {number} time
|
|
199
|
+
* @param {number} timescale
|
|
200
|
+
* @param {number} defaultSampleDuration
|
|
201
|
+
* @param {number} defaultSampleSize
|
|
202
202
|
* @param {!Array<shaka.util.ParsedTRUNSample>} sampleData
|
|
203
203
|
* @param {!Array<!shaka.cea.ICeaParser.CaptionPacket>} captionPackets
|
|
204
204
|
* @private
|
package/lib/dash/dash_parser.js
CHANGED
|
@@ -301,6 +301,11 @@ shaka.dash.DashParser = class {
|
|
|
301
301
|
const Functional = shaka.util.Functional;
|
|
302
302
|
const XmlUtils = shaka.util.XmlUtils;
|
|
303
303
|
|
|
304
|
+
const manifestPreprocessor = this.config_.dash.manifestPreprocessor;
|
|
305
|
+
if (manifestPreprocessor) {
|
|
306
|
+
manifestPreprocessor(mpd);
|
|
307
|
+
}
|
|
308
|
+
|
|
304
309
|
// Get any Location elements. This will update the manifest location and
|
|
305
310
|
// the base URI.
|
|
306
311
|
/** @type {!Array.<string>} */
|
package/lib/hls/hls_parser.js
CHANGED
|
@@ -92,7 +92,8 @@ shaka.hls.HlsParser = class {
|
|
|
92
92
|
* timestamps, offsets, and to handle TS rollover.
|
|
93
93
|
*
|
|
94
94
|
* During parsing, used to avoid duplicates in the async methods
|
|
95
|
-
* createStreamInfoFromMediaTag_ and
|
|
95
|
+
* createStreamInfoFromMediaTag_, createStreamInfoFromImageTag_ and
|
|
96
|
+
* createStreamInfoFromVariantTag_.
|
|
96
97
|
*
|
|
97
98
|
* During parsing of updates, used by getStartTime_ to determine the start
|
|
98
99
|
* time of the first segment from existing segment references.
|
|
@@ -403,6 +404,9 @@ shaka.hls.HlsParser = class {
|
|
|
403
404
|
/** @type {!Array.<!shaka.hls.Tag>} */
|
|
404
405
|
const variantTags = Utils.filterTagsByName(
|
|
405
406
|
playlist.tags, 'EXT-X-STREAM-INF');
|
|
407
|
+
/** @type {!Array.<!shaka.hls.Tag>} */
|
|
408
|
+
const imageTags = Utils.filterTagsByName(
|
|
409
|
+
playlist.tags, 'EXT-X-IMAGE-STREAM-INF');
|
|
406
410
|
|
|
407
411
|
this.parseCodecs_(variantTags);
|
|
408
412
|
|
|
@@ -437,6 +441,7 @@ shaka.hls.HlsParser = class {
|
|
|
437
441
|
this.parseClosedCaptions_(mediaTags);
|
|
438
442
|
const variants = await this.createVariantsForTags_(variantTags);
|
|
439
443
|
const textStreams = await this.parseTexts_(mediaTags);
|
|
444
|
+
const imageStreams = await this.parseImages_(imageTags);
|
|
440
445
|
|
|
441
446
|
// Make sure that the parser has not been destroyed.
|
|
442
447
|
if (!this.playerInterface_) {
|
|
@@ -555,7 +560,7 @@ shaka.hls.HlsParser = class {
|
|
|
555
560
|
presentationTimeline: this.presentationTimeline_,
|
|
556
561
|
variants,
|
|
557
562
|
textStreams,
|
|
558
|
-
imageStreams
|
|
563
|
+
imageStreams,
|
|
559
564
|
offlineSessionIds: [],
|
|
560
565
|
minBufferTime: 0,
|
|
561
566
|
};
|
|
@@ -688,6 +693,34 @@ shaka.hls.HlsParser = class {
|
|
|
688
693
|
return textStreams.filter((s) => s);
|
|
689
694
|
}
|
|
690
695
|
|
|
696
|
+
/**
|
|
697
|
+
* @param {!Array.<!shaka.hls.Tag>} imageTags from the playlist.
|
|
698
|
+
* @return {!Promise.<!Array.<!shaka.extern.Stream>>}
|
|
699
|
+
* @private
|
|
700
|
+
*/
|
|
701
|
+
async parseImages_(imageTags) {
|
|
702
|
+
// Create image stream for each image tag.
|
|
703
|
+
const imageStreamPromises = imageTags.map(async (tag) => {
|
|
704
|
+
const disableThumbnails = this.config_.disableThumbnails;
|
|
705
|
+
if (disableThumbnails) {
|
|
706
|
+
return null;
|
|
707
|
+
}
|
|
708
|
+
try {
|
|
709
|
+
const streamInfo = await this.createStreamInfoFromImageTag_(tag);
|
|
710
|
+
goog.asserts.assert(
|
|
711
|
+
streamInfo, 'Should always have a streamInfo for image');
|
|
712
|
+
return streamInfo.stream;
|
|
713
|
+
} catch (e) {
|
|
714
|
+
if (this.config_.hls.ignoreImageStreamFailures) {
|
|
715
|
+
return null;
|
|
716
|
+
}
|
|
717
|
+
throw e;
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
const imageStreams = await Promise.all(imageStreamPromises);
|
|
721
|
+
return imageStreams.filter((s) => s);
|
|
722
|
+
}
|
|
723
|
+
|
|
691
724
|
/**
|
|
692
725
|
* @param {!Array.<!shaka.hls.Tag>} mediaTags Media tags from the playlist.
|
|
693
726
|
* @private
|
|
@@ -1206,6 +1239,75 @@ shaka.hls.HlsParser = class {
|
|
|
1206
1239
|
return streamInfo;
|
|
1207
1240
|
}
|
|
1208
1241
|
|
|
1242
|
+
/**
|
|
1243
|
+
* Parse EXT-X-MEDIA media tag into a Stream object.
|
|
1244
|
+
*
|
|
1245
|
+
* @param {shaka.hls.Tag} tag
|
|
1246
|
+
* @return {!Promise.<?shaka.hls.HlsParser.StreamInfo>}
|
|
1247
|
+
* @private
|
|
1248
|
+
*/
|
|
1249
|
+
async createStreamInfoFromImageTag_(tag) {
|
|
1250
|
+
goog.asserts.assert(tag.name == 'EXT-X-IMAGE-STREAM-INF',
|
|
1251
|
+
'Should only be called on image tags!');
|
|
1252
|
+
/** @type {string} */
|
|
1253
|
+
const type = shaka.util.ManifestParserUtils.ContentType.IMAGE;
|
|
1254
|
+
|
|
1255
|
+
const verbatimImagePlaylistUri = this.variableSubstitution_(
|
|
1256
|
+
tag.getRequiredAttrValue('URI'), this.globalVariables_);
|
|
1257
|
+
const codecs = tag.getAttributeValue('CODECS', 'jpeg') || '';
|
|
1258
|
+
|
|
1259
|
+
// Check if the stream has already been created as part of another Variant
|
|
1260
|
+
// and return it if it has.
|
|
1261
|
+
if (this.uriToStreamInfosMap_.has(verbatimImagePlaylistUri)) {
|
|
1262
|
+
return this.uriToStreamInfosMap_.get(verbatimImagePlaylistUri);
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
const language = this.getLanguage_(tag);
|
|
1266
|
+
const name = tag.getAttributeValue('NAME');
|
|
1267
|
+
|
|
1268
|
+
const characteristics = tag.getAttributeValue('CHARACTERISTICS');
|
|
1269
|
+
|
|
1270
|
+
const streamInfo = await this.createStreamInfo_(
|
|
1271
|
+
verbatimImagePlaylistUri, codecs, type, language, /* primary= */ false,
|
|
1272
|
+
name, /* channelsCount= */ null, /* closedCaptions= */ null,
|
|
1273
|
+
characteristics, /* forced= */ false, /* spatialAudio= */ false);
|
|
1274
|
+
if (streamInfo == null) {
|
|
1275
|
+
return null;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
// TODO: This check is necessary because of the possibility of multiple
|
|
1279
|
+
// calls to createStreamInfoFromImageTag_ before either has resolved.
|
|
1280
|
+
if (this.uriToStreamInfosMap_.has(verbatimImagePlaylistUri)) {
|
|
1281
|
+
return this.uriToStreamInfosMap_.get(verbatimImagePlaylistUri);
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
// Parse misc attributes.
|
|
1285
|
+
const resolution = tag.getAttributeValue('RESOLUTION');
|
|
1286
|
+
if (resolution) {
|
|
1287
|
+
// The RESOLUTION tag represents the resolution of a single thumbnail, not
|
|
1288
|
+
// of the entire sheet at once (like we expect in the output).
|
|
1289
|
+
// So multiply by the layout size.
|
|
1290
|
+
|
|
1291
|
+
const reference = streamInfo.stream.segmentIndex.get(0);
|
|
1292
|
+
const layout = reference.getTilesLayout();
|
|
1293
|
+
if (layout) {
|
|
1294
|
+
streamInfo.stream.width =
|
|
1295
|
+
Number(resolution.split('x')[0]) * Number(layout.split('x')[0]);
|
|
1296
|
+
streamInfo.stream.height =
|
|
1297
|
+
Number(resolution.split('x')[1]) * Number(layout.split('x')[1]);
|
|
1298
|
+
// TODO: What happens if there are multiple grids, with different
|
|
1299
|
+
// layout sizes, inside this image stream?
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
const bandwidth = tag.getAttributeValue('BANDWIDTH');
|
|
1303
|
+
if (bandwidth) {
|
|
1304
|
+
streamInfo.stream.bandwidth = Number(bandwidth);
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
this.uriToStreamInfosMap_.set(verbatimImagePlaylistUri, streamInfo);
|
|
1308
|
+
return streamInfo;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1209
1311
|
/**
|
|
1210
1312
|
* Parse an EXT-X-STREAM-INF media tag into a Stream object.
|
|
1211
1313
|
*
|
|
@@ -1278,7 +1380,8 @@ shaka.hls.HlsParser = class {
|
|
|
1278
1380
|
response.data, absoluteMediaPlaylistUri);
|
|
1279
1381
|
|
|
1280
1382
|
if (playlist.type != shaka.hls.PlaylistType.MEDIA) {
|
|
1281
|
-
// EXT-X-MEDIA tags should point to media
|
|
1383
|
+
// EXT-X-MEDIA and EXT-X-IMAGE-STREAM-INF tags should point to media
|
|
1384
|
+
// playlists.
|
|
1282
1385
|
throw new shaka.util.Error(
|
|
1283
1386
|
shaka.util.Error.Severity.CRITICAL,
|
|
1284
1387
|
shaka.util.Error.Category.MANIFEST,
|
|
@@ -1635,11 +1738,12 @@ shaka.hls.HlsParser = class {
|
|
|
1635
1738
|
* @param {!Map.<string, string>} variables
|
|
1636
1739
|
* @param {string} absoluteMediaPlaylistUri
|
|
1637
1740
|
* @return {!shaka.media.SegmentReference}
|
|
1741
|
+
* @param {string} type
|
|
1638
1742
|
* @private
|
|
1639
1743
|
*/
|
|
1640
1744
|
createSegmentReference_(
|
|
1641
1745
|
initSegmentReference, previousReference, hlsSegment, startTime,
|
|
1642
|
-
timestampOffset, variables, absoluteMediaPlaylistUri) {
|
|
1746
|
+
timestampOffset, variables, absoluteMediaPlaylistUri, type) {
|
|
1643
1747
|
const tags = hlsSegment.tags;
|
|
1644
1748
|
const absoluteSegmentUri = this.variableSubstitution_(
|
|
1645
1749
|
hlsSegment.absoluteUri, variables);
|
|
@@ -1730,6 +1834,22 @@ shaka.hls.HlsParser = class {
|
|
|
1730
1834
|
endByte = partialSegmentRefs[partialSegmentRefs.length - 1].endByte;
|
|
1731
1835
|
}
|
|
1732
1836
|
|
|
1837
|
+
let tilesLayout = '';
|
|
1838
|
+
let tileDuration = null;
|
|
1839
|
+
if (type == shaka.util.ManifestParserUtils.ContentType.IMAGE) {
|
|
1840
|
+
// By default in HLS the tilesLayout is 1x1
|
|
1841
|
+
tilesLayout = '1x1';
|
|
1842
|
+
const tilesTag =
|
|
1843
|
+
shaka.hls.Utils.getFirstTagWithName(tags, 'EXT-X-TILES');
|
|
1844
|
+
if (tilesTag) {
|
|
1845
|
+
tilesLayout = tilesTag.getRequiredAttrValue('LAYOUT');
|
|
1846
|
+
const duration = tilesTag.getAttributeValue('DURATION');
|
|
1847
|
+
if (duration) {
|
|
1848
|
+
tileDuration = Number(duration);
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1733
1853
|
return new shaka.media.SegmentReference(
|
|
1734
1854
|
startTime,
|
|
1735
1855
|
endTime,
|
|
@@ -1741,6 +1861,8 @@ shaka.hls.HlsParser = class {
|
|
|
1741
1861
|
/* appendWindowStart= */ 0,
|
|
1742
1862
|
/* appendWindowEnd= */ Infinity,
|
|
1743
1863
|
partialSegmentRefs,
|
|
1864
|
+
tilesLayout,
|
|
1865
|
+
tileDuration,
|
|
1744
1866
|
);
|
|
1745
1867
|
}
|
|
1746
1868
|
|
|
@@ -1836,12 +1958,13 @@ shaka.hls.HlsParser = class {
|
|
|
1836
1958
|
initSegmentRef = this.getInitSegmentReference_(
|
|
1837
1959
|
playlist.absoluteUri, hlsSegments[0].tags, variables);
|
|
1838
1960
|
goog.asserts.assert(
|
|
1839
|
-
type != shaka.util.ManifestParserUtils.ContentType.TEXT
|
|
1961
|
+
type != shaka.util.ManifestParserUtils.ContentType.TEXT &&
|
|
1962
|
+
type != shaka.util.ManifestParserUtils.ContentType.IMAGE,
|
|
1840
1963
|
'Should only get start time from audio or video streams');
|
|
1841
1964
|
this.playlistStartTime_ = await this.getStartTime_(
|
|
1842
1965
|
verbatimMediaPlaylistUri, initSegmentRef, mimeType,
|
|
1843
1966
|
position, /* isDiscontinuity= */ false,
|
|
1844
|
-
hlsSegments[0], variables);
|
|
1967
|
+
hlsSegments[0], variables, type);
|
|
1845
1968
|
}
|
|
1846
1969
|
firstStartTime = this.playlistStartTime_;
|
|
1847
1970
|
}
|
|
@@ -1894,7 +2017,7 @@ shaka.hls.HlsParser = class {
|
|
|
1894
2017
|
// eslint-disable-next-line no-await-in-loop
|
|
1895
2018
|
timestampOffset = await this.getTimestampOffset_(
|
|
1896
2019
|
discontintuitySequenceNum, verbatimMediaPlaylistUri, initSegmentRef,
|
|
1897
|
-
mimeType, position, item, variables, startTime);
|
|
2020
|
+
mimeType, position, item, variables, startTime, type);
|
|
1898
2021
|
}
|
|
1899
2022
|
|
|
1900
2023
|
// If the stream is low latency and the user has not configured the
|
|
@@ -1919,7 +2042,8 @@ shaka.hls.HlsParser = class {
|
|
|
1919
2042
|
startTime,
|
|
1920
2043
|
timestampOffset,
|
|
1921
2044
|
variables,
|
|
1922
|
-
playlist.absoluteUri
|
|
2045
|
+
playlist.absoluteUri,
|
|
2046
|
+
type);
|
|
1923
2047
|
|
|
1924
2048
|
references.push(reference);
|
|
1925
2049
|
} else if (!this.lowLatencyMode_) {
|
|
@@ -1946,13 +2070,14 @@ shaka.hls.HlsParser = class {
|
|
|
1946
2070
|
* @param {!shaka.hls.Segment} segment
|
|
1947
2071
|
* @param {!Map.<string, string>} variables
|
|
1948
2072
|
* @param {number} startTime
|
|
2073
|
+
* @param {string} type
|
|
1949
2074
|
* @return {!Promise.<number>}
|
|
1950
2075
|
* @throws {shaka.util.Error}
|
|
1951
2076
|
* @private
|
|
1952
2077
|
*/
|
|
1953
2078
|
async getTimestampOffset_(discontintuitySequenceNum,
|
|
1954
2079
|
verbatimMediaPlaylistUri, initSegmentRef,
|
|
1955
|
-
mimeType, mediaSequenceNumber, segment, variables, startTime) {
|
|
2080
|
+
mimeType, mediaSequenceNumber, segment, variables, startTime, type) {
|
|
1956
2081
|
let timestampOffset = 0;
|
|
1957
2082
|
if (this.discontinuityToTso_.has(discontintuitySequenceNum)) {
|
|
1958
2083
|
timestampOffset =
|
|
@@ -1961,7 +2086,7 @@ shaka.hls.HlsParser = class {
|
|
|
1961
2086
|
const mediaStartTime = await this.getStartTime_(
|
|
1962
2087
|
verbatimMediaPlaylistUri, initSegmentRef, mimeType,
|
|
1963
2088
|
mediaSequenceNumber, /* isDiscontinuity= */ true, segment,
|
|
1964
|
-
variables);
|
|
2089
|
+
variables, type);
|
|
1965
2090
|
timestampOffset = startTime - mediaStartTime;
|
|
1966
2091
|
shaka.log.v1('Segment timestampOffset =', timestampOffset);
|
|
1967
2092
|
this.discontinuityToTso_.set(
|
|
@@ -2048,12 +2173,13 @@ shaka.hls.HlsParser = class {
|
|
|
2048
2173
|
* @param {boolean} isDiscontinuity
|
|
2049
2174
|
* @param {!shaka.hls.Segment} segment
|
|
2050
2175
|
* @param {!Map.<string, string>} variables
|
|
2176
|
+
* @param {string} type
|
|
2051
2177
|
* @return {!Promise.<number>}
|
|
2052
2178
|
* @private
|
|
2053
2179
|
*/
|
|
2054
2180
|
async getStartTime_(
|
|
2055
2181
|
verbatimMediaPlaylistUri, initSegmentRef, mimeType, mediaSequenceNumber,
|
|
2056
|
-
isDiscontinuity, segment, variables) {
|
|
2182
|
+
isDiscontinuity, segment, variables, type) {
|
|
2057
2183
|
const segmentRef = this.createSegmentReference_(
|
|
2058
2184
|
initSegmentRef,
|
|
2059
2185
|
/* previousReference= */ null,
|
|
@@ -2061,7 +2187,8 @@ shaka.hls.HlsParser = class {
|
|
|
2061
2187
|
/* startTime= */ 0,
|
|
2062
2188
|
/* timestampOffset= */ 0,
|
|
2063
2189
|
variables,
|
|
2064
|
-
/* absoluteMediaPlaylistUri= */ ''
|
|
2190
|
+
/* absoluteMediaPlaylistUri= */ '',
|
|
2191
|
+
type);
|
|
2065
2192
|
// If we are updating the manifest, we can usually skip fetching the segment
|
|
2066
2193
|
// by examining the references we already have. This won't be possible if
|
|
2067
2194
|
// there was some kind of lag or delay updating the manifest on the server,
|
|
@@ -2437,6 +2564,12 @@ shaka.hls.HlsParser = class {
|
|
|
2437
2564
|
}
|
|
2438
2565
|
}
|
|
2439
2566
|
|
|
2567
|
+
if (contentType == ContentType.IMAGE) {
|
|
2568
|
+
if (!codecs || codecs == 'jpeg') {
|
|
2569
|
+
return 'image/jpeg';
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2440
2573
|
// If unable to guess mime type, request a segment and try getting it
|
|
2441
2574
|
// from the response.
|
|
2442
2575
|
const headRequest = shaka.net.NetworkingEngine.makeRequest(
|
|
@@ -2807,6 +2940,19 @@ shaka.hls.HlsParser.TEXT_EXTENSIONS_TO_MIME_TYPES_ = {
|
|
|
2807
2940
|
};
|
|
2808
2941
|
|
|
2809
2942
|
|
|
2943
|
+
/**
|
|
2944
|
+
* @const {!Object.<string, string>}
|
|
2945
|
+
* @private
|
|
2946
|
+
*/
|
|
2947
|
+
shaka.hls.HlsParser.IMAGE_EXTENSIONS_TO_MIME_TYPES_ = {
|
|
2948
|
+
'jpg': 'image/jpeg',
|
|
2949
|
+
'png': 'image/png',
|
|
2950
|
+
'svg': 'image/svg+xml',
|
|
2951
|
+
'webp': 'image/webp',
|
|
2952
|
+
'avif': 'image/avif',
|
|
2953
|
+
};
|
|
2954
|
+
|
|
2955
|
+
|
|
2810
2956
|
/**
|
|
2811
2957
|
* @const {!Object.<string, !Object.<string, string>>}
|
|
2812
2958
|
* @private
|
|
@@ -2815,6 +2961,7 @@ shaka.hls.HlsParser.EXTENSION_MAP_BY_CONTENT_TYPE_ = {
|
|
|
2815
2961
|
'audio': shaka.hls.HlsParser.AUDIO_EXTENSIONS_TO_MIME_TYPES_,
|
|
2816
2962
|
'video': shaka.hls.HlsParser.VIDEO_EXTENSIONS_TO_MIME_TYPES_,
|
|
2817
2963
|
'text': shaka.hls.HlsParser.TEXT_EXTENSIONS_TO_MIME_TYPES_,
|
|
2964
|
+
'image': shaka.hls.HlsParser.IMAGE_EXTENSIONS_TO_MIME_TYPES_,
|
|
2818
2965
|
};
|
|
2819
2966
|
|
|
2820
2967
|
|
|
@@ -4,14 +4,13 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
goog.provide('shaka.media.IClosedCaptionParser');
|
|
8
7
|
goog.provide('shaka.media.ClosedCaptionParser');
|
|
8
|
+
goog.provide('shaka.media.IClosedCaptionParser');
|
|
9
9
|
|
|
10
10
|
goog.require('shaka.cea.CeaDecoder');
|
|
11
11
|
goog.require('shaka.cea.Mp4CeaParser');
|
|
12
12
|
goog.require('shaka.util.BufferUtils');
|
|
13
13
|
goog.requireType('shaka.cea.ICaptionDecoder');
|
|
14
|
-
goog.requireType('shaka.cea.ICaptionDecoder.ClosedCaption');
|
|
15
14
|
goog.requireType('shaka.cea.ICeaParser');
|
|
16
15
|
|
|
17
16
|
|