Allow enable/disable video in a call.

This commit is contained in:
John Preston 2020-07-16 20:23:55 +04:00
parent a4ee90e8c6
commit da3bbba497
27 changed files with 133 additions and 49 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 456 B

After

Width:  |  Height:  |  Size: 595 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 727 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 405 B

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 736 B

After

Width:  |  Height:  |  Size: 970 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 886 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 412 B

After

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 800 B

After

Width:  |  Height:  |  Size: 1,002 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 945 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 459 B

After

Width:  |  Height:  |  Size: 744 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 843 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -84,6 +84,13 @@ callMuteToggle: IconButton(callButton) {
}
}
callUnmuteIcon: icon {{ "call_record_muted", callIconFg }};
callCameraToggle: IconButton(callButton) {
icon: icon {{ "call_camera_active", callIconFg }};
ripple: RippleAnimation(defaultRippleAnimation) {
color: callMuteRipple;
}
}
callNoCameraIcon: icon {{ "call_camera_muted", callIconFg }};
callControlsTop: 80px;
callControlsSkip: 0px;
@ -124,7 +131,7 @@ callBarMuteToggle: IconButton {
height: 38px;
icon: icon {{ "call_record_active", callBarFg }};
iconPosition: point(9px, 8px);
iconPosition: point(3px, 2px);
ripple: RippleAnimation(defaultRippleAnimation) {
color: callBarMuteRipple;
@ -137,7 +144,7 @@ callBarRightSkip: 12px;
callBarSkip: 10px;
callBarHangup: IconButton(callBarMuteToggle) {
icon: icon {{ "call_discard", callBarFg }};
iconPosition: point(9px, 11px);
iconPosition: point(3px, 1px);
}
callBarLabel: LabelSimple(defaultLabelSimple) {
font: semiboldFont;

View file

@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "facades.h"
#include <tgcalls/Instance.h>
#include <tgcalls/VideoCaptureInterface.h>
namespace tgcalls {
class InstanceImpl;
@ -42,6 +43,7 @@ namespace {
constexpr auto kMinLayer = 65;
constexpr auto kHangupTimeoutMs = 5000;
constexpr auto kSha256Size = 32;
constexpr auto kDropFramesWhileInactive = 5 * crl::time(1000);
const auto kDefaultVersion = "2.4.4"_q;
const auto RegisterTag = tgcalls::Register<tgcalls::InstanceImpl>();
@ -319,12 +321,31 @@ void Call::actuallyAnswer() {
}).send();
}
void Call::setMute(bool mute) {
_mute = mute;
void Call::setMuted(bool mute) {
_muted = mute;
if (_instance) {
_instance->setMuteMicrophone(_mute);
_instance->setMuteMicrophone(mute);
}
}
void Call::setVideoEnabled(bool enabled) {
if (_state.current() != State::Established) {
return;
}
_videoEnabled = enabled;
if (enabled) {
if (!_videoCapture) {
_videoCapture = tgcalls::VideoCaptureInterface::Create();
}
if (_instance) {
_instance->requestVideo(_videoCapture);
} else {
_videoState = VideoState::OutgoingRequested;
}
_videoCapture->setIsVideoEnabled(true);
} else if (_videoCapture) {
_videoCapture->setIsVideoEnabled(false);
}
_muteChanged.notify(_mute);
}
crl::time Call::getDurationMs() const {
@ -430,6 +451,7 @@ bool Call::handleUpdate(const MTPPhoneCall &call) {
}
_id = data.vid().v;
_accessHash = data.vaccess_hash().v;
setVideoEnabled(data.is_video());
auto gaHashBytes = bytes::make_span(data.vg_a_hash().v);
if (gaHashBytes.size() != kSha256Size) {
LOG(("Call Error: Wrong g_a_hash size %1, expected %2."
@ -650,7 +672,7 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
.encryptionKey = tgcalls::EncryptionKey(
std::move(encryptionKeyValue),
(_type == Type::Outgoing)),
.videoCapture = nullptr,
.videoCapture = _videoEnabled.current() ? _videoCapture : nullptr,
.stateUpdated = [=](tgcalls::State state, tgcalls::VideoState videoState) {
crl::on_main(weak, [=] {
handleControllerStateChange(state, videoState);
@ -662,6 +684,14 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
});
},
.remoteVideoIsActiveUpdated = [=](bool active) {
crl::on_main(weak, [=] {
if (!active) {
_frames.fire(QImage());
_remoteVideoInactiveFrom = crl::now();
} else {
_remoteVideoInactiveFrom = 0;
}
});
},
.signalingDataEmitted = [=](const std::vector<uint8_t> &data) {
const auto bytes = QByteArray(
@ -706,8 +736,6 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
}
}
descriptor.videoCapture = tgcalls::CreateVideoCapture();
const auto version = call.vprotocol().match([&](
const MTPDphoneCallProtocol &data) {
return data.vlibrary_versions().v;
@ -727,13 +755,18 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
}
const auto raw = _instance.get();
if (_mute) {
raw->setMuteMicrophone(_mute);
if (_muted.current()) {
raw->setMuteMicrophone(_muted.current());
}
const auto &settings = Core::App().settings();
raw->setIncomingVideoOutput(webrtc::CreateVideoSink([=](QImage frame) {
crl::on_main(weak, [=] {
_frames.fire_copy(frame);
if (_remoteVideoInactiveFrom > 0
&& (_remoteVideoInactiveFrom + kDropFramesWhileInactive
> crl::now())) {
} else {
_frames.fire_copy(frame);
}
});
}));
raw->setAudioOutputDevice(
@ -748,6 +781,18 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
void Call::handleControllerStateChange(
tgcalls::State state,
tgcalls::VideoState videoState) {
_videoState = [&] {
switch (videoState) {
case tgcalls::VideoState::Possible: return VideoState::Disabled;
case tgcalls::VideoState::OutgoingRequested:
return VideoState::OutgoingRequested;
case tgcalls::VideoState::IncomingRequested:
return VideoState::IncomingRequested;
case tgcalls::VideoState::Active: return VideoState::Enabled;
}
Unexpected("VideoState value in Call::handleControllerStateChange.");
}();
switch (state) {
case tgcalls::State::WaitInit: {
DEBUG_LOG(("Call Info: State changed to WaitingInit."));
@ -780,10 +825,7 @@ void Call::handleControllerBarCountChange(int count) {
}
void Call::setSignalBarCount(int count) {
if (_signalBarCount != count) {
_signalBarCount = count;
_signalBarCountChanged.notify(count);
}
_signalBarCount = count;
}
template <typename T>
@ -986,9 +1028,7 @@ void Call::handleControllerError(const QString &error) {
void Call::destroyController() {
if (_instance) {
AssertIsDebug();
const auto state = _instance->stop();
LOG(("CALL_LOG: %1").arg(QString::fromStdString(state.debugLog)));
DEBUG_LOG(("Call Info: Destroying call controller.."));
_instance.reset();

View file

@ -21,6 +21,7 @@ class Track;
namespace tgcalls {
class Instance;
class VideoCaptureInterface;
enum class State;
enum class VideoState;
} // namespace tgcalls
@ -91,29 +92,50 @@ public:
Ringing,
Busy,
};
State state() const {
[[nodiscard]] State state() const {
return _state.current();
}
rpl::producer<State> stateValue() const {
[[nodiscard]] rpl::producer<State> stateValue() const {
return _state.value();
}
enum class VideoState {
Disabled,
OutgoingRequested,
IncomingRequested,
Enabled
};
[[nodiscard]] VideoState videoState() const {
return _videoState.current();
}
[[nodiscard]] rpl::producer<VideoState> videoStateValue() const {
return _videoState.value();
}
static constexpr auto kSignalBarStarting = -1;
static constexpr auto kSignalBarFinished = -2;
static constexpr auto kSignalBarCount = 4;
base::Observable<int> &signalBarCountChanged() {
return _signalBarCountChanged;
[[nodiscard]] rpl::producer<int> signalBarCountValue() const {
return _signalBarCount.value();
}
void setMute(bool mute);
bool isMute() const {
return _mute;
void setMuted(bool mute);
[[nodiscard]] bool muted() const {
return _muted.current();
}
base::Observable<bool> &muteChanged() {
return _muteChanged;
[[nodiscard]] rpl::producer<bool> mutedValue() const {
return _muted.value();
}
rpl::producer<QImage> frames() const {
void setVideoEnabled(bool enabled);
[[nodiscard]] bool videoEnabled() const {
return _videoEnabled.current();
}
[[nodiscard]] rpl::producer<bool> videoEnabledValue() const {
return _videoEnabled.value();
}
[[nodiscard]] rpl::producer<QImage> frames() const {
return _frames.events();
}
@ -178,17 +200,18 @@ private:
MTP::Sender _api;
Type _type = Type::Outgoing;
rpl::variable<State> _state = State::Starting;
rpl::variable<VideoState> _videoState = VideoState::Disabled;
FinishType _finishAfterRequestingCall = FinishType::None;
bool _answerAfterDhConfigReceived = false;
int _signalBarCount = kSignalBarStarting;
base::Observable<int> _signalBarCountChanged;
rpl::variable<int> _signalBarCount = kSignalBarStarting;
crl::time _startTime = 0;
base::DelayedCallTimer _finishByTimeoutTimer;
base::Timer _discardByTimeoutTimer;
bool _mute = false;
base::Observable<bool> _muteChanged;
rpl::variable<bool> _muted = false;
rpl::variable<bool> _videoEnabled = false;
rpl::event_stream<QImage> _frames;
crl::time _remoteVideoInactiveFrom = 0;
DhConfig _dhConfig;
bytes::vector _ga;
@ -203,6 +226,7 @@ private:
uint64 _keyFingerprint = 0;
std::unique_ptr<tgcalls::Instance> _instance;
std::shared_ptr<tgcalls::VideoCaptureInterface> _videoCapture;
std::unique_ptr<Media::Audio::Track> _waitingTrack;

View file

@ -89,9 +89,10 @@ SignalBars::SignalBars(
resize(
_st.width + (_st.width + _st.skip) * (Call::kSignalBarCount - 1),
_st.width * Call::kSignalBarCount);
subscribe(call->signalBarCountChanged(), [=](int count) {
call->signalBarCountValue(
) | rpl::start_with_next([=](int count) {
changed(count);
});
}, lifetime());
}
bool SignalBars::isDisplayed() const {
@ -307,6 +308,7 @@ Panel::Panel(not_null<Call*> call)
, _answerHangupRedial(this, st::callAnswer, &st::callHangup)
, _decline(this, object_ptr<Button>(this, st::callHangup))
, _cancel(this, object_ptr<Button>(this, st::callCancel))
, _camera(this, st::callCameraToggle)
, _mute(this, st::callMuteToggle)
, _name(this, st::callName)
, _status(this, st::callStatus)
@ -353,14 +355,24 @@ void Panel::hideDeactivated() {
void Panel::initControls() {
_hangupShown = (_call->type() == Type::Outgoing);
_mute->setClickedCallback([this] {
_mute->setClickedCallback([=] {
if (_call) {
_call->setMute(!_call->isMute());
_call->setMuted(!_call->muted());
}
});
subscribe(_call->muteChanged(), [this](bool mute) {
_call->mutedValue(
) | rpl::start_with_next([=](bool mute) {
_mute->setIconOverride(mute ? &st::callUnmuteIcon : nullptr);
}, lifetime());
_camera->setClickedCallback([=] {
if (_call) {
_call->setVideoEnabled(!_call->videoEnabled());
}
});
_call->videoEnabledValue(
) | rpl::start_with_next([=](bool enabled) {
_camera->setIconOverride(enabled ? nullptr : &st::callNoCameraIcon);
}, lifetime());
_updateDurationTimer.setCallback([this] {
if (_call) {
@ -691,6 +703,7 @@ void Panel::updateControlsGeometry() {
updateHangupGeometry();
_mute->moveToRight(_padding.right() + st::callMuteRight, controlsTop);
_camera->moveToLeft(_padding.right() + st::callMuteRight, controlsTop);
const auto skip = st::callSignalMargin + st::callSignalPadding;
const auto delta = (_signalBars->width() - _signalBars->height());

View file

@ -34,7 +34,7 @@ struct CallSignalBars;
namespace Calls {
class SignalBars : public Ui::RpWidget, private base::Subscriber {
class SignalBars final : public Ui::RpWidget {
public:
SignalBars(
QWidget *parent,
@ -56,10 +56,7 @@ private:
};
class Panel
: public Ui::RpWidget
, private base::Subscriber
, private Ui::AbstractTooltipShower {
class Panel final : public Ui::RpWidget, private Ui::AbstractTooltipShower {
public:
Panel(not_null<Call*> call);
@ -138,6 +135,7 @@ private:
object_ptr<Ui::FadeWrap<Button>> _cancel;
bool _hangupShown = false;
Ui::Animations::Simple _hangupShownProgress;
object_ptr<Ui::IconButton> _camera;
object_ptr<Ui::IconButton> _mute;
object_ptr<Ui::FlatLabel> _name;
object_ptr<Ui::FlatLabel> _status;

View file

@ -94,14 +94,14 @@ TopBar::TopBar(
void TopBar::initControls() {
_mute->setClickedCallback([=] {
if (const auto call = _call.get()) {
call->setMute(!call->isMute());
call->setMuted(!call->muted());
}
});
setMuted(_call->isMute());
subscribe(_call->muteChanged(), [=](bool mute) {
setMuted(mute);
_call->mutedValue(
) | rpl::start_with_next([=](bool muted) {
setMuted(muted);
update();
});
}, lifetime());
_call->user()->session().changes().peerUpdates(
Data::PeerUpdate::Flag::Name

View file

@ -28,7 +28,7 @@ namespace Calls {
class Call;
class SignalBars;
class TopBar : public Ui::RpWidget, private base::Subscriber {
class TopBar : public Ui::RpWidget {
public:
TopBar(QWidget *parent, const base::weak_ptr<Call> &call);

@ -1 +1 @@
Subproject commit beb6eac853c6f2caf037ab1c374c25cf81af7a95
Subproject commit 8364baddd1e731aec3ae71d6594c10750afce312

View file

@ -19,6 +19,8 @@ PRIVATE
Instance.h
InstanceImpl.cpp
InstanceImpl.h
LogSinkImpl.cpp
LogSinkImpl.h
Manager.cpp
Manager.h
MediaManager.cpp