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
:
Como mostrado na figura 2, a captura de vídeo do CameraX inclui alguns componentes de arquitetura de alto nível:
SurfaceProvider
para a origem do vídeo.AudioSource
para 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 interfaceLifecycleOwner
com uma classeCameraSelector
e 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
PendingRecording
configura uma gravação, fornecendo opções como ativação de áudio e definição de um listener de eventos. É necessário usar umRecorder
para criar umaPendingRecording
. APendingRecording
não grava nada. - Uma
Recording
realiza a gravação em si. É necessário usar umaPendingRecording
para criar umaRecording
.
A figura 3 mostra as relações entre esses objetos:
Legenda:
- Crie um objeto
Recorder
com oQualitySelector
. - Configure o
Recorder
com uma dasOutputOptions
. - Ative o áudio com o método
withAudioEnabled()
, se necessário. - Chame
start()
com um listenerVideoRecordEvent
para começar a gravar. - Use
pause()
/resume()
/stop()
naRecording
para controlar a gravação. - Responda a
VideoRecordEvents
no 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.UHD
para vídeo em 4K Ultra HD (2160p)Quality.FHD
para vídeo em Full HD (1080p)Quality.HD
para vídeo em HD (720p)Quality.SD
para 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 specification
doQualitySelector
para 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 usoVideoCapture
ou para a combinação de casos de usoVideoCapture
ePreview
. A vinculação na CameraX com os casos de usoImageCapture
ouImageAnalysis
ainda 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
OutputOptions
usando o métodoprepareRecording()
. - (Opcional) Ative a gravação de áudio.
- Use a função
start()
para registrar um listenerVideoRecordEvent
e 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:
FileDescriptorOutputOptions
para capturar umFileDescriptor
.FileOutputOptions
para capturar umFile
.MediaStoreOutputOptions
para 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:
pause
para 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