Camera API

Khung Android hỗ trợ nhiều camera và tính năng camera có trên thiết bị, cho phép bạn chụp ảnh và quay video trong các ứng dụng của mình. Tài liệu này trình bày một phương pháp nhanh chóng và đơn giản để chụp ảnh và quay video, đồng thời trình bày một phương pháp nâng cao để tạo trải nghiệm camera tuỳ chỉnh cho người dùng.

Lưu ý: Trang này mô tả lớp Camera (không dùng nữa). Bạn nên dùng thư viện Jetpack CameraX hoặc lớp camera2 (trong một số trường hợp sử dụng cụ thể). Cả CameraX và Camera2 đều hoạt động trên Android 5.0 (API cấp 21) trở lên.

Hãy tham khảo các tài nguyên liên quan sau:

Những yếu tố nên cân nhắc

Trước khi cho phép ứng dụng của bạn sử dụng camera trên thiết bị Android, bạn nên cân nhắc một số câu hỏi về cách ứng dụng của bạn dự định sử dụng tính năng phần cứng này.

  • Yêu cầu về camera – Việc sử dụng camera có quan trọng đến mức bạn không muốn cài đặt ứng dụng của mình trên một thiết bị không có camera không? Nếu có, bạn nên khai báo yêu cầu về camera trong tệp kê khai.
  • Chụp ảnh nhanh hoặc Camera tuỳ chỉnh – Ứng dụng của bạn sẽ sử dụng camera như thế nào? Bạn chỉ muốn chụp nhanh một bức ảnh hoặc đoạn video ngắn, hay ứng dụng của bạn sẽ cung cấp một cách thức mới để sử dụng camera? Để chụp nhanh hoặc quay đoạn video ngắn, hãy cân nhắc Sử dụng các ứng dụng máy ảnh hiện có. Để phát triển một tính năng camera tuỳ chỉnh, hãy xem phần Tạo ứng dụng camera.
  • Yêu cầu đối với dịch vụ trên nền trước – Khi nào ứng dụng của bạn tương tác với camera? Trên Android 9 (API cấp 28) trở lên, các ứng dụng chạy ở chế độ nền không thể truy cập vào camera. Do đó, bạn nên sử dụng camera khi ứng dụng ở nền trước hoặc là một phần của dịch vụ trên nền trước.
  • Bộ nhớ – Những hình ảnh hoặc video mà ứng dụng của bạn tạo ra có chỉ dành cho ứng dụng của bạn hay được chia sẻ để các ứng dụng khác (chẳng hạn như Thư viện hoặc các ứng dụng truyền thông và mạng xã hội khác) có thể sử dụng? Bạn có muốn ảnh và video vẫn có sẵn ngay cả khi ứng dụng của bạn bị gỡ cài đặt không? Hãy xem phần Lưu tệp nội dung nghe nhìn để biết cách triển khai các lựa chọn này.

Thông tin cơ bản

Khung Android hỗ trợ chụp ảnh và quay video thông qua API android.hardware.camera2 hoặc camera Intent. Sau đây là các lớp có liên quan:

android.hardware.camera2
Gói này là API chính để kiểm soát camera của thiết bị. Bạn có thể dùng lớp này để chụp ảnh hoặc quay video khi tạo một ứng dụng camera.
Camera
Lớp này là API cũ không dùng nữa để điều khiển camera của thiết bị.
SurfaceView
Lớp này dùng để trình bày bản xem trước trực tiếp của camera cho người dùng.
MediaRecorder
Lớp này dùng để quay video bằng camera.
Intent
Bạn có thể dùng loại thao tác theo ý định MediaStore.ACTION_IMAGE_CAPTURE hoặc MediaStore.ACTION_VIDEO_CAPTURE để chụp ảnh hoặc quay video mà không cần trực tiếp dùng đối tượng Camera.

Nội dung khai báo trong tệp kê khai

Trước khi bắt đầu phát triển ứng dụng bằng Camera API, bạn nên đảm bảo rằng tệp kê khai có các khai báo thích hợp để cho phép sử dụng phần cứng camera và các tính năng liên quan khác.

  • Quyền sử dụng camera – Ứng dụng của bạn phải yêu cầu cấp quyền sử dụng camera của thiết bị.
    <uses-permission android:name="android.permission.CAMERA" />

    Lưu ý: Nếu bạn đang sử dụng camera bằng cách gọi một ứng dụng camera hiện có, thì ứng dụng của bạn không cần yêu cầu quyền này.

  • Tính năng camera – Ứng dụng của bạn cũng phải khai báo việc sử dụng các tính năng camera, ví dụ:
    <uses-feature android:name="android.hardware.camera" />

    Để biết danh sách các tính năng của camera, hãy xem Thông tin tham khảo về tính năng trong tệp kê khai.

    Việc thêm các tính năng của camera vào tệp kê khai sẽ khiến Google Play ngăn ứng dụng của bạn được cài đặt vào những thiết bị không có camera hoặc không hỗ trợ các tính năng của camera mà bạn chỉ định. Để biết thêm thông tin về cách sử dụng cơ chế lọc dựa trên tính năng với Google Play, hãy xem bài viết Google Play và cơ chế lọc dựa trên tính năng.

    Nếu ứng dụng của bạn có thể sử dụng một camera hoặc tính năng camera để hoạt động đúng cách nhưng không yêu cầu phải có camera, thì bạn nên chỉ định điều này trong tệp kê khai bằng cách thêm thuộc tính android:required và đặt thuộc tính này thành false:

    <uses-feature android:name="android.hardware.camera" android:required="false" />
  • Quyền truy cập vào bộ nhớ – Ứng dụng của bạn có thể lưu hình ảnh hoặc video vào bộ nhớ ngoài (thẻ SD) của thiết bị nếu ứng dụng đó nhắm đến Android 10 (API cấp 29) trở xuống và chỉ định nội dung sau trong tệp kê khai.
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • Quyền ghi âm – Để ghi âm thanh bằng tính năng quay video, ứng dụng của bạn phải yêu cầu quyền ghi âm.
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
  • Quyền truy cập thông tin vị trí – Nếu ứng dụng của bạn gắn thẻ hình ảnh bằng thông tin vị trí GPS, bạn phải yêu cầu quyền ACCESS_FINE_LOCATION. Xin lưu ý rằng nếu ứng dụng của bạn nhắm đến Android 5.0 (API cấp 21) trở lên, bạn cũng cần khai báo rằng ứng dụng của bạn sử dụng GPS của thiết bị:

    <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" />

    Để biết thêm thông tin về cách lấy vị trí của người dùng, hãy xem phần Chiến lược vị trí.

