一.ffmpeg中的解码操作是在函数static int process_input(int file_index)中完成的。
1.在该函数中首先会调用static int get_input_packet(InputFile *f, AVPacket *pkt)函数将通过av_read_frame(f->ctx, pkt)函数将文件中的视频或音频数据读取到AVPacket结构体中。
2.然后通过static int process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eof)函数进行解码操作。
二.在process_input_packet函数中会分别调用decode_audio和decode_video进行音视频的解码。
1.decode_video(InputStream *ist, AVPacket *pkt, int *got_output, int64_t *duration_pts, int eof, int *decode_failed)此时数据是在AVPacket *pkt中。此函数会调用decode(ist->dec_ctx, decoded_frame, got_output, pkt ? &avpkt : NULL)函数进行解码,待解码的数据在avpkt中。解码后的数据放在decoded_frame中。decode函数的定义为:
static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt)
{
int ret;
*got_frame = 0;
if (pkt) {
ret = avcodec_send_packet(avctx, pkt); //完成此操作后,解码后的数据放在avctx->internal->buff_frame中。
// In particular, we don't expect AVERROR(EAGAIN), because we read all
// decoded frames with avcodec_receive_frame() until done.
if (ret < 0 && ret != AVERROR_EOF)
return ret;
}
ret = avcodec_receive_frame(avctx, frame); //此函数只是进行一个拷贝的操作,将avctx中解码后的数据拷贝给avframe。
if (ret < 0 && ret != AVERROR(EAGAIN))
return ret;
if (ret >= 0)
*got_frame = 1;
return 0;
}
主要的解码操作是在avcodec_send_packet中,其定义为:
int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt)
{
AVCodecInternal *avci = avctx->internal;
int ret;
if (!avcodec_is_open(avctx) || !av_codec_is_decoder(avctx->codec)) // 检查 AVCodecContext 是否已打开,并且 AVCodec 是否为解码器
return AVERROR(EINVAL);
if (avctx->internal->draining)
return AVERROR_EOF;
if (avpkt && !avpkt->size && avpkt->data)
return AVERROR(EINVAL);
av_packet_unref(avci->buffer_pkt);
if (avpkt && (avpkt->data || avpkt->side_data_elems)) {
ret = av_packet_ref(avci->buffer_pkt, avpkt);
if (ret < 0)
return ret;
}
//提交数据包进行过滤,把AVPacket的数据传给 avci->filter.bsfs[0],会将AVPacket的数据清掉,bsf为比特流过滤器,如h264_mp4toannexb
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); //真正的解码函数,也说明解码后的frame是放在avctx->internal->buffer_frame
if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
return ret;
}
return 0;
}
解码函数decode_receive_frame_internal,此时的数据是在avctx->internal->filter.bsfs[0]中,该函数会调用decode_simple_receive_frame(avctx, frame)函数,进而调用decode_simple_internal(avctx, frame)函数。
decode_simple_internal(avctx, frame)会先调用ff_decode_get_packet(avctx, pkt)将avpkt数据从avctx->internal->filter.bsf[0]->internal->buff_pkt中提取出到pkt中,pkt的定义为:
AVCodecInternal *avci = avctx->internal;
DecodeSimpleContext *ds = &avci->ds;
AVPacket *pkt = ds->in_pkt;
decode_simple_internal(avctx, frame)然后调用avctx->codec->decode(avctx, frame, &got_frame, pkt)进行解码。此语句会根据解码器的类型调用相应的解码器,下面以h264解码为例:
AVCodec ff_h264_decoder = {
.name = "h264",
.long_name = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_H264,
.priv_data_size = sizeof(H264Context),
.init = h264_decode_init,
.close = h264_decode_end,
.decode = h264_decode_frame,
.capabilities = /*AV_CODEC_CAP_DRAW_HORIZ_BAND |*/ AV_CODEC_CAP_DR1 |
AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS |
AV_CODEC_CAP_FRAME_THREADS,
.hw_configs = (const AVCodecHWConfigInternal*[]) {
#if CONFIG_H264_DXVA2_HWACCEL
HWACCEL_DXVA2(h264),
#endif
#if CONFIG_H264_D3D11VA_HWACCEL
HWACCEL_D3D11VA(h264),
#endif
#if CONFIG_H264_D3D11VA2_HWACCEL
HWACCEL_D3D11VA2(h264),
#endif
#if CONFIG_H264_NVDEC_HWACCEL
HWACCEL_NVDEC(h264),
#endif
#if CONFIG_H264_VAAPI_HWACCEL
HWACCEL_VAAPI(h264),
#endif
#if CONFIG_H264_VDPAU_HWACCEL
HWACCEL_VDPAU(h264),
#endif
#if CONFIG_H264_VIDEOTOOLBOX_HWACCEL
HWACCEL_VIDEOTOOLBOX(h264),
#endif
NULL
},
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_EXPORTS_CROPPING,
.flush = flush_dpb,
.init_thread_copy = ONLY_IF_THREADS_ENABLED(decode_init_thread_copy),
.update_thread_context = ONLY_IF_THREADS_ENABLED(ff_h264_update_thread_context),
.profiles = NULL_IF_CONFIG_SMALL(ff_h264_profiles),
.priv_class = &h264_class,
};
avctx->codec->decode(avctx, frame, &got_frame, pkt)会直接指向h264_decode_frame(AVCodecContext *avctx, void *data,
int *got_frame, AVPacket *avpkt)函数。在h264_decode_frame中会先定义以下四个变量。
const uint8_t *buf = avpkt->data;
int buf_size = avpkt->size;
H264Context *h = avctx->priv_data;
AVFrame *pict = data;
然后调用decode_nal_units(h, buf, buf_size)函数解码NALU单元。在decode_nal_units函数中:
1.先调用ff_h2645_packet_split(&h->pkt, buf, buf_size, avctx, h->is_avc,
h->nal_length_size, avctx->codec_id, avctx->flags2 & AV_CODEC_FLAG2_FAST)函数,进行码流的解析,在其中调用h264_parse_nal_header(nal, logctx)解析NALU单元头部。得到NALU数据。而且这函数会对h->pkt进行数据填充。此时的数据被转移到h->pkt中。
2.然后调用ff_h264_queue_decode_slice(h, nal)函数进行片解码,其中H2645NAL *nal = &h->pkt.nals[i]。
最后会调用finalize_frame(h, pict, h->next_output_pic, got_frame)函数将解码后的数据赋给pict也就是data,从而最后将解码后的数据传输出去。