SpatialExternalSurface 是一个子空间可组合项,用于创建和管理 Surface,您的应用可以在其中绘制内容,例如图片或视频。SpatialExternalSurface 支持立体或单视场内容。
添加用于展示图片或视频内容的平台
以下示例演示了如何使用 Media3 Exoplayer 和 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() } } } }
代码要点
- 将
StereoMode设置为Mono、SideBySide或TopBottom,具体取决于您要呈现的内容类型:Mono:图片或视频帧由向双眼显示的单个相同图片组成。SideBySide:图片或视频帧包含一对并排排列的图片或视频帧,其中左侧的图片或帧代表左眼视图,右侧的图片或帧代表右眼视图。TopBottom:图片或视频帧包含一对垂直堆叠的图片或视频帧,其中顶部的图片或帧表示左眼视图,底部的图片或帧表示右眼视图。
SpatialExternalSurface仅支持矩形表面。- 此
Surface不会捕获输入事件。 - 无法将
StereoMode更改与应用渲染或视频解码同步。 - 此可组合项无法在其他面板前面呈现,因此如果布局中还有其他面板,您不应使用
MovePolicy。
为受 DRM 保护的视频内容添加 Surface
SpatialExternalSurface 还支持播放受 DRM 保护的视频流。如需实现此目的,您必须创建一个可渲染到受保护图形缓冲区的安全 Surface。这样可以防止内容被录屏或被不安全的系统组件访问。
如需创建安全 Surface,请在 SpatialExternalSurface 可组合项上将 SpatialExternalSurfaceProtection 参数设置为 SpatialExternalSurfaceProtection.Protected。此外,您还必须为 Media3 Exoplayer 配置适当的 DRM 信息,以便处理从许可服务器获取许可的操作。
以下示例演示了如何配置 SpatialExternalSurface 和 ExoPlayer 以播放受 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() } } } }
代码要点
- 受保护的 Surface:在
SpatialExternalSurface上设置surfaceProtection = SpatialExternalSurfaceProtection.Protected至关重要,这样底层Surface才能由适合 DRM 内容的安全缓冲区提供支持。 - DRM 配置:您必须使用 DRM 方案(例如
C.WIDEVINE_UUID)和许可服务器的 URI 配置MediaItem。ExoPlayer 会使用这些信息来管理 DRM 会话。 - 安全内容:在渲染到受保护的 Surface 时,视频内容会在安全路径上进行解码和显示,这有助于满足内容许可要求。这还可以防止内容出现在屏幕截图中。