Sesi dan permintaan pengambilan gambar kamera

Catatan: Halaman ini merujuk ke paket Camera2. Kecuali aplikasi Anda memerlukan fitur tingkat rendah tertentu dari Camera2, sebaiknya gunakan CameraX. CameraX dan Camera2 mendukung Android 5.0 (level API 21) dan versi yang lebih baru.

Satu perangkat Android dapat memiliki beberapa kamera. Setiap kamera adalah CameraDevice, dan CameraDevice dapat menghasilkan lebih dari satu stream secara bersamaan.

Salah satu alasan untuk melakukan ini adalah agar satu aliran, bingkai kamera berurutan dari CameraDevice, dioptimalkan untuk tugas tertentu, seperti menampilkan jendela bidik, sementara yang lain mungkin digunakan untuk mengambil foto atau membuat video perekaman.Stream berfungsi sebagai pipeline paralel yang memproses frame mentah keluar dari kamera,satu per satu:

Gambar 1. Ilustrasi dari Membuat Aplikasi Kamera Universal (Google I/O ‘18)

Pemrosesan paralel menunjukkan bahwa mungkin ada batas performa yang bergantung pada daya pemrosesan yang tersedia dari CPU, GPU, atau prosesor lain. Jika tidak dapat mengikuti {i>frame<i} yang masuk, maka akan mulai meninggalkannya.

Setiap pipeline memiliki format output-nya sendiri. Data mentah yang masuk otomatis diubah menjadi model format output dengan logika implisit yang terkait dengan setiap pipeline. CameraDevice yang digunakan di seluruh halaman contoh kode tidak spesifik, jadi Anda enumerasi terlebih dahulu semua kamera yang tersedia sebelum melanjutkan.

Anda dapat menggunakan CameraDevice untuk membuat CameraCaptureSession, yang spesifik untuk CameraDevice tersebut. CameraDevice harus menerima untuk setiap frame mentah dengan menggunakan CameraCaptureSession. Tujuan menentukan atribut kamera, seperti fokus otomatis, apertur, efek, dan eksposur. Karena keterbatasan perangkat keras, hanya ada satu konfigurasi yang aktif pada sensor kamera pada waktu tertentu, yang disebut active.

Namun, Kasus Penggunaan Streaming meningkatkan kualitas dan memperluas cara sebelumnya untuk menggunakan CameraDevice untuk melakukan streaming sesi pengambilan gambar, yang memungkinkan Anda mengoptimalkan streaming kamera untuk kasus penggunaan tertentu. Misalnya, dapat meningkatkan daya tahan baterai saat pengoptimalan panggilan video.

CameraCaptureSession menjelaskan semua kemungkinan pipeline yang terikat dengan CameraDevice. Saat sesi dibuat, Anda tidak dapat menambahkan atau menghapus pipeline. CameraCaptureSession mempertahankan antrean CaptureRequest, yang menjadi konfigurasi aktif.

CaptureRequest menambahkan konfigurasi ke antrean dan memilih satu, lebih dari satu, atau semua {i>pipelines<i} yang tersedia untuk menerima sebuah {i>frame<i} dari CameraDevice. Anda bisa mengirim banyak permintaan penangkapan selama masa pengambilan gambar sesi. Setiap permintaan dapat mengubah konfigurasi aktif dan kumpulan output pipeline yang menerima image mentah.

Gunakan Kasus Penggunaan Streaming untuk performa yang lebih baik

Kasus Penggunaan Streaming adalah cara untuk meningkatkan performa pengambilan gambar Camera2 sesi. Mereka memberi perangkat keras lebih banyak informasi untuk menyesuaikan parameter, sehingga memberikan pengalaman kamera yang lebih baik untuk tugas spesifik Anda.

Ini memungkinkan perangkat kamera mengoptimalkan pipeline hardware dan software kamera berdasarkan skenario pengguna untuk setiap streaming. Untuk informasi selengkapnya tentang Penggunaan Streaming Kasus, lihat setStreamUseCase.

Kasus Penggunaan Streaming memungkinkan Anda menentukan penggunaan streaming kamera tertentu secara lebih mendetail, selain mengatur CameraDevice.createCaptureRequest(). Hal ini memungkinkan hardware kamera mengoptimalkan parameter, seperti tuning, mode sensor, atau setelan sensor kamera, berdasarkan kompromi kualitas atau latensi yang cocok untuk kasus penggunaan tertentu.