Sử dụng các ứng dụng camera hiện có

Một cách nhanh chóng để cho phép chụp ảnh hoặc quay video trong ứng dụng mà không cần nhiều mã bổ sung là sử dụng Intent để gọi một ứng dụng camera Android hiện có. Thông tin chi tiết được mô tả trong các bài học đào tạo Chụp ảnh đơn giảnQuay video đơn giản.

Tạo ứng dụng máy ảnh

Một số nhà phát triển có thể yêu cầu giao diện người dùng camera được tuỳ chỉnh theo giao diện của ứng dụng hoặc cung cấp các tính năng đặc biệt. Việc viết mã chụp ảnh của riêng bạn có thể mang lại trải nghiệm hấp dẫn hơn cho người dùng.

Lưu ý: Hướng dẫn sau đây dành cho API Camera cũ, không được dùng nữa. Đối với các ứng dụng máy ảnh mới hoặc nâng cao, bạn nên dùng android.hardware.camera2API mới hơn.

Sau đây là các bước chung để tạo một giao diện máy ảnh tuỳ chỉnh cho ứng dụng của bạn:

  • Phát hiện và truy cập camera – Tạo mã để kiểm tra xem có camera hay không và yêu cầu quyền truy cập.
  • Tạo một lớp xem trước – Tạo một lớp xem trước của camera mở rộng SurfaceView và triển khai giao diện SurfaceHolder. Lớp này xem trước hình ảnh trực tiếp từ camera.
  • Tạo bố cục xem trước – Sau khi có lớp xem trước của camera, hãy tạo một bố cục khung hiển thị kết hợp bản xem trước và các chế độ kiểm soát giao diện người dùng mà bạn muốn.
  • Thiết lập trình nghe để chụp – Kết nối trình nghe cho các chế độ điều khiển giao diện của bạn để bắt đầu chụp ảnh hoặc quay video để phản hồi các hành động của người dùng, chẳng hạn như nhấn nút.
  • Chụp và lưu tệp – Thiết lập mã để chụp ảnh hoặc quay video và lưu đầu ra.
  • Giải phóng Camera – Sau khi sử dụng camera, ứng dụng của bạn phải giải phóng đúng cách để các ứng dụng khác có thể sử dụng.

Phần cứng camera là một tài nguyên dùng chung mà bạn phải quản lý cẩn thận để ứng dụng của bạn không xung đột với các ứng dụng khác cũng có thể muốn sử dụng phần cứng này. Các phần sau đây thảo luận về cách phát hiện phần cứng camera, cách yêu cầu quyền truy cập vào camera, cách chụp ảnh hoặc quay video và cách giải phóng camera khi ứng dụng của bạn đã dùng xong.

Thận trọng: Nhớ giải phóng đối tượng Camera bằng cách gọi Camera.release() khi ứng dụng của bạn dùng xong đối tượng này! Nếu ứng dụng của bạn không giải phóng camera đúng cách, thì tất cả các lần thử truy cập camera sau đó (kể cả những lần do chính ứng dụng của bạn thực hiện) sẽ không thành công và có thể khiến ứng dụng của bạn hoặc các ứng dụng khác bị tắt.

Phát hiện phần cứng camera

Nếu ứng dụng của bạn không yêu cầu cụ thể một camera bằng cách sử dụng một khai báo tệp kê khai, thì bạn nên kiểm tra xem có camera nào đang hoạt động hay không trong thời gian chạy. Để thực hiện bước kiểm tra này, hãy sử dụng phương thức PackageManager.hasSystemFeature(), như trong mã ví dụ bên dưới:

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

Các thiết bị Android có thể có nhiều camera, chẳng hạn như camera sau để chụp ảnh và camera trước để gọi video. Android 2.3 (API cấp 9) trở lên cho phép bạn kiểm tra số lượng camera có trên thiết bị bằng phương thức Camera.getNumberOfCameras().

Truy cập vào camera

Nếu xác định rằng thiết bị mà ứng dụng của bạn đang chạy có camera, thì bạn phải yêu cầu truy cập vào camera đó bằng cách lấy một thực thể của Camera (trừ phi bạn đang dùng một intent để truy cập vào camera).

Để truy cập vào camera chính, hãy sử dụng phương thức Camera.open() và nhớ nắm bắt mọi trường hợp ngoại lệ, như minh hoạ trong mã dưới đây:

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
}

Thận trọng: Luôn kiểm tra các trường hợp ngoại lệ khi sử dụng Camera.open(). Nếu không kiểm tra các trường hợp ngoại lệ khi camera đang được sử dụng hoặc không tồn tại, hệ thống sẽ tắt ứng dụng của bạn.

