Cấu trúc CameraX

Trang này đề cập đến cấu trúc của CameraX, bao gồm cấu trúc, cách làm việc với API, cách làm việc với vòng đời và cách kết hợp các trường hợp sử dụng.

Cấu trúc CameraX

Bạn có thể sử dụng CameraX để giao tiếp với máy ảnh của thiết bị thông qua một giá trị trừu tượng được gọi là trường hợp sử dụng. Bạn có thể sử dụng các trường hợp sau:

  • Xem trước: chấp nhận một nền tảng để hiển thị bản xem trước, chẳng hạn như PreviewView.
  • Phân tích hình ảnh: cung cấp vùng đệm mà CPU có thể truy cập để phân tích, chẳng hạn như cho công nghệ học máy.
  • Chụp ảnh: chụp và lưu ảnh.
  • Quay video: quay video và ghi âm bằng VideoCapture

Các trường hợp sử dụng có thể được kết hợp và hoạt động đồng thời. Ví dụ: Một ứng dụng có thể cho phép người dùng xem hình ảnh mà máy ảnh thấy bằng trường hợp sử dụng xem trước, có trường hợp sử dụng phân tích hình ảnh xác định liệu những người trong ảnh có đang cười hay không và đưa vào một trường hợp sử dụng chụp ảnh để chụp khi mọi người đã sẵn sàng.

Mô hình API

Để làm việc với thư viện, bạn cần chỉ định những điều sau:

  • Trường hợp sử dụng mong muốn với các tuỳ chọn cấu hình.
  • Việc cần làm với dữ liệu đầu ra bằng cách đính kèm trình nghe.
  • Quy trình dự kiến, chẳng hạn như thời điểm bật máy ảnh và thời điểm tạo dữ liệu, bằng cách liên kết trường hợp sử dụng với Vòng đời kiến trúc Android.

Có 2 cách để viết một ứng dụng CameraX: CameraController (phù hợp nếu bạn muốn sử dụng CameraX theo cách đơn giản nhất) hoặc CameraProvider (phù hợp nếu bạn cần tăng tính linh hoạt).

CameraController

CameraController cung cấp hầu hết các chức năng cốt lõi của CameraX trong một lớp. Lớp này yêu cầu ít mã thiết lập và tự động xử lý việc khởi chạy máy ảnh, quản lý trường hợp sử dụng, xoay đích, nhấn để lấy nét, chụm để thu phóng và nhiều tác vụ khác. Lớp cụ thể giúp mở rộng CameraControllerLifecycleCameraController.

Kotlin

val previewView: PreviewView = viewBinding.previewView
var cameraController = LifecycleCameraController(baseContext)
cameraController.bindToLifecycle(this)
cameraController.cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
previewView.controller = cameraController

Java

PreviewView previewView = viewBinding.previewView;
LifecycleCameraController cameraController = new LifecycleCameraController(baseContext);
cameraController.bindToLifecycle(this);
cameraController.setCameraSelector(CameraSelector.DEFAULT_BACK_CAMERA);
previewView.setController(cameraController);

UseCase mặc định cho CameraControllerPreview, ImageCaptureImageAnalysis. Để tắt ImageCapture hoặc ImageAnalysis hay để bật VideoCapture, hãy dùng phương thức setEnabledUseCases().

Để biết thêm về cách sử dụng CameraController, hãy xem mẫu trình quét mã QR hoặc video kiến thức cơ bản về CameraController.

CameraProvider

CameraProvider vẫn dễ sử dụng, nhưng vì nhà phát triển ứng dụng xử lý nhiều vấn đề hơn trong khâu thiết lập, nên khả năng tuỳ chỉnh cấu hình sẽ lớn hơn, chẳng hạn như bật chế độ xoay hình ảnh đầu ra hoặc đặt định dạng hình ảnh đầu ra trong ImageAnalysis. Bạn cũng có thể dùng Surface tuỳ chỉnh cho trường hợp xem trước máy ảnh nhằm tăng tính linh hoạt, còn đối với CameraController, bạn bắt buộc phải dùng PreviewView. Việc dùng Surface hiện có có thể hữu ích nếu đoạn mã này đã là dữ liệu đầu vào cho các phần khác của ứng dụng.

