Yêu cầu và phiên chụp của máy ảnh

Lưu ý: Trang này đề cập đến gói Camera2. Trừ phi ứng dụng của bạn yêu cầu các tính năng cụ thể ở cấp độ thấp từ Camera2, bạn nên sử dụng CameraX. Cả CameraX và Camera2 đều hỗ trợ Android 5.0 (API cấp 21) trở lên.

Một thiết bị chạy Android có thể có nhiều máy ảnh. Mỗi camera là một CameraDevice! và CameraDevice có thể xuất nhiều luồng cùng lúc.

Một lý do để làm điều này là để một luồng, các khung hình máy ảnh tuần tự xuất hiện từ CameraDevice được tối ưu hoá cho một tác vụ cụ thể, chẳng hạn như hiển thị kính ngắm, trong khi những người khác có thể dùng để chụp ảnh hoặc tạo video ghi lại.Các luồng hoạt động như các quy trình song song xử lý khung hình thô ra khỏi máy ảnh lần lượt từng khung hình:

Hình 1. Hình minh hoạ quá trình Xây dựng ứng dụng máy ảnh toàn cầu (Google I/O năm 2018)

Quá trình xử lý song song cho thấy có thể có giới hạn hiệu suất phụ thuộc vào công suất xử lý có sẵn từ CPU, GPU hoặc bộ xử lý khác. Nếu một quy trình không thể theo kịp các khung hình đến, nó sẽ bắt đầu bỏ các khung hình đó.

Mỗi quy trình có định dạng đầu ra riêng. Dữ liệu thô được thu thập là đã được tự động chuyển đổi thành định dạng đầu ra bằng logic ngầm ẩn liên kết với mỗi quy trình. CameraDevice được sử dụng trong toàn bộ thời gian của trang này mã mẫu là không cụ thể, vì vậy trước tiên bạn phải liệt kê tất cả các máy ảnh có sẵn trước khi tiếp tục.

Bạn có thể sử dụng CameraDevice để tạo CameraCaptureSession, dành riêng cho CameraDevice đó. CameraDevice phải nhận được cấu hình khung cho từng khung thô bằng cách sử dụng CameraCaptureSession. Chiến lược phát hành đĩa đơn cấu hình chỉ định các thuộc tính của máy ảnh như tự động lấy nét, khẩu độ, hiệu ứng, và độ phơi sáng. Do hạn chế về phần cứng, nên chỉ có một cấu hình duy nhất hoạt động trong cảm biến của máy ảnh tại một thời điểm bất kỳ, được gọi là đang hoạt động.

Tuy nhiên, các Trường hợp sử dụng phát trực tuyến giúp nâng cao và mở rộng các cách sử dụng CameraDevice trước đây để phát trực tuyến các phiên chụp, cho phép bạn tối ưu hóa luồng máy ảnh cho trường hợp sử dụng cụ thể. Ví dụ: tính năng này có thể cải thiện thời lượng pin khi tối ưu hoá cuộc gọi video.

CameraCaptureSession mô tả tất cả các quy trình có thể liên kết với CameraDevice. Khi một phiên được tạo, bạn không thể thêm hoặc xoá quy trình. CameraCaptureSession duy trì một hàng đợi CaptureRequest! trở thành cấu hình hoạt động.

CaptureRequest thêm cấu hình vào hàng đợi và chọn một cấu hình, hơn một hoặc tất cả các quy trình có sẵn để nhận khung từ CameraDevice. Bạn có thể gửi nhiều yêu cầu chụp trong suốt thời gian chụp phiên hoạt động. Mỗi yêu cầu có thể thay đổi cấu hình đang hoạt động và tập hợp đầu ra các đường ống nhận hình ảnh thô.

Sử dụng các trường hợp sử dụng phát trực tuyến để đạt được hiệu suất tốt hơn

