Por lo general, un sistema de captura graba transmisiones de video y audio, las comprime, combina varias transmisiones y, luego, escribe la transmisión resultante en el disco.
En CameraX, la solución para la captura de video es el caso de uso de VideoCapture
:
Como se muestra en la figura 2, la captura de video de CameraX incluye algunos componentes arquitectónicos de alto nivel:
SurfaceProvider
para la fuente de videoAudioSource
para la fuente de audio- Dos codificadores para codificar y comprimir video/audio
- Un combinador de medios para combinar las dos transmisiones
- Un ahorro de archivos para escribir el resultado
La API de VideoCapture simplifica el motor de captura complejo y proporciona a las aplicaciones una API mucho más simple y directa.
Descripción general de la API de VideoCapture
VideoCapture
es un caso de uso de CameraX que funciona bien por sí solo o cuando se combina con otros casos de uso. Las combinaciones específicas admitidas dependen de las capacidades del hardware de la cámara, aunque Preview
y VideoCapture
son una combinación válida de casos de uso en todos los dispositivos.
La API de VideoCapture consta de los siguientes objetos que se comunican con aplicaciones:
VideoCapture
es la clase de caso de uso de nivel superior.VideoCapture
se vincula a unLifecycleOwner
con unCameraSelector
y otros UseCases de CameraX. Para obtener más información sobre estos conceptos y usos, consulta Arquitectura de CameraX.- Un
Recorder
es una implementación de VideoOutput que tiene un acoplamiento alto conVideoCapture
.Recorder
se usa para realizar la captura de video y audio. Una aplicación crea grabaciones a partir de unRecorder
. - Un
PendingRecording
configura una grabación y proporciona opciones como habilitar el audio y configurar un objeto de escucha de eventos. Debes usarRecorder
para crear unaPendingRecording
.PendingRecording
no graba nada. Recording
realiza la grabación. Debes usarPendingRecording
para crear unRecording
.
En la Figura 3, se muestran las relaciones entre estos objetos:
Leyenda:
- Crea una
Recorder
conQualitySelector
. - Configura
Recorder
con una de lasOutputOptions
. - Si es necesario, habilita el audio con
withAudioEnabled()
. - Llama a
start()
con un objeto de escuchaVideoRecordEvent
para comenzar a grabar. - Usa
pause()
/resume()
/stop()
enRecording
para controlar la grabación. - Responde a
VideoRecordEvents
dentro del objeto de escucha de eventos.
La lista detallada de las API se encuentra en el archivo current.txt dentro del código fuente.
Cómo usar la API de VideoCapture
Para integrar el caso de uso de CameraX VideoCapture
a tu app, haz lo siguiente:
- Vincula
VideoCapture
. - Prepara y configura la grabación.
- Inicia y controla la grabación del tiempo de ejecución.
En las siguientes secciones, se describe lo que puedes hacer en cada paso para obtener una sesión de grabación completa.
Cómo vincular VideoCapture
Para vincular el caso de uso de VideoCapure
, haz lo siguiente:
- Crea un objeto
Recorder
. - Crea el objeto
VideoCapture
. - Haz la vinculación a
Lifecycle
.
La API de CameraX VideoCapture sigue el patrón de diseño del compilador. Las aplicaciones usan Recorder.Builder
para crear un Recorder
. También puedes configurar la resolución del video para Recorder
mediante un objeto QualitySelector
.
CameraX Recorder
admite las siguientes Qualities
predefinidas para resoluciones de video:
Quality.UHD
para videos con resolución 4K Ultra HD (2160p)Quality.FHD
para videos con resolución Full HD (1080p)Quality.HD
para videos con resolución HD (720p)Quality.SD
para videos con resolución SD (480p)
Ten en cuenta que CameraX también podrá elegir otras resoluciones cuando lo autorice la app.
La resolución exacta del video de cada selección depende de la cámara y las capacidades del codificador. Para obtener más información, consulta la documentación de CamcorderProfile
.
Para configurar la resolución, las aplicaciones pueden crear un QualitySelector
.
QualitySelector
se puede crear con uno de los siguientes métodos:
Brinda algunas resoluciones preferidas mediante
fromOrderedList()
y agrega una estrategia de resguardo para usar en caso de que no se admita ninguna de las resoluciones preferidas.CameraX puede decidir la mejor coincidencia de resguardo según la capacidad de la cámara seleccionada. Para obtener más detalles, consulta el objeto
FallbackStrategy specification
deQualitySelector
. Por ejemplo, el siguiente código solicita la resolución más alta compatible para la grabación y, si no se admite ninguna de las resoluciones de solicitud, autoriza a CameraX a elegir la que esté más cerca de la resolución Quality.SD:val qualitySelector = QualitySelector.fromOrderedList( listOf(Quality.UHD, Quality.FHD, Quality.HD, Quality.SD), FallbackStrategy.lowerQualityOrHigherThan(Quality.SD))
Consulta las capacidades de la cámara primero y elige una de las resoluciones compatibles mediante
QualitySelector::from()
:val cameraInfo = cameraProvider.availableCameraInfos.filter { Camera2CameraInfo .from(it) .getCameraCharacteristic(CameraCharacteristics.LENS\_FACING) == CameraMetadata.LENS_FACING_BACK } val supportedQualities = QualitySelector.getSupportedQualities(cameraInfo[0]) val filteredQualities = arrayListOf (Quality.UHD, Quality.FHD, Quality.HD, Quality.SD) .filter { supportedQualities.contains(it) } // Use a simple ListView with the id of simple_quality_list_view viewBinding.simpleQualityListView.apply { adapter = ArrayAdapter(context, android.R.layout.simple_list_item_1, filteredQualities.map { it.qualityToString() }) // Set up the user interaction to manually show or hide the system UI. setOnItemClickListener { _, _, position, _ -> // Inside View.OnClickListener, // convert Quality.* constant to QualitySelector val qualitySelector = QualitySelector.from(filteredQualities[position]) // Create a new Recorder/VideoCapture for the new quality // and bind to lifecycle val recorder = Recorder.Builder() .setQualitySelector(qualitySelector).build() // ... } } // A helper function to translate Quality to a string fun Quality.qualityToString() : String { return when (this) { Quality.UHD -> "UHD" Quality.FHD -> "FHD" Quality.HD -> "HD" Quality.SD -> "SD" else -> throw IllegalArgumentException() } }
Ten en cuenta que se garantiza que la capacidad que se muestra de
QualitySelector.getSupportedQualities()
funcione para el caso de uso deVideoCapture
o la combinación deVideoCapture
yPreview
. Cuando se realiza la vinculación con el caso de uso deImageCapture
oImageAnalysis
, es posible que CameraX falle al hacerlo si la combinación requerida no es compatible con la cámara solicitada.
Una vez que tengas un QualitySelector
, la aplicación podrá crear un objeto VideoCapture
y realizar la vinculación. Ten en cuenta que esta vinculación es la misma que con otros casos de uso:
val recorder = Recorder.Builder()
.setExecutor(cameraExecutor).setQualitySelector(qualitySelector)
.build()
val videoCapture = VideoCapture.withOutput(recorder)
try {
// Bind use cases to camera
cameraProvider.bindToLifecycle(
this, CameraSelector.DEFAULT_BACK_CAMERA, preview, videoCapture)
} catch(exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
Ten en cuenta que bindToLifecycle()
muestra un objeto Camera
. Consulta esta guía a fin de obtener más información para controlar la salida de la cámara, como el zoom y la exposición.
Recorder
selecciona el formato más adecuado para el sistema. El códec de video más común es H.264 AVC con formato de contenedor MPEG-4.
Cómo configurar y crear una grabación
Desde un Recorder
, la aplicación puede crear objetos de grabación para realizar la captura de audio y de video. Las aplicaciones crean grabaciones de la siguiente manera:
- Configuran
OutputOptions
conprepareRecording()
. - Habilita la grabación de audio (opcional).
- Usa
start()
para registrar un objeto de escuchaVideoRecordEvent
y comienza a capturar videos.
El objeto Recorder
muestra un objeto Recording
cuando llamas a la función start()
.
Tu aplicación puede usar este objeto Recording
para finalizar la captura o realizar otras acciones, como pausar o reanudar.
Recorder
admite un objeto Recording
por vez. Puedes iniciar una grabación nueva una vez que hayas llamado a Recording.stop()
o Recording.close()
en el objeto Recording
anterior.
Analicemos estos pasos con más detalle. Primero, la aplicación configura OutputOptions
para una grabadora con Recorder.prepareRecording()
.
Recorder
admite los siguientes tipos de OutputOptions
:
FileDescriptorOutputOptions
para hacer la captura en unFileDescriptor
FileOutputOptions
para hacer la captura en unFile
MediaStoreOutputOptions
para hacer la captura en unMediaStore
Todos los tipos OutputOptions
te permiten establecer un tamaño de archivo máximo con setFileSizeLimit()
. Otras opciones son específicas del tipo de salida individual, como ParcelFileDescriptor
para FileDescriptorOutputOptions
.
prepareRecording()
muestra un objeto PendingRecording
, que es un objeto intermedio que se usa para crear la Recording
correspondiente. PendingRecording
es una clase transitoria que debería ser invisible en la mayoría de los casos y la app rara vez almacena en caché.
Las aplicaciones pueden configurar las siguientes opciones de la grabación:
- Habilitar el audio con
withAudioEnabled()
- Registrar un objeto de escucha para recibir eventos de grabación de video con
start(Executor, Consumer<VideoRecordEvent>)
- Permitir una grabación continua mientras que el elemento VideoCapture adjunto se envía a otra cámara, con
PendingRecording.asPersistentRecording()
.
Para comenzar a grabar, llama a PendingRecording.start()
. CameraX convierte el PendingRecording
en un Recording
, pone en cola la solicitud de grabación y muestra el objeto Recording
recién creado a la aplicación.
Una vez que comienza la grabación en el dispositivo correspondiente, CameraX envía un evento VideoRecordEvent.EVENT_TYPE_START
.
En el siguiente ejemplo, se muestra cómo grabar video y audio en un archivo MediaStore
:
// Create MediaStoreOutputOptions for our recorder
val name = "CameraX-recording-" +
SimpleDateFormat(FILENAME_FORMAT, Locale.US)
.format(System.currentTimeMillis()) + ".mp4"
val contentValues = ContentValues().apply {
put(MediaStore.Video.Media.DISPLAY_NAME, name)
}
val mediaStoreOutput = MediaStoreOutputOptions.Builder(this.contentResolver,
MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
.setContentValues(contentValues)
.build()
// 2. Configure Recorder and Start recording to the mediaStoreOutput.
val recording = videoCapture.output
.prepareRecording(context, mediaStoreOutput)
.withAudioEnabled()
.start(ContextCompat.getMainExecutor(this), captureListener)
La vista previa de la cámara se duplica en la cámara frontal de forma predeterminada, pero eso no sucede con los videos grabados por VideoCapture. Con CameraX 1.3, ahora es posible duplicar las grabaciones de video de modo que la vista previa de la cámara frontal coincida con el video grabado.
Existen tres opciones de MirrorMode: MIRROR_MODE_OFF, MIRROR_MODE_ON y MIRROR_MODE_ON_FRONT_ONLY. Para alinear la vista previa de la cámara, Google recomienda que uses MIRROR_MODE_ON_FRONT_ONLY, lo que significa que no se habilitará la duplicación para la cámara posterior, pero sí para la frontal. Para obtener más información sobre MirrorMode, consulta MirrorMode constants
.
Este fragmento de código te muestra cómo llamar a VideoCapture.Builder.setMirrorMode()
usando MIRROR_MODE_ON_FRONT_ONLY
. Si deseas obtener información detallada, consulta setMirrorMode()
.
Kotlin
val recorder = Recorder.Builder().build() val videoCapture = VideoCapture.Builder(recorder) .setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY) .build() useCases.add(videoCapture);
Java
Recorder.Builder builder = new Recorder.Builder(); if (mVideoQuality != QUALITY_AUTO) { builder.setQualitySelector( QualitySelector.from(mVideoQuality)); } VideoCapture<Recorder> videoCapture = new VideoCapture.Builder<>(builder.build()) .setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY) .build(); useCases.add(videoCapture);
Cómo controlar una grabación activa
Puedes pausar, reanudar y detener una Recording
en curso mediante los siguientes métodos:
pause
para pausar la grabación activa actualresume()
para reanudar una grabación activa en pausastop()
para finalizar la grabación y limpiar los objetos de grabación asociadosmute()
para silenciar o activar el sonido de la grabación actual
Ten en cuenta que puedes llamar a stop()
para finalizar una Recording
, independientemente de si la grabación está en estado de pausa o en estado activo.
Si registraste un EventListener
con PendingRecording.start()
, Recording
se comunica mediante VideoRecordEvent
.
VideoRecordEvent.EVENT_TYPE_STATUS
se usa para grabar estadísticas, como el tamaño actual del archivo y el intervalo de tiempo grabado.VideoRecordEvent.EVENT_TYPE_FINALIZE
se usa para el resultado de la grabación y, además, incluye información como el URI del archivo final junto con cualquier error relacionado.
Una vez que la app reciba un EVENT_TYPE_FINALIZE
que indique una sesión de grabación exitosa, podrás acceder al video capturado desde la ubicación que se haya especificado en OutputOptions
.
Recursos adicionales
Para obtener más información sobre CameraX, consulta los siguientes recursos adicionales:
- Codelab para empezar a usar CameraX
- App de ejemplo de CameraX oficial
- Lista más reciente de la API de captura de video de CameraX
- Notas de la versión de CameraX
- Código fuente de CameraX