Bạn định cấu hình các trường hợp sử dụng bằng các phương thức set() và hoàn tất các trường hợp sử dụng đó bằng phương thức build(). Mỗi đối tượng trường hợp sử dụng cung cấp một tập hợp API dành riêng cho trường hợp sử dụng. Ví dụ: Trường hợp sử dụng chụp ảnh cung cấp lệnh gọi phương thức takePicture().

Thay vì thực hiện các lệnh gọi phương thức bắt đầu và dừng cụ thể trong onResume()onPause(), ứng dụng này chỉ định vòng đời để liên kết máy ảnh bằng cách sử dụng cameraProvider.bindToLifecycle(). Sau đó, vòng đời thông báo cho CameraX về thời điểm cần định cấu hình phiên chụp ảnh và đảm bảo trạng thái của máy ảnh thay đổi phù hợp để khớp với các chuyển đổi vòng đời.

Để biết các bước triển khai cho từng trường hợp sử dụng, xem bài viết Triển khai bản xem trước, Phân tích hình ảnh, Chụp ảnhQuay video

Trường hợp sử dụng xem trước tương tác với một Surface để hiển thị. Ứng dụng tạo trường hợp sử dụng với các tuỳ chọn cấu hình bằng đoạn mã sau:

Kotlin

val preview = Preview.Builder().build()
val viewFinder: PreviewView = findViewById(R.id.previewView)

// The use case is bound to an Android Lifecycle with the following code
val camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)

// PreviewView creates a surface provider and is the recommended provider
preview.setSurfaceProvider(viewFinder.getSurfaceProvider())

Java

Preview preview = new Preview.Builder().build();
PreviewView viewFinder = findViewById(R.id.view_finder);

// The use case is bound to an Android Lifecycle with the following code
Camera camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview);

// PreviewView creates a surface provider, using a Surface from a different
// kind of view will require you to implement your own surface provider.
preview.previewSurfaceProvider = viewFinder.getSurfaceProvider();

Để xem thêm đoạn mã ví dụ, hãy đọc bài viết ứng dụng mẫu CameraX chính thức.

CameraX Lifecycle

CameraX theo dõi vòng đời để xác định thời điểm mở máy ảnh, thời điểm tạo phiên chụp, cũng như thời điểm dừng và tắt. API trường hợp sử dụng cung cấp các lệnh gọi phương thức và lệnh gọi lại để theo dõi tiến trình.

Như giải thích trong phần Kết hợp các trường hợp sử dụng, bạn có thể liên kết một số danh sách kết hợp các trường hợp sử dụng với một vòng đời duy nhất. Khi ứng dụng của bạn cần hỗ trợ các trường hợp sử dụng không thể kết hợp được, bạn có thể thực hiện một trong những thao tác sau:

  • Nhóm các trường hợp sử dụng tương thích lại với nhau thành nhiều mảnh, sau đó chuyển đổi giữa các mảnh
  • Tạo một thành phần trong vòng đời tuỳ chỉnh và sử dụng thành phần đó để điều khiển vòng đời của máy ảnh theo cách thủ công

Nếu bạn tách riêng các chủ sở hữu Lifecycle của trường hợp sử dụng xem và máy ảnh (ví dụ: nếu bạn sử dụng vòng đời tuỳ chỉnh hoặc mảnh giữ lại), bạn phải đảm bảo rằng tất cả các trường hợp sử dụng huỷ liên kết với CameraX bằng cách sử dụng ProcessCameraProvider.unbindAll() hoặc bằng cách huỷ liên kết từng trường hợp sử dụng một. Ngoài ra, khi liên kết các trường hợp sử dụng với Lifecycle, bạn có thể cho phép CameraX quản lý việc mở và đóng phiên chụp và huỷ liên kết các trường hợp sử dụng.

