shaka-player 4.3.9 → 4.4.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.
- package/.github/workflows/build-and-test.yaml +11 -18
- package/.github/workflows/compute-incremental-coverage.py +3 -0
- package/.github/workflows/release-please.yaml +7 -0
- package/.github/workflows/selenium-lab-tests.yaml +45 -10
- package/AUTHORS +9 -0
- package/CHANGELOG.md +295 -190
- package/CONTRIBUTORS +8 -0
- package/LICENSE +32 -0
- package/README.md +63 -10
- package/demo/autoTemplate.txt +40 -5
- package/demo/close_button.js +11 -0
- package/demo/common/asset.js +13 -1
- package/demo/common/assets.js +112 -28
- package/demo/common/message_ids.js +25 -1
- package/demo/config.js +69 -10
- package/demo/customWarning.txt +2 -2
- package/demo/index.html +3 -1
- package/demo/locales/en.json +26 -2
- package/demo/locales/source.json +105 -9
- package/demo/main.js +57 -9
- package/demo/search.js +3 -1
- package/demo/service_worker.js +3 -0
- package/dist/controls.css +1 -1
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +132 -118
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +132 -118
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +46 -28
- package/dist/locales.js +31 -31
- package/dist/receiver.compiled.debug.js +60 -52
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +60 -52
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +676 -391
- package/dist/shaka-player.compiled.debug.d.ts +676 -391
- package/dist/shaka-player.compiled.debug.externs.js +1188 -616
- package/dist/shaka-player.compiled.debug.js +1046 -700
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +1188 -616
- package/dist/shaka-player.compiled.js +1094 -933
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +677 -392
- package/dist/shaka-player.ui.debug.d.ts +677 -392
- package/dist/shaka-player.ui.debug.externs.js +1023 -451
- package/dist/shaka-player.ui.debug.js +1101 -738
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +1023 -451
- package/dist/shaka-player.ui.js +1244 -1071
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/ad_monetization.md +15 -1
- package/docs/tutorials/application-level-redirects.md +1 -1
- package/docs/tutorials/config.md +9 -5
- package/docs/tutorials/drm-config.md +61 -0
- package/docs/tutorials/fairplay.md +19 -4
- package/docs/tutorials/license-server-auth.md +4 -4
- package/docs/tutorials/license-wrapping.md +2 -2
- package/docs/tutorials/ui-customization.md +7 -3
- package/docs/tutorials/ui.md +7 -7
- package/docs/tutorials/upgrade.md +3 -0
- package/externs/ima.js +123 -2
- package/externs/isoboxer.js +217 -0
- package/externs/mediakeys.js +18 -0
- package/externs/mux.js +1 -105
- package/externs/pictureinpicture.js +48 -0
- package/externs/shaka/ads.js +61 -1
- package/externs/shaka/cea.js +110 -0
- package/externs/shaka/codecs.js +32 -0
- package/externs/shaka/manifest.js +101 -1
- package/externs/shaka/manifest_parser.js +0 -10
- package/externs/shaka/net.js +36 -7
- package/externs/shaka/offline.js +8 -1
- package/externs/shaka/player.js +220 -21
- package/externs/shaka/text.js +1 -382
- package/externs/shaka/transmuxer.js +59 -0
- package/karma.conf.js +15 -0
- package/lib/abr/simple_abr_manager.js +6 -3
- package/lib/ads/ad_manager.js +60 -2
- package/lib/ads/client_side_ad.js +86 -0
- package/lib/ads/client_side_ad_manager.js +93 -5
- package/lib/ads/server_side_ad.js +86 -0
- package/lib/ads/server_side_ad_manager.js +13 -0
- package/lib/cast/cast_utils.js +1 -0
- package/lib/cea/cea608_data_channel.js +24 -14
- package/lib/cea/cea608_memory.js +5 -2
- package/lib/cea/cea708_service.js +8 -9
- package/lib/cea/cea708_window.js +1 -2
- package/lib/cea/cea_decoder.js +23 -6
- package/lib/cea/cea_utils.js +25 -2
- package/lib/cea/dummy_caption_decoder.js +21 -0
- package/lib/cea/dummy_cea_parser.js +1 -3
- package/lib/cea/mp4_cea_parser.js +15 -10
- package/lib/cea/ts_cea_parser.js +69 -0
- package/lib/dash/content_protection.js +32 -2
- package/lib/dash/dash_parser.js +93 -31
- package/lib/dash/mpd_utils.js +6 -25
- package/lib/dash/segment_base.js +12 -14
- package/lib/dash/segment_list.js +10 -6
- package/lib/dash/segment_template.js +349 -32
- package/lib/dependencies/all.js +8 -0
- package/lib/hls/hls_classes.js +33 -8
- package/lib/hls/hls_parser.js +805 -191
- package/lib/hls/manifest_text_parser.js +4 -14
- package/lib/media/adaptation_set_criteria.js +43 -3
- package/lib/media/closed_caption_parser.js +74 -12
- package/lib/media/drm_engine.js +189 -49
- package/lib/media/gap_jumping_controller.js +1 -1
- package/lib/media/manifest_parser.js +34 -0
- package/lib/media/media_source_capabilities.js +10 -0
- package/lib/media/media_source_engine.js +475 -231
- package/lib/media/playhead.js +1 -1
- package/lib/media/presentation_timeline.js +62 -0
- package/lib/media/segment_index.js +50 -14
- package/lib/media/segment_prefetch.js +277 -0
- package/lib/media/segment_reference.js +70 -4
- package/lib/media/streaming_engine.js +258 -81
- package/lib/media/video_wrapper.js +15 -7
- package/lib/mss/content_protection.js +353 -0
- package/lib/mss/mss_parser.js +1081 -0
- package/lib/net/networking_engine.js +60 -10
- package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
- package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
- package/lib/offline/manifest_converter.js +6 -0
- package/lib/offline/storage.js +2 -4
- package/lib/player.js +309 -105
- package/lib/polyfill/media_capabilities.js +43 -3
- package/lib/polyfill/patchedmediakeys_apple.js +5 -0
- package/lib/polyfill/patchedmediakeys_nop.js +5 -0
- package/lib/polyfill/patchedmediakeys_webkit.js +5 -0
- package/lib/text/cue.js +208 -130
- package/lib/text/lrc_text_parser.js +2 -2
- package/lib/text/mp4_vtt_parser.js +2 -2
- package/lib/text/sbv_text_parser.js +1 -1
- package/lib/text/simple_text_displayer.js +2 -1
- package/lib/text/srt_text_parser.js +9 -1
- package/lib/text/ssa_text_parser.js +2 -2
- package/lib/text/text_engine.js +3 -21
- package/lib/text/text_utils.js +71 -20
- package/lib/text/ui_text_displayer.js +14 -14
- package/lib/text/vtt_text_parser.js +22 -14
- package/lib/transmuxer/aac_transmuxer.js +224 -0
- package/lib/transmuxer/ac3.js +142 -0
- package/lib/transmuxer/ac3_transmuxer.js +212 -0
- package/lib/transmuxer/adts.js +232 -0
- package/lib/transmuxer/ec3.js +103 -0
- package/lib/transmuxer/ec3_transmuxer.js +212 -0
- package/lib/transmuxer/h264.js +324 -0
- package/lib/transmuxer/mp3_transmuxer.js +204 -0
- package/lib/transmuxer/mpeg_audio.js +196 -0
- package/lib/transmuxer/mpeg_ts_transmuxer.js +182 -0
- package/lib/transmuxer/mss_transmuxer.js +389 -0
- package/lib/{media/transmuxer.js → transmuxer/muxjs_transmuxer.js} +34 -80
- package/lib/transmuxer/transmuxer_engine.js +161 -0
- package/lib/transmuxer/ts_transmuxer.js +738 -0
- package/lib/util/cmcd_manager.js +131 -71
- package/lib/util/error.js +62 -26
- package/lib/util/exp_golomb.js +220 -0
- package/lib/util/fairplay_utils.js +64 -13
- package/lib/util/id3_utils.js +56 -3
- package/lib/util/language_utils.js +11 -8
- package/lib/util/manifest_parser_utils.js +3 -0
- package/lib/util/mime_utils.js +44 -7
- package/lib/util/mp4_box_parsers.js +233 -5
- package/lib/util/mp4_generator.js +1381 -0
- package/lib/util/periods.js +40 -34
- package/lib/util/platform.js +25 -3
- package/lib/util/player_configuration.js +38 -1
- package/lib/util/stream_utils.js +138 -27
- package/lib/util/text_parser.js +1 -1
- package/lib/util/ts_parser.js +470 -47
- package/lib/util/xml_utils.js +16 -3
- package/maintained-branches.md +2 -2
- package/package.json +4 -2
- package/roadmap.md +21 -6
- package/shaka-player.uncompiled.js +18 -0
- package/test/abr/simple_abr_manager_unit.js +32 -4
- package/test/ads/ad_manager_unit.js +18 -2
- package/test/cast/cast_utils_unit.js +7 -2
- package/test/cea/cea608_memory_unit.js +24 -0
- package/test/cea/cea_decoder_unit.js +33 -0
- package/test/dash/dash_parser_content_protection_unit.js +93 -3
- package/test/dash/dash_parser_live_unit.js +56 -13
- package/test/dash/dash_parser_manifest_unit.js +70 -2
- package/test/dash/dash_parser_segment_base_unit.js +14 -9
- package/test/dash/dash_parser_segment_template_unit.js +286 -6
- package/test/demo/demo_unit.js +5 -1
- package/test/hls/hls_live_unit.js +188 -28
- package/test/hls/hls_parser_unit.js +250 -109
- package/test/hls/manifest_text_parser_unit.js +18 -13
- package/test/media/adaptation_set_criteria_unit.js +46 -14
- package/test/media/adaptation_set_unit.js +2 -0
- package/test/media/drm_engine_integration.js +11 -8
- package/test/media/drm_engine_unit.js +220 -7
- package/test/media/media_source_engine_integration.js +122 -38
- package/test/media/media_source_engine_unit.js +133 -153
- package/test/media/playhead_unit.js +3 -0
- package/test/media/presentation_timeline_unit.js +51 -0
- package/test/media/segment_index_unit.js +52 -0
- package/test/media/segment_prefetch_unit.js +243 -0
- package/test/media/streaming_engine_integration.js +9 -3
- package/test/media/streaming_engine_unit.js +440 -59
- package/test/mss/mss_parser_content_protection_unit.js +49 -0
- package/test/mss/mss_parser_unit.js +472 -0
- package/test/mss/mss_player_integration.js +123 -0
- package/test/offline/manifest_convert_unit.js +9 -0
- package/test/offline/storage_integration.js +6 -1
- package/test/player_integration.js +4 -1
- package/test/player_unit.js +386 -133
- package/test/polyfill/media_capabilities_unit.js +18 -0
- package/test/test/assets/hls-raw-aac/fileSequence0.aac +0 -0
- package/test/test/assets/hls-raw-aac/fileSequence1.aac +0 -0
- package/test/test/assets/hls-raw-aac/fileSequence2.aac +0 -0
- package/test/test/assets/hls-raw-aac/fileSequence3.aac +0 -0
- package/test/test/assets/hls-raw-aac/manifest.m3u8 +4 -0
- package/test/test/assets/hls-raw-aac/stream.m4a.m3u8 +14 -0
- package/test/test/assets/hls-raw-ac3/fileSequence0.ac3 +0 -0
- package/test/test/assets/hls-raw-ac3/fileSequence1.ac3 +0 -0
- package/test/test/assets/hls-raw-ac3/fileSequence2.ac3 +0 -0
- package/test/test/assets/hls-raw-ac3/fileSequence3.ac3 +0 -0
- package/test/test/assets/hls-raw-ac3/fileSequence4.ac3 +0 -0
- package/test/test/assets/hls-raw-ac3/fileSequence5.ac3 +0 -0
- package/test/test/assets/hls-raw-ac3/fileSequence6.ac3 +0 -0
- package/test/test/assets/hls-raw-ac3/fileSequence7.ac3 +0 -0
- package/test/test/assets/hls-raw-ac3/prog_index.m3u8 +31 -0
- package/test/test/assets/hls-raw-ec3/fileSequence0.ec3 +0 -0
- package/test/test/assets/hls-raw-ec3/fileSequence1.ec3 +0 -0
- package/test/test/assets/hls-raw-ec3/fileSequence2.ec3 +0 -0
- package/test/test/assets/hls-raw-ec3/fileSequence3.ec3 +0 -0
- package/test/test/assets/hls-raw-ec3/fileSequence4.ec3 +0 -0
- package/test/test/assets/hls-raw-ec3/fileSequence5.ec3 +0 -0
- package/test/test/assets/hls-raw-ec3/fileSequence6.ec3 +0 -0
- package/test/test/assets/hls-raw-ec3/fileSequence7.ec3 +0 -0
- package/test/test/assets/hls-raw-ec3/prog_index.m3u8 +31 -0
- package/test/test/assets/hls-raw-mp3/chunklist_w905519204.m3u8 +13 -0
- package/test/test/assets/hls-raw-mp3/media_w905519204_0.mp3 +0 -0
- package/test/test/assets/hls-raw-mp3/media_w905519204_1.mp3 +0 -0
- package/test/test/assets/hls-raw-mp3/media_w905519204_2.mp3 +0 -0
- package/test/test/assets/hls-raw-mp3/media_w905519204_3.mp3 +0 -0
- package/test/test/assets/hls-raw-mp3/playlist.m3u8 +4 -0
- package/test/test/assets/hls-ts-aac/playlist.m3u8 +23 -0
- package/test/test/assets/hls-ts-aac/segment_0.ts +0 -0
- package/test/test/assets/hls-ts-aac/segment_1.ts +0 -0
- package/test/test/assets/hls-ts-aac/segment_2.ts +0 -0
- package/test/test/assets/hls-ts-aac/segment_3.ts +0 -0
- package/test/test/assets/hls-ts-aac/segment_4.ts +0 -0
- package/test/test/assets/hls-ts-aac/segment_5.ts +0 -0
- package/test/test/assets/hls-ts-aac/segment_6.ts +0 -0
- package/test/test/assets/hls-ts-aac/segment_7.ts +0 -0
- package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0000.ts +0 -0
- package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0001.ts +0 -0
- package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0002.ts +0 -0
- package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0003.ts +0 -0
- package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0004.ts +0 -0
- package/test/test/assets/hls-ts-ac3/ChID_voices_6ch_640kbps_dd_sub_0005.ts +0 -0
- package/test/test/assets/hls-ts-ac3/prog_index.m3u8 +19 -0
- package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0000.ts +0 -0
- package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0001.ts +0 -0
- package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0002.ts +0 -0
- package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0003.ts +0 -0
- package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0004.ts +0 -0
- package/test/test/assets/hls-ts-ec3/ChID_voices_6ch_256kbps_ddp_sub_0005.ts +0 -0
- package/test/test/assets/hls-ts-ec3/prog_index.m3u8 +19 -0
- package/test/test/assets/hls-ts-h264/fileSequence0.ts +0 -0
- package/test/test/assets/hls-ts-h264/fileSequence1.ts +0 -0
- package/test/test/assets/hls-ts-h264/fileSequence2.ts +0 -0
- package/test/test/assets/hls-ts-h264/fileSequence3.ts +0 -0
- package/test/test/assets/hls-ts-h264/fileSequence4.ts +0 -0
- package/test/test/assets/hls-ts-h264/fileSequence5.ts +0 -0
- package/test/test/assets/hls-ts-h264/prog_index.m3u8 +25 -0
- package/test/test/assets/hls-ts-mp3/manifest.m3u8 +13 -0
- package/test/test/assets/hls-ts-mp3/media_w239891843_0.ts +0 -0
- package/test/test/assets/hls-ts-mp3/media_w239891843_1.ts +0 -0
- package/test/test/assets/hls-ts-mp3/media_w239891843_2.ts +0 -0
- package/test/test/assets/hls-ts-mp3/media_w239891843_3.ts +0 -0
- package/test/test/assets/hls-ts-muxed-aac-h264/chunk.m3u8 +17 -0
- package/test/test/assets/hls-ts-muxed-aac-h264/n_0_0_0.ts +0 -0
- package/test/test/assets/hls-ts-muxed-aac-h264/n_1_0_0.ts +0 -0
- package/test/test/assets/hls-ts-muxed-aac-h264/n_2_0_0.ts +0 -0
- package/test/test/assets/hls-ts-muxed-aac-h264/n_3_0_0.ts +0 -0
- package/test/test/assets/hls-ts-muxed-aac-h264/n_4_0_0.ts +0 -0
- package/test/test/assets/hls-ts-muxed-aac-h264/n_5_0_0.ts +0 -0
- package/test/test/assets/hls-ts-muxed-aac-h264/playlist.m3u8 +4 -0
- package/test/test/boot.js +7 -2
- package/test/test/util/fake_ad.js +99 -0
- package/test/test/util/fake_ad_manager.js +7 -1
- package/test/test/util/fake_networking_engine.js +41 -14
- package/test/test/util/fake_segment_prefetch.js +81 -0
- package/test/test/util/manifest_generator.js +19 -5
- package/test/test/util/mss_parser_util.js +115 -0
- package/test/test/util/offline_utils.js +1 -0
- package/test/test/util/simple_fakes.js +12 -3
- package/test/test/util/streaming_engine_util.js +14 -4
- package/test/test/util/ttml_utils.js +2 -2
- package/test/test/util/ui_utils.js +5 -1
- package/test/text/cue_integration.js +1 -1
- package/test/text/srt_text_parser_unit.js +78 -0
- package/test/text/text_engine_unit.js +17 -8
- package/test/text/ui_text_displayer_unit.js +4 -4
- package/test/text/vtt_text_parser_unit.js +59 -40
- package/test/text/web_vtt_generator_unit.js +9 -3
- package/test/{media/transmuxer_integration.js → transmuxer/muxjs_transmuxer_integration.js} +30 -33
- package/test/transmuxer/transmuxer_engine_integration.js +60 -0
- package/test/transmuxer/transmuxer_integration.js +296 -0
- package/test/util/cmcd_manager_unit.js +162 -13
- package/test/util/language_utils_unit.js +9 -2
- package/test/util/mp4_box_parsers_unit.js +39 -4
- package/test/util/periods_unit.js +34 -26
- package/test/util/stream_utils_unit.js +60 -107
- package/ui/controls.js +19 -1
- package/ui/controls.less +1 -0
- package/ui/externs/ui.js +13 -3
- package/ui/language_utils.js +15 -0
- package/ui/less/thumbnails.less +26 -0
- package/ui/locales/oc.json +42 -0
- package/ui/pip_button.js +83 -2
- package/ui/range_element.js +15 -0
- package/ui/resolution_selection.js +9 -2
- package/ui/seek_bar.js +230 -1
- package/ui/ui.js +5 -0
- package/lib/cea/i_caption_decoder.js +0 -51
- package/lib/cea/i_cea_parser.js +0 -68
|
@@ -12,10 +12,11 @@ goog.require('shaka.media.Capabilities');
|
|
|
12
12
|
goog.require('shaka.media.ContentWorkarounds');
|
|
13
13
|
goog.require('shaka.media.ClosedCaptionParser');
|
|
14
14
|
goog.require('shaka.media.IClosedCaptionParser');
|
|
15
|
+
goog.require('shaka.media.ManifestParser');
|
|
15
16
|
goog.require('shaka.media.SegmentReference');
|
|
16
17
|
goog.require('shaka.media.TimeRangesUtils');
|
|
17
|
-
goog.require('shaka.media.Transmuxer');
|
|
18
18
|
goog.require('shaka.text.TextEngine');
|
|
19
|
+
goog.require('shaka.transmuxer.TransmuxerEngine');
|
|
19
20
|
goog.require('shaka.util.BufferUtils');
|
|
20
21
|
goog.require('shaka.util.Destroyer');
|
|
21
22
|
goog.require('shaka.util.Error');
|
|
@@ -25,6 +26,8 @@ goog.require('shaka.util.IDestroyable');
|
|
|
25
26
|
goog.require('shaka.util.Id3Utils');
|
|
26
27
|
goog.require('shaka.util.ManifestParserUtils');
|
|
27
28
|
goog.require('shaka.util.MimeUtils');
|
|
29
|
+
goog.require('shaka.util.Mp4BoxParsers');
|
|
30
|
+
goog.require('shaka.util.Mp4Parser');
|
|
28
31
|
goog.require('shaka.util.Platform');
|
|
29
32
|
goog.require('shaka.util.PublicPromise');
|
|
30
33
|
goog.require('shaka.util.TsParser');
|
|
@@ -100,7 +103,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
100
103
|
/** @private {shaka.util.EventManager} */
|
|
101
104
|
this.eventManager_ = new shaka.util.EventManager();
|
|
102
105
|
|
|
103
|
-
/** @private {!Object.<string, !shaka.
|
|
106
|
+
/** @private {!Object.<string, !shaka.extern.Transmuxer>} */
|
|
104
107
|
this.transmuxers_ = {};
|
|
105
108
|
|
|
106
109
|
/** @private {?shaka.media.IClosedCaptionParser} */
|
|
@@ -115,12 +118,24 @@ shaka.media.MediaSourceEngine = class {
|
|
|
115
118
|
/** @private {MediaSource} */
|
|
116
119
|
this.mediaSource_ = this.createMediaSource(this.mediaSourceOpen_);
|
|
117
120
|
|
|
121
|
+
/** @private {boolean} */
|
|
122
|
+
this.reloadingMediaSource_ = false;
|
|
123
|
+
|
|
118
124
|
/** @type {!shaka.util.Destroyer} */
|
|
119
125
|
this.destroyer_ = new shaka.util.Destroyer(() => this.doDestroy_());
|
|
120
126
|
|
|
121
127
|
/** @private {boolean} */
|
|
122
128
|
this.sequenceMode_ = false;
|
|
123
129
|
|
|
130
|
+
/** @private {string} */
|
|
131
|
+
this.manifestType_ = shaka.media.ManifestParser.UNKNOWN;
|
|
132
|
+
|
|
133
|
+
/** @private {boolean} */
|
|
134
|
+
this.ignoreManifestTimestampsInSegmentsMode_ = false;
|
|
135
|
+
|
|
136
|
+
/** @private {boolean} */
|
|
137
|
+
this.attemptTimestampOffsetCalculation_ = false;
|
|
138
|
+
|
|
124
139
|
/** @private {!shaka.util.PublicPromise.<number>} */
|
|
125
140
|
this.textSequenceModeOffset_ = new shaka.util.PublicPromise();
|
|
126
141
|
}
|
|
@@ -174,12 +189,15 @@ shaka.media.MediaSourceEngine = class {
|
|
|
174
189
|
* @return {boolean}
|
|
175
190
|
*/
|
|
176
191
|
static isStreamSupported(stream) {
|
|
177
|
-
const
|
|
192
|
+
const MimeUtils = shaka.util.MimeUtils;
|
|
193
|
+
const fullMimeType = MimeUtils.getFullType(stream.mimeType, stream.codecs);
|
|
194
|
+
const extendedMimeType = MimeUtils.getExtendedType(stream);
|
|
195
|
+
const fullMimeTypeWithAllCodecs = MimeUtils.getFullTypeWithAllCodecs(
|
|
178
196
|
stream.mimeType, stream.codecs);
|
|
179
|
-
const
|
|
197
|
+
const TransmuxerEngine = shaka.transmuxer.TransmuxerEngine;
|
|
180
198
|
return shaka.text.TextEngine.isTypeSupported(fullMimeType) ||
|
|
181
199
|
shaka.media.Capabilities.isTypeSupported(extendedMimeType) ||
|
|
182
|
-
|
|
200
|
+
TransmuxerEngine.isSupported(fullMimeTypeWithAllCodecs, stream.type);
|
|
183
201
|
}
|
|
184
202
|
|
|
185
203
|
/**
|
|
@@ -227,7 +245,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
227
245
|
'application/ttml+xml',
|
|
228
246
|
'application/mp4; codecs="stpp"',
|
|
229
247
|
// Containerless types
|
|
230
|
-
...shaka.
|
|
248
|
+
...shaka.util.MimeUtils.RAW_FORMATS,
|
|
231
249
|
];
|
|
232
250
|
|
|
233
251
|
const support = {};
|
|
@@ -238,7 +256,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
238
256
|
support[type] = true;
|
|
239
257
|
} else {
|
|
240
258
|
support[type] = shaka.media.Capabilities.isTypeSupported(type) ||
|
|
241
|
-
shaka.
|
|
259
|
+
shaka.transmuxer.TransmuxerEngine.isSupported(type);
|
|
242
260
|
}
|
|
243
261
|
} else {
|
|
244
262
|
support[type] = shaka.util.Platform.supportsMediaType(type);
|
|
@@ -344,70 +362,112 @@ shaka.media.MediaSourceEngine = class {
|
|
|
344
362
|
* shaka.extern.Stream>} streamsByType
|
|
345
363
|
* A map of content types to streams. All streams must be supported
|
|
346
364
|
* according to MediaSourceEngine.isStreamSupported.
|
|
347
|
-
* @param {boolean} forceTransmux
|
|
348
|
-
* If true, this will transmux AAC and TS content even if it is natively
|
|
349
|
-
* supported.
|
|
350
365
|
* @param {boolean=} sequenceMode
|
|
351
366
|
* If true, the media segments are appended to the SourceBuffer in strict
|
|
352
367
|
* sequence.
|
|
368
|
+
* @param {string=} manifestType
|
|
369
|
+
* Indicates the type of the manifest.
|
|
370
|
+
* @param {boolean=} ignoreManifestTimestampsInSegmentsMode
|
|
371
|
+
* If true, don't adjust the timestamp offset to account for manifest
|
|
372
|
+
* segment durations being out of sync with segment durations. In other
|
|
373
|
+
* words, assume that there are no gaps in the segments when appending
|
|
374
|
+
* to the SourceBuffer, even if the manifest and segment times disagree.
|
|
375
|
+
* Indicates if the manifest has text streams.
|
|
353
376
|
*
|
|
354
377
|
* @return {!Promise}
|
|
355
378
|
*/
|
|
356
|
-
async init(streamsByType,
|
|
357
|
-
|
|
358
|
-
|
|
379
|
+
async init(streamsByType, sequenceMode=false,
|
|
380
|
+
manifestType=shaka.media.ManifestParser.UNKNOWN,
|
|
381
|
+
ignoreManifestTimestampsInSegmentsMode=false) {
|
|
359
382
|
await this.mediaSourceOpen_;
|
|
360
383
|
|
|
361
384
|
this.sequenceMode_ = sequenceMode;
|
|
385
|
+
this.manifestType_ = manifestType;
|
|
386
|
+
this.ignoreManifestTimestampsInSegmentsMode_ =
|
|
387
|
+
ignoreManifestTimestampsInSegmentsMode;
|
|
388
|
+
|
|
389
|
+
this.attemptTimestampOffsetCalculation_ = !this.sequenceMode_ &&
|
|
390
|
+
this.manifestType_ == shaka.media.ManifestParser.HLS &&
|
|
391
|
+
!this.ignoreManifestTimestampsInSegmentsMode_;
|
|
362
392
|
|
|
363
393
|
for (const contentType of streamsByType.keys()) {
|
|
364
394
|
const stream = streamsByType.get(contentType);
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
395
|
+
this.initSourceBuffer_(contentType, stream);
|
|
396
|
+
this.queues_[contentType] = [];
|
|
397
|
+
}
|
|
398
|
+
}
|
|
368
399
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
400
|
+
/**
|
|
401
|
+
* Initialize a specific SourceBuffer.
|
|
402
|
+
*
|
|
403
|
+
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
|
|
404
|
+
* @param {shaka.extern.Stream} stream
|
|
405
|
+
* @private
|
|
406
|
+
*/
|
|
407
|
+
initSourceBuffer_(contentType, stream) {
|
|
408
|
+
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
409
|
+
|
|
410
|
+
goog.asserts.assert(
|
|
411
|
+
shaka.media.MediaSourceEngine.isStreamSupported(stream),
|
|
412
|
+
'Type negotiation should happen before MediaSourceEngine.init!');
|
|
413
|
+
|
|
414
|
+
let mimeType = shaka.util.MimeUtils.getFullType(
|
|
415
|
+
stream.mimeType, stream.codecs);
|
|
416
|
+
if (contentType == ContentType.TEXT) {
|
|
417
|
+
this.reinitText(mimeType, this.sequenceMode_, stream.external);
|
|
418
|
+
} else {
|
|
419
|
+
let needTransmux = this.config_.forceTransmux;
|
|
420
|
+
if (!shaka.media.Capabilities.isTypeSupported(mimeType) ||
|
|
421
|
+
(!this.sequenceMode_ &&
|
|
422
|
+
shaka.util.MimeUtils.RAW_FORMATS.includes(mimeType))) {
|
|
423
|
+
needTransmux = true;
|
|
424
|
+
}
|
|
425
|
+
const mimeTypeWithAllCodecs =
|
|
426
|
+
shaka.util.MimeUtils.getFullTypeWithAllCodecs(
|
|
427
|
+
stream.mimeType, stream.codecs);
|
|
428
|
+
const TransmuxerEngine = shaka.transmuxer.TransmuxerEngine;
|
|
429
|
+
if (needTransmux) {
|
|
430
|
+
const transmuxerPlugin =
|
|
431
|
+
TransmuxerEngine.findTransmuxer(mimeTypeWithAllCodecs);
|
|
432
|
+
if (transmuxerPlugin) {
|
|
433
|
+
const transmuxer = transmuxerPlugin();
|
|
434
|
+
this.transmuxers_[contentType] = transmuxer;
|
|
379
435
|
mimeType =
|
|
380
|
-
|
|
436
|
+
transmuxer.convertCodecs(contentType, mimeTypeWithAllCodecs);
|
|
381
437
|
}
|
|
382
|
-
|
|
438
|
+
}
|
|
439
|
+
const type = mimeType + this.config_.sourceBufferExtraFeatures;
|
|
383
440
|
|
|
384
|
-
|
|
441
|
+
this.destroyer_.ensureNotDestroyed();
|
|
385
442
|
|
|
386
|
-
|
|
443
|
+
let sourceBuffer;
|
|
387
444
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
445
|
+
try {
|
|
446
|
+
sourceBuffer = this.mediaSource_.addSourceBuffer(type);
|
|
447
|
+
} catch (exception) {
|
|
448
|
+
throw new shaka.util.Error(
|
|
449
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
450
|
+
shaka.util.Error.Category.MEDIA,
|
|
451
|
+
shaka.util.Error.Code.MEDIA_SOURCE_OPERATION_THREW,
|
|
452
|
+
exception,
|
|
453
|
+
'The mediaSource_ status was' + this.mediaSource_.readyState +
|
|
454
|
+
'expected \'open\'');
|
|
455
|
+
}
|
|
399
456
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
this.eventManager_.listen(
|
|
404
|
-
sourceBuffer, 'updateend',
|
|
405
|
-
() => this.onUpdateEnd_(contentType));
|
|
406
|
-
this.sourceBuffers_[contentType] = sourceBuffer;
|
|
407
|
-
this.sourceBufferTypes_[contentType] = mimeType;
|
|
408
|
-
this.queues_[contentType] = [];
|
|
409
|
-
this.expectedEncryption_[contentType] = !!stream.drmInfos.length;
|
|
457
|
+
if (this.sequenceMode_) {
|
|
458
|
+
sourceBuffer.mode =
|
|
459
|
+
shaka.media.MediaSourceEngine.SourceBufferMode_.SEQUENCE;
|
|
410
460
|
}
|
|
461
|
+
|
|
462
|
+
this.eventManager_.listen(
|
|
463
|
+
sourceBuffer, 'error',
|
|
464
|
+
() => this.onError_(contentType));
|
|
465
|
+
this.eventManager_.listen(
|
|
466
|
+
sourceBuffer, 'updateend',
|
|
467
|
+
() => this.onUpdateEnd_(contentType));
|
|
468
|
+
this.sourceBuffers_[contentType] = sourceBuffer;
|
|
469
|
+
this.sourceBufferTypes_[contentType] = mimeType;
|
|
470
|
+
this.expectedEncryption_[contentType] = !!stream.drmInfos.length;
|
|
411
471
|
}
|
|
412
472
|
}
|
|
413
473
|
|
|
@@ -440,6 +500,9 @@ shaka.media.MediaSourceEngine = class {
|
|
|
440
500
|
* object has been destroyed.
|
|
441
501
|
*/
|
|
442
502
|
ended() {
|
|
503
|
+
if (this.reloadingMediaSource_) {
|
|
504
|
+
return false;
|
|
505
|
+
}
|
|
443
506
|
return this.mediaSource_ ? this.mediaSource_.readyState == 'ended' : true;
|
|
444
507
|
}
|
|
445
508
|
|
|
@@ -450,6 +513,9 @@ shaka.media.MediaSourceEngine = class {
|
|
|
450
513
|
* @return {?number} The timestamp in seconds, or null if nothing is buffered.
|
|
451
514
|
*/
|
|
452
515
|
bufferStart(contentType) {
|
|
516
|
+
if (this.reloadingMediaSource_) {
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
453
519
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
454
520
|
if (contentType == ContentType.TEXT) {
|
|
455
521
|
return this.textEngine_.bufferStart();
|
|
@@ -465,6 +531,9 @@ shaka.media.MediaSourceEngine = class {
|
|
|
465
531
|
* @return {?number} The timestamp in seconds, or null if nothing is buffered.
|
|
466
532
|
*/
|
|
467
533
|
bufferEnd(contentType) {
|
|
534
|
+
if (this.reloadingMediaSource_) {
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
468
537
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
469
538
|
if (contentType == ContentType.TEXT) {
|
|
470
539
|
return this.textEngine_.bufferEnd();
|
|
@@ -482,6 +551,9 @@ shaka.media.MediaSourceEngine = class {
|
|
|
482
551
|
* @return {boolean}
|
|
483
552
|
*/
|
|
484
553
|
isBuffered(contentType, time) {
|
|
554
|
+
if (this.reloadingMediaSource_) {
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
485
557
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
486
558
|
if (contentType == ContentType.TEXT) {
|
|
487
559
|
return this.textEngine_.isBuffered(time);
|
|
@@ -500,6 +572,9 @@ shaka.media.MediaSourceEngine = class {
|
|
|
500
572
|
* @return {number} The amount of time buffered ahead in seconds.
|
|
501
573
|
*/
|
|
502
574
|
bufferedAheadOf(contentType, time) {
|
|
575
|
+
if (this.reloadingMediaSource_) {
|
|
576
|
+
return 0;
|
|
577
|
+
}
|
|
503
578
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
504
579
|
if (contentType == ContentType.TEXT) {
|
|
505
580
|
return this.textEngine_.bufferedAheadOf(time);
|
|
@@ -518,11 +593,12 @@ shaka.media.MediaSourceEngine = class {
|
|
|
518
593
|
const TimeRangesUtils = shaka.media.TimeRangesUtils;
|
|
519
594
|
|
|
520
595
|
const info = {
|
|
521
|
-
total:
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
596
|
+
total: this.reloadingMediaSource_ ? [] :
|
|
597
|
+
TimeRangesUtils.getBufferedInfo(this.video_.buffered),
|
|
598
|
+
audio: this.reloadingMediaSource_ ? [] :
|
|
599
|
+
TimeRangesUtils.getBufferedInfo(this.getBuffered_(ContentType.AUDIO)),
|
|
600
|
+
video: this.reloadingMediaSource_ ? [] :
|
|
601
|
+
TimeRangesUtils.getBufferedInfo(this.getBuffered_(ContentType.VIDEO)),
|
|
526
602
|
text: [],
|
|
527
603
|
};
|
|
528
604
|
|
|
@@ -568,6 +644,83 @@ shaka.media.MediaSourceEngine = class {
|
|
|
568
644
|
return new shaka.media.ClosedCaptionParser(mimeType);
|
|
569
645
|
}
|
|
570
646
|
|
|
647
|
+
/**
|
|
648
|
+
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
|
|
649
|
+
* @param {!BufferSource} data
|
|
650
|
+
* @param {?shaka.media.SegmentReference} reference The segment reference
|
|
651
|
+
* we are appending, or null for init segments
|
|
652
|
+
* @param {!string} mimeType
|
|
653
|
+
* @param {!number} timestampOffset
|
|
654
|
+
* @return {?number}
|
|
655
|
+
* @private
|
|
656
|
+
*/
|
|
657
|
+
getTimestampAndDispatchMetadata_(contentType, data, reference, mimeType,
|
|
658
|
+
timestampOffset) {
|
|
659
|
+
let timestamp = null;
|
|
660
|
+
|
|
661
|
+
const uint8ArrayData = shaka.util.BufferUtils.toUint8(data);
|
|
662
|
+
if (shaka.util.MimeUtils.RAW_FORMATS.includes(mimeType)) {
|
|
663
|
+
const frames = shaka.util.Id3Utils.getID3Frames(uint8ArrayData);
|
|
664
|
+
if (frames.length && reference) {
|
|
665
|
+
const metadataTimestamp = frames.find((frame) => {
|
|
666
|
+
return frame.description ===
|
|
667
|
+
'com.apple.streaming.transportStreamTimestamp';
|
|
668
|
+
});
|
|
669
|
+
if (metadataTimestamp && metadataTimestamp.data) {
|
|
670
|
+
timestamp = Math.round(metadataTimestamp.data) / 1000;
|
|
671
|
+
}
|
|
672
|
+
/** @private {shaka.extern.ID3Metadata} */
|
|
673
|
+
const metadata = {
|
|
674
|
+
cueTime: reference.startTime,
|
|
675
|
+
data: uint8ArrayData,
|
|
676
|
+
frames: frames,
|
|
677
|
+
dts: reference.startTime,
|
|
678
|
+
pts: reference.startTime,
|
|
679
|
+
};
|
|
680
|
+
this.onMetadata_([metadata], /* offset= */ 0, reference.endTime);
|
|
681
|
+
}
|
|
682
|
+
} else if (mimeType.includes('/mp4') &&
|
|
683
|
+
reference && reference.timestampOffset == 0 &&
|
|
684
|
+
reference.initSegmentReference &&
|
|
685
|
+
reference.initSegmentReference.timescale) {
|
|
686
|
+
const timescale = reference.initSegmentReference.timescale;
|
|
687
|
+
if (!isNaN(timescale)) {
|
|
688
|
+
const Mp4Parser = shaka.util.Mp4Parser;
|
|
689
|
+
let startTime = 0;
|
|
690
|
+
let parsedMedia = false;
|
|
691
|
+
new Mp4Parser()
|
|
692
|
+
.box('moof', Mp4Parser.children)
|
|
693
|
+
.box('traf', Mp4Parser.children)
|
|
694
|
+
.fullBox('tfdt', (box) => {
|
|
695
|
+
goog.asserts.assert(
|
|
696
|
+
box.version == 0 || box.version == 1,
|
|
697
|
+
'TFDT version can only be 0 or 1');
|
|
698
|
+
const parsed = shaka.util.Mp4BoxParsers.parseTFDTInaccurate(
|
|
699
|
+
box.reader, box.version);
|
|
700
|
+
startTime = parsed.baseMediaDecodeTime / timescale;
|
|
701
|
+
parsedMedia = true;
|
|
702
|
+
box.parser.stop();
|
|
703
|
+
}).parse(data, /* partialOkay= */ true);
|
|
704
|
+
if (parsedMedia) {
|
|
705
|
+
timestamp = startTime;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
} else if (!mimeType.includes('/mp4') && !mimeType.includes('/webm') &&
|
|
709
|
+
shaka.util.TsParser.probe(uint8ArrayData)) {
|
|
710
|
+
const tsParser = new shaka.util.TsParser().parse(uint8ArrayData);
|
|
711
|
+
const startTime = tsParser.getStartTime()[contentType];
|
|
712
|
+
if (startTime != null) {
|
|
713
|
+
timestamp = startTime;
|
|
714
|
+
}
|
|
715
|
+
const metadata = tsParser.getMetadata();
|
|
716
|
+
if (metadata.length) {
|
|
717
|
+
this.onMetadata_(metadata, timestampOffset,
|
|
718
|
+
reference ? reference.endTime : null);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
return timestamp;
|
|
722
|
+
}
|
|
723
|
+
|
|
571
724
|
/**
|
|
572
725
|
* Enqueue an operation to append data to the SourceBuffer.
|
|
573
726
|
* Start and end times are needed for TextEngine, but not for MediaSource.
|
|
@@ -578,6 +731,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
578
731
|
* @param {!BufferSource} data
|
|
579
732
|
* @param {?shaka.media.SegmentReference} reference The segment reference
|
|
580
733
|
* we are appending, or null for init segments
|
|
734
|
+
* @param {shaka.extern.Stream} stream
|
|
581
735
|
* @param {?boolean} hasClosedCaptions True if the buffer contains CEA closed
|
|
582
736
|
* captions
|
|
583
737
|
* @param {boolean=} seeked True if we just seeked
|
|
@@ -588,7 +742,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
588
742
|
* @return {!Promise}
|
|
589
743
|
*/
|
|
590
744
|
async appendBuffer(
|
|
591
|
-
contentType, data, reference, hasClosedCaptions, seeked = false,
|
|
745
|
+
contentType, data, reference, stream, hasClosedCaptions, seeked = false,
|
|
592
746
|
adaptation = false, isChunkedData = false) {
|
|
593
747
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
594
748
|
|
|
@@ -605,77 +759,49 @@ shaka.media.MediaSourceEngine = class {
|
|
|
605
759
|
return;
|
|
606
760
|
}
|
|
607
761
|
|
|
608
|
-
|
|
762
|
+
let timestampOffset = this.sourceBuffers_[contentType].timestampOffset;
|
|
763
|
+
|
|
609
764
|
let mimeType = this.sourceBufferTypes_[contentType];
|
|
610
765
|
if (this.transmuxers_[contentType]) {
|
|
611
766
|
mimeType = this.transmuxers_[contentType].getOrginalMimeType();
|
|
612
767
|
}
|
|
613
|
-
if (
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
768
|
+
if (reference) {
|
|
769
|
+
const timestamp = this.getTimestampAndDispatchMetadata_(
|
|
770
|
+
contentType, data, reference, mimeType, timestampOffset);
|
|
771
|
+
if (timestamp != null) {
|
|
772
|
+
const calculatedTimestampOffset = reference.startTime - timestamp;
|
|
773
|
+
const timestampOffsetDifference =
|
|
774
|
+
Math.abs(timestampOffset - calculatedTimestampOffset);
|
|
775
|
+
if (timestampOffsetDifference >= 0.1 || seeked || adaptation) {
|
|
776
|
+
timestampOffset = calculatedTimestampOffset;
|
|
777
|
+
if (this.attemptTimestampOffsetCalculation_) {
|
|
778
|
+
this.enqueueOperation_(
|
|
779
|
+
contentType,
|
|
780
|
+
() => this.abort_(contentType));
|
|
781
|
+
this.enqueueOperation_(
|
|
782
|
+
contentType,
|
|
783
|
+
() => this.setTimestampOffset_(contentType, timestampOffset));
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
// Timestamps can only be reliably extracted from video, not audio.
|
|
787
|
+
// Packed audio formats do not have internal timestamps at all.
|
|
788
|
+
// Prefer video for this when available.
|
|
789
|
+
const isBestSourceBufferForTimestamps =
|
|
790
|
+
contentType == ContentType.VIDEO ||
|
|
791
|
+
!(ContentType.VIDEO in this.sourceBuffers_);
|
|
792
|
+
if (this.sequenceMode_ && isBestSourceBufferForTimestamps) {
|
|
793
|
+
this.textSequenceModeOffset_.resolve(timestampOffset);
|
|
638
794
|
}
|
|
639
795
|
}
|
|
640
796
|
}
|
|
641
|
-
|
|
642
|
-
if (this.transmuxers_[contentType]) {
|
|
643
|
-
// When seeked we should reset the transmuxer captionstreams
|
|
644
|
-
// so it does not ignores the captions from previous segments
|
|
645
|
-
if (seeked) {
|
|
646
|
-
this.transmuxers_[contentType].resetCaptions();
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
const transmuxedData =
|
|
650
|
-
await this.transmuxers_[contentType].transmux(data);
|
|
651
|
-
// For HLS CEA-608/708 CLOSED-CAPTIONS, text data is embedded in
|
|
652
|
-
// the video stream, so textEngine may not have been initialized.
|
|
653
|
-
if (!this.textEngine_) {
|
|
654
|
-
this.reinitText(shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE,
|
|
655
|
-
this.sequenceMode_, /* external= */ false);
|
|
656
|
-
}
|
|
657
|
-
// This doesn't work for native TS support (ex. Edge/Chromecast),
|
|
658
|
-
// since no transmuxing is needed for native TS.
|
|
659
|
-
if (transmuxedData.captions && transmuxedData.captions.length) {
|
|
660
|
-
const videoOffset =
|
|
661
|
-
this.sourceBuffers_[ContentType.VIDEO].timestampOffset;
|
|
662
|
-
const closedCaptions = this.textEngine_
|
|
663
|
-
.convertMuxjsCaptionsToShakaCaptions(transmuxedData.captions);
|
|
664
|
-
this.textEngine_.storeAndAppendClosedCaptions(
|
|
665
|
-
closedCaptions,
|
|
666
|
-
reference ? reference.startTime : null,
|
|
667
|
-
reference ? reference.endTime : null,
|
|
668
|
-
videoOffset);
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
data = transmuxedData.data;
|
|
672
|
-
} else if (hasClosedCaptions && contentType == ContentType.VIDEO) {
|
|
797
|
+
if (hasClosedCaptions && contentType == ContentType.VIDEO) {
|
|
673
798
|
if (!this.textEngine_) {
|
|
674
799
|
this.reinitText(shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE,
|
|
675
800
|
this.sequenceMode_, /* external= */ false);
|
|
676
801
|
}
|
|
677
802
|
if (!this.captionParser_) {
|
|
678
|
-
|
|
803
|
+
const basicType = mimeType.split(';', 1)[0];
|
|
804
|
+
this.captionParser_ = this.getCaptionParser(basicType);
|
|
679
805
|
}
|
|
680
806
|
// If it is the init segment for closed captions, initialize the closed
|
|
681
807
|
// caption parser.
|
|
@@ -684,92 +810,23 @@ shaka.media.MediaSourceEngine = class {
|
|
|
684
810
|
} else {
|
|
685
811
|
const closedCaptions = this.captionParser_.parseFrom(data);
|
|
686
812
|
if (closedCaptions.length) {
|
|
687
|
-
const videoOffset =
|
|
688
|
-
this.sourceBuffers_[ContentType.VIDEO].timestampOffset;
|
|
689
813
|
this.textEngine_.storeAndAppendClosedCaptions(
|
|
690
814
|
closedCaptions,
|
|
691
815
|
reference.startTime,
|
|
692
816
|
reference.endTime,
|
|
693
|
-
|
|
817
|
+
timestampOffset);
|
|
694
818
|
}
|
|
695
819
|
}
|
|
696
820
|
}
|
|
697
821
|
|
|
822
|
+
if (this.transmuxers_[contentType]) {
|
|
823
|
+
data = await this.transmuxers_[contentType].transmux(
|
|
824
|
+
data, stream, reference, this.mediaSource_.duration);
|
|
825
|
+
}
|
|
826
|
+
|
|
698
827
|
data = this.workAroundBrokenPlatforms_(
|
|
699
828
|
data, reference ? reference.startTime : null, contentType);
|
|
700
829
|
|
|
701
|
-
const sourceBuffer = this.sourceBuffers_[contentType];
|
|
702
|
-
const SEQUENCE = shaka.media.MediaSourceEngine.SourceBufferMode_.SEQUENCE;
|
|
703
|
-
|
|
704
|
-
if (this.sequenceMode_ && sourceBuffer.mode != SEQUENCE && reference) {
|
|
705
|
-
// This is the first media segment to be appended to a SourceBuffer in
|
|
706
|
-
// sequence mode. We set the mode late so that we can trick MediaSource
|
|
707
|
-
// into extracting a timestamp for us to align text segments in sequence
|
|
708
|
-
// mode.
|
|
709
|
-
|
|
710
|
-
const duration = this.mediaSource_.duration;
|
|
711
|
-
|
|
712
|
-
// Timestamps can only be reliably extracted from video, not audio.
|
|
713
|
-
// Packed audio formats do not have internal timestamps at all.
|
|
714
|
-
// Prefer video for this when available.
|
|
715
|
-
const isBestSourceBufferForTimestamps =
|
|
716
|
-
contentType == ContentType.VIDEO ||
|
|
717
|
-
!(ContentType.VIDEO in this.sourceBuffers_);
|
|
718
|
-
if (isBestSourceBufferForTimestamps) {
|
|
719
|
-
// Append the segment in segments mode first, with offset of 0 and an
|
|
720
|
-
// open append window.
|
|
721
|
-
const originalRange =
|
|
722
|
-
[sourceBuffer.appendWindowStart, sourceBuffer.appendWindowEnd];
|
|
723
|
-
sourceBuffer.appendWindowStart = 0;
|
|
724
|
-
sourceBuffer.appendWindowEnd = Infinity;
|
|
725
|
-
|
|
726
|
-
const originalOffset = sourceBuffer.timestampOffset;
|
|
727
|
-
sourceBuffer.timestampOffset = 0;
|
|
728
|
-
|
|
729
|
-
await this.enqueueOperation_(
|
|
730
|
-
contentType, () => this.append_(contentType, data));
|
|
731
|
-
// If the input buffer passed to SourceBuffer#appendBuffer() does not
|
|
732
|
-
// contain a complete media segment, the call will exit while the
|
|
733
|
-
// SourceBuffer's append state is
|
|
734
|
-
// still PARSING_MEDIA_SEGMENT. Reset the parser state by calling
|
|
735
|
-
// abort() to safely reset timestampOffset to 'originalOffset'.
|
|
736
|
-
// https://www.w3.org/TR/media-source-2/#sourcebuffer-segment-parser-loop
|
|
737
|
-
await this.enqueueOperation_(
|
|
738
|
-
contentType, () => this.abort_(contentType));
|
|
739
|
-
|
|
740
|
-
// Reset the offset and append window.
|
|
741
|
-
sourceBuffer.timestampOffset = originalOffset;
|
|
742
|
-
sourceBuffer.appendWindowStart = originalRange[0];
|
|
743
|
-
sourceBuffer.appendWindowEnd = originalRange[1];
|
|
744
|
-
|
|
745
|
-
// Now get the timestamp of the segment and compute the offset for text
|
|
746
|
-
// segments.
|
|
747
|
-
const mediaStartTime = shaka.media.TimeRangesUtils.bufferStart(
|
|
748
|
-
this.getBuffered_(contentType));
|
|
749
|
-
const textOffset = (reference.startTime || 0) - (mediaStartTime || 0);
|
|
750
|
-
this.textSequenceModeOffset_.resolve(textOffset);
|
|
751
|
-
|
|
752
|
-
// Clear the buffer.
|
|
753
|
-
await this.enqueueOperation_(
|
|
754
|
-
contentType,
|
|
755
|
-
() => this.remove_(contentType, 0, duration));
|
|
756
|
-
|
|
757
|
-
// Finally, flush the buffer in case of choppy video start on HLS fMP4.
|
|
758
|
-
if (contentType == ContentType.VIDEO) {
|
|
759
|
-
await this.enqueueOperation_(
|
|
760
|
-
contentType,
|
|
761
|
-
() => this.flush_(contentType));
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
// Now switch to sequence mode and fall through to our normal operations.
|
|
766
|
-
sourceBuffer.mode = SEQUENCE;
|
|
767
|
-
|
|
768
|
-
// When we change the buffer mode the duration is lost, so we need to set
|
|
769
|
-
// it explicitly.
|
|
770
|
-
await this.setDuration(duration);
|
|
771
|
-
}
|
|
772
|
-
|
|
773
830
|
if (reference && this.sequenceMode_ && contentType != ContentType.TEXT) {
|
|
774
831
|
// In sequence mode, for non-text streams, if we just cleared the buffer
|
|
775
832
|
// and are either performing an unbuffered seek or handling an automatic
|
|
@@ -919,23 +976,33 @@ shaka.media.MediaSourceEngine = class {
|
|
|
919
976
|
* @param {number} appendWindowEnd The timestamp to set the append window end
|
|
920
977
|
* to. For future appends, frames/samples with timestamps greater than this
|
|
921
978
|
* value will be dropped.
|
|
922
|
-
* @param {boolean}
|
|
923
|
-
* applied in this step.
|
|
979
|
+
* @param {boolean} ignoreTimestampOffset If true, the timestampOffset will
|
|
980
|
+
* not be applied in this step.
|
|
981
|
+
* @param {shaka.extern.Stream} stream The current stream.
|
|
982
|
+
* @param {!Map.<shaka.util.ManifestParserUtils.ContentType,
|
|
983
|
+
* shaka.extern.Stream>} streamsByType
|
|
984
|
+
* A map of content types to streams. All streams must be supported
|
|
985
|
+
* according to MediaSourceEngine.isStreamSupported.
|
|
986
|
+
*
|
|
924
987
|
* @return {!Promise}
|
|
925
988
|
*/
|
|
926
989
|
async setStreamProperties(
|
|
927
990
|
contentType, timestampOffset, appendWindowStart, appendWindowEnd,
|
|
928
|
-
|
|
991
|
+
ignoreTimestampOffset, stream, streamsByType) {
|
|
929
992
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
930
993
|
if (contentType == ContentType.TEXT) {
|
|
931
|
-
if (!
|
|
994
|
+
if (!ignoreTimestampOffset) {
|
|
932
995
|
this.textEngine_.setTimestampOffset(timestampOffset);
|
|
933
996
|
}
|
|
934
997
|
this.textEngine_.setAppendWindow(appendWindowStart, appendWindowEnd);
|
|
935
998
|
return;
|
|
936
999
|
}
|
|
1000
|
+
const operations = [];
|
|
937
1001
|
|
|
938
|
-
|
|
1002
|
+
const hasChangedCodecs =
|
|
1003
|
+
await this.codecSwitchIfNecessary_(contentType, stream, streamsByType);
|
|
1004
|
+
|
|
1005
|
+
if (!hasChangedCodecs) {
|
|
939
1006
|
// Queue an abort() to help MSE splice together overlapping segments.
|
|
940
1007
|
// We set appendWindowEnd when we change periods in DASH content, and the
|
|
941
1008
|
// period transition may result in overlap.
|
|
@@ -944,20 +1011,21 @@ shaka.media.MediaSourceEngine = class {
|
|
|
944
1011
|
// always enter a PARSING_MEDIA_SEGMENT state and we can't change the
|
|
945
1012
|
// timestamp offset. By calling abort(), we reset the state so we can
|
|
946
1013
|
// set it.
|
|
947
|
-
this.enqueueOperation_(
|
|
948
|
-
contentType,
|
|
949
|
-
() => this.abort_(contentType)),
|
|
950
|
-
// Don't set the timestampOffset here when in sequenceMode, since we
|
|
951
|
-
// use timestampOffset for a different purpose in that mode (e.g. to
|
|
952
|
-
// indicate where the current segment is).
|
|
953
|
-
sequenceMode ? Promise.resolve() : this.enqueueOperation_(
|
|
1014
|
+
operations.push(this.enqueueOperation_(
|
|
954
1015
|
contentType,
|
|
955
|
-
() => this.
|
|
956
|
-
|
|
1016
|
+
() => this.abort_(contentType)));
|
|
1017
|
+
}
|
|
1018
|
+
if (!ignoreTimestampOffset) {
|
|
1019
|
+
operations.push(this.enqueueOperation_(
|
|
957
1020
|
contentType,
|
|
958
|
-
() => this.
|
|
959
|
-
|
|
960
|
-
|
|
1021
|
+
() => this.setTimestampOffset_(contentType, timestampOffset)));
|
|
1022
|
+
}
|
|
1023
|
+
operations.push(this.enqueueOperation_(
|
|
1024
|
+
contentType,
|
|
1025
|
+
() => this.setAppendWindow_(
|
|
1026
|
+
contentType, appendWindowStart, appendWindowEnd)));
|
|
1027
|
+
|
|
1028
|
+
await Promise.all(operations);
|
|
961
1029
|
}
|
|
962
1030
|
|
|
963
1031
|
/**
|
|
@@ -1200,6 +1268,9 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1200
1268
|
* @private
|
|
1201
1269
|
*/
|
|
1202
1270
|
onUpdateEnd_(contentType) {
|
|
1271
|
+
if (this.reloadingMediaSource_) {
|
|
1272
|
+
return;
|
|
1273
|
+
}
|
|
1203
1274
|
const operation = this.queues_[contentType][0];
|
|
1204
1275
|
goog.asserts.assert(operation, 'Spurious updateend event!');
|
|
1205
1276
|
if (!operation) {
|
|
@@ -1238,7 +1309,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1238
1309
|
* Enqueue an operation which must block all other operations on all
|
|
1239
1310
|
* SourceBuffers.
|
|
1240
1311
|
*
|
|
1241
|
-
* @param {function()} run
|
|
1312
|
+
* @param {function():(Promise|undefined)} run
|
|
1242
1313
|
* @return {!Promise}
|
|
1243
1314
|
* @private
|
|
1244
1315
|
*/
|
|
@@ -1304,15 +1375,16 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1304
1375
|
}
|
|
1305
1376
|
}
|
|
1306
1377
|
|
|
1307
|
-
// Run the real operation, which
|
|
1378
|
+
// Run the real operation, which can be asynchronous.
|
|
1308
1379
|
try {
|
|
1309
|
-
run();
|
|
1380
|
+
await run();
|
|
1310
1381
|
} catch (exception) {
|
|
1311
1382
|
throw new shaka.util.Error(
|
|
1312
1383
|
shaka.util.Error.Severity.CRITICAL,
|
|
1313
1384
|
shaka.util.Error.Category.MEDIA,
|
|
1314
1385
|
shaka.util.Error.Code.MEDIA_SOURCE_OPERATION_THREW,
|
|
1315
|
-
exception
|
|
1386
|
+
exception,
|
|
1387
|
+
this.video_.error || 'No error in the media element');
|
|
1316
1388
|
} finally {
|
|
1317
1389
|
// Unblock the queues.
|
|
1318
1390
|
for (const contentType in this.sourceBuffers_) {
|
|
@@ -1355,7 +1427,8 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1355
1427
|
shaka.util.Error.Severity.CRITICAL,
|
|
1356
1428
|
shaka.util.Error.Category.MEDIA,
|
|
1357
1429
|
shaka.util.Error.Code.MEDIA_SOURCE_OPERATION_THREW,
|
|
1358
|
-
exception
|
|
1430
|
+
exception,
|
|
1431
|
+
this.video_.error || 'No error in the media element'));
|
|
1359
1432
|
}
|
|
1360
1433
|
this.popFromQueue_(contentType);
|
|
1361
1434
|
}
|
|
@@ -1410,15 +1483,17 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1410
1483
|
const encryptionExpected = this.expectedEncryption_[contentType];
|
|
1411
1484
|
|
|
1412
1485
|
// If:
|
|
1413
|
-
// 1.
|
|
1414
|
-
// 2. and
|
|
1415
|
-
// 3. and
|
|
1416
|
-
// 4. and the
|
|
1486
|
+
// 1. the configuration tells to insert fake encryption,
|
|
1487
|
+
// 2. and this is an init segment,
|
|
1488
|
+
// 3. and encryption is expected,
|
|
1489
|
+
// 4. and the platform requires encryption in all init segments,
|
|
1490
|
+
// 5. and the content is MP4 (mimeType == "video/mp4" or "audio/mp4"),
|
|
1417
1491
|
// then insert fake encryption metadata for init segments that lack it.
|
|
1418
1492
|
// The MP4 requirement is because we can currently only do this
|
|
1419
1493
|
// transformation on MP4 containers.
|
|
1420
1494
|
// See: https://github.com/shaka-project/shaka-player/issues/2759
|
|
1421
|
-
if (
|
|
1495
|
+
if (this.config_.insertFakeEncryptionInInit &&
|
|
1496
|
+
isInitSegment &&
|
|
1422
1497
|
encryptionExpected &&
|
|
1423
1498
|
shaka.util.Platform.requiresEncryptionInfoInAllInitSegments() &&
|
|
1424
1499
|
shaka.util.MimeUtils.getContainerType(
|
|
@@ -1430,6 +1505,188 @@ shaka.media.MediaSourceEngine = class {
|
|
|
1430
1505
|
return segment;
|
|
1431
1506
|
}
|
|
1432
1507
|
|
|
1508
|
+
/**
|
|
1509
|
+
* Prepare the SourceBuffer to parse a potentially new type or codec.
|
|
1510
|
+
*
|
|
1511
|
+
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
|
|
1512
|
+
* @param {string} mimeType
|
|
1513
|
+
* @param {?shaka.extern.Transmuxer} transmuxer
|
|
1514
|
+
* @private
|
|
1515
|
+
*/
|
|
1516
|
+
change_(contentType, mimeType, transmuxer) {
|
|
1517
|
+
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
1518
|
+
if (contentType === ContentType.TEXT) {
|
|
1519
|
+
shaka.log.debug(`Change not supported for ${contentType}`);
|
|
1520
|
+
return;
|
|
1521
|
+
}
|
|
1522
|
+
shaka.log.debug(
|
|
1523
|
+
`Change Type: ${this.sourceBufferTypes_[contentType]} -> ${mimeType}`);
|
|
1524
|
+
if (shaka.media.Capabilities.isChangeTypeSupported()) {
|
|
1525
|
+
if (this.transmuxers_[contentType]) {
|
|
1526
|
+
this.transmuxers_[contentType].destroy();
|
|
1527
|
+
}
|
|
1528
|
+
if (transmuxer) {
|
|
1529
|
+
this.transmuxers_[contentType] = transmuxer;
|
|
1530
|
+
}
|
|
1531
|
+
const type = mimeType + this.config_.sourceBufferExtraFeatures;
|
|
1532
|
+
this.sourceBuffers_[contentType].changeType(type);
|
|
1533
|
+
this.sourceBufferTypes_[contentType] = mimeType;
|
|
1534
|
+
} else {
|
|
1535
|
+
shaka.log.debug('Change Type not supported');
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
// Fake an 'updateend' event to resolve the operation.
|
|
1539
|
+
this.onUpdateEnd_(contentType);
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
/**
|
|
1543
|
+
* Enqueue an operation to prepare the SourceBuffer to parse a potentially new
|
|
1544
|
+
* type or codec.
|
|
1545
|
+
*
|
|
1546
|
+
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
|
|
1547
|
+
* @param {string} mimeType
|
|
1548
|
+
* @param {?shaka.extern.Transmuxer} transmuxer
|
|
1549
|
+
* @return {!Promise}
|
|
1550
|
+
*/
|
|
1551
|
+
changeType(contentType, mimeType, transmuxer) {
|
|
1552
|
+
return this.enqueueOperation_(
|
|
1553
|
+
contentType,
|
|
1554
|
+
() => this.change_(contentType, mimeType, transmuxer));
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
/**
|
|
1558
|
+
* Resets the MediaSource and re-adds source buffers due to codec mismatch
|
|
1559
|
+
*
|
|
1560
|
+
* @param {!Map.<shaka.util.ManifestParserUtils.ContentType,
|
|
1561
|
+
* shaka.extern.Stream>} streamsByType
|
|
1562
|
+
* @private
|
|
1563
|
+
*/
|
|
1564
|
+
async reset_(streamsByType) {
|
|
1565
|
+
this.reloadingMediaSource_ = true;
|
|
1566
|
+
const currentTime = this.video_.currentTime;
|
|
1567
|
+
try {
|
|
1568
|
+
this.eventManager_.removeAll();
|
|
1569
|
+
this.sourceBuffers_ = {};
|
|
1570
|
+
|
|
1571
|
+
const cleanup = [];
|
|
1572
|
+
for (const contentType in this.transmuxers_) {
|
|
1573
|
+
cleanup.push(this.transmuxers_[contentType].destroy());
|
|
1574
|
+
}
|
|
1575
|
+
for (const contentType in this.sourceBuffers_) {
|
|
1576
|
+
const sourceBuffer = this.sourceBuffers_[contentType];
|
|
1577
|
+
try {
|
|
1578
|
+
this.mediaSource_.removeSourceBuffer(sourceBuffer);
|
|
1579
|
+
} catch (e) {}
|
|
1580
|
+
}
|
|
1581
|
+
await Promise.all(cleanup);
|
|
1582
|
+
this.transmuxers_ = {};
|
|
1583
|
+
|
|
1584
|
+
const previousDuration = this.mediaSource_.duration;
|
|
1585
|
+
this.mediaSourceOpen_ = new shaka.util.PublicPromise();
|
|
1586
|
+
this.mediaSource_ = this.createMediaSource(this.mediaSourceOpen_);
|
|
1587
|
+
await this.mediaSourceOpen_;
|
|
1588
|
+
this.mediaSource_.duration = previousDuration;
|
|
1589
|
+
|
|
1590
|
+
const sourceBufferAdded = new shaka.util.PublicPromise();
|
|
1591
|
+
const sourceBuffers =
|
|
1592
|
+
/** @type {EventTarget} */(this.mediaSource_.sourceBuffers);
|
|
1593
|
+
|
|
1594
|
+
const totalOfBuffers = streamsByType.size;
|
|
1595
|
+
let numberOfSourceBufferAdded = 0;
|
|
1596
|
+
this.eventManager_.listen(sourceBuffers, 'addsourcebuffer', (event) => {
|
|
1597
|
+
numberOfSourceBufferAdded++;
|
|
1598
|
+
if (numberOfSourceBufferAdded === totalOfBuffers) {
|
|
1599
|
+
sourceBufferAdded.resolve();
|
|
1600
|
+
}
|
|
1601
|
+
});
|
|
1602
|
+
|
|
1603
|
+
for (const contentType of streamsByType.keys()) {
|
|
1604
|
+
const stream = streamsByType.get(contentType);
|
|
1605
|
+
this.initSourceBuffer_(contentType, stream);
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
// Fake a seek to catchup the playhead.
|
|
1609
|
+
this.video_.currentTime = currentTime;
|
|
1610
|
+
|
|
1611
|
+
await sourceBufferAdded;
|
|
1612
|
+
} finally {
|
|
1613
|
+
this.reloadingMediaSource_ = false;
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
/**
|
|
1618
|
+
* Resets the Media Source
|
|
1619
|
+
* @param {!Map.<shaka.util.ManifestParserUtils.ContentType,
|
|
1620
|
+
* shaka.extern.Stream>} streamsByType
|
|
1621
|
+
* @return {!Promise}
|
|
1622
|
+
*/
|
|
1623
|
+
reset(streamsByType) {
|
|
1624
|
+
return this.enqueueBlockingOperation_(
|
|
1625
|
+
() => this.reset_(streamsByType));
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
/**
|
|
1629
|
+
* Codec switch if necessary, this will not resolve until the codec
|
|
1630
|
+
* switch is over.
|
|
1631
|
+
* @param {shaka.util.ManifestParserUtils.ContentType} contentType
|
|
1632
|
+
* @param {shaka.extern.Stream} stream
|
|
1633
|
+
* @param {!Map.<shaka.util.ManifestParserUtils.ContentType,
|
|
1634
|
+
* shaka.extern.Stream>} streamsByType
|
|
1635
|
+
* @return {!Promise.<boolean>} true if there was a codec switch,
|
|
1636
|
+
* false otherwise.
|
|
1637
|
+
* @private
|
|
1638
|
+
*/
|
|
1639
|
+
async codecSwitchIfNecessary_(contentType, stream, streamsByType) {
|
|
1640
|
+
const MimeUtils = shaka.util.MimeUtils;
|
|
1641
|
+
const currentCodec = MimeUtils.getCodecBase(
|
|
1642
|
+
MimeUtils.getCodecs(this.sourceBufferTypes_[contentType]));
|
|
1643
|
+
const currentBasicType = MimeUtils.getBasicType(
|
|
1644
|
+
this.sourceBufferTypes_[contentType]);
|
|
1645
|
+
|
|
1646
|
+
/** @type {?shaka.extern.Transmuxer} */
|
|
1647
|
+
let transmuxer;
|
|
1648
|
+
let newMimeType = shaka.util.MimeUtils.getFullType(
|
|
1649
|
+
stream.mimeType, stream.codecs);
|
|
1650
|
+
let needTransmux = this.config_.forceTransmux;
|
|
1651
|
+
if (!shaka.media.Capabilities.isTypeSupported(newMimeType) ||
|
|
1652
|
+
(!this.sequenceMode_ &&
|
|
1653
|
+
shaka.util.MimeUtils.RAW_FORMATS.includes(newMimeType))) {
|
|
1654
|
+
needTransmux = true;
|
|
1655
|
+
}
|
|
1656
|
+
const newMimeTypeWithAllCodecs =
|
|
1657
|
+
shaka.util.MimeUtils.getFullTypeWithAllCodecs(
|
|
1658
|
+
stream.mimeType, stream.codecs);
|
|
1659
|
+
const TransmuxerEngine = shaka.transmuxer.TransmuxerEngine;
|
|
1660
|
+
if (needTransmux) {
|
|
1661
|
+
const transmuxerPlugin =
|
|
1662
|
+
TransmuxerEngine.findTransmuxer(newMimeTypeWithAllCodecs);
|
|
1663
|
+
if (transmuxerPlugin) {
|
|
1664
|
+
transmuxer = transmuxerPlugin();
|
|
1665
|
+
newMimeType =
|
|
1666
|
+
transmuxer.convertCodecs(contentType, newMimeTypeWithAllCodecs);
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
const newCodec = MimeUtils.getCodecBase(
|
|
1671
|
+
MimeUtils.getCodecs(newMimeType));
|
|
1672
|
+
const newBasicType = MimeUtils.getBasicType(newMimeType);
|
|
1673
|
+
|
|
1674
|
+
// Current/new codecs base and basic type match then no need to switch
|
|
1675
|
+
if (currentCodec === newCodec && currentBasicType === newBasicType) {
|
|
1676
|
+
return false;
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
if (shaka.media.Capabilities.isChangeTypeSupported()) {
|
|
1680
|
+
await this.changeType(contentType, newMimeType, transmuxer);
|
|
1681
|
+
} else {
|
|
1682
|
+
if (transmuxer) {
|
|
1683
|
+
transmuxer.destroy();
|
|
1684
|
+
}
|
|
1685
|
+
await this.reset(streamsByType);
|
|
1686
|
+
}
|
|
1687
|
+
return true;
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1433
1690
|
/**
|
|
1434
1691
|
* Update LCEVC DIL object when ready for LCEVC Decodes
|
|
1435
1692
|
* @param {?shaka.lcevc.Dil} lcevcDil
|
|
@@ -1473,16 +1730,3 @@ shaka.media.MediaSourceEngine.SourceBufferMode_ = {
|
|
|
1473
1730
|
SEQUENCE: 'sequence',
|
|
1474
1731
|
SEGMENTS: 'segments',
|
|
1475
1732
|
};
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
/**
|
|
1479
|
-
* MIME types of raw formats.
|
|
1480
|
-
*
|
|
1481
|
-
* @const {!Array.<string>}
|
|
1482
|
-
*/
|
|
1483
|
-
shaka.media.MediaSourceEngine.RAW_FORMATS = [
|
|
1484
|
-
'audio/aac',
|
|
1485
|
-
'audio/ac3',
|
|
1486
|
-
'audio/ec3',
|
|
1487
|
-
'audio/mpeg',
|
|
1488
|
-
];
|