空間外部介面

適用的 XR 裝置
本指南可協助您為這類 XR 裝置打造體驗。
XR 頭戴式裝置
有線 XR 眼鏡

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 保護的影片內容新增途徑

SpatialExternalSurface 也支援播放受 DRM 保護的影片串流。如要啟用這項功能,您必須建立安全途徑,轉譯為受保護的圖形緩衝區。這樣可防止內容遭到螢幕錄影,或由不安全的系統元件存取。

如要建立安全途徑,請將 SpatialExternalSurface 可組合項目的 SpatialExternalSurfaceProtection 參數設為 SpatialExternalSurfaceProtection.Protected。此外,您必須使用適當的 DRM 資訊設定 Media3 Exoplayer,以便處理從授權伺服器取得授權。

以下範例說明如何設定 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 工作階段。
  • 安全內容:在受保護的介面上算繪時,影片內容會解碼並顯示在安全路徑上,有助於滿足內容授權需求。這也能防止內容出現在螢幕擷取畫面中。