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.
Les appareils photo et les aperçus de l'appareil photo ne sont pas toujours dans la même orientation sur les appareils Android.
Une caméra est dans une position fixe sur un appareil, que celui-ci est un téléphone, une tablette ou un ordinateur. Lorsque l'orientation de l'appareil change, l'orientation de la caméra change également.
Par conséquent, les applications d'appareil photo partent généralement du principe qu'il existe une relation fixe entre l'orientation de l'appareil et le format de l'aperçu de l'appareil photo. Lorsqu'un le téléphone est en mode portrait, l'aperçu de l'appareil photo est supposé être plus grand que large. Lorsque le téléphone (et la caméra) sont tournés en mode paysage, l'aperçu de l'appareil photo doit être plus large que haut.
Mais ces hypothèses sont remises en question par de nouveaux facteurs de forme, comme les appareils pliables appareils, et les modes d'affichage mode multifenêtre et multi-écran. Les appareils pliables modifient la taille et le format de l'écran sans modifier l'orientation. Le mode multifenêtre restreint les applications d'appareil photo à une partie à l'écran, en redimensionnant l'aperçu de l'appareil photo, quelle que soit l'orientation de l'appareil. Le mode multi-écran permet d'utiliser des écrans secondaires qui peuvent ne pas être dans la même orientation que l'écran principal.
Orientation de l'appareil photo
La Définition de compatibilité Android spécifie qu'un capteur d'image de l'appareil photo "DOIT être orienté de sorte que le long la dimension de l'appareil photo s'aligne sur la dimension longue de l'écran. C'est-à-dire, lorsque l'appareil est tenu en mode paysage, les appareils DOIVENT capturer des images l'orientation paysage. Cela s'applique quelle que soit la orientation; Autrement dit, elle s'applique aux appareils principaux en mode paysage "portrait-primary",
L'agencement de la caméra par rapport à l'écran maximise la zone d'affichage du viseur de l'appareil photo dans une application Appareil photo. De plus, les capteurs d'image génèrent généralement leurs données au format paysage, le format 4:3 étant le plus courant.

