市面上有很多种H.264硬压采集, 海康/大华/等等等等
找了半天发现所有的卡接口及其相似,在开发包中都是使用 RegisterStreamDirectReadCallback 注册回调函数来获取压缩后的数据流。
在它的回调函数中输出的码流不知道是啥子格式,而我需要标准的H.264码流,保存成flv并同时使用RTMP发布。如果解码后重新编码加重了CPU的负担,不符合要求。难道这就是传说中的海康码?
找啊找啊找啊找,在几乎绝望的情况下发现大华的工具里面居然有一个将 采集卡的录像文件 转换成 标准AVI 的小工具 StreamToAVI.exe。
马上一试,输出的AVI能够被MediaInfo识别,且显示AVI容器的 codec 是 AVC, Main profile. 哟西,看来还是标准的H.264码流啊。
只要能够反向这个工具就能够解析出可爱的NALU了。
打开IDA,首先盯上的就是ReadFile API调用
.text:00401CD1 push 10000h .text:00401CD6 push ecx .text:00401CD7 push ebp .text:00401CD8 call ds:ReadFile .text:00401CDE test eax, eax .text:00401CE0 jz short loc_401D0F .text:00401CE2 mov eax, [esp+10h] .text:00401CE6 cmp eax, 10000h .text:00401CEB jnb short loc_401CF5 .text:00401CED mov dword ptr [esp+14h], 1 .text:00401CF5 mov edx, [esi+60h] .text:00401CF8 push eax .text:00401CF9 push edx .text:00401CFA mov ecx, esi .text:00401CFC call sub_401E30
每次读取长度为0x10000 的文件块到内存,然后就callsub_401E30进行处理, 看来401E30这个函数就是要反向的目标
这个函数是某个类的成员函数,成员变量是由ebp寄存器(this指针)加上偏移量进行操作,看起来真头疼啊
理清类的内存布局以及推测其中某些变量含义,花了3天时间, 虽然人是笨了点,不过好在有耐心。再结合调试跟踪,又花了一个周末的时间摸清了基本的来龙去脉。终于搞出点东西来了!
.text:00402231 mov eax, [ebp+64h] .text:00402234 cmp eax, 1FDh .text:00402239 jz short loc_40227D .text:0040223B cmp eax, 1FCh .text:00402240 jz short loc_40227D .text:00402242 cmp eax, 1FBh .text:00402247 jz short loc_40227D .text:00402249 cmp eax, 1FAh .text:0040224E jz short loc_40227D .text:00402250 cmp eax, 1F0h .text:00402255 jz short loc_40227D .text:00402257 cmp eax, 1F1h .text:0040225C jz short loc_40227D
这个1FD 1FC 1FB 1FA 1F0 应该是类型字段,标明当前的数据类型
1F0 是音频编码,略过, 不感兴趣
1FD 和 1FC 是 H.264编码 , 1FD应该是 I帧
1FB 和 1FA 不知道是什么,调试中发现, 我的录像文件从不进入这个分支执行,它应该是另一种编码类型(0x3447504D?),可能是为其它某种型号的硬件准备的。这个就略过了,反正没用。
在0x403030处发现一个重要函数
.text:00403030 mov ecx, [esp+arg_8] .text:00403034 mov eax, [esp+arg_C] .text:00403038 push ebp .text:00403039 mov ebp, [esp+4+arg_0] .text:0040303D push esi .text:0040303E push edi .text:0040303F lea edi, [ecx+eax] .text:00403042 lea eax, [ebp+1] .text:00403045 mov byte ptr [ebp+0], 0 .text:00403049 xor esi, esi .text:0040304B mov byte ptr [eax], 0 .text:0040304E inc eax .text:0040304F mov byte ptr [eax], 0 .text:00403052 inc eax .text:00403053 mov byte ptr [eax], 1 .text:00403056 mov dl, [ecx] .text:00403058 inc eax .text:00403059 mov [eax], dl .text:0040305B inc eax .text:0040305C inc ecx .text:0040305D cmp ecx, edi .text:0040305F jnb short loc_403084 .text:00403061 cmp esi, 2 .text:00403064 jnz short loc_403071 .text:00403066 cmp byte ptr [ecx], 3 .text:00403069 ja short loc_403071 .text:0040306B mov byte ptr [eax], 3 .text:0040306E inc eax .text:0040306F xor esi, esi .text:00403071 mov dl, [ecx] .text:00403073 test dl, dl .text:00403075 jnz short loc_40307A .text:00403077 inc esi .text:00403078 jmp short loc_40307C .text:0040307A xor esi, esi .text:0040307C mov [eax], dl .text:0040307E inc eax .text:0040307F inc ecx .text:00403080 cmp ecx, edi .text:00403082 jb short loc_403061 .text:00403084 mov ecx, [esp+0Ch+arg_4] .text:00403088 sub eax, ebp .text:0040308A pop edi .text:0040308B pop esi .text:0040308C mov [ecx], eax .text:0040308E pop ebp .text:0040308F retn 10h
这个函数的特征太明显了,连续设置0x00 0x00 0x00 0x01 以及 连续2个零后小于3做处理。
一眼就认出0x40303这个函数肯定是转换成H.264 Annex B format输出
围绕这个函数分析,发现卡输出的H.264分2种, 一种就是H.264 Annex B, 还一种就是普通的NALU
试着自己写了个解析程序,成功地转换了录像文件。
明天继续,离球门不远了 :)