2015年2月9日月曜日

raspberry pi でストリーミングした動画をwindowsで見る(~30fps)

1日つぶしたのでメモ。
今日調べた限りではここで紹介されているライブラリを使わないとraspberrypiカメラモジュールのストリーミングでfpsが出ない(他の手法は写真を連続で撮って配信しているものが多いため遅く、不安定)

主要参考記事
スイッチサイエンスマガジン様 : Cloud Piをつかってみました。(カメラモジュール)
http://mag.switch-science.com/2015/01/22/cloudpi_5/

Pieces for electronic kits 様 : Rasberry Piで動画のストリーミング再生をする
http://neko-kousaku.hatenadiary.jp/entry/2014/05/26/183522

参考記事内の主要ライブラリ(github)

https://github.com/jacksonliam/mjpg-streamer

参考記事中(両方)のgitクローン部分の「jacksoliam」がタイプミスしており、ここを「jacksonliam」とするとDLできた。わざとか?それともjacksonさんがタイプミスしていたのを修正したのか・・・
git clone https://github.com/jacksonliam/mjpg-streamer.git

あとは記事通りにインストール

記事通りにmjpeg-streamer を走らせ、同じLAN内のwindowsのブラウザでスムーズな動画ストリーミングできていることを確認(320x240だが) 

注意点として高FPSにすると電力を食うのかstreamer の挙動が不安定になり停止する場合がある。今回スマホ用モバイルバッテリー(2.1A 出力)を使用していたがコレが原因なのかは未確認

 

2014年4月30日水曜日

GWの工作2014 その4 ~3Dプリンタでステアリングを作ったときのメモ~

GWの前半を使ってラジコンのステアリング部分を作ったときのメモ

3Dプリンタを使った工作についての参考資料があまり無かったのでもしかしたら役に立つ人もいるかもしれない。

外観
















STLはこちら↓(Thingiverse)
https://www.thingiverse.com/thing:315540

四節リンクだの何だのを検討しまくったり試作と挫折を繰り返しながら結局とても原始的な形に落ち着いた次第。ステアリングもそうだが特にサーボをユニバーサルプレートに固定できるようになったのは進歩な気がする(サーボの回転軸もユニバーサルプレートの穴中心に合わせてある。いろいろ便利)。

その1の材料にも書いてあるが、機構部分だけで
 ・タミヤ TAMIYA 72007 4速パワーギヤボックスHE [テクニクラフトシリーズ]
 ・タミヤ TAMIYA 楽しい工作シリーズ シャフトネジセット(直径3、直径4mm) [特殊ネジ]
 ・タミヤ TAMIYA 楽しい工作シリーズ ユニバーサルアームセット
あたりが必要

筆者環境

・3Dプリンタ : makergear M2 (割りとでかいものも作れる3Dプリンタ)
1.75mmPLAフィラメント(最初からついてきたやつ)
・CAD : Cubify Design  (25000円で買えるCAD)
・Gコード生成ソフト : slic3r
・その他使った工具 : ピンバイス(φ2mmくらいのドリル付き、穴のバリ取りやわずかに穴を広げるのに使える) 例:ANEX ピンバイス収納式 ドリル付 (amazon)

3Dプリンタの精度とか設計とかについてのメモ
・前提:筆者使用のプリンタは個人用のやつでは精度やや良い方のもの(だと思う)、ちなみに積層は0.3mm/層とやや厚めにしてある。

・xy平面の穴 : サーボモータのリンク(写真左側)の長円の幅が設計値φ3.6mmに対して実測3.3~3.35mm
水平面(xy平面)の穴は外側(樹脂がある部分から無い部分の方向)に0.1~0.2mm「にじむ」と思って作った方がよさそう

・Z方向の穴(横穴) : 設計φ3.6mmに対して3.3mm(積層の関係で測定位置ごとにばらつく)、構造上やむをえないが割とガタガタ。0.1~2mm小さめに設計してピンバイスややすりで広げるか、ベアリング的なものを別に作ったほうがいい

