Jetpack Media3는 동영상 및 오디오 파일 재생을 위한 기본 기능을 간략히 설명하는 Player
인터페이스를 정의합니다. ExoPlayer
는 Media3의 이 인터페이스의 기본 구현입니다. ExoPlayer는 대부분의 재생 사용 사례를 다루는 포괄적인 기능을 제공하고 추가 사용 사례를 처리하도록 맞춤설정할 수 있으므로 사용하는 것이 좋습니다. 또한 ExoPlayer는 기기 및 OS 단편화를 추상화하므로 코드가 전체 Android 생태계에서 일관되게 작동합니다. ExoPlayer에는 다음이 포함됩니다.
이 페이지에서는 재생 앱을 빌드하는 몇 가지 주요 단계를 안내합니다. 자세한 내용은 Media3 ExoPlayer에 관한 전체 가이드를 참고하세요.
시작하기
시작하려면 Jetpack Media3의 ExoPlayer, UI, 공통 모듈에 종속 항목을 추가합니다.
implementation "androidx.media3:media3-exoplayer:1.4.1" implementation "androidx.media3:media3-ui:1.4.1" implementation "androidx.media3:media3-common:1.4.1"
사용 사례에 따라 DASH 형식의 스트림을 재생하기 위해 Media3의 추가 모듈(예: exoplayer-dash
)이 필요할 수도 있습니다.
1.4.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.4.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(); } }
매니페스트에서 Service
클래스에 MediaSessionService
인텐트 필터를 사용하고 포그라운드 서비스를 실행하기 위해 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()
과 같은 동일한 메서드를 사용하여 재생을 제어할 수 있습니다. 다른 구성요소와 마찬가지로 더 이상 필요하지 않은 경우 MediaController
를 해제해야 합니다(예: Activity
의 onStop()
수명 주기 메서드). 이를 위해 MediaController.releaseFuture()
를 호출합니다.
알림 게시
활성 상태에서 알림을 게시하려면 포그라운드 서비스가 필요합니다. 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
콜백에는 다음과 같은 메서드가 추가됩니다.
onGetLibraryRoot()
: 클라이언트가 콘텐츠 트리의 루트MediaItem
를 요청하는 경우onGetChildren()
: 클라이언트가 콘텐츠 트리에서MediaItem
의 하위 요소를 요청하는 경우onGetSearchResult()
클라이언트가 지정된 검색어에 대해 콘텐츠 트리에서 검색 결과를 요청하는 경우
관련 콜백 메서드에는 클라이언트 앱이 관심을 갖는 콘텐츠 트리의 유형에 관한 추가 신호가 포함된 LibraryParams
객체가 포함됩니다.