Camera API

Android フレームワークでは、デバイスで使用できる各種カメラ / カメラ機能がサポートされるため、アプリ内で写真や動画をキャプチャできます。このドキュメントでは、写真や動画のキャプチャで利用できる簡単でシンプルな方法について説明します。また、ユーザー向けのカスタムのカメラ エクスペリエンスを作成するための高度なアプローチについても概説します。

注: このページでは、サポートが終了した Camera クラスについて説明しています。CameraX Jetpack ライブラリの使用、または camera2 クラスの使用(特定のユースケースの場合)をおすすめします。CameraX と Camera2 はどちらも Android 5.0(API レベル 21)以上で動作します。

以下の関連リソースもご覧ください。

留意事項

アプリで Android デバイスのカメラを使用できるようにするには、アプリがこのハードウェア機能をどのように使用するかに関して、いくつか項目を検討する必要があります。

  • カメラの要件 - アプリでのカメラの使用がきわめて重要で、カメラを搭載していないデバイスにアプリがインストールされることを希望しない場合は、マニフェストでカメラの要件を宣言する必要があります。
  • 単に写真を撮影するのか、カメラをカスタマイズするのか - アプリでカメラをどのように使用しますか?単に写真や動画クリップを撮影することに関心がありますか?あるいは、カメラの新しい使い方をするアプリを提供しようとしていますか?単に写真や動画クリップを撮影するのであれば、既存のカメラアプリを使用することを検討してください。カスタムのカメラ機能を開発するのであれば、カメラアプリの作成 セクションをご覧ください。
  • フォアグラウンド サービスの要件 - アプリはどのタイミングでカメラと連携しますか?Android 9(API レベル 28)以降の場合、バックグラウンドで実行中のアプリはカメラにアクセスできません。そのため、アプリがフォアグラウンドにあるか、フォアグラウンド サービスの一部であるタイミングでのみ、カメラを使用する必要があります。
  • ストレージ - アプリで生成される画像や動画を、そのアプリでのみ表示しますか?またはギャラリーなどの他のアプリや、他のメディアおよびソーシャル アプリで使用できるように共有しますか?アプリをアンインストールした後でも、写真や動画を引き続き利用できるようにしますか?これらのオプションを実装する方法については、メディア ファイルの保存セクションを確認してください。

基本情報

Android フレームワークは、android.hardware.camera2 API またはカメラ Intent による写真や動画のキャプチャをサポートしています。関連するクラスは次のとおりです。

android.hardware.camera2
このパッケージは、デバイスのカメラを制御するためのメインとなる API です。開発するカメラアプリでこの API を使用して、写真や動画を撮影できます。
Camera
このクラスは、デバイスのカメラを制御するための、サポートが終了した古い API です。
SurfaceView
このクラスは、カメラのライブ プレビューをユーザーに表示するために使用されます。
MediaRecorder
このクラスは、カメラから動画を撮影するために使用します。
Intent
インテントのアクション タイプ MediaStore.ACTION_IMAGE_CAPTURE または MediaStore.ACTION_VIDEO_CAPTURE を使用すると、Camera オブジェクトを直接使用せずに画像や動画をキャプチャできます。

マニフェストの宣言

Camera API を使用してアプリの開発を開始する前に、カメラ ハードウェアやその他の関連機能の使用を許可する適切な宣言がマニフェストに含まれていることを確認する必要があります。

  • カメラの権限 - アプリでデバイスのカメラを使用するための権限をリクエストする必要があります。
    <uses-permission android:name="android.permission.CAMERA" />
    

    注: 既存のカメラアプリを起動してカメラを使用する場合、アプリでこの権限をリクエストする必要はありません。

  • カメラ機能 - アプリでカメラ機能の使用も宣言する必要があります。次に例を示します。
    <uses-feature android:name="android.hardware.camera" />
    

    カメラ機能の一覧については、マニフェストの機能のリファレンスをご覧ください。

    マニフェストにカメラ機能を追加すると、カメラを搭載しないデバイスや、指定されたカメラ機能をサポートしないデバイスにアプリが Google Play 経由でインストールされなくなります。Google Play で機能ベースのフィルタリングを使用する方法については、Google Play と機能ベースのフィルタリングをご確認ください。

    アプリで適切な操作のためにカメラまたはカメラ機能を使用できるものの、必須ではない場合、マニフェストで android:required 属性を追加して false に設定することで指定する必要があります。

    <uses-feature android:name="android.hardware.camera" android:required="false" />
    
  • ストレージ権限 - Android 10(API レベル 29)以前をターゲットとし、マニフェストで以下を指定している場合、アプリは写真や動画をデバイスの外部ストレージ(SD カード)に保存できます。
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
  • 録音の権限 - 動画キャプチャで録音するには、アプリで音声キャプチャの権限をリクエストする必要があります。
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    
  • 位置情報の利用許可 - アプリで写真に GPS 位置情報をタグ付けするには、ACCESS_FINE_LOCATION 権限をリクエストする必要があります。Android 5.0(API レベル 21)以降をターゲットとしているアプリの場合、アプリでデバイスの GPS を使用することも宣言する必要があります。

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    ...
    <!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
    <uses-feature android:name="android.hardware.location.gps" />
    

    ユーザーの位置情報の取得について詳しくは、位置情報に関する戦略をご覧ください。

既存のカメラアプリの使用

アプリで多くの追加コードを作成せずに写真や動画を撮影できるようにする簡単な方法は、Intent を使用して既存の Android カメラアプリを呼び出します。詳細については、トレーニング レッスンの簡単な写真の撮影簡単な動画の撮影をご覧ください。

カメラアプリの作成

デベロッパーには、アプリのデザインに合わせてカスタマイズされたカメラ ユーザー インターフェースや、特別な機能を提供するカメラ ユーザー インタフェースが必要となる場合もあるでしょう。独自の写真撮影コードを記述すれば、ユーザーにより魅力的なエクスペリエンスを提供できます。

注: 次のガイドは、古い、サポートを終了した Camera API を対象としています。新しいカメラや高度なカメラアプリの場合は、新しい android.hardware.camera2 API を使用することをおすすめします。

アプリ用にカスタムのカメラ インターフェースを作成する一般的手順は次のとおりです。

  • カメラの検出とアクセス - カメラが搭載されていることを確認し、アクセス権をリクエストするコードを作成します。
  • Preview クラスの作成 - SurfaceView を拡張して SurfaceHolder インターフェースを実装するカメラの Preview クラスを作成します。このクラスは、カメラから取得されるライブ画像をプレビューします。
  • プレビュー レイアウトの作成 - カメラの Preview クラスを準備したら、プレビューと必要なユーザー インターフェース コントロールが組み込まれたビュー レイアウトを作成します。
  • キャプチャ用のリスナーのセットアップ - ボタンの押下などユーザー アクションに反応して写真や動画のキャプチャを開始できるように、インターフェース コントロールのリスナーを接続します。
  • キャプチャしてファイルを保存 - 写真や動画をキャプチャし、出力を保存するためのコードをセットアップします。
  • カメラを解放する - カメラの使用後、アプリがカメラを適切に解放して、他のアプリで使用できるようにする必要があります。

