@amazon/vinyl 3.2.1 → 3.2.3-next.20261001143710
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.cjs +98 -43
- package/dist/index.cjs.map +3 -3
- package/dist/index.js +126 -68
- package/dist/index.js.map +3 -3
- package/dist/test/benchmark.es5.js +7 -6
- package/dist/test/benchmark.js +35 -30
- package/dist/test/benchmark.js.map +3 -3
- package/dist/test/integ.es5.js +329 -312
- package/dist/test/integ.js +519 -187
- package/dist/test/integ.js.map +4 -4
- package/dist/types/current/ad/AdControllerImpl.d.ts.map +1 -1
- package/dist/types/current/ad/AdControllerImpl.js +15 -1
- package/dist/types/current/ad/discoverHlsInterstitials.js +13 -1
- package/dist/types/current/drm/DrmControllerImpl.d.ts +6 -0
- package/dist/types/current/drm/DrmControllerImpl.d.ts.map +1 -1
- package/dist/types/current/drm/DrmControllerImpl.js +11 -1
- package/dist/types/current/drm/commonEme/MsCommonEme.d.ts.map +1 -1
- package/dist/types/current/drm/commonEme/MsCommonEme.js +2 -2
- package/dist/types/current/drm/commonEme/StandardCommonEme.d.ts.map +1 -1
- package/dist/types/current/drm/commonEme/StandardCommonEme.js +3 -2
- package/dist/types/current/drm/commonEme/WebKitCommonEme.d.ts.map +1 -1
- package/dist/types/current/drm/commonEme/WebKitCommonEme.js +2 -2
- package/dist/types/current/drm/dom/MSMediaKeys.d.ts +1 -1
- package/dist/types/current/drm/dom/MSMediaKeys.d.ts.map +1 -1
- package/dist/types/current/drm/dom/WebKitMediaKeys.d.ts +1 -1
- package/dist/types/current/drm/dom/WebKitMediaKeys.d.ts.map +1 -1
- package/dist/types/current/drm/util/extractContentId.js +2 -2
- package/dist/types/current/playback/PlaybackControllerImpl.d.ts +9 -3
- package/dist/types/current/playback/PlaybackControllerImpl.d.ts.map +1 -1
- package/dist/types/current/playback/PlaybackControllerImpl.js +38 -33
- package/dist/types/current/streaming/MediaQualityMetadata.d.ts +3 -1
- package/dist/types/current/streaming/MediaQualityMetadata.d.ts.map +1 -1
- package/dist/types/current/streaming/SegmentControllerImpl.d.ts +1 -1
- package/dist/types/current/streaming/buffering/BufferingController.d.ts +1 -1
- package/dist/types/current/streaming/buffering/BufferingController.d.ts.map +1 -1
- package/dist/types/current/streaming/buffering/BufferingController.js +14 -5
- package/dist/types/current/streaming/buffering/MediaSourceController.d.ts.map +1 -1
- package/dist/types/current/streaming/buffering/MediaSourceController.js +4 -0
- package/dist/types/current/text/discoverDashTextTracks.d.ts +2 -2
- package/dist/types/current/text/discoverDashTextTracks.js +4 -3
- package/dist/types/current/text/parseWebVtt.d.ts.map +1 -1
- package/dist/types/current/text/parseWebVtt.js +3 -2
- package/dist/types/current/track/TrackController.d.ts.map +1 -1
- package/dist/types/current/track/TrackController.js +7 -0
- package/dist/types/current/track/hls/HlsManifestData.d.ts +14 -1
- package/dist/types/current/track/hls/HlsManifestData.d.ts.map +1 -1
- package/dist/types/current/track/hls/buildHlsMediaTimeline.d.ts.map +1 -1
- package/dist/types/current/track/hls/buildHlsMediaTimeline.js +10 -2
- package/dist/types/current/track/hls/createUrlHlsManifestProvider.d.ts +3 -2
- package/dist/types/current/track/hls/createUrlHlsManifestProvider.d.ts.map +1 -1
- package/dist/types/current/track/hls/createUrlHlsManifestProvider.js +8 -3
- package/dist/types/current/track/mse/MseTrack.d.ts +0 -1
- package/dist/types/current/track/mse/MseTrack.d.ts.map +1 -1
- package/dist/types/current/track/mse/MseTrack.js +5 -6
- package/dist/types/current/tsconfig.tsbuildinfo +1 -1
- package/dist/types/ts3.7/drm/DrmControllerImpl.d.ts +6 -0
- package/dist/types/ts3.7/playback/PlaybackControllerImpl.d.ts +9 -3
- package/dist/types/ts3.7/streaming/MediaQualityMetadata.d.ts +3 -1
- package/dist/types/ts3.7/streaming/SegmentControllerImpl.d.ts +1 -1
- package/dist/types/ts3.7/streaming/buffering/BufferingController.d.ts +1 -1
- package/dist/types/ts3.7/text/discoverDashTextTracks.d.ts +2 -2
- package/dist/types/ts3.7/track/hls/HlsManifestData.d.ts +14 -1
- package/dist/types/ts3.7/track/hls/createUrlHlsManifestProvider.d.ts +3 -2
- package/dist/types/ts3.7/track/mse/MseTrack.d.ts +0 -1
- package/dist/types/ts4.1/drm/DrmControllerImpl.d.ts +6 -0
- package/dist/types/ts4.1/playback/PlaybackControllerImpl.d.ts +9 -3
- package/dist/types/ts4.1/streaming/MediaQualityMetadata.d.ts +3 -1
- package/dist/types/ts4.1/streaming/SegmentControllerImpl.d.ts +1 -1
- package/dist/types/ts4.1/streaming/buffering/BufferingController.d.ts +1 -1
- package/dist/types/ts4.1/text/discoverDashTextTracks.d.ts +2 -2
- package/dist/types/ts4.1/track/hls/HlsManifestData.d.ts +14 -1
- package/dist/types/ts4.1/track/hls/createUrlHlsManifestProvider.d.ts +3 -2
- package/dist/types/ts4.1/track/mse/MseTrack.d.ts +0 -1
- package/dist/types/ts4.7/drm/DrmControllerImpl.d.ts +6 -0
- package/dist/types/ts4.7/playback/PlaybackControllerImpl.d.ts +9 -3
- package/dist/types/ts4.7/streaming/MediaQualityMetadata.d.ts +3 -1
- package/dist/types/ts4.7/streaming/SegmentControllerImpl.d.ts +1 -1
- package/dist/types/ts4.7/streaming/buffering/BufferingController.d.ts +1 -1
- package/dist/types/ts4.7/text/discoverDashTextTracks.d.ts +2 -2
- package/dist/types/ts4.7/track/hls/HlsManifestData.d.ts +14 -1
- package/dist/types/ts4.7/track/hls/createUrlHlsManifestProvider.d.ts +3 -2
- package/dist/types/ts4.7/track/mse/MseTrack.d.ts +0 -1
- package/dist/unit/types/current/ad/AdControllerImpl.test.js +66 -0
- package/dist/unit/types/current/ad/discoverHlsInterstitials.test.js +29 -2
- package/dist/unit/types/current/drm/DrmControllerImpl.test.js +19 -1
- package/dist/unit/types/current/drm/commonEme/StandardCommonEme.test.js +12 -4
- package/dist/unit/types/current/drm/util/extractContentId.test.js +7 -0
- package/dist/unit/types/current/index.d.ts +1 -0
- package/dist/unit/types/current/index.d.ts.map +1 -1
- package/dist/unit/types/current/index.js +1 -0
- package/dist/unit/types/current/playback/PlaybackControllerImpl.test.js +15 -0
- package/dist/unit/types/current/streaming/MediaQualityMetadata.test.js +8 -0
- package/dist/unit/types/current/streaming/MseTrack.test.js +15 -0
- package/dist/unit/types/current/streaming/buffering/BufferingController.test.js +26 -0
- package/dist/unit/types/current/streaming/buffering/MediaSourceController.test.js +29 -0
- package/dist/unit/types/current/text/TextTrack.test.d.ts +2 -0
- package/dist/unit/types/current/text/TextTrack.test.d.ts.map +1 -0
- package/dist/unit/types/current/text/TextTrack.test.js +12 -0
- package/dist/unit/types/current/text/discoverDashTextTracks.test.js +17 -0
- package/dist/unit/types/current/text/parseWebVtt.test.js +26 -0
- package/dist/unit/types/current/track/TrackController.test.js +15 -60
- package/dist/unit/types/current/track/hls/buildHlsMediaTimeline.test.js +22 -0
- package/dist/unit/types/current/track/hls/createUrlHlsManifestProvider.test.js +23 -0
- package/dist/unit/types/current/tsconfig.tsbuildinfo +1 -1
- package/dist/vinylTestUtil/types/current/tsconfig.tsbuildinfo +1 -1
- package/docs/CUSTOMIZATION_GUIDE.md +310 -0
- package/package.json +10 -10
- package/docs/DASH_MANIFEST_TRANSFORM_GUIDE.md +0 -236
|
@@ -12,6 +12,12 @@ import { TrackUri } from '../track/Track';
|
|
|
12
12
|
* indicates a stalled promise result from the license provider.
|
|
13
13
|
*/
|
|
14
14
|
export declare const LICENSE_TIMEOUT = 90;
|
|
15
|
+
/**
|
|
16
|
+
* An excessive number of concurrent key sessions. Sessions are expected to be
|
|
17
|
+
* closed as tracks deactivate; a count this high signals sessions are leaking
|
|
18
|
+
* (never closing) rather than a legitimate playback need.
|
|
19
|
+
*/
|
|
20
|
+
export declare const EXCESSIVE_SESSION_COUNT = 20;
|
|
15
21
|
/**
|
|
16
22
|
* Dependencies for creating DrmController.
|
|
17
23
|
*/
|
|
@@ -15,7 +15,7 @@ export interface PlaybackControllerImplOptions {
|
|
|
15
15
|
* The minimum number of seconds before the end of the next seekable range allowed for a seek
|
|
16
16
|
* operation. Some browsers will never complete a seek operation if the seek is too close to
|
|
17
17
|
* this end range.
|
|
18
|
-
* Default: getMinSeekableBufferDefault() which is
|
|
18
|
+
* Default: getMinSeekableBufferDefault(), which is 2 on Chromium and 5 elsewhere.
|
|
19
19
|
*/
|
|
20
20
|
readonly minSeekableBuffer: number;
|
|
21
21
|
/**
|
|
@@ -63,12 +63,11 @@ export declare class PlaybackControllerImpl extends EventHostImpl<PlaybackContro
|
|
|
63
63
|
private _playing;
|
|
64
64
|
private _seeking;
|
|
65
65
|
private _waiting;
|
|
66
|
-
private
|
|
66
|
+
private readonly seekAbortSlot;
|
|
67
67
|
private stopStallDetection;
|
|
68
68
|
private userVolume;
|
|
69
69
|
private _pendingPlay;
|
|
70
70
|
private playAbort;
|
|
71
|
-
private disposeAbort;
|
|
72
71
|
readonly options: PlaybackControllerImplOptions;
|
|
73
72
|
constructor(deps: PlaybackControllerImplDeps, options?: Partial<PlaybackControllerImplOptions>);
|
|
74
73
|
/**
|
|
@@ -174,6 +173,13 @@ export declare class PlaybackControllerImpl extends EventHostImpl<PlaybackContro
|
|
|
174
173
|
get hasMetadata(): boolean;
|
|
175
174
|
get playIsPending(): boolean;
|
|
176
175
|
seekTo(time: number, tolerance?: number): Promise<void>;
|
|
176
|
+
/**
|
|
177
|
+
* The body of a single {@link seekTo} attempt. Extracted so {@link seekTo}
|
|
178
|
+
* can wrap it with per-seek abort/supersession handling. `seekAbort` is
|
|
179
|
+
* aborted when a newer seek supersedes this one (or on dispose).
|
|
180
|
+
* @private
|
|
181
|
+
*/
|
|
182
|
+
private doSeek;
|
|
177
183
|
/**
|
|
178
184
|
* Dispatches a readyStateChange event when {@link readyState} changes.
|
|
179
185
|
* @private
|
|
@@ -148,7 +148,9 @@ export interface MediaQualityMetadata extends MediaFormatMetadata {
|
|
|
148
148
|
* An array restricts switching to only the listed group IDs.
|
|
149
149
|
* Default behavior:
|
|
150
150
|
* - DASH: restricted to own group unless adaptation-set-switching property is present.
|
|
151
|
-
* - HLS:
|
|
151
|
+
* - HLS: unconstrained (null). Every variant is one group, and renditions are
|
|
152
|
+
* selected by the media timeline's language and accessibility filters
|
|
153
|
+
* rather than by a switching group.
|
|
152
154
|
*/
|
|
153
155
|
readonly switchingGroupIds: readonly string[] | null;
|
|
154
156
|
}
|
|
@@ -18,7 +18,7 @@ export interface SegmentControllerImplDeps {
|
|
|
18
18
|
export interface SegmentControllerImplOptions {
|
|
19
19
|
/**
|
|
20
20
|
* The amount of time ahead of the playhead to prefetch when the track is active.
|
|
21
|
-
* Default:
|
|
21
|
+
* Default: 180
|
|
22
22
|
*/
|
|
23
23
|
readonly prefetchActive: number;
|
|
24
24
|
/**
|
|
@@ -119,7 +119,7 @@ export interface BufferingControllerImplOptions {
|
|
|
119
119
|
readonly minBuffer: number;
|
|
120
120
|
/**
|
|
121
121
|
* The maximum number of bytes to append at a time per content type.
|
|
122
|
-
* Default: { audio:
|
|
122
|
+
* Default: { audio: 10MiB, video: 150MiB }
|
|
123
123
|
*/
|
|
124
124
|
readonly maxAppendSize: Partial<Record<ContentType, number>>;
|
|
125
125
|
}
|
|
@@ -5,8 +5,8 @@ import { TextTrackInfo } from './TextTrack';
|
|
|
5
5
|
*
|
|
6
6
|
* Considers an `<AdaptationSet>` to carry text when one of:
|
|
7
7
|
* - `contentType="text"`
|
|
8
|
-
* - `mimeType
|
|
9
|
-
*
|
|
8
|
+
* - its `mimeType`, or that of one of its `<Representation>`s, starts with
|
|
9
|
+
* `text/` (e.g. `text/vtt`)
|
|
10
10
|
*
|
|
11
11
|
* Within a text adaptation set, each `<Representation>` becomes one
|
|
12
12
|
* {@link TextTrackInfo}. The `BaseURL` chain (MPD → Period → AdaptationSet →
|
|
@@ -14,6 +14,19 @@ export interface HlsManifestData {
|
|
|
14
14
|
/**
|
|
15
15
|
* Lazily fetches and caches a media playlist by variant URI.
|
|
16
16
|
*/
|
|
17
|
-
readonly getMediaPlaylist: (uri: string) => Promise<
|
|
17
|
+
readonly getMediaPlaylist: (uri: string) => Promise<HlsMediaPlaylistData>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A media playlist with the URL its relative URIs resolve against.
|
|
21
|
+
*/
|
|
22
|
+
export interface HlsMediaPlaylistData extends HlsMediaPlaylist {
|
|
23
|
+
/**
|
|
24
|
+
* The media playlist's URL after redirects, against which its segment, map,
|
|
25
|
+
* and date range URIs resolve. Guaranteed to be absolute when set.
|
|
26
|
+
*
|
|
27
|
+
* When absent, the variant URI resolved against
|
|
28
|
+
* {@link HlsManifestData.baseUrl} is used instead.
|
|
29
|
+
*/
|
|
30
|
+
readonly baseUrl?: string;
|
|
18
31
|
}
|
|
19
32
|
//# sourceMappingURL=HlsManifestData.d.ts.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { HlsMainPlaylist
|
|
1
|
+
import { HlsMainPlaylist } from '@amazon/vinyl-hls-parser';
|
|
2
2
|
import { Maybe, ReadonlyAbort, RequestInitOptions } from '@amazon/vinyl-util';
|
|
3
|
+
import { HlsMediaPlaylistData } from './HlsManifestData';
|
|
3
4
|
import { HlsManifestProvider } from './createHlsManifestProvider';
|
|
4
5
|
export declare function createUrlHlsManifestProvider(url: string, requestInit?: RequestInitOptions): HlsManifestProvider;
|
|
5
6
|
export interface FetchMediaPlaylistOptions {
|
|
@@ -17,5 +18,5 @@ export interface FetchMediaPlaylistOptions {
|
|
|
17
18
|
/**
|
|
18
19
|
* Fetches and parses an HLS media playlist from a variant URI.
|
|
19
20
|
*/
|
|
20
|
-
export declare function fetchMediaPlaylist(options: FetchMediaPlaylistOptions): Promise<
|
|
21
|
+
export declare function fetchMediaPlaylist(options: FetchMediaPlaylistOptions): Promise<HlsMediaPlaylistData>;
|
|
21
22
|
//# sourceMappingURL=createUrlHlsManifestProvider.d.ts.map
|
|
@@ -45,7 +45,6 @@ export declare class MseTrack extends TrackBase {
|
|
|
45
45
|
protected readonly deps: MseTrackDeps;
|
|
46
46
|
get textTrackController(): (TextTrackController | null);
|
|
47
47
|
private readonly streams;
|
|
48
|
-
private readonly disposeAbort;
|
|
49
48
|
private lastPreloadOptions;
|
|
50
49
|
private readonly allFetchedRanges;
|
|
51
50
|
private readonly _fetchedRanges;
|
|
@@ -13,6 +13,12 @@ import type { TrackUri } from '../track/Track';
|
|
|
13
13
|
* indicates a stalled promise result from the license provider.
|
|
14
14
|
*/
|
|
15
15
|
export declare const LICENSE_TIMEOUT = 90;
|
|
16
|
+
/**
|
|
17
|
+
* An excessive number of concurrent key sessions. Sessions are expected to be
|
|
18
|
+
* closed as tracks deactivate; a count this high signals sessions are leaking
|
|
19
|
+
* (never closing) rather than a legitimate playback need.
|
|
20
|
+
*/
|
|
21
|
+
export declare const EXCESSIVE_SESSION_COUNT = 20;
|
|
16
22
|
/**
|
|
17
23
|
* Dependencies for creating DrmController.
|
|
18
24
|
*/
|
|
@@ -17,7 +17,7 @@ export interface PlaybackControllerImplOptions {
|
|
|
17
17
|
* The minimum number of seconds before the end of the next seekable range allowed for a seek
|
|
18
18
|
* operation. Some browsers will never complete a seek operation if the seek is too close to
|
|
19
19
|
* this end range.
|
|
20
|
-
* Default: getMinSeekableBufferDefault() which is
|
|
20
|
+
* Default: getMinSeekableBufferDefault(), which is 2 on Chromium and 5 elsewhere.
|
|
21
21
|
*/
|
|
22
22
|
readonly minSeekableBuffer: number;
|
|
23
23
|
/**
|
|
@@ -65,12 +65,11 @@ export declare class PlaybackControllerImpl extends EventHostImpl<PlaybackContro
|
|
|
65
65
|
private _playing;
|
|
66
66
|
private _seeking;
|
|
67
67
|
private _waiting;
|
|
68
|
-
private
|
|
68
|
+
private readonly seekAbortSlot;
|
|
69
69
|
private stopStallDetection;
|
|
70
70
|
private userVolume;
|
|
71
71
|
private _pendingPlay;
|
|
72
72
|
private playAbort;
|
|
73
|
-
private disposeAbort;
|
|
74
73
|
readonly options: PlaybackControllerImplOptions;
|
|
75
74
|
constructor(deps: PlaybackControllerImplDeps, options?: Partial<PlaybackControllerImplOptions>);
|
|
76
75
|
/**
|
|
@@ -176,6 +175,13 @@ export declare class PlaybackControllerImpl extends EventHostImpl<PlaybackContro
|
|
|
176
175
|
get hasMetadata(): boolean;
|
|
177
176
|
get playIsPending(): boolean;
|
|
178
177
|
seekTo(time: number, tolerance?: number): Promise<void>;
|
|
178
|
+
/**
|
|
179
|
+
* The body of a single {@link seekTo} attempt. Extracted so {@link seekTo}
|
|
180
|
+
* can wrap it with per-seek abort/supersession handling. `seekAbort` is
|
|
181
|
+
* aborted when a newer seek supersedes this one (or on dispose).
|
|
182
|
+
* @private
|
|
183
|
+
*/
|
|
184
|
+
private doSeek;
|
|
179
185
|
/**
|
|
180
186
|
* Dispatches a readyStateChange event when {@link readyState} changes.
|
|
181
187
|
* @private
|
|
@@ -148,7 +148,9 @@ export interface MediaQualityMetadata extends MediaFormatMetadata {
|
|
|
148
148
|
* An array restricts switching to only the listed group IDs.
|
|
149
149
|
* Default behavior:
|
|
150
150
|
* - DASH: restricted to own group unless adaptation-set-switching property is present.
|
|
151
|
-
* - HLS:
|
|
151
|
+
* - HLS: unconstrained (null). Every variant is one group, and renditions are
|
|
152
|
+
* selected by the media timeline's language and accessibility filters
|
|
153
|
+
* rather than by a switching group.
|
|
152
154
|
*/
|
|
153
155
|
readonly switchingGroupIds: readonly string[] | null;
|
|
154
156
|
}
|
|
@@ -19,7 +19,7 @@ export interface SegmentControllerImplDeps {
|
|
|
19
19
|
export interface SegmentControllerImplOptions {
|
|
20
20
|
/**
|
|
21
21
|
* The amount of time ahead of the playhead to prefetch when the track is active.
|
|
22
|
-
* Default:
|
|
22
|
+
* Default: 180
|
|
23
23
|
*/
|
|
24
24
|
readonly prefetchActive: number;
|
|
25
25
|
/**
|
|
@@ -120,7 +120,7 @@ export interface BufferingControllerImplOptions {
|
|
|
120
120
|
readonly minBuffer: number;
|
|
121
121
|
/**
|
|
122
122
|
* The maximum number of bytes to append at a time per content type.
|
|
123
|
-
* Default: { audio:
|
|
123
|
+
* Default: { audio: 10MiB, video: 150MiB }
|
|
124
124
|
*/
|
|
125
125
|
readonly maxAppendSize: Partial<Record<ContentType, number>>;
|
|
126
126
|
}
|
|
@@ -5,8 +5,8 @@ import type { TextTrackInfo } from './TextTrack';
|
|
|
5
5
|
*
|
|
6
6
|
* Considers an `<AdaptationSet>` to carry text when one of:
|
|
7
7
|
* - `contentType="text"`
|
|
8
|
-
* - `mimeType
|
|
9
|
-
*
|
|
8
|
+
* - its `mimeType`, or that of one of its `<Representation>`s, starts with
|
|
9
|
+
* `text/` (e.g. `text/vtt`)
|
|
10
10
|
*
|
|
11
11
|
* Within a text adaptation set, each `<Representation>` becomes one
|
|
12
12
|
* {@link TextTrackInfo}. The `BaseURL` chain (MPD → Period → AdaptationSet →
|
|
@@ -14,6 +14,19 @@ export interface HlsManifestData {
|
|
|
14
14
|
/**
|
|
15
15
|
* Lazily fetches and caches a media playlist by variant URI.
|
|
16
16
|
*/
|
|
17
|
-
readonly getMediaPlaylist: (uri: string) => Promise<
|
|
17
|
+
readonly getMediaPlaylist: (uri: string) => Promise<HlsMediaPlaylistData>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A media playlist with the URL its relative URIs resolve against.
|
|
21
|
+
*/
|
|
22
|
+
export interface HlsMediaPlaylistData extends HlsMediaPlaylist {
|
|
23
|
+
/**
|
|
24
|
+
* The media playlist's URL after redirects, against which its segment, map,
|
|
25
|
+
* and date range URIs resolve. Guaranteed to be absolute when set.
|
|
26
|
+
*
|
|
27
|
+
* When absent, the variant URI resolved against
|
|
28
|
+
* {@link HlsManifestData.baseUrl} is used instead.
|
|
29
|
+
*/
|
|
30
|
+
readonly baseUrl?: string;
|
|
18
31
|
}
|
|
19
32
|
//# sourceMappingURL=HlsManifestData.d.ts.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { HlsMainPlaylist
|
|
1
|
+
import type { HlsMainPlaylist } from '@amazon/vinyl-hls-parser';
|
|
2
2
|
import type { Maybe, ReadonlyAbort, RequestInitOptions } from '@amazon/vinyl-util';
|
|
3
|
+
import type { HlsMediaPlaylistData } from './HlsManifestData';
|
|
3
4
|
import type { HlsManifestProvider } from './createHlsManifestProvider';
|
|
4
5
|
export declare function createUrlHlsManifestProvider(url: string, requestInit?: RequestInitOptions): HlsManifestProvider;
|
|
5
6
|
export interface FetchMediaPlaylistOptions {
|
|
@@ -17,5 +18,5 @@ export interface FetchMediaPlaylistOptions {
|
|
|
17
18
|
/**
|
|
18
19
|
* Fetches and parses an HLS media playlist from a variant URI.
|
|
19
20
|
*/
|
|
20
|
-
export declare function fetchMediaPlaylist(options: FetchMediaPlaylistOptions): Promise<
|
|
21
|
+
export declare function fetchMediaPlaylist(options: FetchMediaPlaylistOptions): Promise<HlsMediaPlaylistData>;
|
|
21
22
|
//# sourceMappingURL=createUrlHlsManifestProvider.d.ts.map
|
|
@@ -46,7 +46,6 @@ export declare class MseTrack extends TrackBase {
|
|
|
46
46
|
protected readonly deps: MseTrackDeps;
|
|
47
47
|
get textTrackController(): (TextTrackController | null);
|
|
48
48
|
private readonly streams;
|
|
49
|
-
private readonly disposeAbort;
|
|
50
49
|
private lastPreloadOptions;
|
|
51
50
|
private readonly allFetchedRanges;
|
|
52
51
|
private readonly _fetchedRanges;
|
|
@@ -12,6 +12,12 @@ import type { TrackUri } from '../track/Track';
|
|
|
12
12
|
* indicates a stalled promise result from the license provider.
|
|
13
13
|
*/
|
|
14
14
|
export declare const LICENSE_TIMEOUT = 90;
|
|
15
|
+
/**
|
|
16
|
+
* An excessive number of concurrent key sessions. Sessions are expected to be
|
|
17
|
+
* closed as tracks deactivate; a count this high signals sessions are leaking
|
|
18
|
+
* (never closing) rather than a legitimate playback need.
|
|
19
|
+
*/
|
|
20
|
+
export declare const EXCESSIVE_SESSION_COUNT = 20;
|
|
15
21
|
/**
|
|
16
22
|
* Dependencies for creating DrmController.
|
|
17
23
|
*/
|
|
@@ -15,7 +15,7 @@ export interface PlaybackControllerImplOptions {
|
|
|
15
15
|
* The minimum number of seconds before the end of the next seekable range allowed for a seek
|
|
16
16
|
* operation. Some browsers will never complete a seek operation if the seek is too close to
|
|
17
17
|
* this end range.
|
|
18
|
-
* Default: getMinSeekableBufferDefault() which is
|
|
18
|
+
* Default: getMinSeekableBufferDefault(), which is 2 on Chromium and 5 elsewhere.
|
|
19
19
|
*/
|
|
20
20
|
readonly minSeekableBuffer: number;
|
|
21
21
|
/**
|
|
@@ -63,12 +63,11 @@ export declare class PlaybackControllerImpl extends EventHostImpl<PlaybackContro
|
|
|
63
63
|
private _playing;
|
|
64
64
|
private _seeking;
|
|
65
65
|
private _waiting;
|
|
66
|
-
private
|
|
66
|
+
private readonly seekAbortSlot;
|
|
67
67
|
private stopStallDetection;
|
|
68
68
|
private userVolume;
|
|
69
69
|
private _pendingPlay;
|
|
70
70
|
private playAbort;
|
|
71
|
-
private disposeAbort;
|
|
72
71
|
readonly options: PlaybackControllerImplOptions;
|
|
73
72
|
constructor(deps: PlaybackControllerImplDeps, options?: Partial<PlaybackControllerImplOptions>);
|
|
74
73
|
/**
|
|
@@ -174,6 +173,13 @@ export declare class PlaybackControllerImpl extends EventHostImpl<PlaybackContro
|
|
|
174
173
|
get hasMetadata(): boolean;
|
|
175
174
|
get playIsPending(): boolean;
|
|
176
175
|
seekTo(time: number, tolerance?: number): Promise<void>;
|
|
176
|
+
/**
|
|
177
|
+
* The body of a single {@link seekTo} attempt. Extracted so {@link seekTo}
|
|
178
|
+
* can wrap it with per-seek abort/supersession handling. `seekAbort` is
|
|
179
|
+
* aborted when a newer seek supersedes this one (or on dispose).
|
|
180
|
+
* @private
|
|
181
|
+
*/
|
|
182
|
+
private doSeek;
|
|
177
183
|
/**
|
|
178
184
|
* Dispatches a readyStateChange event when {@link readyState} changes.
|
|
179
185
|
* @private
|
|
@@ -148,7 +148,9 @@ export interface MediaQualityMetadata extends MediaFormatMetadata {
|
|
|
148
148
|
* An array restricts switching to only the listed group IDs.
|
|
149
149
|
* Default behavior:
|
|
150
150
|
* - DASH: restricted to own group unless adaptation-set-switching property is present.
|
|
151
|
-
* - HLS:
|
|
151
|
+
* - HLS: unconstrained (null). Every variant is one group, and renditions are
|
|
152
|
+
* selected by the media timeline's language and accessibility filters
|
|
153
|
+
* rather than by a switching group.
|
|
152
154
|
*/
|
|
153
155
|
readonly switchingGroupIds: readonly string[] | null;
|
|
154
156
|
}
|
|
@@ -18,7 +18,7 @@ export interface SegmentControllerImplDeps {
|
|
|
18
18
|
export interface SegmentControllerImplOptions {
|
|
19
19
|
/**
|
|
20
20
|
* The amount of time ahead of the playhead to prefetch when the track is active.
|
|
21
|
-
* Default:
|
|
21
|
+
* Default: 180
|
|
22
22
|
*/
|
|
23
23
|
readonly prefetchActive: number;
|
|
24
24
|
/**
|
|
@@ -119,7 +119,7 @@ export interface BufferingControllerImplOptions {
|
|
|
119
119
|
readonly minBuffer: number;
|
|
120
120
|
/**
|
|
121
121
|
* The maximum number of bytes to append at a time per content type.
|
|
122
|
-
* Default: { audio:
|
|
122
|
+
* Default: { audio: 10MiB, video: 150MiB }
|
|
123
123
|
*/
|
|
124
124
|
readonly maxAppendSize: Partial<Record<ContentType, number>>;
|
|
125
125
|
}
|
|
@@ -5,8 +5,8 @@ import type { TextTrackInfo } from './TextTrack';
|
|
|
5
5
|
*
|
|
6
6
|
* Considers an `<AdaptationSet>` to carry text when one of:
|
|
7
7
|
* - `contentType="text"`
|
|
8
|
-
* - `mimeType
|
|
9
|
-
*
|
|
8
|
+
* - its `mimeType`, or that of one of its `<Representation>`s, starts with
|
|
9
|
+
* `text/` (e.g. `text/vtt`)
|
|
10
10
|
*
|
|
11
11
|
* Within a text adaptation set, each `<Representation>` becomes one
|
|
12
12
|
* {@link TextTrackInfo}. The `BaseURL` chain (MPD → Period → AdaptationSet →
|
|
@@ -14,6 +14,19 @@ export interface HlsManifestData {
|
|
|
14
14
|
/**
|
|
15
15
|
* Lazily fetches and caches a media playlist by variant URI.
|
|
16
16
|
*/
|
|
17
|
-
readonly getMediaPlaylist: (uri: string) => Promise<
|
|
17
|
+
readonly getMediaPlaylist: (uri: string) => Promise<HlsMediaPlaylistData>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A media playlist with the URL its relative URIs resolve against.
|
|
21
|
+
*/
|
|
22
|
+
export interface HlsMediaPlaylistData extends HlsMediaPlaylist {
|
|
23
|
+
/**
|
|
24
|
+
* The media playlist's URL after redirects, against which its segment, map,
|
|
25
|
+
* and date range URIs resolve. Guaranteed to be absolute when set.
|
|
26
|
+
*
|
|
27
|
+
* When absent, the variant URI resolved against
|
|
28
|
+
* {@link HlsManifestData.baseUrl} is used instead.
|
|
29
|
+
*/
|
|
30
|
+
readonly baseUrl?: string;
|
|
18
31
|
}
|
|
19
32
|
//# sourceMappingURL=HlsManifestData.d.ts.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { HlsMainPlaylist
|
|
1
|
+
import type { HlsMainPlaylist } from '@amazon/vinyl-hls-parser';
|
|
2
2
|
import type { Maybe, ReadonlyAbort, RequestInitOptions } from '@amazon/vinyl-util';
|
|
3
|
+
import type { HlsMediaPlaylistData } from './HlsManifestData';
|
|
3
4
|
import type { HlsManifestProvider } from './createHlsManifestProvider';
|
|
4
5
|
export declare function createUrlHlsManifestProvider(url: string, requestInit?: RequestInitOptions): HlsManifestProvider;
|
|
5
6
|
export interface FetchMediaPlaylistOptions {
|
|
@@ -17,5 +18,5 @@ export interface FetchMediaPlaylistOptions {
|
|
|
17
18
|
/**
|
|
18
19
|
* Fetches and parses an HLS media playlist from a variant URI.
|
|
19
20
|
*/
|
|
20
|
-
export declare function fetchMediaPlaylist(options: FetchMediaPlaylistOptions): Promise<
|
|
21
|
+
export declare function fetchMediaPlaylist(options: FetchMediaPlaylistOptions): Promise<HlsMediaPlaylistData>;
|
|
21
22
|
//# sourceMappingURL=createUrlHlsManifestProvider.d.ts.map
|
|
@@ -45,7 +45,6 @@ export declare class MseTrack extends TrackBase {
|
|
|
45
45
|
protected readonly deps: MseTrackDeps;
|
|
46
46
|
get textTrackController(): TextTrackController | null;
|
|
47
47
|
private readonly streams;
|
|
48
|
-
private readonly disposeAbort;
|
|
49
48
|
private lastPreloadOptions;
|
|
50
49
|
private readonly allFetchedRanges;
|
|
51
50
|
private readonly _fetchedRanges;
|
|
@@ -526,6 +526,44 @@ describe('AdControllerImpl', () => {
|
|
|
526
526
|
await flush();
|
|
527
527
|
expect(c.currentAdBreak).toBeNull();
|
|
528
528
|
});
|
|
529
|
+
it('still enters a pending break when skipped while its ads resolve', async () => {
|
|
530
|
+
const c = createController();
|
|
531
|
+
let resolveAds = () => undefined;
|
|
532
|
+
const entered = [];
|
|
533
|
+
await setContent(c, trackAds(makeBreak({
|
|
534
|
+
id: 'b1',
|
|
535
|
+
startTime: 0,
|
|
536
|
+
duration: 30,
|
|
537
|
+
ads: () => new Promise((resolve) => {
|
|
538
|
+
resolveAds = resolve;
|
|
539
|
+
}),
|
|
540
|
+
}), makeBreak({
|
|
541
|
+
id: 'b2',
|
|
542
|
+
startTime: 1,
|
|
543
|
+
duration: 30,
|
|
544
|
+
ads: [
|
|
545
|
+
{
|
|
546
|
+
id: 'a2',
|
|
547
|
+
startTime: 1,
|
|
548
|
+
duration: 10,
|
|
549
|
+
uri: 'ad2.m3u8',
|
|
550
|
+
},
|
|
551
|
+
],
|
|
552
|
+
})));
|
|
553
|
+
c.on('adBreakEntered', (e) => entered.push(e.adBreak.id));
|
|
554
|
+
// Both breaks contain the playhead, so both become pending and the
|
|
555
|
+
// first is entered while its ad list is still in flight.
|
|
556
|
+
updateTime(2);
|
|
557
|
+
await flush();
|
|
558
|
+
expect(entered).toEqual(['b1']);
|
|
559
|
+
c.skipAdBreak();
|
|
560
|
+
resolveAds([]);
|
|
561
|
+
await flush();
|
|
562
|
+
// Nothing else drains the pending list, and a non-empty list blocks
|
|
563
|
+
// further midroll ingress, so b2 must be entered here.
|
|
564
|
+
expect(entered).toEqual(['b1', 'b2']);
|
|
565
|
+
expect(c.currentAdBreak?.id).toBe('b2');
|
|
566
|
+
});
|
|
529
567
|
});
|
|
530
568
|
describe('a break with no ads', () => {
|
|
531
569
|
it('completes immediately with a resume position and plays no ad', async () => {
|
|
@@ -753,6 +791,34 @@ describe('AdControllerImpl', () => {
|
|
|
753
791
|
await flush();
|
|
754
792
|
expect(thirds).toEqual(['a1']);
|
|
755
793
|
});
|
|
794
|
+
it('dispatches each quartile at most once per ad', async () => {
|
|
795
|
+
const c = createController();
|
|
796
|
+
const events = [];
|
|
797
|
+
c.on('adFirstQuartile', () => events.push('first'));
|
|
798
|
+
c.on('adMidpoint', () => events.push('mid'));
|
|
799
|
+
c.on('adThirdQuartile', () => events.push('third'));
|
|
800
|
+
await setContent(c, trackAds(makeBreak({ startTime: 0, duration: 10 })));
|
|
801
|
+
updateTime(0);
|
|
802
|
+
await flush();
|
|
803
|
+
playbackController.dispatch('playing', {});
|
|
804
|
+
playbackController.duration = 10;
|
|
805
|
+
playbackController.playbackRate = 1;
|
|
806
|
+
// Every subsequent timeUpdate is still past each threshold, so an
|
|
807
|
+
// unlatched check would report the quartile again.
|
|
808
|
+
for (const [percent, time] of [
|
|
809
|
+
[0.3, 3],
|
|
810
|
+
[0.4, 4],
|
|
811
|
+
[0.6, 6],
|
|
812
|
+
[0.7, 7],
|
|
813
|
+
[0.8, 8],
|
|
814
|
+
[0.9, 9],
|
|
815
|
+
]) {
|
|
816
|
+
playbackController.currentTimePercent = percent;
|
|
817
|
+
updateTime(time);
|
|
818
|
+
await flush();
|
|
819
|
+
}
|
|
820
|
+
expect(events).toEqual(['first', 'mid', 'third']);
|
|
821
|
+
});
|
|
756
822
|
it('does not emit quartiles or throw when the ad duration is zero', async () => {
|
|
757
823
|
// Regression: a zero reported duration must not divide-by-zero into
|
|
758
824
|
// NaN progress and must not emit bogus quartile events.
|
|
@@ -32,8 +32,9 @@ describe('discoverHlsInterstitials', () => {
|
|
|
32
32
|
* A minimal `fetch` Response stand-in that satisfies `requestWithRetry`
|
|
33
33
|
* (which reads `ok`/`status`/`headers` and rejects on non-ok responses).
|
|
34
34
|
*/
|
|
35
|
-
function okJsonResponse(body) {
|
|
35
|
+
function okJsonResponse(body, url = '') {
|
|
36
36
|
return {
|
|
37
|
+
url,
|
|
37
38
|
ok: true,
|
|
38
39
|
status: 200,
|
|
39
40
|
headers: { get: () => null },
|
|
@@ -199,7 +200,8 @@ describe('discoverHlsInterstitials', () => {
|
|
|
199
200
|
const ads = await resolveValueProvider(breaks[0].ads);
|
|
200
201
|
expect(fetchSpy.calls.argsFor(0)[0]).toBe('https://example.com/ads.json');
|
|
201
202
|
expect(ads.length).toBe(2);
|
|
202
|
-
|
|
203
|
+
// Relative to the asset list, not to the media playlist.
|
|
204
|
+
expect(ads[0].uri).toBe('https://example.com/mid1.m3u8');
|
|
203
205
|
expect(ads[0].duration).toBe(10);
|
|
204
206
|
expect(ads[1].uri).toBe('https://cdn.example.com/mid2.m3u8');
|
|
205
207
|
expect(ads[1].duration).toBeNull();
|
|
@@ -208,6 +210,31 @@ describe('discoverHlsInterstitials', () => {
|
|
|
208
210
|
globalThis.fetch = origFetch;
|
|
209
211
|
}
|
|
210
212
|
});
|
|
213
|
+
it('resolves relative asset URIs against the redirected asset list URL', async () => {
|
|
214
|
+
const origFetch = globalThis.fetch;
|
|
215
|
+
const fetchSpy = jasmine
|
|
216
|
+
.createSpy('fetch')
|
|
217
|
+
.and.resolveTo(okJsonResponse({ ASSETS: [{ URI: 'mid1.m3u8' }] }, 'https://ads.example.net/v2/ads.json'));
|
|
218
|
+
globalThis.fetch = fetchSpy;
|
|
219
|
+
try {
|
|
220
|
+
const playlist = makePlaylist({
|
|
221
|
+
dateRanges: [
|
|
222
|
+
makeRange({
|
|
223
|
+
duration: 10,
|
|
224
|
+
clientAttributes: {
|
|
225
|
+
'X-ASSET-LIST': 'https://example.com/redirected.json',
|
|
226
|
+
},
|
|
227
|
+
}),
|
|
228
|
+
],
|
|
229
|
+
});
|
|
230
|
+
const breaks = await discoverHlsInterstitials(playlist, BASE);
|
|
231
|
+
const ads = await resolveValueProvider(breaks[0].ads);
|
|
232
|
+
expect(ads[0].uri).toBe('https://ads.example.net/v2/mid1.m3u8');
|
|
233
|
+
}
|
|
234
|
+
finally {
|
|
235
|
+
globalThis.fetch = origFetch;
|
|
236
|
+
}
|
|
237
|
+
});
|
|
211
238
|
it('caches the X-ASSET-LIST fetch across calls', async () => {
|
|
212
239
|
const origFetch = globalThis.fetch;
|
|
213
240
|
const fetchSpy = jasmine
|
|
@@ -2,7 +2,7 @@
|
|
|
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, DrmControllerImpl, DrmError, DrmKeySystem, DrmRobustness, LICENSE_TIMEOUT, ReportableMediaError, } from '@amazon/vinyl';
|
|
5
|
+
import { createEmptyMediaQualityMetadata, DrmControllerImpl, DrmError, DrmKeySystem, DrmRobustness, EXCESSIVE_SESSION_COUNT, LICENSE_TIMEOUT, ReportableMediaError, } from '@amazon/vinyl';
|
|
6
6
|
import { createEventSpy, useMockLogger } from '@amazon/vinyl-util/testUtil';
|
|
7
7
|
import { Abort, base64ToByteArray, bufferToBase64, Deferred, ErrorLevel, ErrorOrigin, never, nextEventAsPromise, ReportableError, setUserAgent, utf16ToUint16Array, } from '@amazon/vinyl-util';
|
|
8
8
|
import { flushPromises, implementEventFakes, MockHTMLAudioElement, MockMediaError, useMockTime, } from '@amazon/vinyl-util/browserTestUtil';
|
|
@@ -1321,6 +1321,24 @@ describe('DrmControllerImpl', () => {
|
|
|
1321
1321
|
expect(drmController.isEmeSupported()).toBeFalse();
|
|
1322
1322
|
});
|
|
1323
1323
|
});
|
|
1324
|
+
describe('excessive sessions', () => {
|
|
1325
|
+
it('warns once when the open session count exceeds the threshold', async () => {
|
|
1326
|
+
drmController.setBufferingDrmInfo(drmInfo);
|
|
1327
|
+
// Distinct init data per event so each opens its own session.
|
|
1328
|
+
for (let i = 0; i < EXCESSIVE_SESSION_COUNT; i++) {
|
|
1329
|
+
await emitEncrypted(new Uint8Array([i]));
|
|
1330
|
+
}
|
|
1331
|
+
expect(drmController.activeSessions).toBe(EXCESSIVE_SESSION_COUNT);
|
|
1332
|
+
expect(logger.value.warn).not.toHaveBeenCalled();
|
|
1333
|
+
// Crossing the threshold warns.
|
|
1334
|
+
await emitEncrypted(new Uint8Array([EXCESSIVE_SESSION_COUNT]));
|
|
1335
|
+
expect(drmController.activeSessions).toBe(EXCESSIVE_SESSION_COUNT + 1);
|
|
1336
|
+
expect(logger.value.warn).toHaveBeenCalledTimes(1);
|
|
1337
|
+
// Further sessions past the threshold don't re-warn.
|
|
1338
|
+
await emitEncrypted(new Uint8Array([EXCESSIVE_SESSION_COUNT + 1]));
|
|
1339
|
+
expect(logger.value.warn).toHaveBeenCalledTimes(1);
|
|
1340
|
+
});
|
|
1341
|
+
});
|
|
1324
1342
|
describe('closeSessions', () => {
|
|
1325
1343
|
it('clears active sessions', async () => {
|
|
1326
1344
|
const sessionCloseSpy = createEventSpy(drmController, 'sessionClose');
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import { DrmKeySystem, StandardCommonEme, StandardCommonMediaKeys, StandardCommonMediaKeySession, StandardCommonMediaKeySystemAccess, } from '@amazon/vinyl';
|
|
6
|
-
import { Deferred, DisposedError, never, } from '@amazon/vinyl-util';
|
|
7
|
-
import { expectTypeEquals, flushPromises, implementEventFakes, mockEvent, MockHTMLAudioElement, MockMediaKeyMessageEvent, MockMediaKeys, MockMediaKeySession, MockMediaKeySystemAccess, } from '@amazon/vinyl-util/browserTestUtil';
|
|
6
|
+
import { bufferToArrayBuffer, Deferred, DisposedError, never, } from '@amazon/vinyl-util';
|
|
7
|
+
import { expectIterableEquals, expectTypeEquals, flushPromises, implementEventFakes, mockEvent, MockHTMLAudioElement, MockMediaKeyMessageEvent, MockMediaKeys, MockMediaKeySession, MockMediaKeySystemAccess, } from '@amazon/vinyl-util/browserTestUtil';
|
|
8
8
|
import { createEventSpy, setMockNavigator } from '@amazon/vinyl-util/testUtil';
|
|
9
9
|
var any = jasmine.any;
|
|
10
10
|
describe('StandardCommonEme', () => {
|
|
@@ -163,14 +163,22 @@ describe('StandardCommonEme', () => {
|
|
|
163
163
|
});
|
|
164
164
|
});
|
|
165
165
|
describe('when created', () => {
|
|
166
|
-
it('calls session.generateRequest() with the initData
|
|
167
|
-
expect(mediaKeySession.generateRequest).toHaveBeenCalledOnceWith('cenc', initData
|
|
166
|
+
it('calls session.generateRequest() with the initData', () => {
|
|
167
|
+
expect(mediaKeySession.generateRequest).toHaveBeenCalledOnceWith('cenc', initData);
|
|
168
168
|
mediaKeySession.generateRequest.calls.reset();
|
|
169
169
|
const arrayBuffer = new ArrayBuffer(0);
|
|
170
170
|
standardCommonMediaKeySession =
|
|
171
171
|
new StandardCommonMediaKeySession(mediaKeySession, mimeType, 'cenc', arrayBuffer);
|
|
172
172
|
expect(mediaKeySession.generateRequest).toHaveBeenCalledOnceWith('cenc', arrayBuffer);
|
|
173
173
|
});
|
|
174
|
+
it('passes only the bytes an offset initData view spans', () => {
|
|
175
|
+
mediaKeySession.generateRequest.calls.reset();
|
|
176
|
+
const view = new Uint8Array([1, 2, 3, 4, 5]).subarray(1, 4);
|
|
177
|
+
standardCommonMediaKeySession =
|
|
178
|
+
new StandardCommonMediaKeySession(mediaKeySession, mimeType, 'cenc', view);
|
|
179
|
+
const [, passed] = mediaKeySession.generateRequest.calls.mostRecent().args;
|
|
180
|
+
expectIterableEquals(new Uint8Array(bufferToArrayBuffer(passed)), [2, 3, 4]);
|
|
181
|
+
});
|
|
174
182
|
describe('when session.generateRequest rejects', () => {
|
|
175
183
|
it('emits an error event', async () => {
|
|
176
184
|
mediaKeySession.generateRequest.and.rejectWith(new Error('expected'));
|