Adicionar vídeo espacial ao seu app

O SDK Jetpack XR é compatível com a reprodução de vídeos lado a lado estereoscópicos em superfícies planas. Com o vídeo estereoscópico, cada frame consiste em uma imagem para o olho esquerdo e outra para o direito, dando aos espectadores uma sensação de profundidade, também conhecida como estereopsia.

É possível renderizar vídeos 2D não estereoscópicos em apps Android XR com as APIs de mídia padrão usadas para desenvolvimento do Android em outros formatos.

Reproduzir vídeos lado a lado usando o Jetpack SceneCore

Com o vídeo lado a lado, cada frame estereoscópico é apresentado como duas imagens organizadas horizontalmente adjacentes uma à outra. Os frames de vídeo de cima e de baixo são organizados verticalmente um ao lado do outro.

O vídeo lado a lado não é um codec, mas sim uma maneira de organizar frames estereoscópicos. Isso significa que ele pode ser codificado em qualquer um dos codecs compatíveis com o Android.

É possível carregar vídeos lado a lado usando o Exoplayer da Media3 e renderizar com o novo SurfaceEntity. Para criar um SurfaceEntity, chame SurfaceEntity.create, conforme mostrado no exemplo a seguir.

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

Reproduzir vídeo MV-HEVC usando o Jetpack SceneCore

O padrão de codec MV-HEVC é otimizado e projetado para vídeo estereoscópico, permitindo que seu app reproduza vídeos imersivos com eficiência e alta qualidade. Os arquivos MV-HEVC têm um fluxo principal, geralmente o olho esquerdo, e um fluxo estéreo com o outro olho.

Assim como o vídeo lado a lado, você pode carregá-lo usando o Exoplayer da Media3 e renderizá-lo usando o SurfaceEntity. Você vai precisar especificar se o arquivo MV-HEVC é primário à esquerda ou à direita no parâmetro stereoMode ao chamar SurfaceEntity.create.

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

Reproduzir vídeos em 180 e 360 graus usando o Jetpack SceneCore

O SurfaceEntity é compatível com a reprodução de vídeos em 180° em superfícies hemisféricas e de vídeos em 360° em superfícies esféricas. O parâmetro radius se refere ao tamanho radial das respectivas superfícies em metros por padrão.

O código a seguir mostra como configurar o SurfaceEntity para reprodução em um hemisfério de 180° e uma esfera de 360°. Ao usar essas formas de tela, posicione a superfície aproveitando a postura da cabeça do usuário para oferecer uma experiência imersiva.

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

Controle avançado de SurfaceEntity

Para um controle mais avançado da renderização de vídeo e imagem, como a aplicação de efeitos de materiais personalizados, trabalhe diretamente com SurfaceEntity na biblioteca SceneCore.

As seções a seguir descrevem alguns dos recursos avançados disponíveis no SurfaceEntity.

Aplicar difusão de borda

Suavize as bordas da superfície para ajudar a misturar com o ambiente definindo a propriedade 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)

Aplicar uma máscara alfa

Aplique uma máscara alfa para criar superfícies não retangulares ou adicionar efeitos de transparência. Primeiro, carregue um Texture de um recurso e atribua-o à propriedade 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
}

Reproduzir vídeo espacial usando o Jetpack Compose para XR

Se quiser aprender a reproduzir vídeos usando o Jetpack Compose para XR, saiba como adicionar uma superfície para conteúdo de imagem ou vídeo.