Ihrer App ein räumliches Video hinzufügen

Das Jetpack XR SDK unterstützt die Wiedergabe von stereoskopischen Nebeneinander-Videos auf flachen Oberflächen. Bei stereoskopischen Videos besteht jedes Frame aus einem Bild für das linke und einem Bild für das rechte Auge, um den Zuschauern ein Gefühl von Tiefe zu vermitteln. Dieses Phänomen wird auch als Stereopsis bezeichnet.

Sie können nicht stereoskopische 2D-Videos in Android XR-Apps mit den Standard-Media-APIs rendern, die für die Android-Entwicklung auf anderen Formfaktoren verwendet werden.

Nebeneinander-Video mit Jetpack SceneCore abspielen

Bei der Side-by-Side-Darstellung wird jedes stereoskopische Frame als zwei Bilder dargestellt, die horizontal nebeneinander angeordnet sind. Die oberen und unteren Videoframes werden vertikal nebeneinander angeordnet.

Das Side-by-Side-Video ist kein Codec, sondern eine Methode zum Organisieren stereoskopischer Frames. Es kann also mit einem der von Android unterstützten Codecs codiert werden.

Sie können Side-by-Side-Videos mit Media3 ExoPlayer laden und dann mit dem neuen SurfaceEntity rendern. Rufen Sie zum Erstellen eines SurfaceEntity SurfaceEntity.create auf, wie im folgenden Beispiel gezeigt.

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

MV-HEVC-Video mit Jetpack SceneCore abspielen

Der MV-HEVC-Codec-Standard ist für stereoskopische Videos optimiert und konzipiert. So kann Ihre App immersive Videos in hoher Qualität effizient wiedergeben. MV-HEVC-Dateien haben einen primären Stream, in der Regel das linke Auge, und einen Stereostream mit dem anderen Auge.

Ähnlich wie bei Side-by-Side-Videos können Sie es mit Media3 Exoplayer laden und mit dem SurfaceEntity rendern. Sie müssen im Parameter stereoMode angeben, ob Ihre MV-HEVC-Datei links oder rechts primär ist, wenn Sie SurfaceEntity.create aufrufen.

