Routing between devices - MediaRouter overview Android


MediaRouter overview

https://developer.android.com/guide/topics/media/mediarouter

앱 내에서 미디어 라우터 프레임워크를 사용하려면 MediaRouter 객체의 인스턴스를 받아야 하고 라우팅 이벤트를 듣기위해 MediaRouter.Callback 객체를 붙인다. 콘텐트에 연계된 MediaRouteProvider를 통해 미디어 루트로 콘텐트를 전달한다. (블루투스 출력 장치와 같은 몇몇 특별한 상황은 제외) 그림 1 장치 사이에 콘텐트를 루트하는데 사용되는 클래스의 하이레벨 뷰를 보여준다.


그림 1. 앱에의해 키 미디어 라우터 클래스의 오버뷰

일러두기: 구글 캐스트 장치를 지원하려면 Cast SDK를 사용하고 앱을 Cast 센더로 빌드한다. MediaRouter 프레임워크를 직접적으로 사용하는 대신 Cast 문서의 방향에 따른다.

미디어 라우트 버튼

안드로이드 앱은 미디어 라우팅을 제어하기 위해 미디어 라우트 버튼을 사용하는 것이 좋다. 미디어 라우터 프레임워크는 버튼을 위한 표준 인터페이스를 제공하여 사용자가 인식하고 라우팅을 사용할 수 있게 한다. 미디어 라우트 버튼은 그림 2에서 처럼 보통 앱의 액션바의 오른쪽에 위치한다.


그림2. 액션바의 미디어 라우트 버튼

사용자가 미디어 라우트 버튼을 누를 때 가용한 미디어 라우트가 리스트로 그림 3에서 처럼 보여진다
그림 3. 가용한 미디어 라우트 리스트 미디어 라우트 버튼을 누른 후 보여짐

미디어 라우트 버튼을 생성하려면 다음 단계를 따른다.
1. AppCompatActivity 를 사용한다
2. 미디어 라우트 버튼 메뉴 아이템을 정의한다.
3. MediaRouteSender 를 생성한다.
4. 미디어 라우트 버튼을 액션바에 추가한다.
5. 액티비티의 라이프 사이클 내에 MediaRouter.Callback 메소드를 생성하고 관리한다.

AppCompatActivity 사용하기

액티비티 내에 미디어 라우터 프레임워크를 사용할 때 AppCompatActivity 로 부터 액티비티를 확장하고 android.support.v7.media 패키지를 임포트한다. v7-appcompat 과 v7-mediarouter 지원라이브러리를 추가한다. 프로젝트에 지원 라이브러리 추가하기에 대한 더 많은 정보는 Support Library Setup을 살펴본다.

주의: 미디어 라우터 프레임워크의 android.support.v7.media 구현을 사용하는지 확인한다. 과거의 android.media 패키지를 사용하지 않는다.

미디어 라우트 버튼 메뉴 아이템 정의하기

미디어 라우트 버튼을 위한 메뉴 아이템을 정의하는 xml파일을 생성한다. 아이템의 액션은 MediaRouteActionProvider 클래스이어야 한다. 여기에 예시가 있다.

<?xml version="1.0" encoding="utf-8"?>
<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:actionProviderClass="android.support.v7.app.MediaRouteActionProvider" app:showAsAction="always" />
</menu>

MediaRouteSelector 생성하기

미디어 라우트 버튼 메뉴에서 보여질 루트는 MediaRouteSelector 에 의해 판단된다. 액티비티를 AppCompatActivity 로 부터 파생하고 다음 코드 샘플에서 보여지는 onCreate() 메소드로부터 MediaRouteSelector.Builder 를 호출해 셀렉터를 빌드한다. 셀렉터는 클래스 변수로 저장되고 허용되는 루트 형식은 MediaControlIntent 형식을 추가하여 지정한다.

class MediaRoutePlaybackActivity: AppCompatActivity() {
  private var mSelector: MediaRouteSelector? = null
  override fun onCreate(savedInstanceState:Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    // 앱이 지원하는 루트의 형식에 맞는 루트 셀렉터 생성하기
    mSelector = MediaRouteSelector.Builder()
      .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK).build()
  }
}