Kasus Penggunaan Streaming mencakup:

  • DEFAULT: Mencakup semua perilaku aplikasi yang ada. Setara dengan tidak menetapkan Kasus Penggunaan Streaming.

  • PREVIEW: Direkomendasikan untuk Jendela Bidik atau analisis gambar dalam aplikasi.

  • STILL_CAPTURE: Dioptimalkan untuk pengambilan gambar beresolusi tinggi berkualitas tinggi, dan tidak diharapkan untuk mempertahankan kecepatan frame seperti pratinjau.

  • VIDEO_RECORD: Dioptimalkan untuk perekaman video berkualitas tinggi, termasuk kualitas tinggi stabilisasi gambar, jika didukung oleh perangkat dan diaktifkan oleh aplikasi. Opsi ini dapat menghasilkan {i>output frame<i} dengan jeda yang substansial dari {i>real time<i}, untuk memungkinkan stabilisasi kualitas tertinggi atau pemrosesan lainnya.

  • VIDEO_CALL: Direkomendasikan untuk penggunaan kamera yang berjalan lama yang menguras daya kekhawatiran.

  • PREVIEW_VIDEO_STILL: Direkomendasikan untuk aplikasi media sosial atau penggunaan streaming tunggal penggunaan. Ini adalah streaming serbaguna.

  • VENDOR_START: Digunakan untuk kasus penggunaan yang ditentukan OEM.

Membuat CameraCaptureSession

Untuk membuat sesi kamera, sediakan satu atau beberapa buffer output aplikasi Anda dapat menulis frame output. Setiap buffer mewakili sebuah pipeline. Anda harus lakukan ini sebelum Anda mulai menggunakan kamera sehingga framework dapat mengonfigurasi pipeline internal perangkat dan mengalokasikan {i>buffer<i} memori untuk mengirim {i>frame<i} dengan target output yang dibutuhkan.

Cuplikan kode berikut menunjukkan cara menyiapkan sesi kamera dengan dua buffer output, satu yang dimiliki oleh SurfaceView dan satu lagi ke ImageReader. Menambahkan Kasus Penggunaan Streaming PREVIEW ke previewSurface dan STILL_CAPTURE Penggunaan Feed Kasus untuk imReaderSurface memungkinkan hardware perangkat mengoptimalkan streaming ini bahkan lebih lanjut.

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

Pada tahap ini, Anda belum menentukan konfigurasi aktif kamera. Saat sesi dikonfigurasi, Anda bisa membuat dan mengirim rekaman terhadap permintaan untuk melakukan itu.

Transformasi yang diterapkan pada {i>input<i} ketika ditulis ke {i>buffer<i}-nya adalah ditentukan oleh jenis setiap target, yang harus berupa Surface Framework Android mengetahui cara mengonversi gambar mentah dalam konfigurasi aktif ke format yang sesuai untuk setiap target. Konversi dikontrol oleh format piksel dan ukuran Surface tertentu.

Framework ini akan berusaha melakukan yang terbaik, tetapi beberapa Surface kombinasi konfigurasi mungkin tidak berfungsi, sehingga menyebabkan masalah seperti sesi tidak dibuat, menampilkan error runtime saat Anda mengirim permintaan, atau penurunan performa. Kerangka kerja ini memberikan jaminan untuk kombinasi parameter perangkat, platform, dan permintaan. Dokumentasi untuk createCaptureSession() memberikan informasi selengkapnya.

Satu CaptureRequest

Konfigurasi yang digunakan untuk setiap frame dienkode dalam CaptureRequest, yang dikirim ke kamera. Untuk membuat permintaan pengambilan gambar, Anda dapat menggunakan salah satu standar template, atau Anda dapat menggunakan TEMPLATE_MANUAL untuk kontrol penuh. Bila Anda memilih Anda perlu menyediakan satu atau lebih {i>buffer <i}output yang akan digunakan dengan terhadap permintaan. Anda hanya dapat menggunakan buffer yang sudah ditentukan pada pengambilan sesi yang ingin Anda gunakan.