カメラ ハードウェアは共有リソースです。アプリが、カメラを使用する他のアプリと競合しないように慎重に管理する必要があります。以降のセクションでは、カメラ ハードウェアの検出方法、カメラへのアクセス権のリクエスト方法、写真や動画のキャプチャ方法、アプリでのカメラの使用後にカメラを解放する方法について説明します。

注意: アプリで Camera オブジェクトを使用した後、Camera.release() を呼び出してこのオブジェクトを解放してください。アプリでカメラが適切に解放されない場合、自身のアプリによるものを含め、以降のすべてのカメラへのアクセスの試行が失敗し、自身のアプリや他のアプリがシャットダウンする可能性があります。

カメラ ハードウェアの検出

マニフェスト宣言を使用してアプリがカメラを必要としていることを指定していない場合は、実行時にカメラが使用可能かどうかを確認する必要があります。この確認を実行するには、下記のサンプルコードに示すように、PackageManager.hasSystemFeature() メソッドを使用します。

Kotlin

/** Check if this device has a camera */
private fun checkCameraHardware(context: Context): Boolean {
    if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
        // this device has a camera
        return true
    } else {
        // no camera on this device
        return false
    }
}

Java

/** Check if this device has a camera */
private boolean checkCameraHardware(Context context) {
    if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
        // this device has a camera
        return true;
    } else {
        // no camera on this device
        return false;
    }
}

Android デバイスの中には、写真撮影用の背面カメラとビデオ通話用の前面カメラなど、複数のカメラを搭載しているものがあります。Android 2.3(API レベル 9)以降では、Camera.getNumberOfCameras() メソッドを使用することでデバイスで使用できるカメラの数を確認できます。

カメラへのアクセス

アプリを実行しているデバイスにカメラが搭載されていることを確認した場合は、Camera のインスタンスを取得することでカメラへのアクセス権をリクエストする必要があります(カメラにアクセスするインテントを使用している場合を除く)。

メインのカメラにアクセスするには、Camera.open() メソッドを使用して、次のコードに示すように、例外をキャッチできるようにします。

Kotlin

/** A safe way to get an instance of the Camera object. */
fun getCameraInstance(): Camera? {
    return try {
        Camera.open() // attempt to get a Camera instance
    } catch (e: Exception) {
        // Camera is not available (in use or does not exist)
        null // returns null if camera is unavailable
    }
}

Java

/** A safe way to get an instance of the Camera object. */
public static Camera getCameraInstance(){
    Camera c = null;
    try {
        c = Camera.open(); // attempt to get a Camera instance
    }
    catch (Exception e){
        // Camera is not available (in use or does not exist)
    }
    return c; // returns null if camera is unavailable
}

注意: Camera.open() を使用する場合、必ず例外を確認してください。カメラが使用中、またはカメラが搭載されていない場合に例外の確認を行わないと、アプリがシステムによりシャットダウンされます。

Android 2.3(API レベル 9)以降のデバイスでは、Camera.open(int) を使用して特定のカメラにアクセスできます。上記のサンプルコードは、複数のカメラを搭載するデバイスの 1 番目のカメラ(背面カメラ)にアクセスします。

カメラ機能の確認

カメラへのアクセス権を取得したら、Camera.getParameters() メソッドを使用し、返された Camera.Parameters からサポートされている機能を調べることで、その機能に関する詳細情報を取得できます。API レベル 9 以降を使用している場合、Camera.getCameraInfo() を使用して、カメラがデバイスの前面にあるか、背面にあるか、および写真の向きを確認します。

Preview クラスの作成

写真や動画を効果的に撮影できるようにするためには、デバイスのカメラで見える範囲が表示されるようにする必要があります。カメラの Preview クラスは、カメラから取得されるライブ画像データを表示できる SurfaceView です。ユーザーは、写真や動画をフレームに収めてからキャプチャできるよになります。

次のサンプルコードは、View レイアウトに組み込める基本的なカメラの Preview クラスを作成する方法を示しています。このクラスは、ビューの作成と破棄のコールバック イベントをキャプチャするために SurfaceHolder.Callback を実装しています。コールバック イベントは、カメラ プレビュー入力を割り当てるために必要です。

Kotlin

/** A basic Camera preview class */
class CameraPreview(
        context: Context,
        private val mCamera: Camera
) : SurfaceView(context), SurfaceHolder.Callback {

    private val mHolder: SurfaceHolder = holder.apply {
        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        addCallback(this@CameraPreview)
        // deprecated setting, but required on Android versions prior to 3.0
        setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS)
    }

    override fun surfaceCreated(holder: SurfaceHolder) {
        // The Surface has been created, now tell the camera where to draw the preview.
        mCamera.apply {
            try {
                setPreviewDisplay(holder)
                startPreview()
            } catch (e: IOException) {
                Log.d(TAG, "Error setting camera preview: ${e.message}")
            }
        }
    }

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }

    override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) {
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.
        if (mHolder.surface == null) {
            // preview surface does not exist
            return
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview()
        } catch (e: Exception) {
            // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here

        // start preview with new settings
        mCamera.apply {
            try {
                setPreviewDisplay(mHolder)
                startPreview()
            } catch (e: Exception) {
                Log.d(TAG, "Error starting camera preview: ${e.message}")
            }
        }
    }
}

Java

/** A basic Camera preview class */
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder mHolder;
    private Camera mCamera;

    public CameraPreview(Context context, Camera camera) {
        super(context);
        mCamera = camera;

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, now tell the camera where to draw the preview.
        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.

        if (mHolder.getSurface() == null){
          // preview surface does not exist
          return;
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview();
        } catch (Exception e){
          // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here

        // start preview with new settings
        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();

        } catch (Exception e){
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }
}

特定のサイズでカメラ プレビューを設定する場合は、上記のコメントに記載されているように surfaceChanged() メソッド内で設定してください。プレビュー サイズを設定する場合は、getSupportedPreviewSizes() の値を使用する必要があります。setPreviewSize() メソッドで値を設定しないでください

注: Android 7.0(API レベル 24)以降では、マルチウィンドウ機能が導入されています。これに伴い、プレビューのアスペクト比が setDisplayOrientation() を呼び出した後のアクティビティと同じにならない場合があります。ウィンドウのサイズとアスペクト比によっては、ワイドカメラ プレビューを縦向きレイアウトに合わせたり、あるいはレターボックス レイアウトを使用してその逆にする必要があります。

レイアウト内にプレビューを配置する

カメラの Preview クラスは、前のセクションで説明した例のように、アクティビティのレイアウトや、写真や動画の撮影に関連する他のユーザー インターフェース コントロールに配置する必要があります。このセクションでは、プレビュー用の基本的なレイアウトとアクティビティの作成方法について説明します。

