鴻蒙OS 媒體會話開發(fā)指導

2020-09-18 14:39 更新

場景介紹

AVSession 框架有四個主要的類,控制著整個框架的核心,下圖簡單的說明四個核心媒體框架控制類的關(guān)系。

img

  • AVBrowser

媒體瀏覽器,通常在客戶端創(chuàng)建,成功連接媒體服務后,通過媒體控制器 AVBrowse r向服務端發(fā)送播放控制指令。

其主要流程為,調(diào)用 connect 方法向 AVBrowserService 發(fā)起連接請求,連接成功后在回調(diào)方法 AVConnectionCallback.onConnected 中發(fā)起訂閱數(shù)據(jù)請求,并在回調(diào)方法 AVSubscriptionCallback.onAVElementListLoaded 中保存請求的媒體播放數(shù)據(jù)。

  • AVController

媒體控制器,在客戶端 AVBrowser 連接服務成功后的回調(diào)方法 AVConnectionCallback.onConnected 中創(chuàng)建,用于向 Service 發(fā)送播放控制指令,并通過實現(xiàn) AVControllerCallback 回調(diào)來響應服務端媒體狀態(tài)變化,例如曲目信息變更、播放狀態(tài)變更等,從而完成UI刷新。

  • AVBrowser

Service

媒體瀏覽器服務,通常在服務端,通過媒體會話 AVSession 與媒體瀏覽器建立連接,并通過實現(xiàn) Player 進行媒體播放。其中有兩個重要的方法:

  1. onGetRoot,處理從媒體瀏覽器 AVBrowser 發(fā)來的連接請求,通過返回一個有效的 AVBrowserRoot 對象表示連接成功;
  2. onLoadAVElementList,處理從媒體瀏覽器 AVBrowser 發(fā)來的數(shù)據(jù)訂閱請求,通過 AVBrowserResult.sendAVElementList(List<AVElement>) 方法返回媒體播放數(shù)據(jù)。

  • AVSession

媒體會話,通常在 AVBrowserService 的 onStart 中創(chuàng)建,通過 setAVToken 方法設置到 AVBrowserService 中,并通過實現(xiàn) AVSessionCallback 回調(diào)來接收和處理媒體控制器 AVController 發(fā)送的播放控制指令,如播放、暫停、跳轉(zhuǎn)至上一曲、跳轉(zhuǎn)至下一曲等。

除了上述四個類,AVSession 框架還有 AVElement。

  • AVElement

媒體元素,用于將播放列表從 AVBrowserService 傳遞給 AVBrowser。

接口說明

