实况照片工具箱:从一个小工具到现在
- 1实况照片工具箱:从一个小工具到现在本文

如果不是因为我那台存储空间天天告急的 iPhone,我大概这辈子都不会去折腾各大手机厂商那些奇奇怪怪的实况照片协议。
写这篇博客,主要是想把 Live Photo Box 从最初几行自用脚本,一步步折腾成现在有 GUI、有 CLI,后来直接全部推倒,AI 手搓 C++ 底层的过程记录一下。时间线有点长,中间踩的坑也挺多,今天就当流水账唠唠嗑。
被 90° 旋转视频逼疯的起点
我平时用 iPhone,特别喜欢拍实况照片(Live Photo)。主要是因为会动,可能过个一两年在相册里翻到当年的老照片,按住它还能动起来,一下子就能把你拉回当时那个场景,很有生活感和记录感。
但我手机存储空间太小了,动不动就爆满,只能经常把照片倒到电脑或者移动硬盘里备份。结果一到 Windows 电脑上就傻眼了:根本看不了实况照片。
不仅看不了,苹果直接把一张实况拆成了一个 .HEIC 和 .MOV 视频。更搞人心态的是——那个视频在电脑上播放居然是向左旋转 90° 横着的!
后来去查底层原理才发现,苹果为了拍照速度快,录制视频时压根不花多余的算力在物理像素层做旋转处理,相机传感器抓到什么物理像素就直接存进去,横屏竖屏都一样。为了方向正确,它只在视频容器里塞了一个 rotation 标签,让播放器自己根据标签转过来。苹果自家的设备当然认,但 Windows 自带的播放器和很多看图软件根本不认,直接原样放出来。
每次想看点以前的生活记录,照片和视频不仅是割裂的,还得歪着脖子看视频,给我难受死了。
本来只想写个脚本,结果发现安卓阵营有各种私有协议
转机是有一次我偶然发现,我朋友的小米手机导出来的实况照片,在电脑上居然能正常显示成动态文件。
我赶紧去扒协议,才知道小米用的是谷歌的 Motion Photo 规范。我当时第一反应其实特别简单:写个命令行小工具,自己跑跑就行了。把苹果的实况转成谷歌协议,顺手把苹果那个旋转标签扒掉,把视频重编码一下,真正把像素给转正。
结果我越扒越头大,安卓阵营在实况照片这块简直是一团乱麻:
- 小米:基本遵循 Google 标准的 Motion Photo 规范,格式相对标准规范
- 华为 / 荣耀:自研的 Moving Photo 格式,深度定制的 HEIC/JPEG 私有容器与元数据标记
- OPPO / vivo:各自有独立的私有 XMP 命名空间和封装结构,互不相通
- 三星:采用独家的 SEF 结构把动态视频塞在文件尾部
我当时就想,这肯定不只是我一个人的痛点,换手机或者喜欢冷备份到电脑的人肯定都得被恶心一遍。既然都要搞,干脆搞个桌面端软件,让这些格式能随意互转,而且尽可能保留原始细节。
当时我问了下 ChatGPT 技术选型,最后敲定了第一代的方案:
- 核心框架:C# + .NET
- UI 界面:WinUI 3
- 元数据读写:ExifTool
- 视频像素转正:FFmpeg
- 无损图片旋转:jpegtran(专门用来给苹果擦屁股的,无损处理 JPG)
干了一两个月,MVP 版本做出来了,成功打通了苹果和谷歌协议的双向互转。又过了一个多月,我加上了苹果实况照片的修复功能,能自动识别照片和视频哪些部分的真实像素和 rotation 标签对不上,一键自动修正。
当时看着原本各种实况照片混杂在相册里面,到现在能单独分开查看了,心里还是挺满意的。
封面帧丢了,天天找朋友借手机借到尴尬
但是一个程序员用自己的软件,眼里只有新 Bug 😂
用着用着我又发现了一个大问题:为什么转完格式的实况照片,导回手机相册一点编辑,封面帧永远卡在第 0 帧?
实况照片按快门的那一瞬间,往往是在视频中间(比如第 1.2 秒),那才是抓拍得最好的一帧。结果格式转换之后,封面帧的时间戳丢了,直接回退到了最开头。要是拍的是运动场景,第 0 帧大概率是模糊的起手动作,整张照片直接垮掉。
意识到这个问题后,我开始死磕各家协议里的封面帧机制。因为我手里就一台 iPhone,为了测各种安卓机型,我那阵子天天找身边朋友借手机测试。借到后面,只要我拿着电脑往他们走过去,话还没说,他们就知道我要干嘛了,直接把手机解锁递过来:“拿去拿去,又测实况照片是吧😂”。
搞懂了机制后,我发现光在后台转不行,用户得有个地方能自己看、自己选封面帧。于是我又开发了一个全新的独立页面——实况照片编辑页:
- 文件夹浏览:直接在界面里批量看当前目录下的实况照片
- 精准选帧:拖动时间轴随意挑选封面帧并实时预览
- 自由拆分与导出:能对单个实况照片进行静态帧抽取、视频分离导出
做完之后发布了 2.0 版本。不过这里也必须吐槽一下微软:WinUI 3 的 ListView 组件性能是真的拉跨,文件稍微多一点,缩略图滚动和拖时间轴就会有点卡卡的。但当时手头确实没有更合适的新 UI 框架可用,只能先忍着,后面再想办法填坑。
人家放假吃喝玩乐,我回国天天呆手机店逆向
短学期结束后,我放假回国。
之所以急着回国,是因为我在马来西亚上学,国外校园里几乎找不到什么人用华为和荣耀的新机型,这两家的协议我一直没法实机闭环。
回国那几个星期,别人朋友圈全在到处玩、吃喝打卡。我呢?每天吃了午饭就往各种商场的手机体验店钻。站在华为和荣耀的样机展台前,疯狂拍实况,拿出我的一加手机导入过来,当场在店里解剖容器。店员看我的眼神,逐渐从推销变成了警惕哈哈哈,我都忘不了那个眼神。
但说实话,逆向这种私有协议是真的会上瘾。
华为的 Moving Photo 是我遇到过最难啃的骨头。网上几乎没有任何现成资料,我只能找工具把它的容器一点点扒开容器,看里面的数据,猜哪一段是视频偏移,哪一段是实况相关的私有 Tag。
很快我就发现,之前用的 ExifTool 在华为面前完全没用。华为的 HEIC 容器非常特殊,里面塞了大量自己的私有结构,ExifTool 去处理不是写错就是写不进去,甚至连读取都能读错。
开源工具既然靠不住,那就只能自己上手了。我硬是让 AI 用纯 C# 在二进制流层面,一个字节一个字节地把华为和荣耀的私有元数据写入逻辑给手搓了出来。
其实中间还有一个巨恶心的插曲:华为协议刚通的时候,手机相册能识别了,也能正常播放实况。但只要一点编辑,时间轴上封面的位置永远是第一帧,为了找它底层的帧索引和时间戳算法,我整整被卡了一个星期,最后才在一个隐藏很深的私有 Tag 里面找到了那串纳秒级的数据。华为攻克之后,同源的荣耀自然也就搞定了。
懒得开界面,顺手搓了个 CLI 模式
在频繁测试各家协议的那段时间,反复“打包软件 -> 点开界面 -> 拖文件 -> 点击转换 -> 看日志”这一套流程快把我折磨吐了。每次调个小逻辑,在 UI 上点来点去极度费时间,不如搞个命令行方便。
说干就干,在后来的 2.1.1 版本里,我正式上线了 CLI 命令行模式:
- 自动化批量处理:终端一行命令直接跑转换、修复、封面帧提取
- 全功能无缝对齐:GUI 里能干的事情,CLI 传参数全都能搞定
- 内置实用维护工具:查看实况照片内部元数据、检查版本更新、切换更新通道
本来只是为了自己测试偷懒,结果发出去之后,不少搞自动化脚本和 NAS 整理的小伙伴还挺喜欢这个功能的。
ExifTool 拖后腿,下决心推倒重写底层
随着支持的品牌越来越多,我又盯上了下一个硬骨头:HDR 保留。
现在的手机拍照片基本都自带 HDR 增益图(Ultra HDR / Apple Gain Map)。如果转完协议后照片虽然能动了,但原本很亮的 HDR 高光全没了,那体验大打折扣。
但在尝试提取和保留 HDR Gain Map 的时候,ExifTool 彻底成了最大的瓶颈:
- 性能低下:每张照片都要开一次外部子进程,批量跑几百张时 CPU 全在调度进程,I/O 开销极其严重
- 稳定性差:碰到复杂的 HEIC Aux Image 和 HDR 映射表,外部工具频繁读错写错,容易损坏原图
- 体积臃肿:为了打补丁塞了一堆第三方工具,安装包体积飙升
当时马上就要开学了。那天我躺在床上想了一晚上:算了,要做就干脆做好一点,别再缝缝补补打补丁了。
我决定把核心底层全部推倒重构!全部迁移到 C++ Native 核心手搓:
- 统一中间格式抽象:不管输入的源文件来自哪家厂商,统一先解析成规范的中间层数据模型(图像、视频、时间戳、HDR 增益图完全解耦),再按需组装写入
- 容器盒子精准替换:手搓 ISO BMFF(MP4/MOV/HEIC)与 JPEG 标记段解析器,只对实况照片涉及的 box 进行外科手术式替换,相机原本的色彩配置、HDR 增益图一律不动,原汁原味写回
我知道这条重构的路很长,天天对着二进制数据排错也挺枯燥的,但一旦做完,整套架构会干净、纯粹得多。
暴涨 30 倍的惊喜
最近这段时间,我基本上天天都在忙着把旧逻辑往 C++ Native 核心上迁移。
前段时间我把重构后的提取与清洗模块拼出了一个测试版,跑了跑性能对比,结果直接把我给惊到了:
重构完成的部分,处理速度整整比以前快了 30 倍!
以前处理一批照片还要看着进度条转半天圈,现在几百张丢进去几乎是瞬间刷完,CPU 也不用反复唤醒外部进程了,内存占用稳得不行。看到这个结果的时候,前阵子天天熬夜搞逆向的疲惫真的一扫而空,信心和动力直接拉满了,嘿嘿。
目前底层地基已经搭得差不多了,这几天正在啃最核心的协议转换和 Native 写入部分。如果顺利的话,最快一个月左右,全新的重构版本就能正式和大家见面了。
搞定收工
回头看这个项目,最开始真就只是我自己被那个横过来的 90° 视频折磨得不行,随手写的一个自救脚本。
没想到越陷越深,一路从命令行搞出 GUI,又因为各种厂商私有协议一路逆向手搓,最后硬生生逼得自己把底层推倒重写。
虽然各家厂商都有自己的格式小算盘,但对我们普通用户来说,照片能动起来的那一刻,能把当时那个场景还原出来,这就够了。那些被格式孤岛拆散的照片,能被完完整整地救回来,而且过个几年打开看依然能动,折腾这么久就挺值的。
新版本等我搞定,我们一个月后见!
如果你也在用 Live Photo Box,或者觉得这个项目还挺有意思,欢迎去 GitHub 看看,顺手点个 Star ⭐ 对我来说真的挺有动力的,也能让我知道还有人在等这个项目继续更新 OωO!
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!










