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