Camera1 zu CameraX migrieren

Wenn in Ihrer App das ursprüngliche Camera verwendet wird Klasse ("Camera1"), die seit der Einführung von Android 5.0 (API-Level 21), empfehlen wir die Aktualisierung auf eine moderne Android-Kamera-API. Android-Angebote CameraX (eine standardisierte, robuste Jetpack-Kamera API) und Camera2 (eine Low-Level-Framework-API). Für die In den allermeisten Fällen empfehlen wir die Migration deiner App zu CameraX. Aus folgenden Gründen:

  • Nutzerfreundlichkeit:CameraX übernimmt die Details auf niedriger Ebene, sodass Sie weniger darauf konzentrieren, ein Kameraerlebnis von Grund auf neu zu entwickeln, Ihre App hervorzuheben.
  • CameraX sorgt für Fragmentierung:CameraX reduziert langfristige Wartungskosten und gerätespezifischer Code für eine bessere User Experience für Nutzende zu machen. Weitere Informationen hierzu finden Sie in der Bessere Gerätekompatibilität mit CameraX Blogpost.
  • Erweiterte Funktionen:CameraX wurde sorgfältig entwickelt, um Funktionen, die sich einfach in Ihre App integrieren lassen. Zum Beispiel können Sie ganz einfach Bokeh-Funktion, Gesichtsretusche, HDR (High Dynamic Range) und Helligkeit bei schlechten Lichtverhältnissen Nachtaufnahmemodus für deine Fotos mit CameraX-Erweiterungen:
  • Aktualisierbarkeit:Android veröffentlicht neue Funktionen und Fehlerkorrekturen für CameraX im Laufe des Jahres. Durch die Migration zu CameraX erhält deine App die aktuelle Android-Version Kameratechnologie bei jeder neuen CameraX-Version, nicht nur auf der jährliche Veröffentlichungen der Android-Version.

In diesem Handbuch werden gängige Szenarien für Kameraanwendungen beschrieben. Jedes umfasst eine Camera1-Implementierung und eine CameraX-Implementierung für vergleichen.

Bei der Migration benötigen Sie manchmal zusätzliche Flexibilität, mit einer vorhandenen Codebasis. Der gesamte CameraX-Code in diesem Handbuch enthält ein CameraController ist ideal, wenn ihr CameraX auf einfache Weise nutzen möchtet. CameraProvider Implementierung – ideal, wenn Sie mehr Flexibilität benötigen. Um Ihnen bei der Entscheidung zu helfen, das richtige für Sie ist, finden Sie hier die jeweiligen Vorteile:

Kamera-Controller

Kameraanbieter

Wenig Einrichtungscode erforderlich Mehr Kontrolle
Wenn CameraX einen größeren Einrichtungsvorgang ausführen kann, Funktionen wie Tippen zum Fokussieren und Zoomen durch Auseinander- und Zusammenziehen funktionieren automatisch Da der App-Entwickler die Einrichtung übernimmt, zum Anpassen der Konfiguration, z. B. zum Aktivieren der Ausgabebildrotation oder das Ausgabebildformat in ImageAnalysis festlegen
Wenn PreviewView für die Kameravorschau erforderlich ist, CameraX für eine nahtlose End-to-End-Integration wie in unserem ML-Kit. Integration, die die Ergebniskoordinaten des ML-Modells (z. B. Gesicht) zuordnen kann. Begrenzungsrahmen) direkt auf die Vorschaukoordinaten Die Verwendung einer benutzerdefinierten Oberfläche für die Kameravorschau ermöglicht mehr Flexibilität bieten, indem Sie z. B. Ihren vorhandenen Oberflächencode verwenden, kann eine Eingabe für andere Teile Ihrer App sein.

Wenn Sie bei der Migration nicht weiterkommen, kontaktieren Sie uns auf der CameraX-Diskussionsgruppe

Vor der Migration

Nutzung von CameraX und Camera1 vergleichen

