開始使用 CastPlayer

CastPlayer 是 Jetpack Media3 Player 的實作項目,支援本機播放和投放至遠端支援 Cast 的裝置。CastPlayer 可簡化在應用程式中新增 Cast 功能的程序,並提供豐富的功能,讓使用者在本地和遠端播放之間順暢切換。本指南說明如何將 CastPlayer 整合至媒體應用程式。

如要將 Cast 與其他平台整合,請參閱 Cast SDK

將 CastPlayer 新增為依附元件

如要開始使用 CastPlayer,請在應用程式模組的 build.gradle 檔案中,新增所需的 AndroidX Media3 和 CastPlayer 依附元件。

Kotlin

implementation("androidx.media3:media3-exoplayer:1.9.0-alpha01")
implementation("androidx.media3:media3-ui:1.9.0-alpha01")
implementation("androidx.media3:media3-session:1.9.0-alpha01")
implementation("androidx.media3:media3-cast:1.9.0-alpha01")

Groovy

implementation "androidx.media3:media3-exoplayer:1.9.0-alpha01"
implementation "androidx.media3:media3-ui:1.9.0-alpha01"
implementation "androidx.media3:media3-session:1.9.0-alpha01"
implementation "androidx.media3:media3-cast:1.9.0-alpha01"

請參閱 Jetpack Media 版本說明,找出最新 Alpha 版,以便將 CastPlayer 整合至應用程式。所有模組必須是相同版本。

如要進一步瞭解可用的程式庫模組,請參閱「Google Maven AndroidX Media3 頁面」。

設定 CastPlayer

如要設定 CastPlayer,請使用選項供應商更新 AndroidManifest.xml 檔案。

選項供應商

CastPlayer 需要選項供應器來設定其行為。如要進行基本設定,您可以將預設選項提供者新增至 AndroidManifest.xml 檔案。這會使用預設設定,包括預設接收器應用程式。

<application>
  <meta-data
    android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
    android:value="androidx.media3.cast.DefaultCastOptionsProvider" />
</application>

如要自訂設定,請實作自己的自訂 OptionsProvider。如要瞭解如何設定,請參閱 CastOptions 指南。

新增媒體轉移接收器

在資訊清單中新增 MediaTransferReceiver,即可讓系統 UI 重新導向媒體,不必開啟應用程式活動。舉例來說,使用者可以透過媒體通知,變更播放應用程式媒體的裝置。

<application>
  <receiver android:name="androidx.mediarouter.media.MediaTransferReceiver" />
</application>

建構 CastPlayer

如要使用 Cast 進行遠端播放,即使使用者未與應用程式的活動互動 (例如透過系統媒體通知),應用程式也應能管理播放作業。因此,您應在服務 (例如 MediaSessionServiceMediaLibraryService) 中建立 ExoPlayer (用於本機播放) 和 CastPlayer (用於遠端播放) 執行個體。首先,請建立 ExoPlayer 例項,然後在建構 CastPlayer 例項時,將 ExoPlayer 設為本機播放器例項。這樣一來,當輸出路徑從本機變更為遠端,或從遠端變更為本機時,Media3 就能處理播放器轉移作業。

Kotlin

override fun onCreate() {
  super.onCreate()

  val exoPlayer = ExoPlayer.Builder(context).build()
  val castPlayer = CastPlayer.Builder(context)
      .setLocalPlayer(exoPlayer)
      .build()

  mediaSession = MediaSession.Builder(context, castPlayer).build()
}

Java

@Override
public void onCreate() {
  super.onCreate();

  ExoPlayer exoPlayer = new ExoPlayer.Builder(context).build();
  CastPlayer castPlayer = new CastPlayer.Builder(context)
      .setLocalPlayer(exoPlayer)
      .build();

  mediaSession = new MediaSession.Builder(
    /* context= */ context, /* player= */ castPlayer).build();
}

新增 UI 元素

在應用程式的 UI 中新增 MediaRouteButton,讓使用者選取 Cast 裝置。 本節說明如何新增按鈕及監聽事件,以便在播放作業於本機和遠端裝置之間切換時更新 UI。

設定 MediaRouteButton

您可以透過四種方法,將 MediaRouteButton 新增至活動的 UI,供使用者互動。選擇方式取決於您希望玩家活動的 UI 外觀和運作方式。

在播放器中新增可組合的媒體路徑按鈕

您可以將 MediaRouteButton 可組合函式新增至播放器的 UI。詳情請參閱 Compose 指南。

Kotlin

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.media3.cast.MediaRouteButton

@Composable
fun PlayerComposeView(player: Player, modifier: Modifier = Modifier) {
  var controlsVisible by remember { mutableStateOf(false) }

  Box(
    modifier = modifier.clickable { controlsVisible = true },
    contentAlignment = Alignment.Center,
  ) {
    PlayerSurface(player = player, modifier = modifier)
    AnimatedVisibility(visible = controlsVisible, enter = fadeIn(), exit = fadeOut()) {
      Box(modifier = Modifier.fillMaxSize()) {
        MediaRouteButton(modifier = Modifier.align(Alignment.TopEnd))
        PrimaryControls(player = player, modifier = Modifier.align(Alignment.Center))
      }
    }
  }
}

@Composable
fun PrimaryControls(player: Player, modifier: Modifier = Modifier) {
  ...
}

將媒體路線按鈕新增至 PlayerView

你可以在 PlayerView 的 UI 控制項中直接新增 MediaRouteButton。將 MediaController 設為 PlayerView 的播放器後,請提供 MediaRouteButtonViewProvider,在播放器上顯示 Cast 按鈕。

Kotlin

override fun onStart() {
  super.onStart()

  playerView.player = mediaController
  playerView.setMediaRouteButtonViewProvider(MediaRouteButtonViewProvider())
}

Java

@Override
public void onStart() {
  super.onStart();

  playerView.setPlayer(mediaController);
  playerView.setMediaRouteButtonViewProvider(new MediaRouteButtonViewProvider());
}

在應用程式列選單中新增媒體路線按鈕

這個方法會在應用程式列選單中設定媒體路線按鈕。如要顯示這種樣式的按鈕,必須更新資訊清單檔案和 Activity

<menu xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:app="http://schemas.android.com/apk/res-auto">
  <item android:id="@+id/media_route_menu_item"
    android:title="@string/media_route_menu_title"
    app:showAsAction="always"
    app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"/>
</menu>

Kotlin

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    ...
    menuInflater.inflate(R.menu.sample_media_route_button_menu, menu)
    val menuItemFuture: ListenableFuture<MenuItem> =
        MediaRouteButtonFactory.setUpMediaRouteButton(
            context, menu, R.id.media_route_menu_item)
    Futures.addCallback(
        menuItemFuture,
        object : FutureCallback<MenuItem> {
            override fun onSuccess(menuItem: MenuItem?) {
                // Do something with the menu item.
            }

            override fun onFailure(t: Throwable) {
                // Handle the failure.
            }
        },
        executor)
    ...
}

Java

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    ...
    getMenuInflater().inflate(R.menu.sample_media_route_button_menu, menu);
    ListenableFuture<MenuItem> menuItemFuture =
        MediaRouteButtonFactory.setUpMediaRouteButton(
          context, menu, R.id.media_route_menu_item);
    Futures.addCallback(
        menuItemFuture,
        new FutureCallback<MenuItem>() {
          @Override
          public void onSuccess(MenuItem menuItem) {
            // Do something with the menu item.
          }

          @Override
          public void onFailure(Throwable t) {
            // Handle the failure.
          }
        },
        executor);
    ...
}

將媒體路線按鈕新增為 View

或者,您也可以在活動 layout.xml 中設定 MediaRouteButton。 如要完成 MediaRouteButton 的設定,請在 Activity 程式碼中使用 Media3 Cast MediaRouteButtonFactory

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)

  findViewById<MediaRouteButton>(R.id.media_route_button)?.also {
    val unused = MediaRouteButtonFactory.setUpMediaRouteButton(context, it)
  }
}

Java

@Override
public void onCreate(Bundle savedInstanceState) {
    ...
    MediaRouteButton button = findViewById(R.id.media_route_button);
    ListenableFuture<Void> setUpFuture =
        MediaRouteButtonFactory.setUpMediaRouteButton(context, button);
}

活動監聽器

Activity 中建立 Player.Listener,監聽媒體播放位置的變更。當 playbackTypePLAYBACK_TYPE_LOCALPLAYBACK_TYPE_REMOTE 之間切換時,您可以視需要調整 UI。為避免記憶體流失,並將事件監聽器活動限制在應用程式顯示時,請在 onStart 中註冊事件監聽器,並在 onStop 中取消註冊:

Kotlin

import androidx.media3.common.DeviceInfo
import androidx.media3.common.Player

private val playerListener: Player.Listener =
  object : Player.Listener {
    override fun onDeviceInfoChanged(deviceInfo: DeviceInfo) {
      if (deviceInfo.playbackType == DeviceInfo.PLAYBACK_TYPE_LOCAL) {
        // Add UI changes for local playback.
      } else if (deviceInfo.playbackType == DeviceInfo.PLAYBACK_TYPE_REMOTE) {
        // Add UI changes for remote playback.
      }
    }
  }

override fun onStart() {
  super.onStart()
  mediaController.addListener(playerListener)
}

override fun onStop() {
  super.onStop()
  mediaController.removeListener(playerListener)
}

Java

import androidx.media3.common.DeviceInfo;
import androidx.media3.common.Player;

private Player.Listener playerListener =
    new Player.Listener() {
      @Override
      public void onDeviceInfoChanged(DeviceInfo deviceInfo) {
        if (deviceInfo.playbackType == DeviceInfo.PLAYBACK_TYPE_LOCAL) {
          // Add UI changes for local playback.
        } else if (deviceInfo.playbackType == DeviceInfo.PLAYBACK_TYPE_REMOTE) {
          // Add UI changes for remote playback.
        }
      }
    };

@Override
protected void onStart() {
  super.onStart();
  mediaController.addListener(playerListener);
}

@Override
protected void onStop() {
  super.onStop();
  mediaController.removeListener(playerListener);
}

如要進一步瞭解如何監聽及回應播放事件,請參閱播放器事件指南。