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
StereoModecomoMono,SideBySideouTopBottom, 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
SpatialExternalSurfacesó é compatível com superfícies retangulares. - Este
Surfacenão captura eventos de entrada. - Não é possível sincronizar as mudanças de
StereoModecom 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
MovePolicyse 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.ProtectedemSpatialExternalSurfaceé essencial para que oSurfacesubjacente seja apoiado por buffers seguros adequados para conteúdo de DRM. - Configuração de DRM: é preciso configurar o
MediaItemcom 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.