社内の管理画面に、商品紹介の動画をアップロードできる機能を足すことになりました。受け取った動画はサーバー側で 720p に揃える予定で、変換は ffmpeg に任せます。本番に入れる前に縦向きのサンプルを 1 本流してみたところ、出てきた動画が潰れていました。背の高い風車が、ずんぐりした形で横長の枠に収まっています。再生はできます。エラーも出ていません。ログだけ見ていたら、気づかないままレビューに出していたと思います。
先に素材と環境を書いておきます。サンプルは自分で撮ったものではありません。ネットで見つけた縦向きの動画に、ffmpeg で回転メタデータを書き込んで作った検証用のファイルです。長さは 5 秒です。コーデックは H.264 で、音声はありません。スマホで撮ったままのファイルや HEVC は未検証です。環境は macOS の ffmpeg 7.1 で、再生の確認には Chromium 149、Chrome 154、Firefox 151、WebKit 26.5(macOS)を使いました。iPhone の Safari、Android のブラウザも未確認です。
縦の画が横長の枠に押し込まれていた
変換ジョブに書いていたのは下の 1 つ目です。720p なら 1280×720 だろうと、深く考えずに幅と高さを両方書いていました。2 つ目が直したあとです。
# 潰れた:出力は 1280x720
ffmpeg -i tate_rot90.mp4 -vf scale=1280:720 -c:v libx264 -preset veryfast -crf 26 -an out_wh.mp4
# 正しく出た:出力は 406x720
ffmpeg -i tate_rot90.mp4 -vf scale=-2:720 -c:v libx264 -preset veryfast -crf 26 -an out_h.mp4
左上の赤と右上の緑は、向きを見分けるためにサンプルへ描き足した 160 px 角の目印です。左の出力では、これが横に伸びた長方形になっています。赤は左上、緑は右上のままなので、天地は合っています。横倒しになったのではなく、立った画が上下に押しつぶされたわけです。右は同じ入力から作った 406×720 px です。目印は正方形のままで、風車の形も崩れていません。
回転は scale より先にかかっていた
原因を追うために、入力を ffmpeg -i に通して中身を見ました。寸法は 1920x1080 と出ます。縦の動画なのに横長です。その下の行には「rotation of -90.00 degrees」とありました。これが回転メタデータです。画素は横に寝かせたまま保存しておいて、「再生するときに 90 度回す」という指示だけをファイルに書いておく、という持ち方です。縦向きの動画ではよくあります。MP4 と MOV を <video> で再生すると、試した 4 つのブラウザーはどれもこの指示を読んで縦に表示しました。表示寸法を返す videoWidth と videoHeight も 1080×1920 です。再生しているだけでは、中の画素が寝ていることに気づけません。
ffmpeg 7.1 は、何も指定しなければこの回転を先に適用します。scale に届いた時点で、画はもう 1080×1920 に立っています。そこへ 1280:720 と幅も高さも決め打ちしたので、縦長の画が横長の枠に合わせて引き伸ばされました。出力は 1280×720 px で回転メタデータは付かず、画素は立った状態でした。私は「寝ている 1920×1080 を 1280×720 に縮めるのだから比率は同じ」と考えていました。順番の理解が逆でした。
裏を取るために、-noautorotate(自動で回させない指定)を付けて同じ scale=1280:720 をかけてみました。今度は潰れません。出力は 1280×720 で、回転は -90 度のまま残り、画素は寝たままでした。回転メタデータを読むプレーヤーでは縦に再生されます。寝た画素どうしなら 1920×1080 と 1280×720 は同じ比率です。理屈は合います。ただ、この出力は再生する側が回転を読んでくれることに頼ります。変換後のファイルは別のシステムにも渡す予定なので、これは採用しませんでした。
残した書き方は scale=-2:720 です。高さだけ 720 と決めて、幅は比率から偶数になるよう計算させます。出力は 406×720 px でした。回転メタデータはなく、画素も立っています。scale を付けない既定の変換でも、出力は 1080×1920 で回転メタデータなしになります。サムネイル用に -ss 1 -frames:v 1 で 1 コマ抜いた JPEG も 1080×1920 で立っていました。こちらは -noautorotate を付けると 1920×1080 で寝ます。
メタデータを書き換えても回転は消えなかった
scale を直す前に、一度遠回りをしています。原因が回転だと分かったとき、「先に回転情報を消してしまえば、scale は元の書き方のままで済む」と考えました。試したのは、検索でよく見かける -c copy -metadata:s:v rotate=0 です。出力を ffmpeg -i で見ると、rotation は -90 度のまま残っていました。エラーも警告も出ません。消えたつもりで次へ進むところでした。
入力側に -display_rotation 0 を付けて -c copy すると、今度は回転メタデータが消えます。ところが -c copy は中身を写すだけなので、画素は寝たままです。この出力は 4 つのブラウザーすべてで横倒しになり、videoWidth と videoHeight は 1920×1080 に変わりました。回転情報を消すことと、画素を立てることは別の作業でした。立てるなら再エンコードです。
変換後のファイルが「画素が立っていて、回転メタデータがない」状態になっているか、別の経路の出力とも見比べました。私が使ったのは、ブラウザーの中で画像や動画を処理する ImgIng(https://imging.ai/ )です。90 度、270 度、180 度の回転メタデータを付けた 3 種類のサンプルを通しました。出力はどれも 608×1080 px の立った画素で、回転メタデータは付いていません。試したのは Chromium 149 と、ファイルサイズ優先の設定だけです。
いまの変換ジョブは scale=-2:720 にしてあります。変換の前後で ffmpeg -i の表示を見ます。入力に rotation の行があったら、出力からその行が消えて寸法が縦長になっていることを確かめます。手元の Mac には ffprobe が入っていません。ffprobe での見え方は未確認です。ffmpeg も 7.1 しか試していないので、サーバーの版が違うなら同じサンプルをもう一度流すつもりです。
図の風車の映像は、Wikimedia Commons にある Grendelkhan さんの動画(CC BY-SA 4.0)から取りました。
