LoginSignup
3
4

More than 3 years have passed since last update.

Android の Camera2 API を使って Webカメラにする

Last updated at Posted at 2019-09-14

Android の Camera2 API を使って カメラのプレビュー画面を表示する
の続きです。

Webカメラ

Webインターフェイスを持つリアルタイムカメラのこと。

参考 wikipedia Webカメラ

下図のように、Android 端末を Web サーバーにして、Motion JPEG で配信する。
web_camera_system.png

Motion JPEG

JPEG画像を連続して送信することで、動画とする方式。

複数の画像を送信するための仕様である multipart 形式を使用している。

参考 Androidのカメラ映像をMotionJPEGで配信する

下記のソースコードを参考にした。
どちらも旧来のCamera APIを使用している。
これをベースに Camera2 APIを使用したものに変更する。

カメラから連続画像を取得する

まずは、カメラからの連続画像をキャプチャーするための ImageReader を生成する。

ImageReader の画像形式は、2つある。

(1)YUV 形式
イメージセンサのR(赤)G(緑)B(青)をY(輝度)U(輝度)V(差)に変換したもの。
高速なので、連続画像に向いている。
NV21形式のバイト列が取得できる。
JPEG 形式のバイト列へはアプリで変換する。

(2)JPEG 形式
上記のYUVをJPEGに圧縮したもの。
圧縮処理のため処理時間がかかるが、
ゆっくり処理すればよい静止画の保存に向いている。
JPEG形式のバイト列が取得できる。

今回は連続画像なので、YUV 形式が第一候補になるが、
欲しいのはJPEG画像のバイト列なので JPEG 形式も候補に残す。

両方試して、高速な方を選ぶ。
連続画像を取得する間隔を実測したところ、下記の結果になった。
JPEG が圧倒的に速かった。

YUV1800ms
JPEG 600ms

reference ImageReader

// JPEG 形式の ImageReaderを生成する
    ImageReader imageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, 4);
// リスナーを設定する
    imageReader.setOnImageAvailableListener(
                        imageAvailableListener, backgroundHandler);
// 出力先 に ImageReader の Surface を追加して
// キャプチャーを開始する
    List outputs = Arrays.asList(viewSurface, imageReaderSurface);
    cameraDevice.createCaptureSession(outputs, CameraCaptureSession.StateCallback, backgroundHandler);
// ターゲット に ImageReader の Surface を追加して
// キャプチャーを繰り返す
    CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    builder.addTarget(viewSurface);
    builder.addTarget(imageReaderSurface);
    CaptureRequest request = builder.build();
    captureSession.setRepeatingRequest(request, captureCallback, backgroundHandler);
// JPEG 形式のImageをバイト列に変換する
    ByteBuffer buffer = image.getPlanes()[0].getBuffer();
    int size = buffer.capacity();
    byte[] bytes = new byte[size];
    buffer.get(bytes);

Motion JPEG で配信する

ポート番号を指定して ServerSocket を生成する
クライアントからの接続を待ち受ける
接続されたらOutputStream を生成する
JPEG バイト列を連続して送信する

参考 SERVERSOCKETを使用してクライアントからデータを受信する

reference ServerSocket

referenceOutputStream

画像の大きさ

ImageReader の画像の大きさは、イメージセンサと同じ 3264x2448 となる。
このままでは、止まっているのかと思うほど遅い。

VGAサイズ(640x480)に縮小すると、スムーズに表示される

// バイト列を Bitmap に変換する
    Bitmap source = BitmapFactory.decodeByteArray(
    bytes, 0, bytes.length, null);
// Bitmapを 640x480 にリサイズする
    Bitmap bitmap = Bitmap.createScaledBitmap(
    source, 640, 480, true );

SSDP

参考にした CameraServe
SSDP (Simple Service Discovery Protocol) に対応している

SsdpAdvertiser.java をコンパイルするには、ライブラリを指定する。

app/build.gradle
useLibrary 'org.apache.http.legacy'

参考 SSDPとはを説明してみる

参考 HttpClientをtargetSdkVersion=28でも使う

SSDP の確認

パソコンから M-SEARCH コマンドを送信する。
pythonスクリプトはこちら
https://github.com/ohwada/Android_Samples/blob/master/tools/python/send_ssdp.py

下記の応答となる

HTTP/1.1 200 OK
Ext:
Cache-Control: max-age=120, no-cache="Ext"
ST: urn:android-exsample-com:service:camera-mjpeg:1
USN: urn:android-exsample-com:service:camera-mjpeg:1
Server: CameraMipegServer/1.0
X-Stream-Location: http://192.168.1.4:8080/

別の Android 端末から確認する時はこちらを
https://github.com/ohwada/Android_Samples/tree/master/SsdpClient

参考 M-SEARCH

参考 UPnPやらSSDPやらでいろいろ

サンプルコードをgithub に公開した。
https://github.com/ohwada/Android_Samples/tree/master/Camera219

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4