CastPlayer 使用入门

CastPlayer 是 Jetpack Media3 Player 实现,支持本地播放和投放到远程支持 Cast 的设备。CastPlayer 可简化向应用添加投屏功能的过程,并提供丰富的功能,以便在本地播放和远程播放之间无缝切换。本指南介绍了如何将 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 可让系统界面在不打开应用 activity 的情况下重新路由媒体。例如,用户可以从媒体通知中更改播放应用媒体的设备。

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

构建 CastPlayer

对于使用 Cast 进行的远程播放,即使在用户未与您应用的 Activity 互动时(例如通过系统媒体通知),您的应用也应能够管理播放。因此,您应在服务(例如 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();
}

添加界面元素

向应用的界面添加 MediaRouteButton,以便用户选择 Cast 设备。 本部分将介绍如何添加按钮并监听事件,以便在播放设备在本地设备和远程设备之间切换时更新界面。

设置 MediaRouteButton

您可以通过以下四种方法将 MediaRouteButton 添加到 activity 的界面中,供用户进行互动。选择哪种方法取决于您希望玩家活动的界面看起来如何以及如何运作。

向播放器添加可组合的媒体路由按钮

您可以将 MediaRouteButton 可组合项添加到播放器的界面中。如需了解详情,请参阅 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 的界面控件中添加 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);
    ...
}

将媒体路由按钮添加为视图

或者,您也可以在 activity 的 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 Listener

Activity 中创建 Player.Listener 以监听媒体播放位置的变化。当 playbackTypePLAYBACK_TYPE_LOCALPLAYBACK_TYPE_REMOTE 之间变化时,您可以根据需要调整界面。为防止内存泄漏,并将监听器活动限制为仅在应用可见时进行,请在 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);
}

如需详细了解如何监听和响应播放事件,请参阅播放器事件指南。