【ffmpeg】源码分析:avcodec_send_packet

旧API使用avcodec_decode_video2来进行
写法:

 //旧API需要循环获取视频帧,需要自己实现video_queue
    while (av_read_frame(fmt_ctx.get(), &pkt) >= 0) {
        if (pkt.stream_index == video_stream_index) {
            //packet_queue_put(&video_queue, &pkt);
        } else {
            av_free_packet(&pkt);
        }
    }

而新APIavcodec_send_packet & avcodec_receive_frame通过上下文来操作视频帧:
官方注释:

Unlike with older APIs, the packet is always fully consumed, and if it contains multiple frames (e.g. some audio codecs), will require you to call avcodec_receive_frame() multiple times afterwards before you can send a new packet. It can be NULL (or an AVPacket with data set to NULL and size set to 0); in this case, it is considered a flush packet, which signals the end of the stream. Sending the first flush packet will return success. Subsequent ones are unnecessary and will return AVERROR_EOF. If the decoder still has frames buffered, it will return them after sending a flush packet.

    while (av_read_frame(fmt_ctx.get(), m_Packet.get()) >= 0) {
        if (m_Packet->stream_index == m_StreamIndex) {
            if (avcodec_send_packet(vcodec_ctx.get(), m_Packet.get()) != 0) {
                return 0;
            }
            while (avcodec_receive_frame(vcodec_ctx.get(), m_Frame.get()) == 0) {
                //获取到 m_Frame 解码数据,进行格式转换,然后进行渲染
            }
        }
        av_packet_unref(m_Packet.get()); //释放 m_Packet 引用,防止内存泄漏
    }

下面就来看一下位于 源码路径 libavcodec/decode.c 的代码(加注释 删判断)

int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt)
{
	//获取AVCodecInternal结构体
    AVCodecInternal *avci = avctx->internal;
    av_packet_unref(avci->buffer_pkt);
    if (avpkt && (avpkt->data || avpkt->side_data_elems)) {
    	//数据浅拷贝至avci->buffer_pkt
        ret = av_packet_ref(avci->buffer_pkt, avpkt);
    }
    //再进行一次数据拷贝
    ret = av_bsf_send_packet(avci->bsf, 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);
    }
    return 0;
}

其实分析的重点就在decode_receive_frame_internal函数中,他调用了 ff_decode_get_packet函数,而这个函数会不断从AVPacketList中去AVPacket出来解析。

int ff_decode_get_packet(AVCodecContext *avctx, AVPacket *pkt)
{
	xxx
    ret = av_bsf_receive_packet(avci->bsf, pkt);
    if (ret == AVERROR_EOF)
        avci->draining = 1;
    ret = extract_packet_props(avctx->internal, pkt);

    ret = apply_param_change(avctx, pkt);

#if FF_API_OLD_ENCDEC
    if (avctx->codec->receive_frame)
        avci->compat_decode_consumed += pkt->size;
#endif
    xxx
}

回到上面的官方注释,每加入一个AVPacke到上下文,就必须使用完,不能留到下一次解析,如果是旧API,还需要设置avci->compat_decode_consumed,

上一篇:QUIC


下一篇:2016年下半年 网络规划设计师 上午试卷 综合知识 软考真题【含答案和答案解析】