Surfaces externes spatiales

Appareils XR concernés
Ces conseils vous aideront à créer des expériences pour ces types d'appareils XR.
Casques XR
Lunettes XR filaires

Un SpatialExternalSurface est un composable de sous-espace qui crée et gère le Surface dans lequel votre application peut dessiner du contenu, comme une image ou une vidéo. SpatialExternalSurface est compatible avec les contenus stéréoscopiques ou monoscopiques.

Ajouter une surface pour le contenu image ou vidéo

Cet exemple montre comment charger une vidéo stéréoscopique côte à côte à l'aide de Media3 Exoplayer et de SpatialExternalSurface :

@OptIn(ExperimentalComposeApi::class)
@Composable
fun SpatialExternalSurfaceContent() {
    val context = LocalContext.current
    Subspace {
        SpatialExternalSurface(
            modifier = SubspaceModifier
                .width(1200.dp) // Default width is 400.dp if no width modifier is specified
                .height(676.dp), // Default height is 400.dp if no height modifier is specified
            // Use StereoMode.Mono, StereoMode.SideBySide, or StereoMode.TopBottom, depending
            // upon which type of content you are rendering: monoscopic content, side-by-side stereo
            // content, or top-bottom stereo content
            stereoMode = StereoMode.SideBySide,
        ) {
            val exoPlayer = remember { ExoPlayer.Builder(context).build() }
            val videoUri = Uri.Builder()
                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
                // Represents a side-by-side stereo video, where each frame contains a pair of
                // video frames arranged side-by-side. The frame on the left represents the left
                // eye view, and the frame on the right represents the right eye view.
                .path("sbs_video.mp4")
                .build()
            val mediaItem = MediaItem.fromUri(videoUri)

            // onSurfaceCreated is invoked only one time, when the Surface is created
            onSurfaceCreated { surface ->
                exoPlayer.setVideoSurface(surface)
                exoPlayer.setMediaItem(mediaItem)
                exoPlayer.prepare()
                exoPlayer.play()
            }
            // onSurfaceDestroyed is invoked when the SpatialExternalSurface composable and its
            // associated Surface are destroyed
            onSurfaceDestroyed { exoPlayer.release() }
        }
    }
}

Points clés concernant le code

  • Définissez StereoMode sur Mono, SideBySide ou TopBottom en fonction du type de contenu que vous affichez :
    • Mono : l'image ou l'image vidéo se compose d'une seule image identique affichée pour les deux yeux.
    • SideBySide : l'image ou l'image vidéo contient une paire d'images ou d'images vidéo disposées côte à côte, où l'image ou l'image vidéo de gauche représente la vue de l'œil gauche, et l'image ou l'image vidéo de droite représente la vue de l'œil droit.
    • TopBottom : l'image ou l'image vidéo contient une paire d'images ou d'images vidéo empilées verticalement, où l'image ou l'image vidéo du haut représente la vue de l'œil gauche, et l'image ou l'image vidéo du bas représente la vue de l'œil droit.
  • SpatialExternalSurface n'est compatible qu'avec les surfaces rectangulaires.
  • Ce Surface ne capture pas les événements d'entrée.
  • Il n'est pas possible de synchroniser les modifications de StereoMode avec l'affichage de l'application ou le décodage vidéo.
  • Ce composable ne peut pas s'afficher devant d'autres panneaux. Vous ne devez donc pas utiliser de MovePolicy si d'autres panneaux sont présents dans la mise en page.

Ajouter une surface pour le contenu vidéo protégé par DRM

SpatialExternalSurface est également compatible avec la lecture de flux vidéo protégés par DRM. Pour ce faire, vous devez créer une surface sécurisée qui s'affiche dans des tampons graphiques protégés. Cela empêche l'enregistrement d'écran du contenu ou l'accès à celui-ci par des composants système non sécurisés.

Pour créer une surface sécurisée, définissez le paramètre SpatialExternalSurfaceProtection sur SpatialExternalSurfaceProtection.Protected sur le composable SpatialExternalSurface. De plus, vous devez configurer Media3 ExoPlayer avec les informations DRM appropriées pour gérer l'acquisition de la licence auprès d'un serveur de licences.

L'exemple suivant montre comment configurer SpatialExternalSurface et ExoPlayer pour lire un flux vidéo protégé par DRM :

@OptIn(ExperimentalComposeApi::class)
@Composable
fun DrmSpatialVideoPlayer() {
    val context = LocalContext.current
    Subspace {
        SpatialExternalSurface(
            modifier = SubspaceModifier
                .width(1200.dp)
                .height(676.dp),
            stereoMode = StereoMode.SideBySide,
            surfaceProtection = SpatialExternalSurfaceProtection.Protected
        ) {
            val exoPlayer = remember { ExoPlayer.Builder(context).build() }

            // Define the URI for your DRM-protected content and license server.
            val videoUri = "https://your-content-provider.com/video.mpd"
            val drmLicenseUrl = "https://your-license-server.com/license"

            // Build a MediaItem with the necessary DRM configuration.
            val mediaItem = MediaItem.Builder()
                .setUri(videoUri)
                .setDrmConfiguration(
                    MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID)
                        .setLicenseUri(drmLicenseUrl)
                        .build()
                )
                .build()

            onSurfaceCreated { surface ->
                // The created surface is secure and can be used by the player.
                exoPlayer.setVideoSurface(surface)
                exoPlayer.setMediaItem(mediaItem)
                exoPlayer.prepare()
                exoPlayer.play()
            }

            onSurfaceDestroyed { exoPlayer.release() }
        }
    }
}

Points clés concernant le code

  • Surface protégée : il est essentiel de définir surfaceProtection = SpatialExternalSurfaceProtection.Protected sur SpatialExternalSurface pour que le Surface sous-jacent soit sauvegardé par des tampons sécurisés adaptés au contenu DRM.
  • Configuration DRM : vous devez configurer MediaItem avec le schéma DRM (par exemple, C.WIDEVINE_UUID) et l'URI de votre serveur de licences. ExoPlayer utilise ces informations pour gérer la session DRM.
  • Contenu sécurisé : lors de l'affichage sur une surface protégée, le contenu vidéo est décodé et affiché sur un chemin sécurisé, ce qui permet de répondre aux exigences de licence de contenu. Cela empêche également le contenu d'apparaître dans les captures d'écran.