电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

討論在線教室 iOS 端聲音問題綜合解決方案

瀏覽:103日期:2022-09-16 17:11:44
背景介紹

在線教室場景下,聲音是最重要的內容傳輸渠道之一,保障聲音的穩定可靠,是在線教室質量非常重要的一環。同時在線教室里許多功能模塊都與聲音有關聯,如何處理好各個模塊間的聲音沖突成為一個重要話題。

AVAudioSession

在 iOS 端,說到聲音的話題就繞不開 AVAudioSession。AVAudioSession 的作用是管理音頻這一唯一硬件資源的分配,通過調優合適的 AVAudioSession 來適配我們的 APP 對于音頻的功能需求。切換音頻場景的時候,需要相應的切換 AVAudioSession。

討論在線教室 iOS 端聲音問題綜合解決方案

 AVAudioSessionCategory

教育場景下主要使用到的音頻場景有:

討論在線教室 iOS 端聲音問題綜合解決方案

AVAudioSessionMode

iOS 提供 AVAudioSessionMode[1] 用于與 AVAudioSessionCategory[2] 搭配使用,教育場景下使用到的音頻模式主要有:

討論在線教室 iOS 端聲音問題綜合解決方案

 AVAudioSessionOptions

我們可以使用 options 去微調 Category 行為,教育場景下常用的有:

討論在線教室 iOS 端聲音問題綜合解決方案

通話音量與媒體音量

一般而言,通話音量指的是進行語音、視頻通話時的音量。媒體音量指的是播放音樂、視頻或游戲的音效、背景音的音量。

在實際使用中,兩者的差異在于,通話音量有較好的回聲消除,媒體音量有較好的聲音表現力。媒體音量可以調整到 0,而通話音量不可以。

通話音量與媒體音量只能二選一,因此需要區分系統音量走的是通話音量還是媒體音量。系統音量走通話音量,是指在設備上調整音量時,調整的是通話音量。媒體音量同理。媒體音量和通話音量分別屬于 2 個不同的、獨立的系統,一個設置不會影響到另外一個。

進入通話后,音效的播放音量由通話音量控制。退出通話后,則由媒體音量控制。一般在教育場景下,學生作為觀眾拉流時,使用的媒體音量,老師說話的聲音更加立體飽滿,當學生連麥時,使用的通話音量,以保證通話聲音的質量。

簡單來說,非連麥模式下會使用媒體音量控制,連麥模式下會使用通話音量控制,兩者有獨立的音量控制機制。

討論在線教室 iOS 端聲音問題綜合解決方案

當播放媒體資源時,使用播放器(如 AVPlayer)播放音頻,播放器底層 AudioUnit 的 description 為 VoiceProcessingIO。

RTC SDK 內部維護了一個 AudioUnit,通話音量下 AudioUnit 的 description 為 RemoteIO,媒體音量下為 VoiceProcessingIO,當出現模式切換時,會銷毀原來的 AudioUnit,再創建新的 AudioUnit,始終保持一個 AudioUnit 來進行音頻播放。

通話音量下,AVPlayer 內 VoiceProcessingIO 的 AudioUnit 聲音會被抑制。同樣的,在媒體音量下,RTC SDK 內的 AudioUnit 的 description 設置為 VoiceProcessingIO,如果此時其他模塊通過設置 AVAudioSession 切換到通話音量,RTC 的聲音也會被抑制。

行業現狀

在線教室場景下,很多功能都需要播放聲音,包括課中音視頻直播、課后回放、webview 內嵌課件聲音(包括音頻、視頻、音效)、課堂音頻、課堂視頻、課堂游戲聲音、音效聲音等。除此之外,教室內還包括很多需要聲音錄制的功能,包括連麥、跟讀、集體發言、聊天語音輸入、語音識別等。

教室內這些功能存在各種組合,且對 AVAudioSession 的設置要求存在差異,而 AVAudioSession 又是一個單例,如果沒有一個統一管理的邏輯,很容易就出現設置混亂的問題。

目前行業內碰到的比較多的問題主要是聽不見 RTC 聲音與媒體聲音被抑制。

聽不見 RTC 聲音