Der Code kann zwar anders aussehen, aber die zugrunde liegenden Konzepte in Camera1 und CameraX sind sich sehr ähnlich. KameraX bündelt gängige Kamerafunktionen für Anwendungsfälle, Viele Aufgaben, die dem Entwickler in der App „Kamera1“ überlassen waren, automatisch von CameraX verarbeitet. Es gibt vier UseCases in CameraX, die Sie verschiedene Kameraaufgaben verwenden: Preview, ImageCapture, VideoCapture und ImageAnalysis.

Ein Beispiel dafür, wie CameraX für Entwickler Low-Level-Details verarbeitet, ist die ViewPort, das gemeinsam genutzt wird aktive UseCase s. Dadurch wird sichergestellt, dass alle UseCase-Elemente genau die gleichen Pixel sehen. In Kamera1 musst du diese Details selbst verwalten. Aufgrund der Unterschiede in den Seitenverhältnissen auf den verschiedenen Geräten Kamera-Sensoren und -Bildschirme, kann es schwierig sein, dass die Vorschau mit den aufgenommenen Fotos und Videos übereinstimmt.

Ein weiteres Beispiel: CameraX verarbeitet Lifecycle-Callbacks automatisch auf der Lifecycle-Instanz übergeben, wenn Sie sie übergeben. Das bedeutet, dass CameraX die Daten die Verbindung zur Kamera während der gesamten Android-Aktivitätslebenszyklus einschließlich der folgenden Fälle: Schließen der Kamera, wenn Ihre App in den Hintergrund; Kameravorschau entfernen, wenn der Bildschirm keine weiteren Anzeige und die Kameravorschau anhalten, wenn eine andere Aktivität Vorrang vor einem eingehenden Videoanruf haben.

CameraX übernimmt die Drehung und Skalierung, ohne dass zusätzlicher Code erforderlich ist. Ihrerseits. Bei einem Activity mit einer nicht gesperrten Ausrichtung Die Einrichtung von UseCase wird jedes Mal abgeschlossen, wenn das Gerät gedreht wird, während das System zerstört wird und erstellt den Activity neu, wenn sich die Ausrichtung ändert. Daraus ergibt sich die UseCases: Legt die Zieldrehung so fest, dass sie der Ausrichtung des Displays entspricht, indem immer wieder Standardeinstellungen festlegen. Weitere Informationen zu Drehungen in KameraX

Werfen wir einen kurzen Blick auf die Kamera von CameraX, bevor wir ins Detail gehen. UseCase und wie eine Kamera1-App funktionieren würde. (Die CameraX-Konzepte finden Sie blue und Camera1 Konzepte sind in green)

KameraX

CameraController / CameraProvider-Konfiguration
Vorschau Bilderfassung Videoaufzeichnung Bildanalyse
Surface-Vorschau verwalten und auf Kamera einrichten PictureCallback einrichten und „takePicture()“ auf der Kamera aufrufen Kamera- und MediaRecorder-Konfiguration in bestimmter Reihenfolge verwalten Benutzerdefinierter Analysecode, der auf der Oberfläche der Vorabversion basiert
Gerätespezifischer Code
Verwaltung der Geräterotation und -skalierung
Verwaltung von Kamerasitzungen (Kameraauswahl, Verwaltung des Lebenszyklus)

Kamera1

Kompatibilität und Leistung in CameraX

CameraX unterstützt Geräte mit Android 5.0 (API-Level 21) und höher. Dieses machen mehr als 98% aller vorhandenen Android-Geräte aus. CameraX kann automatisch Unterschiede zwischen den Geräten, sodass weniger gerätespezifische Code in Ihrer App. Außerdem testen wir über 150 physische Geräte auf allen Android-Geräten, Versionen seit 5.0 finden Sie in unserem CameraX Test Lab. Ich können Sie sich die vollständige Liste Geräten, die sich derzeit im Test Lab befinden.

