一、簡介
- AVSession 是一套媒體播放控制框架,對媒體服務和界面進行解耦,并提供規范的通信接口,使應用可以自由、高效地在不同的媒體之間完成切換。
- 在使用完 AVSession 類后,需要及時進行資源釋放。
- 播放器類需要使用 ohos.media.player.Player,否則無法正常接收按鍵事件。
二、應用場景
- AVSession 框架有四個主要的類,控制著整個框架的核心,下圖簡單的說明四個核心媒體框架控制類的關系。
- AVBrowser:
-
- 媒體瀏覽器,通常在客戶端創建,成功連接媒體服務后,通過媒體控制器 AVController 向服務端發送播放控制指令。
-
- 其主要流程為,調用 connect 方法向 AVBrowserService 發起連接請求,連接成功后在回調方法 AVConnectionCallback.onConnected 中發起訂閱數據請求,并在回調方法 AVSubscriptionCallback.onAVElementListLoaded 中保存請求的媒體播放數據。
-
- 調用 AVBrowser的subscribeByParentMediaId(String, AVSubscriptionCallback) 之前,需要先執行 unsubscribeByParentMediaId(String),防止重復訂閱。
- AVController:
-
- 媒體控制器,在客戶端 AVBrowser 連接服務成功后的回調方法 AVConnectionCallback.onConnected 中創建,用于向 Service 發送播放控制指令,并通過實現 AVControllerCallback 回調來響應服務端媒體狀態變化,例如曲目信息變更、播放狀態變更等,從而完成 UI 刷新。
- AVBrowserService:
-
- 媒體瀏覽器服務,通常在服務端,通過媒體會話 AVSession 與媒體瀏覽器建立連接,并通過實現 Player 進行媒體播放。
-
- AVBrowserService 有兩個重要的方法:
-
-
- onGetRoot,處理從媒體瀏覽器 AVBrowser 發來的連接請求,通過返回一個有效的AVBrowserRoot對象表示連接成功;
-
-
- onLoadAVElementList,處理從媒體瀏覽器 AVBrowser 發來的數據訂閱請求,通過 AVBrowserResult.sendAVElementList(List) 方法返回媒體播放數據。使用 onLoadAVElementList(String, AVBrowserResult) 的 result 返回數據前,需要執行 detachForRetrieveAsync()。
- AVSession:
-
- 媒體會話,通常在 AVBrowserService 的 onStart 中創建,通過 setAVToken 方法設置到 AVBrowserService 中,并通過實現 AVSessionCallback 回調來接收和處理媒體控制器 AVController 發送的播放控制指令,如播放、暫停、跳轉至上一曲、跳轉至下一曲等。
- AVElement:媒體元素,用于將播放列表從 AVBrowserService 傳遞給 AVBrowser。
三、API
① AVBrowser 的主要接口
接口名描述
| AVBrowser(Context context, ElementName name, AVConnectionCallback callback, PacMap options) | 構造AVBrowser實例,用于瀏覽AVBrowserService提供的媒體數據 |
| connect() | 連接AVBrowserService |
| disconnect() | 與AVBrowserService斷開連接 |
| isConnected() | 判斷當前是否已經與AVBrowserService連接 |
| getElementName() | 獲取AVBrowserService的ohos.bundle.ElementName實例 |
| getRootMediaId() | 獲取默認媒體id |
| getOptions() | 獲取AVBrowserService提供的附加數據 |
| getAVToken() | 獲取媒體會話的令牌 |
| getAVElement(String mediaId, AVElementCallback callback) | 輸入媒體的id,查詢對應的ohos.media.common.sessioncore.AVElement信息,查詢結果會通過callback返回 |
| subscribeByParentMediaId(String parentMediaId, AVSubscriptionCallback callback) | 查詢指定媒體id包含的所有媒體元素信息,并訂閱它的媒體信息更新通知 |
| subscribeByParentMediaId(String parentMediaId, PacMap options, AVSubscriptionCallback callback) | 基于特定于服務的參數來查詢指定媒體id中的媒體元素的信息,并訂閱它的媒體信息更新通知 |
| unsubscribeByParentMediaId(String parentMediaId) | 取消訂閱對應媒體id的信息更新通知 |
| unsubscribeByParentMediaId(String parentMediaId, AVSubscriptionCallback callback) | 取消訂閱與指定callback相關的媒體id的信息更新通知 |
② AVBrowserService 的主要接口
接口名描述
| onGetRoot(String callerPackageName, int clientUid, PacMap options) | 回調方法,用于返回應用程序的媒體內容的根信息,在AVBrowser.connect()后進行回調 |
| onLoadAVElementList(String parentMediaId, AVBrowserResult result) | 回調方法,用于返回應用程序的媒體內容的結果信息AVBrowserResult,其中包含了子節點的AVElement列表,在AVBrowser的方法subscribeByParentMediaId或notifyAVElementListUpdated執行后進行回調 |
| onLoadAVElement(String mediaId, AVBrowserResult result) | 回調方法,用于獲取特定的媒體項目AVElement結果信息,在AVBrowser.getAVElement方法執行后進行回調 |
| getAVToken() | 獲取AVBrowser與AVBrowserService之間的會話令牌 |
| setAVToken(AVToken token) | 設置AVBrowser與AVBrowserService之間的會話令牌 |
| getBrowserOptions() | 獲取AVBrowser在連接AVBrowserService時設置的服務參數選項 |
| getCallerUserInfo() | 獲取當前發送請求的調用者信息 |
| notifyAVElementListUpdated(String parentMediaId) | 通知所有已連接的AVBrowser當前父節點的子節點已經發生改變 |
| notifyAVElementListUpdated(String parentId, PacMap options) | 通知所有已連接的AVBrowser當前父節點的子節點已經發生改變,可設置服務參數 |
③ AVController 的主要接口
接口名描述
| AVController(Context context, AVToken avToken) | 構造AVController實例,用于應用程序與AVSession進行交互以控制媒體播放 |
| setControllerForAbility(Ability ability, AVController controller) | 將媒體控制器注冊到ability以接收按鍵事件 |
| setAVControllerCallback(AVControllerCallback callback) | 注冊一個回調以接收來自AVSession的變更,例如元數據和播放狀態變更 |
| releaseAVControllerCallback(AVControllerCallback callback) | 釋放與AVSession之間的回調實例 |
| getAVQueueElement() | 獲取播放隊列 |
| getAVQueueTitle() | 獲取播放隊列的標題 |
| getAVPlaybackState() | 獲取播放狀態 |
| dispatchAVKeyEvent(KeyEvent keyEvent) | 應用分發媒體按鍵事件給會話以控制播放 |
| sendCustomCommand(String command, PacMap pacMap, GeneralReceiver receiverCb) | 應用向AVSession發送自定義命令,參考ohos.media.common.sessioncore.AVSessionCallback.onCommand |
| getAVSessionAbility() | 獲取啟動用戶界面的IntentAgent |
| getAVToken() | 獲取應用連接到會話的令牌。此令牌用于創建媒體播放控制器 |
| adjustAVPlaybackVolume(int direction, int flags) | 調節播放音量 |
| setAVPlaybackVolume(int value, int flags) | 設置播放音量,要求支持絕對音量控制 |
| getOptions() | 獲取與此控制器連接的AVSession的附加數據 |
| getFlags() | 獲取AVSession的附加標識,標記在AVSession中的定義 |
| getAVMetadata() | 獲取媒體資源的元數據ohos.media.common.AVMetadata |
| getAVPlaybackInfo() | 獲取播放信息 |
| getSessionOwnerPackageName() | 獲得AVSession實例的應用程序的包名稱 |
| getAVSessionInfo() | 獲取會話的附加數據 |
| getPlayControls() | 獲取一個PlayControls實例,將用于控制播放,比如控制媒體播放、停止、下一首等 |
④ AVSession 的主要接口
接口名描述
| AVSession(Context context, String tag) | 構造AVSession實例,用于控制媒體播放 |
| AVSession(Context context, String tag, PacMap sessionInfo) | 構造帶有附加會話信息的AVSession實例,用于控制媒體播放 |
| setAVSessionCallback(AVSessionCallback callback) | 設置回調函數來控制播放器,控制邏輯由應用實現。如果callback為null則取消控制 |
| setAVSessionAbility(IntentAgent ia) | 給AVSession設置一個IntentAgent,用來啟動用戶界面 |
| setAVButtonReceiver(IntentAgent ia) | 為媒體按鍵接收器設置一個IntentAgent,以便應用結束后,可以通過媒體按鍵重新拉起應用 |
| enableAVSessionActive(boolean active) | 設置是否激活媒體會話。當會話準備接收命令時,將輸入參數設置為true。如果會話停止接收命令,則設置為false |
| isAVSessionActive() | 查詢會話是否激活 |
| sendAVSessionEvent(String event, PacMap options) | 向所有訂閱此會話的控制器發送事件 |
| release() | 釋放資源,應用播放完之后需調用 |
| getAVToken() | 獲取應用連接到會話的令牌。此令牌用于創建媒體播放控制器 |
| getAVController() | 獲取會話構造時創建的控制器,方便應用使用 |
| setAVPlaybackState(AVPlaybackState state) | 設置當前播放狀態 |
| setAVMetadata(AVMetadata avMetadata) | 設置媒體資源元數據ohos.media.common.AVMetadata |
| setAVQueue(List queue) | 設置播放隊列 |
| setAVQueueTitle(CharSequence queueTitle) | 設置播放隊列的標題,UI會顯示此標題 |
| setOptions(PacMap options) | 設置此會話關聯的附加數據 |
| getCurrentControllerInfo() | 獲取發送當前請求的媒體控制器信息 |
⑤ AVElement 的主要接口
接口名描述
| AVElement(AVDescription description, int flags) | 構造AVElement實例 |
| getFlags() | 獲取flags的值 |
| isScannable() | 判斷媒體是否可掃描,如:媒體有子節點,則可繼續掃描獲取子節點內容 |
| isPlayable() | 檢查媒體是否可播放 |
| getAVDescription() | 獲取媒體的詳細信息 |
| getMediaId() | 獲取媒體的id |
四、使用流程
- 使用 AVSession 媒體框架創建一個播放器示例,分為創建客戶端和創建服務端。
① 創建客戶端
- 在客戶端 AVClientAbility 中聲明 avBrowser 和 avController,通過 avBrowser 并向服務端發送連接請求,然后將 avController 注冊到 ability:
public class AVClientAbility extends Ability {// 媒體瀏覽器private AVBrowser avBrowser;// 媒體控制器private AVController avController;// 服務端回傳的媒體列表List
<AVElement> avElements;@Overridepublic void onStart(Intent intent) {super.onStart(intent);// 用于指向媒體瀏覽器服務的包路徑和類名ElementName elementName = new ElementName("", "com.samples.audioplayer", "com.samples.audioplayer.AVService");// connectionCallback在調用avBrowser.connect方法后進行回調。avBrowser = new AVBrowser(context, elementName, connectionCallback, null);// avBrowser發送對媒體瀏覽器服務的連接請求,connect方法需要確保當前處于斷開連接狀態。avBrowser.connect();// 將媒體控制器注冊到ability以接收按鍵事件。AVController.setControllerForAbility(this, avController);}}
- AVConnectionCallback 回調接口中的方法為可選實現,通常需要會在 onConnected 中訂閱媒體數據和創建媒體控制器 AVController:
// 發起連接(avBrowser.connect)后的回調方法實現private AVConnectionCallback connectionCallback = new AVConnectionCallback() {@Overridepublic void onConnected() {// 成功連接媒體瀏覽器服務時回調該方法,否則回調onConnectionFailed()。// 重復訂閱會報錯,所以先解除訂閱。avBrowser.unsubscribeByParentMediaId(avBrowser.getRootMediaId());// 第二個參數AVSubscriptionCallback,用于處理訂閱信息的回調。avBrowser.subscribeByParentMediaId(avBrowser.getRootMediaId(), avSubscriptionCallback);AVToken token = avBrowser.getAVToken();avController = new AVController(AVClient.this, token); // AVController第一個參數為當前類的context// 參數AVControllerCallback,用于處理服務端播放狀態及信息變化時回調。avController.setAVControllerCallback(avControllerCallback);// ...}// 其它回調方法(可選)// ...};
- 通常在訂閱成功時,在 AVSubscriptionCallback 回調接口 onAVElementListLoaded 中保存服務端回傳的媒體列表:
// 發起訂閱信息(avBrowser.subscribeByParentMediaId)后的回調方法實現private AVSubscriptionCallback avSubscriptionCallback = new AVSubscriptionCallback() {@Overridepublic void onAVElementListLoaded(String parentId, List
<AVElement> children) {// 訂閱成功時回調該方法,parentID為標識,children為服務端回傳的媒體列表super.onAVElementListLoaded(parentId, children);avElements.addAll(children);// ...}};
- AVControllerCallback 回調接口中的方法均為可選方法,主要用于服務端播放狀態及信息的變化后對客戶端的回調,客戶端可在這些方法中實現 UI 的刷新:
// 服務對客戶端的媒體數據或播放狀態變更后的回調 private AVControllerCallback avControllerCallback = new AVControllerCallback() {@Overridepublic void onAVMetadataChanged(AVMetadata metadata) {// 當服務端調用avSession.setAVMetadata(avMetadata)時,此方法會被回調。super.onAVMetadataChanged(metadata);AVDescription description = metadata.getAVDescription();String title = description.getTitle().toString();PixelMap pixelMap = description.getIcon();// ...}@Overridepublic void onAVPlaybackStateChanged(AVPlaybackState playbackState) {// 當服務端調用avSession.setAVPlaybackState(...)時,此方法會被回調。super.onAVPlaybackStateChanged(playbackState);long position = playbackState.getCurrentPosition();// ...}// 其它回調方法(可選)// ...};
- 完成以上實現后,則應用可以在 UI 事件中調用 avController 的方法向服務端發送播放控制指令:
// 在UI播放與暫停按鈕的點擊事件中向服務端發送播放或暫停指令public void toPlayOrPause() {switch (avController.getAVPlaybackState().getAVPlaybackState()) {case AVPlaybackState.PLAYBACK_STATE_NONE: {avController.getPlayControls().prepareToPlay();avController.getPlayControls().play();break;}case AVPlaybackState.PLAYBACK_STATE_PLAYING: {avController.getPlayControls().pause();break;}case AVPlaybackState.PLAYBACK_STATE_PAUSED: {avController.getPlayControls().play();break;}default: {// ...}}}
avController.getPlayControls().playNext();avController.getPlayControls().playPrevious();avController.getPlayControls().playFastForward();avController.getPlayControls().rewind();avController.getPlayControls().seekTo(1000);avController.getPlayControls().stop();// ...
AVMetadata avMetadata = avController.getAVMetadata();AVPlaybackState avPlaybackState = avController.getAVPlaybackState();// ...
② 創建服務端
- 在服務端 AVService 中聲明 AVSession 和 Player:
public class AVService extends AVBrowserService {// 根媒體IDprivate static final String AV_ROOT_ID = "av_root_id";// 媒體會話private AVSession avSession;// 媒體播放器private Player player;@Overridepublic void onStart(Intent intent) {super.onStart(intent);avSession = new AVSession(this, "AVService");setAVToken(avSession.getAVToken());// 設置sessioncallback,用于響應客戶端的媒體控制器發起的播放控制指令。avSession.setAVSessionCallback(avSessionCallback);// 設置播放狀態初始狀態為AVPlaybackState.PLAYBACK_STATE_NONE。AVPlaybackState playbackState = new AVPlaybackState.Builder().setAVPlaybackState(AVPlaybackState.PLAYBACK_STATE_NONE, 0, 1.0f).build();avSession.setAVPlaybackState(playbackState);// 完成播放器的初始化,如果使用多個Player,也可以在執行播放時初始化。player = new Player(this);}@Overridepublic AVBrowserRoot onGetRoot(String clientPackageName, int clientUid, PacMap rootHints) {// 響應客戶端avBrowser.connect()方法。若同意連接,則返回有效的AVBrowserRoot實例,否則返回nullreturn new AVBrowserRoot(AV_ROOT_ID, null);}@Overridepublic void onLoadAVElementList(String parentId, AVBrowserResult result) {HiLog.info(TAG, "onLoadChildren");// 響應客戶端avBrowser.subscribeByParentMediaId(...)方法。// 先執行該方法detachForRetrieveAsync() result.detachForRetrieveAsync();// externalAudioItems緩存媒體文件,請開發者自行實現。result.sendAVElementList(externalAudioItems.getAudioItems());}@Overridepublic void onLoadAVElementList(String s, AVBrowserResult avBrowserResult, PacMap pacMap) {// 響應客戶端avBrowser.subscribeByParentMediaId(String, PacMap, AVSubscriptionCallback)方法。}@Overridepublic void onLoadAVElement(String s, AVBrowserResult avBrowserResult) {// 響應客戶端avBrowser.getAVElement(String, AVElementCallback)方法。}}
- 響應客戶端的媒體控制器發起的播放控制指令的回調實現:
private AVSessionCallback avSessionCallback = new AVSessionCallback() {@Overridepublic void onPlay() {super.onPlay();// 當客戶端調用avController.getPlayControls().play()時,該方法會被回調。// 響應播放請求,開始播放。if (avSession.getAVController().getAVPlaybackState() == AVPlaybackState.PLAYBACK_STATE_PAUSED) {if (player.play()) {AVPlaybackState playbackState = new AVPlaybackState.Builder().setAVPlaybackState(AVPlaybackState.PLAYBACK_STATE_PLAYING, player.getCurrentTime(),player.getPlaybackSpeed()).build();avSession.setAVPlaybackState(playbackState);}}}@Overridepublic void onPause() {super.onPause();// 當客戶端調用avController.getPlayControls().pause()時,該方法會被回調。// 響應暫停請求,暫停播放。}@Overridepublic void onPlayNext() {super.onPlayNext();// 當客戶端調用avController.getPlayControls().playNext()時,該方法會被回調。// 響應播放下一曲請求,通過avSession.setAVMetadata設置下一曲曲目的信息。avSession.setAVMetadata(avNextMetadata);}// 重寫以處理按鍵事件@Overridepublic boolean onMediaButtonEvent(Intent mediaButtonIntent) {KeyEvent ke = mediaButtonIntent.getSequenceableParam(AVSession.PARAM_KEY_EVENT);if (ke == null) {HiLog.error(TAG, "getSequenceableParam failed");return false;}if (ke.isKeyDown()) {// 只處理按鍵抬起事件return true;}switch (ke.getKeyCode()) {case KeyEvent.KEY_MEDIA_PLAY_PAUSE: {if (playbackState.getAVPlaybackState() == AVPlaybackState.PLAYBACK_STATE_PAUSED) {onPlay();break;}if (playbackState.getAVPlaybackState() == AVPlaybackState.PLAYBACK_STATE_PLAYING) {onPause();break;}break;}case KeyEvent.KEY_MEDIA_PLAY: {onPlay();break;}case KeyEvent.KEY_MEDIA_PAUSE: {onPause();break;}case KeyEvent.KEY_MEDIA_STOP: {onStop();break;}case KeyEvent.KEY_MEDIA_NEXT: {onPlayNext();break;}case KeyEvent.KEY_MEDIA_PREVIOUS: {onPlayPrevious();break;}default: {break;}}return true;}// 其它回調方法(可選)// ...}
總結
以上是生活随笔為你收集整理的HarmonyOS之深入解析媒体会话的管理的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。