聽不見 RTC 聲音的主要原因是其他功能在設置 AVAudioSession 時,AVAudioSessionOptions 未包含 AVAudioSessionCategoryOptionMixWithOthers 混音模式,導致 RTC 聲音被高優進程打斷。比如在非混音模式下播放 webview 的內嵌音頻,因為 webview 是使用系統進程來播放聲音,優先級最高,所以 APP 進程下的 RTC 聲音就會被抑制導致無法正常發聲。

這類問題一般都比較隱蔽,因為簡單的場景如果有問題,在上線之前一般都能測試出來,而當多個功能場景串起來之后才觸發問題,往往就很難在測試期間發現,且如果線上沒有完備的日志查詢體系,針對線上這類問題排查起來難度也非常大,往往因為定位不到原因而長期遺留。

媒體聲音被抑制

在通話音量模式下,媒體聲音會被壓低,導致聲音變小。比較常見的場景是在小班場景下,學生在推流時播放課堂音視頻等媒體資源,聲音會比 RTC 的聲音要小,導致媒體聲音聽不清楚。

通話模式下(連麥時)媒體聲音會被壓低,原因是 iOS 手機系統會開啟回聲消除以保證人聲體驗,因此會壓低媒體通道的聲音,也會壓低背景音效。

教育行業內部分頭部 APP 也沒有從根本上解決該問題,很多都是通過從產品功能層面上規避問題,通過產品妥協來為技術問題讓步。比如在播放課堂音視頻資源時,默認將所有學生都強制關麥,關麥時學生處于媒體音量,就不存在被壓低的問題了,等到課堂音視頻播放結束后,再允許學生開麥。這種通過規避問題場景來解決問題的方式,不具有可復制性。

RTC 聲音變小

RTC 聲音變小,主要原因是聲音通過聽筒發聲,而沒有正常通過揚聲器發聲,造成聲音變小的假象。另外在 iOS14 系統下,使用過 RTC 的通話模式并切回媒體模式后,再調用 setCategory:PlayAndRecord + DefaultToSpeaker 就會必現聲音小的問題。

解決方案

針對上述行業痛點,通過底層原理的分析與實際項目經驗,從代碼規范、問題兜底、問題報警梳理出一套可行的解決方案。

聽不見 RTC 聲音、RTC 聲音變小

RTC 的聲音問題基本是因為其他模塊功能對 AVAudioSession 進行了更改,且在功能結束之后,也沒有將 AVAudioSession 重置到 RTC 需要的設置。本身音視頻 SDK(如 agora、zego 等)對這種情況會有一定的兜底邏輯,但是這種兜底如果存在侵入性,也是不合理的,因此具有一定的局限性。

AudioSession 修改規范

由于系統無法區分同一個進程中是哪個模塊對 AudioSession 進行了更改,所以為了避免聽不見 RTC 聲音的問題,在使用 RTC 時,其它模塊對 AudioSession 的調用更改,需要遵循以下原則:

模塊調用 setCategory 前先判斷下,當前 AudioSession 如已滿足使用需要,不用再次設置,避免觸發 iOS 14 系統 Bug 模塊需要錄音時,Category 應該使用 PlayAndRecord(為了防止打斷正在播放的音頻,不要使用僅錄音的 CategoryRecord),當前 category 不是 PlayAndRecord 的情況下再調用 setCategory 模塊僅需要播放時,當前 category 為 PlayAndRecord 或 Playback、Ambient 的情況下不需要 setCategory 若當前的 category 不滿足模塊使用,在 setCategory 之前應該先保存當前的 AudioSession 狀態,然后再 setCategory、使用音頻功能,使用結束后,應該重新 setCategory 恢復到之前的 AudioSession 狀態 在設置 audioSession 時,categoryOptions 都應該包含 AVAudioSessionCategoryOptionDefaultToSpeaker 與 AVAudioSessionCategoryOptionMixWithOthers,iOS10 系統及以上還應包含 AVAudioSessionCategoryOptionAllowBluetooth。

核心代碼如下:

