shaka-player 4.7.11 → 4.7.13

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 (60) hide show
  1. package/README.md +2 -4
  2. package/dist/deps.js +6 -7
  3. package/dist/locales.js +34 -34
  4. package/dist/shaka-player.compiled.d.ts +14 -12
  5. package/dist/shaka-player.compiled.debug.d.ts +14 -12
  6. package/dist/shaka-player.compiled.debug.externs.js +78 -64
  7. package/dist/shaka-player.compiled.debug.js +321 -312
  8. package/dist/shaka-player.compiled.debug.map +4 -4
  9. package/dist/shaka-player.compiled.externs.js +78 -64
  10. package/dist/shaka-player.compiled.js +1167 -1153
  11. package/dist/shaka-player.compiled.map +4 -4
  12. package/dist/shaka-player.ui.d.ts +14 -12
  13. package/dist/shaka-player.ui.debug.d.ts +14 -12
  14. package/dist/shaka-player.ui.debug.externs.js +78 -64
  15. package/dist/shaka-player.ui.debug.js +443 -434
  16. package/dist/shaka-player.ui.debug.map +4 -4
  17. package/dist/shaka-player.ui.externs.js +78 -64
  18. package/dist/shaka-player.ui.js +1319 -1305
  19. package/dist/shaka-player.ui.map +4 -4
  20. package/externs/shaka/manifest.js +11 -1
  21. package/externs/shaka/offline.js +7 -1
  22. package/externs/shaka/player.js +32 -19
  23. package/lib/abr/simple_abr_manager.js +21 -26
  24. package/lib/cast/cast_proxy.js +1 -3
  25. package/lib/cast/cast_sender.js +8 -11
  26. package/lib/cea/cea608_memory.js +1 -1
  27. package/lib/cea/ts_cea_parser.js +10 -1
  28. package/lib/dash/dash_parser.js +13 -3
  29. package/lib/dash/segment_base.js +8 -1
  30. package/lib/dash/segment_list.js +24 -18
  31. package/lib/dash/segment_template.js +21 -2
  32. package/lib/hls/hls_parser.js +36 -17
  33. package/lib/media/drm_engine.js +6 -3
  34. package/lib/media/media_source_engine.js +96 -33
  35. package/lib/media/playhead.js +3 -3
  36. package/lib/media/presentation_timeline.js +23 -0
  37. package/lib/media/segment_index.js +21 -20
  38. package/lib/media/segment_utils.js +10 -1
  39. package/lib/media/streaming_engine.js +2 -14
  40. package/lib/mss/mss_parser.js +4 -0
  41. package/lib/offline/indexeddb/v1_storage_cell.js +2 -0
  42. package/lib/offline/indexeddb/v2_storage_cell.js +2 -0
  43. package/lib/offline/manifest_converter.js +10 -3
  44. package/lib/offline/storage.js +2 -0
  45. package/lib/player.js +30 -4
  46. package/lib/polyfill/media_capabilities.js +20 -1
  47. package/lib/polyfill/mediasource.js +37 -14
  48. package/lib/text/vtt_text_parser.js +51 -51
  49. package/lib/transmuxer/h264.js +3 -3
  50. package/lib/transmuxer/h265.js +3 -3
  51. package/lib/transmuxer/ts_transmuxer.js +42 -3
  52. package/lib/util/manifest_parser_utils.js +1 -0
  53. package/lib/util/mime_utils.js +10 -5
  54. package/lib/util/mp4_parser.js +57 -0
  55. package/lib/util/periods.js +5 -37
  56. package/lib/util/platform.js +4 -0
  57. package/lib/util/stream_utils.js +334 -271
  58. package/lib/util/ts_parser.js +99 -10
  59. package/package.json +1 -1
  60. package/lib/polyfill/storage_estimate.js +0 -57
@@ -364,115 +364,122 @@ shaka.util.StreamUtils = class {
364
364
  }
365
365
  }
366
366
 
