音视频开发– 坑整理

作者 : admin 本文共3053个字,预计阅读时间需要8分钟 发布时间: 2024-06-17 共1人阅读

1. 解码时,一定要用avcodec_parameters_to_context(),将流的参数(stream->codecpar)复制到解码器中,否则某些流可能无法正常解码。

    //第七步,给给解码器上下文添加参数, avcodec_parameters_to_context():

    ret = avcodec_parameters_to_context(mp3decodercontext, mp3avstrem->codecpar);

2.解码第一帧前,一定要将解码器的timebase设置为流的timebase(即:dec_ctx->pkt_timebase = stream->time_base),否则提示“Could not update timestamps for skipped samples”。

    //for fix "error   Could not update timestamps for skipped samples. "
    mp3decodercontext->pkt_timebase = mp3avstrem->time_base;

实际上上述两个问题的本质是:

AVStream 和 AVCodecContext 得到的信息不一样,严格来说,是AVStream获得的多。具体分析一下:

AVStream 是从 av_find_best_stream获得的,而 AVCodecContext 是从直接通过 avcodec_find_decoder(enum AVCodecID id) 获得的,

而AVCodecID 就是固定的那几种,例如 AV_CODEC_ID_H264,可以想象,ffmpeg内部的实现一定是有限制的,其实现一定是参考 h264的spec 。因此才有了上述两个方法的必要性。

3. avcodec_send_packet 调用后,要立即av_packet_unref;但是avcodec_receive_frame调用后,不用 av_frame_unref.

当 调用

avcodec_send_packet(mp3decodercontext,mp3avpacket);

后,为什么调用

av_packet_unref(mp3avpacket);

呢?

但是从 

avcodec_receive_frame(mp3decodercontext, mp3avframe);

给mp3avframe 中写入数据后,为什么不用调用 

av_frame_unref(mp3avframe)

呢?

不对称呀,没有问题吗?翻看了一下ffmpeg提供的例子,确实是这么写的呀,难道例子有内存泄漏?

基于上述这个问题,查看了下ffmpeg如下两个方法的源码:

int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
 avcodec_send_packet源码

从 源码我们可以看到,AVCodecContex这个数据结构中有一个AVCodecInternal

AVCodecInternal *avci = avctx->internal;

在没有error发生的时候,调用 av_packet_unref(avci->buffer_pkt);将原本里面的buffer_pkt数据清空然后还原成默认值。

下来就是在条件成立的时候,调用ret = av_packet_ref(avci->buffer_pkt, avpkt);

这个av_packet_ref方法内部的实现实际上也就是 将 第二个参数的内容 拷贝 给第一个参数。

然后整个方法就结束了,从整个流程来看,

我们再调用 int avcodec_send_packet方法的整个过程中,调用了该方法:

 av_packet_ref(avci->buffer_pkt, avpkt); 也就是说,增加了avpkt的引用计数,因此,我们在代码内部紧接着调用av_packet_unref(mp3avpacket); 是完全有必要的。

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))
        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 bsf, avci->buffer_pkt);
    if (ret 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;
}

avcodec_receive_frame源码

int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame)
{
    av_frame_unref(frame);

    if (av_codec_is_decoder(avctx->codec))
        return ff_decode_receive_frame(avctx, frame);
    return ff_encode_receive_frame(avctx, frame);
}

上来先将 frame 清空:av_frame_unref(frame); 联想我们在实际代码中总是会在一个循环中 avcodec_receive_frame数据,也就是说,只要不是最后一次,从第一次到中间的任何一次,都会将frame 清空。

那么最后一次的avframe是谁清空的呢?就是我们在自己写的代码中 

    av_frame_free(&mp3avframe);

如果是解码操作:执行 ff_decode_receive_frame函数

再看核心函数

av_frame_move_ref(frame, avci->buffer_frame); 给frame中填充数据,并转移ref,count并没有增加。

和 核心函数

ret = decode_receive_frame_internal(avctx, frame);

4. QT中如果调用了C++代码,弹出窗口的那种,例如 cout<<"hello world"<<endl;

在有中文的情况下,请保证QT中的代码是 GB2312的,如果是UTF的,那么在windows上窗口显示的出来的是乱码,且有可能后面的log 就不显示了

本站无任何商业行为
个人在线分享 » 音视频开发– 坑整理
E-->