CameraX nutzt ein Executor, um für den Kamera-Stack. Sie können Eigenen Executor auf CameraX festlegen ob Ihre Anwendung bestimmte Threading-Anforderungen hat. Ist die Richtlinie nicht konfiguriert, erstellt CameraX und verwendet eine optimierte standardmäßige Executor. Viele der Plattform-APIs auf welche CameraX-Technologie entwickelt wurde, müssen die Interprozesskommunikation (IPC) mit Hardware, die manchmal Hunderte von Millisekunden dauert, um zu reagieren. In diesem Fall KameraX ruft diese APIs nur aus Hintergrundthreads auf. Dadurch wird sichergestellt, Hauptthread nicht blockiert ist und die Benutzeroberfläche flexibel bleibt. Weitere Informationen zu Threads

Wenn der Zielmarkt für Ihre App Low-End-Geräte sind, bietet CameraX eine die Einrichtungszeit mit einem Kamerabegrenzung. Da die Das Herstellen einer Verbindung zu Hardwarekomponenten kann eine nicht unerhebliche Menge an insbesondere auf Low-End-Geräten, kannst du den Satz an Kameras festlegen, Anforderungen. CameraX stellt nur während der Einrichtung eine Verbindung zu diesen Kameras her. Wenn beispielsweise die Anwendung nur rückseitige Kameras verwendet, kann diese Konfiguration mit DEFAULT_BACK_CAMERA und CameraX vermeidet eine Initialisierung der Frontkamera. um die Latenz zu verringern.

Konzepte der Android-Entwicklung

In diesem Leitfaden wird davon ausgegangen, dass Sie mit der Android-Entwicklung vertraut sind. Jenseits der sollten Sie einige Konzepte kennen, direkt in den Code einzusteigen:

  • View Binding generiert eine Bindungsklasse für XML-Layoutdateien, sodass Sie Auf Ihre Ansichten in Aktivitäten verweisen, Dies geschieht in den folgenden Code-Snippets. Es gibt einige Unterschiede zwischen Ansichtsbindung und findViewById() (frühere Methode zum Referenzieren von Ansichten). Im folgenden Code sollten Sie jedoch in der Lage sein, ersetzen Sie die Bindungszeilen der Ansicht durch einen ähnlichen findViewById()-Aufruf.
  • Asynchrone Koroutinen sind ein Gleichzeitigkeitsdesign. In Kotlin 1.3 hinzugefügtes Muster, das zur Verarbeitung von CameraX-Methoden verwendet werden kann, die ListenableFuture zurückgeben. Mit dem Jetpack ist das jetzt noch einfacher Gleichzeitige Bibliothek ab Version 1.1.0. So fügen Sie Ihrer Anwendung eine asynchrone Koroutine hinzu: <ph type="x-smartling-placeholder">
      </ph>
    1. implementation("androidx.concurrent:concurrent-futures-ktx:1.1.0") hinzufügen in Ihre Gradle-Datei ein.
    2. Fügen Sie einen beliebigen CameraX-Code, der ein ListenableFuture zurückgibt, in ein launch blockieren oder Unterbrechungsfunktion.
    3. Hinzufügen eines await() Aufruf des Funktionsaufrufs, der eine ListenableFuture zurückgibt.
    4. Detaillierte Informationen zur Funktionsweise von Koroutinen finden Sie in den Anleitung zum Starten einer gemeinsamen Routine

Häufige Szenarien migrieren

In diesem Abschnitt wird erläutert, wie du häufige Szenarien von Kamera1 zu CameraX migrieren kannst. Jedes Szenario umfasst eine Camera1-Implementierung, eine CameraX-CameraProvider und eine CameraController-Implementierung von CameraX.

Kamera auswählen

Zunächst sollten Sie in Ihrer Kamera-App Kameras auswählen.

Kamera1

In Kamera1 können Sie entweder Camera.open() ohne Parameter um die erste Kamera auf der Rückseite zu öffnen. Alternativ können Sie eine ganzzahlige ID für die Kamera, die Sie öffnen möchten. So könnte das aussehen:

