shaka-player 4.4.1 → 4.4.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/workflows/deflake.yaml +8 -5
- package/.github/workflows/release-please.yaml +47 -7
- package/.github/workflows/shaka-bot-commands/command-test.sh +1 -1
- package/CHANGELOG.md +20 -0
- package/CONTRIBUTORS +1 -0
- package/demo/custom.js +5 -5
- package/dist/demo.compiled.debug.js +3 -3
- package/dist/demo.compiled.debug.map +1 -1
- package/dist/demo.compiled.js +3 -3
- package/dist/demo.compiled.map +1 -1
- package/dist/deps.js +2 -2
- package/dist/locales.js +31 -31
- package/dist/shaka-player.compiled.d.ts +311 -295
- package/dist/shaka-player.compiled.debug.d.ts +311 -295
- package/dist/shaka-player.compiled.debug.externs.js +37 -0
- package/dist/shaka-player.compiled.debug.js +145 -135
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +37 -0
- package/dist/shaka-player.compiled.js +887 -884
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +355 -339
- package/dist/shaka-player.ui.debug.d.ts +355 -339
- package/dist/shaka-player.ui.debug.externs.js +37 -0
- package/dist/shaka-player.ui.debug.js +161 -151
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +37 -0
- package/dist/shaka-player.ui.js +968 -967
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/fairplay.md +8 -0
- package/docs/tutorials/faq.md +1 -1
- package/externs/shaka/text.js +7 -0
- package/lib/dash/segment_template.js +3 -0
- package/lib/hls/hls_parser.js +8 -4
- package/lib/media/media_source_engine.js +1 -1
- package/lib/player.js +8 -57
- package/lib/text/lrc_text_parser.js +8 -0
- package/lib/text/mp4_ttml_parser.js +8 -0
- package/lib/text/mp4_vtt_parser.js +8 -0
- package/lib/text/sbv_text_parser.js +8 -0
- package/lib/text/srt_text_parser.js +8 -0
- package/lib/text/ssa_text_parser.js +8 -0
- package/lib/text/text_engine.js +8 -1
- package/lib/text/ttml_text_parser.js +10 -1
- package/lib/text/ui_text_displayer.js +49 -3
- package/lib/text/vtt_text_parser.js +21 -5
- package/lib/transmuxer/ts_transmuxer.js +44 -43
- package/lib/util/error.js +16 -10
- package/lib/util/player_configuration.js +2 -2
- package/lib/util/stream_utils.js +13 -11
- package/lib/util/ts_parser.js +70 -13
- package/package.json +2 -2
- package/test/text/text_engine_unit.js +4 -2
- package/test/text/ttml_text_parser_unit.js +3 -4
- package/test/text/vtt_text_parser_unit.js +17 -2
- package/test/text/web_vtt_layout_integration.js +0 -1
- package/ui/language_utils.js +0 -15
|
@@ -170,17 +170,7 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
170
170
|
|
|
171
171
|
const uint8ArrayData = shaka.util.BufferUtils.toUint8(data);
|
|
172
172
|
|
|
173
|
-
let timestamp = reference.endTime * 1000;
|
|
174
|
-
|
|
175
173
|
const tsParser = this.tsParser_.parse(uint8ArrayData);
|
|
176
|
-
const startTime = tsParser.getStartTime();
|
|
177
|
-
|
|
178
|
-
if (startTime.audio != null) {
|
|
179
|
-
timestamp = startTime.audio;
|
|
180
|
-
}
|
|
181
|
-
if (startTime.video != null) {
|
|
182
|
-
timestamp = startTime.video;
|
|
183
|
-
}
|
|
184
174
|
const streamInfos = [];
|
|
185
175
|
const codecs = tsParser.getCodecs();
|
|
186
176
|
try {
|
|
@@ -189,8 +179,7 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
189
179
|
switch (codecs.video) {
|
|
190
180
|
case 'avc':
|
|
191
181
|
streamInfo =
|
|
192
|
-
this.getAvcStreamInfo_(
|
|
193
|
-
tsParser, timestamp, stream, duration);
|
|
182
|
+
this.getAvcStreamInfo_(tsParser, stream, duration);
|
|
194
183
|
break;
|
|
195
184
|
}
|
|
196
185
|
if (streamInfo) {
|
|
@@ -202,23 +191,19 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
202
191
|
switch (codecs.audio) {
|
|
203
192
|
case 'aac':
|
|
204
193
|
streamInfo =
|
|
205
|
-
this.getAacStreamInfo_(
|
|
206
|
-
tsParser, timestamp, stream, duration);
|
|
194
|
+
this.getAacStreamInfo_(tsParser, stream, duration);
|
|
207
195
|
break;
|
|
208
196
|
case 'ac3':
|
|
209
197
|
streamInfo =
|
|
210
|
-
this.getAc3StreamInfo_(
|
|
211
|
-
tsParser, timestamp, stream, duration);
|
|
198
|
+
this.getAc3StreamInfo_(tsParser, stream, duration);
|
|
212
199
|
break;
|
|
213
200
|
case 'ec3':
|
|
214
201
|
streamInfo =
|
|
215
|
-
this.getEc3StreamInfo_(
|
|
216
|
-
tsParser, timestamp, stream, duration);
|
|
202
|
+
this.getEc3StreamInfo_(tsParser, stream, duration);
|
|
217
203
|
break;
|
|
218
204
|
case 'mp3':
|
|
219
205
|
streamInfo =
|
|
220
|
-
this.getMp3StreamInfo_(
|
|
221
|
-
tsParser, timestamp, stream, duration);
|
|
206
|
+
this.getMp3StreamInfo_(tsParser, stream, duration);
|
|
222
207
|
break;
|
|
223
208
|
}
|
|
224
209
|
if (streamInfo) {
|
|
@@ -255,20 +240,22 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
255
240
|
|
|
256
241
|
/**
|
|
257
242
|
* @param {shaka.util.TsParser} tsParser
|
|
258
|
-
* @param {number} timestamp
|
|
259
243
|
* @param {shaka.extern.Stream} stream
|
|
260
244
|
* @param {number} duration
|
|
261
245
|
* @return {shaka.util.Mp4Generator.StreamInfo}
|
|
262
246
|
* @private
|
|
263
247
|
*/
|
|
264
|
-
getAacStreamInfo_(tsParser,
|
|
248
|
+
getAacStreamInfo_(tsParser, stream, duration) {
|
|
265
249
|
const ADTS = shaka.transmuxer.ADTS;
|
|
250
|
+
const timescale = shaka.util.TsParser.Timescale;
|
|
266
251
|
|
|
267
252
|
/** @type {!Array.<shaka.util.Mp4Generator.Mp4Sample>} */
|
|
268
253
|
const samples = [];
|
|
269
254
|
|
|
270
255
|
let info;
|
|
271
256
|
|
|
257
|
+
let firstPts = null;
|
|
258
|
+
|
|
272
259
|
for (const audioData of tsParser.getAudioData()) {
|
|
273
260
|
const data = audioData.data;
|
|
274
261
|
if (!data) {
|
|
@@ -284,7 +271,9 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
284
271
|
}
|
|
285
272
|
stream.audioSamplingRate = info.sampleRate;
|
|
286
273
|
stream.channelsCount = info.channelCount;
|
|
287
|
-
|
|
274
|
+
if (firstPts == null && audioData.pts !== null) {
|
|
275
|
+
firstPts = audioData.pts;
|
|
276
|
+
}
|
|
288
277
|
while (offset < data.length) {
|
|
289
278
|
const header = ADTS.parseHeader(data, offset);
|
|
290
279
|
if (!header) {
|
|
@@ -316,7 +305,7 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
316
305
|
}
|
|
317
306
|
}
|
|
318
307
|
|
|
319
|
-
if (!info) {
|
|
308
|
+
if (!info || firstPts == null) {
|
|
320
309
|
throw new shaka.util.Error(
|
|
321
310
|
shaka.util.Error.Severity.CRITICAL,
|
|
322
311
|
shaka.util.Error.Category.MEDIA,
|
|
@@ -326,7 +315,7 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
326
315
|
/** @type {number} */
|
|
327
316
|
const sampleRate = info.sampleRate;
|
|
328
317
|
/** @type {number} */
|
|
329
|
-
const baseMediaDecodeTime =
|
|
318
|
+
const baseMediaDecodeTime = firstPts / timescale * sampleRate;
|
|
330
319
|
|
|
331
320
|
return {
|
|
332
321
|
id: stream.id,
|
|
@@ -350,14 +339,14 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
350
339
|
|
|
351
340
|
/**
|
|
352
341
|
* @param {shaka.util.TsParser} tsParser
|
|
353
|
-
* @param {number} timestamp
|
|
354
342
|
* @param {shaka.extern.Stream} stream
|
|
355
343
|
* @param {number} duration
|
|
356
344
|
* @return {shaka.util.Mp4Generator.StreamInfo}
|
|
357
345
|
* @private
|
|
358
346
|
*/
|
|
359
|
-
getAc3StreamInfo_(tsParser,
|
|
347
|
+
getAc3StreamInfo_(tsParser, stream, duration) {
|
|
360
348
|
const Ac3 = shaka.transmuxer.Ac3;
|
|
349
|
+
const timescale = shaka.util.TsParser.Timescale;
|
|
361
350
|
|
|
362
351
|
/** @type {!Array.<shaka.util.Mp4Generator.Mp4Sample>} */
|
|
363
352
|
const samples = [];
|
|
@@ -368,8 +357,13 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
368
357
|
/** @type {!Uint8Array} */
|
|
369
358
|
let audioConfig = new Uint8Array([]);
|
|
370
359
|
|
|
360
|
+
let firstPts = null;
|
|
361
|
+
|
|
371
362
|
for (const audioData of tsParser.getAudioData()) {
|
|
372
363
|
const data = audioData.data;
|
|
364
|
+
if (firstPts == null && audioData.pts !== null) {
|
|
365
|
+
firstPts = audioData.pts;
|
|
366
|
+
}
|
|
373
367
|
let offset = 0;
|
|
374
368
|
while (offset < data.length) {
|
|
375
369
|
const frame = Ac3.parseFrame(data, offset);
|
|
@@ -403,7 +397,7 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
403
397
|
}
|
|
404
398
|
}
|
|
405
399
|
|
|
406
|
-
if (sampleRate == 0 || audioConfig.byteLength == 0) {
|
|
400
|
+
if (sampleRate == 0 || audioConfig.byteLength == 0 || firstPts == null) {
|
|
407
401
|
throw new shaka.util.Error(
|
|
408
402
|
shaka.util.Error.Severity.CRITICAL,
|
|
409
403
|
shaka.util.Error.Category.MEDIA,
|
|
@@ -412,7 +406,7 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
412
406
|
|
|
413
407
|
|
|
414
408
|
/** @type {number} */
|
|
415
|
-
const baseMediaDecodeTime =
|
|
409
|
+
const baseMediaDecodeTime = firstPts / timescale * sampleRate;
|
|
416
410
|
|
|
417
411
|
return {
|
|
418
412
|
id: stream.id,
|
|
@@ -436,14 +430,14 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
436
430
|
|
|
437
431
|
/**
|
|
438
432
|
* @param {shaka.util.TsParser} tsParser
|
|
439
|
-
* @param {number} timestamp
|
|
440
433
|
* @param {shaka.extern.Stream} stream
|
|
441
434
|
* @param {number} duration
|
|
442
435
|
* @return {shaka.util.Mp4Generator.StreamInfo}
|
|
443
436
|
* @private
|
|
444
437
|
*/
|
|
445
|
-
getEc3StreamInfo_(tsParser,
|
|
438
|
+
getEc3StreamInfo_(tsParser, stream, duration) {
|
|
446
439
|
const Ec3 = shaka.transmuxer.Ec3;
|
|
440
|
+
const timescale = shaka.util.TsParser.Timescale;
|
|
447
441
|
|
|
448
442
|
/** @type {!Array.<shaka.util.Mp4Generator.Mp4Sample>} */
|
|
449
443
|
const samples = [];
|
|
@@ -454,8 +448,13 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
454
448
|
/** @type {!Uint8Array} */
|
|
455
449
|
let audioConfig = new Uint8Array([]);
|
|
456
450
|
|
|
451
|
+
let firstPts = null;
|
|
452
|
+
|
|
457
453
|
for (const audioData of tsParser.getAudioData()) {
|
|
458
454
|
const data = audioData.data;
|
|
455
|
+
if (firstPts == null && audioData.pts !== null) {
|
|
456
|
+
firstPts = audioData.pts;
|
|
457
|
+
}
|
|
459
458
|
let offset = 0;
|
|
460
459
|
while (offset < data.length) {
|
|
461
460
|
const frame = Ec3.parseFrame(data, offset);
|
|
@@ -489,7 +488,7 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
489
488
|
}
|
|
490
489
|
}
|
|
491
490
|
|
|
492
|
-
if (sampleRate == 0 || audioConfig.byteLength == 0) {
|
|
491
|
+
if (sampleRate == 0 || audioConfig.byteLength == 0 || firstPts == null) {
|
|
493
492
|
throw new shaka.util.Error(
|
|
494
493
|
shaka.util.Error.Severity.CRITICAL,
|
|
495
494
|
shaka.util.Error.Category.MEDIA,
|
|
@@ -498,7 +497,7 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
498
497
|
|
|
499
498
|
|
|
500
499
|
/** @type {number} */
|
|
501
|
-
const baseMediaDecodeTime =
|
|
500
|
+
const baseMediaDecodeTime = firstPts / timescale * sampleRate;
|
|
502
501
|
|
|
503
502
|
return {
|
|
504
503
|
id: stream.id,
|
|
@@ -522,25 +521,30 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
522
521
|
|
|
523
522
|
/**
|
|
524
523
|
* @param {shaka.util.TsParser} tsParser
|
|
525
|
-
* @param {number} timestamp
|
|
526
524
|
* @param {shaka.extern.Stream} stream
|
|
527
525
|
* @param {number} duration
|
|
528
526
|
* @return {shaka.util.Mp4Generator.StreamInfo}
|
|
529
527
|
* @private
|
|
530
528
|
*/
|
|
531
|
-
getMp3StreamInfo_(tsParser,
|
|
529
|
+
getMp3StreamInfo_(tsParser, stream, duration) {
|
|
532
530
|
const MpegAudio = shaka.transmuxer.MpegAudio;
|
|
531
|
+
const timescale = shaka.util.TsParser.Timescale;
|
|
533
532
|
|
|
534
533
|
/** @type {!Array.<shaka.util.Mp4Generator.Mp4Sample>} */
|
|
535
534
|
const samples = [];
|
|
536
535
|
|
|
537
536
|
let firstHeader;
|
|
538
537
|
|
|
538
|
+
let firstPts = null;
|
|
539
|
+
|
|
539
540
|
for (const audioData of tsParser.getAudioData()) {
|
|
540
541
|
const data = audioData.data;
|
|
541
542
|
if (!data) {
|
|
542
543
|
continue;
|
|
543
544
|
}
|
|
545
|
+
if (firstPts == null && audioData.pts !== null) {
|
|
546
|
+
firstPts = audioData.pts;
|
|
547
|
+
}
|
|
544
548
|
let offset = 0;
|
|
545
549
|
while (offset < data.length) {
|
|
546
550
|
const header = MpegAudio.parseHeader(data, offset);
|
|
@@ -570,7 +574,7 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
570
574
|
offset += header.frameLength;
|
|
571
575
|
}
|
|
572
576
|
}
|
|
573
|
-
if (!firstHeader) {
|
|
577
|
+
if (!firstHeader || firstPts == null) {
|
|
574
578
|
throw new shaka.util.Error(
|
|
575
579
|
shaka.util.Error.Severity.CRITICAL,
|
|
576
580
|
shaka.util.Error.Category.MEDIA,
|
|
@@ -579,7 +583,7 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
579
583
|
/** @type {number} */
|
|
580
584
|
const sampleRate = firstHeader.sampleRate;
|
|
581
585
|
/** @type {number} */
|
|
582
|
-
const baseMediaDecodeTime =
|
|
586
|
+
const baseMediaDecodeTime = firstPts / timescale * sampleRate;
|
|
583
587
|
|
|
584
588
|
return {
|
|
585
589
|
id: stream.id,
|
|
@@ -603,13 +607,12 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
603
607
|
|
|
604
608
|
/**
|
|
605
609
|
* @param {shaka.util.TsParser} tsParser
|
|
606
|
-
* @param {number} timestamp
|
|
607
610
|
* @param {shaka.extern.Stream} stream
|
|
608
611
|
* @param {number} duration
|
|
609
612
|
* @return {shaka.util.Mp4Generator.StreamInfo}
|
|
610
613
|
* @private
|
|
611
614
|
*/
|
|
612
|
-
getAvcStreamInfo_(tsParser,
|
|
615
|
+
getAvcStreamInfo_(tsParser, stream, duration) {
|
|
613
616
|
const H264 = shaka.transmuxer.H264;
|
|
614
617
|
const timescale = shaka.util.TsParser.Timescale;
|
|
615
618
|
|
|
@@ -620,9 +623,7 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
620
623
|
let baseMediaDecodeTime = null;
|
|
621
624
|
|
|
622
625
|
let nalus = [];
|
|
623
|
-
const videoData = tsParser.getVideoData()
|
|
624
|
-
return pes.pts != null && pes.dts != null;
|
|
625
|
-
});
|
|
626
|
+
const videoData = tsParser.getVideoData();
|
|
626
627
|
for (let i = 0; i < videoData.length; i++) {
|
|
627
628
|
const pes = videoData[i];
|
|
628
629
|
let nextPes;
|
|
@@ -635,7 +636,7 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
635
636
|
if (!frame) {
|
|
636
637
|
continue;
|
|
637
638
|
}
|
|
638
|
-
if (baseMediaDecodeTime == null
|
|
639
|
+
if (baseMediaDecodeTime == null) {
|
|
639
640
|
baseMediaDecodeTime = pes.dts;
|
|
640
641
|
}
|
|
641
642
|
let duration;
|
package/lib/util/error.js
CHANGED
|
@@ -63,8 +63,11 @@ shaka.util.Error = class {
|
|
|
63
63
|
*/
|
|
64
64
|
this.handled = false;
|
|
65
65
|
|
|
66
|
-
//
|
|
66
|
+
// A basic message for compiled mode.
|
|
67
|
+
let formattedMessage = 'Shaka Error ' + this.code;
|
|
67
68
|
if (goog.DEBUG) {
|
|
69
|
+
// This improves the formatting of Errors in failure messages in the
|
|
70
|
+
// tests in debug mode.
|
|
68
71
|
let categoryName = 'UNKNOWN';
|
|
69
72
|
let codeName = 'UNKNOWN';
|
|
70
73
|
|
|
@@ -79,17 +82,20 @@ shaka.util.Error = class {
|
|
|
79
82
|
}
|
|
80
83
|
}
|
|
81
84
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
* <i>(Only available in uncompiled mode.)</i>
|
|
85
|
-
*
|
|
86
|
-
* @const {string}
|
|
87
|
-
* @exportDoc
|
|
88
|
-
*/
|
|
89
|
-
this.message = 'Shaka Error ' + categoryName + '.' + codeName +
|
|
90
|
-
' (' + this.data.toString() + ')';
|
|
85
|
+
formattedMessage = 'Shaka Error ' + categoryName + '.' + codeName +
|
|
86
|
+
' (' + this.data.toString() + ')';
|
|
91
87
|
}
|
|
92
88
|
|
|
89
|
+
/**
|
|
90
|
+
* In compiled mode, contains a basic message string with the error code.
|
|
91
|
+
* In uncompiled and debug modes, contains a human-readable version of the
|
|
92
|
+
* category and code as enums.
|
|
93
|
+
*
|
|
94
|
+
* @const {string}
|
|
95
|
+
* @exportDoc
|
|
96
|
+
*/
|
|
97
|
+
this.message = formattedMessage;
|
|
98
|
+
|
|
93
99
|
if (shaka.util.Error.createStack) {
|
|
94
100
|
try {
|
|
95
101
|
throw new Error(this.message || 'Shaka Error');
|
|
@@ -71,8 +71,8 @@ shaka.util.PlayerConfiguration = class {
|
|
|
71
71
|
persistentSessionOnlinePlayback: false,
|
|
72
72
|
persistentSessionsMetadata: [],
|
|
73
73
|
initDataTransform: (initData, initDataType, drmInfo) => {
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
if (shaka.util.Platform.isMediaKeysPolyfilled() &&
|
|
75
|
+
initDataType == 'skd') {
|
|
76
76
|
const cert = drmInfo.serverCertificate;
|
|
77
77
|
const contentId =
|
|
78
78
|
shaka.util.FairPlayUtils.defaultGetContentId(initData);
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -1543,22 +1543,23 @@ shaka.util.StreamUtils = class {
|
|
|
1543
1543
|
|
|
1544
1544
|
/**
|
|
1545
1545
|
* Chooses streams according to the given config.
|
|
1546
|
+
* Works both for Stream and Track types due to their similarities.
|
|
1546
1547
|
*
|
|
1547
|
-
* @param {!Array
|
|
1548
|
+
* @param {!Array<!shaka.extern.Stream>|!Array<!shaka.extern.Track>} streams
|
|
1548
1549
|
* @param {string} preferredLanguage
|
|
1549
1550
|
* @param {string} preferredRole
|
|
1550
1551
|
* @param {boolean} preferredForced
|
|
1551
|
-
* @return {!Array
|
|
1552
|
+
* @return {!Array<!shaka.extern.Stream>|!Array<!shaka.extern.Track>}
|
|
1552
1553
|
*/
|
|
1553
1554
|
static filterStreamsByLanguageAndRole(
|
|
1554
1555
|
streams, preferredLanguage, preferredRole, preferredForced) {
|
|
1555
1556
|
const LanguageUtils = shaka.util.LanguageUtils;
|
|
1556
1557
|
|
|
1557
|
-
/** @type {!Array
|
|
1558
|
+
/** @type {!Array<!shaka.extern.Stream>|!Array<!shaka.extern.Track>} */
|
|
1558
1559
|
let chosen = streams;
|
|
1559
1560
|
|
|
1560
1561
|
// Start with the set of primary streams.
|
|
1561
|
-
/** @type {!Array
|
|
1562
|
+
/** @type {!Array<!shaka.extern.Stream>|!Array<!shaka.extern.Track>} */
|
|
1562
1563
|
const primary = streams.filter((stream) => {
|
|
1563
1564
|
return stream.primary;
|
|
1564
1565
|
});
|
|
@@ -1598,7 +1599,7 @@ shaka.util.StreamUtils = class {
|
|
|
1598
1599
|
|
|
1599
1600
|
// Now refine the choice based on role preference.
|
|
1600
1601
|
if (preferredRole) {
|
|
1601
|
-
const roleMatches = shaka.util.StreamUtils.
|
|
1602
|
+
const roleMatches = shaka.util.StreamUtils.filterStreamsByRole_(
|
|
1602
1603
|
chosen, preferredRole);
|
|
1603
1604
|
if (roleMatches.length) {
|
|
1604
1605
|
return roleMatches;
|
|
@@ -1626,20 +1627,21 @@ shaka.util.StreamUtils = class {
|
|
|
1626
1627
|
if (!allRoles.length) {
|
|
1627
1628
|
return chosen;
|
|
1628
1629
|
}
|
|
1629
|
-
return shaka.util.StreamUtils.
|
|
1630
|
+
return shaka.util.StreamUtils.filterStreamsByRole_(chosen, allRoles[0]);
|
|
1630
1631
|
}
|
|
1631
1632
|
|
|
1632
1633
|
|
|
1633
1634
|
/**
|
|
1634
|
-
* Filter
|
|
1635
|
+
* Filter Streams by role.
|
|
1636
|
+
* Works both for Stream and Track types due to their similarities.
|
|
1635
1637
|
*
|
|
1636
|
-
* @param {!Array
|
|
1638
|
+
* @param {!Array<!shaka.extern.Stream>|!Array<!shaka.extern.Track>} streams
|
|
1637
1639
|
* @param {string} preferredRole
|
|
1638
|
-
* @return {!Array
|
|
1640
|
+
* @return {!Array<!shaka.extern.Stream>|!Array<!shaka.extern.Track>}
|
|
1639
1641
|
* @private
|
|
1640
1642
|
*/
|
|
1641
|
-
static
|
|
1642
|
-
return
|
|
1643
|
+
static filterStreamsByRole_(streams, preferredRole) {
|
|
1644
|
+
return streams.filter((stream) => {
|
|
1643
1645
|
return stream.roles.includes(preferredRole);
|
|
1644
1646
|
});
|
|
1645
1647
|
}
|
package/lib/util/ts_parser.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
goog.provide('shaka.util.TsParser');
|
|
8
8
|
|
|
9
|
+
goog.require('shaka.Deprecate');
|
|
9
10
|
goog.require('shaka.log');
|
|
10
11
|
goog.require('shaka.util.ExpGolomb');
|
|
11
12
|
goog.require('shaka.util.Id3Utils');
|
|
@@ -361,6 +362,12 @@ shaka.util.TsParser = class {
|
|
|
361
362
|
dts: null,
|
|
362
363
|
};
|
|
363
364
|
|
|
365
|
+
// if PES parsed length is not zero and greater than total received length,
|
|
366
|
+
// stop parsing. PES might be truncated. minus 6 : PES header size
|
|
367
|
+
if (pes.packetLength && pes.packetLength > data.byteLength - 6) {
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
|
|
364
371
|
// PES packets may be annotated with a PTS value, or a PTS value
|
|
365
372
|
// and a DTS value. Determine what combination of values is
|
|
366
373
|
// available to work with.
|
|
@@ -395,10 +402,15 @@ shaka.util.TsParser = class {
|
|
|
395
402
|
(data[18] & 0xfe) / 2;
|
|
396
403
|
}
|
|
397
404
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
//
|
|
401
|
-
|
|
405
|
+
|
|
406
|
+
const pesHdrLen = data[8];
|
|
407
|
+
// 9 bytes : 6 bytes for PES header + 3 bytes for PES extension
|
|
408
|
+
const payloadStartOffset = pesHdrLen + 9;
|
|
409
|
+
if (data.byteLength <= payloadStartOffset) {
|
|
410
|
+
return null;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
pes.data = data.subarray(payloadStartOffset);
|
|
402
414
|
|
|
403
415
|
return pes;
|
|
404
416
|
}
|
|
@@ -450,6 +462,10 @@ shaka.util.TsParser = class {
|
|
|
450
462
|
// previous Nalu and don't scan for more nalus.
|
|
451
463
|
const startCodeSize = numZeros > 3 ? 3 : numZeros;
|
|
452
464
|
const lastByteToKeep = i - startCodeSize;
|
|
465
|
+
// Optimization
|
|
466
|
+
if (lastByteToKeep == 0) {
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
453
469
|
infoOfLastNalu.data = shaka.util.Uint8ArrayUtils.concat(
|
|
454
470
|
infoOfLastNalu.data, data.subarray(0, lastByteToKeep));
|
|
455
471
|
infoOfLastNalu.fullData = shaka.util.Uint8ArrayUtils.concat(
|
|
@@ -575,11 +591,21 @@ shaka.util.TsParser = class {
|
|
|
575
591
|
* @export
|
|
576
592
|
*/
|
|
577
593
|
getVideoData() {
|
|
594
|
+
const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
|
|
578
595
|
const video = [];
|
|
579
596
|
for (const videoData of this.videoData_) {
|
|
580
597
|
const pes = this.parsePES_(videoData);
|
|
581
|
-
if (pes) {
|
|
598
|
+
if (pes && pes.pts != null && pes.dts != null) {
|
|
582
599
|
video.push(pes);
|
|
600
|
+
} else if (video.length) {
|
|
601
|
+
const data = pes ? pes.data : videoData;
|
|
602
|
+
if (!data) {
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
const previousPes = video.pop();
|
|
606
|
+
previousPes.data =
|
|
607
|
+
Uint8ArrayUtils.concat(previousPes.data, data);
|
|
608
|
+
video.push(previousPes);
|
|
583
609
|
}
|
|
584
610
|
}
|
|
585
611
|
return video;
|
|
@@ -660,29 +686,47 @@ shaka.util.TsParser = class {
|
|
|
660
686
|
* @export
|
|
661
687
|
*/
|
|
662
688
|
getVideoResolution() {
|
|
689
|
+
shaka.Deprecate.deprecateFeature(5,
|
|
690
|
+
'TsParser',
|
|
691
|
+
'Please use getVideoInfo function instead.');
|
|
692
|
+
const videoInfo = this.getVideoInfo();
|
|
693
|
+
return {
|
|
694
|
+
height: videoInfo.height,
|
|
695
|
+
width: videoInfo.width,
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Return the video information
|
|
701
|
+
*
|
|
702
|
+
* @return {{height: ?string, width: ?string, codec: ?string}}
|
|
703
|
+
* @export
|
|
704
|
+
*/
|
|
705
|
+
getVideoInfo() {
|
|
663
706
|
const TsParser = shaka.util.TsParser;
|
|
664
|
-
const
|
|
707
|
+
const videoInfo = {
|
|
665
708
|
height: null,
|
|
666
709
|
width: null,
|
|
710
|
+
codec: null,
|
|
667
711
|
};
|
|
668
712
|
const videoNalus = this.getVideoNalus();
|
|
669
713
|
if (!videoNalus.length) {
|
|
670
|
-
return
|
|
714
|
+
return videoInfo;
|
|
671
715
|
}
|
|
672
716
|
const spsNalu = videoNalus.find((nalu) => {
|
|
673
717
|
return nalu.type == TsParser.H264_NALU_TYPE_SPS_;
|
|
674
718
|
});
|
|
675
719
|
if (!spsNalu) {
|
|
676
|
-
return
|
|
720
|
+
return videoInfo;
|
|
677
721
|
}
|
|
678
722
|
|
|
679
723
|
const expGolombDecoder = new shaka.util.ExpGolomb(spsNalu.data);
|
|
680
724
|
// profile_idc
|
|
681
725
|
const profileIdc = expGolombDecoder.readUnsignedByte();
|
|
682
726
|
// constraint_set[0-5]_flag
|
|
683
|
-
expGolombDecoder.readUnsignedByte();
|
|
727
|
+
const profileCompatibility = expGolombDecoder.readUnsignedByte();
|
|
684
728
|
// level_idc u(8)
|
|
685
|
-
expGolombDecoder.readUnsignedByte();
|
|
729
|
+
const levelIdc = expGolombDecoder.readUnsignedByte();
|
|
686
730
|
// seq_parameter_set_id
|
|
687
731
|
expGolombDecoder.skipExpGolomb();
|
|
688
732
|
|
|
@@ -768,13 +812,26 @@ shaka.util.TsParser = class {
|
|
|
768
812
|
frameCropBottomOffset = expGolombDecoder.readUnsignedExpGolomb();
|
|
769
813
|
}
|
|
770
814
|
|
|
771
|
-
|
|
815
|
+
videoInfo.height = String(((2 - frameMbsOnlyFlag) *
|
|
772
816
|
(picHeightInMapUnitsMinus1 + 1) * 16) - (frameCropTopOffset * 2) -
|
|
773
817
|
(frameCropBottomOffset * 2));
|
|
774
|
-
|
|
818
|
+
videoInfo.width = String(((picWidthInMbsMinus1 + 1) * 16) -
|
|
775
819
|
frameCropLeftOffset * 2 - frameCropRightOffset * 2);
|
|
820
|
+
videoInfo.codec = 'avc1.' + this.byteToHex_(profileIdc) +
|
|
821
|
+
this.byteToHex_(profileCompatibility) + this.byteToHex_(levelIdc);
|
|
776
822
|
|
|
777
|
-
return
|
|
823
|
+
return videoInfo;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Convert a byte to 2 digits of hex. (Only handles values 0-255.)
|
|
828
|
+
*
|
|
829
|
+
* @param {number} x
|
|
830
|
+
* @return {string}
|
|
831
|
+
* @private
|
|
832
|
+
*/
|
|
833
|
+
byteToHex_(x) {
|
|
834
|
+
return ('0' + x.toString(16).toUpperCase()).slice(-2);
|
|
778
835
|
}
|
|
779
836
|
|
|
780
837
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shaka-player",
|
|
3
3
|
"description": "DASH/EME video player library",
|
|
4
|
-
"version": "4.4.
|
|
4
|
+
"version": "4.4.2",
|
|
5
5
|
"homepage": "https://github.com/shaka-project/shaka-player",
|
|
6
6
|
"author": "Google",
|
|
7
7
|
"maintainers": [
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"karma-coverage": "^2.2.0",
|
|
44
44
|
"karma-jasmine": "^4.0.1",
|
|
45
45
|
"karma-jasmine-ajax": "^0.1.13",
|
|
46
|
-
"karma-local-wd-launcher": "^1.6.
|
|
46
|
+
"karma-local-wd-launcher": "^1.6.6",
|
|
47
47
|
"karma-opera-launcher": "^1.0.0",
|
|
48
48
|
"karma-sourcemap-loader": "^0.3.8",
|
|
49
49
|
"karma-spec-reporter": "^0.0.34",
|
|
@@ -49,7 +49,8 @@ describe('TextEngine', () => {
|
|
|
49
49
|
textEngine.initParser(
|
|
50
50
|
dummyMimeType,
|
|
51
51
|
/* sequenceMode= */ false,
|
|
52
|
-
/* segmentRelativeVttTiming= */ false
|
|
52
|
+
/* segmentRelativeVttTiming= */ false,
|
|
53
|
+
shaka.media.ManifestParser.UNKNOWN);
|
|
53
54
|
});
|
|
54
55
|
|
|
55
56
|
afterEach(() => {
|
|
@@ -298,7 +299,8 @@ describe('TextEngine', () => {
|
|
|
298
299
|
textEngine.initParser(
|
|
299
300
|
dummyMimeType,
|
|
300
301
|
/* sequenceMode= */ false,
|
|
301
|
-
/* segmentRelativeVttTiming= */ true
|
|
302
|
+
/* segmentRelativeVttTiming= */ true,
|
|
303
|
+
shaka.media.ManifestParser.UNKNOWN);
|
|
302
304
|
|
|
303
305
|
mockParseMedia.and.callFake((data, time) => {
|
|
304
306
|
return [
|
|
@@ -1866,9 +1866,8 @@ describe('TtmlTextParser', () => {
|
|
|
1866
1866
|
endTime: 60,
|
|
1867
1867
|
payload: '',
|
|
1868
1868
|
fontSize: '15px',
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
color: '',
|
|
1869
|
+
backgroundColor: 'transparent',
|
|
1870
|
+
color: 'blue',
|
|
1872
1871
|
displayAlign: Cue.displayAlign.CENTER,
|
|
1873
1872
|
textAlign: Cue.textAlign.CENTER,
|
|
1874
1873
|
|
|
@@ -1970,7 +1969,7 @@ describe('TtmlTextParser', () => {
|
|
|
1970
1969
|
startTime: 0,
|
|
1971
1970
|
endTime: 60,
|
|
1972
1971
|
payload: '',
|
|
1973
|
-
fontSize: '',
|
|
1972
|
+
fontSize: '16px',
|
|
1974
1973
|
nestedCues: [
|
|
1975
1974
|
{
|
|
1976
1975
|
startTime: 0,
|
|
@@ -198,15 +198,30 @@ describe('VttTextParser', () => {
|
|
|
198
198
|
{
|
|
199
199
|
startTime: 40,
|
|
200
200
|
endTime: 50,
|
|
201
|
-
payload: '
|
|
201
|
+
payload: '',
|
|
202
202
|
writingMode: Cue.writingMode.VERTICAL_LEFT_TO_RIGHT,
|
|
203
|
+
nestedCues: [
|
|
204
|
+
{
|
|
205
|
+
startTime: 40,
|
|
206
|
+
endTime: 50,
|
|
207
|
+
payload: 'Test',
|
|
208
|
+
writingMode: Cue.writingMode.VERTICAL_LEFT_TO_RIGHT,
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
startTime: 40,
|
|
212
|
+
endTime: 50,
|
|
213
|
+
payload: '2',
|
|
214
|
+
fontWeight: Cue.fontWeight.BOLD,
|
|
215
|
+
writingMode: Cue.writingMode.VERTICAL_LEFT_TO_RIGHT,
|
|
216
|
+
},
|
|
217
|
+
],
|
|
203
218
|
},
|
|
204
219
|
],
|
|
205
220
|
'WEBVTT\n\n' +
|
|
206
221
|
'00:00:20.000 --> 00:00:40.000 vertical:rl\n' +
|
|
207
222
|
'Test\n\n' +
|
|
208
223
|
'00:00:40.000 --> 00:00:50.000 vertical:lr\n' +
|
|
209
|
-
'
|
|
224
|
+
'Test<b>2</b>',
|
|
210
225
|
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
|
211
226
|
});
|
|
212
227
|
|
|
@@ -213,7 +213,6 @@ filterDescribe('WebVTT layout', shaka.test.TextLayoutTests.supported, () => {
|
|
|
213
213
|
await helper.checkScreenshot('line-negative-2');
|
|
214
214
|
});
|
|
215
215
|
|
|
216
|
-
// FIXME: UI version is wrong: not positioned by line
|
|
217
216
|
it('line 0', async () => {
|
|
218
217
|
parseAndDisplay([
|
|
219
218
|
'WEBVTT\n',
|