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
意味合いごとに改行を加えると以下のようになります:
# ヘッダ4バイト
47 60 00 1B
# pointer_field
00
# 固定長部分 (table_id 〜 last_section_number)
00 B0 1D 7E 87 D9 00 00
# ここから可変長部分。長さは section_length = 0x1D = 29バイトから
# 固定部分 5 バイトと CRC_32 の 4 バイトを引いた20バイト
# 1つ目のループ。 program_number が 0 なので、 network_id = 0x010
00 00 E0 10
# 2つ目のループ。 program_number = 0x5C38, program_map_PID = 0x101
5C 38 E1 01
# 3つ目のループ。 program_number = 0x5C39, program_map_PID = 0x102
5C 39 E1 02
# 4つ目のループ。 program_number = 0x5DB8, program_map_PID = 0x1FC8
5D B8 FF C8
# 5つ目のループ。 program_number = 0x5D89, program_map_PID = 0x1Fc9
5D B9 FF C9
# 20バイトパースしたのでここで可変長部分終了
# CRC_32
90 3F 0A 85
# 余った部分は 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
}
意味合いごとに改行と記号を加えると以下のようになります。
# ヘッダ4バイト
47 61 01 1E
# pointer_field
00
# 固定長部分 (table_id〜program_info_length)
02 B0 C7 5C 38 D1 00 00 E1 00 F0 09
# program_info_length が 0x09 なので、後続 9 バイトは descriptor()
# descriptor_tag = 0x09, descriptor_length = 0x04 と
# descriptor_tag = 0xC1, descriptor_legnth = 0x01 の2つの descriptor がある
[09 04 00 05 E0 31, C1 01 84]
# 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 を参照
# 1つ目のループ。 stream_type = 0x02(動画), elementary_PID = 0x121, 記述子長 6 バイト
02 E1 21 F0 06 [52 01 00, C8 01 53]
# 2つ目のループ。 stream_type = 0x0F(音声), elementary_PID = 0x112, 記述子長 3 バイト
0F E1 12 F0 03 [52 01 10]
# ここから下は 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つ目のパケットは終了
# 2つ目のパケットのヘッダ
47 21 01 1F
01 6E, 09 04 00 05 FF FF, FD 05 00 0C 1F FF BF]
# 177バイトパースしたのでここで可変長部分終了
# CRC_32
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
ということで、動画の 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 を見れば番組内の音声がステレオかモノラルかは分かるので、番組切り替えのタイミングをうまく掴んで切り離すとか、音声バイナリじたいを調べてモノラルからステレオに変わったところで切り離すとかいった手があります)