透過 MediaSession 控制媒體

1. 簡介

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

在影片播放中加入 MediaSession 有哪些好處?

媒體工作階段是 Android 平台和媒體應用程式的必要連結。Android 不僅會告知 Android 正在播放媒體,藉此將媒體動作轉送至正確的工作階段,還會告知平台正在播放的內容及控制方式。

透過應用程式顯示 MediaSession 可以帶給使用者各種好處。以下有幾個絕佳的範例

Google 助理

使用者可以透過語音指令 (例如「暫停」),輕鬆與應用程式中的媒體互動。「繼續」和「下一步」媒體的中繼資料也可用於取得目前播放中內容的相關資訊。

Android TV

在大螢幕上,Android TV 應用程式可以在支援 HDMI-CEC 的電視上使用傳統的遙控器。透過播放/暫停、停止、下一個和上一個按鈕發出的指令會轉發至您的應用程式。

畫面上的媒體控制項

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

背景媒體

即使應用程式正在背景執行,仍可透過上述任何一種情境操控媒體。

環境 運算

將您的媒體與正在播放的內容及其控制方式分享,就能在裝置之間搭起橋樑,讓使用者以多種喜愛的方式與媒體互動。

建構項目

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

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

課程內容

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

本程式碼研究室著重於 MediaSession SDK。我們不會討論不相關的概念和程式碼區塊 (包括 ExoPlayer 實作的詳細資訊),但會事先提供程式碼區塊,您只要複製及貼上即可。

軟硬體需求

  • 最新版 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

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. 請務必選取「示範」會獲選從 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) 支援的功能。

新增時間軸佇列導覽工具

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.+'

參考文件