1# 多音频播放的并发策略 2 3## 音频打断策略 4 5多音频并发,即多个音频流同时播放。此场景下,如果系统不加管控,会造成多个音频流混音播放,容易让用户感到嘈杂,造成不好的用户体验。为了解决这个问题,系统预设了音频打断策略,对多音频播放的并发进行管控,只有持有音频焦点的音频流才可以正常播放,避免多个音频流无序并发播放的现象出现。 6 7当应用开始播放音频时,系统首先为相应的音频流申请音频焦点,获得焦点的音频流可以播放;若焦点申请被拒绝,则不能播放。在音频流播放的过程中,若被其他音频流打断,则会失去音频焦点。当音频流失去音频焦点时,只能暂停播放。在应用播放音频的过程中,这些动作均由系统自行完成,无需应用主动触发。但为了维持应用和系统的状态一致性,保证良好的用户体验,推荐应用[监听音频打断事件](#监听音频打断事件),并在收到音频打断事件([InterruptEvent](../reference/apis/js-apis-audio.md#interruptevent9))时做出相应处理。 8 9为满足应用对多音频并发策略的不同需求,音频打断策略预设了两种焦点模式,针对同一应用创建的多个音频流,应用可通过设置[焦点模式](#焦点模式),选择由应用自主管控或由系统统一管控。 10 11音频打断策略决定了应该对音频流采取何种操作,如暂停播放、继续播放、降低音量播放、恢复音量播放等,这些操作可能由系统或应用来执行。音频打断策略预置了两种[打断类型](#打断类型),用于区分音频打断事件(InterruptEvent)的执行者。 12 13### 焦点模式 14 15音频打断策略预设了两种焦点模式([InterruptMode](../reference/apis/js-apis-audio.md#interruptmode9)): 16 17- 共享焦点模式(SHARE_MODE):由同一应用创建的多个音频流,共享一个音频焦点。这些音频流之间的并发规则由应用自主决定,音频打断策略不会介入。当其他应用创建的音频流与该应用的音频流并发播放时,才会触发音频打断策略的管控。 18 19- 独立焦点模式(INDEPENDENT_MODE):应用创建的每一个音频流均会独立拥有一个音频焦点,当多个音频流并发播放时,会触发音频打断策略的管控。 20 21应用可以按需选择合适的焦点模式,在创建音频流时,系统默认采用共享焦点模式,应用可主动设置所需的模式。 22 23设置焦点模式的方法: 24 25- 若[使用AVPlayer开发音频播放功能](using-avplayer-for-playback.md),则可以通过修改AVPlayer的[audioInterruptMode](../reference/apis/js-apis-media.md#avplayer9)属性进行设置。 26 27- 若[使用AudioRenderer开发音频播放功能](using-audiorenderer-for-playback.md),则可以调用AudioRenderer的[setInterruptMode](../reference/apis/js-apis-audio.md#setinterruptmode9)函数进行设置。 28 29 30### 打断类型 31 32音频打断策略(包括两种焦点模式)决定了应该对各个音频流采取何种操作,如暂停播放、继续播放、降低音量播放、恢复音量播放等。而针对这些操作的执行过程,根据执行者的不同,可以分为两种打断类型([InterruptForceType](../reference/apis/js-apis-audio.md#interruptforcetype9)): 33 34- 强制打断类型(INTERRUPT_FORCE):由系统进行操作,强制打断音频播放。 35 36- 共享打断类型(INTERRUPT_SHARE):由应用进行操作,可以选择打断或忽略。 37 38对于音频打断策略的执行,系统默认采用强制打断类型(INTERRUPT_FORCE),应用无法更改。但对于一些策略(如继续播放等),系统无法强制执行,所以这两种打断类型均可能出现。应用可根据音频打断事件(InterruptEvent)的成员变量forceType的值,获取该事件采用的打断类型。 39 40在应用播放音频的过程中,系统自动为音频流执行申请焦点、持有焦点、释放焦点等动作,当发生音频打断事件时,系统强制对音频流执行暂停、停止、降低音量、恢复音量等操作,并向应用发送音频打断事件(InterruptEvent)回调。由于系统会强制改变音频流状态,为了维持应用和系统的状态一致性,保证良好的用户体验,推荐应用[监听音频打断事件](#监听音频打断事件),并在收到音频打断事件(InterruptEvent)时做出相应处理。 41 42对于一些系统无法强制执行的操作(例如音频流继续播放的场景),会向应用发送包含了共享打断类型的音频打断事件,由应用自行执行相应操作,此时应用可以选择执行或忽略,系统不会干涉。 43 44## 监听音频打断事件 45 46在应用播放音频时,推荐应用监听音频打断事件,当音频打断事件发生时,系统会根据预设策略,对音频流做出相应的操作,并针对状态发生改变的音频流,向所属的应用发送音频打断事件。 47 48应用收到音频打断事件后,需根据其内容提示,做出相应的处理,避免出现应用状态与预期效果不一致的问题。 49 50监听音频打断事件的方法: 51 52- 若[使用AVPlayer开发音频播放功能](using-avplayer-for-playback.md),则可以调用AVPlayer的[on('audioInterrupt')](../reference/apis/js-apis-media.md#onaudiointerrupt9)函数进行监听,当收到音频打断事件(InterruptEvent)时,应用需根据其内容,做出相应的调整。 53 54- 若[使用AudioRenderer开发音频播放功能](using-audiorenderer-for-playback.md),则可以调用AudioRenderer的[on('audioInterrupt')](../reference/apis/js-apis-audio.md#onaudiointerrupt9)函数进行监听,当收到音频打断事件(InterruptEvent)时,应用需根据其内容,做出相应的调整。 55 56 为了带给用户更好的体验,针对不同的音频打断事件内容,应用需要做出相应的处理操作。此处以使用AudioRenderer开发音频播放功能为例,展示推荐应用采取的处理方法,提供伪代码供开发者参考(若使用AVPlayer开发音频播放功能,处理方法类似),具体的代码实现,开发者可结合实际情况编写,处理方法也可自行调整。 57 58```ts 59let isPlay; // 是否正在播放,实际开发中,对应与音频播放状态相关的模块 60let isDucked; //是否降低音量,实际开发中,对应与音频音量相关的模块 61let started; // 标识符,记录“开始播放(start)”操作是否成功 62 63async function onAudioInterrupt(){ 64 // 此处以使用AudioRenderer开发音频播放功能举例,变量audioRenderer即为播放时创建的AudioRenderer实例。 65 audioRenderer.on('audioInterrupt', async(interruptEvent) => { 66 // 在发生音频打断事件时,audioRenderer收到interruptEvent回调,此处根据其内容做相应处理 67 // 先读取interruptEvent.forceType的类型,判断系统是否已强制执行相应操作 68 // 再读取interruptEvent.hintType的类型,做出相应的处理 69 if (interruptEvent.forceType === audio.InterruptForceType.INTERRUPT_FORCE) { 70 // 强制打断类型(INTERRUPT_FORCE):音频相关处理已由系统执行,应用需更新自身状态,做相应调整 71 switch (interruptEvent.hintType) { 72 case audio.InterruptHint.INTERRUPT_HINT_PAUSE: 73 // 此分支表示系统已将音频流暂停(临时失去焦点),为保持状态一致,应用需切换至音频暂停状态 74 // 临时失去焦点:待其他音频流释放音频焦点后,本音频流会收到resume对应的音频打断事件,到时可自行继续播放 75 isPlay = false; // 此句为简化处理,代表应用切换至音频暂停状态的若干操作 76 break; 77 case audio.InterruptHint.INTERRUPT_HINT_STOP: 78 // 此分支表示系统已将音频流停止(永久失去焦点),为保持状态一致,应用需切换至音频暂停状态 79 // 永久失去焦点:后续不会再收到任何音频打断事件,若想恢复播放,需要用户主动触发。 80 isPlay = false; // 此句为简化处理,代表应用切换至音频暂停状态的若干操作 81 break; 82 case audio.InterruptHint.INTERRUPT_HINT_DUCK: 83 // 此分支表示系统已将音频音量降低(默认降到正常音量的20%),为保持状态一致,应用需切换至降低音量播放状态 84 // 若应用不接受降低音量播放,可在此处选择其他处理方式,如主动暂停等 85 isDucked = true; // 此句为简化处理,代表应用切换至降低音量播放状态的若干操作 86 break; 87 case audio.InterruptHint.INTERRUPT_HINT_UNDUCK: 88 // 此分支表示系统已将音频音量恢复正常,为保持状态一致,应用需切换至正常音量播放状态 89 isDucked = false; // 此句为简化处理,代表应用切换至正常音量播放状态的若干操作 90 break; 91 default: 92 break; 93 } 94 } else if (interruptEvent.forceType === audio.InterruptForceType.INTERRUPT_SHARE) { 95 // 共享打断类型(INTERRUPT_SHARE):应用可自主选择执行相关操作或忽略音频打断事件 96 switch (interruptEvent.hintType) { 97 case audio.InterruptHint.INTERRUPT_HINT_RESUME: 98 // 此分支表示临时失去焦点后被暂停的音频流此时可以继续播放,建议应用继续播放,切换至音频播放状态 99 // 若应用此时不想继续播放,可以忽略此音频打断事件,不进行处理即可 100 // 继续播放,此处主动执行start(),以标识符变量started记录start()的执行结果 101 await audioRenderer.start().then(async function () { 102 started = true; // start()执行成功 103 }).catch((err) => { 104 started = false; // start()执行失败 105 }); 106 // 若start()执行成功,则切换至音频播放状态 107 if (started) { 108 isPlay = true; // 此句为简化处理,代表应用切换至音频播放状态的若干操作 109 } else { 110 // 音频继续播放执行失败 111 } 112 break; 113 default: 114 break; 115 } 116 } 117 }); 118} 119``` 120