פלטפורמות חיצוניות של Meridian

מכשירי XR רלוונטיים
ההנחיות האלה יעזרו לכם ליצור חוויות למכשירי XR מהסוגים הבאים.
משקפי XR
משקפי XR עם חיבור חוטי

SpatialExternalSurface הוא רכיב Composable של מרחב משנה שיוצר ומנהל את 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. כדי להפעיל את זה, צריך ליצור משטח מאובטח שמעבד למאגרי גרפיקה מוגנים. כך אפשר למנוע הקלטת מסך של התוכן או גישה אליו על ידי רכיבי מערכת לא מאובטחים.

כדי ליצור משטח מאובטח, מגדירים את הפרמטר SpatialExternalSurfaceProtection לערך SpatialExternalSurfaceProtection.Protected ב-composable‏ SpatialExternalSurface. בנוסף, צריך להגדיר את 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: Setting surfaceProtection = SpatialExternalSurfaceProtection.Protected on SpatialExternalSurface is essential so that the underlying Surface is backed by secure buffers suitable for DRM content.
  • הגדרת DRM: צריך להגדיר את MediaItem עם סכימת ה-DRM (לדוגמה, C.WIDEVINE_UUID) ואת ה-URI של שרת הרישיונות. ‫ExoPlayer משתמש במידע הזה כדי לנהל את סשן ה-DRM.
  • תוכן מאובטח: כשמעבדים קטע וידאו למשטח מוגן, התוכן מפוענח ומוצג בנתיב מאובטח, מה שעוזר לעמוד בדרישות רישיון לתוכן. כך גם התוכן לא יופיע בצילומי מסך.