Tag Archives: Profession

使用ffmpeg命令行裁剪合并mp4封装的H.264视频音频流

最近因为工作上的关系又要重操旧业折腾视频编码了,而且这次更偏向实际应用一些。

工作时使用到的工具:

  • CamStudio – 屏幕录像,可录音。Free, GPL license
  • MediaCoder – Audio/Video Transcode Interface
  • FFmpeg – Audio/Video CODEC Converter. Free, LGPL
  • StreamEye – 视频码流分析器。Commercial
  • YUVviewer – YUV RAW码流播放器
  • MediaPlayer

这次的工作是为了录制一次培训讲座中Lab部分的演示。录制很麻烦,起码NG了千万次,而且有过长时间的返工。那么最后为了减少重新录制的麻烦,我选择了输出片段的方式。期望依靠后期处理来进行裁剪和合并的操作。但我并没有Adobe Premier这种非线性编辑器,所以就略显麻烦。折腾很久。

首先CamStudio输出的视频是AVI封装,视频为CamStudio专用编码、音频则是PCM。所以第一步是将这个原始的AVI码流使用MediaCoder的界面小批量地将它用x264编码,Baseline profile@L3.1。这样可以在其他并未预装CamStudio解码器的机器正常播放。

接下来,需要把录成的各个章节小片段合并。我尝试过许多种组合,国产流氓软件我们就不再提及。这里记录下使用FFmpeg的操作过程。来源于搜索到的这篇文章。它的一个思路就是将mp4用MPEG TS码流,而TS码流在文本数据层面可以进行拼接的动作。那么我这里还多了一步转换成mp4封装的步骤,vcode和acode就不指定了,FFmpeg的默认编码必然满足我的需要。

ffmpeg -i demo2-1.avi demo2-1.mp4
ffmpeg -i demo2-2.avi demo2-2.mp4
...
ffmpeg -i 1.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 1.ts
ffmpeg -i 2.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 2.ts
ffmpeg -i "concat:1.ts|2.ts" -acodec copy -vcodec copy -absf aac_adtstoasc output.mp4

后来嫌麻烦,写了个Batch做这个合并的事情。放心,FFmpeg跑的挺快的。我在Windows下干活,也可以根据需要转成shell脚本。

@echo off

ffmpeg -i %1 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 1.ts
ffmpeg -i %2 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 2.ts
ffmpeg -i "concat:1.ts|2.ts" -acodec copy -vcodec copy -absf aac_adtstoasc %3

del 1.ts
del 2.ts

最后呢,就是按照需要去裁剪那些因为不断失误而录下来的错误。参考了这篇这篇文章。他们的一个思路都是利用ffmpeg -ss <Start time> -t <Duration>的选项去做裁剪输出。但是直接对H.264和AAC等编码的码流使用会有两大问题:1、视频裁剪只能对关键参考帧动刀子,如果需要裁切的时间落在了P帧或者B帧最后输出的视频片断肯定有偏差。2、比较明显的音画不同步问题。

那么为了解决上述问题,思路则为:针对问题1,首先将码流转换为仅帧内预测编码(Intra Prediction),另外将AAC等压缩的音频流还原成PCM也对问题2有帮助。参考文章之一选择了MJPEG作为“无损”的视频编码格式,并且提到不使用RAW的理由。而在我的实际使用步骤并不绝对符合。我使用h264的纯帧内编码和PCM音频。

ffmpeg -i demo3-1_1.avi -vcodec h264 -intra -acodec pcm_s16e demo3-1_out.avi

接下来,想要将一段25秒长的错误之处去掉。这里利用MPlayer播放暂停后使用Goto(Ctrl+G)得到比较精确的时间:2’59.121” ~ 3’24.638”。那么用FFmpeg裁剪,就等价于截取0’0”~2’59.121”和3’24.638”这段,抛弃不要的,用上面讲的方法合并前后。为了计算Duration方便,我将分秒格式的时间(1:2.30)换算成秒钟浮点数(62.30)——这两种格式都被FFmpeg支持。

ffmpeg -ss 0.00 -t 179.121 -i demo3-1_out.avi -vcodec libx264 -acodec libvo_aacenc
-ab 320k demo3-1_1.mp4
ffmpeg -ss 204.638 -t 80.372 -i demo3-1a.avi -vcodec libx264 -acodec libvo_aace
nc -ab 320k demo3-1_2.mp4

join_mp4.bat demo3-1_1.mp4 demo3-1_2.mp4 demo3-1.mp4

好了,烦恼我一个月的视频编辑做完啦。

苦力笔记

还是写点流水账记一下吧。

到今天中午,手上的mbaff项目终于能够仿真通过一个mbaff编码的视频码流了,这是自从今年3月份我接手以来的第一次。虽然对于最终结果来说,这样的阶段成果距离最后结束还是太遥远了,我们还有很多其他各种各样比如,paff、普通的帧编码、普通的场编码,大尺寸、小尺寸,单帧多slice,high profile、baseline profile等等等等测试视频需要仿,我们最终的指标QFHD尺寸的mbaff视频还完全没有踪影,仿真以后能不能成功综合成满足时序没有意外latch还是个未知数,综合之后放在fpga上面能不能配合firmware继续跑一圈QA Video更是遥远的事情。

