Imaginary Code

from kougaku-navi.net

ネットワークカメラの映像をp5.jsで受信し表示する

iOS端末をネットワークカメラ化するアプリとしてipCamがあります。370円と有料ですが、リモートカメラをインスタントに作る手段として重宝します。ipCamが送信するカメラ画像をp5.jsで受信して表示するコードを書きました。

f:id:kougaku-navi:20211004155645g:plain

ipCam - Mobile IP Camera

ipCam - Mobile IP Camera

  • SKJM, LLC
  • 写真/ビデオ
  • ¥370
apps.apple.com


【p5.jsで書かれたコード】

var IP_ADDRESS = 'http://192.168.11.11/'; // IPアドレス(末尾の/まで入れること)

var img = null;         // 表示画像(バッファを参照しているだけ) 
var buf = [null, null]; // バッファ画像

function setup() {
    createCanvas(800, 600);
    startCapture(IP_ADDRESS);  // 画像受信の開始
}

function draw() {
    background(0);
    if ( img != null ) {        
        image(img, 0, 0);  // 画像の表示
    }
}

// ipCamからの画像受信を開始する関数
function startCapture(ip_address) {
    getImage(0, ip_address);
    // 以後、getImage()は再帰的に自動実行され、最新画像がimgという変数に入る。
}

// ipCamから画像を読み込む関数(idは読み込み先のバッファのインデックス)
function getImage(id, ip_address) {
    var now = new Date();  // 現在時刻
    var url = ip_address + 'image.jpg?' + now.getTime();  // カメラ画像のURL

    if ( buf[id] != null ) {
        buf[id].remove();  // 画像の削除
    }
    buf[id] = createImg(url, function() {
        // バッファを交互に入れ換えながら画像読み込み
        getImage(1 - id, ip_address);
        img = buf[id];
    });
    buf[id].hide();
}

いくつかポイントがあるので書いておきます。

  • 画像を読み込むには createImg()を使う。loadImage()やcreateImage()ではだめ。
  • JPEG画像を取得する際のURLにはクエリとして時刻をつける必要がある。これについてはWebブラウザでアクセスしたときの「JPEG Video」のページに書かれているJavaScriptを参考にした。
  • 生成された画像はそのままだと画面下に追加表示されてしまうので、.hide()で非表示にする。
  • 画像読み込み完了を待たずに画像を表示しようとしたり、画像読み込み完了を待たずに次の画像を読み込もうとする処理はダメ。ここではcreateImg()のコールバックを利用して、読み込みが終わってから表示の切り替えと次の画像の読み込みを行うようにしている。
  • createImg()で生成された画像はどんどんたまっていくので、使わなくなったタイミングで破棄していく必要がある。
  • 読み込み→表示→削除 を繰り返すが、画像を入れる変数が1つだと読み込み中や削除中に表示できる画像がないという問題(→ちらつきの原因になる)があるので、バッファを2個用意して「表示」と「削除&読み込み」を交互に行っている。いわゆるダブルバッファリング。

 
  
関連:以下は以前書いた記事です。こっちではPC用のGmax IP Cameraというアプリを使っています。この時は今回ほど複雑なことをやらなくてもよかったので、送信側アプリの仕様に依存するところがあるのかな、という感じです。
kougaku-navi.hatenablog.com