Uygulamanız, Android 5.0 (API düzeyi 21)'den beri kullanımdan kaldırılan orijinal Camera sınıfını ("Camera1") kullanıyorsa modern bir Android kamera API'sine güncellemenizi önemle tavsiye ederiz. Android, CameraX (standartlaştırılmış, sağlam bir Jetpack kamera API'si) ve Camera2 (düşük seviyeli bir çerçeve API'si) sunar.
Ç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üzeydeki ayrıntıları ele aldığından sıfırdan kamera deneyimi oluşturmaya daha az, uygulamanızı farklılaştırmaya daha çok odaklanabilirsiniz.
- CameraX, parçalanmayı sizin için yönetir: CameraX, uzun vadeli bakım maliyetlerini ve cihaza özel kodu azaltarak kullanıcılara daha kaliteli deneyimler sunar. Bu konuyla ilgili daha fazla bilgi için Better Device Compatibility with CameraX (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 edilebilmesi için dikkatle tasarlanmıştır. Örneğin, CameraX Uzantıları ile fotoğraflarınıza kolayca Bokeh, yüz rötuşu, HDR (Yüksek Dinamik Aralık) ve düşük ışıkta parlaklık sağlayan gece çekimi modu uygulayabilirsiniz.
- Güncellenebilirlik: Android, yıl boyunca CameraX'e yeni özellikler ve hata düzeltmeleri ekler. CameraX'e geçerek uygulamanız, yalnızca yıllık Android sürümü yayınlarında değil, her CameraX sürümünde en yeni Android kamera teknolojisine sahip olur.
Bu kılavuzda, kamera uygulamalarıyla ilgili yaygın senaryoları bulabilirsiniz. Her senaryoda karşılaştırma için bir Camera1 uygulaması ve bir CameraX uygulaması yer alır.
Taşıma söz konusu olduğunda, bazen mevcut bir kod tabanıyla entegrasyon için daha fazla esneklik gerekir. Bu kılavuzdaki tüm CameraX kodları, CameraX'i daha basit bir şekilde kullanmak istiyorsanız ideal olan CameraController uygulamasına ve daha fazla esnekliğe ihtiyacınız varsa ideal olan CameraProvider uygulamasına sahiptir. Size en uygun olanı seçmenize yardımcı olmak için her birinin avantajlarını aşağıda bulabilirsiniz:
CameraController |
CameraProvider |
| Az kurulum kodu gerektirir. | Daha fazla kontrol sağlar |
| CameraX'in kurulum sürecinin daha fazlasını yönetmesine izin vermek, dokunarak odaklanma ve parmakla yakınlaştırma gibi işlevlerin otomatik olarak çalışmasını sağlar. |
Kurulumu uygulama geliştirici yaptığından, yapılandırmayı özelleştirmek için daha fazla fırsat vardır. Örneğin, ImageAnalysis içinde çıkış görüntüsü döndürmeyi etkinleştirebilir veya çıkış görüntüsü biçimini ayarlayabilirsiniz.
|
Kamera önizlemesi için PreviewView izni verilmesi, CameraX'in sorunsuz uçtan uca entegrasyon sunmasına olanak tanır. Bu, ML Kit entegrasyonumuzda olduğu gibi, ML modeli sonuç koordinatlarını (ör. yüz sınırlama kutuları) doğrudan önizleme koordinatlarına eşleyebilir.
|
Kamera önizlemesi için özel bir "Surface" kullanabilme özelliği, daha fazla esneklik sağlar. Örneğin, uygulamanızın diğer bölümlerine giriş olabilecek mevcut "Surface" kodunuzu kullanabilirsiniz. |
Taşıma işlemi sırasında takılırsanız CameraX Tartışma Grubu'ndan bize ulaşabilirsiniz.
Taşıma işleminden ö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, ortak kamera işlevlerini kullanım alanlarına ayırır. Bu nedenle, Camera1'de geliştiriciye bırakılan birçok görev CameraX tarafından otomatik olarak işlenir. CameraX'te, çeşitli kamera görevlerinde kullanabileceğiniz dört UseCase bulunur: Preview, ImageCapture, VideoCapture ve ImageAnalysis.
CameraX'in geliştiriciler için düşük düzeyli ayrıntıları ele almasına bir örnek, etkin UseCase'ler arasında paylaşılan ViewPort'dir. Bu şekilde, tüm
UseCaselar tam olarak aynı pikselleri görür. Camera1'de bu ayrıntıları kendiniz yönetmeniz gerekir. Cihazlar arasındaki en boy oranları değişken olduğundan önizlemenin çekilen medyayla eşleştirilmesi zordur.
Başka bir örnek olarak, CameraX, sağladığınız Lifecycle örneğinde Lifecycle geri çağırmalarını otomatik olarak işler. CameraX, bu mimari sayesinde uygulamanızın kamera bağlantısını tüm Android etkinliği yaşam döngüsü boyunca yönetir. Bu kapsamda, uygulamanız arka plana geçtiğinde kameranın kapatılması, ekranın artık göstermesi gerekmediğinde kamera önizlemesinin kaldırılması ve gelen bir görüntülü arama gibi başka bir etkinlik ön plana çıktığında kamera önizlemesinin duraklatılması gibi durumlar yer alır.
Son olarak, CameraX döndürme ve ölçeklendirme işlemlerini sizin tarafınızdan ek kod girilmesi gerekmeden gerçekleştirir. Yönü kilidi açılmış bir Activity söz konusu olduğunda, sistem yön değişikliklerinde Activity öğesini kaldırıp yeniden oluşturduğundan cihaz her döndürüldüğünde UseCase kurulumu yapılır. Bu durumda, UseCases her seferinde varsayılan olarak hedef döndürmelerini ekranın yönüyle eşleşecek şekilde ayarlar. CameraX'teki döndürmeler hakkında daha fazla bilgi edinin.
Ayrıntılara geçmeden önce CameraX'in UseCase yapısına ve Camera1 uygulamasının bu yapıyla nasıl ilişkili olduğuna dair genel bir bakış sunalım. (CameraX kavramları mavi, Camera1 kavramları ise yeşil renkte gösterilir.)
CameraX |
|||
| CameraController / CameraProvider Yapılandırması | |||
| ↓ | ↓ | ↓ | ↓ |
| Önizle | ImageCapture | VideoCapture | ImageAnalysis |
| ⁞ | ⁞ | ⁞ | ⁞ |
| Önizleme yüzeyini yönetme ve kameraya ayarlama | PictureCallback'i ayarlayın ve Kamera'da takePicture() işlevini çağırın. | Kamera ve MediaRecorder yapılandırmasını belirli bir sırayla yönetme | Önizleme yüzeyinin üzerine oluşturulmuş özel analiz kodu |
| ↑ | ↑ | ↑ | ↑ |
| Cihaza özel kod | |||
| ↑ | |||
| Cihaz döndürme ve ölçeklendirme yönetimi | |||
| ↑ | |||
| Kamera Oturumu Yönetimi (Kamera Seçimi, Yaşam Döngüsü Yönetimi) | |||
Camera1 |
|||
CameraX'te uyumluluk ve performans
CameraX, Android 5.0 (API düzeyi 21) ve sonraki sürümlerin yüklü olduğu 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 geliştirilmiştir. Bu sayede uygulamanızda cihaza özel kod kullanma ihtiyacı azalır. Ayrıca CameraX Test Laboratuvarımızda 5.0'dan itibaren tüm Android sürümlerinde 150'den fazla fiziksel cihazı test ediyoruz. Test laboratuvarındaki cihazların tam listesini inceleyebilirsiniz.
CameraX, kamera yığınını çalıştırmak için Executor kullanır. Uygulamanızın belirli iş parçacığı oluşturma şartları varsa CameraX'te kendi yürütücünüzü ayarlayabilirsiniz.
Ayarlanmazsa CameraX, optimize edilmiş varsayılan bir dahili Executor oluşturur ve kullanır.
CameraX'in temelini oluşturan platform API'lerinin çoğu, bazen yüzlerce milisaniye yanıt verebilen donanımla işlemler arası iletişimin (IPC) engellenmesini gerektirir. Bu nedenle CameraX, bu API'leri yalnızca arka plan iş parçacıklarından çağırır. Bu sayede ana iş parçacığının engellenmemesi ve kullanıcı arayüzünün akıcı kalması sağlanır. İleti dizileri hakkında daha fazla bilgi edinin.
Uygulamanızın hedef pazarında düşük seviye cihazlar varsa 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 düşük seviye cihazlarda önemli bir süre alabileceğinden uygulamanızın ihtiyaç duyduğu kamera grubunu belirtebilirsiniz. CameraX yalnızca kurulum sırasında bu kameralara bağlanır. Örneğin, uygulama yalnızca arkaya bakan kameraları kullanıyorsa bu yapılandırmayı DEFAULT_BACK_CAMERA ile ayarlayabilir. Bu durumda CameraX, gecikmeyi azaltmak için ön kamerayı başlatmaktan kaçınır.
Android geliştirme kavramları
Bu kılavuzda, Android geliştirme konusunda genel bilgi sahibi olduğunuz varsayılmaktadır. Temel bilgilerin yanı sıra aşağıdaki koda geçmeden önce anlamanız gereken birkaç kavramı aşağıda bulabilirsiniz:
- View Binding, XML düzen dosyalarınız için bir bağlama sınıfı oluşturarak Görünümlerinize Etkinliklerde referans vermenize olanak tanır. Bu işlem, sonraki birkaç kod snippet'inde gösterilmiştir. Görünüm bağlama ve
findViewById()(görünümlere referans vermenin önceki yolu) arasında bazı farklılıklar vardır ancak aşağıdaki kodda görünüm bağlama satırlarını benzer birfindViewById()çağrısıyla değiştirebilirsiniz. - Asenkron Coroutine'ler, Kotlin 1.3'te eklenen ve
ListenableFuturedö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ünden itibaren Jetpack Concurrent kitaplığıyla kolaylaştırılmıştır. Uygulamanıza eşzamansız bir eş yordam eklemek için:- Gradle dosyanıza
implementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")ekleyin. ListenableFuturedöndüren tüm CameraX kodlarınılaunchbloğuna veya askıya alma işlevine yerleştirin.ListenableFuturedöndüren işlev çağrısınaawait()geri çağırma işlevi ekleyin.- Eş yordamların işleyiş şeklini daha iyi anlamak için Eş yordam başlatma kılavuzuna bakın.
- Gradle dosyanıza
Sık karşılaşılan senaryoları taşıma
Bu bölümde, yaygın senaryoların Camera1'den CameraX'e nasıl taşınacağı açıklanmaktadır.
Her senaryo, Camera1 uygulaması, CameraX CameraProvider uygulaması ve CameraX CameraController uygulamasını kapsar.
Kamera seçme
Kamera uygulamanızda sunmak isteyebileceğiniz ilk özelliklerden biri, farklı kameraları seçme yöntemidir.
Camera1
Camera1'de, arka kamerayı açmak için parametre içermeyen Camera.open() işlevini çağırabilir veya açmak istediğiniz kameranın tam sayı kimliğini iletebilirsiniz. Bunun nasıl görünebileceğine dair bir örneği aşağıda bulabilirsiniz:
// 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 yapılır. CameraX, varsayılan kamerayı kullanma gibi yaygın durumları basitleştirir. Varsayılan olarak ön kamerayı mı yoksa arka kamerayı mı kullanmak istediğinizi belirtebilirsiniz. Ayrıca, CameraX'in CameraControl nesnesi, uygulamanız için yakınlaştırma seviyesini ayarlamanıza olanak tanır. Bu nedenle, uygulamanız mantıksal kameraları destekleyen bir cihazda çalışıyorsa uygun lense geçer.
Varsayılan arka kamerayı CameraController ile kullanmak için CameraX kodunu aşağıda bulabilirsiniz:
// 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çme örneğini aşağıda bulabilirsiniz (CameraController veya CameraProvider ile ön ya da 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 preceding "Android development concepts" // section. 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 getAvailableCameraInfos() işlevini çağırarak CameraProvider kullanırsanız bu da CameraX'te mümkündür. Bu işlev, isFocusMeteringSupported() gibi belirli kamera özelliklerini kontrol etmek için CameraInfo nesnesi sağlar. Ardından, CameraSelector yönteminde önceki örneklerde gösterildiği gibi kullanılmak üzere CameraSelector biçimine dönüştürebilirsiniz.CameraInfo.getCameraSelector()
Camera2CameraInfo sınıfını kullanarak her kamera hakkında daha fazla bilgi edinebilirsiniz. İstediğiniz kamera verileri için bir anahtarla getCameraCharacteristic() işlevini çağırın. Sorgulayabileceğiniz tüm anahtarların listesi için CameraCharacteristics sınıfına göz atın.
Aşağıda, kendinizin tanımlayabileceği özel bir checkFocalLength() işlevinin kullanıldığı 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
Çoğu kamera uygulaması, kamera feed'ini ekranda bir noktada göstermelidir. Camera1 ile yaşam döngüsü geri çağırmalarını doğru şekilde yönetmeniz ve önizlemenizin döndürme ve ölçeklendirme işlemlerini de belirlemeniz gerekir.
Ayrıca Camera1'de önizleme yüzeyi olarak TextureView veya SurfaceView kullanıp kullanmayacağınıza karar vermeniz gerekir. Her iki seçeneğin de dezavantajları vardır ve her iki durumda da Camera1, döndürme ve ölçeklendirme işlemlerini doğru şekilde yapmanızı gerektirir. Öte yandan CameraX'in PreviewView'sinde hem TextureView hem de SurfaceView için temel uygulamalar bulunur. CameraX, cihaz türü ve uygulamanızın üzerinde çalıştığı Android sürümü gibi faktörlere bağlı olarak hangi uygulamanın en iyi olduğuna karar verir. Uygulamalardan biri uyumluysa PreviewView.ImplementationMode ile tercihinizi belirtebilirsiniz. COMPATIBLE seçeneği, önizleme için TextureView kullanır ve PERFORMANCE değeri, mümkün olduğunda SurfaceView kullanır.
Camera1
Önizleme göstermek için kamera donanımından uygulamaya görüntü verilerini aktarmak üzere kullanılan android.view.SurfaceHolder.Callback arayüzünün uygulandığı kendi Preview sınıfınızı yazmanız gerekir.
Ardından, canlı görüntü önizlemesini başlatabilmeniz için Preview sınıfının Camera nesnesine iletilmesi gerekir.
// 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 da kullanmanız gerekir. Bu, Preview UseCase değerinin varsayıldığı ve kurulumun çok daha az iş gerektirdiği anlamına gelir:
// 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 ile PreviewView kullanmanız gerekmez ancak bu yine de önizleme kurulumunu Camera1'e kıyasla büyük ölçüde basitleştirir. Bu örnekte, gösterim amacıyla PreviewView kullanılmaktadır. Ancak daha karmaşık ihtiyaçlarınız varsa setSurfaceProvider()'ye iletilecek özel bir SurfaceProvider yazabilirsiniz.
Burada Preview UseCase, CameraController ile olduğu gibi 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 preceding "Android development concepts" // section. 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ının önizlemeye dokunduğunda odak noktasını ayarlamak yaygın bir kontrol yöntemidir.
Camera1
Camera1'de dokunarak odaklanma özelliğini uygulamak için Area öğesinin nereye odaklanmaya çalışacağını belirtmek üzere optimum odaklanma Camera değerini hesaplamanız gerekir. Bu Area, setFocusAreas()'ye aktarılır. 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 etkilidir.
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 odak ayarlanmışsa odağa öncelik vermek için kullanılır Areas. Bu örnekte yalnızca bir Area kullanıldığından ağırlık değeri önemli değildir. Dikdörtgenin koordinatları -1000 ile 1000 arasında değişir. Sol üst nokta (-1000, -1000) olur.
Sağ alt nokta (1000, 1000) olur. Yön, sensörün yönüne (yani sensörün gördüklerine) göre belirlenir. Yön, Camera.setDisplayOrientation() döndürme veya yansıtma işleminden 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, dokunarak odaklanma özelliğini otomatik olarak işlemek için PreviewView'nin dokunma etkinliklerini dinler. setTapToFocusEnabled() ile dokunarak odaklanma özelliğini etkinleştirebilir ve devre dışı bırakabilir, değeri de ilgili getter isTapToFocusEnabled() ile kontrol edebilirsiniz.
getTapToFocusState() yöntemi, CameraController üzerinde odaklanma durumundaki değişiklikleri izlemek için 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 odaklanma özelliğinin çalışması için bazı kurulum işlemleri yapmanız gerekir. Bu örnekte, PreviewView kullandığınız varsayılır. Aksi takdirde, mantığı özel Surface için geçerli olacak ş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
MeteringPointkullanarakMeteringPointFactory.createPoint()oluşturun. MeteringPointileFocusMeteringActionoluşturun.CameracihazınızdakiCameraControlnesnesiyle (bindToLifecycle()'dan döndürülen)startFocusAndMetering()'ü çağırın veFocusMeteringAction'i iletin.- (İsteğe bağlı)
FocusMeteringResultyanıtlayın. - Hareket dedektörünüzü
PreviewView.setOnTouchListener()içindeki 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 // preceding "Android development concepts" section. 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-zoom scenario for scaleGestureDetector definition. var didConsume = scaleGestureDetector.onTouchEvent(event) if (!scaleGestureDetector.isInProgress) { didConsume = gestureDetector.onTouchEvent(event) } didConsume }
Sıkıştırarak yakınlaştırma
Önizlemeyi yakınlaştırma ve uzaklaştırma, kamera önizlemesinin yaygın olarak kullanılan bir diğer doğrudan manipülasyonudur. Cihazlardaki kamera sayısının artmasıyla birlikte kullanıcılar, yakınlaştırma sonucunda en iyi odak uzaklığına sahip lensin otomatik olarak seçilmesini bekliyor.
Camera1
Camera1'i kullanarak yakınlaştırmanın iki yolu vardır. Camera.startSmoothZoom() yöntemi, geçerli yakınlaştırma seviyesinden ilettiğiniz yakınlaştırma seviyesine animasyon uygular. Camera.Parameters.setZoom() yöntemi, doğrudan ilettiğiniz yakınlaştırma seviyesine atlar. İkisinden birini kullanmadan önce, ihtiyacınız olan ilgili yakınlaştırma yöntemlerinin kameranızda mevcut olduğundan emin olmak için sırasıyla isSmoothZoomSupported() veya isZoomSupported()'u arayın.
Bu örnekte, iki parmakla yakınlaştırma özelliğini uygulamak için setZoom() kullanılır. Bunun nedeni, önizleme yüzeyindeki dokunma işleyicisinin, iki parmakla yakınlaştırma hareketi gerçekleştiğinde sürekli olarak etkinlikleri tetiklemesi ve bu nedenle yakınlaştırma seviyesini her seferinde anında güncellemesidir. ZoomTouchListener sınıfı bu bölümün ilerleyen kısımlarında tanımlanır ve bunu önizleme yüzeyinizin dokunma dinleyicisine geri çağırma olarak ayarlamanız gerekir.
// 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
Dokunarak odaklanmaya benzer şekilde, CameraController, otomatik olarak iki parmakla yakınlaştırmayı işlemek için PreviewView'un dokunma etkinliklerini dinler. setPinchToZoomEnabled() ile iki parmakla yakınlaştırma özelliğini etkinleştirebilir ve devre dışı bırakabilir, değeri ise ilgili getter isPinchToZoomEnabled() ile kontrol edebilirsiniz.
getZoomState() yöntemi, CameraController üzerindeki ZoomState'da 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
CameraProvider ile yakınlaştırma özelliğini kullanmak için kurulum yapmanız gerekir. PreviewView kullanmıyorsanız mantığı, özel Surface için geçerli olacak şekilde uyarlamanız gerekir.
PreviewView kullanırken uygulamanız gereken adımlar şunlardır:
- Pinch etkinliklerini işlemek için bir ölçek hareketi algılayıcı ayarlayın.
bindToLifecycle()işlevini çağırdığınızdaCameraörneğinin döndürüldüğüCamera.CameraInfonesnesindenZoomStatedeğerini alın.ZoomState,zoomRatiodeğerine sahipse bunu mevcut yakınlaştırma oranı olarak kaydedin.ZoomStateüzerindezoomRatioyoksa kameranın varsayılan yakınlaştırma oranını (1.0) kullanın.- Yeni yakınlaştırma oranını belirlemek için geçerli yakınlaştırma oranı ile
scaleFactordeğerini çarpın ve sonucuCameraControl.setZoomRatio()içine aktarın. - Hareket dedektörünüzü
PreviewView.setOnTouchListener()içindeki 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-zoom scenario for gestureDetector definition. didConsume = gestureDetector.onTouchEvent(event) } didConsume }
Fotoğraf çekme
Bu bölümde, fotoğraf çekiminin nasıl tetikleneceği (deklanşör düğmesine basıldığında, zamanlayıcı süresi dolduktan sonra veya seçtiğiniz başka bir etkinlikte) gösterilmektedir.
Camera1
Camera1'de, resim verileri istendiğinde yönetmek için önce bir Camera.PictureCallback tanımlarsınız. JPEG resim verilerini işlemek için PictureCallback ile ilgili basit bir örneği burada 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öntemi, farklı veri türleri için üç farklı parametreye sahiptir. İlk parametre, ShutterCallback için kullanılır (bu örnekte tanımlanmamıştır). İkinci parametre, ham (sıkıştırılmamış) kamera verilerini işlemek için PictureCallback'dır. Üçüncü parametre, JPEG resim verilerini işlemek için 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 yöntemi, kendi takePicture() yöntemini uygulayarak görüntü yakalama için Camera1'in basitliğini korur. Burada, MediaStore girişi yapılandırmak ve buraya kaydedilecek bir fotoğraf çekmek için 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 çekme işlemi, CameraController ile fotoğraf çekme işlemiyle neredeyse aynı şekilde çalışır. Ancak takePicture() öğesini çağırmak için öncelikle bir ImageCapture oluşturup bağlamanız gerekir:
UseCase
// 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() işlevini çağırabilirsiniz. takePhoto() işlevinin tam bir ö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 kaydetmek, şu ana kadar ele alınan senaryolardan çok daha karmaşıktır. Sürecin her bölümü genellikle belirli bir sırayla düzgün şekilde ayarlanmalıdır. Ayrıca, video ve sesin senkronize olduğunu doğrulamanız veya ek cihaz tutarsızlıklarıyla uğraşmanız gerekebilir.
Gördüğünüz gibi, CameraX bu karmaşıklığın büyük bir kısmını sizin için tekrar ele alıyor.
Camera1
Camera1 kullanılarak video yakalama işlemi için Camera ve MediaRecorder dikkatli bir şekilde yönetilmelidir. Ayrıca yöntemler belirli bir sırayla çağrılmalıdır. Uygulamanızın düzgün çalışması için bu sırayı izlemeniz gerekir:
- Kamerayı açın.
- Önizlemeyi hazırlayın ve başlatın (Uygulamanız, kaydedilen videoyu gösteriyorsa, ki genellikle durum böyledir).
MediaRecordertarafından kullanılabilmesi için kameranın kilidini açmak üzereCamera.unlock()numaralı telefonu arayın.MediaRecorderüzerinde aşağıdaki yöntemleri çağırarak kaydı yapılandırın:CameraörneğinizisetCamera(camera)ile 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çinCamcorderProfilebölümüne bakın. - Şu numaraya telefon et:
setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()). - Uygulamanızda videonun önizlemesi varsa
setPreviewDisplay(preview?.holder?.surface)işlevini çağırı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). prepare()yapılandırmanızı tamamlamak içinprepare()numaralı telefonu arayın.MediaRecorder
- Kaydı başlatmak için
MediaRecorder.start()'ı arayın. - Kaydı durdurmak için bu yöntemleri çağırın. Yine aynı sırayı izleyin:
- Şu numaraya telefon et:
MediaRecorder.stop(). - İsteğe bağlı olarak,
MediaRecorderçağrısı yaparak mevcutMediaRecorder.reset()yapılandırmasını kaldırın. - Şu numaraya telefon et:
MediaRecorder.release(). - Gelecekteki
MediaRecorderoturumlarındaCamera.lock()çağrılarak kullanılabilmesi için kamerayı kilitleyin.
- Şu numaraya telefon et:
- Önizlemeyi durdurmak için
Camera.stopPreview()numaralı telefonu arayın. - Son olarak, diğer işlemlerin kullanabilmesi için
Cameraserbest bırakmak üzereCamera.release()işlevini çağırın.
Tüm bu adımlar bir arada aşağıda verilmiştir:
// 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'lerini UseCase listesi eşzamanlı olarak kullanılabildiği sürece bağımsız şekilde açıp kapatabilirsiniz. ImageCapture ve
ImageAnalysis UseCase'ler varsayılan olarak etkindir. Bu nedenle fotoğraf çekmek için setEnabledUseCases()'ı aramanız gerekmedi.
Video kaydı için CameraController kullanmak istiyorsanız önce setEnabledUseCases() kullanarak VideoCapture UseCase izni vermeniz gerekir.
// CameraX: Enable VideoCapture UseCase on CameraController. cameraController.setEnabledUseCases(VIDEO_CAPTURE);
Video kaydetmeye başlamak istediğinizde CameraController.startRecording() işlevini çağırabilirsiniz. Bu işlev, kaydedilen videoyu File olarak kaydedebilir. Aşağıdaki örnekte bunu görebilirsiniz.
Ayrıca, başarı ve hata geri aramalarını işlemek için Executor ve OnVideoSavedCallback uygulayan bir sınıfı geçmeniz gerekir. Kaydın ne zaman sona ermesi gerektiğini belirtmek için CameraController.stopRecording() numaralı telefonu arayın.
Not: CameraX 1.3.0-alpha02 veya sonraki bir sürümü kullanıyorsanız videonuzda ses kaydını etkinleştirmenize ya da devre dışı bırakmanıza olanak tanıyan ek bir AudioConfig parametresi bulunur. Ses kaydını etkinleştirmek için mikrofon izinlerinizin olduğundan emin olmanız gerekir. Ayrıca, stopRecording() yöntemi 1.3.0-alpha02 sürümünde kaldırıldı ve startRecording(), video kaydını duraklatmak, devam ettirmek ve durdurmak 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 VideoCapture
UseCase oluşturmanız ve Recorder nesnesi iletmeniz gerekir. Recorder.Builder bölümünde video kalitesini ve isteğe bağlı olarak, cihazın istediğiniz kalite özelliklerini karşılayamadığı durumlarda kullanılan bir FallbackStrategy ayarlayabilirsiniz. Ardından, diğer UseCase'lerinizle VideoCapture örneğini CameraProvider'ye 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'ye kaydedilen video kayıtlarını başlatabilir. Bu örnekte MediaStore kullanılmaktadır.
Recorder üzerinde, hazırlamak için kullanabileceğiniz çeşitli yöntemler vardır. prepareRecording() çıkış seçeneklerini ayarlamak için MediaStore işlevini çağırın. Uygulamanızın cihazın mikrofonunu kullanma izni varsa withAudioEnabled() işlevini de çağırın.
Ardından, bağlam ve video kayıt etkinliklerini işlemek için start() etkinlik işleyicisi ileterek kaydı başlatmak üzere start() işlevini çağırın.Consumer<VideoRecordEvent> İş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
Camera Samples GitHub deposunda birkaç eksiksiz CameraX uygulaması bulunmaktadır. Bu örneklerde, bu kılavuzdaki senaryoların tam teşekküllü bir Android uygulamasına nasıl uyduğu gösterilmektedir.
CameraX'e geçiş konusunda ek destek almak veya Android Kamera API'leri paketiyle ilgili sorularınız varsa lütfen CameraX Tartışma Grubu'nda bizimle iletişime geçin.