Nếu tất cả chức năng của máy ảnh tương ứng với vòng đời của một thành phần nhận biết vòng đời duy nhất, chẳng hạn như AppCompatActivity hoặc một mảnh AppCompat, thì việc sử dụng vòng đời của thành phần đó khi liên kết tất cả các trường hợp sử dụng mong muốn sẽ đảm bảo rằng chức năng của máy ảnh đã sẵn sàng khi thành phần nhận biết vòng đời hoạt động và được xử lý một cách an toàn, không tốn bất kỳ tài nguyên nào.

LifecycleOwner tuỳ chỉnh

Đối với các trường hợp nâng cao, bạn có thể tạo một LifecycleOwner tuỳ chỉnh để cho phép ứng dụng của bạn kiểm soát rõ ràng vòng đời phiên CameraX thay vì liên kết với LifecycleOwner tiêu chuẩn của Android.

Đoạn mã mẫu sau đây trình bày cách tạo một LifecycleOwner tuỳ chỉnh đơn giản:

Kotlin

class CustomLifecycle : LifecycleOwner {
    private val lifecycleRegistry: LifecycleRegistry

    init {
        lifecycleRegistry = LifecycleRegistry(this);
        lifecycleRegistry.markState(Lifecycle.State.CREATED)
    }
    ...
    fun doOnResume() {
        lifecycleRegistry.markState(State.RESUMED)
    }
    ...
    override fun getLifecycle(): Lifecycle {
        return lifecycleRegistry
    }
}

Java

public class CustomLifecycle implements LifecycleOwner {
    private LifecycleRegistry lifecycleRegistry;
    public CustomLifecycle() {
        lifecycleRegistry = new LifecycleRegistry(this);
        lifecycleRegistry.markState(Lifecycle.State.CREATED);
    }
   ...
   public void doOnResume() {
        lifecycleRegistry.markState(State.RESUMED);
    }
   ...
    public Lifecycle getLifecycle() {
        return lifecycleRegistry;
    }
}

Khi sử dụng LifecycleOwner này, ứng dụng của bạn có thể đặt các lượt chuyển đổi trạng thái tại các điểm mong muốn trong mã nguồn. Để biết thêm thông tin về cách triển khai chức năng này trong ứng dụng của bạn, hãy xem phần Triển khai một LifecycleOwner tuỳ chỉnh.

Các trường hợp sử dụng đồng thời

Các trường hợp sử dụng có thể chạy đồng thời. Mặc dù các trường hợp sử dụng có thể được liên kết tuần tự với một vòng đời, nhưng bạn nên liên kết tất cả các trường hợp sử dụng bằng một lệnh gọi tới CameraProcessProvider.bindToLifecycle(). Để biết thêm thông tin về các phương pháp hay nhất khi thay đổi cấu hình, xem phần Xử lý các thay đổi về cấu hình.

Trong đoạn mã mẫu sau, ứng dụng chỉ định hai trường hợp sử dụng cần được tạo và chạy đồng thời. Đoạn mã mẫu này cũng xác định vòng đời cần sử dụng cho cả hai trường hợp sử dụng, để cả hai đều bắt đầu và dừng theo vòng đời đó.

Kotlin

private lateinit var imageCapture: ImageCapture

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

    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

    cameraProviderFuture.addListener(Runnable {
        // Camera provider is now guaranteed to be available
        val cameraProvider = cameraProviderFuture.get()

        // Set up the preview use case to display camera preview.
        val preview = Preview.Builder().build()

        // Set up the capture use case to allow users to take photos.
        imageCapture = ImageCapture.Builder()
                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                .build()

        // Choose the camera by requiring a lens facing
        val cameraSelector = CameraSelector.Builder()
                .requireLensFacing(CameraSelector.LENS_FACING_FRONT)
                .build()

        // Attach use cases to the camera with the same lifecycle owner
        val camera = cameraProvider.bindToLifecycle(
                this as LifecycleOwner, cameraSelector, preview, imageCapture)

        // Connect the preview use case to the previewView
        preview.setSurfaceProvider(
                previewView.getSurfaceProvider())
    }, ContextCompat.getMainExecutor(this))
}

Java

