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