• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 媒体会话提供方
2
3音视频应用在实现音视频功能的同时,需要作为媒体会话提供方接入媒体会话,在媒体会话控制方(例如播控中心)中展示媒体相关信息,及响应媒体会话控制方下发的播控命令。
4
5## 基本概念
6
7- 媒体会话元数据(AVMetadata): 用于描述媒体数据相关属性,包含标识当前媒体的ID(assetId),上一首媒体的ID(previousAssetId),下一首媒体的ID(nextAssetId),标题(title),专辑作者(author),专辑名称(album),词作者(writer),媒体时长(duration)等属性。
8
9- 媒体播放状态(AVPlaybackState):用于描述媒体播放状态的相关属性,包含当前媒体的播放状态(state)、播放位置(position)、播放倍速(speed)、缓冲时间(bufferedTime)、循环模式(loopMode)、是否收藏(isFavorite)、正在播放的媒体Id(activeItemId)、自定义媒体数据(extras)等属性。
10
11## 接口说明
12
13媒体会话提供方使用的关键接口如下表所示。接口返回值有两种返回形式:callback和promise,下表中为callback形式接口,promise和callback只是返回值方式不一样,功能相同。
14
15更多API说明请参见[API文档](../../reference/apis-avsession-kit/js-apis-avsession.md)。
16
17| 接口名 | 说明 |
18| -------- | -------- |
19| createAVSession(context: Context, tag: string, type: AVSessionType, callback: AsyncCallback&lt;AVSession&gt;): void<sup>10+<sup> | 创建媒体会话。<br/>一个UIAbility只能存在一个媒体会话,重复创建会失败。 |
20| setAVMetadata(data: AVMetadata, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | 设置媒体会话元数据。 |
21| setAVPlaybackState(state: AVPlaybackState, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | 设置媒体会话播放状态。 |
22| setLaunchAbility(ability: WantAgent, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | 设置启动UIAbility。 |
23| getController(callback: AsyncCallback&lt;AVSessionController&gt;): void<sup>10+<sup> | 获取当前会话自身控制器。 |
24| getOutputDevice(callback: AsyncCallback&lt;OutputDeviceInfo&gt;): void<sup>10+<sup> | 获取播放设备相关信息。 |
25| activate(callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | 激活媒体会话。 |
26| deactivate(callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | 禁用当前会话。 |
27| destroy(callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | 销毁媒体会话。 |
28| setAVQueueItems(items: Array&lt;AVQueueItem&gt;, callback: AsyncCallback&lt;void&gt;): void <sup>10+<sup> | 设置媒体播放列表。 |
29| setAVQueueTitle(title: string, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | 设置媒体播放列表名称。 |
30| dispatchSessionEvent(event: string, args: {[key: string]: Object}, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | 设置会话内自定义事件。 |
31| setExtras(extras: {[key: string]: Object}, callback: AsyncCallback&lt;void&gt;): void<sup>10+<sup> | 设置键值对形式的自定义媒体数据包。|
32| getOutputDeviceSync(): OutputDeviceInfo<sup>10+<sup> | 使用同步方法获取当前输出设备信息。 |
33
34## 开发步骤
35
36音视频应用作为媒体会话提供方接入媒体会话的基本步骤如下所示:
37
381. 通过AVSessionManager的方法创建并激活媒体会话。
39
40  ```ts
41  import { avSession as AVSessionManager } from '@kit.AVSessionKit';
42  @Entry
43  @Component
44  struct Index {
45    @State message: string = 'hello world';
46
47    build() {
48      Column() {
49          Text(this.message)
50            .onClick(()=>{
51              // 开始创建并激活媒体会话。
52              // 创建session。
53              let context = this.getUIContext().getHostContext() as Context;
54              async function createSession() {
55                  let type: AVSessionManager.AVSessionType = 'audio';
56                  let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
57                  await session.activate();
58                  console.info(`session create done : sessionId : ${session.sessionId}`);
59              }
60            })
61        }
62      .width('100%')
63      .height('100%')
64    }
65  }
66  ```
67
682. 跟随媒体信息的变化,及时设置媒体会话信息。需要设置的媒体会话信息主要包括:
69   - 媒体会话元数据AVMetadata。
70   - 媒体播放状态AVPlaybackState。
71
72   音视频应用设置的媒体会话信息,会被媒体会话控制方通过AVSessionController相关方法获取后进行显示或处理。
73
74  ```ts
75  import { avSession as AVSessionManager } from '@kit.AVSessionKit';
76  import { BusinessError } from '@kit.BasicServicesKit';
77  @Entry
78  @Component
79  struct Index {
80    @State message: string = 'hello world';
81
82    build() {
83      Column() {
84          Text(this.message)
85            .onClick(()=>{
86              let context = this.getUIContext().getHostContext() as Context;
87              async function setSessionInfo() {
88                  // 假设已经创建了一个session,如何创建session可以参考之前的案例。
89                  let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', 'audio');
90                  // 播放器逻辑··· 引发媒体信息与播放状态的变更。
91                  // 设置必要的媒体信息
92                  let metadata: AVSessionManager.AVMetadata = {
93                  assetId: '0', // 由应用指定,用于标识应用媒体库里的媒体。
94                  title: 'TITLE',
95                  mediaImage: 'IMAGE',
96                  artist: 'ARTIST'
97                  };
98                  session.setAVMetadata(metadata).then(() => {
99                  console.info(`SetAVMetadata successfully`);
100                  }).catch((err: BusinessError) => {
101                  console.error(`Failed to set AVMetadata. Code: ${err.code}, message: ${err.message}`);
102                  });
103                  // 简单设置一个播放状态 - 暂停 未收藏。
104                  let playbackState: AVSessionManager.AVPlaybackState = {
105                  state:AVSessionManager.PlaybackState.PLAYBACK_STATE_PAUSE,
106                  isFavorite:false
107                  };
108                  session.setAVPlaybackState(playbackState, (err) => {
109                  if (err) {
110                      console.error(`Failed to set AVPlaybackState. Code: ${err.code}, message: ${err.message}`);
111                  } else {
112                      console.info(`SetAVPlaybackState successfully`);
113                  }
114                  });
115                  // 设置一个播放列表。
116                  let queueItemDescription_1: AVSessionManager.AVMediaDescription = {
117                  assetId: '001',
118                  title: 'music_name',
119                  subtitle: 'music_sub_name',
120                  description: 'music_description',
121                  mediaImage: "PIXELMAP_OBJECT",
122                  extras: {'extras':'any'}
123                  };
124                  let queueItem_1: AVSessionManager.AVQueueItem = {
125                  itemId: 1,
126                  description: queueItemDescription_1
127                  };
128                  let queueItemDescription_2: AVSessionManager.AVMediaDescription = {
129                  assetId: '002',
130                  title: 'music_name',
131                  subtitle: 'music_sub_name',
132                  description: 'music_description',
133                  mediaImage: "PIXELMAP_OBJECT",
134                  extras: {'extras':'any'}
135                  };
136                  let queueItem_2: AVSessionManager.AVQueueItem = {
137                  itemId: 2,
138                  description: queueItemDescription_2
139                  };
140                  let queueItemsArray = [queueItem_1, queueItem_2];
141                  session.setAVQueueItems(queueItemsArray).then(() => {
142                  console.info(`SetAVQueueItems successfully`);
143                  }).catch((err: BusinessError) => {
144                  console.error(`Failed to set AVQueueItem, error code: ${err.code}, error message: ${err.message}`);
145                  });
146                  // 设置媒体播放列表名称。
147                  let queueTitle = 'QUEUE_TITLE';
148                  session.setAVQueueTitle(queueTitle).then(() => {
149                  console.info(`SetAVQueueTitle successfully`);
150                  }).catch((err: BusinessError) => {
151                  console.info(`Failed to set AVQueueTitle, error code: ${err.code}, error message: ${err.message}`);
152                  });
153              }
154            })
155        }
156      .width('100%')
157      .height('100%')
158    }
159  }
160  ```
161
1623. 设置用于被媒体会话控制方拉起的UIAbility。当用户操作媒体会话控制方的界面时,例如点击播控中心的卡片,可以拉起此处配置的UIAbility。
163   设置UIAbility时通过WantAgent接口实现,更多关于WantAgent的信息请参考[WantAgent](../../reference/apis-ability-kit/js-apis-app-ability-wantAgent.md)。
164
165   ```ts
166   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
167   import { wantAgent } from '@kit.AbilityKit';
168   @Entry
169   @Component
170   struct Index {
171      @State message: string = 'hello world';
172
173      build() {
174        Column() {
175            Text(this.message)
176              .onClick(()=>{
177                let context = this.getUIContext().getHostContext() as Context;
178                async function getWantAgent() {
179                    let type: AVSessionManager.AVSessionType = 'audio';
180                    // 假设已经创建了一个session,如何创建session可以参考之前的案例。
181                    let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
182                    let wantAgentInfo: wantAgent.WantAgentInfo = {
183                    wants: [
184                        {
185                        bundleName: 'com.example.musicdemo',
186                        abilityName: 'MainAbility'
187                        }
188                    ],
189                    // OperationType.START_ABILITIES
190                    operationType: 2,
191                    requestCode: 0,
192                    wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
193                    }
194                    wantAgent.getWantAgent(wantAgentInfo).then((agent) => {
195                    session.setLaunchAbility(agent);
196                    })
197                }
198              })
199          }
200        .width('100%')
201        .height('100%')
202      }
203   }
204   ```
205
2064. 设置一个即时的自定义会话事件,以供媒体控制方接收到事件后进行相应的操作。
207
208   > **说明:**<br>
209   > 通过dispatchSessionEvent方法设置的数据不会保存在会话对象或AVSession服务中。
210
211  ```ts
212  import { avSession as AVSessionManager } from '@kit.AVSessionKit';
213  import { BusinessError } from '@kit.BasicServicesKit';
214  @Entry
215  @Component
216  struct Index {
217    @State message: string = 'hello world';
218
219    build() {
220      Column() {
221          Text(this.message)
222            .onClick(()=>{
223              let context = this.getUIContext().getHostContext() as Context;
224              async function dispatchSessionEvent() {
225                  // 假设已经创建了一个session,如何创建session可以参考之前的案例。
226                  let type: AVSessionManager.AVSessionType = 'audio';
227                  let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
228                  let eventName = 'dynamic_lyric';
229                  await session.dispatchSessionEvent(eventName, {lyric : 'This is my lyric'}).then(() => {
230                  console.info(`Dispatch session event successfully`);
231                  }).catch((err: BusinessError) => {
232                  console.error(`Failed to dispatch session event. Code: ${err.code}, message: ${err.message}`);
233                  })
234              }
235            })
236        }
237      .width('100%')
238      .height('100%')
239    }
240  }
241  ```
242
2435. 设置与当前会话相关的自定义媒体数据包,以供媒体控制方接收到事件后进行相应的操作。
244
245   > **说明:**<br>
246   > 通过setExtras方法设置的数据包会被存储在AVSession服务中,数据的生命周期与会话一致;会话对应的Controller可以使用getExtras来获取该数据。
247
248   ```ts
249   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
250   import { BusinessError } from '@kit.BasicServicesKit';
251   @Entry
252   @Component
253   struct Index {
254     @State message: string = 'hello world';
255
256    build() {
257      Column() {
258          Text(this.message)
259            .onClick(()=>{
260              let context = this.getUIContext().getHostContext() as Context;
261              async function setExtras() {
262                  // 假设已经创建了一个session,如何创建session可以参考之前的案例。
263                  let type: AVSessionManager.AVSessionType = 'audio';
264                  let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
265                  await session.setExtras({extra : 'This is my custom meida packet'}).then(() => {
266                  console.info(`Set extras successfully`);
267                  }).catch((err: BusinessError) => {
268                  console.error(`Failed to set extras. Code: ${err.code}, message: ${err.message}`);
269                  })
270              }
271            })
272        }
273      .width('100%')
274      .height('100%')
275     }
276   }
277   ```
278
2796. 注册播控命令事件监听,便于响应用户通过媒体会话控制方,例如播控中心,下发的播控命令。
280
281   在Session侧注册的监听分为`固定播控命令`和`高级播控事件`两种。
282
283   6.1 固定控制命令的监听
284
285   > **说明:**
286   >
287   > 媒体会话提供方在注册相关固定播控命令事件监听时,监听的事件会在媒体会话控制方的getValidCommands()方法中体现,即媒体会话控制方会认为对应的方法有效,进而根据需要触发相应暂不使用时的事件。为了保证媒体会话控制方下发的播控命令可以被正常执行,媒体会话提供方请勿进行无逻辑的空实现监听。
288
289   Session侧的固定播控命令主要包括播放、暂停、上一首、下一首等基础操作命令,详细介绍请参见[AVControlCommand](../../reference/apis-avsession-kit/js-apis-avsession.md#avcontrolcommand10)。
290
291   ```ts
292   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
293
294   @Entry
295   @Component
296   struct Index {
297     @State message: string = 'hello world';
298
299     build() {
300       Column() {
301           Text(this.message)
302             .onClick(()=>{
303               let context = this.getUIContext().getHostContext() as Context;
304               async function setListenerForMesFromController() {
305                   // 假设已经创建了一个session,如何创建session可以参考之前的案例。
306                   let type: AVSessionManager.AVSessionType = 'audio';
307                   let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
308                   // 一般在监听器中会对播放器做相应逻辑处理。
309                   // 不要忘记处理完后需要通过set接口同步播放相关信息,参考上面的用例。
310                   session.on('play', () => {
311                   console.info(`on play , do play task`);
312                   // 如暂不支持该指令,请勿注册;或在注册后但暂不使用时,通过session.off('play')取消监听。
313                   // 处理完毕后,请使用SetAVPlayState上报播放状态。
314                   });
315                   session.on('pause', () => {
316                   console.info(`on pause , do pause task`);
317                       // 如暂不支持该指令,请勿注册;或在注册后但暂不使用时,通过session.off('pause')取消监听。
318                       // 处理完毕后,请使用SetAVPlayState上报播放状态。
319                   });
320                   session.on('stop', () => {
321                   console.info(`on stop , do stop task`);
322                       // 如暂不支持该指令,请勿注册;或在注册后但暂不使用时,通过session.off('stop')取消监听。
323                       // 处理完毕后,请使用SetAVPlayState上报播放状态。
324                   });
325                   session.on('playNext', () => {
326                   console.info(`on playNext , do playNext task`);
327                       // 如暂不支持该指令,请勿注册;或在注册后但暂不使用时,通过session.off('playNext')取消监听。
328                       // 处理完毕后,请使用SetAVPlayState上报播放状态,使用SetAVMetadata上报媒体信息。
329                   });
330                   session.on('playPrevious', () => {
331                   console.info(`on playPrevious , do playPrevious task`);
332                       // 如暂不支持该指令,请勿注册;或在注册后但暂不使用时,通过session.off('playPrevious')取消监听。
333                       // 处理完毕后,请使用SetAVPlayState上报播放状态,使用SetAVMetadata上报媒体信息。
334                   });
335                   session.on('fastForward', () => {
336                   console.info(`on fastForward , do fastForward task`);
337                       // 如暂不支持该指令,请勿注册;或在注册后但暂不使用时,通过session.off('fastForward')取消监听。
338                       // 处理完毕后,请使用SetAVPlayState上报播放状态和播放position。
339                   });
340                   session.on('rewind', () => {
341                   console.info(`on rewind , do rewind task`);
342                       // 如暂不支持该指令,请勿注册;或在注册后但暂不使用时,通过session.off('rewind')取消监听。
343                       // 处理完毕后,请使用SetAVPlayState上报播放状态和播放position。
344                   });
345                   session.on('seek', (time) => {
346                   console.info(`on seek , the seek time is ${time}`);
347                       // 如暂不支持该指令,请勿注册;或在注册后但暂不使用时,通过session.off('seek')取消监听。
348                       // 处理完毕后,请使用SetAVPlayState上报播放状态和播放position。
349                   });
350                   session.on('setSpeed', (speed) => {
351                   console.info(`on setSpeed , the speed is ${speed}`);
352                   // do some tasks ···
353                   });
354                   session.on('setLoopMode', (mode) => {
355                   console.info(`on setLoopMode , the loop mode is ${mode}`);
356                       // 如暂不支持该指令,请勿注册;或在注册后但暂不使用时,通过session.off('setLoopMode')取消监听。
357                       // 应用自定下一个模式,处理完毕后,请使用SetAVPlayState上报切换后的LoopMode。
358                   });
359                   session.on('toggleFavorite', (assetId) => {
360                   console.info(`on toggleFavorite , the target asset Id is ${assetId}`);
361                       // 如暂不支持该指令,请勿注册;或在注册后但暂不使用时,通过session.off('toggleFavorite')取消监听。
362                       // 处理完毕后,请使用SetAVPlayState上报收藏结果isFavorite。
363                   });
364               }
365             })
366         }
367       .width('100%')
368       .height('100%')
369     }
370   }
371   ```
372
373   6.2 高级播控事件的监听
374
375   Session侧的可以注册的高级播控事件主要包括:
376
377   - skipToQueueItem: 播放列表其中某项被选中的事件。
378   - handleKeyEvent: 按键事件。
379   - outputDeviceChange: 播放设备变化的事件。
380   - commonCommand: 自定义控制命令变化的事件。
381
382   ```ts
383   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
384   @Entry
385   @Component
386   struct Index {
387     @State message: string = 'hello world';
388
389     build() {
390       Column() {
391           Text(this.message)
392             .onClick(()=>{
393               let context = this.getUIContext().getHostContext() as Context;
394               async function setListenerForMesFromController() {
395                   // 假设已经创建了一个session,如何创建session可以参考之前的案例。
396                   let type: AVSessionManager.AVSessionType = 'audio';
397                   let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
398                   // 一般在监听器中会对播放器做相应逻辑处理。
399                   // 不要忘记处理完后需要通过set接口同步播放相关信息,参考上面的用例。
400                   session.on('skipToQueueItem', (itemId) => {
401                   console.info(`on skipToQueueItem , do skip task`);
402                   // do some tasks ···
403                   });
404                   session.on('handleKeyEvent', (event) => {
405                   console.info(`on handleKeyEvent , the event is ${JSON.stringify(event)}`);
406                   // do some tasks ···
407                   });
408                   session.on('outputDeviceChange', (device) => {
409                   console.info(`on outputDeviceChange , the device info is ${JSON.stringify(device)}`);
410                   // do some tasks ···
411                   });
412                   session.on('commonCommand', (commandString, args) => {
413                   console.info(`on commonCommand , command is ${commandString}, args are ${JSON.stringify(args)}`);
414                   // do some tasks ···
415                   });
416               }
417             })
418         }
419       .width('100%')
420       .height('100%')
421     }
422   }
423   ```
424
4257. 获取当前媒体会话自身的控制器,与媒体会话对应进行通信交互。
426
427   ```ts
428   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
429
430   @Entry
431   @Component
432   struct Index {
433     @State message: string = 'hello world';
434
435     build() {
436       Column() {
437           Text(this.message)
438             .onClick(()=>{
439               let context = this.getUIContext().getHostContext() as Context;
440               async function createControllerFromSession() {
441                   // 假设已经创建了一个session,如何创建session可以参考之前的案例。
442                   let type: AVSessionManager.AVSessionType = 'audio';
443                   let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
444
445                   // 通过已有session获取一个controller对象。
446                   let controller = await session.getController();
447
448                   // controller可以与原session对象进行基本的通信交互,比如下发播放命令。
449                   let avCommand: AVSessionManager.AVControlCommand = {command:'play'};
450                   controller.sendControlCommand(avCommand);
451
452                   // 或者做状态变更监听。
453                   controller.on('playbackStateChange', 'all', (state) => {
454
455                   // do some things.
456                   });
457
458                   // controller可以做的操作还有很多,具体可以参考媒体会话控制方相关的说明。
459               }
460             })
461         }
462       .width('100%')
463       .height('100%')
464     }
465   }
466   ```
467
4688. 音视频应用在退出,并且不需要继续播放时,及时取消监听以及销毁媒体会话释放资源。
469   取消播控命令监听的示例代码如下所示 :
470
471   ```ts
472   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
473   @Entry
474   @Component
475   struct Index {
476     @State message: string = 'hello world';
477
478     build() {
479       Column() {
480           Text(this.message)
481             .onClick(()=>{
482               let context = this.getUIContext().getHostContext() as Context;
483               async function unregisterSessionListener() {
484                   // 假设已经创建了一个session,如何创建session可以参考之前的案例。
485                   let type: AVSessionManager.AVSessionType = 'audio';
486                   let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
487
488                   // 取消指定session下的相关监听。
489                   session.off('play');
490                   session.off('pause');
491                   session.off('stop');
492                   session.off('playNext');
493                   session.off('playPrevious');
494                   session.off('skipToQueueItem');
495                   session.off('handleKeyEvent');
496                   session.off('outputDeviceChange');
497                   session.off('commonCommand');
498               }
499             })
500         }
501       .width('100%')
502       .height('100%')
503     }
504   }
505   ```
506
507     销毁媒体会话示例代码如下所示:
508
509   ```ts
510   ```ts
511   import { avSession as AVSessionManager } from '@kit.AVSessionKit';
512   @Entry
513   @Component
514   struct Index {
515     @State message: string = 'hello world';
516
517     build() {
518       Column() {
519           Text(this.message)
520             .onClick(()=>{
521               let context = this.getUIContext().getHostContext() as Context;
522               async function destroySession() {
523                   // 假设已经创建了一个session,如何创建session可以参考之前的案例。
524                   let type: AVSessionManager.AVSessionType = 'audio';
525                   let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
526                   // 主动销毁已创建的session。
527                   session.destroy((err) => {
528                   if (err) {
529                       console.error(`Failed to destroy session. Code: ${err.code}, message: ${err.message}`);
530                   } else {
531                       console.info(`Destroy : SUCCESS `);
532                   }
533                   });
534               }
535             })
536         }
537       .width('100%')
538       .height('100%')
539     }
540   }
541   ```
542
543## 相关实例
544
545针对媒体会话提供方开发,有以下相关实例可供参考:
546
547- [媒体会话——提供方(ArkTS)(Full SDK)(API10)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Media/AVSession/MediaProvider)