@amazon/vinyl 3.2.2 → 3.2.3-next.20261001224604

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.
Files changed (92) hide show
  1. package/README.md +2 -2
  2. package/dist/index.cjs +62 -21
  3. package/dist/index.cjs.map +3 -3
  4. package/dist/index.js +65 -22
  5. package/dist/index.js.map +3 -3
  6. package/dist/test/benchmark.es5.js +7 -6
  7. package/dist/test/benchmark.js +35 -30
  8. package/dist/test/benchmark.js.map +3 -3
  9. package/dist/test/integ.es5.js +184 -171
  10. package/dist/test/integ.js +450 -165
  11. package/dist/test/integ.js.map +4 -4
  12. package/dist/types/current/ad/AdControllerImpl.d.ts.map +1 -1
  13. package/dist/types/current/ad/AdControllerImpl.js +15 -1
  14. package/dist/types/current/ad/discoverHlsInterstitials.js +13 -1
  15. package/dist/types/current/drm/commonEme/MsCommonEme.d.ts.map +1 -1
  16. package/dist/types/current/drm/commonEme/MsCommonEme.js +2 -2
  17. package/dist/types/current/drm/commonEme/StandardCommonEme.d.ts.map +1 -1
  18. package/dist/types/current/drm/commonEme/StandardCommonEme.js +3 -2
  19. package/dist/types/current/drm/commonEme/WebKitCommonEme.d.ts.map +1 -1
  20. package/dist/types/current/drm/commonEme/WebKitCommonEme.js +2 -2
  21. package/dist/types/current/drm/dom/MSMediaKeys.d.ts +1 -1
  22. package/dist/types/current/drm/dom/MSMediaKeys.d.ts.map +1 -1
  23. package/dist/types/current/drm/dom/WebKitMediaKeys.d.ts +1 -1
  24. package/dist/types/current/drm/dom/WebKitMediaKeys.d.ts.map +1 -1
  25. package/dist/types/current/drm/util/extractContentId.js +2 -2
  26. package/dist/types/current/playback/PlaybackControllerImpl.d.ts +1 -1
  27. package/dist/types/current/streaming/MediaQualityMetadata.d.ts +3 -1
  28. package/dist/types/current/streaming/MediaQualityMetadata.d.ts.map +1 -1
  29. package/dist/types/current/streaming/SegmentControllerImpl.d.ts +1 -1
  30. package/dist/types/current/streaming/buffering/BufferingController.d.ts +1 -1
  31. package/dist/types/current/streaming/buffering/BufferingController.d.ts.map +1 -1
  32. package/dist/types/current/streaming/buffering/BufferingController.js +14 -5
  33. package/dist/types/current/streaming/buffering/MediaSourceController.d.ts.map +1 -1
  34. package/dist/types/current/streaming/buffering/MediaSourceController.js +4 -0
  35. package/dist/types/current/text/discoverDashTextTracks.d.ts +2 -2
  36. package/dist/types/current/text/discoverDashTextTracks.js +4 -3
  37. package/dist/types/current/text/parseWebVtt.d.ts.map +1 -1
  38. package/dist/types/current/text/parseWebVtt.js +3 -2
  39. package/dist/types/current/track/TrackController.d.ts.map +1 -1
  40. package/dist/types/current/track/TrackController.js +7 -0
  41. package/dist/types/current/track/hls/HlsManifestData.d.ts +14 -1
  42. package/dist/types/current/track/hls/HlsManifestData.d.ts.map +1 -1
  43. package/dist/types/current/track/hls/buildHlsMediaTimeline.d.ts.map +1 -1
  44. package/dist/types/current/track/hls/buildHlsMediaTimeline.js +10 -2
  45. package/dist/types/current/track/hls/createUrlHlsManifestProvider.d.ts +3 -2
  46. package/dist/types/current/track/hls/createUrlHlsManifestProvider.d.ts.map +1 -1
  47. package/dist/types/current/track/hls/createUrlHlsManifestProvider.js +8 -3
  48. package/dist/types/current/tsconfig.tsbuildinfo +1 -1
  49. package/dist/types/ts3.7/playback/PlaybackControllerImpl.d.ts +1 -1
  50. package/dist/types/ts3.7/streaming/MediaQualityMetadata.d.ts +3 -1
  51. package/dist/types/ts3.7/streaming/SegmentControllerImpl.d.ts +1 -1
  52. package/dist/types/ts3.7/streaming/buffering/BufferingController.d.ts +1 -1
  53. package/dist/types/ts3.7/text/discoverDashTextTracks.d.ts +2 -2
  54. package/dist/types/ts3.7/track/hls/HlsManifestData.d.ts +14 -1
  55. package/dist/types/ts3.7/track/hls/createUrlHlsManifestProvider.d.ts +3 -2
  56. package/dist/types/ts4.1/playback/PlaybackControllerImpl.d.ts +1 -1
  57. package/dist/types/ts4.1/streaming/MediaQualityMetadata.d.ts +3 -1
  58. package/dist/types/ts4.1/streaming/SegmentControllerImpl.d.ts +1 -1
  59. package/dist/types/ts4.1/streaming/buffering/BufferingController.d.ts +1 -1
  60. package/dist/types/ts4.1/text/discoverDashTextTracks.d.ts +2 -2
  61. package/dist/types/ts4.1/track/hls/HlsManifestData.d.ts +14 -1
  62. package/dist/types/ts4.1/track/hls/createUrlHlsManifestProvider.d.ts +3 -2
  63. package/dist/types/ts4.7/playback/PlaybackControllerImpl.d.ts +1 -1
  64. package/dist/types/ts4.7/streaming/MediaQualityMetadata.d.ts +3 -1
  65. package/dist/types/ts4.7/streaming/SegmentControllerImpl.d.ts +1 -1
  66. package/dist/types/ts4.7/streaming/buffering/BufferingController.d.ts +1 -1
  67. package/dist/types/ts4.7/text/discoverDashTextTracks.d.ts +2 -2
  68. package/dist/types/ts4.7/track/hls/HlsManifestData.d.ts +14 -1
  69. package/dist/types/ts4.7/track/hls/createUrlHlsManifestProvider.d.ts +3 -2
  70. package/dist/unit/types/current/ad/AdControllerImpl.test.js +66 -0
  71. package/dist/unit/types/current/ad/discoverHlsInterstitials.test.js +29 -2
  72. package/dist/unit/types/current/drm/commonEme/StandardCommonEme.test.js +12 -4
  73. package/dist/unit/types/current/drm/util/extractContentId.test.js +7 -0
  74. package/dist/unit/types/current/index.d.ts +1 -0
  75. package/dist/unit/types/current/index.d.ts.map +1 -1
  76. package/dist/unit/types/current/index.js +1 -0
  77. package/dist/unit/types/current/streaming/MediaQualityMetadata.test.js +8 -0
  78. package/dist/unit/types/current/streaming/buffering/BufferingController.test.js +26 -0
  79. package/dist/unit/types/current/streaming/buffering/MediaSourceController.test.js +29 -0
  80. package/dist/unit/types/current/text/TextTrack.test.d.ts +2 -0
  81. package/dist/unit/types/current/text/TextTrack.test.d.ts.map +1 -0
  82. package/dist/unit/types/current/text/TextTrack.test.js +12 -0
  83. package/dist/unit/types/current/text/discoverDashTextTracks.test.js +17 -0
  84. package/dist/unit/types/current/text/parseWebVtt.test.js +26 -0
  85. package/dist/unit/types/current/track/TrackController.test.js +15 -60
  86. package/dist/unit/types/current/track/hls/buildHlsMediaTimeline.test.js +22 -0
  87. package/dist/unit/types/current/track/hls/createUrlHlsManifestProvider.test.js +23 -0
  88. package/dist/unit/types/current/tsconfig.tsbuildinfo +1 -1
  89. package/dist/vinylTestUtil/types/current/tsconfig.tsbuildinfo +1 -1
  90. package/docs/CUSTOMIZATION_GUIDE.md +310 -0
  91. package/package.json +10 -10
  92. package/docs/DASH_MANIFEST_TRANSFORM_GUIDE.md +0 -236
