多音頻播放的并發(fā)策略

2024-02-16 13:53 更新

音頻打斷策略

多音頻并發(fā),即多個音頻流同時播放。此場景下,如果系統(tǒng)不加管控,會造成多個音頻流混音播放,容易讓用戶感到嘈雜,造成不好的用戶體驗(yàn)。為了解決這個問題,系統(tǒng)預(yù)設(shè)了音頻打斷策略,對多音頻播放的并發(fā)進(jìn)行管控,只有持有音頻焦點(diǎn)的音頻流才可以正常播放,避免多個音頻流無序并發(fā)播放的現(xiàn)象出現(xiàn)。

當(dāng)應(yīng)用開始播放音頻時,系統(tǒng)首先為相應(yīng)的音頻流申請音頻焦點(diǎn),獲得焦點(diǎn)的音頻流可以播放;若焦點(diǎn)申請被拒絕,則不能播放。在音頻流播放的過程中,若被其他音頻流打斷,則會失去音頻焦點(diǎn)。當(dāng)音頻流失去音頻焦點(diǎn)時,只能暫停播放。在應(yīng)用播放音頻的過程中,這些動作均由系統(tǒng)自行完成,無需應(yīng)用主動觸發(fā)。但為了維持應(yīng)用和系統(tǒng)的狀態(tài)一致性,保證良好的用戶體驗(yàn),推薦應(yīng)用監(jiān)聽音頻打斷事件,并在收到音頻打斷事件(InterruptEvent)時做出相應(yīng)處理。

為滿足應(yīng)用對多音頻并發(fā)策略的不同需求,音頻打斷策略預(yù)設(shè)了兩種焦點(diǎn)模式,針對同一應(yīng)用創(chuàng)建的多個音頻流,應(yīng)用可通過設(shè)置焦點(diǎn)模式,選擇由應(yīng)用自主管控或由系統(tǒng)統(tǒng)一管控。

音頻打斷策略決定了應(yīng)該對音頻流采取何種操作,如暫停播放、繼續(xù)播放、降低音量播放、恢復(fù)音量播放等,這些操作可能由系統(tǒng)或應(yīng)用來執(zhí)行。音頻打斷策略預(yù)置了兩種打斷類型,用于區(qū)分音頻打斷事件(InterruptEvent)的執(zhí)行者。

焦點(diǎn)模式

音頻打斷策略預(yù)設(shè)了兩種焦點(diǎn)模式(InterruptMode):

  • 共享焦點(diǎn)模式(SHARE_MODE):由同一應(yīng)用創(chuàng)建的多個音頻流,共享一個音頻焦點(diǎn)。這些音頻流之間的并發(fā)規(guī)則由應(yīng)用自主決定,音頻打斷策略不會介入。當(dāng)其他應(yīng)用創(chuàng)建的音頻流與該應(yīng)用的音頻流并發(fā)播放時,才會觸發(fā)音頻打斷策略的管控。
  • 獨(dú)立焦點(diǎn)模式(INDEPENDENT_MODE):應(yīng)用創(chuàng)建的每一個音頻流均會獨(dú)立擁有一個音頻焦點(diǎn),當(dāng)多個音頻流并發(fā)播放時,會觸發(fā)音頻打斷策略的管控。

應(yīng)用可以按需選擇合適的焦點(diǎn)模式,在創(chuàng)建音頻流時,系統(tǒng)默認(rèn)采用共享焦點(diǎn)模式,應(yīng)用可主動設(shè)置所需的模式。

設(shè)置焦點(diǎn)模式的方法:

打斷類型

音頻打斷策略(包括兩種焦點(diǎn)模式)決定了應(yīng)該對各個音頻流采取何種操作,如暫停播放、繼續(xù)播放、降低音量播放、恢復(fù)音量播放等。而針對這些操作的執(zhí)行過程,根據(jù)執(zhí)行者的不同,可以分為兩種打斷類型(InterruptForceType):

  • 強(qiáng)制打斷類型(INTERRUPT_FORCE):由系統(tǒng)進(jìn)行操作,強(qiáng)制打斷音頻播放。
  • 共享打斷類型(INTERRUPT_SHARE):由應(yīng)用進(jìn)行操作,可以選擇打斷或忽略。

