Separate Call/GroupCall delegates and Instance.

This commit is contained in:
John Preston 2021-04-22 20:05:17 +04:00
parent 6e34360f7e
commit c93ddf6aac
23 changed files with 422 additions and 262 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 932 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 756 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -1107,7 +1107,9 @@ void PeerListContent::paintEvent(QPaintEvent *e) {
Painter p(this);
const auto clip = e->rect();
p.fillRect(clip, _st.item.button.textBg);
if (_mode != Mode::Custom) {
p.fillRect(clip, _st.item.button.textBg);
}
const auto repaintByStatusAfter = _repaintByStatus.remainingTime();
auto repaintAfterMin = repaintByStatusAfter;

View file

@ -736,7 +736,7 @@ groupCallMemberRaisedHand: icon {{ "calls/group_calls_raised_hand", groupCallMem
groupCallSettingsInner: IconButton(callButton) {
iconPosition: point(-1px, 22px);
icon: icon {{ "calls/call_settings", groupCallIconFg }};
icon: icon {{ "calls/calls_settings", groupCallIconFg }};
ripple: RippleAnimation(defaultRippleAnimation) {
color: callMuteRipple;
}
@ -1022,3 +1022,5 @@ desktopCaptureSourceSkip: 12px;
groupCallNarrowSkip: 9px;
groupCallNarrowRowSkip: 8px;
groupCallNarrowSize: size(90px, 90px);
groupCallWideModeWidthMin: 800px;
groupCallWideModeSize: size(960px, 520px);

View file

@ -161,8 +161,12 @@ Call::Call(
, _user(user)
, _api(&_user->session().mtp())
, _type(type)
, _videoIncoming(std::make_unique<Webrtc::VideoTrack>(StartVideoState(video)))
, _videoOutgoing(std::make_unique<Webrtc::VideoTrack>(StartVideoState(video))) {
, _videoIncoming(
std::make_unique<Webrtc::VideoTrack>(
StartVideoState(video)))
, _videoOutgoing(
std::make_unique<Webrtc::VideoTrack>(
StartVideoState(video))) {
_discardByTimeoutTimer.setCallback([=] { hangup(); });
if (_type == Type::Outgoing) {

View file

@ -53,6 +53,11 @@ struct Error {
QString details;
};
enum class CallType {
Incoming,
Outgoing,
};
class Call : public base::has_weak_ptr {
public:
class Delegate {
@ -81,11 +86,12 @@ public:
static constexpr auto kSoundSampleMs = 100;
enum class Type {
Incoming,
Outgoing,
};
Call(not_null<Delegate*> delegate, not_null<UserData*> user, Type type, bool video);
using Type = CallType;
Call(
not_null<Delegate*> delegate,
not_null<UserData*> user,
Type type,
bool video);
[[nodiscard]] Type type() const {
return _type;

View file

@ -7,7 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "calls/calls_instance.h"
#include "calls/calls_call.h"
#include "calls/group/calls_group_common.h"
#include "calls/group/calls_choose_join_as.h"
#include "calls/group/calls_group_call.h"
#include "mtproto/mtproto_dh_utils.h"
#include "core/application.h"
#include "main/main_session.h"
@ -41,9 +44,128 @@ namespace {
constexpr auto kServerConfigUpdateTimeoutMs = 24 * 3600 * crl::time(1000);
using CallSound = Call::Delegate::CallSound;
using GroupCallSound = GroupCall::Delegate::GroupCallSound;
} // namespace
Instance::Instance() = default;
class Instance::Delegate final
: public Call::Delegate
, public GroupCall::Delegate {
public:
explicit Delegate(not_null<Instance*> instance);
DhConfig getDhConfig() const override;
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,
bool video) override;
void callPlaySound(CallSound sound) override;
auto callGetVideoCapture()
-> std::shared_ptr<tgcalls::VideoCaptureInterface> override;
void groupCallFinished(not_null<GroupCall*> call) override;
void groupCallFailed(not_null<GroupCall*> call) override;
void groupCallRequestPermissionsOrFail(Fn<void()> onSuccess) override;
void groupCallPlaySound(GroupCallSound sound) override;
auto groupCallGetVideoCapture(const QString &deviceId)
-> std::shared_ptr<tgcalls::VideoCaptureInterface> override;
private:
const not_null<Instance*> _instance;
};
Instance::Delegate::Delegate(not_null<Instance*> instance)
: _instance(instance) {
}
DhConfig Instance::Delegate::getDhConfig() const {
return *_instance->_cachedDhConfig;
}
void Instance::Delegate::callFinished(not_null<Call*> call) {
crl::on_main(call, [=] {
_instance->destroyCall(call);
});
}
void Instance::Delegate::callFailed(not_null<Call*> call) {
crl::on_main(call, [=] {
_instance->destroyCall(call);
});
}
void Instance::Delegate::callRedial(not_null<Call*> call) {
if (_instance->_currentCall.get() == call) {
_instance->refreshDhConfig();
}
}
void Instance::Delegate::callRequestPermissionsOrFail(
Fn<void()> onSuccess,
bool video) {
_instance->requestPermissionsOrFail(std::move(onSuccess), video);
}
void Instance::Delegate::callPlaySound(CallSound sound) {
_instance->playSoundOnce([&] {
switch (sound) {
case CallSound::Busy: return "call_busy";
case CallSound::Ended: return "call_end";
case CallSound::Connecting: return "call_connect";
}
Unexpected("CallSound in Instance::callPlaySound.");
}());
}
auto Instance::Delegate::callGetVideoCapture()
-> std::shared_ptr<tgcalls::VideoCaptureInterface> {
return _instance->getVideoCapture();
}
void Instance::Delegate::groupCallFinished(not_null<GroupCall*> call) {
crl::on_main(call, [=] {
_instance->destroyGroupCall(call);
});
}
void Instance::Delegate::groupCallFailed(not_null<GroupCall*> call) {
crl::on_main(call, [=] {
_instance->destroyGroupCall(call);
});
}
void Instance::Delegate::groupCallRequestPermissionsOrFail(
Fn<void()> onSuccess) {
_instance->requestPermissionsOrFail(std::move(onSuccess), false);
}
void Instance::Delegate::groupCallPlaySound(GroupCallSound sound) {
_instance->playSoundOnce([&] {
switch (sound) {
case GroupCallSound::Started: return "group_call_start";
case GroupCallSound::Ended: return "group_call_end";
case GroupCallSound::AllowedToSpeak: return "group_call_allowed";
case GroupCallSound::Connecting: return "group_call_connect";
}
Unexpected("GroupCallSound in Instance::groupCallPlaySound.");
}());
}
auto Instance::Delegate::groupCallGetVideoCapture(const QString &deviceId)
-> std::shared_ptr<tgcalls::VideoCaptureInterface> {
return _instance->getVideoCapture(deviceId);
}
Instance::Instance()
: _delegate(std::make_unique<Delegate>(this))
, _cachedDhConfig(std::make_unique<DhConfig>())
, _chooseJoinAs(std::make_unique<Group::ChooseJoinAsProcess>()) {
}
Instance::~Instance() = default;
@ -72,7 +194,7 @@ void Instance::startOrJoinGroupCall(
: peer->groupCall()
? Group::ChooseJoinAsProcess::Context::Join
: Group::ChooseJoinAsProcess::Context::Create;
_chooseJoinAs.start(peer, context, [=](object_ptr<Ui::BoxContent> box) {
_chooseJoinAs->start(peer, context, [=](object_ptr<Ui::BoxContent> box) {
Ui::show(std::move(box), Ui::LayerOption::KeepOther);
}, [=](QString text) {
Ui::Toast::Show(text);
@ -85,36 +207,6 @@ void Instance::startOrJoinGroupCall(
});
}
void Instance::callFinished(not_null<Call*> call) {
crl::on_main(call, [=] {
destroyCall(call);
});
}
void Instance::callFailed(not_null<Call*> call) {
crl::on_main(call, [=] {
destroyCall(call);
});
}
void Instance::callRedial(not_null<Call*> call) {
if (_currentCall.get() == call) {
refreshDhConfig();
}
}
void Instance::groupCallFinished(not_null<GroupCall*> call) {
crl::on_main(call, [=] {
destroyGroupCall(call);
});
}
void Instance::groupCallFailed(not_null<GroupCall*> call) {
crl::on_main(call, [=] {
destroyGroupCall(call);
});
}
not_null<Media::Audio::Track*> Instance::ensureSoundLoaded(
const QString &key) {
const auto i = _tracks.find(key);
@ -132,31 +224,6 @@ void Instance::playSoundOnce(const QString &key) {
ensureSoundLoaded(key)->playOnce();
}
void Instance::callPlaySound(CallSound sound) {
playSoundOnce([&] {
switch (sound) {
case CallSound::Busy: return "call_busy";
case CallSound::Ended: return "call_end";
case CallSound::Connecting: return "call_connect";
}
Unexpected("CallSound in Instance::callPlaySound.");
return "";
}());
}
void Instance::groupCallPlaySound(GroupCallSound sound) {
playSoundOnce([&] {
switch (sound) {
case GroupCallSound::Started: return "group_call_start";
case GroupCallSound::Ended: return "group_call_end";
case GroupCallSound::AllowedToSpeak: return "group_call_allowed";
case GroupCallSound::Connecting: return "group_call_connect";
}
Unexpected("GroupCallSound in Instance::groupCallPlaySound.");
return "";
}());
}
void Instance::destroyCall(not_null<Call*> call) {
if (_currentCall.get() == call) {
_currentCallPanel->closeBeforeDestroy();
@ -174,7 +241,7 @@ void Instance::destroyCall(not_null<Call*> call) {
}
void Instance::createCall(not_null<UserData*> user, Call::Type type, bool video) {
auto call = std::make_unique<Call>(getCallDelegate(), user, type, video);
auto call = std::make_unique<Call>(_delegate.get(), user, type, video);
const auto raw = call.get();
user->session().account().sessionChanges(
@ -217,7 +284,7 @@ void Instance::createGroupCall(
destroyCurrentCall();
auto call = std::make_unique<GroupCall>(
getGroupCallDelegate(),
_delegate.get(),
std::move(info),
inputCall);
const auto raw = call.get();
@ -237,7 +304,7 @@ void Instance::refreshDhConfig() {
const auto weak = base::make_weak(_currentCall);
_currentCall->user()->session().api().request(MTPmessages_GetDhConfig(
MTP_int(_dhConfig.version),
MTP_int(_cachedDhConfig->version),
MTP_int(MTP::ModExpFirst::kRandomPowerSize)
)).done([=](const MTPmessages_DhConfig &result) {
const auto call = weak.get();
@ -249,14 +316,14 @@ void Instance::refreshDhConfig() {
Assert(random.size() == MTP::ModExpFirst::kRandomPowerSize);
call->start(random);
} else {
callFailed(call);
_delegate->callFailed(call);
}
}).fail([=](const MTP::Error &error) {
const auto call = weak.get();
if (!call) {
return;
}
callFailed(call);
_delegate->callFailed(call);
}).send();
}
@ -277,13 +344,13 @@ bytes::const_span Instance::updateDhConfig(
} else if (!validRandom(data.vrandom().v)) {
return {};
}
_dhConfig.g = data.vg().v;
_dhConfig.p = std::move(primeBytes);
_dhConfig.version = data.vversion().v;
_cachedDhConfig->g = data.vg().v;
_cachedDhConfig->p = std::move(primeBytes);
_cachedDhConfig->version = data.vversion().v;
return bytes::make_span(data.vrandom().v);
}, [&](const MTPDmessages_dhConfigNotModified &data)
-> bytes::const_span {
if (!_dhConfig.g || _dhConfig.p.empty()) {
if (!_cachedDhConfig->g || _cachedDhConfig->p.empty()) {
LOG(("API Error: dhConfigNotModified on zero version."));
return {};
} else if (!validRandom(data.vrandom().v)) {
@ -592,22 +659,19 @@ void Instance::requestPermissionOrFail(Platform::PermissionType type, Fn<void()>
}
}
std::shared_ptr<tgcalls::VideoCaptureInterface> Instance::callGetVideoCapture() {
return getVideoCapture();
}
std::shared_ptr<tgcalls::VideoCaptureInterface> Instance::groupCallGetVideoCapture() {
return getVideoCapture();
}
std::shared_ptr<tgcalls::VideoCaptureInterface> Instance::getVideoCapture() {
std::shared_ptr<tgcalls::VideoCaptureInterface> Instance::getVideoCapture(
QString deviceId) {
if (deviceId.isEmpty()) {
deviceId = Core::App().settings().callVideoInputDeviceId();
}
if (auto result = _videoCapture.lock()) {
result->switchToDevice(deviceId.toStdString());
return result;
}
auto result = std::shared_ptr<tgcalls::VideoCaptureInterface>(
tgcalls::VideoCaptureInterface::Create(
tgcalls::StaticThreads::getThreads(),
Core::App().settings().callVideoInputDeviceId().toStdString()));
deviceId.toStdString()));
_videoCapture = result;
return result;
}

View file

@ -8,9 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "mtproto/sender.h"
#include "calls/calls_call.h"
#include "calls/group/calls_group_call.h"
#include "calls/group/calls_choose_join_as.h"
namespace Platform {
enum class PermissionType;
@ -27,17 +24,22 @@ class Session;
namespace Calls::Group {
struct JoinInfo;
class Panel;
class ChooseJoinAsProcess;
} // namespace Calls::Group
namespace tgcalls {
class VideoCaptureInterface;
} // namespace tgcalls
namespace Calls {
class Call;
enum class CallType;
class GroupCall;
class Panel;
struct DhConfig;
class Instance
: private Call::Delegate
, private GroupCall::Delegate
, private base::Subscriber
, public base::has_weak_ptr {
class Instance : private base::Subscriber, public base::has_weak_ptr {
public:
Instance();
~Instance();
@ -69,7 +71,8 @@ public:
bool activateCurrentCall(const QString &joinHash = QString());
bool minimizeCurrentActiveCall();
bool closeCurrentActiveCall();
std::shared_ptr<tgcalls::VideoCaptureInterface> getVideoCapture();
[[nodiscard]] auto getVideoCapture(QString deviceId = QString())
-> std::shared_ptr<tgcalls::VideoCaptureInterface>;
void requestPermissionsOrFail(Fn<void()> onSuccess, bool video = true);
void setCurrentAudioDevice(bool input, const QString &deviceId);
@ -77,44 +80,13 @@ public:
[[nodiscard]] bool isQuitPrevent();
private:
using CallSound = Call::Delegate::CallSound;
using GroupCallSound = GroupCall::Delegate::GroupCallSound;
[[nodiscard]] not_null<Call::Delegate*> getCallDelegate() {
return static_cast<Call::Delegate*>(this);
}
[[nodiscard]] not_null<GroupCall::Delegate*> getGroupCallDelegate() {
return static_cast<GroupCall::Delegate*>(this);
}
[[nodiscard]] DhConfig getDhConfig() const override {
return _dhConfig;
}
class Delegate;
friend class Delegate;
not_null<Media::Audio::Track*> ensureSoundLoaded(const QString &key);
void playSoundOnce(const QString &key);
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,
bool video) override {
requestPermissionsOrFail(std::move(onSuccess), video);
}
void callPlaySound(CallSound sound) override;
auto callGetVideoCapture()
->std::shared_ptr<tgcalls::VideoCaptureInterface> override;
void groupCallFinished(not_null<GroupCall*> call) override;
void groupCallFailed(not_null<GroupCall*> call) override;
void groupCallRequestPermissionsOrFail(Fn<void()> onSuccess) override {
requestPermissionsOrFail(std::move(onSuccess), false);
}
void groupCallPlaySound(GroupCallSound sound) override;
auto groupCallGetVideoCapture()
->std::shared_ptr<tgcalls::VideoCaptureInterface> override;
void createCall(not_null<UserData*> user, Call::Type type, bool video);
void createCall(not_null<UserData*> user, CallType type, bool video);
void destroyCall(not_null<Call*> call);
void createGroupCall(
@ -141,7 +113,8 @@ private:
not_null<Main::Session*> session,
const MTPUpdate &update);
DhConfig _dhConfig;
const std::unique_ptr<Delegate> _delegate;
const std::unique_ptr<DhConfig> _cachedDhConfig;
crl::time _lastServerConfigUpdateTime = 0;
base::weak_ptr<Main::Session> _serverConfigRequestSession;
@ -157,7 +130,7 @@ private:
base::flat_map<QString, std::unique_ptr<Media::Audio::Track>> _tracks;
Group::ChooseJoinAsProcess _chooseJoinAs;
const std::unique_ptr<Group::ChooseJoinAsProcess> _chooseJoinAs;
};

View file

@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/calls_call.h"
#include "calls/calls_instance.h"
#include "calls/calls_signal_bars.h"
#include "calls/group/calls_group_call.h"
#include "calls/group/calls_group_menu.h" // Group::LeaveBox.
#include "history/view/history_view_group_call_tracker.h" // ContentByCall.
#include "data/data_user.h"

View file

@ -313,8 +313,8 @@ GroupCall::GroupCall(
, _joinHash(info.joinHash)
, _id(inputCall.c_inputGroupCall().vid().v)
, _scheduleDate(info.scheduleDate)
, _videoOutgoing(std::make_unique<Webrtc::VideoTrack>(
Webrtc::VideoState::Inactive))
, _videoOutgoing(
std::make_unique<Webrtc::VideoTrack>(Webrtc::VideoState::Inactive))
, _lastSpokeCheckTimer([=] { checkLastSpoke(); })
, _checkJoinedTimer([=] { checkJoined(); })
, _pushToTalkCancelTimer([=] { pushToTalkCancel(); })
@ -402,6 +402,7 @@ void GroupCall::switchToCamera() {
return;
}
_videoDeviceId = _videoInputId;
if (_videoOutgoing)
_videoCapture->switchToDevice(_videoDeviceId.toStdString());
}
@ -1366,10 +1367,42 @@ void GroupCall::setupMediaDevices() {
}
void GroupCall::setupOutgoingVideo() {
_videoCapture = _delegate->groupCallGetVideoCapture();
_videoOutgoing->setState(Webrtc::VideoState::Active);
_videoCapture->setOutput(_videoOutgoing->sink());
_videoDeviceId = _videoInputId;
static const auto hasDevices = [] {
return !Webrtc::GetVideoInputList().empty();
};
const auto started = _videoOutgoing->state();
if (!hasDevices()) {
_videoOutgoing->setState(Webrtc::VideoState::Inactive);
}
_videoOutgoing->stateValue(
) | rpl::start_with_next([=](Webrtc::VideoState state) {
if (state != Webrtc::VideoState::Inactive && !hasDevices()) {
//_errors.fire({ ErrorType::NoCamera }); // #TODO videochats
_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->groupCallGetVideoCapture(
_videoDeviceId);
_videoCapture->setOutput(_videoOutgoing->sink());
} else {
_videoCapture->switchToDevice(_videoDeviceId.toStdString());
}
if (_instance) {
_instance->setVideoCapture(_videoCapture, nullptr);
}
_videoCapture->setState(tgcalls::VideoState::Active);
} else if (_videoCapture) {
_videoCapture->setState(tgcalls::VideoState::Inactive);
}
}, _lifetime);
}
void GroupCall::changeTitle(const QString &title) {
@ -1424,6 +1457,7 @@ void GroupCall::ensureControllerCreated() {
const auto weak = base::make_weak(this);
const auto myLevel = std::make_shared<tgcalls::GroupLevelValue>();
_videoCall = true;
tgcalls::GroupInstanceDescriptor descriptor = {
.threads = tgcalls::StaticThreads::getThreads(),
.config = tgcalls::GroupConfig{
@ -1451,13 +1485,10 @@ void GroupCall::ensureControllerCreated() {
.createAudioDeviceModule = Webrtc::AudioDeviceModuleCreator(
settings.callAudioBackend()),
.videoCapture = _videoCapture,
//.getVideoSource = [=] {
// return _videoCapture->
//},
.incomingVideoSourcesUpdated = [=](
const std::vector<uint32_t> &ssrcs) {
crl::on_main(weak, [=] {
showVideoStreams(ssrcs);
setVideoStreams(ssrcs);
});
},
.participantDescriptionsRequired = [=](
@ -1609,9 +1640,53 @@ void GroupCall::requestParticipantsInformation(
addPreparedParticipants();
}
void GroupCall::showVideoStreams(const std::vector<std::uint32_t> &ssrcs) {
void GroupCall::setVideoStreams(const std::vector<std::uint32_t> &ssrcs) {
const auto large = _videoStreamLarge.current();
auto newLarge = large;
if (large && !ranges::contains(ssrcs, large)) {
newLarge = 0;
_videoStreamPinned = 0;
}
auto lastSpokeVoice = crl::time(0);
auto lastSpokeVoiceSsrc = uint32(0);
auto lastSpokeAnything = crl::time(0);
auto lastSpokeAnythingSsrc = uint32(0);
auto removed = _videoStreamSsrcs;
for (const auto ssrc : ssrcs) {
_videoStreamUpdated.fire_copy(ssrc);
const auto i = removed.find(ssrc);
if (i != end(removed)) {
removed.erase(i);
} else {
_videoStreamSsrcs.emplace(ssrc);
_streamsVideoUpdated.fire({ ssrc, true });
}
if (!newLarge) {
const auto j = _lastSpoke.find(ssrc);
if (j != end(_lastSpoke)) {
if (!lastSpokeVoiceSsrc
|| lastSpokeVoice < j->second.voice) {
lastSpokeVoiceSsrc = ssrc;
lastSpokeVoice = j->second.voice;
}
if (!lastSpokeAnythingSsrc
|| lastSpokeAnything < j->second.anything) {
lastSpokeAnythingSsrc = ssrc;
lastSpokeAnything = j->second.anything;
}
}
}
}
if (!newLarge) {
_videoStreamLarge = lastSpokeVoiceSsrc
? lastSpokeVoiceSsrc
: lastSpokeAnythingSsrc
? lastSpokeAnythingSsrc
: ssrcs.empty()
? 0
: ssrcs.front();
}
for (const auto ssrc : removed) {
_streamsVideoUpdated.fire({ ssrc, false });
}
}
@ -2035,45 +2110,6 @@ auto GroupCall::otherParticipantStateValue() const
return _otherParticipantStateValue.events();
}
//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 MTP::Error &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);
@ -2084,9 +2120,6 @@ MTPInputGroupCall GroupCall::inputCall() const {
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."));

View file

@ -74,6 +74,11 @@ struct LevelUpdate {
bool me = false;
};
struct StreamsVideoUpdate {
uint32 ssrc = 0;
bool streams = false;
};
struct VideoParams;
[[nodiscard]] std::shared_ptr<VideoParams> ParseVideoParams(
@ -100,7 +105,7 @@ public:
Ended,
};
virtual void groupCallPlaySound(GroupCallSound sound) = 0;
virtual auto groupCallGetVideoCapture()
virtual auto groupCallGetVideoCapture(const QString &deviceId)
-> std::shared_ptr<tgcalls::VideoCaptureInterface> = 0;
};
@ -158,6 +163,13 @@ public:
return _muted.value();
}
[[nodiscard]] bool videoCall() const {
return _videoCall.current();
}
[[nodiscard]] rpl::producer<bool> videoCallValue() const {
return _videoCall.value();
}
[[nodiscard]] auto otherParticipantStateValue() const
-> rpl::producer<Group::ParticipantState>;
@ -194,8 +206,15 @@ public:
[[nodiscard]] rpl::producer<LevelUpdate> levelUpdates() const {
return _levelUpdates.events();
}
[[nodiscard]] rpl::producer<uint32> videoStreamUpdated() const {
return _videoStreamUpdated.events();
[[nodiscard]] auto streamsVideoUpdates() const
-> rpl::producer<StreamsVideoUpdate> {
return _streamsVideoUpdated.events();
}
[[nodiscard]] uint32 videoStreamLarge() const {
return _videoStreamLarge.current();
}
[[nodiscard]] rpl::producer<uint32> videoStreamLargeValue() const {
return _videoStreamLarge.value();
}
[[nodiscard]] rpl::producer<Group::RejoinEvent> rejoinEvents() const {
return _rejoinEvents.events();
@ -213,8 +232,6 @@ public:
bool isScreenSharing() const;
void switchToCamera();
void switchToScreenSharing(const QString &uniqueId);
//void setAudioVolume(bool input, float level);
void setAudioDuckingEnabled(bool enabled);
void toggleMute(const Group::MuteRequest &data);
void changeVolume(const Group::VolumeRequest &data);
@ -264,8 +281,6 @@ private:
void handlePossibleDiscarded(const MTPDgroupCallDiscarded &data);
void handleUpdate(const MTPDupdateGroupCall &data);
void handleUpdate(const MTPDupdateGroupCallParticipants &data);
void handleRequestError(const MTP::Error &error);
void handleControllerError(const QString &error);
void ensureControllerCreated();
void destroyController();
@ -304,7 +319,7 @@ private:
const Data::GroupCallParticipant &participant);
void addPreparedParticipants();
void addPreparedParticipantsDelayed();
void showVideoStreams(const std::vector<std::uint32_t> &ssrcs);
void setVideoStreams(const std::vector<std::uint32_t> &ssrcs);
void editParticipant(
not_null<PeerData*> participantPeer,
@ -347,6 +362,7 @@ private:
QString _joinHash;
rpl::variable<MuteState> _muted = MuteState::Muted;
rpl::variable<bool> _videoCall = false;
bool _initialMuteStateSent = false;
bool _acceptFields = false;
@ -365,7 +381,10 @@ private:
std::shared_ptr<tgcalls::VideoCaptureInterface> _videoCapture;
const std::unique_ptr<Webrtc::VideoTrack> _videoOutgoing;
rpl::event_stream<LevelUpdate> _levelUpdates;
rpl::event_stream<uint32> _videoStreamUpdated;
rpl::event_stream<StreamsVideoUpdate> _streamsVideoUpdated;
base::flat_set<uint32> _videoStreamSsrcs;
rpl::variable<uint32> _videoStreamLarge = 0;
uint32 _videoStreamPinned = 0;
base::flat_map<uint32, Data::LastSpokeTimes> _lastSpoke;
rpl::event_stream<Group::RejoinEvent> _rejoinEvents;
rpl::event_stream<> _allowedToSpeakNotifications;

View file

@ -47,4 +47,9 @@ struct JoinInfo {
TimeId scheduleDate = 0;
};
enum class PanelMode {
Default,
Wide,
};
} // namespace Calls::Group

View file

@ -143,6 +143,7 @@ public:
}
[[nodiscard]] not_null<Webrtc::VideoTrack*> createVideoTrack();
void clearVideoTrack();
void setVideoTrack(not_null<Webrtc::VideoTrack*> track);
void addActionRipple(QPoint point, Fn<void()> updateCallback) override;
@ -352,12 +353,14 @@ private:
not_null<Row*> row,
uint64 raiseHandRating) const;
Row *findRow(not_null<PeerData*> participantPeer) const;
Row *findRow(uint32 audioSsrc) const;
void appendInvitedUsers();
void scheduleRaisedHandStatusRemove();
const not_null<GroupCall*> _call;
not_null<PeerData*> _peer;
uint32 _largeSsrc = 0;
bool _prepared = false;
rpl::event_stream<MuteRequest> _toggleMuteRequests;
@ -926,6 +929,13 @@ not_null<Webrtc::VideoTrack*> Row::createVideoTrack() {
return _videoTrack.get();
}
void Row::clearVideoTrack() {
_videoTrackLifetime.destroy();
_videoTrackShown = nullptr;
_videoTrack = nullptr;
_delegate->rowUpdateRow(this);
}
void Row::setVideoTrack(not_null<Webrtc::VideoTrack*> track) {
_videoTrackLifetime.destroy();
_videoTrackShown = track;
@ -1043,17 +1053,29 @@ void MembersController::setupListChangeViewers() {
}
}, _lifetime);
_call->videoStreamUpdated(
) | rpl::start_with_next([=](uint32 ssrc) {
const auto real = _call->lookupReal();
const auto participantPeer = real
? real->participantPeerByAudioSsrc(ssrc)
: nullptr;
const auto row = participantPeer
? findRow(participantPeer)
: nullptr;
if (row) {
_call->addVideoOutput(ssrc, row->createVideoTrack());
_call->videoStreamLargeValue(
) | rpl::filter([=](uint32 largeSsrc) {
return (_largeSsrc != largeSsrc);
}) | rpl::start_with_next([=](uint32 largeSsrc) {
if (const auto row = findRow(_largeSsrc)) {
_call->addVideoOutput(_largeSsrc, row->createVideoTrack());
}
_largeSsrc = largeSsrc;
if (const auto row = findRow(_largeSsrc)) {
row->clearVideoTrack();
}
}, _lifetime);
_call->streamsVideoUpdates(
) | rpl::start_with_next([=](StreamsVideoUpdate update) {
Assert(update.ssrc != _largeSsrc);
if (const auto row = findRow(update.ssrc)) {
if (update.streams) {
_call->addVideoOutput(update.ssrc, row->createVideoTrack());
} else {
row->clearVideoTrack();
}
}
}, _lifetime);
@ -1345,8 +1367,6 @@ void MembersController::updateRow(
}
if (isMe(row->peer())) {
row->setVideoTrack(_call->outgoingVideoTrack());
} else if (nowSsrc) {
_call->addVideoOutput(nowSsrc, row->createVideoTrack());
}
}
const auto nowNoSounding = _soundingRowBySsrc.empty();
@ -1378,6 +1398,17 @@ Row *MembersController::findRow(not_null<PeerData*> participantPeer) const {
delegate()->peerListFindRow(participantPeer->id.value));
}
Row *MembersController::findRow(uint32 audioSsrc) const {
if (!audioSsrc) {
return nullptr;
}
const auto real = _call->lookupReal();
const auto participantPeer = real
? real->participantPeerByAudioSsrc(audioSsrc)
: nullptr;
return participantPeer ? findRow(participantPeer) : nullptr;
}
Main::Session &MembersController::session() const {
return _call->peer()->session();
}
@ -1943,8 +1974,11 @@ int Members::desiredHeight() const {
return 0;
}();
const auto use = std::max(count, _list->fullRowsCount());
const auto single = (_mode == PanelMode::Wide)
? (st::groupCallNarrowSize.height() + st::groupCallNarrowRowSkip)
: st::groupCallMembersList.item.height;
return top
+ (use * st::groupCallMembersList.item.height)
+ (use * single)
+ (use ? st::lineWidth : 0);
}
@ -2008,6 +2042,16 @@ void Members::setupAddMember(not_null<GroupCall*> call) {
}, lifetime());
}
void Members::setMode(PanelMode mode) {
if (_mode == mode) {
return;
}
_mode = mode;
_list->setMode((_mode == PanelMode::Wide)
? PeerListContent::Mode::Custom
: PeerListContent::Mode::Default);
}
rpl::producer<int> Members::fullCountValue() const {
return static_cast<MembersController*>(
_listController.get())->fullCountValue();

View file

@ -26,6 +26,7 @@ namespace Calls::Group {
struct VolumeRequest;
struct MuteRequest;
enum class PanelMode;
class Members final
: public Ui::RpWidget
@ -48,6 +49,8 @@ public:
return _addMemberRequests.events();
}
void setMode(PanelMode mode);
private:
using ListWidget = PeerListContent;
@ -76,6 +79,7 @@ private:
void updateControlsGeometry();
const not_null<GroupCall*> _call;
PanelMode _mode = PanelMode();
object_ptr<Ui::ScrollArea> _scroll;
std::unique_ptr<PeerListController> _listController;
object_ptr<Ui::SettingsButton> _addMember = { nullptr };

View file

@ -525,6 +525,11 @@ void Panel::initWindow() {
? (Flag::Move | Flag::Maximize)
: Flag::None;
});
_call->videoCallValue(
) | rpl::start_with_next([=] {
updateMode();
}, _window->lifetime());
}
void Panel::initWidget() {
@ -536,8 +541,10 @@ void Panel::initWidget() {
}, widget()->lifetime());
widget()->sizeValue(
) | rpl::skip(1) | rpl::start_with_next([=] {
updateControlsGeometry();
) | rpl::skip(1) | rpl::start_with_next([=](QSize size) {
if (!updateMode()) {
updateControlsGeometry();
}
// title geometry depends on _controls->geometry,
// which is not updated here yet.
@ -1384,6 +1391,21 @@ QRect Panel::computeTitleRect() const {
#endif // !Q_OS_MAC
}
bool Panel::updateMode() {
const auto wide = _call->videoCall()
&& (widget()->width() >= st::groupCallWideModeWidthMin);
const auto mode = wide ? PanelMode::Wide : PanelMode::Default;
if (_mode == mode) {
return false;
}
_mode = mode;
if (_members) {
_members->setMode(mode);
}
updateControlsGeometry();
return true;
}
void Panel::updateControlsGeometry() {
if (widget()->size().isEmpty() || (!_settings && !_share)) {
return;
@ -1434,27 +1456,35 @@ void Panel::updateMembersGeometry() {
if (!_members) {
return;
}
const auto muteTop = widget()->height() - st::groupCallMuteBottomSkip;
const auto membersTop = st::groupCallMembersTop;
const auto availableHeight = muteTop
- membersTop
- st::groupCallMembersMargin.bottom();
const auto desiredHeight = _members->desiredHeight();
const auto membersWidthAvailable = widget()->width()
- st::groupCallMembersMargin.left()
- st::groupCallMembersMargin.right();
const auto membersWidthMin = st::groupCallWidth
- st::groupCallMembersMargin.left()
- st::groupCallMembersMargin.right();
const auto membersWidth = std::clamp(
membersWidthAvailable,
membersWidthMin,
st::groupCallMembersWidthMax);
_members->setGeometry(
(widget()->width() - membersWidth) / 2,
membersTop,
membersWidth,
std::min(desiredHeight, availableHeight));
if (_mode == PanelMode::Wide) {
_members->setGeometry(
st::groupCallNarrowSkip,
0,
st::groupCallNarrowSize.width(),
std::min(desiredHeight, widget()->height()));
} else {
const auto muteTop = widget()->height() - st::groupCallMuteBottomSkip;
const auto membersTop = st::groupCallMembersTop;
const auto availableHeight = muteTop
- membersTop
- st::groupCallMembersMargin.bottom();
const auto membersWidthAvailable = widget()->width()
- st::groupCallMembersMargin.left()
- st::groupCallMembersMargin.right();
const auto membersWidthMin = st::groupCallWidth
- st::groupCallMembersMargin.left()
- st::groupCallMembersMargin.right();
const auto membersWidth = std::clamp(
membersWidthAvailable,
membersWidthMin,
st::groupCallMembersWidthMax);
_members->setGeometry(
(widget()->width() - membersWidth) / 2,
membersTop,
membersWidth,
std::min(desiredHeight, availableHeight));
}
}
void Panel::refreshTitle() {

View file

@ -53,6 +53,7 @@ struct CallBodyLayout;
namespace Calls::Group {
class Members;
enum class PanelMode;
class Panel final : private Ui::DesktopCapture::ChooseSourceDelegate {
public:
@ -88,6 +89,7 @@ private:
bool handleClose();
void startScheduledNow();
bool updateMode();
void updateControlsGeometry();
void updateMembersGeometry();
void showControls();
@ -118,6 +120,7 @@ private:
const std::unique_ptr<Ui::Window> _window;
const std::unique_ptr<Ui::LayerManager> _layerBg;
PanelMode _mode = PanelMode();
#ifndef Q_OS_MAC
std::unique_ptr<Ui::Platform::TitleControls> _controls;

View file

@ -93,8 +93,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h"
#include "core/changelogs.h"
#include "base/unixtime.h"
#include "calls/calls_call.h"
#include "calls/calls_instance.h"
#include "calls/calls_top_bar.h"
#include "calls/group/calls_group_call.h"
#include "export/export_settings.h"
#include "export/export_manager.h"
#include "export/view/export_view_top_bar.h"

View file

@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "calls/calls_call.h"
#include "calls/calls_instance.h"
#include "calls/calls_video_bubble.h"
#include "webrtc/webrtc_media_devices.h"
@ -250,40 +251,6 @@ void Calls::setupContent() {
AddSkip(content);
AddSubsectionTitle(content, tr::lng_settings_call_section_other());
//#if defined Q_OS_MAC && !defined OS_MAC_STORE
// AddButton(
// content,
// tr::lng_settings_call_audio_ducking(),
// st::settingsButton
// )->toggleOn(
// rpl::single(settings.callAudioDuckingEnabled())
// )->toggledValue() | rpl::filter([](bool enabled) {
// return (enabled != Core::App().settings().callAudioDuckingEnabled());
// }) | rpl::start_with_next([=](bool enabled) {
// Core::App().settings().setCallAudioDuckingEnabled(enabled);
// Core::App().saveSettingsDelayed();
// if (const auto call = Core::App().calls().currentCall()) {
// call->setAudioDuckingEnabled(enabled);
// }
// }, content->lifetime());
//#endif // Q_OS_MAC && !OS_MAC_STORE
//const auto backend = [&]() -> QString {
// using namespace Webrtc;
// switch (settings.callAudioBackend()) {
// case Backend::OpenAL: return "OpenAL";
// case Backend::ADM: return "WebRTC ADM";
// case Backend::ADM2: return "WebRTC ADM2";
// }
// Unexpected("Value in backend.");
//}();
//AddButton(
// content,
// rpl::single("Call audio backend: " + backend),
// st::settingsButton
//)->addClickHandler([] {
// Ui::show(ChooseAudioBackendBox());
//});
AddButton(
content,
tr::lng_settings_call_accept_calls(),

View file

@ -42,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/toast/toast.h"
#include "ui/toasts/common_toasts.h"
#include "calls/calls_instance.h" // Core::App().calls().inCall().
#include "calls/group/calls_group_call.h"
#include "ui/boxes/calendar_box.h"
#include "boxes/confirm_box.h"
#include "mainwidget.h"