private ImageCapture imageCapture;

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

    PreviewView previewView = findViewById(R.id.previewView);

    ListenableFuture<ProcessCameraProvider> cameraProviderFuture =
            ProcessCameraProvider.getInstance(this);

    cameraProviderFuture.addListener(() -> {
        try {
            // Camera provider is now guaranteed to be available
            ProcessCameraProvider cameraProvider = cameraProviderFuture.get();

            // Set up the view finder use case to display camera preview
            Preview preview = new Preview.Builder().build();

            // Set up the capture use case to allow users to take photos
            imageCapture = new ImageCapture.Builder()
                    .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                    .build();

            // Choose the camera by requiring a lens facing
            CameraSelector cameraSelector = new CameraSelector.Builder()
                    .requireLensFacing(lensFacing)
                    .build();

            // Attach use cases to the camera with the same lifecycle owner
            Camera camera = cameraProvider.bindToLifecycle(
                    ((LifecycleOwner) this),
                    cameraSelector,
                    preview,
                    imageCapture);

            // Connect the preview use case to the previewView
            preview.setSurfaceProvider(
                    previewView.getSurfaceProvider());
        } catch (InterruptedException | ExecutionException e) {
            // Currently no exceptions thrown. cameraProviderFuture.get()
            // shouldn't block since the listener is being called, so no need to
            // handle InterruptedException.
        }
    }, ContextCompat.getMainExecutor(this));
}

Chúng tôi đảm bảo hỗ trợ các tổ hợp cấu hình sau đây (khi yêu cầu trường hợp sử dụng Xem trước hoặc Quay video, nhưng không yêu cầu cả hai cùng một lúc):

Xem trước hoặc Quay video Chụp ảnh Phân tích Mô tả
Cung cấp cho người dùng tính năng xem trước hoặc quay video, chụp ảnh và phân tích luồng hình ảnh.
  Chụp ảnh và phân tích luồng hình ảnh.
  Cung cấp cho người dùng tính năng xem trước hoặc quay video, chụp ảnh.
  Cung cấp cho người dùng tính năng xem trước hoặc quay video, cũng như phân tích luồng hình ảnh.

Khi yêu cầu cả Xem trước và Quay video, các tổ hợp trường hợp sử dụng sau đây được hỗ trợ theo điều kiện:

Xem trước Quay video Chụp ảnh Phân tích Yêu cầu đặc biệt
    Được đảm bảo cho tất cả máy ảnh
  Thiết bị máy ảnh LIMITED (hoặc cao hơn).
  Thiết bị máy ảnh LEVEL_3 (hoặc cao hơn).

Ngoài ra,

  • Mỗi trường hợp sử dụng đều có thể tự hoạt động. Ví dụ: Một ứng dụng có thể quay video mà không cần sử dụng tính năng xem trước.
  • Khi các tiện ích được bật, chỉ tổ hợp ImageCapturePreview là đảm bảo hoạt động. Tuỳ thuộc vào cách triển khai của OEM (Nhà sản xuất thiết bị gốc), bạn cũng có thể không thêm được ImageAnalysis; không thể bật tiện ích cho trường hợp sử dụng VideoCapture. Xem Tài liệu tham khảo về tiện ích để biết thông tin chi tiết.
  • Tuỳ thuộc vào tính năng của máy ảnh, một số máy ảnh có thể hỗ trợ tổ hợp ở các chế độ độ phân giải thấp nhưng không thể hỗ trợ sử dụng cùng tổ hợp đó ở độ phân giải cao.

Bạn có thể truy xuất cấp độ phần cứng được hỗ trợ từ Camera2CameraInfo. Ví dụ: Đoạn mã sau đây kiểm tra xem máy ảnh mặt sau mặc định có phải là thiết bị LEVEL_3 không:

Kotlin

@androidx.annotation.OptIn(ExperimentalCamera2Interop::class)
fun isBackCameraLevel3Device(cameraProvider: ProcessCameraProvider) : Boolean {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        return CameraSelector.DEFAULT_BACK_CAMERA
            .filter(cameraProvider.availableCameraInfos)
            .firstOrNull()
            ?.let { Camera2CameraInfo.from(it) }
            ?.getCameraCharacteristic(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) ==
            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
    }
    return false
}