對于音頻打斷策略的執(zhí)行,系統(tǒng)默認(rèn)采用強(qiáng)制打斷類型(INTERRUPT_FORCE),應(yīng)用無法更改。但對于一些策略(如繼續(xù)播放等),系統(tǒng)無法強(qiáng)制執(zhí)行,所以這兩種打斷類型均可能出現(xiàn)。應(yīng)用可根據(jù)音頻打斷事件(InterruptEvent)的成員變量forceType的值,獲取該事件采用的打斷類型。

在應(yīng)用播放音頻的過程中,系統(tǒng)自動為音頻流執(zhí)行申請焦點(diǎn)、持有焦點(diǎn)、釋放焦點(diǎn)等動作,當(dāng)發(fā)生音頻打斷事件時,系統(tǒng)強(qiáng)制對音頻流執(zhí)行暫停、停止、降低音量、恢復(fù)音量等操作,并向應(yīng)用發(fā)送音頻打斷事件(InterruptEvent)回調(diào)。由于系統(tǒng)會強(qiáng)制改變音頻流狀態(tài),為了維持應(yīng)用和系統(tǒng)的狀態(tài)一致性,保證良好的用戶體驗(yàn),推薦應(yīng)用監(jiān)聽音頻打斷事件,并在收到音頻打斷事件(InterruptEvent)時做出相應(yīng)處理。

對于一些系統(tǒng)無法強(qiáng)制執(zhí)行的操作(例如音頻流繼續(xù)播放的場景),會向應(yīng)用發(fā)送包含了共享打斷類型的音頻打斷事件,由應(yīng)用自行執(zhí)行相應(yīng)操作,此時應(yīng)用可以選擇執(zhí)行或忽略,系統(tǒng)不會干涉。

監(jiān)聽音頻打斷事件

在應(yīng)用播放音頻時,推薦應(yīng)用監(jiān)聽音頻打斷事件,當(dāng)音頻打斷事件發(fā)生時,系統(tǒng)會根據(jù)預(yù)設(shè)策略,對音頻流做出相應(yīng)的操作,并針對狀態(tài)發(fā)生改變的音頻流,向所屬的應(yīng)用發(fā)送音頻打斷事件。

應(yīng)用收到音頻打斷事件后,需根據(jù)其內(nèi)容提示,做出相應(yīng)的處理,避免出現(xiàn)應(yīng)用狀態(tài)與預(yù)期效果不一致的問題。

監(jiān)聽音頻打斷事件的方法:

