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 VideoCapure, 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.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.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.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() } }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 seja gravada continuamente enquanto a VideoCapture estiver conectada
é revinculado para 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)
Embora a visualização da câmera seja espelhada na câmera frontal por padrão, os vídeos gravados pela VideoCapture não são espelhados por padrão. Com o CameraX 1.3, é agora é possível espelhar gravações de vídeo para que a visualização da câmera frontal e o correspondência de vídeo gravada.
Há três opções de MirrorMode: MIRROR_MODE_OFF, MIRROR_MODE_ON e
MIRROR_MODE_ON_FRONT_ONLY. Para alinhar com o
o Google recomenda o uso de MIROR_MODE_ON_FRONT_ONLY, ou seja,
que
o espelhamento não está ativado para a câmera traseira, mas está ativado para a
câmera. Para saber mais sobre o MirrorMode, consulte
MirrorMode constants
Este snippet de código mostra como chamar
VideoCapture.Builder.setMirrorMode() usando MIRROR_MODE_ON_FRONT_ONLY. Para
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);
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.
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