Slide 1

Slide 1 text

DroidKaigi 2016 カメラアプリはじめの一歩 @mhidaka #DroidKaigi #DroidKaigiC 2/19/2016 1

Slide 2

Slide 2 text

Camera2 API Overview 2/19/2016 2

Slide 3

Slide 3 text

Camera2 API What’s new? Android 5.0 から追加された新しいパッケージ • android.hardware.camera2 Camera API を置き換える高性能なカメラ制御が特徴 • リクエストやセッションといった新しい概念 2/19/2016 3

Slide 4

Slide 4 text

カメラアプリはじめの一歩 セッションの対象者 • Camera2 APIに触れたことがないひと • Camera1 APIとの違いを知りたいひと セッションのゴール • Camera2 APIの概念を理解する • サンプルコードを独力で読み、試行錯誤をしながら変更できる 2/19/2016 4

Slide 5

Slide 5 text

自己紹介 @mhidaka/日高 正博 エンジニア(組込) Android開発は 7年目 TechBooster 本を書いたり、技術同人誌を発行したり techbooster.org 2/19/2016 5

Slide 6

Slide 6 text

Target Platform Versions Camera 2 API Covered : 35% https://developer.android.com/intl/ja/about/dashboards/index.html 2/19/2016 6

Slide 7

Slide 7 text

Camera Use case Androidにおけるカメラは次の組み合わせ • Point & Shoot • DSLR • Effects • Innovations 2/19/2016 7

Slide 8

Slide 8 text

FAQ Camera1 APIに価値はなくなったのか? • No, 利用価値はまだまだ高い • 枯れたAPI&たくさんのバッドノウハウ • This class was deprecated. Camera2 APIは時期尚早か? • No, Camera1 APIではできないことが可能に • モダンなAPI, 高い拡張性 2/19/2016 8

Slide 9

Slide 9 text

If You Build Camera App アプリの一部、Optionとして開発する場合 • 工数はあまり消費したくない • 主機能は別にある • Camera2 APIは不要 • Intentによるカメラアプリ連携 • Google Play Services 7.8 – Mobile Vision API (QRコード) • それでも足りなければCamera1 APIを。 https://github.com/googlesamples/android-vision 2/19/2016 9

Slide 10

Slide 10 text

If You Build Camera App アプリの主機能として開発する場合 • 一体化したユーザー体験を作りたい • 将来的な拡張を視野に入れたい • Camera2 APIを利用できる • Innovationsな分野であれば適用可能 • 1年後にはAndroid 5.x以上が70%に。 • つまり今日の知識は今後数年生き続ける 2/19/2016 10

Slide 11

Slide 11 text

Camera2 API Workflow 2/19/2016 11

Slide 12

Slide 12 text

Requestによる非同期モデル モードレス/パイプライン処理 Request Request Queue Camera Device Image Buffer Surface パイプライン 2/19/2016 12

Slide 13

Slide 13 text

Requestによる非同期モデル ストリームを隠蔽したパイプライン処理により複数のリクエスト 実行が可能に Request Request Queue Camera Device Image Buffer Surface Request Request Request Result Result Result Surface Surface Surface パイプライン 2/19/2016 13

Slide 14

Slide 14 text

RequestとSession Camera Deviceに対してキャプチャリクエストとセッションを発 行。Requestが操作、Sessionが操作の反映を担当 Request Session Camera Device リクエスト:操作 カメラパラメータの設定 セッション:実行単位 複数のリクエストを受けることで 同時処理が可能 カメラデバイス:処理 結果はSurfaceへ 2/19/2016 14

Slide 15

Slide 15 text

RequestとSession カメラプレビューの場合 • ※プレビュー中の静止画撮影は一旦プレビューをとめて実行する (カメラデバイスは1動作しかできない) Request Session Camera Device リクエスト:操作 カメラプレビューを指定 セッション:実行単位 常時プレビューを行うため リクエストをRepeat実行 カメラデバイス:処理 結果はTextureViewへ 2/19/2016 15

Slide 16

Slide 16 text

Camera2 Advantage 出力のSurface、ImageBufferの設定次第で自由な表現 • プレビュー表示:TextureView • 録画 :MediaRecorder • エンコードのみ:MediaCodec • YUV処理 :RenderScriptAllocation 2/19/2016 16

Slide 17

Slide 17 text

