设备断电等异常导致MP4文件无法打开的解决方案

在实际项目中,比如DVR设备,视频一般是MP4格式,MP4并非是流媒体格式,如果视频没有正常写完,会导致无法打开和播放这个MP4文件,有没有办法恢复已经保存的MP4文件,就这个文章我们讨论一下可行的方法。

首先我们需要了解MP4文件的组织架构,MP4文件都是以box的形式存储的,box非常的简单,具有如下的格式

长度4 byte也就是说一个box长度不超过uint32长度
名称4 byte比如 ftyp free mdat moov等
数据n bytebox内的真正数据

具体的MP4格式,我不会过多的介绍,可以参考其他人的博客,这里只要知道MP4结构比较简单就行了,不要产生畏难心理,以下列出一些博客,可供参考
wqyuwss的专栏
超详细MP4格式分析
以上我们就算对MP4格式有一些基础了解了,如果这些博客已经看不到了,或者不想看这些博客,也没什么大问题,针对我们的实际应用,也就是怎么修复未正常写完的MP4文件,我们往下也会分析。

首先我们需要知道为什么MP4文件打不开,没写完MP4肯定是缺少了文件的一些重要部分,一个正常的MP4文件,其结构如下:
MP4文件结构
在一级的box结构里面包含四种box类型:ftyp free mdat 和 moov。打不开的原因就是文件缺少了moov box,这个在文件结束时才能确定里面的数据,才能补充到文件的末尾。也有一些MP4文件为了在网络上快速打开,将moov放到mdat的前面,这是在文件写完时,刻意的将mdat整体向后移动moov长度字节,再将moov写到原来mdat起始位置。
是不是我们将moov补齐就可以播放了?
of course not.

我们之前介绍过,box有个描述自身长度的字段,占四个字节,我们写入视频帧到MP4文件,实际上,我们是将这些帧写入mdat box里面,这时候我们也不会去修改mdat box的长度,只有最后会更新这个长度,所以还要相应的修改mdat box的长度字段。

有了这两个地方的修改和添加,我们的MP4文件就完整了。

理论我们已经有了,实际怎么处理我们介绍两种方法。

实际当中,我们也不会自己写muxer的代码,一般我们使用一些开源的库,比如MP4V2和FFMPEG,我们这里以FFMPEG为例。

FFMPEG中基本的MP4文件写入遵循以下调用:

// 写MP4头
avformat_write_header(avfc, &dict);
// 写MP4视频帧
while(n--) {
    av_interleaved_write_frame(avfc, &video_packet);
}
// 写MP4视频尾,异常情况导致无法调用下面语句,使MP4无法打开
av_write_trailer(avfc);

第一种解决方法要求我们能够修改FFMPEG库,在运行时,将我们需要的数据保存下来,在修复时将moov补充到文件未尾。这需要对FFMPEG库有一定的了解,理论上是非常可行的,我们每隔比如10s,更新一次mdat box的长度字段,并且将此时的moov数据写入一个备用文件,一旦断电,我们检测到这个文件moov没有写入,那就在mdat box向后便宜mdat box 长度字节的位置上,写入我们预存的moov数据。

以上的方法要求我们一直有一个moov备用文件,而且每个10s更新一次。

第二种方法,我们可以只每10s更新一次mdat box的长度字段,甚至不更新这个字段,我们知道moov中有些数据是相对不变的(比如编码器参数,视频的宽高等),有些数据是经常变化的(比如视频时长,帧数等),相对不变的参数我们只需要从一个相似的视频文件中获取(比如DVR前一分钟我们存的视频,与后一分钟视频),经常变化的参数可以从mdat box中获取。

我们需要区分哪些是时变的参数,哪些是相对不变的参数。

对此我们需要分析 moov 中 mvhd edts 和 mdia 这些 box。充分的理解它们的数据组成。

mvhd box

字段长度(字节单位)描述是否时变
size4mvhd box 的长度
类型4mvhd
版本1一般是0
flag3一般是0
生成时间4基准是1904-1-1 0:00 AM
修订时间4基准是1904-1-1 0:00 AM
Time scale4计算时间单位
Duration4视频长度
播放速度41.0正常速度,16.16浮点表示
播放音量21.0最大音量,8.8浮点表示
保留10
矩阵结构36
预览时间4开始预览此文件的时间
预览 duration4
以下查阅其它博客

以上可以看到只有一个字段需要我们修改,需要知道视频的具体时长。

edts box
edts 是一个容器,里面装着 elst box,他的主要作用是对track分成一段一段的,每一段做特殊的播放处理