次のレイアウト コードを使用すると、カメラ プレビューの表示に使用できる非常に基本的なビューを実装できます。この例では、FrameLayout 要素がカメラの Preview クラスのコンテナとなることを想定しています。このレイアウト タイプでは、別の画像情報やコントロールをライブカメラのプレビュー画像に重ねることができます。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
  <FrameLayout
    android:id="@+id/camera_preview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1"
    />

  <Button
    android:id="@+id/button_capture"
    android:text="Capture"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    />
</LinearLayout>

ほとんどのデバイスでは、カメラ プレビューのデフォルトの方向は横向きです。このレイアウトの例では、水平(横向き)レイアウトが指定されており、以下のコードでアプリの画面の向きが横向きに固定されます。カメラ プレビューのレンダリングを簡素化するため、マニフェストに以下を追加して、アプリのプレビュー アクティビティの向きを横向きに変更する必要があります。

<activity android:name=".CameraActivity"
          android:label="@string/app_name"

          android:screenOrientation="landscape">
          <!-- configure this activity to use landscape orientation -->

          <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

注: カメラ プレビューを必ず横表示にしなければならないわけではありません。Android 2.2(API レベル 8)以降では、setDisplayOrientation() メソッドを使用してプレビュー画像の回転を設定できるようになりました。ユーザーがスマートフォンの向きを変えるとプレビューの向きが変わるようにするには、Preview クラスの surfaceChanged() メソッド内で、最初に Camera.stopPreview() でプレビューを停止して画面の向きを変更してから、Camera.startPreview() でプレビューを再開します。

カメラビューのアクティビティで、上記の例に示すように FrameLayout 要素に Preview クラスを追加します。カメラ アクティビティでは、一時停止またはシャットダウン時にカメラを解放する必要もあります。次の例では、Preview クラスの作成に示されている Preview クラスに添付するカメラ アクティビティを変更する方法を示しています。

Kotlin

class CameraActivity : Activity() {

    private var mCamera: Camera? = null
    private var mPreview: CameraPreview? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Create an instance of Camera
        mCamera = getCameraInstance()

        mPreview = mCamera?.let {
            // Create our Preview view
            CameraPreview(this, it)
        }

        // Set the Preview view as the content of our activity.
        mPreview?.also {
            val preview: FrameLayout = findViewById(R.id.camera_preview)
            preview.addView(it)
        }
    }
}

Java

public class CameraActivity extends Activity {

    private Camera mCamera;
    private CameraPreview mPreview;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // Create an instance of Camera
        mCamera = getCameraInstance();

        // Create our Preview view and set it as the content of our activity.
        mPreview = new CameraPreview(this, mCamera);
        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
        preview.addView(mPreview);
    }
}

注: 上記の例の getCameraInstance() メソッドは、カメラへのアクセスに記載されているサンプル メソッドを指します。

写真のキャプチャ

Preview クラスと、それを表示するビュー レイアウトを作成したら、アプリで写真のキャプチャを開始できます。アプリコードで、ユーザー インターフェース コントロールがユーザー アクションに反応して写真を撮影するように、リスナーをセットアップする必要があります。

写真を取得するには、Camera.takePicture() メソッドを使用します。このメソッドは、カメラからデータを受け取る 3 つのパラメータを指定します。JPEG 形式でデータを取得する場合は、Camera.PictureCallback インターフェースを実装して画像データを取得し、ファイルに書き込みます。次のコードは、カメラから受信した画像を保存する Camera.PictureCallback インターフェースの基本的な実装を示しています。

Kotlin

private val mPicture = Camera.PictureCallback { data, _ ->
    val pictureFile: File = getOutputMediaFile(MEDIA_TYPE_IMAGE) ?: run {
        Log.d(TAG, ("Error creating media file, check storage permissions"))
        return@PictureCallback
    }

    try {
        val fos = FileOutputStream(pictureFile)
        fos.write(data)
        fos.close()
    } catch (e: FileNotFoundException) {
        Log.d(TAG, "File not found: ${e.message}")
    } catch (e: IOException) {
        Log.d(TAG, "Error accessing file: ${e.message}")
    }
}

Java

private PictureCallback mPicture = new PictureCallback() {

    @Override
    public void onPictureTaken(byte[] data, Camera camera) {

        File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
        if (pictureFile == null){
            Log.d(TAG, "Error creating media file, check storage permissions");
            return;
        }

        try {
            FileOutputStream fos = new FileOutputStream(pictureFile);
            fos.write(data);
            fos.close();
        } catch (FileNotFoundException e) {
            Log.d(TAG, "File not found: " + e.getMessage());
        } catch (IOException e) {
            Log.d(TAG, "Error accessing file: " + e.getMessage());
        }
    }
};

Camera.takePicture() メソッドを呼び出して写真のキャプチャをトリガーします。次のサンプルコードは、ボタン View.OnClickListener からこのメソッドを呼び出す方法を示しています。

Kotlin

val captureButton: Button = findViewById(R.id.button_capture)
captureButton.setOnClickListener {
    // get an image from the camera
    mCamera?.takePicture(null, null, picture)
}

Java

// Add a listener to the Capture button
Button captureButton = (Button) findViewById(R.id.button_capture);
captureButton.setOnClickListener(
    new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // get an image from the camera
            mCamera.takePicture(null, null, picture);
        }
    }
);

注: 次のサンプルの mPicture メンバーは、上のサンプルコードを指します。

注意: アプリで Camera オブジェクトを使用した後、Camera.release() を呼び出してこのオブジェクトを解放してください。カメラを解放する方法について詳しくは、カメラの解放をご覧ください。

動画のキャプチャ

Android フレームワークを使用して動画をキャプチャするには、Camera オブジェクトの管理を慎重に行い、MediaRecorder クラスと連携させる必要があります。Camera を使用して動画を撮影する際は、Camera.open()Camera.release() の呼び出しに加えて、Camera.lock()Camera.unlock() の呼び出しを管理して MediaRecorder がカメラ ハードウェアにアクセスできるようにする必要があります。

注: Android 4.0(API レベル 14)以降では、Camera.lock()Camera.unlock() の呼び出しが自動的に管理されます。