// 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

In CameraX wird die Kameraauswahl von der Klasse CameraSelector übernommen. KameraX erleichtert den Einsatz der Standardkamera. Sie können angeben, die Standard-Frontkamera oder die Standard-Rückkamera. Außerdem Mit dem Objekt CameraControl von CameraX kannst du die Zoomstufe für Ihre App festlegen. Wenn Ihre App auf einem Gerät ausgeführt wird, logischen Kameras und wechselt dann in das richtige Objektiv.

Hier ist der CameraX-Code für die Verwendung der Standard-Rückkamera mit einem CameraController:

// 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

Hier ein Beispiel für die Auswahl der Standard-Frontkamera mit CameraProvider (entweder die Front- oder Rückkamera kann mit CameraController oder CameraProvider):

// 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()
    }
}

Wenn Sie steuern möchten, welche Kamera ausgewählt wird, können Sie das auch in CameraX, wenn Sie CameraProvider über einen Anruf getAvailableCameraInfos(), Damit erhalten Sie ein CameraInfo-Objekt, mit dem Sie bestimmte Kameraeigenschaften wie isFocusMeteringSupported() Anschließend können Sie ihn in ein CameraSelector konvertieren, um es wie oben zu verwenden. mit der Methode CameraInfo.getCameraSelector().

Weitere Informationen zu den einzelnen Kameras erhalten Sie über das Camera2CameraInfo . Anruf getCameraCharacteristic() mit einem Schlüssel für die gewünschten Kameradaten. Prüfen Sie die CameraCharacteristics Klasse, um eine Liste aller Schlüssel zu erhalten, die Sie abfragen können.

Hier ist ein Beispiel mit einer benutzerdefinierten checkFocalLength()-Funktion, die Sie Definieren Sie sich selbst:

// 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()

Vorschau wird angezeigt

Bei den meisten Kamera-Apps muss der Kamerafeed an manchen Punkt. Mit Camera1 musst du die Lebenszyklus-Callbacks richtig verwalten müssen Sie auch die Rotation und Skalierung für die Vorschau festlegen.

Außerdem müssen Sie in Kamera1 festlegen, ob Sie ein TextureView oder SurfaceView als Vorschauoberfläche aus. Beide Optionen haben Vor- und Nachteile. In beiden Fällen müssen Sie für Kamera1 die Drehung und Skalierung richtig zu handhaben. PreviewView von CameraX auf der anderen liegen zugrunde liegende Implementierungen für TextureView und SurfaceView. CameraX entscheidet anhand von Faktoren wie vom Gerätetyp und von der Android-Version, auf der die App ausgeführt wird. Wenn entweder Implementierung kompatibel ist, können Sie Ihre Präferenzen PreviewView.ImplementationMode Die Option COMPATIBLE verwendet ein TextureView für die Vorschau und das Für den PERFORMANCE-Wert wird nach Möglichkeit SurfaceView verwendet.

Kamera1

Wenn Sie eine Vorschau sehen möchten, müssen Sie Ihren eigenen Preview-Kurs mit einem Implementierung der android.view.SurfaceHolder.Callback über die Bilddaten von der Kamerahardware an den . Bevor Sie die Live-Bildvorschau starten können, muss der Preview -Klasse muss an das Camera-Objekt übergeben werden.

// 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

In CameraX müssen Sie als Entwickler viel weniger verwalten. Wenn Sie eine CameraController eingeben, müssen Sie auch PreviewView verwenden. Das bedeutet, dass der Preview UseCase wird impliziert, wodurch die Einrichtung wesentlich weniger aufwendig ist:

// 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

