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
@@ -159,7 +159,8 @@ shaka.transmuxer.TsTransmuxer = class {
159
159
  * @override
160
160
  * @export
161
161
  */
162
- transmux(data, stream, reference, duration) {
162
+ transmux(data, stream, reference, duration, contentType) {
163
+ const ContentType = shaka.util.ManifestParserUtils.ContentType;
163
164
  if (!this.tsParser_) {
164
165
  this.tsParser_ = new shaka.util.TsParser();
165
166
  } else {
@@ -169,57 +170,46 @@ shaka.transmuxer.TsTransmuxer = class {
169
170
 
170
171
  const uint8ArrayData = shaka.util.BufferUtils.toUint8(data);
171
172
 
172
- let timestamp = reference.endTime * 1000;
173
-
174
173
  const tsParser = this.tsParser_.parse(uint8ArrayData);
175
- const startTime = tsParser.getStartTime();
176
-
177
- if (startTime.audio != null) {
178
- timestamp = startTime.audio;
179
- }
180
- if (startTime.video != null) {
181
- timestamp = startTime.video;
182
- }
183
174
  const streamInfos = [];
184
175
  const codecs = tsParser.getCodecs();
185
176
  try {
186
177
  let streamInfo = null;
187
- switch (codecs.video) {
188
- case 'avc':
189
- streamInfo =
190
- this.getAvcStreamInfo_(tsParser, timestamp, stream, duration);
191
- break;
192
- }
193
- if (streamInfo) {
194
- streamInfos.push(streamInfo);
195
- streamInfo = null;
196
- }
197
- const isMuxed = !!(codecs.video && codecs.audio);
198
- switch (codecs.audio) {
199
- case 'aac':
200
- streamInfo =
201
- this.getAacStreamInfo_(
202
- tsParser, timestamp, stream, duration, isMuxed);
203
- break;
204
- case 'ac3':
205
- streamInfo =
206
- this.getAc3StreamInfo_(
207
- tsParser, timestamp, stream, duration, isMuxed);
208
- break;
209
- case 'ec3':
210
- streamInfo =
211
- this.getEc3StreamInfo_(
212
- tsParser, timestamp, stream, duration, isMuxed);
213
- break;
214
- case 'mp3':
215
- streamInfo =
216
- this.getMp3StreamInfo_(
217
- tsParser, timestamp, stream, duration, isMuxed);
218
- break;
178
+ if (contentType == ContentType.VIDEO) {
179
+ switch (codecs.video) {
180
+ case 'avc':
181
+ streamInfo =
182
+ this.getAvcStreamInfo_(tsParser, stream, duration);
183
+ break;
184
+ }
185
+ if (streamInfo) {
186
+ streamInfos.push(streamInfo);
187
+ streamInfo = null;
188
+ }
219
189
  }
220
- if (streamInfo) {
221
- streamInfos.push(streamInfo);
222
- streamInfo = null;
190
+ if (contentType == ContentType.AUDIO) {
191
+ switch (codecs.audio) {
192
+ case 'aac':
193
+ streamInfo =
194
+ this.getAacStreamInfo_(tsParser, stream, duration);
195
+ break;
196
+ case 'ac3':
197
+ streamInfo =
198
+ this.getAc3StreamInfo_(tsParser, stream, duration);
199
+ break;
200
+ case 'ec3':
201
+ streamInfo =
202
+ this.getEc3StreamInfo_(tsParser, stream, duration);
203
+ break;
204
+ case 'mp3':
205
+ streamInfo =
206
+ this.getMp3StreamInfo_(tsParser, stream, duration);
207
+ break;
208
+ }
209
+ if (streamInfo) {
210
+ streamInfos.push(streamInfo);
211
+ streamInfo = null;
212
+ }
223
213
  }
224
214
  } catch (e) {
225
215
  return Promise.reject(e);
@@ -250,21 +240,22 @@ shaka.transmuxer.TsTransmuxer = class {
250
240
 
251
241
  /**
252
242
  * @param {shaka.util.TsParser} tsParser
253
- * @param {number} timestamp
254
243
  * @param {shaka.extern.Stream} stream
255
244
  * @param {number} duration
256
- * @param {boolean} isMuxed
257
245
  * @return {shaka.util.Mp4Generator.StreamInfo}
258
246
  * @private
259
247
  */
260
- getAacStreamInfo_(tsParser, timestamp, stream, duration, isMuxed) {
248
+ getAacStreamInfo_(tsParser, stream, duration) {
261
249
  const ADTS = shaka.transmuxer.ADTS;
250
+ const timescale = shaka.util.TsParser.Timescale;
262
251
 
263
252
  /** @type {!Array.<shaka.util.Mp4Generator.Mp4Sample>} */
264
253
  const samples = [];
265
254
 
266
255
  let info;
267
256
 
257
+ let firstPts = null;
258
+
268
259
  for (const audioData of tsParser.getAudioData()) {
269
260
  const data = audioData.data;
270
261
  if (!data) {
@@ -280,8 +271,9 @@ shaka.transmuxer.TsTransmuxer = class {
280
271
  }
281
272
  stream.audioSamplingRate = info.sampleRate;
282
273
  stream.channelsCount = info.channelCount;
283
- stream.codecs = info.codec;
284
-
274
+ if (firstPts == null && audioData.pts !== null) {
275
+ firstPts = audioData.pts;
276
+ }
285
277
  while (offset < data.length) {
286
278
  const header = ADTS.parseHeader(data, offset);
287
279
  if (!header) {
@@ -313,7 +305,7 @@ shaka.transmuxer.TsTransmuxer = class {
313
305
  }
314
306
  }
315
307
 
316
- if (!info) {
308
+ if (!info || firstPts == null) {
317
309
  throw new shaka.util.Error(
318
310
  shaka.util.Error.Severity.CRITICAL,
319
311
  shaka.util.Error.Category.MEDIA,
@@ -323,15 +315,12 @@ shaka.transmuxer.TsTransmuxer = class {
323
315
  /** @type {number} */
324
316
  const sampleRate = info.sampleRate;
325
317
  /** @type {number} */
326
- const baseMediaDecodeTime = Math.floor(timestamp * sampleRate / 1000);
318
+ const baseMediaDecodeTime = firstPts / timescale * sampleRate;
327
319
 
328
- let id = stream.id;
329
- if (isMuxed) {
330
- id += shaka.transmuxer.TsTransmuxer.START_ID_FOR_MUXED_CONTENT_;
331
- }
332
320
  return {
333
- id: id,
321
+ id: stream.id,
334
322
  type: shaka.util.ManifestParserUtils.ContentType.AUDIO,
323
+ codecs: info.codec,
335
324
  encrypted: stream.encrypted && stream.drmInfos.length > 0,
336
325
  timescale: sampleRate,
337
326
  duration: duration,
@@ -350,15 +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
- * @param {boolean} isMuxed
357
344
  * @return {shaka.util.Mp4Generator.StreamInfo}
358
345
  * @private
359
346
  */
360
- getAc3StreamInfo_(tsParser, timestamp, stream, duration, isMuxed) {
347
+ getAc3StreamInfo_(tsParser, stream, duration) {
361
348
  const Ac3 = shaka.transmuxer.Ac3;
349
+ const timescale = shaka.util.TsParser.Timescale;
362
350
 
363
351
  /** @type {!Array.<shaka.util.Mp4Generator.Mp4Sample>} */
364
352
  const samples = [];
@@ -369,8 +357,13 @@ shaka.transmuxer.TsTransmuxer = class {
369
357
  /** @type {!Uint8Array} */
370
358
  let audioConfig = new Uint8Array([]);
371
359
 
360
+ let firstPts = null;
361
+
372
362
  for (const audioData of tsParser.getAudioData()) {
373
363
  const data = audioData.data;
364
+ if (firstPts == null && audioData.pts !== null) {
365
+ firstPts = audioData.pts;
366
+ }
374
367
  let offset = 0;
375
368
  while (offset < data.length) {
376
369
  const frame = Ac3.parseFrame(data, offset);
@@ -404,7 +397,7 @@ shaka.transmuxer.TsTransmuxer = class {
404
397
  }
405
398
  }
406
399
 
407
- if (sampleRate == 0 || audioConfig.byteLength == 0) {
400
+ if (sampleRate == 0 || audioConfig.byteLength == 0 || firstPts == null) {
408
401
  throw new shaka.util.Error(
409
402
  shaka.util.Error.Severity.CRITICAL,
410
403
  shaka.util.Error.Category.MEDIA,
@@ -413,15 +406,12 @@ shaka.transmuxer.TsTransmuxer = class {
413
406
 
414
407
 
415
408
  /** @type {number} */
416
- const baseMediaDecodeTime = Math.floor(timestamp * sampleRate / 1000);
409
+ const baseMediaDecodeTime = firstPts / timescale * sampleRate;
417
410
 
418
- let id = stream.id;
419
- if (isMuxed) {
420
- id += shaka.transmuxer.TsTransmuxer.START_ID_FOR_MUXED_CONTENT_;
421
- }
422
411
  return {
423
- id: id,
412
+ id: stream.id,
424
413
  type: shaka.util.ManifestParserUtils.ContentType.AUDIO,
414
+ codecs: 'ac-3',
425
415
  encrypted: stream.encrypted && stream.drmInfos.length > 0,
426
416
  timescale: sampleRate,
427
417
  duration: duration,
@@ -440,15 +430,14 @@ shaka.transmuxer.TsTransmuxer = class {
440
430
 
441
431
  /**
442
432
  * @param {shaka.util.TsParser} tsParser
443
- * @param {number} timestamp
444
433
  * @param {shaka.extern.Stream} stream
445
434
  * @param {number} duration
446
- * @param {boolean} isMuxed
447
435
  * @return {shaka.util.Mp4Generator.StreamInfo}
448
436
  * @private
449
437
  */
450
- getEc3StreamInfo_(tsParser, timestamp, stream, duration, isMuxed) {
438
+ getEc3StreamInfo_(tsParser, stream, duration) {
451
439
  const Ec3 = shaka.transmuxer.Ec3;
440
+ const timescale = shaka.util.TsParser.Timescale;
452
441
 
453
442
  /** @type {!Array.<shaka.util.Mp4Generator.Mp4Sample>} */
454
443
  const samples = [];
@@ -459,8 +448,13 @@ shaka.transmuxer.TsTransmuxer = class {
459
448
  /** @type {!Uint8Array} */
460
449
  let audioConfig = new Uint8Array([]);
461
450
 
451
+ let firstPts = null;
452
+
462
453
  for (const audioData of tsParser.getAudioData()) {
463
454
  const data = audioData.data;
455
+ if (firstPts == null && audioData.pts !== null) {
456
+ firstPts = audioData.pts;
457
+ }
464
458
  let offset = 0;
465
459
  while (offset < data.length) {
466
460
  const frame = Ec3.parseFrame(data, offset);
@@ -494,7 +488,7 @@ shaka.transmuxer.TsTransmuxer = class {
494
488
  }
495
489
  }
496
490
 
497
- if (sampleRate == 0 || audioConfig.byteLength == 0) {
491
+ if (sampleRate == 0 || audioConfig.byteLength == 0 || firstPts == null) {
498
492
  throw new shaka.util.Error(
499
493
  shaka.util.Error.Severity.CRITICAL,
500
494
  shaka.util.Error.Category.MEDIA,
@@ -503,15 +497,12 @@ shaka.transmuxer.TsTransmuxer = class {
503
497
 
504
498
 
505
499
  /** @type {number} */
506
- const baseMediaDecodeTime = Math.floor(timestamp * sampleRate / 1000);
500
+ const baseMediaDecodeTime = firstPts / timescale * sampleRate;
507
501
 
508
- let id = stream.id;
509
- if (isMuxed) {
510
- id += shaka.transmuxer.TsTransmuxer.START_ID_FOR_MUXED_CONTENT_;
511
- }
512
502
  return {
513
- id: id,
503
+ id: stream.id,
514
504
  type: shaka.util.ManifestParserUtils.ContentType.AUDIO,
505
+ codecs: 'ec-3',
515
506
  encrypted: stream.encrypted && stream.drmInfos.length > 0,
516
507
  timescale: sampleRate,
517
508
  duration: duration,
@@ -530,26 +521,30 @@ shaka.transmuxer.TsTransmuxer = class {
530
521
 
531
522
  /**
532
523
  * @param {shaka.util.TsParser} tsParser
533
- * @param {number} timestamp
534
524
  * @param {shaka.extern.Stream} stream
535
525
  * @param {number} duration
536
- * @param {boolean} isMuxed
537
526
  * @return {shaka.util.Mp4Generator.StreamInfo}
538
527
  * @private
539
528
  */
540
- getMp3StreamInfo_(tsParser, timestamp, stream, duration, isMuxed) {
529
+ getMp3StreamInfo_(tsParser, stream, duration) {
541
530
  const MpegAudio = shaka.transmuxer.MpegAudio;
531
+ const timescale = shaka.util.TsParser.Timescale;
542
532
 
543
533
  /** @type {!Array.<shaka.util.Mp4Generator.Mp4Sample>} */
544
534
  const samples = [];
545
535
 
546
536
  let firstHeader;
547
537
 
538
+ let firstPts = null;
539
+
548
540
  for (const audioData of tsParser.getAudioData()) {
549
541
  const data = audioData.data;
550
542
  if (!data) {
551
543
  continue;
552
544
  }
545
+ if (firstPts == null && audioData.pts !== null) {
546
+ firstPts = audioData.pts;
547
+ }
553
548
  let offset = 0;
554
549
  while (offset < data.length) {
555
550
  const header = MpegAudio.parseHeader(data, offset);
@@ -579,7 +574,7 @@ shaka.transmuxer.TsTransmuxer = class {
579
574
  offset += header.frameLength;
580
575
  }
581
576
  }
582
- if (!firstHeader) {
577
+ if (!firstHeader || firstPts == null) {
583
578
  throw new shaka.util.Error(
584
579
  shaka.util.Error.Severity.CRITICAL,
585
580
  shaka.util.Error.Category.MEDIA,
@@ -588,15 +583,12 @@ shaka.transmuxer.TsTransmuxer = class {
588
583
  /** @type {number} */
589
584
  const sampleRate = firstHeader.sampleRate;
590
585
  /** @type {number} */
591
- const baseMediaDecodeTime = Math.floor(timestamp * sampleRate / 1000);
586
+ const baseMediaDecodeTime = firstPts / timescale * sampleRate;
592
587
 
593
- let id = stream.id;
594
- if (isMuxed) {
595
- id += shaka.transmuxer.TsTransmuxer.START_ID_FOR_MUXED_CONTENT_;
596
- }
597
588
  return {
598
- id: id,
589
+ id: stream.id,
599
590
  type: shaka.util.ManifestParserUtils.ContentType.AUDIO,
591
+ codecs: 'mp3',
600
592
  encrypted: stream.encrypted && stream.drmInfos.length > 0,
601
593
  timescale: sampleRate,
602
594
  duration: duration,
@@ -615,13 +607,12 @@ shaka.transmuxer.TsTransmuxer = class {
615
607
 
616
608
  /**
617
609
  * @param {shaka.util.TsParser} tsParser
618
- * @param {number} timestamp
619
610
  * @param {shaka.extern.Stream} stream
620
611
  * @param {number} duration
621
612
  * @return {shaka.util.Mp4Generator.StreamInfo}
622
613
  * @private
623
614
  */
624
- getAvcStreamInfo_(tsParser, timestamp, stream, duration) {
615
+ getAvcStreamInfo_(tsParser, stream, duration) {
625
616
  const H264 = shaka.transmuxer.H264;
626
617
  const timescale = shaka.util.TsParser.Timescale;
627
618
 
@@ -632,9 +623,7 @@ shaka.transmuxer.TsTransmuxer = class {
632
623
  let baseMediaDecodeTime = null;
633
624
 
634
625
  let nalus = [];
635
- const videoData = tsParser.getVideoData().filter((pes) => {
636
- return pes.pts != null && pes.dts != null;
637
- });
626
+ const videoData = tsParser.getVideoData();
638
627
  for (let i = 0; i < videoData.length; i++) {
639
628
  const pes = videoData[i];
640
629
  let nextPes;
@@ -647,7 +636,7 @@ shaka.transmuxer.TsTransmuxer = class {
647
636
  if (!frame) {
648
637
  continue;
649
638
  }
650
- if (baseMediaDecodeTime == null && pes.dts != null) {
639
+ if (baseMediaDecodeTime == null) {
651
640
  baseMediaDecodeTime = pes.dts;
652
641
  }
653
642
  let duration;
@@ -686,6 +675,7 @@ shaka.transmuxer.TsTransmuxer = class {
686
675
  return {
687
676
  id: stream.id,
688
677
  type: shaka.util.ManifestParserUtils.ContentType.VIDEO,
678
+ codecs: 'avc1',
689
679
  encrypted: stream.encrypted && stream.drmInfos.length > 0,
690
680
  timescale: timescale,
691
681
  duration: duration,
@@ -725,12 +715,6 @@ shaka.transmuxer.TsTransmuxer.SUPPORTED_VIDEO_CODECS_ = [
725
715
  'avc',
726
716
  ];
727
717
 
728
- /**
729
- * @private
730
- * @const {number}
731
- */
732
- shaka.transmuxer.TsTransmuxer.START_ID_FOR_MUXED_CONTENT_ = 128;
733
-
734
718
 
735
719
  shaka.transmuxer.TransmuxerEngine.registerTransmuxer(
736
720
  'video/mp2t',
package/lib/util/error.js CHANGED
@@ -63,8 +63,11 @@ shaka.util.Error = class {
63
63
  */
64
64
  this.handled = false;
65
65
 
66
- // This improves the formatting of Errors in failure messages in the tests.
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
- * A human-readable version of the category and code.
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');
@@ -55,7 +55,7 @@ shaka.util.Mp4Generator = class {
55
55
  return Mp4Generator.box('moov',
56
56
  this.mvhd_(firstStreamInfo),
57
57
  traks,
58
- this.mvex_(firstStreamInfo),
58
+ this.mvex_(),
59
59
  this.pssh_(firstStreamInfo));
60
60
  }
61
61
 
@@ -130,9 +130,14 @@ shaka.util.Mp4Generator = class {
130
130
  */
131
131
  tkhd_(streamInfo) {
132
132
  const Mp4Generator = shaka.util.Mp4Generator;
133
+ const ContentType = shaka.util.ManifestParserUtils.ContentType;
133
134
  const id = streamInfo.id + 1;
134
- const width = streamInfo.stream.width || 0;
135
- const height = streamInfo.stream.height || 0;
135
+ let width = streamInfo.stream.width || 0;
136
+ let height = streamInfo.stream.height || 0;
137
+ if (streamInfo.type == ContentType.AUDIO) {
138
+ width = 0;
139
+ height = 0;
140
+ }
136
141
  const duration = streamInfo.duration * streamInfo.timescale;
137
142
  const upperWordDuration =
138
143
  Math.floor(duration / (Mp4Generator.UINT32_MAX_ + 1));
@@ -295,24 +300,18 @@ shaka.util.Mp4Generator = class {
295
300
  let bytes = new Uint8Array([]);
296
301
  switch (streamInfo.type) {
297
302
  case ContentType.VIDEO:
298
- if (streamInfo.stream.codecs.includes('avc1.') ||
299
- streamInfo.stream.codecs.includes('avc3.')) {
303
+ if (streamInfo.codecs.includes('avc1')) {
300
304
  bytes = this.avc1_(streamInfo);
301
- } else if (streamInfo.stream.codecs.includes('hev1.') ||
302
- streamInfo.stream.codecs.includes('hvc1.')) {
305
+ } else if (streamInfo.codecs.includes('hvc1')) {
303
306
  bytes = this.hvc1_(streamInfo);
304
307
  }
305
308
  break;
306
309
  case ContentType.AUDIO:
307
- if (streamInfo.stream.mimeType === 'audio/mpeg' ||
308
- streamInfo.stream.codecs.includes('mp3') ||
309
- streamInfo.stream.codecs.includes('mp4a.40.34')) {
310
+ if (streamInfo.codecs.includes('mp3')) {
310
311
  bytes = this.mp3_(streamInfo);
311
- } else if (streamInfo.stream.mimeType === 'audio/ac3' ||
312
- streamInfo.stream.codecs.includes('ac-3')) {
312
+ } else if (streamInfo.codecs.includes('ac-3')) {
313
313
  bytes = this.ac3_(streamInfo);
314
- } else if (streamInfo.stream.mimeType === 'audio/ec3' ||
315
- streamInfo.stream.codecs.includes('ec-3')) {
314
+ } else if (streamInfo.codecs.includes('ec-3')) {
316
315
  bytes = this.ec3_(streamInfo);
317
316
  } else {
318
317
  bytes = this.mp4a_(streamInfo);
@@ -630,7 +629,7 @@ shaka.util.Mp4Generator = class {
630
629
  const audioSamplingRate = streamInfo.stream.audioSamplingRate || 44100;
631
630
 
632
631
  const audioCodec = shaka.util.ManifestParserUtils.guessCodecs(
633
- ContentType.AUDIO, streamInfo.stream.codecs.split(','));
632
+ ContentType.AUDIO, streamInfo.codecs.split(','));
634
633
 
635
634
  const samplingFrequencyIndex = {
636
635
  96000: 0x0,
@@ -689,13 +688,16 @@ shaka.util.Mp4Generator = class {
689
688
  /**
690
689
  * Generate a MVEX box
691
690
  *
692
- * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
693
691
  * @return {!Uint8Array}
694
692
  * @private
695
693
  */
696
- mvex_(streamInfo) {
694
+ mvex_() {
697
695
  const Mp4Generator = shaka.util.Mp4Generator;
698
- return Mp4Generator.box('mvex', this.trex_(streamInfo));
696
+ let trexs = new Uint8Array([]);
697
+ for (const streamInfo of this.streamInfos_) {
698
+ trexs = shaka.util.Uint8ArrayUtils.concat(trexs, this.trex_(streamInfo));
699
+ }
700
+ return Mp4Generator.box('mvex', trexs);
699
701
  }
700
702
 
701
703
  /**
@@ -765,8 +767,8 @@ shaka.util.Mp4Generator = class {
765
767
  * @private
766
768
  */
767
769
  frma_(streamInfo) {
768
- const codec = streamInfo.stream.codecs.substring(
769
- 0, streamInfo.stream.codecs.indexOf('.'));
770
+ const codec = streamInfo.codecs.substring(
771
+ 0, streamInfo.codecs.indexOf('.'));
770
772
  const Mp4Generator = shaka.util.Mp4Generator;
771
773
  const codecNumber = this.stringToCharCode_(codec);
772
774
  const bytes = new Uint8Array([
@@ -1290,6 +1292,7 @@ shaka.util.Mp4Generator.DINF_ = new Uint8Array([]);
1290
1292
  * @typedef {{
1291
1293
  * id: number,
1292
1294
  * type: string,
1295
+ * codecs: string,
1293
1296
  * encrypted: boolean,
1294
1297
  * timescale: number,
1295
1298
  * duration: number,
@@ -1304,6 +1307,10 @@ shaka.util.Mp4Generator.DINF_ = new Uint8Array([]);
1304
1307
  * A unique ID
1305
1308
  * @property {string} type
1306
1309
  * Indicate the content type: 'video' or 'audio'.
1310
+ * @property {string} codecs
1311
+ * <i>Defaults to '' (i.e., unknown / not needed).</i> <br>
1312
+ * The Stream's codecs, e.g., 'avc1.4d4015' or 'vp9'<br>
1313
+ * See {@link https://tools.ietf.org/html/rfc6381}
1307
1314
  * @property {boolean} encrypted
1308
1315
  * Indicate if the stream is encrypted.
1309
1316
  * @property {number} timescale
@@ -71,8 +71,8 @@ shaka.util.PlayerConfiguration = class {
71
71
  persistentSessionOnlinePlayback: false,
72
72
  persistentSessionsMetadata: [],
73
73
  initDataTransform: (initData, initDataType, drmInfo) => {
74
- const keySystem = drmInfo.keySystem;
75
- if (keySystem == 'com.apple.fps.1_0' && initDataType == 'skd') {
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);
@@ -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.<shaka.extern.Stream>} streams
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.<!shaka.extern.Stream>}
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.<!shaka.extern.Stream>} */
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.<!shaka.extern.Stream>} */
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.filterTextStreamsByRole_(
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.filterTextStreamsByRole_(chosen, allRoles[0]);
1630
+ return shaka.util.StreamUtils.filterStreamsByRole_(chosen, allRoles[0]);
1630
1631
  }
1631
1632
 
1632
1633
 
1633
1634
  /**
1634
- * Filter text Streams by role.
1635
+ * Filter Streams by role.
1636
+ * Works both for Stream and Track types due to their similarities.
1635
1637
  *
1636
- * @param {!Array.<shaka.extern.Stream>} textStreams
1638
+ * @param {!Array<!shaka.extern.Stream>|!Array<!shaka.extern.Track>} streams
1637
1639
  * @param {string} preferredRole
1638
- * @return {!Array.<shaka.extern.Stream>}
1640
+ * @return {!Array<!shaka.extern.Stream>|!Array<!shaka.extern.Track>}
1639
1641
  * @private
1640
1642
  */
1641
- static filterTextStreamsByRole_(textStreams, preferredRole) {
1642
- return textStreams.filter((stream) => {
1643
+ static filterStreamsByRole_(streams, preferredRole) {
1644
+ return streams.filter((stream) => {
1643
1645
  return stream.roles.includes(preferredRole);
1644
1646
  });
1645
1647
  }