/* 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 GroupInstanceImpl; struct GroupLevelsUpdate; } // namespace tgcalls namespace base { class GlobalShortcutManager; class GlobalShortcutValue; } // namespace base namespace Webrtc { class MediaDevices; } // namespace Webrtc namespace Data { struct LastSpokeTimes; } // namespace Data namespace Calls { namespace Group { struct MuteRequest; struct VolumeRequest; struct ParticipantState; } // namespace Group enum class MuteState { Active, PushToTalk, Muted, ForceMuted, }; [[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, Ended, }; virtual void groupCallPlaySound(GroupCallSound sound) = 0; }; using GlobalShortcutManager = base::GlobalShortcutManager; GroupCall( not_null delegate, not_null peer, const MTPInputGroupCall &inputCall, not_null joinAs); ~GroupCall(); [[nodiscard]] uint64 id() const { return _id; } [[nodiscard]] not_null peer() const { return _peer; } [[nodiscard]] not_null joinAs() const { return _joinAs; } void start(); void hangup(); void discard(); void join(const MTPInputGroupCall &inputCall); void handleUpdate(const MTPGroupCall &call); void handleUpdate(const MTPDupdateGroupCallParticipants &data); void setMuted(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, Joining, Connecting, Joined, FailedHangingUp, Failed, HangingUp, Ended, }; [[nodiscard]] State state() const { return _state.current(); } [[nodiscard]] rpl::producer stateValue() const { return _state.value(); } [[nodiscard]] rpl::producer connectingValue() const; [[nodiscard]] rpl::producer levelUpdates() const { return _levelUpdates.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: using GlobalShortcutValue = base::GlobalShortcutValue; enum class FinishType { None, Ended, Failed, }; void handleRequestError(const RPCError &error); void handleControllerError(const QString &error); void createAndStartController(); void destroyController(); void setState(State state); void finish(FinishType type); void maybeSendMutedUpdate(MuteState previous); void sendMutedUpdate(); void updateInstanceMuteState(); void updateInstanceVolumes(); void applyMeInCallLocally(); void rejoin(); void audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data); void setInstanceConnected(bool connected); void checkLastSpoke(); void pushToTalkCancel(); void checkGlobalShortcutAvailability(); void checkJoined(); void playConnectingSound(); void stopConnectingSound(); void playConnectingSoundOnce(); 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. not_null _history; // Can change in legacy group migration. not_null _joinAs; MTP::Sender _api; rpl::variable _state = State::Creating; bool _instanceConnected = false; rpl::variable _muted = MuteState::Muted; bool _acceptFields = false; rpl::event_stream _otherParticipantStateValue; uint64 _id = 0; uint64 _accessHash = 0; uint32 _mySsrc = 0; mtpRequestId _createRequestId = 0; mtpRequestId _updateMuteRequestId = 0; std::unique_ptr _instance; rpl::event_stream _levelUpdates; base::flat_map _lastSpoke; 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