대부분의 어플리케이션에서 필요한 루트 형식은 CATEGORY_REMOTE_PLAYBACK 이다. 이 루트 형식은 앱을 실행하는 장치를 리모트 컨트롤로 다룬다. 연결된 리시버 장치는 콘텐트 데이터 리터리벌, 디코딩, 플레이백을 다룬다. 이 것이 크롬캐스트와 같은 구글 캐스트를 지원하는 앱이 작동하는 방식이다.

몇몇 제조사는 세컨더리 아웃풋 이라 불리는 특별한 루팅 옵션을 지원한다. 이 루팅으로 미디어 앱은 비디오나 음악을 직접적으로 리트리브, 렌더, 스트림을 하여 선택된 리모트리시버 장치의 화면에 전달하거나 스피커로 전달한다. 이들 장치의 발견이나 선택을 허용하려면, CATEGORY_LIVE_AUDIO또는 CATEGORY_LIVE_VIDEO 를 MediaRouteSelector 로 카테고리를 전달한다. Presentation 다이얼로그를 생성하고 다뤄야 한다.

액션바에 미디어 라우트 버튼 추가하기

미디어 라우트 메뉴와 정의된 MediaRouteSelector 와 함께 액티비티에 미디어 루트 버튼을 추가할 수 있다. 옵션 메뉴에 추가하기 위해 액티비티의 각각의 onCreateOptionsMenu() 메소드를 오버라이드한다.

override fun onCreateOptionsMenu(menu: Menu): Boolean {
  super.onCreateOptionsMenu(menu)

  // 메뉴를 인플레이트하고 미디어 라우터 액션 프로바이더를 설정한다.
  menuInflator.inflate(R.menu.sample_media_router_menu, menu)

  // MediaRouteSelector 를 메뉴아이템에 붙인다
  val mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item)
  val mediaRouteActionProvider = MenuItemCompat.getActionProvider(mediaRouteMenuItem) as MediaRouteActionProvider

  // onCreate() 에서 만든 MediaRouteSelector 를 붙인다.
  mSelector?.also(mediaRouteActionProvider::setRouteSelector)

  // 메뉴를 보여주려면 true 를 반환한다.
  return true
}

앱 내에서 액션바를 구현함에 대한 더 자세한 정보는 Action Bar 개발자 가이드를 살펴본다.

미디어 라우트 버튼을 MediaRouteButton 으로 모든 뷰에 추가할 수 있다. setRouteSelector() 메소드를 사용하는 버튼에 MediaRouteSelector 를 붙여야 한다. 어플리케이션내에 미디어 루트 버튼을 연결하는 가이드라인을 위해 Google Cast Design Checklist 를 살펴본다.

미디어 라우터 콜백

같은 장치상의 모든 앱은 단일 MediaRouter 인스턴스를 공유하고 그 것으로 전달한다. (앱의 MediaRouteSelector 에 의해 앱당 필터된다) 각 액티비티는 MediaRouter 로 MediaRouter.Callback 메소드의 구현을 사용해 MediaRouter 와 교신한다. MediaRouter 는 사용자가 선택하고, 변경하고 루트에서 접속종료할때 콜백 메소드를 호출한다.

콜백에서의 루팅 이벤트에 대한 정보를 얻기 위해 오버라이드 할 수 있는 몇가지 메소드가 있다. 최소한 MediaRouter.Callback 클래스의 구현은 onRouteSelected() 와 onRouteUnselected() 를 오버라이드 하는 것이 좋다.

MediaRouter 가 공유된 리소스이므로 일반 액티비티 라이프사이클 콜백에 반응하는 MediaRouter 콜백을 관리해야 할 필요가 있다.
- 액티비티가 생성될 때 (onCreate(Bundle)) MediaRouter 에 대한 포인터를 잡고 앱의 라이프타임동안 이 것을 잡고 있는다.
- 액티비티가 보여질 때 (onStart()) MediaRouter에 콜백을 붙이고 사라질 때 (onStop()) 떼어낸다.

다음 코드는 콜백 객체를 생성하고 저장하는 방법, MediaRouter 의 인스턴스를 얻는 방법, 콜백을 관리하는 방법을 보여준다. onStart() 에서 콜백을 붙일 때 CALLBACK_FLAG_REQUEST_DISCOVERY 플래그를 사용하는 것을 살펴본다. 이를 통해 MediaRouterSelector 가 가용한 루트의 미디어 루트 버튼의 리스트를 새로고침할 수 있게 해준다.