デバイスのカメラで写真を撮影する場合と異なり、動画をキャプチャするには非常に特殊な呼び出し順が必要です。適切に準備してアプリで動画をキャプチャするには、以下の詳細で示すように、特定の実行順序に従う必要があります。

  1. カメラを起動 - Camera.open() を使用して、Camera オブジェクトのインスタンスを取得します。
  2. プレビューの接続 - Camera.setPreviewDisplay() を使用して SurfaceView をカメラに接続してライブカメラ画像のプレビューを準備します。
  3. プレビューを開始 - Camera.startPreview() を呼び出して、ライブカメラ画像の表示を開始します。
  4. 動画の録画を開始 - 動画を正常に録画するには、次の手順を順番通りに完了する必要があります。
    1. カメラのロックを解除 - Camera.unlock() を呼び出して、MediaRecorder によるカメラの使用をロック解除します。
    2. MediaRecorder の設定 - 次の MediaRecorder メソッドをこの順序で呼び出します。詳細については、MediaRecorder リファレンス ドキュメントをご覧ください。
      1. setCamera() - 動画キャプチャで使用するカメラを設定します。アプリの現在の Camera インスタンスを使用します。
      2. setAudioSource() - オーディオ ソースを設定します。MediaRecorder.AudioSource.CAMCORDER を使用します。
      3. setVideoSource() - 動画のソースを設定します。MediaRecorder.VideoSource.CAMERA を使用します。
      4. 動画の出力形式とエンコードを設定します。Android 2.2(API レベル 8)以降の場合、MediaRecorder.setProfile メソッドを使用して、CamcorderProfile.get() を使用してプロファイル インスタンスを取得します。Android 2.2 より前のバージョンでは、動画の出力形式とエンコード パラメータを設定する必要があります。
        1. setOutputFormat() - 出力形式を設定します。デフォルトの設定または MediaRecorder.OutputFormat.MPEG_4 を指定します。
        2. setAudioEncoder() - 音声のエンコード タイプを設定します。デフォルトの設定または MediaRecorder.AudioEncoder.AMR_NB を指定します。
        3. setVideoEncoder() - 動画のエンコード タイプを設定します。デフォルト設定または MediaRecorder.VideoEncoder.MPEG_4_SP を指定します。
      5. setOutputFile() - メディア ファイルの保存セクションのメソッド例の getOutputMediaFile(MEDIA_TYPE_VIDEO).toString() を使用して、出力ファイルを設定します。
      6. setPreviewDisplay() - アプリの SurfaceView プレビュー レイアウト要素を指定します。プレビューの接続で指定したものと同じオブジェクトを使用します。

      注意: これらの MediaRecorder 構成メソッドは必ずこの順序で呼び出してください。そうしないと、アプリでエラーが発生し、録画に失敗します。

    3. MediaRecorder の準備 - MediaRecorder.prepare() を呼び出して、構成された設定で MediaRecorder を準備します。
    4. MediaRecorder を開始 - MediaRecorder.start() を呼び出して、録画を開始します。
  5. 動画の録画を停止- 次のメソッドを順番に呼び出し、動画の録画を正常に完了します。
    1. MediaRecorder を停止 - MediaRecorder.stop() を呼び出して、動画の録画を停止します。
    2. MediaRecorder をリセット - 必要に応じて、MediaRecorder.reset() を呼び出してレコーダーから構成設定を削除します。
    3. MediaRecorder を解放 - MediaRecorder.release() を呼び出して MediaRecorder を解放します。
    4. カメラをロックする - Camera.lock() を呼び出して、今後 MediaRecorder セッションで使用できるようにカメラをロックします。Android 4.0(API レベル 14)以降では、MediaRecorder.prepare() 呼び出しが失敗しない限り、この呼び出しは不要です。
  6. プレビューを停止する - アクティビティでのカメラの使用が終了したら、Camera.stopPreview() を使用してプレビューを停止します。
  7. カメラを解放する - Camera.release() を呼び出して、他のアプリがカメラを使用できるようにカメラを解放します。

注: 最初にカメラ プレビューを作成せずに MediaRecorder を使用して、このプロセスの最初の手順をスキップすることもできます。ただし、録画を開始する前にプレビューを確認することが普通であるため、このプロセスについてはここでは説明しません。

ヒント: 主にアプリが動画の録画に使用される場合、プレビューを開始する前に setRecordingHint(boolean)true に設定します。この設定で、録画開始までの時間を短縮できます。

MediaRecorder の設定

MediaRecorder クラスを使用して動画を録画する場合は、特定の順序で設定手順を実行してから、MediaRecorder.prepare() メソッドを呼び出して設定の確認と実装を行う必要があります。次のコード例は、録画用に MediaRecorder クラスを適切に設定して準備する方法を示しています。

Kotlin

private fun prepareVideoRecorder(): Boolean {
    mediaRecorder = MediaRecorder()

    mCamera?.let { camera ->
        // Step 1: Unlock and set camera to MediaRecorder
        camera?.unlock()

        mediaRecorder?.run {
            setCamera(camera)

            // Step 2: Set sources
            setAudioSource(MediaRecorder.AudioSource.CAMCORDER)
            setVideoSource(MediaRecorder.VideoSource.CAMERA)

            // Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
            setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH))

            // Step 4: Set output file
            setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString())

            // Step 5: Set the preview output
            setPreviewDisplay(mPreview?.holder?.surface)

            setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
            setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT)
            setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT)


            // Step 6: Prepare configured MediaRecorder
            return try {
                prepare()
                true
            } catch (e: IllegalStateException) {
                Log.d(TAG, "IllegalStateException preparing MediaRecorder: ${e.message}")
                releaseMediaRecorder()
                false
            } catch (e: IOException) {
                Log.d(TAG, "IOException preparing MediaRecorder: ${e.message}")
                releaseMediaRecorder()
                false
            }
        }

    }
    return false
}

Java

private boolean prepareVideoRecorder(){

    mCamera = getCameraInstance();
    mediaRecorder = new MediaRecorder();

    // Step 1: Unlock and set camera to MediaRecorder
    mCamera.unlock();
    mediaRecorder.setCamera(mCamera);

    // Step 2: Set sources
    mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
    mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

    // Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
    mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));

    // Step 4: Set output file
    mediaRecorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString());

    // Step 5: Set the preview output
    mediaRecorder.setPreviewDisplay(mPreview.getHolder().getSurface());

    // Step 6: Prepare configured MediaRecorder
    try {
        mediaRecorder.prepare();
    } catch (IllegalStateException e) {
        Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage());
        releaseMediaRecorder();
        return false;
    } catch (IOException e) {
        Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage());
        releaseMediaRecorder();
        return false;
    }
    return true;
}

Android 2.2(API レベル 8)以前では、CamcorderProfile を使用する代わりに、出力形式とエンコード形式のパラメータを直接設定する必要があります。このアプローチを次のコードで示します。

Kotlin

    // Step 3: Set output format and encoding (for versions prior to API Level 8)
    mediaRecorder?.apply {
        setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
        setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT)
        setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT)
    }

Java

    // Step 3: Set output format and encoding (for versions prior to API Level 8)
    mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
    mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
    mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);

次に示す MediaRecorder の動画録画パラメータにはデフォルト設定がありますが、必要に応じて、アプリに対する設定を調整してください。

MediaRecorder の起動と停止

MediaRecorder クラスを使用して動画録画の開始や停止を行う場合、以下に示されるように、特定の順序に従う必要があります。

  1. Camera.unlock() を使用してカメラのロックを解除する
  2. 上記のコード例に示すように MediaRecorder を設定する
  3. MediaRecorder.start() を使用して録画を開始する
  4. 動画を録画する
  5. MediaRecorder.stop() を使用して録画を停止する
  6. MediaRecorder.release() を使用してメディア レコーダーを解放する
  7. Camera.lock() を使用してカメラをロックする

