FFmpeg的基本概述(二)
目录
一、基本概念
二、 音视频解码流程解析
2.1音视频编解码基本概念
2.2 FFmpeg解码相关函数和数据结构解析
2.1初始化相关
2.2打开视频输入流
2.3获取视频文件信息
2.4 查找对应的解码器并打开
2.5 分配帧
2.6 创建图像缩放和颜色空间转换的上下文结构体swsCtx
2.7 解码相关
2.8 资源释放相关
2.9 错误输出相关
2.10 相关数据结构
一、基本概念
FFmpeg是一个开源的音视频处理框架,它提供了非常强大的库和命令行工具,用于录制、转换、流化以及播放几乎所有类型的音视频数据。FFmpeg拥有一个模块化的架构,主要分为几个部分:libavformat(处理音视频流的封装格式)、libavcodec(处理音视频编解码)、libavfilter(处理音视频数据的过滤)、libavutil(工具库)等。
架构上,FFmpeg通过一系列精心设计的数据结构来组织音视频数据,它支持几乎所有的编解码器(codecs),并且能够运行在多种操作系统和处理器架构之上。这种设计使得FFmpeg在处理音视频数据时具有极高的灵活性和可扩展性。
二、 音视频解码流程解析
2.1音视频编解码基本概念
音视频编解码流程包含编码和解码两个主要步骤:
编码 :编码过程是指将模拟信号(例如,来自麦克风或摄像头的信号)转换为数字信号,然后将数字信号通过压缩算法转换成更小的文件大小,以便存储和传输。这个过程涉及到数据的采样、量化、编码,以及可能的压缩。
解码 :解码过程则是编码的逆过程,目的是将压缩过的音视频数据流还原回人类可识别的音频和视频格式。解码器通常会逐帧或逐包处理数据,对数据进行解压缩、重建采样和量化值,最终输出原始的音视频流。
这里主要对解码流程进行研究,以下是 FFmpeg对视频的解析流程
av_register_all() //注册所有组件
|
avformat_open_input() //打开输入的视频文件
|
avformat_find_stream_info() //获取视频文件信息
|
avcodec_find_decoder() //查找对应的解码器
|
avcode_open2() //打开解码器
|
av_read_frame() //读取数据包
|
AVPacket
|
avcode_decode_video2() //解压一帧数据
|
AVFrame
|
sws_scale()
|
渲染绘制到设备
2.2 FFmpeg解码相关函数和数据结构解析
2.1初始化相关
av_register_all() //: 注册所有组件
avformat_network_init();//初始化 FFmpeg 的网络支持,适用于通过网络协议(如 RTSP、HTTP 等)获取媒体数据的场景
2.2打开视频输入流
/**
* Open an input stream and read the header. The codecs are not opened.
* The stream must be closed with avformat_close_input().
*
* @param ps Pointer to user-supplied AVFormatContext (allocated by
* avformat_alloc_context). May be a pointer to NULL, in
* which case an AVFormatContext is allocated by this
* function and written into ps.
* Note that a user-supplied AVFormatContext will be freed
* on failure.
* @param url URL of the stream to open.
* @param fmt If non-NULL, this parameter forces a specific input format.
* Otherwise the format is autodetected.
* @param options A dictionary filled with AVFormatContext and demuxer-private
* options.
* On return this parameter will be destroyed and replaced with
* a dict containing options that were not found. May be NULL.
*
* @return 0 on success, a negative AVERROR on failure.
*
* @note If you want to use custom IO, preallocate the format context and set its pb field.
*///fmtCtx传入传出参数记录文件信息 文件地址(URL或本地地址) 指定格式()
int avformat_open_input(AVFormatContext **ps, const char *url,
const AVInputFormat *fmt, AVDictionary **options);
2.3获取视频文件信息
/**
* Read packets of a media file to get stream information. This
* is useful for file formats with no headers such as MPEG. This
* function also computes the real framerate in case of MPEG-2 repeat
* frame mode.
* The logical file position is not changed by this function;
* examined packets may be buffered for later processing.
*
* @param ic media file handle
* @param options If non-NULL, an ic.nb_streams long array of pointers to
* dictionaries, where i-th member contains options for
* codec corresponding to i-th stream.
* On return each dictionary will be filled with options that were not found.
* @return >=0 if OK, AVERROR_xxx on error
*
* @note this function isn't guaranteed to open all the codecs, so
* options being non-empty at return is a perfectly normal behavior.
*
* @todo Let the user decide somehow what information is needed so that
* we do not waste time getting stuff the user does not need.
*///获取媒体文件中的流信息。该函数会解析文件并填充 AVFormatContext 结构体中的 streams 字段
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
2.4 查找对应的解码器并打开
/**
* Find a registered decoder with a matching codec ID.
*
* @param id AVCodecID of the requested decoder
* @return A decoder if one was found, NULL otherwise.
*/
const AVCodec *avcodec_find_decoder(enum AVCodecID id);打开解码器
const AVCodec *videoCodec = avcodec_find_decoder(videoStream->codecpar->codec_id);
//分配一个新的编解码上下文(AVCodecContext)
AVCodecContext *videoCodecCtx = avcodec_alloc_context3(videoCodec);
//编解码参数复制到编解码Contex
avcodec_parameters_to_context(videoCodecCtx, videoStream->codecpar)// 打开解码器
avcodec_open2(videoCodecCtx, videoCodec, nullptr)
2.5 分配帧
AVFrame *YuvFrame = av_frame_alloc();
AVFrame *RgbFrame = av_frame_alloc();//计算存储原始图像数据所需的最小缓冲区大小pix_fmt:像素格式(如 AV_PIX_FMT_YUV420P)。width:图像宽度。height:图像高度。align:缓冲区对齐方式(通常为 1 或 32)。
int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, videoCodecCtx->width, videoCodecCtx->height, 1);
buffer = (uint8_t*)av_malloc(numBytes * sizeof(uint8_t));
av_image_fill_arrays(RgbFrame->data, RgbFrame->linesize, buffer, AV_PIX_FMT_RGB24, videoCodecCtx->width, videoCodecCtx->height, 1);
2.6 创建图像缩放和颜色空间转换的上下文结构体swsCtx
由 sws_getContext() 创建。它保存缩放算法、源/目标格式、尺寸等信息,供 sws_scale() 函数使用,完成视频帧的尺寸调整或像素格式转换。处理完成后需通过 sws_freeContext() 释放资源。
/**
* Allocate and return an SwsContext. You need it to perform
* scaling/conversion operations using sws_scale().
*
* @param srcW the width of the source image
* @param srcH the height of the source image
* @param srcFormat the source image format
* @param dstW the width of the destination image
* @param dstH the height of the destination image
* @param dstFormat the destination image format
* @param flags specify which algorithm and options to use for rescaling
* @param param extra parameters to tune the used scaler
* For SWS_BICUBIC param[0] and [1] tune the shape of the basis
* function, param[0] tunes f(1) and param[1] f´(1)
* For SWS_GAUSS param[0] tunes the exponent and thus cutoff
* frequency
* For SWS_LANCZOS param[0] tunes the width of the window function
* @return a pointer to an allocated context, or NULL in case of error
* @note this function is to be removed after a saner alternative is
* written
*/
struct SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat,
int dstW, int dstH, enum AVPixelFormat dstFormat,
int flags, SwsFilter *srcFilter,
SwsFilter *dstFilter, const double *param);
2.7 解码相关
int av_read_frame(AVFormatContext *s, AVPacket *pkt);从输入媒体文件中读取单个音频或视频帧数据包(AVPacket)。该函数成功时返回 0,失败时返回负的错误码。读取到的数据包可送入解码器进行解码,读取完毕后需使用 av_packet_unref 释放
int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *pkt); 将数据包送入解码器
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame); 从解码器读取解码后的帧,无帧可取时返回AVERROR(EAGAIN)
或AVERROR_EOF
。必须与avcodec_send_packet
配合使用,获取帧后需调用av_frame_unref
释放。//将解码后的YUVFrame转换为RGBFrame便于后续显示
int ret = sws_scale(swsCtx, YuvFrame->data, YuvFrame->linesize, 0,YuvFrame->height, RgbFrame->data, RgbFrame->linesize);
2.8 资源释放相关
av_frame_free(AVFrame **frame):释放帧并置指针为 NULL。
av_packet_free(AVPacket **pkt):释放数据包并置指针为 NULL。
av_free(void *ptr):释放由 FFmpeg 分配的内存(如 av_malloc 分配的内存)。
avcodec_close(AVCodecContext *ctx):关闭解码器上下文(建议使用 avcodec_free_context)。
avcodec_free_context(AVCodecContext **ctx):释放并置上下文指针为 NULL。
avformat_close_input(AVFormatContext **ctx):关闭输入文件并置指针为 NULL。
avio_closep(AVIOContext **s):关闭并置 AVIO 上下文指针为 NULL。
sws_freeContext(SwsContext *ctx):释放图像缩放上下文。
swr_close(SwrContext *ctx):关闭音频重采样上下文(建议使用 swr_free)。
swr_free(SwrContext **ctx):释放音频重采样上下文并置指针为 NULL。
2.9 错误输出相关
av_strerror 将错误码转为字符串描述
av_log 输出日志信息,通常配合 AV_LOG_ERROR 等级别使用。可调用 av_log_set_level 设置日志等级。
2.10 相关数据结构
AVFormatContext *fmtCtx = nullptr;//封装格式上下文结构体,保存文件封装格式相关信息,用于存储媒体文件的信息和状态
AVStream *videoStream = nullptr;//多媒体流(如视频流或音频流)的信息,每个音视频流对应一个
AVCodecContext *videoCodecCtx = nullptr;//编解码上下文结构体,存储音视频编解码器的配置信息;配置编解码器的各种参数,比特率、帧率、分辨率等
.codec 编解码器的AVCodec
.width height 图像宽高(只针对视频)
.pix_fmt:像素格式(只针对视频)
.sample_rate:采样率(音频)
.sample_fmt 采样格式(音频)
AVCodec *avCoderc = nullptr; //每种视频扁家吗器(如H.264解码器)对用一个结构体
AVPacket packet;//用于存储一阵编码压缩后的数据包,包含音视频数据、时间戳、数据指针和数据大小等信息
.pts 显示时间戳
.dts 解码时间戳
.data 压缩编码数据
.size 压缩编码数据大小
.stream_index 所属流AVStream的索引
AVFrame *YuvFrame = nullptr;//存储一帧解码后像素数据
.data 解码后数据
.Linesize 对视频图像中一行像素的代下 音频:整个音频帧的大小
.width heigth 图像的宽高
.key_frame 是否为关键帧(只针对视频)
.pict_type 帧类型(只针对视频 I,P,B)
SwsContext *swsCtx = nullptr;//存储图像缩放和颜色空间转换的相关信息