• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# AVSession Controller (for System Applications Only)
2<!--Kit: AVSession Kit-->
3<!--Subsystem: Multimedia-->
4<!--Owner: @ccfriend; @liao_qian-->
5<!--SE: @ccfriend-->
6<!--TSE: @chenmingxi1_huawei-->
7
8Media Controller preset in OpenHarmony functions as the controller to interact with audio and video applications, for example, obtaining and displaying media information and delivering playback control commands.
9
10You can develop a system application (for example, a new playback control center or voice assistant) as the controller to interact with audio and video applications in the system.
11
12## Basic Concepts
13
14- AVSessionDescriptor: session information, including the session ID, session type (audio/video), custom session name (**sessionTag**), information about the corresponding application (**elementName**), and whether the session is pined on top (isTopSession).
15
16- Top session: session with the highest priority in the system, for example, a session that is being played. Generally, the controller must hold an AVSessionController object to communicate with a session. However, the controller can directly communicate with the top session, for example, directly sending a playback control command or key event, without holding an AVSessionController object.
17
18## Available APIs
19
20The key APIs used by the controller are classified into the following types:
21
221. APIs called by the AVSessionManager object, which is obtained by means of import. An example API is **AVSessionManager.createController(sessionId)**.
232. APIs called by the AVSessionController object. An example API is **controller.getAVPlaybackState()**.
24
25Asynchronous JavaScript 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.
26
27For details, see [AVSession Management](../../reference/apis-avsession-kit/arkts-apis-avsession.md).
28
29### APIs Called by the AVSessionManager Object
30
31| API| Description|
32| -------- | -------- |
33| getAllSessionDescriptors(callback: AsyncCallback&lt;Array&lt;Readonly&lt;AVSessionDescriptor&gt;&gt;&gt;): void | Obtains the descriptors of all AVSessions in the system.|
34| createController(sessionId: string, callback: AsyncCallback&lt;AVSessionController&gt;): void | Creates an AVSessionController.|
35| sendSystemAVKeyEvent(event: KeyEvent, callback: AsyncCallback&lt;void&gt;): void | Sends a key event to the top session.|
36| sendSystemControlCommand(command: AVControlCommand, callback: AsyncCallback&lt;void&gt;): void | Sends a playback control command to the top session.|
37| getHistoricalSessionDescriptors(maxSize: number, callback: AsyncCallback\<Array\<Readonly\<AVSessionDescriptor>>>): void<sup>10+<sup> | Obtains the descriptors of historical sessions.|
38
39### APIs Called by the AVSessionController Object
40
41| API| Description|
42| -------- | -------- |
43| getAVPlaybackState(callback: AsyncCallback&lt;AVPlaybackState&gt;): void<sup>10+<sup> | Obtains the information related to the playback state.|
44| getAVMetadata(callback: AsyncCallback&lt;AVMetadata&gt;): void<sup>10+<sup> | Obtains the session metadata.|
45| getOutputDevice(callback: AsyncCallback&lt;OutputDeviceInfo&gt;): void<sup>10+<sup> | Obtains the output device information.|
46| sendAVKeyEvent(event: KeyEvent, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Sends a key event to the session corresponding to this controller.|
47| getLaunchAbility(callback: AsyncCallback&lt;WantAgent&gt;): void<sup>10+<sup> | Obtains the WantAgent object saved by the application in the session.|
48| isActive(callback: AsyncCallback&lt;boolean&gt;): void<sup>10+<sup> | Checks whether the session is activated.|
49| destroy(callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Destroys this controller. A controller can no longer be used after being destroyed.|
50| getValidCommands(callback: AsyncCallback&lt;Array&lt;AVControlCommandType&gt;&gt;): void<sup>10+<sup> | Obtains valid commands supported by the session.|
51| sendControlCommand(command: AVControlCommand, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Sends a playback control command to the session through the controller.|
52| sendCommonCommand(command: string, args: {[key: string]: Object}, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Sends a custom playback control command to the session through the controller.|
53| getAVQueueItems(callback: AsyncCallback&lt;Array&lt;AVQueueItem&gt;&gt;): void<sup>10+<sup> | Obtains the information related to the items in the playlist.|
54| getAVQueueTitle(callback: AsyncCallback&lt;string&gt;): void<sup>10+<sup> | Obtains the name of the playlist.|
55| skipToQueueItem(itemId: number, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | Sends the ID of an item in the playlist to the session for processing. The session can play the song.|
56| getExtras(callback: AsyncCallback&lt;{[key: string]: Object}&gt;): void<sup>10+<sup> | Obtains the custom media packet set by the provider.|
57| getOutputDeviceSync(): OutputDeviceInfo<sup>10+<sup> | Obtains the output device information. This API returns the result synchronously.|
58| getAVPlaybackStateSync(): AVPlaybackState<sup>10+<sup> | Obtains the information related to the playback state. This API returns the result synchronously.|
59| getAVMetadataSync(): AVMetadata<sup>10+<sup> | Obtains the session metadata. This API returns the result synchronously.|
60| getAVQueueTitleSync(): string<sup>10+<sup> | Obtains the name of the playlist. This API returns the result synchronously.|
61| getAVQueueItemsSync(): Array&lt;AVQueueItem&gt;<sup>10+<sup> | Obtains the information related to the items in the playlist. This API returns the result synchronously.|
62| isActiveSync(): boolean<sup>10+<sup> | Checks whether the session is activated. This API returns the result synchronously.|
63| getValidCommandsSync(): Array&lt;AVControlCommandType&gt;<sup>10+<sup> | Obtains valid commands supported by the session. This API returns the result synchronously.|
64
65## How to Develop
66
67To enable a system application to access the AVSession service as a controller, proceed as follows:
68
691. Obtain AVSessionDescriptor through AVSessionManager and create an AVSessionController object.
70
71   The controller may obtain all AVSessionDescriptors in the current system, and create an AVSessionController object for each session, so as to perform unified playback control on all the audio and video applications.
72
73   ```ts
74   // Import the AVSessionManager module.
75   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
76   import { BusinessError } from '@kit.BasicServicesKit';
77
78   // Define global variables.
79   let g_controller = new Array<AVSessionManager.AVSessionController>();
80   let g_centerSupportCmd:Set<AVSessionManager.AVControlCommandType> = new Set(['play', 'pause', 'playNext', 'playPrevious', 'fastForward', 'rewind', 'seek','setSpeed', 'setLoopMode', 'toggleFavorite']);
81   let g_validCmd:Set<AVSessionManager.AVControlCommandType>;
82   // Obtain the session descriptors and create an AVSessionController object.
83   AVSessionManager.getAllSessionDescriptors().then((descriptors) => {
84     descriptors.forEach((descriptor) => {
85       AVSessionManager.createController(descriptor.sessionId).then((controller) => {
86         g_controller.push(controller);
87       }).catch((err: BusinessError) => {
88         console.error(`Failed to create controller. Code: ${err.code}, message: ${err.message}`);
89       });
90     });
91   }).catch((err: BusinessError) => {
92     console.error(`Failed to get all session descriptors. Code: ${err.code}, message: ${err.message}`);
93   });
94
95   // Obtain the descriptors of historical sessions.
96   AVSessionManager.getHistoricalSessionDescriptors().then((descriptors) => {
97     console.info(`getHistoricalSessionDescriptors : SUCCESS : descriptors.length : ${descriptors.length}`);
98     if (descriptors.length > 0){
99       console.info(`getHistoricalSessionDescriptors : SUCCESS : descriptors[0].isActive : ${descriptors[0].isActive}`);
100       console.info(`getHistoricalSessionDescriptors : SUCCESS : descriptors[0].type : ${descriptors[0].type}`);
101       console.info(`getHistoricalSessionDescriptors : SUCCESS : descriptors[0].sessionTag : ${descriptors[0].sessionTag}`);
102       console.info(`getHistoricalSessionDescriptors : SUCCESS : descriptors[0].sessionId : ${descriptors[0].sessionId}`);
103       console.info(`getHistoricalSessionDescriptors : SUCCESS : descriptors[0].elementName.bundleName : ${descriptors[0].elementName.bundleName}`);
104     }
105   }).catch((err: BusinessError) => {
106     console.error(`Failed to get historical session descriptors, error code: ${err.code}, error message: ${err.message}`);
107   });
108   ```
109
1102. Listen for the session state and service state events.
111
112   The following session state events are available:
113
114   - **sessionCreate**: triggered when a session is created.
115   - **sessionDestroy**: triggered when a session is destroyed.
116   - **topSessionChange**: triggered when the top session is changed.
117
118   The service state event **sessionServiceDie** is reported when the AVSession service is abnormal.
119
120   ```ts
121   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
122   import { BusinessError } from '@kit.BasicServicesKit';
123
124   let g_controller = new Array<AVSessionManager.AVSessionController>();
125   // Subscribe to the 'sessionCreate' event and create an AVSessionController object.
126   AVSessionManager.on('sessionCreate', (session) => {
127     // After an AVSession is added, you must create an AVSessionController object.
128     AVSessionManager.createController(session.sessionId).then((controller) => {
129       g_controller.push(controller);
130     }).catch((err: BusinessError) => {
131       console.error(`Failed to create controller. Code: ${err.code}, message: ${err.message}`);
132     });
133   });
134
135   // Subscribe to the 'sessionDestroy' event to enable the application to get notified when the session dies.
136   AVSessionManager.on('sessionDestroy', (session) => {
137     let index = g_controller.findIndex((controller) => {
138       return controller.sessionId === session.sessionId;
139     });
140     if (index !== 0) {
141       g_controller[index].destroy();
142       g_controller.splice(index, 1);
143     }
144   });
145   // Subscribe to the 'topSessionChange' event.
146   AVSessionManager.on('topSessionChange', (session) => {
147     let index = g_controller.findIndex((controller) => {
148       return controller.sessionId === session.sessionId;
149     });
150     // Place the session on the top.
151     if (index !== 0) {
152       g_controller.sort((a, b) => {
153         return a.sessionId === session.sessionId ? -1 : 0;
154       });
155     }
156   });
157   // Subscribe to the 'sessionServiceDie' event.
158   AVSessionManager.on('sessionServiceDie', () => {
159     // The server is abnormal, and the application clears resources.
160     console.info(`Server exception.`);
161   })
162   ```
163
1643. Subscribe to media information changes and other session events.
165
166   The following media information change events are available:
167
168   - **metadataChange**: triggered when the session metadata changes.
169   - **playbackStateChange**: triggered when the playback state changes.
170   - **activeStateChange**: triggered when the activation state of the session changes.
171   - **validCommandChange**: triggered when the valid commands supported by the session changes.
172   - **outputDeviceChange**: triggered when the output device changes.
173   - **sessionDestroy**: triggered when a session is destroyed.
174   - **sessionEvent**: triggered when the custom session event changes.
175   - **extrasChange**: triggered when the custom media packet of the session changes.
176   - **queueItemsChange**: triggered when one or more items in the custom playlist of the session changes.
177   - **queueTitleChange**: triggered when the custom playlist name of the session changes.
178
179   The controller can listen for events as required.
180
181   ```ts
182   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
183   import { BusinessError } from '@kit.BasicServicesKit';
184
185   let g_controller = new Array<AVSessionManager.AVSessionController>();
186   let controller = g_controller[0];
187   let g_validCmd:Set<AVSessionManager.AVControlCommandType>;
188   let g_centerSupportCmd:Set<AVSessionManager.AVControlCommandType> = new Set(['play', 'pause', 'playNext', 'playPrevious', 'fastForward', 'rewind', 'seek','setSpeed', 'setLoopMode', 'toggleFavorite']);
189   // Subscribe to the 'activeStateChange' event.
190   controller.on('activeStateChange', (isActive) => {
191     if (isActive) {
192       console.info(`The widget corresponding to the controller is highlighted.`);
193     } else {
194       console.info(`The widget corresponding to the controller is invalid.`);
195     }
196   });
197   // Subscribe to the 'sessionDestroy' event to enable the controller to get notified when the session dies.
198   controller.on('sessionDestroy', () => {
199     console.info(`on sessionDestroy : SUCCESS `);
200     controller.destroy().then(() => {
201       console.info(`destroy : SUCCESS`);
202     }).catch((err: BusinessError) => {
203       console.error(`Failed to destroy session. Code: ${err.code}, message: ${err.message}`);
204     });
205   });
206
207   // Subscribe to metadata changes.
208   controller.on('metadataChange', ['assetId', 'title', 'description'], (metadata: AVSessionManager.AVMetadata) => {
209     console.info(`on metadataChange assetId : ${metadata.assetId}`);
210   });
211   // Subscribe to playback state changes.
212   controller.on('playbackStateChange', ['state', 'speed', 'loopMode'], (playbackState: AVSessionManager.AVPlaybackState) => {
213     console.info(`on playbackStateChange state : ${playbackState.state}`);
214   });
215   // Subscribe to supported command changes.
216   controller.on('validCommandChange', (cmds) => {
217     console.info(`validCommandChange : SUCCESS : size : ${cmds.length}`);
218     console.info(`validCommandChange : SUCCESS : cmds : ${cmds.values()}`);
219     g_validCmd.clear();
220     let centerSupportCmd = Array.from(g_centerSupportCmd.values())
221     for (let c of centerSupportCmd) {
222       if (cmds.concat(c)) {
223         g_validCmd.add(c);
224       }
225     }
226   });
227   // Subscribe to output device changes.
228   controller.on('outputDeviceChange', (state, device) => {
229     console.info(`outputDeviceChange device are : ${JSON.stringify(device)}`);
230   });
231   // Subscribe to custom session event changes.
232   controller.on('sessionEvent', (eventName, eventArgs) => {
233     console.info(`Received new session event, event name is ${eventName}, args are ${JSON.stringify(eventArgs)}`);
234   });
235   // Subscribe to custom media packet changes.
236   controller.on('extrasChange', (extras) => {
237     console.info(`Received custom media packet, packet data is ${JSON.stringify(extras)}`);
238   });
239   // Subscribe to custom playlist item changes.
240   controller.on('queueItemsChange', (items) => {
241     console.info(`Caught queue items change, items length is ${items.length}`);
242   });
243   // Subscribe to custom playback name changes.
244   controller.on('queueTitleChange', (title) => {
245     console.info(`Caught queue title change, title is ${title}`);
246   });
247   ```
248
2494. Obtain the media information transferred by the provider for display on the UI, for example, displaying the track being played and the playback state in Media Controller.
250
251   ```ts
252   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
253   async function getInfoFromSessionByController() {
254     // It is assumed that an AVSessionController object corresponding to the session already exists. For details about how to create an AVSessionController object, see the code snippet above.
255     let controller = await AVSessionManager.createController("");
256     // Obtain the session ID.
257     let sessionId = controller.sessionId;
258     console.info(`get sessionId by controller : isActive : ${sessionId}`);
259     // Obtain the activation state of the session.
260     let isActive = await controller.isActive();
261     console.info(`get activeState by controller : ${isActive}`);
262     // Obtain the media information of the session.
263     let metadata = await controller.getAVMetadata();
264     console.info(`get media title by controller : ${metadata.title}`);
265     console.info(`get media artist by controller : ${metadata.artist}`);
266     // Obtain the playback information of the session.
267     let avPlaybackState = await controller.getAVPlaybackState();
268     console.info(`get playbackState by controller : ${avPlaybackState.state}`);
269     console.info(`get favoriteState by controller : ${avPlaybackState.isFavorite}`);
270     // Obtain the playlist items of the session.
271     let queueItems = await controller.getAVQueueItems();
272     console.info(`get queueItems length by controller : ${queueItems.length}`);
273     // Obtain the playlist name of the session.
274     let queueTitle = await controller.getAVQueueTitle();
275     console.info(`get queueTitle by controller : ${queueTitle}`);
276     // Obtain the custom media packet of the session.
277     let extras = await controller.getExtras();
278     console.info(`get custom media packets by controller : ${JSON.stringify(extras)}`);
279     // Obtain the ability information provided by the application corresponding to the session.
280     let agent = await controller.getLaunchAbility();
281     console.info(`get want agent info by controller : ${JSON.stringify(agent)}`);
282     // Obtain the current playback position of the session.
283     let currentTime = controller.getRealPlaybackPositionSync();
284     console.info(`get current playback time by controller : ${currentTime}`);
285     // Obtain valid commands supported by the session.
286     let validCommands = await controller.getValidCommands();
287     console.info(`get valid commands by controller : ${JSON.stringify(validCommands)}`);
288   }
289   ```
290
2915. Control the playback behavior, for example, sending a command to operate (play/pause/previous/next) the item being played in Media Controller.
292
293   After listening for the playback control command event, the audio and video application serving as the provider needs to implement the corresponding operation.
294
295   ```ts
296   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
297   import { BusinessError } from '@kit.BasicServicesKit';
298
299   async function  sendCommandToSessionByController() {
300     // It is assumed that an AVSessionController object corresponding to the session already exists. For details about how to create an AVSessionController object, see the code snippet above.
301     let controller = await AVSessionManager.createController("");
302     // Obtain valid commands supported by the session.
303     let validCommandTypeArray = await controller.getValidCommands();
304     console.info(`get validCommandArray by controller : length : ${validCommandTypeArray.length}`);
305     // Deliver the 'play' command.
306     // If the 'play' command is valid, deliver it. Normal sessions should provide and implement the playback.
307     if (validCommandTypeArray.indexOf('play') >= 0) {
308       let avCommand: AVSessionManager.AVControlCommand = {command:'play'};
309       controller.sendControlCommand(avCommand);
310     }
311     // Deliver the 'pause' command.
312     if (validCommandTypeArray.indexOf('pause') >= 0) {
313       let avCommand: AVSessionManager.AVControlCommand = {command:'pause'};
314       controller.sendControlCommand(avCommand);
315     }
316     // Deliver the 'playPrevious' command.
317     if (validCommandTypeArray.indexOf('playPrevious') >= 0) {
318       let avCommand: AVSessionManager.AVControlCommand = {command:'playPrevious'};
319       controller.sendControlCommand(avCommand);
320     }
321     // Deliver the 'playNext' command.
322     if (validCommandTypeArray.indexOf('playNext') >= 0) {
323       let avCommand: AVSessionManager.AVControlCommand = {command:'playNext'};
324       controller.sendControlCommand(avCommand);
325     }
326     // Deliver a custom playback control command.
327     let commandName = 'custom command';
328     await controller.sendCommonCommand(commandName, {command : 'This is my custom command'}).then(() => {
329       console.info(`SendCommonCommand successfully`);
330     }).catch((err: BusinessError) => {
331       console.error(`Failed to send common command. Code: ${err.code}, message: ${err.message}`);
332     })
333     // Set the ID of an item in the specified playlist for the session to play.
334     let queueItemId = 0;
335     await controller.skipToQueueItem(queueItemId).then(() => {
336       console.info(`SkipToQueueItem successfully`);
337     }).catch((err: BusinessError) => {
338       console.error(`Failed to skip to queue item. Code: ${err.code}, message: ${err.message}`);
339     });
340   }
341   ```
342
3436. When the audio and video application exits, cancel the listener and release the resources.
344
345   ```ts
346   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
347   import { BusinessError } from '@kit.BasicServicesKit';
348
349   async function destroyController() {
350     // It is assumed that an AVSessionController object corresponding to the session already exists. For details about how to create an AVSessionController object, see the code snippet above.
351     let controller = await AVSessionManager.createController("");
352
353     // Destroy the AVSessionController object. After being destroyed, it is no longer available.
354     controller.destroy((err: BusinessError) => {
355       if (err) {
356         console.error(`Failed to destroy controller. Code: ${err.code}, message: ${err.message}`);
357       } else {
358         console.info(`Destroy controller SUCCESS`);
359       }
360     });
361   }
362   ```
363