のしメモ アプリ開発ブログ

Unityアプリとかロボットとか作ってるときに困ったこととかメモ

UnityでTHETA SのWifiストリーミングをする手法メモ

UnityでTHETA SのWifiストリーミングをする手法メモしてみました

実現させること

vine.co
上記のことをUnityで実装する方法です。

2016 8/25追記

warapuriさんがUnityプロジェクトを公開してくださっているので、手っ取り早く試してみたい方は下記のUnityプロジェクトを使ってみてください。
github.com

________________________________________________________
以下、解説です。

APIについて

Wifiライブストリーミングを扱う際に、APIとしては_getLivePreviewを使用します。
公式リファレンス

他のAPIと違い_getLivePreviewに関しては、ストリームでデータが垂れ流しで渡されるため、リクエスト完了まで待つWWWクラスで取得することができません(たぶん)。

処理の流れ

1.HttpWebRequestクラスを作成しPostリクエストを設定

string url = "thetaのHttpパスをここに入力します";
var request = HttpWebRequest.Create (url);
HttpWebResponse response = null;
request.Method = "POST";
request.Timeout = (int)(30 * 10000f); // タイムアウトしないようにする
request.ContentType = "application/json;charset=utf-8";
			
byte[] postBytes = Encoding.Default.GetBytes ("jsonデータをここにいれます");
request.ContentLength = postBytes.Length;

2.byteデータを取得するためBinaryReaderのクラスを生成します(1byteずつ取れます)

// ポストデータの送信開始
Stream reqStream = request.GetRequestStream ();
reqStream.Write (postBytes, 0, postBytes.Length);
reqStream.Close ();
stream = request.GetResponse ().GetResponseStream ();

BinaryReader reader = new BinaryReader (new BufferedStream (stream), new System.Text.ASCIIEncoding ());

3.mjpegの1枚画像の開始byte〜終了byteで画像を切り取る

Mjpegの仕切り値をbyteでチェックして切り取るという感じです。

...(http)
0xFF 0xD8      --|
[jpeg data]      |--1枚の画像
0xFF 0xD9      --|
...(http)
0xFF 0xD8      --|
[jpeg data]      |--1枚の画像
0xFF 0xD9      --|
...(http)

python - How to parse mjpeg http stream from ip camera? - Stack Overflow

※ Mjpegの仕様の仕切り値
開始2byteが0xFF、0xD8で、終了byteは0xD9

コードはこんな感じになるかと

List<byte> imageBytes = new List<byte> ();
bool isLoadStart = false; // 画像の頭のバイナリとったかフラグ
byte oldByte = 0; // 1つ前のByteデータを格納する
while( true ) { 
    byte byteData = reader.ReadByte ();

    if (!isLoadStart) {
        if (oldByte == 0xFF){
            // 画像の最初のバイナリ
           imageBytes.Add(0xFF);
        }
        if (byteData == 0xD8){
           // 画像の2番目のバイナリ
           imageBytes.Add(0xD8);

           // 画像の頭をとったので終了バイナリを獲得するまでとる
           isLoadStart = true;
        }
    } else {
        // 画像バイナリの配列にいれます
        imageBytes.Add(byteData);

        // byteが終了byteだった場合
        // 0xFF -> 0xD9の場合、終了byte
        if(oldByte == 0xFF && byteData == 0xD9){
            // 画像の終了byteなので
            // ここで溜まったbyteから画像を生成し、テクスチャの作成をすることができる
            // imageBytesをテクスチャに反映して
            // imageBytesを空にしておく

            // 画像の頭のバイナリ取得のループに戻る
            isLoadStart = false;
        }
    }
    oldByte = byteData;
}

※ 2016 5/25 宣言がループ内にあったのと、フラグがわかり難かったので修正しました
※ 2016 8/23 画像の終了byteチェックでバグ(一つ前のbyteのチェックが抜けていた)があったので修正

4.区切ったbyteでテクスチャ生成

Byteをテクスチャに反映させます

mainTexture.LoadImage ((byte[])imageBytes.ToArray ());

ちょこちょこ端折っていますが、こんな感じでなんとか実装することが可能です。
もっといい方法もありそうですね!