class MediaRouterPlaybackActivity : AppCompatActivity() {
  private var mMediaRouter: MediaRouter? = null
  private var mSelector: MediaRouteSelector? = null

  // 현재 선택된 루트를 잡는 변수와 플레이백 클라이언트
  private var mRoute: MediaRouter.RouteInfo?=null
  private var mRemotePlaybackClient: RemotePlaybackClient?=null

  // 콜백 객체와 그 메소드를 정의하고 클래스 변수에 객체를 저장한다.
  private val mMediaRouterCallback = object: MediaRouter.Callback() {
    override fun onRouteSelected(router: MediaRouter, route: MediaRouter.RouteInfo) {
      Log.d(TAG, "onRouteSelected: route=$route")
      if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
        // Stop local playback (if necessary)
        // ...
        // Save the new route
        mRoute = route

        // Attach a new playback client
        mRemotePlaybackClient = RemotePlaybackClient(this@MediaRouterPlaybackActivity, mRoute)
        // Start remote playback(if necessary)

      }
    }
    override fun onRouteUnselected(router: MediaRouter, route: MediaRouter.RouteInfo, reason : Int) {
      Log.d(TAG, "onRouteUnselected: route=$route")
      if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
        // Changed route: tear down previous client
        mRoute?.also {
          mRemotePlaybackClient?.release()
          mRemotePlaybackClient = null
        }
        // Save the new route
        mRoute = route
        when (reason) {
          MediaRouter.UNSELECT_REASON_ROUTE_CHANGED -> {
            // Resume local playback (if necessary)
            // ...
          }
        }
      }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)
        // Get the media router service
        mMediaRouter = MediaRouter.getInstance(this)
    }

    override fun onStart() {
      mSelector?.also { selector ->
         mMediaRouter?.addCallback(selector, mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY)
      }
      super.onStart()
    }

    override fun onStop() {
      mMediaRouter?.removeCallback(mMediaRouterCallback)
      super.onStop()
    }
    ...
}

미디어 라우터 프레임워크는 MediaRouteDiscoveryFragment 클래스 또한 지원하는데 액티비티를 위한 콜백을 추가하고 제거하는 것을 관리해준다.

일러두기: 만약 음악 재생 앱을 작성하고 앱이 백그라운드에서 음악을 재생하기를 원한다면 반드시 플레이백을 위해 서비스로 작성하고 서비스의 라이프사이클 콜백으로부터 미디어 라우터 프레임워크를 호출한다.

리모트 플레이백 루트 제어하기

리모트 플레이백 루트를 선택할 때 앱은 리모트 컨트롤과 같이 행동한다. 루트의 다른 끝에 있는 장치는 데이터를 얻고, 디코딩하고, 플레이백을 모두처리한다. 앱의 UI내의 컨트롤들은 RemotePlaybackClient 객체를 사용해 장치리시버와 교신한다.

RemotePlaybackClient 클래스는 콘텐트 플레이백을 위한 추가적인 메소드를 제공한다. 여기에 RemotePlaybackClient 클래스로부터 핵심 플레이백 메소드의 몇몇이 있다.
- play() - 특정 미디어 파일을 재생한다. Uri로 지정된다.
- pause() - 현 재생 미디어 트랙을 일시정지한다.
- resume() - 일시정지 명령 이후에 현 트랙을 재생을 지속한다.
- seek() - 현 트랙의 특정 위치로 이동한다.
- release() - 앱에서 원격 플레이백 장치로의 연결을 종료한다.

이들 메소드를 앱에서 제공되는 플레이백 컨트롤로의 붙이기 액션에 사용할 수 있다. 이들메소드의 대부분은 콜백 객체를 포함한 메소드를 허용하고 그래서 플레이백 작업이나 컨트롤 요청의 프로그래스를 모니터할 수 있다.

RemotePlaybackClient 클래스는 또한 플레이 백과 미디어 큐의 관리를 위한 다중 미디어 아이템 큐잉을 지원한다.

샘플 코드

안드로이드 BasicMediaRouter 와 Android MediaRouter 샘플은 추가적인 MediaRouter API의 사용을 보여준다.





덧글

댓글 입력 영역