Trên các thiết bị chạy Android 2.3 (API cấp 9) trở lên, bạn có thể truy cập vào các camera cụ thể bằng cách sử dụng Camera.open(int). Đoạn mã ví dụ ở trên sẽ truy cập vào camera mặt sau đầu tiên trên thiết bị có nhiều camera.

Kiểm tra các tính năng của camera

Sau khi có quyền truy cập vào một camera, bạn có thể nhận thêm thông tin về các chức năng của camera đó bằng phương thức Camera.getParameters() và kiểm tra đối tượng Camera.Parameters được trả về để biết các chức năng được hỗ trợ. Khi sử dụng API cấp 9 trở lên, hãy dùng Camera.getCameraInfo() để xác định xem camera nằm ở mặt trước hay mặt sau của thiết bị, cũng như hướng của hình ảnh.

Tạo một lớp xem trước

Để chụp ảnh hoặc quay video một cách hiệu quả, người dùng phải có thể nhìn thấy những gì camera của thiết bị nhìn thấy. Lớp xem trước camera là một SurfaceView có thể hiển thị dữ liệu hình ảnh trực tiếp từ camera, nhờ đó người dùng có thể tạo khung hình và chụp ảnh hoặc quay video.

Đoạn mã ví dụ sau đây minh hoạ cách tạo một lớp xem trước camera cơ bản có thể được đưa vào bố cục View. Lớp này triển khai SurfaceHolder.Callback để ghi lại các sự kiện gọi lại để tạo và huỷ chế độ xem, cần thiết cho việc chỉ định đầu vào xem trước của camera.

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

Nếu bạn muốn đặt một kích thước cụ thể cho bản xem trước của camera, hãy đặt kích thước này trong phương thức surfaceChanged() như đã lưu ý trong phần nhận xét ở trên. Khi đặt kích thước xem trước, bạn phải sử dụng các giá trị từ getSupportedPreviewSizes(). Không đặt các giá trị tuỳ ý trong phương thức setPreviewSize().

Lưu ý: Khi tính năng Nhiều cửa sổ ra mắt trong Android 7.0 (API cấp 24) trở lên, bạn không thể giả định tỷ lệ khung hình của bản xem trước giống với hoạt động của bạn ngay cả sau khi gọi setDisplayOrientation(). Tuỳ thuộc vào kích thước và tỷ lệ khung hình của cửa sổ, bạn có thể phải điều chỉnh bản xem trước của camera có góc rộng vào bố cục dọc hoặc ngược lại, bằng cách sử dụng bố cục hòm thư.

Đặt bản xem trước trong bố cục

Một lớp xem trước camera (chẳng hạn như ví dụ minh hoạ ở phần trước) phải được đặt trong bố cục của một hoạt động cùng với các thành phần điều khiển giao diện người dùng khác để chụp ảnh hoặc quay video. Phần này hướng dẫn bạn cách tạo bố cục và hoạt động cơ bản cho bản xem trước.

Mã bố cục sau đây cung cấp một khung hiển thị rất cơ bản có thể dùng để hiển thị bản xem trước của camera. Trong ví dụ này, phần tử FrameLayout được dùng làm vùng chứa cho lớp xem trước của camera. Loại bố cục này được dùng để có thể phủ thêm thông tin hoặc các chế độ điều khiển khác về hình ảnh lên hình ảnh xem trước trực tiếp của camera.

<?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>

Trên hầu hết các thiết bị, hướng mặc định của bản xem trước camera là hướng ngang. Bố cục ví dụ này chỉ định một bố cục ngang và mã bên dưới sẽ cố định hướng của ứng dụng thành hướng ngang. Để đơn giản hoá việc kết xuất bản xem trước của camera, bạn nên thay đổi hướng hoạt động xem trước của ứng dụng thành hướng ngang bằng cách thêm nội dung sau vào tệp kê khai.

<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>

Lưu ý: Bản xem trước của camera không nhất thiết phải ở chế độ ngang. Kể từ Android 2.2 (API cấp 8), bạn có thể dùng phương thức setDisplayOrientation() để đặt hướng xoay của hình ảnh xem trước. Để thay đổi hướng xem trước khi người dùng thay đổi hướng điện thoại, trong phương thức surfaceChanged() của lớp xem trước, trước tiên, hãy dừng bản xem trước bằng Camera.stopPreview(), thay đổi hướng rồi bắt đầu lại bản xem trước bằng Camera.startPreview().

Trong hoạt động cho chế độ xem camera, hãy thêm lớp xem trước vào phần tử FrameLayout như trong ví dụ ở trên. Hoạt động của camera cũng phải đảm bảo rằng camera sẽ giải phóng khi bị tạm dừng hoặc tắt. Ví dụ sau đây cho thấy cách sửa đổi một hoạt động của camera để đính kèm lớp xem trước xuất hiện trong phần Tạo lớp xem trước.

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

Lưu ý: Phương thức getCameraInstance() trong ví dụ trên đề cập đến phương thức ví dụ xuất hiện trong phần Truy cập vào camera.

Chụp ảnh

Sau khi tạo một lớp xem trước và bố cục khung hiển thị để hiển thị lớp đó, bạn đã sẵn sàng bắt đầu chụp ảnh bằng ứng dụng của mình. Trong mã ứng dụng, bạn phải thiết lập trình nghe cho các chế độ kiểm soát giao diện người dùng để phản hồi hành động của người dùng bằng cách chụp ảnh.

Để truy xuất một bức ảnh, hãy sử dụng phương thức Camera.takePicture(). Phương thức này có 3 tham số nhận dữ liệu từ camera. Để nhận dữ liệu ở định dạng JPEG, bạn phải triển khai một giao diện Camera.PictureCallback để nhận dữ liệu hình ảnh và ghi dữ liệu đó vào một tệp. Đoạn mã sau đây cho thấy cách triển khai cơ bản giao diện Camera.PictureCallback để lưu hình ảnh nhận được từ camera.

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

