MP4 は箱の中の箱
MP4 ファイルはボックス(box)から組み立てられています。 (仕様上の古い呼び名であるアトム(atom)のほうが、 エラーメッセージには定着しました。)どのボックスも同じ8バイトのヘッダーで 始まります。ボックスの大きさを表す4バイト、続いてボックスの種類を表す4文字 です。文法はこれで全部です。ボックスは別のボックスを内包でき、パーサーは ヘッダーを読み、中を覗くかどうかを決め、宣言されたサイズだけ先へ飛ばして 次のボックスへ進む、という形でファイルをたどっていきます。
完成した録画ファイルを開いてみれば(スマホ、GoPro、OBS、ドローンのどれでも)、 最上位で同じ3つのボックスがすべての仕事をこなしているのが分かります。
「これは MP4 です」ということと、プレーヤーが想定すべき形式の バリエーションを宣言します。ファイルの一番最初にあります。
圧縮された音声・映像フレームが、記録された順に隙間なく詰め込まれて います。ラベルもタイムスタンプもなく、外から見えるフレームの境界も ありません。ただのペイロードです。
上にあるすべての地図です。各フレームがどこから始まり、どれだけの 大きさで、いつ再生されるか。箱の中の箱です。
MP4 の元になった QuickTime 形式である MOV ファイルも、まさにこの同じ構造を 共有しています。だからこそ、開かない MOV と 開かない MP4 は、拡張子だけ違う、ほとんど常に同じ問題なのです。
moov アトムは地図
ここが多くの人を驚かせる点です。データブロックは不透明なのです。 圧縮されたフレームは mdat の中に隙間なく並んでいて、それらを 区切るものは何もなく、「フレーム 412 はここから始まる」といった目印も ありません。プレーヤーが答えを必要とするあらゆる問いは、代わりに moov アトムの中の、サンプルテーブルと呼ばれる一群の構造に 収められています。
- 各フレームはどこにあるか? チャンクオフセットテーブル (
stco)がファイル内のバイト位置を記録しています。 - 各フレームはどれだけの大きさか? サンプルサイズ テーブル(
stsz)がすべてのフレームの長さを、1フレームに つき1エントリで列挙しています。 - 各フレームはいつ再生されるか? タイムトゥサンプル テーブル(
stts)がフレームをムービーの時計に対応づけ、 音声と映像がぴったり揃って進むようにします。 - 再生はどこへ飛べるか? シンクサンプルテーブル (
stss)がキーフレームを列挙しています。これはデコーダーが 再生を開始できる唯一のフレームであり、シークを可能にしているものです。
デコーダーの設定(動画がどのように圧縮されたかを伝えるパラメーター)で さえ、ストリームの中ではなく moov アトムに保存されています。moov アトムの ないプレーヤーは、単にチャプターの目印を欠いているだけではありません。 1つのフレームがどこから始まるのかも、動画の長さも、デコーダーをどう 設定すればよいのかも分からないのです。背表紙もタイトルもなく、目録もない 図書館に本が並んでいるところを想像してみてください。情報はすべて存在して いるのに、そのどれにも手が届きません。それが moov のないmdat であり、プレーヤーが壊れたファイルの「大部分」を再生する のではなく、完全にあきらめてしまう理由です。
なぜインデックスは最後に書き込まれるのか
moov アトムはすべてのフレームを記述します。その位置、サイズ、タイミング です。録画している間は、そのどれも知りようがありません。カメラは、あなたが 10秒後に止めるのか2時間後に止めるのか見当もつきませんし、フレームの バイトオフセットは、そのフレームが存在する前には記録できません。そこで、 どの録画機も、できる唯一のことをします。圧縮されたフレームを、エンコード されるそばから mdat へと流し込み、進行中の集計をメモリに 保ち、あなたが「停止」を押した瞬間にインデックスを一度のパスで書き込む のです。
録画中:フレームはエンコードされるそばから追記されます。インデックスはまだ存在できません。その中身は最後のフレームが書き込まれるまで分からないからです。
終了処理:録画機は書き込んだすべてを測定し、moov アトムを追記します。ここで初めてファイルは再生可能になります。
この設計は効率的で安全です。ただし、録画が終了処理の工程まで到達しない ときだけは別です。電池切れ、OBS のクラッシュ、湖に落ちたドローン。ファイルを 書き込んでいるプロセスが止まり、インデックスは一度も書き込まれません。 ディスクに残るのは、まっとうなサイズのファイル、mdat に収まった 何時間もの無傷の映像、そして地図がない状態です。どのプレーヤーもそれを 受け付けず、ffmpeg は moov atom not found と表示します。映像は 消えたのではなく、取り残されているのです。(カメラのメモリバッファにまだ あり、ストレージへ書き出されていなかったごく最後の数秒は、本当に失われます。正直なツールはそう言います。)その「取り残されたが無傷」という 状態こそ、moov アトムの修復が存在する理由で あり、クラッシュした OBS 録画の 典型的な失敗のかたちです。
faststart とフラグメント化 MP4
インデックスが末尾にあるという設計には、よく知られた2つの帰結があり、 業界はそれぞれに対する解決策を築いてきました。
faststart:インデックスを、あとから先頭へ移す
ウェブから動画をストリーミング再生するブラウザは、ファイルを先頭から末尾へ と読みます。moov アトムが末尾にあると、ファイル全体がダウンロードされるまで 再生を開始できません。プレーヤーが本を手にして目録を待っている状態です。 その解決策が後処理のパス(ffmpeg の -movflags +faststart)で、 これは完成したファイルを、moov アトムを先頭へ移した形に書き換えます。
「faststart」ファイル:インデックスが先頭にあるので、ストリーミング再生をすぐに始められます。一部のウェブ動画が瞬時に始まり、他が途中で止まるのはこのためです。
faststart がそうではないものに注意してください。それはクラッシュ 対策ではありません。エンコードが終わったあとの、完全で健全なファイルに 対して実行されます。書き込み途中で死んでしまった録画は、その工程には 決してたどり着けません。
フラグメント化 MP4:1つの大きなインデックスの代わりに、多数の小さなインデックス
2つ目の帰結(終了処理を失えば、ファイルを失う)に対処するのがフラグメント化 MP4です。末尾に1つの mdat と 1つの moov を置く代わりに、ファイルは短い自己記述的なセグメントの連なりと して書き込まれます。それぞれがデータの一片で、自分専用のミニインデックス (moof ボックス)を持ちます。ffmpeg で言えば-movflags frag_keyframe+empty_moov、つまり先頭に骨組みだけの moov を置き、あとはフラグメントを次々と続けていきます。録画機が死んでも、 失うのはせいぜい最後のフラグメントだけで、それ以前のすべてはすでに インデックス化されていて再生できます。ストリーミングサービスが同じ理由で 動画をこの方式で配信しています。引き換えになるのは互換性です。一部の編集 ソフトや古いプレーヤーはフラグメント化ファイルをうまく扱えず、そのため カメラは今も従来のレイアウトで書き込みます。OBS の答え(OBS 30.2 以降)は 「Hybrid MP4」で、フラグメント方式のクラッシュ耐性を保ちつつ、広く互換性の あるファイルへと終了処理します。過去に OBS のクラッシュで痛い目に遭った ことがあるなら、有効にする価値があります。
moov 再構築の仕組み
というわけで、クラッシュはあなたの手元に ftyp と巨大なmdat、そしてインデックスのない状態を残しました。それを 再構築できるのは、この「不透明な」データブロックが、コーデックを知って いるパーサーにとっては完全に不透明ではないからです。mdat の 中では、映像フレームは長さのプレフィックスを持つ単位として格納されており、 各コーデックのビットストリームには見分けのつくパターンがあります。フレームの 種類・寸法・境界を示すヘッダーです。再構築ツール(オープンソースのuntrunc がこの手法を確立し、IntactFile の動画エンジンが行うのも この種の修復です)は、生データをバイト単位でたどっていきます。
- フレームの境界を見つける。
mdatを走査し、 長さのプレフィックスとコーデックのシグネチャを使って、各映像・音声 フレームがどこで始まりどこで終わるかを特定します。 - 分類して測定する。 各フレームをそれぞれのストリームに 割り当て、そのサイズと位置を記録し、
stszとstcoの素材をサンプルごとに組み直します。 - タイミングを再構築する。 フレームの長さをエンコーダーの 設定から導き出します。
untruncが、同じ設定で録画された 健全な参照ファイルを求めるのはこのためで、あなたの機器にとっての「正常」が どんなものかを学ぶためです。 - 新しい moov を書いて終了処理する。 サンプルテーブルを 新しいインデックスに組み上げ、仕様どおりの正しいファイルを書き出します。
その結果、物理的に残ったすべてが再生され、データが尽きるところで終わります。 正直な限界は、この仕組みから直接導かれます。フレームのタイミングが極めて 不規則な録画は、音声と映像にわずかなズレが生じて戻ってくることがありますし、mdat 自体がゼロ(録画機ではなくストレージが壊れた場合)なら、 見つけるべきフレームがなく、どんなツールもそれを呼び出すことはできません。 どの損傷がどれに当たるのか、より大きな全体像については、動画ファイルはどう壊れるのか、そして実際に何が修復できるのかをご覧ください。
よくある質問
MP4 ファイルの moov アトムとは何ですか?
moov アトムは MP4 のインデックスです。すべての映像フレームと音声フレームがファイル内のどこにあるか、それぞれの大きさ、いつ表示されるべきか、そして各ストリームがどう同期するかを記録した構造化されたブロックです。デコーダーの設定も保持しています。プレーヤーはまず moov アトムを読み、それをメディアデータへの地図として使います。有効な moov アトムがなければ、映像そのものが無傷であっても、ファイルは開きません。
なぜ moov アトムはファイルの末尾にあるのですか?
その中身は録画が終わるまで分からないからです。moov アトムはすべてのフレームのサイズ・位置・タイミングを列挙しますが、これは最後のフレームが書き込まれて初めて存在する情報です。そのため録画機はフレームを順次追記していき、終了処理の際にインデックスを一度のパスで書き込みます。結果として、それはメディアデータの後ろに置かれます。ウェブ配信向けに用意されたファイルでは、あとから moov が先頭へ移されていることがよくあります(「faststart」)が、それは後処理の工程であって、ファイルが生まれる本来の姿ではありません。
失われた moov アトムは再構築できますか?
たいていは可能です。ただしメディアデータ(mdat ブロック)が残っている場合に限ります。再構築ツールは生のストリームを走査し、各フレームの境界を検出し、サイズと位置を測定して、moov アトムが本来持っていたはずのサンプルテーブルを組み直します。その結果、中断された地点までのすべてを含む、再生可能なファイルが得られます。mdat ブロックが失われている、ゼロで埋められている、あるいは上書きされている場合は、インデックス化すべきものが何もなく、どんなツールでもファイルを再構築できません。あなたのファイルがどちらの場合なのかは、ブラウザ内でファイルをチェックして確かめられます。
moov と mdat の違いは何ですか?
mdat はペイロードを保持します。圧縮された音声フレームと映像フレームが隙間なく詰め込まれており、通常はファイルサイズの99%を占めます。moov はメタデータを保持します。mdat 内の各フレームがどこから始まるか、そのサイズ、タイミング、そして再生に必要なデコーダー設定を記述したインデックスです。moov のない mdat は、地図のない映像です。データはそこにあるのに、どのプレーヤーもその中を辿れません。mdat のない moov は、もう存在しない土地の地図です。
movflags faststart は何をするのですか?
ffmpeg では、-movflags +faststart は、完成したファイルの末尾にある moov アトムを、ファイルタイプヘッダーのすぐ後ろ、つまり先頭へと移動させます。これによりプレーヤーは、ファイルの残りがまだダウンロード中でも再生を開始できます。ウェブ動画で標準的に使われるのはこのためです。これはファイルの書き換えを伴い(ffmpeg は2回目のパスを行います)、健全で完全なファイルにしか効きません。録画をクラッシュから守るものではなく、moov が一度も書き込まれなかったファイルを直すこともできません。
このシリーズの他の記事:動画ファイルはどう壊れるのか、そして実際に何が修復できるのか。