CastPlayer 是 Jetpack Media3 Player 实现,支持本地播放和投放到远程支持 Cast 的设备。CastPlayer可简化向应用添加投屏功能的过程,并提供丰富的功能,以便在本地播放和远程播放之间无缝切换。本指南介绍了如何将 CastPlayer 集成到媒体应用中。
如需将 Cast 与其他平台集成,请参阅 Cast SDK。
获取支持 Cast 的设备
如需测试 CastPlayer,您需要支持 Cast 的设备。您可以选择 Android TV、Chromecast、智能音箱和智能显示屏。验证设备是否已设置并连接到与开发移动设备相同的 Wi-Fi 网络,以便进行发现。
添加 build 依赖项
如需开始使用 CastPlayer,请将 AndroidX Media3 和 CastPlayer 依赖项添加到应用模块的 build.gradle 文件中。
Kotlin
implementation("androidx.media3:media3-exoplayer:1.9.2")
implementation("androidx.media3:media3-ui:1.9.2")
implementation("androidx.media3:media3-session:1.9.2")
implementation("androidx.media3:media3-cast:1.9.2")
Groovy
implementation "androidx.media3:media3-exoplayer:1.9.2"
implementation "androidx.media3:media3-ui:1.9.2"
implementation "androidx.media3:media3-session:1.9.2"
implementation "androidx.media3:media3-cast:1.9.2"
配置 CastPlayer
如需配置 CastPlayer,请使用选项提供程序更新 AndroidManifest.xml 文件。
选项提供商
CastPlayer 需要一个选项提供程序来配置其行为。对于基本设置,您可以通过将 DefaultCastOptionsProvider 添加到 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 可让系统界面发现网络上支持 Cast 的设备,并在不打开应用 activity 的情况下重新路由媒体。例如,用户可以通过媒体通知更改播放应用媒体的设备。
<application>
...
<receiver android:name="androidx.mediarouter.media.MediaTransferReceiver" />
...
</application>
构建 CastPlayer
对于使用 Cast 进行的远程播放,即使在用户未与您应用的 Activity 互动时(例如通过系统媒体通知),您的应用也应能够管理播放。因此,您应在服务(例如 MediaSessionService 或 MediaLibraryService)中创建 ExoPlayer(用于本地播放)和 CastPlayer(用于远程播放)实例。首先,创建 ExoPlayer 实例,然后在构建 CastPlayer 实例时,将 ExoPlayer 设置为本地播放器实例。然后,您可以通过媒体通知或锁屏通知在移动设备和支持 Cast 的设备之间切换媒体播放。当输出路由从本地更改为远程或从远程更改为本地时,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。点按 MediaRouteButton 会打开一个对话框,其中显示了网络上可用的支持 Cast 的设备列表。当用户选择设备后,媒体播放会从移动设备转移到所选的接收器设备。本部分将介绍如何添加按钮并监听事件,以便在播放设备在本地设备和远程设备之间切换时更新界面。
设置 MediaRouteButton
您可以通过四种方式将 MediaRouteButton 添加到 activity 的界面中。最佳选择取决于应用的设计和要求。
- Compose 界面:添加按钮可组合项。
- 观看次数界面:
- 将按钮添加到应用栏菜单。
- 在
PlayerView内添加按钮。 - 将按钮添加为标准
View。
向播放器添加可组合项 MediaRouteButton
您可以将 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) { ... }
将 MediaRouteButton 添加到 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()); }
将 MediaRouteButton 添加到应用栏菜单中
如需在应用栏菜单中设置 MediaRouteButton,请创建 XML 菜单并在 Activity 中替换 onCreateOptionsMenu。
<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); ... }
将 MediaRouteButton 添加为视图
您可以在 activity 的 layout.xml 中设置 MediaRouteButton。
<androidx.mediarouter.app.MediaRouteButton
android:id="@+id/media_route_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:mediaRouteButtonTint="@android:color/white" />
如需完成 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 以监听媒体播放位置的变化。当 playbackType 在 PLAYBACK_TYPE_LOCAL 和 PLAYBACK_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); }
如需详细了解如何监听和响应播放事件,请参阅播放器事件指南。