・xy方向の凸(横方向への出っ張り) :  設計φ2.8mmに対して3~3.15mm、やはり積層の関係でばらつくし触った感じガタガタだが意外に精度は悪くない。PLAの凸を上手く削るのは面倒なので若干小さめに設計して穴凹の側で嵌め合いを調整するのが吉か。

・ 積層高さ : 当然のことながら設定した積層値の倍数でしか積層されない。高さ1mmのものを0.3mm/層で作ると0.9mmか1.2mmになると思われる(未確認)

・直径で0.3mm程度の余裕があるとスムーズに動く(写真左のサーボリンク部分)、この辺は機械設計とかの教科書なりを見たほうがいい気がする(ちなみに筆者は素人)。

更新するかもしれないが、とりあえず以上

2014年4月29日火曜日

GWの工作2014 その3 ~操作インターフェースのwebアプリ化~ 

androidのバージョンがどうたらみたいな問題に悩まされたり、
PC上でちょっとした動作テストを行うために専用コードを書いたり(PCでのテスト用にGUIのインタフェースを作ろうとも考えた)、
もしかしたらiphoneでも使いたくなるかもしれないとか考えたときの解としてhtml(とjavascript)を選択。これは今回のラジコン製作の中でも最も成果が大きかった。ブラウザがあれば大体何でも使えるインターフェースが作れるのは多分後で効いてくる。はず

~今回のお話~
pythonのwebフレームワークを使ってPCをサーバー化、websocketを使ってリアルタイム性の高い通信を行ったときのメモ

環境
・python2.7 (eclipse4.3)
・c,c++のコンパイラはmingw32
・webフレームワークとしてgevent、geventwebsocket.handlerを使用
・htmlとjavascriptはBracketsとかいうフリーソフトを使用。ライブプレビュー機能が便利。

参考資料:
geventの導入関連(筆者環境では非常に重要だった。visualstudioを普段から使っている人は素通りできるかも)
・ http://stackoverflow.com/questions/2817869/error-unable-to-find-vcvarsall-bat
(概要、上記参考URLより引用) 
C:\Python27\Lib\distutils\distutils.cfg(←ファイルが無かったら新規作成)の中身を↓

[build]
compiler=mingw32

とする(内容を追記、変更する)。そのあとプロンプトを立ち上げてgeventをインストール(↓の問題が無ければ)

・http://stackoverflow.com/questions/13592192/compiling-pygraphviz-unrecognized-command-line-option-mno-cygwin
 (概要、上記参考URLより引用)
C:\Python27\Lib\distutils\cygwinccompiler.py  というファイル内の

    self.set_executables(compiler='gcc -mno-cygwin -O -Wall',
                         compiler_so='gcc -mno-cygwin -mdll -O -Wall',
                         compiler_cxx='g++ -mno-cygwin -O -Wall',
                         linker_exe='gcc -mno-cygwin',
                         linker_so='%s -mno-cygwin %s %s'
                                    % (self.linker_dll, shared_option,
                                       entry_point))
 を
 
    self.set_executables(compiler='gcc "" -O -Wall',
                         compiler_so='gcc "" -mdll -O -Wall',
                         compiler_cxx='g++ "" -O -Wall',
                         linker_exe='gcc ""',
                         linker_so='%s "" %s %s'
                                    % (self.linker_dll, shared_option,
                                       entry_point))
 
に変更する(-mno-~というオプションを消す)
これでgeventに限らずコンパイルが必要なpythonのツールが
スムーズにインストールできるようになるはず。
 
Websocketの参考資料: 
geventを使ったwebsocketの実施例
 へきょのーと 様 記事URL http://d.hatena.ne.jp/hekyou/20120712/p1
 
上の記事を参考にしてwebsockサーバーを立て、↓のサイトで動作を確認
Echo Test  http://www.websocket.org/echo.html

htmlの参考資料:
stackoverflow http://stackoverflow.com/questions/5165579/onchange-event-for-html5-range
htmlのスライダーの値をリアルタイム変化させる為の質問。
Scott氏の回答が非常にスマート 
 