次のコード例は、カメラと MediaRecorder クラスを使用して動画の録画を適切に開始および停止するためのボタンの接続方法を示しています。

注: 録画完了時にカメラを解放しないでください。解放すると、プレビューが停止します。

Kotlin

var isRecording = false
val captureButton: Button = findViewById(R.id.button_capture)
captureButton.setOnClickListener {
    if (isRecording) {
        // stop recording and release camera
        mediaRecorder?.stop() // stop the recording
        releaseMediaRecorder() // release the MediaRecorder object
        mCamera?.lock() // take camera access back from MediaRecorder

        // inform the user that recording has stopped
        setCaptureButtonText("Capture")
        isRecording = false
    } else {
        // initialize video camera
        if (prepareVideoRecorder()) {
            // Camera is available and unlocked, MediaRecorder is prepared,
            // now you can start recording
            mediaRecorder?.start()

            // inform the user that recording has started
            setCaptureButtonText("Stop")
            isRecording = true
        } else {
            // prepare didn't work, release the camera
            releaseMediaRecorder()
            // inform user
        }
    }
}

Java

private boolean isRecording = false;

// Add a listener to the Capture button
Button captureButton = (Button) findViewById(id.button_capture);
captureButton.setOnClickListener(
    new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (isRecording) {
                // stop recording and release camera
                mediaRecorder.stop();  // stop the recording
                releaseMediaRecorder(); // release the MediaRecorder object
                mCamera.lock();         // take camera access back from MediaRecorder

                // inform the user that recording has stopped
                setCaptureButtonText("Capture");
                isRecording = false;
            } else {
                // initialize video camera
                if (prepareVideoRecorder()) {
                    // Camera is available and unlocked, MediaRecorder is prepared,
                    // now you can start recording
                    mediaRecorder.start();

                    // inform the user that recording has started
                    setCaptureButtonText("Stop");
                    isRecording = true;
                } else {
                    // prepare didn't work, release the camera
                    releaseMediaRecorder();
                    // inform user
                }
            }
        }
    }
);

注: 上記の例では、prepareVideoRecorder() メソッドは、MediaRecorder の設定に示されているサンプルコードを示します。このメソッドは、カメラのロック、MediaRecorder インスタンスの設定と準備を行います。

カメラの解放

カメラは、デバイス上の複数のアプリが共有する 1 つのリソースです。アプリは、Camera インスタンスの取得後にカメラを使用できます。アプリがカメラの使用を停止する際やアプリが一時停止(Activity.onPause())したらすぐに、必ず Camera オブジェクトを解放するように特に注意してください。アプリがカメラを適切に解放しないと、自身のアプリによるものを含めて、以降のすべてのカメラへのアクセスの試行が失敗し、自身のアプリや他のアプリがシャットダウンする可能性があります。

Camera オブジェクトのインスタンスを解放するには、以下のコード例に示すように Camera.release() メソッドを使用します。

Kotlin

class CameraActivity : Activity() {
    private var mCamera: Camera?
    private var preview: SurfaceView?
    private var mediaRecorder: MediaRecorder?

    override fun onPause() {
        super.onPause()
        releaseMediaRecorder() // if you are using MediaRecorder, release it first
        releaseCamera() // release the camera immediately on pause event
    }

    private fun releaseMediaRecorder() {
        mediaRecorder?.reset() // clear recorder configuration
        mediaRecorder?.release() // release the recorder object
        mediaRecorder = null
        mCamera?.lock() // lock camera for later use
    }

    private fun releaseCamera() {
        mCamera?.release() // release the camera for other applications
        mCamera = null
    }
}

Java

public class CameraActivity extends Activity {
    private Camera mCamera;
    private SurfaceView preview;
    private MediaRecorder mediaRecorder;

    ...

    @Override
    protected void onPause() {
        super.onPause();
        releaseMediaRecorder();       // if you are using MediaRecorder, release it first
        releaseCamera();              // release the camera immediately on pause event
    }

    private void releaseMediaRecorder(){
        if (mediaRecorder != null) {
            mediaRecorder.reset();   // clear recorder configuration
            mediaRecorder.release(); // release the recorder object
            mediaRecorder = null;
            mCamera.lock();           // lock camera for later use
        }
    }

    private void releaseCamera(){
        if (mCamera != null){
            mCamera.release();        // release the camera for other applications
            mCamera = null;
        }
    }
}

注意: アプリでカメラが適切に解放されない場合、自身のアプリによるものを含め、以降のすべてのカメラへのアクセスの試行が失敗し、自身のアプリや他のアプリがシャットダウンする可能性があります。

メディア ファイルの保存

写真や動画など、ユーザーが作成したメディア ファイルはデバイスの外部ストレージ ディレクトリ(SD カード)に保存することが理想的です。これによりシステム スペースが節約され、また、デバイスを使用せずにファイルにアクセスできるようになります。デバイスでメディア ファイルを保存できるディレクトリは多数ありますが、デベロッパーが検討すべき標準的なディレクトリは以下の 2 つのみです。

  • Environment.getExternalStoragePublicDirectoryEnvironment.DIRECTORY_PICTURES - このメソッドは、写真や動画の保存に使用する、標準的で、共有された、推奨の場所を返します。このディレクトリは共有(公開)されているため、他のアプリはこのディレクトリに保存されたファイルを簡単に検出、読み取り、変更、削除できます。ユーザーがアプリをアンインストールしても、この場所に保存されているメディア ファイルは削除されません。ユーザーの既存の写真や動画に影響しないようにするには、下記のコードサンプルで示すように、このディレクトリ内にアプリのメディア ファイル用のサブディレクトリを作成します。このメソッドは、Android 2.2(API レベル 8)以降で使用できます。それ以前の API バージョンでの同等の呼び出しについては、共有ファイルの保存をご覧ください。
  • Context.getExternalFilesDirEnvironment.DIRECTORY_PICTURES - このメソッドは、アプリに関連する写真や動画を保存するための標準的な場所を返します。アプリをアンインストールすると、この場所に保存されているすべてのファイルが削除されます。この場所にあるファイルにはセキュリティが適用されません。他のアプリは、これらのファイルの読み取り、変更、削除を行えます。

次のコード例は、Intent を使用して、あるいはカメラアプリの作成の一環として、デバイスのカメラを起動するメディア ファイルの File または Uri の場所を作成する方法を示します。

Kotlin

val MEDIA_TYPE_IMAGE = 1
val MEDIA_TYPE_VIDEO = 2

/** Create a file Uri for saving an image or video */
private fun getOutputMediaFileUri(type: Int): Uri {
    return Uri.fromFile(getOutputMediaFile(type))
}