字段长度描述是否时变
size4edts box 的长度
类型4edts
elst boxnelst box

以下是elst box 的定义

字段长度描述是否时变
size4elst box 的长度
类型4elst
版本1一般是0
flag3一般是0
list数量4
list12*nlist在下面定义

list 如下

字段长度描述是否时变
track duration4这一段在视频中的时长
开始时间4这一段的开始时间
播放速度4这一段的播放速度

这里举一个edts的例子

00 00 00 24    // edts 的长度 总共36字节
65 64 74 73    // edts 的ASCII字符
00 00 00 1C    // elst 的长度 28 字节
65 6C 73 74    // elst 的ASCII字符
00             // 版本
00 00 00       // flag
00 00 00 01    // list 数量 1个
00 00 26 6A    // 这一段的时长 也就是整个视频时长
00 00 00 00    // 从 0 时刻开始
00 01 00 00    // 播放速度 1.0 正常速度

这里我们可以看到,每个视频需要修改 track duration 为视频的整个时长即可。

mdia box
mida box 也是个容器,他的内容很多,包含 mdhd hdlr minf 三种box,同样我们一个个分析

mdhd box

字段长度描述是否时变
size4mdhd box 的长度
类型4mdhd
版本1一般是0
flag3一般是0
生成时间4
修改时间4
time scale4
duration4
语言2
质量2

这里可以看到很多和 mvhd 一样的定义,只不过这是针对track的

hdlr box

字段长度描述是否时变
size4mdhd box 的长度
类型4hdlr
版本1一般是0
flag3一般是0
Component type4handler 类型:mhlr(media handler)dhlr(data handler)
Component subtype4
Component manufacturer4
Component flags4
Component flags mask4
Component namen

hdlr的内容可以看成是不变的

minf box
minf box 包含 vmhd dinf stbl 三种box
vmhd box

00 00 00 14 76 6D 68 64 00 00 00 01 00 00 00 00 ; ....vmhd........
00 00 00 00                                     ; ....

==================  解析  ===================
00 00 00 14: size 20
76 6D 68 64: vmhd
00: version = 0
00 00 01: flag 默认1
00 00: Graphics mode 0,直接拷贝原始图片
00 00 00 00 00 00:Opcolor 三个16位值,用于指定图形模式字段中指示的传输模式操作的红色,绿色和蓝色。

vmhd 的内容也不需要修改

dinf box
dinf 包含一个 dref box

00 00 00 24 64 69 6E 66 00 00 00 1C 64 72 65 66 ; ...$dinf....dref
00 00 00 00 00 00 00 01 00 00 00 0C 75 72 6C 20 ; ............url 
00 00 00 01                                     ; ....

==================  解析  ===================
00 00 00 24: size 36
64 69 6E 66: dinf
00 00 00 1C: size 28
64 72 65 66: dref
00 : version 0
00 00 00: flag 均为0
00 00 00 01:Entry count 1
00 00 00 0C 75 72 6C 20 00 00 00 01: entry data

dinf 的内容也不需要修改

stbl box
stbl 包含 stsd stts stss stsc stsz stco 6 种 box
stsd box
存放解码必须的描述信息。对于H264视频流,包含一个avc1 box,avc1包含avcC和pasp。avc1包含视频的width和height,avcC包含编码器相关信息,sps,pps等。也不需要改动。
stts box
定义每个sample的时长。

00 00 00 20 73 74 74 73    00 00 00 00 00 00 00 02    ....stts........
00 00 01 2B 00 00 0B 9A    00 00 01 00 00 00 00 00    ................

==================  解析  ===================
00 00 00 20: size 32
73 74 74 73: stts
00:version
00 00 00: flag
00 00 00 02: time-to-sample 数量
00 00 01 2B: 具有相同 duration 的连续sample 数量 299
00 00 00 00: sample 的 duration 0
00 00 01 00: 具有相同 duration 的连续sample 数量 256
00 00 00 00: sample 的 duration 0

这里可以让所有sample duration 相同,只修改 sample 数量

stss box
同步sample表,存放关键帧列表,关键帧是为了支持随机访问。

00 00 00 38 73 74 73 73    00 00 00 00 00 00 00 0A    ....stss........
00 00 00 01 00 00 00 1F    00 00 00 3D 00 00 00 5B    ................
00 00 00 79 00 00 00 97    00 00 00 B5 00 00 00 D3    ...y............
00 00 F1 00 00 01 0F 00                               .......

