您好,登錄后才能下訂單哦!
小編給大家分享一下怎么在IOS上使用ReplayKit與RTC,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
直播場景下的屏幕分享,不僅要將當(dāng)前顯示器所展示的畫面分享給遠(yuǎn)端,也要將聲音傳輸出去,包括應(yīng)用的聲音,以及主播的聲音。鑒于這兩點(diǎn)需求,我們可以簡單分析出,進(jìn)行一次屏幕分享的直播所需要的媒體流如下:
一條顯示器畫面的視頻流
一條應(yīng)用聲音的音頻流
一條主播聲音的音頻流
ReplayKit 是蘋果提供的用于 iOS 系統(tǒng)進(jìn)行屏幕錄制的框架。
首先我們來看看蘋果提供的用于屏幕錄制的 ReplayKit 的數(shù)據(jù)回調(diào)接口:
override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) { DispatchQueue.main.async { switch sampleBufferType { case .video: AgoraUploader.sendVideoBuffer(sampleBuffer) case .audioApp: AgoraUploader.sendAudioAppBuffer(sampleBuffer) case .audioMic: AgoraUploader.sendAudioMicBuffer(sampleBuffer) @unknown default: break } } }
從枚舉 sampleBufferType 上,我們不難看出,剛好能符合我們上述對媒體流的需求。
guard let videoFrame = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } let type = CVPixelBufferGetPixelFormatType(videoFrame)
type = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
通過 CVPixelBufferGetPixelFormatType,我們可以獲取到每幀的視頻格式為 yuv420。
通過打印接口的回調(diào)次數(shù),可以知道每秒能夠獲取的視頻幀為30次,也就是幀率為 30。
格式與幀率都能符合 Agora RTC 所能接收的范圍,所以通過 Agora RTC 的 pushExternalVideoFrame 就可以將視頻分享到遠(yuǎn)端了。
agoraKit.pushExternalVideoFrame(frame)
顯示器所顯示的幀來自于一個幀緩存區(qū),一般常見的為雙緩存或三緩存。當(dāng)屏幕顯示完一幀后,發(fā)出一個垂直同步信號(V-Sync),告訴幀緩存區(qū)切換到下一幀的緩存上,然后顯示器開始讀取新的一幀數(shù)據(jù)做顯示。
這個幀緩存區(qū)是系統(tǒng)級別的,一般的開發(fā)者是無法讀取跟寫入的。但是如果是蘋果自身提供的錄制框架 ReplayKit 能夠直接讀取到已經(jīng)渲染好且將用于顯示器的幀,且這一過程不會影響渲染流程而造成掉幀,那就能減少一次用于提供給 ReplayKit 回調(diào)數(shù)據(jù)的渲染過程。
音頻
ReplayKit 能提供的音頻有兩種,分為麥克風(fēng)錄制進(jìn)來的音頻流,與當(dāng)前響應(yīng)的應(yīng)用播放的音頻流。(下文將前者稱為 AudioMic,后者為 AudioApp)
可以通過下面的兩行代碼,來獲取音頻格式
CMAudioFormatDescriptionRef format = CMSampleBufferGetFormatDescription(sampleBuffer); const AudioStreamBasicDescription *description = CMAudioFormatDescriptionGetStreamBasicDescription(format);
AudioApp 會在不同的機(jī)型下有不一樣的聲道數(shù)。例如在 iPad 或 iPhone7 以下機(jī)型中,不具備雙聲道播放的設(shè)備,這時候 AudioApp 的數(shù)據(jù)就是單聲道,反之則是雙聲道。
采樣率在部分試過的機(jī)型里,都是 44100,但不排除在未測試過的機(jī)型會是其他的采樣率。
AudioMic 在測試過的機(jī)型里,采樣率為 32000,聲道數(shù)為單聲道。
如果我們將 AudioApp 與 AudioMic 作為兩條音頻流去發(fā)送,那么流量肯定是大于一條音頻流的。我們?yōu)榱斯?jié)省一條音頻流的流量,就需要將這兩條音頻流做混音(融合)。
但是通過上述,我們不難看出,兩條音頻流的格式是不一樣的,而且不能保證隨著機(jī)型的不同,是不是會出現(xiàn)其他的格式。在測試的過程中還發(fā)現(xiàn) OS 版本的不同,每次回調(diào)給到的音頻數(shù)據(jù)長度也會出現(xiàn)變化。那么我們在對兩條音頻流做混音前,就需要進(jìn)行格式統(tǒng)一,來應(yīng)對 ReplayKit 給出的各種格式。所以我們采取了以下幾個重要的步驟:
if (channels == 1) { int16_t* intData = (int16_t*)dataPointer; int16_t newBuffer[totalSamples * 2]; for (int i = 0; i < totalSamples; i++) { newBuffer[2 * i] = intData[i]; newBuffer[2 * i + 1] = intData[i]; } totalSamples *= 2; memcpy(dataPointer, newBuffer, sizeof(int16_t) * totalSamples); totalBytes *= 2; channels = 2; }
無論是 AudioMic 還是 AudioApp,只要進(jìn)來的流為單聲道,我們都將它轉(zhuǎn)化為雙聲道;
if (sampleRate != resampleRate) { int inDataSamplesPer10ms = sampleRate / 100; int outDataSamplesPer10ms = (int)resampleRate / 100; int16_t* intData = (int16_t*)dataPointer; switch (type) { case AudioTypeApp: totalSamples = resampleApp(intData, dataPointerSize, totalSamples, inDataSamplesPer10ms, outDataSamplesPer10ms, channels, sampleRate, (int)resampleRate); break; case AudioTypeMic: totalSamples = resampleMic(intData, dataPointerSize, totalSamples, inDataSamplesPer10ms, outDataSamplesPer10ms, channels, sampleRate, (int)resampleRate); break; } totalBytes = totalSamples * sizeof(int16_t); }
無論是 AudioMic 還是 AudioApp,只要進(jìn)來的流采樣率不為 48000,我們將它們重采樣為 48000;
memcpy(appAudio + appAudioIndex, dataPointer, totalBytes); appAudioIndex += totalSamples;
memcpy(micAudio + micAudioIndex, dataPointer, totalBytes); micAudioIndex += totalSamples;
通過第一步與第二步,我們保證了兩條音頻流都為同樣的音頻格式。但是由于 ReplayKit 是一次回調(diào)給到一種數(shù)據(jù)的,所以在混音前我們還得用兩個緩存區(qū)來存儲這兩條流數(shù)據(jù);
int64_t mixIndex = appAudioIndex > micAudioIndex ? micAudioIndex : appAudioIndex; int16_t pushBuffer[appAudioIndex]; memcpy(pushBuffer, appAudio, appAudioIndex * sizeof(int16_t)); for (int i = 0; i < mixIndex; i ++) { pushBuffer[i] = (appAudio[i] + micAudio[i]) / 2; }
ReplayKit 有選項是否開啟麥克風(fēng)錄制,所以在關(guān)閉麥克風(fēng)錄制的時候,我們就只有一條 AudioApp 音頻流。所以我們以這條流為主,去讀取 AudioMic 緩存區(qū)的數(shù)據(jù)長度,然后對比兩個緩存區(qū)的數(shù)據(jù)長度,以最小的數(shù)據(jù)長度為我們的混音長度。將混音長度的兩個緩存區(qū)里的數(shù)據(jù)做融合,得到混音后的數(shù)據(jù),寫入一個新的混音緩存區(qū)(或者直接寫入 AudioApp 緩存區(qū));
[AgoraAudioProcessing pushAudioFrame:(*unsigned* *char* *)pushBuffer withFrameSize:appAudioIndex * *sizeof*(int16_t)];
最后我們再將這段混音后的數(shù)據(jù)拷貝進(jìn) Agora RTC 的 C++ 錄制回調(diào)接口里,這時候就可以把麥克風(fēng)錄制的聲音與應(yīng)用播放的聲音傳輸?shù)竭h(yuǎn)端了。
通過對音視頻流的處理,結(jié)合 Agora RTC SDK,我們就完成了一個屏幕分享直播場景的實(shí)現(xiàn)了。
以上是“怎么在IOS上使用ReplayKit與RTC”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。