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
StereoModesurMono,SideBySideouTopBottomen 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.
SpatialExternalSurfacen'est compatible qu'avec les surfaces rectangulaires.- Ce
Surfacene capture pas les événements d'entrée. - Il n'est pas possible de synchroniser les modifications de
StereoModeavec 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
MovePolicysi 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.ProtectedsurSpatialExternalSurfacepour que leSurfacesous-jacent soit sauvegardé par des tampons sécurisés adaptés au contenu DRM. - Configuration DRM : vous devez configurer
MediaItemavec 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.