透過 MediaSession 控制媒體

1. 簡介

上次更新時間:2020 年 9 月 9 日

在影片播放期間新增 MediaSession 有哪些好處?

媒體工作階段是 Android 平台和媒體應用程式之間的必要連結。這項事件不僅會通知 Android 媒體正在播放,以便將媒體動作轉送至正確的工作階段,還會通知平台正在播放的內容,以及如何控制該內容。

透過應用程式公開 MediaSession 可為使用者帶來多項好處。以下列舉幾個絕佳的範例。

Google 助理

使用者可以透過語音指令與應用程式中的媒體互動,例如「暫停」、「繼續」和「下一首」。您也可以使用媒體的中繼資料,取得目前正在播放的內容。

Android TV

在大型螢幕體驗中,Android TV 應用程式可利用傳統遙控器,為支援 HDMI-CEC 的電視使用者提供服務。透過播放/暫停、停止、下一首和上一首按鈕發出的指令會傳送至應用程式。

螢幕上的媒體控制選項

從 Android 4.0 (API 級別 14) 開始,系統可以存取媒體工作階段的播放狀態和中繼資料。這項功能可讓鎖定畫面顯示媒體控制項和圖片。這項行為會因 Android 版本而異。

背景媒體

即使播放媒體的應用程式在背景執行,您仍可在上述任何情況下控制媒體。

微光 運算

您可以透過媒體提供的資料,說明正在播放的內容和控制方式,讓使用者在不同裝置之間建立連結,以便使用者以各種喜愛的方式與媒體互動。

建構項目

在本程式碼研究室中,您將擴充現有的 Exoplayer 範例,以便新增媒體工作階段支援功能。您的應用程式將會:

  • 正確反映媒體工作階段的有效狀態
  • 將媒體控制項轉送至 ExoPlayer
  • 將佇列中項目的中繼資料傳遞至媒體工作階段

課程內容

  • 媒體工作階段為何可為使用者提供更豐富的體驗
  • 如何建立媒體工作階段及管理其狀態
  • 如何將媒體工作階段連結至 ExoPlayer
  • 如何在媒體工作階段的播放佇列中加入項目中繼資料
  • 如何新增其他 (自訂) 動作

本程式碼研究室著重於 MediaSession SDK。我們不會對與本主題無關的概念和程式碼區塊多做介紹,但會事先準備好這些程式碼區塊,屆時您只要複製及貼上即可。

軟硬體需求

  • 最新版 Android Studio (3.5 以上版本)
  • 具備開發 Android 應用程式的基本知識

2. 開始設定

從哪裡開始?

我們要從 ExoPlayer 的主要示範開始。這個示範包含內含畫面上播放控制項的影片,但不會使用媒體工作階段。這對我們來說是個很好的切入點,可以開始新增這些功能!

取得 ExoPlayer 範例

為了快速上手,我們先從 ExoPlayer 範例開始。執行下列程式碼,複製 GitHub 存放區。

git clone https://github.com/google/ExoPlayer.git

開啟示範影片

在 Android Studio 中,開啟 demos/main 下的主要示範專案。

Android Studio 會提示您設定 SDK 路徑。如有任何問題,建議您按照「更新 IDE 和 SDK 工具」中的建議項目操作。

10e3b5c652186d57.png

如果系統要求您使用最新版 Gradle,請直接進行更新。

請花點時間瞭解應用程式的設計方式。請注意,這裡有兩個活動:SampleChooserActivity 和 PlayerActivity。我們會在本程式碼研究室的剩餘部分中,使用 PlayerActivity 實際播放媒體,因此請開啟這個類別,並繼續閱讀下一節。

3. 建立媒體工作階段並管理其狀態

建立媒體工作階段

開啟 PlayerActivity.java。這個類別會建立 ExoPlayer,並管理其功能,例如將影片算繪至螢幕。在這個活動中,我們會將 ExoPlayer 連結至媒體工作階段。

請在類別頂端宣告以下兩個欄位。我們會在本節中使用這些欄位。

private MediaSessionCompat mediaSession;
private MediaSessionConnector mediaSessionConnector;

您必須將「extension-mediasession」專案依附元件新增至模組層級的 build.gradle,以便支援「Module: demo」:

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

請注意,如果您將滑鼠游標懸停在解決 MediaSessionConnector 時發生的錯誤上,Android Studio 會自動協助您新增這個依附元件:

60055e4ad54fbb97.png

最後,請新增下列內容來解決類別匯入問題:

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. 請確認已選取「demo」,以便從 Android Studio 工具列執行。cb1ec4e50886874f.png
  3. 按一下 Android Studio 工具列中的 9d8fb3a9ddf12827.png
  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 如何檢視媒體工作階段。特別是,您可以查看動作欄位,瞭解系統支援哪些動作。這裡顯示的數字是動作 ID 的組合,如 PlaybackState 物件中所宣告。如要查看媒體工作階段的執行情形:adb shell dumpsys media_session
  2. 如果使用的是實體裝置,且裝置有麥克風,請嘗試喚醒 Google 助理並發出語音指令,例如「暫停」。「繼續」。「快轉 1 分鐘」。

b8dda02a6fb0f6a4.png在 Android TV 上執行的 ExoPlayer 範例。

4. 在播放佇列中加入項目的中繼資料

我們現在可以擴充媒體工作階段的支援功能,我們先前已在 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(player.getCurrentMediaItem().mediaMetadata.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 參數會對應至播放佇列中該索引的項目。

新增中繼資料後,你可以測試 Google 助理是否能瞭解正在播放的內容。在 Android TV 上播放影片時,請喚醒 Google 助理,然後詢問「現在播放的是什麼?」

6c7fc0cb853cbc38.png

5. 自訂動作

也許您的播放器不支援某些動作,或者您想加入更多支援功能?接下來,我們將進一步探討先前在 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 訊息,瞭解這項呼叫如何進入您的程式碼。

6. 恭喜

恭喜,您已成功將媒體工作階段新增至範例!

您可以透過以下方式獲得大量功能:

  • 新增媒體工作階段、
  • 將媒體工作階段連結至 ExoPlayer 的例項
  • 新增中繼資料和其他動作。

您現在已瞭解豐富媒體應用程式所需的關鍵步驟,並能為使用者提供更多元化的體驗!

最終 備註

這個程式碼研究室是根據 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.+'

參考文件