Unityでカメラ（WebCamTexture）を使ってみた

対象はiOSとAndroid。
わりとネイティブな領域の対応が必要だった。

## すること
### 必須
- 権限付与
- 回転制御

### 任意
- シャッター音
- カメラフラッシュ的な何か

## 必要な権限
### iOS
PBXProjectの`NSCameraUsageDescription`に何か書く。

### Android
`CAMERA`。
`Assets/Plugins/Android/AndroidManifest.xml`に`<uses-permission android:name="android.permission.CAMERA" />`を書き足す。

## ユーザーへの権限問合せ
カメラ起動のタイミングではなく事前に権限を確認する。

### iOS
iOSのネイティブAPIを用いて、`AVCaptureDevice authorizationStatusForMediaType:`を利用して現状を確認、未確認の場合は`AVCaptureDevice requestAccessForMediaType: completionHandler:`を用いてユーザーに権限を確認する必要がある。

```objc
extern "C" bool _CameraPlugin_RequestPermission()
{
    AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType: AVMediaTypeVideo];

    switch (status) {
        case AVAuthorizationStatusAuthorized:
            return true;
        case AVAuthorizationStatusNotDetermined:
            {
                __block BOOL granted = NO;
                
                dispatch_semaphore_t sema = dispatch_semaphore_create(0);
                [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL _granted) {
                    granted = _granted;
                    dispatch_semaphore_signal(sema);
                }];
                dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
                
                return granted;
            }
        default:
            return false;
    }
}
```

### Android
多くのアプリでは、アプリ起動時に`AndroidManifest.xml`に記載しておいて、起動時にすべて許可しないとアプリを終了といった設計になっている事が多いが、稀に権限付与のタイミングを遅らせたい時がある。

その場合はAndroidのAPIで`requestPermissions`する。
戻りは`UnityPlayerActivity.onRequestPermissionsResult`で受け取って、`UnityPlayer.UnitySendMessage`でUnity側に戻す。

という諸々を対応してくれるUnityプラグインがあるので、基本的にはそれを使う。
https://github.com/sanukin39/UniAndroidPermission

## カメラの起動
Unityの機能が使える。

```csharp
currentWebcamTexture = new WebCamTexture(WebCamTexture.devices[0].name, Screen.width, Screen.height, (new AppSetting()).FrameRate);
screenTexture.mainTexture = currentWebcamTexture;
currentWebcamTexture.Play();
```
`WebCamTexture.devices`にリアカメラ、フロントカメラ等が配列で入っているので、これのインデックスを変更することでカメラ切り替えができる。

## カメラの回転
通常は端末の回転に合わせてカメラも回転させる必要がある。

```csharp
void Awake()
{
    baseRotation = screenTexture.transform.rotation;
}

void Update()
{
    if (currentWebcamTexture != null)
    {
        screenTexture.transform.rotation = baseRotation * Quaternion.AngleAxis(currentWebcamTexture.videoRotationAngle, Vector3.forward);
    }
}
```

## キャプチャ処理
`Texture2D`に入れてからバイト列で持っておく。
その後で色々するのにこのやり方が便利だった。

```csharp
var width = Screen.width;
var height = Screen.height;
var tex = new Texture2D(width, height, TextureFormat.RGB24, false);

yield return new WaitForEndOfFrame();

tex.ReadPixels(new Rect(0, 0, width, height), 0, 0);

tex.Apply();
pngBytes = tex.EncodeToPNG();
```

`ScreenCapture.CaptureScreenshot`という一見便利そうなメソッドもあるが、ファイル保存固定だったり、保存先ディレクトリが固定されていたり、終了がフックできなかったりとわりと手間がかかる。

## シャッター音
端末やOSによって異なるのと、ボリュームゼロ時に無音はダメなので、ネイティブのメソッドを呼び出す。

### iOS

```objc
AudioServicesPlaySystemSound(1108);
```

### Android

```java
var mediaActionSound = new AndroidJavaObject("android.media.MediaActionSound");
mediaActionSound.Call("play", mediaActionSound.GetStatic<int>("SHUTTER_CLICK"));
```

## カメラフラッシュ的なもの
無いのでそれっぽいものを自前で実装する。
なお例示は無い。

## カメラの停止
カメラの利用を停止する場合は、Stopする。

```csharp
currentWebcamTexture.Stop();
```