SpatialExternalSurface to funkcja kompozycyjna podprzestrzeni, która tworzy Surface i nim zarządza. Twoja aplikacja może w nim rysować treści, takie jak obraz czy film. SpatialExternalSurface obsługuje treści stereoskopowe i monoskopowe.
Dodawanie powierzchni na obrazy lub treści wideo
Ten przykład pokazuje, jak wczytać stereoskopowy film side-by-side za pomocą Media3 Exoplayer i 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() } } } }
Najważniejsze informacje o kodzie
- Ustaw
StereoModenaMono,SideBySidelubTopBottomw zależności od tego, jaki typ treści renderujesz:Mono: klatka obrazu lub filmu składa się z jednego, identycznego obrazu wyświetlanego dla obu oczu.SideBySide: obraz lub klatka wideo zawiera parę obrazów lub klatek wideo ułożonych obok siebie, przy czym obraz lub klatka po lewej stronie przedstawia widok dla lewego oka, a obraz lub klatka po prawej stronie przedstawia widok dla prawego oka.TopBottom: obraz lub klatka filmu zawiera parę obrazów lub klatek filmu ułożonych pionowo, przy czym obraz lub klatka na górze przedstawia widok dla lewego oka, a obraz lub klatka na dole przedstawia widok dla prawego oka.
SpatialExternalSurfaceobsługuje tylko powierzchnie prostokątne.- Ten element
Surfacenie rejestruje zdarzeń wejściowych. - Nie można zsynchronizować zmian
StereoModez renderowaniem aplikacji ani dekodowaniem wideo. - Tego elementu kompozycyjnego nie można renderować przed innymi panelami, więc nie należy używać elementu
MovePolicy, jeśli w układzie są inne panele.
Dodawanie platformy na potrzeby treści wideo chronionych DRM
SpatialExternalSurface obsługuje też odtwarzanie strumieni wideo chronionych przez DRM. Aby to umożliwić, musisz utworzyć bezpieczną powierzchnię, która renderuje do chronionych buforów graficznych. Zapobiega to nagrywaniu treści na ekranie i uzyskiwaniu do nich dostępu przez niezabezpieczone komponenty systemu.
Aby utworzyć bezpieczną platformę, ustaw parametr SpatialExternalSurfaceProtection na SpatialExternalSurfaceProtection.Protected w funkcji kompozycyjnej SpatialExternalSurface. Dodatkowo musisz skonfigurować Media3 Exoplayer, podając odpowiednie informacje DRM, aby umożliwić pobieranie licencji z serwera licencji.
Poniższy przykład pokazuje, jak skonfigurować SpatialExternalSurface i ExoPlayer, aby odtwarzać strumień wideo chroniony przez 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() } } } }
Najważniejsze informacje o kodzie
- Protected Surface: ustawienie
surfaceProtection = SpatialExternalSurfaceProtection.ProtectednaSpatialExternalSurfacejest niezbędne, aby podstawowySurfacebył obsługiwany przez bezpieczne buforowanie odpowiednie dla treści DRM. - Konfiguracja DRM: musisz skonfigurować
MediaItemza pomocą schematu DRM (np.C.WIDEVINE_UUID) i identyfikatora URI serwera licencji. ExoPlayer używa tych informacji do zarządzania sesją DRM. - Bezpieczne treści: podczas renderowania na chronionej powierzchni treści wideo są dekodowane i wyświetlane na bezpiecznej ścieżce, co pomaga spełnić wymagania dotyczące licencji na treści. Zapobiega to też wyświetlaniu treści na zrzutach ekranu.