@amazon/vinyl 3.1.0 → 3.2.1

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 (69) hide show
  1. package/dist/index.cjs +236 -187
  2. package/dist/index.cjs.map +4 -4
  3. package/dist/index.js +224 -174
  4. package/dist/index.js.map +4 -4
  5. package/dist/test/integ.es5.js +245 -231
  6. package/dist/test/integ.js +676 -271
  7. package/dist/test/integ.js.map +4 -4
  8. package/dist/types/current/ad/AdControllerImpl.d.ts +18 -2
  9. package/dist/types/current/ad/AdControllerImpl.d.ts.map +1 -1
  10. package/dist/types/current/ad/AdControllerImpl.js +42 -20
  11. package/dist/types/current/drm/DrmControllerImpl.d.ts +17 -1
  12. package/dist/types/current/drm/DrmControllerImpl.d.ts.map +1 -1
  13. package/dist/types/current/drm/DrmControllerImpl.js +45 -10
  14. package/dist/types/current/playback/createStallMonitor.d.ts.map +1 -1
  15. package/dist/types/current/playback/createStallMonitor.js +19 -14
  16. package/dist/types/current/streaming/LoadMetric.d.ts +15 -1
  17. package/dist/types/current/streaming/LoadMetric.d.ts.map +1 -1
  18. package/dist/types/current/streaming/mediaTimelineFilter.d.ts +4 -2
  19. package/dist/types/current/streaming/mediaTimelineFilter.d.ts.map +1 -1
  20. package/dist/types/current/streaming/mediaTimelineFilter.js +23 -8
  21. package/dist/types/current/track/filters/sampleRateFilter.d.ts +8 -4
  22. package/dist/types/current/track/filters/sampleRateFilter.d.ts.map +1 -1
  23. package/dist/types/current/track/filters/sampleRateFilter.js +31 -21
  24. package/dist/types/current/track/mse/MseTrack.d.ts +0 -9
  25. package/dist/types/current/track/mse/MseTrack.d.ts.map +1 -1
  26. package/dist/types/current/track/mse/MseTrack.js +2 -29
  27. package/dist/types/current/tsconfig.tsbuildinfo +1 -1
  28. package/dist/types/current/vinyl/VinylPlayer.d.ts +5 -0
  29. package/dist/types/current/vinyl/VinylPlayer.d.ts.map +1 -1
  30. package/dist/types/current/vinyl/VinylPlayer.js +23 -1
  31. package/dist/types/ts3.7/ad/AdControllerImpl.d.ts +18 -2
  32. package/dist/types/ts3.7/drm/DrmControllerImpl.d.ts +17 -1
  33. package/dist/types/ts3.7/streaming/LoadMetric.d.ts +15 -1
  34. package/dist/types/ts3.7/streaming/mediaTimelineFilter.d.ts +4 -2
  35. package/dist/types/ts3.7/track/filters/sampleRateFilter.d.ts +8 -4
  36. package/dist/types/ts3.7/track/mse/MseTrack.d.ts +0 -9
  37. package/dist/types/ts3.7/vinyl/VinylPlayer.d.ts +5 -0
  38. package/dist/types/ts4.1/ad/AdControllerImpl.d.ts +18 -2
  39. package/dist/types/ts4.1/drm/DrmControllerImpl.d.ts +17 -1
  40. package/dist/types/ts4.1/streaming/LoadMetric.d.ts +15 -1
  41. package/dist/types/ts4.1/streaming/mediaTimelineFilter.d.ts +4 -2
  42. package/dist/types/ts4.1/track/filters/sampleRateFilter.d.ts +8 -4
  43. package/dist/types/ts4.1/track/mse/MseTrack.d.ts +0 -9
  44. package/dist/types/ts4.1/vinyl/VinylPlayer.d.ts +5 -0
  45. package/dist/types/ts4.7/ad/AdControllerImpl.d.ts +18 -2
  46. package/dist/types/ts4.7/drm/DrmControllerImpl.d.ts +17 -1
  47. package/dist/types/ts4.7/streaming/LoadMetric.d.ts +15 -1
  48. package/dist/types/ts4.7/streaming/mediaTimelineFilter.d.ts +4 -2
  49. package/dist/types/ts4.7/track/filters/sampleRateFilter.d.ts +8 -4
  50. package/dist/types/ts4.7/track/mse/MseTrack.d.ts +0 -9
  51. package/dist/types/ts4.7/vinyl/VinylPlayer.d.ts +5 -0
  52. package/dist/unit/types/current/ad/AdControllerImpl.test.js +110 -4
  53. package/dist/unit/types/current/drm/DrmControllerImpl.test.js +53 -9
  54. package/dist/unit/types/current/playback/createStallMonitor.test.js +38 -0
  55. package/dist/unit/types/current/streaming/MseTrack.test.js +11 -8
  56. package/dist/unit/types/current/streaming/mediaTimelineFilter.test.js +60 -54
  57. package/dist/unit/types/current/track/filters/sampleRateFilter.test.js +75 -0
  58. package/dist/unit/types/current/tsconfig.tsbuildinfo +1 -1
  59. package/dist/unit/types/current/vinyl/VinylPlayer.test.js +33 -2
  60. package/dist/vinylTestUtil/index.js +10 -1
  61. package/dist/vinylTestUtil/index.js.map +2 -2
  62. package/dist/vinylTestUtil/types/current/assets/vinylTestAssets.d.ts +5 -0
  63. package/dist/vinylTestUtil/types/current/assets/vinylTestAssets.d.ts.map +1 -1
  64. package/dist/vinylTestUtil/types/current/assets/vinylTestAssets.js +9 -0
  65. package/dist/vinylTestUtil/types/current/tsconfig.tsbuildinfo +1 -1
  66. package/dist/vinylTestUtil/types/ts3.7/assets/vinylTestAssets.d.ts +5 -0
  67. package/dist/vinylTestUtil/types/ts4.1/assets/vinylTestAssets.d.ts +5 -0
  68. package/dist/vinylTestUtil/types/ts4.7/assets/vinylTestAssets.d.ts +5 -0
  69. package/package.json +10 -10
