MediaSession을 통한 미디어 제어

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

최종 업데이트: 2020년 1월 22일

동영상 재생과 관련하여 MediaSession 추가의 이점은 무엇인가요?

미디어 세션은 Android 플랫폼과 미디어 앱 간의 필수 링크입니다. 미디어 작업을 올바른 세션으로 전달할 수 있도록 Android에 미디어가 재생되고 있다고 알릴 뿐 아니라 플랫폼에 재생 중인 항목과 제어 방법도 알립니다.

앱을 통해 MediaSession을 노출하면 사용자가 다양한 이점을 누릴 수 있습니다. 다음은 몇 가지 좋은 예입니다.

Google 어시스턴트

사용자는 "일시중지해 줘", "다시 시작해 줘", "다음" 등의 음성 명령을 통해 앱에서 미디어와 쉽게 상호작용할 수 있습니다. 미디어의 메타데이터를 사용하여 현재 재생 중인 콘텐츠에 관한 답변도 제공합니다.

Android TV

대형 화면 환경에서 Android TV용 앱은 HDMI-CEC를 지원하는 TV 사용자를 위해 기존의 리모컨을 활용할 수 있습니다. 재생/일시중지, 중지, 다음, 이전 버튼으로 실행된 명령어가 앱에 전달됩니다.

화면 미디어 컨트롤

Android 4.0(API 수준 14)부터 시스템은 미디어 세션의 재생 상태와 메타데이터에 액세스할 수 있습니다. 화면 미디어 컨트롤 기능으로 잠금 화면에서 미디어 컨트롤과 앨범 이미지를 표시합니다. 이 동작은 Android 버전에 따라 다릅니다.

백그라운드 미디어

미디어를 재생하는 앱이 백그라운드에서 실행되고 있더라도 미디어를 제어할 수 있습니다.

앰비언트 컴퓨팅

재생 중인 항목 및 제어 방법에 관한 데이터와 함께 미디어를 노출하면 기기 간에 연결되므로 사용자가 원하는 다양한 방식으로 미디어와 상호작용할 수 있습니다.

빌드할 항목

이 Codelab에서는 기존 Exoplayer 샘플을 확장하여 미디어 세션 지원을 추가합니다. 여러분의 앱은 다음과 같이 됩니다.

  • 미디어 세션의 활성 상태를 올바르게 반영합니다.
  • ExoPlayer로 미디어 컨트롤을 전달합니다.
  • 대기열에 있는 항목의 메타데이터를 미디어 세션에 전달합니다.

학습 내용

  • 미디어 세션이 사용자에게 더 풍부한 환경을 제공하는 이유
  • 미디어 세션을 만들고 상태를 관리하는 방법
  • 미디어 세션을 ExoPlayer에 연결하는 방법
  • 미디어 세션의 재생 대기열에 항목 메타데이터를 포함하는 방법
  • 다른(맞춤) 작업을 추가하는 방법

이 Codelab에서는 MediaSession SDK에 중점을 둡니다. ExoPlayer 구현에 관한 세부정보를 비롯하여 관련이 없는 개념 및 코드 블록은 다루지 않지만 간단히 복사하여 붙여넣을 수 있도록 제공됩니다.

필요한 항목

  • 최신 버전의 Android 스튜디오(3.5 이상)
  • Android 애플리케이션 개발에 관한 기본 지식

시작 지점

ExoPlayer의 기본 데모에서 시작합니다. 기본 데모는 화면 재생 컨트롤이 있는 동영상을 포함하지만 처음부터 미디어 세션을 사용하지는 않습니다. 기본 데모를 자세히 알아보고 미디어 세션을 추가로 다룹니다.

ExoPlayer 샘플 가져오기

우선 ExoPlayer 샘플부터 시작해 보겠습니다. 아래 링크에서 GitHub 저장소를 클론합니다.

ExoPlayer 샘플 클론

데모 열기

Android 스튜디오에서 demos/main 아래에 있는 기본 데모 프로젝트를 엽니다.

Android 스튜디오에서 SDK 경로를 설정하라는 메시지를 표시합니다. 문제가 발생하면 IDE 및 SDK 도구 업데이트 권장사항을 따르는 것이 좋습니다.

최신 Gradle 버전을 사용하라는 메시지가 표시되면 계속 진행하며 업데이트합니다.

잠시 시간을 내어 앱의 설계 방식을 기본적으로 파악합니다. 활동은 SampleChooserActivity, PlayerActivity 두 가지가 있습니다. 미디어가 실제로 재생되는 PlayerActivity에서 Codelab의 나머지 부분이 진행되므로 이 클래스를 열고 다음 섹션으로 이동합니다.

미디어 세션 만들기

PlayerActivity.java를 엽니다. 이 클래스는 ExoPlayer를 만들고 화면에 동영상을 렌더링하는 작업과 같은 기능을 관리합니다. 이 활동에서 ExoPlayer를 미디어 세션에 연결합니다.

