golang实现高效FFmpeg C绑定功能的插件库go-astiav的使用
Golang实现高效FFmpeg C绑定功能的插件库go-astiav使用指南
介绍
go-astiav
是一个为FFmpeg提供C语言绑定的Golang库。它仅兼容FFmpeg n7.0版本。
它的主要目标是:
- 提供更符合Go语言习惯的API
- 标准错误模式
- 类型化的常量和标志
- 基于结构体的函数
- …
- 提供与FFmpeg示例对应的Go版本实现
- 经过全面测试
示例
示例位于项目的examples
目录中,尽可能与FFmpeg示例相对应。
功能名称 | go-astiav示例 | FFmpeg示例 |
---|---|---|
BitStream过滤 | 查看 | X |
自定义IO解复用 | 查看 | 查看 |
自定义IO复用 | 查看 | X |
解复用/解码 | 查看 | 查看 |
过滤 | 查看 | 查看 |
帧数据操作 | 查看 | X |
硬件解码/过滤 | 查看 | 查看 |
硬件编码 | 查看 | 查看 |
复用途 | 查看 | 查看 |
音频重采样 | 查看 | 查看 |
视频缩放 | 查看 | 查看 |
转码 | 查看 | 查看 |
提示:你可以使用testdata
目录中的视频样本进行测试
使用模式
注意:为便于阅读,下面的代码没有检查错误,但在实际使用中你应该检查!
首先,所有用例都不同,不可能提供适用于所有情况的模式。这就是为什么FFmpeg的文档或源代码应该是你关于如何使用这个库的最终真相来源。这就是为什么这个库的所有方法都记录了它们使用的C函数的文档链接。
何时调用Alloc()、.Unref()和.Free()
让我们以FormatContext.ReadFrame()
模式为例。帧的模式类似。
// 你可以分配packet一次并在下面的for循环中重复使用同一个对象
pkt := astiav.AllocPacket()
// 然而,一旦你完成使用packet,你需要确保释放它
defer pkt.Free()
// 循环
for {
// 我们将使用一个闭包来方便取消引用packet
func() {
// 每次使用同一个packet读取帧
formatContext.ReadFrame(pkt)
// 然而,一旦你完成使用.ReadFrame()方法"注入"的内容,
// 确保取消引用packet
defer pkt.Unref()
// 在这里你可以用你的packet做任何你想做的事情
}()
}
从源代码安装FFmpeg
如果你不知道如何安装ffmpeg
,你可以使用以下命令从源代码安装:
$ make install-ffmpeg
ffmpeg
将从源代码构建在你工作目录中名为tmp
的目录中。
为了让你的Go代码自动获取ffmpeg
依赖项,你需要添加以下环境变量:
(不要忘记用你工作目录的绝对路径替换{{ path to your working directory }}
)
export CGO_LDFLAGS="-L{{ path to your working directory }}/tmp/n7.0/lib/",
export CGO_CFLAGS="-I{{ path to your working directory }}/tmp/n7.0/include/",
export PKG_CONFIG_PATH="{{ path to your working directory }}/tmp/n7.0/lib/pkgconfig",
在Windows上构建
在Windows上构建需要msys2/mingw64 gcc工具链。阅读快速入门指南安装Msys2。
完成后,从安装文件夹运行Mingw64 shell,运行以下命令:
# 更新包
pacman -Syu
# 安装构建要求
pacman -S --noconfirm --needed git diffutils mingw-w64-x86_64-toolchain pkg-config make yasm
# 使用git克隆仓库
git clone https://github.com/asticode/go-astiav
cd go-astiav
然后克隆这个仓库后,按照上面的构建说明操作。
注意: 对于
pkg-config
,使用choco的pkgconfiglite
。 记得正确设置CGO
和PKG_CONFIG
环境变量,指向构建ffmpeg的文件夹。
为什么选择astiav?
更多关于golang实现高效FFmpeg C绑定功能的插件库go-astiav的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang实现高效FFmpeg C绑定功能的插件库go-astiav的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
go-astiav: 高效的FFmpeg Go绑定库
go-astiav是一个高效的Go语言FFmpeg绑定库,它提供了对FFmpeg功能的直接访问,同时保持了Go语言的简洁性和安全性。下面我将详细介绍如何使用go-astiav以及它的主要功能。
安装与配置
首先需要安装go-astiav和FFmpeg开发库:
go get github.com/asticode/go-astiav
在Linux上安装FFmpeg开发库:
# Ubuntu/Debian
sudo apt-get install libavformat-dev libavcodec-dev libavutil-dev libavdevice-dev libavfilter-dev libswscale-dev libswresample-dev
# CentOS/RHEL
sudo yum install ffmpeg-devel
基本使用示例
1. 打开媒体文件
package main
import (
"fmt"
"log"
"github.com/asticode/go-astiav"
)
func main() {
// 注册所有编解码器和格式
astiav.RegisterAll()
// 打开输入文件
input, err := astiav.OpenInput("input.mp4")
if err != nil {
log.Fatal(err)
}
defer input.Close()
// 打印文件信息
fmt.Printf("Input format: %s\n", input.Format().Name())
fmt.Printf("Duration: %v\n", input.Duration())
fmt.Printf("Bitrate: %d\n", input.BitRate())
}
2. 解码视频流
func decodeVideo() error {
// 打开输入文件
input, err := astiav.OpenInput("input.mp4")
if err != nil {
return err
}
defer input.Close()
// 查找视频流
stream := input.FindBestStream(astiav.MediaTypeVideo)
if stream == nil {
return fmt.Errorf("no video stream found")
}
// 创建解码器上下文
codec := astiav.FindDecoder(stream.CodecParameters().CodecID())
if codec == nil {
return fmt.Errorf("no codec found")
}
codecContext := astiav.AllocCodecContext(codec)
if err := stream.CodecParameters().ToCodecContext(codecContext); err != nil {
return err
}
defer codecContext.Free()
// 打开解码器
if err := codecContext.Open(codec); err != nil {
return err
}
// 创建数据包和帧
pkt := astiav.AllocPacket()
defer pkt.Free()
frame := astiav.AllocFrame()
defer frame.Free()
// 读取并解码帧
for {
if err := input.ReadFrame(pkt); err != nil {
if err == astiav.ErrEof {
break
}
return err
}
if pkt.StreamIndex() != stream.Index() {
continue
}
if err := codecContext.SendPacket(pkt); err != nil {
return err
}
for {
if err := codecContext.ReceiveFrame(frame); err != nil {
if err == astiav.ErrEof || err == astiav.ErrEagain {
break
}
return err
}
// 处理解码后的帧
fmt.Printf("Decoded frame: width=%d, height=%d, format=%d\n",
frame.Width(), frame.Height(), frame.Format())
}
}
return nil
}
3. 编码和保存视频
func encodeVideo() error {
// 创建输出格式上下文
output, err := astiav.AllocOutputFormatContext("output.mp4")
if err != nil {
return err
}
defer output.Free()
// 添加视频流
codec := astiav.FindEncoder(astiav.CodecIdH264)
if codec == nil {
return fmt.Errorf("codec not found")
}
stream := output.NewStream(codec)
if stream == nil {
return fmt.Errorf("failed to create stream")
}
// 配置编码器参数
codecContext := stream.CodecContext()
codecContext.SetWidth(640)
codecContext.SetHeight(480)
codecContext.SetPixelFormat(astiav.PixelFormatYuv420p)
codecContext.SetTimeBase(astiav.NewRational(1, 25))
codecContext.SetBitRate(400000)
// 打开编码器
if err := codecContext.Open(codec); err != nil {
return err
}
// 写入文件头
if err := output.WriteHeader(); err != nil {
return err
}
// 创建帧和包
frame := astiav.AllocFrame()
defer frame.Free()
frame.SetWidth(640)
frame.SetHeight(480)
frame.SetFormat(int(astiav.PixelFormatYuv420p))
if err := frame.AllocBuffer(32); err != nil {
return err
}
pkt := astiav.AllocPacket()
defer pkt.Free()
// 编码并写入帧
for i := 0; i < 100; i++ {
// 这里应该填充真实的帧数据
// 例如: frame.SetData(...)
if err := codecContext.SendFrame(frame); err != nil {
return err
}
for {
if err := codecContext.ReceivePacket(pkt); err != nil {
if err == astiav.ErrEof || err == astiav.ErrEagain {
break
}
return err
}
pkt.SetStreamIndex(stream.Index())
if err := output.WriteFrame(pkt); err != nil {
return err
}
}
}
// 写入文件尾
if err := output.WriteTrailer(); err != nil {
return err
}
return nil
}
高级功能
1. 硬件加速解码
func hwDecode() error {
// 查找硬件解码器
hwCodec := astiav.FindDecoderByName("h264_cuvid")
if hwCodec == nil {
return fmt.Errorf("hardware decoder not found")
}
// 创建硬件解码器上下文
hwCodecContext := astiav.AllocCodecContext(hwCodec)
defer hwCodecContext.Free()
// 配置硬件设备
hwDevice := astiav.AllocHWDeviceContext(astiav.HWDeviceTypeCuda)
if hwDevice == nil {
return fmt.Errorf("failed to create HW device")
}
defer hwDevice.Free()
// 关联硬件设备到解码器
if err := hwCodecContext.SetHWDevice(hwDevice); err != nil {
return err
}
// 打开解码器
if err := hwCodecContext.Open(hwCodec); err != nil {
return err
}
// 后续解码流程与软件解码类似...
return nil
}
2. 滤镜处理
func applyFilter() error {
// 创建滤镜图
filterGraph := astiav.AllocFilterGraph()
defer filterGraph.Free()
// 创建buffer源
bufferSrc := filterGraph.AllocFilter("buffer", "in")
bufferSrcArgs := fmt.Sprintf("video_size=%dx%d:pix_fmt=%d:time_base=%d/%d",
640, 480, astiav.PixelFormatYuv420p, 1, 25)
if err := bufferSrc.SetArgs(bufferSrcArgs); err != nil {
return err
}
// 创建buffer接收器
bufferSink := filterGraph.AllocFilter("buffersink", "out")
if err := filterGraph.CreateFilter(bufferSink); err != nil {
return err
}
// 创建滤镜
filters := "scale=320:240,transpose=1" // 缩放并旋转
if err := filterGraph.Parse(filters, bufferSrc, bufferSink); err != nil {
return err
}
// 初始化滤镜图
if err := filterGraph.Configure(); err != nil {
return err
}
// 处理帧
frame := astiav.AllocFrame()
defer frame.Free()
// 设置输入帧参数...
if err := bufferSrc.AddFrame(frame); err != nil {
return err
}
filteredFrame := astiav.AllocFrame()
defer filteredFrame.Free()
if err := bufferSink.GetFrame(filteredFrame); err != nil {
return err
}
// 使用处理后的帧...
return nil
}
性能优化建议
- 复用对象:尽可能复用Packet、Frame等对象,避免频繁分配和释放内存
- 批量处理:当处理多个文件时,保持编解码器上下文打开状态
- 硬件加速:在支持的环境中优先使用硬件编解码
- 并行处理:使用Go的goroutine处理多个流或文件
- 内存池:对于频繁创建的对象,考虑使用sync.Pool
总结
go-astiav提供了对FFmpeg功能的全面绑定,使Go开发者能够高效地处理多媒体数据。它的主要优势包括:
- 直接绑定FFmpeg C API,性能接近原生
- 完整的FFmpeg功能覆盖
- Go风格的内存管理和错误处理
- 良好的文档和示例
对于需要高性能多媒体处理的Go应用,go-astiav是一个值得考虑的选择。