shaka-player 4.3.10 → 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/release-please.yaml +7 -0
- package/AUTHORS +9 -0
- package/CHANGELOG.md +295 -200
- 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 +53 -3
- 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 +52 -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 +124 -112
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +124 -112
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +46 -28
- package/dist/locales.js +28 -28
- package/dist/receiver.compiled.debug.js +52 -46
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +52 -46
- 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 +1045 -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 +1100 -739
- 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 +13 -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 +79 -3
- 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 +17 -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 +3 -1
- 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_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/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 +11 -1
- 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 +223 -0
- package/ui/ui.js +5 -0
- package/lib/cea/i_caption_decoder.js +0 -51
- package/lib/cea/i_cea_parser.js +0 -68
|
@@ -17,6 +17,7 @@ goog.require('shaka.media.InitSegmentReference');
|
|
|
17
17
|
goog.require('shaka.media.MediaSourceEngine');
|
|
18
18
|
goog.require('shaka.media.SegmentIterator');
|
|
19
19
|
goog.require('shaka.media.SegmentReference');
|
|
20
|
+
goog.require('shaka.media.SegmentPrefetch');
|
|
20
21
|
goog.require('shaka.net.Backoff');
|
|
21
22
|
goog.require('shaka.net.NetworkingEngine');
|
|
22
23
|
goog.require('shaka.util.DelayedTick');
|
|
@@ -173,6 +174,21 @@ shaka.media.StreamingEngine = class {
|
|
|
173
174
|
const autoReset = true;
|
|
174
175
|
this.failureCallbackBackoff_ =
|
|
175
176
|
new shaka.net.Backoff(failureRetryParams, autoReset);
|
|
177
|
+
|
|
178
|
+
// Allow configuring the segment prefetch in middle of the playback.
|
|
179
|
+
for (const type of this.mediaStates_.keys()) {
|
|
180
|
+
const state = this.mediaStates_.get(type);
|
|
181
|
+
if (state.segmentPrefetch) {
|
|
182
|
+
state.segmentPrefetch.resetLimit(config.segmentPrefetchLimit);
|
|
183
|
+
if (!(config.segmentPrefetchLimit > 0)) {
|
|
184
|
+
// ResetLimit is still needed in this case,
|
|
185
|
+
// to abort existing prefetch operations.
|
|
186
|
+
state.segmentPrefetch = null;
|
|
187
|
+
}
|
|
188
|
+
} else if (config.segmentPrefetchLimit > 0) {
|
|
189
|
+
state.segmentPrefetch = this.createSegmentPrefetch_(state.stream);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
176
192
|
}
|
|
177
193
|
|
|
178
194
|
|
|
@@ -451,6 +467,10 @@ shaka.media.StreamingEngine = class {
|
|
|
451
467
|
return;
|
|
452
468
|
}
|
|
453
469
|
|
|
470
|
+
if (mediaState.segmentPrefetch) {
|
|
471
|
+
mediaState.segmentPrefetch.switchStream(stream);
|
|
472
|
+
}
|
|
473
|
+
|
|
454
474
|
if (stream.type == ContentType.TEXT) {
|
|
455
475
|
// Mime types are allowed to change for text streams.
|
|
456
476
|
// Reinitialize the text parser, but only if we are going to fetch the
|
|
@@ -806,10 +826,12 @@ shaka.media.StreamingEngine = class {
|
|
|
806
826
|
|
|
807
827
|
// Init MediaSourceEngine.
|
|
808
828
|
const mediaSourceEngine = this.playerInterface_.mediaSourceEngine;
|
|
809
|
-
const forceTransmux = this.config_.forceTransmux;
|
|
810
829
|
|
|
811
|
-
await mediaSourceEngine.init(streamsByType,
|
|
812
|
-
this.manifest_.sequenceMode
|
|
830
|
+
await mediaSourceEngine.init(streamsByType,
|
|
831
|
+
this.manifest_.sequenceMode,
|
|
832
|
+
this.manifest_.type,
|
|
833
|
+
this.manifest_.ignoreManifestTimestampsInSegmentsMode,
|
|
834
|
+
);
|
|
813
835
|
this.destroyer_.ensureNotDestroyed();
|
|
814
836
|
|
|
815
837
|
this.updateDuration();
|
|
@@ -837,6 +859,7 @@ shaka.media.StreamingEngine = class {
|
|
|
837
859
|
stream,
|
|
838
860
|
type: stream.type,
|
|
839
861
|
segmentIterator: null,
|
|
862
|
+
segmentPrefetch: this.createSegmentPrefetch_(stream),
|
|
840
863
|
lastSegmentReference: null,
|
|
841
864
|
lastInitSegmentReference: null,
|
|
842
865
|
lastTimestampOffset: null,
|
|
@@ -860,6 +883,31 @@ shaka.media.StreamingEngine = class {
|
|
|
860
883
|
});
|
|
861
884
|
}
|
|
862
885
|
|
|
886
|
+
/**
|
|
887
|
+
* Creates a media state.
|
|
888
|
+
*
|
|
889
|
+
* @param {shaka.extern.Stream} stream
|
|
890
|
+
* @return {shaka.media.SegmentPrefetch | null}
|
|
891
|
+
* @private
|
|
892
|
+
*/
|
|
893
|
+
createSegmentPrefetch_(stream) {
|
|
894
|
+
if (
|
|
895
|
+
stream.type !== shaka.util.ManifestParserUtils.ContentType.VIDEO &&
|
|
896
|
+
stream.type !== shaka.util.ManifestParserUtils.ContentType.AUDIO
|
|
897
|
+
) {
|
|
898
|
+
return null;
|
|
899
|
+
}
|
|
900
|
+
if (this.config_.segmentPrefetchLimit > 0) {
|
|
901
|
+
return new shaka.media.SegmentPrefetch(
|
|
902
|
+
this.config_.segmentPrefetchLimit,
|
|
903
|
+
stream,
|
|
904
|
+
(reference, stream, streamDataCallback) => {
|
|
905
|
+
return this.dispatchFetch_(reference, stream, streamDataCallback);
|
|
906
|
+
},
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
return null;
|
|
910
|
+
}
|
|
863
911
|
|
|
864
912
|
/**
|
|
865
913
|
* Sets the MediaSource's duration.
|
|
@@ -947,7 +995,7 @@ shaka.media.StreamingEngine = class {
|
|
|
947
995
|
mediaState.hasError = false;
|
|
948
996
|
}
|
|
949
997
|
} catch (error) {
|
|
950
|
-
await this.handleStreamingError_(error);
|
|
998
|
+
await this.handleStreamingError_(mediaState, error);
|
|
951
999
|
return;
|
|
952
1000
|
}
|
|
953
1001
|
|
|
@@ -1114,6 +1162,10 @@ shaka.media.StreamingEngine = class {
|
|
|
1114
1162
|
return this.config_.updateIntervalSeconds;
|
|
1115
1163
|
}
|
|
1116
1164
|
|
|
1165
|
+
if (mediaState.segmentPrefetch && mediaState.segmentIterator) {
|
|
1166
|
+
mediaState.segmentPrefetch.prefetchSegments(reference);
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1117
1169
|
const p = this.fetchAndAppend_(mediaState, presentationTime, reference);
|
|
1118
1170
|
p.catch(() => {}); // TODO(#1993): Handle asynchronous errors.
|
|
1119
1171
|
return null;
|
|
@@ -1278,10 +1330,10 @@ shaka.media.StreamingEngine = class {
|
|
|
1278
1330
|
stream.mimeType == 'audio/mp4';
|
|
1279
1331
|
const isReadableStreamSupported = window.ReadableStream;
|
|
1280
1332
|
// Enable MP4 low latency streaming with ReadableStream chunked data.
|
|
1281
|
-
//
|
|
1282
|
-
// segment.
|
|
1333
|
+
// And only for DASH and HLS with byterange optimization.
|
|
1283
1334
|
if (this.config_.lowLatencyMode && isReadableStreamSupported && isMP4 &&
|
|
1284
|
-
|
|
1335
|
+
(this.manifest_.type != shaka.media.ManifestParser.HLS ||
|
|
1336
|
+
reference.hasByterangeOptimization())) {
|
|
1285
1337
|
let remaining = new Uint8Array(0);
|
|
1286
1338
|
let processingResult = false;
|
|
1287
1339
|
let callbackCalled = false;
|
|
@@ -1443,7 +1495,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1443
1495
|
mediaState.hasError = true;
|
|
1444
1496
|
|
|
1445
1497
|
error.severity = shaka.util.Error.Severity.CRITICAL;
|
|
1446
|
-
await this.handleStreamingError_(error);
|
|
1498
|
+
await this.handleStreamingError_(mediaState, error);
|
|
1447
1499
|
}
|
|
1448
1500
|
}
|
|
1449
1501
|
}
|
|
@@ -1602,6 +1654,8 @@ shaka.media.StreamingEngine = class {
|
|
|
1602
1654
|
* @private
|
|
1603
1655
|
*/
|
|
1604
1656
|
async initSourceBuffer_(mediaState, reference) {
|
|
1657
|
+
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
1658
|
+
const MimeUtils = shaka.util.MimeUtils;
|
|
1605
1659
|
const StreamingEngine = shaka.media.StreamingEngine;
|
|
1606
1660
|
const logPrefix = StreamingEngine.logPrefix_(mediaState);
|
|
1607
1661
|
|
|
@@ -1620,28 +1674,69 @@ shaka.media.StreamingEngine = class {
|
|
|
1620
1674
|
reference.startTime <= appendWindowEnd,
|
|
1621
1675
|
logPrefix + ' segment should start before append window end');
|
|
1622
1676
|
|
|
1677
|
+
const codecs = MimeUtils.getCodecBase(mediaState.stream.codecs);
|
|
1678
|
+
const mimeType = MimeUtils.getBasicType(mediaState.stream.mimeType);
|
|
1623
1679
|
const timestampOffset = reference.timestampOffset;
|
|
1624
1680
|
if (timestampOffset != mediaState.lastTimestampOffset ||
|
|
1625
1681
|
appendWindowStart != mediaState.lastAppendWindowStart ||
|
|
1626
|
-
appendWindowEnd != mediaState.lastAppendWindowEnd
|
|
1682
|
+
appendWindowEnd != mediaState.lastAppendWindowEnd ||
|
|
1683
|
+
codecs != mediaState.lastCodecs ||
|
|
1684
|
+
mimeType != mediaState.lastMimeType) {
|
|
1627
1685
|
shaka.log.v1(logPrefix, 'setting timestamp offset to ' + timestampOffset);
|
|
1628
1686
|
shaka.log.v1(logPrefix,
|
|
1629
1687
|
'setting append window start to ' + appendWindowStart);
|
|
1630
1688
|
shaka.log.v1(logPrefix,
|
|
1631
1689
|
'setting append window end to ' + appendWindowEnd);
|
|
1632
1690
|
|
|
1691
|
+
if ((mediaState.lastCodecs && codecs != mediaState.lastCodecs) ||
|
|
1692
|
+
(mediaState.lastMimeType && mimeType != mediaState.lastMimeType)) {
|
|
1693
|
+
if (mediaState.type === ContentType.VIDEO) {
|
|
1694
|
+
/** @type {shaka.media.StreamingEngine.MediaState_} */
|
|
1695
|
+
const audioState = this.mediaStates_.get(ContentType.AUDIO);
|
|
1696
|
+
if (audioState) {
|
|
1697
|
+
audioState.lastInitSegmentReference = null;
|
|
1698
|
+
this.abortOperations_(audioState);
|
|
1699
|
+
}
|
|
1700
|
+
} else if (mediaState.type === ContentType.AUDIO) {
|
|
1701
|
+
/** @type {shaka.media.StreamingEngine.MediaState_} */
|
|
1702
|
+
const videoState = this.mediaStates_.get(ContentType.VIDEO);
|
|
1703
|
+
if (videoState) {
|
|
1704
|
+
videoState.lastInitSegmentReference = null;
|
|
1705
|
+
this.abortOperations_(videoState);
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1633
1710
|
const setProperties = async () => {
|
|
1711
|
+
/**
|
|
1712
|
+
* @type {!Map.<shaka.util.ManifestParserUtils.ContentType,
|
|
1713
|
+
* shaka.extern.Stream>}
|
|
1714
|
+
*/
|
|
1715
|
+
const streamsByType = new Map();
|
|
1716
|
+
if (this.currentVariant_.audio) {
|
|
1717
|
+
streamsByType.set(ContentType.AUDIO, this.currentVariant_.audio);
|
|
1718
|
+
}
|
|
1719
|
+
if (this.currentVariant_.video) {
|
|
1720
|
+
streamsByType.set(ContentType.VIDEO, this.currentVariant_.video);
|
|
1721
|
+
}
|
|
1634
1722
|
try {
|
|
1635
1723
|
mediaState.lastAppendWindowStart = appendWindowStart;
|
|
1636
1724
|
mediaState.lastAppendWindowEnd = appendWindowEnd;
|
|
1725
|
+
mediaState.lastCodecs = codecs;
|
|
1726
|
+
mediaState.lastMimeType = mimeType;
|
|
1637
1727
|
mediaState.lastTimestampOffset = timestampOffset;
|
|
1638
1728
|
|
|
1729
|
+
const ignoreTimestampOffset = this.manifest_.sequenceMode ||
|
|
1730
|
+
this.manifest_.type == shaka.media.ManifestParser.HLS;
|
|
1731
|
+
|
|
1639
1732
|
await this.playerInterface_.mediaSourceEngine.setStreamProperties(
|
|
1640
1733
|
mediaState.type, timestampOffset, appendWindowStart,
|
|
1641
|
-
appendWindowEnd,
|
|
1734
|
+
appendWindowEnd, ignoreTimestampOffset,
|
|
1735
|
+
mediaState.stream, streamsByType);
|
|
1642
1736
|
} catch (error) {
|
|
1643
1737
|
mediaState.lastAppendWindowStart = null;
|
|
1644
1738
|
mediaState.lastAppendWindowEnd = null;
|
|
1739
|
+
mediaState.lastCodecs = null;
|
|
1645
1740
|
mediaState.lastTimestampOffset = null;
|
|
1646
1741
|
|
|
1647
1742
|
throw error;
|
|
@@ -1658,22 +1753,52 @@ shaka.media.StreamingEngine = class {
|
|
|
1658
1753
|
shaka.log.v1(logPrefix, 'fetching init segment');
|
|
1659
1754
|
|
|
1660
1755
|
const fetchInit =
|
|
1661
|
-
this.fetch_(mediaState, reference.initSegmentReference
|
|
1756
|
+
this.fetch_(mediaState, reference.initSegmentReference,
|
|
1757
|
+
/* streamDataCallback= */ undefined, /* isInit= */ true);
|
|
1662
1758
|
const append = async () => {
|
|
1663
1759
|
try {
|
|
1664
1760
|
const initSegment = await fetchInit;
|
|
1665
1761
|
this.destroyer_.ensureNotDestroyed();
|
|
1666
1762
|
|
|
1763
|
+
let lastTimescale = null;
|
|
1764
|
+
const timescaleMap = new Map();
|
|
1765
|
+
|
|
1667
1766
|
const parser = new shaka.util.Mp4Parser();
|
|
1668
1767
|
const Mp4Parser = shaka.util.Mp4Parser;
|
|
1669
1768
|
parser.box('moov', Mp4Parser.children)
|
|
1670
1769
|
.box('trak', Mp4Parser.children)
|
|
1671
1770
|
.box('mdia', Mp4Parser.children)
|
|
1672
1771
|
.fullBox('mdhd', (box) => {
|
|
1673
|
-
|
|
1772
|
+
goog.asserts.assert(
|
|
1773
|
+
box.version != null,
|
|
1774
|
+
'MDHD is a full box and should have a valid version.');
|
|
1775
|
+
const parsedMDHDBox = shaka.util.Mp4BoxParsers.parseMDHD(
|
|
1776
|
+
box.reader, box.version);
|
|
1777
|
+
lastTimescale = parsedMDHDBox.timescale;
|
|
1778
|
+
})
|
|
1779
|
+
.box('hdlr', (box) => {
|
|
1780
|
+
const parsedHDLR = shaka.util.Mp4BoxParsers.parseHDLR(
|
|
1781
|
+
box.reader);
|
|
1782
|
+
switch (parsedHDLR.handlerType) {
|
|
1783
|
+
case 'soun':
|
|
1784
|
+
timescaleMap.set(ContentType.AUDIO, lastTimescale);
|
|
1785
|
+
break;
|
|
1786
|
+
case 'vide':
|
|
1787
|
+
timescaleMap.set(ContentType.VIDEO, lastTimescale);
|
|
1788
|
+
break;
|
|
1789
|
+
}
|
|
1790
|
+
lastTimescale = null;
|
|
1674
1791
|
})
|
|
1675
1792
|
.parse(initSegment);
|
|
1676
1793
|
|
|
1794
|
+
if (timescaleMap.has(mediaState.type)) {
|
|
1795
|
+
reference.initSegmentReference.timescale =
|
|
1796
|
+
timescaleMap.get(mediaState.type);
|
|
1797
|
+
} else if (lastTimescale != null) {
|
|
1798
|
+
// Fallback for segments without HDLR box
|
|
1799
|
+
reference.initSegmentReference.timescale = lastTimescale;
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1677
1802
|
shaka.log.v1(logPrefix, 'appending init segment');
|
|
1678
1803
|
const hasClosedCaptions = mediaState.stream.closedCaptions &&
|
|
1679
1804
|
mediaState.stream.closedCaptions.size > 0;
|
|
@@ -1681,7 +1806,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1681
1806
|
mediaState.type, initSegment);
|
|
1682
1807
|
await this.playerInterface_.mediaSourceEngine.appendBuffer(
|
|
1683
1808
|
mediaState.type, initSegment, /* reference= */ null,
|
|
1684
|
-
hasClosedCaptions);
|
|
1809
|
+
mediaState.stream, hasClosedCaptions);
|
|
1685
1810
|
} catch (error) {
|
|
1686
1811
|
mediaState.lastInitSegmentReference = null;
|
|
1687
1812
|
throw error;
|
|
@@ -1706,7 +1831,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1706
1831
|
operations.push(this.playerInterface_.mediaSourceEngine.resync(
|
|
1707
1832
|
mediaState.type, reference.startTime));
|
|
1708
1833
|
}
|
|
1709
|
-
} else {
|
|
1834
|
+
} else if (this.manifest_.ignoreManifestTimestampsInSegmentsMode) {
|
|
1710
1835
|
// In segments mode, we need to resync to set the timestampOffset
|
|
1711
1836
|
// to the start of the current discontinuity sequence. This is
|
|
1712
1837
|
// because individual discontinuity sequences may have internal
|
|
@@ -1789,6 +1914,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1789
1914
|
mediaState.type,
|
|
1790
1915
|
segment,
|
|
1791
1916
|
reference,
|
|
1917
|
+
stream,
|
|
1792
1918
|
hasClosedCaptions,
|
|
1793
1919
|
seeked,
|
|
1794
1920
|
adaptation,
|
|
@@ -1920,18 +2046,6 @@ shaka.media.StreamingEngine = class {
|
|
|
1920
2046
|
}
|
|
1921
2047
|
}
|
|
1922
2048
|
|
|
1923
|
-
/**
|
|
1924
|
-
* Parse MDHD box.
|
|
1925
|
-
* @param {!shaka.media.SegmentReference} reference
|
|
1926
|
-
* @param {!shaka.extern.ParsedBox} box
|
|
1927
|
-
* @private
|
|
1928
|
-
*/
|
|
1929
|
-
parseMDHD_(reference, box) {
|
|
1930
|
-
const parsedMDHDBox = shaka.util.Mp4BoxParsers.parseMDHD(
|
|
1931
|
-
box.reader || 0, box.version || 0);
|
|
1932
|
-
reference.initSegmentReference.timescale = parsedMDHDBox.timescale;
|
|
1933
|
-
}
|
|
1934
|
-
|
|
1935
2049
|
/**
|
|
1936
2050
|
* Parse PRFT box.
|
|
1937
2051
|
* @param {!shaka.media.SegmentReference} reference
|
|
@@ -1943,30 +2057,16 @@ shaka.media.StreamingEngine = class {
|
|
|
1943
2057
|
!reference.initSegmentReference.timescale) {
|
|
1944
2058
|
return;
|
|
1945
2059
|
}
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
const
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
let mediaTime;
|
|
1953
|
-
if (box.version === 0) {
|
|
1954
|
-
mediaTime = box.reader.readUint32();
|
|
1955
|
-
} else {
|
|
1956
|
-
try {
|
|
1957
|
-
mediaTime = box.reader.readUint64();
|
|
1958
|
-
} catch (e) {
|
|
1959
|
-
shaka.log.warning('parsePrft_: parsing mediatime resulted in a '+
|
|
1960
|
-
'MEDIA.JS_INTEGER_OVERFLOW exception');
|
|
1961
|
-
this.parsedPrftEventRaised_ = true;
|
|
1962
|
-
return;
|
|
1963
|
-
}
|
|
1964
|
-
}
|
|
2060
|
+
goog.asserts.assert(
|
|
2061
|
+
box.version == 0 || box.version == 1,
|
|
2062
|
+
'PRFT version can only be 0 or 1');
|
|
2063
|
+
const parsed = shaka.util.Mp4BoxParsers.parsePRFTInaccurate(
|
|
2064
|
+
box.reader, box.version);
|
|
1965
2065
|
|
|
1966
2066
|
const timescale = reference.initSegmentReference.timescale;
|
|
1967
|
-
const wallClockTime = this.convertNtp(ntpTimestamp);
|
|
2067
|
+
const wallClockTime = this.convertNtp(parsed.ntpTimestamp);
|
|
1968
2068
|
const programStartDate = new Date(wallClockTime -
|
|
1969
|
-
(mediaTime / timescale) * 1000);
|
|
2069
|
+
(parsed.mediaTime / timescale) * 1000);
|
|
1970
2070
|
const prftInfo = {
|
|
1971
2071
|
wallClockTime,
|
|
1972
2072
|
programStartDate,
|
|
@@ -1982,11 +2082,11 @@ shaka.media.StreamingEngine = class {
|
|
|
1982
2082
|
|
|
1983
2083
|
|
|
1984
2084
|
/**
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
2085
|
+
* Convert Ntp ntpTimeStamp to UTC Time
|
|
2086
|
+
*
|
|
2087
|
+
* @param {number} ntpTimeStamp
|
|
2088
|
+
* @return {number} utcTime
|
|
2089
|
+
*/
|
|
1990
2090
|
convertNtp(ntpTimeStamp) {
|
|
1991
2091
|
const start = new Date(Date.UTC(1900, 0, 1, 0, 0, 0));
|
|
1992
2092
|
return new Date(start.getTime() + ntpTimeStamp).getTime();
|
|
@@ -2070,14 +2170,58 @@ shaka.media.StreamingEngine = class {
|
|
|
2070
2170
|
* @param {(!shaka.media.InitSegmentReference|!shaka.media.SegmentReference)}
|
|
2071
2171
|
* reference
|
|
2072
2172
|
* @param {?function(BufferSource):!Promise=} streamDataCallback
|
|
2173
|
+
* @param {boolean=} isInit
|
|
2073
2174
|
*
|
|
2074
2175
|
* @return {!Promise.<BufferSource>}
|
|
2075
2176
|
* @private
|
|
2076
2177
|
* @suppress {strictMissingProperties}
|
|
2077
2178
|
*/
|
|
2078
|
-
async fetch_(mediaState, reference, streamDataCallback) {
|
|
2079
|
-
|
|
2179
|
+
async fetch_(mediaState, reference, streamDataCallback, isInit) {
|
|
2180
|
+
if (reference instanceof shaka.media.InitSegmentReference) {
|
|
2181
|
+
const segmentData = reference.getSegmentData();
|
|
2182
|
+
if (segmentData) {
|
|
2183
|
+
return segmentData;
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
let op = null;
|
|
2187
|
+
if (
|
|
2188
|
+
mediaState.segmentPrefetch &&
|
|
2189
|
+
reference instanceof shaka.media.SegmentReference
|
|
2190
|
+
) {
|
|
2191
|
+
op = mediaState.segmentPrefetch.getPrefetchedSegment(
|
|
2192
|
+
reference, streamDataCallback);
|
|
2193
|
+
}
|
|
2194
|
+
if (!op) {
|
|
2195
|
+
op = this.dispatchFetch_(
|
|
2196
|
+
reference, mediaState.stream, streamDataCallback, isInit,
|
|
2197
|
+
);
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
mediaState.operation = op;
|
|
2201
|
+
const response = await op.promise;
|
|
2202
|
+
mediaState.operation = null;
|
|
2203
|
+
return response.data;
|
|
2204
|
+
}
|
|
2080
2205
|
|
|
2206
|
+
/**
|
|
2207
|
+
* Fetches the given segment.
|
|
2208
|
+
*
|
|
2209
|
+
* @param {!shaka.extern.Stream} stream
|
|
2210
|
+
* @param {(!shaka.media.InitSegmentReference|!shaka.media.SegmentReference)}
|
|
2211
|
+
* reference
|
|
2212
|
+
* @param {?function(BufferSource):!Promise=} streamDataCallback
|
|
2213
|
+
* @param {boolean=} isInit
|
|
2214
|
+
*
|
|
2215
|
+
* @return {!shaka.net.NetworkingEngine.PendingRequest}
|
|
2216
|
+
* @private
|
|
2217
|
+
*/
|
|
2218
|
+
dispatchFetch_(reference, stream, streamDataCallback, isInit) {
|
|
2219
|
+
const requestType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
|
|
2220
|
+
const type = isInit ?
|
|
2221
|
+
shaka.net.NetworkingEngine.AdvancedRequestType.INIT_SEGMENT :
|
|
2222
|
+
shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_SEGMENT;
|
|
2223
|
+
const segment = reference instanceof shaka.media.SegmentReference ?
|
|
2224
|
+
reference : undefined;
|
|
2081
2225
|
const request = shaka.util.Networking.createSegmentRequest(
|
|
2082
2226
|
reference.getUris(),
|
|
2083
2227
|
reference.startByte,
|
|
@@ -2087,27 +2231,14 @@ shaka.media.StreamingEngine = class {
|
|
|
2087
2231
|
|
|
2088
2232
|
shaka.log.v2('fetching: reference=', reference);
|
|
2089
2233
|
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
duration: reference.endTime - reference.startTime,
|
|
2097
|
-
mimeType: stream.mimeType,
|
|
2098
|
-
codecs: stream.codecs,
|
|
2099
|
-
bandwidth: stream.bandwidth,
|
|
2100
|
-
},
|
|
2101
|
-
);
|
|
2102
|
-
|
|
2103
|
-
const op = this.playerInterface_.netEngine.request(requestType, request);
|
|
2104
|
-
mediaState.operation = op;
|
|
2105
|
-
const response = await op.promise;
|
|
2106
|
-
mediaState.operation = null;
|
|
2107
|
-
return response.data;
|
|
2234
|
+
return this.playerInterface_.netEngine.request(
|
|
2235
|
+
requestType, request, {
|
|
2236
|
+
type: type,
|
|
2237
|
+
stream: stream,
|
|
2238
|
+
segment: segment,
|
|
2239
|
+
});
|
|
2108
2240
|
}
|
|
2109
2241
|
|
|
2110
|
-
|
|
2111
2242
|
/**
|
|
2112
2243
|
* Clears the buffer and schedules another update.
|
|
2113
2244
|
* The optional parameter safeMargin allows to retain a certain amount
|
|
@@ -2135,6 +2266,9 @@ shaka.media.StreamingEngine = class {
|
|
|
2135
2266
|
mediaState.segmentIterator = null;
|
|
2136
2267
|
|
|
2137
2268
|
shaka.log.debug(logPrefix, 'clearing buffer');
|
|
2269
|
+
if (mediaState.segmentPrefetch) {
|
|
2270
|
+
mediaState.segmentPrefetch.clearAll();
|
|
2271
|
+
}
|
|
2138
2272
|
|
|
2139
2273
|
if (safeMargin) {
|
|
2140
2274
|
const presentationTime = this.playerInterface_.getPresentationTime();
|
|
@@ -2150,6 +2284,7 @@ shaka.media.StreamingEngine = class {
|
|
|
2150
2284
|
mediaState.type);
|
|
2151
2285
|
}
|
|
2152
2286
|
}
|
|
2287
|
+
|
|
2153
2288
|
this.destroyer_.ensureNotDestroyed();
|
|
2154
2289
|
|
|
2155
2290
|
shaka.log.debug(logPrefix, 'cleared buffer');
|
|
@@ -2230,17 +2365,31 @@ shaka.media.StreamingEngine = class {
|
|
|
2230
2365
|
* Handle streaming errors by delaying, then notifying the application by
|
|
2231
2366
|
* error callback and by streaming failure callback.
|
|
2232
2367
|
*
|
|
2368
|
+
* @param {shaka.media.StreamingEngine.MediaState_} mediaState
|
|
2233
2369
|
* @param {!shaka.util.Error} error
|
|
2234
2370
|
* @return {!Promise}
|
|
2235
2371
|
* @private
|
|
2236
2372
|
*/
|
|
2237
|
-
async handleStreamingError_(error) {
|
|
2373
|
+
async handleStreamingError_(mediaState, error) {
|
|
2238
2374
|
// If we invoke the callback right away, the application could trigger a
|
|
2239
2375
|
// rapid retry cycle that could be very unkind to the server. Instead,
|
|
2240
2376
|
// use the backoff system to delay and backoff the error handling.
|
|
2241
2377
|
await this.failureCallbackBackoff_.attempt();
|
|
2242
2378
|
this.destroyer_.ensureNotDestroyed();
|
|
2243
2379
|
|
|
2380
|
+
const maxDisabledTime = this.getDisabledTime_(error);
|
|
2381
|
+
// Try to recover from network errors
|
|
2382
|
+
if (error.category === shaka.util.Error.Category.NETWORK &&
|
|
2383
|
+
maxDisabledTime > 0) {
|
|
2384
|
+
error.handled = this.playerInterface_.disableStream(
|
|
2385
|
+
mediaState.stream, maxDisabledTime);
|
|
2386
|
+
|
|
2387
|
+
// Decrease the error severity to recoverable
|
|
2388
|
+
if (error.handled) {
|
|
2389
|
+
error.severity = shaka.util.Error.Severity.RECOVERABLE;
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2244
2393
|
// First fire an error event.
|
|
2245
2394
|
this.playerInterface_.onError(error);
|
|
2246
2395
|
|
|
@@ -2251,6 +2400,25 @@ shaka.media.StreamingEngine = class {
|
|
|
2251
2400
|
}
|
|
2252
2401
|
}
|
|
2253
2402
|
|
|
2403
|
+
/**
|
|
2404
|
+
* @param {!shaka.util.Error} error
|
|
2405
|
+
* @private
|
|
2406
|
+
*/
|
|
2407
|
+
getDisabledTime_(error) {
|
|
2408
|
+
if (this.config_.maxDisabledTime === 0 &&
|
|
2409
|
+
error.code == shaka.util.Error.Code.SEGMENT_MISSING) {
|
|
2410
|
+
// Spec: https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-6.3.3
|
|
2411
|
+
// The client SHOULD NOT attempt to load Media Segments that have been
|
|
2412
|
+
// marked with an EXT-X-GAP tag, or to load Partial Segments with a
|
|
2413
|
+
// GAP=YES attribute. Instead, clients are encouraged to look for
|
|
2414
|
+
// another Variant Stream of the same Rendition which does not have the
|
|
2415
|
+
// same gap, and play that instead.
|
|
2416
|
+
return 1;
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
return this.config_.maxDisabledTime;
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2254
2422
|
/**
|
|
2255
2423
|
* @param {shaka.media.StreamingEngine.MediaState_} mediaState
|
|
2256
2424
|
* @return {string} A log prefix of the form ($CONTENT_TYPE:$STREAM_ID), e.g.,
|
|
@@ -2267,8 +2435,6 @@ shaka.media.StreamingEngine = class {
|
|
|
2267
2435
|
* @typedef {{
|
|
2268
2436
|
* getPresentationTime: function():number,
|
|
2269
2437
|
* getBandwidthEstimate: function():number,
|
|
2270
|
-
* modifySegmentRequest: function(shaka.extern.Request,
|
|
2271
|
-
* shaka.util.CmcdManager.SegmentInfo),
|
|
2272
2438
|
* mediaSourceEngine: !shaka.media.MediaSourceEngine,
|
|
2273
2439
|
* netEngine: shaka.net.NetworkingEngine,
|
|
2274
2440
|
* onError: function(!shaka.util.Error),
|
|
@@ -2279,7 +2445,8 @@ shaka.media.StreamingEngine = class {
|
|
|
2279
2445
|
* onInitSegmentAppended: function(!number,!shaka.media.InitSegmentReference),
|
|
2280
2446
|
* beforeAppendSegment: function(
|
|
2281
2447
|
* shaka.util.ManifestParserUtils.ContentType,!BufferSource):Promise,
|
|
2282
|
-
* onMetadata: !function(!Array.<shaka.extern.ID3Metadata>, number, ?number)
|
|
2448
|
+
* onMetadata: !function(!Array.<shaka.extern.ID3Metadata>, number, ?number),
|
|
2449
|
+
* disableStream: function(!shaka.extern.Stream, number):boolean
|
|
2283
2450
|
* }}
|
|
2284
2451
|
*
|
|
2285
2452
|
* @property {function():number} getPresentationTime
|
|
@@ -2287,9 +2454,6 @@ shaka.media.StreamingEngine = class {
|
|
|
2287
2454
|
* viewer is seeing on screen right now.
|
|
2288
2455
|
* @property {function():number} getBandwidthEstimate
|
|
2289
2456
|
* Get the estimated bandwidth in bits per second.
|
|
2290
|
-
* @property {function(shaka.extern.Request,
|
|
2291
|
-
* shaka.extern.Cmcd.SegmentInfo)} modifySegmentRequest
|
|
2292
|
-
* The request modifier
|
|
2293
2457
|
* @property {!shaka.media.MediaSourceEngine} mediaSourceEngine
|
|
2294
2458
|
* The MediaSourceEngine. The caller retains ownership.
|
|
2295
2459
|
* @property {shaka.net.NetworkingEngine} netEngine
|
|
@@ -2315,6 +2479,9 @@ shaka.media.StreamingEngine = class {
|
|
|
2315
2479
|
* @property
|
|
2316
2480
|
* {!function(!Array.<shaka.extern.ID3Metadata>, number, ?number)} onMetadata
|
|
2317
2481
|
* Called when an ID3 is found in a EMSG.
|
|
2482
|
+
* @property {function(!shaka.extern.Stream, number):boolean} disableStream
|
|
2483
|
+
* Called to temporarily disable a stream i.e. disabling all variant
|
|
2484
|
+
* containing said stream.
|
|
2318
2485
|
*/
|
|
2319
2486
|
shaka.media.StreamingEngine.PlayerInterface;
|
|
2320
2487
|
|
|
@@ -2329,6 +2496,8 @@ shaka.media.StreamingEngine.PlayerInterface;
|
|
|
2329
2496
|
* lastTimestampOffset: ?number,
|
|
2330
2497
|
* lastAppendWindowStart: ?number,
|
|
2331
2498
|
* lastAppendWindowEnd: ?number,
|
|
2499
|
+
* lastCodecs: ?string,
|
|
2500
|
+
* lastMimeType: ?string,
|
|
2332
2501
|
* restoreStreamAfterTrickPlay: ?shaka.extern.Stream,
|
|
2333
2502
|
* endOfStream: boolean,
|
|
2334
2503
|
* performingUpdate: boolean,
|
|
@@ -2342,7 +2511,8 @@ shaka.media.StreamingEngine.PlayerInterface;
|
|
|
2342
2511
|
* adaptation: boolean,
|
|
2343
2512
|
* recovering: boolean,
|
|
2344
2513
|
* hasError: boolean,
|
|
2345
|
-
* operation: shaka.net.NetworkingEngine.PendingRequest
|
|
2514
|
+
* operation: shaka.net.NetworkingEngine.PendingRequest,
|
|
2515
|
+
* segmentPrefetch: shaka.media.SegmentPrefetch
|
|
2346
2516
|
* }}
|
|
2347
2517
|
*
|
|
2348
2518
|
* @description
|
|
@@ -2366,6 +2536,10 @@ shaka.media.StreamingEngine.PlayerInterface;
|
|
|
2366
2536
|
* The last append window start given to MediaSourceEngine for this type.
|
|
2367
2537
|
* @property {?number} lastAppendWindowEnd
|
|
2368
2538
|
* The last append window end given to MediaSourceEngine for this type.
|
|
2539
|
+
* @property {?string} lastCodecs
|
|
2540
|
+
* The last append codecs given to MediaSourceEngine for this type.
|
|
2541
|
+
* @property {?string} lastMimeType
|
|
2542
|
+
* The last append mime type given to MediaSourceEngine for this type.
|
|
2369
2543
|
* @property {?shaka.extern.Stream} restoreStreamAfterTrickPlay
|
|
2370
2544
|
* The Stream to restore after trick play mode is turned off.
|
|
2371
2545
|
* @property {boolean} endOfStream
|
|
@@ -2399,6 +2573,9 @@ shaka.media.StreamingEngine.PlayerInterface;
|
|
|
2399
2573
|
* updating.
|
|
2400
2574
|
* @property {shaka.net.NetworkingEngine.PendingRequest} operation
|
|
2401
2575
|
* Operation with the number of bytes to be downloaded.
|
|
2576
|
+
* @property {?shaka.media.SegmentPrefetch} segmentPrefetch
|
|
2577
|
+
* A prefetch object for managing prefetching. Null if unneeded
|
|
2578
|
+
* (if prefetching is disabled, etc).
|
|
2402
2579
|
*/
|
|
2403
2580
|
shaka.media.StreamingEngine.MediaState_;
|
|
2404
2581
|
|
|
@@ -28,9 +28,9 @@ shaka.media.VideoWrapper = class {
|
|
|
28
28
|
* @param {!HTMLMediaElement} video
|
|
29
29
|
* @param {function()} onSeek Called when the video seeks.
|
|
30
30
|
* @param {function(number)} onStarted Called when the video has started.
|
|
31
|
-
* @param {number}
|
|
31
|
+
* @param {function():number} getStartTime Calle to get the time to start at.
|
|
32
32
|
*/
|
|
33
|
-
constructor(video, onSeek, onStarted,
|
|
33
|
+
constructor(video, onSeek, onStarted, getStartTime) {
|
|
34
34
|
/** @private {HTMLMediaElement} */
|
|
35
35
|
this.video_ = video;
|
|
36
36
|
|
|
@@ -40,8 +40,16 @@ shaka.media.VideoWrapper = class {
|
|
|
40
40
|
/** @private {function(number)} */
|
|
41
41
|
this.onStarted_ = onStarted;
|
|
42
42
|
|
|
43
|
-
/** @private {number} */
|
|
44
|
-
this.startTime_ =
|
|
43
|
+
/** @private {?number} */
|
|
44
|
+
this.startTime_ = null;
|
|
45
|
+
|
|
46
|
+
/** @private {function():number} */
|
|
47
|
+
this.getStartTime_ = () => {
|
|
48
|
+
if (this.startTime_ == null) {
|
|
49
|
+
this.startTime_ = getStartTime();
|
|
50
|
+
}
|
|
51
|
+
return this.startTime_;
|
|
52
|
+
};
|
|
45
53
|
|
|
46
54
|
/** @private {boolean} */
|
|
47
55
|
this.started_ = false;
|
|
@@ -62,7 +70,7 @@ shaka.media.VideoWrapper = class {
|
|
|
62
70
|
HTMLMediaElement.HAVE_METADATA,
|
|
63
71
|
this.eventManager_,
|
|
64
72
|
() => {
|
|
65
|
-
this.setStartTime_(this.
|
|
73
|
+
this.setStartTime_(this.getStartTime_());
|
|
66
74
|
});
|
|
67
75
|
}
|
|
68
76
|
|
|
@@ -90,7 +98,7 @@ shaka.media.VideoWrapper = class {
|
|
|
90
98
|
* @return {number}
|
|
91
99
|
*/
|
|
92
100
|
getTime() {
|
|
93
|
-
return this.started_ ? this.video_.currentTime : this.
|
|
101
|
+
return this.started_ ? this.video_.currentTime : this.getStartTime_();
|
|
94
102
|
}
|
|
95
103
|
|
|
96
104
|
|
|
@@ -107,7 +115,7 @@ shaka.media.VideoWrapper = class {
|
|
|
107
115
|
HTMLMediaElement.HAVE_METADATA,
|
|
108
116
|
this.eventManager_,
|
|
109
117
|
() => {
|
|
110
|
-
this.setStartTime_(this.
|
|
118
|
+
this.setStartTime_(this.getStartTime_());
|
|
111
119
|
});
|
|
112
120
|
}
|
|
113
121
|
}
|