@@ -0,0 +1,310 @@
1
+ # Customization Guide (Advanced)
2
+
3
+ Amazon Vinyl exposes several extension points that let an application filter,
4
+ sort, or otherwise reshape playback without forking the player. The extension
5
+ points are stacked from the most general to the most specific:
6
+
7
+ 1. **Media timeline transform** — protocol-agnostic filtering and shaping of the
8
+ qualities the ABR selector may pick from.
9
+ 2. **DASH manifest transform** — DASH-specific filtering, sorting, and
10
+ restructuring of the parsed manifest.
11
+ 3. **HLS manifest transform** — HLS-specific filtering and sorting of the parsed
12
+ master playlist.
13
+ 4. **Parser rule extensions** — parse additional XML elements/attributes on top
14
+ of the default DASH parser rules (or the HLS parser hooks), for custom
15
+ `ContentProtection` children, supplemental descriptors, etc.
16
+
17
+ Start at the highest layer that solves your problem. Timeline-level filtering is
18
+ protocol-agnostic and applies to both DASH and HLS.
19
+
20
+ ---
21
+
22
+ ## 1. Media timeline transform (protocol-agnostic)
23
+
24
+ The `MediaTimeline` is the last layer before the ABR selector sees the stream: a
25
+ list of `MediaPeriod`s, each with a set of `MediaQualityData` entries carrying
26
+ `MediaQualityMetadata` (mime type, codecs, bandwidth, resolution, language,
27
+ content protections, characteristics, …). Filtering here is the recommended
28
+ place for anything that doesn't need to look at the raw manifest, because it
29
+ works identically for DASH and HLS.
30
+
31
+ Vinyl ships `createDefaultMediaTimelineTransformer`, which chains codec/mime
32
+ support, DRM key-system support, sample-rate support, audio-description gating,
33
+ and language preference. Applications can stack their own filters over the same
34
+ primitives.
35
+
36
+ ### Filter timeline qualities
37
+
38
+ ```typescript
39
+ import {
40
+ createVinylPlayer,
41
+ filterTimelineQualities,
42
+ createDefaultMediaTimelineTransformer,
43
+ type MediaTimeline,
44
+ type MediaQualityMetadata,
45
+ } from '@amazon/vinyl'
46
+ import { MediaUnsupportedError } from '@amazon/vinyl-util'
47
+
48
+ /** Drop everything over 2 Mbps. */
49
+ function dropHighBitrate(timeline: MediaTimeline): MediaTimeline {
50
+ return filterTimelineQualities(
51
+ (quality: MediaQualityMetadata) => quality.bandwidth <= 2_000_000,
52
+ () => {
53
+ throw new MediaUnsupportedError(
54
+ 'No qualities within bitrate cap',
55
+ 'bitrate-cap'
56
+ )
57
+ },
58
+ timeline
59
+ )
60
+ }
61
+
62
+ const player = createVinylPlayer(
63
+ { media: new Audio() },
64
+ {
65
+ // Override the timeline transformer factory to chain the extra filter
66
+ // on top of the default one.
67
+ createMediaTimelineTransformer: (deps) => {
68
+ const defaults = createDefaultMediaTimelineTransformer(deps)
69
+ return defaults.map(async (t) => dropHighBitrate(await t))
70
+ },
71
+ }
72
+ )
73
+ ```
74
+
75
+ > **Note:** A plain bitrate cap already has a built-in configuration value —
76
+ > `player.configure({ abr: { maxBandwidth: 2_000_000 } })` — and resolution caps
77
+ > map to `abr.maxHeight` / `abr.maxWidth`. The filter above is shown only to
78
+ > illustrate the mechanism; reach for a custom timeline filter when you need
79
+ > logic the built-in ABR restrictions don't cover.
80
+
81
+ `filterTimelineQualitiesAsync` is available for filters that need to await (e.g.
82
+ an EME key-system probe). Both throw via the callback when a period ends up
83
+ empty, so callers can surface a specific error.
84
+
85
+ ---
86
+
87
+ ## 2. DASH manifest transform
88
+
89
+ Applications that need to look at raw DASH elements (adaptation-set switching
90
+ groups, per-representation descriptors, essential/supplemental properties) work
91
+ at this layer. The manifest transformer is a factory over
92
+ `ObservableValue<Promise<DashManifestData>>`, so multiple transformers compose.
93
+
94
+ Vinyl's default (`createDefaultDashManifestTransformer`) sorts adaptation sets
95
+ by descending `selectionPriority` and representations by ascending
96
+ `qualityRanking` then descending `bandwidth`. Filtering utilities
97
+ (`filterDashRepresentations`, `filterDashAdaptationSets`,
98
+ `sortDashRepresentations`, `sortDashAdaptationSets`) return cloned manifests, so
99
+ composition is safe.
100
+
101
+ ### Add a representation filter
102
+
103
+ ```typescript
104
+ import {
105
+ createDefaultDashManifestTransformer,
106
+ createVinylPlayer,
107
+ filterDashRepresentations,
108
+ mapManifestTransform,
109
+ type DashManifest,
110
+ } from '@amazon/vinyl'
111
+ import { MediaUnsupportedError } from '@amazon/vinyl-util'
112
+
113
+ /** Drop stereo-plus channel counts (headphones-only build). */
114
+ function stereoOnly(manifest: DashManifest): DashManifest {
115
+ return filterDashRepresentations(
116
+ (rep) =>
117
+ !rep.AudioChannelConfiguration ||
118
+ Number(rep.AudioChannelConfiguration.value) <= 2,
119
+ () => {
120
+ throw new MediaUnsupportedError(
121
+ 'Only stereo audio supported',
122
+ 'stereo-only'
123
+ )
124
+ },
125
+ manifest
126
+ )
127
+ }
128
+
129
+ const player = createVinylPlayer(
130
+ { media: new Audio() },
131
+ {
132
+ createDashFactories: (options) => (deps) => (loadOptions) => {
133
+ const base = deps.createDashFactories(options)(deps)(loadOptions)
134
+ return {
135
+ ...base,
136
+ manifestTransformed: (transformDeps) =>
137
+ mapManifestTransform(
138
+ createDefaultDashManifestTransformer(transformDeps),
139
+ stereoOnly
140
+ ),
141
+ }
142
+ },
143
+ }
144
+ )
145
+ ```
146
+
147
+ Filters can pull in additional reactive inputs (a config observable, a network
148
+ class, etc.) by mapping over `combineData({ manifestController, config })`
149
+ instead of `mapManifestTransform`.
150
+
151
+ ---
152
+
153
+ ## 3. HLS manifest transform
154
+
155
+ The HLS layer is smaller than DASH — one master playlist with a list of
156
+ variants, media playlists loaded lazily by the selected variant.
157
+ `createDefaultHlsManifestTransformer` sorts variants by descending bandwidth.
158
+ Extend it the same way as DASH by overriding the `manifestTransformed` slot on
159
+ the HLS factories.
160
+
161
+ ```typescript
162
+ import {
163
+ createDefaultHlsManifestTransformer,
164
+ createVinylPlayer,
165
+ type HlsManifestData,
166
+ } from '@amazon/vinyl'
167
+
168
+ /** Drop every variant over 720p. */
169
+ function cap720p(data: HlsManifestData): HlsManifestData {
170
+ const variants = data.mainPlaylist.variants.filter(
171
+ (v) => !v.resolution || v.resolution.height <= 720
172
+ )
173
+ return {
174
+ ...data,
175
+ mainPlaylist: { ...data.mainPlaylist, variants },
176
+ }
177
+ }
178
+
179
+ const player = createVinylPlayer(
180
+ { media: new Audio() },
181
+ {
182
+ createHlsFactories: (options) => (deps) => (loadOptions) => {
183
+ const base = deps.createHlsFactories(options)(deps)(loadOptions)
184
+ return {
185
+ ...base,
186
+ manifestTransformed: (transformDeps) =>
187
+ createDefaultHlsManifestTransformer(transformDeps).map(
188
+ async (v) => cap720p(await v)
189
+ ),
190
+ }
191
+ },
192
+ }
193
+ )
194
+ ```
195
+
196
+ > **Note:** A resolution cap like the one above also has a built-in
197
+ > configuration value — `player.configure({ abr: { maxHeight: 720 } })`. The
198
+ > filter is shown only to illustrate the mechanism.
199
+
200
+ Cross-cutting quality filtering (bandwidth caps, codec support, language
201
+ preference) belongs at the timeline layer above; this layer is for HLS-specific
202
+ shape (e.g. dropping variants by an HLS-only attribute the ABR restrictions
203
+ don't model).
204
+
205
+ ---
206
+
207
+ ## 4. Parser rule extensions
208
+
209
+ Vinyl's DASH parser is rule-based. The `dashDrmRules` ruleset (default DASH
210
+ manifest rules + cenc + PlayReady content-protection extensions) is exported so
211
+ consumers can layer additional element/attribute rules on top of it without
212
+ reimplementing anything.
213
+
214
+ ### Parse a custom `ContentProtection` child
215
+
216
+ Consider a manifest that carries a vendor-specific `<LicenseUrl>` element inside
217
+ each `ContentProtection`, under its own namespace:
218
+
219
+ ```xml
220
+ <ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"
221
+ xmlns:ext="urn:example:drm:2020">
222
+ <cenc:pssh>...</cenc:pssh>
223
+ <ext:LicenseUrl>https://example.com/getLicense</ext:LicenseUrl>
224
+ </ContentProtection>
225
+ ```
226
+
227
+ Define a rule for the new element, merge it in, and build a parser:
228
+
229
+ ```typescript
230
+ import {
231
+ charactersString,
232
+ createDashProtectionXmlRules,
233
+ dashDrmRules,
234
+ element,
235
+ mapXmlRules,
236
+ mergeXmlRules,
237
+ parseXml,
238
+ ParseXmlHandlerImpl,
239
+ type XmlRules,
240
+ } from '@amazon/vinyl-mpd-parser'
241
+
242
+ const NS = 'urn:example:drm:2020'
243
+
244
+ interface LicenseUrlProtection {
245
+ readonly LicenseUrl?: { readonly _content: string }
246
+ }
247
+
248
+ const licenseUrlRules: XmlRules<LicenseUrlProtection> = {
249
+ LicenseUrl: element({ _content: charactersString }, { namespaceUri: NS }),
250
+ }
251
+
252
+ const rules = mapXmlRules(
253
+ mergeXmlRules(dashDrmRules, createDashProtectionXmlRules(licenseUrlRules))
254
+ )
255
+
256
+ export function parseDashManifestWithLicenseUrl(xml: string) {
257
+ return parseXml(xml, new ParseXmlHandlerImpl(rules))
258
+ }
259
+ ```
260
+
261
+ Feed the extended parser into a track via a custom `manifestProvider`:
262
+
263
+ ```typescript
264
+ player.load({
265
+ type: 'dash',
266
+ uri: manifestUrl,
267
+ manifestProvider: async () => {
268
+ const response = await fetch(manifestUrl)
269
+ return {
270
+ manifest: parseDashManifestWithLicenseUrl(await response.text()),
271
+ baseUrl: manifestUrl,
272
+ }
273
+ },
274
+ // …bind license-server providers that read the parsed LicenseUrl from the
275
+ // matching ContentProtection element.
276
+ })
277
+ ```
278
+
279
+ `createDashProtectionXmlRules` wires a set of `ContentProtection` child rules
280
+ into every level
281
+ (`MPD > Period > AdaptationSet > Representation > SubRepresentation`), so the
282
+ same rule automatically applies wherever `ContentProtection` may appear.
283
+
284
+ ### Utility reference
285
+
286
+ - `filterTimelineQualities` / `filterTimelineQualitiesAsync` — timeline-level
287
+ quality filter with a throw-on-empty callback.
288
+ - `filterDashRepresentations` / `filterDashAdaptationSets` — DASH element
289
+ filters with a throw-on-empty callback.
290
+ - `sortDashRepresentations` / `sortDashAdaptationSets` — DASH sorters.
291
+ - `mapManifestTransform` — apply a
292
+ `(DashManifest) => MaybePromise<DashManifest>` over a manifest observable,
293
+ preserving `baseUrl`.
294
+ - `manifestIsPlayable` — check whether a filtered DASH manifest still has
295
+ playable representations.
296
+ - `dashDrmRules` (from `@amazon/vinyl-mpd-parser`) — default DASH manifest +
297
+ cenc + PlayReady rules, ready to merge additional rules onto.
298
+ - `createDashProtectionXmlRules` — lifts a `ContentProtection` child rule to
299
+ every place `ContentProtection` may appear.
300
+
301
+ ## Guidelines
302
+
303
+ 1. **Pick the highest layer that fits.** Timeline filters are DASH/HLS-neutral
304
+ and cheapest to write.
305
+ 2. **Give every filter a distinct error code** so surfaced errors are
306
+ attributable.
307
+ 3. **Sync filters are preferred**; only use async filters for genuinely
308
+ asynchronous checks (e.g. EME probes).
309
+ 4. **Filter utilities clone.** You can chain them freely without worrying about
310
+ mutating shared state.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amazon/vinyl",
3
- "version": "3.2.2",
3
+ "version": "3.2.3-next.20261001224604",
4
4
  "description": "An HTML5 streaming media player",