/** Create a File for saving an image or video */
private fun getOutputMediaFile(type: Int): File? {
    // To be safe, you should check that the SDCard is mounted
    // using Environment.getExternalStorageState() before doing this.

    val mediaStorageDir = File(
            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
            "MyCameraApp"
    )
    // This location works best if you want the created images to be shared
    // between applications and persist after your app has been uninstalled.

    // Create the storage directory if it does not exist
    mediaStorageDir.apply {
        if (!exists()) {
            if (!mkdirs()) {
                Log.d("MyCameraApp", "failed to create directory")
                return null
            }
        }
    }

    // Create a media file name
    val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
    return when (type) {
        MEDIA_TYPE_IMAGE -> {
            File("${mediaStorageDir.path}${File.separator}IMG_$timeStamp.jpg")
        }
        MEDIA_TYPE_VIDEO -> {
            File("${mediaStorageDir.path}${File.separator}VID_$timeStamp.mp4")
        }
        else -> null
    }
}

Java

public static final int MEDIA_TYPE_IMAGE = 1;
public static final int MEDIA_TYPE_VIDEO = 2;

/** Create a file Uri for saving an image or video */
private static Uri getOutputMediaFileUri(int type){
      return Uri.fromFile(getOutputMediaFile(type));
}

/** Create a File for saving an image or video */
private static File getOutputMediaFile(int type){
    // To be safe, you should check that the SDCard is mounted
    // using Environment.getExternalStorageState() before doing this.

    File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
              Environment.DIRECTORY_PICTURES), "MyCameraApp");
    // This location works best if you want the created images to be shared
    // between applications and persist after your app has been uninstalled.

    // Create the storage directory if it does not exist
    if (! mediaStorageDir.exists()){
        if (! mediaStorageDir.mkdirs()){
            Log.d("MyCameraApp", "failed to create directory");
            return null;
        }
    }

    // Create a media file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    File mediaFile;
    if (type == MEDIA_TYPE_IMAGE){
        mediaFile = new File(mediaStorageDir.getPath() + File.separator +
        "IMG_"+ timeStamp + ".jpg");
    } else if(type == MEDIA_TYPE_VIDEO) {
        mediaFile = new File(mediaStorageDir.getPath() + File.separator +
        "VID_"+ timeStamp + ".mp4");
    } else {
        return null;
    }

    return mediaFile;
}

注: Environment.getExternalStoragePublicDirectory() は Android 2.2(API レベル 8)以降で使用できます。これより前のバージョンの Android のデバイスを対象にしている場合は、代わりに Environment.getExternalStorageDirectory() を使用してください。詳細については、共有ファイルの保存をご覧ください。

URI でサポートされる仕事用プロファイルを作るには、まずファイル URI をコンテンツ URI に変換します。次に、コンテンツ URI を IntentEXTRA_OUTPUT に追加します。

Android デバイスにファイルを保存する方法について詳しくは、データ ストレージをご覧ください。

カメラ機能

Android では、カメラアプリで使用できるさまざまなカメラ機能(画像形式、フラッシュ モード、フォーカス設定など)が用意されています。このセクションでは、一般的なカメラ機能の概要と、それらの使用方法について簡単に説明します。ほとんどのカメラ機能は、Camera.Parameters オブジェクトを使用してアクセスおよび設定できます。ただし、Camera.Parameters で詳細な設定を必要とする重要な機能がいくつかあります。これらの機能については、次のセクションで説明します。

Camera.Parameters によって制御される機能の使用方法に関する全般的な情報については、カメラ機能の使用セクションをご覧ください。カメラ パラメータ オブジェクトによって制御される機能の使用方法について詳しくは、下記の機能リストのリンク先の API リファレンス ドキュメントをご覧ください。

表 1. 一般的なカメラ機能(機能が導入された Android API レベル順)

機能 API レベル 説明
顔検出 14 写真内の人物の顔を識別し、フォーカス、測光、ホワイト バランスに使用します
測光領域 14 ホワイト バランスの計算で使用する写真内の 1 つ以上の領域を指定します
フォーカス領域 14 フォーカス合わせに使用する写真内の 1 つ以上の領域を設定します
White Balance Lock 14 ホワイト バランスの自動調整を停止 / 開始します
Exposure Lock 14 自動露出調整を停止 / 開始します
Video Snapshot 14 動画撮影中に写真を撮影します(フレーム キャプチャ)
タイムラプス動画 11 遅延を設定してフレームを撮影し、タイムラプス動画を録画します
Multiple Cameras 9 1 つのデバイスに搭載された複数のカメラをサポートします(前面カメラと背面カメラなど)
Focus Distance 9 フォーカスを合わせようとしている被写体からカメラまでの距離を報告します
Zoom 8 画像拡大を設定します
Exposure Compensation 8 露光レベルを調整します
GPS Data 5 写真に地理的位置情報を追加 / 削除します
White Balance 5 ホワイト バランス モードを設定します(キャプチャした写真のカラー値に影響します)
Focus Mode 5 被写体にフォーカスを合わせる方法(自動フォーカス、固定、マクロ、無限大など)を設定します
Scene Mode 5 夜間、ビーチ、雪、キャンドルライトなど、特定の撮影状況のプリセット モードを適用します
JPEG Quality 5 JPEG 写真の圧縮レベルを設定します(写真出力ファイルの品質やサイズを調整できます)
Flash Mode 5 フラッシュのオン、オフ、オートを切り替えます
Color Effects 5 キャプチャした写真に対して色彩効果(白黒、セピアトーン、ネガティブなど)を適用します
Anti-Banding 5 JPEG 圧縮に起因するカラー グラデーションのバンディングの影響を低減します
Picture Format 1 写真のファイル形式を指定します
Picture Size 1 保存する写真の寸法(ピクセル値)を指定します

注: ハードウェアの違いやソフトウェア実装により、これらの機能はすべてのデバイスでサポートされているわけではありません。アプリが実行されているデバイスで使用できる機能を確認する方法については、使用できる機能の確認をご覧ください。

使用できる機能の確認

Android デバイスのカメラ機能を使用する設定をする際にまず理解すべき点として、すべてのカメラ機能がすべてのデバイスで利用できるわけではないことがあります。また、デバイスで特定の機能がサポートされている場合、デバイスによってサポートされているレベルが異なっていたり、サポートされているオプションが異なることがあります。そのため、カメラアプリを開発する際の決定プロセスの一環として、サポートするカメラ機能とサポートするレベルを決定する必要があります。決定したら、デバイスのハードウェアでこれらの機能がサポートされているかどうかを確認し、機能が利用できない場合はエラーとして適切に処理されるようにするコードをカメラアプリに含めることを計画する必要があります。

カメラ機能が利用できるかどうかを確認するには、カメラの Parameters オブジェクトのインスタンスを取得し、関連するメソッドを確認します。次のコードサンプルは、Camera.Parameters オブジェクトを取得し、カメラでオートフォーカス機能がサポートされているかどうかを確認する方法を示しています。

Kotlin

val params: Camera.Parameters? = camera?.parameters
val focusModes: List<String>? = params?.supportedFocusModes
if (focusModes?.contains(Camera.Parameters.FOCUS_MODE_AUTO) == true) {
    // Autofocus mode is supported
}

Java

