mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 22:54:01 +02:00
PoC conference call creation.
This commit is contained in:
parent
5e6c81a98e
commit
c80c23e8e8
14 changed files with 363 additions and 38 deletions
|
@ -1540,6 +1540,8 @@ PRIVATE
|
||||||
support/support_preload.h
|
support/support_preload.h
|
||||||
support/support_templates.cpp
|
support/support_templates.cpp
|
||||||
support/support_templates.h
|
support/support_templates.h
|
||||||
|
tde2e/tde2e_integration.cpp
|
||||||
|
tde2e/tde2e_integration.h
|
||||||
ui/boxes/edit_invite_link_session.cpp
|
ui/boxes/edit_invite_link_session.cpp
|
||||||
ui/boxes/edit_invite_link_session.h
|
ui/boxes/edit_invite_link_session.h
|
||||||
ui/boxes/peer_qr_box.cpp
|
ui/boxes/peer_qr_box.cpp
|
||||||
|
|
|
@ -4911,6 +4911,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_confcall_join_title" = "Group Call";
|
"lng_confcall_join_title" = "Group Call";
|
||||||
"lng_confcall_join_text" = "You are invited to join a Telegram Call.";
|
"lng_confcall_join_text" = "You are invited to join a Telegram Call.";
|
||||||
"lng_confcall_join_button" = "Join Group Call";
|
"lng_confcall_join_button" = "Join Group Call";
|
||||||
|
"lng_confcall_create_link" = "Create Call Link";
|
||||||
|
"lng_confcall_create_link_description" = "You can create a link that will allow your friends on Telegram to join the call.";
|
||||||
|
|
||||||
"lng_no_mic_permission" = "Telegram needs microphone access so that you can make calls and record voice messages.";
|
"lng_no_mic_permission" = "Telegram needs microphone access so that you can make calls and record voice messages.";
|
||||||
|
|
||||||
|
|
|
@ -303,14 +303,19 @@ void Updates::feedUpdateVector(
|
||||||
auto list = updates.v;
|
auto list = updates.v;
|
||||||
const auto hasGroupCallParticipantUpdates = ranges::contains(
|
const auto hasGroupCallParticipantUpdates = ranges::contains(
|
||||||
list,
|
list,
|
||||||
mtpc_updateGroupCallParticipants,
|
true,
|
||||||
&MTPUpdate::type);
|
[](const MTPUpdate &update) {
|
||||||
|
return update.type() == mtpc_updateGroupCallParticipants
|
||||||
|
|| update.type() == mtpc_updateGroupCallChainBlocks;
|
||||||
|
});
|
||||||
if (hasGroupCallParticipantUpdates) {
|
if (hasGroupCallParticipantUpdates) {
|
||||||
ranges::stable_sort(list, std::less<>(), [](const MTPUpdate &entry) {
|
ranges::stable_sort(list, std::less<>(), [](const MTPUpdate &entry) {
|
||||||
if (entry.type() == mtpc_updateGroupCallParticipants) {
|
if (entry.type() == mtpc_updateGroupCallChainBlocks) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else if (entry.type() == mtpc_updateGroupCallParticipants) {
|
||||||
return 1;
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 2;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (policy == SkipUpdatePolicy::SkipExceptGroupCallParticipants) {
|
} else if (policy == SkipUpdatePolicy::SkipExceptGroupCallParticipants) {
|
||||||
|
@ -324,7 +329,8 @@ void Updates::feedUpdateVector(
|
||||||
if ((policy == SkipUpdatePolicy::SkipMessageIds
|
if ((policy == SkipUpdatePolicy::SkipMessageIds
|
||||||
&& type == mtpc_updateMessageID)
|
&& type == mtpc_updateMessageID)
|
||||||
|| (policy == SkipUpdatePolicy::SkipExceptGroupCallParticipants
|
|| (policy == SkipUpdatePolicy::SkipExceptGroupCallParticipants
|
||||||
&& type != mtpc_updateGroupCallParticipants)) {
|
&& type != mtpc_updateGroupCallParticipants
|
||||||
|
&& type != mtpc_updateGroupCallChainBlocks)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
feedUpdate(entry);
|
feedUpdate(entry);
|
||||||
|
@ -954,7 +960,8 @@ void Updates::applyGroupCallParticipantUpdates(const MTPUpdates &updates) {
|
||||||
data.vupdates(),
|
data.vupdates(),
|
||||||
SkipUpdatePolicy::SkipExceptGroupCallParticipants);
|
SkipUpdatePolicy::SkipExceptGroupCallParticipants);
|
||||||
}, [&](const MTPDupdateShort &data) {
|
}, [&](const MTPDupdateShort &data) {
|
||||||
if (data.vupdate().type() == mtpc_updateGroupCallParticipants) {
|
if (data.vupdate().type() == mtpc_updateGroupCallParticipants
|
||||||
|
|| data.vupdate().type() == mtpc_updateGroupCallChainBlocks) {
|
||||||
feedUpdate(data.vupdate());
|
feedUpdate(data.vupdate());
|
||||||
}
|
}
|
||||||
}, [](const auto &) {
|
}, [](const auto &) {
|
||||||
|
@ -2110,6 +2117,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||||
case mtpc_updatePhoneCall:
|
case mtpc_updatePhoneCall:
|
||||||
case mtpc_updatePhoneCallSignalingData:
|
case mtpc_updatePhoneCallSignalingData:
|
||||||
case mtpc_updateGroupCallParticipants:
|
case mtpc_updateGroupCallParticipants:
|
||||||
|
case mtpc_updateGroupCallChainBlocks:
|
||||||
case mtpc_updateGroupCallConnection:
|
case mtpc_updateGroupCallConnection:
|
||||||
case mtpc_updateGroupCall: {
|
case mtpc_updateGroupCall: {
|
||||||
Core::App().calls().handleUpdate(&session(), update);
|
Core::App().calls().handleUpdate(&session(), update);
|
||||||
|
|
|
@ -235,16 +235,18 @@ void Instance::startOrJoinConferenceCall(
|
||||||
StartConferenceCallArgs args) {
|
StartConferenceCallArgs args) {
|
||||||
destroyCurrentCall();
|
destroyCurrentCall();
|
||||||
|
|
||||||
|
const auto session = &args.call->peer()->session();
|
||||||
auto call = std::make_unique<GroupCall>(
|
auto call = std::make_unique<GroupCall>(
|
||||||
_delegate.get(),
|
_delegate.get(),
|
||||||
Calls::Group::ConferenceInfo{
|
Calls::Group::ConferenceInfo{
|
||||||
.call = args.call,
|
.call = std::move(args.call),
|
||||||
|
.e2e = std::move(args.e2e),
|
||||||
.linkSlug = args.linkSlug,
|
.linkSlug = args.linkSlug,
|
||||||
.joinMessageId = args.joinMessageId,
|
.joinMessageId = args.joinMessageId,
|
||||||
});
|
});
|
||||||
const auto raw = call.get();
|
const auto raw = call.get();
|
||||||
|
|
||||||
args.call->peer()->session().account().sessionChanges(
|
session->account().sessionChanges(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
destroyGroupCall(raw);
|
destroyGroupCall(raw);
|
||||||
}, raw->lifetime());
|
}, raw->lifetime());
|
||||||
|
@ -547,6 +549,8 @@ void Instance::handleUpdate(
|
||||||
handleGroupCallUpdate(session, update);
|
handleGroupCallUpdate(session, update);
|
||||||
}, [&](const MTPDupdateGroupCallParticipants &data) {
|
}, [&](const MTPDupdateGroupCallParticipants &data) {
|
||||||
handleGroupCallUpdate(session, update);
|
handleGroupCallUpdate(session, update);
|
||||||
|
}, [&](const MTPDupdateGroupCallChainBlocks &data) {
|
||||||
|
handleGroupCallUpdate(session, update);
|
||||||
}, [](const auto &) {
|
}, [](const auto &) {
|
||||||
Unexpected("Update type in Calls::Instance::handleUpdate.");
|
Unexpected("Update type in Calls::Instance::handleUpdate.");
|
||||||
});
|
});
|
||||||
|
@ -677,6 +681,12 @@ void Instance::handleGroupCallUpdate(
|
||||||
}, [](const MTPDinputGroupCallSlug &) -> CallId {
|
}, [](const MTPDinputGroupCallSlug &) -> CallId {
|
||||||
Unexpected("slug in Instance::handleGroupCallUpdate");
|
Unexpected("slug in Instance::handleGroupCallUpdate");
|
||||||
});
|
});
|
||||||
|
}, [](const MTPDupdateGroupCallChainBlocks &data) {
|
||||||
|
return data.vcall().match([&](const MTPDinputGroupCall &data) {
|
||||||
|
return data.vid().v;
|
||||||
|
}, [](const MTPDinputGroupCallSlug &) -> CallId {
|
||||||
|
Unexpected("slug in Instance::handleGroupCallUpdate");
|
||||||
|
});
|
||||||
}, [](const auto &) -> CallId {
|
}, [](const auto &) -> CallId {
|
||||||
Unexpected("Type in Instance::handleGroupCallUpdate.");
|
Unexpected("Type in Instance::handleGroupCallUpdate.");
|
||||||
});
|
});
|
||||||
|
|
|
@ -45,6 +45,10 @@ namespace tgcalls {
|
||||||
class VideoCaptureInterface;
|
class VideoCaptureInterface;
|
||||||
} // namespace tgcalls
|
} // namespace tgcalls
|
||||||
|
|
||||||
|
namespace TdE2E {
|
||||||
|
class Call;
|
||||||
|
} // namespace TdE2E
|
||||||
|
|
||||||
namespace Calls {
|
namespace Calls {
|
||||||
|
|
||||||
class Call;
|
class Call;
|
||||||
|
@ -66,6 +70,7 @@ struct StartGroupCallArgs {
|
||||||
|
|
||||||
struct StartConferenceCallArgs {
|
struct StartConferenceCallArgs {
|
||||||
std::shared_ptr<Data::GroupCall> call;
|
std::shared_ptr<Data::GroupCall> call;
|
||||||
|
std::shared_ptr<TdE2E::Call> e2e;
|
||||||
QString linkSlug;
|
QString linkSlug;
|
||||||
MsgId joinMessageId;
|
MsgId joinMessageId;
|
||||||
};
|
};
|
||||||
|
|
|
@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/global_shortcuts.h"
|
#include "base/global_shortcuts.h"
|
||||||
#include "base/random.h"
|
#include "base/random.h"
|
||||||
#include "tde2e/tde2e_api.h"
|
#include "tde2e/tde2e_api.h"
|
||||||
|
#include "tde2e/tde2e_integration.h"
|
||||||
#include "webrtc/webrtc_video_track.h"
|
#include "webrtc/webrtc_video_track.h"
|
||||||
#include "webrtc/webrtc_create_adm.h"
|
#include "webrtc/webrtc_create_adm.h"
|
||||||
#include "webrtc/webrtc_environment.h"
|
#include "webrtc/webrtc_environment.h"
|
||||||
|
@ -53,6 +54,10 @@ constexpr auto kFixManualLargeVideoDuration = 5 * crl::time(1000);
|
||||||
constexpr auto kFixSpeakingLargeVideoDuration = 3 * crl::time(1000);
|
constexpr auto kFixSpeakingLargeVideoDuration = 3 * crl::time(1000);
|
||||||
constexpr auto kFullAsMediumsCount = 4; // 1 Full is like 4 Mediums.
|
constexpr auto kFullAsMediumsCount = 4; // 1 Full is like 4 Mediums.
|
||||||
constexpr auto kMaxMediumQualities = 16; // 4 Fulls or 16 Mediums.
|
constexpr auto kMaxMediumQualities = 16; // 4 Fulls or 16 Mediums.
|
||||||
|
constexpr auto kShortPollChainBlocksTimeout = 5 * crl::time(1000);
|
||||||
|
constexpr auto kShortPollChainBlocksPerRequest = 50;
|
||||||
|
constexpr auto kSubChain0 = 0;
|
||||||
|
constexpr auto kSubChain1 = 1;
|
||||||
|
|
||||||
[[nodiscard]] const Data::GroupCallParticipant *LookupParticipant(
|
[[nodiscard]] const Data::GroupCallParticipant *LookupParticipant(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
|
@ -603,7 +608,8 @@ GroupCall::GroupCall(
|
||||||
Group::ConferenceInfo conference,
|
Group::ConferenceInfo conference,
|
||||||
const MTPInputGroupCall &inputCall)
|
const MTPInputGroupCall &inputCall)
|
||||||
: _delegate(delegate)
|
: _delegate(delegate)
|
||||||
, _conferenceCall(conference.call)
|
, _conferenceCall(std::move(conference.call))
|
||||||
|
, _e2e(std::move(conference.e2e))
|
||||||
, _peer(join.peer)
|
, _peer(join.peer)
|
||||||
, _history(_peer->owner().history(_peer))
|
, _history(_peer->owner().history(_peer))
|
||||||
, _api(&_peer->session().mtp())
|
, _api(&_peer->session().mtp())
|
||||||
|
@ -641,8 +647,15 @@ GroupCall::GroupCall(
|
||||||
, _rtmp(join.rtmp)
|
, _rtmp(join.rtmp)
|
||||||
, _rtmpVolume(Group::kDefaultVolume) {
|
, _rtmpVolume(Group::kDefaultVolume) {
|
||||||
if (_conferenceCall) {
|
if (_conferenceCall) {
|
||||||
_e2eState = std::make_unique<TdE2E::CallState>(
|
if (!_e2e) {
|
||||||
TdE2E::CreateCallState());
|
_e2e = std::make_shared<TdE2E::Call>(
|
||||||
|
TdE2E::MakeUserId(_peer->session().user()));
|
||||||
|
}
|
||||||
|
for (auto i = 0; i != kSubChainsCount; ++i) {
|
||||||
|
_subchains[i].timer.setCallback([=] {
|
||||||
|
checkChainBlocksRequest(i);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_muted.value(
|
_muted.value(
|
||||||
|
@ -1419,10 +1432,7 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
|
||||||
? Flag::f_video_stopped
|
? Flag::f_video_stopped
|
||||||
: Flag(0))
|
: Flag(0))
|
||||||
| (_conferenceJoinMessageId ? Flag::f_invite_msg_id : Flag())
|
| (_conferenceJoinMessageId ? Flag::f_invite_msg_id : Flag())
|
||||||
| (_e2eState ? Flag::f_public_key : Flag());
|
| (_e2e ? Flag::f_public_key : Flag());
|
||||||
const auto publicKey = _e2eState
|
|
||||||
? _e2eState->myKey
|
|
||||||
: TdE2E::PublicKey();
|
|
||||||
_api.request(MTPphone_JoinGroupCall(
|
_api.request(MTPphone_JoinGroupCall(
|
||||||
MTP_flags(flags),
|
MTP_flags(flags),
|
||||||
(_conferenceLinkSlug.isEmpty()
|
(_conferenceLinkSlug.isEmpty()
|
||||||
|
@ -1431,9 +1441,7 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
|
||||||
MTP_string(_conferenceLinkSlug))),
|
MTP_string(_conferenceLinkSlug))),
|
||||||
joinAs()->input,
|
joinAs()->input,
|
||||||
MTP_string(_joinHash),
|
MTP_string(_joinHash),
|
||||||
MTP_int256(
|
(_e2e ? TdE2E::PublicKeyToMTP(_e2e->myKey()) : MTPint256()),
|
||||||
MTP_int128(publicKey.a, publicKey.b),
|
|
||||||
MTP_int128(publicKey.c, publicKey.d)),
|
|
||||||
MTP_int(_conferenceJoinMessageId.bare),
|
MTP_int(_conferenceJoinMessageId.bare),
|
||||||
MTP_dataJSON(MTP_bytes(json))
|
MTP_dataJSON(MTP_bytes(json))
|
||||||
)).done([=](
|
)).done([=](
|
||||||
|
@ -1470,6 +1478,8 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
|
||||||
real->reloadIfStale();
|
real->reloadIfStale();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
checkChainBlocksRequest(kSubChain0);
|
||||||
|
checkChainBlocksRequest(kSubChain1);
|
||||||
}).fail([=](const MTP::Error &error) {
|
}).fail([=](const MTP::Error &error) {
|
||||||
_joinState.finish();
|
_joinState.finish();
|
||||||
|
|
||||||
|
@ -1490,6 +1500,40 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GroupCall::checkChainBlocksRequest(int subchain) {
|
||||||
|
Expects(subchain >= 0 && subchain < kSubChainsCount);
|
||||||
|
|
||||||
|
auto &state = _subchains[subchain];
|
||||||
|
if (state.requestId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto now = crl::now();
|
||||||
|
const auto left = state.lastUpdate + kShortPollChainBlocksTimeout - now;
|
||||||
|
if (left > 0) {
|
||||||
|
if (!state.timer.isActive()) {
|
||||||
|
state.timer.callOnce(left);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.requestId = _api.request(MTPphone_GetGroupCallChainBlocks(
|
||||||
|
inputCall(),
|
||||||
|
MTP_int(subchain),
|
||||||
|
MTP_int(state.height),
|
||||||
|
MTP_int(kShortPollChainBlocksPerRequest)
|
||||||
|
)).done([=](const MTPUpdates &result) {
|
||||||
|
auto &state = _subchains[subchain];
|
||||||
|
state.lastUpdate = crl::now();
|
||||||
|
state.requestId = 0;
|
||||||
|
_peer->session().api().applyUpdates(result);
|
||||||
|
state.timer.callOnce(kShortPollChainBlocksTimeout + 1);
|
||||||
|
}).fail([=](const MTP::Error &error) {
|
||||||
|
auto &state = _subchains[subchain];
|
||||||
|
state.lastUpdate = crl::now();
|
||||||
|
state.requestId = 0;
|
||||||
|
state.timer.callOnce(kShortPollChainBlocksTimeout + 1);
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
void GroupCall::checkNextJoinAction() {
|
void GroupCall::checkNextJoinAction() {
|
||||||
if (_joinState.action != JoinAction::None) {
|
if (_joinState.action != JoinAction::None) {
|
||||||
return;
|
return;
|
||||||
|
@ -1640,7 +1684,6 @@ void GroupCall::applyMeInCallLocally() {
|
||||||
: participant
|
: participant
|
||||||
? participant->raisedHandRating
|
? participant->raisedHandRating
|
||||||
: FindLocalRaisedHandRating(real->participants());
|
: FindLocalRaisedHandRating(real->participants());
|
||||||
const auto publicKey = _e2eState ? _e2eState->myKey : TdE2E::PublicKey();
|
|
||||||
const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0))
|
const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0))
|
||||||
| (lastActive ? Flag::f_active_date : Flag(0))
|
| (lastActive ? Flag::f_active_date : Flag(0))
|
||||||
| (_joinState.ssrc ? Flag(0) : Flag::f_left)
|
| (_joinState.ssrc ? Flag(0) : Flag::f_left)
|
||||||
|
@ -1650,7 +1693,7 @@ void GroupCall::applyMeInCallLocally() {
|
||||||
| Flag::f_volume_by_admin // Self volume can only be set by admin.
|
| Flag::f_volume_by_admin // Self volume can only be set by admin.
|
||||||
| ((muted() != MuteState::Active) ? Flag::f_muted : Flag(0))
|
| ((muted() != MuteState::Active) ? Flag::f_muted : Flag(0))
|
||||||
| (raisedHandRating > 0 ? Flag::f_raise_hand_rating : Flag(0))
|
| (raisedHandRating > 0 ? Flag::f_raise_hand_rating : Flag(0))
|
||||||
| (_e2eState ? Flag::f_public_key : Flag(0));
|
| (_e2e ? Flag::f_public_key : Flag(0));
|
||||||
real->applyLocalUpdate(
|
real->applyLocalUpdate(
|
||||||
MTP_updateGroupCallParticipants(
|
MTP_updateGroupCallParticipants(
|
||||||
inputCall(),
|
inputCall(),
|
||||||
|
@ -1667,9 +1710,9 @@ void GroupCall::applyMeInCallLocally() {
|
||||||
MTP_long(raisedHandRating),
|
MTP_long(raisedHandRating),
|
||||||
MTPGroupCallParticipantVideo(),
|
MTPGroupCallParticipantVideo(),
|
||||||
MTPGroupCallParticipantVideo(),
|
MTPGroupCallParticipantVideo(),
|
||||||
MTP_int256(
|
(_e2e
|
||||||
MTP_int128(publicKey.a, publicKey.b),
|
? TdE2E::PublicKeyToMTP(_e2e->myKey())
|
||||||
MTP_int128(publicKey.c, publicKey.d)))),
|
: MTPint256()))),
|
||||||
MTP_int(0)).c_updateGroupCallParticipants());
|
MTP_int(0)).c_updateGroupCallParticipants());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2022,6 +2065,8 @@ void GroupCall::handleUpdate(const MTPUpdate &update) {
|
||||||
handleUpdate(data);
|
handleUpdate(data);
|
||||||
}, [&](const MTPDupdateGroupCallParticipants &data) {
|
}, [&](const MTPDupdateGroupCallParticipants &data) {
|
||||||
handleUpdate(data);
|
handleUpdate(data);
|
||||||
|
}, [&](const MTPDupdateGroupCallChainBlocks &data) {
|
||||||
|
handleUpdate(data);
|
||||||
}, [](const auto &) {
|
}, [](const auto &) {
|
||||||
Unexpected("Type in Instance::applyGroupCallUpdateChecked.");
|
Unexpected("Type in Instance::applyGroupCallUpdateChecked.");
|
||||||
});
|
});
|
||||||
|
@ -2063,6 +2108,33 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GroupCall::handleUpdate(const MTPDupdateGroupCallChainBlocks &data) {
|
||||||
|
const auto callId = data.vcall().match([](
|
||||||
|
const MTPDinputGroupCall &data) {
|
||||||
|
return data.vid().v;
|
||||||
|
}, [](const MTPDinputGroupCallSlug &) -> CallId {
|
||||||
|
Unexpected("inputGroupCallSlug in GroupCall::handleUpdate.");
|
||||||
|
});
|
||||||
|
if (_id != callId || !_e2e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto subchain = data.vsub_chain_id().v;
|
||||||
|
if (subchain < 0 || subchain >= kSubChainsCount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto &entry = _subchains[subchain];
|
||||||
|
entry.lastUpdate = crl::now();
|
||||||
|
entry.height = data.vnext_offset().v;
|
||||||
|
entry.timer.callOnce(kShortPollChainBlocksTimeout + 1);
|
||||||
|
for (const auto &block : data.vblocks().v) {
|
||||||
|
const auto result = _e2e->apply({ block.v });
|
||||||
|
if (result == TdE2E::Call::ApplyResult::BlockSkipped) {
|
||||||
|
AssertIsDebug();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GroupCall::applyQueuedSelfUpdates() {
|
void GroupCall::applyQueuedSelfUpdates() {
|
||||||
const auto weak = base::make_weak(this);
|
const auto weak = base::make_weak(this);
|
||||||
while (weak
|
while (weak
|
||||||
|
|
|
@ -44,7 +44,7 @@ class GroupCall;
|
||||||
|
|
||||||
namespace TdE2E {
|
namespace TdE2E {
|
||||||
struct ParticipantState;
|
struct ParticipantState;
|
||||||
struct CallState;
|
class Call;
|
||||||
} // namespace TdE2E
|
} // namespace TdE2E
|
||||||
|
|
||||||
namespace Calls {
|
namespace Calls {
|
||||||
|
@ -437,6 +437,7 @@ private:
|
||||||
struct SinkPointer;
|
struct SinkPointer;
|
||||||
|
|
||||||
static constexpr uint32 kDisabledSsrc = uint32(-1);
|
static constexpr uint32 kDisabledSsrc = uint32(-1);
|
||||||
|
static constexpr int kSubChainsCount = 2;
|
||||||
|
|
||||||
struct LoadingPart {
|
struct LoadingPart {
|
||||||
std::shared_ptr<LoadPartTask> task;
|
std::shared_ptr<LoadPartTask> task;
|
||||||
|
@ -475,6 +476,12 @@ private:
|
||||||
ssrc = updatedSsrc;
|
ssrc = updatedSsrc;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
struct SubChainState {
|
||||||
|
crl::time lastUpdate = 0;
|
||||||
|
base::Timer timer;
|
||||||
|
int height = 0;
|
||||||
|
mtpRequestId requestId = 0;
|
||||||
|
};
|
||||||
|
|
||||||
friend inline constexpr bool is_flag_type(SendUpdateType) {
|
friend inline constexpr bool is_flag_type(SendUpdateType) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -507,6 +514,7 @@ private:
|
||||||
void handlePossibleDiscarded(const MTPDgroupCallDiscarded &data);
|
void handlePossibleDiscarded(const MTPDgroupCallDiscarded &data);
|
||||||
void handleUpdate(const MTPDupdateGroupCall &data);
|
void handleUpdate(const MTPDupdateGroupCall &data);
|
||||||
void handleUpdate(const MTPDupdateGroupCallParticipants &data);
|
void handleUpdate(const MTPDupdateGroupCallParticipants &data);
|
||||||
|
void handleUpdate(const MTPDupdateGroupCallChainBlocks &data);
|
||||||
bool tryCreateController();
|
bool tryCreateController();
|
||||||
void destroyController();
|
void destroyController();
|
||||||
bool tryCreateScreencast();
|
bool tryCreateScreencast();
|
||||||
|
@ -535,6 +543,7 @@ private:
|
||||||
void rejoinPresentation();
|
void rejoinPresentation();
|
||||||
void leavePresentation();
|
void leavePresentation();
|
||||||
void checkNextJoinAction();
|
void checkNextJoinAction();
|
||||||
|
void checkChainBlocksRequest(int subchain);
|
||||||
|
|
||||||
void audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data);
|
void audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data);
|
||||||
void setInstanceConnected(tgcalls::GroupNetworkState networkState);
|
void setInstanceConnected(tgcalls::GroupNetworkState networkState);
|
||||||
|
@ -593,6 +602,8 @@ private:
|
||||||
|
|
||||||
const not_null<Delegate*> _delegate;
|
const not_null<Delegate*> _delegate;
|
||||||
const std::shared_ptr<Data::GroupCall> _conferenceCall;
|
const std::shared_ptr<Data::GroupCall> _conferenceCall;
|
||||||
|
std::shared_ptr<TdE2E::Call> _e2e;
|
||||||
|
|
||||||
not_null<PeerData*> _peer; // Can change in legacy group migration.
|
not_null<PeerData*> _peer; // Can change in legacy group migration.
|
||||||
rpl::event_stream<PeerData*> _peerStream;
|
rpl::event_stream<PeerData*> _peerStream;
|
||||||
not_null<History*> _history; // Can change in legacy group migration.
|
not_null<History*> _history; // Can change in legacy group migration.
|
||||||
|
@ -624,8 +635,6 @@ private:
|
||||||
int64 _serverTimeMs = 0;
|
int64 _serverTimeMs = 0;
|
||||||
crl::time _serverTimeMsGotAt = 0;
|
crl::time _serverTimeMsGotAt = 0;
|
||||||
|
|
||||||
std::unique_ptr<TdE2E::CallState> _e2eState;
|
|
||||||
|
|
||||||
QString _rtmpUrl;
|
QString _rtmpUrl;
|
||||||
QString _rtmpKey;
|
QString _rtmpKey;
|
||||||
|
|
||||||
|
@ -710,6 +719,8 @@ private:
|
||||||
bool _reloadedStaleCall = false;
|
bool _reloadedStaleCall = false;
|
||||||
int _rtmpVolume = 0;
|
int _rtmpVolume = 0;
|
||||||
|
|
||||||
|
SubChainState _subchains[kSubChainsCount];
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,6 +19,10 @@ namespace Ui {
|
||||||
class GenericBox;
|
class GenericBox;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace TdE2E {
|
||||||
|
class Call;
|
||||||
|
} // namespace TdE2E
|
||||||
|
|
||||||
namespace Calls::Group {
|
namespace Calls::Group {
|
||||||
|
|
||||||
constexpr auto kDefaultVolume = 10000;
|
constexpr auto kDefaultVolume = 10000;
|
||||||
|
@ -67,6 +71,7 @@ struct JoinInfo {
|
||||||
|
|
||||||
struct ConferenceInfo {
|
struct ConferenceInfo {
|
||||||
std::shared_ptr<Data::GroupCall> call;
|
std::shared_ptr<Data::GroupCall> call;
|
||||||
|
std::shared_ptr<TdE2E::Call> e2e;
|
||||||
QString linkSlug;
|
QString linkSlug;
|
||||||
MsgId joinMessageId;
|
MsgId joinMessageId;
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,6 +19,7 @@ struct ParticipantVideoParams;
|
||||||
|
|
||||||
namespace TdE2E {
|
namespace TdE2E {
|
||||||
struct ParticipantState;
|
struct ParticipantState;
|
||||||
|
struct UserId;
|
||||||
} // namespace TdE2E
|
} // namespace TdE2E
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
|
|
@ -12,20 +12,77 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include <tde2e/td/e2e/e2e_api.h>
|
#include <tde2e/td/e2e/e2e_api.h>
|
||||||
|
|
||||||
namespace TdE2E {
|
namespace TdE2E {
|
||||||
|
namespace {
|
||||||
|
|
||||||
CallState CreateCallState() {
|
constexpr auto kPermissionAdd = 1;
|
||||||
|
constexpr auto kPermissionRemove = 2;
|
||||||
|
|
||||||
|
[[nodiscard]] tde2e_api::Slice Slice(const QByteArray &data) {
|
||||||
|
return {
|
||||||
|
data.constData(),
|
||||||
|
std::string_view::size_type(data.size())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Call::Call(UserId myUserId)
|
||||||
|
: _myUserId(myUserId) {
|
||||||
const auto id = tde2e_api::key_generate_temporary_private_key();
|
const auto id = tde2e_api::key_generate_temporary_private_key();
|
||||||
Assert(id.is_ok());
|
Assert(id.is_ok());
|
||||||
const auto key = tde2e_api::key_to_public_key(id.value());
|
_myKeyId = { .v = uint64(id.value()) };
|
||||||
|
|
||||||
|
const auto key = tde2e_api::key_to_public_key(_myKeyId.v);
|
||||||
Assert(key.is_ok());
|
Assert(key.is_ok());
|
||||||
|
Assert(key.value().size() == sizeof(_myKey));
|
||||||
|
memcpy(&_myKey, key.value().data(), sizeof(_myKey));
|
||||||
|
}
|
||||||
|
|
||||||
auto result = CallState{
|
PublicKey Call::myKey() const {
|
||||||
.myKeyId = PrivateKeyId{ .v = uint64(id.value()) },
|
return _myKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block Call::makeZeroBlock() const {
|
||||||
|
const auto publicKeyView = std::string_view{
|
||||||
|
reinterpret_cast<const char*>(&_myKey),
|
||||||
|
sizeof(_myKey),
|
||||||
};
|
};
|
||||||
Assert(key.value().size() == sizeof(result.myKey));
|
const auto publicKeyId = tde2e_api::key_from_public_key(publicKeyView);
|
||||||
memcpy(&result.myKey, key.value().data(), 32);
|
Assert(publicKeyId.is_ok());
|
||||||
|
|
||||||
return result;
|
const auto myKeyId = std::int64_t(_myKeyId.v);
|
||||||
|
const auto result = tde2e_api::call_create_zero_block(myKeyId, {
|
||||||
|
.height = 0,
|
||||||
|
.participants = { {
|
||||||
|
.user_id = std::int64_t(_myUserId.v),
|
||||||
|
.public_key_id = publicKeyId.value(),
|
||||||
|
.permissions = kPermissionAdd | kPermissionRemove,
|
||||||
|
} },
|
||||||
|
});
|
||||||
|
Assert(result.is_ok());
|
||||||
|
|
||||||
|
return {
|
||||||
|
.data = QByteArray::fromStdString(result.value()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Call::create(const Block &last) {
|
||||||
|
tde2e_api::call_create(std::int64_t(_myKeyId.v), Slice(last.data));
|
||||||
|
}
|
||||||
|
|
||||||
|
Call::ApplyResult Call::apply(const Block &block) {
|
||||||
|
const auto result = tde2e_api::call_apply_block(
|
||||||
|
std::int64_t(_id.v),
|
||||||
|
Slice(block.data));
|
||||||
|
|
||||||
|
if (!result.is_ok()) {
|
||||||
|
const auto error = result.error();
|
||||||
|
(void)error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.is_ok()
|
||||||
|
? ApplyResult::Success
|
||||||
|
: ApplyResult::BlockSkipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace TdE2E
|
} // namespace TdE2E
|
||||||
|
|
|
@ -11,10 +11,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace TdE2E {
|
namespace TdE2E {
|
||||||
|
|
||||||
|
struct UserId {
|
||||||
|
uint64 v = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct PrivateKeyId {
|
struct PrivateKeyId {
|
||||||
uint64 v = 0;
|
uint64 v = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CallId {
|
||||||
|
uint64 v = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct PublicKey {
|
struct PublicKey {
|
||||||
uint64 a = 0;
|
uint64 a = 0;
|
||||||
uint64 b = 0;
|
uint64 b = 0;
|
||||||
|
@ -23,15 +31,36 @@ struct PublicKey {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ParticipantState {
|
struct ParticipantState {
|
||||||
uint64 id = 0;
|
UserId id;
|
||||||
PublicKey key;
|
PublicKey key;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CallState {
|
struct Block {
|
||||||
PrivateKeyId myKeyId;
|
QByteArray data;
|
||||||
PublicKey myKey;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] CallState CreateCallState();
|
class Call final {
|
||||||
|
public:
|
||||||
|
explicit Call(UserId myUserId);
|
||||||
|
|
||||||
|
[[nodiscard]] PublicKey myKey() const;
|
||||||
|
|
||||||
|
[[nodiscard]] Block makeZeroBlock() const;
|
||||||
|
|
||||||
|
void create(const Block &last);
|
||||||
|
|
||||||
|
enum class ApplyResult {
|
||||||
|
Success,
|
||||||
|
BlockSkipped
|
||||||
|
};
|
||||||
|
[[nodiscard]] ApplyResult apply(const Block &block);
|
||||||
|
|
||||||
|
private:
|
||||||
|
CallId _id;
|
||||||
|
UserId _myUserId;
|
||||||
|
PrivateKeyId _myKeyId;
|
||||||
|
PublicKey _myKey;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace TdE2E
|
} // namespace TdE2E
|
||||||
|
|
27
Telegram/SourceFiles/tde2e/tde2e_integration.cpp
Normal file
27
Telegram/SourceFiles/tde2e/tde2e_integration.cpp
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
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 "tde2e/tde2e_integration.h"
|
||||||
|
|
||||||
|
#include "data/data_user.h"
|
||||||
|
#include "tde2e/tde2e_api.h"
|
||||||
|
|
||||||
|
namespace TdE2E {
|
||||||
|
|
||||||
|
UserId MakeUserId(not_null<UserData*> user) {
|
||||||
|
return MakeUserId(peerToUser(user->id));
|
||||||
|
}
|
||||||
|
|
||||||
|
UserId MakeUserId(::UserId id) {
|
||||||
|
return { .v = id.bare };
|
||||||
|
}
|
||||||
|
|
||||||
|
MTPint256 PublicKeyToMTP(const PublicKey &key) {
|
||||||
|
return MTP_int256(MTP_int128(key.a, key.b), MTP_int128(key.c, key.d));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace TdE2E
|
20
Telegram/SourceFiles/tde2e/tde2e_integration.h
Normal file
20
Telegram/SourceFiles/tde2e/tde2e_integration.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
namespace TdE2E {
|
||||||
|
|
||||||
|
struct UserId;
|
||||||
|
struct PublicKey;
|
||||||
|
|
||||||
|
[[nodiscard]] UserId MakeUserId(not_null<UserData*> user);
|
||||||
|
[[nodiscard]] UserId MakeUserId(::UserId id);
|
||||||
|
|
||||||
|
[[nodiscard]] MTPint256 PublicKeyToMTP(const PublicKey &key);
|
||||||
|
|
||||||
|
} // namespace TdE2E
|
|
@ -14,17 +14,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "boxes/peer_list_controllers.h"
|
#include "boxes/peer_list_controllers.h"
|
||||||
#include "boxes/premium_preview_box.h"
|
#include "boxes/premium_preview_box.h"
|
||||||
#include "calls/calls_box_controller.h"
|
#include "calls/calls_box_controller.h"
|
||||||
|
#include "calls/calls_instance.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "core/click_handler_types.h"
|
#include "core/click_handler_types.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "data/data_document_media.h"
|
#include "data/data_document_media.h"
|
||||||
#include "data/data_folder.h"
|
#include "data/data_folder.h"
|
||||||
|
#include "data/data_group_call.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_stories.h"
|
#include "data/data_stories.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "info/info_memento.h"
|
#include "info/info_memento.h"
|
||||||
#include "info/profile/info_profile_badge.h"
|
#include "info/profile/info_profile_badge.h"
|
||||||
#include "info/profile/info_profile_emoji_status_panel.h"
|
#include "info/profile/info_profile_emoji_status_panel.h"
|
||||||
|
#include "info/profile/info_profile_icon.h"
|
||||||
#include "info/stories/info_stories_widget.h"
|
#include "info/stories/info_stories_widget.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "main/main_account.h"
|
#include "main/main_account.h"
|
||||||
|
@ -38,6 +41,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "storage/localstorage.h"
|
#include "storage/localstorage.h"
|
||||||
#include "storage/storage_account.h"
|
#include "storage/storage_account.h"
|
||||||
#include "support/support_templates.h"
|
#include "support/support_templates.h"
|
||||||
|
#include "tde2e/tde2e_api.h"
|
||||||
|
#include "tde2e/tde2e_integration.h"
|
||||||
#include "ui/boxes/confirm_box.h"
|
#include "ui/boxes/confirm_box.h"
|
||||||
#include "ui/chat/chat_theme.h"
|
#include "ui/chat/chat_theme.h"
|
||||||
#include "ui/controls/swipe_handler.h"
|
#include "ui/controls/swipe_handler.h"
|
||||||
|
@ -92,6 +97,66 @@ constexpr auto kPlayStatusLimit = 2;
|
||||||
|| (now.month() == 1 && now.day() == 1);
|
|| (now.month() == 1 && now.day() == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] not_null<Ui::SettingsButton*> AddCreateCallLinkButton(
|
||||||
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
Fn<void()> done) {
|
||||||
|
const auto result = container->add(object_ptr<Ui::SettingsButton>(
|
||||||
|
container,
|
||||||
|
tr::lng_confcall_create_link(),
|
||||||
|
st::inviteViaLinkButton), QMargins());
|
||||||
|
Ui::AddSkip(container);
|
||||||
|
Ui::AddDividerText(
|
||||||
|
container,
|
||||||
|
tr::lng_confcall_create_link_description(Ui::Text::WithEntities));
|
||||||
|
|
||||||
|
const auto icon = Ui::CreateChild<Info::Profile::FloatingIcon>(
|
||||||
|
result,
|
||||||
|
st::inviteViaLinkIcon,
|
||||||
|
QPoint());
|
||||||
|
result->heightValue(
|
||||||
|
) | rpl::start_with_next([=](int height) {
|
||||||
|
icon->moveToLeft(
|
||||||
|
st::inviteViaLinkIconPosition.x(),
|
||||||
|
(height - st::inviteViaLinkIcon.height()) / 2);
|
||||||
|
}, icon->lifetime());
|
||||||
|
|
||||||
|
const auto creating = result->lifetime().make_state<bool>();
|
||||||
|
result->setClickedCallback([=] {
|
||||||
|
if (*creating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*creating = true;
|
||||||
|
auto e2e = std::make_shared<TdE2E::Call>(
|
||||||
|
TdE2E::MakeUserId(controller->session().user()));
|
||||||
|
const auto session = &controller->session();
|
||||||
|
session->api().request(MTPphone_CreateConferenceCall(
|
||||||
|
TdE2E::PublicKeyToMTP(e2e->myKey()),
|
||||||
|
MTP_bytes(e2e->makeZeroBlock().data)
|
||||||
|
)).done(crl::guard(controller, [=](const MTPphone_GroupCall &result) {
|
||||||
|
result.data().vcall().match([&](const auto &data) {
|
||||||
|
const auto call = std::make_shared<Data::GroupCall>(
|
||||||
|
session->user(),
|
||||||
|
data.vid().v,
|
||||||
|
data.vaccess_hash().v,
|
||||||
|
TimeId(), // scheduleDate
|
||||||
|
false); // rtmp
|
||||||
|
call->processFullCall(result);
|
||||||
|
Core::App().calls().startOrJoinConferenceCall(
|
||||||
|
controller->uiShow(),
|
||||||
|
{ .call = call, .e2e = e2e });
|
||||||
|
});
|
||||||
|
if (const auto onstack = done) {
|
||||||
|
onstack();
|
||||||
|
}
|
||||||
|
})).fail(crl::guard(controller, [=](const MTP::Error &error) {
|
||||||
|
controller->uiShow()->showToast(error.type());
|
||||||
|
*creating = false;
|
||||||
|
})).send();
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void ShowCallsBox(not_null<Window::SessionController*> window) {
|
void ShowCallsBox(not_null<Window::SessionController*> window) {
|
||||||
struct State {
|
struct State {
|
||||||
State(not_null<Window::SessionController*> window)
|
State(not_null<Window::SessionController*> window)
|
||||||
|
@ -129,6 +194,17 @@ void ShowCallsBox(not_null<Window::SessionController*> window) {
|
||||||
Ui::AddDivider(groupCalls->entity());
|
Ui::AddDivider(groupCalls->entity());
|
||||||
Ui::AddSkip(groupCalls->entity());
|
Ui::AddSkip(groupCalls->entity());
|
||||||
|
|
||||||
|
const auto button = AddCreateCallLinkButton(
|
||||||
|
box->verticalLayout(),
|
||||||
|
window,
|
||||||
|
crl::guard(box, [=] { box->closeBox(); }));
|
||||||
|
button->events(
|
||||||
|
) | rpl::filter([=](not_null<QEvent*> e) {
|
||||||
|
return (e->type() == QEvent::Enter);
|
||||||
|
}) | rpl::start_with_next([=] {
|
||||||
|
state->callsDelegate.peerListMouseLeftGeometry();
|
||||||
|
}, button->lifetime());
|
||||||
|
|
||||||
const auto content = box->addRow(
|
const auto content = box->addRow(
|
||||||
object_ptr<PeerListContent>(box, &state->callsController),
|
object_ptr<PeerListContent>(box, &state->callsController),
|
||||||
{});
|
{});
|
||||||
|
|
Loading…
Add table
Reference in a new issue