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-->