mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-02 00:17:02 +00:00
Video call improvements
This commit is contained in:
parent
bb974d4cc1
commit
bead41710b
@ -2446,6 +2446,7 @@ Unused sets are archived when you add more.";
|
|||||||
"Call.StatusBar" = "Touch to return to call %@";
|
"Call.StatusBar" = "Touch to return to call %@";
|
||||||
|
|
||||||
"Call.ParticipantVersionOutdatedError" = "%@'s app does not support calls. They need to update their app before you can call them.";
|
"Call.ParticipantVersionOutdatedError" = "%@'s app does not support calls. They need to update their app before you can call them.";
|
||||||
|
"Call.ParticipantVideoVersionOutdatedError" = "%@'s app does not support video calls. They need to update their app before you can call them.";
|
||||||
|
|
||||||
"Privacy.Calls" = "Voice Calls";
|
"Privacy.Calls" = "Voice Calls";
|
||||||
|
|
||||||
@ -2755,6 +2756,7 @@ Unused sets are archived when you add more.";
|
|||||||
|
|
||||||
"Channel.EditAdmin.CannotEdit" = "You cannot edit the rights of this admin.";
|
"Channel.EditAdmin.CannotEdit" = "You cannot edit the rights of this admin.";
|
||||||
"Call.RateCall" = "Rate This Call";
|
"Call.RateCall" = "Rate This Call";
|
||||||
|
"Call.ShareStats" = "Share Statistics";
|
||||||
|
|
||||||
"Settings.ApplyProxyAlert" = "Are you sure you want to enable this proxy?\nServer: %1$@\nPort: %2$@\n\nYou can change your proxy server later it in the Settings (Data and Storage).";
|
"Settings.ApplyProxyAlert" = "Are you sure you want to enable this proxy?\nServer: %1$@\nPort: %2$@\n\nYou can change your proxy server later it in the Settings (Data and Storage).";
|
||||||
"Settings.ApplyProxyAlertCredentials" = "Are you sure you want to enable this proxy?\nServer: %1$@\nPort: %2$@\nUsername: %3$@\nPassword: %4$@\n\nYou can change your proxy server later it in the Settings (Data and Storage).";
|
"Settings.ApplyProxyAlertCredentials" = "Are you sure you want to enable this proxy?\nServer: %1$@\nPort: %2$@\nUsername: %3$@\nPassword: %4$@\n\nYou can change your proxy server later it in the Settings (Data and Storage).";
|
||||||
@ -4002,6 +4004,8 @@ Unused sets are archived when you add more.";
|
|||||||
"CallFeedback.ReasonSilentLocal" = "I couldn't hear the other side";
|
"CallFeedback.ReasonSilentLocal" = "I couldn't hear the other side";
|
||||||
"CallFeedback.ReasonSilentRemote" = "The other side couldn't hear me";
|
"CallFeedback.ReasonSilentRemote" = "The other side couldn't hear me";
|
||||||
"CallFeedback.ReasonDropped" = "Call ended unexpectedly";
|
"CallFeedback.ReasonDropped" = "Call ended unexpectedly";
|
||||||
|
"CallFeedback.VideoReasonDistorted" = "Video was distorted";
|
||||||
|
"CallFeedback.VideoReasonLowQuality" = "Video was pixelated";
|
||||||
"CallFeedback.AddComment" = "Add an optional comment";
|
"CallFeedback.AddComment" = "Add an optional comment";
|
||||||
"CallFeedback.IncludeLogs" = "Include technical information";
|
"CallFeedback.IncludeLogs" = "Include technical information";
|
||||||
"CallFeedback.IncludeLogsInfo" = "This won't reveal the contents of your conversation, but will help us fix the issue sooner.";
|
"CallFeedback.IncludeLogsInfo" = "This won't reveal the contents of your conversation, but will help us fix the issue sooner.";
|
||||||
|
0
Wallet/Strings/fa.lproj/Localizable.strings
Normal file
0
Wallet/Strings/fa.lproj/Localizable.strings
Normal file
0
Wallet/Strings/pl.lproj/Localizable.strings
Normal file
0
Wallet/Strings/pl.lproj/Localizable.strings
Normal file
0
Wallet/Strings/uz.lproj/Localizable.strings
Normal file
0
Wallet/Strings/uz.lproj/Localizable.strings
Normal file
@ -94,19 +94,22 @@ public final class PresentationCallVideoView {
|
|||||||
public let setOnFirstFrameReceived: (((Float) -> Void)?) -> Void
|
public let setOnFirstFrameReceived: (((Float) -> Void)?) -> Void
|
||||||
|
|
||||||
public let getOrientation: () -> Orientation
|
public let getOrientation: () -> Orientation
|
||||||
public let setOnOrientationUpdated: (((Orientation) -> Void)?) -> Void
|
public let getAspect: () -> CGFloat
|
||||||
|
public let setOnOrientationUpdated: (((Orientation, CGFloat) -> Void)?) -> Void
|
||||||
public let setOnIsMirroredUpdated: (((Bool) -> Void)?) -> Void
|
public let setOnIsMirroredUpdated: (((Bool) -> Void)?) -> Void
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
view: UIView,
|
view: UIView,
|
||||||
setOnFirstFrameReceived: @escaping (((Float) -> Void)?) -> Void,
|
setOnFirstFrameReceived: @escaping (((Float) -> Void)?) -> Void,
|
||||||
getOrientation: @escaping () -> Orientation,
|
getOrientation: @escaping () -> Orientation,
|
||||||
setOnOrientationUpdated: @escaping (((Orientation) -> Void)?) -> Void,
|
getAspect: @escaping () -> CGFloat,
|
||||||
|
setOnOrientationUpdated: @escaping (((Orientation, CGFloat) -> Void)?) -> Void,
|
||||||
setOnIsMirroredUpdated: @escaping (((Bool) -> Void)?) -> Void
|
setOnIsMirroredUpdated: @escaping (((Bool) -> Void)?) -> Void
|
||||||
) {
|
) {
|
||||||
self.view = view
|
self.view = view
|
||||||
self.setOnFirstFrameReceived = setOnFirstFrameReceived
|
self.setOnFirstFrameReceived = setOnFirstFrameReceived
|
||||||
self.getOrientation = getOrientation
|
self.getOrientation = getOrientation
|
||||||
|
self.getAspect = getAspect
|
||||||
self.setOnOrientationUpdated = setOnOrientationUpdated
|
self.setOnOrientationUpdated = setOnOrientationUpdated
|
||||||
self.setOnIsMirroredUpdated = setOnIsMirroredUpdated
|
self.setOnIsMirroredUpdated = setOnIsMirroredUpdated
|
||||||
}
|
}
|
||||||
@ -137,6 +140,7 @@ public protocol PresentationCall: class {
|
|||||||
func toggleIsMuted()
|
func toggleIsMuted()
|
||||||
func setIsMuted(_ value: Bool)
|
func setIsMuted(_ value: Bool)
|
||||||
func requestVideo()
|
func requestVideo()
|
||||||
|
func setRequestedVideoAspect(_ aspect: Float)
|
||||||
func disableVideo()
|
func disableVideo()
|
||||||
func setOutgoingVideoIsPaused(_ isPaused: Bool)
|
func setOutgoingVideoIsPaused(_ isPaused: Bool)
|
||||||
func switchVideoCamera()
|
func switchVideoCamera()
|
||||||
|
@ -945,8 +945,8 @@ public final class Transaction {
|
|||||||
self.postbox?.scanMessages(peerId: peerId, namespace: namespace, tag: tag, f)
|
self.postbox?.scanMessages(peerId: peerId, namespace: namespace, tag: tag, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func scanMessageAttributes(peerId: PeerId, namespace: MessageId.Namespace, _ f: (MessageId, [MessageAttribute]) -> Bool) {
|
public func scanMessageAttributes(peerId: PeerId, namespace: MessageId.Namespace, limit: Int, _ f: (MessageId, [MessageAttribute]) -> Bool) {
|
||||||
self.postbox?.scanMessageAttributes(peerId: peerId, namespace: namespace, f)
|
self.postbox?.scanMessageAttributes(peerId: peerId, namespace: namespace, limit: limit, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func invalidateMessageHistoryTagsSummary(peerId: PeerId, namespace: MessageId.Namespace, tagMask: MessageTags) {
|
public func invalidateMessageHistoryTagsSummary(peerId: PeerId, namespace: MessageId.Namespace, tagMask: MessageTags) {
|
||||||
@ -3174,9 +3174,10 @@ public final class Postbox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func scanMessageAttributes(peerId: PeerId, namespace: MessageId.Namespace, _ f: (MessageId, [MessageAttribute]) -> Bool) {
|
fileprivate func scanMessageAttributes(peerId: PeerId, namespace: MessageId.Namespace, limit: Int, _ f: (MessageId, [MessageAttribute]) -> Bool) {
|
||||||
|
var remainingLimit = limit
|
||||||
var index = MessageIndex.upperBound(peerId: peerId, namespace: namespace)
|
var index = MessageIndex.upperBound(peerId: peerId, namespace: namespace)
|
||||||
while true {
|
while remainingLimit > 0 {
|
||||||
let messages = self.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, from: index, includeFrom: false, to: MessageIndex.lowerBound(peerId: peerId, namespace: namespace), limit: 32)
|
let messages = self.messageHistoryTable.fetch(peerId: peerId, namespace: namespace, tag: nil, from: index, includeFrom: false, to: MessageIndex.lowerBound(peerId: peerId, namespace: namespace), limit: 32)
|
||||||
for message in messages {
|
for message in messages {
|
||||||
let attributes = MessageHistoryTable.renderMessageAttributes(message)
|
let attributes = MessageHistoryTable.renderMessageAttributes(message)
|
||||||
@ -3184,6 +3185,7 @@ public final class Postbox {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
remainingLimit -= messages.count
|
||||||
if let last = messages.last {
|
if let last = messages.last {
|
||||||
index = last.index
|
index = last.index
|
||||||
} else {
|
} else {
|
||||||
|
@ -43,6 +43,7 @@ private enum DebugControllerSection: Int32 {
|
|||||||
case logging
|
case logging
|
||||||
case experiments
|
case experiments
|
||||||
case videoExperiments
|
case videoExperiments
|
||||||
|
case videoExperiments2
|
||||||
case info
|
case info
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,6 +74,8 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
case playerEmbedding(Bool)
|
case playerEmbedding(Bool)
|
||||||
case playlistPlayback(Bool)
|
case playlistPlayback(Bool)
|
||||||
case preferredVideoCodec(Int, String, String?, Bool)
|
case preferredVideoCodec(Int, String, String?, Bool)
|
||||||
|
case disableVideoAspectScaling(Bool)
|
||||||
|
case enableVoipTcp(Bool)
|
||||||
case hostInfo(PresentationTheme, String)
|
case hostInfo(PresentationTheme, String)
|
||||||
case versionInfo(PresentationTheme)
|
case versionInfo(PresentationTheme)
|
||||||
|
|
||||||
@ -90,6 +93,8 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
return DebugControllerSection.experiments.rawValue
|
return DebugControllerSection.experiments.rawValue
|
||||||
case .preferredVideoCodec:
|
case .preferredVideoCodec:
|
||||||
return DebugControllerSection.videoExperiments.rawValue
|
return DebugControllerSection.videoExperiments.rawValue
|
||||||
|
case .disableVideoAspectScaling, .enableVoipTcp:
|
||||||
|
return DebugControllerSection.videoExperiments2.rawValue
|
||||||
case .hostInfo, .versionInfo:
|
case .hostInfo, .versionInfo:
|
||||||
return DebugControllerSection.info.rawValue
|
return DebugControllerSection.info.rawValue
|
||||||
}
|
}
|
||||||
@ -149,10 +154,14 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
return 25
|
return 25
|
||||||
case let .preferredVideoCodec(index, _, _, _):
|
case let .preferredVideoCodec(index, _, _, _):
|
||||||
return 26 + index
|
return 26 + index
|
||||||
case .hostInfo:
|
case .disableVideoAspectScaling:
|
||||||
return 100
|
return 100
|
||||||
case .versionInfo:
|
case .enableVoipTcp:
|
||||||
return 101
|
return 101
|
||||||
|
case .hostInfo:
|
||||||
|
return 102
|
||||||
|
case .versionInfo:
|
||||||
|
return 103
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -580,6 +589,26 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
|||||||
})
|
})
|
||||||
}).start()
|
}).start()
|
||||||
})
|
})
|
||||||
|
case let .disableVideoAspectScaling(value):
|
||||||
|
return ItemListSwitchItem(presentationData: presentationData, title: "Video Cropping Optimization", value: !value, sectionId: self.section, style: .blocks, updated: { value in
|
||||||
|
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||||
|
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
||||||
|
var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings
|
||||||
|
settings.disableVideoAspectScaling = !value
|
||||||
|
return settings
|
||||||
|
})
|
||||||
|
}).start()
|
||||||
|
})
|
||||||
|
case let .enableVoipTcp(value):
|
||||||
|
return ItemListSwitchItem(presentationData: presentationData, title: "Enable VoIP TCP", value: !value, sectionId: self.section, style: .blocks, updated: { value in
|
||||||
|
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||||
|
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
||||||
|
var settings = settings as? ExperimentalUISettings ?? ExperimentalUISettings.defaultSettings
|
||||||
|
settings.enableVoipTcp = value
|
||||||
|
return settings
|
||||||
|
})
|
||||||
|
}).start()
|
||||||
|
})
|
||||||
case let .hostInfo(theme, string):
|
case let .hostInfo(theme, string):
|
||||||
return ItemListTextItem(presentationData: presentationData, text: .plain(string), sectionId: self.section)
|
return ItemListTextItem(presentationData: presentationData, text: .plain(string), sectionId: self.section)
|
||||||
case let .versionInfo(theme):
|
case let .versionInfo(theme):
|
||||||
@ -637,6 +666,9 @@ private func debugControllerEntries(presentationData: PresentationData, loggingS
|
|||||||
for i in 0 ..< codecs.count {
|
for i in 0 ..< codecs.count {
|
||||||
entries.append(.preferredVideoCodec(i, codecs[i].0, codecs[i].1, experimentalSettings.preferredVideoCodec == codecs[i].1))
|
entries.append(.preferredVideoCodec(i, codecs[i].0, codecs[i].1, experimentalSettings.preferredVideoCodec == codecs[i].1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entries.append(.disableVideoAspectScaling(experimentalSettings.disableVideoAspectScaling))
|
||||||
|
entries.append(.enableVoipTcp(experimentalSettings.enableVoipTcp))
|
||||||
|
|
||||||
if let backupHostOverride = networkSettings?.backupHostOverride {
|
if let backupHostOverride = networkSettings?.backupHostOverride {
|
||||||
entries.append(.hostInfo(presentationData.theme, "Host: \(backupHostOverride)"))
|
entries.append(.hostInfo(presentationData.theme, "Host: \(backupHostOverride)"))
|
||||||
|
@ -24,7 +24,7 @@ protocol CallControllerNodeProtocol: class {
|
|||||||
var acceptCall: (() -> Void)? { get set }
|
var acceptCall: (() -> Void)? { get set }
|
||||||
var endCall: (() -> Void)? { get set }
|
var endCall: (() -> Void)? { get set }
|
||||||
var back: (() -> Void)? { get set }
|
var back: (() -> Void)? { get set }
|
||||||
var presentCallRating: ((CallId) -> Void)? { get set }
|
var presentCallRating: ((CallId, Bool) -> Void)? { get set }
|
||||||
var present: ((ViewController) -> Void)? { get set }
|
var present: ((ViewController) -> Void)? { get set }
|
||||||
var callEnded: ((Bool) -> Void)? { get set }
|
var callEnded: ((Bool) -> Void)? { get set }
|
||||||
var dismissedInteractively: (() -> Void)? { get set }
|
var dismissedInteractively: (() -> Void)? { get set }
|
||||||
@ -230,13 +230,13 @@ public final class CallController: ViewController {
|
|||||||
let _ = self?.dismiss()
|
let _ = self?.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.controllerNode.presentCallRating = { [weak self] callId in
|
self.controllerNode.presentCallRating = { [weak self] callId, isVideo in
|
||||||
if let strongSelf = self, !strongSelf.presentedCallRating {
|
if let strongSelf = self, !strongSelf.presentedCallRating {
|
||||||
strongSelf.presentedCallRating = true
|
strongSelf.presentedCallRating = true
|
||||||
|
|
||||||
Queue.mainQueue().after(0.5, {
|
Queue.mainQueue().after(0.5, {
|
||||||
let window = strongSelf.window
|
let window = strongSelf.window
|
||||||
let controller = callRatingController(sharedContext: strongSelf.sharedContext, account: strongSelf.account, callId: callId, userInitiated: false, present: { c, a in
|
let controller = callRatingController(sharedContext: strongSelf.sharedContext, account: strongSelf.account, callId: callId, userInitiated: false, isVideo: isVideo, present: { c, a in
|
||||||
if let window = window {
|
if let window = window {
|
||||||
c.presentationArguments = a
|
c.presentationArguments = a
|
||||||
window.present(c, on: .root, blockInteraction: false, completion: {})
|
window.present(c, on: .root, blockInteraction: false, completion: {})
|
||||||
|
@ -42,18 +42,21 @@ private final class CallVideoNode: ASDisplayNode {
|
|||||||
private let isFlippedUpdated: (CallVideoNode) -> Void
|
private let isFlippedUpdated: (CallVideoNode) -> Void
|
||||||
|
|
||||||
private(set) var currentOrientation: PresentationCallVideoView.Orientation
|
private(set) var currentOrientation: PresentationCallVideoView.Orientation
|
||||||
|
private(set) var currentAspect: CGFloat = 0.0
|
||||||
|
|
||||||
|
private var previousVideoHeight: CGFloat?
|
||||||
|
|
||||||
init(videoView: PresentationCallVideoView, disabledText: String?, assumeReadyAfterTimeout: Bool, isReadyUpdated: @escaping () -> Void, orientationUpdated: @escaping () -> Void, isFlippedUpdated: @escaping (CallVideoNode) -> Void) {
|
init(videoView: PresentationCallVideoView, disabledText: String?, assumeReadyAfterTimeout: Bool, isReadyUpdated: @escaping () -> Void, orientationUpdated: @escaping () -> Void, isFlippedUpdated: @escaping (CallVideoNode) -> Void) {
|
||||||
self.isReadyUpdated = isReadyUpdated
|
self.isReadyUpdated = isReadyUpdated
|
||||||
self.isFlippedUpdated = isFlippedUpdated
|
self.isFlippedUpdated = isFlippedUpdated
|
||||||
|
|
||||||
self.videoTransformContainer = ASDisplayNode()
|
self.videoTransformContainer = ASDisplayNode()
|
||||||
self.videoTransformContainer.clipsToBounds = true
|
|
||||||
self.videoView = videoView
|
self.videoView = videoView
|
||||||
videoView.view.clipsToBounds = true
|
videoView.view.clipsToBounds = true
|
||||||
videoView.view.backgroundColor = .black
|
videoView.view.backgroundColor = .black
|
||||||
|
|
||||||
self.currentOrientation = videoView.getOrientation()
|
self.currentOrientation = videoView.getOrientation()
|
||||||
|
self.currentAspect = videoView.getAspect()
|
||||||
|
|
||||||
self.videoPausedNode = ImmediateTextNode()
|
self.videoPausedNode = ImmediateTextNode()
|
||||||
self.videoPausedNode.alpha = 0.0
|
self.videoPausedNode.alpha = 0.0
|
||||||
@ -89,13 +92,14 @@ private final class CallVideoNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.videoView.setOnOrientationUpdated { [weak self] orientation in
|
self.videoView.setOnOrientationUpdated { [weak self] orientation, aspect in
|
||||||
Queue.mainQueue().async {
|
Queue.mainQueue().async {
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if strongSelf.currentOrientation != orientation {
|
if strongSelf.currentOrientation != orientation || strongSelf.currentAspect != aspect {
|
||||||
strongSelf.currentOrientation = orientation
|
strongSelf.currentOrientation = orientation
|
||||||
|
strongSelf.currentAspect = aspect
|
||||||
orientationUpdated()
|
orientationUpdated()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,85 +168,91 @@ private final class CallVideoNode: ASDisplayNode {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateLayout(size: CGSize, cornerRadius: CGFloat, deviceOrientation: UIDeviceOrientation, transition: ContainedViewLayoutTransition) {
|
func updateLayout(size: CGSize, cornerRadius: CGFloat, isOutgoing: Bool, deviceOrientation: UIDeviceOrientation, isCompactLayout: Bool, transition: ContainedViewLayoutTransition) {
|
||||||
self.currentCornerRadius = cornerRadius
|
self.currentCornerRadius = cornerRadius
|
||||||
|
|
||||||
var rotationAngle: CGFloat
|
var rotationAngle: CGFloat
|
||||||
switch self.currentOrientation {
|
if isOutgoing {
|
||||||
case .rotation0:
|
|
||||||
rotationAngle = 0.0
|
|
||||||
case .rotation90:
|
|
||||||
rotationAngle = -CGFloat.pi / 2.0
|
|
||||||
case .rotation180:
|
|
||||||
rotationAngle = -CGFloat.pi
|
|
||||||
case .rotation270:
|
|
||||||
rotationAngle = CGFloat.pi / 2.0
|
rotationAngle = CGFloat.pi / 2.0
|
||||||
}
|
|
||||||
|
|
||||||
var additionalAngle: CGFloat = 0.0
|
|
||||||
switch deviceOrientation {
|
|
||||||
case .portrait:
|
|
||||||
additionalAngle = 0.0
|
|
||||||
case .landscapeLeft:
|
|
||||||
additionalAngle = CGFloat.pi / 2.0
|
|
||||||
case .landscapeRight:
|
|
||||||
additionalAngle = -CGFloat.pi / 2.0
|
|
||||||
case .portraitUpsideDown:
|
|
||||||
rotationAngle = -CGFloat.pi
|
|
||||||
default:
|
|
||||||
additionalAngle = 0.0
|
|
||||||
}
|
|
||||||
rotationAngle += additionalAngle
|
|
||||||
if abs(rotationAngle - (-CGFloat.pi)) < 1.0 {
|
|
||||||
rotationAngle = -CGFloat.pi + 0.001
|
|
||||||
}
|
|
||||||
|
|
||||||
var rotateFrame = abs(rotationAngle.remainder(dividingBy: CGFloat.pi)) > 1.0
|
|
||||||
|
|
||||||
var originalRotateFrame = rotateFrame
|
|
||||||
if size.width > size.height {
|
|
||||||
rotateFrame = !rotateFrame
|
|
||||||
if rotateFrame {
|
|
||||||
originalRotateFrame = true
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if rotateFrame {
|
switch self.currentOrientation {
|
||||||
originalRotateFrame = false
|
case .rotation0:
|
||||||
|
rotationAngle = 0.0
|
||||||
|
case .rotation90:
|
||||||
|
rotationAngle = CGFloat.pi / 2.0
|
||||||
|
case .rotation180:
|
||||||
|
rotationAngle = CGFloat.pi
|
||||||
|
case .rotation270:
|
||||||
|
rotationAngle = -CGFloat.pi / 2.0
|
||||||
|
}
|
||||||
|
|
||||||
|
var additionalAngle: CGFloat = 0.0
|
||||||
|
switch deviceOrientation {
|
||||||
|
case .portrait:
|
||||||
|
additionalAngle = 0.0
|
||||||
|
case .landscapeLeft:
|
||||||
|
additionalAngle = CGFloat.pi / 2.0
|
||||||
|
case .landscapeRight:
|
||||||
|
additionalAngle = -CGFloat.pi / 2.0
|
||||||
|
case .portraitUpsideDown:
|
||||||
|
rotationAngle = CGFloat.pi
|
||||||
|
default:
|
||||||
|
additionalAngle = 0.0
|
||||||
|
}
|
||||||
|
rotationAngle += additionalAngle
|
||||||
|
if abs(rotationAngle - (-CGFloat.pi)) < 1.0 {
|
||||||
|
rotationAngle = -CGFloat.pi + 0.001
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let videoFrame: CGRect
|
|
||||||
let scale: CGFloat
|
let rotateFrame = abs(rotationAngle.remainder(dividingBy: CGFloat.pi)) > 1.0
|
||||||
|
let fittingSize: CGSize
|
||||||
if rotateFrame {
|
if rotateFrame {
|
||||||
let frameSize = CGSize(width: size.height, height: size.width).aspectFitted(size)
|
fittingSize = CGSize(width: size.height, height: size.width)
|
||||||
videoFrame = CGRect(origin: CGPoint(x: floor((size.width - frameSize.width) / 2.0), y: floor((size.height - frameSize.height) / 2.0)), size: frameSize)
|
|
||||||
if size.width > size.height {
|
|
||||||
scale = frameSize.height / size.width
|
|
||||||
} else {
|
|
||||||
scale = frameSize.width / size.height
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
videoFrame = CGRect(origin: CGPoint(), size: size)
|
fittingSize = size
|
||||||
if size.width > size.height {
|
}
|
||||||
scale = 1.0
|
|
||||||
|
let unboundVideoSize = CGSize(width: self.currentAspect * 10000.0, height: 10000.0)
|
||||||
|
|
||||||
|
var fittedVideoSize = unboundVideoSize.fitted(fittingSize)
|
||||||
|
if fittedVideoSize.width < fittingSize.width || fittedVideoSize.height < fittingSize.height {
|
||||||
|
let isVideoPortrait = unboundVideoSize.width < unboundVideoSize.height
|
||||||
|
let isFittingSizePortrait = fittingSize.width < fittingSize.height
|
||||||
|
|
||||||
|
if isCompactLayout && isVideoPortrait == isFittingSizePortrait {
|
||||||
|
fittedVideoSize = unboundVideoSize.aspectFilled(fittingSize)
|
||||||
} else {
|
} else {
|
||||||
scale = 1.0
|
let maxFittingEdgeDistance: CGFloat
|
||||||
|
if isCompactLayout {
|
||||||
|
maxFittingEdgeDistance = 200.0
|
||||||
|
} else {
|
||||||
|
maxFittingEdgeDistance = 400.0
|
||||||
|
}
|
||||||
|
if fittedVideoSize.width > fittingSize.width - maxFittingEdgeDistance && fittedVideoSize.height > fittingSize.height - maxFittingEdgeDistance {
|
||||||
|
fittedVideoSize = unboundVideoSize.aspectFilled(fittingSize)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let rotatedVideoHeight: CGFloat = max(fittedVideoSize.height, fittedVideoSize.width)
|
||||||
|
|
||||||
|
let videoFrame: CGRect = CGRect(origin: CGPoint(), size: fittedVideoSize)
|
||||||
|
|
||||||
let videoPausedSize = self.videoPausedNode.updateLayout(CGSize(width: size.width - 16.0, height: 100.0))
|
let videoPausedSize = self.videoPausedNode.updateLayout(CGSize(width: size.width - 16.0, height: 100.0))
|
||||||
transition.updateFrame(node: self.videoPausedNode, frame: CGRect(origin: CGPoint(x: floor((size.width - videoPausedSize.width) / 2.0), y: floor((size.height - videoPausedSize.height) / 2.0)), size: videoPausedSize))
|
transition.updateFrame(node: self.videoPausedNode, frame: CGRect(origin: CGPoint(x: floor((size.width - videoPausedSize.width) / 2.0), y: floor((size.height - videoPausedSize.height) / 2.0)), size: videoPausedSize))
|
||||||
|
|
||||||
let previousVideoFrame = self.videoTransformContainer.frame
|
self.videoTransformContainer.bounds = CGRect(origin: CGPoint(), size: videoFrame.size)
|
||||||
self.videoTransformContainer.bounds = CGRect(origin: CGPoint(), size: size)
|
if transition.isAnimated && !videoFrame.height.isZero, let previousVideoHeight = self.previousVideoHeight, !previousVideoHeight.isZero {
|
||||||
if transition.isAnimated && !videoFrame.height.isZero && !previousVideoFrame.height.isZero {
|
let scaleDifference = previousVideoHeight / rotatedVideoHeight
|
||||||
transition.animateTransformScale(node: self.videoTransformContainer, from: previousVideoFrame.height / size.height, additive: true)
|
if abs(scaleDifference - 1.0) > 0.001 {
|
||||||
|
transition.animateTransformScale(node: self.videoTransformContainer, from: scaleDifference, additive: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
transition.updatePosition(node: self.videoTransformContainer, position: videoFrame.center)
|
self.previousVideoHeight = rotatedVideoHeight
|
||||||
transition.updateSublayerTransformScale(node: self.videoTransformContainer, scale: scale)
|
transition.updatePosition(node: self.videoTransformContainer, position: CGPoint(x: size.width / 2.0, y: size.height / 2.0))
|
||||||
|
|
||||||
let localVideoSize = originalRotateFrame ? CGSize(width: size.height, height: size.width) : size
|
|
||||||
let localVideoFrame = CGRect(origin: CGPoint(x: floor((size.width - localVideoSize.width) / 2.0), y: floor((size.height - localVideoSize.height) / 2.0)), size: localVideoSize)
|
|
||||||
|
|
||||||
|
let localVideoFrame = CGRect(origin: CGPoint(), size: videoFrame.size)
|
||||||
self.videoView.view.bounds = localVideoFrame
|
self.videoView.view.bounds = localVideoFrame
|
||||||
self.videoView.view.center = localVideoFrame.center
|
self.videoView.view.center = localVideoFrame.center
|
||||||
transition.updateTransformRotation(view: self.videoView.view, angle: rotationAngle)
|
transition.updateTransformRotation(view: self.videoView.view, angle: rotationAngle)
|
||||||
@ -392,7 +402,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
|||||||
var acceptCall: (() -> Void)?
|
var acceptCall: (() -> Void)?
|
||||||
var endCall: (() -> Void)?
|
var endCall: (() -> Void)?
|
||||||
var back: (() -> Void)?
|
var back: (() -> Void)?
|
||||||
var presentCallRating: ((CallId) -> Void)?
|
var presentCallRating: ((CallId, Bool) -> Void)?
|
||||||
var callEnded: ((Bool) -> Void)?
|
var callEnded: ((Bool) -> Void)?
|
||||||
var dismissedInteractively: (() -> Void)?
|
var dismissedInteractively: (() -> Void)?
|
||||||
var present: ((ViewController) -> Void)?
|
var present: ((ViewController) -> Void)?
|
||||||
@ -419,6 +429,8 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
|||||||
private var deviceOrientation: UIDeviceOrientation = .portrait
|
private var deviceOrientation: UIDeviceOrientation = .portrait
|
||||||
private var orientationDidChangeObserver: NSObjectProtocol?
|
private var orientationDidChangeObserver: NSObjectProtocol?
|
||||||
|
|
||||||
|
private var currentRequestedAspect: CGFloat?
|
||||||
|
|
||||||
init(sharedContext: SharedAccountContext, account: Account, presentationData: PresentationData, statusBar: StatusBar, debugInfo: Signal<(String, String), NoError>, shouldStayHiddenUntilConnection: Bool = false, easyDebugAccess: Bool, call: PresentationCall) {
|
init(sharedContext: SharedAccountContext, account: Account, presentationData: PresentationData, statusBar: StatusBar, debugInfo: Signal<(String, String), NoError>, shouldStayHiddenUntilConnection: Bool = false, easyDebugAccess: Bool, call: PresentationCall) {
|
||||||
self.sharedContext = sharedContext
|
self.sharedContext = sharedContext
|
||||||
self.account = account
|
self.account = account
|
||||||
@ -922,11 +934,18 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
|||||||
case let .error(error):
|
case let .error(error):
|
||||||
let text = self.presentationData.strings.Call_StatusFailed
|
let text = self.presentationData.strings.Call_StatusFailed
|
||||||
switch error {
|
switch error {
|
||||||
case .notSupportedByPeer:
|
case let .notSupportedByPeer(isVideo):
|
||||||
if !self.displayedVersionOutdatedAlert, let peer = self.peer {
|
if !self.displayedVersionOutdatedAlert, let peer = self.peer {
|
||||||
self.displayedVersionOutdatedAlert = true
|
self.displayedVersionOutdatedAlert = true
|
||||||
|
|
||||||
self.present?(textAlertController(sharedContext: self.sharedContext, title: nil, text: self.presentationData.strings.Call_ParticipantVersionOutdatedError(peer.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)).0, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {
|
let text: String
|
||||||
|
if isVideo {
|
||||||
|
text = self.presentationData.strings.Call_ParticipantVideoVersionOutdatedError(peer.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)).0
|
||||||
|
} else {
|
||||||
|
text = self.presentationData.strings.Call_ParticipantVersionOutdatedError(peer.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)).0
|
||||||
|
}
|
||||||
|
|
||||||
|
self.present?(textAlertController(sharedContext: self.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {
|
||||||
})]))
|
})]))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -1019,7 +1038,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
|||||||
if case let .terminated(id, _, reportRating) = callState.state, let callId = id {
|
if case let .terminated(id, _, reportRating) = callState.state, let callId = id {
|
||||||
let presentRating = reportRating || self.forceReportRating
|
let presentRating = reportRating || self.forceReportRating
|
||||||
if presentRating {
|
if presentRating {
|
||||||
self.presentCallRating?(callId)
|
self.presentCallRating?(callId, self.call.isVideo)
|
||||||
}
|
}
|
||||||
self.callEnded?(presentRating)
|
self.callEnded?(presentRating)
|
||||||
}
|
}
|
||||||
@ -1253,12 +1272,12 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
|||||||
let previewVideoSide = interpolate(from: 350.0, to: 200.0, value: 1.0 - self.pictureInPictureTransitionFraction)
|
let previewVideoSide = interpolate(from: 350.0, to: 200.0, value: 1.0 - self.pictureInPictureTransitionFraction)
|
||||||
var previewVideoSize = layout.size.aspectFitted(CGSize(width: previewVideoSide, height: previewVideoSide))
|
var previewVideoSize = layout.size.aspectFitted(CGSize(width: previewVideoSide, height: previewVideoSide))
|
||||||
previewVideoSize = CGSize(width: 30.0, height: 45.0).aspectFitted(previewVideoSize)
|
previewVideoSize = CGSize(width: 30.0, height: 45.0).aspectFitted(previewVideoSize)
|
||||||
if let minimizedVideoNode = minimizedVideoNode {
|
if let minimizedVideoNode = self.minimizedVideoNode {
|
||||||
switch minimizedVideoNode.currentOrientation {
|
switch minimizedVideoNode.currentOrientation {
|
||||||
case .rotation90, .rotation270:
|
case .rotation90, .rotation270:
|
||||||
previewVideoSize = CGSize(width: previewVideoSize.height, height: previewVideoSize.width)
|
|
||||||
default:
|
|
||||||
break
|
break
|
||||||
|
default:
|
||||||
|
previewVideoSize = CGSize(width: previewVideoSize.height, height: previewVideoSize.width)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let previewVideoY: CGFloat
|
let previewVideoY: CGFloat
|
||||||
@ -1305,6 +1324,13 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
|||||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
self.validLayout = (layout, navigationBarHeight)
|
self.validLayout = (layout, navigationBarHeight)
|
||||||
|
|
||||||
|
var mappedDeviceOrientation = self.deviceOrientation
|
||||||
|
var isCompactLayout = true
|
||||||
|
if case .regular = layout.metrics.widthClass, case .regular = layout.metrics.heightClass {
|
||||||
|
mappedDeviceOrientation = .portrait
|
||||||
|
isCompactLayout = false
|
||||||
|
}
|
||||||
|
|
||||||
var isUIHidden = self.isUIHidden
|
var isUIHidden = self.isUIHidden
|
||||||
switch self.callState?.state {
|
switch self.callState?.state {
|
||||||
case .terminated, .terminating:
|
case .terminated, .terminating:
|
||||||
@ -1445,7 +1471,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
|||||||
expandedVideoTransition.updateFrame(node: expandedVideoNode, frame: fullscreenVideoFrame)
|
expandedVideoTransition.updateFrame(node: expandedVideoNode, frame: fullscreenVideoFrame)
|
||||||
}
|
}
|
||||||
|
|
||||||
expandedVideoNode.updateLayout(size: expandedVideoNode.frame.size, cornerRadius: 0.0, deviceOrientation: self.deviceOrientation, transition: expandedVideoTransition)
|
expandedVideoNode.updateLayout(size: expandedVideoNode.frame.size, cornerRadius: 0.0, isOutgoing: expandedVideoNode === self.outgoingVideoNodeValue, deviceOrientation: mappedDeviceOrientation, isCompactLayout: isCompactLayout, transition: expandedVideoTransition)
|
||||||
|
|
||||||
if self.animateRequestedVideoOnce {
|
if self.animateRequestedVideoOnce {
|
||||||
self.animateRequestedVideoOnce = false
|
self.animateRequestedVideoOnce = false
|
||||||
@ -1495,7 +1521,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
|||||||
self.animationForExpandedVideoSnapshotView = nil
|
self.animationForExpandedVideoSnapshotView = nil
|
||||||
}
|
}
|
||||||
minimizedVideoTransition.updateFrame(node: minimizedVideoNode, frame: previewVideoFrame)
|
minimizedVideoTransition.updateFrame(node: minimizedVideoNode, frame: previewVideoFrame)
|
||||||
minimizedVideoNode.updateLayout(size: previewVideoFrame.size, cornerRadius: interpolate(from: 14.0, to: 24.0, value: self.pictureInPictureTransitionFraction), deviceOrientation: .portrait, transition: minimizedVideoTransition)
|
minimizedVideoNode.updateLayout(size: previewVideoFrame.size, cornerRadius: interpolate(from: 14.0, to: 24.0, value: self.pictureInPictureTransitionFraction), isOutgoing: minimizedVideoNode === self.outgoingVideoNodeValue, deviceOrientation: .portrait, isCompactLayout: false, transition: minimizedVideoTransition)
|
||||||
if transition.isAnimated && didAppear {
|
if transition.isAnimated && didAppear {
|
||||||
minimizedVideoNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.5)
|
minimizedVideoNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.5)
|
||||||
}
|
}
|
||||||
@ -1511,6 +1537,43 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
|||||||
if let debugNode = self.debugNode {
|
if let debugNode = self.debugNode {
|
||||||
transition.updateFrame(node: debugNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
transition.updateFrame(node: debugNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let requestedAspect: CGFloat
|
||||||
|
if case .compact = layout.metrics.widthClass, case .compact = layout.metrics.heightClass {
|
||||||
|
var isIncomingVideoRotated = false
|
||||||
|
var rotationCount = 0
|
||||||
|
|
||||||
|
switch mappedDeviceOrientation {
|
||||||
|
case .portrait:
|
||||||
|
break
|
||||||
|
case .landscapeLeft:
|
||||||
|
rotationCount += 1
|
||||||
|
case .landscapeRight:
|
||||||
|
rotationCount += 1
|
||||||
|
case .portraitUpsideDown:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if rotationCount % 2 != 0 {
|
||||||
|
isIncomingVideoRotated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isIncomingVideoRotated {
|
||||||
|
requestedAspect = layout.size.width / layout.size.height
|
||||||
|
} else {
|
||||||
|
requestedAspect = 0.0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
requestedAspect = 0.0
|
||||||
|
}
|
||||||
|
if self.currentRequestedAspect != requestedAspect {
|
||||||
|
self.currentRequestedAspect = requestedAspect
|
||||||
|
if !self.sharedContext.immediateExperimentalUISettings.disableVideoAspectScaling {
|
||||||
|
self.call.setRequestedVideoAspect(Float(requestedAspect))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func keyPressed() {
|
@objc func keyPressed() {
|
||||||
|
@ -12,6 +12,9 @@ import OverlayStatusController
|
|||||||
import AccountContext
|
import AccountContext
|
||||||
|
|
||||||
private enum CallFeedbackReason: Int32, CaseIterable {
|
private enum CallFeedbackReason: Int32, CaseIterable {
|
||||||
|
case videoDistorted
|
||||||
|
case videoLowQuality
|
||||||
|
|
||||||
case echo
|
case echo
|
||||||
case noise
|
case noise
|
||||||
case interruption
|
case interruption
|
||||||
@ -36,6 +39,19 @@ private enum CallFeedbackReason: Int32, CaseIterable {
|
|||||||
return "silent_remote"
|
return "silent_remote"
|
||||||
case .dropped:
|
case .dropped:
|
||||||
return "dropped"
|
return "dropped"
|
||||||
|
case .videoDistorted:
|
||||||
|
return "distorted_video"
|
||||||
|
case .videoLowQuality:
|
||||||
|
return "pixelated_video"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isVideoRelated: Bool {
|
||||||
|
switch self {
|
||||||
|
case .videoDistorted, .videoLowQuality:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +71,10 @@ private enum CallFeedbackReason: Int32, CaseIterable {
|
|||||||
return strings.CallFeedback_ReasonSilentRemote
|
return strings.CallFeedback_ReasonSilentRemote
|
||||||
case .dropped:
|
case .dropped:
|
||||||
return strings.CallFeedback_ReasonDropped
|
return strings.CallFeedback_ReasonDropped
|
||||||
|
case .videoDistorted:
|
||||||
|
return strings.CallFeedback_VideoReasonDistorted
|
||||||
|
case .videoLowQuality:
|
||||||
|
return strings.CallFeedback_VideoReasonLowQuality
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -214,11 +234,22 @@ private struct CallFeedbackState: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func callFeedbackControllerEntries(theme: PresentationTheme, strings: PresentationStrings, state: CallFeedbackState) -> [CallFeedbackControllerEntry] {
|
private func callFeedbackControllerEntries(theme: PresentationTheme, strings: PresentationStrings, state: CallFeedbackState, isVideo: Bool) -> [CallFeedbackControllerEntry] {
|
||||||
var entries: [CallFeedbackControllerEntry] = []
|
var entries: [CallFeedbackControllerEntry] = []
|
||||||
|
|
||||||
entries.append(.reasonsHeader(theme, strings.CallFeedback_WhatWentWrong))
|
entries.append(.reasonsHeader(theme, strings.CallFeedback_WhatWentWrong))
|
||||||
|
if isVideo {
|
||||||
|
for reason in CallFeedbackReason.allCases {
|
||||||
|
if !reason.isVideoRelated {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
entries.append(.reason(theme, reason, CallFeedbackReason.localizedString(for: reason, strings: strings), state.reasons.contains(reason)))
|
||||||
|
}
|
||||||
|
}
|
||||||
for reason in CallFeedbackReason.allCases {
|
for reason in CallFeedbackReason.allCases {
|
||||||
|
if reason.isVideoRelated {
|
||||||
|
continue
|
||||||
|
}
|
||||||
entries.append(.reason(theme, reason, CallFeedbackReason.localizedString(for: reason, strings: strings), state.reasons.contains(reason)))
|
entries.append(.reason(theme, reason, CallFeedbackReason.localizedString(for: reason, strings: strings), state.reasons.contains(reason)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,7 +261,7 @@ private func callFeedbackControllerEntries(theme: PresentationTheme, strings: Pr
|
|||||||
return entries
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
public func callFeedbackController(sharedContext: SharedAccountContext, account: Account, callId: CallId, rating: Int, userInitiated: Bool) -> ViewController {
|
public func callFeedbackController(sharedContext: SharedAccountContext, account: Account, callId: CallId, rating: Int, userInitiated: Bool, isVideo: Bool) -> ViewController {
|
||||||
let initialState = CallFeedbackState()
|
let initialState = CallFeedbackState()
|
||||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||||
let stateValue = Atomic(value: initialState)
|
let stateValue = Atomic(value: initialState)
|
||||||
@ -290,7 +321,7 @@ public func callFeedbackController(sharedContext: SharedAccountContext, account:
|
|||||||
})
|
})
|
||||||
|
|
||||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.CallFeedback_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.CallFeedback_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: callFeedbackControllerEntries(theme: presentationData.theme, strings: presentationData.strings, state: state), style: .blocks, animateChanges: false)
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: callFeedbackControllerEntries(theme: presentationData.theme, strings: presentationData.strings, state: state, isVideo: isVideo), style: .blocks, animateChanges: false)
|
||||||
|
|
||||||
return (controllerState, (listState, arguments))
|
return (controllerState, (listState, arguments))
|
||||||
}
|
}
|
||||||
|
@ -246,7 +246,7 @@ func rateCallAndSendLogs(account: Account, callId: CallId, starsCount: Int, comm
|
|||||||
let rate = rateCall(account: account, callId: callId, starsCount: Int32(starsCount), comment: comment, userInitiated: userInitiated)
|
let rate = rateCall(account: account, callId: callId, starsCount: Int32(starsCount), comment: comment, userInitiated: userInitiated)
|
||||||
if includeLogs {
|
if includeLogs {
|
||||||
let id = arc4random64()
|
let id = arc4random64()
|
||||||
let name = "\(callId.id)_\(callId.accessHash).log"
|
let name = "\(callId.id)_\(callId.accessHash).log.json"
|
||||||
let path = callLogsPath(account: account) + "/" + name
|
let path = callLogsPath(account: account) + "/" + name
|
||||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)])
|
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)])
|
||||||
let message = EnqueueMessage.message(text: comment, attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil)
|
let message = EnqueueMessage.message(text: comment, attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil)
|
||||||
@ -266,7 +266,7 @@ func rateCallAndSendLogs(account: Account, callId: CallId, starsCount: Int, comm
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func callRatingController(sharedContext: SharedAccountContext, account: Account, callId: CallId, userInitiated: Bool, present: @escaping (ViewController, Any) -> Void, push: @escaping (ViewController) -> Void) -> AlertController {
|
public func callRatingController(sharedContext: SharedAccountContext, account: Account, callId: CallId, userInitiated: Bool, isVideo: Bool, present: @escaping (ViewController, Any) -> Void, push: @escaping (ViewController) -> Void) -> AlertController {
|
||||||
let presentationData = sharedContext.currentPresentationData.with { $0 }
|
let presentationData = sharedContext.currentPresentationData.with { $0 }
|
||||||
let theme = presentationData.theme
|
let theme = presentationData.theme
|
||||||
let strings = presentationData.strings
|
let strings = presentationData.strings
|
||||||
@ -282,7 +282,7 @@ public func callRatingController(sharedContext: SharedAccountContext, account: A
|
|||||||
}, apply: { rating in
|
}, apply: { rating in
|
||||||
dismissImpl?(true)
|
dismissImpl?(true)
|
||||||
if rating < 4 {
|
if rating < 4 {
|
||||||
push(callFeedbackController(sharedContext: sharedContext, account: account, callId: callId, rating: rating, userInitiated: userInitiated))
|
push(callFeedbackController(sharedContext: sharedContext, account: account, callId: callId, rating: rating, userInitiated: userInitiated, isVideo: isVideo))
|
||||||
} else {
|
} else {
|
||||||
let _ = rateCallAndSendLogs(account: account, callId: callId, starsCount: rating, comment: "", userInitiated: userInitiated, includeLogs: false).start()
|
let _ = rateCallAndSendLogs(account: account, callId: callId, starsCount: rating, comment: "", userInitiated: userInitiated, includeLogs: false).start()
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol
|
|||||||
var endCall: (() -> Void)?
|
var endCall: (() -> Void)?
|
||||||
var setIsVideoPaused: ((Bool) -> Void)?
|
var setIsVideoPaused: ((Bool) -> Void)?
|
||||||
var back: (() -> Void)?
|
var back: (() -> Void)?
|
||||||
var presentCallRating: ((CallId) -> Void)?
|
var presentCallRating: ((CallId, Bool) -> Void)?
|
||||||
var callEnded: ((Bool) -> Void)?
|
var callEnded: ((Bool) -> Void)?
|
||||||
var dismissedInteractively: (() -> Void)?
|
var dismissedInteractively: (() -> Void)?
|
||||||
var present: ((ViewController) -> Void)?
|
var present: ((ViewController) -> Void)?
|
||||||
@ -307,7 +307,7 @@ final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol
|
|||||||
if case let .terminated(id, _, reportRating) = callState.state, let callId = id {
|
if case let .terminated(id, _, reportRating) = callState.state, let callId = id {
|
||||||
let presentRating = reportRating || self.forceReportRating
|
let presentRating = reportRating || self.forceReportRating
|
||||||
if presentRating {
|
if presentRating {
|
||||||
self.presentCallRating?(callId)
|
self.presentCallRating?(callId, false)
|
||||||
}
|
}
|
||||||
self.callEnded?(presentRating)
|
self.callEnded?(presentRating)
|
||||||
}
|
}
|
||||||
|
@ -169,6 +169,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
public let isOutgoing: Bool
|
public let isOutgoing: Bool
|
||||||
public var isVideo: Bool
|
public var isVideo: Bool
|
||||||
public var isVideoPossible: Bool
|
public var isVideoPossible: Bool
|
||||||
|
private let enableStunMarking: Bool
|
||||||
public let preferredVideoCodec: String?
|
public let preferredVideoCodec: String?
|
||||||
public let peer: Peer?
|
public let peer: Peer?
|
||||||
|
|
||||||
@ -184,6 +185,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
private var callContextState: OngoingCallContextState?
|
private var callContextState: OngoingCallContextState?
|
||||||
private var ongoingContext: OngoingCallContext?
|
private var ongoingContext: OngoingCallContext?
|
||||||
private var ongoingContextStateDisposable: Disposable?
|
private var ongoingContextStateDisposable: Disposable?
|
||||||
|
private var requestedVideoAspect: Float?
|
||||||
private var reception: Int32?
|
private var reception: Int32?
|
||||||
private var receptionDisposable: Disposable?
|
private var receptionDisposable: Disposable?
|
||||||
private var reportedIncomingCall = false
|
private var reportedIncomingCall = false
|
||||||
@ -265,6 +267,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
updatedNetworkType: Signal<NetworkType, NoError>,
|
updatedNetworkType: Signal<NetworkType, NoError>,
|
||||||
startWithVideo: Bool,
|
startWithVideo: Bool,
|
||||||
isVideoPossible: Bool,
|
isVideoPossible: Bool,
|
||||||
|
enableStunMarking: Bool,
|
||||||
preferredVideoCodec: String?
|
preferredVideoCodec: String?
|
||||||
) {
|
) {
|
||||||
self.account = account
|
self.account = account
|
||||||
@ -292,6 +295,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
self.isOutgoing = isOutgoing
|
self.isOutgoing = isOutgoing
|
||||||
self.isVideo = initialState?.type == .video
|
self.isVideo = initialState?.type == .video
|
||||||
self.isVideoPossible = isVideoPossible
|
self.isVideoPossible = isVideoPossible
|
||||||
|
self.enableStunMarking = enableStunMarking
|
||||||
self.preferredVideoCodec = preferredVideoCodec
|
self.preferredVideoCodec = preferredVideoCodec
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.isVideo = startWithVideo
|
self.isVideo = startWithVideo
|
||||||
@ -606,9 +610,12 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
if let _ = audioSessionControl, !wasActive || previousControl == nil {
|
if let _ = audioSessionControl, !wasActive || previousControl == nil {
|
||||||
let logName = "\(id.id)_\(id.accessHash)"
|
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, audioSessionActive: self.audioSessionActive.get(), logName: logName, preferredVideoCodec: self.preferredVideoCodec)
|
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, enableStunMarking: self.enableStunMarking, audioSessionActive: self.audioSessionActive.get(), logName: logName, preferredVideoCodec: self.preferredVideoCodec)
|
||||||
self.ongoingContext = ongoingContext
|
self.ongoingContext = ongoingContext
|
||||||
ongoingContext.setIsMuted(self.isMutedValue)
|
ongoingContext.setIsMuted(self.isMutedValue)
|
||||||
|
if let requestedVideoAspect = self.requestedVideoAspect {
|
||||||
|
ongoingContext.setRequestedVideoAspect(requestedVideoAspect)
|
||||||
|
}
|
||||||
|
|
||||||
self.debugInfoValue.set(ongoingContext.debugInfo())
|
self.debugInfoValue.set(ongoingContext.debugInfo())
|
||||||
|
|
||||||
@ -848,6 +855,11 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func setRequestedVideoAspect(_ aspect: Float) {
|
||||||
|
self.requestedVideoAspect = aspect
|
||||||
|
self.ongoingContext?.setRequestedVideoAspect(aspect)
|
||||||
|
}
|
||||||
|
|
||||||
public func disableVideo() {
|
public func disableVideo() {
|
||||||
if let _ = self.videoCapturer {
|
if let _ = self.videoCapturer {
|
||||||
self.videoCapturer = nil
|
self.videoCapturer = nil
|
||||||
@ -909,8 +921,15 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
return .rotation0
|
return .rotation0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getAspect: { [weak view] in
|
||||||
|
if let view = view {
|
||||||
|
return view.getAspect()
|
||||||
|
} else {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
setOnOrientationUpdated: { f in
|
setOnOrientationUpdated: { f in
|
||||||
setOnOrientationUpdated { value in
|
setOnOrientationUpdated { value, aspect in
|
||||||
let mappedValue: PresentationCallVideoView.Orientation
|
let mappedValue: PresentationCallVideoView.Orientation
|
||||||
switch value {
|
switch value {
|
||||||
case .rotation0:
|
case .rotation0:
|
||||||
@ -922,7 +941,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
case .rotation270:
|
case .rotation270:
|
||||||
mappedValue = .rotation270
|
mappedValue = .rotation270
|
||||||
}
|
}
|
||||||
f?(mappedValue)
|
f?(mappedValue, aspect)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setOnIsMirroredUpdated: { f in
|
setOnIsMirroredUpdated: { f in
|
||||||
@ -971,8 +990,15 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
return .rotation0
|
return .rotation0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getAspect: { [weak view] in
|
||||||
|
if let view = view {
|
||||||
|
return view.getAspect()
|
||||||
|
} else {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
setOnOrientationUpdated: { f in
|
setOnOrientationUpdated: { f in
|
||||||
setOnOrientationUpdated { value in
|
setOnOrientationUpdated { value, aspect in
|
||||||
let mappedValue: PresentationCallVideoView.Orientation
|
let mappedValue: PresentationCallVideoView.Orientation
|
||||||
switch value {
|
switch value {
|
||||||
case .rotation0:
|
case .rotation0:
|
||||||
@ -984,7 +1010,7 @@ public final class PresentationCallImpl: PresentationCall {
|
|||||||
case .rotation270:
|
case .rotation270:
|
||||||
mappedValue = .rotation270
|
mappedValue = .rotation270
|
||||||
}
|
}
|
||||||
f?(mappedValue)
|
f?(mappedValue, aspect)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setOnIsMirroredUpdated: { f in
|
setOnIsMirroredUpdated: { f in
|
||||||
|
@ -17,45 +17,14 @@ private func callKitIntegrationIfEnabled(_ integration: CallKitIntegration?, set
|
|||||||
return enabled ? integration : nil
|
return enabled ? integration : nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private func auxiliaryServers(appConfiguration: AppConfiguration) -> [CallAuxiliaryServer] {
|
private func shouldEnableStunMarking(appConfiguration: AppConfiguration) -> Bool {
|
||||||
guard let data = appConfiguration.data else {
|
guard let data = appConfiguration.data else {
|
||||||
return []
|
return true
|
||||||
}
|
}
|
||||||
guard let servers = data["rtc_servers"] as? [[String: Any]] else {
|
guard let enableStunMarking = data["voip_enable_stun_marking"] as? Bool else {
|
||||||
return []
|
return true
|
||||||
}
|
}
|
||||||
var result: [CallAuxiliaryServer] = []
|
return enableStunMarking
|
||||||
for server in servers {
|
|
||||||
guard let host = server["host"] as? String else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
guard let portString = server["port"] as? String else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
guard let username = server["username"] as? String else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
guard let password = server["password"] as? String else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
guard let port = Int(portString) else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
result.append(CallAuxiliaryServer(
|
|
||||||
host: host,
|
|
||||||
port: port,
|
|
||||||
connection: .stun
|
|
||||||
))
|
|
||||||
result.append(CallAuxiliaryServer(
|
|
||||||
host: host,
|
|
||||||
port: port,
|
|
||||||
connection: .turn(
|
|
||||||
username: username,
|
|
||||||
password: password
|
|
||||||
)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum CurrentCall {
|
private enum CurrentCall {
|
||||||
@ -309,11 +278,12 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
isOutgoing: false,
|
isOutgoing: false,
|
||||||
peer: firstState.1,
|
peer: firstState.1,
|
||||||
proxyServer: strongSelf.proxyServer,
|
proxyServer: strongSelf.proxyServer,
|
||||||
auxiliaryServers: auxiliaryServers(appConfiguration: appConfiguration),
|
auxiliaryServers: [],
|
||||||
currentNetworkType: firstState.4,
|
currentNetworkType: firstState.4,
|
||||||
updatedNetworkType: firstState.0.networkType,
|
updatedNetworkType: firstState.0.networkType,
|
||||||
startWithVideo: firstState.2.isVideo,
|
startWithVideo: firstState.2.isVideo,
|
||||||
isVideoPossible: firstState.2.isVideoPossible,
|
isVideoPossible: firstState.2.isVideoPossible,
|
||||||
|
enableStunMarking: shouldEnableStunMarking(appConfiguration: appConfiguration),
|
||||||
preferredVideoCodec: experimentalSettings.preferredVideoCodec
|
preferredVideoCodec: experimentalSettings.preferredVideoCodec
|
||||||
)
|
)
|
||||||
strongSelf.updateCurrentCall(call)
|
strongSelf.updateCurrentCall(call)
|
||||||
@ -551,11 +521,12 @@ public final class PresentationCallManagerImpl: PresentationCallManager {
|
|||||||
isOutgoing: true,
|
isOutgoing: true,
|
||||||
peer: nil,
|
peer: nil,
|
||||||
proxyServer: strongSelf.proxyServer,
|
proxyServer: strongSelf.proxyServer,
|
||||||
auxiliaryServers: auxiliaryServers(appConfiguration: appConfiguration),
|
auxiliaryServers: [],
|
||||||
currentNetworkType: currentNetworkType,
|
currentNetworkType: currentNetworkType,
|
||||||
updatedNetworkType: account.networkType,
|
updatedNetworkType: account.networkType,
|
||||||
startWithVideo: isVideo,
|
startWithVideo: isVideo,
|
||||||
isVideoPossible: isVideoPossible,
|
isVideoPossible: isVideoPossible,
|
||||||
|
enableStunMarking: shouldEnableStunMarking(appConfiguration: appConfiguration),
|
||||||
preferredVideoCodec: experimentalSettings.preferredVideoCodec
|
preferredVideoCodec: experimentalSettings.preferredVideoCodec
|
||||||
)
|
)
|
||||||
strongSelf.updateCurrentCall(call)
|
strongSelf.updateCurrentCall(call)
|
||||||
|
@ -2878,10 +2878,10 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
|||||||
}
|
}
|
||||||
|
|
||||||
// could be the reason for unbounded slowdown, needs investigation
|
// could be the reason for unbounded slowdown, needs investigation
|
||||||
/*for (peerIdAndNamespace, pts) in clearHolesFromPreviousStateForChannelMessagesWithPts {
|
for (peerIdAndNamespace, pts) in clearHolesFromPreviousStateForChannelMessagesWithPts {
|
||||||
var upperMessageId: Int32?
|
var upperMessageId: Int32?
|
||||||
var lowerMessageId: Int32?
|
var lowerMessageId: Int32?
|
||||||
transaction.scanMessageAttributes(peerId: peerIdAndNamespace.peerId, namespace: peerIdAndNamespace.namespace, { id, attributes in
|
transaction.scanMessageAttributes(peerId: peerIdAndNamespace.peerId, namespace: peerIdAndNamespace.namespace, limit: 200, { id, attributes in
|
||||||
for attribute in attributes {
|
for attribute in attributes {
|
||||||
if let attribute = attribute as? ChannelMessageStateVersionAttribute {
|
if let attribute = attribute as? ChannelMessageStateVersionAttribute {
|
||||||
if attribute.pts >= pts {
|
if attribute.pts >= pts {
|
||||||
@ -2906,7 +2906,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
|||||||
transaction.removeHole(peerId: peerIdAndNamespace.peerId, namespace: peerIdAndNamespace.namespace, space: .everywhere, range: lowerMessageId ... upperMessageId)
|
transaction.removeHole(peerId: peerIdAndNamespace.peerId, namespace: peerIdAndNamespace.namespace, space: .everywhere, range: lowerMessageId ... upperMessageId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
if !peerActivityTimestamps.isEmpty {
|
if !peerActivityTimestamps.isEmpty {
|
||||||
updatePeerPresenceLastActivities(transaction: transaction, accountPeerId: accountPeerId, activities: peerActivityTimestamps)
|
updatePeerPresenceLastActivities(transaction: transaction, accountPeerId: accountPeerId, activities: peerActivityTimestamps)
|
||||||
|
@ -11,44 +11,9 @@ private let minLayer: Int32 = 65
|
|||||||
public enum CallSessionError: Equatable {
|
public enum CallSessionError: Equatable {
|
||||||
case generic
|
case generic
|
||||||
case privacyRestricted
|
case privacyRestricted
|
||||||
case notSupportedByPeer
|
case notSupportedByPeer(isVideo: Bool)
|
||||||
case serverProvided(String)
|
case serverProvided(text: String)
|
||||||
case disconnected
|
case disconnected
|
||||||
|
|
||||||
public static func ==(lhs: CallSessionError, rhs: CallSessionError) -> Bool {
|
|
||||||
switch lhs {
|
|
||||||
case .generic:
|
|
||||||
if case .generic = rhs {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
case .privacyRestricted:
|
|
||||||
if case .privacyRestricted = rhs {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
case .notSupportedByPeer:
|
|
||||||
if case .notSupportedByPeer = rhs {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
case let .serverProvided(text):
|
|
||||||
if case .serverProvided(text) = rhs {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
case .disconnected:
|
|
||||||
if case .disconnected = rhs {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum CallSessionEndedType {
|
public enum CallSessionEndedType {
|
||||||
@ -1204,12 +1169,12 @@ private func requestCallSession(postbox: Postbox, network: Network, peerId: Peer
|
|||||||
|> `catch` { error -> Signal<RequestCallSessionResult, NoError> in
|
|> `catch` { error -> Signal<RequestCallSessionResult, NoError> in
|
||||||
switch error.errorDescription {
|
switch error.errorDescription {
|
||||||
case "PARTICIPANT_VERSION_OUTDATED":
|
case "PARTICIPANT_VERSION_OUTDATED":
|
||||||
return .single(.failed(.notSupportedByPeer))
|
return .single(.failed(.notSupportedByPeer(isVideo: isVideo)))
|
||||||
case "USER_PRIVACY_RESTRICTED":
|
case "USER_PRIVACY_RESTRICTED":
|
||||||
return .single(.failed(.privacyRestricted))
|
return .single(.failed(.privacyRestricted))
|
||||||
default:
|
default:
|
||||||
if error.errorCode == 406 {
|
if error.errorCode == 406 {
|
||||||
return .single(.failed(.serverProvided(error.errorDescription)))
|
return .single(.failed(.serverProvided(text: error.errorDescription)))
|
||||||
} else {
|
} else {
|
||||||
return .single(.failed(.generic))
|
return .single(.failed(.generic))
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -1710,9 +1710,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}, completed: {})
|
}, completed: {})
|
||||||
}
|
}
|
||||||
}, rateCall: { [weak self] message, callId in
|
}, rateCall: { [weak self] message, callId, isVideo in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let controller = callRatingController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, callId: callId, userInitiated: true, present: { [weak self] c, a in
|
let controller = callRatingController(sharedContext: strongSelf.context.sharedContext, account: strongSelf.context.account, callId: callId, userInitiated: true, isVideo: isVideo, present: { [weak self] c, a in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.present(c, in: .window(.root), with: a)
|
strongSelf.present(c, in: .window(.root), with: a)
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ public final class ChatControllerInteraction {
|
|||||||
let navigateToFirstDateMessage: (Int32) -> Void
|
let navigateToFirstDateMessage: (Int32) -> Void
|
||||||
let requestRedeliveryOfFailedMessages: (MessageId) -> Void
|
let requestRedeliveryOfFailedMessages: (MessageId) -> Void
|
||||||
let addContact: (String) -> Void
|
let addContact: (String) -> Void
|
||||||
let rateCall: (Message, CallId) -> Void
|
let rateCall: (Message, CallId, Bool) -> Void
|
||||||
let requestSelectMessagePollOptions: (MessageId, [Data]) -> Void
|
let requestSelectMessagePollOptions: (MessageId, [Data]) -> Void
|
||||||
let requestOpenMessagePollResults: (MessageId, MediaId) -> Void
|
let requestOpenMessagePollResults: (MessageId, MediaId) -> Void
|
||||||
let openAppStorePage: () -> Void
|
let openAppStorePage: () -> Void
|
||||||
@ -138,7 +138,7 @@ public final class ChatControllerInteraction {
|
|||||||
var searchTextHighightState: (String, [MessageIndex])?
|
var searchTextHighightState: (String, [MessageIndex])?
|
||||||
var seenOneTimeAnimatedMedia = Set<MessageId>()
|
var seenOneTimeAnimatedMedia = Set<MessageId>()
|
||||||
|
|
||||||
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId, Bool) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> ChatControllerInteractionSwipeAction, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, NSAttributedString, TextSelectionAction) -> Void, updateMessageLike: @escaping (MessageId, Bool) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, displayPollSolution: @escaping (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void, displayPsa: @escaping (String, ASDisplayNode) -> Void, displayDiceTooltip: @escaping (TelegramMediaDice) -> Void, animateDiceSuccess: @escaping () -> Void, greetingStickerNode: @escaping () -> (ASDisplayNode, ASDisplayNode, ASDisplayNode, () -> Void)?, openPeerContextMenu: @escaping (Peer, ASDisplayNode, CGRect, ContextGesture?) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
|
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId, Bool) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> ChatControllerInteractionSwipeAction, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId, Bool) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, NSAttributedString, TextSelectionAction) -> Void, updateMessageLike: @escaping (MessageId, Bool) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, displayPollSolution: @escaping (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void, displayPsa: @escaping (String, ASDisplayNode) -> Void, displayDiceTooltip: @escaping (TelegramMediaDice) -> Void, animateDiceSuccess: @escaping () -> Void, greetingStickerNode: @escaping () -> (ASDisplayNode, ASDisplayNode, ASDisplayNode, () -> Void)?, openPeerContextMenu: @escaping (Peer, ASDisplayNode, CGRect, ContextGesture?) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
|
||||||
self.openMessage = openMessage
|
self.openMessage = openMessage
|
||||||
self.openPeer = openPeer
|
self.openPeer = openPeer
|
||||||
self.openPeerMention = openPeerMention
|
self.openPeerMention = openPeerMention
|
||||||
@ -228,7 +228,7 @@ public final class ChatControllerInteraction {
|
|||||||
}, navigateToFirstDateMessage: { _ in
|
}, navigateToFirstDateMessage: { _ in
|
||||||
}, requestRedeliveryOfFailedMessages: { _ in
|
}, requestRedeliveryOfFailedMessages: { _ in
|
||||||
}, addContact: { _ in
|
}, addContact: { _ in
|
||||||
}, rateCall: { _, _ in
|
}, rateCall: { _, _, _ in
|
||||||
}, requestSelectMessagePollOptions: { _, _ in
|
}, requestSelectMessagePollOptions: { _, _ in
|
||||||
}, requestOpenMessagePollResults: { _, _ in
|
}, requestOpenMessagePollResults: { _, _ in
|
||||||
}, openAppStorePage: {
|
}, openAppStorePage: {
|
||||||
|
@ -1484,7 +1484,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
strongSelf._buttonKeyboardMessage.set(.single(transition.keyboardButtonsMessage))
|
strongSelf._buttonKeyboardMessage.set(.single(transition.keyboardButtonsMessage))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if transition.animateIn || animateIn {
|
if transition.animateIn || animateIn {
|
||||||
let heightNorm = strongSelf.bounds.height - strongSelf.insets.top
|
let heightNorm = strongSelf.bounds.height - strongSelf.insets.top
|
||||||
strongSelf.forEachVisibleItemNode { itemNode in
|
strongSelf.forEachVisibleItemNode { itemNode in
|
||||||
if let itemNode = itemNode as? ChatMessageItemView {
|
if let itemNode = itemNode as? ChatMessageItemView {
|
||||||
@ -1502,7 +1502,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
|||||||
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: delay)
|
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: delay)
|
||||||
itemNode.layer.animateScale(from: 0.9, to: 1.0, duration: 0.4, delay: delay, timingFunction: kCAMediaTimingFunctionSpring)
|
itemNode.layer.animateScale(from: 0.9, to: 1.0, duration: 0.4, delay: delay, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
if let scrolledToIndex = transition.scrolledToIndex {
|
if let scrolledToIndex = transition.scrolledToIndex {
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
|
@ -405,20 +405,22 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
|||||||
|
|
||||||
if data.messageActions.options.contains(.rateCall) {
|
if data.messageActions.options.contains(.rateCall) {
|
||||||
var callId: CallId?
|
var callId: CallId?
|
||||||
|
var isVideo: Bool = false
|
||||||
for media in message.media {
|
for media in message.media {
|
||||||
if let action = media as? TelegramMediaAction, case let .phoneCall(id, discardReason, _, _) = action.action {
|
if let action = media as? TelegramMediaAction, case let .phoneCall(id, discardReason, _, isVideoValue) = action.action {
|
||||||
|
isVideo = isVideoValue
|
||||||
if discardReason != .busy && discardReason != .missed {
|
if discardReason != .busy && discardReason != .missed {
|
||||||
if let logName = callLogNameForId(id: id, account: context.account) {
|
if let logName = callLogNameForId(id: id, account: context.account) {
|
||||||
let logsPath = callLogsPath(account: context.account)
|
let logsPath = callLogsPath(account: context.account)
|
||||||
let logPath = logsPath + "/" + logName
|
let logPath = logsPath + "/" + logName
|
||||||
let start = logName.index(logName.startIndex, offsetBy: "\(id)".count + 1)
|
let start = logName.index(logName.startIndex, offsetBy: "\(id)".count + 1)
|
||||||
let end = logName.index(logName.endIndex, offsetBy: -4)
|
let end = logName.index(logName.endIndex, offsetBy: -4 - 5)
|
||||||
let accessHash = logName[start..<end]
|
let accessHash = logName[start..<end]
|
||||||
if let accessHash = Int64(accessHash) {
|
if let accessHash = Int64(accessHash) {
|
||||||
callId = CallId(id: id, accessHash: accessHash)
|
callId = CallId(id: id, accessHash: accessHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
actions.append(.action(ContextMenuActionItem(text: "Share Statistics", icon: { theme in
|
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Call_ShareStats, icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.actionSheet.primaryTextColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.actionSheet.primaryTextColor)
|
||||||
}, action: { _, f in
|
}, action: { _, f in
|
||||||
f(.dismissWithoutContent)
|
f(.dismissWithoutContent)
|
||||||
@ -446,7 +448,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
|||||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Call_RateCall, icon: { theme in
|
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Call_RateCall, icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Rate"), color: theme.actionSheet.primaryTextColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Rate"), color: theme.actionSheet.primaryTextColor)
|
||||||
}, action: { _, f in
|
}, action: { _, f in
|
||||||
let _ = controllerInteraction.rateCall(message, callId)
|
let _ = controllerInteraction.rateCall(message, callId, isVideo)
|
||||||
f(.dismissWithoutContent)
|
f(.dismissWithoutContent)
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
@ -868,7 +868,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
statusNode.backgroundNodeColor = backgroundNodeColor
|
statusNode.backgroundNodeColor = backgroundNodeColor
|
||||||
}
|
}
|
||||||
|
|
||||||
if state != .none && isVoice && self.playbackAudioLevelView == nil {
|
if state != .none && isVoice && self.playbackAudioLevelView == nil && false {
|
||||||
let blobFrame = progressFrame.insetBy(dx: -12.0, dy: -12.0)
|
let blobFrame = progressFrame.insetBy(dx: -12.0, dy: -12.0)
|
||||||
let playbackAudioLevelView = VoiceBlobView(
|
let playbackAudioLevelView = VoiceBlobView(
|
||||||
frame: blobFrame,
|
frame: blobFrame,
|
||||||
|
@ -425,7 +425,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
}, navigateToFirstDateMessage: { _ in
|
}, navigateToFirstDateMessage: { _ in
|
||||||
}, requestRedeliveryOfFailedMessages: { _ in
|
}, requestRedeliveryOfFailedMessages: { _ in
|
||||||
}, addContact: { _ in
|
}, addContact: { _ in
|
||||||
}, rateCall: { _, _ in
|
}, rateCall: { _, _, _ in
|
||||||
}, requestSelectMessagePollOptions: { _, _ in
|
}, requestSelectMessagePollOptions: { _, _ in
|
||||||
}, requestOpenMessagePollResults: { _, _ in
|
}, requestOpenMessagePollResults: { _, _ in
|
||||||
}, openAppStorePage: { [weak self] in
|
}, openAppStorePage: { [weak self] in
|
||||||
|
@ -121,7 +121,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
|
|||||||
}, navigateToFirstDateMessage: { _ in
|
}, navigateToFirstDateMessage: { _ in
|
||||||
}, requestRedeliveryOfFailedMessages: { _ in
|
}, requestRedeliveryOfFailedMessages: { _ in
|
||||||
}, addContact: { _ in
|
}, addContact: { _ in
|
||||||
}, rateCall: { _, _ in
|
}, rateCall: { _, _, _ in
|
||||||
}, requestSelectMessagePollOptions: { _, _ in
|
}, requestSelectMessagePollOptions: { _, _ in
|
||||||
}, requestOpenMessagePollResults: { _, _ in
|
}, requestOpenMessagePollResults: { _, _ in
|
||||||
}, openAppStorePage: {
|
}, openAppStorePage: {
|
||||||
|
@ -108,7 +108,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
|||||||
}, navigateToFirstDateMessage: { _ in
|
}, navigateToFirstDateMessage: { _ in
|
||||||
}, requestRedeliveryOfFailedMessages: { _ in
|
}, requestRedeliveryOfFailedMessages: { _ in
|
||||||
}, addContact: { _ in
|
}, addContact: { _ in
|
||||||
}, rateCall: { _, _ in
|
}, rateCall: { _, _, _ in
|
||||||
}, requestSelectMessagePollOptions: { _, _ in
|
}, requestSelectMessagePollOptions: { _, _ in
|
||||||
}, requestOpenMessagePollResults: { _, _ in
|
}, requestOpenMessagePollResults: { _, _ in
|
||||||
}, openAppStorePage: {
|
}, openAppStorePage: {
|
||||||
|
@ -1930,7 +1930,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}, navigateToFirstDateMessage: { _ in
|
}, navigateToFirstDateMessage: { _ in
|
||||||
}, requestRedeliveryOfFailedMessages: { _ in
|
}, requestRedeliveryOfFailedMessages: { _ in
|
||||||
}, addContact: { _ in
|
}, addContact: { _ in
|
||||||
}, rateCall: { _, _ in
|
}, rateCall: { _, _, _ in
|
||||||
}, requestSelectMessagePollOptions: { _, _ in
|
}, requestSelectMessagePollOptions: { _, _ in
|
||||||
}, requestOpenMessagePollResults: { _, _ in
|
}, requestOpenMessagePollResults: { _, _ in
|
||||||
}, openAppStorePage: {
|
}, openAppStorePage: {
|
||||||
|
@ -413,7 +413,7 @@ public class PeerMediaCollectionController: TelegramBaseController {
|
|||||||
}, navigateToFirstDateMessage: { _ in
|
}, navigateToFirstDateMessage: { _ in
|
||||||
}, requestRedeliveryOfFailedMessages: { _ in
|
}, requestRedeliveryOfFailedMessages: { _ in
|
||||||
}, addContact: { _ in
|
}, addContact: { _ in
|
||||||
}, rateCall: { _, _ in
|
}, rateCall: { _, _, _ in
|
||||||
}, requestSelectMessagePollOptions: { _, _ in
|
}, requestSelectMessagePollOptions: { _, _ in
|
||||||
}, requestOpenMessagePollResults: { _, _ in
|
}, requestOpenMessagePollResults: { _, _ in
|
||||||
}, openAppStorePage: {
|
}, openAppStorePage: {
|
||||||
|
@ -1171,7 +1171,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}, navigateToFirstDateMessage: { _ in
|
}, navigateToFirstDateMessage: { _ in
|
||||||
}, requestRedeliveryOfFailedMessages: { _ in
|
}, requestRedeliveryOfFailedMessages: { _ in
|
||||||
}, addContact: { _ in
|
}, addContact: { _ in
|
||||||
}, rateCall: { _, _ in
|
}, rateCall: { _, _, _ in
|
||||||
}, requestSelectMessagePollOptions: { _, _ in
|
}, requestSelectMessagePollOptions: { _, _ in
|
||||||
}, requestOpenMessagePollResults: { _, _ in
|
}, requestOpenMessagePollResults: { _, _ in
|
||||||
}, openAppStorePage: {
|
}, openAppStorePage: {
|
||||||
|
@ -12,6 +12,8 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
|
|||||||
public var playerEmbedding: Bool
|
public var playerEmbedding: Bool
|
||||||
public var playlistPlayback: Bool
|
public var playlistPlayback: Bool
|
||||||
public var preferredVideoCodec: String?
|
public var preferredVideoCodec: String?
|
||||||
|
public var disableVideoAspectScaling: Bool
|
||||||
|
public var enableVoipTcp: Bool
|
||||||
|
|
||||||
public static var defaultSettings: ExperimentalUISettings {
|
public static var defaultSettings: ExperimentalUISettings {
|
||||||
return ExperimentalUISettings(
|
return ExperimentalUISettings(
|
||||||
@ -23,7 +25,9 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
|
|||||||
foldersTabAtBottom: false,
|
foldersTabAtBottom: false,
|
||||||
playerEmbedding: false,
|
playerEmbedding: false,
|
||||||
playlistPlayback: false,
|
playlistPlayback: false,
|
||||||
preferredVideoCodec: nil
|
preferredVideoCodec: nil,
|
||||||
|
disableVideoAspectScaling: false,
|
||||||
|
enableVoipTcp: false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +40,9 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
|
|||||||
foldersTabAtBottom: Bool,
|
foldersTabAtBottom: Bool,
|
||||||
playerEmbedding: Bool,
|
playerEmbedding: Bool,
|
||||||
playlistPlayback: Bool,
|
playlistPlayback: Bool,
|
||||||
preferredVideoCodec: String?
|
preferredVideoCodec: String?,
|
||||||
|
disableVideoAspectScaling: Bool,
|
||||||
|
enableVoipTcp: Bool
|
||||||
) {
|
) {
|
||||||
self.keepChatNavigationStack = keepChatNavigationStack
|
self.keepChatNavigationStack = keepChatNavigationStack
|
||||||
self.skipReadHistory = skipReadHistory
|
self.skipReadHistory = skipReadHistory
|
||||||
@ -47,6 +53,8 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
|
|||||||
self.playerEmbedding = playerEmbedding
|
self.playerEmbedding = playerEmbedding
|
||||||
self.playlistPlayback = playlistPlayback
|
self.playlistPlayback = playlistPlayback
|
||||||
self.preferredVideoCodec = preferredVideoCodec
|
self.preferredVideoCodec = preferredVideoCodec
|
||||||
|
self.disableVideoAspectScaling = disableVideoAspectScaling
|
||||||
|
self.enableVoipTcp = enableVoipTcp
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(decoder: PostboxDecoder) {
|
public init(decoder: PostboxDecoder) {
|
||||||
@ -59,6 +67,8 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
|
|||||||
self.playerEmbedding = decoder.decodeInt32ForKey("playerEmbedding", orElse: 0) != 0
|
self.playerEmbedding = decoder.decodeInt32ForKey("playerEmbedding", orElse: 0) != 0
|
||||||
self.playlistPlayback = decoder.decodeInt32ForKey("playlistPlayback", orElse: 0) != 0
|
self.playlistPlayback = decoder.decodeInt32ForKey("playlistPlayback", orElse: 0) != 0
|
||||||
self.preferredVideoCodec = decoder.decodeOptionalStringForKey("preferredVideoCodec")
|
self.preferredVideoCodec = decoder.decodeOptionalStringForKey("preferredVideoCodec")
|
||||||
|
self.disableVideoAspectScaling = decoder.decodeInt32ForKey("disableVideoAspectScaling", orElse: 0) != 0
|
||||||
|
self.enableVoipTcp = decoder.decodeInt32ForKey("enableVoipTcp", orElse: 0) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(_ encoder: PostboxEncoder) {
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
@ -73,6 +83,8 @@ public struct ExperimentalUISettings: Equatable, PreferencesEntry {
|
|||||||
if let preferredVideoCodec = self.preferredVideoCodec {
|
if let preferredVideoCodec = self.preferredVideoCodec {
|
||||||
encoder.encodeString(preferredVideoCodec, forKey: "preferredVideoCodec")
|
encoder.encodeString(preferredVideoCodec, forKey: "preferredVideoCodec")
|
||||||
}
|
}
|
||||||
|
encoder.encodeInt32(self.disableVideoAspectScaling ? 1 : 0, forKey: "disableVideoAspectScaling")
|
||||||
|
encoder.encodeInt32(self.enableVoipTcp ? 1 : 0, forKey: "enableVoipTcp")
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isEqual(to: PreferencesEntry) -> Bool {
|
public func isEqual(to: PreferencesEntry) -> Bool {
|
||||||
|
@ -34,8 +34,6 @@ private func callConnectionDescriptionsWebrtc(_ connection: CallSessionConnectio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let callLogsLimit = 20
|
|
||||||
|
|
||||||
public func callLogNameForId(id: Int64, account: Account) -> String? {
|
public func callLogNameForId(id: Int64, account: Account) -> String? {
|
||||||
let path = callLogsPath(account: account)
|
let path = callLogsPath(account: account)
|
||||||
let namePrefix = "\(id)_"
|
let namePrefix = "\(id)_"
|
||||||
@ -63,26 +61,25 @@ private func cleanupCallLogs(account: Account) {
|
|||||||
try? fileManager.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)
|
try? fileManager.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
var oldest: (URL, Date)? = nil
|
var oldest: [(URL, Date)] = []
|
||||||
var count = 0
|
var count = 0
|
||||||
if let enumerator = FileManager.default.enumerator(at: URL(fileURLWithPath: path), includingPropertiesForKeys: [.contentModificationDateKey], options: [.skipsHiddenFiles, .skipsSubdirectoryDescendants], errorHandler: nil) {
|
if let enumerator = FileManager.default.enumerator(at: URL(fileURLWithPath: path), includingPropertiesForKeys: [.contentModificationDateKey], options: [.skipsHiddenFiles, .skipsSubdirectoryDescendants], errorHandler: nil) {
|
||||||
for url in enumerator {
|
for url in enumerator {
|
||||||
if let url = url as? URL {
|
if let url = url as? URL {
|
||||||
if let date = (try? url.resourceValues(forKeys: Set([.contentModificationDateKey])))?.contentModificationDate {
|
if let date = (try? url.resourceValues(forKeys: Set([.contentModificationDateKey])))?.contentModificationDate {
|
||||||
if let currentOldest = oldest {
|
oldest.append((url, date))
|
||||||
if date < currentOldest.1 {
|
|
||||||
oldest = (url, date)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
oldest = (url, date)
|
|
||||||
}
|
|
||||||
count += 1
|
count += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if count > callLogsLimit, let oldest = oldest {
|
let callLogsLimit = 40
|
||||||
try? fileManager.removeItem(atPath: oldest.0.path)
|
if count > callLogsLimit {
|
||||||
|
oldest.sort(by: { $0.1 > $1.1 })
|
||||||
|
while oldest.count > callLogsLimit {
|
||||||
|
try? fileManager.removeItem(atPath: oldest[oldest.count - 1].0.path)
|
||||||
|
oldest.removeLast()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,6 +268,7 @@ private protocol OngoingCallThreadLocalContextProtocol: class {
|
|||||||
func nativeSetIsMuted(_ value: Bool)
|
func nativeSetIsMuted(_ value: Bool)
|
||||||
func nativeSetIsLowBatteryLevel(_ value: Bool)
|
func nativeSetIsLowBatteryLevel(_ value: Bool)
|
||||||
func nativeRequestVideo(_ capturer: OngoingCallVideoCapturer)
|
func nativeRequestVideo(_ capturer: OngoingCallVideoCapturer)
|
||||||
|
func nativeSetRequestedVideoAspect(_ aspect: Float)
|
||||||
func nativeDisableVideo()
|
func nativeDisableVideo()
|
||||||
func nativeStop(_ completion: @escaping (String?, Int64, Int64, Int64, Int64) -> Void)
|
func nativeStop(_ completion: @escaping (String?, Int64, Int64, Int64, Int64) -> Void)
|
||||||
func nativeBeginTermination()
|
func nativeBeginTermination()
|
||||||
@ -309,6 +307,9 @@ extension OngoingCallThreadLocalContext: OngoingCallThreadLocalContextProtocol {
|
|||||||
func nativeRequestVideo(_ capturer: OngoingCallVideoCapturer) {
|
func nativeRequestVideo(_ capturer: OngoingCallVideoCapturer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nativeSetRequestedVideoAspect(_ aspect: Float) {
|
||||||
|
}
|
||||||
|
|
||||||
func nativeDisableVideo() {
|
func nativeDisableVideo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,10 +348,24 @@ public final class OngoingCallVideoCapturer {
|
|||||||
setOnFirstFrameReceived: { [weak view] f in
|
setOnFirstFrameReceived: { [weak view] f in
|
||||||
view?.setOnFirstFrameReceived(f)
|
view?.setOnFirstFrameReceived(f)
|
||||||
},
|
},
|
||||||
getOrientation: {
|
getOrientation: { [weak view] in
|
||||||
return .rotation0
|
if let view = view {
|
||||||
|
return OngoingCallVideoOrientation(view.orientation)
|
||||||
|
} else {
|
||||||
|
return .rotation0
|
||||||
|
}
|
||||||
},
|
},
|
||||||
setOnOrientationUpdated: { _ in
|
getAspect: { [weak view] in
|
||||||
|
if let view = view {
|
||||||
|
return view.aspect
|
||||||
|
} else {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setOnOrientationUpdated: { [weak view] f in
|
||||||
|
view?.setOnOrientationUpdated { value, aspect in
|
||||||
|
f?(OngoingCallVideoOrientation(value), aspect)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
setOnIsMirroredUpdated: { [weak view] f in
|
setOnIsMirroredUpdated: { [weak view] f in
|
||||||
view?.setOnIsMirroredUpdated(f)
|
view?.setOnIsMirroredUpdated(f)
|
||||||
@ -392,6 +407,10 @@ extension OngoingCallThreadLocalContextWebrtc: OngoingCallThreadLocalContextProt
|
|||||||
self.requestVideo(capturer.impl)
|
self.requestVideo(capturer.impl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nativeSetRequestedVideoAspect(_ aspect: Float) {
|
||||||
|
self.setRequestedVideoAspect(aspect)
|
||||||
|
}
|
||||||
|
|
||||||
func nativeDisableVideo() {
|
func nativeDisableVideo() {
|
||||||
self.disableVideo()
|
self.disableVideo()
|
||||||
}
|
}
|
||||||
@ -456,11 +475,11 @@ private extension OngoingCallVideoOrientation {
|
|||||||
case .orientation0:
|
case .orientation0:
|
||||||
self = .rotation0
|
self = .rotation0
|
||||||
case .orientation90:
|
case .orientation90:
|
||||||
self = .rotation270
|
self = .rotation90
|
||||||
case .orientation180:
|
case .orientation180:
|
||||||
self = .rotation180
|
self = .rotation180
|
||||||
case .orientation270:
|
case .orientation270:
|
||||||
self = .rotation90
|
self = .rotation270
|
||||||
@unknown default:
|
@unknown default:
|
||||||
self = .rotation0
|
self = .rotation0
|
||||||
}
|
}
|
||||||
@ -471,19 +490,22 @@ public final class OngoingCallContextPresentationCallVideoView {
|
|||||||
public let view: UIView
|
public let view: UIView
|
||||||
public let setOnFirstFrameReceived: (((Float) -> Void)?) -> Void
|
public let setOnFirstFrameReceived: (((Float) -> Void)?) -> Void
|
||||||
public let getOrientation: () -> OngoingCallVideoOrientation
|
public let getOrientation: () -> OngoingCallVideoOrientation
|
||||||
public let setOnOrientationUpdated: (((OngoingCallVideoOrientation) -> Void)?) -> Void
|
public let getAspect: () -> CGFloat
|
||||||
|
public let setOnOrientationUpdated: (((OngoingCallVideoOrientation, CGFloat) -> Void)?) -> Void
|
||||||
public let setOnIsMirroredUpdated: (((Bool) -> Void)?) -> Void
|
public let setOnIsMirroredUpdated: (((Bool) -> Void)?) -> Void
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
view: UIView,
|
view: UIView,
|
||||||
setOnFirstFrameReceived: @escaping (((Float) -> Void)?) -> Void,
|
setOnFirstFrameReceived: @escaping (((Float) -> Void)?) -> Void,
|
||||||
getOrientation: @escaping () -> OngoingCallVideoOrientation,
|
getOrientation: @escaping () -> OngoingCallVideoOrientation,
|
||||||
setOnOrientationUpdated: @escaping (((OngoingCallVideoOrientation) -> Void)?) -> Void,
|
getAspect: @escaping () -> CGFloat,
|
||||||
|
setOnOrientationUpdated: @escaping (((OngoingCallVideoOrientation, CGFloat) -> Void)?) -> Void,
|
||||||
setOnIsMirroredUpdated: @escaping (((Bool) -> Void)?) -> Void
|
setOnIsMirroredUpdated: @escaping (((Bool) -> Void)?) -> Void
|
||||||
) {
|
) {
|
||||||
self.view = view
|
self.view = view
|
||||||
self.setOnFirstFrameReceived = setOnFirstFrameReceived
|
self.setOnFirstFrameReceived = setOnFirstFrameReceived
|
||||||
self.getOrientation = getOrientation
|
self.getOrientation = getOrientation
|
||||||
|
self.getAspect = getAspect
|
||||||
self.setOnOrientationUpdated = setOnOrientationUpdated
|
self.setOnOrientationUpdated = setOnOrientationUpdated
|
||||||
self.setOnIsMirroredUpdated = setOnIsMirroredUpdated
|
self.setOnIsMirroredUpdated = setOnIsMirroredUpdated
|
||||||
}
|
}
|
||||||
@ -541,6 +563,9 @@ public final class OngoingCallContext {
|
|||||||
return OngoingCallThreadLocalContext.maxLayer()
|
return OngoingCallThreadLocalContext.maxLayer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private let tempLogFile: TempBoxFile
|
||||||
|
private let tempStatsLogFile: TempBoxFile
|
||||||
|
|
||||||
public static func versions(includeExperimental: Bool, includeReference: Bool) -> [(version: String, supportsVideo: Bool)] {
|
public static func versions(includeExperimental: Bool, includeReference: Bool) -> [(version: String, supportsVideo: Bool)] {
|
||||||
var result: [(version: String, supportsVideo: Bool)] = [(OngoingCallThreadLocalContext.version(), false)]
|
var result: [(version: String, supportsVideo: Bool)] = [(OngoingCallThreadLocalContext.version(), false)]
|
||||||
if includeExperimental {
|
if includeExperimental {
|
||||||
@ -551,7 +576,7 @@ public final class OngoingCallContext {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(account: Account, callSessionManager: CallSessionManager, internalId: CallSessionInternalId, proxyServer: ProxyServerSettings?, initialNetworkType: NetworkType, updatedNetworkType: Signal<NetworkType, NoError>, serializedData: String?, dataSaving: VoiceCallDataSaving, derivedState: VoipDerivedState, key: Data, isOutgoing: Bool, video: OngoingCallVideoCapturer?, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, allowP2P: Bool, audioSessionActive: Signal<Bool, NoError>, logName: String, preferredVideoCodec: String?) {
|
public init(account: Account, callSessionManager: CallSessionManager, internalId: CallSessionInternalId, proxyServer: ProxyServerSettings?, initialNetworkType: NetworkType, updatedNetworkType: Signal<NetworkType, NoError>, serializedData: String?, dataSaving: VoiceCallDataSaving, derivedState: VoipDerivedState, key: Data, isOutgoing: Bool, video: OngoingCallVideoCapturer?, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, allowP2P: Bool, enableStunMarking: Bool, audioSessionActive: Signal<Bool, NoError>, logName: String, preferredVideoCodec: String?) {
|
||||||
let _ = setupLogs
|
let _ = setupLogs
|
||||||
OngoingCallThreadLocalContext.applyServerConfig(serializedData)
|
OngoingCallThreadLocalContext.applyServerConfig(serializedData)
|
||||||
|
|
||||||
@ -560,6 +585,11 @@ public final class OngoingCallContext {
|
|||||||
self.callSessionManager = callSessionManager
|
self.callSessionManager = callSessionManager
|
||||||
self.logPath = logName.isEmpty ? "" : callLogsPath(account: self.account) + "/" + logName + ".log"
|
self.logPath = logName.isEmpty ? "" : callLogsPath(account: self.account) + "/" + logName + ".log"
|
||||||
let logPath = self.logPath
|
let logPath = self.logPath
|
||||||
|
self.tempLogFile = TempBox.shared.tempFile(fileName: "CallLog.txt")
|
||||||
|
let tempLogPath = self.tempLogFile.path
|
||||||
|
|
||||||
|
self.tempStatsLogFile = TempBox.shared.tempFile(fileName: "CallStats.json")
|
||||||
|
let tempStatsLogPath = self.tempStatsLogFile.path
|
||||||
|
|
||||||
let queue = self.queue
|
let queue = self.queue
|
||||||
|
|
||||||
@ -581,11 +611,6 @@ public final class OngoingCallContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let screenSize = UIScreen.main.bounds.size
|
|
||||||
let portraitSize = CGSize(width: min(screenSize.width, screenSize.height), height: max(screenSize.width, screenSize.height))
|
|
||||||
let preferredAspectRatio = portraitSize.width / portraitSize.height
|
|
||||||
|
|
||||||
|
|
||||||
let unfilteredConnections = [connections.primary] + connections.alternatives
|
let unfilteredConnections = [connections.primary] + connections.alternatives
|
||||||
var processedConnections: [CallSessionConnection] = []
|
var processedConnections: [CallSessionConnection] = []
|
||||||
var filteredConnections: [OngoingCallConnectionDescriptionWebrtc] = []
|
var filteredConnections: [OngoingCallConnectionDescriptionWebrtc] = []
|
||||||
@ -597,9 +622,9 @@ public final class OngoingCallContext {
|
|||||||
filteredConnections.append(contentsOf: callConnectionDescriptionsWebrtc(connection))
|
filteredConnections.append(contentsOf: callConnectionDescriptionsWebrtc(connection))
|
||||||
}
|
}
|
||||||
|
|
||||||
let context = OngoingCallThreadLocalContextWebrtc(version: version, queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForTypeWebrtc(initialNetworkType), dataSaving: ongoingDataSavingForTypeWebrtc(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, connections: filteredConnections, maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath, sendSignalingData: { [weak callSessionManager] data in
|
let context = OngoingCallThreadLocalContextWebrtc(version: version, queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForTypeWebrtc(initialNetworkType), dataSaving: ongoingDataSavingForTypeWebrtc(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, connections: filteredConnections, maxLayer: maxLayer, allowP2P: allowP2P, enableStunMarking: enableStunMarking, logPath: tempLogPath, statsLogPath: tempStatsLogPath, sendSignalingData: { [weak callSessionManager] data in
|
||||||
callSessionManager?.sendSignalingData(internalId: internalId, data: data)
|
callSessionManager?.sendSignalingData(internalId: internalId, data: data)
|
||||||
}, videoCapturer: video?.impl, preferredAspectRatio: Float(preferredAspectRatio), preferredVideoCodec: preferredVideoCodec)
|
}, videoCapturer: video?.impl, preferredVideoCodec: preferredVideoCodec)
|
||||||
|
|
||||||
strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context))
|
strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context))
|
||||||
context.stateChanged = { [weak callSessionManager] state, videoState, remoteVideoState, remoteAudioState, remoteBatteryLevel, _ in
|
context.stateChanged = { [weak callSessionManager] state, videoState, remoteVideoState, remoteAudioState, remoteBatteryLevel, _ in
|
||||||
@ -748,10 +773,15 @@ public final class OngoingCallContext {
|
|||||||
public func stop(callId: CallId? = nil, sendDebugLogs: Bool = false, debugLogValue: Promise<String?>) {
|
public func stop(callId: CallId? = nil, sendDebugLogs: Bool = false, debugLogValue: Promise<String?>) {
|
||||||
let account = self.account
|
let account = self.account
|
||||||
let logPath = self.logPath
|
let logPath = self.logPath
|
||||||
|
var statsLogPath = ""
|
||||||
|
if !logPath.isEmpty {
|
||||||
|
statsLogPath = logPath + ".json"
|
||||||
|
}
|
||||||
|
let tempLogPath = self.tempLogFile.path
|
||||||
|
let tempStatsLogPath = self.tempStatsLogFile.path
|
||||||
|
|
||||||
self.withContextThenDeallocate { context in
|
self.withContextThenDeallocate { context in
|
||||||
context.nativeStop { debugLog, bytesSentWifi, bytesReceivedWifi, bytesSentMobile, bytesReceivedMobile in
|
context.nativeStop { debugLog, bytesSentWifi, bytesReceivedWifi, bytesSentMobile, bytesReceivedMobile in
|
||||||
debugLogValue.set(.single(debugLog))
|
|
||||||
let delta = NetworkUsageStatsConnectionsEntry(
|
let delta = NetworkUsageStatsConnectionsEntry(
|
||||||
cellular: NetworkUsageStatsDirectionsEntry(
|
cellular: NetworkUsageStatsDirectionsEntry(
|
||||||
incoming: bytesReceivedMobile,
|
incoming: bytesReceivedMobile,
|
||||||
@ -761,17 +791,22 @@ public final class OngoingCallContext {
|
|||||||
outgoing: bytesSentWifi))
|
outgoing: bytesSentWifi))
|
||||||
updateAccountNetworkUsageStats(account: self.account, category: .call, delta: delta)
|
updateAccountNetworkUsageStats(account: self.account, category: .call, delta: delta)
|
||||||
|
|
||||||
if !logPath.isEmpty, let debugLog = debugLog {
|
if !logPath.isEmpty {
|
||||||
let logsPath = callLogsPath(account: account)
|
let logsPath = callLogsPath(account: account)
|
||||||
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
|
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
|
||||||
if let data = debugLog.data(using: .utf8) {
|
let _ = try? FileManager.default.moveItem(atPath: tempLogPath, toPath: logPath)
|
||||||
let _ = try? data.write(to: URL(fileURLWithPath: logPath))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let callId = callId, let debugLog = debugLog {
|
if !statsLogPath.isEmpty {
|
||||||
|
let logsPath = callLogsPath(account: account)
|
||||||
|
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
|
||||||
|
let _ = try? FileManager.default.moveItem(atPath: tempStatsLogPath, toPath: statsLogPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let callId = callId, !statsLogPath.isEmpty, let data = try? Data(contentsOf: URL(fileURLWithPath: statsLogPath)), let dataString = String(data: data, encoding: .utf8) {
|
||||||
|
debugLogValue.set(.single(dataString))
|
||||||
if sendDebugLogs {
|
if sendDebugLogs {
|
||||||
let _ = saveCallDebugLog(network: self.account.network, callId: callId, log: debugLog).start()
|
let _ = saveCallDebugLog(network: self.account.network, callId: callId, log: dataString).start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -800,6 +835,12 @@ public final class OngoingCallContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func setRequestedVideoAspect(_ aspect: Float) {
|
||||||
|
self.withContext { context in
|
||||||
|
context.nativeSetRequestedVideoAspect(aspect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func disableVideo() {
|
public func disableVideo() {
|
||||||
self.withContext { context in
|
self.withContext { context in
|
||||||
context.nativeDisableVideo()
|
context.nativeDisableVideo()
|
||||||
@ -837,9 +878,16 @@ public final class OngoingCallContext {
|
|||||||
return .rotation0
|
return .rotation0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getAspect: { [weak view] in
|
||||||
|
if let view = view {
|
||||||
|
return view.aspect
|
||||||
|
} else {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
setOnOrientationUpdated: { [weak view] f in
|
setOnOrientationUpdated: { [weak view] f in
|
||||||
view?.setOnOrientationUpdated { value in
|
view?.setOnOrientationUpdated { value, aspect in
|
||||||
f?(OngoingCallVideoOrientation(value))
|
f?(OngoingCallVideoOrientation(value), aspect)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setOnIsMirroredUpdated: { [weak view] f in
|
setOnIsMirroredUpdated: { [weak view] f in
|
||||||
|
@ -95,11 +95,12 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) {
|
|||||||
@protocol OngoingCallThreadLocalContextWebrtcVideoView <NSObject>
|
@protocol OngoingCallThreadLocalContextWebrtcVideoView <NSObject>
|
||||||
|
|
||||||
@property (nonatomic, readonly) OngoingCallVideoOrientationWebrtc orientation;
|
@property (nonatomic, readonly) OngoingCallVideoOrientationWebrtc orientation;
|
||||||
|
@property (nonatomic, readonly) CGFloat aspect;
|
||||||
|
|
||||||
- (void)setOnFirstFrameReceived:(void (^ _Nullable)(float))onFirstFrameReceived;
|
- (void)setOnFirstFrameReceived:(void (^ _Nullable)(float))onFirstFrameReceived;
|
||||||
- (void)setOnOrientationUpdated:(void (^ _Nullable)(OngoingCallVideoOrientationWebrtc))onOrientationUpdated;
|
- (void)setOnOrientationUpdated:(void (^ _Nullable)(OngoingCallVideoOrientationWebrtc, CGFloat))onOrientationUpdated;
|
||||||
- (void)setOnIsMirroredUpdated:(void (^ _Nullable)(bool))onIsMirroredUpdated;
|
- (void)setOnIsMirroredUpdated:(void (^ _Nullable)(bool))onIsMirroredUpdated;
|
||||||
#ifdef WEBRTC_MAC
|
#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
|
||||||
- (void)setVideoContentMode:(CALayerContentsGravity _Nonnull )mode;
|
- (void)setVideoContentMode:(CALayerContentsGravity _Nonnull )mode;
|
||||||
#endif
|
#endif
|
||||||
@end
|
@end
|
||||||
@ -125,7 +126,7 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) {
|
|||||||
@property (nonatomic, copy) void (^ _Nullable stateChanged)(OngoingCallStateWebrtc, OngoingCallVideoStateWebrtc, OngoingCallRemoteVideoStateWebrtc, OngoingCallRemoteAudioStateWebrtc, OngoingCallRemoteBatteryLevelWebrtc, float);
|
@property (nonatomic, copy) void (^ _Nullable stateChanged)(OngoingCallStateWebrtc, OngoingCallVideoStateWebrtc, OngoingCallRemoteVideoStateWebrtc, OngoingCallRemoteAudioStateWebrtc, OngoingCallRemoteBatteryLevelWebrtc, float);
|
||||||
@property (nonatomic, copy) void (^ _Nullable signalBarsChanged)(int32_t);
|
@property (nonatomic, copy) void (^ _Nullable signalBarsChanged)(int32_t);
|
||||||
|
|
||||||
- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing connections:(NSArray<OngoingCallConnectionDescriptionWebrtc *> * _Nonnull)connections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath sendSignalingData:(void (^ _Nonnull)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer preferredAspectRatio:(float)preferredAspectRatio preferredVideoCodec:(NSString * _Nullable)preferredVideoCodec;
|
- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing connections:(NSArray<OngoingCallConnectionDescriptionWebrtc *> * _Nonnull)connections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P enableStunMarking:(BOOL)enableStunMarking logPath:(NSString * _Nonnull)logPath statsLogPath:(NSString * _Nonnull)statsLogPath sendSignalingData:(void (^ _Nonnull)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer preferredVideoCodec:(NSString * _Nullable)preferredVideoCodec;
|
||||||
|
|
||||||
- (void)beginTermination;
|
- (void)beginTermination;
|
||||||
- (void)stop:(void (^_Nullable)(NSString * _Nullable debugLog, int64_t bytesSentWifi, int64_t bytesReceivedWifi, int64_t bytesSentMobile, int64_t bytesReceivedMobile))completion;
|
- (void)stop:(void (^_Nullable)(NSString * _Nullable debugLog, int64_t bytesSentWifi, int64_t bytesReceivedWifi, int64_t bytesSentMobile, int64_t bytesReceivedMobile))completion;
|
||||||
@ -141,6 +142,7 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) {
|
|||||||
- (void)setNetworkType:(OngoingCallNetworkTypeWebrtc)networkType;
|
- (void)setNetworkType:(OngoingCallNetworkTypeWebrtc)networkType;
|
||||||
- (void)makeIncomingVideoView:(void (^_Nonnull)(UIView<OngoingCallThreadLocalContextWebrtcVideoView> * _Nullable))completion;
|
- (void)makeIncomingVideoView:(void (^_Nonnull)(UIView<OngoingCallThreadLocalContextWebrtcVideoView> * _Nullable))completion;
|
||||||
- (void)requestVideo:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer;
|
- (void)requestVideo:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer;
|
||||||
|
- (void)setRequestedVideoAspect:(float)aspect;
|
||||||
- (void)disableVideo;
|
- (void)disableVideo;
|
||||||
- (void)addSignalingData:(NSData * _Nonnull)data;
|
- (void)addSignalingData:(NSData * _Nonnull)data;
|
||||||
|
|
||||||
|
@ -49,12 +49,14 @@
|
|||||||
@protocol OngoingCallThreadLocalContextWebrtcVideoViewImpl <NSObject>
|
@protocol OngoingCallThreadLocalContextWebrtcVideoViewImpl <NSObject>
|
||||||
|
|
||||||
@property (nonatomic, readwrite) OngoingCallVideoOrientationWebrtc orientation;
|
@property (nonatomic, readwrite) OngoingCallVideoOrientationWebrtc orientation;
|
||||||
|
@property (nonatomic, readonly) CGFloat aspect;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface VideoMetalView (VideoViewImpl) <OngoingCallThreadLocalContextWebrtcVideoView, OngoingCallThreadLocalContextWebrtcVideoViewImpl>
|
@interface VideoMetalView (VideoViewImpl) <OngoingCallThreadLocalContextWebrtcVideoView, OngoingCallThreadLocalContextWebrtcVideoViewImpl>
|
||||||
|
|
||||||
@property (nonatomic, readwrite) OngoingCallVideoOrientationWebrtc orientation;
|
@property (nonatomic, readwrite) OngoingCallVideoOrientationWebrtc orientation;
|
||||||
|
@property (nonatomic, readonly) CGFloat aspect;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@ -64,14 +66,18 @@
|
|||||||
return (OngoingCallVideoOrientationWebrtc)self.internalOrientation;
|
return (OngoingCallVideoOrientationWebrtc)self.internalOrientation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (CGFloat)aspect {
|
||||||
|
return self.internalAspect;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setOrientation:(OngoingCallVideoOrientationWebrtc)orientation {
|
- (void)setOrientation:(OngoingCallVideoOrientationWebrtc)orientation {
|
||||||
[self setInternalOrientation:(int)orientation];
|
[self setInternalOrientation:(int)orientation];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setOnOrientationUpdated:(void (^ _Nullable)(OngoingCallVideoOrientationWebrtc))onOrientationUpdated {
|
- (void)setOnOrientationUpdated:(void (^ _Nullable)(OngoingCallVideoOrientationWebrtc, CGFloat))onOrientationUpdated {
|
||||||
if (onOrientationUpdated) {
|
if (onOrientationUpdated) {
|
||||||
[self internalSetOnOrientationUpdated:^(int value) {
|
[self internalSetOnOrientationUpdated:^(int value, CGFloat aspect) {
|
||||||
onOrientationUpdated((OngoingCallVideoOrientationWebrtc)value);
|
onOrientationUpdated((OngoingCallVideoOrientationWebrtc)value, aspect);
|
||||||
}];
|
}];
|
||||||
} else {
|
} else {
|
||||||
[self internalSetOnOrientationUpdated:nil];
|
[self internalSetOnOrientationUpdated:nil];
|
||||||
@ -93,6 +99,7 @@
|
|||||||
@interface GLVideoView (VideoViewImpl) <OngoingCallThreadLocalContextWebrtcVideoView, OngoingCallThreadLocalContextWebrtcVideoViewImpl>
|
@interface GLVideoView (VideoViewImpl) <OngoingCallThreadLocalContextWebrtcVideoView, OngoingCallThreadLocalContextWebrtcVideoViewImpl>
|
||||||
|
|
||||||
@property (nonatomic, readwrite) OngoingCallVideoOrientationWebrtc orientation;
|
@property (nonatomic, readwrite) OngoingCallVideoOrientationWebrtc orientation;
|
||||||
|
@property (nonatomic, readonly) CGFloat aspect;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@ -102,14 +109,18 @@
|
|||||||
return (OngoingCallVideoOrientationWebrtc)self.internalOrientation;
|
return (OngoingCallVideoOrientationWebrtc)self.internalOrientation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (CGFloat)aspect {
|
||||||
|
return self.internalAspect;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setOrientation:(OngoingCallVideoOrientationWebrtc)orientation {
|
- (void)setOrientation:(OngoingCallVideoOrientationWebrtc)orientation {
|
||||||
[self setInternalOrientation:(int)orientation];
|
[self setInternalOrientation:(int)orientation];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setOnOrientationUpdated:(void (^ _Nullable)(OngoingCallVideoOrientationWebrtc))onOrientationUpdated {
|
- (void)setOnOrientationUpdated:(void (^ _Nullable)(OngoingCallVideoOrientationWebrtc, CGFloat))onOrientationUpdated {
|
||||||
if (onOrientationUpdated) {
|
if (onOrientationUpdated) {
|
||||||
[self internalSetOnOrientationUpdated:^(int value) {
|
[self internalSetOnOrientationUpdated:^(int value, CGFloat aspect) {
|
||||||
onOrientationUpdated((OngoingCallVideoOrientationWebrtc)value);
|
onOrientationUpdated((OngoingCallVideoOrientationWebrtc)value, aspect);
|
||||||
}];
|
}];
|
||||||
} else {
|
} else {
|
||||||
[self internalSetOnOrientationUpdated:nil];
|
[self internalSetOnOrientationUpdated:nil];
|
||||||
@ -207,6 +218,7 @@
|
|||||||
NSTimeInterval _callPacketTimeout;
|
NSTimeInterval _callPacketTimeout;
|
||||||
|
|
||||||
std::unique_ptr<tgcalls::Instance> _tgVoip;
|
std::unique_ptr<tgcalls::Instance> _tgVoip;
|
||||||
|
bool _didStop;
|
||||||
|
|
||||||
OngoingCallStateWebrtc _state;
|
OngoingCallStateWebrtc _state;
|
||||||
OngoingCallVideoStateWebrtc _videoState;
|
OngoingCallVideoStateWebrtc _videoState;
|
||||||
@ -224,7 +236,6 @@
|
|||||||
void (^_sendSignalingData)(NSData *);
|
void (^_sendSignalingData)(NSData *);
|
||||||
|
|
||||||
float _remotePreferredAspectRatio;
|
float _remotePreferredAspectRatio;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)controllerStateChanged:(tgcalls::State)state;
|
- (void)controllerStateChanged:(tgcalls::State)state;
|
||||||
@ -312,7 +323,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing connections:(NSArray<OngoingCallConnectionDescriptionWebrtc *> * _Nonnull)connections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath sendSignalingData:(void (^)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer preferredAspectRatio:(float)preferredAspectRatio preferredVideoCodec:(NSString * _Nullable)preferredVideoCodec {
|
- (instancetype _Nonnull)initWithVersion:(NSString * _Nonnull)version queue:(id<OngoingCallThreadLocalContextQueueWebrtc> _Nonnull)queue proxy:(VoipProxyServerWebrtc * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtc)networkType dataSaving:(OngoingCallDataSavingWebrtc)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing connections:(NSArray<OngoingCallConnectionDescriptionWebrtc *> * _Nonnull)connections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P enableStunMarking:(BOOL)enableStunMarking logPath:(NSString * _Nonnull)logPath statsLogPath:(NSString * _Nonnull)statsLogPath sendSignalingData:(void (^)(NSData * _Nonnull))sendSignalingData videoCapturer:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer preferredVideoCodec:(NSString * _Nullable)preferredVideoCodec {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self != nil) {
|
if (self != nil) {
|
||||||
_version = version;
|
_version = version;
|
||||||
@ -387,13 +398,14 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||||||
.receiveTimeout = _callPacketTimeout,
|
.receiveTimeout = _callPacketTimeout,
|
||||||
.dataSaving = callControllerDataSavingForType(dataSaving),
|
.dataSaving = callControllerDataSavingForType(dataSaving),
|
||||||
.enableP2P = (bool)allowP2P,
|
.enableP2P = (bool)allowP2P,
|
||||||
|
.enableStunMarking = (bool)enableStunMarking,
|
||||||
.enableAEC = false,
|
.enableAEC = false,
|
||||||
.enableNS = true,
|
.enableNS = true,
|
||||||
.enableAGC = true,
|
.enableAGC = true,
|
||||||
.enableCallUpgrade = false,
|
.enableCallUpgrade = false,
|
||||||
.logPath = "", //logPath.length == 0 ? "" : std::string(logPath.UTF8String),
|
.logPath = logPath.length == 0 ? "" : std::string(logPath.UTF8String),
|
||||||
|
.statsLogPath = statsLogPath.length == 0 ? "" : std::string(statsLogPath.UTF8String),
|
||||||
.maxApiLayer = [OngoingCallThreadLocalContextWebrtc maxLayer],
|
.maxApiLayer = [OngoingCallThreadLocalContextWebrtc maxLayer],
|
||||||
.preferredAspectRatio = preferredAspectRatio,
|
|
||||||
.enableHighBitrateVideo = true,
|
.enableHighBitrateVideo = true,
|
||||||
.preferredVideoCodecs = preferredVideoCodecs,
|
.preferredVideoCodecs = preferredVideoCodecs,
|
||||||
.protocolVersion = [OngoingCallThreadLocalContextWebrtc protocolVersionFromLibraryVersion:version]
|
.protocolVersion = [OngoingCallThreadLocalContextWebrtc protocolVersionFromLibraryVersion:version]
|
||||||
@ -543,13 +555,10 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||||||
- (void)beginTermination {
|
- (void)beginTermination {
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)stopWithTerminationResult:(OngoingCallThreadLocalContextWebrtcTerminationResult *)terminationResult completion:(void (^)(NSString *, int64_t, int64_t, int64_t, int64_t))completion {
|
+ (void)stopWithTerminationResult:(OngoingCallThreadLocalContextWebrtcTerminationResult *)terminationResult completion:(void (^)(NSString *, int64_t, int64_t, int64_t, int64_t))completion {
|
||||||
_tgVoip.reset();
|
|
||||||
|
|
||||||
if (completion) {
|
if (completion) {
|
||||||
if (terminationResult) {
|
if (terminationResult) {
|
||||||
NSString *debugLog = [NSString stringWithUTF8String:terminationResult.finalState.debugLog.c_str()];
|
NSString *debugLog = [NSString stringWithUTF8String:terminationResult.finalState.debugLog.c_str()];
|
||||||
_lastDerivedState = [[NSData alloc] initWithBytes:terminationResult.finalState.persistentState.value.data() length:terminationResult.finalState.persistentState.value.size()];
|
|
||||||
|
|
||||||
if (completion) {
|
if (completion) {
|
||||||
completion(debugLog, terminationResult.finalState.trafficStats.bytesSentWifi, terminationResult.finalState.trafficStats.bytesReceivedWifi, terminationResult.finalState.trafficStats.bytesSentMobile, terminationResult.finalState.trafficStats.bytesReceivedMobile);
|
completion(debugLog, terminationResult.finalState.trafficStats.bytesSentWifi, terminationResult.finalState.trafficStats.bytesReceivedWifi, terminationResult.finalState.trafficStats.bytesSentMobile, terminationResult.finalState.trafficStats.bytesReceivedMobile);
|
||||||
@ -567,24 +576,28 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (completion == nil) {
|
if (completion == nil) {
|
||||||
_tgVoip->stop([](tgcalls::FinalState finalState) {
|
if (!_didStop) {
|
||||||
});
|
_tgVoip->stop([](tgcalls::FinalState finalState) {
|
||||||
|
});
|
||||||
|
}
|
||||||
_tgVoip.reset();
|
_tgVoip.reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
__weak OngoingCallThreadLocalContextWebrtc *weakSelf = self;
|
__weak OngoingCallThreadLocalContextWebrtc *weakSelf = self;
|
||||||
id<OngoingCallThreadLocalContextQueueWebrtc> queue = _queue;
|
id<OngoingCallThreadLocalContextQueueWebrtc> queue = _queue;
|
||||||
|
_didStop = true;
|
||||||
_tgVoip->stop([weakSelf, queue, completion = [completion copy]](tgcalls::FinalState finalState) {
|
_tgVoip->stop([weakSelf, queue, completion = [completion copy]](tgcalls::FinalState finalState) {
|
||||||
[queue dispatch:^{
|
[queue dispatch:^{
|
||||||
__strong OngoingCallThreadLocalContextWebrtc *strongSelf = weakSelf;
|
__strong OngoingCallThreadLocalContextWebrtc *strongSelf = weakSelf;
|
||||||
if (!strongSelf) {
|
if (strongSelf) {
|
||||||
|
strongSelf->_tgVoip.reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
OngoingCallThreadLocalContextWebrtcTerminationResult *terminationResult = [[OngoingCallThreadLocalContextWebrtcTerminationResult alloc] initWithFinalState:finalState];
|
OngoingCallThreadLocalContextWebrtcTerminationResult *terminationResult = [[OngoingCallThreadLocalContextWebrtcTerminationResult alloc] initWithFinalState:finalState];
|
||||||
|
|
||||||
[strongSelf stopWithTerminationResult:terminationResult completion:completion];
|
[OngoingCallThreadLocalContextWebrtc stopWithTerminationResult:terminationResult completion:completion];
|
||||||
}];
|
}];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -694,7 +707,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||||||
if ([VideoMetalView isSupported]) {
|
if ([VideoMetalView isSupported]) {
|
||||||
VideoMetalView *remoteRenderer = [[VideoMetalView alloc] initWithFrame:CGRectZero];
|
VideoMetalView *remoteRenderer = [[VideoMetalView alloc] initWithFrame:CGRectZero];
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
remoteRenderer.videoContentMode = UIViewContentModeScaleAspectFill;
|
remoteRenderer.videoContentMode = UIViewContentModeScaleToFill;
|
||||||
#else
|
#else
|
||||||
remoteRenderer.videoContentMode = UIViewContentModeScaleAspect;
|
remoteRenderer.videoContentMode = UIViewContentModeScaleAspect;
|
||||||
#endif
|
#endif
|
||||||
@ -737,6 +750,12 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setRequestedVideoAspect:(float)aspect {
|
||||||
|
if (_tgVoip) {
|
||||||
|
_tgVoip->setRequestedVideoAspect(aspect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (void)disableVideo {
|
- (void)disableVideo {
|
||||||
if (_tgVoip) {
|
if (_tgVoip) {
|
||||||
_videoCapturer = nil;
|
_videoCapturer = nil;
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit d8b106c94e5a5e20020ad862d835e921d4f102d9
|
Subproject commit 34c37a8e332ab1cd645bfd90af9a2a4900866e3e
|
6
third-party/webrtc/BUILD
vendored
6
third-party/webrtc/BUILD
vendored
@ -1,3 +1,5 @@
|
|||||||
|
use_gn_build = True
|
||||||
|
|
||||||
webrtc_libs = [
|
webrtc_libs = [
|
||||||
"libwebrtc.a",
|
"libwebrtc.a",
|
||||||
]
|
]
|
||||||
@ -70,7 +72,7 @@ genrule(
|
|||||||
)
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "webrtc_lib_gn",
|
name = "webrtc_lib_gn" if not use_gn_build else "webrtc_lib",
|
||||||
srcs = [":" + x for x in webrtc_libs],
|
srcs = [":" + x for x in webrtc_libs],
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
)
|
)
|
||||||
@ -2182,7 +2184,7 @@ objc_library(
|
|||||||
)
|
)
|
||||||
|
|
||||||
objc_library(
|
objc_library(
|
||||||
name = "webrtc_lib",
|
name = "webrtc_lib_custom" if use_gn_build else "webrtc_lib",
|
||||||
enable_modules = True,
|
enable_modules = True,
|
||||||
module_name = "webrtc",
|
module_name = "webrtc",
|
||||||
srcs = combined_sources,
|
srcs = combined_sources,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user