欢迎光临
我们一直在努力

嵌入式产品MP4如何实现生成固定大小文件

什么是MP4

MP4或称MPEG-4 Part 14,是一种多媒体容器格式,扩展名为.mp4,是最常见的封装格式之一,因为其跨平台的特性而得到广泛应用。

为什么要本地存储MP4

MP4 是嵌入式音视频产品的标准存储格式,没有之一。

嵌入式设备本地存 MP4,不是为了 “存视频”,是为了 “稳定、可靠、能回溯、能取证”

固定大小的MP4本地存储可以做的业务

可以使用统一的SD卡管理策略,来对存储本地的视频进行管理

可以对MP4进行重新解码来还原音视频流来进行回放操作

如何实现固定大小的MP4本地存储

一、生成256M或者符合业务需求的空文件用来存储MP4录像

1)、手动生成256M或者符合业务需求的空文件例如:

 但是不建议这么去做,受限于IO速度,创建起来会非常的慢

2、mkfs.fat 格式化时候生成256M或者符合业务需求的固定大小文件

优点:

1)、新卡插入时候可以提醒用户进行格式化,方便整个产品对SD卡生命周期的管理和业务需求增添

2)、可以快速生成指定大小指定指定个数的空文件,方便对其进行二次业务操作

缺点:

1)、需要手动修改mkfs.fat源码,手动操作 FAT 目录项、FAT 表、数据区等以用来创建指定大小的文件,需要掌握一定代码基础;

2)、下载git连接git clone https://github.com/dosfstools/dosfstools.git

如何在指定文件中录制MP4

一、编码准备

1、第三方库

        我采用的是media-server的第三方库

优势:开源,功能强大体积小,可以直接提取出需要部分集成到代码里完成独立的模块

注:我们本期讲的是mp4部分,所以我们要是不想重新编译的话可以直接提取出来libmov里的一些函数来直接进行封装,提取出来这部分就完全可以完成基础的mp4的编码的全部需求

下载链接 https://github.com/ireader/media-server

        

二、实现步骤

1、mp4的基础结构 

ftyp->mdat(音视频数据)->moov(索引)

ftyp → moov(索引) → mdat(音视频数据)

第一种为mp4的默认制式,或者是默认格式

第二种为mp4fast start,也就是moov前置,播放器可以先直接读取到索引然后进行播放

以上两种方式都可以增加一些free字段等等,仅是对普通mp4的简单描述,不包含fmp4,因为主要做本地存储相关

moov后置使用场景 

┌──────────┐ ┌──────────────────────┐ ┌──────────┐
│ ftyp │ │ mdat(音视频数据) │ │ moov │
│ 文件头 │ │ 边录边写,体积最大 │ │ 最后写 │
└──────────┘ └──────────────────────┘ └──────────┘
嵌入式录制场景:断电易丢moov → 文件损坏

moov前置使用场景

┌──────────┐ ┌──────────┐ ┌──────────────────────┐
│ ftyp │ │ moov │ │ mdat(音视频数据) │
│ 文件头 │ │ 先写索引 │ │ 后写数据 │
└──────────┘ └──────────┘ └──────────────────────┘
播放场景:播放器秒加载,不用等文件读完

我们采用moov后置的方式来实现我们的需求

优点:可以边录边写,不用缓存索引,可以做moov阶段性写入来实现断电时数据保护(后续我会出实现和方案)

2、文件盒子大小和盒子内容规划

1)、根据码率不同mp4的mdat的大小和moov的大小不确定,也就是256M大小下存储的视频时长不同,因此盒子的大小位置也不会确定,可以用查看mdat的size和moov的size来逐步调整来看一下当前moov应处在的位置。(具体做法可以联系我询问方案)

2)以我目前的mp4为例,256M的文件我用mp4dump给大家很直观的展现了出来

  ftype:ftyp大小为8+24  8字节的名字和size +24字节的头部,这根据需要兼容的品类不同可以修改

  mdat:mdat的大小也就是数据区的大小我们定下的是8+250432868个字节

 free:free盒子可写可以不写,不写的话需要在libmoov库里去掉,属于是保留位

 moov:moov盒子大小是8+18001492的大小,里面的mvhd和tark之类等盒子可以预留也可以不预留,之所以我预留了moov盒子里的内容是因为我需要来做断电保护的moov复写,保证每多少个帧或多长时间更新moov,这样指定了大小和位置可以在stts stss stsc stsz stco这几个盒子里进行数据追加减少moov写入时间

