visionOS 音视频SDK集成使用文档1、下载对应的SDK包 rtc_sdk_vision2、添加framework包到visionOS工程中如图3、在使用的文件中导入sdk import rtc_sdk_vision4、音视频开发示例4.1 获取授权4.2 引擎(AVDEngine)模块4.3 房间(AVDRoom)模块4.4 加入房间4.5 视频(AVDVideo)模块4.6 音频(AVDAudio)模块4.7 用户(AVDUserManager)模块4.8 屏幕(AVDScreen)共享模块4.9 电子白板(AVDWhiteboard)模块4.10 聊天(AVDChat)模块4.11 日志(LOG)文件4.12 常见问题
rtc_sdk_vision
import rtc_sdk_vision如需使用 SDK 提供的音视频功能,需要给 App 授权麦克风和摄像头的使用权限。在 App 的 Info.plist 中添加以下两项,分别对应麦克风和摄像头在系统弹出授权对话框时的提示信息。
Privacy - Microphone Usage Description,并填入麦克风使用目的提示语。
Privacy - Camera Usage Description,并填入摄像头使用目的提示语。

本开发示例只涉及到音视频的基本功能,以visionos_test_demo 基础Demo加以说明,其它进阶的功能,如屏幕共享、电子白板、聊天、音视频的导出、呼叫外呼设备等的支持,可以参阅SDK API接口开发或咨询我们的技术支持工程师发送更完整工程的Demo示例.
针对正式客户及测试客户,公司会提供一对有效的Access Key 和Secret Key, 这对密钥可以产生访问令牌(24小时有效),用于生成会议房间号,及加会房间等操作。
目前SDK Demo提供的测试的授权信息及服务器地址如下(ps:如需修改请在demo中Constant文件中修改),公司会不定期的更新对应的key,有问题请咨询技术支持。
struct K { static var url = "https://avd.nice2meet.cn:9610" static let appKey = "demo_access" static let secretKey = "demo_secret" static let urlKey = "urlKey" static let roomId = "roomid"}1、 引擎初始化可以放到AppDeleagte中也可能根据需要再使用音视频时在去初始化引擎,引擎初始化成功之后才能正常操作调用音视频相关代码,示例代码如下
func initEngine(){ var serURL = "" if let serverUrl = UserDefaults.standard.string(forKey: K.urlKey) { serURL = serverUrl }else{ serURL = K.url } AVDEngine.instance().initWithServerUrl(serURL, accessKey: K.appKey, secretKey: K.secretKey, delegate: self) }2、引擎初始化是异步回调,需要再回调接口中根据Result判断成功或者失败
extension JoinMeetingViewController: AVDEngineDelegate{ func onInitResult(_ result: AVDResult) { print("onInitResult = \(result)") var msg = "" if result == AVD_Success { msg = "✅✅✅✅初始化引擎成功✅✅✅✅" }else{ msg = "❌❌❌❌引擎初始化失败❌❌❌❌" } AlertTool().show(msg, in: self.view) } }3、初始化引擎错误码对照表
| 错误码 | 说明 |
|---|---|
| 401 | 传入的密匙Access Key 和Secret Key错误。 |
| 405 | 服务器授权过期,请联系续期。 |
| 436 | AVD SDK版本和服务器版本不适配,请联系我方对服务器做适配调整。 |
| 1008 | 初始化引擎传入的上下文context无效(为空或未传)。 |
| 1014 | 网络错误,需要排查传入的服务器地址是否正确,设备网络是否正常连接,网络质量是否良好,服务器端部署是否正常。 |
1、创建房间,根据传人的房间信息创建一个房间,roomId不传则系统自己生成一个随机数房间号
func createRoom() { let roomInfo = AVDRoomInfo(roomId: self.roomTf.text, roomName: "按照需求", appRoomId: self.roomTf.text, ownerId: nil, maxAttendee: 5, maxAudio: 5, maxVideo: 5, roomMode: 0, voiceActivated: true) roomInfo?.setRoomModeFlag(AVDRoomMode(mcu.rawValue)) AVDEngine.instance().scheduleRoom(roomInfo) }2、房间创建结果异步回调,根据返回的Result判断是否成功
func onScheduleRoomResult(_ result: AVDResult, roomId: String!) { var msg = "" if result == AVD_Success { msg = "✅✅✅✅房间创建成功✅✅✅✅" }else{ msg = "❌❌❌❌房间创建失败❌❌❌❌" } AlertTool().show(msg, in: self.view) }3、创建房间错误码
| 错误码 | 说明 |
|---|---|
| 40003/40005 | 重复创建房间,房间已存在。 |
| 其他非0错误码 | 非0则是创建失败,这类错误码相对较少,出现时和我方确认解决。 |
1、根据传人的roomId加入房间,确保userid的唯一性,如果一个房间有2个相同的userid先入会的user就会被踢出房间
@IBAction func joinMeetingClicked(_ sender: UIButton) { let room = AVDRoom.obtain(roomTf.text); let user = AVDUser(userId: UUID().uuidString, userName:nameTf.text , userData: nil) room?.join(with: user, delegate: self) }2、加入房间结果异步回调,根据返回的Result判断是否成功
extension JoinMeetingViewController: AVDRoomJoinDelegate{ func onJoinResult(_ aResult: AVDResult) { var msg = "" if aResult == 0 { msg = "✅✅✅✅房间加入成功✅✅✅✅" UserDefaults().setValue(roomTf.text, forKey:K.roomId) self.navigationController?.pushViewController(MeetingViewController(), animated: true) } else { print("加入房间失败 = \(aResult)") msg = "❌❌❌❌房间加入失败❌❌❌❌" } //AlertTool().show(msg, in: self.view) AlertTool.alertShow(msg, alertVC: self) }}
| 错误码 | 说明 |
|---|---|
| 401 | 服务器认证失败 |
| 404 | 房间不存在 |
| 405 | 服务器授权过期,请联系续期。 |
| 1014 | 网络异常,检查设备和服务器整个网络链路是否正常。 |
3、房间网络状态,比如网络不好提示用户
func onConnectionStatus(_ status: AVDConnectionStatus) { if status == AVDConnectionStatus(connecting.rawValue){ AlertTool().show("断线重连中,,,,,", in: self.view) }else if status == AVDConnectionStatus(connected.rawValue){ AlertTool().show("断线重连成功🤭🤭🤭🤭", in: self.view) }else{ AlertTool().show("断线重连失败☹️☹️☹️☹️", in: self.view) } } 4、自己被踢出房间会收到下面回调
xxxxxxxxxx func onLeaveIndication(_ reason: AVDResult, fromUser fromId: String!) { }| 特征码 | 离会原因 |
|---|---|
| 0 | 正常离会,用户自己调用destoryRoom离会 |
| 804 | 被相同userId用户入会挤下线 |
| 807 | UDP媒体通道建立超时,常见于join加会时。 |
| 808 | 该用户被其他用户踢出房间 |
| 809 | 服务没有授权用户入会 |
| 810 | 用户关闭房间时,所有用户被踢出房间。 |
| 811 | 服务器维护是关闭房间 |
| 812 | 和服务器之间心跳超时,异常离会。例如:网络中断、APP被系统或后台杀掉。 |
5、离开房间 ps:不会影响房间内其他参会者
xxxxxxxxxxmroom?.leave(0)6、关闭房间 ps:当前房间参会者都会被踢出房间
xxxxxxxxxxmroom?.close7、释放sdk资源
xxxxxxxxxxmroom?.destory()
1、根据room实例获取视频模块实例并设置代理,后续的打开关闭以及房间内其他用户的视频状态都会通过video回调和操作
xxxxxxxxxxmvideo = AVDVideo.getVideo(mroom)mvideo?.delegate = self2、房间内其他参会者发布摄像头回调,并且进行订阅渲染
xxxxxxxxxx func onPublishCameraNotify(_ camera: AVDCamera!) { self.mvideo?.subscribe(camera.id) let freeRender = getFreeRender() let lab = freeRender.viewWithTag(666) as? UILabel let userid = mvideo?.getOwnerId(camera.id) let user = musermanager?.getUser(userid) lab?.text = user?.userName self.mvideo?.attachRender(withDeviceId: camera.id, render: freeRender) } 3、房间内其他参会者取消发布摄像头回调,并且进行取消订阅和渲染
xxxxxxxxxx func onUnpublishCameraNotify(_ camera: AVDCamera!) { self.mvideo?.unsubscribe(camera.id) let attachRender = getRender(with: camera.id) let lab = attachRender.viewWithTag(666) as? UILabel lab?.text = nil self.mvideo?.detachRender(withRender: attachRender) }4、获取加入房间之前已经发布的摄像头列表
xxxxxxxxxxself.mVideo.publishedCameras
1、根据room实例获取音频模块实例并设置代理,后续的打开关闭以及房间内其他用户的音频状态都会通过audio回调和操作
xxxxxxxxxxmaudio = AVDAudio.getAudio(mroom)
2、打开本地音频
xxxxxxxxxxmaudio?.openMicrophone()
3、关闭本地音频
xxxxxxxxxxmaudio?.closeMicrophone()
1、根据room实例获取用户模块实例并设置代理,房间内其他用户的入会离会都会通过user回调和操作
xxxxxxxxxxuserManager = AVDUserManager.getUserManager(mroom)userManager?.delegate = self
2、用户入会通知
xxxxxxxxxxfunc onUserJoinNotify(_ user: AVDUser!) {}
3、用户离会通知
xxxxxxxxxxfunc onUserLeaveNotify(_ user: AVDUser!) {}
4、获取房间总的参会者列表
xxxxxxxxxx@property (nonatomic,retain,readonly) NSMutableArray* participants; /**< 参会者列表用户信息,数组中存放AVDUser对象 */
1、根据room实例获取屏幕模块实例并设置代理,房间内其他用户的入会离会都会通过screen回调和操作
xxxxxxxxxxmscreen = AVDScreen.getScreen(mroom)mscreen?.delegate = self
2、其他人发布屏幕共享,订阅渲染
xxxxxxxxxx func onPublishScreenNotify(_ screen: AVDVideoDevice!) { self.mscreen?.subscribe(screen.id) let freeRender = getFreeRender() let lab = freeRender.viewWithTag(666) as? UILabel let userid = mscreen?.getOwnerId(screen.id) let user = musermanager?.getUser(userid) lab?.text = user?.userName self.mvideo?.attachRender(withDeviceId: screen.id, render: freeRender) }3、其他人取消发布,取消订阅渲染
xxxxxxxxxxfunc onUnpublishScreenNotify(_ screen: AVDVideoDevice!) { self.mscreen?.unsubscribe(screen.id) let attachRender = getRender(with: screen.id) let lab = attachRender.viewWithTag(666) as? UILabel lab?.text = nil self.mscreen?.detachRender(withRender: attachRender) }暂时没有实现
1、根据room实例获取聊天模块实例并设置代理,房间内其他用户的入会离会都会通过chat回调和操作
xxxxxxxxxxmchat = AVDChat.getChat(mroom);mchat.delegate = self;
2、发送公聊消息
xxxxxxxxxxmchat.sendPublicMessage(msg)
3、发送私聊消息
xxxxxxxxxxmchat?.sendPrivateMessage(msglen, toUser: user.userID)
4、收到公聊消息回调
xxxxxxxxxx func onPublicMessage(_ message: AVDMessage!) { msg = @"收到公聊消息";}5、 收到私聊消息回调
xxxxxxxxxx func onPrivateMessage(_ message: AVDMessage!) { msg = @"收到公聊消息";}1、设置日志文件
xxxxxxxxxxAVDEngine.instance().setLogParams("debug", file: filePath)
2、创建日志文件存放路径,可以根据自己需求存放到自己想存放的目录
xxxxxxxxxx
func logFilePath() -> String { let dateFormat = DateFormatter() dateFormat.dateFormat = "yyyy-MM-dd-HHmmss" let dateString = dateFormat.string(from: Date()) let fileName = "\(dateString)_log.txt" let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true) var documentPath = paths[0] documentPath = documentPath + "/" + fileName return documentPath }黑屏:
如果是本地打开视频黑屏则需要排查编码和打开摄像头是否有报错(发布本地摄像头回调result值是否为0、查看日志分析是否有异常);显示其他端的视频黑屏,排查是否视频有正常发布出来,可以加入其他设备进来查看是否黑屏,如果发布端没有问题,则需要排查本地解码视频是否有报错或者当前是否是弱网情况。在网络比较差的情况下,渲染多路视频较大概率出现视频显示黑屏问题,此时需要实时监测视频的媒体统计信息,视频帧率恢复正常后再去做渲染操作。
模糊花屏:
视频模糊(晃动镜头的时候会更明显)一般由于码流设置偏低导致,需要将码流适当增加(详见5.15节)。通常情况下640×480分辨率对应码流为500kbps~1000kbps,1280×720分辨率对应码流为1000kbps~1500kbps,1920×1080分辨率对应码流为2000kbps~4000kbps。注:SDK所有发布的视频(摄像头、共享屏幕、虚拟摄像头)共用一个码流配置。例如:发布一路1280×720摄像头视频,码流设置为1200kbps,帧率20帧。此时再发布一路1280×720共享桌面视频,两路720P视频总码流是1200kbps,可能会出现共享屏幕模糊问题,则需要根据发布的视频路数动态设置码流大小。
卡顿:
卡顿通常情况是编解码帧率输出不稳定(编码或解码丢帧)和网络比较差丢包引起,需要查看SDK日志分析丢帧和丢包情况,结合设备CPU占用(CPU占用过大)情况,路由器带宽占用情况,服务器交互日志具体分析。
延时:
码流设置过大导致设备编解码压力增加或者由于网络环境差存在丢包均会出现视频延时问题,此类情况需要结合终端日志和服务器交互日志具体分析。