dongwo1914
dongwo1914
2017-04-25 04:49

Golang和ffmpeg实时流输入/输出

  • ffmpeg
  • udp
已采纳

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条回答

  • douyou7797 douyou7797 4年前

    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)
    
    点赞 评论 复制链接分享

为你推荐