@videojs/spf 10.0.0-alpha.11 → 10.0.0-beta.10

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.
@@ -345,6 +345,28 @@ interface Actor<Status extends string, Context> {
345
345
  destroy(): void;
346
346
  }
347
347
  //#endregion
348
+ //#region src/dom/media/append-segment.d.ts
349
+ /**
350
+ * Segment appender helper.
351
+ *
352
+ * Appends media data (ArrayBuffer or AsyncIterable<Uint8Array> stream) to a
353
+ * SourceBuffer, waiting for `updateend` between calls so the browser can
354
+ * process each append before the next one arrives.
355
+ */
356
+ /** Data accepted by appendSegment — a full buffer or an async chunk stream. */
357
+ type AppendData = ArrayBuffer | AsyncIterable<Uint8Array>;
358
+ /**
359
+ * Append media data to a SourceBuffer.
360
+ *
361
+ * Accepts either a full ArrayBuffer (single append) or an AsyncIterable of
362
+ * Uint8Array chunks (one append per chunk, in order). Waits for `updateend`
363
+ * between each call so appends are serialized correctly.
364
+ *
365
+ * Errors from the SourceBuffer (`error` event) or from the iterable are
366
+ * propagated as rejections.
367
+ */
368
+ declare function appendSegment(sourceBuffer: SourceBuffer, data: AppendData, signal?: AbortSignal): Promise<void>;
369
+ //#endregion
348
370
  //#region src/dom/media/source-buffer-actor.d.ts
