Transport Stream の各パケットの内容は PES (Packetized Elementary Stream) と PSI (Program Specific Information) の2種類に分別できます。 PES には動画や音声などのデータ、 PSI には様々なメタデータが入っています。中身がどちらにせよパケットレイヤのデータ構造は同じで、 ISO-13818-1 の2.4.3.2節表2-2にある通りです。
transport_packet(){
sync_byte 8 bslbf
transport_error_indicator 1 bslbf
payload_unit_start_indicator 1 bslbf
transport_priority 1 bslbf
PID 13 uimsbf
transport_scrambling_control 2 bslbf
adaptation_field_control 2 bslbf
continuity_counter 4 uimsbf
if(adaptation_field_control = = '10' || adaptation_field_control = = '11'){
adaptation_field()
}
if(adaptation_field_control = = '01' || adaptation_field_control = = '11') {
for (i = 0; i < N; i++){
data_byte 8 bslbf
}
}
}
この表の言わんとしていることは以下のとおりです
sync_byte
は先頭 0 ビット目から 8 ビットをbslbf
(Bit string, left bit first, 左ビットが先頭のビット列) として解釈した値であるPID
は先頭 11 ビット目から 13 ビットをuimsbf
(Unsigned integer, most significant bit first, 符号無し整数、最上位ビットが先頭) として解釈した値であるadaptation_field
はadaptation_field_control
が 0b10 か 0b11 のときに 32 ビット目から続くフィールドであるdata_byte
はadaptation_field_control
が 0b01 か 0b11 のときに 32+n ビット目から続くフィールドである
PES や PSI の実際のデータは data_byte
の中です。複数のパケットに別れて送出されることがあり、先頭のパケットかどうかは payload_unit_start_indicator
の値で判断します。 1 なら先頭パケット、 0 なら後続パケットです。先頭パケットの data_byte
の先頭3バイト (packet_start_code_prefix
) が 0x000001 なら PES、そうでないなら PSI です。より詳しく書くと、 PSI の先頭パケットの data_byte
は pointer_field
から始まります。この pointer_field
の値は、一つ前の PSI パケットで伝送しきれなかった残りのバイナリがこの後何バイトあるかを表しています。pointer_field
の後はデータ本体 (payload
) で、 table_id
が8ビット、そのあと必ず上4ビットが 0b1011 であるバイトから始まるので、pointer_field
と table_id
が 0 でも次の3バイト目は必ず異なることになり PES と PSI の区別が付くようになっています。
PID
は data_byte
がどういうデータかを表すものですが、一律に動画の PID
が何番などとは決まっていません。動画や音声の PES の PID
は PMT (Program Map Table) で指定され、その PMT の PID
も決まっていないので PAT (Program Association Table) をパースして調べる必要があります。 PAT の PID は 0x00 であると ISO13818-1 2.4.3.3節表2-3で決められています。
ということで、TS ストリームの中から望みの PSI を取り出すには、少なくとも以下の操作が必要です
PID
の値が望みの PSI のものかを調べるpayload_unit_start_indicator
を見て先頭パケットを探すadaptation_field_control
とpointer_field
を見てdata_byte
が何バイト目から始まるか調べる- 次に
payload_unit_start_indicator
が 1 になるまでpayload
をバッファしておいて、次の先頭パケットのpointer_field
分を取り込んで返す
PES の場合は pointer_field
がないので 3番目の処理が不要です。
実装例は packet.py の payload 関数や tables 関数あたりを参考にしてみてください。