Remarque:Cette page fait référence au package Camera2. Nous vous recommandons d'utiliser CameraX, sauf si votre application nécessite des fonctionnalités spécifiques de base de Camera2. CameraX et Camera2 sont compatibles avec Android 5.0 (niveau d'API 21) ou version ultérieure.
Un même appareil Android peut être équipé de plusieurs caméras. Chaque caméra est
CameraDevice
,
et un CameraDevice
peut générer plusieurs flux simultanément.
Cela permet notamment qu'un flux unique, des images séquentielles de l'appareil photo
à partir d'un CameraDevice
, est optimisé pour une tâche spécifique, comme afficher
un viseur, tandis que d'autres peuvent servir à prendre une photo ou à enregistrer une vidéo.
Les flux agissent comme des pipelines parallèles qui traitent les trames brutes.
qui sortent de la caméra,une image à la fois:
![](https://developer.android.com/static/images/training/camera/camera2/capture-sessions-requests-1.png?authuser=9&hl=fr)
Le traitement en parallèle suggère qu'il peut exister des limites de performances selon la puissance de traitement disponible du CPU, du GPU ou d'un autre processeur. Si un le pipeline ne peut pas suivre le rythme des frames entrants, il commence à les supprimer.
Chaque pipeline a son propre format de sortie. Les données brutes qui arrivent sont
automatiquement
format de sortie par logique implicite
associées à chaque pipeline. Le CameraDevice
utilisé tout au long des
exemples de code n'est pas spécifique. Vous devez donc d'abord énumérer
toutes les caméras disponibles avant de continuer.
Vous pouvez utiliser CameraDevice
pour créer un
CameraCaptureSession
,
qui est spécifique à ce CameraDevice
. Un CameraDevice
doit recevoir une
configuration de frame pour chaque frame brut à l'aide de CameraCaptureSession
. La
spécifie les attributs de l'appareil photo : autofocus, ouverture, effets,
et l'exposition. En raison de contraintes matérielles, une seule configuration
dans le capteur de l'appareil photo à un moment donné. C'est ce que l'on appelle
active.
Cependant, les cas d'utilisation des flux améliorent et étendent les façons précédentes d'utiliser CameraDevice
.
pour diffuser les sessions de capture, ce qui vous permet d'optimiser le flux de la caméra
un cas d'utilisation particulier. Par exemple, il peut améliorer l'autonomie de la batterie lors de l'optimisation
appels vidéo.
Un CameraCaptureSession
décrit tous les pipelines possibles liés au
CameraDevice
Lorsqu'une session est créée, vous ne pouvez pas ajouter ni supprimer de pipelines.
Le CameraCaptureSession
gère une file d'attente de
CaptureRequest
,
qui deviennent la configuration active.
Un CaptureRequest
ajoute une configuration à la file d'attente et en sélectionne une, de plus de
l'un ou l'ensemble des pipelines disponibles pour recevoir une trame du
CameraDevice
Vous pouvez envoyer de nombreuses demandes de capture tout au long de la durée de vie d'une capture
session. Chaque requête peut modifier la configuration active et l'ensemble des résultats
qui reçoivent l'image brute.
Utiliser des cas d'utilisation de flux pour de meilleures performances
Les cas d'utilisation des flux permettent d'améliorer les performances de la capture Camera2 sessions. Elles donnent au dispositif matériel plus d'informations pour régler les paramètres, ce qui améliore l'expérience de la caméra pour votre tâche spécifique.
Ce
permet à l'appareil photo d'optimiser les pipelines logiciels et matériels de la caméra
en fonction de scénarios utilisateur pour chaque flux. Pour en savoir plus sur l'utilisation du flux
Pour les cas, consultez setStreamUseCase
.
Les cas d'utilisation des flux vous permettent de spécifier l'utilisation d'un flux de caméra spécifique
plus de détails, en plus de définir un modèle dans
CameraDevice.createCaptureRequest()
Cela permet au matériel de l'appareil photo d'optimiser
tels que les réglages, le mode du capteur ou les réglages du capteur de l'appareil photo, en fonction
des compromis de qualité ou de latence
adaptés à des cas d'utilisation spécifiques.
Voici quelques cas d'utilisation des flux:
DEFAULT
: couvre tous les comportements existants de l'application. Cela équivaut à ne pas en définissant un cas d'utilisation de flux.PREVIEW
: recommandé pour l'analyse d'images dans le viseur ou dans l'application.STILL_CAPTURE
: optimisé pour la capture haute résolution de haute qualité, et non devrait maintenir des fréquences d'images semblables à celles d'un aperçu.VIDEO_RECORD
: optimisé pour la capture vidéo de haute qualité, y compris des images de haute qualité stabilisation de l'image, si elle est compatible avec l'appareil et activée par l'application. Cette option peut produire des frames de sortie avec un décalage important par rapport au temps réel, pour permettre une stabilisation ou un autre traitement de haute qualité.VIDEO_CALL
: recommandé pour les utilisations de longue durée des caméras lorsqu'une consommation électrique problème.PREVIEW_VIDEO_STILL
: recommandé pour les applications de réseaux sociaux ou l'utilisation d'un seul flux cas d'utilisation. Il s'agit d'un flux polyvalent.VENDOR_START
: utilisé pour les cas d'utilisation définis par l'OEM.
Créer une CameraCaptureSession
Pour créer une session de caméra, fournissez-lui un ou plusieurs tampons de sortie votre application peut écrire des frames de sortie. Chaque tampon représente un pipeline. Vous devez avant d'utiliser l'appareil photo pour que le framework puisse configurer aux pipelines internes de l'appareil et allouent des tampons de mémoire pour l'envoi de trames aux cibles de sortie requises.
L'extrait de code suivant vous montre comment préparer une session d'appareil photo avec deux
tampons de sortie, l'un appartenant à
SurfaceView
et un autre à un
ImageReader
Ajout du cas d'utilisation du flux PREVIEW
à previewSurface
et
STILL_CAPTURE
Utilisation du flux
La casse jusqu'à imReaderSurface
permet au matériel de l'appareil d'optimiser ces flux
plus loin.
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 = findViewByIdS<urfaceView(>...) 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: ListS<urface)>{ val configs = mutableListOfO<utputConfiguration(>) 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 = findViewByIdS<urfaceView(>...); ImageReader imageReader = ImageReader.newInstance(...); // Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated() Surface previewSurface = surfaceView.getHolder().getSurface(); Surface imageSurface = imageReader.getSurface(); ListS<urface >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, ListS<urface >targets){ ArrayListO<utputConfiguration >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) }
À ce stade, vous n'avez pas défini la configuration active de la caméra. Une fois la session configurée, vous pouvez créer et envoyer une capture les requêtes pour y parvenir.
La transformation appliquée aux entrées telles qu'elles sont écrites dans leur tampon est
déterminé par le type de chaque cible, qui doit être
Surface
Le framework Android sait comment
convertir une image brute de la configuration active dans un format approprié
chaque cible. La conversion est contrôlée par le format et la taille en pixels du
un Surface
spécifique.
Le framework essaie de faire de son mieux, mais certains Surface
les combinaisons de configuration peuvent ne pas fonctionner, ce qui peut entraîner des problèmes
en cours de création, une erreur d'exécution est générée lorsque vous envoyez une requête ;
et de dégradation des performances. Le framework fournit des garanties pour des
des combinaisons de paramètres d'appareil, de surface et de requête. La documentation
createCaptureSession()
fournit plus d'informations.
Demandes de capture uniques
La configuration utilisée pour chaque trame est encodée dans un CaptureRequest
, qui est
envoyé à la caméra. Pour créer une demande de capture, vous pouvez utiliser l'un des
prédéfini
templates,
Vous pouvez aussi utiliser TEMPLATE_MANUAL
pour bénéficier d'un contrôle total. Lorsque vous choisissez
vous devez fournir un ou plusieurs tampons de sortie à utiliser
la demande. Vous ne pouvez utiliser que des tampons déjà définis dans la capture
que vous prévoyez d'utiliser.
Les demandes de capture utilisent
schéma de création
et donner aux développeurs la possibilité de définir de nombreuses
options, y compris
exposition automatique,
l'autofocus,
et
ouverture de l'objectif.
Avant de définir un champ, assurez-vous que l'option spécifique est disponible pour la
appareil en appelant
CameraCharacteristics.getAvailableCaptureRequestKeys()
et que la valeur souhaitée est prise en charge en vérifiant l'appareil photo
comme l'exposition automatique disponible
modes.
Créer une demande de capture pour un SurfaceView
à l'aide du modèle
prévisualisée sans modification, utilisez
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);
Maintenant qu'une demande de capture est définie, vous pouvez envoyer à la session caméra:
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);
Lorsqu'une image de sortie est placée dans le tampon spécifique, une capture
rappel
est déclenché. Dans de nombreux cas, des rappels supplémentaires, tels que
ImageReader.OnImageAvailableListener
,
se déclenche lorsque le frame qu'il contient est traité. Elle est à
vous pouvez récupérer les données d'image
à partir du tampon spécifié.
Requêtes de capture répétées
Les demandes à caméra unique sont simples à utiliser, mais elles permettent d'afficher un aperçu ou une vidéo, ils ne sont pas très utiles. Dans ce cas, vous devez recevoir un un flux continu de trames, pas seulement une seule. L'extrait de code suivant montre comment ajouter demande répétée à la session:
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);
Lorsque les requêtes sont répétées, l'appareil photo capture en continu
à l'aide des paramètres du fichier CaptureRequest
fourni. L'API Camera2
permet également aux utilisateurs d'enregistrer une vidéo à partir de la caméra en envoyant
"CaptureRequests
", comme vous le voyez ici,
Exemple Camera2
sur GitHub. Il peut également restituer une vidéo au ralenti en capturant un
vidéo rapide (au ralenti) avec une rafale répétée CaptureRequests
comme indiqué dans l'application exemple de vidéo au ralenti de CameraX
sur GitHub.
Entrelacement de requêtes de capture
Pour envoyer une deuxième requête de capture lorsque la requête de capture répétée est active, procédez comme suit : (par exemple, pour afficher un viseur et permettre aux utilisateurs de prendre une photo), vous n'avez pas besoin pour arrêter la requête récurrente en cours. Au lieu de cela, vous émettez une capture non récurrente pendant que la requête récurrente continue de s'exécuter.
Le tampon de sortie utilisé doit être configuré dans le cadre de la session d'appareil photo. lors de la création de la session. Les requêtes répétées ont une priorité inférieure à de requêtes uniques ou intensives, qui permettent à l'exemple suivant de fonctionner:
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);
Cette approche présente cependant un inconvénient: vous ne savez pas exactement quand la demande unique se produit. Dans la figure suivante, si A est la demande de capture et B est la demande de capture d'image unique. Voilà comment la requête traite la file d'attente des requêtes:
![](https://developer.android.com/static/images/training/camera/camera2/capture-sessions-requests-2.png?authuser=9&hl=fr)
La latence n'est pas garantie entre la dernière requête répétée de A avant l'activation de la demande B et la prochaine fois que la requête A est utilisée Il se peut donc que des images soient ignorées. Il y a certaines choses que vous pouvez faire pour atténuer ce problème:
Ajoutez les cibles de sortie de la requête A à la requête B. De cette façon, lorsque Le frame de B est prêt, il est copié dans les cibles de sortie de A. C'est essentiel, par exemple, lorsque vous créez des instantanés vidéo pour maintenir une fréquence d'images stable. Dans le code précédent, vous ajoutez
singleRequest.addTarget(previewSurface)
avant de créer la requête.Utilisez une combinaison de modèles conçus pour fonctionner pour ce scénario particulier, comme le déclenchement instantané.