[Android] Jetpack Media3: MediaSession

성승모·2026년 4월 17일

Android

목록 보기
11/11

기기 기반으로 추상화되어 간단한 아키텍처로 강력한 맞춤설정, 안정성, 최적화를 제공하는 라이브러리.

요약

  1. Media Player
    : 미디어 파일을 재생하는 제일 기본적인 구성요소
  • Player: 재생, 일시중지, 탐색 등의 고급 기능을 정의하는 인터페이스.
  • ExoPlayer: Player 인터페이스의 구현체
  1. Media Session
  • MediaSession: 오디오/비디오 플레이어와 상호작용할 수 있도록 하는 컨테이너 역할. 생성 시 Player를 주입받음.
  • MediaSessionService: 백그라운드 재생을 지원하며, MediaSessionPlayer를 갖고 백그라운드에서 Activity에 독립적으로 실행될 수 있게 함.
  • MediaController: 앱 외부에서 제어할 때 사용. Player 구현체이지만, MediaSessionPlayer에 명령을 전달하는 역할만 수행함. 즉, TV(Player) - 셋탑박스(MediaSession) - 리모컨(MediaController)의 관계.
  • MediaLibraryService: MediaSessionService와 같지만, 라이브러리 형태로 컨텐츠를 제공할 수 있게 돕는다.
  • MediaBrowser: PlayerMediaController를 모두 구현하여, 라이브러리에서 재생할 컨텐츠를 고르는 등의 액션을 제공한다.
  1. UI Components
  • PlayerView: 비디오나 Playback 컨트롤러를 제공하는 기본적인 view
  • PlayerSurface: 비디오를 보여주기 위한 Compose view. 단, playback 컨트롤러에는 연결할 수 없다.

Editing Components 간단 소개

class설명노트
Transformer미디어 변환을 시작하거나 확인할 수 있음
EffectsMediaItem에 적용될 액션들ExoPlayer를 사용하여 적용된 Effect를 export하기 전에 볼 수 있다.
EditMediaItem편집될 MediaItem들의 컬렉션


Media Session

Player Interface

핵심 컴포넌트인 ExoPlayer, MediaControllerPlayer의 구현체로, Media3의 재생 아키텍처에서 핵심적인 인터페이스로 작동한다.

  • 상태
    • 재생 상태 - getPlaybackState()로 가져오며, 상태 값은 STATE_IDLE, STATE_BUFFERING, STATE_READY, and STATE_ENDED이다.
    • 재생 목록: getCurrentTimeline()으로 가져오며, MediaItem(재생목록)에 대해 추가, 삭제 같은 작업을 할 수 있다.
    • 재생/일시중지 속성: 재생 억제 사유, isPlaying, playWhenReady
    • 재생 위치: 현 미디어항목 인덱스, isPlayingAd, 현재 재생 위치 등
    • 그 밖에도, 사용가능한 트랙 목럭, 메타데이터, 재생속도, 볼륨 등에 엑세스할 수도 있다.
  • 제어
    • 기본: play(), pause(), prepare(), stop()
    • 재생목록: addMediaItem(), removeMediaItem()
    • 현재 재생 아이템, 탐색, 위치 변경
    • 반복 모드, 셔플 모드
    • 트랙 선택 설정
    • 재생 속도

MediaSession 시작하기

생성 시 Player 구현체와 Context를 파라미터로 받습니다.

val player = ExoPlayer.Builder(context).build()
val mediaSession = MediaSession.Builder(context, player).build()
  • 자동으로 MediaSession에 있는 재생 상태를 업데이트합니다.
  • BuildersetId를 추가하여 여러 세션을 만들고 관리할 수 있습니다.

다음 경우들에 쉽게 대응할 수 있다.

  • 헤드셋/이어폰, 스마트워치, TV 등을 통한 조작
  • Google 어시스턴트에 "Ok Google, 음악 일시정지해줘" 같은 작업

이를 위해선 해당 클라이언트에 제어 권한을 부여해야 한다.