接口名 描述
AVBrowser(Context context, ElementName name, AVConnectionCallback callback, PacMap options) 構(gòu)造 AVBrowser 實例,用于瀏覽 AVBrowserService 提供的媒體數(shù)據(jù)。
void connect() 連接 AVBrowserService。
void disconnect() 與 AVBrowserService 斷開連接。
boolean isConnected() 判斷當前是否已經(jīng)與 AVBrowserService 連接。
ElementName getElementName() 獲取 AVBrowserService 的ohos.bundle.ElementName實例。
String getRootMediaId() 獲取默認媒體 id。
PacMap getOptions() 獲取 AVBrowserService 提供的附加數(shù)據(jù)。
AVToken getAVToken() 獲取媒體會話的令牌。
void getAVElement(String mediaId, AVElementCallback callback) 輸入媒體的 id,查詢對應的 ohos.media.common.sessioncore.AVElement 信息,查詢結(jié)果會通過 callback 返回。
void subscribeByParentMediaId(String parentMediaId, AVSubscriptionCallback callback) 查詢指定媒體 id 包含的所有媒體元素信息,并訂閱它的媒體信息更新通知。
void subscribeByParentMediaId(String parentMediaId, PacMap options, AVSubscriptionCallback callback) 基于特定于服務的參數(shù)來查詢指定媒體 id 中的媒體元素的信息,并訂閱它的媒體信息更新通知。
void unsubscribeByParentMediaId(String parentMediaId) 取消訂閱對應媒體 id 的信息更新通知。
void unsubscribeByParentMediaId(String parentMediaId, AVSubscriptionCallback callback) 取消訂閱與指定 callback 相關(guān)的媒體 id 的信息更新通知。
接口名 描述
abstract AVBrowserRoot onGetRoot(String callerPackageName, int clientUid, PacMap options) 回調(diào)方法,用于返回應用程序的媒體內(nèi)容的根信息,在 AVBrowser.connect()后進行回調(diào)。
abstract void onLoadAVElementList(String parentMediaId, AVBrowserResult result) 回調(diào)方法,用于返回應用程序的媒體內(nèi)容的結(jié)果信息 AVBrowserResult,其中包含了子節(jié)點的 AVElement 列表,在 AVBrowser 的方法 subscribeByParentMediaId 或 notifyAVElementListUpdated 執(zhí)行后進行回調(diào)。
abstract void onLoadAVElement(String mediaId, AVBrowserResult result) 回調(diào)方法,用于獲取特定的媒體項目 AVElement 結(jié)果信息,在 AVBrowser.getAVElement 方法執(zhí)行后進行回調(diào)。
AVToken getAVToken() 獲取 AVBrowser 與 AVBrowserService 之間的會話令牌。
void setAVToken(AVToken token) 設置 AVBrowser 與 AVBrowserService 之間的會話令牌。
final PacMap getBrowserOptions() 獲取 AVBrowser 在連接 AVBrowserService 時設置的服務參數(shù)選項。
final AVRemoteUserInfo getCallerUserInfo() 獲取當前發(fā)送請求的調(diào)用者信息。
void notifyAVElementListUpdated(String parentMediaId) 通知所有已連接的 AVBrowser 當前父節(jié)點的子節(jié)點已經(jīng)發(fā)生改變。
void notifyAVElementListUpdated(String parentId, PacMap options) 通知所有已連接的 AVBrowser 當前父節(jié)點的子節(jié)點已經(jīng)發(fā)生改變,可設置服務參數(shù)。
接口名 描述
AVController(Context context, AVToken avToken) 構(gòu)造 AVController 實例,用于應用程序與 AVSession 進行交互以控制媒體播放。
static boolean setControllerForAbility(Ability ability, AVController controller) 將媒體控制器注冊到 ability 以接收按鍵事件。
boolean setAVControllerCallback(AVControllerCallback callback) 注冊一個回調(diào)以接收來自 AVSession 的變更,例如元數(shù)據(jù)和播放狀態(tài)變更。
boolean releaseAVControllerCallback(AVControllerCallback callback) 釋放與 AVSession 之間的回調(diào)實例。
List<AVQueueElement> getAVQueueElement() 獲取播放隊列。
CharSequence getAVQueueTitle() 獲取播放隊列的標題。
AVPlaybackState getAVPlaybackState() 獲取播放狀態(tài)。
boolean dispatchAVKeyEvent(KeyEvent keyEvent) 應用分發(fā)媒體按鍵事件給會話以控制播放。
void sendCustomCommand(String command, PacMap pacMap, GeneralReceiver receiverCb) 應用向 AVSession 發(fā)送自定義命令,參考ohos.media.common.sessioncore.AVSessionCallback.onCommand。
IntentAgent getAVSessionAbility() 獲取啟動用戶界面的IntentAgent。
AVToken getAVToken() 獲取應用連接到會話的令牌。此令牌用于創(chuàng)建媒體播放控制器。
void adjustAVPlaybackVolume(int direction, int flags) 調(diào)節(jié)播放音量。
void setAVPlaybackVolume(int value, int flags) 設置播放音量,要求支持絕對音量控制。
PacMap getOptions() 獲取與此控制器連接的AVSession的附加數(shù)據(jù)。
long getFlags() 獲取 AVSession 的附加標識,標記在 AVSession 中的定義。
AVMetadata getAVMetadata() 獲取媒體資源的元數(shù)據(jù)ohos.media.common.AVMetadata。
AVPlaybackInfo getAVPlaybackInfo() 獲取播放信息。
String getSessionOwnerPackageName() 獲得 AVSession 實例的應用程序的包名稱。
PacMap getAVSessionInfo() 獲取會話的附加數(shù)據(jù)。
PlayControls getPlayControls() 獲取一個 PlayControls 實例,將用于控制播放,比如控制媒體播放、停止、下一首等。
接口名 描述
AVSession(Context context, String tag) 構(gòu)造 AVSession 實例,用于控制媒體播放。
AVSession(Context context, String tag, PacMap sessionInfo) 構(gòu)造帶有附加會話信息的 AVSession 實例,用于控制媒體播放。
void setAVSessionCallback(AVSessionCallback callback) 設置回調(diào)函數(shù)來控制播放器,控制邏輯由應用實現(xiàn)。如果 callback 為 null 則取消控制。
boolean setAVSessionAbility(IntentAgent ia) 給 AVSession 設置一個IntentAgent,用來啟動用戶界面。
boolean setAVButtonReceiver(IntentAgent ia) 為媒體按鍵接收器設置一個 IntentAgent,以便應用結(jié)束后,可以通過媒體按鍵重新拉起應用。
void enableAVSessionActive(boolean active) 設置是否激活媒體會話。當會話準備接收命令時,將輸入?yún)?shù)設置為 true。如果會話停止接收命令,則設置為 false。
boolean isAVSessionActive() 查詢會話是否激活。
void sendAVSessionEvent(String event, PacMap options) 向所有訂閱此會話的控制器發(fā)送事件。
void release() 釋放資源,應用播放完之后需調(diào)用。
AVToken getAVToken() 獲取應用連接到會話的令牌。此令牌用于創(chuàng)建媒體播放控制器。
AVController getAVController() 獲取會話構(gòu)造時創(chuàng)建的控制器,方便應用使用。
void setAVPlaybackState(AVPlaybackState state) 設置當前播放狀態(tài)。
void setAVMetadata(AVMetadata avMetadata) 設置媒體資源元數(shù)據(jù)ohos.media.common.AVMetadata。
void setAVQueue(List<AVQueueElement> queue) 設置播放隊列。
void setAVQueueTitle(CharSequence queueTitle) 設置播放隊列的標題,UI會顯示此標題。
void setOptions(PacMap options) 設置此會話關(guān)聯(lián)的附加數(shù)據(jù)。
AVCallerUserInfo getCurrentControllerInfo() 獲取發(fā)送當前請求的媒體控制器信息。
接口名 描述
AVElement(AVDescription description, int flags) 構(gòu)造 AVElement 實例。
int getFlags() 獲取flags的值。
boolean isScannable() 判斷媒體是否可掃描,如:媒體有子節(jié)點,則可繼續(xù)掃描獲取子節(jié)點內(nèi)容。
boolean isPlayable() 檢查媒體是否可播放。
AVDescription getAVDescription() 獲取媒體的詳細信息。
String getMediaId() 獲取媒體的id。

