アプリに空間オーディオ動画を追加する

対応する XR デバイス
このガイダンスは、次のようなタイプの XR デバイス向けのエクスペリエンスを構築する際に役立ちます。
[
XR Headsets
]
有線 XR グラス

Jetpack XR SDK は、ステレオ スコピック サイドバイサイド 動画 を平面に再生することをサポートしています。ステレオ スコピック動画では、各フレームが左目用と右目用の画像で構成され、視聴者に奥行き感(立体視)を与えます。

他のフォーム ファクタで Android 開発に使用される標準 メディア API を使用して、Android XR アプリで非ステレオ スコピック 2D 動画をレンダリングできます。

Jetpack SceneCore を使用してサイドバイサイド動画を再生する

サイドバイサイド動画では、各ステレオ スコピック フレームが、水平方向に隣接して配置された 2 つの画像として表示されます。トップアンドボトムの動画フレームは、垂直方向に隣接して配置されます。

サイドバイサイド動画はコーデックではなく、ステレオ スコピック フレームを整理する方法です。つまり、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()

Jetpack SceneCore を使用して MV-HEVC 動画を再生する

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

Jetpack SceneCore を使用して DRM で保護された空間動画を再生する

Jetpack XR SDK は、Android's 組み込みデジタル著作権管理(DRM)フレームワークを使用した暗号化された動画ストリームの再生をサポートしています。DRM は、安全な配信を可能にし、不正なコピーや再生を防ぐことでコンテンツを保護します。

このプロセスでは、メディア プレーヤー アプリケーションがライセンス サーバーに接続して復号鍵を取得します。Android では、このプロセスは安全に管理され、復号された動画フレームは、システムや他のアプリケーションからアクセスできない保護されたグラフィック バッファにレンダリングされるため、画面キャプチャを防ぐことができます。

Jetpack SceneCore で DRM で保護された動画を再生するには、次の操作を行う必要があります。

  1. 保護されたサーフェスをリクエストするように SurfaceEntity を構成します。
  2. 鍵交換を処理するために必要な DRM 情報を使用して Media3 Exoplayer を構成します。
  3. プレーヤーの出力を SurfaceEntity のサーフェスに設定します。

次の例は、DRM で保護された ストリームを再生して SurfaceEntity にレンダリングするように ExoPlayer を構成する方法を示しています。

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

Android のメディア DRM フレームワークの詳細については、source.android.com のMedia DRM ドキュメントをご覧ください

Jetpack SceneCore を使用して 180° 動画と 360° 動画を再生する

SurfaceEntity は、半球サーフェスでの 180° 動画の再生と、球体サーフェスでの 360° 動画の再生をサポートしています。radius パラメータは、デフォルトでは、それぞれのサーフェスの半径サイズ(メートル単位)を表します。

次のコードは、180° 半球と 360° 球体で再生するように SurfaceEntity を設定する方法を示しています。これらのキャンバスの形状を使用する場合は、ユーザーの頭のポーズを活用してサーフェスを配置し、没入感のあるエクスペリエンスを提供します。

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 の高度な制御

カスタム マテリアル エフェクトの適用など、動画と画像のレンダリングをより高度に制御するには、SceneCore ライブラリから SurfaceEntity を直接操作します。

以降のセクションでは、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 for XR を使用して空間動画を再生する

Jetpack Compose for XR を使用して動画を再生する方法については、 画像または動画コンテンツのサーフェスを追加する方法をご覧ください。