shaka-player 4.4.2 → 4.5.0

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 (202) hide show
  1. package/.github/workflows/release-please.yaml +2 -27
  2. package/CHANGELOG.md +67 -25
  3. package/README.md +16 -10
  4. package/demo/asset_card.js +18 -21
  5. package/demo/cast_receiver/index.html +0 -1
  6. package/demo/close_button.js +0 -3
  7. package/demo/common/asset.js +27 -5
  8. package/demo/common/assets.js +150 -52
  9. package/demo/config.js +192 -253
  10. package/demo/custom.js +39 -98
  11. package/demo/front.js +7 -10
  12. package/demo/index.html +19 -22
  13. package/demo/input.js +6 -11
  14. package/demo/input_container.js +8 -9
  15. package/demo/main.js +52 -124
  16. package/demo/search.js +23 -35
  17. package/demo/service_worker.js +0 -3
  18. package/demo/tooltip.js +3 -5
  19. package/demo/visualizer.js +3 -5
  20. package/demo/visualizer_button.js +1 -20
  21. package/dist/demo.compiled.debug.js +187 -211
  22. package/dist/demo.compiled.debug.map +4 -4
  23. package/dist/demo.compiled.js +187 -211
  24. package/dist/demo.compiled.map +4 -4
  25. package/dist/deps.js +27 -24
  26. package/dist/locales.js +31 -31
  27. package/dist/receiver.compiled.debug.js +42 -61
  28. package/dist/receiver.compiled.debug.map +4 -4
  29. package/dist/receiver.compiled.js +42 -61
  30. package/dist/receiver.compiled.map +4 -4
  31. package/dist/shaka-player.compiled.d.ts +728 -365
  32. package/dist/shaka-player.compiled.debug.d.ts +728 -365
  33. package/dist/shaka-player.compiled.debug.externs.js +475 -51
  34. package/dist/shaka-player.compiled.debug.js +576 -480
  35. package/dist/shaka-player.compiled.debug.map +4 -4
  36. package/dist/shaka-player.compiled.externs.js +475 -51
  37. package/dist/shaka-player.compiled.js +1117 -1056
  38. package/dist/shaka-player.compiled.map +4 -4
  39. package/dist/shaka-player.ui.d.ts +783 -410
  40. package/dist/shaka-player.ui.debug.d.ts +783 -410
  41. package/dist/shaka-player.ui.debug.externs.js +496 -51
  42. package/dist/shaka-player.ui.debug.js +647 -542
  43. package/dist/shaka-player.ui.debug.map +4 -4
  44. package/dist/shaka-player.ui.externs.js +496 -51
  45. package/dist/shaka-player.ui.js +1260 -1194
  46. package/dist/shaka-player.ui.map +4 -4
  47. package/docs/design/lcevc-integration.md +14 -14
  48. package/docs/tutorials/faq.md +27 -55
  49. package/docs/tutorials/index.json +2 -1
  50. package/docs/tutorials/lcevc.md +118 -0
  51. package/docs/tutorials/offline.md +1 -1
  52. package/docs/tutorials/ui-customization.md +4 -0
  53. package/externs/css_style_declaration.js +19 -0
  54. package/externs/lcevc.js +37 -13
  55. package/externs/media_remote.js +68 -0
  56. package/externs/mediatailor.js +193 -0
  57. package/externs/pictureinpicture.js +11 -0
  58. package/externs/shaka/ads.js +27 -0
  59. package/externs/shaka/manifest.js +10 -5
  60. package/externs/shaka/manifest_parser.js +7 -1
  61. package/externs/shaka/offline.js +4 -0
  62. package/externs/shaka/player.js +12 -1
  63. package/karma.conf.js +7 -3
  64. package/lib/abr/simple_abr_manager.js +43 -3
  65. package/lib/ads/ad_manager.js +104 -50
  66. package/lib/ads/media_tailor_ad.js +297 -0
  67. package/lib/ads/media_tailor_ad_manager.js +828 -0
  68. package/lib/cea/ts_cea_parser.js +15 -2
  69. package/lib/config/codec_switching_strategy.js +24 -0
  70. package/lib/dash/content_protection.js +120 -2
  71. package/lib/dash/dash_parser.js +79 -13
  72. package/lib/dash/mpd_utils.js +10 -6
  73. package/lib/dash/segment_base.js +13 -5
  74. package/lib/dash/segment_list.js +13 -5
  75. package/lib/dash/segment_template.js +43 -85
  76. package/lib/dependencies/all.js +8 -7
  77. package/lib/hls/hls_parser.js +101 -37
  78. package/lib/lcevc/lcevc_dec.js +220 -0
  79. package/lib/media/adaptation_set.js +22 -11
  80. package/lib/media/adaptation_set_criteria.js +25 -7
  81. package/lib/media/media_source_engine.js +215 -27
  82. package/lib/media/segment_index.js +5 -5
  83. package/lib/media/segment_reference.js +10 -5
  84. package/lib/media/streaming_engine.js +44 -39
  85. package/lib/mss/mss_parser.js +1 -0
  86. package/lib/net/networking_engine.js +1 -0
  87. package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
  88. package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
  89. package/lib/offline/manifest_converter.js +1 -0
  90. package/lib/offline/storage.js +3 -0
  91. package/lib/player.js +142 -72
  92. package/lib/routing/payload.js +5 -1
  93. package/lib/text/cue.js +62 -47
  94. package/lib/text/ttml_text_parser.js +17 -0
  95. package/lib/text/ui_text_displayer.js +4 -0
  96. package/lib/text/vtt_text_parser.js +35 -22
  97. package/lib/transmuxer/h265.js +862 -0
  98. package/lib/transmuxer/mpeg_ts_transmuxer.js +3 -2
  99. package/lib/transmuxer/ts_transmuxer.js +160 -15
  100. package/lib/util/cmcd_manager.js +6 -1
  101. package/lib/util/error.js +28 -9
  102. package/lib/util/exp_golomb.js +30 -2
  103. package/lib/util/fake_event.js +3 -0
  104. package/lib/util/manifest_parser_utils.js +10 -4
  105. package/lib/util/mp4_generator.js +4 -4
  106. package/lib/util/periods.js +2 -0
  107. package/lib/util/platform.js +54 -0
  108. package/lib/util/player_configuration.js +8 -0
  109. package/lib/util/state_history.js +6 -2
  110. package/lib/util/stream_utils.js +133 -217
  111. package/lib/util/ts_parser.js +229 -43
  112. package/maintained-branches.md +3 -2
  113. package/package.json +4 -6
  114. package/roadmap.md +15 -5
  115. package/shaka-player.uncompiled.js +2 -1
  116. package/support.html +0 -1
  117. package/test/cast/cast_receiver_integration.js +47 -0
  118. package/test/codec_switching/codec_switching_integration.js +181 -0
  119. package/test/dash/dash_parser_content_protection_unit.js +2 -0
  120. package/test/dash/dash_parser_integration.js +73 -0
  121. package/test/dash/dash_parser_live_unit.js +2 -0
  122. package/test/dash/dash_parser_manifest_unit.js +47 -0
  123. package/test/dash/dash_parser_segment_base_unit.js +7 -0
  124. package/test/dash/dash_parser_segment_list_unit.js +23 -17
  125. package/test/dash/dash_parser_segment_template_unit.js +49 -0
  126. package/test/demo/demo_unit.js +0 -16
  127. package/test/hls/hls_live_unit.js +35 -0
  128. package/test/hls/hls_parser_unit.js +8 -61
  129. package/test/media/adaptation_set_criteria_unit.js +118 -0
  130. package/test/media/adaptation_set_unit.js +1 -0
  131. package/test/media/media_source_engine_unit.js +165 -1
  132. package/test/media/segment_index_unit.js +4 -4
  133. package/test/media/segment_prefetch_unit.js +1 -1
  134. package/test/media/streaming_engine_unit.js +17 -17
  135. package/test/mss/mss_parser_unit.js +2 -0
  136. package/test/offline/manifest_convert_unit.js +5 -0
  137. package/test/offline/storage_integration.js +1 -4
  138. package/test/player_unit.js +102 -131
  139. package/test/routing/walker_unit.js +1 -0
  140. package/test/test/assets/dash-aes-128/Fragments(video=0,format=mpd-time-csf) +0 -0
  141. package/test/test/assets/dash-aes-128/Fragments(video=20000000,format=mpd-time-csf) +0 -0
  142. package/test/test/assets/dash-aes-128/Fragments(video=40000000,format=mpd-time-csf) +0 -0
  143. package/test/test/assets/dash-aes-128/Fragments(video=60000000,format=mpd-time-csf) +0 -0
  144. package/test/test/assets/dash-aes-128/Fragments(video=i,format=mpd-time-csf) +0 -0
  145. package/test/test/assets/dash-aes-128/dash.mpd +18 -0
  146. package/test/test/assets/dash-aes-128/license +1 -0
  147. package/test/test/assets/dash-multi-codec/audio_en_2c_128k_aac.mp4 +0 -0
  148. package/test/test/assets/dash-multi-codec/audio_es_2c_64k_opus.webm +0 -0
  149. package/test/test/assets/dash-multi-codec/dash.mpd +32 -0
  150. package/test/test/assets/dash-multi-codec/video_144p_108k_h264.mp4 +0 -0
  151. package/test/test/assets/hls-ts-h265/0.ts +0 -0
  152. package/test/test/assets/hls-ts-h265/hevc.m3u8 +6 -0
  153. package/test/test/assets/hls-ts-h265/index.m3u8 +7 -0
  154. package/test/test/assets/hls-ts-muxed-aac-h265/0.ts +0 -0
  155. package/test/test/assets/hls-ts-muxed-aac-h265/1.ts +0 -0
  156. package/test/test/assets/hls-ts-muxed-aac-h265/media.m3u8 +9 -0
  157. package/test/test/assets/hls-ts-muxed-ac3-h264/media.m3u8 +24 -0
  158. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00000.ts +0 -0
  159. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00001.ts +0 -0
  160. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00002.ts +0 -0
  161. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00003.ts +0 -0
  162. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00004.ts +0 -0
  163. package/test/test/assets/hls-ts-muxed-ac3-h264/s_00005.ts +0 -0
  164. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0000.ts +0 -0
  165. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0001.ts +0 -0
  166. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0002.ts +0 -0
  167. package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0003.ts +0 -0
  168. package/test/test/assets/hls-ts-muxed-ec3-h264/prog_index.m3u8 +14 -0
  169. package/test/test/assets/hls-ts-muxed-mp3-h264/index.m3u8 +23 -0
  170. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-1-v1-a1.ts +0 -0
  171. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-2-v1-a1.ts +0 -0
  172. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-3-v1-a1.ts +0 -0
  173. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-4-v1-a1.ts +0 -0
  174. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-5-v1-a1.ts +0 -0
  175. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-6-v1-a1.ts +0 -0
  176. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-7-v1-a1.ts +0 -0
  177. package/test/test/assets/hls-ts-muxed-mp3-h264/seg-8-v1-a1.ts +0 -0
  178. package/test/test/util/dash_parser_util.js +20 -7
  179. package/test/test/util/fake_ad_manager.js +20 -0
  180. package/test/test/util/fake_media_source_engine.js +8 -2
  181. package/test/test/util/manifest_generator.js +7 -3
  182. package/test/test/util/mss_parser_util.js +4 -0
  183. package/test/test/util/offline_utils.js +1 -0
  184. package/test/test/util/streaming_engine_util.js +7 -4
  185. package/test/text/ttml_text_parser_unit.js +52 -0
  186. package/test/text/vtt_text_parser_unit.js +105 -48
  187. package/test/transmuxer/transmuxer_integration.js +121 -4
  188. package/test/ui/ui_unit.js +3 -10
  189. package/test/util/stream_utils_unit.js +208 -74
  190. package/test/util/ts_parser_unit.js +1 -1
  191. package/ui/controls.js +125 -0
  192. package/ui/externs/ui.js +8 -1
  193. package/ui/pip_button.js +22 -120
  194. package/ui/remote_button.js +201 -0
  195. package/ui/ui.js +5 -2
  196. package/demo/common/message_ids.js +0 -309
  197. package/demo/locales/en.json +0 -287
  198. package/demo/locales/source.json +0 -1142
  199. package/externs/mux.js +0 -69
  200. package/lib/lcevc/lcevc_dil.js +0 -152
  201. package/lib/transmuxer/muxjs_transmuxer.js +0 -242
  202. package/test/transmuxer/muxjs_transmuxer_integration.js +0 -177