開發(fā)步驟

使用 AVSession 媒體框架創(chuàng)建一個播放器示例,分為創(chuàng)建客戶端和創(chuàng)建服務端。

創(chuàng)建客戶端

在客戶端 AVClientAbility 中聲明 AVBrowser 和 AVController,并向服務端發(fā)送連接請求。

  1. public class AVClientAbility extends Ability {
  2. // 媒體瀏覽器
  3. private AVBrowser avBrowser;
  4. // 媒體控制器
  5. private AVController avController;
  6. @Override
  7. public void onStart(Intent intent) {
  8. super.onStart(intent);
  9. // 用于指向媒體瀏覽器服務的包路徑和類名
  10. ElementName elementName = new ElementName("", "com.huawei.samples.audioplayer", "com.huawei.samples.audioplayer.AVService");
  11. // connectionCallback 在調(diào)用 avBrowser.connect 方法后進行回調(diào)。
  12. avBrowser = new AVBrowser(context, elementName, connectionCallback, null);
  13. // avBrowser 發(fā)送對媒體瀏覽器服務的連接請求。
  14. avBrowser.connect();
  15. // 將媒體控制器注冊到 ability 以接收按鍵事件。
  16. AVController.setControllerForAbility(this, avController);
  17. }
  18. }

AVConnectionCallback 回調(diào)接口中的方法為可選實現(xiàn),通常需要會在 onConnected 中訂閱媒體數(shù)據(jù)和創(chuàng)建媒體控制器 AVController。

  1. // 發(fā)起連接(avBrowser.connect)后的回調(diào)方法實現(xiàn)
  2. private AVConnectionCallback connectionCallback = new AVConnectionCallback() {
  3. @Override
  4. public void onConnected() {
  5. // 成功連接媒體瀏覽器服務時回調(diào)該方法,否則回調(diào) onConnectionFailed()。
  6. // 重復訂閱會報錯,所以先解除訂閱。
  7. avBrowser.unsubscribeByParentMediaId(avBrowser.getRootMediaId());
  8. // 第二個參數(shù) AVSubscriptionCallback,用于處理訂閱信息的回調(diào)。
  9. avBrowser.subscribeByParentMediaId(AV_ROOTavBrowser.getRootMediaId(), avSubscriptionCallback);
  10. AVToken token = avBrowser.getAVToken();
  11. avController = new AVController(AVClient.this, token); // AVController第一個參數(shù)為當前類的context
  12. // 參數(shù) AVControllerCallback,用于處理服務端播放狀態(tài)及信息變化時回調(diào)。
  13. avController.setAVControllerCallback(avControllerCallback);
  14. // ...
  15. }
  16. // 其它回調(diào)方法(可選)
  17. // ...
  18. };

