Inside of WebM
2015.06.06
インフォコム株式会社
がねこまさし
@massie_g
1
WebMとは
• Wikipediaより http://ja.wikipedia.org/wiki/WebM
– 米Googleが開発している
– オープンでロイヤリティフリーな
– 動画コンテナフォーマット
• コーデック
– 映像: VP8 / VP9
– 音声: Vorbis
• コンテナ
– Matroskaのサブセット
Matroska
• Wikipediaより http://ja.wikipedia.org/wiki/Matroska
– ロシアの入れ子人形マトリョーシカにちなむ
– オープンソース(GNU LGPL)で開発中
– EBML(Extensible Binary Meta Language)採用
– http://www.matroska.org/technical/specs/index.html
– こちらの記事からたどり着きました。ありがとうございます!
• Media Source Extensionsを使ってみた (WebM編) @othersight
• http://qiita.com/tomoyukilabs/items/57ba8a982ab372611669
• EBML (Extensible Binary Meta Language)
– XMLを基に作られた、拡張性に優れたデータ格納方式
• 要素のみ、属性なし、入れ子あり
• ※むしろYAMLと言った方が近いかも
– 対応していない機能(タグ)は無視する
– テキストではなくバイナリで表現
EMBLのイメージ
<EBML>
<EBMLVersion>1</EBMLVersion>
<DocType>webm</DocType>
</EBML>
<Segment> …
<Info> … </Info>
<Tracks> … </Tracks>
<Cluster> … </Cluster>
<Cluster> … </Cluster>
…
</Segment>
これがバイナリで格納されている
WebMのおおまかな構造
ヘッダー 部分
映像/音声 部分
おまけ 部分
Header
Meta Seek Info
Segment Info
Tracks
(Chapters)
Cluster
Cluster
Cluster
Cluster
(Cue Data)
(Attachment)
(Tagging)
Matroska的
EBML
Segment
WebRTC的
Headerの主要要素
EBML:[1A][45][DF][A3]... EBML形式であることを示す
EBMLVersion:[42][86]… EBMLのバージョン 1
EBMLMaxIDLength:[42][F2]…ID(タグ)の最大長4
EBMLMaxSizeLength:[42][F3]…
要素値のサイズを指定する数値の最大バイト数8
DocType:[42][82]…ファイル形式”webm”
DocTypeVersion:[42][87]…ファイル形式のバージョン
Header
Headerの主要要素
EBML:[1A][45][DF][A3]... EBML形式であることを示す
EBMLVersion:[42][86]… EBMLのバージョン 1
EBMLMaxIDLength:[42][F2]…ID(タグ)の最大長4
EBMLMaxSizeLength:[42][F3]…
要素値のサイズを指定する数値の最大バイト数8
DocType:[42][82]…ファイル形式”webm”
DocTypeVersion:[42][87]…ファイル形式のバージョン
Header
ファイル名が変更されて拡張子がなくても
・先頭の4バイトが 0x1A, 0x45, 0xDF, 0xA3 であれば、高確率でWebM
・さらにDocTypeの値を見れば、 WebM であることがほぼ確定
Segment Infoの主要要素
Info:[15][49][A9][66]
TimecodeScale:[2A][D7][B1]…
トラックの時刻を示す単位を、ナノ秒で指定
→1,000,000 ナノ秒 = 1 ミリ秒
Duration:[44][89]…映像/音声の長さ
TimecodeScaleを単位として示す
※不明(継続中)の場合は0
Segment Info
Segment Infoの主要要素
Info:[15][49][A9][66]
TimecodeScale:[2A][D7][B1]…
トラックの時刻を示す単位を、ナノ秒で指定
→1,000,000 ナノ秒 = 1 ミリ秒
Duration:[44][89]…映像/音声の長さ
TimecodeScaleを単位として示す
※不明(継続中)の場合は0
Segment Info
TimecodeScaleで、この後登場する時刻、時間の単位を指定
・仕様上ナノ秒単位でも指定できるので、光の動きも捉えられそう
Durationでは再生時間を指定
・MediaRecorderで録画したものでは、0(ゼロ)が指定
Tracksの主要要素
Tracks:[16][54][AE][6B]… Video/Audioトラックのセット
TrackEntry:[AE]…トラック
TrackNumber:[D7]
CodecName:[25][86][88]
TrackType:[83] … Video/Audio
Video:[E0]
PixelWidth:[B0]
PixelHeight:[BA]
FrameRate:[23][83][E3]
Audio:[E1]
SamplingFrequency:[B5]
Channels:[9F]
Tracks
Trackについて
Tracks
・仕様上は、Trackは127個まで入れることが可能
※今のところ、Videoが1つ、Audioが1つ、というケースしか見たことがない
・TrackTypeは、Video/Audio以外にも字幕、ロゴなどが用意されている模様
Clusterの主要要素
Cluster[1F][43][B6][75]… 実際のVideoとAudioの断片を格納
Timecode:[E7] …その断片の開始時刻(TimecodeScaleの値単位)
SimpleBlock:[A3] …VideoかAudioの断片。複数
※SimpleBlockの中身は、EBML形式ではない
Cluster
Clusterの主要要素
Cluster[1F][43][B6][75]… 実際のVideoとAudioの断片を格納
Timecode:[E7] …その断片の開始時刻(TimecodeScaleの値単位)
SimpleBlock:[A3] …VideoかAudioの断片。複数
※SimpleBlockの中身は、EBML形式ではない
Cluster
・実際の映像(Video)と音声(Audio)のデータはClusterに含まれる
・一つのWebMファイルには複数のClusterが含まれる
WebM/Matroska/EBML
バイナリのレイアウト
EBML
EBMLVersion
“webm”
Segment
1
Tracks
CodecName
“VP8”
DOCType
タグ/Elementのバイナリ表現
• 3つのパートでタグ/Elementは構成される
ID DataSize Data
1~4バイト 1~8バイト 0~0x00FFFFFFFFFFFFFEバイト
ペタバイト級まで
可変長 可変長
可変長
実際のデータだけでなく、IDもDataSizeも可変長
厄介なヤツ。マジ勘弁…
ID部のバイナリ表現
1バイト目(2進)
1xxx-xxxx
01xx-xxxx xxxx-xxxx
001x-xxxx xxxx-xxxx xxxx-xxxx
0001-xxxx xxxx-xxxx xxxx-xxxx xxxx-xxxx
0x80~0xFF
0x40~0x7F
1バイト目(16進)
0x20~0x3F
0x10~0x1F
※先頭ビットも、IDの値に含める
DataSize部のバイナリ表現
xxxx-xxxxxxxx-xxxx xxxx-xxxxxxxx-xxxx xxxx-xxxx xxxx-xxxxxxxx-xxxx0000-0001
xxxx-xxxxxxxx-xxxx xxxx-xxxxxxxx-xxxx xxxx-xxxx xxxx-xxxx0000-001x
xxxx-xxxxxxxx-xxxx xxxx-xxxxxxxx-xxxx xxxx-xxxx0000-01xx
xxxx-xxxxxxxx-xxxx xxxx-xxxxxxxx-xxxx0000-1xxx
xxxx-xxxxxxxx-xxxx xxxx-xxxx0001-xxxx
xxxx-xxxxxxxx-xxxx001x-xxxx
xxxx-xxxx01xx-xxxx
1xxx-xxxx
1バイト目(2進)
7bits
14bits
21bits
28bits
35bits
42bits
49bits
56bits
値はBig Endian のUnsigned Integer
※先頭ビットは、DataSizeの値に含めない
※すべてのビットが1の値は予約済(多分)
Data部分
• DataSizeで指定されたバイト数
• タグ(ID)の種類によって、さまざま型
– 整数: unsigned int, signed int (Big Endian)
– 文字列: ASCII string, UTF-8 string
– 実数: float (Big Endian)
– 日付: date (Big Endian)
– バイナリ: binary
– 他の複数のタグ: master
• タグの入れ子の構造
• 型を識別するルールは無い …みたい
– タグの値と型を対応付ける辞書を持つしかなさそう
– http://www.matroska.org/technical/specs/index.html
20
http://www.matroska.org/technical/specs/index.html より
ID 型
解析例(1) EBMLVersion
• 16進表記: 42 86 81 01
• IDの最初のバイトの2進表記: 0100 0010
– → IDは2バイトの[42][86]
– 一覧表から、EBMLVersionと判明
• DataSizeの最初のバイトの2進表記: 1000 0001
– → DataSizeは1バイト
– サイズは 0000 0001 = 1バイト
• Dataは1バイト。先ほどの一覧表から、型はUnsigend Int
– → 値は 1
• 結果: EBMLVersion = 1
解析例(2) DocType
• 16進表記: 42 86 84 77 65 62 6D
• IDの最初のバイトの2進表記: 0100 0010
– → IDは2バイトの[42][84]
– 一覧表から、DocTypeと判明
• DataSizeの最初のバイトの2進表記: 1000 1000
– → DataSizeは1バイト。サイズは 0000 1000 = 4バイト
• Dataは4バイト。先ほどの一覧表から、型はASCII string
– 77 65 62 6D → 値は "webm"
• 結果: DocType = "webm"
WebMのパース
• 先頭から1バイトづつパースしていけば、タグの内容を解析
可能
• とくに興味が無いタグや、理解できないタグが出現した場合
– データー長はルールに従って算出可能 → スキップすることが可能
• ※もちろんライブラリもあります
– libebml http://dl.matroska.org/downloads/libebml/
– libmatroska http://dl.matroska.org/downloads/libmatroska/
– yamka https://sourceforge.net/projects/yamka/
※使ってはいません。詳細不明
• Node.js のサンプルコードを書いてみました
– https://gist.github.com/mganeko/9ceee931ac5dde298e81
– メモリ上に一括して読み込む、しょぼい実装ですが…

Inside WebM