mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Update video API
This commit is contained in:
parent
7334cabc08
commit
f4df115850
@ -308,6 +308,67 @@ public enum PresentationGroupCallTone {
|
||||
case recordingStarted
|
||||
}
|
||||
|
||||
public struct PresentationGroupCallRequestedVideo {
|
||||
public enum Quality {
|
||||
case thumbnail
|
||||
case medium
|
||||
case full
|
||||
}
|
||||
|
||||
public var audioSsrc: UInt32
|
||||
public var endpointId: String
|
||||
public var videoInformation: String
|
||||
public var quality: Quality
|
||||
}
|
||||
|
||||
public extension GroupCallParticipantsContext.Participant {
|
||||
var videoEndpointId: String? {
|
||||
if let jsonParams = self.videoJsonDescription, let jsonData = jsonParams.data(using: .utf8), let json = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
|
||||
if let endpoint = json["endpoint"] as? String {
|
||||
return endpoint
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var presentationEndpointId: String? {
|
||||
if let jsonParams = self.presentationJsonDescription, let jsonData = jsonParams.data(using: .utf8), let json = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
|
||||
if let endpoint = json["endpoint"] as? String {
|
||||
return endpoint
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public extension GroupCallParticipantsContext.Participant {
|
||||
func requestedVideoChannel(quality: PresentationGroupCallRequestedVideo.Quality) -> PresentationGroupCallRequestedVideo? {
|
||||
guard let audioSsrc = self.ssrc else {
|
||||
return nil
|
||||
}
|
||||
guard let videoInformation = self.videoJsonDescription else {
|
||||
return nil
|
||||
}
|
||||
guard let videoEndpointId = self.videoEndpointId else {
|
||||
return nil
|
||||
}
|
||||
return PresentationGroupCallRequestedVideo(audioSsrc: audioSsrc, endpointId: videoEndpointId, videoInformation: videoInformation, quality: quality)
|
||||
}
|
||||
|
||||
func requestedPresentationVideoChannel(quality: PresentationGroupCallRequestedVideo.Quality) -> PresentationGroupCallRequestedVideo? {
|
||||
guard let audioSsrc = self.ssrc else {
|
||||
return nil
|
||||
}
|
||||
guard let videoInformation = self.presentationJsonDescription else {
|
||||
return nil
|
||||
}
|
||||
guard let presentationEndpointId = self.presentationEndpointId else {
|
||||
return nil
|
||||
}
|
||||
return PresentationGroupCallRequestedVideo(audioSsrc: audioSsrc, endpointId: presentationEndpointId, videoInformation: videoInformation, quality: quality)
|
||||
}
|
||||
}
|
||||
|
||||
public protocol PresentationGroupCall: class {
|
||||
var account: Account { get }
|
||||
var accountContext: AccountContext { get }
|
||||
@ -323,6 +384,7 @@ public protocol PresentationGroupCall: class {
|
||||
|
||||
var canBeRemoved: Signal<Bool, NoError> { get }
|
||||
var state: Signal<PresentationGroupCallState, NoError> { get }
|
||||
var stateVersion: Signal<Int, NoError> { get }
|
||||
var summaryState: Signal<PresentationGroupCallSummaryState?, NoError> { get }
|
||||
var members: Signal<PresentationGroupCallMembers?, NoError> { get }
|
||||
var audioLevels: Signal<[(PeerId, UInt32, Float, Bool)], NoError> { get }
|
||||
@ -352,7 +414,7 @@ public protocol PresentationGroupCall: class {
|
||||
func switchVideoCamera()
|
||||
func updateDefaultParticipantsAreMuted(isMuted: Bool)
|
||||
func setVolume(peerId: PeerId, volume: Int32, sync: Bool)
|
||||
func setFullSizeVideo(endpointId: String?)
|
||||
func setRequestedVideoList(items: [PresentationGroupCallRequestedVideo])
|
||||
func setCurrentAudioOutput(_ output: AudioSessionOutput)
|
||||
|
||||
func playTone(_ tone: PresentationGroupCallTone)
|
||||
@ -368,8 +430,6 @@ public protocol PresentationGroupCall: class {
|
||||
|
||||
var inviteLinks: Signal<GroupCallInviteLinks?, NoError> { get }
|
||||
|
||||
var incomingVideoSources: Signal<Set<String>, NoError> { get }
|
||||
|
||||
func makeIncomingVideoView(endpointId: String, completion: @escaping (PresentationCallVideoView?) -> Void)
|
||||
func makeOutgoingVideoView(completion: @escaping (PresentationCallVideoView?) -> Void)
|
||||
|
||||
|
@ -90,26 +90,6 @@ private extension GroupCallParticipantsContext.Participant {
|
||||
}
|
||||
}
|
||||
|
||||
extension GroupCallParticipantsContext.Participant {
|
||||
var videoEndpointId: String? {
|
||||
if let jsonParams = self.videoJsonDescription, let jsonData = jsonParams.data(using: .utf8), let json = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
|
||||
if let endpoint = json["endpoint"] as? String {
|
||||
return endpoint
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var presentationEndpointId: String? {
|
||||
if let jsonParams = self.presentationJsonDescription, let jsonData = jsonParams.data(using: .utf8), let json = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
|
||||
if let endpoint = json["endpoint"] as? String {
|
||||
return endpoint
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public final class AccountGroupCallContextImpl: AccountGroupCallContext {
|
||||
public final class Proxy {
|
||||
public let context: AccountGroupCallContextImpl
|
||||
@ -553,6 +533,18 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
public var state: Signal<PresentationGroupCallState, NoError> {
|
||||
return self.statePromise.get()
|
||||
}
|
||||
|
||||
private var stateVersionValue: Int = 0 {
|
||||
didSet {
|
||||
if self.stateVersionValue != oldValue {
|
||||
self.stateVersionPromise.set(self.stateVersionValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
private let stateVersionPromise = ValuePromise<Int>(0)
|
||||
public var stateVersion: Signal<Int, NoError> {
|
||||
return self.stateVersionPromise.get()
|
||||
}
|
||||
|
||||
private var membersValue: PresentationGroupCallMembers? {
|
||||
didSet {
|
||||
@ -626,11 +618,6 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
private var videoCapturer: OngoingCallVideoCapturer?
|
||||
private var useFrontCamera: Bool = true
|
||||
|
||||
private let incomingVideoSourcePromise = Promise<Set<String>>(Set())
|
||||
public var incomingVideoSources: Signal<Set<String>, NoError> {
|
||||
return self.incomingVideoSourcePromise.get()
|
||||
}
|
||||
|
||||
private var peerUpdatesSubscription: Disposable?
|
||||
|
||||
public private(set) var schedulePending = false
|
||||
@ -1423,12 +1410,9 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
}
|
||||
}
|
||||
}, outgoingAudioBitrateKbit: outgoingAudioBitrateKbit, videoContentType: self.isVideoEnabled ? .generic : .none, enableNoiseSuppression: enableNoiseSuppression)
|
||||
self.incomingVideoSourcePromise.set(genericCallContext.videoSources
|
||||
|> deliverOnMainQueue
|
||||
|> map { sources -> Set<String> in
|
||||
return Set(sources)
|
||||
})
|
||||
|
||||
self.genericCallContext = genericCallContext
|
||||
self.stateVersionValue += 1
|
||||
}
|
||||
self.joinDisposable.set((genericCallContext.joinPayload
|
||||
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
||||
@ -2561,12 +2545,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
}
|
||||
let clientParams = joinCallResult.jsonParams
|
||||
|
||||
//screencastIpcContext.setJoinResponsePayload(clientParams)
|
||||
|
||||
screencastCallContext.setConnectionMode(.rtc, keepBroadcastConnectedIfWasEnabled: false)
|
||||
screencastCallContext.setJoinResponse(payload: clientParams)
|
||||
|
||||
strongSelf.genericCallContext?.setIgnoreVideoEndpointIds(endpointIds: [joinCallResult.endpointId])
|
||||
}, error: { error in
|
||||
guard let _ = self else {
|
||||
return
|
||||
@ -2721,8 +2701,23 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
}
|
||||
}
|
||||
|
||||
public func setFullSizeVideo(endpointId: String?) {
|
||||
self.genericCallContext?.setFullSizeVideo(endpointId: endpointId)
|
||||
public func setRequestedVideoList(items: [PresentationGroupCallRequestedVideo]) {
|
||||
self.genericCallContext?.setRequestedVideoChannels(items.compactMap { item -> OngoingGroupCallContext.VideoChannel in
|
||||
let mappedQuality: OngoingGroupCallContext.VideoChannel.Quality
|
||||
switch item.quality {
|
||||
case .thumbnail:
|
||||
mappedQuality = .thumbnail
|
||||
case .medium:
|
||||
mappedQuality = .medium
|
||||
case .full:
|
||||
mappedQuality = .full
|
||||
}
|
||||
return OngoingGroupCallContext.VideoChannel(
|
||||
audioSsrc: item.audioSsrc,
|
||||
videoDescription: item.videoInformation,
|
||||
quality: mappedQuality
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
public func setCurrentAudioOutput(_ output: AudioSessionOutput) {
|
||||
|
@ -1048,6 +1048,8 @@ public final class VoiceChatController: ViewController {
|
||||
private let displayedRaisedHandsPromise = ValuePromise<Set<PeerId>>(Set())
|
||||
|
||||
private var requestedVideoSources = Set<String>()
|
||||
private var requestedVideoChannels: [PresentationGroupCallRequestedVideo] = []
|
||||
|
||||
private var videoNodes: [(String, GroupVideoNode)] = []
|
||||
private var endpointToPeerId: [String: PeerId] = [:]
|
||||
private var peerIdToEndpoint: [PeerId: String] = [:]
|
||||
@ -2147,8 +2149,17 @@ public final class VoiceChatController: ViewController {
|
||||
}
|
||||
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: strongSelf.presentationData.strings.VoiceChat_DisplayAsSuccess(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).0), action: { _ in return false })
|
||||
}))
|
||||
|
||||
self.voiceSourcesDisposable.set((self.call.stateVersion
|
||||
|> distinctUntilChanged
|
||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.callStateDidReset()
|
||||
}))
|
||||
|
||||
self.voiceSourcesDisposable.set((self.call.incomingVideoSources
|
||||
/*self.voiceSourcesDisposable.set((self.call.incomingVideoSources
|
||||
|> deliverOnMainQueue).start(next: { [weak self] endpointIds in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -2233,7 +2244,7 @@ public final class VoiceChatController: ViewController {
|
||||
strongSelf.updateMainStageVideo(waitForFullSize: false)
|
||||
}
|
||||
}
|
||||
}))
|
||||
}))*/
|
||||
|
||||
self.titleNode.tapped = { [weak self] in
|
||||
if let strongSelf = self, !strongSelf.isScheduling {
|
||||
@ -4542,6 +4553,8 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
var endpointIdToPeerId: [String: PeerId] = [:]
|
||||
var peerIdToEndpointId: [PeerId: String] = [:]
|
||||
|
||||
var requestedVideoChannels: [PresentationGroupCallRequestedVideo] = []
|
||||
|
||||
var pinnedEntry: ListEntry?
|
||||
for member in callMembers.0 {
|
||||
@ -4667,7 +4680,25 @@ public final class VoiceChatController: ViewController {
|
||||
style: .list
|
||||
))
|
||||
}
|
||||
|
||||
if var videoChannel = member.requestedVideoChannel(quality: .thumbnail) {
|
||||
if self.effectiveSpeakerWithVideo?.1 == videoChannel.endpointId {
|
||||
videoChannel.quality = .full
|
||||
}
|
||||
|
||||
requestedVideoChannels.append(videoChannel)
|
||||
}
|
||||
if var presentationChannel = member.requestedVideoChannel(quality: .thumbnail) {
|
||||
if self.effectiveSpeakerWithVideo?.1 == presentationChannel.endpointId {
|
||||
presentationChannel.quality = .full
|
||||
}
|
||||
|
||||
requestedVideoChannels.append(presentationChannel)
|
||||
}
|
||||
}
|
||||
|
||||
self.requestedVideoChannels = requestedVideoChannels
|
||||
self.updateRequestedVideoChannels()
|
||||
|
||||
for peer in invitedPeers {
|
||||
if processedPeerIds.contains(peer.id) {
|
||||
@ -4761,6 +4792,101 @@ public final class VoiceChatController: ViewController {
|
||||
let tileTransition = preparedTransition(from: previousTileEntries, to: tileEntries, isLoading: false, isEmpty: false, canInvite: canInvite, crossFade: false, animated: !disableAnimation, context: self.context, presentationData: presentationData, interaction: self.itemInteraction!)
|
||||
self.enqueueTileTransition(tileTransition)
|
||||
}
|
||||
|
||||
private func callStateDidReset() {
|
||||
self.requestedVideoSources.removeAll()
|
||||
self.filterRequestedVideoChannels(channels: [])
|
||||
self.updateRequestedVideoChannels()
|
||||
}
|
||||
|
||||
private func filterRequestedVideoChannels(channels: [PresentationGroupCallRequestedVideo]) {
|
||||
var validSources = Set<String>()
|
||||
for channel in channels {
|
||||
validSources.insert(channel.endpointId)
|
||||
|
||||
if !self.requestedVideoSources.contains(channel.endpointId) {
|
||||
self.requestedVideoSources.insert(channel.endpointId)
|
||||
self.call.makeIncomingVideoView(endpointId: channel.endpointId, completion: { [weak self] videoView in
|
||||
Queue.mainQueue().async {
|
||||
guard let strongSelf = self, let videoView = videoView else {
|
||||
return
|
||||
}
|
||||
let videoNode = GroupVideoNode(videoView: videoView)
|
||||
strongSelf.videoNodes.append((channel.endpointId, videoNode))
|
||||
|
||||
if let _ = strongSelf.validLayout {
|
||||
loop: for i in 0 ..< strongSelf.currentEntries.count {
|
||||
let entry = strongSelf.currentEntries[i]
|
||||
let tileEntry = strongSelf.currentTileEntries[i]
|
||||
switch entry {
|
||||
case let .peer(peerEntry):
|
||||
if peerEntry.effectiveVideoEndpointId == channel.endpointId {
|
||||
let presentationData = strongSelf.presentationData.withUpdated(theme: strongSelf.darkTheme)
|
||||
strongSelf.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [ListViewUpdateItem(index: i, previousIndex: i, item: entry.item(context: strongSelf.context, presentationData: presentationData, interaction: strongSelf.itemInteraction!, transparent: false), directionHint: nil)], options: [.Synchronous], updateOpaqueState: nil)
|
||||
strongSelf.tileListNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [ListViewUpdateItem(index: i, previousIndex: i, item: tileEntry.item(context: strongSelf.context, presentationData: presentationData, interaction: strongSelf.itemInteraction!, transparent: false), directionHint: nil)], options: [.Synchronous], updateOpaqueState: nil)
|
||||
break loop
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var removeRequestedVideoSources: [String] = []
|
||||
for source in self.requestedVideoSources {
|
||||
if !validSources.contains(source) {
|
||||
removeRequestedVideoSources.append(source)
|
||||
}
|
||||
}
|
||||
for source in removeRequestedVideoSources {
|
||||
self.requestedVideoSources.remove(source)
|
||||
}
|
||||
|
||||
for i in (0 ..< self.videoNodes.count).reversed() {
|
||||
if !validSources.contains(self.videoNodes[i].0) {
|
||||
let endpointId = self.videoNodes[i].0
|
||||
self.videoNodes.remove(at: i)
|
||||
|
||||
loop: for j in 0 ..< self.currentEntries.count {
|
||||
let entry = self.currentEntries[j]
|
||||
let tileEntry = self.currentTileEntries[j]
|
||||
switch entry {
|
||||
case let .peer(peerEntry):
|
||||
if peerEntry.effectiveVideoEndpointId == endpointId {
|
||||
let presentationData = self.presentationData.withUpdated(theme: self.darkTheme)
|
||||
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [ListViewUpdateItem(index: j, previousIndex: j, item: entry.item(context: self.context, presentationData: presentationData, interaction: self.itemInteraction!, transparent: false), directionHint: nil)], options: [.Synchronous], updateOpaqueState: nil)
|
||||
self.tileListNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [ListViewUpdateItem(index: j, previousIndex: j, item: tileEntry.item(context: self.context, presentationData: presentationData, interaction: self.itemInteraction!, transparent: false), directionHint: nil)], options: [.Synchronous], updateOpaqueState: nil)
|
||||
break loop
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let (peerId, endpointId) = self.effectiveSpeakerWithVideo {
|
||||
if !validSources.contains(endpointId) {
|
||||
if peerId == self.currentForcedSpeakerWithVideo {
|
||||
self.currentForcedSpeakerWithVideo = nil
|
||||
}
|
||||
if peerId == self.currentDominantSpeakerWithVideo {
|
||||
self.currentDominantSpeakerWithVideo = nil
|
||||
}
|
||||
self.updateMainStageVideo(waitForFullSize: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateRequestedVideoChannels() {
|
||||
self.filterRequestedVideoChannels(channels: self.requestedVideoChannels)
|
||||
|
||||
self.call.setRequestedVideoList(items: self.requestedVideoChannels)
|
||||
}
|
||||
|
||||
private func updateMainStageVideo(waitForFullSize: Bool, currentEntries: [ListEntry]? = nil, updateMembers: Bool = true, force: Bool = false) {
|
||||
let effectiveMainParticipant = self.currentForcedSpeakerWithVideo ?? self.currentDominantSpeakerWithVideo
|
||||
@ -4858,9 +4984,21 @@ public final class VoiceChatController: ViewController {
|
||||
if updateMembers {
|
||||
self.updateMembers(muteState: self.effectiveMuteState, callMembers: self.currentCallMembers ?? ([], nil), invitedPeers: self.currentInvitedPeers ?? [], speakingPeers: self.currentSpeakingPeers ?? Set(), updatePinnedPeer: false)
|
||||
}
|
||||
self.call.setFullSizeVideo(endpointId: effectivePeer?.1)
|
||||
|
||||
self.mainStageVideoContainerNode?.updatePeer(peer: effectivePeer, waitForFullSize: waitForFullSize, completion: { [weak self] in
|
||||
if waitForFullSize {
|
||||
completion()
|
||||
|
||||
if let strongSelf = self, strongSelf.mainStageVideoClippingNode.alpha.isZero {
|
||||
strongSelf.mainStageVideoClippingNode.alpha = 1.0
|
||||
strongSelf.mainStageVideoClippingNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
//self.call.setFullSizeVideo(endpointId: effectivePeer?.1)
|
||||
|
||||
self.updateSpeakerWithVideoDisposable.set((self.call.incomingVideoSources
|
||||
/*self.updateSpeakerWithVideoDisposable.set((self.call.incomingVideoSources
|
||||
|> mapToSignal { videoSources -> Signal<Bool, NoError> in
|
||||
if let (_, endpointId, otherEndpointId) = effectivePeer {
|
||||
var exists = true
|
||||
@ -4891,7 +5029,7 @@ public final class VoiceChatController: ViewController {
|
||||
}
|
||||
})
|
||||
}
|
||||
}))
|
||||
}))*/
|
||||
|
||||
if !waitForFullSize {
|
||||
completion()
|
||||
|
@ -186,6 +186,24 @@ public final class OngoingGroupCallContext {
|
||||
self.videoDescription = videoDescription
|
||||
}
|
||||
}
|
||||
|
||||
public struct VideoChannel {
|
||||
public enum Quality {
|
||||
case thumbnail
|
||||
case medium
|
||||
case full
|
||||
}
|
||||
|
||||
public var audioSsrc: UInt32
|
||||
public var videoDescription: String
|
||||
public var quality: Quality
|
||||
|
||||
public init(audioSsrc: UInt32, videoDescription: String, quality: Quality) {
|
||||
self.audioSsrc = audioSsrc
|
||||
self.videoDescription = videoDescription
|
||||
self.quality = quality
|
||||
}
|
||||
}
|
||||
|
||||
private final class Impl {
|
||||
let queue: Queue
|
||||
@ -199,8 +217,6 @@ public final class OngoingGroupCallContext {
|
||||
let isNoiseSuppressionEnabled = ValuePromise<Bool>(true, ignoreRepeated: true)
|
||||
let audioLevels = ValuePipe<[(AudioLevelKey, Float, Bool)]>()
|
||||
|
||||
let videoSources = ValuePromise<Set<String>>(Set(), ignoreRepeated: true)
|
||||
|
||||
private var broadcastPartsSource: BroadcastPartSource?
|
||||
|
||||
init(queue: Queue, inputDeviceId: String, outputDeviceId: String, video: OngoingCallVideoCapturer?, requestMediaChannelDescriptions: @escaping (Set<UInt32>, @escaping ([MediaChannelDescription]) -> Void) -> Disposable, audioStreamData: AudioStreamData?, rejoinNeeded: @escaping () -> Void, outgoingAudioBitrateKbit: Int32?, videoContentType: VideoContentType, enableNoiseSuppression: Bool) {
|
||||
@ -225,8 +241,7 @@ public final class OngoingGroupCallContext {
|
||||
case .none:
|
||||
_videoContentType = .none
|
||||
}
|
||||
|
||||
let videoSources = self.videoSources
|
||||
|
||||
self.context = GroupCallThreadLocalContext(
|
||||
queue: ContextQueueImpl(queue: queue),
|
||||
networkStateUpdated: { state in
|
||||
@ -238,9 +253,6 @@ public final class OngoingGroupCallContext {
|
||||
inputDeviceId: inputDeviceId,
|
||||
outputDeviceId: outputDeviceId,
|
||||
videoCapturer: video?.impl,
|
||||
incomingVideoSourcesUpdated: { endpointIds in
|
||||
videoSources.set(Set(endpointIds))
|
||||
},
|
||||
requestMediaChannelDescriptions: { ssrcs, completion in
|
||||
final class OngoingGroupCallMediaChannelDescriptionTaskImpl : NSObject, OngoingGroupCallMediaChannelDescriptionTask {
|
||||
private let disposable: Disposable
|
||||
@ -356,13 +368,24 @@ public final class OngoingGroupCallContext {
|
||||
func setVolume(ssrc: UInt32, volume: Double) {
|
||||
self.context.setVolumeForSsrc(ssrc, volume: volume)
|
||||
}
|
||||
|
||||
func setFullSizeVideo(endpointId: String?) {
|
||||
self.context.setFullSizeVideoEndpointId(endpointId)
|
||||
}
|
||||
|
||||
func setIgnoreVideoEndpointIds(endpointIds: [String]) {
|
||||
self.context.setIgnoreVideoEndpointIds(endpointIds)
|
||||
func setRequestedVideoChannels(_ channels: [VideoChannel]) {
|
||||
self.context.setRequestedVideoChannels(channels.map { channel -> OngoingGroupCallRequestedVideoChannel in
|
||||
let mappedQuality: OngoingGroupCallRequestedVideoQuality
|
||||
switch channel.quality {
|
||||
case .thumbnail:
|
||||
mappedQuality = .thumbnail
|
||||
case .medium:
|
||||
mappedQuality = .medium
|
||||
case .full:
|
||||
mappedQuality = .full
|
||||
}
|
||||
return OngoingGroupCallRequestedVideoChannel(
|
||||
audioSsrc: channel.audioSsrc,
|
||||
videoInformation: channel.videoDescription,
|
||||
quality: mappedQuality
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
func stop() {
|
||||
@ -576,18 +599,6 @@ public final class OngoingGroupCallContext {
|
||||
}
|
||||
}
|
||||
|
||||
public var videoSources: Signal<Set<String>, NoError> {
|
||||
return Signal { subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
self.impl.with { impl in
|
||||
disposable.set(impl.videoSources.get().start(next: { value in
|
||||
subscriber.putNext(value)
|
||||
}))
|
||||
}
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
|
||||
public init(inputDeviceId: String = "", outputDeviceId: String = "", video: OngoingCallVideoCapturer?, requestMediaChannelDescriptions: @escaping (Set<UInt32>, @escaping ([MediaChannelDescription]) -> Void) -> Disposable, audioStreamData: AudioStreamData?, rejoinNeeded: @escaping () -> Void, outgoingAudioBitrateKbit: Int32?, videoContentType: VideoContentType, enableNoiseSuppression: Bool) {
|
||||
let queue = self.queue
|
||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||
@ -664,16 +675,10 @@ public final class OngoingGroupCallContext {
|
||||
impl.setVolume(ssrc: ssrc, volume: volume)
|
||||
}
|
||||
}
|
||||
|
||||
public func setFullSizeVideo(endpointId: String?) {
|
||||
self.impl.with { impl in
|
||||
impl.setFullSizeVideo(endpointId: endpointId)
|
||||
}
|
||||
}
|
||||
|
||||
public func setIgnoreVideoEndpointIds(endpointIds: [String]) {
|
||||
public func setRequestedVideoChannels(_ channels: [VideoChannel]) {
|
||||
self.impl.with { impl in
|
||||
impl.setIgnoreVideoEndpointIds(endpointIds: endpointIds)
|
||||
impl.setRequestedVideoChannels(channels)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,9 +226,35 @@ typedef NS_ENUM(int32_t, OngoingGroupCallVideoContentType) {
|
||||
|
||||
@end
|
||||
|
||||
typedef NS_ENUM(int32_t, OngoingGroupCallRequestedVideoQuality) {
|
||||
OngoingGroupCallRequestedVideoQualityThumbnail,
|
||||
OngoingGroupCallRequestedVideoQualityMedium,
|
||||
OngoingGroupCallRequestedVideoQualityFull,
|
||||
};
|
||||
|
||||
@interface OngoingGroupCallRequestedVideoChannel : NSObject
|
||||
|
||||
@property (nonatomic, readonly) uint32_t audioSsrc;
|
||||
@property (nonatomic, strong, readonly) NSString * _Nonnull videoInformation;
|
||||
@property (nonatomic, readonly) OngoingGroupCallRequestedVideoQuality quality;
|
||||
|
||||
- (instancetype)initWithAudioSsrc:(uint32_t)audioSsrc videoInformation:(NSString * _Nonnull)videoInformation quality:(OngoingGroupCallRequestedVideoQuality)quality;
|
||||
|
||||
@end
|
||||
|
||||
@interface GroupCallThreadLocalContext : NSObject
|
||||
|
||||
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray<NSString *> * _Nonnull))incomingVideoSourcesUpdated requestMediaChannelDescriptions:(id<OngoingGroupCallMediaChannelDescriptionTask> _Nonnull (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull, void (^ _Nonnull)(NSArray<OngoingGroupCallMediaChannelDescription *> * _Nonnull)))requestMediaChannelDescriptions requestBroadcastPart:(id<OngoingGroupCallBroadcastPartTask> _Nonnull (^ _Nonnull)(int64_t, int64_t, void (^ _Nonnull)(OngoingGroupCallBroadcastPart * _Nullable)))requestBroadcastPart outgoingAudioBitrateKbit:(int32_t)outgoingAudioBitrateKbit videoContentType:(OngoingGroupCallVideoContentType)videoContentType enableNoiseSuppression:(bool)enableNoiseSuppression;
|
||||
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue
|
||||
networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated
|
||||
audioLevelsUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))audioLevelsUpdated
|
||||
inputDeviceId:(NSString * _Nonnull)inputDeviceId
|
||||
outputDeviceId:(NSString * _Nonnull)outputDeviceId
|
||||
videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer
|
||||
requestMediaChannelDescriptions:(id<OngoingGroupCallMediaChannelDescriptionTask> _Nonnull (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull, void (^ _Nonnull)(NSArray<OngoingGroupCallMediaChannelDescription *> * _Nonnull)))requestMediaChannelDescriptions
|
||||
requestBroadcastPart:(id<OngoingGroupCallBroadcastPartTask> _Nonnull (^ _Nonnull)(int64_t, int64_t, void (^ _Nonnull)(OngoingGroupCallBroadcastPart * _Nullable)))requestBroadcastPart
|
||||
outgoingAudioBitrateKbit:(int32_t)outgoingAudioBitrateKbit
|
||||
videoContentType:(OngoingGroupCallVideoContentType)videoContentType
|
||||
enableNoiseSuppression:(bool)enableNoiseSuppression;
|
||||
|
||||
- (void)stop;
|
||||
|
||||
@ -244,8 +270,7 @@ typedef NS_ENUM(int32_t, OngoingGroupCallVideoContentType) {
|
||||
- (void)disableVideo:(void (^ _Nonnull)(NSString * _Nonnull, uint32_t))completion;
|
||||
|
||||
- (void)setVolumeForSsrc:(uint32_t)ssrc volume:(double)volume;
|
||||
- (void)setFullSizeVideoEndpointId:(NSString * _Nullable)endpointId;
|
||||
- (void)setIgnoreVideoEndpointIds:(NSArray<NSString *> * _Nonnull)ignoreVideoEndpointIds;
|
||||
- (void)setRequestedVideoChannels:(NSArray<OngoingGroupCallRequestedVideoChannel *> * _Nonnull)requestedVideoChannels;
|
||||
|
||||
- (void)switchAudioOutput:(NSString * _Nonnull)deviceId;
|
||||
- (void)switchAudioInput:(NSString * _Nonnull)deviceId;
|
||||
|
@ -941,7 +941,17 @@ private:
|
||||
|
||||
@implementation GroupCallThreadLocalContext
|
||||
|
||||
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated audioLevelsUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))audioLevelsUpdated inputDeviceId:(NSString * _Nonnull)inputDeviceId outputDeviceId:(NSString * _Nonnull)outputDeviceId videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer incomingVideoSourcesUpdated:(void (^ _Nonnull)(NSArray<NSString *> * _Nonnull))incomingVideoSourcesUpdated requestMediaChannelDescriptions:(id<OngoingGroupCallMediaChannelDescriptionTask> _Nonnull (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull, void (^ _Nonnull)(NSArray<OngoingGroupCallMediaChannelDescription *> * _Nonnull)))requestMediaChannelDescriptions requestBroadcastPart:(id<OngoingGroupCallBroadcastPartTask> _Nonnull (^ _Nonnull)(int64_t, int64_t, void (^ _Nonnull)(OngoingGroupCallBroadcastPart * _Nullable)))requestBroadcastPart outgoingAudioBitrateKbit:(int32_t)outgoingAudioBitrateKbit videoContentType:(OngoingGroupCallVideoContentType)videoContentType enableNoiseSuppression:(bool)enableNoiseSuppression {
|
||||
- (instancetype _Nonnull)initWithQueue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue
|
||||
networkStateUpdated:(void (^ _Nonnull)(GroupCallNetworkState))networkStateUpdated
|
||||
audioLevelsUpdated:(void (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull))audioLevelsUpdated
|
||||
inputDeviceId:(NSString * _Nonnull)inputDeviceId
|
||||
outputDeviceId:(NSString * _Nonnull)outputDeviceId
|
||||
videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer
|
||||
requestMediaChannelDescriptions:(id<OngoingGroupCallMediaChannelDescriptionTask> _Nonnull (^ _Nonnull)(NSArray<NSNumber *> * _Nonnull, void (^ _Nonnull)(NSArray<OngoingGroupCallMediaChannelDescription *> * _Nonnull)))requestMediaChannelDescriptions
|
||||
requestBroadcastPart:(id<OngoingGroupCallBroadcastPartTask> _Nonnull (^ _Nonnull)(int64_t, int64_t, void (^ _Nonnull)(OngoingGroupCallBroadcastPart * _Nullable)))requestBroadcastPart
|
||||
outgoingAudioBitrateKbit:(int32_t)outgoingAudioBitrateKbit
|
||||
videoContentType:(OngoingGroupCallVideoContentType)videoContentType
|
||||
enableNoiseSuppression:(bool)enableNoiseSuppression {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_queue = queue;
|
||||
@ -1002,13 +1012,6 @@ private:
|
||||
.initialInputDeviceId = inputDeviceId.UTF8String,
|
||||
.initialOutputDeviceId = outputDeviceId.UTF8String,
|
||||
.videoCapture = [_videoCapturer getInterface],
|
||||
.incomingVideoSourcesUpdated = [incomingVideoSourcesUpdated](std::vector<std::string> const &sources) {
|
||||
NSMutableArray<NSString *> *mappedSources = [[NSMutableArray alloc] init];
|
||||
for (auto it : sources) {
|
||||
[mappedSources addObject:[NSString stringWithUTF8String:it.c_str()]];
|
||||
}
|
||||
incomingVideoSourcesUpdated(mappedSources);
|
||||
},
|
||||
.requestBroadcastPart = [requestBroadcastPart](int64_t timestampMilliseconds, int64_t durationMilliseconds, std::function<void(tgcalls::BroadcastPart &&)> completion) -> std::shared_ptr<tgcalls::BroadcastPartTask> {
|
||||
id<OngoingGroupCallBroadcastPartTask> task = requestBroadcastPart(timestampMilliseconds, durationMilliseconds, ^(OngoingGroupCallBroadcastPart * _Nullable part) {
|
||||
tgcalls::BroadcastPart parsedPart;
|
||||
@ -1178,19 +1181,33 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setFullSizeVideoEndpointId:(NSString * _Nullable)endpointId {
|
||||
- (void)setRequestedVideoChannels:(NSArray<OngoingGroupCallRequestedVideoChannel *> * _Nonnull)requestedVideoChannels {
|
||||
if (_instance) {
|
||||
_instance->setFullSizeVideoEndpointId(endpointId.UTF8String ?: "");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setIgnoreVideoEndpointIds:(NSArray<NSString *> * _Nonnull)ignoreVideoEndpointIds {
|
||||
if (_instance) {
|
||||
std::vector<std::string> mappedEndpointIds;
|
||||
for (NSString *value in ignoreVideoEndpointIds) {
|
||||
mappedEndpointIds.push_back(value.UTF8String);
|
||||
std::vector<tgcalls::VideoChannelDescription> mappedChannels;
|
||||
for (OngoingGroupCallRequestedVideoChannel *channel : requestedVideoChannels) {
|
||||
tgcalls::VideoChannelDescription description;
|
||||
description.audioSsrc = channel.audioSsrc;
|
||||
description.videoInformation = channel.videoInformation.UTF8String ?: "";
|
||||
switch (channel.quality) {
|
||||
case OngoingGroupCallRequestedVideoQualityThumbnail: {
|
||||
description.quality = tgcalls::VideoChannelDescription::Quality::Thumbnail;
|
||||
break;
|
||||
}
|
||||
case OngoingGroupCallRequestedVideoQualityMedium: {
|
||||
description.quality = tgcalls::VideoChannelDescription::Quality::Medium;
|
||||
break;
|
||||
}
|
||||
case OngoingGroupCallRequestedVideoQualityFull: {
|
||||
description.quality = tgcalls::VideoChannelDescription::Quality::Full;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
mappedChannels.push_back(std::move(description));
|
||||
}
|
||||
_instance->setIgnoreVideoEndpointIds(mappedEndpointIds);
|
||||
_instance->setRequestedVideoChannels(std::move(mappedChannels));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1278,3 +1295,17 @@ private:
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation OngoingGroupCallRequestedVideoChannel
|
||||
|
||||
- (instancetype)initWithAudioSsrc:(uint32_t)audioSsrc videoInformation:(NSString * _Nonnull)videoInformation quality:(OngoingGroupCallRequestedVideoQuality)quality {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_audioSsrc = audioSsrc;
|
||||
_videoInformation = videoInformation;
|
||||
_quality = quality;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 9a136a755aafe710311fb4a8f99c373513f7fd4a
|
||||
Subproject commit 0cdddae960951aca74907685d7adb56a0d1efcb8
|
Loading…
x
Reference in New Issue
Block a user