一、目的
本文旨在阐述ffmpeg模块的作用以及播放视频调用的关系,方便查找使用ffmpeg播放视频时问题所在。
二、模块作用以及主要数据结构
module
|-libavcodec: 音视频格式编解码器(audio/video encoder/decoder)
| |-AVCodecContext: 编解码上下文,如比特率、编码ID、类型(音/视)、类型特定信息(视频:算法、宽高、视频组大小、像素格式、Q值、b帧等,音频:采样率、通道数、采样格式等)
| |-AVCodec: 编解码实现函数
| |-AVClass: 类信息,如类名,类选项,log时可用
|-libavformat: 音视频封装格式的生成和解析,包括获取解码所需信息以及生成解码上下文结构和读取音视频帧功能(muxer/demuxer)
| |-AVFormatContext: 文件上下文、流、包列表
| |-AVClass: 类信息,如类名,类选项,log时可用
| |-AVInputFormat: 输入函数指针
| |-AVOutputFormat:输出函数指针
| |-AVStream: 音视频流信息
| |-AVCodecContext: 引用编解码器,参考module->libavcodec->AVCodecContext
|-libavutil: 公共的工具函数,如内存操作和CRC整数算法之类的
|-libavdevice: 对输入输出设备的支持
|-libpostproc: 对后期效果的处理
|-libswscale: 视频缩放和颜色映射转换
|-libavcore: 操作一些很裸的东西,没数据结构的,如av_image_fill_linesizes、av_image_fill_pointers、av_image_alloc之类,直接操作数组
三、播放视频调用关系以及作用
app
|-ffserver: http多媒体广播串流服务
|-ffplay: 使用SDL的播放器
| |-av_register_all(libavformat\allformats.c): 注册所有编码器、混编器(REGISTER_MUXER(libavformat/x##enc.c的x_muxer)、REGISTER_DEMUXER(libavformat/x##dec.c的x_demuxer))、REGISTER_PROTOCOL(libavformat/x##.c的x##_protocol)
| | |-av_register_output_format(libavformat\utils.c):放进first_oformat这个链表里
| | |-av_register_input_format(libavformat\utils.c):放进first_iformat这个链表里
| | |-av_register_protocol2(libavformat\avio.c):放进first_protocol这个链表里
| | |-avcodec_register_all(libavcodec\allcodecs.c):REGISTER_ENCDEC(libavcodec/x##enc.c的x##_encoder) REGISTER_DECODER(libavcodec/x##dec.c的x##_decoder) REGISTER_HWACCEL REGISTER_PARSER(libavcodec\x##_parser.c的x##_parser)
| | |-avcodec_register(libavcodec\utils.c): 放进first_avcodec这个链表里
| | |-av_register_codec_parser(libavcodec\parser.c): 放进av_first_parser这个链表里
| |-avformat_alloc_context(libavformat\options.c):分配AVFormatContext内存,设置类信息(缺省值以及Log用)
| |-av_open_input_file(libavformat\utils.c): 打开文件,将文件头信息填入AVFormatContext结构体
| | |-av_probe_input_format(libavformat\utils.c):查找最合适的输入格式
| | | |-av_iformat_next(libavformat\utils.c): 遍历first_iformat这个链表
| | | |-AVInputFormat::read_probe(libavformat\x##dec.c)/av_match_ext(libavformat\utils.c):计算匹配程度
| | |-url_fopen(libavformat\aviobuf.c): 打开URL或文件
| | | |-url_open(libavformat\avio.c):
| | | | |-url_alloc(libavformat\avio.c): 从first_protocol链表里查找匹配的协议并分配内存和初始化
| | | | | |-url_alloc_for_protocol(libavformat\avio.c): 分配内存初始化并将匹配的协议填到结构体里面
| | | | |-url_connect(libavformat\avio.c): 调用协议结构体里的url_open
| | | |-url_fdopen(libavformat\aviobuf.c): 分配内存ByteIOContext,分配包缓冲区,填充URLContext的部分信息到ByteIOContext(读写、seek、pause)
| | |-ff_probe_input_buffer(如无匹配format)(libavformat\utils.c): 匹配内容
| | | |-get_buffer(libavformat\aviobuf.c): 读取一个包内容
| | | |-av_probe_input_format2(libavformat\utils.c):查找最合适的输入格式
| | |-av_open_input_stream(libavformat\utils.c):填充ByteIOContext和AVInputFormat进AVFormatContext,读取标签和头部信息AVInputFormat::read_header
| | |-ff_id3v2_read(libavformat\id3v2.c): 读取(如有)并解析ID3标签
| |-av_find_stream_info(libavformat\utils.c): 获取编码器信息,包括时长、宽高、帧率
| | |-av_parser_init(libavcodec\parser.c): 在av_first_parser里查找id对应的的AVCodecParser,分配AVCodecParserContext内存并设置成员,调用AVCodecParser::parser_init
| | |-avcodec_open(libavcodec\utils.c): 将编码器私有信息拷到编码器上下文里,设置编码器上下文的编码器、类型和编码器id,调用具体编码器的init函数
| | |-av_read_frame_internal(libavformat\utils.c):读取一个包
| | |-avcodec_decode_video2/avcodec_decode_audio3(libavcodec\utils.c): 调用AVCodec::decode解码,或许能填充信息
| | |-avcodec_close(libavcodec\utils.c): 关闭编码器
| |-avformat_seek_file(如果需要从某个时间点开始播放)(libavformat\utils.c):跳到某个时间点(us)
| |-avcodec_find_decoder(libavcodec\utils.c): 从first_avcodec里面查找id匹配的返回
| |-av_set_string3(libavutil\opt.c): 设置编解码器特定选项
| |-avcodec_open(libavcodec\utils.c): 将编码器私有信息拷到编码器上下文里,设置编码器上下文的编码器、类型和编码器id,调用具体编码器的init函数。
| |-av_init_packet(libavcodec\avpacket.c): 初始化临时包
| |-av_read_frame(libavformat\utils.c): 读取一个包(如有需要,则在读完后生成pts才返回)
| | |-av_read_frame_internal(libavformat\utils.c)
| | |-av_init_packet(libavcodec\avpacket.c): 初始化临时包
| | |-av_read_packet(libavformat\utils.c): 读取一个包
| | | |-AVInputFormat::read_packet(libavformat\x##dec.c):读取一个包
| | |-av_parser_parse2(libavcodec\parser.c): 解析包内容
| |-avcodec_decode_video2(libavcodec\utils.c): 解码视频包
| | |-av_image_check_size(libavcore\imgutils.c):检查宽高是否合法
| | |-AVCodecContext::AVCodec::decode(libavcodec\*dec.c):调用注册解码器时的结构体函数,如mpeg4videodec.c里面结构体里面其实是引用了libavcodec\h263dec.c的函数ff_h263_decode_frame
| |-avcodec_close(libavcodec\utils.c): 关闭解码器
| |-avcodec_thread_free(libavcodec\pthread.c):调用pthread_mutex_lock、pthread_cond_broadcast、pthread_mutex_unlock通知线程结束,pthread_join等待线程结束,pthread_mutex_destroy、pthread_cond_destroy销毁资源,av_freep(&avctx->thread_opaque)释放线程上下文
| |-AVCodecContext::AVCodec::close(libavcodec\*dec.c):调用注册解码器中的函数
| |-avcodec_default_free_buffers(libavcodec\utils.c):释放内部缓存
| |-av_freep(libavutil\mem.c): 释放私有数据