Java

@androidx.annotation.OptIn(markerClass = ExperimentalCamera2Interop.class)
Boolean isBackCameraLevel3Device(ProcessCameraProvider cameraProvider) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        List\ filteredCameraInfos = CameraSelector.DEFAULT_BACK_CAMERA
                .filter(cameraProvider.getAvailableCameraInfos());
        if (!filteredCameraInfos.isEmpty()) {
            return Objects.equals(
                Camera2CameraInfo.from(filteredCameraInfos.get(0)).getCameraCharacteristic(
                        CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL),
                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3);
        }
    }
    return false;
}

Quyền

Ứng dụng của bạn sẽ cần được cấp quyền CAMERA. Để lưu hình ảnh vào tệp, bạn cũng cần có quyền WRITE_EXTERNAL_STORAGE, ngoại trừ trên các thiết bị chạy Android 10 trở lên.

Để biết thêm thông tin về cách định cấu hình quyền cho ứng dụng của bạn, đọc bài viết Yêu cầu quyền cho ứng dụng.

Yêu cầu

CameraX có các yêu cầu tối thiểu về phiên bản sau đây:

  • Android API cấp 21
  • Bộ thành phần cấu trúc Android 1.1.1

Đối với các hoạt động nhận biết vòng đời, sử dụng FragmentActivity hoặc AppCompatActivity.

Khai báo phần phụ thuộc

Để thêm một phần phụ thuộc trên CameraX, bạn phải thêm kho lưu trữ Google Maven vào dự án.

Mở tệp settings.gradle cho dự án của bạn và thêm kho lưu trữ google() như trong đoạn mã sau:

Groovy

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

Kotlin

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

Thêm đoạn mã sau vào cuối khối lệnh Android:

Groovy

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    // For Kotlin projects
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

Kotlin

android {
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    // For Kotlin projects
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

Thêm phần sau vào tệp build.gradle của từng mô-đun cho một ứng dụng:

Groovy

dependencies {
  // CameraX core library using the camera2 implementation
  def camerax_version = "1.3.0-alpha04"
  // The following line is optional, as the core library is included indirectly by camera-camera2
  implementation "androidx.camera:camera-core:${camerax_version}"
  implementation "androidx.camera:camera-camera2:${camerax_version}"
  // If you want to additionally use the CameraX Lifecycle library
  implementation "androidx.camera:camera-lifecycle:${camerax_version}"
  // If you want to additionally use the CameraX VideoCapture library
  implementation "androidx.camera:camera-video:${camerax_version}"
  // If you want to additionally use the CameraX View class
  implementation "androidx.camera:camera-view:${camerax_version}"
  // If you want to additionally add CameraX ML Kit Vision Integration
  implementation "androidx.camera:camera-mlkit-vision:${camerax_version}"
  // If you want to additionally use the CameraX Extensions library
  implementation "androidx.camera:camera-extensions:${camerax_version}"
}

Kotlin

dependencies {
    // CameraX core library using the camera2 implementation
    val camerax_version = "1.3.0-alpha04"
    // The following line is optional, as the core library is included indirectly by camera-camera2
    implementation("androidx.camera:camera-core:${camerax_version}")
    implementation("androidx.camera:camera-camera2:${camerax_version}")
    // If you want to additionally use the CameraX Lifecycle library
    implementation("androidx.camera:camera-lifecycle:${camerax_version}")
    // If you want to additionally use the CameraX VideoCapture library
    implementation("androidx.camera:camera-video:${camerax_version}")
    // If you want to additionally use the CameraX View class
    implementation("androidx.camera:camera-view:${camerax_version}")
    // If you want to additionally add CameraX ML Kit Vision Integration
    implementation("androidx.camera:camera-mlkit-vision:${camerax_version}")
    // If you want to additionally use the CameraX Extensions library
    implementation("androidx.camera:camera-extensions:${camerax_version}")
}

Để biết thêm thông tin về cách định cấu hình ứng dụng của bạn nhằm tuân thủ các yêu cầu này, hãy xem bài viết Khai báo phần phụ thuộc.

Khả năng tương tác của CameraX với Camera2

CameraX được tạo dựa trên Camera2. CameraX cho biết các cách đọc và thậm chí là ghi các thuộc tính trong quá trình triển khai Camera2. Để biết toàn bộ thông tin chi tiết, hãy xem bài viết về Gói khả năng tương tác.

Để biết thêm thông tin về cách CameraX định cấu hình các thuộc tính của Camera2, hãy dùng Camera2CameraInfo để đọc CameraCharacteristics cơ bản. Bạn cũng có thể chọn ghi các thuộc tính của Camera2 cơ bản vào một trong hai đường dẫn sau:

Mã mẫu sau đây dùng các trường hợp sử dụng phát trực tuyến để tối ưu hoá cho một cuộc gọi video. Dùng Camera2CameraInfo để tìm nạp xem có trường hợp sử dụng truyền phát cuộc gọi video hay không. Sau đó, hãy dùng Camera2Interop.Extender để đặt trường hợp sử dụng phát trực tuyến cơ bản.

Kotlin

// Set underlying Camera2 stream use case to optimize for video calls.

val videoCallStreamId =
    CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL.toLong()

// Check available CameraInfos to find the first one that supports
// the video call stream use case.
val frontCameraInfo = cameraProvider.getAvailableCameraInfos()
    .first { cameraInfo ->
        val isVideoCallStreamingSupported = Camera2CameraInfo.from(cameraInfo)
            .getCameraCharacteristic(
                CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES
            )?.contains(videoCallStreamId)
        val isFrontFacing = (cameraInfo.getLensFacing() == 
                             CameraSelector.LENS_FACING_FRONT)
        (isVideoCallStreamingSupported == true) && isFrontFacing
    }

val cameraSelector = frontCameraInfo.cameraSelector

// Start with a Preview Builder.
val previewBuilder = Preview.Builder()
    .setTargetAspectRatio(screenAspectRatio)
    .setTargetRotation(rotation)

// Use Camera2Interop.Extender to set the video call stream use case.
Camera2Interop.Extender(previewBuilder).setStreamUseCase(videoCallStreamId)

// Bind the Preview UseCase and the corresponding CameraSelector.
val preview = previewBuilder.build()
camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview)

