1.AVIO的作用
自认为最重要的是可以自定义读取和写入,也就是说可以用AVIO来自定义封装格式
2.AVIO的使用
/**
* Allocate and initialize an AVIOContext for buffered I/O. It must be later
* freed with avio_context_free().
*
* @param buffer Memory block for input/output operations via AVIOContext.
* The buffer must be allocated with av_malloc() and friends.
* It may be freed and replaced with a new buffer by libavformat.
* AVIOContext.buffer holds the buffer currently in use,
* which must be later freed with av_free().
* @param buffer_size The buffer size is very important for performance.
* For protocols with fixed blocksize it should be set to this blocksize.
* For others a typical size is a cache page, e.g. 4kb.
* @param write_flag Set to 1 if the buffer should be writable, 0 otherwise.
* @param opaque An opaque pointer to user-specific data.
* @param read_packet A function for refilling the buffer, may be NULL.
* For stream protocols, must never return 0 but rather
* a proper AVERROR code.
* @param write_packet A function for writing the buffer contents, may be NULL.
* The function may not change the input buffers content.
* @param seek A function for seeking to specified byte position, may be NULL.
*
* @return Allocated AVIOContext or NULL on failure.
*/
/**
*为缓冲I/O分配和初始化AVIOContext。必须稍后
*释放avio_context_free()。
*
*@参数缓冲区通过AVIOContext进行输入/输出操作的内存块。
*缓冲区必须分配给av_malloc()和朋友。
*它可以被libavformat释放并替换为新的缓冲区。
*AVIOContext.缓冲区保存当前正在使用的缓冲区,
*以后必须用av_free()释放。
*@参数buffer_size缓冲区大小对性能非常重要。
*对于具有固定块大小的协议,应将其设置为此块大小。
*对于其他人,典型大小是缓存页面,例如4kb。
*@参数write_flag如果缓冲区应该是可写的,则设置为1,否则设置为0。
*@参数不透明指向用户特定数据的不透明指针。
*@参数read_packet重新填充缓冲区的函数,可能为NULL。
*对于流协议,绝不能返回0,而是
*正确的AVERROR代码。
*@参数write_packet用于写入缓冲区内容的函数,可能为NULL。
*该功能可能不会改变输入缓冲区的内容。
*@参数查找用于查找指定字节位置的函数,可能为NULL。
*
*@返回分配的AVIOContext或失败时为NULL。
*/
AVIOContext *avio_alloc_context(
unsigned char *buffer,
int buffer_size,
int write_flag,
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创建
buffer_size是缓冲区大小
write_flag:1表示当前AVIO用于写,0表示用于读
opaque:表示用户数据
read_packet:是读取的钩子函数,当需要读取时调用该函数
write_packet:是写入的钩子函数,当需要写入时调用该函数
seek:是随机访问的钩子函数,当需要随机访问时调用该函数
使用方法:
创建AVIOContext,然后创建AVFormatContext,将AVIOContext绑定到AVFormatContext->pb,最后调用avformat_open_input函数即可,只不过这时avformat_open_input函数的第二个参数url就不用传了,直接传入NULL即可。
3.案例
/**
* @projectName 07-05-decode_audio
* @brief 解码音频,主要的测试格式aac和mp3
* @author Liao Qingfu
* @date 2020-01-16
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libavutil/frame.h>
#include <libavutil/mem.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#define IO_BUF_SIZE 20480
int read_packet(void *opaque, uint8_t *buf, int buf_size)
{
FILE* in_file=opaque;
int size=fread(buf,1,buf_size,in_file);
if(size<=0)
{
return AVERROR_EOF;
}
return size;
}
static void print_sample_format(const AVFrame *frame)
{
printf("ar-samplerate: %uHz\n", frame->sample_rate);
printf("ac-channel: %u\n", frame->channels);
printf("f-format: %u\n", frame->format);// 格式需要注意,实际存储到本地文件时已经改成交错模式
}
void decode(AVCodecContext* codec_ctx,AVPacket* packet,AVFrame* frame,FILE* out_file)
{
int ret=avcodec_send_packet(codec_ctx,packet);
if(ret<0)
{
printf("avcodec_send_packet failed: %s\n",av_err2str(ret));
return ;
}
while(ret>=0)
{
ret= avcodec_receive_frame(codec_ctx,frame);
if(ret<0)
{
break;
}
// /**
// P表示Planar(平面),其数据格式排列方式为 :
// LLLLLLRRRRRRLLLLLLRRRRRRLLLLLLRRRRRRL...(每个LLLLLLRRRRRR为一个音频帧)
// 而不带P的数据格式(即交错排列)排列方式为:
// LRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRL...(每个LR为一个音频样本)
// 播放范例: ffplay -ar 48000 -ac 2 -f f32le believe.pcm
// 并不是每一种都是这样的格式
// */
size_t data_size = av_get_bytes_per_sample(codec_ctx->sample_fmt);// 每一帧的字节数
print_sample_format(frame);
for(int i=0;i<frame->nb_samples;i++)
{
for(int j=0;j<codec_ctx->channels;j++)
{
fwrite(frame->data[j] + data_size *i, 1, data_size, out_file);
//data[j]的j表示第几个声道,data是一个二维数组,
}
}
printf("2. data_size :%d\n",data_size);
}
}
int main(int argc,char** argv)
{
if(argc!=3)
{
printf("usege:./process <in_file> <out_file>\n");
return -1;
}
char* in_file_name=argv[1];
char* out_file_name=argv[2];
FILE* in_file=NULL;
FILE* out_file=NULL;
int ret=0;
//打开文件
in_file=fopen(in_file_name,"rb");
if(!in_file)
{
printf("in_file open failed\n");
return -1;
}
out_file=fopen(out_file_name,"wb");
if(!out_file)
{
printf("out_file open failed\n");
return -1;
}
//自定义IO
unsigned char * io_buf=(unsigned char *)av_malloc(IO_BUF_SIZE);
AVIOContext* avio_ctx=avio_alloc_context(io_buf,IO_BUF_SIZE,0,(void*)in_file,read_packet,NULL,NULL);
//创建复用器上下文
AVFormatContext* fmt_ctx=avformat_alloc_context();
if(!fmt_ctx)
{
printf("av_read_frame failed\n");
return -1;
}
fmt_ctx->pb=avio_ctx;//绑定自定义IO
ret = avformat_open_input(&fmt_ctx,NULL,NULL,NULL);
if(ret)
{
printf("avformat_open_input failed:%s \n",av_err2str(ret));
return -1;
}
//创建解码器上下文
AVCodec* codec=avcodec_find_decoder(AV_CODEC_ID_AAC);
AVCodecContext* codec_ctx=avcodec_alloc_context3(codec);
//解码器与解码器上下文绑定
avcodec_open2(codec_ctx,codec,NULL);
AVPacket* packet=av_packet_alloc();
AVFrame * frame=av_frame_alloc();
while(1)
{
ret=av_read_frame(fmt_ctx,packet);
if(ret)
{
printf("av_read_frame failed:%s \n",av_err2str(ret));
break;
}
decode(codec_ctx,packet,frame,out_file);
}
decode(codec_ctx,NULL,frame,out_file);
return 0;
}