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:
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:
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.