ffmpeg example 1.解封装,解码学习
背景
学习ffmpeg,打算从源码入手,源码又太多太复杂。好在ffmpeg提供了示例代码,演示如何使用ffmpeg的api, 示例代码位于ffmpeg/doc/examples
目录下,可以通过vscode 来调试这些示例代码,理解ffmpeg的调用方式。
该目录下的示例代码如下
decode_audio.c 演示如何解码音频
decode_video.c 演示如何解码视频
demuxing_decoding.c 演示如何解封装文件,和解码音视频
今天来分析 demuxing_decoding.c
流程
使用的模块libavutil
,libavcodec
,libavformat
libavutil 包含一些公共的工具函数
libavcodec 用于各种类型声音/图像编解码
libavformat 用于各种音视频封装格式的生成和解析,包括获取解码所需信息以生成解码上下文结构和读取音视频帧等功能,包含demuxers和muxer库
1.解封装
打开文件、获取封装信息上下文AVFormatContext(avformat_open_input)
获取媒体文件音视频信息,这一步会将AVFormatContext内部变量填充(avformat_find_stream_info)
获取音视频流ID。一般有两种方法:
遍历AVFormatContext内部所有的stream,如果stream的codec_type对应为audio/video,这记录当前stream的ID;
FFmpeg提供av_find_best_stream接口,可以直接获取相应类型(audio or video)的流ID
获取流的每一帧数据(av_read_frame)
关闭文件
2.解码
解码在解封装的基础上,将每一帧数据进行解码。步骤如下:
申请解码器上下文AVCodecContext(avcodec_alloc_context3)
初始化AVCodecContext参数,可以使用将解封装得到的流的解码器参数设置进来(avcodec_parameters_to_context)
打开解码器(avcodec_open2)
解码每一帧数据。只需要将解封装获取的帧传递给解码器(avcodec_send_packet),再接收即可(avcodec_receive_frame)
关闭文件和解码器
源码分析
main 函数,简化版
1 |
|
验证输入参数是否正确
打开文件,读取封装信息
根据封装信息,分别创建视频解码器和音频解码器
循环从文件中读取pkt
如果是视频数据,送到视频解码器,解码后写入视频文件
如果是音频数据,送到音频解码器,解码后写入音频文件
冲洗音频视频解码器,读取剩余数据
清理资源退出
open_codec_context
1 |
|
获取AVStream
从AVStream读取解码信息,创建AVCodecContext
给AVCodecContext配置参数
打开解码器
decode_packet
1 |
|
调用avcodec_send_packet给解码器送pkt
调用avcodec_receive_frame从解码器读frame
调用output_video_frame将解码后视频写文件,调用output_audio_frame将解码后音频写文件
output_video_frame
1 |
|
判断解码后的宽高,格式是否变更,写入文件的宽高,格式应该是一致的,如果变更就报错
将frame中的数据拷贝到之前申请的buffer中,拷贝的时候,去除字节对齐数据
将buffer中的数据写入文件
output_audio_frame
1 |
|
将frame中的音频的第一个声道数据写入文件
如果想写入多声道,需要通过libswresample或libavfilter转为交错型的再写入文件