From ea4044e38c626113ffaa364678535392cfac3daf Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 14 May 2020 18:12:07 +0400 Subject: [PATCH] Use TgVoip interface instead of VoIPController. --- Telegram/CMakeLists.txt | 3 + Telegram/SourceFiles/calls/calls_call.cpp | 276 ++++++++---------- Telegram/SourceFiles/calls/calls_call.h | 39 +-- .../SourceFiles/calls/calls_controller.cpp | 31 ++ Telegram/SourceFiles/calls/calls_controller.h | 53 ++++ .../calls/calls_controller_tgvoip.h | 97 ++++++ 6 files changed, 313 insertions(+), 186 deletions(-) create mode 100644 Telegram/SourceFiles/calls/calls_controller.cpp create mode 100644 Telegram/SourceFiles/calls/calls_controller.h create mode 100644 Telegram/SourceFiles/calls/calls_controller_tgvoip.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 1066d68f0d..fd33b51a30 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -264,6 +264,9 @@ PRIVATE calls/calls_box_controller.h calls/calls_call.cpp calls/calls_call.h + calls/calls_controller.cpp + calls/calls_controller.h + calls/calls_controller_tgvoip.h calls/calls_emoji_fingerprint.cpp calls/calls_emoji_fingerprint.h calls/calls_instance.cpp diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp index 732c516c26..511909785c 100644 --- a/Telegram/SourceFiles/calls/calls_call.cpp +++ b/Telegram/SourceFiles/calls/calls_call.cpp @@ -18,23 +18,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/audio/media_audio_track.h" #include "base/platform/base_platform_info.h" #include "calls/calls_panel.h" +#include "calls/calls_controller.h" #include "data/data_user.h" #include "data/data_session.h" #include "facades.h" -#ifdef slots -#undef slots -#define NEED_TO_RESTORE_SLOTS -#endif // slots - -#include -#include - -#ifdef NEED_TO_RESTORE_SLOTS -#define slots Q_SLOTS -#undef NEED_TO_RESTORE_SLOTS -#endif // NEED_TO_RESTORE_SLOTS - namespace Calls { namespace { @@ -43,25 +31,25 @@ constexpr auto kHangupTimeoutMs = 5000; constexpr auto kSha256Size = 32; void AppendEndpoint( - std::vector &list, + std::vector &list, const MTPPhoneConnection &connection) { connection.match([&](const MTPDphoneConnection &data) { if (data.vpeer_tag().v.length() != 16) { return; } - const auto ipv4 = tgvoip::IPv4Address(std::string( - data.vip().v.constData(), - data.vip().v.size())); - const auto ipv6 = tgvoip::IPv6Address(std::string( - data.vipv6().v.constData(), - data.vipv6().v.size())); - list.emplace_back( - (int64_t)data.vid().v, - (uint16_t)data.vport().v, - ipv4, - ipv6, - tgvoip::Endpoint::Type::UDP_RELAY, - (unsigned char*)data.vpeer_tag().v.data()); + auto endpoint = TgVoipEndpoint{ + .endpointId = (int64_t)data.vid().v, + .host = TgVoipEdpointHost{ + .ipv4 = data.vip().v.toStdString(), + .ipv6 = data.vipv6().v.toStdString() }, + .port = (uint16_t)data.vport().v, + .type = TgVoipEndpointType::UdpRelay + }; + const auto tag = data.vpeer_tag().v; + if (tag.size() >= 16) { + memcpy(endpoint.peerTag, tag.data(), 16); + } + list.push_back(std::move(endpoint)); }); } @@ -80,47 +68,25 @@ uint64 ComputeFingerprint(bytes::const_span authKey) { | (gsl::to_integer(hash[12])); } -} // namespace - -void Call::ControllerPointer::create() { - Expects(_data == nullptr); - - _data = std::make_unique(); +[[nodiscard]] std::vector CollectVersions() { + return { TgVoip::getVersion() }; } -void Call::ControllerPointer::reset() { - if (const auto controller = base::take(_data)) { - controller->Stop(); +[[nodiscard]] QVector WrapVersions( + const std::vector &data) { + auto result = QVector(); + result.reserve(data.size()); + for (const auto &version : data) { + result.push_back(MTP_string(version)); } + return result; } -bool Call::ControllerPointer::empty() const { - return (_data == nullptr); +[[nodiscard]] QVector CollectVersionsForApi() { + return WrapVersions(CollectVersions()); } -bool Call::ControllerPointer::operator==(std::nullptr_t) const { - return empty(); -} - -Call::ControllerPointer::operator bool() const { - return !empty(); -} - -tgvoip::VoIPController *Call::ControllerPointer::operator->() const { - Expects(!empty()); - - return _data.get(); -} - -tgvoip::VoIPController &Call::ControllerPointer::operator*() const { - Expects(!empty()); - - return *_data; -} - -Call::ControllerPointer::~ControllerPointer() { - reset(); -} +} // namespace Call::Delegate::~Delegate() = default; @@ -199,8 +165,8 @@ void Call::startOutgoing() { MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p | MTPDphoneCallProtocol::Flag::f_udp_reflector), MTP_int(kMinLayer), - MTP_int(tgvoip::VoIPController::GetConnectionMaxLayer()), - MTP_vector(1, MTP_string("2.4.4"))) + MTP_int(TgVoip::getConnectionMaxLayer()), + MTP_vector(CollectVersionsForApi())) )).done([=](const MTPphone_PhoneCall &result) { Expects(result.type() == mtpc_phone_phoneCall); @@ -278,8 +244,8 @@ void Call::actuallyAnswer() { MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p | MTPDphoneCallProtocol::Flag::f_udp_reflector), MTP_int(kMinLayer), - MTP_int(tgvoip::VoIPController::GetConnectionMaxLayer()), - MTP_vector(1, MTP_string("2.4.4"))) + MTP_int(TgVoip::getConnectionMaxLayer()), + MTP_vector(CollectVersionsForApi())) )).done([=](const MTPphone_PhoneCall &result) { Expects(result.type() == mtpc_phone_phoneCall); auto &call = result.c_phone_phoneCall(); @@ -300,7 +266,7 @@ void Call::actuallyAnswer() { void Call::setMute(bool mute) { _mute = mute; if (_controller) { - _controller->SetMicMute(_mute); + _controller->setMuteMicrophone(_mute); } _muteChanged.notify(_mute); } @@ -334,8 +300,7 @@ void Call::redial() { } QString Call::getDebugLog() const { - const auto debug = _controller->GetDebugString(); - return QString::fromUtf8(debug.data(), debug.size()); + return QString::fromStdString(_controller->getDebugInfo()); } void Call::startWaitingTrack() { @@ -441,7 +406,9 @@ bool Call::handleUpdate(const MTPPhoneCall &call) { return false; } if (data.is_need_debug()) { - auto debugLog = _controller ? _controller->GetDebugLog() : std::string(); + auto debugLog = _controller + ? _controller->getDebugInfo() + : std::string(); if (!debugLog.empty()) { MTP::send( MTPphone_SaveCallDebug( @@ -517,8 +484,8 @@ void Call::confirmAcceptedCall(const MTPDphoneCallAccepted &call) { MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p | MTPDphoneCallProtocol::Flag::f_udp_reflector), MTP_int(kMinLayer), - MTP_int(tgvoip::VoIPController::GetConnectionMaxLayer()), - MTP_vector(1, MTP_string("2.4.4"))) + MTP_int(TgVoip::getConnectionMaxLayer()), + MTP_vector(CollectVersionsForApi())) )).done([this](const MTPphone_PhoneCall &result) { Expects(result.type() == mtpc_phone_phoneCall); @@ -566,126 +533,123 @@ void Call::createAndStartController(const MTPDphoneCall &call) { return; } - tgvoip::VoIPController::Config config; - config.dataSaving = tgvoip::DATA_SAVING_NEVER; + const auto &protocol = call.vprotocol().c_phoneCallProtocol(); + + TgVoipConfig config; + config.dataSaving = TgVoipDataSaving::Never; config.enableAEC = !Platform::IsMac10_7OrGreater(); config.enableNS = true; config.enableAGC = true; config.enableVolumeControl = true; - config.initTimeout = Global::CallConnectTimeoutMs() / 1000; - config.recvTimeout = Global::CallPacketTimeoutMs() / 1000; + config.initializationTimeout = Global::CallConnectTimeoutMs() / 1000.; + config.receiveTimeout = Global::CallPacketTimeoutMs() / 1000.; + config.enableP2P = call.is_p2p_allowed(); + config.maxApiLayer = protocol.vmax_layer().v; if (Logs::DebugEnabled()) { auto callLogFolder = cWorkingDir() + qsl("DebugLogs"); auto callLogPath = callLogFolder + qsl("/last_call_log.txt"); auto callLogNative = QDir::toNativeSeparators(callLogPath); #ifdef Q_OS_WIN - config.logFilePath = callLogNative.toStdWString(); + config.logPath = callLogNative.toStdWString(); #else // Q_OS_WIN const auto callLogUtf = QFile::encodeName(callLogNative); - config.logFilePath.resize(callLogUtf.size()); - ranges::copy(callLogUtf, config.logFilePath.begin()); + config.logPath.resize(callLogUtf.size()); + ranges::copy(callLogUtf, config.logPath.begin()); #endif // Q_OS_WIN QFile(callLogPath).remove(); QDir().mkpath(callLogFolder); } - const auto &protocol = call.vprotocol().c_phoneCallProtocol(); - auto endpoints = std::vector(); + auto endpoints = std::vector(); for (const auto &connection : call.vconnections().v) { AppendEndpoint(endpoints, connection); } - auto callbacks = tgvoip::VoIPController::Callbacks(); - callbacks.connectionStateChanged = []( - tgvoip::VoIPController *controller, - int state) { - const auto call = static_cast(controller->implData); - call->handleControllerStateChange(controller, state); - }; - callbacks.signalBarCountChanged = []( - tgvoip::VoIPController *controller, - int count) { - const auto call = static_cast(controller->implData); - call->handleControllerBarCountChange(controller, count); - }; - - _controller.create(); - if (_mute) { - _controller->SetMicMute(_mute); - } - _controller->implData = static_cast(this); - _controller->SetRemoteEndpoints( - endpoints, - call.is_p2p_allowed(), - protocol.vmax_layer().v); - _controller->SetConfig(config); - _controller->SetCurrentAudioOutput(Global::CallOutputDeviceID().toStdString()); - _controller->SetCurrentAudioInput(Global::CallInputDeviceID().toStdString()); - _controller->SetOutputVolume(Global::CallOutputVolume()/100.0f); - _controller->SetInputVolume(Global::CallInputVolume()/100.0f); -#ifdef Q_OS_MAC - _controller->SetAudioOutputDuckingEnabled(Global::CallAudioDuckingEnabled()); -#endif - _controller->SetEncryptionKey(reinterpret_cast(_authKey.data()), (_type == Type::Outgoing)); - _controller->SetCallbacks(callbacks); + auto proxy = TgVoipProxy(); if (Global::UseProxyForCalls() && (Global::ProxySettings() == MTP::ProxyData::Settings::Enabled)) { - const auto &proxy = Global::SelectedProxy(); - if (proxy.supportsCalls()) { - Assert(proxy.type == MTP::ProxyData::Type::Socks5); - _controller->SetProxy( - tgvoip::PROXY_SOCKS5, - proxy.host.toStdString(), - proxy.port, - proxy.user.toStdString(), - proxy.password.toStdString()); + const auto &selected = Global::SelectedProxy(); + if (selected.supportsCalls()) { + Assert(selected.type == MTP::ProxyData::Type::Socks5); + proxy.host = selected.host.toStdString(); + proxy.port = selected.port; + proxy.login = selected.user.toStdString(); + proxy.password = selected.password.toStdString(); } } - _controller->Start(); - _controller->Connect(); + + auto encryptionKey = TgVoipEncryptionKey(); + encryptionKey.isOutgoing = (_type == Type::Outgoing); + encryptionKey.value = ranges::view::all( + _authKey + ) | ranges::view::transform([](bytes::type byte) { + return static_cast(byte); + }) | ranges::to_vector; + + _controller = MakeController( + "2.4.4", + config, + TgVoipPersistentState(), + endpoints, + proxy.host.empty() ? nullptr : &proxy, + TgVoipNetworkType::Unknown, + encryptionKey); + + const auto raw = _controller.get(); + raw->setOnStateUpdated([=](TgVoipState state) { + handleControllerStateChange(raw, state); + }); + raw->setOnSignalBarsUpdated([=](int count) { + handleControllerBarCountChange(count); + }); + if (_mute) { + raw->setMuteMicrophone(_mute); + } + raw->setAudioOutputDevice( + Global::CallOutputDeviceID().toStdString()); + raw->setAudioInputDevice( + Global::CallInputDeviceID().toStdString()); + raw->setOutputVolume(Global::CallOutputVolume() / 100.0f); + raw->setInputVolume(Global::CallInputVolume() / 100.0f); + raw->setAudioOutputDuckingEnabled(Global::CallAudioDuckingEnabled()); } void Call::handleControllerStateChange( - tgvoip::VoIPController *controller, - int state) { + not_null controller, + TgVoipState state) { // NB! Can be called from an arbitrary thread! // This can be called from ~VoIPController()! - // Expects(controller == _controller.get()); - Expects(controller->implData == static_cast(this)); switch (state) { - case tgvoip::STATE_WAIT_INIT: { + case TgVoipState::WaitInit: { DEBUG_LOG(("Call Info: State changed to WaitingInit.")); setStateQueued(State::WaitingInit); } break; - case tgvoip::STATE_WAIT_INIT_ACK: { + case TgVoipState::WaitInitAck: { DEBUG_LOG(("Call Info: State changed to WaitingInitAck.")); setStateQueued(State::WaitingInitAck); } break; - case tgvoip::STATE_ESTABLISHED: { + case TgVoipState::Established: { DEBUG_LOG(("Call Info: State changed to Established.")); setStateQueued(State::Established); } break; - case tgvoip::STATE_FAILED: { - auto error = controller->GetLastError(); + case TgVoipState::Failed: { + auto error = QString::fromStdString(controller->getLastError()); LOG(("Call Info: State changed to Failed, error: %1.").arg(error)); setFailedQueued(error); } break; - default: LOG(("Call Error: Unexpected state in handleStateChange: %1").arg(state)); + default: LOG(("Call Error: Unexpected state in handleStateChange: %1" + ).arg(int(state))); } } -void Call::handleControllerBarCountChange( - tgvoip::VoIPController *controller, - int count) { +void Call::handleControllerBarCountChange(int count) { // NB! Can be called from an arbitrary thread! // This can be called from ~VoIPController()! - // Expects(controller == _controller.get()); - Expects(controller->implData == static_cast(this)); crl::on_main(this, [=] { setSignalBarCount(count); @@ -790,32 +754,30 @@ void Call::setState(State state) { } } -void Call::setCurrentAudioDevice(bool input, std::string deviceID){ +void Call::setCurrentAudioDevice(bool input, std::string deviceID) { if (_controller) { if (input) { - _controller->SetCurrentAudioInput(deviceID); + _controller->setAudioInputDevice(deviceID); } else { - _controller->SetCurrentAudioOutput(deviceID); + _controller->setAudioOutputDevice(deviceID); } } } -void Call::setAudioVolume(bool input, float level){ +void Call::setAudioVolume(bool input, float level) { if (_controller) { - if(input) { - _controller->SetInputVolume(level); + if (input) { + _controller->setInputVolume(level); } else { - _controller->SetOutputVolume(level); + _controller->setOutputVolume(level); } } } -void Call::setAudioDuckingEnabled(bool enabled){ -#ifdef Q_OS_MAC +void Call::setAudioDuckingEnabled(bool enabled) { if (_controller) { - _controller->SetAudioOutputDuckingEnabled(enabled); + _controller->setAudioOutputDuckingEnabled(enabled); } -#endif } void Call::finish(FinishType type, const MTPPhoneCallDiscardReason &reason) { @@ -844,7 +806,7 @@ void Call::finish(FinishType type, const MTPPhoneCallDiscardReason &reason) { setState(hangupState); auto duration = getDurationMs() / 1000; - auto connectionId = _controller ? _controller->GetPreferredRelayID() : 0; + auto connectionId = _controller ? _controller->getPreferredRelayId() : 0; _finishByTimeoutTimer.call(kHangupTimeoutMs, [this, finalState] { setState(finalState); }); _api.request(MTPphone_DiscardCall( MTP_flags(0), @@ -870,7 +832,7 @@ void Call::setStateQueued(State state) { }); } -void Call::setFailedQueued(int error) { +void Call::setFailedQueued(const QString &error) { crl::on_main(this, [=] { handleControllerError(error); }); @@ -887,13 +849,13 @@ void Call::handleRequestError(const RPCError &error) { finish(FinishType::Failed); } -void Call::handleControllerError(int error) { - if (error == tgvoip::ERROR_INCOMPATIBLE) { +void Call::handleControllerError(const QString &error) { + if (error == u"ERROR_INCOMPATIBLE"_q) { Ui::show(Box( Lang::Hard::CallErrorIncompatible().replace( "{user}", _user->name))); - } else if (error == tgvoip::ERROR_AUDIO_IO) { + } else if (error == u"ERROR_AUDIO_IO"_q) { Ui::show(Box(tr::lng_call_error_audio_io(tr::now))); } finish(FinishType::Failed); @@ -912,8 +874,8 @@ Call::~Call() { destroyController(); } -void UpdateConfig(const std::string& data) { - tgvoip::ServerConfig::GetSharedInstance()->Update(data); +void UpdateConfig(const std::string &data) { + TgVoip::setGlobalServerConfig(data); } } // namespace Calls diff --git a/Telegram/SourceFiles/calls/calls_call.h b/Telegram/SourceFiles/calls/calls_call.h index 98bc6b5a66..2f91c9fe1b 100644 --- a/Telegram/SourceFiles/calls/calls_call.h +++ b/Telegram/SourceFiles/calls/calls_call.h @@ -19,12 +19,12 @@ class Track; } // namespace Audio } // namespace Media -namespace tgvoip { -class VoIPController; -} // namespace tgvoip +enum class TgVoipState; namespace Calls { +class Controller; + struct DhConfig { int32 version = 0; int32 g = 0; @@ -129,30 +129,13 @@ public: ~Call(); private: - class ControllerPointer { - public: - void create(); - void reset(); - bool empty() const; - - bool operator==(std::nullptr_t) const; - explicit operator bool() const; - tgvoip::VoIPController *operator->() const; - tgvoip::VoIPController &operator*() const; - - ~ControllerPointer(); - - private: - std::unique_ptr _data; - - }; enum class FinishType { None, Ended, Failed, }; void handleRequestError(const RPCError &error); - void handleControllerError(int error); + void handleControllerError(const QString &error); void finish(FinishType type, const MTPPhoneCallDiscardReason &reason = MTP_phoneCallDiscardReasonDisconnect()); void startOutgoing(); void startIncoming(); @@ -160,11 +143,9 @@ private: void generateModExpFirst(bytes::const_span randomSeed); void handleControllerStateChange( - tgvoip::VoIPController *controller, - int state); - void handleControllerBarCountChange( - tgvoip::VoIPController *controller, - int count); + not_null controller, + TgVoipState state); + void handleControllerBarCountChange(int count); void createAndStartController(const MTPDphoneCall &call); template @@ -177,7 +158,7 @@ private: void startConfirmedCall(const MTPDphoneCall &call); void setState(State state); void setStateQueued(State state); - void setFailedQueued(int error); + void setFailedQueued(const QString &error); void setSignalBarCount(int count); void destroyController(); @@ -210,12 +191,12 @@ private: uint64 _accessHash = 0; uint64 _keyFingerprint = 0; - ControllerPointer _controller; + std::unique_ptr _controller; std::unique_ptr _waitingTrack; }; -void UpdateConfig(const std::string& data); +void UpdateConfig(const std::string &data); } // namespace Calls diff --git a/Telegram/SourceFiles/calls/calls_controller.cpp b/Telegram/SourceFiles/calls/calls_controller.cpp new file mode 100644 index 0000000000..d20f65e5dd --- /dev/null +++ b/Telegram/SourceFiles/calls/calls_controller.cpp @@ -0,0 +1,31 @@ +/* +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 +*/ +#include "calls/calls_controller.h" + +#include "calls/calls_controller_tgvoip.h" + +namespace Calls { + +[[nodiscard]] std::unique_ptr MakeController( + const std::string &version, + const TgVoipConfig &config, + const TgVoipPersistentState &persistentState, + const std::vector &endpoints, + const TgVoipProxy *proxy, + TgVoipNetworkType initialNetworkType, + const TgVoipEncryptionKey &encryptionKey) { + return std::make_unique( + config, + persistentState, + endpoints, + proxy, + initialNetworkType, + encryptionKey); +} + +} // namespace Calls diff --git a/Telegram/SourceFiles/calls/calls_controller.h b/Telegram/SourceFiles/calls/calls_controller.h new file mode 100644 index 0000000000..9c98d91be8 --- /dev/null +++ b/Telegram/SourceFiles/calls/calls_controller.h @@ -0,0 +1,53 @@ +/* +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 "TgVoip.h" + +namespace Calls { + +class Controller { +public: + virtual ~Controller() = default; + + [[nodiscard]] virtual std::string version() = 0; + + virtual void setNetworkType(TgVoipNetworkType networkType) = 0; + virtual void setMuteMicrophone(bool muteMicrophone) = 0; + virtual void setAudioOutputGainControlEnabled(bool enabled) = 0; + virtual void setEchoCancellationStrength(int strength) = 0; + virtual void setAudioInputDevice(std::string id) = 0; + virtual void setAudioOutputDevice(std::string id) = 0; + virtual void setInputVolume(float level) = 0; + virtual void setOutputVolume(float level) = 0; + virtual void setAudioOutputDuckingEnabled(bool enabled) = 0; + + virtual std::string getLastError() = 0; + virtual std::string getDebugInfo() = 0; + virtual int64_t getPreferredRelayId() = 0; + virtual TgVoipTrafficStats getTrafficStats() = 0; + virtual TgVoipPersistentState getPersistentState() = 0; + + virtual void setOnStateUpdated(Fn onStateUpdated) = 0; + virtual void setOnSignalBarsUpdated( + Fn onSignalBarsUpdated) = 0; + + virtual TgVoipFinalState stop() = 0; + +}; + +[[nodiscard]] std::unique_ptr MakeController( + const std::string &version, + const TgVoipConfig &config, + const TgVoipPersistentState &persistentState, + const std::vector &endpoints, + const TgVoipProxy *proxy, + TgVoipNetworkType initialNetworkType, + const TgVoipEncryptionKey &encryptionKey); + +} // namespace Calls diff --git a/Telegram/SourceFiles/calls/calls_controller_tgvoip.h b/Telegram/SourceFiles/calls/calls_controller_tgvoip.h new file mode 100644 index 0000000000..ebcb2bdd1f --- /dev/null +++ b/Telegram/SourceFiles/calls/calls_controller_tgvoip.h @@ -0,0 +1,97 @@ +/* +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 "calls/calls_controller.h" + +namespace Calls { + +class TgVoipController final : public Controller { +public: + TgVoipController( + const TgVoipConfig &config, + const TgVoipPersistentState &persistentState, + const std::vector &endpoints, + const TgVoipProxy *proxy, + TgVoipNetworkType initialNetworkType, + const TgVoipEncryptionKey &encryptionKey) + : _impl(TgVoip::makeInstance( + config, + persistentState, + endpoints, + proxy, + initialNetworkType, + encryptionKey)) { + } + + [[nodiscard]] static std::string Version() { + return TgVoip::getVersion(); + } + + [[nodiscard]] std::string version() override { + return Version(); + } + void setNetworkType(TgVoipNetworkType networkType) override { + _impl->setNetworkType(networkType); + } + void setMuteMicrophone(bool muteMicrophone) override { + _impl->setMuteMicrophone(muteMicrophone); + } + void setAudioOutputGainControlEnabled(bool enabled) override { + _impl->setAudioOutputGainControlEnabled(enabled); + } + void setEchoCancellationStrength(int strength) override { + _impl->setEchoCancellationStrength(strength); + } + void setAudioInputDevice(std::string id) override { + _impl->setAudioInputDevice(id); + } + void setAudioOutputDevice(std::string id) override { + _impl->setAudioOutputDevice(id); + } + void setInputVolume(float level) override { + _impl->setInputVolume(level); + } + void setOutputVolume(float level) override { + _impl->setOutputVolume(level); + } + void setAudioOutputDuckingEnabled(bool enabled) override { + _impl->setAudioOutputDuckingEnabled(enabled); + } + std::string getLastError() override { + return _impl->getLastError(); + } + std::string getDebugInfo() override { + return _impl->getDebugInfo(); + } + int64_t getPreferredRelayId() override { + return _impl->getPreferredRelayId(); + } + TgVoipTrafficStats getTrafficStats() override { + return _impl->getTrafficStats(); + } + TgVoipPersistentState getPersistentState() override { + return _impl->getPersistentState(); + } + void setOnStateUpdated(Fn onStateUpdated) override { + _impl->setOnStateUpdated(std::move(onStateUpdated)); + } + void setOnSignalBarsUpdated( + Fn onSignalBarsUpdated) override { + _impl->setOnSignalBarsUpdated(std::move(onSignalBarsUpdated)); + } + TgVoipFinalState stop() override { + return _impl->stop(); + } + +private: + const std::unique_ptr _impl; + +}; + +} // namespace Calls