mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 22:55:00 +00:00
Screen sharing in 1-to-1 calls
This commit is contained in:
@@ -186,7 +186,7 @@ final class PresentationCallToneRenderer {
|
||||
}
|
||||
|
||||
public final class PresentationCallImpl: PresentationCall {
|
||||
public let account: Account
|
||||
public let context: AccountContext
|
||||
private let audioSession: ManagedAudioSession
|
||||
private let callSessionManager: CallSessionManager
|
||||
private let callKitIntegration: CallKitIntegration?
|
||||
@@ -286,11 +286,19 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
|
||||
private var useFrontCamera: Bool = true
|
||||
private var videoCapturer: OngoingCallVideoCapturer?
|
||||
|
||||
private var screencastBufferServerContext: IpcGroupCallBufferAppContext?
|
||||
private var screencastCapturer: OngoingCallVideoCapturer?
|
||||
private var isScreencastActive: Bool = false
|
||||
|
||||
private var proximityManagerIndex: Int?
|
||||
|
||||
private let screencastFramesDisposable = MetaDisposable()
|
||||
private let screencastAudioDataDisposable = MetaDisposable()
|
||||
private let screencastStateDisposable = MetaDisposable()
|
||||
|
||||
init(
|
||||
account: Account,
|
||||
context: AccountContext,
|
||||
audioSession: ManagedAudioSession,
|
||||
callSessionManager: CallSessionManager,
|
||||
callKitIntegration: CallKitIntegration?,
|
||||
@@ -313,7 +321,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
enableTCP: Bool,
|
||||
preferredVideoCodec: String?
|
||||
) {
|
||||
self.account = account
|
||||
self.context = context
|
||||
self.audioSession = audioSession
|
||||
self.callSessionManager = callSessionManager
|
||||
self.callKitIntegration = callKitIntegration
|
||||
@@ -345,7 +353,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
self.isVideo = startWithVideo
|
||||
if self.isVideo {
|
||||
self.videoCapturer = OngoingCallVideoCapturer()
|
||||
self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: .active, remoteVideoState: .inactive, remoteAudioState: .active, remoteBatteryLevel: .normal))
|
||||
self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: .active(isScreencast: self.isScreencastActive), remoteVideoState: .inactive, remoteAudioState: .active, remoteBatteryLevel: .normal))
|
||||
} else {
|
||||
self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: self.isVideoPossible ? .inactive : .notAvailable, remoteVideoState: .inactive, remoteAudioState: .active, remoteBatteryLevel: .normal))
|
||||
}
|
||||
@@ -457,6 +465,11 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
strongSelf.updateIsAudioSessionActive(value)
|
||||
}
|
||||
})
|
||||
|
||||
let screencastCapturer = OngoingCallVideoCapturer(isCustom: true)
|
||||
self.screencastCapturer = screencastCapturer
|
||||
|
||||
self.resetScreencastContext()
|
||||
|
||||
if callKitIntegration == nil {
|
||||
self.proximityManagerIndex = DeviceProximityManager.shared().add { _ in
|
||||
@@ -473,6 +486,9 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
self.audioLevelDisposable?.dispose()
|
||||
self.batteryLevelDisposable?.dispose()
|
||||
self.audioSessionDisposable?.dispose()
|
||||
self.screencastFramesDisposable.dispose()
|
||||
self.screencastAudioDataDisposable.dispose()
|
||||
self.screencastStateDisposable.dispose()
|
||||
|
||||
if let dropCallKitCallTimer = self.dropCallKitCallTimer {
|
||||
dropCallKitCallTimer.invalidate()
|
||||
@@ -530,11 +546,11 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
case .notAvailable:
|
||||
mappedVideoState = .notAvailable
|
||||
case .active:
|
||||
mappedVideoState = .active
|
||||
mappedVideoState = .active(isScreencast: self.isScreencastActive)
|
||||
case .inactive:
|
||||
mappedVideoState = .inactive
|
||||
case .paused:
|
||||
mappedVideoState = .paused
|
||||
mappedVideoState = .paused(isScreencast: self.isScreencastActive)
|
||||
}
|
||||
switch callContextState.remoteVideoState {
|
||||
case .inactive:
|
||||
@@ -565,7 +581,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
mappedVideoState = previousVideoState
|
||||
} else {
|
||||
if self.isVideo {
|
||||
mappedVideoState = .active
|
||||
mappedVideoState = .active(isScreencast: self.isScreencastActive)
|
||||
} else if self.isVideoPossible && sessionState.isVideoPossible {
|
||||
mappedVideoState = .inactive
|
||||
} else {
|
||||
@@ -664,7 +680,7 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
if let _ = audioSessionControl, !wasActive || previousControl == nil {
|
||||
let logName = "\(id.id)_\(id.accessHash)"
|
||||
|
||||
let ongoingContext = OngoingCallContext(account: account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, derivedState: self.derivedState, key: key, isOutgoing: sessionState.isOutgoing, video: self.videoCapturer, connections: connections, maxLayer: maxLayer, version: version, allowP2P: allowsP2P, enableTCP: self.enableTCP, enableStunMarking: self.enableStunMarking, audioSessionActive: self.audioSessionActive.get(), logName: logName, preferredVideoCodec: self.preferredVideoCodec)
|
||||
let ongoingContext = OngoingCallContext(account: self.context.account, callSessionManager: self.callSessionManager, internalId: self.internalId, proxyServer: proxyServer, initialNetworkType: self.currentNetworkType, updatedNetworkType: self.updatedNetworkType, serializedData: self.serializedData, dataSaving: dataSaving, derivedState: self.derivedState, key: key, isOutgoing: sessionState.isOutgoing, video: self.videoCapturer, connections: connections, maxLayer: maxLayer, version: version, allowP2P: allowsP2P, enableTCP: self.enableTCP, enableStunMarking: self.enableStunMarking, audioSessionActive: self.audioSessionActive.get(), logName: logName, preferredVideoCodec: self.preferredVideoCodec)
|
||||
self.ongoingContext = ongoingContext
|
||||
ongoingContext.setIsMuted(self.isMutedValue)
|
||||
if let requestedVideoAspect = self.requestedVideoAspect {
|
||||
@@ -927,6 +943,57 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
self.ongoingContext?.disableVideo()
|
||||
}
|
||||
}
|
||||
|
||||
private func resetScreencastContext() {
|
||||
let basePath = self.context.sharedContext.basePath + "/broadcast-coordination"
|
||||
let screencastBufferServerContext = IpcGroupCallBufferAppContext(basePath: basePath)
|
||||
self.screencastBufferServerContext = screencastBufferServerContext
|
||||
|
||||
self.screencastFramesDisposable.set((screencastBufferServerContext.frames
|
||||
|> deliverOnMainQueue).start(next: { [weak screencastCapturer] screencastFrame in
|
||||
guard let screencastCapturer = screencastCapturer else {
|
||||
return
|
||||
}
|
||||
screencastCapturer.injectPixelBuffer(screencastFrame.0, rotation: screencastFrame.1)
|
||||
}))
|
||||
self.screencastAudioDataDisposable.set((screencastBufferServerContext.audioData
|
||||
|> deliverOnMainQueue).start(next: { _ in
|
||||
}))
|
||||
self.screencastStateDisposable.set((screencastBufferServerContext.isActive
|
||||
|> distinctUntilChanged
|
||||
|> deliverOnMainQueue).start(next: { [weak self] isActive in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if isActive {
|
||||
strongSelf.requestScreencast()
|
||||
} else {
|
||||
strongSelf.disableScreencast(reset: false)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
private func requestScreencast() {
|
||||
self.disableVideo()
|
||||
|
||||
if let screencastCapturer = self.screencastCapturer {
|
||||
self.isScreencastActive = true
|
||||
self.ongoingContext?.requestVideo(screencastCapturer)
|
||||
}
|
||||
}
|
||||
|
||||
func disableScreencast(reset: Bool = true) {
|
||||
if self.isScreencastActive {
|
||||
if let _ = self.videoCapturer {
|
||||
self.videoCapturer = nil
|
||||
}
|
||||
self.isScreencastActive = false
|
||||
self.ongoingContext?.disableVideo()
|
||||
if reset {
|
||||
self.resetScreencastContext()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func setOutgoingVideoIsPaused(_ isPaused: Bool) {
|
||||
self.videoCapturer?.setIsVideoEnabled(!isPaused)
|
||||
|
||||
Reference in New Issue
Block a user