即便这样,手里的代码也是经过反复折腾才长成如此规模的。3月时刚从上一届学长那里拿到原始的代码,首先要做的就是与现有的QFHD解码器合并代码。问题是,这两个版本之间的差距差不多有两年多,两年前,他们各自在各自的工作空间里添加流水线或者MBAFF,然后随着时间推移一届传一届的又分别长成了自成一格的两个项目。我从软件开始到后来的RTL合并,光读懂前辈和前辈的前辈们的代码就已经很痛苦了,何况双份,更何况他们持续多年的人肉版本管理终于让一份能战的Soft Model都淹没在各种各样命名的目录之内消失不见。作为什么都不懂的笨蛋,每天开着Beyond Compare作文字处理也会惶惶恐恐,哪些是新的架构更改,哪些是MBAFF特有的计算模块我那时还一概不知。后来的提交也会引起上司们的抱怨:“你怎么可以如此merge” “你弄错了,现在这些信号都不用了”

可是,新的系统架构,新的模块组织什么的,你都没教我,你只是这么一句话:看代码,一切都很清楚。

整个3月和半个4月我都在做这种其实算是打字和文字处理的事情,迷迷茫茫。也不知道该吐槽哪边的代码风格更加令人费解才好。也不是没有注释或者设计文档,只是,我才见识原来到注释可以不写一行文字却纯用符号表达,好似甲骨文占卜,而设计文档老旧的无法和现在主流的Project Tree兼容。光RTL部分来说我并没有全包这种合并工作,只是整个系统的前端部分,解码器的FrontEnd模块——这已经是五人两代的劳动结晶了(笑),具体的说有这么几块MBAFF支持比较集中的地方:算数编解码CAVLC与CABAC[未完成]、预测运动矢量readMotion、显示图像缓存DPB、去块滤波参数DBP以及其他宏块前期处理的过程。 至于作为模型的C Model,在一个很偶然的情况下发现已经合并结束的工程并不能正确的解码.264的视频,别说mbaff编码,普通的码流输出的yuv序列都无法和参考对应。这时面对混乱的工程和项目,上司们显得比我还要焦躁。最后我在硬盘深处找到一个号称QA测试结果最优的版本,反向合并(也就是在中古项目里加QFHD支持),通过大部分QA码流的解码测试。这才稳住了摇摇欲坠的软件模型,而此时已经5月过半了。

两个月以来干的事情其实仍然很杂并且效率低下,而且自己一方面新丁上场不懂的地方实在多到影响工作效率(不懂mbaff原理、不懂项目里的一些设计结构、甚至不懂稍微具体的解码流程),一方面给前辈本应完成而未完成的事情扫尾自然会状况百出。这种情况直到自己开始调试,开始做功能仿真时才得到改善。其实,还有一点不得不说的是春季的三四月,也是公司和大佬双双push得最厉害的时候,leader经常在要开会或者要用我的时候发现我不在,而相似的大佬每次来打水也会出现上午明明看到下午就不见踪影的事情。这段日子两边让我都觉得压力好大,每天也很疲劳,四月还有自己私人交际上的第三方压力。我没有崩溃,却大病一场体温窜到39度如同泄气皮球一样释放出来了。五月用了一周时间和父母在一起,之后就自然而然的辞职quit,专心做事。

这段我先前曾拼命隐瞒——起码想和三次元的交际圈里面千方百计隐瞒的经历想了想还是坦诚写在这里吧,我憋了也有好几月了,一直没处说,虽然经常经常依赖Twitter来抱怨生活上的不如意,但出现在人家的Timeline上毕竟是与大家相关不大的事情,虽然安全却有着些些陌生的落寂感。过去这么长时间,即便三次元的、身边的、甚至就是敏感的大佬本人如果google到这篇,我也觉得无所谓了,哪怕域名被墙,尽管来看好了,来看看你身边的人是混的多么的苦逼——当你有着另一半陪着看电影或者完全不用为晚饭的食物操心的时候你身边的人有多么的苦逼。

—————有多少爱,可以重来;有多少人,愿意等待—————