Mit CameraProvider von CameraX brauchst du PreviewView nicht, aber es die Einrichtung der Vorschau über Kamera1 immer noch erheblich vereinfacht. Zu Demonstrationszwecken wird in diesem Beispiel ein PreviewView verwendet. Sie können aber auch ein benutzerdefiniertes SurfaceProvider, um an setSurfaceProvider() zu übergeben, wenn Sie eine komplexere Abfrage haben Anforderungen.

Hier wird der UseCase von Preview nicht wie bei CameraController impliziert, Sie müssen es also einrichten:

// 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()
    }
}

Zum Fokussieren tippen

Wenn die Kameravorschau auf dem Bildschirm angezeigt wird, wenn Nutzende auf die Vorschau tippen.

Kamera1

Um den Tap-to-Fokus in Kamera1 zu implementieren, müssen Sie den optimalen Fokus berechnen Area, um anzugeben, worauf sich Camera konzentrieren soll. Dieses Gerät (Area) ist an setFocusAreas() übergeben. Außerdem müssen Sie auf dem Display einen kompatiblen Fokusmodus einstellen, Camera Der Fokusbereich wird nur angewendet, wenn der aktuelle Fokusmodus FOCUS_MODE_AUTO, FOCUS_MODE_MACRO, FOCUS_MODE_CONTINUOUS_VIDEO oder FOCUS_MODE_CONTINUOUS_PICTURE.

Jedes Area ist ein Rechteck mit einer angegebenen Gewichtung. Die Gewichtung ist ein Wert zwischen 1 und 1.000. Sie wird verwendet, um den Fokus Areas zu priorisieren, wenn mehrere festgelegt sind. Dieses Beispiel verwendet nur ein Area, daher spielt der Gewichtungswert keine Rolle. Koordinaten von Rechtecks von -1.000 bis 1.000. Der obere linke Punkt ist (-1000, -1000). Der untere rechte Punkt ist (1.000, 1.000). Die Richtung ist relativ zum Sensor d. h., was der Sensor sieht. Die Richtung wird vom Drehung oder Spiegelung von Camera.setDisplayOrientation(). Sie müssen also Koordinaten des Touch-Ereignisses in Sensorkoordinaten konvertieren.

// 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 hört zur Verarbeitung auf Touch-Events von PreviewView automatisch durch Tippen fokussieren. Du kannst den Touchpad-Fokus aktivieren oder deaktivieren: setTapToFocusEnabled(), und prüfen Sie den Wert mit dem entsprechenden Getter isTapToFocusEnabled()

Die getTapToFocusState() Methode gibt ein LiveData-Objekt zurück. zum Nachverfolgen von Änderungen des Fokusstatus auf CameraController.

// 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

Bei Verwendung von CameraProvider sind einige Einrichtungsschritte erforderlich, um den Fokus durch Tippen zu fokussieren funktionieren. In diesem Beispiel wird davon ausgegangen, dass Sie PreviewView verwenden. Falls nicht, müssen Sie Passen Sie die Logik an, die auf Ihre benutzerdefinierte Surface angewendet werden soll.

So verwendest du PreviewView:

  1. Gestenerkennung einrichten, um Tippereignisse zu verarbeiten
  2. Erstellen Sie mit dem Tippereignis eine MeteringPoint mit MeteringPointFactory.createPoint().
  3. Erstellen Sie mit der MeteringPoint eine FocusMeteringAction.
  4. Mit dem CameraControl-Objekt auf Ihrem Camera (zurückgegeben von bindToLifecycle()), rufen Sie startFocusAndMetering() auf und übergeben Sie die FocusMeteringAction
  5. Optional: Antworten Sie auf die FocusMeteringResult.
  6. Bewegungserkennung so einstellen, dass sie auf Touch-Ereignisse reagiert in PreviewView.setOnTouchListener()
// 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
}

Zum Zoomen auseinander- und zusammenziehen

Das Vergrößern und Verkleinern einer Vorschau ist eine weitere häufige direkte Bearbeitung Kameravorschau. Mit zunehmender Anzahl von Kameras auf den Geräten wird das Objektiv mit der besten Brennweite automatisch Ergebnis des Zoomens.

