Ajouter une vidéo spatiale à votre application

Appareils XR concernés
Ces conseils vous aideront à créer des expériences pour ces types d'appareils XR.
Casques XR
Lunettes XR filaires

Le SDK Jetpack XR est compatible avec la lecture de vidéos stéréoscopiques côte à côte vidéo sur des surfaces planes. Dans une vidéo stéréoscopique, chaque image est composée d'une image pour l'œil gauche et d'une image pour l'œil droit afin de donner aux spectateurs une impression de profondeur, également appelée stéréoscopie.

Vous pouvez afficher des vidéos 2D non stéréoscopiques sur des applications Android XR à l'aide des API média standards utilisées pour le développement Android sur d'autres facteurs de forme.

Lire des vidéos côte à côte à l'aide de Jetpack SceneCore

Dans une vidéo côte à côte, chaque image stéréoscopique est présentée sous la forme de deux images disposées horizontalement l'une à côté de l'autre. Les images vidéo de haut en bas sont disposées verticalement l'une à côté de l'autre.

La vidéo côte à côte n'est pas un codec, mais plutôt une façon d'organiser les images stéréoscopiques . Cela signifie qu'elle peut être encodée dans n'importe quel codec compatible avec Android.

Vous pouvez charger des vidéos côte à côte à l'aide de Media3 Exoplayer, puis les afficher à l'aide du nouveau SurfaceEntity. Pour créer un SurfaceEntity, appelez SurfaceEntity.create, comme illustré dans l'exemple suivant.

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

Lire des vidéos MV-HEVC à l'aide de Jetpack SceneCore

La norme de codec MV-HEVC est optimisée et conçue pour les vidéos stéréoscopiques, ce qui permet à votre application de lire efficacement des vidéos immersives de haute qualité. Les fichiers MV-HEVC comportent un flux principal, généralement l'œil gauche, et un flux stéréo avec l'autre œil.

Comme pour les vidéos côte à côte, vous pouvez les charger à l’aide de Media3 Exoplayer et les afficher à l’aide de SurfaceEntity. Vous devez spécifier si votre fichier MV-HEVC est principal à gauche ou à droite dans le paramètre stereoMode lorsque vous appelez SurfaceEntity.create.

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

Lire des vidéos spatiales protégées par DRM à l'aide de Jetpack SceneCore

Le SDK Jetpack XR est compatible avec la lecture de flux vidéo chiffrés à l'aide du framework de gestion des droits numériques (DRM) intégré à Android's . La DRM protège votre contenu en permettant une distribution sécurisée et en empêchant la copie ou la lecture non autorisées.

Le processus implique que votre application de lecteur multimédia contacte un serveur de licences pour obtenir des clés de déchiffrement. Sur Android, ce processus est géré de manière sécurisée, et les images vidéo déchiffrées sont affichées dans une mémoire tampon graphique protégée à laquelle le système ou d'autres applications ne peuvent pas accéder, ce qui empêche la capture d'écran.

Pour lire des vidéos protégées par DRM avec Jetpack SceneCore, vous devez procéder comme suit :

  1. Configure SurfaceEntity pour demander une surface protégée.
  2. Configurez Media3 Exoplayer avec les informations DRM nécessaires pour gérer l'échange de clés.
  3. Définissez la sortie du lecteur sur la surface de SurfaceEntity.

L'exemple suivant montre comment configurer ExoPlayer pour lire un flux protégé par DRM et l'afficher sur un SurfaceEntity :

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

Pour obtenir une présentation plus détaillée du framework DRM média d'Android, consultez la documentation DRM média sur source.android.com.

Lire des vidéos à 180 et 360° à l'aide de Jetpack SceneCore

SurfaceEntity est compatible avec la lecture de vidéos à 180° sur des surfaces hémisphériques et de vidéos à 360° sur des surfaces sphériques. Par défaut, le paramètre radius fait référence à la taille radiale des surfaces respectives en mètres.

Le code suivant montre comment configurer SurfaceEntity pour la lecture sur un hémisphère à 180° et une sphère à 360°. Lorsque vous utilisez ces formes de canevas, positionnez la surface en tirant parti de la pose de la tête de l'utilisateur pour offrir une expérience immersive.

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.

Contrôle avancé de SurfaceEntity

Pour un contrôle plus avancé du rendu vidéo et image, par exemple pour appliquer des effets de matériau personnalisés, vous pouvez travailler directement avec SurfaceEntity à partir de la bibliothèque SceneCore.

Les sections suivantes décrivent certaines des fonctionnalités avancées disponibles sur SurfaceEntity.

Appliquer un adoucissement des bords

Adoucissez les bords de la surface pour l'aider à se fondre dans l'environnement en définissant la propriété edgeFeatheringParams.

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

Appliquer un masque alpha

Appliquez un masque alpha pour créer des surfaces non rectangulaires ou ajouter des effets de transparence. Commencez par charger un Texture à partir d’un asset, puis attribuez-le à la propriété primaryAlphaMaskTexture :

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

Lire des vidéos spatiales à l'aide de Jetpack Compose pour XR

Si vous souhaitez savoir comment lire des vidéos à l'aide de Jetpack Compose pour XR, découvrez comment ajouter une surface pour du contenu image ou vidéo.