Как добавить пространственное видео в приложение

Поддерживаемые XR-устройства
Это руководство поможет вам создавать контент для таких устройств.
XR-шлемы
Проводные XR-очки

Jetpack XR SDK поддерживает воспроизведение стереоскопического видео с разделением по вертикали на плоских поверхностях. В стереоскопическом видео каждый кадр состоит из двух изображений – для левого и правого глаза. Благодаря этому зритель ощущает глубину, или стереопсис.

В приложениях для Android XR можно воспроизводить обычные 2D-видео, используя стандартные медиа API, которые применяются при разработке приложений для Android на других типах устройств.

Как воспроизводить видео рядом друг с другом с помощью Jetpack SceneCore

В видео с разделением по горизонтали каждый стереоскопический кадр представлен в виде двух изображений, расположенных рядом друг с другом по горизонтали. Верхние и нижние кадры видео располагаются друг над другом.

Видео с разделением по горизонтали – это не кодек, а способ организации стереоскопических кадров, поэтому его можно закодировать с помощью любого кодека, поддерживаемого Android.

Вы можете загрузить видео с разделением экрана с помощью Media3 Exoplayer, а затем отрисовать его с помощью нового SurfaceEntity. Чтобы создать SurfaceEntity, вызовите SurfaceEntity.create, как показано в следующем примере.

val stereoSurfaceEntity = SurfaceEntity.create(
    session = xrSession,
    stereoMode = SurfaceEntity.StereoMode.SIDE_BY_SIDE,
    pose = Pose(Vector3(0.0f, 0.0f, -1.5f)),
    shape = SurfaceEntity.Shape.Quad(FloatSize2d(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()

Как воспроизвести видео в формате MV-HEVC с помощью Jetpack SceneCore

Стандарт кодека MV-HEVC оптимизирован и разработан для стереоскопического видео, поэтому ваше приложение сможет эффективно воспроизводить видео с эффектом погружения в высоком качестве. В файлах MV-HEVC есть основной поток (обычно для левого глаза) и стереопоток для другого глаза.

Как и в случае с видео, снятым бок о бок, его можно загрузить с помощью Media3 Exoplayer и воспроизвести с помощью SurfaceEntity. При вызове SurfaceEntity.create вам нужно будет указать, какой из файлов MV-HEVC является основным (левый или правый), с помощью параметра stereoMode.

// Create the SurfaceEntity with the StereoMode corresponding to the MV-HEVC content
val stereoSurfaceEntity = SurfaceEntity.create(
    session = xrSession,
    stereoMode = SurfaceEntity.StereoMode.MULTIVIEW_LEFT_PRIMARY,
    pose = Pose(Vector3(0.0f, 0.0f, -1.5f)),
    shape = SurfaceEntity.Shape.Quad(FloatSize2d(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()

Как воспроизводить пространственное видео с защитой DRM с помощью Jetpack SceneCore

Jetpack XR SDK поддерживает воспроизведение зашифрованных видеопотоков с помощью встроенного в Android фреймворка для управления цифровыми правами (DRM). DRM защищает ваш контент, обеспечивая безопасное распространение и предотвращая несанкционированное копирование или воспроизведение.

Для этого медиапроигрыватель обращается к серверу лицензий, чтобы получить ключи дешифрования. На устройствах Android этот процесс надежно защищен. Расшифрованные кадры видео передаются в защищенный буфер графики, к которому не могут получить доступ ни система, ни другие приложения. Это предотвращает запись экрана.

Чтобы воспроизводить видео с защитой DRM с помощью Jetpack SceneCore, необходимо:

  1. Настройте SurfaceEntity, чтобы запросить защищенную поверхность.
  2. Настройте Media3 Exoplayer, указав необходимую информацию о DRM, чтобы обеспечить обмен ключами.
  3. Настройте вывод звука на поверхность SurfaceEntity.

В следующем примере показано, как настроить 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)),
    shape = SurfaceEntity.Shape.Quad(FloatSize2d(1.0f, 1.0f)),
    surfaceProtection = SurfaceEntity.SurfaceProtection.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()

Более подробную информацию о фреймворке Media DRM в Android можно найти в документации на сайте source.android.com.

Как воспроизводить панорамные видео с помощью Jetpack SceneCore

SurfaceEntity поддерживает воспроизведение 180-градусных видео на полусферических поверхностях и 360-градусных видео на сферических поверхностях. Параметр radius по умолчанию относится к радиальному размеру соответствующих поверхностей в метрах.

В приведенном ниже коде показано, как настроить параметр SurfaceEntity для воспроизведения на полусфере 180° и сфере 360°. При использовании этих фигур размещайте поверхность, учитывая положение головы пользователя, чтобы обеспечить эффект погружения.

val devicePose = ArDevice.getInstance(xrSession).state.value.devicePose
val activitySpacePose = xrSession.scene.perceptionSpace.transformPoseTo(devicePose, xrSession.scene.activitySpace)

// Set up the surface for playing a 180° video on a hemisphere.
val hemisphereStereoSurfaceEntity =
    SurfaceEntity.create(
        session = xrSession,
        stereoMode = SurfaceEntity.StereoMode.SIDE_BY_SIDE,
        pose = activitySpacePose,
        shape = SurfaceEntity.Shape.Hemisphere(1.0f),
    )
// ... and use the surface for playing the media.

val devicePose = ArDevice.getInstance(xrSession).state.value.devicePose
val activitySpacePose = xrSession.scene.perceptionSpace.transformPoseTo(devicePose, xrSession.scene.activitySpace)
// Set up the surface for playing a 360° video on a sphere.
val sphereStereoSurfaceEntity =
    SurfaceEntity.create(
        session = xrSession,
        stereoMode = SurfaceEntity.StereoMode.TOP_BOTTOM,
        pose = activitySpacePose,
        shape = SurfaceEntity.Shape.Sphere(1.0f),
    )
// ... and use the surface for playing the media.

Расширенные настройки SurfaceEntity

Чтобы получить более точный контроль над обработкой видео и изображений, например применить специальные эффекты материалов, вы можете работать непосредственно с SurfaceEntity из библиотеки SceneCore.

В разделах ниже описаны некоторые расширенные функции, доступные на устройстве SurfaceEntity.

Как применить растушевку краев

Смягчите края поверхности, чтобы она лучше сочеталась с окружением, задав свойство edgeFeatheringParams.

// 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.edgeFeatheringParams =
    SurfaceEntity.EdgeFeatheringParams.RectangleFeather(0.1f, 0.1f)

Как применить альфа-маску

Применяйте альфа-маску, чтобы создавать поверхности неправильной формы или добавлять эффекты прозрачности. Сначала загрузите 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"),
        )

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

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

Как воспроизводить пространственное видео с помощью Jetpack Compose для XR

Если вы хотите узнать, как воспроизводить видео с помощью Jetpack Compose для XR, прочитайте статью о том, как добавить поверхность для изображений или видеоконтента.