• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 应用接入AVSession场景介绍
2
3音视频应用在实现音视频功能的同时,需要接入媒体会话即AVSession Kit,下文将提供一些典型的接入AVSession的展示和控制场景,方便开发者根据场景进行适配。
4
5对于不同的场景,将会在系统的播控中心看到不同的UI呈现。同时,在不同的场景下,应用的接入处理也需要遵循不同的规范约束。
6
7## 哪些场景下需要接入AVSession
8
9AVSession会对后台的音频播放、VOIP通话做约束,所以通常来说,长音频应用、听书类应用、长视频应用、VOIP类应用等都需要接入AVSession。当应用在没有创建接入AVSession的情况下进行了上述业务,那么系统会在检测到应用后台时,停止对应的音频播放,静音通话声音,以达到约束应用行为的目的。这种约束,应用上架前在本地就可以验证。
10
11对于其他使用到音频播放的应用,比如游戏,直播等场景,接入AVSession不是必选项,只是可选,取决于应用是否有后台播放的使用诉求。若应用需要后台播放,那么接入AVSession仍然是必须的,否则业务的正常功能会受到限制。
12
13当应用需要实现后台播放等功能时,需要使用[BackgroundTasks Kit](../../task-management/background-task-overview.md)(后台任务管理)的能力,申请对应的长时任务,避免进入挂起(Suspend)状态。
14
15## 接入流程
16
17应用接入AVSession流程分为如下几个步骤:
18
191. 确定应用需要创建的会话类型,[创建对应的会话](#创建不同类型的会话),不同类型决定了播控中心展示的控制模板样式。
202. 按需[创建后台任务](#创建后台任务)。
213. [设置必要的元数据(Metadata)](#设置元数据),以在播控中心展示响应的信息,包括不限于:当前媒体的ID(assetId),上一首媒体的ID(previousAssetId),下一首媒体的ID(nextAssetId),标题(title),专辑作者(author),专辑名称(album),词作者(writer),媒体时长(duration)等属性。
224. [设置播放相关的状态](#设置播放状态),包括不限于:当前媒体的播放状态(state)、播放位置(position)、播放倍速(speed)、缓冲时间(bufferedTime)、循环模式(loopMode)、是否收藏(isFavorite)、正在播放的媒体Id(activeItemId)、自定义媒体数据(extras)等属性。
235. 按需[注册不同的控制命令](#注册控制命令),包括不限于:播放/暂停、上下一首、快进快退、收藏、循环模式、进度条。
246. 应用退出或者无对应业务时,注销会话。
25
26## 创建不同类型的会话
27
28AVSession在构造方法中支持不同的类型参数,由 [AVSessionType](../../reference/apis-avsession-kit/js-apis-avsession.md#avsessiontype10) 定义,不同的类型代表了不同场景的控制能力,对于播控中心来说,会展示不同的控制模版。
29
30- audio类型,播控中心的控制样式为:收藏,上一首,播放/暂停,下一首,循环模式。
31
32- video类型,播控中心的控制样式为:快退,上一首,播放/暂停,下一首,快进。
33
34- voice_call类型,通话类型。
35
36使用代码示例:
37
38```ts
39import { avSession as AVSessionManager } from '@kit.AVSessionKit';
40@Entry
41@Component
42struct Index {
43  @State message: string = 'hello world';
44
45  build() {
46    Column() {
47        Text(this.message)
48          .onClick(()=>{
49            // 开始创建并激活媒体会话。
50            // 创建session。
51            let context = this.getUIContext().getHostContext() as Context;
52            async function createSession() {
53            let type: AVSessionManager.AVSessionType = 'audio';
54            let session = await AVSessionManager.createAVSession(context,'SESSION_NAME', type);
55
56            // 激活接口要在元数据、控制命令注册完成之后再执行。
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
68## 创建后台任务
69
70当应用需要实现后台播放等功能时,需要使用[BackgroundTasks Kit](../../task-management/background-task-overview.md)(后台任务管理)的能力,申请对应的长时任务,避免进入挂起(Suspend)状态。
71
72对媒体类播放来说,需要申请[AUDIO_PLAYBACK BackgroundMode](../../reference/apis-backgroundtasks-kit/js-apis-resourceschedule-backgroundTaskManager.md#backgroundmode)的长时任务。
73
74
75## 设置元数据
76
77### 通用元数据
78
79应用可以通过setAVMetadata把会话的一些元数据信息设置给系统,从而在播控中心界面进行展示,包括不限制:当前媒体的ID(assetId),上一首媒体的ID(previousAssetId),下一首媒体的ID(nextAssetId),标题(title),专辑作者(author),专辑名称(album),词作者(writer),媒体时长(duration)等。
80
81```ts
82import { avSession as AVSessionManager } from '@kit.AVSessionKit';
83import { BusinessError } from '@kit.BasicServicesKit';
84@Entry
85@Component
86struct Index {
87  @State message: string = 'hello world';
88
89  build() {
90    Column() {
91        Text(this.message)
92          .onClick(()=>{
93            let context = this.getUIContext().getHostContext() as Context;
94            async function setSessionInfo() {
95            // 假设已经创建了一个session,如何创建session可以参考之前的案例。
96            let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', 'audio');
97            // 设置必要的媒体信息。
98            let metadata: AVSessionManager.AVMetadata = {
99                assetId: '0', // 由应用指定,用于标识应用媒体库里的媒体。
100                title: 'TITLE',
101                mediaImage: 'IMAGE',
102                artist: 'ARTIST',
103            };
104            session.setAVMetadata(metadata).then(() => {
105                console.info(`SetAVMetadata successfully`);
106            }).catch((err: BusinessError) => {
107                console.error(`Failed to set AVMetadata. Code: ${err.code}, message: ${err.message}`);
108            });
109            }
110          })
111      }
112    .width('100%')
113    .height('100%')
114  }
115}
116```
117
118### 歌词
119
120对于长音频来说,播控中心提供了歌词的展示页面,对于应用来说,接入也比较简单,只需要把歌词内容设置给系统。播控中心会解析歌词内容,并根据播放进度进行同步的刷新。
121
122```ts
123import { avSession as AVSessionManager } from '@kit.AVSessionKit';
124import { BusinessError } from '@kit.BasicServicesKit';
125@Entry
126@Component
127struct Index {
128  @State message: string = 'hello world';
129
130  build() {
131    Column() {
132        Text(this.message)
133          .onClick(()=>{
134            let context = this.getUIContext().getHostContext() as Context;
135            async function setListener() {
136            // 假设已经创建了一个session,如何创建session可以参考之前的案例。
137            let type: AVSessionManager.AVSessionType = 'audio';
138            let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
139
140            // 把歌词信息设置给AVSession。
141            let metadata: AVSessionManager.AVMetadata = {
142                assetId: '0',
143                title: 'TITLE',
144                mediaImage: 'IMAGE',
145                // LRC中有两类元素:一种是时间标签+歌词,一种是ID标签。
146                // 例如:[00:25.44]xxx\r\n[00:26.44]xxx\r\n。
147                lyric: "lrc格式歌词内容",
148                // singleLyricText字段存储单条歌词文本,不包含时间戳。
149                // 例如:"单条歌词内容"。
150                singleLyricText: "单条歌词内容",
151            };
152            session.setAVMetadata(metadata).then(() => {
153                console.info(`SetAVMetadata successfully`);
154            }).catch((err: BusinessError) => {
155                console.error(`Failed to set AVMetadata. Code: ${err.code}, message: ${err.message}`);
156            });
157            }
158          })
159      }
160    .width('100%')
161    .height('100%')
162  }
163}
164```
165
166<!--RP1-->
167<!--RP1End-->
168
169### 媒体资源金标
170
171对于长音频,播控中心提供了媒体资源金标的展示,媒体资源金标又可称为应用媒体音频音源的标识,目前暂时只支持展示AudioVivid标识。
172对于应用来说,接入只需要在AVMetadata中通知系统,当前播放音频的音源标识,播控就会同步展示。
173
174```ts
175import { avSession as AVSessionManager } from '@kit.AVSessionKit';
176import { BusinessError } from '@kit.BasicServicesKit';
177@Entry
178@Component
179struct Index {
180  @State message: string = 'hello world';
181
182  build() {
183    Column() {
184        Text(this.message)
185          .onClick(()=>{
186            let context = this.getUIContext().getHostContext() as Context;
187            async function setListener() {
188            // 假设已经创建了一个session,如何创建session可以参考之前的案例。
189            let type: AVSessionManager.AVSessionType = 'audio';
190            let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
191
192            // 把媒体音源信息设置给AVSession。
193            let metadata: AVSessionManager.AVMetadata = {
194                assetId: '0',
195                title: 'TITLE',
196                mediaImage: 'IMAGE',
197                // 标识该媒体音源是AudioVivid。
198                displayTags: AVSessionManager.DisplayTag.TAG_AUDIO_VIVID,
199            };
200            session.setAVMetadata(metadata).then(() => {
201                console.info(`SetAVMetadata successfully`);
202            }).catch((err: BusinessError) => {
203                console.error(`Failed to set AVMetadata. Code: ${err.code}, message: ${err.message}`);
204            });
205            }
206          })
207      }
208    .width('100%')
209    .height('100%')
210  }
211}
212```
213
214## 设置播放状态
215
216### 通用播放状态
217
218应用可以通过[setAVPlaybackState](../../reference/apis-avsession-kit/js-apis-avsession.md#setavplaybackstate10)。把当前的播放状态设置给系统,以在播控中心界面进行展示。
219播放状态一般是在资源播放后会进行变化的内容,包括:当前媒体的播放状态(state)、播放位置(position)、播放倍速(speed)、缓冲时间(bufferedTime)、循环模式(loopMode)、是否收藏(isFavorite)、正在播放的媒体Id(activeItemId)、自定义媒体数据(extras)等。
220
221```ts
222import { avSession as AVSessionManager } from '@kit.AVSessionKit';
223import { BusinessError } from '@kit.BasicServicesKit';
224@Entry
225@Component
226struct Index {
227  @State message: string = 'hello world';
228
229  build() {
230    Column() {
231        Text(this.message)
232          .onClick(()=>{
233            let context = this.getUIContext().getHostContext() as Context;
234            async function setSessionInfo() {
235            // 假设已经创建了一个session,如何创建session可以参考之前的案例。
236            let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', 'audio');
237
238            // 播放器逻辑··· 引发媒体信息与播放状态的变更。
239            // 简单设置一个播放状态 - 暂停 未收藏。
240            let playbackState: AVSessionManager.AVPlaybackState = {
241                state:AVSessionManager.PlaybackState.PLAYBACK_STATE_PAUSE,
242                isFavorite:false
243            };
244            session.setAVPlaybackState(playbackState, (err: BusinessError) => {
245            if (err) {
246                console.error(`Failed to set AVPlaybackState. Code: ${err.code}, message: ${err.message}`);
247                } else {
248                console.info(`SetAVPlaybackState successfully`);
249                }
250            });
251            }
252          })
253      }
254    .width('100%')
255    .height('100%')
256  }
257}
258```
259
260### 进度条
261
262应用如果支持在播控中心展示进度,那么在媒体资源播放中,需要设置资源的时长、播放状态(暂停、播放)、播放位置、倍速,播控中心会使用这些信息进行进度的展示:
263
264```ts
265import { avSession as AVSessionManager } from '@kit.AVSessionKit';
266import { BusinessError } from '@kit.BasicServicesKit';
267@Entry
268@Component
269struct Index {
270  @State message: string = 'hello world';
271
272  build() {
273    Column() {
274        Text(this.message)
275          .onClick(()=>{
276            let context = this.getUIContext().getHostContext() as Context;
277            async function setListener() {
278            // 假设已经创建了一个session,如何创建session可以参考之前的案例。
279            let type: AVSessionManager.AVSessionType = 'audio';
280            let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
281
282            // 设置媒体资源时长。
283            let metadata: AVSessionManager.AVMetadata = {
284                assetId: '0',
285                title: 'TITLE',
286                mediaImage: 'IMAGE',
287                duration: 23000, // 资源的时长,以ms为单位。
288            };
289            session.setAVMetadata(metadata).then(() => {
290                console.info(`SetAVMetadata successfully`);
291            }).catch((err: BusinessError) => {
292                console.error(`Failed to set AVMetadata. Code: ${err.code}, message: ${err.message}`);
293            });
294
295            // 设置状态: 播放状态,进度位置,播放倍速,缓存的时间。
296            let playbackState: AVSessionManager.AVPlaybackState = {
297                state: AVSessionManager.PlaybackState.PLAYBACK_STATE_PLAY, // 播放状态。
298                position: {
299                elapsedTime: 1000, // 已经播放的位置,以ms为单位。
300                updateTime: new Date().getTime(), // 应用更新当前位置时的时间戳,以ms为单位。
301                },
302                speed: 1.0, // 可选,默认是1.0,播放的倍速,按照应用内支持的speed进行设置,系统不做校验。
303                bufferedTime: 14000, // 可选,资源缓存的时间,以ms为单位。
304            };
305            session.setAVPlaybackState(playbackState, (err) => {
306                if (err) {
307                console.error(`Failed to set AVPlaybackState. Code: ${err.code}, message: ${err.message}`);
308                } else {
309                console.info(`SetAVPlaybackState successfully`);
310                }
311            });
312            }
313          })
314      }
315    .width('100%')
316    .height('100%')
317  }
318}
319```
320
321系统的播控中心会根据应用设置的信息自行进行播放进度的计算,而不需要应用实时更新播放进度;
322但是应用需要如下状态发生变化的时候,再更新AVPlaybackState,否则系统会发生计算错误:
323
324- state
325- position
326- speed
327
328应用在真实播放开始时,再上报进度起始position;若播放存在buffer状态,可以先上报播放状态为AVSessionManager.PlaybackState.PLAYBACK_STATE_BUFFERING,来通知系统不刷新进度。
329
330关于进度条有一些特殊情况需要处理:
331
3321. 歌曲支持试听
333
334    (1)应用不需要设置完整的歌曲时长,则只需要设置歌曲的试听时长。当应用仅设置歌曲的试听时长而不是完整时长,用户在播控中心触发进度控制时,应用收到的时长也是VIP试听时长内的相对时间戳位置,而不是完整歌曲的绝对时间戳位置,应用需要重新计算歌曲从零开始的绝对时间戳进行实际响应处理。
335
336    (2)如果应用设置完整歌曲时长,但需要系统支持试听片段,也可以在播放时上报起始进度position,当收到的seek指令超过试听片段时,上报试听截止position,系统播控的进度会跟随回弹。
337
3382. 歌曲不支持试听
339
340    如果歌曲不支持试听,那么理论上应用内也不支持播放,这时可以把 duration 设置为 -1,以通知系统不显示实际的时长。
341
3423. 广告等内容的时长设置
343
344    对于有前贴广告、后贴广告的资源来说,建议这么处理:
345    - 播放广告时,单独设置广告的时长 duration。
346    - 当进入到正片播放的时候,则重新设置一次新的时长,以与广告进行区分。
347
348## 注册控制命令
349
350应用接入AVSession,可以通过注册不同的控制命令来实现播控中心界面上的控制操作,即通过on接口注册不同的控制命令参数,即可实现对应的功能。
351具体的接口参考[接口注册](../../reference/apis-avsession-kit/js-apis-avsession.md#onplay10)。
352> **说明:**
353>
354> 创建AVSession后,请先注册应用支持的控制命令,再激活 Session。
355
356媒体资源支持的控制命令列表:
357
358| 控制命令 | 功能说明   |
359| ------  | -------------------------|
360| play    | 播放命令。 |
361| pause    | 暂停命令。 |
362| stop    | 停止命令。 |
363| playNext    | 播放下一首命令。 |
364| playPrevious    | 播放上一首命令。 |
365| fastForward    | 快进命令。 |
366| rewind    | 快退命令。 |
367| playFromAssetId    | 根据某个资源id进行播放命令。 |
368| seek    | 跳转命令。 |
369| setSpeed    | 设置播放速率命令。 |
370| setLoopMode    | 设置循环模式命令。 |
371| toggleFavorite    | 设置是否收藏命令。 |
372| skipToQueueItem    | 设置播放列表其中某项被选中播放的命令。 |
373| handleKeyEvent    | 设置按键事件的命令。 |
374| commonCommand    | 设置自定义控制命令。 |
375
376通话类应用支持的控制:
377
378| 控制命令 | 功能说明   |
379| ------  | -------------------------|
380| answer    | 接听电话的命令。 |
381| hangUp    | 通话挂断的命令。 |
382| toggleCallMute    | 通话静音或解除静音的命令。 |
383
384### 不支持命令的处理
385
386系统支持的控制命令对于不支持的控制,比如应用不支持“上一首”的命令处理,只需要使用off 接口注销对应的控制命令,系统的播控中心会相应的对该控制界面进行置灰处理,以明确告知用户此控制命令不支持。
387
388```ts
389import { avSession as AVSessionManager } from '@kit.AVSessionKit';
390@Entry
391@Component
392struct Index {
393  @State message: string = 'hello world';
394
395  build() {
396    Column() {
397        Text(this.message)
398          .onClick(()=>{
399            let context = this.getUIContext().getHostContext() as Context;
400            async function unregisterSessionListener() {
401            // 假设已经创建了一个session,如何创建session可以参考之前的案例。
402            let type: AVSessionManager.AVSessionType = 'audio';
403            let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
404
405            // 取消指定session下的相关监听。
406            session.off('play');
407            session.off('pause');
408            session.off('stop');
409            session.off('playNext');
410            session.off('playPrevious');
411            }
412          })
413      }
414    .width('100%')
415    .height('100%')
416  }
417}
418```
419
420### 快进快退
421
422系统支持三种快进快退的时长,应用可以通过接口进行设置;同时注册快进快退的回调命令,以响应控制。
423
424```ts
425import { avSession as AVSessionManager } from '@kit.AVSessionKit';
426import { BusinessError } from '@kit.BasicServicesKit';
427@Entry
428@Component
429struct Index {
430  @State message: string = 'hello world';
431
432  build() {
433    Column() {
434        Text(this.message)
435          .onClick(()=>{
436            let context = this.getUIContext().getHostContext() as Context;
437            async function unregisterSessionListener() {
438            // 假设已经创建了一个session,如何创建session可以参考之前的案例。
439            let type: AVSessionManager.AVSessionType = 'audio';
440            let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
441
442            // 设置支持的快进快退的时长设置给AVSession。
443            let metadata: AVSessionManager.AVMetadata = {
444                assetId: '0', // 由应用指定,用于标识应用媒体库里的媒体。
445                title: 'TITLE',
446                mediaImage: 'IMAGE',
447                skipIntervals: AVSessionManager.SkipIntervals.SECONDS_10,
448            };
449            session.setAVMetadata(metadata).then(() => {
450                console.info(`SetAVMetadata successfully`);
451            }).catch((err: BusinessError) => {
452                console.error(`Failed to set AVMetadata. Code: ${err.code}, message: ${err.message}`);
453            });
454
455            session.on('fastForward', (time ?: number) => {
456                console.info(`on fastForward , do fastForward task`);
457                // do some tasks ···
458            });
459            session.on('rewind', (time ?: number) => {
460                console.info(`on rewind , do rewind task`);
461                // do some tasks ···
462            });
463            }
464          })
465      }
466    .width('100%')
467    .height('100%')
468  }
469}
470```
471
472### 收藏
473
474音乐类应用实现收藏功能,那么需要注册收藏的控制响应[on('toggleFavorite')](../../reference/apis-avsession-kit/js-apis-avsession.md#ontogglefavorite10)。
475
476```ts
477import { avSession as AVSessionManager } from '@kit.AVSessionKit';
478import { BusinessError } from '@kit.BasicServicesKit';
479@Entry
480@Component
481struct Index {
482  @State message: string = 'hello world';
483
484  build() {
485    Column() {
486        Text(this.message)
487          .onClick(()=>{
488            let context = this.getUIContext().getHostContext() as Context;
489            async function setListener() {
490            // 假设已经创建了一个session,如何创建session可以参考之前的案例。
491            let type: AVSessionManager.AVSessionType = 'audio';
492            let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
493            session.on('toggleFavorite', (assetId) => {
494            console.info(`on toggleFavorite `);
495            // 应用收到收藏命令,进行收藏处理。
496
497            // 应用内完成或者取消收藏,把新的收藏状态设置给AVSession。
498            let playbackState: AVSessionManager.AVPlaybackState = {
499                isFavorite:true,
500            };
501            session.setAVPlaybackState(playbackState).then(() => {
502                console.info(`SetAVPlaybackState successfully`);
503            }).catch((err: BusinessError) => {
504                console.info(`SetAVPlaybackState BusinessError: code: ${err.code}, message: ${err.message}`);
505            });
506
507            });
508            }
509          })
510      }
511    .width('100%')
512    .height('100%')
513  }
514}
515```
516
517### 循环模式
518
519针对音乐类应用,系统的播控中心界面会默认展示循环模式的控制操作,目前系统支持四种固定的循环模式控制,参考: [LoopMode](../../reference/apis-avsession-kit/js-apis-avsession.md#loopmode10)。
520
521播控中心支持固定的四种循环模式的切换,即: 随机播放、顺序播放、单曲循环、列表循环。应用收到循环模式切换的指令并切换后,需要向系统上报切换后的LoopMode。
522若应用内支持的LoopMode不在系统固定的四个循环模式内,需要选择四个固定循环模式其一向系统上报,由应用自定。
523
524实现参考:
525
526```ts
527@Entry
528@Component
529struct Index {
530  @State message: string = 'hello world';
531
532  build() {
533    Column() {
534        Text(this.message)
535          .onClick(()=>{
536            let context = this.getUIContext().getHostContext() as Context;
537            async function setListener() {
538            // 假设已经创建了一个session,如何创建session可以参考之前的案例。
539            let type: AVSessionManager.AVSessionType = 'audio';
540            let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
541
542            // 应用启动时/内部切换循环模式,需要把应用内的当前的循环模式设置给AVSession。
543            let playBackState: AVSessionManager.AVPlaybackState = {
544            loopMode: AVSessionManager.LoopMode.LOOP_MODE_SINGLE,
545            };
546            session.setAVPlaybackState(playBackState).then(() => {
547            console.info(`set AVPlaybackState successfully`);
548            }).catch((err: BusinessError) => {
549            console.error(`Failed to set AVPlaybackState. Code: ${err.code}, message: ${err.message}`);
550            });
551
552            // 应用注册循环模式的控制监听。
553            session.on('setLoopMode', (mode) => {
554            console.info(`on setLoopMode ${mode}`);
555            // 应用收到设置循环模式的指令后,应用自定下一个模式,切换完毕后通过AVPlaybackState上报切换后的LoopMode。
556            let playBackState: AVSessionManager.AVPlaybackState = {
557                loopMode: AVSessionManager.LoopMode.LOOP_MODE_SINGLE,
558            };
559            session.setAVPlaybackState(playBackState).then(() => {
560                console.info(`set AVPlaybackState successfully`);
561            }).catch((err: BusinessError) => {
562                console.error(`Failed to set AVPlaybackState. Code: ${err.code}, message: ${err.message}`);
563            });
564            });
565            }
566          })
567      }
568    .width('100%')
569    .height('100%')
570  }
571}
572```
573
574### 进度控制
575
576应用如果支持进度显示,进一步也可以支持进度控制。应用需要响应seek的控制命令,那么当用户在播控中心的界面上进行拖动操作时,应用就会收到对应的回调。参考实现:
577
578```ts
579import { avSession as AVSessionManager } from '@kit.AVSessionKit';
580@Entry
581@Component
582struct Index {
583  @State message: string = 'hello world';
584
585  build() {
586    Column() {
587        Text(this.message)
588          .onClick(()=>{
589            let context = this.getUIContext().getHostContext() as Context;
590            async function setListener() {
591            // 假设已经创建了一个session,如何创建session可以参考之前的案例。
592            let type: AVSessionManager.AVSessionType = 'audio';
593            let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
594
595            session.on('seek', (position: number) => {
596            console.info(`on seek , the time is ${JSON.stringify(position)}`);
597
598            // 由于应用内seek可能会触发较长的缓冲等待,可以先把状态设置为 Buffering。
599            let playbackState: AVSessionManager.AVPlaybackState = {
600                state: AVSessionManager.PlaybackState.PLAYBACK_STATE_BUFFERING, // 缓冲状态。
601            };
602            session.setAVPlaybackState(playbackState, (err) => {
603                if (err) {
604                console.error(`Failed to set AVPlaybackState. Code: ${err.code}, message: ${err.message}`);
605                } else {
606                console.info(`SetAVPlaybackState successfully`);
607                }
608            });
609
610            // 应用响应seek命令,使用应用内播放器完成seek实现。
611
612            // 应用内更新新的位置后,也需要同步更新状态给系统。
613            playbackState.state = AVSessionManager.PlaybackState.PLAYBACK_STATE_PLAY; // 播放状态。
614            playbackState.position = {
615                elapsedTime: position, // 已经播放的位置,以ms为单位。
616                updateTime: new Date().getTime(), // 应用更新当前位置的时间戳,以ms为单位。
617            }
618            session.setAVPlaybackState(playbackState, (err) => {
619                if (err) {
620                console.error(`Failed to set AVPlaybackState. Code: ${err.code}, message: ${err.message}`);
621                } else {
622                console.info(`SetAVPlaybackState successfully`);
623                }
624            });
625            });
626            }
627          })
628      }
629    .width('100%')
630    .height('100%')
631  }
632}
633```
634
635## 适配媒体通知
636
637当前系统不直接向应用提供主动发送媒体控制通知的接口,那么当应用正确接入媒体播控中心并进入播放状态时,系统会自动发送通知,同时在通知和锁屏界面进行展示。
638
639> **说明:**
640>
641> 通知中心、锁屏下的播控卡片的展示,由系统进行发送,并控制相应的生命周期。
642
643## 适配蓝牙按键与有线按键事件
644
645当前系统不直接向应用提供监听多模按键事件的接口,应用如需要监听蓝牙与有线耳机的媒体按键事件,可以通过注册AVSession的控制指令来实现。AVSession提供了如下两种实现方式:
646- 方式一(推荐使用):
647  按照应用业务需求,正确接入媒体播控中心,[注册需要的控制指令](#注册控制命令)并实现对应的功能。AVSession会监听多模按键事件,将其转换为AVSession的控制指令发送回应用。应用无须区分不同的按键事件,按照AVSession的回调处理即可。按照此方式接入播放暂停,也等同于适配了蓝牙耳机的佩戴检测,在双耳佩戴与摘下时也会收到如下播放暂停控制指令。目前支持转换的AVSession控制指令如下:
648  | 控制命令 | 功能说明   |
649  | ------  | -------------------------|
650  | play    | 播放命令。 |
651  | pause    | 暂停命令。 |
652  | stop    | 停止命令。 |
653  | playNext    | 播放下一首命令。 |
654  | playPrevious    | 播放上一首命令。 |
655  | fastForward    | 快进命令。 |
656  | rewind    | 快退命令。 |
657
658  ```ts
659  import { avSession as AVSessionManager } from '@kit.AVSessionKit';
660
661  @Entry
662  @Component
663  struct Index {
664    @State message: string = 'hello world';
665
666    build() {
667      Column() {
668          Text(this.message)
669            .onClick(()=>{
670              let context = this.getUIContext().getHostContext() as Context;
671              async function setListenerForMesFromController() {
672                  let type: AVSessionManager.AVSessionType = 'audio';
673                  let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
674                  // 设置必要的媒体信息,务必设置,否则接收不到控制事件。
675                  let metadata: AVSessionManager.AVMetadata = {
676                  assetId: '0', // 由应用指定,用于标识应用媒体库里的媒体。
677                  title: 'TITLE',
678                  mediaImage: 'IMAGE',
679                  artist: 'ARTIST'
680                  };
681                  session.setAVMetadata(metadata).then(() => {
682                  console.info(`SetAVMetadata successfully`);
683                  }).catch((err: BusinessError) => {
684                  console.error(`Failed to set AVMetadata. Code: ${err.code}, message: ${err.message}`);
685                  });
686                  // 一般在监听器中会对播放器做相应逻辑处理。
687                  // 不要忘记处理完后需要通过set接口同步播放相关信息,参考上面的用例。
688                  session.on('play', () => {
689                  console.info(`on play , do play task`);
690                  // 如暂不支持该指令,请勿注册;或在注册后但暂不使用时,通过session.off('play')取消监听。
691                  // 处理完毕后,请使用SetAVPlayState上报播放状态。
692                  });
693                  session.on('pause', () => {
694                  console.info(`on pause , do pause task`);
695                  // 如暂不支持该指令,请勿注册;或在注册后但暂不使用时,通过session.off('pause')取消监听。
696                  // 处理完毕后,请使用SetAVPlayState上报播放状态。
697                  });
698              }
699            })
700        }
701      .width('100%')
702      .height('100%')
703    }
704  }
705  ```
706
707- 方式二:
708  通过AVSession注册[HandleMediaKeyEvent](../../reference/apis-avsession-kit/js-apis-avsession.md#onhandlekeyevent10)指令。该回调接口会直接转发媒体按键事件[KeyEvent](../../reference/apis-input-kit/js-apis-keyevent.md)。应用需要自行识别按键事件的类型,并响应事件实现对应的功能。目前支持转发的按键事件类型如下:
709  | 按键类型([KeyCode](../../reference/apis-input-kit/js-apis-keycode.md#keycode)) | 功能说明   |
710  | ------  | -------------------------|
711  | KEYCODE_MEDIA_PLAY_PAUSE    | 多媒体键:播放/暂停 |
712  | KEYCODE_MEDIA_STOP    | 多媒体键:停止 |
713  | KEYCODE_MEDIA_NEXT    | 多媒体键:下一首 |
714  | KEYCODE_MEDIA_PREVIOUS    | 多媒体键:上一首 |
715  | KEYCODE_MEDIA_REWIND    | 多媒体键:快退 |
716  | KEYCODE_MEDIA_FAST_FORWARD    | 	多媒体键:快进 |
717  | KEYCODE_MEDIA_PLAY    | 多媒体键:播放 |
718  | KEYCODE_MEDIA_PAUSE   | 多媒体键:暂停|
719
720  ```ts
721  import { avSession as AVSessionManager } from '@kit.AVSessionKit';
722  import { BusinessError } from '@kit.BasicServicesKit';
723
724  @Entry
725  @Component
726  struct Index {
727    @State message: string = 'hello world';
728
729    build() {
730      Column() {
731          Text(this.message)
732            .onClick(()=>{
733              let context = this.getUIContext().getHostContext() as Context;
734              async function setListenerForMesFromController() {
735                  let type: AVSessionManager.AVSessionType = 'audio';
736                  let session = await AVSessionManager.createAVSession(context, 'SESSION_NAME', type);
737                  // 设置必要的媒体信息,务必设置,否则接收不到按键事件。
738                  let metadata: AVSessionManager.AVMetadata = {
739                  assetId: '0', // 由应用指定,用于标识应用媒体库里的媒体。
740                  title: 'TITLE',
741                  mediaImage: 'IMAGE',
742                  artist: 'ARTIST'
743                  };
744                  session.setAVMetadata(metadata).then(() => {
745                  console.info(`SetAVMetadata successfully`);
746                  }).catch((err: BusinessError) => {
747                  console.error(`Failed to set AVMetadata. Code: ${err.code}, message: ${err.message}`);
748                  });
749                  session.on('handleKeyEvent', (event) => {
750                  // 解析keycode,应用需要根据keycode对播放器做相应逻辑处理。
751                  console.info(`on handleKeyEvent, keyCode=${event.key.code}`);
752                  });
753              }
754            })
755        }
756      .width('100%')
757      .height('100%')
758    }
759  }
760  ```
761
762> **说明:**
763>
764> 1. 方式一与方式二均需正确设置媒体信息AVMetadata并注册相应控制接口,否则会无法接收到控制指令与按键事件。
765> 2. 方式一与方式二,选择其一接入即可,无须同时接入,系统推荐按照方式一接入。
766
767<!--RP2-->
768<!--RP2End-->