Kamera1

Es gibt zwei Möglichkeiten, mit Kamera 1 zu zoomen. Die Methode Camera.startSmoothZoom() wird von der aktuellen Zoomstufe zur entsprechenden Zoomstufe gewechselt. Die Die Camera.Parameters.setZoom()-Methode springt direkt auf die Zoomstufe, die Sie übergeben. . Bevor Sie eines verwenden, rufen Sie isSmoothZoomSupported() auf oder isZoomSupported(), um sicherzustellen, dass die benötigten Zoommethoden verwendet werden sind auf deiner Kamera verfügbar.

Zum Implementieren des Zoomens durch Auseinander- und Zusammenziehen der Finger wird in diesem Beispiel setZoom() verwendet, da die Touch-Geste Listener auf der Vorschauoberfläche löst kontinuierlich Ereignisse aus, wenn das Pinch wird die Zoomstufe jedes Mal sofort aktualisiert. Die Die Klasse ZoomTouchListener ist unten definiert und sollte als Callback festgelegt werden Touch-Listener für die Vorschauoberfläche.

// 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

Ähnlich wie beim Tippen zum Fokussieren erfasst CameraController die Berührung von PreviewView -Ereignisse zum automatischen Zoomen durch Auseinander- und Zusammenziehen der Finger. Sie können die Funktion Zoomen durch Auseinander- und Zusammenziehen setPinchToZoomEnabled(), und prüfen Sie den Wert mit dem entsprechenden Getter isPinchToZoomEnabled()

Die getZoomState() -Methode gibt ein LiveData-Objekt zum Verfolgen von Änderungen an der ZoomState im CameraController

// 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

Damit du mit CameraProvider durch Auseinander- und Zusammenziehen der Finger zoomen kannst, sind einige Einrichtungsschritte erforderlich. Wenn keine PreviewView verwenden, müssen Sie die Logik für Ihre benutzerdefiniert Surface.

So verwendest du PreviewView:

  1. Richten Sie einen Gestenerkennung für Skalierung ein, um Pinch-Ereignisse zu verarbeiten.
  2. Rufen Sie die ZoomState aus dem Camera.CameraInfo-Objekt ab, wobei die Camera -Instanz zurückgegeben, wenn Sie bindToLifecycle()
  3. Wenn ZoomState einen zoomRatio-Wert hat, speichere diesen als aktuellen Zoom Seitenverhältnis. Wenn sich auf ZoomState kein zoomRatio befindet, verwende die Standardeinstellung der Kamera. Zoomstufe (1,0).
  4. Das Produkt des aktuellen Zoomverhältnisses mit dem scaleFactor berechnen, das neue Zoomverhältnis bestimmen und an CameraControl.setZoomRatio() übergeben.
  5. Bewegungserkennung so einstellen, dass sie auf Touch-Ereignisse reagiert in PreviewView.setOnTouchListener()
// 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 machen

In diesem Abschnitt erfahren Sie, wie die Fotoaufnahme ausgelöst wird, wenn der Timer abgelaufen ist oder ein anderes Ereignis Auswahl.

Kamera1

In Camera1 definieren Sie zunächst eine Camera.PictureCallback um die Bilddaten zu verwalten, wenn sie angefordert werden. Hier ist ein einfaches Beispiel PictureCallback für die Verarbeitung von JPEG-Bilddaten:

// 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)
    }
}

Wenn Sie dann ein Bild aufnehmen möchten, rufen Sie die Methode takePicture() auf. auf Ihrer Camera-Instanz. Diese takePicture()-Methode hat drei verschiedene -Parameter für unterschiedliche Datentypen. Der erste Parameter ist für ein ShutterCallback (in diesem Beispiel nicht definiert). Der zweite Parameter ist für eine PictureCallback zur Verarbeitung der (unkomprimierten) Kamera-Rohdaten. Die dritte der in diesem Beispiel verwendet wird, da es sich um einen PictureCallback-Parameter handelt, JPEG-Bilddaten.

