Improve calls settings (camera / microphone).

This commit is contained in:
John Preston 2020-08-21 14:50:13 +04:00
parent ade7745b0b
commit 4672e3d068
5 changed files with 186 additions and 137 deletions

View file

@ -744,8 +744,8 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
.mediaDevicesConfig = tgcalls::MediaDevicesConfig{ .mediaDevicesConfig = tgcalls::MediaDevicesConfig{
.audioInputId = settings.callInputDeviceId().toStdString(), .audioInputId = settings.callInputDeviceId().toStdString(),
.audioOutputId = settings.callOutputDeviceId().toStdString(), .audioOutputId = settings.callOutputDeviceId().toStdString(),
.inputVolume = settings.callInputVolume() / 100.f, .inputVolume = 1.f,//settings.callInputVolume() / 100.f,
.outputVolume = settings.callOutputVolume() / 100.f, .outputVolume = 1.f,//settings.callOutputVolume() / 100.f,
}, },
.videoCapture = _videoCapture, .videoCapture = _videoCapture,
.stateUpdated = [=](tgcalls::State state) { .stateUpdated = [=](tgcalls::State state) {
@ -962,19 +962,20 @@ void Call::setState(State state) {
} }
} }
void Call::setCurrentAudioDevice(bool input, std::string deviceId) { void Call::setCurrentAudioDevice(bool input, const QString &deviceId) {
if (_instance) { if (_instance) {
const auto id = deviceId.toStdString();
if (input) { if (input) {
_instance->setAudioInputDevice(deviceId); _instance->setAudioInputDevice(id);
} else { } else {
_instance->setAudioOutputDevice(deviceId); _instance->setAudioOutputDevice(id);
} }
} }
} }
void Call::setCurrentVideoDevice(std::string deviceId) { void Call::setCurrentVideoDevice(const QString &deviceId) {
if (_videoCapture) { if (_videoCapture) {
_videoCapture->switchToDevice(deviceId); _videoCapture->switchToDevice(deviceId.toStdString());
} }
} }

View file

@ -172,8 +172,8 @@ public:
QString getDebugLog() const; QString getDebugLog() const;
void setCurrentAudioDevice(bool input, std::string deviceId); void setCurrentAudioDevice(bool input, const QString &deviceId);
void setCurrentVideoDevice(std::string deviceId); void setCurrentVideoDevice(const QString &deviceId);
void setAudioVolume(bool input, float level); void setAudioVolume(bool input, float level);
void setAudioDuckingEnabled(bool enabled); void setAudioDuckingEnabled(bool enabled);

View file

