CastPlayer는 로컬 재생과 원격 Cast 지원 기기로의 전송을 모두 지원하는 Jetpack Media3 플레이어 구현입니다. CastPlayer는 앱에 전송 기능을 쉽게 추가할 수 있도록 지원하며, 로컬 재생과 원격 재생 간에 원활하게 전환할 수 있는 다양한 기능을 제공합니다. 이 가이드에서는 CastPlayer를 미디어 앱에 통합하는 방법을 설명합니다.
Cast를 다른 플랫폼과 통합하려면 Cast SDK를 참고하세요.
Cast 지원 기기 가져오기
CastPlayer를 테스트하려면 Cast 지원 기기가 필요합니다. Android TV, Chromecast, 스마트 스피커, 스마트 디스플레이 등이 있습니다. 기기가 설정되어 있고 검색을 위해 개발 모바일과 동일한 Wi-Fi 네트워크에 연결되어 있는지 확인합니다.
빌드 종속 항목 추가
CastPlayer 사용을 시작하려면 앱 모듈의 build.gradle 파일에 AndroidX Media3 및 CastPlayer 종속 항목을 추가합니다.
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는 동작을 구성하기 위해 옵션 제공자가 필요합니다. 기본 설정의 경우 AndroidManifest.xml 파일에 추가하여 DefaultCastOptionsProvider를 사용할 수 있습니다. 여기에는 기본 수신기 애플리케이션을 비롯한 기본 설정이 사용됩니다.
<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를 로컬 플레이어 인스턴스로 설정합니다. 그런 다음 미디어 알림 또는 잠금 화면 알림에서 휴대기기와 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() }
자바
@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를 추가합니다. MediaRouteButton 아이콘을 탭하면 네트워크에서 사용 가능한 Cast 지원 기기 목록이 표시된 대화상자가 열립니다. 사용자가 기기를 선택하면 미디어 재생이 모바일에서 선택한 수신기 기기로 트랜스퍼됩니다. 이 섹션에서는 재생이 로컬 기기와 원격 기기 간에 전환될 때 버튼을 추가하고 이벤트를 수신 대기하여 UI를 업데이트하는 방법을 보여줍니다.
MediaRouteButton 설정
활동의 UI에 MediaRouteButton를 추가하는 방법에는 네 가지가 있습니다. 가장 적합한 선택은 앱의 설계와 요구사항에 따라 달라집니다.
- Compose UI: 버튼 컴포저블을 추가합니다.
- 조회수 UI:
- 앱 바 메뉴에 버튼을 추가합니다.
PlayerView내부에 버튼을 추가합니다.- 버튼을 표준
View로 추가합니다.
플레이어에 컴포저블 MediaRouteButton 추가
MediaRouteButton 컴포저블을 플레이어의 UI에 추가할 수 있습니다. 자세한 내용은 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에 MediaRouteButton 추가
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()); }
앱 바 메뉴에 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) ... }
자바
@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를 뷰로 추가합니다.
활동 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) } }
자바
@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); }
재생 이벤트 수신 대기 및 응답에 관한 자세한 내용은 플레이어 이벤트 가이드를 참고하세요.