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.
Files changed (75) hide show
  1. package/.github/workflows/deflake.yaml +8 -5
  2. package/.github/workflows/release-please.yaml +64 -16
  3. package/.github/workflows/report-incremental-coverage.yaml +2 -2
  4. package/.github/workflows/shaka-bot-commands/command-help.sh +27 -0
  5. package/.github/workflows/shaka-bot-commands/command-test.sh +56 -0
  6. package/.github/workflows/shaka-bot-commands/lib.sh +83 -0
  7. package/.github/workflows/shaka-bot-commands/main.sh +51 -0
  8. package/.github/workflows/talk-to-shaka-bot.yaml +29 -0
  9. package/.github/workflows/update-issues.yaml +2 -1
  10. package/.github/workflows/validate-pr-title.yaml +3 -11
  11. package/CHANGELOG.md +33 -0
  12. package/CONTRIBUTORS +1 -0
  13. package/demo/custom.js +5 -5
  14. package/dist/demo.compiled.debug.js +3 -3
  15. package/dist/demo.compiled.debug.map +1 -1
  16. package/dist/demo.compiled.js +3 -3
  17. package/dist/demo.compiled.map +1 -1
  18. package/dist/deps.js +2 -2
  19. package/dist/locales.js +32 -32
  20. package/dist/shaka-player.compiled.d.ts +18 -2
  21. package/dist/shaka-player.compiled.debug.d.ts +18 -2
  22. package/dist/shaka-player.compiled.debug.externs.js +38 -1
  23. package/dist/shaka-player.compiled.debug.js +289 -280
  24. package/dist/shaka-player.compiled.debug.map +3 -3
  25. package/dist/shaka-player.compiled.externs.js +38 -1
  26. package/dist/shaka-player.compiled.js +944 -940
  27. package/dist/shaka-player.compiled.map +4 -4
  28. package/dist/shaka-player.ui.d.ts +18 -2
  29. package/dist/shaka-player.ui.debug.d.ts +18 -2
  30. package/dist/shaka-player.ui.debug.externs.js +38 -1
  31. package/dist/shaka-player.ui.debug.js +352 -343
  32. package/dist/shaka-player.ui.debug.map +3 -3
  33. package/dist/shaka-player.ui.externs.js +38 -1
  34. package/dist/shaka-player.ui.js +1037 -1034
  35. package/dist/shaka-player.ui.map +4 -4
  36. package/docs/tutorials/fairplay.md +8 -0
  37. package/docs/tutorials/faq.md +1 -1
  38. package/externs/shaka/namespace.js +2 -2
  39. package/externs/shaka/text.js +7 -0
  40. package/externs/shaka/transmuxer.js +2 -1
  41. package/lib/dash/segment_template.js +3 -0
  42. package/lib/hls/hls_parser.js +9 -4
  43. package/lib/media/media_source_engine.js +23 -3
  44. package/lib/mss/mss_parser.js +1 -0
  45. package/lib/player.js +8 -57
  46. package/lib/text/lrc_text_parser.js +8 -0
  47. package/lib/text/mp4_ttml_parser.js +8 -0
  48. package/lib/text/mp4_vtt_parser.js +8 -0
  49. package/lib/text/sbv_text_parser.js +8 -0
  50. package/lib/text/simple_text_displayer.js +0 -1
  51. package/lib/text/srt_text_parser.js +8 -0
  52. package/lib/text/ssa_text_parser.js +8 -0
  53. package/lib/text/text_engine.js +8 -1
  54. package/lib/text/ttml_text_parser.js +10 -1
  55. package/lib/text/ui_text_displayer.js +49 -3
  56. package/lib/text/vtt_text_parser.js +21 -5
  57. package/lib/transmuxer/aac_transmuxer.js +1 -1
  58. package/lib/transmuxer/ac3_transmuxer.js +1 -0
  59. package/lib/transmuxer/ec3_transmuxer.js +1 -0
  60. package/lib/transmuxer/mp3_transmuxer.js +1 -0
  61. package/lib/transmuxer/ts_transmuxer.js +84 -100
  62. package/lib/util/error.js +16 -10
  63. package/lib/util/mp4_generator.js +27 -20
  64. package/lib/util/player_configuration.js +2 -2
  65. package/lib/util/stream_utils.js +13 -11
  66. package/lib/util/ts_parser.js +137 -117
  67. package/package.json +2 -2
  68. package/test/hls/hls_parser_unit.js +12 -13
  69. package/test/media/media_source_engine_integration.js +2 -2
  70. package/test/mss/mss_player_integration.js +1 -1
  71. package/test/text/text_engine_unit.js +4 -2
  72. package/test/text/ttml_text_parser_unit.js +3 -4
  73. package/test/text/vtt_text_parser_unit.js +17 -2
  74. package/test/text/web_vtt_layout_integration.js +0 -1
  75. package/ui/language_utils.js +0 -15