Kích hoạt quá trình chụp ảnh bằng cách gọi phương thức Camera.takePicture(). Đoạn mã ví dụ sau đây cho thấy cách gọi phương thức này từ một nút 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);
        }
    }
);

Lưu ý: Thành phần mPicture trong ví dụ sau đây đề cập đến mã ví dụ ở trên.

Thận trọng: Nhớ giải phóng đối tượng Camera bằng cách gọi Camera.release() khi ứng dụng của bạn dùng xong đối tượng này! Để biết thông tin về cách tháo camera, hãy xem phần Tháo camera.

Quay video

Việc quay video bằng khung Android yêu cầu bạn phải quản lý cẩn thận đối tượng Camera và phối hợp với lớp MediaRecorder. Khi quay video bằng Camera, bạn phải quản lý các lệnh gọi Camera.lock()Camera.unlock() để cho phép MediaRecorder truy cập vào phần cứng camera, ngoài các lệnh gọi Camera.open()Camera.release().

Lưu ý: Kể từ Android 4.0 (API cấp 14), các lệnh gọi Camera.lock()Camera.unlock() sẽ được tự động quản lý cho bạn.

Không giống như việc chụp ảnh bằng camera của thiết bị, việc quay video đòi hỏi một thứ tự gọi rất cụ thể. Bạn phải làm theo một trình tự thực thi cụ thể để chuẩn bị và ghi hình thành công bằng ứng dụng của mình, như trình bày chi tiết bên dưới.

  1. Mở Camera – Sử dụng Camera.open() để lấy một thực thể của đối tượng camera.
  2. Connect Preview (Kết nối bản xem trước) – Chuẩn bị bản xem trước hình ảnh trực tiếp của camera bằng cách kết nối SurfaceView với camera bằng Camera.setPreviewDisplay().
  3. Bắt đầu xem trước – Gọi Camera.startPreview() để bắt đầu hiển thị hình ảnh trực tiếp từ camera.
  4. Bắt đầu quay video – Bạn phải hoàn tất các bước sau theo thứ tự để quay video thành công:
    1. Mở khoá Camera – Mở khoá camera để MediaRecorder sử dụng bằng cách gọi Camera.unlock().
    2. Định cấu hình MediaRecorder – Gọi các phương thức MediaRecorder sau theo thứ tự này. Để biết thêm thông tin, hãy xem tài liệu tham khảo về MediaRecorder.
      1. setCamera() – Đặt camera sẽ dùng để quay video, sử dụng phiên bản Camera hiện tại của ứng dụng.
      2. setAudioSource() – Đặt nguồn âm thanh, hãy dùng MediaRecorder.AudioSource.CAMCORDER.
      3. setVideoSource() – Đặt nguồn video, sử dụng MediaRecorder.VideoSource.CAMERA.
      4. Đặt định dạng đầu ra và phương thức mã hoá video. Đối với Android 2.2 (API cấp 8) trở lên, hãy sử dụng phương thức MediaRecorder.setProfile và nhận một thực thể hồ sơ bằng cách sử dụng CamcorderProfile.get(). Đối với các phiên bản Android trước 2.2, bạn phải đặt định dạng đầu ra video và các thông số mã hoá:
        1. setOutputFormat() – Đặt định dạng đầu ra, chỉ định chế độ cài đặt mặc định hoặc MediaRecorder.OutputFormat.MPEG_4.
        2. setAudioEncoder() – Đặt loại mã hoá âm thanh, chỉ định chế độ cài đặt mặc định hoặc MediaRecorder.AudioEncoder.AMR_NB.
        3. setVideoEncoder() – Đặt loại mã hoá video, chỉ định chế độ cài đặt mặc định hoặc MediaRecorder.VideoEncoder.MPEG_4_SP.
      5. setOutputFile() – Đặt tệp đầu ra, sử dụng getOutputMediaFile(MEDIA_TYPE_VIDEO).toString() từ phương thức ví dụ trong phần Lưu tệp nội dung nghe nhìn.
      6. setPreviewDisplay() – Chỉ định phần tử bố cục xem trước SurfaceView cho ứng dụng của bạn. Sử dụng cùng một đối tượng mà bạn đã chỉ định cho Connect Preview (Xem trước kết nối).

      Thận trọng: Bạn phải gọi các phương thức định cấu hình MediaRecorder này theo thứ tự này, nếu không ứng dụng của bạn sẽ gặp lỗi và quá trình ghi sẽ không thành công.

    3. Chuẩn bị MediaRecorder – Chuẩn bị MediaRecorder bằng các chế độ cài đặt cấu hình được cung cấp bằng cách gọi MediaRecorder.prepare().
    4. Start MediaRecorder – Bắt đầu ghi hình bằng cách gọi MediaRecorder.start().
  5. Dừng ghi hình – Gọi các phương thức sau theo thứ tự để hoàn tất quá trình ghi hình:
    1. Dừng MediaRecorder – Dừng ghi video bằng cách gọi MediaRecorder.stop().
    2. Đặt lại MediaRecorder – Nếu muốn, hãy xoá các chế độ cài đặt cấu hình khỏi trình ghi bằng cách gọi MediaRecorder.reset().
    3. Giải phóng MediaRecorder – Giải phóng MediaRecorder bằng cách gọi MediaRecorder.release().
    4. Khoá Camera – Khoá camera để các phiên MediaRecorder trong tương lai có thể sử dụng camera bằng cách gọi Camera.lock(). Kể từ Android 4.0 (API cấp 14), bạn không cần gọi phương thức này, trừ phi lệnh gọi MediaRecorder.prepare() không thành công.
  6. Dừng bản xem trước – Khi hoạt động của bạn đã dùng xong camera, hãy dừng bản xem trước bằng cách sử dụng Camera.stopPreview().
  7. Giải phóng Camera – Giải phóng camera để các ứng dụng khác có thể sử dụng bằng cách gọi Camera.release().

