ガイド

MP4の構造:moov アトムとは何か、そして動画が再生できない理由

moov アトムは MP4 ファイルのインデックスです。すべてのフレームがどこにあるのかをプレーヤーに伝える地図です。これは録画が終了処理される瞬間に書き込まれます。だからこそ、クラッシュや電源喪失があると、映像はまだ中に残っているのにファイルが開かない、という状態に陥るのです。本記事では、このファイル形式をバイト単位で、わかりやすい言葉でひととおり見ていきます。

MP4 は箱の中の箱

MP4 ファイルはボックス(box)から組み立てられています。 (仕様上の古い呼び名であるアトム(atom)のほうが、 エラーメッセージには定着しました。)どのボックスも同じ8バイトのヘッダーで 始まります。ボックスの大きさを表す4バイト、続いてボックスの種類を表す4文字 です。文法はこれで全部です。ボックスは別のボックスを内包でき、パーサーは ヘッダーを読み、中を覗くかどうかを決め、宣言されたサイズだけ先へ飛ばして 次のボックスへ進む、という形でファイルをたどっていきます。

完成した録画ファイルを開いてみれば(スマホ、GoPro、OBS、ドローンのどれでも)、 最上位で同じ3つのボックスがすべての仕事をこなしているのが分かります。

典型的な完成録画のバイト配置(縮尺は正確ではありません。データブロックが 他のすべてを圧倒しています)。等幅で書かれた名前は、ファイル自身に 書き込まれた4文字のボックスタイプで、ffmpeg のようなツールがエラー メッセージで示すのもこれです。

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 の動画エンジンが行うのも この種の修復です)は、生データをバイト単位でたどっていきます。

  1. フレームの境界を見つける。 mdat を走査し、 長さのプレフィックスとコーデックのシグネチャを使って、各映像・音声 フレームがどこで始まりどこで終わるかを特定します。
  2. 分類して測定する。 各フレームをそれぞれのストリームに 割り当て、そのサイズと位置を記録し、stszstco の素材をサンプルごとに組み直します。
  3. タイミングを再構築する。 フレームの長さをエンコーダーの 設定から導き出します。untrunc が、同じ設定で録画された 健全な参照ファイルを求めるのはこのためで、あなたの機器にとっての「正常」が どんなものかを学ぶためです。
  4. 新しい 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 が一度も書き込まれなかったファイルを直すこともできません。

このシリーズの他の記事:動画ファイルはどう壊れるのか、そして実際に何が修復できるのか