CastPlayer 시작하기

CastPlayer는 로컬 재생과 원격 Cast 지원 기기로의 전송을 모두 지원하는 Jetpack Media3 플레이어 구현입니다. CastPlayer를 사용하면 앱에 전송 기능을 쉽게 추가하고 로컬 재생과 원격 재생 간에 원활하게 전환할 수 있는 다양한 기능을 제공할 수 있습니다. 이 가이드에서는 CastPlayer를 미디어 앱에 통합하는 방법을 설명합니다.

Cast를 다른 플랫폼과 통합하려면 Cast SDK를 참고하세요.

CastPlayer를 종속 항목으로 추가

CastPlayer 사용을 시작하려면 앱 모듈의 build.gradle 파일에 필요한 AndroidX Media3 및 CastPlayer 종속 항목을 추가합니다.

Kotlin

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

Groovy

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

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를 사용한 원격 재생의 경우 사용자가 시스템 미디어 알림과 같은 앱의 활동과 상호작용하지 않을 때도 앱이 재생을 관리할 수 있어야 합니다. 따라서 MediaSessionService 또는 MediaLibraryService와 같은 서비스에서 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()
}

자바

@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를 추가합니다. 이 섹션에서는 버튼을 추가하고 재생이 로컬 기기와 원격 기기 간에 전환될 때 UI를 업데이트하기 위해 이벤트를 수신 대기하는 방법을 보여줍니다.

MediaRouteButton 설정

사용자가 상호작용할 수 있도록 활동의 UI에 MediaRouteButton를 추가하는 방법은 네 가지가 있습니다. 선택은 플레이어 활동의 UI가 어떻게 표시되고 작동하기를 원하는지에 따라 달라집니다.

플레이어에 컴포저블 미디어 경로 버튼 추가

플레이어의 UI에 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의 UI 컨트롤 내에서 MediaRouteButton를 직접 추가할 수 있습니다. MediaControllerPlayerView의 플레이어로 설정한 후 MediaRouteButtonViewProvider를 제공하여 플레이어에 전송 버튼을 표시합니다.

Kotlin

override fun onStart() {
  super.onStart()

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

자바

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

자바

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

미디어 경로 버튼을 뷰로 추가

또는 활동 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)
  }
}

자바

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

활동 리스너

Activity에서 Player.Listener를 만들어 미디어 재생 위치의 변경사항을 수신 대기합니다. PLAYBACK_TYPE_LOCALPLAYBACK_TYPE_REMOTE 간에 playbackType가 변경되면 필요에 따라 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)
}

자바

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

재생 이벤트 수신 대기 및 응답에 관한 자세한 내용은 플레이어 이벤트 가이드를 참고하세요.