@@ -8,6 +8,7 @@ goog.provide('shaka.media.MediaSourceEngine');
8
8
 
9
9
  goog.require('goog.asserts');
10
10
  goog.require('shaka.log');
11
+ goog.require('shaka.config.CodecSwitchingStrategy');
11
12
  goog.require('shaka.media.Capabilities');
12
13
  goog.require('shaka.media.ContentWorkarounds');
13
14
  goog.require('shaka.media.ClosedCaptionParser');
@@ -31,7 +32,7 @@ goog.require('shaka.util.Mp4Parser');
31
32
  goog.require('shaka.util.Platform');
32
33
  goog.require('shaka.util.PublicPromise');
33
34
  goog.require('shaka.util.TsParser');
34
- goog.require('shaka.lcevc.Dil');
35
+ goog.require('shaka.lcevc.Dec');
35
36
 
36
37
 
37
38
  /**
@@ -53,10 +54,10 @@ shaka.media.MediaSourceEngine = class {
53
54
  * MediaSourceEngine is destroyed, it will destroy the displayer.
54
55
  * @param {!function(!Array.<shaka.extern.ID3Metadata>, number, ?number)=}
55
56
  * onMetadata
56
- * @param {?shaka.lcevc.Dil} [lcevcDil] Optional - LCEVC Dil Object
57
+ * @param {?shaka.lcevc.Dec} [lcevcDec] Optional - LCEVC Decoder Object
57
58
  *
58
59
  */
