本节主要讲AVPacket中的数据解码到AVFrame中的过程。
AVFormatContext:封装格式上下文结构体,也是统领全局的结构体,保存了视频文件封装格式相关信息
AVInputFormat:每种封装格式对应一个该结构体
AVStream:视频文件每个视频(音频)流对应一个该结构体
AVCodecContext:编码器上下文结构体,保存了视频(音频)编解码相关信息
AVCodec:每种视频(音频)编解码器对应一个该结构体
AVPacket:存储一帧压缩编码数据
AVFrame:存储一帧解码后像素(采样)数据
AVCodecContext中持有一个AVCodecInternal变量,保存了AVCodecContext的各种状态信息,详细如下:
/**
* libavcodec->internal.h l:132
*
*/
typedef struct AVCodecInternal {
/**
* parent AVCodecContext 是否为使用init()调用自己的
*
* 多线程使用 - 共享表和图片指针应仅从原始上下文中释放。
*/
int is_copy;
/**
* 是否为帧线程分配进度。
*
* 如果编解码器使用ff_thread_await / report_progress(),则必须将其设置为1,
* 然后将在ff_thread_get_buffer()中分配进度。
* 然后必须使用ff_thread_release_buffer()释放帧。
*
* 如果编解码器不需要调用进度函数(帧之间没有依赖关系),它应该将其保留为0.
* 然后它可以直接解码到用户提供的帧(然后用户将使用av_frame_unref()释放它们 ),
* 没有必要调用ff_thread_release_buffer()。
*/
int allocate_progress;
/**
* 提交了少于所需样本的音频帧并用静音填充。 拒绝所有后续帧。
*/
int last_audio_frame;
AVFrame *to_free;
FramePool *pool;
void *thread_ctx;
DecodeSimpleContext ds;
DecodeFilterContext filter;
/**
* 从传递用于解码的最后一个数据包中提取的属性(时间戳+辅助数据)。
*/
AVPacket *last_pkt_props;
/**
* 用于编码器存储其比特流的临时缓冲区
*/
uint8_t *byte_buffer;
unsigned int byte_buffer_size;
void *frame_thread_encoder;
/**
* 在下一个解码帧开始时跳过的音频样本数
*/
int skip_samples;
/**
* hwaccel-specific private data
* 硬件加速专有的私有数据
*/
void *hwaccel_priv_data;
/**
* 检查API使用情况:编解码器耗尽后,需要刷新才能恢复操作
*/
int draining;
/**
* buffers for using new encode/decode API through legacy API
* 缓冲区,用于通过旧API使用新的编码/解码API
*/
AVPacket *buffer_pkt;
int buffer_pkt_valid; // encoding: packet without data can be valid
AVFrame *buffer_frame;
int draining_done;
/**
* set to 1 when the caller is using the old decoding API
* 当调用者使用旧的解码API时,设置为1
*/
int compat_decode;
int compat_decode_warned;
/**
* this variable is set by the decoder internals to signal to the old
* API compat wrappers the amount of data consumed from the last packet
* 此变量由解码器内部设置,以向使用旧API的compat wrappers发送最后一个数据包中消耗的数据量
*/
size_t compat_decode_consumed;
/**
* when a partial packet has been consumed, this stores the remaining size
* of the packet (that should be submitted in the next decode call
* 当消耗了部分数据包时,它会存储数据包的剩余大小(应该在下一个解码调用中提交)
*/
size_t compat_decode_partial_size;
AVFrame *compat_decode_frame;
int showed_multi_packet_warning;
int skip_samples_multiplier;
/**
* to prevent infinite loop on errors when draining
* 清空缓存数据时(draining)防止出现无限循环错误
*/
int nb_draining_errors;
} AVCodecInternal;
typedef struct DecodeFilterContext {
AVBSFContext **bsfs;
int nb_bsfs;
} DecodeFilterContext;
编解码器的结构体,详细如下:
/**
* AVCodec.
*/
typedef struct AVCodec {
/**
* Name of the codec implementation.
* The name is globally unique among encoders and among decoders (but an
* encoder and a decoder can share the same name).
* This is the primary way to find a codec from the user perspective.
* 编解码器实现的名称。
* 该名称在编码器和解码器之间是全局唯一的(但编码器和解码器可以共享相同的名称)。
* 这是从用户角度查找编解码器的主要方法。
*/
const char *name;
/**
* Descriptive name for the codec, meant to be more human readable than name.
* You should use the NULL_IF_CONFIG_SMALL() macro to define it.
* 编解码器的描述性名称,意味着比名称更易于阅读。
* 应该使用NULL_IF_CONFIG_SMALL()宏来定义它。
*/
const char *long_name;
enum AVMediaType type;
enum AVCodecID id;
/**
* Codec capabilities.
* 编解码器功能。
*
* see AV_CODEC_CAP_*
*/
int capabilities;
const AVRational *supported_framerates; ///< array of supported framerates, or NULL if any, array is terminated by {0,0}
const enum AVPixelFormat *pix_fmts; ///< array of supported pixel formats, or NULL if unknown, array is terminated by -1
const int *supported_samplerates; ///< array of supported audio samplerates, or NULL if unknown, array is terminated by 0
const enum AVSampleFormat *sample_fmts; ///< array of supported sample formats, or NULL if unknown, array is terminated by -1
const uint64_t *channel_layouts; ///< array of support channel layouts, or NULL if unknown. array is terminated by 0
uint8_t max_lowres; ///< maximum value for lowres supported by the decoder
const AVClass *priv_class; ///< AVClass for the private context
const AVProfile *profiles; ///< array of recognized profiles, or NULL if unknown, array is terminated by {FF_PROFILE_UNKNOWN}
/*****************************************************************
* No fields below this line are part of the public API. They
* may not be used outside of libavcodec and can be changed and
* removed at will.
* New public fields should be added right above.
*****************************************************************
*/
int priv_data_size;
struct AVCodec *next;
/**
* @name Frame-level threading support functions 帧级线程支持功能
* @{
*/
/**
* If defined, called on thread contexts when they are created.
* If the codec allocates writable tables in init(), re-allocate them here.
* priv_data will be set to a copy of the original.
* 如果已定义,则在创建时调用线程上下文。
* 如果编解码器在init()中分配可写表,请在此处重新分配它们。
* priv_data将设置为原始副本。
*/
int (*init_thread_copy)(AVCodecContext *);
/**
* Copy necessary context variables from a previous thread context to the current one.
* If not defined, the next thread will start automatically; otherwise, the codec
* must call ff_thread_finish_setup().
* 将必要的上下文变量从先前的线程上下文复制到当前的线程上下
* 如果未定义,则下一个线程将自动启动; 否则,编解码器必须调用ff_thread_finish_setup()。
*
* dst and src will (rarely) point to the same context, in which case memcpy should be skipped.
* dst和src将(很少)指向相同的上下文,在这种情况下应该跳过memcpy。
*/
int (*update_thread_context)(AVCodecContext *dst, const AVCodecContext *src);
/** @} */
/**
* Private codec-specific defaults.
*/
const AVCodecDefault *defaults;
/**
* Initialize codec static data, called from avcodec_register().
*/
void (*init_static_data)(struct AVCodec *codec);
int (*init)(AVCodecContext *);
int (*encode_sub)(AVCodecContext *, uint8_t *buf, int buf_size,
const struct AVSubtitle *sub);
/**
* Encode data to an AVPacket.
*
* @param avctx codec context
* @param avpkt output AVPacket (may contain a user-provided buffer)
* @param[in] frame AVFrame containing the raw data to be encoded
* @param[out] got_packet_ptr encoder sets to 0 or 1 to indicate that a
* non-empty packet was returned in avpkt.
* @return 0 on success, negative error code on failure
*/
int (*encode2)(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame,
int *got_packet_ptr);
int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, AVPacket *avpkt);
int (*close)(AVCodecContext *);
/**
* Encode API with decoupled packet/frame dataflow. The API is the
* same as the avcodec_ prefixed APIs (avcodec_send_frame() etc.), except
* that:
* - never called if the codec is closed or the wrong type,
* - if AV_CODEC_CAP_DELAY is not set, drain frames are never sent,
* - only one drain frame is ever passed down,
*/
int (*send_frame)(AVCodecContext *avctx, const AVFrame *frame);
int (*receive_packet)(AVCodecContext *avctx, AVPacket *avpkt);
/**
* Decode API with decoupled packet/frame dataflow. This function is called
* to get one output frame. It should call ff_decode_get_packet() to obtain
* input data.
*/
int (*receive_frame)(AVCodecContext *avctx, AVFrame *frame);
/**
* Flush buffers.
* Will be called when seeking
*/
void (*flush)(AVCodecContext *);
/**
* Internal codec capabilities.
* See FF_CODEC_CAP_* in internal.h
*/
int caps_internal;
/**
* Decoding only, a comma-separated list of bitstream filters to apply to
* packets before decoding.
*/
const char *bsfs;
} AVCodec;
由于作者也在学习阶段,本系列文章的规划路线也是参照了雷神总结的视频播放流程图。而本篇文章的起点也就是avcodec_decode_video2。所以,让我们从avcodec_decode_video2开始一步一步分析吧~
首先来看一下avcodec_decode_video2的注释和实现:
/**
* 将大小为avpkt-> size的视频帧从avpkt-> data解码为picture.
* 一些解码器可以在单个AVPacket中支持多个帧,这样解码器就可以解码第一帧。
*
* @warning 输入缓冲区大小必须大于实际读取比特数(AV_INPUT_BUFFER_PADDING_SIZE),
* 因为一些优化过的比特流读取器一次可以读取32bits或64bits,可能会超过边界
*
* @warning 输入缓冲区buf的结尾应设置为0,以确保对损坏的MPEG流不会发生过度读取。
*
* @note 具有AV_CODEC_CAP_DELAY功能集的编解码器在输入和输出之间具有延迟,
* 读取一帧后需要将avpkt-> data = NULL,avpkt-> size = 0,来返回剩余的帧。
*
* @note 在将数据包馈送到解码器之前,必须使用@ref avcodec_open2()打开AVCodecContext。
*
* @param avctx the codec context
* @param[out] picture 将存储解码视频帧的AVFrame。
* 使用av_frame_alloc()获取AVFrame。
* 编解码器将通过调用AVCodecContext.get_buffer2()回调为实际位图分配内存。
*
* 当AVCodecContext.refcounted_frames设置为1时,帧被引用计数,返回的引用属于调用者。
* 当不再需要帧时,调用者必须使用av_frame_unref()释放帧。
* 如果av_frame_is_writable()返回1,则调用者可以安全地写入帧。
*
* 当AVCodecContext.refcounted_frames设置为0时,返回的引用属于解码器,仅在下次调用此函数或关闭/刷新解码器之前有效。
* 调用这可能不会写入数据。
*
* @param[in] avpkt 输入AVPacket包含了输入缓冲区。
* 你可以使用av_init_packet()创建此类数据包,然后设置数据和大小,
* 某些解码器可能还需要其他字段,如flags和AV_PKT_FLAG_KEY。
* 所有解码器都设计为使用尽可能少的字段。
*
* @param[in,out] got_picture_ptr 如果没有帧可以解压缩则为零,否则为非零。
*
* @return 出错时,返回负值,否则使用的字节数,如果没有帧可以解压缩,则返回零。
*
* @deprecated Use avcodec_send_packet() and avcodec_receive_frame().
*/
attribute_deprecated
int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
int *got_picture_ptr,
const AVPacket *avpkt);
int attribute_align_arg avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
int *got_picture_ptr,
const AVPacket *avpkt)
{
return compat_decode(avctx, picture, got_picture_ptr, avpkt);
}
static int compat_decode(AVCodecContext *avctx, AVFrame *frame,
int *got_frame, const AVPacket *pkt)
{
// 取出AVCodecInternal结构体
AVCodecInternal *avci = avctx->internal;
int ret = 0;
// compat_decode_consumed:此变量由解码器内部设置,以向使用旧API的compat wrappers发送最后一个数据包中消耗的数据量
av_assert0(avci->compat_decode_consumed == 0);
// draining_done:缓冲数据是否耗尽
if (avci->draining_done && pkt && pkt->size != 0) {
av_log(avctx, AV_LOG_WARNING, "Got unexpected packet after EOF\n");
avcodec_flush_buffers(avctx);
}
// 标志:是否获取到frame
*got_frame = 0;
// 当调用者使用旧的解码API时,设置为1
avci->compat_decode = 1;
// compat_decode_partial_size:当消耗了部分数据包时,它会存储数据包的剩余大小(应该在下一个解码调用中提交)
if (avci->compat_decode_partial_size > 0 &&
avci->compat_decode_partial_size != pkt->size) {
av_log(avctx, AV_LOG_ERROR,
"Got unexpected packet size after a partial decode\n");
ret = AVERROR(EINVAL);
goto finish;
}
if (!avci->compat_decode_partial_size) {
// 调用新API:avcodec_send_packet -> 提供原始packet->data数据作为解码器的输入。
// 具体返回值的含义,详见文中avcodec_send_packet()函数的讲解
ret = avcodec_send_packet(avctx, pkt);
if (ret == AVERROR_EOF)
ret = 0;
else if (ret == AVERROR(EAGAIN)) {
/* we fully drain all the output in each decode call, so this should not
* ever happen */
ret = AVERROR_BUG;
goto finish;
} else if (ret < 0)
goto finish;
}
while (ret >= 0) {
// 调用新API:avcodec_receive_frame -> 从解码器返回解码的输出数据。
// 具体返回值的含义,详见文中avcodec_receive_frame()函数的讲解
ret = avcodec_receive_frame(avctx, frame);
if (ret < 0) {
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
ret = 0;
goto finish;
}
if (frame != avci->compat_decode_frame) {
// frame为引用计数的帧
if (!avctx->refcounted_frames) {
ret = unrefcount_frame(avci, frame);
if (ret < 0)
goto finish;
}
*got_frame = 1;
frame = avci->compat_decode_frame;
} else {
if (!avci->compat_decode_warned) {
av_log(avctx, AV_LOG_WARNING, "The deprecated avcodec_decode_* "
"API cannot return all the frames for this decoder. "
"Some frames will be dropped. Update your code to the "
"new decoding API to fix this.\n");
avci->compat_decode_warned = 1;
}
}
// codec->bsfs:仅解码,在解码之前应用于数据包的,使用逗号分隔的,比特流过滤器列表。
// 解码数据耗尽 或 (bsfs列表不为空 且 已消耗数据小于packet的数据)
if (avci->draining || (!avctx->codec->bsfs && avci->compat_decode_consumed < pkt->size))
break;
}
finish:
if (ret == 0) {
/**
* if there are any bsfs then assume full packet is always consumed
* 如果有任何bsfs,则假设始终消耗完整数据包
*/
if (avctx->codec->bsfs)
ret = pkt->size;
else
ret = FFMIN(avci->compat_decode_consumed, pkt->size);
}
avci->compat_decode_consumed = 0;
avci->compat_decode_partial_size = (ret >= 0) ? pkt->size - ret : 0;
return ret;
}
在这里我们发现,avcodec_decode_video2这个函数已经被废弃掉了,新的函数为avcodec_send_packet() 和 avcodec_receive_frame(),而新的avcodec_decode_video2也是通过调用这两个函数来实现的。那么接下来我们来看看这两个函数
/**
* 提供原始packet->data数据作为解码器的输入。
*
* 在内部,此调用将复制相关的AVCodecContext字段,这些字段可影响每个数据包的解码,并在数据包实际解码时应用它们。
* (例如AVCodecContext.skip_frame,它可能指示解码器丢弃使用此函数发送的数据包所包含的帧。)
*
* @warning 输入缓冲区大小必须大于实际读取比特数(AV_INPUT_BUFFER_PADDING_SIZE),
* 因为一些优化过的比特流读取器一次可以读取32bits或64bits,可能会超过边界
*
* @warning 请勿在同一AVCodecContext上将此API与旧API(如avcodec_decode_video2())混合使用。
* 它将在现在或未来的libavcodec版本中返回意外结果。
*
* @note 在将数据包馈送到解码器之前,必须使用@ref avcodec_open2()打开AVCodecContext。
*
* @param avctx codec context
* @param[in] avpkt 输入AVPacket。 通常,这将是单个视频帧,或几个完整的音频帧。
* 数据包的所有权保留在调用者处,解码器不会写入数据包。
* 解码器可以创建对分组数据的引用(或者没有被分组技术,则复制它)。
*
* 与较旧的API不同,数据包总是被完全消耗,如果它包含多个帧(例如一些音频编解码器),
* 则需要在发送新数据包之前多次调用avcodec_receive_frame()。
*
* 它可以是NULL(或者数据设置为NULL且大小设置为0的AVPacket);
* 在这种情况下,它被认为是一个刷新数据包,它向流的结束发出信号。
* 发送第一个flush数据包将返回成功。 后续的是不必要的,将返回AVERROR_EOF。
* 如果解码器仍有缓冲帧,则在发送刷新数据包后将返回它们。
*
* @return 0 on success, otherwise negative error code:
* AVERROR(EAGAIN): 在当前状态下不接受输入 - 用户必须使用avcodec_receive_frame()读取输出
* (读取所有输出后,应重新发送数据包,EAGAIN下调用不会失败)。
*
* AVERROR_EOF: 已刷新解码器,并且不会向其发送新数据包(如果发送的数据包超过1个,也会返回)
*
* AVERROR(EINVAL): 1.编解码器未打开。
* 2.它是编码器
* 3.需要刷新
*
* AVERROR(ENOMEM): 无法将数据包添加到内部队列或类似的其他错误: legitimate decoding errors
*/
int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt)
{
// 取出AVCodecInternal结构体
AVCodecInternal *avci = avctx->internal;
int ret;
// avcodec_is_open(avctx):如果avctx打开则返回正值(即,在没有相应的avcodec_close()的情况下调用avcodec_open2()),否则返回0
// av_codec_is_decoder(avctx->codec):如果avctx->codec是解码器,则返回非零数字,否则返回零
if (!avcodec_is_open(avctx) || !av_codec_is_decoder(avctx->codec))
return AVERROR(EINVAL);
// 检查API使用情况:编解码器耗尽后,需要刷新才能恢复操作
if (avctx->internal->draining)
return AVERROR_EOF;
// packet中是否有数据
if (avpkt && !avpkt->size && avpkt->data)
return AVERROR(EINVAL);
// 初始化avci的DecodeFilterContext
ret = bsfs_init(avctx);
if (ret < 0)
return ret;
av_packet_unref(avci->buffer_pkt);
// 绑定avpkt中的数据
if (avpkt && (avpkt->data || avpkt->side_data_elems)) {
ret = av_packet_ref(avci->buffer_pkt, avpkt);
if (ret < 0)
return ret;
}
// core:将AVPacket中的数据发送给avci->filter.bsfs[0]
ret = av_bsf_send_packet(avci->filter.bsfs[0], avci->buffer_pkt);
if (ret < 0) {
av_packet_unref(avci->buffer_pkt);
return ret;
}
if (!avci->buffer_frame->buf[0]) {
// 解码
ret = decode_receive_frame_internal(avctx, avci->buffer_frame);
if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
return ret;
}
return 0;
}
代码中我们可以看到,通过av_bsf_send_packet发送数据,通过decode_receive_frame_internal解码数据,接下来看这两个函数
/**
* libavcodec->bsf.c
*
* 调用av_packet_move_ref
*/
int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt)
{
if (!pkt || (!pkt->data && !pkt->side_data_elems)) {
ctx->internal->eof = 1;
return 0;
}
if (ctx->internal->eof) {
av_log(ctx, AV_LOG_ERROR, "A non-NULL packet sent after an EOF.\n");
return AVERROR(EINVAL);
}
if (ctx->internal->buffer_pkt->data ||
ctx->internal->buffer_pkt->side_data_elems)
return AVERROR(EAGAIN);
// 将pkt中的data复制到ctx->internal->buffer_pkt中,并清空pkt
av_packet_move_ref(ctx->internal->buffer_pkt, pkt);
return 0;
}
/**
* Move every field in src to dst and reset src.
* 将src中的每个字段移动到dst并重置src。
*
* @see av_packet_unref
*
* @param src Source packet, will be reset
* @param dst Destination packet
*/
void av_packet_move_ref(AVPacket *dst, AVPacket *src);
void av_packet_move_ref(AVPacket *dst, AVPacket *src)
{
*dst = *src;
av_init_packet(src);
src->data = NULL;
src->size = 0;
}
这里完成了将AVPacket中的数据放入了指定的AVCodecContext->AVCodecInternal->DecodeFilterContext->AVBSFContext->AVPacket中
static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame)
{
AVCodecInternal *avci = avctx->internal;
int ret;
av_assert0(!frame->buf[0]);
// 如果指定了解码方式,则使用codec->receive_frame;否则使用decode_simple_receive_frame
if (avctx->codec->receive_frame)
ret = avctx->codec->receive_frame(avctx, frame);
else
ret = decode_simple_receive_frame(avctx, frame);
if (ret == AVERROR_EOF)
avci->draining_done = 1;
return ret;
}
解码数据时,根据是否指定了解码方式,分为调用receive_frame和调用decode_simple_receive_frame两种方式:
/**
* Decode API with decoupled packet/frame dataflow. This function is called
* to get one output frame. It should call ff_decode_get_packet() to obtain
* input data.
* 使用解耦的数据包/帧数据流解码API,调用此函数以获取一个输出帧。
* 它应该调用ff_decode_get_packet()来获取输入数据。
*/
int (*receive_frame)(AVCodecContext *avctx, AVFrame *frame);
// 以crystalhd.c为例
static inline CopyRet receive_frame(AVCodecContext *avctx,
AVFrame *frame, int *got_frame)
{
BC_STATUS ret;
BC_DTS_PROC_OUT output = {
.PicInfo.width = avctx->width,
.PicInfo.height = avctx->height,
};
CHDContext *priv = avctx->priv_data;
HANDLE dev = priv->dev;
*got_frame = 0;
// Request decoded data from the driver
ret = DtsProcOutputNoCopy(dev, OUTPUT_PROC_TIMEOUT, &output);
if (ret == BC_STS_FMT_CHANGE) {
av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: Initial format change\n");
avctx->width = output.PicInfo.width;
avctx->height = output.PicInfo.height;
switch ( output.PicInfo.aspect_ratio ) {
case vdecAspectRatioSquare:
avctx->sample_aspect_ratio = (AVRational) { 1, 1};
break;
...
}
return RET_COPY_AGAIN;
} else if (ret == BC_STS_SUCCESS) {
int copy_ret = -1;
if (output.PoutFlags & BC_POUT_FLAGS_PIB_VALID) {
print_frame_info(priv, &output);
copy_ret = copy_frame(avctx, &output, frame, got_frame);
} else {
/*
* An invalid frame has been consumed.
*/
av_log(avctx, AV_LOG_ERROR, "CrystalHD: ProcOutput succeeded with "
"invalid PIB\n");
copy_ret = RET_COPY_AGAIN;
}
DtsReleaseOutputBuffs(dev, NULL, FALSE);
return copy_ret;
} else if (ret == BC_STS_BUSY) {
return RET_COPY_AGAIN;
} else {
av_log(avctx, AV_LOG_ERROR, "CrystalHD: ProcOutput failed %d\n", ret);
return RET_ERROR;
}
}
decode_simple_receive_frame中直接调用了decode_simple_internal
static int decode_simple_receive_frame(AVCodecContext *avctx, AVFrame *frame)
{
int ret;
while (!frame->buf[0]) {
ret = decode_simple_internal(avctx, frame);
if (ret < 0)
return ret;
}
return 0;
}
/*
* The core of the receive_frame_wrapper for the decoders implementing
* the simple API. Certain decoders might consume partial packets without
* returning any output, so this function needs to be called in a loop until it
* returns EAGAIN.
* receive_frame_wrapper的核心,用于实现简单API的解码器。
* 某些解码器可能会消耗部分数据包而不返回任何输出,因此需要在循环中调用此函数,直到它返回EAGAIN。
**/
static int decode_simple_internal(AVCodecContext *avctx, AVFrame *frame)
{
AVCodecInternal *avci = avctx->internal;
DecodeSimpleContext *ds = &avci->ds;
AVPacket *pkt = ds->in_pkt;
// copy to ensure we do not change pkt
AVPacket tmp;
int got_frame, actual_got_frame, did_split;
int ret;
if (!pkt->data && !avci->draining) {
av_packet_unref(pkt);
// 获取av_bsf_send_packet时复制的AVPacket数据
ret = ff_decode_get_packet(avctx, pkt);
if (ret < 0 && ret != AVERROR_EOF)
return ret;
}
// Some codecs (at least wma lossless) will crash when feeding drain packets
// after EOF was signaled.
if (avci->draining_done)
return AVERROR_EOF;
if (!pkt->data &&
!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY ||
avctx->active_thread_type & FF_THREAD_FRAME))
return AVERROR_EOF;
tmp = *pkt;
got_frame = 0;
if (HAVE_THREADS && avctx->active_thread_type & FF_THREAD_FRAME) {
/**
* Submit a new frame to a decoding thread.
* Returns the next available frame in picture. *got_picture_ptr
* will be 0 if none is available.
* The return value on success is the size of the consumed packet for
* compatibility with avcodec_decode_video2(). This means the decoder
* has to consume the full packet.
*
* 将新帧提交到解码线程。返回图片中的下一个可用帧。 如果没有,*got_picture_ptr将为0。
* 成功的返回值是与avcodec_decode_video2()兼容的消耗数据包的大小,这意味着解码器必须使用完整的数据包。
*
* Parameters are the same as avcodec_decode_video2().
*/
ret = ff_thread_decode_frame(avctx, frame, &got_frame, &tmp);
} else {
// 调用AVCodec的decode
ret = avctx->codec->decode(avctx, frame, &got_frame, &tmp);
...(internal、pkt_pos、sample_aspect_ratio、width、height、pix_fmt等)
}
emms_c();
actual_got_frame = got_frame;
if (avctx->codec->type == AVMEDIA_TYPE_VIDEO) {
// Video
if (frame->flags & AV_FRAME_FLAG_DISCARD)
got_frame = 0;
if (got_frame)
frame->best_effort_timestamp = guess_correct_pts(avctx,
frame->pts,
frame->pkt_dts);
} else if (avctx->codec->type == AVMEDIA_TYPE_AUDIO) {
// Audio
uint8_t *side;
int side_size;
uint32_t discard_padding = 0;
uint8_t skip_reason = 0;
uint8_t discard_reason = 0;
... (sample、sample_rate、channels、额外数据等)
}
...
if (!got_frame)
av_frame_unref(frame);
if (ret >= 0 && avctx->codec->type == AVMEDIA_TYPE_VIDEO && !(avctx->flags & AV_CODEC_FLAG_TRUNCATED))
ret = pkt->size;
/* do not stop draining when actual_got_frame != 0 or ret < 0 */
/* got_frame == 0 but actual_got_frame != 0 when frame is discarded */
if (avctx->internal->draining && !actual_got_frame) {
if (ret < 0) {
/* prevent infinite loop if a decoder wrongly always return error on draining */
/* reasonable nb_errors_max = maximum b frames + thread count */
int nb_errors_max = 20 + (HAVE_THREADS && avctx->active_thread_type & FF_THREAD_FRAME ?
avctx->thread_count : 1);
if (avci->nb_draining_errors++ >= nb_errors_max) {
av_log(avctx, AV_LOG_ERROR, "Too many errors when draining, this is a bug. "
"Stop draining and force EOF.\n");
avci->draining_done = 1;
ret = AVERROR_BUG;
}
} else {
avci->draining_done = 1;
}
}
avci->compat_decode_consumed += ret;
// 数据draining,取消引用;数据未用完,更新参数
if (ret >= pkt->size || ret < 0) {
av_packet_unref(pkt);
} else {
int consumed = ret;
pkt->data += consumed;
pkt->size -= consumed;
avci->last_pkt_props->size -= consumed; // See extract_packet_props() comment.
pkt->pts = AV_NOPTS_VALUE;
pkt->dts = AV_NOPTS_VALUE;
avci->last_pkt_props->pts = AV_NOPTS_VALUE;
avci->last_pkt_props->dts = AV_NOPTS_VALUE;
}
if (got_frame)
av_assert0(frame->buf[0]);
return ret < 0 ? ret : 0;
}
解码时调用了AVCodec的decode函数,以AYUV为例
#if CONFIG_AYUV_DECODER
AVCodec ff_ayuv_decoder = {
.name = "ayuv",
.long_name = NULL_IF_CONFIG_SMALL("Uncompressed packed MS 4:4:4:4"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_AYUV,
.init = v408_decode_init,
.decode = v408_decode_frame,
.capabilities = AV_CODEC_CAP_DR1,
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
};
static int v408_decode_frame(AVCodecContext *avctx, void *data,
int *got_frame, AVPacket *avpkt)
{
AVFrame *pic = data;
const uint8_t *src = avpkt->data;
uint8_t *y, *u, *v, *a;
int i, j, ret;
if (avpkt->size < 4 * avctx->height * avctx->width) {
av_log(avctx, AV_LOG_ERROR, "Insufficient input data.\n");
return AVERROR(EINVAL);
}
if ((ret = ff_get_buffer(avctx, pic, 0)) < 0)
return ret;
pic->key_frame = 1;
pic->pict_type = AV_PICTURE_TYPE_I;
y = pic->data[0];
u = pic->data[1];
v = pic->data[2];
a = pic->data[3];
for (i = 0; i < avctx->height; i++) {
for (j = 0; j < avctx->width; j++) {
if (avctx->codec_id==AV_CODEC_ID_AYUV) {
v[j] = *src++;
u[j] = *src++;
y[j] = *src++;
a[j] = *src++;
} else {
u[j] = *src++;
y[j] = *src++;
v[j] = *src++;
a[j] = *src++;
}
}
y += pic->linesize[0];
u += pic->linesize[1];
v += pic->linesize[2];
a += pic->linesize[3];
}
*got_frame = 1;
return avpkt->size;
}
send的过程就是以上这些,接下来我们看一下receive的过程:
/**
* 从解码器返回解码的输出数据。
*
* @param avctx codec context
* @param frame frame将被设置为参考计数的视频或音频帧(取决于解码器类型),具体由解码器分配。
* 请注意,在执行任何其他操作之前,该函数都需调用av_frame_unref(frame)。
*
* @return
* 0: success, a frame was returned
*
* AVERROR(EAGAIN): 输出在此状态下不可用 - 用户必须尝试发送新输入
*
* AVERROR_EOF: 已刷新解码器,并且不会向其发送新数据包
*
* AVERROR(EINVAL): 编解码器未打开,或者是编码器
*
* 其他错误: legitimate decoding errors
*/
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame)
{
// 取出AVCodecInternal结构体
AVCodecInternal *avci = avctx->internal;
int ret;
// 取消引用帧引用的所有缓冲区并重置帧字段。
av_frame_unref(frame);
// avcodec_is_open(avctx):如果avctx打开则返回正值(即,在没有相应的avcodec_close()的情况下调用avcodec_open2()),否则返回0
// av_codec_is_decoder(avctx->codec):如果avctx->codec是解码器,则返回非零数字,否则返回零
if (!avcodec_is_open(avctx) || !av_codec_is_decoder(avctx->codec))
return AVERROR(EINVAL);
// 初始化avci的DecodeFilterContext
ret = bsfs_init(avctx);
if (ret < 0)
return ret;
// 与avcodec_send_packet逻辑类似:如果存在可以已解码的数据,则直接取出使用;
// 否则调用decode_receive_frame_internal自行解码
if (avci->buffer_frame->buf[0]) {
av_frame_move_ref(frame, avci->buffer_frame);
} else {
ret = decode_receive_frame_internal(avctx, frame);
if (ret < 0)
return ret;
}
// 裁剪
if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) {
ret = apply_cropping(avctx, frame);
if (ret < 0) {
av_frame_unref(frame);
return ret;
}
}
avctx->frame_number++;
return 0;
}
旧API:avcodec_decode_video2
新API:avcodec_send_packet与avcodec_receive_frame
avcodec_decode_video2:新版本中的实现也是使用新的API来完成
avcodec_send_packet:通过av_bsf_send_packet发送数据,通过decode_receive_frame_internal解码数据,将AVPacket中的数据放入了指定的AVCodecContext->AVCodecInternal->DecodeFilterContext->AVBSFContext->AVPacket中;解码数据时,根据是否指定了解码方式,分为调用receive_frame和调用decode_simple_receive_frame,解码时调用了AVCodec的decode函数
avcodec_receive_frame:如果存在可以已解码的数据,则直接取出使用,否则调用decode_receive_frame_internal自行解码。decode_receive_frame_internal的解码流程同上。