Update video API

This commit is contained in:
Ali 2021-05-11 14:11:08 +04:00
parent 7334cabc08
commit f4df115850
7 changed files with 354 additions and 100 deletions

View File

@ -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)

View File

@ -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) {

View File

@ -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()

View File

@ -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)
}
}

View File

@ -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;

View File

@ -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