Lưu ý: Bạn có thể sử dụng MediaRecorder mà không cần tạo bản xem trước camera trước và bỏ qua một vài bước đầu tiên của quy trình này. Tuy nhiên, vì người dùng thường muốn xem bản xem trước trước khi bắt đầu ghi, nên quy trình đó không được thảo luận ở đây.

Lưu ý: Nếu ứng dụng của bạn thường được dùng để quay video, hãy đặt setRecordingHint(boolean) thành true trước khi bắt đầu xem trước. Chế độ cài đặt này có thể giúp giảm thời gian cần thiết để bắt đầu ghi hình.

Định cấu hình MediaRecorder

Khi dùng lớp MediaRecorder để quay video, bạn phải thực hiện các bước định cấu hình theo một thứ tự cụ thể rồi gọi phương thức MediaRecorder.prepare() để kiểm tra và triển khai cấu hình. Đoạn mã ví dụ sau đây minh hoạ cách định cấu hình và chuẩn bị đúng cách lớp MediaRecorder để quay video.

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

Trước Android 2.2 (API cấp 8), bạn phải đặt trực tiếp các tham số định dạng đầu ra và định dạng mã hoá thay vì sử dụng CamcorderProfile. Phương pháp này được minh hoạ trong đoạn mã sau:

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

Các thông số ghi hình sau đây cho MediaRecorder được đặt ở chế độ cài đặt mặc định. Tuy nhiên, bạn có thể muốn điều chỉnh các chế độ cài đặt này cho ứng dụng của mình:

Bắt đầu và dừng MediaRecorder

Khi bắt đầu và dừng quay video bằng lớp MediaRecorder, bạn phải làm theo một thứ tự cụ thể như được liệt kê bên dưới.

  1. Mở khoá camera bằng Camera.unlock()
  2. Định cấu hình MediaRecorder như trong ví dụ về mã ở trên
  3. Bắt đầu ghi bằng MediaRecorder.start()
  4. Quay video
  5. Dừng ghi bằng cách nhấn vào MediaRecorder.stop()
  6. Giải phóng trình ghi nội dung nghe nhìn bằng MediaRecorder.release()
  7. Khoá camera bằng Camera.lock()

Đoạn mã ví dụ sau đây minh hoạ cách kết nối một nút để bắt đầu và dừng đúng cách quá trình quay video bằng camera và lớp MediaRecorder.

Lưu ý: Khi hoàn tất quá trình quay video, đừng nhả camera nếu không bản xem trước sẽ dừng.

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

Lưu ý: Trong ví dụ trên, phương thức prepareVideoRecorder() đề cập đến đoạn mã ví dụ xuất hiện trong phần Định cấu hình MediaRecorder. Phương thức này đảm nhiệm việc khoá camera, định cấu hình và chuẩn bị phiên bản MediaRecorder.

Tháo camera

Camera là một tài nguyên được các ứng dụng trên thiết bị dùng chung. Ứng dụng của bạn có thể sử dụng camera sau khi nhận được một thực thể Camera và bạn phải đặc biệt cẩn thận để giải phóng đối tượng camera khi ứng dụng của bạn ngừng sử dụng đối tượng đó, cũng như ngay khi ứng dụng của bạn bị tạm dừng (Activity.onPause()). Nếu ứng dụng của bạn không giải phóng camera đúng cách, thì tất cả các lần thử truy cập camera sau đó (kể cả những lần thử của chính ứng dụng) sẽ không thành công và có thể khiến ứng dụng của bạn hoặc các ứng dụng khác bị tắt.

Để phát hành một thực thể của đối tượng Camera, hãy sử dụng phương thức Camera.release(), như trong mã ví dụ bên dưới.

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

Thận trọng: Nếu ứng dụng của bạn không giải phóng camera đúng cách, thì mọi nỗ lực truy cập camera sau đó (kể cả nỗ lực của chính ứng dụng) sẽ thất bại và có thể khiến ứng dụng của bạn hoặc các ứng dụng khác bị tắt.

Lưu tệp đa phương tiện

Các tệp đa phương tiện do người dùng tạo (chẳng hạn như hình ảnh và video) phải được lưu vào thư mục bộ nhớ ngoài của thiết bị (thẻ SD) để tiết kiệm dung lượng hệ thống và cho phép người dùng truy cập vào các tệp này mà không cần thiết bị. Có nhiều vị trí thư mục có thể lưu các tệp đa phương tiện trên thiết bị, tuy nhiên, chỉ có 2 vị trí tiêu chuẩn mà bạn nên cân nhắc với tư cách là nhà phát triển:

  • Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) – Phương thức này trả về vị trí tiêu chuẩn, được chia sẻ và được đề xuất để lưu ảnh và video. Thư mục này là thư mục dùng chung (công khai), nên các ứng dụng khác có thể dễ dàng phát hiện, đọc, thay đổi và xoá các tệp được lưu tại vị trí này. Nếu người dùng gỡ cài đặt ứng dụng của bạn, thì các tệp đa phương tiện được lưu vào vị trí này sẽ không bị xoá. Để tránh ảnh hưởng đến ảnh và video hiện có của người dùng, bạn nên tạo một thư mục con cho các tệp nội dung đa phương tiện của ứng dụng trong thư mục này, như minh hoạ trong đoạn mã bên dưới. Phương thức này có trong Android 2.2 (API cấp 8). Để biết các lệnh gọi tương đương trong các phiên bản API trước đó, hãy xem phần Lưu tệp dùng chung.
  • Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) – Phương thức này trả về một vị trí tiêu chuẩn để lưu hình ảnh và video được liên kết với ứng dụng của bạn. Nếu ứng dụng của bạn bị gỡ cài đặt, mọi tệp được lưu tại vị trí này sẽ bị xoá. Bảo mật không được thực thi đối với các tệp ở vị trí này và các ứng dụng khác có thể đọc, thay đổi và xoá các tệp đó.

