Sony Creators' App 实况照片(Motion Photo)文件原理分析

本文主要借助我本地的 file, xxd, ffprobe, ffmpeg, python3 等程序分析的结果以及 AI 的力量,仅供参考。实况照片(Motion Photo)素材使用昨晚生成的 C0076.MP.JPG 文件进行分析。

文件: C0076.MP.JPG
来源: Sony A7C2 → Creators' App → 手机导出
原始素材: 4K 4:2:0 8bit 视频, 截取 3 秒片段
分析日期: 2025-06-26


1. 概述

Sony Creators' App 生成的实况照片采用的是 Google Motion Photo 格式规范。本质上是一张标准 JPEG 图片和一段 MP4 视频的直接拼接(concatenation),通过嵌入在 JPEG 中的 XMP 元数据来描述这个二合一容器的结构。

文件命名为 .MP.JPG(Motion Photo JPEG),对于操作系统而言它首先是一个合法的 JPEG 文件,因此任何看图软件都能显示静态画面。支持 Motion Photo 的相册应用(如 Google Photos、iOS Photos)会识别 XMP 元数据中的 Container:Directory 信息,在用户点击播放或长按时读取附在文件末尾的 MP4 视频数据并播放。


2. 文件整体结构

┌──────────────────────────────────────────────────┐
│                                                  │
│  JPEG 静态图像      1,088,582 bytes  (1.04 MB)      │
│  ├─ SOI  (FF D8)   — JPEG 起始标记                 │
│  ├─ APP1            — EXIF / XMP 元数据段            │
│  │   └─ XMP: Camera:MotionPhoto, Container:Directory │
│  ├─ APP2            — ICC Profile (sRGB)            │
│  ├─ 图像数据          — 3840×2160, 8-bit, Baseline  │
│  └─ EOI  (FF D9)   — JPEG 结束标记                 │
│                                                  │
├──────────────────────────────────────────────────┤
│                                                  │
│  MP4 视频           16,869,480 bytes  (16.09 MB)    │
│  ├─ ftyp box         — XAVC brand                  │
│  ├─ moov box         — 元数据 (track info)          │
│  ├─ mdat box         — 媒体数据                     │
│  │   ├─ Stream 0:  H.264 视频 (3840×2160, 29.97fps) │
│  │   ├─ Stream 1:  PCM 音频 (48kHz, stereo, 16bit)  │
│  │   └─ Stream 2:  Timed Metadata (Sony RTMD)      │
│  └─ ...                                            │
│                                                  │
└──────────────────────────────────────────────────┘

  总文件大小: 17,958,062 bytes (17.13 MB)
部分 字节偏移 大小
JPEG 静态图 01,088,581 1,088,582 bytes (1.04 MB)
MP4 视频 1,088,58217,958,061 16,869,480 bytes (16.09 MB)

3. XMP 元数据详解

嵌入在 JPEG APP1 段的 XMP 描述是整个 Motion Photo 机制的"索引"。完整内容如下:

<x:xmpmeta xmlns:x="adobe:ns:meta/">
    <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
             xmlns:Camera="http://ns.google.com/photos/1.0/camera/"
             xmlns:Container="http://ns.google.com/photos/1.0/container/"
             xmlns:Item="http://ns.google.com/photos/1.0/container/item/">
        <rdf:Description
            Camera:MotionPhoto="1"
            Camera:MotionPhotoVersion="1"
            Camera:MotionPhotoPresentationTimestampUs="1049000">
            <Container:Directory>
                <rdf:Seq>
                    <rdf:li rdf:parseType="Resource">
                        <Container:Item Item:Mime="image/jpeg"
                                        Item:Semantic="Primary"/>
                    </rdf:li>
                    <rdf:li rdf:parseType="Resource">
                        <Container:Item Item:Mime="video/mp4"
                                        Item:Semantic="MotionPhoto"
                                        Item:Length="16869480"/>
                    </rdf:li>
                </rdf:Seq>
            </Container:Directory>
        </rdf:Description>
    </rdf:RDF>
