Qcon 演講紀(jì)實(shí):詳解如何在實(shí)時(shí)視頻通話中實(shí)現(xiàn)AR功能

2018年4月20日-22日,由 infoQ 主辦的 Qcon 2018全球軟件開(kāi)發(fā)大會(huì)在北京如期舉行。聲網(wǎng)首席 iOS 研發(fā)工程師,iOS 端移動(dòng)應(yīng)用產(chǎn)品設(shè)計(jì)和技術(shù)架構(gòu)負(fù)責(zé)人龔宇華,受邀分享了《基于 ARkit 和 ARcore,在實(shí)時(shí)視頻通話中實(shí)現(xiàn) AR 功能》,在演講中剖析了 AR 與 VR 差異,ARKit 的工作原理,以及逐步講解如何基于 ARKit 與聲網(wǎng)Agora SDK 創(chuàng)建 AR 視頻會(huì)議場(chǎng)景。

image

以下為演講精華摘錄:

首先,龔宇華簡(jiǎn)要分析了 AR 與 VR 的差別是什么,“VR 是將人置身于一個(gè)完全虛擬的場(chǎng)景中,它發(fā)展到極致就會(huì)像《黑客帝國(guó)》一樣,你所看到的一切都是虛擬的;AR 則是在現(xiàn)實(shí)場(chǎng)景中增加虛擬元素,讓兩者結(jié)合,就像《鋼鐵俠》電影中的頭盔一樣,在可以查看周圍環(huán)境的同時(shí),顯示虛擬元素”。不只是在電影中,現(xiàn)實(shí)中也有很多AR的應(yīng)用案例,比如曾火爆一時(shí)的游戲 Pocketmon go。

ARKit 如何實(shí)現(xiàn) AR 場(chǎng)景?

在演講中,龔宇華為大家演示了如何實(shí)現(xiàn)一個(gè) AR 視頻會(huì)議場(chǎng)景,實(shí)現(xiàn)效果會(huì)與我們?cè)陔娪啊锻跖铺毓ぁ分兴吹降膱?chǎng)景類似(如下圖),特工戴上眼鏡之后,世界另一端的特工的虛擬形象就與他坐在同一張桌旁。

image

“就像我們說(shuō)把大象放進(jìn)冰箱里三步驟,我們要打開(kāi)冰箱,把大象放進(jìn)去,關(guān)上冰箱。那我們實(shí)現(xiàn) AR 的虛擬會(huì)議室也是分三步驟,就是實(shí)現(xiàn) AR、實(shí)現(xiàn)視頻會(huì)議,然后把它兩個(gè)結(jié)合起來(lái)?!饼徲钊A表示。最終我們會(huì)實(shí)現(xiàn)下圖中的Demo效果。

image

目前,蘋果和 Google 分別推出了 ARKit 和 ARCore,為我們?cè)谝苿?dòng)端實(shí)現(xiàn) AR 場(chǎng)景降低了門檻。實(shí)現(xiàn)AR的原理是怎樣的呢?我們以 ARKit為例,來(lái)看下它是如何工作的。如剛剛所說(shuō),AR 就是在實(shí)際環(huán)境中植入虛擬元素,那么首先要識(shí)別出周圍環(huán)境,也就是通過(guò) iPhone 的攝像頭來(lái)實(shí)現(xiàn)。在手機(jī)加速計(jì)、陀螺儀的幫助下,ARKit 可以識(shí)別3D 的環(huán)境并判斷手機(jī)在環(huán)境中的姿態(tài)。

通過(guò)以上實(shí)現(xiàn)過(guò)程,我們很容易理解 ARKit 的限制,比如:

  • 光線差:沒(méi)有足夠的光或光線過(guò)強(qiáng)的鏡面反光。嘗試避免這些光線差的環(huán)境。

  • 缺少紋理:如果攝像頭指向一面白墻,那也沒(méi)法獲得特征,ARKit 也去無(wú)法找到并追蹤用戶。嘗試避免看向純色、反光表面等地方。

  • 快速移動(dòng):通常情況下檢測(cè)和估算 3D 姿態(tài)只會(huì)借助圖片,如果攝像頭移動(dòng)太快圖片就會(huì)糊,從而導(dǎo)致追蹤失敗。但 ARKit 會(huì)利用視覺(jué)慣性里程計(jì),綜合圖片信息和設(shè)備運(yùn)動(dòng)傳感器來(lái)估計(jì)用戶轉(zhuǎn)向的位置。因此 ARKit 在追蹤方面非常強(qiáng)大。

