需求
视频录制设备,在没有内置电池的情况下,突然断电,导致视频文件不完整,使用播放器播放失败;录制视频文件格式是MP4,需要提取里面的H264裸流进行播放。
实现
使用开源工具ffmpeg和ffplay进行转换和播放,windows环境下;
思想:首先使用工具ffmpeg转换正常的MP4文件,然后对比查看差异,再结合H264帧格式数据,最后手动写代码提取H264裸流;
正常的MP4转换后对比:
根据查询H264帧头格式信息看出,H264帧头是以00 00 01 61开头的,当然,还有其他的常用的NAL头:
0x67: SPS 0x68: PPS 0x65: IDR 0x61: non-IDR Slice 0x01: B Slice 0x06: SEI 0x09: AU Delimiter
前面4个字节是后面数据长度,所有需要把后面的数据取出然后加上固定的头格式00 00 00 01就形成了一帧数据;
但是在实验过程中,发现不同厂商的摄像头输出的帧头格式有细微的差异,需要做特殊处理;
代码
思想:
-- LEN -- NAL-固定值
| |
00 00 01 1D 61 E0 08 -> 00 00 00 01 61 E0 08
..
00 00 00 E7 61 E0 E8 -> 00 00 00 01 61 E0 08
00 00 00 0E 67 4D 00 -> 00 00 00 01 67 4D 00
00 00 00 04 68 EE 3C -> 00 00 00 01 68 EE 3C
00 00 00 05 06 E5 01 -> 00 00 00 01 06 E5 01
00 00 A1 15 65 B8 XX XX -> 00 00 00 01 65 B8 XX XX
黄色金属摄像头h264头数据:
00 00 xx xx 41 E1/E0
实现:
for(i=0; i<size-3; i++){
if((0x00 == buffer[i] && 0x00 == buffer[i+1]) &&
((0x61 == buffer[i+4] && 0xE0 == buffer[i+5]) ||
(0x67 == buffer[i+4] && 0x4D == buffer[i+5] && 0x00 == buffer[i+6]) ||
(0x68 == buffer[i+4] && 0xEE == buffer[i+5] && 0x3C == buffer[i+6]) ||
(0x06 == buffer[i+4] && 0xE5 == buffer[i+5] && 0x01 == buffer[i+6]) ||
(0x65 == buffer[i+4] && 0xB8 == buffer[i+5])))
{
date_len = ((buffer[i+2]<<8) | buffer[i+3]) + 4;
buffer[i+2] = 0x00;
buffer[i+3] = 0x01;
ret = fwrite(p_buffer+i, 1, date_len, pFile_out);
//printf("ret=%02X\n",ret);
if(ret != date_len)
{
printf("Fail to fwrite size=%d\n",date_len);
break;
}
}
else if(0x00 == buffer[i+2] && 0x18 == buffer[i+3] && 0x66 == buffer[i+4])
{
printf("over index=%x, flag: 00 00 00 18 66\n",i);
break;
}
}
由于对MP4和H264格式没有深入的了解,所以上面的内容根据自己经验得出,有不足之处请多多指教;
播放
提出的H264裸流保存成一个文件,使用ffplay工具进行播放,可以播放不完整的MP4文件,还能测试出视频丢失多少秒;