• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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&lt;AVSession&gt;): void<sup>10+<sup> | Creates an AVSession.<br>Only one AVSession can be created for a UIAbility.|
25| setAVMetadata(data: AVMetadata, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Sets AVSession metadata.|
26| setAVPlaybackState(state: AVPlaybackState, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Sets the AVSession playback state.|
27| setLaunchAbility(ability: WantAgent, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Starts a UIAbility.|
28| getController(callback: AsyncCallback&lt;AVSessionController&gt;): void<sup>10+<sup> | Obtains the controller of the AVSession.|
29| getOutputDevice(callback: AsyncCallback&lt;OutputDeviceInfo&gt;): void<sup>10+<sup> | Obtains the output device information.|
30| activate(callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Activates the AVSession.|
31| deactivate(callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Deactivates this session.|
32| destroy(callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Destroys the AVSession.|
33| setAVQueueItems(items: Array&lt;AVQueueItem&gt;, callback: AsyncCallback&lt;void&gt;): void <sup>10+<sup> | Sets a playlist.|
34| setAVQueueTitle(title: string, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Sets a name for the playlist.|
35| dispatchSessionEvent(event: string, args: {[key: string]: Object}, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Dispatches a custom session event.|
36| setExtras(extras: {[key: string]: Object}, callback: AsyncCallback&lt;void&gt;): 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