AVIO内存输入模式

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;
}

猜你喜欢

转载自blog.csdn.net/m0_60565784/article/details/131354508