Streaming improvements

This commit is contained in:
Ali 2022-02-24 01:13:10 +04:00
parent 925746e896
commit baec592ca1
22 changed files with 625 additions and 54 deletions

View File

@ -402,6 +402,8 @@ public protocol PresentationGroupCall: AnyObject {
var schedulePending: Bool { get } var schedulePending: Bool { get }
var isStream: Bool { get }
var audioOutputState: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> { get } var audioOutputState: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> { get }
var isSpeaking: Signal<Bool, NoError> { get } var isSpeaking: Signal<Bool, NoError> { get }

View File

@ -84,10 +84,13 @@ extension UIView {
} }
} }
public class ComponentState { open class ComponentState {
var _updated: ((Transition) -> Void)? var _updated: ((Transition) -> Void)?
var isUpdated: Bool = false var isUpdated: Bool = false
public init() {
}
public final func updated(transition: Transition = .immediate) { public final func updated(transition: Transition = .immediate) {
self.isUpdated = true self.isUpdated = true
self._updated?(transition) self._updated?(transition)

View File

@ -542,6 +542,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-6249322] = { return Api.InputStickerSetItem.parse_inputStickerSetItem($0) } dict[-6249322] = { return Api.InputStickerSetItem.parse_inputStickerSetItem($0) }
dict[-1728664459] = { return Api.help.PromoData.parse_promoDataEmpty($0) } dict[-1728664459] = { return Api.help.PromoData.parse_promoDataEmpty($0) }
dict[-1942390465] = { return Api.help.PromoData.parse_promoData($0) } dict[-1942390465] = { return Api.help.PromoData.parse_promoData($0) }
dict[767505458] = { return Api.phone.GroupCallStreamRtmpUrl.parse_groupCallStreamRtmpUrl($0) }
dict[1753266509] = { return Api.messages.PeerSettings.parse_peerSettings($0) } dict[1753266509] = { return Api.messages.PeerSettings.parse_peerSettings($0) }
dict[-1613493288] = { return Api.NotifyPeer.parse_notifyPeer($0) } dict[-1613493288] = { return Api.NotifyPeer.parse_notifyPeer($0) }
dict[-1261946036] = { return Api.NotifyPeer.parse_notifyUsers($0) } dict[-1261946036] = { return Api.NotifyPeer.parse_notifyUsers($0) }
@ -1336,6 +1337,8 @@ public struct Api {
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.help.PromoData: case let _1 as Api.help.PromoData:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.phone.GroupCallStreamRtmpUrl:
_1.serialize(buffer, boxed)
case let _1 as Api.messages.PeerSettings: case let _1 as Api.messages.PeerSettings:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.NotifyPeer: case let _1 as Api.NotifyPeer:

View File

@ -1821,6 +1821,44 @@ public struct phone {
} }
} }
}
public enum GroupCallStreamRtmpUrl: TypeConstructorDescription {
case groupCallStreamRtmpUrl(url: String, key: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .groupCallStreamRtmpUrl(let url, let key):
if boxed {
buffer.appendInt32(767505458)
}
serializeString(url, buffer: buffer, boxed: false)
serializeString(key, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .groupCallStreamRtmpUrl(let url, let key):
return ("groupCallStreamRtmpUrl", [("url", url), ("key", key)])
}
}
public static func parse_groupCallStreamRtmpUrl(_ reader: BufferReader) -> GroupCallStreamRtmpUrl? {
var _1: String?
_1 = parseString(reader)
var _2: String?
_2 = parseString(reader)
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.phone.GroupCallStreamRtmpUrl.groupCallStreamRtmpUrl(url: _1!, key: _2!)
}
else {
return nil
}
}
} }
public enum GroupCall: TypeConstructorDescription { public enum GroupCall: TypeConstructorDescription {
case groupCall(call: Api.GroupCall, participants: [Api.GroupCallParticipant], participantsNextOffset: String, chats: [Api.Chat], users: [Api.User]) case groupCall(call: Api.GroupCall, participants: [Api.GroupCallParticipant], participantsNextOffset: String, chats: [Api.Chat], users: [Api.User])
@ -8687,6 +8725,21 @@ public extension Api {
return result return result
}) })
} }
public static func getGroupCallStreamRtmpUrl(peer: Api.InputPeer, revoke: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.phone.GroupCallStreamRtmpUrl>) {
let buffer = Buffer()
buffer.appendInt32(-558650433)
peer.serialize(buffer, true)
revoke.serialize(buffer, true)
return (FunctionDescription(name: "phone.getGroupCallStreamRtmpUrl", parameters: [("peer", peer), ("revoke", revoke)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.phone.GroupCallStreamRtmpUrl? in
let reader = BufferReader(buffer)
var result: Api.phone.GroupCallStreamRtmpUrl?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.phone.GroupCallStreamRtmpUrl
}
return result
})
}
} }
} }
} }

View File

@ -420,7 +420,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
strongSelf.joinGroupCall( strongSelf.joinGroupCall(
peerId: groupCallPanelData.peerId, peerId: groupCallPanelData.peerId,
invite: nil, invite: nil,
activeCall: EngineGroupCallDescription(id: groupCallPanelData.info.id, accessHash: groupCallPanelData.info.accessHash, title: groupCallPanelData.info.title, scheduleTimestamp: groupCallPanelData.info.scheduleTimestamp, subscribedToScheduled: groupCallPanelData.info.subscribedToScheduled) activeCall: EngineGroupCallDescription(id: groupCallPanelData.info.id, accessHash: groupCallPanelData.info.accessHash, title: groupCallPanelData.info.title, scheduleTimestamp: groupCallPanelData.info.scheduleTimestamp, subscribedToScheduled: groupCallPanelData.info.subscribedToScheduled, isStream: groupCallPanelData.info.isStream)
) )
}) })
self.navigationBar?.additionalContentNode.addSubnode(groupCallAccessoryPanel) self.navigationBar?.additionalContentNode.addSubnode(groupCallAccessoryPanel)

View File

