2017-04-25 60 views
1

我是新來的Go!Golang和ffmpeg實時流式輸入/輸出

我正在做一個簡單的測試,它讀取ffmpeg的輸出並寫入文件。

我知道我可以用不同的方式完成它,只需轉換,但這是項目的開始,我希望稍後操作讀取的字節,更改它們,然後將它們發送到輸出。輸入將是UDP,並且輸出也將是UDP,也就是說,我將得到ffmpeg輸出,我將按照我希望的方式處理字節,然後將這些字節作爲輸入引入另一個輸出爲UDP的ffmpeg進程好。

通過這個簡單的測試,文件的結果不會在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() 
} 

回答

2

您必須檢查的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)