// get Camera parameters
Camera.Parameters params = camera.getParameters();

List<String> focusModes = params.getSupportedFocusModes();
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
  // Autofocus mode is supported
}

ほとんどのカメラ機能で上記の手法を使用できます。Camera.Parameters オブジェクトには getSupported...()is...Supported()getMax...() メソッドがあり、機能がサポートされているかどうか、どの程度サポートされているかを確認できます。

アプリを適切に動作させるために特定のカメラ機能が必要な場合は、アプリのマニフェストに追加することで、その特定のカメラ機能を要求できます。フラッシュやオートフォーカスなどの特定のカメラ機能の使用を宣言すると、Google Play は、こうした機能をサポートしていないデバイスにアプリをインストールできないように制限します。アプリ マニフェストで宣言できるカメラ機能の一覧については、マニフェストの機能リファレンスをご覧ください。

カメラ機能の使用

ほとんどのカメラ機能は、Camera.Parameters オブジェクトを使用して有効化、管理されます。このオブジェクトを取得するには、まず Camera オブジェクトのインスタンスを取得し、getParameters() メソッドを呼び出して返された Parameter オブジェクトを変更してから、それを Camera オブジェクトに戻して設定します。次のサンプルコードをご覧ください。

Kotlin

val params: Camera.Parameters? = camera?.parameters
params?.focusMode = Camera.Parameters.FOCUS_MODE_AUTO
camera?.parameters = params

Java

// get Camera parameters
Camera.Parameters params = camera.getParameters();
// set the focus mode
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
// set Camera parameters
camera.setParameters(params);

この手法は、ほぼすべてのカメラ機能で有効です。ほとんどのパラメータは、Camera オブジェクトのインスタンスの取得後にいつでも変更できます。パラメータの変更は、通常、アプリのカメラ プレビューでユーザーにすぐに表示されます。ソフトウェア側でパラメータの変更が実際に反映されるのは数フレーム後になります。これは、カメラ ハードウェアが新しい命令を処理してから更新された画像データを送信するためです。

重要: 一部のカメラ機能は変更できません。特に、カメラ プレビューのサイズや向きを変更するには、まずプレビューを停止し、プレビュー サイズを変更してから、プレビューを再開する必要があります。Android 4.0(API レベル 14)以降では、プレビューを再開せずにプレビューの向きを変更できます。

実装するために多くのコードを必要とするカメラ機能には、以下のものがあります。

  • 測光領域とフォーカス領域
  • 顔検出
  • タイムラプス動画

以降のセクションで、これらの機能の実装方法について概説します。

測光領域とフォーカス領域

写真を撮影する状況によっては、フォーカスや測光をオートに設定している場合、思った通りの結果が得られないことがあります。Android 4.0(API レベル 14)以降では、画像上でフォーカスや光量レベルの設定を決定するために使用する領域をアプリまたはユーザーが指定できるようにする追加のコントロールをカメラアプリが提供できます。これらの値はカメラ ハードウェアに渡され、画像また動画の撮影時に使用されます。

測光領域とフォーカス領域は、他のカメラ機能と同様に Camera.Parameters オブジェクトのメソッドを使用して制御します。次のコードは、Camera のインスタンスに対して 2 つの測光領域を設定する方法を示しています。

Kotlin

// Create an instance of Camera
camera = getCameraInstance()

// set Camera parameters
val params: Camera.Parameters? = camera?.parameters

params?.apply {
    if (maxNumMeteringAreas > 0) { // check that metering areas are supported
        meteringAreas = ArrayList<Camera.Area>().apply {
            val areaRect1 = Rect(-100, -100, 100, 100) // specify an area in center of image
            add(Camera.Area(areaRect1, 600)) // set weight to 60%
            val areaRect2 = Rect(800, -1000, 1000, -800) // specify an area in upper right of image
            add(Camera.Area(areaRect2, 400)) // set weight to 40%
        }
    }
    camera?.parameters = this
}

Java

// Create an instance of Camera
camera = getCameraInstance();

// set Camera parameters
Camera.Parameters params = camera.getParameters();

if (params.getMaxNumMeteringAreas() > 0){ // check that metering areas are supported
    List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>();

    Rect areaRect1 = new Rect(-100, -100, 100, 100);    // specify an area in center of image
    meteringAreas.add(new Camera.Area(areaRect1, 600)); // set weight to 60%
    Rect areaRect2 = new Rect(800, -1000, 1000, -800);  // specify an area in upper right of image
    meteringAreas.add(new Camera.Area(areaRect2, 400)); // set weight to 40%
    params.setMeteringAreas(meteringAreas);
}

camera.setParameters(params);

Camera.Area オブジェクトには、2 つのデータ パラメータが含まれます。カメラの視野内の領域と重み値を指定する Rect オブジェクトは、測光やフォーカスの計算における指定された領域の重要度をカメラに通知します。

Camera.Area オブジェクトの Rect フィールドには、2,000 × 2,000 の単位グリッドにマッピングされる長方形形状が記録されます。以下の画像に示すように、座標 -1,000, -1,000 はカメラ画像の左上隅、座標 1,000, 1,000 は、カメラ画像の右下隅を表します。

図 1. 赤い線は、カメラ プレビュー内で Camera.Area を指定するための座標系を示しています。青いボックスは、カメラ領域の場所と形状を Rect 値(333,333,667,667)で示しています。

この座標系の境界は、常にカメラ プレビューに表示される画像の外縁に対応しているため、ズームレベルに対応して拡大縮小されることはありません。同様に、Camera.setDisplayOrientation() を使用して画像プレビューを回転しても、座標系は再配置されません。

顔検出

人物が写っている写真の場合、通常であれば人物の顔が写真の中で最も重要な領域になります。この場合には、人物の顔を使用してフォーカスとホワイト バランスの両方を決定して写真をキャプチャすることが理想的です。Android 4.0(API レベル 14)フレームワークでは、顔認識技術を使用して顔を認識し、写真設定を計算する API が用意されています。

注: 顔検出機能の実行中、setWhiteBalance(String)setFocusAreas(List<Camera.Area>)setMeteringAreas(List<Camera.Area>) は無効になります。

カメラアプリで顔検出機能を使用するには、いくつかの手順を完了する必要があります。

  • 顔検出がデバイスでサポートされることを確認する
  • 顔検出リスナーを作成する
  • 顔検出リスナーを Camera オブジェクトに追加する
  • プレビュー後(およびプレビューの再開後に毎回)顔検出を開始する

顔検出機能は、一部のデバイスではサポートされていません。この機能がサポートされていることを確認するには、getMaxNumDetectedFaces() を呼び出します。この確認の例については、以下の startFaceDetection() サンプル メソッドに示します。

顔検出が通知され対応できるようにするには、カメラアプリで顔検出イベントのリスナーを設定する必要があります。そのためには、下記のサンプルコードに示すように、Camera.FaceDetectionListener インターフェースを実装する Listener クラスを作成する必要があります。

Kotlin

