Catatan: Halaman ini merujuk ke paket Camera2. Kecuali aplikasi Anda memerlukan fitur tingkat rendah khusus 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 aliran data secara bersamaan.
Salah satu alasan untuk melakukannya 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.Stream berfungsi sebagai pipeline paralel yang memproses frame mentah
yang keluar dari kamera,satu frame dalam satu waktu:
Pemrosesan paralel menunjukkan bahwa mungkin ada batas performa yang bergantung pada daya pemrosesan yang tersedia dari CPU, GPU, atau prosesor lainnya. Jika pipeline tidak dapat mengikuti frame yang masuk, pipeline akan mulai menjatuhkannya.
Setiap pipeline memiliki format output-nya sendiri. Data mentah yang masuk
secara otomatis diubah menjadi
format output yang sesuai dengan logika implisit
yang terkait dengan setiap pipeline. CameraDevice
yang digunakan di seluruh contoh kode halaman ini tidak spesifik, jadi Anda harus mengenumerasi 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, bukaan, efek,
dan eksposur. Karena keterbatasan 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 menggunakan CameraDevice
untuk melakukan streaming sesi pengambilan gambar, yang memungkinkan Anda mengoptimalkan streaming kamera untuk
kasus penggunaan tertentu. Misalnya, hal ini dapat meningkatkan masa pakai 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
mempertahankan 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 sesi
pengambilan gambar aktif. Setiap permintaan dapat mengubah konfigurasi aktif dan kumpulan pipeline
output yang menerima gambar mentah.
Gunakan Kasus Penggunaan Streaming untuk performa yang lebih baik
Kasus Penggunaan Streaming adalah cara untuk meningkatkan performa sesi pemotretan Camera2. Class ini memberi perangkat hardware lebih banyak informasi untuk menyesuaikan parameter, sehingga 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 streaming kamera tertentu digunakan secara lebih
detail, selain menetapkan template dalam
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. Ini sama 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 stabilisasi gambar berkualitas tinggi, jika didukung oleh perangkat dan diaktifkan oleh aplikasi. Opsi ini mungkin menghasilkan frame output dengan jeda yang cukup lama dari real time, untuk memungkinkan stabilisasi berkualitas tertinggi atau pemrosesan lainnya.VIDEO_CALL
: Direkomendasikan untuk penggunaan kamera yang berjalan lama di mana konsumsi daya menjadi masalah.PREVIEW_VIDEO_STILL
: Direkomendasikan untuk aplikasi media sosial atau kasus penggunaan satu streaming. 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 yang dapat digunakan aplikasi Anda untuk menulis frame output. Setiap buffer mewakili 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 jauh lagi.
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 dapat membuat dan mengirim permintaan pengambilan untuk melakukannya.
Transformasi yang diterapkan ke input saat ditulis ke buffer-nya
ditentukan oleh jenis setiap target, yang harus berupa
Surface
. Framework Android mengetahui 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 akan berusaha melakukan yang terbaik, tetapi beberapa kombinasi konfigurasi Surface
mungkin tidak berfungsi, sehingga menyebabkan masalah seperti sesi
tidak dibuat, sehingga memunculkan error runtime saat Anda mengirim permintaan, atau
penurunan performa. Framework ini memberikan jaminan untuk kombinasi
parameter perangkat, platform, dan permintaan tertentu. 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 gambar, 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 buffer output untuk digunakan dengan
permintaan. Anda hanya dapat menggunakan buffer yang sudah ditentukan pada sesi
pengambilan yang ingin digunakan.
Permintaan pengambilan menggunakan
pola builder
dan memberi developer kesempatan untuk menetapkan banyak
opsi yang berbeda termasuk
eksposur otomatis,
fokus otomatis,
dan
bukaan lensa.
Sebelum menetapkan kolom, pastikan opsi spesifik tersedia untuk
perangkat dengan memanggil
CameraCharacteristics.getAvailableCaptureRequestKeys()
dan nilai yang diinginkan didukung dengan memeriksa karakteristik kamera
yang sesuai, seperti mode eksposur otomatis
yang tersedia.
Guna membuat permintaan pengambilan untuk SurfaceView
menggunakan template
yang 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 menentukan permintaan pengambilan gambar, kini Anda dapat mengirimkannya 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);
Saat frame output dimasukkan ke dalam buffering tertentu, callback
capture
akan dipicu. Dalam banyak kasus, callback tambahan, seperti
ImageReader.OnImageAvailableListener
,
akan dipicu saat frame yang dimuatnya diproses. Pada tahap ini, Anda dapat mengambil data gambar dari buffer yang ditentukan.
Mengulangi CaptureRequest
Permintaan satu kamera mudah dilakukan, tetapi untuk menampilkan pratinjau langsung, atau video, permintaan ini tidak terlalu berguna. Dalam hal ini, Anda harus menerima aliran frame yang berkelanjutan, bukan hanya satu. Cuplikan kode berikut menunjukkan cara menambahkan permintaan berulang ke 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 berulang membuat perangkat kamera terus mengambil
gambar menggunakan setelan di 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. Aplikasi ini juga dapat merender video gerak lambat dengan merekam
video berkecepatan tinggi (gerakan lambat) menggunakan CaptureRequests
burst berulang
seperti yang ditampilkan di aplikasi contoh video gerak lambat Camera2
di GitHub.
Interleave CaptureRequests
Untuk mengirim permintaan pengambilan kedua saat permintaan pengambilan berulang sedang aktif, seperti untuk menampilkan jendela bidik dan mengizinkan pengguna mengambil foto, Anda tidak perlu menghentikan permintaan berulang yang sedang berlangsung. Sebagai gantinya, Anda mengeluarkan permintaan pengambilan yang tidak berulang saat 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 burst atau frame tunggal, 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);
Namun, ada kelemahan dari pendekatan ini: Anda tidak tahu persis kapan satu permintaan terjadi. Pada gambar berikut, jika A adalah permintaan pengambilan berulang dan B adalah permintaan pengambilan frame tunggal, beginilah cara sesi memproses antrean permintaan:
Tidak ada jaminan latensi antara permintaan berulang terakhir dari A sebelum permintaan B diaktifkan dan saat A digunakan lagi, sehingga Anda mungkin mengalami beberapa frame yang dilewati. Ada beberapa hal yang dapat Anda lakukan untuk memitigasi masalah ini:
Tambahkan target output dari permintaan A untuk meminta B. Dengan begitu, jika frame B sudah siap, frame tersebut akan disalin ke target output A. Misalnya, hal ini penting saat melakukan snapshot video untuk mempertahankan kecepatan frame yang stabil. Dalam kode sebelumnya, Anda menambahkan
singleRequest.addTarget(previewSurface)
sebelum membuat permintaan.Gunakan kombinasi template yang didesain agar berfungsi untuk skenario khusus ini, seperti zero-shutter-lag.