Modify audio encoder to fill gaps between timestamps with silence to ensure AV sync (fixes #176)

This commit is contained in:
Vanilagy
2025-11-11 18:03:28 +01:00
parent 9d477dfb14
commit 23f814679e
3 changed files with 51 additions and 2 deletions
+4 -1
View File
@@ -57,6 +57,7 @@
output,
audio: (_, n) => ({
discard: n > 1,
codec: 'aac',
//codec: 'opus',
/*
process: (sample) => {
@@ -72,7 +73,7 @@
//numberOfChannels: 1,
//sampleRate: 4000
//discard: true
//forceTranscode: true,
forceTranscode: true,
}),
/*
video: {
@@ -100,6 +101,7 @@
*/
video: () => ({
//discard: true,
/*
process: (sample) => {
if (!ctx) {
// Create a canvas for image compositing
@@ -118,6 +120,7 @@
return ctx.canvas;
},
*/
//width: 300,
//alpha: 'keep',
//width: 320,
+16 -1
View File
@@ -14,7 +14,21 @@
source: new Mediabunny.BlobSource(file),
});
const videoTrack = await input.getPrimaryAudioTrack();
const audioTrack = await input.getPrimaryAudioTrack();
const sink = new Mediabunny.AudioSampleSink(audioTrack);
let lastEnd = 0;
for await (const sample of sink.samples()) {
if (sample.timestamp - lastEnd > 0) {
console.warn(sample.timestamp - lastEnd)
console.log(sample.timestamp, sample.duration, "diffie", sample.timestamp - lastEnd);
}
lastEnd = sample.timestamp + sample.duration;
sample.close();
}
/*
const sink = new Mediabunny.EncodedPacketSink(videoTrack);
for await (const packet of sink.packets()) {
@@ -22,6 +36,7 @@
if (packet.timestamp > 10) break;
}
*/
/*
const sink = new Mediabunny.VideoSampleSink(videoTrack);
+31
View File
@@ -1294,6 +1294,8 @@ class AudioEncoderWrapper {
private customEncoderCallSerializer = new CallSerializer();
private customEncoderQueueSize = 0;
private lastEndSampleIndex: number | null = null;
/**
* Encoders typically throw their errors "out of band", meaning asynchronously in some other execution context.
* However, we want to surface these errors to the user within the normal control flow, so they don't go uncaught.
@@ -1342,6 +1344,35 @@ class AudioEncoderWrapper {
}
assert(this.encoderInitialized);
// Handle padding of gaps with silence to avoid audio drift over time, like in
// https://github.com/Vanilagy/mediabunny/issues/176
// TODO An open question is how encoders deal with the first AudioData having a non-zero timestamp, and with
// AudioDatas that have an overlapping timestamp range.
{
const startSampleIndex = Math.round(
audioSample.timestamp * audioSample.sampleRate,
);
const endSampleIndex = Math.round(
(audioSample.timestamp + audioSample.duration) * audioSample.sampleRate,
);
if (this.lastEndSampleIndex !== null && startSampleIndex > this.lastEndSampleIndex) {
const sampleCount = startSampleIndex - this.lastEndSampleIndex;
const fillSample = new AudioSample({
data: new Float32Array(sampleCount * audioSample.numberOfChannels),
format: 'f32-planar',
sampleRate: audioSample.sampleRate,
numberOfChannels: audioSample.numberOfChannels,
numberOfFrames: sampleCount,
timestamp: this.lastEndSampleIndex / audioSample.sampleRate,
});
await this.add(fillSample, true); // Recursive call
}
this.lastEndSampleIndex = endSampleIndex;
}
if (this.customEncoder) {
this.customEncoderQueueSize++;