Начало работы с CastPlayer

CastPlayer – это реализация проигрывателя Jetpack Media3, которая поддерживает как локальное воспроизведение, так и трансляцию на удаленное устройство с поддержкой Cast. CastPlayer позволяет легко добавить в приложение функцию трансляции и обеспечивает удобный переход между локальным и удаленным воспроизведением. В этом руководстве рассказывается, как интегрировать CastPlayer в мультимедийное приложение.

Чтобы интегрировать Cast с другими платформами, ознакомьтесь с Cast SDK.

Как получить устройство с поддержкой Cast

Чтобы протестировать CastPlayer, вам понадобится устройство с поддержкой Cast. Это могут быть устройства Android TV, Chromecast, умные колонки и умные дисплеи. Убедитесь, что устройство настроено и подключено к той же сети Wi-Fi, что и мобильное устройство для разработки.

Добавление зависимостей

Чтобы начать использовать CastPlayer, добавьте зависимости AndroidX Media3 и CastPlayer в файл build.gradle модуля приложения.

Kotlin

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

Яркий

implementation "androidx.media3:media3-exoplayer:1.11.1"
implementation "androidx.media3:media3-ui:1.11.1"
implementation "androidx.media3:media3-session:1.11.1"
implementation "androidx.media3:media3-cast:1.11.1"

Как настроить 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. Подробнее о том, как это сделать…

Как добавить получатель для передачи медиаконтента

Добавление MediaTransferReceiver в манифест позволяет системному интерфейсу обнаруживать в сети устройства с поддержкой Cast и перенаправлять медиаконтент без открытия активности приложения. Например, пользователь может изменить устройство, на котором воспроизводится медиаконтент из вашего приложения, с помощью уведомления о медиаконтенте.

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

Как создать CastPlayer

При удаленном воспроизведении с помощью Cast приложение должно управлять воспроизведением, даже когда пользователь не взаимодействует с его Activity, например через системное уведомление о мультимедиа. Поэтому создавайте экземпляры ExoPlayer (для локального воспроизведения) и CastPlayer (для удаленного воспроизведения) в сервисе, например MediaSessionService или MediaLibraryService. Сначала создайте экземпляр ExoPlayer, а затем при создании экземпляра CastPlayer задайте ExoPlayer в качестве локального экземпляра проигрывателя. После этого вы сможете переключаться между мобильным устройством и устройством с поддержкой Cast в уведомлении о медиаконтенте или уведомлении на заблокированном экране. Media3 использует функцию переключения выходного устройства, чтобы обрабатывать переходы проигрывателя, когда маршрут вывода меняется с локального на удаленный или наоборот.

Скриншот интерфейса переключателя аудиовыхода в уведомлениях.
Изображение 1. а) Чип устройства в уведомлении о мультимедиа. б) Устройства с поддержкой Cast, которые показываются при нажатии на чип устройства. в) Чип устройства в уведомлении на заблокированном экране.

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 UI. Добавьте кнопку composable.
  • Интерфейс просмотра:
    • Добавьте кнопку в меню панели приложений.
    • Добавьте кнопку в элемент PlayerView.
    • Добавьте кнопку как стандартный элемент View.
Скриншот, на котором показана кнопка MediaRouteButton в интерфейсе.
Изображение 2. Кнопка MediaRouteButton: а) в строке меню; б) в виде элемента View; в) в PlayerView; г) диалоговое окно устройств с поддержкой Cast.
.

Как добавить в проигрыватель компонуемый элемент MediaRouteButton

Вы можете добавить MediaRouteButton composable-функцию в интерфейс проигрывателя. Подробнее о создании писем…

@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.

Вы можете добавить MediaRouteButton непосредственно в элементы управления интерфейса PlayerView. После того как вы зададите MediaController в качестве проигрывателя для PlayerView, укажите MediaRouteButtonViewProvider, чтобы на проигрывателе появилась кнопка трансляции.

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 и переопределите onCreateOptionsMenu в 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,
  )
  // ...
  return true
}

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);
  // ...
  return true;
}

Добавьте MediaRouteButton в качестве представления

Вы можете настроить MediaRouteButton в файле макета activity_layout.xml.

  <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, используйте Media3 Cast MediaRouteButtonFactory в коде Activity.

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) {
  super.onCreate(savedInstanceState);
  // ...
  MediaRouteButton button = findViewById(R.id.media_route_button);
  ListenableFuture<Void> setUpFuture =
      MediaRouteButtonFactory.setUpMediaRouteButton(context, button);
}

Прослушиватель действий

Создайте Player.Listener в Activity, чтобы отслеживать изменения местоположения воспроизведения медиаконтента. Когда значение playbackType меняется с PLAYBACK_TYPE_LOCAL на PLAYBACK_TYPE_REMOTE, вы можете настроить интерфейс нужным образом. Чтобы предотвратить утечки памяти и ограничить активность прослушивателя только временем, когда приложение видно, зарегистрируйте прослушиватель в onStart и отмените его регистрацию в onStop:

Kotlin

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

private final 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);
}

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