공간 외부 표면

적용 가능한 XR 기기
이 가이드는 이러한 유형의 XR 기기를 위한 환경을 빌드하는 데 도움이 됩니다.
XR 헤드셋
유선 XR 글라스

SpatialExternalSurface는 앱이 이미지나 동영상과 같은 콘텐츠를 그릴 수 있는 Surface를 만들고 관리하는 Subspace 컴포저블입니다. 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를 구성해야 합니다.

다음 예에서는 DRM으로 보호된 동영상 스트림을 재생하도록 SpatialExternalSurface 및 ExoPlayer를 구성하는 방법을 보여줍니다.

@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가 DRM 콘텐츠에 적합한 보안 버퍼로 지원되도록 SpatialExternalSurface에서 surfaceProtection = SpatialExternalSurfaceProtection.Protected를 설정하는 것이 필수적입니다.
  • DRM 구성: DRM 스키마(예: C.WIDEVINE_UUID)와 라이선스 서버의 URI를 사용하여 MediaItem를 구성해야 합니다. ExoPlayer는 이 정보를 사용하여 DRM 세션을 관리합니다.
  • 보안 콘텐츠: 보호된 노출 영역에 렌더링할 때 동영상 콘텐츠는 보안 경로에서 디코딩되고 표시되므로 콘텐츠 라이선스 요구사항을 충족하는 데 도움이 됩니다. 이렇게 하면 콘텐츠가 화면 캡처에 표시되지 않습니다.