@ -95,6 +95,7 @@ swift_library(
"//submodules/Markdown:Markdown", "//submodules/Markdown:Markdown",
"//submodules/ChatTitleActivityNode:ChatTitleActivityNode", "//submodules/ChatTitleActivityNode:ChatTitleActivityNode",
"//third-party/libyuv:LibYuvBinding", "//third-party/libyuv:LibYuvBinding",
"//submodules/ComponentFlow:ComponentFlow",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -0,0 +1,253 @@
import Foundation
import UIKit
import ComponentFlow
import Display
import AccountContext
import SwiftSignalKit
private final class NavigationBarComponent: CombinedComponent {
let leftItem: AnyComponent<Empty>?
let rightItem: AnyComponent<Empty>?
let centerItem: AnyComponent<Empty>?
init(
leftItem: AnyComponent<Empty>?,
rightItem: AnyComponent<Empty>?,
centerItem: AnyComponent<Empty>?
) {
self.leftItem = leftItem
self.rightItem = rightItem
self.centerItem = centerItem
}
static func ==(lhs: NavigationBarComponent, rhs: NavigationBarComponent) -> Bool {
return true
}
static var body: Body {
let leftItem = Child(environment: Empty.self)
let rightItem = Child(environment: Empty.self)
let centerItem = Child(environment: Empty.self)
return { context in
var availableWidth = context.availableSize.width
let sideInset: CGFloat = 16.0
let leftItem = context.component.leftItem.flatMap { leftItemComponent in
return leftItem.update(
component: leftItemComponent,
availableSize: CGSize(width: availableWidth, height: context.availableSize.height),
transition: context.transition
)
}
if let leftItem = leftItem {
availableWidth -= leftItem.size.width
}
let rightItem = context.component.rightItem.flatMap { rightItemComponent in
return rightItem.update(
component: rightItemComponent,
availableSize: CGSize(width: availableWidth, height: context.availableSize.height),
transition: context.transition
)
}
if let rightItem = rightItem {
availableWidth -= rightItem.size.width
}
let centerItem = context.component.centerItem.flatMap { centerItemComponent in
return centerItem.update(
component: centerItemComponent,
availableSize: CGSize(width: availableWidth, height: context.availableSize.height),
transition: context.transition
)
}
if let centerItem = centerItem {
availableWidth -= centerItem.size.width
}
var centerLeftInset = sideInset
if let leftItem = leftItem {
context.add(leftItem
.position(CGPoint(x: sideInset + leftItem.size.width / 2.0, y: context.availableSize.height / 2.0))
)
centerLeftInset += leftItem.size.width + 4.0
}
var centerRightInset = sideInset
if let rightItem = rightItem {
context.add(rightItem
.position(CGPoint(x: context.availableSize.width - sideInset - rightItem.size.width / 2.0, y: context.availableSize.height / 2.0))
)
centerRightInset += rightItem.size.width + 4.0
}
let maxCenterInset = max(centerLeftInset, centerRightInset)
if let centerItem = centerItem {
context.add(centerItem
.position(CGPoint(x: maxCenterInset + (context.availableSize.width - maxCenterInset - maxCenterInset) / 2.0, y: context.availableSize.height / 2.0))
)
}
return context.availableSize
}
}
}
public final class MediaStreamComponent: CombinedComponent {
public typealias EnvironmentType = ViewControllerComponentContainer.Environment
public let call: PresentationGroupCallImpl
public init(call: PresentationGroupCallImpl) {
self.call = call
}
public static func ==(lhs: MediaStreamComponent, rhs: MediaStreamComponent) -> Bool {
if lhs.call !== rhs.call {
return false
}
return true
}
public final class State: ComponentState {
private let call: PresentationGroupCallImpl
private(set) var hasVideo: Bool = false
private var stateDisposable: Disposable?
init(call: PresentationGroupCallImpl) {
self.call = call
super.init()
self.stateDisposable = (call.state
|> map { state -> Bool in
switch state.networkState {
case .connected:
return true
default:
return false
}
}
|> filter { $0 }
|> take(1)).start(next: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.hasVideo = true
strongSelf.updated(transition: .immediate)
})
let _ = call.accountContext.engine.calls.getGroupCallStreamCredentials(peerId: call.peerId, revokePreviousCredentials: false).start()
}
deinit {
self.stateDisposable?.dispose()
}
}
public func makeState() -> State {
return State(call: self.call)
}
public static var body: Body {
let background = Child(Rectangle.self)
let video = Child(MediaStreamVideoComponent.self)
let navigationBar = Child(NavigationBarComponent.self)
return { context in
let environment = context.environment[ViewControllerComponentContainer.Environment.self].value
let background = background.update(
component: Rectangle(color: .black),
availableSize: context.availableSize,
transition: context.transition
)
let video = Condition(context.state.hasVideo) {
return video.update(
component: MediaStreamVideoComponent(
call: context.component.call
),
availableSize: CGSize(width: context.availableSize.width, height: floor(context.availableSize.width * 9.0 / 16.0)),
transition: context.transition
)
}
let call = context.component.call
let navigationBar = navigationBar.update(
component: NavigationBarComponent(
leftItem: AnyComponent(Button(
content: AnyComponent(Text(text: "Leave", font: Font.regular(17.0), color: .white)),
insets: UIEdgeInsets(),
action: { [weak call] in
let _ = call?.leave(terminateIfPossible: false)
})
),
rightItem: nil,
centerItem: AnyComponent(Text(text: "Live Stream", font: Font.semibold(17.0), color: .white))
),
availableSize: CGSize(width: context.availableSize.width, height: 44.0),
transition: context.transition
)
context.add(background
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
)
if let video = video {
context.add(video
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0)))
}
context.add(navigationBar
.position(CGPoint(x: context.availableSize.width / 2.0, y: environment.statusBarHeight + navigationBar.size.height / 2.0))
)
return context.availableSize
}
}
}
public final class MediaStreamComponentController: ViewControllerComponentContainer, VoiceChatController {
public let call: PresentationGroupCall
public private(set) var currentOverlayController: VoiceChatOverlayController? = nil
public var parentNavigationController: NavigationController?
public var onViewDidAppear: (() -> Void)?
public var onViewDidDisappear: (() -> Void)?
public init(call: PresentationGroupCall) {
self.call = call
super.init(MediaStreamComponent(call: call as! PresentationGroupCallImpl))
self.statusBar.statusBarStyle = .White
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
DispatchQueue.main.async {
self.onViewDidAppear?()
}
}
override public func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
DispatchQueue.main.async {
self.onViewDidDisappear?()
}
}
public func dismiss(closing: Bool, manual: Bool) {
self.dismiss(animated: true, completion: nil)
}
}