349
371
  interface BufferedRange {
350
372
  start: number;
@@ -357,14 +379,14 @@ type AppendSegmentMeta = Pick<Segment, 'id' | 'startTime' | 'duration'> & {
357
379
  };
358
380
  type AppendInitMessage = {
359
381
  type: 'append-init';
360
- data: ArrayBuffer;
382
+ data: AppendData;
361
383
  meta: {
362
384
  trackId: Track['id'];
363
385
  };
364
386
  };
365
387
  type AppendSegmentMessage = {
366
388
  type: 'append-segment';
367
- data: ArrayBuffer;
389
+ data: AppendData;
368
390
  meta: AppendSegmentMeta;
369
391
  };
370
392
  type RemoveMessage = {
@@ -381,6 +403,12 @@ interface SourceBufferActorContext {
381
403
  segments: Array<Pick<Segment, 'id' | 'startTime' | 'duration'> & {
382
404
  trackId: Track['id'];
383
405
  trackBandwidth?: number;
406
+ /**
407
+ * True while a streaming append is in progress for this segment.
408
+ * The segment's data is partially present in the SourceBuffer.
409
+ * Downstream code must not treat a partial segment as fully buffered.
410
+ */
411
+ partial?: boolean;
384
412
  }>;
385
413
  bufferedRanges: BufferedRange[];
386
414
  }
@@ -680,10 +708,12 @@ declare class SpfMedia {
680
708
  attach(mediaElement: HTMLMediaElement): void;
681
709
  detach(): void;
682
710
  destroy(): void;
711
+ get preload(): '' | 'none' | 'metadata' | 'auto';
712
+ set preload(value: '' | 'none' | 'metadata' | 'auto');
683
713
  get src(): string;
684
714
  set src(value: string);
685
715
  play(): Promise<void>;
686
716
  }
687
717
  //#endregion
688
- export { PresentationAction as a, SourceBufferActor as c, WritableState as d, BandwidthState as f, createPlaybackEngine as i, TrackSelectionState as l, PlaybackEngine as n, EventStream as o, PlaybackEngineConfig as r, loadTextTrackCues as s, SpfMedia as t, Presentation as u };
689
- //# sourceMappingURL=adapter-l8iJN_GI.d.ts.map
718
+ export { PresentationAction as a, SourceBufferActor as c, Presentation as d, WritableState as f, createPlaybackEngine as i, appendSegment as l, PlaybackEngine as n, EventStream as o, BandwidthState as p, PlaybackEngineConfig as r, loadTextTrackCues as s, SpfMedia as t, TrackSelectionState as u };
719
+ //# sourceMappingURL=adapter-25AtMN8s.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-25AtMN8s.d.ts","names":[],"sources":["../src/core/abr/bandwidth-estimator.ts","../src/core/state/create-state.ts","../src/core/types/index.ts","../src/core/utils/track-selection.ts","../src/core/actor.ts","../src/dom/media/append-segment.ts","../src/dom/media/source-buffer-actor.ts","../src/dom/features/load-text-track-cues.ts","../src/core/events/create-event-stream.ts","../src/core/features/resolve-presentation.ts","../src/core/features/resolve-track.ts","../src/core/features/select-tracks.ts","../src/dom/playback-engine/engine.ts","../src/dom/playback-engine/adapter.ts"],"sourcesContent":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { listen } from "@videojs/utils/dom";
2
- import { isNil, isUndefined } from "@videojs/utils/predicate";
2
+ import { isNil } from "@videojs/utils/predicate";
3
3
 
4
4
  //#region src/core/state/create-state.ts
5
5
  /**
@@ -1067,6 +1067,53 @@ function isCodecSupported(mimeCodec) {
1067
1067
  return MediaSource.isTypeSupported(mimeCodec);
1068
1068
  }
1069
1069
 
1070
+ //#endregion
1071
+ //#region src/dom/network/chunked-stream-iterable.ts
1072
+ const DEFAULT_MIN_CHUNK_SIZE = 2 ** 17;
1073
+ /**
1074
+ * Adapts a `ReadableStream<Uint8Array>` (e.g. `response.body`) into an
1075
+ * `AsyncIterable<Uint8Array>` that yields chunks no smaller than
1076
+ * `minChunkSize` bytes. Smaller network chunks are accumulated and yielded
1077
+ * together once the threshold is met. Any remainder is flushed on stream end.
1078
+ *
1079
+ * Errors from the underlying stream propagate naturally — the reader lock is
1080
+ * always released via `finally`.
1081
+ */
1082
+ var ChunkedStreamIterable = class {
1083
+ minChunkSize;
1084
+ #readableStream;
1085
+ constructor(readableStream, { minChunkSize = DEFAULT_MIN_CHUNK_SIZE } = {}) {
1086
+ this.#readableStream = readableStream;
1087
+ this.minChunkSize = minChunkSize;
1088
+ }
1089
+ async *[Symbol.asyncIterator]() {
1090
+ let pending;
1091
+ const reader = this.#readableStream.getReader();
1092
+ try {
1093
+ while (true) {
1094
+ const { done, value } = await reader.read();
1095
+ if (done) {
1096
+ if (pending) yield pending;
1097
+ break;
1098
+ }
1099
+ pending = pending ? concat(pending, value) : value;
1100
+ if (pending.length >= this.minChunkSize) {
1101
+ yield pending;
1102
+ pending = void 0;
1103
+ }
1104
+ }
1105
+ } finally {
1106
+ reader.releaseLock();
1107
+ }
1108
+ }
1109
+ };
1110
+ function concat(a, b) {
1111
+ const result = new Uint8Array(a.length + b.length);
1112
+ result.set(a);
1113
+ result.set(b, a.length);
1114
+ return result;
1115
+ }
1116
+
1070
1117
  //#endregion
1071
1118
  //#region src/dom/network/fetch.ts
1072
1119
  /**
@@ -1103,16 +1150,6 @@ async function fetchResolvable(addressable, options) {
1103
1150
  return fetch(request);
1104
1151
  }
1105
1152
  /**
1106
- * Fetch resolvable as bytes.
1107
- *
1108
- * Convenience wrapper around fetchResolvable that resolves the body as an
1109
- * ArrayBuffer. Use this when you need the raw bytes (e.g. segment appends).
1110
- * For text or streaming consumption, use fetchResolvable directly.
1111
- */
1112
- async function fetchResolvableBytes(addressable, options) {
1113
- return (await fetchResolvable(addressable, options)).arrayBuffer();
1114
- }
1115
- /**
1116
1153
  * Extract text from Response.
1117
1154
  *
1118
1155
  * Accepts minimal Response-like object (just needs text() method).
@@ -1283,6 +1320,7 @@ function shouldResolve$1(state, event) {
1283
1320
  */
1284
1321
  function syncPreloadAttribute(state, owners) {
1285
1322
  return owners.subscribe((current) => {
1323
+ if (state.current.preload !== void 0) return;
1286
1324
  const preload = current.mediaElement?.preload || void 0;
1287
1325
  state.patch({ preload });
1288
1326
  });
@@ -1411,14 +1449,6 @@ const SelectedTrackIdKeyByType = {
1411
1449
  text: "selectedTextTrackId"
1412
1450
  };
1413
1451
  /**
1414
- * Map track type to buffer owner property key.
1415
- * Used for SourceBuffer references in owners.
1416
- */
1417
- const BufferKeyByType = {
1418
- video: "videoBuffer",
1419
- audio: "audioBuffer"
1420
- };
1421
- /**
1422
1452
  * Get selected track from state by type.
1423
1453
  * Returns properly typed track (partially or fully resolved) or undefined.
1424
1454
  * Type parameter T is inferred from the type argument.
@@ -1452,7 +1482,9 @@ function getSelectedTrack(state, type) {
1452
1482
  * operation (if still needed) or preempts it.
1453
1483
  *
1454
1484
  * @param sourceBufferActor - Shared SourceBufferActor reference (not owned)
1455
- * @param fetchBytes - Tracked fetch closure (owns throughput sampling)
1485
+ * @param fetchBytes - Tracked fetch closure (owns throughput sampling for segments).
1486
+ * Accepts an optional `minChunkSize` in options; init segments pass `Infinity`
1487
+ * so the entire body accumulates as one chunk before appending.
1456
1488
  */
1457
1489
  function createSegmentLoaderActor(sourceBufferActor, fetchBytes) {
1458
1490
  let pendingTasks = null;
@@ -1462,7 +1494,7 @@ function createSegmentLoaderActor(sourceBufferActor, fetchBytes) {
1462
1494
  let running = false;
1463
1495
  let destroyed = false;
1464
1496
  const getBufferedSegments = (allSegments) => {
1465
- const bufferedIds = new Set(sourceBufferActor.snapshot.context.segments.map((s) => s.id));
1497
+ const bufferedIds = new Set(sourceBufferActor.snapshot.context.segments.filter((s) => !s.partial).map((s) => s.id));
1466
1498
  return allSegments.filter((s) => bufferedIds.has(s.id));
1467
1499
  };
1468
1500
  /**
@@ -1503,12 +1535,14 @@ function createSegmentLoaderActor(sourceBufferActor, fetchBytes) {
1503
1535
  if (actorCtx.initTrackId !== track.id) tasks.push({
1504
1536
  type: "append-init",
1505
1537
  meta: { trackId: track.id },
1506
- url: track.initialization.url
1538
+ url: track.initialization.url,
1539
+ ...track.initialization.byteRange !== void 0 && { byteRange: track.initialization.byteRange }
1507
1540
  });
1508
1541
  if (range) {
1509
1542
  const EPSILON = 1e-4;
1510
1543
  const segmentsToLoad = getSegmentsToLoad(track.segments, bufferedSegments, currentTime).filter((seg) => {
1511
1544
  const existing = actorCtx.segments.find((s) => Math.abs(s.startTime - seg.startTime) < EPSILON);
1545
+ if (existing?.partial) return true;
1512
1546
  if (!existing?.trackBandwidth || !track.bandwidth) return true;
1513
1547
  return track.bandwidth > existing.trackBandwidth;
1514
1548
  });
@@ -1521,7 +1555,8 @@ function createSegmentLoaderActor(sourceBufferActor, fetchBytes) {
1521
1555
  trackId: track.id,
1522
1556
  trackBandwidth: track.bandwidth
1523
1557
  },
1524
- url: segment.url
1558
+ url: segment.url,
1559
+ ...segment.byteRange !== void 0 && { byteRange: segment.byteRange }
1525
1560
  });
1526
1561
  }
1527
1562
  return tasks;
@@ -1543,7 +1578,10 @@ function createSegmentLoaderActor(sourceBufferActor, fetchBytes) {
1543
1578
  if (task.type === "append-init") {
1544
1579
  inFlightInitTrackId = task.meta.trackId;
1545
1580
  if (!signal.aborted) {
1546
- const data = await fetchBytes(task, { signal });
1581
+ const data = await fetchBytes(task, {
1582
+ signal,
1583
+ minChunkSize: Infinity
1584
+ });
1547
1585
  const isTrackSwitch = pendingTasks?.some((t) => t.type === "append-init" && t.meta.trackId !== task.meta.trackId);
1548
1586
  if (!signal.aborted || !isTrackSwitch) {
1549
1587
  const appendSignal = signal.aborted ? new AbortController().signal : signal;
@@ -1558,10 +1596,10 @@ function createSegmentLoaderActor(sourceBufferActor, fetchBytes) {
1558
1596
  }
1559
1597
  inFlightSegmentId = task.meta.id;
1560
1598
  if (!signal.aborted) {
1561
- const data = await fetchBytes(task, { signal });
1599
+ const stream = await fetchBytes(task, { signal });
1562
1600
  if (!signal.aborted) await sourceBufferActor.send({
1563
1601
  type: "append-segment",
1564
- data,
1602
+ data: stream,
1565
1603
  meta: task.meta
1566
1604
  }, signal);
1567
1605
  }
@@ -1623,32 +1661,42 @@ function createSegmentLoaderActor(sourceBufferActor, fetchBytes) {
1623
1661
 
1624
1662
  //#endregion
1625
1663
  //#region src/dom/features/load-segments.ts
1626
- const ActorKeyByType$1 = {
1664
+ const ActorKeyByType = {
1627
1665
  video: "videoBufferActor",
1628
1666
  audio: "audioBufferActor"
1629
1667
  };
1630
- /**
1631
- * Creates a fetch function that transparently samples bandwidth after each
1632
- * completed request. Callers receive bytes; throughput tracking is invisible.
1633
- *
1634
- * `onSample` is an optional callback invoked after each sample is recorded,
1635
- * used for bridging throughput state outward (e.g. migration bridge to global
1636
- * state). A callback is used rather than a subscription so that no immediate
1637
- * fire occurs at setup time — subscriptions fire on registration and would
1638
- * trigger spurious state changes before any work has started.
1639
- */
1640
1668
  function createTrackedFetch(throughput, onSample) {
1641
1669
  return async (addressable, options) => {
1642
- const start = performance.now();
1643
- const data = await fetchResolvableBytes(addressable, options);
1644
- const elapsed = performance.now() - start;
1645
- const next = sampleBandwidth(throughput.current, elapsed, data.byteLength);
1646
- throughput.patch(next);
1647
- throughput.flush();
1648
- onSample?.(next);
1649
- return data;
1670
+ const { minChunkSize,...fetchOptions } = options ?? {};
1671
+ const response = await fetchResolvable(addressable, fetchOptions);
1672
+ if (!response.body) throw new Error("Response has no body");
1673
+ const body = response.body;
1674
+ return { [Symbol.asyncIterator]: async function* () {
1675
+ let chunkStart = performance.now();
1676
+ for await (const chunk of new ChunkedStreamIterable(body, ...minChunkSize !== void 0 ? [{ minChunkSize }] : [])) {
1677
+ const elapsed = performance.now() - chunkStart;
1678
+ const next = sampleBandwidth(throughput.current, elapsed, chunk.byteLength);
1679
+ throughput.patch(next);
1680
+ throughput.flush();
1681
+ onSample?.(next);
1682
+ yield chunk;
1683
+ chunkStart = performance.now();
1684
+ }
1685
+ } };
1650
1686
  };
1651
1687
  }
1688
+ /**
1689
+ * Non-tracking fetch: eagerly starts the request and returns the response body
1690
+ * as a lazy chunk iterable. Used for audio tracks which don't sample bandwidth.
1691
+ * Pass `minChunkSize: Infinity` to accumulate the full body as a single chunk
1692
+ * (equivalent to arrayBuffer() but through the same streaming path).
1693
+ */
1694
+ async function fetchStream(addressable, options) {
1695
+ const { minChunkSize,...fetchOptions } = options ?? {};
1696
+ const response = await fetchResolvable(addressable, fetchOptions);
1697
+ if (!response.body) throw new Error("Response has no body");
1698
+ return new ChunkedStreamIterable(response.body, ...minChunkSize !== void 0 ? [{ minChunkSize }] : []);
1699
+ }
1652
1700
  function selectLoadingInputs([segmentsCanLoad, state], type) {
1653
1701
  const { playbackInitiated, preload, currentTime } = state;
1654
1702
  return {
@@ -1731,7 +1779,7 @@ function loadingInputsEq(prevState, curState) {
1731
1779
  */
1732
1780
  function loadSegments({ state, owners }, config) {
1733
1781
  const { type } = config;
1734
- const actorKey = ActorKeyByType$1[type];
1782
+ const actorKey = ActorKeyByType[type];
1735
1783
  const initialBandwidth = state.current.bandwidthState;
1736
1784
  const throughput = createState(initialBandwidth ?? {
1737
1785
  fastEstimate: 0,
@@ -1743,7 +1791,7 @@ function loadSegments({ state, owners }, config) {
1743
1791
  const fetchBytes = type === "video" ? createTrackedFetch(throughput, initialBandwidth !== void 0 ? (next) => {
1744
1792
  state.patch({ bandwidthState: next });
1745
1793
  state.flush();
1746
- } : void 0) : fetchResolvableBytes;
1794
+ } : void 0) : fetchStream;
1747
1795
  const segmentLoader = createState(void 0);
1748
1796
  const unsubActorLifecycle = owners.subscribe((o) => o[actorKey], (actor) => {
1749
1797
  if (actor) segmentLoader.patch(createSegmentLoaderActor(actor, fetchBytes));
@@ -2080,25 +2128,30 @@ function trackPlaybackInitiated({ state, owners, events }) {
2080
2128
  //#endregion
2081
2129
  //#region src/dom/media/append-segment.ts
2082
2130
  /**
2083
- * Segment appender helper (P11)
2131
+ * Append media data to a SourceBuffer.
2084
2132
  *
2085
- * Appends media segments (ArrayBuffer) to SourceBuffer.
2086
- */
2087
- /**
2088
- * Append a media segment to a SourceBuffer.
2089
- *
2090
- * Waits for the SourceBuffer to be ready (not updating), then appends
2091
- * the segment data. Returns a promise that resolves when append completes.
2133
+ * Accepts either a full ArrayBuffer (single append) or an AsyncIterable of
2134
+ * Uint8Array chunks (one append per chunk, in order). Waits for `updateend`
2135
+ * between each call so appends are serialized correctly.
2092
2136
  *
2093
- * @param sourceBuffer - The SourceBuffer to append to
2094
- * @param segmentData - The segment data as ArrayBuffer
2095
- * @returns Promise that resolves when append completes
2096
- *
2097
- * @example
2098
- * const data = await fetch(segmentUrl).then(r => r.arrayBuffer());
2099
- * await appendSegment(videoSourceBuffer, data);
2137
+ * Errors from the SourceBuffer (`error` event) or from the iterable are
2138
+ * propagated as rejections.
2100
2139
  */
2101
- async function appendSegment(sourceBuffer, segmentData) {
2140
+ async function appendSegment(sourceBuffer, data, signal) {
2141
+ if (data instanceof ArrayBuffer) await appendChunk(sourceBuffer, data);
2142
+ else try {
2143
+ for await (const chunk of data) {
2144
+ if (signal?.aborted) throw signal.reason ?? new DOMException("Aborted", "AbortError");
2145
+ await appendChunk(sourceBuffer, chunk);
2146
+ }
2147
+ } catch (e) {
2148
+ if (e instanceof DOMException && e.name === "AbortError" && !sourceBuffer.updating) try {
2149
+ sourceBuffer.abort();
2150
+ } catch {}
2151
+ throw e;
2152
+ }
2153
+ }
2154
+ async function appendChunk(sourceBuffer, data) {
2102
2155
  if (sourceBuffer.updating) await new Promise((resolve) => {
2103
2156
  const onUpdateEnd = () => {
2104
2157
  sourceBuffer.removeEventListener("updateend", onUpdateEnd);
@@ -2122,7 +2175,7 @@ async function appendSegment(sourceBuffer, segmentData) {
2122
2175
  sourceBuffer.addEventListener("updateend", onUpdateEnd);
2123
2176
  sourceBuffer.addEventListener("error", onError);
2124
2177
  try {
2125
- sourceBuffer.appendBuffer(segmentData);
2178
+ sourceBuffer.appendBuffer(data);
2126
2179
  } catch (error) {
2127
2180
  cleanup();
2128
2181
  reject(error);
@@ -2570,7 +2623,7 @@ function isLastSegmentAppended(segments, actor) {
2570
2623
  if (segments.length === 0) return true;
2571
2624
  const lastSeg = segments[segments.length - 1];
2572
2625
  if (!lastSeg) return false;
2573
- return actor?.snapshot.context.segments.some((s) => s.id === lastSeg.id) ?? false;
2626
+ return actor?.snapshot.context.segments.some((s) => s.id === lastSeg.id && !s.partial) ?? false;
2574
2627
  }
2575
2628
  /**
2576
2629
  * Check if the last segment has been appended for each selected track.
@@ -2800,14 +2853,26 @@ function appendInitTask(message, { signal, getCtx, sourceBuffer }) {
2800
2853
  };
2801
2854
  }, { signal });
2802
2855
  }
2803
- function appendSegmentTask(message, { signal, getCtx, sourceBuffer }) {
2856
+ function appendSegmentTask(message, { signal, getCtx, sourceBuffer, onPartialContext }) {
2804
2857
  return new Task(async (taskSignal) => {
2805
2858
  const ctx = getCtx();
2806
2859
  if (taskSignal.aborted) return ctx;
2807
- await appendSegment(sourceBuffer, message.data);
2808
2860
  const { meta } = message;
2809
2861
  const EPSILON = 1e-4;
2810
2862
  const filtered = ctx.segments.filter((s) => Math.abs(s.startTime - meta.startTime) >= EPSILON);
2863
+ if (!(message.data instanceof ArrayBuffer)) onPartialContext({
2864
+ ...ctx,
2865
+ segments: [...filtered, {
2866
+ id: meta.id,
2867
+ startTime: meta.startTime,
2868
+ duration: meta.duration,
2869
+ trackId: meta.trackId,
2870
+ ...meta.trackBandwidth !== void 0 && { trackBandwidth: meta.trackBandwidth },
2871
+ partial: true
2872
+ }],
2873
+ bufferedRanges: ctx.bufferedRanges
2874
+ });
2875
+ await appendSegment(sourceBuffer, message.data, taskSignal);
2811
2876
  return {
2812
2877
  ...ctx,
2813
2878
  segments: [...filtered, {
@@ -2882,10 +2947,18 @@ function createSourceBufferActor(sourceBuffer, initialContext) {
2882
2947
  send(message, signal) {
2883
2948
  if (state.current.status !== "idle") return Promise.reject(new SourceBufferActorError(`send() called while actor is ${state.current.status}`));
2884
2949
  state.patch({ status: "updating" });
2950
+ const onPartialContext = (ctx) => {
2951
+ state.patch({
2952
+ status: "updating",
2953
+ context: ctx
2954
+ });
2955
+ state.flush();
2956
+ };
2885
2957
  const task = messageToTask(message, {
2886
2958
  signal,
2887
2959
  getCtx: () => state.current.context,
2888
- sourceBuffer
2960
+ sourceBuffer,
2961
+ onPartialContext
2889
2962
  });
2890
2963
  return runner.schedule(task).then(applyResult).catch(handleError);
2891
2964
  },
@@ -2894,11 +2967,19 @@ function createSourceBufferActor(sourceBuffer, initialContext) {
2894
2967
  if (messages.length === 0) return Promise.resolve();
2895
2968
  state.patch({ status: "updating" });
2896
2969
  let workingCtx = state.current.context;
2970
+ const onPartialContext = (ctx) => {
2971
+ state.patch({
2972
+ status: "updating",
2973
+ context: ctx
2974
+ });
2975
+ state.flush();
2976
+ };
2897
2977
  for (const message of messages.slice(0, -1)) {
2898
2978
  const task = messageToTask(message, {
2899
2979
  signal,
2900
2980
  getCtx: () => workingCtx,
2901
- sourceBuffer
2981
+ sourceBuffer,
2982
+ onPartialContext
2902
2983
  });
2903
2984
  runner.schedule(task).then((newCtx) => {
2904
2985
  workingCtx = newCtx;
@@ -2907,7 +2988,8 @@ function createSourceBufferActor(sourceBuffer, initialContext) {
2907
2988
  const lastTask = messageToTask(messages[messages.length - 1], {
2908
2989
  signal,
2909
2990
  getCtx: () => workingCtx,
2910
- sourceBuffer
2991
+ sourceBuffer,
2992
+ onPartialContext
2911
2993
  });
2912
2994
  return runner.schedule(lastTask).then(applyResult).catch(handleError);
2913
2995
  },
@@ -2921,30 +3003,6 @@ function createSourceBufferActor(sourceBuffer, initialContext) {
2921
3003
 
2922
3004
  //#endregion
2923
3005
  //#region src/dom/features/setup-sourcebuffer.ts
2924
- /** Map track type to SourceBufferActor owner property key. */
2925
- const ActorKeyByType = {
2926
- video: "videoBufferActor",
2927
- audio: "audioBufferActor"
2928
- };
2929
- /**
2930
- * Setup SourceBuffer task (module-level, pure).
2931
- * Creates SourceBuffer for resolved track and waits a frame before completing.
2932
- */
2933
- const setupSourceBufferTask = async ({ currentState, currentOwners }, context) => {
2934
- const track = getSelectedTrack(currentState, context.config.type);
2935
- if (!track || !isResolvedTrack(track)) return;
2936
- if (!track.codecs || track.codecs.length === 0) return;
2937
- const mimeCodec = buildMimeCodec(track);
2938
- const buffer = createSourceBuffer(currentOwners.mediaSource, mimeCodec);
2939
- const actor = createSourceBufferActor(buffer);
2940
- const bufferKey = BufferKeyByType[context.config.type];
2941
- const actorKey = ActorKeyByType[context.config.type];
2942
- context.owners.patch({
2943
- [bufferKey]: buffer,
2944
- [actorKey]: actor
2945
- });
2946
- await new Promise((resolve) => requestAnimationFrame(resolve));
2947
- };
2948
3006
  /**
2949
3007
  * Build MIME codec string from track metadata.
2950
3008
  *
@@ -2960,66 +3018,47 @@ function buildMimeCodec(track) {
2960
3018
  return `${track.mimeType}; codecs="${codecString}"`;
2961
3019
  }
2962
3020
  /**
2963
- * Check if we can setup SourceBuffer for track type.
3021
+ * Setup all needed SourceBuffers as a single coordinated operation.
2964
3022
  *
2965
- * Requires:
2966
- * - MediaSource exists in owners
2967
- * - Track is selected
3023
+ * Waits until ALL selected tracks (video and/or audio) are resolved with
3024
+ * codecs, then creates every SourceBuffer in one synchronous block before
3025
+ * patching owners. This guarantees that downstream consumers (e.g.
3026
+ * loadSegments) never see a partial set of SourceBuffers — preventing the
3027
+ * Firefox bug where appending to a video SourceBuffer before the audio
3028
+ * SourceBuffer exists causes mozHasAudio to be permanently false.
2968
3029
  *
2969
- * Note: We don't check mediaSource.readyState because owners holds references
2970
- * to mutable objects. Changes to properties on those objects won't trigger
2971
- * observations. Instead, setupMediaSource only patches owners.mediaSource after
2972
- * it's already open, so if it exists in owners, it's ready to use.
3030
+ * Handles video-only, audio-only, and combined presentations correctly:
3031
+ * only the tracks that are actually selected are waited on and created.
2973
3032
  *
2974
- * Note: Track does not need to be resolved yet. The orchestration will wait
2975
- * for the track to be resolved (via resolveTrack) before creating the SourceBuffer.
2976
- */
2977
- function canSetupBuffer(state, owners, type) {
2978
- if (!owners.mediaSource) return false;
2979
- if (!getSelectedTrack(state, type)) return false;
2980
- return true;
2981
- }
2982
- /**
2983
- * Check if we should create SourceBuffer (not already created).
2984
- */
2985
- function shouldSetupBuffer(owners, type) {
2986
- const bufferKey = BufferKeyByType[type];
2987
- return isUndefined(owners[bufferKey]);
2988
- }
2989
- /**
2990
- * Setup SourceBuffer orchestration.
2991
- *
2992
- * Triggers when:
2993
- * - MediaSource exists and is in 'open' state
2994
- * - Track is selected (same condition as resolveTrack)
2995
- *
2996
- * Creates SourceBuffer when track becomes resolved with codecs.
2997
- * This allows setupSourceBuffer to run in parallel with resolveTrack.
2998
- *
2999
- * Note: Text tracks don't use SourceBuffers and should be handled separately.
3000
- *
3001
- * Generic over track type - create one orchestration per track type:
3002
3033
  * @example
3003
- * const videoCleanup = setupSourceBuffer({ state, owners }, { type: 'video' });
3004
- * const audioCleanup = setupSourceBuffer({ state, owners }, { type: 'audio' });
3034
+ * const cleanup = setupSourceBuffers({ state, owners });
3005
3035
  */
3006
- function setupSourceBuffer({ state, owners }, config) {
3007
- let currentTask = null;
3036
+ function setupSourceBuffers({ state, owners }) {
3037
+ let setupDone = false;
3008
3038
  return combineLatest([state, owners]).subscribe(async ([currentState, currentOwners]) => {
3009
- if (currentTask) return;
3010
- if (!canSetupBuffer(currentState, currentOwners, config.type) || !shouldSetupBuffer(currentOwners, config.type)) return;
3011
- currentTask = setupSourceBufferTask({
3012
- currentState,
3013
- currentOwners
3014
- }, {
3015
- owners,
3016
- config
3017
- });
3018
- try {
3019
- await currentTask;
3020
- } finally {
3021
- currentTask = null;
3039
+ if (setupDone) return;
3040
+ if (!currentOwners.mediaSource) return;
3041
+ const videoSelected = !!currentState.selectedVideoTrackId;
3042
+ const audioSelected = !!currentState.selectedAudioTrackId;
3043
+ if (!videoSelected && !audioSelected) return;
3044
+ const videoTrack = videoSelected ? getSelectedTrack(currentState, "video") : null;
3045
+ const audioTrack = audioSelected ? getSelectedTrack(currentState, "audio") : null;
3046
+ if (videoSelected && (!videoTrack || !isResolvedTrack(videoTrack) || !videoTrack.codecs?.length)) return;
3047
+ if (audioSelected && (!audioTrack || !isResolvedTrack(audioTrack) || !audioTrack.codecs?.length)) return;
3048
+ setupDone = true;
3049
+ const patch = {};
3050
+ if (videoSelected && videoTrack && isResolvedTrack(videoTrack)) {
3051
+ const buffer = createSourceBuffer(currentOwners.mediaSource, buildMimeCodec(videoTrack));
3052
+ patch.videoBuffer = buffer;
3053
+ patch.videoBufferActor = createSourceBufferActor(buffer);
3022
3054
  }
3055
+ if (audioSelected && audioTrack && isResolvedTrack(audioTrack)) {
3056
+ const buffer = createSourceBuffer(currentOwners.mediaSource, buildMimeCodec(audioTrack));
3057
+ patch.audioBuffer = buffer;
3058
+ patch.audioBufferActor = createSourceBufferActor(buffer);
3059
+ }
3060
+ owners.patch(patch);
3061
+ await new Promise((resolve) => requestAnimationFrame(resolve));
3023
3062
  });
3024
3063
  }
3025
3064
 
@@ -3371,14 +3410,10 @@ function createPlaybackEngine(config = {}) {
3371
3410
  state,
3372
3411
  owners
3373
3412
  }),
3374
- setupSourceBuffer({
3413
+ setupSourceBuffers({
3375
3414
  state,
3376
3415
  owners
3377
- }, { type: "video" }),
3378
- setupSourceBuffer({
3379
- state,
3380
- owners
3381
- }, { type: "audio" }),
3416
+ }),
3382
3417
  trackCurrentTime({
3383
3418
  state,
3384
3419
  owners
@@ -3452,6 +3487,7 @@ function createPlaybackEngine(config = {}) {
3452
3487
  var SpfMedia = class {
3453
3488
  #engine;
3454
3489
  #config;
3490
+ #preload = "";
3455
3491
  /** Pending loadstart listener from a deferred play() retry, if any. */
3456
3492
  #loadstartListener = null;
3457
3493
  constructor(config = {}) {
@@ -3472,6 +3508,13 @@ var SpfMedia = class {
3472
3508
  this.#cancelPendingPlay();
3473
3509
  this.#engine.destroy();
3474
3510
  }
3511
+ get preload() {
3512
+ return this.#preload;
3513
+ }
3514
+ set preload(value) {
3515
+ this.#preload = value;
3516
+ if (value) this.#engine.state.patch({ preload: value });
3517
+ }
3475
3518
  get src() {
3476
3519
  return this.#engine.state.current.presentation?.url ?? "";
3477
3520
  }
@@ -3480,6 +3523,7 @@ var SpfMedia = class {
3480
3523
  this.#cancelPendingPlay();
3481
3524
  this.#engine.destroy();
3482
3525
  this.#engine = createPlaybackEngine(this.#config);
3526
+ if (this.#preload) this.#engine.state.patch({ preload: this.#preload });
3483
3527
  if (prevMediaElement) this.#engine.owners.patch({ mediaElement: prevMediaElement });
3484
3528
  if (value) this.#engine.state.patch({ presentation: { url: value } });
3485
3529
  }
@@ -3509,4 +3553,4 @@ var SpfMedia = class {
3509
3553
 
3510
3554
  //#endregion
3511
3555
  export { trackPlaybackInitiated as a, destroyVttParser as c, appendSegment as i, parseVttSegment as l, createPlaybackEngine as n, trackCurrentTime as o, flushBuffer as r, loadTextTrackCues as s, SpfMedia as t, loadSegments as u };
3512
- //# sourceMappingURL=adapter-CMw-rvbk.js.map
3556
+ //# sourceMappingURL=adapter-CflgYzjF.js.map