Um sistema de captura geralmente grava streams de vídeo e áudio, faz a compactação e a multiplexação deles e grava o stream resultante no disco.
No CameraX, a solução para captura de vídeo é o
caso de uso de
VideoCapture:
VideoCapture.Como mostrado na figura 2, a captura de vídeo do CameraX inclui alguns componentes de arquitetura de alto nível:
SurfaceProviderpara a origem do vídeo.AudioSourcepara a fonte de áudio.- Dois codificadores para codificar e compactar vídeo/áudio.
- Um multiplexador de mídia para combinar os dois streams.
- Um protetor de arquivo para gravar o resultado.
A API VideoCapture abstrai o mecanismo de captura complexo e fornece aplicativos com uma API muito mais simples e direta.
Visão geral da API VideoCapture
A VideoCapture é um caso de uso do CameraX que funciona bem sozinho ou quando
combinado com outros casos de uso. O suporte a combinações específicas depende dos
recursos de hardware da câmera, mas Preview e VideoCapture são uma
combinação de casos de uso válida em todos os dispositivos.
A API VideoCapture consiste nos objetos abaixo que se comunicam com aplicativos:
- A
VideoCaptureé a classe do caso de uso de nível mais alto. Ela se vincula a uma interfaceLifecycleOwnercom uma classeCameraSelectore outros UseCases da CameraX. Para ver mais informações sobre esses conceitos e usos, consulte a Arquitetura da CameraX. - Um objeto
Recorderé uma implementação de VideoOutput que está rigidamente acoplada àVideoCapture. ORecorderé usado para executar a captura de vídeo e de áudio. Um aplicativo cria gravações usando umRecorder. - Uma
PendingRecordingconfigura uma gravação, fornecendo opções como ativação de áudio e definição de um listener de eventos. É necessário usar umRecorderpara criar umaPendingRecording. APendingRecordingnão grava nada. - Uma
Recordingrealiza a gravação em si. É necessário usar umaPendingRecordingpara criar umaRecording.
A figura 3 mostra as relações entre esses objetos:
Legenda:
- Crie um objeto
Recordercom oQualitySelector. - Configure o
Recordercom uma dasOutputOptions. - Ative o áudio com o método
withAudioEnabled(), se necessário. - Chame
start()com um listenerVideoRecordEventpara começar a gravar. - Use
pause()/resume()/stop()naRecordingpara controlar a gravação. - Responda a
VideoRecordEventsno listener de eventos.
A lista detalhada da API está no current.txt dentro do código-fonte (link em inglês).
Como usar a API VideoCapture
Para integrar o caso de uso da VideoCapture do CameraX ao seu app,
siga estas etapas:
- Vincule a
VideoCapture. - Prepare e configure a gravação.
- Inicie e controle a gravação em execução.
Veja nas seções abaixo o que você pode fazer em cada etapa para ter uma sessão de gravação completa.
Vincular a VideoCapture
Para vincular o caso de uso da VideoCapture, siga estas etapas:
- Crie um objeto
Recorder. - Crie um objeto
VideoCapture. - Vincule um
Lifecycle.
A API VideoCapture do CameraX segue o padrão de design do builder. Os aplicativos
usam o Recorder.Builder para criar um Recorder. Você também pode configurar a
resolução de vídeo para o Recorder usando um objeto QualitySelector.
O Recorder da CameraX oferece suporte a estas Qualities predefinidas
para resoluções de vídeo:
Quality.UHDpara vídeo em 4K Ultra HD (2160p)Quality.QHDpara vídeo em Quad HD (1440p), introduzido no CameraX1.7.0-alpha02Quality.FHDpara vídeo em Full HD (1080p)Quality.HDpara vídeo em HD (720p)Quality.SDpara vídeo em SD (480p)
O CameraX também pode escolher outras resoluções quando permitido pelo app.
O tamanho exato do vídeo de cada seleção depende dos recursos da câmera e do
codificador. Para mais informações, consulte a documentação do
CamcorderProfile.
Os aplicativos podem criar um
QualitySelector para configurar a resolução.
Você pode criar um QualitySelector usando um destes métodos:
Use
fromOrderedList()para fornecer algumas resoluções preferenciais e inclua uma estratégia substituta para ser usada caso não haja suporte a nenhuma delas.A CameraX pode decidir a melhor correspondência substituta com base na capacidade da câmera selecionada. Consulte a
FallbackStrategy specificationdoQualitySelectorpara ver mais detalhes. Por exemplo, o código abaixo solicita a resolução mais alta com suporte à gravação e, se nenhuma das resoluções da solicitação tiver suporte, autoriza a CameraX a escolher uma que seja a mais próxima da qualidade Quality.SD:val qualitySelector = QualitySelector.fromOrderedList( listOf(Quality.UHD, Quality.QHD, Quality.FHD, Quality.HD, Quality.SD), FallbackStrategy.lowerQualityOrHigherThan(Quality.SD))Primeiro, consulte os recursos da câmera e escolha uma das resoluções com suporte usando o método
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() } }O recurso retornado de
QualitySelector.getSupportedQualities()tem garantia de funcionar para o caso de usoVideoCaptureou para a combinação de casos de usoVideoCaptureePreview. A vinculação na CameraX com os casos de usoImageCaptureouImageAnalysisainda pode falhar quando a combinação necessária não tiver suporte à câmera solicitada.
Quando você tiver um QualitySelector, o aplicativo poderá criar um
objeto VideoCapture e realizar a vinculação. Observe que essa vinculação é
igual a outros 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)
}
Observe que bindToLifecycle() retorna um objeto Camera. Consulte este guia para ver mais informações sobre como controlar a saída da câmera, por exemplo, zoom e exposição.
O Recorder seleciona o formato mais adequado para o sistema. O codec
de vídeo mais comum é o
H.264 AVC,
com formato de contêiner
MPEG-4.
Configurar e criar uma gravação
Usando um Recorder, o aplicativo pode criar objetos de gravação para
realizar a captura de vídeo e áudio. Para criar gravações em aplicativos
siga estas etapas:
- Configure a classe
OutputOptionsusando o métodoprepareRecording(). - (Opcional) Ative a gravação de áudio.
- Use a função
start()para registrar um listenerVideoRecordEvente inicie a captura de vídeo.
O Recorder retorna um objeto Recording quando você chama a função start().
O aplicativo pode usar esse objeto Recording para concluir
a captura ou realizar outras ações, como pausar ou retomar.
Um Recorder oferece suporte a um objeto Recording por vez. Você pode iniciar uma
nova gravação depois de chamar o método Recording.stop() ou
Recording.close() no objeto Recording anterior.
Vamos analisar as etapas com mais detalhes. Primeiro, o aplicativo configura
as OutputOptions para um Gravador usando o método Recorder.prepareRecording().
Um Recorder oferece suporte aos tipos de OutputOptions abaixo:
FileDescriptorOutputOptionspara capturar umFileDescriptor.FileOutputOptionspara capturar umFile.MediaStoreOutputOptionspara capturar umMediaStore.
Todos os tipos de OutputOptions permitem definir um tamanho máximo de arquivo usando o método
setFileSizeLimit(). Outras opções são específicas do tipo de saída
individual, como o ParcelFileDescriptor para FileDescriptorOutputOptions.
O método prepareRecording() retorna um objeto PendingRecording, que é um
objeto intermediário usado para criar o objeto
Recording correspondente. A PendingRecording é uma classe temporária que
precisa ficar invisível na maioria dos casos e raramente é armazenada em cache pelo app.
Os aplicativos podem fazer mais configurações para a gravação, como:
- ativar o áudio usando o método
withAudioEnabled(); - registrar um listener para receber eventos de gravação de vídeo
usando
start(Executor, Consumer<VideoRecordEvent>). - Permitir que uma gravação continue sendo feita enquanto o VideoCapture ao qual ela está anexada
é vinculado a outra câmera, com
PendingRecording.asPersistentRecording().
Para iniciar a gravação, chame PendingRecording.start(). A CameraX transforma a
PendingRecording em uma Recording, coloca a solicitação de gravação em fila
e retorna o objeto Recording recém-criado ao aplicativo.
Quando a gravação é iniciada no dispositivo de câmera correspondente, a CameraX envia um
evento VideoRecordEvent.EVENT_TYPE_START.
O exemplo abaixo mostra como gravar vídeos e áudio em um arquivo
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)
Enquanto a visualização da câmera é espelhada na câmera frontal por padrão, os vídeos gravados pela VideoCapture não são espelhados por padrão. Com a CameraX 1.3, agora é possível espelhar gravações de vídeo para que a visualização da câmera frontal e o vídeo gravado correspondam.
Há três opções de MirrorMode: MIRROR_MODE_OFF, MIRROR_MODE_ON e
MIRROR_MODE_ON_FRONT_ONLY. Para alinhar com a
visualização da câmera, o Google recomenda usar MIROR_MODE_ON_FRONT_ONLY, o que significa
que
o espelhamento não está ativado para a câmera traseira, mas está ativado para a câmera
frontal. Para mais informações sobre o MirrorMode, consulte
MirrorMode constants.
Este snippet de código mostra como chamar
VideoCapture.Builder.setMirrorMode() usando MIRROR_MODE_ON_FRONT_ONLY. Para
mais informações, consulte 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);
Intervalo dinâmico (vídeo HDR)
O CameraX oferece suporte à gravação de vídeo em High Dynamic Range (HDR) de 10 bits, como
HLG10, HDR10, HDR10+ ou Dolby Vision, usando DynamicRange.
Para usar a gravação de vídeo em HDR, siga estas etapas:
- Consulte os intervalos dinâmicos compatíveis com a câmera usando
VideoCapabilities.getSupportedDynamicRanges(). - Opcional: se você configurar um
QualitySelectorpersonalizado, verifique quais qualidades são compatíveis com esse intervalo dinâmico usandogetSupportedQualities(DynamicRange). - Configure
VideoCapture.BuildercomsetDynamicRange().
O exemplo a seguir ativa a gravação HDR de 10 bits HLG quando a câmera oferece suporte a ela:
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();
Estabilização de vídeo
A CameraX oferece suporte à estabilização de vídeo para reduzir a trepidação e o jitter durante a gravação de vídeos.
Para ativar a estabilização de vídeo, siga estas etapas:
- Verifique se o dispositivo de câmera oferece suporte à estabilização usando
VideoCapabilities.isStabilizationSupported(). - Ative-o em
VideoCapture.BuilderusandosetVideoStabilizationEnabled(true).
O exemplo a seguir ativa a estabilização quando a câmera é compatível com ela:
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();
Controlar uma gravação ativa
Você pode pausar, retomar e parar uma Recording em andamento
usando estes métodos:
pausepara pausar a gravação ativa atual.resume()para retomar uma gravação ativa pausada.stop()para concluir a gravação e transferir todos os objetos de gravação associados.mute()para ativar ou desativar o som da gravação atual.
É possível chamar o método stop() para encerrar uma Recording, independente
de a gravação estar ou não pausada.
Se você registrou um EventListener com
PendingRecording.start(), a Recording vai se comunicar
usando um listener
VideoRecordEvent.
- O
VideoRecordEvent.EVENT_TYPE_STATUSé usado para gravar estatísticas como o tamanho de arquivo atual e o período gravado. - O
VideoRecordEvent.EVENT_TYPE_FINALIZEé usado para o resultado da gravação e inclui informações como o URI do arquivo final, além de qualquer erro relacionado.
Depois que o app receber um EVENT_TYPE_FINALIZE, que indica uma sessão
de gravação bem-sucedida, você poderá acessar o vídeo capturado no local
especificado nas OutputOptions.
Amplitude e estado do áudio em tempo real
Durante a gravação, os eventos VideoRecordEvent.Status fornecem estatísticas de áudio em tempo real usando AudioStats:
AudioStats.getAudioAmplitude()retorna a amplitude de pico de áudio atual, que varia de0.0a1.0. É possível usar esse valor para controlar medidores de IU ou visualizadores de forma de onda de áudio.AudioStats.getAudioState()indica o status atual do stream de áudio, comoAUDIO_STATE_ACTIVE,AUDIO_STATE_MUTEDouAUDIO_STATE_SOURCE_SILENCED.
O exemplo a seguir lê a amplitude de áudio de um evento de status:
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); } }
Vídeo em alta velocidade e câmera lenta
A CameraX oferece suporte à gravação de vídeo em alta velocidade, como 120, 240 ou 480 qps, o que permite dois casos de uso distintos:
- Vídeo com frame rate alto (câmera lenta processada pelo player): a CameraX captura e salva frames com frame rate alto (por exemplo, 120 QPS). O player de vídeo controla a velocidade do vídeo, permitindo que os usuários alternem entre a velocidade normal e a câmera lenta ou realizem análises quadro a quadro.
- Vídeo em câmera lenta pronto para reprodução: o CameraX processa o stream de alta velocidade e gera um arquivo de vídeo padrão de 30 QPS com marcações de tempo de frame ajustadas. O efeito de câmera lenta é "incorporado", então o vídeo é reproduzido em câmera lenta automaticamente em qualquer player de vídeo padrão sem suporte especial. Por exemplo, uma captura de 120 qps reproduzida a 30 qps produz um vídeo em câmera lenta de 1/4x.
Verificar os recursos de alta velocidade
A captura de vídeo em alta velocidade requer suporte de hardware e as definições
CamcorderProfile correspondentes dos fabricantes de dispositivos,
como CamcorderProfile.QUALITY_HIGH_SPEED_1080P ou
QUALITY_HIGH_SPEED_720P.
Antes de configurar a gravação em alta velocidade, consulte os recursos de alta velocidade usando
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);
Configurar HighSpeedVideoSessionConfig
Para usar a captura de alta velocidade, combine os casos de uso VideoCapture e Preview
em um HighSpeedVideoSessionConfig. Para configurar
a sessão, siga estas etapas:
- Crie um caso de uso de
VideoCapturee um caso de uso dePreview. - Inicialize
HighSpeedVideoSessionConfig.Buildercom o caso de usoVideoCapturee anexe oPreview. - Consulte os intervalos de frame rate compatíveis usando
CameraInfo.getSupportedFrameRateRanges(SessionConfig)e defina o intervalo selecionado. - Escolha entre vídeo com alta taxa de quadros ou vídeo em câmera lenta pronto para reprodução:
- Para criar um vídeo com frame rate alto, deixe
setSlowMotionEnabled(false)(padrão). - Para criar um vídeo em câmera lenta pronto para reprodução, chame
setSlowMotionEnabled(true).
- Para criar um vídeo com frame rate alto, deixe
- Vincule a configuração da sessão usando
cameraProvider.bindToLifecycle().
O exemplo a seguir configura e vincula uma sessão de câmera lenta pronta para reprodução:
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 );
Também é possível usar a DSL do Kotlin, introduzida no CameraX 1.7.0-alpha02, para
configurar sessões de alta velocidade de forma concisa:
val sessionConfig = highSpeedVideoSessionConfig(videoCapture) {
this.preview = preview
isSlowMotionEnabled = true
frameRateRange = supportedFpsRanges.first()
}
cameraProvider.bindToLifecycle(
lifecycleOwner,
CameraSelector.DEFAULT_BACK_CAMERA,
sessionConfig
)
Gravar vídeos em alta velocidade ou câmera lenta
Depois de vincular o HighSpeedVideoSessionConfig, comece a gravar usando
VideoCapture da mesma forma que as gravações de vídeo padrão:
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()); } } });
Outros recursos
Para saber mais sobre o CameraX, consulte os recursos abaixo:
- Codelab: Primeiros passos com o CameraX
- App de exemplo oficial do CameraX (link em inglês)
- Lista mais recente da API CameraX Video Capture (link em inglês)
- Notas da versão do CameraX
- Código-fonte do CameraX
- Captura em alta velocidade e vídeo em câmera lenta com o CameraX 1.5