@@ -6,7 +6,7 @@
6
6
 
7
7
  goog.provide('shaka.util.TsParser');
8
8
 
9
- goog.require('goog.asserts');
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
- const pes = this.parsePES_(videoData, /* isVideo= */ true);
165
- if (pes && pes.pts != null) {
166
- const startTime = Math.min(pes.dts, pes.pts) / timescale;
167
- if (this.videoStartTime_ == null ||
168
- this.videoStartTime_ > startTime) {
169
- this.videoStartTime_ = startTime;
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
- const pes = this.parsePES_(audioData);
178
- if (pes && pes.pts != null) {
179
- const startTime = Math.min(pes.dts, pes.pts) / timescale;
180
- if (this.audioStartTime_ == null ||
181
- this.audioStartTime_ > startTime) {
182
- this.audioStartTime_ = startTime;
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
- this.id3Data_.push(data.subarray(offset, start + packetLength));
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, isVideo = false) {
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
- // If it is video, we expect the packet length to be 0. When the PES packet
365
- // length is set to zero, the PES packet can be any length.
366
- if (isVideo && pes.packetLength != 0) {
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
- // the data section starts immediately after the PES header.
405
- // pes_header_data_length specifies the number of header bytes
406
- // that follow the last byte of the field.
407
- pes.data = data.subarray(9 + data[8]);
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
- if (i <= 2) {
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, i - 2));
470
+ infoOfLastNalu.data, data.subarray(0, lastByteToKeep));
462
471
  infoOfLastNalu.fullData = shaka.util.Uint8ArrayUtils.concat(
463
- infoOfLastNalu.fullData, data.subarray(0, i - 2));
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
- let prevId3Data = new Uint8Array(0);
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.unshift({
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
- let prevAudioData = new Uint8Array(0);
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.unshift(pes);
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
- let prevVideoData = new Uint8Array(0);
614
- // parsePES_() only works if the data begins on a PES boundary.
615
- // Try the last data blob first, and if it doesn't begin on a
616
- // PES boundary, prepend the previous blob and try again.
617
- // This way, a successful parse will always begin and end on
618
- // the correct boundary, and no data will be skipped.
619
- for (let i = this.videoData_.length - 1; i >= 0; i--) {
620
- const data = this.videoData_[i];
621
- goog.asserts.assert(data, 'We should have a data');
622
- const videoData = Uint8ArrayUtils.concat(data, prevVideoData);
623
- const pes = this.parsePES_(videoData, /* isVideo= */ true);
624
- if (pes) {
625
- video.unshift(pes);
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: this.audioStartTime_,
643
- video: this.videoStartTime_,
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 Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
668
- let nalus = [];
669
- let prevVideoData = new Uint8Array(0);
670
- // parsePES_() only works if the data begins on a PES boundary.
671
- // Try the last data blob first, and if it doesn't begin on a
672
- // PES boundary, prepend the previous blob and try again.
673
- // This way, a successful parse will always begin and end on
674
- // the correct boundary, and no data will be skipped.
675
- for (let i = this.videoData_.length - 1; i >= 0; i--) {
676
- const data = this.videoData_[i];
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
- // We need to invert the array to return it in the correct order.
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 resolution = {
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 resolution;
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 resolution;
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
- resolution.height = String(((2 - frameMbsOnlyFlag) *
815
+ videoInfo.height = String(((2 - frameMbsOnlyFlag) *
809
816
  (picHeightInMapUnitsMinus1 + 1) * 16) - (frameCropTopOffset * 2) -
810
817
  (frameCropBottomOffset * 2));
811
- resolution.width = String(((picWidthInMbsMinus1 + 1) * 16) -
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 resolution;
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.0",
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.4",
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
- // Issue #1875
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",LANG="en",URI="audio1"\n',
4195
- '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud",LANG="eo",URI="audio2"\n',
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 = 'und';
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(1);
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
- return;
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
- return;
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
- return;
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
- // Styles from regionStyle should apply only to the nested cue.
1870
- backgroundColor: '',
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: 'Test2',
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
- 'Test2',
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',
@@ -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".