Camera1'i CameraX'e taşı

Uygulamanız, Android 5.0 (API düzeyi 21) sürümünden bu yana kullanımdan kaldırılmış olan orijinal Camera sınıfını ("Kamera1") kullanıyorsa modern bir Android Camera API'sine güncelleme yapmanızı önemle tavsiye ederiz. Android, KameraX (standartlaştırılmış, sağlam bir Jetpack kamera API'si) ve Kamera2 (düşük seviye bir çerçeve API'si) sunar. Çoğu durumda uygulamanızı CameraX'e taşımanızı öneririz. Bu durumun nedeni şudur:

  • Kullanım kolaylığı: CameraX, düşük seviyeli ayrıntıları halleder. Böylece sıfırdan kamera deneyimi oluşturmaya daha az, uygulamanızı diğerlerinden ayırmaya daha çok odaklanabilirsiniz.
  • KameraX, parçalamayı sizin için halleder: CameraX, uzun vadeli bakım maliyetlerini ve cihaza özel kodları azaltarak kullanıcılara daha kaliteli deneyimler sunar. Bu konuda daha fazla bilgi için KameraX ile Daha İyi Cihaz Uyumluluğu blog yayınımıza göz atın.
  • Gelişmiş özellikler: CameraX, gelişmiş işlevleri uygulamanıza entegre etmeyi kolaylaştırmak için özenle tasarlanmıştır. Örneğin, KameraX Uzantıları ile fotoğraflarınıza kolayca Bokeh, Yüz Rötuş, HDR (Yüksek Dinamik Aralık) ve düşük ışıkta parlaklık veren gece çekim modunu uygulayabilirsiniz.
  • Güncellenebilirlik: Android, yıl boyunca CameraX için yeni özellikler ve hata düzeltmeleri yayınlar. CameraX'e geçiş yaparak uygulamanız yalnızca yılda yayınlanan Android sürümlerinde değil, her CameraX sürümünde en son Android kamera teknolojisine sahip olur.

Bu kılavuzda kamera uygulamalarına yönelik yaygın senaryoları bulacaksınız. Her senaryo, karşılaştırma için bir Camera1 uygulaması ve bir CameraX uygulaması içerir.

Taşıma söz konusu olduğunda, bazen mevcut bir kod tabanıyla entegrasyon için daha fazla esnekliğe ihtiyacınız olur. Bu kılavuzdaki tüm CameraX kodlarının bir CameraController uygulaması vardır. CameraX'i kullanmanın en kolay yolunu öğrenmek istiyorsanız ve daha fazla esnekliğe ihtiyacınız varsa CameraProvider uygulamasını kullanabilirsiniz. Sizin için hangisinin uygun olduğuna karar vermenize yardımcı olmak için her birinin avantajları aşağıda açıklanmıştır:

Kamera Denetleyicisi

KameraSağlayıcı

Küçük kurulum kodu gerektirir Daha fazla kontrol olanağı sağlar
CameraX'in kurulum işleminin daha büyük kısmını işlemesine izin verdiğinizde, dokunmak için dokunma ve sıkıştırmak için sıkıştırma gibi işlevlerin otomatik olarak çalışması anlamına gelir Kurulum işlemleri uygulama geliştirici tarafından yapıldığından, yapılandırmayı özelleştirmek için daha fazla fırsat elde edebilirsiniz. Örneğin, çıkış için resim döndürmeyi etkinleştirebilir veya ImageAnalysis ürününde çıktının resim biçimini ayarlayabilirsiniz.
Kamera önizlemesi için PreviewView özelliğinin zorunlu kılınması, CameraX'in sorunsuz bir uçtan uca entegrasyon sunmasını sağlar. Bu entegrasyon, ML modeli sonuç koordinatlarını (yüz sınırlayıcı kutular gibi) doğrudan önizleme koordinatlarıyla eşleyebilen ML Kit entegrasyonunda yer alır. Kamera önizlemesi için özel bir "Yüzey" kullanma imkanı, daha fazla esneklik sağlar. Örneğin, uygulamanızın diğer bölümlerine giriş niteliğinde olan mevcut "Yüzey" kodunuzu kullanmak gibi

Taşımaya çalışırken sorun yaşarsanız bize KameraX Tartışma Grubu üzerinden ulaşın.

Taşıma işleminden önce

CameraX ile Camera1 kullanımını karşılaştırma

Kod farklı görünebilir ancak Camera1 ve CameraX'teki temel kavramlar çok benzerdir. CameraX, yaygın kamera işlevlerini kullanım alanlarına soyutlar ve sonuç olarak Camera1'de geliştiriciye bırakılan birçok görev CameraX tarafından otomatik olarak işlenmektedir. 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 alt düzey ayrıntıları işlemesine örnek olarak, etkin UseCase'lar arasında paylaşılan ViewPort verilebilir. Bu, tüm UseCase öğelerinin tam olarak aynı pikselleri görmesini sağlar. Kamera1'de bu ayrıntıları kendiniz yönetmeniz gerekir. Cihazların kamera sensörleri ile ekranlarındaki en boy oranları değişkenliği göz önünde bulundurulduğunda, önizlemenizin çekilen fotoğraflar ve videolarla eşleştiğinden emin olmak zor olabilir.

Başka bir örnek olarak CameraX, Lifecycle geri çağırma işlemini ilettiğiniz Lifecycle örneğinde otomatik olarak işler. Bu durum, şu durumlar da dahil olmak üzere Android etkinlik döngüsünün tamamı boyunca uygulamanızın kamerayla olan bağlantısını CameraX'in işlemesi anlamına gelir: Uygulamanız arka plana geçtiğinde kamerayı kapatma; ekranda artık gösterilmesi gerekmediğinde kamera önizlemesini kaldırma ve gelen görüntülü görüşme gibi öncelikli başka bir etkinlik sırasında öncelikli olduğunda kamera önizlemesini duraklatma.

Son olarak, CameraX herhangi bir ek koda gerek kalmadan döndürme ve ölçeklendirme işlemlerini gerçekleştirir. Yönü kilidi açılmış bir Activity söz konusu olduğunda, sistem yön değiştirildiğinde Activity öğesini yok edip yeniden oluşturduğundan cihaz her döndürüldüğünde UseCase kurulumu yapılır. Bunun sonucunda UseCases, hedef döndürmelerini her defasında ekranın yönüyle varsayılan olarak eşleşecek şekilde ayarlar. KameraX'te döndürmeler hakkında daha fazla bilgi edinin.

Ayrıntılara geçmeden önce CameraX'in UseCase'lerine ve Camera1 uygulamasının nasıl bir ilişki kurduğuna dair genel bir bakışı burada bulabilirsiniz. (KameraX kavramları mavi, Camera1 kavramları ise yeşil renkle vurgulanmıştır.)

KameraX

CameraController / CameraProvider Yapılandırması
Önizleme Resim Yakalama Video Yakalama ImageAnalysis
Önizleme yüzeyini yönet ve Kamera'da ayarla ImageCallback'i ayarlayın ve Kamera'da TakeResim() işlevini çağırın Kamera ve MediaRecorder yapılandırmasını belirli bir sırayla yönetme Önizleme yüzeyinin üzerinde oluşturulan özel analiz kodu
Cihaza Özgü Kod
Cihaz Rotasyonu 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 etmektedir. CameraX, cihazlar arasındaki farklılıkları otomatik olarak ele alacak şekilde tasarlanmıştır ve uygulamanızdaki cihaza özel kod ihtiyacını azaltır. Ayrıca, KameraX Test Lab'de 5.0 sürümünden beri tüm Android sürümlerinde 150'den fazla fiziksel cihazı test ediyoruz. Şu anda Test Lab'de bulunan cihazların tam listesini inceleyebilirsiniz.

CameraX, kamera yığınını uygulamak için bir Executor kullanır. Uygulamanızın belirli iş parçacığı gereksinimleri varsa KameraX'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 derlendiği platform API'lerinin çoğu, bazen yanıt vermesi yüzlerce milisaniye sürebilen donanımlarla işlemler arası iletişimi (IPC) engelleme gerektirir. Bu nedenle CameraX, bu API'leri yalnızca arka plan iş parçacıklarından çağırır. Bu da ana iş parçacığının engellenmemesini ve kullanıcı arayüzünün değişken kalmasını sağlar. Mesaj dizileri hakkında daha fazla bilgi edinin.

Uygulamanızın hedef pazarında düşük teknolojiye sahip 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 teknolojili cihazlarda çok da uzun sürebileceğinden, uygulamanızın ihtiyacı olan kamera setini belirleyebilirsiniz. CameraX bu kameralara yalnızca kurulum sırasında bağlanır. Örneğin, uygulama yalnızca arka yüz kameraları kullanıyorsa bu yapılandırmayı DEFAULT_BACK_CAMERA ile ayarlayabilir ve daha sonra CameraX, gecikmeyi azaltmak için ön kameraları 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 dışında, aşağıdaki koda geçmeden önce anlaşılmasına yardımcı olacak birkaç kavram şunlardır:

Taşıma yaygın senaryoları

Bu bölümde, sık karşılaşılan senaryoların Camera1'den CameraX'e nasıl taşınacağı açıklanmaktadır. Her senaryo bir Camera1 uygulamasını, bir CameraX CameraProvider uygulamasını ve bir CameraX CameraController uygulamasını kapsar.

Kamera seçiliyor

Kamera uygulamanızda, sunmak isteyebileceğiniz ilk şeylerden biri farklı kameralar seçmenin bir yoludur.

Kamera1

Kamera1'de, ilk arka kamerayı açmak için parametre olmadan Camera.open() çağırabilir veya açmak istediğiniz kamera için bir tam sayı kimliği aktarabilirsiniz. Bunun nasıl görünebileceğine dair bir örnek aşağıda 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: Kamera Denetleyicisi

CameraX'te kamera seçimi CameraSelector sınıfı tarafından yapılır. CameraX, varsayılan kamerayı kullanma sürecini kolaylaştırır. Varsayılan ön kamerayı mı yoksa varsayılan arka kamerayı mı istediğinizi belirtebilirsiniz. Dahası, CameraX'in CameraControl nesnesi, uygulamanız için yakınlaştırma düzeyini kolayca ayarlamanıza olanak tanıyor. Böylece uygulamanız, mantıksal kameraları destekleyen bir cihazda çalışıyorsa uygun lense geçiş yapıyor.

Varsayılan arka kamerayı CameraController ile kullanmak için gereken CameraX kodu:

// 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çmeye ilişkin bir örneği aşağıda bulabilirsiniz (ön veya arka kamera, CameraController veya CameraProvider ile 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 bu işlemi, getAvailableCameraInfos() yöntemini çağırarak CameraProvider özelliğini kullanarak CameraX'te de yapabilirsiniz. Bu yöntem, isFocusMeteringSupported() gibi belirli kamera özelliklerini kontrol etmeniz için size bir CameraInfo nesnesi sağlar. Daha sonra, bunu yukarıdaki örneklerde olduğu gibi CameraInfo.getCameraSelector() yöntemiyle kullanmak üzere bir CameraSelector biçimine dönüştürebilirsiniz.

Camera2CameraInfo sınıfını kullanarak her kamera hakkında daha fazla bilgi edinebilirsiniz. getCameraCharacteristic() numaralı telefonu arayarak istediğiniz kamera verisi anahtarını arayın. Sorgulayabileceğiniz tüm anahtarların listesi için CameraCharacteristics sınıfına bakın.

Aşağıda, kendi tanımlayabileceğiniz ö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

Kamera uygulamalarının çoğu, kamera feed'inin ekranda bir noktada gösterilmesi gerekir. Kamera1'de yaşam döngüsü geri çağırmalarını doğru bir şekilde yönetmeniz ve önizlemeniz için döndürme ve ölçeklendirmeyi de belirlemeniz gerekir.

Buna ek olarak, Kamera1'de önizleme yüzeyi olarak TextureView mı yoksa SurfaceView mi kullanacağınıza karar vermeniz gerekir. Her iki seçenekte de ödünler verilir. Her iki durumda da Kamera1, döndürmeyi ve ölçeklendirmeyi doğru bir şekilde yönetmenizi gerektirir. Öte yandan CameraX'in PreviewView öğesi 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 uygulamanın hangisi olduğuna karar verir. Uygulamalardan biri uyumluysa tercihinizi PreviewView.ImplementationMode ile belirtebilirsiniz. COMPATIBLE seçeneği önizleme için TextureView ve PERFORMANCE değeri (mümkün olduğunda) bir SurfaceView kullanır.

Kamera1

Önizleme göstermek için kamera donanımından uygulamaya görüntü verilerini iletmek için kullanılan android.view.SurfaceHolder.Callback arayüzü uygulamasını kullanarak kendi Preview sınıfınızı yazmanız gerekir. Ardından, canlı resim önizlemesini başlatabilmeniz için önce Preview sınıfının Camera nesnesine geçirilmesi 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: Kamera Denetleyicisi

CameraX'te, yani geliştirici olarak yönetmeniz gereken çok daha az şey var. CameraController kullanıyorsanız PreviewView kullanmanız da gerekir. Bu da Preview UseCase değerinin ima edildiği anlamına gelir. Bu da kurulumun çok daha az iş yapmasını sağlar:

// 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 ürünüyle PreviewView kullanmanız gerekmez, ancak Kamera1 üzerinde önizleme kurulumunu yine de büyük ölçüde basitleştirir. Bu örnekte, gösterim amaçlı olarak PreviewView kullanılmaktadır ancak daha karmaşık ihtiyaçlarınız varsa setSurfaceProvider() öğesine iletmek için özel bir SurfaceProvider yazabilirsiniz.

Burada, Preview UseCase, CameraController ile olduğu gibi ima edilmez. Bu nedenle, UseCase öğesini 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 dokunun

Kamera önizlemeniz ekrandayken, kullanıcı önizlemeye dokunduğunda odak noktasını ayarlamak yaygın kullanılan bir kontroldür.

Kamera1

Kamera1'de dokunarak odaklamayı uygulamak için Camera öğesinin nereye odaklanmayı denemesi gerektiğini belirtmek üzere optimum odağı Area hesaplamanız gerekir. Bu Area, setFocusAreas() öğesine aktarıldı. Ayrıca, Camera üzerinde uyumlu bir odak modu ayarlamanız gerekir. Odak alanı yalnızca geçerli odak modu FOCUS_MODE_AUTO, FOCUS_MODE_MACRO, FOCUS_MODE_CONTINUOUS_VIDEO veya FOCUS_MODE_CONTINUOUS_PICTURE olduğunda 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 çok değer ayarlanırsa Areas odağına öncelik vermek için kullanılır. 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). Sağ alt nokta (1000, 1000). Yön, sensör yönüyle, yani sensörün gördükleriyle ilişkilidir. Yön, Camera.setDisplayOrientation() öğesinin döndürmesinden veya yansıtması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: Kamera Denetleyicisi

CameraController, dokunarak odaklanmak için PreviewView kullanıcısının dokunma etkinliklerini dinler. Odaklanma özelliğini setTapToFocusEnabled() ile etkinleştirip devre dışı bırakabilir ve değeri ilgili alıcıyla isTapToFocusEnabled() kontrol edebilirsiniz.

getTapToFocusState() yöntemi, CameraController üzerindeki 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 odaklanmak için bazı ayarlar yapmanız gerekir. Bu örnekte, PreviewView kullandığınız varsayılmaktadır. Aksi takdirde, mantığı özel Surface öğenize uygulanacak şekilde uyarlamanız gerekir.

PreviewView kullanılırken uygulanacak adımlar şunlardır:

  1. Dokunma etkinliklerini işlemek için bir hareket algılayıcı oluşturun.
  2. Tap etkinliğinde, MeteringPointFactory.createPoint() kullanarak bir MeteringPoint oluşturun.
  3. MeteringPoint ile bir FocusMeteringAction oluşturun.
  4. Camera üzerinde CameraControl nesnesi varken (bindToLifecycle() tarafından geri verildi) FocusMeteringAction öğesini kullanarak startFocusAndMetering() çağrısı yapın.
  5. (İsteğe bağlı) FocusMeteringResult öğesini yanıtlayın.
  6. Hareket algılayıcı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
// "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ırın

Önizlemeyi yakınlaştırmak ve uzaklaştırmak, kamera önizlemesinin yaygın olarak kullanıldığı bir başka doğrudan yönlendirmedir. Cihazlardaki kamera sayısının artmasıyla birlikte kullanıcılar, yakınlaştırma sonucu olarak en iyi odak uzaklığına sahip lensin otomatik olarak seçilmesini de bekler.

Kamera1

Kamera1'i kullanarak iki şekilde yakınlaştırma yapılabilir. Camera.startSmoothZoom() yöntemi, geçerli yakınlaştırma düzeyinden geçiş yaptığınız yakınlaştırma düzeyine geçiş yapar. Camera.Parameters.setZoom() yöntemi, doğrudan geçiş yaptığınız yakınlaştırma düzeyine atlar. İkisinden birini kullanmadan önce, ihtiyacınız olan ilgili yakınlaştırma yöntemlerinin Kameranızda bulunduğundan emin olmak için sırasıyla isSmoothZoomSupported() veya isZoomSupported() yöntemini çağırın.

Bu örnekte, iki parmak ucunu yakınlaştırma ya da uzaklaştırmanın uygulanması için setZoom() kullanılmaktadır. Bunun nedeni, önizleme yüzeyindeki dokunma dinleyicinin, sıkıştırma hareketi sırasında sürekli olarak etkinlikleri tetiklemesi ve dolayısıyla her seferinde yakınlaştırma düzeyini hemen güncellemesidir. ZoomTouchListener sınıfı aşağıda tanımlanmıştır ve önizleme yüzeyinizin dokunma işleyicisine bir geri çağırma 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: Kamera Denetleyicisi

Odaklanmaya benzer şekilde CameraController, yakınlaştırmak için sıkıştırma işlemini otomatik olarak işlemek üzere PreviewView'un dokunma etkinliklerini dinler. setPinchToZoomEnabled() ile yakınlaştırmak için sıkıştırma özelliğini etkinleştirip devre dışı bırakabilir ve değeri ilgili getter isPinchToZoomEnabled() ile kontrol edebilirsiniz.

getZoomState() yöntemi, CameraController üzerinde ZoomState üzerinde 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ırmak için sıkıştırma özelliğini kullanmak için bazı ayarlar gerekir. PreviewView kullanmıyorsanız özel Surface öğenize uygulanacak mantığı uyarlamanız gerekir.

PreviewView kullanılırken uygulanacak adımlar şunlardır:

  1. Sıkıştırma etkinliklerini işlemek için bir ölçek hareketi algılayıcısı ayarlayın.
  2. bindToLifecycle() çağrısı yaptığınızda Camera örneğinin döndürüldüğü Camera.CameraInfo nesnesinden ZoomState kodunu alın.
  3. ZoomState için zoomRatio değeri varsa bunu geçerli yakınlaştırma oranı olarak kaydedin. ZoomState üzerinde zoomRatio yoksa kameranın varsayılan yakınlaştırma hızını (1, 0) kullanın.
  4. Yeni yakınlaştırma oranını belirlemek için mevcut yakınlaştırma oranının çarpımını scaleFactor ile alın ve bunu CameraControl.setZoomRatio() aralığına geçirin.
  5. Hareket algılayıcı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-zooom scenario for gestureDetector definition.
        didConsume = gestureDetector.onTouchEvent(event)
    }
    didConsume
}

