Jetpack Media3는 동영상 및 오디오 파일 재생의 기본 기능을 설명하는 Player
인터페이스를 정의합니다. ExoPlayer
은 Media3에서 이 인터페이스의 기본 구현입니다. ExoPlayer는 대부분의 재생 사용 사례를 지원하는 포괄적인 기능 세트를 제공하며 추가 사용 사례를 처리하도록 맞춤설정할 수 있으므로 ExoPlayer를 사용하는 것이 좋습니다. 또한 ExoPlayer는 기기 및 OS 조각화를 추상화하므로 코드가 전체 Android 생태계에서 일관되게 작동합니다. ExoPlayer에는 다음이 포함됩니다.
- 재생목록 지원
- 다양한 프로그레시브 및 적응형 스트리밍 형식 지원
- 클라이언트 측 및 서버 측 광고 삽입 지원
- DRM으로 보호되는 재생 지원
이 페이지에서는 재생 앱을 빌드하는 주요 단계를 안내하며 자세한 내용은 Media3 ExoPlayer의 전체 가이드를 참고하세요.
시작하기
시작하려면 Jetpack Media3의 ExoPlayer, UI, Common 모듈에 종속 항목을 추가하세요.
implementation "androidx.media3:media3-exoplayer:1.7.1" implementation "androidx.media3:media3-ui:1.7.1" implementation "androidx.media3:media3-common:1.7.1"
사용 사례에 따라 DASH 형식으로 스트림을 재생하기 위한 exoplayer-dash
와 같은 Media3의 추가 모듈이 필요할 수도 있습니다.
1.7.1
을 원하는 라이브러리 버전으로 바꿔야 합니다. 출시 노트를 참고하여 최신 버전을 확인할 수 있습니다.
미디어 플레이어 만들기
Media3를 사용하면 포함된 Player
인터페이스(ExoPlayer
) 구현을 사용하거나 자체 맞춤 구현을 빌드할 수 있습니다.
ExoPlayer 만들기
ExoPlayer
인스턴스를 만드는 가장 간단한 방법은 다음과 같습니다.
Kotlin
val player = ExoPlayer.Builder(context).build()
자바
ExoPlayer player = new ExoPlayer.Builder(context).build();
미디어 플레이어를 Activity
, Fragment
또는 Service
의 onCreate()
수명 주기 메서드에서 만들 수 있습니다.
Builder
에는 다음과 같은 다양한 맞춤설정 옵션이 포함되어 있습니다.
setAudioAttributes()
를 사용하여 오디오 포커스 처리를 구성합니다.- 오디오 출력 기기가 연결 해제될 때 재생 동작을 구성하는
setHandleAudioBecomingNoisy()
setTrackSelector()
을 사용하여 트랙 선택을 구성합니다.
Media3는 앱의 레이아웃 파일에 포함할 수 있는 PlayerView
UI 구성요소를 제공합니다. 이 구성요소는 재생 컨트롤용 PlayerControlView
, 자막 표시용 SubtitleView
, 동영상 렌더링용 Surface
을 캡슐화합니다.
플레이어 준비
setMediaItem()
및 addMediaItem()
과 같은 메서드를 사용하여 재생할 재생목록에 미디어 항목을 추가합니다.
그런 다음 prepare()
를 호출하여 미디어를 로드하고 필요한 리소스를 획득합니다.
앱이 포그라운드에 있기 전에 이러한 단계를 수행해서는 안 됩니다. 플레이어가 Activity
또는 Fragment
에 있는 경우 API 수준 24 이상에서는 onStart()
수명 주기 메서드에서, API 수준 23 이하에서는 onResume()
수명 주기 메서드에서 플레이어를 준비해야 합니다. Service
에 있는 플레이어의 경우 onCreate()
에서 준비할 수 있습니다.
플레이어 제어
플레이어가 준비되면 다음과 같은 플레이어의 메서드를 호출하여 재생을 제어할 수 있습니다.
play()
및pause()
을 사용하여 재생 시작 및 일시중지seekTo()
: 현재 미디어 항목 내의 위치를 탐색seekToNextMediaItem()
및seekToPreviousMediaItem()
를 사용하여 재생목록을 탐색합니다.
PlayerView
또는 PlayerControlView
과 같은 UI 구성요소는 플레이어에 바인딩되면 그에 따라 업데이트됩니다.
플레이어 해제
재생에는 동영상 디코더와 같이 공급이 제한된 리소스가 필요할 수 있으므로 플레이어가 더 이상 필요하지 않을 때 플레이어에서 release()
를 호출하여 리소스를 확보하는 것이 중요합니다.
플레이어가 Activity
또는 Fragment
에 있는 경우 API 수준 24 이상에서는 onStop()
수명 주기 메서드에서, API 수준 23 이하에서는 onPause()
메서드에서 플레이어를 해제합니다. Service
에 있는 플레이어의 경우 onDestroy()
에서 해제할 수 있습니다.
미디어 세션으로 재생 관리
Android에서 미디어 세션은 프로세스 경계를 넘어 미디어 플레이어와 상호작용하는 표준화된 방법을 제공합니다. 미디어 세션을 플레이어에 연결하면 외부에서 미디어 재생을 광고하고 외부 소스에서 재생 명령어를 수신할 수 있습니다. 예를 들어 모바일 및 대형 화면 기기에서 시스템 미디어 컨트롤과 통합할 수 있습니다.
미디어 세션을 사용하려면 Media3 세션 모듈의 종속 항목을 추가하세요.
implementation "androidx.media3:media3-session:1.7.1"
미디어 세션 만들기
다음과 같이 플레이어를 초기화한 후 MediaSession
를 만들 수 있습니다.
Kotlin
val player = ExoPlayer.Builder(context).build() val mediaSession = MediaSession.Builder(context, player).build()
자바
ExoPlayer player = new ExoPlayer.Builder(context).build(); MediaSession mediaSession = new MediaSession.Builder(context, player).build();
Media3는 Player
의 상태를 MediaSession
의 상태와 자동으로 동기화합니다. 이 방법은 ExoPlayer
, CastPlayer
, 맞춤 구현 등 모든 Player
구현에서 작동합니다.
다른 클라이언트에 제어 권한 부여
클라이언트 앱은 미디어 컨트롤러를 구현하여 미디어 세션의 재생을 제어할 수 있습니다. 이러한 요청을 수신하려면 MediaSession
를 빌드할 때 콜백 객체를 설정하세요.
컨트롤러가 미디어 세션에 연결되려고 하면 onConnect()
메서드가 호출됩니다. 제공된 ControllerInfo
를 사용하여 요청을 수락할지 거부할지 결정할 수 있습니다. Media3 세션 데모 앱에서 이 예시를 확인하세요.
연결되면 컨트롤러가 세션에 재생 명령어를 전송할 수 있습니다. 그런 다음 세션은 이러한 명령어를 플레이어에게 위임합니다. Player
인터페이스에 정의된 재생 및 재생목록 명령어는 세션에서 자동으로 처리합니다.
다른 콜백 메서드를 사용하면 예를 들어 맞춤 재생 명령어 요청과 재생목록 수정을 처리할 수 있습니다. 이러한 콜백에도 마찬가지로 ControllerInfo
객체가 포함되어 있으므로 요청별로 액세스 제어를 결정할 수 있습니다.
백그라운드에서 미디어 재생
앱이 포그라운드에 있지 않을 때 미디어를 계속 재생하려면(예: 사용자가 앱을 열지 않은 경우에도 음악, 오디오북 또는 팟캐스트를 재생) Player
및 MediaSession
이 포그라운드 서비스에 캡슐화되어야 합니다. Media3는 이러한 용도로 MediaSessionService
인터페이스를 제공합니다.
MediaSessionService
구현
MediaSessionService
를 확장하는 클래스를 만들고 onCreate()
수명 주기 메서드에서 MediaSession
를 인스턴스화합니다.
Kotlin
class PlaybackService : MediaSessionService() { private var mediaSession: MediaSession? = null // Create your Player and MediaSession in the onCreate lifecycle event override fun onCreate() { super.onCreate() val player = ExoPlayer.Builder(this).build() mediaSession = MediaSession.Builder(this, player).build() } // Remember to release the player and media session in onDestroy override fun onDestroy() { mediaSession?.run { player.release() release() mediaSession = null } super.onDestroy() } }
자바
public class PlaybackService extends MediaSessionService { private MediaSession mediaSession = null; @Override public void onCreate() { super.onCreate(); ExoPlayer player = new ExoPlayer.Builder(this).build(); mediaSession = new MediaSession.Builder(this, player).build(); } @Override public void onDestroy() { mediaSession.getPlayer().release(); mediaSession.release(); mediaSession = null; super.onDestroy(); } }
매니페스트에서 MediaSessionService
인텐트 필터가 있는 Service
클래스를 선언하고 FOREGROUND_SERVICE
권한을 요청하여 포그라운드 서비스를 실행합니다.
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
</intent-filter>
</service>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
마지막으로 만든 클래스에서 onGetSession()
메서드를 재정의하여 미디어 세션에 대한 클라이언트 액세스를 제어합니다. 연결 요청을 수락하려면 MediaSession
를 반환하고 요청을 거부하려면 null
를 반환합니다.
Kotlin
// This example always accepts the connection request override fun onGetSession( controllerInfo: MediaSession.ControllerInfo ): MediaSession? = mediaSession
자바
@Override public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) { // This example always accepts the connection request return mediaSession; }
UI에 연결
이제 미디어 세션이 플레이어 UI가 있는 Activity
또는 Fragment
와 별도의 Service
에 있으므로 MediaController
를 사용하여 함께 연결할 수 있습니다. UI가 있는 Activity
또는 Fragment
의 onStart()
메서드에서 MediaSession
의 SessionToken
를 만든 다음 SessionToken
를 사용하여 MediaController
를 빌드합니다. MediaController
빌드는 비동기적으로 발생합니다.
Kotlin
override fun onStart() { val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java)) val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync() controllerFuture.addListener( { // Call controllerFuture.get() to retrieve the MediaController. // MediaController implements the Player interface, so it can be // attached to the PlayerView UI component. playerView.setPlayer(controllerFuture.get()) }, MoreExecutors.directExecutor() ) }
자바
@Override public void onStart() { SessionToken sessionToken = new SessionToken(this, new ComponentName(this, PlaybackService.class)); ListenableFuture<MediaController> controllerFuture = new MediaController.Builder(this, sessionToken).buildAsync(); controllerFuture.addListener(() -> { // Call controllerFuture.get() to retrieve the MediaController. // MediaController implements the Player interface, so it can be // attached to the PlayerView UI component. playerView.setPlayer(controllerFuture.get()); }, MoreExecutors.directExecutor()) }
MediaController
는 Player
인터페이스를 구현하므로 play()
및 pause()
과 같은 동일한 메서드를 사용하여 재생을 제어할 수 있습니다. 다른 구성요소와 마찬가지로 Activity
의 onStop()
수명 주기 메서드와 같이 더 이상 필요하지 않은 경우 MediaController.releaseFuture()
를 호출하여 MediaController
를 해제해야 합니다.
알림 게시
포그라운드 서비스는 활성 상태일 때 알림을 게시해야 합니다. MediaSessionService
는 MediaNotification
형식으로 MediaStyle
알림을 자동으로 생성합니다.
맞춤 알림을 제공하려면 DefaultMediaNotificationProvider.Builder
를 사용하여 또는 제공자 인터페이스의 맞춤 구현을 만들어 MediaNotification.Provider
를 만드세요. setMediaNotificationProvider
을 사용하여 MediaSession
에 제공업체를 추가합니다.
콘텐츠 라이브러리 광고
MediaLibraryService
는 클라이언트 앱이 앱에서 제공하는 미디어 콘텐츠를 탐색할 수 있도록 하여 MediaSessionService
를 기반으로 빌드됩니다. 클라이언트 앱은 MediaBrowser
를 구현하여 MediaLibraryService
와 상호작용합니다.
MediaLibraryService
구현은 MediaSessionService
구현과 비슷하지만 onGetSession()
에서는 MediaSession
대신 MediaLibrarySession
를 반환해야 합니다. MediaSession.Callback
와 비교할 때 MediaLibrarySession.Callback
에는 브라우저 클라이언트가 라이브러리 서비스에서 제공하는 콘텐츠를 탐색할 수 있는 추가 메서드가 포함되어 있습니다.
MediaSessionService
와 마찬가지로 매니페스트에서 MediaLibraryService
을 선언하고 FOREGROUND_SERVICE
권한을 요청하여 포그라운드 서비스를 실행합니다.
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaLibraryService"/>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
위의 예시에는 MediaLibraryService
와 하위 호환성을 위한 기존 MediaBrowserService
모두의 인텐트 필터가 포함되어 있습니다. 추가 인텐트 필터를 사용하면 MediaBrowserCompat
API를 사용하는 클라이언트 앱이 Service
를 인식할 수 있습니다.
MediaLibrarySession
를 사용하면 단일 루트 MediaItem
으로 트리 구조에서 콘텐츠 라이브러리를 제공할 수 있습니다. 트리의 각 MediaItem
에는 원하는 수의 하위 MediaItem
노드가 있을 수 있습니다. 클라이언트 앱의 요청에 따라 다른 루트 또는 다른 트리를 제공할 수 있습니다. 예를 들어 추천 미디어 항목 목록을 찾는 클라이언트에 반환하는 트리에는 루트 MediaItem
와 단일 수준의 하위 MediaItem
노드만 포함될 수 있는 반면, 다른 클라이언트 앱에 반환하는 트리는 더 완전한 콘텐츠 라이브러리를 나타낼 수 있습니다.
MediaLibrarySession
만들기
MediaLibrarySession
는 MediaSession
API를 확장하여 콘텐츠 탐색 API를 추가합니다. MediaSession
콜백과 비교할 때 MediaLibrarySession
콜백은 다음과 같은 메서드를 추가합니다.
- 클라이언트가 콘텐츠 트리의 루트
MediaItem
를 요청하는 경우의onGetLibraryRoot()
onGetChildren()
클라이언트가 콘텐츠 트리의MediaItem
하위 요소를 요청하는 경우onGetSearchResult()
클라이언트가 지정된 쿼리의 콘텐츠 트리에서 검색 결과를 요청하는 경우
관련 콜백 메서드에는 클라이언트 앱이 관심을 갖는 콘텐츠 트리의 유형에 관한 추가 신호가 포함된 LibraryParams
객체가 포함됩니다.