Aggiungere video spaziali alla tua app

L'SDK Jetpack XR supporta la riproduzione di video stereoscopici side-by-side su superfici piane. Nei video stereoscopici, ogni frame è composto da un'immagine per l'occhio sinistro e una per l'occhio destro, per dare agli spettatori un senso di profondità, noto anche come stereopsi.

Puoi eseguire il rendering di video 2D non stereoscopici sulle app Android XR con le API multimediali standard utilizzate per lo sviluppo Android su altri fattori di forma.

Riprodurre video affiancati utilizzando Jetpack SceneCore

Con il video affiancato, ogni frame stereoscopico viene presentato come due immagini disposte orizzontalmente una accanto all'altra. I frame video superiore e inferiore sono disposti verticalmente uno accanto all'altro.

Il video side-by-side non è un codec, ma un modo per organizzare i fotogrammi stereoscopici, il che significa che può essere codificato in uno qualsiasi dei codec supportati da Android.

Puoi caricare video affiancati utilizzando Media3 Exoplayer e poi eseguirne il rendering utilizzando il nuovo SurfaceEntity. Per creare un SurfaceEntity, chiama SurfaceEntity.create, come mostrato nell'esempio seguente.

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

Riprodurre video MV-HEVC utilizzando Jetpack SceneCore

Lo standard del codec MV-HEVC è ottimizzato e progettato per i video stereoscopici, consentendo alla tua app di riprodurre in modo efficiente video immersivi di ottima qualità. I file MV-HEVC hanno uno stream principale, di solito l'occhio sinistro, e uno stream stereo con l'altro occhio.

Come per il video affiancato, puoi caricarlo utilizzando Media3 Exoplayer e renderizzarlo utilizzando SurfaceEntity. Quando chiami SurfaceEntity.create, devi specificare se il file MV-HEVC è primario a sinistra o a destra nel parametro stereoMode.

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

Riprodurre video spaziali protetti da DRM utilizzando Jetpack SceneCore

L'SDK Jetpack XR supporta la riproduzione di stream video criptati utilizzando il framework DRM (Digital Rights Management) integrato di Android. DRM protegge i tuoi contenuti consentendo una distribuzione sicura e impedendo la copia o la riproduzione non autorizzate.

La procedura prevede che l'applicazione del media player contatti un server delle licenze per ottenere le chiavi di decrittografia. Su Android, questo processo viene gestito in modo sicuro e i frame video decriptati vengono visualizzati in un buffer grafico protetto a cui non è possibile accedere dal sistema o da altre applicazioni, impedendo l'acquisizione dello schermo.

Per riprodurre video protetti da DRM con Jetpack SceneCore, devi:

  1. Configura SurfaceEntity per richiedere una superficie protetta.
  2. Configura Media3 Exoplayer con le informazioni DRM necessarie per gestire lo scambio di chiavi.
  3. Imposta l'output del lettore sulla superficie del SurfaceEntity.

L'esempio seguente mostra come configurare ExoPlayer per riprodurre uno stream protetto da DRM e visualizzarlo su 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)),
    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()

Per una panoramica più dettagliata del framework DRM per contenuti multimediali di Android, consulta la documentazione DRM per contenuti multimediali su source.android.com.

Riprodurre video a 180 e 360 gradi utilizzando Jetpack SceneCore

SurfaceEntity supporta la riproduzione di video a 180° su superfici emisferiche e di video a 360° su superfici sferiche. Il parametro radius si riferisce alle dimensioni radiali delle rispettive superfici in metri per impostazione predefinita.

Il seguente codice mostra come configurare SurfaceEntity per la riproduzione su un emisfero a 180° e una sfera a 360°. Quando utilizzi queste forme della tela, posiziona la superficie sfruttando la postura della testa dell'utente per offrire un'esperienza immersiva.

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

Controllo avanzato di SurfaceEntity

Per un controllo più avanzato del rendering di video e immagini, ad esempio per applicare effetti materiali personalizzati, puoi lavorare direttamente con SurfaceEntity dalla libreria SceneCore.

Le sezioni seguenti descrivono alcune delle funzionalità avanzate disponibili su SurfaceEntity.

Applica sfumatura bordo

Ammorbidisci i bordi della superficie per integrarla meglio con l'ambiente impostando la proprietà edgeFeather.

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

Applicare una maschera alfa

Applica una maschera alfa per creare superfici non rettangolari o aggiungere effetti di trasparenza. Innanzitutto, carica un Texture da un asset, poi assegnalo alla proprietà 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"),
            TextureSampler.create()
        )

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

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

Riprodurre video spaziali utilizzando Jetpack Compose per XR

Se ti interessa scoprire come riprodurre video utilizzando Jetpack Compose per XR, scopri come aggiungere una superficie per contenuti di immagini o video.