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:

VideoCaptureComo se muestra en la figura 2, la captura de video de CameraX incluye algunos componentes arquitectónicos de alto nivel:
SurfaceProviderpara la fuente de videoAudioSourcepara 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:
VideoCapturees la clase de caso de uso de nivel superior.VideoCapturese vincula a unLifecycleOwnercon unCameraSelectory otros UseCases de CameraX. Para obtener más información sobre estos conceptos y usos, consulta Arquitectura de CameraX.- Un
Recorderes una implementación de VideoOutput que tiene un acoplamiento alto conVideoCapture.Recorderse usa para realizar la captura de video y audio. Una aplicación crea grabaciones a partir de unRecorder. - Un
PendingRecordingconfigura una grabación y proporciona opciones como habilitar el audio y configurar un objeto de escucha de eventos. Debes usarRecorderpara crear unaPendingRecording.PendingRecordingno graba nada. Recordingrealiza la grabación. Debes usarPendingRecordingpara crear unRecording.
En la Figura 3, se muestran las relaciones entre estos objetos:

Leyenda:
- Crea una
RecorderconQualitySelector. - Configura
Recordercon una de lasOutputOptions. - Si es necesario, habilita el audio con
withAudioEnabled(). - Llama a
start()con un objeto de escuchaVideoRecordEventpara comenzar a grabar. - Usa
pause()/resume()/stop()enRecordingpara controlar la grabación. - Responde a
VideoRecordEventsdentro 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 VideoCapture, 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.UHDpara videos con resolución 4K Ultra HD (2160p)Quality.QHDpara videos con resolución Quad HD (1440p), que se introdujo en CameraX1.7.0-alpha02Quality.FHDpara videos con resolución Full HD (1080p)Quality.HDpara videos con resolución HD (720p)Quality.SDpara 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 specificationdeQualitySelector. 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.QHD, 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.QHD, 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.QHD -> "QHD" 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 deVideoCaptureo la combinación deVideoCaptureyPreview. Cuando se realiza la vinculación con el caso de uso deImageCaptureoImageAnalysis, 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
OutputOptionsconprepareRecording(). - Habilita la grabación de audio (opcional).
- Usa
start()para registrar un objeto de escuchaVideoRecordEventy 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:
FileDescriptorOutputOptionspara hacer la captura en unFileDescriptorFileOutputOptionspara hacer la captura en unFileMediaStoreOutputOptionspara 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);
Rango dinámico (video HDR)
CameraX admite la grabación de video de alto rango dinámico (HDR) de 10 bits, como HLG10, HDR10, HDR10+ o Dolby Vision, con DynamicRange.
Para usar la grabación de video en HDR, completa los siguientes pasos:
- Consulta los rangos dinámicos compatibles de la cámara con
VideoCapabilities.getSupportedDynamicRanges(). - Opcional: Si configuras un
QualitySelectorpersonalizado, verifica qué calidades se admiten para ese rango dinámico congetSupportedQualities(DynamicRange). - Configura
VideoCapture.BuilderconsetDynamicRange().
En el siguiente ejemplo, se habilita la grabación en HDR de 10 bits con HLG cuando la cámara lo admite:
Kotlin
val videoCapabilities = Recorder.getVideoCapabilities(cameraInfo) val supportedHdrRanges = videoCapabilities.supportedDynamicRanges .filter { it != DynamicRange.SDR } val recorder = Recorder.Builder().build() val videoCapture = VideoCapture.Builder(recorder) .apply { if (supportedHdrRanges.contains(DynamicRange.HLG_10_BIT)) { setDynamicRange(DynamicRange.HLG_10_BIT) } } .build()
Java
VideoCapabilities videoCapabilities = Recorder.getVideoCapabilities(cameraInfo); Set<DynamicRange> supportedRanges = videoCapabilities.getSupportedDynamicRanges(); Recorder recorder = new Recorder.Builder().build(); VideoCapture.Builder<Recorder> builder = new VideoCapture.Builder<>(recorder); if (supportedRanges.contains(DynamicRange.HLG_10_BIT)) { builder.setDynamicRange(DynamicRange.HLG_10_BIT); } VideoCapture<Recorder> videoCapture = builder.build();
Estabilización de video
CameraX admite la estabilización de video para reducir la vibración y la inestabilidad de la cámara durante la grabación de video.
Para habilitar la estabilización de video, sigue estos pasos:
- Verifica si el dispositivo de la cámara admite la estabilización con
VideoCapabilities.isStabilizationSupported(). - Habilítalo en
VideoCapture.BuilderconsetVideoStabilizationEnabled(true).
En el siguiente ejemplo, se habilita la estabilización cuando la cámara la admite:
Kotlin
val videoCapabilities = Recorder.getVideoCapabilities(cameraInfo) val videoCapture = VideoCapture.Builder(recorder) .setVideoStabilizationEnabled(videoCapabilities.isStabilizationSupported) .build()
Java
VideoCapabilities videoCapabilities = Recorder.getVideoCapabilities(cameraInfo); VideoCapture<Recorder> videoCapture = new VideoCapture.Builder<>(recorder) .setVideoStabilizationEnabled(videoCapabilities.isStabilizationSupported()) .build();
Cómo controlar una grabación activa
Puedes pausar, reanudar y detener una Recording en curso mediante los siguientes métodos:
pausepara 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_STATUSse usa para grabar estadísticas, como el tamaño actual del archivo y el intervalo de tiempo grabado.VideoRecordEvent.EVENT_TYPE_FINALIZEse 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.
Amplitud y estado del audio en tiempo real
Durante la grabación, los eventos VideoRecordEvent.Status proporcionan estadísticas de audio en tiempo real a través de AudioStats:
AudioStats.getAudioAmplitude()devuelve la amplitud máxima de audio actual, que varía de0.0a1.0. Puedes usar este valor para controlar los medidores de la IU o los visualizadores de formas de onda de audio.AudioStats.getAudioState()indica el estado actual de la transmisión de audio, comoAUDIO_STATE_ACTIVE,AUDIO_STATE_MUTEDoAUDIO_STATE_SOURCE_SILENCED.
En el siguiente ejemplo, se lee la amplitud de audio de un evento de estado:
Kotlin
when (event) { is VideoRecordEvent.Status -> { val audioStats = event.recordingStats.audioStats if (audioStats.hasAudio()) { val amplitude = audioStats.audioAmplitude updateAudioWaveform(amplitude) } } // ... }
Java
if (event instanceof VideoRecordEvent.Status) { VideoRecordEvent.Status status = (VideoRecordEvent.Status) event; AudioStats audioStats = status.getRecordingStats().getAudioStats(); if (audioStats.hasAudio()) { double amplitude = audioStats.getAudioAmplitude(); updateAudioWaveform(amplitude); } }
Videos de alta velocidad y en cámara lenta
CameraX admite la grabación de video de alta velocidad, como 120, 240 o 480 fps, lo que permite dos casos de uso distintos:
- Video con alta velocidad de fotogramas (cámara lenta controlada por el reproductor): CameraX captura y guarda fotogramas con una alta velocidad de fotogramas (por ejemplo, 120 fps). El reproductor de video controla la velocidad de reproducción, lo que permite a los usuarios alternar entre la velocidad normal y la cámara lenta, o realizar un análisis fotograma por fotograma.
- Video en cámara lenta listo para reproducir: CameraX procesa la transmisión de alta velocidad y genera un archivo de video estándar de 30 fps con marcas de tiempo de fotogramas ajustadas. El efecto de cámara lenta está "incorporado", por lo que el video se reproduce en cámara lenta automáticamente en cualquier reproductor de video estándar sin compatibilidad especial con el reproductor de video. Por ejemplo, una captura de 120 fps reproducida a 30 fps produce un video en cámara lenta de 1/4x.
Verifica las capacidades de alta velocidad
La captura de video de alta velocidad requiere compatibilidad de hardware y las definiciones de CamcorderProfile correspondientes de los fabricantes de dispositivos, como CamcorderProfile.QUALITY_HIGH_SPEED_1080P o QUALITY_HIGH_SPEED_720P.
Antes de configurar la grabación de alta velocidad, consulta las capacidades de alta velocidad con Recorder.getHighSpeedVideoCapabilities(CameraInfo):
Kotlin
val highSpeedCapabilities = Recorder.getHighSpeedVideoCapabilities(cameraInfo) if (highSpeedCapabilities == null) { // Camera does not support high-speed video capture return } val supportedQualities = highSpeedCapabilities.getSupportedQualities(DynamicRange.SDR)
Java
VideoCapabilities highSpeedCapabilities = Recorder.getHighSpeedVideoCapabilities(cameraInfo); if (highSpeedCapabilities == null) { // Camera does not support high-speed video capture return; } List<Quality> supportedQualities = highSpeedCapabilities.getSupportedQualities(DynamicRange.SDR);
Configura HighSpeedVideoSessionConfig
Para usar la captura de alta velocidad, combina tus casos de uso de VideoCapture y Preview en un HighSpeedVideoSessionConfig. Para configurar
la sesión, completa los siguientes pasos:
- Crea un caso de uso de
VideoCapturey un caso de uso dePreview. - Inicializa
HighSpeedVideoSessionConfig.Buildercon el caso de usoVideoCapturey adjunta elPreview. - Consulta los rangos de velocidad de fotogramas admitidos con
CameraInfo.getSupportedFrameRateRanges(SessionConfig)y establece el rango seleccionado. - Elige entre un video con una velocidad de fotogramas alta o un video en cámara lenta listo para reproducir:
- Para crear un video con una velocidad de fotogramas alta, deja
setSlowMotionEnabled(false)(predeterminado). - Para crear un video en cámara lenta listo para reproducir, llama a
setSlowMotionEnabled(true).
- Para crear un video con una velocidad de fotogramas alta, deja
- Vincula la configuración de la sesión con
cameraProvider.bindToLifecycle().
En el siguiente ejemplo, se configura y vincula una sesión de cámara lenta lista para reproducirse:
Kotlin
val preview = Preview.Builder().build() val recorder = Recorder.Builder() .setQualitySelector(QualitySelector.from(supportedQualities.first())) .build() val videoCapture = VideoCapture.withOutput(recorder) // Build HighSpeedVideoSessionConfig val sessionConfigBuilder = HighSpeedVideoSessionConfig.Builder(videoCapture) .setPreview(preview) // Enable automatic slow-motion playback at 30 fps .setSlowMotionEnabled(true) // Query and set a supported high-speed frame rate range (such as 120 or 240 fps). // The set is non-empty because getHighSpeedVideoCapabilities() returned non-null earlier. val supportedFpsRanges = cameraInfo.getSupportedFrameRateRanges(sessionConfigBuilder.build()) sessionConfigBuilder.setFrameRateRange(supportedFpsRanges.first()) val sessionConfig = sessionConfigBuilder.build() // Bind session config to lifecycle cameraProvider.bindToLifecycle( lifecycleOwner, CameraSelector.DEFAULT_BACK_CAMERA, sessionConfig )
Java
Preview preview = new Preview.Builder().build(); Recorder recorder = new Recorder.Builder() .setQualitySelector(QualitySelector.from(supportedQualities.get(0))) .build(); VideoCapture<Recorder> videoCapture = VideoCapture.withOutput(recorder); HighSpeedVideoSessionConfig.Builder sessionConfigBuilder = new HighSpeedVideoSessionConfig.Builder(videoCapture) .setPreview(preview) .setSlowMotionEnabled(true); // The set is non-empty because getHighSpeedVideoCapabilities() returned non-null earlier. Set<Range<Integer>> supportedFpsRanges = cameraInfo.getSupportedFrameRateRanges(sessionConfigBuilder.build()); sessionConfigBuilder.setFrameRateRange(supportedFpsRanges.iterator().next()); HighSpeedVideoSessionConfig sessionConfig = sessionConfigBuilder.build(); cameraProvider.bindToLifecycle( lifecycleOwner, CameraSelector.DEFAULT_BACK_CAMERA, sessionConfig );
También puedes usar el DSL de Kotlin, que se introdujo en CameraX 1.7.0-alpha02, para configurar sesiones de alta velocidad de forma concisa:
val sessionConfig = highSpeedVideoSessionConfig(videoCapture) {
this.preview = preview
isSlowMotionEnabled = true
frameRateRange = supportedFpsRanges.first()
}
cameraProvider.bindToLifecycle(
lifecycleOwner,
CameraSelector.DEFAULT_BACK_CAMERA,
sessionConfig
)
Cómo grabar videos en alta velocidad o en cámara lenta
Después de vincular el HighSpeedVideoSessionConfig, comienza a grabar con VideoCapture de la misma manera que con las grabaciones de video estándar:
Kotlin
val recording = videoCapture.output .prepareRecording(context, mediaStoreOutput) .withAudioEnabled() .start(ContextCompat.getMainExecutor(context)) { event -> when (event) { is VideoRecordEvent.Start -> Log.d(TAG, "Recording started") is VideoRecordEvent.Finalize -> { if (!event.hasError()) { Log.d(TAG, "Video saved: ${event.outputResults.outputUri}") } } } }
Java
Recording recording = videoCapture.getOutput() .prepareRecording(context, mediaStoreOutput) .withAudioEnabled() .start(ContextCompat.getMainExecutor(context), event -> { if (event instanceof VideoRecordEvent.Start) { Log.d(TAG, "Recording started"); } else if (event instanceof VideoRecordEvent.Finalize) { VideoRecordEvent.Finalize finalize = (VideoRecordEvent.Finalize) event; if (!finalize.hasError()) { Log.d(TAG, "Video saved: " + finalize.getOutputResults().getOutputUri()); } } });
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
- Captura de alta velocidad y video en cámara lenta con CameraX 1.5