mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-27 02:43:48 +02:00
Modify audio encoder to fill gaps between timestamps with silence to ensure AV sync (fixes #176)
This commit is contained in:
+4
-1
@@ -57,6 +57,7 @@
|
|||||||
output,
|
output,
|
||||||
audio: (_, n) => ({
|
audio: (_, n) => ({
|
||||||
discard: n > 1,
|
discard: n > 1,
|
||||||
|
codec: 'aac',
|
||||||
//codec: 'opus',
|
//codec: 'opus',
|
||||||
/*
|
/*
|
||||||
process: (sample) => {
|
process: (sample) => {
|
||||||
@@ -72,7 +73,7 @@
|
|||||||
//numberOfChannels: 1,
|
//numberOfChannels: 1,
|
||||||
//sampleRate: 4000
|
//sampleRate: 4000
|
||||||
//discard: true
|
//discard: true
|
||||||
//forceTranscode: true,
|
forceTranscode: true,
|
||||||
}),
|
}),
|
||||||
/*
|
/*
|
||||||
video: {
|
video: {
|
||||||
@@ -100,6 +101,7 @@
|
|||||||
*/
|
*/
|
||||||
video: () => ({
|
video: () => ({
|
||||||
//discard: true,
|
//discard: true,
|
||||||
|
/*
|
||||||
process: (sample) => {
|
process: (sample) => {
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
// Create a canvas for image compositing
|
// Create a canvas for image compositing
|
||||||
@@ -118,6 +120,7 @@
|
|||||||
|
|
||||||
return ctx.canvas;
|
return ctx.canvas;
|
||||||
},
|
},
|
||||||
|
*/
|
||||||
//width: 300,
|
//width: 300,
|
||||||
//alpha: 'keep',
|
//alpha: 'keep',
|
||||||
//width: 320,
|
//width: 320,
|
||||||
|
|||||||
+16
-1
@@ -14,7 +14,21 @@
|
|||||||
source: new Mediabunny.BlobSource(file),
|
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);
|
const sink = new Mediabunny.EncodedPacketSink(videoTrack);
|
||||||
|
|
||||||
for await (const packet of sink.packets()) {
|
for await (const packet of sink.packets()) {
|
||||||
@@ -22,6 +36,7 @@
|
|||||||
|
|
||||||
if (packet.timestamp > 10) break;
|
if (packet.timestamp > 10) break;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
const sink = new Mediabunny.VideoSampleSink(videoTrack);
|
const sink = new Mediabunny.VideoSampleSink(videoTrack);
|
||||||
|
|||||||
@@ -1294,6 +1294,8 @@ class AudioEncoderWrapper {
|
|||||||
private customEncoderCallSerializer = new CallSerializer();
|
private customEncoderCallSerializer = new CallSerializer();
|
||||||
private customEncoderQueueSize = 0;
|
private customEncoderQueueSize = 0;
|
||||||
|
|
||||||
|
private lastEndSampleIndex: number | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encoders typically throw their errors "out of band", meaning asynchronously in some other execution context.
|
* 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.
|
* 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);
|
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) {
|
if (this.customEncoder) {
|
||||||
this.customEncoderQueueSize++;
|
this.customEncoderQueueSize++;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user