Fotoğraf çekme

Bu bölümde, fotoğraf çekiminin nasıl tetikleneceği gösterilmektedir. Bu işlemi deklanşör düğmesine bastığınızda, zamanlayıcı sona erdikten sonra veya seçtiğiniz başka bir etkinlikte yapmanız gerekip gerekmediği gösterilmektedir.

Kamera1

Kamera1'de, ilk olarak istendiğinde resim verilerini yönetmek için bir Camera.PictureCallback tanımlarsınız. JPEG resim verilerini işlemek için aşağıdaki basit bir PictureCallback örneğini görebilirsiniz:

// 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, her 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 bir ShutterCallback içindir (bu örnekte tanımlanmamıştır). İkinci parametre, ham (sıkıştırılmamış) kamera verilerini işlemesi için bir PictureCallback parametresidir. Üçü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: Kamera Denetleyicisi

CameraX'in CameraController özelliği, kendi başına bir takePicture() yöntemini uygulayarak görüntü kaydı için Camera1'in basitliğini korur. Burada, bir 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 neredeyse aynı şekilde yapılır, ancak takePicture() çağrısı yapacağınız bir nesne için önce bir ImageCapture UseCase oluşturup 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() numaralı telefonu 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 kaydı, şimdiye kadar izlenen senaryolardan çok daha karmaşıktır. Sürecin her aşaması düzgün bir şekilde, genellikle belirli bir sırada ayarlanmalıdır. Ayrıca, video ve sesin senkronize olduğunu doğrulamanız veya ek cihaz tutarsızlıklarıyla başa çıkmanız gerekebilir.

