From 23ebf3922121fb7c5b57bdcf798ee99797c1ba07 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Fri, 28 Feb 2025 00:34:28 +0100 Subject: [PATCH] Various improvements --- .../Public/FFMpegBinding/FFMpegPacket.h | 1 + .../Sources/FFMpegAVFormatContext.m | 1 + .../FFMpegBinding/Sources/FFMpegPacket.m | 4 + .../TGMediaPickerGalleryVideoScrubber.m | 2 +- ...hunkMediaPlayerDirectFetchSourceImpl.swift | 39 +- .../Sources/PresentationGroupCall.swift | 660 +++++++++--------- .../Sources/HLSVideoJSNativeContentNode.swift | 45 +- .../Sources/WebViewNativeJSContextImpl.swift | 14 +- .../Sources/OngoingCallThreadLocalContext.mm | 42 +- 9 files changed, 448 insertions(+), 360 deletions(-) diff --git a/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegPacket.h b/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegPacket.h index 12cc07def2..a3e26d45c5 100644 --- a/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegPacket.h +++ b/submodules/FFMpegBinding/Public/FFMpegBinding/FFMpegPacket.h @@ -16,6 +16,7 @@ NS_ASSUME_NONNULL_BEGIN - (void *)impl; - (int32_t)sendToDecoder:(FFMpegAVCodecContext *)codecContext; +- (void)reuse; @end diff --git a/submodules/FFMpegBinding/Sources/FFMpegAVFormatContext.m b/submodules/FFMpegBinding/Sources/FFMpegAVFormatContext.m index 1ec9a8e55d..ebc0d4f963 100644 --- a/submodules/FFMpegBinding/Sources/FFMpegAVFormatContext.m +++ b/submodules/FFMpegBinding/Sources/FFMpegAVFormatContext.m @@ -77,6 +77,7 @@ int FFMpegCodecIdAV1 = AV_CODEC_ID_AV1; } - (bool)readFrameIntoPacket:(FFMpegPacket *)packet { + [packet reuse]; int result = av_read_frame(_impl, (AVPacket *)[packet impl]); return result >= 0; } diff --git a/submodules/FFMpegBinding/Sources/FFMpegPacket.m b/submodules/FFMpegBinding/Sources/FFMpegPacket.m index d077b28615..7da5655c96 100644 --- a/submodules/FFMpegBinding/Sources/FFMpegPacket.m +++ b/submodules/FFMpegBinding/Sources/FFMpegPacket.m @@ -65,4 +65,8 @@ return avcodec_send_packet((AVCodecContext *)[codecContext impl], _impl); } +- (void)reuse { + av_packet_unref(_impl); +} + @end diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m index 097e0cc147..e47a8912da 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m @@ -287,7 +287,7 @@ typedef enum NSTimeInterval duration = trimEndPosition - trimStartPosition; - if (trimEndPosition - trimStartPosition < self.minimumLength) + if (trimEndPosition - trimStartPosition < strongSelf.minimumLength) return; if (strongSelf.maximumLength > DBL_EPSILON && duration > strongSelf.maximumLength) diff --git a/submodules/MediaPlayer/Sources/ChunkMediaPlayerDirectFetchSourceImpl.swift b/submodules/MediaPlayer/Sources/ChunkMediaPlayerDirectFetchSourceImpl.swift index cf93171377..15ed80f1e6 100644 --- a/submodules/MediaPlayer/Sources/ChunkMediaPlayerDirectFetchSourceImpl.swift +++ b/submodules/MediaPlayer/Sources/ChunkMediaPlayerDirectFetchSourceImpl.swift @@ -472,12 +472,16 @@ final class ChunkMediaPlayerDirectFetchSourceImpl: ChunkMediaPlayerSourceImpl { return self.partsStateValue.get() } + private var resourceSizeDisposable: Disposable? private var completeFetchDisposable: Disposable? private var seekTimestamp: Double? private var currentLookaheadId: Int = 0 private var lookahead: FFMpegLookahead? + private var resolvedResourceSize: Int64? + private var pendingSeek: (id: Int, position: Double)? + init(resource: ChunkMediaPlayerV2.SourceDescription.ResourceDescription) { self.resource = resource @@ -494,16 +498,43 @@ final class ChunkMediaPlayerDirectFetchSourceImpl: ChunkMediaPlayerSourceImpl { } deinit { + self.resourceSizeDisposable?.dispose() self.completeFetchDisposable?.dispose() } func seek(id: Int, position: Double) { + if self.resource.size == 0 && self.resolvedResourceSize == nil { + self.pendingSeek = (id, position) + + if self.resourceSizeDisposable == nil { + self.resourceSizeDisposable = (self.resource.postbox.mediaBox.resourceData(self.resource.reference.resource, option: .complete(waitUntilFetchStatus: false)) + |> deliverOnMainQueue).start(next: { [weak self] data in + guard let self else { + return + } + if data.complete { + if self.resolvedResourceSize == nil { + self.resolvedResourceSize = data.size + + if let pendingSeek = self.pendingSeek { + self.seek(id: pendingSeek.id, position: pendingSeek.position) + } + } + } + }) + } + + return + } + self.seekTimestamp = position self.currentLookaheadId += 1 let lookaheadId = self.currentLookaheadId let resource = self.resource + let resourceSize = self.resolvedResourceSize ?? Int64(resource.size) + let updateState: (FFMpegLookahead.State) -> Void = { [weak self] state in Queue.mainQueue().async { guard let self else { @@ -559,7 +590,7 @@ final class ChunkMediaPlayerDirectFetchSourceImpl: ChunkMediaPlayerSourceImpl { return ChunkMediaPlayerPartsState.DirectReader.Stream( mediaBox: resource.postbox.mediaBox, resource: resource.reference.resource, - size: resource.size, + size: resourceSize, index: media.info.index, seek: (streamIndex: state.seek.streamIndex, pts: state.seek.pts), maxReadablePts: (streamIndex: maxReadablePts.streamIndex, pts: maxReadablePts.pts, isEnded: state.isEnded), @@ -602,18 +633,18 @@ final class ChunkMediaPlayerDirectFetchSourceImpl: ChunkMediaPlayerSourceImpl { ).startStrict() }, getDataInRange: { range, completion in - return resource.postbox.mediaBox.resourceData(resource.reference.resource, size: resource.size, in: range, mode: .complete).start(next: { result, isComplete in + return resource.postbox.mediaBox.resourceData(resource.reference.resource, size: resourceSize, in: range, mode: .complete).start(next: { result, isComplete in completion(isComplete ? result : nil) }) }, isDataCachedInRange: { range in return resource.postbox.mediaBox.internal_resourceDataIsCached( id: resource.reference.resource.id, - size: resource.size, + size: resourceSize, in: range ) }, - size: self.resource.size + size: resourceSize ) } diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index 32ed884120..8893bb0cf0 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -111,7 +111,7 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext { peer ) |> deliverOnMainQueue).start(next: { [weak self] state, peer in - guard let strongSelf = self, let state = state else { + guard let self, let state = state else { return } let context = engine.calls.groupCall( @@ -123,10 +123,10 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext { previousServiceState: nil ) - strongSelf.participantsContext = context + self.participantsContext = context if let peerId { - strongSelf.panelDataPromise.set(combineLatest(queue: .mainQueue(), + self.panelDataPromise.set(combineLatest(queue: .mainQueue(), context.state, context.activeSpeakers ) @@ -199,12 +199,12 @@ public final class AccountGroupCallContextCacheImpl: AccountGroupCallContextCach result.removeTimer = nil return AccountGroupCallContextImpl.Proxy(context: result.context, removed: { [weak self, weak result] in Queue.mainQueue().async { - if let strongResult = result, let strongSelf = self, strongSelf.contexts[call.id] === strongResult { + if let strongResult = result, let self, self.contexts[call.id] === strongResult { strongResult.subscribers.remove(index) if strongResult.subscribers.isEmpty { - let removeTimer = SwiftSignalKit.Timer(timeout: 30, repeat: false, completion: { - if let result = result, let strongSelf = self, strongSelf.contexts[call.id] === result, result.subscribers.isEmpty { - strongSelf.contexts.removeValue(forKey: call.id) + let removeTimer = SwiftSignalKit.Timer(timeout: 30, repeat: false, completion: { [weak self] in + if let result = result, let self, self.contexts[call.id] === result, result.subscribers.isEmpty { + self.contexts.removeValue(forKey: call.id) } }, queue: .mainQueue()) strongResult.removeTimer = removeTimer @@ -217,10 +217,10 @@ public final class AccountGroupCallContextCacheImpl: AccountGroupCallContextCach public func leaveInBackground(engine: TelegramEngine, id: Int64, accessHash: Int64, source: UInt32) { let disposable = engine.calls.leaveGroupCall(callId: id, accessHash: accessHash, source: source).start(completed: { [weak self] in - guard let strongSelf = self else { + guard let self else { return } - if let context = strongSelf.contexts[id] { + if let context = self.contexts[id] { context.context.participantsContext?.removeLocalPeerId() } }) @@ -1151,19 +1151,19 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.audioSessionDisposable = audioSession.push(audioSessionType: self.isStream ? .play(mixWithOthers: false) : .voiceCall, activateImmediately: true, manualActivate: { [weak self] control in Queue.mainQueue().async { - if let strongSelf = self { - strongSelf.updateSessionState(internalState: strongSelf.internalState, audioSessionControl: control) + if let self { + self.updateSessionState(internalState: self.internalState, audioSessionControl: control) } } }, deactivate: { [weak self] _ in return Signal { subscriber in Queue.mainQueue().async { - if let strongSelf = self { - strongSelf.updateIsAudioSessionActive(false) - strongSelf.updateSessionState(internalState: strongSelf.internalState, audioSessionControl: nil) + if let self { + self.updateIsAudioSessionActive(false) + self.updateSessionState(internalState: self.internalState, audioSessionControl: nil) - if strongSelf.isStream { - let _ = strongSelf.leave(terminateIfPossible: false) + if self.isStream { + let _ = self.leave(terminateIfPossible: false) } } subscriber.putCompletion() @@ -1172,10 +1172,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } }, availableOutputsChanged: { [weak self] availableOutputs, currentOutput in Queue.mainQueue().async { - guard let strongSelf = self else { + guard let self else { return } - strongSelf.audioOutputStateValue = (availableOutputs, currentOutput) + self.audioOutputStateValue = (availableOutputs, currentOutput) var signal: Signal<([AudioSessionOutput], AudioSessionOutput?), NoError> = .single((availableOutputs, currentOutput)) if !didReceiveAudioOutputs { @@ -1188,18 +1188,18 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { ) } } - strongSelf.audioOutputStatePromise.set(signal) + self.audioOutputStatePromise.set(signal) } }) self.audioSessionShouldBeActiveDisposable = (self.audioSessionShouldBeActive.get() |> deliverOnMainQueue).start(next: { [weak self] value in - guard let strongSelf = self else { + guard let self else { return } if value { - if let audioSessionControl = strongSelf.audioSessionControl { - if !strongSelf.isStream, let callKitIntegration = strongSelf.callKitIntegration { + if let audioSessionControl = self.audioSessionControl { + if !self.isStream, let callKitIntegration = self.callKitIntegration { _ = callKitIntegration.audioSessionActive |> filter { $0 } |> timeout(2.0, queue: Queue.mainQueue(), alternate: Signal { subscriber in @@ -1208,47 +1208,47 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { return EmptyDisposable }) } else { - audioSessionControl.activate({ _ in + audioSessionControl.activate({ [weak self] _ in Queue.mainQueue().async { - guard let strongSelf = self else { + guard let self else { return } - strongSelf.audioSessionActive.set(.single(true)) + self.audioSessionActive.set(.single(true)) } }) } } else { - strongSelf.audioSessionActive.set(.single(false)) + self.audioSessionActive.set(.single(false)) } } else { - strongSelf.audioSessionActive.set(.single(false)) + self.audioSessionActive.set(.single(false)) } }) if self.sharedAudioContext == nil { self.audioSessionActiveDisposable = (self.audioSessionActive.get() |> deliverOnMainQueue).start(next: { [weak self] value in - if let strongSelf = self { - strongSelf.updateIsAudioSessionActive(value) + if let self { + self.updateIsAudioSessionActive(value) } }) self.audioOutputStateDisposable = (self.audioOutputStatePromise.get() |> deliverOnMainQueue).start(next: { [weak self] availableOutputs, currentOutput in - guard let strongSelf = self else { + guard let self else { return } - strongSelf.updateAudioOutputs(availableOutputs: availableOutputs, currentOutput: currentOutput) + self.updateAudioOutputs(availableOutputs: availableOutputs, currentOutput: currentOutput) }) } } self.groupCallParticipantUpdatesDisposable = (self.account.stateManager.groupCallParticipantUpdates |> deliverOnMainQueue).start(next: { [weak self] updates in - guard let strongSelf = self else { + guard let self else { return } - if case let .established(callInfo, _, _, _, _) = strongSelf.internalState { + if case let .established(callInfo, _, _, _, _) = self.internalState { var removedSsrc: [UInt32] = [] for (callId, update) in updates { if callId == callInfo.id { @@ -1260,15 +1260,15 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { removedSsrc.append(ssrc) } - if participantUpdate.peerId == strongSelf.joinAsPeerId { - if case let .established(_, _, _, ssrc, _) = strongSelf.internalState, ssrc == participantUpdate.ssrc { - strongSelf.markAsCanBeRemoved() + if participantUpdate.peerId == self.joinAsPeerId { + if case let .established(_, _, _, ssrc, _) = self.internalState, ssrc == participantUpdate.ssrc { + self.markAsCanBeRemoved() } } - } else if participantUpdate.peerId == strongSelf.joinAsPeerId { - if case let .established(_, connectionMode, _, ssrc, _) = strongSelf.internalState { + } else if participantUpdate.peerId == self.joinAsPeerId { + if case let .established(_, connectionMode, _, ssrc, _) = self.internalState { if ssrc != participantUpdate.ssrc { - strongSelf.markAsCanBeRemoved() + self.markAsCanBeRemoved() } else if case .broadcast = connectionMode { let canUnmute: Bool if let muteState = participantUpdate.muteState { @@ -1278,23 +1278,23 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } if canUnmute { - strongSelf.requestCall(movingFromBroadcastToRtc: true) + self.requestCall(movingFromBroadcastToRtc: true) } } } } else if case .joined = participantUpdate.participationStatusChange { - } else if let ssrc = participantUpdate.ssrc, strongSelf.ssrcMapping[ssrc] == nil { + } else if let ssrc = participantUpdate.ssrc, self.ssrcMapping[ssrc] == nil { } } case let .call(isTerminated, _, _, _, _, _, _): if isTerminated { - strongSelf.markAsCanBeRemoved() + self.markAsCanBeRemoved() } } } } if !removedSsrc.isEmpty { - if case let .call(callContext) = strongSelf.genericCallContext { + if case let .call(callContext) = self.genericCallContext { callContext.removeSsrcs(ssrcs: removedSsrc) } } @@ -1329,12 +1329,12 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.removedChannelMembersDisposable = (accountContext.peerChannelMemberCategoriesContextsManager.removedChannelMembers |> deliverOnMainQueue).start(next: { [weak self] pairs in - guard let strongSelf = self else { + guard let self else { return } for (channelId, memberId) in pairs { - if channelId == strongSelf.peerId { - strongSelf.removedPeer(memberId) + if channelId == self.peerId { + self.removedPeer(memberId) } } }) @@ -1342,7 +1342,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { if let peerId { let _ = (self.account.postbox.loadedPeerWithId(peerId) |> deliverOnMainQueue).start(next: { [weak self] peer in - guard let strongSelf = self else { + guard let self else { return } var canManageCall = false @@ -1358,11 +1358,11 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } else if (peer.adminRights?.rights.contains(.canManageCalls) == true) { canManageCall = true } - strongSelf.peerUpdatesSubscription = strongSelf.accountContext.account.viewTracker.polledChannel(peerId: peer.id).start() + self.peerUpdatesSubscription = self.accountContext.account.viewTracker.polledChannel(peerId: peer.id).start() } - var updatedValue = strongSelf.stateValue + var updatedValue = self.stateValue updatedValue.canManageCall = canManageCall - strongSelf.stateValue = updatedValue + self.stateValue = updatedValue }) } @@ -1390,13 +1390,13 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.screencastStateDisposable = (screencastIPCContext.isActive |> distinctUntilChanged |> deliverOnMainQueue).start(next: { [weak self] isActive in - guard let strongSelf = self else { + guard let self else { return } if isActive { - strongSelf.requestScreencast() + self.requestScreencast() } else { - strongSelf.disableScreencast() + self.disableScreencast() } }) @@ -1478,7 +1478,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { temporaryParticipantsContext.activeSpeakers ) |> take(1)).start(next: { [weak self] myPeerData, state, activeSpeakers in - guard let strongSelf = self else { + guard let self else { return } @@ -1491,7 +1491,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { loadMoreToken: nil ) - var updatedInvitedPeers = strongSelf.invitedPeersValue + var updatedInvitedPeers = self.invitedPeersValue var didUpdateInvitedPeers = false var participants = state.participants @@ -1518,15 +1518,15 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { ssrc: nil, videoDescription: nil, presentationDescription: nil, - joinTimestamp: strongSelf.temporaryJoinTimestamp, - raiseHandRating: strongSelf.temporaryRaiseHandRating, - hasRaiseHand: strongSelf.temporaryHasRaiseHand, - activityTimestamp: strongSelf.temporaryActivityTimestamp, - activityRank: strongSelf.temporaryActivityRank, - muteState: strongSelf.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false), + joinTimestamp: self.temporaryJoinTimestamp, + raiseHandRating: self.temporaryRaiseHandRating, + hasRaiseHand: self.temporaryHasRaiseHand, + activityTimestamp: self.temporaryActivityTimestamp, + activityRank: self.temporaryActivityRank, + muteState: self.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false), volume: nil, about: about, - joinedVideo: strongSelf.temporaryJoinedVideo + joinedVideo: self.temporaryJoinedVideo )) participants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: state.sortAscending) }) } @@ -1548,22 +1548,22 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { members.totalCount = state.totalCount members.loadMoreToken = state.nextParticipantsFetchOffset - strongSelf.membersValue = members + self.membersValue = members - var stateValue = strongSelf.stateValue - stateValue.myPeerId = strongSelf.joinAsPeerId + var stateValue = self.stateValue + stateValue.myPeerId = self.joinAsPeerId stateValue.adminIds = state.adminIds - strongSelf.stateValue = stateValue + self.stateValue = stateValue - strongSelf.summaryParticipantsState.set(.single(SummaryParticipantsState( + self.summaryParticipantsState.set(.single(SummaryParticipantsState( participantCount: state.totalCount, topParticipants: topParticipants, activeSpeakers: activeSpeakers ))) if didUpdateInvitedPeers { - strongSelf.invitedPeersValue = updatedInvitedPeers + self.invitedPeersValue = updatedInvitedPeers } })) } else { @@ -1571,7 +1571,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.participantsContextStateDisposable.set((myPeerData |> deliverOnMainQueue |> take(1)).start(next: { [weak self] myPeerData in - guard let strongSelf = self else { + guard let self else { return } @@ -1598,15 +1598,15 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { ssrc: nil, videoDescription: nil, presentationDescription: nil, - joinTimestamp: strongSelf.temporaryJoinTimestamp, - raiseHandRating: strongSelf.temporaryRaiseHandRating, - hasRaiseHand: strongSelf.temporaryHasRaiseHand, - activityTimestamp: strongSelf.temporaryActivityTimestamp, - activityRank: strongSelf.temporaryActivityRank, - muteState: strongSelf.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false), + joinTimestamp: self.temporaryJoinTimestamp, + raiseHandRating: self.temporaryRaiseHandRating, + hasRaiseHand: self.temporaryHasRaiseHand, + activityTimestamp: self.temporaryActivityTimestamp, + activityRank: self.temporaryActivityRank, + muteState: self.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false), volume: nil, about: about, - joinedVideo: strongSelf.temporaryJoinedVideo + joinedVideo: self.temporaryJoinedVideo )) } @@ -1618,12 +1618,12 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } } - strongSelf.membersValue = members + self.membersValue = members - var stateValue = strongSelf.stateValue - stateValue.myPeerId = strongSelf.joinAsPeerId + var stateValue = self.stateValue + stateValue.myPeerId = self.joinAsPeerId - strongSelf.stateValue = stateValue + self.stateValue = stateValue })) } } @@ -1756,7 +1756,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { myPeerData, peerView ).start(next: { [weak self] state, adminIds, myPeerData, view in - guard let strongSelf = self else { + guard let self else { return } @@ -1767,8 +1767,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { loadMoreToken: state.nextParticipantsFetchOffset ) - strongSelf.stateValue.adminIds = adminIds - let canManageCall = state.isCreator || strongSelf.stateValue.adminIds.contains(strongSelf.accountContext.account.peerId) + self.stateValue.adminIds = adminIds + let canManageCall = state.isCreator || self.stateValue.adminIds.contains(self.accountContext.account.peerId) var participants: [GroupCallParticipantsContext.Participant] = [] var topParticipants: [GroupCallParticipantsContext.Participant] = [] @@ -1784,15 +1784,15 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { ssrc: nil, videoDescription: nil, presentationDescription: nil, - joinTimestamp: strongSelf.temporaryJoinTimestamp, - raiseHandRating: strongSelf.temporaryRaiseHandRating, - hasRaiseHand: strongSelf.temporaryHasRaiseHand, - activityTimestamp: strongSelf.temporaryActivityTimestamp, - activityRank: strongSelf.temporaryActivityRank, - muteState: strongSelf.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: canManageCall || !state.defaultParticipantsAreMuted.isMuted, mutedByYou: false), + joinTimestamp: self.temporaryJoinTimestamp, + raiseHandRating: self.temporaryRaiseHandRating, + hasRaiseHand: self.temporaryHasRaiseHand, + activityTimestamp: self.temporaryActivityTimestamp, + activityRank: self.temporaryActivityRank, + muteState: self.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: canManageCall || !state.defaultParticipantsAreMuted.isMuted, mutedByYou: false), volume: nil, about: about, - joinedVideo: strongSelf.temporaryJoinedVideo + joinedVideo: self.temporaryJoinedVideo )) } @@ -1804,21 +1804,21 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } } - strongSelf.membersValue = members - strongSelf.stateValue.canManageCall = state.isCreator || adminIds.contains(strongSelf.accountContext.account.peerId) - strongSelf.stateValue.defaultParticipantMuteState = state.defaultParticipantsAreMuted.isMuted ? .muted : .unmuted + self.membersValue = members + self.stateValue.canManageCall = state.isCreator || adminIds.contains(self.accountContext.account.peerId) + self.stateValue.defaultParticipantMuteState = state.defaultParticipantsAreMuted.isMuted ? .muted : .unmuted - strongSelf.stateValue.recordingStartTimestamp = state.recordingStartTimestamp - strongSelf.stateValue.title = state.title - strongSelf.stateValue.muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: canManageCall || !state.defaultParticipantsAreMuted.isMuted, mutedByYou: false) + self.stateValue.recordingStartTimestamp = state.recordingStartTimestamp + self.stateValue.title = state.title + self.stateValue.muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: canManageCall || !state.defaultParticipantsAreMuted.isMuted, mutedByYou: false) - 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, isStream: callInfo.isStream, upgradedPrivateCallId: callInfo.upgradedPrivateCallId)), audioSessionControl: strongSelf.audioSessionControl) + self.stateValue.subscribedToScheduled = state.subscribedToScheduled + self.stateValue.scheduleTimestamp = self.isScheduledStarted ? nil : state.scheduleTimestamp + if state.scheduleTimestamp == nil && !self.isScheduledStarted { + self.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, upgradedPrivateCallId: callInfo.upgradedPrivateCallId)), audioSessionControl: self.audioSessionControl) } else { - strongSelf.summaryInfoState.set(.single(SummaryInfoState(info: GroupCallInfo( + self.summaryInfoState.set(.single(SummaryInfoState(info: GroupCallInfo( id: callInfo.id, accessHash: callInfo.accessHash, participantCount: state.totalCount, @@ -1835,7 +1835,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { upgradedPrivateCallId: callInfo.upgradedPrivateCallId )))) - strongSelf.summaryParticipantsState.set(.single(SummaryParticipantsState( + self.summaryParticipantsState.set(.single(SummaryParticipantsState( participantCount: state.totalCount, topParticipants: topParticipants, activeSpeakers: Set() @@ -1908,14 +1908,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { if self.isStream, self.accountContext.sharedContext.immediateExperimentalUISettings.liveStreamV2 { let externalMediaStream = DirectMediaStreamingContext(id: self.internalId, rejoinNeeded: { [weak self] in Queue.mainQueue().async { - guard let strongSelf = self else { + guard let self else { return } - if strongSelf.leaving { + if self.leaving { return } - if case .established = strongSelf.internalState { - strongSelf.requestCall(movingFromBroadcastToRtc: false) + if case .established = self.internalState { + self.requestCall(movingFromBroadcastToRtc: false) } } }) @@ -1941,27 +1941,27 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { genericCallContext = .call(OngoingGroupCallContext(audioSessionActive: contextAudioSessionActive, video: self.videoCapturer, requestMediaChannelDescriptions: { [weak self] ssrcs, completion in let disposable = MetaDisposable() Queue.mainQueue().async { - guard let strongSelf = self else { + guard let self else { return } - disposable.set(strongSelf.requestMediaChannelDescriptions(ssrcs: ssrcs, completion: completion)) + disposable.set(self.requestMediaChannelDescriptions(ssrcs: ssrcs, completion: completion)) } return disposable }, rejoinNeeded: { [weak self] in Queue.mainQueue().async { - guard let strongSelf = self else { + guard let self else { return } - if case .established = strongSelf.internalState { - strongSelf.requestCall(movingFromBroadcastToRtc: false) + if case .established = self.internalState { + self.requestCall(movingFromBroadcastToRtc: false) } } }, outgoingAudioBitrateKbit: outgoingAudioBitrateKbit, videoContentType: self.isVideoEnabled ? .generic : .none, enableNoiseSuppression: false, disableAudioInput: self.isStream, enableSystemMute: self.accountContext.sharedContext.immediateExperimentalUISettings.experimentalCallMute, preferX264: self.accountContext.sharedContext.immediateExperimentalUISettings.preferredVideoCodec == "H264", logPath: allocateCallLogPath(account: self.account), onMutedSpeechActivityDetected: { [weak self] value in Queue.mainQueue().async { - guard let strongSelf = self else { + guard let self else { return } - strongSelf.onMutedSpeechActivityDetected?(value) + self.onMutedSpeechActivityDetected?(value) } }, encryptionKey: encryptionKey, isConference: self.isConference, isStream: self.isStream, sharedAudioDevice: self.sharedAudioContext?.audioDevice)) } @@ -2028,16 +2028,22 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { return true }) |> deliverOnMainQueue).start(next: { [weak self] joinPayload, ssrc in - guard let strongSelf = self else { + guard let self else { return } let peerAdminIds: Signal<[PeerId], NoError> - let peerId = strongSelf.peerId + let peerId = self.peerId if let peerId { if peerId.namespace == Namespaces.Peer.CloudChannel { - peerAdminIds = Signal { subscriber in - let (disposable, _) = strongSelf.accountContext.peerChannelMemberCategoriesContextsManager.admins(engine: strongSelf.accountContext.engine, postbox: strongSelf.accountContext.account.postbox, network: strongSelf.accountContext.account.network, accountPeerId: strongSelf.accountContext.account.peerId, peerId: peerId, updated: { list in + peerAdminIds = Signal { [weak self] subscriber in + guard let self else { + subscriber.putNext([]) + subscriber.putCompletion() + return EmptyDisposable + } + + let (disposable, _) = self.accountContext.peerChannelMemberCategoriesContextsManager.admins(engine: self.accountContext.engine, postbox: self.accountContext.account.postbox, network: self.accountContext.account.network, accountPeerId: self.accountContext.account.peerId, peerId: peerId, updated: { list in var peerIds = Set() for item in list.list { if let adminInfo = item.participant.adminInfo, adminInfo.rights.rights.contains(.canManageCalls) { @@ -2051,7 +2057,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { |> distinctUntilChanged |> runOn(.mainQueue()) } else { - peerAdminIds = strongSelf.accountContext.engine.data.get( + peerAdminIds = self.accountContext.engine.data.get( TelegramEngine.EngineData.Item.Peer.LegacyGroupParticipants(id: peerId) ) |> map { participants -> [EnginePeer.Id] in @@ -2073,100 +2079,100 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { peerAdminIds = .single([]) } - strongSelf.currentLocalSsrc = ssrc - strongSelf.requestDisposable.set((strongSelf.accountContext.engine.calls.joinGroupCall( - peerId: strongSelf.peerId, - joinAs: strongSelf.joinAsPeerId, + self.currentLocalSsrc = ssrc + self.requestDisposable.set((self.accountContext.engine.calls.joinGroupCall( + peerId: self.peerId, + joinAs: self.joinAsPeerId, callId: callInfo.id, accessHash: callInfo.accessHash, preferMuted: true, joinPayload: joinPayload, peerAdminIds: peerAdminIds, - inviteHash: strongSelf.invite, - keyFingerprint: strongSelf.encryptionKey?.fingerprint + inviteHash: self.invite, + keyFingerprint: self.encryptionKey?.fingerprint ) - |> deliverOnMainQueue).start(next: { joinCallResult in - guard let strongSelf = self else { + |> deliverOnMainQueue).start(next: { [weak self] joinCallResult in + guard let self else { return } let clientParams = joinCallResult.jsonParams if let data = clientParams.data(using: .utf8), let dict = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any] { if let video = dict["video"] as? [String: Any] { if let endpointId = video["endpoint"] as? String { - strongSelf.currentLocalEndpointId = endpointId + self.currentLocalEndpointId = endpointId } } } - strongSelf.ssrcMapping.removeAll() + self.ssrcMapping.removeAll() for participant in joinCallResult.state.participants { if let ssrc = participant.ssrc { - strongSelf.ssrcMapping[ssrc] = SsrcMapping(peerId: participant.peer.id, isPresentation: false) + self.ssrcMapping[ssrc] = SsrcMapping(peerId: participant.peer.id, isPresentation: false) } if let presentationSsrc = participant.presentationDescription?.audioSsrc { - strongSelf.ssrcMapping[presentationSsrc] = SsrcMapping(peerId: participant.peer.id, isPresentation: true) + self.ssrcMapping[presentationSsrc] = SsrcMapping(peerId: participant.peer.id, isPresentation: true) } } - if let genericCallContext = strongSelf.genericCallContext { + if let genericCallContext = self.genericCallContext { switch genericCallContext { case let .call(callContext): switch joinCallResult.connectionMode { case .rtc: - strongSelf.currentConnectionMode = .rtc + self.currentConnectionMode = .rtc callContext.setConnectionMode(.rtc, keepBroadcastConnectedIfWasEnabled: false, isUnifiedBroadcast: false) callContext.setJoinResponse(payload: clientParams) case .broadcast: - strongSelf.currentConnectionMode = .broadcast - callContext.setAudioStreamData(audioStreamData: OngoingGroupCallContext.AudioStreamData(engine: strongSelf.accountContext.engine, callId: callInfo.id, accessHash: callInfo.accessHash, isExternalStream: callInfo.isStream)) + self.currentConnectionMode = .broadcast + callContext.setAudioStreamData(audioStreamData: OngoingGroupCallContext.AudioStreamData(engine: self.accountContext.engine, callId: callInfo.id, accessHash: callInfo.accessHash, isExternalStream: callInfo.isStream)) callContext.setConnectionMode(.broadcast, keepBroadcastConnectedIfWasEnabled: false, isUnifiedBroadcast: callInfo.isStream) } case let .mediaStream(mediaStreamContext): switch joinCallResult.connectionMode { case .rtc: - strongSelf.currentConnectionMode = .rtc + self.currentConnectionMode = .rtc case .broadcast: - strongSelf.currentConnectionMode = .broadcast - mediaStreamContext.setAudioStreamData(audioStreamData: OngoingGroupCallContext.AudioStreamData(engine: strongSelf.accountContext.engine, callId: callInfo.id, accessHash: callInfo.accessHash, isExternalStream: callInfo.isStream)) + self.currentConnectionMode = .broadcast + mediaStreamContext.setAudioStreamData(audioStreamData: OngoingGroupCallContext.AudioStreamData(engine: self.accountContext.engine, callId: callInfo.id, accessHash: callInfo.accessHash, isExternalStream: callInfo.isStream)) } case let .externalMediaStream(externalMediaStream): switch joinCallResult.connectionMode { case .rtc: - strongSelf.currentConnectionMode = .rtc + self.currentConnectionMode = .rtc case .broadcast: - strongSelf.currentConnectionMode = .broadcast - externalMediaStream.setAudioStreamData(audioStreamData: OngoingGroupCallContext.AudioStreamData(engine: strongSelf.accountContext.engine, callId: callInfo.id, accessHash: callInfo.accessHash, isExternalStream: callInfo.isStream)) + self.currentConnectionMode = .broadcast + externalMediaStream.setAudioStreamData(audioStreamData: OngoingGroupCallContext.AudioStreamData(engine: self.accountContext.engine, callId: callInfo.id, accessHash: callInfo.accessHash, isExternalStream: callInfo.isStream)) } } } - strongSelf.updateSessionState(internalState: .established(info: joinCallResult.callInfo, connectionMode: joinCallResult.connectionMode, clientParams: clientParams, localSsrc: ssrc, initialState: joinCallResult.state), audioSessionControl: strongSelf.audioSessionControl) - }, error: { error in - guard let strongSelf = self else { + self.updateSessionState(internalState: .established(info: joinCallResult.callInfo, connectionMode: joinCallResult.connectionMode, clientParams: clientParams, localSsrc: ssrc, initialState: joinCallResult.state), audioSessionControl: self.audioSessionControl) + }, error: { [weak self] error in + guard let self else { return } if case .anonymousNotAllowed = error { - let presentationData = strongSelf.accountContext.sharedContext.currentPresentationData.with { $0 } - strongSelf.accountContext.sharedContext.mainWindow?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: strongSelf.isChannel ? presentationData.strings.LiveStream_AnonymousDisabledAlertText : presentationData.strings.VoiceChat_AnonymousDisabledAlertText, actions: [ + let presentationData = self.accountContext.sharedContext.currentPresentationData.with { $0 } + self.accountContext.sharedContext.mainWindow?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: self.isChannel ? presentationData.strings.LiveStream_AnonymousDisabledAlertText : presentationData.strings.VoiceChat_AnonymousDisabledAlertText, actions: [ TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {}) ]), on: .root, blockInteraction: false, completion: {}) } else if case .tooManyParticipants = error { - let presentationData = strongSelf.accountContext.sharedContext.currentPresentationData.with { $0 } - strongSelf.accountContext.sharedContext.mainWindow?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: strongSelf.isChannel ? presentationData.strings.LiveStream_ChatFullAlertText : presentationData.strings.VoiceChat_ChatFullAlertText, actions: [ + let presentationData = self.accountContext.sharedContext.currentPresentationData.with { $0 } + self.accountContext.sharedContext.mainWindow?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: self.isChannel ? presentationData.strings.LiveStream_ChatFullAlertText : presentationData.strings.VoiceChat_ChatFullAlertText, actions: [ TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {}) ]), on: .root, blockInteraction: false, completion: {}) } else if case .invalidJoinAsPeer = error { - if let peerId = strongSelf.peerId { - let _ = strongSelf.accountContext.engine.calls.clearCachedGroupCallDisplayAsAvailablePeers(peerId: peerId).start() + if let peerId = self.peerId { + let _ = self.accountContext.engine.calls.clearCachedGroupCallDisplayAsAvailablePeers(peerId: peerId).start() } } - strongSelf.markAsCanBeRemoved() + self.markAsCanBeRemoved() })) })) self.networkStateDisposable.set((genericCallContext.networkState |> deliverOnMainQueue).start(next: { [weak self] state in - guard let strongSelf = self else { + guard let self else { return } let mappedState: PresentationGroupCallState.NetworkState @@ -2176,63 +2182,63 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { mappedState = .connecting } - let wasConnecting = strongSelf.stateValue.networkState == .connecting - if strongSelf.stateValue.networkState != mappedState { - strongSelf.stateValue.networkState = mappedState + let wasConnecting = self.stateValue.networkState == .connecting + if self.stateValue.networkState != mappedState { + self.stateValue.networkState = mappedState } let isConnecting = mappedState == .connecting - if strongSelf.isCurrentlyConnecting != isConnecting { - strongSelf.isCurrentlyConnecting = isConnecting + if self.isCurrentlyConnecting != isConnecting { + self.isCurrentlyConnecting = isConnecting if isConnecting { - strongSelf.startCheckingCallIfNeeded() + self.startCheckingCallIfNeeded() } else { - strongSelf.checkCallDisposable?.dispose() - strongSelf.checkCallDisposable = nil + self.checkCallDisposable?.dispose() + self.checkCallDisposable = nil } } - strongSelf.isReconnectingAsSpeaker = state.isTransitioningFromBroadcastToRtc + self.isReconnectingAsSpeaker = state.isTransitioningFromBroadcastToRtc - if (wasConnecting != isConnecting && strongSelf.didConnectOnce) { + if (wasConnecting != isConnecting && self.didConnectOnce) { if isConnecting { - strongSelf.beginTone(tone: .groupConnecting) + self.beginTone(tone: .groupConnecting) } else { - strongSelf.beginTone(tone: nil) + self.beginTone(tone: nil) } } if isConnecting { - strongSelf.didStartConnectingOnce = true + self.didStartConnectingOnce = true } if state.isConnected { - if !strongSelf.didConnectOnce { - strongSelf.didConnectOnce = true + if !self.didConnectOnce { + self.didConnectOnce = true - if !strongSelf.isScheduled { - strongSelf.beginTone(tone: .groupJoined) + if !self.isScheduled { + self.beginTone(tone: .groupJoined) } } - if let peer = strongSelf.reconnectingAsPeer { - strongSelf.reconnectingAsPeer = nil - strongSelf.reconnectedAsEventsPipe.putNext(peer) + if let peer = self.reconnectingAsPeer { + self.reconnectingAsPeer = nil + self.reconnectedAsEventsPipe.putNext(peer) } } })) self.isNoiseSuppressionEnabledDisposable.set((genericCallContext.isNoiseSuppressionEnabled |> deliverOnMainQueue).start(next: { [weak self] value in - guard let strongSelf = self else { + guard let self else { return } - strongSelf.isNoiseSuppressionEnabledPromise.set(value) + self.isNoiseSuppressionEnabledPromise.set(value) })) self.audioLevelsDisposable.set((genericCallContext.audioLevels |> deliverOnMainQueue).start(next: { [weak self] levels in - guard let strongSelf = self else { + guard let self else { return } var result: [(PeerId, UInt32, Float, Bool)] = [] @@ -2245,10 +2251,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { let ssrcValue: UInt32 switch ssrcKey { case .local: - peerId = strongSelf.joinAsPeerId + peerId = self.joinAsPeerId ssrcValue = 0 case let .source(ssrc): - if let mapping = strongSelf.ssrcMapping[ssrc] { + if let mapping = self.ssrcMapping[ssrc] { if mapping.isPresentation { peerId = nil ssrcValue = 0 @@ -2272,16 +2278,16 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } } - strongSelf.speakingParticipantsContext.update(levels: result) + self.speakingParticipantsContext.update(levels: result) let mappedLevel = myLevel * 1.5 - strongSelf.myAudioLevelPipe.putNext(mappedLevel) - strongSelf.myAudioLevelAndSpeakingPipe.putNext((mappedLevel, myLevelHasVoice)) - strongSelf.processMyAudioLevel(level: mappedLevel, hasVoice: myLevelHasVoice) - strongSelf.isSpeakingPromise.set(orignalMyLevelHasVoice) + self.myAudioLevelPipe.putNext(mappedLevel) + self.myAudioLevelAndSpeakingPipe.putNext((mappedLevel, myLevelHasVoice)) + self.processMyAudioLevel(level: mappedLevel, hasVoice: myLevelHasVoice) + self.isSpeakingPromise.set(orignalMyLevelHasVoice) - if !missingSsrcs.isEmpty && !strongSelf.isStream { - strongSelf.participantsContext?.ensureHaveParticipants(ssrcs: missingSsrcs) + if !missingSsrcs.isEmpty && !self.isStream { + self.participantsContext?.ensureHaveParticipants(ssrcs: missingSsrcs) } })) } @@ -2439,13 +2445,13 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { peerView, self.isReconnectingAsSpeakerPromise.get() ).start(next: { [weak self] state, activeSpeakers, speakingParticipants, adminIds, myPeerAndCachedData, chatPeer, view, isReconnectingAsSpeaker in - guard let strongSelf = self else { + guard let self else { return } - let appConfiguration = strongSelf.accountContext.currentAppConfiguration.with({ $0 }) + let appConfiguration = self.accountContext.currentAppConfiguration.with({ $0 }) let configuration = VoiceChatConfiguration.with(appConfiguration: appConfiguration) - strongSelf.participantsContext?.updateAdminIds(adminIds) + self.participantsContext?.updateAdminIds(adminIds) var topParticipants: [GroupCallParticipantsContext.Participant] = [] @@ -2453,20 +2459,23 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { let timestamp = CFAbsoluteTimeGetCurrent() for (peerId, ssrc) in speakingParticipants { let shouldReport: Bool - if let previousTimestamp = strongSelf.speakingParticipantsReportTimestamp[peerId] { + if let previousTimestamp = self.speakingParticipantsReportTimestamp[peerId] { shouldReport = previousTimestamp + 1.0 < timestamp } else { shouldReport = true } if shouldReport { - strongSelf.speakingParticipantsReportTimestamp[peerId] = timestamp + self.speakingParticipantsReportTimestamp[peerId] = timestamp reportSpeakingParticipants[peerId] = ssrc } } if !reportSpeakingParticipants.isEmpty { - Queue.mainQueue().justDispatch { - self?.participantsContext?.reportSpeakingParticipants(ids: reportSpeakingParticipants) + Queue.mainQueue().justDispatch { [weak self] in + guard let self else { + return + } + self.participantsContext?.reportSpeakingParticipants(ids: reportSpeakingParticipants) } } @@ -2477,12 +2486,12 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { loadMoreToken: nil ) - var updatedInvitedPeers = strongSelf.invitedPeersValue + var updatedInvitedPeers = self.invitedPeersValue var didUpdateInvitedPeers = false var participants = state.participants - if let (ignorePeerId, ignoreSsrc) = strongSelf.ignorePreviousJoinAsPeerId { + if let (ignorePeerId, ignoreSsrc) = self.ignorePreviousJoinAsPeerId { for i in 0 ..< participants.count { if participants[i].peer.id == ignorePeerId && participants[i].ssrc == ignoreSsrc { participants.remove(at: i) @@ -2491,7 +2500,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } } - if !participants.contains(where: { $0.peer.id == myPeerId }) && !strongSelf.leaving { + if !participants.contains(where: { $0.peer.id == myPeerId }) && !self.leaving { if let (myPeer, cachedData) = myPeerAndCachedData { let about: String? if let cachedData = cachedData as? CachedUserData { @@ -2507,15 +2516,15 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { ssrc: nil, videoDescription: nil, presentationDescription: nil, - joinTimestamp: strongSelf.temporaryJoinTimestamp, - raiseHandRating: strongSelf.temporaryRaiseHandRating, - hasRaiseHand: strongSelf.temporaryHasRaiseHand, - activityTimestamp: strongSelf.temporaryActivityTimestamp, - activityRank: strongSelf.temporaryActivityRank, - muteState: strongSelf.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false), + joinTimestamp: self.temporaryJoinTimestamp, + raiseHandRating: self.temporaryRaiseHandRating, + hasRaiseHand: self.temporaryHasRaiseHand, + activityTimestamp: self.temporaryActivityTimestamp, + activityRank: self.temporaryActivityRank, + muteState: self.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false), volume: nil, about: about, - joinedVideo: strongSelf.temporaryJoinedVideo + joinedVideo: self.temporaryJoinedVideo )) participants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: state.sortAscending) }) } @@ -2532,13 +2541,13 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } if let ssrc = participant.ssrc { - strongSelf.ssrcMapping[ssrc] = SsrcMapping(peerId: participant.peer.id, isPresentation: false) + self.ssrcMapping[ssrc] = SsrcMapping(peerId: participant.peer.id, isPresentation: false) } if let presentationSsrc = participant.presentationDescription?.audioSsrc { - strongSelf.ssrcMapping[presentationSsrc] = SsrcMapping(peerId: participant.peer.id, isPresentation: true) + self.ssrcMapping[presentationSsrc] = SsrcMapping(peerId: participant.peer.id, isPresentation: true) } - if participant.peer.id == strongSelf.joinAsPeerId { + if participant.peer.id == self.joinAsPeerId { if let (myPeer, cachedData) = myPeerAndCachedData { let about: String? if let cachedData = cachedData as? CachedUserData { @@ -2553,27 +2562,27 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } var filteredMuteState = participant.muteState - if isReconnectingAsSpeaker || strongSelf.currentConnectionMode != .rtc { + if isReconnectingAsSpeaker || self.currentConnectionMode != .rtc { filteredMuteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: false) participant.muteState = filteredMuteState } - let previousRaisedHand = strongSelf.stateValue.raisedHand - if !(strongSelf.stateValue.muteState?.canUnmute ?? false) { - strongSelf.stateValue.raisedHand = participant.hasRaiseHand + let previousRaisedHand = self.stateValue.raisedHand + if !(self.stateValue.muteState?.canUnmute ?? false) { + self.stateValue.raisedHand = participant.hasRaiseHand } if let muteState = participant.muteState, muteState.canUnmute && previousRaisedHand { - let _ = (strongSelf.accountContext.sharedContext.hasGroupCallOnScreen + let _ = (self.accountContext.sharedContext.hasGroupCallOnScreen |> take(1) - |> deliverOnMainQueue).start(next: { hasGroupCallOnScreen in - guard let strongSelf = self else { + |> deliverOnMainQueue).start(next: { [weak self] hasGroupCallOnScreen in + guard let self else { return } - let presentationData = strongSelf.accountContext.sharedContext.currentPresentationData.with { $0 } + let presentationData = self.accountContext.sharedContext.currentPresentationData.with { $0 } if !hasGroupCallOnScreen { let title: String? - if let voiceChatTitle = strongSelf.stateValue.title { + if let voiceChatTitle = self.stateValue.title { title = voiceChatTitle } else if let view, let peer = peerViewMainPeer(view) { title = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) @@ -2587,38 +2596,38 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } else { text = presentationData.strings.VoiceChat_YouCanNowSpeak } - strongSelf.accountContext.sharedContext.mainWindow?.present(UndoOverlayController(presentationData: presentationData, content: .voiceChatCanSpeak(text: text), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return true }), on: .root, blockInteraction: false, completion: {}) - strongSelf.playTone(.unmuted) + self.accountContext.sharedContext.mainWindow?.present(UndoOverlayController(presentationData: presentationData, content: .voiceChatCanSpeak(text: text), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return true }), on: .root, blockInteraction: false, completion: {}) + self.playTone(.unmuted) } }) } if let muteState = filteredMuteState { if muteState.canUnmute { - if let currentMuteState = strongSelf.stateValue.muteState, !currentMuteState.canUnmute { - strongSelf.isMutedValue = .muted(isPushToTalkActive: false) - strongSelf.isMutedPromise.set(strongSelf.isMutedValue) - strongSelf.stateValue.muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false) - strongSelf.genericCallContext?.setIsMuted(true) + if let currentMuteState = self.stateValue.muteState, !currentMuteState.canUnmute { + self.isMutedValue = .muted(isPushToTalkActive: false) + self.isMutedPromise.set(self.isMutedValue) + self.stateValue.muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false) + self.genericCallContext?.setIsMuted(true) } else { - switch strongSelf.isMutedValue { + switch self.isMutedValue { case .muted: break case .unmuted: - let _ = strongSelf.updateMuteState(peerId: strongSelf.joinAsPeerId, isMuted: false) + let _ = self.updateMuteState(peerId: self.joinAsPeerId, isMuted: false) } } } else { - strongSelf.isMutedValue = .muted(isPushToTalkActive: false) - strongSelf.isMutedPromise.set(strongSelf.isMutedValue) - strongSelf.genericCallContext?.setIsMuted(true) - strongSelf.stateValue.muteState = muteState + self.isMutedValue = .muted(isPushToTalkActive: false) + self.isMutedPromise.set(self.isMutedValue) + self.genericCallContext?.setIsMuted(true) + self.stateValue.muteState = muteState } - } else if let currentMuteState = strongSelf.stateValue.muteState, !currentMuteState.canUnmute { - strongSelf.isMutedValue = .muted(isPushToTalkActive: false) - strongSelf.isMutedPromise.set(strongSelf.isMutedValue) - strongSelf.stateValue.muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false) - strongSelf.genericCallContext?.setIsMuted(true) + } else if let currentMuteState = self.stateValue.muteState, !currentMuteState.canUnmute { + self.isMutedValue = .muted(isPushToTalkActive: false) + self.isMutedPromise.set(self.isMutedValue) + self.stateValue.muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false) + self.genericCallContext?.setIsMuted(true) } if participant.joinedVideo { @@ -2627,16 +2636,16 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } else { if let ssrc = participant.ssrc { if let volume = participant.volume { - strongSelf.genericCallContext?.setVolume(ssrc: ssrc, volume: Double(volume) / 10000.0) + self.genericCallContext?.setVolume(ssrc: ssrc, volume: Double(volume) / 10000.0) } else if participant.muteState?.mutedByYou == true { - strongSelf.genericCallContext?.setVolume(ssrc: ssrc, volume: 0.0) + self.genericCallContext?.setVolume(ssrc: ssrc, volume: 0.0) } } if let presentationSsrc = participant.presentationDescription?.audioSsrc { if let volume = participant.volume { - strongSelf.genericCallContext?.setVolume(ssrc: presentationSsrc, volume: Double(volume) / 10000.0) + self.genericCallContext?.setVolume(ssrc: presentationSsrc, volume: Double(volume) / 10000.0) } else if participant.muteState?.mutedByYou == true { - strongSelf.genericCallContext?.setVolume(ssrc: presentationSsrc, volume: 0.0) + self.genericCallContext?.setVolume(ssrc: presentationSsrc, volume: 0.0) } } @@ -2659,21 +2668,21 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { members.totalCount = state.totalCount members.loadMoreToken = state.nextParticipantsFetchOffset - strongSelf.membersValue = members + self.membersValue = members - strongSelf.stateValue.adminIds = adminIds + self.stateValue.adminIds = adminIds - strongSelf.stateValue.canManageCall = state.isCreator || adminIds.contains(strongSelf.accountContext.account.peerId) - if (state.isCreator || strongSelf.stateValue.adminIds.contains(strongSelf.accountContext.account.peerId)) && state.defaultParticipantsAreMuted.canChange { - strongSelf.stateValue.defaultParticipantMuteState = state.defaultParticipantsAreMuted.isMuted ? .muted : .unmuted + self.stateValue.canManageCall = state.isCreator || adminIds.contains(self.accountContext.account.peerId) + if (state.isCreator || self.stateValue.adminIds.contains(self.accountContext.account.peerId)) && state.defaultParticipantsAreMuted.canChange { + self.stateValue.defaultParticipantMuteState = state.defaultParticipantsAreMuted.isMuted ? .muted : .unmuted } - strongSelf.stateValue.recordingStartTimestamp = state.recordingStartTimestamp - strongSelf.stateValue.title = state.title - strongSelf.stateValue.scheduleTimestamp = state.scheduleTimestamp - strongSelf.stateValue.isVideoEnabled = state.isVideoEnabled && otherParticipantsWithVideo < state.unmutedVideoLimit - strongSelf.stateValue.isVideoWatchersLimitReached = videoWatchingParticipants >= configuration.videoParticipantsMaxCount + self.stateValue.recordingStartTimestamp = state.recordingStartTimestamp + self.stateValue.title = state.title + self.stateValue.scheduleTimestamp = state.scheduleTimestamp + self.stateValue.isVideoEnabled = state.isVideoEnabled && otherParticipantsWithVideo < state.unmutedVideoLimit + self.stateValue.isVideoWatchersLimitReached = videoWatchingParticipants >= configuration.videoParticipantsMaxCount - strongSelf.summaryInfoState.set(.single(SummaryInfoState(info: GroupCallInfo( + self.summaryInfoState.set(.single(SummaryInfoState(info: GroupCallInfo( id: callInfo.id, accessHash: callInfo.accessHash, participantCount: state.totalCount, @@ -2690,14 +2699,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { upgradedPrivateCallId: callInfo.upgradedPrivateCallId )))) - strongSelf.summaryParticipantsState.set(.single(SummaryParticipantsState( + self.summaryParticipantsState.set(.single(SummaryParticipantsState( participantCount: state.totalCount, topParticipants: topParticipants, activeSpeakers: activeSpeakers ))) if didUpdateInvitedPeers { - strongSelf.invitedPeersValue = updatedInvitedPeers + self.invitedPeersValue = updatedInvitedPeers } })) @@ -2719,19 +2728,19 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } } |> deliverOnMainQueue).start(next: { [weak self] event in - guard let strongSelf = self, event.peer.id != strongSelf.stateValue.myPeerId else { + guard let self, event.peer.id != self.stateValue.myPeerId else { return } var skip = false - if let participantsCount = strongSelf.participantsContext?.immediateState?.totalCount, participantsCount >= 250 { - if event.peer.isVerified || event.isContact || event.isInChatList || (strongSelf.stateValue.defaultParticipantMuteState == .muted && event.canUnmute) { + if let participantsCount = self.participantsContext?.immediateState?.totalCount, participantsCount >= 250 { + if event.peer.isVerified || event.isContact || event.isInChatList || (self.stateValue.defaultParticipantMuteState == .muted && event.canUnmute) { skip = false } else { skip = true } } if !skip { - strongSelf.memberEventsPipe.putNext(event) + self.memberEventsPipe.putNext(event) } })) @@ -2830,11 +2839,11 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { |> restartIfError |> take(1) |> deliverOnMainQueue).start(completed: { [weak self] in - guard let strongSelf = self else { + guard let self else { return } - strongSelf.checkCallDisposable = nil - strongSelf.requestCall(movingFromBroadcastToRtc: false) + self.checkCallDisposable = nil + self.requestCall(movingFromBroadcastToRtc: false) }) } } @@ -2855,13 +2864,25 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } } if let tone, let toneData = presentationCallToneData(tone) { - self.genericCallContext?.setTone(tone: OngoingGroupCallContext.Tone( - samples: toneData, - sampleRate: 48000, - loopCount: tone.loopCount ?? 100000 - )) + if let sharedAudioContext = self.sharedAudioContext { + sharedAudioContext.audioDevice?.setTone(tone: OngoingCallContext.Tone( + samples: toneData, + sampleRate: 48000, + loopCount: tone.loopCount ?? 100000 + )) + } else { + self.genericCallContext?.setTone(tone: OngoingGroupCallContext.Tone( + samples: toneData, + sampleRate: 48000, + loopCount: tone.loopCount ?? 100000 + )) + } } else { - self.genericCallContext?.setTone(tone: nil) + if let sharedAudioContext = self.sharedAudioContext { + sharedAudioContext.audioDevice?.setTone(tone: nil) + } else { + self.genericCallContext?.setTone(tone: nil) + } } } @@ -2926,45 +2947,45 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } let _ = (self.accountContext.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).start(next: { [weak self] myPeer in - guard let strongSelf = self, let myPeer = myPeer else { + guard let self, let myPeer = myPeer else { return } - let previousPeerId = strongSelf.joinAsPeerId - if let localSsrc = strongSelf.currentLocalSsrc { - strongSelf.ignorePreviousJoinAsPeerId = (previousPeerId, localSsrc) + let previousPeerId = self.joinAsPeerId + if let localSsrc = self.currentLocalSsrc { + self.ignorePreviousJoinAsPeerId = (previousPeerId, localSsrc) } - strongSelf.joinAsPeerId = peerId + self.joinAsPeerId = peerId - if strongSelf.stateValue.scheduleTimestamp != nil { - strongSelf.stateValue.myPeerId = peerId - strongSelf.reconnectedAsEventsPipe.putNext(myPeer) - strongSelf.switchToTemporaryScheduledParticipantsContext() + if self.stateValue.scheduleTimestamp != nil { + self.stateValue.myPeerId = peerId + self.reconnectedAsEventsPipe.putNext(myPeer) + self.switchToTemporaryScheduledParticipantsContext() } else { - strongSelf.disableVideo() - strongSelf.isMutedValue = .muted(isPushToTalkActive: false) - strongSelf.isMutedPromise.set(strongSelf.isMutedValue) + self.disableVideo() + self.isMutedValue = .muted(isPushToTalkActive: false) + self.isMutedPromise.set(self.isMutedValue) - strongSelf.reconnectingAsPeer = myPeer + self.reconnectingAsPeer = myPeer - if let participantsContext = strongSelf.participantsContext, let immediateState = participantsContext.immediateState { + if let participantsContext = self.participantsContext, let immediateState = participantsContext.immediateState { for participant in immediateState.participants { if participant.peer.id == previousPeerId { - strongSelf.temporaryJoinTimestamp = participant.joinTimestamp - strongSelf.temporaryActivityTimestamp = participant.activityTimestamp - strongSelf.temporaryActivityRank = participant.activityRank - strongSelf.temporaryRaiseHandRating = participant.raiseHandRating - strongSelf.temporaryHasRaiseHand = participant.hasRaiseHand - strongSelf.temporaryMuteState = participant.muteState - strongSelf.temporaryJoinedVideo = participant.joinedVideo + self.temporaryJoinTimestamp = participant.joinTimestamp + self.temporaryActivityTimestamp = participant.activityTimestamp + self.temporaryActivityRank = participant.activityRank + self.temporaryRaiseHandRating = participant.raiseHandRating + self.temporaryHasRaiseHand = participant.hasRaiseHand + self.temporaryMuteState = participant.muteState + self.temporaryJoinedVideo = participant.joinedVideo } } - strongSelf.switchToTemporaryParticipantsContext(sourceContext: participantsContext, oldMyPeerId: previousPeerId) + self.switchToTemporaryParticipantsContext(sourceContext: participantsContext, oldMyPeerId: previousPeerId) } else { - strongSelf.stateValue.myPeerId = peerId + self.stateValue.myPeerId = peerId } - strongSelf.requestCall(movingFromBroadcastToRtc: false) + self.requestCall(movingFromBroadcastToRtc: false) } }) } @@ -2975,10 +2996,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { if terminateIfPossible { self.leaveDisposable.set((self.accountContext.engine.calls.stopGroupCall(peerId: self.peerId, callId: callInfo.id, accessHash: callInfo.accessHash) |> deliverOnMainQueue).start(completed: { [weak self] in - guard let strongSelf = self else { + guard let self else { return } - strongSelf.markAsCanBeRemoved() + self.markAsCanBeRemoved() })) } else if let localSsrc = self.currentLocalSsrc { if let contexts = self.accountContext.cachedGroupCallContexts as? AccountGroupCallContextCacheImpl { @@ -3069,13 +3090,13 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.startDisposable.set((self.accountContext.engine.calls.createGroupCall(peerId: peerId, title: nil, scheduleDate: timestamp, isExternalStream: false) |> deliverOnMainQueue).start(next: { [weak self] callInfo in - guard let strongSelf = self else { + guard let self else { return } - strongSelf.updateSessionState(internalState: .active(callInfo), audioSessionControl: strongSelf.audioSessionControl) + self.updateSessionState(internalState: .active(callInfo), audioSessionControl: self.audioSessionControl) }, error: { [weak self] error in - if let strongSelf = self { - strongSelf.markAsCanBeRemoved() + if let self { + self.markAsCanBeRemoved() } })) } @@ -3094,12 +3115,12 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.startDisposable.set((self.accountContext.engine.calls.startScheduledGroupCall(peerId: peerId, callId: callInfo.id, accessHash: callInfo.accessHash) |> deliverOnMainQueue).start(next: { [weak self] callInfo in - guard let strongSelf = self else { + guard let self else { return } - strongSelf.updateSessionState(internalState: .active(callInfo), audioSessionControl: strongSelf.audioSessionControl) + self.updateSessionState(internalState: .active(callInfo), audioSessionControl: self.audioSessionControl) - strongSelf.beginTone(tone: .groupJoined) + self.beginTone(tone: .groupJoined) })) } @@ -3299,11 +3320,11 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.isVideoMutedDisposable.set((videoCapturer.isActive |> distinctUntilChanged |> deliverOnMainQueue).start(next: { [weak self] value in - guard let strongSelf = self else { + guard let self else { return } - strongSelf.isVideoMuted = !value - strongSelf.updateLocalVideoState() + self.isVideoMuted = !value + self.updateLocalVideoState() })) self.updateLocalVideoState() @@ -3342,25 +3363,22 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.screencastJoinDisposable.set((joinPayload |> take(1) |> deliverOnMainQueue).start(next: { [weak self] joinPayload in - guard let strongSelf = self else { + guard let self else { return } - strongSelf.requestDisposable.set((strongSelf.accountContext.engine.calls.joinGroupCallAsScreencast( + self.requestDisposable.set((self.accountContext.engine.calls.joinGroupCallAsScreencast( callId: callInfo.id, accessHash: callInfo.accessHash, joinPayload: joinPayload.0 ) - |> deliverOnMainQueue).start(next: { joinCallResult in - guard let strongSelf = self, let screencastIPCContext = strongSelf.screencastIPCContext else { + |> deliverOnMainQueue).start(next: { [weak self] joinCallResult in + guard let self, let screencastIPCContext = self.screencastIPCContext else { return } screencastIPCContext.setJoinResponse(clientParams: joinCallResult.jsonParams) - }, error: { error in - guard let _ = self else { - return - } + }, error: { _ in })) })) } @@ -3630,16 +3648,16 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.requestDisposable.set((currentOrRequestedCall |> deliverOnMainQueue).start(next: { [weak self] value in - guard let strongSelf = self else { + guard let self else { return } if let value = value { - strongSelf.initialCall = EngineGroupCallDescription(id: value.id, accessHash: value.accessHash, title: value.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: value.isStream) + self.initialCall = EngineGroupCallDescription(id: value.id, accessHash: value.accessHash, title: value.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: value.isStream) - strongSelf.updateSessionState(internalState: .active(value), audioSessionControl: strongSelf.audioSessionControl) + self.updateSessionState(internalState: .active(value), audioSessionControl: self.audioSessionControl) } else { - strongSelf.markAsCanBeRemoved() + self.markAsCanBeRemoved() } })) } @@ -3780,30 +3798,30 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { return } let myAudioLevelTimer = SwiftSignalKit.Timer(timeout: 0.1, repeat: false, completion: { [weak self] in - guard let strongSelf = self else { + guard let self else { return } - strongSelf.myAudioLevelTimer = nil + self.myAudioLevelTimer = nil let timestamp = CACurrentMediaTime() var shouldBeSendingTyping = false - if strongSelf.currentMyAudioLevel > 0.01 && timestamp < strongSelf.currentMyAudioLevelTimestamp + 1.0 { - strongSelf.restartMyAudioLevelTimer() + if self.currentMyAudioLevel > 0.01 && timestamp < self.currentMyAudioLevelTimestamp + 1.0 { + self.restartMyAudioLevelTimer() shouldBeSendingTyping = true } else { - if timestamp < strongSelf.currentMyAudioLevelTimestamp + 1.0 { - strongSelf.restartMyAudioLevelTimer() + if timestamp < self.currentMyAudioLevelTimestamp + 1.0 { + self.restartMyAudioLevelTimer() shouldBeSendingTyping = true } } - if shouldBeSendingTyping != strongSelf.isSendingTyping { - strongSelf.isSendingTyping = shouldBeSendingTyping + if shouldBeSendingTyping != self.isSendingTyping { + self.isSendingTyping = shouldBeSendingTyping if shouldBeSendingTyping { - strongSelf.typingDisposable.set(strongSelf.accountContext.account.acquireLocalInputActivity(peerId: PeerActivitySpace(peerId: peerId, category: .voiceChat), activity: .speakingInGroupCall(timestamp: 0))) - strongSelf.restartMyAudioLevelTimer() + self.typingDisposable.set(self.accountContext.account.acquireLocalInputActivity(peerId: PeerActivitySpace(peerId: peerId, category: .voiceChat), activity: .speakingInGroupCall(timestamp: 0))) + self.restartMyAudioLevelTimer() } else { - strongSelf.typingDisposable.set(nil) + self.typingDisposable.set(nil) } } }, queue: .mainQueue()) @@ -3879,11 +3897,11 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { func getStats() -> Signal { return Signal { [weak self] subscriber in - guard let strongSelf = self else { + guard let self else { subscriber.putCompletion() return EmptyDisposable } - if let genericCallContext = strongSelf.genericCallContext { + if let genericCallContext = self.genericCallContext { genericCallContext.getStats(completion: { stats in subscriber.putNext(stats) subscriber.putCompletion() diff --git a/submodules/TelegramUniversalVideoContent/Sources/HLSVideoJSNativeContentNode.swift b/submodules/TelegramUniversalVideoContent/Sources/HLSVideoJSNativeContentNode.swift index 859df02d41..2115c94488 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/HLSVideoJSNativeContentNode.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/HLSVideoJSNativeContentNode.swift @@ -447,6 +447,7 @@ private final class SharedHLSVideoJSContext: NSObject { } guard let instance = self.contextReferences[instanceId]?.contentNode else { self.contextReferences.removeValue(forKey: instanceId) + self.cleanupContextsIfEmpty() return } guard let eventData = message["data"] as? [String: Any] else { @@ -484,6 +485,10 @@ private final class SharedHLSVideoJSContext: NSObject { self.jsContext = nil } self.isJsContextReady = false + + self.videoElements.removeAll() + self.mediaSources.removeAll() + self.sourceBuffers.removeAll() } private func bridgeInvoke( @@ -848,21 +853,32 @@ private final class SharedHLSVideoJSContext: NSObject { self.jsContext?.evaluateJavaScript("window.hlsPlayer_destroyInstance(\(contextInstanceId));") - if self.contextReferences.isEmpty { - if self.emptyTimer == nil { - self.emptyTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 10.0, repeats: false, block: { [weak self] timer in - guard let self else { - return - } - if self.emptyTimer === timer { - self.emptyTimer = nil - } - if self.contextReferences.isEmpty { - self.disposeJsContext() - } - }) + self.cleanupContextsIfEmpty() + } + } + } + + private func cleanupContextsIfEmpty() { + if self.contextReferences.isEmpty { + if self.emptyTimer == nil { + let disposeTimeout: Double + #if DEBUG + disposeTimeout = 0.5 + #else + disposeTimeout = 10.0 + #endif + + self.emptyTimer = Foundation.Timer.scheduledTimer(withTimeInterval: disposeTimeout, repeats: false, block: { [weak self] timer in + guard let self else { + return } - } + if self.emptyTimer === timer { + self.emptyTimer = nil + } + if self.contextReferences.isEmpty { + self.disposeJsContext() + } + }) } } } @@ -894,6 +910,7 @@ private final class SharedHLSVideoJSContext: NSObject { for (instanceId, urlPrefix) in pendingInitializeInstanceIds { guard let _ = self.contextReferences[instanceId]?.contentNode else { self.contextReferences.removeValue(forKey: instanceId) + self.cleanupContextsIfEmpty() continue } userScriptJs.append("window.hlsPlayer_makeInstance(\(instanceId));\n") diff --git a/submodules/TelegramUniversalVideoContent/Sources/WebViewNativeJSContextImpl.swift b/submodules/TelegramUniversalVideoContent/Sources/WebViewNativeJSContextImpl.swift index bc1ee29454..c31e8a6686 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/WebViewNativeJSContextImpl.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/WebViewNativeJSContextImpl.swift @@ -86,9 +86,14 @@ private var ObjCKey_ContextReference: Int? } deinit { + self.cleanup() + } + + func cleanup() { for (_, timer) in self.timers { timer.invalidate() } + self.timers.removeAll() } func register(jsContext: JSContext) { @@ -175,6 +180,7 @@ final class WebViewNativeJSContextImpl: HLSJSContext { fileprivate final class Impl { let queue: Queue let context: JSContext + let timeoutPolyfill: TimeoutPolyfill let handleScriptMessage: ([String: Any]) -> Void init(queue: Queue, handleScriptMessage: @escaping ([String: Any]) -> Void) { @@ -182,6 +188,8 @@ final class WebViewNativeJSContextImpl: HLSJSContext { self.context = JSContext() self.handleScriptMessage = handleScriptMessage + self.timeoutPolyfill = TimeoutPolyfill(queue: self.queue) + #if DEBUG if #available(iOS 16.4, *) { self.context.isInspectable = true @@ -197,9 +205,8 @@ final class WebViewNativeJSContextImpl: HLSJSContext { } } - let timeoutPolyfill = TimeoutPolyfill(queue: self.queue) - self.context.setObject(timeoutPolyfill, forKeyedSubscript: "_timeoutPolyfill" as (NSCopying & NSObjectProtocol)) - timeoutPolyfill.register(jsContext: self.context) + self.context.setObject(self.timeoutPolyfill, forKeyedSubscript: "_timeoutPolyfill" as (NSCopying & NSObjectProtocol)) + self.timeoutPolyfill.register(jsContext: self.context) self.context.setObject(JsCorePolyfills(queue: self.queue, context: Reference(context: self)), forKeyedSubscript: "_JsCorePolyfills" as (NSCopying & NSObjectProtocol)) @@ -219,6 +226,7 @@ final class WebViewNativeJSContextImpl: HLSJSContext { } deinit { + self.timeoutPolyfill.cleanup() print("WebViewNativeJSContextImpl.deinit") } diff --git a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm index 66d632c72e..b07b39824b 100644 --- a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm +++ b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm @@ -148,6 +148,8 @@ public: } void UpdateAudioCallback(webrtc::AudioTransport *previousAudioCallback, webrtc::AudioTransport *audioCallback) { + _mutex.Lock(); + if (audioCallback) { _audioTransports.push_back(audioCallback); } else if (previousAudioCallback) { @@ -158,6 +160,8 @@ public: } } } + + _mutex.Unlock(); } virtual int32_t RegisterAudioCallback(webrtc::AudioTransport *audioCallback) override { @@ -467,9 +471,10 @@ public: bool keyPressed, uint32_t& newMicLevel ) override { + _mutex.Lock(); if (!_audioTransports.empty()) { for (size_t i = 0; i < _audioTransports.size(); i++) { - auto result = _audioTransports[_audioTransports.size() - 1]->RecordedDataIsAvailable( + _audioTransports[_audioTransports.size() - 1]->RecordedDataIsAvailable( audioSamples, nSamples, nBytesPerSample, @@ -481,15 +486,10 @@ public: keyPressed, newMicLevel ); - if (i == _audioTransports.size() - 1) { - return result; - } } - - return 0; - } else { - return 0; } + _mutex.Unlock(); + return 0; } virtual int32_t RecordedDataIsAvailable( @@ -505,9 +505,10 @@ public: uint32_t& newMicLevel, absl::optional estimatedCaptureTimeNS ) override { + _mutex.Lock(); if (!_audioTransports.empty()) { for (size_t i = _audioTransports.size() - 1; i < _audioTransports.size(); i++) { - auto result = _audioTransports[_audioTransports.size() - 1]->RecordedDataIsAvailable( + _audioTransports[_audioTransports.size() - 1]->RecordedDataIsAvailable( audioSamples, nSamples, nBytesPerSample, @@ -520,14 +521,10 @@ public: newMicLevel, estimatedCaptureTimeNS ); - if (i == _audioTransports.size() - 1) { - return result; - } } - return 0; - } else { - return 0; } + _mutex.Unlock(); + return 0; } // Implementation has to setup safe values for all specified out parameters. @@ -541,8 +538,11 @@ public: int64_t* elapsed_time_ms, int64_t* ntp_time_ms ) override { + _mutex.Lock(); + + int32_t result = 0; if (!_audioTransports.empty()) { - return _audioTransports[_audioTransports.size() - 1]->NeedMorePlayData( + result = _audioTransports[_audioTransports.size() - 1]->NeedMorePlayData( nSamples, nBytesPerSample, nChannels, @@ -554,8 +554,11 @@ public: ); } else { nSamplesOut = 0; - return 0; } + + _mutex.Unlock(); + + return result; } virtual void PullRenderData( @@ -567,6 +570,8 @@ public: int64_t* elapsed_time_ms, int64_t* ntp_time_ms ) override { + _mutex.Lock(); + if (!_audioTransports.empty()) { _audioTransports[_audioTransports.size() - 1]->PullRenderData( bits_per_sample, @@ -578,6 +583,8 @@ public: ntp_time_ms ); } + + _mutex.Unlock(); } public: @@ -612,6 +619,7 @@ public: private: bool _isStarted = false; std::vector _audioTransports; + webrtc::Mutex _mutex; }; class WrappedChildAudioDeviceModule : public tgcalls::DefaultWrappedAudioDeviceModule {