Các trường hợp sử dụng phát trực tuyến là một cách để cải thiện hiệu suất chụp của Camera2 phiên hoạt động. Chúng cung cấp thêm thông tin cho thiết bị phần cứng để điều chỉnh thông số, mang lại trải nghiệm camera tốt hơn cho tác vụ cụ thể của bạn.

Chiến dịch này cho phép thiết bị máy ảnh tối ưu hoá quy trình phần cứng và phần mềm của máy ảnh dựa trên tình huống của người dùng đối với từng luồng. Để biết thêm thông tin về việc sử dụng luồng Các trường hợp, xem setStreamUseCase.

Các trường hợp sử dụng phát trực tuyến cho phép bạn chỉ định cách sử dụng một luồng camera cụ thể trong chi tiết hơn, ngoài việc thiết lập mẫu trong CameraDevice.createCaptureRequest(). Việc này giúp phần cứng của máy ảnh tối ưu hoá các thông số, chẳng hạn như cách điều chỉnh, chế độ cảm biến hoặc cài đặt cảm biến của máy ảnh, dựa trên chất lượng hoặc độ trễ phù hợp với các trường hợp sử dụng cụ thể.

Các trường hợp sử dụng phát trực tuyến bao gồm:

  • DEFAULT: Bao gồm mọi hành vi hiện có của ứng dụng. Điều này tương đương với đặt bất kỳ Trường hợp sử dụng phát trực tuyến nào.

  • PREVIEW: Nên dùng cho kính ngắm hoặc phân tích hình ảnh trong ứng dụng.

  • STILL_CAPTURE: Được tối ưu hoá để chụp ảnh có độ phân giải cao chất lượng cao, và không duy trì tốc độ khung hình giống như bản xem trước.

  • VIDEO_RECORD: Được tối ưu hoá để quay video chất lượng cao, bao gồm cả video chất lượng cao ổn định hình ảnh, nếu được thiết bị hỗ trợ và được ứng dụng bật. Tùy chọn này có thể tạo ra các khung hình đầu ra có độ trễ đáng kể so với thời gian thực, để cho phép ổn định chất lượng cao nhất hoặc xử lý khác.

  • VIDEO_CALL: Nên dùng cho các camera sử dụng trong thời gian dài ở nơi tiêu hao pin mối lo ngại.

  • PREVIEW_VIDEO_STILL: Nên dùng cho các ứng dụng mạng xã hội hoặc một lần phát trực tuyến trường hợp. Đây là một luồng đa năng.

  • VENDOR_START: Dùng cho các trường hợp sử dụng do OEM (Nhà sản xuất thiết bị gốc) xác định.

Tạo CameraCaptureSession

Để tạo một phiên máy ảnh, hãy cung cấp cho phiên đó một hoặc nhiều vùng đệm đầu ra ứng dụng của bạn có thể ghi các khung đầu ra vào đó. Mỗi vùng đệm đại diện cho một quy trình. Bạn phải hãy làm việc này trước khi bắt đầu sử dụng camera để khung có thể định cấu hình kênh nội bộ của thiết bị và phân bổ vùng đệm bộ nhớ để gửi khung cho các mục tiêu đầu ra cần thiết.

Đoạn mã sau đây cho biết cách chuẩn bị một phiên camera với hai vùng đệm đầu ra, một vùng đệm thuộc về SurfaceView và một URL khác đến một ImageReader. Thêm PREVIEW trường hợp sử dụng phát trực tuyến vào previewSurfaceSTILL_CAPTURE Sử dụng luồng Trường hợp imReaderSurface cho phép phần cứng thiết bị tối ưu hoá những luồng này ngay cả khi thực tế hơn.

Kotlin


// Retrieve the target surfaces, which might be coming from a number of places:
// 1. SurfaceView, if you want to display the image directly to the user
// 2. ImageReader, if you want to read each frame or perform frame-by-frame
// analysis
// 3. OpenGL Texture or TextureView, although discouraged for maintainability
      reasons
// 4. RenderScript.Allocation, if you want to do parallel processing
val surfaceView = findViewById<SurfaceView>(...)
val imageReader = ImageReader.newInstance(...)

// Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated()
val previewSurface = surfaceView.holder.surface
val imReaderSurface = imageReader.surface
val targets = listOf(previewSurface, imReaderSurface)

// Create a capture session using the predefined targets; this also involves
// defining the session state callback to be notified of when the session is
// ready
// Setup Stream Use Case while setting up your Output Configuration.
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun configureSession(device: CameraDevice, targets: List<Surface>){
    val configs = mutableListOf<OutputConfiguration>()
    val streamUseCase = CameraMetadata
        .SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL

    targets.forEach {
        val config = OutputConfiguration(it)
        config.streamUseCase = streamUseCase.toLong()
        configs.add(config)
    }
    ...
    device.createCaptureSession(session)
}

Java


// Retrieve the target surfaces, which might be coming from a number of places:
// 1. SurfaceView, if you want to display the image directly to the user
// 2. ImageReader, if you want to read each frame or perform frame-by-frame
      analysis
// 3. RenderScript.Allocation, if you want to do parallel processing
// 4. OpenGL Texture or TextureView, although discouraged for maintainability
      reasons
Surface surfaceView = findViewById<SurfaceView>(...);
ImageReader imageReader = ImageReader.newInstance(...);

// Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated()
Surface previewSurface = surfaceView.getHolder().getSurface();
Surface imageSurface = imageReader.getSurface();
List<Surface> targets = Arrays.asList(previewSurface, imageSurface);

// Create a capture session using the predefined targets; this also involves defining the
// session state callback to be notified of when the session is ready
private void configureSession(CameraDevice device, List<Surface> targets){
    ArrayList<OutputConfiguration> configs= new ArrayList()
    String streamUseCase=  CameraMetadata
        .SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL

    for(Surface s : targets){
        OutputConfiguration config = new OutputConfiguration(s)
        config.setStreamUseCase(String.toLong(streamUseCase))
        configs.add(config)
}

device.createCaptureSession(session)
}

Đến đây, bạn chưa xác định được cấu hình đang hoạt động của camera. Khi phiên được định cấu hình, bạn có thể tạo và gửi ảnh chụp yêu cầu thực hiện việc đó.

Phép biến đổi được áp dụng cho dữ liệu đầu vào khi chúng được ghi vào bộ đệm là được xác định theo loại của mỗi mục tiêu, điều này phải là Surface. Khung Android biết cách chuyển đổi hình ảnh thô trong cấu hình đang hoạt động sang định dạng phù hợp cho từng mục tiêu. Việc chuyển đổi được kiểm soát bởi định dạng và kích thước pixel của Surface cụ thể.

Khung này sẽ cố gắng hết sức, nhưng một số Surface có thể không hoạt động, gây ra các sự cố như phiên không được tạo, báo lỗi thời gian chạy khi bạn gửi yêu cầu, hoặc suy giảm hiệu suất. Khung này đưa ra đảm bảo cho các các tổ hợp tham số thiết bị, giao diện và yêu cầu. Tài liệu về createCaptureSession() sẽ cung cấp thêm thông tin.

CaptureRequests duy nhất

Cấu hình dùng cho mỗi khung được mã hoá trong CaptureRequest, gửi đến máy ảnh. Để tạo một yêu cầu chụp ảnh, bạn có thể sử dụng một trong được xác định trước template (mẫu), hoặc bạn có thể sử dụng TEMPLATE_MANUAL để có toàn quyền kiểm soát. Khi bạn chọn một mẫu, bạn cần cung cấp một hoặc nhiều vùng đệm đầu ra để sử dụng cùng yêu cầu. Bạn chỉ có thể sử dụng vùng đệm đã được xác định khi chụp mà bạn dự định sử dụng.

