ffmpeg模块以及播放视频调用关系

一、目的

本文旨在阐述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): 释放私有数据

FFmpeg是一个强大的跨平台多媒体框架,它支持广泛的音频和视频文件处理,包括编码、解码、流媒体转换等。对于解码视频API的调用流程,通常分为以下几个步骤: 1. **初始化 FFmpeg**:首先需要包含FFmpeg,并创建一个`avformat_context`结构体实例,这是FFmpeg的核心结构,用于存储解码器上下文信息。 ```c av_register_all(); avformat_network_init(); // 初始化网络模块 avformat_instance = avformat_alloc_context(); ``` 2. **打开输入文件**:通过`avformat_open_input`函数指定要解码的文件路径,并设置`avformat_instance`。 ```c if (avformat_open_input(&avformat_instance, input_filename, NULL, NULL) < 0) { // 处理错误 } ``` 3. **查找并注册解析器**:调用`avformat_find_stream_info`来获取文件的元数据,并自动注册相关的解析器。 ```c if (avformat_find_stream_info(avformat_instance, NULL) < 0) { // 处理错误 } ``` 4. **选择视频流**:遍历`avformat_instance->streams`,找到视频流并获取解码器上下文`avcodec_context`。 ```c for (int i = 0; i < avformat_instance->nb_streams; ++i) { if (avstream_is_video(avformat_instance->streams[i])) { video_codec_ctx = avformat_get.stream(i)->codec; break; } } ``` 5. **分配解码器和帧缓冲区**:初始化解码器并分配内存缓冲区用于存放解码后的像素数据。 ```c if (avcodec_open2(video_codec_ctx, avcodec_find_decoder(video_codec_ctx->codec_id), NULL) < 0) { // 处理错误 } frame = av_frame_alloc(); ``` 6. **解码视频帧**:调用`avcodec_decode_video2`函数,提供输入缓冲区和输出缓冲区,直到返回错误或者达到文件结束。 7. **播放/处理解码后的帧**:循环解码,可以对解码后的帧进行显示、分析或者其他操作。 8. **释放资源**:解码完成后,记得关闭解码器、释放帧,以及清理FFmpeg的其他资源。 ```c avcodec_close(video_codec_ctx); av_frame_free(&frame); avformat_close_input(&avformat_instance); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值