외부 클라이언트(MediaController)가 보낸 명령은 MediaSession이 수신하여, 명령어를 수행한다.

  • onConnect(): 외부 MediaController가 연결을 시도할 경우 호출된다. ContollerInfo를 보고 수락, 거부를 결정하면 된다.

  • MediaItem

    • id: 아이템 식별
    • RequestMetaData.mediaUri: 재생 uri
    • RequestMetaData.searchQuery: Google 어시스턴트 등이 사용하는 텍스트 검색어
    • MediaMetadata: 제목, 가수 와 같은 메타데이터
  • System Media Controls :

    • Media Notification : 상태바를 내렸을 때 보이는 재생기
    • Lockscreen Media Controls : 폰을 켜자마자 비밀번호를 치기 전에 크게 보이는 재생기


MediaSessionService를 사용한 백그라운드 재생

Service 생명 주기 구현

class PlaybackService : MediaSessionService() {
  private var mediaSession: MediaSession? = null

  override fun onCreate() {
    super.onCreate()
    
    // 초기화
    val player = ExoPlayer.Builder(this).build()
    mediaSession = MediaSession.Builder(this, player).build()
  }


override fun onDestroy() {
	// 리소스 해제
    mediaSession?.run {
      player.release()
      release()
      mediaSession = null
    }
    super.onDestroy()
  }

// MediaSession 할당
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? =
  mediaSession

// 앱 종료 시 필요한 적업 수행
override fun onTaskRemoved(rootIntent: Intent?) {
  pauseAllPlayersAndStopSelf()
}
  • onCreate(): MediaController가 연결을 시도하는 시점. PlayerMediaSession을 빌드하기에 최적
  • onDestroy(): 리소스 해제 필요
  • onTaskRemoved(Intent): 앱이 종료될 시 호출되는 함수로, 필요 시 재정의

Manifest 설정

권한

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />

서비스 선언

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaSessionService"/>
        <action android:name="android.media.browse.MediaBrowserService"/>
    </intent-filter>
</service>

MediaController로 재생 제어

생성

val sessionToken = SessionToken(context, ComponentName(context, PlaybackService::class.java))

val controllerFuture = MediaController.Builder(context, sessionToken).buildAsync()
controllerFuture.addListener(
  {
    // MediaController is available here with controllerFuture.get()
    mediaController = controllFuture.get()
  },
  MoreExecutors.directExecutor(),
)

또는 CoroutineScope에서


val sessionToken = SessionToken(context, ComponentName(context, PlaybackService::class.java))

val mediaController = MediaController
                         .Builder(context, sessionToken)
                         .buildAsync()
                         .await()  // Future에서 객체가 할당되기를 기다리는 코루틴 확장 함수
  • onStart()에서 sessionToken 만들기
  • MediaController를 빌더 함수로 비동기 생성.

사용

  • play(), pause(), release()
  • Player에 상태 변경 리스너 등록
  • MediaSession.Callback 등록
    • 맞춤 명령어 처리: onCustomCommand()
    • 세션에서 연결 해제 시 : onDisconnected()
  • MediaController.releaseFuture(controllerFuture) : 리소스 해제

알림

자동으로 MediaNotification을 만듦. 세션을 인식하여 동일한 세션에 연결된 다른 앱의 재생을 제어 할 수도 있음.

val mediaItem =
  MediaItem.Builder()
    .setMediaId("media-1")
    .setUri(mediaUri)
    .setMediaMetadata(
      MediaMetadata.Builder()
        .setArtist("David Bowie")
        .setTitle("Heroes")
        .setArtworkUri(artworkUri)
        .build()
    )
    .build()

mediaController.setMediaItem(mediaItem)
mediaController.prepare()
mediaController.play()

생명주기

  • Player 재생 목록에 MediaItem 인스턴스에 따라 자동 생성 및 자동 업데이트
  • 포그라운드 서비스가 실행되는 동안, 알림을 삭제할 수 없음. 삭제하려면 Player.release()Player.clearMediaItems()를 호출해야함.
  • 10분 이상 일시 중지되거나 상호작용이 없으면 소멸될 수 있음.

MediaLibraryService

기본적으로 MediaSessionService와 같음.

  • onGetSession() 메서드에서 MediaSession 대신 MediaLibrarySession를 반환
  • MediaItem을 트리 형식으로 제공해야 함.
  • Callback
    • onGetLibraryRoot(): 컨텐츠 트리의 루트 MediaItem를 요청하는 경우
    • onGetChildren(): 컨텐츠 트리의 루트 MediaItem의 하위 요소를 요청하는 경우
    • onGetSearchResult(): 특정 쿼리의 콘센츠 트리에서 결과를 요청하는 경우
profile
안녕하세요!

0개의 댓글