Compare commits
No commits in common. "a02b76c360744bc0b3f5bfbf5697eba7b4adc013" and "5dde87dc133911acae6bbe9fd493799cc3a19a8f" have entirely different histories.
a02b76c360
...
5dde87dc13
3 changed files with 156 additions and 201 deletions
|
|
@ -223,7 +223,7 @@ class LinphoneBridge(
|
||||||
core.uploadBandwidth = 512
|
core.uploadBandwidth = 512
|
||||||
core.downloadBandwidth = 1500
|
core.downloadBandwidth = 1500
|
||||||
|
|
||||||
val preferredAudio = listOf("g729") // in order of preference
|
val preferredAudio = listOf("g729"/*, "opus", "speex", "pcmu", "pcma"*/) // in order of preference
|
||||||
val preferredVideo = listOf("h264", "vp8")
|
val preferredVideo = listOf("h264", "vp8")
|
||||||
|
|
||||||
core.audioPayloadTypes.forEach { pt: PayloadType ->
|
core.audioPayloadTypes.forEach { pt: PayloadType ->
|
||||||
|
|
|
||||||
|
|
@ -80,21 +80,35 @@ public class LiblinphoneFlutterPlugin: NSObject, FlutterPlugin {
|
||||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||||
switch call.method {
|
switch call.method {
|
||||||
case "checkPermissions":
|
case "checkPermissions":
|
||||||
result(linphoneBridge.checkPermissions())
|
let hasPermissions = linphoneBridge.checkPermissions()
|
||||||
|
result(hasPermissions)
|
||||||
|
|
||||||
case "initialize":
|
case "initialize":
|
||||||
linphoneBridge = LinphoneBridge(
|
do {
|
||||||
remoteViewAcquisitor: { [weak self] in self?.acquireRemoteView() },
|
linphoneBridge = LinphoneBridge(
|
||||||
localViewAcquisitor: { [weak self] in self?.acquireLocalView() },
|
remoteViewAcquisitor: { [weak self] in
|
||||||
onRegistrationStateChanged: { [weak self] state in
|
return self?.acquireRemoteView()
|
||||||
self?.registrationEventsChannel?(state)
|
},
|
||||||
},
|
localViewAcquisitor: { [weak self] in
|
||||||
onCallStateChanged: { [weak self] state in
|
return self?.acquireLocalView()
|
||||||
self?.callEventsChannel?(state)
|
},
|
||||||
}
|
onRegistrationStateChanged: { [weak self] state in
|
||||||
)
|
self?.registrationEventsChannel?(state)
|
||||||
linphoneBridge.initializeLinphone()
|
},
|
||||||
result(true)
|
onCallStateChanged: { [weak self] state in
|
||||||
|
self?.callEventsChannel?(state)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
linphoneBridge.initializeLinphone()
|
||||||
|
result(true)
|
||||||
|
} catch {
|
||||||
|
print("[\(LiblinphoneFlutterPlugin.TAG)] initialize error: \(error.localizedDescription)")
|
||||||
|
result(FlutterError(
|
||||||
|
code: "ERROR",
|
||||||
|
message: error.localizedDescription,
|
||||||
|
details: nil
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
case "register":
|
case "register":
|
||||||
guard let args = call.arguments as? [String: Any],
|
guard let args = call.arguments as? [String: Any],
|
||||||
|
|
@ -102,11 +116,30 @@ public class LiblinphoneFlutterPlugin: NSObject, FlutterPlugin {
|
||||||
let password = args["password"] as? String,
|
let password = args["password"] as? String,
|
||||||
let serverIp = args["serverIp"] as? String,
|
let serverIp = args["serverIp"] as? String,
|
||||||
let serverPort = args["serverPort"] as? Int else {
|
let serverPort = args["serverPort"] as? Int else {
|
||||||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing required arguments", details: nil))
|
result(FlutterError(
|
||||||
|
code: "INVALID_ARGUMENTS",
|
||||||
|
message: "Missing required arguments",
|
||||||
|
details: nil
|
||||||
|
))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
linphoneBridge.register(username: username, password: password, serverIp: serverIp, serverPort: serverPort)
|
|
||||||
result(true)
|
do {
|
||||||
|
linphoneBridge.register(
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
serverIp: serverIp,
|
||||||
|
serverPort: serverPort
|
||||||
|
)
|
||||||
|
result(true)
|
||||||
|
} catch {
|
||||||
|
print("[\(LiblinphoneFlutterPlugin.TAG)] register error: \(error.localizedDescription)")
|
||||||
|
result(FlutterError(
|
||||||
|
code: "ERROR",
|
||||||
|
message: error.localizedDescription,
|
||||||
|
details: nil
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
case "unregister":
|
case "unregister":
|
||||||
linphoneBridge.unregister()
|
linphoneBridge.unregister()
|
||||||
|
|
@ -116,114 +149,118 @@ public class LiblinphoneFlutterPlugin: NSObject, FlutterPlugin {
|
||||||
guard let args = call.arguments as? [String: Any],
|
guard let args = call.arguments as? [String: Any],
|
||||||
let callTo = args["callTo"] as? String,
|
let callTo = args["callTo"] as? String,
|
||||||
let isVideoEnabled = args["isVideoEnabled"] as? Bool else {
|
let isVideoEnabled = args["isVideoEnabled"] as? Bool else {
|
||||||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing required arguments", details: nil))
|
result(FlutterError(
|
||||||
|
code: "INVALID_ARGUMENTS",
|
||||||
|
message: "Missing required arguments",
|
||||||
|
details: nil
|
||||||
|
))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try linphoneBridge.makeCall(callTo: callTo, isVideoEnabled: isVideoEnabled)
|
try linphoneBridge.makeCall(callTo: callTo, isVideoEnabled: isVideoEnabled)
|
||||||
result(true)
|
result(true)
|
||||||
} catch {
|
} catch {
|
||||||
result(FlutterError(code: "ERROR", message: error.localizedDescription, details: nil))
|
print("[\(LiblinphoneFlutterPlugin.TAG)] makeCall error: \(error.localizedDescription)")
|
||||||
|
result(FlutterError(
|
||||||
|
code: "ERROR",
|
||||||
|
message: error.localizedDescription,
|
||||||
|
details: nil
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
case "answerCall":
|
case "answerCall":
|
||||||
result(linphoneBridge.answerCall())
|
let success = linphoneBridge.answerCall()
|
||||||
|
result(success)
|
||||||
|
|
||||||
case "hangupCall":
|
case "hangupCall":
|
||||||
result(linphoneBridge.hangupCall())
|
let success = linphoneBridge.hangupCall()
|
||||||
|
result(success)
|
||||||
|
|
||||||
case "inCall":
|
case "inCall":
|
||||||
result(linphoneBridge.inCall())
|
let inCall = linphoneBridge.inCall()
|
||||||
|
result(inCall)
|
||||||
|
|
||||||
case "callType":
|
case "callType":
|
||||||
switch linphoneBridge.callType() {
|
let callType = linphoneBridge.callType()
|
||||||
case .audio: result(0)
|
let ordinal: Int
|
||||||
case .video: result(1)
|
switch callType {
|
||||||
case .unknown: result(2)
|
case .audio:
|
||||||
|
ordinal = 0
|
||||||
|
case .video:
|
||||||
|
ordinal = 1
|
||||||
|
case .unknown:
|
||||||
|
ordinal = 2
|
||||||
}
|
}
|
||||||
|
result(ordinal)
|
||||||
|
|
||||||
case "toggleVideo":
|
case "toggleVideo":
|
||||||
result(linphoneBridge.toggleVideo())
|
let enabled = linphoneBridge.toggleVideo()
|
||||||
|
result(enabled)
|
||||||
|
|
||||||
case "toggleMicrophone":
|
case "toggleMicrophone":
|
||||||
result(linphoneBridge.toggleMicrophone())
|
let enabled = linphoneBridge.toggleMicrophone()
|
||||||
|
result(enabled)
|
||||||
|
|
||||||
case "stop":
|
case "stop":
|
||||||
linphoneBridge.stop()
|
linphoneBridge.stop()
|
||||||
result(true)
|
result(true)
|
||||||
|
|
||||||
case "syncCurrentState":
|
case "syncCurrentState" :
|
||||||
linphoneBridge.syncCurrentState()
|
linphoneBridge.syncCurrentState()
|
||||||
result(true)
|
result(true)
|
||||||
|
|
||||||
case "sendDtmf":
|
case "sendDtmf":
|
||||||
guard let args = call.arguments as? [String: Any],
|
guard let args = call.arguments as? [String: Any],
|
||||||
let tone = args["tone"] as? String else {
|
let tone = args["tone"] as? String else {
|
||||||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing required arguments", details: nil))
|
result(FlutterError(
|
||||||
|
code: "INVALID_ARGUMENTS",
|
||||||
|
message: "Missing required arguments",
|
||||||
|
details: nil
|
||||||
|
))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
result(linphoneBridge.sendDtmf(tone: tone))
|
|
||||||
|
let success = linphoneBridge.sendDtmf(tone: tone)
|
||||||
|
result(success)
|
||||||
|
|
||||||
case "stopCallService":
|
case "stopCallService":
|
||||||
// Android-only; no-op on iOS
|
|
||||||
result(true)
|
result(true)
|
||||||
|
|
||||||
case "setMicGain":
|
case "setMicGain":
|
||||||
guard let args = call.arguments as? [String: Any],
|
guard let args = call.arguments as? [String: Any],
|
||||||
let levelStr = args["level"] as? String,
|
let level = args["level"] as? String else {
|
||||||
let level = Float(levelStr) else {
|
result(FlutterError(
|
||||||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing or invalid 'level'", details: nil))
|
code: "INVALID_ARGUMENTS",
|
||||||
|
message: "Missing required arguments",
|
||||||
|
details: nil
|
||||||
|
))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
linphoneBridge.setMicGain(level: level)
|
linphoneBridge.setMicGain(level: level)
|
||||||
result(true)
|
result(success)
|
||||||
|
|
||||||
case "getMicGain":
|
case "getMicGain":
|
||||||
result(String(linphoneBridge.getMicGain()))
|
result(linphoneBridge.getMicGain())
|
||||||
|
|
||||||
case "setPlaybackGain":
|
case "setPlaybackGain":
|
||||||
guard let args = call.arguments as? [String: Any],
|
guard let args = call.arguments as? [String: Any],
|
||||||
let levelStr = args["level"] as? String,
|
let level = args["level"] as? String else {
|
||||||
let level = Float(levelStr) else {
|
result(FlutterError(
|
||||||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing or invalid 'level'", details: nil))
|
code: "INVALID_ARGUMENTS",
|
||||||
|
message: "Missing required arguments",
|
||||||
|
details: nil
|
||||||
|
))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
linphoneBridge.setPlaybackGain(level: level)
|
linphoneBridge.setPlaybackGain(level: level)
|
||||||
result(true)
|
result(success)
|
||||||
|
|
||||||
case "getPlaybackGain":
|
case "getPlaybackGain":
|
||||||
result(linphoneBridge.getPlaybackGain())
|
result(linphoneBridge.getPlaybackGain())
|
||||||
|
|
||||||
case "setDscp":
|
|
||||||
guard let args = call.arguments as? [String: Any] else {
|
|
||||||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing arguments", details: nil))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// All three are optional — only present keys are applied
|
|
||||||
let sipDscp = args["sipDscp"] as? Int
|
|
||||||
let audioDscp = args["audioDscp"] as? Int
|
|
||||||
let videoDscp = args["videoDscp"] as? Int
|
|
||||||
linphoneBridge.setDscp(sipDscp: sipDscp, audioDscp: audioDscp, videoDscp: videoDscp)
|
|
||||||
result(true)
|
|
||||||
|
|
||||||
case "getDscp":
|
|
||||||
result(linphoneBridge.getDscp())
|
|
||||||
|
|
||||||
case "getCurrentCallStats":
|
|
||||||
result(linphoneBridge.getCurrentCallStats())
|
|
||||||
|
|
||||||
case "getAvailableAudioCodecs":
|
|
||||||
result(linphoneBridge.getAvailableAudioCodecs())
|
|
||||||
|
|
||||||
case "setAudioCodec":
|
|
||||||
guard let args = call.arguments as? [String: Any],
|
|
||||||
let mime = args["mime"] as? String,
|
|
||||||
let clockRate = args["clockRate"] as? Int else {
|
|
||||||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing required arguments", details: nil))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
result(linphoneBridge.setAudioCodec(mime: mime, clockRate: clockRate))
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
result(FlutterMethodNotImplemented)
|
result(FlutterMethodNotImplemented)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ class LinphoneBridge {
|
||||||
let videoStatus = AVCaptureDevice.authorizationStatus(for: .video)
|
let videoStatus = AVCaptureDevice.authorizationStatus(for: .video)
|
||||||
|
|
||||||
if audioStatus != .authorized || videoStatus != .authorized {
|
if audioStatus != .authorized || videoStatus != .authorized {
|
||||||
|
// Request permissions
|
||||||
AVCaptureDevice.requestAccess(for: .audio) { _ in }
|
AVCaptureDevice.requestAccess(for: .audio) { _ in }
|
||||||
AVCaptureDevice.requestAccess(for: .video) { _ in }
|
AVCaptureDevice.requestAccess(for: .video) { _ in }
|
||||||
return false
|
return false
|
||||||
|
|
@ -57,11 +58,14 @@ class LinphoneBridge {
|
||||||
do {
|
do {
|
||||||
let factory = Factory.Instance
|
let factory = Factory.Instance
|
||||||
core = try factory.createCore(configPath: nil, factoryConfigPath: nil, systemContext: nil)
|
core = try factory.createCore(configPath: nil, factoryConfigPath: nil, systemContext: nil)
|
||||||
core.addDelegate(delegate: self)
|
|
||||||
|
|
||||||
core.ipv6Enabled = false
|
core.ipv6Enabled = false
|
||||||
core.agcEnabled = false
|
core.config?.setInt(section: "net", key: "ipv6", value: 0)
|
||||||
|
try? core.config?.sync()
|
||||||
|
|
||||||
|
// Add core listener
|
||||||
|
core.addDelegate(delegate: self)
|
||||||
|
|
||||||
// Enable video
|
// Enable video
|
||||||
core.videoCaptureEnabled = true
|
core.videoCaptureEnabled = true
|
||||||
core.videoDisplayEnabled = true
|
core.videoDisplayEnabled = true
|
||||||
|
|
@ -80,7 +84,7 @@ class LinphoneBridge {
|
||||||
core.downloadBandwidth = 1500
|
core.downloadBandwidth = 1500
|
||||||
|
|
||||||
// Configure audio codecs
|
// Configure audio codecs
|
||||||
let preferredAudio = ["g729"]
|
let preferredAudio = ["opus", "pcmu", "pcma"]
|
||||||
for pt in core.audioPayloadTypes {
|
for pt in core.audioPayloadTypes {
|
||||||
let mime = pt.mimeType.lowercased()
|
let mime = pt.mimeType.lowercased()
|
||||||
let enabled = preferredAudio.contains(mime)
|
let enabled = preferredAudio.contains(mime)
|
||||||
|
|
@ -98,9 +102,9 @@ class LinphoneBridge {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set bitrates
|
// Set bitrates
|
||||||
// core.getPayloadType(type: "opus", rate: -1, channels: 0)?.normalBitrate = 32
|
core.getPayloadType(type: "opus", rate: -1, channels: 0)?.normalBitrate = 32
|
||||||
// core.getPayloadType(type: "h264", rate: -1, channels: 0)?.normalBitrate = 600
|
core.getPayloadType(type: "h264", rate: -1, channels: 0)?.normalBitrate = 600
|
||||||
// core.getPayloadType(type: "vp8", rate: -1, channels: 0)?.normalBitrate = 600
|
core.getPayloadType(type: "vp8", rate: -1, channels: 0)?.normalBitrate = 600
|
||||||
|
|
||||||
// Video settings
|
// Video settings
|
||||||
let preferredVidDef = try factory.createVideoDefinition(width: 720, height: 1280)
|
let preferredVidDef = try factory.createVideoDefinition(width: 720, height: 1280)
|
||||||
|
|
@ -152,23 +156,29 @@ class LinphoneBridge {
|
||||||
throw NSError(domain: "LinphoneBridge", code: 1, userInfo: [NSLocalizedDescriptionKey: "Not registered"])
|
throw NSError(domain: "LinphoneBridge", code: 1, userInfo: [NSLocalizedDescriptionKey: "Not registered"])
|
||||||
}
|
}
|
||||||
|
|
||||||
let factory = Factory.Instance
|
do {
|
||||||
guard let remoteAddress = try? factory.createAddress(addr: callTo) else {
|
let factory = Factory.Instance
|
||||||
throw NSError(domain: "LinphoneBridge", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to create remote address"])
|
guard let remoteAddress = try? factory.createAddress(addr: callTo) else {
|
||||||
|
throw NSError(domain: "LinphoneBridge", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to create remote address"])
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let callParams = try? core.createCallParams(call: nil) else {
|
||||||
|
throw NSError(domain: "LinphoneBridge", code: 3, userInfo: [NSLocalizedDescriptionKey: "Failed to create call params"])
|
||||||
|
}
|
||||||
|
|
||||||
|
callParams.videoEnabled = isVideoEnabled
|
||||||
|
callParams.videoDirection = isVideoEnabled ? .SendRecv : .Inactive
|
||||||
|
|
||||||
|
currentCall = try core.inviteAddressWithParams(addr: remoteAddress, params: callParams)
|
||||||
|
} catch {
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let callParams = try? core.createCallParams(call: nil) else {
|
|
||||||
throw NSError(domain: "LinphoneBridge", code: 3, userInfo: [NSLocalizedDescriptionKey: "Failed to create call params"])
|
|
||||||
}
|
|
||||||
|
|
||||||
callParams.videoEnabled = isVideoEnabled
|
|
||||||
callParams.videoDirection = isVideoEnabled ? .SendRecv : .Inactive
|
|
||||||
|
|
||||||
currentCall = try core.inviteAddressWithParams(addr: remoteAddress, params: callParams)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func answerCall() -> Bool {
|
func answerCall() -> Bool {
|
||||||
guard let call = currentCall else { return false }
|
guard let call = currentCall else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let callParams = try core.createCallParams(call: call)
|
let callParams = try core.createCallParams(call: call)
|
||||||
|
|
@ -193,7 +203,9 @@ class LinphoneBridge {
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleVideo() -> Bool {
|
func toggleVideo() -> Bool {
|
||||||
guard let call = currentCall else { return false }
|
guard let call = currentCall else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let params = try core.createCallParams(call: call)
|
let params = try core.createCallParams(call: call)
|
||||||
|
|
@ -226,12 +238,25 @@ class LinphoneBridge {
|
||||||
}
|
}
|
||||||
|
|
||||||
func callType() -> CallType {
|
func callType() -> CallType {
|
||||||
guard let params = currentCall?.currentParams else { return .unknown }
|
guard let params = currentCall?.currentParams else {
|
||||||
return params.videoEnabled ? .video : .audio
|
return .unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.videoEnabled {
|
||||||
|
return .video
|
||||||
|
} else {
|
||||||
|
return .audio
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendDtmf(tone: String) -> Bool {
|
func sendDtmf(tone: String) -> Bool {
|
||||||
guard let call = currentCall, !tone.isEmpty else { return false }
|
guard let call = currentCall else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
guard !tone.isEmpty else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
let dtmfChar = tone.first!
|
let dtmfChar = tone.first!
|
||||||
do {
|
do {
|
||||||
|
|
@ -248,8 +273,6 @@ class LinphoneBridge {
|
||||||
onCallStateChanged(callState.rawValue)
|
onCallStateChanged(callState.rawValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Gain
|
|
||||||
|
|
||||||
func setMicGain(level: Float) {
|
func setMicGain(level: Float) {
|
||||||
core.micGainDb = level
|
core.micGainDb = level
|
||||||
}
|
}
|
||||||
|
|
@ -265,111 +288,6 @@ class LinphoneBridge {
|
||||||
func getPlaybackGain() -> Float {
|
func getPlaybackGain() -> Float {
|
||||||
return core.playbackGainDb
|
return core.playbackGainDb
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - DSCP
|
|
||||||
|
|
||||||
func setDscp(sipDscp: Int?, audioDscp: Int?, videoDscp: Int?) {
|
|
||||||
if let sip = sipDscp { core.sipDscp = sip }
|
|
||||||
if let audio = audioDscp { core.audioDscp = audio }
|
|
||||||
if let video = videoDscp { core.videoDscp = video }
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDscp() -> [String: Any] {
|
|
||||||
return [
|
|
||||||
"sipDscp": core.sipDscp,
|
|
||||||
"audioDscp": core.audioDscp,
|
|
||||||
"videoDscp": core.videoDscp,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Call Stats
|
|
||||||
|
|
||||||
func getCurrentCallStats() -> [String: Any]? {
|
|
||||||
guard let call = core.currentCall,
|
|
||||||
let stats = call.getStats(type: .Audio) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
"recordTs": Int64(Date().timeIntervalSince1970 * 1000),
|
|
||||||
"remoteAddress": call.remoteAddress?.asString() ?? "",
|
|
||||||
"currentQuality": call.currentQuality,
|
|
||||||
"downloadBandwidth": stats.downloadBandwidth,
|
|
||||||
"estimatedDownloadBandwidth": stats.estimatedDownloadBandwidth,
|
|
||||||
"fecCumulativeLostPacketsNumber": stats.fecCumulativeLostPacketsNumber,
|
|
||||||
"fecDownloadBandwidth": stats.fecDownloadBandwidth,
|
|
||||||
"fecRepairedPacketsNumber": stats.fecRepairedPacketsNumber,
|
|
||||||
"fecUploadBandwidth": stats.fecUploadBandwidth,
|
|
||||||
"iceState": stats.iceState.rawValue,
|
|
||||||
"jitterBufferSizeMs": stats.jitterBufferSizeMs,
|
|
||||||
"latePacketsCumulativeNumber": stats.latePacketsCumulativeNumber,
|
|
||||||
"localLateRate": stats.localLateRate,
|
|
||||||
"localLossRate": stats.localLossRate,
|
|
||||||
"receiverInterarrivalJitter": stats.receiverInterarrivalJitter,
|
|
||||||
"receiverLossRate": stats.receiverLossRate,
|
|
||||||
"roundTripDelay": stats.roundTripDelay,
|
|
||||||
"rtcpDownloadBandwidth": stats.rtcpDownloadBandwidth,
|
|
||||||
"rtcpUploadBandwidth": stats.rtcpUploadBandwidth,
|
|
||||||
"rtpCumPacketLoss": stats.rtpCumPacketLoss,
|
|
||||||
"rtpDiscarded": stats.rtpDiscarded,
|
|
||||||
"rtpHwRecv": stats.rtpHwRecv,
|
|
||||||
"rtpPacketRecv": stats.rtpPacketRecv,
|
|
||||||
"rtpPacketSent": stats.rtpPacketSent,
|
|
||||||
"rtpRecv": stats.rtpRecv,
|
|
||||||
"rtpSent": stats.rtpSent,
|
|
||||||
"senderInterarrivalJitter": stats.senderInterarrivalJitter,
|
|
||||||
"senderLossRate": stats.senderLossRate,
|
|
||||||
"srtpSource": stats.srtpSource.rawValue,
|
|
||||||
"srtpSuite": stats.srtpSuite.rawValue,
|
|
||||||
"uploadBandwidth": stats.uploadBandwidth,
|
|
||||||
"upnpState": stats.upnpState.rawValue,
|
|
||||||
"zrtpAuthTagAlgo": stats.zrtpAuthTagAlgo,
|
|
||||||
"zrtpCipherAlgo": stats.zrtpCipherAlgo,
|
|
||||||
"zrtpHashAlgo": stats.zrtpHashAlgo,
|
|
||||||
"zrtpKeyAgreementAlgo": stats.zrtpKeyAgreementAlgo,
|
|
||||||
"zrtpSasAlgo": stats.zrtpSasAlgo,
|
|
||||||
"isZrtpKeyAgreementAlgoPostQuantum": stats.isZrtpKeyAgreementAlgoPostQuantum,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Codec management
|
|
||||||
|
|
||||||
func getAvailableAudioCodecs() -> [[String: Any]] {
|
|
||||||
return core.audioPayloadTypes.map { pt in
|
|
||||||
[
|
|
||||||
"mimeType": pt.mimeType.lowercased(),
|
|
||||||
"clockRate": pt.clockRate,
|
|
||||||
"enabled": pt.enabled(),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enables the codec matching (mime, clockRate) and disables all others.
|
|
||||||
/// Returns true if the target codec was found and enabled successfully.
|
|
||||||
func setAudioCodec(mime: String, clockRate: Int) -> Bool {
|
|
||||||
var found = false
|
|
||||||
var ok = false
|
|
||||||
|
|
||||||
for pt in core.audioPayloadTypes {
|
|
||||||
let ptMime = pt.mimeType.lowercased()
|
|
||||||
let ptClockRate = pt.clockRate
|
|
||||||
let enable = ptMime == mime && ptClockRate == clockRate
|
|
||||||
if enable {
|
|
||||||
ok = pt.enable(enabled: true) == 0
|
|
||||||
found = true
|
|
||||||
print("\(LinphoneBridge.TAG) setAudioCodec: preferred codec found (\(ptMime) @ \(ptClockRate)) switch: \(ok)")
|
|
||||||
} else {
|
|
||||||
let switchedOk = pt.enable(enabled: false) == 0
|
|
||||||
print("\(LinphoneBridge.TAG) setAudioCodec: \(ptMime) @ \(ptClockRate) switch: \(switchedOk)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
print("\(LinphoneBridge.TAG) setAudioCodec: could not find codec with params \(mime) @ \(clockRate)")
|
|
||||||
}
|
|
||||||
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - CoreDelegate
|
// MARK: - CoreDelegate
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue