AOYAMA KOJI's PROGRAMMING BLOG

ボックス形式のバイナリーデータを生成【mp4ファイル生成解説】プログラマー向け実装解説

2025/05/10
ボックス形式のバイナリーデータを生成【mp4ファイル生成解説】:

 mp4動画ファイル生成について、プログラマー向けに解説します。 大まかな流れとしては、各フレーム画像をエンコードし、mp4フォーマットに合わせてまとめます。 本記事では、全4工程中の3番目、取得した NAL Unit の配列等を取り込み、mp4ファイルフォーマットであるボックス形式のバイナリーデータを作成する処理について、詳述します。 ここが山場です。
 なお、実際にこれを用いて作られたWebアプリケーションがこの記事などにありますので、 プログラマー以外の方も、ぜひお試しください。
ボックス形式のバイナリーデータを生成【mp4ファイル生成解説】:

[PR]

mp4動画ファイル生成の工程


 当ブログにおける、mp4動画ファイルを生成する工程は、以下の4ステップです。
 本記事では「3.NAL Unit 等からボックス形式のバイナリーデータを生成」を解説します。
  1. WebCodecsのVideoEncoderで各フレーム画像をエンコードWebCodecsのVideoEncoderで各フレーム画像をエンコード
  2. エンコード結果から NAL Unit を取得エンコード結果から NAL Unit を取得
  3. NAL Unit 等からボックス形式のバイナリーデータを生成 ← 本記事で解説
  4. バイナリーデータを保存バイナリーデータを保存

前工程で取得できたもの


ボックス形式のバイナリーデータを生成【mp4ファイル生成解説】: NAL Unit フレームデータ配列 および SPSとPPS
 前工程において、 エンコードされた情報から、動画情報である NAL Unit のフレームデータの配列と、デコードに必要な情報が記された SPS および PPS の情報を取得できました。
 本記事ではこれを使用して、mp4ファイルのバイナリーデータを構築します。

ボックス形式


ボックス形式のバイナリーデータを生成【mp4ファイル生成解説】: ボックス形式
 mp4ファイルはボックスという形式で作られています。

基本形式


 各ボックスは、図のように、先頭4バイトがそのボックスの長さ、次の4バイトがboxタイプ、それ以降がデータ本体です。 Boxの先頭4バイトは、自身を含む長さになりますので、ボックスタイプと合わせて「データ本体の長さ+8」になります。

コンテナボックス


 コンテナボックスには、子のボックスが並びます。 コンテナボックスは実データを持たず、子のボックスのみを持ちます。

必要最小限のボックス構成


 mp4ファイルに必要な最小限のボックスの構成は以下になります。 最小限といっても、下記のように多数あります。
 ひとつづつ、サンプルデータと添えて、解説します。 offsetとsizeの単位はバイトです。
 なお、本記事で値を記載している場合、それ以外の値も有効な場合があります。 例えば厳密には、上述したボックス長のバイト数は可変ですが、本記事では4バイトに固定して進めます。 解説をシンプルにするため、他の可能性にはできる限り言及しません。
[ftyp]
[mdat]
[moov]
  [mvhd]
  [trak]
    [tkhd]
    [mdia]
      [mdhd]
      [hdlr]
      [minf]
        [vmhd]
        [dinf]
          [dref]
        [stbl]
          [stsd]
          [stts]
          [stss]
          [stsc]
          [stsz]
          [stco]

ftyp


 ftypは、mp4のファイルタイプを記述します。 当ブログでは、VideoEncoderに指定したコーデック 'avc1.42E01E' に基づいて生成AIと議論して導いた値を設定しています。
offset
size
内容
0x00
4
ボックス長
0x20
0x04
4
ボックスタイプ
'ftyp'
0x08
4
メインタイプ
'isom'
0x0c
4
バージョン
0x200
0x10
4
サブタイプ
'isom'
0x14
4
サブタイプ
'iso2'
0x18
4
サブタイプ
'avc1'
0x1c
4
サブタイプ
'mp41'

サンプル

[ftyp] 32bytes
  isom 512
  isom,iso2,avc1,mp41
   0x0000: 00 00 00 20 66 74 79 70 69 73 6f 6d 00 00 02 00 ... ftypisom....
   0x0010: 69 73 6f 6d 69 73 6f 32 61 76 63 31 6d 70 34 31 isomiso2avc1mp41

mdat


 mdatにはエンコードデータ本体が入ります。 当ブログでは、AvcC形式にしたNAL Unit のフレームデータの配列を繋げたものを、設定しています。
offset
size
内容
0x00
4
ボックス長
(要計算)
0x04
4
ボックスタイプ
'mdat'
0x08
*
データ
全フレームデータ

サンプル

[mdat] 6057bytes
  5657bytes (5661bytes)
    00 00 16 19 65 88 80 4f ff ff c3 c0 84 50 00 11 8a f2 72 72 72 72 72 72 72 72 70 86 00 08 00 60  ....e..O.....P....rrrrrrrrp....`
    5669/6057
  198bytes (202bytes)
    00 00 00 c6 41 9a 02 3c 5f 30 21 ee 4e 17 ec 07 a0 ed ae 82 e8 2a e1 bb d0 3a e8 2f ae 1c f2 01  ....A..<_0!.N........*...:./....
    5871/6057
  98bytes (102bytes)
    00 00 00 62 41 9a 04 3c 5f 30 21 ee 4e 6d 80 b5 c3 77 eb a7 f5 cd ec bd f7 be 5e ff 04 5d 00 67  ...bA..<_0!.Nm...w........^..].g
    5973/6057
  80bytes (84bytes)
    00 00 00 50 41 9a 06 3c 5f 30 21 ee 4e 6d 87 ae 1b bf 5d 85 f7 df 7b e5 ef f0 45 d9 4e 97 cb d0  ...PA..<_0!.Nm....].......E.N...
    6057/6057
  FrameCount=4(0x4), NALCount=4(0x4)

moov


 moovはコンテナボックスです。mvhdボックスとtrakボックスが入ります。
 なお、必須ではありませんが、当ブログはudtaボックスも作り、ここに含めています。
offset
size
内容
0x00
4
ボックス長
(要計算)
0x04
4
ボックスタイプ
'moov'
0x08
*
子ボックス列挙
mvhd, trak, udtaボックス

mvhd


 mvhdには、動画再生時間に関する基本情報が入ります。
offset
size
内容
0x00
4
ボックス長
0x6c
0x04
4
ボックスタイプ
'mvhd'
0x14
4
時間単位
1000 (=ms)
0x18
4
動画時間
単位ms
0x1c
4
再生速度
0x10000 (=1.0)
0x20
2
ボリューム
0x100 (=1.0)
0x2c
4
行列11
0x10000 (=1.0)
0x3c
4
行列22
0x10000 (=1.0)
0x4c
4
行列33
0x40000000 (=1.0)
0x68
4
次トラックID
2

サンプル

  [mvhd] 108bytes
    0x0000: 00 00 00 6c 6d 76 68 64 00 00 00 00 00 00 00 00 ...lmvhd........
    0x0010: 00 00 00 00 00 00 03 e8 00 00 00 84 00 01 00 00 ................
    0x0020: 01 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 ................
    0x0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 ................
    0x0040: 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 ............@...
    0x0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    0x0060: 00 00 00 00 00 00 00 00 00 00 00 02             ............

trak


 trakはコンテナボックスです。tkhdボックスとmdiaボックスを含みます。
offset
size
内容
0x00
4
ボックス長
(要計算)
0x04
4
ボックスタイプ
'trak'
0x08
*
子ボックス列挙
tkhd, mdiaボックス

tkhd


 tkhdは、動画再生画像に関する基本情報が入ります。
offset
size
内容
0x00
4
ボックス長
0x5c
0x04
4
ボックスタイプ
'tkhd'
0x08
4
再生フラグ
0x0f
0x14
4
トラックID
1
0x1c
4
動画時間
単位ms
0x30
2
行列11
0x10000 (=1.0)
0x40
4
行列22
0x10000 (=1.0)
0x50
4
行列33
0x40000000 (=1.0)
0x54
4
画像横幅
.width
0x58
4
画像縦幅
.height

サンプル

    [tkhd] 92bytes
      0x0000: 00 00 00 5c 74 6b 68 64 00 00 00 0f 00 00 00 00 ...\tkhd........
      0x0010: 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 84 ................
      0x0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
      0x0030: 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
      0x0040: 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
      0x0050: 40 00 00 00 00 90 00 00 00 90 00 00             @...........

mdia


 mdiaはコンテナボックスです。mdhd,hdlr,minfボックスを含みます。
offset
size
内容
0x00
4
ボックス長
(要計算)
0x04
4
ボックスタイプ
'mdia'
0x08
*
子ボックス列挙
mdhd, hdlr, minfボックス

mdhd


 mdhdには表示用の情報が入ります。
 フレームレートについて、当ブログのツールは可変ですが、30fpsとして例示します。 言語コードの詳細は後述します。
offset
size
内容
0x00
4
ボックス長
0x20
0x04
4
ボックスタイプ
'mdhd'
0x14
4
フレームレート
0x1e (30)
0x18
4
フレーム数
フレーム数
0x1c
2
言語コード
2a0e

サンプル

      [mdhd] 32bytes
        0x0000: 00 00 00 20 6d 64 68 64 00 00 00 00 00 00 00 00 ... mdhd........
        0x0010: 00 00 00 00 00 00 00 1e 00 00 00 04 2a 0e 00 00 ............*...

hdlr


 hdlrにはビデオハンドラーを設定します。
offset
size
内容
0x00
4
ボックス長
0x2d
0x04
4
ボックスタイプ
'hdlr'
0x10
4
ハンドラータイプ
'vide'
0x20
12
ハンドラー名
'VideoHandler'

サンプル

      [hdlr] 45bytes
        0x0000: 00 00 00 2d 68 64 6c 72 00 00 00 00 00 00 00 00 ...-hdlr........
        0x0010: 76 69 64 65 00 00 00 00 00 00 00 00 00 00 00 00 vide............
        0x0020: 56 69 64 65 6f 48 61 6e 64 6c 65 72 00          VideoHandler.

minf


 minfはコンテナボックスです。vmhd,dinf,stblボックスが入ります
offset
size
内容
0x00
4
ボックス長
(要計算)
0x04
4
ボックスタイプ
'minf'
0x08
*
子ボックス列挙
vmhd, dinf, stblボックス

vmhd


 vmhdはビデオメディアの情報です。デフォルト値を設定します。
offset
size
内容
0x00
4
ボックス長
0x14
0x04
4
ボックスタイプ
'vmhd'
0x0a
2
フラグ
1 (デフォルト)

サンプル

        [vmhd] 20bytes
          0x0000: 00 00 00 14 76 6d 68 64 00 00 00 01 00 00 00 00 ....vmhd........
          0x0010: 00 00 00 00                                     ....

dinf


 dinfはコンテナボックスです。drefボックスが入ります
offset
size
内容
0x00
4
ボックス長
0x24
0x04
4
ボックスタイプ
'dinf'
0x08
0x1c
子ボックス
drefボックス

dref


 drefボックスには、参照すべきデータが、すべてこのファイル内にあることを示す情報を設定します。
offset
size
内容
0x00
4
ボックス長
0x1c
0x04
4
ボックスタイプ
'dref'
0x0c
4
登録数
1
0x10
4
url ボックス長
0x0c
0x14
4
参照タイプ
'url '
0x18
4
フラグ
1 (含有)

サンプル

          [dref] 28bytes
            0x0000: 00 00 00 1c 64 72 65 66 00 00 00 00 00 00 00 01 ....dref........
            0x0010: 00 00 00 0c 75 72 6c 20 00 00 00 01             ....url ....

stbl


 stblはコンテナボックスです。stsd,stts,stss,stsc,stsz,stcoボックスが入ります
offset
size
内容
0x00
4
ボックス長
(要計算)
0x04
4
ボックスタイプ
'stbl'
0x08
*
子ボックス列挙
stsd, stts, stss, stsc, stsz, stcoボックス

stsd


 stsdには、デコードに必要な情報を設定します。 別途保持していた SPS と PPS の情報をここで使用します。
 本記事において、ここが最難関ポイントです。
 表内の SPS[0] と PPS[0] は、それぞれの NAL Unit ヘッダーです。 SPS[1] から SPS[3] は、SPS の NAL Unit ヘッダーの次から3つ(各1バイトずつ)を指します。
offset
size
内容
0x00
4
ボックス長
(要計算)
0x04
4
ボックスタイプ
'stsd'
0x0c
4
登録数
1
0x10
4
avc1ボックス長
(残り全部)
0x14
4
avc1タイプ
'avc1'
0x1e
2
インデックス
1
0x30
2
画像横幅
.width
0x32
2
画像縦幅
.htight
0x34
4
横解像度
0x480000 (=72dpi)
0x38
4
縦解像度
0x480000 (=72dpi)
0x62
2
色深度
24 (ビット)
0x64
2
カラーテーブル
0xffff (=無し)
0x66
4
avcCボックス長
(残り全部)
0x6a
4
avcCタイプ
'avcC'
0x6e
1
configureバージョン
1
0x6f
1
avc profile indication
SPS[1]
0x70
1
profile compatilibity
SPS[2]
0x71
1
avc level indication
SPS[3]
0x72
1
0xfc | (データ長サイズ-1)
0xff
0x73
1
SPS数
1
0x74
2
SPS NAL Unit長
SPS.length
0x76
1
SPS NAUl Unit ヘッダー
SPS[0]
0x77
*
SPS NAUl Unit 本体
SPS
$pps
1
PPS数
1
$pps+1
2
PPS NAL Unit長
PPS.length
$pps+3
1
PPS NAL Unit ヘッダー
PPS[0]
$pps+4
*
PPS NAL Unit 本体
PPS

サンプル

          [stsd] 149bytes
            0x0000: 00 00 00 95 73 74 73 64 00 00 00 00 00 00 00 01 ....stsd........
            0x0010: 00 00 00 85 61 76 63 31 00 00 00 00 00 00 00 01 ....avc1........
            0x0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
            0x0030: 00 90 00 90 00 48 00 00 00 48 00 00 00 00 00 00 .....H...H......
            0x0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
            0x0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
            0x0060: 00 00 00 18 ff ff 00 00 00 2f 61 76 63 43 01 42 ........./avcC.B
            0x0070: 40 29 ff 01 00 18 67 42 40 29 95 b8 91 3b 01 10 @)....gB@)...;..
            0x0080: 00 00 03 00 10 00 00 03 03 c8 da 1c 32 e0 01 00 ............2...
            0x0090: 04 68 ce 3c 80                                  .h.<.

stts


 sttsボックスには、動画フレーム数関連の情報を設定します。
offset
size
内容
0x00
4
ボックス長
0x18
0x04
4
ボックスタイプ
'stts'
0x0c
4
登録数
1
0x10
4
フレーム数
フレーム数
0x14
4
フレーム数差
1

サンプル

          [stts] 24bytes
            0x0000: 00 00 00 18 73 74 74 73 00 00 00 00 00 00 00 01 ....stts........
            0x0010: 00 00 00 04 00 00 00 01                         ........

stss


 stssボックスには、キーフレームの情報を設定します。
offset
size
内容
0x00
4
ボックス長
(要計算)
0x04
4
ボックスタイプ
'stss'
0x0c
4
キーフレーム数
*
0x10+
4
キーフレーム番号
(数値列挙)

サンプル

          [stss] 20bytes
            0x0000: 00 00 00 14 73 74 73 73 00 00 00 00 00 00 00 01 ....stss........
            0x0010: 00 00 00 01                                     ....

stsc


 stscボックスには、チャンク情報を設定しますが、1チャンクに単純化して、フレーム数の情報を設定します。
offset
size
内容
0x00
4
ボックス長
0x1c
0x04
4
ボックスタイプ
'stsc'
0x0c
4
登録数
1
0x10
4
先頭チャンク
1
0x14
4
フレーム数
フレーム数
0x18
4
対応stsdインデックス
1

サンプル

          [stsc] 28bytes
            0x0000: 00 00 00 1c 73 74 73 63 00 00 00 00 00 00 00 01 ....stsc........
            0x0010: 00 00 00 01 00 00 00 04 00 00 00 01             ............

stsz


 stszボックスには、各フレームのデータ長の情報を設定します。
offset
size
内容
0x00
4
ボックス長
(要計算)
0x04
4
ボックスタイプ
'stsz'
0x10
4
フレーム数
(フレーム数)
0x14+
4
フレーム長
(数値列挙)

サンプル

          [stsz] 36bytes
            0x0000: 00 00 00 24 73 74 73 7a 00 00 00 00 00 00 00 00 ...$stsz........
            0x0010: 00 00 00 04 00 00 16 1d 00 00 00 ca 00 00 00 66 ...............f
            0x0020: 00 00 00 54                                     ...T


stco


 stcoボックスには、チャンクオフセットとして、フレームデータのファイル先頭からの位置を設定します。
offset
size
内容
0x00
4
ボックス長
0x14
0x04
4
ボックスタイプ
'stco'
0x0c
4
登録数
1
0x10
4
チャンクオフセット
ファイル先頭からの位置

サンプル

          [stco] 20bytes
            0x0000: 00 00 00 14 73 74 63 6f 00 00 00 00 00 00 00 01 ....stco........
            0x0010: 00 00 00 28                                     ...(

udta


 udtaはコンテナボックスです。metaボックスが入ります。 必須ではありませんが、ツール名を設定したいために当ブログでは生成しています。
offset
size
内容
0x00
4
ボックス長
(要計算)
0x04
4
ボックスタイプ
'udta'
0x08
*
子ボックス
metaボックス

meta


 metaボックスには、再生に直接影響しないデータが入ります。 必須ではありませんが、ツール名を設定するために当ブログでは設定しています。
 (c)のところには、◯の中にcの、コピーライトを表す文字コード 0xa9 を設定します。
 言語コードの詳細は後述します。
 ツール名は当ブログでは 'LogicLoversInc.' としています。15文字です。
offset
size
内容
0x00
4
ボックス長
(要計算)
0x04
4
ボックスタイプ
'meta'
0x0c
4
hdlr長
0x21
0x10
4
hdlrタイプ
'hdlr'
0x1c
4
mdirタイプ
'mdir'
0x20
4
applタイプ
'appl'
0x2d
4
ilst長
(残り全部)
0x31
4
ilstタイプ
'ilst'
0x35
4
(c)too長
(残り全部)
0x39
4
(c)tooタイプ
'(c)too'
0x3d
4
data長
(残り全部)
0x41
4
dataタイプ
'data'
0x45
4
タイプ
1
0x49
2
言語コード
0x2a0e
0x4d
*
ツール名
ツール名文字列

サンプル

    [meta] 92bytes
      0x0000: 00 00 00 5c 6d 65 74 61 00 00 00 00 00 00 00 21 ...\meta.......!
      0x0010: 68 64 6c 72 00 00 00 00 00 00 00 00 6d 64 69 72 hdlr........mdir
      0x0020: 61 70 70 6c 00 00 00 00 00 00 00 00 00 00 00 00 appl............
      0x0030: 2f 69 6c 73 74 00 00 00 27 a9 74 6f 6f 00 00 00 /ilst...'.too...
      0x0040: 1f 64 61 74 61 00 00 00 01 2a 0e 00 00 4c 6f 67 .data....*...Log
      0x0050: 69 63 4c 6f 76 65 72 73 49 6e 63 2e             icLoversInc.

全ボックスを繋げて完成


 各ボックスの解説は以上です。 これらを繋げることで、ボックス形式のバイナリーデータ、すなわちmp4ファイルのバイナリーデータが完成します。
[PR]

言語コード


 mp4ファイルに設定する言語コードは、aを1、bを2、、、zを26(2進数で11010) として、5ビットずつ割り当てた3文字15ビットを2バイトで表現します。 参考にしたmp4ファイルでは、undefined を意味する 'und' を変換した 0x55c4 が入っているケースをよく見ました。 当ブログでは、日本語を表す 'jpn' を変換した 0x2a0e を設定しています。
 言語コードを求めるプログラムは以下の通りです。
function getLanguageCode() {
  let iResult = 0x00;
  for ( let c of [ 'j' , 'p' , 'n' ] ) {
    const sAlphabet=' abcdefghijklmnopqrstuvwxyz';
    iResult <<= 5;
    iResult |= sAlphabet.indexOf(c);
  }
  return iResult;
}

実践コード


 本記事で解説した内容は、概念はmp4ファイルフォーマットに特有のものですが、 プログラミングは一般的な範囲のため、ソースコード例はほとんど載せていません。
 以下に、実践コードから、BOXに関するソースコードを2つだけピックアップして抜き出します。 その他も参考にされたい場合は、 この記事など、各mp4クリエイター記事のソースから、import 先を調べてみてください。 プログラムを読める方に限定するため、ここではあえて不親切にさせていただきますが、コードの再利用は、悪意の無い範囲でしたら、まったく問題ありません

新規BOXを生成する関数newBox


//**********************************************************************
// new box
//**********************************************************************
function newBox( sType , iLength ) {
  let aResult = new Uint8Array( iLength );
  aResult.fill(0);
  aResult.set( getArrayByInt( iLength , 4 ) , 4*0 );
  aResult.set( getArrayByStr( sType )       , 4*1 );
  return aResult;
}

コンテナボックスmoov生成する関数getMoov


//**********************************************************************
// get box 'moov'
//**********************************************************************
function getMoov( oMp4Encoder ) {
  let aMvhd = getMvhd( oMp4Encoder );
  let aTrak = getTrak( oMp4Encoder );
  let aUdta = getUdta( oMp4Encoder );
  let aMoov = newBox(  'moov' , iBOX_HEADER_SIZE + aMvhd.length + aTrak.length + aUdta.length );
  aMoov.set( aMvhd , iBOX_HEADER_SIZE );
  aMoov.set( aTrak , iBOX_HEADER_SIZE + aMvhd.length );
  aMoov.set( aUdta , iBOX_HEADER_SIZE + aMvhd.length + aTrak.length );
  return aMoov;
}

[PR]

まとめ


 本記事では、mp4動画ファイルを生成する工程のうち、 取得した NAL Unit の配列等を取り込み、mp4ファイルフォーマットであるボックス形式のバイナリーデータを作成する処理について、解説しました。
 mp4のボックス形式に関して、見やすくまとまっている資料が見つけられませんでしたので、一覧で詳述されたのは本記事が初めてかもしれません。 これが、mp4を生成しようとする皆さんのお役に、少しでも立てましたら幸いです。
 また、この完成までの試行錯誤に根気よく付き合ってくれた、生成AIのGemini2さんとClaude3.7さんに感謝です。

補足

  • 画像内のラスタライズ文字フォントにOpen Font LicenseNoto Sans Japaneseを使用しております。
  • 画像内のラスタライズ文字フォントにOpen Font LicenseNoto Sans Monoを使用しております。
  • (本記事公開後)本記事公開後に公開された記事へのリンクを追加しております。
カテゴリー:プログラミング解説,mp4ファイル生成解説
[PR]