4
2
Fork 0

chore: finished webrtc swift implementation, ready to test

pull/43/head
Sudharshan S. 2019-05-12 08:52:45 +08:00
parent ec5f5a1d1a
commit 056eed8c70
Signed by: sudharshan
GPG Key ID: C861C97AAF3D9559
3 changed files with 159 additions and 29 deletions

View File

@ -11,16 +11,19 @@ import WebRTC
public enum RTCWrapperState {
case disconnected
case connecting
case ready
case connected
}
class PeerConnectionWrapper: NSObject{
// State
var state: RTCWrapperState = .disconnected
var remoteUserId: String?
var remoteDeviceId: String?
// WebRTC initialization
var connectionFactory: RTCPeerConnectionFactory?
var signalingApiProvider: SignalingApiProvider?
var peerConnection: RTCPeerConnection?
var remoteIceCandidates: [RTCIceCandidate] = []
@ -38,15 +41,19 @@ class PeerConnectionWrapper: NSObject{
super.init()
}
public convenience init(connectionFactory: RTCPeerConnectionFactory) {
public convenience init(connectionFactory: RTCPeerConnectionFactory, signalingApiProvider: SignalingApiProvider, remoteUserId: String, remoteDeviceId: String) {
self.init()
self.connectionFactory = connectionFactory
self.signalingApiProvider = signalingApiProvider
self.remoteUserId = remoteUserId
self.remoteDeviceId = remoteDeviceId
initialisePeerConnection()
}
public func connect() {
if let peerConnection = self.peerConnection {
self.state = .connecting
self.state = .connected
let localStream = self.localStream()
peerConnection.add(localStream)
}
@ -58,10 +65,11 @@ class PeerConnectionWrapper: NSObject{
if let stream = peerConnection.localStreams.first {
peerConnection.remove(stream)
}
self.state = .disconnected
}
}
public func createOffer() {
public func createOffer(userId: String, deviceId: String) {
if let peerConnection = self.peerConnection {
peerConnection.offer(for: self.channelConstraint, completionHandler: { [weak self] (sdp, error) in
@ -69,15 +77,16 @@ class PeerConnectionWrapper: NSObject{
guard self != nil else { return }
if let error = error {
// Throw an error or smth
print(error)
} else {
// Use the sdp generated
self?.signalingApiProvider?.postDataToUser(userId: userId, deviceId: deviceId, data: sdp!.sdp, event: "offer")
}
})
}
}
public func handleAnswer(withRemoteSDP remoteSdp: String) {
public func handleAnswer(remoteSdp: String) {
if let peerConnection = self.peerConnection {
let sessionDescription = RTCSessionDescription.init(type: .answer, sdp: remoteSdp)
@ -87,15 +96,16 @@ class PeerConnectionWrapper: NSObject{
if let error = error {
// Throw an error
print(error)
} else {
// handle the remote sdp
this.state = .connected
this.state = .ready
}
})
}
}
public func createAnswerForOffer(withRemoteSDP remoteSdp: String) {
public func createAnswerForOffer(userId: String, deviceId: String, remoteSdp: String) {
if let peerConnection = self.peerConnection {
let sessionDescription = RTCSessionDescription.init(type: .answer, sdp: remoteSdp)
peerConnection.setRemoteDescription(sessionDescription, completionHandler: { [weak self] (error) in
@ -104,6 +114,7 @@ class PeerConnectionWrapper: NSObject{
if let error = error {
// Throw an error
print(error)
} else {
// handle the remote sdp
@ -112,9 +123,11 @@ class PeerConnectionWrapper: NSObject{
{ (sdp, error) in
if let error = error {
// Throw an error
print(error)
} else {
// handle generated local sdp
this.state = .connected
self?.signalingApiProvider?.postDataToUser(userId: userId, deviceId: deviceId, data: sdp!.sdp, event: "answer")
this.state = .ready
}
})
}
@ -182,7 +195,8 @@ extension PeerConnectionWrapper: RTCPeerConnectionDelegate {
}
func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate) {
<#code#>
let candidateString = "\(candidate.sdp)-\(candidate.sdpMLineIndex)-\(candidate.sdpMid ?? "")"
self.signalingApiProvider?.postDataToUser(userId: remoteUserId!, deviceId: remoteDeviceId!, data: candidateString, event: "ice-candidate")
}
func peerConnection(_ peerConnection: RTCPeerConnection, didRemove candidates: [RTCIceCandidate]) {

View File

@ -12,26 +12,58 @@ import WebRTC
class PeerManager: NSObject {
// WebRTC initialization
var connectionFactory: RTCPeerConnectionFactory?
var signalingApiProvider: SignalingApiProvider?
var eventSource: EventSource = EventSource(url: "https://staging.beepvoice.app/signal")
// List of users
var peerList: [String: PeerConnectionWrapper] = [:]
var whitePeerList: [String] = []
var activeConversation: String?
public override init() {
super.init()
initialisePeerConnectionFactory()
}
public func join(conversationId: String, usersIds: [String]) {
public func join(conversationId: String) {
let userOpList = signalingApiProvider?.getConversationUsers(conversationId: conversationId)
activeConversation = conversationId
guard let userList = userOpList else {
// Error incorrect conversation ID
return
}
for user in userList {
whitePeerList.append(user)
let deviceOpList = signalingApiProvider?.getUserDevices(userId: user)
guard let deviceList = deviceOpList else {
// Error incorrect user ID
return
}
for device in deviceList {
let connection: PeerConnectionWrapper = PeerConnectionWrapper(connectionFactory: self.connectionFactory!, signalingApiProvider: self.signalingApiProvider!, remoteUserId: user, remoteDeviceId: device)
self.peerList["\(user)-\(device)"] = connection
connection.createOffer(userId: user, deviceId: device)
}
}
}
public func exit() {
for (_, connection) in peerList {
connection.disconnect()
}
whitePeerList = []
peerList = [:]
activeConversation = nil
}
public func get() -> String {
public func get() -> String? {
return activeConversation
}
}
@ -43,15 +75,73 @@ private extension PeerManager {
func initialiseEventSource() {
eventSource.addEventListener("offer") { (id, event, data) in
guard let id = id, let data = data else {
// Incorrect packet type error
}
// Handling offers, if in list accept
if self.whitePeerList.contains(id) {
// Split id into user and device
let idArr = id.components(separatedBy: "-")
// Check id format
if idArr.count != 2 {
// Incorrect id format error
return
}
let connection: PeerConnectionWrapper = PeerConnectionWrapper(connectionFactory: self.connectionFactory!, signalingApiProvider: self.signalingApiProvider!, remoteUserId: idArr[0], remoteDeviceId: idArr[1])
self.peerList[id] = connection
connection.createAnswerForOffer(userId: idArr[0], deviceId: idArr[1], remoteSdp: data)
connection.connect()
}
}
eventSource.addEventListener("answer") { (id, event, data) in
guard let id = id, let data = data else {
// Incorrect packet type error
}
// Handling answers, if in list accept
if self.whitePeerList.contains(id) {
let connection: PeerConnectionWrapper = self.peerList[id]!
connection.handleAnswer(remoteSdp: data)
connection.connect()
}
}
eventSource.addEventListener("ice-candidate") { (id, event, data) in
guard let id = id, let data = data else {
// Incorrect packet type error
return
}
// Handling ice candidates, if in list accept
if self.whitePeerList.contains(id) {
let connection: PeerConnectionWrapper = self.peerList[id]!
let dataArr = data.components(separatedBy: "-")
// Check dataArr size of 3
if dataArr.count != 3 {
// Incorrect data format error
return
}
// Convert sdpMLineIndex to Int32
guard let sdpMLineIndex = Int32(dataArr[1]) else {
// Invalid sdpMLineIndex error
return
}
let iceCandidate = RTCIceCandidate(sdp: dataArr[0], sdpMLineIndex: sdpMLineIndex, sdpMid: dataArr[1])
connection.addIceCandidate(iceCandidate: iceCandidate)
}
}
}
}

View File

@ -7,6 +7,7 @@
//
import Foundation
import WebRTC
class SignalingApiProvider: NSObject {
var authToken: String
@ -20,8 +21,9 @@ class SignalingApiProvider: NSObject {
self.authToken = authToken
}
public func getUserDevices(userId: String) -> [String] {
public func getUserDevices(userId: String) -> [String]? {
let url: URL = URL(string: "http://staging.beepvoice.app/user/\(userId)/devices")!
var deviceList: [String] = []
var request = URLRequest(url: url)
request.addValue("Bearer \(authToken)", forHTTPHeaderField: "Authorization")
request.httpMethod = "GET"
@ -33,9 +35,17 @@ class SignalingApiProvider: NSObject {
print(response)
do {
if let jsonResult = try JSONSerialization.jsonObject(with: dataVal, options: []) as? NSDictionary {
if let jsonResult = try JSONSerialization.jsonObject(with: dataVal, options: []) as? [String] {
print("Synchronous\(jsonResult)")
// Need code to convert this to an array of strings
// convert this to an array of strings
for device in jsonResult {
deviceList.append(device)
}
return deviceList
} else {
// Invalid response format
return nil
}
} catch let error as NSError {
print(error.localizedDescription)
@ -43,10 +53,13 @@ class SignalingApiProvider: NSObject {
} catch let error as NSError {
print(error.localizedDescription)
}
return nil
}
public func getConversationUsers(conversationId: String) -> [String] {
public func getConversationUsers(conversationId: String) -> [String]? {
let url: URL = URL(string: "http://staging.beepvoice.app/user/conversation/\(conversationId)/member")!
var userList: [String] = []
var request = URLRequest(url: url)
request.addValue("Bearer \(authToken)", forHTTPHeaderField: "Authorization")
request.httpMethod = "GET"
@ -58,9 +71,21 @@ class SignalingApiProvider: NSObject {
print(response)
do {
if let jsonResult = try JSONSerialization.jsonObject(with: dataVal, options: []) as? NSDictionary {
if let jsonResult = try JSONSerialization.jsonObject(with: dataVal, options: []) as? [Any] {
print("Synchronous\(jsonResult)")
// Need code to convert this to an array of strings
for user in jsonResult {
if let userObject = user as? [String: String] {
guard let userId = userObject["id"] else {
// Invalid response format
return nil
}
userList.append(userId)
}
}
return userList
}
} catch let error as NSError {
print(error.localizedDescription)
@ -68,28 +93,29 @@ class SignalingApiProvider: NSObject {
} catch let error as NSError {
print(error.localizedDescription)
}
return nil
}
public func postDataToUser(userId: String, deviceId: String) {
// CHECK FOR WHEN DEVICE IS UNAVAILABLE
public func postDataToUser(userId: String, deviceId: String, data: String, event: String) {
let url: URL = URL(string: "http://staging.beepvoice.app/user/\(userId)/device/\(deviceId)")!
// prepare json data
let json: [String: Any] = ["event": event, "data": data]
let jsonData = try? JSONSerialization.data(withJSONObject: json)
var request = URLRequest(url: url)
request.addValue("Bearer \(authToken)", forHTTPHeaderField: "Authorization")
request.httpMethod = "POST"
request.httpBody = jsonData
let response: AutoreleasingUnsafeMutablePointer<URLResponse?>
do {
let dataVal = try NSURLConnection.sendSynchronousRequest(request, returning: response)
let _ = try NSURLConnection.sendSynchronousRequest(request, returning: response)
print(response)
do {
if let jsonResult = try JSONSerialization.jsonObject(with: dataVal, options: []) as? NSDictionary {
print("Synchronous\(jsonResult)")
// Need code to convert this to an array of strings
}
} catch let error as NSError {
print(error.localizedDescription)
}
} catch let error as NSError {
print(error.localizedDescription)
}