View File

@ -0,0 +1,51 @@
import Foundation
import UIKit
import ComponentFlow
import AccountContext
final class MediaStreamVideoComponent: Component {
let call: PresentationGroupCallImpl
init(call: PresentationGroupCallImpl) {
self.call = call
}
public static func ==(lhs: MediaStreamVideoComponent, rhs: MediaStreamVideoComponent) -> Bool {
if lhs.call !== rhs.call {
return false
}
return true
}
public final class View: UIView {
private let videoRenderingContext = VideoRenderingContext()
private var videoView: VideoRenderingView?
func update(component: MediaStreamVideoComponent, availableSize: CGSize, transition: Transition) -> CGSize {
if self.videoView == nil {
if let input = component.call.video(endpointId: "unified") {
if let videoView = self.videoRenderingContext.makeView(input: input, blur: false) {
self.videoView = videoView
self.addSubview(videoView)
}
}
}
if let videoView = self.videoView {
videoView.updateIsEnabled(true)
videoView.frame = CGRect(origin: CGPoint(), size: availableSize)
}
return availableSize
}
}
public func makeView() -> View {
return View()
}
public func update(view: View, availableSize: CGSize, environment: Environment<Empty>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, transition: transition)
}
}

View File

@ -0,0 +1,121 @@
import Foundation
import UIKit
import ComponentFlow
import Display
import SwiftSignalKit
public extension Transition.Animation.Curve {
init(_ curve: ContainedViewLayoutTransitionCurve) {
switch curve {
case .linear:
self = .easeInOut
case .easeInOut:
self = .easeInOut
case .custom:
self = .spring
case .customSpring:
self = .spring
case .spring:
self = .spring
}
}
}
public extension Transition {
init(_ transition: ContainedViewLayoutTransition) {
switch transition {
case .immediate:
self.init(animation: .none)
case let .animated(duration, curve):
self.init(animation: .curve(duration: duration, curve: Transition.Animation.Curve(curve)))
}
}
}
open class ViewControllerComponentContainer: ViewController {
public final class Environment: Equatable {
public let statusBarHeight: CGFloat
public let safeInsets: UIEdgeInsets
public init(
statusBarHeight: CGFloat,
safeInsets: UIEdgeInsets
) {
self.statusBarHeight = statusBarHeight
self.safeInsets = safeInsets
}
public static func ==(lhs: Environment, rhs: Environment) -> Bool {
if lhs.statusBarHeight != rhs.statusBarHeight {
return false
}
if lhs.safeInsets != rhs.safeInsets {
return false
}
return true
}
}
private final class Node: ViewControllerTracingNode {
private weak var controller: ViewControllerComponentContainer?
private let component: AnyComponent<ViewControllerComponentContainer.Environment>
private let hostView: ComponentHostView<ViewControllerComponentContainer.Environment>
init(controller: ViewControllerComponentContainer, component: AnyComponent<ViewControllerComponentContainer.Environment>) {
self.controller = controller
self.component = component
self.hostView = ComponentHostView()
super.init()
self.view.addSubview(self.hostView)
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
let environment = ViewControllerComponentContainer.Environment(
statusBarHeight: layout.statusBarHeight ?? 0.0,
safeInsets: layout.intrinsicInsets
)
let _ = self.hostView.update(
transition: Transition(transition),
component: self.component,
environment: {
environment
},
containerSize: layout.size
)
transition.updateFrame(view: self.hostView, frame: CGRect(origin: CGPoint(), size: layout.size))
}
}
private var node: Node {
return self.displayNode as! Node
}
private let component: AnyComponent<ViewControllerComponentContainer.Environment>
public init<C: Component>(_ component: C) where C.EnvironmentType == ViewControllerComponentContainer.Environment {
self.component = AnyComponent(component)
super.init(navigationBarPresentationData: nil)
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override open func loadDisplayNode() {
self.displayNode = Node(controller: self, component: self.component)
self.displayNodeDidLoad()
}
override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
self.node.containerLayoutUpdated(layout, transition: transition)
}
}

View File

