Catatan: Halaman ini merujuk ke paket Camera2. Kecuali jika aplikasi Anda memerlukan fitur level rendah tertentu dari Camera2, sebaiknya gunakan CameraX. CameraX dan Camera2 mendukung Android 5.0 (level API 21) dan versi yang lebih baru.
Satu perangkat yang didukung Android dapat memiliki beberapa kamera. Setiap kamera adalah
CameraDevice, dan CameraDevice dapat menghasilkan lebih dari satu
streaming secara bersamaan.
Salah satu alasannya adalah agar satu streaming, frame kamera berurutan yang berasal
dari CameraDevice, dioptimalkan untuk tugas tertentu, seperti menampilkan
jendela bidik, sementara yang lain mungkin digunakan untuk mengambil foto atau membuat rekaman
video. Streaming bertindak sebagai pipeline paralel yang memproses frame mentah yang keluar
dari kamera, satu per satu:
Pemrosesan paralel menunjukkan bahwa mungkin ada batas performa bergantung pada daya pemrosesan yang tersedia dari CPU, GPU, atau prosesor lainnya. Jika pipeline tidak dapat mengimbangi frame yang masuk, pipeline akan mulai menghapusnya.
Setiap pipeline memiliki format outputnya sendiri. Data mentah yang masuk akan otomatis diubah menjadi format output yang sesuai oleh logika implisit yang terkait dengan setiap pipeline. CameraDevice yang digunakan di seluruh contoh kode halaman ini tidak spesifik, jadi Anda harus menghitung semua kamera yang tersedia terlebih dahulu sebelum melanjutkan.
Anda dapat menggunakan CameraDevice untuk membuat
CameraCaptureSession, yang khusus untuk
CameraDevice tersebut. CameraDevice harus menerima konfigurasi frame untuk setiap frame mentah menggunakan CameraCaptureSession. Konfigurasi menentukan atribut kamera
seperti fokus otomatis, apertur, efek, dan eksposur. Karena batasan hardware, hanya satu konfigurasi yang aktif di sensor kamera pada waktu tertentu, yang disebut konfigurasi aktif.
Namun, Kasus Penggunaan Streaming meningkatkan dan memperluas cara sebelumnya dalam menggunakan CameraDevice
untuk menstreaming sesi pengambilan, sehingga Anda dapat mengoptimalkan streaming kamera untuk
kasus penggunaan tertentu. Misalnya, fitur ini dapat meningkatkan daya tahan baterai saat mengoptimalkan
panggilan video.
CameraCaptureSession menjelaskan semua kemungkinan pipeline yang terikat ke
CameraDevice. Saat sesi dibuat, Anda tidak dapat menambahkan atau menghapus pipeline.
CameraCaptureSession mengelola antrean
CaptureRequest, yang menjadi konfigurasi aktif.
CaptureRequest menambahkan konfigurasi ke antrean dan memilih satu, lebih dari satu, atau semua pipeline yang tersedia untuk menerima frame dari CameraDevice. Anda dapat mengirim banyak permintaan pengambilan selama masa aktif sesi
pengambilan. Setiap permintaan dapat mengubah konfigurasi aktif dan kumpulan pipeline output yang menerima gambar mentah.
Menggunakan Kasus Penggunaan Streaming untuk performa yang lebih baik
Kasus Penggunaan Streaming adalah cara untuk meningkatkan performa sesi pengambilan gambar Camera2. Mode ini memberi perangkat hardware lebih banyak informasi untuk menyesuaikan parameter, yang memberikan pengalaman kamera yang lebih baik untuk tugas spesifik Anda.
Hal ini memungkinkan perangkat kamera mengoptimalkan pipeline hardware dan software kamera
berdasarkan skenario pengguna untuk setiap streaming. Untuk mengetahui informasi selengkapnya tentang Kasus Penggunaan
Streaming, lihat setStreamUseCase.
Kasus Penggunaan Streaming memungkinkan Anda menentukan cara penggunaan streaming kamera tertentu secara
lebih mendetail, selain menetapkan template di
CameraDevice.createCaptureRequest(). Hal ini memungkinkan hardware kamera mengoptimalkan
parameter, seperti penyesuaian, mode sensor, atau setelan sensor kamera, berdasarkan
kompromi kualitas atau latensi yang sesuai untuk kasus penggunaan tertentu.
Kasus Penggunaan Stream mencakup:
DEFAULT: Mencakup semua perilaku aplikasi yang ada. Hal ini setara dengan tidak menetapkan Kasus Penggunaan Streaming apa pun.PREVIEW: Direkomendasikan untuk Jendela bidik atau analisis gambar dalam aplikasi.STILL_CAPTURE: Dioptimalkan untuk pengambilan gambar resolusi tinggi berkualitas tinggi, dan tidak diharapkan untuk mempertahankan kecepatan frame seperti pratinjau.VIDEO_RECORD: Dioptimalkan untuk perekaman video berkualitas tinggi, termasuk stabilisasi gambar berkualitas tinggi, jika didukung oleh perangkat dan diaktifkan oleh aplikasi. Opsi ini mungkin menghasilkan frame output dengan jeda yang signifikan dari real time, untuk memungkinkan stabilisasi atau pemrosesan lainnya dengan kualitas tertinggi.VIDEO_CALL: Direkomendasikan untuk penggunaan kamera yang berjalan lama dan menguras daya.PREVIEW_VIDEO_STILL: Direkomendasikan untuk aplikasi media sosial atau kasus penggunaan streaming tunggal. Streaming ini serbaguna.VENDOR_START: Digunakan untuk kasus penggunaan yang ditentukan OEM.
Membuat CameraCaptureSession
Untuk membuat sesi kamera, berikan satu atau beberapa buffer output yang dapat digunakan aplikasi Anda untuk menulis frame output. Setiap buffer mewakili sebuah pipeline. Anda harus melakukannya sebelum mulai menggunakan kamera agar framework dapat mengonfigurasi pipeline internal perangkat dan mengalokasikan buffering memori untuk mengirim frame ke target output yang diperlukan.
Cuplikan kode berikut menunjukkan cara menyiapkan sesi kamera dengan dua
buffer output, satu milik SurfaceView dan satu lagi milik
ImageReader. Menambahkan PREVIEW
Kasus Penggunaan Streaming ke previewSurface dan
STILL_CAPTURE Kasus Penggunaan Streaming ke
imReaderSurface memungkinkan hardware perangkat mengoptimalkan streaming ini lebih lanjut.
// 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) }
Pada tahap ini, Anda belum menentukan konfigurasi aktif kamera. Setelah sesi dikonfigurasi, Anda dapat membuat dan mengirim permintaan pengambilan untuk melakukannya.
Transformasi yang diterapkan ke input saat ditulis ke buffering ditentukan oleh jenis setiap target, yang harus berupa Surface. Framework
Android tahu cara mengonversi gambar mentah dalam konfigurasi aktif
menjadi format yang sesuai untuk setiap target. Konversi dikontrol oleh
format piksel dan ukuran Surface tertentu.
Framework ini mencoba melakukan yang terbaik, tetapi beberapa kombinasi konfigurasi Surface mungkin tidak berfungsi, sehingga menyebabkan masalah seperti sesi tidak dibuat, menampilkan error runtime saat Anda mengirim permintaan, atau penurunan performa. Framework ini memberikan jaminan untuk kombinasi tertentu dari
parameter perangkat, platform, dan permintaan. Dokumentasi untuk
createCaptureSession() memberikan informasi selengkapnya.
CaptureRequest Tunggal
Konfigurasi yang digunakan untuk setiap frame dienkode dalam CaptureRequest, yang
dikirim ke kamera. Untuk membuat permintaan pengambilan, Anda dapat menggunakan salah satu
template yang telah ditetapkan,
atau Anda dapat menggunakan TEMPLATE_MANUAL untuk kontrol penuh. Saat memilih template,
Anda harus menyediakan satu atau beberapa buffering output untuk digunakan dengan permintaan. Anda
hanya dapat menggunakan buffering yang telah ditentukan pada sesi pengambilan yang ingin
Anda gunakan.
Permintaan pengambilan menggunakan pola builder dan memberi
developer kesempatan untuk menetapkan
banyak opsi berbeda termasuk
eksposur otomatis, fokus otomatis, dan
apertur lensa. Sebelum menetapkan kolom, pastikan
opsi tertentu tersedia untuk perangkat dengan memanggil
CameraCharacteristics.getAvailableCaptureRequestKeys()
dan nilai yang dipilih didukung dengan memeriksa karakteristik kamera
yang sesuai, seperti
mode eksposur otomatis yang tersedia.
Untuk membuat permintaan pengambilan untuk SurfaceView menggunakan template yang dirancang untuk
pratinjau tanpa modifikasi apa pun, gunakan
CameraDevice.TEMPLATE_PREVIEW:
val session: CameraCaptureSession = /* ... */ // from CameraCaptureSession.StateCallback val captureRequest = session.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) captureRequest.addTarget(previewSurface)
Setelah permintaan pengambilan ditentukan, Anda kini dapat mengirimkannya ke sesi kamera:
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest: CaptureRequest.Builder = ... // 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)
Saat frame output dimasukkan ke dalam buffer tertentu,
callback pengambilan akan dipicu. Dalam banyak kasus, callback tambahan, seperti ImageReader.OnImageAvailableListener, dipicu saat frame yang dimuatnya diproses. Pada tahap ini, Anda dapat mengambil data gambar dari buffering yang ditentukan.
Mengulangi CaptureRequest
Permintaan kamera tunggal mudah dilakukan, tetapi untuk menampilkan pratinjau langsung, atau video, permintaan tersebut tidak terlalu berguna. Dalam hal ini, Anda perlu menerima streaming frame yang berkelanjutan, bukan hanya satu frame. Cuplikan kode berikut menunjukkan cara menambahkan permintaan berulang ke sesi:
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest: CaptureRequest.Builder = ... // 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 berulang membuat perangkat kamera terus mengambil gambar
menggunakan setelan dalam CaptureRequest yang disediakan. Camera2 API juga memungkinkan
pengguna merekam video dari kamera dengan mengirimkan CaptureRequests berulang seperti yang
terlihat dalam repositori contoh Camera2 ini di GitHub. API ini juga
dapat merender video gerak lambat dengan merekam video kecepatan tinggi (gerak lambat)
menggunakan burst CaptureRequests berulang seperti yang ditampilkan dalam
aplikasi contoh video gerak lambat Camera2 di GitHub.
Menyisipkan 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 mengeluarkan permintaan pengambilan yang tidak berulang sementara permintaan berulang terus berjalan.
Setiap buffering output yang digunakan perlu 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:
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)
Namun, ada kelemahan dari pendekatan ini: Anda tidak tahu persis kapan permintaan tunggal terjadi. Pada gambar berikut, jika A adalah permintaan pengambilan berulang dan B adalah permintaan pengambilan frame tunggal, berikut cara sesi memproses antrean permintaan:
Tidak ada jaminan untuk latensi antara permintaan berulang terakhir dari A sebelum permintaan B diaktifkan dan saat berikutnya A digunakan lagi, sehingga Anda mungkin mengalami beberapa frame yang terlewati. Ada beberapa hal yang dapat Anda lakukan untuk mengurangi masalah ini:
Tambahkan target output dari permintaan A ke permintaan B. Dengan begitu, saat frame B siap, frame tersebut 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 template yang dirancang untuk berfungsi dalam skenario tertentu, seperti zero-shutter-lag.