2013年4月10日水曜日
Webカメラ画像のリアルタイム表示
Webカメラの映像をAndroidスマホで表示するデモを何度か受けたことがある。
その際いつも指摘されることが、スマホ側での画像の遅延。
動作環境は以下のようなイメージ。
USBカメラの画像は数秒遅れでAndroid端末に表示される。
ストリーミングソフト=WebCamXPでは3~5秒、YawCamで2秒程度。
USBカメラとAndroid端末が離れている場合は問題ないんですが、デモなどでは絶対指摘されてしまいます。
「この遅れはなんとかならないのか ・・ 」と。
確かにそうなんですよね。
車のバックモニタなどを想定した場合、2~3秒遅延があれば「ゴツン!」間違いなしです。
ストリーミングソフトの自作を想定して、どこまでリアルタイムに近づけるか調べてみた。
USBカメラをDirectShowなどで取り込む場合、取り込みに必要な時間は33mS。
1/30秒での画像取り込みですね。
取り込んだ画像サイズは9216154バイト(640*480*3+α:ヘッダ分)でした。
この画像(Bitmap)をメモリ上でjpegに変換した場合(品質40%)
処理時間に約31mS
出力画像サイズは25871バイト(原本の約1/35)でした。
※ ファイル出力していません。あくまでもメモリ上だけの展開です。
あとはWifiでの転送速度とAndroidの受信~表示の遅れ。
これはJNI(C++)などを使ってがんばるしかないです。
実際には1秒に10枚程度の画像でパラパラ漫画的に、遅延は100mS程度って感じでしょうか。
(これでも2メガBPSの通信速度が必要で、3G回線だととっても無理)。
これで実用レベルに達するなら、DirectShowでの画像取り込み~Jpeg変換~Androidへの転送ってのが実現できそうです。
MotionJPEGでのパラパラ漫画ストリーミングです。
最近はやりのストリーミングもリアルタイムには厳しいので ・・
2013年4月8日月曜日
Webカメラの画像表示(Panasonicカメラ)
1年ほど前に調査していたネタ。
PanasonicのWebカメラをAndroidで表示したい。
たとえば ・・
株式会社リネット様
http://180.131.124.38:8101/CgiStart?page=Single&Language=1
株式会社SimPro様
http://simpro.mydns.jp:84/CgiStart?page=Single&Resolution=640x480&Quality=Standard&Mode=JPEG&RPeriod=65535&Size=STD&PresetOperation=Move&SendMethod=1&Language=1
PCのブラウザでは動画を表示できますが、Androidのブラウザでは表示できません。
そしてまたAndroid版FireFoxだけは表示できるのも謎です。
ではこれらの映像をAndroidのアプリで表示するにはどうしたらいいでしょうか?
※ HTMLコードばかりで長くなってしまいますが、ご容赦ください。
追いかけていく ポイントはこれらの中のほんの少しだけなので。
■ 上記ページのHTMLコードを見てみよう。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN"> <HTML> <HEAD> <META HTTP-EQUIV="expires" CONTENT="0"> <META HTTP-EQUIV="Pragma" CONTENT="no-cache"> <META HTTP-EQUIV="Cache-Control" CONTENT="no-cache"> <META NAME="ROBOTS" CONTENT="NONE"> <META NAME="ROBOTS" CONTENT="NOINDEX,NOFOLLOW"> <META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=Shift_JIS"> <TITLE>Network Camera</TITLE> </HEAD> <FRAMESET border=0 frameSpacing=0 rows=30,8,* frameBorder=0> <FRAME name=bar src="CgiTagMenu?page=Single&Language=1" scrolling=no NORESIZE> <FRAME name=hrbar src="BarFoot.html" scrolling=no NORESIZE> <FRAME name=body src="ViewerFrame?page=Single&Language=1"> </FRAMESET> </HTML>
これだけ? トップページには確かにこれだけ ・・ 3つのフレームがあるだけです。
■ それではこの内部を追いかけてみる。
映像が含まれていると思われる場所のソース
ViewerFrame?page=Single&Language=1をクリックして出てきたソースを見てみる。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN"> <HTML> <HEAD> <META HTTP-EQUIV="expires" CONTENT="0"> <META HTTP-EQUIV="Pragma" CONTENT="no-cache"> <META HTTP-EQUIV="Cache-Control" CONTENT="no-cache"> <META NAME="ROBOTS" CONTENT="NONE"> <META NAME="ROBOTS" CONTENT="NOINDEX,NOFOLLOW"> <META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=Shift_JIS"> <TITLE>ViewerFrame</TITLE> <SCRIPT Language="JavaScript"> <!-- if(top.location.href.match("/CgiStart") == null) { top.location.href = "CgiStart?page=Single&page=Single&Language=1"; } // --> </SCRIPT> </HEAD> <FRAMESET COLS="123,*" FRAMEBORDER=0 BORDER=0 FRAMESPACING=0> <FRAMESET ROWS="26,*"> <FRAME SCROLLING=no SRC="nphControlCamera?Direction=&Resolution=640x480&Quality=Standard&Size=STD&Width=640&Height=480&NewPosition.x=&NewPosition.y=&PresetOperation=Move&Language=1&RPeriod=0" NAME="Message"> <FRAME SRC="nphPanTiltControl?Resolution=640x480&Quality=Standard&Size=STD&PresetOperation=Move&Language=1&RPeriod=0" NAME="Control"> </FRAMESET> <FRAMESET ROWS="24,*"> <FRAME SRC="CgiTitle?Resolution=640x480&Size=STD&Sound=Enable&Language=1" NAME="audio" NORESIZE SCROLLING="NO"> <FRAME SRC="ImageViewer?Resolution=640x480&Quality=Standard&Size=STD&PresetOperation=Move&Data=0&Frame2=PanTilt&Type=&Language=1&RPeriod=0&Sound=Enable" NAME="right"> </FRAMESET> <NOFRAMES> <BODY> </BODY> </NOFRAMES> </FRAMESET> </HTML>
映像データと思われるものはまだない。
でもAndroid版FireFoxでは表示されるので、これらのコードをたどっていくと映像データが存在するはず!
■ もう少し追いかけてみよう。
上記コードの中から、この部分をクリックして出てきたソースを見る。
ImageViewer?Resolution=640x480&Quality=Standard&Size=STD&PresetOperation=Move&Data=0&Frame2=PanTilt&Type=&Language=1&RPeriod=0&Sound=Enable
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN"> <HTML> <HEAD> <META HTTP-EQUIV="expires" CONTENT="0"> <META HTTP-EQUIV="Pragma" CONTENT="no-cache"> <META HTTP-EQUIV="Cache-Control" CONTENT="no-cache"> <META NAME="ROBOTS" CONTENT="NONE"> <META NAME="ROBOTS" CONTENT="NOINDEX,NOFOLLOW"> <META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=Shift_JIS"> <TITLE>ImageViewer</TITLE> <META HTTP-EQUIV="Refresh" CONTENT="1800;URL=ImageViewer?Resolution=640x480&Quality=Standard&RPeriod=3&Size=STD&Sound=Enable&Language=1"> <META http-equiv="Content-Script-Type" content="text/javascript"> <META http-equiv="Content-Style-Type" content="text/css"> </HEAD> <BODY BGCOLOR="#EFEFEF" TEXT="#ffffff" LINK="#ffffff" VLINK="#ffffff" ALINK="#ffffff" TOPMARGIN="0" LEFTMARGIN="0" MARGINHEIGHT="0" MARGINWIDTH="0"> <!-- ** view ** --> <FORM METHOD="POST" ACTION="nphControlCamera" TARGET="Message"> <INPUT TYPE=hidden NAME="Width" VALUE="640"> <INPUT TYPE=hidden NAME="Height" VALUE="480"> <INPUT TYPE=hidden NAME="Language" VALUE="1"> <INPUT TYPE=hidden NAME="Direction" VALUE="Direct"> <INPUT TYPE=hidden NAME="PermitNo" VALUE="-2104609529"> <TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0> <TR><TD title="マウスでクリックした位置を画面の中央に移動します。"><INPUT TYPE=image NAME="NewPosition" SRC="nphMotionJpeg?Resolution=640x480&Quality=Standard" WIDTH=640 HEIGHT=480 BORDER=0></TD></TR> </TABLE> </FORM> <TABLE BORDER="0" CELLSPACING="0" CELLPADDING="0" style="padding-left: 10px;"> <TR><TD HEIGHT=1></TD></TR> <TR><TD HEIGHT="24"><FONT Face="Arial" Style="font-size: 11px;" COLOR="blue">音声が聴こえないときはこちらをクリックしてください</FONT></TD></TR> <TR><TD><A HREF="http://panasonic.biz/netsys/netwkcam/support/technic/sound/index.html" target="_blank"><FONT Face="Arial" Style="font-size: 11px;" COLOR="blue">http://panasonic.biz/netsys/netwkcam/support/technic/sound/index.html</FONT></A></TD></TR> <TR> <TD HEIGHT="24"> <FONT FACE="Arial" STYLE="font-size: 11px" COLOR="black"> <B>IPv4 で動作中</B> </FONT> </TD> </TR> </TABLE> <!-- ** /view ** --> </BODY> </HTML>
!!!! SRC="nphMotionJpeg? ・・・" というのが気になりますね!!!
ここをクリックしてみましょう。
■ こういう表示になります。
--myboundary Content-length: 31015 Content-type: image/jpeg リ�#タ� ン � ��������������������ロ�C� ( (H0 0H`H&(6M`phX::JhpplhXhjpppjoppzzzzzロ�C 0 2dB8Dddddddddddddddddddddddddddddddddddddddddddddddddddタ� � "� ト� �� �������� ト�オ � �� } � !1A Qa "q 2¢。 #Bアチ Rム�$3br�
: 以下、文字化けした記号が続く。
やっとたどり着きました!
文字化けした記号部分を31015バイト分受信して、そのデータをjpeg表示したらカメラ画像が出てきます。
■ Androidアプリで実現するには、こういう手順です。
PanasonicカメラのHTMLコードから上記のコードを追いかけ、SRC="npnMotionJpeg" というキーワードまでたどっていく。
Content-lengthで取得できるバイト数分のメモリ領域を確保し、その内部に受信データを蓄積する。
蓄積したデータをjpeg画像表示~メモリ解放を行う。
もう一度ここにアクセスすると、バイト数、データ内容が変化しているため、メモリ確保、jpegデータ取得、jpegデータ表示、メモリ解放を繰り返す。
この方法でPanasonicカメラの動画表示ができます!
ここまで追いかけて表示してくれるAndroid用ブラウザはFireFoxだけだったんですね。
やり方がわかれば実現はさほど難しくありません。
是非興味がある方、お試しください。
現段階では都合上ソースの公開は控えておきますが、興味のある方は連絡ください。
2013年4月5日金曜日
Windowsでのリアルタイム処理(2)
昨日に引き続き、Windowsでのリアルタイム処理をテストしてみた。
今回のテスト環境は
CPU Core-i3-2330M
メモリ 4ギガ
Windows7 Ultimate 64bit版 ServicePack1
開発環境 VisualStudio 2008
ソースは変更せず、こちらの環境で再ビルドを行い実行してみた。
下図はバックグラウンドでファイルコピーを行いながら、500マイクロ秒周期での呼び出しができているかを確認している状態を示している。
ほぼ500マイクロ秒で動いているが、途中1600マイクロ秒(1.6ミリ秒)、793マイクロ秒などがちらほら出現している。
※ ディスクコピーを行っていない状態では、500マイクロ秒近辺で安定している。
昨日の結果から、単純にハードウェア(CPU)のパフォーマンスによる差だけではなさそう。
CPU使用率が100%に達していないため、強制的にタスク切り替えなどが発生し、そのための遅延のような気がする。
WindowsのOS内部まで入り込んで、リアルタイム制御を実行するというものもあるが、そこまでシステムに入り込まなくても、ある程度リアルタイムを実行できる方法はないものだろうか ・・・
もう少し調査してみよう。
2013年4月4日木曜日
Windowsでのリアルタイム処理
RTL(Real Time Linux)のようなリアルタイム処理がWindowsでできるのだろうか?
テストプログラムを作って確認してみた。
実行マシン
CPU = Core-i7 870 2.93GHz
メモリ = 4G
Windows8 Pro with Media center 32bit版
開発環境 VC++/Express2008
約500マイクロ秒周期でクロック値をバッファリングできていることが表示された。
単純にみると、RT-Linuxと同等のことができている。
ただし背後に隠れているからくりは存在しているのだが。
途中ディスクアクセスなどが発生したらどうなるのだろう ・・・
お客さんからはいつもミリ秒単位できちんと関数を呼び出してなどと言われるが、そのたびにWindowsではOSが保障してないから難しいんですよと回答していた。
システム構成、ハードウェア構成を考えたら、こいつは利用できるかもと思う。
もうしばらく落とし穴などを調べてから、提案してみよう。
というわけでソースの公開はもう少し熟考してから(する? しない? 現状不明)。
興味がある方は声をかけてください。
2013年4月1日月曜日
AIDL呼び出しの処理時間
Androidアプリ間連携などで使用できるAIDLの処理時間を調べてみた。
テストに使用したタブレットはNEC N-08D
http://www1.medias.net/jp/sp/n08d/
テストの動作イメージを下図に記載する。
処理1) AIDL経由でバッファ転送処理を100000回繰り返し呼び出す。
処理2) AIDL経由でバッファ転送処理を1度のみ呼び出し、内部で10000回繰り返す。
計測した処理時間は以下のようになった。
処理1 処理2 AIDL実質呼び出し時間
1回目 34.702秒 0.154秒 0.34548ミリ秒
2回目 34.028秒 0.151秒 0.33877ミリ秒
3回目 34.034秒 0.176秒 0.33858ミリ秒
4回目 33.914秒 0.147秒 0.33767ミリ秒
5回目 33.882秒 0.176秒 0.33706ミリ秒
6回目 33.596秒 0.152秒 0.33444ミリ秒
7回目 33.503秒 0.149秒 0.33354ミリ秒
8回目 34.122秒 0.148秒 0.33974ミリ秒
9回目 33.842秒 0.146秒 0.33696ミリ秒
10回目 33.737秒 0.149秒 0.33588ミリ秒
※ AIDL実質呼び出し時間は(処理1時間-処理2時間)/100000で算出した。
この結果から、AIDL経由での呼び出しには約0.33ミリ秒の時間が必要なことがわかる。
一度関数呼び出すだけで0.3ミリ秒かかるということは、Javaだからということを考慮してもかなり遅い。
そこでもう少し考えてみた。
バッファ転送処理はJNI(C++)で128バイトのデータ転送を行っている。
転送のためにGetByteArrayElementsとReleaseByteArrayElementsを呼び出しているため、AIDL呼び出しではなく、実はこの関数ここで時間がかかっているのでは ・・ ?
バッファ転送処理の前後で GetByteArrayElements / ReleaseByteArrayElements関数の呼び出しをなくした処理でテストしてみた。
計測した処理時間は以下のようになった。
処理3 AIDL実質呼び出し時間
1回目 31.241秒 0.31241ミリ秒
2回目 29.703秒 0.29703ミリ秒
3回目 31.627秒 0.31627ミリ秒
4回目 30.118秒 0.30118ミリ秒
5回目 29.386秒 0.29386ミリ秒
6回目 29.331秒 0.29331ミリ秒
7回目 29.221秒 0.29221ミリ秒
8回目 29.567秒 0.29567ミリ秒
9回目 29.377秒 0.29377ミリ秒
10回目 29.992秒 0.29992ミリ秒
※ AIDL実質呼び出し時間は処理3時間/100000で算出した。
これらの結果から128バイト分のGetByteArrayElements/ReleaseByteArrayElementsには0.03ミリ秒程度かかっていること、そしてAIDL呼び出しには0.3ミリ秒が必要ことがわかる。
やはりAIDL呼び出し処理の負荷はかなり大きいことを前提でシステム構築する必要がありそうだ。
実際のソースコードはここに置いている。
ソース一式はこちらから
fifo.zip : バッファ転送を行う処理環境一式
fifotest.zip : AIDL呼び出し側テスト環境一式
4月1日に計測したが、エイプリルフールネタは含まれていない(まじめ)。
テストに使用したタブレットはNEC N-08D
http://www1.medias.net/jp/sp/n08d/
テストの動作イメージを下図に記載する。
処理1) AIDL経由でバッファ転送処理を100000回繰り返し呼び出す。
処理2) AIDL経由でバッファ転送処理を1度のみ呼び出し、内部で10000回繰り返す。
計測した処理時間は以下のようになった。
処理1 処理2 AIDL実質呼び出し時間
1回目 34.702秒 0.154秒 0.34548ミリ秒
2回目 34.028秒 0.151秒 0.33877ミリ秒
3回目 34.034秒 0.176秒 0.33858ミリ秒
4回目 33.914秒 0.147秒 0.33767ミリ秒
5回目 33.882秒 0.176秒 0.33706ミリ秒
6回目 33.596秒 0.152秒 0.33444ミリ秒
7回目 33.503秒 0.149秒 0.33354ミリ秒
8回目 34.122秒 0.148秒 0.33974ミリ秒
9回目 33.842秒 0.146秒 0.33696ミリ秒
10回目 33.737秒 0.149秒 0.33588ミリ秒
※ AIDL実質呼び出し時間は(処理1時間-処理2時間)/100000で算出した。
この結果から、AIDL経由での呼び出しには約0.33ミリ秒の時間が必要なことがわかる。
一度関数呼び出すだけで0.3ミリ秒かかるということは、Javaだからということを考慮してもかなり遅い。
そこでもう少し考えてみた。
バッファ転送処理はJNI(C++)で128バイトのデータ転送を行っている。
転送のためにGetByteArrayElementsとReleaseByteArrayElementsを呼び出しているため、AIDL呼び出しではなく、実はこの関数ここで時間がかかっているのでは ・・ ?
バッファ転送処理の前後で GetByteArrayElements / ReleaseByteArrayElements関数の呼び出しをなくした処理でテストしてみた。
計測した処理時間は以下のようになった。
処理3 AIDL実質呼び出し時間
1回目 31.241秒 0.31241ミリ秒
2回目 29.703秒 0.29703ミリ秒
3回目 31.627秒 0.31627ミリ秒
4回目 30.118秒 0.30118ミリ秒
5回目 29.386秒 0.29386ミリ秒
6回目 29.331秒 0.29331ミリ秒
7回目 29.221秒 0.29221ミリ秒
8回目 29.567秒 0.29567ミリ秒
9回目 29.377秒 0.29377ミリ秒
10回目 29.992秒 0.29992ミリ秒
※ AIDL実質呼び出し時間は処理3時間/100000で算出した。
これらの結果から128バイト分のGetByteArrayElements/ReleaseByteArrayElementsには0.03ミリ秒程度かかっていること、そしてAIDL呼び出しには0.3ミリ秒が必要ことがわかる。
やはりAIDL呼び出し処理の負荷はかなり大きいことを前提でシステム構築する必要がありそうだ。
実際のソースコードはここに置いている。
ソース一式はこちらから
fifo.zip : バッファ転送を行う処理環境一式
fifotest.zip : AIDL呼び出し側テスト環境一式
4月1日に計測したが、エイプリルフールネタは含まれていない(まじめ)。
登録:
投稿 (Atom)