//需要錄音時,AudioSession的設置代碼如下:if ([AVAudioSession sharedInstance].category != AVAudioSessionCategoryPlayAndRecord) {            [RTCAudioSessionCacheManager cacheCurrentAudioSession];            AVAudioSessionCategoryOptions categoryOptions = AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionMixWithOthers;            if (@available(iOS 10.0, *)) {                categoryOptions |= AVAudioSessionCategoryOptionAllowBluetooth;            }            [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:categoryOptions error:nil];            [[AVAudioSession sharedInstance] setActive:YES error:nil];} //功能結束時重置audioSession[RTCAudioSessionCacheManager resetToCachedAudioSession];

static AVAudioSessionCategory cachedCategory = nil;static AVAudioSessionCategoryOptions cachedCategoryOptions = nil; @implementation RTCAudioSessionCacheManager //更改audioSession前緩存RTC當下的設置+ (void)cacheCurrentAudioSession {    if (![[AVAudioSession sharedInstance].category isEqualToString:AVAudioSessionCategoryPlayback] && ![[AVAudioSession sharedInstance].category isEqualToString:AVAudioSessionCategoryPlayAndRecord]) {        return;    }    @synchronized (self) {        cachedCategory = [AVAudioSession sharedInstance].category;        cachedCategoryOptions = [AVAudioSession sharedInstance].categoryOptions;    }} //重置到緩存的audioSession設置+ (void)resetToCachedAudioSession {    if (!cachedCategory || !cachedCategoryOptions) {        return;    }    BOOL needResetAudioSession = ![[AVAudioSession sharedInstance].category isEqualToString:cachedCategory] || [AVAudioSession sharedInstance].categoryOptions != cachedCategoryOptions;    if (needResetAudioSession) {        dispatch_async(dispatch_get_global_queue(0, 0), ^{            [[AVAudioSession sharedInstance] setCategory:cachedCategory withOptions:cachedCategoryOptions error:nil];            [[AVAudioSession sharedInstance] setActive:YES error:nil];            @synchronized (self) {                cachedCategory = nil;                cachedCategoryOptions = nil;            }        });    }} @end兜底策略

考慮到在線教室場景的復雜度,讓教室內所有功能代碼都遵循 AVAudioSession 的修改規范,雖然有嚴格的 codeReview,但是也存在一定的人為因素風險,隨著業務功能不斷迭代,無法完全保證線上不出問題,因此一套可靠的兜底策略顯得非常有必要。

兜底策略的基本邏輯是 hook 到 AVAudioSession 的變化,當各模塊對 AVAudioSession 的設置不符合規范要求時,我們在不影響功能的前提下強制進行修正,比如對 options 補充上混音模式。

通過方法交換我們可以 hook 到 AVAudioSession 的更改。比如用 kk_setCategory:withOptions: error: 與系統的 setCategory:withOptions: error: 進行交換,在交換的方法里,我們判斷 options 是否包含 AVAudioSessionCategoryOptionMixWithOthers,如果沒有包含我們就進行追加。

