1# AVSession Provider 2<!--Kit: AVSession Kit--> 3<!--Subsystem: Multimedia--> 4<!--Owner: @ccfriend; @liao_qian--> 5<!--SE: @ccfriend--> 6<!--TSE: @chenmingxi1_huawei--> 7 8An 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. 9 10## Basic Concepts 11 12- 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. 13 14- 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). 15 16## Available APIs 17 18The 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. 19 20For details, see [AVSession Management](../../reference/apis-avsession-kit/arkts-apis-avsession.md). 21 22| API| Description| 23| -------- | -------- | 24| 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.| 25| setAVMetadata(data: AVMetadata, callback: AsyncCallback<void>): void<sup>10+<sup> | Sets AVSession metadata.| 26| setAVPlaybackState(state: AVPlaybackState, callback: AsyncCallback<void>): void<sup>10+<sup> | Sets the AVSession playback state.| 27| setLaunchAbility(ability: WantAgent, callback: AsyncCallback<void>): void<sup>10+<sup> | Starts a UIAbility.| 28| getController(callback: AsyncCallback<AVSessionController>): void<sup>10+<sup> | Obtains the controller of the AVSession.| 29| getOutputDevice(callback: AsyncCallback<OutputDeviceInfo>): void<sup>10+<sup> | Obtains the output device information.| 30| activate(callback: AsyncCallback<void>): void<sup>10+<sup> | Activates the AVSession.| 31| deactivate(callback: AsyncCallback<void>): void<sup>10+<sup> | Deactivates this session.| 32| destroy(callback: AsyncCallback<void>): void<sup>10+<sup> | Destroys the AVSession.| 33| setAVQueueItems(items: Array<AVQueueItem>, callback: AsyncCallback<void>): void <sup>10+<sup> | Sets a playlist.| 34| setAVQueueTitle(title: string, callback: AsyncCallback<void>): void<sup>10+<sup> | Sets a name for the playlist.| 35| dispatchSessionEvent(event: string, args: {[key: string]: Object}, callback: AsyncCallback<void>): void<sup>10+<sup> | Dispatches a custom session event.| 36| 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.| 37| getOutputDeviceSync(): OutputDeviceInfo<sup>10+<sup> | Obtains the output device information. This API is a synchronous API.| 38 39## How to Develop 40 41To enable an audio and video application to access the AVSession service as a provider, proceed as follows: 42 431. Call an API in the **AVSessionManager** class to create and activate an AVSession object. 44 45 > **NOTE** 46 > 47 > The sample code below demonstrates only the API call for creating an AVSession object. When actually using it, the application must ensure that the AVSession object remains throughout the application's background playback activities. This prevents the system from reclaiming or releasing it, which could lead to playback being controlled by the system. 48 49 ```ts 50 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 51 @Entry 52 @Component 53 struct Index { 54 @State message: string = 'hello world'; 55 build() { 56 Column() { 57 Text(this.message) 58 .onClick(async () => { 59 // Start to create and activate an AVSession object. 60 // Create an AVSession object. 61 let context = this.getUIContext().getHostContext() as Context; 62 let type: AVSessionManager.AVSessionType = 'audio'; 63 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 64 await session.activate(); 65 console.info(`session create done : sessionId : ${session.sessionId}`); 66 }) 67 } 68 .width('100%') 69 .height('100%') 70 } 71 } 72 ``` 73 742. Set AVSession information, which includes: 75 - AVMetadata 76 - AVPlaybackState 77 78 The controller will call an API in the **AVSessionController** class to obtain the information and display or process the information. 79 80 ```ts 81 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 82 import { BusinessError } from '@kit.BasicServicesKit'; 83 84 @Entry 85 @Component 86 struct Index { 87 @State message: string = 'hello world'; 88 89 build() { 90 Column() { 91 Text(this.message) 92 .onClick(async () => { 93 let context = this.getUIContext().getHostContext() as Context; 94 // 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. 95 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', 'audio'); 96 // The player logic that triggers changes in the session metadata and playback state is omitted here. 97 // Set necessary AVSession metadata. 98 let metadata: AVSessionManager.AVMetadata = { 99 assetId: '0', // Specified by the application, used to identify the media asset in the application media library. 100 title: 'TITLE', 101 mediaImage: 'IMAGE', 102 artist: 'ARTIST' 103 }; 104 session.setAVMetadata(metadata).then(() => { 105 console.info(`SetAVMetadata successfully`); 106 }).catch((err: BusinessError) => { 107 console.error(`Failed to set AVMetadata. Code: ${err.code}, message: ${err.message}`); 108 }); 109 // Set the playback state to paused and set isFavorite to false. 110 let playbackState: AVSessionManager.AVPlaybackState = { 111 state: AVSessionManager.PlaybackState.PLAYBACK_STATE_PAUSE, 112 isFavorite: false 113 }; 114 session.setAVPlaybackState(playbackState, (err) => { 115 if (err) { 116 console.error(`Failed to set AVPlaybackState. Code: ${err.code}, message: ${err.message}`); 117 } else { 118 console.info(`SetAVPlaybackState successfully`); 119 } 120 }); 121 // Set a playlist. 122 let queueItemDescription_1: AVSessionManager.AVMediaDescription = { 123 assetId: '001', 124 title: 'music_name', 125 subtitle: 'music_sub_name', 126 description: 'music_description', 127 mediaImage: "PIXELMAP_OBJECT", 128 extras: { 'extras': 'any' } 129 }; 130 let queueItem_1: AVSessionManager.AVQueueItem = { 131 itemId: 1, 132 description: queueItemDescription_1 133 }; 134 let queueItemDescription_2: AVSessionManager.AVMediaDescription = { 135 assetId: '002', 136 title: 'music_name', 137 subtitle: 'music_sub_name', 138 description: 'music_description', 139 mediaImage: "PIXELMAP_OBJECT", 140 extras: { 'extras': 'any' } 141 }; 142 let queueItem_2: AVSessionManager.AVQueueItem = { 143 itemId: 2, 144 description: queueItemDescription_2 145 }; 146 let queueItemsArray = [queueItem_1, queueItem_2]; 147 session.setAVQueueItems(queueItemsArray).then(() => { 148 console.info(`SetAVQueueItems successfully`); 149 }).catch((err: BusinessError) => { 150 console.error(`Failed to set AVQueueItem, error code: ${err.code}, error message: ${err.message}`); 151 }); 152 // Set a name for the playlist. 153 let queueTitle = 'QUEUE_TITLE'; 154 session.setAVQueueTitle(queueTitle).then(() => { 155 console.info(`SetAVQueueTitle successfully`); 156 }).catch((err: BusinessError) => { 157 console.error(`Failed to set AVQueueTitle, error code: ${err.code}, error message: ${err.message}`); 158 }); 159 }) 160 } 161 .width('100%') 162 .height('100%') 163 } 164 } 165 ``` 166 1673. 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. 168 The UIAbility is set through the **WantAgent** API. For details, see [WantAgent](../../reference/apis-ability-kit/js-apis-app-ability-wantAgent.md). 169 170 ```ts 171 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 172 import { wantAgent } from '@kit.AbilityKit'; 173 174 @Entry 175 @Component 176 struct Index { 177 @State message: string = 'hello world'; 178 179 build() { 180 Column() { 181 Text(this.message) 182 .onClick(async () => { 183 let context = this.getUIContext().getHostContext() as Context; 184 let type: AVSessionManager.AVSessionType = 'audio'; 185 // 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. 186 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 187 let wantAgentInfo: wantAgent.WantAgentInfo = { 188 wants: [ 189 { 190 bundleName: 'com.example.musicdemo', 191 abilityName: 'MainAbility' 192 } 193 ], 194 // OperationType.START_ABILITIES 195 operationType: 2, 196 requestCode: 0, 197 wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG] 198 } 199 wantAgent.getWantAgent(wantAgentInfo).then((agent) => { 200 session.setLaunchAbility(agent); 201 }) 202 }) 203 } 204 .width('100%') 205 .height('100%') 206 } 207 } 208 ``` 209 2104. Set a custom session event. The controller performs an operation after receiving the event. 211 212 > **NOTE** 213 > 214 > The data set through **dispatchSessionEvent** is not saved in the AVSession object or AVSession service. 215 ```ts 216 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 217 import { BusinessError } from '@kit.BasicServicesKit'; 218 219 @Entry 220 @Component 221 struct Index { 222 @State message: string = 'hello world'; 223 224 build() { 225 Column() { 226 Text(this.message) 227 .onClick(async () => { 228 let context = this.getUIContext().getHostContext() as Context; 229 // 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. 230 let type: AVSessionManager.AVSessionType = 'audio'; 231 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 232 let eventName = 'dynamic_lyric'; 233 await session.dispatchSessionEvent(eventName, { lyric: 'This is my lyric' }).then(() => { 234 console.info(`Dispatch session event successfully`); 235 }).catch((err: BusinessError) => { 236 console.error(`Failed to dispatch session event. Code: ${err.code}, message: ${err.message}`); 237 }) 238 }) 239 } 240 .width('100%') 241 .height('100%') 242 } 243 } 244 ``` 245 2465. Set a custom media packet. The controller performs an operation after receiving the event. 247 248 > **NOTE** 249 > 250 > 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. 251 252 ```ts 253 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 254 import { BusinessError } from '@kit.BasicServicesKit'; 255 256 @Entry 257 @Component 258 struct Index { 259 @State message: string = 'hello world'; 260 261 build() { 262 Column() { 263 Text(this.message) 264 .onClick(async () => { 265 let context = this.getUIContext().getHostContext() as Context; 266 // 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. 267 let type: AVSessionManager.AVSessionType = 'audio'; 268 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 269 await session.setExtras({ extra: 'This is my custom meida packet' }).then(() => { 270 console.info(`Set extras successfully`); 271 }).catch((err: BusinessError) => { 272 console.error(`Failed to set extras. Code: ${err.code}, message: ${err.message}`); 273 }) 274 }) 275 } 276 .width('100%') 277 .height('100%') 278 } 279 } 280 ``` 281 2826. Listen for playback control commands or events delivered by the controller, for example, Media Controller. 283 284 Both fixed playback control commands and advanced playback control events can be listened for. 285 286 6.1 Listen for fixed playback control commands. 287 288 > **NOTE** 289 > 290 > 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. 291 292 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/arkts-apis-avsession-i.md#avcontrolcommand10). 293 294 295 ```ts 296 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 297 298 @Entry 299 @Component 300 struct Index { 301 @State message: string = 'hello world'; 302 303 build() { 304 Column() { 305 Text(this.message) 306 .onClick(async () => { 307 let context = this.getUIContext().getHostContext() as Context; 308 // 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. 309 let type: AVSessionManager.AVSessionType = 'audio'; 310 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 311 // Generally, logic processing on the player is implemented in the listener. 312 // After the processing is complete, use the setter to synchronize the playback information. For details, see the code snippet above. 313 session.on('play', () => { 314 console.info(`on play , do play task`); 315 // 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. 316 // After the processing is complete, call SetAVPlaybackState to report the playback state. 317 }); 318 session.on('pause', () => { 319 console.info(`on pause , do pause task`); 320 // 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. 321 // After the processing is complete, call SetAVPlaybackState to report the playback state. 322 }); 323 session.on('stop', () => { 324 console.info(`on stop , do stop task`); 325 // 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. 326 // After the processing is complete, call SetAVPlaybackState to report the playback state. 327 }); 328 session.on('playNext', () => { 329 console.info(`on playNext , do playNext task`); 330 // 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. 331 // After the processing is complete, call SetAVPlaybackState to report the playback state and call SetAVMetadata to report the media information. 332 }); 333 session.on('playPrevious', () => { 334 console.info(`on playPrevious , do playPrevious task`); 335 // 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. 336 // After the processing is complete, call SetAVPlaybackState to report the playback state and call SetAVMetadata to report the media information. 337 }); 338 session.on('fastForward', () => { 339 console.info(`on fastForward , do fastForward task`); 340 // 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. 341 // After the processing is complete, call SetAVPlaybackState to report the playback state and position. 342 }); 343 session.on('rewind', () => { 344 console.info(`on rewind , do rewind task`); 345 // 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. 346 // After the processing is complete, call SetAVPlaybackState to report the playback state and position. 347 }); 348 session.on('seek', (time) => { 349 console.info(`on seek , the seek time is ${time}`); 350 // 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. 351 // After the processing is complete, call SetAVPlaybackState to report the playback state and position. 352 }); 353 session.on('setSpeed', (speed) => { 354 console.info(`on setSpeed , the speed is ${speed}`); 355 // do some tasks ··· 356 }); 357 session.on('setLoopMode', (mode) => { 358 console.info(`on setLoopMode , the loop mode is ${mode}`); 359 // 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. 360 // The application determines the next loop mode. After the processing is complete, call SetAVPlaybackState to report the new loop mode. 361 }); 362 session.on('toggleFavorite', (assetId) => { 363 console.info(`on toggleFavorite , the target asset Id is ${assetId}`); 364 // 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. 365 // After the processing is complete, call SetAVPlaybackState to report the value of isFavorite. 366 }); 367 }) 368 } 369 .width('100%') 370 .height('100%') 371 } 372 } 373 ``` 374 375 6.2 Listen for advanced playback control events. 376 377 The following advanced playback control events can be listened for: 378 379 - **skipToQueueItem**: triggered when an item in the playlist is selected. 380 - **handleKeyEvent**: triggered when a key is pressed. 381 - **outputDeviceChange**: triggered when the output device changes. 382 - **commonCommand**: triggered when a custom playback control command changes. 383 384 ```ts 385 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 386 387 @Entry 388 @Component 389 struct Index { 390 @State message: string = 'hello world'; 391 392 build() { 393 Column() { 394 Text(this.message) 395 .onClick(async () => { 396 let context = this.getUIContext().getHostContext() as Context; 397 // 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. 398 let type: AVSessionManager.AVSessionType = 'audio'; 399 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 400 // Generally, logic processing on the player is implemented in the listener. 401 // After the processing is complete, use the setter to synchronize the playback information. For details, see the code snippet above. 402 session.on('skipToQueueItem', (itemId) => { 403 console.info(`on skipToQueueItem , do skip task`); 404 // do some tasks ··· 405 }); 406 session.on('handleKeyEvent', (event) => { 407 console.info(`on handleKeyEvent , the event is ${JSON.stringify(event)}`); 408 // do some tasks ··· 409 }); 410 session.on('outputDeviceChange', (device) => { 411 console.info(`on outputDeviceChange , the device info is ${JSON.stringify(device)}`); 412 // do some tasks ··· 413 }); 414 session.on('commonCommand', (commandString, args) => { 415 console.info(`on commonCommand , command is ${commandString}, args are ${JSON.stringify(args)}`); 416 // do some tasks ··· 417 }); 418 }) 419 } 420 .width('100%') 421 .height('100%') 422 } 423 } 424 ``` 425 4267. Obtain an AVSessionController object for this AVSession object for interaction. 427 428 ```ts 429 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 430 431 @Entry 432 @Component 433 struct Index { 434 @State message: string = 'hello world'; 435 436 build() { 437 Column() { 438 Text(this.message) 439 .onClick(async () => { 440 let context = this.getUIContext().getHostContext() as Context; 441 // 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. 442 let type: AVSessionManager.AVSessionType = 'audio'; 443 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 444 445 // Obtain an AVSessionController object for this AVSession object. 446 let controller = await session.getController(); 447 448 // The AVSessionController object can interact with the AVSession object, for example, by delivering a playback control command. 449 let avCommand: AVSessionManager.AVControlCommand = { command: 'play' }; 450 controller.sendControlCommand(avCommand); 451 452 // Alternatively, listen for state changes. 453 controller.on('playbackStateChange', 'all', (state) => { 454 455 // do some things. 456 }); 457 }) 458 } 459 .width('100%') 460 .height('100%') 461 } 462 } 463 ``` 464 4658. When the audio and video application exits and does not need to continue playback, cancel the listener and destroy the AVSession object. 466 467 The code snippet below is used for canceling the listener for playback control commands: 468 469 ```ts 470 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 471 472 @Entry 473 @Component 474 struct Index { 475 @State message: string = 'hello world'; 476 477 build() { 478 Column() { 479 Text(this.message) 480 .onClick(async () => { 481 let context = this.getUIContext().getHostContext() as Context; 482 // 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. 483 let type: AVSessionManager.AVSessionType = 'audio'; 484 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 485 486 // Cancel the listener of the AVSession object. 487 session.off('play'); 488 session.off('pause'); 489 session.off('stop'); 490 session.off('playNext'); 491 session.off('playPrevious'); 492 session.off('skipToQueueItem'); 493 session.off('handleKeyEvent'); 494 session.off('outputDeviceChange'); 495 session.off('commonCommand'); 496 }) 497 } 498 .width('100%') 499 .height('100%') 500 } 501 } 502 ``` 503 504 The code snippet below is used for destroying the AVSession object: 505 506 ```ts 507 import { avSession as AVSessionManager } from '@kit.AVSessionKit'; 508 509 @Entry 510 @Component 511 struct Index { 512 @State message: string = 'hello world'; 513 514 build() { 515 Column() { 516 Text(this.message) 517 .onClick(async () => { 518 let context = this.getUIContext().getHostContext() as Context; 519 // 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. 520 let type: AVSessionManager.AVSessionType = 'audio'; 521 let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type); 522 // Destroy the AVSession object. 523 session.destroy((err) => { 524 if (err) { 525 console.error(`Failed to destroy session. Code: ${err.code}, message: ${err.message}`); 526 } else { 527 console.info(`Destroy : SUCCESS `); 528 } 529 }); 530 }) 531 } 532 .width('100%') 533 .height('100%') 534 } 535 } 536 ``` 537 538