MPEG2 TSの仕様について調べた(1)

前回の流れからJavaでTSファイルを読み込みたい訳だが、JDKにはそんなAPIはない。ということは誰かが作ったものを利用するか自分で作らないと駄目ってことだ。まあその誰かが作ったものが前回までで失敗しているわけであるが。
FFMPEGを利用して何かやろうとしている物もあったが、あれは一般の利用する側としては全てのメディアに対応してて便利だが使用するとなるとメンドイし、何より開発がとまってるっぽい。JNIかJNAを使って何らかのデコードDLLを利用する手も考えたが、そもそもその手のDLLは32ビットであるので目標から考えるといまいち。Windows7のmsmpeg2vdec.dllを使えばWindowsの標準だし64ビットだしいいじゃん!と思ったがDLLの利用関数が調べられなくて頓挫した。これの使い方が載っている場所があったら教えて欲しい。


という訳でもう面倒になったので一からPureJavaで読み込める物を作ることにした。
当然仕様から調べる必要がある。こういうときはWikipediaさんに限る。

MPEG-2 SYSTEMS(ここのトランスポートストリーム)

なるほどだから拡張子はTSなのか。更にこのMPEG2-TSの1パケット(188byte)についての説明が載ってたのが以下。

http://www.mpeg.co.jp/libraries/video_it/video_03.html
http://www.mpeg.co.jp/libraries/video_it/video_04.html

これらにはパケットにくっついているヘッダ(先頭4byte、PIDを持つ)とアダプテーションフィールド(ヘッダの後ろ2byte+α)についての説明が載っていた。
なるほど大体ヘッダについては理解できたので、早速JavaからTSファイルのヘッダの解析をしてみた。参照したTSファイルはドラゴンボール改の1話分30分をHDUSで録画したもので、そこから先頭1024パケットを見てみた。

File Byte size: 2147483647
Bytes/PaketSize: 11422785
PID:0112 - 14 packets
PID:0111 - 977 packets
PID:0060 - 1 packets
PID:0012 - 24 packets
PID:0102 - 2 packets
PID:0101 - 2 packets
PID:0100 - 2 packets
PID:0000 - 2 packets

===メモリチェック===
初期サイズ:100631936
使用サイズ:1006896
保証サイズ:96468992
最大サイズ:1431699456

PIDが0x0000(PAT)と0x0012(EIT)のは予約された特別なやつ。まあどう考えても0x0111が映像部分で0x0112が音声部分だろう。PATのペイロード部分(ヘッダの後ろの184byte)に映像や音声のPIDが格納されたPMTのPIDが入ってるようなのでそれを見てみることにする。
[00, 00, b0, 19, 7c, 86, f7, 00, 00, 00, 00, e0, 10, dc, 30, e1, 01, dc, 31, e1, 02, dd, b0, ff, c8, 5f, 50, 6f, 22, ff, .... ff, ff]
単純に見ると意味不明。ffの入っている部分はいらない部分だと推測はできる。
取り合えずPATの情報を探すとPythonでTSの解析を書いてる方のサイトに情報があったので、それを見るとデジタル放送に関する規格についての説明文献へのリンクを発見した。これが欲しかった!

ARIB標準規格:デジタル放送に使用する番組配列情報

これによるとPATのペイロード部分は以下の図のような構造になっているようだ。

数字の単位はビット。さっき見たPATのペイロードは

00, 00, b0, 19, 7c, 86, f7, 00, 00, 00, 00, e0, 10, dc, 30, e1, 01, dc, 31, e1, 02, dd, b0, ff, c8, 5f, 50, 6f, 22, ff, ....

だったから、ペイロードの先頭の0x00は区切りとして使われていてその次がPATのPIDである0x00は正しいのが分かる。後は図と照らし合わせて行くと、いきなりおかしいのが分かる。規格によると3番目と4番目のバイトにおいて2進表示で1011からすなわち0xb000以上であると定めている。ディジタル映像伝送回線という資料も確認すると確かに同じことが書いてある。今の表示から見ると0xb019 = 1011000000011001である。これは正しい。

続きの12ビットはセクション長を表してる。今のデータでは0xb019の頭4ビットを切ったものなので、0x0019 = 25バイトになる。セクション長はきちんと見てないがその次のバイトからCRCの終わりまで何バイトあるからしいから0x19の後から25バイト数えると丁度0xffに切り替わるポイントを示しているので正しいらしい。

次の16ビットはトランスポートストリームIDなのでTS固有の値が入っているっぽい。ここでは0x7c86である。

その次の8ビットは0xf7なので2進表示だと11110111である。頭の2ビットについては11と規定されており正しい。続く5ビットはバージョン番号なので取り合えず確保して、最後の1ビットはカレントネクスト指示で「送られてきたPATが現在有効であるか、次に有効であるかを示す。 」を判断するらしい。今は1なので現在有効であることが分かる。

続く8ビットがセクション番号で、更に8ビットが最終セクション番号となっているがどちらも0である。なぜだ。ここのところはよく分からない。

更に規格書によると続く4バイト毎ににネットワークPIDかPMTのPIDがループして格納されていることになっている。4バイト毎に見やすくしてみると以下となる。


[00, 00, e0, 10] [dc, 30, e1, 01] [dc, 31, e1, 02] [dd, b0, ff, c8] [5f, 50, 6f, 22] ff, ff, ....

