2015/11/09(月)IPカメラ(Webcam)リレープログラム / ip-camera-relay.pl

IP Cameraの映像を中継するプログラムです。以下の機能があります。

  • IPカメラへの自動再接続。
  • 複数のIPカメラを登録し、接続できたカメラの映像を中継する。接続が切れたら、次に接続できたカメラに接続する。
  • IPカメラに接続できていない時は、指定した画像(ブランクイメージ)を代わりに送信する。

VLCなどのIPCamストリーム受信ソフトでは、カメラとの接続が切れた場合に自動再接続を行ってくれないものがあります。またカメラと接続が切れたときにブランク画像を挿入したいこともあり、それを実現するためのソフトになります。

プログラム

Gistに置いたので、適当に拾ってください。

  • ip-camera-relay.pl Perl用プログラム
  • ライセンス : 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の青い画像)を送信します。

その他オプションはヘルプを参照してください。

*1 : ブラウザから接続したりカメラの仕様書を見れば、書かれているかと思います。

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)で処理していました。

スレッドを使用することで接続待ちや受信待ち発生時にも別スレッドで確実に処理が行えるのですが、書き方が悪いのか、はたまたPerlのithreadとsocketの相性が悪いのか、それなりの接続数を処理するとよく落ちていました

それを踏まえて今回はポーリングで処理しています。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);
}

※GistにWindows対応版を置いてあります。

こうすることで、接続待ちを避けつつシングルスレッド処理を実現しています。

なるべくわかりやすく書きましたし、VAPIXのWebCam MJPEGプロトコルは単純ですので、ソースを読めば他の流れはほとんどつかめるかと思います。

*2 : 当時は画期的でそれなりに使われましたが、ニコ生、ツイキャス、Ustreamなどが一般化して使われなくなりました。

注意

httpヘッダ解析部などでかなり手抜きをしています(笑)

ついでにその部分のブロッキング回避も手抜きをしてたのですが、一応1秒のalarm処理を仕込みました。1秒以内にヘッダを処理しないクライアントやサーバは容赦なく切断します(苦笑)

とはいえDoSするつもりなら可能な実装ですので、何か本格的に使いたいなら修正するか自作するか、もしくはご相談ください。

OK キャンセル 確認 その他