package/dist/index.cjs CHANGED
@@ -84,6 +84,7 @@ __export(index_exports, {
84
84
  MsCommonMediaKeySystemAccess: () => MsCommonMediaKeySystemAccess,
85
85
  MsCommonMediaKeys: () => MsCommonMediaKeys,
86
86
  MseTrack: () => MseTrack,
87
+ PLAYOUT_LIMIT_TOLERANCE: () => PLAYOUT_LIMIT_TOLERANCE,
87
88
  PLAY_READY_KEY_SYSTEMS: () => PLAY_READY_KEY_SYSTEMS,
88
89
  POLL_BUFFER_THROTTLE: () => POLL_BUFFER_THROTTLE,
89
90
  PREFETCH_POLL_THROTTLE: () => PREFETCH_POLL_THROTTLE,
@@ -353,6 +354,7 @@ var AdError = class _AdError extends import_vinyl_util.ReportableError {
353
354
 
354
355
  // src/ad/AdControllerImpl.ts
355
356
  var AD_START_TOLERANCE = 0.05;
357
+ var PLAYOUT_LIMIT_TOLERANCE = 1;
356
358
  var defaultAdControllerImplOptions = {
357
359
  adLoadTimeout: 15,
358
360
  preloadAheadTime: 10
@@ -843,23 +845,29 @@ var AdControllerImpl = class extends import_vinyl_util2.EventHostImpl {
843
845
  });
844
846
  }
845
847
  /**
846
- * Ads breaks can have a playout limit that may cut off ad playback.
847
- * End the ad if that limit is reached.
848
+ * Enforces the break-level playout limit (X-PLAYOUT-LIMIT): a budget shared
849
+ * across all of the break's ads. When cumulative playout reaches it, the
850
+ * whole break ends, dropping any ads that have not started.
851
+ *
852
+ * This is deliberately NOT a per-ad cap. Each ad asset is its own source and
853
+ * ends on its media `ended` (see the 'ended' handler), which is what advances
854
+ * a multi-ad break. Ending an ad at its declared per-ad DURATION instead would
855
+ * truncate the real asset whenever a packager under-declares it — e.g. rounds
856
+ * a 6.732s asset down to a whole-second 6 — cutting off the fractional tail.
848
857
  */
849
858
  enforcePlayoutLimits(adState) {
859
+ if (!adState.started) return;
860
+ const { parentBreak } = adState;
861
+ const limit = parentBreak.adBreak.playoutLimit;
862
+ if (limit == null) return;
850
863
  const pC = this.deps.playbackController;
851
- const { ad, parentBreak } = adState;
852
- if (adState.started) {
853
- const elapsed = Math.max(0, pC.currentTime - adState.timeStart);
854
- const adBreak = parentBreak.adBreak;
855
- const limit = adBreak.playoutLimit;
856
- if (limit != null && parentBreak.playoutElapsed + elapsed >= limit) {
857
- parentBreak.clear();
858
- this.endAd(adState);
859
- } else if (ad.duration != null && elapsed >= ad.duration) {
860
- this.endAd(adState);
861
- }
864
+ const elapsed = Math.max(0, pC.currentTime - adState.timeStart);
865
+ if (parentBreak.playoutElapsed + elapsed < limit) return;
866
+ if (isFinite(pC.duration) && pC.duration > 0 && pC.duration - pC.currentTime <= PLAYOUT_LIMIT_TOLERANCE) {
867
+ return;
862
868
  }
869
+ parentBreak.clear();
870
+ this.endAd(adState);
863
871
  }
864
872
  setPendingBreaks(newPending) {
865
873
  this.pendingAdBreaks = newPending.filter(
@@ -2104,6 +2112,10 @@ var DrmControllerImpl = class extends import_vinyl_util24.EventHostImpl {
2104
2112
  __publicField(this, "bufferingTrackUri", null);
2105
2113
  __publicField(this, "sessionAbort", null);
2106
2114
  __publicField(this, "pendingMessageProps", null);
2115
+ // Armed per track load; latches on the first session so only the
2116
+ // critical-path session's EME/CDM setup is measured.
2117
+ __publicField(this, "keySetupSpanArmed", true);
2118
+ __publicField(this, "keySetupSpanStart", null);
2107
2119
  __publicField(this, "disposer", /* @__PURE__ */ (0, import_vinyl_util24.createDisposer)());
2108
2120
  __publicField(this, "checkIsSupportedCached", /* @__PURE__ */ (0, import_vinyl_util24.memoize)(
2109
2121
  async (params) => {
@@ -2269,6 +2281,7 @@ var DrmControllerImpl = class extends import_vinyl_util24.EventHostImpl {
2269
2281
  if (!hasMimeType(drmInfo)) {
2270
2282
  throw new DrmError("Encrypted content must have a mimeType.");
2271
2283
  }
2284
+ this.markKeySetupStart();
2272
2285
  const mediaKeys = await this.attachMediaKeys(drmInfo);
2273
2286
  this.abortIfDisposed();
2274
2287
  const keySystemOptions = (_a = this.options.keySystems[mediaKeys.keySystem]) != null ? _a : this.options.keySystems["*"];
@@ -2313,6 +2326,28 @@ var DrmControllerImpl = class extends import_vinyl_util24.EventHostImpl {
2313
2326
  });
2314
2327
  return newSession;
2315
2328
  }
2329
+ /**
2330
+ * Captures the key-setup span start at the top of the session-creation path
2331
+ * (before media keys are attached), latching so only the first session of
2332
+ * the current track load is measured.
2333
+ */
2334
+ markKeySetupStart() {
2335
+ if (!this.keySetupSpanArmed) return;
2336
+ this.keySetupSpanArmed = false;
2337
+ this.keySetupSpanStart = Date.now();
2338
+ }
2339
+ dispatchLoadSpan(kind, startTime, endTime, messageProps) {
2340
+ this.dispatch("loadSpanMeasured", __spreadValues(__spreadValues({
2341
+ kind,
2342
+ startTime,
2343
+ endTime,
2344
+ mimeType: messageProps.mimeType
2345
+ }, messageProps.trackUri != null && {
2346
+ trackUri: messageProps.trackUri
2347
+ }), messageProps.contentType != null && {
2348
+ contentType: messageProps.contentType
2349
+ }));
2350
+ }
2316
2351
  initializeForPlayback(drmInfo, options) {
2317
2352
  var _a;
2318
2353
  (0, import_vinyl_util24.logDebug)(this, "initializeForPlayback", drmInfo);
@@ -2336,6 +2371,8 @@ var DrmControllerImpl = class extends import_vinyl_util24.EventHostImpl {
2336
2371
  this.drmInfo = drmInfo;
2337
2372
  this.bufferingTrackUri = (_a = options == null ? void 0 : options.trackUri) != null ? _a : null;
2338
2373
  this.sessionAbort = (_b = options == null ? void 0 : options.abort) != null ? _b : null;
2374
+ this.keySetupSpanArmed = true;
2375
+ this.keySetupSpanStart = null;
2339
2376
  }
2340
2377
  /**
2341
2378
  * If the drm metadata contains initialization data for the current key system, create a new session.
@@ -2347,6 +2384,7 @@ var DrmControllerImpl = class extends import_vinyl_util24.EventHostImpl {
2347
2384
  this.handleError(new DrmError("DRM not supported."));
2348
2385
  return;
2349
2386
  }
2387
+ this.markKeySetupStart();
2350
2388
  const mediaKeys = await this.attachMediaKeys(drmInfo);
2351
2389
  const drmProtection = (_a = drmInfo.contentProtections.find((cP) => {
2352
2390
  return cP.keySystem === mediaKeys.keySystem;
@@ -2485,7 +2523,9 @@ var DrmControllerImpl = class extends import_vinyl_util24.EventHostImpl {
2485
2523
  event,
2486
2524
  // Captured per session, so a concurrent preloaded track's
2487
2525
  // license exchange keeps its own attribution.
2488
- trackUri
2526
+ trackUri,
2527
+ mimeType: drmInfo.mimeType,
2528
+ contentType: drmInfo.contentType
2489
2529
  }),
2490
2530
  LICENSE_TIMEOUT,
2491
2531
  { message: LICENSE_TIMEOUT_MESSAGE }
@@ -2507,6 +2547,15 @@ var DrmControllerImpl = class extends import_vinyl_util24.EventHostImpl {
2507
2547
  this.pendingMessageProps = messageProps;
2508
2548
  const keySystem = mediaKeys.keySystem;
2509
2549
  (0, import_vinyl_util24.logDebug)(this, "message", keySystem, event.message.byteLength);
2550
+ if (this.keySetupSpanStart != null) {
2551
+ this.dispatchLoadSpan(
2552
+ "keySetup",
2553
+ this.keySetupSpanStart,
2554
+ Date.now(),
2555
+ messageProps
2556
+ );
2557
+ this.keySetupSpanStart = null;
2558
+ }
2510
2559
  const keySystemOptions = (_a = this.options.keySystems[keySystem]) != null ? _a : this.options.keySystems["*"];
2511
2560
  const licenseServerOptionsProvider = keySystemOptions == null ? void 0 : keySystemOptions.licenseServer;
2512
2561
  const licenseServerOptions = (_b = (0, import_vinyl_util24.clone)(await (0, import_vinyl_util24.resolveValueProvider)(licenseServerOptionsProvider))) != null ? _b : {};
@@ -2528,13 +2577,12 @@ var DrmControllerImpl = class extends import_vinyl_util24.EventHostImpl {
2528
2577
  challenge
2529
2578
  );
2530
2579
  if (this.disposed) return;
2531
- this.dispatch("loadSpanMeasured", __spreadValues({
2532
- kind: "license",
2533
- startTime: licenseSpanStart,
2534
- endTime: Date.now()
2535
- }, messageProps.trackUri != null && {
2536
- trackUri: messageProps.trackUri
2537
- }));
2580
+ this.dispatchLoadSpan(
2581
+ "license",
2582
+ licenseSpanStart,
2583
+ Date.now(),
2584
+ messageProps
2585
+ );
2538
2586
  this.pendingMessageProps = null;
2539
2587
  if (session.disposed) return;
2540
2588
  (0, import_vinyl_util24.logDebug)(this, "update");
@@ -2777,7 +2825,7 @@ function configureVinylGlobal(options) {
2777
2825
  var import_vinyl_util27 = require("@amazon/vinyl-util");
2778
2826
  var vinylVersion = /* @__PURE__ */ (0, import_vinyl_util27.parseVersion)(
2779
2827
  /* istanbul ignore next */
2780
- "3.1.0"
2828
+ "3.2.1"
2781
2829
  );
2782
2830
 
2783
2831
  // src/global/globalRef.ts
@@ -2976,6 +3024,7 @@ var PlaybackNetworkState = /* @__PURE__ */ ((PlaybackNetworkState2) => {
2976
3024
  })(PlaybackNetworkState || {});
2977
3025
 
2978
3026
  // src/playback/createStallMonitor.ts
3027
+ var PROGRESS_EPSILON = 1e-6;
2979
3028
  function createStallMonitor(controller, callbacks, options = {}) {
2980
3029
  var _a, _b;
2981
3030
  const stallThresholdMs = ((_a = options.stallThreshold) != null ? _a : 1) * 1e3;
@@ -2984,6 +3033,7 @@ function createStallMonitor(controller, callbacks, options = {}) {
2984
3033
  let lastProgressAt = Date.now();
2985
3034
  let inStall = false;
2986
3035
  let pollId = null;
3036
+ const { add, dispose } = (0, import_vinyl_util30.createDisposer)();
2987
3037
  const endStall = (reason) => {
2988
3038
  if (!inStall) return;
2989
3039
  inStall = false;
@@ -3000,6 +3050,7 @@ function createStallMonitor(controller, callbacks, options = {}) {
3000
3050
  clearInterval(pollId);
3001
3051
  pollId = null;
3002
3052
  };
3053
+ add(stopPoll);
3003
3054
  const startPoll = () => {
3004
3055
  if (pollId != null) return;
3005
3056
  pollId = setInterval(() => {
@@ -3015,17 +3066,18 @@ function createStallMonitor(controller, callbacks, options = {}) {
3015
3066
  observedPlaying = false;
3016
3067
  stopPoll();
3017
3068
  };
3018
- const { add, dispose } = (0, import_vinyl_util30.createDisposer)();
3019
3069
  add(
3020
3070
  controller.on("playing", () => {
3021
3071
  if (inStall) endStall("playing" /* PLAYING */);
3022
- else lastProgressAt = Date.now();
3072
+ lastProgressAt = Date.now();
3023
3073
  observedPlaying = true;
3024
3074
  startPoll();
3025
3075
  })
3026
3076
  );
3027
3077
  add(
3028
- controller.on("timeUpdate", () => {
3078
+ controller.on("timeUpdate", ({ previous, current }) => {
3079
+ if (previous != null && current - previous <= PROGRESS_EPSILON)
3080
+ return;
3029
3081
  if (inStall) endStall("playing" /* PLAYING */);
3030
3082
  lastProgressAt = Date.now();
3031
3083
  })
@@ -3033,13 +3085,7 @@ function createStallMonitor(controller, callbacks, options = {}) {
3033
3085
  add(controller.on("pause", () => suspend("pause" /* PAUSE */)));
3034
3086
  add(controller.on("seeking", () => suspend("seeking" /* SEEKING */)));
3035
3087
  add(controller.on("emptied", () => suspend("emptied" /* EMPTIED */)));
3036
- let stopped = false;
3037
- return () => {
3038
- if (stopped) return;
3039
- stopped = true;
3040
- stopPoll();
3041
- dispose();
3042
- };
3088
+ return dispose;
3043
3089
  }
3044
3090
 
3045
3091
  // src/playback/error/InvalidSeekError.ts
@@ -5108,16 +5154,102 @@ function filterContentTypes(contentTypes, allowedContentTypes) {
5108
5154
  // src/streaming/createDefaultMediaTimelineTransformer.ts
5109
5155
  var import_vinyl_observable2 = require("@amazon/vinyl-observable");
5110
5156
 
5157
+ // src/streaming/MediaQualityMetadata.ts
5158
+ var import_vinyl_util53 = require("@amazon/vinyl-util");
5159
+ var import_vinyl_validation10 = require("@amazon/vinyl-validation");
5160
+
5161
+ // src/streaming/DescriptorRecord.ts
5162
+ var import_vinyl_validation9 = require("@amazon/vinyl-validation");
5163
+ var import_vinyl_util52 = require("@amazon/vinyl-util");
5164
+ function getDescriptorValue(record4, uri, id) {
5165
+ var _a, _b;
5166
+ let arr = record4[uri];
5167
+ if (!arr) return null;
5168
+ if (id !== void 0) arr = arr.filter((descriptor) => descriptor.id === id);
5169
+ return (_b = (_a = (0, import_vinyl_util52.first)(arr)) == null ? void 0 : _a.value) != null ? _b : null;
5170
+ }
5171
+ var descriptorValidator = /* @__PURE__ */ (0, import_vinyl_validation9.object)({
5172
+ id: /* @__PURE__ */ (0, import_vinyl_validation9.string)().maybe().optional(),
5173
+ value: /* @__PURE__ */ (0, import_vinyl_validation9.string)().maybe().optional()
5174
+ });
5175
+ var descriptorRecordValidator = /* @__PURE__ */ (0, import_vinyl_validation9.record)(/* @__PURE__ */ (0, import_vinyl_validation9.string)(), /* @__PURE__ */ (0, import_vinyl_validation9.array)(descriptorValidator).readonly().orUndefined());
5176
+
5177
+ // src/streaming/MediaQualityMetadata.ts
5178
+ var ALL_CONTENT_TYPES = [
5179
+ "video",
5180
+ "audio",
5181
+ "text"
5182
+ ];
5183
+ var RESTRICTABLE_CONTENT_TYPES = [
5184
+ "video",
5185
+ "audio"
5186
+ ];
5187
+ var drmProtectionValidator = /* @__PURE__ */ descriptorValidator.extend({
5188
+ keySystem: /* @__PURE__ */ (0, import_vinyl_validation10.enumOf)(DrmKeySystem),
5189
+ pro: /* @__PURE__ */ (0, import_vinyl_validation10.string)().maybe().optional(),
5190
+ pssh: /* @__PURE__ */ (0, import_vinyl_validation10.string)().maybe().optional()
5191
+ });
5192
+ var mediaFormatMetadataValidator = /* @__PURE__ */ (0, import_vinyl_validation10.object)({
5193
+ contentProtections: /* @__PURE__ */ (0, import_vinyl_validation10.array)(drmProtectionValidator).readonly(),
5194
+ contentType: /* @__PURE__ */ (0, import_vinyl_validation10.isOneOf)("audio", "video", "text").orNull(),
5195
+ initDataType: /* @__PURE__ */ (0, import_vinyl_validation10.string)().orNull(),
5196
+ encryptionScheme: /* @__PURE__ */ (0, import_vinyl_validation10.string)().orNull(),
5197
+ mimeType: /* @__PURE__ */ (0, import_vinyl_validation10.string)().orNull()
5198
+ });
5199
+ function createEmptyMediaFormatMetadata() {
5200
+ return {
5201
+ contentType: null,
5202
+ mimeType: null,
5203
+ initDataType: null,
5204
+ encryptionScheme: null,
5205
+ contentProtections: []
5206
+ };
5207
+ }
5208
+ function createEmptyMediaQualityMetadata() {
5209
+ return __spreadProps(__spreadValues({}, createEmptyMediaFormatMetadata()), {
5210
+ decoderId: (0, import_vinyl_util53.createShortUid)(),
5211
+ qualityId: (0, import_vinyl_util53.createShortUid)(),
5212
+ codecs: null,
5213
+ bandwidth: null,
5214
+ bandwidthTotal: null,
5215
+ supplementalProperties: {},
5216
+ audioSamplingRate: null,
5217
+ frameRate: null,
5218
+ height: null,
5219
+ width: null,
5220
+ lang: null,
5221
+ characteristics: [],
5222
+ groupId: (0, import_vinyl_util53.createShortUid)(),
5223
+ switchingGroupIds: null
5224
+ });
5225
+ }
5226
+ var mediaQualityMetadataValidator = /* @__PURE__ */ mediaFormatMetadataValidator.extend({
5227
+ audioSamplingRate: /* @__PURE__ */ (0, import_vinyl_validation10.array)(/* @__PURE__ */ (0, import_vinyl_validation10.number)()).readonly().orNull(),
5228
+ bandwidth: /* @__PURE__ */ (0, import_vinyl_validation10.number)().orNull(),
5229
+ bandwidthTotal: /* @__PURE__ */ (0, import_vinyl_validation10.number)().orNull(),
5230
+ codecs: /* @__PURE__ */ (0, import_vinyl_validation10.string)().orNull(),
5231
+ decoderId: /* @__PURE__ */ (0, import_vinyl_validation10.string)(),
5232
+ frameRate: /* @__PURE__ */ (0, import_vinyl_validation10.tuple)(/* @__PURE__ */ (0, import_vinyl_validation10.number)(), /* @__PURE__ */ (0, import_vinyl_validation10.number)()).readonly().orNull(),
5233
+ height: /* @__PURE__ */ (0, import_vinyl_validation10.number)().orNull(),
5234
+ qualityId: /* @__PURE__ */ (0, import_vinyl_validation10.string)(),
5235
+ supplementalProperties: descriptorRecordValidator,
5236
+ width: /* @__PURE__ */ (0, import_vinyl_validation10.number)().orNull(),
5237
+ lang: /* @__PURE__ */ (0, import_vinyl_validation10.string)().orNull(),
5238
+ characteristics: /* @__PURE__ */ (0, import_vinyl_validation10.array)(/* @__PURE__ */ (0, import_vinyl_validation10.string)()).readonly(),
5239
+ groupId: /* @__PURE__ */ (0, import_vinyl_validation10.string)(),
5240
+ switchingGroupIds: /* @__PURE__ */ (0, import_vinyl_validation10.array)(/* @__PURE__ */ (0, import_vinyl_validation10.string)()).readonly().orNull()
5241
+ });
5242
+
5111
5243
  // src/streaming/mediaTimelineFilter.ts
5112
- var import_vinyl_util54 = require("@amazon/vinyl-util");
5244
+ var import_vinyl_util56 = require("@amazon/vinyl-util");
5113
5245
 
5114
5246
  // src/streaming/mediaTimelineLanguageFilter.ts
5115
- var import_vinyl_util53 = require("@amazon/vinyl-util");
5247
+ var import_vinyl_util55 = require("@amazon/vinyl-util");
5116
5248
 
5117
5249
  // src/track/filters/languageFilter.ts
5118
- var import_vinyl_util52 = require("@amazon/vinyl-util");
5250
+ var import_vinyl_util54 = require("@amazon/vinyl-util");
5119
5251
  function throwLanguagesUnsupported() {
5120
- throw new import_vinyl_util52.MediaUnsupportedError(
5252
+ throw new import_vinyl_util54.MediaUnsupportedError(
5121
5253
  "No content matching preferred language",
5122
5254
  "language"
5123
5255
  );
@@ -5347,9 +5479,9 @@ function languageRelatedness(target, candidate) {
5347
5479
  return 1;
5348
5480
  }
5349
5481
  function findBestLanguageMatch(target, available) {
5350
- return (0, import_vinyl_util52.max)(
5482
+ return (0, import_vinyl_util54.max)(
5351
5483
  available,
5352
- (0, import_vinyl_util52.compareBy)((lang) => languageRelatedness(target, lang))
5484
+ (0, import_vinyl_util54.compareBy)((lang) => languageRelatedness(target, lang))
5353
5485
  );
5354
5486
  }
5355
5487
 
@@ -5373,7 +5505,7 @@ function createLanguageFilter(preferredLanguage, contentType2) {
5373
5505
  }
5374
5506
  return best;
5375
5507
  };
5376
- const bestScoreForPeriod = (0, import_vinyl_util53.memoize)(
5508
+ const bestScoreForPeriod = (0, import_vinyl_util55.memoize)(
5377
5509
  (array6) => {
5378
5510
  let best = 0;
5379
5511
  for (const quality of Array.from(array6)) {
@@ -5396,10 +5528,17 @@ function createLanguageFilter(preferredLanguage, contentType2) {
5396
5528
  }
5397
5529
 
5398
5530
  // src/streaming/mediaTimelineFilter.ts
5531
+ function assertMediaStreamsRetained(original, filtered, throwError) {
5532
+ for (const contentType2 of RESTRICTABLE_CONTENT_TYPES) {
5533
+ if (original.some((q) => q.metadata.contentType === contentType2) && !filtered.some((q) => q.metadata.contentType === contentType2)) {
5534
+ throwError();
5535
+ }
5536
+ }
5537
+ }
5399
5538
  function filterTimelineQualities(filter, throwError, timeline) {
5400
5539
  if (!filter) return timeline;
5401
- const mapMetadataArray = (0, import_vinyl_util54.memoize)(
5402
- (qualities) => (0, import_vinyl_util54.map)(qualities, (q) => q.metadata),
5540
+ const mapMetadataArray = (0, import_vinyl_util56.memoize)(
5541
+ (qualities) => (0, import_vinyl_util56.map)(qualities, (q) => q.metadata),
5403
5542
  (qualities) => qualities,
5404
5543
  1
5405
5544
  );
@@ -5407,16 +5546,14 @@ function filterTimelineQualities(filter, throwError, timeline) {
5407
5546
  const filtered = period.qualities.filter(
5408
5547
  (q, index) => filter(q.metadata, index, mapMetadataArray(period.qualities))
5409
5548
  );
5549
+ assertMediaStreamsRetained(period.qualities, filtered, throwError);
5410
5550
  return __spreadProps(__spreadValues({}, period), { qualities: filtered });
5411
5551
  });
5412
- if (periods.some((p) => p.qualities.length === 0)) {
5413
- throwError();
5414
- }
5415
5552
  return __spreadProps(__spreadValues({}, timeline), { periods });
5416
5553
  }
5417
5554
  async function filterTimelineQualitiesAsync(filter, throwError, timeline) {
5418
- const mapMetadataArray = (0, import_vinyl_util54.memoize)(
5419
- (qualities) => (0, import_vinyl_util54.map)(qualities, (q) => q.metadata),
5555
+ const mapMetadataArray = (0, import_vinyl_util56.memoize)(
5556
+ (qualities) => (0, import_vinyl_util56.map)(qualities, (q) => q.metadata),
5420
5557
  (qualities) => qualities,
5421
5558
  1
5422
5559
  );
@@ -5429,18 +5566,16 @@ async function filterTimelineQualitiesAsync(filter, throwError, timeline) {
5429
5566
  filtered.push(period.qualities[i]);
5430
5567
  }
5431
5568
  }
5569
+ assertMediaStreamsRetained(period.qualities, filtered, throwError);
5432
5570
  periods.push(__spreadProps(__spreadValues({}, period), { qualities: filtered }));
5433
5571
  }
5434
- if (periods.some((p) => p.qualities.length === 0)) {
5435
- throwError();
5436
- }
5437
5572
  return __spreadProps(__spreadValues({}, timeline), { periods });
5438
5573
  }
5439
5574
 
5440
5575
  // src/streaming/mediaTimelineAccessibilityFilter.ts
5441
- var import_vinyl_util55 = require("@amazon/vinyl-util");
5576
+ var import_vinyl_util57 = require("@amazon/vinyl-util");
5442
5577
  function throwNoPlayableAudio() {
5443
- throw new import_vinyl_util55.MediaUnsupportedError("No playable audio rendition", "audio");
5578
+ throw new import_vinyl_util57.MediaUnsupportedError("No playable audio rendition", "audio");
5444
5579
  }
5445
5580
  var AUDIO_DESCRIPTION_CHARACTERISTICS = [
5446
5581
  "public.accessibility.describes-video",
@@ -5452,7 +5587,7 @@ function isAudioDescription(quality) {
5452
5587
  );
5453
5588
  }
5454
5589
  function createAudioDescriptionFilter(preferDescription) {
5455
- const periodFlags = (0, import_vinyl_util55.memoize)(
5590
+ const periodFlags = (0, import_vinyl_util57.memoize)(
5456
5591
  (array6) => {
5457
5592
  let hasDescription = false;
5458
5593
  let hasMain = false;
@@ -5479,9 +5614,9 @@ function createAudioDescriptionFilter(preferDescription) {
5479
5614
  }
5480
5615
 
5481
5616
  // src/track/filters/resourceTypeFilter.ts
5482
- var import_vinyl_util56 = require("@amazon/vinyl-util");
5617
+ var import_vinyl_util58 = require("@amazon/vinyl-util");
5483
5618
  function throwMimeTypesUnsupported() {
5484
- throw new import_vinyl_util56.MediaUnsupportedError(
5619
+ throw new import_vinyl_util58.MediaUnsupportedError(
5485
5620
  "No resource type supported (container/codec).",
5486
5621
  "resource-type"
5487
5622
  );
@@ -5492,9 +5627,9 @@ function canPlayMimeType(deps, metadata) {
5492
5627
  }
5493
5628
 
5494
5629
  // src/track/filters/keySystemFilter.ts
5495
- var import_vinyl_util57 = require("@amazon/vinyl-util");
5630
+ var import_vinyl_util59 = require("@amazon/vinyl-util");
5496
5631
  function throwKeySystemsUnsupported() {
5497
- throw new import_vinyl_util57.MediaUnsupportedError("No key system supported.", "key-system");
5632
+ throw new import_vinyl_util59.MediaUnsupportedError("No key system supported.", "key-system");
5498
5633
  }
5499
5634
  async function canPlayKeySystem(deps, metadata) {
5500
5635
  if (!metadata.contentProtections.length) return true;
@@ -5502,32 +5637,37 @@ async function canPlayKeySystem(deps, metadata) {
5502
5637
  }
5503
5638
 
5504
5639
  // src/track/filters/sampleRateFilter.ts
5505
- var import_vinyl_util58 = require("@amazon/vinyl-util");
5640
+ var import_vinyl_util60 = require("@amazon/vinyl-util");
5506
5641
  function throwSamplingRatesUnsupported() {
5507
- throw new import_vinyl_util58.MediaUnsupportedError("No supported sample rate", "sampling-rate");
5642
+ throw new import_vinyl_util60.MediaUnsupportedError("No supported sample rate", "sampling-rate");
5508
5643
  }
5509
5644
  function withinAudioSampleRateRange(options, metadata, _index, array6) {
5510
5645
  var _a;
5511
5646
  if (metadata.contentType !== "audio") return true;
5512
- const samplingRate = (0, import_vinyl_util58.last)(metadata.audioSamplingRate);
5647
+ const samplingRate = (0, import_vinyl_util60.last)(metadata.audioSamplingRate);
5513
5648
  if (!samplingRate) return true;
5514
- const audioRates = () => (0, import_vinyl_util58.map)(
5649
+ const { minSampleRate } = options;
5650
+ const maxSampleRate = (_a = options.maxSampleRate) != null ? _a : options.capabilities.sampleRate;
5651
+ const audioRates = () => (0, import_vinyl_util60.map)(
5515
5652
  array6,
5516
- (item) => item.contentType === "audio" ? (0, import_vinyl_util58.last)(item.audioSamplingRate) : void 0
5653
+ (item) => item.contentType === "audio" ? (0, import_vinyl_util60.last)(item.audioSamplingRate) : void 0
5517
5654
  ).filter((rate) => rate != null);
5518
- const { minSampleRate } = options;
5655
+ const outOfBandFallback = () => {
5656
+ const rates = audioRates();
5657
+ const inBand = (rate) => (minSampleRate == null || rate >= minSampleRate) && (!maxSampleRate || rate <= maxSampleRate);
5658
+ if (rates.some(inBand)) return void 0;
5659
+ const decodable = maxSampleRate ? rates.filter((rate) => rate <= maxSampleRate) : rates;
5660
+ return decodable.length ? (0, import_vinyl_util60.max)(decodable) : (0, import_vinyl_util60.min)(rates);
5661
+ };
5519
5662
  if (minSampleRate != null && samplingRate < minSampleRate) {
5520
- const rates2 = audioRates();
5521
- return (0, import_vinyl_util58.every)(rates2, (rate) => rate < minSampleRate) && samplingRate === (0, import_vinyl_util58.max)(rates2);
5663
+ return samplingRate === outOfBandFallback();
5522
5664
  }
5523
- const maxSampleRate = (_a = options.maxSampleRate) != null ? _a : options.capabilities.sampleRate;
5524
5665
  if (!maxSampleRate) return true;
5525
- if ((0, import_vinyl_util58.hasBrowser)(import_vinyl_util58.Browser.FIREFOX)) {
5666
+ if ((0, import_vinyl_util60.hasBrowser)(import_vinyl_util60.Browser.FIREFOX)) {
5526
5667
  return samplingRate <= 48e3;
5527
5668
  }
5528
5669
  if (samplingRate <= maxSampleRate) return true;
5529
- const rates = audioRates();
5530
- return (0, import_vinyl_util58.every)(rates, (rate) => rate > maxSampleRate) && samplingRate === (0, import_vinyl_util58.min)(rates);
5670
+ return samplingRate === outOfBandFallback();
5531
5671
  }
5532
5672
 
5533
5673
  // src/util/media/codecOverrides.ts
@@ -5614,33 +5754,17 @@ function createDefaultMediaTimelineTransformer(deps) {
5614
5754
  });
5615
5755
  }
5616
5756
 
5617
- // src/streaming/DescriptorRecord.ts
5618
- var import_vinyl_validation9 = require("@amazon/vinyl-validation");
5619
- var import_vinyl_util59 = require("@amazon/vinyl-util");
5620
- function getDescriptorValue(record4, uri, id) {
5621
- var _a, _b;
5622
- let arr = record4[uri];
5623
- if (!arr) return null;
5624
- if (id !== void 0) arr = arr.filter((descriptor) => descriptor.id === id);
5625
- return (_b = (_a = (0, import_vinyl_util59.first)(arr)) == null ? void 0 : _a.value) != null ? _b : null;
5626
- }
5627
- var descriptorValidator = /* @__PURE__ */ (0, import_vinyl_validation9.object)({
5628
- id: /* @__PURE__ */ (0, import_vinyl_validation9.string)().maybe().optional(),
5629
- value: /* @__PURE__ */ (0, import_vinyl_validation9.string)().maybe().optional()
5630
- });
5631
- var descriptorRecordValidator = /* @__PURE__ */ (0, import_vinyl_validation9.record)(/* @__PURE__ */ (0, import_vinyl_validation9.string)(), /* @__PURE__ */ (0, import_vinyl_validation9.array)(descriptorValidator).readonly().orUndefined());
5632
-
5633
5757
  // src/streaming/ManifestControllerImpl.ts
5634
5758
  var import_vinyl_observable3 = require("@amazon/vinyl-observable");
5635
- var import_vinyl_util60 = require("@amazon/vinyl-util");
5759
+ var import_vinyl_util61 = require("@amazon/vinyl-util");
5636
5760
  var ManifestControllerImpl = class {
5637
5761
  constructor(deps) {
5638
5762
  __publicField(this, "deps", deps);
5639
5763
  __publicField(this, "_data");
5640
5764
  __publicField(this, "_error", null);
5641
- __publicField(this, "abortSlot", /* @__PURE__ */ (0, import_vinyl_util60.createAbortSlot)());
5642
- __publicField(this, "disposer", /* @__PURE__ */ (0, import_vinyl_util60.createDisposer)());
5643
- __publicField(this, "events", /* @__PURE__ */ new import_vinyl_util60.EventHostImpl());
5765
+ __publicField(this, "abortSlot", /* @__PURE__ */ (0, import_vinyl_util61.createAbortSlot)());
5766
+ __publicField(this, "disposer", /* @__PURE__ */ (0, import_vinyl_util61.createDisposer)());
5767
+ __publicField(this, "events", /* @__PURE__ */ new import_vinyl_util61.EventHostImpl());
5644
5768
  __publicField(this, "manifestSpanArmed", true);
5645
5769
  __publicField(this, "__eventMapType", this.events.__eventMapType);
5646
5770
  this._data = (0, import_vinyl_observable3.data)(this.requestManifest());
@@ -5667,7 +5791,7 @@ var ManifestControllerImpl = class {
5667
5791
  return this._error;
5668
5792
  }
5669
5793
  refresh() {
5670
- (0, import_vinyl_util60.logDebug)(this, "refresh");
5794
+ (0, import_vinyl_util61.logDebug)(this, "refresh");
5671
5795
  this._data.value = this.requestManifest();
5672
5796
  }
5673
5797
  requestManifest() {
@@ -5698,7 +5822,7 @@ var ManifestControllerImpl = class {
5698
5822
  );
5699
5823
  }
5700
5824
  reset() {
5701
- (0, import_vinyl_util60.logDebug)(this, "reset");
5825
+ (0, import_vinyl_util61.logDebug)(this, "reset");
5702
5826
  if (this._error) {
5703
5827
  this.refresh();
5704
5828
  }
@@ -5731,74 +5855,6 @@ var ManifestControllerImpl = class {
5731
5855
  }
5732
5856
  };
5733
5857
 
5734
- // src/streaming/MediaQualityMetadata.ts
5735
- var import_vinyl_util61 = require("@amazon/vinyl-util");
5736
- var import_vinyl_validation10 = require("@amazon/vinyl-validation");
5737
- var ALL_CONTENT_TYPES = [
5738
- "video",
5739
- "audio",
5740
- "text"
5741
- ];
5742
- var RESTRICTABLE_CONTENT_TYPES = [
5743
- "video",
5744
- "audio"
5745
- ];
5746
- var drmProtectionValidator = /* @__PURE__ */ descriptorValidator.extend({
5747
- keySystem: /* @__PURE__ */ (0, import_vinyl_validation10.enumOf)(DrmKeySystem),
5748
- pro: /* @__PURE__ */ (0, import_vinyl_validation10.string)().maybe().optional(),
5749
- pssh: /* @__PURE__ */ (0, import_vinyl_validation10.string)().maybe().optional()
5750
- });
5751
- var mediaFormatMetadataValidator = /* @__PURE__ */ (0, import_vinyl_validation10.object)({
5752
- contentProtections: /* @__PURE__ */ (0, import_vinyl_validation10.array)(drmProtectionValidator).readonly(),
5753
- contentType: /* @__PURE__ */ (0, import_vinyl_validation10.isOneOf)("audio", "video", "text").orNull(),
5754
- initDataType: /* @__PURE__ */ (0, import_vinyl_validation10.string)().orNull(),
5755
- encryptionScheme: /* @__PURE__ */ (0, import_vinyl_validation10.string)().orNull(),
5756
- mimeType: /* @__PURE__ */ (0, import_vinyl_validation10.string)().orNull()
5757
- });
5758
- function createEmptyMediaFormatMetadata() {
5759
- return {
5760
- contentType: null,
5761
- mimeType: null,
5762
- initDataType: null,
5763
- encryptionScheme: null,
5764
- contentProtections: []
5765
- };
5766
- }
5767
- function createEmptyMediaQualityMetadata() {
5768
- return __spreadProps(__spreadValues({}, createEmptyMediaFormatMetadata()), {
5769
- decoderId: (0, import_vinyl_util61.createShortUid)(),
5770
- qualityId: (0, import_vinyl_util61.createShortUid)(),
5771
- codecs: null,
5772
- bandwidth: null,
5773
- bandwidthTotal: null,
5774
- supplementalProperties: {},
5775
- audioSamplingRate: null,
5776
- frameRate: null,
5777
- height: null,
5778
- width: null,
5779
- lang: null,
5780
- characteristics: [],
5781
- groupId: (0, import_vinyl_util61.createShortUid)(),
5782
- switchingGroupIds: null
5783
- });
5784
- }
5785
- var mediaQualityMetadataValidator = /* @__PURE__ */ mediaFormatMetadataValidator.extend({
5786
- audioSamplingRate: /* @__PURE__ */ (0, import_vinyl_validation10.array)(/* @__PURE__ */ (0, import_vinyl_validation10.number)()).readonly().orNull(),
5787
- bandwidth: /* @__PURE__ */ (0, import_vinyl_validation10.number)().orNull(),
5788
- bandwidthTotal: /* @__PURE__ */ (0, import_vinyl_validation10.number)().orNull(),
5789
- codecs: /* @__PURE__ */ (0, import_vinyl_validation10.string)().orNull(),
5790
- decoderId: /* @__PURE__ */ (0, import_vinyl_validation10.string)(),
5791
- frameRate: /* @__PURE__ */ (0, import_vinyl_validation10.tuple)(/* @__PURE__ */ (0, import_vinyl_validation10.number)(), /* @__PURE__ */ (0, import_vinyl_validation10.number)()).readonly().orNull(),
5792
- height: /* @__PURE__ */ (0, import_vinyl_validation10.number)().orNull(),
5793
- qualityId: /* @__PURE__ */ (0, import_vinyl_validation10.string)(),
5794
- supplementalProperties: descriptorRecordValidator,
5795
- width: /* @__PURE__ */ (0, import_vinyl_validation10.number)().orNull(),
5796
- lang: /* @__PURE__ */ (0, import_vinyl_validation10.string)().orNull(),
5797
- characteristics: /* @__PURE__ */ (0, import_vinyl_validation10.array)(/* @__PURE__ */ (0, import_vinyl_validation10.string)()).readonly(),
5798
- groupId: /* @__PURE__ */ (0, import_vinyl_validation10.string)(),
5799
- switchingGroupIds: /* @__PURE__ */ (0, import_vinyl_validation10.array)(/* @__PURE__ */ (0, import_vinyl_validation10.string)()).readonly().orNull()
5800
- });
5801
-
5802
5858
  // src/streaming/MediaTimeline.ts
5803
5859
  var import_vinyl_util62 = require("@amazon/vinyl-util");
5804
5860
  function getMediaPeriodAtTime(timeline, time) {
@@ -7955,10 +8011,7 @@ var MseTrack = class extends TrackBase {
7955
8011
  this.deps = deps;
7956
8012
  add(
7957
8013
  deps.contentTypesValue.onData((contentTypesPromise) => {
7958
- contentTypesPromise.then((value) => {
7959
- this.setContentTypes(value);
7960
- this.validateRequiredContentTypesPlayable();
7961
- }).catch(this.errorHandler);
8014
+ contentTypesPromise.then((value) => this.setContentTypes(value)).catch(this.errorHandler);
7962
8015
  })
7963
8016
  );
7964
8017
  this.on("streamingQualityChange", (event) => {
@@ -8059,33 +8112,8 @@ var MseTrack = class extends TrackBase {
8059
8112
  });
8060
8113
  }
8061
8114
  }
8062
- this.validateRequiredContentTypesPlayable();
8063
8115
  });
8064
8116
  }
8065
- /**
8066
- * Fails fast when a media content type the manifest declared (audio/video)
8067
- * has no playable qualities left after timeline filtering. Its stream can
8068
- * never create a source buffer, so without this the track waits forever for
8069
- * a source buffer that never arrives, surfacing only as an opaque
8070
- * `readyToAppend` timeout. Runs once both the required content types and the
8071
- * filtered timeline qualities are known (either order).
8072
- */
8073
- validateRequiredContentTypesPlayable() {
8074
- const qualities = this._qualities;
8075
- if (!qualities) return;
8076
- const present = new Set(qualities.map((q) => q.contentType));
8077
- for (const contentType2 of this._contentTypes) {
8078
- if (!present.has(contentType2)) {
8079
- this.errorHandler(
8080
- new import_vinyl_util80.MediaUnsupportedError(
8081
- `No playable ${contentType2} rendition`,
8082
- `no-playable-${contentType2}`
8083
- )
8084
- );
8085
- return;
8086
- }
8087
- }
8088
- }
8089
8117
  async getAds() {
8090
8118
  const timeline = await this.deps.mediaTimeline.value;
8091
8119
  return {
@@ -10482,6 +10510,7 @@ var VinylPlayer = class extends import_vinyl_util99.EventHostImpl {
10482
10510
  playerRegistryRef.value.addPlayer(this);
10483
10511
  this.redispatchSubControllerEvents();
10484
10512
  this.redispatchCurrentTrackEvents();
10513
+ this.initializeLoadSpanLogging();
10485
10514
  this.initializeAutoResetHandling();
10486
10515
  }
10487
10516
  get [Symbol.toStringTag]() {
@@ -10499,6 +10528,26 @@ var VinylPlayer = class extends import_vinyl_util99.EventHostImpl {
10499
10528
  get autoResetController() {
10500
10529
  return this.deps.autoResetController;
10501
10530
  }
10531
+ /**
10532
+ * Logs each republished load span once at the verbose level, formatting its
10533
+ * bounds as ISO date strings and its duration in milliseconds.
10534
+ */
10535
+ initializeLoadSpanLogging() {
10536
+ this.disposer.add(
10537
+ this.on("loadSpan", (span) => {
10538
+ if ((0, import_vinyl_util99.getLogLevel)() > import_vinyl_util99.LogLevel.VERBOSE) return;
10539
+ (0, import_vinyl_util99.logVerbose)(this, "loadSpan", {
10540
+ kind: span.kind,
10541
+ trackUri: span.trackUri,
10542
+ mimeType: span.mimeType,
10543
+ contentType: span.contentType,
10544
+ startTime: new Date(span.startTime).toISOString(),
10545
+ endTime: new Date(span.endTime).toISOString(),
10546
+ durationMs: (span.endTime - span.startTime).toFixed(2)
10547
+ });
10548
+ })
10549
+ );
10550
+ }
10502
10551
  redispatchSubControllerEvents() {
10503
10552
  const add = this.disposer.add;
10504
10553
  add(