AR+ 實時音視頻通話,虛擬與現實無縫結合

今年中旬 Google 在萬衆期待下推出了 ARCore,能將現實與數碼完美無縫地融合在一塊兒,豐富咱們的現實世界。經過它開發者能夠更加快速方便地在 Android 平臺開發 AR 應用,憑藉 AR 技術大量產品能找到新穎的應用場景,甚至開闢出新的一條產品線。java

目前市場上已經有很多基於 AR 技術的產品,例如宜家家居的 IKEA Place 應用提供了新型的在線選購家俬方式,用戶只須要將手機攝像頭擺向想要放置傢俱的角落,接着選取你想要的傢俱,經過簡單的拖拉以及旋轉便可完成佈局,查看這件傢俱是否符合你的心意。android

下圖爲使用 IKEA Place 的示意圖,看起來這張椅子還挺適合的 :)git

IKEA Place 示意圖

那麼假如 AR 與其餘技術進行結合,是否會有更激動人心的應用場景呢?github

七牛實時音視頻雲 (如下簡稱七牛 RTN)基於已被普遍標準化的 WebRTC 技術棧,有全平臺的兼容性,支持各大瀏覽器如 Chrome、Safari、Firefox 以及各端 Android、iOS、Windows 等。強大的七牛實時音視頻流媒體網絡在全球有 180 多個數據中心,具備強大的鏈路加速功能,豐富的節點保證了不管客戶分佈在全球的什麼地區均可以得到加速。平均 200ms 的超低延時,爲諸多對實時性有苛刻要求的客戶場景提供最根本支持,如一對一語聊、聊天室、視頻會議、在線教育等對交互性有強需求的場景均十分適合使用七牛 RTN。算法

在本篇中,咱們會結合 Google 官方的示例 hello_ar_java 將 AR 技術融入到實時音視頻通話,其中會應用到 1.1.0+ 版本七牛 RTN SDK 的新功能 「外部音視頻數據導入」。shell

如下爲效果動圖api

article_20181115_02

準備工做0: 集成七牛 RTN SDK 到 AR Demo

在真正開始編碼前,咱們須要先將相應的項目和環境搭建完成數組

下載 七牛 RTN SDK 到當前目錄 QNRTC-Android

git clone git@github.com:pili-engineering/QNRTC-Android.git
複製代碼

下載 ARCore 到當前目錄 arcore-android-sdk

git clone git@github.com:google-ar/arcore-android-sdk.git
複製代碼

拷貝相應七牛 RTN SDK 文件到 hello_ar_java 工程中

  1. 將文件 QNRTC-Android/releases/qndroid-rtc-1.2.0.jar 拷貝到 arcore-android-sdk/samples/hello_ar_java/app/libs/ 中(libs 目錄須要自行建立)
  2. QNRTC-Android/releases/ 下的 armeabi、armeabi-v7a、arm64-v8a、x86 等 4 個文件夾拷貝到 arcore-android-sdk/samples/hello_ar_java/app/src/main/jniLibs 文件夾中(jniLibs 目錄須要自行建立)
  3. 使用 AndroidStudio 打開 arcore-android-sdk/samples/hello_ar_java 工程,修改其中幾項配置
    • 爲了讓工程引用上面兩步中添加的庫,打開 app/build.gradle 文件,在 dependencies 中增長行 implementation fileTree(include: ['*.jar'], dir: 'libs')
    • 爲了能進行實時通話,須要設置程序使用網絡的權限,打開 AndroidManifest.xml 文件,在 manifest 標籤中增長如下使用權限聲明
      • <uses-permission android:name="android.permission.INTERNET"/>
      • <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

核心類介紹

在實際編碼與代碼分析前,咱們先簡單概述介紹其中會涉及到的核心類瀏覽器

QNRTCManager:七牛 RTN SDK 核心類,提供低延時實時音視頻通話能力markdown

Session:ARCore 核心類,管理 AR 系統狀態包括攝像頭 Camera 採集、點網監測、平面檢測等能力

GLSurfaceView & Renderer:Android 系統提供的視圖類與渲染類,分別提供負責畫面顯示與渲染