클래스 상단에서 다음 두 필드를 선언합니다. 이 섹션 전체에서 두 필드를 사용합니다.

private MediaSessionCompat mediaSession;
private MediaSessionConnector mediaSessionConnector;

'Module: demo'의 모듈 수준 build.gradle에 'extension-mediasession' 프로젝트 종속 항목을 추가해야 합니다.

implementation project(path: ':extension-mediasession')

Android 스튜디오를 사용하면 MediaSessionConnector를 결정하면서 오류 위로 마우스를 가져가는 경우 자동으로 'extension-mediasession' 프로젝트 종속 항목을 추가할 수 있습니다.

마지막으로 다음을 추가하여 가져온 클래스를 결정합니다.

import android.support.v4.media.session.MediaSessionCompat;
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;

활동이 만들어지면 미디어 세션과 ExoPlayer 사이의 중개 역할을 하는 미디어 세션 커넥터와 미디어 세션을 만듭니다.

미디어 세션 커넥터와 미디어 세션을 삽입할 가장 좋은 위치도 ExoPlayer이 만들어지는 곳입니다. 데모 앱에서 코드를 initializePlayer() 끝에 추가할 수 있습니다. 플레이어가 인스턴스화된 후에 이 로직을 추가해야 합니다.

private void initializePlayer() {
  if (player == null) {
    ...
    player = ...
    ...
    mediaSession = new MediaSessionCompat(this, "sample");
    mediaSessionConnector = new MediaSessionConnector(mediaSession);
    mediaSessionConnector.setPlayer(player);
  }
  ...
}

미디어 세션 해제

더 이상 필요하지 않을 때 미디어 세션을 해제합니다. releasePlayer()에서 ExoPlayer를 해제할 때 다음 코드도 포함하여 해제할 수 있습니다.

private void releasePlayer() {
  if (mediaSession != null) {
    mediaSession.release();
  }
  ...
}

미디어 세션 상태 관리

미디어 세션을 인스턴스화했으므로 사용자가 활동과 상호작용할 때 상태가 올바르게 반영되는지 확인해야 합니다.

사용자가 활동을 시작할 때 미디어 세션이 활성화되어야 합니다.

@Override
public void onStart() {
  ...
  if (mediaSession != null) {
    mediaSession.setActive(true);
  }
}

애플리케이션은 백그라운드에서 미디어를 재생하지 않으므로 사용자가 활동을 종료할 때 미디어 세션이 비활성화되는지 확인해야 합니다.

@Override
public void onStop() {
  super.onStop();
  if (mediaSession != null) {
    mediaSession.setActive(false);
  }
  ...
}

데모 실행

  1. Android 기기를 연결하거나 에뮬레이터를 시작합니다.
  2. Android 스튜디오 툴바에서 실행되도록 '데모'가 선택되어 있는지 확인합니다.
  3. Android 스튜디오 툴바에서 을 클릭합니다.
  4. 기기에서 앱이 실행되면 재생할 동영상 스트림을 선택합니다.
  5. 재생이 시작되면 다음 adb 명령어를 사용하여 미디어 세션을 제어하는 방법을 알아봅니다.
    adb shell media dispatch pause
    adb shell media dispatch play
    adb shell media dispatch play-pause
    adb shell media dispatch fast-forward
    adb shell media dispatch rewind
  1. 다음을 실행하여 Android에서 미디어 세션을 확인하는 방법도 알아봅니다.
    adb shell dumpsys media_session
  2. 마이크가 있는 실제 기기를 사용하는 경우 Google 어시스턴트를 호출하여 다음과 같은 음성 명령을 실행해 봅니다.
    "일시중지해 줘."
    "다시 시작해 줘."
    "1분 빨리 감기 해 줘."


Android TV에서 실행되는 ExoPlayer 샘플

이제 이전에 initializePlayer()에 MediaSessionConnector를 만든 미디어 세션의 지원되는 기능을 확장할 수 있습니다.

TimelineQueueNavigator 추가

ExoPlayer는 미디어 구조를 타임라인으로 나타냅니다. 작동 방식에 관한 자세한 내용은 ExoPlayer의 타임라인 객체를 참고하세요. 미디어 구조를 활용하면 콘텐츠가 변경될 때 알림을 받고 요청 시 현재 재생 중인 콘텐츠의 메타데이터를 노출할 수 있습니다.

이를 위해 TimelineQueueNavigator를 정의합니다. initializePlayer()에서 인스턴트화된 MediaSessionConnector를 찾고 mediaSession이 초기화된 후에 TimelineQueueNavigator 구현을 추가합니다.

mediaSessionConnector.setQueueNavigator(new TimelineQueueNavigator(mediaSession) {
  @Override
  public MediaDescriptionCompat getMediaDescription(Player player, int windowIndex) {
    return new MediaDescriptionCompat.Builder()
            .setTitle("MediaDescription title")
            .setDescription("MediaDescription description for " + windowIndex)
            .setSubtitle("MediaDescription subtitle")
            .build();
  }
});

