Golang和ffmpeg实时流输入/输出

huangapple go评论145阅读模式
英文:

Golang and ffmpeg realtime streaming input/output

问题

我是你的中文翻译助手,以下是你提供的代码的翻译:

我对Go语言还不熟悉!

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

我知道可以用不同的方式来做,只需简单地转换,但这是一个项目的开端,我想稍后操作读取的字节,对其进行更改,然后将其发送到输出。输入将是UDP,输出也将是UDP,也就是说,我将获取ffmpeg的输出,按照自己的意愿处理字节,然后将这些字节作为输入传递给另一个ffmpeg进程,其输出也是UDP。

通过这个简单的测试,文件的结果在VLC中无法运行,我相信我正确地将字节写入输出文件,但输出文件始终比输入文件少1MB。

我希望能得到一些帮助,阐明我正在进行的这个测试的最佳编写方式,基于此,我可以离开这个困境。我不知道是否完全错误,但我有这种印象。

输入文件是一段4K的视频,h264编码,我认为输出应该是相同的,因为在这个简单的测试中,我只是读取cmd中的输出并将其写入文件。

以下是代码供您分析:

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()
}

希望对你有帮助!如果你有任何其他问题,请随时提问。

英文:

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()
}

答案1

得分: 5

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

chunk := make([]byte, 40*1024)
for {
    nr, err5 := stdout.Read(chunk)
    fmt.Printf("读取 %d 字节\n", nr)

    //处理数据
    //例如写入文件
    if nr > 0 {
        validData := chunk[:nr]
        nw, err6 := f.Write(validData)
        fmt.Printf("写入 %d 字节\n", nw)
        verificaErro(err6)
    }

    if err5 != nil {
        //到达文件(流)末尾,退出循环
        if err5 == io.EOF {
            break
        }
        fmt.Printf("错误 = %v\n", err5)
        continue
    }
}

if err := cmd.Wait(); err != nil {
    fmt.Printf("等待命令错误:%v\n", err)
}

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

var buf bytes.Buffer

n, err := io.Copy(&buf, stdout)
verificaErro(err)
fmt.Printf("复制了 %d 字节\n", n)

err = cmd.Wait()
fmt.Printf("等待错误 %v\n", err)

//处理数据
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("写入大小 %d 字节\n", nw)
英文:

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\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)
}

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", 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)

huangapple
  • 本文由 发表于 2017年4月25日 12:49:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/43601846.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定