This commit is contained in:
Ali 2020-10-16 15:10:19 +04:00
parent 303fa91caa
commit 799bae0cc2
6 changed files with 379 additions and 305 deletions

View File

@ -13,6 +13,7 @@ import ItemListUI
import PresentationDataUtils
import OverlayStatusController
import AccountContext
import TelegramCallsUI
@objc private final class DebugControllerMailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate {
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
@ -74,6 +75,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
case alternativeFolderTabs(Bool)
case playerEmbedding(Bool)
case playlistPlayback(Bool)
case voiceConference
case preferredVideoCodec(Int, String, String?, Bool)
case disableVideoAspectScaling(Bool)
case enableVoipTcp(Bool)
@ -90,7 +92,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return DebugControllerSection.logging.rawValue
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
return DebugControllerSection.experiments.rawValue
case .clearTips, .reimport, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .alternativeFolderTabs, .playerEmbedding, .playlistPlayback:
case .clearTips, .reimport, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .alternativeFolderTabs, .playerEmbedding, .playlistPlayback, .voiceConference:
return DebugControllerSection.experiments.rawValue
case .preferredVideoCodec:
return DebugControllerSection.videoExperiments.rawValue
@ -155,8 +157,10 @@ private enum DebugControllerEntry: ItemListNodeEntry {
return 24
case .playlistPlayback:
return 25
case .voiceConference:
return 26
case let .preferredVideoCodec(index, _, _, _):
return 26 + index
return 27 + index
case .disableVideoAspectScaling:
return 100
case .enableVoipTcp:
@ -648,6 +652,15 @@ private enum DebugControllerEntry: ItemListNodeEntry {
})
}).start()
})
case .voiceConference:
return ItemListDisclosureItem(presentationData: presentationData, title: "Voice Conference (Test)", label: "", sectionId: self.section, style: .blocks, action: {
guard let context = arguments.context else {
return
}
let controller = GroupCallController(context: context)
controller.navigationPresentation = .modal
arguments.pushController(controller)
})
case let .preferredVideoCodec(_, title, value, isSelected):
return ItemListCheckboxItem(presentationData: presentationData, title: title, style: .right, checked: isSelected, zeroSeparatorInsets: false, sectionId: self.section, action: {
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
@ -725,6 +738,8 @@ private func debugControllerEntries(presentationData: PresentationData, loggingS
entries.append(.playerEmbedding(experimentalSettings.playerEmbedding))
entries.append(.playlistPlayback(experimentalSettings.playlistPlayback))
entries.append(.voiceConference)
let codecs: [(String, String?)] = [
("No Preference", nil),
("H265", "H265"),

View File

@ -0,0 +1,109 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
import TelegramPresentationData
import TelegramUIPreferences
import TelegramVoip
import TelegramAudio
import AccountContext
public final class GroupCallController: ViewController {
private final class Node: ViewControllerTracingNode {
private let context: AccountContext
private let presentationData: PresentationData
private var callContext: GroupCallContext?
private var callDisposable: Disposable?
private var memberCountDisposable: Disposable?
private let audioSessionActive = Promise<Bool>(false)
private var memberCount: Int = 0
private let memberCountNode: ImmediateTextNode
private var validLayout: ContainerViewLayout?
init(context: AccountContext) {
self.context = context
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.memberCountNode = ImmediateTextNode()
super.init()
self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
self.addSubnode(self.memberCountNode)
let audioSessionActive = self.audioSessionActive
self.callDisposable = self.context.sharedContext.mediaManager.audioSession.push(audioSessionType: .voiceCall, manualActivate: { audioSessionControl in
audioSessionControl.activate({ _ in })
audioSessionActive.set(.single(true))
}, deactivate: {
return Signal { subscriber in
subscriber.putCompletion()
return EmptyDisposable
}
}, availableOutputsChanged: { _, _ in
})
let callContext = GroupCallContext(audioSessionActive: self.audioSessionActive.get())
self.callContext = callContext
memberCountDisposable = (callContext.memberCount
|> deliverOnMainQueue).start(next: { [weak self] value in
guard let strongSelf = self else {
return
}
strongSelf.memberCount = value
if let layout = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout, transition: .immediate)
}
})
}
deinit {
self.callDisposable?.dispose()
self.memberCountDisposable?.dispose()
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
self.validLayout = layout
self.memberCountNode.attributedText = NSAttributedString(string: "Members: \(self.memberCount)", font: Font.regular(17.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
let textSize = self.memberCountNode.updateLayout(CGSize(width: layout.size.width - 16.0 * 2.0, height: 100.0))
transition.updateFrameAdditiveToCenter(node: self.memberCountNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - textSize.width) / 2.0), y: floor((layout.size.width - textSize.width) / 2.0)), size: textSize))
}
}
private let context: AccountContext
private let presentationData: PresentationData
private var controllerNode: Node {
return self.displayNode as! Node
}
public init(context: AccountContext) {
self.context = context
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func loadDisplayNode() {
self.displayNode = Node(context: self.context)
self.displayNodeDidLoad()
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
self.controllerNode.containerLayoutUpdated(layout, transition: transition)
}
}

View File

@ -497,6 +497,9 @@ private extension ConferenceDescription.Content.Channel.PayloadType {
result["name"] = self.name
result["channels"] = self.channels
result["clockrate"] = self.clockrate
result["rtcp-fbs"] = [[
"type": "transport-cc"
] as [String: Any]] as [Any]
if let parameters = self.parameters {
result["parameters"] = parameters
}
@ -544,6 +547,7 @@ private extension ConferenceDescription.Content.Channel {
if !self.rtpHdrExts.isEmpty {
result["rtp-hdrexts"] = self.rtpHdrExts.map { $0.outgoingColibriDescription() }
}
result["rtcp-mux"] = true
return result
}
@ -635,6 +639,21 @@ private extension ConferenceDescription.ChannelBundle {
}
}
private struct RemoteOffer {
struct State: Equatable {
struct Item: Equatable {
var ssrc: Int
var isRemoved: Bool
}
var items: [Item]
}
var sdpList: [String]
var isPartial: Bool
var state: State
}
private extension ConferenceDescription {
func outgoingColibriDescription() -> [String: Any] {
var result: [String: Any] = [:]
@ -646,283 +665,205 @@ private extension ConferenceDescription {
return result
}
func offerSdp(sessionId: UInt32, bundleId: String, bridgeHost: String, transport: ConferenceDescription.Transport, currentSsrcOrder: [Int]) -> (String, [Int])? {
var otherSsrc: [(Bool, Int, String)] = []
func offerSdp(sessionId: UInt32, bundleId: String, bridgeHost: String, transport: ConferenceDescription.Transport, currentState: RemoteOffer.State?) -> RemoteOffer? {
struct Ssrc {
var isMain: Bool
var value: Int
var streamId: String
var isRemoved: Bool
}
func createSdp(sessionId: UInt32, bundleSsrcs: [Ssrc], isPartial: Bool) -> String {
var sdp = ""
func appendSdp(_ string: String) {
if !sdp.isEmpty {
sdp.append("\n")
}
sdp.append(string)
}
appendSdp("v=0")
appendSdp("o=- \(sessionId) 2 IN IP4 0.0.0.0")
appendSdp("s=-")
appendSdp("t=0 0")
appendSdp("a=group:BUNDLE \(bundleSsrcs.map({ "audio\($0.value)" }).joined(separator: " "))")
appendSdp("a=ice-lite")
for ssrc in bundleSsrcs {
appendSdp("m=audio \(ssrc.isMain ? "1" : "0") RTP/SAVPF 111 103 104 126")
if ssrc.isMain {
appendSdp("c=IN IP4 0.0.0.0")
}
appendSdp("a=mid:audio\(ssrc.value)")
if ssrc.isRemoved {
appendSdp("a=inactive")
continue
}
if ssrc.isMain {
appendSdp("a=ice-ufrag:\(transport.ufrag)")
appendSdp("a=ice-pwd:\(transport.pwd)")
for fingerprint in transport.fingerprints {
appendSdp("a=fingerprint:\(fingerprint.hashType) \(fingerprint.fingerprint)")
appendSdp("a=setup:\(fingerprint.setup)")
}
for candidate in transport.candidates {
var candidateString = "a=candidate:"
candidateString.append("\(candidate.foundation) ")
candidateString.append("\(candidate.component) ")
var protocolValue = candidate.protocol
if protocolValue == "ssltcp" {
protocolValue = "tcp"
}
candidateString.append("\(protocolValue) ")
candidateString.append("\(candidate.priority) ")
var ip = candidate.ip
ip = bridgeHost
candidateString.append("\(ip) ")
candidateString.append("\(candidate.port) ")
candidateString.append("typ \(candidate.type) ")
switch candidate.type {
case "srflx", "prflx", "relay":
if let relAddr = candidate.relAddr, let relPort = candidate.relPort {
candidateString.append("raddr \(relAddr) rport \(relPort) ")
}
break
default:
break
}
if protocolValue == "tcp" {
guard let tcpType = candidate.tcpType else {
continue
}
candidateString.append("tcptype \(tcpType) ")
}
candidateString.append("generation \(candidate.generation)")
appendSdp(candidateString)
}
}
appendSdp("a=rtpmap:111 opus/48000/2")
appendSdp("a=rtpmap:103 ISAC/16000")
appendSdp("a=rtpmap:104 ISAC/32000")
appendSdp("a=rtpmap:126 telephone-event/8000")
appendSdp("a=fmtp:111 minptime=10; useinbandfec=1")
appendSdp("a=rtcp:1 IN IP4 0.0.0.0")
appendSdp("a=rtcp-mux")
appendSdp("a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level")
appendSdp("a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time")
appendSdp("a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/transport-wide-cc-02")
appendSdp("a=rtcp-fb:111 transport-cc")
//appendSdp("a=rtcp-fb:111 ccm fir")
//appendSdp("a=rtcp-fb:111 nack")
if ssrc.isMain {
appendSdp("a=sendrecv")
} else {
appendSdp("a=sendonly")
appendSdp("a=bundle-only")
}
appendSdp("a=ssrc-group:FID \(ssrc.value)")
appendSdp("a=ssrc:\(ssrc.value) cname:stream\(ssrc.value)")
appendSdp("a=ssrc:\(ssrc.value) msid:stream\(ssrc.value) audio\(ssrc.value)")
appendSdp("a=ssrc:\(ssrc.value) mslabel:audio\(ssrc.value)")
appendSdp("a=ssrc:\(ssrc.value) label:audio\(ssrc.value)")
}
appendSdp("")
return sdp
}
var ssrcList: [Ssrc] = []
var maybeMainSsrcId: Int?
for content in self.contents {
for channel in content.channels {
if channel.endpoint == bundleId {
otherSsrc.append(contentsOf: channel.sources.map { ssrc in
return (true, ssrc, "stream0")
precondition(channel.sources.count == 1)
ssrcList.append(contentsOf: channel.sources.map { ssrc in
return Ssrc(
isMain: true,
value: ssrc,
streamId: "stream0",
isRemoved: false
)
})
maybeMainSsrcId = channel.sources[0]
} else {
otherSsrc.append(contentsOf: channel.ssrcs.map { ssrc in
return (false, ssrc, channel.channelBundleId)
precondition(channel.ssrcs.count <= 1)
ssrcList.append(contentsOf: channel.ssrcs.map { ssrc in
return Ssrc(
isMain: false,
value: ssrc,
streamId: "stream\(ssrc)",
isRemoved: false
)
})
}
}
}
otherSsrc.sort(by: { lhs, rhs in
/*if let previousLhsIndex = currentSsrcOrder.firstIndex(of: lhs.1), let previousRhsIndex = currentSsrcOrder.firstIndex(of: rhs.1) {
return previousLhsIndex < previousRhsIndex
}
if currentSsrcOrder.contains(lhs.1) != currentSsrcOrder.contains(rhs.1) {
return currentSsrcOrder.contains(lhs.1)
}*/
if lhs.0 != rhs.0 {
return lhs.0
} else {
return lhs.1 < rhs.1
}
})
var sdp = ""
func appendSdp(_ string: String) {
if !sdp.isEmpty {
sdp.append("\n")
}
sdp.append(string)
guard let mainSsrcId = maybeMainSsrcId else {
preconditionFailure()
}
appendSdp("v=0")
appendSdp("o=- \(sessionId) 2 IN IP4 0.0.0.0")
appendSdp("s=-")
appendSdp("t=0 0")
/*appendSdp("a=group:BUNDLE audio0")
do {
appendSdp("m=audio 1 RTP/SAVPF 111 103 104 126")
appendSdp("c=IN IP4 0.0.0.0")
appendSdp("a=rtpmap:111 opus/48000/2")
appendSdp("a=rtpmap:103 ISAC/16000")
appendSdp("a=rtpmap:104 ISAC/32000")
appendSdp("a=rtpmap:126 telephone-event/8000")
appendSdp("a=fmtp:111 minptime=10; useinbandfec=1")
appendSdp("a=rtcp:1 IN IP4 0.0.0.0")
appendSdp("a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level")
appendSdp("a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time")
appendSdp("a=mid:audio0")
appendSdp("a=sendrecv")
appendSdp("a=ice-ufrag:\(transport.ufrag)")
appendSdp("a=ice-pwd:\(transport.pwd)")
for fingerprint in transport.fingerprints {
appendSdp("a=fingerprint:\(fingerprint.hashType) \(fingerprint.fingerprint)")
appendSdp("a=setup:\(fingerprint.setup)")
var bundleSsrcs: [Ssrc] = []
if let currentState = currentState {
for item in currentState.items {
let isRemoved = !ssrcList.contains(where: { $0.value == item.ssrc })
bundleSsrcs.append(Ssrc(
isMain: item.ssrc == mainSsrcId,
value: item.ssrc,
streamId: item.ssrc == mainSsrcId ? "audio0" : "stream\(item.ssrc)",
isRemoved: isRemoved
))
}
for candidate in transport.candidates {
var candidateString = "a=candidate:"
candidateString.append("\(candidate.foundation) ")
candidateString.append("\(candidate.component) ")
var protocolValue = candidate.protocol
if protocolValue == "ssltcp" {
protocolValue = "tcp"
}
candidateString.append("\(protocolValue) ")
candidateString.append("\(candidate.priority) ")
var ip = candidate.ip
ip = bridgeHost
candidateString.append("\(ip) ")
candidateString.append("\(candidate.port) ")
candidateString.append("typ \(candidate.type) ")
switch candidate.type {
case "srflx", "prflx", "relay":
if let relAddr = candidate.relAddr, let relPort = candidate.relPort {
candidateString.append("raddr \(relAddr) rport \(relPort) ")
}
break
default:
break
}
if protocolValue == "tcp" {
guard let tcpType = candidate.tcpType else {
continue
}
candidateString.append("tcptype \(tcpType) ")
}
candidateString.append("generation \(candidate.generation)")
appendSdp(candidateString)
}
for ssrc in bridgeSources {
appendSdp("a=ssrc:\(ssrc) cname:cname\(ssrc)")
appendSdp("a=ssrc:\(ssrc) msid:stream0 audio0")
appendSdp("a=ssrc:\(ssrc) mslabel:stream0")
appendSdp("a=ssrc:\(ssrc) label:audio0")
}
/*for (ssrc, streamId) in otherSsrc {
appendSdp("a=ssrc:\(ssrc) cname:cname\(ssrc)")
appendSdp("a=ssrc:\(ssrc) msid:\(streamId) audio0")
//appendSdp("a=ssrc:\(ssrc) mslabel:\(streamId)")
//appendSdp("a=ssrc:\(ssrc) label:\(streamId)")
}*/
appendSdp("a=rtcp-mux")
}*/
appendSdp("a=group:BUNDLE audio")
appendSdp("a=ice-lite")
appendSdp("a=msid-semantic:WMS *")
appendSdp("m=audio 1 RTP/SAVPF 111 103 104 126")
appendSdp("c=IN IP4 0.0.0.0")
appendSdp("a=ice-ufrag:\(transport.ufrag)")
appendSdp("a=ice-pwd:\(transport.pwd)")
for fingerprint in transport.fingerprints {
appendSdp("a=fingerprint:\(fingerprint.hashType) \(fingerprint.fingerprint)")
appendSdp("a=setup:\(fingerprint.setup)")
}
for candidate in transport.candidates {
var candidateString = "a=candidate:"
candidateString.append("\(candidate.foundation) ")
candidateString.append("\(candidate.component) ")
var protocolValue = candidate.protocol
if protocolValue == "ssltcp" {
protocolValue = "tcp"
for ssrc in ssrcList {
if bundleSsrcs.contains(where: { $0.value == ssrc.value }) {
continue
}
candidateString.append("\(protocolValue) ")
candidateString.append("\(candidate.priority) ")
var ip = candidate.ip
ip = bridgeHost
candidateString.append("\(ip) ")
candidateString.append("\(candidate.port) ")
candidateString.append("typ \(candidate.type) ")
switch candidate.type {
case "srflx", "prflx", "relay":
if let relAddr = candidate.relAddr, let relPort = candidate.relPort {
candidateString.append("raddr \(relAddr) rport \(relPort) ")
}
break
default:
break
}
if protocolValue == "tcp" {
guard let tcpType = candidate.tcpType else {
bundleSsrcs.append(ssrc)
}
var sdpList: [String] = []
sdpList.append(createSdp(sessionId: sessionId, bundleSsrcs: bundleSsrcs, isPartial: false))
/*if currentState == nil {
sdpList.append(createSdp(sessionId: sessionId, bundleSsrcs: bundleSsrcs, isPartial: false))
} else {
for ssrc in bundleSsrcs {
if ssrc.isMain {
continue
}
candidateString.append("tcptype \(tcpType) ")
sdpList.append(createSdp(sessionId: sessionId, bundleSsrcs: [ssrc], isPartial: true))
}
candidateString.append("generation \(candidate.generation)")
appendSdp(candidateString)
}
appendSdp("a=rtpmap:111 opus/48000/2")
appendSdp("a=rtpmap:103 ISAC/16000")
appendSdp("a=rtpmap:104 ISAC/32000")
appendSdp("a=rtpmap:126 telephone-event/8000")
appendSdp("a=fmtp:111 minptime=10; useinbandfec=1")
appendSdp("a=rtcp:1 IN IP4 0.0.0.0")
appendSdp("a=rtcp-mux")
appendSdp("a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level")
appendSdp("a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time")
appendSdp("a=mid:audio")
appendSdp("a=sendrecv")
for (_, ssrc, streamId) in otherSsrc {
appendSdp("a=ssrc-group:FID \(ssrc)")
appendSdp("a=ssrc:\(ssrc) cname:stream\(streamId)")
appendSdp("a=ssrc:\(ssrc) msid:stream\(streamId) audio\(streamId)")
//appendSdp("a=ssrc:\(ssrc) mslabel:stream\(streamId)")
//appendSdp("a=ssrc:\(ssrc) label:audio\(streamId)")
}
/*for (isBridge, ssrc, streamId) in otherSsrc {
let mPort: Int
if isBridge {
mPort = 1
} else {
mPort = 0
}
appendSdp("m=audio \(mPort) RTP/SAVPF 111 103 104 126")
appendSdp("c=IN IP4 0.0.0.0")
if isBridge {
appendSdp("a=sendrecv")
} else {
appendSdp("a=bundle-only")
appendSdp("a=sendonly")
}
appendSdp("a=rtpmap:111 opus/48000/2")
appendSdp("a=rtpmap:103 ISAC/16000")
appendSdp("a=rtpmap:104 ISAC/32000")
appendSdp("a=rtpmap:126 telephone-event/8000")
appendSdp("a=fmtp:111 minptime=10; useinbandfec=1")
appendSdp("a=rtcp:1 IN IP4 0.0.0.0")
appendSdp("a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level")
appendSdp("a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time")
appendSdp("a=mid:audio\(ssrc)")
if isBridge {
for candidate in transport.candidates {
var candidateString = "a=candidate:"
candidateString.append("\(candidate.foundation) ")
candidateString.append("\(candidate.component) ")
var protocolValue = candidate.protocol
if protocolValue == "ssltcp" {
protocolValue = "tcp"
}
candidateString.append("\(protocolValue) ")
candidateString.append("\(candidate.priority) ")
var ip = candidate.ip
ip = bridgeHost
candidateString.append("\(ip) ")
candidateString.append("\(candidate.port) ")
candidateString.append("typ \(candidate.type) ")
switch candidate.type {
case "srflx", "prflx", "relay":
if let relAddr = candidate.relAddr, let relPort = candidate.relPort {
candidateString.append("raddr \(relAddr) rport \(relPort) ")
}
break
default:
break
}
if protocolValue == "tcp" {
guard let tcpType = candidate.tcpType else {
continue
}
candidateString.append("tcptype \(tcpType) ")
}
candidateString.append("generation \(candidate.generation)")
appendSdp(candidateString)
}
}
appendSdp("a=ssrc:\(ssrc) cname:stream\(streamId)")
appendSdp("a=ssrc:\(ssrc) msid:stream\(streamId) audio\(streamId)")
//appendSdp("a=ssrc:\(ssrc) mslabel:stream\(streamId)")
//appendSdp("a=ssrc:\(ssrc) label:audio\(streamId)")
appendSdp("a=rtcp-mux")
}*/
appendSdp("")
return (sdp, otherSsrc.map(\.1))
return RemoteOffer(
sdpList: sdpList,
isPartial: false,
state: RemoteOffer.State(
items: bundleSsrcs.map { ssrc in
RemoteOffer.State.Item(
ssrc: ssrc.value,
isRemoved: ssrc.isRemoved
)
}
)
)
}
mutating func updateLocalChannelFromSdpAnswer(bundleId: String, sdpAnswer: String) {
@ -956,36 +897,6 @@ private extension ConferenceDescription {
return result
}
/*
v=0
o=- 3432551037272164134 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio
a=msid-semantic: WMS stream0
m=audio 9 RTP/SAVPF 103 104 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:XTZl
a=ice-pwd:GS+K9fcajkZ96gy5hCIyx1BV
a=ice-options:trickle
a=fingerprint:sha-256 88:A3:3E:2C:E3:3C:DF:E8:31:1B:59:AA:73:60:D8:EF:E7:FE:0D:F5:B8:F1:79:26:58:A3:D2:93:D9:8C:49:29
a=setup:active
a=mid:audio
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=sendrecv
a=msid:stream0 audio0
a=rtcp-mux
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
a=rtpmap:126 telephone-event/8000
a=ssrc:666769703 cname:g5ORSLYV5oOfoEBX
a=ssrc:666769703 msid:stream0 audio0
a=ssrc:666769703 mslabel:stream0
a=ssrc:666769703 label:audio0
*/
var audioSources: [Int] = []
for line in getLines(prefix: "a=ssrc:") {
let scanner = Scanner(string: line)
@ -1013,7 +924,10 @@ private extension ConferenceDescription {
parameters: [
"fmtp": [
"minptime=10;useinbandfec=1"
] as [Any]
] as [Any],
"rtcp-fbs": [[
"type": "transport-cc"
] as [String: Any]] as [Any]
]
),
ConferenceDescription.Content.Channel.PayloadType(
@ -1045,6 +959,10 @@ private extension ConferenceDescription {
id: 3,
uri: "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"
),
ConferenceDescription.Content.Channel.RtpHdrExt(
id: 5,
uri: "http://www.webrtc.org/experiments/rtp-hdrext/transport-wide-cc-02"
),
]
guard let ufrag = getLines(prefix: "a=ice-ufrag:").first else {
@ -1190,12 +1108,14 @@ public final class GroupCallContext {
private var localBundleId: String?
private var localTransport: ConferenceDescription.Transport?
let memberCount = ValuePromise<Int>(0, ignoreRepeated: true)
init(queue: Queue, audioSessionActive: Signal<Bool, NoError>) {
self.queue = queue
self.sessionId = UInt32.random(in: 0 ..< UInt32(Int32.max))
//self.colibriHost = "192.168.8.118"
self.colibriHost = "192.168.93.24"
self.colibriHost = "192.168.8.118"
//self.colibriHost = "192.168.93.24"
//self.colibriHost = "51.104.206.109"
var relaySdpAnswerImpl: ((String) -> Void)?
@ -1264,7 +1184,7 @@ public final class GroupCallContext {
}))
}
private var currentSsrcOrder: [Int] = []
private var currentOfferState: RemoteOffer.State?
func allocateChannels(conference: ConferenceDescription) {
let bundleId = UUID().uuidString
@ -1368,16 +1288,22 @@ public final class GroupCallContext {
return
}
guard let (offerSdp, updatedOrder) = conference.offerSdp(sessionId: strongSelf.sessionId, bundleId: bundleId, bridgeHost: strongSelf.colibriHost, transport: transport, currentSsrcOrder: strongSelf.currentSsrcOrder) else {
return
}
strongSelf.currentSsrcOrder = updatedOrder
strongSelf.conferenceId = conference.id
strongSelf.localBundleId = bundleId
strongSelf.localTransport = transport
strongSelf.context.setOfferSdp(offerSdp)
//strongSelf.context.emitOffer()
guard let offer = conference.offerSdp(sessionId: strongSelf.sessionId, bundleId: bundleId, bridgeHost: strongSelf.colibriHost, transport: transport, currentState: strongSelf.currentOfferState) else {
return
}
strongSelf.currentOfferState = offer.state
strongSelf.memberCount.set(offer.state.items.filter({ !$0.isRemoved }).count)
for sdp in offer.sdpList {
strongSelf.context.setOfferSdp(sdp, isPartial: offer.isPartial)
}
}))
}
@ -1453,9 +1379,14 @@ public final class GroupCallContext {
return
}
if let (offerSdp, updatedOrder) = conference.offerSdp(sessionId: strongSelf.sessionId, bundleId: localBundleId, bridgeHost: strongSelf.colibriHost, transport: localTransport, currentSsrcOrder: strongSelf.currentSsrcOrder) {
strongSelf.currentSsrcOrder = updatedOrder
strongSelf.context.setOfferSdp(offerSdp)
if let offer = conference.offerSdp(sessionId: strongSelf.sessionId, bundleId: localBundleId, bridgeHost: strongSelf.colibriHost, transport: localTransport, currentState: strongSelf.currentOfferState) {
strongSelf.currentOfferState = offer.state
strongSelf.memberCount.set(offer.state.items.filter({ !$0.isRemoved }).count)
for sdp in offer.sdpList {
strongSelf.context.setOfferSdp(sdp, isPartial: offer.isPartial)
}
}
strongSelf.pollOnceDelayed()
@ -1472,4 +1403,16 @@ public final class GroupCallContext {
return Impl(queue: queue, audioSessionActive: audioSessionActive)
})
}
public var memberCount: Signal<Int, NoError> {
return Signal { subscriber in
let disposable = MetaDisposable()
self.impl.with { impl in
disposable.set(impl.memberCount.get().start(next: { value in
subscriber.putNext(value)
}))
}
return disposable
}
}
}

View File

@ -9,7 +9,8 @@
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue relaySdpAnswer:(void (^ _Nonnull)(NSString * _Nonnull))relaySdpAnswer;
- (void)setOfferSdp:(NSString * _Nonnull)offerSdp;
- (void)emitOffer;
- (void)setOfferSdp:(NSString * _Nonnull)offerSdp isPartial:(bool)isPartial;
@end

View File

@ -36,9 +36,15 @@
return self;
}
- (void)setOfferSdp:(NSString * _Nonnull)offerSdp {
- (void)emitOffer {
if (_instance) {
_instance->setOfferSdp([offerSdp UTF8String]);
_instance->emitOffer();
}
}
- (void)setOfferSdp:(NSString * _Nonnull)offerSdp isPartial:(bool)isPartial {
if (_instance) {
_instance->setOfferSdp([offerSdp UTF8String], isPartial);
}
}

View File

@ -1,4 +1,4 @@
use_gn_build = True
use_gn_build = False
webrtc_libs = [
"libwebrtc.a",