よく分からない挙動
OpenCVで作ったソフトで社内のネットワークカメラに繋いでみると、ちょっと理解しにくい挙動がみられた。一部のネットワークカメラが有線LANのときだけ受信できない。カメラの設定か、ネットワークの設定か、ファイアウォールの設定か、怪しい所を調べたが変な所はない。
試してみると、ffplayやffmpegでも同じ挙動がみられる。どうもOpenCVそのものが臭い。
デバッガで挙動を見る
デバッグバージョンのOpenCVのバイナリがあったので、簡単なコードを書いてOpenCVのそのものの動作を見てみることにした。
#include <iostream>
#include <opencv2\opencv.hpp>
int main()
{
int ret;
cv::Mat frame;
cv::VideoCapture capture;
capture.open("http://hogehoge/cgi-bin/mjpeg");
while (true) {
capture >> frame;
if (frame.empty()) {
break;
}
cv::imshow("Capture", frame);
ret = cv::waitKey(1);
if (ret == 'q') {
break;
}
}
}
↑これを、capture.open()のところから、ひたすらステップインで実行していく。capture.open()がコケている所を見つけようと思ったのだが、コンストラクタとオーバーロード関数とテンプレートの嵐で、何がどうなっているのかさっぱり分からない。が、何度も動作させているうちに、capture.open()の動作が上手くいく時と行かない時で動作が切り変わる少し手前で気になる所を一つ見つけた。
拡張子の無いvectorというファイルの中の↓このコード。
_Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal()));
PROXY? ビルドやPythonでpipするときにProxyの設定が必要なのは知っているけど、OpenCV自体がProxyの設定を見に行くというのは意外だった。でいろいろ調べると、OpenCVの公式でProxyに言及しているところは見つからなかったが、stackoverflow↓に環境変数のHTTP_PROXYの値を見に行くというコメントが有った。
https://stackoverflow.com/questions/56325448/how-to-capture-a-video-behind-proxy-with-opencv
確かにこの環境変数にProxyのurlを入れてある。半信半疑でHTTP_PROXYの設定を消してみる。(_を付けて無効にする)
映像が受信できた!!
HTTP_PROXYはffmpegやPythonのツールが使うようだが、OpenCVそのものも参照するらしい。とっかかりが見つかったので、いろいろ試すと次の法則があるらしい。
OpenCVのcapture.open()は、指定アドレスが「グローバルIPアドレスぽい」だと環境変数HTTP_PROXYに指定してあるProxyサーバーを見に行く。
「グローバルIPアドレスぽい」というのは、ローカルでないIPアドレスのことだ。10...とか192.168..*のようなアドレスはローカルIPである。そうではないアドレスはグローバルIPであると推測できるのでProxyを見に行くということだ。ウチの会社は、32bitのネットマスクを割り当てられていて、社内のかなりのセグメントがグローバルIPになっている(なんと贅沢な)。なのでグローバルIPを割り当てられたネットワークカメラは、Proxyの外側と判定され、接続できなかったということらしい。
じゃあなんでWIFIで繋いだ時は問題なかったのか、という謎は残るのだが、これは社内のProxyの自動設定に問題があるらしい。こちらはまだ謎のまま。
まとめ
- OpenCVのcapture.open()は、指定アドレスが「グローバルIPアドレスぽい」だと環境変数HTTP_PROXYに指定してあるProxyサーバーを見に行く。
- 挙動が怪しいときはプロキシの設定と、環境変数HTTP_PROXYをチェックする。