앱에 공간 동영상 추가

Jetpack XR SDK는 평평한 표면에 대한 입체 비디오 나란히 재생을 지원합니다. 입체 동영상의 경우 각 프레임은 시청자에게 깊이감을 주기 위해 왼쪽 눈 이미지와 오른쪽 눈 이미지로 구성됩니다.

다른 폼 팩터의 Android 개발에 사용되는 표준 미디어 API를 사용하여 Android XR 앱에서 비입체 2D 동영상을 렌더링할 수 있습니다.

Jetpack XR SDK를 사용하여 동영상 나란히 재생

나란히 표시된 동영상에서는 각 입체 프레임이 서로 나란히 가로로 배열된 두 이미지로 표시됩니다. 상단 및 하단 동영상 프레임이 서로 나란히 세로로 정렬됩니다.

나란히 표시 동영상은 코덱이 아니라 입체 프레임을 구성하는 방법입니다. 즉, Android에서 지원하는 모든 코덱으로 인코딩할 수 있습니다.

Jetpack SceneCore

Media3 Exoplayer를 사용하여 나란히 표시되는 동영상을 로드한 다음 새 SurfaceEntity를 사용하여 렌더링할 수 있습니다. SurfaceEntity를 만들려면 다음 예와 같이 SurfaceEntity.create를 호출합니다.

val stereoSurfaceEntity = SurfaceEntity.create(
    xrSession,
    SurfaceEntity.StereoMode.SIDE_BY_SIDE,
    Pose(Vector3(0.0f, 0.0f, -1.5f)),
    SurfaceEntity.CanvasShape.Quad(1.0f, 1.0f)
)
val videoUri = Uri.Builder()
    .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
    .path("sbs_video.mp4")
    .build()
val mediaItem = MediaItem.fromUri(videoUri)

val exoPlayer = ExoPlayer.Builder(this).build()
exoPlayer.setVideoSurface(stereoSurfaceEntity.getSurface())
exoPlayer.setMediaItem(mediaItem)
exoPlayer.prepare()
exoPlayer.play()

XR용 Jetpack Compose

Alpha04 이상

버전 1.0.0-alpha04부터 XR용 Jetpack Compose는 나란히 동영상을 로드하고 렌더링하는 또 다른 방법을 제공합니다. 앱이 이미지나 동영상과 같은 콘텐츠를 그릴 수 있는 Surface를 만들고 관리하는 하위 공간 컴포저블인 SpatialExternalSurface를 사용하세요. SpatialExternalSurface에 관한 자세한 내용은 XR용 Compose로 UI 개발 가이드를 참고하세요.

이 예에서는 Media3 ExoplayerSpatialExternalSurface를 사용하여 나란히 표시되는 동영상을 로드하는 방법을 보여줍니다.

@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() }
        }
    }
}

Jetpack XR SDK를 사용하여 180도 및 360도 동영상 재생

SurfaceEntity는 반구형 표면에서 180도 동영상을 재생하고 구형 표면에서 360도 동영상을 재생합니다. 동영상이 입체인 경우 파일은 나란히 표시되는 형식이어야 합니다.

다음 코드는 180도 반구와 360도 구체에서 재생할 수 있도록 SurfaceEntity를 설정하는 방법을 보여줍니다. 이러한 캔버스 도형을 사용할 때는 사용자의 머리 자세를 활용하여 노출 영역을 배치하여 몰입도 높은 환경을 제공합니다.

// Set up the surface for playing a 180° video on a hemisphere.
val hemisphereStereoSurfaceEntity =
    SurfaceEntity.create(
        xrSession,
        SurfaceEntity.StereoMode.SIDE_BY_SIDE,
        xrSession.scene.spatialUser.head?.transformPoseTo(
            Pose.Identity,
            xrSession.scene.activitySpace
        )!!,
        SurfaceEntity.CanvasShape.Vr180Hemisphere(1.0f),
    )
// ... and use the surface for playing the media.

// Set up the surface for playing a 360° video on a sphere.
val sphereStereoSurfaceEntity =
    SurfaceEntity.create(
        xrSession,
        SurfaceEntity.StereoMode.TOP_BOTTOM,
        xrSession.scene.spatialUser.head?.transformPoseTo(
            Pose.Identity,
            xrSession.scene.activitySpace
        )!!,
        SurfaceEntity.CanvasShape.Vr360Sphere(1.0f),
    )
// ... and use the surface for playing the media.