• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 媒体会话控制方(仅对系统应用开放)
2<!--Kit: AVSession Kit-->
3<!--Subsystem: Multimedia-->
4<!--Owner: @ccfriend; @liao_qian-->
5<!--Designer: @ccfriend-->
6<!--Tester: @chenmingxi1_huawei-->
7<!--Adviser: @zengyawen-->
8
9OpenHarmony系统预置的播控中心,作为媒体会话控制方与音视频应用进行交互,包括获取媒体信息进行展示以及下发播控命令等。
10
11系统应用开发者也可以根据需要,按照本章节的内容自行开发一款新的系统应用(例如新开发一款播控中心或语音助手),作为媒体会话控制方的角色,与系统中的音视频应用进行交互。
12
13## 基本概念
14
15- 媒体会话描述符(AVSessionDescriptor):描述媒体会话的相关信息,包含标识媒体会话的ID(sessionId),媒体会话的类型type(音频Audio/视频Video),媒体会话自定义名称(sessionTag),媒体会话所属应用的信息(elementName)、是否为置顶会话(isTopSession)等。
16
17- 置顶会话(TopSession):系统中优先级最高的媒体会话,例如当前处于正在播放状态的会话。一般来说,如果想与媒体会话通信,需要获取会话对应的控制器,而媒体会话控制方可以在不用获取对应控制器的情况下,直接与置顶会话通信,例如直接向置顶会话发送播控命令和按键事件。
18
19## 接口说明
20
21媒体会话控制方使用的关键接口包括两类:
22
231. 直接通过import得到的AVSessionManager来调用,例如接口`AVSessionManager.createController(sessionId)`。
242. 通过AVSessionController对象来调用,例如接口`controller.getAVPlaybackState()`。
25
26异步的JavaScript接口返回值有两种返回形式:callback和promise,本说明仅提供callback形式接口,promise和callback只是返回值方式不一样,功能相同。
27
28更多API说明请参见[API文档](../../reference/apis-avsession-kit/arkts-apis-avsession.md)。
29
30### 直接通过AVSessionManager调用的接口
31
32| 接口名 | 说明 |
33| -------- | -------- |
34| getAllSessionDescriptors(callback: AsyncCallback&lt;Array&lt;Readonly&lt;AVSessionDescriptor&gt;&gt;&gt;): void | 获取系统中所有媒体会话的描述符。 |
35| createController(sessionId: string, callback: AsyncCallback&lt;AVSessionController&gt;): void | 创建媒体会话控制器。 |
36| sendSystemAVKeyEvent(event: KeyEvent, callback: AsyncCallback&lt;void&gt;): void | 向置顶会话发送按键命令。 |
37| sendSystemControlCommand(command: AVControlCommand, callback: AsyncCallback&lt;void&gt;): void | 向置顶会话发送播控命令。 |
38| getHistoricalSessionDescriptors(maxSize: number, callback: AsyncCallback\<Array\<Readonly\<AVSessionDescriptor>>>): void<sup>10+<sup> | 获取历史会话的描述符。 |
39
40### 通过AVSessionController对象调用的接口
41
42| 接口名 | 说明 |
43| -------- | -------- |
44| getAVPlaybackState(callback: AsyncCallback&lt;AVPlaybackState&gt;): void<sup>10+<sup> | 获取当前会话播放状态相关信息。 |
45| getAVMetadata(callback: AsyncCallback&lt;AVMetadata&gt;): void<sup>10+<sup> | 获取会话元数据。 |
46| getOutputDevice(callback: AsyncCallback&lt;OutputDeviceInfo&gt;): void<sup>10+<sup> | 获取播放设备信息。 |
47| sendAVKeyEvent(event: KeyEvent, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | 发送按键事件到会话。|
48| getLaunchAbility(callback: AsyncCallback&lt;WantAgent&gt;): void<sup>10+<sup> | 获取应用在会话中保存的WantAgent对象。 |
49| isActive(callback: AsyncCallback&lt;boolean&gt;): void<sup>10+<sup> | 判断会话是否被激活。 |
50| destroy(callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | 销毁当前控制器,销毁后当前控制器不再可用。 |
51| getValidCommands(callback: AsyncCallback&lt;Array&lt;AVControlCommandType&gt;&gt;): void<sup>10+<sup> | 获取会话支持的有效命令。 |
52| sendControlCommand(command: AVControlCommand, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | 通过会话控制器发送命令到其对应的会话。 |
53| sendCommonCommand(command: string, args: {[key: string]: Object}, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | 通过会话控制器发送自定义命令到其对应的会话。 |
54| getAVQueueItems(callback: AsyncCallback&lt;Array&lt;AVQueueItem&gt;&gt;): void<sup>10+<sup> | 获取当前播放列表相关信息。 |
55| getAVQueueTitle(callback: AsyncCallback&lt;string&gt;): void<sup>10+<sup> | 获取当前播放列表的名称。 |
56| skipToQueueItem(itemId: number, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | 设置指定播放列表单项的ID,发送给session端处理,session端可以选择对这个单项歌曲进行播放。 |
57| getExtras(callback: AsyncCallback&lt;{[key: string]: Object}&gt;): void<sup>10+<sup> | 获取媒体提供方设置的自定义媒体数据包。 |
58| getOutputDeviceSync(): OutputDeviceInfo<sup>10+<sup> | 使用同步方法获取当前输出设备信息。 |
59| getAVPlaybackStateSync(): AVPlaybackState<sup>10+<sup> | 使用同步方法获取当前会话播放状态相关信息。 |
60| getAVMetadataSync(): AVMetadata<sup>10+<sup> | 使用同步方法获取当前会话元数据信息。 |
61| getAVQueueTitleSync(): string<sup>10+<sup> | 使用同步方法当前播放列表的名称。 |
62| getAVQueueItemsSync(): Array&lt;AVQueueItem&gt;<sup>10+<sup> | 使用同步方法获取当前播放列表相关信息。 |
63| isActiveSync(): boolean<sup>10+<sup> | 使用同步方法判断会话是否被激活。 |
64| getValidCommandsSync(): Array&lt;AVControlCommandType&gt;<sup>10+<sup> | 使用同步方法获取会话支持的有效命令。 |
65
66## 开发步骤
67
68系统应用作为媒体会话控制方接入媒体会话的基本步骤如下所示:
69
701. 通过AVSessionManager获取媒体会话描述符AVSessionDescriptor,创建媒体会话控制器AVSessionController。
71   媒体会话控制方可以获取当前系统中所有的AVSessionDescriptor,并创建每个会话对应的AVSessionController,从而对系统中的音视频应用进行统一的播放控制。
72
73   ```ts
74   //导入AVSessionManager模块。
75   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
76   import { BusinessError } from '@kit.BasicServicesKit';
77
78   // 全局变量定义。
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   // 获取会话描述符,创建控制器。
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   // 获取历史会话的描述符。
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. 监听AVSession会话状态及AVSession服务状态事件。
111
112   AVSession会话状态事件包括:
113
114   - sessionCreate:媒体会话创建事件。
115   - sessionDestroy:媒体会话销毁事件。
116   - topSessionChange:置顶会话发生变化事件。
117
118   AVSession服务状态事件指sessionServiceDie,在AVSession服务异常时产生该事件。
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   // 注册会话创建监听,创建控制器。
126   AVSessionManager.on('sessionCreate', (session) => {
127     // 新增会话,需要创建控制器。
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   // 注册系统会话销毁监听。
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   // 注册系统最高优先级会话变更监听。
146   AVSessionManager.on('topSessionChange', (session) => {
147     let index = g_controller.findIndex((controller) => {
148       return controller.sessionId === session.sessionId;
149     });
150     // 将该会话显示排到第一个。
151     if (index !== 0) {
152       g_controller.sort((a, b) => {
153         return a.sessionId === session.sessionId ? -1 : 0;
154       });
155     }
156   });
157   // 注册服务异常监听。
158   AVSessionManager.on('sessionServiceDie', () => {
159     // 服务端异常,应用清理资源。
160     console.info(`服务端异常`);
161   })
162   ```
163
1643. 监听媒体信息变化及会话其他事件。
165
166   AVSession媒体信息变化事件主要包括:
167
168   - metadataChange:媒体会话元数据变化事件。
169   - playbackStateChange:媒体播放状态变化事件。
170   - activeStateChange:媒体会话激活状态变化事件。
171   - validCommandChange:媒体会话支持的有效命令变化事件。
172   - outputDeviceChange:播放设备变化事件。
173   - sessionDestroy:媒体会话销毁事件。
174   - sessionEvent:媒体会话自定义事件变化事件。
175   - extrasChange:媒体会话自定义数据包变化事件。
176   - queueItemsChange:媒体会话自定义播放列表变化事件。
177   - queueTitleChange:媒体会话自定义播放列表的名称变化事件。
178
179   媒体会话控制方可以根据实际需要监听对应的事件。
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   if (g_controller && g_controller.length > 0 && g_controller[0]) {
187    let controller = g_controller[0];
188    let g_validCmd:Set<AVSessionManager.AVControlCommandType>;
189    let g_centerSupportCmd:Set<AVSessionManager.AVControlCommandType> = new Set(['play', 'pause', 'playNext', 'playPrevious', 'fastForward', 'rewind', 'seek','setSpeed', 'setLoopMode', 'toggleFavorite']);
190    // 注册会话激活状态变更监听。
191    controller.on('activeStateChange', (isActive) => {
192      if (isActive) {
193        console.info(`控制器卡片按键高亮`);
194      } else {
195        console.info(`控制器卡片按键变更为无效`);
196      }
197    });
198    // 注册会话销毁监听。
199    controller.on('sessionDestroy', () => {
200      console.info(`on sessionDestroy : SUCCESS `);
201      controller.destroy().then(() => {
202        console.info(`destroy : SUCCESS`);
203      }).catch((err: BusinessError) => {
204        console.error(`Failed to destroy session. Code: ${err.code}, message: ${err.message}`);
205      });
206    });
207
208    // 注册元数据更新监听。
209    controller.on('metadataChange', ['assetId', 'title', 'description'], (metadata: AVSessionManager.AVMetadata) => {
210      console.info(`on metadataChange assetId : ${metadata.assetId}`);
211    });
212    // 注册播放状态更新监听。
213    controller.on('playbackStateChange', ['state', 'speed', 'loopMode'], (playbackState: AVSessionManager.AVPlaybackState) => {
214      console.info(`on playbackStateChange state : ${playbackState.state}`);
215    });
216    // 注册会话支持的命令变更监听。
217    controller.on('validCommandChange', (cmds) => {
218      console.info(`validCommandChange : SUCCESS : size : ${cmds.length}`);
219      console.info(`validCommandChange : SUCCESS : cmds : ${Array.from(cmds)}`);
220      g_validCmd.clear();
221      let centerSupportCmd = Array.from(g_centerSupportCmd.values())
222      for (let c of centerSupportCmd) {
223        if (cmds.indexOf(c) != -1) {
224          g_validCmd.add(c);
225        }
226      }
227    });
228    // 注册输出设备变更监听。
229    controller.on('outputDeviceChange', (state, device) => {
230      console.info(`outputDeviceChange device are : ${JSON.stringify(device)}`);
231    });
232    // 注册会话自定义事件变更监听。
233    controller.on('sessionEvent', (eventName, eventArgs) => {
234      console.info(`Received new session event, event name is ${eventName}, args are ${JSON.stringify(eventArgs)}`);
235    });
236    // 注册会话自定义媒体数据包变更监听。
237    controller.on('extrasChange', (extras) => {
238      console.info(`Received custom media packet, packet data is ${JSON.stringify(extras)}`);
239    });
240    // 注册会话自定义播放列表变更监听。
241    controller.on('queueItemsChange', (items) => {
242      console.info(`Caught queue items change, items length is ${items.length}`);
243    });
244    // 注册会话自定义播放标题变更监听。
245    controller.on('queueTitleChange', (title) => {
246      console.info(`Caught queue title change, title is ${title}`);
247    });
248   }
249   ```
250
2514. 获取媒体会话提供方传递的媒体信息,可以用于界面展示,例如在播控中心展示当前播放的曲目及对应的播放状态。
252
253   ```ts
254   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
255   async function getInfoFromSessionByController() {
256     // 假设已经有了一个对应session的controller,如何创建controller可以参考之前的案例。
257     let controller = await AVSessionManager.createController("");
258     // 获取sessionId。
259     let sessionId = controller.sessionId;
260     console.info(`get sessionId by controller : isActive : ${sessionId}`);
261     // 获取session激活状态。
262     let isActive = await controller.isActive();
263     console.info(`get activeState by controller : ${isActive}`);
264     // 获取session的媒体信息。
265     let metadata = await controller.getAVMetadata();
266     console.info(`get media title by controller : ${metadata.title}`);
267     console.info(`get media artist by controller : ${metadata.artist}`);
268     // 获取session的播放信息。
269     let avPlaybackState = await controller.getAVPlaybackState();
270     console.info(`get playbackState by controller : ${avPlaybackState.state}`);
271     console.info(`get favoriteState by controller : ${avPlaybackState.isFavorite}`);
272     // 获取session的播放列表信息。
273     let queueItems = await controller.getAVQueueItems();
274     console.info(`get queueItems length by controller : ${queueItems.length}`);
275     // 获取session的播放标题信息。
276     let queueTitle = await controller.getAVQueueTitle();
277     console.info(`get queueTitle by controller : ${queueTitle}`);
278     // 获取session的自定义媒体数据包。
279     let extras = await controller.getExtras();
280     console.info(`get custom media packets by controller : ${extras ? JSON.stringify(extras) : ''}`);
281     // 获取session对应应用提供的ability信息。
282     let agent = await controller.getLaunchAbility();
283     console.info(`get want agent info by controller : ${agent ? JSON.stringify(agent) : ''}`);
284     // 获取session的当前播放位置信息。
285     let currentTime = controller.getRealPlaybackPositionSync();
286     console.info(`get current playback time by controller : ${currentTime}`);
287     // 获取session支持的有效命令。
288     let validCommands = await controller.getValidCommands();
289     console.info(`get valid commands by controller : ${validCommands ? JSON.stringify(validCommands) : '[]'}`);
290   }
291   ```
292
2935. 控制媒体会话行为,例如发送用户在播控中心对当前曲目的操作(播放/暂停/上一首/下一首等)命令。
294
295   作为媒体会话提供方的音视频应用在监听到相关的播控命令事件后,在相应的逻辑中可以完成对应的操作动作。
296
297   ```ts
298   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
299   import { BusinessError } from '@kit.BasicServicesKit';
300
301   async function  sendCommandToSessionByController() {
302     // 假设我们已经有了一个对应session的controller,如何创建controller可以参考之前的案例。
303     let controller = await AVSessionManager.createController("");
304     // 获取这个session支持的命令种类。
305     let validCommandTypeArray = await controller.getValidCommands();
306     console.info(`get validCommandArray by controller : length : ${validCommandTypeArray.length}`);
307     // 下发播放命令。
308     // 如果可用命令包含播放,则下发播放命令,正常session都应该提供并实现播放功能。
309     if (validCommandTypeArray.indexOf('play') >= 0) {
310       let avCommand: AVSessionManager.AVControlCommand = {command:'play'};
311       controller.sendControlCommand(avCommand);
312     }
313     // 下发暂停命令。
314     if (validCommandTypeArray.indexOf('pause') >= 0) {
315       let avCommand: AVSessionManager.AVControlCommand = {command:'pause'};
316       controller.sendControlCommand(avCommand);
317     }
318     // 下发上一首命令。
319     if (validCommandTypeArray.indexOf('playPrevious') >= 0) {
320       let avCommand: AVSessionManager.AVControlCommand = {command:'playPrevious'};
321       controller.sendControlCommand(avCommand);
322     }
323     // 下发下一首命令。
324     if (validCommandTypeArray.indexOf('playNext') >= 0) {
325       let avCommand: AVSessionManager.AVControlCommand = {command:'playNext'};
326       controller.sendControlCommand(avCommand);
327     }
328     // 下发自定义控制命令。
329     let commandName = 'custom command';
330     await controller.sendCommonCommand(commandName, {command : 'This is my custom command'}).then(() => {
331       console.info(`SendCommonCommand successfully`);
332     }).catch((err: BusinessError) => {
333       console.error(`Failed to send common command. Code: ${err.code}, message: ${err.message}`);
334     })
335     // 设置指定播放列表单项的ID,供session选择播放。
336     let queueItemId = 0;
337     await controller.skipToQueueItem(queueItemId).then(() => {
338       console.info(`SkipToQueueItem successfully`);
339     }).catch((err: BusinessError) => {
340       console.error(`Failed to skip to queue item. Code: ${err.code}, message: ${err.message}`);
341     });
342   }
343   ```
344
3456. 在媒体会话控制方应用退出时及时取消事件监听,并释放资源。
346
347   ```ts
348   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
349   import { BusinessError } from '@kit.BasicServicesKit';
350
351   async function destroyController() {
352     // 假设我们已经有了一个对应session的controller,如何创建controller可以参考之前的案例。
353     let controller = await AVSessionManager.createController("");
354
355     // 销毁当前的controller,销毁后这个controller将不在可用。
356     controller.destroy((err: BusinessError) => {
357       if (err) {
358         console.error(`Failed to destroy controller. Code: ${err.code}, message: ${err.message}`);
359       } else {
360         console.info(`Destroy controller SUCCESS`);
361       }
362     });
363   }
364   ```
365
366## 相关实例
367
368针对媒体会话控制方开发,有以下相关实例可供参考:
369
370- [媒体会话——控制方(仅对系统应用开放)(ArkTS)(Full SDK)(API10)](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/SystemFeature/Media/AVSession/MediaController)