Nota: Esta página hace referencia al paquete Camera2. A menos que la app requiera funciones específicas y de bajo nivel de Camera2, te recomendamos que uses CameraX. CameraX y Camera2 admiten Android 5.0 (nivel de API 21) y versiones posteriores.
Un solo dispositivo Android puede tener varias cámaras. Cada cámara es un
CameraDevice
:
y una CameraDevice
puede generar más de una transmisión de forma simultánea.
Un motivo es que una transmisión, los fotogramas de cámara secuenciales
de un objeto CameraDevice
, se optimiza para una tarea específica, como mostrar
un visor, mientras que otros se pueden usar para tomar una foto o hacer un video
registro. Las transmisiones actúan como canalizaciones paralelas que procesan fotogramas sin procesar.
saliendo de la cámara,de a un fotograma a la vez:
El procesamiento paralelo sugiere que puede haber límites de rendimiento la potencia de procesamiento disponible de la CPU, la GPU o algún otro procesador. Si un no puede seguir el ritmo de los fotogramas entrantes, comienza a descartarlos.
Cada canalización tiene su propio formato de salida. Los datos sin procesar que ingresan
transformarse automáticamente en la solución
formato de salida por lógica implícita
asociados con cada canalización. El CameraDevice
que se usa en todas las
las muestras de código no son específicas, por lo que primero debes enumerar
todas las cámaras disponibles antes de continuar.
Puedes usar el CameraDevice
para crear un
CameraCaptureSession
,
que es específico de ese CameraDevice
. Un CameraDevice
debe recibir un
de fotogramas para cada fotograma sin procesar usando CameraCaptureSession
. El
especifica los atributos de la cámara, como enfoque automático, apertura, efectos,
y la exposición. Debido a restricciones de hardware, solo se puede configurar
activo en el sensor de la cámara en cualquier momento, lo que se denomina
active.
Sin embargo, los casos de uso de transmisión mejoran y extienden las formas anteriores de usar CameraDevice
.
para transmitir sesiones de captura, lo que te permite optimizar la transmisión de la cámara para tu
caso de uso particular. Por ejemplo, puede mejorar la duración de batería al optimizar
videollamadas.
Un CameraCaptureSession
describe todas las posibles canalizaciones vinculadas al
CameraDevice
Cuando se crea una sesión, no puedes agregar ni quitar canalizaciones.
CameraCaptureSession
mantiene una cola de
CaptureRequest
,
que se convierten en la configuración activa.
Un CaptureRequest
agrega una configuración a la cola y selecciona una, más de
una o todas las canalizaciones disponibles para recibir una trama del
CameraDevice
Puedes enviar muchas solicitudes de captura durante la vida útil de una captura
sesión. Cada solicitud puede cambiar la configuración activa y el conjunto de resultados
de datos que reciben la imagen sin procesar.
Usa casos de uso de transmisión para obtener un mejor rendimiento
Los casos de uso de transmisión permiten mejorar el rendimiento de las capturas de Camera2 sesiones. Le dan al dispositivo de hardware más información para ajustar los parámetros lo que brinda una mejor experiencia con la cámara para realizar una tarea específica.
Esta
Permite que el dispositivo de cámara optimice el hardware de la cámara y las canalizaciones de software.
según las situaciones de los usuarios para cada transmisión. Para obtener más información sobre el uso de las Novedades
Casos, consulta setStreamUseCase
.
Los casos de uso de transmisión te permiten especificar cómo se usa una transmisión de cámara en particular
más detalles, además de configurar una plantilla en
CameraDevice.createCaptureRequest()
Esto permite que el hardware de la cámara optimice
parámetros, como el ajuste, el modo del sensor o la configuración del sensor de la cámara, según
entre calidad y latencia,
adecuados para casos de uso específicos.
Los casos de uso de transmisión incluyen los siguientes:
DEFAULT
: Abarca todo el comportamiento existente de la aplicación. Es lo mismo que no establecer cualquier caso de uso de Stream.PREVIEW
: Se recomienda para el visor o el análisis de imágenes en la app.STILL_CAPTURE
: Optimizada para la captura de alta calidad y alta resolución, pero no que mantenga velocidades de fotogramas similares a las de la vista previa.VIDEO_RECORD
: Optimizada para la captura de video de alta calidad, incluidas las de alta calidad la estabilización de imagen, si es compatible con el dispositivo y la aplicación la habilita. Esta opción puede producir fotogramas de salida con un retraso considerable a partir del tiempo real para permitir una estabilización de más alta calidad u otro procesamiento.VIDEO_CALL
: Se recomienda para usos de cámara de larga duración en los que el consumo de energía sea un problema.PREVIEW_VIDEO_STILL
: Se recomienda para las apps de redes sociales o el uso de transmisión única. diferentes. Es una transmisión multipropósito.VENDOR_START
: Se usa para los casos de uso definidos por el OEM.
Cómo crear una CameraCaptureSession
Para crear una sesión de cámara, proporciónale uno o más búferes de salida en las que tu app pueda escribir marcos de salida. Cada búfer representa una canalización. Debes hazlo antes de empezar a usar la cámara para que el framework pueda configurar las canalizaciones internas del dispositivo y asignan los búferes de memoria para enviar tramas a los objetivos de salida necesarios.
En el siguiente fragmento de código, se muestra cómo puedes preparar una sesión de la cámara con dos
búferes de salida, uno que pertenece a un
SurfaceView
y otro a un
ImageReader
Agregando el caso de uso de transmisión de PREVIEW
a previewSurface
y
STILL_CAPTURE
Uso de transmisión
La sentencia case hasta imReaderSurface
permite que el hardware del dispositivo optimice estas transmisiones incluso
un poco más lejos.
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) }
En este punto, no definiste la configuración activa de la cámara. Cuando se configura la sesión, puedes crear y enviar capturas para hacer eso.
Se aplica la transformación que se aplica a las entradas tal como se escriben en el búfer.
determinados por el tipo de cada objetivo, que debe ser una
Surface
El framework de Android sabe cómo
convertir una imagen sin procesar en la configuración activa a un formato adecuado
para cada objetivo. La conversión se controla según el formato de píxeles y el tamaño de la
Surface
particular.
El framework intenta dar lo mejor de sí, pero algunas Surface
es posible que las combinaciones de configuración no funcionen y que provoquen problemas
o un error de entorno de ejecución cuando envías una solicitud
la disminución del rendimiento. El framework proporciona garantías
de dispositivos, de superficie y de solicitud. La documentación para
createCaptureSession()
proporciona más información.
Capturas únicas
La configuración que se usa para cada fotograma está codificada en un CaptureRequest
, que se
enviados a la cámara. Para crear una solicitud de captura, puedes usar uno de los
predefinidos
plantillas,
o puedes usar TEMPLATE_MANUAL
para tener el control total. Cuando eliges un
debes proporcionar uno o más búferes de salida para usar con
la solicitud. Solo puedes usar los búferes que ya se definieron en la captura
de usuario que quieres usar.
Las solicitudes de captura usan un
patrón de compilador
y brindarles a los desarrolladores la oportunidad de configurar muchos tipos de
varias opciones, como
exposición automática,
enfoque automático,
y
Apertura del lente.
Antes de configurar un campo, asegúrate de que la opción específica esté disponible para el
el dispositivo llamando
CameraCharacteristics.getAvailableCaptureRequestKeys()
y de que se admita el valor deseado verificando la cámara adecuada
característica, como la exposición automática disponible
modos.
Cómo crear una solicitud de captura para un SurfaceView
con la plantilla
diseñada para ofrecer vistas previas sin modificaciones, usa
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);
Con una solicitud de captura definida, ahora puedes enviar a la sesión de la cámara:
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);
Cuando una trama de salida se coloca en el búfer específico, una captura
devolución de llamada
. En muchos casos, devoluciones de llamada adicionales, como
ImageReader.OnImageAvailableListener
:
se activa cuando se procesa la trama que contiene. Se encuentra a la(s)
en este punto, puedes recuperar datos de imagen del búfer especificado.
Repetir CaptureRequests
Las solicitudes de una sola cámara son fáciles de hacer, pero se usan para mostrar una transmisión en vivo. de vista previa o video, no son muy útiles. En ese caso, debes recibir un de fotogramas, no solo uno. El siguiente fragmento de código muestra cómo agregar un repetición de la solicitud a la sesión:
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);
Una solicitud de captura recurrente hace que el dispositivo de la cámara capture imágenes de forma continua
con la configuración del CaptureRequest
proporcionado. La API de Camera2
permite capturar videos con la cámara enviando
se repite CaptureRequests
, como se muestra en este
Ejemplo de Camera2
en un repositorio de GitHub. También puede renderizar videos en cámara lenta capturando una
video de alta velocidad (cámara lenta) con ráfaga repetida CaptureRequests
como se muestra en la app de ejemplo de video en cámara lenta Camera2
en GitHub.
Cómo intercalar CaptureRequests
Para enviar una segunda solicitud de captura mientras la solicitud de captura recurrente está activa, haz lo siguiente: como mostrar un visor y permitir que los usuarios tomen una foto, no necesitas detener la solicitud recurrente en curso. En cambio, se emitirá una captura no repetitiva mientras se ejecuta la solicitud recurrente.
Cualquier búfer de salida utilizado se debe configurar como parte de la sesión de la cámara. cuando se crea la sesión por primera vez. Las solicitudes repetidas tienen menor prioridad que solicitudes de un solo fotograma o de ráfaga, que permiten que funcione el siguiente ejemplo:
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);
Sin embargo, este enfoque presenta una desventaja: no se sabe exactamente cuándo se produce la única solicitud. En la siguiente figura, si A es la columna solicitud de captura y B es la solicitud de captura de un solo fotograma. Así es como la sesión procesa la cola de solicitudes:
No hay garantías para la latencia entre la última solicitud recurrente desde A antes de que se active la solicitud B y la próxima vez que se use A por lo que es posible que se omitan algunos fotogramas. Hay algunas cosas que puedes hacer para mitigar este problema:
Agrega los destinos de salida de la solicitud A a la solicitud B. De esa manera, cuando El marco de B está listo y se copia en los destinos de salida de A. Esto es esencial cuando se hacen instantáneas de video para mantener una velocidad de fotogramas constante. En el código anterior, agregas
singleRequest.addTarget(previewSurface)
antes de crear la solicitud.Usar una combinación de plantillas diseñadas para esta situación en particular como un retraso sin obturador.