working webrtc
parent
fe2f10fecf
commit
1d4d369b6e
|
@ -43,6 +43,8 @@ target 'Runner' do
|
|||
# Custom Pods
|
||||
source 'https://github.com/CocoaPods/Specs.git'
|
||||
pod 'GoogleWebRTC'
|
||||
pod 'Just'
|
||||
pod 'PercentEncoder'
|
||||
|
||||
# Flutter Pods
|
||||
generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig')
|
||||
|
|
|
@ -281,6 +281,8 @@
|
|||
"${BUILT_PRODUCTS_DIR}/FMDB/FMDB.framework",
|
||||
"${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework",
|
||||
"${PODS_ROOT}/GoogleWebRTC/Frameworks/frameworks/WebRTC.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/Just/Just.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/PercentEncoder/PercentEncoder.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/shared_preferences/shared_preferences.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/sqflite/sqflite.framework",
|
||||
);
|
||||
|
@ -289,6 +291,8 @@
|
|||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FMDB.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebRTC.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Just.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PercentEncoder.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite.framework",
|
||||
);
|
||||
|
|
|
@ -11,8 +11,8 @@ import WebRTC
|
|||
|
||||
public enum RTCWrapperState {
|
||||
case disconnected
|
||||
case ready
|
||||
case connected
|
||||
case connecting
|
||||
}
|
||||
|
||||
class PeerConnectionWrapper: NSObject{
|
||||
|
@ -53,7 +53,7 @@ class PeerConnectionWrapper: NSObject{
|
|||
|
||||
public func connect() {
|
||||
if let peerConnection = self.peerConnection {
|
||||
self.state = .connected
|
||||
self.state = .connecting
|
||||
let localStream = self.localStream()
|
||||
peerConnection.add(localStream)
|
||||
}
|
||||
|
@ -80,6 +80,7 @@ class PeerConnectionWrapper: NSObject{
|
|||
print(error)
|
||||
} else {
|
||||
// Use the sdp generated
|
||||
self?.handleLocalSdpSet(sdp: sdp)
|
||||
self?.signalingApiProvider?.postDataToUser(userId: userId, deviceId: deviceId, data: sdp!.sdp, event: "offer")
|
||||
}
|
||||
})
|
||||
|
@ -100,7 +101,7 @@ class PeerConnectionWrapper: NSObject{
|
|||
} else {
|
||||
// handle the remote sdp
|
||||
this.handleRemoteDescriptionSet()
|
||||
this.state = .ready
|
||||
this.state = .connected
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -108,7 +109,7 @@ class PeerConnectionWrapper: NSObject{
|
|||
|
||||
public func createAnswerForOffer(userId: String, deviceId: String, remoteSdp: String) {
|
||||
if let peerConnection = self.peerConnection {
|
||||
let sessionDescription = RTCSessionDescription.init(type: .answer, sdp: remoteSdp)
|
||||
let sessionDescription = RTCSessionDescription.init(type: .offer, sdp: remoteSdp)
|
||||
peerConnection.setRemoteDescription(sessionDescription, completionHandler: { [weak self] (error) in
|
||||
// Exit if this object doesn't exist anymore cause it is a weak link
|
||||
guard let this = self else { return }
|
||||
|
@ -119,6 +120,7 @@ class PeerConnectionWrapper: NSObject{
|
|||
} else {
|
||||
// handle the remote sdp
|
||||
this.handleRemoteDescriptionSet()
|
||||
|
||||
// create answer
|
||||
peerConnection.answer(for: this.channelConstraint, completionHandler:
|
||||
{ (sdp, error) in
|
||||
|
@ -127,8 +129,9 @@ class PeerConnectionWrapper: NSObject{
|
|||
print(error)
|
||||
} else {
|
||||
// handle generated local sdp
|
||||
self?.handleLocalSdpSet(sdp: sdp)
|
||||
self?.signalingApiProvider?.postDataToUser(userId: userId, deviceId: deviceId, data: sdp!.sdp, event: "answer")
|
||||
this.state = .ready
|
||||
this.state = .connected
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -159,6 +162,17 @@ private extension PeerConnectionWrapper {
|
|||
self.peerConnection = self.connectionFactory?.peerConnection(with: configuration, constraints: self.connectionConstraint, delegate: self)
|
||||
}
|
||||
|
||||
func handleLocalSdpSet(sdp: RTCSessionDescription?) {
|
||||
guard let sdp = sdp else{
|
||||
return
|
||||
}
|
||||
|
||||
self.peerConnection?.setLocalDescription(sdp, completionHandler: {[weak self] (error) in
|
||||
guard let _ = self, let error = error else { return }
|
||||
print(error)
|
||||
})
|
||||
}
|
||||
|
||||
func handleRemoteDescriptionSet() {
|
||||
for iceCandidate in self.remoteIceCandidates {
|
||||
self.peerConnection?.add(iceCandidate)
|
||||
|
@ -204,7 +218,7 @@ extension PeerConnectionWrapper: RTCPeerConnectionDelegate {
|
|||
}
|
||||
|
||||
func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate) {
|
||||
let candidateString = "\(candidate.sdp)-\(candidate.sdpMLineIndex)-\(candidate.sdpMid ?? "")"
|
||||
let candidateString = "\(candidate.sdp)::\(candidate.sdpMLineIndex)::\(candidate.sdpMid ?? "")"
|
||||
self.signalingApiProvider?.postDataToUser(userId: remoteUserId!, deviceId: remoteDeviceId!, data: candidateString, event: "ice-candidate")
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import Foundation
|
||||
import WebRTC
|
||||
import PercentEncoder
|
||||
|
||||
class PeerManager: NSObject {
|
||||
// WebRTC initialization
|
||||
|
@ -30,6 +31,7 @@ class PeerManager: NSObject {
|
|||
public func initializeToken(authToken: String) {
|
||||
self.signalingApiProvider = SignalingApiProvider(authToken: authToken)
|
||||
self.eventSource = EventSource(url: "http://localhost/signal/subscribe?token=\(authToken)")
|
||||
initialiseEventSource()
|
||||
}
|
||||
|
||||
public func join(conversationId: String) {
|
||||
|
@ -42,8 +44,6 @@ class PeerManager: NSObject {
|
|||
}
|
||||
|
||||
for user in userList {
|
||||
whitePeerList.append(user)
|
||||
|
||||
let deviceOpList = signalingApiProvider?.getUserDevices(userId: user)
|
||||
|
||||
guard let deviceList = deviceOpList else {
|
||||
|
@ -53,7 +53,10 @@ class PeerManager: NSObject {
|
|||
|
||||
for device in deviceList {
|
||||
let connection: PeerConnectionWrapper = PeerConnectionWrapper(connectionFactory: self.connectionFactory!, signalingApiProvider: self.signalingApiProvider!, remoteUserId: user, remoteDeviceId: device)
|
||||
self.peerList["\(user)-\(device)"] = connection
|
||||
self.peerList["\(user):\(device)"] = connection
|
||||
whitePeerList.append("\(user):\(device)")
|
||||
|
||||
connection.connect()
|
||||
connection.createOffer(userId: user, deviceId: device)
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +80,6 @@ class PeerManager: NSObject {
|
|||
private extension PeerManager {
|
||||
func initialiseEventSource() {
|
||||
eventSource?.addEventListener("offer") { (id, event, data) in
|
||||
print(data!)
|
||||
guard let id = id, let data = data else {
|
||||
// Incorrect packet type error
|
||||
return
|
||||
|
@ -86,7 +88,7 @@ private extension PeerManager {
|
|||
// Handling offers, if in list accept
|
||||
if self.whitePeerList.contains(id) {
|
||||
// Split id into user and device
|
||||
let idArr = id.components(separatedBy: "-")
|
||||
let idArr = id.components(separatedBy: ":")
|
||||
|
||||
// Check id format
|
||||
if idArr.count != 2 {
|
||||
|
@ -94,12 +96,16 @@ private extension PeerManager {
|
|||
return
|
||||
}
|
||||
|
||||
// Formatting data
|
||||
let decoded_data = PercentEncoding.decodeURI.evaluate(string: data)
|
||||
|
||||
print("OFFER!")
|
||||
|
||||
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()
|
||||
connection.createAnswerForOffer(userId: idArr[0], deviceId: idArr[1], remoteSdp: decoded_data)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,9 +118,13 @@ private extension PeerManager {
|
|||
|
||||
// Handling answers, if in list accept
|
||||
if self.whitePeerList.contains(id) {
|
||||
// Formatting data
|
||||
let decoded_data = PercentEncoding.decodeURI.evaluate(string: data)
|
||||
|
||||
print("ANSWER")
|
||||
|
||||
let connection: PeerConnectionWrapper = self.peerList[id]!
|
||||
connection.handleAnswer(remoteSdp: data)
|
||||
connection.connect()
|
||||
connection.handleAnswer(remoteSdp: decoded_data)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,7 +139,12 @@ private extension PeerManager {
|
|||
if self.whitePeerList.contains(id) {
|
||||
let connection: PeerConnectionWrapper = self.peerList[id]!
|
||||
|
||||
let dataArr = data.components(separatedBy: "-")
|
||||
// Formatting data
|
||||
let decoded_data = PercentEncoding.decodeURI.evaluate(string: data)
|
||||
|
||||
print("ICE!")
|
||||
|
||||
let dataArr = decoded_data.components(separatedBy: "::")
|
||||
|
||||
// Check dataArr size of 3
|
||||
if dataArr.count != 3 {
|
||||
|
@ -143,7 +158,7 @@ private extension PeerManager {
|
|||
return
|
||||
}
|
||||
|
||||
let iceCandidate = RTCIceCandidate(sdp: dataArr[0], sdpMLineIndex: sdpMLineIndex, sdpMid: dataArr[1])
|
||||
let iceCandidate = RTCIceCandidate(sdp: dataArr[0], sdpMLineIndex: sdpMLineIndex, sdpMid: dataArr[2])
|
||||
connection.addIceCandidate(iceCandidate: iceCandidate)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import Foundation
|
||||
import WebRTC
|
||||
import Just
|
||||
|
||||
class SignalingApiProvider: NSObject {
|
||||
var authToken: String?
|
||||
|
@ -22,18 +23,13 @@ class SignalingApiProvider: NSObject {
|
|||
}
|
||||
|
||||
public func getUserDevices(userId: String) -> [String]? {
|
||||
let url: URL = URL(string: "http://localhost/signal/user/\(userId)/devices")!
|
||||
var deviceList: [String] = []
|
||||
var request = URLRequest(url: url)
|
||||
request.addValue("Bearer \(authToken ?? "0")", forHTTPHeaderField: "Authorization")
|
||||
request.httpMethod = "GET"
|
||||
let response = Just.get("http://localhost/signal/user/\(userId)/devices",
|
||||
headers: ["Authorization": "Bearer \(authToken ?? "0")"])
|
||||
|
||||
var response: URLResponse?
|
||||
|
||||
do {
|
||||
let dataVal = try NSURLConnection.sendSynchronousRequest(request, returning: &response)
|
||||
if(response.ok) {
|
||||
do {
|
||||
if let jsonResult = try JSONSerialization.jsonObject(with: dataVal, options: []) as? [String] {
|
||||
if let jsonResult = try JSONSerialization.jsonObject(with: response.content!, options: []) as? [String] {
|
||||
// convert this to an array of strings
|
||||
for device in jsonResult {
|
||||
deviceList.append(device)
|
||||
|
@ -47,26 +43,18 @@ class SignalingApiProvider: NSObject {
|
|||
} catch let error as NSError {
|
||||
print(error.localizedDescription)
|
||||
}
|
||||
} catch let error as NSError {
|
||||
print(error.localizedDescription)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
public func getConversationUsers(conversationId: String) -> [String]? {
|
||||
let url: URL = URL(string: "http://localhost/core/user/conversation/\(conversationId)/member")!
|
||||
var userList: [String] = []
|
||||
var request = URLRequest(url: url)
|
||||
request.addValue("Bearer \(authToken ?? "0")", forHTTPHeaderField: "Authorization")
|
||||
request.httpMethod = "GET"
|
||||
let response = Just.get("http://localhost/core/user/conversation/\(conversationId)/member",
|
||||
headers: ["Authorization": "Bearer \(authToken ?? "0")"])
|
||||
|
||||
var response: URLResponse?
|
||||
|
||||
do {
|
||||
let dataVal = try NSURLConnection.sendSynchronousRequest(request, returning: &response)
|
||||
if (response.ok) {
|
||||
do {
|
||||
if let jsonResult = try JSONSerialization.jsonObject(with: dataVal, options: []) as? [Any] {
|
||||
if let jsonResult = try JSONSerialization.jsonObject(with: response.content!, options: []) as? [Any] {
|
||||
// Need code to convert this to an array of strings
|
||||
for user in jsonResult {
|
||||
if let userObject = user as? [String: String] {
|
||||
|
@ -84,34 +72,14 @@ class SignalingApiProvider: NSObject {
|
|||
} catch let error as NSError {
|
||||
print(error.localizedDescription)
|
||||
}
|
||||
} catch let error as NSError {
|
||||
print(error.localizedDescription)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CHECK FOR WHEN DEVICE IS UNAVAILABLE
|
||||
public func postDataToUser(userId: String, deviceId: String, data: String, event: String) {
|
||||
let url: URL = URL(string: "http://localhost/signal/user/\(userId)/device/\(deviceId)")!
|
||||
|
||||
// prepare json data
|
||||
let json: [String: Any] = ["event": event, "data": data]
|
||||
print(json)
|
||||
|
||||
let jsonData = try? JSONSerialization.data(withJSONObject: json)
|
||||
|
||||
var request = URLRequest(url: url)
|
||||
request.addValue("Bearer \(authToken ?? "0")", forHTTPHeaderField: "Authorization")
|
||||
request.httpMethod = "POST"
|
||||
request.httpBody = jsonData
|
||||
|
||||
var response: URLResponse?
|
||||
|
||||
do {
|
||||
let _ = try NSURLConnection.sendSynchronousRequest(request, returning: &response)
|
||||
} catch let error as NSError {
|
||||
print(error.localizedDescription)
|
||||
}
|
||||
Just.post("http://localhost/signal/user/\(userId)/device/\(deviceId)",
|
||||
json: ["event": event, "data": data],
|
||||
headers: ["Authorization": "Bearer \(authToken ?? "0")"])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import "package:rxdart/rxdart.dart";
|
|||
import "package:http/http.dart" as http;
|
||||
|
||||
import "../models/ping_model.dart";
|
||||
import "../models/user_model.dart";
|
||||
import "../services/login_manager.dart";
|
||||
import "../resources/contact_api_provider.dart";
|
||||
import "../../settings.dart";
|
||||
|
|
Loading…
Reference in New Issue