- (BOOL)kk_setCategory:(AVAudioSessionCategory)category withOptions:(AVAudioSessionCategoryOptions)options error:(NSError **)outError {    //在需要進行對audioSession進行修正的場景下(RTC直播),修改options時未包含mixWithOther,則給options追加mixWithOther    BOOL addMixWithOthersEnable = shouldFixAudioSession && !(options & AVAudioSessionCategoryOptionMixWithOthers)];    if (addMixWithOthersEnable) {        return [self kk_setCategory:category withOptions:options | AVAudioSessionCategoryOptionMixWithOthers error:outError];;    }    return [self kk_setCategory:category withOptions:options error:outError];}

但上述方法只對通過調用 setCategory:withOptions: error: 來設置 audioSession 有效,如果調用了 setCategory:error: 來更改 audioSession,則會造成調用死循環的問題。在 iOS 底層實現中,調用 setCategory:error: 時,內部會再調用 setCategory:withOptions: error: 方法,因為進行了方法交換,從而出現嵌套調用問題。

針對該問題,我們通過監聽 AVAudioSessionRouteChangeNotification 通知,來 hookcategory 的變化,AVAudioSessionRouteChangeNotification 在調用 setCategory:error: 時會觸發,而不會在調用 setCategory:withOptions: error: 時直接觸發,進而與上述方法形成了很好的互補。

 //添加對AVAudioSessionRouteChange的監聽[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleRouteChangeNotification:) name:AVAudioSessionRouteChangeNotification object:nil]; - (void)handleRouteChangeNotification:(NSNotification *)notification {  NSNumber* reasonNumber =      notification.userInfo[AVAudioSessionRouteChangeReasonKey];  AVAudioSessionRouteChangeReason reason =      (AVAudioSessionRouteChangeReason)reasonNumber.unsignedIntegerValue;    if (reason == AVAudioSessionRouteChangeReasonCategoryChange) {        AVAudioSessionCategoryOptions currentCategoryOptions = [AVAudioSession sharedInstance].categoryOptions;        AVAudioSessionCategory currentCategory = [AVAudioSession sharedInstance].category;        //在需要進行對audioSession進行修正的場景下(RTC直播),修改category時options未包含mixWithOther,則給options追加mixWithOther        if (shouldFixAudioSession  && !(currentCategoryOptions & AVAudioSessionCategoryOptionMixWithOthers)) {            [[AVAudioSession sharedInstance] setCategory:currentCategory withOptions:currentCategoryOptions | AVAudioSessionCategoryOptionMixWithOthers error:nil];        }    }}報警機制

即使有修改規范與兜底策略的保障,隨著教室業務迭代與 iOS 系統升級,也無法保證線上完全不出問題,因此我們建立了問題報警機制,當線上出現問題時,能在工作群里及時收到警報,根據警報的問題信息,通過日志進一步排查問題。通過報警機制,我們可以更快速的對線上問題作出反應,不被動依賴于學生的投訴反饋,以最快的速度推進問題解決。

當 RTC 聲音被打斷時,底層音視頻 SDK 會回調警告錯誤碼(如 agora 的 warningCode 為 1025),當出現對應的警告碼時,結合 slardar 的報警功能,在飛書群里以消息的形式進行同步。同時在 hook 到 AVAudioSession 的變更時,通過獲取堆棧信息,可以定位到是哪個模塊觸發的更改,結合報警用戶信息,可以更方便的定位問題。

媒體聲音被抑制

媒體聲音在媒體音量下開啟播放,播放途中因為連麥而切換到了通話音量,此時因為系統特性,媒體音量會被通話音量抑制而導致聲音變小。

針對該問題,我們使用音視頻 SDK 提供的混音、混流功能來規避?;驹硎遣シ琶襟w資源時,我們拿到資源的 pcm 音頻數據,將數據拋給 RTC 的 audioUnit 進行混合,由 RTC 音頻播放單元統一播放,如果此時 RTC 使用的是通話音量,則媒體資源也是使用的通話音量播放,反之亦然。以此來保證媒體資源與 RTC 始終保持統一的音量控制機制,而避免聲音大小存在差異。

混音是指給到音頻的本地文件路徑,或者播放的 url,由 SDK 進行數據讀取與播放?;炝魇侵羔槍σ曨l文件,播放器只解碼播放視頻數據,將音頻數據實時拋出來給到 SDK,SDK 將傳入的實時音頻數據與 RTC 音頻數據進行混合與播放。項目中我們使用點播 SDK TTVideoEngine 來實現視頻播放與音頻外拋。

總結

通過上線上述綜合解決方案,聲音問題得到了有效的解決,同時也能從容應對快速迭代的教室需求,有效提升了在線教室的體驗。

到此,這篇關于討論在線教室 iOS 端聲音問題綜合解決方案的文章就介紹到這了,更多相關在線教室IOS端聲音解決方案內容,請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章,希望大家以后多多支持好吧啦網!

標簽: IOS
相關文章:
主站蜘蛛池模板: 低噪声电流前置放大器-SR570电流前置放大器-深圳市嘉士达精密仪器有限公司 | 智能监控-安防监控-监控系统安装-弱电工程公司_成都万全电子 | 江西自考网-江西自学考试网 | 东风体检车厂家_公共卫生体检车_医院体检车_移动体检车-锦沅科贸 | 紧急切断阀_气动切断阀_不锈钢阀门_截止阀_球阀_蝶阀_闸阀-上海上兆阀门制造有限公司 | 不锈钢水箱厂家,不锈钢保温水箱-山东桑特供水设备 | 成都顶呱呱信息技术有限公司-贷款_个人贷款_银行贷款在线申请 - 成都贷款公司 | 北京森语科技有限公司-模型制作专家-展览展示-沙盘模型设计制作-多媒体模型软硬件开发-三维地理信息交互沙盘 | 光照全温振荡器(智能型)-恒隆仪器| 硅胶管挤出机厂家_硅胶挤出机生产线_硅胶条挤出机_臣泽智能装备 贵州科比特-防雷公司厂家提供贵州防雷工程,防雷检测,防雷接地,防雷设备价格,防雷产品报价服务-贵州防雷检测公司 | 企业管理培训,企业培训公开课,企业内训课程,企业培训师 - 名课堂企业管理培训网 | 深圳成考网-深圳成人高考报名网 深圳工程师职称评定条件及流程_深圳职称评审_职称评审-职称网 | 【星耀裂变】_企微SCRM_任务宝_视频号分销裂变_企业微信裂变增长_私域流量_裂变营销 | 船老大板材_浙江船老大全屋定制_船老大官网 | 干粉砂浆设备-干粉砂浆生产线-干混-石膏-保温砂浆设备生产线-腻子粉设备厂家-国恒机械 | 日本细胞免疫疗法_肿瘤免疫治疗_NK细胞疗法 - 免疫密码 | 手板-手板模型-手板厂-手板加工-生产厂家,[东莞创域模型] | 气密性检测仪_气密性检测设备_防水测试仪_密封测试仪-岳信仪器 | 黑龙江「京科脑康」医院-哈尔滨失眠医院_哈尔滨治疗抑郁症医院_哈尔滨精神心理医院 | WTB5光栅尺-JIE WILL磁栅尺-B60数显表-常州中崴机电科技有限公司 | 网架支座@球铰支座@钢结构支座@成品支座厂家@万向滑动支座_桥兴工程橡胶有限公司 | 铝合金脚手架厂家-专注高空作业平台-深圳腾达安全科技 | 合肥触摸一体机_触摸查询机厂家_合肥拼接屏-安徽迅博智能科技 | 仓储货架_南京货架_钢制托盘_仓储笼_隔离网_环球零件盒_诺力液压车_货架-南京一品仓储设备制造公司 | 透平油真空滤油机-变压器油板框滤油机-滤油车-华之源过滤设备 | 安徽合肥格力空调专卖店_格力中央空调_格力空调总经销公司代理-皖格制冷设备 | 万烁建筑设计院-建筑设计公司加盟,设计院加盟分公司,市政设计加盟 | 原子吸收设备-国产分光光度计-光谱分光光度计-上海光谱仪器有限公司 | 进口消泡剂-道康宁消泡剂-陶氏消泡剂-大洋消泡剂 | 连续油炸机,全自动油炸机,花生米油炸机-烟台茂源食品机械制造有限公司 | 美的商用净水器_美的直饮机_一级代理经销商_Midea租赁价格-厂家反渗透滤芯-直饮水批发品牌售后 | 定量包装机,颗粒定量包装机,粉剂定量包装机,背封颗粒包装机,定量灌装机-上海铸衡电子科技有限公司 | 绿萝净除甲醛|深圳除甲醛公司|测甲醛怎么收费|培训机构|电影院|办公室|车内|室内除甲醛案例|原理|方法|价格立马咨询 | 机房监控|动环监控|动力环境监控系统方案产品定制厂家 - 迈世OMARA | 订做不锈钢_不锈钢定做加工厂_不锈钢非标定制-重庆侨峰金属加工厂 | 烟气在线监测系统_烟气在线监测仪_扬尘检测仪_空气质量监测站「山东风途物联网」 | 电液推杆生产厂家|电动推杆|液压推杆-扬州唯升机械有限公司 | 一体化污水处理设备,一体化污水设备厂家-宜兴市福源水处理设备有限公司 | 济南宣传册设计-画册设计_济南莫都品牌设计公司 | 洛阳永磁工业大吊扇研发生产-工厂通风降温解决方案提供商-中实洛阳环境科技有限公司 | 耐酸碱胶管_耐腐蚀软管总成_化学品输送软管_漯河利通液压科技耐油耐磨喷砂软管|耐腐蚀化学软管 |