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)