Last active
October 30, 2018 03:50
-
-
Save saroar/20cde456d4952f0f3644c187303bdd7b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
class WebRTC: NSObject, RTCPeerConnectionDelegate, RTCEAGLVideoViewDelegate { | |
private var didReceiveRemoteStream:( () -> Void )? | |
private var onCreatedLocalSdp:((_ localSdp:NSDictionary) -> Void )? | |
private let factory = RTCPeerConnectionFactory() | |
private var localStream: RTCMediaStream? | |
private var localRenderView = RTCEAGLVideoView() | |
private let _localView = UIView(frame:CGRect(x:0, y:0, width: windowWidth(), height: windowHeight())) | |
private var remoteStream: RTCMediaStream? | |
private var remoteRenderView = RTCEAGLVideoView() | |
private let _remoteView = UIView(frame: CGRect(x: 0, y: 0, width: windowWidth(), height: windowWidth())) | |
private var peerConnections = [String: RTCPeerConnection]() | |
private var peerConnectionUserList = [String]() | |
static let sharedInstance = WebRTC() | |
private override init() { | |
super.init() | |
} | |
deinit { | |
for peerConnection in peerConnections { | |
if let stream = peerConnection.value.localStreams.first { | |
peerConnection.value.remove(stream) | |
} | |
} | |
} | |
// MARK: inerface | |
func localView() -> UIView { | |
return _localView | |
} | |
func remoteView() -> UIView { | |
return _remoteView | |
} | |
func setup() { | |
setupLocalStream() | |
} | |
func connect(userID: String, iceServerUrlList: [String], onCreatedLocalSdp: @escaping ((_ localSdp:NSDictionary) -> Void), didReceiveRemoteStream: @escaping ( () -> Void ) ) { | |
self.onCreatedLocalSdp = onCreatedLocalSdp | |
self.didReceiveRemoteStream = didReceiveRemoteStream | |
let configuration = RTCConfiguration() | |
configuration.iceServers = [RTCIceServer(urlStrings: iceServerUrlList)] | |
peerConnectionUserList.append(userID) | |
peerConnections[userID] = factory.peerConnection(with: configuration, constraints: WebRTCUtil.peerConnectionConstraints(), delegate: self) | |
peerConnections[userID]?.add(localStream!) | |
} | |
func diconnect(userID: String, iceServerUrlList: [String], onCreatedLocalSdp: @escaping ((_ localSdp:NSDictionary) -> Void), didReceiveRemoteStream: @escaping ( () -> Void ) ) { | |
} | |
func flipCamera() { | |
if let mysource = localStream?.videoTracks.first?.source as? RTCAVFoundationVideoSource { | |
mysource.useBackCamera = !mysource.useBackCamera | |
} | |
} | |
func enableVideo() { | |
if let mysvideo = localStream?.videoTracks.first { | |
mysvideo.isEnabled = !mysvideo.isEnabled | |
} | |
} | |
func enableAudio() { | |
if let myAudio = localStream?.audioTracks.first { | |
myAudio.isEnabled = !myAudio.isEnabled | |
} | |
} | |
// Answer | |
func receiveAnswer(hostID: String, remoteSdp: NSDictionary) { | |
_receiveAnswer(hostID: hostID, remoteSdp: remoteSdp) | |
} | |
// Offer | |
func receiveOffer(joinUID: String, remoteSdp: NSDictionary) { | |
_receiveOffer(joinUID: joinUID, remoteSdp: remoteSdp) | |
} | |
// Offer | |
func createOffer(hostID: String) { | |
_createOffer(hostID: hostID) | |
} | |
// MARK: implements | |
private func _receiveAnswer(hostID: String, remoteSdp: NSDictionary) { | |
guard let sdpContents = remoteSdp.object(forKey: "sdp") as? String else { | |
print("noSDp") | |
return | |
} | |
let sdp = RTCSessionDescription(type: .answer, sdp: sdpContents) | |
// 1. remote SDP | |
peerConnections[hostID]?.setRemoteDescription(sdp, completionHandler: { (error) in print(105, error?.localizedDescription) }) | |
} | |
private func _receiveOffer(joinUID: String, remoteSdp: NSDictionary) { | |
guard let sdpContents = remoteSdp.object(forKey: "sdp") as? String else { | |
print("noSDp") | |
return | |
} | |
// 1. remote SDP | |
let remoteSdp = RTCSessionDescription(type: .offer, sdp: sdpContents) | |
peerConnections[joinUID]?.setRemoteDescription(remoteSdp, completionHandler: { (error) in | |
// if any user offer me i commend it for one user | |
// 2. answer | |
self.peerConnections[joinUID]?.answer(for: WebRTCUtil.answerConstraints(), completionHandler: { (sdp, error) in | |
guard let sdp = sdp else { | |
print("can not create sdp") | |
return | |
} | |
print(128, error?.localizedDescription) | |
// 3.SDP | |
self.peerConnections[joinUID]?.setLocalDescription(sdp, completionHandler: { (error) in print(131, error?.localizedDescription) }) | |
// 4. peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceGatheringState) | |
// complete Answer | |
}) | |
}) | |
} | |
private func _createOffer(hostID: String) { | |
// 1. offer | |
peerConnections[hostID]?.offer(for: WebRTCUtil.mediaStreamConstraints(), completionHandler: { (description, error) in | |
guard let description = description else { | |
print("----- no description ----") | |
return | |
} | |
print(149, error?.localizedDescription) | |
// 2.SDP | |
self.peerConnections[hostID]?.setLocalDescription(description, completionHandler: { (error) in | |
print(153, error?.localizedDescription) | |
}) | |
// 3. peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceGatheringState) | |
// complete offer | |
}) | |
} | |
private func setupLocalStream() { | |
let streamId = WebRTCUtil.idWithPrefix(prefix: "stream") | |
localStream = factory.mediaStream(withStreamId: streamId) | |
setupView() | |
setupLocalVideoTrack() | |
setupLocalAudioTrack() | |
} | |
private func setupView() { | |
localRenderView.delegate = self | |
_localView.backgroundColor = UIColor.white | |
_localView.frame.origin = CGPoint(x: 20, y: _remoteView.frame.size.height - (_localView.frame.size.height / 2)) | |
_localView.addSubview(localRenderView) | |
remoteRenderView.delegate = self | |
_remoteView.backgroundColor = UIColor.lightGray | |
_remoteView.addSubview(remoteRenderView) | |
} | |
private func setupLocalVideoTrack() { | |
let localVideoSource = factory.avFoundationVideoSource(with: WebRTCUtil.mediaStreamConstraints()) | |
let localVideoTrack = factory.videoTrack(with: localVideoSource, trackId: WebRTCUtil.idWithPrefix(prefix: "video")) | |
if let avSource = localVideoTrack.source as? RTCAVFoundationVideoSource { | |
avSource.useBackCamera = false | |
} | |
localVideoTrack.add(localRenderView) | |
localStream?.addVideoTrack(localVideoTrack) | |
} | |
private func setupLocalAudioTrack() { | |
let localAudioTrack = factory.audioTrack(withTrackId: WebRTCUtil.idWithPrefix(prefix: "audio")) | |
localStream?.addAudioTrack(localAudioTrack) | |
} | |
// MARK: RTCPeerConnectionDelegate | |
// いったんスルー | |
public func peerConnectionShouldNegotiate(_ peerConnection: RTCPeerConnection) {} | |
public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove stream: RTCMediaStream) {} | |
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceConnectionState) {} | |
public func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate) { } | |
public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove candidates: [RTCIceCandidate]) {} | |
public func peerConnection(_ peerConnection: RTCPeerConnection, didOpen dataChannel: RTCDataChannel) {} | |
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange stateChanged: RTCSignalingState) {} | |
public func peerConnection(_ peerConnection: RTCPeerConnection, didAdd stream: RTCMediaStream) { | |
print("peerConnection didAdd stream:") | |
if stream == localStream { | |
return | |
} | |
self.remoteStream = stream | |
if let remoteVideoTrack = stream.videoTracks.first { | |
DispatchQueue.main.async { | |
remoteVideoTrack.add(self.remoteRenderView) | |
} | |
} | |
if let callback = self.didReceiveRemoteStream { | |
DispatchQueue.main.async { | |
callback() | |
} | |
self.didReceiveRemoteStream = nil | |
} | |
} | |
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceGatheringState) { | |
print("peerConnection didChange newState: RTCIceGatheringState, \(newState)") | |
if newState != .complete { | |
return | |
} | |
print("1. userList", peerConnectionUserList) | |
for user in peerConnectionUserList { | |
print("2. user, self.peerConnection[user]", user, self.peerConnections[user]) | |
if self.peerConnections[user] == peerConnection { | |
print("peerConnection connected", peerConnection) | |
guard let callback = self.onCreatedLocalSdp, let localDescription = WebRTCUtil.jsonFromDescription(description: self.peerConnections[user]?.localDescription) else { | |
print("no localDescription") | |
return | |
} | |
callback(localDescription) | |
self.onCreatedLocalSdp = nil | |
print("3. peerConnection didChange") | |
} | |
} | |
} | |
// MARK: RTCEAGLVideoViewDelegate | |
func videoView(_ videoView: RTCEAGLVideoView, didChangeVideoSize size: CGSize) { | |
print("---- didChangeVideoSize -----") | |
let ratio:CGFloat = size.width / size.height | |
if videoView == localRenderView { | |
let parentWidth = _localView.frame.size.width | |
_ = parentWidth * ratio | |
// localRenderView.frame = CGRect(x: (parentWidth - width) / 2, y: 2, width: width, height: _localView.frame.size.height-4) | |
localRenderView.frame = CGRect(x: 0, y: 0, width: size.width, height: _localView.frame.size.height) | |
} else if videoView == remoteRenderView { | |
let parentWidth = _remoteView.frame.size.width | |
_ = parentWidth * ratio | |
// remoteRenderView.frame = CGRect(x: (parentWidth - width) / 2, y: 0, width: width, height: _remoteView.frame.size.height) | |
remoteRenderView.frame = CGRect(x: 0, y: 0, width: size.width, height: _remoteView.frame.size.height) | |
} | |
} | |
} |
you need "func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate)" .
Send RTCIceCandidate to each joinUID and Add it to PeerConnection of each joinUID.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I think that multiple "remoteRenderViews" are required for each joinUID.