Đoạn mã ví dụ sau đây minh hoạ cách tạo vị trí File hoặc Uri cho một tệp nội dung nghe nhìn có thể dùng khi gọi camera của thiết bị bằng Intent hoặc trong quá trình Tạo ứng dụng camera.

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

Lưu ý: Environment.getExternalStoragePublicDirectory() có trong Android 2.2 (API cấp 8) trở lên. Nếu bạn đang nhắm đến các thiết bị chạy phiên bản Android cũ, hãy sử dụng Environment.getExternalStorageDirectory(). Để biết thêm thông tin, hãy xem bài viết Lưu tệp được chia sẻ.

Để URI hỗ trợ hồ sơ công việc, trước tiên, hãy chuyển đổi URI tệp thành URI nội dung. Sau đó, hãy thêm URI nội dung vào EXTRA_OUTPUT của một Intent.

Để biết thêm thông tin về cách lưu tệp trên thiết bị Android, hãy xem phần Bộ nhớ dữ liệu.

Tính năng của camera

Android hỗ trợ nhiều tính năng của camera mà bạn có thể kiểm soát bằng ứng dụng camera, chẳng hạn như định dạng ảnh, chế độ đèn flash, chế độ cài đặt lấy nét và nhiều tính năng khác. Phần này liệt kê các tính năng thường dùng của camera và thảo luận ngắn gọn về cách sử dụng các tính năng đó. Bạn có thể truy cập và thiết lập hầu hết các tính năng của camera bằng cách sử dụng đối tượng Camera.Parameters. Tuy nhiên, có một số tính năng quan trọng cần nhiều hơn các chế độ cài đặt đơn giản trong Camera.Parameters. Các tính năng này được đề cập trong các phần sau:

Để biết thông tin chung về cách sử dụng các tính năng được kiểm soát thông qua Camera.Parameters, hãy xem phần Sử dụng các tính năng của camera. Để biết thêm thông tin chi tiết về cách sử dụng các tính năng được kiểm soát thông qua đối tượng tham số camera, hãy truy cập vào tài liệu tham khảo API theo các đường liên kết trong danh sách tính năng bên dưới.

Bảng 1. Các tính năng thường dùng của camera được sắp xếp theo Cấp độ API Android mà chúng được giới thiệu.

Tính năng Cấp độ API: Mô tả
Phát hiện khuôn mặt 14 Xác định khuôn mặt người trong ảnh và sử dụng khuôn mặt đó để lấy nét, đo sáng và cân bằng trắng
Vùng đo sáng 14 Chỉ định một hoặc nhiều vùng trong ảnh để tính toán cân bằng trắng
Lĩnh vực trọng tâm 14 Đặt một hoặc nhiều vùng trong hình ảnh để lấy nét
White Balance Lock 14 Dừng hoặc bắt đầu điều chỉnh tự động cân bằng trắng
Exposure Lock 14 Dừng hoặc bắt đầu điều chỉnh độ phơi sáng tự động
Video Snapshot 14 Chụp ảnh khi quay video (chụp khung hình)
Video tua nhanh thời gian 11 Quay các khung hình với độ trễ đã đặt để quay video tua nhanh thời gian
Multiple Cameras 9 Hỗ trợ nhiều camera trên một thiết bị, bao gồm cả camera trước và camera sau
Focus Distance 9 Báo cáo khoảng cách giữa camera và các đối tượng có vẻ như đang được lấy nét
Zoom 8 Đặt chế độ phóng to hình ảnh
Exposure Compensation 8 Tăng hoặc giảm mức độ phơi sáng ánh sáng
GPS Data 5 Đưa vào hoặc bỏ qua dữ liệu vị trí địa lý trong hình ảnh
White Balance 5 Đặt chế độ cân bằng trắng, ảnh hưởng đến các giá trị màu trong hình ảnh đã chụp
Focus Mode 5 Đặt cách camera lấy nét vào một đối tượng, chẳng hạn như tự động, cố định, cận cảnh hoặc vô cực
Scene Mode 5 Áp dụng một chế độ đặt sẵn cho các loại tình huống chụp ảnh cụ thể, chẳng hạn như cảnh đêm, cảnh bãi biển, cảnh tuyết hoặc cảnh ánh nến
JPEG Quality 5 Đặt mức nén cho hình ảnh JPEG, giúp tăng hoặc giảm chất lượng và kích thước của tệp đầu ra hình ảnh
Flash Mode 5 Bật, tắt đèn flash hoặc sử dụng chế độ cài đặt tự động
Color Effects 5 Áp dụng hiệu ứng màu cho hình ảnh đã chụp, chẳng hạn như đen trắng, tông màu nâu đỏ hoặc âm bản.
Anti-Banding 5 Giảm hiệu ứng phân dải trong các chuyển màu do quá trình nén JPEG
Picture Format 1 Chỉ định định dạng tệp cho hình ảnh
Picture Size 1 Chỉ định kích thước pixel của bức ảnh đã lưu

Lưu ý: Các tính năng này không được hỗ trợ trên một số thiết bị do sự khác biệt về phần cứng và cách triển khai phần mềm. Để biết thông tin về cách kiểm tra phạm vi cung cấp của các tính năng trên thiết bị mà ứng dụng của bạn đang chạy, hãy xem phần Kiểm tra phạm vi cung cấp của các tính năng.

