```
feat: 添加 goffmpeg 项目的 README 文档 新增完整的项目说明文档,包含以下内容: - 项目简介和系统依赖说明 - 详细的项目结构介绍 - 构建方法和使用示例 - API 概览,包括 FormatContext、OutputFormatContext、Stream、 CodecParameters、Context、Packet、Frame 等核心组件的接口说明 - 使用注意事项和许可证信息 ```
This commit is contained in:
parent
03039dec91
commit
b938e3ac5f
|
|
@ -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
|
||||
Loading…
Reference in New Issue