Android の Camera2 API を使って カメラのプレビュー画面を表示する
の続きです。
Webカメラ
Webインターフェイスを持つリアルタイムカメラのこと。
下図のように、Android 端末を Web サーバーにして、Motion JPEG で配信する。
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
// 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]
(https://developer.android.com/reference/java/net/ServerSocket)
画像の大きさ
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 をコンパイルするには、ライブラリを指定する。
useLibrary 'org.apache.http.legacy'
参考 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
サンプルコードをgithub に公開した。
https://github.com/ohwada/Android_Samples/tree/master/Camera219