Göreceğiniz üzere CameraX yine sizin için bu karmaşıklığın çoğunu hallediyor.

Kamera1

Kamera1 ile yapılan video çekimi, Camera ve MediaRecorder öğelerinin dikkatli bir şekilde yönetilmesini gerektirir. Yöntemler belirli bir sırada çağrılmalıdır. Uygulamanızın düzgün bir şekilde çalışması için şu sıraya uymanız zorunludur:

  1. Kamerayı açın.
  2. Bir önizleme hazırlayıp başlatın (uygulamanız kaydedilen videoyu gösteriyorsa bu genellikle geçerlidir).
  3. Camera.unlock() numaralı telefonu arayarak MediaRecorder tarafından kullanılmak üzere kameranın kilidini açın.
  4. MediaRecorder üzerinde aşağıdaki yöntemleri çağırarak kaydı yapılandırın:
    1. Camera örneğinizi setCamera(camera) ile bağlayın.
    2. Şu numaraya telefon et: setAudioSource(MediaRecorder.AudioSource.CAMCORDER).
    3. Şu numaraya telefon et: setVideoSource(MediaRecorder.VideoSource.CAMERA).
    4. Kaliteyi ayarlamak için setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_1080P)) numaralı telefonu arayın. Tüm kalite seçenekleri için CamcorderProfile adresine bakın.
    5. Şu numaraya telefon et: setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()).
    6. Uygulamanızda video önizlemesi varsa setPreviewDisplay(preview?.holder?.surface) numaralı telefonu arayın.
    7. Şu numaraya telefon et: setOutputFormat(MediaRecorder.OutputFormat.MPEG_4).
    8. Şu numaraya telefon et: setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT).
    9. Şu numaraya telefon et: setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT).
    10. MediaRecorder yapılandırmasını tamamlamak için prepare() numaralı telefonu arayın.
  5. Kaydı başlatmak için MediaRecorder.start() numaralı telefonu arayın.
  6. Kaydı durdurmak için aşağıdaki yöntemleri çağırın. Aynı şekilde aşağıdaki sıralamayı da uygulayın:
    1. Şu numaraya telefon et: MediaRecorder.stop().
    2. İsteğe bağlı olarak, MediaRecorder.reset() yöntemini çağırarak geçerli MediaRecorder yapılandırmasını kaldırın.
    3. Şu numaraya telefon et: MediaRecorder.release().
    4. Gelecekteki MediaRecorder oturumlarında Camera.lock() numaralı telefonu arayarak kamerayı kullanabilmesi için kamerayı kilitleyin.
  7. Önizlemeyi durdurmak için Camera.stopPreview() numaralı telefonu arayın.
  8. Son olarak, diğer işlemlerin kullanabilmesi amacıyla Camera öğesini serbest bırakmak için Camera.release() çağrısı yapın.

