找回密码
 立即注册
LiveVideoStack 首页 资讯 查看内容
  • QQ空间
  • 回复
  • 收藏

FFmpeg专栏 | 代码导读——基础篇(二)

2018-4-12 21:16

2.3代码结构


下面这段代码完成了读取媒体文件中音视频数据的基本功能,本节以此为例,分析FFmpeg内部代码的调用逻辑。


char *url = "http://192.168.1.105/test.flv";
AVPacket pkt;
int ret = 0;
//注册复用器、编码器等
av_register_all();
avformat_network_init();
//打开文件
AVFormatContext *fmtCtx = avformat_alloc_context();
ret = avformat_open_input(&fmtCtx, url, NULL, NULL);
ret = avformat_find_stream_info(fmtCtx, NULL);
//读取音视频数据
while(ret >= 0)
{
 ret = av_read_frame(s, &pkt);
}


2.3.1注册


av_register_all函数的作用是注册一系列的(解)复用器、编/解码器等。它在所有基于FFmpeg的应用程序中几乎都是第一个被调用的,只有调用了该函数,才能使用复用器、编码器等。


static void register_all(void)
{
avcodec_register_all();
   /* (de)muxers */
   ……
   REGISTER_MUXDEMUX(FLV,              flv);
   ……
}REGISTER_MUXDEMUX实际上调用的是av_register_input_format和av_register_output_format,通过这两个方法,将(解)复用器分别添加到了全局变量first_iformat与first_oformat链表的最后位置。


编/解码其注册过程相同,此处不再赘述。


2.3.2文件打开


FFmpeg读取媒体数据的过程始于avformat_open_input,该方法中完成了媒体文件的打开和格式探测的功能。但FFmpeg是如何找到正确的流媒体协议和解复用器呢?可以看到avformat_open_input方法中调用了init_input函数,在这里面完成了查找流媒体协议和解复用器的工作。


static intinit_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);
}


1.s->io_open实际上调用的就是io_open_default,它最终调用到url_find_protocol方法。


static conststructURLProtocol *url_find_protocol(const char *filename)
{
constURLProtocol **protocols;
   ……
   protocols = ffurl_get_protocols(NULL, NULL);
   if (!protocols)
       return NULL;
   for (i = 0; protocols[i]; i++) {
constURLProtocol *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。例如本例中URLProtocol最终指向了libavformat/http.c中的ff_http_protocol。


2.av_probe_input_buffer2最终调用到av_probe_input_format3,该方法遍历所有的解复用器,即first_iformat链表中的所有节点,调用它们的read_probe()函数计算匹配得分,函数最终返回计算找到的最匹配的解复用器。本例中AVInputFormat最终指向了libavformat/flvdec.c中的ff_flv_demuxer。


2.3.3数据读取


av_read_frame作用是读取媒体数据中的每个音视频帧,该方法中最关键的地方就是调用了AVInputFormat的read_packet()方法。AVInputFormat的read_packet()是一个函数指针,指向当前的AVInputFormat的读取数据的函数。在本例中,AVInputFormat为ff_flv_demuxer,也就是说read_packet最终指向了flv_read_packet。

原作者: 施雪梅 来自: LiveVideoStack
文章点评