dr.benchi
dr.benchi16mo ago

Piping streams through a shell program (ffmpeg) and writing the output to a file

I've been trying to use ffmpeg on the backend of my super simple audio-uploading app. I can save the files as-uploaded, but after getting a Reader for the uploaded file I can't figure out how to pipe it into Deno.Command. The examples on the website don't quite capture my use case (the example shows piping output, but I can't get the magic combination to pipe in input that doesn't make it choke. I've got it working fine if I save the uploaded file to filesystem first and then pass its path to ffmpeg, but that's a hack, surely I can do this with just the streams? Part of this is just me trying to understand the difference between command.output() and command.spawn() and then accessing the stdi/o streams. Literally following the example from the web eg:
import { extname } from 'https://deno.land/std/path/mod.ts';
const input_file = `./sample_recording.wav`;
const ffmpeg_bin = `/usr/bin/ffmpeg`;
const output_file = './sample_output.ogg';

const input_data = await Deno.open(input_file, { read: true });

const command = new Deno.Command(ffmpeg_bin, {
args: [
'-i', '-',
],
stdin: 'piped',
stdout: 'piped',
});

const childProcess = command.spawn();

childProcess.stdout.pipeTo(
Deno.openSync(output_file, { write: true, create: true }).writable,
);

childProcess.stdin.close();

input_data.close();
import { extname } from 'https://deno.land/std/path/mod.ts';
const input_file = `./sample_recording.wav`;
const ffmpeg_bin = `/usr/bin/ffmpeg`;
const output_file = './sample_output.ogg';

const input_data = await Deno.open(input_file, { read: true });

const command = new Deno.Command(ffmpeg_bin, {
args: [
'-i', '-',
],
stdin: 'piped',
stdout: 'piped',
});

const childProcess = command.spawn();

childProcess.stdout.pipeTo(
Deno.openSync(output_file, { write: true, create: true }).writable,
);

childProcess.stdin.close();

input_data.close();
This results in pipe:: Invalid data found when processing input and creates an empty output file. However this file gets processed fine if passed as a file path... What am I missing/misunderstanding?
2 Replies
ioB
ioB16mo ago
Does this example help at all? https://examples.deno.land/subprocesses-spawn
Subprocesses: Spawning - Deno by Example
For more complex usecases, we don't simply want the output of some command. In this case, we can spawn a subprocess and interact with it. -- Deno by example is a collection of annotated examples for how to use Deno, and the various features it provides.
dr.benchi
dr.benchiOP16mo ago
Thanks, that's helped me get the stdin Writer, though I got a little stuck feeding into it. For anyone else who reads this my now-working code looks like this:
const input_file = `./sample_recording.wav`;
const ffmpeg_bin = `/usr/bin/ffmpeg`;
const output_file = './sample_output.ogg';

const input_data = await Deno.open(input_file, { read: true });

const command = new Deno.Command(ffmpeg_bin, {
args: [
'-i', '-',
output_file,
],
stdin: 'piped',
stdout: 'piped',
});

const child_process = await command.spawn();

const stdin = await child_process.stdin.getWriter();

for await (const chunk of input_data.readable) {
stdin.write(chunk);
}
stdin.releaseLock();
await child_process.stdin.close();
const input_file = `./sample_recording.wav`;
const ffmpeg_bin = `/usr/bin/ffmpeg`;
const output_file = './sample_output.ogg';

const input_data = await Deno.open(input_file, { read: true });

const command = new Deno.Command(ffmpeg_bin, {
args: [
'-i', '-',
output_file,
],
stdin: 'piped',
stdout: 'piped',
});

const child_process = await command.spawn();

const stdin = await child_process.stdin.getWriter();

for await (const chunk of input_data.readable) {
stdin.write(chunk);
}
stdin.releaseLock();
await child_process.stdin.close();