VoxeetSDK 3.11.0

VoxeetSDK 3.11.0

测试已测试
语言语言 Obj-CObjective C
证书 MIT
发布最后发布2023年8月

VTCocoBenoit SENARDVincent JardelCorentin LarroqueThomas GilbertElise MarieEdyta ZugajYuriy GanushevichiAPIMariusz KrzysiekPiotr Brzeski维护。



VoxeetSDK 3.11.0

  • Voxeet

Voxeet iOS SDK

Voxeet SDK logo

Voxeet SDK 是一个 Swift 库,允许用户

  • 创建/加入会议
  • 改变每个会议用户的音频角度和方向
  • 向其他参与者广播信息
  • 发送和接收视频流
  • 录制和回放会议
  • 使用 CallKit 接收调用

目录

  1. 需求
  2. 示例应用
  3. 安装 iOS SDK
  4. SDK 初始化
  5. SDK 使用
  6. VTAudioSound 使用
  7. 可用的代理/回调函数
  8. 使用 Voxeet SDK 发布您的应用

需求

  • 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)。

Capabilities

隐私 权限,在 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
})

回放录音会议

此方法类似于 demojoin 方法,它将像普通会议一样自动开始录制的会议。

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:VTCallKitStartedVTCallKitUpdatedVTCallKitEnded

为了处理 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