TSファイルから番組内容を取得する

epgrec が内部で使っているepgdumpというプログラムが、なんか引数にontv codeというのをとるので、これはontv japanをスクレイピングしているのかと思っていたが、その割にはtsファイルのパスも必要だしどうなってるんだと思ってよくよくソースを読んでみたら、tsファイルを解析していることがわかった。日本の地上デジタル放送はMPEG-2 TSという形式が採られていて、動画データや音声データのほかに時刻情報やら番組情報やら、いろんなメタデータが付加されている。そこらへんの仕様がデジタル放送に使用する番組配列情報(ARIB STD-B10)というARIBが公開している資料に書かれているということなので、読んでみることにした。

MPEG-2 TSファイルの内容はNHKの資料朋栄IBEの資料に詳しい。すなわち、TSファイルは188バイトのパケットを1単位として構成されていて、パケットの先頭4バイトがヘッダ、残り184バイトがアダプテーションフィールド(ヘッダを拡張したもの)とペイロード(情報本体)のどちらか、または両方となっている。パケットの先頭バイトは必ず0x47であり、ペイロードの中身が何であるかはPIDと呼ばれる13ビットのフィールドで分かる。一連の情報が複数のパケットに分割されることもあり、その場合の先頭パケットはPayload-unit start indicatorフラグが1かどうかで判断できる。パケットが欠落することもあるが、Countinuity indexという4バイトのフィールドがあり、もし欠落していたらこの値が飛ぶので判断できる(16パケット一度に飛んだ場合は判断できない)。

番組情報はEIT(Event Information Table)というテーブルに記述され、PIDは0x12, 0x26, 0x27である。EITテーブルには1つ以上のイベント(=テレビ番組ひとつひとつ)が含まれ、そのイベントにも1つ以上の記述子が含まれている。EITテーブルの定義はARIB STD-B10 第2部の表5-7にあるとおり、以下のようになっている。

EITテーブルの定義
フィールド名ビット数
table_id8
section_syntax_indicator1
reserved_future_use1
reserved2
section_length12
service_id16
reserved2
version_number5
current_next_indicator1
section_number8
last_section_number8
transport_stream_id16
original_network_id16
segment_last_section_number8
last_table_id8
(以下繰り返し)
event_id16
start_time40
duration24
running_status3
free_CA_mode1
descriotors_loop_length12
<以下繰り返し>
descriotor(2バイト目の数値+16)
<以上繰り返し>
(以上繰り返し)
CRC32

イベントと記述子が複数あるため、一連のパケットのうちどこまでがひとかたまりなのかはsection_lengthの値を見ないと分からない。section_lengthの値は、section_lengthフィールドの後に何バイト続くかであることに注意する必要もある。各記述子は、共通して先頭1バイトが記述子タグ、2バイトが記述子長であるので、先頭2バイトを見ればどの種類の記述子が何バイト続くかが分かる。記述子長も、やはり記述子長フィールドの後に何バイト続くかの数値なので、記述子全体の長さはそれに+2したものになる。それぞれが何回繰り返されるかは、section_length、descriptors_loop_length、descriptor_lengthの値から求められる。パケットの途中でテーブルの記述が終わった場合は、残りが0xFFで埋められる。

どの記述子がどのテーブルにひもづいて登場しうるかはARIB STD-B10 第2部の表6-1に定義されており、EITでは短形式イベント記述子が必須、ほか数項目が任意となっている。短形式イベント記述子は番組名と番組内容を定義するもの(ARIB STD-B10 第1部 図6-32)である。epgdumpなどの既存のtsファイルパーサもこの記述子の内容を見て、どういう番組が何時から何時まで放送されるのか表示している。地デジテレビの番組表表示機能もおそらくこのフィールドを見ているのだろう。

ただ、このフィールドのデータがいまいち使えない。フォーマットが番組によってバラバラなのである。たとえば以下のような感じ (ARIB外字は既存のepgdumpの実装通りに展開):

