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。您可以在建構相機應用程式時,使用這項功能拍照或錄影。
Camera
這個類別是較舊的已淘汰 API,用於控制裝置相機。
SurfaceView
這個類別用於向使用者顯示攝影機即時預覽畫面。
MediaRecorder
這個類別用於錄製攝影機影片。
Intent
意圖動作類型 MediaStore.ACTION_IMAGE_CAPTUREMediaStore.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。

為應用程式建立自訂攝影機介面的一般步驟如下:

  • 偵測及存取攝影機:建立程式碼,檢查攝影機是否存在並要求存取權。
  • 建立預覽類別 - 建立擴充 SurfaceView 的相機預覽類別,並實作 SurfaceHolder 介面。這個類別會預覽攝影機的即時影像。
  • 建立預覽版面配置 - 取得相機預覽類別後,請建立檢視畫面版面配置,納入預覽畫面和所需的使用者介面控制項。
  • 設定擷取作業的接聽程式 - 連線介面控制項的接聽程式,以便在使用者執行動作 (例如按下按鈕) 時開始擷取圖片或影片。
  • 擷取及儲存檔案 - 設定擷取圖片或影片的程式碼,並儲存輸出內容。
  • 釋放攝影機 - 使用攝影機後,應用程式必須正確釋放攝影機,才能供其他應用程式使用。

相機硬體是共用資源,必須謹慎管理,以免應用程式與其他可能也想使用相機的應用程式發生衝突。以下各節將討論如何偵測攝影機硬體、如何要求攝影機存取權、如何拍攝相片或影片,以及應用程式使用完畢後如何釋放攝影機。

注意:應用程式使用完畢後,請記得呼叫 Camera.release() 來釋出 Camera 物件!如果應用程式未正確釋放攝影機,後續所有存取攝影機的嘗試 (包括您自己的應用程式) 都會失敗,且可能導致您或其他應用程式關閉。

偵測相機硬體

如果應用程式並非一定要使用相機 (透過資訊清單宣告),您應在執行階段檢查相機是否可用。如要執行這項檢查,請使用 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 的例項,要求存取相機 (除非您是使用 Intent 存取相機)。

如要存取主要攝影機,請使用 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() 方法。這個方法會採用三個參數,接收來自攝影機的資料。 如要接收 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.release() 來釋出 Camera 物件!如要瞭解如何鬆開攝影機,請參閱「鬆開攝影機」一文。

錄製影片

使用 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. 解鎖相機:呼叫 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 執行個體。

釋放攝影機

攝影機是裝置上應用程式共用的資源。取得 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 卡),以節省系統空間,並讓使用者在沒有裝置的情況下存取這些檔案。裝置上可儲存媒體檔案的目錄位置有很多,但開發人員應考慮的標準位置只有兩個:

  • Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) - 這個方法會傳回儲存相片和影片的標準、共用和建議位置。這個目錄是共用 (公開) 的,因此其他應用程式可以輕鬆尋找、讀取、變更及刪除儲存在這個位置的檔案。如果使用者解除安裝應用程式,系統不會移除儲存在這個位置的媒體檔案。為避免干擾使用者現有的圖片和影片,您應在這個目錄中為應用程式的媒體檔案建立子目錄,如下列程式碼範例所示。這個方法適用於 Android 2.2 (API 級別 8),如需早期 API 版本中的對等呼叫,請參閱「儲存共用檔案」。
  • Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) - This method returns a standard location for saving pictures and videos which are associated with your application. 如果解除安裝應用程式,系統會移除儲存在這個位置的所有檔案。這個位置的檔案不會強制執行安全性措施,其他應用程式可能會讀取、變更及刪除這些檔案。

下列程式碼範例說明如何為媒體檔案建立 FileUri 位置,以便在透過 Intent 叫用裝置的相機時使用,或做為建構相機應用程式的一部分。

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 新增至 EXTRA_OUTPUTIntent

如要進一步瞭解如何在 Android 裝置上儲存檔案,請參閱「資料儲存空間」。

相機功能

Android 支援多種相機功能,可透過相機應用程式控制,例如相片格式、閃光燈模式、對焦設定等。本節列出常見的攝影機功能,並簡要說明如何使用。您可以使用 Camera.Parameters 物件存取及設定大多數的攝影機功能。不過,有幾項重要功能需要的不只是 Camera.Parameters 中的簡單設定。以下各節將說明這些功能:

如要大致瞭解如何使用透過 Camera.Parameters 控制的功能,請參閱「使用攝影機功能」一節。如要進一步瞭解如何使用透過攝影機參數物件控制的功能,請點選下方功能清單中的連結,前往 API 參考文件。

表 1. 依導入的 Android API 級別排序的常見相機功能。

功能 API 級別 說明
臉部偵測 14 辨識圖片中的人臉,並用於對焦、測光和白平衡
計量區域 14 指定圖片中的一或多個區域,以計算白平衡
重點領域 14 設定圖片中要用於對焦的一或多個區域
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 裝置上使用相機功能時,首先要瞭解並非所有裝置都支援所有相機功能。此外,支援特定功能的裝置可能支援不同等級或不同選項。因此,在開發相機應用程式時,您需要決定要支援哪些相機功能,以及支援的程度。做出決定後,您應規劃在相機應用程式中加入程式碼,檢查裝置硬體是否支援這些功能,如果功能無法使用,則應正常失敗。

您可以取得攝影機參數物件的執行個體,並檢查相關方法,瞭解攝影機功能是否可用。下列程式碼範例說明如何取得 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 執行個體設定兩個測光區域:

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 物件包含兩個資料參數:Rect 物件,用於指定攝影機視野內的區域;以及權重值,用於告知攝影機在測光或焦點計算時,應給予這個區域多大的重要性。

Camera.Area 物件中的 Rect 欄位會說明對應至 2000 x 2000 單位格線的矩形形狀。座標 -1000、-1000 代表攝影機影像的左上角,座標 1000、1000 代表攝影機影像的右下角,如下圖所示。

圖 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>) 不會產生任何作用。

如要在相機應用程式中使用臉部偵測功能,請按照下列一般步驟操作:

  • 確認裝置支援臉部偵測功能
  • 建立臉部偵測事件監聽器
  • 將臉部偵測監聽器新增至攝影機物件
  • 在預覽後 (以及每次預覽重新啟動後) 開始偵測人臉

並非所有裝置都支援臉部偵測功能。您可以呼叫 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 錄製縮時影片,請設定錄影機物件,就像錄製一般影片一樣,將每秒擷取的影格數設為較低的值,並使用其中一種縮時品質設定,如下方的程式碼範例所示。

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) 以上版本的應用程式必須具備 CAMERA 權限,才能存取 getCameraCharacteristics() 方法傳回的下列欄位值:

  • 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 範例應用程式