다음을 추가하여 가져온 클래스를 결정합니다.

import android.support.v4.media.MediaDescriptionCompat;
import com.google.android.exoplayer2.ext.mediasession.TimelineQueueNavigator;

windowIndex 매개변수가 재생 대기열의 해당 색인 항목과 일치하는지 확인합니다.

일부 메타데이터를 추가했으므로 어시스턴트가 재생 중인 항목을 이해하는지 테스트할 수 있습니다. Android TV에서 동영상을 재생하는 동안 어시스턴트를 호출하여 "지금 재생 중인 거 뭐야?"라고 묻습니다.

플레이어가 일부 작업을 지원하지 않거나 더 많은 지원을 포함하고 싶다면 이전에 initializePlayer()에 MediaSessionConnector를 만든 미디어 세션을 좀 더 자세히 살펴보겠습니다.

지원되는 작업 선언

mediaSessionConnector.setEnabledPlaybackActions()를 사용하여 미디어 세션에서 지원하길 바라는 작업을 맞춤설정해 보세요.

전체 세트는 다음과 같습니다.

mediaSessionConnector.setEnabledPlaybackActions(
        PlaybackStateCompat.ACTION_PLAY_PAUSE
                | PlaybackStateCompat.ACTION_PLAY
                | PlaybackStateCompat.ACTION_PAUSE
                | PlaybackStateCompat.ACTION_SEEK_TO
                | PlaybackStateCompat.ACTION_FAST_FORWARD
                | PlaybackStateCompat.ACTION_REWIND
                | PlaybackStateCompat.ACTION_STOP
                | PlaybackStateCompat.ACTION_SET_REPEAT_MODE
                | PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE
);

데이터가 플랫폼에 노출되는 방식을 다시 확인해 보겠습니다.

  1. 이전과 마찬가지로 동영상을 시작합니다.
  2. 다음을 실행하여 Android에서 미디어 세션의 메타데이터를 확인하는 방법을 알아봅니다.
    adb shell dumpsys media_session
  3. 메타데이터가 포함된 행을 찾아 제목과 설명이 포함되어 있고 com.google.android.exoplayer2.demo/sample이 연결되어 있는지 확인합니다.

다른 작업 추가

추가 작업으로 미디어 세션을 확장할 수 있습니다. 이 섹션에서는 자막 지원만 추가합니다.

자막 지원

미디어 세션에 자막 지원을 추가하면 사용자가 음성으로 자막을 전환할 수 있습니다. 미디어 세션 커넥터를 초기화한 위치에 다음을 추가합니다.

mediaSessionConnector.setCaptionCallback(new MediaSessionConnector.CaptionCallback() {
      @Override
      public void onSetCaptioningEnabled(Player player, boolean enabled) {
        Log.d("MediaSession", "onSetCaptioningEnabled: enabled=" + enabled);
      }

      @Override
      public boolean hasCaptions(Player player) {
        return true;
      }

      @Override
      public boolean onCommand(Player player, ControlDispatcher controlDispatcher, String command, Bundle extras, ResultReceiver cb) {
        return false;
      }
    }
);

마지막으로 누락된 가져오기를 결정합니다.

Android TV에서 Google 어시스턴트를 호출하고 "자막 사용 설정해 줘"라고 말하여 테스트할 수 있습니다. Logcat에서 메시지를 확인하여 메시지가 코드를 어떻게 호출하는지 살펴봅니다.

축하합니다. 미디어 세션을 샘플에 추가했습니다.

다음을 통해 엄청나게 다양한 기능을 사용할 수 있습니다.

  • 미디어 세션 추가
  • 미디어 세션을 ExoPlayer의 인스턴스에 연결
  • 메타데이터와 다른 작업 추가

미디어 앱을 개선하고 사용자에게 더 다양한 환경을 제공하는 데 필요한 주요 단계를 알아봤습니다.

마지막 참고사항

이 Codelab은 ExoPlayer 소스 코드의 샘플에 기반하여 빌드되었습니다. 소스에서 ExoPlayer를 사용할 필요는 없으며 대신 ExoPlayer 및 MediaSessionConnector의 종속 항목을 가져와서 최신 버전으로 더 쉽게 최신 상태를 유지하는 것이 좋습니다.

이렇게 하려면 다음과 같은 프로젝트 종속 항목을 대체하기만 하면 됩니다.

implementation project(modulePrefix + 'library-core')
implementation project(path: ':extension-mediasession')

Maven 저장소에서 가져옵니다. 예는 다음과 같습니다.

implementation 'com.google.android.exoplayer:exoplayer-core:2.+'
implementation 'com.google.android.exoplayer:extension-mediasession:2.+'

참조 문서