くまりゅう日記

もっと過去の日記
[.NET | BeOS | Blender | COLLADA | fossil | mono | monotone | NPR | OpenGL | PeerCastStation | Riko | Ruby | Silverlight | TRPG | XNA | ゲーム | ゲーム作り | プログラム | | 模型]

2012-05-16

日記

土日になると体調崩す。 かといって無駄に休日出勤するわけにも……つっても忙しいのでそれ程無駄でもないんだが。

最近は仕事とmkv読み込みしかしてないので特に日記書くこともない。あ、ゲームマーケットにちょこっと行ったくらい。

[PeerCastStation] Matroska読み込み

PeerCastStation自体は手を入れてないんだが。Matroska(.mkv)読み込みをずっとやってた。

MatroskaはEBMLというバイナリ化XMLみたいなもので構成されてるのでまずはそれを読み込み。 要素のIDやらサイズが可変長整数なのが面倒だが難しくはなかった。

一点だけ、整数は可変長整数なんだーと思って要素のデータがuintやintの物も可変長整数としてパースしてたら間違っていた。こいつらも可変長であることは違いないが、サイズは事前にわかるので普通に1〜8バイトのビッグエンディアン固定長整数として読んでやればいいだけだった。ちゃんと仕様を読んでなかったのが悪いが紛らわしい。

書けはしたのでじゃあいざテストファイルを読み込みましょうとやってみたら上手くいかん。 単純なファイルtest1.mkvは大丈夫だったんだが、当初の目的であるライブストリーミング用ファイルtest4.mkvを読むと途中でおかしなデータにぶちあたって止まってしまう。バイナリエディタで見るとSegmentタグのあとに0xA0が130バイトちょい連続していてファイル壊れてるっぽく見えるんだが、壊れてるテストファイルはtest7.mkvだと言うし、検証アプリのmkvvalidateでも文句言われないしじゃあこれは正当なのかと。

しばらく悩んでると、同じこと言ってるForumのスレを教えてもらったので読んでみた。確かにぶっこわれてるけどストリーミングなら壊れるもんなんだから頑張れ、的なことが書いてあった。このくらいのゴミデータはぶっこわれてるうちに入らないので検証器も普通に通るようだ。あと普通にGStreamerから吐かれたファイルらしい。いつのか知らんがGStreamerぶっこわれてますよそれ……。

壊れてるファイルを読むのはまあいいんだけど、復帰方法が具体的に書かれてるところないんだよね……。一応Streaming方法のページにも上記スレにも次のClusterタグを探せよと書かれているんだが、Segmentの直後なのでここからClusterまで飛ばしてしまうとTrackとかのコーデック情報が入ってるタグもすっとばすので再生できないっすよ。

仕方ないのでmkv読み込むCとかC++のライブラリソースを眺めてみよう。 するとすっごいめんどくさいのがわかった。 不正な要素が来たと判断するのに必要な項目がたくさんある。

  • 可変長整数であるIDの長さが4バイトより長い
  • 可変長整数であるサイズの長さが8バイトより長い

上記の場合は問答無用でおかしい。ただしIDもサイズも最大長はファイル先頭のEBMLタグ内に書かれてるので本当はそれを参照すべき。

  • 今の読取位置+サイズが今の親のサイズを突破する
  • 仕様的に今の親にくっつくIDではない

上記の場合は一個親のタグにくっつかないかなーというのを再帰的に調べる必要がある。というわけで親の読取り開始位置と親のサイズと親のIDとそれに仕様的にくっつくと思われるID一覧を知ってないといけない。

  • VoidとかCrc32とかのどこにでもくっつくタグがある
  • 拡張性が売りのmkvさんなので知らないIDのタグが来る可能性があるんだがどうしよう

上記の場合の処理がよくわかってない。特に後者。知らないタグなのかぶっこわれたデータなのか判別できないんだが、どうもタグによって子に知らないタグを許可するかどうか切り替えてるっぽい?やっぱりよくわかってないが。

というわけでなかなか難しいことはわかった。OGGやMPEG-2 TSなんかと比べると明らかにストリーミング向けじゃないのな。階層構造とストリーミングは相性悪いわ。mp4でライブストリーミングできない理由がわかった気がする。まああっちは仕様上mkvみたいに不定サイズのタグを持てないからってのもあるんだけど、やったとしても破壊に弱いから対応しないんだろうな。

さてはて困ったな、と言いたいところだが、PeerCastで扱う限りはそんなに難しいことしなくていい。TCPでしか通信できないんでパケット破壊とかそうそう起きるものではない1。 さらに言うならPeerCastでコンテナのパースをしないといけないのは配信開始の部分だし、そこもヘッダとそのあとのボディに分けるだけ。 というわけで 判別が必要そうなところだけ見てあとは出来る限りすっとばそう。

  1. EBMLを読み込む。
  2. Segmentが見つかるまですっとばしてSegmentを読み込む。すっとばした分とSegmentはヘッダとして保持しておく。
  3. Clusterが見つかるまですっとばす。こちらもすっとばした分はヘッダとして保持しておくが、Clusterはヘッダではないのでまだ入れない。
  4. Clusterを読み込んでボディ部に追加。ここからボディ開始。
  5. SimpleBlockかBlockGroupかClusterかSegmentかEBMLが見つかるまで読み込んでボディ部につっこむ。
  6. Clusterが見つかったら普通に読み込んでボディに追加。その後再度5へ。
  7. SimpleBlockが見つかったら普通に読み込んでボディに追加。SimpleBlockとBlockGroupは混ざらないはずなので、以降BlockGroupは無視するようにして再度5へ。
  8. BlockGroupが見つかったら普通に読み込んでボディに追加。SimpleBlockとBlockGroupは混ざらないはずなので、以降SimpleBlockは無視するようにして再度5へ。
  9. EBMLかSegmentが来たら新しいストリームが始まったっぽい。保持してたヘッダをクリアして1か2へ。
  10. それ以外が来たらなんかぶっこわれたか知らんタグが来たみたい。次のClusterかEBMLかSegmentがみつかるまですっとばして読み込んだら1か2か5へ戻る。

これだけだ。ちゃんとパースして再生するのはプレイヤーの役目なので、途中で受け渡しするだけのPeerCastではあまり構造については深く考えないことにして、ごく少数の知ってるデータ以外は何も見ずに右から左に流すだけにした。 ただしどこからがヘッダ部でどこまでがボディ部、というかどこからまた新しいヘッダが流れてきたのかを判別はする必要はあるのである程度同期はとってる感じ。 同期をとるワードが状態によって変わるけどね。

あとはこれC#で書くだけか。うーん、まあすごい大変ってほどではないかな……。

  1. 生成されたストリームが既にぶっこわれてるのは仕方ないんで対処しないといけないんですけどね。 


ページのトップへ | トップ «前の日記(2012-05-07) 最新 次の日記(2012-05-24)» | 編集 | kumaryu.net by kumaryu