MediaRecorderAPIで作ったwebmをseekableにしたい
これまでのあらすじ
先駆者がいるのは知ってる
知ってるが、自分でやりたくなるやつ
最低限の実装をブックマークレットにそのまま埋め込めたらよくない?
webm
プリミティブ型、VINT、不定長要素の終端ルール、EBMLHeader等の定義、編集時にpositionを変えない工夫など
Header + Segment > {Info+, SeekHead*, Cluster*, Cues?, Tags*, ...}
不定長Segment(sizeを全ビット立てる)にするか、固定長のSegmentを連続させる
固定長Segmentの連続はHeader入れるのだろうか?(EBML Stream)
MetaSeekもCuesもなければnon-seekableと判断すべき → MediaRecorderが生成するwebmがこれ
ここでいうshouldはmustとしてmuxerは取り扱うべき(SHOULD)、という温度感
Cues要素の存在がわかるようにSeekHeadを入れる
SeekHeadはトップレベル要素の目次。これがないとファイル全体からトップレベル要素を探すことになる。
SeekHeadを置く場合、Segmentの先頭に置く。SeekHeadからSeekHeadを参照して連結することもできるが、いずれにせよ全てのトップレベル要素をSeekHeadに載せなければならない。
Cues要素をCluster群より前に入れる(それはそうだ)
動画のキーフレームのみを含める(サイズ節約)
キーフレームはClusterの先頭に置く(Clusterの中から目的のBlockを探すことになるからかな?CueRelativePositionをつける手もありそうだけど)
これ元々はClusterの順序保証もないのかそういえば(webmではTimestampが単調増加することを要求しているが)
Seeking will be disabled if the webm file does not have a key frame Cues element.
mkvtoolnix-gui がつよい。多分mkvinfoでもいいけど、こういうのはツリーを展開したりできるGUIつよい。
特定の要素のバイト列も確認できるので、デバッグにもってこい
各種出力
MediaRecorder
基本的にはChromeもFirefoxも Segment(inf) > (Info + Tracks + Cluster(inf)*) という構造
キーフレームはClusterの先頭に存在する
Firefoxは空のSeekHeadが存在する
分割された場合、Chromeは要素ヘッダの途中だろうとぶった切ってくるが、FirefoxはClusterの切れ目に揃えてるっぽい
ts-ebml: Duration, 先頭Cues, SeekHead(Info,Tracks,Cues,0)
mkclean: Duration, 先頭Cues, SeekHead(Info,Tracks,Cues,Cluster,201) ffmpeg: Duration, 末尾Cues, SeekHead(Info,Tracks,Tags,Cues,226)
mkvtoolbox(mkvmerge): Duration, 末尾Cues, SeekHead(Info,Tracks,Cues,4096)
実験
チャンクが分割された場合、ちょうどいいところで切られるのか
A. 切られるとは限らない
例の記事だとSeekHeadを追加してるだけだが、Cuesの方がよいのでは?
The user agent must accept and ignore Cues or Chapters elements that follow a Cluster element. というのも気になる
ts-ebml、実行してみたら普通にCues書いてた
実験結果
※Range requests に対応していないとキューポイント知っててもシークできない
Win10の動画アプリ
Durationがない(Cuesに関わらず): バーが伸びない
Cuesがない/SeekHeadなしの末尾Cue: シークできない
Chrome
Durationがない(Cuesに関わらず): 動画長なし、バーが伸びない
最後まで読みこむとシーク可能になる
積極的な先読みはないらしく、20秒程度の動画でスロットルなしでもほぼ最後まで再生しないとシーク可能にならなかった
それまでは手前にシークすることもできない
Cuesなし/SeekHeadなし末尾Cue: クリックはできるが読み込みを待つ
先頭Cue/SeekHead+末尾Cue: どこでもシーク可能(末尾Cueの場合Cueを取りに行く)
Firefox
Durationなし(Cuesに関わらず): 読みこめた分動画長が伸びていく。先読みあり。
Cuesなし/SeekHeadなし末尾Cue: 読み込めたところまでシーク可能。先読みあり。
先頭Cue/SeekHead+末尾Cue: どこでもシーク可能(末尾Cueの場合Cueを取りに行く)
進捗
DataViewでさぼれるかと思ったけど結局微妙な長さの数値読んだりできないのであれ
EBMLSchemaから定義持ってきてやるのはできたのでよき
EBML側で定義されているものはSchemaないのでSpecから手でおこした
2021/2/8 実験用に↑のやつで一部要素のVoid化や不定長化をできるようにしてみた
5バイト以上のreadUintが盛大にバグってたのも直した(bitwise operator が32bit想定とは…)