Android開発 | Camera2を使ってプレビュー表示するための最低限のコード
2017/04/10
カメラを使ってAndroidアプリを作ろうと思い、なんとかプレビュー表示までできるようになったので最低限のコードと補足を記載します。
Camera2と参考情報について
Android5.0(API level 21)以降はCamera2というAPIを利用してアプリを開発することができるようです。カメラAPIについてはここにガイドラインがありますが、Camera2についてはリファレンスやgoogleのブログ、公式サンプルなどを参考に開発していく必要があります。
とりあえず今後はCamera2になっていくだろうということで、Camera2で開発をしてみました。
SurfaceViewでもCamera2でプレビューを表示できる
Camera2についていくつかサイトを調査していたのですが、Camera2ではSurfaceViewにプレビューを表示できないという記事がいくつかありました。が、そんなことはなくSurfaceViewにプレビュー表示できており、本記事のサンプルコードはSurfaceViewにプレビュー表示する内容になっています。
性能的にSurfaceViewはよろしくないという話もあったりしますが・・・
プレビューを表示する最低限のコード
Camera2はコールバックで処理が進んでいくような仕様になっています。以下のコードはアクティビティ起動時にカメラを起動してプレビューを表示する内容で、onCreate内のコードになっています。カメラの起動に成功または失敗したらOpenCameraCallbackと定義したコールバック関数が呼ばれるようになっています。
private CameraDevice backCameraDevice;
private CameraCaptureSession backCameraSession;
@Override
protected void onCreate(Bundle savedInstanceState) {
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
try {
//カメラIDを取得(背面カメラを選択)
String backCameraId;
for (String cameraId : manager.getCameraIdList()) {
CameraCharacteristics chars
= manager.getCameraCharacteristics(cameraId);
Integer facing = chars.get(CameraCharacteristics.LENS_FACING);
if (facing != null && facing ==
CameraCharacteristics.LENS_FACING_BACK) {
backCameraId = cameraId;
}
}
// SurfaceViewにプレビューサイズを設定する(サンプルなので適当な値です)
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
surfaceView.getHolder().setFixedSize(640, 320);
// カメラオープン(オープンに成功したときに第2引数のコールバッククラスが呼ばれる)
manager.openCamera(backCameraId, new OpenCameraCallback(), null);
} catch (CameraAccessException e) {
//例外処理を記述
}
}
//ホームボタンを押した際などにカメラを開放する(開放しないと裏で動き続ける・・・)
@Override
protected void onPause() {
super.onPause();
// カメラセッションの終了
if (backCameraSession != null) {
try {
backCameraSession.stopRepeating();
} catch (CameraAccessException e) {
Log.e(TAG, e.toString());
}
backCameraSession.close();
}
// カメラデバイスとの切断
if (backCameraDevice != null) {
backCameraDevice.close();
}
}
次にOpenCameraCallbackの定義になります。この中ではカメラのキャプチャを流すSurfaceリストを作り、そのリストを対象にキャプチャセッションを開始します。今回はプレビュー用のSurfaceViewだけをリストに登録していますが、実際にJPEG画像などに保存する際はリストにImageReaderも登録する必要があります。(今後の記事で記載予定)
キャプチャセッション開始時にコールバッククラスを引数に指定しますが、セッション開始後にこのコールバッククラスが呼ばれます。
/**
* カメラデバイス接続完了後のコールバッククラス
*/
private class OpenCameraCallback extends CameraDevice.StateCallback {
@Override
public void onOpened(CameraDevice cameraDevice) {
backCameraDevice = cameraDevice;
// プレビュー用のSurfaceViewをリストに登録
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
ArrayList<Surface> surfaceList = new ArrayList();
surfaceList.add(surfaceView.getHolder().getSurface());
try {
// プレビューリクエストの設定(SurfaceViewをターゲットに)
mPreviewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surfaceView.getHolder().getSurface());
// キャプチャーセッションの開始(セッション開始後に第2引数のコールバッククラスが呼ばれる)
cameraDevice.createCaptureSession(surfaceList, new CameraCaptureSessionCallback(), null);
} catch (CameraAccessException e) {
// エラー時の処理を記載
}
}
@Override
public void onDisconnected(CameraDevice cameraDevice) {
// 切断時の処理を記載
}
@Override
public void onError(CameraDevice cameraDevice, int error) {
// エラー時の処理を記載
}
}
次にCameraCaptureSessionCallbackのコールバッククラスの定義になります。ここでは、プレビュー時のAF設定等を任意で行い最後にsetRepeatingRequestを呼び出してプレビューを開始します。この関数の第2引数に撮影時のコールバッククラスを指定しますが、とりあえずプレビューを出すまでですのでコールバッククラスの中身は空にしています。
/**
* カメラが起動し使える状態になったら呼ばれるコールバック
*/
private class CameraCaptureSessionCallback extends CameraCaptureSession.StateCallback {
@Override
public void onConfigured(CameraCaptureSession session) {
backCameraSession = session;
try {
// オートフォーカスの設定
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CameraMetadata.CONTROL_AF_TRIGGER_START);
// プレビューの開始(撮影時に第2引数のコールバッククラスが呼ばれる)
session.setRepeatingRequest(mPreviewRequestBuilder.build(), new CaptureCallback(), null);
} catch (CameraAccessException e) {
Log.e(TAG, e.toString());
}
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
//失敗時の処理を記載
}
}
/**
* カメラ撮影時に呼ばれるコールバック関数
*/
private class CaptureCallback extends CameraCaptureSession.CaptureCallback {
}
長くなりましたが、これでSurfaceViewにカメラプレビューが表示できました。次は撮影まで実装できたら続きを書こうと思います。