6月到来,带来的魔都夏日却一直挺凉爽的,我早早的铺上麻将席却时时冻得鸡皮疙瘩发抖。6月以来的项目就进入了真正的调试阶段,接触了许多项目里的设计概念,学到了不少。在此期间我有好几次中二发作,作为什么资历都没有的小虾米愣是不爽研究所里山寨小作坊一般的工作环境。于是先从自己用的EDA软件入手,搞定了license,搞定了linux版本的novas,接着搞定了linux下一套Soft Model+RTL Model的工作调试环境,最后搞定了自用的版本管理svn,因为讨厌在老的掉牙的RHEL4里面无论装个什么都发愁库与库之间依赖性,索性自己做了个新的debian amd64虚拟机偷偷塞进去(起码deb包和apt-get可以迅速的装上从gcc到svn再到vim一切常用工具);而windows平台下成功说服同僚扔掉VC6,起码debug时用vs2008看你的变量和条件断点比较不那么反人类吧。中二的自己也没法把自己这一套习惯向外推广,一直只是这么自个儿坚持的。我不能说外面无论大小公司基本工作环境都是*nix基础动辄vim或emacs你们还死守UltraEdit32破解版怎么行啊,也不能劝服说放弃你自己的bat批处理吧那么多脚本可以写。总之,在三次元的同僚眼里或许我就是这么个格格不入甚至有时很装逼的二愣子吧,没差啦。也许你可以忍受win7下面debussy 5.4 for win的频频崩溃而我不能;也许你认为双击bat文件比起敲make命令方便而我讨厌虚拟机内外切来切去——我要插一句话,我们的仿真流程一般是这样的:如果更新过C代码的话接下来要到虚拟机里使用Makefile编译打包用于or1200 cpu的firmware,接下来使用GenTestBench.bat调用vc编译的软件模型解出待仿真的码流的YUV序列,之后调用一些C写的数据处理小工具制作二进制文件为仿真做准备——为什么这些不能一股脑在linux环境下做完?反正仿真肯定没法在win下面跑了。所以我凭借这么一股中二的热情用若干Makefile嵌套补完了整个project tree(看上去像模像样),也照着《鸟哥的linux私房菜》(感谢我的第一个leader)写了点shell脚本,这么着不仅把仿真环境,更是把项目的树状结构理清楚了,也能给建立版本库做准备。在这里我要感谢曾经实习的DesignCAD team,这些树状项目和IP组件管理的概念都是在组里做CDS移植的时候学习到的。

说回项目本身,实地仿真出了许多错误,而这些错误都作为改进文本合并或是了解项目中系统结构与数据、时序处理细节的小小经验积累。起初为了保证文本合并之后代码功能正常,挑选的都是非mbaff编码格式的码流,我印象最深的是CVPA_TOSHIBA_D这个(也许命名记忆有偏差),同僚跟我说是作为非mbaff码流来说最考验去块滤波的一个QA码流,作为图像级帧场自适应编码的视频考验了去块滤波本来就足够复杂的各种可能情况。解决了这一类问题(主要是当初合并时对现有的架构和流水线机制并不太清楚)已经差不多到了7月的关头。此时距离传说中的暑假只剩下一个月,实验室内的大家也陆陆续续大体完成了属于自己那份的活计,各自忙着写小论文投简历面试找实习如何如何,可是我还什么都没个头绪,再去找寻个肯给工资的雇主我也怕自己手上东西没有做好,去哪里都是半调子继续自我厌恶的这种循环。但毕竟要加快自己进度了,当时决定否决再调一些“传统”非mbaff码流的建议,直接挑选mbaff编码中图像尺寸最小、帧数也最少的CVQP3_Sony_D,开始了没日没夜没有周末没有夜间休息的昏天暗地生活。实际上我解决问题最多最集中的也就是7月这么20来天,多到没法回想起一些充满成就令人回味的改动。我只能说,自从邯郸学步地把svn版本库建好到现在整个码流跑通,现在的revision已经到r37了(抱歉大佬,r不是release的意思)。从茫茫的错误中我知道了前端FrontEnd里关于流水线的设计动机和原理,也知道SRAM配合流水线进行读写的机制,软硬件配合甚至firmware编写与调试,也算修改过总数达到二十五六个状态的状态机,修正过细小模块的时序错误,此外还有软件内铺张浪费的数组声明到硬件里只能处处以吝啬节省为主的时候只好用相当扭曲消耗脑细胞的表达方式组织数据,诸如此类。

我并不是一个爱看书和会读书的人,很多好书系统的讲解了某方面的知识我最多当作字典或者参考书一样用到的时候随手翻翻,用完就丢脑后忘掉。这些零零碎碎的东西竟然都能作为自己经验,那我这样的得多么不学无术才行啊——很多细节都是书里详细讲解的典型案例才对。呵呵,作为从小到大被人骂做笨蛋的人也只能做到这种程度了,摊手。睡觉了,真的懒得在wp后台给blog插图,对于软件对于web对于基于这一切的技术来说我就是个什么都不懂的小白。

在和研究所里的项目抗争之余,我那一个周末缝补起来的脚本知识全部忘光了,现在肯定没什么人要我的吧(笑。

十月计划

下午prof过来,把我们统一划分到了解码组。所里的安排计划如下:
decoder:
纠错——C mode已完善,需要迁移至RTL实现。
MBAFF——模块调试工作完成进度,包括DBST接口(马王张)
多路decoding——整体架构大改动,目的实现4路FullHD/8路720p/16路480p实时解码
encoder:
RTL模型空白

我接手MBAFF的部分,需要了解Macroblock的预测/DDR数据分配。等这些做完则转入encoder实现。

花十个月做这些事情,希望自己早点完成提前出去多多实习。

也许实习5个月,休2个月,再出去5个月是个不错的折衷,但似乎我们更加忙碌一些。