BackgroundRenderer & ObjectRenderer & PlaneRenderer & PointCloudRenderer: Demo 中提供的渲染類,分別負責如下部分的渲染

  • 背景圖渲染(攝像頭預覽原始圖)
  • 物體及其陰影渲染(Android 模型及其陰影)
  • 平面渲染(AR 系統檢測到的平面)
  • 點雲渲染(AR 系統檢測到的點雲)

準備工做1: 創建基本的實時音視頻通話環境

首先須要實現實時音視頻的房間事件監聽器 QNRoomEventListener,其須要實現的方法不少,如下只展示此次簡單示例須要用到的方法,完整的接口說明在這裏

public class HelloArActivity extends AppCompatActivity implements GLSurfaceView.Renderer, QNRoomEventListener {
    private boolean mPublished = false; // 標識本地是否發佈成功
    
    ...
        
    @Override
    public void onJoinedRoom() {
    	mRTCManager.publish(); // 加入房間成功後,嘗試發佈
    }

    @Override
    public void onLocalPublished() {
    	mPublished = true; // 發佈成功後,標識爲 true
    }
    
	...
}
複製代碼

onCreate 方法尾部初始化實時音視頻通話環境並加入指定房間,其中關於 Room Token 獲取的方式能夠參考這裏

protected void onCreate(Bundle savedInstanceState) {
    ...
    QNRTCSetting setting = new QNRTCSetting();
    setting.setExternalVideoInputEnabled(true); // 開啓外部視頻導入功能
    
    mRTCManager.setRoomEventListener(this); // 設置房間事件監聽器
    mRTCManager.initialize(this, setting); // 七牛 RTN SDK 初始化
    
    mRTCManager.joinRoom(###Your Room Token###); // 經過 Room Token 加入指定房間
}
複製代碼

準備工做2: 創建基本的 AR 環境

利用 GLSurfaceView & Renderer 爲繪製 AR 畫面作好準備

在 Activity 類聲明中實現 GLSurfaceView.Renderer 接口,在本 Demo 中以下,隨即須要咱們實現 3 個相應的方法,意義分別在註釋中被描述

public class HelloArActivity extends AppCompatActivity implements GLSurfaceView.Renderer, QNRoomEventListener {
    /** * 顯示 Surface 建立完成時回調 **/
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    }
  
    ...
        
   /** * 顯示 Surface 尺寸大小改變時回調 **/
    public void onSurfaceChanged(GL10 gl, int width, int height) {
    }
    
    ...
    
    /** * 顯示 Surface 建立完成時回調 **/
    public void onDrawFrame(GL10 gl) {
    }
}
複製代碼

在實現了 Renderer 渲染類後,咱們須要提供用做顯示的 Surface,以便讓 Renderer 在其上進行渲染顯示,GLSurfaceView 就有這種能力。

如下示例代碼,從佈局 xml 文件中解析出 GLSurfaceView 並設置 Renderer

surfaceView = findViewById(R.id.surfaceview); // 從佈局 xml 中解析 GLSurfaceView
...
surfaceView.setRenderer(this); // 設置 Renderer
複製代碼

建立 Session

Session 是 AR 系統的主入口類,在任何 AR 操做前必須先初始化並啓動

protected void onResume() {
    session = new Session(/* context= */ this); // AR 系統初始化
    ...
    session.resume(); // 開始 AR 會話,嘗試開啓攝像頭,如攝像頭被佔用,會拋出 CameraNotAvailableException 異常
}
複製代碼

使用 OpenGL Shader 在顯示 Surface 上繪製 AR 加強畫面

在 AR 會話開始後,攝像頭的每一幀數據都能提供如下信息

  • 原始攝像頭預覽數據
  • 檢測到的平面數組
  • 檢測到的點雲數組
  • 平面觸摸事件

咱們能夠在 onDrawFrame 方法中利用以上的事件進行相應的處理,例如遇到平面觸摸事件,則在相應的位置放上一個 Android 模型,而且同時繪製出檢測到的平面以及點雲。