</x:xmpmeta>

关键字段说明

字段 说明
Camera:MotionPhoto 1 标记此文件为 Motion Photo(动态照片)
Camera:MotionPhotoVersion 1 规范的版本号
Camera:MotionPhotoPresentationTimestampUs 1049000 (1.049 秒) 封面帧在视频时间轴中的微秒级时间戳,用于播放停止时精确定位到静态图对应的帧
Container:Directory (Item 0) MIME: image/jpeg
Semantic: Primary
第一个元素:静态封面图片,相册默认显示
Container:Directory (Item 1) MIME: video/mp4
Semantic: MotionPhoto
Length: 16869480
第二个元素:嵌入式视频,Length 指定视频数据的字节数,Semantic 标识为动态照片视频

4. 静态 JPEG 部分

通过 ffprobe 分析整体文件时,它仅识别到 JPEG 部分(因为 JPEG 是第一个合法格式):

属性
编解码器 Motion JPEG (MJPEG)
分辨率 3840 × 2160 (4K UHD)
宽高比 16:9
色彩空间 BT.470BG (sRGB equivalent)
像素格式 yuvj420p (full range)
位深 8-bit
文件内大小 1,088,582 bytes

ffprobe 仅报告 1 frame (duration 0.04s), 因为它只解析了 JPEG 头部就停止了——标准 JPEG demuxer 不会去读文件尾部拼接的 MP4 数据。


5. 嵌入式 MP4 视频部分

将文件从偏移 1,088,582 处切出,得到独立的 MP4 文件。ffprobe 完整解析如下:

5.1 容器格式

属性
容器格式 QuickTime / MOV
Major Brand XAVC
兼容品牌 XAVC, mp42, iso6
总时长 2.002 秒
文件大小 16,869,480 bytes (16.09 MB)
总码率 67.41 Mbps