@ -107,7 +107,8 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
sortAscending: true, sortAscending: true,
defaultParticipantsAreMuted: nil, defaultParticipantsAreMuted: nil,
isVideoEnabled: false, isVideoEnabled: false,
unmutedVideoLimit: 0 unmutedVideoLimit: 0,
isStream: call.isStream
), ),
topParticipants: [], topParticipants: [],
participantCount: 0, participantCount: 0,
@ -160,7 +161,7 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
return GroupCallPanelData( return GroupCallPanelData(
peerId: peerId, peerId: peerId,
isChannel: isChannel, isChannel: isChannel,
info: GroupCallInfo(id: call.id, accessHash: call.accessHash, participantCount: state.totalCount, streamDcId: nil, title: state.title, scheduleTimestamp: state.scheduleTimestamp, subscribedToScheduled: state.subscribedToScheduled, recordingStartTimestamp: nil, sortAscending: state.sortAscending, defaultParticipantsAreMuted: state.defaultParticipantsAreMuted, isVideoEnabled: state.isVideoEnabled, unmutedVideoLimit: state.unmutedVideoLimit), info: GroupCallInfo(id: call.id, accessHash: call.accessHash, participantCount: state.totalCount, streamDcId: nil, title: state.title, scheduleTimestamp: state.scheduleTimestamp, subscribedToScheduled: state.subscribedToScheduled, recordingStartTimestamp: nil, sortAscending: state.sortAscending, defaultParticipantsAreMuted: state.defaultParticipantsAreMuted, isVideoEnabled: state.isVideoEnabled, unmutedVideoLimit: state.unmutedVideoLimit, isStream: call.isStream),
topParticipants: topParticipants, topParticipants: topParticipants,
participantCount: state.totalCount, participantCount: state.totalCount,
activeSpeakers: activeSpeakers, activeSpeakers: activeSpeakers,
@ -629,6 +630,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
private var screencastAudioDataDisposable: Disposable? private var screencastAudioDataDisposable: Disposable?
private var screencastStateDisposable: Disposable? private var screencastStateDisposable: Disposable?
public var isStream = false
init( init(
accountContext: AccountContext, accountContext: AccountContext,
audioSession: ManagedAudioSession, audioSession: ManagedAudioSession,
@ -656,6 +659,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
self.schedulePending = initialCall == nil self.schedulePending = initialCall == nil
self.isScheduled = initialCall == nil || initialCall?.scheduleTimestamp != nil self.isScheduled = initialCall == nil || initialCall?.scheduleTimestamp != nil
if let initialCall = initialCall {
self.isStream = initialCall.isStream
}
self.stateValue = PresentationGroupCallState.initialValue(myPeerId: self.joinAsPeerId, title: initialCall?.title, scheduleTimestamp: initialCall?.scheduleTimestamp, subscribedToScheduled: initialCall?.subscribedToScheduled ?? false) self.stateValue = PresentationGroupCallState.initialValue(myPeerId: self.joinAsPeerId, title: initialCall?.title, scheduleTimestamp: initialCall?.scheduleTimestamp, subscribedToScheduled: initialCall?.subscribedToScheduled ?? false)
self.statePromise = ValuePromise(self.stateValue) self.statePromise = ValuePromise(self.stateValue)
@ -837,7 +844,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
}) })
if let initialCall = initialCall, let temporaryParticipantsContext = (self.accountContext.cachedGroupCallContexts as? AccountGroupCallContextCacheImpl)?.impl.syncWith({ impl in if let initialCall = initialCall, let temporaryParticipantsContext = (self.accountContext.cachedGroupCallContexts as? AccountGroupCallContextCacheImpl)?.impl.syncWith({ impl in
impl.get(account: accountContext.account, engine: accountContext.engine, peerId: peerId, isChannel: isChannel, call: EngineGroupCallDescription(id: initialCall.id, accessHash: initialCall.accessHash, title: initialCall.title, scheduleTimestamp: initialCall.scheduleTimestamp, subscribedToScheduled: initialCall.subscribedToScheduled)) impl.get(account: accountContext.account, engine: accountContext.engine, peerId: peerId, isChannel: isChannel, call: EngineGroupCallDescription(id: initialCall.id, accessHash: initialCall.accessHash, title: initialCall.title, scheduleTimestamp: initialCall.scheduleTimestamp, subscribedToScheduled: initialCall.subscribedToScheduled, isStream: initialCall.isStream))
}) { }) {
self.switchToTemporaryParticipantsContext(sourceContext: temporaryParticipantsContext.context.participantsContext, oldMyPeerId: self.joinAsPeerId) self.switchToTemporaryParticipantsContext(sourceContext: temporaryParticipantsContext.context.participantsContext, oldMyPeerId: self.joinAsPeerId)
} else { } else {
@ -1312,7 +1319,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
strongSelf.stateValue.subscribedToScheduled = state.subscribedToScheduled strongSelf.stateValue.subscribedToScheduled = state.subscribedToScheduled
strongSelf.stateValue.scheduleTimestamp = strongSelf.isScheduledStarted ? nil : state.scheduleTimestamp strongSelf.stateValue.scheduleTimestamp = strongSelf.isScheduledStarted ? nil : state.scheduleTimestamp
if state.scheduleTimestamp == nil && !strongSelf.isScheduledStarted { if state.scheduleTimestamp == nil && !strongSelf.isScheduledStarted {
strongSelf.updateSessionState(internalState: .active(GroupCallInfo(id: callInfo.id, accessHash: callInfo.accessHash, participantCount: state.totalCount, streamDcId: callInfo.streamDcId, title: state.title, scheduleTimestamp: nil, subscribedToScheduled: false, recordingStartTimestamp: nil, sortAscending: true, defaultParticipantsAreMuted: callInfo.defaultParticipantsAreMuted ?? state.defaultParticipantsAreMuted, isVideoEnabled: callInfo.isVideoEnabled, unmutedVideoLimit: callInfo.unmutedVideoLimit)), audioSessionControl: strongSelf.audioSessionControl) strongSelf.updateSessionState(internalState: .active(GroupCallInfo(id: callInfo.id, accessHash: callInfo.accessHash, participantCount: state.totalCount, streamDcId: callInfo.streamDcId, title: state.title, scheduleTimestamp: nil, subscribedToScheduled: false, recordingStartTimestamp: nil, sortAscending: true, defaultParticipantsAreMuted: callInfo.defaultParticipantsAreMuted ?? state.defaultParticipantsAreMuted, isVideoEnabled: callInfo.isVideoEnabled, unmutedVideoLimit: callInfo.unmutedVideoLimit, isStream: callInfo.isStream)), audioSessionControl: strongSelf.audioSessionControl)
} else { } else {
strongSelf.summaryInfoState.set(.single(SummaryInfoState(info: GroupCallInfo( strongSelf.summaryInfoState.set(.single(SummaryInfoState(info: GroupCallInfo(
id: callInfo.id, id: callInfo.id,
@ -1326,7 +1333,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
sortAscending: state.sortAscending, sortAscending: state.sortAscending,
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted, defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
isVideoEnabled: state.isVideoEnabled, isVideoEnabled: state.isVideoEnabled,
unmutedVideoLimit: state.unmutedVideoLimit unmutedVideoLimit: state.unmutedVideoLimit,
isStream: callInfo.isStream
)))) ))))
strongSelf.summaryParticipantsState.set(.single(SummaryParticipantsState( strongSelf.summaryParticipantsState.set(.single(SummaryParticipantsState(
@ -2067,7 +2075,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
sortAscending: state.sortAscending, sortAscending: state.sortAscending,
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted, defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
isVideoEnabled: state.isVideoEnabled, isVideoEnabled: state.isVideoEnabled,
unmutedVideoLimit: state.unmutedVideoLimit unmutedVideoLimit: state.unmutedVideoLimit,
isStream: callInfo.isStream
)))) ))))
strongSelf.summaryParticipantsState.set(.single(SummaryParticipantsState( strongSelf.summaryParticipantsState.set(.single(SummaryParticipantsState(
@ -2980,7 +2989,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
} }
if let value = value { if let value = value {
strongSelf.initialCall = EngineGroupCallDescription(id: value.id, accessHash: value.accessHash, title: value.title, scheduleTimestamp: nil, subscribedToScheduled: false) strongSelf.initialCall = EngineGroupCallDescription(id: value.id, accessHash: value.accessHash, title: value.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: value.isStream)
strongSelf.isStream = value.isStream
strongSelf.updateSessionState(internalState: .active(value), audioSessionControl: strongSelf.audioSessionControl) strongSelf.updateSessionState(internalState: .active(value), audioSessionControl: strongSelf.audioSessionControl)
} else { } else {

View File

@ -240,7 +240,15 @@ struct VoiceChatPeerEntry: Identifiable {
} }
} }
public final class VoiceChatController: ViewController { public protocol VoiceChatController: ViewController {
var call: PresentationGroupCall { get }
var currentOverlayController: VoiceChatOverlayController? { get }
var parentNavigationController: NavigationController? { get set }
func dismiss(closing: Bool, manual: Bool)
}
public final class VoiceChatControllerImpl: ViewController, VoiceChatController {
enum DisplayMode { enum DisplayMode {
case modal(isExpanded: Bool, isFilled: Bool) case modal(isExpanded: Bool, isFilled: Bool)
case fullscreen(controlsHidden: Bool) case fullscreen(controlsHidden: Bool)
@ -748,7 +756,7 @@ public final class VoiceChatController: ViewController {
private var configuration: VoiceChatConfiguration? private var configuration: VoiceChatConfiguration?
private weak var controller: VoiceChatController? private weak var controller: VoiceChatControllerImpl?
private let sharedContext: SharedAccountContext private let sharedContext: SharedAccountContext
private let context: AccountContext private let context: AccountContext
private let call: PresentationGroupCall private let call: PresentationGroupCall
@ -952,7 +960,7 @@ public final class VoiceChatController: ViewController {
private var statsDisposable: Disposable? private var statsDisposable: Disposable?
init(controller: VoiceChatController, sharedContext: SharedAccountContext, call: PresentationGroupCall) { init(controller: VoiceChatControllerImpl, sharedContext: SharedAccountContext, call: PresentationGroupCall) {
self.controller = controller self.controller = controller
self.sharedContext = sharedContext self.sharedContext = sharedContext
self.context = call.accountContext self.context = call.accountContext

View File

@ -144,7 +144,7 @@ public final class VoiceChatJoinScreen: ViewController {
defaultJoinAsPeerId = cachedData.callJoinPeerId defaultJoinAsPeerId = cachedData.callJoinPeerId
} }
let activeCall = CachedChannelData.ActiveCall(id: call.info.id, accessHash: call.info.accessHash, title: call.info.title, scheduleTimestamp: call.info.scheduleTimestamp, subscribedToScheduled: call.info.subscribedToScheduled) let activeCall = CachedChannelData.ActiveCall(id: call.info.id, accessHash: call.info.accessHash, title: call.info.title, scheduleTimestamp: call.info.scheduleTimestamp, subscribedToScheduled: call.info.subscribedToScheduled, isStream: call.info.isStream)
if availablePeers.count > 0 && defaultJoinAsPeerId == nil { if availablePeers.count > 0 && defaultJoinAsPeerId == nil {
strongSelf.dismiss() strongSelf.dismiss()
strongSelf.join(activeCall) strongSelf.join(activeCall)

View File

@ -3174,9 +3174,9 @@ func replayFinalState(
if let info = GroupCallInfo(call) { if let info = GroupCallInfo(call) {
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
if let current = current as? CachedChannelData { if let current = current as? CachedChannelData {
return current.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: info.scheduleTimestamp, subscribedToScheduled: info.subscribedToScheduled)) return current.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: info.scheduleTimestamp, subscribedToScheduled: info.subscribedToScheduled, isStream: info.isStream))
} else if let current = current as? CachedGroupData { } else if let current = current as? CachedGroupData {
return current.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: info.scheduleTimestamp, subscribedToScheduled: info.subscribedToScheduled)) return current.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: info.scheduleTimestamp, subscribedToScheduled: info.subscribedToScheduled, isStream: info.isStream))
} else { } else {
return current return current
} }

View File

@ -161,19 +161,22 @@ public final class CachedChannelData: CachedPeerData {
public var title: String? public var title: String?
public var scheduleTimestamp: Int32? public var scheduleTimestamp: Int32?
public var subscribedToScheduled: Bool public var subscribedToScheduled: Bool
public var isStream: Bool
public init( public init(
id: Int64, id: Int64,
accessHash: Int64, accessHash: Int64,
title: String?, title: String?,
scheduleTimestamp: Int32?, scheduleTimestamp: Int32?,
subscribedToScheduled: Bool subscribedToScheduled: Bool,
isStream: Bool
) { ) {
self.id = id self.id = id
self.accessHash = accessHash self.accessHash = accessHash
self.title = title self.title = title
self.scheduleTimestamp = scheduleTimestamp self.scheduleTimestamp = scheduleTimestamp
self.subscribedToScheduled = subscribedToScheduled self.subscribedToScheduled = subscribedToScheduled
self.isStream = isStream
} }
public init(decoder: PostboxDecoder) { public init(decoder: PostboxDecoder) {
@ -182,6 +185,7 @@ public final class CachedChannelData: CachedPeerData {
self.title = decoder.decodeOptionalStringForKey("title") self.title = decoder.decodeOptionalStringForKey("title")
self.scheduleTimestamp = decoder.decodeOptionalInt32ForKey("scheduleTimestamp") self.scheduleTimestamp = decoder.decodeOptionalInt32ForKey("scheduleTimestamp")
self.subscribedToScheduled = decoder.decodeBoolForKey("subscribed", orElse: false) self.subscribedToScheduled = decoder.decodeBoolForKey("subscribed", orElse: false)
self.isStream = decoder.decodeBoolForKey("isStream", orElse: false)
} }
public func encode(_ encoder: PostboxEncoder) { public func encode(_ encoder: PostboxEncoder) {
@ -198,6 +202,7 @@ public final class CachedChannelData: CachedPeerData {
encoder.encodeNil(forKey: "scheduleTimestamp") encoder.encodeNil(forKey: "scheduleTimestamp")
} }
encoder.encodeBool(self.subscribedToScheduled, forKey: "subscribed") encoder.encodeBool(self.subscribedToScheduled, forKey: "subscribed")
encoder.encodeBool(self.isStream, forKey: "isStream")
} }
} }

View File

@ -16,6 +16,7 @@ public struct GroupCallInfo: Equatable {
public var defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted? public var defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?
public var isVideoEnabled: Bool public var isVideoEnabled: Bool
public var unmutedVideoLimit: Int public var unmutedVideoLimit: Int
public var isStream: Bool
public init( public init(
id: Int64, id: Int64,
@ -29,7 +30,8 @@ public struct GroupCallInfo: Equatable {
sortAscending: Bool, sortAscending: Bool,
defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?, defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?,
isVideoEnabled: Bool, isVideoEnabled: Bool,
unmutedVideoLimit: Int unmutedVideoLimit: Int,
isStream: Bool
) { ) {
self.id = id self.id = id
self.accessHash = accessHash self.accessHash = accessHash
@ -43,6 +45,7 @@ public struct GroupCallInfo: Equatable {
self.defaultParticipantsAreMuted = defaultParticipantsAreMuted self.defaultParticipantsAreMuted = defaultParticipantsAreMuted
self.isVideoEnabled = isVideoEnabled self.isVideoEnabled = isVideoEnabled
self.unmutedVideoLimit = unmutedVideoLimit self.unmutedVideoLimit = unmutedVideoLimit
self.isStream = isStream
} }
} }
@ -67,7 +70,8 @@ extension GroupCallInfo {
sortAscending: (flags & (1 << 6)) != 0, sortAscending: (flags & (1 << 6)) != 0,
defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: (flags & (1 << 1)) != 0, canChange: (flags & (1 << 2)) != 0), defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: (flags & (1 << 1)) != 0, canChange: (flags & (1 << 2)) != 0),
isVideoEnabled: (flags & (1 << 9)) != 0, isVideoEnabled: (flags & (1 << 9)) != 0,
unmutedVideoLimit: Int(unmutedVideoLimit) unmutedVideoLimit: Int(unmutedVideoLimit),
isStream: (flags & (1 << 12)) != 0
) )
case .groupCallDiscarded: case .groupCallDiscarded:
return nil return nil
@ -111,9 +115,9 @@ func _internal_getCurrentGroupCall(account: Account, callId: Int64, accessHash:
if let peerId = peerId { if let peerId = peerId {
transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in
if let cachedData = current as? CachedChannelData { if let cachedData = current as? CachedChannelData {
return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall.init(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: info.scheduleTimestamp, subscribedToScheduled: cachedData.activeCall?.subscribedToScheduled ?? false)) return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall.init(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: info.scheduleTimestamp, subscribedToScheduled: cachedData.activeCall?.subscribedToScheduled ?? false, isStream: info.isStream))
} else if let cachedData = current as? CachedGroupData { } else if let cachedData = current as? CachedGroupData {
return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: info.scheduleTimestamp, subscribedToScheduled: cachedData.activeCall?.subscribedToScheduled ?? false)) return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: info.scheduleTimestamp, subscribedToScheduled: cachedData.activeCall?.subscribedToScheduled ?? false, isStream: info.isStream))
} else { } else {
return current return current
} }
@ -191,9 +195,9 @@ func _internal_createGroupCall(account: Account, peerId: PeerId, title: String?,
return account.postbox.transaction { transaction -> GroupCallInfo in return account.postbox.transaction { transaction -> GroupCallInfo in
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
if let cachedData = cachedData as? CachedChannelData { if let cachedData = cachedData as? CachedChannelData {
return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash, title: callInfo.title, scheduleTimestamp: callInfo.scheduleTimestamp, subscribedToScheduled: callInfo.subscribedToScheduled)) return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash, title: callInfo.title, scheduleTimestamp: callInfo.scheduleTimestamp, subscribedToScheduled: callInfo.subscribedToScheduled, isStream: callInfo.isStream))
} else if let cachedData = cachedData as? CachedGroupData { } else if let cachedData = cachedData as? CachedGroupData {
return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash, title: callInfo.title, scheduleTimestamp: callInfo.scheduleTimestamp, subscribedToScheduled: callInfo.subscribedToScheduled)) return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash, title: callInfo.title, scheduleTimestamp: callInfo.scheduleTimestamp, subscribedToScheduled: callInfo.subscribedToScheduled, isStream: callInfo.isStream))
} else { } else {
return cachedData return cachedData
} }
@ -236,9 +240,9 @@ func _internal_startScheduledGroupCall(account: Account, peerId: PeerId, callId:
return account.postbox.transaction { transaction -> GroupCallInfo in return account.postbox.transaction { transaction -> GroupCallInfo in
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
if let cachedData = cachedData as? CachedChannelData { if let cachedData = cachedData as? CachedChannelData {
return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash, title: callInfo.title, scheduleTimestamp: nil, subscribedToScheduled: false)) return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash, title: callInfo.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: callInfo.isStream))
} else if let cachedData = cachedData as? CachedGroupData { } else if let cachedData = cachedData as? CachedGroupData {
return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash, title: callInfo.title, scheduleTimestamp: nil, subscribedToScheduled: false)) return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash, title: callInfo.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: callInfo.isStream))
} else { } else {
return cachedData return cachedData
} }
@ -280,9 +284,9 @@ func _internal_toggleScheduledGroupCallSubscription(account: Account, peerId: Pe
return account.postbox.transaction { transaction in return account.postbox.transaction { transaction in
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
if let cachedData = cachedData as? CachedChannelData { if let cachedData = cachedData as? CachedChannelData {
return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash, title: callInfo.title, scheduleTimestamp: callInfo.scheduleTimestamp, subscribedToScheduled: callInfo.subscribedToScheduled)) return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash, title: callInfo.title, scheduleTimestamp: callInfo.scheduleTimestamp, subscribedToScheduled: callInfo.subscribedToScheduled, isStream: callInfo.isStream))
} else if let cachedData = cachedData as? CachedGroupData { } else if let cachedData = cachedData as? CachedGroupData {
return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash, title: callInfo.title, scheduleTimestamp: callInfo.scheduleTimestamp, subscribedToScheduled: callInfo.subscribedToScheduled)) return cachedData.withUpdatedActiveCall(CachedChannelData.ActiveCall(id: callInfo.id, accessHash: callInfo.accessHash, title: callInfo.title, scheduleTimestamp: callInfo.scheduleTimestamp, subscribedToScheduled: callInfo.subscribedToScheduled, isStream: callInfo.isStream))
} else { } else {
return cachedData return cachedData
} }
@ -607,9 +611,9 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?,
return account.postbox.transaction { transaction -> JoinGroupCallResult in return account.postbox.transaction { transaction -> JoinGroupCallResult in
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
if let cachedData = cachedData as? CachedChannelData { if let cachedData = cachedData as? CachedChannelData {
return cachedData.withUpdatedCallJoinPeerId(joinAs).withUpdatedActiveCall(CachedChannelData.ActiveCall(id: parsedCall.id, accessHash: parsedCall.accessHash, title: parsedCall.title, scheduleTimestamp: nil, subscribedToScheduled: false)) return cachedData.withUpdatedCallJoinPeerId(joinAs).withUpdatedActiveCall(CachedChannelData.ActiveCall(id: parsedCall.id, accessHash: parsedCall.accessHash, title: parsedCall.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: parsedCall.isStream))
} else if let cachedData = cachedData as? CachedGroupData { } else if let cachedData = cachedData as? CachedGroupData {
return cachedData.withUpdatedCallJoinPeerId(joinAs).withUpdatedActiveCall(CachedChannelData.ActiveCall(id: parsedCall.id, accessHash: parsedCall.accessHash, title: parsedCall.title, scheduleTimestamp: nil, subscribedToScheduled: false)) return cachedData.withUpdatedCallJoinPeerId(joinAs).withUpdatedActiveCall(CachedChannelData.ActiveCall(id: parsedCall.id, accessHash: parsedCall.accessHash, title: parsedCall.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: parsedCall.isStream))
} else { } else {
return cachedData return cachedData
} }
@ -2606,3 +2610,35 @@ private extension GroupCallParticipantsContext.Participant.VideoDescription {
} }
} }
} }
public struct GroupCallStreamCredentials {
public var url: String
public var streamKey: String
}
public enum GetGroupCallStreamCredentialsError {
case generic
}
func _internal_getGroupCallStreamCredentials(account: Account, peerId: PeerId, revokePreviousCredentials: Bool) -> Signal<GroupCallStreamCredentials, GetGroupCallStreamCredentialsError> {
return account.postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(peerId).flatMap(apiInputPeer)
}
|> castError(GetGroupCallStreamCredentialsError.self)
|> mapToSignal { inputPeer -> Signal<GroupCallStreamCredentials, GetGroupCallStreamCredentialsError> in
guard let inputPeer = inputPeer else {
return .fail(.generic)
}
return account.network.request(Api.functions.phone.getGroupCallStreamRtmpUrl(peer: inputPeer, revoke: revokePreviousCredentials ? .boolTrue : .boolFalse))
|> mapError { _ -> GetGroupCallStreamCredentialsError in
return .generic
}
|> map { result -> GroupCallStreamCredentials in
switch result {
case let .groupCallStreamRtmpUrl(url, key):
return GroupCallStreamCredentials(url: url, streamKey: key)
}
}
}
}

