目录
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