5
5
  "repository": {
6
6
  "type": "git",
@@ -90,18 +90,18 @@
90
90
  "oxlint:write": "oxlint --type-aware --fix"
91
91
  },
92
92
  "devDependencies": {
93
- "@amazon/vinyl-browserstack": "^3.2.2",
94
- "@amazon/vinyl-build-utils": "^3.2.2",
93
+ "@amazon/vinyl-browserstack": "3.2.3-next.20261001224604",
94
+ "@amazon/vinyl-build-utils": "3.2.3-next.20261001224604",
95
95
  "commander": "^15.0.0"
96
96
  },
97
97
  "dependencies": {
98
- "@amazon/vinyl-di": "^3.2.2",
99
- "@amazon/vinyl-hls-parser": "^3.2.2",
100
- "@amazon/vinyl-mpd-parser": "^3.2.2",
101
- "@amazon/vinyl-observable": "^3.2.2",
102
- "@amazon/vinyl-transmux": "^3.2.2",
103
- "@amazon/vinyl-util": "^3.2.2",
104
- "@amazon/vinyl-validation": "^3.2.2"
98
+ "@amazon/vinyl-di": "3.2.3-next.20261001224604",
99
+ "@amazon/vinyl-hls-parser": "3.2.3-next.20261001224604",
100
+ "@amazon/vinyl-mpd-parser": "3.2.3-next.20261001224604",
101
+ "@amazon/vinyl-observable": "3.2.3-next.20261001224604",
102
+ "@amazon/vinyl-transmux": "3.2.3-next.20261001224604",
103
+ "@amazon/vinyl-util": "3.2.3-next.20261001224604",
104
+ "@amazon/vinyl-validation": "3.2.3-next.20261001224604"
105
105
  },