通常在訂閱成功時,在 AVSubscriptionCallback 回調(diào)接口 onAVElementListLoaded 中保存服務端回傳的媒體列表。

  1. // 發(fā)起訂閱信息(avBrowser.subscribeByParentMediaId)后的回調(diào)方法實現(xiàn)
  2. private AVSubscriptionCallback avSubscriptionCallback = new AVSubscriptionCallback() {
  3. @Override
  4. public void onAVElementListLoaded(String parentId, List<AVElement> children) {
  5. // 訂閱成功時回調(diào)該方法,parentID 為標識,children 為服務端回傳的媒體列表
  6. super.onAVElementListLoaded(parentId, children);
  7. list.addAll(children);
  8. // ...
  9. }
  10. };

AVControllerCallback 回調(diào)接口中的方法均為可選方法,主要用于服務端播放狀態(tài)及信息的變化后對客戶端的回調(diào),客戶端可在這些方法中實現(xiàn)UI的刷新。

  1. // 服務對客戶端的媒體數(shù)據(jù)或播放狀態(tài)變更后的回調(diào)
  2. private AVControllerCallback avControllerCallback = new AVControllerCallback() {
  3. @Override
  4. public void onAVMetadataChanged(AVMetadata metadata) {
  5. // 當服務端調(diào)用 avSession.setAVMetadata(avMetadata)時,此方法會被回調(diào)。
  6. super.onAVMetadataChanged(metadata);
  7. AVDescription description = metadata.getAVDescription();
  8. String title = description.getTitle().toString();
  9. PixelMap pixelMap = description.getIcon();
  10. // ...
  11. }
  12. @Override
  13. public void onAVPlaybackStateChanged(AVPlaybackState playbackState) {
  14. // 當服務端調(diào)用avSession.setAVPlaybackState(...)時,此方法會被回調(diào)。
  15. super.onAVPlaybackStateChanged(playbackState);
  16. long position = playbackState.getCurrentPosition();
  17. // ...
  18. }
  19. // 其它回調(diào)方法(可選)
  20. // ...
  21. };

