Base64 を MP4 にデコードする方法
- Base64 または data URI に Base64 を貼り付けます。
data:video/mp4;base64,接頭辞は残したままで構いません。長い文字列には .txt ファイルを開く を使ってください。4 MB を超えるものはテキストエリアに表示せずにデコードします。 - プレーヤーに動画が読み込まれます。ブラウザがインデックスを読み込むと、再生時間 と 寸法 が表示されます。サンプルなら
0:02.0と96 × 64 pxです。 - 注意書きの欄を確認します。本物の MP4 なら空のままで、そうでなければ形式が表示されます。
- ファイルをダウンロード を押します。
デコードも再生もブラウザ内で完結します。ページには Cloudflare のページビュー計測以外のネットワークリクエストが許されていません。
MP4、MOV、M4A:1 つの構造、複数の形式
MP4 ファイルはボックスで構成されていて、最初のボックス ftyp には、ファイルがどのバリエーションに従うかを示すブランドが入っています。ツールはこのブランドを読みます。
ftyp のブランド | 判別結果 | このページでの注意書き |
|---|---|---|
isom、mp41、mp42、avc1、iso6 など大半のもの | MP4 | なし |
qt | MOV(QuickTime) | 「これは MP4 ではなく MOV のようです。」と .mov としてダウンロード |
M4A | M4A | 「これは MP4 ではなく M4A のようです。」:音声のみ |
3gp4、3gp5 などの 3g ブランド | 3GP(古い携帯電話の動画) | 「これは MP4 ではなく 3GP のようです。」 |
heic、mif1、avif | HEIC、AVIF | 「これは MP4 ではなく HEIC のようです。」:同じボックス構造の静止画 |
ブランドが MP4 でも、中身が音声だけのこともあります。その場合は再生でき、寸法 に 音声のみ と表示されます。「MP4」として送られてきた WebM ファイルは、「これは MP4 ではなく WebM のようです。」と報告されます。
インデックスの位置と、壊れたファイルにとっての意味
どの MP4 にも、各フレームの位置をプレーヤーに教えるインデックスの moov ボックスがあります。ffmpeg をはじめ多くのエンコーダーは、デフォルトでこれを mdat のメディアデータの後、つまりファイルの末尾に書き込みます。-movflags +faststart を付けると、ffmpeg はこれを先頭に移すので、ファイル全体が届く前にストリーミングを始められます。
このページではファイル全体がメモリ上にあるので、完全な動画なら位置は関係ありません。問題になるのは、ログ、データベースのカラム、メッセージキューのサイズ制限で Base64 が途中で切れた場合です。
- インデックスが末尾にある場合: 切れたことでインデックスが失われ、何も再生されません。先頭のバイトは無傷なのでツールは MP4 と判別しますが、プレーヤーは「再生できません」と表示し、再生時間 は「該当なし」になります。
- インデックスが先頭にある場合: ブラウザはインデックスを読んで全体の再生時間を表示し、データが尽きるところまで再生します。
ですから、短い動画は再生できるのに長い動画だけ失敗する場合は、コーデックを疑う前に、デコード後のサイズを元のものと比べてください。
Python でボックスを調べる
ボックスの構造は単純なので、ライブラリなしでもたどれます。4 バイトのサイズ、4 バイトの種類、その後に中身が続きます。次のコードはトップレベルのボックスとブランドを一覧表示します。
import base64, struct
def mp4_boxes(data):
pos = 0
while pos + 8 <= len(data):
size, kind = struct.unpack(">I4s", data[pos:pos + 8])
if size == 1:
size = struct.unpack(">Q", data[pos + 8:pos + 16])[0]
elif size == 0:
size = len(data) - pos
yield kind.decode("latin-1"), pos, size
if size < 8:
break
pos += size
with open("clip.b64") as f:
data = base64.b64decode(f.read())
for kind, pos, size in mp4_boxes(data):
print(kind, pos, size)
if kind == "ftyp":
body = data[pos + 8:pos + size]
brands = [body[i:i + 4].decode("latin-1") for i in range(8, len(body), 4)]
print(" major:", body[:4].decode("latin-1"), "compatible:", brands)
ffmpeg で作った H.264 の動画なら、まずメジャーブランド isom と一緒に ftyp 0 32 が表示され、続いて free、mdat、最後に moov が表示されます。moov が mdat より前にあれば faststart です。moof ボックスがあれば、ストリーミング用やこのページのサンプルのような断片化 MP4 です。一覧に moov がなければ、ファイルは不完全です。
ブラウザごとのコーデック対応については Base64 を動画に変換 を参照してください。動画をエンコードするには MP4 を Base64 に変換 を使います。大きな入力のデコードについては Python での Base64 で解説しています。
よくある質問
Base64 を MP4 ファイルに変換するには?
上に文字列を貼り付け、プレーヤーで動画を確認して ファイルをダウンロード を押してください。動画はエンコードされたときとバイト単位で同じまま、decoded.mp4 として保存されます。
MP4 なのに「再生できません」と表示されるのはなぜですか?
中のコーデック(最も多いのは HEVC)にブラウザが対応していないか、ファイルが不完全でインデックスが欠けているかのどちらかです。ダウンロードしたファイルに ffprobe を実行すれば区別できます。コーデック名が表示されるか、moov アトムが見つからないと報告されます。
MOV を .mp4 として保存すべきですか?
内容に合った拡張子の .mov としてダウンロード を使ってください。MOV と MP4 は構造の大部分が共通なので、名前を変えても実際には動くことが多いのですが、ブランドを確認して不一致を拒否するプレーヤーもあります。