Bu adımların tümünü toplu olarak burada bulabilirsiniz:

// 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: Kamera Denetleyicisi

CameraX'in CameraController cihazıyla Kullanım Alanları listesi eş zamanlı olarak kullanılabildiği sürece ImageCapture, VideoCapture ve ImageAnalysis UseCase arasında bağımsız olarak geçiş yapabilirsiniz. ImageCapture ve ImageAnalysis UseCase varsayılan olarak etkin olduğundan, fotoğraf çekmek için setEnabledUseCases() numarasını çağırmanız gerekmiyordu.

Video kaydında CameraController kullanmak için öncelikle setEnabledUseCases() öğesini kullanarak VideoCapture UseCase öğesine izin 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 aşağıdaki örnekte gösterildiği gibi bir File klasörüne kaydedebilir. Ayrıca, başarı ve hata geri çağırmalarını işlemek için Executor ve OnVideoSavedCallback uygulayan bir sınıf iletmeniz gerekir. Kaydın sona ermesi gerektiğinde 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 veya devre dışı bırakmanıza olanak tanıyan ek bir AudioConfig parametresi vardır. Ses kaydını etkinleştirmek için mikrofon izinlerine sahip olmanız gerekir. Ayrıca stopRecording() yöntemi 1.3.0-alpha02 sürümünden kaldırılmıştır ve startRecording(), video kaydını duraklatmak, devam ettirmek ve durdurmak için kullanılabilecek bir Recording nesnesi döndürür.