Yêu cầu chụp ảnh sử dụng trình tạo mô hình đồng thời mang lại cho nhà phát triển cơ hội đặt nhiều kiểu các lựa chọn bao gồm tự động phơi sáng, tự động lấy nét, và khẩu độ ống kính. Trước khi đặt một trường, hãy đảm bảo rằng có sẵn tuỳ chọn cụ thể cho thiết bị bằng cách gọi CameraCharacteristics.getAvailableCaptureRequestKeys() và chọn camera phù hợp để hỗ trợ giá trị mong muốn đặc tính, chẳng hạn như độ phơi sáng tự động có sẵn chế độ.

Để tạo yêu cầu chụp cho SurfaceView bằng mẫu được thiết kế để xem trước mà không sửa đổi, hãy sử dụng CameraDevice.TEMPLATE_PREVIEW:

Kotlin

val session: CameraCaptureSession = ...  // from CameraCaptureSession.StateCallback
val captureRequest = session.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
captureRequest.addTarget(previewSurface)

Java

CameraCaptureSession session = ...;  // from CameraCaptureSession.StateCallback
CaptureRequest.Builder captureRequest =
    session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureRequest.addTarget(previewSurface);

Khi xác định được yêu cầu chụp, bạn hiện có thể gửi video đó vào phiên camera:

Kotlin

val session: CameraCaptureSession = ...  // from CameraCaptureSession.StateCallback
val captureRequest: CaptureRequest = ...  // from CameraDevice.createCaptureRequest()

// The first null argument corresponds to the capture callback, which you
// provide if you want to retrieve frame metadata or keep track of failed capture
// requests that can indicate dropped frames; the second null argument
// corresponds to the Handler used by the asynchronous callback, which falls
// back to the current thread's looper if null
session.capture(captureRequest.build(), null, null)

Java

CameraCaptureSession session = ...;  // from CameraCaptureSession.StateCallback
CaptureRequest captureRequest = ...;  // from CameraDevice.createCaptureRequest()

// The first null argument corresponds to the capture callback, which you
// provide if you want to retrieve frame metadata or keep track of failed
// capture
// requests that can indicate dropped frames; the second null argument
// corresponds to the Handler used by the asynchronous callback, which falls
// back to the current thread's looper if null
session.capture(captureRequest.build(), null, null);

Khi một khung hình đầu ra được đưa vào vùng đệm cụ thể, một sẽ chụp lệnh gọi lại được kích hoạt. Trong nhiều trường hợp, các lệnh gọi lại bổ sung, chẳng hạn như ImageReader.OnImageAvailableListener! được kích hoạt khi khung có chứa được xử lý. Bây giờ là lúc điểm này là bạn có thể truy xuất dữ liệu hình ảnh từ vùng đệm được chỉ định.

Lặp lại CaptureRequests

Các yêu cầu về camera đơn rất dễ thực hiện, nhưng để hiển thị một video phát trực tiếp bản xem trước hoặc video đều không hữu ích lắm. Trong trường hợp đó, bạn cần nhận được luồng khung hình liên tục chứ không chỉ một luồng đơn. Đoạn mã sau đây cho biết cách thêm yêu cầu lặp lại vào phiên họp:

Kotlin

val session: CameraCaptureSession = ...  // from CameraCaptureSession.StateCallback
val captureRequest: CaptureRequest = ...  // from CameraDevice.createCaptureRequest()

// This keeps sending the capture request as frequently as possible until
// the
// session is torn down or session.stopRepeating() is called
// session.setRepeatingRequest(captureRequest.build(), null, null)

Java

CameraCaptureSession session = ...;  // from CameraCaptureSession.StateCallback
CaptureRequest captureRequest = ...;  // from CameraDevice.createCaptureRequest()

// This keeps sending the capture request as frequently as possible until the
// session is torn down or session.stopRepeating() is called
// session.setRepeatingRequest(captureRequest.build(), null, null);