// 繪製背景
private final BackgroundRenderer backgroundRenderer = new BackgroundRenderer();
// 繪製物體
private final ObjectRenderer virtualObject = new ObjectRenderer();
// 繪製物體陰影
private final ObjectRenderer virtualObjectShadow = new ObjectRenderer();
// 繪製平面
private final PlaneRenderer planeRenderer = new PlaneRenderer();
// 繪製雲點
private final PointCloudRenderer pointCloudRenderer = new PointCloudRenderer();

public void onDrawFrame(GL10 gl) {
    frame = session.update(); // 獲取攝像頭原始數據幀(阻塞方法)
    
    // Handle one tap per frame.
    handleTap(frame, camera); // 檢測是否有平面點擊事件,若有則在相應位置放置 Android 模型
    
    ...
    // Draw background.
    backgroundRenderer.draw(frame); // 將攝像頭預覽數據做爲背景圖繪製
    
    ...
    // Visualize tracked points.
    PointCloud pointCloud = frame.acquirePointCloud();
    pointCloudRenderer.update(pointCloud);
    pointCloudRenderer.draw(viewmtx, projmtx); // 繪製點雲
    
    ...
    // Visualize planes.
    planeRenderer.drawPlanes(session.getAllTrackables(Plane.class), camera.getDisplayOrientedPose(), projmtx); // 繪製平面
    
    ...
    // Update and draw the model and its shadow.
    virtualObject.updateModelMatrix(anchorMatrix, scaleFactor);
    virtualObjectShadow.updateModelMatrix(anchorMatrix, scaleFactor);
    virtualObject.draw(viewmtx, projmtx, colorCorrectionRgba, coloredAnchor.color); // 繪製 Android 模型
    virtualObjectShadow.draw(viewmtx, projmtx, colorCorrectionRgba, coloredAnchor.color); // 繪製 Android 模型的陰影
}
複製代碼

##技術結合: 將 AR 加強畫面發佈到實時音視頻雲

在分別實現了基本的 實時音視頻通話AR 加強畫面 後,如今只須要將它們進行最後的結合。

由於 Session 啓動後會佔用設備攝像頭,所以七牛 RTN SDK 沒法進行採集,這時候咱們須要使用最新版本提供的功能 」外部音視頻數據導入「。

在發佈流前,咱們須要獲取到 AR 加強畫面 的 NV21 格式數據,由於當前七牛 RTN Android SDK 的 「外部視頻數據導入」 功能只支持 NV21 格式的數據。

如下示例代碼在 onDrawFrame 方法中的尾部添加,將 GLSurfaceView 的 Surface 內容數據讀取出來,進行必要的格式轉換,接着發佈出去

public void onDrawFrame(GL10 gl) {
	...

    if (mPublished) { // 只在七牛 RTN 發佈流成功後才導入 AR 數據
        // 將 AR 加強畫面 的數據從 GPU 中讀取出來
        GLES20.glReadPixels(0, 0, mSurfaceWidth, mSurfaceHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mBufferRGBA);

        // RGBA 轉爲 NV21(篇幅緣由,不在此展開算法)
        mBufferNV21 = RGBAToNV21(mBufferRGBA, mSurfaceWidth, mSurfaceHeight);

        // 經過 "外部視頻數據導入" 功能將 NV21 數據形式的 AR 加強畫面 發佈出去
        mRTCManager.inputVideoFrame(mBufferNV21, mSurfaceWidth, mSurfaceHeight, 0, frame.getTimestamp());
    }
}
複製代碼

總結

使用 1.1.0+ 版本七牛 RTN SDK 提供的 「外部音視頻數據導入」 功能,能夠輕鬆地把 AR 與實時音視頻通訊結合起來。以上程序基於七牛 RTN SDK 以及相應的 RTN 網絡運行,最大能夠支持 20 人同時低延時音視頻通話。相信不久未來 AR 技術與實時音視頻通訊的結合會帶來更多的應用場景。

免費時長額度贈送活動

七牛實時音視頻雲從 10 月 30 日 起,實行每個月免費時長額度贈送活動。純音頻、標清、高清、超清 4 檔位分別贈送 5000 分鐘,若是徹底使用,折算總消費金額共 770 元。

相關文章
相關標籤/搜索