我先說點不相干的,一個是感覺我今年很能寫,這跟我換了一台新的高配 16” Mac 關係很大(之前在兩台電腦上同步真的會降低寫東西的熱情),二個是買了兩台很好的顯示器(老家和上海都有了),然後外接我的鍵鼠,哇,別提多舒服了。

前幾天,就在我在青海西北部剛得到聯不通的信號📶的時候,我看見一條 tweet 說 Google Photos 更新了。哇,它做了一個我很想要的功能,那就是 GeoTag Heatmap。我之前準備用 Flickr 和 Google Map 提供的一些 API 自己做一個,但是沒有時間琢磨。這下好了,我反正兩個 Google 帳號是分開的,所以我的私人照片和公開照片都可以有這個很強的功能了。

Google Photos 在 iOS 上直接上傳的話,其實是支持 Live Photo 的。這個功能我覺得還是很有意思的,而且如果用電腦來做的話,畫質會很棒的。我就研究了一下如何創建一個高質量的 Live Photo。

目前 App Store 裡有不少 App 可以在手機上直接把視頻(+自定義照片)轉換為一張 Live Photo,但是,我試用一下,發現它們各有各的問題,總結一下就是:

  • 完全遺失 Exif 等 Metadata,這樣日期、地點都沒了(地點沒了我還怎麼用 Google 的神奇新功能呢❓)

  • 視頻都是 re-encoding 過的,參数亂搞,有的強制 30fps,有的 bit rate 隨心所欲,VFR 和 CFR 也是很隨意

  • 對於照片通通都是 JPEG 輸出,會莫名其妙的參数壓縮,視頻也是都是 AVC (H.264)

總之,寫這些 App 的人,一定都沒有正確處理照片和影片的經驗。其實回顧一下,這個要求挺高的,看看 Android 的手機,就會發現幾乎全部中國廠商都沒有正確 Tag 這些信息,這說來話長也涉及很多知識。所以 iPhone 真是偉大的手機,我也很興奮看到一些國內開發者在做一些「專業」App 時是真的懂這些東西的,比如最近的 ProTake,真的很棒。


說正題,經過昨天一晚上試驗和學習,我得出,完美的解決方案一定需要親自寫一個軟件,調用 QuickTime 來生成視頻(尤其是對於 HEIC+HEVC)。但是目前,利用現有工具,我有一個很不完美的方案,可以出 JPEG+AVC,圖完全不改変,視頻在 1080P 下近乎完美。

預備知識

一個 Live Photo 包含相同文件名的 .JPG.MOV 文件。除了文件名相同,它們還有一個奇妙的 Metadata,這會讓系統認為他們是一起的。在 JPEG 中,會有一段 Apple - ContentIdentifier,在 MOV 中,會有一段 com.apple.quicktime.content.identifier,這兩個 UUID 一致的時候,合體就這樣發生了!

比較難辦的是 MOV,畢竟是水果的特殊信息,很多開源工具都不能寫入這個信息,我試過 FFmpegExifTool 等,都不行。我看了下 Mac 上的現有軟件,都是調用 QuickTime,比如這個 LoveLiver,所以完美方案還是需要自己寫 Mac/iOS 的代碼。

目前方法

目前,我找到一個 App,叫做 LiveStudio(居然是我 4 年前買的),它會保持 frame rate 和 resolution,缺點就是會轉成 VFR(這是 iOS default,問題不大吧),以及給 1080P~14Mbps4K~56Mbps。這個 bit rate 太高了,所以用 4K 文件會很大,2K 的話 5s 在 10MB 左右,合理!好處大概是,反正視頻也很短,配上這種 bit rate,默認參数壓縮畫質也很難変差。所以,把視頻先導入手機,用這個 App 生成一個 Live Photo。

好,現在 Live Photo 導入到 Mac 上,拿 MediaInfo 之類的就可以看到這個 UUID 是多少了,或者直接看它給出的 JPEG 裡面的,用 exif.cn 什麼都行。現在需要往自己的「高清」JPEG 裡寫入這個 UUID。我覺得,用 Python3 最方便不過了,首先安裝 piexif 這個庫,我用的 Python3.7 一切正常。這個 UUID,在 Exif 的 37500 這個位置。類似於 37500: b'Apple iOS\x00\x00\x01MM\x00\x01\x00\x11\x00\x02\x00\x00\x00%\x00\x00\x00 \x00\x00\x00\x003E1CB85B-98DC-4B34-96DA-0A1EB6BA3172\x00\x00'

祖傳代碼來了:

1
2
3
4
5
6
7
8
9
10
11
src = "~/Downloads/src.jpg"
dst = "~/Downloads/dst.jpg"

import piexif

exif_dict_src = piexif.load(src)
exif_dict_dst = piexif.load(dst)

exif_dict_dst["Exif"].update({37500: exif_dict_src["Exif"][37500]})
exif_bytes = piexif.dump(exif_dict_dst)
piexif.insert(exif_bytes, dst)

就這麼簡單,這個 UUID 就被插入。而其他所有信息都保持不変,圖片的 bytes 也是完全不変。然後只要把這個 dst 和之前的 mov 改成同一個文件名就 OK。

希望年底之前我能寫個完美 App(感覺在說屁),什麼時候寫了什麼時候更新。

Google 的奇葩事:它的視頻最寬是 1920(×1920?),長度是 4.5s。我服了,Apple 都沒有這種限制。不過下載下來的文件還是原始的,就是 App 內不能播放。