Das Jetpack XR SDK unterstützt die Wiedergabe von stereoskopischen Side-by-Side Videos auf flachen Oberflächen. Bei stereoskopischen Videos besteht jeder 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.
Side-by-Side-Videos mit Jetpack SceneCore abspielen
Bei Side-by-Side-Videos wird jeder stereoskopische Frame als zwei Bilder dargestellt, die horizontal nebeneinander angeordnet sind. Die Video-Frames für oben und unten sind vertikal nebeneinander angeordnet.
Side-by-Side-Videos sind kein Codec, sondern eine Möglichkeit, stereoskopische Frames zu organisieren. Sie können also mit jedem der von Android unterstützten Codecs codiert werden.
Sie können Side-by-Side-Videos mit Media3 ExoPlayer laden und dann
mit der neuen SurfaceEntity rendern. Rufen Sie
SurfaceEntity.create auf, um eine SurfaceEntity zu erstellen, wie im folgenden Beispiel gezeigt.
val stereoSurfaceEntity = SurfaceEntity.create( session = xrSession, stereoMode = SurfaceEntity.StereoMode.SIDE_BY_SIDE, pose = Pose(Vector3(0.0f, 0.0f, -1.5f)), shape = SurfaceEntity.Shape.Quad(FloatSize2d(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-Videos mit Jetpack SceneCore abspielen
Der MV-HEVC-Codec-Standard ist für stereoskopische Videos optimiert und konzipiert. So kann Ihre App immersive Videos effizient und in hoher Qualität wiedergeben. MV-HEVC-Dateien haben einen primären Stream, in der Regel für das linke Auge, und einen Stereostream für das andere Auge.
Ähnlich wie bei Side-by-Side-Videos können Sie sie mit Media3 ExoPlayer laden und
mit der SurfaceEntity rendern. Sie müssen angeben, ob Ihre MV-HEVC-Datei im Parameter stereoMode 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( session = xrSession, stereoMode = SurfaceEntity.StereoMode.MULTIVIEW_LEFT_PRIMARY, pose = Pose(Vector3(0.0f, 0.0f, -1.5f)), shape = SurfaceEntity.Shape.Quad(FloatSize2d(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 sichere Verteilung und verhindert unbefugtes Kopieren oder Abspielen.
Dabei kontaktiert Ihre Mediaplayer-Anwendung einen Lizenzserver, um Entschlüsselungsschlüssel zu erhalten. Unter Android wird dieser Vorgang sicher verwaltet und die entschlüsselten Video-Frames werden in einem geschützten Grafikpuffer gerendert, auf den das System oder andere Anwendungen nicht zugreifen können. So wird die Bildschirmaufnahme verhindert.
So spielen Sie DRM-geschützte Videos mit Jetpack SceneCore ab:
- Konfigurieren Sie
SurfaceEntity, um eine geschützte Oberfläche anzufordern. - Konfigurieren Sie Media3 ExoPlayer mit den erforderlichen DRM-Informationen, um den Schlüsselaustausch zu verarbeiten.
- Legen Sie die Ausgabe des Players auf die
SurfaceEntityOberfläche fest.
Das folgende Beispiel zeigt, wie Sie ExoPlayer so konfigurieren, dass ein DRM-geschützter
Stream wiedergegeben und auf einer 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)), shape = SurfaceEntity.Shape.Quad(FloatSize2d(1.0f, 1.0f)), surfaceProtection = SurfaceEntity.SurfaceProtection.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()
Eine detailliertere Übersicht ü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 Sie SurfaceEntity für die Wiedergabe auf einer 180°-Halbkugel und einer 360°-Kugel einrichten. Wenn Sie diese Canvas-Formen verwenden, positionieren Sie die Oberfläche anhand der Kopfhaltung des Nutzers, um ein immersives Erlebnis zu bieten.
val devicePose = ArDevice.getInstance(xrSession).state.value.devicePose val activitySpacePose = xrSession.scene.perceptionSpace.transformPoseTo(devicePose, xrSession.scene.activitySpace) // Set up the surface for playing a 180° video on a hemisphere. val hemisphereStereoSurfaceEntity = SurfaceEntity.create( session = xrSession, stereoMode = SurfaceEntity.StereoMode.SIDE_BY_SIDE, pose = activitySpacePose, shape = SurfaceEntity.Shape.Hemisphere(1.0f), ) // ... and use the surface for playing the media.
val devicePose = ArDevice.getInstance(xrSession).state.value.devicePose val activitySpacePose = xrSession.scene.perceptionSpace.transformPoseTo(devicePose, xrSession.scene.activitySpace) // Set up the surface for playing a 360° video on a sphere. val sphereStereoSurfaceEntity = SurfaceEntity.create( session = xrSession, stereoMode = SurfaceEntity.StereoMode.TOP_BOTTOM, pose = activitySpacePose, shape = SurfaceEntity.Shape.Sphere(1.0f), ) // ... and use the surface for playing the media.
Erweiterte Steuerung von SurfaceEntity
Für eine erweiterte Steuerung der Video- und Bildwiedergabe, z. B. zum Anwenden benutzerdefinierter Materialeffekte, können Sie direkt mit SurfaceEntity aus der SceneCore-Bibliothek arbeiten.
In den folgenden Abschnitten werden einige der erweiterten Funktionen beschrieben, die für SurfaceEntity verfügbar sind.
Kantenweichzeichnung anwenden
Sie können die Kanten der Oberfläche weicher machen, damit sie besser in die Umgebung passt. Dazu legen Sie die Eigenschaft edgeFeatheringParams fest.
// 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.edgeFeatheringParams = SurfaceEntity.EdgeFeatheringParams.RectangleFeather(0.1f, 0.1f)
Alphamasken anwenden
Sie können Alphamasken anwenden, um nicht rechteckige Oberflächen zu erstellen oder Transparenzeffekte hinzuzufügen. Laden Sie zuerst ein Texture aus einem Asset und weisen Sie es dann der
primaryAlphaMaskTexture Eigenschaft 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"), ) // 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 for XR abspielen
Wenn Sie erfahren möchten, wie Sie Videos mit Jetpack Compose for XR abspielen, lesen Sie den Leitfaden Oberflächen für Bild- oder Videoinhalte hinzufügen.