Python で TS ファイルをパースするにあたって、まず Python でバイナリファイルを扱う方法についておさらいをしたいと思います。 open 関数の第2引数に 'rb' を与えればバイナリモードでファイルを開けます。開いたファイルオブジェクトを for ループで回すと、先読みバッファを使って効率的にファイルを読んでくれます。
import sys
with open(sys.argv[1], 'rb') as ts:
for packet in ts:
pass
手元の2世代前の MacBook Air 梅モデル (CPU 1.4 GHz Core 2 Duo, メモリ 2 GB 1067 MHz DDR3) で、30分の TS ファイルを読ませた結果、37秒ほどかかりました。
real 0m37.927s
user 0m22.847s
sys 0m8.328s
このアプローチには問題があります。TS ファイルは188バイト単位のパケットから成り立っているのに、変数 packet のサイズがまちまちなのです。
with open(sys.argv[1], 'rb') as ts:
for packet in ts:
print(len(packet))
388
103
92
77
428
114
423
157
154
66
362
21
32
21
41
2
^C6
iter 関数を使って、必ず 188 バイト返すようにしてみます。
with open(sys.argv[1], 'rb') as ts:
packets = iter(lambda: ts.read(188), b'')
for packet in packets:
pass
real 0m48.537s
user 0m31.532s
sys 0m8.579s
10秒ほど遅くなってしまいました。
これは正直に 188 バイトずつファイルシークしているからで、一度にまとまった量をシークして 188 バイトずつ返すようにすれば解決しそうです。
def packets(ts):
packet_size = 188
chunk_size = 10000
buffer_size = packet_size * chunk_size
packets = iter(lambda: ts.read(buffer_size), b'')
for packet in packets:
for start, stop in zip(range(0, buffer_size - packet_size, packet_size),
range(packet_size, buffer_size, packet_size)):
yield packet[start:stop]
with open(sys.argv[1], 'rb') as ts:
for packet in packets(ts):
pass
とりあえず 1880000 バイトを読んで、そこから188バイトずつ返すようにしてみました。
real 0m20.419s
user 0m0.226s
sys 0m5.098s
なかなかいいかんじです。
10000倍にしたのは適当だったのですが、1000倍でも100000倍でもこれより悪い結果になったので、おそらくこのあたりが最適なのでしょう。