读取媒体文件中音视频数据的基本功能

调用逻辑

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;
}
//找AVCodec
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``demuxprotocols。只有调用了该函数,才能使用复用器、编码器等

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

avformat_open_input

完成媒体文件的打开和格式探测的功能。方法中调用了 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 的读取数据的函数

https://img-blog.csdn.net/20141109152923350?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGVpeGlhb2h1YTEwMjA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast

参考