AR+ 實時音視訊通話,虛擬與現實無縫結合
今年中旬 Google 在萬眾期待下推出了 ARCore,能將現實與數碼完美無縫地融合在一起,豐富我們的現實世界。通過它開發者可以更加快速方便地在 Android 平臺開發 AR 應用,憑藉 AR 技術大量產品能找到新穎的應用場景,甚至開闢出新的一條產品線。
目前市場上已經有不少基於 AR 技術的產品,例如宜家家居的 IKEA Place 應用提供了新型的線上選購家俬方式,使用者只需要將手機攝像頭擺向想要放置傢俱的角落,接著選取你想要的傢俱,通過簡單的拖拉以及旋轉即可完成佈局,檢視這件傢俱是否符合你的心意。
下圖為使用 IKEA Place 的示意圖,看起來這張椅子還挺適合的 :)
那麼假如 AR 與其他技術進行結合,是否會有更激動人心的應用場景呢?
七牛實時音視訊雲 (以下簡稱七牛 RTN)基於已被廣泛標準化的 WebRTC 技術棧,有全平臺的相容性,支援各大瀏覽器如 Chrome、Safari、Firefox 以及各端 Android、iOS、Windows 等。強大的七牛實時音視訊流媒體網路在全球有 180 多個數據中心,具有強大的鏈路加速功能,豐富的節點保證了無論客戶分佈在全球的什麼地區都可以獲得加速。平均 200ms 的超低延時,為諸多對實時性有苛刻要求的客戶場景提供最根本支援,如一對一語聊、聊天室、視訊會議、線上教育等對互動性有強需求的場景均十分適合使用七牛 RTN。
在本篇中,我們會結合 Google 官方的示例 hello_ar_java
以下為效果動圖
準備工作0: 整合七牛 RTN SDK 到 AR Demo
在真正開始編碼前,我們需要先將相應的專案和環境搭建完成
下載 七牛 RTN SDK 到當前目錄 QNRTC-Android
git clone [email protected]:pili-engineering/QNRTC-Android.git
下載 ARCore 到當前目錄 arcore-android-sdk
git clone [email protected] :google-ar/arcore-android-sdk.git
拷貝相應七牛 RTN SDK 檔案到 hello_ar_java 工程中
- 將檔案
QNRTC-Android/releases/qndroid-rtc-1.2.0.jar
拷貝到arcore-android-sdk/samples/hello_ar_java/app/libs/
中(libs 目錄需要自行建立) - 將
QNRTC-Android/releases/
下的armeabi、armeabi-v7a、arm64-v8a、x86
等 4 個資料夾拷貝到arcore-android-sdk/samples/hello_ar_java/app/src/main/jniLibs
資料夾中(jniLibs 目錄需要自行建立) - 使用 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 核心類,提供低延時實時音視訊通話能力
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 元。