seek(10) で意図する動作が何かによって変わってきますが、
スレッドの扱いと、動画フォーマット特有のseek、辺りがキーポイントでしょうか。
- 別スレッドからのseek は、スレッドセーフな操作ではない。
- seek の引数(単位)の確認。オフセットは、timestamp です。
→ bytes の場合は意味が変わってきます。フレームを正しく読み出せない位置へのseek
→ frame 単位のseekは出来ません。フレーム間圧縮等があると、デコードが出来ない。
正しく読み出すには、必ず近くの keyframe から読み出しを開始する必要があります。
任意のフレームに移動したい場合は seek 後に自分で読み進める。(PyAV側では対応しない)
time以外を指定する whence オプションは廃止になってます。
訂正: オプションの仕様が変更され any_frame オプションが有りました。
- seek 後に demux/decode を呼び出す。(途中でseekは出来ない、理由は上述)
- f.index は読み出し時の通し番号なので、seek 後の確認としては使えません。→ f.pts
1) Thread-safe
seek は(排他制御をしない場合) 同一スレッドから呼び出す必要があるので、
20秒迄のフレームを読み出した後に、seek → 改めて demux/decode の繰り返し。
video の後に audio もある点には注意。
同一スレッド内で呼び出す方法は幾つか有ります。
- 別スレッドからthreading.Eventでメインスレッドへ通知。
- 毎ループ時間を確認する。
if pts * time_base > 20.0: seek ...
→ 事前にパケット単位で確認する等で、効率化は出来そう。
2) Seek
- seek 位置は 00:10 秒付近なら
seek(10 * av.time_base)
seek(-1)
で開始位置。
time_base は、seek 対象が、Container, CodecContext, Stream で変わります。
PyAV - time
検証用コード追記
python
1
2def read_frame_until(timestamp):
3 for p in a.demux(video=0, audio=0):
4 if p.pts * p.time_base >= timestamp:
5 # XXX: packet の区切り位置の確認
6 # この条件で packet のループを抜けていいかどうかは未検証。
7 break
8 for f in p.decode():
9 if f.pts * p.time_base >= timestamp:
10 break
11 yield p, f
12
13for p, f in read_frame_until(20):
14 print(f, f.pts * float(p.time_base)) # frame, 秒数を表示
15
16# 00:00:10 直近の keyframe 迄 seek
17print("SEEK 10")
18a.seek(10 * av.time_base)
19
20# keyframe 次第では、9秒付近から再開されることもある。
21for p, f in read_frame_until(13):
22 print(f, f.pts * float(p.time_base)) # frame, 秒数を表示
→ フラグにより内部の操作は分岐。別のフラグを指定したい場合は、この辺りのコードを読み解く。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/07/11 06:29
2021/07/11 06:33
2021/07/11 09:43 編集