質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

1929閲覧

PyAVでシークができない...

Marusoftware

総合スコア189

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/07/10 02:46

PyAVでseekができません...

python

1import av, time, threading 2 3def seeker(a): 4 time.sleep(20) 5 a.seek(10) 6a = av.open("[video_file]") 7b = a.demux(audio=0,video=0) 8threading.Thread(target=seeker, args=(a,)).start() 9for p in b: 10 for f in p.decode(): 11 print(f.index) 12 time.sleep(0.1)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

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 ...

 → 事前にパケット単位で確認する等で、効率化は出来そう。

  • sched モジュールをノンブロッキングで使う。

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 02:24

編集2021/07/11 09:24
teamikl

総合スコア8760

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Marusoftware

2021/07/11 06:29

そういうことだったんですね... seekの単位は、秒だったはず...(もっかい読みます...) もう一回demuxしなきゃいけないのか.... 理解しました...ありがとうございます。
Marusoftware

2021/07/11 06:33

(ちなみに、別スレッドから呼び出してるのは、何かUI準備するのがめんどくさかったから...)
teamikl

2021/07/11 09:43 編集

frame 単位や bytes での seek も、引数次第で指定出来そうな雰囲気でした。 訂正: frame単位は誤りでした。keyframe以外のframeへのseekは可。 一点訂正、keyframe 以外へのseek は、 any_frame オプション> Seek to any frame, not just a keyframe. --- スレッドを使った簡易的な実装なら、 from threading import Event, Timer event = Event() Timer(20, event.set).start() # 20秒後にフラグをセット event.clear() for f in a.decode(video=0, audio=0):  print(f)  # time.sleepの代わり。※ time.sleepでは待機時間を大きくしたときにラグが発生します。  if event.wait(0.01): # event.set が呼ばれると待機状態をキャンセルしTrueを返す   break a.seek(10 * av.time_base) 注意点としては、読み出し速度依存なので、動画内の時間は超過したフレームが含まれる事がある点。 意図する動作が、プログラムの「処理を開始し20秒後」にseek と、 「動画の時間が 20秒に達した時」にseek で若干異なります。 (sleep速度で読み出せるフレーム数が変わります)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問