ffmpeg结构体,函数分析

来源:互联网 发布:端口转发 8283被禁止 编辑:程序博客网 时间:2024/06/05 14:47

如何gdb调试?

 ./configure  --enable-shared --prefix=/usr/local/ffmpeg  --enable-debug --disable-yasm --disable-optimizations --disable-asm --disable-stripping

等编译好了后,gdb ffmpeg_g即可调试。

结构分类

FFMPEG中结构体很多。最关键的结构体可以分成以下几类:

a)        解协议(http,rtsp,rtmp,mms)

AVIOContext,URLProtocol,URLContext主要存储视音频使用的协议的类型以及状态。URLProtocol存储输入视音频使用的封装格式。每种协议都对应一个URLProtocol结构。(注意:FFMPEG中文件也被当做一种协议“file”)

b)        解封装(flv,avi,rmvb,mp4)

AVFormatContext主要存储视音频封装格式中包含的信息;AVInputFormat存储输入视音频使用的封装格式。每种视音频封装格式都对应一个AVInputFormat 结构。

c)        解码(h264,mpeg2,aac,mp3)

每个AVStream存储一个视频/音频流的相关数据;每个AVStream对应一个AVCodecContext,存储该视频/音频流使用解码方式的相关数据;每个AVCodecContext中对应一个AVCodec,包含该视频/音频对应的解码器。每种解码器都对应一个AVCodec结构。

d) 存数据

视频的话,每个结构一般是存一帧;音频可能有好几帧

解码前数据:AVPacket

解码后数据:AVFrame

他们之间的对应关系如下所示:



AVFormatContext

