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 就不显示了