短形式イベント記述子の記述例
番組名フィールドの値番組記述フィールドの値
逆境無頼カイジ -破戒録篇- 「熱風の到来」▽3ヶ月間、耐えに耐え、正規の給料を手にするカイジたち45組!再び地下チンチロにて、にっくき大槻と掛け金上限無しの青天井勝負!!必勝を期して挑むカイジだが…
DOG DAYS#8「開戦の日」
プリティーリズム・オーロラドリーム「プリズムの輝きはいつもここに」【字】母の日の企画のツアーに参加することとなったあいらとりずむ。そこで母親が遅れて寂しいのに虚勢を張る少女なつきと出会う。りずむは彼女に自分の姿を重ねるのだが…。
【字】アニメ もしドラ(10)「みなみは高校野球に感動した」この決勝戦に勝てば甲子園出場が決まる。みなみが駆けつけ声援を飛ばす中、一点リードされた九回裏ツーアウト、打席に立ったのは祐之助だった。程高野球部の運命は?
【字】アニメ GIANT KILLING「#06」【原作】ツジトモ,【脚本】川瀬敏文【監督】紅優【声】関智一,置鮎龍太郎,小野大輔,川島得愛,浅野真澄
Aチャンネル #07【字】「夏祭り」女子高生4人のキュートでユルユルなハイスクールライフ!
スイートプリキュア♪【デ】【字】第13回「ムムム〜ン! セイレーンとハミィの秘密ニャ♪」ハミィが『幸福のメロディ』の歌い手になれたのはセイレーンのおかげ!?セイレーンの過去が明らかに!!

ざっとみて以下のようなパターンがあるようである:

録画したアニメのファイルを管理するのに、タイトルと話数とサブタイトルはもっとも重要な要素だといえる。その重要な要素が、短形式イベント記述子からは体系だてて取得しにくい。しょぼいカレンダーなどの人力ソリューションは、おそらくそこら辺の問題からの需要も強いのだろう。ただし、しょぼいカレンダーのヘルプに「編集者が「視聴または録画するデータしか登録してはいけない」というルールがある」というとおり、必ずしもすべてのアニメ情報が配信されるとは限らない。最近だと、黄金の法とかはれときどきぶたの再放送などが配信されなかった。

なんかいい方法はないかとARIBのドキュメントを引き続き読み進めて行ったら、「シリーズ記述子」という便利そうなものが定義されていることが分かった。

この記述子は、シリーズ化された複数のイベントを識別するために用いる。個々のシリーズはシリーズ識別により識別される。受信機では、シリーズ化されたイベント群に対して一括の操作(予約等)を行う際に用いることができる。

シリーズ記述子は記述子タグ0xD5で定義され、以下のフィールドを持つ:

こんな便利な情報が定義されているなら話は早い。再放送ラベルを見れば、「もしドラ」や「バクマン」の同じ話がなんども録画されるのを避けられるし、シリーズ名や話数が取れるので録画したファイルのネーミングに使える。(サブタイトルを定義するフィールドがないことに気づいたが、短形式イベント記述子から正規表現とか使って持ってこれないこともないだろう)

ということで、シリーズ記述子を捕まえるべく、TSファイルから記述子を取得するコードを書いてみた。

実行結果は以下のとおりである:

40秒録画したtsファイルのEIT(PID=0x12)テーブル数
テーブル識別NHKETVNTVTBSCXEXTXMXBSNTV中身
0x004E7811879793911911823478EIT(自ストリームの現在と次の番組)
0x004F304EIT(他ストリームの現在と次の番組)
0x00506211313856103155162192188EIT(自ストリーム、スケジュール)
0x005112172410527443714EIT(自ストリーム、スケジュール)
0x005892730733418174319EIT(自ストリーム、スケジュール)
0x005981913111335393420EIT(自ストリーム、スケジュール)
0x00601029EIT(他ストリーム、スケジュール)
0x0061254EIT(他ストリーム、スケジュール)
40秒録画したtsファイルのEIT(PID=0x12)テーブルに含まれる記述子の数
記述子タグNHKETVNTVTBSCXEXTXMXBSNTV中身
0x004D3528595623025564169210504855短形式イベント記述子
0x004E221736810177242359780948774拡張形式イベント記述子
0x00503528595623025564169210504823コンポーネント記述子
0x00543528525583025463769210424698コンテント記述子
0x00C13528595623025564169210504811デジタルコピー制御記述子
0x00C43639105723025566070810504945音声コンポーネント記述子
0x00C714424092246148106194810501393データコンテンツ記述子
0x00CB56CA契約情報記述子
0x00D6511131483439249693912028365083イベントグループ記述子

なんということだ、シリーズ記述子がEITパケットに含まれていない!

ていうか、そういう記述子が含まれているなら既存の類似実装が見逃すはずがないのであった。たとえば、Rec10epgdump/eit.cは、実際に配信されているコンテント記述子・コンポーネント記述子・音声コンポーネント記述子は値を取得している(738行目付近以降)が、シリーズ記述子はパースしたあとデータを取り込んでいない。

ということで、EPGデータは全放送データが含まれていて量はよいが内容が適切に表現されていないところがあって質が良くない。しょぼいカレンダーはフォーマットがしっかりしていて内容を再利用しやすいが配信されない番組がある。両者をおりまぜていい感じに使うのがよい。