参考資料などを頼りになんとかインターフェースを構築 
メモ :
・javascriptのファイルとhtmlのファイルは別にすること
(htmlに直接スクリプトを書き込まないこと) これをやらないとIDEのエラーチェックがちゃんと機能しない

・python側、html側ともにPC(サーバー)のアドレスを127.0.0.1にしないこと。
別の機器からアクセスするとき面倒なことになるので192.168~のようにIPアドレスを記入すること

・IPカメラの映像を写すには以下のようにする。大きさはお好みで
(AI-ball(インフラストラクチャモード)の場合) 
<img src="http://(カメラのIPアドレス)/?action=stream.mjpeg" width="320" height="240"> 
(「IPwebcam」を使ったスマホの場合)
 <img src="(スマホのIPとポート)/videofeed" width="320" height="240">
 
 それぞれ上のタグを入れるだけで(大抵の)ブラウザ上に一発で動画が出る。
アンドロイドアプリで動画を出そうと苦しんでいたのは何だったのか。googleの人(?)を
問い詰めたくなるレベル。あとhtmlの仕様を策定している人たちは神
 
 肝心のインターフェースのほうは一応動かすのに必要な要素はあるが(その1の動画参照)
まだぜんぜんなのでまた今度

GWの工作2014 その2 ~VPN(SoftEther VPN)導入時に詰まった箇所のメモ~

~VPNの導入~ とか大げさなタイトルだが単純に自分が詰まったところのメモ

レジストリを変更する部分があるので自己責任で

設定時筆者環境
・win7 64bit
・NTTのフレッツ光 マンションタイプ 、プロバイダはyahooBB
・壁の電話線→NTTモデム→ルータ(BUFFALO WHR-300)→有線でPCと無線でタブレットなど
・スマホはDocomoのgalaxynote2。ドコモのケータイはVPN接続に癖があるらしいが以下のやり方(L2TP/IPsecを使用)では問題なし

