Monkey takes a .heic
山间回荡着H.265压缩的活力!
随着iOS 11和macOS High Sierra(10.13)的发布,苹果引入了一种名为高效图像文件格式(HEIF - 据说发音为"heef")的文件容器格式。苹果使用HEIF来存储相机/视频/苹果"实况照片"。HEIF基于多个标准:
- 用于文件容器内数据部分结构的ISO基础媒体文件格式(14496-12)
- 用于压缩实际静态图片和视频数据的ISO/IEC 23008-12 MPEG-H第2部分/ITU H.265。也称为高效视频编码(HEVC)。理论上,HEIF可以使用其他压缩算法,但苹果专门将其与HEVC/H.265一起使用。
HEIF的一些优点包括:
- 在给定的图像/视频质量下,文件大小大约减半
- 允许单个文件包含多个媒体(例如多个动画静态图片和声音,如苹果"实况照片")
苹果HEIF图像将具有.heic文件扩展名。苹果HEVC编码的电影将具有熟悉的Quicktime .mov扩展名,但内部将使用HEVC/H.265压缩。ISO基础媒体文件格式(14496-12)基于Quicktime文件结构,因此它将适用于.heic图像和HEVC .mov文件。
由于它使用比先前标准(例如H.264和JPEG)更复杂的压缩算法,只有最近的苹果设备型号具有创建HEVC内容所需的硬件。
根据苹果2017年WWDC演示503"引入HEIF和HEVC",要创建HEVC图片/视频,您需要(至少)运行iOS 11的iPhone 7/iPad Pro(A10 Fusion芯片)或运行macOS 10.13 High Sierra的第六代Intel Core处理器。
软件解码支持显然适用于所有苹果设备(大概运行iOS 11/High Sierra),但在旧硬件上播放性能可能会受到影响。
在本文的其余部分,我们将讨论:
- 如何查看.heic和HEVC .mov文件
- .heic文件的文件格式
- HEVC .mov文件的文件格式
我们不会讨论HEVC/H.265压缩的工作原理。有关一些基本概念以及H.264和H.265之间差异的快速概述,请观看此视频。
在我们深入探讨之前…
特别感谢来自Teeltech USA的Maggie Gaffney为我们提供iPhone 8 Plus测试媒体文件。
我们还使用了来自Ars Technica评论(iPad Pro)的样本.heic文件和提供给FFmpeg论坛的样本文件(iPhone 7 Plus)。
查看与兼容性问题
这里有一篇文章展示了如何设置iOS 11设备以原始格式保存/传输.heic文件(相机设置为"高效"和"照片 - 传输到Mac或PC"设置为"保留原件")。当传输到不兼容的设备/目的地(例如PC或电子邮件)时,苹果可以自动将.heic文件转换为.jpg文件(将h.265 .mov转换为h.264 .mov)。因此,如果您没有收到.heic文件,请检查这些iOS设置。
除了在iOS或High Sierra上本地查看(例如使用Apple Photos或Preview)之外,我们发现查看.heic文件的最简单方法是使用@liuziangexit的免费Windows HEIF实用程序。
还有一个将.heic转换为.jpeg的网站,但这可能不适用于敏感照片。
对于播放HEVC/H.265编码的.mov文件,我们发现IrfanView和VLC播放器工作正常(在查看高分辨率视频时,IrfanView的性能似乎比VLC更好)。
FFmpeg(v3.3.3)也可用于从H.265 .mov中屏幕抓取帧(每秒1帧)。命令是:
1
|
ffmpeg.exe -i sourcemovie.mov -vf fps=1 outputframe_%d.png
|
这将导致"outputframe_1.png"、“outputframe_2.png"等被生成到当前目录。
为了更兼容的播放,我们可以将H.265 .mov转换为H.264 .mov。命令是:
1
|
ffmpeg.exe -i source265movie.mov -map 0 -c copy -c:v libx264 outputmovie264.mov
|
这将所有其他流(例如音频、字幕)复制到新的输出文件,并将源视频流重新编码/输出为H.264。有关使用FFmpeg map参数的详细信息,请参见此处。
我们发现将测试.heic从iPhone发送到PC的最简单方法是将其上传到Dropbox,该服务已更新以支持.heic和H.265编码的.mov文件。您可以从Dropbox.com网站查看.heic和.mov文件。不幸的是,Dropbox似乎在上传时重命名了文件。我们期望看到类似"IMG_4479.heic"的内容,但Dropbox上的文件名是类似"Photo Oct 08, 10 20 05.heic"的内容。因此,可能需要源/目标文件的哈希比较来验证精确副本。
Exiftool(v10.63)增加了对HEIF的改进支持,它将显示苹果生成的.heic或H.265 .mov文件的EXIF数据。尚未确认iOS创建的.heic/HEVC .mov文件在自动转换为.jpg/H.264文件后是否会保留所有原始EXIF元数据。
我们无法找到HEVC编码"实况照片"的非苹果查看器。尝试通过Dropbox传输它们导致"实况照片”.heic文件包含单个图像(没有声音或其他动画)。抱歉,没有"实况照片"给您!
文件结构
既然我们知道如何查看iOS创建的HEIF图像和视频,让我们仔细看看实际的文件格式。
这将是一个(合理的?)简短概述 - 我们不会成为"数据受虐狂"并深入研究每个字段或压缩方面。也许在未来的帖子中(特别是如果您是一个坏的、坏的、肮脏的、肮脏的猴子并感到需要被惩罚LOL)…
苹果创建的.heic和.mov文件是BIG Endian。
.heic和.mov文件都基于ISO基础媒体文件格式。这意味着.heic或.mov文件容器被划分为数十个功能"盒子"的数据。每个盒子的开头将以4字节的盒子大小(通常)和四字节的盒子类型字符串(例如’ftyp’、‘mdat’、‘meta’)标记。在盒子内,将有其他数据字段,这些字段可能包括其他盒子和/或结构化的字节模式。因此,存在盒子内盒子的复杂层次结构,这使得快速理解每个细节变得困难。大部分字节(即压缩数据)将存储在"mdat"盒子中。其他盒子将用于存储关于如何访问/处理那些"mdat"盒子中数据的元数据。
有关这些盒子如何结构的进一步细节,请参考ISO基础媒体文件格式标准。它和Quicktime电影格式文档将是本节的最佳朋友。仅供参考,ISO基础媒体文件格式也用于.mp4和.3gp文件 - 因此学习这种格式将有助于理解多种类型的媒体文件。
其他方便的参考包括:
- Lasse Heikkila的HEIF实现论文的第3章
- Nokiatech HEIF Github站点
- 2017年苹果WWDC HEIF演示(遵循转录和幻灯片PDF链接)用于HEIC文件格式和HEIF和HEVC介绍。
对于苹果iPhone 8 Plus .heic文件(包含单个4032 x 3024图像),文件结构可以如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
ftyp (size=0x18, majorbrand = 'heic', minorversion = 0, compatiblebrands = mif1, heic)
meta (size = 0xF74)
hdlr (size = 0x22, handler_type is "pict" i.e. file is an image)
dinf (size = 0x24)
pitm (size = 0xE, item_ID = 0x31 = primary item)
iinf (size 0x43D, entry_count = 0x33 = number of items stored)
infe = ItemInfoEntry, size = 0x15, version = 2, item_ID = 0x1, item_type = hvc1, item_name = "" [Tile 1]
infe = ItemInfoEntry, size = 0x15, version = 2, item_ID = 0x2, item_type = hvc1, item_name = "" [Tile 2]
...
infe = ItemInfoEntry, size = 0x15, version = 2, item_ID = 0x30, item_type = hvc1, item_name = "" [Tile 30]
infe = ItemInfoEntry, size = 0x15, version = 2, item_ID = 0x31, item_type = grid, item_name = "" [derived image from all tiles]
infe = ItemInfoEntry, size = 0x15, version = 2, item_ID = 0x32, item_type = hvc1, item_name = "" [thumbnail]
infe = ItemInfoEntry, size = 0x15, version = 2, item_ID = 0x33, item_type = Exif, item_name = "" [EXIF]
iref (size = 0x94, version = 0, contains array of SingleItemTypeReferenceBox)
dimg (size = 0x6C, from_item_ID = 0x31, reference_count = 0x30, to_item_ID = 0x1, 0x2 ... 0x30) [derived image]
thmb (size = 0xE, from_item_ID = 0x32, reference_count = 0x1, to_item_ID = 0x31) [thumbnail]
cdsc (size = 0xE, from_item_ID = 0x33, reference_count = 0x1, to_item_ID = 0x31) [content description ref / exif]
iprp (size = 0x6F3)
ipco (size = 0x5AD) = ItemPropertyContainerBox = property data*
colr (size = 0x230) = Colour Information 1
hvcC (size = 0x70) = decoder configuration 1
ispe (size = 0x14) = spatial extent 1-1
ispe (size = 0x14) = spatial extent 1-2
irot (size = 0x9) = Image rotation 1
pixi (size = 0x10) = Pixel information 1
colr (size = 0x230) = Colour Information 2
hvcC (size = 0x70) = decoder configuration 2
ispe (size = 0x14) = spatial extent 2-1
pixi (size = 0x10) = Pixel information 2
ipma (size = 0x13E) = Item Property Association = connects property data in ipco to item numbers*
List of 0x32 items. Each item has the structure [item number(2 bytes), size (1 byte), data (size bytes)]
idat (size = 0x10)
iloc (size = 0x340, version = 1, offset_size = 4, length_size = 4, base_offset_size = 0, index_size = 0, item_count = 0x33 )
[item_id = 0x1, file offsets used, base_offset = 0, extent_count = 0x1, extent_offset = X1, extent_length = Y1]
[item_id = 0x2, file offsets used, base_offset = 0, extent_count = 0x1, extent_offset = X2, extent_length = Y2]
...
[item_id = 0x33, file offsets used, base_offset = 0, extent_count = 0x1, extent_offset = X33, extent_length = Y33]
mdat (size = variable, contains data on EXIF / thumbnail / image data)
|
这看起来有点令人生畏(这甚至没有显示所有盒子/字段!),但一旦您弄清楚哪些字段相关,它就不算太糟。我们对一些部分进行了颜色编码,使其更易于跟踪/唤醒那些疲倦的眼睛。
ftyp部分将’majorbrand’(即文件类型)声明为"heic"。
meta部分声明如何解释存储在mdat部分中的原始数据。值得注意的子盒子包括:
hdlr = ‘handler_type’设置为"pict",这意味着这是一个图像(与视频相对)。
pitm = 指定主项目编号(例如item_ID 0x31)
iinf = 包含ItemInfoEntrys列表。项目的数量和大小将随分辨率/形状(例如相机规格、方形照片)而变化。从2017年WWDC 513演示和我们观察到的实际iOS样本中,图像被划分/存储为图块。
对于4032 x 3024分辨率图像,每个.heic文件中声明了0x33个项目。这些包括:
0x30个项目,每个item_type = ‘hvc1’。每个项目对应一个512x512图块。
1个’grid’项目代表完整的派生图像
1个’hvc1’项目用于320x240缩略图
1个’Exif’项目用于存储EXIF数据
iref = 包含SingleItemTypeReferenceBox项目数组。从本节中,我们可以看到item_ID = 31是一个派生图像(‘dimg’),它引用item_IDs 0x1到0x30(图块)。还有对缩略图和exif项目的引用。
iprp = 将’ipma’子节中的item_IDs连接到’ipco’子节中的属性。*我们无法找到关于如何实现这一点的公共文档(除了Nokia HEIF Github源代码)。
iloc = 包含每个item_ID部分的文件偏移量。例如,对于EXIF(item_ID = 0x33),extent_offset = 0x000043DB,extent_length = 0x000007F8。因此,如果我们转到文件偏移量0x000043DB,我们将看到EXIF项目数据。
mdat部分包含原始图像数据、缩略图和Exif信息。
由于分块,与恢复jpeg(您可以在0xFFD8和0xFFD9标记之间雕刻所有内容)相比,完整文件恢复可能更加复杂。
由于iOS 11还使用基于文件的加密,无论如何应该不可能雕刻和恢复.heic文件。
但是,如果那些.heic文件也被复制到单独的非加密设备(例如PC)然后损坏/删除,可能可以修复或恢复部分/所有图块(理论上!)。
好的,振作起来,黄油杯…因为还有更多!
这是一个使用iPhone 8 Plus拍摄的6.73秒7.3 MB苹果HEVC/H.265编码的.mov的文件结构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
ftyp (size = 0x14, majorbrand = 'qt ')
wide (size = 0x8)
mdat (size = 0x00746120, contains HEVC / H.265 video data)
moov (size = 0x0028FA)
mvhd (size = 0x6C, version = 0, creation_time = 0xD5FFE81E (secs since 1JAN1904), modification_time = 0xD5FFE825,
timescale = 0x0000258 = 600 dec. units per sec, duration = 0x00000FC7 = 4039 dec units => 4039/600 = 6.73 secs,
next_track_ID = 0x5)
trak (size = 0x0FE6)
tkhd (size = 0x5C, version = 0, creation_time = 0xD5FFE81E, modification_time = 0xD5FFE825, track_ID = 0x1,
duration = 0xFC7, width = 0x07800000 => 0x780 = 1920 decimal, height = 0x04380000 => 0x438 = 1080 decimal)
tapt (size = 0x44)
edts (size = 0x24)
mdia (size = 0xF1A) = media box
mdhd (size = 0x20, version = 0, creation_time = 0xD5FFE81E, modification_time = 0xD5FFE825, timescale = 0x258,
duration = 0xFC7)
hdlr (size = 0x31, component type = mhlr = media handler, component subtype = vide, component manufacturer = appl,
component name = "Core Media Video"
minf (size = 0xEC1) = contains file offsets to samples/chunks of samples
trak (size = 0x07B4)
tkhd (size = 0x5C, version = 0, creation_time = 0xD5FFE81E, modification_time = 0xD5FFE825, track_ID = 0x2,
duration = 0xFC7, width = 0, height = 0)
edts (size = 0x24)
mdia (size = 0x72C)
mdhd (size = 0x20, version = 0, creation_time = 0xD5FFE81E, modification_time = 0xD5FFE825, timescale = 0x000AC44 =
44100 samples/sec, duration = 0x00049000 = 299008 samples = 6.78 sec)
hdlr (size = 0x31, component type = mhlr = media handler, component subtype = soun, component manufacturer = appl,
component name = "Core Media Audio"
minf (size = 0x6D3) = contains file offsets to samples/chunks of samples
trak (size = 0x042E)
tkhd (size = 0x5C, version = 0, creation_time = 0xD5FFE81E, modification_time = 0xD5FFE825, track_ID = 0x3,
duration = 0xFC7, width = 0, height = 0)
edts (size = 0x24)
tref (size = 0x20)
mdia (size = 0x386)
mdhd (size = 0x20, version = 0, creation_time = 0xD5FFE81E, modification_time = D5FFE825, timescale = 0x258,
duration = 0xFC7)
hdlr (size = 0x34, component type = mhlr = media handler, component subtype = meta, component manufacturer = appl,
component name = "Core Media Metadata"
minf (size = 0x32A) = contains file offsets to samples/chunks of samples
trak (size = 0x0271)
tkhd (size = 0x5C, version = 0, creation_time = 0xD5FFE81E, modification_time = 0xD5FFE825, track_ID = 0x4,
duration = 0xFC7, width = 0, height = 0)
edts (size = 0x24)
tref (size = 0x20)
mdia (size = 0x1C9)
mdhd (size = 0x20, version = 0, creation_time = 0xD5FFE81E, modification_time = 0xD5FFE825, timescale = 0x258,
duration = 0xFC7)
hdlr (size = 0x34, component type = mhlr = media handler, component subtype = meta, component manufacturer = appl,
component name = "Core Media Metadata"
minf (size = 0x16D) => contains file offsets to samples/chunks of samples
udta (size= 0x08)
free (size = 0x400)
meta (size = 0x5BD)
hdlr (size = 0x22, component subtype = mdta)
keys (size = 0xC9) => contains various metadata field names
ilst (size = 0xCA) => contains various metadata field values
free (size = 0x400)
free (size = 0x88)
|
我们可以看到一些熟悉的4字母字符串(猜想您现在可能已经在念叨其他一些了…),并且偏移信息现在包含在’moov’部分(回想一下,偏移信息存储在.heic的’meta’/‘iloc’部分)。
此外,与.heic使用"项目"不同,电影被组织成trak(例如视频trak、声音trak)。这些’trak’盒子包括到’mdat’部分的文件偏移(通过’trak’/‘mdia’/‘minf’)。
‘moov’盒子有一个标记为’mvhd’的电影头原子。这显示了电影的长度和创建/修改日期(以及其他内容)。
记录了4个trak - 一个用于视频(track_ID=1),一个用于声音(track_ID=2)和两个用于元数据(track_ID=3和4)。第二个(较小的)元数据trak(track_ID=4)可能扩展了第一个(track_ID=3)元数据trak(由于空间限制?),因为元数据字符串不同但似乎相关。
‘free’标记可以忽略/跳过的盒子
‘meta’标记包含元数据的盒子,但是,来自.mov的’meta’结构不会匹配来自.heic图像的’meta’数据结构。大概Exiftool将从’trak’和’meta’盒子中获取元数据。
在其他观察到的H.265 .mov文件(包括较小和较大的)中,观察到多个’mdat’部分。这可能与’hoov’盒子的存在有关,我们找不到任何关于此的文档。‘hoov’盒子出现在比’moov’更低(更早)的文件偏移处,并且也包含’mvhd’和’trak’盒子等。
也许’hoov’盒子是以前的’moov’盒子,其名称被修改以便其数据可以被覆盖?例如,随着文件大小的增长,数据被重新写入?#推测猴子
最终想法
哦,我疼痛的皮裤!
而这篇文章只是触及了HEIF野兽的屁股表面。
HEIF比苹果目前实现的还有更多可能性。Nokiatech Github站点展示了一堆不同的图像文件可能性(例如单个图像、图像序列、高清电影、组合图像/视频)。
尽管我们无法捕获本机苹果"实况照片"进行检查,但我们怀疑它将使用序列.heics文件,并除了’ftyp’、‘mdat’和’meta’盒子外还有一个’moov’盒子。这在2017年苹果WWDC高效图像文件格式幻灯片的第60张幻灯片中有所展示。
macrumors.com的这篇文章指出,苹果"实况照片"最初由12 MP jpeg和45帧H.264 .mov组成,速度为15 fps(即3秒视频 = 按下按钮前后1.5秒)。
这个anandtech论坛文章指出"实况照片"是:
“在某些设备上是1440x1080 HEVC,尽管现在与HEIC图像配对而不是JPEG。还有选项将其保留为1440x1080 h.264与JPEG。”
无论如何,如果您能够捕获"实况照片"独角兽文件,我们将非常有兴趣了解其文件结构(留下评论?)。
更新15OCT2017:
对于"实况照片",我们尝试将运行iOS 11.0.3的iPhone 8直接连接到Windows 7 PC,并能够看到DCIM文件夹。iPhone 8设置为默认的"高效"照片,Mac/PC传输设置为"保留原件"。然而,在复制文件后,当我们在PC上查看传输的.heic文件结构时,它们是单个图像。没有’moov’或’trak’项目。因此,看起来iOS没有向非苹果设备公开其"实况照片"文件结构。嘘!:’(
最后,如果您知道任何其他易于安装/使用的.heic查看器或有任何想法/建议,请在评论中留言。