1# AVSession Provider 2 3An audio and video application needs to access the AVSession service as a provider in order to display media information in the controller (for example, Media Controller) and respond to playback control commands delivered by the controller. 4 5## Basic Concepts 6 7- AVMetadata: media data related attributes, including the IDs of the current media asset (assetId), previous media asset (previousAssetId), and next media asset (nextAssetId), title, author, album, writer, and duration. 8 9- AVPlaybackState: playback state attributes, including the playback state, position, speed, buffered time, loop mode, media item being played (activeItemId), custom media data (extras), and whether the media asset is favorited (isFavorite). 10 11## Available APIs 12 13The table below lists the key APIs used by the provider. The APIs use either a callback or promise to return the result. The APIs listed below use a callback. They provide the same functions as their counterparts that use a promise. 14 15For details, see [AVSession Management](../../reference/apis-avsession-kit/js-apis-avsession.md). 16 17| API | Description | 18| -------- | -------- | 19| createAVSession(context: Context, tag: string, type: AVSessionType, callback: AsyncCallback<AVSession>): void<sup>10+<sup> | Creates an AVSession.<br>Only one AVSession can be created for a UIAbility. | 20| setAVMetadata(data: AVMetadata, callback: AsyncCallback<void>): void<sup>10+<sup> | Sets AVSession metadata. | 21| setAVPlaybackState(state: AVPlaybackState, callback: AsyncCallback<void>): void<sup>10+<sup> | Sets the AVSession playback state. | 22| setLaunchAbility(ability: WantAgent, callback: AsyncCallback<void>): void<sup>10+<sup> | Starts a UIAbility. | 23| getController(callback: AsyncCallback<AVSessionController>): void<sup>10+<sup> | Obtains the controller of the AVSession. | 24| getOutputDevice(callback: AsyncCallback<OutputDeviceInfo>): void<sup>10+<sup> | Obtains the output device information. | 25| activate(callback: AsyncCallback<void>): void<sup>10+<sup> | Activates the AVSession. | 26| deactivate(callback: AsyncCallback<void>): void<sup>10+<sup> | Deactivates this session. | 27| destroy(callback: AsyncCallback<void>): void<sup>10+<sup> | Destroys the AVSession. | 28| setAVQueueItems(items: Array<AVQueueItem>, callback: AsyncCallback<void>): void <sup>10+<sup> | Sets a playlist. | 29| setAVQueueTitle(title: string, callback: AsyncCallback<void>): void<sup>10+<sup> | Sets a name for the playlist. | 30| dispatchSessionEvent(event: string, args: {[key: string]: Object}, callback: AsyncCallback<void>): void<sup>10+<sup> | Dispatches a custom session event. | 31| setExtras(extras: {[key: string]: Object}, callback: AsyncCallback<void>): void<sup>10+<sup> | Sets a custom media packet in the form of a key-value pair.| 32| getOutputDeviceSync(): OutputDeviceInfo<sup>10+<sup> | Obtains the output device information. This API is a synchronous API. | 33 34## How to Develop 35 36To enable an audio and video application to access the AVSession service as a provider, proceed as follows: 37 381. Call an API in the **AVSessionManager** class to create and activate an **AVSession** object. 39 40 ```ts 41 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 42 43 // Start to create and activate an AVSession object. 44 // Create an AVSession object. 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. Set AVSession information, which includes: 55 - AVMetadata 56 - AVPlaybackState 57 58 The controller will call an API in the **AVSessionController** class to obtain the information and display or process the information. 59 60 ```ts 61 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 62 import { BusinessError } from '@kit.BasicServicesKit'; 63 64 let context: Context = getContext(this); 65 async function setSessionInfo() { 66 // It is assumed that an AVSession object has been created. For details about how to create an AVSession object, see the node snippet in step 1. 67 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', 'audio'); 68 // The player logic that triggers changes in the session metadata and playback state is omitted here. 69 // Set necessary session metadata. 70 let metadata: AVSessionManager.AVMetadata = { 71 assetId: '0', // Specified by the application, used to identify the media asset in the application media library. 72 title: 'TITLE', 73 mediaImage: 'IMAGE', 74 artist: 'ARTIST' 75 }; 76 session.setAVMetadata(metadata).then(() => { 77 console.info(`SetAVMetadata successfully`); 78 }).catch((err: BusinessError) => { 79 console.error(`Failed to set AVMetadata. Code: ${err.code}, message: ${err.message}`); 80 }); 81 // Set the playback state to paused and set isFavorite to false. 82 let playbackState: AVSessionManager.AVPlaybackState = { 83 state:AVSessionManager.PlaybackState.PLAYBACK_STATE_PAUSE, 84 isFavorite:false 85 }; 86 session.setAVPlaybackState(playbackState, (err) => { 87 if (err) { 88 console.error(`Failed to set AVPlaybackState. Code: ${err.code}, message: ${err.message}`); 89 } else { 90 console.info(`SetAVPlaybackState successfully`); 91 } 92 }); 93 // Set a playlist. 94 let queueItemDescription_1: AVSessionManager.AVMediaDescription = { 95 assetId: '001', 96 title: 'music_name', 97 subtitle: 'music_sub_name', 98 description: 'music_description', 99 mediaImage: "PIXELMAP_OBJECT", 100 extras: {'extras':'any'} 101 }; 102 let queueItem_1: AVSessionManager.AVQueueItem = { 103 itemId: 1, 104 description: queueItemDescription_1 105 }; 106 let queueItemDescription_2: AVSessionManager.AVMediaDescription = { 107 assetId: '002', 108 title: 'music_name', 109 subtitle: 'music_sub_name', 110 description: 'music_description', 111 mediaImage: "PIXELMAP_OBJECT", 112 extras: {'extras':'any'} 113 }; 114 let queueItem_2: AVSessionManager.AVQueueItem = { 115 itemId: 2, 116 description: queueItemDescription_2 117 }; 118 let queueItemsArray = [queueItem_1, queueItem_2]; 119 session.setAVQueueItems(queueItemsArray).then(() => { 120 console.info(`SetAVQueueItems successfully`); 121 }).catch((err: BusinessError) => { 122 console.error(`Failed to set AVQueueItem, error code: ${err.code}, error message: ${err.message}`); 123 }); 124 // Set a name for the playlist. 125 let queueTitle = 'QUEUE_TITLE'; 126 session.setAVQueueTitle(queueTitle).then(() => { 127 console.info(`SetAVQueueTitle successfully`); 128 }).catch((err: BusinessError) => { 129 console.info(`Failed to set AVQueueTitle, error code: ${err.code}, error message: ${err.message}`); 130 }); 131 } 132 ``` 133 1343. Set the UIAbility to be started by the controller. The UIAbility configured here is started when a user operates the UI of the controller, for example, clicking a widget in Media Controller. 135 The UIAbility is set through the **WantAgent** API. For details, see [WantAgent](../../reference/apis-ability-kit/js-apis-app-ability-wantAgent.md). 136 137 ```ts 138 import { wantAgent } from '@kit.AbilityKit'; 139 ``` 140 141 ```ts 142 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 143 import { wantAgent } from '@kit.AbilityKit'; 144 145 let context: Context = getContext(this); 146 async function getWantAgent() { 147 let type: AVSessionManager.AVSessionType = 'audio'; 148 // It is assumed that an AVSession object has been created. For details about how to create an AVSession object, see the node snippet in step 1. 149 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 150 let wantAgentInfo: wantAgent.WantAgentInfo = { 151 wants: [ 152 { 153 bundleName: 'com.example.musicdemo', 154 abilityName: 'MainAbility' 155 } 156 ], 157 operationType: wantAgent.OperationType.START_ABILITIES, 158 requestCode: 0, 159 wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG] 160 } 161 wantAgent.getWantAgent(wantAgentInfo).then((agent) => { 162 session.setLaunchAbility(agent); 163 }) 164 } 165 ``` 166 1674. Set a custom session event. The controller performs an operation after receiving the event. 168 169 > **NOTE** 170 > 171 > The data set through **dispatchSessionEvent** is not saved in the **AVSession** object or AVSession service. 172 173 ```ts 174 175 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 176 import { BusinessError } from '@kit.BasicServicesKit'; 177 178 let context: Context = getContext(this); 179 async function dispatchSessionEvent() { 180 // It is assumed that an AVSession object has been created. For details about how to create an AVSession object, see the node snippet in step 1. 181 let type: AVSessionManager.AVSessionType = 'audio'; 182 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 183 let eventName = 'dynamic_lyric'; 184 await session.dispatchSessionEvent(eventName, {lyric : 'This is my lyric'}).then(() => { 185 console.info(`Dispatch session event successfully`); 186 }).catch((err: BusinessError) => { 187 console.error(`Failed to dispatch session event. Code: ${err.code}, message: ${err.message}`); 188 }) 189 } 190 191 ``` 192 1935. Set a custom media packet. The controller performs an operation after receiving the event. 194 195 > **NOTE** 196 > 197 > The data set by using **setExtras** is stored in the AVSession service. The data lifecycle is the same as that of the **AVSession** object, and the controller corresponding to the object can use **getExtras** to obtain the data. 198 199 ```ts 200 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 201 import { BusinessError } from '@kit.BasicServicesKit'; 202 203 let context: Context = getContext(this); 204 async function setExtras() { 205 // It is assumed that an AVSession object has been created. For details about how to create an AVSession object, see the node snippet in step 1. 206 let type: AVSessionManager.AVSessionType = 'audio'; 207 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 208 await session.setExtras({extra : 'This is my custom meida packet'}).then(() => { 209 console.info(`Set extras successfully`); 210 }).catch((err: BusinessError) => { 211 console.error(`Failed to set extras. Code: ${err.code}, message: ${err.message}`); 212 }) 213 } 214 ``` 215 2166. Listen for playback control commands or events delivered by the controller, for example, Media Controller. 217 218 Both fixed playback control commands and advanced playback control events can be listened for. 219 220 6.1 Listening for Fixed Playback Control Commands 221 222 > **NOTE** 223 > 224 > After the provider registers a listener for fixed playback control commands, the commands will be reflected in **getValidCommands()** of the controller. In other words, the controller determines that the command is valid and triggers the corresponding event (not used temporarily) as required. To ensure that the playback control commands delivered by the controller can be executed normally, the provider should not use a null implementation for listening. 225 226 Fixed playback control commands on the session side include basic operation commands such as play, pause, previous, and next. For details, see [AVControlCommand](../../reference/apis-avsession-kit/js-apis-avsession.md). 227 228 ```ts 229 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 230 231 let context: Context = getContext(this); 232 async function setListenerForMesFromController() { 233 // It is assumed that an AVSession object has been created. For details about how to create an AVSession object, see the node snippet in step 1. 234 let type: AVSessionManager.AVSessionType = 'audio'; 235 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 236 // Generally, logic processing on the player is implemented in the listener. 237 // After the processing is complete, use the setter to synchronize the playback information. For details, see the code snippet above. 238 session.on('play', () => { 239 console.info(`on play , do play task`); 240 // If this command is not supported, do not register it. If the command has been registered but is not used temporarily, use session.off('play') to cancel listening. 241 // After the processing is complete, call SetAVPlayState to report the playback state. 242 }); 243 session.on('pause', () => { 244 console.info(`on pause , do pause task`); 245 // If this command is not supported, do not register it. If the command has been registered but is not used temporarily, use session.off('pause') to cancel listening. 246 // After the processing is complete, call SetAVPlayState to report the playback state. 247 }); 248 session.on('stop', () => { 249 console.info(`on stop , do stop task`); 250 // If this command is not supported, do not register it. If the command has been registered but is not used temporarily, use session.off('stop') to cancel listening. 251 // After the processing is complete, call SetAVPlayState to report the playback state. 252 }); 253 session.on('playNext', () => { 254 console.info(`on playNext , do playNext task`); 255 // If this command is not supported, do not register it. If the command has been registered but is not used temporarily, use session.off('playNext') to cancel listening. 256 // After the processing is complete, call SetAVPlayState to report the playback state and call SetAVMetadata to report the media information. 257 }); 258 session.on('playPrevious', () => { 259 console.info(`on playPrevious , do playPrevious task`); 260 // If this command is not supported, do not register it. If the command has been registered but is not used temporarily, use session.off('playPrevious') to cancel listening. 261 // After the processing is complete, call SetAVPlayState to report the playback state and call SetAVMetadata to report the media information. 262 }); 263 session.on('fastForward', () => { 264 console.info(`on fastForward , do fastForward task`); 265 // If this command is not supported, do not register it. If the command has been registered but is not used temporarily, use session.off('fastForward') to cancel listening. 266 // After the processing is complete, call SetAVPlayState to report the playback state and position. 267 }); 268 session.on('rewind', () => { 269 console.info(`on rewind , do rewind task`); 270 // If this command is not supported, do not register it. If the command has been registered but is not used temporarily, use session.off('rewind') to cancel listening. 271 // After the processing is complete, call SetAVPlayState to report the playback state and position. 272 }); 273 274 session.on('seek', (time) => { 275 console.info(`on seek , the seek time is ${time}`); 276 // If this command is not supported, do not register it. If the command has been registered but is not used temporarily, use session.off('seek') to cancel listening. 277 // After the processing is complete, call SetAVPlayState to report the playback state and position. 278 }); 279 session.on('setSpeed', (speed) => { 280 console.info(`on setSpeed , the speed is ${speed}`); 281 // do some tasks ··· 282 }); 283 session.on('setLoopMode', (mode) => { 284 console.info(`on setLoopMode , the loop mode is ${mode}`); 285 // If this command is not supported, do not register it. If the command has been registered but is not used temporarily, use session.off('setLoopMode') to cancel listening. 286 // The application determines the next loop mode. After the processing is complete, call SetAVPlayState to report the new loop mode. 287 }); 288 session.on('toggleFavorite', (assetId) => { 289 console.info(`on toggleFavorite , the target asset Id is ${assetId}`); 290 // If this command is not supported, do not register it. If the command has been registered but is not used temporarily, use session.off('toggleFavorite') to cancel listening. 291 // After the processing is complete, call SetAVPlayState to report the value of isFavorite. 292 }); 293 } 294 ``` 295 296 6.2 Listening for Advanced Playback Control Events 297 298 The following advanced playback control events can be listened for: 299 300 - **skipToQueueItem**: triggered when an item in the playlist is selected. 301 - **handleKeyEvent**: triggered when a key is pressed. 302 - **outputDeviceChange**: triggered when the output device changes. 303 - **commonCommand**: triggered when a custom playback control command changes. 304 305 ```ts 306 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 307 308 let context: Context = getContext(this); 309 async function setListenerForMesFromController() { 310 // It is assumed that an AVSession object has been created. For details about how to create an AVSession object, see the node snippet in step 1. 311 let type: AVSessionManager.AVSessionType = 'audio'; 312 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 313 // Generally, logic processing on the player is implemented in the listener. 314 // After the processing is complete, use the setter to synchronize the playback information. For details, see the code snippet above. 315 session.on('skipToQueueItem', (itemId) => { 316 console.info(`on skipToQueueItem , do skip task`); 317 // do some tasks ··· 318 }); 319 session.on('handleKeyEvent', (event) => { 320 console.info(`on handleKeyEvent , the event is ${JSON.stringify(event)}`); 321 // do some tasks ··· 322 }); 323 session.on('outputDeviceChange', (device) => { 324 console.info(`on outputDeviceChange , the device info is ${JSON.stringify(device)}`); 325 // do some tasks ··· 326 }); 327 session.on('commonCommand', (commandString, args) => { 328 console.info(`on commonCommand , command is ${commandString}, args are ${JSON.stringify(args)}`); 329 // do some tasks ··· 330 }); 331 } 332 ``` 333 3347. Obtain an **AVSessionController** object for this **AVSession** object for interaction. 335 336 ```ts 337 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 338 339 let context: Context = getContext(this); 340 async function createControllerFromSession() { 341 // It is assumed that an AVSession object has been created. For details about how to create an AVSession object, see the node snippet in step 1. 342 let type: AVSessionManager.AVSessionType = 'audio'; 343 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 344 345 // Obtain an AVSessionController object for this AVSession object. 346 let controller = await session.getController(); 347 348 // The AVSessionController object can interact with the AVSession object, for example, by delivering a playback control command. 349 let avCommand: AVSessionManager.AVControlCommand = {command:'play'}; 350 controller.sendControlCommand(avCommand); 351 352 // Alternatively, listen for state changes. 353 controller.on('playbackStateChange', 'all', (state) => { 354 355 // do some things 356 }); 357 358 // The AVSessionController object can perform many operations. For details, see the description of the controller. 359 } 360 ``` 361 3628. When the audio and video application exits and does not need to continue playback, cancel the listener and destroy the **AVSession** object. 363 364 The code snippet below is used for canceling the listener for playback control commands: 365 366 ```ts 367 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 368 369 let context: Context = getContext(this); 370 async function unregisterSessionListener() { 371 // It is assumed that an AVSession object has been created. For details about how to create an AVSession object, see the node snippet in step 1. 372 let type: AVSessionManager.AVSessionType = 'audio'; 373 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 374 375 // Cancel the listener of the AVSession object. 376 session.off('play'); 377 session.off('pause'); 378 session.off('stop'); 379 session.off('playNext'); 380 session.off('playPrevious'); 381 session.off('skipToQueueItem'); 382 session.off('handleKeyEvent'); 383 session.off('outputDeviceChange'); 384 session.off('commonCommand'); 385 } 386 ``` 387 388 The code snippet below is used for destroying the AVSession object: 389 390 ```ts 391 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 392 393 let context: Context = getContext(this); 394 async function destroySession() { 395 // It is assumed that an AVSession object has been created. For details about how to create an AVSession object, see the node snippet in step 1. 396 let type: AVSessionManager.AVSessionType = 'audio'; 397 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 398 // Destroy the AVSession object. 399 session.destroy((err) => { 400 if (err) { 401 console.error(`Failed to destroy session. Code: ${err.code}, message: ${err.message}`); 402 } else { 403 console.info(`Destroy : SUCCESS `); 404 } 405 }); 406 } 407 ``` 408