OpenCV:VideoCaptureクラスでフレームを待機するタイムアウトを設定する

すべての人に良い一日を! どういうわけか、タスクが登場しました。カメラからRTSPビデオストリームを再現することです。 なぜなら 私はOpenCV APIに精通しており、それを使用することに決めました。 OpenCVはVideoCaptureクラスを使用してビデオストリームをキャプチャします。 残念ながら、ネットワークはしばしば途切れ、この問題は私のレベルでは解決されないため、ビデオストリームのドロップへの迅速な応答が快適な作業の前提条件になりました-標準の接続タイムアウトと次のフレームの待機は30秒で、VideoCapture内では呼び出しが開いています( )とread()はブロックしているため、別のスレッドで呼び出して非同期モードで結果が受信されるのを待つなど、本当にシンプルなコードの周りにさまざまなラッパーを書く必要があります。 当然、私はこれについて喜びを感じませんでした-これらはすべて、コードの複雑さは言うまでもなく、プログラムが他の目的のために行ったはずのリソースです。 決定されたのは、標準タイムアウトを変更するか、外部にインストールする機能を追加することです。 それはかなり汚いハックであることが判明しましたが、それは誰かに役立つ可能性があります。 おそらくもっと良い方法があれば-もしあれば-私は本当にそれを知りたいので、コメントをお願いします。 理想的には、Habrの読者の中には、この問題に注意を払うOpenCV開発者がいるかもしれません。 目標は、コードを「Windows x64で正常に機能するようにする」ことでした。



誰が気にする-私は猫をお願いします。



リサーチ



Googleの助けを借りて、この問題はffmpegにかかっていることがわかりました。切断に関する情報を提供するコールバックがあります。 30秒のタイムアウトがプル要求#6053で設定されました。 この問題は次のように追加されました。現時点では、cmake-collectorはopencv_ffmpeg.dllファイルを所定の場所にアセンブルするのではなくダウンロードし、ffmpegを使用したアセンブリ手順は消えています。 タイムアウト定数を使用したコード(少なくともWindowsではコンパイルできません)は、modules / videoio / src / cap_ffmpeg_impl.hppファイルにあります。



#define LIBAVFORMAT_INTERRUPT_OPEN_TIMEOUT_MS 30000 #define LIBAVFORMAT_INTERRUPT_READ_TIMEOUT_MS 30000
      
      





したがって、タスクは次のように形成されました。



  1. このファイルをWindowsでビルドします。
  2. その中の定数を変更します。
  3. 職場で問題がないことを確認してください。


考え直さずに、それがどのように解決されたかについてお話します。 最初に、ヘッダーファイル、dll、およびlibを含むffmpegの最新の開発バージョンをダウンロードし、ソース/サードパーティの適切な場所にすべて配置する必要があります。便宜上、実際にはどこにでも配置できます。 次に、OpenCVソースファイルに次の変更を加える必要があります。



モジュール/ videoio / src / cap_ffmpeg.cpp:



cap_ffmpeg_api.hppの潜在的なインクルードに関する行をインクルードでコメントアウトします



 //#if defined HAVE_FFMPEG && !defined WIN32 #include "cap_ffmpeg_impl.hpp" //#else //#include "cap_ffmpeg_api.hpp" //#endif
      
      





icvInitFFMPEG()関数で



 ... #ifdef WIN32... // ,    #elif defined HAVE_FFMPEG //  ,    #endif ...
      
      





ここで何が起こったのか-LoadLibraryを使用してopencv_ffmpeg.dllからロードするffmpeg機能の使用を放棄し、cap_ffmpeg_impl.hppファイル内の内部実装に切り替えました。



モジュール/ videoio / src / cap_ffmpeg_impl.hpp:



上記の定数を見つけて、目的の定数に変更します。 私の場合-



 #define LIBAVFORMAT_INTERRUPT_OPEN_TIMEOUT_MS 2000 #define LIBAVFORMAT_INTERRUPT_READ_TIMEOUT_MS 1000
      
      





一般に、このファイルはWindowsでのアセンブリ用に作成されたものではないため、コンパイルに必要な機能の一部が欠落しています。snprintfについて説明しています。 コードはStackOverflowでどこかから引き出されました。 便利な場所に貼り付けます。



 #if defined(_MSC_VER) && _MSC_VER < 1900 #define snprintf c99_snprintf #define vsnprintf c99_vsnprintf __inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) { int count = -1; if (size != 0) count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); if (count == -1) count = _vscprintf(format, ap); return count; } __inline int c99_snprintf(char *outBuf, size_t size, const char *format, ...) { int count; va_list ap; va_start(ap, format); count = c99_vsnprintf(outBuf, size, format, ap); va_end(ap); return count; } #endif
      
      





変更を加えた後、これらすべてを強制的にコンパイルする必要があります。 開始するには、CMakeを使用してOpenCVの* .slnを生成する必要があります。 現時点でopencv_videoioをビルドしようとしてもうまくいきません。 修正可能。 videoioモジュールのVisual Studioプロジェクトを開きます:build / modules / videoio / opencv_videoio.sln。 ffmpegヘッダーファイルを追加してディレクトリ含め 、リンクに* .libを追加します。



opencv_videoioがビルドされるとすぐに、このdllを「標準」dllの代わりに簡単に配置できます。 ffmpeg配信からのすべてのdllも動作するために必要になることに注意してください-アプリケーションはそれらなしでは動作しません。



重要なポイント:これが機能するには、cv :: CAP_FFMPEGをVideoCaptureバックエンドとして指定する必要があります。



現時点での結果は、飛行が正常であり、その月のバグに気づかなかった。 ただし、上記のすべてを考慮すると、これらは可能な範囲を超えているため、ご自身の責任とリスクでのみ使用してください。 希望を達成する他の方法があれば、私が言ったように、私は聞いてとてもうれしいです。



ご清聴ありがとうございました。



All Articles