internal class MyFaceDetectionListener : Camera.FaceDetectionListener {

    override fun onFaceDetection(faces: Array<Camera.Face>, camera: Camera) {
        if (faces.isNotEmpty()) {
            Log.d("FaceDetection", ("face detected: ${faces.size}" +
                    " Face 1 Location X: ${faces[0].rect.centerX()}" +
                    "Y: ${faces[0].rect.centerY()}"))
        }
    }
}

Java

class MyFaceDetectionListener implements Camera.FaceDetectionListener {

    @Override
    public void onFaceDetection(Face[] faces, Camera camera) {
        if (faces.length > 0){
            Log.d("FaceDetection", "face detected: "+ faces.length +
                    " Face 1 Location X: " + faces[0].rect.centerX() +
                    "Y: " + faces[0].rect.centerY() );
        }
    }
}

このクラスを作成したら、以下のサンプルコードに示すように、このクラスをアプリの Camera オブジェクトに設定します。

Kotlin

camera?.setFaceDetectionListener(MyFaceDetectionListener())

Java

camera.setFaceDetectionListener(new MyFaceDetectionListener());

カメラ プレビューを開始(または再開)するたびに、アプリで顔検出機能を開始する必要があります。以下のサンプルコードに示すように、顔検出を開始するためのメソッドを作成して、必要に応じて呼び出すことができます。

Kotlin

fun startFaceDetection() {
    // Try starting Face Detection
    val params = mCamera?.parameters
    // start face detection only *after* preview has started

    params?.apply {
        if (maxNumDetectedFaces > 0) {
            // camera supports face detection, so can start it:
            mCamera?.startFaceDetection()
        }
    }
}

Java

public void startFaceDetection(){
    // Try starting Face Detection
    Camera.Parameters params = mCamera.getParameters();

    // start face detection only *after* preview has started
    if (params.getMaxNumDetectedFaces() > 0){
        // camera supports face detection, so can start it:
        mCamera.startFaceDetection();
    }
}

カメラ プレビューを開始(または再開)するたびに、顔検出を開始する必要があります。Preview クラスの作成に示されている Preview クラスを使用する場合は、surfaceCreated() メソッドと surfaceChanged() メソッドの両方に startFaceDetection() メソッドを追加します。以下のサンプルコードをご覧ください。

Kotlin

override fun surfaceCreated(holder: SurfaceHolder) {
    try {
        mCamera.setPreviewDisplay(holder)
        mCamera.startPreview()

        startFaceDetection() // start face detection feature
    } catch (e: IOException) {
        Log.d(TAG, "Error setting camera preview: ${e.message}")
    }
}

override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) {
    if (holder.surface == null) {
        // preview surface does not exist
        Log.d(TAG, "holder.getSurface() == null")
        return
    }
    try {
        mCamera.stopPreview()
    } catch (e: Exception) {
        // ignore: tried to stop a non-existent preview
        Log.d(TAG, "Error stopping camera preview: ${e.message}")
    }
    try {
        mCamera.setPreviewDisplay(holder)
        mCamera.startPreview()

        startFaceDetection() // re-start face detection feature
    } catch (e: Exception) {
        // ignore: tried to stop a non-existent preview
        Log.d(TAG, "Error starting camera preview: ${e.message}")
    }
}

Java

public void surfaceCreated(SurfaceHolder holder) {
    try {
        mCamera.setPreviewDisplay(holder);
        mCamera.startPreview();

        startFaceDetection(); // start face detection feature

    } catch (IOException e) {
        Log.d(TAG, "Error setting camera preview: " + e.getMessage());
    }
}

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {

    if (holder.getSurface() == null){
        // preview surface does not exist
        Log.d(TAG, "holder.getSurface() == null");
        return;
    }

    try {
        mCamera.stopPreview();

    } catch (Exception e){
        // ignore: tried to stop a non-existent preview
        Log.d(TAG, "Error stopping camera preview: " + e.getMessage());
    }

    try {
        mCamera.setPreviewDisplay(holder);
        mCamera.startPreview();

        startFaceDetection(); // re-start face detection feature

    } catch (Exception e){
        // ignore: tried to stop a non-existent preview
        Log.d(TAG, "Error starting camera preview: " + e.getMessage());
    }
}

注: このメソッドの呼び出しは、startPreview() を呼び出した後に行ってください。カメラアプリのメイン アクティビティの onCreate() メソッドで顔検出を開始しようとしないでください。アプリを実行するこの時点では、プレビューは利用できません。

タイムラプス動画

タイムラプス動画機能を使用すれば、数秒~数分間隔で撮影された写真を組み合わせて動画クリップを作成できます。この機能は MediaRecorder を使用して、タイムラプス シーケンスの画像を記録します。

MediaRecorder でタイムラプス動画を録画するには、Recorder オブジェクトを通常の動画の録画と同様に設定し、キャプチャするフレーム / 秒を低い数値に設定し、タイムラプスの品質設定のいずれかを使用します。コードサンプルを以下に示します。

Kotlin

mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_TIME_LAPSE_HIGH))
mediaRecorder.setCaptureRate(0.1) // capture a frame every 10 seconds

Java

// Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_TIME_LAPSE_HIGH));
...
// Step 5.5: Set the video capture rate to a low number
mediaRecorder.setCaptureRate(0.1); // capture a frame every 10 seconds

これらの設定は、MediaRecorder のより大きな設定手順の一環として行う必要があります。完全なサンプルコードの例については、MediaRecorder の設定をご覧ください。設定が完了したら、通常の動画クリップの録画と同じように録画を開始します。MediaRecorder の設定方法と実行の詳細については、動画のキャプチャをご覧ください。

Camera2VideoHdrViewfinder のサンプルでは、このページで扱っている API の使用方法をさらに詳しく説明しています。

権限を必要とするカメラ フィールド

Android 10(API レベル 29)以降を実行するアプリでは、getCameraCharacteristics() メソッドを返す以下のフィールドの値にアクセスするために、CAMERA 権限が必要です。

  • LENS_POSE_ROTATION
  • LENS_POSE_TRANSLATION
  • LENS_INTRINSIC_CALIBRATION
  • LENS_RADIAL_DISTORTION
  • LENS_POSE_REFERENCE
  • LENS_DISTORTION
  • LENS_INFO_HYPERFOCAL_DISTANCE
  • LENS_INFO_MINIMUM_FOCUS_DISTANCE
  • SENSOR_REFERENCE_ILLUMINANT1
  • SENSOR_REFERENCE_ILLUMINANT2
  • SENSOR_CALIBRATION_TRANSFORM1
  • SENSOR_CALIBRATION_TRANSFORM2
  • SENSOR_COLOR_TRANSFORM1
  • SENSOR_COLOR_TRANSFORM2
  • SENSOR_FORWARD_MATRIX1
  • SENSOR_FORWARD_MATRIX2

他のサンプルコード

サンプルアプリをダウンロードするには、Camera2Basic サンプルCameraX の公式サンプルアプリをご覧ください。