本文經過ffmpeg編程的例子來說述如何封裝cgo庫
更多內容訪問 個人博客
繼上一篇 ffmpeg音視頻C編程入門, 使用高性能的C語言進行音視頻的處理,比較執行效率比較高,可是業務需求,快捷開發須要使用更方便的語言,好比 golang,本文介紹如何將 將視頻轉成GIF 的C語言方法封裝成 golang 方法以便調用。(不明白的同窗請點擊上面連接多瞭解)html
最簡單的 cgo 封裝例子看這篇 cgo快速入門linux
我這裏講幾個注意事項git
CGO構建程序會自動構建當前目錄下的C源文件,便是 go 會將當前目錄下 .c 文件都編譯成 .o目標文件,再連接彙編,這個特色衍生出幾個注意事項:github
extern C
第一步,處理例子中已經寫好的 gen_gif 方法,修改 gen_gif.h 文件golang
#ifdef __cplusplus extern "C" { #endif int gen_gif(const int gifSeconds, const int rotate, void* data, int data_size, void* outBuf, int outBufLen, int *outSize); #ifdef __cplusplus } #endif
在處理 ffmpeg 的 av_log 日誌的回調方法編程
/* log.c */ /* mpeg 的日誌庫用法 #include <libavutil/log.h> // 設置日誌級別 av_log_set_level(AV_LOG_DEBUG) // 打印日誌 av_log(NULL, AV_LOG_INFO,"...%s\n",op) */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <libavutil/log.h> // 定義輸出日誌的函數,留白給使用者實現 extern void Ffmpeglog(int , char*); static void log_callback(void *avcl, int level, const char *fmt, va_list vl) { (void) avcl; char log[1024] = {0}; // int vsnprintf(buffer,bufsize ,fmt, argptr) , va_list 是可變參數的指針,相關方法有: va_start(), type va_atg(va_list, type), va_end() int n = vsnprintf(log, 1024, fmt, vl); if (n > 0 && log[n - 1] == '\n') log[n - 1] = 0; if (strlen(log) == 0) return; Ffmpeglog(level, log); } void set_log_callback() { // 給 av 解碼器註冊日誌回調函數 av_log_set_callback(log_callback); }
第二步,編寫 go 文件函數
package c /* #include <stdlib.h> #include "gen_gif.h" #include "log.h" #cgo LDFLAGS: -lavcodec -lavformat -lswscale -lavutil -lavfilter -lm */ import "C" import ( "errors" "fmt" "unsafe" ) func init() { C.set_log_callback() } var logger func(s string) = nil func SetFfmpegLogger(f func(s string)) { logger = f } //export Ffmpeglog func Ffmpeglog(l C.int, t *C.char) { if l <= 32 { if logger == nil { fmt.Printf("ffmpeg log:%s\n", C.GoString(t)) } else { logger(fmt.Sprintf("ffmpeg log:%s\n", C.GoString(t))) } } } func GenGif(second, rotate int, input []byte) (err error, output []byte) { buf := make([]byte, 1<<20) var outsz C.int ret := C.gen_gif(C.int(second), C.int(rotate), unsafe.Pointer(&input[0]), C.int(len(input)), unsafe.Pointer(&buf[0]), C.int(len(buf)), &outsz) if ret != 0 { return errors.New(fmt.Sprintf("error, ret=%v", ret)), nil } output = make([]byte, outsz) copy(output, buf[:outsz]) return nil, output }
libav*
的函數庫,設置 #cgo LDFLAGS
的動態連接選項log.c
中提供使用者實現的函數,使用 go 語言的方法打印日誌import "C"
提供的方法轉化變量將上文編寫好的代碼,提交到代碼倉庫,或者在本地的 GOPATH 中建好相應的目錄,如筆者的 github.com/lightfish-zhang/mpegUtil/c
路徑。性能
接下來,能夠在業務須要的地方使用 GenGif()
了,讓咱們來測試一下測試
package main import ( "fmt" "io/ioutil" "os" mpegUtil "github.com/lightfish-zhang/mpegUtil/c" ) func main() { if len(os.Args) < 3 { fmt.Printf("Usage: %s <input file> <output file>\n", os.Args[0]) os.Exit(1) } inFile, err := os.Open(os.Args[1]) if err != nil { fmt.Printf("open file fail, path=%v, err=%v", os.Args[1], err) os.Exit(1) } outFile, err := os.Create(os.Args[2]) if err != nil { fmt.Printf("create file fail, path=%v, err=%v", os.Args[2], err) os.Exit(1) } input, err := ioutil.ReadAll(inFile) if err != nil { fmt.Printf("read file fail, err=%v", err) os.Exit(1) } err, output := mpegUtil.GenGif(5, 90, input) if err != nil { fmt.Printf("generate gif fail, err=%v", err) os.Exit(1) } _, err = outFile.Write(output) if err != nil { fmt.Printf("write file fail, err=%v", err) os.Exit(1) } }
我在本地執行,對某一個 mp4 文件進行轉碼 GIF,如指針
./genGif test.mp4 test.gif
運行成功,在個人電腦上能夠看到完美的 GIF 圖片!
golang 語言的優點是打包出一個靜態的執行文件,能夠在相同平臺下運行。可是,咱們例子是動態連接了 ffmpeg
的 libav*.so
,在編譯或者部署時,都須要在機器上安裝好 ffmpeg 庫。
有沒有便利的方法進行編譯或部署呢?
一個使用 LD_LIBRARY_PATH
指定動態連接目錄的小技巧,不過這個技巧須要 編譯機器與部署機器的運行環境差很少,除了 libav*.so 能夠沒有預先安裝在部署機器。爲了保持 libc.so
的函數可用,最好機器之間的 linux 版本同樣。
使用 ldd genGif
查看編譯出來的執行文件,須要動態連接哪些 *.so
文件,將關鍵的文件拷貝出來,好比 libav*
的 so 文件是必須的,其餘好比 libm.so
, libz.so
視部署機器有沒有而定。
部署時候,須要把以上找到的 *.so 一塊兒拷貝到目標機器上,同時在運行 golang 程序時,設置好全局變量 LD_LIBRARY_PATH
,指向 *.so
文件目錄。
這個方法也須要機器的 linux 最好保持一致,ffmpeg 依賴的 libc.so 的函數好像會隨着 linux 版本不一樣而有所差別。
準備好各類依賴庫的靜態庫,參照以下命令
g++ -o gen_gif -I./ -I./ffmpeg/include -I/usr/local/include main.o gen_gif.a ./ffmpeg/lib/libavdevice.a ./ffmpeg/lib/libavfilter.a ./ffmpeg/lib/libavformat.a ./ffmpeg/lib/libavcodec.a ./ffmpeg/lib/libavutil.a ./ffmpeg/lib/libswreample.a ./ffmpeg/lib/libswcale.a ./lib/liblzma.a ./lib/libm.a ./lib/libz.a ./lib/libbz2.a -lpthread
#cgo
命令/* #cgo CFLAGS: -I./ffmpeg/include -I/usr/local/include #cgo LDFLAGS: -L./lib -L./ffmpeg/lib */