-
-
Notifications
You must be signed in to change notification settings - Fork 10
Description
Describe the Bug
Greetings!
See this exact example from the documentation. It demonstrates how to open muxer for writing. It also shows an example with custom callbacks.
// Custom I/O callbacks
const callbacks = {
write: async (buffer: Buffer) => {
// Write to custom destination
return buffer.length;
},
seek: async (offset: bigint, whence: AVSeekWhence) => {
// Seek in custom destination
return offset;
}
};
await using output = await Muxer.open(callbacks, {
format: 'mp4',
bufferSize: 8192
});However, if you copy-paste the example, you will get type errors. That's because the IOOutputCallbacks interface does not allow a Promise as a return value from it's member methods - write, seek or read.
Lines 757 to 786 in ffff4a7
| export interface IOOutputCallbacks { | |
| /** | |
| * Write callback - called when FFmpeg needs to write data. | |
| * | |
| * @param buffer - Buffer containing data to write | |
| * | |
| * @returns Number of bytes written or void | |
| */ | |
| write: (buffer: Buffer) => number | void; | |
| /** | |
| * Seek callback - called when FFmpeg needs to seek in the output. | |
| * | |
| * @param offset - Offset to seek to | |
| * | |
| * @param whence - Seek origin (AVSEEK_SET, AVSEEK_CUR, AVSEEK_END) | |
| * | |
| * @returns New position or negative error code | |
| */ | |
| seek?: (offset: bigint, whence: AVSeekWhence) => bigint | number; | |
| /** | |
| * Read callback - some formats may need to read back data. | |
| * | |
| * @param size - Number of bytes to read | |
| * | |
| * @returns Buffer with data, null for EOF, or negative error code | |
| */ | |
| read?: (size: number) => Buffer | null | number; | |
| } |
To Reproduce
See example above.
Expected Behavior
I think async methods should be allowed and not throw any type errors? Unless there is some runtime limitations beyond my knowledge.
Code Sample
To give a real life example, I am currently trying to use Hono to stream a video file to the frontend. The problem is that streamWriter.write is an asynchronous method, but I can't use it inside of the callbacks because they have to be synchronous. So a bit of a catch 22 here.
import { Hono } from 'hono';
import { stream } from 'hono/streaming';
import { Demuxer, Muxer } from 'node-av';
const app = new Hono();
app.get('/stream', (c) => {
try {
return stream(c, async (streamWriter) => {
c.header('Content-Type', 'video/mp4');
c.header('Cache-Control', 'no-cache');
await using input = await Demuxer.open('some-file.mp4');
const videoStream = input.video();
const audioStream = input.audio();
if (!videoStream || !audioStream) {
await streamWriter.write('No video stream found');
return;
}
await using output = await Muxer.open(
{
// ❌ Can't use async here
write: async (buffer: Buffer) => {
await streamWriter.write(buffer);
return buffer.length;
}
},
{
format: 'mp4',
options: {
movflags: 'frag_keyframe+empty_moov+default_base_moof',
frag_duration: '5000000', // 5 second fragments
min_frag_duration: '2000000', // Minimum 2 seconds
}
}
);
// Add video and audio streams (copy codec, no transcoding)
const videoOutputIndex = output.addStream(videoStream);
const audioOutputIndex = output.addStream(audioStream);
try {
// Stream packets directly without decoding/encoding
for await (const packet of input.packets()) {
if (!packet) continue;
if (packet.streamIndex === videoStream.index) {
await output.writePacket(packet, videoOutputIndex);
}
else if (packet.streamIndex === audioStream.index) {
await output.writePacket(packet, audioOutputIndex);
}
packet.free();
}
}
catch (error) {
console.error('Stream error:', error);
}
});
}
catch (error) {
console.error('Failed to open stream:', error);
return c.text('Failed to stream', 500);
}
});Relevant Log Output / Error Messages
No overload matches this call.
Overload 1 of 2, '(target: string, options?: MuxerOptions | undefined): Promise<Muxer>', gave the following error.
Argument of type '{ write: (buffer: Buffer) => Promise<number>; seek: (offset: bigint, whence: AVSeekWhence) => Promise<bigint>; }' is not assignable to parameter of type 'string'.
Overload 2 of 2, '(target: IOOutputCallbacks, options: MuxerOptions & { format: string; }): Promise<Muxer>', gave the following error.
Argument of type '{ write: (buffer: Buffer) => Promise<number>; seek: (offset: bigint, whence: AVSeekWhence) => Promise<bigint>; }' is not assignable to parameter of type 'IOOutputCallbacks'.
The types returned by 'write(...)' are incompatible between these types.
Type 'Promise<number>' is not assignable to type 'number | void'.ts(2769)Node.js Version
25.2.1
NodeAV Version
5.0.3
Operating System
Linux
Installation Method
pnpm add node-av
API Used
Low-Level API
Hardware Acceleration
None
Additional Context
No response