@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
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { createStallMonitor, StallEndedReason, } from '@amazon/vinyl';
|
|
6
|
+
import { EventHostImpl } from '@amazon/vinyl-util';
|
|
7
|
+
import { useMockTime } from '@amazon/vinyl-util/browserTestUtil';
|
|
8
|
+
describe('createStallMonitor', () => {
|
|
9
|
+
const clock = useMockTime();
|
|
10
|
+
let host;
|
|
11
|
+
let onStallEntered;
|
|
12
|
+
let onStallEnded;
|
|
13
|
+
let stop;
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
host = new EventHostImpl();
|
|
16
|
+
onStallEntered = jasmine.createSpy('onStallEntered');
|
|
17
|
+
onStallEnded = jasmine.createSpy('onStallEnded');
|
|
18
|
+
});
|
|
19
|
+
afterEach(() => stop?.());
|
|
20
|
+
function monitor(overrides = {}) {
|
|
21
|
+
stop = createStallMonitor(host, { onStallEntered, onStallEnded }, overrides);
|
|
22
|
+
}
|
|
23
|
+
/** Advances the mock clock by `n` default (0.25s) poll intervals. */
|
|
24
|
+
async function poll(n) {
|
|
25
|
+
for (let i = 0; i < n; i++)
|
|
26
|
+
await clock.tick(0.25);
|
|
27
|
+
}
|
|
28
|
+
it('does not report a stall before playback is observed', async () => {
|
|
29
|
+
monitor();
|
|
30
|
+
// No 'playing' has been observed (e.g. initial load / awaiting a seek).
|
|
31
|
+
await poll(8);
|
|
32
|
+
expect(onStallEntered).not.toHaveBeenCalled();
|
|
33
|
+
});
|
|
34
|
+
it('reports a stall once the play head is frozen past the threshold', async () => {
|
|
35
|
+
monitor();
|
|
36
|
+
host.dispatch('playing', {});
|
|
37
|
+
await poll(3); // 0.75s < 1s threshold
|
|
38
|
+
expect(onStallEntered).not.toHaveBeenCalled();
|
|
39
|
+
await poll(1); // 1s
|
|
40
|
+
expect(onStallEntered).toHaveBeenCalledTimes(1);
|
|
41
|
+
});
|
|
42
|
+
it('honors a configurable threshold', async () => {
|
|
43
|
+
monitor({ stallThreshold: 2 });
|
|
44
|
+
host.dispatch('playing', {});
|
|
45
|
+
await poll(4); // 1s < 2s
|
|
46
|
+
expect(onStallEntered).not.toHaveBeenCalled();
|
|
47
|
+
await poll(4); // 2s
|
|
48
|
+
expect(onStallEntered).toHaveBeenCalledTimes(1);
|
|
49
|
+
});
|
|
50
|
+
it('does not report a stall while timeUpdates keep arriving', async () => {
|
|
51
|
+
monitor();
|
|
52
|
+
host.dispatch('playing', {});
|
|
53
|
+
for (let i = 0; i < 10; i++) {
|
|
54
|
+
await poll(2); // 0.5s, under the threshold
|
|
55
|
+
host.dispatch('timeUpdate', { previous: i, current: i + 1 });
|
|
56
|
+
}
|
|
57
|
+
expect(onStallEntered).not.toHaveBeenCalled();
|
|
58
|
+
});
|
|
59
|
+
it('ends a stall with reason "playing" when the play head advances, measuring from the last timeUpdate', async () => {
|
|
60
|
+
monitor();
|
|
61
|
+
host.dispatch('playing', {});
|
|
62
|
+
host.dispatch('timeUpdate', { previous: 0, current: 1 });
|
|
63
|
+
await poll(4); // freeze 1s -> stallEntered
|
|
64
|
+
expect(onStallEntered).toHaveBeenCalledTimes(1);
|
|
65
|
+
await poll(4); // frozen another 1s (total 2s)
|
|
66
|
+
host.dispatch('timeUpdate', { previous: 1, current: 2 }); // resume
|
|
67
|
+
expect(onStallEnded).toHaveBeenCalledTimes(1);
|
|
68
|
+
const info = onStallEnded.calls.mostRecent().args[0];
|
|
69
|
+
expect(info.reason).toBe(StallEndedReason.PLAYING);
|
|
70
|
+
expect(info.duration).toBeCloseTo(2, 1);
|
|
71
|
+
});
|
|
72
|
+
for (const [event, reason] of [
|
|
73
|
+
['pause', StallEndedReason.PAUSE],
|
|
74
|
+
['seeking', StallEndedReason.SEEKING],
|
|
75
|
+
['emptied', StallEndedReason.EMPTIED],
|
|
76
|
+
]) {
|
|
77
|
+
it(`ends a stall with reason "${reason}" on ${event}`, async () => {
|
|
78
|
+
monitor();
|
|
79
|
+
host.dispatch('playing', {});
|
|
80
|
+
await poll(4); // stallEntered
|
|
81
|
+
expect(onStallEntered).toHaveBeenCalledTimes(1);
|
|
82
|
+
host.dispatch(event, {});
|
|
83
|
+
expect(onStallEnded).toHaveBeenCalledTimes(1);
|
|
84
|
+
expect(onStallEnded.calls.mostRecent().args[0].reason).toBe(reason);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
it('ends a stall with reason "playing" when a playing event fires (resume without a timeUpdate)', async () => {
|
|
88
|
+
monitor();
|
|
89
|
+
host.dispatch('playing', {});
|
|
90
|
+
await poll(4); // stallEntered
|
|
91
|
+
expect(onStallEntered).toHaveBeenCalledTimes(1);
|
|
92
|
+
// A 'playing' event (rather than a timeUpdate) signals the resume.
|
|
93
|
+
host.dispatch('playing', {});
|
|
94
|
+
expect(onStallEnded).toHaveBeenCalledTimes(1);
|
|
95
|
+
expect(onStallEnded.calls.mostRecent().args[0].reason).toBe(StallEndedReason.PLAYING);
|
|
96
|
+
});
|
|
97
|
+
it('suspends detection after a seek until playback is observed again', async () => {
|
|
98
|
+
monitor();
|
|
99
|
+
host.dispatch('playing', {});
|
|
100
|
+
host.dispatch('seeking', {}); // suspends; observedPlaying = false
|
|
101
|
+
await poll(8);
|
|
102
|
+
expect(onStallEntered).not.toHaveBeenCalled();
|
|
103
|
+
// Once playback is observed again, detection resumes.
|
|
104
|
+
host.dispatch('playing', {});
|
|
105
|
+
await poll(4);
|
|
106
|
+
expect(onStallEntered).toHaveBeenCalledTimes(1);
|
|
107
|
+
});
|
|
108
|
+
it('reports each stall/recovery cycle', async () => {
|
|
109
|
+
monitor();
|
|
110
|
+
host.dispatch('playing', {});
|
|
111
|
+
await poll(4);
|
|
112
|
+
host.dispatch('timeUpdate', { previous: 0, current: 1 }); // resume 1
|
|
113
|
+
await poll(4);
|
|
114
|
+
host.dispatch('timeUpdate', { previous: 1, current: 2 }); // resume 2
|
|
115
|
+
expect(onStallEntered).toHaveBeenCalledTimes(2);
|
|
116
|
+
expect(onStallEnded).toHaveBeenCalledTimes(2);
|
|
117
|
+
});
|
|
118
|
+
it('stops reporting after disposal', async () => {
|
|
119
|
+
monitor();
|
|
120
|
+
host.dispatch('playing', {});
|
|
121
|
+
stop();
|
|
122
|
+
await poll(8);
|
|
123
|
+
expect(onStallEntered).not.toHaveBeenCalled();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -58,17 +58,6 @@ describe('ContentStreamImpl', () => {
|
|
|
58
58
|
bufferingController.dispatch('error', { error, target });
|
|
59
59
|
expect(spy).toHaveBeenCalledWith({ error, target });
|
|
60
60
|
});
|
|
61
|
-
it('redispatches hasDataChange from bufferingController', () => {
|
|
62
|
-
const spy = createEventSpy(contentStream, 'hasDataChange');
|
|
63
|
-
const event = { previous: false, current: true };
|
|
64
|
-
bufferingController.dispatch('hasDataChange', event);
|
|
65
|
-
expect(spy).toHaveBeenCalledWith(event);
|
|
66
|
-
});
|
|
67
|
-
it('exposes hasData from the bufferingController', () => {
|
|
68
|
-
expect(contentStream.hasData).toBeFalse();
|
|
69
|
-
bufferingController.hasData = true;
|
|
70
|
-
expect(contentStream.hasData).toBeTrue();
|
|
71
|
-
});
|
|
72
61
|
});
|
|
73
62
|
describe('preload', () => {
|
|
74
63
|
it('configures segmentController with startTime and prefetchPriority', () => {
|
|
@@ -2,10 +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 { createEmptyMediaQualityMetadata, MseTrack,
|
|
5
|
+
import { createEmptyMediaQualityMetadata, MseTrack, } from '@amazon/vinyl';
|
|
6
6
|
import { createMockDashDependencies, } from '@amazon/vinyl/vinylTestUtil';
|
|
7
7
|
import { createEventSpy, useMockLogger } from '@amazon/vinyl-util/testUtil';
|
|
8
|
-
import { flushPromises
|
|
8
|
+
import { flushPromises } from '@amazon/vinyl-util/browserTestUtil';
|
|
9
9
|
import { externalDependencies } from '@amazon/vinyl-di';
|
|
10
10
|
import { Abort } from '@amazon/vinyl-util';
|
|
11
11
|
var any = jasmine.any;
|
|
@@ -413,73 +413,6 @@ describe('MseTrack', () => {
|
|
|
413
413
|
expect(track.bufferingEnded).toBe(false);
|
|
414
414
|
});
|
|
415
415
|
});
|
|
416
|
-
describe('stalled-seek recovery nudge', () => {
|
|
417
|
-
const clock = useMockTime();
|
|
418
|
-
function fillStream(stream) {
|
|
419
|
-
stream.hasData = true;
|
|
420
|
-
stream.dispatch('hasDataChange', { previous: false, current: true });
|
|
421
|
-
}
|
|
422
|
-
async function setup() {
|
|
423
|
-
deps.playbackController.seekTo.and.resolveTo(void 0);
|
|
424
|
-
track = createTrack();
|
|
425
|
-
await awaitContentTypes();
|
|
426
|
-
return { audio: getAudioStream(), video: getVideoStream() };
|
|
427
|
-
}
|
|
428
|
-
it('nudges the playhead when all streams have data but the seek is still stuck', async () => {
|
|
429
|
-
const { audio, video } = await setup();
|
|
430
|
-
deps.playbackController.seeking = true;
|
|
431
|
-
deps.playbackController.currentTime = 30;
|
|
432
|
-
fillStream(audio);
|
|
433
|
-
// Only one stream has data — nothing scheduled yet.
|
|
434
|
-
fillStream(video);
|
|
435
|
-
expect(deps.playbackController.seekTo).not.toHaveBeenCalled();
|
|
436
|
-
await clock.tick(SEEKING_STALL_TIME_CHECK * 1000);
|
|
437
|
-
expect(deps.playbackController.seekTo).toHaveBeenCalledOnceWith(30 + PLAYHEAD_NUDGE, 0);
|
|
438
|
-
});
|
|
439
|
-
it('does not nudge if the seek completes before the check fires', async () => {
|
|
440
|
-
const { audio, video } = await setup();
|
|
441
|
-
deps.playbackController.seeking = true;
|
|
442
|
-
fillStream(audio);
|
|
443
|
-
fillStream(video);
|
|
444
|
-
deps.playbackController.seeking = false;
|
|
445
|
-
await clock.tick(SEEKING_STALL_TIME_CHECK * 1000);
|
|
446
|
-
expect(deps.playbackController.seekTo).not.toHaveBeenCalled();
|
|
447
|
-
});
|
|
448
|
-
it('does not nudge until every stream has data', async () => {
|
|
449
|
-
const { audio } = await setup();
|
|
450
|
-
deps.playbackController.seeking = true;
|
|
451
|
-
fillStream(audio);
|
|
452
|
-
await clock.tick(SEEKING_STALL_TIME_CHECK * 1000);
|
|
453
|
-
expect(deps.playbackController.seekTo).not.toHaveBeenCalled();
|
|
454
|
-
});
|
|
455
|
-
it('does not nudge if a stream loses its data before the check fires', async () => {
|
|
456
|
-
const { audio, video } = await setup();
|
|
457
|
-
deps.playbackController.seeking = true;
|
|
458
|
-
fillStream(audio);
|
|
459
|
-
fillStream(video);
|
|
460
|
-
// A stream's buffer was cleared during the wait window.
|
|
461
|
-
video.hasData = false;
|
|
462
|
-
await clock.tick(SEEKING_STALL_TIME_CHECK * 1000);
|
|
463
|
-
expect(deps.playbackController.seekTo).not.toHaveBeenCalled();
|
|
464
|
-
});
|
|
465
|
-
it('does not nudge when not seeking', async () => {
|
|
466
|
-
const { audio, video } = await setup();
|
|
467
|
-
deps.playbackController.seeking = false;
|
|
468
|
-
fillStream(audio);
|
|
469
|
-
fillStream(video);
|
|
470
|
-
await clock.tick(SEEKING_STALL_TIME_CHECK * 1000);
|
|
471
|
-
expect(deps.playbackController.seekTo).not.toHaveBeenCalled();
|
|
472
|
-
});
|
|
473
|
-
it('does not nudge after the track is disposed', async () => {
|
|
474
|
-
const { audio, video } = await setup();
|
|
475
|
-
deps.playbackController.seeking = true;
|
|
476
|
-
fillStream(audio);
|
|
477
|
-
fillStream(video);
|
|
478
|
-
track.dispose();
|
|
479
|
-
await clock.tick(SEEKING_STALL_TIME_CHECK * 1000);
|
|
480
|
-
expect(deps.playbackController.seekTo).not.toHaveBeenCalled();
|
|
481
|
-
});
|
|
482
|
-
});
|
|
483
416
|
describe('checkBufferingEnded', () => {
|
|
484
417
|
it('calls endOfStream and dispatches bufferingEnded when all streams are done', async () => {
|
|
485
418
|
track = createTrack();
|
|
@@ -652,6 +585,41 @@ describe('MseTrack', () => {
|
|
|
652
585
|
expect(track.qualities).toEqual([qualityMetadata]);
|
|
653
586
|
expect(track.qualitiesUnfiltered).toEqual([qualityMetadata]);
|
|
654
587
|
});
|
|
588
|
+
it('plays the surviving content type when another is entirely filtered out', async () => {
|
|
589
|
+
// The content types now come from the FILTERED timeline: with every
|
|
590
|
+
// audio rendition filtered, only 'video' is required. No audio stream
|
|
591
|
+
// is created, so the load is never stranded waiting for an audio
|
|
592
|
+
// source buffer that will never arrive, and the track does not error.
|
|
593
|
+
deps.contentTypesValue.value = Promise.resolve(new Set(['video']));
|
|
594
|
+
const videoQuality = {
|
|
595
|
+
...createEmptyMediaQualityMetadata(),
|
|
596
|
+
contentType: 'video',
|
|
597
|
+
qualityId: 'v1',
|
|
598
|
+
};
|
|
599
|
+
setTimeline({
|
|
600
|
+
periods: [
|
|
601
|
+
{
|
|
602
|
+
startTime: 0,
|
|
603
|
+
endTime: 100,
|
|
604
|
+
qualities: [
|
|
605
|
+
{
|
|
606
|
+
metadata: videoQuality,
|
|
607
|
+
getSegment: () => Promise.resolve(null),
|
|
608
|
+
},
|
|
609
|
+
],
|
|
610
|
+
},
|
|
611
|
+
],
|
|
612
|
+
minBufferTime: 2,
|
|
613
|
+
getAdBreaks: () => Promise.resolve([]),
|
|
614
|
+
getDuration: () => Promise.resolve(Infinity),
|
|
615
|
+
});
|
|
616
|
+
track = createTrack();
|
|
617
|
+
await flushPromises();
|
|
618
|
+
expect(track.error).toBeNull();
|
|
619
|
+
expect(track.contentTypes).toEqual(new Set(['video']));
|
|
620
|
+
expect(getStream('video')).not.toBeNull();
|
|
621
|
+
expect(getStream('audio')).toBeNull();
|
|
622
|
+
});
|
|
655
623
|
it('reads qualities from the filtered timeline but qualitiesUnfiltered from the raw one', async () => {
|
|
656
624
|
// The transformed timeline is filtered (e.g. by preferred audio
|
|
657
625
|
// language) down to one quality; the raw timeline keeps them all.
|
|
@@ -1025,10 +993,6 @@ describe('MseTrack', () => {
|
|
|
1025
993
|
name: 'audio language',
|
|
1026
994
|
apply: () => (deps.audio.value = jaAudio),
|
|
1027
995
|
},
|
|
1028
|
-
{
|
|
1029
|
-
name: 'allowedContentTypes',
|
|
1030
|
-
apply: () => (deps.allowedContentTypes.value = allowed),
|
|
1031
|
-
},
|
|
1032
996
|
{
|
|
1033
997
|
name: 'audio descriptive',
|
|
1034
998
|
apply: () => (deps.audio.value = descriptiveAudio),
|
|
@@ -1053,6 +1017,18 @@ describe('MseTrack', () => {
|
|
|
1053
1017
|
});
|
|
1054
1018
|
});
|
|
1055
1019
|
}
|
|
1020
|
+
describe('when allowedContentTypes changes', () => {
|
|
1021
|
+
// It only affects the content-type set, which flows through
|
|
1022
|
+
// contentTypesValue -> setContentTypes. Reloading directly here too
|
|
1023
|
+
// would double hard-reset and seek to 0.
|
|
1024
|
+
it('does not reload directly', () => {
|
|
1025
|
+
const clearSpy = spyOn(track, 'clearPrefetch');
|
|
1026
|
+
const resetSpy = spyOn(track, 'reset');
|
|
1027
|
+
deps.allowedContentTypes.value = allowed;
|
|
1028
|
+
expect(clearSpy).not.toHaveBeenCalled();
|
|
1029
|
+
expect(resetSpy).not.toHaveBeenCalled();
|
|
1030
|
+
});
|
|
1031
|
+
});
|
|
1056
1032
|
describe('when abr resolution restrictions change', () => {
|
|
1057
1033
|
it('clears prefetch (without a rebuild) when maxHeight changes', () => {
|
|
1058
1034
|
const clearSpy = spyOn(track, 'clearPrefetch');
|
|
@@ -333,7 +333,7 @@ describe('QualitySelectorImpl', () => {
|
|
|
333
333
|
});
|
|
334
334
|
});
|
|
335
335
|
});
|
|
336
|
-
describe('when options change', () => {
|
|
336
|
+
describe('when options change after construction', () => {
|
|
337
337
|
it('merges set options with defaults', () => {
|
|
338
338
|
options.value = {
|
|
339
339
|
highBufferThreshold: 32,
|
|
@@ -226,27 +226,6 @@ describe('BufferingControllerImpl', () => {
|
|
|
226
226
|
expect(sourceBufferController.buffered.ranges).toEqual([[40, 60]]);
|
|
227
227
|
});
|
|
228
228
|
});
|
|
229
|
-
describe('hasData', () => {
|
|
230
|
-
beforeEach(() => {
|
|
231
|
-
bufferingController = createBufferingController();
|
|
232
|
-
bufferingController.activate();
|
|
233
|
-
});
|
|
234
|
-
it('flips true when the buffer fills and false when cleared', async () => {
|
|
235
|
-
const spy = createEventSpy(bufferingController, 'hasDataChange');
|
|
236
|
-
expect(bufferingController.hasData).toBeFalse();
|
|
237
|
-
setSegmentList([1]);
|
|
238
|
-
await open();
|
|
239
|
-
expect(bufferingController.hasData).toBeTrue();
|
|
240
|
-
expect(spy).toHaveBeenCalledWith({ previous: false, current: true });
|
|
241
|
-
// Stop the segment provider so the post-clear re-poll can't re-fill.
|
|
242
|
-
segmentController.getSegment.and.returnValue(Promise.resolve(null));
|
|
243
|
-
spy.calls.reset();
|
|
244
|
-
bufferingController.clear();
|
|
245
|
-
await nextPollImmediate();
|
|
246
|
-
expect(bufferingController.hasData).toBeFalse();
|
|
247
|
-
expect(spy).toHaveBeenCalledWith({ previous: true, current: false });
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
229
|
describe('when segments exceed max append size', () => {
|
|
251
230
|
beforeEach(() => {
|
|
252
231
|
bufferingController = createBufferingController({
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { createDefaultMediaTimelineTransformer, createEmptyMediaQualityMetadata, } from '@amazon/vinyl';
|
|
6
6
|
import { data } from '@amazon/vinyl-observable';
|
|
7
|
+
import { setUserAgent } from '@amazon/vinyl-util';
|
|
7
8
|
import { MockCapabilities, MockDrmController, } from '@amazon/vinyl/vinylTestUtil';
|
|
8
9
|
function createQuality(overrides) {
|
|
9
10
|
return {
|
|
@@ -332,6 +333,138 @@ describe('createDefaultMediaTimelineTransformer', () => {
|
|
|
332
333
|
expect(result.periods[0].qualities[0].metadata.qualityId).toBe('ja');
|
|
333
334
|
unsub();
|
|
334
335
|
});
|
|
336
|
+
it('disables the sampling-rate filter via audio options at runtime', async () => {
|
|
337
|
+
// Output device at 44.1kHz: the 96kHz rendition is normally dropped
|
|
338
|
+
// (a 44.1kHz alternative exists, so the keep-lowest fallback declines).
|
|
339
|
+
capabilities.sampleRate = 44_100;
|
|
340
|
+
const timeline = {
|
|
341
|
+
periods: [
|
|
342
|
+
{
|
|
343
|
+
startTime: 0,
|
|
344
|
+
endTime: 100,
|
|
345
|
+
qualities: [
|
|
346
|
+
createQuality({
|
|
347
|
+
contentType: 'audio',
|
|
348
|
+
mimeType: 'audio/mp4',
|
|
349
|
+
qualityId: 'a44',
|
|
350
|
+
audioSamplingRate: [44_100],
|
|
351
|
+
}),
|
|
352
|
+
createQuality({
|
|
353
|
+
contentType: 'audio',
|
|
354
|
+
mimeType: 'audio/mp4',
|
|
355
|
+
qualityId: 'a96',
|
|
356
|
+
audioSamplingRate: [96_000],
|
|
357
|
+
}),
|
|
358
|
+
],
|
|
359
|
+
},
|
|
360
|
+
],
|
|
361
|
+
minBufferTime: 2,
|
|
362
|
+
getAdBreaks: () => Promise.resolve([]),
|
|
363
|
+
getDuration: () => Promise.resolve(Infinity),
|
|
364
|
+
};
|
|
365
|
+
const options = data({ audio: { disableSampleRateFilter: false } });
|
|
366
|
+
const transformed = createDefaultMediaTimelineTransformer({
|
|
367
|
+
capabilities,
|
|
368
|
+
drmController,
|
|
369
|
+
mediaTimeline: data(Promise.resolve(timeline)),
|
|
370
|
+
options,
|
|
371
|
+
});
|
|
372
|
+
const unsub = transformed.onData(() => { });
|
|
373
|
+
const ids = (t) => t.periods[0].qualities.map((q) => q.metadata.qualityId);
|
|
374
|
+
// Filter on: the 96kHz rendition is dropped.
|
|
375
|
+
expect(ids(await transformed.value)).toEqual(['a44']);
|
|
376
|
+
// Filter off: both renditions are kept.
|
|
377
|
+
options.value = { audio: { disableSampleRateFilter: true } };
|
|
378
|
+
expect(ids(await transformed.value)).toEqual(['a44', 'a96']);
|
|
379
|
+
unsub();
|
|
380
|
+
});
|
|
381
|
+
it('honors audio.maxSampleRate over the reported rate at runtime', async () => {
|
|
382
|
+
// Non-Firefox: Firefox hard-caps at 48kHz regardless of maxSampleRate.
|
|
383
|
+
setUserAgent('Chrome');
|
|
384
|
+
// AudioContext reports 48kHz, so the 96kHz rendition is dropped.
|
|
385
|
+
capabilities.sampleRate = 48_000;
|
|
386
|
+
const timeline = {
|
|
387
|
+
periods: [
|
|
388
|
+
{
|
|
389
|
+
startTime: 0,
|
|
390
|
+
endTime: 100,
|
|
391
|
+
qualities: [
|
|
392
|
+
createQuality({
|
|
393
|
+
contentType: 'audio',
|
|
394
|
+
mimeType: 'audio/mp4',
|
|
395
|
+
qualityId: 'a48',
|
|
396
|
+
audioSamplingRate: [48_000],
|
|
397
|
+
}),
|
|
398
|
+
createQuality({
|
|
399
|
+
contentType: 'audio',
|
|
400
|
+
mimeType: 'audio/mp4',
|
|
401
|
+
qualityId: 'a96',
|
|
402
|
+
audioSamplingRate: [96_000],
|
|
403
|
+
}),
|
|
404
|
+
],
|
|
405
|
+
},
|
|
406
|
+
],
|
|
407
|
+
minBufferTime: 2,
|
|
408
|
+
getAdBreaks: () => Promise.resolve([]),
|
|
409
|
+
getDuration: () => Promise.resolve(Infinity),
|
|
410
|
+
};
|
|
411
|
+
const options = data({ audio: {} });
|
|
412
|
+
const transformed = createDefaultMediaTimelineTransformer({
|
|
413
|
+
capabilities,
|
|
414
|
+
drmController,
|
|
415
|
+
mediaTimeline: data(Promise.resolve(timeline)),
|
|
416
|
+
options,
|
|
417
|
+
});
|
|
418
|
+
const unsub = transformed.onData(() => { });
|
|
419
|
+
const ids = (t) => t.periods[0].qualities.map((q) => q.metadata.qualityId);
|
|
420
|
+
expect(ids(await transformed.value)).toEqual(['a48']);
|
|
421
|
+
// Raise the cap to 96kHz: the 96kHz rendition is now kept.
|
|
422
|
+
options.value = { audio: { maxSampleRate: 96_000 } };
|
|
423
|
+
expect(ids(await transformed.value)).toEqual(['a48', 'a96']);
|
|
424
|
+
unsub();
|
|
425
|
+
});
|
|
426
|
+
it('applies audio.minSampleRate as a soft floor at runtime', async () => {
|
|
427
|
+
capabilities.sampleRate = 192_000; // max cap drops nothing
|
|
428
|
+
const timeline = {
|
|
429
|
+
periods: [
|
|
430
|
+
{
|
|
431
|
+
startTime: 0,
|
|
432
|
+
endTime: 100,
|
|
433
|
+
qualities: [
|
|
434
|
+
createQuality({
|
|
435
|
+
contentType: 'audio',
|
|
436
|
+
mimeType: 'audio/mp4',
|
|
437
|
+
qualityId: 'a48',
|
|
438
|
+
audioSamplingRate: [48_000],
|
|
439
|
+
}),
|
|
440
|
+
createQuality({
|
|
441
|
+
contentType: 'audio',
|
|
442
|
+
mimeType: 'audio/mp4',
|
|
443
|
+
qualityId: 'a24',
|
|
444
|
+
audioSamplingRate: [24_000],
|
|
445
|
+
}),
|
|
446
|
+
],
|
|
447
|
+
},
|
|
448
|
+
],
|
|
449
|
+
minBufferTime: 2,
|
|
450
|
+
getAdBreaks: () => Promise.resolve([]),
|
|
451
|
+
getDuration: () => Promise.resolve(Infinity),
|
|
452
|
+
};
|
|
453
|
+
const options = data({ audio: {} });
|
|
454
|
+
const transformed = createDefaultMediaTimelineTransformer({
|
|
455
|
+
capabilities,
|
|
456
|
+
drmController,
|
|
457
|
+
mediaTimeline: data(Promise.resolve(timeline)),
|
|
458
|
+
options,
|
|
459
|
+
});
|
|
460
|
+
const unsub = transformed.onData(() => { });
|
|
461
|
+
const ids = (t) => t.periods[0].qualities.map((q) => q.metadata.qualityId);
|
|
462
|
+
expect(ids(await transformed.value)).toEqual(['a48', 'a24']);
|
|
463
|
+
// Floor at 48kHz: the 24kHz rendition is dropped.
|
|
464
|
+
options.value = { audio: { minSampleRate: 48_000 } };
|
|
465
|
+
expect(ids(await transformed.value)).toEqual(['a48']);
|
|
466
|
+
unsub();
|
|
467
|
+
});
|
|
335
468
|
it('preserves minBufferTime', async () => {
|
|
336
469
|
const timeline = {
|
|
337
470
|
periods: [
|
|
@@ -13,81 +13,87 @@ function createQuality(contentType, bandwidth) {
|
|
|
13
13
|
getSegment: () => Promise.resolve(null),
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
periods: [
|
|
19
|
-
{
|
|
20
|
-
startTime: 0,
|
|
21
|
-
endTime: 10,
|
|
22
|
-
qualities: [
|
|
23
|
-
createQuality('audio', 128000),
|
|
24
|
-
createQuality('audio', 256000),
|
|
25
|
-
createQuality('video', 500000),
|
|
26
|
-
],
|
|
27
|
-
},
|
|
28
|
-
],
|
|
16
|
+
function timelineOf(qualities) {
|
|
17
|
+
return {
|
|
18
|
+
periods: [{ startTime: 0, endTime: 10, qualities: [...qualities] }],
|
|
29
19
|
minBufferTime: 2,
|
|
30
20
|
getAdBreaks: () => Promise.resolve([]),
|
|
31
21
|
getDuration: () => Promise.resolve(Infinity),
|
|
32
22
|
};
|
|
23
|
+
}
|
|
24
|
+
function contentTypesOf(timeline) {
|
|
25
|
+
return new Set(timeline.periods[0].qualities.map((q) => q.metadata.contentType));
|
|
26
|
+
}
|
|
27
|
+
function throwEmpty() {
|
|
28
|
+
throw new Error('no qualities');
|
|
29
|
+
}
|
|
30
|
+
describe('filterTimelineQualities', () => {
|
|
31
|
+
const timeline = timelineOf([
|
|
32
|
+
createQuality('audio', 128000),
|
|
33
|
+
createQuality('audio', 256000),
|
|
34
|
+
createQuality('video', 500000),
|
|
35
|
+
]);
|
|
33
36
|
it('returns the timeline unchanged when the filter is null', () => {
|
|
34
37
|
// A null predicate (e.g. no language preference) is a no-op.
|
|
35
|
-
const result = filterTimelineQualities(null,
|
|
36
|
-
throw new Error('empty');
|
|
37
|
-
}, timeline);
|
|
38
|
+
const result = filterTimelineQualities(null, throwEmpty, timeline);
|
|
38
39
|
expect(result).toBe(timeline);
|
|
39
40
|
});
|
|
40
|
-
it('filters qualities by predicate', () => {
|
|
41
|
-
const result = filterTimelineQualities((q) => q.
|
|
42
|
-
throw new Error('empty');
|
|
43
|
-
}, timeline);
|
|
41
|
+
it('filters qualities by predicate while keeping every present stream', () => {
|
|
42
|
+
const result = filterTimelineQualities((q) => q.bandwidth !== 256000, throwEmpty, timeline);
|
|
44
43
|
expect(result.periods[0].qualities.length).toBe(2);
|
|
45
|
-
expect(result.
|
|
44
|
+
expect(contentTypesOf(result)).toEqual(new Set(['audio', 'video']));
|
|
46
45
|
});
|
|
47
46
|
it('preserves minBufferTime', () => {
|
|
48
|
-
const result = filterTimelineQualities(() => true,
|
|
49
|
-
throw new Error('empty');
|
|
50
|
-
}, timeline);
|
|
47
|
+
const result = filterTimelineQualities(() => true, throwEmpty, timeline);
|
|
51
48
|
expect(result.minBufferTime).toBe(2);
|
|
52
49
|
});
|
|
53
50
|
it('throws when filtering removes all qualities', () => {
|
|
54
|
-
expect(() => filterTimelineQualities(() => false, ()
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
expect(() => filterTimelineQualities(() => false, throwEmpty, timeline)).toThrowError('no qualities');
|
|
52
|
+
});
|
|
53
|
+
it('throws when filtering drops a present video stream to zero', () => {
|
|
54
|
+
expect(() => filterTimelineQualities((q) => q.contentType === 'audio', throwEmpty, timeline)).toThrowError('no qualities');
|
|
55
|
+
});
|
|
56
|
+
it('throws when filtering drops a present audio stream to zero', () => {
|
|
57
|
+
expect(() => filterTimelineQualities((q) => q.contentType === 'video', throwEmpty, timeline)).toThrowError('no qualities');
|
|
58
|
+
});
|
|
59
|
+
it('does not throw when losing only an optional text sidecar stream', () => {
|
|
60
|
+
const withText = timelineOf([
|
|
61
|
+
createQuality('audio', 128000),
|
|
62
|
+
createQuality('video', 500000),
|
|
63
|
+
createQuality('text', 1000),
|
|
64
|
+
]);
|
|
65
|
+
const result = filterTimelineQualities((q) => q.contentType !== 'text', throwEmpty, withText);
|
|
66
|
+
expect(contentTypesOf(result)).toEqual(new Set(['audio', 'video']));
|
|
57
67
|
});
|
|
58
68
|
});
|
|
59
69
|
describe('filterTimelineQualitiesAsync', () => {
|
|
60
|
-
const timeline =
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
},
|
|
70
|
-
],
|
|
71
|
-
minBufferTime: 2,
|
|
72
|
-
getAdBreaks: () => Promise.resolve([]),
|
|
73
|
-
getDuration: () => Promise.resolve(Infinity),
|
|
74
|
-
};
|
|
75
|
-
it('filters qualities by async predicate', async () => {
|
|
76
|
-
const result = await filterTimelineQualitiesAsync((q) => Promise.resolve(q.contentType === 'audio'), () => {
|
|
77
|
-
throw new Error('empty');
|
|
78
|
-
}, timeline);
|
|
79
|
-
expect(result.periods[0].qualities.length).toBe(1);
|
|
80
|
-
expect(result.periods[0].qualities[0].metadata.contentType).toBe('audio');
|
|
70
|
+
const timeline = timelineOf([
|
|
71
|
+
createQuality('audio', 128000),
|
|
72
|
+
createQuality('audio', 256000),
|
|
73
|
+
createQuality('video', 500000),
|
|
74
|
+
]);
|
|
75
|
+
it('filters qualities by async predicate while keeping every present stream', async () => {
|
|
76
|
+
const result = await filterTimelineQualitiesAsync((q) => Promise.resolve(q.bandwidth !== 256000), throwEmpty, timeline);
|
|
77
|
+
expect(result.periods[0].qualities.length).toBe(2);
|
|
78
|
+
expect(contentTypesOf(result)).toEqual(new Set(['audio', 'video']));
|
|
81
79
|
});
|
|
82
80
|
it('preserves minBufferTime', async () => {
|
|
83
|
-
const result = await filterTimelineQualitiesAsync(() => Promise.resolve(true),
|
|
84
|
-
throw new Error('empty');
|
|
85
|
-
}, timeline);
|
|
81
|
+
const result = await filterTimelineQualitiesAsync(() => Promise.resolve(true), throwEmpty, timeline);
|
|
86
82
|
expect(result.minBufferTime).toBe(2);
|
|
87
83
|
});
|
|
88
84
|
it('throws when filtering removes all qualities', async () => {
|
|
89
|
-
await expectAsync(filterTimelineQualitiesAsync(() => Promise.resolve(false), ()
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
await expectAsync(filterTimelineQualitiesAsync(() => Promise.resolve(false), throwEmpty, timeline)).toBeRejectedWithError('no qualities');
|
|
86
|
+
});
|
|
87
|
+
it('throws when filtering drops a present video stream to zero', async () => {
|
|
88
|
+
await expectAsync(filterTimelineQualitiesAsync((q) => Promise.resolve(q.contentType === 'audio'), throwEmpty, timeline)).toBeRejectedWithError('no qualities');
|
|
89
|
+
});
|
|
90
|
+
it('does not throw when losing only an optional text sidecar stream', async () => {
|
|
91
|
+
const withText = timelineOf([
|
|
92
|
+
createQuality('audio', 128000),
|
|
93
|
+
createQuality('video', 500000),
|
|
94
|
+
createQuality('text', 1000),
|
|
95
|
+
]);
|
|
96
|
+
const result = await filterTimelineQualitiesAsync((q) => Promise.resolve(q.contentType !== 'text'), throwEmpty, withText);
|
|
97
|
+
expect(contentTypesOf(result)).toEqual(new Set(['audio', 'video']));
|
|
92
98
|
});
|
|
93
99
|
});
|