Yêu cầu chụp lặp lại giúp thiết bị máy ảnh liên tục chụp bằng cách sử dụng chế độ cài đặt trong CaptureRequest được cung cấp. API Camera2 cho phép người dùng quay video từ máy ảnh bằng cách gửi lặp lại CaptureRequests như trong nội dung này Mẫu Camera2 kho lưu trữ trên GitHub. Ứng dụng này cũng có thể kết xuất video chuyển động chậm bằng cách chụp video tốc độ cao (chuyển động chậm) sử dụng loạt ảnh lặp lại CaptureRequests như giới thiệu trong ứng dụng mẫu video chuyển động chậm Camera2 trên GitHub.

Xen kẽ CaptureRequests

Để gửi yêu cầu chụp thứ hai trong khi yêu cầu chụp lặp lại đang hoạt động, hiển thị kính ngắm và cho phép người dùng chụp ảnh, bạn không cần dừng yêu cầu lặp lại đang diễn ra. Thay vào đó, bạn tạo một ảnh chụp không lặp lại trong khi yêu cầu lặp lại tiếp tục chạy.

Bạn cần định cấu hình mọi vùng đệm đầu ra được sử dụng trong phiên hoạt động của máy ảnh khi phiên được tạo lần đầu tiên. Các yêu cầu lặp lại có mức độ ưu tiên thấp hơn yêu cầu một khung hình hoặc yêu cầu hàng loạt để cho phép mẫu sau hoạt động:

Kotlin

val session: CameraCaptureSession = ...  // from CameraCaptureSession.StateCallback

// Create the repeating request and dispatch it
val repeatingRequest = session.device.createCaptureRequest(
CameraDevice.TEMPLATE_PREVIEW)
repeatingRequest.addTarget(previewSurface)
session.setRepeatingRequest(repeatingRequest.build(), null, null)

// Some time later...

// Create the single request and dispatch it
// NOTE: This can disrupt the ongoing repeating request momentarily
val singleRequest = session.device.createCaptureRequest(
CameraDevice.TEMPLATE_STILL_CAPTURE)
singleRequest.addTarget(imReaderSurface)
session.capture(singleRequest.build(), null, null)

Java

CameraCaptureSession session = ...;  // from CameraCaptureSession.StateCallback

// Create the repeating request and dispatch it
CaptureRequest.Builder repeatingRequest =
session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
repeatingRequest.addTarget(previewSurface);
session.setRepeatingRequest(repeatingRequest.build(), null, null);

// Some time later...

// Create the single request and dispatch it
// NOTE: This can disrupt the ongoing repeating request momentarily
CaptureRequest.Builder singleRequest =
session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
singleRequest.addTarget(imReaderSurface);
session.capture(singleRequest.build(), null, null);

Tuy nhiên, phương pháp này có nhược điểm là bạn không biết chính xác thời điểm thì yêu cầu duy nhất xảy ra. Trong hình sau, nếu A là đoạn lặp lại và B là yêu cầu chụp một khung hình, đây là cách phiên xử lý hàng đợi yêu cầu:

Hình 2. Hình minh hoạ hàng đợi yêu cầu cho phiên camera đang diễn ra

Chúng tôi không đảm bảo về độ trễ giữa lần yêu cầu lặp lại gần đây nhất từ A trước khi yêu cầu B kích hoạt và vào lần tiếp theo A được sử dụng nên bạn có thể sẽ gặp một số khung hình bị bỏ qua. Có một số điều bạn có thể làm để giảm thiểu vấn đề này:

  • Thêm các mục tiêu đầu ra từ yêu cầu A vào yêu cầu B. Bằng cách đó, khi Khung của B đã sẵn sàng, khung này được sao chép vào mục tiêu đầu ra của A. Ví dụ: đây là tính năng cần thiết khi tạo ảnh chụp nhanh video để duy trì tốc độ khung hình ổn định. Trong mã trước, bạn thêm singleRequest.addTarget(previewSurface) trước khi tạo yêu cầu.

  • Sử dụng kết hợp các mẫu được thiết kế để hoạt động trong trường hợp cụ thể này, chẳng hạn như zero-Shutter-lag.