พื้นผิวภายนอกเชิงพื้นที่

อุปกรณ์ XR ที่รองรับ
คำแนะนำนี้จะช่วยคุณสร้างประสบการณ์การใช้งานสำหรับอุปกรณ์ XR ประเภทนี้
ชุดหูฟัง XR
แว่นตา XR แบบใช้สาย

SpatialExternalSurface คือคอมโพสิเบิลของ Subspace ที่สร้างและจัดการ 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: เฟรมรูปภาพหรือเฟรมวิดีโอประกอบด้วยรูปภาพเดียวที่เหมือนกันซึ่งแสดงต่อตาทั้ง 2 ข้าง
    • SideBySide: รูปภาพหรือเฟรมวิดีโอมีรูปภาพหรือเฟรมวิดีโอคู่หนึ่งจัดเรียงเคียงข้างกัน โดยรูปภาพหรือเฟรมทางด้านซ้ายแสดงมุมมองตาซ้าย และรูปภาพหรือเฟรมทางด้านขวาแสดงมุมมองตาขวา
    • TopBottom: รูปภาพหรือเฟรมวิดีโอมีรูปภาพหรือเฟรมวิดีโอคู่หนึ่งซ้อนกันในแนวตั้ง โดยรูปภาพหรือเฟรมด้านบนแสดงมุมมองตาซ้าย และรูปภาพหรือเฟรมด้านล่างแสดงมุมมองตาขวา
  • SpatialExternalSurface รองรับเฉพาะพื้นผิวสี่เหลี่ยมผืนผ้าเท่านั้น
  • Surface นี้ไม่บันทึกเหตุการณ์การป้อนข้อมูล
  • ไม่สามารถซิงค์การเปลี่ยนแปลง StereoMode กับการแสดงผลแอปพลิเคชันหรือการถอดรหัสวิดีโอ
  • คอมโพสิเบิลนี้ไม่สามารถแสดงผลที่ด้านหน้าของแผงอื่นๆ ดังนั้นคุณจึงไม่ควรใช้ MovePolicy หากมีแผงอื่นๆ ในเลย์เอาต์

เพิ่มแพลตฟอร์มสำหรับเนื้อหาวิดีโอที่ได้รับการคุ้มครองโดย DRM

SpatialExternalSurface ยังรองรับการเล่นสตรีมวิดีโอที่ได้รับการคุ้มครองด้วย DRM ด้วย หากต้องการเปิดใช้ฟีเจอร์นี้ คุณต้องสร้างแพลตฟอร์มที่ปลอดภัยซึ่งแสดงผลไปยังบัฟเฟอร์กราฟิกที่มีการป้องกัน ซึ่งจะป้องกันไม่ให้มีการบันทึกหน้าจอเนื้อหาหรือเข้าถึงเนื้อหาโดยคอมโพเนนต์ของระบบที่ไม่ปลอดภัย

หากต้องการสร้างแพลตฟอร์มที่ปลอดภัย ให้ตั้งค่าพารามิเตอร์ SpatialExternalSurfaceProtection เป็น SpatialExternalSurfaceProtection.Protected ใน SpatialExternalSurface Composable นอกจากนี้ คุณต้องกำหนดค่า 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() }
        }
    }
}

ประเด็นสำคัญเกี่ยวกับโค้ด

  • Protected Surface: การตั้งค่า surfaceProtection = SpatialExternalSurfaceProtection.Protected ใน SpatialExternalSurface เป็นสิ่งสำคัญเพื่อให้ Surface ที่สำคัญได้รับการสนับสนุนโดยบัฟเฟอร์ที่ปลอดภัยซึ่งเหมาะสำหรับเนื้อหา DRM
  • การกำหนดค่า DRM: คุณต้องกำหนดค่า MediaItem ด้วยรูปแบบ DRM (เช่น C.WIDEVINE_UUID) และ URI ของเซิร์ฟเวอร์ใบอนุญาต ExoPlayer จะใช้ข้อมูลนี้เพื่อจัดการเซสชัน DRM
  • เนื้อหาที่ปลอดภัย: เมื่อแสดงผลบนพื้นผิวที่มีการป้องกัน เนื้อหาวิดีโอจะได้รับการถอดรหัสและแสดงในเส้นทางที่ปลอดภัย ซึ่งช่วยให้เป็นไปตามข้อกำหนดของใบอนุญาตการใช้เนื้อหา นอกจากนี้ยังป้องกันไม่ให้เนื้อหาปรากฏในการจับภาพหน้าจอด้วย