在使用FFMPEG进行开发的时候,AVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数。它是FFMPEG解封装(flv,mp4,rmvb,avi)功能的结构体。
typedef struct AVFormatContext {

    struct AVInputFormat *iformat:      //Demuxing only

    struct AVOutputFormat *oformat;    //Muxing only

    AVIOContext *pb:输入数据的缓存
    unsigned int nb_streams:视音频流的个数
    AVStream **streams:视音频流
    char filename[1024]:文件名
    int64_t duration:时长(单位:微秒us,转换为秒需要除以1000000)
    int bit_rate:比特率(单位bps,转换为kbps需要除以1000)
    AVDictionary *metadata:元数据
    ......

}
av_read_frame
主要用到了如下函数。read_from_packet_buffer,read_frame_internal(ff_read_packet(probe_codec),parse_packet(av_parser_parse2)。


与av_read_packet的区别是读出的是包,它可能是半帧或多帧,不保证帧的完整性。av_read_frame对av_read_packet进行了封装,使读出的数据总是完整的帧。
read_from_packet_buffer
read_from_packet_buffer函数比较简单,从AVPacketList里取出数据即可。若缓存中没有数据,av_read_frame函数则会调用read_frame_internal函数来获取数据。
read_frame_internal
read_frame_internal主要有以下两步,
(1)调用了ff_read_packet()从相应的AVInputFormat读取数据。
(2)如果媒体频流需要使用AVCodecParser,则调用parse_packet()解析相应的AVPacket。
ff_read_packet
ff_read_packet函数也是会判断缓存是否有数据,若有则从缓存中取出,若没有,则调用demuxer的read_packet来读取数据。ff_read_packet()中最关键的地方就是调用了AVInputFormat的read_packet()方法。AVInputFormat的read_packet()是一个函数指针,指向当前的AVInputFormat的读取数据的函数,比如flv的flv_read_packet,flv_read_packet()的代码比较长,但是逻辑比较简单。它的主要功能就是根据FLV文件格式的规范,逐层解析Tag以及TagData,获取Tag以及TagData中的信息。
ff_read_packet的缓存和av_read_frame函数里的缓存是不一样的,ff_read_packet的缓存为AVFormatInternal::raw_packet_buffer,在av_read_frame函数里缓存则为
AVFormatInternal::packet_buffer,简单的理解为在codec被识别之前用raw_packet_buffer缓存,codec识别后用packet_buffer。
probe_codec
probe_codec是用来探测codec的,probe_codec先是把pkt的数据打包进AVProbeData,然后调用set_codec_from_probe_data来进行探测的,set_codec_from_probe_data的基本思想是根据av_probe_input_format3函数返回的一个AVInputFormat格式来和fmt_id_type匹配得出的codec_id和type的。
parse_packet
parse_packet()给需要AVCodecParser的媒体流提供解析AVPacket的功能,最终调用了相应AVCodecParser的av_parser_parse2()函数,代码为s->parser->parser_parse,接着会调用具体的解析函数,如h264_parse(parse_nal_units),最终解析出来AVPacket。
h264解析
ff_h264_decode_seq_parameter_set  解析SPS。
ff_h264_decode_picture_parameter_set   解析PPS。
ff_h264_decode_sei                   解析SEI。
以上代码在libavcdec/h264_parser.c。

AVClass与AVOption
AVClass最主要的作用就是给结构体(例如AVFormatContext等)增加AVOption功能的支持。换句话说AVClass就是AVOption和目标结构体之间的“桥梁”。AVClass要求必须声明为目标结构体的第一个变量。 AVClass中有一个option数组用于存储目标结构体的所有的AVOption。举个例子,AVFormatContext结构体,AVClass和AVOption之间的关系如下图所示。

    图中AVFormatContext结构体的第一个变量为AVClass类型的指针av_class,它在AVFormatContext结构体初始化的时候,被赋值指向了全局静态变量av_format_context_class结构体(定义位于libavformat\options.c)。而AVClass类型的av_format_context_class结构体中的option变量指向了全局静态数组avformat_options(定义位于libavformat\options_table.h)。现在回到AVOption。其实除了可以对FFmpeg常用结构体AVFormatContext,AVCodecContext等进行赋值之外,还可以对它们的私有数据priv_data进行赋值。这个字段里通常存储了各种编码器特有的结构体。而这些结构体的定义在FFmpeg的SDK中是找不到的。例如使用libx264进行编码的时候,通过AVCodecContext的priv_data字段可以对X264Context结构体中的变量进行赋值,设置preset,profile等。使用libx265进行编码的时候,通过AVCodecContext的priv_data字段可以对libx265Context结构体中的变量进行赋值,设置preset,tune等。

AVProbeData
typedef struct AVProbeData {
    const char *filename;  //文件名
    unsigned char *buf; /**< Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero. // buf存储用于推测AVInputFormat的媒体数据,最后还有个

mime_type保存媒体的类型。其中buf可以为空,但是其后面无论如何都需要 填充AVPROBE_PADDING_SIZE个0(AVPROBE_PADDING_SIZE取值为32,即32个0)。 */
    int buf_size;       /**< Size of buf except extra allocated bytes */
    const char *mime_type; /**< mime_type, when known. */
} AVProbeData;

avformat_open_input(init_input(av_probe_input_format3), s->iformat->read_header());
init_ input
if ((ret = init_input(s, filename)) < 0)  
        goto fail;  
    //执行完此函数后,s->pb和s->iformat都已经指向了有效实例.pb是用于读写数据的,它把媒体数据当做流来读写,不管是什么媒体格式,而iformat把pb读出来的流按某种媒体格式进行分析,也就是说pb在底层,iformat在上层。
主要分两种情况
1 知道s->pb,从s->pb得到s->iformat。
2 不知道s->pb,打开文件(avio_open),探测文件格式(av_probe_input_buffer)。
av_probe_input_format3
av_probe_input_format3()根据输入数据查找合适的AVInputFormat。输入的数据位于AVProbeData中。该函数最主要的部分是一个循环。该循环调用av_iformat_next()遍历FFmpeg中所有的AVInputFormat(av_register_all时组建),并根据以下规则确定AVInputFormat和输入媒体数据的匹配分数(score,反应匹配程度):
(1) 如果AVInputFormat中包含read_probe(),就调用read_probe()函数获取匹配分数(这一方法如果结果匹配的话,一般会获 得AVPROBE_SCORE_MAX的分值,即100分)。如果不包含该函数,就使用av_match_ext()函数比较输入媒体的扩展名和 AVInputFormat的扩展名是否匹配,如果匹配的话,设定匹配分数为 AVPROBE_SCORE_EXTENSION(AVPROBE_SCORE_EXTENSION取值为50,即50分)。
(2)使用av_match_name()比较输入媒体的mime_type和AVInputFormat的mime_type,如果匹配的话,设定匹配分数为AVPROBE_SCORE_MIME(AVPROBE_SCORE_MIME取值为75,即75分)。
(3)如果该AVInputFormat的匹配分数大于此前的最大匹配分数,则记录当前的匹配分数为最大匹配分数,并且记录当前的AVInputFormat为最佳匹配的AVInputFormat。

ffmpeg.c
parse_option()
    解析一个输入选项。具体的解析步骤不再赘述。parse_options()会循环调用parse_option()直到所有选项解析完毕。FFmpeg的每一个选项信息存储在一个OptionDef结构体中。



问题

#如何gdb调试?

编译的时候加上--disable-yasm --enable-debug --disable-optimizations,应当调试ffmpeg_g,而不是ffmpeg。

Unable to find a suitable output format for 'ffmpeg'

eclipse参数第一个不用写ffmpeg,直接写其他参数,如-i。

1 0
原创粉丝点击