@openmoq/playback 0.0.0-stage → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/LICENSE.txt +202 -0
  2. package/README.md +18 -2
  3. package/dist/adaptive-tolerance.d.ts +128 -0
  4. package/dist/adaptive-tolerance.d.ts.map +1 -0
  5. package/dist/adaptive-tolerance.js +339 -0
  6. package/dist/adaptive-tolerance.js.map +1 -0
  7. package/dist/bandwidth-estimator.d.ts +49 -0
  8. package/dist/bandwidth-estimator.d.ts.map +1 -0
  9. package/dist/bandwidth-estimator.js +77 -0
  10. package/dist/bandwidth-estimator.js.map +1 -0
  11. package/dist/browser/index.global.js +2813 -0
  12. package/dist/browser/index.global.js.map +1 -0
  13. package/dist/browser/index.js +2788 -0
  14. package/dist/browser/index.js.map +1 -0
  15. package/dist/buffer-based-controller.d.ts +87 -0
  16. package/dist/buffer-based-controller.d.ts.map +1 -0
  17. package/dist/buffer-based-controller.js +85 -0
  18. package/dist/buffer-based-controller.js.map +1 -0
  19. package/dist/decoder-state.d.ts +103 -0
  20. package/dist/decoder-state.d.ts.map +1 -0
  21. package/dist/decoder-state.js +145 -0
  22. package/dist/decoder-state.js.map +1 -0
  23. package/dist/gap-detector.d.ts +83 -0
  24. package/dist/gap-detector.d.ts.map +1 -0
  25. package/dist/gap-detector.js +129 -0
  26. package/dist/gap-detector.js.map +1 -0
  27. package/dist/index.d.ts +33 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +33 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/jitter-buffer.d.ts +73 -0
  32. package/dist/jitter-buffer.d.ts.map +1 -0
  33. package/dist/jitter-buffer.js +216 -0
  34. package/dist/jitter-buffer.js.map +1 -0
  35. package/dist/keyframe-validator.d.ts +26 -0
  36. package/dist/keyframe-validator.d.ts.map +1 -0
  37. package/dist/keyframe-validator.js +170 -0
  38. package/dist/keyframe-validator.js.map +1 -0
  39. package/dist/pipeline.d.ts +215 -0
  40. package/dist/pipeline.d.ts.map +1 -0
  41. package/dist/pipeline.js +852 -0
  42. package/dist/pipeline.js.map +1 -0
  43. package/dist/recovery.d.ts +166 -0
  44. package/dist/recovery.d.ts.map +1 -0
  45. package/dist/recovery.js +176 -0
  46. package/dist/recovery.js.map +1 -0
  47. package/dist/render-scheduler.d.ts +50 -0
  48. package/dist/render-scheduler.d.ts.map +1 -0
  49. package/dist/render-scheduler.js +84 -0
  50. package/dist/render-scheduler.js.map +1 -0
  51. package/dist/startup-buffer.d.ts +68 -0
  52. package/dist/startup-buffer.d.ts.map +1 -0
  53. package/dist/startup-buffer.js +107 -0
  54. package/dist/startup-buffer.js.map +1 -0
  55. package/dist/sync.d.ts +234 -0
  56. package/dist/sync.d.ts.map +1 -0
  57. package/dist/sync.js +343 -0
  58. package/dist/sync.js.map +1 -0
  59. package/dist/types.d.ts +229 -0
  60. package/dist/types.d.ts.map +1 -0
  61. package/dist/types.js +12 -0
  62. package/dist/types.js.map +1 -0
  63. package/package.json +56 -3