導入メモ
・SoftEther VPN(https://ja.softether.org/) というVPNを導入。完全無料!筑波大学は神。オープンソースらしいので多分ずっと使えるだろう。

・基本的に上記HPのマニュアル通りに設定するだけでいける。
→チュートリアル「モバイルにおけるVPN」と「SoftEther VPN Server を使った L2TP/IPsec VPN の構成」 (一番下の方)を参照

・ マニュアルに書いてあったのだが見落としていて1日消費した箇所を以下にメモ
→見落としていたのはチュートリアル「VPN Server 側での L2TP/IPsec 機能の有効化方法 」の下の方の「VPN サーバー側に NAT がある場合」という項目の

・Windows 標準搭載の L2TP VPN クライアント機能を用いる場合で、接続先の VPN Server が NAT の背後に設置されている場合は、クライアント側のレジストリを以下のリンクの解説に従って編集する必要があります。

という文章とリンク(上の文言の下にリンクが張ってある。)。要は(ちゃんと理解してないが)外部のスマホとサーバー(今回は自宅PC)の間にNATデバイス(多分ルータのこと)がある場合はレジストリをいじる必要があるということ。リンク先を読めばわかるが筆者は値を1に変更

これをやらないとスマホでいくらがんばってもVPNが張られない(スマホからのデータがルータを通過できてもPCで弾かれる)

GWの工作2014 その1 ~スマホから動かせるラジコンの概要~

ここ最近作っていたラジコン(rev4?)のまとめ
細かいことは後の記事に個別でメモ

要約 : LANの届く範囲でなら動いたり色々入出力できる何か

主な機能 : 
1、スマホで外からでもコントロールできる。 (VPNで繋いでるだけ)。操作する人は電波さえあればどこにいてもいいがラジコンはLANの範囲(家の中)から出られない。

2、操作インターフェースはhtml+javascriptを採用。PCだろうがスマホ(androidでもiphoneでも)だろうがブラウザさえあれば関係なし。

3、前回は マルチタッチとかなんだかおしゃれなインターフェースを採用していたが、操作感が微妙だったので今回はとりあえずファミコン式(8方向のボタンで操作)

4、IP カメラで前方を見ながらのコントロールが可能。

5、arduinoとpythonの連携プラットフォームを使用しているのでいろいろ拡張可能。(そのうちちゃんとしたものができればGithubとかに上げたい)

6、無駄に駆動輪を独立駆動可能。ステアリングの操作と合わせて戦車的な挙動ができる(片方の駆動輪を中心にして回転とか)。

見た目:

上から : (前回のものと比べると)洗練された外観。まだ臓物載せただけ感は払拭できない
















 横から : 前回のラジコンはキャタピラだったが、やたら脱輪するのでタイヤに変更

















操作風景(動画)

 

ちなみにスマホのほうはWIFIではなくVPNを使って携帯の電波で動かしている。つまりスマホの電波さえ入っていれば家の中の映像を見ながらラジコンを操作できる!これで鍵の閉め忘れも確認できる(カメラの配置に課題)。
スマホ側の動画が若干カクカクしているがこれは電波が弱い為。外に出てLTEのアンテナが最大だともう少しましになった。

さっきその辺の公園から家にあるラジコンを動画のような感じで動かせることを確認(おもむろに公園のベンチに座りスマホでラジコンの動作確認をしてすぐ立ち去る様は若干不審人物だった気がする)。

材料 : 
(´д`;).oO(材料のリンクをアドセンスにすれば小遣い稼ぎができるのでは・・・ゴクリ。と思ったがそんなアクセス無かった。)
 気を取り直して材料リスト(以下に無いものも写真に写っているが今回は使ってないのでカット)

ヨドバシ.comで購入 (タミヤの部品はヨドバシが便利だった201404月現在)
・タミヤ TAMIYA 楽しい工作シリーズ ユニバーサルプレート(210×160mm)
・タミヤ TAMIYA 楽しい工作シリーズ ユニバーサルプレート(2枚セット)
・タミヤ TAMIYA 楽しい工作シリーズ ナロータイヤセット(58mm径)  ×2
・タミヤ TAMIYA 72007 4速パワーギヤボックスHE [テクニクラフトシリーズ] ×2
・タミヤ TAMIYA 楽しい工作シリーズ シャフトネジセット(直径3、直径4mm) [特殊ネジ] ×2くらい
・タミヤ TAMIYA 楽しい工作シリーズ ユニバーサルアームセット
スイッチサイエンスで購入 (同等品でも可。ただしArduino Leonardo系は何となくおすすめしない )
・デュアル・モータードライバTB6612FNG(連続最大1.2A)
・リチウムイオンポリマー電池×2
・Arduino Uno R3
・XBee ZB モジュール ×2
・XBeeエクスプローラUSBドングル
・XBee 5Vインターフェースアダプタ
・SparkFun製 プロトシールド・キット
・ジャンパワイヤ ×適量

アマゾンで購入。IPカメラなら何でもいい(余ったスマホに「IPwebcam」をインストールしたもので問題ない。というより重さ以外はコレがおすすめ)
・Trek 2000  Ai-Ball (写真ではなにやら回路がくっついているが(写真1枚目左側)、これは3.3V出すための電源回路。弄り倒して付属の電源アダプタをぶっ壊していなければ不要)

秋月電子で購入
・GWSサーボ PIC+F/BB/F(フタバ) (設定値、スペックなどは過去記事参照)




2014年2月23日日曜日

xbee wifiに関する走り書き

xbee wifiをソケット通信で普通に使うのに必要そうな情報

受信と送信のソケットを同時に開くことは(多分)できない。送信(または受信)が終わったらそのたびにソケットを閉じること

・基本だがsocket.accept()の直後に(ウェイトを入れずに) socket.recv() などでデータを取り出そうとしてもエラーになる。バッファがちゃんと溜まるまで(人間にとって)ちょっとでも待つべき。

・連続してデータの送受信を繰り返したいとき、デフォルトの設定だと受信(PC←xbee)で10秒、送信で多分60秒くらい待つようになっている。「Network」のTMとTSが受信と送信のタイムアウトになっているのでこれらを限界まで小さくすると待ち時間が減らせる(とりあえず0.5秒に1回程度の送受信ができる)

・「serial Interfacing」のROもタイムアウトになっている。小さくすると通信が多分速くなる。

・送受信のソケットを再生成するのにも時間が必要なので繰り返す場合は生成前にウェイトを入れること。

とりあえず上記で(電源系の問題を除けば)普通のxbeeと同じように使えるはず。詳細は来週

2013年12月23日月曜日

opencvで処理した画像(動画)をandroid端末に送信して表示するあたりのメモ

以前、SimpleMjpegViewというライブラリを利用してwebcamの映像をandroid上に表示したが、今回は別の手法で(もっと原始的な形で)端末上に動画を表示する。

前回:

 webcam → (mjpegストリーム) → android端末

今回:
 webcam → PC(c++(Opencv)) → (jpeg) → android端末

 今回の方でやれば重たい画像処理を母艦PCやraspiで行ってその処理結果をandroidに送信できる。

とりあえず結果↓

 いいwebカメラ(ロジクールの1万のやつ)なのに若干カクつく。サイズを小さくしたり、jpg品質落とすことでましにできると思われる



opencvを使ったりしているのでMITライセンスになります。
強制終了前提のコードなのであくまでご参考、自己責任でお願いします。

↓MITLicenceに基づく告知

Some codes in OpenCV are utilized.
Therefore, before downloading, you have to agree to the following license.
//////////////////////////////////////////////////////////////////////////////////
IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
By downloading, copying, installing or using the software you agree to this license.
If you do not agree to this license, do not download, install,
copy or use the software.

                    Intel License Agreement

            For Open Source Computer Vision Library
Copyright (C) 2000, Intel Corporation, all rights reserved.
Third party copyrights are property of their respective owners.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
  • Redistribution's of source code must retain the above copyright notice,
    this list of conditions and the following disclaimer.
  • Redistribution's in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.
  • The name of Intel Corporation may not be used to endorse or promote products
    derived from this software without specific prior written permission.
This software is provided by the copyright holders and contributors "as is" and
any express or implied warranties, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose are disclaimed.
In no event shall the Intel Corporation or contributors be liable for any direct,
indirect, incidental, special, exemplary, or consequential damages
(including, but not limited to, procurement of substitute goods or services;
loss of use, data, or profits; or business interruption) however caused
and on any theory of liability, whether in contract, strict liability,
or tort (including negligence or otherwise) arising in any way out of
the use of this software, even if advised of the possibility of such damage.

 ライセンス告知ここまで


まずC++側、環境はwin7 64bit Gcc(MINGW) opencvが必要(今回は2.4.7使用)


概要 : 
0、ソケット構築、android側からのアクセスを待ち受け

1、opencvで動画や画像フレームを取ってきて好きなように処理して最終的にjpgに圧縮 jpgのデータはvector<uchar>で保存される

2、jpgのデータサイズ(何バイトか)をバイナリ(intだと4byte)にする、地味なビット演算処理が必要(もっとスマートにできそうだが)。要は送信先でintとしてデータサイズを取れるようにする。これがわからないとあちらで正確なバッファが作れない

3、 jpgデータサイズとjpgデータをandroidに送る。
   vector<uchar>をソケットで送信できるconst char*に変換する部分の調査に時間がかかった(初見だとここで詰まる可能性あり)

4、おわり。上記処理をループさせている


(参考ソース)

#include <iostream>
#include <windows.h>
#include <winsock2.h>

#include "opencv2\highgui\highgui.hpp"
#include <vector>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <sstream>

using namespace cv;
using std::stringstream;
int main()
{
~~~~~~~~~~~~~



ここにソケットの初期化(bindまで)の処理が入るが諸事情(参考文献の丸写しのため)によりカット
参考文献 : http://blog.pusheax.com/2013/07/windows-api-winsock-create-your-first.html

windowsソケットプログラミング(C++)の基本説明



~~~~~~~~~~~~~



    //////////////// start(original from here)

    int lis=listen(u_sock,3);
    /////////////// waiting client data   
   
    //opencvによる動画の取得
    VideoCapture cap(0);
   
    //メインループ
    while(1){
        stringstream ss;
        //クライアントからの受信用バッファ、何らかのコマンド受け取りを想定
        int receive_buf_size = 20;
        char vect[receive_buf_size];

        std::cout <<"start waiting client command" <<std::endl;
        //client adress define
        struct sockaddr_in client;
        int len = sizeof(client);

        //client connect waiting・・・クライアント定義、待ちうけはループ外でもいいかもしれない
        SOCKET sockCli = accept(u_sock, (struct sockaddr *)&client, &len);   
        //クライアントから20byte何かしら受け取る、必要に応じてパース
        int get=recv(sockCli,vect,receive_buf_size,0);
        if(get==SOCKET_ERROR){
            std::cout<<"Error in Receiving: "<<WSAGetLastError()<<std::endl;
            continue;
        }

        // receivedta(command) process 受け取ったデータの処理、Stringstreamを使って文字列にして表示
        Sleep(10);//data receive waiting クライアントからのデータを待つ
        std::cout <<"end receive"<<std::endl;
        ss.clear();
        String res_string;
        ss << vect;
        ss >> res_string;

        if (get <0){
            std::cout <<"no receive data"<<std::endl;
            //    break;
        }else{
            std::cout <<"receive data : ";
            std::cout <<res_string<<std::endl;
        }

        ss.clear();

        /////////// image data send process 画像の取得と送信   
        Mat frame;

        cap >> frame;

        int framesize = 1000000; ///Max size

        //送信データ用のバッファ
        vector<uchar> bufv;
        //ちゃんと変換されたかの確認用
        bool imok;
        //jpg圧縮用のパラメータ設定
        vector<int> jpgparam;
        jpgparam.push_back(CV_IMWRITE_JPEG_QUALITY);
        jpgparam.push_back(20); /* jpeg quality */
        //ここまでパラメータ設定

        //圧縮した画像をバッファに入れる
        imok=imencode(".jpg",frame,bufv,jpgparam);
       
        //Mat sframe;

        std::cout<<bufv.size()<<std::endl;

        //Send some message to remote host

        ///////////// jpg data sending
        std::cout <<"start sending"<<std::endl;

        std::cout <<"send size"<<bufv.size() <<std::endl;
        std::cout <<"senddata "<<bufv.data()<<std::endl;
        std::cout <<"start sending"<<std::endl;

        // sizeof jpg send to java  画像サイズ(何バイトか)をクライアントに送信する
        // intの数値をバイナリ(4バイト)に変換する。泥臭い処理
        vector<uchar> jpgsize_send(4);
        int jpgsize=bufv.size();
       
        //int(4byte)のバイナリを1バイトずつ送信用の配列に入れる。エンディアン注意   
        for (int i=0;i<4;i++){
        jpgsize_send[3-i]=(jpgsize >>(i * 8) );
        }
        ////////// bynary genelator

        std::cout <<"data size : "<< jpgsize <<"type size"<<sizeof(jpgsize) <<std::endl;
        //std::string siz=std::to_string(jpgsize);
        std::cout <<"type size"<<sizeof(jpgsize_send) <<std::endl;
       
        /////////////////////// send data to android
        //vectorをcharに変換してクライアントに送信、
        //ロベール本によるとreinteroret_castの使用には注意が必要らしい

        //ヘッダ部(今回は画像データバイト数のみ)送信
        int smsg_header=send(sockCli,reinterpret_cast<const char*>(jpgsize_send
                    .data()),jpgsize_send.size(),0);
        //画像データ送信
        int smsg=send(sockCli,reinterpret_cast<const char*>(bufv.data()),bufv.size(),0);

        ///////////////////////


        if(smsg==SOCKET_ERROR){
            std::cout<<"Error: "<<WSAGetLastError()<<std::endl;
            WSACleanup();
        }

        /*
        //get=recv(u_sock,vect,512,0);
        if(get==SOCKET_ERROR){
        std::cout<<"Error in Receiving: "<<WSAGetLastError()<<std::endl;
        }
        //std::cout<<vect<<std::endl;
        */
    }
    closesocket(u_sock);

~~~~~ ここから終了処理を適当に

}

------------------------------------------  C++ここまで



Java(Android側)、eclipse4.3(Kepler Service Release 1) androidosは4.4が入ったnexsus7(2012)


概要:androidのプロジェクトで新しいクラスを作って以下のソースをコピペして、パラメータ(アドレス、ポートなど)設定してからonCreateに他のTextViewみたいに設定するだけで動作する(コピーした時点でレイアウトのxmlのカスタム&~のところに出てくるので普通にレイアウトできる)。はず。
インターネットの許可が必要(忘れてちょっと詰まった)


ゴミコードが混ざっているのはご愛嬌。あくまで個人的なメモなので。
自動生成とかどうでもいいところでないところは太字にしてある



基本的なsurfaceviewのクラスにネットから画像を取ってくるクラスを接続している。サーフェスの更新をマルチスレッドでずっと回してその中でさらにネットから画像を取ってくるスレッドを走らせる。
はっきり言ってバグの温床なのでいろいろ注意、ちなみに強制終了前提




コード

(Class Mysurfaceview.java)


package com.example.jpeg_binaly_client;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.ImageView;
import android.widget.TextView;

public class Mysurfaceview extends SurfaceView implements SurfaceHolder.Callback, Runnable {

    //ビューの常時更新には普通のマルチスレッドを使う
    Thread thread;


    SurfaceHolder holder;

    //更新される画像
    public Bitmap tcpbmp;
    //ネットワークスレッドの更新可能シグナル
    public int imggetflag;

    public Mysurfaceview(Context context, AttributeSet attrs ) {//太字の部分がないとxml関連のエラーになる
        super(context,attrs);
        // TODO 自動生成されたコンストラクター・スタブ
        getHolder().addCallback(this);
        this.holder = getHolder();

    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
        // TODO 自動生成されたメソッド・スタブ

    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // TODO 自動生成されたメソッド・スタブ
        thread = new Thread(this);
        thread.start();


    }

    @Override
    public void run() {
        while(true){//無限ループ注意

            doDraw(getHolder());

            try {
                Thread.sleep(100);
//通信も絡むので若干待つ、が、ここで待たなくてもいいかも
             } catch (InterruptedException e) {
                // TODO 自動生成された catch ブロック
                e.printStackTrace();
            }
        }

    }


    //描画用関数
    public void doDraw(SurfaceHolder holder) {
       
        tcpbmp =null;
        //画像取得スレッドを走らせる
        tcp_getimage3 jpgg= new tcp_getimage3();
        jpgg.execute("nl");
//特にコマンドなんかが無ければ何でもいい

        int cnt=0;
        int timeout =10;
        int waiting=10;
   //画像取得が終わるまで待つ
        while (imggetflag ==0){
            System.out.println("waiting get...");

            //一定時間待つ
            try {
                Thread.sleep(waiting);
            } catch (InterruptedException e1) {
                // TODO 自動生成された catch ブロック
                e1.printStackTrace();
            }
            cnt += waiting;
            //一定時間待っても更新シグナルがこない場合はタイムアウトとする
            if(cnt>timeout){
                System.out.println("waiting get...timeout");
                break;}
        }

        //更新可能(フラグが出ており、bmpがカラでないとき)なら画像を更新
        if(imggetflag==1 && tcpbmp !=null){
             Canvas canvas = holder.lockCanvas();

             canvas.drawBitmap(tcpbmp, 0, 0, null);
          
            holder.unlockCanvasAndPost(canvas);

        }
        //フラグを0にする
        imggetflag=0;

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // TODO 自動生成されたメソッド・スタブ

    }

    //ネットワークからの画像取得クラス
    public class tcp_getimage3 extends AsyncTask<String,Integer,byte[]>{


        public String ipaddr="(IPアドレス)"; 

        int port_bg = (ポート番号);
       
        Socket socket_bg = null;

        DataOutputStream out=null;
        BufferedReader inp=null;

        DataOutputStream out_bg=null;
        DataInputStream inp_bg=null;
       

        //エラー出力用のカラのバイト列
        public byte[] reterr =null;


        public int cnt =0;
        public int serverflag_bg=0;  //1だとサーバ無しとみなしている。

        public tcp_getimage3(){
            super();
        }



        @Override
        protected byte[] doInBackground(String... val) {
            // TODO 自動生成されたメソッド・スタブ
           
            if (serverflag_bg==0){

                try{

                    try{
                        InetSocketAddress addr = new InetSocketAddress(ipaddr, port_bg);
                        socket_bg =new Socket();
                        socket_bg.connect(addr,50);//タイムアウトの時間は重要、繋がらなかったらさっさと切り上げて再接続したほうが早いので、とりあえず短いほうがいい。でも短すぎると電波環境によっては通信できない可能性もある。

                    }catch(SocketException e){
                        e.printStackTrace();
                        Log.i("network", "socket not connect");
                        return reterr;  //タイムアウトしたらさっさとスレッドを閉じにいく

                    }catch(Exception e){  ///調べたがこの例外じゃないと死ぬ。重要
                        e.printStackTrace();
                        Log.i("network", "その他のネットワーク系エラー");

                        return reterr;

                    }


                    Log.i("network", "sendstart ");
         ///////////////////////////////////////ここからが実質的な処理
                    DataOutputStream out_bg = new DataOutputStream(socket_bg.getOutputStream());

                    ////// データの送信、コマンドなどが無いなら何でもいい
                    out_bg.writeBytes("jpgg");

                    byte[] buf =new byte[1000000]; //最大データサイズ(byte)c++と合わせる

                    int readflag;

                    //データ読み込み↓
                    InputStream is =socket_bg.getInputStream();
                    DataInputStream inp_bg = new DataInputStream(is);

                    Thread.sleep(50);//4バイト受信できるくらい待つ必要がある


                    int receive_size=inp_bg.readInt(); //C++からヘッダ(画像のバイト数)情報を受け取る

                    //画像容量分のバッファを定義する、ここら辺が正確じゃないとBMPがちゃんと生成されない

                    byte[] buffull =new byte[receive_size];

                    //画像データをすべて受信するまで待つ
                    if (inp_bg.available() != receive_size){

                        Thread.sleep(10);
                    }


                    //受信データをバッファに入れる、ループしなくていいかも
                    while(inp_bg.available()>=0){
                        System.out.print("datasize is ");
                        System.out.println(receive_size);

                        //受信データをバッファに入れる。何らかの方法で全部のバッファを受け取ったか確認する必要がある
                        inp_bg.read(buffull,0,receive_size);
                        if(inp_bg.available()==0){break;}

                    };
                   
                    out_bg.close();
                    inp_bg.close();
                    socket_bg.close();


                    //バイナリをビットマップに変換する。jpgも勝手に解析してBMPにしてくれる
                    Bitmap bmplocal=null;
                    bmplocal=BitmapFactory.decodeByteArray(buffull,0,buffull.length);
                    System.out.println(buffull.length);←データサイズと同じか一応比較

                    //グローバルのbmpに変換したBMPを入れる
                    tcpbmp = bmplocal;


                    ///////////////////////////////////////ここまでが実質的な処理
                    //バイナリを出力する。onPostExecuteでの処理はこっちに持ってきても問題ない
                    return buffull;

                } catch (IOException e) {
                    e.printStackTrace();
                    System.out.println("io err"+e);

                    serverflag_bg=1;
                    return reterr;
                }catch (Exception e) {
                    e.printStackTrace();
                    Log.i("network", "Iilligal ");
                    System.out.println("io err"+e);

                    serverflag_bg=1;
                    return reterr;

                }

            }else{
                Log.v("tcp","noserver");
                return reterr;
            }
        }

        protected void onPostExecute(byte[] result) {

            //呼び出し元のスレッドに描画更新OKのシグナルを出す
            if (result !=null){
                imggetflag=1;}
            else{
                //何らかのエラーでバイナリが無い場合はシグナルを出さない
                imggetflag=0;
            }

            System.out.println("end tcp thread");


        }

    }
}