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-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 미디어 출시 노트를 참고하여 최신 알파 버전을 찾아 앱에 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를 추가하면 시스템 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를 직접 추가할 수 있습니다. MediaController를 PlayerView의 플레이어로 설정한 후 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_LOCAL와 PLAYBACK_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); }
재생 이벤트 수신 대기 및 응답에 관한 자세한 내용은 플레이어 이벤트 가이드를 참고하세요.