Camera API

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

注:このページで説明する Camera クラスは廃止されています。Android 5.0(API レベル 21)以降で動作する新しいクラス camera2 を使用することが推奨されます。camera2 の詳細については、このブログ記事この動画をご覧ください。

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

考慮事項

Android 端末でアプリがカメラを使用できるようにする前に、アプリがこのハードウェア機能をどのように使用するかについて、いくつか確認しておく点があります。

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

基本

Android フレームワークでは、android.hardware.camera2 API またはカメラ Intent により画像と動画の撮影がサポートされています。関連するクラスを次に示します。

android.hardware.camera2
このパッケージは、端末のカメラをコントロールするための主な 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" />
    
  • ストレージのパーミッション - アプリで画像や動画を外部ストレージ(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 が推奨されます。

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

  • カメラを検出してカメラにアクセスする - カメラの存在を確認し、アクセスをリクエストするコードを作成します。
  • プレビュー クラスを作成する - SurfaceView を拡張し、SurfaceHolder インターフェースを実装するカメラ プレビュー クラスを作成します。このクラスは、カメラからのライブ画像をプレビューします。
  • プレビュー レイアウトを作成する - カメラ プレビュー クラスを作成したら、プレビューと必要なユーザー インターフェース コントロールを統合するビュー レイアウトを作成します。
  • 撮影のためのリスナーをセットアップする - インターフェース コントロールが、ユーザーの操作(ボタンを押す操作など)に反応して画像や動画の撮影を開始できるようにするため、リスナーを接続します。
  • 撮影してファイルを保存する - 写真や動画を撮影して出力を保存するためのコードをセットアップします。
  • カメラを解放する - カメラを使い終わったら、別のアプリがカメラを使用できるように、アプリがカメラを適切に解放する必要があります。

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

注意:アプリでの 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) を使用して特定のカメラにアクセスできます。上記のサンプルコードでは、複数のカメラが搭載されている端末では最初に背面カメラにアクセスします。

カメラ機能の確認

カメラへのアクセスを取得したら、Camera.getParameters() メソッドを使用し、返されたCamera.Parameters オブジェクトからサポートされている機能を調べることで、カメラの機能に関する詳しい情報を確認できます。API レベル 9 以降を使用している場合は、Camera.getCameraInfo() を使用して、カメラが端末の前面または背面のいずれにあるかと、画像の向きを確認します。

プレビュー クラスの作成

ユーザーが写真や動画を効率的に撮影できるようにするには、端末カメラに映る内容をユーザーが確認できる必要があります。カメラ プレビュー クラスは SurfaceView であり、カメラからのライブ画像データを表示できます。これにより、ユーザーが写真や動画を確認して撮影できます。

次のサンプルコードに、View レイアウトに追加できる基本的なカメラ プレビュー クラスの作成方法を示します。このクラスは、ビューの作成と破棄のコールバック イベントをキャプチャするために 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() を呼び出した後でも、プレビューのアスペクト比がアクティビティと同じであると想定することはできません。ウィンドウのサイズとアスペクト比によっては、ワイドカメラ プレビューを縦向きのレイアウトに収めたり、レターボックス レイアウトを使用してこの逆の操作を行う必要があります。

レイアウトへのプレビューの配置

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

カメラ プレビューの表示に使用できる非常に基本的なビューを作成するレイアウト コードを次に示します。この例では、FrameLayout 要素はカメラ プレビュー クラスのコンテナとして使用されています。このレイアウト タイプが使用されているため、追加の写真情報やコントロールをライブカメラ プレビュー画像の上に重ねることができます。

<?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() メソッドを使用してプレビュー画像の回転を設定できるようになりました。ユーザーがスマートフォンの向きを変えるときにプレビューの向きを変更するには、プレビュー クラスのsurfaceChanged() メソッド内で、最初に Camera.stopPreview()でプレビューを停止し、向きを変更してから、Camera.startPreview() でプレビューを再開します。

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

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() メソッドは、カメラへのアクセスに示されているサンプル メソッドを参照しています。