View File

@ -143,16 +143,16 @@ public extension TelegramEngine {
return EngineCallStreamState.Channel(id: channel, scale: scale, latestTimestamp: lastTimestampMs) return EngineCallStreamState.Channel(id: channel, scale: scale, latestTimestamp: lastTimestampMs)
} }
}) })
/*if state.channels.isEmpty {
return .fail(MTRpcError(errorCode: 500, errorDescription: "Generated")) |> delay(10.0, queue: .mainQueue())
}*/
return .single(state) return .single(state)
} }
} }
//|> restartIfError
|> `catch` { _ -> Signal<EngineCallStreamState?, NoError> in |> `catch` { _ -> Signal<EngineCallStreamState?, NoError> in
return .single(nil) return .single(nil)
} }
} }
public func getGroupCallStreamCredentials(peerId: EnginePeer.Id, revokePreviousCredentials: Bool) -> Signal<GroupCallStreamCredentials, GetGroupCallStreamCredentialsError> {
return _internal_getGroupCallStreamCredentials(account: self.account, peerId: peerId, revokePreviousCredentials: revokePreviousCredentials)
}
} }
} }

View File

@ -6,19 +6,22 @@ public final class EngineGroupCallDescription {
public let title: String? public let title: String?
public let scheduleTimestamp: Int32? public let scheduleTimestamp: Int32?
public let subscribedToScheduled: Bool public let subscribedToScheduled: Bool
public let isStream: Bool
public init( public init(
id: Int64, id: Int64,
accessHash: Int64, accessHash: Int64,
title: String?, title: String?,
scheduleTimestamp: Int32?, scheduleTimestamp: Int32?,
subscribedToScheduled: Bool subscribedToScheduled: Bool,
isStream: Bool
) { ) {
self.id = id self.id = id
self.accessHash = accessHash self.accessHash = accessHash
self.title = title self.title = title
self.scheduleTimestamp = scheduleTimestamp self.scheduleTimestamp = scheduleTimestamp
self.subscribedToScheduled = subscribedToScheduled self.subscribedToScheduled = subscribedToScheduled
self.isStream = isStream
} }
} }
@ -29,7 +32,8 @@ public extension EngineGroupCallDescription {
accessHash: activeCall.accessHash, accessHash: activeCall.accessHash,
title: activeCall.title, title: activeCall.title,
scheduleTimestamp: activeCall.scheduleTimestamp, scheduleTimestamp: activeCall.scheduleTimestamp,
subscribedToScheduled: activeCall.subscribedToScheduled subscribedToScheduled: activeCall.subscribedToScheduled,
isStream: activeCall.isStream
) )
} }
} }

