/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once #include "base/weak_ptr.h" #include "base/timer.h" #include "base/bytes.h" #include "mtproto/sender.h" #include "mtproto/mtproto_auth_key.h" class History; namespace tgcalls { class GroupInstanceCustomImpl; struct GroupLevelsUpdate; struct GroupNetworkState; struct GroupParticipantDescription; } // namespace tgcalls namespace base { class GlobalShortcutManager; class GlobalShortcutValue; } // namespace base namespace Webrtc { class MediaDevices; } // namespace Webrtc namespace Data { struct LastSpokeTimes; struct GroupCallParticipant; class GroupCall; } // namespace Data namespace Calls { namespace Group { struct MuteRequest; struct VolumeRequest; struct ParticipantState; struct JoinInfo; struct RejoinEvent; } // namespace Group enum class MuteState { Active, PushToTalk, Muted, ForceMuted, RaisedHand, }; [[nodiscard]] inline auto MapPushToTalkToActive() { return rpl::map([=](MuteState state) { return (state == MuteState::PushToTalk) ? MuteState::Active : state; }); } [[nodiscard]] bool IsGroupCallAdmin( not_null peer, not_null participantPeer); struct LevelUpdate { uint32 ssrc = 0; float value = 0.; bool voice = false; bool me = false; }; class GroupCall final : public base::has_weak_ptr { public: class Delegate { public: virtual ~Delegate() = default; virtual void groupCallFinished(not_null call) = 0; virtual void groupCallFailed(not_null call) = 0; virtual void groupCallRequestPermissionsOrFail( Fn onSuccess) = 0; enum class GroupCallSound { Started, Connecting, AllowedToSpeak, Ended, }; virtual void groupCallPlaySound(GroupCallSound sound) = 0; }; using GlobalShortcutManager = base::GlobalShortcutManager; GroupCall( not_null delegate, Group::JoinInfo info, const MTPInputGroupCall &inputCall); ~GroupCall(); [[nodiscard]] uint64 id() const { return _id; } [[nodiscard]] not_null peer() const { return _peer; } [[nodiscard]] not_null joinAs() const { return _joinAs; } [[nodiscard]] bool showChooseJoinAs() const; [[nodiscard]] TimeId scheduleDate() const { return _scheduleDate; } [[nodiscard]] bool scheduleStartSubscribed() const; [[nodiscard]] Data::GroupCall *lookupReal() const; [[nodiscard]] rpl::producer> real() const; void start(TimeId scheduleDate); void hangup(); void discard(); void rejoinAs(Group::JoinInfo info); void rejoinWithHash(const QString &hash); void join(const MTPInputGroupCall &inputCall); void handleUpdate(const MTPUpdate &update); void handlePossibleCreateOrJoinResponse(const MTPDupdateGroupCall &data); void changeTitle(const QString &title); void toggleRecording(bool enabled, const QString &title); [[nodiscard]] bool recordingStoppedByMe() const { return _recordingStoppedByMe; } void startScheduledNow(); void toggleScheduleStartSubscribed(bool subscribed); void setMuted(MuteState mute); void setMutedAndUpdate(MuteState mute); [[nodiscard]] MuteState muted() const { return _muted.current(); } [[nodiscard]] rpl::producer mutedValue() const { return _muted.value(); } [[nodiscard]] auto otherParticipantStateValue() const -> rpl::producer; enum State { Creating, Waiting, Joining, Connecting, Joined, FailedHangingUp, Failed, HangingUp, Ended, }; [[nodiscard]] State state() const { return _state.current(); } [[nodiscard]] rpl::producer stateValue() const { return _state.value(); } enum class InstanceState { Disconnected, TransitionToRtc, Connected, }; [[nodiscard]] InstanceState instanceState() const { return _instanceState.current(); } [[nodiscard]] rpl::producer instanceStateValue() const { return _instanceState.value(); } [[nodiscard]] rpl::producer levelUpdates() const { return _levelUpdates.events(); } [[nodiscard]] rpl::producer rejoinEvents() const { return _rejoinEvents.events(); } [[nodiscard]] rpl::producer<> allowedToSpeakNotifications() const { return _allowedToSpeakNotifications.events(); } [[nodiscard]] rpl::producer<> titleChanged() const { return _titleChanged.events(); } static constexpr auto kSpeakLevelThreshold = 0.2; void setCurrentAudioDevice(bool input, const QString &deviceId); //void setAudioVolume(bool input, float level); void setAudioDuckingEnabled(bool enabled); void toggleMute(const Group::MuteRequest &data); void changeVolume(const Group::VolumeRequest &data); std::variant> inviteUsers( const std::vector> &users); std::shared_ptr ensureGlobalShortcutManager(); void applyGlobalShortcutChanges(); void pushToTalk(bool pressed, crl::time delay); [[nodiscard]] rpl::lifetime &lifetime() { return _lifetime; } private: class LoadPartTask; public: void broadcastPartStart(std::shared_ptr task); void broadcastPartCancel(not_null task); private: using GlobalShortcutValue = base::GlobalShortcutValue; struct LoadingPart { std::shared_ptr task; mtpRequestId requestId = 0; }; enum class FinishType { None, Ended, Failed, }; enum class InstanceMode { None, Rtc, Stream, }; enum class SendUpdateType { Mute, RaiseHand, }; void handlePossibleCreateOrJoinResponse(const MTPDgroupCall &data); void handlePossibleDiscarded(const MTPDgroupCallDiscarded &data); void handleUpdate(const MTPDupdateGroupCall &data); void handleUpdate(const MTPDupdateGroupCallParticipants &data); void handleRequestError(const MTP::Error &error); void handleControllerError(const QString &error); void ensureControllerCreated(); void destroyController(); void setState(State state); void finish(FinishType type); void maybeSendMutedUpdate(MuteState previous); void sendSelfUpdate(SendUpdateType type); void updateInstanceMuteState(); void updateInstanceVolumes(); void applyMeInCallLocally(); void rejoin(); void rejoin(not_null as); void setJoinAs(not_null as); void saveDefaultJoinAs(not_null as); void subscribeToReal(not_null real); void audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data); void setInstanceConnected(tgcalls::GroupNetworkState networkState); void setInstanceMode(InstanceMode mode); void checkLastSpoke(); void pushToTalkCancel(); void checkGlobalShortcutAvailability(); void checkJoined(); void checkFirstTimeJoined(); void notifyAboutAllowedToSpeak(); void playConnectingSound(); void stopConnectingSound(); void playConnectingSoundOnce(); void requestParticipantsInformation(const std::vector &ssrcs); void addParticipantsToInstance(); void prepareParticipantForAdding( const Data::GroupCallParticipant &participant); void addPreparedParticipants(); void addPreparedParticipantsDelayed(); void editParticipant( not_null participantPeer, bool mute, std::optional volume); void applyParticipantLocally( not_null participantPeer, bool mute, std::optional volume); [[nodiscard]] MTPInputGroupCall inputCall() const; const not_null _delegate; not_null _peer; // Can change in legacy group migration. rpl::event_stream _peerStream; not_null _history; // Can change in legacy group migration. MTP::Sender _api; rpl::event_stream> _realChanges; rpl::variable _state = State::Creating; rpl::variable _instanceState = InstanceState::Disconnected; bool _instanceTransitioning = false; InstanceMode _instanceMode = InstanceMode::None; base::flat_set _unresolvedSsrcs; std::vector _preparedParticipants; bool _addPreparedParticipantsScheduled = false; bool _recordingStoppedByMe = false; MTP::DcId _broadcastDcId = 0; base::flat_map, LoadingPart> _broadcastParts; not_null _joinAs; std::vector> _possibleJoinAs; QString _joinHash; rpl::variable _muted = MuteState::Muted; bool _initialMuteStateSent = false; bool _acceptFields = false; rpl::event_stream _otherParticipantStateValue; uint64 _id = 0; uint64 _accessHash = 0; uint32 _mySsrc = 0; TimeId _scheduleDate = 0; base::flat_set _mySsrcs; mtpRequestId _createRequestId = 0; mtpRequestId _updateMuteRequestId = 0; std::unique_ptr _instance; rpl::event_stream _levelUpdates; base::flat_map _lastSpoke; rpl::event_stream _rejoinEvents; rpl::event_stream<> _allowedToSpeakNotifications; rpl::event_stream<> _titleChanged; base::Timer _lastSpokeCheckTimer; base::Timer _checkJoinedTimer; crl::time _lastSendProgressUpdate = 0; std::shared_ptr _shortcutManager; std::shared_ptr _pushToTalk; base::Timer _pushToTalkCancelTimer; base::Timer _connectingSoundTimer; bool _hadJoinedState = false; std::unique_ptr _mediaDevices; QString _audioInputId; QString _audioOutputId; rpl::lifetime _lifetime; }; } // namespace Calls