2015/11/09(月)IPカメラ(Webcam)リレープログラム / ip-camera-relay.pl
IP Cameraの映像を中継するプログラムです。以下の機能があります。
- IPカメラへの自動再接続。
- 複数のIPカメラを登録し、接続できたカメラの映像を中継する。接続が切れたら、次に接続できたカメラに接続する。
- IPカメラに接続できていない時は、指定した画像(ブランクイメージ)を代わりに送信する。
VLCなどのIPCamストリーム受信ソフトでは、カメラとの接続が切れた場合に自動再接続を行ってくれないものがあります。またカメラと接続が切れたときにブランク画像を挿入したいこともあり、それを実現するためのソフトになります。
プログラム
Gistに置いたので、適当に拾ってください。
- ip-camera-relay.pl Perl用プログラム
- Windows用EXEファイル x64用
- ライセンス : GPLv2 or later
- http転送。パスワードなし。Motion JPEG(以下MJPEG)のみ対応。
使い方
$ ip-camera-relay.pl -p 8888 http://(camera-ip):port/stream-path
クライアント接続用に8888番ポートを開き、指定したカメラに接続します。カメラURLはブラウザから接続するURLではなく、ビデオストリームのURLを指定してください*1。
複数のカメラを指定すると、それぞれを順番に接続します。最初に接続できたストリームをクライアントに配信し、カメラとの接続が切れたら次に接続できたカメラの映像を送ります。
どのカメラとも接続できない時は、ブランク画像(標準では640x480の青い画像)を送信します。
その他オプションはヘルプを参照してください。
WebCamのプロトコル
ほとんどのIPカメラはVAPIXというプロトコルを用いてデータをリアルタイム転送しています。検索すればすぐに仕様書が手に入りますが、一番単純な仕様は「http/MJPEG転送」です。
IPカメラのストリームURLに対して、httpリクエストを投げます。すると普通に応答が返り、httpヘッダに続いてMJPEGデータが送られて来ます。
HTTP/1.1 200 OK Connection: close Server: IP Camera name... Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0 Pragma: no-cache Expires: -1 Access-Control-Allow-Origin: * Content-Type: multipart/x-mixed-replace;boundary=--Boundary1234567
httpのマルチパートフォームの処理を書いたことのある人ならお馴染みのデータが後に続きます。
--Boundary1234567 Content-Type: image/jpeg Content-Length: 23411 (生のJPEGデータ) --Boundary1234567 Content-Type: image/jpeg Content-Length: 23411 (生のJPEGデータ) --Boundary1234567 (以下繰り返し)
JPEGファイルが永遠と送りつけられるだけで、送られてくるタイミングも特に決まっていません。
例によって例のごとく、改行コードは「CR/LF」であることだけ注意してください。
プログラム解説
以前Windows Media Streamをリレーするソフトを作ったことがありますが*2、その時はマルチスレッド(ithread)で処理していました。
スレッドを使用することで接続待ちや受信待ち発生時にも別スレッドで確実に処理が行えるのですが、書き方が悪いのか、はたまたithreadとsocketの相性が悪いのか、それなりの接続数を処理するとよく落ちていました。*3
今回はポーリングで処理しています。IP Cameraに接続に行く段階で接続待ちが発生してしまうので、connect時のみノンブロッキングIOを使用しています。
sub set_block { my $sock = shift; return &set_nonblock($sock, 1); } sub set_nonblock { my $sock = shift; my $block = shift; my $flags = fcntl($sock, F_GETFL, 0); if ($block) { $flags &= ~O_NONBLOCK; } else { $flags |= O_NONBLOCK; } fcntl($sock, F_SETFL, $flags); }
こうすることで、接続待ちを避けつつシングルスレッド処理を実現しています。
なるべくわかりやすく書きましたし、VAPIXのWebCam MJPEGプロトコルは単純ですので、ソースを読めば他の流れはほとんどつかめるかと思います。
注意
httpヘッダ解析部などでかなり手抜きをしています(笑)
ついでにその部分のブロッキング回避も手抜きをしてたのですが、一応1秒のalarm処理を仕込みました。1秒以内にヘッダを処理しないクライアントやサーバは容赦なく切断します(苦笑)
とはいえDoSするつもりなら可能な実装ですので、何か本格的に使いたいなら修正するか自作するか、もしくはご相談ください。