為了帶給用戶更好的體驗(yàn),針對不同的音頻打斷事件內(nèi)容,應(yīng)用需要做出相應(yīng)的處理操作。此處以使用AudioRenderer開發(fā)音頻播放功能為例,展示推薦應(yīng)用采取的處理方法,提供偽代碼供開發(fā)者參考(若使用AVPlayer開發(fā)音頻播放功能,處理方法類似),具體的代碼實(shí)現(xiàn),開發(fā)者可結(jié)合實(shí)際情況編寫,處理方法也可自行調(diào)整。
  1. let isPlay; // 是否正在播放,實(shí)際開發(fā)中,對應(yīng)與音頻播放狀態(tài)相關(guān)的模塊
  2. let isDucked; //是否降低音量,實(shí)際開發(fā)中,對應(yīng)與音頻音量相關(guān)的模塊
  3. let started; // 標(biāo)識符,記錄“開始播放(start)”操作是否成功
  4. async function onAudioInterrupt(){
  5. // 此處以使用AudioRenderer開發(fā)音頻播放功能舉例,變量audioRenderer即為播放時創(chuàng)建的AudioRenderer實(shí)例。
  6. audioRenderer.on('audioInterrupt', async(interruptEvent) => {
  7. // 在發(fā)生音頻打斷事件時,audioRenderer收到interruptEvent回調(diào),此處根據(jù)其內(nèi)容做相應(yīng)處理
  8. // 先讀取interruptEvent.forceType的類型,判斷系統(tǒng)是否已強(qiáng)制執(zhí)行相應(yīng)操作
  9. // 再讀取interruptEvent.hintType的類型,做出相應(yīng)的處理
  10. if (interruptEvent.forceType === audio.InterruptForceType.INTERRUPT_FORCE) {
  11. // 強(qiáng)制打斷類型(INTERRUPT_FORCE):音頻相關(guān)處理已由系統(tǒng)執(zhí)行,應(yīng)用需更新自身狀態(tài),做相應(yīng)調(diào)整
  12. switch (interruptEvent.hintType) {
  13. case audio.InterruptHint.INTERRUPT_HINT_PAUSE:
  14. // 此分支表示系統(tǒng)已將音頻流暫停(臨時失去焦點(diǎn)),為保持狀態(tài)一致,應(yīng)用需切換至音頻暫停狀態(tài)
  15. // 臨時失去焦點(diǎn):待其他音頻流釋放音頻焦點(diǎn)后,本音頻流會收到resume對應(yīng)的音頻打斷事件,到時可自行繼續(xù)播放
  16. isPlay = false; // 此句為簡化處理,代表應(yīng)用切換至音頻暫停狀態(tài)的若干操作
  17. break;
  18. case audio.InterruptHint.INTERRUPT_HINT_STOP:
  19. // 此分支表示系統(tǒng)已將音頻流停止(永久失去焦點(diǎn)),為保持狀態(tài)一致,應(yīng)用需切換至音頻暫停狀態(tài)
  20. // 永久失去焦點(diǎn):后續(xù)不會再收到任何音頻打斷事件,若想恢復(fù)播放,需要用戶主動觸發(fā)。
  21. isPlay = false; // 此句為簡化處理,代表應(yīng)用切換至音頻暫停狀態(tài)的若干操作
  22. break;
  23. case audio.InterruptHint.INTERRUPT_HINT_DUCK:
  24. // 此分支表示系統(tǒng)已將音頻音量降低(默認(rèn)降到正常音量的20%),為保持狀態(tài)一致,應(yīng)用需切換至降低音量播放狀態(tài)
  25. // 若應(yīng)用不接受降低音量播放,可在此處選擇其他處理方式,如主動暫停等
  26. isDucked = true; // 此句為簡化處理,代表應(yīng)用切換至降低音量播放狀態(tài)的若干操作
  27. break;
  28. case audio.InterruptHint.INTERRUPT_HINT_UNDUCK:
  29. // 此分支表示系統(tǒng)已將音頻音量恢復(fù)正常,為保持狀態(tài)一致,應(yīng)用需切換至正常音量播放狀態(tài)
  30. isDucked = false; // 此句為簡化處理,代表應(yīng)用切換至正常音量播放狀態(tài)的若干操作
  31. break;
  32. default:
  33. break;
  34. }
  35. } else if (interruptEvent.forceType === audio.InterruptForceType.INTERRUPT_SHARE) {
  36. // 共享打斷類型(INTERRUPT_SHARE):應(yīng)用可自主選擇執(zhí)行相關(guān)操作或忽略音頻打斷事件
  37. switch (interruptEvent.hintType) {
  38. case audio.InterruptHint.INTERRUPT_HINT_RESUME:
  39. // 此分支表示臨時失去焦點(diǎn)后被暫停的音頻流此時可以繼續(xù)播放,建議應(yīng)用繼續(xù)播放,切換至音頻播放狀態(tài)
  40. // 若應(yīng)用此時不想繼續(xù)播放,可以忽略此音頻打斷事件,不進(jìn)行處理即可
  41. // 繼續(xù)播放,此處主動執(zhí)行start(),以標(biāo)識符變量started記錄start()的執(zhí)行結(jié)果
  42. await audioRenderer.start().then(async function () {
  43. started = true; // start()執(zhí)行成功
  44. }).catch((err) => {
  45. started = false; // start()執(zhí)行失敗
  46. });
  47. // 若start()執(zhí)行成功,則切換至音頻播放狀態(tài)
  48. if (started) {
  49. isPlay = true; // 此句為簡化處理,代表應(yīng)用切換至音頻播放狀態(tài)的若干操作
  50. } else {
  51. // 音頻繼續(xù)播放執(zhí)行失敗
  52. }
  53. break;
  54. default:
  55. break;
  56. }
  57. }
  58. });
  59. }
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號