在本文中,我们将向您展示如何在15分钟内将视频呼叫集成到iOS应用中。您不必从头开始实现整个WebRTC堆栈;您可以只使用现成的SDK。
这是结果的样子:
您将实现业务逻辑和界面。视频呼叫函数将使用GCoreVideOcallssdk集成,GCoreVideOcallssdk是一个GCORE框架,它可以照顾与插座和WEBRTC进行创建和交互,连接/创建视频呼叫室并与服务器进行交互。
>注意:本文是有关与iOS合作的系列的一部分。在其他文章中,我们向您展示了如何create a mobile streaming app on iOS,以及如何将add VOD uploading和smooth scrolling VOD介绍给现有应用程序。
您可以在本指南的帮助下添加什么功能
解决方案包括以下内容:
- 带有相机和麦克风的视频通话。
- 使用您的设计显示对话参与者。
- 将视频流从内置相机发送到服务器。
- 从服务器接收视频流。
解决方案体系结构
这是视频调用的解决方案体系结构的样子:
如何将视频通话功能集成到您的应用中
步骤1:准备
应用程序必须连接到房间,并在屏幕上显示用户和扬声器。为此,我们将使用UICollectionView和UicollectionViewCel来显示参与者,以及Uiview以显示用户。 WEBRTC提供RTCEAGLVideoView
来在应用程序中显示视频流。我们还将创建一个小型模型来存储数据并将其链接起来。 SDK将是UiviewController。
最终应用程序将看起来像这样:
首先,您需要安装所有依赖项,并要求用户录制视频/麦克风的权利。
依赖性
通过在podfile中指定以下内容:
source 'https://github.com/G-Core/ios-video-calls-SDK.git'
...
pod "mediasoup_ios_client", '1.5.3'
pod "GCoreVideoCallsSDK", '2.6.0'
权限
要允许应用程序访问相机和麦克风,请在项目信息中指定权限:
-
NSMicrophoneUsageDescription
(隐私麦克风用法描述) -
NSCameraUsageDescription
(隐私摄像机用法描述)
步骤2:创建UI
模型
需要一个模型来存储任何数据,在这种情况下是用户数据。
创建一个模型文件,并将gcorevideocallssdk导入其中。要存储用户数据,请创建将包含用户的ID和名称以及分配给其的RTCEAGLVIDEOVIEW的VideoCallunit结构。这是整个文件的外观:
import GCoreVideoCallsSDK
final class Model {
var localUser: VideoCallUnit?
var remoteUsers: [VideoCallUnit] = []
}
struct VideoCallUnit {
let peerID: String
let name: String
let view = RTCEAGLVideoView()
}
CollectionCell
CollectionCell将用于显示扬声器。
细胞被重复使用,可以在其一生中显示大量不同的扬声器。为此,我们需要一种机制,该机制将从屏幕上删除上一个线程,将新线程拉到屏幕上,然后在屏幕上设置其位置。要实现该机制,请创建一个CollectionCell类并从UicollectionViewCell设置继承。此类仅包含一个属性: rtcview 。
import GCoreVideoCallsSDK
final class CollectionCell: UICollectionViewCell {
weak var rtcView: RTCEAGLVideoView? {
didSet {
oldValue?.removeFromSuperview()
guard let rtcView = rtcView else { return }
rtcView.frame = self.bounds
addSubview(rtcView)
}
}
}
ViewController
设置控制器来管理整个过程:
导入gcorevideocallssdk。
import GCoreVideoCallsSDK
创建模型属性。
let model = Model()
创建属性牢房以容纳单元格ID,以及懒惰的属性收集视图来管理单元格(懒惰使用CellID,View和Self)。设置集合布局,将控制器分配为 dataSource ,然后注册单元格。
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.itemSize.width = UIScreen.main.bounds.width - 100
layout.itemSize.height = layout.itemSize.width
layout.minimumInteritemSpacing = 10
let collection = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collection.backgroundColor = .white
collection.dataSource = self
collection.register(CollectionCell.self, forCellWithReuseIdentifier: cellID)
return collection
}()
创建LocalView属性。该视图的布局需要从代码中制定;为此,创建方法initconstraints。
let localView: UIView = {
let view = UIView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
view.layer.cornerRadius = 10
view.backgroundColor = .black
view.clipsToBounds = true
return view
}()
func initConstraints() {
NSLayoutConstraint.activate([
localView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width / 3),
localView.heightAnchor.constraint(equalTo: localView.widthAnchor, multiplier: 4/3),
localView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 5),
localView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -5)
])
}
创建gcmeet属性。
var gcMeet = gcMeet.shared
在 viewDidload 方法中
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(collectonView)
view.addSubview(localView)
initConstraints()
})
创建一个扩展程序,将控制器订阅到UicollectionViewDataSource。这是为了将UI与模型链接并在出现时设置单元格是必要的。
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// Get remote peers count
return model.remoteUsers.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath) as! CollectionCell
cell.rtcView = model.remoteUsers[indexPath.row].view
cell.layer.cornerRadius = 10
cell.backgroundColor = .black
cell.clipsToBounds = true
return cell
}
}
UI现在准备显示呼叫。现在我们需要与SDK建立关系。
初始化gcoremeet
通过 gcoremeet.shared 建立了与服务器的连接。本地用户,房间和相机的参数将传递给它。您还需要调用该方法激活音频会话,这将使您可以捕获连接的耳机。 SDK通过听众将数据从服务器传递到应用程序: roomListener 和 em> epreteratorListener 。。
将所有这些添加到ViewDidload方法:
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(collectonView)
view.addSubview(localView)
let userParams = GCoreLocalUserParams(name: "EvgenMob", role: .moderator)
let roomParams = GCoreRoomParams(id: "serv1z3snbnoq")
gcMeet.connectionParams = (userParams, roomParams)
gcMeet.audioSessionActivate()
gcMeet.moderatorListener = self
gcMeet.roomListener = self
try? gcMeet.startConnection()
initConstraints()
}
可以通过单击创建一个免费的按钮并选择会议的空间来从https://meet.gcore.com获取 Roomid 。
在室内列表方法中,您还将收到视频流(您自己和远程用户),音频流以及有关用户和主持人操作的信息,这将使您能够呈现必要的UI。这将在文章后面进一步讨论。
通过主持人路标,您将收到其他用户的请求以启用流,并在新用户加入候诊室时通知。 MeetRoomParameters
也可以使用以下参数:
-
clientHostName
:您可以离开此零,在这种情况下,默认值将被满足.gcorelabs.com -
peerId
:如果您离开此零,ID将由SDK自动生成。
步骤3:与GCORE服务器进行交互
与服务器的互动是通过订阅室列表协议的对象进行的。它有多种方法。您可以在SDK readme上找到有关这些以及有关SDK的更多详细信息。下面是这种互动过程的图像:
对于最简单的实现,您只需要几种方法。
首先,将控制器订阅室列表协议:
extension ViewController: RoomListener {
}
将所需方法添加到扩展。
加入房间并发送与房间权限,参与者列表以及有关本地用户的信息有关的第一种方法。
func roomClientHandle(_ client: GCoreRoomClient, forAllRoles joinData: GCoreJoinData)
我们对用户列表感兴趣:
// To get data at the moment of entering the room
func roomClientHandle(_ client: GCoreRoomClient, forAllRoles joinData: GCoreJoinData) {
switch joinData {
case othersInRoom(remoteUsers: [GCoreRemoteUser]):
remoteUsers.forEach {
model.remoteUsers += [ .init(peerID: $0.id, name: $0.displayName ?? "") ]
}
collectonView.reloadData()
default:
break
}
}
当房间的连接状态更改时,下一个方法是调用的。
func roomClientHandle(_ client: GCoreRoomClient, connectionEvent: GCoreRoomConnectionEvent)
设备的摄像头和麦克风成功连接后将打开。
// To update the status of the connection to the room
func roomClientHandle(_ client: GCoreRoomClient, forAllRoles joinData: GCoreJoinData) {
switch joinData {
case othersInRoom(remoteUsers: [GCoreRemoteUser]):
remoteUsers.forEach {
model.remoteUsers += [ .init(peerID: $0.id, name: $0.displayName ?? "") ]
}
collectonView.reloadData()
default:
break
}
}
对于与远程用户数据有关的事件,下一个方法被调用,但与媒体无关。
func roomClientHandle(_ client: GCoreRoomClient, remoteUsersEvent: GCoreRemoteUsersEvent)
您将使用它在通话过程中添加和删除用户。
// To respond to actions related to remote users, not related to video/audio streams
func roomClientHandle(_ client: GCoreRoomClient, remoteUsersEvent: GCoreRemoteUsersEvent) {
switch remoteUsersEvent {
case handleRemote(user: GCoreRemoteUser):
model.remoteUsers += [.init(peerID: handlePeer.id, name: handlePeer.displayName ?? "")]
collectonView.reloadData()
case closedRemote(userId: String):
model.remoteUsers.removeAll(where: { $0.peerID == peerClosed })
collectonView.reloadData()
default:
break
}
}
当SDK准备提供用户的视频流以及同行的视频到达时,最后一个方法是调用的。
func roomClientHandle(_ client: GCoreRoomClient, mediaEvent: GCoreMediaEvent)
您将使用它来渲染UI。
// To respond to receiving and disabling video/audio streams
func roomClientHandle(_ client: GCoreRoomClient, mediaEvent: GCoreMediaEvent) {
switch mediaEvent {
case produceLocalVideo(track: RTCVideoTrack):
guard let localUser = model.localUser else { return }
videoTrack.add(localUser.view)
localUser.view.frame = self.localView.bounds
localView.addSubview(localUser.view)
case handledRemoteVideo(videoObject: GCoreVideoStream):
guard let user = model.remoteUsers.first(where: { $0.peerID == videoObject.peerId }) else { return }
videoObject.rtcVideoTrack.add(user.view)
default:
break
}
}
令 xcode 填充其余方法而无需提供功能。
设置已完成!该项目准备在真实设备上运行(不建议在模拟器上运行)。
结果
您的应用程序现在具有视频通话功能。这是我们的demo application中此功能的实现。
开发人员注意
您可以从SDK中获得PixelBuffer,其中包含一个图像框架链接(从相机接收),因此您可以对此做任何喜欢的事情。为此,您需要在MediaCapturerBufferDelegate
下订阅控制器并实现mediaCapturerDidBuffer
方法。下面的示例在将框架发送到服务器之前添加了模糊:
extension ViewController: MediaCapturerBufferDelegate {
func mediaCapturerDidBuffer(_ pixelBuffer: CVPixelBuffer) {
let image = CIImage(cvPixelBuffer: pixelBuffer).applyingGaussianBlur(sigma: 10)
CIContext().render(image, to: pixelBuffer)
}
}
结论
使用SDK和GCORE服务,您可以轻松,快速地将视频通话功能集成到应用程序中。用户会很高兴;他们曾经在Instagram,WhatsApp和Facebook等流行服务中进行视频通话,现在他们将在您的应用程序中看到一个熟悉的功能。
您可以在此处查看我们项目的源代码:ios-demo-video-calls。在那里,您还可以窥视其他方法,主持人模式,屏幕预览等。
Mediasoup iOS Client用于在iOS上实现WEBRTC。
GcoreVideoCallsSDK用于与房间连接和互动,以及创建插座。
本文基于GcoreVideoCalls应用程序。