59
- constructor(video, textDisplayer, onMetadata, lcevcDil) {
60
+ constructor(video, textDisplayer, onMetadata, lcevcDec) {
60
61
  /** @private {HTMLMediaElement} */
61
62
  this.video_ = video;
62
63
 
@@ -91,8 +92,8 @@ shaka.media.MediaSourceEngine = class {
91
92
  number, ?number)} */
92
93
  this.onMetadata_ = onMetadata || onMetadataNoOp;
93
94
 
94
- /** @private {?shaka.lcevc.Dil} */
95
- this.lcevcDil_ = lcevcDil || null;
95
+ /** @private {?shaka.lcevc.Dec} */
96
+ this.lcevcDec_ = lcevcDec || null;
96
97
 
97
98
  /**
98
99
  * @private {!Object.<string,
@@ -115,6 +116,9 @@ shaka.media.MediaSourceEngine = class {
115
116
  /** @private {string} */
116
117
  this.url_ = '';
117
118
 
119
+ /** @private {boolean} */
120
+ this.playbackHasBegun_ = false;
121
+
118
122
  /** @private {MediaSource} */
119
123
  this.mediaSource_ = this.createMediaSource(this.mediaSourceOpen_);
120
124
 
@@ -138,6 +142,9 @@ shaka.media.MediaSourceEngine = class {
138
142
 
139
143
  /** @private {!shaka.util.PublicPromise.<number>} */
140
144
  this.textSequenceModeOffset_ = new shaka.util.PublicPromise();
145
+
146
+ /** @private {boolean} */
147
+ this.needSplitMuxedContent_ = false;
141
148
  }
142
149
 
143
150
  /**
@@ -156,6 +163,11 @@ shaka.media.MediaSourceEngine = class {
156
163
  this.eventManager_.listenOnce(
157
164
  mediaSource, 'sourceopen', () => this.onSourceOpen_(p));
158
165
 
166
+ // Correctly set when playback has begun.
167
+ this.eventManager_.listenOnce(this.video_, 'playing', () => {
168
+ this.playbackHasBegun_ = true;
169
+ });
170
+
159
171
  // Store the object URL for releasing it later.
160
172
  this.url_ = shaka.media.MediaSourceEngine.createObjectURL(mediaSource);
161
173
 
@@ -341,7 +353,7 @@ shaka.media.MediaSourceEngine = class {
341
353
  this.queues_ = {};
342
354
 
343
355
  // This object is owned by Player
344
- this.lcevcDil_ = null;
356
+ this.lcevcDec_ = null;
345
357
  }
346
358
 
347
359
  /**
@@ -379,6 +391,8 @@ shaka.media.MediaSourceEngine = class {
379
391
  async init(streamsByType, sequenceMode=false,
380
392
  manifestType=shaka.media.ManifestParser.UNKNOWN,
381
393
  ignoreManifestTimestampsInSegmentsMode=false) {
394
+ const ContentType = shaka.util.ManifestParserUtils.ContentType;
395
+
382
396
  await this.mediaSourceOpen_;
383
397
 
384
398
  this.sequenceMode_ = sequenceMode;
@@ -392,8 +406,13 @@ shaka.media.MediaSourceEngine = class {
392
406
 
393
407
  for (const contentType of streamsByType.keys()) {
394
408
  const stream = streamsByType.get(contentType);
395
- this.initSourceBuffer_(contentType, stream);
396
- this.queues_[contentType] = [];
409
+ this.initSourceBuffer_(contentType, stream, stream.codecs);
410
+ if (this.needSplitMuxedContent_) {
411
+ this.queues_[ContentType.AUDIO] = [];
412
+ this.queues_[ContentType.VIDEO] = [];
413
+ } else {
414
+ this.queues_[contentType] = [];
415
+ }
397
416
  }
398
417
  }
399
418
 
@@ -402,9 +421,10 @@ shaka.media.MediaSourceEngine = class {
402
421
  *
403
422
  * @param {shaka.util.ManifestParserUtils.ContentType} contentType
404
423
  * @param {shaka.extern.Stream} stream
424
+ * @param {string} codecs
405
425
  * @private
406
426
  */
407
- initSourceBuffer_(contentType, stream) {
427
+ initSourceBuffer_(contentType, stream, codecs) {
408
428
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
409
429
 
410
430
  goog.asserts.assert(
@@ -412,7 +432,7 @@ shaka.media.MediaSourceEngine = class {
412
432
  'Type negotiation should happen before MediaSourceEngine.init!');
413
433
 
414
434
  let mimeType = shaka.util.MimeUtils.getFullType(
415
- stream.mimeType, stream.codecs);
435
+ stream.mimeType, codecs);
416
436
  if (contentType == ContentType.TEXT) {
417
437
  this.reinitText(mimeType, this.sequenceMode_, stream.external);
418
438
  } else {
@@ -424,9 +444,19 @@ shaka.media.MediaSourceEngine = class {
424
444
  }
425
445
  const mimeTypeWithAllCodecs =
426
446
  shaka.util.MimeUtils.getFullTypeWithAllCodecs(
427
- stream.mimeType, stream.codecs);
447
+ stream.mimeType, codecs);
428
448
  const TransmuxerEngine = shaka.transmuxer.TransmuxerEngine;
429
449
  if (needTransmux) {
450
+ const audioCodec = shaka.util.ManifestParserUtils.guessCodecsSafe(
451
+ ContentType.AUDIO, (codecs || '').split(','));
452
+ const videoCodec = shaka.util.ManifestParserUtils.guessCodecsSafe(
453
+ ContentType.VIDEO, (codecs || '').split(','));
454
+ if (audioCodec && videoCodec) {
455
+ this.needSplitMuxedContent_ = true;
456
+ this.initSourceBuffer_(ContentType.AUDIO, stream, audioCodec);
457
+ this.initSourceBuffer_(ContentType.VIDEO, stream, videoCodec);
458
+ return;
459
+ }
430
460
  const transmuxerPlugin =
431
461
  TransmuxerEngine.findTransmuxer(mimeTypeWithAllCodecs);
432
462
  if (transmuxerPlugin) {
@@ -513,7 +543,8 @@ shaka.media.MediaSourceEngine = class {
513
543
  * @return {?number} The timestamp in seconds, or null if nothing is buffered.
514
544
  */
515
545
  bufferStart(contentType) {
516
- if (this.reloadingMediaSource_) {
546
+ if (this.reloadingMediaSource_ ||
547
+ !Object.keys(this.sourceBuffers_).length) {
517
548
  return null;
518
549
  }
519
550
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
@@ -743,7 +774,7 @@ shaka.media.MediaSourceEngine = class {
743
774
  */
744
775
  async appendBuffer(
745
776
  contentType, data, reference, stream, hasClosedCaptions, seeked = false,
746
- adaptation = false, isChunkedData = false) {
777
+ adaptation = false, isChunkedData = false, fromSplit = false) {
747
778
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
748
779
 
749
780
  if (contentType == ContentType.TEXT) {
@@ -759,6 +790,21 @@ shaka.media.MediaSourceEngine = class {
759
790
  return;
760
791
  }
761
792
 
793
+ if (!fromSplit && this.needSplitMuxedContent_) {
794
+ await this.appendBuffer(ContentType.AUDIO, data, reference, stream,
795
+ hasClosedCaptions, seeked, adaptation, isChunkedData,
796
+ /* fromSplit= */ true);
797
+ await this.appendBuffer(ContentType.VIDEO, data, reference, stream,
798
+ hasClosedCaptions, seeked, adaptation, isChunkedData,
799
+ /* fromSplit= */ true);
800
+ return;
801
+ }
802
+
803
+ if (!this.sourceBuffers_[contentType]) {
804
+ shaka.log.warning('Attempted to restore a non-existent source buffer');
805
+ return;
806
+ }
807
+
762
808
  let timestampOffset = this.sourceBuffers_[contentType].timestampOffset;
763
809
 
764
810
  let mimeType = this.sourceBufferTypes_[contentType];
@@ -850,7 +896,7 @@ shaka.media.MediaSourceEngine = class {
850
896
  if (goog.DEBUG && reference && !reference.isPreload() && !isChunkedData) {
851
897
  bufferedBefore = this.getBuffered_(contentType);
852
898
  }
853
- this.append_(contentType, data);
899
+ this.append_(contentType, data, timestampOffset);
854
900
  });
855
901
 
856
902
  if (goog.DEBUG && reference && !reference.isPreload() && !isChunkedData) {
@@ -910,6 +956,11 @@ shaka.media.MediaSourceEngine = class {
910
956
  await this.enqueueOperation_(
911
957
  contentType,
912
958
  () => this.remove_(contentType, startTime, endTime));
959
+ if (this.needSplitMuxedContent_) {
960
+ await this.enqueueOperation_(
961
+ ContentType.AUDIO,
962
+ () => this.remove_(ContentType.AUDIO, startTime, endTime));
963
+ }
913
964
  }
914
965
  }
915
966
 
@@ -931,6 +982,12 @@ shaka.media.MediaSourceEngine = class {
931
982
  await this.enqueueOperation_(
932
983
  contentType,
933
984
  () => this.remove_(contentType, 0, this.mediaSource_.duration));
985
+ if (this.needSplitMuxedContent_) {
986
+ await this.enqueueOperation_(
987
+ ContentType.AUDIO,
988
+ () => this.remove_(
989
+ ContentType.AUDIO, 0, this.mediaSource_.duration));
990
+ }
934
991
  }
935
992
  }
936
993
 
@@ -961,6 +1018,11 @@ shaka.media.MediaSourceEngine = class {
961
1018
  await this.enqueueOperation_(
962
1019
  contentType,
963
1020
  () => this.flush_(contentType));
1021
+ if (this.needSplitMuxedContent_) {
1022
+ await this.enqueueOperation_(
1023
+ ContentType.AUDIO,
1024
+ () => this.flush_(ContentType.AUDIO));
1025
+ }
964
1026
  }
965
1027
 
966
1028
  /**
@@ -1014,16 +1076,33 @@ shaka.media.MediaSourceEngine = class {
1014
1076
  operations.push(this.enqueueOperation_(
1015
1077
  contentType,
1016
1078
  () => this.abort_(contentType)));
1079
+ if (this.needSplitMuxedContent_) {
1080
+ operations.push(this.enqueueOperation_(
1081
+ ContentType.AUDIO,
1082
+ () => this.abort_(ContentType.AUDIO)));
1083
+ }
1017
1084
  }
1018
1085
  if (!ignoreTimestampOffset) {
1019
1086
  operations.push(this.enqueueOperation_(
1020
1087
  contentType,
1021
1088
  () => this.setTimestampOffset_(contentType, timestampOffset)));
1089
+ if (this.needSplitMuxedContent_) {
1090
+ operations.push(this.enqueueOperation_(
1091
+ ContentType.AUDIO,
1092
+ () => this.setTimestampOffset_(
1093
+ ContentType.AUDIO, timestampOffset)));
1094
+ }
1022
1095
  }
1023
1096
  operations.push(this.enqueueOperation_(
1024
1097
  contentType,
1025
1098
  () => this.setAppendWindow_(
1026
1099
  contentType, appendWindowStart, appendWindowEnd)));
1100
+ if (this.needSplitMuxedContent_) {
1101
+ operations.push(this.enqueueOperation_(
1102
+ ContentType.AUDIO,
1103
+ () => this.setAppendWindow_(
1104
+ ContentType.AUDIO, appendWindowStart, appendWindowEnd)));
1105
+ }
1027
1106
 
1028
1107
  await Promise.all(operations);
1029
1108
  }
@@ -1054,9 +1133,19 @@ shaka.media.MediaSourceEngine = class {
1054
1133
  this.enqueueOperation_(
1055
1134
  contentType,
1056
1135
  () => this.abort_(contentType));
1136
+ if (this.needSplitMuxedContent_) {
1137
+ this.enqueueOperation_(
1138
+ ContentType.AUDIO,
1139
+ () => this.abort_(ContentType.AUDIO));
1140
+ }
1057
1141
  await this.enqueueOperation_(
1058
1142
  contentType,
1059
1143
  () => this.setTimestampOffset_(contentType, timestampOffset));
1144
+ if (this.needSplitMuxedContent_) {
1145
+ await this.enqueueOperation_(
1146
+ ContentType.AUDIO,
1147
+ () => this.setTimestampOffset_(ContentType.AUDIO, timestampOffset));
1148
+ }
1060
1149
  }
1061
1150
 
1062
1151
  /**
@@ -1120,16 +1209,17 @@ shaka.media.MediaSourceEngine = class {
1120
1209
  * Append data to the SourceBuffer.
1121
1210
  * @param {shaka.util.ManifestParserUtils.ContentType} contentType
1122
1211
  * @param {BufferSource} data
1212
+ * @param {number} timestampOffset
1123
1213
  * @private
1124
1214
  */
1125
- append_(contentType, data) {
1215
+ append_(contentType, data, timestampOffset) {
1126
1216
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
1127
1217
 
1128
- // Append only video data to the LCEVC Dil.
1129
- if (contentType == ContentType.VIDEO && this.lcevcDil_) {
1130
- // Append video buffers to the LCEVC Dil for parsing and storing
1218
+ // Append only video data to the LCEVC Dec.
1219
+ if (contentType == ContentType.VIDEO && this.lcevcDec_) {
1220
+ // Append video buffers to the LCEVC Dec for parsing and storing
1131
1221
  // of LCEVC data.
1132
- this.lcevcDil_.appendBuffer(data);
1222
+ this.lcevcDec_.appendBuffer(data, timestampOffset);
1133
1223
  }
1134
1224
 
1135
1225
  // This will trigger an 'updateend' event.
@@ -1292,7 +1382,6 @@ shaka.media.MediaSourceEngine = class {
1292
1382
  */
1293
1383
  enqueueOperation_(contentType, start) {
1294
1384
  this.destroyer_.ensureNotDestroyed();
1295
-
1296
1385
  const operation = {
1297
1386
  start: start,
1298
1387
  p: new shaka.util.PublicPromise(),
@@ -1563,8 +1652,24 @@ shaka.media.MediaSourceEngine = class {
1563
1652
  */
1564
1653
  async reset_(streamsByType) {
1565
1654
  const Functional = shaka.util.Functional;
1655
+ const ContentType = shaka.util.ManifestParserUtils.ContentType;
1566
1656
  this.reloadingMediaSource_ = true;
1657
+ this.needSplitMuxedContent_ = false;
1567
1658
  const currentTime = this.video_.currentTime;
1659
+
1660
+ // When codec switching if the user is currently paused we don't want
1661
+ // to trigger a play when switching codec.
1662
+ // Playing can also end up in a paused state after a codec switch
1663
+ // so we need to remember the current states.
1664
+ const previousAutoPlayState = this.video_.autoplay;
1665
+ const previousPausedState = this.video_.paused;
1666
+ if (this.playbackHasBegun_) {
1667
+ // Only set autoplay to false if the video playback has already begun.
1668
+ // When a codec switch happens before playback has begun this can cause
1669
+ // autoplay not to work as expected.
1670
+ this.video_.autoplay = false;
1671
+ }
1672
+
1568
1673
  try {
1569
1674
  this.eventManager_.removeAll();
1570
1675
 
@@ -1598,7 +1703,6 @@ shaka.media.MediaSourceEngine = class {
1598
1703
  }
1599
1704
  await Promise.all(cleanup);
1600
1705
  this.transmuxers_ = {};
1601
- this.queues_ = {};
1602
1706
  this.sourceBuffers_ = {};
1603
1707
 
1604
1708
  const previousDuration = this.mediaSource_.duration;
@@ -1622,7 +1726,13 @@ shaka.media.MediaSourceEngine = class {
1622
1726
 
1623
1727
  for (const contentType of streamsByType.keys()) {
1624
1728
  const stream = streamsByType.get(contentType);
1625
- this.initSourceBuffer_(contentType, stream);
1729
+ this.initSourceBuffer_(contentType, stream, stream.codecs);
1730
+ if (this.needSplitMuxedContent_) {
1731
+ this.queues_[ContentType.AUDIO] = [];
1732
+ this.queues_[ContentType.VIDEO] = [];
1733
+ } else {
1734
+ this.queues_[contentType] = [];
1735
+ }
1626
1736
  }
1627
1737
 
1628
1738
  // Fake a seek to catchup the playhead.
@@ -1631,6 +1741,17 @@ shaka.media.MediaSourceEngine = class {
1631
1741
  await sourceBufferAdded;
1632
1742
  } finally {
1633
1743
  this.reloadingMediaSource_ = false;
1744
+
1745
+ this.destroyer_.ensureNotDestroyed();
1746
+
1747
+ this.eventManager_.listenOnce(this.video_, 'canplay', () => {
1748
+ this.destroyer_.ensureNotDestroyed();
1749
+
1750
+ this.video_.autoplay = previousAutoPlayState;
1751
+ if (!previousPausedState) {
1752
+ this.video_.play();
1753
+ }
1754
+ });
1634
1755
  }
1635
1756
  }
1636
1757
 
@@ -1696,7 +1817,10 @@ shaka.media.MediaSourceEngine = class {
1696
1817
  return false;
1697
1818
  }
1698
1819
 
1699
- if (shaka.media.Capabilities.isChangeTypeSupported()) {
1820
+ if (this.config_.codecSwitchingStrategy ===
1821
+ shaka.config.CodecSwitchingStrategy.SMOOTH &&
1822
+ shaka.media.Capabilities.isChangeTypeSupported() &&
1823
+ !this.needSplitMuxedContent_) {
1700
1824
  await this.changeType(contentType, newMimeType, transmuxer);
1701
1825
  } else {
1702
1826
  if (transmuxer) {
@@ -1708,11 +1832,75 @@ shaka.media.MediaSourceEngine = class {
1708
1832
  }
1709
1833
 
1710
1834
  /**
1711
- * Update LCEVC DIL object when ready for LCEVC Decodes
1712
- * @param {?shaka.lcevc.Dil} lcevcDil
1835
+ * Returns true if it's necessary codec switch to load the new stream.
1836
+ *
1837
+ * @param {shaka.util.ManifestParserUtils.ContentType} contentType
1838
+ * @param {shaka.extern.Stream} stream
1839
+ * @return {boolean}
1840
+ * @private
1841
+ */
1842
+ isCodecSwitchNecessary_(contentType, stream) {
1843
+ const MimeUtils = shaka.util.MimeUtils;
1844
+ const currentCodec = MimeUtils.getCodecBase(
1845
+ MimeUtils.getCodecs(this.sourceBufferTypes_[contentType]));
1846
+ const currentBasicType = MimeUtils.getBasicType(
1847
+ this.sourceBufferTypes_[contentType]);
1848
+
1849
+ let newMimeType = shaka.util.MimeUtils.getFullType(
1850
+ stream.mimeType, stream.codecs);
1851
+ let needTransmux = this.config_.forceTransmux;
1852
+ if (!shaka.media.Capabilities.isTypeSupported(newMimeType) ||
1853
+ (!this.sequenceMode_ &&
1854
+ shaka.util.MimeUtils.RAW_FORMATS.includes(newMimeType))) {
1855
+ needTransmux = true;
1856
+ }
1857
+ const newMimeTypeWithAllCodecs =
1858
+ shaka.util.MimeUtils.getFullTypeWithAllCodecs(
1859
+ stream.mimeType, stream.codecs);
1860
+ const TransmuxerEngine = shaka.transmuxer.TransmuxerEngine;
1861
+ if (needTransmux) {
1862
+ const transmuxerPlugin =
1863
+ TransmuxerEngine.findTransmuxer(newMimeTypeWithAllCodecs);
1864
+ if (transmuxerPlugin) {
1865
+ const transmuxer = transmuxerPlugin();
1866
+ newMimeType =
1867
+ transmuxer.convertCodecs(contentType, newMimeTypeWithAllCodecs);
1868
+ transmuxer.destroy();
1869
+ }
1870
+ }
1871
+
1872
+ const newCodec = MimeUtils.getCodecBase(
1873
+ MimeUtils.getCodecs(newMimeType));
1874
+ const newBasicType = MimeUtils.getBasicType(newMimeType);
1875
+
1876
+ return currentCodec !== newCodec || currentBasicType !== newBasicType;
1877
+ }
1878
+
1879
+ /**
1880
+ * Returns true if it's necessary reset the media source to load the
1881
+ * new stream.
1882
+ *
1883
+ * @param {shaka.util.ManifestParserUtils.ContentType} contentType
1884
+ * @param {shaka.extern.Stream} stream
1885
+ * @return {boolean}
1886
+ */
1887
+ isResetMediaSourceNecessary(contentType, stream) {
1888
+ if (!this.isCodecSwitchNecessary_(contentType, stream)) {
1889
+ return false;
1890
+ }
1891
+
1892
+ return this.config_.codecSwitchingStrategy !==
1893
+ shaka.config.CodecSwitchingStrategy.SMOOTH ||
1894
+ !shaka.media.Capabilities.isChangeTypeSupported() ||
1895
+ this.needSplitMuxedContent_;
1896
+ }
1897
+
1898
+ /**
1899
+ * Update LCEVC Decoder object when ready for LCEVC Decode.
1900
+ * @param {?shaka.lcevc.Dec} lcevcDec
1713
1901
  */
1714
- updateLcevcDil(lcevcDil) {
1715
- this.lcevcDil_ = lcevcDil;
1902
+ updateLcevcDec(lcevcDec) {
1903
+ this.lcevcDec_ = lcevcDec;
1716
1904
  }
1717
1905
  };
1718
1906
 
@@ -379,7 +379,7 @@ shaka.media.SegmentIndex = class {
379
379
  lastReference.tileDuration,
380
380
  lastReference.syncTime,
381
381
  lastReference.status,
382
- lastReference.hlsAes128Key,
382
+ lastReference.aes128Key,
383
383
  );
384
384
  this.references[this.references.length - 1].discontinuitySequence =
385
385
  lastReference.discontinuitySequence;
@@ -708,7 +708,7 @@ shaka.media.MetaSegmentIndex = class extends shaka.media.SegmentIndex {
708
708
  }
709
709
 
710
710
  numPassedInEarlierIndexes += index.numEvicted_ +
711
- index.references.length;
711
+ index.getNumReferences();
712
712
  }
713
713
 
714
714
  return null;
@@ -732,9 +732,9 @@ shaka.media.MetaSegmentIndex = class extends shaka.media.SegmentIndex {
732
732
  return reference;
733
733
  }
734
734
 
735
- numPassedInEarlierIndexes += index.numEvicted_ +
736
- index.references.length;
737
- sawSegments = sawSegments || index.references.length != 0;
735
+ const num = index.getNumReferences();
736
+ numPassedInEarlierIndexes += index.numEvicted_ + num;
737
+ sawSegments = sawSegments || num != 0;
738
738
  }
739
739
 
740
740
  return null;
@@ -32,9 +32,11 @@ shaka.media.InitSegmentReference = class {
32
32
  * the quality of the media associated with this init segment.
33
33
  * @param {(null|number)=} timescale
34
34
  * @param {(null|BufferSource)=} segmentData
35
+ * @param {?shaka.extern.aes128Key=} aes128Key
36
+ * The segment's AES-128-CBC full segment encryption key and iv.
35
37
  */
36
38
  constructor(uris, startByte, endByte, mediaQuality = null, timescale = null,
37
- segmentData = null) {
39
+ segmentData = null, aes128Key = null) {
38
40
  /** @type {function():!Array.<string>} */
39
41
  this.getUris = uris;
40
42
 
@@ -52,6 +54,9 @@ shaka.media.InitSegmentReference = class {
52
54
 
53
55
  /** @type {BufferSource|null} */
54
56
  this.segmentData = segmentData;
57
+
58
+ /** @type {?shaka.extern.aes128Key} */
59
+ this.aes128Key = aes128Key;
55
60
  }
56
61
 
57
62
  /**
@@ -191,7 +196,7 @@ shaka.media.SegmentReference = class {
191
196
  * @param {shaka.media.SegmentReference.Status=} status
192
197
  * The segment status is used to indicate that a segment does not exist or is
193
198
  * not available.
194
- * @param {?shaka.extern.HlsAes128Key=} hlsAes128Key
199
+ * @param {?shaka.extern.aes128Key=} aes128Key
195
200
  * The segment's AES-128-CBC full segment encryption key and iv.
196
201
  */
197
202
  constructor(
@@ -199,7 +204,7 @@ shaka.media.SegmentReference = class {
199
204
  timestampOffset, appendWindowStart, appendWindowEnd,
200
205
  partialReferences = [], tilesLayout = '', tileDuration = null,
201
206
  syncTime = null, status = shaka.media.SegmentReference.Status.AVAILABLE,
202
- hlsAes128Key = null) {
207
+ aes128Key = null) {
203
208
  // A preload hinted Partial Segment has the same startTime and endTime.
204
209
  goog.asserts.assert(startTime <= endTime,
205
210
  'startTime must be less than or equal to endTime');
@@ -271,8 +276,8 @@ shaka.media.SegmentReference = class {
271
276
  /** @type {boolean} */
272
277
  this.byterangeOptimization = false;
273
278
 
274
- /** @type {?shaka.extern.HlsAes128Key} */
275
- this.hlsAes128Key = hlsAes128Key;
279
+ /** @type {?shaka.extern.aes128Key} */
280
+ this.aes128Key = aes128Key;
276
281
 
277
282
  /** @type {?shaka.media.SegmentReference.ThumbnailSprite} */
278
283
  this.thumbnailSprite = null;