Projection multimédia

Les API android.media.projection introduites dans Android 5 (niveau d'API 21) vous permettent de capturer le contenu d'un écran d'appareil en tant que flux multimédia que vous pouvez lire, enregistrer ou caster sur d'autres appareils, tels que des téléviseurs.

Android 14 (niveau d'API 34) introduit le partage d'écran d'application, qui permet aux utilisateurs de partager une seule fenêtre d'application plutôt que l'intégralité de l'écran de l'appareil, quel que soit le mode de fenêtrage. Le partage d'écran de l'application exclut la barre d'état, la barre de navigation, les notifications et les autres éléments d'interface utilisateur du système de l'écran partagé, même lorsque le partage d'écran de l'application est utilisé pour capturer une application en plein écran. Seul le contenu de l'application sélectionnée est partagé.

Le partage d'écran garantit la confidentialité des utilisateurs, améliore leur productivité et améliore le multitâche en leur permettant d'exécuter plusieurs applications tout en limitant le partage de contenu à une seule application.

Trois représentations d'affichage

Une projection multimédia capture le contenu de l'écran d'un appareil ou d'une fenêtre d'application, puis projette l'image capturée sur un écran virtuel qui affiche l'image sur un Surface.

Écran réel de l'appareil projeté sur l'écran virtuel Contenu de l'écran virtuel écrit sur la "surface" fournie par l'application
Figure 1 : Écran d'appareil réel ou fenêtre d'application projetée sur l'écran virtuel. Écran virtuel écrit dans le Surface fourni par l'application.

L'application fournit le Surface au moyen d'un élément MediaRecorder, SurfaceTexture ou ImageReader, qui utilise le contenu de l'écran capturé et vous permet de gérer les images affichées sur le Surface en temps réel. Vous pouvez sauvegarder les images sous forme d'enregistrement ou les caster sur un téléviseur ou un autre appareil.

Display réel

Démarrez une session de projection multimédia en obtenant un jeton permettant à votre application de capturer le contenu de l'écran de l'appareil ou de la fenêtre de l'application. Ce jeton est représenté par une instance de la classe MediaProjection.

Utilisez la méthode getMediaProjection() du service système MediaProjectionManager pour créer une instance MediaProjection lorsque vous démarrez une nouvelle activité. Démarrez l'activité avec un intent de la méthode createScreenCaptureIntent() pour spécifier une opération de capture d'écran:

Kotlin

val mediaProjectionManager = getSystemService(MediaProjectionManager::class.java)
var mediaProjection : MediaProjection

val startMediaProjection = registerForActivityResult(
    StartActivityForResult()
) { result ->
    if (result.resultCode == RESULT_OK) {
        mediaProjection = mediaProjectionManager
            .getMediaProjection(result.resultCode, result.data!!)
    }
}

startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent())

Java

final MediaProjectionManager mediaProjectionManager =
    getSystemService(MediaProjectionManager.class);
final MediaProjection[] mediaProjection = new MediaProjection[1];

ActivityResultLauncher<Intent> startMediaProjection = registerForActivityResult(
    new StartActivityForResult(),
    result -> {
        if (result.getResultCode() == Activity.RESULT_OK) {
            mediaProjection[0] = mediaProjectionManager
                .getMediaProjection(result.getResultCode(), result.getData());
        }
    }
);

startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent());

Écran virtuel

La pièce maîtresse d'une projection multimédia est l'écran virtuel, que vous créez en appelant createVirtualDisplay() sur une instance MediaProjection :

Kotlin

virtualDisplay = mediaProjection.createVirtualDisplay(
                     "ScreenCapture",
                     width,
                     height,
                     screenDensity,
                     DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                     surface,
                     null, null)

Java

virtualDisplay = mediaProjection.createVirtualDisplay(
                     "ScreenCapture",
                     width,
                     height,
                     screenDensity,
                     DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                     surface,
                     null, null);

Les paramètres width et height spécifient les dimensions de l'écran virtuel. Pour obtenir les valeurs de largeur et de hauteur, utilisez les API WindowMetrics introduites dans Android 11 (niveau d'API 30). (Pour en savoir plus, consultez la section Taille de la projection multimédia.)