今のデータではセクション長が25バイトで規定情報として5バイト使っているので、残りの20バイト即ち5個のセットがあれば正しいことになる。実際それは正しく格納されている。このうち最後の4バイトについては「PAT全体のCRCの計算値が入る」とされているので、4個のPID情報が格納されていることになる。
各4バイトについては前2バイトが放送番組番号識別で、後ろ2バイトがPIDになる。うちPIDについては上位3ビットに111が立っている規約のためPIDとして認識するためには上位3ビットを除いた13ビットで考える必要がある。そう考えると今のPATデータから得られるPIDは[0x0010, 0x101, 0x0102, 0x1fc8]の4つだ。4つ目のPID[0x1fc8]はワンセグ用のPIDで固定になっているらしい。PIDが0の4バイトについてはネットワークPID=0x0010を指し示す仕様で、実際今のデータでも1つ目はその規則を守っている。
以上によりPATのパケットの中からPMTのPID[0x101, 0x0102]が2つ得られた。

それぞれのPIDを持つパケットは2つずつあるようだ。続きはまた次に。
ちなみに以下は1万個のパケットについて上記の解析を行った結果の出力。


File Byte size: 2147483647
Bytes/PaketSize: 11422785

===== PID & Packet count =====
PID:0112 - 137 packets
PID:0111 - 9616 packets
PID:0012 - 42 packets
PID:1fc8 - 2 packets
PID:0010 - 1 packets
PID:0152 - 24 packets
PID:0027 - 1 packets
PID:0151 - 109 packets
PID:0150 - 3 packets
PID:0024 - 1 packets
PID:0023 - 2 packets
PID:0102 - 11 packets
PID:0101 - 11 packets
PID:0060 - 11 packets
PID:0100 - 18 packets
PID:0000 - 11 packets

===== analyze PAT payload =====
SecSize:25 TSI:7c86 VER:27 NEXT:1 SECN:0 LSECN:0 [ID:0000, NET_PID:0010] [ID:dc30, PMT_PID:0101] [ID:dc31, PMT_PID:0102] [ID:ddb0, PMT_PID:1fc8] CRC:5f506f22
SecSize:25 TSI:7c86 VER:27 NEXT:1 SECN:0 LSECN:0 [ID:0000, NET_PID:0010] [ID:dc30, PMT_PID:0101] [ID:dc31, PMT_PID:0102] [ID:ddb0, PMT_PID:1fc8] CRC:5f506f22
SecSize:25 TSI:7c86 VER:27 NEXT:1 SECN:0 LSECN:0 [ID:0000, NET_PID:0010] [ID:dc30, PMT_PID:0101] [ID:dc31, PMT_PID:0102] [ID:ddb0, PMT_PID:1fc8] CRC:5f506f22
SecSize:25 TSI:7c86 VER:27 NEXT:1 SECN:0 LSECN:0 [ID:0000, NET_PID:0010] [ID:dc30, PMT_PID:0101] [ID:dc31, PMT_PID:0102] [ID:ddb0, PMT_PID:1fc8] CRC:5f506f22
SecSize:25 TSI:7c86 VER:27 NEXT:1 SECN:0 LSECN:0 [ID:0000, NET_PID:0010] [ID:dc30, PMT_PID:0101] [ID:dc31, PMT_PID:0102] [ID:ddb0, PMT_PID:1fc8] CRC:5f506f22
SecSize:25 TSI:7c86 VER:27 NEXT:1 SECN:0 LSECN:0 [ID:0000, NET_PID:0010] [ID:dc30, PMT_PID:0101] [ID:dc31, PMT_PID:0102] [ID:ddb0, PMT_PID:1fc8] CRC:5f506f22
SecSize:25 TSI:7c86 VER:27 NEXT:1 SECN:0 LSECN:0 [ID:0000, NET_PID:0010] [ID:dc30, PMT_PID:0101] [ID:dc31, PMT_PID:0102] [ID:ddb0, PMT_PID:1fc8] CRC:5f506f22
SecSize:25 TSI:7c86 VER:27 NEXT:1 SECN:0 LSECN:0 [ID:0000, NET_PID:0010] [ID:dc30, PMT_PID:0101] [ID:dc31, PMT_PID:0102] [ID:ddb0, PMT_PID:1fc8] CRC:5f506f22
SecSize:25 TSI:7c86 VER:27 NEXT:1 SECN:0 LSECN:0 [ID:0000, NET_PID:0010] [ID:dc30, PMT_PID:0101] [ID:dc31, PMT_PID:0102] [ID:ddb0, PMT_PID:1fc8] CRC:5f506f22
SecSize:25 TSI:7c86 VER:27 NEXT:1 SECN:0 LSECN:0 [ID:0000, NET_PID:0010] [ID:dc30, PMT_PID:0101] [ID:dc31, PMT_PID:0102] [ID:ddb0, PMT_PID:1fc8] CRC:5f506f22
SecSize:25 TSI:7c86 VER:27 NEXT:1 SECN:0 LSECN:0 [ID:0000, NET_PID:0010] [ID:dc30, PMT_PID:0101] [ID:dc31, PMT_PID:0102] [ID:ddb0, PMT_PID:1fc8] CRC:5f506f22

===== memory check =====
初期サイズ:100631936
使用サイズ:4531640
保証サイズ:96468992
最大サイズ:1431699456


パケットの数が増えるとPIDの数が増えるがキーであるPATは全て同じ事、PMTのPIDも変わらない事が分かる。

以下はMPEG2-TSの映像部分であるMPEGについての技術的な説明が載っているところ。MPEG2については10章から先。

MPEG Video 技術
参考:MPEG公式ページ

テレビ放送についてのメモ
http://elm-chan.org/docs/rs170a/spec_j.html

自分用メモ

以下JNAについて調べたときのURLメモ
http://www.avisynth.info/?FFmpegSource
http://d.hatena.ne.jp/t-katochin/20090122/1232615970
http://hp.vector.co.jp/authors/VA033015/jnasamples.html
http://m-ono.info/tech/programming/jna/
http://d.hatena.ne.jp/ujiujise/20091202/p1

0 コメント:

コメントを投稿