作者简介:王颖,声网Agora.io 媒体引擎高级工程师,负责媒体引擎设计开发和多平台扩展
ARKit 和 ARCore 给我们 AR 应用的开发带来了极大便利。ARCore 在 2 月 24 日发布了 1.0 正式版本,Android Studio Beta 模拟器也开始支持 AR Core,这是 AR 的一个里程碑。
往常,我们看过的大多数 AR 应用或直播应用中,都是以动画、图像、视频形式展现 AR 特效,而且都是在本地处理的视频文件。如果想在视频通话场景中,以 AR 的形式引入另一个实时视频,需要实时获取视频数据,并进行渲染处理。
本篇,我们将会把 ARCore 融合入直播场景中,与大家分享如何在直播应用中加入 AR 元素,以及如何利用最新发布的 Agora SDK 2.1 的自定义视频源、自定义渲染器两大新功能实现更炫的效果。
本文将分为两部分:
基于 ARCore 实现一个 AR 应用的基本功能
通过引入Agora SDK,将直播连麦画面渲染到 AR 场景中
如果你还不了解 ARCore 可以先回顾《ARCore 基础知识》,我们在其中介绍了 ARCore 的基本类,以及实现一个 AR 应用的原理。
我们首先使用ARCore创建一个简单的识别平面的应用做为开发基础。
在Android Studio中创建一个新项目,在 gradle 中添加:
implementation 'com.google.ar:core:1.0.0'
在 Activity 中设置 Config 为平面检测。
@Override
protected void onCreate(Bundle savedInstanceState) {
try {
mSession = new Session(/* context= */ this);
} catch(Exception e) {
}
Config config = new Config(mSession);
mSession.configure(config);
}
@Override
protected void onResume() {
super.onResume();
mSession.resume();
}
实现 onDrawFrame,在识别出的平面上画出平面网格。
@Override
public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
try {
Frame frame = mSession.update();
Camera camera = frame.getCamera();
float[] projmtx = new float[16];
camera.getProjectionMatrix(projmtx, 0, 0.1f, 100.0f);
// Get camera matrix and draw.
float[] viewmtx = new float[16];
camera.getViewMatrix(viewmtx, 0);
mPlaneRenderer.drawPlanes(mSession.getAllTrackables(Plane.class), camera.getDisplayOrientedPose(), projmtx);
} catch(Exception e) {
}
}
这样就完成了一个最简单的AR应用,当识别出环境中的平面时,会在上面画出白色网格的画面
接下来我们需要使用 Agora SDK 在应用中添加直播功能。
首先在官网下载最新的 SDK 包并添加到我们的 Demo 中。接着在 Activity 中添加 RtcEngine 的实例,并且进行直播相关的设置。
mRtcEngine = RtcEngine.create(this, 'appId', mRtcEventHandler);
mRtcEngine.setChannelProfile(Constants.CHANNEL_PROFILE_LIVE_BROADCASTING);
mRtcEngine.enableVideo();
mRtcEngine.enableDualStreamMode(true);
mRtcEngine.setVideoProfile(Constants.VIDEO_PROFILE_480P, false);
mRtcEngine.setClientRole(Constants.CLIENT_ROLE_BROADCASTER);
mRtcEngine.joinChannel(null, "arcore", "ARCore with RtcEngine", 0);
至此,所有的准备工作都已经完成,我们有了一个可以识别平面的 AR 应用,同时又可以进行音视频通话,接下来要做的就是把这两个功能结合起来。
因为 ARCore 已经占用了设备摄像头,我们无法自己启动 RtcEngine引擎 默认的采集方式进行采集。我们可以使用 ARCore 的数据直接使用。
为了发送视频数据,我们需要构造一个实现了 IVideoSource
接口 AgoraVideoSource
。其中 bufferType 返回 MediaIO.BufferType
类型。
public class AgoraVideoSource implements IVideoSource {
private IVideoFrameConsumer mConsumer;
@Override
public boolean onInitialize(IVideoFrameConsumer iVideoFrameConsumer) {
mConsumer = iVideoFrameConsumer;
return true;
}
@Override
public boolean onStart() {
return true;
}
@Override
public void onStop() {
}
@Override
public void onDispose() {
}
@Override
public int getBufferType() {
return MediaIO.BufferType.BYTE_ARRAY.intValue();
}
public IVideoFrameConsumer getConsumer() {
return mConsumer;
}
}
接着在 Activity 中实例化一个 ARVideoSource
, 并在 RtcEngine 初始化之后通过 setVideoSource
接口设置给 Agora SDK
mSource = new AgoraVideoSource();
mRtcEngine.setVideoSource(mSource);
我们可以通过 GL10 的回调拿到每一帧数据,并使用 videoSource 的 consumer 发送出去。
gl.glReadPixels(0, 0, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, mSendBuffer);
mSource.getConsumer().consumeByteArrayFrame(data, MediaIO.PixelFormat.RGBA.intValue(), width, height, 270, System.currentTimeMillis());
我们可以先在 AR 场景中添加一个 Anchor, 接着通过 OpenGL 把连麦对方的视频数据渲染到 Anchor 上。这样即可实现在 AR 环境中显示连麦端的画面。
首先我们需要创建用来渲染远端视频的虚拟显示屏,并通过用户的点击添加到 AR 场景中。
通过 HitResult 创建 Anchor,并在 Anchor 处画出一个虚拟显示屏放在点击的位置上。
HitResult hit;
mAnchors.add(hit.createAnchor());
用户通过点击可以添加多个显示屏,并被存在 Anchor
数组中待用。
为了从 Agora SDK 获取到远端的视频数据,我们需要构造一个实现了 IVideoSink
接口的类型 AgoraVideoRender
。
public class AgoraVideoRender implements IVideoSink {
private Peer mPeer;
private boolean mIsLocal;
public AgoraVideoRender(int uid, boolean local) {
mPeer = new Peer();
mPeer.uid = uid;
mIsLocal = local;
}
public Peer getPeer() {
return mPeer;
}
@Override
public boolean onInitialize() {
return true;
}
@Override
public boolean onStart() {
return true;
}
@Override
public void onStop() {
}
@Override
public void onDispose() {
}
@Override
public int getBufferType() {
return MediaIO.BufferType.BYTE_BUFFER.intValue();
}
@Override
public int getPixelFormat() {
return MediaIO.PixelFormat.RGBA.intValue();
}
@Override
public void consumeByteBufferFrame(ByteBuffer buffer, int format, int width, int height, int rotation, long ts) {
if (!mIsLocal) {
mPeer.data = buffer;
mPeer.width = width;
mPeer.height = height;
mPeer.rotation = rotation;
mPeer.ts = ts;
}
}
}
通过 consumeByteBufferFrame
方法可以拿到远端的视频数据,然后就可以使用 OpenGL 渲染到 Anchor 上。具体的 OpenGL 渲染代码可以参考文末的完整版 Demo.
把实现 IVideoSink
接口的自定义渲染器设置给 Agora SDK。
AgoraVideoRender render = new AgoraVideoRender(uid, false);
mRemoteRenders.add(render);
mRtcEngine.setRemoteVideoRenderer(uid, render);
这样当连麦端加入频道后,就会在虚拟显示屏上显示对方的视频,得到一个虚拟会议室的效果。
使用 Agora SDK 的自定义视频源和自定义视频渲染器接口,可以轻松地把 AR 和直播场景结合起来。Demo 基于 Agora SDK 以及 SD-RTN 运行,可以支持7人的同时视频连麦。可以预见,AR 技术会为实时视频连麦带来全新的体验。
完整 Demo 地址:Agora-Video-With-ARCore