Surface

Dimensionnez la surface de projection multimédia pour obtenir une sortie avec la résolution appropriée. Faites en sorte que la surface soit grande (basse résolution) pour caster l'écran sur des téléviseurs ou des écrans d'ordinateur, et petite (haute résolution) pour l'enregistrement de l'écran de l'appareil.

À partir d'Android 12L (niveau d'API 32), lors du rendu du contenu capturé sur la surface, le système adapte le contenu de manière uniforme, en conservant le format, de sorte que les deux dimensions du contenu (largeur et hauteur) soient égales ou inférieures aux dimensions correspondantes de la surface. Le contenu capturé est ensuite centré sur la surface.

L'approche de mise à l'échelle d'Android 12L améliore la diffusion de l'écran sur les téléviseurs et d'autres grands écrans en maximisant la taille de l'image de la surface tout en garantissant un format correct.

Autorisation pour le service de premier plan

Si votre application cible Android 14 ou une version ultérieure, son fichier manifeste doit inclure une déclaration d'autorisation pour le type de service de premier plan mediaProjection:

<manifest ...>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
    <application ...>
        <service
            android:name=".MyMediaProjectionService"
            android:foregroundServiceType="mediaProjection"
            android:exported="false">
        </service>
    </application>
</manifest>

Démarrez le service de projection multimédia avec un appel à startForeground().

Si vous ne spécifiez pas le type de service de premier plan dans l'appel, il est défini par défaut sur un entier au niveau du bit des types de services de premier plan définis dans le fichier manifeste. Si le fichier manifeste ne spécifie aucun type de service, le système génère une erreur MissingForegroundServiceTypeException.

Votre application doit demander le consentement de l'utilisateur avant chaque session de projection multimédia. Une session correspond à un appel unique à createVirtualDisplay(). Un jeton MediaProjection ne doit être utilisé qu'une seule fois pour effectuer l'appel.

Sur Android 14 ou version ultérieure, la méthode createVirtualDisplay() génère une erreur SecurityException si votre application effectue l'une des opérations suivantes:

  • Transmet plusieurs fois une instance Intent renvoyée par createScreenCaptureIntent() à getMediaProjection()
  • Appelle createVirtualDisplay() plusieurs fois sur la même instance MediaProjection.

Taille de la projection multimédia

Une projection multimédia peut capturer la totalité de l'écran de l'appareil ou une fenêtre d'application, quel que soit le mode de fenêtrage.

Taille initiale

Avec la projection multimédia en plein écran, votre application doit déterminer la taille de l'écran de l'appareil. Avec le partage d'écran, votre application ne peut pas déterminer la taille de l'écran capturé tant que l'utilisateur n'a pas sélectionné la zone de capture. Ainsi, la taille initiale de toute projection multimédia correspond à la taille de l'écran de l'appareil.

Utilisez la méthode getMaximumWindowMetrics() WindowManager de la plate-forme pour renvoyer un objet WindowMetrics pour l'écran de l'appareil, même si l'application hôte de projection multimédia est en mode multifenêtre et n'occupe donc qu'une partie de l'écran.

Pour assurer la compatibilité des versions remontant jusqu'au niveau d'API 14, utilisez la méthode WindowMetricsCalculator computeMaximumWindowMetrics() de la bibliothèque Jetpack WindowManager.

Appelez la méthode WindowMetrics getBounds() pour obtenir la largeur et la hauteur de l'écran de l'appareil.

Changements de taille

La taille de la projection multimédia peut changer lorsque l'utilisateur fait pivoter l'appareil ou que l'utilisateur sélectionne une fenêtre d'application comme zone de capture dans le partage d'écran de l'application. La projection multimédia peut être mise au format letterbox si la taille du contenu capturé est différente de celle des métriques de fenêtre maximales obtenues au moment de la configuration de la projection multimédia.

