Golang和ffmpeg实时流输入/输出

4

我刚接触Go语言!

我正在进行一个简单的测试,读取来自ffmpeg的输出并写入文件。

我知道我可以用不同的方式来完成这个任务,但这只是项目的开始。我想以后操作读取的字节,对它们进行更改,然后将它们发送到另一个输出为UDP的ffmpeg进程中。输入也是UDP,也就是说,我会处理ffmpeg的输出,将其字节按照我的意愿进行处理,然后将这些字节作为输入传递给另一个输出为UDP的ffmpeg进程。

在这个简单的测试中,生成的文件无法在VLC中播放。我相信我已经正确地将字节写入输出文件,但是输出文件始终比输入文件少1MB。

我希望能够得到一些帮助,阐明我正在做的测试的最佳方法,以便我能够摆脱困境。我不知道是否完全有错,但我有这种感觉。

输入文件是4K H264视频,我认为输出应该是相同的,因为在这个简单的测试中,我仅仅是读取了命令行中的输出并将其写入文件中。

以下是代码:

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()
}
1个回答

5

您必须检查stdout.Read的返回值。请注意,读取的字节数(nr)可能小于缓冲区大小,因此您需要重新切片缓冲区以获取有效内容。将读取循环修改如下:

chunk := make([]byte, 40*1024)
for {
    nr, err5 := stdout.Read(chunk)
    fmt.Printf("Read %d bytes\n", 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\n", nw)
        verificaErro(err6)
    }

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

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

另一个解决方案是利用 io.Copy 将整个输出复制到 Golang 缓冲区中。代码片段如下所示:
var buf bytes.Buffer

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

err = cmd.Wait()
fmt.Printf("Wait error %v\n", 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\n", nw)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接