Un SpatialExternalSurface es un elemento componible de subespacio que crea y administra el Surface en el que tu app puede dibujar contenido, como una imagen o un video. SpatialExternalSurface admite contenido estereoscópico o monoscópico.
Agrega una superficie para el contenido de imagen o video
En este ejemplo, se muestra cómo cargar un video estereoscópico en paralelo con Media3 Exoplayer y 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() } } } }
Puntos clave sobre el código
- Establece
StereoModecomoMono,SideBySideoTopBottomsegún el tipo de contenido que renderices:Mono: La imagen o el fotograma consta de una sola imagen idéntica que se muestra en ambos ojos.SideBySide: La imagen o el fotograma contiene un par de imágenes o fotogramas dispuestos uno al lado del otro, en los que la imagen o el fotograma de la izquierda representa la vista del ojo izquierdo, y la imagen o el fotograma de la derecha representa la vista del ojo derecho.TopBottom: La imagen o el fotograma contiene un par de imágenes o fotogramas de video apilados verticalmente, en los que la imagen o el fotograma de la parte superior representan la vista del ojo izquierdo, y la imagen o el fotograma de la parte inferior representan la vista del ojo derecho.
SpatialExternalSurfacesolo admite superficies rectangulares.- Este
Surfaceno captura eventos de entrada. - No es posible sincronizar los cambios de
StereoModecon la renderización de la aplicación ni la decodificación de video. - Este elemento componible no se puede renderizar delante de otros paneles, por lo que no debes usar un
MovePolicysi hay otros paneles en el diseño.
Agrega una plataforma para el contenido de video protegido por DRM
SpatialExternalSurface también admite la reproducción de transmisiones de video por Internet protegidas por DRM. Para habilitar esto, debes crear una superficie segura que se renderice en búferes gráficos protegidos. Esto evita que el contenido se grabe en pantalla o que los componentes del sistema no seguros accedan a él.
Para crear una superficie segura, establece el parámetro SpatialExternalSurfaceProtection en SpatialExternalSurfaceProtection.Protected en el elemento componible SpatialExternalSurface. Además, debes configurar
Media3 ExoPlayer con la información de DRM adecuada para controlar la adquisición de licencias
desde un servidor de licencias.
En el siguiente ejemplo, se muestra cómo configurar SpatialExternalSurface y ExoPlayer para reproducir una transmisión de video por Internet protegida por 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() } } } }
Puntos clave sobre el código
- Superficie protegida: Configurar
surfaceProtection = SpatialExternalSurfaceProtection.ProtectedenSpatialExternalSurfacees esencial para que elSurfacesubyacente esté respaldado por búferes seguros adecuados para el contenido de DRM. - Configuración de DRM: Debes configurar el
MediaItemcon el esquema de DRM (por ejemplo,C.WIDEVINE_UUID) y el URI de tu servidor de licencias. ExoPlayer usa esta información para administrar la sesión de DRM. - Contenido seguro: Cuando se renderiza en una plataforma protegida, el contenido de video se decodifica y se muestra en una ruta de acceso segura, lo que ayuda a cumplir con los requisitos de licencias de contenido. Esto también evita que el contenido aparezca en las capturas de pantalla.