最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

【已解决】用飞语FYRtcEngineKit去实现基本的iOS间的语音通话

iOS crifan 1958浏览 0评论

折腾:

【已解决】使用飞语云平台实现iOS的电话录音

期间,需要去先去试试,用飞语的SDK

  • FYRtcEngineKit

  • libFyCloud.a

去实现基本的语音通话。

参考官网文档:

飞语云通信,提供点对点语音、多人语音、网络直拨、隐私保护通话、回拨电话,专线电话等互联网通信服务

FeiyuCloud/objc-sdk-demo: Feiyu Cloud Objective-C SDK Demo

点到点语音 · DOCS

和:

API · PrivateDocs

然后开始去折腾。

发现官网的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

https://github.com/crifan/feiyuiOSDemo

转载请注明:在路上 » 【已解决】用飞语FYRtcEngineKit去实现基本的iOS间的语音通话

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
90 queries in 0.173 seconds, using 22.10MB memory