【FFmpeg视频播放器开发】解封装解码流程、常用API和结构体简介(一)

时间:2021-04-01 00:33:44   收藏:0   阅读:35

一、前言

在正式编写 FFmpeg 播放器前,我们需要先简单了解下所要用到的 FFmpeg 库、播放与解码流程、函数和相关结构体。

二、FFmpeg 库简介

介绍
avcodec 音视频编解码核心库
avformat 音视频容器格式的封装和解析
avutil 核心工具库
swscal 图像格式转换的模块
swresampel 音频重采样
avfilter 音视频滤镜库 如视频加水印、音频变声
avdevice 输入输出设备库,提供设备数据的输入与输出

FFmpeg 就是依靠以上几个库,实现了强大的音视频编码、解码、编辑、转换、采集等能力。这里实现视频播放就除了 avfilter 库没用到。

三、FFmpeg播放流程

通常情况下,视频文件如 MP4,MKV、FLV 等都属于封装格式,就是将已经压缩编码的视频数据和音频数据按照一定的格式放到一起。当我们播放一个媒体文件时,通常需要经过以下几个步骤:
技术分享图片


可以看到这个视频播放器的实现需要涉及到以下内容:

其中解码是最重要的,下面介绍一下解码的流程以及用到的 API 和结构体。

四、FFmpeg解码流程

技术分享图片

五、使用到的FFmpeg API说明

5.1 av_register_all()


5.2 avformat_alloc_context()

用于初始化 AVFormatContext 对象。其原型如下:

AVFormatContext *avformat_alloc_context(void)

5.3 avformat_open_input()

打开媒体文件,并获得解封装上下文。其原型如下:

int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options)

5.4 avformat_find_stream_info()

探测获取流信息。其原型如下:

int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)  

5.5 avcodec_find_decoder()

查找解码器。函数的参数是所要用解码器的ID,成功返回查找到的解码器(没有找到就返回 NULL)。其原型如下:

AVCodec *avcodec_find_decoder(enum AVCodecID id);

5.6 avcodec_open2()

用于初始化一个音视频编解码器的 AVCodecContext,声明位于 libavcodec\utils.c。其原型如下:

int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)  

5.7 av_read_frame()

读取码流中的音频若干帧或者视频一帧。例如,解码视频的时候,每解码一个视频帧,需要先调用av_read_frame()获得一帧视频的压缩数据,然后才能对该数据进行解码。其原型如下:

int av_read_frame(AVFormatContext *s, AVPacket *pkt)

5.8 avcodec_decode_video2()

解码一帧视频数据。输入一个压缩编码的结构体 AVPacket,输出一个解码后的结构体 AVFrame。其原型如下:

int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
                         int *got_picture_ptr,
                         const AVPacket *avpkt);

5.9 avformat_close_input()

关闭释放解封装上下文,并且设置为 0。其原型如下:

void avformat_close_input(AVFormatContext **s)

六、使用到的FFmpeg结构体说明

这些结构体之间的对应关系如下所示:

技术分享图片


6.1 AVFormatContext

解封装上下文,是存储音视频封装格式中包含信息的结构体。

char filename[1024] // 保存打开的文件名,一般用在 rtsp、rtmp 断开重连
unsigned int nb_streams // 音视频流的个数
AVStream **streams // 存储视频流、音频流、字幕流信息
int64_t duration // 媒体文件的总时长,单位是把 1 秒切成 AV_TIME_BASE(1000000)份,即单位。为 us,注意不一定每个视频都能获取到 duration
int64_t bit_rate // 比特率(单位bps,转换为kbps需要除以1000)

6.2 AVStream

AVStream 是存储每一个音频/视频流信息的结构体。其重要的变量如下所示:

int index // 标识该视频/音频流
AVCodecContext *codec // 解码器,4.0 版本后已弃用
AVRational time_base // 时基。通过该值可以把PTS,DTS转化为实际的时间(单位为秒s)
int64_t duration // 该视频/音频流时长,单位为 ms
AVRational avg_frame_rate // 帧率(注:对视频来说,这个挺重要的)
AVPacket attached_pic // 附带的图片。比如说一些 MP3,AAC 音频文件附带的专辑封面
AVCodecParameters *codecpar // 音视频参数,新增用来替换AVCodecContext *codec

6.3 AVCodecContext

AVCodecContext 是一个描述编解码器上下文的结构体,包含了众多编解码器需要的参数信息。下面挑一些关键的变量来看看(这里只考虑解码)。

enum AVMediaType codec_type // 编解码器的类型(视频,音频...)
struct AVCodec  *codec // 采用的解码器AVCodec(H.264,MPEG2...)    
enum AVCodecID codec_id // 标示特定的编解码器(H.264,MPEG2...)
int format // 视频像素格式/音频采样数据格式
int width, height // 表示视频的宽和高
int bit_rate // 平均比特率    
int channels // 声道数(音频)
uint64_t channel_layout // 声道格式
int sample_rate // 采样率(音频)
AVRational time_base; // 时基。通过该值可以把PTS,DTS转化为实际的时间(单位为秒s)
uint8_t *extradata; int extradata_size; // 针对特定编码器包含的附加信息(例如对于H.264解码器来说,存储SPS,PPS等)