写真の撮影

プレビュー クラスを作成し、このクラスを表示するビュー レイアウトを作成したら、アプリで画像を撮影する準備ができたことになります。アプリのコードで、ユーザー インターフェース コントロールがユーザー アクションに応答して写真を撮影するように、リスナーをセットアップする必要があります。

写真を取得するには、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() を使用してカメラ オブジェクトのインスタンスを取得します。
  2. プレビューを接続する - Camera.setPreviewDisplay() を使用して SurfaceView をカメラに接続し、ライブカメラ画像プレビューを準備します。
  3. プレビューを開始する - Camera.startPreview() を呼び出し、ライブカメラ画像の表示を開始します。
  4. 動画の録画を開始する - 動画を適切に録画するには、以下の手順を実行する必要があります。
    1. カメラのロックを解除する - MediaRecorder でカメラを使用できるようにするため、Camera.unlock() を呼び出してカメラのロックを解除します。
    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 インスタンスの設定と準備を行います。

カメラの解放

カメラは、端末で複数のアプリにより共有されるリソースです。アプリは Camera インスタンスの取得後にカメラを使用できます。また、アプリがカメラの使用を停止した時点、およびアプリが一時停止になった時点(Activity.onPause())でカメラ オブジェクトを解放するよう十分注意してください。作成するアプリがカメラを適切に解放しないと、それ以降カメラにアクセスしようとする操作は、そのアプリからの操作であっても失敗し、そのアプリまたは他のアプリがシャットダウンします。

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.getExternalFilesDir(Environment.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 端末に搭載されている複数のカメラ(前面カメラと背面カメラなど)をサポートします
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() メソッドを呼び出し、返されるパラメータ オブジェクトを変更し、カメラ オブジェクトに再び設定します。次のサンプルコードにこの流れを示します。

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 フィールドには 2000 x 2000 単位のグリッドにマップされる長方形の形状が記述されています。次の図に示すように、座標 -1000, -1000 はカメラ画像の左上隅を示し、座標 1000, 1000 はカメラ画像の右下隅を示します。

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

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

顔検出

通常、人物が写っている写真で最も重要な要素は顔であることから、画像撮影時に焦点とホワイト バランスの両方を決定するために顔を使用する必要があります。Android 4.0(API レベル 14)フレームワークには、顔認識テクノロジーを使用して顔を認識し、写真の設定を計算するための API があります。

注:顔検出機能が実行されている間は、setWhiteBalance(String)setFocusAreas(List<Camera.Area>)setMeteringAreas(List<Camera.Area>) の効果はありません。

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

  • 端末で顔検出機能がサポートされていることを確認する。
  • 顔検出リスナーを作成する。
  • カメラ オブジェクトに顔検出リスナーを追加する。
  • プレビューの後(およびプレビューが再開されるたびに)顔検出を開始する。

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

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

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();
    }
}

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

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 でタイムラプス動画を録画するには、通常の動画の録画の場合と同様にレコーダー オブジェクトを設定し、秒あたりの撮影フレーム数を小さな数値に設定し、タイムラプス品質設定の 1 つを使用する必要があります。次のサンプルコードにこれを示します。

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 の設定と実行の詳細については、動画の撮影をご覧ください。

Android Camera2VideoAndroid HcrViewfinder の例では、このページで説明した API の使い方をさらに詳しく示します。

パーミッションが必要なカメラ フィールド

getCameraCharacteristics() メソッドが返す以下のフィールドの値にアクセスするには、Android 10 (API レベル 29) 以上を実行しているアプリに 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

その他のサンプルコード

Camera2 basic のサンプルアプリをダウンロードするには、Android Camera2Basic サンプルをご覧ください。Camera2 raw のサンプルアプリをダウンロードするには、Android Camera2Raw サンプルをご覧ください。