==================  解析  ===================
00 00 00 30: size 56
73 74 74 73: stss
00:version
00 00 00: flag
00 00 00 0A: 同步 sample 数量
00 00 00 01: 第1个同步 sample 序号
00 00 00 1F: 第2个同步 sample 序号
00 00 00 3D: 第3个同步 sample 序号
00 00 00 5B: 第4个同步 sample 序号
...

需要根据 mdat box 数据恢复这个表

stsc box
sample-chunk 印射表

00 00 00 40 73 74 73 63    00 00 00 00 00 00 00 04    ....stsc........
00 00 00 01 00 00 00 63    00 00 00 01 00 00 00 02    .......c........
00 00 00 68 00 00 00 01    00 00 00 03 00 00 00 60    ...h............
00 00 00 01 00 00 00 04    00 00 00 01 00 00 00 01    ................

==================  解析  ===================
00 00 00 40: size 64
73 74 74 63: stsc
00:version
00 00 00: flag
00 00 00 04: sample to chunk 数量
00 00 00 01: trunk 序号 1
00 00 00 63: trunk 内 sample 数量
00 00 00 01: sample description 的序号
00 00 00 02: trunk 序号 2
00 00 00 68: trunk 内 sample 数量
00 00 00 01: sample description 的序号
00 00 00 03: trunk 序号 3
...

需要根据 mdat box 数据恢复这个表
stsz box
指定每个sample的size。

00 00 04 C4 73 74 73 7A    00 00 00 00 00 00 00 00    ....stsz........
00 00 01 2C 00 00 61 B0    00 00 15 E9 00 00 30 B0    ......a.........
00 00 24 C8 00 00 29 FA    00 00 37 49 00 00 33 5C    ................
00 00 22 A3 00 00 1E 57    00 00 25 A7 00 00 1C DB    ................

==================  解析  ===================
00 00 04 C4: size 1220
73 74 74 7A: stsz
00:version
00 00 00: flag
00 00 00 00: 仅当所有sample size相同时使用
00 00 01 2C: sample 数量
00 00 61 B0: 第1个 sample 的 size
00 00 15 E9: 第2个 sample 的 size
00 00 30 B0: 第3个 sample 的 size
00 00 24 C8: 第4个 sample 的 size
...

需要根据 mdat box 数据恢复这个表
stco box
指定每个chunk在文件中的位置

00 00 00 20 73 74 63 6F    00 00 00 00 00 00 00 04    ....stco........
00 00 00 30 00 0F FC 4C    00 1F FA 2E 00 2F DA A4    

==================  解析  ===================
00 00 00 20: size 32
73 74 63 6F: stco
00:version
00 00 00: flag
00 00 00 00: 仅当所有sample size相同时使用
00 00 00 04: chunk 数量
00 00 00 30: 文件第 48 字节
00 0F FC 4C: 文件第 1047628 字节
00 1F FA 2E: 文件第 2095662 字节
00 2F DA A4: 文件第 3136164 字节
...

需要根据 mdat box 数据恢复这个表。

以上就是恢复一个仅有一个视频track的MP4文件需要恢复的数据。

mdat的数据与编码器有关,可能还需要进一步了解编码器的数据格式,比如H264编码。

=====================================================================
以上实现太过麻烦,实际上MP4有一种moof Box,能够将MP4文件分段,分段后的存储格式类似下图
分段的MP4文件结构
只要分段足够小,视频基本上和流式媒体没什么区别,FFMPEG支持按照文件大小和时长分段,这样在保存时断电,也不会导致整个文件不可用。

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
MP4文件是一种常见的视频格式,但有时候可能会出现无法打开的情况。造成MP4文件无法打开的原因可能是文件缺少了moov box,这是文件结束时才能确定里面的数据,也可能是文件损坏导致的。如果文件缺少moov box,补齐moov box可能无法解决问题。另外,MP4文件也可以分段存储,类似于流媒体的格式,这样即使在保存时断电也不会导致整个文件不可用。FFMPEG支持按照文件大小和时长分段。如果你遇到了无法打开MP4文件,可以尝试使用在线转换工具或者视频转换器来将MP4文件转换成其他格式,这样可能能够解决无法打开的问题。 #### 引用[.reference_title] - *1* *2* *3* *4* [设备断电异常导致MP4文件无法打开解决方案](https://blog.csdn.net/HUAJUN998/article/details/115005667)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *5* [电脑上如何进行MP4格式转换成其它格式?](https://blog.csdn.net/ban_gong/article/details/102726199)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值