您的位置:首页 > 游戏 > 游戏 > 音视频开发-- 坑整理

音视频开发-- 坑整理

2024/11/16 2:42:11 来源:https://blog.csdn.net/hunandede/article/details/139687445  浏览:    关键词:音视频开发-- 坑整理

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 < 0)return ret;}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);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 就不显示了

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com