@ -26,22 +26,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h" #include "core/application.h"
#include "core/core_settings.h" #include "core/core_settings.h"
#include "calls/calls_instance.h" #include "calls/calls_instance.h"
#include "calls/calls_video_bubble.h"
#include "webrtc/webrtc_media_devices.h" #include "webrtc/webrtc_media_devices.h"
#include "webrtc/webrtc_video_track.h"
#include "webrtc/webrtc_audio_input_tester.h"
#include "tgcalls/VideoCaptureInterface.h"
#include "facades.h" #include "facades.h"
#include "styles/style_layers.h"
#ifdef slots
#undef slots
#define NEED_TO_RESTORE_SLOTS
#endif // slots
#include <VoIPController.h>
#ifdef NEED_TO_RESTORE_SLOTS
#define slots Q_SLOTS
#undef NEED_TO_RESTORE_SLOTS
#endif // NEED_TO_RESTORE_SLOTS
namespace Settings { namespace Settings {
namespace {
constexpr auto kMicTestUpdateInterval = crl::time(100);
constexpr auto kMicTestAnimationDuration = crl::time(200);
} // namespace
Calls::Calls( Calls::Calls(
QWidget *parent, QWidget *parent,
@ -49,6 +48,7 @@ Calls::Calls(
: Section(parent) : Section(parent)
, _controller(controller) { , _controller(controller) {
setupContent(); setupContent();
requestPermissionAndStartTestingMicrophone();
} }
Calls::~Calls() { Calls::~Calls() {
@ -99,6 +99,15 @@ void Calls::setupContent() {
const auto cameras = Webrtc::GetVideoInputList(); const auto cameras = Webrtc::GetVideoInputList();
if (!cameras.empty()) { if (!cameras.empty()) {
auto capturerOwner = tgcalls::VideoCaptureInterface::Create(
settings.callVideoInputDeviceId().toStdString());
const auto capturer = capturerOwner.get();
content->lifetime().add([owner = std::move(capturerOwner)]{});
const auto track = content->lifetime().make_state<Webrtc::VideoTrack>(
Webrtc::VideoState::Active);
capturer->setOutput(track->sink());
const auto currentCameraName = [&] { const auto currentCameraName = [&] {
const auto i = ranges::find( const auto i = ranges::find(
cameras, cameras,
@ -138,10 +147,11 @@ void Calls::setupContent() {
const auto deviceId = option const auto deviceId = option
? devices[option - 1].id ? devices[option - 1].id
: "default"; : "default";
capturer->switchToDevice(deviceId.toStdString());
Core::App().settings().setCallVideoInputDeviceId(deviceId); Core::App().settings().setCallVideoInputDeviceId(deviceId);
Core::App().saveSettingsDelayed(); Core::App().saveSettingsDelayed();
if (const auto call = Core::App().calls().currentCall()) { if (const auto call = Core::App().calls().currentCall()) {
call->setCurrentVideoDevice(deviceId.toStdString()); call->setCurrentVideoDevice(deviceId);
} }
}); });
Ui::show(Box<SingleChoiceBox>( Ui::show(Box<SingleChoiceBox>(
@ -150,6 +160,37 @@ void Calls::setupContent() {
currentOption, currentOption,
save)); save));
}); });
const auto bubbleWrap = content->add(object_ptr<Ui::RpWidget>(content));
const auto bubble = content->lifetime().make_state<::Calls::VideoBubble>(
bubbleWrap,
track);
const auto padding = st::settingsButton.padding.left();
const auto top = st::boxRoundShadow.extend.top();
const auto bottom = st::boxRoundShadow.extend.bottom();
bubbleWrap->widthValue(
) | rpl::filter([=](int width) {
return (width > 2 * padding + 1);
}) | rpl::start_with_next([=](int width) {
const auto use = (width - 2 * padding);
bubble->updateGeometry(
::Calls::VideoBubble::DragMode::None,
QRect(padding, top, use, (use * 480) / 640));
}, bubbleWrap->lifetime());
track->renderNextFrame(
) | rpl::start_with_next([=] {
const auto size = track->frameSize();
if (size.isEmpty()) {
return;
}
const auto width = bubbleWrap->width();
const auto use = (width - 2 * padding);
bubbleWrap->resize(
width,
top + ((use * size.height()) / size.width()) + bottom);
bubbleWrap->update();
}, bubbleWrap->lifetime());
AddSkip(content); AddSkip(content);
AddDivider(content); AddDivider(content);
@ -186,7 +227,7 @@ void Calls::setupContent() {
Core::App().settings().setCallOutputDeviceId(deviceId); Core::App().settings().setCallOutputDeviceId(deviceId);
Core::App().saveSettingsDelayed(); Core::App().saveSettingsDelayed();
if (const auto call = Core::App().calls().currentCall()) { if (const auto call = Core::App().calls().currentCall()) {
call->setCurrentAudioDevice(false, deviceId.toStdString()); call->setCurrentAudioDevice(false, deviceId);
} }
}); });
Ui::show(Box<SingleChoiceBox>( Ui::show(Box<SingleChoiceBox>(
@ -196,36 +237,36 @@ void Calls::setupContent() {
save)); save));
}); });
const auto outputLabel = content->add( //const auto outputLabel = content->add(
object_ptr<Ui::LabelSimple>( // object_ptr<Ui::LabelSimple>(
content, // content,
st::settingsAudioVolumeLabel), // st::settingsAudioVolumeLabel),
st::settingsAudioVolumeLabelPadding); // st::settingsAudioVolumeLabelPadding);
const auto outputSlider = content->add( //const auto outputSlider = content->add(
object_ptr<Ui::MediaSlider>( // object_ptr<Ui::MediaSlider>(
content, // content,
st::settingsAudioVolumeSlider), // st::settingsAudioVolumeSlider),
st::settingsAudioVolumeSliderPadding); // st::settingsAudioVolumeSliderPadding);
const auto updateOutputLabel = [=](int value) { //const auto updateOutputLabel = [=](int value) {
const auto percent = QString::number(value); // const auto percent = QString::number(value);
outputLabel->setText( // outputLabel->setText(
tr::lng_settings_call_output_volume(tr::now, lt_percent, percent)); // tr::lng_settings_call_output_volume(tr::now, lt_percent, percent));
}; //};
const auto updateOutputVolume = [=](int value) { //const auto updateOutputVolume = [=](int value) {
_needWriteSettings = true; // _needWriteSettings = true;
updateOutputLabel(value); // updateOutputLabel(value);
Core::App().settings().setCallOutputVolume(value); // Core::App().settings().setCallOutputVolume(value);
if (const auto call = Core::App().calls().currentCall()) { // if (const auto call = Core::App().calls().currentCall()) {
call->setAudioVolume(false, value / 100.0f); // call->setAudioVolume(false, value / 100.0f);
} // }
}; //};
outputSlider->resize(st::settingsAudioVolumeSlider.seekSize); //outputSlider->resize(st::settingsAudioVolumeSlider.seekSize);
outputSlider->setPseudoDiscrete( //outputSlider->setPseudoDiscrete(
101, // 101,
[](int val) { return val; }, // [](int val) { return val; },
settings.callOutputVolume(), // settings.callOutputVolume(),
updateOutputVolume); // updateOutputVolume);
updateOutputLabel(Core::App().settings().callOutputVolume()); //updateOutputLabel(Core::App().settings().callOutputVolume());
AddSkip(content); AddSkip(content);
AddDivider(content); AddDivider(content);
@ -261,10 +302,10 @@ void Calls::setupContent() {
Core::App().settings().setCallInputDeviceId(deviceId); Core::App().settings().setCallInputDeviceId(deviceId);
Core::App().saveSettingsDelayed(); Core::App().saveSettingsDelayed();
if (_micTester) { if (_micTester) {
stopTestingMicrophone(); _micTester->setDeviceId(deviceId);
} }
if (const auto call = Core::App().calls().currentCall()) { if (const auto call = Core::App().calls().currentCall()) {
call->setCurrentAudioDevice(true, deviceId.toStdString()); call->setCurrentAudioDevice(true, deviceId);
} }
}); });
Ui::show(Box<SingleChoiceBox>( Ui::show(Box<SingleChoiceBox>(
@ -274,51 +315,51 @@ void Calls::setupContent() {
save)); save));
}); });
const auto inputLabel = content->add( //const auto inputLabel = content->add(
object_ptr<Ui::LabelSimple>( // object_ptr<Ui::LabelSimple>(
content, // content,
st::settingsAudioVolumeLabel), // st::settingsAudioVolumeLabel),
st::settingsAudioVolumeLabelPadding); // st::settingsAudioVolumeLabelPadding);
const auto inputSlider = content->add( //const auto inputSlider = content->add(
object_ptr<Ui::MediaSlider>( // object_ptr<Ui::MediaSlider>(
content, // content,
st::settingsAudioVolumeSlider), // st::settingsAudioVolumeSlider),
st::settingsAudioVolumeSliderPadding); // st::settingsAudioVolumeSliderPadding);
const auto updateInputLabel = [=](int value) { //const auto updateInputLabel = [=](int value) {
const auto percent = QString::number(value); // const auto percent = QString::number(value);
inputLabel->setText( // inputLabel->setText(
tr::lng_settings_call_input_volume(tr::now, lt_percent, percent)); // tr::lng_settings_call_input_volume(tr::now, lt_percent, percent));
}; //};
const auto updateInputVolume = [=](int value) { //const auto updateInputVolume = [=](int value) {
_needWriteSettings = true; // _needWriteSettings = true;
updateInputLabel(value); // updateInputLabel(value);
Core::App().settings().setCallInputVolume(value); // Core::App().settings().setCallInputVolume(value);
if (const auto call = Core::App().calls().currentCall()) { // if (const auto call = Core::App().calls().currentCall()) {
call->setAudioVolume(true, value / 100.0f); // call->setAudioVolume(true, value / 100.0f);
} // }
}; //};
inputSlider->resize(st::settingsAudioVolumeSlider.seekSize); //inputSlider->resize(st::settingsAudioVolumeSlider.seekSize);
inputSlider->setPseudoDiscrete(101, //inputSlider->setPseudoDiscrete(101,
[](int val) { return val; }, // [](int val) { return val; },
settings.callInputVolume(), // settings.callInputVolume(),
updateInputVolume); // updateInputVolume);
updateInputLabel(settings.callInputVolume()); //updateInputLabel(settings.callInputVolume());
AddButton( //AddButton(
content, // content,
rpl::single( // rpl::single(
tr::lng_settings_call_test_mic(tr::now) // tr::lng_settings_call_test_mic(tr::now)
) | rpl::then( // ) | rpl::then(
_micTestTextStream.events() // _micTestTextStream.events()
), // ),
st::settingsButton // st::settingsButton
)->addClickHandler([=] { //)->addClickHandler([=] {
if (!_micTester) { // if (!_micTester) {
requestPermissionAndStartTestingMicrophone(); // requestPermissionAndStartTestingMicrophone();
} else { // } else {
stopTestingMicrophone(); // stopTestingMicrophone();
} // }
}); //});
_micTestLevel = content->add( _micTestLevel = content->add(
object_ptr<Ui::LevelMeter>( object_ptr<Ui::LevelMeter>(
@ -328,7 +369,11 @@ void Calls::setupContent() {
_micTestLevel->resize(QSize(0, st::defaultLevelMeter.height)); _micTestLevel->resize(QSize(0, st::defaultLevelMeter.height));
_levelUpdateTimer.setCallback([=] { _levelUpdateTimer.setCallback([=] {
_micTestLevel->setValue(_micTester->GetAndResetLevel()); const auto was = _micLevel;
_micLevel = _micTester->getAndResetLevel();
_micLevelAnimation.start([=] {
_micTestLevel->setValue(_micLevelAnimation.value(_micLevel));
}, was, _micLevel, kMicTestAnimationDuration);
}); });
AddSkip(content); AddSkip(content);
@ -336,23 +381,23 @@ void Calls::setupContent() {
AddSkip(content); AddSkip(content);
AddSubsectionTitle(content, tr::lng_settings_call_section_other()); AddSubsectionTitle(content, tr::lng_settings_call_section_other());
#if defined Q_OS_MAC && !defined OS_MAC_STORE //#if defined Q_OS_MAC && !defined OS_MAC_STORE
AddButton( // AddButton(
content, // content,
tr::lng_settings_call_audio_ducking(), // tr::lng_settings_call_audio_ducking(),
st::settingsButton // st::settingsButton
)->toggleOn( // )->toggleOn(
rpl::single(settings.callAudioDuckingEnabled()) // rpl::single(settings.callAudioDuckingEnabled())
)->toggledValue() | rpl::filter([](bool enabled) { // )->toggledValue() | rpl::filter([](bool enabled) {
return (enabled != Core::App().settings().callAudioDuckingEnabled()); // return (enabled != Core::App().settings().callAudioDuckingEnabled());
}) | rpl::start_with_next([=](bool enabled) { // }) | rpl::start_with_next([=](bool enabled) {
Core::App().settings().setCallAudioDuckingEnabled(enabled); // Core::App().settings().setCallAudioDuckingEnabled(enabled);
Core::App().saveSettingsDelayed(); // Core::App().saveSettingsDelayed();
if (const auto call = Core::App().calls().currentCall()) { // if (const auto call = Core::App().calls().currentCall()) {
call->setAudioDuckingEnabled(enabled); // call->setAudioDuckingEnabled(enabled);
} // }
}, content->lifetime()); // }, content->lifetime());
#endif // Q_OS_MAC && !OS_MAC_STORE //#endif // Q_OS_MAC && !OS_MAC_STORE
AddButton( AddButton(
content, content,
@ -401,22 +446,22 @@ void Calls::requestPermissionAndStartTestingMicrophone() {
} }
void Calls::startTestingMicrophone() { void Calls::startTestingMicrophone() {
_micTestTextStream.fire(tr::lng_settings_call_stop_mic_test(tr::now)); //_micTestTextStream.fire(tr::lng_settings_call_stop_mic_test(tr::now));
_levelUpdateTimer.callEach(50); _levelUpdateTimer.callEach(kMicTestUpdateInterval);
_micTester = std::make_unique<tgvoip::AudioInputTester>( _micTester = std::make_unique<Webrtc::AudioInputTester>(
Core::App().settings().callInputDeviceId().toStdString()); Core::App().settings().callInputDeviceId());
if (_micTester->Failed()) { //if (_micTester->Failed()) {
stopTestingMicrophone(); // stopTestingMicrophone();
Ui::show(Box<InformBox>(tr::lng_call_error_audio_io(tr::now))); // Ui::show(Box<InformBox>(tr::lng_call_error_audio_io(tr::now)));
} //}
} }
void Calls::stopTestingMicrophone() { //void Calls::stopTestingMicrophone() {
_micTestTextStream.fire(tr::lng_settings_call_test_mic(tr::now)); // _micTestTextStream.fire(tr::lng_settings_call_test_mic(tr::now));
_levelUpdateTimer.cancel(); // _levelUpdateTimer.cancel();
_micTester.reset(); // _micTester.reset();
_micTestLevel->setValue(0.0f); // _micTestLevel->setValue(0.0f);
} //}
} // namespace Settings } // namespace Settings

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "settings/settings_common.h" #include "settings/settings_common.h"
#include "ui/effects/animations.h"
#include "base/timer.h" #include "base/timer.h"
namespace Calls { namespace Calls {
@ -18,9 +19,9 @@ namespace Ui {
class LevelMeter; class LevelMeter;
} // namespace Ui } // namespace Ui
namespace tgvoip { namespace Webrtc {
class AudioInputTester; class AudioInputTester;
} // namespace tgvoip } // namespace Webrtc
namespace Settings { namespace Settings {
@ -35,16 +36,18 @@ private:
void setupContent(); void setupContent();
void requestPermissionAndStartTestingMicrophone(); void requestPermissionAndStartTestingMicrophone();
void startTestingMicrophone(); void startTestingMicrophone();
void stopTestingMicrophone(); //void stopTestingMicrophone();
const not_null<Window::SessionController*> _controller; const not_null<Window::SessionController*> _controller;
rpl::event_stream<QString> _cameraNameStream; rpl::event_stream<QString> _cameraNameStream;
rpl::event_stream<QString> _outputNameStream; rpl::event_stream<QString> _outputNameStream;
rpl::event_stream<QString> _inputNameStream; rpl::event_stream<QString> _inputNameStream;
rpl::event_stream<QString> _micTestTextStream; //rpl::event_stream<QString> _micTestTextStream;
bool _needWriteSettings = false; bool _needWriteSettings = false;
std::unique_ptr<tgvoip::AudioInputTester> _micTester; std::unique_ptr<Webrtc::AudioInputTester> _micTester;
Ui::LevelMeter *_micTestLevel = nullptr; Ui::LevelMeter *_micTestLevel = nullptr;
float _micLevel = 0.;
Ui::Animations::Simple _micLevelAnimation;
base::Timer _levelUpdateTimer; base::Timer _levelUpdateTimer;
}; };

@ -1 +1 @@
Subproject commit 48988791c082648d9876cc0e734db33af1440c02 Subproject commit 8a83223c5e3133e5470a633fb9ac188d60f44b84