// Camera1: call takePicture on Camera instance, passing our PictureCallback.

camera?.takePicture(null, null, picture)

CameraX: CameraController

Mit CameraController von CameraX wird die Einfachheit von Kamera1 für Bilder beibehalten. indem Sie eine eigene takePicture()-Methode implementieren. Definieren Sie hier um einen MediaStore-Eintrag zu konfigurieren und ein Foto aufzunehmen, das dort gespeichert wird.

// 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

Das Aufnehmen von Fotos mit CameraProvider funktioniert fast genauso wie mit CameraController, aber Sie müssen zuerst ein ImageCapture erstellen und binden. UseCase, um ein Objekt zum Aufrufen von takePicture() zu haben:

// 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)

Dann können Sie jederzeit, wenn Sie ein Foto machen möchten, ImageCapture.takePicture() CameraController-Code in diesem Abschnitt ansehen finden Sie ein vollständiges Beispiel für die takePhoto()-Funktion.

// 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 aufzeichnen

Das Aufzeichnen eines Videos ist wesentlich komplizierter als die vorgestellten Szenarien. . Jeder Teil des Prozesses muss ordnungsgemäß eingerichtet werden, in eine bestimmte Reihenfolge bringen. Außerdem müssen Sie möglicherweise überprüfen, Geräteinkonsistenzen zu synchronisieren oder zu beheben.

Wie ihr seht, übernimmt KameraX wieder einen Großteil dieser Komplexität für euch.

Kamera1

Für Videoaufnahmen mit Kamera1 ist eine sorgfältige Verwaltung von Camera und MediaRecorder und die Methoden müssen in einer bestimmten Reihenfolge aufgerufen werden. Sie müssen diese Reihenfolge für damit Ihre Anwendung ordnungsgemäß funktioniert:

  1. Öffne die Kamera.
  2. Bereiten Sie eine Vorschau vor und starten Sie sie. Wenn in Ihrer App das aufgezeichnete Video angezeigt wird, was normalerweise der Fall ist).
  3. Entsperre die Kamera zur Verwendung durch MediaRecorder, indem du Camera.unlock() aufrufst.
  4. Konfigurieren Sie die Aufzeichnung, indem Sie in MediaRecorder die folgenden Methoden aufrufen: <ph type="x-smartling-placeholder">
      </ph>
    1. Verbinden Sie Ihre Camera-Instanz mit setCamera(camera).
    2. setAudioSource(MediaRecorder.AudioSource.CAMCORDER) anrufen.
    3. setVideoSource(MediaRecorder.VideoSource.CAMERA) anrufen.
    4. setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_1080P)) anrufen um die Qualität festzulegen. Weitere Informationen finden Sie unter CamcorderProfile für alle die Qualitätsoptionen.
    5. setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()) anrufen.
    6. Wenn Ihre App eine Vorschau des Videos hat, rufen Sie setPreviewDisplay(preview?.holder?.surface)
    7. setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) anrufen.
    8. setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT) anrufen.
    9. setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT) anrufen.
    10. Rufe prepare() auf, um die Konfiguration von MediaRecorder abzuschließen.
  5. Wenn du die Aufzeichnung starten möchtest, ruf MediaRecorder.start() an.
  6. Rufen Sie diese Methoden auf, um die Aufzeichnung zu beenden. Gehen Sie dabei genau wie folgt vor: <ph type="x-smartling-placeholder">
      </ph>
    1. MediaRecorder.stop() anrufen.
    2. Entfernen Sie optional die aktuelle MediaRecorder-Konfiguration, indem Sie folgenden Befehl aufrufen: MediaRecorder.reset().
    3. MediaRecorder.release() anrufen.
    4. Sperre die Kamera, damit sie in zukünftigen MediaRecorder-Sitzungen wie folgt verwendet werden kann: Camera.lock() wird angerufen.
  7. Rufen Sie Camera.stopPreview() auf, um die Vorschau zu beenden.
  8. Um den Camera freizugeben, damit andere Prozesse ihn verwenden können, rufen Sie Camera.release().

