Ein SpatialExternalSurface ist ein Subspace-Composable, das die Surface erstellt und verwaltet, in die Ihre App Inhalte wie ein Bild oder ein Video zeichnen kann. SpatialExternalSurface unterstützt entweder stereoskopische oder monoskopische Inhalte.
Oberfläche für Bild- oder Videoinhalte hinzufügen
In diesem Beispiel wird gezeigt, wie Sie ein stereoskopisches Side-by-Side-Video mit Media3 Exoplayer und SpatialExternalSurface laden:
@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() } } } }
Wichtige Punkte zum Code
- Legen Sie
StereoModeje nach Art des zu rendernden Inhalts aufMono,SideBySideoderTopBottomfest:Mono: Das Bild oder der Videoframe besteht aus einem einzelnen, identischen Bild, das für beide Augen angezeigt wird.SideBySide: Das Bild oder der Videoframe enthält ein Paar Bilder oder Videoframes, die nebeneinander angeordnet sind. Das Bild oder der Frame auf der linken Seite stellt die Ansicht für das linke Auge dar, das Bild oder der Frame auf der rechten Seite die Ansicht für das rechte Auge.TopBottom: Das Bild oder der Videoframe enthält ein Paar vertikal gestapelter Bilder oder Videoframes, wobei das Bild oder der Frame oben die Ansicht für das linke Auge und das Bild oder der Frame unten die Ansicht für das rechte Auge darstellt.
SpatialExternalSurfaceunterstützt nur rechteckige Oberflächen.- Dieses
Surfaceerfasst keine Eingabeereignisse. - Es ist nicht möglich,
StereoMode-Änderungen mit dem Anwendungsrendering oder der Videodecodierung zu synchronisieren. - Dieses Compose-Element kann nicht vor anderen Bereichen gerendert werden. Sie sollten also kein
MovePolicyverwenden, wenn das Layout andere Bereiche enthält.
Oberfläche für DRM-geschützte Videoinhalte hinzufügen
SpatialExternalSurface unterstützt auch die Wiedergabe von DRM-geschützten Videostreams. Dazu musst du eine sichere Oberfläche erstellen, die in geschützten Grafikpuffern gerendert wird. Dadurch wird verhindert, dass der Inhalt per Bildschirmaufzeichnung aufgenommen oder von unsicheren Systemkomponenten abgerufen wird.
Wenn Sie eine sichere Oberfläche erstellen möchten, setzen Sie den Parameter SpatialExternalSurfaceProtection in der zusammensetzbaren Funktion SpatialExternalSurface auf SpatialExternalSurfaceProtection.Protected. Außerdem musst du Media3 Exoplayer mit den entsprechenden DRM-Informationen konfigurieren, um den Lizenzerwerb von einem Lizenzserver zu verarbeiten.
Im folgenden Beispiel wird gezeigt, wie Sie SpatialExternalSurface und ExoPlayer für die Wiedergabe eines DRM-geschützten Videostreams konfigurieren:
@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() } } } }
Wichtige Punkte zum Code
- Geschützte Oberfläche: Die Einstellung
surfaceProtection = SpatialExternalSurfaceProtection.ProtectedfürSpatialExternalSurfaceist wichtig, damit die zugrunde liegendeSurfacedurch sichere Puffer unterstützt wird, die für DRM-Inhalte geeignet sind. - DRM-Konfiguration: Du musst die
MediaItemmit dem DRM-Schema (z. B.C.WIDEVINE_UUID) und dem URI deines Lizenzservers konfigurieren. ExoPlayer verwendet diese Informationen, um die DRM-Sitzung zu verwalten. - Sicherer Inhalt: Beim Rendern auf einer geschützten Oberfläche wird der Videoinhalt decodiert und auf einem sicheren Pfad angezeigt, was zur Erfüllung der Anforderungen an die Inhaltslizenzierung beiträgt. Dadurch wird auch verhindert, dass der Inhalt in Bildschirmaufnahmen angezeigt wird.