ARCore 1.0 实践:在直播场景中加入AR特效

作者简介:王颖,声网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 场景中

第一部分:基础的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 的画面直播出去

因为 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);

发送AR中的数据

我们可以通过 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 场景中

我们可以先在 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.

将自定义渲染器设置给 Agora SDK

把实现 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

你可能感兴趣的:(技术干货)