读取媒体文件中音视频数据的基本功能
调用逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| char filepath[]="bigbuckbunny_480x272.h265"; AVPacket pkt; int ret = 0; AVCodecContext *pCodecCtx; AVCodec *pCodec; int videoindex = -1;
av_register_all(); avformat_network_init();
AVFormatContext *pFormatCtx = avformat_alloc_context(); if(avformat_open_input(&pFormatCtx, filepath, NULL, NULL)!=0){ return -1; }
if(avformat_find_stream_info(pFormatCtx, NULL)<0){ return -1; }
for(i=0; i<pFormatCtx->nb_streams; i++) { if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){ videoindex=i; break; } } if(videoindex==-1) { return -1; } pCodecCtx=pFormatCtx->streams[videoindex]->codec; pCodec=avcodec_find_decoder(pCodecCtx->codec_id); if(pCodec==NULL){ return -1; }
if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){ return -1; }
while(ret >= 0) { ret = av_read_frame(pFormatCtx, &pkt); }
|
av_register_all
作用是注册codecs``demux
和protocols
。只有调用了该函数,才能使用复用器、编码器等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #define REGISTER_MUXER(X, x) \ { \ extern AVOutputFormat ff_##x##_muxer; \ if (CONFIG_##X##_MUXER) \ av_register_output_format(&ff_##x##_muxer); \ }
#define REGISTER_DEMUXER(X, x) \ { \ extern AVInputFormat ff_##x##_demuxer; \ if (CONFIG_##X##_DEMUXER) \ av_register_input_format(&ff_##x##_demuxer); \ }
#define REGISTER_MUXDEMUX(X, x) REGISTER_MUXER(X, x); REGISTER_DEMUXER(X, x)
REGISTER_MUXDEMUX(HEVC,hevc);
|
REGISTER_MUXDEMUX
实际上调用的是 av_register_input_format()
和 av_register_output_format()
,通过这两个方法,将(解)复用器分别添加到了全局变量 first_iformat
与 first_oformat
链表的最后位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| void av_register_input_format(AVInputFormat *format) { AVInputFormat **p = last_iformat;
format->next = NULL; while(*p || avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format)) p = &(*p)->next; last_iformat = &format->next; }
void av_register_output_format(AVOutputFormat *format) { AVOutputFormat **p = last_oformat;
format->next = NULL; while(*p || avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format)) p = &(*p)->next; last_oformat = &format->next; }
|
完成媒体文件的打开和格式探测的功能。方法中调用了 init_input
函数,在这里完成了查找流媒体协议和解复用器的工作
1 2 3 4 5 6 7 8 9 10 11 12 13
| static int init_input(AVFormatContext *s, const char *filename, AVDictionary **options) { int ret; …… if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0) return ret;
if (s->iformat) return 0; return av_probe_input_buffer2(s->pb, &s->iformat, filename, s, 0, s->format_probesize); }
|
s->io_open
实际上调用的就是 io_open_default()
,它最终调用到 url_find_protocol
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| static const struct URLProtocol *url_find_protocol(const char *filename) { const URLProtocol **protocols; …… protocols = ffurl_get_protocols(NULL, NULL); if (!protocols) return NULL; for (i = 0; protocols[i]; i++) { const URLProtocol *up = protocols[i]; if (!strcmp(proto_str, up->name)) { av_freep(&protocols); return up; } if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME && !strcmp(proto_nested, up->name)) { av_freep(&protocols); return up; } } av_freep(&protocols);
return NULL; }
|
ffurl_get_protocols()
可以得到当前编译的 FFmpeg 支持的所有流媒体协议,通过 url 的 scheme 和 protocol->name 相比较,得到正确的 protocol
av_probe_input_buffer2()
最终调用到 av_probe_input_format3()
, 该方法遍历所有的解复用器,即 first_iformat 链表中的所有节点,调用它们的 read_probe()
函数计算匹配得分,函数最终返回计算找到的最匹配的解复用器
av_read_frame
读取媒体数据中的每个音视频帧,该方法中最关键的地方就是调用了 AVInputFormat 的 read_packet()
方法。AVInputFormat 的 read_packet()
是一个函数指针,指向当前的 AVInputFormat 的读取数据的函数
参考