关于修复mp4文件损坏的过程小记

        最近项目中遇到设备异常断电,导致mp4文件损坏的问题,最近花了点时间,深入分析原因,并进行了相关修复尝试工作,最后,虽然文件中的音视频数据大部分可以修复,但因为原始文件中视频的帧率是动态变化的,导致最终修复的视频出现音视频不同步。

       下面的内容会大概对MP4文件格式结构分析和修复策略两方面进行简单记录。

MP4文件格式的分析

       分析MP4格式,首先想到的就是看标准协议,从相关的MP4格式协议入手,那作为程序猿的人,google和维基百科两大利器,相信大部分人都会用到,mp4和mov格式是很相似的,后者的苹果搞出来的标准,具体的相关链接,可以在

https://en.wikipedia.org/wiki/MPEG-4_Part_14

https://wiki.multimedia.cx/index.php?title=QuickTime_container

维基百科页面看到,

通常mp4的格式有如下内容:

mp4格式
mp4格式

同样通过mp4info工具打开录制的mp4文件,发现视频文件包含下面几个atom,ftyp、free、mdat、moov,通常如果异常掉电后,其中moov包含了解析音视频数据的信息,缺少或者损坏了moov中的数据,会导致mp4视频文件打开异常,而mdat结构中则是实际的音视频数据块,它们以chunk的形式进行保存,解析它们的相关指示数据在moov结构中的stco中,stco记录着实际音视频数据的迁移地址,也就是说异常掉电使得moov结构中的数据丢失,进而导致mp4文件打开异常。

 

修复思路

       通过上面的简单描述,修复的思路是:如果存在同样环境下录制的mp4视频文件,这里所说的同样环境是指音频的采样率、通道数,采样点的位宽,视频的帧率、分辨率参数都是一样的情况下。ftyp、free、moov三个结构体中的大部分数据是一样,可以借助这个部分完好的数据,将mdat中区分开的音视频数据重新封装为mp4,注意:这三个结构中数据是重新封装mp4文件会用到。

      下面将如果将mdat块中的音频数据进行区分,通过上面的工具,我们可以观察到mdat数据是在moov前面的,而且mdat的数据是按照chunk块紧密存储的,所以对于损坏的文件,我们可以按照二进制的方式打开文件,逐步将ftyp、free去掉,然后将mda数据块缓存到一个buffer中,后面的工作就是对应这个buffer块中数据进行处理,区分工作这里主要是分区视频数据还是音频数据。而且每个视频的nal单元通常都是0开头,nal单元的第5个单元代表nal的类型,通过这种方式,可以将视频chunk分离出来,并将剩下的非视频chunk标记为音频数据。至此,能够将音视频数据,并通过moov结构中信息将视频文件重新修复。

       目前还存在的问题,由于项目的特殊原因,录制的帧率是动态的,导致修复后音视频不同步,此问题暂时没有解决,待后续考虑。

  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Protobuf是一种高效的序列化协议,可以用于数据交换和数据存储。它的主要优势是大小小,速度快,可扩展性强。下面是使用Protobuf的一些小记: 1. 定义消息格式 首先,需要定义消息格式,以便Protobuf可以将数据序列化和反序列化。消息格式定义在.proto文件中,使用protobuf语言编写。例如,下面是一个简单的消息格式定义: ``` syntax = "proto3"; message Person { string name = 1; int32 age = 2; } ``` 这个消息格式定义了一个名为Person的消息,包含两个字段:name和age。 2. 生成代码 一旦消息格式定义好,就可以使用Protobuf编译器生成代码。编译器将根据消息格式定义生成相应的代码,包括消息类、序列化和反序列化方法等。可以使用以下命令生成代码: ``` protoc --java_out=. message.proto ``` 这将生成一个名为message.pb.java的Java类,该类包含Person消息的定义以及相关方法。 3. 序列化和反序列化 一旦生成了代码,就可以使用Protobuf序列化和反序列化数据。例如,下面是一个示例代码,将一个Person对象序列化为字节数组,并将其反序列化为另一个Person对象: ``` Person person = Person.newBuilder() .setName("Alice") .setAge(25) .build(); byte[] bytes = person.toByteArray(); Person deserializedPerson = Person.parseFrom(bytes); ``` 这个示例代码创建了一个Person对象,将其序列化为字节数组,然后将其反序列化为另一个Person对象。在这个过程中,Protobuf使用生成的代码执行序列化和反序列化操作。 以上是使用Protobuf的一些基本步骤和注意事项,希望对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值