增强现实视频会议





0/5 (0投票)
在本文中,我们将 ARKit 集成到视频会议场景中。
去年在 WWDC 2017 上,苹果推出了 ARKit。利用这项技术,开发者可以快速创建 iOS 平台上的混合现实应用程序,并使用设备的摄像头让增强现实变为现实。
在本文中,我们将 ARKit 集成到视频会议场景中。本文描述了视频中两个场景的实现
- 将 ARKit 与实时视频流集成
- 使用 Agora 的视频 SDK 将实时视频流渲染到 AR 平面上
我们将使用 ARKit 检测房间中的平面,然后使用 Agora.io Video SDK v2.1.1 中包含的 Custom Video Source 和 Renderer 功能,将实时视频流渲染到该平面上。这将为视频通话带来全息感,就像你在《星球大战》中看到的那样!此演示的源代码包含在文章的最后。只需将你的 Agora.io App ID 添加到 ViewController.swift 文件中,然后在你的设备上运行该应用!
基本 AR 准备
首先,我们将使用 ARKit 创建一个简单的平面感知应用程序作为开发基础。使用 Augmented Reality App 模板在 Xcode 中创建一个新项目,并选择 SceneKit 作为 Content Technology。
开始平面检测
在 ViewController 中将 ARConfiguration
设置为平面检测。
override func viewDidLoad() { super.viewDidLoad() sceneView.delegate = self sceneView.session.delegate = self sceneView.showsStatistics = true } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) let configuration = ARWorldTrackingConfiguration() configuration.planeDetection = .horizontal sceneView.session.run(configuration) }
显示已识别的平面
要在已识别的平面上添加红色背景,请实现 ARSCNViewDelegate
回调方法 renderer:didAddNode:forAnchor
。
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { guard let planeAnchor = anchor as? ARPlaneAnchor else { return } let plane = SCNBox(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.y), length: CGFloat(planeAnchor.extent.z), chamferRadius: 0) plane.firstMaterial?.diffuse.contents = UIColor.red let planeNode = SCNNode(geometry: plane) node.addChildNode(planeNode) planeNode.runAction(SCNAction.fadeOut(duration: 1)) }
您现在已经完成了一个非常简单的 AR 应用程序。当环境中的平面被识别时,会在其上添加一个红色矩形并逐渐消失。
交互式广播准备
现在,我们将使用 Agora SDK 为应用添加实时视频通话功能。从官方网站下载 最新的 SDK 包 并将其添加到 Xcode 项目中。接下来,在 View Controller 中创建一个 AgoraRtcEngineKit
实例,并添加以下与实时视频相关的设置。
let agoraKit: AgoraRtcEngineKit = { let engine = AgoraRtcEngineKit.sharedEngine(withAppId:<#YourAppId#>, delegate: nil) engine.setChannelProfile(.liveBroadcasting) engine.setClientRole(.broadcaster) engine.enableVideo() return engine }()
最后,在 viewDidLoad
函数中,将 agoraKit
的委托设置为视图控制器(self),然后加入 Agora 频道。
agoraKit.delegate = self agoraKit.joinChannel(byToken: nil, channelId: "agoraar", info: nil, uid: 0, joinSuccess: nil)
此时,所有准备工作都已完成。我们有了一个可以识别平面并能进行音视频通话的 AR 应用程序。下一步是结合这两个功能。
广播 ARKit 屏幕
由于 ARKit 已经在使用设备摄像头,我们无法启动 AVCaptureSession
进行视频捕获。幸运的是,ARFrame
中的 capturedImage
接口为我们提供了摄像头捕获的图像。
添加自定义视频源
为了传输视频数据,我们需要创建一个类(ARVideoSource
)并实现 AgoraVideoSourceProtocol
,其中 bufferType
应返回 AgoraVideoBufferType
。
class ARVideoSource: NSObject, AgoraVideoSourceProtocol { var consumer: AgoraVideoFrameConsumer? func shouldInitialize() -> Bool { return true } func shouldStart() { } func shouldStop() { } func shouldDispose() { } func bufferType() -> AgoraVideoBufferType { return .pixelBuffer } }
添加一个方法将视频帧传输到 ARVideoSource
类
func sendBuffer(_ buffer: CVPixelBuffer, timestamp: TimeInterval) { let time = CMTime(seconds: timestamp, preferredTimescale: 10000) consumer?.consumePixelBuffer(buffer, withTimestamp: time, rotation: .rotationNone) }
接下来,在 View Controller 中实例化一个 ARVideoSource
,并通过 viewDidLoad()
中的 setVideoSource
接口将实例变量传递给 Agora SDK。
let videoSource = ARVideoSource() override func viewDidLoad() { …… agoraKit.setVideoSource(videoSource) …… }
这使得只要我们调用 videoSource 的 sendBuffer:timestamp
: 方法,就可以将视频帧传递给 Agora SDK。
发送相机数据
我们可以通过 ARSession
回调获取每个 ARFrame
,从中读取相机数据,并使用 videoSource
发送出去。
在 viewDidLoad
方法中,将 ARSession
的委托设置为 View Controller 并添加回调函数。
override func viewDidLoad() { …… sceneView.session.delegate = self …… } extension ViewController: ARSessionDelegate { func session(_ session: ARSession, didUpdate frame: ARFrame) { videoSource.sendBuffer(frame.capturedImage, timestamp: frame.timestamp) } }
发送 ARSCNView 数据
ARFrame 的 capturedImage
方法收集来自摄像机的原始数据。如果我们想发送已经添加了虚拟对象的图片,我们必须获取 ARSCNView
数据。这里有一个简单的想法:设置一个计时器,将 SCNView
转换为 UIImage
,然后将其转换为 CVPixelBuffer
,并提供给 videoSource
。示例逻辑代码如下。
func startCaptureView() { //Timer with 0.1 second interval timer.schedule(deadline: .now(), repeating: .milliseconds(100)) timer.setEventHandler { [unowned self] in // Turn sceneView data into UIImage let sceneImage: UIImage = self.image(ofView: self.sceneView) // Provide to Agora SDK after being converted to CVPixelBuffer self.videoSourceQueue.async { [unowned self] in let buffer: CVPixelBuffer = self.pixelBuffer(ofImage: sceneImage) self.videoSource.sendBuffer(buffer, timestamp: Double(mach_absolute_time())) } } timer.resume() }
将实时流视频渲染到 AR 场景
添加虚拟显示
首先,我们需要创建一个用于渲染远程视频的虚拟显示器,并在用户点击时将其添加到 AR 场景中。
在 Storyboard 中向 ARSCNView
添加一个 UITapGestureRecognizer
。当用户点击屏幕时,通过 ARSCNView
的 hitTest
方法获取平面的位置,并在点击位置放置一个虚拟显示器。
@IBAction func doSceneViewTapped(_ recognizer: UITapGestureRecognizer) { let location = recognizer.location(in: sceneView) guard let result = sceneView.hitTest(location, types: .existingPlane).first else { return } let scene = SCNScene(named: "art.scnassets/displayer.scn")! let rootNode = scene.rootNode rootNode.simdTransform = result.worldTransform sceneView.scene.rootNode.addChildNode(rootNode) let displayer = rootNode.childNode(withName: "displayer", recursively: false)! let screen = displayer.childNode(withName: "screen", recursively: false)! unusedScreenNodes.append(screen) }
用户可以通过点击屏幕添加多个显示屏,它们会暂时保留在 unusedScreenNodes
数组中,直到它们被使用并渲染视频。
添加自定义视频渲染器
为了从 Agora SDK 获取远程视频数据,我们需要构建一个 ARVideoRenderer
对象,它实现 AgoraVideoSinkProtocol
。
class ARVideoRenderer: NSObject { var renderNode: SCNNode? } extension ARVideoRenderer: AgoraVideoSinkProtocol { func shouldInitialize() -> Bool { return true } func shouldStart() { } func shouldStop() { } func shouldDispose() { } func bufferType() -> AgoraVideoBufferType { return .rawData } func pixelFormat() -> AgoraVideoPixelFormat { return .I420 } func renderRawData(_ rawData: UnsafeMutableRawPointer, size: CGSize, rotation: AgoraVideoRotation) { …… } }
remoteRenderData:size:rotation
: 方法可以获取远程视频数据,然后使用 Metal 框架渲染到 SCNNode
。完整的 Metal 渲染代码可以在演示的最终版本中找到。
将自定义渲染器设置为 Agora SDK
通过实现 AgoraRtcEngineDelegate
协议的 rtcEngine:didJoinedOfUid:elapsed
: 回调,可以识别流媒体加入频道的时机/位置。在回调中创建一个 ARVideoRenderer
实例,将虚拟屏幕节点(由用户之前点击屏幕时创建)设置为 ARVideoRenderer
,并通过 setRemoteVideoRenderer:forUserId
: 接口将自定义渲染器设置为 Agora SDK。
func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) { guard !unusedScreenNodes.isEmpty else { return } let screenNode = unusedScreenNodes.removeFirst() let renderer = ARVideoRenderer() renderer.renderNode = screenNode agoraKit.setRemoteVideoRenderer(renderer, forUserId: uid) }
这样,当其他用户加入频道时,他们的视频就会显示在 AR 平面上,并获得虚拟会议室的效果。
通过使用 Agora SDK 的自定义视频源和自定义视频渲染器功能,可以轻松地结合 AR 和实时视频场景。此演示运行在 Agora SDK 上,使用 Agora 软件定义的实时网络,并支持 17 个同步视频流。这清楚地表明,AR 技术将为实时视频流带来全新的体验。
接下来的发展方向
- 在《Pokemon Go》中挑战你的朋友
- 在视频通话中让你的朋友/家人/同事与你更亲近
- 创建一个混合现实健身应用程序,连接教练和他们的客户
完整的源代码,请在此处查看 Github 仓库 here。
如果您有任何疑问,请随时通过我们的 开发者 Slack 频道 联系我们!如果您想加入我们的 Slack 社区,请填写此 表单,我们将发送邀请!