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.
Kamera dan pratinjau kamera tidak selalu dalam orientasi yang sama di perangkat Android.
Kamera berada dalam posisi tetap di perangkat, terlepas dari apakah perangkat tersebut adalah ponsel, tablet, atau komputer. Saat orientasi perangkat berubah, orientasi kamera juga berubah.
Akibatnya, aplikasi kamera biasanya mengasumsikan hubungan tetap antara orientasi perangkat dan rasio aspek pratinjau kamera. Saat ponsel dalam orientasi potret, pratinjau kamera diasumsikan lebih tinggi daripada lebarnya. Saat ponsel (dan kamera) diputar ke lanskap, pratinjau kamera diharapkan lebih lebar daripada tingginya.
Namun, asumsi ini mulai tidak berlaku lagi akibat munculnya beragam faktor bentuk baru, seperti perangkat foldable, dan mode tampilan layar baru seperti multi-aplikasi dan multi-layar. Perangkat foldable mengubah ukuran layar dan rasio aspek tanpa mengubah orientasi. Mode multi-aplikasi membatasi aplikasi kamera ke sebagian layar, sehingga menskalakan pratinjau kamera terlepas dari orientasi perangkat. Mode multi-layar memungkinkan penggunaan layar sekunder yang mungkin tidak memiliki orientasi yang sama dengan layar utama.
Orientasi kamera
Android Compatibility Definition menetapkan bahwa sensor gambar kamera "HARUS diorientasikan sehingga dimensi panjang kamera selaras dengan dimensi panjang layar. Artinya, saat perangkat dipegang dalam orientasi lanskap, kamera HARUS mengambil gambar dalam orientasi lanskap. Hal ini berlaku terlepas dari orientasi alami perangkat; artinya, hal ini berlaku untuk perangkat utama lanskap serta perangkat utama potret."
Pengaturan kamera ke layar memaksimalkan area tampilan jendela bidik kamera di aplikasi kamera. Selain itu, sensor gambar biasanya menghasilkan data dalam rasio aspek lanskap, dengan 4:3 sebagai rasio yang paling umum.
Orientasi alami sensor kamera adalah lanskap. Pada gambar 1, sensor kamera depan (kamera yang mengarah ke arah yang sama dengan layar) diputar 270 derajat terhadap ponsel untuk mematuhi Android Compatibility Definition.
Untuk mengekspos rotasi sensor ke aplikasi, API camera2
menyertakan konstanta SENSOR_ORIENTATION. Untuk sebagian besar ponsel
dan tablet, perangkat melaporkan orientasi sensor 270 derajat untuk
kamera depan dan 90 derajat (sudut pandang dari bagian belakang perangkat)
untuk kamera belakang, yang menyelaraskan tepi panjang sensor dengan tepi
panjang perangkat. Kamera laptop umumnya melaporkan orientasi sensor 0 atau
180 derajat.
Karena sensor gambar kamera menghasilkan data (buffer gambar) dalam
orientasi alami sensor (lanskap), buffer gambar harus diputar
sejumlah derajat yang ditentukan oleh SENSOR_ORIENTATION agar pratinjau kamera
muncul tegak dalam orientasi alami perangkat. Untuk kamera depan,
rotasinya berlawanan arah jarum jam; untuk kamera belakang, searah jarum jam.
Misalnya, untuk kamera depan pada gambar 1, buffer gambar yang dihasilkan oleh sensor kamera terlihat seperti ini:
Gambar harus diputar 270 derajat berlawanan arah jarum jam agar orientasi pratinjau sesuai dengan orientasi perangkat:
Kamera belakang akan menghasilkan buffer gambar dengan orientasi yang sama
seperti buffer sebelumnya, tetapi SENSOR_ORIENTATION adalah 90 derajat. Akibatnya, buffer diputar 90 derajat searah jarum jam.
Rotasi perangkat
Rotasi perangkat adalah jumlah derajat perangkat diputar dari orientasi naturalnya. Misalnya, ponsel dalam orientasi lanskap memiliki rotasi perangkat 90 atau 270 derajat, bergantung pada arah rotasi.
Buffer gambar sensor kamera harus diputar dengan jumlah derajat yang sama dengan rotasi perangkat (selain derajat orientasi sensor) agar pratinjau kamera tampil tegak lurus.
Penghitungan orientasi
Orientasi pratinjau kamera yang tepat mempertimbangkan orientasi sensor dan rotasi perangkat.
Rotasi keseluruhan buffering gambar sensor dapat dihitung menggunakan formula berikut:
rotation = (sensorOrientationDegrees - deviceOrientationDegrees * sign + 360) % 360
dengan sign adalah 1 untuk kamera depan, -1 untuk kamera belakang.
Untuk kamera depan, buffer gambar diputar berlawanan arah jarum jam (dari orientasi alami sensor). Untuk kamera belakang, buffer gambar sensor diputar searah jarum jam.
Ekspresi deviceOrientationDegrees * sign + 360 mengonversi rotasi perangkat
dari berlawanan arah jarum jam menjadi searah jarum jam untuk kamera belakang (misalnya,
mengonversi 270 derajat berlawanan arah jarum jam menjadi 90 derajat searah jarum jam). Operasi modulo
menskalakan hasilnya menjadi kurang dari 360 derajat (misalnya, menskalakan 540
derajat rotasi menjadi 180).
API yang berbeda melaporkan rotasi perangkat secara berbeda:
Display#getRotation()memberikan rotasi perangkat berlawanan arah jarum jam (dari sudut pandang pengguna). Nilai ini dimasukkan ke dalam formula sebelumnya apa adanya.OrientationEventListener#onOrientationChanged()menampilkan rotasi perangkat searah jarum jam (dari sudut pandang pengguna). Negasikan nilai untuk digunakan dalam formula sebelumnya.
Kamera depan
Berikut adalah buffer gambar yang dihasilkan oleh sensor kamera pada gambar 4:
Buffer harus diputar 270 derajat berlawanan arah jarum jam untuk menyesuaikan orientasi sensor (lihat Orientasi kamera di atas):
Kemudian, buffering diputar 90 derajat lagi berlawanan arah jarum jam untuk memperhitungkan rotasi perangkat, sehingga menghasilkan orientasi pratinjau kamera yang benar pada gambar 4:
Berikut adalah kamera yang diputar ke kanan ke orientasi lanskap:
Berikut adalah buffering gambar:
Buffer harus diputar 270 derajat berlawanan arah jarum jam untuk menyesuaikan orientasi sensor:
Kemudian, buffer diputar 270 derajat lagi berlawanan arah jarum jam untuk memperhitungkan rotasi perangkat:
Kamera belakang
Kamera belakang biasanya memiliki orientasi sensor 90 derajat (seperti yang dilihat dari bagian belakang perangkat). Saat mengorientasikan pratinjau kamera, buffer gambar sensor diputar searah jarum jam sebesar rotasi sensor (bukan berlawanan arah jarum jam seperti kamera depan), lalu buffer gambar diputar berlawanan arah jarum jam sebesar rotasi perangkat.
Berikut adalah buffer gambar dari sensor kamera pada gambar 12:
Buffer harus diputar 90 derajat searah jarum jam untuk menyesuaikan orientasi sensor:
Kemudian, buffer diputar 270 derajat berlawanan arah jarum jam untuk memperhitungkan rotasi perangkat:
Rasio aspek
Rasio aspek tampilan berubah saat orientasi perangkat berubah, tetapi juga saat perangkat foldable dilipat dan dibentangkan, saat jendela diubah ukurannya di lingkungan multi-aplikasi, dan saat aplikasi dibuka di layar sekunder.
Buffer gambar sensor kamera harus diorientasikan dan diskalakan agar sesuai dengan orientasi dan rasio aspek elemen UI jendela bidik saat UI berubah orientasi secara dinamis—dengan atau tanpa perangkat berubah orientasi.
Pada faktor bentuk baru atau di lingkungan multi-aplikasi atau multi-layar, jika aplikasi Anda mengasumsikan bahwa pratinjau kamera memiliki orientasi yang sama dengan perangkat (potret atau lanskap), pratinjau Anda mungkin memiliki orientasi yang salah, diskalakan dengan salah, atau keduanya.
Pada gambar 16, aplikasi salah mengasumsikan bahwa perangkat diputar 90 derajat berlawanan arah jarum jam; sehingga, aplikasi memutar pratinjau dengan jumlah yang sama.
Pada gambar 17, aplikasi tidak menyesuaikan rasio aspek buffering gambar untuk memungkinkannya menskalakan dengan benar agar sesuai dengan dimensi baru elemen UI pratinjau kamera.
Aplikasi kamera dengan orientasi tetap biasanya mengalami masalah pada perangkat foldable dan perangkat layar besar lainnya seperti laptop:
Pada gambar 18, UI aplikasi kamera miring karena orientasi aplikasi dibatasi hanya untuk potret. Gambar jendela bidik diorientasikan dengan benar terhadap sensor kamera.
Mode potret inset
Aplikasi kamera yang tidak mendukung mode multi-aplikasi
(resizeableActivity="false") dan membatasi
orientasinya
(screenOrientation="portrait" atau
screenOrientation="landscape") dapat ditempatkan
dalam mode potret inset di perangkat layar besar untuk mengorientasikan pratinjau kamera
dengan benar.
Mode potret inset menampilkan aplikasi khusus potret dalam tampilan lebar (inset) dalam orientasi potret meskipun rasio aspek layar adalah lanskap. Aplikasi khusus lanskap memiliki tampilan lebar dalam orientasi lanskap meskipun rasio aspek layar adalah potret. Gambar kamera diputar agar selaras dengan UI aplikasi, dipangkas agar sesuai dengan rasio aspek pratinjau kamera, lalu diskalakan untuk mengisi pratinjau.
Mode potret inset dipicu saat rasio aspek sensor gambar kamera dan rasio aspek aktivitas utama aplikasi tidak cocok.
Pada gambar 19, aplikasi kamera khusus potret telah diputar untuk menampilkan UI tegak di layar laptop. Aplikasi memiliki tampilan lebar karena perbedaan rasio aspek antara aplikasi potret dan layar lanskap. Gambar pratinjau kamera telah diputar untuk mengimbangi rotasi UI aplikasi (karena mode potret inset), dan gambar telah dipangkas serta diskalakan agar sesuai dengan orientasi potret, sehingga mengurangi ruang pandang.
Memutar, memangkas, menskalakan
Mode potret inset dipanggil untuk aplikasi kamera khusus potret di layar yang memiliki rasio aspek lanskap:
Aplikasi memiliki tampilan lebar dalam orientasi potret:
Gambar kamera diputar 90 derajat untuk menyesuaikan dengan orientasi ulang aplikasi:
Gambar dipangkas sesuai rasio aspek pratinjau kamera, lalu diskalakan untuk mengisi pratinjau (ruang pandang dikurangi):
Pada perangkat foldable, orientasi sensor kamera dapat berupa potret, sementara rasio aspek layar adalah lanskap:
Karena pratinjau kamera diputar untuk menyesuaikan orientasi sensor, gambar diorientasikan dengan benar di jendela bidik, tetapi aplikasi khusus potret berada dalam posisi miring.
Mode potret inset hanya perlu menampilkan aplikasi dalam tampilan lebar dalam orientasi potret untuk mengorientasikan aplikasi dan pratinjau kamera dengan benar:
API
Mulai Android 12 (API level 31), aplikasi juga dapat secara eksplisit mengontrol mode potret inset
melalui properti SCALER_ROTATE_AND_CROP
dari class CaptureRequest.
Nilai defaultnya adalah
SCALER_ROTATE_AND_CROP_AUTO, yang memungkinkan
sistem memanggil mode potret inset.
SCALER_ROTATE_AND_CROP_90 adalah perilaku
mode potret inset seperti yang dijelaskan sebelumnya.
Tidak semua perangkat mendukung semua nilai SCALER_ROTATE_AND_CROP. Untuk mendapatkan daftar nilai yang didukung, lihat CameraCharacteristics#SCALER_AVAILABLE_ROTATE_AND_CROP_MODES.
CameraX
Library Jetpack CameraX memudahkan pembuatan jendela bidik kamera yang mengakomodasi orientasi sensor dan rotasi perangkat.
Elemen tata letak PreviewView membuat pratinjau kamera,
yang otomatis menyesuaikan orientasi sensor, rotasi perangkat, dan penskalaan.
PreviewView mempertahankan rasio aspek gambar kamera dengan menerapkan
jenis skala FILL_CENTER, yang memusatkan gambar, tetapi
mungkin memangkasnya agar sesuai dengan dimensi PreviewView. Untuk menerapkan tampilan lebar pada
gambar kamera, tetapkan jenis skala ke FIT_CENTER.
Untuk mempelajari dasar-dasar pembuatan pratinjau kamera dengan PreviewView, lihat
Mengimplementasikan pratinjau.
Untuk mengetahui contoh implementasi lengkap, lihat repositori CameraXBasic di GitHub.
CameraViewfinder
Serupa dengan kasus penggunaan Pratinjau, library CameraViewfinder menyediakan serangkaian alat untuk menyederhanakan pembuatan pratinjau kamera. Library ini tidak bergantung pada CameraX Core, sehingga Anda dapat mengintegrasikannya dengan lancar ke dalam codebase Camera2 yang ada.
Daripada menggunakan Surface secara langsung, Anda dapat menggunakan
widget CameraViewfinder untuk menampilkan feed kamera untuk
Camera2.
CameraViewfinder secara internal menggunakan TextureView atau SurfaceView untuk
menampilkan feed kamera, dan menerapkan transformasi yang diperlukan untuk
menampilkan jendela bidik dengan benar. Hal ini termasuk mengoreksi rasio aspek,
skala, dan rotasinya.
Untuk meminta platform dari objek CameraViewfinder, Anda perlu membuat
ViewfinderSurfaceRequest.
Permintaan ini berisi persyaratan untuk resolusi platform dan informasi perangkat kamera dari CameraCharacteristics.
Memanggil requestSurfaceAsync() akan mengirimkan permintaan ke
penyedia platform, yang merupakan TextureView atau SurfaceView dan mendapatkan
ListenableFuture dari Surface.
Memanggil markSurfaceSafeToRelease() akan memberi tahu
penyedia platform bahwa platform tidak diperlukan dan resource terkait dapat
dirilis.
fun startCamera() {
val previewResolution = Size(width, height)
val viewfinderSurfaceRequest =
ViewfinderSurfaceRequest(previewResolution, characteristics)
val surfaceListenableFuture =
cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest)
Futures.addCallback(surfaceListenableFuture, object : FutureCallback<Surface> {
override fun onSuccess(surface: Surface) {
/* create a CaptureSession using this surface as usual */
}
override fun onFailure(t: Throwable) { /* something went wrong */ }
}, ContextCompat.getMainExecutor(context))
}
SurfaceView
SurfaceView adalah pendekatan yang mudah untuk membuat pratinjau
kamera jika pratinjau tidak memerlukan pemrosesan dan tidak dianimasikan.
SurfaceView otomatis memutar buffering gambar sensor kamera agar sesuai dengan
orientasi layar, dengan mempertimbangkan orientasi sensor dan rotasi perangkat.
Namun, buffer gambar diskalakan agar sesuai dengan dimensi SurfaceView tanpa
mempertimbangkan rasio aspek.
Anda harus memastikan bahwa rasio aspek buffering gambar cocok dengan rasio aspek SurfaceView, yang dapat Anda lakukan dengan menskalakan konten SurfaceView dalam metode onMeasure() komponen:
(Kode sumber computeRelativeRotation() ada di
Rotasi relatif di bawah.)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { val width = MeasureSpec.getSize(widthMeasureSpec) val height = MeasureSpec.getSize(heightMeasureSpec) val relativeRotation = computeRelativeRotation(characteristics, surfaceRotationDegrees) if (previewWidth > 0f && previewHeight > 0f) { /* Scale factor required to scale the preview to its original size on the x-axis. */ val scaleX = if (relativeRotation % 180 == 0) { width.toFloat() / previewWidth } else { width.toFloat() / previewHeight } /* Scale factor required to scale the preview to its original size on the y-axis. */ val scaleY = if (relativeRotation % 180 == 0) { height.toFloat() / previewHeight } else { height.toFloat() / previewWidth } /* Scale factor required to fit the preview to the SurfaceView size. */ val finalScale = min(scaleX, scaleY) setScaleX(1 / scaleX * finalScale) setScaleY(1 / scaleY * finalScale) } setMeasuredDimension(width, height) }
Untuk mengetahui detail selengkapnya tentang cara menerapkan SurfaceView sebagai pratinjau kamera, lihat
Orientasi kamera.
TextureView
TextureView memiliki performa yang lebih rendah daripada SurfaceView—dan
lebih banyak pekerjaan—tetapi TextureView memberi Anda kontrol maksimum atas pratinjau
kamera.
TextureView memutar buffering gambar sensor berdasarkan orientasi sensor, tetapi tidak menangani rotasi perangkat atau penskalaan pratinjau.
Penskalaan dan rotasi dapat dienkode dalam transformasi Matrix.
Untuk mempelajari cara menskalakan dan memutar TextureView dengan benar, lihat Mendukung
platform yang dapat diubah ukurannya di aplikasi kamera Anda.
Rotasi relatif
Rotasi relatif sensor kamera adalah jumlah rotasi yang diperlukan untuk menyelaraskan output sensor kamera dengan orientasi perangkat.
Rotasi relatif digunakan oleh komponen seperti SurfaceView dan TextureView
untuk menentukan faktor penskalaan x dan y untuk gambar pratinjau. Fungsi ini juga digunakan untuk
menentukan rotasi buffer gambar sensor.
Class CameraCharacteristics dan Surface
memungkinkan penghitungan rotasi relatif sensor kamera:
/** * Computes rotation required to transform the camera sensor output orientation to the * device's current orientation in degrees. * * @param characteristics The CameraCharacteristics to query for the sensor orientation. * @param surfaceRotationDegrees The current device orientation as a Surface constant. * @return Relative rotation of the camera sensor output. */ public fun computeRelativeRotation( characteristics: CameraCharacteristics, surfaceRotationDegrees: Int ): Int { val sensorOrientationDegrees = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION) ?: 0 // Reverse device orientation for back-facing cameras. val sign = if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT ) 1 else -1 // Calculate desired orientation relative to camera orientation to make // the image upright relative to the device orientation. return (sensorOrientationDegrees - surfaceRotationDegrees * sign + 360) % 360 }
Metrik jendela aplikasi
Ukuran layar tidak boleh digunakan untuk menentukan dimensi jendela bidik kamera; aplikasi kamera mungkin berjalan di sebagian layar, baik dalam mode multi-aplikasi di perangkat seluler atau mode bebas di ChromeOS.
WindowManager#getCurrentWindowMetrics()
(ditambahkan di API level 30) menampilkan ukuran jendela aplikasi, bukan
ukuran layar. Metode library Jetpack WindowManager
WindowMetricsCalculator#computeCurrentWindowMetrics()
dan WindowInfoTracker#currentWindowMetrics()
memberikan dukungan serupa dengan kompatibilitas mundur ke level API 14.
Rotasi 180 derajat
Rotasi perangkat 180 derajat (misalnya, dari orientasi alami ke
orientasi alami terbalik) tidak memicu
callback onConfigurationChanged(). Akibatnya, pratinjau kamera mungkin terbalik.
Untuk mendeteksi rotasi 180 derajat, terapkan
DisplayListener dan periksa rotasi perangkat dengan panggilan
ke Display#getRotation() di
callback onDisplayChanged().
Referensi eksklusif
Sebelum Android 10, hanya aktivitas terlihat paling atas di lingkungan multi-aplikasi
yang berada dalam status RESUMED. Hal ini membingungkan pengguna karena
sistem tidak memberikan indikasi aktivitas mana yang dilanjutkan.
Android 10 (level API 29) memperkenalkan multi-resume, yang membuat semua aktivitas yang terlihat berada dalam status RESUMED. Aktivitas yang terlihat masih dapat memasuki status PAUSED
jika, misalnya, aktivitas transparan berada di atas aktivitas atau
aktivitas tidak dapat difokuskan, seperti dalam mode picture-in-picture (lihat
Dukungan picture-in-picture).
Aplikasi yang menggunakan kamera, mikrofon, atau resource eksklusif atau singleton
apa pun di API level 29 atau yang lebih tinggi harus mendukung multi-resume. Misalnya, jika
tiga aktivitas yang dilanjutkan ingin menggunakan kamera, hanya satu yang dapat mengakses
resource eksklusif ini. Setiap aktivitas harus mengimplementasikan callback
onDisconnected() agar tetap mengetahui
akses preemptive ke kamera oleh aktivitas dengan prioritas lebih tinggi.
Untuk mengetahui informasi selengkapnya, lihat Multi-resume.
Referensi lainnya
- Untuk contoh Camera2, lihat aplikasi Camera2Basic di GitHub.
- Untuk mempelajari kasus penggunaan pratinjau CameraX, lihat Mengimplementasikan pratinjau CameraX.
- Untuk contoh implementasi pratinjau kamera CameraX, lihat repositori CameraXBasic di GitHub.
- Untuk mengetahui informasi tentang pratinjau kamera di ChromeOS, lihat Orientasi kamera.
- Untuk mengetahui informasi tentang pengembangan untuk perangkat foldable, lihat Mempelajari perangkat foldable.