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&lt;AVSession&gt;): void<sup>10+<sup> | Creates an AVSession.<br>Only one AVSession can be created for a UIAbility.|
20| setAVMetadata(data: AVMetadata, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Sets AVSession metadata.|
21| setAVPlaybackState(state: AVPlaybackState, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Sets the AVSession playback state.|
22| setLaunchAbility(ability: WantAgent, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Starts a UIAbility.|
23| getController(callback: AsyncCallback&lt;AVSessionController&gt;): void<sup>10+<sup> | Obtains the controller of the AVSession.|
24| getOutputDevice(callback: AsyncCallback&lt;OutputDeviceInfo&gt;): void<sup>10+<sup> | Obtains the output device information.|
25| activate(callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Activates the AVSession.|
26| deactivate(callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Deactivates this session.|
27| destroy(callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Destroys the AVSession.|
28| setAVQueueItems(items: Array&lt;AVQueueItem&gt;, callback: AsyncCallback&lt;void&gt;): void <sup>10+<sup> | Sets a playlist.|
29| setAVQueueTitle(title: string, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Sets a name for the playlist.|
30| dispatchSessionEvent(event: string, args: {[key: string]: Object}, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Dispatches a custom session event.|
31| 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.|
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 { avSession as AVSessionManager } from '@kit.AVSessionKit';
139   import { wantAgent } from '@kit.AbilityKit';
140
141   let context: Context = getContext(this);
142   async function getWantAgent() {
143     let type: AVSessionManager.AVSessionType = 'audio';
144     // 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.
145     let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
146     let wantAgentInfo: wantAgent.WantAgentInfo = {
147       wants: [
148         {
149           bundleName: 'com.example.musicdemo',
150           abilityName: 'MainAbility'
151         }
152       ],
153       // OperationType.START_ABILITIES
154       operationType: 2,
155       requestCode: 0,
156       wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
157     }
158     wantAgent.getWantAgent(wantAgentInfo).then((agent) => {
159       session.setLaunchAbility(agent);
160     })
161   }
162   ```
163
1644. Set a custom session event. The controller performs an operation after receiving the event.
165
166   > **NOTE**
167   >
168   > The data set through **dispatchSessionEvent** is not saved in the **AVSession** object or AVSession service.
169
170   ```ts
171
172   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
173   import { BusinessError } from '@kit.BasicServicesKit';
174
175   let context: Context = getContext(this);
176   async function dispatchSessionEvent() {
177     // 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.
178     let type: AVSessionManager.AVSessionType = 'audio';
179     let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
180     let eventName = 'dynamic_lyric';
181     await session.dispatchSessionEvent(eventName, {lyric : 'This is my lyric'}).then(() => {
182       console.info(`Dispatch session event successfully`);
183     }).catch((err: BusinessError) => {
184       console.error(`Failed to dispatch session event. Code: ${err.code}, message: ${err.message}`);
185     })
186   }
187
188   ```
189
1905. Set a custom media packet. The controller performs an operation after receiving the event.
191
192   > **NOTE**
193   >
194   > 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.
195
196   ```ts
197   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
198   import { BusinessError } from '@kit.BasicServicesKit';
199
200   let context: Context = getContext(this);
201   async function setExtras() {
202     // 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.
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. Listen for playback control commands or events delivered by the controller, for example, Media Controller.
214
215   Both fixed playback control commands and advanced playback control events can be listened for.
216
217   6.1 Listening for Fixed Playback Control Commands
218
219   > **NOTE**
220   >
221   > 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.
222
223   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#avcontrolcommand10).
224
225   ```ts
226   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
227
228   let context: Context = getContext(this);
229   async function setListenerForMesFromController() {
230     // 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.
231     let type: AVSessionManager.AVSessionType = 'audio';
232     let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
233     // Generally, logic processing on the player is implemented in the listener.
234     // After the processing is complete, use the setter to synchronize the playback information. For details, see the code snippet above.
235     session.on('play', () => {
236       console.info(`on play , do play task`);
237       // 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.
238       // After the processing is complete, call SetAVPlayState to report the playback state.
239     });
240     session.on('pause', () => {
241       console.info(`on pause , do pause task`);
242        // 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.
243        // After the processing is complete, call SetAVPlayState to report the playback state.
244     });
245     session.on('stop', () => {
246       console.info(`on stop , do stop task`);
247        // 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.
248        // After the processing is complete, call SetAVPlayState to report the playback state.
249     });
250     session.on('playNext', () => {
251       console.info(`on playNext , do playNext task`);
252        // 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.
253        // After the processing is complete, call SetAVPlayState to report the playback state and call SetAVMetadata to report the media information.
254     });
255     session.on('playPrevious', () => {
256       console.info(`on playPrevious , do playPrevious task`);
257        // 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.
258        // After the processing is complete, call SetAVPlayState to report the playback state and call SetAVMetadata to report the media information.
259     });
260     session.on('fastForward', () => {
261       console.info(`on fastForward , do fastForward task`);
262        // 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.
263        // After the processing is complete, call SetAVPlayState to report the playback state and position.
264     });
265     session.on('rewind', () => {
266       console.info(`on rewind , do rewind task`);
267        // 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.
268        // After the processing is complete, call SetAVPlayState to report the playback state and position.
269     });
270     session.on('seek', (time) => {
271       console.info(`on seek , the seek time is ${time}`);
272        // 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.
273        // After the processing is complete, call SetAVPlayState to report the playback state and position.
274     });
275     session.on('setSpeed', (speed) => {
276       console.info(`on setSpeed , the speed is ${speed}`);
277       // do some tasks ···
278     });
279     session.on('setLoopMode', (mode) => {
280       console.info(`on setLoopMode , the loop mode is ${mode}`);
281        // 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.
282        // The application determines the next loop mode. After the processing is complete, call SetAVPlayState to report the new loop mode.
283     });
284     session.on('toggleFavorite', (assetId) => {
285       console.info(`on toggleFavorite , the target asset Id is ${assetId}`);
286        // 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.
287        // After the processing is complete, call SetAVPlayState to report the value of isFavorite.
288     });
289   }
290   ```
291
292   6.2 Listening for Advanced Playback Control Events
293
294   The following advanced playback control events can be listened for:
295
296   - **skipToQueueItem**: triggered when an item in the playlist is selected.
297   - **handleKeyEvent**: triggered when a key is pressed.
298   - **outputDeviceChange**: triggered when the output device changes.
299   - **commonCommand**: triggered when a custom playback control command changes.
300
301   ```ts
302   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
303
304   let context: Context = getContext(this);
305   async function setListenerForMesFromController() {
306     // 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.
307     let type: AVSessionManager.AVSessionType = 'audio';
308     let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
309     // Generally, logic processing on the player is implemented in the listener.
310     // After the processing is complete, use the setter to synchronize the playback information. For details, see the code snippet above.
311     session.on('skipToQueueItem', (itemId) => {
312       console.info(`on skipToQueueItem , do skip task`);
313       // do some tasks ···
314     });
315     session.on('handleKeyEvent', (event) => {
316       console.info(`on handleKeyEvent , the event is ${JSON.stringify(event)}`);
317       // do some tasks ···
318     });
319     session.on('outputDeviceChange', (device) => {
320       console.info(`on outputDeviceChange , the device info is ${JSON.stringify(device)}`);
321       // do some tasks ···
322     });
323     session.on('commonCommand', (commandString, args) => {
324       console.info(`on commonCommand , command is ${commandString}, args are ${JSON.stringify(args)}`);
325       // do some tasks ···
326     });
327   }
328   ```
329
3307. Obtain an **AVSessionController** object for this **AVSession** object for interaction.
331
332   ```ts
333   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
334
335   let context: Context = getContext(this);
336   async function createControllerFromSession() {
337     // 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.
338     let type: AVSessionManager.AVSessionType = 'audio';
339     let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
340
341     // Obtain an AVSessionController object for this AVSession object.
342     let controller = await session.getController();
343
344     // The AVSessionController object can interact with the AVSession object, for example, by delivering a playback control command.
345     let avCommand: AVSessionManager.AVControlCommand = {command:'play'};
346     controller.sendControlCommand(avCommand);
347
348     // Alternatively, listen for state changes.
349     controller.on('playbackStateChange', 'all', (state) => {
350
351       // do some things
352     });
353
354     // The AVSessionController object can perform many operations. For details, see the description of the controller.
355   }
356   ```
357
3588. When the audio and video application exits and does not need to continue playback, cancel the listener and destroy the **AVSession** object.
359
360   The code snippet below is used for canceling the listener for playback control commands:
361
362   ```ts
363   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
364
365   let context: Context = getContext(this);
366   async function unregisterSessionListener() {
367     // 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.
368     let type: AVSessionManager.AVSessionType = 'audio';
369     let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
370
371     // Cancel the listener of the AVSession object.
372     session.off('play');
373     session.off('pause');
374     session.off('stop');
375     session.off('playNext');
376     session.off('playPrevious');
377     session.off('skipToQueueItem');
378     session.off('handleKeyEvent');
379     session.off('outputDeviceChange');
380     session.off('commonCommand');
381   }
382   ```
383
384     The code snippet below is used for destroying the AVSession object:
385
386   ```ts
387   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
388
389   let context: Context = getContext(this);
390   async function destroySession() {
391     // 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.
392     let type: AVSessionManager.AVSessionType = 'audio';
393     let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
394     // Destroy the AVSession object.
395     session.destroy((err) => {
396       if (err) {
397         console.error(`Failed to destroy session. Code: ${err.code}, message: ${err.message}`);
398       } else {
399         console.info(`Destroy : SUCCESS `);
400       }
401     });
402   }
403   ```
404