基本的な処理シーケンス 1. カメラデバイスを準備する 2. 準備の完了通知を受け取る 3. キャプチャリクエストを準備する 4. キャプチャセッションを準備する 5. セッションの状態通知を受け取る 6. キャプチャリクエストを発行する 7. カメラデバイスの利用を終了する 2/19/2016 17

Slide 18

Slide 18 text

How to Use 2/19/2016 18

Slide 19

Slide 19 text

Camera2 Sample いま公開したよ!! https://github.com/mhidaka/Camera2App/ 2/19/2016 19

Slide 20

Slide 20 text

Camera 2 API Classes • CameraManager カメラのシステムサービス • CameraDevice カメラ制御 • CaptureRequest 画像の取得リクエスト • CameraCaptureSession 画像を取得・処理するセッション 2/19/2016 20

Slide 21

Slide 21 text

基本的な処理シーケンス 1. カメラデバイスを準備する CameraManager#openCamera 2. 準備の完了通知を受け取る CameraDevice.StateCallback 3. キャプチャリクエストを準備する CaptureRequest.Builder 4. キャプチャセッションを準備する CameraDevicecreateCaptureSession 5. セッションの状態通知を受け取る CameraCaptureSession.StateCallback 6. キャプチャリクエストを発行する CameraCaptureSession#capture 7. カメラデバイスの利用を終了する CameraDevice#close 2/19/2016 21

Slide 22

Slide 22 text

1.カメラデバイスを準備する 動作:TextureViewにプレビューを表示する CameraManager CameraManagerの主なメソッド 説明 open カメラの利用開始 getCameraIdList カメラのリストを取得(id) getCameraCharacteristics 向きなどのパラメータ Close カメラの利用終了 2/19/2016 22

Slide 23

Slide 23 text

private void openCamera(int width, int height) { if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { requestCameraPermission(); return; } String cameraId = setUpCameraOutputs(width, height); CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { if (!mCamera.isLocked()) { throw new RuntimeException( "Time out waiting to lock camera opening."); } manager.openCamera(cameraId, mCamera.stateCallback, mThread.getHandler()); } …error handling… } 2/19/2016 23

Slide 24

Slide 24 text

Viewに依存する処理 • 表示サイズに依存してカメラ出力のアスペクト比を計算 • 表示サイズは画面サイズに依存 • カメラの出力サイズがデバイスごと固有 • 画面回転、再描画時でリサイズが必要 • TextureViewへの描画はImageBuffer経由で実施 • Camera2ではストリームとして画像を扱う(ファイルではない) • YUV表色系にするとやたら高速(BurstCaptureで顕著) 2/19/2016 24

Slide 25

Slide 25 text

2.カメラデバイスの状態変化を受け取る 非同期でカメラデバイスへの接続を検出する CameraDevise.StateCallbackインターフェイス StateCallbackインターフェイス 説明 onOpend カメラデバイスと接続が完了 onDisconnected カメラデバイスから切断された onError 回復不能な状況 2/19/2016 25

Slide 26

Slide 26 text

public class BasicCamera { private Semaphore mCameraOpenCloseLock = new Semaphore(1); private CameraDevice mCameraDevice; ...省略... public final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice cameraDevice) { // カメラが利用可能状態になった mCameraOpenCloseLock.release(); mCameraDevice = cameraDevice; createCameraPreviewSession(); } 2/19/2016 26

Slide 27

Slide 27 text

@Override public void onDisconnected(CameraDevice cameraDevice) { mCameraOpenCloseLock.release(); cameraDevice.close(); mCameraDevice = null; } @Override public void onError(CameraDevice cameraDevice, int error) { mCameraOpenCloseLock.release(); cameraDevice.close(); mCameraDevice = null; Log.e(TAG, "Camera StateCallback onError: Please Reboot Android OS"); } }; 2/19/2016 27

Slide 28

Slide 28 text

3,4,5リクエストとセッションを使う Request Session Camera Device CameraSessionに関する主なメソッド 説明 CameraDevice.createCaptureSession CaptureSessionの生成 CameraCaptureSession.setRepeatingRequest リクエストの繰り返し実行(プレビューなど) CameraCaptureSession.capture リクエストの実行 CameraRequestに関する主なメソッド 説明 CameraDevice.createCaptureRequest CaptureRequestBuilderの取得 CaptureRequest.Build.set AFなど撮影内容の設定 CaptureRequest.Build.build CaptureRequestの生成 2/19/2016 28

