Golang和ffmpeg实时流输入/输出

I'm new to Go!

I'm doing a simple test that is reading the output from ffmpeg and writing to a file.

I know I can do it in a different way, simply convert, but this is the beginning of a project where I want to later manipulate the bytes read, changing them and then sending them to the output. And the input will be UDP and output will be UDP too, that is, I will get the ffmpeg output I will treat the bytes as I wish to do and then I will throw these bytes as input into another ffmpeg process which output is UDP as well.

With this simple test the result of the file does not run in VLC, I believe I'm writing the bytes in the output file correctly, but the output file always has 1MB less than the input file.

I would like some help to elucidate what would be the best way to write this test that I am doing, based on that I can get out of the place. I do not know if it's exactly wrong, but I have the impression that it is.

The input file is a video in 4K, h264, I believe the output should be the same, because in this simple test I am simply reading what goes out in the cmd writing in the file.

Follow the code for analysis:

package main

import (
    "os/exec"
    "os"
)

func verificaErro(e error) {
    if e != nil {
        panic(e)
    }
}

func main() {
    dir, _ := os.Getwd()

    cmdName := "ffmpeg"
    args := []string{
        "-hide_banner",
        "-re",
        "-i",
        dir + "\\teste-4k.mp4",
        "-preset",
        "superfast",
        "-c:v",
        "h264",
        "-crf",
        "0",
        "-c",
        "copy",
        "-f", "rawvideo", "-",
    }
    cmd := exec.Command(cmdName, args...)

    stdout, err := cmd.StdoutPipe()
    verificaErro(err)
    err2 := cmd.Start()
    verificaErro(err2)

    fileOutput := dir + "/out.raw"
    var _, err3 = os.Stat(fileOutput)

    if os.IsNotExist(err3) {
        var file, err = os.Create(fileOutput)
        verificaErro(err)
        defer file.Close()
    }

    f, err4 := os.OpenFile(dir+"/out.raw", os.O_RDWR|os.O_APPEND, 0666)

    verificaErro(err4)

    bytes := make([]byte, 1024)
    for {
        _, err5 := stdout.Read(bytes)
        if err5 != nil {
            continue
        }
        if len(bytes) > 0 {
            _, err6 := f.Write(bytes)
            verificaErro(err6)
        } else {
            break
        }
    }

    f.Close()
}

You must check return values of stdout.Read. Please note that the number of bytes read (nr) may be smaller than the buffer size, so you need to re-slice the buffer to get a valid content. Modify reading loop as follows:

chunk := make([]byte, 40*1024)
for {
    nr, err5 := stdout.Read(chunk)
    fmt.Printf("Read %d bytes
", nr)

    //do something with the data
    //e.g. write to file
    if nr > 0 {
        validData := chunk[:nr]
        nw, err6 := f.Write(validData)
        fmt.Printf("Write %d bytes
", nw)
        verificaErro(err6)
    }

    if err5 != nil {
        //Reach end of file (stream), exit from loop
        if err5 == io.EOF {
            break
        }
        fmt.Printf("Error = %v
", err5)
        continue
    }
}

if err := cmd.Wait(); err != nil {
    fmt.Printf("Wait command error: %v
", err)
}

Another solution is utilizing io.Copy to copy the whole output into golang buffer. The code snippet will look like:

var buf bytes.Buffer

n, err := io.Copy(&buf, stdout)
verificaErro(err)
fmt.Printf("Copied %d bytes
", n)

err = cmd.Wait()
fmt.Printf("Wait error %v
", err)

//do something with the data
data := buf.Bytes()
f, err4 := os.OpenFile(dir+"/out.raw", os.O_RDWR|os.O_APPEND, 0666)
verificaErro(err4)
defer f.Close()
nw, err := f.Write(data)
f.Sync()
fmt.Printf("Write size %d bytes
", nw)