Superfícies externas espaciais

Dispositivos XR relevantes
Estas orientações ajudam você a criar experiências para esses tipos de dispositivos XR.
Headsets XR
Óculos XR com fio

Um SpatialExternalSurface é um subespaço combinável que cria e gerencia o Surface em que o app pode desenhar conteúdo, como uma imagem ou um vídeo. SpatialExternalSurface aceita conteúdo estereoscópico ou monoscópico.

Adicionar uma superfície para conteúdo de imagem ou vídeo

Este exemplo demonstra como carregar um vídeo estereoscópico lado a lado usando o Exoplayer da Media3 e o SpatialExternalSurface:

@OptIn(ExperimentalComposeApi::class)
@Composable
fun SpatialExternalSurfaceContent() {
    val context = LocalContext.current
    Subspace {
        SpatialExternalSurface(
            modifier = SubspaceModifier
                .width(1200.dp) // Default width is 400.dp if no width modifier is specified
                .height(676.dp), // Default height is 400.dp if no height modifier is specified
            // Use StereoMode.Mono, StereoMode.SideBySide, or StereoMode.TopBottom, depending
            // upon which type of content you are rendering: monoscopic content, side-by-side stereo
            // content, or top-bottom stereo content
            stereoMode = StereoMode.SideBySide,
        ) {
            val exoPlayer = remember { ExoPlayer.Builder(context).build() }
            val videoUri = Uri.Builder()
                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
                // Represents a side-by-side stereo video, where each frame contains a pair of
                // video frames arranged side-by-side. The frame on the left represents the left
                // eye view, and the frame on the right represents the right eye view.
                .path("sbs_video.mp4")
                .build()
            val mediaItem = MediaItem.fromUri(videoUri)

            // onSurfaceCreated is invoked only one time, when the Surface is created
            onSurfaceCreated { surface ->
                exoPlayer.setVideoSurface(surface)
                exoPlayer.setMediaItem(mediaItem)
                exoPlayer.prepare()
                exoPlayer.play()
            }
            // onSurfaceDestroyed is invoked when the SpatialExternalSurface composable and its
            // associated Surface are destroyed
            onSurfaceDestroyed { exoPlayer.release() }
        }
    }
}

Pontos principais sobre o código

  • Defina StereoMode como Mono, SideBySide ou TopBottom, dependendo do tipo de conteúdo que você está renderizando:
    • Mono: o frame de imagem ou vídeo consiste em uma única imagem idêntica mostrada para os dois olhos.
    • SideBySide: o frame de imagem ou vídeo contém um par de imagens ou frames de vídeo organizados lado a lado, em que a imagem ou o frame à esquerda representa a visão do olho esquerdo, e a imagem ou o frame à direita representa a visão do olho direito.
    • TopBottom: a imagem ou o frame de vídeo contém um par de imagens ou frames de vídeo empilhados verticalmente, em que a imagem ou o frame na parte de cima representa a visão do olho esquerdo, e a imagem ou o frame na parte de baixo representa a visão do olho direito.
  • O SpatialExternalSurface só é compatível com superfícies retangulares.
  • Este Surface não captura eventos de entrada.
  • Não é possível sincronizar as mudanças de StereoMode com a renderização do aplicativo ou a decodificação de vídeo.
  • Esse elemento de composição não pode ser renderizado na frente de outros painéis. Portanto, não use um MovePolicy se houver outros painéis no layout.

Adicionar uma superfície para conteúdo de vídeo protegido por DRM

O SpatialExternalSurface também oferece suporte à reprodução de streams de vídeo protegidos por DRM. Para ativar esse recurso, crie uma superfície segura que renderize para buffers gráficos protegidos. Isso impede que o conteúdo seja gravado na tela ou acessado por componentes não seguros do sistema.

Para criar uma superfície segura, defina o parâmetro SpatialExternalSurfaceProtection como SpatialExternalSurfaceProtection.Protected no combinável SpatialExternalSurface. Além disso, é necessário configurar o Media3 Exoplayer com as informações de DRM adequadas para processar a aquisição de licenças de um servidor de licenças.

O exemplo a seguir demonstra como configurar SpatialExternalSurface e ExoPlayer para reproduzir um stream de vídeo protegido por DRM:

@OptIn(ExperimentalComposeApi::class)
@Composable
fun DrmSpatialVideoPlayer() {
    val context = LocalContext.current
    Subspace {
        SpatialExternalSurface(
            modifier = SubspaceModifier
                .width(1200.dp)
                .height(676.dp),
            stereoMode = StereoMode.SideBySide,
            surfaceProtection = SpatialExternalSurfaceProtection.Protected
        ) {
            val exoPlayer = remember { ExoPlayer.Builder(context).build() }

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

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

            onSurfaceCreated { surface ->
                // The created surface is secure and can be used by the player.
                exoPlayer.setVideoSurface(surface)
                exoPlayer.setMediaItem(mediaItem)
                exoPlayer.prepare()
                exoPlayer.play()
            }

            onSurfaceDestroyed { exoPlayer.release() }
        }
    }
}

Pontos principais sobre o código

  • Superfície protegida: definir surfaceProtection = SpatialExternalSurfaceProtection.Protected em SpatialExternalSurface é essencial para que o Surface subjacente seja apoiado por buffers seguros adequados para conteúdo de DRM.
  • Configuração de DRM: é preciso configurar o MediaItem com o esquema de DRM (por exemplo, C.WIDEVINE_UUID) e o URI do servidor de licença. O ExoPlayer usa essas informações para gerenciar a sessão de DRM.
  • Conteúdo seguro: ao renderizar em uma superfície protegida, o conteúdo do vídeo é decodificado e exibido em um caminho seguro, o que ajuda a atender aos requisitos de licença de conteúdo. Isso também impede que o conteúdo apareça em capturas de tela.