package/dist/sync.js ADDED
@@ -0,0 +1,343 @@
1
+ /**
2
+ * A/V sync controller with injectable clock.
3
+ *
4
+ * Audio-master model: the first audio sample establishes a sync reference
5
+ * mapping CaptureTimestamp (wall-clock microseconds) to local clock time.
6
+ * Video render times are computed relative to this reference.
7
+ *
8
+ * Drift detection: reportPresentationTiming measures VIDEO PRESENTATION SCHEDULE
9
+ * drift — actual presentation time minus the exact scheduled time carried with
10
+ * the frame. If it exceeds the adaptive threshold, presentationDriftExceeded
11
+ * signals and a sync_drift event is emitted — but no automatic re-anchor occurs.
12
+ * Actual audio/video skew is a separate metric (sync_skew).
13
+ * The reference is set once to avoid audio glitching from re-anchor jumps.
14
+ * In practice, gap recovery (skip_forward) and seek/pause naturally re-anchor.
15
+ *
16
+ * Live catch-up: measures end-to-end latency by comparing CaptureTimestamp
17
+ * (publisher wall-clock) to subscriber wall-clock. When latency exceeds
18
+ * targetLatency + threshold, activates catch-up (playback rate > 1.0).
19
+ * Hysteresis prevents oscillation near the threshold boundary.
20
+ *
21
+ * @see draft-ietf-moq-loc-01 §2.3.1.1 (CaptureTimestamp)
22
+ * @see draft-ietf-moq-msf-00 §5.1.16 (targetLatency)
23
+ * @module
24
+ */
25
+ /** Default: frames more than 500ms late are dropped. */
26
+ const DEFAULT_DROP_THRESHOLD_US = 500_000;
27
+ /** Default wall clock — Date.now() converted to microseconds. */
28
+ const DEFAULT_WALL_CLOCK = { now: () => Date.now() * 1000 };
29
+ // ─── SyncController ─────────────────────────────────────────────────
30
+ /**
31
+ * A/V sync controller with live catch-up.
32
+ *
33
+ * @see draft-ietf-moq-loc-01 §2.3.1.1
34
+ * @see draft-ietf-moq-msf-00 §5.1.16
35
+ */
36
+ export class SyncController {
37
+ _driftThresholdUs;
38
+ dropThresholdUs;
39
+ clock;
40
+ /** Local clock time when audio reference was set. */
41
+ localBaselineUs;
42
+ /** CaptureTimestamp of the audio reference sample. */
43
+ captureBaselineUs;
44
+ /** Accumulated drift measurement. */
45
+ _currentDriftUs = 0;
46
+ /**
47
+ * Video join offset — shifts video render times forward during live join.
48
+ *
49
+ * When joining a live stream, video starts from the GOP boundary
50
+ * (which has an older CaptureTimestamp than the current audio).
51
+ * This offset shifts video render times forward so the first frames
52
+ * render in the future instead of the past, preventing startup stutter.
53
+ *
54
+ * Snaps to zero when the video timeline catches up to the audio live edge.
55
+ * Audio render times are unaffected.
56
+ */
57
+ _videoJoinOffsetUs = 0;
58
+ // ── Catch-up state ─────────────────────────────────────────
59
+ wallClock;
60
+ targetLatencyMs;
61
+ maxCatchUpRate;
62
+ catchUpThresholdMs;
63
+ catchUpRecoveryMs;
64
+ _catchUpActive = false;
65
+ _currentRate = 1.0;
66
+ _lastLatencyUs = 0;
67
+ constructor(config) {
68
+ this._driftThresholdUs = config.driftThresholdUs;
69
+ this.dropThresholdUs = config.dropThresholdUs ?? DEFAULT_DROP_THRESHOLD_US;
70
+ this.clock = config.clock;
71
+ // Catch-up config
72
+ this.wallClock = config.wallClock ?? DEFAULT_WALL_CLOCK;
73
+ this.targetLatencyMs = config.targetLatencyMs;
74
+ this.maxCatchUpRate = config.maxCatchUpRate ?? 1.0;
75
+ this.catchUpThresholdMs = config.catchUpThresholdMs ?? 500;
76
+ this.catchUpRecoveryMs = config.catchUpRecoveryMs ?? 50;
77
+ }
78
+ /** Whether a sync reference has been established. */
79
+ get hasReference() {
80
+ return this.localBaselineUs !== undefined;
81
+ }
82
+ /** Current drift magnitude in microseconds. */
83
+ get currentDriftUs() {
84
+ return this._currentDriftUs;
85
+ }
86
+ /** Current drift threshold in microseconds. Mutable for adaptive tolerance. */
87
+ get driftThresholdUs() { return this._driftThresholdUs; }
88
+ set driftThresholdUs(value) { this._driftThresholdUs = value; }
89
+ /**
90
+ * Whether PRESENTATION SCHEDULE drift exceeds the threshold.
91
+ *
92
+ * Deliberately has NO live-join suppression. The exact queued schedule
93
+ * already contains the join offset, so missing that schedule during a join
94
+ * is a real miss. (The legacy PTS-era metric needed the suppression because
95
+ * the offset itself showed up as drift; see {@link needsResync}.)
96
+ */
97
+ get presentationDriftExceeded() {
98
+ return Math.abs(this._currentDriftUs) >= this._driftThresholdUs;
99
+ }
100
+ /**
101
+ * @deprecated Threshold check for the legacy {@link reportActualRenderTime}
102
+ * metric, retaining its live-join suppression. Use
103
+ * {@link presentationDriftExceeded} with schedule-based measurement.
104
+ */
105
+ get needsResync() {
106
+ // Suppress during video join — under the LEGACY PTS-anchored metric the
107
+ // offset itself appeared as drift.
108
+ if (this._videoJoinOffsetUs > 0)
109
+ return false;
110
+ return Math.abs(this._currentDriftUs) >= this._driftThresholdUs;
111
+ }
112
+ /** Whether catch-up is currently active. */
113
+ get catchUpActive() {
114
+ return this._catchUpActive;
115
+ }
116
+ /** Current playback rate (1.0 when catch-up inactive). */
117
+ get currentRate() {
118
+ return this._currentRate;
119
+ }
120
+ /**
121
+ * Last measured latency in microseconds.
122
+ * 0 before first measurement.
123
+ * @see draft-ietf-moq-loc-01 §2.3.1.1
124
+ */
125
+ get latencyUs() {
126
+ return this._lastLatencyUs;
127
+ }
128
+ /**
129
+ * Establish sync reference from an audio sample.
130
+ * Maps captureTimestamp to the current local clock time.
131
+ * Resets drift measurement.
132
+ *
133
+ * @param captureTimestampUs CaptureTimestamp in microseconds
134
+ */
135
+ setAudioReference(captureTimestampUs) {
136
+ this.localBaselineUs = this.clock.now();
137
+ this.captureBaselineUs = captureTimestampUs;
138
+ this._currentDriftUs = 0;
139
+ }
140
+ /**
141
+ * Establish sync reference from a video frame (video-only mode).
142
+ * No-op if a reference already exists — audio has priority.
143
+ *
144
+ * @param captureTimestampUs CaptureTimestamp of the first video frame
145
+ */
146
+ setVideoReference(captureTimestampUs) {
147
+ if (this.hasReference)
148
+ return;
149
+ this.localBaselineUs = this.clock.now();
150
+ this.captureBaselineUs = captureTimestampUs;
151
+ this._currentDriftUs = 0;
152
+ }
153
+ /** Current video join offset in microseconds. 0 = no offset active. */
154
+ get videoJoinOffsetUs() {
155
+ return this._videoJoinOffsetUs;
156
+ }
157
+ /**
158
+ * Apply video join re-anchor for live stream join.
159
+ *
160
+ * Called when the first video keyframe is decoded after a live join.
161
+ * Computes an offset that shifts video render times forward so the
162
+ * first frame renders at `now + cushion` instead of in the past.
163
+ *
164
+ * The offset is a constant added to all video render times. It preserves
165
+ * inter-frame timing (33ms spacing) while shifting the whole timeline.
166
+ * Snaps to zero when video catches up to the live edge.
167
+ *
168
+ * @param firstKeyframeCaptureUs CaptureTimestamp of the first keyframe
169
+ * @param cushionUs Extra future cushion (default: 16ms ≈ 1 rAF tick)
170
+ */
171
+ onVideoJoin(firstKeyframeCaptureUs, cushionUs = 16_000) {
172
+ if (this.localBaselineUs === undefined || this.captureBaselineUs === undefined)
173
+ return;
174
+ const naiveRenderTimeUs = this.localBaselineUs +
175
+ Number(firstKeyframeCaptureUs - this.captureBaselineUs);
176
+ const nowUs = this.clock.now();
177
+ const gapUs = nowUs + cushionUs - naiveRenderTimeUs;
178
+ if (gapUs > 0) {
179
+ this._videoJoinOffsetUs = gapUs;
180
+ }
181
+ }
182
+ /**
183
+ * Compute render timing for a video frame.
184
+ *
185
+ * @param captureTimestampUs CaptureTimestamp in microseconds
186
+ */
187
+ computeVideoRenderTime(captureTimestampUs) {
188
+ const timing = this.computeRenderTime(captureTimestampUs);
189
+ if (timing === null)
190
+ return null;
191
+ if (this._videoJoinOffsetUs > 0) {
192
+ // Apply offset — shift render time forward
193
+ const adjustedRenderTimeUs = timing.renderTimeUs + this._videoJoinOffsetUs;
194
+ const nowUs = this.clock.now();
195
+ // Check if base render time (without offset) is now in the future
196
+ // → video has caught up to live edge, snap offset to zero
197
+ if (timing.renderTimeUs > nowUs) {
198
+ this._videoJoinOffsetUs = 0;
199
+ return timing; // use unmodified timing from here
200
+ }
201
+ const offsetUs = adjustedRenderTimeUs - nowUs;
202
+ return {
203
+ renderTimeUs: adjustedRenderTimeUs,
204
+ offsetUs,
205
+ shouldDrop: offsetUs < -this.dropThresholdUs,
206
+ };
207
+ }
208
+ return timing;
209
+ }
210
+ /**
211
+ * Compute render timing for an audio sample.
212
+ *
213
+ * @param captureTimestampUs CaptureTimestamp in microseconds
214
+ */
215
+ computeAudioRenderTime(captureTimestampUs) {
216
+ return this.computeRenderTime(captureTimestampUs);
217
+ }
218
+ /**
219
+ * Record PRESENTATION SCHEDULE drift: did the renderer present the frame
220
+ * when it was told to?
221
+ *
222
+ * `scheduledUs` must be the EXACT timestamp stored with the rendered frame.
223
+ * It already accounts for the playout cushion actually applied to that
224
+ * frame, the live-join offset, decode-output recomputation and late-frame
225
+ * handling — none of which may be recomputed or subtracted later, because an
226
+ * adaptive cushion can move between enqueue and presentation.
227
+ *
228
+ * The schedule is REQUIRED here. Optionality lives at
229
+ * `FrameRenderedFeedback`, where the pipeline suppresses the whole
230
+ * measurement/event transaction cleanly: missing evidence is not drift.
231
+ *
232
+ * Actual audio/video skew is measured separately as `sync_skew`.
233
+ */
234
+ reportPresentationTiming(scheduledUs, actualUs) {
235
+ this._currentDriftUs = actualUs - scheduledUs;
236
+ }
237
+ /**
238
+ * @deprecated Compares against the UNCUSHIONED PTS-anchored time, so a
239
+ * stream presenting exactly on its intended schedule reports the whole
240
+ * playout cushion as drift. Retained as a compatibility path only; use
241
+ * {@link reportPresentationTiming}.
242
+ */
243
+ reportActualRenderTime(captureTimestampUs, actualLocalUs) {
244
+ if (this.localBaselineUs === undefined || this.captureBaselineUs === undefined)
245
+ return;
246
+ const expectedRenderUs = this.localBaselineUs +
247
+ Number(captureTimestampUs - this.captureBaselineUs);
248
+ this._currentDriftUs = actualLocalUs - expectedRenderUs;
249
+ }
250
+ // ─── Live Catch-Up ──────────────────────────────────────────────
251
+ /**
252
+ * Measure end-to-end latency from a wall-clock timestamp.
253
+ *
254
+ * Compares the publisher's wall-clock timestamp to the subscriber's
255
+ * wall clock, both in Unix epoch microseconds. A media-time timestamp
256
+ * (LOC-04 with Timescale) has no wall-clock meaning, so latency is
257
+ * unmeasurable and this returns null.
258
+ *
259
+ * @param captureTimestampUs Timestamp in microseconds
260
+ * @param isWallClock False when the timestamp is media time. Undefined means wall clock.
261
+ * @returns Latency in microseconds, or null if unmeasurable
262
+ * @see draft-ietf-moq-loc-01 §2.3.1.1
263
+ * @see draft-ietf-moq-loc-04 §2.3.1.2
264
+ */
265
+ measureLatency(captureTimestampUs, isWallClock) {
266
+ if (captureTimestampUs === 0n || isWallClock === false)
267
+ return null;
268
+ return this.wallClock.now() - Number(captureTimestampUs);
269
+ }
270
+ /**
271
+ * Evaluate catch-up state based on current latency.
272
+ *
273
+ * Hysteresis: activates when latency > target + threshold,
274
+ * deactivates when latency <= target + recovery.
275
+ *
276
+ * Rate is proportional to overshoot, quantized to 0.05x increments.
277
+ *
278
+ * @param captureTimestampUs CaptureTimestamp in microseconds (Unix epoch)
279
+ * @returns CatchUpState if evaluated, null if catch-up is disabled or no timestamp
280
+ * @see draft-ietf-moq-msf-00 §5.1.16 (targetLatency)
281
+ */
282
+ evaluateCatchUp(captureTimestampUs, isWallClock) {
283
+ // Measure latency
284
+ const latencyUs = this.measureLatency(captureTimestampUs, isWallClock);
285
+ if (latencyUs === null)
286
+ return null;
287
+ this._lastLatencyUs = latencyUs;
288
+ // Catch-up disabled?
289
+ if (this.targetLatencyMs === undefined || this.maxCatchUpRate <= 1.0)
290
+ return null;
291
+ const latencyMs = latencyUs / 1000;
292
+ const targetMs = this.targetLatencyMs;
293
+ // Hysteresis: activate/deactivate
294
+ if (!this._catchUpActive && latencyMs > targetMs + this.catchUpThresholdMs) {
295
+ this._catchUpActive = true;
296
+ }
297
+ else if (this._catchUpActive && latencyMs <= targetMs + this.catchUpRecoveryMs) {
298
+ this._catchUpActive = false;
299
+ this._currentRate = 1.0;
300
+ return { active: false, currentRate: 1.0, latencyMs, targetMs };
301
+ }
302
+ if (this._catchUpActive) {
303
+ // Proportional rate: scale linearly with overshoot
304
+ const overshoot = latencyMs - targetMs;
305
+ const ratio = Math.min(Math.max(overshoot / this.catchUpThresholdMs, 0), 1);
306
+ const rawRate = 1.0 + (this.maxCatchUpRate - 1.0) * ratio;
307
+ // Quantize to 0.05x increments
308
+ const quantized = Math.round(rawRate * 20) / 20;
309
+ // Clamp to [1.0, maxCatchUpRate]
310
+ this._currentRate = Math.min(Math.max(quantized, 1.0), this.maxCatchUpRate);
311
+ }
312
+ return {
313
+ active: this._catchUpActive,
314
+ currentRate: this._currentRate,
315
+ latencyMs,
316
+ targetMs,
317
+ };
318
+ }
319
+ /**
320
+ * Reset the sync reference (e.g., after a skip-forward).
321
+ */
322
+ reset() {
323
+ this.localBaselineUs = undefined;
324
+ this.captureBaselineUs = undefined;
325
+ this._currentDriftUs = 0;
326
+ this._videoJoinOffsetUs = 0;
327
+ this._catchUpActive = false;
328
+ this._currentRate = 1.0;
329
+ }
330
+ // ─── Internal ───────────────────────────────────────────────────
331
+ computeRenderTime(captureTimestampUs) {
332
+ if (this.localBaselineUs === undefined || this.captureBaselineUs === undefined) {
333
+ return null;
334
+ }
335
+ const now = this.clock.now();
336
+ const renderTimeUs = this.localBaselineUs +
337
+ Number(captureTimestampUs - this.captureBaselineUs);
338
+ const offsetUs = renderTimeUs - now;
339
+ const shouldDrop = offsetUs < -this.dropThresholdUs;
340
+ return { renderTimeUs, offsetUs, shouldDrop };
341
+ }
342
+ }
343
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Sans-I/O playback core type definitions.
3
+ *
4
+ * These types define the boundary between the playback core (testable in
5
+ * Node.js) and the browser adapter (WebCodecs, Canvas, AudioContext).
6
+ *
7
+ * @see draft-ietf-moq-loc-01 §4 (Application Mapping)
8
+ * @see draft-ietf-moq-transport-16 §10 (Objects)
9
+ * @module
10
+ */
11
+ import type { VideoChunkInit, AudioChunkInit } from '@openmoq/loc';
12
+ import type { RecoveryAction } from './recovery.js';
13
+ /**
14
+ * Injectable clock source for testability.
15
+ *
16
+ * Browser adapter injects `performance.now() * 1000` (microseconds).
17
+ * Tests inject a manual clock with deterministic time.
18
+ *
19
+ * @see draft-ietf-moq-loc-01 §2.3.1.1 (CaptureTimestamp uses microseconds)
20
+ */
21
+ export interface ClockSource {
22
+ /** Current time in microseconds (monotonic). */
23
+ now(): number;
24
+ }
25
+ /**
26
+ * Commands emitted by the playback core to the browser decoder adapter.
27
+ * The core never calls WebCodecs directly — it produces these commands.
28
+ */
29
+ export type DecoderCommand = ConfigureCommand | DecodeVideoCommand | DecodeAudioCommand | FlushCommand | ResetCommand | SetPlaybackRateCommand;
30
+ /** Configure the decoder with codec extradata. @see draft-ietf-moq-loc-01 §2.3.2.1 */
31
+ export interface ConfigureCommand {
32
+ readonly type: 'configure';
33
+ readonly mediaType: 'video' | 'audio';
34
+ readonly config: Uint8Array;
35
+ }
36
+ /** Decode a video chunk at the specified render time. @see draft-ietf-moq-loc-01 §2.1 */
37
+ export interface DecodeVideoCommand {
38
+ readonly type: 'decode_video';
39
+ readonly chunk: VideoChunkInit;
40
+ readonly renderTimeUs: number;
41
+ /**
42
+ * CaptureTimestamp in microseconds — for PTS-anchored render scheduling.
43
+ * The renderer should use this (via RenderScheduler) instead of renderTimeUs
44
+ * for correct frame pacing after async decode.
45
+ * @see draft-ietf-moq-loc-01 §2.3.1.1
46
+ */
47
+ readonly captureTimestampUs?: bigint;
48
+ }
49
+ /** Decode an audio chunk at the specified render time. @see draft-ietf-moq-loc-01 §2 */
50
+ export interface DecodeAudioCommand {
51
+ readonly type: 'decode_audio';
52
+ readonly chunk: AudioChunkInit;
53
+ readonly renderTimeUs: number;
54
+ }
55
+ /** Flush the decoder (drain pending frames). */
56
+ export interface FlushCommand {
57
+ readonly type: 'flush';
58
+ readonly mediaType: 'video' | 'audio';
59
+ }
60
+ /** Reset the decoder (e.g., after a gap). */
61
+ export interface ResetCommand {
62
+ readonly type: 'reset';
63
+ readonly mediaType: 'video' | 'audio';
64
+ readonly reason: string;
65
+ }
66
+ /** Adjust audio playback rate for live catch-up. @see draft-ietf-moq-msf-00 §5.1.16 */
67
+ export interface SetPlaybackRateCommand {
68
+ readonly type: 'set_playback_rate';
69
+ readonly rate: number;
70
+ }
71
+ /**
72
+ * Observability events emitted by the playback core.
73
+ */
74
+ export type PlaybackEvent = GapDetectedEvent | SkipForwardEvent | SyncDriftEvent | KeyframeWaitingEvent | KeyframeValidationFailedEvent | TrackEndedEvent | RecoveryEvent | CatchUpChangedEvent | BacklogShedEvent | PartialGroupAbandonedEvent;
75
+ /** A partial video GOP was abandoned before END_OF_GROUP was received. */
76
+ export interface PartialGroupAbandonedEvent {
77
+ readonly type: 'partial_group_abandoned';
78
+ readonly fromGroupId: bigint;
79
+ readonly toGroupId: bigint;
80
+ readonly reason: string;
81
+ }
82
+ /** Old groups were dropped before decode to prevent backlog buildup. */
83
+ export interface BacklogShedEvent {
84
+ readonly type: 'backlog_shed';
85
+ readonly droppedGroups: number;
86
+ readonly remainingGroups: number;
87
+ readonly reason: string;
88
+ }
89
+ /** A group gap was detected. @see draft-ietf-moq-transport-16 §10.2.1.1 */
90
+ export interface GapDetectedEvent {
91
+ readonly type: 'gap_detected';
92
+ readonly groupId: bigint;
93
+ }
94
+ /** Skipped forward past a gap to a new group. */
95
+ export interface SkipForwardEvent {
96
+ readonly type: 'skip_forward';
97
+ readonly fromGroupId: bigint;
98
+ readonly toGroupId: bigint;
99
+ }
100
+ /**
101
+ * Video PRESENTATION SCHEDULE drift: the renderer presented a frame later (or
102
+ * earlier) than the exact time it was queued to present. This is NOT audio/video
103
+ * skew — see `sync_skew` for that.
104
+ */
105
+ export interface SyncDriftEvent {
106
+ readonly type: 'sync_drift';
107
+ readonly driftUs: number;
108
+ }
109
+ /** Waiting for a keyframe after a gap. @see draft-ietf-moq-loc-01 §2.3.2.2 */
110
+ export interface KeyframeWaitingEvent {
111
+ readonly type: 'keyframe_waiting';
112
+ readonly groupId: bigint;
113
+ }
114
+ /** Keyframe payload validation failed — bitstream does not start with a keyframe. @see draft-ietf-moq-loc-01 §4.2 */
115
+ export interface KeyframeValidationFailedEvent {
116
+ readonly type: 'keyframe_validation_failed';
117
+ readonly groupId: bigint;
118
+ readonly objectId: bigint;
119
+ readonly codec: string;
120
+ }
121
+ /** Track has ended (END_OF_TRACK received). @see draft-ietf-moq-transport-16 §10.2.1.1 */
122
+ export interface TrackEndedEvent {
123
+ readonly type: 'track_ended';
124
+ }
125
+ /** Recovery controller recommends an action (local policy). */
126
+ export interface RecoveryEvent {
127
+ readonly type: 'recovery';
128
+ readonly action: RecoveryAction;
129
+ }
130
+ /**
131
+ * Catch-up state — latency measurement and rate adjustment.
132
+ *
133
+ * @see draft-ietf-moq-msf-00 §5.1.16 (targetLatency)
134
+ * @see draft-ietf-moq-loc-01 §2.3.1.1 (CaptureTimestamp for latency measurement)
135
+ */
136
+ export interface CatchUpState {
137
+ /** Whether catch-up is currently active. */
138
+ readonly active: boolean;
139
+ /** Current playback rate (1.0 = normal, >1.0 = catching up). */
140
+ readonly currentRate: number;
141
+ /** Current end-to-end latency in milliseconds. */
142
+ readonly latencyMs: number;
143
+ /** Target latency in milliseconds. */
144
+ readonly targetMs: number;
145
+ }
146
+ /** Catch-up state changed (activated, rate adjusted, or deactivated). @see draft-ietf-moq-msf-00 §5.1.16 */
147
+ export interface CatchUpChangedEvent {
148
+ readonly type: 'catch_up_changed';
149
+ readonly state: CatchUpState;
150
+ }
151
+ /**
152
+ * Feedback from browser adapters back to the pipeline.
153
+ *
154
+ * Flow: adapter → CommandDispatcher → player → pipeline.handleFeedback()
155
+ *
156
+ * @see draft-ietf-moq-loc-01 §2.3.1.1 (CaptureTimestamp for drift detection)
157
+ */
158
+ export type DecoderFeedback = QueuePressureFeedback | DecodeErrorFeedback | FrameRenderedFeedback | FlushCompleteFeedback;
159
+ /** Decoder queue getting deep — signal to throttle draining. */
160
+ export interface QueuePressureFeedback {
161
+ readonly type: 'queue_pressure';
162
+ readonly mediaType: 'video' | 'audio';
163
+ readonly depth: number;
164
+ readonly maxRecommended: number;
165
+ }
166
+ /** A decode() call failed. */
167
+ export interface DecodeErrorFeedback {
168
+ readonly type: 'decode_error';
169
+ readonly mediaType: 'video' | 'audio';
170
+ readonly message: string;
171
+ }
172
+ /** Actual render time for drift detection — video only. @see draft-ietf-moq-loc-01 §2.3.1.1 */
173
+ export interface FrameRenderedFeedback {
174
+ readonly type: 'frame_rendered';
175
+ readonly mediaType: 'video';
176
+ readonly captureTimestampUs: bigint;
177
+ readonly actualRenderUs: number;
178
+ /**
179
+ * The exact time this frame was scheduled to present. Absent when the
180
+ * renderer cannot report it, in which case presentation-schedule drift is
181
+ * suppressed rather than fabricated.
182
+ */
183
+ readonly scheduledRenderUs?: number;
184
+ }
185
+ /** Flush completed (forward compatibility — currently a no-op). */
186
+ export interface FlushCompleteFeedback {
187
+ readonly type: 'flush_complete';
188
+ readonly mediaType: 'video' | 'audio';
189
+ }
190
+ /**
191
+ * Playback pipeline configuration.
192
+ */
193
+ export interface PlaybackConfig {
194
+ /** Maximum wait time in microseconds for a missing group before skip-forward. */
195
+ readonly gapTimeoutUs: number;
196
+ /** Video presentation-schedule drift threshold in microseconds. */
197
+ readonly driftThresholdUs: number;
198
+ /** Maximum number of objects in the jitter buffer. */
199
+ readonly maxBufferDepth: number;
200
+ /** Target end-to-end latency in milliseconds. @see draft-ietf-moq-msf-00 §5.1.16 */
201
+ readonly targetLatencyMs?: number;
202
+ /** Maximum playback rate for catch-up (e.g., 1.1 = 10% faster). Default: 1.0 (disabled). */
203
+ readonly maxCatchUpRate?: number;
204
+ /** Latency above target before catch-up activates (ms). Default: 500. */
205
+ readonly catchUpThresholdMs?: number;
206
+ /** Latency above target before catch-up deactivates (ms, hysteresis). Default: 50. */
207
+ readonly catchUpRecoveryMs?: number;
208
+ /**
209
+ * Maximum objects to release from the jitter buffer per tick (video only).
210
+ * Prevents greedy drain from flooding the decoder with backlog.
211
+ * 0 = unlimited (greedy drain, legacy behavior). Default: 5.
212
+ */
213
+ readonly maxReleasePerTick?: number;
214
+ /**
215
+ * Maximum groups allowed in the jitter buffer before shedding old ones.
216
+ * When exceeded, oldest groups are dropped before decode.
217
+ * 0 = unlimited (no shedding). Default: 3.
218
+ */
219
+ readonly maxBacklogGroups?: number;
220
+ /** Enable adaptive jitter tolerance. Auto-calibrates gap timeout and drift threshold. */
221
+ readonly adaptiveTolerance?: boolean;
222
+ /**
223
+ * QUIC handshake RTT in milliseconds. Used to classify network conditions
224
+ * and determine startup buffer depth. If not provided, defaults to
225
+ * conservative WAN parameters (8 frames / 300ms).
226
+ */
227
+ readonly handshakeRttMs?: number;
228
+ }
229
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Sans-I/O playback core type definitions.
3
+ *
4
+ * These types define the boundary between the playback core (testable in
5
+ * Node.js) and the browser adapter (WebCodecs, Canvas, AudioContext).
6
+ *
7
+ * @see draft-ietf-moq-loc-01 §4 (Application Mapping)
8
+ * @see draft-ietf-moq-transport-16 §10 (Objects)
9
+ * @module
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
package/package.json CHANGED
@@ -1,6 +1,59 @@
1
1
  {
2
2
  "name": "@openmoq/playback",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.6.0",
4
+ "description": "Sans-I/O media playback core for Media over QUIC (MoQ): jitter buffer, A/V sync, gap detection, decoder state machine, and pipeline orchestration.",
5
+ "license": "Apache-2.0",
6
+ "author": "OpenMOQ (https://github.com/openmoq)",
7
+ "contributors": [
8
+ "Raymond Lucke <ray@raylucke.com>"
9
+ ],
10
+ "type": "module",
11
+ "main": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js"
17
+ },
18
+ "./browser": {
19
+ "import": "./dist/browser/index.js",
20
+ "default": "./dist/browser/index.js"
21
+ }
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "keywords": [
27
+ "moq",
28
+ "media-over-quic",
29
+ "moqt",
30
+ "quic",
31
+ "streaming",
32
+ "low-latency",
33
+ "live-video",
34
+ "playback",
35
+ "sans-io",
36
+ "jitter-buffer",
37
+ "av-sync"
38
+ ],
39
+ "homepage": "https://github.com/openmoq/moq-playa/tree/main/packages/playback#readme",
40
+ "bugs": {
41
+ "url": "https://github.com/openmoq/moq-playa/issues"
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git+https://github.com/openmoq/moq-playa.git",
46
+ "directory": "packages/playback"
47
+ },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ },
51
+ "dependencies": {
52
+ "@openmoq/transport": "0.6.0",
53
+ "@openmoq/loc": "0.6.0"
54
+ },
55
+ "scripts": {
56
+ "build": "tsc && tsup",
57
+ "build:browser": "tsup"
58
+ }
6
59
  }