折腾:
期间,需要去先去试试,用飞语的SDK
FYRtcEngineKit
libFyCloud.a
去实现基本的语音通话。
参考官网文档:
飞语云通信,提供点对点语音、多人语音、网络直拨、隐私保护通话、回拨电话,专线电话等互联网通信服务
FeiyuCloud/objc-sdk-demo: Feiyu Cloud Objective-C SDK Demo
和:
然后开始去折腾。
发现官网的demo和库,是不能在模拟器中运行的。
所以只能用真机去测试,所以要找2个iPhone去测试。
然后为了测试去写两份代码,一份用于主叫方,一份用于被叫方
主叫方:
// // ViewController.swift // FYRtcEngineKitDemo // // Created by zhangyusheng on 2017/9/23. // Copyright © 2017年 zhangyusheng. All rights reserved. // import UIKit let FY_APPID = “FBC7xxxxxxxxxEC4” let FY_APPTOKEN = “27998Axxxxxxxxxx4E7” let FY_CALLER_ID = “13800001111″ let FY_CALLEE_ID = “13922223333″ class ViewController: UIViewController, FYRtcEngineKitDelegate{ var fyRtcEngine: FYRtcEngineKit! override func viewDidLoad() { super.viewDidLoad() // fyRtcEngine = FYRtcEngineKit.sharedEngine(withAppId:”your appid”, appToken:”your apptoken”, delegate: nil) fyRtcEngine = FYRtcEngineKit.sharedEngine(withAppId: FY_APPID, appToken: FY_APPTOKEN, delegate: self) // fyRtcEngine.joinChannel(“channelId123”, uid: nil, optionData: nil, joinSuccess: nil) } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(true) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(true) if let sdkVerStr = fyRtcEngine.version() { self.infoNotice(“飞语SDK版本:\(sdkVerStr)”) } } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } @IBAction func speakerBtnEvent(_ sender: UIButton) { self.infoNotice(“点击了扬声器🔉按钮”) sender.isSelected = !sender.isSelected fyRtcEngine.enabledSpeaker( sender.isSelected) } @IBAction func muteBtnEvent(_ sender: UIButton) { self.infoNotice(“点击了静音🔇按钮”) sender.isSelected = !sender.isSelected fyRtcEngine.muteLocalAudio(sender.isSelected) } @IBAction func endCallBtnEvent(_ sender: UIButton) { self.infoNotice(“点击了拨号📲按钮”) // fyRtcEngine.leaveChannel(nil) let dialOption:FYOptionData = FYOptionData() dialOption.isRecord = true dialOption.maxDuration = 60 * 60 * 24 fyRtcEngine.dialPeer(FY_CALLEE_ID, callerUid: FY_CALLER_ID, optionData: dialOption) } /**************************************************************** * 点到点语音相关Delegate 方法(FYRtcEngineKitDelegate) ****************************************************************/ //- (void)onFYRtcEngine:(FYRtcEngineKit *)engine OutgoingCall:(NSString *)callee Uid:(NSString *)uid func onFYRtcEngine(_ engine: FYRtcEngineKit!, outgoingCall callee: String!, uid: String!) { let logStr = “拨出号码成功: engine=\(engine),callee=\(callee),uid=\(uid)” print(logStr) self.infoNotice(logStr) } func onFYRtcEngineDialBackSuccess(_ engine: FYRtcEngineKit!) { self.infoNotice(“拨号回调成功”) } //- (void)onFYRtcEngine:(FYRtcEngineKit *)engine OnError:(FYError *)error func onFYRtcEngine(_ engine: FYRtcEngineKit!, onError error: FYError!) { let logStr = “出错: [\(error.code)] =\(error.msg!)” print(logStr) self.infoNotice(logStr) } } |
被叫方:
// // ViewController.swift // FYRtcEngineKitDemo // // Created by zhangyusheng on 2017/9/23. // Copyright © 2017年 zhangyusheng. All rights reserved. // import UIKit let FY_APPID = “FBCxxxxxxxxxxxx8EC4” let FY_APPTOKEN = “279xxxxxxxxxxx4E7” let FY_CALLER_ID = “13800001111” let FY_CALLEE_ID = “13922223333” class ViewController: UIViewController, FYRtcEngineKitDelegate { var fyRtcEngine: FYRtcEngineKit! override func viewDidLoad() { super.viewDidLoad() fyRtcEngine = FYRtcEngineKit.sharedEngine(withAppId: FY_APPID, appToken: FY_APPTOKEN, delegate: self) // fyRtcEngine.joinChannel(“channelId123”, uid: nil, optionData: nil, joinSuccess: nil) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(true) if let sdkVerStr = fyRtcEngine.version() { self.infoNotice(“飞语SDK版本:\(sdkVerStr)”) } } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } @IBAction func speakerBtnEvent(_ sender: UIButton) { sender.isSelected = !sender.isSelected fyRtcEngine.enabledSpeaker( sender.isSelected) } @IBAction func muteBtnEvent(_ sender: UIButton) { sender.isSelected = !sender.isSelected fyRtcEngine.muteLocalAudio(sender.isSelected) } @IBAction func endCallBtnEvent(_ sender: UIButton) { // fyRtcEngine.leaveChannel(nil) fyRtcEngine.calleePrepare(FY_CALLEE_ID, prepareSuccess: nil) } /**************************************************************** * 点到点语音相关Delegate 方法(FYRtcEngineKitDelegate) ****************************************************************/ // 接通中回调 // – (void)onFYRtcEngineCallConnect:(FYRtcEngineKit *)engine // 被叫已经开始接听点到点语音 func onFYRtcEngineCallConnect(_ engine: FYRtcEngineKit!) { self.infoNotice(“接通中回调”) } //- (void)onFYRtcEngine:(FYRtcEngineKit *)engine OnError:(FYError *)error func onFYRtcEngine(_ engine: FYRtcEngineKit!, onError error: FYError!) { let logStr = “出错: [\(error.code)] =\(error.msg!)” print(logStr) self.infoNotice(logStr) } } |
调试到最后发现,会onError,error的
后来继续调试,问了飞语工程师,加上:
incomingCall
在其中answerCall,才真正能语音通话的。
接着去尝试写一个既可以打电话也可以接电话的,一套代码。
【已解决】iOS的项目中swift文件中如何获得Storyboard中的UITextField控件
然后继续优化代码。
然后期间又出现了:
【已解决】iOS中程序崩溃:ViewController onFYRtcEngine CallEnd unrecognized selector sent to instance
【总结】
之后,实现了基本的点对点(主叫方通过网络发起,被叫方通过网络接听)的语音通话。
核心代码:
// // ViewController.swift // FYRtcEngineKitDemo // // Created by zhangyusheng on 2017/9/23. // Copyright © 2017年 zhangyusheng. All rights reserved. // import UIKit let FY_APPID = “FBC74xxxxxxxxxEC4” let FY_APPTOKEN = “27998xxxxxxxxxxxE7” //let FY_CALLER_ID = “13800001111” //let FY_CALLEE_ID = “13922223333” class ViewController: UIViewController, UITextFieldDelegate, FYRtcEngineKitDelegate{ let SelfTextFieldTag = 1 let OtherTextFieldTag = 2 let SpeakerButtonTag = 3 let CallButtonTag = 4 let MuteButtonTag = 5 var selfTextField:UITextField! var otherTextField:UITextField! var speakerButton:UIButton! var callButton:UIButton! var muteButton:UIButton! var isCalling = false override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } required init?(coder aDecoder: NSCoder) { //fatalError(“init(coder:) has not been implemented”) super.init(coder: aDecoder) } var fyRtcEngine: FYRtcEngineKit! override func viewDidLoad() { super.viewDidLoad() // fyRtcEngine = FYRtcEngineKit.sharedEngine(withAppId:”your appid”, appToken:”your apptoken”, delegate: nil) // fyRtcEngine.joinChannel(“channelId123”, uid: nil, optionData: nil, joinSuccess: nil) fyRtcEngine = FYRtcEngineKit.sharedEngine(withAppId: FY_APPID, appToken: FY_APPTOKEN, delegate: self) self.selfTextField = self.view.viewWithTag(SelfTextFieldTag) as! UITextField self.selfTextField.delegate = self self.selfTextField.returnKeyType = .done self.otherTextField = self.view.viewWithTag(OtherTextFieldTag) as! UITextField self.otherTextField.delegate = self self.otherTextField.returnKeyType = .done self.speakerButton = self.view.viewWithTag(SpeakerButtonTag) as! UIButton self.speakerButton.setImage(UIImage(named: “speaker_normal”), for: UIControlState.normal) self.speakerButton.setImage(UIImage(named: “speaker_pressed”), for: UIControlState.selected) self.callButton = self.view.viewWithTag(CallButtonTag) as! UIButton self.muteButton = self.view.viewWithTag(MuteButtonTag) as! UIButton self.muteButton.setImage(UIImage(named: “mute_normal”), for: UIControlState.normal) self.muteButton.setImage(UIImage(named: “mute_pressed”), for: UIControlState.selected) } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(true) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(true) if let sdkVerStr = fyRtcEngine.version() { self.infoNotice(“飞语SDK版本:\(sdkVerStr)”) self.selfPrepareReceiveCall() } } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } @IBAction func speakerBtnEvent(_ sender: UIButton) { sender.isSelected = !sender.isSelected fyRtcEngine.enabledSpeaker( sender.isSelected) self.infoNotice(sender.isSelected ? “开启外放🔉” : “关闭外放🔉”) } @IBAction func muteBtnEvent(_ sender: UIButton) { sender.isSelected = !sender.isSelected fyRtcEngine.muteLocalAudio(sender.isSelected) self.infoNotice(sender.isSelected ? “开启静音🔇” : “取消静音🔇”) } @IBAction func endCallBtnEvent(_ sender: UIButton) { // fyRtcEngine.leaveChannel(nil) if (isCalling) { fyRtcEngine.endCall(self.endCallCallback) self.infoNotice(“挂断通话”) } else { let dialOption:FYOptionData = FYOptionData() dialOption.isRecord = true //dialOption.maxDuration = 60 * 60 * 24 dialOption.maxDuration = 60 * 10 let otherUid = self.otherTextField.text! let selfUid = self.selfTextField.text! fyRtcEngine.dialPeer(otherUid, callerUid: selfUid, optionData: dialOption) self.infoNotice(“主叫:调用dialPeer”) self.isCalling = true self.updateCallButtonImage() } } /**************************************************************** * UITextFieldDelegate ****************************************************************/ func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() return false } func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { textField.resignFirstResponder() return true } func textFieldDidEndEditing(_ textField: UITextField) { textField.resignFirstResponder() if textField === selfTextField { self.selfPrepareReceiveCall() } } /**************************************************************** * 自定义函数 ****************************************************************/ func updateCallButtonImage(){ dispatchMain_sync{ if (self.isCalling) { //self.callButton.imageView?.image = UIImage(named: “hangup_normal”) self.callButton.setImage(UIImage(named: “hangup_normal”), for: UIControlState.normal) } else { //self.callButton.imageView?.image = UIImage(named: “answer_normal”) self.callButton.setImage(UIImage(named: “answer_normal”), for: UIControlState.normal) } } } func selfPrepareReceiveCall(){ let selfUid = self.selfTextField.text! fyRtcEngine.calleePrepare(selfUid, prepareSuccess: self.calleePrepareSuccess) self.infoNotice(“\(selfUid) 准备接听”) } func calleePrepareSuccess() { self.infoNotice(“准备接听成功”) } func endCallCallback(status:FYRtcEngineStatus?) { self.infoNotice(“挂断回调: \(statusToShortStr(fyStatus: status))”) self.afterCallEnd() } func afterCallEnd(){ self.isCalling = false self.updateCallButtonImage() self.selfPrepareReceiveCall() } func statusToFullStr(fyStatus:FYRtcEngineStatus?) -> String { var statusStr = “” if let status = fyStatus { statusStr = “reportRtcStats: 累计发送字节数:\(status.sendBytes),累计接收字节数:\(status.recvBytes),下行带宽:\(status.downBandwidth),上行带宽:\(status.upBandwidth),发送丢包率:\(status.sendLossRate),接受丢包率:\(status.recvLossRate)” } return statusStr } func statusToShortStr(fyStatus:FYRtcEngineStatus?) -> String { var statusStr = “” if let status = fyStatus { statusStr = “已发:\(status.sendBytes),已收:\(status.recvBytes),下行:\(status.downBandwidth),上行:\(status.upBandwidth),发丢率:\(status.sendLossRate),接丢率:\(status.recvLossRate)” } return statusStr } /**************************************************************** * 点到点语音相关Delegate 方法(FYRtcEngineKitDelegate) ****************************************************************/ func onFYRtcEngine(_ engine: FYRtcEngineKit!, callEnd status: FYRtcEngineStatus!) { self.infoNotice(“通话结束: \(statusToShortStr(fyStatus: status))”) self.afterCallEnd() } func onFYRtcEngine(_ engine: FYRtcEngineKit!, didAudioMuted muted: Bool, uid: String!) { self.infoNotice(“静音回调: muted=\(muted),uid=\(uid)”) } func onFYRtcEngine(_ engine: FYRtcEngineKit!, incomingCall caller: String!) { self.infoNotice(“电话☎打入:\(caller),去调用接听”) fyRtcEngine.answerCall() self.isCalling = true self.updateCallButtonImage() } func onFYRtcEngine(_ engine: FYRtcEngineKit!, reportRtcStats status: FYRtcEngineStatus!) { let logStr = statusToShortStr(fyStatus: status) print(logStr) // self.infoNotice(logStr) self.noticeTop(logStr) } //- (void)onFYRtcEngine:(FYRtcEngineKit *)engine OutgoingCall:(NSString *)callee Uid:(NSString *)uid func onFYRtcEngine(_ engine: FYRtcEngineKit!, outgoingCall callee: String!, uid: String!) { let logStr = “打出电话: 被叫=\(callee!),主叫=\(uid!)” print(logStr) self.infoNotice(logStr) } // 接通中回调,被叫已经开始接听点到点语音 // – (void)onFYRtcEngineCallConnect:(FYRtcEngineKit *)engine func onFYRtcEngineCallConnect(_ engine: FYRtcEngineKit!) { self.infoNotice(“接通中回调”) } func onFYRtcEngineDialBackSuccess(_ engine: FYRtcEngineKit!) { self.infoNotice(“回拨成功”) } //- (void)onFYRtcEngine:(FYRtcEngineKit *)engine OnError:(FYError *)error func onFYRtcEngine(_ engine: FYRtcEngineKit!, onError error: FYError!) { let logStr = “出错: \(error.code)=\(error.msg!)” print(logStr) //self.infoNotice(logStr) self.errorNotice(logStr) } } |
后来完整的demo,都整理到了:
crifan/feiyuiOSDemo: 飞语云平台iOS点对点通话录音Demo