[ftyp] size=8+24
major_brand = isom
minor_version = 200
compatible_brand = isom
compatible_brand = iso2
compatible_brand = avc1
compatible_brand = mp41
[mdat] size=8+250432868
[free] size=8+1040
[moov] size=8+18001492
[mvhd] size=12+96
timescale = 1000
duration = 1690381
duration(ms) = 1690381
[trak] size=8+9600792
[tkhd] size=12+80, flags=3
enabled = 1
id = 1
duration = 1690381
width = 1920.000000
height = 1080.000000
[edts] size=8+28
[elst] size=12+16
entry_count = 1
entry/segment duration = 1690381
entry/media time = 0
entry/media rate = 1
[mdia] size=8+9600656
[mdhd] size=12+20
timescale = 90000
duration = 152130732
duration(ms) = 1690341
language = und
[hdlr] size=12+33
handler_type = vide
handler_name = VideoHandler
[minf] size=8+9600571
[vmhd] size=12+8, flags=1
graphics_mode = 0
op_color = 0000,0000,0000
[dinf] size=8+28
[dref] size=12+16
[url ] size=12+0, flags=1
location = [local to file]
[stbl] size=8+9600507
[stsd] size=12+176
entry_count = 1
[avc1] size=8+164
data_reference_index = 1
width = 1920
height = 1080
compressor =
[avcC] size=8+78
Configuration Version = 1
Profile = High
Profile Compatibility = 0
Level = 40
NALU Length Size = 4
Sequence Parameter = [67 64 00 28 ac 3b 50 3c 01 13 f2 c2 00 00 03 00 02 00 00 03 00 3d 08]
Picture Parameter = [68 ee 3c e1 00 42 42 00 84 84 04 4c 52 1b 93 c5 7c 9f 93 f9 3f 27 c9 e6 e4 c9 24 2c 22 42 90 9c 9e 4f af c9 fd 7e 4f af 27 26 a4 c0]
[stts] size=12+2400004
entry_count = 25356
[stss] size=12+1200004
entry_count = 846
[stsc] size=12+3600004
entry_count = 25356
[stsz] size=12+1200008
sample_size = 0
sample_count = 25356
[stco] size=12+1200239
entry_count = 25356
[trak] size=8+8400576
[tkhd] size=12+80, flags=3
enabled = 1
id = 2
duration = 1690250
width = 0.000000
height = 0.000000
[edts] size=8+40
[elst] size=12+28
entry_count = 2
entry/segment duration = 49
entry/media time = -1
entry/media rate = 1
entry/segment duration = 1690250
entry/media time = 0
entry/media rate = 1
[mdia] size=8+8400428
[mdhd] size=12+20
timescale = 90000
duration = 152118923
duration(ms) = 1690210
language = und
[hdlr] size=12+33
handler_type = soun
handler_name = AudioHandler
[minf] size=8+8400343
[smhd] size=12+4
balance = 0
[dinf] size=8+28
[dref] size=12+16
[url ] size=12+0, flags=1
location = [local to file]
[stbl] size=8+8400283
[stsd] size=12+40
entry_count = 1
[alaw] size=8+28
data_reference_index = 1
[stts] size=12+2400004
entry_count = 16903
[stsc] size=12+3600004
entry_count = 14718
[stsz] size=12+1200008
sample_size = 0
sample_count = 16903
[stco] size=12+1200167
entry_count = 14718

三、总结 

在嵌入式产品(如行车记录仪、监控摄像头、工业数据采集器)中,采用**固定大小(Pre-allocation/Fixed-size)**的方式将 MP4 文件存储在 SD 卡上,是一项非常硬核且实用的工程设计。

相比于普通电脑上那种“写多少占多少”的动态存储,这种方式有以下几个核心优势:


1. 彻底解决“磁盘碎片”问题 (Anti-Fragmentation)

SD 卡(特别是存储类卡)最怕频繁的碎片化写入。

  • 普通写入:文件随着写入不断变大,文件系统(如 FAT32/exFAT)需要不断寻找空闲的簇。如果卡内空间不连续,文件就会散落在各个角落,导致写入效率骤降。

  • 固定大小:在录制开始前就直接申请好一个例如 256MB 的大空间。后续的写入只是填充这块已有的“空坑”,不涉及新簇的分配。这能保证文件在物理扇区上是绝对连续的。

2. 规避文件系统(FAT/Table)损坏

这是嵌入式产品最容易“坏卡”的原因。

  • 动态写入:每写一小段数据,就要更新一次 FAT(文件分配表)或目录项,以记录文件长度的变化。如果此时突然断电,FAT 表还没写完,整个文件甚至整张卡都会变成“未格式化”。

  • 固定大小:文件头(Size/Offset)在初始化时就定死了。录制过程中,文件系统不需要频繁修改 Metadata,只需要进行 裸块(Raw Block) 写入。即使断电,也只是文件尾部的数据没写进去,已有的结构还是完整的。

3. 实现“极速覆盖”循环录像 (Seamless Looping)

对于需要 24 小时循环录影的产品,这个方案优势极大:

  • 传统方式:卡满后,必须先删除旧文件(涉及大量的擦除和文件系统更新),再创建新文件。这个过程会有明显的“掉帧”或“延迟”。

  • 固定大小方案:卡内是一堆固定大小的“槽位(Slots)”。当存储满时,系统直接从 Slot 1 的头部开始覆盖写入,不需要执行“删除”指令。

4. 降低 CPU 和 IO 的瞬时开销

在嵌入式实时操作系统(RTOS)中,资源非常紧缺。

  • 查找开销:动态分配需要 CPU 不断去计算:“下一个空块在哪?”。

  • 零计算:固定大小文件下,逻辑地址到物理地址的映射是固定的。代码只需要做一个简单的加法:Next_Address = Current_Address + Offset,极大节省了系统资源。

5. 延长 SD 卡寿命 (Wear Leveling Optimization)

虽然 SD 卡内部有磨损均衡逻辑,但频繁更新文件系统的元数据区(FAT 表区域)会导致该特定区域反复擦写。固定大小方案减少了元数据区的负担,让擦写更均匀地分布在数据区,变相延长了卡的使用寿命。

                                                                                                               —引用自gemini

注:博主本人借用了开源库,我就以开源库的做法来实现需求,可能大家有更好的方法,可以留言探讨,本方案经过本人亲自检验可行可靠,方案成熟稳定

赞(0)
未经允许不得转载:171主机测评 » 嵌入式产品MP4如何实现生成固定大小文件
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址