影片為什麼躺著播放,為什麼修好它只要半秒鐘

手機鏡頭永遠橫著錄,只是往檔案裡寫一條 11 位元組的備註告訴播放器轉過來。備註丟了,影片就躺下了。這條備註是什麼、誰把它弄丟的,以及為什麼修復不碰任何一個畫素。

一句話

手機的鏡頭感測器是橫向固定在機身裡的。你直著拿手機錄影,感測器錄下的仍然是橫向畫面;手機只是往檔案裡寫一個標記:“顯示時轉 90°”。手機上每個播放器都認這個標記。別處的一些軟體——舊的剪輯軟體、某些網頁播放器、某些上傳流程——不認它或者把它抹掉了,結果就是影片側著播。把標記寫回去是一次後設資料編輯:不管檔案多大都只要零點幾秒,什麼都不重新編碼。

這個標記到底是什麼

MP4 或 MOV 檔案是一棵盒子樹。描述一條影片軌如何呈現的那個盒子叫 tkhd(track header),裡面有一個 3 × 3 的變換矩陣——九個數、36 位元組——播放器在畫每一幀之前都先套上它。沒旋轉的影片,矩陣是單位陣。直著拍的影片,矩陣是一個 90° 旋轉。檔案裡的畫素是橫的,矩陣說“把它們轉過來”。安卓把同樣的資訊寫成一個單獨的 rotation 值;ffmpeg 把兩者都報告為串流後設資料裡的 rotate: 90。

這個設計是有意的。錄影時真的去轉動畫素,意味著每一幀都要在編碼器裡多跑一整遍,還是靠電池。寫九個數不花任何代價。這和照片裡 EXIF 方向標記是同一個思路,也以同一種方式出問題:任何不讀這個標記的消費方,顯示的就是原始方向。

誰把它弄丟的

  • 直式影片出現之前寫的桌面剪輯軟體和播放器。 VLC 和 QuickTime 多年前就認這個矩陣;某些企業影片系統和舊版 Windows Media Player 不認。
  • 複製影片串流但重建容器的轉碼器。 一個號稱“不重編碼、無損”的工具,仍然可能寫一個帶單位矩陣的全新 tkhd。畫素活下來了,指令沒有。
  • 某些網站上傳。 上傳時重新封裝的網站可能丟掉標記;上傳時重新編碼的網站通常會把旋轉正確地烤進去。
  • 拼接。 把一段直式和一段橫式拼成一個檔案,合併後的軌只有一個矩陣,兩半裡必有一半是錯的。

兩種修法,一種是免費的

重寫矩陣。 把 tkhd 矩陣(或旋轉欄位)改成讓影片正確顯示的值。這是往一個可能好幾 GB 的檔案裡寫幾十個位元組;影片串流和音訊串流原樣拷過去。半秒鐘,不掉畫質,體積不變。這是影片旋轉的預設做法:在你的裝置上讀檔案,寫入修正後的頭,把檔案還給你。因為什麼都不解碼,4 GB 的錄影和 4 MB 的一樣快。

重編碼,把旋轉烤進去。 解碼每一幀,轉動畫素,再編碼。只有在消費方確定不認矩陣、而你又改不了消費方的時候才需要——數位看板播放器、老舊的內容系統。耗時和影片一樣長(沒有硬體編碼器還更長),而且掉一代畫質。影片旋轉把它作為第二個選項,用瀏覽器的 WebCodecs 編碼器完成,介面上寫明了,因為這是貴的那條路。

“免費”的修法有時為什麼不管用

如果影片只在一個程式裡側著,在別處都正常,那是這個程式不認矩陣,重寫也沒用——只能烤進去。如果到處都側著,那是矩陣丟了,重寫就是解法。一個快速測試:在現行瀏覽器裡開啟檔案(Chrome、Safari、Firefox 都認這個標記)。瀏覽器裡側著 → 矩陣錯了或丟了 → 重寫。瀏覽器里正常、那個程式裡側著 → 程式的問題 → 烤進去。

相關的坑

  • 縮圖。 從側著的影片裡截出的一幀也是側著的,因為畫面取自流,不是取自顯示結果。先修影片,再截幀。
  • 剪輯。 不重編碼的剪輯會保留矩陣;經過重新封裝的剪輯工具可能不會。剪出來的片段躺著回來,就是剪輯器丟了標記,重寫矩陣一次就回去了。
  • 前置鏡頭的映象影片。 那是另一個標記(同一個矩陣裡的水平翻轉),修法相同。

本文用到的工具