shaka-player 4.6.4 → 4.7.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/README.md +1 -0
- package/dist/controls.css +1 -1
- package/dist/controls.css.map +1 -1
- package/dist/deps.js +9 -5
- package/dist/locales.js +29 -29
- package/dist/shaka-player.compiled.d.ts +46 -3
- package/dist/shaka-player.compiled.debug.d.ts +46 -3
- package/dist/shaka-player.compiled.debug.externs.js +45 -0
- package/dist/shaka-player.compiled.debug.js +382 -351
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +45 -0
- package/dist/shaka-player.compiled.js +1135 -1121
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +68 -4
- package/dist/shaka-player.ui.debug.d.ts +68 -4
- package/dist/shaka-player.ui.debug.externs.js +77 -0
- package/dist/shaka-player.ui.debug.js +428 -392
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +77 -0
- package/dist/shaka-player.ui.js +1304 -1290
- package/dist/shaka-player.ui.map +4 -4
- package/externs/mediasession.js +7 -0
- package/externs/shaka/abr_manager.js +8 -0
- package/externs/shaka/manifest.js +3 -3
- package/externs/shaka/player.js +59 -4
- package/lib/abr/simple_abr_manager.js +41 -15
- package/lib/cea/cea708_service.js +1 -1
- package/lib/cea/cea708_window.js +95 -7
- package/lib/dash/dash_parser.js +55 -11
- package/lib/media/adaptation_set_criteria.js +76 -10
- package/lib/media/media_source_engine.js +42 -18
- package/lib/media/segment_reference.js +12 -0
- package/lib/media/streaming_engine.js +55 -3
- package/lib/player.js +92 -6
- package/lib/util/cmsd_manager.js +269 -0
- package/lib/util/content_steering_manager.js +5 -1
- package/lib/util/mime_utils.js +6 -2
- package/lib/util/periods.js +108 -39
- package/lib/util/player_configuration.js +19 -5
- package/lib/util/stream_utils.js +82 -13
- package/package.json +1 -1
- package/ui/audio_language_selection.js +3 -0
- package/ui/controls.js +60 -0
- package/ui/externs/ui.js +12 -1
- package/ui/hidden_fast_forward_button.js +32 -0
- package/ui/hidden_rewind_button.js +32 -0
- package/ui/hidden_seek_button.js +121 -0
- package/ui/less/buttons.less +50 -0
- package/ui/less/containers.less +18 -0
- package/ui/resolution_selection.js +2 -0
- package/ui/ui.js +2 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -75
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -26
- package/.github/ISSUE_TEMPLATE/question.md +0 -26
- package/.github/config.yml +0 -1
- package/.github/workflows/appspot.yaml +0 -39
- package/.github/workflows/build-and-test.yaml +0 -222
- package/.github/workflows/compute-incremental-coverage.py +0 -265
- package/.github/workflows/custom-actions/prep-for-appspot/action.yaml +0 -56
- package/.github/workflows/custom-actions/set-commit-status/action.yaml +0 -37
- package/.github/workflows/deflake.yaml +0 -31
- package/.github/workflows/demo-version-index.yaml +0 -43
- package/.github/workflows/nightly-demo.yaml +0 -42
- package/.github/workflows/release-please.yaml +0 -193
- package/.github/workflows/report-incremental-coverage.yaml +0 -39
- package/.github/workflows/safari-homedir-launcher.sh +0 -18
- package/.github/workflows/selenium-lab-tests.yaml +0 -370
- package/.github/workflows/shaka-bot-commands/command-help.sh +0 -27
- package/.github/workflows/shaka-bot-commands/command-test.sh +0 -56
- package/.github/workflows/shaka-bot-commands/lib.sh +0 -83
- package/.github/workflows/shaka-bot-commands/main.sh +0 -51
- package/.github/workflows/sync-labels.yaml +0 -33
- package/.github/workflows/talk-to-shaka-bot.yaml +0 -29
- package/.github/workflows/update-issues.yaml +0 -31
- package/.github/workflows/update-screenshots.yaml +0 -142
- package/.github/workflows/validate-pr-title.yaml +0 -27
- package/CHANGELOG.md +0 -5774
- package/CODE_OF_CONDUCT.md +0 -93
- package/CONTRIBUTING.md +0 -76
- package/demo/TengwarTelcontar.woff2 +0 -0
- package/demo/app_logo_144.png +0 -0
- package/demo/app_logo_192.png +0 -0
- package/demo/app_logo_256.png +0 -0
- package/demo/app_logo_384.png +0 -0
- package/demo/app_logo_512.png +0 -0
- package/demo/app_manifest.json +0 -29
- package/demo/asset_card.js +0 -410
- package/demo/autoTemplate.txt +0 -61
- package/demo/cast_receiver/idle1.jpg +0 -0
- package/demo/cast_receiver/idle2.jpg +0 -0
- package/demo/cast_receiver/idle3.jpg +0 -0
- package/demo/cast_receiver/index.html +0 -75
- package/demo/cast_receiver/receiver_app.css +0 -111
- package/demo/cast_receiver/receiver_app.js +0 -158
- package/demo/close_button.js +0 -60
- package/demo/common/asset.js +0 -510
- package/demo/common/assets.js +0 -1634
- package/demo/config.js +0 -859
- package/demo/custom.js +0 -907
- package/demo/customWarning.txt +0 -8
- package/demo/demo.less +0 -591
- package/demo/demo_uncompiled.js +0 -11
- package/demo/demo_utils.js +0 -113
- package/demo/favicon.ico +0 -0
- package/demo/front.js +0 -156
- package/demo/icons/README.md +0 -4
- package/demo/icons/baseline-audiotrack-24px.svg +0 -1
- package/demo/icons/baseline-closed_caption-24px.svg +0 -1
- package/demo/icons/baseline-fast_forward-24px.svg +0 -1
- package/demo/icons/baseline-language-24px.svg +0 -1
- package/demo/icons/baseline-live_tv-24px.svg +0 -1
- package/demo/icons/baseline-subtitles-24px.svg +0 -1
- package/demo/icons/baseline-surround_sound-24px.svg +0 -1
- package/demo/icons/custom_ad.svg +0 -1
- package/demo/icons/custom_box_with_text.svg +0 -1
- package/demo/icons/custom_clear_key.svg +0 -1
- package/demo/icons/custom_high_definition.svg +0 -1
- package/demo/icons/custom_playready.svg +0 -1
- package/demo/icons/custom_ultra_high_definition.svg +0 -1
- package/demo/icons/custom_widevine.svg +0 -1
- package/demo/index.html +0 -173
- package/demo/input.js +0 -270
- package/demo/input_container.js +0 -218
- package/demo/load.js +0 -156
- package/demo/main.js +0 -1854
- package/demo/search.js +0 -452
- package/demo/service_worker.js +0 -355
- package/demo/shaka_logo_trans.png +0 -0
- package/demo/tooltip.js +0 -29
- package/demo/visualizer.js +0 -335
- package/demo/visualizer_button.js +0 -84
- package/dist/demo.compiled.debug.js +0 -340
- package/dist/demo.compiled.debug.map +0 -8
- package/dist/demo.compiled.js +0 -340
- package/dist/demo.compiled.map +0 -8
- package/dist/demo.css +0 -6
- package/dist/demo.css.map +0 -1
- package/dist/receiver.compiled.debug.js +0 -187
- package/dist/receiver.compiled.debug.map +0 -8
- package/dist/receiver.compiled.js +0 -187
- package/dist/receiver.compiled.map +0 -8
- package/docs/announcement-list-join-group.png +0 -0
- package/docs/api-mainpage.md +0 -28
- package/docs/design/REDESIGN.md +0 -283
- package/docs/design/architecture.md +0 -13
- package/docs/design/bg-fetch-after.gv +0 -29
- package/docs/design/bg-fetch-after.gv.png +0 -0
- package/docs/design/bg-fetch-before.gv +0 -18
- package/docs/design/bg-fetch-before.gv.png +0 -0
- package/docs/design/bg-fetch.md +0 -134
- package/docs/design/cast.gv +0 -35
- package/docs/design/cast.gv.png +0 -0
- package/docs/design/chromecast.md +0 -151
- package/docs/design/codec_preferences.md +0 -158
- package/docs/design/dash-audio-channels.md +0 -123
- package/docs/design/dash-manifests.md +0 -248
- package/docs/design/dash-timeline.svg +0 -4
- package/docs/design/dataflow.gv +0 -66
- package/docs/design/dataflow.gv.png +0 -0
- package/docs/design/export.md +0 -70
- package/docs/design/fuzzy-locale-matching.md +0 -40
- package/docs/design/gap-jumping.md +0 -151
- package/docs/design/lcevc-architecture.png +0 -0
- package/docs/design/lcevc-demo.png +0 -0
- package/docs/design/lcevc-integration.md +0 -72
- package/docs/design/locale_compatibility.svg +0 -1
- package/docs/design/locales_tree.svg +0 -1
- package/docs/design/localization-design-principles.md +0 -158
- package/docs/design/newdemo.gv +0 -83
- package/docs/design/newdemo.gv.png +0 -0
- package/docs/design/offline.gv +0 -43
- package/docs/design/offline.gv.png +0 -0
- package/docs/design/offline.md +0 -153
- package/docs/design/ownership.gv +0 -76
- package/docs/design/ownership.gv.png +0 -0
- package/docs/design/preload.md +0 -299
- package/docs/design/preload.png +0 -0
- package/docs/design/talking-about-languages.md +0 -46
- package/docs/design/timeline.svg +0 -152
- package/docs/design/ui.md +0 -31
- package/docs/design/ui_player_relationships.svg +0 -1
- package/docs/jsdoc-plugin.js +0 -74
- package/docs/jsdoc-template/README.md +0 -1
- package/docs/jsdoc-template/publish.js +0 -879
- package/docs/jsdoc-template/static/fonts/OpenSans-Bold-webfont.eot +0 -0
- package/docs/jsdoc-template/static/fonts/OpenSans-Bold-webfont.svg +0 -1830
- package/docs/jsdoc-template/static/fonts/OpenSans-Bold-webfont.woff +0 -0
- package/docs/jsdoc-template/static/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
- package/docs/jsdoc-template/static/fonts/OpenSans-BoldItalic-webfont.svg +0 -1830
- package/docs/jsdoc-template/static/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
- package/docs/jsdoc-template/static/fonts/OpenSans-Italic-webfont.eot +0 -0
- package/docs/jsdoc-template/static/fonts/OpenSans-Italic-webfont.svg +0 -1830
- package/docs/jsdoc-template/static/fonts/OpenSans-Italic-webfont.woff +0 -0
- package/docs/jsdoc-template/static/fonts/OpenSans-Light-webfont.eot +0 -0
- package/docs/jsdoc-template/static/fonts/OpenSans-Light-webfont.svg +0 -1831
- package/docs/jsdoc-template/static/fonts/OpenSans-Light-webfont.woff +0 -0
- package/docs/jsdoc-template/static/fonts/OpenSans-LightItalic-webfont.eot +0 -0
- package/docs/jsdoc-template/static/fonts/OpenSans-LightItalic-webfont.svg +0 -1835
- package/docs/jsdoc-template/static/fonts/OpenSans-LightItalic-webfont.woff +0 -0
- package/docs/jsdoc-template/static/fonts/OpenSans-Regular-webfont.eot +0 -0
- package/docs/jsdoc-template/static/fonts/OpenSans-Regular-webfont.svg +0 -1831
- package/docs/jsdoc-template/static/fonts/OpenSans-Regular-webfont.woff +0 -0
- package/docs/jsdoc-template/static/scripts/linenumber.js +0 -44
- package/docs/jsdoc-template/static/scripts/show-widget.js +0 -55
- package/docs/jsdoc-template/static/styles/jsdoc-default.css +0 -438
- package/docs/jsdoc-template/tmpl/augments.tmpl +0 -10
- package/docs/jsdoc-template/tmpl/container.tmpl +0 -205
- package/docs/jsdoc-template/tmpl/details.tmpl +0 -152
- package/docs/jsdoc-template/tmpl/example.tmpl +0 -2
- package/docs/jsdoc-template/tmpl/examples.tmpl +0 -13
- package/docs/jsdoc-template/tmpl/exceptions.tmpl +0 -32
- package/docs/jsdoc-template/tmpl/layout.tmpl +0 -50
- package/docs/jsdoc-template/tmpl/mainpage.tmpl +0 -14
- package/docs/jsdoc-template/tmpl/members.tmpl +0 -47
- package/docs/jsdoc-template/tmpl/method.tmpl +0 -140
- package/docs/jsdoc-template/tmpl/modifies.tmpl +0 -14
- package/docs/jsdoc-template/tmpl/params.tmpl +0 -131
- package/docs/jsdoc-template/tmpl/properties.tmpl +0 -125
- package/docs/jsdoc-template/tmpl/returns.tmpl +0 -19
- package/docs/jsdoc-template/tmpl/source.tmpl +0 -8
- package/docs/jsdoc-template/tmpl/tutorial.tmpl +0 -21
- package/docs/jsdoc-template/tmpl/type.tmpl +0 -7
- package/docs/jsdoc.conf.json +0 -38
- package/docs/shaka-player-logo.png +0 -0
- package/docs/style.md +0 -174
- package/docs/tutorials/a11y.md +0 -64
- package/docs/tutorials/ad_monetization.md +0 -292
- package/docs/tutorials/application-level-redirects.md +0 -94
- package/docs/tutorials/architecture.md +0 -32
- package/docs/tutorials/basic-usage.md +0 -89
- package/docs/tutorials/blob-url.md +0 -15
- package/docs/tutorials/config.md +0 -148
- package/docs/tutorials/debugging.md +0 -189
- package/docs/tutorials/drm-config.md +0 -274
- package/docs/tutorials/errors.md +0 -95
- package/docs/tutorials/fairplay.md +0 -242
- package/docs/tutorials/faq.md +0 -252
- package/docs/tutorials/index.json +0 -29
- package/docs/tutorials/lcevc.md +0 -118
- package/docs/tutorials/license-server-auth.md +0 -248
- package/docs/tutorials/license-wrapping.md +0 -168
- package/docs/tutorials/manifest-parser.md +0 -365
- package/docs/tutorials/network-and-buffering-config.md +0 -119
- package/docs/tutorials/offline.md +0 -683
- package/docs/tutorials/plugins.md +0 -184
- package/docs/tutorials/selenium-grid-config.md +0 -174
- package/docs/tutorials/service-worker.md +0 -106
- package/docs/tutorials/ui-customization.md +0 -288
- package/docs/tutorials/ui.md +0 -232
- package/docs/tutorials/upgrade-manifest.md +0 -281
- package/docs/tutorials/upgrade.md +0 -104
- package/docs/tutorials/welcome.md +0 -108
- package/docs/tutorials/widevine-service-certs.md +0 -45
- package/karma.conf.js +0 -879
- package/maintained-branches.md +0 -15
- package/roadmap.md +0 -186
- package/test/abr/simple_abr_manager_unit.js +0 -398
- package/test/ads/ad_manager_unit.js +0 -260
- package/test/assumptions/uint8_array_unit.js +0 -22
- package/test/cast/cast_proxy_unit.js +0 -736
- package/test/cast/cast_receiver_integration.js +0 -488
- package/test/cast/cast_receiver_unit.js +0 -1183
- package/test/cast/cast_sender_unit.js +0 -973
- package/test/cast/cast_utils_unit.js +0 -311
- package/test/cea/cea608_memory_unit.js +0 -411
- package/test/cea/cea708_service_unit.js +0 -674
- package/test/cea/cea708_window_unit.js +0 -373
- package/test/cea/cea_decoder_unit.js +0 -615
- package/test/cea/dtvcc_packet_builder_unit.js +0 -158
- package/test/cea/dtvcc_packet_unit.js +0 -60
- package/test/cea/mp4_cea_parser_unit.js +0 -127
- package/test/codec_switching/codec_switching_integration.js +0 -182
- package/test/dash/dash_parser_content_protection_unit.js +0 -998
- package/test/dash/dash_parser_integration.js +0 -74
- package/test/dash/dash_parser_live_unit.js +0 -1542
- package/test/dash/dash_parser_manifest_unit.js +0 -3121
- package/test/dash/dash_parser_segment_base_unit.js +0 -421
- package/test/dash/dash_parser_segment_list_unit.js +0 -380
- package/test/dash/dash_parser_segment_template_unit.js +0 -910
- package/test/dash/mpd_utils_unit.js +0 -794
- package/test/demo/demo_unit.js +0 -108
- package/test/deprecate/enforcer_unit.js +0 -74
- package/test/deprecate/version_unit.js +0 -57
- package/test/hls/hls_live_unit.js +0 -1330
- package/test/hls/hls_parser_unit.js +0 -5276
- package/test/hls/manifest_text_parser_unit.js +0 -545
- package/test/media/adaptation_set_criteria_unit.js +0 -806
- package/test/media/adaptation_set_unit.js +0 -229
- package/test/media/buffering_observer_unit.js +0 -143
- package/test/media/closed_caption_parser_unit.js +0 -18
- package/test/media/content_workarounds_unit.js +0 -43
- package/test/media/drm_engine_integration.js +0 -356
- package/test/media/drm_engine_unit.js +0 -2805
- package/test/media/media_source_engine_integration.js +0 -691
- package/test/media/media_source_engine_unit.js +0 -1512
- package/test/media/mp4_segment_index_parser_unit.js +0 -80
- package/test/media/play_rate_controller_unit.js +0 -69
- package/test/media/playhead_unit.js +0 -1394
- package/test/media/presentation_timeline_unit.js +0 -477
- package/test/media/quality_observer_unit.js +0 -138
- package/test/media/region_observer_unit.js +0 -374
- package/test/media/region_timeline_unit.js +0 -139
- package/test/media/segment_index_unit.js +0 -1080
- package/test/media/segment_prefetch_unit.js +0 -243
- package/test/media/segment_reference_unit.js +0 -63
- package/test/media/stall_detector_unit.js +0 -184
- package/test/media/streaming_engine_integration.js +0 -632
- package/test/media/streaming_engine_unit.js +0 -4211
- package/test/media/time_ranges_utils_unit.js +0 -123
- package/test/media/webm_segment_index_parser_unit.js +0 -90
- package/test/mss/mss_parser_content_protection_unit.js +0 -49
- package/test/mss/mss_parser_unit.js +0 -474
- package/test/mss/mss_player_integration.js +0 -124
- package/test/net/data_uri_plugin_unit.js +0 -105
- package/test/net/http_plugin_unit.js +0 -439
- package/test/net/networking_engine_unit.js +0 -1236
- package/test/net/networking_utils_unit.js +0 -37
- package/test/offline/download_progress_estimator_unit.js +0 -90
- package/test/offline/indexeddb_storage_unit.js +0 -192
- package/test/offline/manifest_convert_unit.js +0 -622
- package/test/offline/offline_integration.js +0 -168
- package/test/offline/offline_manifest_parser_unit.js +0 -231
- package/test/offline/offline_scheme_unit.js +0 -154
- package/test/offline/offline_uri_unit.js +0 -59
- package/test/offline/storage_compatibility_unit.js +0 -344
- package/test/offline/storage_integration.js +0 -1858
- package/test/player_external.js +0 -237
- package/test/player_integration.js +0 -1371
- package/test/player_load_graph_integration.js +0 -865
- package/test/player_src_equals_external.js +0 -154
- package/test/player_src_equals_integration.js +0 -527
- package/test/player_unit.js +0 -4434
- package/test/polyfill/media_capabilities_unit.js +0 -277
- package/test/polyfill/patchedmediakeys_apple_unit.js +0 -130
- package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
- package/test/test/assets/3675/dash_0.mpd +0 -30
- package/test/test/assets/3675/dash_1.mpd +0 -30
- package/test/test/assets/3675/dash_2.mpd +0 -30
- package/test/test/assets/3675/dash_3.mpd +0 -30
- package/test/test/assets/3675/dash_4.mpd +0 -30
- package/test/test/assets/3675/dash_5.mpd +0 -26
- package/test/test/assets/3675/init-stream0.m4s +0 -0
- package/test/test/assets/audio-xhe-aac.mp4 +0 -0
- package/test/test/assets/audio.ts +0 -0
- package/test/test/assets/captions-test.ts +0 -0
- package/test/test/assets/cea-init.mp4 +0 -0
- package/test/test/assets/cea-segment.mp4 +0 -0
- package/test/test/assets/chapters.srt +0 -11
- package/test/test/assets/chapters.vtt +0 -10
- package/test/test/assets/chapters2.vtt +0 -10
- package/test/test/assets/dash-aes-128/Fragments(video=0,format=mpd-time-csf) +0 -0
- package/test/test/assets/dash-aes-128/Fragments(video=20000000,format=mpd-time-csf) +0 -0
- package/test/test/assets/dash-aes-128/Fragments(video=40000000,format=mpd-time-csf) +0 -0
- package/test/test/assets/dash-aes-128/Fragments(video=60000000,format=mpd-time-csf) +0 -0
- package/test/test/assets/dash-aes-128/Fragments(video=i,format=mpd-time-csf) +0 -0
- package/test/test/assets/dash-aes-128/dash.mpd +0 -18
- package/test/test/assets/dash-aes-128/license +0 -1
- package/test/test/assets/dash-multi-codec/audio_en_2c_128k_aac.mp4 +0 -0
- package/test/test/assets/dash-multi-codec/audio_es_2c_64k_opus.webm +0 -0
- package/test/test/assets/dash-multi-codec/dash.mpd +0 -32
- package/test/test/assets/dash-multi-codec/video_144p_108k_h264.mp4 +0 -0
- package/test/test/assets/db-dump-v1.json +0 -1
- package/test/test/assets/db-dump-v2-broken.json +0 -1
- package/test/test/assets/db-dump-v2-clean.json +0 -1
- package/test/test/assets/db-dump-v3.json +0 -1
- package/test/test/assets/db-dump-v4-broken.json +0 -1
- package/test/test/assets/db-dump-v5.json +0 -1
- package/test/test/assets/empty_caption_video_init.mp4 +0 -0
- package/test/test/assets/empty_caption_video_segment.mp4 +0 -0
- package/test/test/assets/encrypted-sintel-audio-init.mp4 +0 -0
- package/test/test/assets/encrypted-sintel-audio-segment.mp4 +0 -0
- package/test/test/assets/encrypted-sintel-video-init.mp4 +0 -0
- package/test/test/assets/encrypted-sintel-video-segment.mp4 +0 -0
- package/test/test/assets/green-pixel.mp4 +0 -0
- package/test/test/assets/green-pixel.txt +0 -15
- package/test/test/assets/h265-cea-init.mp4 +0 -0
- package/test/test/assets/h265-cea-segment.mp4 +0 -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 +0 -4
- package/test/test/assets/hls-raw-aac/stream.m4a.m3u8 +0 -14
- 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 +0 -31
- 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 +0 -31
- package/test/test/assets/hls-raw-mp3/chunklist_w905519204.m3u8 +0 -13
- 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 +0 -4
- package/test/test/assets/hls-ts-aac/playlist.m3u8 +0 -23
- 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 +0 -19
- 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 +0 -19
- 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 +0 -25
- package/test/test/assets/hls-ts-h265/0.ts +0 -0
- package/test/test/assets/hls-ts-h265/hevc.m3u8 +0 -6
- package/test/test/assets/hls-ts-h265/index.m3u8 +0 -7
- package/test/test/assets/hls-ts-mp3/manifest.m3u8 +0 -13
- 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 +0 -17
- 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 +0 -4
- package/test/test/assets/hls-ts-muxed-aac-h265/0.ts +0 -0
- package/test/test/assets/hls-ts-muxed-aac-h265/1.ts +0 -0
- package/test/test/assets/hls-ts-muxed-aac-h265/media.m3u8 +0 -9
- package/test/test/assets/hls-ts-muxed-ac3-h264/media.m3u8 +0 -24
- package/test/test/assets/hls-ts-muxed-ac3-h264/s_00000.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ac3-h264/s_00001.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ac3-h264/s_00002.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ac3-h264/s_00003.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ac3-h264/s_00004.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ac3-h264/s_00005.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0000.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0001.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0002.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ec3-h264/ChID_voices_1280x720p_25fps_h264_6ch_256kbps_ddp_sub_0003.ts +0 -0
- package/test/test/assets/hls-ts-muxed-ec3-h264/prog_index.m3u8 +0 -14
- package/test/test/assets/hls-ts-muxed-mp3-h264/index.m3u8 +0 -23
- package/test/test/assets/hls-ts-muxed-mp3-h264/seg-1-v1-a1.ts +0 -0
- package/test/test/assets/hls-ts-muxed-mp3-h264/seg-2-v1-a1.ts +0 -0
- package/test/test/assets/hls-ts-muxed-mp3-h264/seg-3-v1-a1.ts +0 -0
- package/test/test/assets/hls-ts-muxed-mp3-h264/seg-4-v1-a1.ts +0 -0
- package/test/test/assets/hls-ts-muxed-mp3-h264/seg-5-v1-a1.ts +0 -0
- package/test/test/assets/hls-ts-muxed-mp3-h264/seg-6-v1-a1.ts +0 -0
- package/test/test/assets/hls-ts-muxed-mp3-h264/seg-7-v1-a1.ts +0 -0
- package/test/test/assets/hls-ts-muxed-mp3-h264/seg-8-v1-a1.ts +0 -0
- package/test/test/assets/id3-metadata.aac +0 -0
- package/test/test/assets/id3-metadata.ts +0 -0
- package/test/test/assets/index-segment.mp4 +0 -0
- package/test/test/assets/index-segment.webm +0 -0
- package/test/test/assets/init-segment.webm +0 -0
- package/test/test/assets/large_file.mpd +0 -4930
- package/test/test/assets/multidrm-audio-init.mp4 +0 -0
- package/test/test/assets/multidrm-audio-segment.mp4 +0 -0
- package/test/test/assets/multidrm-video-init.mp4 +0 -0
- package/test/test/assets/multidrm-video-segment.mp4 +0 -0
- package/test/test/assets/multiple-trun-init.mp4 +0 -0
- package/test/test/assets/multiple-trun-segment.mp4 +0 -0
- package/test/test/assets/sintel-audio-init.mp4 +0 -0
- package/test/test/assets/sintel-audio-segment.mp4 +0 -0
- package/test/test/assets/sintel-video-init.mp4 +0 -0
- package/test/test/assets/sintel-video-segment.mp4 +0 -0
- package/test/test/assets/small.mp4 +0 -0
- package/test/test/assets/text-clip-alt.vtt +0 -63
- package/test/test/assets/text-clip.vtt +0 -63
- package/test/test/assets/thumbnails-sprites.vtt +0 -10
- package/test/test/assets/thumbnails.vtt +0 -10
- package/test/test/assets/ttml-init.mp4 +0 -0
- package/test/test/assets/ttml-segment-multiplemdat.mp4 +0 -0
- package/test/test/assets/ttml-segment.mp4 +0 -0
- package/test/test/assets/video.ts +0 -0
- package/test/test/assets/vtt-init.mp4 +0 -0
- package/test/test/assets/vtt-segment-multi-payload.mp4 +0 -0
- package/test/test/assets/vtt-segment-no-duration.mp4 +0 -0
- package/test/test/assets/vtt-segment-settings.mp4 +0 -0
- package/test/test/assets/vtt-segment.mp4 +0 -0
- package/test/test/boot.js +0 -495
- package/test/test/externs/filters.js +0 -44
- package/test/test/externs/fontfaceonload.js +0 -29
- package/test/test/externs/jasmine.js +0 -670
- package/test/test/externs/karma.js +0 -26
- package/test/test/externs/require.js +0 -21
- package/test/test/externs/sprintf.js +0 -18
- package/test/test/namespace.js +0 -10
- package/test/test/util/canned_idb.js +0 -379
- package/test/test/util/cea_utils.js +0 -60
- package/test/test/util/dash_parser_util.js +0 -329
- package/test/test/util/fake_ad.js +0 -321
- package/test/test/util/fake_ad_manager.js +0 -122
- package/test/test/util/fake_demo_main.js +0 -144
- package/test/test/util/fake_drm_engine.js +0 -103
- package/test/test/util/fake_media_source_engine.js +0 -464
- package/test/test/util/fake_networking_engine.js +0 -381
- package/test/test/util/fake_segment_prefetch.js +0 -84
- package/test/test/util/fake_text_displayer.js +0 -60
- package/test/test/util/id3_generator.js +0 -100
- package/test/test/util/indexeddb_utils.js +0 -124
- package/test/test/util/jasmine_fetch.js +0 -382
- package/test/test/util/layout_tests.js +0 -435
- package/test/test/util/loader.js +0 -88
- package/test/test/util/manifest_generator.js +0 -705
- package/test/test/util/manifest_parser_util.js +0 -101
- package/test/test/util/mss_parser_util.js +0 -119
- package/test/test/util/offline_utils.js +0 -105
- package/test/test/util/simple_fakes.js +0 -535
- package/test/test/util/stream_generator.js +0 -676
- package/test/test/util/streaming_engine_util.js +0 -506
- package/test/test/util/test_scheme.js +0 -778
- package/test/test/util/ttml_utils.js +0 -77
- package/test/test/util/ui_utils.js +0 -183
- package/test/test/util/util.js +0 -374
- package/test/test/util/waiter.js +0 -406
- package/test/text/cue_integration.js +0 -51
- package/test/text/lrc_text_parser_unit.js +0 -98
- package/test/text/mp4_ttml_parser_unit.js +0 -171
- package/test/text/mp4_vtt_parser_unit.js +0 -195
- package/test/text/sbv_text_parser_unit.js +0 -81
- package/test/text/simple_text_displayer_unit.js +0 -468
- package/test/text/srt_text_parser_unit.js +0 -163
- package/test/text/ssa_text_parser_unit.js +0 -382
- package/test/text/text_displayer_layout_unit.js +0 -323
- package/test/text/text_engine_unit.js +0 -500
- package/test/text/text_track_integration.js +0 -186
- package/test/text/ttml_text_parser_unit.js +0 -2176
- package/test/text/ui_text_displayer_unit.js +0 -608
- package/test/text/vtt_text_parser_unit.js +0 -1363
- package/test/text/web_vtt_generator_unit.js +0 -144
- package/test/text/web_vtt_layout_integration.js +0 -524
- package/test/transmuxer/transmuxer_engine_integration.js +0 -69
- package/test/transmuxer/transmuxer_integration.js +0 -443
- package/test/ui/ad_ui_unit.js +0 -369
- package/test/ui/localization_unit.js +0 -508
- package/test/ui/ui_customization_unit.js +0 -208
- package/test/ui/ui_integration.js +0 -669
- package/test/ui/ui_unit.js +0 -902
- package/test/util/abortable_operation_unit.js +0 -624
- package/test/util/array_utils_unit.js +0 -58
- package/test/util/buffer_utils_unit.js +0 -185
- package/test/util/cmcd_manager_unit.js +0 -440
- package/test/util/content_steering_manager_unit.js +0 -427
- package/test/util/data_view_reader_unit.js +0 -254
- package/test/util/ebml_parser_unit.js +0 -297
- package/test/util/event_manager_unit.js +0 -185
- package/test/util/fake_event_target_unit.js +0 -123
- package/test/util/id3_utils_unit.js +0 -168
- package/test/util/iterables_unit.js +0 -68
- package/test/util/language_utils_unit.js +0 -240
- package/test/util/manifest_parser_utils_unit.js +0 -55
- package/test/util/mime_utils_unit.js +0 -64
- package/test/util/mp4_box_parsers_unit.js +0 -246
- package/test/util/mp4_parser_unit.js +0 -373
- package/test/util/multi_map_unit.js +0 -94
- package/test/util/object_utils_unit.js +0 -79
- package/test/util/periods_unit.js +0 -1623
- package/test/util/pssh_unit.js +0 -226
- package/test/util/state_history_unit.js +0 -101
- package/test/util/stream_utils_unit.js +0 -1068
- package/test/util/string_utils_unit.js +0 -166
- package/test/util/text_parser_unit.js +0 -165
- package/test/util/ts_parser_unit.js +0 -77
- package/test/util/xml_utils_unit.js +0 -444
|
@@ -1,1512 +0,0 @@
|
|
|
1
|
-
/*! @license
|
|
2
|
-
* Shaka Player
|
|
3
|
-
* Copyright 2016 Google LLC
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @typedef {{
|
|
9
|
-
* length: number,
|
|
10
|
-
* start: jasmine.Spy,
|
|
11
|
-
* end: jasmine.Spy
|
|
12
|
-
* }}
|
|
13
|
-
*/
|
|
14
|
-
let MockTimeRanges;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @typedef {{
|
|
19
|
-
* abort: jasmine.Spy,
|
|
20
|
-
* appendBuffer: jasmine.Spy,
|
|
21
|
-
* remove: jasmine.Spy,
|
|
22
|
-
* updating: boolean,
|
|
23
|
-
* addEventListener: jasmine.Spy,
|
|
24
|
-
* removeEventListener: jasmine.Spy,
|
|
25
|
-
* buffered: (MockTimeRanges|TimeRanges),
|
|
26
|
-
* timestampOffset: number,
|
|
27
|
-
* appendWindowEnd: number,
|
|
28
|
-
* updateend: function(),
|
|
29
|
-
* error: function(),
|
|
30
|
-
* mode: string
|
|
31
|
-
* }}
|
|
32
|
-
*/
|
|
33
|
-
let MockSourceBuffer;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
describe('MediaSourceEngine', () => {
|
|
37
|
-
const Util = shaka.test.Util;
|
|
38
|
-
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
39
|
-
|
|
40
|
-
const originalIsTypeSupported = window.MediaSource.isTypeSupported;
|
|
41
|
-
const originalTextEngine = shaka.text.TextEngine;
|
|
42
|
-
const originalCreateMediaSource =
|
|
43
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
44
|
-
shaka.media.MediaSourceEngine.prototype.createMediaSource;
|
|
45
|
-
|
|
46
|
-
const originalFindTransmuxer =
|
|
47
|
-
shaka.transmuxer.TransmuxerEngine.findTransmuxer;
|
|
48
|
-
const originalConvertCodecs =
|
|
49
|
-
shaka.transmuxer.TransmuxerEngine.convertCodecs;
|
|
50
|
-
const originalIsSupported =
|
|
51
|
-
shaka.transmuxer.TransmuxerEngine.isSupported;
|
|
52
|
-
|
|
53
|
-
// Jasmine Spies don't handle toHaveBeenCalledWith well with objects, so use
|
|
54
|
-
// some numbers instead.
|
|
55
|
-
const buffer = /** @type {!ArrayBuffer} */ (/** @type {?} */ (1));
|
|
56
|
-
const buffer2 = /** @type {!ArrayBuffer} */ (/** @type {?} */ (2));
|
|
57
|
-
const buffer3 = /** @type {!ArrayBuffer} */ (/** @type {?} */ (3));
|
|
58
|
-
|
|
59
|
-
const fakeVideoStream = {mimeType: 'video/mp4', drmInfos: [{}]};
|
|
60
|
-
const fakeAudioStream = {mimeType: 'audio/mp4', drmInfos: []};
|
|
61
|
-
const fakeTextStream = {mimeType: 'text/mp4', drmInfos: []};
|
|
62
|
-
const fakeTransportStream = {mimeType: 'tsMimetype', drmInfos: []};
|
|
63
|
-
|
|
64
|
-
/** @type {shaka.extern.Stream} */
|
|
65
|
-
const fakeStream = shaka.test.StreamingEngineUtil.createMockVideoStream(1);
|
|
66
|
-
|
|
67
|
-
let audioSourceBuffer;
|
|
68
|
-
let videoSourceBuffer;
|
|
69
|
-
let mockVideo;
|
|
70
|
-
|
|
71
|
-
/** @type {HTMLMediaElement} */
|
|
72
|
-
let video;
|
|
73
|
-
let mockMediaSource;
|
|
74
|
-
|
|
75
|
-
let mockTextEngine;
|
|
76
|
-
/** @type {!shaka.test.FakeTextDisplayer} */
|
|
77
|
-
let mockTextDisplayer;
|
|
78
|
-
/** @type {!shaka.test.FakeClosedCaptionParser} */
|
|
79
|
-
let mockClosedCaptionParser;
|
|
80
|
-
/** @type {!shaka.test.FakeTransmuxer} */
|
|
81
|
-
let mockTransmuxer;
|
|
82
|
-
|
|
83
|
-
/** @type {!jasmine.Spy} */
|
|
84
|
-
let createMediaSourceSpy;
|
|
85
|
-
/** @type {!jasmine.Spy} */
|
|
86
|
-
let requiresEncryptionInfoInAllInitSegmentsSpy;
|
|
87
|
-
/** @type {!jasmine.Spy} */
|
|
88
|
-
let fakeEncryptionSpy;
|
|
89
|
-
|
|
90
|
-
/** @type {!shaka.media.MediaSourceEngine} */
|
|
91
|
-
let mediaSourceEngine;
|
|
92
|
-
|
|
93
|
-
beforeAll(() => {
|
|
94
|
-
// Since this is not an integration test, we don't want MediaSourceEngine to
|
|
95
|
-
// fail assertions based on browser support for types. Pretend that all
|
|
96
|
-
// video and audio types are supported.
|
|
97
|
-
window.MediaSource.isTypeSupported = (mimeType) => {
|
|
98
|
-
const type = mimeType.split('/')[0];
|
|
99
|
-
return type == 'video' || type == 'audio';
|
|
100
|
-
};
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
afterAll(() => {
|
|
104
|
-
window.MediaSource.isTypeSupported = originalIsTypeSupported;
|
|
105
|
-
shaka.transmuxer.TransmuxerEngine.findTransmuxer =
|
|
106
|
-
originalFindTransmuxer;
|
|
107
|
-
shaka.transmuxer.TransmuxerEngine.convertCodecs =
|
|
108
|
-
originalConvertCodecs;
|
|
109
|
-
shaka.transmuxer.TransmuxerEngine.isSupported =
|
|
110
|
-
originalIsSupported;
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
beforeEach(/** @suppress {invalidCasts} */ () => {
|
|
114
|
-
audioSourceBuffer = createMockSourceBuffer();
|
|
115
|
-
videoSourceBuffer = createMockSourceBuffer();
|
|
116
|
-
mockMediaSource = createMockMediaSource();
|
|
117
|
-
mockMediaSource.addSourceBuffer.and.callFake((mimeType) => {
|
|
118
|
-
const type = mimeType.split('/')[0];
|
|
119
|
-
const buffer = type == 'audio' ? audioSourceBuffer : videoSourceBuffer;
|
|
120
|
-
// reset buffer params
|
|
121
|
-
buffer.timestampOffset = 0;
|
|
122
|
-
buffer.appendWindowEnd = Infinity;
|
|
123
|
-
buffer.appendWindowStart = 0;
|
|
124
|
-
|
|
125
|
-
return buffer;
|
|
126
|
-
});
|
|
127
|
-
mockTransmuxer = new shaka.test.FakeTransmuxer();
|
|
128
|
-
shaka.transmuxer.TransmuxerEngine.findTransmuxer =
|
|
129
|
-
(mimeType) => {
|
|
130
|
-
return () => mockTransmuxer;
|
|
131
|
-
};
|
|
132
|
-
shaka.transmuxer.TransmuxerEngine.convertCodecs =
|
|
133
|
-
(mimeType, contentType) => {
|
|
134
|
-
return 'video/mp4; codecs="avc1.42E01E"';
|
|
135
|
-
};
|
|
136
|
-
shaka.transmuxer.TransmuxerEngine.isSupported =
|
|
137
|
-
(mimeType, contentType) => {
|
|
138
|
-
return mimeType == 'tsMimetype';
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
shaka.text.TextEngine = createMockTextEngineCtor();
|
|
142
|
-
|
|
143
|
-
createMediaSourceSpy = jasmine.createSpy('createMediaSource');
|
|
144
|
-
createMediaSourceSpy.and.callFake((p) => {
|
|
145
|
-
p.resolve();
|
|
146
|
-
return mockMediaSource;
|
|
147
|
-
});
|
|
148
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
149
|
-
shaka.media.MediaSourceEngine.prototype.createMediaSource =
|
|
150
|
-
Util.spyFunc(createMediaSourceSpy);
|
|
151
|
-
|
|
152
|
-
requiresEncryptionInfoInAllInitSegmentsSpy = spyOn(shaka.util.Platform,
|
|
153
|
-
'requiresEncryptionInfoInAllInitSegments').and.returnValue(false);
|
|
154
|
-
|
|
155
|
-
fakeEncryptionSpy = spyOn(shaka.media.ContentWorkarounds, 'fakeEncryption')
|
|
156
|
-
.and.callFake((data) => data + 100);
|
|
157
|
-
|
|
158
|
-
// MediaSourceEngine uses video to:
|
|
159
|
-
// - set src attribute
|
|
160
|
-
// - read error codes when operations fail
|
|
161
|
-
// - seek to flush the pipeline on some platforms
|
|
162
|
-
// - check buffered.length to assert that flushing the pipeline is okay
|
|
163
|
-
mockVideo = {
|
|
164
|
-
src: '',
|
|
165
|
-
error: null,
|
|
166
|
-
currentTime: 0,
|
|
167
|
-
buffered: {
|
|
168
|
-
length: 0,
|
|
169
|
-
},
|
|
170
|
-
removeAttribute: /** @this {HTMLVideoElement} */ (attr) => {
|
|
171
|
-
// Only called with attr == 'src'.
|
|
172
|
-
// This assertion alerts us if the requirements for this mock change.
|
|
173
|
-
goog.asserts.assert(attr == 'src', 'Unexpected removeAttribute() call');
|
|
174
|
-
mockVideo.src = '';
|
|
175
|
-
},
|
|
176
|
-
addEventListener: jasmine.createSpy('addVideoEventListener'),
|
|
177
|
-
load: /** @this {HTMLVideoElement} */ () => {
|
|
178
|
-
// This assertion alerts us if the requirements for this mock change.
|
|
179
|
-
goog.asserts.assert(mockVideo.src == '', 'Unexpected load() call');
|
|
180
|
-
},
|
|
181
|
-
play: jasmine.createSpy('play'),
|
|
182
|
-
};
|
|
183
|
-
video = /** @type {HTMLMediaElement} */(mockVideo);
|
|
184
|
-
mockClosedCaptionParser = new shaka.test.FakeClosedCaptionParser();
|
|
185
|
-
mockTextDisplayer = new shaka.test.FakeTextDisplayer();
|
|
186
|
-
mediaSourceEngine = new shaka.media.MediaSourceEngine(
|
|
187
|
-
video,
|
|
188
|
-
mockTextDisplayer);
|
|
189
|
-
mediaSourceEngine.getCaptionParser = () => {
|
|
190
|
-
return mockClosedCaptionParser;
|
|
191
|
-
};
|
|
192
|
-
const config = shaka.util.PlayerConfiguration.createDefault().mediaSource;
|
|
193
|
-
mediaSourceEngine.configure(config);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
afterEach(() => {
|
|
197
|
-
mockTextEngine = null;
|
|
198
|
-
shaka.text.TextEngine = originalTextEngine;
|
|
199
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
200
|
-
shaka.media.MediaSourceEngine.prototype.createMediaSource =
|
|
201
|
-
originalCreateMediaSource;
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
describe('constructor', () => {
|
|
205
|
-
const originalCreateObjectURL =
|
|
206
|
-
shaka.media.MediaSourceEngine.createObjectURL;
|
|
207
|
-
const originalRevokeObjectURL = window.URL.revokeObjectURL;
|
|
208
|
-
const originalMediaSource = window.MediaSource;
|
|
209
|
-
/** @type {jasmine.Spy} */
|
|
210
|
-
let createObjectURLSpy;
|
|
211
|
-
/** @type {jasmine.Spy} */
|
|
212
|
-
let revokeObjectURLSpy;
|
|
213
|
-
|
|
214
|
-
beforeEach(async () => {
|
|
215
|
-
// Mock out MediaSource so we can test the production version of
|
|
216
|
-
// createMediaSource. To do this, the test must call the
|
|
217
|
-
// MediaSourceEngine constructor again. The call beforeEach was done with
|
|
218
|
-
// a mocked createMediaSource.
|
|
219
|
-
createMediaSourceSpy.calls.reset();
|
|
220
|
-
createMediaSourceSpy.and.callFake(originalCreateMediaSource);
|
|
221
|
-
|
|
222
|
-
createObjectURLSpy = jasmine.createSpy('createObjectURL');
|
|
223
|
-
createObjectURLSpy.and.returnValue('blob:foo');
|
|
224
|
-
shaka.media.MediaSourceEngine.createObjectURL =
|
|
225
|
-
Util.spyFunc(createObjectURLSpy);
|
|
226
|
-
|
|
227
|
-
revokeObjectURLSpy = jasmine.createSpy('revokeObjectURL');
|
|
228
|
-
window.URL.revokeObjectURL = Util.spyFunc(revokeObjectURLSpy);
|
|
229
|
-
|
|
230
|
-
const mediaSourceSpy = jasmine.createSpy('MediaSource');
|
|
231
|
-
// Because this is a fake constructor, it must be callable with "new".
|
|
232
|
-
// This will cause jasmine to invoke the callback with "new" as well, so
|
|
233
|
-
// the callback must be a "function". This detail is hidden when babel
|
|
234
|
-
// transpiles the tests.
|
|
235
|
-
// eslint-disable-next-line prefer-arrow-callback, no-restricted-syntax
|
|
236
|
-
mediaSourceSpy.and.callFake(function() {
|
|
237
|
-
return mockMediaSource;
|
|
238
|
-
});
|
|
239
|
-
window.MediaSource = Util.spyFunc(mediaSourceSpy);
|
|
240
|
-
|
|
241
|
-
await mediaSourceEngine.destroy();
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
afterAll(() => {
|
|
245
|
-
shaka.media.MediaSourceEngine.createObjectURL = originalCreateObjectURL;
|
|
246
|
-
window.MediaSource = originalMediaSource;
|
|
247
|
-
window.URL.revokeObjectURL = originalRevokeObjectURL;
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
it('creates a MediaSource object and sets video.src', () => {
|
|
251
|
-
mediaSourceEngine = new shaka.media.MediaSourceEngine(
|
|
252
|
-
video,
|
|
253
|
-
new shaka.test.FakeTextDisplayer());
|
|
254
|
-
|
|
255
|
-
expect(createMediaSourceSpy).toHaveBeenCalled();
|
|
256
|
-
expect(createObjectURLSpy).toHaveBeenCalled();
|
|
257
|
-
expect(mockVideo.src).toBe('blob:foo');
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
it('revokes object URL after MediaSource opens', () => {
|
|
261
|
-
let onSourceOpenListener;
|
|
262
|
-
|
|
263
|
-
mockMediaSource.addEventListener.and.callFake((event, callback, _) => {
|
|
264
|
-
if (event == 'sourceopen') {
|
|
265
|
-
onSourceOpenListener = callback;
|
|
266
|
-
}
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
mediaSourceEngine = new shaka.media.MediaSourceEngine(
|
|
270
|
-
video,
|
|
271
|
-
new shaka.test.FakeTextDisplayer());
|
|
272
|
-
|
|
273
|
-
expect(mockMediaSource.addEventListener).toHaveBeenCalledTimes(1);
|
|
274
|
-
expect(mockMediaSource.addEventListener.calls.mostRecent().args[0])
|
|
275
|
-
.toBe('sourceopen');
|
|
276
|
-
expect(typeof onSourceOpenListener).toBe(typeof Function);
|
|
277
|
-
|
|
278
|
-
onSourceOpenListener();
|
|
279
|
-
|
|
280
|
-
expect(revokeObjectURLSpy).toHaveBeenCalledWith('blob:foo');
|
|
281
|
-
});
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
describe('init', () => {
|
|
285
|
-
it('creates SourceBuffers for the given types', async () => {
|
|
286
|
-
const initObject = new Map();
|
|
287
|
-
initObject.set(ContentType.AUDIO, fakeAudioStream);
|
|
288
|
-
initObject.set(ContentType.VIDEO, fakeVideoStream);
|
|
289
|
-
await mediaSourceEngine.init(initObject, false);
|
|
290
|
-
expect(mockMediaSource.addSourceBuffer).toHaveBeenCalledWith('audio/mp4');
|
|
291
|
-
expect(mockMediaSource.addSourceBuffer).toHaveBeenCalledWith('video/mp4');
|
|
292
|
-
expect(shaka.text.TextEngine).not.toHaveBeenCalled();
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it('creates TextEngines for text types', async () => {
|
|
296
|
-
const initObject = new Map();
|
|
297
|
-
initObject.set(ContentType.TEXT, fakeTextStream);
|
|
298
|
-
await mediaSourceEngine.init(initObject, false);
|
|
299
|
-
expect(mockMediaSource.addSourceBuffer).not.toHaveBeenCalled();
|
|
300
|
-
expect(shaka.text.TextEngine).toHaveBeenCalled();
|
|
301
|
-
});
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
describe('bufferStart and bufferEnd', () => {
|
|
305
|
-
beforeEach(async () => {
|
|
306
|
-
const initObject = new Map();
|
|
307
|
-
initObject.set(ContentType.AUDIO, fakeAudioStream);
|
|
308
|
-
initObject.set(ContentType.TEXT, fakeTextStream);
|
|
309
|
-
await mediaSourceEngine.init(initObject, false);
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
it('returns correct timestamps for one range', () => {
|
|
313
|
-
audioSourceBuffer.buffered = createFakeBuffered([{start: 0, end: 10}]);
|
|
314
|
-
|
|
315
|
-
expect(mediaSourceEngine.bufferStart(ContentType.AUDIO)).toBeCloseTo(0);
|
|
316
|
-
expect(mediaSourceEngine.bufferEnd(ContentType.AUDIO)).toBeCloseTo(10);
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
it('returns correct timestamps for multiple ranges', () => {
|
|
320
|
-
audioSourceBuffer.buffered =
|
|
321
|
-
createFakeBuffered([{start: 5, end: 10}, {start: 20, end: 30}]);
|
|
322
|
-
|
|
323
|
-
expect(mediaSourceEngine.bufferStart(ContentType.AUDIO)).toBeCloseTo(5);
|
|
324
|
-
expect(mediaSourceEngine.bufferEnd(ContentType.AUDIO)).toBeCloseTo(30);
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
it('returns null if there are no ranges', () => {
|
|
328
|
-
audioSourceBuffer.buffered = createFakeBuffered([]);
|
|
329
|
-
|
|
330
|
-
expect(mediaSourceEngine.bufferStart(ContentType.AUDIO)).toBeNull();
|
|
331
|
-
expect(mediaSourceEngine.bufferEnd(ContentType.AUDIO)).toBeNull();
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
it('will forward to TextEngine', () => {
|
|
335
|
-
mockTextEngine.bufferStart.and.returnValue(10);
|
|
336
|
-
mockTextEngine.bufferEnd.and.returnValue(20);
|
|
337
|
-
|
|
338
|
-
expect(mockTextEngine.bufferStart).not.toHaveBeenCalled();
|
|
339
|
-
expect(mediaSourceEngine.bufferStart(ContentType.TEXT)).toBe(10);
|
|
340
|
-
expect(mockTextEngine.bufferStart).toHaveBeenCalled();
|
|
341
|
-
|
|
342
|
-
expect(mockTextEngine.bufferEnd).not.toHaveBeenCalled();
|
|
343
|
-
expect(mediaSourceEngine.bufferEnd(ContentType.TEXT)).toBe(20);
|
|
344
|
-
expect(mockTextEngine.bufferEnd).toHaveBeenCalled();
|
|
345
|
-
});
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
describe('bufferedAheadOf', () => {
|
|
349
|
-
beforeEach(async () => {
|
|
350
|
-
const initObject = new Map();
|
|
351
|
-
initObject.set(ContentType.AUDIO, fakeAudioStream);
|
|
352
|
-
initObject.set(ContentType.TEXT, fakeTextStream);
|
|
353
|
-
await mediaSourceEngine.init(initObject, false);
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
it('returns the amount of data ahead of the given position', () => {
|
|
357
|
-
audioSourceBuffer.buffered = createFakeBuffered([{start: 0, end: 10}]);
|
|
358
|
-
|
|
359
|
-
expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 0))
|
|
360
|
-
.toBeCloseTo(10);
|
|
361
|
-
expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 5))
|
|
362
|
-
.toBeCloseTo(5);
|
|
363
|
-
expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 9.9))
|
|
364
|
-
.toBeCloseTo(0.1);
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
it('returns zero when given an unbuffered time', () => {
|
|
368
|
-
audioSourceBuffer.buffered = createFakeBuffered([{start: 5, end: 10}]);
|
|
369
|
-
|
|
370
|
-
expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 10))
|
|
371
|
-
.toBeCloseTo(0);
|
|
372
|
-
expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 100))
|
|
373
|
-
.toBeCloseTo(0);
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
it('returns the correct amount with multiple ranges', () => {
|
|
377
|
-
audioSourceBuffer.buffered =
|
|
378
|
-
createFakeBuffered([{start: 1, end: 3}, {start: 6, end: 10}]);
|
|
379
|
-
|
|
380
|
-
// in range 0
|
|
381
|
-
expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 1))
|
|
382
|
-
.toBeCloseTo(6);
|
|
383
|
-
expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 2.5))
|
|
384
|
-
.toBeCloseTo(4.5);
|
|
385
|
-
|
|
386
|
-
// between range 0 and 1
|
|
387
|
-
expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 5))
|
|
388
|
-
.toBeCloseTo(4);
|
|
389
|
-
|
|
390
|
-
// in range 1
|
|
391
|
-
expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 6))
|
|
392
|
-
.toBeCloseTo(4);
|
|
393
|
-
expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 9.9))
|
|
394
|
-
.toBeCloseTo(0.1);
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
it('will forward to TextEngine', () => {
|
|
398
|
-
mockTextEngine.bufferedAheadOf.and.returnValue(10);
|
|
399
|
-
|
|
400
|
-
expect(mockTextEngine.bufferedAheadOf).not.toHaveBeenCalled();
|
|
401
|
-
expect(mediaSourceEngine.bufferedAheadOf(ContentType.TEXT, 5)).toBe(10);
|
|
402
|
-
expect(mockTextEngine.bufferedAheadOf).toHaveBeenCalledWith(5);
|
|
403
|
-
});
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
describe('appendBuffer', () => {
|
|
407
|
-
beforeEach(async () => {
|
|
408
|
-
captureEvents(audioSourceBuffer, ['updateend', 'error']);
|
|
409
|
-
captureEvents(videoSourceBuffer, ['updateend', 'error']);
|
|
410
|
-
const initObject = new Map();
|
|
411
|
-
initObject.set(ContentType.AUDIO, fakeAudioStream);
|
|
412
|
-
initObject.set(ContentType.VIDEO, fakeVideoStream);
|
|
413
|
-
initObject.set(ContentType.TEXT, fakeTextStream);
|
|
414
|
-
await mediaSourceEngine.init(initObject, false);
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
it('should apply fake encryption by default', async () => {
|
|
418
|
-
requiresEncryptionInfoInAllInitSegmentsSpy.and.returnValue(true);
|
|
419
|
-
|
|
420
|
-
const p = mediaSourceEngine.appendBuffer(
|
|
421
|
-
ContentType.VIDEO, buffer, null, fakeStream,
|
|
422
|
-
/* hasClosedCaptions= */ false);
|
|
423
|
-
|
|
424
|
-
expect(fakeEncryptionSpy).toHaveBeenCalled();
|
|
425
|
-
|
|
426
|
-
expect(videoSourceBuffer.appendBuffer)
|
|
427
|
-
.toHaveBeenCalledWith((buffer + 100));
|
|
428
|
-
videoSourceBuffer.updateend();
|
|
429
|
-
|
|
430
|
-
await p;
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
it('should not apply fake encryption when config is off', async () => {
|
|
434
|
-
requiresEncryptionInfoInAllInitSegmentsSpy.and.returnValue(true);
|
|
435
|
-
|
|
436
|
-
const config = shaka.util.PlayerConfiguration.createDefault().mediaSource;
|
|
437
|
-
config.insertFakeEncryptionInInit = false;
|
|
438
|
-
|
|
439
|
-
mediaSourceEngine.configure(config);
|
|
440
|
-
|
|
441
|
-
const p = mediaSourceEngine.appendBuffer(
|
|
442
|
-
ContentType.VIDEO, buffer, null, fakeStream,
|
|
443
|
-
/* hasClosedCaptions= */ false);
|
|
444
|
-
|
|
445
|
-
expect(fakeEncryptionSpy).not.toHaveBeenCalled();
|
|
446
|
-
|
|
447
|
-
expect(videoSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
|
|
448
|
-
videoSourceBuffer.updateend();
|
|
449
|
-
|
|
450
|
-
await p;
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
it('appends the given data', async () => {
|
|
454
|
-
const p = mediaSourceEngine.appendBuffer(
|
|
455
|
-
ContentType.AUDIO, buffer, null, fakeStream,
|
|
456
|
-
/* hasClosedCaptions= */ false);
|
|
457
|
-
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
|
|
458
|
-
audioSourceBuffer.updateend();
|
|
459
|
-
await p;
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
it('rejects promise when operation throws', async () => {
|
|
463
|
-
audioSourceBuffer.appendBuffer.and.throwError('fail!');
|
|
464
|
-
mockVideo.error = {code: 5, message: 'something failed'};
|
|
465
|
-
const expected = Util.jasmineError(new shaka.util.Error(
|
|
466
|
-
shaka.util.Error.Severity.CRITICAL,
|
|
467
|
-
shaka.util.Error.Category.MEDIA,
|
|
468
|
-
shaka.util.Error.Code.MEDIA_SOURCE_OPERATION_THREW,
|
|
469
|
-
jasmine.objectContaining({message: 'fail!'}),
|
|
470
|
-
{code: 5, message: 'something failed'}));
|
|
471
|
-
await expectAsync(
|
|
472
|
-
mediaSourceEngine.appendBuffer(
|
|
473
|
-
ContentType.AUDIO, buffer, null, fakeStream,
|
|
474
|
-
/* hasClosedCaptions= */ false))
|
|
475
|
-
.toBeRejectedWith(expected);
|
|
476
|
-
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
it('rejects promise when op. throws QuotaExceededError', async () => {
|
|
480
|
-
const fakeDOMException = {name: 'QuotaExceededError'};
|
|
481
|
-
audioSourceBuffer.appendBuffer.and.callFake(() => {
|
|
482
|
-
throw fakeDOMException;
|
|
483
|
-
});
|
|
484
|
-
mockVideo.error = {code: 5};
|
|
485
|
-
const expected = Util.jasmineError(new shaka.util.Error(
|
|
486
|
-
shaka.util.Error.Severity.CRITICAL,
|
|
487
|
-
shaka.util.Error.Category.MEDIA,
|
|
488
|
-
shaka.util.Error.Code.QUOTA_EXCEEDED_ERROR,
|
|
489
|
-
ContentType.AUDIO));
|
|
490
|
-
await expectAsync(
|
|
491
|
-
mediaSourceEngine.appendBuffer(
|
|
492
|
-
ContentType.AUDIO, buffer, null, fakeStream,
|
|
493
|
-
/* hasClosedCaptions= */ false))
|
|
494
|
-
.toBeRejectedWith(expected);
|
|
495
|
-
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
it('handles QuotaExceededError for pending operations', async () => {
|
|
499
|
-
const fakeDOMException = {name: 'QuotaExceededError'};
|
|
500
|
-
audioSourceBuffer.appendBuffer.and.callFake(() => {
|
|
501
|
-
if (audioSourceBuffer.appendBuffer.calls.count() > 1) {
|
|
502
|
-
throw fakeDOMException;
|
|
503
|
-
}
|
|
504
|
-
});
|
|
505
|
-
mockVideo.error = {code: 5};
|
|
506
|
-
const expected = Util.jasmineError(new shaka.util.Error(
|
|
507
|
-
shaka.util.Error.Severity.CRITICAL,
|
|
508
|
-
shaka.util.Error.Category.MEDIA,
|
|
509
|
-
shaka.util.Error.Code.QUOTA_EXCEEDED_ERROR,
|
|
510
|
-
ContentType.AUDIO));
|
|
511
|
-
|
|
512
|
-
const p1 = mediaSourceEngine.appendBuffer(
|
|
513
|
-
ContentType.AUDIO, buffer, null, fakeStream,
|
|
514
|
-
/* hasClosedCaptions= */ false);
|
|
515
|
-
const p2 = mediaSourceEngine.appendBuffer(
|
|
516
|
-
ContentType.AUDIO, buffer, null, fakeStream,
|
|
517
|
-
/* hasClosedCaptions= */ false);
|
|
518
|
-
audioSourceBuffer.updateend();
|
|
519
|
-
await expectAsync(p1).toBeResolved();
|
|
520
|
-
await expectAsync(p2).toBeRejectedWith(expected);
|
|
521
|
-
});
|
|
522
|
-
|
|
523
|
-
it('rejects the promise if this operation fails async', async () => {
|
|
524
|
-
mockVideo.error = {code: 5};
|
|
525
|
-
const p = mediaSourceEngine.appendBuffer(
|
|
526
|
-
ContentType.AUDIO, buffer, null, fakeStream,
|
|
527
|
-
/* hasClosedCaptions= */ false);
|
|
528
|
-
audioSourceBuffer.error();
|
|
529
|
-
audioSourceBuffer.updateend();
|
|
530
|
-
|
|
531
|
-
const expected = Util.jasmineError(new shaka.util.Error(
|
|
532
|
-
shaka.util.Error.Severity.CRITICAL,
|
|
533
|
-
shaka.util.Error.Category.MEDIA,
|
|
534
|
-
shaka.util.Error.Code.MEDIA_SOURCE_OPERATION_FAILED,
|
|
535
|
-
5));
|
|
536
|
-
await expectAsync(p).toBeRejectedWith(expected);
|
|
537
|
-
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
it('queues operations on a single SourceBuffer', async () => {
|
|
541
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
542
|
-
const p1 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
|
|
543
|
-
ContentType.AUDIO, buffer, null, fakeStream,
|
|
544
|
-
/* hasClosedCaptions= */ false));
|
|
545
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
546
|
-
const p2 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
|
|
547
|
-
ContentType.AUDIO, buffer2, null, fakeStream,
|
|
548
|
-
/* hasClosedCaptions= */ false));
|
|
549
|
-
|
|
550
|
-
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
|
|
551
|
-
expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalledWith(buffer2);
|
|
552
|
-
expect(p1.status).toBe('pending');
|
|
553
|
-
expect(p2.status).toBe('pending');
|
|
554
|
-
|
|
555
|
-
audioSourceBuffer.updateend();
|
|
556
|
-
await p1;
|
|
557
|
-
expect(p2.status).toBe('pending');
|
|
558
|
-
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer2);
|
|
559
|
-
audioSourceBuffer.updateend();
|
|
560
|
-
await p2;
|
|
561
|
-
});
|
|
562
|
-
|
|
563
|
-
it('queues operations independently for different types', async () => {
|
|
564
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
565
|
-
const p1 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
|
|
566
|
-
ContentType.AUDIO, buffer, null, fakeStream,
|
|
567
|
-
/* hasClosedCaptions= */ false));
|
|
568
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
569
|
-
const p2 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
|
|
570
|
-
ContentType.AUDIO, buffer2, null, fakeStream,
|
|
571
|
-
/* hasClosedCaptions= */ false));
|
|
572
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
573
|
-
const p3 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
|
|
574
|
-
ContentType.VIDEO, buffer3, null, fakeStream,
|
|
575
|
-
/* hasClosedCaptions= */ false));
|
|
576
|
-
|
|
577
|
-
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
|
|
578
|
-
expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalledWith(buffer2);
|
|
579
|
-
expect(videoSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer3);
|
|
580
|
-
expect(p1.status).toBe('pending');
|
|
581
|
-
expect(p2.status).toBe('pending');
|
|
582
|
-
expect(p3.status).toBe('pending');
|
|
583
|
-
|
|
584
|
-
audioSourceBuffer.updateend();
|
|
585
|
-
videoSourceBuffer.updateend();
|
|
586
|
-
// Wait a tick between each updateend() and the status check that follows.
|
|
587
|
-
await p1;
|
|
588
|
-
expect(p2.status).toBe('pending');
|
|
589
|
-
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer2);
|
|
590
|
-
await p3;
|
|
591
|
-
audioSourceBuffer.updateend();
|
|
592
|
-
await p2;
|
|
593
|
-
});
|
|
594
|
-
|
|
595
|
-
it('continues if an operation throws', async () => {
|
|
596
|
-
audioSourceBuffer.appendBuffer.and.callFake((value) => {
|
|
597
|
-
if (value == 2) {
|
|
598
|
-
// throw synchronously.
|
|
599
|
-
throw new Error();
|
|
600
|
-
} else {
|
|
601
|
-
// complete successfully asynchronously.
|
|
602
|
-
Promise.resolve().then(() => {
|
|
603
|
-
audioSourceBuffer.updateend();
|
|
604
|
-
});
|
|
605
|
-
}
|
|
606
|
-
});
|
|
607
|
-
|
|
608
|
-
const p1 = mediaSourceEngine.appendBuffer(
|
|
609
|
-
ContentType.AUDIO, buffer, null, fakeStream,
|
|
610
|
-
/* hasClosedCaptions= */ false);
|
|
611
|
-
const p2 = mediaSourceEngine.appendBuffer(
|
|
612
|
-
ContentType.AUDIO, buffer2, null, fakeStream,
|
|
613
|
-
/* hasClosedCaptions= */ false);
|
|
614
|
-
const p3 = mediaSourceEngine.appendBuffer(
|
|
615
|
-
ContentType.AUDIO, buffer3, null, fakeStream,
|
|
616
|
-
/* hasClosedCaptions= */ false);
|
|
617
|
-
|
|
618
|
-
await expectAsync(p1).toBeResolved();
|
|
619
|
-
await expectAsync(p2).toBeRejected();
|
|
620
|
-
await expectAsync(p3).toBeResolved();
|
|
621
|
-
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
|
|
622
|
-
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer2);
|
|
623
|
-
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer3);
|
|
624
|
-
});
|
|
625
|
-
|
|
626
|
-
it('forwards to TextEngine', async () => {
|
|
627
|
-
const data = new ArrayBuffer(0);
|
|
628
|
-
expect(mockTextEngine.appendBuffer).not.toHaveBeenCalled();
|
|
629
|
-
const reference = dummyReference(0, 10);
|
|
630
|
-
await mediaSourceEngine.appendBuffer(
|
|
631
|
-
ContentType.TEXT, data, reference, fakeStream,
|
|
632
|
-
/* hasClosedCaptions= */ false);
|
|
633
|
-
expect(mockTextEngine.appendBuffer).toHaveBeenCalledWith(
|
|
634
|
-
data, 0, 10, 'foo://bar');
|
|
635
|
-
});
|
|
636
|
-
|
|
637
|
-
it('appends transmuxed data', async () => {
|
|
638
|
-
const initObject = new Map();
|
|
639
|
-
initObject.set(ContentType.VIDEO, fakeTransportStream);
|
|
640
|
-
|
|
641
|
-
const output = {
|
|
642
|
-
data: new Uint8Array(1),
|
|
643
|
-
captions: [{}],
|
|
644
|
-
};
|
|
645
|
-
mockTransmuxer.transmux.and.returnValue(Promise.resolve(output));
|
|
646
|
-
|
|
647
|
-
const init = async () => {
|
|
648
|
-
await mediaSourceEngine.init(initObject, false);
|
|
649
|
-
await mediaSourceEngine.appendBuffer(
|
|
650
|
-
ContentType.VIDEO, buffer, null, fakeStream,
|
|
651
|
-
/* hasClosedCaptions= */ false);
|
|
652
|
-
expect(videoSourceBuffer.appendBuffer).toHaveBeenCalled();
|
|
653
|
-
};
|
|
654
|
-
|
|
655
|
-
// The 'updateend' event fires once the data is done appending to the
|
|
656
|
-
// media source. We only append to the media source once transmuxing is
|
|
657
|
-
// done. Since transmuxing is done using Promises, we need to delay the
|
|
658
|
-
// event until MediaSourceEngine calls appendBuffer.
|
|
659
|
-
const delay = async () => {
|
|
660
|
-
await Util.shortDelay();
|
|
661
|
-
videoSourceBuffer.updateend();
|
|
662
|
-
};
|
|
663
|
-
await Promise.all([init(), delay()]);
|
|
664
|
-
});
|
|
665
|
-
|
|
666
|
-
it('appends parsed closed captions from CaptionParser', async () => {
|
|
667
|
-
const initObject = new Map();
|
|
668
|
-
initObject.set(ContentType.VIDEO, fakeVideoStream);
|
|
669
|
-
|
|
670
|
-
mockClosedCaptionParser.parseFromSpy.and.callFake((data) => {
|
|
671
|
-
return ['foo', 'bar'];
|
|
672
|
-
});
|
|
673
|
-
|
|
674
|
-
await mediaSourceEngine.init(initObject, false);
|
|
675
|
-
|
|
676
|
-
// Initialize the closed caption parser.
|
|
677
|
-
const appendInit = mediaSourceEngine.appendBuffer(
|
|
678
|
-
ContentType.VIDEO, buffer, null, fakeStream,
|
|
679
|
-
/* hasClosedCaptions= */ true);
|
|
680
|
-
// In MediaSourceEngine, appendBuffer() is async and Promise-based, but
|
|
681
|
-
// at the browser level, it's event-based.
|
|
682
|
-
// MediaSourceEngine waits for the 'updateend' event from the
|
|
683
|
-
// SourceBuffer, and uses that to resolve the appendBuffer Promise.
|
|
684
|
-
// Here, we must trigger the event on the fake/mock SourceBuffer before
|
|
685
|
-
// waiting on the appendBuffer Promise.
|
|
686
|
-
videoSourceBuffer.updateend();
|
|
687
|
-
await appendInit;
|
|
688
|
-
|
|
689
|
-
expect(mockTextEngine.storeAndAppendClosedCaptions).not
|
|
690
|
-
.toHaveBeenCalled();
|
|
691
|
-
// Parse and append the closed captions embedded in video stream.
|
|
692
|
-
const reference = dummyReference(0, 1000);
|
|
693
|
-
const appendVideo = mediaSourceEngine.appendBuffer(
|
|
694
|
-
ContentType.VIDEO, buffer, reference, fakeStream,
|
|
695
|
-
/* hasClosedCaptions= */ true);
|
|
696
|
-
videoSourceBuffer.updateend();
|
|
697
|
-
await appendVideo;
|
|
698
|
-
|
|
699
|
-
expect(mockTextEngine.storeAndAppendClosedCaptions).toHaveBeenCalled();
|
|
700
|
-
});
|
|
701
|
-
|
|
702
|
-
it('sets timestampOffset on adaptations in sequence mode', async () => {
|
|
703
|
-
const initObject = new Map();
|
|
704
|
-
initObject.set(ContentType.VIDEO, fakeVideoStream);
|
|
705
|
-
videoSourceBuffer.mode = 'sequence';
|
|
706
|
-
|
|
707
|
-
await mediaSourceEngine.init(initObject, /* sequenceMode= */ true);
|
|
708
|
-
|
|
709
|
-
expect(videoSourceBuffer.timestampOffset).toBe(0);
|
|
710
|
-
|
|
711
|
-
// Mocks appending a segment from a newly adapted variant with a 0.50
|
|
712
|
-
// second misalignment from the old variant.
|
|
713
|
-
const reference = dummyReference(0, 1000);
|
|
714
|
-
reference.startTime = 0.50;
|
|
715
|
-
const appendVideo = mediaSourceEngine.appendBuffer(
|
|
716
|
-
ContentType.VIDEO, buffer, reference, fakeStream,
|
|
717
|
-
/* hasClosedCaptions= */ false,
|
|
718
|
-
/* seeked= */ false, /* adaptation= */ true);
|
|
719
|
-
videoSourceBuffer.updateend();
|
|
720
|
-
await appendVideo;
|
|
721
|
-
|
|
722
|
-
expect(videoSourceBuffer.timestampOffset).toBe(0.50);
|
|
723
|
-
});
|
|
724
|
-
});
|
|
725
|
-
|
|
726
|
-
describe('remove', () => {
|
|
727
|
-
beforeEach(async () => {
|
|
728
|
-
captureEvents(audioSourceBuffer, ['updateend', 'error']);
|
|
729
|
-
captureEvents(videoSourceBuffer, ['updateend', 'error']);
|
|
730
|
-
const initObject = new Map();
|
|
731
|
-
initObject.set(ContentType.AUDIO, fakeAudioStream);
|
|
732
|
-
initObject.set(ContentType.VIDEO, fakeVideoStream);
|
|
733
|
-
initObject.set(ContentType.TEXT, fakeTextStream);
|
|
734
|
-
await mediaSourceEngine.init(initObject, false);
|
|
735
|
-
});
|
|
736
|
-
|
|
737
|
-
it('removes the given data', async () => {
|
|
738
|
-
const p = mediaSourceEngine.remove(ContentType.AUDIO, 1, 5);
|
|
739
|
-
audioSourceBuffer.updateend();
|
|
740
|
-
|
|
741
|
-
await p;
|
|
742
|
-
expect(audioSourceBuffer.remove).toHaveBeenCalledWith(1, 5);
|
|
743
|
-
});
|
|
744
|
-
|
|
745
|
-
it('rejects promise when operation throws', async () => {
|
|
746
|
-
audioSourceBuffer.remove.and.throwError('fail!');
|
|
747
|
-
mockVideo.error = {code: 5};
|
|
748
|
-
|
|
749
|
-
const expected = Util.jasmineError(new shaka.util.Error(
|
|
750
|
-
shaka.util.Error.Severity.CRITICAL,
|
|
751
|
-
shaka.util.Error.Category.MEDIA,
|
|
752
|
-
shaka.util.Error.Code.MEDIA_SOURCE_OPERATION_THREW,
|
|
753
|
-
jasmine.objectContaining({message: 'fail!'}),
|
|
754
|
-
{code: 5}));
|
|
755
|
-
await expectAsync(mediaSourceEngine.remove(ContentType.AUDIO, 1, 5))
|
|
756
|
-
.toBeRejectedWith(expected);
|
|
757
|
-
expect(audioSourceBuffer.remove).toHaveBeenCalledWith(1, 5);
|
|
758
|
-
});
|
|
759
|
-
|
|
760
|
-
it('rejects the promise if this operation fails async', async () => {
|
|
761
|
-
mockVideo.error = {code: 5};
|
|
762
|
-
const p = mediaSourceEngine.remove(ContentType.AUDIO, 1, 5);
|
|
763
|
-
audioSourceBuffer.error();
|
|
764
|
-
audioSourceBuffer.updateend();
|
|
765
|
-
|
|
766
|
-
const expected = Util.jasmineError(new shaka.util.Error(
|
|
767
|
-
shaka.util.Error.Severity.CRITICAL,
|
|
768
|
-
shaka.util.Error.Category.MEDIA,
|
|
769
|
-
shaka.util.Error.Code.MEDIA_SOURCE_OPERATION_FAILED,
|
|
770
|
-
5));
|
|
771
|
-
await expectAsync(p).toBeRejectedWith(expected);
|
|
772
|
-
expect(audioSourceBuffer.remove).toHaveBeenCalledWith(1, 5);
|
|
773
|
-
});
|
|
774
|
-
|
|
775
|
-
it('queues operations on a single SourceBuffer', async () => {
|
|
776
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
777
|
-
const p1 = new shaka.test.StatusPromise(
|
|
778
|
-
mediaSourceEngine.remove(ContentType.AUDIO, 1, 5));
|
|
779
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
780
|
-
const p2 = new shaka.test.StatusPromise(
|
|
781
|
-
mediaSourceEngine.remove(ContentType.AUDIO, 6, 10));
|
|
782
|
-
|
|
783
|
-
expect(audioSourceBuffer.remove).toHaveBeenCalledWith(1, 5);
|
|
784
|
-
expect(audioSourceBuffer.remove).not.toHaveBeenCalledWith(6, 10);
|
|
785
|
-
expect(p1.status).toBe('pending');
|
|
786
|
-
expect(p2.status).toBe('pending');
|
|
787
|
-
|
|
788
|
-
audioSourceBuffer.updateend();
|
|
789
|
-
await p1;
|
|
790
|
-
expect(p2.status).toBe('pending');
|
|
791
|
-
expect(audioSourceBuffer.remove).toHaveBeenCalledWith(6, 10);
|
|
792
|
-
audioSourceBuffer.updateend();
|
|
793
|
-
await p2;
|
|
794
|
-
});
|
|
795
|
-
|
|
796
|
-
it('queues operations independently for different types', async () => {
|
|
797
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
798
|
-
const p1 = new shaka.test.StatusPromise(
|
|
799
|
-
mediaSourceEngine.remove(ContentType.AUDIO, 1, 5));
|
|
800
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
801
|
-
const p2 = new shaka.test.StatusPromise(
|
|
802
|
-
mediaSourceEngine.remove(ContentType.AUDIO, 6, 10));
|
|
803
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
804
|
-
const p3 = new shaka.test.StatusPromise(
|
|
805
|
-
mediaSourceEngine.remove(ContentType.VIDEO, 3, 8));
|
|
806
|
-
|
|
807
|
-
expect(audioSourceBuffer.remove).toHaveBeenCalledWith(1, 5);
|
|
808
|
-
expect(audioSourceBuffer.remove).not.toHaveBeenCalledWith(6, 10);
|
|
809
|
-
expect(videoSourceBuffer.remove).toHaveBeenCalledWith(3, 8);
|
|
810
|
-
expect(p1.status).toBe('pending');
|
|
811
|
-
expect(p2.status).toBe('pending');
|
|
812
|
-
expect(p3.status).toBe('pending');
|
|
813
|
-
|
|
814
|
-
audioSourceBuffer.updateend();
|
|
815
|
-
videoSourceBuffer.updateend();
|
|
816
|
-
await p1;
|
|
817
|
-
expect(p2.status).toBe('pending');
|
|
818
|
-
expect(audioSourceBuffer.remove).toHaveBeenCalledWith(6, 10);
|
|
819
|
-
await p3;
|
|
820
|
-
audioSourceBuffer.updateend();
|
|
821
|
-
await p2;
|
|
822
|
-
});
|
|
823
|
-
|
|
824
|
-
it('continues if an operation throws', async () => {
|
|
825
|
-
audioSourceBuffer.remove.and.callFake((start, end) => {
|
|
826
|
-
if (start == 2) {
|
|
827
|
-
// throw synchronously.
|
|
828
|
-
throw new Error();
|
|
829
|
-
} else {
|
|
830
|
-
// complete successfully asynchronously.
|
|
831
|
-
Promise.resolve().then(() => {
|
|
832
|
-
audioSourceBuffer.updateend();
|
|
833
|
-
});
|
|
834
|
-
}
|
|
835
|
-
});
|
|
836
|
-
|
|
837
|
-
const p1 = mediaSourceEngine.remove(ContentType.AUDIO, 1, 2);
|
|
838
|
-
const p2 = mediaSourceEngine.remove(ContentType.AUDIO, 2, 3);
|
|
839
|
-
const p3 = mediaSourceEngine.remove(ContentType.AUDIO, 3, 4);
|
|
840
|
-
|
|
841
|
-
await expectAsync(p1).toBeResolved();
|
|
842
|
-
await expectAsync(p2).toBeRejected();
|
|
843
|
-
await expectAsync(p3).toBeResolved();
|
|
844
|
-
expect(audioSourceBuffer.remove).toHaveBeenCalledWith(1, 2);
|
|
845
|
-
expect(audioSourceBuffer.remove).toHaveBeenCalledWith(2, 3);
|
|
846
|
-
expect(audioSourceBuffer.remove).toHaveBeenCalledWith(3, 4);
|
|
847
|
-
});
|
|
848
|
-
|
|
849
|
-
it('will forward to TextEngine', async () => {
|
|
850
|
-
expect(mockTextEngine.remove).not.toHaveBeenCalled();
|
|
851
|
-
await mediaSourceEngine.remove(ContentType.TEXT, 10, 20);
|
|
852
|
-
expect(mockTextEngine.remove).toHaveBeenCalledWith(10, 20);
|
|
853
|
-
});
|
|
854
|
-
});
|
|
855
|
-
|
|
856
|
-
describe('clear', () => {
|
|
857
|
-
beforeEach(async () => {
|
|
858
|
-
captureEvents(audioSourceBuffer, ['updateend', 'error']);
|
|
859
|
-
captureEvents(videoSourceBuffer, ['updateend', 'error']);
|
|
860
|
-
const initObject = new Map();
|
|
861
|
-
initObject.set(ContentType.AUDIO, fakeAudioStream);
|
|
862
|
-
initObject.set(ContentType.VIDEO, fakeVideoStream);
|
|
863
|
-
initObject.set(ContentType.TEXT, fakeTextStream);
|
|
864
|
-
await mediaSourceEngine.init(initObject, false);
|
|
865
|
-
});
|
|
866
|
-
|
|
867
|
-
it('clears the given data', async () => {
|
|
868
|
-
mockMediaSource.durationGetter.and.returnValue(20);
|
|
869
|
-
const p = mediaSourceEngine.clear(ContentType.AUDIO);
|
|
870
|
-
audioSourceBuffer.updateend();
|
|
871
|
-
|
|
872
|
-
await p;
|
|
873
|
-
expect(audioSourceBuffer.remove).toHaveBeenCalledTimes(1);
|
|
874
|
-
expect(audioSourceBuffer.remove.calls.argsFor(0)[0]).toBe(0);
|
|
875
|
-
expect(audioSourceBuffer.remove.calls.argsFor(0)[1] >= 20).toBeTruthy();
|
|
876
|
-
});
|
|
877
|
-
|
|
878
|
-
it('does not seek', async () => {
|
|
879
|
-
// We had a bug in which we got into a seek loop. Seeking caused
|
|
880
|
-
// StreamingEngine to call clear(). Clearing triggered a pipeline flush
|
|
881
|
-
// which was implemented by seeking. See issue #569.
|
|
882
|
-
|
|
883
|
-
// This loop is difficult to test for directly.
|
|
884
|
-
|
|
885
|
-
// A unit test on StreamingEngine would not suffice, since reproduction of
|
|
886
|
-
// the bug would involve making the mock MediaSourceEngine seek on clear.
|
|
887
|
-
// Since the fix was to remove the implicit seek, this behavior would then
|
|
888
|
-
// be removed from the mock, which would render the test useless.
|
|
889
|
-
|
|
890
|
-
// An integration test involving both StreamingEngine and MediaSourcEngine
|
|
891
|
-
// would also be problematic. The bug involved a race, so it would be
|
|
892
|
-
// difficult to reproduce the necessary timing. And if we succeeded, it
|
|
893
|
-
// would be tough to detect that we were definitely in a seek loop, since
|
|
894
|
-
// nothing was mocked.
|
|
895
|
-
|
|
896
|
-
// So the best option seems to be to enforce that clear() does not result
|
|
897
|
-
// in a seek. This can be done here, in a unit test on MediaSourceEngine.
|
|
898
|
-
// It does not reproduce the seek loop, but it does ensure that the test
|
|
899
|
-
// would fail if we ever reintroduced this behavior.
|
|
900
|
-
|
|
901
|
-
const originalTime = 10;
|
|
902
|
-
mockVideo.currentTime = originalTime;
|
|
903
|
-
|
|
904
|
-
mockMediaSource.durationGetter.and.returnValue(20);
|
|
905
|
-
const p = mediaSourceEngine.clear(ContentType.AUDIO);
|
|
906
|
-
audioSourceBuffer.updateend();
|
|
907
|
-
|
|
908
|
-
await p;
|
|
909
|
-
expect(mockVideo.currentTime).toBe(originalTime);
|
|
910
|
-
});
|
|
911
|
-
|
|
912
|
-
it('will forward to TextEngine', async () => {
|
|
913
|
-
expect(mockTextEngine.setTimestampOffset).not.toHaveBeenCalled();
|
|
914
|
-
expect(mockTextEngine.setAppendWindow).not.toHaveBeenCalled();
|
|
915
|
-
await mediaSourceEngine.setStreamProperties(ContentType.TEXT,
|
|
916
|
-
/* timestampOffset= */ 10,
|
|
917
|
-
/* appendWindowStart= */ 0,
|
|
918
|
-
/* appendWindowEnd= */ 20,
|
|
919
|
-
/* sequenceMode= */ false,
|
|
920
|
-
fakeStream,
|
|
921
|
-
/* streamsByType= */ new Map());
|
|
922
|
-
expect(mockTextEngine.setTimestampOffset).toHaveBeenCalledWith(10);
|
|
923
|
-
expect(mockTextEngine.setAppendWindow).toHaveBeenCalledWith(0, 20);
|
|
924
|
-
});
|
|
925
|
-
});
|
|
926
|
-
|
|
927
|
-
describe('endOfStream', () => {
|
|
928
|
-
beforeEach(async () => {
|
|
929
|
-
captureEvents(audioSourceBuffer, ['updateend', 'error']);
|
|
930
|
-
captureEvents(videoSourceBuffer, ['updateend', 'error']);
|
|
931
|
-
const initObject = new Map();
|
|
932
|
-
initObject.set(ContentType.AUDIO, fakeAudioStream);
|
|
933
|
-
initObject.set(ContentType.VIDEO, fakeVideoStream);
|
|
934
|
-
await mediaSourceEngine.init(initObject, false);
|
|
935
|
-
});
|
|
936
|
-
|
|
937
|
-
it('ends the MediaSource stream with the given reason', async () => {
|
|
938
|
-
await mediaSourceEngine.endOfStream('foo');
|
|
939
|
-
expect(mockMediaSource.endOfStream).toHaveBeenCalledWith('foo');
|
|
940
|
-
});
|
|
941
|
-
|
|
942
|
-
it('waits for all previous operations to complete', async () => {
|
|
943
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
944
|
-
const p1 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
|
|
945
|
-
ContentType.AUDIO, buffer, null, fakeStream,
|
|
946
|
-
/* hasClosedCaptions= */ false));
|
|
947
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
948
|
-
const p2 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
|
|
949
|
-
ContentType.VIDEO, buffer, null, fakeStream,
|
|
950
|
-
/* hasClosedCaptions= */ false));
|
|
951
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
952
|
-
const p3 = new shaka.test.StatusPromise(mediaSourceEngine.endOfStream());
|
|
953
|
-
|
|
954
|
-
expect(mockMediaSource.endOfStream).not.toHaveBeenCalled();
|
|
955
|
-
expect(p1.status).toBe('pending');
|
|
956
|
-
expect(p2.status).toBe('pending');
|
|
957
|
-
expect(p3.status).toBe('pending');
|
|
958
|
-
|
|
959
|
-
audioSourceBuffer.updateend();
|
|
960
|
-
await p1;
|
|
961
|
-
expect(p2.status).toBe('pending');
|
|
962
|
-
expect(p3.status).toBe('pending');
|
|
963
|
-
videoSourceBuffer.updateend();
|
|
964
|
-
await p2;
|
|
965
|
-
await p3;
|
|
966
|
-
expect(mockMediaSource.endOfStream).toHaveBeenCalled();
|
|
967
|
-
});
|
|
968
|
-
|
|
969
|
-
it('makes subsequent operations wait', async () => {
|
|
970
|
-
/** @type {!Promise} */
|
|
971
|
-
const p1 = mediaSourceEngine.endOfStream();
|
|
972
|
-
mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null,
|
|
973
|
-
fakeStream, /* hasClosedCaptions= */ false);
|
|
974
|
-
mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer, null,
|
|
975
|
-
fakeStream, /* hasClosedCaptions= */ false);
|
|
976
|
-
mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer2, null,
|
|
977
|
-
fakeStream, /* hasClosedCaptions= */ false);
|
|
978
|
-
|
|
979
|
-
// endOfStream hasn't been called yet because blocking multiple queues
|
|
980
|
-
// takes an extra tick, even when they are empty.
|
|
981
|
-
expect(mockMediaSource.endOfStream).not.toHaveBeenCalled();
|
|
982
|
-
|
|
983
|
-
expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalled();
|
|
984
|
-
expect(videoSourceBuffer.appendBuffer).not.toHaveBeenCalled();
|
|
985
|
-
|
|
986
|
-
await p1;
|
|
987
|
-
expect(mockMediaSource.endOfStream).toHaveBeenCalled();
|
|
988
|
-
// The next operations have already been kicked off.
|
|
989
|
-
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
|
|
990
|
-
expect(videoSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
|
|
991
|
-
// This one is still in queue.
|
|
992
|
-
expect(videoSourceBuffer.appendBuffer).not.toHaveBeenCalledWith(buffer2);
|
|
993
|
-
audioSourceBuffer.updateend();
|
|
994
|
-
videoSourceBuffer.updateend();
|
|
995
|
-
await Promise.resolve();
|
|
996
|
-
expect(videoSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer2);
|
|
997
|
-
videoSourceBuffer.updateend();
|
|
998
|
-
});
|
|
999
|
-
|
|
1000
|
-
it('runs subsequent operations if this operation throws', async () => {
|
|
1001
|
-
mockMediaSource.endOfStream.and.throwError(new Error());
|
|
1002
|
-
/** @type {!Promise} */
|
|
1003
|
-
const p1 = mediaSourceEngine.endOfStream();
|
|
1004
|
-
mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null,
|
|
1005
|
-
fakeStream, /* hasClosedCaptions= */ false);
|
|
1006
|
-
|
|
1007
|
-
expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalled();
|
|
1008
|
-
|
|
1009
|
-
await expectAsync(p1).toBeRejected();
|
|
1010
|
-
expect(mockMediaSource.endOfStream).toHaveBeenCalled();
|
|
1011
|
-
await Util.shortDelay();
|
|
1012
|
-
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(1);
|
|
1013
|
-
audioSourceBuffer.updateend();
|
|
1014
|
-
});
|
|
1015
|
-
});
|
|
1016
|
-
|
|
1017
|
-
describe('setDuration', () => {
|
|
1018
|
-
beforeEach(async () => {
|
|
1019
|
-
mockMediaSource.durationGetter.and.returnValue(0);
|
|
1020
|
-
captureEvents(audioSourceBuffer, ['updateend', 'error']);
|
|
1021
|
-
captureEvents(videoSourceBuffer, ['updateend', 'error']);
|
|
1022
|
-
const initObject = new Map();
|
|
1023
|
-
initObject.set(ContentType.AUDIO, fakeAudioStream);
|
|
1024
|
-
initObject.set(ContentType.VIDEO, fakeVideoStream);
|
|
1025
|
-
await mediaSourceEngine.init(initObject, false);
|
|
1026
|
-
});
|
|
1027
|
-
|
|
1028
|
-
it('sets the given duration', async () => {
|
|
1029
|
-
await mediaSourceEngine.setDuration(100);
|
|
1030
|
-
expect(mockMediaSource.durationSetter).toHaveBeenCalledWith(100);
|
|
1031
|
-
});
|
|
1032
|
-
|
|
1033
|
-
it('waits for all previous operations to complete', async () => {
|
|
1034
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
1035
|
-
const p1 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
|
|
1036
|
-
ContentType.AUDIO, buffer, null, fakeStream,
|
|
1037
|
-
/* hasClosedCaptions= */ false));
|
|
1038
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
1039
|
-
const p2 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
|
|
1040
|
-
ContentType.VIDEO, buffer, null, fakeStream,
|
|
1041
|
-
/* hasClosedCaptions= */ false));
|
|
1042
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
1043
|
-
const p3 =
|
|
1044
|
-
new shaka.test.StatusPromise(mediaSourceEngine.setDuration(100));
|
|
1045
|
-
|
|
1046
|
-
expect(mockMediaSource.durationSetter).not.toHaveBeenCalled();
|
|
1047
|
-
expect(p1.status).toBe('pending');
|
|
1048
|
-
expect(p2.status).toBe('pending');
|
|
1049
|
-
expect(p3.status).toBe('pending');
|
|
1050
|
-
|
|
1051
|
-
audioSourceBuffer.updateend();
|
|
1052
|
-
await p1;
|
|
1053
|
-
expect(p2.status).toBe('pending');
|
|
1054
|
-
expect(p3.status).toBe('pending');
|
|
1055
|
-
videoSourceBuffer.updateend();
|
|
1056
|
-
await p2;
|
|
1057
|
-
await p3;
|
|
1058
|
-
expect(mockMediaSource.durationSetter).toHaveBeenCalledWith(100);
|
|
1059
|
-
});
|
|
1060
|
-
|
|
1061
|
-
it('makes subsequent operations wait', async () => {
|
|
1062
|
-
/** @type {!Promise} */
|
|
1063
|
-
const p1 = mediaSourceEngine.setDuration(100);
|
|
1064
|
-
mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null,
|
|
1065
|
-
fakeStream, /* hasClosedCaptions= */ false);
|
|
1066
|
-
mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer, null,
|
|
1067
|
-
fakeStream, /* hasClosedCaptions= */ false);
|
|
1068
|
-
mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer2, null,
|
|
1069
|
-
fakeStream, /* hasClosedCaptions= */ false);
|
|
1070
|
-
|
|
1071
|
-
// The setter hasn't been called yet because blocking multiple queues
|
|
1072
|
-
// takes an extra tick, even when they are empty.
|
|
1073
|
-
expect(mockMediaSource.durationSetter).not.toHaveBeenCalled();
|
|
1074
|
-
|
|
1075
|
-
expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalled();
|
|
1076
|
-
expect(videoSourceBuffer.appendBuffer).not.toHaveBeenCalled();
|
|
1077
|
-
|
|
1078
|
-
await p1;
|
|
1079
|
-
expect(mockMediaSource.durationSetter).toHaveBeenCalled();
|
|
1080
|
-
// The next operations have already been kicked off.
|
|
1081
|
-
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
|
|
1082
|
-
expect(videoSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
|
|
1083
|
-
// This one is still in queue.
|
|
1084
|
-
expect(videoSourceBuffer.appendBuffer)
|
|
1085
|
-
.not.toHaveBeenCalledWith(buffer2);
|
|
1086
|
-
audioSourceBuffer.updateend();
|
|
1087
|
-
videoSourceBuffer.updateend();
|
|
1088
|
-
await Promise.resolve();
|
|
1089
|
-
expect(videoSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer2);
|
|
1090
|
-
videoSourceBuffer.updateend();
|
|
1091
|
-
});
|
|
1092
|
-
|
|
1093
|
-
it('runs subsequent operations if this operation throws', async () => {
|
|
1094
|
-
mockMediaSource.durationSetter.and.throwError(new Error());
|
|
1095
|
-
/** @type {!Promise} */
|
|
1096
|
-
const p1 = mediaSourceEngine.setDuration(100);
|
|
1097
|
-
mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null,
|
|
1098
|
-
fakeStream, /* hasClosedCaptions= */ false);
|
|
1099
|
-
|
|
1100
|
-
expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalled();
|
|
1101
|
-
|
|
1102
|
-
await expectAsync(p1).toBeRejected();
|
|
1103
|
-
expect(mockMediaSource.durationSetter).toHaveBeenCalled();
|
|
1104
|
-
await Util.shortDelay();
|
|
1105
|
-
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
|
|
1106
|
-
audioSourceBuffer.updateend();
|
|
1107
|
-
});
|
|
1108
|
-
|
|
1109
|
-
it('allows duration to be shrunk', async () => {
|
|
1110
|
-
// Pretend the initial duration was 100.
|
|
1111
|
-
mockMediaSource.durationGetter.and.returnValue(100);
|
|
1112
|
-
|
|
1113
|
-
// When duration is shrunk, 'updateend' events are generated. This is
|
|
1114
|
-
// because reducing the duration triggers the MSE removal algorithm to
|
|
1115
|
-
// run.
|
|
1116
|
-
mockMediaSource.durationSetter.and.callFake((duration) => {
|
|
1117
|
-
expect(duration).toBe(50);
|
|
1118
|
-
videoSourceBuffer.updateend();
|
|
1119
|
-
audioSourceBuffer.updateend();
|
|
1120
|
-
});
|
|
1121
|
-
|
|
1122
|
-
audioSourceBuffer.appendBuffer.and.callFake(() => {
|
|
1123
|
-
audioSourceBuffer.updateend();
|
|
1124
|
-
});
|
|
1125
|
-
videoSourceBuffer.appendBuffer.and.callFake(() => {
|
|
1126
|
-
videoSourceBuffer.updateend();
|
|
1127
|
-
});
|
|
1128
|
-
|
|
1129
|
-
/** @type {!Promise} */
|
|
1130
|
-
const p1 = mediaSourceEngine.setDuration(50);
|
|
1131
|
-
expect(mockMediaSource.durationSetter).not.toHaveBeenCalled();
|
|
1132
|
-
|
|
1133
|
-
// These operations should be blocked until after duration is shrunk.
|
|
1134
|
-
// This is tested because shrinking duration generates 'updateend'
|
|
1135
|
-
// events, and we want to show that the queue still operates correctly.
|
|
1136
|
-
const a1 = mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null,
|
|
1137
|
-
fakeStream, /* hasClosedCaptions= */ false);
|
|
1138
|
-
const a2 = mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer, null,
|
|
1139
|
-
fakeStream, /* hasClosedCaptions= */ false);
|
|
1140
|
-
|
|
1141
|
-
await p1;
|
|
1142
|
-
await a1;
|
|
1143
|
-
await a2;
|
|
1144
|
-
});
|
|
1145
|
-
});
|
|
1146
|
-
|
|
1147
|
-
describe('reload codec switching', () => {
|
|
1148
|
-
beforeEach(
|
|
1149
|
-
/** @suppress {visibility, checkTypes} */
|
|
1150
|
-
() => {
|
|
1151
|
-
mediaSourceEngine.eventManager_.listenOnce =
|
|
1152
|
-
jasmine.createSpy('listener');
|
|
1153
|
-
mediaSourceEngine.eventManager_.listen =
|
|
1154
|
-
jasmine.createSpy('eventListener');
|
|
1155
|
-
});
|
|
1156
|
-
const initObject = new Map();
|
|
1157
|
-
initObject.set(ContentType.VIDEO, fakeVideoStream);
|
|
1158
|
-
initObject.set(ContentType.AUDIO, fakeAudioStream);
|
|
1159
|
-
|
|
1160
|
-
it('should re-create a new MediaSource',
|
|
1161
|
-
/** @suppress {visibility} */ async () => {
|
|
1162
|
-
await mediaSourceEngine.init(initObject, false);
|
|
1163
|
-
mediaSourceEngine.reset_(initObject);
|
|
1164
|
-
expect(createMediaSourceSpy).toHaveBeenCalled();
|
|
1165
|
-
});
|
|
1166
|
-
|
|
1167
|
-
it('should re-create the audio & video source buffers',
|
|
1168
|
-
/** @suppress {invalidCasts, visibility, checkTypes} */ async () => {
|
|
1169
|
-
await mediaSourceEngine.init(initObject, false);
|
|
1170
|
-
mediaSourceEngine.reset_(initObject);
|
|
1171
|
-
expect(mockMediaSource.addSourceBuffer).toHaveBeenCalledTimes(2);
|
|
1172
|
-
});
|
|
1173
|
-
|
|
1174
|
-
it('should persist the previous source buffer parameters',
|
|
1175
|
-
/** @suppress {invalidCasts, visibility, checkTypes} */async () => {
|
|
1176
|
-
await mediaSourceEngine.init(initObject, false);
|
|
1177
|
-
|
|
1178
|
-
audioSourceBuffer.timestampOffset = 10;
|
|
1179
|
-
audioSourceBuffer.appendWindowStart = 5;
|
|
1180
|
-
audioSourceBuffer.appendWindowEnd = 20;
|
|
1181
|
-
|
|
1182
|
-
videoSourceBuffer.timestampOffset = 20;
|
|
1183
|
-
videoSourceBuffer.appendWindowStart = 15;
|
|
1184
|
-
videoSourceBuffer.appendWindowEnd = 30;
|
|
1185
|
-
|
|
1186
|
-
mediaSourceEngine.reset_(initObject);
|
|
1187
|
-
|
|
1188
|
-
expect(audioSourceBuffer.timestampOffset).toBe(10);
|
|
1189
|
-
expect(audioSourceBuffer.appendWindowStart).toBe(5);
|
|
1190
|
-
expect(audioSourceBuffer.appendWindowEnd).toBe(20);
|
|
1191
|
-
|
|
1192
|
-
expect(videoSourceBuffer.timestampOffset).toBe(20);
|
|
1193
|
-
expect(videoSourceBuffer.appendWindowStart).toBe(15);
|
|
1194
|
-
expect(videoSourceBuffer.appendWindowEnd).toBe(30);
|
|
1195
|
-
});
|
|
1196
|
-
|
|
1197
|
-
it('should preserve autoplay state',
|
|
1198
|
-
/** @suppress {invalidCasts, visibility, checkTypes} */
|
|
1199
|
-
async () => {
|
|
1200
|
-
const originalInitSourceBuffer = mediaSourceEngine.initSourceBuffer_;
|
|
1201
|
-
try {
|
|
1202
|
-
await mediaSourceEngine.init(initObject, false);
|
|
1203
|
-
video.autoplay = true;
|
|
1204
|
-
video.paused = true;
|
|
1205
|
-
const playSpy = /** @type {jasmine.Spy} */ (video.play);
|
|
1206
|
-
const addListenOnceSpy =
|
|
1207
|
-
/** @type {jasmine.Spy} */
|
|
1208
|
-
(mediaSourceEngine.eventManager_.listenOnce);
|
|
1209
|
-
const addEventListenerSpy =
|
|
1210
|
-
/** @type {jasmine.Spy} */
|
|
1211
|
-
(mediaSourceEngine.eventManager_.listen);
|
|
1212
|
-
mediaSourceEngine.playbackHasBegun_ = true;
|
|
1213
|
-
mediaSourceEngine.initSourceBuffer_ =
|
|
1214
|
-
jasmine.createSpy('initSourceBuffer');
|
|
1215
|
-
const initSourceBufferSpy =
|
|
1216
|
-
/** @type {jasmine.Spy} */
|
|
1217
|
-
(mediaSourceEngine.initSourceBuffer_);
|
|
1218
|
-
addEventListenerSpy.and.callFake((o, e, c) => {
|
|
1219
|
-
c(); // audio
|
|
1220
|
-
c(); // video
|
|
1221
|
-
});
|
|
1222
|
-
await mediaSourceEngine.reset_(initObject);
|
|
1223
|
-
const callback = addListenOnceSpy.calls.argsFor(0)[2];
|
|
1224
|
-
callback();
|
|
1225
|
-
expect(initSourceBufferSpy).toHaveBeenCalled();
|
|
1226
|
-
expect(addListenOnceSpy.calls.argsFor(0)[1]).toBe('canplay');
|
|
1227
|
-
expect(video.autoplay).toBe(true);
|
|
1228
|
-
expect(playSpy).not.toHaveBeenCalled();
|
|
1229
|
-
} finally {
|
|
1230
|
-
mediaSourceEngine.initSourceBuffer_ = originalInitSourceBuffer;
|
|
1231
|
-
}
|
|
1232
|
-
});
|
|
1233
|
-
|
|
1234
|
-
it('should not set autoplay to false if playback has not begun',
|
|
1235
|
-
/** @suppress {invalidCasts, visibility, checkTypes} */
|
|
1236
|
-
async () => {
|
|
1237
|
-
const originalInitSourceBuffer = mediaSourceEngine.initSourceBuffer_;
|
|
1238
|
-
try {
|
|
1239
|
-
await mediaSourceEngine.init(initObject, false);
|
|
1240
|
-
video.autoplay = true;
|
|
1241
|
-
let setCount = 0;
|
|
1242
|
-
const addEventListenerSpy =
|
|
1243
|
-
/** @type {jasmine.Spy} */
|
|
1244
|
-
(mediaSourceEngine.eventManager_.listen);
|
|
1245
|
-
addEventListenerSpy.and.callFake((o, e, c) => {
|
|
1246
|
-
c(); // audio
|
|
1247
|
-
c(); // video
|
|
1248
|
-
});
|
|
1249
|
-
mediaSourceEngine.initSourceBuffer_ =
|
|
1250
|
-
jasmine.createSpy('initSourceBuffer');
|
|
1251
|
-
Object.defineProperty(video, 'autoplay', {
|
|
1252
|
-
get: () => true,
|
|
1253
|
-
set: () => {
|
|
1254
|
-
setCount++;
|
|
1255
|
-
},
|
|
1256
|
-
});
|
|
1257
|
-
await mediaSourceEngine.reset_(initObject);
|
|
1258
|
-
expect(setCount).toBe(0);
|
|
1259
|
-
} finally {
|
|
1260
|
-
mediaSourceEngine.initSourceBuffer_ = originalInitSourceBuffer;
|
|
1261
|
-
}
|
|
1262
|
-
});
|
|
1263
|
-
|
|
1264
|
-
it('should preserve playing state',
|
|
1265
|
-
/** @suppress {invalidCasts, visibility, checkTypes} */
|
|
1266
|
-
async () => {
|
|
1267
|
-
const originalInitSourceBuffer = mediaSourceEngine.initSourceBuffer_;
|
|
1268
|
-
try {
|
|
1269
|
-
await mediaSourceEngine.init(initObject, false);
|
|
1270
|
-
video.autoplay = false;
|
|
1271
|
-
video.paused = false;
|
|
1272
|
-
const playSpy = /** @type {jasmine.Spy} */ (video.play);
|
|
1273
|
-
const addListenOnceSpy =
|
|
1274
|
-
/** @type {jasmine.Spy} */
|
|
1275
|
-
(mediaSourceEngine.eventManager_.listenOnce);
|
|
1276
|
-
const addEventListenerSpy =
|
|
1277
|
-
/** @type {jasmine.Spy} */
|
|
1278
|
-
(mediaSourceEngine.eventManager_.listen);
|
|
1279
|
-
mediaSourceEngine.playbackHasBegun_ = true;
|
|
1280
|
-
mediaSourceEngine.initSourceBuffer_ =
|
|
1281
|
-
jasmine.createSpy('initSourceBuffer');
|
|
1282
|
-
const initSourceBufferSpy =
|
|
1283
|
-
/** @type {jasmine.Spy} */
|
|
1284
|
-
(mediaSourceEngine.initSourceBuffer_);
|
|
1285
|
-
addEventListenerSpy.and.callFake((o, e, c) => {
|
|
1286
|
-
c(); // audio
|
|
1287
|
-
c(); // video
|
|
1288
|
-
});
|
|
1289
|
-
await mediaSourceEngine.reset_(initObject);
|
|
1290
|
-
const callback = addListenOnceSpy.calls.argsFor(0)[2];
|
|
1291
|
-
callback();
|
|
1292
|
-
expect(initSourceBufferSpy).toHaveBeenCalled();
|
|
1293
|
-
expect(addListenOnceSpy.calls.argsFor(0)[1]).toBe('canplay');
|
|
1294
|
-
expect(video.autoplay).toBe(false);
|
|
1295
|
-
expect(playSpy).toHaveBeenCalled();
|
|
1296
|
-
} finally {
|
|
1297
|
-
mediaSourceEngine.initSourceBuffer_ = originalInitSourceBuffer;
|
|
1298
|
-
}
|
|
1299
|
-
});
|
|
1300
|
-
});
|
|
1301
|
-
|
|
1302
|
-
describe('destroy', () => {
|
|
1303
|
-
beforeEach(async () => {
|
|
1304
|
-
captureEvents(audioSourceBuffer, ['updateend', 'error']);
|
|
1305
|
-
captureEvents(videoSourceBuffer, ['updateend', 'error']);
|
|
1306
|
-
const initObject = new Map();
|
|
1307
|
-
initObject.set(ContentType.AUDIO, fakeAudioStream);
|
|
1308
|
-
initObject.set(ContentType.VIDEO, fakeVideoStream);
|
|
1309
|
-
await mediaSourceEngine.init(initObject, false);
|
|
1310
|
-
});
|
|
1311
|
-
|
|
1312
|
-
it('waits for all operations to complete', async () => {
|
|
1313
|
-
mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null,
|
|
1314
|
-
fakeStream, /* hasClosedCaptions= */ false);
|
|
1315
|
-
mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer, null,
|
|
1316
|
-
fakeStream, /* hasClosedCaptions= */ false);
|
|
1317
|
-
|
|
1318
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
1319
|
-
const d = new shaka.test.StatusPromise(mediaSourceEngine.destroy());
|
|
1320
|
-
|
|
1321
|
-
expect(d.status).toBe('pending');
|
|
1322
|
-
await Util.shortDelay();
|
|
1323
|
-
expect(d.status).toBe('pending');
|
|
1324
|
-
audioSourceBuffer.updateend();
|
|
1325
|
-
await Util.shortDelay();
|
|
1326
|
-
expect(d.status).toBe('pending');
|
|
1327
|
-
videoSourceBuffer.updateend();
|
|
1328
|
-
await d;
|
|
1329
|
-
});
|
|
1330
|
-
|
|
1331
|
-
it('resolves even when a pending operation fails', async () => {
|
|
1332
|
-
const p = mediaSourceEngine.appendBuffer(
|
|
1333
|
-
ContentType.AUDIO, buffer, null, fakeStream,
|
|
1334
|
-
/* hasClosedCaptions= */ false);
|
|
1335
|
-
const d = mediaSourceEngine.destroy();
|
|
1336
|
-
|
|
1337
|
-
audioSourceBuffer.error();
|
|
1338
|
-
audioSourceBuffer.updateend();
|
|
1339
|
-
await expectAsync(p).toBeRejected();
|
|
1340
|
-
await d;
|
|
1341
|
-
});
|
|
1342
|
-
|
|
1343
|
-
it('waits for blocking operations to complete', async () => {
|
|
1344
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
1345
|
-
const p = new shaka.test.StatusPromise(mediaSourceEngine.endOfStream());
|
|
1346
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
1347
|
-
const d = new shaka.test.StatusPromise(mediaSourceEngine.destroy());
|
|
1348
|
-
|
|
1349
|
-
expect(p.status).toBe('pending');
|
|
1350
|
-
expect(d.status).toBe('pending');
|
|
1351
|
-
await p;
|
|
1352
|
-
expect(d.status).toBe('pending');
|
|
1353
|
-
await d;
|
|
1354
|
-
});
|
|
1355
|
-
|
|
1356
|
-
it('cancels operations that have not yet started', async () => {
|
|
1357
|
-
mediaSourceEngine.appendBuffer(
|
|
1358
|
-
ContentType.AUDIO, buffer, null, fakeStream,
|
|
1359
|
-
/* hasClosedCaptions= */ false);
|
|
1360
|
-
const rejected = mediaSourceEngine.appendBuffer(
|
|
1361
|
-
ContentType.AUDIO, buffer2, null, fakeStream,
|
|
1362
|
-
/* hasClosedCaptions= */ false);
|
|
1363
|
-
// Create the expectation first so we don't get unhandled rejection errors
|
|
1364
|
-
const expected = expectAsync(rejected).toBeRejected();
|
|
1365
|
-
|
|
1366
|
-
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
|
|
1367
|
-
expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalledWith(buffer2);
|
|
1368
|
-
|
|
1369
|
-
/** @type {!shaka.test.StatusPromise} */
|
|
1370
|
-
const d = new shaka.test.StatusPromise(mediaSourceEngine.destroy());
|
|
1371
|
-
|
|
1372
|
-
expect(d.status).toBe('pending');
|
|
1373
|
-
await Util.shortDelay();
|
|
1374
|
-
expect(d.status).toBe('pending');
|
|
1375
|
-
await expected;
|
|
1376
|
-
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
|
|
1377
|
-
expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalledWith(buffer2);
|
|
1378
|
-
audioSourceBuffer.updateend();
|
|
1379
|
-
await d;
|
|
1380
|
-
expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalledWith(buffer2);
|
|
1381
|
-
});
|
|
1382
|
-
|
|
1383
|
-
it('cancels blocking operations that have not yet started', async () => {
|
|
1384
|
-
const p1 = mediaSourceEngine.appendBuffer(
|
|
1385
|
-
ContentType.AUDIO, buffer, null, fakeStream,
|
|
1386
|
-
/* hasClosedCaptions= */ false);
|
|
1387
|
-
const p2 = mediaSourceEngine.endOfStream();
|
|
1388
|
-
const d = mediaSourceEngine.destroy();
|
|
1389
|
-
|
|
1390
|
-
audioSourceBuffer.updateend();
|
|
1391
|
-
await expectAsync(p1).toBeResolved();
|
|
1392
|
-
await expectAsync(p2).toBeRejected();
|
|
1393
|
-
await d;
|
|
1394
|
-
});
|
|
1395
|
-
|
|
1396
|
-
it('prevents new operations from being added', async () => {
|
|
1397
|
-
const d = mediaSourceEngine.destroy();
|
|
1398
|
-
await expectAsync(
|
|
1399
|
-
mediaSourceEngine.appendBuffer(
|
|
1400
|
-
ContentType.AUDIO, buffer, null, fakeStream,
|
|
1401
|
-
/* hasClosedCaptions= */ false))
|
|
1402
|
-
.toBeRejected();
|
|
1403
|
-
await d;
|
|
1404
|
-
expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalled();
|
|
1405
|
-
});
|
|
1406
|
-
|
|
1407
|
-
it('destroys text engines', async () => {
|
|
1408
|
-
mediaSourceEngine.reinitText('text/vtt', false, false);
|
|
1409
|
-
|
|
1410
|
-
await mediaSourceEngine.destroy();
|
|
1411
|
-
expect(mockTextEngine).toBeTruthy();
|
|
1412
|
-
expect(mockTextEngine.destroy).toHaveBeenCalled();
|
|
1413
|
-
});
|
|
1414
|
-
|
|
1415
|
-
// Regression test for https://github.com/shaka-project/shaka-player/issues/984
|
|
1416
|
-
it('destroys TextDisplayer on destroy', async () => {
|
|
1417
|
-
await mediaSourceEngine.destroy();
|
|
1418
|
-
expect(mockTextDisplayer.destroySpy).toHaveBeenCalled();
|
|
1419
|
-
});
|
|
1420
|
-
});
|
|
1421
|
-
|
|
1422
|
-
function createMockMediaSource() {
|
|
1423
|
-
const mediaSource = {
|
|
1424
|
-
readyState: 'open',
|
|
1425
|
-
sourceBuffers: document.createElement('div'),
|
|
1426
|
-
addSourceBuffer: jasmine.createSpy('addSourceBuffer'),
|
|
1427
|
-
endOfStream: jasmine.createSpy('endOfStream'),
|
|
1428
|
-
durationGetter: jasmine.createSpy('duration getter'),
|
|
1429
|
-
durationSetter: jasmine.createSpy('duration setter'),
|
|
1430
|
-
addEventListener: jasmine.createSpy('addEventListener'),
|
|
1431
|
-
removeEventListener: () => {},
|
|
1432
|
-
};
|
|
1433
|
-
Object.defineProperty(mediaSource, 'duration', {
|
|
1434
|
-
get: Util.spyFunc(mediaSource.durationGetter),
|
|
1435
|
-
set: Util.spyFunc(mediaSource.durationSetter),
|
|
1436
|
-
});
|
|
1437
|
-
return mediaSource;
|
|
1438
|
-
}
|
|
1439
|
-
|
|
1440
|
-
/** @return {MockSourceBuffer} */
|
|
1441
|
-
function createMockSourceBuffer() {
|
|
1442
|
-
return {
|
|
1443
|
-
abort: jasmine.createSpy('abort'),
|
|
1444
|
-
appendBuffer: jasmine.createSpy('appendBuffer'),
|
|
1445
|
-
remove: jasmine.createSpy('remove'),
|
|
1446
|
-
updating: false,
|
|
1447
|
-
addEventListener: jasmine.createSpy('addEventListener'),
|
|
1448
|
-
removeEventListener: jasmine.createSpy('removeEventListener'),
|
|
1449
|
-
buffered: {
|
|
1450
|
-
length: 0,
|
|
1451
|
-
start: jasmine.createSpy('buffered.start'),
|
|
1452
|
-
end: jasmine.createSpy('buffered.end'),
|
|
1453
|
-
},
|
|
1454
|
-
timestampOffset: 0,
|
|
1455
|
-
appendWindowEnd: Infinity,
|
|
1456
|
-
updateend: () => {},
|
|
1457
|
-
error: () => {},
|
|
1458
|
-
mode: 'segments',
|
|
1459
|
-
};
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
function createMockTextEngineCtor() {
|
|
1463
|
-
const ctor = jasmine.createSpy('TextEngine');
|
|
1464
|
-
ctor['isTypeSupported'] = () => true;
|
|
1465
|
-
// Because this is a fake constructor, it must be callable with "new".
|
|
1466
|
-
// This will cause jasmine to invoke the callback with "new" as well, so
|
|
1467
|
-
// the callback must be a "function". This detail is hidden when babel
|
|
1468
|
-
// transpiles the tests.
|
|
1469
|
-
// eslint-disable-next-line prefer-arrow-callback, no-restricted-syntax
|
|
1470
|
-
ctor.and.callFake(function() {
|
|
1471
|
-
expect(mockTextEngine).toBeFalsy();
|
|
1472
|
-
mockTextEngine = jasmine.createSpyObj('TextEngine', [
|
|
1473
|
-
'initParser', 'destroy', 'appendBuffer', 'remove', 'setTimestampOffset',
|
|
1474
|
-
'setAppendWindow', 'bufferStart', 'bufferEnd', 'bufferedAheadOf',
|
|
1475
|
-
'storeAndAppendClosedCaptions',
|
|
1476
|
-
]);
|
|
1477
|
-
|
|
1478
|
-
const resolve = () => Promise.resolve();
|
|
1479
|
-
mockTextEngine.destroy.and.callFake(resolve);
|
|
1480
|
-
mockTextEngine.appendBuffer.and.callFake(resolve);
|
|
1481
|
-
mockTextEngine.remove.and.callFake(resolve);
|
|
1482
|
-
return mockTextEngine;
|
|
1483
|
-
});
|
|
1484
|
-
return ctor;
|
|
1485
|
-
}
|
|
1486
|
-
|
|
1487
|
-
function captureEvents(object, targetEventNames) {
|
|
1488
|
-
object.addEventListener.and.callFake((eventName, listener) => {
|
|
1489
|
-
if (targetEventNames.includes(eventName)) {
|
|
1490
|
-
object[eventName] = listener;
|
|
1491
|
-
}
|
|
1492
|
-
});
|
|
1493
|
-
object.removeEventListener.and.callFake((eventName, listener) => {
|
|
1494
|
-
if (targetEventNames.includes(eventName)) {
|
|
1495
|
-
expect(object[eventName]).toBe(listener);
|
|
1496
|
-
object[eventName] = null;
|
|
1497
|
-
}
|
|
1498
|
-
});
|
|
1499
|
-
}
|
|
1500
|
-
|
|
1501
|
-
function dummyReference(startTime, endTime) {
|
|
1502
|
-
return new shaka.media.SegmentReference(
|
|
1503
|
-
startTime, endTime,
|
|
1504
|
-
/* uris= */ () => ['foo://bar'],
|
|
1505
|
-
/* startByte= */ 0,
|
|
1506
|
-
/* endByte= */ null,
|
|
1507
|
-
/* initSegmentReference= */ null,
|
|
1508
|
-
/* timestampOffset= */ 0,
|
|
1509
|
-
/* appendWindowStart= */ 0,
|
|
1510
|
-
/* appendWindowEnd= */ Infinity);
|
|
1511
|
-
}
|
|
1512
|
-
});
|