Pour vous assurer que la projection multimédia s'aligne précisément sur la taille du contenu capturé pour n'importe quelle région capturée et lors des rotations de l'appareil, utilisez le rappel onCapturedContentResize() afin de redimensionner la capture. Pour en savoir plus, consultez la section Personnalisation ci-après.

Fonctionnalités

Votre application peut personnaliser l'expérience utilisateur de projection multimédia avec les API MediaProjection.Callback suivantes:

  • onCapturedContentVisibilityChanged() : permet à l'application hôte (l'application qui a lancé la projection multimédia) d'afficher ou de masquer le contenu partagé.

    Utilisez ce rappel pour personnaliser l'UI de votre application selon que la région capturée est visible ou non par l'utilisateur. Par exemple, si votre application est visible par l'utilisateur et qu'elle affiche le contenu capturé dans l'interface utilisateur de l'application, et que l'application capturée est également visible par l'utilisateur (comme indiqué via ce rappel), celui-ci voit le même contenu deux fois. Utilisez le rappel pour mettre à jour l'interface utilisateur de votre application afin de masquer le contenu capturé et de libérer de l'espace dans votre application pour d'autres contenus.

  • onCapturedContentResize() : permet à l'application hôte de modifier la taille de la projection multimédia sur l'écran virtuel et la projection multimédia Surface en fonction de la taille de la zone d'affichage capturée.

    Déclenché chaque fois que le contenu capturé (une seule fenêtre d'application ou l'affichage complet de l'appareil) change de taille (en raison de la rotation de l'appareil ou du passage de l'application capturée dans un autre mode de fenêtrage). Utilisez cette API pour redimensionner l'écran virtuel et la surface afin de vous assurer que le format correspond au contenu capturé et que la capture n'est pas au format letterbox.

Récupération des ressources

Votre application doit enregistrer le rappel MediaProjection onStop() pour libérer les ressources qu'elle détient, telles que l'écran virtuel et la surface de projection.

Le rappel est appelé lorsque la projection multimédia se termine ou lorsque l'utilisateur n'autorise pas la poursuite d'une session de capture.

Si votre application n'enregistre pas le rappel et que l'utilisateur n'accepte pas la session de projection multimédia, les appels à createVirtualDisplay() génèrent IllegalStateException.

Désactiver

Android 14 ou version ultérieure active le partage d'écran des applications par défaut. Chaque session de projection multimédia permet aux utilisateurs de partager une fenêtre de l'application ou l'intégralité de l'écran.

Votre application peut désactiver le partage d'écran en appelant la méthode createScreenCaptureIntent(MediaProjectionConfig) avec un argument MediaProjectionConfig renvoyé par un appel à createConfigForDefaultDisplay().

Un appel à createScreenCaptureIntent(MediaProjectionConfig) avec un argument MediaProjectionConfig renvoyé par un appel à createConfigForUserChoice() est identique au comportement par défaut, à savoir un appel à createScreenCaptureIntent().

Applications redimensionnables

Faites toujours en sorte que vos applications de projection multimédia soient redimensionnables (resizeableActivity="true"). Les applications redimensionnables sont compatibles avec les modifications de configuration des appareils et le mode multifenêtre (voir Compatibilité avec le mode multifenêtre).

Si votre application n'est pas redimensionnable, elle doit interroger les limites d'affichage à partir du contexte d'une fenêtre et utiliser getMaximumWindowMetrics() pour récupérer le WindowMetrics de la zone d'affichage maximale disponible pour l'application :

Kotlin

val windowContext = context.createWindowContext(context.display!!,
      WindowManager.LayoutParams.TYPE_APPLICATION, null)
val projectionMetrics = windowContext.getSystemService(WindowManager::class.java)
      .maximumWindowMetrics

Java

Context windowContext = context.createWindowContext(context.getDisplay(),
      WindowManager.LayoutParams.TYPE_APPLICATION, null);
WindowMetrics projectionMetrics = windowContext.getSystemService(WindowManager.class)
      .getMaximumWindowMetrics();

Ressources supplémentaires

Pour en savoir plus sur la projection multimédia, consultez la section Capturer la lecture vidéo et audio.