Làm quen với CastPlayer

CastPlayer là một phương thức triển khai Player của Jetpack Media3, hỗ trợ cả tính năng phát cục bộ và truyền đến một thiết bị từ xa có hỗ trợ Cast. CastPlayer giúp đơn giản hoá việc thêm chức năng truyền vào ứng dụng của bạn và cung cấp nhiều tính năng để chuyển đổi liền mạch giữa chế độ phát cục bộ và phát từ xa. Hướng dẫn này cho bạn biết cách tích hợp CastPlayer vào ứng dụng đa phương tiện của bạn.

Để tích hợp Cast với các nền tảng khác, hãy xem Cast SDK.

Mua một thiết bị hỗ trợ truyền

Để kiểm thử CastPlayer, bạn cần có một thiết bị có thể truyền. Các lựa chọn bao gồm Android TV, Chromecast, loa thông minh và màn hình thông minh. Xác minh rằng thiết bị của bạn đã được thiết lập và kết nối với cùng mạng Wi-Fi như thiết bị di động phát triển để khám phá.

Thêm phần phụ thuộc của bản dựng

Để bắt đầu sử dụng CastPlayer, hãy thêm các phần phụ thuộc AndroidX Media3 và CastPlayer vào tệp build.gradle của mô-đun ứng dụng.

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"

Định cấu hình CastPlayer

Để định cấu hình CastPlayer, hãy cập nhật tệp AndroidManifest.xml bằng một trình cung cấp lựa chọn.

Nhà cung cấp lựa chọn

CastPlayer yêu cầu một nhà cung cấp tuỳ chọn để định cấu hình hành vi của nó. Để thiết lập cơ bản, bạn có thể sử dụng DefaultCastOptionsProvider bằng cách thêm mã này vào tệp AndroidManifest.xml. Thao tác này sử dụng chế độ cài đặt mặc định, bao gồm cả ứng dụng nhận mặc định.

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

Để tuỳ chỉnh cấu hình, hãy triển khai OptionsProvider tuỳ chỉnh của riêng bạn. Hãy xem hướng dẫn về CastOptions để tìm hiểu cách thực hiện.

Thêm một receiver để truyền nội dung nghe nhìn

Việc thêm một MediaTransferReceiver vào tệp kê khai cho phép Giao diện người dùng hệ thống phát hiện các thiết bị có thể truyền trên mạng và định tuyến lại nội dung nghe nhìn mà không cần mở hoạt động của ứng dụng. Ví dụ: người dùng có thể thay đổi thiết bị phát nội dung nghe nhìn của ứng dụng từ thông báo về nội dung nghe nhìn.

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

Tạo một CastPlayer

Đối với tính năng phát từ xa bằng Cast, ứng dụng của bạn phải có khả năng quản lý hoạt động phát ngay cả khi người dùng không tương tác với một Hoạt động trong ứng dụng của bạn, chẳng hạn như thông qua thông báo của hệ thống về nội dung nghe nhìn. Vì lý do này, bạn nên tạo các thực thể ExoPlayer (để phát cục bộ) và CastPlayer (để phát từ xa) trong một dịch vụ, chẳng hạn như MediaSessionService hoặc MediaLibraryService. Trước tiên, hãy tạo phiên bản ExoPlayer, sau đó khi tạo phiên bản CastPlayer, hãy đặt ExoPlayer làm phiên bản trình phát cục bộ. Sau đó, bạn có thể chuyển đổi chế độ phát nội dung nghe nhìn giữa thiết bị di động và thiết bị có thể truyền qua thông báo về nội dung nghe nhìn hoặc thông báo trên màn hình khoá. Media3 sử dụng tính năng OutputSwitcher để xử lý các hoạt động chuyển trình phát khi tuyến đầu ra thay đổi từ cục bộ sang từ xa hoặc từ từ xa sang cục bộ.

