Home > Net >  Writing to ffpmeg stdin freezes program
Writing to ffpmeg stdin freezes program

Time:12-26

I'm trying to convert a file in memory using ffmpeg to another format by using stdin and stdout, but everytime I try to write to stdin, of my ffmpeg command, it just freezes there.

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
    "os/exec"
)

func test(bytes []byte) ([]byte, error) {

    cmd := exec.Command(
        "ffmpeg",
        "-i", "pipe:0", // read from stdin
        "-vcodec", "copy",
        "-acodec", "copy",
        "-f", "matroska",
        "pipe:1",
    )

    in, err := cmd.StdinPipe()
    if err != nil {
        panic(err)
    }
    out, err := cmd.StdoutPipe()
    if err != nil {
        panic(err)
    }

    fmt.Println("starting")
    err = cmd.Start()
    if err != nil {
        panic(err)
    }

    fmt.Println("writing")
    w := bufio.NewWriter(in)
    _, err = w.Write(bytes)
    if err != nil {
        panic(err)
    }

    err = w.Flush()
    if err != nil {
        panic(err)
    }

    err = in.Close()
    if err != nil {
        panic(err)
    }

    fmt.Println("reading")
    outBytes, err := io.ReadAll(out)
    if err != nil {
        panic(err)
    }

    fmt.Println("waiting")
    err = cmd.Wait()
    if err != nil {
        panic(err)
    }

    return outBytes, nil
}

func main() {
    dat, err := os.ReadFile("speech.mp4")
    if err != nil {
        panic(err)
    }

    out, err := test(dat)
    if err != nil {
        panic(err)
    }

    err = os.WriteFile("test.m4v", out, 0644)
    if err != nil {
        panic(err)
    }
}


It prints

starting
writing

and gets stuck there. I tried similar code with grep, and the everything worked fine, so this seems to be some ffmpeg specific problem.

I tried running

cat speech.mp4 | ffmpeg -i pipe:0 -vcodec copy -acodec copy -f matroska pipe:1 | cat > test.mkv

and that works fine, so it's not an ffmpeg problem, but some problem with how I'm piping/reading/writing my data.

My speech.mp4 file is around 2MB.

CodePudding user response:

So the secret lied in reading stdout as you dumped the bytes into stdin, since writing to the pipe blocks. Thanks @JimB for helping me figure this out.

You just have to read as you write:

cmd := exec.Command(
    "ffmpeg",
    "-i", "pipe:0", // read from stdin
    "-vcodec", "copy",
    "-acodec", "copy",
    "-f", "matroska",
    "pipe:1",
)

out, err := cmd.StdoutPipe()
if err != nil {
    panic(err)
}

in, err := cmd.StdinPipe()
writer := bufio.NewWriter(in)
if err != nil {
    panic(err)
}

fmt.Println("starting")
err = cmd.Start()
if err != nil {
    panic(err)
}

go func() {
    defer writer.Flush()
    defer in.Close()
    fmt.Println("writing")
    _, err = writer.Write(bytes)
    if err != nil {
        panic(err)
    }
}()

var outBytes []byte

defer out.Close()
fmt.Println("reading")
outBytes, err = io.ReadAll(out)
if err != nil {
    panic(err)
}
fmt.Println("waiting")
err = cmd.Wait()
if err != nil {
    panic(err)
}

return outBytes, nil
  • Related