Voxeet iOS SDK
Voxeet SDK 是一个 Swift 库,允许用户
- 创建/加入会议
- 改变每个会议用户的音频角度和方向
- 向其他参与者广播信息
- 发送和接收视频流
- 录制和回放会议
- 使用 CallKit 接收调用
目录
需求
- iOS 9.0+
- Xcode 9.0+
- Swift 4.0+ / Objective-C
示例应用
一个示例应用程序可在本 GitHub 仓库 找到。您还可以使用现成的 UI:VoxeetConferenceKit,链接为 此处(其中包含此 SDK)。
安装 iOS SDK
您需要在 Xcode 目标设置中禁用 位代码:“构建设置” -> “启用位代码” -> 不启用
启用 后台模式(进入您的目标设置 -> “能力” -> “后台模式”)
- 开启“音频、AirPlay 和画中画”
- 开启“VoIP”(Xcode 9 bug 缺失)
如果您想通过 VoIP 推送通知支持 CallKit(当应用程序被终止时接收来电),启用“推送通知”(您需要将您的 VoIP 推送证书 发送给 Voxeet)。
隐私 权限,在 Info.plist 中添加两个新键值
- 隐私 - 麦克风使用说明
- 隐私 - 摄像头使用说明
CocoaPods 安装
CocoaPods是针对 Cocoa 项目的依赖管理器。您可以使用以下命令安装:
$ sudo gem install cocoapods --pre
要在 Xcode 项目中使用 CocoaPods 集成 VoxeetSDK,在您的 Podfile
中指定它
use_frameworks!
target "YourTarget" do
pod 'VoxeetSDK', '~> 1.0'
end
然后,运行以下命令
$ pod install
Carthage 安装
Carthage是一个去中心化的依赖管理器,负责构建您的依赖并提供二进制框架。
您可以使用 Homebrew 通过以下命令安装 Carthage:
$ brew update
$ brew install carthage
要在 Xcode 项目中使用 Carthage 集成 VoxeetSDK,在您的 Cartfile
中指定它
github "voxeet/voxeet-ios-sdk" ~> 1.0
运行 carthage update
以构建框架,并将构建的 VoxeetSDK.framework
拖动到您的 Xcode 项目中(需要放入 ‘嵌入式二进制文件’ 和‘链接框架和库’)。更多信息请参考 https://github.com/Carthage/Carthage#if-you're-building-for-ios-tvos-or-watchos。
手动操作
检查此仓库,并在VoxeetSDK文件夹中找到VoxeetSDK.framework
。将框架拖放到您的项目中,并将其添加到主目标中的'嵌入式二进制文件'和'链接框架和库'部分。
SDK 初始化
在您的应用程序的AppDelegate.swift文件中初始化SDK
import VoxeetSDK
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Initialization of the Voxeet SDK.
VoxeetSDK.shared.initialize(consumerKey: "consumerKey", consumerSecret: "consumerSecret")
return true
}
}
SDK 使用
初始化
VoxeetSDK.shared.initialize(consumerKey: "consumerKey", consumerSecret: "consumerSecret")
// With all parameters.
VoxeetSDK.shared.initialize(consumerKey: "consumerKey", consumerSecret: "consumerSecret", userInfo: nil, callKit: true, connectSession: false)
如果您使用外部登录如O365、LDAP或自定义登录来检索联系信息,还可以添加您的联系人ID,包含显示名称、头像URL和任何其他额外信息。这允许您要求访客用户介绍自己并提供他们的显示名称,以及对于您的企业中的经过身份验证的用户或客户,可以通过O365(姓名、部门等)检索的ID。
VoxeetSDK.shared.initialize(consumerKey: "consumerKey", consumerSecret: "consumerSecret", userInfo: ["externalId": "1234", "externalName": "User", "externalPhotoUrl": "http://voxeet.com/voxeet-logo.jpg"])
手动连接会话
连接会话就像登录一样。但是,您需要使用connectSession
设置为false来初始化SDK
VoxeetSDK.shared.initialize(consumerKey: "consumerKey", consumerSecret: "consumerSecret", connectSession: false)
通过传递用户ID,它将使用您的ID将您的用户登录到我们的服务器(您还可以通过可选的userInfo
参数传递一些额外信息)。此方法如果想要实现CallKit(VoIP推送通知)可能很有用,因为一旦会话连接,您就可以接收通知。
let userInfo = ["externalName": "User", "externalPhotoUrl": "http://voxeet.com/voxeet-logo.jpg"]
VoxeetSDK.shared.session.connect(userID: "userID", userInfo: userInfo, completion: { (error) in
})
如果会话未使用上述方法连接,则加入会议时将自动建立连接。
手动断开会话
断开会话就像是注销,它会停止socket连接,并停止发送VoIP推送通知。
VoxeetSDK.shared.session.disconnect(completion: { (error) in
})
创建演示会议
轻松与机器人测试会议。
VoxeetSDK.shared.conference.demo { (error) in
}
创建会议
手动创建会议(隐式创建一个方法)。
VoxeetSDK.shared.conference.create(success: { (json) in
guard let confID = json?["conferenceId"] as? String,
let confAlias = json?["conferenceAlias"] as? String else {
return
}
}, fail: { (error) in
})
创建会议时也可以传递一些可选参数,例如 conferenceAlias
(示例:["conferenceAlias": "myCustomConferenceAlias", "conferenceType": "standard", "metadata": ...])。这些参数特定于会议(不要与用户的userInfo混淆)。
VoxeetSDK.shared.conference.create(parameters: ["conferenceAlias": "myCustomConferenceAlias", "conferenceType": "standard", "metadata": ...], success: { (json) in
}, fail: { (error) in
})
加入会议
如果你加入了一个不存在的会议,它将自动创建一个。基本上,如果你不需要自定义首选项,可以使用join方法代替上面的create方法。
VoxeetSDK.shared.conference.join(conferenceID: conferenceID, success: { (json) in
}, fail: { (error) in
})
还有一些可选参数也同样存在
- video 参数:默认启动自己的视频。
- userInfo 参数:通过这个字典,你可以传递与你的用户相关联的附加信息。例如,如果用户处于“监听模式”,可以添加:
["participantType": "listener"]
。其他示例:["externalName": "User", "externalPhotoUrl": "http://voxeet.com/voxeet-logo.jpg", ...] 如果SDK没有初始化userInfo或session尚未连接。
VoxeetSDK.shared.conference.join(conferenceID: conferenceID, video: true, userInfo: ["participantType": "listener"], success: { (json) in
}, fail: { (error) in
})
离开会议
VoxeetSDK.shared.conference.leave { (error) in
}
获取会议ID / 别名
获取当前会议标识符(id
是会议的内部Voxeet标识符,而 alias
是您的自定义ID)。如果没有任何正在进行中的会议,则两者都是空的。
let confID = VoxeetSDK.shared.conference.id
let confAlias = VoxeetSDK.shared.conference.alias
在会议中发送广播消息
VoxeetSDK.shared.conference.broadcast(message: "message", completion: { (error) in
})
要接收广播消息,您可以使用VTConferenceDelegate中的'messageReceived'方法(见下文:可用委托/回调
部分)。
获取特定会议状态
VoxeetSDK.shared.conference.status(conferenceID: conferenceID, success: { (json) in
}, fail: { (error) in
})
订阅会议以获取其状态通知
此方法的目的是在通知中获得所有关于会议状态(参与者添加/删除、会议销毁等)的更新,而不加入会议。
在调用此方法之前,必须先创建会议。
通知名称:VTConferenceStatusUpdated
init() {
NotificationCenter.default.addObserver(self, selector: #selector(conferenceStatusUpdated), name: .VTConferenceStatusUpdated, object: nil)
// Subscribe.
VoxeetSDK.shared.conference.statusSubscribe(conferenceID: conferenceID, completion: { (error) in
})
// Unsubscribe.
VoxeetSDK.shared.conference.statusUnsubscribe(conferenceID: conferenceID, completion: { (error) in
})
}
@objc func conferenceStatusUpdated(_ notification: Notification) {
//
}
获取特定会议的历史记录
VoxeetSDK.shared.conference.history(conferenceID: conferenceID, success: { (json) in
}, fail: { (error) in
})
获取自己的会议历史记录
您可以从每页中检索 0 到 40 个事件。使用返回的 JSON 中的 lastMeetingId
& lastTimestamp
来处理分页。
VoxeetSDK.shared.conference.histories(nbEvents: 10, lastConferenceID: nil, lastConferenceTimestamp: nil, success: { (json) in
}, fail: { (error) in
})
邀请用户
在会议中邀请一些用户。如果启用了 CallKit 和推送通知,它将在被邀请用户的设备上以 Apple 来电用户界面响起。
VoxeetSDK.shared.conference.invite(conferenceID: confID, ids: ["userID", ...], completion: { (error) in
})
拒绝通话
拒绝通话(会议用户将收到 VTParticipantUpdated 通知)。
VoxeetSDK.shared.conference.decline(conferenceID: confID, completion: { (error) in
})
订阅/取消订阅邀请模拟
// Subscribe.
VoxeetSDK.shared.conference.subscribe(conferenceAlias: conferenceAlias, success: { (json) in
}, fail: { (error) in
})
// Unsubscribe.
VoxeetSDK.shared.conference.unsubscribe(conferenceAlias: conferenceAlias, success: { (json) in
}, fail: { (error) in
})
开始/停止当前会议的录音
要录音,您需要处于会议中。
// Start recording.
VoxeetSDK.shared.conference.startRecording(conferenceID: conferenceID, completion: { (error) in
})
// Stop recording.
VoxeetSDK.shared.conference.stopRecording(conferenceID: conferenceID, completion: { (error) in
})
回放录音会议
此方法类似于 demo
或 join
方法,它将像普通会议一样自动开始录制的会议。
VoxeetSDK.shared.conference.replay(conferenceID: conferenceID, completion: { (error) in
})
您可以通过传递一个额外的参数来在 x 毫秒(偏移量)后开始回放。
// Replay a conference without the first second.
VoxeetSDK.shared.conference.replay(conferenceID: conferenceID, offset: 1000, completion: { (error) in
})
加入黑白名单
将外部 ID 加入或从黑白名单中移除,不再接收此用户的的通知。
VoxeetSDK.shared.blacklist(externalID: "1234", ban: true, completion: { (error) in
})
获取用户
返回 VTUser 对象。
let ownUser = VoxeetSDK.shared.session.user
let user = VoxeetSDK.shared.conference.user(userID: "userID")
获取当前会议用户
返回包含当前用户 ID 和信息的 VTUser 对象数组。
let users = VoxeetSDK.shared.conference.users
更改用户位置
使用此方法可以通过 TrueVoice 技术对用户的音效进行空间化(仅限本地)。
// Values for angle and distance are between: angle = [-1, 1], distance = [0, 1]
VoxeetSDK.shared.conference.userPosition(userID: "userID", angle: 0, distance: 0)
静音/取消静音用户
VoxeetSDK.shared.conference.mute(userID: "userID", isMuted: true)
let isMuted = VoxeetSDK.shared.conference.toggleMute(userID: "userID")
获取用户的语音级别
let voiceLevel = VoxeetSDK.shared.conference.voiceLevel(userID: "userID")
在内部扬声器与内部接收器之间切换
VoxeetSDK.shared.conference.switchDeviceSpeaker()
// You can also force the BuiltInSpeaker (true) / BuildInReceiver (false)
VoxeetSDK.shared.conference.switchDeviceSpeaker(forceBuiltInSpeaker: true)
您还可以通过在初始化 SDK 之前使用此方法来启用或禁用默认的内置扬声器。
VoxeetSDK.shared.defaultBuiltInSpeaker = true / false
翻转设备摄像头(前/后)
VoxeetSDK.shared.conference.flipCamera()
将媒体流连接到渲染器
可以使用继承自 VideoRenderer
的 UIView 创建渲染器。
VoxeetSDK.shared.conference.attachMediaStream(stream, renderer: videoRenderer)
从渲染器断开媒体流
如果从 UI 中移除了视频渲染器,就不需要调用此方法。此方法在切换同一视频渲染器上的多个流时可能很有用。
VoxeetSDK.shared.conference.unattachMediaStream(stream, renderer: videoRenderer)
启动/停止特定用户的视频流
可以使用用户 ID “静音/取消静音” 用户的视频(然而不能强制启动或停止除了您之外的特定用户的视频)。
let ownUserID = VoxeetSDK.shared.session.user!.id!
// Start own video.
VoxeetSDK.shared.conference.startVideo(userID: ownUserID, completion: { (error) in
})
// Stop own video.
VoxeetSDK.shared.conference.stopVideo(userID: ownUserID, completion: { (error) in
})
CallKit
CallKit 默认是 禁用 的,您可以在初始化时启用它。
VoxeetSDK.shared.initialize(consumerKey: "consumerKey", consumerSecret: "consumerSecret", callKit: true)
使用 CallKit 可以接收一些有趣的通知以更新 UI:VTCallKitStarted
、VTCallKitUpdated
和 VTCallKitEnded
。
为了处理 iOS 10 以下版本的 VoIP 推送通知,需要这个 AppDelegate 扩展。
/*
* MARK: - Voxeet VoIP push notifications
*/
extension AppDelegate {
/// Useful bellow iOS 10.
func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
VoxeetSDK.shared.application(application, didReceive: notification)
}
/// Useful bellow iOS 10.
func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, completionHandler: @escaping () -> Void) {
VoxeetSDK.shared.application(application, handleActionWithIdentifier: identifier, for: notification, completionHandler: completionHandler)
}
}
可用的委托/回调
会话代理人
class myClass: VTSessionDelegate {
init() {
// Session delegate.
VoxeetSDK.shared.session.delegate = self
}
func sessionUpdated(state: VTSessionState) {
}
}
或
VoxeetSDK.shared.session.updated = { (state) in
}
会议代理人
class myClass: VTConferenceDelegate {
init() {
// Conference delegate.
VoxeetSDK.shared.conference.delegate = self
}
func participantAdded(userID: String, userInfo: [String: Any], stream: MediaStream) {
}
func participantUpdated(userID: String, userInfo: [String: Any], stream: MediaStream) {
}
func participantRemoved(userID: String, userInfo: [String: Any]) {
}
func messageReceived(userID: String, userInfo: [String: Any], message: String) {
}
func screenShareStarted(userID: String, stream: MediaStream) {
}
func screenShareStopped(userID: String) {
}
}
或
VoxeetSDK.shared.conference.participantAdded = { (userID, userInfo, stream) in
}
VoxeetSDK.shared.conference.participantUpdated = { (userID, userInfo, stream) in
}
VoxeetSDK.shared.conference.participantRemoved = { (userID, userInfo) in
}
VoxeetSDK.shared.conference.messageReceived = { (userID, userInfo, message) in
}
VoxeetSDK.shared.conference.screenShareStarted = { (userID, stream) in
}
VoxeetSDK.shared.conference.screenShareStopped = { (userID) in
}
可用枚举
会话状态枚举
enum VTSessionState {
case connecting
case connected
case reconnecting
case disconnected
}
会议状态枚举
enum VTConferenceState {
case connecting
case connected
case disconnecting
case disconnected
}
通知处理器
下面是一个处理Voxeet SDK推送的通知的示例(每次推送通知时,都会在一个日志中显示其名称)
[VoxeetSDK] Voxeet notification: \(NotificationName) (you can register to this notification to handle it)
init() {
NotificationCenter.default.addObserver(self, selector: #selector(myFunc), name: Notification.Name("NotificationName"), object: nil)
}
@objc func myFunc(notification: Notification) {
// Get JSON.
guard let userInfo = notification.userInfo?.values.first as? Data else {
return
}
let json = try? JSONSerialization.jsonObject(with: userInfo, options: .mutableContainers)
}
Voxeet的通知可以轻松处理,例如:Notification.Name.VTConferenceStatusUpdated。这个扩展不是详尽的。
extension Notification.Name {
public static let VTOwnConferenceCreatedEvent = Notification.Name("OwnConferenceCreatedEvent")
public static let VTConferenceStatusUpdated = Notification.Name("ConferenceStatusUpdated")
public static let VTConferenceDestroyedPush = Notification.Name("ConferenceDestroyedPush")
public static let VTConferenceMessageReceived = Notification.Name("ConferenceMessageReceived")
public static let VTOwnUserInvitedEvent = Notification.Name("OwnUserInvitedEvent")
public static let VTInvitationReceivedEvent = Notification.Name("InvitationReceivedEvent")
public static let VTOfferCreated = Notification.Name("OfferCreated")
public static let VTParticipantAdded = Notification.Name("ParticipantAdded")
public static let VTParticipantUpdated = Notification.Name("ParticipantUpdated")
public static let VTParticipantSwitched = Notification.Name("ParticipantSwitched")
public static let VTCallKitStarted = Notification.Name("VTCallKitStarted")
public static let VTCallKitUpdated = Notification.Name("VTCallKitUpdated")
public static let VTCallKitEnded = Notification.Name("VTCallKitEnded")
}
VTAudioSound使用
VTAudioSound可以帮助你在你的应用程序中播放3D音频声音。
声音必须以 单声道 编码才能播放空间化。
初始化
var sound: VTAudioSound!
// Initializes VTAudioSound.
sound = try? VTAudioSound(forResource: "myFile", ofType: "mp3")
sound = try? VTAudioSound(fileURL: path)
播放声音
try? sound.play() {
// Debug.
print("The sound has finished being played.")
}
停止播放
sound.stop()
在当前声音上循环
sound.loop = true
设置/获取音量
// The range of valid values are from 0.0 to 1.0.
sound.volume = 1
let volume = sound.volume
设置/获取角度
// The range of valid values are from -1.0 to 1.0.
sound.angle = 0
let angle = sound.angle
设置/获取距离
// The range of valid values are from 0.0 to 1.0.
sound.distance = 0
let distance = sound.distance
使用Voxeet SDK发布您的应用程序
该SDK旨在编译与模拟器和通用iOS设备。自2016年11月起,苹果已停止支持此类“肥库”(模拟器和设备一起编译)。因此,当您要发布应用程序时,您需要执行一个脚本来删除模拟器支持
APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"
# This script loops through the frameworks embedded in the application and
# removes unused architectures.
find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK
do
FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)
FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"
echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"
EXTRACTED_ARCHS=()
for ARCH in $ARCHS
do
echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME"
lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"
EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")
done
echo "Merging extracted architectures: ${ARCHS}"
lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"
rm "${EXTRACTED_ARCHS[@]}"
echo "Replacing original executable with thinned version"
rm "$FRAMEWORK_EXECUTABLE_PATH"
mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"
done
您可以将此脚本添加到项目的构建阶段
中。如果您想了解更多详细信息,请看这篇教程:http://ikennd.ac/blog/2015/02/stripping-unwanted-architectures-from-dynamic-libraries-in-xcode/
版本
1.1.2
技术
Voxeet iOS SDK使用了一些开源项目以确保其正常工作
- Starscream - Starscream是Swift在iOS和OSX中符合WebSocket(RFC 6455)客户端库。
- Alamofire - Alamofire是用Swift编写的HTTP网络库。
- SwiftyJSON - SwiftyJSON简化了在Swift中处理JSON数据。
- CryptoSwift - CryptoSwift使用Swift实现了与Swift相关的函数助手。
© Voxeet,2018