golang FFmpeg音视频处理库gmf的Go语言绑定插件使用
Golang FFmpeg音视频处理库gmf的Go语言绑定插件使用
Go FFmpeg绑定
gmf是一个Go语言的FFmpeg绑定库,可以用来处理音视频文件。
安装
先决条件
当前master分支支持从1.6开始的所有主要Go版本。
构建/安装FFmpeg
需要构建最新版本的FFmpeg,必须启用--enable-shared
选项:
./configure --prefix=/usr/local/ffmpeg --enable-shared
make
make install
添加pkgconfig路径:
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/ffmpeg/lib/pkgconfig/
确保PKG_CONFIG_PATH
包含FFmpeg的pkgconfig文件夹路径:
# 检查方法
pkg-config --libs libavformat
应该会打印出avformat库的有效路径。
现在只需运行:
go get github.com/3d0c/gmf
其他方法
这个包使用pkg-config方式来获取标志、包含和库路径,所以如果你已经安装了FFmpeg,只需确保你的安装包含它们(带有适当pc
文件的pkgconfig/文件夹)。
Docker容器
感谢@ergoz,你可以尝试使用docker容器riftbit/ffalpine。
感谢@denismakogon,还有一个值得提及的项目。
使用示例
下面是一个使用gmf库转码视频文件的完整示例:
package main
import (
"fmt"
"log"
"github.com/3d0c/gmf"
)
func main() {
// 输入文件路径
inputFile := "input.mp4"
// 输出文件路径
outputFile := "output.mp4"
// 创建输入上下文
inputCtx, err := gmf.NewInputCtx(inputFile)
if err != nil {
log.Fatal(err)
}
defer inputCtx.Close()
// 创建输出上下文
outputCtx, err := gmf.NewOutputCtx(outputFile)
if err != nil {
log.Fatal(err)
}
defer outputCtx.Close()
// 从输入文件获取流信息
streams, err := inputCtx.GetStreams()
if err != nil {
log.Fatal(err)
}
// 为每个流创建输出流
for _, stream := range streams {
// 复制流参数到输出流
outputStream, err := outputCtx.AddStreamWithCodeCtx(stream.CodecCtx())
if err != nil {
log.Fatal(err)
}
// 设置编码器参数
outputStream.CodecCtx().SetBitRate(stream.CodecCtx().BitRate())
outputStream.CodecCtx().SetWidth(stream.CodecCtx().Width())
outputStream.CodecCtx().SetHeight(stream.CodecCtx().Height())
outputStream.CodecCtx().SetTimeBase(stream.CodecCtx().TimeBase())
outputStream.CodecCtx().SetGopSize(stream.CodecCtx().GopSize())
outputStream.CodecCtx().SetPixFmt(stream.CodecCtx().PixFmt())
}
// 初始化输出上下文
if err := outputCtx.WriteHeader(); err != nil {
log.Fatal(err)
}
// 处理输入包的循环
for {
// 读取输入包
pkt, err := inputCtx.GetNextPacket()
if err != nil {
if err.Error() == "EOF" {
break
}
log.Fatal(err)
}
// 写入输出包
if err := outputCtx.WritePacket(pkt); err != nil {
log.Fatal(err)
}
// 释放包
pkt.Free()
}
// 写入尾部
if err := outputCtx.WriteTrailer(); err != nil {
log.Fatal(err)
}
fmt.Println("转码完成")
}
支持与贡献
如果有东西不能正常工作,直接修复它。不要犹豫提交Pull Request。
致谢
我借用了code.google.com/p/gmf上被放弃的项目的名称。原始代码可以在2013年4月3日的初始提交中找到。
更多关于golang FFmpeg音视频处理库gmf的Go语言绑定插件使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang FFmpeg音视频处理库gmf的Go语言绑定插件使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
使用gmf进行Go语言音视频处理
gmf是FFmpeg的Go语言绑定库,它提供了对FFmpeg功能的直接访问,可以用于音视频的编解码、转码、过滤等操作。下面我将介绍gmf的基本使用方法并提供示例代码。
安装gmf
首先需要安装gmf库:
go get -u github.com/3d0c/gmf
注意:使用前需要确保系统已安装FFmpeg开发库。
基本功能示例
1. 读取视频文件信息
package main
import (
"fmt"
"github.com/3d0c/gmf"
"log"
)
func main() {
// 打开输入文件
inputCtx, err := gmf.NewInputCtx("input.mp4")
if err != nil {
log.Fatal(err)
}
defer inputCtx.Free()
// 打印流信息
fmt.Printf("File contains %d streams\n", inputCtx.StreamsCnt())
for i := 0; i < inputCtx.StreamsCnt(); i++ {
stream := inputCtx.GetStream(i)
if stream == nil {
log.Fatal("Unable to get stream")
}
fmt.Printf("Stream %d: %s\n", i, stream.CodecCtx().Codec().LongName())
}
}
2. 视频转码示例
package main
import (
"log"
"github.com/3d0c/gmf"
)
func main() {
inputCtx, err := gmf.NewInputCtx("input.mp4")
if err != nil {
log.Fatal(err)
}
defer inputCtx.Free()
// 创建输出上下文
outputCtx, err := gmf.NewOutputCtx("output.mp4")
if err != nil {
log.Fatal(err)
}
defer outputCtx.Free()
// 查找视频流
videoStream, err := inputCtx.GetBestStream(gmf.AVMEDIA_TYPE_VIDEO)
if err != nil {
log.Fatal(err)
}
// 创建输出视频流
outputVideoStream := outputCtx.NewStream()
if outputVideoStream == nil {
log.Fatal("Unable to create stream in output context")
}
// 配置输出编解码器
codec, err := gmf.FindEncoder("libx264")
if err != nil {
log.Fatal(err)
}
outputVideoCodecCtx := gmf.NewCodecCtx(codec)
outputVideoCodecCtx.SetWidth(videoStream.CodecCtx().Width())
outputVideoCodecCtx.SetHeight(videoStream.CodecCtx().Height())
outputVideoCodeCtx.SetTimeBase(videoStream.TimeBase().AVR())
outputVideoCodecCtx.SetPixFmt(gmf.AV_PIX_FMT_YUV420P)
if err := outputVideoCodecCtx.Open(nil); err != nil {
log.Fatal(err)
}
outputVideoStream.SetCodecCtx(outputVideoCodecCtx)
// 写入文件头
if err := outputCtx.WriteHeader(); err != nil {
log.Fatal(err)
}
// 处理帧
for packet := range inputCtx.GetNewPackets() {
if packet.StreamIndex() != videoStream.Index() {
continue
}
frames, err := videoStream.CodecCtx().Decode(packet)
if err != nil {
log.Printf("Error decoding packet: %s", err)
continue
}
for _, frame := range frames {
// 转换帧格式
swsCtx := gmf.NewSwsCtx(
videoStream.CodecCtx(),
outputVideoCodecCtx,
gmf.SWS_BICUBIC,
)
defer swsCtx.Free()
dstFrame := gmf.NewFrame().
SetWidth(outputVideoCodecCtx.Width()).
SetHeight(outputVideoCodecCtx.Height()).
SetFormat(outputVideoCodecCtx.PixFmt())
defer dstFrame.Free()
if err := swsCtx.Scale(frame, dstFrame); err != nil {
log.Printf("Error scaling frame: %s", err)
continue
}
// 编码帧
pkts, err := outputVideoCodecCtx.Encode(dstFrame, -1)
if err != nil {
log.Printf("Error encoding frame: %s", err)
continue
}
// 写入输出文件
for _, pkt := range pkts {
pkt.SetStreamIndex(outputVideoStream.Index())
pkt.SetPts(outputVideoStream.Pts())
pkt.SetDts(outputVideoStream.Dts())
if err := outputCtx.WritePacket(pkt); err != nil {
log.Printf("Error writing packet: %s", err)
}
outputVideoStream.IncrPts()
outputVideoStream.IncrDts()
pkt.Free()
}
}
frame.Free()
}
// 写入文件尾
if err := outputCtx.WriteTrailer(); err != nil {
log.Fatal(err)
}
}
3. 提取音频
package main
import (
"log"
"github.com/3d0c/gmf"
)
func extractAudio(inputFile, outputFile string) error {
inputCtx, err := gmf.NewInputCtx(inputFile)
if err != nil {
return err
}
defer inputCtx.Free()
outputCtx, err := gmf.NewOutputCtx(outputFile)
if err != nil {
return err
}
defer outputCtx.Free()
audioStream, err := inputCtx.GetBestStream(gmf.AVMEDIA_TYPE_AUDIO)
if err != nil {
return err
}
outputStream := outputCtx.NewStream()
if outputStream == nil {
return err
}
codec, err := gmf.FindEncoder("aac")
if err != nil {
return err
}
outputCodecCtx := gmf.NewCodecCtx(codec)
outputCodecCtx.SetSampleRate(audioStream.CodecCtx().SampleRate())
outputCodecCtx.SetBitRate(audioStream.CodecCtx().BitRate())
outputCodecCtx.SetChannels(audioStream.CodecCtx().Channels())
outputCodecCtx.SetSampleFmt(audioStream.CodecCtx().SampleFmt())
outputCodecCtx.SetTimeBase(audioStream.TimeBase().AVR())
if err := outputCodecCtx.Open(nil); err != nil {
return err
}
outputStream.SetCodecCtx(outputCodecCtx)
if err := outputCtx.WriteHeader(); err != nil {
return err
}
for packet := range inputCtx.GetNewPackets() {
if packet.StreamIndex() != audioStream.Index() {
packet.Free()
continue
}
frames, err := audioStream.CodecCtx().Decode(packet)
if err != nil {
log.Printf("Error decoding packet: %s", err)
continue
}
for _, frame := range frames {
pkts, err := outputCodecCtx.Encode(frame, -1)
if err != nil {
log.Printf("Error encoding frame: %s", err)
continue
}
for _, pkt := range pkts {
pkt.SetStreamIndex(outputStream.Index())
pkt.SetPts(outputStream.Pts())
pkt.SetDts(outputStream.Dts())
if err := outputCtx.WritePacket(pkt); err != nil {
log.Printf("Error writing packet: %s", err)
}
outputStream.IncrPts()
outputStream.IncrDts()
pkt.Free()
}
}
frame.Free()
}
return outputCtx.WriteTrailer()
}
func main() {
if err := extractAudio("input.mp4", "output.aac"); err != nil {
log.Fatal(err)
}
}
注意事项
- gmf是FFmpeg的低级绑定,使用时需要手动管理内存,注意及时调用Free()方法释放资源
- 错误处理非常重要,FFmpeg操作可能会因各种原因失败
- 性能优化需要考虑缓冲区管理、并行处理等
- 不同版本的FFmpeg可能有API差异
gmf提供了对FFmpeg功能的直接访问,虽然使用起来比高级封装库复杂,但可以更灵活地控制音视频处理流程。对于更简单的需求,可以考虑使用goav或ffmpeg-go等更高级的封装库。