视频为什么躺着播放,为什么修好它只要半秒钟

手机摄像头永远横着录,只是往文件里写一条 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 都认这个标记)。浏览器里侧着 → 矩阵错了或丢了 → 重写。浏览器里正常、那个程序里侧着 → 程序的问题 → 烤进去。

相关的坑

  • 缩略图。 从侧着的视频里截出的一帧也是侧着的,因为画面取自流,不是取自显示结果。先修视频,再截帧。
  • 剪辑。 不重编码的剪辑会保留矩阵;经过重新封装的剪辑工具可能不会。剪出来的片段躺着回来,就是剪辑器丢了标记,重写矩阵一次就回去了。
  • 前置摄像头的镜像视频。 那是另一个标记(同一个矩阵里的水平翻转),修法相同。

本文用到的工具