今年中旬 Google 在万众期待下推出了 ARCore https://developers.google.com/ar/ , 能将现实与数码完美无缝地融合在一起, 丰富我们的现实世界. 通过它开发者可以更加快速方便地在 Android 平台开发 AR 应用, 凭借 AR 技术大量产品能找到新颖的应用场景, 甚至开辟出新的一条产品线.
目前市场上已经有不少基于 AR 技术的产品, 例如宜家家居的 IKEA Place 应用提供了新型的在线选购家俬方式, 用户只需要将手机摄像头摆向想要放置家具的角落, 接着选取你想要的家具, 通过简单的拖拉以及旋转即可完成布局, 查看这件家具是否符合你的心意.
下图为使用 IKEA Place 的示意图, 看起来这张椅子还挺适合的 :)
那么假如 AR 与其他技术进行结合, 是否会有更激动人心的应用场景呢?
七牛实时音视频云 https://www.qiniu.com/products/rtn (以下简称七牛 RTN) 基于已被广泛标准化的 webRTC 技术栈, 有全平台的兼容性, 支持各大浏览器如 Chrome,Safari,Firefox 以及各端 Android,iOS,Windows 等. 强大的七牛实时音视频流媒体网络在全球有 180 多个数据中心, 具有强大的链路加速功能, 丰富的节点保证了无论客户分布在全球的什么地区都可以获得加速. 平均 200ms 的超低延时, 为诸多对实时性有苛刻要求的客户场景提供最根本支持, 如一对一语聊, 聊天室, 视频会议, 在线教育等对交互性有强需求的场景均十分适合使用七牛 RTN.
在本篇中, 我们会结合 Google 官方的示例 hello_ar_java https://github.com/google-ar/arcore-android-sdk 将 AR 技术融入到实时音视频通话, 其中会应用到 1.1.0+ 版本七牛 RTN SDK 的新功能 "外部音视频数据导入".
以下为效果动图
准备工作 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 工程中
将文件
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 技术与实时音视频通信的结合会带来更多的应用场景.
来源: http://www.bubuko.com/infodetail-2849618.html