文章目录
avio_alloc_context 作用与原理
基本概念
avio_alloc_context() 是FFmpeg中用于创建自定义I/O上下文的核心函数,它允许开发者将内存缓冲区作为FFmpeg的输入或输出源,替代传统的文件I/O操作。
函数原型分析
c
AVIOContext *avio_alloc_context(
unsigned char *buffer, // FFmpeg使用的数据缓冲区
int buffer_size, // 缓冲区大小
int write_flag, // 读写标志:0-FFmpeg读,1-FFmpeg写
void *opaque, // 用户数据指针,传递给回调函数
int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
int64_t (*seek)(void *opaque, int64_t offset, int whence)
);
参数详细说明
| 参数 | 说明 |
|---|---|
| buffer | 由av_malloc()分配的内存缓冲区,FFmpeg使用该区域进行数据操作 |
| buffer_size | 缓冲区大小,影响I/O性能,建议设置为缓存页大小(如4KB) |
| write_flag | 0: FFmpeg从缓冲区读取(输入模式) 1: FFmpeg向缓冲区写入(输出模式) |
| opaque | 用户自定义数据,会作为第一个参数传递给所有回调函数 |
| read_packet | 数据读取回调函数,当FFmpeg需要更多数据时调用 |
| write_packet | 数据写入回调函数,当缓冲区有数据需要输出时调用 |
| seek | 定位回调函数,支持随机访问时使用 |
回调函数工作机制
read_packet 回调函数
c
static int read_packet(void *opaque, uint8_t *buf, int buf_size)
{
FILE *in_file = (FILE *)opaque;
int read_size = fread(buf, 1, buf_size, in_file);
printf("read_packet read_size:%d, buf_size:%d\n", read_size, buf_size);
if(read_size <=0) {
return AVERROR_EOF; // 数据读取完毕
}
return read_size;
}
工作流程:
FFmpeg内部缓冲区数据不足时自动调用
用户从实际数据源(如文件、网络)读取数据到buf
返回实际读取的字节数
返回AVERROR_EOF表示数据结束
write_packet 回调函数
c
// 本例中未使用,但机制类似
static int write_packet(void *opaque, uint8_t *buf, int buf_size)
{
FILE *out_file = (FILE *)opaque;
return fwrite(buf, 1, buf_size, out_file);
}
seek 回调函数
c
// 支持随机访问的示例
static int64_t seek(void *opaque, int64_t offset, int whence)
{
FILE *file = (FILE *)opaque;
if(fseek(file, offset, whence) < 0)
return -1;
return ftell(file);
}
程序功能概述
这是一个音频解码器程序,主要功能:
- 从输入文件读取音频数据(如AAC、MP3)
- 使用FFmpeg进行解码
- 将解码后的PCM数据写入输出文件
内存I/O模式设置流程
c
// 分配I/O缓冲区
uint8_t *io_buffer = av_malloc(BUF_SIZE);
// 创建AVIO上下文
AVIOContext *avio_ctx = avio_alloc_context(
io_buffer, // 缓冲区
BUF_SIZE, // 缓冲区大小
0, // 读模式(FFmpeg从缓冲区读)
(void *)in_file, // 用户数据(文件指针)
read_packet, // 读取回调
NULL, // 无写入回调
NULL // 无seek支持
);
// 关联到格式上下文
AVFormatContext *format_ctx = avformat_alloc_context();
format_ctx->pb = avio_ctx; // 设置自定义I/O
// 打开输入(注意第二个参数为NULL,因为使用自定义I/O)
int ret = avformat_open_input(&format_ctx, NULL, NULL, NULL);
数据流向示意图
文件数据 → read_packet回调 → io_buffer → FFmpeg解码器 → PCM输出文件
解码和格式转换
c
static void decode(AVCodecContext *dec_ctx, AVPacket *packet, AVFrame *frame, FILE *outfile)
{
// 发送数据包到解码器
avcodec_send_packet(dec_ctx, packet);
// 接收解码后的帧
while (avcodec_receive_frame(dec_ctx, frame) >= 0) {
// 平面格式(Planar)转交错格式(Interleaved)
int data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);
for(int i = 0; i < frame->nb_samples; i++) {
for(int ch = 0; ch < dec_ctx->channels; ch++) {
// 将各通道的数据交错排列写入文件
fwrite(frame->data[ch] + data_size * i, 1, data_size, outfile);
}
}
}
}
音频格式说明
- Planar格式 :各通道数据分开存储
LLLLLLRRRRRRLLLLLLRRRRRR... - Interleaved格式 :各通道数据交错存储
LRLRLRLRLRLRLRLR...
适用场景
- 加密媒体处理:解密后数据直接送入FFmpeg
- 网络流媒体:从网络接收数据实时解码
- 内存数据处理:数据已在内存中,避免文件I/O开销
- 自定义协议:实现特殊的数据获取方式
优势
- 灵活性:可以处理各种非标准数据源
- 效率:减少磁盘I/O,提高处理速度
- 控制性:完全控制数据的读取和写入过程
总结
通过avio_alloc_context()实现的内存I/O模式为FFmpeg提供了极大的灵活性,使得开发者能够处理各种特殊的数据源。这种机制的核心在于回调函数的设计,通过用户自定义的读取、写入和定位函数,FFmpeg可以与任何数据源进行交互,而不仅限于传统的文件系统。
在实际应用中,这种模式特别适合处理加密媒体、网络流、内存数据等场景,为多媒体处理提供了强大的扩展能力。