【FFmpeg】avcodec_receive_frame函数

发布于:2024-07-27 ⋅ 阅读:(31) ⋅ 点赞:(0)

FFmpeg相关记录:

示例工程:
【FFmpeg】调用ffmpeg库实现264软编
【FFmpeg】调用ffmpeg库实现264软解
【FFmpeg】调用ffmpeg库进行RTMP推流和拉流
【FFmpeg】调用ffmpeg库进行SDL2解码后渲染

流程分析:
【FFmpeg】编码链路上主要函数的简单分析
【FFmpeg】解码链路上主要函数的简单分析

结构体分析:
【FFmpeg】AVCodec结构体
【FFmpeg】AVCodecContext结构体
【FFmpeg】AVStream结构体
【FFmpeg】AVFormatContext结构体
【FFmpeg】AVIOContext结构体
【FFmpeg】AVPacket结构体

函数分析:
【通用】
【FFmpeg】avcodec_find_encoder和avcodec_find_decoder
【FFmpeg】关键结构体的初始化和释放(AVFormatContext、AVIOContext等)
【FFmpeg】avcodec_open2函数
【FFmpeg】内存分配和释放(av_malloc、av_realloc等)

【推流】
【FFmpeg】avformat_open_input函数
【FFmpeg】avformat_find_stream_info函数
【FFmpeg】avformat_alloc_output_context2函数
【FFmpeg】avio_open2函数
【FFmpeg】avformat_write_header函数
【FFmpeg】av_write_frame函数

【编码】
【FFmpeg】avcodec_send_frame函数

【解码】
【FFmpeg】avcodec_send_packet函数

1.avcodec_receive_frame

函数的主要功能是从解码器或编码器返回解码后的输出数据(当使用AV_CODEC_FLAG_RECON_FRAME标志时)

/**
 * Return decoded output data from a decoder or encoder (when the
 * @ref AV_CODEC_FLAG_RECON_FRAME flag is used).
 *
 * @param avctx codec context
 * @param frame This will be set to a reference-counted video or audio
 *              frame (depending on the decoder type) allocated by the
 *              codec. Note that the function will always call
 *              av_frame_unref(frame) before doing anything else.
 *
 * @retval 0                success, a frame was returned
 * @retval AVERROR(EAGAIN)  output is not available in this state - user must
 *                          try to send new input
 * @retval AVERROR_EOF      the codec has been fully flushed, and there will be
 *                          no more output frames
 * @retval AVERROR(EINVAL)  codec not opened, or it is an encoder without the
 *                          @ref AV_CODEC_FLAG_RECON_FRAME flag enabled
 * @retval "other negative error code" legitimate decoding errors
 */
int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame)
{
    av_frame_unref(frame);
	// 1.如果是解码器,返回decoded frame(一般情况)
    if (av_codec_is_decoder(avctx->codec))
        return ff_decode_receive_frame(avctx, frame);
    // 2.如果是编码器,返回重建帧
    return ff_encode_receive_frame(avctx, frame);
}

1.1 返回解码帧(ff_decode_receive_frame)

函数的主要功能是返回解码的帧,定义位于libavcodec\avcodec.c中。如果代码需要从已编码的缓冲区中取出数据,会调用这个函数。这个函数的主要流程是检查缓冲区是否有数据,如果有则直接取出,否则尝试进行解码,随后会进行一些其他的检查和处理

/**
 * avcodec_receive_frame() implementation for decoders.
 */
int ff_decode_receive_frame(AVCodecContext *avctx, AVFrame *frame)
{
    AVCodecInternal *avci = avctx->internal;
    int ret;
	
    if (!avcodec_is_open(avctx) || !av_codec_is_decoder(avctx->codec))
        return AVERROR(EINVAL);
	// 1.如果缓冲区有数据,则直接取出
    if (avci->buffer_frame->buf[0]) {
        av_frame_move_ref(frame, avci->buffer_frame);
    } else {
    	// 2.如果缓冲区没有数据,尝试进行解码
        ret = decode_receive_frame_internal(avctx, frame);
        if (ret < 0)
            return ret;
    }
	// 3.确保返回给调用者的帧是有效的
	// 主要是检查frame的format, width, height这些信息
    ret = frame_validate(avctx, frame);
    if (ret < 0)
        goto fail;

    if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) {
    	// Video decode only
    	// 4.某些视频编解码器支持裁剪,这意味着只有被解码帧的子矩形用于显示, 这个选项控制libavcodec如何处理裁剪
        ret = apply_cropping(avctx, frame);
        if (ret < 0)
            goto fail;
    }

    avctx->frame_num++;

#if FF_API_DROPCHANGED
	// AV_CODEC_FLAG_DROPCHANGED表示不要输出与流中第一个解码帧参数不同的帧
    if (avctx->flags & AV_CODEC_FLAG_DROPCHANGED) {

        if (avctx->frame_num == 1) {
            avci->initial_format = frame->format;
            switch(avctx->codec_type) {
            case AVMEDIA_TYPE_VIDEO:
                avci->initial_width  = frame->width;
                avci->initial_height = frame->height;
                break;
            case AVMEDIA_TYPE_AUDIO:
                avci->initial_sample_rate = frame->sample_rate ? frame->sample_rate :
                                                                 avctx->sample_rate;
                ret = av_channel_layout_copy(&avci->initial_ch_layout, &frame->ch_layout);
                if (ret < 0)
                    goto fail;
                break;
            }
        }

        if (avctx->frame_num > 1) {
            int changed = avci->initial_format != frame->format;

            switch(avctx->codec_type) {
            case AVMEDIA_TYPE_VIDEO:
                changed |= avci->initial_width  != frame->width ||
                           avci->initial_height != frame->height;
                break;
            case AVMEDIA_TYPE_AUDIO:
                changed |= avci->initial_sample_rate    != frame->sample_rate ||
                           avci->initial_sample_rate    != avctx->sample_rate ||
                           av_channel_layout_compare(&avci->initial_ch_layout, &frame->ch_layout);
                break;
            }

            if (changed) {
                avci->changed_frames_dropped++;
                av_log(avctx, AV_LOG_INFO, "dropped changed frame #%"PRId64" pts %"PRId64
                                            " drop count: %d \n",
                                            avctx->frame_num, frame->pts,
                                            avci->changed_frames_dropped);
                ret = AVERROR_INPUT_CHANGED;
                goto fail;
            }
        }
    }
#endif
    return 0;
fail:
    av_frame_unref(frame);
    return ret;
}

1.2 返回重建帧(ff_encode_receive_frame)

如果不是decoder,会进入到下面函数,去调重建帧

/**
 * avcodec_receive_frame() implementation for encoders.
 */
int ff_encode_receive_frame(AVCodecContext *avctx, AVFrame *frame)
{
    AVCodecInternal *avci = avctx->internal;
	// 不存在重建帧,报错
    if (!avci->recon_frame)
        return AVERROR(EINVAL);
    if (!avci->recon_frame->buf[0])
        return avci->draining_done ? AVERROR_EOF : AVERROR(EAGAIN);
	// 返回重建帧
    av_frame_move_ref(frame, avci->recon_frame);
    return 0;
}

2.小结

avcodec_receive_frame函数比较简单,可以概述为从已编码的缓冲区buffer中将frame取出,使用的函数是av_frame_move_ref,这个在其他文中记录过,不再记录

CSDN : https://blog.csdn.net/weixin_42877471
Github : https://github.com/DoFulangChen


网站公告

今日签到

点亮在社区的每一天
去签到