Kiểm tra phạm vi cung cấp của các tính năng

Điều đầu tiên cần hiểu khi bắt đầu sử dụng các tính năng của camera trên thiết bị Android là không phải thiết bị nào cũng hỗ trợ tất cả các tính năng của camera. Ngoài ra, những thiết bị hỗ trợ một tính năng cụ thể có thể hỗ trợ tính năng đó ở nhiều cấp độ hoặc với nhiều lựa chọn. Do đó, một phần trong quy trình quyết định của bạn khi phát triển một ứng dụng camera là quyết định xem bạn muốn hỗ trợ những tính năng nào của camera và ở mức độ nào. Sau khi đưa ra quyết định đó, bạn nên lên kế hoạch đưa mã vào ứng dụng máy ảnh để kiểm tra xem phần cứng của thiết bị có hỗ trợ các tính năng đó hay không và thất bại một cách duyên dáng nếu một tính năng không có sẵn.

Bạn có thể kiểm tra khả năng hoạt động của các tính năng máy ảnh bằng cách lấy một thực thể của đối tượng tham số của máy ảnh và kiểm tra các phương thức có liên quan. Đoạn mã mẫu sau đây cho biết cách lấy đối tượng Camera.Parameters và kiểm tra xem camera có hỗ trợ tính năng tự động lấy nét hay không:

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
}

Bạn có thể sử dụng kỹ thuật nêu trên cho hầu hết các tính năng của camera. Đối tượng Camera.Parameters cung cấp phương thức getSupported...(), is...Supported() hoặc getMax...() để xác định xem một tính năng có được hỗ trợ hay không (và mức độ hỗ trợ).

Nếu ứng dụng của bạn cần một số tính năng nhất định của camera để hoạt động bình thường, bạn có thể yêu cầu các tính năng đó thông qua việc bổ sung vào tệp kê khai ứng dụng. Khi bạn khai báo việc sử dụng các tính năng cụ thể của camera, chẳng hạn như đèn flash và chế độ lấy nét tự động, Google Play sẽ hạn chế việc cài đặt ứng dụng của bạn trên những thiết bị không hỗ trợ các tính năng này. Để xem danh sách các tính năng của camera có thể được khai báo trong tệp kê khai ứng dụng, hãy xem Tài liệu tham khảo về các tính năng trong tệp kê khai.

Sử dụng các tính năng của camera

Hầu hết các tính năng của camera đều được kích hoạt và kiểm soát bằng đối tượng Camera.Parameters. Bạn có được đối tượng này bằng cách trước tiên lấy một thực thể của đối tượng Camera, gọi phương thức getParameters(), thay đổi đối tượng tham số được trả về rồi đặt lại vào đối tượng camera, như minh hoạ trong ví dụ về mã sau:

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

Kỹ thuật này hoạt động cho hầu hết các tính năng của camera và bạn có thể thay đổi hầu hết các tham số bất cứ lúc nào sau khi nhận được một thực thể của đối tượng Camera. Người dùng thường thấy ngay các thay đổi đối với thông số trong bản xem trước camera của ứng dụng. Về phía phần mềm, các thay đổi về thông số có thể mất vài khung hình để thực sự có hiệu lực khi phần cứng camera xử lý các chỉ dẫn mới rồi gửi dữ liệu hình ảnh đã cập nhật.

Lưu ý quan trọng: Bạn không thể tuỳ ý thay đổi một số tính năng của camera. Cụ thể, khi thay đổi kích thước hoặc hướng của bản xem trước camera, trước tiên, bạn phải dừng bản xem trước, thay đổi kích thước bản xem trước rồi khởi động lại bản xem trước. Kể từ Android 4.0 (API cấp 14), bạn có thể thay đổi hướng xem trước mà không cần khởi động lại bản xem trước.

Các tính năng khác của camera cần nhiều mã hơn để triển khai, bao gồm:

  • Đo sáng và vùng lấy nét
  • Phát hiện khuôn mặt
  • Video tua nhanh thời gian

Phần sau đây trình bày nhanh cách triển khai các tính năng này.

Đo sáng và vùng lấy nét

Trong một số trường hợp chụp ảnh, tính năng lấy nét tự động và đo sáng có thể không mang lại kết quả như mong muốn. Kể từ Android 4.0 (API cấp 14), ứng dụng máy ảnh của bạn có thể cung cấp các chế độ kiểm soát bổ sung để cho phép ứng dụng hoặc người dùng chỉ định các vùng trong hình ảnh để dùng cho việc xác định chế độ cài đặt tiêu điểm hoặc mức ánh sáng và truyền các giá trị này đến phần cứng máy ảnh để dùng trong việc chụp ảnh hoặc quay video.

Các vùng đo sáng và lấy nét hoạt động rất giống với các tính năng khác của camera, tức là bạn điều khiển chúng thông qua các phương thức trong đối tượng Camera.Parameters. Đoạn mã sau đây minh hoạ cách thiết lập 2 vùng đo sáng cho một thực thể 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);

Đối tượng Camera.Area chứa 2 tham số dữ liệu: Đối tượng Rect để chỉ định một vùng trong trường nhìn của camera và giá trị trọng số, cho biết mức độ quan trọng của vùng này trong phép đo sáng hoặc tính toán tiêu cự.

Trường Rect trong đối tượng Camera.Area mô tả một hình chữ nhật được ánh xạ trên lưới 2000 x 2000 đơn vị. Toạ độ -1000, -1000 biểu thị góc trên cùng bên trái của hình ảnh camera và toạ độ 1000, 1000 biểu thị góc dưới cùng bên phải của hình ảnh camera, như minh hoạ trong hình dưới đây.

