Un SpatialExternalSurface è un componibile di sottospazio che crea e gestisce il Surface in cui la tua app può disegnare contenuti, ad esempio un'immagine o un video. SpatialExternalSurface supporta contenuti stereoscopici o monoscopici.
Aggiungi una piattaforma per i contenuti video o immagine
Questo esempio mostra come caricare un video stereoscopico affiancato utilizzando Media3 Exoplayer e 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() } } } }
Punti chiave sul codice
- Imposta
StereoModesuMono,SideBySideoTopBottoma seconda del tipo di contenuti di cui esegui il rendering:Mono: il frame dell'immagine o del video è costituito da una singola immagine identica mostrata a entrambi gli occhi.SideBySide: l'immagine o il fotogramma video contiene una coppia di immagini o fotogrammi video disposti uno accanto all'altro, dove l'immagine o il fotogramma a sinistra rappresenta la visione dell'occhio sinistro e l'immagine o il fotogramma a destra rappresenta la visione dell'occhio destro.TopBottom: l'immagine o il fotogramma del video contiene una coppia di immagini o fotogrammi video impilati verticalmente, in cui l'immagine o il fotogramma in alto rappresenta la visuale dell'occhio sinistro e l'immagine o il fotogramma in basso rappresenta la visuale dell'occhio destro.
SpatialExternalSurfacesupporta solo superfici rettangolari.- Questo
Surfacenon acquisisce gli eventi di input. - Non è possibile sincronizzare le modifiche di
StereoModecon il rendering dell'applicazione o la decodifica del video. - Questo composable non può essere visualizzato davanti ad altri riquadri, quindi non devi utilizzare un
MovePolicyse nel layout sono presenti altri riquadri.
Aggiungi una piattaforma per i contenuti video protetti da DRM
SpatialExternalSurface supporta anche la riproduzione di stream video protetti da DRM. Per abilitare questa funzionalità, devi creare una superficie sicura che esegua il rendering in buffer grafici protetti. In questo modo, i contenuti non possono essere registrati sullo schermo o accessibili da componenti di sistema non sicuri.
Per creare una superficie sicura, imposta il parametro SpatialExternalSurfaceProtection su SpatialExternalSurfaceProtection.Protected nel componibile SpatialExternalSurface. Inoltre, devi configurare Media3 Exoplayer con le informazioni DRM appropriate per gestire l'acquisizione della licenza da un server licenze.
L'esempio seguente mostra come configurare SpatialExternalSurface e ExoPlayer per riprodurre uno stream video protetto da 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() } } } }
Punti chiave sul codice
- Protected Surface: l'impostazione di
surfaceProtection = SpatialExternalSurfaceProtection.ProtectedsuSpatialExternalSurfaceè essenziale per garantire che l'elementoSurfacesottostante sia supportato da buffer sicuri adatti ai contenuti DRM. - Configurazione DRM: devi configurare
MediaItemcon lo schema DRM (ad esempioC.WIDEVINE_UUID) e l'URI del tuo server licenze. ExoPlayer utilizza queste informazioni per gestire la sessione DRM. - Contenuti protetti: durante il rendering su una superficie protetta, i contenuti video vengono decodificati e visualizzati su un percorso sicuro, il che aiuta a soddisfare i requisiti di licenza per i contenuti. In questo modo, i contenuti non vengono visualizzati negli screenshot.