向应用添加空间视频

Jetpack XR SDK 支持在平面上播放立体并排视频。对于立体视频,每个帧都包含左眼图像和右眼图像,以便为观看者提供深度感,也称为立体视觉

您可以使用用于其他设备规格的 Android 开发的标准媒体 API 在 Android XR 应用上渲染非立体 2D 视频。

使用 Jetpack SceneCore 播放并排视频

对于并排视频,每个立体帧都以两张水平相邻的图片呈现。顶部和底部视频帧垂直相邻排列。

并排视频不是编解码器,而是一种组织立体帧的方式,这意味着它可以采用Android 支持的任何编解码器进行编码。

您可以使用 Media3 Exoplayer 加载并排视频,然后使用新的 SurfaceEntity 进行渲染。如需创建 SurfaceEntity,请调用 SurfaceEntity.create,如以下示例所示。

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

使用 Jetpack SceneCore 播放 MV-HEVC 视频

MV-HEVC 编解码器标准经过优化,专为立体视频设计,可让您的应用以出色的画质高效播放沉浸式视频。 MV-HEVC 文件包含一个主视频流(通常是左眼视频)和一个包含另一只眼睛的立体视频流。

与并排视频类似,您可以使用 Media3 Exoplayer 加载它,并使用 SurfaceEntity 渲染它。调用 SurfaceEntity.create 时,您需要在 stereoMode 参数中指定 MV-HEVC 文件是左侧还是右侧主文件。

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

使用 Jetpack SceneCore 播放受 DRM 保护的空间视频

Jetpack XR SDK 支持使用 Android 的内置数字版权管理 (DRM) 框架播放加密的视频流。DRM 可实现安全分发,并防止未经授权的复制或播放,从而保护您的内容。

此流程涉及媒体播放器应用与许可服务器联系以获取解密密钥。在 Android 上,此过程会受到安全管理,解密的视频帧会渲染到受保护的图形缓冲区,该缓冲区无法被系统或其他应用访问,从而防止屏幕捕获。

如需使用 Jetpack SceneCore 播放受 DRM 保护的视频,您需要:

  1. 配置 SurfaceEntity 以请求受保护的 surface。
  2. 使用必要的 DRM 信息配置 Media3 Exoplayer 以处理密钥交换。
  3. 将播放器的输出设置为 SurfaceEntity 的 surface。

以下示例展示了如何配置 ExoPlayer 以播放受 DRM 保护的视频流,并将其呈现在 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()

如需更详细地了解 Android 的媒体 DRM 框架,请参阅 source.android.com 上的媒体 DRM 文档

使用 Jetpack SceneCore 播放 180 度视频和 360 度全景视频

SurfaceEntity 支持在半球面表面上播放 180 度视频,在球面表面上播放 360 度视频。radius 参数默认是指相应表面的径向大小(以米为单位)。

以下代码展示了如何设置 SurfaceEntity 以在 180° 半球和 360° 球面上播放。使用这些画布形状时,请利用用户的头部姿势来定位表面,以提供沉浸式体验。

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

高级 SurfaceEntity 控制

如需更高级地控制视频和图片渲染,例如应用自定义材质效果,您可以直接使用 SceneCore 库中的 SurfaceEntity

以下部分介绍了 SurfaceEntity 上提供的一些高级功能。

应用边缘羽化

通过设置 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)

应用 Alpha 蒙版

应用 Alpha 遮罩可创建非矩形 surface 或添加透明度效果。首先,从资源加载 Texture,然后将其分配给 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
}

使用 Jetpack Compose for XR 播放空间视频

如果您有兴趣了解如何使用 Jetpack Compose for XR 播放视频,请了解如何添加用于显示图片或视频内容 Surface