Java

// Set underlying Camera2 stream use case to optimize for video calls.

Long videoCallStreamId =
    CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL.toLong();

// Check available CameraInfos to find the first one that supports
// the video call stream use case.
List<CameraInfo> cameraInfos = cameraProvider.getAvailableCameraInfos();
CameraInfo frontCameraInfo = null;
for (cameraInfo in cameraInfos) {
    Long[] availableStreamUseCases = Camera2CameraInfo.from(cameraInfo)
        .getCameraCharacteristic(
            CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES
        );
    boolean isVideoCallStreamingSupported = Arrays.List(availableStreamUseCases)
                .contains(videoCallStreamId);
    boolean isFrontFacing = (cameraInfo.getLensFacing() ==
                             CameraSelector.LENS_FACING_FRONT);

    if (isVideoCallStreamingSupported && isFrontFacing) {
        frontCameraInfo = cameraInfo;
    }
}

if (frontCameraInfo == null) {
    // Handle case where video call streaming is not supported.
}

CameraSelector cameraSelector = frontCameraInfo.getCameraSelector();

// Start with a Preview Builder.
Preview.Builder previewBuilder = Preview.Builder()
    .setTargetAspectRatio(screenAspectRatio)
    .setTargetRotation(rotation);

// Use Camera2Interop.Extender to set the video call stream use case.
Camera2Interop.Extender(previewBuilder).setStreamUseCase(videoCallStreamId);

// Bind the Preview UseCase and the corresponding CameraSelector.
Preview preview = previewBuilder.build()
Camera camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview)

Tài nguyên khác

Để tìm hiểu thêm về CameraX, hãy tham khảo các tài liệu bổ sung sau đây.

Lớp học lập trình

  • Bắt đầu sử dụng CameraX
  • Đoạn mã mẫu

  • Ứng dụng mẫu CameraX