Ảnh chụp màn hình cho thấy giao diện người dùng của Trình chuyển đổi đầu ra trong phần thông báo.
Hình 1: (a) Chip thiết bị trên Thông báo về nội dung nghe nhìn (b) Các thiết bị có thể truyền nội dung xuất hiện khi nhấn vào chip thiết bị (c) Chip thiết bị trên Thông báo trên màn hình khoá

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

Thêm các phần tử trên giao diện người dùng

Thêm MediaRouteButton vào giao diện người dùng của ứng dụng. Khi bạn nhấn vào biểu tượng MediaRouteButton, một hộp thoại sẽ mở ra và hiển thị danh sách các thiết bị hỗ trợ Cast hiện có trên mạng. Khi người dùng chọn một thiết bị, nội dung nghe nhìn đang phát sẽ được chuyển từ thiết bị di động sang thiết bị nhận đã chọn. Phần này hướng dẫn bạn cách thêm nút và theo dõi các sự kiện để cập nhật giao diện người dùng khi quá trình phát chuyển đổi giữa thiết bị cục bộ và thiết bị từ xa.

Đặt MediaRouteButton

Có 4 cách để thêm MediaRouteButton vào giao diện người dùng của hoạt động. Lựa chọn tốt nhất sẽ phụ thuộc vào thiết kế và các yêu cầu của ứng dụng.

  • Giao diện người dùng Compose: Thêm một thành phần kết hợp nút.
  • Giao diện người dùng của Views:
    • Thêm nút vào trình đơn thanh ứng dụng.
    • Thêm nút vào bên trong PlayerView.
    • Thêm nút này dưới dạng View tiêu chuẩn.
Ảnh chụp màn hình cho thấy MediaRouteButton trong giao diện người dùng.
Hình 2: (a) MediaRouteButton trong thanh trình đơn, (b) dưới dạng một View, (c) trong PlayerView và (d) Hộp thoại của các thiết bị có thể truyền.

Thêm một thành phần kết hợp MediaRouteButton vào Trình phát

Bạn có thể thêm thành phần kết hợp MediaRouteButton vào giao diện người dùng của trình phát. Để biết thêm thông tin, hãy xem hướng dẫn về Compose.

@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) {
  // ...
}

Thêm MediaRouteButton vào PlayerView

Bạn có thể thêm MediaRouteButton ngay trong các chế độ điều khiển giao diện người dùng của PlayerView. Sau khi đặt MediaController làm trình phát cho PlayerView, hãy cung cấp MediaRouteButtonViewProvider để hiển thị nút Truyền trên Trình phát.

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

Thêm MediaRouteButton vào trình đơn thanh ứng dụng

Để thiết lập MediaRouteButton trong trình đơn thanh ứng dụng, hãy tạo một trình đơn XML và ghi đè onCreateOptionsMenu trong 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;
}

Thêm MediaRouteButton làm Chế độ xem

Bạn có thể thiết lập một MediaRouteButton trong 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" />

Để hoàn tất quá trình thiết lập cho MediaRouteButton, hãy sử dụng MediaRouteButtonFactory Media3 Cast trong mã Activity của bạn.

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

Trình nghe hoạt động

Tạo một Player.Listener trong Activity để theo dõi các thay đổi về vị trí phát nội dung nghe nhìn. Khi playbackType thay đổi giữa PLAYBACK_TYPE_LOCALPLAYBACK_TYPE_REMOTE, bạn có thể điều chỉnh giao diện người dùng nếu cần. Để ngăn chặn tình trạng rò rỉ bộ nhớ và giới hạn hoạt động của trình nghe chỉ khi ứng dụng của bạn hiển thị, hãy đăng ký trình nghe trong onStart và huỷ đăng ký trình nghe trong 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);
}

Để biết thêm thông tin về cách theo dõi và phản hồi các sự kiện phát, hãy xem hướng dẫn về sự kiện của trình phát.