// 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 bir Recorder nesnesini aktarmanız gerekir. Recorder.Builder üzerinde video kalitesini ve isteğe bağlı olarak bir cihazın istediğiniz kalite özelliklerini karşılayamadığı durumları işleyen FallbackStrategy öğesini ayarlayabilirsiniz. Ardından VideoCapture örneğini diğer UseCase öğelerinizle CameraProvider öğesine 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: VideoCapture
private 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 öğesine videoCapture.output mülkünden erişilebilir. Recorder; File, ParcelFileDescriptor veya MediaStore içine kaydedilen video kayıtlarını başlatabilir. Bu örnekte MediaStore kullanılmıştır.

Recorder sayfasında, raporu hazırlamak için kullanabileceğiniz birkaç yöntem vardır. MediaStore çıkış seçeneklerini ayarlamak için prepareRecording() çağrısı yapın. Uygulamanızın, cihaz mikrofonunu kullanma izni varsa withAudioEnabled() numarasını da arayın. Ardından, kayda başlamak ve bir bağlamda geçirmek için start() yöntemini, video kayıt etkinliklerini işlemek için de Consumer<VideoRecordEvent> etkinlik işleyiciyi çağırın. Başarılı olursa döndürülen Recording özelliği 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 Depomuzda birçok eksiksiz CameraX uygulamamız vardır. Bu örneklerde, bu kılavuzdaki senaryoların tam kapsamlı bir Android uygulamasında nasıl yer aldığı gösterilmektedir.

CameraX'e geçiş konusunda ek destek istiyorsanız veya Android Camera API'leri paketiyle ilgili sorularınız varsa lütfen KameraX Tartışma Grubu'ndan bize ulaşın.