@amazon/vinyl 3.0.1 → 3.2.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/dist/index.cjs +1103 -1042
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +810 -739
- package/dist/index.js.map +4 -4
- package/dist/test/benchmark.js.map +2 -2
- package/dist/test/integ.es5.js +394 -388
- package/dist/test/integ.js +2626 -2032
- package/dist/test/integ.js.map +4 -4
- package/dist/test/jasmine.css +50 -3
- package/dist/test/jasmineHtml.es5.js +195 -155
- package/dist/test/jasmineHtml.js +2938 -1833
- package/dist/test/jasmineHtml.js.map +3 -3
- package/dist/types/current/ad/AdControllerImpl.d.ts.map +1 -1
- package/dist/types/current/ad/AdControllerImpl.js +4 -1
- package/dist/types/current/ad/discoverHlsInterstitials.d.ts.map +1 -1
- package/dist/types/current/ad/discoverHlsInterstitials.js +2 -2
- package/dist/types/current/drm/DrmControllerImpl.d.ts +19 -1
- package/dist/types/current/drm/DrmControllerImpl.d.ts.map +1 -1
- package/dist/types/current/drm/DrmControllerImpl.js +101 -58
- package/dist/types/current/drm/licenseProvider/defaultLicenseProvider.d.ts.map +1 -1
- package/dist/types/current/drm/licenseProvider/defaultLicenseProvider.js +2 -2
- package/dist/types/current/index.d.ts +3 -6
- package/dist/types/current/index.d.ts.map +1 -1
- package/dist/types/current/index.js +3 -6
- package/dist/types/current/playback/PlaybackControllerImpl.d.ts +15 -0
- package/dist/types/current/playback/PlaybackControllerImpl.d.ts.map +1 -1
- package/dist/types/current/playback/PlaybackControllerImpl.js +100 -9
- package/dist/types/current/playback/ReadonlyPlaybackController.d.ts +27 -1
- package/dist/types/current/playback/ReadonlyPlaybackController.d.ts.map +1 -1
- package/dist/types/current/playback/ReadonlyPlaybackController.js +16 -0
- package/dist/types/current/playback/createStallDetector.d.ts +43 -0
- package/dist/types/current/playback/createStallDetector.d.ts.map +1 -0
- package/dist/types/current/playback/createStallDetector.js +101 -0
- package/dist/types/current/playback/createStallMonitor.d.ts +45 -0
- package/dist/types/current/playback/createStallMonitor.d.ts.map +1 -0
- package/dist/types/current/playback/createStallMonitor.js +94 -0
- package/dist/types/current/streaming/AudioOptions.d.ts +27 -0
- package/dist/types/current/streaming/AudioOptions.d.ts.map +1 -1
- package/dist/types/current/streaming/AudioOptions.js +5 -1
- package/dist/types/current/streaming/ContentStream.d.ts +0 -5
- package/dist/types/current/streaming/ContentStream.d.ts.map +1 -1
- package/dist/types/current/streaming/ContentStream.js +0 -4
- package/dist/types/current/streaming/LoadMetric.d.ts +15 -1
- package/dist/types/current/streaming/LoadMetric.d.ts.map +1 -1
- package/dist/types/current/streaming/StreamingEventMap.d.ts +1 -5
- package/dist/types/current/streaming/StreamingEventMap.d.ts.map +1 -1
- package/dist/types/current/streaming/StreamingEventMap.js +0 -1
- package/dist/types/current/streaming/buffering/BufferingController.d.ts +0 -11
- package/dist/types/current/streaming/buffering/BufferingController.d.ts.map +1 -1
- package/dist/types/current/streaming/buffering/BufferingController.js +0 -13
- package/dist/types/current/streaming/createDefaultMediaTimelineTransformer.d.ts.map +1 -1
- package/dist/types/current/streaming/createDefaultMediaTimelineTransformer.js +13 -4
- package/dist/types/current/streaming/mediaTimelineFilter.d.ts +4 -2
- package/dist/types/current/streaming/mediaTimelineFilter.d.ts.map +1 -1
- package/dist/types/current/streaming/mediaTimelineFilter.js +23 -8
- package/dist/types/current/text/SidecarTextTrackLoader.d.ts.map +1 -1
- package/dist/types/current/text/SidecarTextTrackLoader.js +4 -4
- package/dist/types/current/text/TextTrack.d.ts.map +1 -1
- package/dist/types/current/track/TrackController.d.ts.map +1 -1
- package/dist/types/current/track/TrackController.js +8 -2
- package/dist/types/current/track/createSegmentDataProvider.d.ts.map +1 -1
- package/dist/types/current/track/createSegmentDataProvider.js +6 -3
- package/dist/types/current/track/dash/DashMediaQualityMetadataResolver.d.ts +10 -6
- package/dist/types/current/track/dash/DashMediaQualityMetadataResolver.d.ts.map +1 -1
- package/dist/types/current/track/dash/DashMediaQualityMetadataResolver.js +13 -8
- package/dist/types/current/track/dash/createDashFactories.d.ts +1 -1
- package/dist/types/current/track/dash/timeline/DashRepresentationSegmentProvider.d.ts.map +1 -1
- package/dist/types/current/track/dash/timeline/DashRepresentationSegmentProvider.js +71 -3
- package/dist/types/current/track/dash/urlDashManifestProvider.js +2 -2
- package/dist/types/current/track/dash/util/mpd.d.ts +6 -0
- package/dist/types/current/track/dash/util/mpd.d.ts.map +1 -1
- package/dist/types/current/track/dash/util/mpd.js +24 -3
- package/dist/types/current/track/filters/resourceTypeFilter.d.ts +4 -0
- package/dist/types/current/track/filters/resourceTypeFilter.d.ts.map +1 -1
- package/dist/types/current/track/filters/resourceTypeFilter.js +4 -0
- package/dist/types/current/track/filters/sampleRateFilter.d.ts +25 -7
- package/dist/types/current/track/filters/sampleRateFilter.d.ts.map +1 -1
- package/dist/types/current/track/filters/sampleRateFilter.js +52 -24
- package/dist/types/current/track/hls/createHlsFactories.d.ts +1 -1
- package/dist/types/current/track/hls/createUrlHlsManifestProvider.d.ts.map +1 -1
- package/dist/types/current/track/hls/createUrlHlsManifestProvider.js +3 -3
- package/dist/types/current/track/mse/MseTrack.d.ts +0 -17
- package/dist/types/current/track/mse/MseTrack.d.ts.map +1 -1
- package/dist/types/current/track/mse/MseTrack.js +1 -45
- package/dist/types/current/tsconfig.tsbuildinfo +1 -1
- package/dist/types/current/util/media/mimeType.d.ts +8 -0
- package/dist/types/current/util/media/mimeType.d.ts.map +1 -0
- package/dist/types/current/util/media/mimeType.js +13 -0
- package/dist/types/current/vinyl/VinylPlayer.d.ts +5 -0
- package/dist/types/current/vinyl/VinylPlayer.d.ts.map +1 -1
- package/dist/types/current/vinyl/VinylPlayer.js +23 -1
- package/dist/types/current/vinyl/createVinylFactories.d.ts +7 -16
- package/dist/types/current/vinyl/createVinylFactories.d.ts.map +1 -1
- package/dist/types/current/vinyl/createVinylFactories.js +2 -12
- package/dist/types/ts3.7/drm/DrmControllerImpl.d.ts +19 -1
- package/dist/types/ts3.7/index.d.ts +3 -6
- package/dist/types/ts3.7/playback/PlaybackControllerImpl.d.ts +15 -0
- package/dist/types/ts3.7/playback/ReadonlyPlaybackController.d.ts +28 -0
- package/dist/types/ts3.7/playback/createStallDetector.d.ts +43 -0
- package/dist/types/ts3.7/playback/createStallMonitor.d.ts +45 -0
- package/dist/types/ts3.7/streaming/AudioOptions.d.ts +27 -0
- package/dist/types/ts3.7/streaming/ContentStream.d.ts +0 -5
- package/dist/types/ts3.7/streaming/LoadMetric.d.ts +15 -1
- package/dist/types/ts3.7/streaming/StreamingEventMap.d.ts +0 -5
- package/dist/types/ts3.7/streaming/buffering/BufferingController.d.ts +0 -11
- package/dist/types/ts3.7/streaming/mediaTimelineFilter.d.ts +4 -2
- package/dist/types/ts3.7/track/dash/DashMediaQualityMetadataResolver.d.ts +10 -6
- package/dist/types/ts3.7/track/dash/createDashFactories.d.ts +1 -1
- package/dist/types/ts3.7/track/dash/util/mpd.d.ts +6 -0
- package/dist/types/ts3.7/track/filters/resourceTypeFilter.d.ts +4 -0
- package/dist/types/ts3.7/track/filters/sampleRateFilter.d.ts +25 -7
- package/dist/types/ts3.7/track/hls/createHlsFactories.d.ts +1 -1
- package/dist/types/ts3.7/track/mse/MseTrack.d.ts +0 -17
- package/dist/types/ts3.7/util/media/mimeType.d.ts +8 -0
- package/dist/types/ts3.7/vinyl/VinylPlayer.d.ts +5 -0
- package/dist/types/ts3.7/vinyl/createVinylFactories.d.ts +7 -16
- package/dist/types/ts4.1/drm/DrmControllerImpl.d.ts +19 -1
- package/dist/types/ts4.1/index.d.ts +3 -6
- package/dist/types/ts4.1/playback/PlaybackControllerImpl.d.ts +15 -0
- package/dist/types/ts4.1/playback/ReadonlyPlaybackController.d.ts +28 -0
- package/dist/types/ts4.1/playback/createStallDetector.d.ts +43 -0
- package/dist/types/ts4.1/playback/createStallMonitor.d.ts +45 -0
- package/dist/types/ts4.1/streaming/AudioOptions.d.ts +27 -0
- package/dist/types/ts4.1/streaming/ContentStream.d.ts +0 -5
- package/dist/types/ts4.1/streaming/LoadMetric.d.ts +15 -1
- package/dist/types/ts4.1/streaming/StreamingEventMap.d.ts +0 -5
- package/dist/types/ts4.1/streaming/buffering/BufferingController.d.ts +0 -11
- package/dist/types/ts4.1/streaming/mediaTimelineFilter.d.ts +4 -2
- package/dist/types/ts4.1/track/dash/DashMediaQualityMetadataResolver.d.ts +10 -6
- package/dist/types/ts4.1/track/dash/createDashFactories.d.ts +1 -1
- package/dist/types/ts4.1/track/dash/util/mpd.d.ts +6 -0
- package/dist/types/ts4.1/track/filters/resourceTypeFilter.d.ts +4 -0
- package/dist/types/ts4.1/track/filters/sampleRateFilter.d.ts +25 -7
- package/dist/types/ts4.1/track/hls/createHlsFactories.d.ts +1 -1
- package/dist/types/ts4.1/track/mse/MseTrack.d.ts +0 -17
- package/dist/types/ts4.1/util/media/mimeType.d.ts +8 -0
- package/dist/types/ts4.1/vinyl/VinylPlayer.d.ts +5 -0
- package/dist/types/ts4.1/vinyl/createVinylFactories.d.ts +7 -16
- package/dist/types/ts4.7/drm/DrmControllerImpl.d.ts +19 -1
- package/dist/types/ts4.7/index.d.ts +3 -6
- package/dist/types/ts4.7/playback/PlaybackControllerImpl.d.ts +15 -0
- package/dist/types/ts4.7/playback/ReadonlyPlaybackController.d.ts +28 -0
- package/dist/types/ts4.7/playback/createStallDetector.d.ts +43 -0
- package/dist/types/ts4.7/playback/createStallMonitor.d.ts +45 -0
- package/dist/types/ts4.7/streaming/AudioOptions.d.ts +27 -0
- package/dist/types/ts4.7/streaming/ContentStream.d.ts +0 -5
- package/dist/types/ts4.7/streaming/LoadMetric.d.ts +15 -1
- package/dist/types/ts4.7/streaming/StreamingEventMap.d.ts +0 -5
- package/dist/types/ts4.7/streaming/buffering/BufferingController.d.ts +0 -11
- package/dist/types/ts4.7/streaming/mediaTimelineFilter.d.ts +4 -2
- package/dist/types/ts4.7/track/dash/DashMediaQualityMetadataResolver.d.ts +10 -6
- package/dist/types/ts4.7/track/dash/createDashFactories.d.ts +1 -1
- package/dist/types/ts4.7/track/dash/util/mpd.d.ts +6 -0
- package/dist/types/ts4.7/track/filters/resourceTypeFilter.d.ts +4 -0
- package/dist/types/ts4.7/track/filters/sampleRateFilter.d.ts +25 -7
- package/dist/types/ts4.7/track/hls/createHlsFactories.d.ts +1 -1
- package/dist/types/ts4.7/track/mse/MseTrack.d.ts +0 -17
- package/dist/types/ts4.7/util/media/mimeType.d.ts +8 -0
- package/dist/types/ts4.7/vinyl/VinylPlayer.d.ts +5 -0
- package/dist/types/ts4.7/vinyl/createVinylFactories.d.ts +7 -16
- package/dist/unit/types/current/ad/AdControllerImpl.test.js +27 -1
- package/dist/unit/types/current/drm/DrmControllerImpl.test.js +146 -9
- package/dist/unit/types/current/index.d.ts +3 -4
- package/dist/unit/types/current/index.d.ts.map +1 -1
- package/dist/unit/types/current/index.js +3 -4
- package/dist/unit/types/current/playback/PlaybackControllerImpl.test.js +165 -0
- package/dist/unit/types/current/playback/createStallDetector.test.d.ts +2 -0
- package/dist/unit/types/current/playback/createStallDetector.test.d.ts.map +1 -0
- package/dist/unit/types/current/playback/createStallDetector.test.js +163 -0
- package/dist/unit/types/current/playback/createStallMonitor.test.d.ts +2 -0
- package/dist/unit/types/current/playback/createStallMonitor.test.d.ts.map +1 -0
- package/dist/unit/types/current/playback/createStallMonitor.test.js +125 -0
- package/dist/unit/types/current/streaming/ContentStream.test.js +0 -11
- package/dist/unit/types/current/streaming/MseTrack.test.js +49 -73
- package/dist/unit/types/current/streaming/abr/QualitySelectorImpl.test.js +1 -1
- package/dist/unit/types/current/streaming/buffering/BufferingController.test.js +0 -21
- package/dist/unit/types/current/streaming/createDefaultMediaTimelineTransformer.test.js +133 -0
- package/dist/unit/types/current/streaming/mediaTimelineFilter.test.js +60 -54
- package/dist/unit/types/current/track/TrackController.test.js +61 -1
- package/dist/unit/types/current/track/createSegmentDataProvider.test.js +18 -0
- package/dist/unit/types/current/track/dash/DashMediaQualityMetadataResolver.test.js +46 -14
- package/dist/unit/types/current/track/dash/createSegmentControllerFactory.test.js +1 -0
- package/dist/unit/types/current/track/dash/timeline/DashRepresentationSegmentProvider.test.js +114 -0
- package/dist/unit/types/current/track/dash/util/mpd.test.js +37 -0
- package/dist/unit/types/current/track/filters/resourceTypeFilter.test.js +7 -0
- package/dist/unit/types/current/track/filters/sampleRateFilter.test.js +211 -34
- package/dist/unit/types/current/track/source/SourceObjectTrack.test.js +1 -1
- package/dist/unit/types/current/tsconfig.tsbuildinfo +1 -1
- package/dist/unit/types/current/util/media/mimeType.test.d.ts +2 -0
- package/dist/unit/types/current/util/media/mimeType.test.d.ts.map +1 -0
- package/dist/unit/types/current/util/media/mimeType.test.js +22 -0
- package/dist/unit/types/current/vinyl/VinylPlayer.test.js +33 -2
- package/dist/unit/types/current/vinyl/createVinylFactories.test.js +2 -36
- package/dist/vinylTestUtil/index.js +6 -4
- package/dist/vinylTestUtil/index.js.map +2 -2
- package/dist/vinylTestUtil/types/current/assets/vinylTestAssets.d.ts +1 -0
- package/dist/vinylTestUtil/types/current/assets/vinylTestAssets.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/assets/vinylTestAssets.js +1 -0
- package/dist/vinylTestUtil/types/current/media/FrequencyAnalyzer.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/mock/streaming/MockContentStream.d.ts +0 -1
- package/dist/vinylTestUtil/types/current/mock/streaming/MockContentStream.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/mock/streaming/MockContentStream.js +0 -1
- package/dist/vinylTestUtil/types/current/mock/streaming/buffering/MockBufferingController.d.ts +0 -1
- package/dist/vinylTestUtil/types/current/mock/streaming/buffering/MockBufferingController.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/mock/streaming/buffering/MockBufferingController.js +0 -1
- package/dist/vinylTestUtil/types/current/tsconfig.tsbuildinfo +1 -1
- package/dist/vinylTestUtil/types/current/util/playback/eventPromises.js +1 -1
- package/dist/vinylTestUtil/types/current/util/playback/expectTrackCanSeek.d.ts.map +1 -1
- package/dist/vinylTestUtil/types/current/util/playback/expectTrackCanSeek.js +9 -1
- package/dist/vinylTestUtil/types/ts3.7/assets/vinylTestAssets.d.ts +1 -0
- package/dist/vinylTestUtil/types/ts3.7/mock/streaming/MockContentStream.d.ts +0 -1
- package/dist/vinylTestUtil/types/ts3.7/mock/streaming/buffering/MockBufferingController.d.ts +0 -1
- package/dist/vinylTestUtil/types/ts4.1/assets/vinylTestAssets.d.ts +1 -0
- package/dist/vinylTestUtil/types/ts4.1/mock/streaming/MockContentStream.d.ts +0 -1
- package/dist/vinylTestUtil/types/ts4.1/mock/streaming/buffering/MockBufferingController.d.ts +0 -1
- package/dist/vinylTestUtil/types/ts4.7/assets/vinylTestAssets.d.ts +1 -0
- package/dist/vinylTestUtil/types/ts4.7/mock/streaming/MockContentStream.d.ts +0 -1
- package/dist/vinylTestUtil/types/ts4.7/mock/streaming/buffering/MockBufferingController.d.ts +0 -1
- package/docs/USAGE.md +29 -0
- package/package.json +11 -10
- package/dist/types/current/patch/media/HtmlMediaElementPatch.d.ts +0 -3
- package/dist/types/current/patch/media/HtmlMediaElementPatch.d.ts.map +0 -1
- package/dist/types/current/patch/media/HtmlMediaElementPatch.js +0 -5
- package/dist/types/current/patch/media/MediaElementPatchOptions.d.ts +0 -19
- package/dist/types/current/patch/media/MediaElementPatchOptions.d.ts.map +0 -1
- package/dist/types/current/patch/media/MediaElementPatchOptions.js +0 -16
- package/dist/types/current/patch/media/patchMediaElement.d.ts +0 -9
- package/dist/types/current/patch/media/patchMediaElement.d.ts.map +0 -1
- package/dist/types/current/patch/media/patchMediaElement.js +0 -15
- package/dist/types/current/patch/media/patches/createPreventStallsPatch.d.ts +0 -17
- package/dist/types/current/patch/media/patches/createPreventStallsPatch.d.ts.map +0 -1
- package/dist/types/current/patch/media/patches/createPreventStallsPatch.js +0 -55
- package/dist/types/current/patch/media/patches/createUnreliablePlaybackEventsPatch.d.ts +0 -26
- package/dist/types/current/patch/media/patches/createUnreliablePlaybackEventsPatch.d.ts.map +0 -1
- package/dist/types/current/patch/media/patches/createUnreliablePlaybackEventsPatch.js +0 -218
- package/dist/types/current/vinyl/VinylPatchOptions.d.ts +0 -10
- package/dist/types/current/vinyl/VinylPatchOptions.d.ts.map +0 -1
- package/dist/types/current/vinyl/VinylPatchOptions.js +0 -11
- package/dist/types/ts3.7/patch/media/HtmlMediaElementPatch.d.ts +0 -3
- package/dist/types/ts3.7/patch/media/MediaElementPatchOptions.d.ts +0 -19
- package/dist/types/ts3.7/patch/media/patchMediaElement.d.ts +0 -9
- package/dist/types/ts3.7/patch/media/patches/createPreventStallsPatch.d.ts +0 -17
- package/dist/types/ts3.7/patch/media/patches/createUnreliablePlaybackEventsPatch.d.ts +0 -26
- package/dist/types/ts3.7/vinyl/VinylPatchOptions.d.ts +0 -10
- package/dist/types/ts4.1/patch/media/HtmlMediaElementPatch.d.ts +0 -3
- package/dist/types/ts4.1/patch/media/MediaElementPatchOptions.d.ts +0 -19
- package/dist/types/ts4.1/patch/media/patchMediaElement.d.ts +0 -9
- package/dist/types/ts4.1/patch/media/patches/createPreventStallsPatch.d.ts +0 -17
- package/dist/types/ts4.1/patch/media/patches/createUnreliablePlaybackEventsPatch.d.ts +0 -26
- package/dist/types/ts4.1/vinyl/VinylPatchOptions.d.ts +0 -10
- package/dist/types/ts4.7/patch/media/HtmlMediaElementPatch.d.ts +0 -3
- package/dist/types/ts4.7/patch/media/MediaElementPatchOptions.d.ts +0 -19
- package/dist/types/ts4.7/patch/media/patchMediaElement.d.ts +0 -9
- package/dist/types/ts4.7/patch/media/patches/createPreventStallsPatch.d.ts +0 -17
- package/dist/types/ts4.7/patch/media/patches/createUnreliablePlaybackEventsPatch.d.ts +0 -26
- package/dist/types/ts4.7/vinyl/VinylPatchOptions.d.ts +0 -10
- package/dist/unit/types/current/patch/media/MediaElementPatchOptions.test.d.ts +0 -2
- package/dist/unit/types/current/patch/media/MediaElementPatchOptions.test.d.ts.map +0 -1
- package/dist/unit/types/current/patch/media/MediaElementPatchOptions.test.js +0 -17
- package/dist/unit/types/current/patch/media/patchMediaElement.test.d.ts +0 -2
- package/dist/unit/types/current/patch/media/patchMediaElement.test.d.ts.map +0 -1
- package/dist/unit/types/current/patch/media/patchMediaElement.test.js +0 -36
- package/dist/unit/types/current/patch/media/patches/createPreventStallsPatch.test.d.ts +0 -2
- package/dist/unit/types/current/patch/media/patches/createPreventStallsPatch.test.d.ts.map +0 -1
- package/dist/unit/types/current/patch/media/patches/createPreventStallsPatch.test.js +0 -93
- package/dist/unit/types/current/patch/media/patches/createUnreliablePlaybackEventsPatch.test.d.ts +0 -2
- package/dist/unit/types/current/patch/media/patches/createUnreliablePlaybackEventsPatch.test.d.ts.map +0 -1
- package/dist/unit/types/current/patch/media/patches/createUnreliablePlaybackEventsPatch.test.js +0 -423
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
|
-
import { Abort, AbortError, Browser, clamp, closeTo, createDisposer, createTimeRangesReader, DomEventHost, ErrorLevel, ErrorOrigin, EventHostImpl,
|
|
5
|
+
import { Abort, AbortError, Browser, clamp, closeTo, createDisposer, createTimeRangesReader, DomEventHost, ErrorLevel, ErrorOrigin, EventHostImpl, hasBrowser, logDebug, logInfo, logWarn, nextEventAsPromise, noop, onAny, ownKeys, TimeoutSlot, toLowerCase, withAbort, withTimeout, } from '@amazon/vinyl-util';
|
|
6
6
|
import { LIVE_DURATION } from './PlaybackController';
|
|
7
7
|
import { PlaybackReadyState, playedReasons, waitedReasons, } from './ReadonlyPlaybackController';
|
|
8
|
+
import { createStallDetector } from './createStallDetector';
|
|
9
|
+
import { createStallMonitor } from './createStallMonitor';
|
|
8
10
|
import { InvalidSeekError } from './error/InvalidSeekError';
|
|
9
11
|
import { playbackStateLoggingHandler } from './logging/playbackStateLoggingHandler';
|
|
10
12
|
import { createChangeEventTrigger } from '../event/ChangeEvent';
|
|
@@ -15,6 +17,7 @@ export const defaultPlaybackControllerImplOptions = {
|
|
|
15
17
|
minSeekableBuffer: -1, // Default comes from getMinSeekableBufferDefault()
|
|
16
18
|
playTimeout: 30,
|
|
17
19
|
seekTimeout: 30,
|
|
20
|
+
stallThreshold: 1,
|
|
18
21
|
};
|
|
19
22
|
/**
|
|
20
23
|
* Safari and Edge Legacy cannot reliably seek close to the end of a seekable range.
|
|
@@ -24,6 +27,19 @@ export const defaultPlaybackControllerImplOptions = {
|
|
|
24
27
|
export function getMinSeekableBufferDefault() {
|
|
25
28
|
return !hasBrowser(Browser.CHROMIUM) ? 5 : 2;
|
|
26
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Re-issue a seek up to this many times when it lands outside tolerance of the target (some WebKit
|
|
32
|
+
* versions report `seeked` at the pre-seek position) or when it never completes (see
|
|
33
|
+
* {@link SEEK_ATTEMPT_TIMEOUT}).
|
|
34
|
+
*/
|
|
35
|
+
const SEEK_RETRY_LIMIT = 2;
|
|
36
|
+
/**
|
|
37
|
+
* Seconds to wait for `seeked` before treating a seek attempt as stuck. WebKit can leave a seek on
|
|
38
|
+
* a freshly-attached, never-decoded source pending forever (observed on iOS Safari); on a stuck
|
|
39
|
+
* attempt the decoder is warmed with play() and the seek re-issued. Comfortably above a normal
|
|
40
|
+
* seek's completion time so a slow-but-valid seek isn't cut short.
|
|
41
|
+
*/
|
|
42
|
+
const SEEK_ATTEMPT_TIMEOUT = 4;
|
|
27
43
|
export class PlaybackControllerImpl extends EventHostImpl {
|
|
28
44
|
loudnessNormalizationController;
|
|
29
45
|
get [Symbol.toStringTag]() {
|
|
@@ -38,6 +54,8 @@ export class PlaybackControllerImpl extends EventHostImpl {
|
|
|
38
54
|
_waiting = false;
|
|
39
55
|
// Monotonic counter identifying the most recent seekTo call.
|
|
40
56
|
_seekId = 0;
|
|
57
|
+
// Stops the stall detector started after the last seek, if any.
|
|
58
|
+
stopStallDetection = null;
|
|
41
59
|
// default user volume (0.0 to 1.0 scale)
|
|
42
60
|
userVolume = 1.0;
|
|
43
61
|
_pendingPlay = null;
|
|
@@ -59,6 +77,7 @@ export class PlaybackControllerImpl extends EventHostImpl {
|
|
|
59
77
|
add(() => {
|
|
60
78
|
this.disposeAbort.abort();
|
|
61
79
|
this.disposeAbort.dispose();
|
|
80
|
+
this.stopStallDetection?.();
|
|
62
81
|
});
|
|
63
82
|
this.domEvents = add(new DomEventHost(deps.media));
|
|
64
83
|
// Log media events.
|
|
@@ -78,6 +97,12 @@ export class PlaybackControllerImpl extends EventHostImpl {
|
|
|
78
97
|
});
|
|
79
98
|
});
|
|
80
99
|
this.initializeEvents();
|
|
100
|
+
// Detect and report play-head stalls (frozen while playing) via
|
|
101
|
+
// stallEntered/stallEnded, independent of the recovery stall detector.
|
|
102
|
+
add(createStallMonitor(this, {
|
|
103
|
+
onStallEntered: () => this.dispatch('stallEntered', {}),
|
|
104
|
+
onStallEnded: (info) => this.dispatch('stallEnded', info),
|
|
105
|
+
}, { stallThreshold: this.options.stallThreshold }));
|
|
81
106
|
// Listen for loudness normalization changes
|
|
82
107
|
add(this.loudnessNormalizationController.on('change', () => {
|
|
83
108
|
this.refreshVolume();
|
|
@@ -278,7 +303,7 @@ export class PlaybackControllerImpl extends EventHostImpl {
|
|
|
278
303
|
!this.paused &&
|
|
279
304
|
this.currentTime <= this.options.loopTimeAffordance &&
|
|
280
305
|
lastTime >= this.duration - this.options.loopTimeAffordance) {
|
|
281
|
-
if (
|
|
306
|
+
if (hasBrowser(Browser.FIREFOX)) {
|
|
282
307
|
// Firefox does not emit a 'playing' event after a loop, fabricate one.
|
|
283
308
|
logDebug(this, 'fabricating Firefox playing event on loop');
|
|
284
309
|
this.dispatch('playing', {});
|
|
@@ -388,8 +413,27 @@ export class PlaybackControllerImpl extends EventHostImpl {
|
|
|
388
413
|
current: value != null,
|
|
389
414
|
});
|
|
390
415
|
}
|
|
416
|
+
/**
|
|
417
|
+
* Starts (or restarts) the stall detector: recovers a play head that stays frozen when it
|
|
418
|
+
* should be advancing — some WebKit versions never resume after a seek or a play, e.g. after a
|
|
419
|
+
* buffer-flushing seek. Superseded by the next seek/play and stopped on dispose.
|
|
420
|
+
* @private
|
|
421
|
+
*/
|
|
422
|
+
startStallDetection() {
|
|
423
|
+
this.stopStallDetection?.();
|
|
424
|
+
this.stopStallDetection = createStallDetector(this.media, {
|
|
425
|
+
fabricatePlaying: () => this.dispatch('playing', {}),
|
|
426
|
+
// Safari/iOS can leave a seek on a freshly-attached source pending
|
|
427
|
+
// with a stale empty `buffered`; re-issue the seek to un-stick it.
|
|
428
|
+
nudgeUnbuffered: hasBrowser(Browser.WEBKIT),
|
|
429
|
+
});
|
|
430
|
+
}
|
|
391
431
|
play() {
|
|
392
432
|
logDebug(this, 'play');
|
|
433
|
+
// Recover a play that never starts progressing (stuck WebKit) — start
|
|
434
|
+
// even when a prior play() is still pending, e.g. a resume that stalled
|
|
435
|
+
// after a buffer-flushing seek, whose play() promise this call returns.
|
|
436
|
+
this.startStallDetection();
|
|
393
437
|
if (!this.pendingPlay) {
|
|
394
438
|
const playAbort = new Abort();
|
|
395
439
|
this.playAbort = playAbort;
|
|
@@ -430,15 +474,18 @@ export class PlaybackControllerImpl extends EventHostImpl {
|
|
|
430
474
|
// Claim this seek as the newest. A seek that parks on an await (e.g. waiting for loadedMetadata) can resume
|
|
431
475
|
// AFTER a later-issued seek has already applied its target.
|
|
432
476
|
const seekId = ++this._seekId;
|
|
477
|
+
// A new seek supersedes any stall watch from the previous one.
|
|
478
|
+
this.stopStallDetection?.();
|
|
479
|
+
this.stopStallDetection = null;
|
|
433
480
|
const superseded = () => {
|
|
434
481
|
if (this._seekId === seekId)
|
|
435
482
|
return false;
|
|
436
483
|
logDebug(this, `seekTo: ${time} superseded by a newer seek`);
|
|
437
484
|
return true;
|
|
438
485
|
};
|
|
439
|
-
const nextEvent = (type) => {
|
|
486
|
+
const nextEvent = (type, options) => {
|
|
440
487
|
return nextEventAsPromise(this, type, {
|
|
441
|
-
timeout: this.options.seekTimeout,
|
|
488
|
+
timeout: options?.timeout ?? this.options.seekTimeout,
|
|
442
489
|
timeoutMessage: `seek timed out on ${type} event after {time}s`,
|
|
443
490
|
timeoutOrigin: ErrorOrigin.INTERNAL,
|
|
444
491
|
timeoutLevel: ErrorLevel.WARN,
|
|
@@ -471,14 +518,58 @@ export class PlaybackControllerImpl extends EventHostImpl {
|
|
|
471
518
|
logDebug(this, `seekTo: requested: ${time}, actual: ${clampedTime} no-op`);
|
|
472
519
|
}
|
|
473
520
|
else {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
521
|
+
// A dispose rejects the seek; a timeout means the attempt is stuck.
|
|
522
|
+
const nullIfStuck = (error) => {
|
|
523
|
+
if (this.disposeAbort.aborted())
|
|
524
|
+
throw error;
|
|
525
|
+
return null;
|
|
526
|
+
};
|
|
527
|
+
// Issues one seek attempt. Returns the seeked event, or null if it
|
|
528
|
+
// didn't complete within SEEK_ATTEMPT_TIMEOUT. Observed on WebKit/iOS
|
|
529
|
+
// Safari: seeking a freshly-attached source that has never decoded a
|
|
530
|
+
// frame hangs — the seek never completes and playback stalls in
|
|
531
|
+
// `waiting`. The initial attempt keeps the full seekTimeout on
|
|
532
|
+
// `seeking` (a media that never starts seeking is a real timeout); a
|
|
533
|
+
// re-seek to the current position emits no fresh `seeking` on WebKit,
|
|
534
|
+
// so bound that wait and fall through to the stall detector.
|
|
535
|
+
const seekOnce = async (reSeeking) => {
|
|
536
|
+
const seeking = reSeeking
|
|
537
|
+
? nextEvent('seeking', {
|
|
538
|
+
timeout: SEEK_ATTEMPT_TIMEOUT,
|
|
539
|
+
}).catch(nullIfStuck)
|
|
540
|
+
: nextEvent('seeking');
|
|
541
|
+
this.media.currentTime = clampedTime;
|
|
542
|
+
if ((await seeking) === null)
|
|
543
|
+
return null;
|
|
544
|
+
return nextEvent('seeked', {
|
|
545
|
+
timeout: SEEK_ATTEMPT_TIMEOUT,
|
|
546
|
+
}).catch(nullIfStuck);
|
|
547
|
+
};
|
|
548
|
+
let seekedEvent = await seekOnce(false);
|
|
549
|
+
// Re-issue the seek when it never completed (kick the decoder with
|
|
550
|
+
// play() first — decoding a frame unsticks WebKit) or when it landed
|
|
551
|
+
// outside tolerance of the target. Bounded, and not fighting a newer
|
|
552
|
+
// seek. The play() kick is fire-and-forget: a wedged seek can leave
|
|
553
|
+
// its promise unsettled forever, which would stall the retry loop.
|
|
554
|
+
for (let attempt = 0; !superseded() &&
|
|
555
|
+
attempt < SEEK_RETRY_LIMIT &&
|
|
556
|
+
(seekedEvent === null ||
|
|
557
|
+
(seekedEvent.reason === 'seeked' &&
|
|
558
|
+
!closeTo(clampedTime, this.currentTime, tolerance))); attempt++) {
|
|
559
|
+
if (seekedEvent === null)
|
|
560
|
+
void this.media.play().catch(noop);
|
|
561
|
+
logDebug(this, `seekTo: re-seeking to ${clampedTime}`);
|
|
562
|
+
seekedEvent = await seekOnce(true);
|
|
563
|
+
}
|
|
564
|
+
if (seekedEvent?.reason === 'emptied') {
|
|
479
565
|
// Seek finished due to track unloading. Abort any pending seek.
|
|
480
566
|
throw new AbortError();
|
|
481
567
|
}
|
|
568
|
+
// Some WebKit versions don't resume after seeking. Watch for that:
|
|
569
|
+
// nudge/kick playback back to life, and fabricate a playing event
|
|
570
|
+
// if it resumes without emitting one.
|
|
571
|
+
if (!this.paused)
|
|
572
|
+
this.startStallDetection();
|
|
482
573
|
}
|
|
483
574
|
}
|
|
484
575
|
/**
|
|
@@ -188,6 +188,19 @@ export interface PlaybackControllerEventMap {
|
|
|
188
188
|
* true and the media is seeking a new position.
|
|
189
189
|
*/
|
|
190
190
|
readonly seeking: AnyRecord;
|
|
191
|
+
/**
|
|
192
|
+
* Fired when the play head has been detected as frozen while playing — i.e. no `timeUpdate`
|
|
193
|
+
* was observed for longer than the stall threshold. Only emitted once playback has actually
|
|
194
|
+
* been observed (a `playing` event), so it is not emitted during initial loading or while
|
|
195
|
+
* awaiting a seek. Each stall is bracketed by a `stallEntered`/{@link stallEnded} pair.
|
|
196
|
+
*/
|
|
197
|
+
readonly stallEntered: AnyRecord;
|
|
198
|
+
/**
|
|
199
|
+
* Fired when a stall (see {@link stallEntered}) has ended, for any reason. `started` is the
|
|
200
|
+
* time of the last `timeUpdate` observed before the play head froze, and `duration` is the
|
|
201
|
+
* total number of seconds the play head was frozen.
|
|
202
|
+
*/
|
|
203
|
+
readonly stallEnded: OperationCompletedEvent<StallEndedReason>;
|
|
191
204
|
/**
|
|
192
205
|
* Fired when the time indicated by the currentTime attribute has been updated.
|
|
193
206
|
*/
|
|
@@ -230,7 +243,20 @@ export declare enum WaitedReason {
|
|
|
230
243
|
* A list of all events that cause the 2nd-order 'waited' event.
|
|
231
244
|
*/
|
|
232
245
|
export declare const waitedReasons: readonly [WaitedReason.PLAYING, WaitedReason.PAUSE, WaitedReason.EMPTIED, WaitedReason.SEEKING];
|
|
233
|
-
|
|
246
|
+
/**
|
|
247
|
+
* Reasons a stall (see {@link PlaybackControllerEventMap.stallEntered}) has ended.
|
|
248
|
+
*/
|
|
249
|
+
export declare enum StallEndedReason {
|
|
250
|
+
/** The play head advanced again — playback resumed. */
|
|
251
|
+
PLAYING = "playing",
|
|
252
|
+
/** Playback was paused. */
|
|
253
|
+
PAUSE = "pause",
|
|
254
|
+
/** A seek started. */
|
|
255
|
+
SEEKING = "seeking",
|
|
256
|
+
/** The media was emptied (e.g. unloaded/reloaded). */
|
|
257
|
+
EMPTIED = "emptied"
|
|
258
|
+
}
|
|
259
|
+
export declare const ALL_PLAYBACK_STATE_EVENTS: readonly ["abort", "canPlay", "canPlayThrough", "durationChange", "emptied", "ended", "error", "playIsPendingChange", "loadedData", "loadedMetadata", "loadStart", "looped", "mutedChange", "pause", "play", "playRejected", "played", "playing", "progress", "rateChange", "readyStateChange", "reset", "resize", "seeked", "seeking", "stallEntered", "stallEnded", "timeUpdate", "volumeChange", "waited", "waiting", "waitingForKey"];
|
|
234
260
|
export declare enum PlaybackReadyState {
|
|
235
261
|
/**
|
|
236
262
|
* No information is available about the media resource.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReadonlyPlaybackController.d.ts","sourceRoot":"","sources":["../../../../src/playback/ReadonlyPlaybackController.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ReadonlyPlaybackController.d.ts","sourceRoot":"","sources":["../../../../src/playback/ReadonlyPlaybackController.ts"],"names":[],"mappings":"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"}
|
|
@@ -48,6 +48,20 @@ export const waitedReasons = [
|
|
|
48
48
|
WaitedReason.EMPTIED,
|
|
49
49
|
WaitedReason.SEEKING,
|
|
50
50
|
];
|
|
51
|
+
/**
|
|
52
|
+
* Reasons a stall (see {@link PlaybackControllerEventMap.stallEntered}) has ended.
|
|
53
|
+
*/
|
|
54
|
+
export var StallEndedReason;
|
|
55
|
+
(function (StallEndedReason) {
|
|
56
|
+
/** The play head advanced again — playback resumed. */
|
|
57
|
+
StallEndedReason["PLAYING"] = "playing";
|
|
58
|
+
/** Playback was paused. */
|
|
59
|
+
StallEndedReason["PAUSE"] = "pause";
|
|
60
|
+
/** A seek started. */
|
|
61
|
+
StallEndedReason["SEEKING"] = "seeking";
|
|
62
|
+
/** The media was emptied (e.g. unloaded/reloaded). */
|
|
63
|
+
StallEndedReason["EMPTIED"] = "emptied";
|
|
64
|
+
})(StallEndedReason || (StallEndedReason = {}));
|
|
51
65
|
export const ALL_PLAYBACK_STATE_EVENTS = [
|
|
52
66
|
'abort',
|
|
53
67
|
'canPlay',
|
|
@@ -74,6 +88,8 @@ export const ALL_PLAYBACK_STATE_EVENTS = [
|
|
|
74
88
|
'resize',
|
|
75
89
|
'seeked',
|
|
76
90
|
'seeking',
|
|
91
|
+
'stallEntered',
|
|
92
|
+
'stallEnded',
|
|
77
93
|
'timeUpdate',
|
|
78
94
|
'volumeChange',
|
|
79
95
|
'waited',
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface StallDetectorOptions {
|
|
2
|
+
/**
|
|
3
|
+
* Called when playback resumes without emitting a `playing` event (or after the detector
|
|
4
|
+
* revived it), so the caller can announce playback.
|
|
5
|
+
*/
|
|
6
|
+
readonly fabricatePlaying: () => void;
|
|
7
|
+
/** Poll interval in seconds. Default: 0.25 */
|
|
8
|
+
readonly pollInterval?: number;
|
|
9
|
+
/** Seconds the play head must be frozen before recovering. Default: 0.75 */
|
|
10
|
+
readonly stallThreshold?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Don't nudge this close to the end of buffered data; a freeze there is a
|
|
13
|
+
* legitimate wait for more data. Seconds. Default: 0.5
|
|
14
|
+
*/
|
|
15
|
+
readonly bufferEndMargin?: number;
|
|
16
|
+
/** Seconds to nudge the play head forward to prod a resume. Default: 0.1 */
|
|
17
|
+
readonly stallSkip?: number;
|
|
18
|
+
/** Max recovery attempts before giving up. Default: 3 */
|
|
19
|
+
readonly maxAttempts?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Nudge even when the playhead isn't inside a buffered range. Some WebKit versions leave a
|
|
22
|
+
* seek perpetually pending (no `seeked`) with a stale empty `buffered` even though data is
|
|
23
|
+
* appended; re-issuing the seek via a nudge can complete it. Off by default (only nudge over
|
|
24
|
+
* genuinely buffered data). Default: false
|
|
25
|
+
*/
|
|
26
|
+
readonly nudgeUnbuffered?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Watches a media element after a seek/play and drives it back into a playing state. Some WebKit
|
|
30
|
+
* versions stall: the play head freezes inside buffered data and playback never resumes (often with
|
|
31
|
+
* a stale low readyState). This polls the play head and, while the element should be advancing:
|
|
32
|
+
*
|
|
33
|
+
* - if it's frozen inside buffered data, nudges currentTime forward to prod a resume;
|
|
34
|
+
* - if there's no data at the playhead, waits (it's legitimately buffering, or between sources).
|
|
35
|
+
*
|
|
36
|
+
* When the play head progresses it calls {@link StallDetectorOptions.fabricatePlaying} if playback
|
|
37
|
+
* resumed without a `playing` event (or after the detector revived it), then stops. It also stops
|
|
38
|
+
* on pause/ended/emptied or after {@link StallDetectorOptions.maxAttempts}.
|
|
39
|
+
*
|
|
40
|
+
* Start it after a seek or play only when playback is intended — a paused element won't advance.
|
|
41
|
+
*/
|
|
42
|
+
export declare function createStallDetector(media: HTMLMediaElement, options: StallDetectorOptions): () => void;
|
|
43
|
+
//# sourceMappingURL=createStallDetector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createStallDetector.d.ts","sourceRoot":"","sources":["../../../../src/playback/createStallDetector.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,oBAAoB;IACjC;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,EAAE,MAAM,IAAI,CAAA;IACrC,8CAA8C;IAC9C,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,4EAA4E;IAC5E,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;IAChC;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;IACjC,4EAA4E;IAC5E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,yDAAyD;IACzD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAA;CACrC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAC/B,KAAK,EAAE,gBAAgB,EACvB,OAAO,EAAE,oBAAoB,GAC9B,MAAM,IAAI,CAgEZ"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Watches a media element after a seek/play and drives it back into a playing state. Some WebKit
|
|
7
|
+
* versions stall: the play head freezes inside buffered data and playback never resumes (often with
|
|
8
|
+
* a stale low readyState). This polls the play head and, while the element should be advancing:
|
|
9
|
+
*
|
|
10
|
+
* - if it's frozen inside buffered data, nudges currentTime forward to prod a resume;
|
|
11
|
+
* - if there's no data at the playhead, waits (it's legitimately buffering, or between sources).
|
|
12
|
+
*
|
|
13
|
+
* When the play head progresses it calls {@link StallDetectorOptions.fabricatePlaying} if playback
|
|
14
|
+
* resumed without a `playing` event (or after the detector revived it), then stops. It also stops
|
|
15
|
+
* on pause/ended/emptied or after {@link StallDetectorOptions.maxAttempts}.
|
|
16
|
+
*
|
|
17
|
+
* Start it after a seek or play only when playback is intended — a paused element won't advance.
|
|
18
|
+
*/
|
|
19
|
+
export function createStallDetector(media, options) {
|
|
20
|
+
const { fabricatePlaying } = options;
|
|
21
|
+
const pollInterval = options.pollInterval ?? 0.25;
|
|
22
|
+
const stallThreshold = options.stallThreshold ?? 0.75;
|
|
23
|
+
const bufferEndMargin = options.bufferEndMargin ?? 0.5;
|
|
24
|
+
const stallSkip = options.stallSkip ?? 0.1;
|
|
25
|
+
const maxAttempts = options.maxAttempts ?? 3;
|
|
26
|
+
const nudgeUnbuffered = options.nudgeUnbuffered ?? false;
|
|
27
|
+
let lastTime = media.currentTime;
|
|
28
|
+
let stalledFor = 0;
|
|
29
|
+
let attempts = 0;
|
|
30
|
+
let sawPlaying = false;
|
|
31
|
+
let recovered = false;
|
|
32
|
+
const onPlaying = () => {
|
|
33
|
+
sawPlaying = true;
|
|
34
|
+
};
|
|
35
|
+
// A cleared source (track unload) means there's nothing to recover.
|
|
36
|
+
const onEmptied = () => stop();
|
|
37
|
+
media.addEventListener('playing', onPlaying);
|
|
38
|
+
media.addEventListener('emptied', onEmptied);
|
|
39
|
+
const stop = () => {
|
|
40
|
+
clearInterval(id);
|
|
41
|
+
media.removeEventListener('playing', onPlaying);
|
|
42
|
+
media.removeEventListener('emptied', onEmptied);
|
|
43
|
+
};
|
|
44
|
+
const id = setInterval(() => {
|
|
45
|
+
// Only a playing element is expected to advance.
|
|
46
|
+
if (media.paused || media.ended)
|
|
47
|
+
return stop();
|
|
48
|
+
if (media.currentTime !== lastTime) {
|
|
49
|
+
lastTime = media.currentTime;
|
|
50
|
+
// Progressing. Announce playback if it resumed without a `playing`
|
|
51
|
+
// event, or if the detector had to revive it (a prior `playing` was
|
|
52
|
+
// cleared by the intervening stall).
|
|
53
|
+
if (!sawPlaying || recovered)
|
|
54
|
+
fabricatePlaying();
|
|
55
|
+
return stop();
|
|
56
|
+
}
|
|
57
|
+
// Frozen. Recover only over buffered data; otherwise it's legitimately
|
|
58
|
+
// waiting to buffer (or between sources) and nudging can disrupt
|
|
59
|
+
// loading — except on platforms whose `buffered` is unreliable here
|
|
60
|
+
// ({@link nudgeUnbuffered}), where re-issuing the seek is the recovery.
|
|
61
|
+
if (!nudgeUnbuffered && !withinBufferedData(media, bufferEndMargin)) {
|
|
62
|
+
stalledFor = 0;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
stalledFor += pollInterval;
|
|
66
|
+
if (stalledFor < stallThreshold)
|
|
67
|
+
return;
|
|
68
|
+
stalledFor = 0;
|
|
69
|
+
if (attempts++ >= maxAttempts)
|
|
70
|
+
return stop();
|
|
71
|
+
recovered = true;
|
|
72
|
+
// Data is buffered at the playhead — a small nudge prods a resume.
|
|
73
|
+
// (iOS can sit here reporting a stale low readyState even though the
|
|
74
|
+
// range around the playhead is buffered.)
|
|
75
|
+
media.currentTime += stallSkip;
|
|
76
|
+
lastTime = media.currentTime;
|
|
77
|
+
}, pollInterval * 1000);
|
|
78
|
+
return stop;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* True when the play head sits inside a buffered range, clear of its end by
|
|
82
|
+
* {@link endMargin} — i.e. there is data to keep playing.
|
|
83
|
+
*
|
|
84
|
+
* A range that ends at the end of the media is treated as nudgeable up to its
|
|
85
|
+
* end: no more data is coming, so a freeze there is a stuck end-of-track (seen
|
|
86
|
+
* on progressive `src` tracks), and a nudge pushes the element to `ended`.
|
|
87
|
+
*/
|
|
88
|
+
function withinBufferedData(media, endMargin) {
|
|
89
|
+
const t = media.currentTime;
|
|
90
|
+
const duration = media.duration;
|
|
91
|
+
const buffered = media.buffered;
|
|
92
|
+
for (let i = 0; i < buffered.length; i++) {
|
|
93
|
+
const end = buffered.end(i);
|
|
94
|
+
const isEndOfMedia = Number.isFinite(duration) &&
|
|
95
|
+
duration > 0 &&
|
|
96
|
+
end >= duration - endMargin;
|
|
97
|
+
if (t >= buffered.start(i) && (t < end - endMargin || isEndOfMedia))
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type Timestamp } from '@amazon/vinyl-util';
|
|
2
|
+
import type { ReadonlyPlaybackController } from './ReadonlyPlaybackController';
|
|
3
|
+
import { StallEndedReason } from './ReadonlyPlaybackController';
|
|
4
|
+
export interface StallMonitorOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Seconds the play head must be frozen (no `timeUpdate`) while playing before a stall is
|
|
7
|
+
* reported. Default: 1
|
|
8
|
+
*/
|
|
9
|
+
readonly stallThreshold?: number;
|
|
10
|
+
/**
|
|
11
|
+
* How often (seconds) the play head freeze is checked. Default: 0.25
|
|
12
|
+
*/
|
|
13
|
+
readonly pollInterval?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface StallEndedInfo {
|
|
16
|
+
/** Time of the last `timeUpdate` observed before the play head froze. */
|
|
17
|
+
readonly started: Timestamp;
|
|
18
|
+
/** Time the stall ended. */
|
|
19
|
+
readonly ended: Timestamp;
|
|
20
|
+
/** Total seconds the play head was frozen. */
|
|
21
|
+
readonly duration: number;
|
|
22
|
+
/** Why the stall ended. */
|
|
23
|
+
readonly reason: StallEndedReason;
|
|
24
|
+
}
|
|
25
|
+
export interface StallMonitorCallbacks {
|
|
26
|
+
/** Called when a stall is first detected. */
|
|
27
|
+
readonly onStallEntered: () => void;
|
|
28
|
+
/** Called when a detected stall ends. */
|
|
29
|
+
readonly onStallEnded: (info: StallEndedInfo) => void;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Watches a {@link ReadonlyPlaybackController} for stalls while playing: the play head freezing
|
|
33
|
+
* (no `timeUpdate`) for longer than {@link StallMonitorOptions.stallThreshold} seconds.
|
|
34
|
+
*
|
|
35
|
+
* Detection only runs once playback has actually been observed (a `playing` event) and is
|
|
36
|
+
* suspended on `pause`/`seeking`/`emptied` until playback is observed again — so a stall is never
|
|
37
|
+
* reported during initial loading or while awaiting a seek. A detected stall ends when the play
|
|
38
|
+
* head advances again (`playing`) or playback stops for another reason (`pause`/`seeking`/
|
|
39
|
+
* `emptied`); {@link StallEndedInfo.duration} is measured from the last `timeUpdate` before the
|
|
40
|
+
* freeze, so it reflects the full frozen time rather than only the time past the threshold.
|
|
41
|
+
*
|
|
42
|
+
* Returns a function that stops the monitor.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createStallMonitor(controller: ReadonlyPlaybackController, callbacks: StallMonitorCallbacks, options?: StallMonitorOptions): () => void;
|
|
45
|
+
//# sourceMappingURL=createStallMonitor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createStallMonitor.d.ts","sourceRoot":"","sources":["../../../../src/playback/createStallMonitor.ts"],"names":[],"mappings":"AAKA,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACnE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAA;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AAE/D,MAAM,WAAW,mBAAmB;IAChC;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;IAEhC;;OAEG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CACjC;AAED,MAAM,WAAW,cAAc;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAA;IAC3B,4BAA4B;IAC5B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;IACzB,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,2BAA2B;IAC3B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAA;CACpC;AAED,MAAM,WAAW,qBAAqB;IAClC,6CAA6C;IAC7C,QAAQ,CAAC,cAAc,EAAE,MAAM,IAAI,CAAA;IACnC,yCAAyC;IACzC,QAAQ,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAA;CACxD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAC9B,UAAU,EAAE,0BAA0B,EACtC,SAAS,EAAE,qBAAqB,EAChC,OAAO,GAAE,mBAAwB,GAClC,MAAM,IAAI,CA8EZ"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { createDisposer } from '@amazon/vinyl-util';
|
|
6
|
+
import { StallEndedReason } from './ReadonlyPlaybackController';
|
|
7
|
+
/**
|
|
8
|
+
* Watches a {@link ReadonlyPlaybackController} for stalls while playing: the play head freezing
|
|
9
|
+
* (no `timeUpdate`) for longer than {@link StallMonitorOptions.stallThreshold} seconds.
|
|
10
|
+
*
|
|
11
|
+
* Detection only runs once playback has actually been observed (a `playing` event) and is
|
|
12
|
+
* suspended on `pause`/`seeking`/`emptied` until playback is observed again — so a stall is never
|
|
13
|
+
* reported during initial loading or while awaiting a seek. A detected stall ends when the play
|
|
14
|
+
* head advances again (`playing`) or playback stops for another reason (`pause`/`seeking`/
|
|
15
|
+
* `emptied`); {@link StallEndedInfo.duration} is measured from the last `timeUpdate` before the
|
|
16
|
+
* freeze, so it reflects the full frozen time rather than only the time past the threshold.
|
|
17
|
+
*
|
|
18
|
+
* Returns a function that stops the monitor.
|
|
19
|
+
*/
|
|
20
|
+
export function createStallMonitor(controller, callbacks, options = {}) {
|
|
21
|
+
const stallThresholdMs = (options.stallThreshold ?? 1) * 1000;
|
|
22
|
+
const pollIntervalMs = (options.pollInterval ?? 0.25) * 1000;
|
|
23
|
+
// Only detect once playback has been observed; gates out initial load and post-seek buffering.
|
|
24
|
+
let observedPlaying = false;
|
|
25
|
+
// Wall-clock time of the last `timeUpdate` — the reference start of any freeze.
|
|
26
|
+
let lastProgressAt = Date.now();
|
|
27
|
+
let inStall = false;
|
|
28
|
+
let pollId = null;
|
|
29
|
+
const endStall = (reason) => {
|
|
30
|
+
if (!inStall)
|
|
31
|
+
return;
|
|
32
|
+
inStall = false;
|
|
33
|
+
const ended = Date.now();
|
|
34
|
+
callbacks.onStallEnded({
|
|
35
|
+
started: lastProgressAt,
|
|
36
|
+
ended,
|
|
37
|
+
duration: (ended - lastProgressAt) / 1000,
|
|
38
|
+
reason,
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
const stopPoll = () => {
|
|
42
|
+
if (pollId == null)
|
|
43
|
+
return;
|
|
44
|
+
clearInterval(pollId);
|
|
45
|
+
pollId = null;
|
|
46
|
+
};
|
|
47
|
+
const startPoll = () => {
|
|
48
|
+
if (pollId != null)
|
|
49
|
+
return;
|
|
50
|
+
pollId = setInterval(() => {
|
|
51
|
+
if (inStall || !observedPlaying)
|
|
52
|
+
return;
|
|
53
|
+
if (Date.now() - lastProgressAt >= stallThresholdMs) {
|
|
54
|
+
inStall = true;
|
|
55
|
+
callbacks.onStallEntered();
|
|
56
|
+
}
|
|
57
|
+
}, pollIntervalMs);
|
|
58
|
+
};
|
|
59
|
+
// Playback stopped for a non-resume reason: close any open stall and suspend detection until
|
|
60
|
+
// playback is observed again.
|
|
61
|
+
const suspend = (reason) => {
|
|
62
|
+
endStall(reason);
|
|
63
|
+
observedPlaying = false;
|
|
64
|
+
stopPoll();
|
|
65
|
+
};
|
|
66
|
+
const { add, dispose } = createDisposer();
|
|
67
|
+
add(controller.on('playing', () => {
|
|
68
|
+
// Resuming from a stall closes it; otherwise this is a fresh start of playback, so
|
|
69
|
+
// begin the freeze clock now (no `timeUpdate` has necessarily arrived yet).
|
|
70
|
+
if (inStall)
|
|
71
|
+
endStall(StallEndedReason.PLAYING);
|
|
72
|
+
else
|
|
73
|
+
lastProgressAt = Date.now();
|
|
74
|
+
observedPlaying = true;
|
|
75
|
+
startPoll();
|
|
76
|
+
}));
|
|
77
|
+
add(controller.on('timeUpdate', () => {
|
|
78
|
+
// The play head advanced: close any open stall, then reset the freeze clock.
|
|
79
|
+
if (inStall)
|
|
80
|
+
endStall(StallEndedReason.PLAYING);
|
|
81
|
+
lastProgressAt = Date.now();
|
|
82
|
+
}));
|
|
83
|
+
add(controller.on('pause', () => suspend(StallEndedReason.PAUSE)));
|
|
84
|
+
add(controller.on('seeking', () => suspend(StallEndedReason.SEEKING)));
|
|
85
|
+
add(controller.on('emptied', () => suspend(StallEndedReason.EMPTIED)));
|
|
86
|
+
let stopped = false;
|
|
87
|
+
return () => {
|
|
88
|
+
if (stopped)
|
|
89
|
+
return;
|
|
90
|
+
stopped = true;
|
|
91
|
+
stopPoll();
|
|
92
|
+
dispose();
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ObjectSchema } from '@amazon/vinyl-validation';
|
|
2
|
+
import type { Maybe } from '@amazon/vinyl-util';
|
|
2
3
|
/**
|
|
3
4
|
* Criteria used to pick which audio rendition to play.
|
|
4
5
|
*/
|
|
@@ -33,6 +34,32 @@ export interface AudioOptions {
|
|
|
33
34
|
* The audio selection criteria (language / descriptive).
|
|
34
35
|
*/
|
|
35
36
|
readonly selection?: AudioSelection;
|
|
37
|
+
/**
|
|
38
|
+
* When `true`, disables the audio sampling-rate support filter, so audio
|
|
39
|
+
* renditions whose sampling rate exceeds what the platform reports (via the
|
|
40
|
+
* `AudioContext` output rate) are kept and offered to playback anyway.
|
|
41
|
+
* `false` (the default) applies the filter. Changing this re-filters the
|
|
42
|
+
* timeline immediately.
|
|
43
|
+
*/
|
|
44
|
+
readonly disableSampleRateFilter?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* The maximum audio sampling rate (Hz) the sampling-rate filter treats as
|
|
47
|
+
* supported. When set, it takes precedence over the platform's
|
|
48
|
+
* `AudioContext`-reported output rate. Ignored when
|
|
49
|
+
* {@link disableSampleRateFilter} is `true`. Changing this re-filters the
|
|
50
|
+
* timeline immediately. `null`/`undefined` falls back to the reported rate.
|
|
51
|
+
*/
|
|
52
|
+
readonly maxSampleRate?: Maybe<number>;
|
|
53
|
+
/**
|
|
54
|
+
* The minimum audio sampling rate (Hz) the sampling-rate filter treats as
|
|
55
|
+
* supported. When set, renditions below it are filtered out as a soft floor
|
|
56
|
+
* (if every audio rendition is below it, the highest is kept so playback is
|
|
57
|
+
* never stranded). Applied alongside the max cap whenever the filter runs,
|
|
58
|
+
* regardless of how the max was determined; ignored when
|
|
59
|
+
* {@link disableSampleRateFilter} is `true`. Changing this re-filters the
|
|
60
|
+
* timeline immediately.
|
|
61
|
+
*/
|
|
62
|
+
readonly minSampleRate?: Maybe<number>;
|
|
36
63
|
}
|
|
37
64
|
export declare const defaultAudioOptions: AudioOptions;
|
|
38
65
|
export declare const audioOptionsValidator: ObjectSchema<AudioOptions>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AudioOptions.d.ts","sourceRoot":"","sources":["../../../../src/streaming/AudioOptions.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"AudioOptions.d.ts","sourceRoot":"","sources":["../../../../src/streaming/AudioOptions.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAQ5D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAE/C;;GAEG;AACH,MAAM,WAAW,cAAc;IAC3B;;;;;;;;OAQG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,IAAI,CAAA;IAErD;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CACjC;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IACzB;;OAEG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,cAAc,CAAA;IAEnC;;;;;;OAMG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAA;IAE1C;;;;;;OAMG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;IAEtC;;;;;;;;OAQG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;CACzC;AAED,eAAO,MAAM,mBAAmB,EAAE,YAGjC,CAAA;AAOD,eAAO,MAAM,qBAAqB,EAAE,YAAY,CAAC,YAAY,CAK3D,CAAA"}
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
|
-
import { array, boolean, object, string } from '@amazon/vinyl-validation';
|
|
5
|
+
import { array, boolean, number, object, string, } from '@amazon/vinyl-validation';
|
|
6
6
|
export const defaultAudioOptions = {
|
|
7
7
|
selection: { language: null, descriptive: false },
|
|
8
|
+
disableSampleRateFilter: false,
|
|
8
9
|
};
|
|
9
10
|
const audioSelectionValidator = object({
|
|
10
11
|
language: string().or(array(string()).readonly()).orNull().optional(),
|
|
@@ -12,4 +13,7 @@ const audioSelectionValidator = object({
|
|
|
12
13
|
});
|
|
13
14
|
export const audioOptionsValidator = object({
|
|
14
15
|
selection: audioSelectionValidator.optional(),
|
|
16
|
+
disableSampleRateFilter: boolean().optional(),
|
|
17
|
+
maxSampleRate: number().maybe().optional(),
|
|
18
|
+
minSampleRate: number().maybe().optional(),
|
|
15
19
|
});
|