5.2 视频流 (Stream #0)

属性
编解码器 H.264 / AVC (High Profile, Level 5.1)
分辨率 3840 × 2160
帧率 30,000/1001 ≈ 29.97 fps
总帧数 60 帧
时长 2.002 秒
像素格式 yuv420p (TV range, 即 16-235)
色彩空间 BT.709
码率 60.14 Mbps
位深 8-bit
GOP 结构 含 B 帧 (has_b_frames=1)
编码器 AVC Coding

5.3 音频流 (Stream #1)

属性
编解码器 PCM signed 16-bit big-endian
采样率 48,000 Hz
声道 立体声 (2ch)
位深 16-bit
码率 1,536 kbps (48k × 2 × 16 = 1,536,000)
总采样帧数 96,096

这个 PCM 音频流解释了为什么播放实况照片时 有声音——它不是无声音轨,而是完整的未压缩 CD 级别立体声音频。

5.4 元数据流 (Stream #2)

属性
类型 Timed Metadata
Codec Tag rtmd (Sony Real-Time Metadata)
码率 4.66 Mbps
帧数 60 (与视频帧一一对应)
Timecode 00:03:58:00

这是索尼的专有 RTMD 轨道,通常携带每帧的拍摄参数(焦距、曝光等),A7C2 录制时会写入此轨道,Creators' App 导出时保留。


6. 工作原理问答

Q1: 为什么相册直接看是一张静态照片?

.MP.JPG 的文件头是标准的 JPEG SOI 标记 (FF D8),开头 ~1MB 是完全合法的 JPEG 数据。文件管理器/相册根据扩展名和文件头将其识别为 JPEG 图片,解码并显示 3840×2160 的静态图像。文件尾部的 MP4 数据被忽略(JPEG decoder 读到 FF D9 EOI 标记后即停止)。

Q2: 为什么又会显示"动态图片"标识?

支持 Motion Photo 的相册(如 Google Photos、iOS Photos)会深度解析 JPEG 的元数据段(APP1/EXIF/XMP)。当发现 Camera:MotionPhoto="1"Container:Directory 时,它就知道这不是普通照片,而是在末尾附带了视频数据,于是在 UI 上显示"动态图片"角标或播放按钮。

Q3: 为什么点击播放按钮后能播放视频,甚至包含声音?

点击播放时,相册 app 读取 XMP 中的 Item:Length="16869480",直接从文件尾部向前取 16,869,480 字节作为 MP4 数据进行解码。这个 MP4 是完整的 XAVC 容器,包含:

  • H.264 视频流 → 画面可播放
  • PCM 音频流 → 声音也能播放

手机系统播放器同时解码视频和音频轨道,用户就能看到带声音的 2 秒短视频。

Q4: 静态封面图和视频是什么关系?

XMP 中的 MotionPhotoPresentationTimestampUs="1049000"(1.049 秒)指示了封面帧在视频中的时间位置。播放停止或反向定位时,播放器可以精确跳到这一帧,保证从"静态观看"到"动态播放"的视觉过渡无缝。

需要注意的是,JPEG 封面图是 独立编码 的(不是从视频中抽取的),它有自己的色彩空间 (BT.470BG full range, yuvj420p),而视频是 BT.709 TV range (yuv420p),所以两者的色彩可能略有差异。


7. 拆分/提取命令

将 Motion Photo 拆分为独立的 JPEG 和 MP4:

# 提取 JPEG 静态图
python3 -c "
data = open('C0076.MP.JPG', 'rb').read()
jpeg_end = data.find(b'\xff\xd9') + 2
with open('still.jpg', 'wb') as f:
    f.write(data[:jpeg_end])
"

# 提取 MP4 视频(含音频)
python3 -c "
data = open('C0076.MP.JPG', 'rb').read()
mp4_start = data.find(b'\xff\xd9') + 2
with open('video.mp4', 'wb') as f:
    f.write(data[mp4_start:])
"

也可以用 ffmpeg 播放/转码嵌入的视频(前提是提取后或直接用 -skip_initial_bytes):

# 直接播放嵌入式视频
ffplay -skip_initial_bytes 1088582 C0076.MP.JPG

# 直接转码嵌入式视频为 H.265
ffmpeg -skip_initial_bytes 1088582 -i C0076.MP.JPG -c:v libx265 output.mp4

8. 与 Apple Live Photos 的对比

特性 Google Motion Photo (本文件) Apple Live Photos
容器结构 JPEG + MP4 拼接 HEIC + MOV 拼接 (或 JPEG + MOV)
封面图格式 JPEG HEIC (或 JPEG, 旧版)
视频格式 MP4 (H.264) MOV (H.264 / HEVC)
音频 PCM uncompressed AAC (通常)
元数据位置 JPEG APP1/XMP 段 HEIC/JPEG EXIF + MOV metadata
视频长度 可变 (本文: 2.0s) 固定 3 秒 (拍摄前后各 1.5s)
标准制定方 Google Apple

两者的核心思想一致:封面图 + 附尾视频,用元数据桥接。Sony 选择 Google Motion Photo 标准是因为 Android 生态原生支持,且该规范对第三方更开放。


9. 总结

Sony Creators' App 导出的 .MP.JPG 文件本质上是一个"二合一捆绑包":

  1. 静态 JPEG(~1 MB)→ 负责预览/相册展示
  2. 嵌入式 MP4(~16 MB)→ 负责动态播放
  3. XMP 元数据 → 充当两者之间的"目录索引"

这种设计巧妙的利用了 JPEG 格式的可扩展性(APPn 段可携带任意数据)和大部分文件系统对 JPEG 的原生支持,在"向下兼容"的同时提供了动态照片体验。对于不支持 Motion Photo 的软件,它就是一个普通的 4K 分辨率 JPEG 照片;对于支持的软件,它是一个带有 CD 级音质的 2 秒短视频。


分析工具: file, xxd, ffprobe, ffmpeg, python3

添加新评论