106
106
  "publishConfig": {
107
107
  "access": "public"
@@ -1,236 +0,0 @@
1
- # DASH Manifest Transform Extension Guide (Advanced)
2
-
3
- This guide explains how to extend or modify DASH manifest transformations in
4
- Amazon Vinyl.
5
-
6
- ## Overview
7
-
8
- DASH manifest transformers sort and modify DASH manifests before playback. The
9
- system uses a functional architecture where transformers are composable
10
- functions over `ObservableValue<Promise<DashManifestData>>`.
11
-
12
- Quality filtering (MIME type, key system, sample rate) is handled separately by
13
- the media timeline transformer, not the manifest transformer. The manifest
14
- transformer focuses on structural sorting and representation-level filtering.
15
-
16
- ## Default Implementation
17
-
18
- `createDefaultDashManifestTransformer()` sorts:
19
-
20
- - Adaptation sets by `selectionPriority` (descending)
21
- - Representations by `qualityRanking` (ascending), then `bandwidth` (descending)
22
-
23
- The result is mapped over the manifest controller observable.
24
-
25
- ## Creating Custom Filters
26
-
27
- ### Representation Filters
28
-
29
- Filter representations based on properties like bandwidth:
30
-
31
- ```typescript
32
- import { filterDashRepresentations, mapManifestTransform } from '@amazon/vinyl'
33
- import { MediaUnsupportedError } from '@amazon/vinyl-util'
34
-
35
- function filterHighBitrate(manifest: DashManifest) {
36
- return filterDashRepresentations(
37
- (representation) => representation.bandwidth <= 1_000_000,
38
- () => {
39
- throw new MediaUnsupportedError(
40
- 'No representations within bitrate range',
41
- 'bitrate_filter'
42
- )
43
- },
44
- manifest
45
- )
46
- }
47
- ```
48
-
49
- ### Adaptation Set Filters
50
-
51
- Filter entire adaptation sets:
52
-
53
- ```typescript
54
- import { filterDashAdaptationSets } from '@amazon/vinyl'
55
- import { MediaUnsupportedError } from '@amazon/vinyl-util'
56
-
57
- function filterVideoOnly(manifest: DashManifest) {
58
- return filterDashAdaptationSets(
59
- (adaptationSet) =>
60
- adaptationSet.mimeType?.startsWith('video/') ?? false,
61
- () => {
62
- throw new MediaUnsupportedError(
63
- 'No video adaptation sets found',
64
- 'video_only'
65
- )
66
- },
67
- manifest
68
- )
69
- }
70
- ```
71
-
72
- ## Creating Custom Transformers
73
-
74
- ### Transform Function
75
-
76
- Use `flowAsync` to compose multiple filter/sort steps, and
77
- `mapManifestTransform` to apply them over a manifest observable:
78
-
79
- ```typescript
80
- import {
81
- filterDashRepresentations,
82
- sortDashRepresentations,
83
- mapManifestTransform,
84
- } from '@amazon/vinyl'
85
- import { flowAsync } from '@amazon/vinyl-util'
86
- import type { ObservableValue } from '@amazon/vinyl-observable'
87
- import type { DashManifestData } from '@amazon/vinyl'
88
-
89
- function createCustomTransformer(
90
- manifestController: ObservableValue<Promise<DashManifestData>>
91
- ) {
92
- const transformManifest = flowAsync(
93
- (m) => filterDashRepresentations(customPredicate, throwCustomError, m),
94
- (m) => sortDashRepresentations(customComparator, m)
95
- )
96
-
97
- return mapManifestTransform(manifestController, transformManifest)
98
- }
99
- ```
100
-
101
- ### Filter Factory
102
-
103
- Transformers are factory functions with the signature
104
- `(ObservableValue<Promise<DashManifestData>>) => ObservableValue<Promise<DashManifestData>>`.
105
- This allows composition with `flow`:
106
-
107
- ```typescript
108
- import { mapManifestTransform, filterDashRepresentations } from '@amazon/vinyl'
109
-
110
- function createCustomFilter(deps: CustomDeps) {
111
- return (
112
- manifestController: ObservableValue<Promise<DashManifestData>>
113
- ): ObservableValue<Promise<DashManifestData>> =>
114
- mapManifestTransform(manifestController, (manifest) =>
115
- filterDashRepresentations(
116
- (rep) => customPredicate(deps, rep),
117
- throwCustomError,
118
- manifest
119
- )
120
- )
121
- }
122
- ```
123
-
124
- For filters that depend on additional reactive inputs, use `combineData`:
125
-
126
- ```typescript
127
- import { combineData } from '@amazon/vinyl-observable'
128
- import { filterDashRepresentations } from '@amazon/vinyl'
129
-
130
- function createConfigFilter(deps: {
131
- readonly configProvider: ObservableValue<FilterConfig>
132
- }): (
133
- manifestAndPath: ObservableValue<Promise<DashManifestData>>
134
- ) => ObservableValue<Promise<DashManifestData>> {
135
- return (manifestAndPath) =>
136
- combineData({
137
- manifestAndPath,
138
- config: deps.configProvider,
139
- }).map(async ({ manifestAndPath, config }) => {
140
- const { manifest, baseUrl } = await manifestAndPath
141
- return {
142
- baseUrl,
143
- manifest: filterDashRepresentations(
144
- (rep) => rep.bandwidth <= config.maxBitrate,
145
- throwConfigFilterError,
146
- manifest
147
- ),
148
- }
149
- })
150
- }
151
- ```
152
-
153
- ### Composing Transformers with flow
154
-
155
- ```typescript
156
- import { flow } from '@amazon/vinyl-util'
157
-
158
- function createMyManifestTransformer(
159
- deps: MyTransformerDeps
160
- ): ObservableValue<Promise<DashManifestData>> {
161
- return flow(
162
- createCustomFilter(deps),
163
- createSortTransformer()
164
- )(createDefaultDashManifestTransformer(deps))
165
- }
166
- ```
167
-
168
- ## Integration with createVinylPlayer
169
-
170
- Override the `manifestTransformed` factory to inject custom transformations
171
- after the default sorting:
172
-
173
- ```typescript
174
- import {
175
- createVinylPlayer,
176
- createDefaultDashManifestTransformer,
177
- createDashFactories,
178
- filterDashRepresentations,
179
- mapManifestTransform,
180
- type DashManifestTransformerDeps,
181
- } from '@amazon/vinyl'
182
- import { flowAsync } from '@amazon/vinyl-util'
183
- import { MediaUnsupportedError } from '@amazon/vinyl-util'
184
-
185
- function filterStereoOnly(manifest: DashManifest) {
186
- return filterDashRepresentations(
187
- (rep) =>
188
- !rep.AudioChannelConfiguration ||
189
- Number(rep.AudioChannelConfiguration.value) <= 2,
190
- () => {
191
- throw new MediaUnsupportedError(
192
- 'Only stereo audio supported',
193
- 'stereo_only'
194
- )
195
- },
196
- manifest
197
- )
198
- }
199
-
200
- const player = createVinylPlayer(
201
- { media: new Audio() },
202
- {
203
- createDashFactories: (options) => (deps) => (loadOptions) => ({
204
- ...createDashFactories(options)(deps)(loadOptions),
205
- manifestTransformed: (
206
- transformDeps: DashManifestTransformerDeps
207
- ) => {
208
- const transformManifest = flowAsync(filterStereoOnly)
209
-
210
- return mapManifestTransform(
211
- createDefaultDashManifestTransformer(transformDeps),
212
- transformManifest
213
- )
214
- },
215
- }),
216
- }
217
- )
218
- ```
219
-
220
- ## Utility Functions
221
-
222
- - `filterDashAdaptationSets()` - Filter adaptation sets with error handling
223
- - `filterDashRepresentations()` - Filter representations with error handling
224
- - `sortDashAdaptationSets()` - Sort adaptation sets by comparator
225
- - `sortDashRepresentations()` - Sort representations by comparator
226
- - `manifestIsPlayable()` - Check if manifest has playable content
227
- - `mapManifestTransform()` - Apply a
228
- `(DashManifest) => MaybePromise<DashManifest>` over a manifest observable,
229
- preserving baseUrl
230
-
231
- ## Best Practices
232
-
233
- 1. **Error Handling**: Always provide meaningful error codes and messages
234
- 2. **Performance**: Use async filters only for expensive operations (e.g. DRM)
235
- 3. **Composition**: Prefer small, focused filter factories composed with `flow`
236
- 4. **Immutability**: Filter utilities clone manifests internally