Permintaan pengambilan menggunakan pola builder dan memberi kesempatan kepada pengembang untuk menetapkan berbagai opsi termasuk eksposur otomatis, fokus otomatis, dan bukaan lensa. Sebelum menyetel bidang, pastikan bahwa opsi khusus tersedia untuk perangkat dengan memanggil CameraCharacteristics.getAvailableCaptureRequestKeys() dan nilai yang diinginkan didukung dengan memeriksa kamera karakteristik, seperti eksposur otomatis yang tersedia mode.

Untuk membuat permintaan pengambilan SurfaceView menggunakan template dirancang untuk pratinjau tanpa modifikasi apa pun, gunakan 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);

Setelah permintaan pengambilan yang ditentukan, Anda sekarang dapat mengirim ke sesi kamera:

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

Ketika frame output dimasukkan ke dalam buffer tertentu, sebuah rekaman telepon balik akan terpicu. Dalam banyak kasus callback tambahan, seperti ImageReader.OnImageAvailableListener, dipicu saat {i>frame<i} di dalamnya sedang diproses. Pukul di titik ini Anda bisa mengambil data gambar dari buffer yang ditetapkan.

Mengulangi CaptureRequest

Permintaan kamera tunggal mudah dilakukan, tetapi untuk menampilkan live streaming pratinjau, atau video, mereka tidak terlalu berguna. Dalam hal ini, Anda perlu menerima aliran {i>frame<i} yang berkelanjutan, bukan hanya satu saja. Cuplikan kode berikut menunjukkan cara menambahkan permintaan berulang dalam sesi:

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

Permintaan pengambilan gambar yang berulang membuat perangkat kamera terus-menerus mengambil gambar gambar menggunakan setelan dalam CaptureRequest yang disediakan. Camera2 API juga memungkinkan pengguna merekam video dari kamera dengan mengirimkan mengulangi CaptureRequests seperti yang terlihat dalam ini Contoh Camera2 repositori di GitHub. Aplikasi ini juga bisa merender video gerak lambat dengan merekam video berkecepatan tinggi (gerakan lambat) menggunakan burst CaptureRequests berulang seperti yang ditampilkan di aplikasi contoh video gerak lambat Camera2 di GitHub.

Mengalihkan CaptureRequest

Untuk mengirim permintaan pengambilan kedua saat permintaan pengambilan berulang aktif, seperti untuk menampilkan jendela bidik dan memungkinkan pengguna mengambil foto, Anda tidak perlu menghentikan permintaan berulang yang sedang berlangsung. Sebagai gantinya, Anda bisa melakukan pengambilan gambar yang tidak berulang sementara permintaan berulang tetap berjalan.

Setiap buffer output yang digunakan harus dikonfigurasi sebagai bagian dari sesi kamera saat sesi pertama kali dibuat. Permintaan berulang memiliki prioritas lebih rendah daripada permintaan frame tunggal atau burst, yang memungkinkan contoh berikut berfungsi:

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

Meskipun pendekatan ini memiliki kelemahan: Anda tidak tahu persis kapan permintaan tunggal terjadi. Pada gambar berikut, jika A adalah nilai berulang dan B adalah permintaan pengambilan frame tunggal, ini adalah cara antrian permintaan akan diproses:

Gambar 2. Ilustrasi antrean permintaan untuk sesi kamera yang sedang berlangsung

Tidak ada jaminan untuk latensi antara permintaan berulang terakhir dari A sebelum permintaan B aktif dan saat berikutnya A digunakan sekali lagi, sehingga Anda mungkin mengalami beberapa frame yang dilewati. Ada beberapa hal yang dapat untuk memitigasi masalah ini:

  • Tambahkan target output dari permintaan A untuk meminta B. Dengan begitu, ketika Frame B sudah siap, dan akan disalin ke target output A. Misalnya, hal ini penting saat melakukan snapshot video untuk mempertahankan kecepatan frame yang stabil. Pada kode sebelumnya, Anda menambahkan singleRequest.addTarget(previewSurface) sebelum membuat permintaan.

  • Gunakan kombinasi {i>template<i} yang dirancang untuk digunakan dalam skenario khusus ini, seperti zero-shutter-lag.