View File

@ -349,7 +349,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
if let inputCall = chatFullCall { if let inputCall = chatFullCall {
switch inputCall { switch inputCall {
case let .inputGroupCall(id, accessHash): case let .inputGroupCall(id, accessHash):
updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash, title: previous.activeCall?.title, scheduleTimestamp: previous.activeCall?.scheduleTimestamp, subscribedToScheduled: previous.activeCall?.subscribedToScheduled ?? false) updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash, title: previous.activeCall?.title, scheduleTimestamp: previous.activeCall?.scheduleTimestamp, subscribedToScheduled: previous.activeCall?.subscribedToScheduled ?? false, isStream: previous.activeCall?.isStream ?? false)
} }
} }
@ -568,7 +568,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
if let inputCall = inputCall { if let inputCall = inputCall {
switch inputCall { switch inputCall {
case let .inputGroupCall(id, accessHash): case let .inputGroupCall(id, accessHash):
updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash, title: previous.activeCall?.title, scheduleTimestamp: previous.activeCall?.scheduleTimestamp, subscribedToScheduled: previous.activeCall?.subscribedToScheduled ?? false) updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash, title: previous.activeCall?.title, scheduleTimestamp: previous.activeCall?.scheduleTimestamp, subscribedToScheduled: previous.activeCall?.subscribedToScheduled ?? false, isStream: previous.activeCall?.isStream ?? false)
} }
} }

