Uygulamanız, Android 5.0 (API düzeyi 21)'den beri desteği sonlandırılan orijinal Camera
sınıfını ("Camera1") kullanıyorsa modern bir Android kamera API'sine geçmenizi önemle tavsiye ederiz. Android'de CameraX (standartlaştırılmış, güçlü bir Jetpack kamera API'si) ve Camera2 (düşük düzeyli bir çerçeve API'si) bulunur. Çoğu durumda uygulamanızı CameraX'e taşımanızı öneririz. Bunun nedeni aşağıda açıklanmıştır:
- Kullanım kolaylığı: CameraX, düşük düzey ayrıntıları ele alır. Böylece, sıfırdan kamera deneyimi oluşturmaya değil, uygulamanızı farklılaştırmaya daha fazla odaklanabilirsiniz.
- CameraX, parçalanmayı sizin adınıza yönetir: CameraX, uzun vadeli bakım maliyetlerini ve cihaza özgü kodu azaltarak kullanıcılara daha kaliteli deneyimler sunar. Bu konu hakkında daha fazla bilgi edinmek için CameraX ile Daha İyi Cihaz Uyumluluğu başlıklı blog yayınımıza göz atın.
- Gelişmiş özellikler: CameraX, gelişmiş işlevlerin uygulamanıza kolayca dahil edilmesini sağlamak için özenle tasarlanmıştır. Örneğin, CameraX uzantılarını kullanarak fotoğraflarınıza bokeh, yüz rötuşu, HDR (yüksek dinamik aralık) ve düşük ışıkta parlaklaştırma özelliğine sahip gece çekimi modunu kolayca uygulayabilirsiniz.
- Güncelleme yapılabilirlik: Android, yıl boyunca CameraX'te yeni özellikler ve hata düzeltmeleri yayınlar. CameraX'e geçiş yaptığınızda uygulamanız, yalnızca yıllık Android sürüm sürümlerinde değil, her CameraX sürümünde en son Android kamera teknolojisini alır.
Bu kılavuzda, kamera uygulamalarıyla ilgili yaygın senaryoları bulabilirsiniz. Her senaryo, karşılaştırma için bir Camera1 uygulaması ve bir CameraX uygulaması içerir.
Taşıma işlemi söz konusu olduğunda, bazen mevcut kod tabanıyla entegrasyon için ek esnekliğe ihtiyacınız olur. Bu kılavuzdaki tüm CameraX kodunda, CameraX'i kullanmanın en basit yolunu istiyorsanız kullanabileceğiniz bir CameraController
ve daha fazla esnekliğe ihtiyacınız varsa kullanabileceğiniz bir CameraProvider
uygulama vardır. Hangisinin sizin için en uygun olduğuna karar vermenize yardımcı olması amacıyla her birinin avantajlarını aşağıda bulabilirsiniz:
CameraController |
CameraProvider |
Az miktarda kurulum kodu gerektirir | Daha fazla kontrol sağlar |
Kurulum sürecinin daha büyük bir kısmını CameraX'in yönetmesine izin vermek, dokunarak odaklama ve iki parmak ucunu yakınlaştırma/uzaklaştırma gibi işlevlerin otomatik olarak çalışması anlamına gelir |
Kurulum uygulama geliştirici tarafından yapıldığından, çıkış görüntüsünü döndürme veya çıkış görüntü biçimini ImageAnalysis
|
Kamera önizlemesi için PreviewView gerektirmek, CameraX'in ML model sonucu koordinatlarını (yüz sınırlayıcı kutuları gibi) doğrudan önizleme koordinatlarıyla eşleyebilen ML Kit entegrasyonumuzda olduğu gibi sorunsuz bir uçtan uca entegrasyon sunmasına olanak tanır.
|
Kamera önizlemesi için özel bir "Surface" kullanabilmek daha fazla esneklik sağlar. Örneğin, uygulamanızın diğer bölümlerine giriş olarak kullanılabilecek mevcut "Surface" kodunuzu kullanabilirsiniz. |
Taşıma işleminde takılırsanız CameraX Tartışma Grubu'nda bizimle iletişime geçebilirsiniz.
Taşıma işlemini gerçekleştirmeden önce
CameraX ile Camera1 kullanımını karşılaştırma
Kod farklı görünse de Camera1 ve CameraX'teki temel kavramlar çok benzerdir. CameraX, yaygın kamera işlevlerini kullanım alanlarına soyutlar. Sonuç olarak, Camera1'de geliştiriciye bırakılan birçok görev CameraX tarafından otomatik olarak yönetilir. CameraX'te çeşitli kamera görevleri için kullanabileceğiniz dört UseCase
vardır: Preview
, ImageCapture
, VideoCapture
ve ImageAnalysis
.
CameraX'in geliştiriciler için düşük düzey ayrıntıları ele almasına örnek olarak, etkin UseCase
'lar arasında paylaşılan ViewPort
verilebilir. Bu sayede tüm UseCase
'ler tam olarak aynı pikselleri görür.
Camera1'de bu ayrıntıları kendiniz yönetmeniz gerekir. Ayrıca, cihazların kamera sensörleri ve ekranlarındaki en boy oranlarında değişiklik olması nedeniyle, önizlemenizin çekilen fotoğraf ve videolarla eşleşmesini sağlamak zor olabilir.
Başka bir örnek olarak CameraX, ilettiğiniz Lifecycle
örneğinde Lifecycle
geri çağırma işlevlerini otomatik olarak yönetir. Bu, CameraX'in uygulamanızın kameraya olan bağlantısını Android etkinliği yaşam döngüsünün tamamında ele aldığı anlamına gelir. Bu işlemler arasında uygulamanız arka plana geçtiğinde kameranın kapatılması, ekranda artık gösterilmesi gerekmediğinde kamera önizlemesinin kaldırılması ve gelen bir görüntülü görüşme gibi başka bir etkinlik ön planda öncelik kazandığında kamera önizlemesinin duraklatılması yer alır.
Son olarak CameraX, herhangi bir ek kod yazmanıza gerek kalmadan döndürme ve ölçeklendirme işlemlerini gerçekleştirir. Ekran yönünün kilidi açık olan bir Activity
'te, sistem yön değişikliklerinde Activity
'yi silip yeniden oluşturduğundan UseCase
kurulumu cihaz her döndürüldüğünde yapılır. Bu, UseCases
'ün hedef dönüşünü her seferinde varsayılan olarak ekranın yönüne uyacak şekilde ayarlamasına neden olur.
CameraX'te rotasyonlar hakkında daha fazla bilgi edinin.
Ayrıntılara geçmeden önce, CameraX'in UseCase
özelliklerine ve Camera1 uygulamasının bu özelliklere nasıl ilişkili olduğuna dair genel bir bakış sunuyoruz. (CameraX kavramları mavi, Camera1 kavramları ise yeşil renktedir.)
CameraX |
|||
CameraController / CameraProvider Yapılandırması | |||
↓ | ↓ | ↓ | ↓ |
Önizle | ImageCapture | VideoCapture | ImageAnalysis |
⁞ | ⁞ | ⁞ | ⁞ |
Önizleme yüzeyini yönetme ve kamerada ayarlama | PictureCallback'i ayarlayın ve Kamera'da takePicture() işlevini çağırın | Kamera ve MediaRecorder yapılandırmasını belirli bir sırada yönetme | Önizleme yüzeyinin üzerine inşa edilmiş özel analiz kodu |
↑ | ↑ | ↑ | ↑ |
Cihaza özgü kod | |||
↑ | |||
Cihaz Döndürme ve Ölçeklendirme Yönetimi | |||
↑ | |||
Kamera Oturumu Yönetimi (Kamera Seçimi, Yaşam Döngüsü Yönetimi) | |||
Kamera1 |
CameraX'te uyumluluk ve performans
CameraX, Android 5.0 (API düzeyi 21) ve sonraki sürümleri çalıştıran cihazları destekler. Bu, mevcut Android cihazların% 98'inden fazlasını temsil eder. CameraX, cihazlar arasındaki farklılıkları otomatik olarak ele alacak şekilde tasarlanmıştır. Bu sayede, uygulamanızda cihaza özel kod kullanma ihtiyacı azalır. Ayrıca, CameraX Test Lab'imizde 5.0'dan sonraki tüm Android sürümlerinde 150'den fazla fiziksel cihazı test ederiz. Test Lab'da bulunan cihazların tam listesini inceleyebilirsiniz.
CameraX, kamera yığınını çalıştırmak için bir Executor
kullanır. Uygulamanızda belirli iş parçacığı gereksinimleri varsa CameraX'te kendi yürütücünüzü ayarlayabilirsiniz. Ayarlanmazsa CameraX, optimize edilmiş bir varsayılan dahili Executor
oluşturur ve kullanır. CameraX'in oluşturulduğu platform API'lerinin çoğu, bazen yanıt vermesi yüzlerce milisaniye sürebilen donanımla işlemler arası iletişimi (IPC) engellemeyi gerektirir. Bu nedenle CameraX, bu API'leri yalnızca arka plan mesaj dizilerinden çağırır. Bu da ana mesaj dizisinin engellenmesini ve kullanıcı arayüzünün akıcı kalmasını sağlar.
Mesaj dizileri hakkında daha fazla bilgi edinin.
Uygulamanızın hedef pazarı düşük kaliteli cihazları kapsıyorsa CameraX, kamera sınırlayıcı ile kurulum süresini kısaltmanın bir yolunu sunar. Donanım bileşenlerine bağlanma işlemi, özellikle de düşük kaliteli cihazlarda önemli miktarda zaman alabileceğinden, uygulamanızın ihtiyaç duyduğu kamera grubunu belirtebilirsiniz. CameraX, bu kameralara yalnızca kurulum sırasında bağlanır. Örneğin, uygulama yalnızca arka kameraları kullanıyorsa bu yapılandırmayı DEFAULT_BACK_CAMERA
ile ayarlayabilir. Ardından CameraX, gecikmeyi azaltmak için ön kameraları başlatmaz.
Android geliştirme kavramları
Bu kılavuzda, Android geliştirme hakkında genel bilgi sahibi olduğunuz varsayılmaktadır. Temel bilgilere ek olarak, aşağıdaki koda geçmeden önce anlamanız gereken birkaç kavram aşağıda verilmiştir:
- Görüntü Bağlama, XML düzen dosyalarınız için bir bağlama sınıfı oluşturur. Böylece, aşağıdaki çeşitli kod snippet'lerinde yapıldığı gibi etkinliklerdeki görünümlerinize kolayca referans verebilirsiniz. Görüntü bağlama ile
findViewById()
(görüntülere referans vermeden önceki yöntem) arasında bazı farklılıklar vardır ancak aşağıdaki kodda görüntü bağlama satırlarını benzer birfindViewById()
çağrısıyla değiştirebilirsiniz. - Asenkron coroutine'ler, Kotlin 1.3'e eklenen ve
ListenableFuture
döndüren CameraX yöntemlerini işlemek için kullanılabilen bir eşzamanlılık tasarım kalıbıdır. Bu işlem, 1.1.0 sürümü itibarıyla Jetpack Concurrent kitaplığıyla kolaylaştırılmıştır. Uygulamanıza eşzamansız bir iş parçacığı eklemek için:- Gradle dosyanıza
implementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")
ekleyin. ListenableFuture
döndüren tüm CameraX kodlarınılaunch
bloğuna veya askıya alma işlevine koyun.- İşlev çağrısına
ListenableFuture
döndüren birawait()
çağrısı ekleyin. - İş parçacıklarının işleyiş şekli hakkında daha fazla bilgi edinmek için İş parçacığı başlatma kılavuzunu inceleyin.
- Gradle dosyanıza
Sık karşılaşılan senaryoları taşıma
Bu bölümde, Camera1'den CameraX'e yaygın senaryoların nasıl taşınacağı açıklanmaktadır.
Her senaryo, bir Camera1 uygulaması, bir CameraX CameraProvider
uygulanması ve bir CameraX CameraController
uygulaması içerir.
Kamera seçme
Kamera uygulamanızda, farklı kameraları seçmenin bir yolunu sunmak isteyebilirsiniz.
Kamera1
Camera1'de, ilk arka kamerayı açmak için Camera.open()
işlevini parametre olmadan çağırabilir veya açmak istediğiniz kameranın tam sayı kimliğini iletebilirsiniz. Aşağıda, bunun nasıl görünebileceğine dair bir örnek verilmiştir:
// Camera1: select a camera from id. // Note: opening the camera is a non-trivial task, and it shouldn't be // called from the main thread, unlike CameraX calls, which can be // on the main thread since CameraX kicks off background threads // internally as needed. private fun safeCameraOpen(id: Int): Boolean { return try { releaseCameraAndPreview() camera = Camera.open(id) true } catch (e: Exception) { Log.e(TAG, "failed to open camera", e) false } } private fun releaseCameraAndPreview() { preview?.setCamera(null) camera?.release() camera = null }
CameraX: CameraController
CameraX'te kamera seçimi CameraSelector
sınıfı tarafından yönetilir. CameraX, varsayılan kamerayı kullanmanın yaygın durumunu kolaylaştırır. Varsayılan ön kamerayı mı yoksa varsayılan arka kamerayı mı istediğinizi belirtebilirsiniz. Ayrıca CameraX'in CameraControl
nesnesi, uygulamanız için yakınlaştırma düzeyini kolayca ayarlamanıza olanak tanır. Bu sayede, uygulamanız mantıksal kameraları destekleyen bir cihazda çalışıyorsa uygun lense geçer.
Varsayılan arka kamerayı CameraController
ile kullanmaya yönelik CameraX kodu aşağıda verilmiştir:
// CameraX: select a camera with CameraController var cameraController = LifecycleCameraController(baseContext) val selector = CameraSelector.Builder() .requireLensFacing(CameraSelector.LENS_FACING_BACK).build() cameraController.cameraSelector = selector
CameraX: CameraProvider
CameraProvider
ile varsayılan ön kamerayı seçmeyi gösteren bir örnek aşağıda verilmiştir (CameraController
veya CameraProvider
ile ön veya arka kamera kullanılabilir):
// CameraX: select a camera with CameraProvider. // Use await() within a suspend function to get CameraProvider instance. // For more details on await(), see the "Android development concepts" // section above. private suspend fun startCamera() { val cameraProvider = ProcessCameraProvider.getInstance(this).await() // Set up UseCases (more on UseCases in later scenarios) var useCases:Array= ... // Set the cameraSelector to use the default front-facing (selfie) // camera. val cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA try { // Unbind UseCases before rebinding. cameraProvider.unbindAll() // Bind UseCases to camera. This function returns a camera // object which can be used to perform operations like zoom, // flash, and focus. var camera = cameraProvider.bindToLifecycle( this, cameraSelector, useCases) } catch(exc: Exception) { Log.e(TAG, "UseCase binding failed", exc) } }) ... // Call startCamera in the setup flow of your app, such as in onViewCreated. override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) ... lifecycleScope.launch { startCamera() } }
Hangi kameranın seçileceğini kontrol etmek istiyorsanız CameraX'te de getAvailableCameraInfos()
çağrısını yaparak CameraProvider
kullanabilirsiniz. Bu çağrı, isFocusMeteringSupported()
gibi belirli kamera özelliklerini kontrol etmek için size bir CameraInfo
nesnesi sağlar.
Ardından, CameraInfo.getCameraSelector()
yöntemiyle yukarıdaki örneklerde olduğu gibi kullanılacak bir CameraSelector
öğesine dönüştürebilirsiniz.
Camera2CameraInfo
sınıfını kullanarak her kamera hakkında daha fazla bilgi edinebilirsiniz. İstediğiniz kamera verilerinin anahtarıyla getCameraCharacteristic()
işlevini çağırın. Sorgulayabileceğiniz tüm anahtarların listesini görmek için CameraCharacteristics
sınıfını inceleyin.
Aşağıda, kendiniz tanımlayabileceğiniz özel bir checkFocalLength()
işlevi kullanan bir örnek verilmiştir:
// CameraX: get a cameraSelector for first camera that matches the criteria // defined in checkFocalLength(). val cameraInfo = cameraProvider.getAvailableCameraInfos() .first { cameraInfo -> val focalLengths = Camera2CameraInfo.from(cameraInfo) .getCameraCharacteristic( CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS ) return checkFocalLength(focalLengths) } val cameraSelector = cameraInfo.getCameraSelector()
Önizleme gösterme
Kamera uygulamalarının çoğunun, kamera feed'ini bir noktada ekranda göstermesi gerekir. Camera1 ile yaşam döngüsü geri çağrılarını doğru şekilde yönetmeniz ve önizlemenizin dönüşümünü ve ölçeklendirmesini belirlemeniz gerekir.
Ayrıca, Kamera1'de önizleme yüzeyiniz olarak TextureView
mi yoksa SurfaceView
mi kullanacağınıza karar vermeniz gerekir.
Her iki seçenek de bazı dezavantajlara sahiptir ve her iki durumda da Camera1, döndürme ve ölçeklendirmeyi doğru şekilde yönetmenizi gerektirir. Öte yandan CameraX'in PreviewView
, hem TextureView
hem de SurfaceView
için temel uygulamalara sahiptir.
CameraX, cihaz türü ve uygulamanızın çalıştığı Android sürümü gibi faktörlere bağlı olarak en iyi uygulamaya karar verir. Her iki uygulama da uyumluysa tercihinizi PreviewView.ImplementationMode
ile belirtebilirsiniz.
COMPATIBLE
seçeneği, önizleme için TextureView
, PERFORMANCE
değeri ise (mümkün olduğunda) SurfaceView
kullanır.
Kamera1
Önizleme göstermek için, kamera donanımından uygulamaya resim verilerini aktarmak için kullanılan android.view.SurfaceHolder.Callback
arayüzünün bir uygulamasını içeren kendi Preview
sınıfınızı yazmanız gerekir. Ardından, canlı resim önizlemesini başlatabilmek için Preview
sınıfı Camera
nesnesine aktarılmalıdır.
// Camera1: set up a camera preview. class Preview( context: Context, private val camera: Camera ) : SurfaceView(context), SurfaceHolder.Callback { private val holder: SurfaceHolder = holder.apply { addCallback(this@Preview) setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS) } override fun surfaceCreated(holder: SurfaceHolder) { // The Surface has been created, now tell the camera // where to draw the preview. camera.apply { try { setPreviewDisplay(holder) startPreview() } catch (e: IOException) { Log.d(TAG, "error setting camera preview", e) } } } override fun surfaceDestroyed(holder: SurfaceHolder) { // Take care of releasing the Camera preview in your activity. } override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) { // If your preview can change or rotate, take care of those // events here. Make sure to stop the preview before resizing // or reformatting it. if (holder.surface == null) { return // The preview surface does not exist. } // Stop preview before making changes. try { camera.stopPreview() } catch (e: Exception) { // Tried to stop a non-existent preview; nothing to do. } // Set preview size and make any resize, rotate or // reformatting changes here. // Start preview with new settings. camera.apply { try { setPreviewDisplay(holder) startPreview() } catch (e: Exception) { Log.d(TAG, "error starting camera preview", e) } } } } class CameraActivity : AppCompatActivity() { private lateinit var viewBinding: ActivityMainBinding private var camera: Camera? = null private var preview: Preview? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewBinding = ActivityMainBinding.inflate(layoutInflater) setContentView(viewBinding.root) // Create an instance of Camera. camera = getCameraInstance() preview = camera?.let { // Create the Preview view. Preview(this, it) } // Set the Preview view as the content of the activity. val cameraPreview: FrameLayout = viewBinding.cameraPreview cameraPreview.addView(preview) } }
CameraX: CameraController
CameraX'te geliştirici olarak yönetmeniz gereken çok daha az şey vardır. CameraController
kullanıyorsanız PreviewView
'u da kullanmanız gerekir. Bu, Preview
UseCase
değerinin ima edildiği anlamına gelir. Bu da kurulumu çok daha az iş haline getirir:
// CameraX: set up a camera preview with a CameraController. class MainActivity : AppCompatActivity() { private lateinit var viewBinding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewBinding = ActivityMainBinding.inflate(layoutInflater) setContentView(viewBinding.root) // Create the CameraController and set it on the previewView. var cameraController = LifecycleCameraController(baseContext) cameraController.bindToLifecycle(this) val previewView: PreviewView = viewBinding.cameraPreview previewView.controller = cameraController } }
CameraX: CameraProvider
CameraX'in CameraProvider
özelliğiyle PreviewView
'ü kullanmanız gerekmez ancak Camera1'e kıyasla önizleme kurulumunu büyük ölçüde basitleştirir. Bu örnekte, açıklama amacıyla bir PreviewView
kullanılmıştır ancak daha karmaşık ihtiyaçlarınız varsa setSurfaceProvider()
'ye aktarmak için özel bir SurfaceProvider
yazabilirsiniz.
Burada, CameraController
'de olduğu gibi Preview
UseCase
ima edilmez. Bu nedenle, bunu ayarlamanız gerekir:
// CameraX: set up a camera preview with a CameraProvider. // Use await() within a suspend function to get CameraProvider instance. // For more details on await(), see the "Android development concepts" // section above. private suspend fun startCamera() { val cameraProvider = ProcessCameraProvider.getInstance(this).await() // Create Preview UseCase. val preview = Preview.Builder() .build() .also { it.setSurfaceProvider( viewBinding.viewFinder.surfaceProvider ) } // Select default back camera. val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA try { // Unbind UseCases before rebinding. cameraProvider.unbindAll() // Bind UseCases to camera. This function returns a camera // object which can be used to perform operations like zoom, // flash, and focus. var camera = cameraProvider.bindToLifecycle( this, cameraSelector, useCases) } catch(exc: Exception) { Log.e(TAG, "UseCase binding failed", exc) } }) ... // Call startCamera() in the setup flow of your app, such as in onViewCreated. override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) ... lifecycleScope.launch { startCamera() } }
Odaklamak için dokunma
Kamera önizlemeniz ekrandayken kullanıcı önizlemeye dokunduğunda odak noktasını ayarlamak yaygın bir kontroldür.
Kamera1
Kamera1'de dokunarak odaklama özelliğini uygulamak için Camera
'ın odaklanmaya çalışmasını istediğiniz yeri belirtmek üzere optimum odaklama Area
değerini hesaplamanız gerekir. Bu Area
, setFocusAreas()
'a iletilir. Ayrıca, Camera
üzerinde uyumlu bir odak modu ayarlamanız gerekir. Odaklanılan alan yalnızca mevcut odaklanma modu FOCUS_MODE_AUTO
, FOCUS_MODE_MACRO
, FOCUS_MODE_CONTINUOUS_VIDEO
veya FOCUS_MODE_CONTINUOUS_PICTURE
ise etkili olur.
Her Area
, belirtilen ağırlığa sahip bir dikdörtgendir. Ağırlık, 1 ile 1.000 arasında bir değerdir ve birden fazla ayarlanmışsa odak Areas
için öncelik belirlemek amacıyla kullanılır. Bu örnekte yalnızca bir Area
kullanıldığı için ağırlık değeri önemli değildir. Dikdörtgenin koordinatları -1000 ile 1000 arasındadır. Sol üst nokta (-1000, -1000) olur.
Sağ alttaki nokta (1000, 1000) olur. Yön, sensörün yönüne (yani sensörün gördüğü yöne) göredir. Yön, Camera.setDisplayOrientation()
'ün döndürülmesinden veya yansıtılmasından etkilenmez. Bu nedenle, dokunma etkinliği koordinatlarını sensör koordinatlarına dönüştürmeniz gerekir.
// Camera1: implement tap-to-focus. class TapToFocusHandler : Camera.AutoFocusCallback { private fun handleFocus(event: MotionEvent) { val camera = camera ?: return val parameters = try { camera.getParameters() } catch (e: RuntimeException) { return } // Cancel previous auto-focus function, if one was in progress. camera.cancelAutoFocus() // Create focus Area. val rect = calculateFocusAreaCoordinates(event.x, event.y) val weight = 1 // This value's not important since there's only 1 Area. val focusArea = Camera.Area(rect, weight) // Set the focus parameters. parameters.setFocusMode(Parameters.FOCUS_MODE_AUTO) parameters.setFocusAreas(listOf(focusArea)) // Set the parameters back on the camera and initiate auto-focus. camera.setParameters(parameters) camera.autoFocus(this) } private fun calculateFocusAreaCoordinates(x: Int, y: Int) { // Define the size of the Area to be returned. This value // should be optimized for your app. val focusAreaSize = 100 // You must define functions to rotate and scale the x and y values to // be values between 0 and 1, where (0, 0) is the upper left-hand side // of the preview, and (1, 1) is the lower right-hand side. val normalizedX = (rotateAndScaleX(x) - 0.5) * 2000 val normalizedY = (rotateAndScaleY(y) - 0.5) * 2000 // Calculate the values for left, top, right, and bottom of the Rect to // be returned. If the Rect would extend beyond the allowed values of // (-1000, -1000, 1000, 1000), then crop the values to fit inside of // that boundary. val left = max(normalizedX - (focusAreaSize / 2), -1000) val top = max(normalizedY - (focusAreaSize / 2), -1000) val right = min(left + focusAreaSize, 1000) val bottom = min(top + focusAreaSize, 1000) return Rect(left, top, left + focusAreaSize, top + focusAreaSize) } override fun onAutoFocus(focused: Boolean, camera: Camera) { if (!focused) { Log.d(TAG, "tap-to-focus failed") } } }
CameraX: CameraController
CameraController
, otomatik olarak odaklanmak için dokunma işlemlerini yönetmek üzere PreviewView
'un dokunma etkinliklerini dinler. setTapToFocusEnabled()
ile dokunarak odaklama özelliğini etkinleştirebilir ve devre dışı bırakabilir, ilgili alıcı isTapToFocusEnabled()
ile değeri kontrol edebilirsiniz.
getTapToFocusState()
yöntemi, CameraController
öğesindeki odak durumundaki değişiklikleri izlemek için bir LiveData
nesnesi döndürür.
// CameraX: track the state of tap-to-focus over the Lifecycle of a PreviewView, // with handlers you can define for focused, not focused, and failed states. val tapToFocusStateObserver = Observer{ state -> when (state) { CameraController.TAP_TO_FOCUS_NOT_STARTED -> Log.d(TAG, "tap-to-focus init") CameraController.TAP_TO_FOCUS_STARTED -> Log.d(TAG, "tap-to-focus started") CameraController.TAP_TO_FOCUS_FOCUSED -> Log.d(TAG, "tap-to-focus finished (focus successful)") CameraController.TAP_TO_FOCUS_NOT_FOCUSED -> Log.d(TAG, "tap-to-focus finished (focused unsuccessful)") CameraController.TAP_TO_FOCUS_FAILED -> Log.d(TAG, "tap-to-focus failed") } } cameraController.getTapToFocusState().observe(this, tapToFocusStateObserver)
CameraX: CameraProvider
CameraProvider
kullanırken dokunarak odaklama özelliğini etkinleştirmek için bazı ayarlar yapmanız gerekir. Bu örnekte PreviewView
kullandığınız varsayılır. Aksi takdirde mantığı özel Surface
'inize uyacak şekilde uyarlamanız gerekir.
PreviewView
kullanırken uygulamanız gereken adımlar şunlardır:
- Dokunma etkinliklerini işlemek için bir hareket algılayıcı ayarlayın.
- Dokunma etkinliğiyle
MeteringPointFactory.createPoint()
kullanarak birMeteringPoint
oluşturun. MeteringPoint
ileFocusMeteringAction
oluşturun.Camera
öğenizle (bindToLifecycle()
'den döndürülen)startFocusAndMetering()
'ı çağırın veFocusMeteringAction
öğesini iletin.CameraControl
- (İsteğe bağlı)
FocusMeteringResult
'e yanıt verin. - Hareket algılama cihazınızı
PreviewView.setOnTouchListener()
'te dokunma etkinliklerine yanıt verecek şekilde ayarlayın.
// CameraX: implement tap-to-focus with CameraProvider. // Define a gesture detector to respond to tap events and call // startFocusAndMetering on CameraControl. If you want to use a // coroutine with await() to check the result of focusing, see the // "Android development concepts" section above. val gestureDetector = GestureDetectorCompat(context, object : SimpleOnGestureListener() { override fun onSingleTapUp(e: MotionEvent): Boolean { val previewView = previewView ?: return val camera = camera ?: return val meteringPointFactory = previewView.meteringPointFactory val focusPoint = meteringPointFactory.createPoint(e.x, e.y) val meteringAction = FocusMeteringAction .Builder(meteringPoint).build() lifecycleScope.launch { val focusResult = camera.cameraControl .startFocusAndMetering(meteringAction).await() if (!result.isFocusSuccessful()) { Log.d(TAG, "tap-to-focus failed") } } } } ) ... // Set the gestureDetector in a touch listener on the PreviewView. previewView.setOnTouchListener { _, event -> // See pinch-to-zooom scenario for scaleGestureDetector definition. var didConsume = scaleGestureDetector.onTouchEvent(event) if (!scaleGestureDetector.isInProgress) { didConsume = gestureDetector.onTouchEvent(event) } didConsume }
Yakınlaştırmak için sıkıştırma
Önizlemeyi yakınlaştırma ve uzaklaştırma, kamera önizlemesinde yapılan diğer bir yaygın doğrudan değişikliktir. Cihazlardaki kamera sayısı arttıkça kullanıcılar, yakınlaştırma sonucunda en iyi odak uzaklığına sahip lensin otomatik olarak seçilmesini de bekler.
Kamera1
Camera1'i kullanarak yakınlaştırmanın iki yolu vardır. Camera.startSmoothZoom()
yöntemi, mevcut yakınlaştırma düzeyinden ilettiğiniz yakınlaştırma düzeyine animasyonlu geçiş yapar. Camera.Parameters.setZoom()
yöntemi, doğrudan ilettiğiniz yakınlaştırma düzeyine atlar. Bu iki yöntemden birini kullanmadan önce, ihtiyacınız olan yakınlaştırma yöntemlerinin kameranızda bulunduğundan emin olmak için sırasıyla isSmoothZoomSupported()
veya isZoomSupported()
numaralı telefonu arayın.
Bu örnekte, yakınlaştırmak için parmak ucunu yakınlaştırmak/uzaklaştırmak hareketi uygulanır. Önizleme yüzeyindeki dokunma dinleyicisi, parmak ucunu yakınlaştırmak/uzaklaştırmak hareketi gerçekleştikçe sürekli olarak etkinlikleri tetikler. Bu nedenle, her seferinde yakınlaştırma düzeyi hemen güncellenir.setZoom()
Aşağıda tanımlanan ZoomTouchListener
sınıfı, önizleme yüzeyinizin dokunma dinleyicisine geri çağırma işlevi olarak ayarlanmalıdır.
// Camera1: implement pinch-to-zoom. // Define a scale gesture detector to respond to pinch events and call // setZoom on Camera.Parameters. val scaleGestureDetector = ScaleGestureDetector(context, object : ScaleGestureDetector.OnScaleGestureListener { override fun onScale(detector: ScaleGestureDetector): Boolean { val camera = camera ?: return false val parameters = try { camera.parameters } catch (e: RuntimeException) { return false } // In case there is any focus happening, stop it. camera.cancelAutoFocus() // Set the zoom level on the Camera.Parameters, and set // the Parameters back onto the Camera. val currentZoom = parameters.zoom parameters.setZoom(detector.scaleFactor * currentZoom) camera.setParameters(parameters) return true } } ) // Define a View.OnTouchListener to attach to your preview view. class ZoomTouchListener : View.OnTouchListener { override fun onTouch(v: View, event: MotionEvent): Boolean = scaleGestureDetector.onTouchEvent(event) } // Set a ZoomTouchListener to handle touch events on your preview view // if zoom is supported by the current camera. if (camera.getParameters().isZoomSupported()) { view.setOnTouchListener(ZoomTouchListener()) }
CameraX: CameraController
Odaklamak için dokunma işlevine benzer şekilde CameraController
, iki parmak ucunu yakınlaştırmak için otomatik olarak işlem yapmak üzere PreviewView'ın dokunma etkinliklerini dinler. setPinchToZoomEnabled()
ile yakınlaştırma/uzaklaştırma özelliğini etkinleştirebilir ve devre dışı bırakabilir, ilgili alıcı isPinchToZoomEnabled()
ile değeri kontrol edebilirsiniz.
getZoomState()
yöntemi, CameraController
üzerinde ZoomState
öğesinde yapılan değişiklikleri izlemek için bir LiveData
nesnesi döndürür.
// CameraX: track the state of pinch-to-zoom over the Lifecycle of // a PreviewView, logging the linear zoom ratio. val pinchToZoomStateObserver = Observer{ state -> val zoomRatio = state.getZoomRatio() Log.d(TAG, "ptz-zoom-ratio $zoomRatio") } cameraController.getZoomState().observe(this, pinchToZoomStateObserver)
CameraX: CameraProvider
İki parmak ucunu yakınlaştırmak için CameraProvider
'te bazı ayarlar yapmanız gerekir. PreviewView
kullanmıyorsanız mantığı özel Surface
'inize uyarlamanız gerekir.
PreviewView
kullanırken uygulamanız gereken adımlar şunlardır:
- İki parmak ucunu yakınlaştırma/uzaklaştırma etkinliklerini işlemek için bir ölçek hareketi algılayıcısı oluşturun.
Camera.CameraInfo
nesnesindenZoomState
'yi alın. Bu nesne,bindToLifecycle()
çağrıldığındaCamera
örneğini döndürür.ZoomState
değerindezoomRatio
varsa bunu mevcut yakınlaştırma oranı olarak kaydedin.ZoomState
üzerindezoomRatio
yoksa kameranın varsayılan yakınlaştırma oranını (1, 0) kullanın.- Yeni yakınlaştırma oranını belirlemek için mevcut yakınlaştırma oranının
scaleFactor
ile çarpımını alın ve bunuCameraControl.setZoomRatio()
'a iletin. - Hareket algılama cihazınızı
PreviewView.setOnTouchListener()
'te dokunma etkinliklerine yanıt verecek şekilde ayarlayın.
// CameraX: implement pinch-to-zoom with CameraProvider. // Define a scale gesture detector to respond to pinch events and call // setZoomRatio on CameraControl. val scaleGestureDetector = ScaleGestureDetector(context, object : SimpleOnGestureListener() { override fun onScale(detector: ScaleGestureDetector): Boolean { val camera = camera ?: return val zoomState = camera.cameraInfo.zoomState val currentZoomRatio: Float = zoomState.value?.zoomRatio ?: 1f camera.cameraControl.setZoomRatio( detector.scaleFactor * currentZoomRatio ) } } ) ... // Set the scaleGestureDetector in a touch listener on the PreviewView. previewView.setOnTouchListener { _, event -> var didConsume = scaleGestureDetector.onTouchEvent(event) if (!scaleGestureDetector.isInProgress) { // See pinch-to-zooom scenario for gestureDetector definition. didConsume = gestureDetector.onTouchEvent(event) } didConsume }
Fotoğraf çekme
Bu bölümde, fotoğraf çekme işleminin nasıl tetikleneceği gösterilmektedir. Deklanşör düğmesine basıldığında, zamanlayıcının süresi dolduktan sonra veya sizin seçtiğiniz başka bir etkinlikte tetiklenmesi gerekir.
Kamera1
Camera1'de, istek üzerine resim verilerini yönetmek için önce bir Camera.PictureCallback
tanımlarsınız. JPEG resim verilerini işleme yönelik basit bir PictureCallback
örneğini aşağıda bulabilirsiniz:
// Camera1: define a Camera.PictureCallback to handle JPEG data. private val picture = Camera.PictureCallback { data, _ -> val pictureFile: File = getOutputMediaFile(MEDIA_TYPE_IMAGE) ?: run { Log.d(TAG, "error creating media file, check storage permissions") return@PictureCallback } try { val fos = FileOutputStream(pictureFile) fos.write(data) fos.close() } catch (e: FileNotFoundException) { Log.d(TAG, "file not found", e) } catch (e: IOException) { Log.d(TAG, "error accessing file", e) } }
Ardından, fotoğraf çekmek istediğinizde Camera
örneğinizde takePicture()
yöntemini çağırırsınız. Bu takePicture()
yönteminde, farklı veri türleri için üç farklı parametre vardır. İlk parametre bir ShutterCallback
içindir (bu örnekte tanımlanmamıştır). İkinci parametre, ham (sıkıştırılmamış) kamera verilerini işlemek için bir PictureCallback
içindir. Üçüncü parametre, JPEG resim verilerini işlemek için bir PictureCallback
olduğundan bu örnekte kullanılan parametredir.
// Camera1: call takePicture on Camera instance, passing our PictureCallback. camera?.takePicture(null, null, picture)
CameraX: CameraController
CameraX'in CameraController
, kendi takePicture()
yöntemini uygulayarak resim çekme konusunda Camera1'in basitliğini korur. Burada, bir MediaStore
girişini yapılandıracak ve bu girişe kaydedilecek bir fotoğraf çekecek bir işlev tanımlayın.
// CameraX: define a function that uses CameraController to take a photo. private val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS" private fun takePhoto() { // Create time stamped name and MediaStore entry. val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US) .format(System.currentTimeMillis()) val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, name) put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg") if(Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image") } } // Create output options object which contains file + metadata. val outputOptions = ImageCapture.OutputFileOptions .Builder(context.getContentResolver(), MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues) .build() // Set up image capture listener, which is triggered after photo has // been taken. cameraController.takePicture( outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback { override fun onError(e: ImageCaptureException) { Log.e(TAG, "photo capture failed", e) } override fun onImageSaved( output: ImageCapture.OutputFileResults ) { val msg = "Photo capture succeeded: ${output.savedUri}" Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show() Log.d(TAG, msg) } } ) }
CameraX: CameraProvider
CameraProvider
ile fotoğraf çekmek, CameraController
ile neredeyse aynı şekilde çalışır ancak takePicture()
'i çağıracak bir nesneye sahip olmak için önce bir ImageCapture
UseCase
oluşturmanız ve bağlamanız gerekir:
// CameraX: create and bind an ImageCapture UseCase. // Make a reference to the ImageCapture UseCase at a scope that can be accessed // throughout the camera logic in your app. private var imageCapture: ImageCapture? = null ... // Create an ImageCapture instance (can be added with other // UseCase definitions). imageCapture = ImageCapture.Builder().build() ... // Bind UseCases to camera (adding imageCapture along with preview here, but // preview is not required to use imageCapture). This function returns a camera // object which can be used to perform operations like zoom, flash, and focus. var camera = cameraProvider.bindToLifecycle( this, cameraSelector, preview, imageCapture)
Ardından, fotoğraf çekmek istediğinizde ImageCapture.takePicture()
'ü arayabilirsiniz. takePhoto()
işlevinin tam örneği için bu bölümdeki CameraController
koduna bakın.
// CameraX: define a function that uses CameraController to take a photo. private fun takePhoto() { // Get a stable reference of the modifiable ImageCapture UseCase. val imageCapture = imageCapture ?: return ... // Call takePicture on imageCapture instance. imageCapture.takePicture( ... ) }
Video kaydetme
Video kaydetme, şimdiye kadar ele aldığımız senaryolardan çok daha karmaşık bir süreçtir. Sürecin her bölümü, genellikle belirli bir sırayla düzgün şekilde ayarlanmalıdır. Ayrıca, video ve sesin senkronize olup olmadığını doğrulamanız veya cihazla ilgili başka tutarsızlıklarla ilgilenmeniz gerekebilir.
Göreceğiniz gibi, CameraX bu karmaşıklığın büyük bir kısmını sizin için yönetir.
Kamera1
Camera1 kullanılarak video kaydı yapmak için Camera
ve MediaRecorder
'nin dikkatli bir şekilde yönetilmesi gerekir. Ayrıca yöntemlerin belirli bir sırayla çağrılması gerekir. Uygulamanızın düzgün çalışması için bu sıraya uymanız gerekir:
- Kamerayı açın.
- Bir önizleme hazırlayın ve başlatın (uygulamanızda videonun kaydedildiğini gösteriyorsa, ki genellikle öyledir).
Camera.unlock()
'u arayarak kameranın kilidiniMediaRecorder
tarafından kullanılması için açın.MediaRecorder
üzerinde aşağıdaki yöntemleri çağırarak kaydı yapılandırın:Camera
örneğinizisetCamera(camera)
'a bağlayın.- Şu numaraya telefon et:
setAudioSource(MediaRecorder.AudioSource.CAMCORDER)
. - Şu numaraya telefon et:
setVideoSource(MediaRecorder.VideoSource.CAMERA)
. - Kaliteyi ayarlamak için
setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_1080P))
numaralı telefonu arayın. Tüm kalite seçenekleri içinCamcorderProfile
bölümüne bakın. - Şu numaraya telefon et:
setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString())
. - Uygulamanızda videonun önizlemesi varsa
setPreviewDisplay(preview?.holder?.surface)
numaralı telefonu arayın. - Şu numaraya telefon et:
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
. - Şu numaraya telefon et:
setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT)
. - Şu numaraya telefon et:
setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT)
. MediaRecorder
cihazınızın yapılandırmasını tamamlamak içinprepare()
numaralı telefonu arayın.
- Kaydı başlatmak için
MediaRecorder.start()
numaralı telefonu arayın. - Kaydı durdurmak için bu yöntemleri çağırın. Yine tam olarak şu sırayı uygulayın:
- Şu numaraya telefon et:
MediaRecorder.stop()
. - İsteğe bağlı olarak,
MediaRecorder.reset()
işlevini çağırarak mevcutMediaRecorder
yapılandırmasını kaldırabilirsiniz. - Şu numaraya telefon et:
MediaRecorder.release()
. Camera.lock()
'u arayarak gelecektekiMediaRecorder
oturumlarının kullanabilmesi için kamerayı kilitleyin.
- Şu numaraya telefon et:
- Önizlemeyi durdurmak için
Camera.stopPreview()
numaralı telefonu arayın. - Son olarak, diğer süreçlerin kullanabilmesi için
Camera
kaynağını serbest bırakmak üzereCamera.release()
işlevini çağırın.
Tüm bu adımları birlikte inceleyin:
// Camera1: set up a MediaRecorder and a function to start and stop video // recording. // Make a reference to the MediaRecorder at a scope that can be accessed // throughout the camera logic in your app. private var mediaRecorder: MediaRecorder? = null private var isRecording = false ... private fun prepareMediaRecorder(): Boolean { mediaRecorder = MediaRecorder() // Unlock and set camera to MediaRecorder. camera?.unlock() mediaRecorder?.run { setCamera(camera) // Set the audio and video sources. setAudioSource(MediaRecorder.AudioSource.CAMCORDER) setVideoSource(MediaRecorder.VideoSource.CAMERA) // Set a CamcorderProfile (requires API Level 8 or higher). setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)) // Set the output file. setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()) // Set the preview output. setPreviewDisplay(preview?.holder?.surface) setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT) setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT) // Prepare configured MediaRecorder. return try { prepare() true } catch (e: IllegalStateException) { Log.d(TAG, "preparing MediaRecorder failed", e) releaseMediaRecorder() false } catch (e: IOException) { Log.d(TAG, "setting MediaRecorder file failed", e) releaseMediaRecorder() false } } return false } private fun releaseMediaRecorder() { mediaRecorder?.reset() mediaRecorder?.release() mediaRecorder = null camera?.lock() } private fun startStopVideo() { if (isRecording) { // Stop recording and release camera. mediaRecorder?.stop() releaseMediaRecorder() camera?.lock() isRecording = false // This is a good place to inform user that video recording has stopped. } else { // Initialize video camera. if (prepareVideoRecorder()) { // Camera is available and unlocked, MediaRecorder is prepared, now // you can start recording. mediaRecorder?.start() isRecording = true // This is a good place to inform the user that recording has // started. } else { // Prepare didn't work, release the camera. releaseMediaRecorder() // Inform user here. } } }
CameraX: CameraController
CameraX'in CameraController
özelliğiyle, ImageCapture
,
VideoCapture
ve ImageAnalysis
UseCase
'leri bağımsız olarak değiştirebilirsiniz (UseCases listesi eşzamanlı olarak kullanılabiliyorsa).
ImageCapture
ve ImageAnalysis
UseCase
'ler varsayılan olarak etkindir. Bu nedenle, fotoğraf çekmek için setEnabledUseCases()
'ı çağırmanız gerekmez.
Video kaydı için CameraController
kullanmak istiyorsanız önce VideoCapture
UseCase
'e izin vermek üzere setEnabledUseCases()
'ü kullanmanız gerekir.
// CameraX: Enable VideoCapture UseCase on CameraController. cameraController.setEnabledUseCases(VIDEO_CAPTURE);
Video kaydetmeye başlamak istediğinizde CameraController.startRecording()
işlevini çağırabilirsiniz. Aşağıdaki örnekte de görebileceğiniz gibi, bu işlev kaydedilen videoyu bir File
'a kaydedebilir. Ayrıca, başarı ve hata geri çağırmalarını işlemek için bir Executor
ve OnVideoSavedCallback
sınıfını uygulayan bir sınıf da iletmeniz gerekir. Kaydın sona ermesi gerektiğinde CameraController.stopRecording()
düğmesine basın.
Not: CameraX 1.3.0-alpha02 veya sonraki bir sürümü kullanıyorsanız videonuzda ses kaydını etkinleştirmenize veya devre dışı bırakmanıza olanak tanıyan ek bir AudioConfig
parametresi vardır. Ses kaydını etkinleştirmek için mikrofon izinlerinizin bulunduğundan emin olmanız gerekir.
Ayrıca, 1.3.0-alpha02 sürümünde stopRecording()
yöntemi kaldırıldı ve startRecording()
, video kaydını duraklatma, devam ettirme ve durdurma için kullanılabilecek bir Recording
nesnesi döndürüyor.
// CameraX: implement video capture with CameraController. private val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS" // Define a VideoSaveCallback class for handling success and error states. class VideoSaveCallback : OnVideoSavedCallback { override fun onVideoSaved(outputFileResults: OutputFileResults) { val msg = "Video capture succeeded: ${outputFileResults.savedUri}" Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show() Log.d(TAG, msg) } override fun onError(videoCaptureError: Int, message: String, cause: Throwable?) { Log.d(TAG, "error saving video: $message", cause) } } private fun startStopVideo() { if (cameraController.isRecording()) { // Stop the current recording session. cameraController.stopRecording() return } // Define the File options for saving the video. val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US) .format(System.currentTimeMillis()) val outputFileOptions = OutputFileOptions .Builder(File(this.filesDir, name)) .build() // Call startRecording on the CameraController. cameraController.startRecording( outputFileOptions, ContextCompat.getMainExecutor(this), VideoSaveCallback() ) }
CameraX: CameraProvider
CameraProvider
kullanıyorsanız bir VideoCapture
UseCase
oluşturmanız ve bir Recorder
nesnesi iletmeniz gerekir. Recorder.Builder
'te video kalitesini ve isteğe bağlı olarak da bir FallbackStrategy
belirleyebilirsiniz. Bu, bir cihazın istediğiniz kalite özelliklerini karşılayamadığı durumları ele alır. Ardından, VideoCapture
örneğini diğer UseCase
'lerinizle CameraProvider
'a bağlayın.
// CameraX: create and bind a VideoCapture UseCase with CameraProvider. // Make a reference to the VideoCapture UseCase and Recording at a // scope that can be accessed throughout the camera logic in your app. private lateinit var videoCapture: VideoCaptureprivate var recording: Recording? = null ... // Create a Recorder instance to set on a VideoCapture instance (can be // added with other UseCase definitions). val recorder = Recorder.Builder() .setQualitySelector(QualitySelector.from(Quality.FHD)) .build() videoCapture = VideoCapture.withOutput(recorder) ... // Bind UseCases to camera (adding videoCapture along with preview here, but // preview is not required to use videoCapture). This function returns a camera // object which can be used to perform operations like zoom, flash, and focus. var camera = cameraProvider.bindToLifecycle( this, cameraSelector, preview, videoCapture)
Bu noktada, Recorder
videoCapture.output
mülkünde kullanılabilir. Recorder
, File
, ParcelFileDescriptor
veya MediaStore
'a kaydedilen video kayıtları başlatabilir. Bu örnekte MediaStore
kullanılmaktadır.
Recorder
'te, hazırlamak için çağrılabilecek çeşitli yöntemler vardır. MediaStore
çıkış seçeneklerini ayarlamak için prepareRecording()
'ü arayın. Uygulamanız cihazın mikrofonunu kullanma iznine sahipse withAudioEnabled()
numaralı telefonu da arayın.
Ardından, video kaydı etkinliklerini işlemek için bir bağlam ve Consumer<VideoRecordEvent>
etkinlik dinleyicisi ile birlikte kaydı başlatmak için start()
işlevini çağırın. İşlem başarılı olursa döndürülen Recording
, kaydı duraklatmak, devam ettirmek veya durdurmak için kullanılabilir.
// CameraX: implement video capture with CameraProvider. private val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS" private fun startStopVideo() { val videoCapture = this.videoCapture ?: return if (recording != null) { // Stop the current recording session. recording.stop() recording = null return } // Create and start a new recording session. val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US) .format(System.currentTimeMillis()) val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, name) put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4") if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/CameraX-Video") } } val mediaStoreOutputOptions = MediaStoreOutputOptions .Builder(contentResolver, MediaStore.Video.Media.EXTERNAL_CONTENT_URI) .setContentValues(contentValues) .build() recording = videoCapture.output .prepareRecording(this, mediaStoreOutputOptions) .withAudioEnabled() .start(ContextCompat.getMainExecutor(this)) { recordEvent -> when(recordEvent) { is VideoRecordEvent.Start -> { viewBinding.videoCaptureButton.apply { text = getString(R.string.stop_capture) isEnabled = true } } is VideoRecordEvent.Finalize -> { if (!recordEvent.hasError()) { val msg = "Video capture succeeded: " + "${recordEvent.outputResults.outputUri}" Toast.makeText( baseContext, msg, Toast.LENGTH_SHORT ).show() Log.d(TAG, msg) } else { recording?.close() recording = null Log.e(TAG, "video capture ends with error", recordEvent.error) } viewBinding.videoCaptureButton.apply { text = getString(R.string.start_capture) isEnabled = true } } } } }
Ek kaynaklar
Kamera Örnekleri GitHub Depomuz'da birkaç tam CameraX uygulaması bulunmaktadır. Bu örnekler, bu kılavuzda yer alan senaryoların tam teşekküllü bir Android uygulamasına nasıl uygulandığını gösterir.
CameraX'e geçiş konusunda daha fazla destek almak istiyorsanız veya Android kamera API'leri paketiyle ilgili sorularınız varsa lütfen CameraX Tartışma Grubu'nda bizimle iletişime geçin.