音视频开发-- 坑整理

news/2025/5/26 6:38:37

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

文章来源:https://blog.csdn.net/hunandede/article/details/139687445
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:https://dhexx.cn/news/show-5395747.html

相关文章

ubuntu16因swap分区uuid错误启动慢排查

感觉ubuntu16启动特别慢 dmesg查看如下&#xff1a; [ 10.050123] audit: type1400 audit(1718608189.395:11): apparmor"STATUS" operation"profile_load" profile"unconfined" name"webbrowser-app//oxide_helper" pid708 comm&q…

大数据之flink与hive

其实吧我不太想写flink&#xff0c;因为线上经验确实不多&#xff0c;这也是我需要补的地方&#xff0c;没有条件创造条件&#xff0c;先来一篇吧 flink&#xff1a; 高性能 低延迟 流批一体的分布式计算框架 基于事件时间 对实时数据精准处理 快速响应 支持批处理&#xff0c…

【电脑小白】装机从认识电脑部件开始

前言 在 B 站上刷到了一个很牛逼的电脑装机视频&#xff0c;很适合电脑小白学习&#xff0c;故用文本记录下。 推荐对组装台式电脑有兴趣的小伙伴都去看看这个视频&#xff1a; 原视频链接&#xff1a;【装机教程】全网最好的装机教程&#xff0c;没有之一_哔哩哔哩_bilibil…

C# + easyui 写的一个web项目

用C# easyui 来开发&#xff0c;其实就是为了开发速度&#xff0c;用easyui可以一天写很多页面&#xff0c;比一些低代码平台还快。 登陆页面 主界面 记录数统计 家庭信息采集表 新建家庭 家庭成员 低保、五保人员帮扶情况登记表 低保、五保人员帮扶情况登记表的新增和编辑 治…

LDO电容选型指南

1 为什么电容的选择至关重要 电容往往被人们所忽视&#xff0c;在许多工程师的心目中&#xff0c;电容不过是两个导体加上中间的隔离电解质。总而言之&#xff0c;它们属于最低级的电子元件之一。 工程师们通常通过添加一些电容的办法来解决噪声问题。这是因为他们普遍将电容视…

Android 项目中自定义多个 RadioButton 并排一列选择效果实现

在 Android 项目中,如果你想要实现多个 RadioButton 并排显示,并且只能选择其中一个(类似于单选按钮组的效果),通常你不会直接使用多个 RadioButton 控件,因为标准的 RadioButton 控件在布局中默认是独占一行的。 为了实现多个并排显示的 RadioButton 并只能选择其中一个…

RunMe_Aobut TC103848_UEFIShellFactoryDiagnostics.nsh

:: ***************************************************************************************************************************************************************** :: 20240617 :: 该脚本可以用于BIOS Case TC103848测试,功能包括&#xff1a;在EFIShell环境下运行…

微信小程序,设置页面标题和获取页面标题

设置标题&#xff1a; wx.setNavigationBarTitle({title: 页面标题, })获取标题&#xff1a; const pages getCurrentPages(); const pageUrl pages[pages.length - 1].route; let pageWindow {}; const wxPage __wxConfig.page; if(wxPage && Object.keys(wxPag…

mysql对VARCHAR和int的误解

VARCHAR: 你将VARCHAR类型的长度设置为255时&#xff0c;意味着该字段可以存储最多255个字符&#xff0c;不论这些字符是哪种语言的文字 tinyint int等 任何整数,只要类型定了,他表达的最大值和存储所占的空间就是确定的.不会因为你设置的长度不同而改变,所以正常情况下,我们…

【WEB前端2024】3D智体编程:乔布斯3D纪念馆-第38课-密室逃脱-3D互动剧情

【WEB前端2024】3D智体编程&#xff1a;乔布斯3D纪念馆-第38课-密室逃脱 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由JavaScript编写的智体世界引擎&…