View File

@ -648,7 +648,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
case .groupPhoneCall, .inviteToGroupPhoneCall: case .groupPhoneCall, .inviteToGroupPhoneCall:
if let activeCall = strongSelf.presentationInterfaceState.activeGroupCallInfo?.activeCall { if let activeCall = strongSelf.presentationInterfaceState.activeGroupCallInfo?.activeCall {
strongSelf.joinGroupCall(peerId: message.id.peerId, invite: nil, activeCall: EngineGroupCallDescription(id: activeCall.id, accessHash: activeCall.accessHash, title: activeCall.title, scheduleTimestamp: activeCall.scheduleTimestamp, subscribedToScheduled: activeCall.subscribedToScheduled)) strongSelf.joinGroupCall(peerId: message.id.peerId, invite: nil, activeCall: EngineGroupCallDescription(id: activeCall.id, accessHash: activeCall.accessHash, title: activeCall.title, scheduleTimestamp: activeCall.scheduleTimestamp, subscribedToScheduled: activeCall.subscribedToScheduled, isStream: activeCall.isStream))
} else { } else {
var canManageGroupCalls = false var canManageGroupCalls = false
if let channel = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramChannel { if let channel = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramChannel {
@ -688,7 +688,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
strongSelf.joinGroupCall(peerId: message.id.peerId, invite: nil, activeCall: EngineGroupCallDescription(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: info.scheduleTimestamp, subscribedToScheduled: info.subscribedToScheduled)) strongSelf.joinGroupCall(peerId: message.id.peerId, invite: nil, activeCall: EngineGroupCallDescription(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: info.scheduleTimestamp, subscribedToScheduled: info.subscribedToScheduled, isStream: info.isStream))
}, error: { [weak self] error in }, error: { [weak self] error in
dismissStatus?() dismissStatus?()

View File

@ -4151,7 +4151,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
} }
strongSelf.context.joinGroupCall(peerId: peerId, invite: nil, requestJoinAsPeerId: { result in strongSelf.context.joinGroupCall(peerId: peerId, invite: nil, requestJoinAsPeerId: { result in
result(joinAsPeerId) result(joinAsPeerId)
}, activeCall: EngineGroupCallDescription(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: nil, subscribedToScheduled: false)) }, activeCall: EngineGroupCallDescription(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: info.isStream))
}, error: { [weak self] error in }, error: { [weak self] error in
guard let strongSelf = self else { guard let strongSelf = self else {
return return

View File

@ -650,28 +650,49 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|> deliverOnMainQueue).start(next: { [weak self] call in |> deliverOnMainQueue).start(next: { [weak self] call in
if let strongSelf = self { if let strongSelf = self {
if call !== strongSelf.groupCallController?.call { if call !== strongSelf.groupCallController?.call {
strongSelf.groupCallController?.dismiss(closing: true) strongSelf.groupCallController?.dismiss(closing: true, manual: false)
strongSelf.groupCallController = nil strongSelf.groupCallController = nil
strongSelf.hasOngoingCall.set(false) strongSelf.hasOngoingCall.set(false)
if let call = call, let navigationController = mainWindow.viewController as? NavigationController { if let call = call, let navigationController = mainWindow.viewController as? NavigationController {
mainWindow.hostView.containerView.endEditing(true) mainWindow.hostView.containerView.endEditing(true)
strongSelf.hasGroupCallOnScreenPromise.set(true)
let groupCallController = VoiceChatController(sharedContext: strongSelf, accountContext: call.accountContext, call: call) if call.isStream {
groupCallController.onViewDidAppear = { [weak self] in strongSelf.hasGroupCallOnScreenPromise.set(true)
if let strongSelf = self { let groupCallController = MediaStreamComponentController(call: call)
strongSelf.hasGroupCallOnScreenPromise.set(true) groupCallController.onViewDidAppear = { [weak self] in
if let strongSelf = self {
strongSelf.hasGroupCallOnScreenPromise.set(true)
}
} }
} groupCallController.onViewDidDisappear = { [weak self] in
groupCallController.onViewDidDisappear = { [weak self] in if let strongSelf = self {
if let strongSelf = self { strongSelf.hasGroupCallOnScreenPromise.set(false)
strongSelf.hasGroupCallOnScreenPromise.set(false) }
} }
groupCallController.navigationPresentation = .flatModal
groupCallController.parentNavigationController = navigationController
strongSelf.groupCallController = groupCallController
navigationController.pushViewController(groupCallController)
} else {
strongSelf.hasGroupCallOnScreenPromise.set(true)
let groupCallController = VoiceChatControllerImpl(sharedContext: strongSelf, accountContext: call.accountContext, call: call)
groupCallController.onViewDidAppear = { [weak self] in
if let strongSelf = self {
strongSelf.hasGroupCallOnScreenPromise.set(true)
}
}
groupCallController.onViewDidDisappear = { [weak self] in
if let strongSelf = self {
strongSelf.hasGroupCallOnScreenPromise.set(false)
}
}
groupCallController.navigationPresentation = .flatModal
groupCallController.parentNavigationController = navigationController
strongSelf.groupCallController = groupCallController
navigationController.pushViewController(groupCallController)
} }
groupCallController.navigationPresentation = .flatModal
groupCallController.parentNavigationController = navigationController
strongSelf.groupCallController = groupCallController
navigationController.pushViewController(groupCallController)
strongSelf.hasOngoingCall.set(true) strongSelf.hasOngoingCall.set(true)
} else { } else {
strongSelf.hasOngoingCall.set(false) strongSelf.hasOngoingCall.set(false)

@ -1 +1 @@
Subproject commit 7214d1df903dfcfb58a78858414d5724cfae5578 Subproject commit a5ae22266f4113c60dd21bc405371b98af0359cd