完成以上實現(xiàn)后,則應用可以在UI事件中調(diào)用avController的方法向服務端發(fā)送播放控制指令。

  1. // 在 UI 播放與暫停按鈕的點擊事件中向服務端發(fā)送播放或暫停指令
  2. public void toPlayOrPause() {
  3. switch (avController.getAVPlaybackState().getAVPlaybackState()) {
  4. case AVPlaybackState.PLAYBACK_STATE_NONE: {
  5. avController.getPlayControls().prepareToPlay();
  6. avController.getPlayControls().play();
  7. break;
  8. }
  9. case AVPlaybackState.PLAYBACK_STATE_PLAYING: {
  10. avController.getPlayControls().pause();
  11. break;
  12. }
  13. case AVPlaybackState.PLAYBACK_STATE_PAUSED: {
  14. avController.getPlayControls().play();
  15. break;
  16. }
  17. default: {
  18. // ...
  19. }
  20. }
  21. }

其它播放控制根據(jù)業(yè)務是否需要實現(xiàn),比如:

  1. avController.getPlayControls().playNext();
  2. avController.getPlayControls().playPrevious();
  3. avController.getPlayControls().playFastForward();
  4. avController.getPlayControls().rewind();
  5. avController.getPlayControls().seekTo(1000);
  6. avController.getPlayControls().stop();
  7. // ...

也可以主動獲取媒體信息、播放狀態(tài)等數(shù)據(jù):

  1. AVMetadata avMetadata = avController.getAVMetadata();
  2. AVPlaybackState avPlaybackState = avController.getAVPlaybackState();
  3. // ...

創(chuàng)建服務端

在服務端 AVService 中聲明 AVSession 和 Player。

  1. public class AVService extends AVBrowserService {
  2. // 媒體會話
  3. private AVSession avSession;
  4. // 媒體播放器
  5. private Player player;
  6. @Override
  7. public void onStart(Intent intent) {
  8. super.onStart(intent);
  9. avSession = new AVSession(this, "AVService");
  10. setAVToken(avSession.getAVToken());
  11. // 設置 sessioncallback,用于響應客戶端的媒體控制器發(fā)起的播放控制指令。
  12. avSession.setAVSessionCallback(avSessionCallback);
  13. // 設置播放狀態(tài)初始狀態(tài)為 AVPlaybackState.PLAYBACK_STATE_NONE。
  14. AVPlaybackState playbackState = new AVPlaybackState.Builder().setAVPlaybackState(AVPlaybackState.PLAYBACK_STATE_NONE, 0, 1.0f).build();
  15. avSession.setAVPlaybackState(playbackState);
  16. // 完成播放器的初始化,如果使用多個 Player,也可以在執(zhí)行播放時初始化。
  17. player = new Player(this);
  18. }
  19. @Override
  20. public AVBrowserRoot onGetRoot(String clientPackageName, int clientUid, PacMap rootHints) {
  21. // 響應客戶端 avBrowser.connect()方法。若同意連接,則返回有效的AVBrowserRoot實例,否則返回null
  22. return new AVBrowserRoot(AV_ROOT, null);
  23. }
  24. @Override
  25. public void onLoadAVElementList(String parentId, AVBrowserResult result) {
  26. LogUtil.info(TAG, "onLoadChildren");
  27. // 響應客戶端avBrowser.subscribeByParentMediaId(...)方法。
  28. // 先執(zhí)行該方法detachForRetrieveAsync()
  29. result.detachForRetrieveAsync();
  30. // externalAudioItems緩存媒體文件,請開發(fā)者自行實現(xiàn)。
  31. result.sendAVElementList(externalAudioItems.getAudioItems());
  32. }
  33. @Override
  34. public void onLoadAVElementList(String s, AVBrowserResult avBrowserResult, PacMap pacMap) {
  35. // 響應客戶端avBrowser.subscribeByParentMediaId(String, PacMap, AVSubscriptionCallback)方法。
  36. }
  37. @Override
  38. public void onLoadAVElement(String s, AVBrowserResult avBrowserResult) {
  39. // 響應客戶端avBrowser.getAVElement(String, AVElementCallback)方法。
  40. }
  41. }