Slide 29

Slide 29 text

mCameraDevice.createCaptureSession(Arrays.asList(surface, imageRenderSurface), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession cameraCaptureSession) { // プレビュー準備が完了したのでカメラのAF,AE制御を指定する mCaptureSession = cameraCaptureSession; try { // プレビューがぼやけては困るのでオートフォーカスを利用する mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 露光、フラッシュは自動モードを使用する mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); 2/19/2016 29

Slide 30

Slide 30 text

// カメラプレビューを開始する(ここでは開始要求のみ) mPreviewRequest = mPreviewRequestBuilder.build(); mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mInterface.getBackgroundHandler()); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { Log.e(TAG, "CameraCaptureSession onConfigureFailed"); } }, null ); 2/19/2016 30

Slide 31

Slide 31 text

なぜこんなに非同期処理が多いのか • つらすぎる • ハードウェアデバイスという特性 • 入力に対して応答までの時差がかならずある • CameraManager#openCameraは500ms単位での処理(高コスト) • パイプラインの特性 • 複数リクエストをパイプラインで処理するには独立性を上げる • ResultはリクエストのQueuing時にはわからないので(仕方ない) 2/19/2016 31

Slide 32

Slide 32 text

6.キャプチャリクエストを発行する 静止画を撮影する CameraCaptureSession.StateCallback セッションの状態通知 CameraCaptureSession.CaptureCallback セッションでの処理通知 Camera Device Image Buffer File Save 2/19/2016 32

Slide 33

Slide 33 text

CameraCaptureSession.CaptureCallback CaptureCallback 説明 onCaptureProgressed セッションのリクエスト処理結果 onCaptureCompleted キャプチャ(撮影)の完了 onCaptureProgressedメソッドは、リクエストの内容に応じて、 複数回呼び出されることがある点に注意 (CaptureRequest.Build.setをパラメータごと複数回指定できる から) 2/19/2016 33

Slide 34

Slide 34 text

private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult) { // キャプチャの進捗状況(随時呼び出される) process(partialResult); } @Override public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { // キャプチャの完了(プレビューの場合、プレビュー状態が継続) process(result); } } 2/19/2016 34

Slide 35

Slide 35 text

カメラで撮影するということ 日本語的表現 「プレビュー中に写真撮影ボタンを押したので、オートフォーカ スを合わせる。フォーカスが合ったときに写真の明るさ(露光) がちょうどよいところで静止画を取得して撮影が完了したら画面 をプレビュー中にもどす、そのときにフォーカスロックを解除す ることを忘れない」 を「撮影する」の一言で表現できる日本語がやばい。 2/19/2016 35

Slide 36

Slide 36 text

プログラム的表現 2/19/2016 36

Slide 37

Slide 37 text

日本語的表現 「プレビュー中に写真撮影ボタンを押したので、オートフォーカ スを合わせる。フォーカスが合ったときに写真の明るさ(露光) がちょうどよいところで静止画を取得して撮影が完了したら画面 をプレビュー中にもどす、そのときにフォーカスロックを解除す ることを忘れない」 2/19/2016 37

Slide 38

Slide 38 text

private void process(CaptureResult result) { switch (mState) { case STATE_PREVIEW: { // プレビュー中は何もしない break; } case STATE_WAITING_LOCK: { // 焦点が合った時に静止画を撮影する(AF) Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); if (afState == null) { captureStillPicture(); } else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState || CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) { // CONTROL_AE_STATE がnullのデバイスがある Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) { mState = STATE_PICTURE_TAKEN; captureStillPicture(); } ...省略... 2/19/2016 38

Slide 39

Slide 39 text

private void captureStillPicture() { try { // 静止画の撮影を開始する final CaptureRequest.Builder captureBuilder = mCameraDevice. createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureBuilder.addTarget(mInterface.getImageRenderSurface()); // 静止画の撮影モードを指定(AF,AE) captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); // 現在のカメラの向きを指定する(0~270度) int rotation = mInterface.getRotation(); captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation)); 2/19/2016 39

Slide 40

Slide 40 text

カメラの撮影条件 カメラには、どうしても「オートフォーカスがあわない」など苦 手な対象物がある。ピントのずれた画像がとれたときは悲しいが、 撮影できないという状況が一番体験を損なう そのため、どんな状況でも撮影できる(とりあえず撮れる)こと がカメラとして使うには重要となる。 2/19/2016 40

Slide 41

Slide 41 text

日本語的表現 「プレビュー中に写真撮影ボタンを押したので、オートフォーカ スを合わせる。フォーカスが合ったときに写真の明るさ(露光) がちょうどよいところで静止画を取得して撮影が完了したら画面 をプレビュー中にもどす、そのときにフォーカスロックを解除す ることを忘れない」 2/19/2016 41

Slide 42

Slide 42 text

private void captureStillPicture() { try { ...前のリストから継続... CameraCaptureSession.CaptureCallback CaptureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { // 静止画撮影が完了した時に呼ばれるコールバック Log.e(TAG, "onCaptureCompleted Picture Saved"); // プレビュー用の設定に戻す unlockFocus(); } }; mCaptureSession.stopRepeating(); // プレビューを一旦停止する // 静止画を撮影する(captureBuilder) mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null); } 2/19/2016 42

Slide 43

Slide 43 text

private void unlockFocus() { try { // AFのロックを解除する(トリガーをキャンセルする) mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); // AFトリガーのキャンセルを実行する mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mInterface.getBackgroundHandler()); // プレビューを継続するためsetRepeatingRequestメソッドを実行する mState = STATE_PREVIEW; mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mInterface.getBackgroundHandler()); } catch (CameraAccessException e) { e.printStackTrace(); } } 2/19/2016 43

Slide 44

Slide 44 text

Camera2 API、複雑すぎませんか 間違いなく難しい。が、自由度は著しく向上している。カメラのス テートマシンはアプリの機能要件に依存が強く、性質が特異。きちん と使いこなすにはカメラの知識が必要 アプリで必要な満たすために、カメラ制御(非同期処理)とステート マシンを密結合するケースが多くなり、複雑度が加速度的に増す。 これは設計で回避できる問題でもある(解説でつかったサンプルでは ステートマシンとCamera2 APIの実装を別クラスに分けている。 GoogleのCamera2Basicは密結合している) 2/19/2016 44

Slide 45

Slide 45 text

Abyss of Camera2 2/19/2016 45

Slide 46

Slide 46 text

https://source.android.com/devices/camera/index.html アプリケーション層 ハードウェア層 レイヤ同士はモジュール ごとにインターフェイス を使い、疎結合 2/19/2016 46

Slide 47

Slide 47 text

Camera HAL3 Overview https://source.android.com/devices/camera/camera3.html METADATAを制御できる ことがCamera2の意義 METADATA:色空間や輝度などカメラのパラメータ 2/19/2016 47

Slide 48

Slide 48 text

HAL subsystem ハードウェアを抽象化 ・レンズ、センサー フラッシュ ・動作モード ・YUV変換 https://source.android.com/devices/camera/camera3_requests_hal.html 2/19/2016 48

Slide 49

Slide 49 text

Camera HAL3 Overview 内部ではRequestをみて 撮影フレームごとに 設定値を変更、処理に備える Captureのタイミングで キューイング ハードウェア処理 Requestをパイプライン (Aの準備をしつつBを実行) 処理できる Requestごとの設定 Surfaceへ書き出し 2/19/2016 49

Slide 50

Slide 50 text

Tips • 3A Modes • AF,AE,AWの設定のこと • カメラデバイス起動時はオートフォーカス、自動露光、ホワイトバラ ンスいずれも”STATE_INACTIVE” • CTS requirements • Android 5.0 and later devices must pass both Camera API1 CTS and Camera API2 CTS. And as always, devices are required to pass the CTS Verifier camera tests. 2/19/2016 50

Slide 51

Slide 51 text

まとめ 2/19/2016 51

Slide 52

Slide 52 text

まとめ • Camera2 APIは今後の主流 • 多くの開発者はカメラの中身を作りたいわけではなく、カメラをつ かったあたらしい体験をつくりたい • カンタンを追求していくとCamera1 APIの形に落ち着く • 手間のかかる部分をサンプルとしてまとめて作って公開、多分誰かがよい抽 象化実装を作ると流行りだす • Camera2 API はCamera1 APIのrebase、rebuild • 近年のAndroid API(MediaCodec、BLE、NFCなど)は内部構造に深くアク セスできるようになっており、その流れの中では自然 • カメラという言葉には人を引きつける沼っぽさがある 2/19/2016 52

Slide 53

Slide 53 text

@mhidaka [email protected] Question ? / Thank You ! 2/19/2016 53