| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470 |
- /*
- 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_call.h"
- #include "apiwrap.h"
- #include "base/openssl_help.h"
- #include "base/platform/base_platform_info.h"
- #include "base/random.h"
- #include "boxes/abstract_box.h"
- #include "calls/calls_instance.h"
- #include "calls/calls_panel.h"
- #include "core/application.h"
- #include "core/core_settings.h"
- #include "data/data_session.h"
- #include "data/data_user.h"
- #include "lang/lang_keys.h"
- #include "main/main_app_config.h"
- #include "main/main_session.h"
- #include "media/audio/media_audio_track.h"
- #include "mtproto/mtproto_config.h"
- #include "mtproto/mtproto_dh_utils.h"
- #include "ui/boxes/confirm_box.h"
- #include "ui/boxes/rate_call_box.h"
- #include "webrtc/webrtc_create_adm.h"
- #include "webrtc/webrtc_environment.h"
- #include "webrtc/webrtc_video_track.h"
- #include "window/window_controller.h"
- #include <tgcalls/Instance.h>
- #include <tgcalls/VideoCaptureInterface.h>
- #include <tgcalls/StaticThreads.h>
- namespace tgcalls {
- class InstanceImpl;
- class InstanceV2Impl;
- class InstanceV2ReferenceImpl;
- class InstanceImplLegacy;
- void SetLegacyGlobalServerConfig(const std::string &serverConfig);
- } // namespace tgcalls
- namespace Calls {
- namespace {
- constexpr auto kMinLayer = 65;
- constexpr auto kHangupTimeoutMs = 5000;
- constexpr auto kSha256Size = 32;
- constexpr auto kAuthKeySize = 256;
- const auto kDefaultVersion = "2.4.4"_q;
- const auto Register = tgcalls::Register<tgcalls::InstanceImpl>();
- const auto RegisterV2 = tgcalls::Register<tgcalls::InstanceV2Impl>();
- const auto RegV2Ref = tgcalls::Register<tgcalls::InstanceV2ReferenceImpl>();
- const auto RegisterLegacy = tgcalls::Register<tgcalls::InstanceImplLegacy>();
- [[nodiscard]] base::flat_set<int64> CollectEndpointIds(
- const QVector<MTPPhoneConnection> &list) {
- auto result = base::flat_set<int64>();
- result.reserve(list.size());
- for (const auto &connection : list) {
- connection.match([&](const MTPDphoneConnection &data) {
- result.emplace(int64(data.vid().v));
- }, [](const MTPDphoneConnectionWebrtc &) {
- });
- }
- return result;
- }
- void AppendEndpoint(
- std::vector<tgcalls::Endpoint> &list,
- const MTPPhoneConnection &connection) {
- connection.match([&](const MTPDphoneConnection &data) {
- if (data.vpeer_tag().v.length() != 16 || data.is_tcp()) {
- return;
- }
- tgcalls::Endpoint endpoint = {
- .endpointId = (int64_t)data.vid().v,
- .host = tgcalls::EndpointHost{
- .ipv4 = data.vip().v.toStdString(),
- .ipv6 = data.vipv6().v.toStdString() },
- .port = (uint16_t)data.vport().v,
- .type = tgcalls::EndpointType::UdpRelay,
- };
- const auto tag = data.vpeer_tag().v;
- if (tag.size() >= 16) {
- memcpy(endpoint.peerTag, tag.data(), 16);
- }
- list.push_back(std::move(endpoint));
- }, [&](const MTPDphoneConnectionWebrtc &data) {
- });
- }
- void AppendServer(
- std::vector<tgcalls::RtcServer> &list,
- const MTPPhoneConnection &connection,
- const base::flat_set<int64> &ids) {
- connection.match([&](const MTPDphoneConnection &data) {
- const auto hex = [](const QByteArray &value) {
- const auto digit = [](uchar c) {
- return char((c < 10) ? ('0' + c) : ('a' + c - 10));
- };
- auto result = std::string();
- result.reserve(value.size() * 2);
- for (const auto ch : value) {
- result += digit(uchar(ch) / 16);
- result += digit(uchar(ch) % 16);
- }
- return result;
- };
- const auto host = data.vip().v;
- const auto hostv6 = data.vipv6().v;
- const auto port = uint16_t(data.vport().v);
- const auto username = std::string("reflector");
- const auto password = hex(data.vpeer_tag().v);
- const auto i = ids.find(int64(data.vid().v));
- Assert(i != end(ids));
- const auto id = uint8_t((i - begin(ids)) + 1);
- const auto pushTurn = [&](const QString &host) {
- list.push_back(tgcalls::RtcServer{
- .id = id,
- .host = host.toStdString(),
- .port = port,
- .login = username,
- .password = password,
- .isTurn = true,
- .isTcp = data.is_tcp(),
- });
- };
- pushTurn(host);
- pushTurn(hostv6);
- }, [&](const MTPDphoneConnectionWebrtc &data) {
- const auto host = qs(data.vip());
- const auto hostv6 = qs(data.vipv6());
- const auto port = uint16_t(data.vport().v);
- if (data.is_stun()) {
- const auto pushStun = [&](const QString &host) {
- if (host.isEmpty()) {
- return;
- }
- list.push_back(tgcalls::RtcServer{
- .host = host.toStdString(),
- .port = port,
- .isTurn = false
- });
- };
- pushStun(host);
- pushStun(hostv6);
- }
- const auto username = qs(data.vusername());
- const auto password = qs(data.vpassword());
- if (data.is_turn() && !username.isEmpty() && !password.isEmpty()) {
- const auto pushTurn = [&](const QString &host) {
- list.push_back(tgcalls::RtcServer{
- .host = host.toStdString(),
- .port = port,
- .login = username.toStdString(),
- .password = password.toStdString(),
- .isTurn = true,
- });
- };
- pushTurn(host);
- pushTurn(hostv6);
- }
- });
- }
- constexpr auto kFingerprintDataSize = 256;
- uint64 ComputeFingerprint(bytes::const_span authKey) {
- Expects(authKey.size() == kFingerprintDataSize);
- auto hash = openssl::Sha1(authKey);
- return (gsl::to_integer<uint64>(hash[19]) << 56)
- | (gsl::to_integer<uint64>(hash[18]) << 48)
- | (gsl::to_integer<uint64>(hash[17]) << 40)
- | (gsl::to_integer<uint64>(hash[16]) << 32)
- | (gsl::to_integer<uint64>(hash[15]) << 24)
- | (gsl::to_integer<uint64>(hash[14]) << 16)
- | (gsl::to_integer<uint64>(hash[13]) << 8)
- | (gsl::to_integer<uint64>(hash[12]));
- }
- [[nodiscard]] QVector<MTPstring> WrapVersions(
- const std::vector<std::string> &data) {
- return ranges::views::all(
- data
- ) | ranges::views::transform([=](const std::string &string) {
- return MTP_string(string);
- }) | ranges::to<QVector<MTPstring>>;
- }
- [[nodiscard]] QVector<MTPstring> CollectVersionsForApi() {
- return WrapVersions(tgcalls::Meta::Versions() | ranges::actions::reverse);
- }
- [[nodiscard]] Webrtc::VideoState StartVideoState(bool enabled) {
- using State = Webrtc::VideoState;
- return enabled ? State::Active : State::Inactive;
- }
- } // namespace
- Call::Call(
- not_null<Delegate*> delegate,
- not_null<UserData*> user,
- Type type,
- bool video)
- : _delegate(delegate)
- , _user(user)
- , _api(&_user->session().mtp())
- , _type(type)
- , _discardByTimeoutTimer([=] { hangup(); })
- , _playbackDeviceId(
- &Core::App().mediaDevices(),
- Webrtc::DeviceType::Playback,
- Webrtc::DeviceIdValueWithFallback(
- Core::App().settings().callPlaybackDeviceIdValue(),
- Core::App().settings().playbackDeviceIdValue()))
- , _captureDeviceId(
- &Core::App().mediaDevices(),
- Webrtc::DeviceType::Capture,
- Webrtc::DeviceIdValueWithFallback(
- Core::App().settings().callCaptureDeviceIdValue(),
- Core::App().settings().captureDeviceIdValue()))
- , _cameraDeviceId(
- &Core::App().mediaDevices(),
- Webrtc::DeviceType::Camera,
- Core::App().settings().cameraDeviceIdValue())
- , _videoIncoming(
- std::make_unique<Webrtc::VideoTrack>(
- StartVideoState(video)))
- , _videoOutgoing(
- std::make_unique<Webrtc::VideoTrack>(
- StartVideoState(video))) {
- if (_type == Type::Outgoing) {
- setState(State::WaitingUserConfirmation);
- } else {
- const auto &config = _user->session().serverConfig();
- _discardByTimeoutTimer.callOnce(config.callRingTimeoutMs);
- startWaitingTrack();
- }
- setupMediaDevices();
- setupOutgoingVideo();
- }
- void Call::generateModExpFirst(bytes::const_span randomSeed) {
- auto first = MTP::CreateModExp(_dhConfig.g, _dhConfig.p, randomSeed);
- if (first.modexp.empty()) {
- LOG(("Call Error: Could not compute mod-exp first."));
- finish(FinishType::Failed);
- return;
- }
- _randomPower = std::move(first.randomPower);
- if (_type == Type::Incoming) {
- _gb = std::move(first.modexp);
- } else {
- _ga = std::move(first.modexp);
- _gaHash = openssl::Sha256(_ga);
- }
- }
- bool Call::isIncomingWaiting() const {
- if (type() != Call::Type::Incoming) {
- return false;
- }
- return (state() == State::Starting)
- || (state() == State::WaitingIncoming);
- }
- void Call::start(bytes::const_span random) {
- // Save config here, because it is possible that it changes between
- // different usages inside the same call.
- _dhConfig = _delegate->getDhConfig();
- Assert(_dhConfig.g != 0);
- Assert(!_dhConfig.p.empty());
- generateModExpFirst(random);
- const auto state = _state.current();
- if (state == State::Starting || state == State::Requesting) {
- if (_type == Type::Outgoing) {
- startOutgoing();
- } else {
- startIncoming();
- }
- } else if (state == State::ExchangingKeys
- && _answerAfterDhConfigReceived) {
- answer();
- }
- }
- void Call::startOutgoing() {
- Expects(_type == Type::Outgoing);
- Expects(_state.current() == State::Requesting);
- Expects(_gaHash.size() == kSha256Size);
- const auto flags = _videoCapture
- ? MTPphone_RequestCall::Flag::f_video
- : MTPphone_RequestCall::Flag(0);
- _api.request(MTPphone_RequestCall(
- MTP_flags(flags),
- _user->inputUser,
- MTPInputGroupCall(),
- MTP_int(base::RandomValue<int32>()),
- MTP_bytes(_gaHash),
- MTP_phoneCallProtocol(
- MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p
- | MTPDphoneCallProtocol::Flag::f_udp_reflector),
- MTP_int(kMinLayer),
- MTP_int(tgcalls::Meta::MaxLayer()),
- MTP_vector(CollectVersionsForApi()))
- )).done([=](const MTPphone_PhoneCall &result) {
- Expects(result.type() == mtpc_phone_phoneCall);
- setState(State::Waiting);
- const auto &call = result.c_phone_phoneCall();
- _user->session().data().processUsers(call.vusers());
- if (call.vphone_call().type() != mtpc_phoneCallWaiting) {
- LOG(("Call Error: Expected phoneCallWaiting in response to "
- "phone.requestCall()"));
- finish(FinishType::Failed);
- return;
- }
- const auto &phoneCall = call.vphone_call();
- const auto &waitingCall = phoneCall.c_phoneCallWaiting();
- _id = waitingCall.vid().v;
- _accessHash = waitingCall.vaccess_hash().v;
- if (_finishAfterRequestingCall != FinishType::None) {
- if (_finishAfterRequestingCall == FinishType::Failed) {
- finish(_finishAfterRequestingCall);
- } else {
- hangup();
- }
- return;
- }
- const auto &config = _user->session().serverConfig();
- _discardByTimeoutTimer.callOnce(config.callReceiveTimeoutMs);
- handleUpdate(phoneCall);
- }).fail([this](const MTP::Error &error) {
- handleRequestError(error.type());
- }).send();
- }
- void Call::startIncoming() {
- Expects(_type == Type::Incoming);
- Expects(_state.current() == State::Starting);
- _api.request(MTPphone_ReceivedCall(
- MTP_inputPhoneCall(MTP_long(_id), MTP_long(_accessHash))
- )).done([=] {
- if (_state.current() == State::Starting) {
- setState(State::WaitingIncoming);
- }
- }).fail([=](const MTP::Error &error) {
- handleRequestError(error.type());
- }).send();
- }
- void Call::applyUserConfirmation() {
- if (_state.current() == State::WaitingUserConfirmation) {
- setState(State::Requesting);
- }
- }
- void Call::answer() {
- const auto video = isSharingVideo();
- _delegate->callRequestPermissionsOrFail(crl::guard(this, [=] {
- actuallyAnswer();
- }), video);
- }
- void Call::actuallyAnswer() {
- Expects(_type == Type::Incoming);
- const auto state = _state.current();
- if (state != State::Starting && state != State::WaitingIncoming) {
- if (state != State::ExchangingKeys
- || !_answerAfterDhConfigReceived) {
- return;
- }
- }
- setState(State::ExchangingKeys);
- if (_gb.empty()) {
- _answerAfterDhConfigReceived = true;
- return;
- } else {
- _answerAfterDhConfigReceived = false;
- }
- _api.request(MTPphone_AcceptCall(
- MTP_inputPhoneCall(MTP_long(_id), MTP_long(_accessHash)),
- MTP_bytes(_gb),
- MTP_phoneCallProtocol(
- MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p
- | MTPDphoneCallProtocol::Flag::f_udp_reflector),
- MTP_int(kMinLayer),
- MTP_int(tgcalls::Meta::MaxLayer()),
- MTP_vector(CollectVersionsForApi()))
- )).done([=](const MTPphone_PhoneCall &result) {
- Expects(result.type() == mtpc_phone_phoneCall);
- const auto &call = result.c_phone_phoneCall();
- _user->session().data().processUsers(call.vusers());
- if (call.vphone_call().type() != mtpc_phoneCallWaiting) {
- LOG(("Call Error: "
- "Not phoneCallWaiting in response to phone.acceptCall."));
- finish(FinishType::Failed);
- return;
- }
- handleUpdate(call.vphone_call());
- }).fail([=](const MTP::Error &error) {
- handleRequestError(error.type());
- }).send();
- }
- void Call::captureMuteChanged(bool mute) {
- setMuted(mute);
- }
- rpl::producer<Webrtc::DeviceResolvedId> Call::captureMuteDeviceId() {
- return _captureDeviceId.value();
- }
- void Call::setMuted(bool mute) {
- _muted = mute;
- if (_instance) {
- _instance->setMuteMicrophone(mute);
- }
- }
- void Call::setupMediaDevices() {
- _playbackDeviceId.changes() | rpl::filter([=] {
- return _instance && _setDeviceIdCallback;
- }) | rpl::start_with_next([=](const Webrtc::DeviceResolvedId &deviceId) {
- _setDeviceIdCallback(deviceId);
- // Value doesn't matter here, just trigger reading of the new value.
- _instance->setAudioOutputDevice(deviceId.value.toStdString());
- }, _lifetime);
- _captureDeviceId.changes() | rpl::filter([=] {
- return _instance && _setDeviceIdCallback;
- }) | rpl::start_with_next([=](const Webrtc::DeviceResolvedId &deviceId) {
- _setDeviceIdCallback(deviceId);
- // Value doesn't matter here, just trigger reading of the new value.
- _instance->setAudioInputDevice(deviceId.value.toStdString());
- }, _lifetime);
- }
- void Call::setupOutgoingVideo() {
- const auto cameraId = [] {
- return Core::App().mediaDevices().defaultId(
- Webrtc::DeviceType::Camera);
- };
- const auto started = _videoOutgoing->state();
- if (cameraId().isEmpty()) {
- _videoOutgoing->setState(Webrtc::VideoState::Inactive);
- }
- _videoOutgoing->stateValue(
- ) | rpl::start_with_next([=](Webrtc::VideoState state) {
- if (state != Webrtc::VideoState::Inactive
- && cameraId().isEmpty()
- && !_videoCaptureIsScreencast) {
- _errors.fire({ ErrorType::NoCamera });
- _videoOutgoing->setState(Webrtc::VideoState::Inactive);
- } else if (_state.current() != State::Established
- && (state != Webrtc::VideoState::Inactive)
- && (started == Webrtc::VideoState::Inactive)) {
- _errors.fire({ ErrorType::NotStartedCall });
- _videoOutgoing->setState(Webrtc::VideoState::Inactive);
- } else if (state != Webrtc::VideoState::Inactive
- && _instance
- && !_instance->supportsVideo()) {
- _errors.fire({ ErrorType::NotVideoCall });
- _videoOutgoing->setState(Webrtc::VideoState::Inactive);
- } else if (state != Webrtc::VideoState::Inactive) {
- // Paused not supported right now.
- Assert(state == Webrtc::VideoState::Active);
- if (!_videoCapture) {
- _videoCapture = _delegate->callGetVideoCapture(
- _videoCaptureDeviceId,
- _videoCaptureIsScreencast);
- _videoCapture->setOutput(_videoOutgoing->sink());
- }
- _videoCapture->setState(tgcalls::VideoState::Active);
- if (_instance) {
- _instance->setVideoCapture(_videoCapture);
- }
- } else if (_videoCapture) {
- _videoCapture->setState(tgcalls::VideoState::Inactive);
- if (_instance) {
- _instance->setVideoCapture(nullptr);
- }
- }
- }, _lifetime);
- _cameraDeviceId.changes(
- ) | rpl::filter([=] {
- return !_videoCaptureIsScreencast;
- }) | rpl::start_with_next([=](Webrtc::DeviceResolvedId deviceId) {
- const auto &id = deviceId.value;
- _videoCaptureDeviceId = id;
- if (_videoCapture) {
- _videoCapture->switchToDevice(id.toStdString(), false);
- if (_instance) {
- _instance->sendVideoDeviceUpdated();
- }
- }
- }, _lifetime);
- }
- not_null<Webrtc::VideoTrack*> Call::videoIncoming() const {
- return _videoIncoming.get();
- }
- not_null<Webrtc::VideoTrack*> Call::videoOutgoing() const {
- return _videoOutgoing.get();
- }
- crl::time Call::getDurationMs() const {
- return _startTime ? (crl::now() - _startTime) : 0;
- }
- void Call::hangup() {
- const auto state = _state.current();
- if (state == State::Busy) {
- _delegate->callFinished(this);
- } else {
- const auto missed = (state == State::Ringing
- || (state == State::Waiting && _type == Type::Outgoing));
- const auto declined = isIncomingWaiting();
- const auto reason = missed
- ? MTP_phoneCallDiscardReasonMissed()
- : declined
- ? MTP_phoneCallDiscardReasonBusy()
- : MTP_phoneCallDiscardReasonHangup();
- finish(FinishType::Ended, reason);
- }
- }
- void Call::redial() {
- if (_state.current() != State::Busy) {
- return;
- }
- Assert(_instance == nullptr);
- _type = Type::Outgoing;
- setState(State::Requesting);
- _answerAfterDhConfigReceived = false;
- startWaitingTrack();
- _delegate->callRedial(this);
- }
- QString Call::getDebugLog() const {
- return _instance
- ? QString::fromStdString(_instance->getDebugInfo())
- : QString();
- }
- void Call::startWaitingTrack() {
- _waitingTrack = Media::Audio::Current().createTrack();
- const auto trackFileName = Core::App().settings().getSoundPath(
- (_type == Type::Outgoing)
- ? u"call_outgoing"_q
- : u"call_incoming"_q);
- _waitingTrack->samplePeakEach(kSoundSampleMs);
- _waitingTrack->fillFromFile(trackFileName);
- _waitingTrack->playInLoop();
- }
- void Call::sendSignalingData(const QByteArray &data) {
- _api.request(MTPphone_SendSignalingData(
- MTP_inputPhoneCall(
- MTP_long(_id),
- MTP_long(_accessHash)),
- MTP_bytes(data)
- )).done([=](const MTPBool &result) {
- if (!mtpIsTrue(result)) {
- finish(FinishType::Failed);
- }
- }).fail([=](const MTP::Error &error) {
- handleRequestError(error.type());
- }).send();
- }
- float64 Call::getWaitingSoundPeakValue() const {
- if (_waitingTrack) {
- const auto when = crl::now() + kSoundSampleMs / 4;
- return _waitingTrack->getPeakValue(when);
- }
- return 0.;
- }
- bool Call::isKeyShaForFingerprintReady() const {
- return (_keyFingerprint != 0);
- }
- bytes::vector Call::getKeyShaForFingerprint() const {
- Expects(isKeyShaForFingerprintReady());
- Expects(!_ga.empty());
- auto encryptedChatAuthKey = bytes::vector(
- _authKey.size() + _ga.size(),
- gsl::byte{});
- bytes::copy(
- gsl::make_span(encryptedChatAuthKey).subspan(0, _authKey.size()),
- _authKey);
- bytes::copy(
- gsl::make_span(encryptedChatAuthKey).subspan(
- _authKey.size(),
- _ga.size()),
- _ga);
- return openssl::Sha256(encryptedChatAuthKey);
- }
- bool Call::handleUpdate(const MTPPhoneCall &call) {
- switch (call.type()) {
- case mtpc_phoneCallRequested: {
- const auto &data = call.c_phoneCallRequested();
- if (_type != Type::Incoming
- || _id != 0
- || peerToUser(_user->id) != UserId(data.vadmin_id())) {
- Unexpected("phoneCallRequested call inside an existing call "
- "handleUpdate()");
- }
- if (_user->session().userId() != UserId(data.vparticipant_id())) {
- LOG(("Call Error: Wrong call participant_id %1, expected %2."
- ).arg(data.vparticipant_id().v
- ).arg(_user->session().userId().bare));
- finish(FinishType::Failed);
- return true;
- }
- _id = data.vid().v;
- _accessHash = data.vaccess_hash().v;
- const auto gaHashBytes = bytes::make_span(data.vg_a_hash().v);
- if (gaHashBytes.size() != kSha256Size) {
- LOG(("Call Error: Wrong g_a_hash size %1, expected %2."
- ).arg(gaHashBytes.size()
- ).arg(kSha256Size));
- finish(FinishType::Failed);
- return true;
- }
- _gaHash = bytes::make_vector(gaHashBytes);
- } return true;
- case mtpc_phoneCallEmpty: {
- const auto &data = call.c_phoneCallEmpty();
- if (data.vid().v != _id) {
- return false;
- }
- LOG(("Call Error: phoneCallEmpty received."));
- finish(FinishType::Failed);
- } return true;
- case mtpc_phoneCallWaiting: {
- const auto &data = call.c_phoneCallWaiting();
- if (data.vid().v != _id) {
- return false;
- }
- if (_type == Type::Outgoing
- && _state.current() == State::Waiting
- && data.vreceive_date().value_or_empty() != 0) {
- const auto &config = _user->session().serverConfig();
- _discardByTimeoutTimer.callOnce(config.callRingTimeoutMs);
- setState(State::Ringing);
- startWaitingTrack();
- }
- } return true;
- case mtpc_phoneCall: {
- const auto &data = call.c_phoneCall();
- if (data.vid().v != _id) {
- return false;
- }
- if (_type == Type::Incoming
- && _state.current() == State::ExchangingKeys
- && !_instance) {
- startConfirmedCall(data);
- }
- } return true;
- case mtpc_phoneCallDiscarded: {
- const auto &data = call.c_phoneCallDiscarded();
- if (data.vid().v != _id) {
- return false;
- }
- if (data.is_need_debug()) {
- const auto debugLog = _instance
- ? _instance->getDebugInfo()
- : std::string();
- if (!debugLog.empty()) {
- user()->session().api().request(MTPphone_SaveCallDebug(
- MTP_inputPhoneCall(
- MTP_long(_id),
- MTP_long(_accessHash)),
- MTP_dataJSON(MTP_string(debugLog))
- )).send();
- }
- }
- if (data.is_need_rating() && _id && _accessHash) {
- const auto window = Core::App().windowFor(
- Window::SeparateId(_user));
- const auto session = &_user->session();
- const auto callId = _id;
- const auto callAccessHash = _accessHash;
- auto owned = Box<Ui::RateCallBox>(
- Core::App().settings().sendSubmitWay());
- const auto box = window
- ? window->show(std::move(owned))
- : Ui::show(std::move(owned));
- const auto sender = box->lifetime().make_state<MTP::Sender>(
- &session->mtp());
- box->sends(
- ) | rpl::take(
- 1 // Instead of keeping requestId.
- ) | rpl::start_with_next([=](const Ui::RateCallBox::Result &r) {
- sender->request(MTPphone_SetCallRating(
- MTP_flags(0),
- MTP_inputPhoneCall(
- MTP_long(callId),
- MTP_long(callAccessHash)),
- MTP_int(r.rating),
- MTP_string(r.comment)
- )).done([=](const MTPUpdates &updates) {
- session->api().applyUpdates(updates);
- box->closeBox();
- }).fail([=] {
- box->closeBox();
- }).send();
- }, box->lifetime());
- }
- const auto reason = data.vreason();
- if (reason
- && reason->type() == mtpc_phoneCallDiscardReasonDisconnect) {
- LOG(("Call Info: Discarded with DISCONNECT reason."));
- }
- if (reason && reason->type() == mtpc_phoneCallDiscardReasonBusy) {
- setState(State::Busy);
- } else if (_type == Type::Outgoing
- || _state.current() == State::HangingUp) {
- setState(State::Ended);
- } else {
- setState(State::EndedByOtherDevice);
- }
- } return true;
- case mtpc_phoneCallAccepted: {
- const auto &data = call.c_phoneCallAccepted();
- if (data.vid().v != _id) {
- return false;
- }
- if (_type != Type::Outgoing) {
- LOG(("Call Error: "
- "Unexpected phoneCallAccepted for an incoming call."));
- finish(FinishType::Failed);
- } else if (checkCallFields(data)) {
- confirmAcceptedCall(data);
- }
- } return true;
- }
- Unexpected("phoneCall type inside an existing call handleUpdate()");
- }
- void Call::updateRemoteMediaState(
- tgcalls::AudioState audio,
- tgcalls::VideoState video) {
- _remoteAudioState = [&] {
- using From = tgcalls::AudioState;
- using To = RemoteAudioState;
- switch (audio) {
- case From::Active: return To::Active;
- case From::Muted: return To::Muted;
- }
- Unexpected("Audio state in remoteMediaStateUpdated.");
- }();
- _videoIncoming->setState([&] {
- using From = tgcalls::VideoState;
- using To = Webrtc::VideoState;
- switch (video) {
- case From::Inactive: return To::Inactive;
- case From::Paused: return To::Paused;
- case From::Active: return To::Active;
- }
- Unexpected("Video state in remoteMediaStateUpdated.");
- }());
- }
- bool Call::handleSignalingData(
- const MTPDupdatePhoneCallSignalingData &data) {
- if (data.vphone_call_id().v != _id || !_instance) {
- return false;
- }
- auto prepared = ranges::views::all(
- data.vdata().v
- ) | ranges::views::transform([](char byte) {
- return static_cast<uint8_t>(byte);
- }) | ranges::to_vector;
- _instance->receiveSignalingData(std::move(prepared));
- return true;
- }
- void Call::confirmAcceptedCall(const MTPDphoneCallAccepted &call) {
- Expects(_type == Type::Outgoing);
- if (_state.current() == State::ExchangingKeys
- || _instance) {
- LOG(("Call Warning: Unexpected confirmAcceptedCall."));
- return;
- }
- const auto firstBytes = bytes::make_span(call.vg_b().v);
- const auto computedAuthKey = MTP::CreateAuthKey(
- firstBytes,
- _randomPower,
- _dhConfig.p);
- if (computedAuthKey.empty()) {
- LOG(("Call Error: Could not compute mod-exp final."));
- finish(FinishType::Failed);
- return;
- }
- MTP::AuthKey::FillData(_authKey, computedAuthKey);
- _keyFingerprint = ComputeFingerprint(_authKey);
- setState(State::ExchangingKeys);
- _api.request(MTPphone_ConfirmCall(
- MTP_inputPhoneCall(MTP_long(_id), MTP_long(_accessHash)),
- MTP_bytes(_ga),
- MTP_long(_keyFingerprint),
- MTP_phoneCallProtocol(
- MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p
- | MTPDphoneCallProtocol::Flag::f_udp_reflector),
- MTP_int(kMinLayer),
- MTP_int(tgcalls::Meta::MaxLayer()),
- MTP_vector(CollectVersionsForApi()))
- )).done([=](const MTPphone_PhoneCall &result) {
- Expects(result.type() == mtpc_phone_phoneCall);
- const auto &call = result.c_phone_phoneCall();
- _user->session().data().processUsers(call.vusers());
- if (call.vphone_call().type() != mtpc_phoneCall) {
- LOG(("Call Error: Expected phoneCall in response to "
- "phone.confirmCall()"));
- finish(FinishType::Failed);
- return;
- }
- createAndStartController(call.vphone_call().c_phoneCall());
- }).fail([=](const MTP::Error &error) {
- handleRequestError(error.type());
- }).send();
- }
- void Call::startConfirmedCall(const MTPDphoneCall &call) {
- Expects(_type == Type::Incoming);
- const auto firstBytes = bytes::make_span(call.vg_a_or_b().v);
- if (_gaHash != openssl::Sha256(firstBytes)) {
- LOG(("Call Error: Wrong g_a hash received."));
- finish(FinishType::Failed);
- return;
- }
- _ga = bytes::vector(firstBytes.begin(), firstBytes.end());
- const auto computedAuthKey = MTP::CreateAuthKey(
- firstBytes,
- _randomPower,
- _dhConfig.p);
- if (computedAuthKey.empty()) {
- LOG(("Call Error: Could not compute mod-exp final."));
- finish(FinishType::Failed);
- return;
- }
- MTP::AuthKey::FillData(_authKey, computedAuthKey);
- _keyFingerprint = ComputeFingerprint(_authKey);
- createAndStartController(call);
- }
- void Call::createAndStartController(const MTPDphoneCall &call) {
- _discardByTimeoutTimer.cancel();
- if (!checkCallFields(call) || _authKey.size() != kAuthKeySize) {
- return;
- }
- const auto &protocol = call.vprotocol().c_phoneCallProtocol();
- const auto &serverConfig = _user->session().serverConfig();
- auto encryptionKeyValue = std::make_shared<std::array<
- uint8_t,
- kAuthKeySize>>();
- memcpy(encryptionKeyValue->data(), _authKey.data(), kAuthKeySize);
- const auto version = call.vprotocol().match([&](
- const MTPDphoneCallProtocol &data) {
- return data.vlibrary_versions().v;
- }).value(0, MTP_bytes(kDefaultVersion)).v;
- LOG(("Call Info: Creating instance with version '%1', allowP2P: %2").arg(
- QString::fromUtf8(version),
- Logs::b(call.is_p2p_allowed())));
- const auto versionString = version.toStdString();
- const auto &settings = Core::App().settings();
- const auto weak = base::make_weak(this);
- _setDeviceIdCallback = nullptr;
- const auto playbackDeviceIdInitial = _playbackDeviceId.current();
- const auto captureDeviceIdInitial = _captureDeviceId.current();
- const auto saveSetDeviceIdCallback = [=](
- Fn<void(Webrtc::DeviceResolvedId)> setDeviceIdCallback) {
- setDeviceIdCallback(playbackDeviceIdInitial);
- setDeviceIdCallback(captureDeviceIdInitial);
- crl::on_main(weak, [=] {
- _setDeviceIdCallback = std::move(setDeviceIdCallback);
- const auto playback = _playbackDeviceId.current();
- if (_instance && playback != playbackDeviceIdInitial) {
- _setDeviceIdCallback(playback);
- // Value doesn't matter here, just trigger reading of the...
- _instance->setAudioOutputDevice(
- playback.value.toStdString());
- }
- const auto capture = _captureDeviceId.current();
- if (_instance && capture != captureDeviceIdInitial) {
- _setDeviceIdCallback(capture);
- // Value doesn't matter here, just trigger reading of the...
- _instance->setAudioInputDevice(capture.value.toStdString());
- }
- });
- };
- tgcalls::Descriptor descriptor = {
- .version = versionString,
- .config = tgcalls::Config{
- .initializationTimeout
- = serverConfig.callConnectTimeoutMs / 1000.,
- .receiveTimeout = serverConfig.callPacketTimeoutMs / 1000.,
- .dataSaving = tgcalls::DataSaving::Never,
- .enableP2P = call.is_p2p_allowed(),
- .enableAEC = false,
- .enableNS = true,
- .enableAGC = true,
- .enableVolumeControl = true,
- .maxApiLayer = protocol.vmax_layer().v,
- },
- .encryptionKey = tgcalls::EncryptionKey(
- std::move(encryptionKeyValue),
- (_type == Type::Outgoing)),
- .mediaDevicesConfig = tgcalls::MediaDevicesConfig{
- .audioInputId = captureDeviceIdInitial.value.toStdString(),
- .audioOutputId = playbackDeviceIdInitial.value.toStdString(),
- .inputVolume = 1.f,//settings.callInputVolume() / 100.f,
- .outputVolume = 1.f,//settings.callOutputVolume() / 100.f,
- },
- .videoCapture = _videoCapture,
- .stateUpdated = [=](tgcalls::State state) {
- crl::on_main(weak, [=] {
- handleControllerStateChange(state);
- });
- },
- .signalBarsUpdated = [=](int count) {
- crl::on_main(weak, [=] {
- handleControllerBarCountChange(count);
- });
- },
- .remoteBatteryLevelIsLowUpdated = [=](bool isLow) {
- #ifdef _DEBUG
- // isLow = true;
- #endif
- crl::on_main(weak, [=] {
- _remoteBatteryState = isLow
- ? RemoteBatteryState::Low
- : RemoteBatteryState::Normal;
- });
- },
- .remoteMediaStateUpdated = [=](
- tgcalls::AudioState audio,
- tgcalls::VideoState video) {
- crl::on_main(weak, [=] {
- updateRemoteMediaState(audio, video);
- });
- },
- .signalingDataEmitted = [=](const std::vector<uint8_t> &data) {
- const auto bytes = QByteArray(
- reinterpret_cast<const char*>(data.data()),
- data.size());
- crl::on_main(weak, [=] {
- sendSignalingData(bytes);
- });
- },
- .createAudioDeviceModule = Webrtc::AudioDeviceModuleCreator(
- saveSetDeviceIdCallback),
- };
- if (Logs::DebugEnabled()) {
- const auto callLogFolder = cWorkingDir() + u"DebugLogs"_q;
- const auto callLogPath = callLogFolder + u"/last_call_log.txt"_q;
- const auto callLogNative = QDir::toNativeSeparators(callLogPath);
- #ifdef Q_OS_WIN
- descriptor.config.logPath.data = callLogNative.toStdWString();
- #else // Q_OS_WIN
- const auto callLogUtf = QFile::encodeName(callLogNative);
- descriptor.config.logPath.data.resize(callLogUtf.size());
- ranges::copy(callLogUtf, descriptor.config.logPath.data.begin());
- #endif // Q_OS_WIN
- QFile(callLogPath).remove();
- QDir().mkpath(callLogFolder);
- }
- const auto ids = CollectEndpointIds(call.vconnections().v);
- for (const auto &connection : call.vconnections().v) {
- AppendEndpoint(descriptor.endpoints, connection);
- }
- for (const auto &connection : call.vconnections().v) {
- AppendServer(descriptor.rtcServers, connection, ids);
- }
- {
- const auto &settingsProxy = Core::App().settings().proxy();
- using ProxyData = MTP::ProxyData;
- if (settingsProxy.useProxyForCalls() && settingsProxy.isEnabled()) {
- const auto &selected = settingsProxy.selected();
- if (selected.supportsCalls() && !selected.host.isEmpty()) {
- Assert(selected.type == ProxyData::Type::Socks5);
- descriptor.proxy = std::make_unique<tgcalls::Proxy>();
- descriptor.proxy->host = selected.host.toStdString();
- descriptor.proxy->port = selected.port;
- descriptor.proxy->login = selected.user.toStdString();
- descriptor.proxy->password = selected.password.toStdString();
- }
- }
- }
- _instance = tgcalls::Meta::Create(versionString, std::move(descriptor));
- if (!_instance) {
- LOG(("Call Error: Wrong library version: %1."
- ).arg(QString::fromUtf8(version)));
- finish(FinishType::Failed);
- return;
- }
- const auto raw = _instance.get();
- if (_muted.current()) {
- raw->setMuteMicrophone(_muted.current());
- }
- raw->setIncomingVideoOutput(_videoIncoming->sink());
- raw->setAudioOutputDuckingEnabled(settings.callAudioDuckingEnabled());
- _state.value() | rpl::start_with_next([=](State state) {
- const auto track = (state != State::FailedHangingUp)
- && (state != State::Failed)
- && (state != State::HangingUp)
- && (state != State::Ended)
- && (state != State::EndedByOtherDevice)
- && (state != State::Busy);
- Core::App().mediaDevices().setCaptureMuteTracker(this, track);
- }, _instanceLifetime);
- _muted.value() | rpl::start_with_next([=](bool muted) {
- Core::App().mediaDevices().setCaptureMuted(muted);
- }, _instanceLifetime);
- #if 0
- Core::App().batterySaving().value(
- ) | rpl::start_with_next([=](bool isSaving) {
- crl::on_main(weak, [=] {
- if (_instance) {
- _instance->setIsLowBatteryLevel(isSaving);
- }
- });
- }, _instanceLifetime);
- #endif
- }
- void Call::handleControllerStateChange(tgcalls::State state) {
- switch (state) {
- case tgcalls::State::WaitInit: {
- DEBUG_LOG(("Call Info: State changed to WaitingInit."));
- setState(State::WaitingInit);
- } break;
- case tgcalls::State::WaitInitAck: {
- DEBUG_LOG(("Call Info: State changed to WaitingInitAck."));
- setState(State::WaitingInitAck);
- } break;
- case tgcalls::State::Established: {
- DEBUG_LOG(("Call Info: State changed to Established."));
- setState(State::Established);
- } break;
- case tgcalls::State::Failed: {
- const auto error = _instance
- ? QString::fromStdString(_instance->getLastError())
- : QString();
- LOG(("Call Info: State changed to Failed, error: %1.").arg(error));
- handleControllerError(error);
- } break;
- default: LOG(("Call Error: Unexpected state in handleStateChange: %1"
- ).arg(int(state)));
- }
- }
- void Call::handleControllerBarCountChange(int count) {
- setSignalBarCount(count);
- }
- void Call::setSignalBarCount(int count) {
- _signalBarCount = count;
- }
- template <typename T>
- bool Call::checkCallCommonFields(const T &call) {
- const auto checkFailed = [this] {
- finish(FinishType::Failed);
- return false;
- };
- if (call.vaccess_hash().v != _accessHash) {
- LOG(("Call Error: Wrong call access_hash."));
- return checkFailed();
- }
- const auto adminId = (_type == Type::Outgoing)
- ? _user->session().userId()
- : peerToUser(_user->id);
- const auto participantId = (_type == Type::Outgoing)
- ? peerToUser(_user->id)
- : _user->session().userId();
- if (UserId(call.vadmin_id()) != adminId) {
- LOG(("Call Error: Wrong call admin_id %1, expected %2.")
- .arg(call.vadmin_id().v)
- .arg(adminId.bare));
- return checkFailed();
- }
- if (UserId(call.vparticipant_id()) != participantId) {
- LOG(("Call Error: Wrong call participant_id %1, expected %2.")
- .arg(call.vparticipant_id().v)
- .arg(participantId.bare));
- return checkFailed();
- }
- return true;
- }
- bool Call::checkCallFields(const MTPDphoneCall &call) {
- if (!checkCallCommonFields(call)) {
- return false;
- }
- if (call.vkey_fingerprint().v != _keyFingerprint) {
- LOG(("Call Error: Wrong call fingerprint."));
- finish(FinishType::Failed);
- return false;
- }
- return true;
- }
- bool Call::checkCallFields(const MTPDphoneCallAccepted &call) {
- return checkCallCommonFields(call);
- }
- void Call::setState(State state) {
- const auto was = _state.current();
- if (was == State::Failed) {
- return;
- }
- if (was == State::FailedHangingUp
- && state != State::Failed) {
- return;
- }
- if (was != state) {
- _state = state;
- if (true
- && state != State::Starting
- && state != State::Requesting
- && state != State::Waiting
- && state != State::WaitingIncoming
- && state != State::Ringing) {
- _waitingTrack.reset();
- }
- if (false
- || state == State::Ended
- || state == State::EndedByOtherDevice
- || state == State::Failed
- || state == State::Busy) {
- // Destroy controller before destroying Call Panel,
- // so that the panel hide animation is smooth.
- destroyController();
- }
- switch (state) {
- case State::Established:
- _startTime = crl::now();
- break;
- case State::ExchangingKeys:
- _delegate->callPlaySound(Delegate::CallSound::Connecting);
- break;
- case State::Ended:
- if (was != State::WaitingUserConfirmation) {
- _delegate->callPlaySound(Delegate::CallSound::Ended);
- }
- [[fallthrough]];
- case State::EndedByOtherDevice:
- _delegate->callFinished(this);
- break;
- case State::Failed:
- _delegate->callPlaySound(Delegate::CallSound::Ended);
- _delegate->callFailed(this);
- break;
- case State::Busy:
- _delegate->callPlaySound(Delegate::CallSound::Busy);
- _discardByTimeoutTimer.cancel();
- break;
- }
- }
- }
- //void Call::setAudioVolume(bool input, float level) {
- // if (_instance) {
- // if (input) {
- // _instance->setInputVolume(level);
- // } else {
- // _instance->setOutputVolume(level);
- // }
- // }
- //}
- void Call::setAudioDuckingEnabled(bool enabled) {
- if (_instance) {
- _instance->setAudioOutputDuckingEnabled(enabled);
- }
- }
- bool Call::isSharingVideo() const {
- return (_videoOutgoing->state() != Webrtc::VideoState::Inactive);
- }
- bool Call::isSharingCamera() const {
- return !_videoCaptureIsScreencast && isSharingVideo();
- }
- bool Call::isSharingScreen() const {
- return _videoCaptureIsScreencast && isSharingVideo();
- }
- QString Call::cameraSharingDeviceId() const {
- return isSharingCamera() ? _videoCaptureDeviceId : QString();
- }
- QString Call::screenSharingDeviceId() const {
- return isSharingScreen() ? _videoCaptureDeviceId : QString();
- }
- void Call::toggleCameraSharing(bool enabled) {
- if (isSharingCamera() == enabled) {
- return;
- } else if (!enabled) {
- if (_videoCapture) {
- _videoCapture->setState(tgcalls::VideoState::Inactive);
- }
- _videoOutgoing->setState(Webrtc::VideoState::Inactive);
- _videoCaptureDeviceId = QString();
- return;
- }
- _delegate->callRequestPermissionsOrFail(crl::guard(this, [=] {
- toggleScreenSharing(std::nullopt);
- _videoCaptureDeviceId = _cameraDeviceId.current().value;
- if (_videoCapture) {
- _videoCapture->switchToDevice(
- _videoCaptureDeviceId.toStdString(),
- false);
- if (_instance) {
- _instance->sendVideoDeviceUpdated();
- }
- }
- _videoOutgoing->setState(Webrtc::VideoState::Active);
- }), true);
- }
- void Call::toggleScreenSharing(std::optional<QString> uniqueId) {
- if (!uniqueId) {
- if (isSharingScreen()) {
- if (_videoCapture) {
- _videoCapture->setState(tgcalls::VideoState::Inactive);
- }
- _videoOutgoing->setState(Webrtc::VideoState::Inactive);
- }
- _videoCaptureDeviceId = QString();
- _videoCaptureIsScreencast = false;
- return;
- } else if (screenSharingDeviceId() == *uniqueId) {
- return;
- }
- toggleCameraSharing(false);
- _videoCaptureIsScreencast = true;
- _videoCaptureDeviceId = *uniqueId;
- if (_videoCapture) {
- _videoCapture->switchToDevice(uniqueId->toStdString(), true);
- if (_instance) {
- _instance->sendVideoDeviceUpdated();
- }
- }
- _videoOutgoing->setState(Webrtc::VideoState::Active);
- }
- auto Call::playbackDeviceIdValue() const
- -> rpl::producer<Webrtc::DeviceResolvedId> {
- return _playbackDeviceId.value();
- }
- rpl::producer<Webrtc::DeviceResolvedId> Call::captureDeviceIdValue() const {
- return _captureDeviceId.value();
- }
- rpl::producer<Webrtc::DeviceResolvedId> Call::cameraDeviceIdValue() const {
- return _cameraDeviceId.value();
- }
- void Call::finish(FinishType type, const MTPPhoneCallDiscardReason &reason) {
- Expects(type != FinishType::None);
- setSignalBarCount(kSignalBarFinished);
- const auto finalState = (type == FinishType::Ended)
- ? State::Ended
- : State::Failed;
- const auto hangupState = (type == FinishType::Ended)
- ? State::HangingUp
- : State::FailedHangingUp;
- const auto state = _state.current();
- if (state == State::Requesting) {
- _finishByTimeoutTimer.call(kHangupTimeoutMs, [this, finalState] {
- setState(finalState);
- });
- _finishAfterRequestingCall = type;
- return;
- }
- if (state == State::HangingUp
- || state == State::FailedHangingUp
- || state == State::EndedByOtherDevice
- || state == State::Ended
- || state == State::Failed) {
- return;
- }
- if (!_id) {
- setState(finalState);
- return;
- }
- setState(hangupState);
- const auto duration = getDurationMs() / 1000;
- const auto connectionId = _instance
- ? _instance->getPreferredRelayId()
- : 0;
- _finishByTimeoutTimer.call(kHangupTimeoutMs, [this, finalState] {
- setState(finalState);
- });
- using Video = Webrtc::VideoState;
- const auto flags = ((_videoIncoming->state() != Video::Inactive)
- || (_videoOutgoing->state() != Video::Inactive))
- ? MTPphone_DiscardCall::Flag::f_video
- : MTPphone_DiscardCall::Flag(0);
- // We want to discard request still being sent and processed even if
- // the call is already destroyed.
- const auto session = &_user->session();
- const auto weak = base::make_weak(this);
- session->api().request(MTPphone_DiscardCall( // We send 'discard' here.
- MTP_flags(flags),
- MTP_inputPhoneCall(
- MTP_long(_id),
- MTP_long(_accessHash)),
- MTP_int(duration),
- reason,
- MTP_long(connectionId)
- )).done([=](const MTPUpdates &result) {
- // Here 'this' could be destroyed by updates, so we set Ended after
- // updates being handled, but in a guarded way.
- crl::on_main(weak, [=] { setState(finalState); });
- session->api().applyUpdates(result);
- }).fail(crl::guard(weak, [this, finalState] {
- setState(finalState);
- })).send();
- }
- void Call::setStateQueued(State state) {
- crl::on_main(this, [=] {
- setState(state);
- });
- }
- void Call::setFailedQueued(const QString &error) {
- crl::on_main(this, [=] {
- handleControllerError(error);
- });
- }
- void Call::handleRequestError(const QString &error) {
- const auto inform = (error == u"USER_PRIVACY_RESTRICTED"_q)
- ? tr::lng_call_error_not_available(tr::now, lt_user, _user->name())
- : (error == u"PARTICIPANT_VERSION_OUTDATED"_q)
- ? tr::lng_call_error_outdated(tr::now, lt_user, _user->name())
- : (error == u"CALL_PROTOCOL_LAYER_INVALID"_q)
- ? Lang::Hard::CallErrorIncompatible().replace(
- "{user}",
- _user->name())
- : QString();
- if (!inform.isEmpty()) {
- if (const auto window = Core::App().windowFor(
- Window::SeparateId(_user))) {
- window->show(Ui::MakeInformBox(inform));
- } else {
- Ui::show(Ui::MakeInformBox(inform));
- }
- }
- finish(FinishType::Failed);
- }
- void Call::handleControllerError(const QString &error) {
- const auto inform = (error == u"ERROR_INCOMPATIBLE"_q)
- ? Lang::Hard::CallErrorIncompatible().replace(
- "{user}",
- _user->name())
- : (error == u"ERROR_AUDIO_IO"_q)
- ? tr::lng_call_error_audio_io(tr::now)
- : QString();
- if (!inform.isEmpty()) {
- if (const auto window = Core::App().windowFor(
- Window::SeparateId(_user))) {
- window->show(Ui::MakeInformBox(inform));
- } else {
- Ui::show(Ui::MakeInformBox(inform));
- }
- }
- finish(FinishType::Failed);
- }
- void Call::destroyController() {
- _instanceLifetime.destroy();
- Core::App().mediaDevices().setCaptureMuteTracker(this, false);
- if (_instance) {
- _instance->stop([](tgcalls::FinalState) {
- });
- DEBUG_LOG(("Call Info: Destroying call controller.."));
- _instance.reset();
- DEBUG_LOG(("Call Info: Call controller destroyed."));
- }
- setSignalBarCount(kSignalBarFinished);
- }
- Call::~Call() {
- destroyController();
- }
- void UpdateConfig(const std::string &data) {
- tgcalls::SetLegacyGlobalServerConfig(data);
- }
- } // namespace Calls
|