L'orientation naturelle du capteur de l'appareil photo est en mode paysage. Dans la figure 1, le capteur de la caméra avant (l'appareil photo pointant dans la même direction que l'écran) est pivoté de 270 degrés par rapport au téléphone pour respecter la définition de compatibilité Android.
Pour exposer la rotation du capteur aux applications, l'API camera2 inclut une constante SENSOR_ORIENTATION
. Pour la plupart des téléphones et tablettes, l'appareil indique une orientation de capteur de 270 degrés pour les caméras avant et de 90 degrés (point de vue depuis l'arrière de l'appareil) pour les caméras arrière, ce qui aligne le bord long du capteur sur le bord long de l'appareil. Les caméras d'ordinateur portable indiquent généralement une orientation du capteur de 0 ou 180 degrés.
Comme les capteurs d'image de l'appareil photo envoient leurs données (tampon d'image) dans la
l'orientation naturelle du capteur (paysage), le tampon d'image doit faire pivoter
nombre de degrés spécifié par SENSOR_ORIENTATION
pour que l'aperçu de l'appareil photo
à la verticale dans l'orientation naturelle de l'appareil. Pour les caméras avant, la rotation se fait dans le sens inverse des aiguilles d'une montre. Pour les caméras arrière, elle se fait dans le sens des aiguilles d'une montre.
Par exemple, pour la caméra avant de la figure 1, le tampon d'image produit par le capteur de l'appareil photo se présente comme suit :

L'image doit pivoter de 270 degrés dans le sens inverse des aiguilles d'une montre correspond à l'orientation de l'appareil:

Une caméra arrière produirait un tampon d'image avec la même orientation que le tampon ci-dessus, mais SENSOR_ORIENTATION
est de 90 degrés. Par conséquent, le tampon est pivoté de 90 degrés dans le sens des aiguilles d'une montre.
Rotation de l'appareil
La rotation de l'appareil correspond au nombre de degrés de rotation de l'appareil par rapport à sa valeur naturelle l'orientation. Par exemple, un téléphone en mode paysage pivote de 90 ou 270 degrés, selon la direction de rotation.
La rotation de la mémoire tampon d'image d'un capteur photo doit être égale au même nombre de degrés que de rotation de l'appareil (en plus des degrés d'orientation du capteur) l'aperçu de l'appareil photo pour qu'il s'affiche à la verticale.
Calcul de l'orientation
L'orientation correcte de l'aperçu de l'appareil photo tient compte du capteur l'orientation et la rotation de l'appareil.
La rotation globale du tampon d'image du capteur peut être calculée à l'aide de la formule suivante :
rotation = (sensorOrientationDegrees - deviceOrientationDegrees * sign + 360) % 360
où sign
est 1
pour les caméras avant et -1
pour les caméras arrière.
Pour les caméras avant, la mémoire tampon image est pivotée dans le sens inverse des aiguilles d'une montre (de l'orientation naturelle du capteur). Sur les caméras arrière, le capteur la mémoire tampon de l'image est pivotée dans le sens des aiguilles d'une montre.
L'expression deviceOrientationDegrees * sign + 360
convertit la rotation de l'appareil
dans le sens inverse des aiguilles d'une montre pour les caméras arrière
conversion de 270 degrés dans le sens inverse des aiguilles d'une montre à 90 degrés dans le sens des aiguilles d'une montre). L'opération modulo réduit le résultat à moins de 360 degrés (par exemple, en réduisant 540 degrés de rotation à 180).
Différentes API signalent la rotation de l'appareil différemment :
Display#getRotation()
permet de faire pivoter l'appareil dans le sens inverse des aiguilles d'une montre (du point de l'utilisateur de vue). Cette valeur s'intègre telle quelle dans la formule ci-dessus.OrientationEventListener#onOrientationChanged()
renvoie la rotation de l'appareil dans le sens des aiguilles d'une montre (du point de vue de l'utilisateur). N'annule pas la valeur à utiliser dans la formule ci-dessus.
Caméras avant

Voici le tampon d'image produit par le capteur de l'appareil photo dans la figure 2 :

Le tampon doit être pivoté de 270 degrés dans le sens inverse des aiguilles d'une montre pour s'adapter à l'orientation du capteur (voir Orientation de la caméra ci-dessus) :

Le tampon est ensuite pivoté de 90 degrés supplémentaires dans le sens inverse des aiguilles d'une montre pour tenir compte de la rotation de l'appareil, ce qui permet d'obtenir l'orientation correcte de l'aperçu de l'appareil photo dans la figure 2 :

Voici la caméra orientée vers la droite en mode paysage :

Voici le tampon d'image :

Faites pivoter la mémoire tampon de 270 degrés dans le sens inverse des aiguilles d'une montre pour s'adapter au capteur. orientation:

Ensuite, le tampon fait encore pivoter de 270 degrés dans le sens inverse des aiguilles d'une montre pour tenir compte la rotation de l'appareil:

Caméras arrière
Les caméras arrière ont généralement une orientation de capteur de 90 degrés (vu de l'arrière de l'appareil). Lorsque vous orientez l'aperçu de l'appareil photo, le tampon d'image du capteur est pivoté dans le sens des aiguilles d'une montre en fonction de la rotation du capteur (plutôt que dans le sens inverse des aiguilles d'une montre comme pour les caméras avant), puis le tampon d'image est pivoté dans le sens inverse des aiguilles d'une montre en fonction de la rotation de l'appareil.

Voici le tampon d'image du capteur de l'appareil photo dans la figure 4 :

Le tampon doit être pivoté de 90 degrés dans le sens des aiguilles d'une montre pour s'adapter à l'orientation du capteur :

Le tampon est ensuite pivoté de 270 degrés dans le sens inverse des aiguilles d'une montre pour tenir compte de la rotation de l'appareil :

Format
Le format d'affichage change lorsque l'orientation de l'appareil change, mais aussi lorsque les appareils pliables se plient et se déplient, lorsque les fenêtres sont redimensionnées en mode multifenêtre et lorsque les applications s'ouvrent sur des écrans secondaires.
La mémoire tampon de l'image du capteur de l'appareil photo doit être orientée et mise à l'échelle pour correspondre à l'orientation et le format de l'élément d'interface utilisateur du viseur en tant qu'interface utilisateur modifie l'orientation de manière dynamique, avec ou sans l'appareil. l'orientation.
Sur de nouveaux facteurs de forme, ou dans des environnements multifenêtres ou multi-écrans, l'application suppose que l'aperçu de l'appareil photo a la même orientation que l'appareil (portrait ou paysage) il est possible que votre aperçu ne soit pas correctement orienté ou mis à l'échelle. incorrectement, ou les deux.

Dans la figure 5, l'application a supposé à tort que l'appareil avait fait l'objet d'une rotation de 90 degrés dans le sens inverse des aiguilles d'une montre ; et donc, l'application a fait pivoter l'aperçu de la même quantité.

Dans la figure 6, l'application n'a pas ajusté le format du tampon d'image à permettent-lui de s'adapter correctement aux nouvelles dimensions de l'UI d'aperçu de l'appareil photo .
Les applications d'appareil photo à orientation fixe rencontrent généralement des problèmes sur les appareils pliables et Autres appareils à grand écran, tels que les ordinateurs portables:

Dans la figure 7, l'UI de l'application Appareil photo est à l'envers, car l'orientation de l'application est limitée au mode Portrait uniquement. L'image du viseur est correctement orientée. par rapport au capteur de l'appareil photo.
Mode Portrait en incrustation
Applications d'appareil photo non compatibles avec le mode multifenêtre
(resizeableActivity="false"
)
et limiter leur orientation
(screenOrientation="portrait"
)
ou screenOrientation="landscape"
)
peuvent être placés en mode Portrait en incrustation sur les appareils à grand écran pour s'orienter correctement
l'aperçu de l'appareil photo.
Boîtes aux lettres en mode Portrait en incrustation (encarts) pour les applications en mode portrait uniquement en mode Portrait même si l'écran est au format paysage. Les applications en mode paysage sont affichées au format letterbox en mode paysage, même si le format de l'écran est en mode portrait. L'image de l'appareil photo est pivotée pour s'aligner avec l'UI de l'application, recadrée pour correspondre au format de l'aperçu de l'appareil photo ; puis mise à l'échelle pour remplir l'aperçu.
Le mode portrait intégré se déclenche lorsque le format du capteur d'image de l'appareil photo et le format de l'activité principale de l'application ne correspondent pas.

Dans la figure 8, l'application d'appareil photo en mode portrait uniquement a été pivotée pour afficher l'interface utilisateur à l'endroit sur l'écran de l'ordinateur portable. L'application est mise au format letterbox en raison de la différence. entre l'application en mode portrait et l'écran en mode paysage. L'appareil photo l'image d'aperçu a été pivotée pour compenser la rotation de l'interface utilisateur de l'application (en raison de mode Portrait incrusté), et l'image a été recadrée et mise à l'échelle pour s'adapter l'orientation portrait, ce qui réduit le champ de vision.
Faire pivoter, recadrer, mettre à l'échelle
Le mode Portrait en encart est appelé pour une application d'appareil photo en mode portrait sur un écran. au format paysage:

L'application est mise au format letterbox en mode portrait:

L'image de la caméra fait l'objet d'une rotation de 90 degrés pour ajuster la réorientation de la application:

L'image est recadrée selon le format de l'aperçu de l'appareil photo, puis mise à l'échelle à remplir l'aperçu (le champ de vision est réduit):

Sur les appareils pliables, l'orientation du capteur de l'appareil photo peut être en mode portrait. alors que l'écran est au format paysage:

Étant donné que l'aperçu de l'appareil photo est pivoté pour s'adapter à l'orientation du capteur, l'image est correctement orientée dans le viseur, mais l'application en mode portrait uniquement est de travers.
Le mode Portrait en incrustation n'a besoin d'utiliser le format letterbox que pour l'application en mode Portrait. pour orienter correctement l'application et l'aperçu de l'appareil photo:

API
À partir d'Android 12 (niveau d'API 31), les applications peuvent également contrôler explicitement le mode portrait intégré à l'aide de la propriété SCALER_ROTATE_AND_CROP
de la classe CaptureRequest
.
La valeur par défaut est SCALER_ROTATE_AND_CROP_AUTO
, ce qui permet au système d'appeler le mode portrait intégré.
SCALER_ROTATE_AND_CROP_90
est le comportement du mode portrait intégré, comme décrit ci-dessus.
Tous les appareils ne sont pas compatibles avec toutes les valeurs SCALER_ROTATE_AND_CROP
. Pour obtenir la liste des valeurs acceptées, consultez CameraCharacteristics#SCALER_AVAILABLE_ROTATE_AND_CROP_MODES
.
CameraX
Bibliothèque Jetpack CameraX permet de créer un viseur d'appareil photo qui s'adapte à l'orientation du capteur la rotation des appareils une tâche simple.
L'élément de mise en page PreviewView
crée un aperçu de l'appareil photo qui ajuste automatiquement l'orientation du capteur,
la rotation des appareils et le scaling. PreviewView
conserve le format de l'image de l'appareil photo en appliquant le type d'échelle FILL_CENTER
, qui centre l'image, mais peut la recadrer pour qu'elle corresponde aux dimensions de PreviewView
. Pour mettre l'image de la caméra au format letterbox, définissez le type d'échelle sur FIT_CENTER
.
Pour découvrir les principes de base de la création d'un aperçu de l'appareil photo avec PreviewView
, consultez la section Implémenter un aperçu.
Pour obtenir un exemple d'implémentation complète, consultez le dépôt CameraXBasic
sur GitHub.
CameraViewfinder
Comme pour le cas d'utilisation Preview, CameraViewfinder propose un ensemble d'outils permettant de simplifier la création d'un aperçu d'appareil photo. Il ne dépend pas de CameraX Core. Vous pouvez donc l'intégrer facilement à votre le codebase Camera2 existant.
Au lieu d'utiliser
Surface
vous pouvez utiliser
CameraViewfinder
pour afficher le flux de la caméra de Camera2.
CameraViewfinder
utilise TextureView
ou SurfaceView
en interne.
pour afficher le flux de la caméra et applique les transformations requises à
pour afficher correctement le viseur.
Cela implique de corriger leur format, leur échelle et leur rotation.
Pour demander la surface de l'objet CameraViewfinder
, vous devez :
créer un ViewfinderSurfaceRequest
;
Cette demande contient les exigences liées à la résolution de la surface et à la caméra
informations de CameraCharacteristics
.
L'appel de requestSurfaceAsync()
envoie la requête au fournisseur de surface, qui est un TextureView
ou un SurfaceView
, et obtient un ListenableFuture
de Surface
.
L'appel de markSurfaceSafeToRelease()
informe le fournisseur de surface que la surface n'est pas nécessaire et que les ressources associées peuvent être libérées.
fun startCamera(){ val previewResolution = Size(width, height) val viewfinderSurfaceRequest = ViewfinderSurfaceRequest(previewResolution, characteristics) val surfaceListenableFuture = cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest) Futures.addCallback(surfaceListenableFuture, object : FutureCallback<Surface> { override fun onSuccess(surface: Surface) { /* create a CaptureSession using this surface as usual */ } override fun onFailure(t: Throwable) { /* something went wrong */} }, ContextCompat.getMainExecutor(context)) }
void startCamera(){ Size previewResolution = new Size(width, height); ViewfinderSurfaceRequest viewfinderSurfaceRequest = new ViewfinderSurfaceRequest(previewResolution, characteristics); ListenableFuture<Surface> surfaceListenableFuture = cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest); Futures.addCallback(surfaceListenableFuture, new FutureCallback<Surface>() { @Override public void onSuccess(Surface result) { /* create a CaptureSession using this surface as usual */ } @Override public void onFailure(Throwable t) { /* something went wrong */} }, ContextCompat.getMainExecutor(context)); }
SurfaceView
SurfaceView
est une approche simple pour créer un aperçu de l'appareil photo si l'aperçu ne nécessite pas de traitement et n'est pas animé.
SurfaceView
fait pivoter automatiquement le tampon d'image du capteur de l'appareil photo pour qu'il corresponde à l'orientation de l'écran, en tenant compte à la fois de l'orientation du capteur et de la rotation de l'appareil. Toutefois, le tampon d'image est mis à l'échelle pour s'adapter à SurfaceView
sans tenir compte du format.
Vous devez vous assurer que les proportions du tampon d'image correspondent aux proportions
le ratio de SurfaceView
, que vous pouvez obtenir en redimensionnant le contenu
de SurfaceView
dans l'objet
onMeasure()
méthode:
(Le code source computeRelativeRotation()
se trouve dans la section Rotation relative ci-dessous.)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { val width = MeasureSpec.getSize(widthMeasureSpec) val height = MeasureSpec.getSize(heightMeasureSpec) val relativeRotation = computeRelativeRotation(characteristics, surfaceRotationDegrees) if (previewWidth > 0f && previewHeight > 0f) { /* Scale factor required to scale the preview to its original size on the x-axis. */ val scaleX = if (relativeRotation % 180 == 0) { width.toFloat() / previewWidth } else { width.toFloat() / previewHeight } /* Scale factor required to scale the preview to its original size on the y-axis. */ val scaleY = if (relativeRotation % 180 == 0) { height.toFloat() / previewHeight } else { height.toFloat() / previewWidth } /* Scale factor required to fit the preview to the SurfaceView size. */ val finalScale = min(scaleX, scaleY) setScaleX(1 / scaleX * finalScale) setScaleY(1 / scaleY * finalScale) } setMeasuredDimension(width, height) }
@Override void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); int relativeRotation = computeRelativeRotation(characteristics, surfaceRotationDegrees); if (previewWidth > 0f && previewHeight > 0f) { /* Scale factor required to scale the preview to its original size on the x-axis. */ float scaleX = (relativeRotation % 180 == 0) ? (float) width / previewWidth : (float) width / previewHeight; /* Scale factor required to scale the preview to its original size on the y-axis. */ float scaleY = (relativeRotation % 180 == 0) ? (float) height / previewHeight : (float) height / previewWidth; /* Scale factor required to fit the preview to the SurfaceView size. */ float finalScale = Math.min(scaleX, scaleY); setScaleX(1 / scaleX * finalScale); setScaleY(1 / scaleY * finalScale); } setMeasuredDimension(width, height); }
Pour savoir comment implémenter SurfaceView
en tant qu'aperçu de l'appareil photo, consultez
Orientation de la caméra.
TextureView
TextureView
est moins performant que SurfaceView
(et plus de travail), mais TextureView
vous offre un contrôle maximal de l'aperçu de l'appareil photo.
TextureView
fait pivoter le tampon d'image du capteur en fonction de son orientation, mais ne gère pas la rotation de l'appareil ni le redimensionnement de l'aperçu.
Le redimensionnement et la rotation peuvent être encodés dans une transformation Matrix. Pour savoir comment mettre à l'échelle et faire pivoter correctement un TextureView
, consultez Prendre en charge les surfaces redimensionnables dans votre application d'appareil photo.
Rotation relative
La rotation relative du capteur de l'appareil photo correspond au degré de rotation requis pour aligner la sortie du capteur de l'appareil photo sur l'orientation de l'appareil.
La rotation relative est utilisée par des composants tels que SurfaceView
et TextureView
afin de déterminer les facteurs de mise à l'échelle x et y pour l'image d'aperçu. Il permet également de spécifier la rotation du tampon d'image du capteur.
La
CameraCharacteristics
et
Les classes Surface
permettent de calculer le
rotation relative du capteur de l'appareil photo:
/** * Computes rotation required to transform the camera sensor output orientation to the * device's current orientation in degrees. * * @param characteristics The CameraCharacteristics to query for the sensor orientation. * @param surfaceRotationDegrees The current device orientation as a Surface constant. * @return Relative rotation of the camera sensor output. */ public fun computeRelativeRotation( characteristics: CameraCharacteristics, surfaceRotationDegrees: Int ): Int { val sensorOrientationDegrees = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!! // Reverse device orientation for back-facing cameras. val sign = if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT ) 1 else -1 // Calculate desired orientation relative to camera orientation to make // the image upright relative to the device orientation. return (sensorOrientationDegrees - surfaceRotationDegrees * sign + 360) % 360 }
/** * Computes rotation required to transform the camera sensor output orientation to the * device's current orientation in degrees. * * @param characteristics The CameraCharacteristics to query for the sensor orientation. * @param surfaceRotationDegrees The current device orientation as a Surface constant. * @return Relative rotation of the camera sensor output. */ public int computeRelativeRotation( CameraCharacteristics characteristics, int surfaceRotationDegrees ){ Integer sensorOrientationDegrees = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); // Reverse device orientation for back-facing cameras. int sign = characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT ? 1 : -1; // Calculate desired orientation relative to camera orientation to make // the image upright relative to the device orientation. return (sensorOrientationDegrees - surfaceRotationDegrees * sign + 360) % 360; }
Métriques sur les fenêtres
La taille de l'écran ne doit pas être utilisée pour déterminer les dimensions du viseur de l'appareil photo. L'application Appareil photo peut s'exécuter dans une partie de l'écran, en mode multifenêtre sur les appareils mobiles ou en mode sans encombrement sur ChromeOS.
WindowManager#getCurrentWindowMetrics()
(ajoutée au niveau d'API 30) renvoie la taille de la fenêtre de l'application au lieu de
la taille de l'écran. Les méthodes de bibliothèque Jetpack WindowManager WindowMetricsCalculator#computeCurrentWindowMetrics()
et WindowInfoTracker#currentWindowMetrics()
offrent une compatibilité similaire avec le niveau d'API 14.
Rotation à 180 degrés
Rotation à 180 degrés d'un appareil (par exemple, de l'orientation naturelle à
l'orientation naturelle à l'envers) ne déclenche pas
onConfigurationChanged()
. Par conséquent, l'aperçu de l'appareil photo peut être à l'envers.
Pour détecter une rotation à 180 degrés, implémentez un DisplayListener
et vérifiez la rotation de l'appareil avec un appel à Display#getRotation()
dans le rappel onDisplayChanged()
.
Ressources exclusives
Avant Android 10, seule l'activité visible la plus élevée dans un environnement multifenêtre était à l'état RESUMED
. Cela était déroutant pour
les utilisateurs parce que
le système n'a fourni aucune indication
sur l'activité réactivée.
Android 10 (niveau d'API 29) introduit la multireprise, où toutes les activités visibles
sont à l'état RESUMED
; Les activités visibles peuvent toujours être incluses dans le PAUSED
si, par exemple, une activité transparente se superpose à l'activité
l'activité ne peut pas être mise au point, par exemple en mode Picture-in-picture (voir
fonctionnalité Picture-in-picture).
Une application qui utilise l'appareil photo, le micro ou toute ressource exclusive ou singleton au niveau d'API 29 ou supérieur doit prendre en charge la reprise multiple. Pour
Par exemple, si trois activités réactivées souhaitent utiliser la caméra, une seule peut
pour accéder à cette ressource exclusive. Chaque activité doit mettre en œuvre
onDisconnected()
rappel pour savoir si un accès préventif à l'appareil photo est accordé avec une priorité plus élevée
activité.
Pour en savoir plus, consultez la section Multi-reprise.
Ressources supplémentaires
- Pour obtenir un exemple de Camera2, consultez l'application Camera2Basic sur GitHub.
- Pour en savoir plus sur le cas d'utilisation en preview de CameraX, consultez CameraX Implémenter un aperçu
- Pour obtenir un exemple d'implémentation de l'aperçu de l'appareil photo CameraX, consultez le dépôt CameraXBasic sur GitHub.
- Pour en savoir plus sur l'aperçu de l'appareil photo sur ChromeOS, consultez la section Orientations de l'appareil photo.
- Pour en savoir plus sur le développement pour les appareils pliables, consultez la page En savoir plus sur les appareils pliables.