mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Streaming improvements
This commit is contained in:
parent
925746e896
commit
baec592ca1
@ -402,6 +402,8 @@ public protocol PresentationGroupCall: AnyObject {
|
||||
|
||||
var schedulePending: Bool { get }
|
||||
|
||||
var isStream: Bool { get }
|
||||
|
||||
var audioOutputState: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> { get }
|
||||
|
||||
var isSpeaking: Signal<Bool, NoError> { get }
|
||||
|
@ -84,10 +84,13 @@ extension UIView {
|
||||
}
|
||||
}
|
||||
|
||||
public class ComponentState {
|
||||
open class ComponentState {
|
||||
var _updated: ((Transition) -> Void)?
|
||||
var isUpdated: Bool = false
|
||||
|
||||
public init() {
|
||||
}
|
||||
|
||||
public final func updated(transition: Transition = .immediate) {
|
||||
self.isUpdated = true
|
||||
self._updated?(transition)
|
||||
|
@ -542,6 +542,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-6249322] = { return Api.InputStickerSetItem.parse_inputStickerSetItem($0) }
|
||||
dict[-1728664459] = { return Api.help.PromoData.parse_promoDataEmpty($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[-1613493288] = { return Api.NotifyPeer.parse_notifyPeer($0) }
|
||||
dict[-1261946036] = { return Api.NotifyPeer.parse_notifyUsers($0) }
|
||||
@ -1336,6 +1337,8 @@ public struct Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.help.PromoData:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.phone.GroupCallStreamRtmpUrl:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.PeerSettings:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.NotifyPeer:
|
||||
|
@ -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 {
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -420,7 +420,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
|
||||
strongSelf.joinGroupCall(
|
||||
peerId: groupCallPanelData.peerId,
|
||||
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)
|
||||
|
@ -95,6 +95,7 @@ swift_library(
|
||||
"//submodules/Markdown:Markdown",
|
||||
"//submodules/ChatTitleActivityNode:ChatTitleActivityNode",
|
||||
"//third-party/libyuv:LibYuvBinding",
|
||||
"//submodules/ComponentFlow:ComponentFlow",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -107,7 +107,8 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
|
||||
sortAscending: true,
|
||||
defaultParticipantsAreMuted: nil,
|
||||
isVideoEnabled: false,
|
||||
unmutedVideoLimit: 0
|
||||
unmutedVideoLimit: 0,
|
||||
isStream: call.isStream
|
||||
),
|
||||
topParticipants: [],
|
||||
participantCount: 0,
|
||||
@ -160,7 +161,7 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
|
||||
return GroupCallPanelData(
|
||||
peerId: peerId,
|
||||
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,
|
||||
participantCount: state.totalCount,
|
||||
activeSpeakers: activeSpeakers,
|
||||
@ -629,6 +630,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
private var screencastAudioDataDisposable: Disposable?
|
||||
private var screencastStateDisposable: Disposable?
|
||||
|
||||
public var isStream = false
|
||||
|
||||
init(
|
||||
accountContext: AccountContext,
|
||||
audioSession: ManagedAudioSession,
|
||||
@ -656,6 +659,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
self.schedulePending = initialCall == 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.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
|
||||
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)
|
||||
} else {
|
||||
@ -1312,7 +1319,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
strongSelf.stateValue.subscribedToScheduled = state.subscribedToScheduled
|
||||
strongSelf.stateValue.scheduleTimestamp = strongSelf.isScheduledStarted ? nil : state.scheduleTimestamp
|
||||
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 {
|
||||
strongSelf.summaryInfoState.set(.single(SummaryInfoState(info: GroupCallInfo(
|
||||
id: callInfo.id,
|
||||
@ -1326,7 +1333,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
sortAscending: state.sortAscending,
|
||||
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
|
||||
isVideoEnabled: state.isVideoEnabled,
|
||||
unmutedVideoLimit: state.unmutedVideoLimit
|
||||
unmutedVideoLimit: state.unmutedVideoLimit,
|
||||
isStream: callInfo.isStream
|
||||
))))
|
||||
|
||||
strongSelf.summaryParticipantsState.set(.single(SummaryParticipantsState(
|
||||
@ -2067,7 +2075,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
sortAscending: state.sortAscending,
|
||||
defaultParticipantsAreMuted: state.defaultParticipantsAreMuted,
|
||||
isVideoEnabled: state.isVideoEnabled,
|
||||
unmutedVideoLimit: state.unmutedVideoLimit
|
||||
unmutedVideoLimit: state.unmutedVideoLimit,
|
||||
isStream: callInfo.isStream
|
||||
))))
|
||||
|
||||
strongSelf.summaryParticipantsState.set(.single(SummaryParticipantsState(
|
||||
@ -2980,7 +2989,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
}
|
||||
|
||||
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)
|
||||
} else {
|
||||
|
@ -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 {
|
||||
case modal(isExpanded: Bool, isFilled: Bool)
|
||||
case fullscreen(controlsHidden: Bool)
|
||||
@ -748,7 +756,7 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
private var configuration: VoiceChatConfiguration?
|
||||
|
||||
private weak var controller: VoiceChatController?
|
||||
private weak var controller: VoiceChatControllerImpl?
|
||||
private let sharedContext: SharedAccountContext
|
||||
private let context: AccountContext
|
||||
private let call: PresentationGroupCall
|
||||
@ -952,7 +960,7 @@ public final class VoiceChatController: ViewController {
|
||||
|
||||
private var statsDisposable: Disposable?
|
||||
|
||||
init(controller: VoiceChatController, sharedContext: SharedAccountContext, call: PresentationGroupCall) {
|
||||
init(controller: VoiceChatControllerImpl, sharedContext: SharedAccountContext, call: PresentationGroupCall) {
|
||||
self.controller = controller
|
||||
self.sharedContext = sharedContext
|
||||
self.context = call.accountContext
|
||||
|
@ -144,7 +144,7 @@ public final class VoiceChatJoinScreen: ViewController {
|
||||
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 {
|
||||
strongSelf.dismiss()
|
||||
strongSelf.join(activeCall)
|
||||
|
@ -3174,9 +3174,9 @@ func replayFinalState(
|
||||
if let info = GroupCallInfo(call) {
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||
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 {
|
||||
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 {
|
||||
return current
|
||||
}
|
||||
|
@ -161,19 +161,22 @@ public final class CachedChannelData: CachedPeerData {
|
||||
public var title: String?
|
||||
public var scheduleTimestamp: Int32?
|
||||
public var subscribedToScheduled: Bool
|
||||
public var isStream: Bool
|
||||
|
||||
public init(
|
||||
id: Int64,
|
||||
accessHash: Int64,
|
||||
title: String?,
|
||||
scheduleTimestamp: Int32?,
|
||||
subscribedToScheduled: Bool
|
||||
subscribedToScheduled: Bool,
|
||||
isStream: Bool
|
||||
) {
|
||||
self.id = id
|
||||
self.accessHash = accessHash
|
||||
self.title = title
|
||||
self.scheduleTimestamp = scheduleTimestamp
|
||||
self.subscribedToScheduled = subscribedToScheduled
|
||||
self.isStream = isStream
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
@ -182,6 +185,7 @@ public final class CachedChannelData: CachedPeerData {
|
||||
self.title = decoder.decodeOptionalStringForKey("title")
|
||||
self.scheduleTimestamp = decoder.decodeOptionalInt32ForKey("scheduleTimestamp")
|
||||
self.subscribedToScheduled = decoder.decodeBoolForKey("subscribed", orElse: false)
|
||||
self.isStream = decoder.decodeBoolForKey("isStream", orElse: false)
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
@ -198,6 +202,7 @@ public final class CachedChannelData: CachedPeerData {
|
||||
encoder.encodeNil(forKey: "scheduleTimestamp")
|
||||
}
|
||||
encoder.encodeBool(self.subscribedToScheduled, forKey: "subscribed")
|
||||
encoder.encodeBool(self.isStream, forKey: "isStream")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ public struct GroupCallInfo: Equatable {
|
||||
public var defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?
|
||||
public var isVideoEnabled: Bool
|
||||
public var unmutedVideoLimit: Int
|
||||
public var isStream: Bool
|
||||
|
||||
public init(
|
||||
id: Int64,
|
||||
@ -29,7 +30,8 @@ public struct GroupCallInfo: Equatable {
|
||||
sortAscending: Bool,
|
||||
defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted?,
|
||||
isVideoEnabled: Bool,
|
||||
unmutedVideoLimit: Int
|
||||
unmutedVideoLimit: Int,
|
||||
isStream: Bool
|
||||
) {
|
||||
self.id = id
|
||||
self.accessHash = accessHash
|
||||
@ -43,6 +45,7 @@ public struct GroupCallInfo: Equatable {
|
||||
self.defaultParticipantsAreMuted = defaultParticipantsAreMuted
|
||||
self.isVideoEnabled = isVideoEnabled
|
||||
self.unmutedVideoLimit = unmutedVideoLimit
|
||||
self.isStream = isStream
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,7 +70,8 @@ extension GroupCallInfo {
|
||||
sortAscending: (flags & (1 << 6)) != 0,
|
||||
defaultParticipantsAreMuted: GroupCallParticipantsContext.State.DefaultParticipantsAreMuted(isMuted: (flags & (1 << 1)) != 0, canChange: (flags & (1 << 2)) != 0),
|
||||
isVideoEnabled: (flags & (1 << 9)) != 0,
|
||||
unmutedVideoLimit: Int(unmutedVideoLimit)
|
||||
unmutedVideoLimit: Int(unmutedVideoLimit),
|
||||
isStream: (flags & (1 << 12)) != 0
|
||||
)
|
||||
case .groupCallDiscarded:
|
||||
return nil
|
||||
@ -111,9 +115,9 @@ func _internal_getCurrentGroupCall(account: Account, callId: Int64, accessHash:
|
||||
if let peerId = peerId {
|
||||
transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in
|
||||
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 {
|
||||
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 {
|
||||
return current
|
||||
}
|
||||
@ -191,9 +195,9 @@ func _internal_createGroupCall(account: Account, peerId: PeerId, title: String?,
|
||||
return account.postbox.transaction { transaction -> GroupCallInfo in
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||
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 {
|
||||
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 {
|
||||
return cachedData
|
||||
}
|
||||
@ -236,9 +240,9 @@ func _internal_startScheduledGroupCall(account: Account, peerId: PeerId, callId:
|
||||
return account.postbox.transaction { transaction -> GroupCallInfo in
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||
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 {
|
||||
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 {
|
||||
return cachedData
|
||||
}
|
||||
@ -280,9 +284,9 @@ func _internal_toggleScheduledGroupCallSubscription(account: Account, peerId: Pe
|
||||
return account.postbox.transaction { transaction in
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||
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 {
|
||||
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 {
|
||||
return cachedData
|
||||
}
|
||||
@ -607,9 +611,9 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?,
|
||||
return account.postbox.transaction { transaction -> JoinGroupCallResult in
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||
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 {
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -143,16 +143,16 @@ public extension TelegramEngine {
|
||||
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)
|
||||
}
|
||||
}
|
||||
//|> restartIfError
|
||||
|> `catch` { _ -> Signal<EngineCallStreamState?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func getGroupCallStreamCredentials(peerId: EnginePeer.Id, revokePreviousCredentials: Bool) -> Signal<GroupCallStreamCredentials, GetGroupCallStreamCredentialsError> {
|
||||
return _internal_getGroupCallStreamCredentials(account: self.account, peerId: peerId, revokePreviousCredentials: revokePreviousCredentials)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,19 +6,22 @@ public final class EngineGroupCallDescription {
|
||||
public let title: String?
|
||||
public let scheduleTimestamp: Int32?
|
||||
public let subscribedToScheduled: Bool
|
||||
public let isStream: Bool
|
||||
|
||||
public init(
|
||||
id: Int64,
|
||||
accessHash: Int64,
|
||||
title: String?,
|
||||
scheduleTimestamp: Int32?,
|
||||
subscribedToScheduled: Bool
|
||||
subscribedToScheduled: Bool,
|
||||
isStream: Bool
|
||||
) {
|
||||
self.id = id
|
||||
self.accessHash = accessHash
|
||||
self.title = title
|
||||
self.scheduleTimestamp = scheduleTimestamp
|
||||
self.subscribedToScheduled = subscribedToScheduled
|
||||
self.isStream = isStream
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,7 +32,8 @@ public extension EngineGroupCallDescription {
|
||||
accessHash: activeCall.accessHash,
|
||||
title: activeCall.title,
|
||||
scheduleTimestamp: activeCall.scheduleTimestamp,
|
||||
subscribedToScheduled: activeCall.subscribedToScheduled
|
||||
subscribedToScheduled: activeCall.subscribedToScheduled,
|
||||
isStream: activeCall.isStream
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -349,7 +349,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
|
||||
if let inputCall = chatFullCall {
|
||||
switch inputCall {
|
||||
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 {
|
||||
switch inputCall {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -648,7 +648,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
case .groupPhoneCall, .inviteToGroupPhoneCall:
|
||||
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 {
|
||||
var canManageGroupCalls = false
|
||||
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 {
|
||||
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
|
||||
dismissStatus?()
|
||||
|
||||
|
@ -4151,7 +4151,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
}
|
||||
strongSelf.context.joinGroupCall(peerId: peerId, invite: nil, requestJoinAsPeerId: { result in
|
||||
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
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
@ -650,14 +650,16 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
|> deliverOnMainQueue).start(next: { [weak self] call in
|
||||
if let strongSelf = self {
|
||||
if call !== strongSelf.groupCallController?.call {
|
||||
strongSelf.groupCallController?.dismiss(closing: true)
|
||||
strongSelf.groupCallController?.dismiss(closing: true, manual: false)
|
||||
strongSelf.groupCallController = nil
|
||||
strongSelf.hasOngoingCall.set(false)
|
||||
|
||||
if let call = call, let navigationController = mainWindow.viewController as? NavigationController {
|
||||
mainWindow.hostView.containerView.endEditing(true)
|
||||
|
||||
if call.isStream {
|
||||
strongSelf.hasGroupCallOnScreenPromise.set(true)
|
||||
let groupCallController = VoiceChatController(sharedContext: strongSelf, accountContext: call.accountContext, call: call)
|
||||
let groupCallController = MediaStreamComponentController(call: call)
|
||||
groupCallController.onViewDidAppear = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.hasGroupCallOnScreenPromise.set(true)
|
||||
@ -672,6 +674,25 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
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)
|
||||
}
|
||||
|
||||
strongSelf.hasOngoingCall.set(true)
|
||||
} else {
|
||||
strongSelf.hasOngoingCall.set(false)
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 7214d1df903dfcfb58a78858414d5724cfae5578
|
||||
Subproject commit a5ae22266f4113c60dd21bc405371b98af0359cd
|
Loading…
x
Reference in New Issue
Block a user