2
0
mirror of https://github.com/telegramdesktop/tdesktop synced 2025-08-31 06:26:18 +00:00

Start group call bar in HistoryWidget.

This commit is contained in:
John Preston
2020-11-20 22:25:35 +03:00
parent 3aa2619a7f
commit 33941ad1b9
24 changed files with 1211 additions and 22 deletions

View File

@@ -289,7 +289,7 @@ void Call::startIncoming() {
}
void Call::answer() {
_delegate->requestPermissionsOrFail(crl::guard(this, [=] {
_delegate->callRequestPermissionsOrFail(crl::guard(this, [=] {
actuallyAnswer();
}));
}
@@ -776,11 +776,11 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
auto callLogPath = callLogFolder + qsl("/last_call_log.txt");
auto callLogNative = QDir::toNativeSeparators(callLogPath);
#ifdef Q_OS_WIN
descriptor.config.logPath = callLogNative.toStdWString();
descriptor.config.logPath.data = callLogNative.toStdWString();
#else // Q_OS_WIN
const auto callLogUtf = QFile::encodeName(callLogNative);
descriptor.config.logPath.resize(callLogUtf.size());
ranges::copy(callLogUtf, descriptor.config.logPath.begin());
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);

View File

@@ -68,7 +68,7 @@ public:
Ended,
};
virtual void playSound(Sound sound) = 0;
virtual void requestPermissionsOrFail(Fn<void()> onSuccess) = 0;
virtual void callRequestPermissionsOrFail(Fn<void()> onSuccess) = 0;
virtual auto getVideoCapture()
-> std::shared_ptr<tgcalls::VideoCaptureInterface> = 0;

View File

@@ -0,0 +1,329 @@
/*
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_group_call.h"
#include "main/main_session.h"
//#include "main/main_account.h"
//#include "main/main_app_config.h"
#include "apiwrap.h"
#include "lang/lang_keys.h"
#include "boxes/confirm_box.h"
//#include "boxes/rate_call_box.h"
//#include "calls/calls_instance.h"
//#include "base/openssl_help.h"
//#include "mtproto/mtproto_dh_utils.h"
//#include "mtproto/mtproto_config.h"
//#include "core/application.h"
//#include "core/core_settings.h"
//#include "media/audio/media_audio_track.h"
//#include "base/platform/base_platform_info.h"
//#include "calls/calls_panel.h"
//#include "webrtc/webrtc_video_track.h"
//#include "webrtc/webrtc_media_devices.h"
#include "data/data_channel.h"
//#include "data/data_session.h"
//#include "facades.h"
#include <tgcalls/group/GroupInstanceImpl.h>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonArray>
namespace tgcalls {
class GroupInstanceImpl;
} // namespace tgcalls
namespace Calls {
GroupCall::GroupCall(
not_null<Delegate*> delegate,
not_null<ChannelData*> channel,
const MTPInputGroupCall &inputCall)
: _delegate(delegate)
, _channel(channel)
, _api(&_channel->session().mtp()) {
if (inputCall.c_inputGroupCall().vid().v) {
join(inputCall);
} else {
start();
}
}
GroupCall::~GroupCall() {
destroyController();
}
void GroupCall::start() {
const auto randomId = rand_value<int32>();
_api.request(MTPphone_CreateGroupCall(
_channel->inputChannel,
MTP_int(randomId)
)).done([=](const MTPUpdates &result) {
_channel->session().api().applyUpdates(result);
}).fail([=](const RPCError &error) {
int a = error.code();
}).send();
}
void GroupCall::join(const MTPInputGroupCall &inputCall) {
inputCall.match([&](const MTPDinputGroupCall &data) {
_id = data.vid().v;
_accessHash = data.vaccess_hash().v;
createAndStartController();
const auto weak = base::make_weak(this);
_instance->emitJoinPayload([=](tgcalls::GroupJoinPayload payload) {
crl::on_main(weak, [=, payload = std::move(payload)]{
auto fingerprints = QJsonArray();
for (const auto print : payload.fingerprints) {
auto object = QJsonObject();
object.insert("hash", QString::fromStdString(print.hash));
object.insert("setup", QString::fromStdString(print.setup));
object.insert(
"fingerprint",
QString::fromStdString(print.fingerprint));
fingerprints.push_back(object);
}
auto root = QJsonObject();
root.insert("ufrag", QString::fromStdString(payload.ufrag));
root.insert("pwd", QString::fromStdString(payload.pwd));
root.insert("fingerprints", fingerprints);
root.insert("ssrc", int(payload.ssrc));
const auto json = QJsonDocument(root).toJson(
QJsonDocument::Compact);
_api.request(MTPphone_JoinGroupCall(
MTP_flags(_muted.current()
? MTPphone_JoinGroupCall::Flag::f_muted
: MTPphone_JoinGroupCall::Flag(0)),
inputCall,
MTP_dataJSON(MTP_bytes(json))
)).done([=](const MTPUpdates &updates) {
_channel->session().api().applyUpdates(updates);
}).fail([=](const RPCError &error) {
int a = error.code();
}).send();
});
});
});
}
void GroupCall::setMuted(bool mute) {
_muted = mute;
if (_instance) {
_instance->setIsMuted(mute);
}
}
bool GroupCall::handleUpdate(const MTPGroupCall &call) {
return call.match([&](const MTPDgroupCall &data) {
if (_id != data.vid().v
|| _accessHash != data.vaccess_hash().v
|| !_instance) {
return false;
}
if (const auto params = data.vparams()) {
params->match([&](const MTPDdataJSON &data) {
auto error = QJsonParseError{ 0, QJsonParseError::NoError };
const auto document = QJsonDocument::fromJson(
data.vdata().v,
&error);
if (error.error != QJsonParseError::NoError) {
LOG(("API Error: "
"Failed to parse group call params, error: %1."
).arg(error.errorString()));
return;
} else if (!document.isObject()) {
LOG(("API Error: "
"Not an object received in group call params."));
return;
}
const auto readString = [](
const QJsonObject &object,
const char *key) {
return object.value(key).toString().toStdString();
};
const auto root = document.object().value("transport").toObject();
auto payload = tgcalls::GroupJoinResponsePayload();
payload.ufrag = readString(root, "ufrag");
payload.pwd = readString(root, "pwd");
const auto prints = root.value("fingerprints").toArray();
const auto candidates = root.value("candidates").toArray();
for (const auto &print : prints) {
const auto object = print.toObject();
payload.fingerprints.push_back(tgcalls::GroupJoinPayloadFingerprint{
.hash = readString(object, "hash"),
.setup = readString(object, "setup"),
.fingerprint = readString(object, "fingerprint"),
});
}
for (const auto &candidate : candidates) {
const auto object = candidate.toObject();
payload.candidates.push_back(tgcalls::GroupJoinResponseCandidate{
.port = readString(object, "port"),
.protocol = readString(object, "protocol"),
.network = readString(object, "network"),
.generation = readString(object, "generation"),
.id = readString(object, "id"),
.component = readString(object, "component"),
.foundation = readString(object, "foundation"),
.priority = readString(object, "priority"),
.ip = readString(object, "ip"),
.type = readString(object, "type"),
.tcpType = readString(object, "tcpType"),
.relAddr = readString(object, "relAddr"),
.relPort = readString(object, "relPort"),
});
}
_instance->setJoinResponsePayload(payload);
_api.request(MTPphone_GetGroupParticipants(
inputCall(),
MTP_int(0),
MTP_int(10)
)).done([=](const MTPphone_GroupParticipants &result) {
auto sources = std::vector<uint32_t>();
result.match([&](const MTPDphone_groupParticipants &data) {
for (const auto &p : data.vparticipants().v) {
p.match([&](const MTPDgroupCallParticipant &data) {
if (data.vuser_id().v != _channel->session().userId()) {
sources.push_back(data.vsource().v);
}
});
}
});
_instance->setSsrcs(std::move(sources));
_instance->setIsMuted(false);
}).fail([=](const RPCError &error) {
int a = error.code();
}).send();
});
}
return true;
}, [&](const MTPDgroupCallPrivate &data) {
if (_instance || _id) {
return false;
}
join(MTP_inputGroupCall(data.vid(), data.vaccess_hash()));
return true;
}, [&](const MTPDgroupCallDiscarded &data) {
if (data.vid().v != _id) {
return false;
}
return true;
});
}
void GroupCall::createAndStartController() {
using AudioLevels = std::vector<std::pair<uint32_t, float>>;
const auto weak = base::make_weak(this);
tgcalls::GroupInstanceDescriptor descriptor = {
.config = tgcalls::GroupConfig{
},
.networkStateUpdated = [=](bool) {
},
.audioLevelsUpdated = [=](const AudioLevels &data) {
},
};
if (Logs::DebugEnabled()) {
auto callLogFolder = cWorkingDir() + qsl("DebugLogs");
auto callLogPath = callLogFolder + qsl("/last_group_call_log.txt");
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);
}
LOG(("Call Info: Creating group instance"));
_instance = std::make_unique<tgcalls::GroupInstanceImpl>(
std::move(descriptor));
const auto raw = _instance.get();
if (_muted.current()) {
raw->setIsMuted(_muted.current());
}
//raw->setAudioOutputDuckingEnabled(settings.callAudioDuckingEnabled());
}
void GroupCall::setCurrentAudioDevice(bool input, const QString &deviceId) {
if (_instance) {
const auto id = deviceId.toStdString();
//if (input) {
// _instance->setAudioInputDevice(id);
//} else {
// _instance->setAudioOutputDevice(id);
//}
}
}
void GroupCall::setAudioVolume(bool input, float level) {
if (_instance) {
//if (input) {
// _instance->setInputVolume(level);
//} else {
// _instance->setOutputVolume(level);
//}
}
}
void GroupCall::setAudioDuckingEnabled(bool enabled) {
if (_instance) {
//_instance->setAudioOutputDuckingEnabled(enabled);
}
}
void GroupCall::handleRequestError(const RPCError &error) {
//if (error.type() == qstr("USER_PRIVACY_RESTRICTED")) {
// Ui::show(Box<InformBox>(tr::lng_call_error_not_available(tr::now, lt_user, _user->name)));
//} else if (error.type() == qstr("PARTICIPANT_VERSION_OUTDATED")) {
// Ui::show(Box<InformBox>(tr::lng_call_error_outdated(tr::now, lt_user, _user->name)));
//} else if (error.type() == qstr("CALL_PROTOCOL_LAYER_INVALID")) {
// Ui::show(Box<InformBox>(Lang::Hard::CallErrorIncompatible().replace("{user}", _user->name)));
//}
//finish(FinishType::Failed);
}
void GroupCall::handleControllerError(const QString &error) {
if (error == u"ERROR_INCOMPATIBLE"_q) {
//Ui::show(Box<InformBox>(
// Lang::Hard::CallErrorIncompatible().replace(
// "{user}",
// _user->name)));
} else if (error == u"ERROR_AUDIO_IO"_q) {
Ui::show(Box<InformBox>(tr::lng_call_error_audio_io(tr::now)));
}
//finish(FinishType::Failed);
}
MTPInputGroupCall GroupCall::inputCall() const {
Expects(_id != 0);
return MTP_inputGroupCall(
MTP_long(_id),
MTP_long(_accessHash));
}
void GroupCall::destroyController() {
if (_instance) {
//_instance->stop([](tgcalls::FinalState) {
//});
DEBUG_LOG(("Call Info: Destroying call controller.."));
_instance.reset();
DEBUG_LOG(("Call Info: Call controller destroyed."));
}
}
} // namespace Calls

View File

@@ -0,0 +1,84 @@
/*
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"
namespace tgcalls {
class GroupInstanceImpl;
} // namespace tgcalls
namespace Calls {
class GroupCall final : public base::has_weak_ptr {
public:
class Delegate {
public:
virtual ~Delegate() = default;
};
GroupCall(
not_null<Delegate*> delegate,
not_null<ChannelData*> channel,
const MTPInputGroupCall &inputCall);
~GroupCall();
[[nodiscard]] not_null<ChannelData*> channel() const {
return _channel;
}
void start();
void join(const MTPInputGroupCall &inputCall);
bool handleUpdate(const MTPGroupCall &call);
void setMuted(bool mute);
[[nodiscard]] bool muted() const {
return _muted.current();
}
[[nodiscard]] rpl::producer<bool> mutedValue() const {
return _muted.value();
}
void setCurrentAudioDevice(bool input, const QString &deviceId);
void setAudioVolume(bool input, float level);
void setAudioDuckingEnabled(bool enabled);
[[nodiscard]] rpl::lifetime &lifetime() {
return _lifetime;
}
private:
void handleRequestError(const RPCError &error);
void handleControllerError(const QString &error);
void createAndStartController();
void destroyController();
[[nodiscard]] MTPInputGroupCall inputCall() const;
const not_null<Delegate*> _delegate;
const not_null<ChannelData*> _channel;
MTP::Sender _api;
crl::time _startTime = 0;
rpl::variable<bool> _muted = false;
uint64 _id = 0;
uint64 _accessHash = 0;
std::unique_ptr<tgcalls::GroupInstanceImpl> _instance;
rpl::lifetime _lifetime;
};
} // namespace Calls

View File

@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/calls_call.h"
#include "calls/calls_panel.h"
#include "data/data_user.h"
#include "data/data_channel.h"
#include "data/data_session.h"
#include "media/audio/media_audio_track.h"
#include "platform/platform_specific.h"
@@ -39,8 +40,7 @@ Instance::Instance() = default;
Instance::~Instance() = default;
void Instance::startOutgoingCall(not_null<UserData*> user, bool video) {
if (alreadyInCall()) { // Already in a call.
_currentCallPanel->showAndActivate();
if (activateCurrentCall()) {
return;
}
if (user->callsStatus() == UserData::CallsStatus::Private) {
@@ -55,6 +55,26 @@ void Instance::startOutgoingCall(not_null<UserData*> user, bool video) {
}));
}
void Instance::startGroupCall(not_null<ChannelData*> channel) {
if (activateCurrentCall()) {
return;
}
requestPermissionsOrFail(crl::guard(this, [=] {
createGroupCall(channel, MTP_inputGroupCall(MTPlong(), MTPlong()));
}));
}
void Instance::joinGroupCall(
not_null<ChannelData*> channel,
const MTPInputGroupCall &call) {
if (activateCurrentCall()) {
return;
}
requestPermissionsOrFail(crl::guard(this, [=] {
createGroupCall(channel, call);
}));
}
void Instance::callFinished(not_null<Call*> call) {
crl::on_main(call, [=] {
destroyCall(call);
@@ -142,6 +162,40 @@ void Instance::createCall(not_null<UserData*> user, Call::Type type, bool video)
refreshDhConfig();
}
void Instance::destroyGroupCall(not_null<GroupCall*> call) {
if (_currentGroupCall.get() == call) {
//_currentCallPanel->closeBeforeDestroy();
//_currentCallPanel = nullptr;
auto taken = base::take(_currentGroupCall);
_currentGroupCallChanges.fire(nullptr);
taken.reset();
if (App::quitting()) {
LOG(("Calls::Instance doesn't prevent quit any more."));
}
Core::App().quitPreventFinished();
}
}
void Instance::createGroupCall(
not_null<ChannelData*> channel,
const MTPInputGroupCall &inputCall) {
auto call = std::make_unique<GroupCall>(
getGroupCallDelegate(),
channel,
inputCall);
const auto raw = call.get();
channel->session().account().sessionChanges(
) | rpl::start_with_next([=] {
destroyGroupCall(raw);
}, raw->lifetime());
_currentGroupCall = std::move(call);
_currentGroupCallChanges.fire_copy(raw);
}
void Instance::refreshDhConfig() {
Expects(_currentCall != nullptr);
@@ -232,6 +286,10 @@ void Instance::handleUpdate(
handleCallUpdate(session, data.vphone_call());
}, [&](const MTPDupdatePhoneCallSignalingData &data) {
handleSignalingData(data);
}, [&](const MTPDupdateGroupCall &data) {
handleGroupCallUpdate(session, data.vcall());
}, [&](const MTPDupdateGroupCallParticipants &data) {
handleGroupCallUpdate(session, data);
}, [](const auto &) {
Unexpected("Update type in Calls::Instance::handleUpdate.");
});
@@ -267,7 +325,7 @@ void Instance::handleCallUpdate(
LOG(("API Error: Self found in phoneCallRequested."));
}
const auto &config = session->serverConfig();
if (alreadyInCall() || !user || user->isSelf()) {
if (inCall() || inGroupCall() || !user || user->isSelf()) {
const auto flags = phoneCall.is_video()
? MTPphone_DiscardCall::Flag::f_video
: MTPphone_DiscardCall::Flag(0);
@@ -290,6 +348,19 @@ void Instance::handleCallUpdate(
}
}
void Instance::handleGroupCallUpdate(
not_null<Main::Session*> session,
const MTPGroupCall &call) {
if (_currentGroupCall) {
_currentGroupCall->handleUpdate(call);
}
}
void Instance::handleGroupCallUpdate(
not_null<Main::Session*> session,
const MTPDupdateGroupCallParticipants &update) {
}
void Instance::handleSignalingData(
const MTPDupdatePhoneCallSignalingData &data) {
if (!_currentCall || !_currentCall->handleSignalingData(data)) {
@@ -298,10 +369,24 @@ void Instance::handleSignalingData(
}
}
bool Instance::alreadyInCall() {
bool Instance::inCall() const {
return (_currentCall && _currentCall->state() != Call::State::Busy);
}
bool Instance::inGroupCall() const {
return (_currentGroupCall != nullptr);
}
bool Instance::activateCurrentCall() {
if (inCall()) {
_currentCallPanel->showAndActivate();
return true;
} else if (inGroupCall()) {
return true;
}
return false;
}
Call *Instance::currentCall() const {
return _currentCall.get();
}
@@ -335,9 +420,12 @@ void Instance::requestPermissionOrFail(Platform::PermissionType type, Fn<void()>
}
}));
} else {
if (alreadyInCall()) {
if (inCall()) {
_currentCall->hangup();
}
if (inGroupCall()) {
//_currentGroupCall->hangup(); // #TODO calls
}
Ui::show(Box<ConfirmBox>(tr::lng_no_mic_permission(tr::now), tr::lng_menu_settings(tr::now), crl::guard(this, [=] {
Platform::OpenSystemSettingsForPermission(type);
Ui::hideLayer();

View File

@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/sender.h"
#include "calls/calls_call.h"
#include "calls/calls_group_call.h"
namespace Platform {
enum class PermissionType;
@@ -30,6 +31,7 @@ class Panel;
class Instance
: private Call::Delegate
, private GroupCall::Delegate
, private base::Subscriber
, public base::has_weak_ptr {
public:
@@ -37,6 +39,10 @@ public:
~Instance();
void startOutgoingCall(not_null<UserData*> user, bool video);
void startGroupCall(not_null<ChannelData*> channel);
void joinGroupCall(
not_null<ChannelData*> channel,
const MTPInputGroupCall &call);
void handleUpdate(
not_null<Main::Session*> session,
const MTPUpdate &update);
@@ -48,20 +54,33 @@ public:
[[nodiscard]] bool isQuitPrevent();
private:
not_null<Call::Delegate*> getCallDelegate() {
[[nodiscard]] not_null<Call::Delegate*> getCallDelegate() {
return static_cast<Call::Delegate*>(this);
}
DhConfig getDhConfig() const override {
[[nodiscard]] not_null<GroupCall::Delegate*> getGroupCallDelegate() {
return static_cast<GroupCall::Delegate*>(this);
}
[[nodiscard]] DhConfig getDhConfig() const override {
return _dhConfig;
}
void callFinished(not_null<Call*> call) override;
void callFailed(not_null<Call*> call) override;
void callRedial(not_null<Call*> call) override;
void callRequestPermissionsOrFail(Fn<void()> onSuccess) override {
requestPermissionsOrFail(std::move(onSuccess));
}
using Sound = Call::Delegate::Sound;
void playSound(Sound sound) override;
void createCall(not_null<UserData*> user, Call::Type type, bool video);
void destroyCall(not_null<Call*> call);
void requestPermissionsOrFail(Fn<void()> onSuccess) override;
void createGroupCall(
not_null<ChannelData*> channel,
const MTPInputGroupCall &inputCall);
void destroyGroupCall(not_null<GroupCall*> call);
void requestPermissionsOrFail(Fn<void()> onSuccess);
void requestPermissionOrFail(Platform::PermissionType type, Fn<void()> onSuccess);
void handleSignalingData(const MTPDupdatePhoneCallSignalingData &data);
@@ -70,10 +89,18 @@ private:
void refreshServerConfig(not_null<Main::Session*> session);
bytes::const_span updateDhConfig(const MTPmessages_DhConfig &data);
bool alreadyInCall();
bool activateCurrentCall();
[[nodiscard]] bool inCall() const;
[[nodiscard]] bool inGroupCall() const;
void handleCallUpdate(
not_null<Main::Session*> session,
const MTPPhoneCall &call);
void handleGroupCallUpdate(
not_null<Main::Session*> session,
const MTPGroupCall &call);
void handleGroupCallUpdate(
not_null<Main::Session*> session,
const MTPDupdateGroupCallParticipants &update);
DhConfig _dhConfig;
@@ -84,8 +111,9 @@ private:
std::unique_ptr<Call> _currentCall;
rpl::event_stream<Call*> _currentCallChanges;
std::unique_ptr<Panel> _currentCallPanel;
base::Observable<Call*> _currentCallChanged;
base::Observable<FullMsgId> _newServiceMessage;
std::unique_ptr<GroupCall> _currentGroupCall;
rpl::event_stream<GroupCall*> _currentGroupCallChanges;
std::unique_ptr<Media::Audio::Track> _callConnectingTrack;
std::unique_ptr<Media::Audio::Track> _callEndedTrack;