# goffmpeg Go bindings for FFmpeg using CGO. ## 项目简介 本项目提供 Go 语言对 FFmpeg 动态库的 CGO 绑定,以 lib 库形式开放接口供其他应用调用。 ## 系统依赖 ### FFmpeg 开发库 **Ubuntu/Debian:** ```bash sudo apt install libavcodec-dev libavformat-dev libavutil-dev libavfilter-dev libswscale-dev libswresample-dev pkg-config ``` **验证安装:** ```bash pkg-config --exists libavcodec libavformat libavutil echo $? # 应输出 0 ``` ## 项目结构 ``` goffmpeg/ ├── pkg/ffmpeg/ # 核心库 │ ├── cgo.go # CGO 配置和头文件导入 │ ├── errors.go # 错误类型定义 │ ├── ffmpeg.go # FormatContext、Stream、CodecParameters │ ├── codec.go # 编解码器封装 │ ├── frame.go # 帧数据封装 │ └── packet.go # 数据包封装 ├── cmd/ffmpeg-cli/ # CLI 工具 ├── examples/ # 示例代码 │ └── simple-transcode/ # 简单转码示例 └── README.md ``` ## 构建 ```bash go build ./... ``` ## 示例用法 ### 简单转码 (流拷贝模式) ```go package main import ( "fmt" "log" "os" "git.kingecg.top/kingecg/goffmpeg/pkg/ffmpeg" ) func main() { inputURL := "input.mp4" outputURL := "output.mp4" // 打开输入文件 ic := ffmpeg.AllocFormatContext() defer ic.Free() if err := ic.OpenInput(inputURL); err != nil { log.Fatalf("Failed to open input: %v", err) } defer ic.Close() // 查找流信息 if err := ic.FindStreamInfo(); err != nil { log.Fatalf("Failed to find stream info: %v", err) } // 打印输入格式信息 fmt.Printf("Input: %s\n", inputURL) ic.DumpFormat(0, inputURL, false) // 猜测输出格式 of := ffmpeg.GuessFormat("", outputURL) if of == nil { log.Fatal("Failed to guess output format") } // 创建输出上下文 ofc, err := ffmpeg.AllocOutputContext(outputURL, of) if err != nil { log.Fatalf("Failed to allocate output context: %v", err) } defer ofc.Free() // 获取输入流 inputStreams := ic.Streams() // 处理流 for i, is := range inputStreams { cp := is.CodecParameters() if cp == nil { continue } // 只处理视频和音频 if cp.CodecType() != ffmpeg.CodecTypeVideo && cp.CodecType() != ffmpeg.CodecTypeAudio { continue } // 添加输出流 (流拷贝模式) os, err := ofc.AddStream(nil) if err != nil { continue } // 复制编解码参数 os.SetCodecParameters(cp) os.SetTimeBase(is.TimeBase()) fmt.Printf("Stream %d: type=%d\n", i, cp.CodecType()) } // 打开输出文件 if err := ofc.OpenOutput(outputURL); err != nil { log.Fatalf("Failed to open output: %v", err) } // 写入头部 if err := ofc.WriteHeader(); err != nil { log.Fatalf("Failed to write header: %v", err) } // 转封装循环 pkt := ffmpeg.AllocPacket() defer pkt.Free() for { err := ic.ReadPacket(pkt) if err != nil { break } ofc.WritePacket(pkt) pkt.Unref() } // 写入尾部 ofc.WriteTrailer() fmt.Printf("Remuxing complete! Output saved to: %s\n", outputURL) } ``` ## API 概览 ### FormatContext - `AllocFormatContext()` - 分配格式上下文 - `OpenInput(url)` - 打开输入 - `FindStreamInfo()` - 查找流信息 - `Streams()` - 获取所有流 - `VideoStreams()` - 获取视频流 - `AudioStreams()` - 获取音频流 - `ReadPacket()` / `WritePacket()` - 读写数据包 ### OutputFormatContext - `AllocOutputContext(url, format)` - 分配输出上下文 - `AddStream(codec)` - 添加流 - `OpenOutput(url)` - 打开输出文件 - `WriteHeader()` / `WriteTrailer()` - 写入头部/尾部 ### Stream - `CodecParameters()` - 获取编解码参数 - `SetCodecParameters(cp)` - 设置编解码参数 - `TimeBase()` / `SetTimeBase(r)` - 时间基 ### CodecParameters - `CodecType()` - 编解码器类型 - `CodecID()` - 编解码器 ID - `Width()` / `Height()` - 尺寸 - `Format()` - 格式 - `SampleRate()` - 采样率 - `Channels()` - 声道数 ### Context (编解码器上下文) - `AllocContext()` - 分配上下文 - `SetCodec(codec)` - 设置编解码器 - `Open(codec)` - 打开编解码器 - `SendPacket()` / `ReceiveFrame()` - 解码 - `SendFrame()` / `ReceivePacket()` - 编码 ### Packet - `AllocPacket()` - 分配数据包 - `Data()` / `Size()` - 数据和大小 - `PTS()` / `DTS()` - 时间戳 - `StreamIndex()` - 流索引 ### Frame - `AllocFrame()` - 分配帧 - `Width()` / `Height()` - 尺寸 - `Format()` - 格式 - `NbSamples()` - 样本数 ## 注意事项 1. **CGO 构建标签**: 项目使用 `//go:build cgo` 约束,需要 CGO 支持才能构建 2. **FFmpeg 版本**: 需要 FFmpeg 4.0+ 3. **线程安全**: FFmpeg 的某些操作不是线程安全的,请参考 FFmpeg 文档 4. **内存管理**: 使用 `Free()` 方法释放资源,或使用 `defer` ## 许可证 MIT License