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

[Android の Camera2 API を使って カメラのプレビュー画面を表示する](https://qiita.com/ohwada/items/7bb65a566114066c8ced)
の続きです。

### Webカメラ

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

参考 [wikipedia Webカメラ](https://ja.wikipedia.org/wiki/Web%E3%82%AB%E3%83%A1%E3%83%A9)

下図のように、Android 端末を Web サーバーにして、Motion JPEG で配信する。
![web_camera_system.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64075/f46290aa-e9c6-38c7-aece-5b5863093b83.png)

### Motion JPEG

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

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

参考 [Androidのカメラ映像をMotionJPEGで配信する](
https://qiita.com/Yukio-Ichikawa/items/cf93c4851f871003a0f2)

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

-  [CameraServe](https://github.com/arktronic/cameraserve)

-  [broadcasttest](https://github.com/daddyYukio/broadcasttest)


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

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

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


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

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

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

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

[reference ImageReader](https://developer.android.com/reference/android/media/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を使用してクライアントからデータを受信する](
https://techbooster.org/android/9539/)

[reference ServerSocket]
(https://developer.android.com/reference/java/net/ServerSocket)

[referenceOutputStream](https://developer.android.com/reference/java/io/OutputStream)

### 画像の大きさ

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](https://github.com/arktronic/cameraserve) は
SSDP (Simple Service Discovery Protocol) に対応している

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


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

参考 [SSDPとはを説明してみる](http://bias.hateblo.jp/entry/20141130)

参考 [HttpClientをtargetSdkVersion=28でも使う](
https://qiita.com/takke/items/030af1054219e73531f6)

### 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](https://nazuna.sakura.ne.jp/wiki/index.php?title=M-SEARCH)

参考 [UPnPやらSSDPやらでいろいろ](http://telracsmoratori.blog.fc2.com/blog-entry-106.html)

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