花了5天时间,终于解决了一个bug,心情非常愉快,憋了这么久,不吐不快
http://www.cnweblog.com/fly2700/archive/2011/12/06/318916.html
(转载)
花了5天时间,终于解决了一个bug,心情非常愉快,憋了这么久,不吐不快。
事情是这样的,前面跟外地一家公司,开发一个二路RTSP音视频合成一路RTMP音视频的设备。设备在公司内运行是好好的,可到了现场,出现直播流畅,录制后点播卡顿的问题。由于设备在外地,调试不方便。只能这边写日志打印代码,那边烧程序调试,于是远程调试的恶梦开始了。远程操作画面卡不说,关键是慢,本来一个几分钟的事情,远程要搞几十分钟。长达5天的远程调试,真是对人的耐性的一种考验。
首先我怀疑的是时间戳不均匀。于是我将发送端的时间戳,接收端的时间戳分别日志成文件,统计,没有发现过大或过小的时间戳。也没有发现累计时间戳和累计到达时间偏差很大。这样能排除时间戳的问题。
其次我怀疑是数据格式的问题。我们这边RTSP的数据源设备和现场的不一样。于是我又写代码,将RTSP下拉的数据保存文件,去掉RTP头,添加SPS、PPS,保存为裸H264文件。数据用VLC播放这个裸H.264文件,结果可以流畅播放,说明视频数据是完整的。再写代码将H264文件分帧并用RTMP协议打包发送直播,FP能流畅直播,录制依然是卡的。开始怀疑我的分帧发送代码是否有问题,于是将我以前录制好的H.264文件拿来,用同样的方法测试,结果直播流畅,录制流畅。同样的代码不同H264文件有不同效果,那么可能是H264文件格式的不同。于是分析h264文件的NAL。NAL等于5的就是关键帧。录制流畅的h264每个关键帧之间的间隔是固定的32,而录制卡顿的H264文件,十几个关键帧连在一起。根据以往经验,这是变码率的H264数据。我的RTMP协议栈并没用支持这种格式。于是开始分析这种变码率的h264格式,在我自己的电脑里面搭建环境调试改写协议栈,轻车熟路,没过多久,我的RTMP协议栈能支持发送这种变码率的H264数据直播了。直播流畅,录制流畅。好像问题攻克了。于是带着高兴的心情,将程序更新到我的远程设备,运行。一看效果,刚开始直播和录制流畅,没过多久就开始卡顿了。和之前卡顿不同的是,卡顿频率降低了,而且FP会反复打印日志NetStream.Buffer.Empty。刚才高兴的心情一下子仿佛回到了解放前。
根据经验,这种情况一般是网络带宽不足,播放端缓存不足,或时间戳过小导致。于是我让在现场的工作的人员测试播放,结果他们在局域网看效果仍然卡顿,排除了带宽不足的问题。然后我增加播放器缓存到5秒,播放依然卡顿,又排除了缓存不足的问题。再然后我将发送端时间戳,接收端时间戳日志到文件,终于发现问题了。发送端时间戳正常,而接收端时间戳出现4000以上的大时间戳。按道理发送30帧每秒的视频,平滑处理后的时间戳应该是33-34。如果是FMS将我的时间戳修改增加了,那么会导致累计时间戳比累计时间大,但结果统计这二个值相差不大。我也没有发现有过小时间戳来中和这个大时间戳,那么累计时间戳是如何保持不变的呢,有一种可能性,丢包了。我将统计的帧数除以时间打印出来发现接收端只有20帧每秒。发送端打印的是30帧每秒。恩,可能是丢包了。我想看看是哪些数据丢了,于是将发送端的数据记录到文件,接收端接收的数据也保存到文件,对比,竟然发现数据总大小一模一样,说明没有丢包。于是我逐帧地对比发送端和接收端的数据,发现接收端有一包里面包含十多个帧的现象。而这种现象出现在接收到一个大关键帧的后面。FMS为什么会将大关键帧帧后面的小参考帧连起来做为一帧呢?这个问题我想了很久,也做了各种各样的实验。修改了多种打时间戳的方法和平滑时间戳的方法,也没有效果。最后,我猜测是否因为音频数据不足导致。因为我知道音频和视频播放不一样,它不会因为时间戳打得快就快放,它按照自己的频率计算时间匀速播放。如果音频数据不足或丢失,那么本来应该和它一起播放的视频帧会快进或跳过。于是我将发送音频部分的日志打印出来,果然发现存放问题,音频数据的环形缓存区满了,导致音频丢包。我为了防止重入,发送视频包的时候,音频不能发送。而且我们是1080P 的视频,视频关键帧有上百KB。我的音频环形缓存长度设置的10个。瞬间导致音频缓存满,然后就是音频数据丢失。于是我将音频环形缓冲长度改为30,日志显示环形缓冲最大不超过20个。小心地将最新的程序更新到设备,看效果,直播依然卡顿。我明明解决了一个BUG,竟然没有效果。神啊,救救我吧,我已经花了4天时间了,早已身心疲惫。
当然,神是不会理我的,这BUG还是要我们程序员自己解决。FP还是打印日志NetStream.Buffer.Empty。于是又来分析时间戳,统计,没有发现过大或过小的时间戳。也没有发现累计时间戳和累计到达时间偏差很大。但是发现累计时间和累计到达时间相比戳抖动比较大。说明时间戳没问题,只是有些包来晚了,然后后来又补上了。这样子好像是远程直播带宽不稳定导致。于是让在现场的工作人员测试直播,效果流畅。再让他们测测录制,也是流畅的。反复测试没出现卡顿,问题终于解决了。心情愉悦。
总结,1,找BUG需要沉下心来,找不到问题不要灰心,一定要充满斗志,否则容易中途放弃不前。 2,判断问题需要准确定位,在一个错误方向上努力完全是浪费时间。3,多做实验,写日志,用数据说话,不要凭空猜测。4,写代码的时候,日志不要多,但处理严重错误的时候还是需要日志一下,方便日后排除错误。不要像我缓冲满了也不printf一下。
问:
发送端时间戳正常,而接收端时间戳出现4000以上的大时间戳。按道理发送30帧每秒的视频,平滑处理后的时间戳应该是33-34。如果是FMS将我的时间戳修改增加了,那么会导致累计时间戳比累计时间大,但结果统计这二个值相差不大
这个时间戳是H264的IBP帧的时间戳还是RTMP包的时间戳呢?如果是264的时间戳不是连续增加的吗?33-34是rtmp包的相对时间戳吗?rtmp包的时间戳有什么作用呢,接触不多,请赐教
答:
1我指的是RTMP包的时间戳。
2发送的rtmp包时间戳是相对时间戳。
3rtmp时间戳是用来做音视频同步的。
q:
你好,我将dm368编码的h264视频裸流保存成文件,但是vlc打开后发现播放速度是原来的两倍,请问知道是为什么吗?
ans:
编码的时候,输入正确帧率就可以了。如果不输入帧率,编码后的裸H264 文件VLC会用默认帧率播放,默认好像是25还是30的。如果你实际压缩是15帧每秒,按照默认帧率播放就会快放了。
花了5天时间,终于解决了一个bug,心情非常愉快,憋了这么久,不吐不快的更多相关文章
- 终于解决了一个Win7 下 VS 编译的问题,困扰了我好几个月
用 Win7 一年多了,一直在这个环境下编程,其他都挺好,就是有个问题非常恶心,在VS下编译经常出现进程正在使用,无法覆盖的错误,这个问题我记得博问中以前也有其他人遇到过,也没有解决掉.此问题困扰了我 ...
- MAC OSX下终端通过NTLM验证,通过代理上网(花了一天时间才解决这个)
MAC OSX下终端通过NTLM验证,通过代理上网 公司网络限制如下: 公司通过代理来控制内网用户访问外网的权限.用户名和密码为域用户,采用的验证方式是NTLM(用的是foreFront TMG) 遇 ...
- 曹工杂谈:花了两天时间,写了一个netty实现的http客户端,支持同步转异步和连接池(1)--核心逻辑讲解
背景 先说下写这个的目的,其实是好奇,dubbo是怎么实现同步转异步的,然后了解到,其依赖了请求中携带的请求id来完成这个连接复用:然后我又发现,redisson这个redis客户端,底层也是用的ne ...
- 今天花了好长的时间终于把SecureCRT安装成功了 现在分享给大家 安装的步骤, 希望对大家用帮助
转载地址:https://www.cnblogs.com/lianghe01/p/6618651.html 今天花了好长的时间终于把SecureCRT安装成功了 现在分享给大家 安装的步骤, 希望对大 ...
- 【生产问题】记还原一个很小的BAK文件,但却花了很长时间,分析过程
[生产问题]还原一个很小的BAK文件,但却花了很长时间? 关键词:备份时事务日志太大会发生什么?还原时,事务日志太大会怎么办? 1.前提: [1.1]原库数据已经丢失,只有这个bak了 [1.2]ba ...
- 2020终于解决Chrome浏览器“崩溃啦”的问题!
Google的chrome莫名其妙突然所有页面都显示"喔唷 崩溃啦",各种插件在右下角弹出报错!这个问题我之前遇到过一次,后来通过改快捷方式的名字解决了.可是这次,隔离回来上班,打 ...
- [ASP.NET 5]终于解决:Unable to load DLL 'api-ms-win-core-localization-obsolete-l1-2-0.dll'
11月12日,惊喜地发现SqlClient(System.Data.SqlClient.dll)跨平台了(对应的nuget包包是runtime.unix.System.Data.SqlClient), ...
- PhpStorm 2016.3 For Mac 重大里程碑更新 -- 终于解决了不能输入中文标点符号的重大bug
PhpStorm 2016.3 For Mac 重大里程碑更新 1.[终于解决了]不能输入中文标点符号的重大bug,如 逗号“,”.“.”: 2.可以在一个窗体中,同时打开多个项目: 3.其他... ...
- 终于解决了IE8不支持数组的indexOf方法,array的IndexOf方法
/* 终于解决了IE8不支持数组的indexOf方法 */ if (!Array.prototype.indexOf) { Array.prototype.indexOf = function (el ...
随机推荐
- electron入门教程
1.atom/electron github: https://github.com/atom/electron 中文文档: https://github.com/atom/electron/tree ...
- dtrace 语法
Usage: dtrace [-aACeFHlqSvVwZ] [-arch i386|x86_64] [-b bufsz] [-c cmd] [-D name[=def]] [-I path ...
- Python爬虫之路——简单网页抓图升级版(添加多线程支持)
转载自我的博客:http://www.mylonly.com/archives/1418.html 经过两个晚上的奋斗.将上一篇文章介绍的爬虫略微改进了下(Python爬虫之路--简单网页抓图),主要 ...
- MachineLearningInAction
2017-01-07 20:14:45 前面两周主要都是在复习然后考试,每天其实过得也挺苦逼的.基本上项目和学习上的是都没有接触了:复习了随机过程和数字信号处理和信号检测和估值:主要都是复习一些理论上 ...
- 以面试官的角度看strcpy函数
一:笔试或者面试的总结 之 一 (1)在笔试或者面试中常常会被问道,strcpy memmove memcpy 函数的实现.有时也会问你STL 中string的 split 和 trim的实现.有的 ...
- java中Volatile修饰符的含义
在java语言中:为了获得最佳速度,同意线程保存共享成员变量的私有拷贝.并且仅仅当线程进入或者离开同步代码块时才与共享成员变量的原始值进行对照. volatilekeyword的作用就是提示vm:对于 ...
- js逻辑非同时两次使用 !!null
今天遇到了“!!null”的写法,百度没有找到直接的解释,翻书在<javascript高级设计>P44找到了相应的解释: 同时使用两个逻辑非操作符,实际上就会模拟Boolean()转型函数 ...
- 阿里云OSS对象存储 简单上传文件
不得不说阿里云的命名比较让人摸不着头脑,开始以为是文件存储NAS,弄了半天什么文件系统,挂载点的搞不明白.后来才搞清楚原来 对象存储OSS才是我需要的. 其中EndPoint就是画红框的部分,但是要加 ...
- MySQL windows集群(转)
http://blog.csdn.net/zhangking/article/details/5670070 MySQL 群集是 MySQL 适合于分布式计算环境的高可用.高冗余版本.它采用了 ...
- Vue 资源
一. 资源教程 综合类 vuejs 英文资料 Vue中文资料总汇 Vue.js 的一些资源索引 vue资料 入门类 vue 快速入门 Vue.js 中文系列视频教程 on Laravist 英文教程 ...