#How to open child process and pipe output into buffer?

1 messages · Page 1 of 1 (latest)

fickle vigil
#

I am trying to read a video stream through a pipe from ffmpeg running in a child process. My program currently looks like this:

const std = @import("std");

const INPUT_WIDTH = 786;
const INPUT_HEIGHT = 588;

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    var allocator = gpa.allocator();

    const argv = [_][]const u8{ "ffmpeg", "ffmpeg -i teapot.mp4 -f image2pipe -vcodec rawvideo -pix_fmt rgb24 -" };

    var process = std.process.Child.init(&argv, allocator);
    process.stdout_behavior = .Pipe;
    try process.spawn();

    var buffer: [INPUT_WIDTH * INPUT_HEIGHT * 3]u8 = undefined;
    var n_bytes: usize = undefined;

    while (true) {
        n_bytes = try process.stdout.?.read(&buffer);

        // TODO: Process video input

        if (n_bytes == 0) break;
    }
}

However when running the program, I get the following error:

Unable to find a suitable output format for ' -i in.mov -f rawvideo - '-i in.mov -f rawvideo - : Invalid argument

Now I am aware that this is an error coming from ffmpeg and not something Zig-specific. BUT: I've tried running this very similar C program which uses popen() to read from ffmpeg running in a child process and it works absolutely fine:

https://batchloaf.wordpress.com/2017/02/12/a-simple-way-to-read-and-write-audio-and-video-files-in-c-using-ffmpeg-part-2-video/

Now I am wondering:

  • Is spawning a child process and reading from its stdout even the correct way to pipe the output of a child process into a Zig program?

  • Is there maybe a different way to do what popen() does in the C example linked above?

short gazelle
terse finch
#

The reason you're getting the "invalid argument" error is because the argv you pass to std.process.Child.init are not expanded as they would be in a shell: the arguments must be passed individually, as in

const argv = [_][]const u8{ "ffmpeg", "-i", "teapot.mp4", "-f", "image2pipe", "-vcodec", "rawvideo", "-pix_fmt", "rgb24", "-" };

popen, on the other hand, passes its argument to /bin/sh -c, which is why it takes a single string with the command instead of multiple strings

#

If you really do want to mimic the behavior of popen, then you can do the same thing it's doing under the hood by calling /bin/sh yourself:

const argv = [_][]const u8{ "/bin/sh", "-c", "ffmpeg -i teapot.mp4 -f image2pipe -vcodec rawvideo -pix_fmt rgb24 -" };