Observação:esta página se refere ao pacote Camera2. A menos que seu app exija recursos específicos e de baixo nível do Camera2, recomendamos o uso do CameraX. CameraX e Camera2 oferecem suporte ao Android 5.0 (nível 21 da API) e versões mais recentes.
Um único dispositivo Android pode ter várias câmeras. Cada câmera é um
CameraDevice
,
e uma CameraDevice
pode gerar mais de um stream ao mesmo tempo.
Uma razão para fazer isso é que um stream, frames sequenciais da câmera,
de um CameraDevice
, é otimizado para uma tarefa específica, como mostrar
um visor, enquanto outros podem ser usados para tirar uma foto ou fazer um vídeo
gravação.Os streams funcionam como pipelines paralelos que processam frames brutos
saindo da câmera,um quadro de cada vez:
O processamento paralelo sugere que pode haver limites de desempenho dependendo a capacidade de processamento disponível na CPU, GPU ou em outro processador. Se um não consegue acompanhar os frames recebidos, ele começa a descartá-los.
Cada pipeline tem o próprio formato de saída. Os dados brutos que chegam são
automaticamente nos modelos apropriados
formato de saída por lógica implícita
associados a cada pipeline. O CameraDevice
usado na atualização desta página
os exemplos de código não são específicos, então você primeiro enumera
todas as câmeras disponíveis antes de continuar.
É possível usar o CameraDevice
para criar um
CameraCaptureSession
,
que é específico para esse CameraDevice
. Um CameraDevice
precisa receber um
configuração de frame para cada frame bruto usando CameraCaptureSession
. A
especifica atributos da câmera, como foco automático, abertura, efeitos,
e exposição. Devido a restrições de hardware, só é possível fazer
ativo no sensor da câmera a qualquer momento, chamado de
active.
No entanto, os casos de uso de streaming aprimoram e estendem as formas anteriores de usar CameraDevice
.
para transmitir sessões de captura, o que permite otimizar o fluxo da câmera para seus
para um caso de uso específico. Por exemplo, ele pode aumentar a duração da bateria ao otimizar
videochamadas.
Um CameraCaptureSession
descreve todos os possíveis pipelines vinculados
CameraDevice
. Quando uma sessão é criada, não é possível adicionar ou remover pipelines.
O CameraCaptureSession
mantém uma fila de
CaptureRequest
s,
que se tornam a configuração ativa.
Um CaptureRequest
adiciona uma configuração à fila e seleciona uma, mais de
um ou todos os pipelines disponíveis para receber um frame da
CameraDevice
. É possível enviar muitas solicitações de captura durante a vida útil de uma captura.
sessão. Cada solicitação pode mudar a configuração ativa e o conjunto de resultados
pipelines que recebem a imagem bruta.
Use casos de uso de streaming para melhorar a performance
Os casos de uso de stream são uma maneira de melhorar o desempenho da captura da Camera2 de conteúdo. Elas dão ao dispositivo de hardware mais informações para ajustar parâmetros, que oferece uma melhor experiência de câmera para sua tarefa específica.
Isso
Permite que o dispositivo de câmera otimize o hardware e os pipelines de software da câmera
com base nos cenários dos usuários para cada fluxo. Para mais informações sobre o uso de streams
Casos, consulte setStreamUseCase
.
Os casos de uso de streaming permitem especificar como um determinado stream da câmera é usado
em mais detalhes, além de definir um modelo em
CameraDevice.createCaptureRequest()
: Isso permite que o hardware da câmera otimize
como ajuste, modo de sensor ou configurações do sensor da câmera, com base no
as compensações de qualidade ou latência adequadas para casos de uso específicos.
Os casos de uso de streaming incluem:
DEFAULT
: abrange todo o comportamento do aplicativo atual. É equivalente a não configurando qualquer caso de uso de stream.PREVIEW
: recomendado para o visor ou a análise de imagens no app.STILL_CAPTURE
: otimizada para captura de alta resolução e alta qualidade. mantenham os frame rates como as visualizações.VIDEO_RECORD
: otimizado para captura de vídeo de alta qualidade, incluindo alta qualidade estabilização de imagem, se compatível com o dispositivo e ativado pelo aplicativo. Essa opção pode produzir frames de saída com um atraso substancial em relação ao tempo real, para permitir a estabilização de alta qualidade ou outro processamento.VIDEO_CALL
: recomendado para usos de câmera de longa duração em que o consumo de energia seja uma preocupante.PREVIEW_VIDEO_STILL
: recomendado para apps de mídia social ou para uso de stream único casos de uso diferentes. É um fluxo multiuso.VENDOR_START
: usado para casos de uso definidos pelo OEM.
Criar uma CameraCaptureSession
Para criar uma sessão de câmera, forneça um ou mais buffers de saída em que o app pode gravar frames de saída. Cada buffer representa um canal. Você deve faça isso antes de começar a usar a câmera para que o framework possa configurar os pipelines internos do dispositivo e alocar buffers de memória para enviar frames aos destinos de saída necessários.
O snippet de código a seguir mostra como preparar uma sessão de câmera com dois
buffers de saída, um que pertence
SurfaceView
e outro para um
ImageReader
Adicionando o caso de uso do stream PREVIEW
a previewSurface
e
Uso de stream do STILL_CAPTURE
O uso de caso para imReaderSurface
permite que o hardware do dispositivo otimize esses streams ainda
mais adiante.
Kotlin
// Retrieve the target surfaces, which might be coming from a number of places: // 1. SurfaceView, if you want to display the image directly to the user // 2. ImageReader, if you want to read each frame or perform frame-by-frame // analysis // 3. OpenGL Texture or TextureView, although discouraged for maintainability reasons // 4. RenderScript.Allocation, if you want to do parallel processing val surfaceView = findViewById<SurfaceView>(...) val imageReader = ImageReader.newInstance(...) // Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated() val previewSurface = surfaceView.holder.surface val imReaderSurface = imageReader.surface val targets = listOf(previewSurface, imReaderSurface) // Create a capture session using the predefined targets; this also involves // defining the session state callback to be notified of when the session is // ready // Setup Stream Use Case while setting up your Output Configuration. @RequiresApi(Build.VERSION_CODES.TIRAMISU) fun configureSession(device: CameraDevice, targets: List<Surface>){ val configs = mutableListOf<OutputConfiguration>() val streamUseCase = CameraMetadata .SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL targets.forEach { val config = OutputConfiguration(it) config.streamUseCase = streamUseCase.toLong() configs.add(config) } ... device.createCaptureSession(session) }
Java
// Retrieve the target surfaces, which might be coming from a number of places: // 1. SurfaceView, if you want to display the image directly to the user // 2. ImageReader, if you want to read each frame or perform frame-by-frame analysis // 3. RenderScript.Allocation, if you want to do parallel processing // 4. OpenGL Texture or TextureView, although discouraged for maintainability reasons Surface surfaceView = findViewById<SurfaceView>(...); ImageReader imageReader = ImageReader.newInstance(...); // Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated() Surface previewSurface = surfaceView.getHolder().getSurface(); Surface imageSurface = imageReader.getSurface(); List<Surface> targets = Arrays.asList(previewSurface, imageSurface); // Create a capture session using the predefined targets; this also involves defining the // session state callback to be notified of when the session is ready private void configureSession(CameraDevice device, List<Surface> targets){ ArrayList<OutputConfiguration> configs= new ArrayList() String streamUseCase= CameraMetadata .SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL for(Surface s : targets){ OutputConfiguration config = new OutputConfiguration(s) config.setStreamUseCase(String.toLong(streamUseCase)) configs.add(config) } device.createCaptureSession(session) }
Até aqui, você não definiu a configuração ativa da câmera. Quando a sessão é configurada, é possível criar e enviar imagens solicitações para fazer isso.
A transformação aplicada às entradas à medida que são gravadas no buffer é
determinados pelo tipo de cada destino, que deve ser um
Surface
O framework do Android sabe como
converter uma imagem bruta da configuração ativa em um formato apropriado para
para cada alvo. A conversão é controlada pelo formato do pixel e tamanho do
Surface
específica.
O framework tenta fazer o possível, mas algumas Surface
de configuração do Terraform podem não funcionar, causando problemas como
não está sendo criada, gerando um erro de tempo de execução quando você envia uma solicitação ou
degradação do desempenho. O framework oferece garantias para soluções
combinações de dispositivo, superfície e parâmetros de solicitação. A documentação
createCaptureSession()
fornece mais informações.
CaptureRequests únicos
A configuração usada para cada frame é codificada em um CaptureRequest
, que é
enviados para a câmera. Para criar uma solicitação de captura, é possível usar um dos
predefinido
modelos,
ou use TEMPLATE_MANUAL
para ter controle total. Quando você escolhe
é necessário fornecer um ou mais buffers de saída para serem usados com
da solicitação. Só é possível usar buffers já definidos na captura
que você pretende usar.
As solicitações de captura usam um
padrão de builder
e dar aos desenvolvedores a oportunidade de definir muitos
incluindo
exposição automática,
foco automático,
e
abertura da lente.
Antes de definir um campo, verifique se a opção específica está disponível para o
do dispositivo chamando
CameraCharacteristics.getAvailableCaptureRequestKeys()
e se há suporte para o valor desejado verificando a câmera apropriada
como a exposição automática disponível
dois modos.
Para criar uma solicitação de captura para um SurfaceView
usando o modelo
projetada para visualização sem qualquer modificação, use
CameraDevice.TEMPLATE_PREVIEW
:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest = session.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) captureRequest.addTarget(previewSurface)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback CaptureRequest.Builder captureRequest = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); captureRequest.addTarget(previewSurface);
Com uma solicitação de captura definida, agora é possível enviar para a sessão da câmera:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest: CaptureRequest = ... // from CameraDevice.createCaptureRequest() // The first null argument corresponds to the capture callback, which you // provide if you want to retrieve frame metadata or keep track of failed capture // requests that can indicate dropped frames; the second null argument // corresponds to the Handler used by the asynchronous callback, which falls // back to the current thread's looper if null session.capture(captureRequest.build(), null, null)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback CaptureRequest captureRequest = ...; // from CameraDevice.createCaptureRequest() // The first null argument corresponds to the capture callback, which you // provide if you want to retrieve frame metadata or keep track of failed // capture // requests that can indicate dropped frames; the second null argument // corresponds to the Handler used by the asynchronous callback, which falls // back to the current thread's looper if null session.capture(captureRequest.build(), null, null);
Quando um frame de saída é colocado em um buffer específico, um objeto de captura
callback
é acionada. Em muitos casos, callbacks adicionais, como
ImageReader.OnImageAvailableListener
,
é acionado quando o frame que ele contém é processado. Está em
nesse ponto, é possível recuperar dados de imagem do buffer especificado.
Repetir CaptureRequests
As solicitações de câmera única são simples de fazer, mas para exibir uma visualização ou vídeo, elas não são muito úteis. Nesse caso, você precisa receber uma stream contínuo de frames, não apenas um único. O snippet de código a seguir mostra como adicionar solicitação recorrente à sessão:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest: CaptureRequest = ... // from CameraDevice.createCaptureRequest() // This keeps sending the capture request as frequently as possible until // the // session is torn down or session.stopRepeating() is called // session.setRepeatingRequest(captureRequest.build(), null, null)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback CaptureRequest captureRequest = ...; // from CameraDevice.createCaptureRequest() // This keeps sending the capture request as frequently as possible until the // session is torn down or session.stopRepeating() is called // session.setRepeatingRequest(captureRequest.build(), null, null);
Uma solicitação de captura recorrente faz com que o dispositivo de câmera capture continuamente
imagens usando as configurações no CaptureRequest
fornecido. A API Camera2
também permite que os usuários capturem vídeos da câmera enviando
repetindo CaptureRequests
como nesta
Amostra Camera2
no GitHub. Ele também pode renderizar vídeos em câmera lenta capturando uma
vídeo de alta velocidade (câmera lenta) usando sequência repetida CaptureRequests
conforme mostrado no app de exemplo de vídeo em câmera lenta do Camera2
no GitHub.
Intercalar CaptureRequests
Para enviar uma segunda solicitação de captura enquanto a solicitação de captura recorrente estiver ativa, faça o seguinte: como para exibir um visor e permitir que os usuários capturem uma foto, você não precisa interromper o pedido recorrente. Em vez disso, você emite uma captura não repetida enquanto a repetição é executada.
Qualquer buffer de saída usado precisa ser configurado como parte da sessão da câmera quando a sessão é criada. Solicitações repetidas têm prioridade menor que solicitações de frame único ou burst, que permitem que o exemplo a seguir funcione:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback // Create the repeating request and dispatch it val repeatingRequest = session.device.createCaptureRequest( CameraDevice.TEMPLATE_PREVIEW) repeatingRequest.addTarget(previewSurface) session.setRepeatingRequest(repeatingRequest.build(), null, null) // Some time later... // Create the single request and dispatch it // NOTE: This can disrupt the ongoing repeating request momentarily val singleRequest = session.device.createCaptureRequest( CameraDevice.TEMPLATE_STILL_CAPTURE) singleRequest.addTarget(imReaderSurface) session.capture(singleRequest.build(), null, null)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback // Create the repeating request and dispatch it CaptureRequest.Builder repeatingRequest = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); repeatingRequest.addTarget(previewSurface); session.setRepeatingRequest(repeatingRequest.build(), null, null); // Some time later... // Create the single request and dispatch it // NOTE: This can disrupt the ongoing repeating request momentarily CaptureRequest.Builder singleRequest = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); singleRequest.addTarget(imReaderSurface); session.capture(singleRequest.build(), null, null);
No entanto, essa abordagem tem uma desvantagem: você não sabe exatamente quando quando ocorre a solicitação única. Na figura a seguir, se A for a sequência captura de tela e B é a solicitação de captura de frame único. É assim que o processa a fila de solicitações:
Não há garantias de latência entre a última solicitação repetida da A antes que a solicitação B seja ativada e na próxima vez que A for usada novamente, o que pode resultar em alguns frames pulados. Há algumas coisas que você pode fazer para atenuar esse problema:
Adicione os destinos de saída da solicitação A à solicitação B. Dessa forma, quando O frame de B está pronto, ele é copiado para os destinos de saída de A. Isso é essencial, por exemplo, ao fazer snapshots de vídeos para manter frame rate estável. No código anterior, você adiciona
singleRequest.addTarget(previewSurface)
antes de criar a solicitação.Use uma combinação de modelos criados para esse cenário específico como o tempo de renderização zero.