1# 媒体会话提供方 2 3音视频应用在实现音视频功能的同时,需要作为媒体会话提供方接入媒体会话,在媒体会话控制方(例如播控中心)中展示媒体相关信息,及响应媒体会话控制方下发的播控命令。 4 5## 基本概念 6 7- 媒体会话元数据(AVMetadata): 用于描述媒体数据相关属性,包含标识当前媒体的ID(assetId),上一首媒体的ID(previousAssetId),下一首媒体的ID(nextAssetId),标题(title),专辑作者(author),专辑名称(album),词作者(writer),媒体时长(duration)等属性。 8 9- 媒体播放状态(AVPlaybackState):用于描述媒体播放状态的相关属性,包含当前媒体的播放状态(state)、播放位置(position)、播放倍速(speed)、缓冲时间(bufferedTime)、循环模式(loopMode)、是否收藏(isFavorite)、正在播放的媒体Id(activeItemId)、自定义媒体数据(extras)等属性。 10 11## 接口说明 12 13媒体会话提供方使用的关键接口如下表所示。接口返回值有两种返回形式:callback和promise,下表中为callback形式接口,promise和callback只是返回值方式不一样,功能相同。 14 15更多API说明请参见[API文档](../../reference/apis-avsession-kit/js-apis-avsession.md)。 16 17| 接口名 | 说明 | 18| -------- | -------- | 19| createAVSession(context: Context, tag: string, type: AVSessionType, callback: AsyncCallback<AVSession>): void<sup>10+<sup> | 创建媒体会话。<br/>一个UIAbility只能存在一个媒体会话,重复创建会失败。 | 20| setAVMetadata(data: AVMetadata, callback: AsyncCallback<void>): void<sup>10+<sup> | 设置媒体会话元数据。 | 21| setAVPlaybackState(state: AVPlaybackState, callback: AsyncCallback<void>): void<sup>10+<sup> | 设置媒体会话播放状态。 | 22| setLaunchAbility(ability: WantAgent, callback: AsyncCallback<void>): void<sup>10+<sup> | 设置启动UIAbility。 | 23| getController(callback: AsyncCallback<AVSessionController>): void<sup>10+<sup> | 获取当前会话自身控制器。 | 24| getOutputDevice(callback: AsyncCallback<OutputDeviceInfo>): void<sup>10+<sup> | 获取播放设备相关信息。 | 25| activate(callback: AsyncCallback<void>): void<sup>10+<sup> | 激活媒体会话。 | 26| deactivate(callback: AsyncCallback<void>): void<sup>10+<sup> | 禁用当前会话。 | 27| destroy(callback: AsyncCallback<void>): void<sup>10+<sup> | 销毁媒体会话。 | 28| setAVQueueItems(items: Array<AVQueueItem>, callback: AsyncCallback<void>): void <sup>10+<sup> | 设置媒体播放列表。 | 29| setAVQueueTitle(title: string, callback: AsyncCallback<void>): void<sup>10+<sup> | 设置媒体播放列表名称。 | 30| dispatchSessionEvent(event: string, args: {[key: string]: Object}, callback: AsyncCallback<void>): void<sup>10+<sup> | 设置会话内自定义事件。 | 31| setExtras(extras: {[key: string]: Object}, callback: AsyncCallback<void>): void<sup>10+<sup> | 设置键值对形式的自定义媒体数据包。| 32| getOutputDeviceSync(): OutputDeviceInfo<sup>10+<sup> | 使用同步方法获取当前输出设备信息。 | 33 34## 开发步骤 35 36音视频应用作为媒体会话提供方接入媒体会话的基本步骤如下所示: 37 381. 通过AVSessionManager的方法创建并激活媒体会话。 39 40 ```ts 41 import AVSessionManager from '@ohos.multimedia.avsession'; 42 43 // 开始创建并激活媒体会话 44 // 创建session 45 let context: Context = getContext(this); 46 async function createSession() { 47 let type: AVSessionManager.AVSessionType = 'audio'; 48 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 49 await session.activate(); 50 console.info(`session create done : sessionId : ${session.sessionId}`); 51 } 52 ``` 53 542. 跟随媒体信息的变化,及时设置媒体会话信息。需要设置的媒体会话信息主要包括: 55 - 媒体会话元数据AVMetadata。 56 - 媒体播放状态AVPlaybackState。 57 58 音视频应用设置的媒体会话信息,会被媒体会话控制方通过AVSessionController相关方法获取后进行显示或处理。 59 60 ```ts 61 import AVSessionManager from '@ohos.multimedia.avsession'; 62 import { BusinessError } from '@ohos.base'; 63 64 let context: Context = getContext(this); 65 async function setSessionInfo() { 66 // 假设已经创建了一个session,如何创建session可以参考之前的案例 67 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', 'audio'); 68 // 播放器逻辑··· 引发媒体信息与播放状态的变更 69 // 设置必要的媒体信息 70 let metadata: AVSessionManager.AVMetadata = { 71 assetId: '0', // 由应用指定,用于标识应用媒体库里的媒体 72 title: 'TITLE', 73 artist: 'ARTIST' 74 }; 75 session.setAVMetadata(metadata).then(() => { 76 console.info(`SetAVMetadata successfully`); 77 }).catch((err: BusinessError) => { 78 console.error(`Failed to set AVMetadata. Code: ${err.code}, message: ${err.message}`); 79 }); 80 // 简单设置一个播放状态 - 暂停 未收藏 81 let playbackState: AVSessionManager.AVPlaybackState = { 82 state:AVSessionManager.PlaybackState.PLAYBACK_STATE_PAUSE, 83 isFavorite:false 84 }; 85 session.setAVPlaybackState(playbackState, (err) => { 86 if (err) { 87 console.error(`Failed to set AVPlaybackState. Code: ${err.code}, message: ${err.message}`); 88 } else { 89 console.info(`SetAVPlaybackState successfully`); 90 } 91 }); 92 // 设置一个播放列表 93 let queueItemDescription_1: AVSessionManager.AVMediaDescription = { 94 assetId: '001', 95 title: 'music_name', 96 subtitle: 'music_sub_name', 97 description: 'music_description', 98 mediaImage: "PIXELMAP_OBJECT", 99 extras: {'extras':'any'} 100 }; 101 let queueItem_1: AVSessionManager.AVQueueItem = { 102 itemId: 1, 103 description: queueItemDescription_1 104 }; 105 let queueItemDescription_2: AVSessionManager.AVMediaDescription = { 106 assetId: '002', 107 title: 'music_name', 108 subtitle: 'music_sub_name', 109 description: 'music_description', 110 mediaImage: "PIXELMAP_OBJECT", 111 extras: {'extras':'any'} 112 }; 113 let queueItem_2: AVSessionManager.AVQueueItem = { 114 itemId: 2, 115 description: queueItemDescription_2 116 }; 117 let queueItemsArray = [queueItem_1, queueItem_2]; 118 session.setAVQueueItems(queueItemsArray).then(() => { 119 console.info(`SetAVQueueItems successfully`); 120 }).catch((err: BusinessError) => { 121 console.error(`Failed to set AVQueueItem, error code: ${err.code}, error message: ${err.message}`); 122 }); 123 // 设置媒体播放列表名称 124 let queueTitle = 'QUEUE_TITLE'; 125 session.setAVQueueTitle(queueTitle).then(() => { 126 console.info(`SetAVQueueTitle successfully`); 127 }).catch((err: BusinessError) => { 128 console.info(`Failed to set AVQueueTitle, error code: ${err.code}, error message: ${err.message}`); 129 }); 130 } 131 ``` 132 1333. 设置用于被媒体会话控制方拉起的UIAbility。当用户操作媒体会话控制方的界面时,例如点击播控中心的卡片,可以拉起此处配置的UIAbility。 134 设置UIAbility时通过WantAgent接口实现,更多关于WantAgent的信息请参考[WantAgent](../../reference/apis-ability-kit/js-apis-app-ability-wantAgent.md)。 135 136 ```ts 137 import wantAgent from "@ohos.app.ability.wantAgent"; 138 ``` 139 140 ```ts 141 import AVSessionManager from '@ohos.multimedia.avsession'; 142 import wantAgent from '@ohos.app.ability.wantAgent'; 143 144 let context: Context = getContext(this); 145 async function getWantAgent() { 146 let type: AVSessionManager.AVSessionType = 'audio'; 147 // 假设已经创建了一个session,如何创建session可以参考之前的案例 148 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 149 let wantAgentInfo: wantAgent.WantAgentInfo = { 150 wants: [ 151 { 152 bundleName: 'com.example.musicdemo', 153 abilityName: 'com.example.musicdemo.MainAbility' 154 } 155 ], 156 operationType: wantAgent.OperationType.START_ABILITIES, 157 requestCode: 0, 158 wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG] 159 } 160 wantAgent.getWantAgent(wantAgentInfo).then((agent) => { 161 session.setLaunchAbility(agent); 162 }) 163 } 164 ``` 165 1664. 设置一个即时的自定义会话事件,以供媒体控制方接收到事件后进行相应的操作。 167 168 > **说明:**<br> 169 > 通过dispatchSessionEvent方法设置的数据不会保存在会话对象或AVSession服务中。 170 171 ```ts 172 173 import AVSessionManager from '@ohos.multimedia.avsession'; 174 import { BusinessError } from '@ohos.base'; 175 176 let context: Context = getContext(this); 177 async function dispatchSessionEvent() { 178 // 假设已经创建了一个session,如何创建session可以参考之前的案例 179 let type: AVSessionManager.AVSessionType = 'audio'; 180 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 181 let eventName = 'dynamic_lyric'; 182 await session.dispatchSessionEvent(eventName, {lyric : 'This is my lyric'}).then(() => { 183 console.info(`Dispatch session event successfully`); 184 }).catch((err: BusinessError) => { 185 console.error(`Failed to dispatch session event. Code: ${err.code}, message: ${err.message}`); 186 }) 187 } 188 189 ``` 190 1915. 设置与当前会话相关的自定义媒体数据包,以供媒体控制方接收到事件后进行相应的操作。 192 193 > **说明:**<br> 194 > 通过setExtras方法设置的数据包会被存储在AVSession服务中,数据的生命周期与会话一致;会话对应的Controller可以使用getExtras来获取该数据。 195 196 ```ts 197 import AVSessionManager from '@ohos.multimedia.avsession'; 198 import { BusinessError } from '@ohos.base'; 199 200 let context: Context = getContext(this); 201 async function setExtras() { 202 // 假设已经创建了一个session,如何创建session可以参考之前的案例 203 let type: AVSessionManager.AVSessionType = 'audio'; 204 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 205 await session.setExtras({extra : 'This is my custom meida packet'}).then(() => { 206 console.info(`Set extras successfully`); 207 }).catch((err: BusinessError) => { 208 console.error(`Failed to set extras. Code: ${err.code}, message: ${err.message}`); 209 }) 210 } 211 ``` 212 2136. 注册播控命令事件监听,便于响应用户通过媒体会话控制方,例如播控中心,下发的播控命令。 214 215 在Session侧注册的监听分为`固定播控命令`和`高级播控事件`两种。 216 217 6.1 固定控制命令的监听 218 219 > **说明:** 220 > 221 > 媒体会话提供方在注册相关固定播控命令事件监听时,监听的事件会在媒体会话控制方的getValidCommands()方法中体现,即媒体会话控制方会认为对应的方法有效,进而根据需要触发相应的事件。为了保证媒体会话控制方下发的播控命令可以被正常执行,媒体会话提供方请勿进行无逻辑的空实现监听。 222 223 Session侧的固定播控命令主要包括播放、暂停、上一首、下一首等基础操作命令,详细介绍请参见[AVControlCommand](../../reference/apis-avsession-kit/js-apis-avsession.md) 224 225 ```ts 226 import AVSessionManager from '@ohos.multimedia.avsession'; 227 228 let context: Context = getContext(this); 229 async function setListenerForMesFromController() { 230 // 假设已经创建了一个session,如何创建session可以参考之前的案例 231 let type: AVSessionManager.AVSessionType = 'audio'; 232 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 233 // 一般在监听器中会对播放器做相应逻辑处理 234 // 不要忘记处理完后需要通过set接口同步播放相关信息,参考上面的用例 235 session.on('play', () => { 236 console.info(`on play , do play task`); 237 // do some tasks ··· 238 }); 239 session.on('pause', () => { 240 console.info(`on pause , do pause task`); 241 // do some tasks ··· 242 }); 243 session.on('stop', () => { 244 console.info(`on stop , do stop task`); 245 // do some tasks ··· 246 }); 247 session.on('playNext', () => { 248 console.info(`on playNext , do playNext task`); 249 // do some tasks ··· 250 }); 251 session.on('playPrevious', () => { 252 console.info(`on playPrevious , do playPrevious task`); 253 // do some tasks ··· 254 }); 255 session.on('fastForward', () => { 256 console.info(`on fastForward , do fastForward task`); 257 // do some tasks ··· 258 }); 259 session.on('rewind', () => { 260 console.info(`on rewind , do rewind task`); 261 // do some tasks ··· 262 }); 263 264 session.on('seek', (time) => { 265 console.info(`on seek , the seek time is ${time}`); 266 // do some tasks ··· 267 }); 268 session.on('setSpeed', (speed) => { 269 console.info(`on setSpeed , the speed is ${speed}`); 270 // do some tasks ··· 271 }); 272 session.on('setLoopMode', (mode) => { 273 console.info(`on setLoopMode , the loop mode is ${mode}`); 274 // do some tasks ··· 275 }); 276 session.on('toggleFavorite', (assetId) => { 277 console.info(`on toggleFavorite , the target asset Id is ${assetId}`); 278 // do some tasks ··· 279 }); 280 } 281 ``` 282 283 6.2 高级播控事件的监听 284 285 Session侧的可以注册的高级播控事件主要包括: 286 287 - skipToQueueItem: 播放列表其中某项被选中的事件。 288 - handleKeyEvent: 按键事件。 289 - outputDeviceChange: 播放设备变化的事件。 290 - commonCommand: 自定义控制命令变化的事件。 291 292 ```ts 293 import AVSessionManager from '@ohos.multimedia.avsession'; 294 295 let context: Context = getContext(this); 296 async function setListenerForMesFromController() { 297 // 假设已经创建了一个session,如何创建session可以参考之前的案例 298 let type: AVSessionManager.AVSessionType = 'audio'; 299 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 300 // 一般在监听器中会对播放器做相应逻辑处理 301 // 不要忘记处理完后需要通过set接口同步播放相关信息,参考上面的用例 302 session.on('skipToQueueItem', (itemId) => { 303 console.info(`on skipToQueueItem , do skip task`); 304 // do some tasks ··· 305 }); 306 session.on('handleKeyEvent', (event) => { 307 console.info(`on handleKeyEvent , the event is ${JSON.stringify(event)}`); 308 // do some tasks ··· 309 }); 310 session.on('outputDeviceChange', (device) => { 311 console.info(`on outputDeviceChange , the device info is ${JSON.stringify(device)}`); 312 // do some tasks ··· 313 }); 314 session.on('commonCommand', (commandString, args) => { 315 console.info(`on commonCommand , command is ${commandString}, args are ${JSON.stringify(args)}`); 316 // do some tasks ··· 317 }); 318 } 319 ``` 320 3217. 获取当前媒体会话自身的控制器,与媒体会话对应进行通信交互。 322 323 ```ts 324 import AVSessionManager from '@ohos.multimedia.avsession'; 325 326 let context: Context = getContext(this); 327 async function createControllerFromSession() { 328 // 假设已经创建了一个session,如何创建session可以参考之前的案例 329 let type: AVSessionManager.AVSessionType = 'audio'; 330 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 331 332 // 通过已有session获取一个controller对象 333 let controller = await session.getController(); 334 335 // controller可以与原session对象进行基本的通信交互,比如下发播放命令 336 let avCommand: AVSessionManager.AVControlCommand = {command:'play'}; 337 controller.sendControlCommand(avCommand); 338 339 // 或者做状态变更监听 340 controller.on('playbackStateChange', 'all', (state) => { 341 342 // do some things 343 }); 344 345 // controller可以做的操作还有很多,具体可以参考媒体会话控制方相关的说明 346 } 347 ``` 348 3498. 音视频应用在退出,并且不需要继续播放时,及时取消监听以及销毁媒体会话释放资源。 350 取消播控命令监听的示例代码如下所示 : 351 352 ```ts 353 import AVSessionManager from '@ohos.multimedia.avsession'; 354 355 let context: Context = getContext(this); 356 async function unregisterSessionListener() { 357 // 假设已经创建了一个session,如何创建session可以参考之前的案例 358 let type: AVSessionManager.AVSessionType = 'audio'; 359 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 360 361 // 取消指定session下的相关监听 362 session.off('play'); 363 session.off('pause'); 364 session.off('stop'); 365 session.off('playNext'); 366 session.off('playPrevious'); 367 session.off('skipToQueueItem'); 368 session.off('handleKeyEvent'); 369 session.off('outputDeviceChange'); 370 session.off('commonCommand'); 371 } 372 ``` 373 374 销毁媒体会话示例代码如下所示: 375 376 ```ts 377 import AVSessionManager from '@ohos.multimedia.avsession'; 378 379 let context: Context = getContext(this); 380 async function destroySession() { 381 // 假设已经创建了一个session,如何创建session可以参考之前的案例 382 let type: AVSessionManager.AVSessionType = 'audio'; 383 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 384 // 主动销毁已创建的session 385 session.destroy((err) => { 386 if (err) { 387 console.error(`Failed to destroy session. Code: ${err.code}, message: ${err.message}`); 388 } else { 389 console.info(`Destroy : SUCCESS `); 390 } 391 }); 392 } 393 ``` 394 395## 相关实例 396 397针对媒体会话提供方开发,有以下相关实例可供参考: 398 399- [媒体会话——提供方(ArkTS)(Full SDK)(API10)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Media/AVSession/MediaProvider)