diff --git a/README.md b/README.md new file mode 100644 index 0000000..6d51e75 --- /dev/null +++ b/README.md @@ -0,0 +1,222 @@ +# 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