Hier sind alle diese Schritte kombiniert:

// 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

Mit CameraController von KameraX kannst du ImageCapture, VideoCapture und ImageAnalysis UseCases unabhängig voneinander, solange die Liste der Anwendungsfälle gleichzeitig verwendet werden kann. Die UseCases für ImageCapture und ImageAnalysis sind standardmäßig aktiviert, was Deshalb musste du nicht setEnabledUseCases() anrufen, um ein Foto aufzunehmen.

Wenn du eine CameraController für die Videoaufnahme verwenden möchtest, musst du zuerst setEnabledUseCases(), um VideoCapture UseCase zuzulassen.

// CameraX: Enable VideoCapture UseCase on CameraController.

cameraController.setEnabledUseCases(VIDEO_CAPTURE);

Wenn Sie die Videoaufnahme starten möchten, können Sie den CameraController.startRecording() . Diese Funktion kann das aufgezeichnete Video in einem File speichern, wie Sie im Beispiel unten. Außerdem müssen Sie ein Executor und einen Kurs bestehen. die das OnVideoSavedCallback Erfolgs- und Fehler-Callbacks verarbeitet werden. Wenn die Aufzeichnung beendet werden soll, rufen Sie CameraController.stopRecording()

Hinweis:Wenn Sie CameraX 1.3.0-alpha02 oder höher verwenden, sind zusätzliche Parameter AudioConfig über das du die Audioaufnahme für dein Video aktivieren oder deaktivieren kannst. Zum Aktivieren für Audioaufnahmen benötigen, müssen Sie die Mikrofonberechtigungen haben. Außerdem wird die Methode stopRecording() in 1.3.0-alpha02 entfernt und startRecording() gibt ein Recording-Objekt zurück, das zum Pausieren verwendet werden kann. Fortsetzen und Beenden der Videoaufzeichnung.

// 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

Wenn du ein CameraProvider verwendest, musst du eine VideoCapture erstellen UseCase und übergeben Sie ein Recorder-Objekt. Auf der Recorder.Builder können Sie die Videoqualität und optional FallbackStrategy, wenn ein Gerät nicht die gewünschten Qualitätsanforderungen erfüllt. Dann Binden Sie die Instanz VideoCapture mit der anderen an CameraProvider. UseCase Sek.

// 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)

Jetzt kann über videoCapture.output auf Recorder zugegriffen werden. Property. Recorder kann Videoaufnahmen starten, die in einem File gespeichert sind, ParcelFileDescriptor oder MediaStore. In diesem Beispiel wird MediaStore verwendet.

Unter Recorder gibt es mehrere Methoden, um sie vorzubereiten. Anruf prepareRecording() zum Festlegen der MediaStore-Ausgabeoptionen. Wenn Ihre App Berechtigung, das Mikrofon des Geräts zu verwenden, musst du auch withAudioEnabled() anrufen. Rufen Sie dann start() auf, um die Aufzeichnung zu starten. Dabei werden ein Kontext und ein Consumer<VideoRecordEvent>-Event-Listener zur Verarbeitung von Videoaufnahmeereignissen. Wenn erfolgreich war, kann die zurückgegebene Recording verwendet werden, um den Aufzeichnung.

// 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
                   }
               }
           }
       }
}

Weitere Informationen

Wir haben mehrere vollständige CameraX-Apps in unserer GitHub-Repository für Kamerabeispiele. Diese Beispiele zeigen, wie sich die Szenarien in diesem Leitfaden auf ein umfassendes Android-App

Wenn Sie zusätzliche Unterstützung bei der Migration zu CameraX benötigen oder Fragen haben Informationen zur Suite der Android Camera APIs erhalten Sie auf der Diskussion zu CameraX Gruppieren.