6.4 AVCodec

AVCodec 是存储编码器信息的结构体。其重要的变量如下所示:

const char *name; // 编解码器的名字的简称
const char *long_name; // 编解码器名字的全称
enum AVMediaType type; // 指明了类型,是视频,音频,还是字幕
enum AVCodecID id; // ID,不重复
const AVRational *supported_framerates; // 支持的帧率(仅视频)
const enum AVPixelFormat *pix_fmts; // 支持的像素格式(仅视频),如RGB24、YUV420P等。
const int *supported_samplerates; // 支持的采样率(仅音频)
const enum AVSampleFormat *sample_fmts; // 支持的采样格式(仅音频)
const uint64_t *channel_layouts; // 支持的声道数(仅音频)
int priv_data_size; // 私有数据的大小

6.5 AVCodecParameters

新增用来替换AVCodecContext *codec因为 AVCodecContext 结构体包含的参数太多,AVCodecParameters 将编码器的参数从 AVCodecContext 分离出来,AVCodecParameters 结构体中部分重要的参数如下:

enum AVMediaType codec_type // 编解码器的类型(视频,音频...)   
enum AVCodecID codec_id // 标示特定的编解码器(H.264,MPEG2...)
int format // 视频像素格式/音频采样数据格式
int width, height // 表示视频的宽和高
int bit_rate // 平均比特率    
int channels // 声道数(音频)
uint64_t channel_layout // 声道格式
int sample_rate // 采样率(音频)
AVRational time_base; // 时基。通过该值可以把PTS,DTS转化为实际的时间(单位为秒s)
uint8_t *extradata; int extradata_size; // 针对特定编码器包含的附加信息(例如对于H.264解码器来说,存储SPS,PPS等)

可以看到两者的成员基本一致。


avcodec_decode_video2():解码一帧视频数据
sws_scale():转换视频数据格式    
av_frame_free():释放xx上下文申请的内存
avcodec_close():关闭解码器

6.6 AVPacket

AVPacket 是存储压缩编码数据相关信息的结构体。其重要的变量如下所示:

uint8_t *data; // 压缩编码的数据。
/* 例如对于H.264来说。1个AVPacket的data通常对应一个NAL。
注意:在这里只是对应,而不是一模一样。他们之间有微小的差别:使用FFMPEG类库分离出多媒体文件中的H.264码流。因此在使用FFMPEG进行音视频处理的时候,常常可以将得到的AVPacket的data数据直接写成文件,从而得到音视频的码流文件。*/
int size; // data的大小
int64_t pts; // 显示时间戳
int64_t dts; // 解码时间戳
int stream_index; // 标识该AVPacket所属的视频/音频流。

6.7 AVFrame

AVFrame 结构体一般用于存储原始数据(即非压缩数据,例如对视频来说是 YUV,RGB,对音频来说是 PCM),此外还包含了一些相关的信息。比如说,解码的时候存储了宏块类型表,QP 表,运动矢量表等数据。编码的时候也存储了相关的数据。因此在使用 FFmpeg 进行码流分析的时候,AVFrame 是一个很重要的结构体。

下面看几个主要变量的作用(在这里考虑解码的情况):

uint8_t *data[AV_NUM_DATA_POINTERS]; // 解码后原始数据(对视频来说是YUV,RGB,对音频来说是PCM)
int linesize[AV_NUM_DATA_POINTERS]; // data中“一行”数据的大小。注意:未必等于图像的宽,一般大于图像的宽。
int width, height; // 视频帧宽和高(1920x1080,1280x720...)
int nb_samples; // 音频的一个AVFrame中可能包含多个音频帧,在此标记包含了几个
int format; // 解码后原始数据类型(YUV420,YUV422,RGB24...)
int key_frame; // 是否是关键帧
enum AVPictureType pict_type; // 帧类型(I,B,P...)
AVRational sample_aspect_ratio; // 宽高比(16:9,4:3...)
int64_t pts; // 显示时间戳
int coded_picture_number; // 编码帧序号
int display_picture_number; // 显示帧序号

参考:

【雷霄骅大神 - 解码】

图解FFMPEG打开媒体的函数avformat_open_input

FFmpeg 源代码简单分析:avformat_open_input()

FFmpeg 源代码简单分析:avformat_find_stream_info()

FFmpeg 源代码简单分析:av_read_frame()

FFmpeg 源代码简单分析:avcodec_decode_video2()

FFmpeg 源代码简单分析:avformat_close_input()

【雷霄骅大神 - FFmpeg结构体】

FFMPEG中最关键的结构体之间的关系

FFMPEG结构体分析:AVFrame

FFMPEG结构体分析:AVFormatContext

FFMPEG结构体分析:AVCodecContext

FFMPEG结构体分析:AVIOContext

FFMPEG结构体分析:AVCodec

FFMPEG结构体分析:AVStream

FFMPEG结构体分析:AVPacket


原文:https://www.cnblogs.com/linuxAndMcu/p/14603442.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!