367
+ const StreamUtils = shaka.util.StreamUtils;
368
+
369
+ manifest.variants = manifest.variants.filter((variant) => {
370
+ const supported = StreamUtils.checkVariantSupported_(variant, keySystem);
371
+ // Filter out all unsupported variants.
372
+ if (!supported) {
373
+ shaka.log.debug('Dropping variant - not compatible with platform',
374
+ StreamUtils.getVariantSummaryString_(variant));
375
+ }
376
+ return supported;
377
+ });
378
+ }
379
+
380
+
381
+ /**
382
+ * @param {!shaka.extern.Variant} variant
383
+ * @param {?string} keySystem
384
+ * @return {boolean}
385
+ * @private
386
+ */
387
+ static checkVariantSupported_(variant, keySystem) {
367
388
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
368
389
  const Capabilities = shaka.media.Capabilities;
369
390
  const ManifestParserUtils = shaka.util.ManifestParserUtils;
370
391
  const MimeUtils = shaka.util.MimeUtils;
371
392
  const StreamUtils = shaka.util.StreamUtils;
372
- const isXboxOne = shaka.util.Platform.isXboxOne();
373
393
 
394
+ const isXboxOne = shaka.util.Platform.isXboxOne();
374
395
  const isFirefoxAndroid = shaka.util.Platform.isFirefox() &&
375
396
  shaka.util.Platform.isAndroid();
376
397
 
377
- manifest.variants = manifest.variants.filter((variant) => {
378
- // See: https://github.com/shaka-project/shaka-player/issues/3860
379
- const video = variant.video;
380
- const videoWidth = (video && video.width) || 0;
381
- const videoHeight = (video && video.height) || 0;
382
-
383
- // See: https://github.com/shaka-project/shaka-player/issues/3380
384
- // Note: it makes sense to drop early
385
- if (isXboxOne && video &&
386
- (videoWidth > 1920 || videoHeight > 1080) &&
387
- (video.codecs.includes('avc1.') || video.codecs.includes('avc3.'))
388
- ) {
389
- shaka.log.debug('Dropping variant - not compatible with platform',
390
- StreamUtils.getVariantSummaryString_(variant));
391
- return false;
392
- }
393
-
394
- if (video) {
395
- let videoCodecs = StreamUtils.getCorrectVideoCodecs_(video.codecs);
396
- // For multiplexed streams. Here we must check the audio of the
397
- // stream to see if it is compatible.
398
- if (video.codecs.includes(',')) {
399
- const allCodecs = video.codecs.split(',');
400
-
401
- videoCodecs = ManifestParserUtils.guessCodecs(
402
- ContentType.VIDEO, allCodecs);
403
- videoCodecs = StreamUtils.getCorrectVideoCodecs_(videoCodecs);
404
- let audioCodecs = ManifestParserUtils.guessCodecs(
405
- ContentType.AUDIO, allCodecs);
406
- audioCodecs = StreamUtils.getCorrectAudioCodecs_(audioCodecs);
398
+ // See: https://github.com/shaka-project/shaka-player/issues/3860
399
+ const video = variant.video;
400
+ const videoWidth = (video && video.width) || 0;
401
+ const videoHeight = (video && video.height) || 0;
407
402
 
408
- const audioFullType = MimeUtils.getFullOrConvertedType(
409
- video.mimeType, audioCodecs, ContentType.AUDIO);
403
+ // See: https://github.com/shaka-project/shaka-player/issues/3380
404
+ // Note: it makes sense to drop early
405
+ if (isXboxOne && video && (videoWidth > 1920 || videoHeight > 1080) &&
406
+ (video.codecs.includes('avc1.') || video.codecs.includes('avc3.'))) {
407
+ return false;
408
+ }
410
409
 
411
- if (!Capabilities.isTypeSupported(audioFullType)) {
412
- return false;
413
- }
410
+ if (video) {
411
+ let videoCodecs = StreamUtils.getCorrectVideoCodecs(video.codecs);
412
+ // For multiplexed streams. Here we must check the audio of the
413
+ // stream to see if it is compatible.
414
+ if (video.codecs.includes(',')) {
415
+ const allCodecs = video.codecs.split(',');
414
416
 
415
- // Update the codec string with the (possibly) converted codecs.
416
- videoCodecs = [videoCodecs, audioCodecs].join(',');
417
- }
417
+ videoCodecs = ManifestParserUtils.guessCodecs(
418
+ ContentType.VIDEO, allCodecs);
419
+ videoCodecs = StreamUtils.getCorrectVideoCodecs(videoCodecs);
420
+ let audioCodecs = ManifestParserUtils.guessCodecs(
421
+ ContentType.AUDIO, allCodecs);
422
+ audioCodecs = StreamUtils.getCorrectAudioCodecs(audioCodecs);
418
423
 
419
- const fullType = MimeUtils.getFullOrConvertedType(
420
- video.mimeType, videoCodecs, ContentType.VIDEO);
424
+ const audioFullType = MimeUtils.getFullOrConvertedType(
425
+ video.mimeType, audioCodecs, ContentType.AUDIO);
421
426
 
422
- if (!Capabilities.isTypeSupported(fullType)) {
427
+ if (!Capabilities.isTypeSupported(audioFullType)) {
423
428
  return false;
424
429
  }
425
430
 
426
431
  // Update the codec string with the (possibly) converted codecs.
427
- video.codecs = videoCodecs;
432
+ videoCodecs = [videoCodecs, audioCodecs].join(',');
428
433
  }
429
434
 
430
- const audio = variant.audio;
435
+ const fullType = MimeUtils.getFullOrConvertedType(
436
+ video.mimeType, videoCodecs, ContentType.VIDEO);
431
437
 
432
- // See: https://github.com/shaka-project/shaka-player/issues/6111
433
- // It seems that Firefox Android reports that it supports
434
- // Opus + Widevine, but it is not actually supported.
435
- // It makes sense to drop early.
436
- if (isFirefoxAndroid && audio && audio.encrypted &&
437
- audio.codecs.toLowerCase().includes('opus')) {
438
- shaka.log.debug('Dropping variant - not compatible with platform',
439
- StreamUtils.getVariantSummaryString_(variant));
438
+ if (!Capabilities.isTypeSupported(fullType)) {
440
439
  return false;
441
440
  }
442
441
 
443
- if (audio) {
444
- const codecs = StreamUtils.getCorrectAudioCodecs_(audio.codecs);
445
- const fullType = MimeUtils.getFullOrConvertedType(
446
- audio.mimeType, codecs, ContentType.AUDIO);
442
+ // Update the codec string with the (possibly) converted codecs.
443
+ video.codecs = videoCodecs;
444
+ }
447
445
 
448
- if (!Capabilities.isTypeSupported(fullType)) {
449
- return false;
450
- }
446
+ const audio = variant.audio;
451
447
 
452
- // Update the codec string with the (possibly) converted codecs.
453
- audio.codecs = codecs;
448
+ // See: https://github.com/shaka-project/shaka-player/issues/6111
449
+ // It seems that Firefox Android reports that it supports
450
+ // Opus + Widevine, but it is not actually supported.
451
+ // It makes sense to drop early.
452
+ if (isFirefoxAndroid && audio && audio.encrypted &&
453
+ audio.codecs.toLowerCase().includes('opus')) {
454
+ return false;
455
+ }
456
+
457
+ if (audio) {
458
+ const codecs = StreamUtils.getCorrectAudioCodecs(audio.codecs);
459
+ const fullType = MimeUtils.getFullOrConvertedType(
460
+ audio.mimeType, codecs, ContentType.AUDIO);
461
+
462
+ if (!Capabilities.isTypeSupported(fullType)) {
463
+ return false;
454
464
  }
455
465
 
456
- const supported = variant.decodingInfos.some((decodingInfo) => {
457
- if (!decodingInfo.supported) {
458
- return false;
459
- }
460
- if (keySystem) {
461
- const keySystemAccess = decodingInfo.keySystemAccess;
462
- if (keySystemAccess) {
463
- if (keySystemAccess.keySystem != keySystem) {
464
- return false;
465
- }
466
+ // Update the codec string with the (possibly) converted codecs.
467
+ audio.codecs = codecs;
468
+ }
469
+
470
+ return variant.decodingInfos.some((decodingInfo) => {
471
+ if (!decodingInfo.supported) {
472
+ return false;
473
+ }
474
+ if (keySystem) {
475
+ const keySystemAccess = decodingInfo.keySystemAccess;
476
+ if (keySystemAccess) {
477
+ if (keySystemAccess.keySystem != keySystem) {
478
+ return false;
466
479
  }
467
480
  }
468
- return true;
469
- });
470
- // Filter out all unsupported variants.
471
- if (!supported) {
472
- shaka.log.debug('Dropping variant - not compatible with platform',
473
- StreamUtils.getVariantSummaryString_(variant));
474
481
  }
475
- return supported;
482
+ return true;
476
483
  });
477
484
  }
478
485
 
@@ -516,51 +523,69 @@ shaka.util.StreamUtils = class {
516
523
  * and assigns it to the given variant.
517
524
  * If that query has been done before, instead return a cached result.
518
525
  * @param {!shaka.extern.Variant} variant
519
- * @param {!MediaDecodingConfiguration} decodingConfig
526
+ * @param {!Array.<!MediaDecodingConfiguration>} decodingConfigs
520
527
  * @private
521
528
  */
522
- static async getDecodingInfosForVariant_(variant, decodingConfig) {
523
- const cacheKey =
524
- shaka.util.StreamUtils.alphabeticalKeyOrderStringify_(decodingConfig);
525
-
526
- const cache = shaka.util.StreamUtils.decodingConfigCache_;
527
- if (cache[cacheKey]) {
528
- shaka.log.v2('Using cached results of mediaCapabilities.decodingInfo',
529
- 'for key', cacheKey);
530
- variant.decodingInfos.push(cache[cacheKey]);
531
- } else {
532
- // Do a final pass-over of the decoding config: if a given stream has
533
- // multiple codecs, that suggests that it switches between those codecs
534
- // at points of the go-through.
535
- // mediaCapabilities by itself will report "not supported" when you
536
- // put in multiple different codecs, so each has to be checked
537
- // individually. So check each and take the worst result, to determine
538
- // overall variant compatibility.
539
- const results = await shaka.util.StreamUtils
540
- .checkEachDecodingConfigCombination_(decodingConfig);
541
- if (results && results.length) {
542
- /** @type {?MediaCapabilitiesDecodingInfo} */
543
- let overallResult = null;
544
- for (const result of results) {
545
- if (!overallResult) {
546
- overallResult = result;
547
- } else {
548
- overallResult.supported =
549
- overallResult.supported && result.supported;
550
- overallResult.powerEfficient =
551
- overallResult.powerEfficient && result.powerEfficient;
552
- overallResult.smooth = overallResult.smooth && result.smooth;
553
- if (!result.keySystemAccess) {
554
- overallResult.keySystemAccess = null;
555
- }
556
- }
557
- }
558
- if (overallResult) {
559
- cache[cacheKey] = overallResult;
560
- variant.decodingInfos.push(overallResult);
529
+ static async getDecodingInfosForVariant_(variant, decodingConfigs) {
530
+ /**
531
+ * @param {?MediaCapabilitiesDecodingInfo} a
532
+ * @param {!MediaCapabilitiesDecodingInfo} b
533
+ * @return {!MediaCapabilitiesDecodingInfo}
534
+ */
535
+ const merge = (a, b) => {
536
+ if (!a) {
537
+ return b;
538
+ } else {
539
+ const res = shaka.util.ObjectUtils.cloneObject(a);
540
+ res.supported = a.supported && b.supported;
541
+ res.powerEfficient = a.powerEfficient && b.powerEfficient;
542
+ res.smooth = a.smooth && b.smooth;
543
+ if (b.keySystemAccess && !res.keySystemAccess) {
544
+ res.keySystemAccess = b.keySystemAccess;
561
545
  }
546
+ return res;
547
+ }
548
+ };
549
+
550
+ const StreamUtils = shaka.util.StreamUtils;
551
+ /** @type {?MediaCapabilitiesDecodingInfo} */
552
+ let finalResult = null;
553
+ const promises = [];
554
+ for (const decodingConfig of decodingConfigs) {
555
+ const cacheKey =
556
+ StreamUtils.alphabeticalKeyOrderStringify_(decodingConfig);
557
+
558
+ const cache = StreamUtils.decodingConfigCache_;
559
+ if (cache[cacheKey]) {
560
+ shaka.log.v2('Using cached results of mediaCapabilities.decodingInfo',
561
+ 'for key', cacheKey);
562
+ finalResult = merge(finalResult, cache[cacheKey]);
563
+ } else {
564
+ // Do a final pass-over of the decoding config: if a given stream has
565
+ // multiple codecs, that suggests that it switches between those codecs
566
+ // at points of the go-through.
567
+ // mediaCapabilities by itself will report "not supported" when you
568
+ // put in multiple different codecs, so each has to be checked
569
+ // individually. So check each and take the worst result, to determine
570
+ // overall variant compatibility.
571
+ promises.push(StreamUtils
572
+ .checkEachDecodingConfigCombination_(decodingConfig).then((res) => {
573
+ /** @type {?MediaCapabilitiesDecodingInfo} */
574
+ let acc = null;
575
+ for (const result of (res || [])) {
576
+ acc = merge(acc, result);
577
+ }
578
+ if (acc) {
579
+ cache[cacheKey] = acc;
580
+ finalResult = merge(finalResult, acc);
581
+ }
582
+ }));
562
583
  }
563
584
  }
585
+ await Promise.all(promises);
586
+ if (finalResult) {
587
+ variant.decodingInfos.push(finalResult);
588
+ }
564
589
  }
565
590
 
566
591
  /**
@@ -634,10 +659,12 @@ shaka.util.StreamUtils = class {
634
659
  for (const preferredKeySystem of preferredKeySystems) {
635
660
  let keySystemSatisfied = false;
636
661
  for (const variant of variants) {
637
- /** @type {!Array.<!MediaDecodingConfiguration>} */
662
+ /** @type {!Array.<!Array.<!MediaDecodingConfiguration>>} */
638
663
  const decodingConfigs = shaka.util.StreamUtils.getDecodingConfigs_(
639
664
  variant, usePersistentLicenses, srcEquals)
640
- .filter((config) => {
665
+ .filter((configs) => {
666
+ // All configs in a batch will have the same keySystem.
667
+ const config = configs[0];
641
668
  const keySystem = config.keySystemConfiguration &&
642
669
  config.keySystemConfiguration.keySystem;
643
670
  return keySystem === preferredKeySystem;
@@ -646,10 +673,10 @@ shaka.util.StreamUtils = class {
646
673
  // The reason we are performing this await in a loop rather than
647
674
  // batching into a `promise.all` is performance related.
648
675
  // https://github.com/shaka-project/shaka-player/pull/4708#discussion_r1022581178
649
- for (const config of decodingConfigs) {
676
+ for (const configs of decodingConfigs) {
650
677
  // eslint-disable-next-line no-await-in-loop
651
678
  await shaka.util.StreamUtils.getDecodingInfosForVariant_(
652
- variant, config);
679
+ variant, configs);
653
680
  }
654
681
  if (variant.decodingInfos.length) {
655
682
  keySystemSatisfied = true;
@@ -662,10 +689,12 @@ shaka.util.StreamUtils = class {
662
689
  } // for (const preferredKeySystem of preferredKeySystems)
663
690
 
664
691
  for (const variant of variants) {
665
- /** @type {!Array.<!MediaDecodingConfiguration>} */
692
+ /** @type {!Array.<!Array.<!MediaDecodingConfiguration>>} */
666
693
  const decodingConfigs = shaka.util.StreamUtils.getDecodingConfigs_(
667
694
  variant, usePersistentLicenses, srcEquals)
668
- .filter((config) => {
695
+ .filter((configs) => {
696
+ // All configs in a batch will have the same keySystem.
697
+ const config = configs[0];
669
698
  const keySystem = config.keySystemConfiguration &&
670
699
  config.keySystemConfiguration.keySystem;
671
700
  // Avoid checking preferred systems twice.
@@ -675,22 +704,24 @@ shaka.util.StreamUtils = class {
675
704
  // The reason we are performing this await in a loop rather than
676
705
  // batching into a `promise.all` is performance related.
677
706
  // https://github.com/shaka-project/shaka-player/pull/4708#discussion_r1022581178
678
- for (const config of decodingConfigs) {
707
+ for (const configs of decodingConfigs) {
679
708
  // eslint-disable-next-line no-await-in-loop
680
709
  await shaka.util.StreamUtils.getDecodingInfosForVariant_(
681
- variant, config);
710
+ variant, configs);
682
711
  }
683
712
  }
684
713
  }
685
714
 
686
715
 
687
716
  /**
688
- * Generate a MediaDecodingConfiguration object to get the decodingInfo
689
- * results for each variant.
717
+ * Generate a batch of MediaDecodingConfiguration objects to get the
718
+ * decodingInfo results for each variant.
719
+ * Each batch shares the same DRM information, and represents the various
720
+ * fullMimeType combinations of the streams.
690
721
  * @param {!shaka.extern.Variant} variant
691
722
  * @param {boolean} usePersistentLicenses
692
723
  * @param {boolean} srcEquals
693
- * @return {!Array.<!MediaDecodingConfiguration>}
724
+ * @return {!Array.<!Array.<!MediaDecodingConfiguration>>}
694
725
  * @private
695
726
  */
696
727
  static getDecodingConfigs_(variant, usePersistentLicenses, srcEquals) {
@@ -702,87 +733,118 @@ shaka.util.StreamUtils = class {
702
733
  const MimeUtils = shaka.util.MimeUtils;
703
734
  const StreamUtils = shaka.util.StreamUtils;
704
735
 
705
- /** @type {!MediaDecodingConfiguration} */
706
- const mediaDecodingConfig = {
707
- type: srcEquals ? 'file' : 'media-source',
708
- };
709
-
736
+ const videoConfigs = [];
737
+ const audioConfigs = [];
710
738
  if (video) {
711
- let videoCodecs = video.codecs;
739
+ for (const fullMimeType of video.fullMimeTypes) {
740
+ let videoCodecs = MimeUtils.getCodecs(fullMimeType);
712
741
 
713
- // For multiplexed streams with audio+video codecs, the config should have
714
- // AudioConfiguration and VideoConfiguration.
715
- if (video.codecs.includes(',')) {
716
- const allCodecs = video.codecs.split(',');
742
+ // For multiplexed streams with audio+video codecs, the config should
743
+ // have AudioConfiguration and VideoConfiguration.
744
+ // We ignore the multiplexed audio when there is normal audio also.
745
+ if (videoCodecs.includes(',') && !audio) {
746
+ const allCodecs = videoCodecs.split(',');
717
747
 
718
- videoCodecs = ManifestParserUtils.guessCodecs(
719
- ContentType.VIDEO, allCodecs);
720
- videoCodecs = StreamUtils.getCorrectVideoCodecs_(videoCodecs);
748
+ videoCodecs = ManifestParserUtils.guessCodecs(
749
+ ContentType.VIDEO, allCodecs);
721
750
 
722
- let audioCodecs = ManifestParserUtils.guessCodecs(
723
- ContentType.AUDIO, allCodecs);
724
- audioCodecs = StreamUtils.getCorrectAudioCodecs_(audioCodecs);
751
+ let audioCodecs = ManifestParserUtils.guessCodecs(
752
+ ContentType.AUDIO, allCodecs);
753
+ audioCodecs = StreamUtils.getCorrectAudioCodecs(audioCodecs);
725
754
 
726
- const audioFullType = MimeUtils.getFullOrConvertedType(
727
- video.mimeType, audioCodecs, ContentType.AUDIO);
755
+ const audioFullType = MimeUtils.getFullOrConvertedType(
756
+ MimeUtils.getBasicType(fullMimeType), audioCodecs,
757
+ ContentType.AUDIO);
758
+
759
+ audioConfigs.push({
760
+ contentType: audioFullType,
761
+ channels: 2,
762
+ bitrate: variant.bandwidth || 1,
763
+ samplerate: 1,
764
+ spatialRendering: false,
765
+ });
766
+ }
728
767
 
729
- mediaDecodingConfig.audio = {
730
- contentType: audioFullType,
731
- channels: 2,
732
- bitrate: variant.bandwidth || 1,
733
- samplerate: 1,
734
- spatialRendering: false,
768
+ videoCodecs = StreamUtils.getCorrectVideoCodecs(videoCodecs);
769
+ const fullType = MimeUtils.getFullOrConvertedType(
770
+ MimeUtils.getBasicType(fullMimeType), videoCodecs,
771
+ ContentType.VIDEO);
772
+
773
+ // VideoConfiguration
774
+ const videoConfig = {
775
+ contentType: fullType,
776
+
777
+ // NOTE: Some decoders strictly check the width and height fields and
778
+ // won't decode smaller than 64x64. So if we don't have this info (as
779
+ // is the case in some of our simpler tests), assume a 64x64
780
+ // resolution to fill in this required field for MediaCapabilities.
781
+ //
782
+ // This became an issue specifically on Firefox on M1 Macs.
783
+ width: video.width || 64,
784
+ height: video.height || 64,
785
+
786
+ bitrate: video.bandwidth || variant.bandwidth || 1,
787
+ // framerate must be greater than 0, otherwise the config is invalid.
788
+ framerate: video.frameRate || 1,
735
789
  };
736
- }
737
-
738
- videoCodecs = StreamUtils.getCorrectVideoCodecs_(videoCodecs);
739
- const fullType = MimeUtils.getFullOrConvertedType(
740
- video.mimeType, videoCodecs, ContentType.VIDEO);
741
-
742
- // VideoConfiguration
743
- mediaDecodingConfig.video = {
744
- contentType: fullType,
745
-
746
- // NOTE: Some decoders strictly check the width and height fields and
747
- // won't decode smaller than 64x64. So if we don't have this info (as
748
- // is the case in some of our simpler tests), assume a 64x64 resolution
749
- // to fill in this required field for MediaCapabilities.
750
- //
751
- // This became an issue specifically on Firefox on M1 Macs.
752
- width: video.width || 64,
753
- height: video.height || 64,
754
-
755
- bitrate: video.bandwidth || variant.bandwidth || 1,
756
- // framerate must be greater than 0, otherwise the config is invalid.
757
- framerate: video.frameRate || 1,
758
- };
759
- if (video.hdr) {
760
- switch (video.hdr) {
761
- case 'SDR':
762
- mediaDecodingConfig.video.transferFunction = 'srgb';
763
- break;
764
- case 'PQ':
765
- mediaDecodingConfig.video.transferFunction = 'pq';
766
- break;
767
- case 'HLG':
768
- mediaDecodingConfig.video.transferFunction = 'hlg';
769
- break;
790
+ if (video.hdr) {
791
+ switch (video.hdr) {
792
+ case 'SDR':
793
+ videoConfig.transferFunction = 'srgb';
794
+ break;
795
+ case 'PQ':
796
+ videoConfig.transferFunction = 'pq';
797
+ break;
798
+ case 'HLG':
799
+ videoConfig.transferFunction = 'hlg';
800
+ break;
801
+ }
770
802
  }
803
+ videoConfigs.push(videoConfig);
771
804
  }
772
805
  }
773
806
  if (audio) {
774
- const codecs = StreamUtils.getCorrectAudioCodecs_(audio.codecs);
775
- const fullType = MimeUtils.getFullOrConvertedType(
776
- audio.mimeType, codecs, ContentType.AUDIO);
807
+ for (const fullMimeType of audio.fullMimeTypes) {
808
+ const codecs = StreamUtils.getCorrectAudioCodecs(MimeUtils.getCodecs(
809
+ fullMimeType));
810
+ const fullType = MimeUtils.getFullOrConvertedType(
811
+ MimeUtils.getBasicType(fullMimeType), codecs, ContentType.AUDIO);
812
+
813
+ // AudioConfiguration
814
+ audioConfigs.push({
815
+ contentType: fullType,
816
+ channels: audio.channelsCount || 2,
817
+ bitrate: audio.bandwidth || variant.bandwidth || 1,
818
+ samplerate: audio.audioSamplingRate || 1,
819
+ spatialRendering: audio.spatialAudio,
820
+ });
821
+ }
822
+ }
777
823
 
778
- // AudioConfiguration
779
- mediaDecodingConfig.audio = {
780
- contentType: fullType,
781
- channels: audio.channelsCount || 2,
782
- bitrate: audio.bandwidth || variant.bandwidth || 1,
783
- samplerate: audio.audioSamplingRate || 1,
784
- spatialRendering: audio.spatialAudio,
785
- };
824
+ // Generate each combination of video and audio config as a separate
825
+ // MediaDecodingConfiguration, inside the main "batch".
826
+ /** @type {!Array.<!MediaDecodingConfiguration>} */
827
+ const mediaDecodingConfigBatch = [];
828
+ if (videoConfigs.length == 0) {
829
+ videoConfigs.push(null);
830
+ }
831
+ if (audioConfigs.length == 0) {
832
+ audioConfigs.push(null);
833
+ }
834
+ for (const videoConfig of videoConfigs) {
835
+ for (const audioConfig of audioConfigs) {
836
+ /** @type {!MediaDecodingConfiguration} */
837
+ const mediaDecodingConfig = {
838
+ type: srcEquals ? 'file' : 'media-source',
839
+ };
840
+ if (videoConfig) {
841
+ mediaDecodingConfig.video = videoConfig;
842
+ }
843
+ if (audioConfig) {
844
+ mediaDecodingConfig.audio = audioConfig;
845
+ }
846
+ mediaDecodingConfigBatch.push(mediaDecodingConfig);
847
+ }
786
848
  }
787
849
 
788
850
  const videoDrmInfos = variant.video ? variant.video.drmInfos : [];
@@ -791,7 +853,7 @@ shaka.util.StreamUtils = class {
791
853
 
792
854
  // Return a list containing the mediaDecodingConfig for unencrypted variant.
793
855
  if (!allDrmInfos.length) {
794
- return [mediaDecodingConfig];
856
+ return [mediaDecodingConfigBatch];
795
857
  }
796
858
 
797
859
  // A list of MediaDecodingConfiguration objects created for the variant.
@@ -813,83 +875,87 @@ shaka.util.StreamUtils = class {
813
875
  usePersistentLicenses ? ['persistent-license'] : ['temporary'];
814
876
 
815
877
  for (const keySystem of drmInfoByKeySystems.keys()) {
816
- // Create a copy of the mediaDecodingConfig.
817
- const config = /** @type {!MediaDecodingConfiguration} */
818
- (Object.assign({}, mediaDecodingConfig));
819
-
820
- const drmInfos = drmInfoByKeySystems.get(keySystem);
821
-
822
- /** @type {!MediaCapabilitiesKeySystemConfiguration} */
823
- const keySystemConfig = {
824
- keySystem: keySystem,
825
- initDataType: 'cenc',
826
- persistentState: persistentState,
827
- distinctiveIdentifier: 'optional',
828
- sessionTypes: sessionTypes,
829
- };
830
-
831
- for (const info of drmInfos) {
832
- if (info.initData && info.initData.length) {
833
- const initDataTypes = new Set();
834
- for (const initData of info.initData) {
835
- initDataTypes.add(initData.initDataType);
836
- }
837
- if (initDataTypes.size > 1) {
838
- shaka.log.v2('DrmInfo contains more than one initDataType,',
839
- 'and we use the initDataType of the first initData.',
840
- info);
878
+ const modifiedMediaDecodingConfigBatch = [];
879
+ for (const base of mediaDecodingConfigBatch) {
880
+ // Create a copy of the mediaDecodingConfig.
881
+ const config = /** @type {!MediaDecodingConfiguration} */
882
+ (Object.assign({}, base));
883
+
884
+ const drmInfos = drmInfoByKeySystems.get(keySystem);
885
+
886
+ /** @type {!MediaCapabilitiesKeySystemConfiguration} */
887
+ const keySystemConfig = {
888
+ keySystem: keySystem,
889
+ initDataType: 'cenc',
890
+ persistentState: persistentState,
891
+ distinctiveIdentifier: 'optional',
892
+ sessionTypes: sessionTypes,
893
+ };
894
+
895
+ for (const info of drmInfos) {
896
+ if (info.initData && info.initData.length) {
897
+ const initDataTypes = new Set();
898
+ for (const initData of info.initData) {
899
+ initDataTypes.add(initData.initDataType);
900
+ }
901
+ if (initDataTypes.size > 1) {
902
+ shaka.log.v2('DrmInfo contains more than one initDataType,',
903
+ 'and we use the initDataType of the first initData.',
904
+ info);
905
+ }
906
+ keySystemConfig.initDataType = info.initData[0].initDataType;
841
907
  }
842
- keySystemConfig.initDataType = info.initData[0].initDataType;
843
- }
844
908
 
845
- if (info.distinctiveIdentifierRequired) {
846
- keySystemConfig.distinctiveIdentifier = 'required';
847
- }
848
- if (info.persistentStateRequired) {
849
- keySystemConfig.persistentState = 'required';
850
- }
851
- if (info.sessionType) {
852
- keySystemConfig.sessionTypes = [info.sessionType];
853
- }
909
+ if (info.distinctiveIdentifierRequired) {
910
+ keySystemConfig.distinctiveIdentifier = 'required';
911
+ }
912
+ if (info.persistentStateRequired) {
913
+ keySystemConfig.persistentState = 'required';
914
+ }
915
+ if (info.sessionType) {
916
+ keySystemConfig.sessionTypes = [info.sessionType];
917
+ }
854
918
 
855
- if (audio) {
856
- // See: https://github.com/shaka-project/shaka-player/issues/4659
857
- if (info.audioRobustness != '') {
858
- if (!keySystemConfig.audio) {
919
+ if (audio) {
920
+ // See: https://github.com/shaka-project/shaka-player/issues/4659
921
+ if (info.audioRobustness != '') {
922
+ if (!keySystemConfig.audio) {
923
+ // KeySystemTrackConfiguration
924
+ keySystemConfig.audio = {
925
+ robustness: info.audioRobustness,
926
+ };
927
+ } else {
928
+ keySystemConfig.audio.robustness =
929
+ keySystemConfig.audio.robustness || info.audioRobustness;
930
+ }
931
+ } else if (!keySystemConfig.audio) {
859
932
  // KeySystemTrackConfiguration
860
- keySystemConfig.audio = {
861
- robustness: info.audioRobustness,
862
- };
863
- } else {
864
- keySystemConfig.audio.robustness =
865
- keySystemConfig.audio.robustness || info.audioRobustness;
933
+ keySystemConfig.audio = {};
866
934
  }
867
- } else if (!keySystemConfig.audio) {
868
- // KeySystemTrackConfiguration
869
- keySystemConfig.audio = {};
870
935
  }
871
- }
872
936
 
873
- if (video) {
874
- // See: https://github.com/shaka-project/shaka-player/issues/4659
875
- if (info.videoRobustness != '') {
876
- if (!keySystemConfig.video) {
937
+ if (video) {
938
+ // See: https://github.com/shaka-project/shaka-player/issues/4659
939
+ if (info.videoRobustness != '') {
940
+ if (!keySystemConfig.video) {
941
+ // KeySystemTrackConfiguration
942
+ keySystemConfig.video = {
943
+ robustness: info.videoRobustness,
944
+ };
945
+ } else {
946
+ keySystemConfig.video.robustness =
947
+ keySystemConfig.video.robustness || info.videoRobustness;
948
+ }
949
+ } else if (!keySystemConfig.video) {
877
950
  // KeySystemTrackConfiguration
878
- keySystemConfig.video = {
879
- robustness: info.videoRobustness,
880
- };
881
- } else {
882
- keySystemConfig.video.robustness =
883
- keySystemConfig.video.robustness || info.videoRobustness;
951
+ keySystemConfig.video = {};
884
952
  }
885
- } else if (!keySystemConfig.video) {
886
- // KeySystemTrackConfiguration
887
- keySystemConfig.video = {};
888
953
  }
889
954
  }
955
+ config.keySystemConfiguration = keySystemConfig;
956
+ modifiedMediaDecodingConfigBatch.push(config);
890
957
  }
891
- config.keySystemConfiguration = keySystemConfig;
892
- configs.push(config);
958
+ configs.push(modifiedMediaDecodingConfigBatch);
893
959
  }
894
960
  return configs;
895
961
  }
@@ -900,9 +966,8 @@ shaka.util.StreamUtils = class {
900
966
  * for MediaSource.isTypeSupported.
901
967
  * @param {string} codecs
902
968
  * @return {string}
903
- * @private
904
969
  */
905
- static getCorrectAudioCodecs_(codecs) {
970
+ static getCorrectAudioCodecs(codecs) {
906
971
  // According to RFC 6381 section 3.3, 'fLaC' is actually the correct
907
972
  // codec string. We still need to map it to 'flac', as some browsers
908
973
  // currently don't support 'fLaC', while 'flac' is supported by most
@@ -926,9 +991,8 @@ shaka.util.StreamUtils = class {
926
991
  * for MediaSource.isTypeSupported.
927
992
  * @param {string} codec
928
993
  * @return {string}
929
- * @private
930
994
  */
931
- static getCorrectVideoCodecs_(codec) {
995
+ static getCorrectVideoCodecs(codec) {
932
996
  if (codec.includes('avc1')) {
933
997
  // Convert avc1 codec string from RFC-4281 to RFC-6381 for
934
998
  // MediaSource.isTypeSupported
@@ -1390,8 +1454,6 @@ shaka.util.StreamUtils = class {
1390
1454
  * @return {shaka.extern.Track}
1391
1455
  */
1392
1456
  static html5TextTrackToTrack(textTrack) {
1393
- const CLOSED_CAPTION_MIMETYPE =
1394
- shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE;
1395
1457
  const StreamUtils = shaka.util.StreamUtils;
1396
1458
 
1397
1459
  /** @type {shaka.extern.Track} */
@@ -1400,7 +1462,8 @@ shaka.util.StreamUtils = class {
1400
1462
  track.type = 'text';
1401
1463
  track.originalTextId = textTrack.id;
1402
1464
  if (textTrack.kind == 'captions') {
1403
- track.mimeType = CLOSED_CAPTION_MIMETYPE;
1465
+ // See: https://github.com/shaka-project/shaka-player/issues/6233
1466
+ track.mimeType = 'unknown';
1404
1467
  }
1405
1468
  if (textTrack.kind == 'subtitles') {
1406
1469
  track.mimeType = 'text/vtt';