shaka-player 4.4.0 → 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 +64 -16
- package/.github/workflows/report-incremental-coverage.yaml +2 -2
- package/.github/workflows/shaka-bot-commands/command-help.sh +27 -0
- package/.github/workflows/shaka-bot-commands/command-test.sh +56 -0
- package/.github/workflows/shaka-bot-commands/lib.sh +83 -0
- package/.github/workflows/shaka-bot-commands/main.sh +51 -0
- package/.github/workflows/talk-to-shaka-bot.yaml +29 -0
- package/.github/workflows/update-issues.yaml +2 -1
- package/.github/workflows/validate-pr-title.yaml +3 -11
- package/CHANGELOG.md +33 -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 +32 -32
- package/dist/shaka-player.compiled.d.ts +18 -2
- package/dist/shaka-player.compiled.debug.d.ts +18 -2
- package/dist/shaka-player.compiled.debug.externs.js +38 -1
- package/dist/shaka-player.compiled.debug.js +289 -280
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +38 -1
- package/dist/shaka-player.compiled.js +944 -940
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +18 -2
- package/dist/shaka-player.ui.debug.d.ts +18 -2
- package/dist/shaka-player.ui.debug.externs.js +38 -1
- package/dist/shaka-player.ui.debug.js +352 -343
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +38 -1
- package/dist/shaka-player.ui.js +1037 -1034
- 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/namespace.js +2 -2
- package/externs/shaka/text.js +7 -0
- package/externs/shaka/transmuxer.js +2 -1
- package/lib/dash/segment_template.js +3 -0
- package/lib/hls/hls_parser.js +9 -4
- package/lib/media/media_source_engine.js +23 -3
- package/lib/mss/mss_parser.js +1 -0
- 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/simple_text_displayer.js +0 -1
- 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/aac_transmuxer.js +1 -1
- package/lib/transmuxer/ac3_transmuxer.js +1 -0
- package/lib/transmuxer/ec3_transmuxer.js +1 -0
- package/lib/transmuxer/mp3_transmuxer.js +1 -0
- package/lib/transmuxer/ts_transmuxer.js +84 -100
- package/lib/util/error.js +16 -10
- package/lib/util/mp4_generator.js +27 -20
- package/lib/util/player_configuration.js +2 -2
- package/lib/util/stream_utils.js +13 -11
- package/lib/util/ts_parser.js +137 -117
- package/package.json +2 -2
- package/test/hls/hls_parser_unit.js +12 -13
- package/test/media/media_source_engine_integration.js +2 -2
- package/test/mss/mss_player_integration.js +1 -1
- 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
package/lib/util/ts_parser.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
goog.provide('shaka.util.TsParser');
|
|
8
8
|
|
|
9
|
-
goog.require('
|
|
9
|
+
goog.require('shaka.Deprecate');
|
|
10
10
|
goog.require('shaka.log');
|
|
11
11
|
goog.require('shaka.util.ExpGolomb');
|
|
12
12
|
goog.require('shaka.util.Id3Utils');
|
|
@@ -26,9 +26,6 @@ shaka.util.TsParser = class {
|
|
|
26
26
|
/** @private {boolean} */
|
|
27
27
|
this.pmtParsed_ = false;
|
|
28
28
|
|
|
29
|
-
/** @private {?number} */
|
|
30
|
-
this.videoStartTime_ = null;
|
|
31
|
-
|
|
32
29
|
/** @private {?number} */
|
|
33
30
|
this.videoPid_ = null;
|
|
34
31
|
|
|
@@ -38,9 +35,6 @@ shaka.util.TsParser = class {
|
|
|
38
35
|
/** @private {!Array.<Uint8Array>} */
|
|
39
36
|
this.videoData_ = [];
|
|
40
37
|
|
|
41
|
-
/** @private {?number} */
|
|
42
|
-
this.audioStartTime_ = null;
|
|
43
|
-
|
|
44
38
|
/** @private {?number} */
|
|
45
39
|
this.audioPid_ = null;
|
|
46
40
|
|
|
@@ -63,9 +57,7 @@ shaka.util.TsParser = class {
|
|
|
63
57
|
* @export
|
|
64
58
|
*/
|
|
65
59
|
clearData() {
|
|
66
|
-
this.videoStartTime_ = null;
|
|
67
60
|
this.videoData_ = [];
|
|
68
|
-
this.audioStartTime_ = null;
|
|
69
61
|
this.audioData_ = [];
|
|
70
62
|
this.id3Data_ = [];
|
|
71
63
|
}
|
|
@@ -78,8 +70,8 @@ shaka.util.TsParser = class {
|
|
|
78
70
|
* @export
|
|
79
71
|
*/
|
|
80
72
|
parse(data) {
|
|
81
|
-
const timescale = shaka.util.TsParser.Timescale;
|
|
82
73
|
const packetLength = shaka.util.TsParser.PacketLength_;
|
|
74
|
+
const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
|
|
83
75
|
|
|
84
76
|
// A TS fragment should contain at least 3 TS packets, a PAT, a PMT, and
|
|
85
77
|
// one PID.
|
|
@@ -161,33 +153,43 @@ shaka.util.TsParser = class {
|
|
|
161
153
|
}
|
|
162
154
|
case this.videoPid_: {
|
|
163
155
|
const videoData = data.subarray(offset, start + packetLength);
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
this.
|
|
156
|
+
if (payloadUnitStartIndicator) {
|
|
157
|
+
this.videoData_.push(videoData);
|
|
158
|
+
} else if (this.videoData_.length) {
|
|
159
|
+
const prevVideoData = this.videoData_[this.videoData_.length - 1];
|
|
160
|
+
if (prevVideoData) {
|
|
161
|
+
this.videoData_[this.videoData_.length - 1] =
|
|
162
|
+
Uint8ArrayUtils.concat(prevVideoData, videoData);
|
|
170
163
|
}
|
|
171
164
|
}
|
|
172
|
-
this.videoData_.push(videoData);
|
|
173
165
|
break;
|
|
174
166
|
}
|
|
175
167
|
case this.audioPid_: {
|
|
176
168
|
const audioData = data.subarray(offset, start + packetLength);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
this.
|
|
169
|
+
if (payloadUnitStartIndicator) {
|
|
170
|
+
this.audioData_.push(audioData);
|
|
171
|
+
} else if (this.audioData_.length) {
|
|
172
|
+
const prevAudioData = this.audioData_[this.audioData_.length - 1];
|
|
173
|
+
if (prevAudioData) {
|
|
174
|
+
this.audioData_[this.audioData_.length - 1] =
|
|
175
|
+
Uint8ArrayUtils.concat(prevAudioData, audioData);
|
|
183
176
|
}
|
|
184
177
|
}
|
|
185
|
-
this.audioData_.push(audioData);
|
|
186
178
|
break;
|
|
187
179
|
}
|
|
188
|
-
case this.id3Pid_:
|
|
189
|
-
|
|
180
|
+
case this.id3Pid_: {
|
|
181
|
+
const id3Data = data.subarray(offset, start + packetLength);
|
|
182
|
+
if (payloadUnitStartIndicator) {
|
|
183
|
+
this.id3Data_.push(id3Data);
|
|
184
|
+
} else if (this.id3Data_.length) {
|
|
185
|
+
const prevId3Data = this.id3Data_[this.id3Data_.length - 1];
|
|
186
|
+
if (prevId3Data) {
|
|
187
|
+
this.id3Data_[this.id3Data_.length - 1] =
|
|
188
|
+
Uint8ArrayUtils.concat(prevId3Data, id3Data);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
190
191
|
break;
|
|
192
|
+
}
|
|
191
193
|
default:
|
|
192
194
|
unknownPIDs = true;
|
|
193
195
|
break;
|
|
@@ -340,11 +342,10 @@ shaka.util.TsParser = class {
|
|
|
340
342
|
* Parse PES
|
|
341
343
|
*
|
|
342
344
|
* @param {Uint8Array} data
|
|
343
|
-
* @param {boolean=} isVideo
|
|
344
345
|
* @return {?shaka.extern.MPEG_PES}
|
|
345
346
|
* @private
|
|
346
347
|
*/
|
|
347
|
-
parsePES_(data
|
|
348
|
+
parsePES_(data) {
|
|
348
349
|
const startPrefix = (data[0] << 16) | (data[1] << 8) | data[2];
|
|
349
350
|
// In certain live streams, the start of a TS fragment has ts packets
|
|
350
351
|
// that are frame data that is continuing from the previous fragment. This
|
|
@@ -361,9 +362,9 @@ shaka.util.TsParser = class {
|
|
|
361
362
|
dts: null,
|
|
362
363
|
};
|
|
363
364
|
|
|
364
|
-
//
|
|
365
|
-
//
|
|
366
|
-
if (
|
|
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) {
|
|
367
368
|
return null;
|
|
368
369
|
}
|
|
369
370
|
|
|
@@ -401,10 +402,15 @@ shaka.util.TsParser = class {
|
|
|
401
402
|
(data[18] & 0xfe) / 2;
|
|
402
403
|
}
|
|
403
404
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
//
|
|
407
|
-
|
|
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);
|
|
408
414
|
|
|
409
415
|
return pes;
|
|
410
416
|
}
|
|
@@ -454,13 +460,16 @@ shaka.util.TsParser = class {
|
|
|
454
460
|
} else if (numZeros >= 2 && value == 1 && tryToFinishLastNalu) {
|
|
455
461
|
// If we are scanning the next PES, we need append the data to the
|
|
456
462
|
// previous Nalu and don't scan for more nalus.
|
|
457
|
-
|
|
463
|
+
const startCodeSize = numZeros > 3 ? 3 : numZeros;
|
|
464
|
+
const lastByteToKeep = i - startCodeSize;
|
|
465
|
+
// Optimization
|
|
466
|
+
if (lastByteToKeep == 0) {
|
|
458
467
|
break;
|
|
459
468
|
}
|
|
460
469
|
infoOfLastNalu.data = shaka.util.Uint8ArrayUtils.concat(
|
|
461
|
-
infoOfLastNalu.data, data.subarray(0,
|
|
470
|
+
infoOfLastNalu.data, data.subarray(0, lastByteToKeep));
|
|
462
471
|
infoOfLastNalu.fullData = shaka.util.Uint8ArrayUtils.concat(
|
|
463
|
-
infoOfLastNalu.fullData, data.subarray(0,
|
|
472
|
+
infoOfLastNalu.fullData, data.subarray(0, lastByteToKeep));
|
|
464
473
|
break;
|
|
465
474
|
} else if (numZeros >= 2 && value == 1) {
|
|
466
475
|
// We just read a start code. Consume the NALU we passed, if any.
|
|
@@ -519,7 +528,7 @@ shaka.util.TsParser = class {
|
|
|
519
528
|
type: lastNaluType,
|
|
520
529
|
time: time,
|
|
521
530
|
};
|
|
522
|
-
if (nextPes) {
|
|
531
|
+
if (nextPes && pes.packetLength == 0) {
|
|
523
532
|
data = nextPes.data;
|
|
524
533
|
len = data.byteLength;
|
|
525
534
|
i = -1;
|
|
@@ -542,30 +551,17 @@ shaka.util.TsParser = class {
|
|
|
542
551
|
*/
|
|
543
552
|
getMetadata() {
|
|
544
553
|
const timescale = shaka.util.TsParser.Timescale;
|
|
545
|
-
const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
|
|
546
554
|
const metadata = [];
|
|
547
|
-
|
|
548
|
-
// parsePES_() only works if the data begins on a PES boundary.
|
|
549
|
-
// Try the last data blob first, and if it doesn't begin on a
|
|
550
|
-
// PES boundary, prepend the previous blob and try again.
|
|
551
|
-
// This way, a successful parse will always begin and end on
|
|
552
|
-
// the correct boundary, and no data will be skipped.
|
|
553
|
-
for (let i = this.id3Data_.length - 1; i >= 0; i--) {
|
|
554
|
-
const data = this.id3Data_[i];
|
|
555
|
-
goog.asserts.assert(data, 'We should have a data');
|
|
556
|
-
const id3Data = Uint8ArrayUtils.concat(data, prevId3Data);
|
|
555
|
+
for (const id3Data of this.id3Data_) {
|
|
557
556
|
const pes = this.parsePES_(id3Data);
|
|
558
557
|
if (pes) {
|
|
559
|
-
metadata.
|
|
558
|
+
metadata.push({
|
|
560
559
|
cueTime: pes.pts ? pes.pts / timescale : null,
|
|
561
560
|
data: pes.data,
|
|
562
561
|
frames: shaka.util.Id3Utils.getID3Frames(pes.data),
|
|
563
562
|
dts: pes.dts,
|
|
564
563
|
pts: pes.pts,
|
|
565
564
|
});
|
|
566
|
-
prevId3Data = new Uint8Array(0);
|
|
567
|
-
} else {
|
|
568
|
-
prevId3Data = id3Data;
|
|
569
565
|
}
|
|
570
566
|
}
|
|
571
567
|
return metadata;
|
|
@@ -578,24 +574,11 @@ shaka.util.TsParser = class {
|
|
|
578
574
|
* @export
|
|
579
575
|
*/
|
|
580
576
|
getAudioData() {
|
|
581
|
-
const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
|
|
582
577
|
const audio = [];
|
|
583
|
-
|
|
584
|
-
// parsePES_() only works if the data begins on a PES boundary.
|
|
585
|
-
// Try the last data blob first, and if it doesn't begin on a
|
|
586
|
-
// PES boundary, prepend the previous blob and try again.
|
|
587
|
-
// This way, a successful parse will always begin and end on
|
|
588
|
-
// the correct boundary, and no data will be skipped.
|
|
589
|
-
for (let i = this.audioData_.length - 1; i >= 0; i--) {
|
|
590
|
-
const data = this.audioData_[i];
|
|
591
|
-
goog.asserts.assert(data, 'We should have a data');
|
|
592
|
-
const audioData = Uint8ArrayUtils.concat(data, prevAudioData);
|
|
578
|
+
for (const audioData of this.audioData_) {
|
|
593
579
|
const pes = this.parsePES_(audioData);
|
|
594
580
|
if (pes) {
|
|
595
|
-
audio.
|
|
596
|
-
prevAudioData = new Uint8Array(0);
|
|
597
|
-
} else {
|
|
598
|
-
prevAudioData = audioData;
|
|
581
|
+
audio.push(pes);
|
|
599
582
|
}
|
|
600
583
|
}
|
|
601
584
|
return audio;
|
|
@@ -610,22 +593,19 @@ shaka.util.TsParser = class {
|
|
|
610
593
|
getVideoData() {
|
|
611
594
|
const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
|
|
612
595
|
const video = [];
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
video.
|
|
626
|
-
prevVideoData = new Uint8Array(0);
|
|
627
|
-
} else {
|
|
628
|
-
prevVideoData = videoData;
|
|
596
|
+
for (const videoData of this.videoData_) {
|
|
597
|
+
const pes = this.parsePES_(videoData);
|
|
598
|
+
if (pes && pes.pts != null && pes.dts != null) {
|
|
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);
|
|
629
609
|
}
|
|
630
610
|
}
|
|
631
611
|
return video;
|
|
@@ -638,9 +618,28 @@ shaka.util.TsParser = class {
|
|
|
638
618
|
* @export
|
|
639
619
|
*/
|
|
640
620
|
getStartTime() {
|
|
621
|
+
const timescale = shaka.util.TsParser.Timescale;
|
|
622
|
+
let audioStartTime = null;
|
|
623
|
+
for (const pes of this.getAudioData()) {
|
|
624
|
+
if (pes && pes.pts != null) {
|
|
625
|
+
const startTime = Math.min(pes.dts, pes.pts) / timescale;
|
|
626
|
+
if (audioStartTime == null || audioStartTime > startTime) {
|
|
627
|
+
audioStartTime = startTime;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
let videoStartTime = null;
|
|
632
|
+
for (const pes of this.getVideoData()) {
|
|
633
|
+
if (pes && pes.pts != null) {
|
|
634
|
+
const startTime = Math.min(pes.dts, pes.pts) / timescale;
|
|
635
|
+
if (videoStartTime == null || videoStartTime > startTime) {
|
|
636
|
+
videoStartTime = startTime;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
641
640
|
return {
|
|
642
|
-
audio:
|
|
643
|
-
video:
|
|
641
|
+
audio: audioStartTime,
|
|
642
|
+
video: videoStartTime,
|
|
644
643
|
};
|
|
645
644
|
}
|
|
646
645
|
|
|
@@ -664,30 +663,20 @@ shaka.util.TsParser = class {
|
|
|
664
663
|
* @export
|
|
665
664
|
*/
|
|
666
665
|
getVideoNalus() {
|
|
667
|
-
const
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
goog.asserts.assert(data, 'We should have a data');
|
|
678
|
-
const videoData = Uint8ArrayUtils.concat(data, prevVideoData);
|
|
679
|
-
const pes = this.parsePES_(videoData);
|
|
680
|
-
if (pes) {
|
|
681
|
-
if (this.videoCodec_ == 'avc') {
|
|
682
|
-
nalus = nalus.concat(this.parseAvcNalus(pes));
|
|
683
|
-
}
|
|
684
|
-
prevVideoData = new Uint8Array(0);
|
|
685
|
-
} else {
|
|
686
|
-
prevVideoData = videoData;
|
|
666
|
+
const nalus = [];
|
|
667
|
+
if (this.videoCodec_ != 'avc') {
|
|
668
|
+
return nalus;
|
|
669
|
+
}
|
|
670
|
+
const videoData = this.getVideoData();
|
|
671
|
+
for (let i = 0; i < videoData.length; i++) {
|
|
672
|
+
const pes = videoData[i];
|
|
673
|
+
let nextPes;
|
|
674
|
+
if (i + 1 < videoData.length) {
|
|
675
|
+
nextPes = videoData[i + 1];
|
|
687
676
|
}
|
|
677
|
+
nalus.push(...this.parseAvcNalus(pes, nextPes));
|
|
688
678
|
}
|
|
689
|
-
|
|
690
|
-
return nalus.reverse();
|
|
679
|
+
return nalus;
|
|
691
680
|
}
|
|
692
681
|
|
|
693
682
|
/**
|
|
@@ -697,29 +686,47 @@ shaka.util.TsParser = class {
|
|
|
697
686
|
* @export
|
|
698
687
|
*/
|
|
699
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() {
|
|
700
706
|
const TsParser = shaka.util.TsParser;
|
|
701
|
-
const
|
|
707
|
+
const videoInfo = {
|
|
702
708
|
height: null,
|
|
703
709
|
width: null,
|
|
710
|
+
codec: null,
|
|
704
711
|
};
|
|
705
712
|
const videoNalus = this.getVideoNalus();
|
|
706
713
|
if (!videoNalus.length) {
|
|
707
|
-
return
|
|
714
|
+
return videoInfo;
|
|
708
715
|
}
|
|
709
716
|
const spsNalu = videoNalus.find((nalu) => {
|
|
710
717
|
return nalu.type == TsParser.H264_NALU_TYPE_SPS_;
|
|
711
718
|
});
|
|
712
719
|
if (!spsNalu) {
|
|
713
|
-
return
|
|
720
|
+
return videoInfo;
|
|
714
721
|
}
|
|
715
722
|
|
|
716
723
|
const expGolombDecoder = new shaka.util.ExpGolomb(spsNalu.data);
|
|
717
724
|
// profile_idc
|
|
718
725
|
const profileIdc = expGolombDecoder.readUnsignedByte();
|
|
719
726
|
// constraint_set[0-5]_flag
|
|
720
|
-
expGolombDecoder.readUnsignedByte();
|
|
727
|
+
const profileCompatibility = expGolombDecoder.readUnsignedByte();
|
|
721
728
|
// level_idc u(8)
|
|
722
|
-
expGolombDecoder.readUnsignedByte();
|
|
729
|
+
const levelIdc = expGolombDecoder.readUnsignedByte();
|
|
723
730
|
// seq_parameter_set_id
|
|
724
731
|
expGolombDecoder.skipExpGolomb();
|
|
725
732
|
|
|
@@ -805,13 +812,26 @@ shaka.util.TsParser = class {
|
|
|
805
812
|
frameCropBottomOffset = expGolombDecoder.readUnsignedExpGolomb();
|
|
806
813
|
}
|
|
807
814
|
|
|
808
|
-
|
|
815
|
+
videoInfo.height = String(((2 - frameMbsOnlyFlag) *
|
|
809
816
|
(picHeightInMapUnitsMinus1 + 1) * 16) - (frameCropTopOffset * 2) -
|
|
810
817
|
(frameCropBottomOffset * 2));
|
|
811
|
-
|
|
818
|
+
videoInfo.width = String(((picWidthInMbsMinus1 + 1) * 16) -
|
|
812
819
|
frameCropLeftOffset * 2 - frameCropRightOffset * 2);
|
|
820
|
+
videoInfo.codec = 'avc1.' + this.byteToHex_(profileIdc) +
|
|
821
|
+
this.byteToHex_(profileCompatibility) + this.byteToHex_(levelIdc);
|
|
813
822
|
|
|
814
|
-
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);
|
|
815
835
|
}
|
|
816
836
|
|
|
817
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",
|
|
@@ -4180,19 +4180,11 @@ describe('HlsParser', () => {
|
|
|
4180
4180
|
expect(videoSegments2[2].endTime).toBe(10);
|
|
4181
4181
|
});
|
|
4182
4182
|
|
|
4183
|
-
|
|
4184
|
-
it('ignores audio groups on audio-only content', async () => {
|
|
4185
|
-
// NOTE: To reproduce the original issue accurately, the two audio playlist
|
|
4186
|
-
// URIs must differ. When the issue occurred, the audio-only variant would
|
|
4187
|
-
// be detected as a video stream and combined with the audio group, leading
|
|
4188
|
-
// the player to buffer "video" that was really audio, resulting in
|
|
4189
|
-
// audio-only playback to the exclusion of any other streams. Since the
|
|
4190
|
-
// root cause of that was the mis-detection, this repro case does not need
|
|
4191
|
-
// to include any audio+video variants.
|
|
4183
|
+
it('allow audio groups on audio-only content', async () => {
|
|
4192
4184
|
const master = [
|
|
4193
4185
|
'#EXTM3U\n',
|
|
4194
|
-
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud",
|
|
4195
|
-
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud",
|
|
4186
|
+
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud",LANGUAGE="en",URI="audio1"\n',
|
|
4187
|
+
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud",LANGUAGE="eo",URI="audio2"\n',
|
|
4196
4188
|
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="mp4a",AUDIO="aud"\n',
|
|
4197
4189
|
'audio3\n',
|
|
4198
4190
|
].join('');
|
|
@@ -4210,7 +4202,14 @@ describe('HlsParser', () => {
|
|
|
4210
4202
|
manifest.anyTimeline();
|
|
4211
4203
|
manifest.addPartialVariant((variant) => {
|
|
4212
4204
|
variant.bandwidth = 200;
|
|
4213
|
-
variant.language = '
|
|
4205
|
+
variant.language = 'en';
|
|
4206
|
+
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
4207
|
+
stream.mime('audio/mp4', 'mp4a');
|
|
4208
|
+
});
|
|
4209
|
+
});
|
|
4210
|
+
manifest.addPartialVariant((variant) => {
|
|
4211
|
+
variant.bandwidth = 200;
|
|
4212
|
+
variant.language = 'eo';
|
|
4214
4213
|
variant.addPartialStream(ContentType.AUDIO, (stream) => {
|
|
4215
4214
|
stream.mime('audio/mp4', 'mp4a');
|
|
4216
4215
|
});
|
|
@@ -4229,7 +4228,7 @@ describe('HlsParser', () => {
|
|
|
4229
4228
|
|
|
4230
4229
|
const actual = await parser.start('test:/master', playerInterface);
|
|
4231
4230
|
await loadAllStreamsFor(actual);
|
|
4232
|
-
expect(actual.variants.length).toBe(
|
|
4231
|
+
expect(actual.variants.length).toBe(2);
|
|
4233
4232
|
expect(actual).toEqual(manifest);
|
|
4234
4233
|
});
|
|
4235
4234
|
|
|
@@ -656,7 +656,7 @@ describe('MediaSourceEngine', () => {
|
|
|
656
656
|
it('extracts ID3 metadata from AAC', async () => {
|
|
657
657
|
if (!MediaSource.isTypeSupported('audio/aac') ||
|
|
658
658
|
!shaka.util.Platform.supportsSequenceMode()) {
|
|
659
|
-
|
|
659
|
+
pending('Raw AAC codec is not supported by the platform.');
|
|
660
660
|
}
|
|
661
661
|
metadata = shaka.test.TestScheme.DATA['id3-metadata_aac'];
|
|
662
662
|
generators = shaka.test.TestScheme.GENERATORS['id3-metadata_aac'];
|
|
@@ -672,7 +672,7 @@ describe('MediaSourceEngine', () => {
|
|
|
672
672
|
|
|
673
673
|
it('extracts ID3 metadata from AAC when transmuxing', async () => {
|
|
674
674
|
if (!MediaSource.isTypeSupported('audio/aac')) {
|
|
675
|
-
|
|
675
|
+
pending('Raw AAC codec is not supported by the platform.');
|
|
676
676
|
}
|
|
677
677
|
metadata = shaka.test.TestScheme.DATA['id3-metadata_aac'];
|
|
678
678
|
generators = shaka.test.TestScheme.GENERATORS['id3-metadata_aac'];
|
|
@@ -94,7 +94,7 @@ describe('MSS Player', () => {
|
|
|
94
94
|
it('MSS VoD PlayReady', async () => {
|
|
95
95
|
const support = await shaka.media.DrmEngine.probeSupport();
|
|
96
96
|
if (!support['com.microsoft.playready']) {
|
|
97
|
-
|
|
97
|
+
pending('PlayReady is not supported by the platform.');
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
player.configure({
|
|
@@ -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',
|
package/ui/language_utils.js
CHANGED
|
@@ -193,21 +193,6 @@ shaka.ui.LanguageUtils = class {
|
|
|
193
193
|
// Extract the base language from the locale as a fallback step.
|
|
194
194
|
const language = shaka.util.LanguageUtils.getBase(locale);
|
|
195
195
|
|
|
196
|
-
// If Intl.DisplayNames is supported we prefer it, because the list of
|
|
197
|
-
// languages is up to date.
|
|
198
|
-
if (window.Intl && 'DisplayNames' in Intl) {
|
|
199
|
-
try {
|
|
200
|
-
if (Intl.DisplayNames.supportedLocalesOf([locale]).length) {
|
|
201
|
-
const languageNames =
|
|
202
|
-
new Intl.DisplayNames([locale], {type: 'language'});
|
|
203
|
-
const language = languageNames.of(locale);
|
|
204
|
-
return language.charAt(0).toUpperCase() + language.slice(1);
|
|
205
|
-
}
|
|
206
|
-
} catch (e) {
|
|
207
|
-
// Ignore errors and try the fallback
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
196
|
// First try to resolve the full language name.
|
|
212
197
|
// If that fails, try the base.
|
|
213
198
|
// Finally, report "unknown".
|