前回で、Transport Stream から一塊の PSI セクションを抜き出せたので、次はパースをします。
PAT (Program Association Table) は PID が固定ですし、構造も単純なのでまずはこれから考えます。
program_association_section() {
table_id 8 uimsbf
section_syntax_indicator 1 bslbf
'0' 1 bslbf
reserved 2 bslbf
section_length 12 uimsbf
transport_stream_id 16 uimsbf
reserved 2 bslbf
version_number 5 uimsbf
current_next_indicator 1 bslbf
section_number 8 uimsbf
last_section_number 8 uimsbf
for (i = 0; i < N; i++) {
program_number 16 uimsbf
reserved 3 bslbf
if (program_number == '0') {
network_PID 13 uimsbf
} else {
program_map_PID 13 uimsbf
}
}
CRC_32 32 rpchof
}
payload の中身が PAT であるパケットの例です
47 60 00 1B 00 00 B0 1D 7E 87 D9 00 00 00 00 E0
10 5C 38 E1 01 5C 39 E1 02 5D B8 FF C8 5D B9 FF
C9 90 3F 0A 85 FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF
意味合いごとに改行を加えると以下のようになります:
47 60 00 1B # ヘッダ4バイト
00 # pointer_field
00 B0 1D 7E 87 D9 00 00 # 固定長部分 (table_id 〜 last_section_number)
# ここから可変長部分。長さは section_length = 0x1D = 29 バイトから固定部分 5 バイトと CRC_32 の 4 バイトを引いた20バイト
00 00 E0 10 # 1つ目のループ。 program_number が 0 なので、 network_id = 0x010
5C 38 E1 01 # 2つ目。 program_number = 0x5C38, program_map_PID = 0x101
5C 39 E1 02 # 3つ目。 program_number = 0x5C39, program_map_PID = 0x102
5D B8 FF C8 # 4つ目。 program_number = 0x5DB8, program_map_PID = 0x1FC8
5D B9 FF C9 # 5つ目。 program_number = 0x5DB9, program_map_PID = 0x1FC9
# 20バイトパースしたのでここで可変長部分終了
90 3F 0A 85 # CRC_32
# 余った部分は 0xFF で埋められる
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF
Program Association Table は、プログラム番号ごとに PMT の PID を指定しています。別途 Service Description Table を見ると分かるのですが、program_number 0x5C38 は TOKYO MX1、 0x5C39 は TOKYO MX2、 0x5DB8 は MXワンセグ1、 0x5D89 は MXワンセグ2 です。だいたい必要になるのは PAT のループで最初に出てくる program_number なのですが、場合によってはそうではないこともあるようで、一律最初の組み合わせで処理をしたために不具合が起きたみたいな例が昔のフリー実装ではあったようです。
recpt1 で channel 20 を録画した場合は program_number 0x5C38 の PMT を参照すべきであるというのは固定情報なので、設定ファイルを作るときはひとまとめにしておいたほうがいいでしょう。
次に PMT (Program Map Table) です。動画、音声、その他もろもろの PID が何番なのかを指定するテーブルです。PMT はそれほど情報量が多くならないので、たいてい1パケットに収まっていますが、下の例では2パケットにまたがっています。
47 61 01 1E 00 02 B0 C7 5C 38 D1 00 00 E1 00 F0
09 09 04 00 05 E0 31 C1 01 84 02 E1 21 F0 06 52
01 00 C8 01 53 0F E1 12 F0 03 52 01 10 0D E7 40
F0 0F 52 01 40 FD 0A 00 0C 33 3F 00 03 00 03 FF
BF 0D E7 50 F0 0A 52 01 50 FD 05 00 0C 1F FF BF
0D E7 51 F0 0A 52 01 51 FD 05 00 0C 1F FF BF 0D
E7 52 F0 0A 52 01 52 FD 05 00 0C 1F FF BF 0D E9
60 F0 0A 52 01 60 FD 05 00 0C 1F FF BF 0D E9 61
F0 0A 52 01 61 FD 05 00 0C 1F FF BF 0D E7 5E F0
10 52 01 5E 09 04 00 05 FF FF FD 05 00 0C 1F FF
BF 0D E7 5F F0 10 52 01 5F 09 04 00 05 FF FF FD
05 00 0C 1F FF BF 0D E9 6E F0 10 52
47 21 01 1F 01 6E 09 04 00 05 FF FF FD 05 00 0C
1F FF BF 32 23 3E 76 FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF
まず、一連の PMT がこの2つのパケットにまたがっていることをヘッダから確認しましょう。 一つ目のパケットは payload_unit_start_indicator ( 2 バイト目の 2 ビット目) が 1 で、確かにこのパケットから payload が開始されます。一方2パケット目の payload_unit_start_indicator は 0 です。
また、 PID ( 2 バイト目の下 5 ビットと 3 バイト目) は両方とも 0x101 で、上で調べた program_id = 0x5C38 のものです。 continuity_counter ( 4 バイト目の下 4 ビット) は上が 0x0E 、下が 0x0F でパケットロスはありません。 adaptation_field_control ( 4 バイト目の上から 3 〜 4 ビット) はともに 0x01 で、 adaptation_field はありません。ゆえに payload_unit_start_indicator が 1 である上のパケットの 5 バイト目は pointer_field で、その値は 0 なので 6 バイト目から payload が始まります。
PMT のデータ構造は下に示す通りで、 payload の 2 バイト目下 4 ビットと 3 バイト目が section_length です。0xC7 = 199 バイトで、この値は section_length から先に何バイトがあるかを示しています。ヘッダ 4 バイト、 poiner_field 1 バイト、 payload のうち section_length の前にあるバイトと section_length 自体の合計が 3バイト、あわせて 8 バイトをすでに使っているので、 199 バイトのうちこのパケット内が 180 バイト、次のパケットに 19 バイトです。次のパケットもやはりヘッダ 4 バイトから始まるので、あわせて 23 バイトになり、 24 バイト目からは余った部分です。例に出したパケットは確かに 2 パケット目の 24 バイト目以降が 0xFF になっており、以上の議論通りであることがわかります。
TS_program_map_section(){
table_id 8 uimsbf
section_syntax_indicator 1 bslbf
‘0’ 1 bslbf
reserved 2 bslbf
section_length 12 uimsbf
program_number 16 uimsbf
reserved 2 bslbf
version_number 5 uimsbf
current_next_indicator 1 bslbf
section_number 8 uimsbf
last_section_number 8 uimsbf
reserved 3 bslbf
PCR_PID 13 uimsbf
reserved 4 bslbf
program_info_length 12 uimsbf
for (i=0;i<N;i++){
descriptor()
}
for (i=0;i<N1;i++){
stream_type 8 uimsbf
reserved 3 bslbf
elementary_PID 13 uimsbf
reserved 4 bslbf
ES_info_length 12 uimsbf
for(i=0;i<N2;i++){
descriptor()
}
}
CRC_32 32 rpchof
}
意味合いごとに改行と記号を加えると以下のようになります。
47 61 01 1E # ヘッダ4バイト
00 # pointer_field
02 B0 C7 5C 38 D1 00 00 E1 00 F0 09 # 固定長部分 (table_id 〜 program_info_length)
# program_info_length が 0x09 なので、後続 9 バイトは descriptor()
[09 04 00 05 E0 31, C1 01 84] # 記述子はすべて 1 バイト目が descriptor_tag 、 2 バイト目が descriptor_length なので
# descriptor() の中にどんな記述子が何バイト出てくるかは、そこを見れば分かる。
# この例では tag 0x09 の 4 バイトの記述子と、 tag 0xC1 の 1 バイトの記述子が存在する。
# descriptor() のあとは可変部分。長さは section_length 0xC7 = 199 から program_info_length 0x09 = 9 と固定長部分 9 と CRC_32 の 4 バイトを引いた 177 バイト
# stream_type の値が何に対応しているのかは ISO 13818-1 2.4.4.10 節表 2-29 を参照
02 E1 21 F0 06 [52 01 00, C8 01 53] # 1つ目のループ。 stream_type = 0x02(動画), elementary_PID = 0x121, 記述子長 6 バイト
0F E1 12 F0 03 [52 01 10] # 2つ目のループ。 stream_type = 0x0F(音声), elementary_PID = 0x112, 記述子長 3 バイト
# ここから下は stream_type = 0x0D で、 ISO/IEC 13818-6 type D なるデータなのらしい。よく調べてない
0D E7 40 F0 0F [52 01 40, FD 0A 00 0C 33 3F 00 03 00 03 FF BF]
0D E7 50 F0 0A [52 01 50, FD 05 00 0C 1F FF BF]
0D E7 51 F0 0A [52 01 51, FD 05 00 0C 1F FF BF]
0D E7 52 F0 0A [52 01 52, FD 05 00 0C 1F FF BF]
0D E9 60 F0 0A [52 01 60, FD 05 00 0C 1F FF BF]
0D E9 61 F0 0A [52 01 61, FD 05 00 0C 1F FF BF]
0D E7 5E F0 10 [52 01 5E, 09 04 00 05 FF FF, FD 05 00 0C 1F FF BF]
0D E7 5F F0 10 [52 01 5F, 09 04 00 05 FF FF, FD 05 00 0C 1F FF BF]
0D E9 6E F0 10 [52 # ここで1つ目のパケットは終了
47 21 01 1F # 2つ目のパケットのヘッダ
01 6E, 09 04 00 05 FF FF, FD 05 00 0C 1F FF BF]
# 177バイトパースしたのでここで可変長部分終了
32 23 3E 76 # CRC_32
# 余った部分は 0xFF で埋められる
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF
ということで、動画の PID が 0x121 で、音声の PID が 0x112 ということが分かりました。動画については、記述子リストの 2 つ目が 0xC8 で、これはビデオデコードコントロール記述子 (ARIB-STD-B10-2 6.2.30) です。第 3 バイトの上から 3 〜 6 ビット目が _format で、0x53 は 2 進数で 0b01010011 なのだから video_decode_format は 0b0100 で、 ARIB-STD-B10 第2部 表6-60 よりこの動画は 480i (SD) です。 TOKYO MX では録画したい番組が HD なのに一つ前の番組が SD なので扱いに困るということがありますが、この値を追いかければ SD を捨てて HD だけ残すということも可能です。
音声の方は、このデータだけではステレオかモノラルか分かりません。以前、録画したい番組がステレオなのに一つ前の番組がモノラルでやはり扱いに困るということがありました。その問題については別の解法が必要です。 (EIT を見れば番組内の音声がステレオかモノラルかは分かるので、番組切り替えのタイミングをうまく掴んで切り離すとか、音声バイナリじたいを調べてモノラルからステレオに変わったところで切り離すとかいった手があります)