在完成環(huán)境識(shí)別之后,還需要渲染,通常我們會(huì)想到使用 OpenGL 或 Metal 渲染,但是它們的開(kāi)發(fā)成本比較高,所以蘋果想了一辦法,就是通過(guò) SceneKit 進(jìn)行渲染。

我們可以通過(guò)以下幾行代碼實(shí)現(xiàn) AR,也就是“將大象關(guān)進(jìn)冰箱”的第一步。

 @IBOutlet weak var sceneView: ARSCNView!
 override func viewDidAppear(_ animated: Bool) {
     super.viewDidAppear(animated)
     guard ARWorldTrackingConfiguration.isSupported else {
         return
     }
     let configuration = ARWorldTrackingConfiguration()
     configuration.planeDetection = .horizontal
     sceneView.session.run(configuration)
}

實(shí)現(xiàn)視頻通話功能

我們可以通過(guò)聲網(wǎng)Agora SDK 來(lái)快速實(shí)現(xiàn)視頻通話。在這樣的視頻通話場(chǎng)景中,聲網(wǎng)Agora SDK 具備幾個(gè)優(yōu)勢(shì):

低延時(shí):聲網(wǎng)SDK 實(shí)時(shí)通訊網(wǎng)絡(luò),可實(shí)現(xiàn)全球百毫秒級(jí)音視頻通話;

快速集成:開(kāi)發(fā)者最快可在30分鐘內(nèi)完成集成;

全球化部署:支持全球200多個(gè)國(guó)家與地區(qū)。

在下載最新版聲網(wǎng)Agora SDK 后,將其添加到我們的 AR Demo 中。通過(guò)以下代碼,可以基于 Agora 實(shí)現(xiàn)視頻會(huì)議。

 // 初始化引擎
 let agoraKit = AgoraRtcEngineKit.sharedEngine(withAppId: myAppId,
                                               delegate: self)
 // 設(shè)置為發(fā)送端
 agoraKit.setChannelProfile(.liveBroadcasting)
 agoraKit.setClientRole(.broadcaster)
 agoraKit.enableVideo()
 // 加入會(huì)議
 agoraKit.joinChannel(byToken: nil,
                     channelId: “QCon2018”,
                     info: nil,
                     uid: 0,
                     joinSuccess: nil)

將視頻通話融入AR場(chǎng)景

在完成視頻會(huì)議的搭建之后,我們還需要傳輸本地視頻與音頻。

image
let videoSource = ARVideoSource()
agoraKit.setVideoSource(videoSource) 
image
func session(_ session: ARSession, didUpdate frame: ARFrame) {
    videoSource.sendBuffer(frame.capturedImage, timestamp: frame.timestamp)
}

1agoraKit.enableExternalAudioSource(withSampleRate: 44100, channelsPerFrame: 1)
1func session(_ session: ARSession, didOutputAudioSampleBuffer audioSampleBuffer: CMSampleBuffer) {
2 agoraKit.pushExternalAudioFrameSampleBuffer(audioSampleBuffer)
3}
最后,當(dāng)我們通過(guò)SDK獲得了其它用戶發(fā)來(lái)的音視頻數(shù)據(jù)后,我們還需要將其渲染到AR環(huán)境中,實(shí)現(xiàn)方式如下。

image
 class ARVideoRenderer : NSObject, AgoraVideoSinkProtocol {
     var renderNode: SCNNode!
     func bufferType() -> AgoraVideoBufferType { return .rawData }
     func pixelFormat() -> AgoraVideoPixelFormat { return .I420 }
     func renderRawData(_ rawData: UnsafeMutableRawPointer,
                        size: CGSize,
                        rotation: AgoraVideoRotation) {
         let rgbTexture = createTexture(widthYUV: rawData, size: size, rotation: rotation)
         renderNode.geometry?.firstMaterial?.diffuse.contents = rgbTexture
    }
    func shouldInitialize() -> Bool { return setupMetal() }
    func shouldStart() { }
    func shouldStop() { }
    func shouldDispose() { }
}

我們?cè)谥?a target="_blank" rel="nofollow">《基于ARKit與Agora SDK實(shí)現(xiàn)AR視頻會(huì)議》文章中分享了詳細(xì)的實(shí)現(xiàn)步驟,大家也可以訪問(wèn)聲網(wǎng)Agora的Githu查看源碼。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容