// Create the SurfaceEntity with the StereoMode corresponding to the MV-HEVC content
val stereoSurfaceEntity = SurfaceEntity.create(
    xrSession,
    SurfaceEntity.StereoMode.MULTIVIEW_LEFT_PRIMARY,
    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("mvhevc_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()

DRM-geschützte räumliche Videos mit Jetpack SceneCore abspielen

Das Jetpack XR SDK unterstützt die Wiedergabe verschlüsselter Videostreams mit dem integrierten DRM-Framework (Digital Rights Management) von Android. DRM schützt Ihre Inhalte durch eine sichere Verteilung und verhindert unautorisiertes Kopieren oder Abspielen.

Dabei kontaktiert Ihre Mediaplayer-Anwendung einen Lizenzserver, um Entschlüsselungsschlüssel zu erhalten. Unter Android wird dieser Prozess sicher verwaltet und die entschlüsselten Videoframes werden in einem geschützten Grafikpuffer gerendert, auf den das System oder andere Anwendungen nicht zugreifen können. So wird die Bildschirmaufnahme verhindert.

Wenn Sie DRM-geschützte Videos mit Jetpack SceneCore abspielen möchten, müssen Sie Folgendes tun:

  1. Konfigurieren Sie SurfaceEntity, um eine geschützte Oberfläche anzufordern.
  2. Konfiguriere Media3 ExoPlayer mit den erforderlichen DRM-Informationen, um den Schlüsselaustausch zu verarbeiten.
  3. Leite die Ausgabe des Players an die Oberfläche des SurfaceEntity weiter.

Das folgende Beispiel zeigt, wie Sie ExoPlayer so konfigurieren, dass ein DRM-geschützter Stream wiedergegeben und auf einem SurfaceEntity gerendert wird:

// Create a SurfaceEntity with DRM content

// 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"

// Create the SurfaceEntity with the PROTECTED content security level.
val protectedSurfaceEntity = SurfaceEntity.create(
    session = xrSession,
    stereoMode = SurfaceEntity.StereoMode.SIDE_BY_SIDE,
    pose = Pose(Vector3(0.0f, 0.0f, -1.5f)),
    canvasShape = SurfaceEntity.CanvasShape.Quad(1.0f, 1.0f),
    contentSecurityLevel = SurfaceEntity.ContentSecurityLevel.PROTECTED
)

// 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()

// Initialize ExoPlayer and set the protected surface.
val exoPlayer = ExoPlayer.Builder(this).build()
exoPlayer.setVideoSurface(protectedSurfaceEntity.getSurface())

// Set the media item and start playback.
exoPlayer.setMediaItem(mediaItem)
exoPlayer.prepare()
exoPlayer.play()

Einen detaillierteren Überblick über das Media-DRM-Framework von Android finden Sie in der Media-DRM-Dokumentation auf source.android.com.

180°- und 360°-Videos mit Jetpack SceneCore abspielen

SurfaceEntity unterstützt die Wiedergabe von 180°-Videos auf halbkugelförmigen Oberflächen und von 360°-Videos auf kugelförmigen Oberflächen. Der Parameter radius bezieht sich standardmäßig auf die radiale Größe der jeweiligen Oberflächen in Metern.

Der folgende Code zeigt, wie SurfaceEntity für die Wiedergabe auf einer 180°-Halbkugel und einer 360°-Kugel eingerichtet wird. Wenn Sie diese Canvas-Formen verwenden, positionieren Sie die Oberfläche mithilfe der Kopfhaltung des Nutzers, um ein immersives Erlebnis zu schaffen.

// 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.

Erweiterte Steuerung von SurfaceEntity

Wenn Sie die Darstellung von Videos und Bildern genauer steuern möchten, z. B. um benutzerdefinierte Materialeffekte anzuwenden, können Sie direkt mit SurfaceEntity aus der SceneCore-Bibliothek arbeiten.

In den folgenden Abschnitten werden einige der erweiterten Funktionen beschrieben, die auf SurfaceEntity verfügbar sind.

Weichzeichnen der Kanten anwenden

Weichen Sie die Kanten der Oberfläche auf, damit sie sich besser in die Umgebung einfügt. Dazu können Sie die Eigenschaft edgeFeather festlegen.

// Create a SurfaceEntity.
val surfaceEntity = SurfaceEntity.create(
    session = xrSession,
    pose = Pose(Vector3(0.0f, 0.0f, -1.5f))
)

// Feather the edges of the surface.
surfaceEntity.edgeFeather =
    SurfaceEntity.EdgeFeatheringParams.SmoothFeather(0.1f, 0.1f)

Alphamasken anwenden

Mit einer Alphamaskierung können Sie nicht rechteckige Oberflächen erstellen oder Transparenzeffekte hinzufügen. Laden Sie zuerst ein Texture aus einem Asset und weisen Sie es dann dem Attribut primaryAlphaMaskTexture zu:

// Create a SurfaceEntity.
val surfaceEntity = SurfaceEntity.create(
    session = xrSession,
    pose = Pose(Vector3(0.0f, 0.0f, -1.5f))
)

// Load the texture in a coroutine scope.
activity.lifecycleScope.launch {
    val alphaMaskTexture =
        Texture.create(
            xrSession,
            Paths.get("textures", "alpha_mask.png"),
            TextureSampler.create()
        )

    // Apply the alpha mask.
    surfaceEntity.primaryAlphaMaskTexture = alphaMaskTexture

    // To remove the mask, set the property to null.
    surfaceEntity.primaryAlphaMaskTexture = null
}

Räumliche Videos mit Jetpack Compose für XR abspielen

Wenn Sie wissen möchten, wie Sie Videos mit Jetpack Compose für XR abspielen, fügen Sie eine Oberfläche für Bild- oder Videoinhalte hinzu.