avbridge 2.12.0 → 2.13.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.
- package/CHANGELOG.md +177 -0
- package/README.md +33 -0
- package/dist/{avi-EQE6AR75.cjs → avi-32UABODO.cjs} +12 -4
- package/dist/avi-32UABODO.cjs.map +1 -0
- package/dist/{avi-Y3N325WZ.cjs → avi-5BPR6QUX.cjs} +12 -4
- package/dist/avi-5BPR6QUX.cjs.map +1 -0
- package/dist/{avi-NNHH4AAA.js → avi-BLIH7KKV.js} +12 -4
- package/dist/avi-BLIH7KKV.js.map +1 -0
- package/dist/{avi-S7EY54YA.js → avi-GX2H34IQ.js} +12 -4
- package/dist/avi-GX2H34IQ.js.map +1 -0
- package/dist/{chunk-2LNXMGT6.js → chunk-5CX7BVVV.js} +5 -5
- package/dist/{chunk-2LNXMGT6.js.map → chunk-5CX7BVVV.js.map} +1 -1
- package/dist/{chunk-5Y5BTB5D.js → chunk-B76QWPFM.js} +3 -3
- package/dist/{chunk-5Y5BTB5D.js.map → chunk-B76QWPFM.js.map} +1 -1
- package/dist/{chunk-GJBNLPGI.cjs → chunk-E5MAM2P4.cjs} +9 -9
- package/dist/{chunk-GJBNLPGI.cjs.map → chunk-E5MAM2P4.cjs.map} +1 -1
- package/dist/{chunk-7EF4VTUS.cjs → chunk-OFJYEITB.cjs} +489 -113
- package/dist/chunk-OFJYEITB.cjs.map +1 -0
- package/dist/{chunk-HBHSUGNI.cjs → chunk-VLI3Y6IJ.cjs} +5 -5
- package/dist/{chunk-HBHSUGNI.cjs.map → chunk-VLI3Y6IJ.cjs.map} +1 -1
- package/dist/{chunk-Z26PXRUY.js → chunk-VOC24LYF.js} +486 -110
- package/dist/chunk-VOC24LYF.js.map +1 -0
- package/dist/element-browser.js +492 -130
- package/dist/element-browser.js.map +1 -1
- package/dist/element.cjs +3 -3
- package/dist/element.js +2 -2
- package/dist/index.cjs +18 -18
- package/dist/index.js +6 -6
- package/dist/player.cjs +658 -170
- package/dist/player.cjs.map +1 -1
- package/dist/player.d.cts +36 -4
- package/dist/player.d.ts +36 -4
- package/dist/player.js +658 -170
- package/dist/player.js.map +1 -1
- package/dist/{remux-VPKCLHHM.cjs → remux-NSBJFMLG.cjs} +9 -9
- package/dist/{remux-VPKCLHHM.cjs.map → remux-NSBJFMLG.cjs.map} +1 -1
- package/dist/{remux-7TA4FKTY.js → remux-PHUHO3VV.js} +4 -4
- package/dist/{remux-7TA4FKTY.js.map → remux-PHUHO3VV.js.map} +1 -1
- package/package.json +1 -1
- package/src/element/avbridge-player.ts +223 -43
- package/src/probe/avi.ts +34 -2
- package/src/strategies/fallback/audio-output.ts +164 -35
- package/src/strategies/fallback/decoder.ts +467 -60
- package/src/strategies/fallback/video-renderer.ts +209 -29
- package/src/strategies/hybrid/decoder.ts +56 -28
- package/src/strategies/remux/pipeline.ts +12 -3
- package/vendor/libav/avbridge/libav-6.8.8.0-avbridge.wasm.mjs +1 -1
- package/vendor/libav/avbridge/libav-6.8.8.0-avbridge.wasm.wasm +0 -0
- package/vendor/libav/avbridge/libav-avbridge.mjs +1 -1
- package/dist/avi-EQE6AR75.cjs.map +0 -1
- package/dist/avi-NNHH4AAA.js.map +0 -1
- package/dist/avi-S7EY54YA.js.map +0 -1
- package/dist/avi-Y3N325WZ.cjs.map +0 -1
- package/dist/chunk-7EF4VTUS.cjs.map +0 -1
- package/dist/chunk-Z26PXRUY.js.map +0 -1
|
@@ -35,6 +35,17 @@ interface PendingChunk {
|
|
|
35
35
|
sampleRate: number;
|
|
36
36
|
frameCount: number;
|
|
37
37
|
durationSec: number;
|
|
38
|
+
/** Source-domain content PTS in seconds. `null` for legacy callers
|
|
39
|
+
* that schedule sequentially without PTS information. */
|
|
40
|
+
ptsSec: number | null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** True when `globalThis.AVBRIDGE_DEBUG` is set. Used to gate [TRACE-AUD]
|
|
44
|
+
* per-chunk logs that are useful for diagnosing scheduling drift but
|
|
45
|
+
* unreadable in normal use. */
|
|
46
|
+
function isDebug(): boolean {
|
|
47
|
+
return typeof globalThis !== "undefined"
|
|
48
|
+
&& !!(globalThis as Record<string, unknown>).AVBRIDGE_DEBUG;
|
|
38
49
|
}
|
|
39
50
|
|
|
40
51
|
export interface ClockSource {
|
|
@@ -42,6 +53,14 @@ export interface ClockSource {
|
|
|
42
53
|
now(): number;
|
|
43
54
|
/** True if media is currently playing (audio scheduler is running). */
|
|
44
55
|
isPlaying(): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Media time at which the current playback session was anchored — i.e. the
|
|
58
|
+
* seek target after the most recent `reset()`, or 0 on cold start. Used by
|
|
59
|
+
* the video renderer for post-flush PTS calibration: `now()` includes any
|
|
60
|
+
* decode-stall lag accumulated since playback resumed, but the anchor is
|
|
61
|
+
* a stable reference that maps directly to the user's intended position.
|
|
62
|
+
*/
|
|
63
|
+
anchorTime(): number;
|
|
45
64
|
}
|
|
46
65
|
|
|
47
66
|
export class AudioOutput implements ClockSource {
|
|
@@ -69,6 +88,17 @@ export class AudioOutput implements ClockSource {
|
|
|
69
88
|
|
|
70
89
|
/** Anchor: media time `mediaTimeOfAnchor` corresponds to ctx time `ctxTimeAtAnchor`. */
|
|
71
90
|
private mediaTimeOfAnchor = 0;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Ctx time at which the first audible chunk will start playing. `-1`
|
|
94
|
+
* before any chunk has been scheduled successfully (clock is frozen);
|
|
95
|
+
* the actual ctx time once one has. The renderer's `clock.now()` uses
|
|
96
|
+
* this to avoid advancing during the silent-gap window between
|
|
97
|
+
* `audio.start()` and the first chunk that schedules without being
|
|
98
|
+
* dropped — that gap is what produces the "audio-less fast-forward"
|
|
99
|
+
* the user sees post-seek when the gate releases on video-only grace.
|
|
100
|
+
*/
|
|
101
|
+
private firstAudibleCtxStart = -1;
|
|
72
102
|
private ctxTimeAtAnchor = 0;
|
|
73
103
|
|
|
74
104
|
private pendingQueue: PendingChunk[] = [];
|
|
@@ -154,11 +184,30 @@ export class AudioOutput implements ClockSource {
|
|
|
154
184
|
return this.mediaTimeOfAnchor;
|
|
155
185
|
}
|
|
156
186
|
if (this.state === "playing") {
|
|
187
|
+
// Freeze the clock until the first audio chunk has actually been
|
|
188
|
+
// scheduled. Without this, when `audio.start()` fires before any
|
|
189
|
+
// post-seek audio packets have made it through the decoder (e.g. the
|
|
190
|
+
// gate's "video-only grace" path released early), `clock.now()`
|
|
191
|
+
// would advance from `mediaTimeOfAnchor` at 1× wall time while the
|
|
192
|
+
// audio scheduler is dropping every chunk that arrives (their
|
|
193
|
+
// PTS-derived `ctxStart` is already in the past). The renderer would
|
|
194
|
+
// paint frames during that silent window — the user perceives that
|
|
195
|
+
// as a "fast-forward burst with no audio." When the first chunk
|
|
196
|
+
// finally arrives and schedules normally, `firstAudibleCtxStart` is
|
|
197
|
+
// set and the clock unfreezes from there in sync with the audible
|
|
198
|
+
// content's PTS.
|
|
199
|
+
if (this.firstAudibleCtxStart < 0) {
|
|
200
|
+
return this.mediaTimeOfAnchor;
|
|
201
|
+
}
|
|
157
202
|
return this.mediaTimeOfAnchor + (this.ctx.currentTime - this.ctxTimeAtAnchor) * this._rate;
|
|
158
203
|
}
|
|
159
204
|
return this.mediaTimeOfAnchor;
|
|
160
205
|
}
|
|
161
206
|
|
|
207
|
+
anchorTime(): number {
|
|
208
|
+
return this.mediaTimeOfAnchor;
|
|
209
|
+
}
|
|
210
|
+
|
|
162
211
|
isPlaying(): boolean {
|
|
163
212
|
return this.state === "playing";
|
|
164
213
|
}
|
|
@@ -192,18 +241,55 @@ export class AudioOutput implements ClockSource {
|
|
|
192
241
|
* Schedule a chunk of decoded samples. Queues internally while idle (cold
|
|
193
242
|
* start or post-seek), schedules directly to the audio graph while playing.
|
|
194
243
|
* In wall-clock mode, samples are silently discarded.
|
|
244
|
+
*
|
|
245
|
+
* `ptsSec` is the chunk's source-domain content PTS in seconds, from
|
|
246
|
+
* the demuxer. When provided, the chunk plays at the ctx-time
|
|
247
|
+
* corresponding to that PTS — so pre-target audio after a seek
|
|
248
|
+
* naturally drops (its computed `ctxStart` falls in the past) and
|
|
249
|
+
* post-target audio plays at its true content time, without any
|
|
250
|
+
* external trim or anchor rebase. When `ptsSec` is null (cold start
|
|
251
|
+
* with no PTS yet, or codecs whose packet→frame mapping isn't 1:1),
|
|
252
|
+
* the chunk is scheduled sequentially after `mediaTimeOfNext` — the
|
|
253
|
+
* pre-refactor behavior.
|
|
195
254
|
*/
|
|
196
|
-
schedule(
|
|
255
|
+
schedule(
|
|
256
|
+
samples: Float32Array,
|
|
257
|
+
channels: number,
|
|
258
|
+
sampleRate: number,
|
|
259
|
+
ptsSec?: number | null,
|
|
260
|
+
): void {
|
|
197
261
|
if (this.destroyed || this.noAudio) return;
|
|
198
262
|
const frameCount = samples.length / channels;
|
|
199
263
|
const durationSec = frameCount / sampleRate;
|
|
264
|
+
const hasPts = ptsSec != null && Number.isFinite(ptsSec);
|
|
265
|
+
|
|
266
|
+
// Pre-target gate: a chunk whose entire PTS span is before the
|
|
267
|
+
// current media anchor will be silently dropped by `scheduleNow`
|
|
268
|
+
// (its `ctxStart` falls in the past). We must apply the same drop
|
|
269
|
+
// here in idle/paused state too — otherwise the chunk sits in
|
|
270
|
+
// `pendingQueue`, `bufferAhead()` reports it as buffered audio,
|
|
271
|
+
// `waitForBuffer()`'s gate releases on a phantom audio buffer, and
|
|
272
|
+
// `audio.start()` fires with a queue full of chunks that immediately
|
|
273
|
+
// drop on drain. The user sees post-seek "sped up no audio" while
|
|
274
|
+
// the demuxer slowly chews through pre-target packets — `clock.now()`
|
|
275
|
+
// is advancing on wall time and the renderer paints video against
|
|
276
|
+
// it, but `node.start()` is never being called.
|
|
277
|
+
if (hasPts && (ptsSec as number) + durationSec / this._rate < this.mediaTimeOfAnchor) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
200
280
|
|
|
201
281
|
if (this.state === "idle" || this.state === "paused") {
|
|
202
|
-
this.pendingQueue.push({
|
|
282
|
+
this.pendingQueue.push({
|
|
283
|
+
samples, channels, sampleRate, frameCount, durationSec,
|
|
284
|
+
ptsSec: hasPts ? (ptsSec as number) : null,
|
|
285
|
+
});
|
|
203
286
|
return;
|
|
204
287
|
}
|
|
205
288
|
|
|
206
|
-
this.scheduleNow(
|
|
289
|
+
this.scheduleNow(
|
|
290
|
+
samples, channels, sampleRate, frameCount,
|
|
291
|
+
hasPts ? (ptsSec as number) : null,
|
|
292
|
+
);
|
|
207
293
|
}
|
|
208
294
|
|
|
209
295
|
private scheduleNow(
|
|
@@ -211,7 +297,67 @@ export class AudioOutput implements ClockSource {
|
|
|
211
297
|
channels: number,
|
|
212
298
|
sampleRate: number,
|
|
213
299
|
frameCount: number,
|
|
300
|
+
ptsSec: number | null,
|
|
214
301
|
): void {
|
|
302
|
+
const durationSec = frameCount / sampleRate;
|
|
303
|
+
|
|
304
|
+
// Compute ctxStart. Two paths:
|
|
305
|
+
//
|
|
306
|
+
// PTS-known: the chunk's content PTS maps to a specific ctx time
|
|
307
|
+
// via (mediaTimeOfAnchor, ctxTimeAtAnchor). If that ctx time is
|
|
308
|
+
// already in the past, the chunk represents audio the user should
|
|
309
|
+
// have heard before now — drop it. After a seek, this is what
|
|
310
|
+
// *automatically* skips pre-target audio packets returned by a
|
|
311
|
+
// keyframe-aligned demuxer seek; no manual trim needed.
|
|
312
|
+
//
|
|
313
|
+
// PTS-unknown (legacy): chain after the last-scheduled sample
|
|
314
|
+
// via `mediaTimeOfNext`. Same behavior as before the refactor.
|
|
315
|
+
let ctxStart: number;
|
|
316
|
+
if (ptsSec != null) {
|
|
317
|
+
ctxStart = this.ctxTimeAtAnchor + (ptsSec - this.mediaTimeOfAnchor) / this._rate;
|
|
318
|
+
if (isDebug()) {
|
|
319
|
+
// eslint-disable-next-line no-console
|
|
320
|
+
console.log(`[TRACE-AUD] PTS sched #${this.framesScheduled} pts=${ptsSec.toFixed(3)} dur=${durationSec.toFixed(4)} ctxStart=${ctxStart.toFixed(4)} ctxNow=${this.ctx.currentTime.toFixed(4)} anchor=${this.mediaTimeOfAnchor.toFixed(3)} ctxAnchor=${this.ctxTimeAtAnchor.toFixed(4)} mtNext=${this.mediaTimeOfNext.toFixed(3)} rate=${this._rate}`);
|
|
321
|
+
}
|
|
322
|
+
if (ctxStart < this.ctx.currentTime - 0.001) {
|
|
323
|
+
if (isDebug()) {
|
|
324
|
+
// eslint-disable-next-line no-console
|
|
325
|
+
console.log(`[TRACE-AUD] DROP late chunk pts=${ptsSec.toFixed(3)} ctxStart=${ctxStart.toFixed(4)} < ctxNow=${this.ctx.currentTime.toFixed(4)}`);
|
|
326
|
+
}
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
// First chunk to schedule successfully unfreezes `clock.now()`.
|
|
330
|
+
// We rebase the anchor onto this chunk: when ctx reaches `ctxStart`,
|
|
331
|
+
// clock should equal `ptsSec` (so `audioNow` matches audible content
|
|
332
|
+
// PTS exactly when the chunk plays). The renderer's deadline will
|
|
333
|
+
// then advance from there, in lockstep with what's audible.
|
|
334
|
+
if (this.firstAudibleCtxStart < 0) {
|
|
335
|
+
this.firstAudibleCtxStart = ctxStart;
|
|
336
|
+
this.mediaTimeOfAnchor = ptsSec;
|
|
337
|
+
this.ctxTimeAtAnchor = ctxStart;
|
|
338
|
+
if (isDebug()) {
|
|
339
|
+
// eslint-disable-next-line no-console
|
|
340
|
+
console.log(`[TRACE-AUD] UNFREEZE clock — first audible chunk pts=${ptsSec.toFixed(3)} ctxStart=${ctxStart.toFixed(4)} → anchor=${this.mediaTimeOfAnchor.toFixed(3)} ctxAnchor=${this.ctxTimeAtAnchor.toFixed(4)}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
const endMediaTime = ptsSec + durationSec / this._rate;
|
|
344
|
+
if (endMediaTime > this.mediaTimeOfNext) {
|
|
345
|
+
this.mediaTimeOfNext = endMediaTime;
|
|
346
|
+
}
|
|
347
|
+
} else {
|
|
348
|
+
ctxStart = this.ctxTimeAtAnchor + (this.mediaTimeOfNext - this.mediaTimeOfAnchor) / this._rate;
|
|
349
|
+
// eslint-disable-next-line no-console
|
|
350
|
+
console.warn(`[TRACE-AUD] LEGACY (no PTS) sched dur=${durationSec.toFixed(4)} ctxStart=${ctxStart.toFixed(4)} ctxNow=${this.ctx.currentTime.toFixed(4)}`);
|
|
351
|
+
if (ctxStart < this.ctx.currentTime) {
|
|
352
|
+
// eslint-disable-next-line no-console
|
|
353
|
+
console.warn(`[TRACE-AUD] REBASE anchor was=${this.mediaTimeOfAnchor.toFixed(3)} ctxAnchor was=${this.ctxTimeAtAnchor.toFixed(4)} → anchor=${this.mediaTimeOfNext.toFixed(3)} ctxAnchor=${this.ctx.currentTime.toFixed(4)}`);
|
|
354
|
+
this.ctxTimeAtAnchor = this.ctx.currentTime;
|
|
355
|
+
this.mediaTimeOfAnchor = this.mediaTimeOfNext;
|
|
356
|
+
ctxStart = this.ctx.currentTime;
|
|
357
|
+
}
|
|
358
|
+
this.mediaTimeOfNext += durationSec;
|
|
359
|
+
}
|
|
360
|
+
|
|
215
361
|
const buffer = this.ctx.createBuffer(channels, frameCount, sampleRate);
|
|
216
362
|
for (let ch = 0; ch < channels; ch++) {
|
|
217
363
|
const channelData = buffer.getChannelData(ch);
|
|
@@ -222,38 +368,8 @@ export class AudioOutput implements ClockSource {
|
|
|
222
368
|
const node = this.ctx.createBufferSource();
|
|
223
369
|
node.buffer = buffer;
|
|
224
370
|
node.connect(this.gain);
|
|
225
|
-
// Pitch the audio to match the playback rate (same as native <video>).
|
|
226
371
|
if (this._rate !== 1) node.playbackRate.value = this._rate;
|
|
227
|
-
|
|
228
|
-
// Convert media time → ctx time using the anchor + rate. At rate=2,
|
|
229
|
-
// each second of media time occupies 0.5s of ctx time.
|
|
230
|
-
let ctxStart = this.ctxTimeAtAnchor + (this.mediaTimeOfNext - this.mediaTimeOfAnchor) / this._rate;
|
|
231
|
-
|
|
232
|
-
// When the decoder is slower than realtime, `ctxStart` falls into
|
|
233
|
-
// the past (ctx.currentTime has already passed it). Clamping each
|
|
234
|
-
// sample to `ctx.currentTime` individually (the old behavior)
|
|
235
|
-
// caused every stale sample in a burst to start at *the same
|
|
236
|
-
// instant*, stacking them on top of each other — the audible
|
|
237
|
-
// symptom was a series of clicks / a chord of stuttering cook
|
|
238
|
-
// packets.
|
|
239
|
-
//
|
|
240
|
-
// Correct behavior: when the first sample of a burst is behind,
|
|
241
|
-
// *rebase the anchor forward* so ctxStart = ctx.currentTime now.
|
|
242
|
-
// Subsequent samples in the same burst then schedule at
|
|
243
|
-
// ctxStart + offset as usual, laying out sequentially on the
|
|
244
|
-
// timeline instead of piling up. The downside is a visible jump
|
|
245
|
-
// in the audio clock — but the alternative was silent corruption.
|
|
246
|
-
// `now()` readers (the video renderer) just see the clock step
|
|
247
|
-
// forward and drop any frames older than the new time.
|
|
248
|
-
if (ctxStart < this.ctx.currentTime) {
|
|
249
|
-
this.ctxTimeAtAnchor = this.ctx.currentTime;
|
|
250
|
-
this.mediaTimeOfAnchor = this.mediaTimeOfNext;
|
|
251
|
-
ctxStart = this.ctx.currentTime;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
372
|
node.start(ctxStart);
|
|
255
|
-
|
|
256
|
-
this.mediaTimeOfNext += frameCount / sampleRate;
|
|
257
373
|
this.framesScheduled++;
|
|
258
374
|
}
|
|
259
375
|
|
|
@@ -286,6 +402,10 @@ export class AudioOutput implements ClockSource {
|
|
|
286
402
|
try { this.gain.connect(this.ctx.destination); } catch { /* ignore */ }
|
|
287
403
|
|
|
288
404
|
if (this.state === "paused") {
|
|
405
|
+
if (isDebug()) {
|
|
406
|
+
// eslint-disable-next-line no-console
|
|
407
|
+
console.log(`[TRACE-AUD] START(resume) anchor=${this.mediaTimeOfAnchor.toFixed(3)} ctxAnchor=${this.ctxTimeAtAnchor.toFixed(4)} → ctxAnchor=${this.ctx.currentTime.toFixed(4)} ctxNow=${this.ctx.currentTime.toFixed(4)} pendingCount=${this.pendingQueue.length}`);
|
|
408
|
+
}
|
|
289
409
|
// Resume: media time should continue from where we paused. ctx.currentTime
|
|
290
410
|
// is preserved across suspend/resume, so re-anchoring it to "now" with
|
|
291
411
|
// the same mediaTimeOfAnchor gives a continuous clock.
|
|
@@ -295,7 +415,7 @@ export class AudioOutput implements ClockSource {
|
|
|
295
415
|
const drain = this.pendingQueue;
|
|
296
416
|
this.pendingQueue = [];
|
|
297
417
|
for (const c of drain) {
|
|
298
|
-
this.scheduleNow(c.samples, c.channels, c.sampleRate, c.frameCount);
|
|
418
|
+
this.scheduleNow(c.samples, c.channels, c.sampleRate, c.frameCount, c.ptsSec);
|
|
299
419
|
}
|
|
300
420
|
return;
|
|
301
421
|
}
|
|
@@ -307,11 +427,15 @@ export class AudioOutput implements ClockSource {
|
|
|
307
427
|
this.ctxTimeAtAnchor = this.ctx.currentTime + STARTUP_DELAY;
|
|
308
428
|
this.mediaTimeOfNext = this.mediaTimeOfAnchor;
|
|
309
429
|
this.state = "playing";
|
|
430
|
+
if (isDebug()) {
|
|
431
|
+
// eslint-disable-next-line no-console
|
|
432
|
+
console.log(`[TRACE-AUD] START(cold) anchor=${this.mediaTimeOfAnchor.toFixed(3)} ctxAnchor=${this.ctxTimeAtAnchor.toFixed(4)} mtNext=${this.mediaTimeOfNext.toFixed(3)} ctxNow=${this.ctx.currentTime.toFixed(4)} pendingCount=${this.pendingQueue.length}`);
|
|
433
|
+
}
|
|
310
434
|
|
|
311
435
|
const drain = this.pendingQueue;
|
|
312
436
|
this.pendingQueue = [];
|
|
313
437
|
for (const c of drain) {
|
|
314
|
-
this.scheduleNow(c.samples, c.channels, c.sampleRate, c.frameCount);
|
|
438
|
+
this.scheduleNow(c.samples, c.channels, c.sampleRate, c.frameCount, c.ptsSec);
|
|
315
439
|
}
|
|
316
440
|
}
|
|
317
441
|
|
|
@@ -341,6 +465,10 @@ export class AudioOutput implements ClockSource {
|
|
|
341
465
|
* supplying new samples) and then call `start()` to resume playback.
|
|
342
466
|
*/
|
|
343
467
|
async reset(newMediaTime: number): Promise<void> {
|
|
468
|
+
if (isDebug()) {
|
|
469
|
+
// eslint-disable-next-line no-console
|
|
470
|
+
console.log(`[TRACE-AUD] RESET to=${newMediaTime.toFixed(3)} prev_anchor=${this.mediaTimeOfAnchor.toFixed(3)} prev_mtNext=${this.mediaTimeOfNext.toFixed(3)} prev_ctxAnchor=${this.ctxTimeAtAnchor.toFixed(4)} ctxNow=${this.ctx.currentTime.toFixed(4)} state=${this.state}`);
|
|
471
|
+
}
|
|
344
472
|
if (this.noAudio) {
|
|
345
473
|
this.pendingQueue = [];
|
|
346
474
|
this.mediaTimeOfAnchor = newMediaTime;
|
|
@@ -358,6 +486,7 @@ export class AudioOutput implements ClockSource {
|
|
|
358
486
|
this.mediaTimeOfAnchor = newMediaTime;
|
|
359
487
|
this.mediaTimeOfNext = newMediaTime;
|
|
360
488
|
this.ctxTimeAtAnchor = this.ctx.currentTime;
|
|
489
|
+
this.firstAudibleCtxStart = -1;
|
|
361
490
|
this.state = "idle";
|
|
362
491
|
|
|
363
492
|
if (this.ctx.state === "running") {
|