響應客戶端的媒體控制器發(fā)起的播放控制指令的回調(diào)實現(xiàn)。

  1. private AVSessionCallback avSessionCallback = new AVSessionCallback() {
  2. @Override
  3. public void onPlay() {
  4. super.onPlay();
  5. // 當客戶端調(diào)用avController.getPlayControls().play()時,該方法會被回調(diào)。
  6. // 響應播放請求,開始播放。
  7. if (avSession.getAVController().getAVPlaybackState().getAVPlaybackState() == AVPlaybackState.PLAYBACK_STATE_PAUSED) {
  8. if (player.play()) {
  9. AVPlaybackState playbackState = new AVPlaybackState.Builder().setAVPlaybackState(
  10. AVPlaybackState.PLAYBACK_STATE_PLAYING, player.getCurrentTime(),
  11. player.getPlaybackSpeed()).build();
  12. avSession.setAVPlaybackState(playbackState);
  13. }
  14. }
  15. }
  16. @Override
  17. public void onPause() {
  18. super.onPause();
  19. // 當客戶端調(diào)用avController.getPlayControls().pause()時,該方法會被回調(diào)。
  20. // 響應暫停請求,暫停播放。
  21. }
  22. @Override
  23. public void onPlayNext() {
  24. super.onPlayNext();
  25. // 當客戶端調(diào)用avController.getPlayControls().playNext()時,該方法會被回調(diào)。
  26. // 響應播放下一曲請求,通過avSession.setAVMetadata 設置下一曲曲目的信息。
  27. avSession.setAVMetadata(avNextMetadata);
  28. }
  29. // 重寫以處理按鍵事件
  30. @Override
  31. public boolean onMediaButtonEvent(Intent mediaButtonIntent) {
  32. KeyEvent ke = mediaButtonIntent.getParcelableParam(AVSession.PARAM_KEY_EVENT);
  33. if (ke == null) {
  34. LogUtil.error("onMediaButtonEvent", "getParcelableParam failed");
  35. return false;
  36. }
  37. if (ke.isKeyDown()) {
  38. // 只處理按鍵抬起事件
  39. return true;
  40. }
  41. switch (ke.getKeyCode()) {
  42. case KeyEvent.KEY_MEDIA_PLAY_PAUSE: {
  43. if (playbackState.getAVPlaybackState() == AVPlaybackState.PLAYBACK_STATE_PAUSED) {
  44. onPlay();
  45. break;
  46. }
  47. if (playbackState.getAVPlaybackState() == AVPlaybackState.PLAYBACK_STATE_PLAYING) {
  48. onPause();
  49. break;
  50. }
  51. break;
  52. }
  53. case KeyEvent.KEY_MEDIA_PLAY: {
  54. onPlay();
  55. break;
  56. }
  57. case KeyEvent.KEY_MEDIA_PAUSE: {
  58. onPause();
  59. break;
  60. }
  61. case KeyEvent.KEY_MEDIA_STOP: {
  62. onStop();
  63. break;
  64. }
  65. case KeyEvent.KEY_MEDIA_NEXT: {
  66. onPlayNext();
  67. break;
  68. }
  69. case KeyEvent.KEY_MEDIA_PREVIOUS: {
  70. onPlayPrevious();
  71. break;
  72. }
  73. default: {
  74. break;
  75. }
  76. }
  77. return true;
  78. }
  79. // 其它回調(diào)方法(可選)
  80. // ...
  81. }
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號