Hình 1. Các đường màu đỏ minh hoạ hệ toạ độ để chỉ định một Camera.Area trong bản xem trước của camera. Hộp màu xanh dương cho biết vị trí và hình dạng của một khu vực camera có giá trị Rect là 333,333,667,667.

Ranh giới của hệ toạ độ này luôn tương ứng với cạnh ngoài của hình ảnh xuất hiện trong bản xem trước của camera và không thu hẹp hoặc mở rộng theo mức thu phóng. Tương tự, thao tác xoay bản xem trước hình ảnh bằng Camera.setDisplayOrientation() không ánh xạ lại hệ toạ độ.

Phát hiện khuôn mặt

Đối với những bức ảnh có người, khuôn mặt thường là phần quan trọng nhất của bức ảnh và nên được dùng để xác định cả tiêu điểm và cân bằng trắng khi chụp ảnh. Khung Android 4.0 (API cấp 14) cung cấp các API để xác định khuôn mặt và tính toán chế độ cài đặt hình ảnh bằng công nghệ nhận dạng khuôn mặt.

Lưu ý: Trong khi tính năng phát hiện khuôn mặt đang chạy, setWhiteBalance(String), setFocusAreas(List<Camera.Area>)setMeteringAreas(List<Camera.Area>) sẽ không có hiệu lực.

Để sử dụng tính năng phát hiện khuôn mặt trong ứng dụng camera, bạn cần thực hiện một số bước chung:

  • Kiểm tra để đảm bảo thiết bị có hỗ trợ tính năng phát hiện khuôn mặt
  • Tạo trình nghe phát hiện khuôn mặt
  • Thêm trình nghe phát hiện khuôn mặt vào đối tượng camera
  • Bắt đầu phát hiện khuôn mặt sau khi xem trước (và sau khi mỗi lần khởi động lại bản xem trước)

Tính năng phát hiện khuôn mặt không được hỗ trợ trên một số thiết bị. Bạn có thể kiểm tra xem tính năng này có được hỗ trợ hay không bằng cách gọi getMaxNumDetectedFaces(). Ví dụ về quy trình kiểm tra này được minh hoạ trong phương thức mẫu startFaceDetection() bên dưới.

Để được thông báo và phản hồi khi phát hiện thấy khuôn mặt, ứng dụng camera của bạn phải đặt một trình nghe cho các sự kiện phát hiện khuôn mặt. Để làm việc này, bạn phải tạo một lớp trình nghe triển khai giao diện Camera.FaceDetectionListener như trong mã ví dụ bên dưới.

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

Sau khi tạo lớp này, bạn sẽ đặt lớp đó vào đối tượng Camera của ứng dụng, như minh hoạ trong mã ví dụ bên dưới:

Kotlin

camera?.setFaceDetectionListener(MyFaceDetectionListener())

Java

camera.setFaceDetectionListener(new MyFaceDetectionListener());

Ứng dụng của bạn phải bắt đầu chức năng phát hiện khuôn mặt mỗi khi bạn bắt đầu (hoặc khởi động lại) bản xem trước của camera. Tạo một phương thức để bắt đầu phát hiện khuôn mặt để bạn có thể gọi phương thức này khi cần, như trong mã ví dụ bên dưới.

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

Bạn phải bắt đầu phát hiện khuôn mặt mỗi lần bạn bắt đầu (hoặc khởi động lại) bản xem trước của camera. Nếu bạn sử dụng lớp xem trước như minh hoạ trong phần Tạo lớp xem trước, hãy thêm phương thức startFaceDetection() vào cả phương thức surfaceCreated()surfaceChanged() trong lớp xem trước, như minh hoạ trong mã mẫu bên dưới.

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

Lưu ý: Nhớ gọi phương thức này sau khi gọi startPreview(). Đừng cố gắng bắt đầu tính năng phát hiện khuôn mặt trong phương thức onCreate() của hoạt động chính trong ứng dụng máy ảnh, vì bản xem trước không có sẵn tại thời điểm này trong quá trình thực thi ứng dụng.

Video tua nhanh thời gian

Video tua nhanh thời gian cho phép người dùng tạo các đoạn video kết hợp những bức ảnh được chụp cách nhau vài giây hoặc vài phút. Tính năng này sử dụng MediaRecorder để ghi lại hình ảnh cho một chuỗi tua nhanh thời gian.

Để quay video tua nhanh thời gian bằng MediaRecorder, bạn phải định cấu hình đối tượng trình ghi như thể bạn đang quay một video thông thường, đặt số khung hình được ghi mỗi giây ở mức thấp và sử dụng một trong các chế độ cài đặt chất lượng tua nhanh thời gian, như minh hoạ trong ví dụ về mã bên dưới.

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

Bạn phải thực hiện các chế độ cài đặt này trong quy trình định cấu hình lớn hơn cho MediaRecorder. Để biết ví dụ đầy đủ về mã cấu hình, hãy xem phần Định cấu hình MediaRecorder. Sau khi hoàn tất việc định cấu hình, bạn bắt đầu quay video như thể đang quay một đoạn video thông thường. Để biết thêm thông tin về cách định cấu hình và chạy MediaRecorder, hãy xem phần Quay video.

Các mẫu Camera2VideoHdrViewfinder minh hoạ thêm về việc sử dụng các API được trình bày trên trang này.

Các trường camera cần có quyền

Các ứng dụng chạy Android 10 (API cấp 29) trở lên phải có quyền CAMERA để truy cập vào các giá trị của những trường sau mà phương thức getCameraCharacteristics() trả về:

  • 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

Các đoạn mã mẫu khác

Để tải các ứng dụng mẫu, hãy xem mẫu Camera2BasicỨng dụng mẫu CameraX chính thức.