我正在使用webRTC进行视频通话功能。我使用的是“ Google webRTC”框架而不是libJingle。建立对等连接后,它始终保持在“ RTCICEConnectionChecking”状态。
我有几个问题。
1)对等连接状态始终保持在“ RTCICEConnectionChecking”。
2]当网络不同时(3g / 4g),视频通话为不工作。
3)相同的网络运行正常。
我使用了很多转弯服务器,但无法成功。
请先建议我,谢谢。
代码段是
//
// CallConnectViewController.swift
// socketWebRTC
//
import UIKit
import AVFoundation
import SwiftHTTP
import AVKit
import CameraManager
import WebRTC
let TAG = "CallConnectViewController"
let VIDEO_TRACK_ID = TAG + "VIDEO"
let AUDIO_TRACK_ID = TAG + "AUDIO"
let LOCAL_MEDIA_STREAM_ID = TAG + "STREAM"
class CallConnectViewController: UIViewController,RTCPeerConnectionDelegate{
func peerConnectionShouldNegotiate(_ peerConnection: RTCPeerConnection) {
print("Negotiation method call ====>")
}
@IBOutlet weak var remoteview_mask: UIView!
@IBOutlet weak var openBtn: UIButton!
@IBOutlet weak var dropBtn: UIButton!
@IBOutlet weak var muteBtn: UIButton!
@IBOutlet weak var remoteUserView: RTCEAGLVideoView!
@IBOutlet weak var localUserView: RTCEAGLVideoView!
@IBOutlet weak var testView:UIView!
@IBOutlet weak var txtName: UITextField!
@IBOutlet weak var txtToName: UITextField!
@IBOutlet weak var indicator:UIActivityIndicatorView!
var mediaStream: RTCMediaStream!
var localVideoTrack: RTCVideoTrack!
var localAudioTrack: RTCAudioTrack!
var remoteVideoTrack: RTCVideoTrack!
var remoteAudioTrack: RTCAudioTrack!
var roomName: String!
var isMute : Bool = false
var peerFactory:RTCPeerConnectionFactory = RTCPeerConnectionFactory();
let cameraManager = CameraManager()
let uuid = UUID().uuidString
var randomString = ""
var roomID = ""
var hangUP = false
var sdpMLineIndex:AnyObject = "" as AnyObject
var sdpMid:AnyObject = "" as AnyObject
var candidate:AnyObject = "" as AnyObject
var videoSource:RTCVideoSource?
var timer = Timer()
func Log(_ value:String) {
print(TAG + " " + value)
}
override func viewDidLoad() {
super.viewDidLoad()
socket = SocketIOManager.sharedInstance.initSocket(uuid)
NotificationCenter.default.addObserver(forName:NSNotification.Name.AVRouteDetectorMultipleRoutesDetectedDidChange , object:nil, queue:nil, using:catchNotification)
self.initComponents();
self.initWebRTC()
self.randomString = self.randomString(length: 12)
print("Random String ======>",self.randomString)
mediaStream = peerConnectionFactory.mediaStream(withStreamId:self.randomString)
localAudioTrack = peerConnectionFactory.audioTrack(withTrackId: AUDIO_TRACK_ID)
mediaStream.addAudioTrack(localAudioTrack)
self.timer = Timer.scheduledTimer(timeInterval: 5,
target: self,
selector: #selector(self.sendCandidate),
userInfo: nil,
repeats: true)
self.indicator.isHidden = true
}
func onLocalStreamReadyForRender() {
let frame = localUserView!.frame
let rtcVideoView = RTCCameraPreviewView.init(frame: CGRect.init())
rtcVideoView.frame = frame
rtcVideoView.frame.origin.x = 0
rtcVideoView.frame.origin.y = 0
self.localUserView?.addSubview(rtcVideoView)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if(!hangUP){
self.dropBtnPressed(UIButton())
}
self.timer.invalidate()
self.socket?.disconnect()
}
func catchNotification(notification:Notification) -> Void {
let interuptionDict = notification.userInfo
if let interuptionRouteChangeReason = interuptionDict?[AVAudioSessionRouteChangeReasonKey] {
let routeChangeReason = interuptionRouteChangeReason as! UInt
switch (routeChangeReason) {
case AVAudioSession.RouteChangeReason.categoryChange.rawValue:
do {
try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)
} catch {
}
default:
break;
}
}
}
@IBAction func btnActionON(_ sender:UIButton){
self.setAudioOutputSpeaker(enabled: true)
}
@IBAction func btnActionOFF(_ sender:UIButton){
self.setAudioOutputSpeaker(enabled: false)
}
override func viewDidDisappear(_ animated: Bool) {
UserDefaults.standard.setValue(false, forKey: "fromAPNS")
UserDefaults.standard.synchronize()
}
func videoView(_ videoView: RTCEAGLVideoView!, didChangeVideoSize size: CGSize) {
print("DidChange Video size:",size)
}
func showRoomDialog() {
sigRecoonect()
}
func getRoomName() -> String {
return (roomName == nil || roomName.isEmpty) ? txtToName.text ?? "123456" : roomName
}
var peerConnectionFactory: RTCPeerConnectionFactory! = nil
var peerConnection: RTCPeerConnection! = nil
var pcConstraints: RTCMediaConstraints! = nil
var videoConstraints: RTCMediaConstraints! = nil
var audioConstraints: RTCMediaConstraints! = nil
var mediaConstraints: RTCMediaConstraints! = nil
var socket = SocketIOManager.sharedInstance.socket
var wsServerUrl: String! = nil
var peerStarted: Bool = false
func initWebRTC() {
peerConnectionFactory = RTCPeerConnectionFactory()
RTCInitializeSSL()
peerConnectionFactory = RTCPeerConnectionFactory()
let mandatoryConstraints = ["OfferToReceiveAudio": "true", "OfferToReceiveVideo": "false"]
let optionalConstraints = [ "DtlsSrtpKeyAgreement": "true", "internalSctpDataChannels" : "true"]
mediaConstraints = RTCMediaConstraints.init(mandatoryConstraints: mandatoryConstraints, optionalConstraints: optionalConstraints)
}
func connect() {
if (!peerStarted) {
sendOffer()
peerStarted = true
}
}
func hangUp() {
stop()
sendDisconnect()
}
func stop() {
if (peerConnection != nil) {
// socket?.disconnect()
//self.remoteUserView.removeFromSuperview()
self.localUserView.removeFromSuperview()
self.peerConnection.remove(self.mediaStream)
self.peerConnection.close()
self.peerConnection = nil
self.peerStarted = false
}
}
func prepareNewConnection() -> RTCPeerConnection {
let url1 = "stun:stun.l.google.com:19302"
// let url2 = "turn:webrtcweb.com:7788"
// let url3 = "turn:webrtcweb.com:4455"
// let url4 = "turn:webrtcweb.com:7788?transport=udp"
// let url5 = "turn:webrtcweb.com:7788?transport=tcp"
// let url6 = "turn:webrtcweb.com:4455?transport=udp"
// let url7 = "turn:webrtcweb.com:5544?transport=tcp"
var icsServers: [RTCIceServer] = []
icsServers.append(RTCIceServer(urlStrings: [url1], username:"",credential: ""))
// icsServers.append(RTCIceServer(urlStrings: [url2,url3,url4,url5,url6,url7,], username: "muazka", credential: "muazka"))
let rtcConfig: RTCConfiguration = RTCConfiguration()
rtcConfig.tcpCandidatePolicy = RTCTcpCandidatePolicy.enabled
rtcConfig.bundlePolicy = RTCBundlePolicy.maxBundle
rtcConfig.rtcpMuxPolicy = RTCRtcpMuxPolicy.require
rtcConfig.keyType = .ECDSA
rtcConfig.iceTransportPolicy = .all
rtcConfig.iceServers = icsServers;
peerConnection = peerConnectionFactory.peerConnection(with: rtcConfig, constraints: mediaConstraints, delegate: self)
peerConnection.add(mediaStream)
return peerConnection;
}
func peerConnection(_ peerConnection: RTCPeerConnection?, didGetStats stats: [Any]?) {
print("Peer connection states ===========????????>>>>>>>",stats)
}
/** Called any time the IceGatheringState changes. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceGatheringState){}
/** Called when a group of local Ice candidates have been removed. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove candidates: [RTCIceCandidate]){}
//MARK:- Manual Speaker Enagle and Disable
func setAudioOutputSpeaker(enabled: Bool)
{
let session = AVAudioSession.sharedInstance()
var _: Error?
try? session.setCategory(AVAudioSession.Category.playAndRecord)
try? session.setMode(AVAudioSession.Mode.voiceChat)
if enabled {
try? session.overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)
} else {
try? session.overrideOutputAudioPort(AVAudioSession.PortOverride.none)
}
try? session.setActive(true)
}
func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceConnectionState) {
var stateString: String = ""
switch newState {
case .new:
stateString = "RTCICEConnectionNew"
case .checking:
stateString = "RTCICEConnectionChecking"
case .connected:
stateString = "RTCICEConnectionConnected"
case .completed:
stateString = "RTCICEConnectionCompleted"
case .failed:
stateString = "RTCICEConnectionFailed"
case .disconnected:
stateString = "RTCICEConnectionDisconnected"
case .closed:
stateString = "RTCICEConnectionClosed"
default:
stateString = "Unknown"
}
Log("ICE connection : \(stateString)")
if(stateString == "RTCICEConnectionConnected"){
DispatchQueue.main.async {
self.indicator.stopAnimating()
self.indicator.isHidden = true
self.openBtn.isEnabled = true
self.dropBtn.isEnabled = true
}
}
}
/** New ice candidate has been found. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate){
print("iceCandidate: " + candidate.description)
let json:[String: AnyObject] = [
"type" : "candidate" as AnyObject,
"sdpMLineIndex" : candidate.sdpMLineIndex as AnyObject,
"sdpMid" : candidate.sdpMid as AnyObject,
"candidate" : candidate.sdp as AnyObject
]
sigSendIce(msg: json as NSDictionary)
}
func sigSendIce(msg:NSDictionary) {
let strMessage = dictToJson(dictionary: msg as NSDictionary)
socket!.emit("event", strMessage!)
}
@objc func adjustmentBestSongBpmHeartRate() {
print("adklhaskldhaklsdhaklsdhklashdlkashdlaksdhl")
}
//This function call every time untill and unless the answer come
@objc private func sendCandidate(){
//Timer Methods call
let json:[String: AnyObject] = [
"type" : "candidate" as AnyObject,
"candidate" : [
"sdpMLineIndex" : self.sdpMLineIndex,
"sdpMid" : self.sdpMid,
"candidate" : self.candidate as! String
] as AnyObject
]
let candidate = json["candidate"] as! [String:AnyObject]
if(candidate["candidate"] as! String != ""){
print("Timer methods call for every seconds <=======================TIMER======================>",json)
sigSend(json as NSDictionary)
}
}
func peerConnection(_ peerConnection: RTCPeerConnection, didAdd stream: RTCMediaStream) {
if (peerConnection == nil) {
return
}
if (stream.audioTracks.count > 1 || stream.videoTracks.count > 1) {
Log("Weird-looking stream: " + stream.description)
return
}
if (stream.videoTracks.count == 1) {
remoteVideoTrack = (stream.videoTracks[0] )
remoteVideoTrack.isEnabled = true
remoteVideoTrack.add(remoteUserView)
}
}
func peerConnection(_ peerConnection: RTCPeerConnection, didOpen dataChannel: RTCDataChannel) {
print("Connected--->")
}
func peerConnection(onRenegotiationNeeded peerConnection: RTCPeerConnection!) {
print(peerConnection.signalingState.rawValue)
print("Connected peer Connection--->")
}
func onOffer(_ sdp:RTCSessionDescription) {
print("SDP:========:::::::",sdp)
setOffer(sdp)
sendAnswer()
peerStarted = true
}
func onAnswer(_ sdp:RTCSessionDescription) {
print("SDP:========:::::::",sdp)
setAnswer(sdp)
}
func onCandidate(candidate:RTCIceCandidate) {
peerConnection.add(candidate)
}
func sendSDP(_ sdp:RTCSessionDescription) {
// print("SDP:========:::::::",sdp)
// let json:[String: AnyObject] = [
// "type" : sdp.type as AnyObject,
// "sdp" : sdp.description as AnyObject
// ]
// print(json)
// sigSend(json as NSDictionary)
//
print(sdp.sdp)
self.offerSend(sdp:sdp.sdp)
}
func sendOffer() {
peerConnection = prepareNewConnection();
peerConnection.offer(for: mediaConstraints) { (RTCSessionDescription, Error) in
if(Error == nil){
print("send offer")
self.peerConnection.setLocalDescription(RTCSessionDescription!, completionHandler: { (Error) in
// print("setLocalDescription @@@ ERRO",Error!)
print(RTCSessionDescription as Any)
self.sendSDP(RTCSessionDescription!)
})
} else {
print("sdp creation error: \(Error!)")
}
}
}
func setOffer(_ sdp:RTCSessionDescription) {
print("SDP:========:::::::",sdp)
if (peerConnection != nil) {
Log("peer connection already exists")
}
peerConnection = prepareNewConnection()
self.peerConnection.setRemoteDescription(sdp) { (error) in
self.peerConnection.delegate = self
}
}
func sendAnswer() {
Log("sending Answer. Creating remote session description...")
if (peerConnection == nil) {
Log("peerConnection NOT exist!")
return
}
self.peerConnection.answer(for: mediaConstraints) { (description, error) in
print("answer @@@ ERRO",error)
print("Answer",description)
}
// peerConnection.createAnswer(with: self, constraints: mediaConstraints)
}
func setAnswer(_ sdp:RTCSessionDescription) {
print("SDP:========:::::::",sdp)
if (peerConnection == nil) {
Log("peerConnection NOT exist!")
return
}
self.peerConnection.setRemoteDescription(sdp) { (error) in
// print("setRemoteDescription",error!)
}
}
func sendDisconnect() {
let json:[String: AnyObject] = [
"type" : "leave" as AnyObject,
"roomid": self.roomID as AnyObject
]
sigSend(json as NSDictionary)
}
//this is for send offer and connection
func peerConnection(_ peerConnection: RTCPeerConnection, didRemove stream: RTCMediaStream) {
}
func peerConnection(_ peerConnection: RTCPeerConnection, didChange stateChanged: RTCSignalingState) {
print("Status changes",stateChanged.rawValue)
}
func peerConnection(_ peerConnection: RTCPeerConnection!, didCreateSessionDescription sdp: RTCSessionDescription!, error: Error!) {
if (error == nil) {
self.peerConnection.setLocalDescription(sdp) { (error) in
self.sendSDP(sdp)
}
//self.offerSend(sdp:sdp.sdp)
print("SDP Connection State",peerConnection.signalingState.rawValue)
} else {
Log("sdp creation error: " + error.localizedDescription)
print("setLocalDescription @@@ ERRO",error!)
}
}
func peerConnection(_ peerConnection: RTCPeerConnection!, didSetSessionDescriptionWithError error: Error!) {
print("Peer Connsction State is:--\(peerConnection.signalingState.rawValue)")
}
func randomString(length: Int) -> String {
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return String((0..<length).map{ _ in letters.randomElement()! })
}
func sigConnect(_ wsUrl:String) {
wsServerUrl = wsUrl
socket!.connect()
let _:[String: AnyObject] = [
"log" : true as AnyObject
]
Log("connecting to " + wsServerUrl)
socket!.on("connect") { data,arg in
self.Log("WebSocket connection opened to: " + self.wsServerUrl)
self.sigEnter()
}
socket!.on("disconnect") { data,arg in
self.Log("WebSocket connection closed.")
}
socket!.on("message") { (data, emitter) in
if (data.count == 0) {
return
}
let json = self.convertToDictionary(text: (data[0] as? String) ?? "")
self.Log("WebServiceResponse->C: " + json!.description)
var type = ""
if let event = json!["event"] as? String {
type = event
}
if let typeString = json!["type"] as? String {
type = typeString
}
print("Printing log")
print(type)
// if (type == "login"){
// self.Log("Login,Logout")
// let sdp = RTCSessionDescription(type: type, sdp: (json!["sdp"] as! String))
// self.onOffer(sdp!)
// }else
if (type == "joined session") || (type == "login") {
//self.loginUser()
self.sendOffer()
self.peerStarted = true
}
else if(type == "leave"){
// self.hangUp()
// DispatchQueue.main.async {
// _ = self.navigationController?.popViewController(animated: true)
// }
self.dropBtnPressed(UIButton())
}
else if (type == "offer") {
self.Log("Received offer, set offer, sending answer....")
print("Json Response",json!)
print("Json Offer",(json!["offer"]!))
let dict:[String:String] = (json!["offer"] as! [String : String])
let sdp = RTCSessionDescription(type:RTCSdpType(rawValue:0)!, sdp: (dict["sdp"]!))
self.onOffer(sdp)
} else if (type == "answer" && self.peerStarted) {
self.timer.invalidate()
self.Log("Received answer, setting answer SDP")
print("Answer form the web",json!)
let dict:[String:String] = (json!["answer"] as! [String : String])
let sdp = RTCSessionDescription(type:RTCSdpType(rawValue:2)!, sdp: (dict["sdp"]!))
self.onAnswer(sdp)
} else if (type == "answer") {
self.timer.invalidate()
self.Log("Received answer, setting answer SDP")
let sdp = RTCSessionDescription(type: RTCSdpType(rawValue:2)!, sdp: (json!["sdp"] as! String))
print(sdp)
self.onAnswer(sdp)
}
else if (type == "Room"){
print("=========================>You are in room<=======================")
print("Json Response",json!)
self.roomID = json!["roomid"] as! String
}
else if (type == "candidate" && self.peerStarted) {
print("Received ICE candidate...",json!["candidate"] as! NSDictionary)
let remotecandidate: NSDictionary = json!["candidate"] as! NSDictionary
print("Remote Candidate List",remotecandidate)
let midIndex = remotecandidate["sdpMid"] as! String
if(midIndex != "")
{
let candidate = RTCIceCandidate(
sdp: (remotecandidate["sdpMid"] as! String),
sdpMLineIndex: (Int32(remotecandidate["sdpMLineIndex"] as! Int)),
sdpMid:(remotecandidate["candidate"] as? String))
self.onCandidate(candidate: candidate);
}
} else if ((type == "user disconnected" || type == "remote left") && self.peerStarted) {
self.Log("disconnected")
self.stop()
} else {
self.Log("Unexpected WebSocket message: " + (data[0] as AnyObject).description)
}
}
// socket!.connect()
}
func loginUser(){
let temDict = [
"type" : "login",
"name" : "Raj_new",
"mobile":"98989898989"
]
socket?.emit("event",temDict as NSDictionary)
// SocketIOManager.sharedInstance.sendMessage(temDict as NSDictionary)
}
func offerSend(sdp:String){
print("SDP:========:::::::",sdp)
let param:[String:Any] = [
"type": "offer",
"name": "Raj_new",
"mobile":"98989898989",
"offer":[
"sdp": sdp,
"type": "offer"],
"image":"www.google.com",
"address":"Ahmedabad,220 Aryan park"
]
// SocketIOManager.sharedInstance.sendMessage(param as NSDictionary)
print("=========================>Sending Offer<=======================")
let strMessage = dictToJson(dictionary: param as NSDictionary)
socket!.emit("event", strMessage!)
}
func sigRecoonect() {
socket!.disconnect()
socket!.connect()
}
func sigEnter() {
let roomName = getRoomName()
self.Log("Entering room: " + roomName)
//SocketIOManager.sharedInstance.sendMessage(["method" : "createOrJoin", "sessionId": roomName])
// SocketIOManager.sharedInstance.sendMessage(["type" : "login", "name": txtName.text as Any,"id":"9898989898"])
// SocketIOManager.sharedInstance.sendMessage(["type" : "login", "name":"Raj_new","id":"9898989898"])
let temDict = [
"type" : "login",
"name" : "Raj_new",
"mobile":"9898989898"
]
let strMessage = dictToJson(dictionary: temDict as NSDictionary)
socket!.emit("event", strMessage!)
}
func sigSend(_ msg:NSDictionary) {
let strMessage = dictToJson(dictionary: msg as NSDictionary)
socket!.emit("event", strMessage!)
}
//Convert Dictonary to JOSN string
func dictToJson(dictionary : NSDictionary) -> String? {
print("Dictionary Value=====>",dictionary)
if let theJSONData = try? JSONSerialization.data(
withJSONObject: dictionary,
options: []) {
let theJSONText = String(data: theJSONData,
encoding: .ascii)
print("JSON string = \(theJSONText!)")
return theJSONText!
}
return nil
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
@IBAction func openBtnPressed(_ sender:UIButton) {
if !peerStarted {
//self.sigConnect(SocketIOManager.sharedInstance.socket)
sender.isEnabled = false
// self.dropBtn.isEnabled = false
self.indicator.isHidden = false
self.indicator.startAnimating()
self.sigConnect((SocketIOManager.sharedInstance.socket.manager?.socketURL.absoluteString)!)
}
}
@IBAction func dropBtnPressed(_ sender: AnyObject) {
self.hangUP = true
self.hangUp()
DispatchQueue.main.async {
_ = self.navigationController?.popViewController(animated: true)
}
}
@IBAction func muteBtnPressed(_ sender: AnyObject) {
if !isMute { // if Current state is mute, turn off the mute
if peerStarted {
self.localAudioTrack.isEnabled = false
isMute = true
self.muteBtn.setImage(UIImage(named: "call_mute.png"), for: .normal)
}
} else { //Otherwise
if peerStarted {
localAudioTrack.isEnabled = true
self.muteBtn.setImage(UIImage(named: "un_mute.png"), for: .normal)
isMute = false
}
}
}
func convertToDictionary(text: String) -> [String: Any]? {
if let data = text.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
}
我在使用google stun时遇到相同的问题:stun:stun.l.google.com:19302
我认为问题是由电击/转弯服务器引起的。
i通过在以下位置应用免费转弯服务器成功解决并测试了它http://numb.viagenie.ca/cgi-bin/numbacct
很快我就添加
RTCIceServer(urlStrings: ["turn:numb.viagenie.ca:3478"], username: "YourUserName", credential: "yourPassword")
进入webrtc的iceServers列表,
这对我有用,至少证明我敏捷的一面是正确的,所有其余的都是转弯/眩晕服务器端。