mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Allow switching between voice/video.
This commit is contained in:
parent
552343fa37
commit
ff44f626ba
17 changed files with 242 additions and 52 deletions
|
@ -3246,7 +3246,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_record_lock_cancel_sure" = "Do you want to stop recording and discard your voice message?";
|
"lng_record_lock_cancel_sure" = "Do you want to stop recording and discard your voice message?";
|
||||||
"lng_record_listen_cancel_sure" = "Do you want to discard your recorded voice message?";
|
"lng_record_listen_cancel_sure" = "Do you want to discard your recorded voice message?";
|
||||||
"lng_record_lock_discard" = "Discard";
|
"lng_record_lock_discard" = "Discard";
|
||||||
"lng_record_hold_tip" = "Please hold the mouse button pressed to record a voice message.";
|
"lng_record_voice_tip" = "Hold to record audio. Click to switch to video.";
|
||||||
|
"lng_record_video_tip" = "Hold to record video. Click to switch to audio.";
|
||||||
"lng_record_once_first_tooltip" = "Click to set this message to **Play Once**.";
|
"lng_record_once_first_tooltip" = "Click to set this message to **Play Once**.";
|
||||||
"lng_record_once_active_tooltip" = "The recipient will be able to listen only once.";
|
"lng_record_once_active_tooltip" = "The recipient will be able to listen only once.";
|
||||||
"lng_will_be_notified" = "Subscribers will be notified when you post.";
|
"lng_will_be_notified" = "Subscribers will be notified when you post.";
|
||||||
|
|
|
@ -150,6 +150,8 @@ SendButton {
|
||||||
inner: IconButton;
|
inner: IconButton;
|
||||||
record: icon;
|
record: icon;
|
||||||
recordOver: icon;
|
recordOver: icon;
|
||||||
|
round: icon;
|
||||||
|
roundOver: icon;
|
||||||
sendDisabledFg: color;
|
sendDisabledFg: color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1159,6 +1161,10 @@ historyRecordVoiceOnceFg: icon {{ "voice_lock/audio_once_number", windowFgActive
|
||||||
historyRecordVoiceOnceFgOver: icon {{ "voice_lock/audio_once_number", windowFgActive }};
|
historyRecordVoiceOnceFgOver: icon {{ "voice_lock/audio_once_number", windowFgActive }};
|
||||||
historyRecordVoiceOnceInactive: icon {{ "chat/audio_once", windowSubTextFg }};
|
historyRecordVoiceOnceInactive: icon {{ "chat/audio_once", windowSubTextFg }};
|
||||||
historyRecordVoiceActive: icon {{ "chat/input_record_filled", historyRecordVoiceFgActiveIcon }};
|
historyRecordVoiceActive: icon {{ "chat/input_record_filled", historyRecordVoiceFgActiveIcon }};
|
||||||
|
historyRecordRound: icon {{ "info/info_media_round", historyRecordVoiceFg }};
|
||||||
|
historyRecordRoundOver: icon {{ "info/info_media_round", historyRecordVoiceFgOver }};
|
||||||
|
historyRecordRoundActive: icon {{ "info/info_media_round", historyRecordVoiceFgActiveIcon }};
|
||||||
|
historyRecordRoundIconPosition: point(0px, 0px);
|
||||||
historyRecordSendIconPosition: point(2px, 0px);
|
historyRecordSendIconPosition: point(2px, 0px);
|
||||||
historyRecordVoiceRippleBgActive: lightButtonBgOver;
|
historyRecordVoiceRippleBgActive: lightButtonBgOver;
|
||||||
historyRecordSignalRadius: 5px;
|
historyRecordSignalRadius: 5px;
|
||||||
|
@ -1264,6 +1270,8 @@ historySend: SendButton {
|
||||||
}
|
}
|
||||||
record: historyRecordVoice;
|
record: historyRecordVoice;
|
||||||
recordOver: historyRecordVoiceOver;
|
recordOver: historyRecordVoiceOver;
|
||||||
|
round: historyRecordRound;
|
||||||
|
roundOver: historyRecordRoundOver;
|
||||||
sendDisabledFg: historyComposeIconFg;
|
sendDisabledFg: historyComposeIconFg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -222,7 +222,7 @@ QByteArray Settings::serialize() const {
|
||||||
+ Serialize::stringSize(_customFontFamily)
|
+ Serialize::stringSize(_customFontFamily)
|
||||||
+ sizeof(qint32) * 3
|
+ sizeof(qint32) * 3
|
||||||
+ Serialize::bytearraySize(_tonsiteStorageToken)
|
+ Serialize::bytearraySize(_tonsiteStorageToken)
|
||||||
+ sizeof(qint32) * 3;
|
+ sizeof(qint32) * 4;
|
||||||
|
|
||||||
auto result = QByteArray();
|
auto result = QByteArray();
|
||||||
result.reserve(size);
|
result.reserve(size);
|
||||||
|
@ -379,7 +379,8 @@ QByteArray Settings::serialize() const {
|
||||||
<< _tonsiteStorageToken
|
<< _tonsiteStorageToken
|
||||||
<< qint32(_includeMutedCounterFolders ? 1 : 0)
|
<< qint32(_includeMutedCounterFolders ? 1 : 0)
|
||||||
<< qint32(_ivZoom.current())
|
<< qint32(_ivZoom.current())
|
||||||
<< qint32(_skipToastsInFocus ? 1 : 0);
|
<< qint32(_skipToastsInFocus ? 1 : 0)
|
||||||
|
<< qint32(_recordVideoMessages ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ensures(result.size() == size);
|
Ensures(result.size() == size);
|
||||||
|
@ -505,6 +506,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||||
QByteArray tonsiteStorageToken = _tonsiteStorageToken;
|
QByteArray tonsiteStorageToken = _tonsiteStorageToken;
|
||||||
qint32 ivZoom = _ivZoom.current();
|
qint32 ivZoom = _ivZoom.current();
|
||||||
qint32 skipToastsInFocus = _skipToastsInFocus ? 1 : 0;
|
qint32 skipToastsInFocus = _skipToastsInFocus ? 1 : 0;
|
||||||
|
qint32 recordVideoMessages = _recordVideoMessages ? 1 : 0;
|
||||||
|
|
||||||
stream >> themesAccentColors;
|
stream >> themesAccentColors;
|
||||||
if (!stream.atEnd()) {
|
if (!stream.atEnd()) {
|
||||||
|
@ -820,6 +822,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||||
if (!stream.atEnd()) {
|
if (!stream.atEnd()) {
|
||||||
stream >> skipToastsInFocus;
|
stream >> skipToastsInFocus;
|
||||||
}
|
}
|
||||||
|
if (!stream.atEnd()) {
|
||||||
|
stream >> recordVideoMessages;
|
||||||
|
}
|
||||||
if (stream.status() != QDataStream::Ok) {
|
if (stream.status() != QDataStream::Ok) {
|
||||||
LOG(("App Error: "
|
LOG(("App Error: "
|
||||||
"Bad data for Core::Settings::constructFromSerialized()"));
|
"Bad data for Core::Settings::constructFromSerialized()"));
|
||||||
|
@ -1033,6 +1038,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||||
_tonsiteStorageToken = tonsiteStorageToken;
|
_tonsiteStorageToken = tonsiteStorageToken;
|
||||||
_ivZoom = ivZoom;
|
_ivZoom = ivZoom;
|
||||||
_skipToastsInFocus = (skipToastsInFocus == 1);
|
_skipToastsInFocus = (skipToastsInFocus == 1);
|
||||||
|
_recordVideoMessages = (recordVideoMessages == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Settings::getSoundPath(const QString &key) const {
|
QString Settings::getSoundPath(const QString &key) const {
|
||||||
|
@ -1422,6 +1428,7 @@ void Settings::resetOnLastLogout() {
|
||||||
_storiesClickTooltipHidden = false;
|
_storiesClickTooltipHidden = false;
|
||||||
_ttlVoiceClickTooltipHidden = false;
|
_ttlVoiceClickTooltipHidden = false;
|
||||||
_ivZoom = 100;
|
_ivZoom = 100;
|
||||||
|
_recordVideoMessages = false;
|
||||||
|
|
||||||
_recentEmojiPreload.clear();
|
_recentEmojiPreload.clear();
|
||||||
_recentEmoji.clear();
|
_recentEmoji.clear();
|
||||||
|
|
|
@ -628,6 +628,13 @@ public:
|
||||||
return _floatPlayerCorner;
|
return _floatPlayerCorner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool recordVideoMessages() const {
|
||||||
|
return _recordVideoMessages;
|
||||||
|
}
|
||||||
|
void setRecordVideoMessages(bool value) {
|
||||||
|
_recordVideoMessages = value;
|
||||||
|
}
|
||||||
|
|
||||||
void updateDialogsWidthRatio(float64 ratio, bool nochat);
|
void updateDialogsWidthRatio(float64 ratio, bool nochat);
|
||||||
[[nodiscard]] float64 dialogsWidthRatio(bool nochat) const;
|
[[nodiscard]] float64 dialogsWidthRatio(bool nochat) const;
|
||||||
|
|
||||||
|
@ -1069,6 +1076,8 @@ private:
|
||||||
bool _rememberedFlashBounceNotifyFromTray = false;
|
bool _rememberedFlashBounceNotifyFromTray = false;
|
||||||
bool _dialogsWidthSetToZeroWithoutChat = false;
|
bool _dialogsWidthSetToZeroWithoutChat = false;
|
||||||
|
|
||||||
|
bool _recordVideoMessages = false;
|
||||||
|
|
||||||
QByteArray _photoEditorBrush;
|
QByteArray _photoEditorBrush;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -150,6 +150,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "main/main_session_settings.h"
|
#include "main/main_session_settings.h"
|
||||||
#include "main/session/send_as_peers.h"
|
#include "main/session/send_as_peers.h"
|
||||||
|
#include "webrtc/webrtc_environment.h"
|
||||||
#include "window/notifications_manager.h"
|
#include "window/notifications_manager.h"
|
||||||
#include "window/window_adaptive.h"
|
#include "window/window_adaptive.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
|
@ -1068,7 +1069,17 @@ void HistoryWidget::initVoiceRecordBar() {
|
||||||
|
|
||||||
_voiceRecordBar->recordingTipRequests(
|
_voiceRecordBar->recordingTipRequests(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
controller()->showToast(tr::lng_record_hold_tip(tr::now));
|
Core::App().settings().setRecordVideoMessages(
|
||||||
|
!Core::App().settings().recordVideoMessages());
|
||||||
|
updateSendButtonType();
|
||||||
|
switch (_send->type()) {
|
||||||
|
case Ui::SendButton::Type::Record:
|
||||||
|
controller()->showToast(tr::lng_record_voice_tip(tr::now));
|
||||||
|
break;
|
||||||
|
case Ui::SendButton::Type::Round:
|
||||||
|
controller()->showToast(tr::lng_record_video_tip(tr::now));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
_voiceRecordBar->recordingStateChanges(
|
_voiceRecordBar->recordingStateChanges(
|
||||||
|
@ -2105,6 +2116,7 @@ void HistoryWidget::showHistory(
|
||||||
MsgId showAtMsgId,
|
MsgId showAtMsgId,
|
||||||
const TextWithEntities &highlightPart,
|
const TextWithEntities &highlightPart,
|
||||||
int highlightPartOffsetHint) {
|
int highlightPartOffsetHint) {
|
||||||
|
|
||||||
_pinnedClickedId = FullMsgId();
|
_pinnedClickedId = FullMsgId();
|
||||||
_minPinnedId = std::nullopt;
|
_minPinnedId = std::nullopt;
|
||||||
_showAtMsgHighlightPart = {};
|
_showAtMsgHighlightPart = {};
|
||||||
|
@ -2299,6 +2311,8 @@ void HistoryWidget::showHistory(
|
||||||
_contactStatus = nullptr;
|
_contactStatus = nullptr;
|
||||||
_businessBotStatus = nullptr;
|
_businessBotStatus = nullptr;
|
||||||
|
|
||||||
|
updateRecordMediaState();
|
||||||
|
|
||||||
if (peerId) {
|
if (peerId) {
|
||||||
using namespace HistoryView;
|
using namespace HistoryView;
|
||||||
_peer = session().data().peer(peerId);
|
_peer = session().data().peer(peerId);
|
||||||
|
@ -4254,7 +4268,10 @@ auto HistoryWidget::computeSendButtonType() const {
|
||||||
} else if (_isInlineBot) {
|
} else if (_isInlineBot) {
|
||||||
return Type::Cancel;
|
return Type::Cancel;
|
||||||
} else if (showRecordButton()) {
|
} else if (showRecordButton()) {
|
||||||
return Type::Record;
|
return (Core::App().settings().recordVideoMessages()
|
||||||
|
&& _canRecordVideoMessage)
|
||||||
|
? Type::Round
|
||||||
|
: Type::Record;
|
||||||
}
|
}
|
||||||
return Type::Send;
|
return Type::Send;
|
||||||
}
|
}
|
||||||
|
@ -4588,7 +4605,8 @@ void HistoryWidget::sendButtonClicked() {
|
||||||
const auto type = _send->type();
|
const auto type = _send->type();
|
||||||
if (type == Ui::SendButton::Type::Cancel) {
|
if (type == Ui::SendButton::Type::Cancel) {
|
||||||
cancelInlineBot();
|
cancelInlineBot();
|
||||||
} else if (type != Ui::SendButton::Type::Record) {
|
} else if (type != Ui::SendButton::Type::Record
|
||||||
|
&& type != Ui::SendButton::Type::Round) {
|
||||||
send({});
|
send({});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4878,7 +4896,7 @@ bool HistoryWidget::isSearching() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryWidget::showRecordButton() const {
|
bool HistoryWidget::showRecordButton() const {
|
||||||
return Media::Capture::instance()->available()
|
return _canRecordAudioMessage
|
||||||
&& !_voiceRecordBar->isListenState()
|
&& !_voiceRecordBar->isListenState()
|
||||||
&& !_voiceRecordBar->isRecordingByAnotherBar()
|
&& !_voiceRecordBar->isRecordingByAnotherBar()
|
||||||
&& !HasSendText(_field)
|
&& !HasSendText(_field)
|
||||||
|
@ -4909,7 +4927,9 @@ void HistoryWidget::updateSendButtonType() {
|
||||||
}();
|
}();
|
||||||
_send->setSlowmodeDelay(delay);
|
_send->setSlowmodeDelay(delay);
|
||||||
_send->setDisabled(disabledBySlowmode
|
_send->setDisabled(disabledBySlowmode
|
||||||
&& (type == Type::Send || type == Type::Record));
|
&& (type == Type::Send
|
||||||
|
|| type == Type::Record
|
||||||
|
|| type == Type::Round));
|
||||||
|
|
||||||
if (delay != 0) {
|
if (delay != 0) {
|
||||||
base::call_delayed(
|
base::call_delayed(
|
||||||
|
@ -5479,6 +5499,15 @@ void HistoryWidget::inlineBotChanged() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HistoryWidget::updateRecordMediaState() {
|
||||||
|
Media::Capture::instance()->check();
|
||||||
|
_canRecordAudioMessage = Media::Capture::instance()->available();
|
||||||
|
|
||||||
|
const auto environment = &Core::App().mediaDevices();
|
||||||
|
const auto type = Webrtc::DeviceType::Camera;
|
||||||
|
_canRecordVideoMessage = !environment->devices(type).empty();
|
||||||
|
}
|
||||||
|
|
||||||
void HistoryWidget::fieldResized() {
|
void HistoryWidget::fieldResized() {
|
||||||
moveFieldControls();
|
moveFieldControls();
|
||||||
updateHistoryGeometry();
|
updateHistoryGeometry();
|
||||||
|
|
|
@ -497,6 +497,7 @@ private:
|
||||||
bool replyToPreviousMessage();
|
bool replyToPreviousMessage();
|
||||||
bool replyToNextMessage();
|
bool replyToNextMessage();
|
||||||
[[nodiscard]] bool showSlowmodeError();
|
[[nodiscard]] bool showSlowmodeError();
|
||||||
|
void updateRecordMediaState();
|
||||||
|
|
||||||
void hideChildWidgets();
|
void hideChildWidgets();
|
||||||
void hideSelectorControlsAnimated();
|
void hideSelectorControlsAnimated();
|
||||||
|
@ -746,6 +747,9 @@ private:
|
||||||
mtpRequestId _inlineBotResolveRequestId = 0;
|
mtpRequestId _inlineBotResolveRequestId = 0;
|
||||||
bool _isInlineBot = false;
|
bool _isInlineBot = false;
|
||||||
|
|
||||||
|
bool _canRecordVideoMessage = false;
|
||||||
|
bool _canRecordAudioMessage = false;
|
||||||
|
|
||||||
std::unique_ptr<HistoryView::ContactStatus> _contactStatus;
|
std::unique_ptr<HistoryView::ContactStatus> _contactStatus;
|
||||||
std::unique_ptr<HistoryView::BusinessBotStatus> _businessBotStatus;
|
std::unique_ptr<HistoryView::BusinessBotStatus> _businessBotStatus;
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/controls/silent_toggle.h"
|
#include "ui/controls/silent_toggle.h"
|
||||||
#include "ui/chat/choose_send_as.h"
|
#include "ui/chat/choose_send_as.h"
|
||||||
#include "ui/effects/spoiler_mess.h"
|
#include "ui/effects/spoiler_mess.h"
|
||||||
|
#include "webrtc/webrtc_environment.h"
|
||||||
#include "window/window_adaptive.h"
|
#include "window/window_adaptive.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
@ -1517,7 +1518,7 @@ void ComposeControls::orderControls() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ComposeControls::showRecordButton() const {
|
bool ComposeControls::showRecordButton() const {
|
||||||
return ::Media::Capture::instance()->available()
|
return _canRecordAudioMessage
|
||||||
&& !_voiceRecordBar->isListenState()
|
&& !_voiceRecordBar->isListenState()
|
||||||
&& !_voiceRecordBar->isRecordingByAnotherBar()
|
&& !_voiceRecordBar->isRecordingByAnotherBar()
|
||||||
&& !HasSendText(_field)
|
&& !HasSendText(_field)
|
||||||
|
@ -2413,12 +2414,36 @@ void ComposeControls::initVoiceRecordBar() {
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_voiceRecordBar->recordingTipRequests(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
Core::App().settings().setRecordVideoMessages(
|
||||||
|
!Core::App().settings().recordVideoMessages());
|
||||||
|
updateSendButtonType();
|
||||||
|
switch (_send->type()) {
|
||||||
|
case Ui::SendButton::Type::Record:
|
||||||
|
_show->showToast(tr::lng_record_voice_tip(tr::now));
|
||||||
|
break;
|
||||||
|
case Ui::SendButton::Type::Round:
|
||||||
|
_show->showToast(tr::lng_record_video_tip(tr::now));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, _wrap->lifetime());
|
||||||
|
|
||||||
_voiceRecordBar->updateSendButtonTypeRequests(
|
_voiceRecordBar->updateSendButtonTypeRequests(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
updateSendButtonType();
|
updateSendButtonType();
|
||||||
}, _wrap->lifetime());
|
}, _wrap->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ComposeControls::updateRecordMediaState() {
|
||||||
|
::Media::Capture::instance()->check();
|
||||||
|
_canRecordAudioMessage = ::Media::Capture::instance()->available();
|
||||||
|
|
||||||
|
const auto environment = &Core::App().mediaDevices();
|
||||||
|
const auto type = Webrtc::DeviceType::Camera;
|
||||||
|
_canRecordVideoMessage = !environment->devices(type).empty();
|
||||||
|
}
|
||||||
|
|
||||||
void ComposeControls::updateWrappingVisibility() {
|
void ComposeControls::updateWrappingVisibility() {
|
||||||
const auto hidden = _hidden.current();
|
const auto hidden = _hidden.current();
|
||||||
const auto &restriction = _writeRestriction.current();
|
const auto &restriction = _writeRestriction.current();
|
||||||
|
@ -2454,7 +2479,10 @@ auto ComposeControls::computeSendButtonType() const {
|
||||||
} else if (_isInlineBot) {
|
} else if (_isInlineBot) {
|
||||||
return Type::Cancel;
|
return Type::Cancel;
|
||||||
} else if (showRecordButton()) {
|
} else if (showRecordButton()) {
|
||||||
return Type::Record;
|
return (Core::App().settings().recordVideoMessages()
|
||||||
|
&& _canRecordVideoMessage)
|
||||||
|
? Type::Round
|
||||||
|
: Type::Record;
|
||||||
}
|
}
|
||||||
return (_mode == Mode::Normal) ? Type::Send : Type::Schedule;
|
return (_mode == Mode::Normal) ? Type::Send : Type::Schedule;
|
||||||
}
|
}
|
||||||
|
@ -2487,7 +2515,9 @@ void ComposeControls::updateSendButtonType() {
|
||||||
}();
|
}();
|
||||||
_send->setSlowmodeDelay(delay);
|
_send->setSlowmodeDelay(delay);
|
||||||
_send->setDisabled(_sendDisabledBySlowmode.current()
|
_send->setDisabled(_sendDisabledBySlowmode.current()
|
||||||
&& (type == Type::Send || type == Type::Record));
|
&& (type == Type::Send
|
||||||
|
|| type == Type::Record
|
||||||
|
|| type == Type::Round));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComposeControls::finishAnimating() {
|
void ComposeControls::finishAnimating() {
|
||||||
|
@ -3149,8 +3179,9 @@ bool ComposeControls::isRecording() const {
|
||||||
bool ComposeControls::isRecordingPressed() const {
|
bool ComposeControls::isRecordingPressed() const {
|
||||||
return !_voiceRecordBar->isRecordingLocked()
|
return !_voiceRecordBar->isRecordingLocked()
|
||||||
&& (!_voiceRecordBar->isHidden()
|
&& (!_voiceRecordBar->isHidden()
|
||||||
|| (_send->type() == Ui::SendButton::Type::Record
|
|| (_send->isDown()
|
||||||
&& _send->isDown()));
|
&& (_send->type() == Ui::SendButton::Type::Record
|
||||||
|
|| _send->type() == Ui::SendButton::Type::Round)));
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<bool> ComposeControls::recordingActiveValue() const {
|
rpl::producer<bool> ComposeControls::recordingActiveValue() const {
|
||||||
|
|
|
@ -278,6 +278,7 @@ private:
|
||||||
bool updateSendAsButton();
|
bool updateSendAsButton();
|
||||||
void updateAttachBotsMenu();
|
void updateAttachBotsMenu();
|
||||||
void updateHeight();
|
void updateHeight();
|
||||||
|
void updateRecordMediaState();
|
||||||
void updateWrappingVisibility();
|
void updateWrappingVisibility();
|
||||||
void updateControlsVisibility();
|
void updateControlsVisibility();
|
||||||
void updateControlsGeometry(QSize size);
|
void updateControlsGeometry(QSize size);
|
||||||
|
@ -437,6 +438,9 @@ private:
|
||||||
bool _botCommandShown = false;
|
bool _botCommandShown = false;
|
||||||
bool _likeShown = false;
|
bool _likeShown = false;
|
||||||
|
|
||||||
|
bool _canRecordVideoMessage = false;
|
||||||
|
bool _canRecordAudioMessage = false;
|
||||||
|
|
||||||
FullMsgId _editingId;
|
FullMsgId _editingId;
|
||||||
std::shared_ptr<Data::PhotoMedia> _photoEditMedia;
|
std::shared_ptr<Data::PhotoMedia> _photoEditMedia;
|
||||||
bool _canReplaceMedia = false;
|
bool _canReplaceMedia = false;
|
||||||
|
|
|
@ -1535,6 +1535,7 @@ void VoiceRecordBar::init() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_recordingTipRequired = true;
|
_recordingTipRequired = true;
|
||||||
|
_recordingVideo = (_send->type() == Ui::SendButton::Type::Round);
|
||||||
_startTimer.callOnce(st::universalDuration);
|
_startTimer.callOnce(st::universalDuration);
|
||||||
} else if (e->type() == QEvent::MouseButtonRelease) {
|
} else if (e->type() == QEvent::MouseButtonRelease) {
|
||||||
if (base::take(_recordingTipRequired)) {
|
if (base::take(_recordingTipRequired)) {
|
||||||
|
@ -1589,6 +1590,11 @@ void VoiceRecordBar::visibilityAnimate(bool show, Fn<void()> &&callback) {
|
||||||
// _videoHiding.back()->hide();
|
// _videoHiding.back()->hide();
|
||||||
//}
|
//}
|
||||||
AssertIsDebug();
|
AssertIsDebug();
|
||||||
|
if (_send->type() == Ui::SendButton::Type::Round) {
|
||||||
|
_level->setType(VoiceRecordButton::Type::Round);
|
||||||
|
} else {
|
||||||
|
_level->setType(VoiceRecordButton::Type::Record);
|
||||||
|
}
|
||||||
const auto to = show ? 1. : 0.;
|
const auto to = show ? 1. : 0.;
|
||||||
const auto from = show ? 0. : 1.;
|
const auto from = show ? 0. : 1.;
|
||||||
auto animationCallback = [=, callback = std::move(callback)](auto value) {
|
auto animationCallback = [=, callback = std::move(callback)](auto value) {
|
||||||
|
@ -1656,7 +1662,6 @@ void VoiceRecordBar::startRecording() {
|
||||||
if (isRecording()) {
|
if (isRecording()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_recordingVideo = true; AssertIsDebug();
|
|
||||||
auto appearanceCallback = [=] {
|
auto appearanceCallback = [=] {
|
||||||
if (_showAnimation.animating()) {
|
if (_showAnimation.animating()) {
|
||||||
return;
|
return;
|
||||||
|
@ -1694,6 +1699,15 @@ void VoiceRecordBar::startRecording() {
|
||||||
}, [=] {
|
}, [=] {
|
||||||
stop(false);
|
stop(false);
|
||||||
}, _recordingLifetime);
|
}, _recordingLifetime);
|
||||||
|
if (_videoRecorder) {
|
||||||
|
_videoRecorder->updated(
|
||||||
|
) | rpl::start_with_next_error([=](const Update &update) {
|
||||||
|
_recordingTipRequired = (update.samples < kMinSamples);
|
||||||
|
recordUpdated(update.level, update.samples);
|
||||||
|
}, [=] {
|
||||||
|
stop(false);
|
||||||
|
}, _recordingLifetime);
|
||||||
|
}
|
||||||
_recordingLifetime.add([=] {
|
_recordingLifetime.add([=] {
|
||||||
_recording = false;
|
_recording = false;
|
||||||
});
|
});
|
||||||
|
@ -2013,7 +2027,8 @@ bool VoiceRecordBar::isListenState() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VoiceRecordBar::isTypeRecord() const {
|
bool VoiceRecordBar::isTypeRecord() const {
|
||||||
return (_send->type() == Ui::SendButton::Type::Record);
|
return (_send->type() == Ui::SendButton::Type::Record)
|
||||||
|
|| (_send->type() == Ui::SendButton::Type::Round);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VoiceRecordBar::isRecordingByAnotherBar() const {
|
bool VoiceRecordBar::isRecordingByAnotherBar() const {
|
||||||
|
|
|
@ -137,10 +137,14 @@ void VoiceRecordButton::init() {
|
||||||
const auto state = *currentState;
|
const auto state = *currentState;
|
||||||
const auto icon = (state == Type::Send)
|
const auto icon = (state == Type::Send)
|
||||||
? st::historySendIcon
|
? st::historySendIcon
|
||||||
: st::historyRecordVoiceActive;
|
: (state == Type::Record)
|
||||||
|
? st::historyRecordVoiceActive
|
||||||
|
: st::historyRecordRoundActive;
|
||||||
const auto position = (state == Type::Send)
|
const auto position = (state == Type::Send)
|
||||||
? st::historyRecordSendIconPosition
|
? st::historyRecordSendIconPosition
|
||||||
: QPoint(0, 0);
|
: (state == Type::Record)
|
||||||
|
? QPoint(0, 0)
|
||||||
|
: st::historyRecordRoundIconPosition;
|
||||||
icon.paint(
|
icon.paint(
|
||||||
p,
|
p,
|
||||||
-icon.width() / 2 + position.x(),
|
-icon.width() / 2 + position.x(),
|
||||||
|
|
|
@ -31,6 +31,7 @@ public:
|
||||||
enum class Type {
|
enum class Type {
|
||||||
Send,
|
Send,
|
||||||
Record,
|
Record,
|
||||||
|
Round,
|
||||||
};
|
};
|
||||||
|
|
||||||
void setType(Type state);
|
void setType(Type state);
|
||||||
|
|
|
@ -480,6 +480,8 @@ storiesLike: IconButton(storiesAttach) {
|
||||||
}
|
}
|
||||||
storiesRecordVoice: icon {{ "chat/input_record", storiesComposeGrayIcon }};
|
storiesRecordVoice: icon {{ "chat/input_record", storiesComposeGrayIcon }};
|
||||||
storiesRecordVoiceOver: icon {{ "chat/input_record", storiesComposeGrayIcon }};
|
storiesRecordVoiceOver: icon {{ "chat/input_record", storiesComposeGrayIcon }};
|
||||||
|
storiesRecordRound: icon {{ "info/info_media_round", storiesComposeGrayIcon }};
|
||||||
|
storiesRecordRoundOver: icon {{ "info/info_media_round", storiesComposeGrayIcon }};
|
||||||
storiesRemoveSet: IconButton(stickerPanRemoveSet) {
|
storiesRemoveSet: IconButton(stickerPanRemoveSet) {
|
||||||
icon: icon {{ "simple_close", storiesComposeGrayIcon }};
|
icon: icon {{ "simple_close", storiesComposeGrayIcon }};
|
||||||
iconOver: icon {{ "simple_close", storiesComposeGrayIcon }};
|
iconOver: icon {{ "simple_close", storiesComposeGrayIcon }};
|
||||||
|
@ -686,6 +688,8 @@ storiesComposeControls: ComposeControls(defaultComposeControls) {
|
||||||
}
|
}
|
||||||
record: storiesRecordVoice;
|
record: storiesRecordVoice;
|
||||||
recordOver: storiesRecordVoiceOver;
|
recordOver: storiesRecordVoiceOver;
|
||||||
|
round: storiesRecordRound;
|
||||||
|
roundOver: storiesRecordRoundOver;
|
||||||
sendDisabledFg: storiesComposeGrayText;
|
sendDisabledFg: storiesComposeGrayText;
|
||||||
}
|
}
|
||||||
attach: storiesAttach;
|
attach: storiesAttach;
|
||||||
|
|
|
@ -679,7 +679,7 @@ object_ptr<Ui::GenericBox> ChooseCameraDeviceBox(
|
||||||
const style::Radio *radioSt) {
|
const style::Radio *radioSt) {
|
||||||
return Box(
|
return Box(
|
||||||
ChooseMediaDeviceBox,
|
ChooseMediaDeviceBox,
|
||||||
tr::lng_settings_call_device_default(),
|
tr::lng_settings_call_camera(),
|
||||||
Core::App().mediaDevices().devicesValue(DeviceType::Camera),
|
Core::App().mediaDevices().devicesValue(DeviceType::Camera),
|
||||||
std::move(currentId),
|
std::move(currentId),
|
||||||
std::move(chosen),
|
std::move(chosen),
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace Ui {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kSide = 400;
|
constexpr auto kSide = 400;
|
||||||
constexpr auto kOutputFilename = "C:\\Tmp\\TestVideo\\output.mp4";
|
constexpr auto kUpdateEach = crl::time(100);
|
||||||
|
|
||||||
using namespace FFmpeg;
|
using namespace FFmpeg;
|
||||||
|
|
||||||
|
@ -33,6 +33,9 @@ public:
|
||||||
void push(int64 mcstimestamp, const QImage &frame);
|
void push(int64 mcstimestamp, const QImage &frame);
|
||||||
void push(const Media::Capture::Chunk &chunk);
|
void push(const Media::Capture::Chunk &chunk);
|
||||||
|
|
||||||
|
using Update = Media::Capture::Update;
|
||||||
|
[[nodiscard]] rpl::producer<Update, rpl::empty_error> updated() const;
|
||||||
|
|
||||||
[[nodiscard]] RoundVideoResult finish();
|
[[nodiscard]] RoundVideoResult finish();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -42,6 +45,23 @@ private:
|
||||||
int write(uint8_t *buf, int buf_size);
|
int write(uint8_t *buf, int buf_size);
|
||||||
int64_t seek(int64_t offset, int whence);
|
int64_t seek(int64_t offset, int whence);
|
||||||
|
|
||||||
|
void initEncoding();
|
||||||
|
bool initVideo();
|
||||||
|
bool initAudio();
|
||||||
|
void deinitEncoding();
|
||||||
|
void finishEncoding();
|
||||||
|
void fail();
|
||||||
|
|
||||||
|
void encodeVideoFrame(int64 mcstimestamp, const QImage &frame);
|
||||||
|
void encodeAudioFrame(const Media::Capture::Chunk &chunk);
|
||||||
|
bool writeFrame(
|
||||||
|
const FramePointer &frame,
|
||||||
|
const CodecPointer &codec,
|
||||||
|
AVStream *stream);
|
||||||
|
|
||||||
|
void updateMaxLevel(const Media::Capture::Chunk &chunk);
|
||||||
|
void updateResultDuration(int64 pts, AVRational timeBase);
|
||||||
|
|
||||||
const crl::weak_on_queue<Private> _weak;
|
const crl::weak_on_queue<Private> _weak;
|
||||||
|
|
||||||
FormatPointer _format;
|
FormatPointer _format;
|
||||||
|
@ -72,18 +92,9 @@ private:
|
||||||
int64_t _resultOffset = 0;
|
int64_t _resultOffset = 0;
|
||||||
crl::time _resultDuration = 0;
|
crl::time _resultDuration = 0;
|
||||||
|
|
||||||
void initEncoding();
|
ushort _maxLevelSinceLastUpdate = 0;
|
||||||
bool initVideo();
|
crl::time _lastUpdateDuration = 0;
|
||||||
bool initAudio();
|
rpl::event_stream<Update, rpl::empty_error> _updates;
|
||||||
void deinitEncoding();
|
|
||||||
void finishEncoding();
|
|
||||||
|
|
||||||
void encodeVideoFrame(int64 mcstimestamp, const QImage &frame);
|
|
||||||
void encodeAudioFrame(const Media::Capture::Chunk &chunk);
|
|
||||||
bool writeFrame(
|
|
||||||
const FramePointer &frame,
|
|
||||||
const CodecPointer &codec,
|
|
||||||
AVStream *stream);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -94,11 +105,6 @@ RoundVideoRecorder::Private::Private(crl::weak_on_queue<Private> weak)
|
||||||
|
|
||||||
RoundVideoRecorder::Private::~Private() {
|
RoundVideoRecorder::Private::~Private() {
|
||||||
finishEncoding();
|
finishEncoding();
|
||||||
|
|
||||||
QFile file(kOutputFilename);
|
|
||||||
if (file.open(QIODevice::WriteOnly)) {
|
|
||||||
file.write(_result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int RoundVideoRecorder::Private::Write(void *opaque, uint8_t *buf, int buf_size) {
|
int RoundVideoRecorder::Private::Write(void *opaque, uint8_t *buf, int buf_size) {
|
||||||
|
@ -155,7 +161,7 @@ void RoundVideoRecorder::Private::initEncoding() {
|
||||||
"mp4"_q);
|
"mp4"_q);
|
||||||
|
|
||||||
if (!initVideo() || !initAudio()) {
|
if (!initVideo() || !initAudio()) {
|
||||||
deinitEncoding();
|
fail();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +170,7 @@ void RoundVideoRecorder::Private::initEncoding() {
|
||||||
nullptr));
|
nullptr));
|
||||||
if (error) {
|
if (error) {
|
||||||
LogError("avformat_write_header", error);
|
LogError("avformat_write_header", error);
|
||||||
deinitEncoding();
|
fail();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,6 +349,11 @@ void RoundVideoRecorder::Private::finishEncoding() {
|
||||||
deinitEncoding();
|
deinitEncoding();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto RoundVideoRecorder::Private::updated() const
|
||||||
|
-> rpl::producer<Update, rpl::empty_error> {
|
||||||
|
return _updates.events();
|
||||||
|
}
|
||||||
|
|
||||||
RoundVideoResult RoundVideoRecorder::Private::finish() {
|
RoundVideoResult RoundVideoRecorder::Private::finish() {
|
||||||
if (!_format) {
|
if (!_format) {
|
||||||
return {};
|
return {};
|
||||||
|
@ -355,6 +366,11 @@ RoundVideoResult RoundVideoRecorder::Private::finish() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void RoundVideoRecorder::Private::fail() {
|
||||||
|
deinitEncoding();
|
||||||
|
_updates.fire_error({});
|
||||||
|
}
|
||||||
|
|
||||||
void RoundVideoRecorder::Private::deinitEncoding() {
|
void RoundVideoRecorder::Private::deinitEncoding() {
|
||||||
_swsContext = nullptr;
|
_swsContext = nullptr;
|
||||||
_videoCodec = nullptr;
|
_videoCodec = nullptr;
|
||||||
|
@ -406,7 +422,7 @@ void RoundVideoRecorder::Private::encodeVideoFrame(
|
||||||
AV_PIX_FMT_YUV420P,
|
AV_PIX_FMT_YUV420P,
|
||||||
&_swsContext);
|
&_swsContext);
|
||||||
if (!_swsContext) {
|
if (!_swsContext) {
|
||||||
deinitEncoding();
|
fail();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,14 +460,15 @@ void RoundVideoRecorder::Private::encodeVideoFrame(
|
||||||
_videoFrame->linesize);
|
_videoFrame->linesize);
|
||||||
|
|
||||||
_videoFrame->pts = mcstimestamp - _videoFirstTimestamp;
|
_videoFrame->pts = mcstimestamp - _videoFirstTimestamp;
|
||||||
|
|
||||||
LOG(("Audio At: %1").arg(_videoFrame->pts / 1'000'000.));
|
|
||||||
if (!writeFrame(_videoFrame, _videoCodec, _videoStream)) {
|
if (!writeFrame(_videoFrame, _videoCodec, _videoStream)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoundVideoRecorder::Private::encodeAudioFrame(const Media::Capture::Chunk &chunk) {
|
void RoundVideoRecorder::Private::encodeAudioFrame(
|
||||||
|
const Media::Capture::Chunk &chunk) {
|
||||||
|
updateMaxLevel(chunk);
|
||||||
|
|
||||||
if (_audioTail.isEmpty()) {
|
if (_audioTail.isEmpty()) {
|
||||||
_audioTail = chunk.samples;
|
_audioTail = chunk.samples;
|
||||||
} else {
|
} else {
|
||||||
|
@ -459,7 +476,8 @@ void RoundVideoRecorder::Private::encodeAudioFrame(const Media::Capture::Chunk &
|
||||||
}
|
}
|
||||||
|
|
||||||
const int inSamples = _audioTail.size() / sizeof(int16_t);
|
const int inSamples = _audioTail.size() / sizeof(int16_t);
|
||||||
const uint8_t *inData = reinterpret_cast<const uint8_t*>(_audioTail.constData());
|
const uint8_t *inData = reinterpret_cast<const uint8_t*>(
|
||||||
|
_audioTail.constData());
|
||||||
int samplesProcessed = 0;
|
int samplesProcessed = 0;
|
||||||
|
|
||||||
while (samplesProcessed + _audioCodec->frame_size <= inSamples) {
|
while (samplesProcessed + _audioCodec->frame_size <= inSamples) {
|
||||||
|
@ -484,7 +502,7 @@ void RoundVideoRecorder::Private::encodeAudioFrame(const Media::Capture::Chunk &
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
LogError("swr_convert", error);
|
LogError("swr_convert", error);
|
||||||
deinitEncoding();
|
fail();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -493,8 +511,6 @@ void RoundVideoRecorder::Private::encodeAudioFrame(const Media::Capture::Chunk &
|
||||||
|
|
||||||
_audioFrame->pts = _audioPts;
|
_audioFrame->pts = _audioPts;
|
||||||
_audioPts += _audioFrame->nb_samples;
|
_audioPts += _audioFrame->nb_samples;
|
||||||
|
|
||||||
LOG(("Audio At: %1").arg(_audioFrame->pts / 48'000.));
|
|
||||||
if (!writeFrame(_audioFrame, _audioCodec, _audioStream)) {
|
if (!writeFrame(_audioFrame, _audioCodec, _audioStream)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -514,10 +530,14 @@ bool RoundVideoRecorder::Private::writeFrame(
|
||||||
const FramePointer &frame,
|
const FramePointer &frame,
|
||||||
const CodecPointer &codec,
|
const CodecPointer &codec,
|
||||||
AVStream *stream) {
|
AVStream *stream) {
|
||||||
|
if (frame) {
|
||||||
|
updateResultDuration(frame->pts, codec->time_base);
|
||||||
|
}
|
||||||
|
|
||||||
auto error = AvErrorWrap(avcodec_send_frame(codec.get(), frame.get()));
|
auto error = AvErrorWrap(avcodec_send_frame(codec.get(), frame.get()));
|
||||||
if (error) {
|
if (error) {
|
||||||
LogError("avcodec_send_frame", error);
|
LogError("avcodec_send_frame", error);
|
||||||
deinitEncoding();
|
fail();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -533,21 +553,19 @@ bool RoundVideoRecorder::Private::writeFrame(
|
||||||
return true; // Encoding finished
|
return true; // Encoding finished
|
||||||
} else if (error) {
|
} else if (error) {
|
||||||
LogError("avcodec_receive_packet", error);
|
LogError("avcodec_receive_packet", error);
|
||||||
deinitEncoding();
|
fail();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pkt->stream_index = stream->index;
|
pkt->stream_index = stream->index;
|
||||||
av_packet_rescale_ts(pkt, codec->time_base, stream->time_base);
|
av_packet_rescale_ts(pkt, codec->time_base, stream->time_base);
|
||||||
|
|
||||||
accumulate_max(
|
updateResultDuration(pkt->pts, stream->time_base);
|
||||||
_resultDuration,
|
|
||||||
PtsToTimeCeil(pkt->pts, stream->time_base));
|
|
||||||
|
|
||||||
error = AvErrorWrap(av_interleaved_write_frame(_format.get(), pkt));
|
error = AvErrorWrap(av_interleaved_write_frame(_format.get(), pkt));
|
||||||
if (error) {
|
if (error) {
|
||||||
LogError("av_interleaved_write_frame", error);
|
LogError("av_interleaved_write_frame", error);
|
||||||
deinitEncoding();
|
fail();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -555,6 +573,30 @@ bool RoundVideoRecorder::Private::writeFrame(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RoundVideoRecorder::Private::updateMaxLevel(
|
||||||
|
const Media::Capture::Chunk &chunk) {
|
||||||
|
const auto &list = chunk.samples;
|
||||||
|
const auto samples = int(list.size() / sizeof(ushort));
|
||||||
|
const auto data = reinterpret_cast<const ushort*>(list.constData());
|
||||||
|
for (const auto value : gsl::make_span(data, samples)) {
|
||||||
|
accumulate_max(_maxLevelSinceLastUpdate, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoundVideoRecorder::Private::updateResultDuration(
|
||||||
|
int64 pts,
|
||||||
|
AVRational timeBase) {
|
||||||
|
accumulate_max(_resultDuration, PtsToTimeCeil(pts, timeBase));
|
||||||
|
|
||||||
|
if (_lastUpdateDuration + kUpdateEach >= _resultDuration) {
|
||||||
|
_lastUpdateDuration = _resultDuration;
|
||||||
|
_updates.fire({
|
||||||
|
.samples = int(_resultDuration * 48),
|
||||||
|
.level = base::take(_maxLevelSinceLastUpdate),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RoundVideoRecorder::RoundVideoRecorder(
|
RoundVideoRecorder::RoundVideoRecorder(
|
||||||
RoundVideoRecorderDescriptor &&descriptor)
|
RoundVideoRecorderDescriptor &&descriptor)
|
||||||
: _descriptor(std::move(descriptor))
|
: _descriptor(std::move(descriptor))
|
||||||
|
@ -573,6 +615,13 @@ Fn<void(Media::Capture::Chunk)> RoundVideoRecorder::audioChunkProcessor() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto RoundVideoRecorder::updated() const
|
||||||
|
-> rpl::producer<Update, rpl::empty_error> {
|
||||||
|
return _private.producer_on_main([](const Private &that) {
|
||||||
|
return that.updated();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void RoundVideoRecorder::hide(Fn<void(RoundVideoResult)> done) {
|
void RoundVideoRecorder::hide(Fn<void(RoundVideoResult)> done) {
|
||||||
if (done) {
|
if (done) {
|
||||||
_private.with([done = std::move(done)](Private &that) {
|
_private.with([done = std::move(done)](Private &that) {
|
||||||
|
@ -642,6 +691,9 @@ void RoundVideoRecorder::setPaused(bool paused) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_paused = paused;
|
_paused = paused;
|
||||||
|
_descriptor.track->setState(paused
|
||||||
|
? Webrtc::VideoState::Inactive
|
||||||
|
: Webrtc::VideoState::Active);
|
||||||
_preview->update();
|
_preview->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace Media::Capture {
|
namespace Media::Capture {
|
||||||
struct Chunk;
|
struct Chunk;
|
||||||
|
struct Update;
|
||||||
} // namespace Media::Capture
|
} // namespace Media::Capture
|
||||||
|
|
||||||
namespace tgcalls {
|
namespace tgcalls {
|
||||||
|
@ -49,6 +50,9 @@ public:
|
||||||
void setPaused(bool paused);
|
void setPaused(bool paused);
|
||||||
void hide(Fn<void(RoundVideoResult)> done = nullptr);
|
void hide(Fn<void(RoundVideoResult)> done = nullptr);
|
||||||
|
|
||||||
|
using Update = Media::Capture::Update;
|
||||||
|
[[nodiscard]] rpl::producer<Update, rpl::empty_error> updated() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Private;
|
class Private;
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,7 @@ void SendButton::paintEvent(QPaintEvent *e) {
|
||||||
}
|
}
|
||||||
switch (_type) {
|
switch (_type) {
|
||||||
case Type::Record: paintRecord(p, over); break;
|
case Type::Record: paintRecord(p, over); break;
|
||||||
|
case Type::Round: paintRound(p, over); break;
|
||||||
case Type::Save: paintSave(p, over); break;
|
case Type::Save: paintSave(p, over); break;
|
||||||
case Type::Cancel: paintCancel(p, over); break;
|
case Type::Cancel: paintCancel(p, over); break;
|
||||||
case Type::Send: paintSend(p, over); break;
|
case Type::Send: paintSend(p, over); break;
|
||||||
|
@ -108,6 +109,20 @@ void SendButton::paintRecord(QPainter &p, bool over) {
|
||||||
icon.paintInCenter(p, rect());
|
icon.paintInCenter(p, rect());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SendButton::paintRound(QPainter &p, bool over) {
|
||||||
|
if (!isDisabled()) {
|
||||||
|
paintRipple(
|
||||||
|
p,
|
||||||
|
(width() - _st.inner.rippleAreaSize) / 2,
|
||||||
|
_st.inner.rippleAreaPosition.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &icon = (isDisabled() || !over)
|
||||||
|
? _st.round
|
||||||
|
: _st.roundOver;
|
||||||
|
icon.paintInCenter(p, rect());
|
||||||
|
}
|
||||||
|
|
||||||
void SendButton::paintSave(QPainter &p, bool over) {
|
void SendButton::paintSave(QPainter &p, bool over) {
|
||||||
const auto &saveIcon = over
|
const auto &saveIcon = over
|
||||||
? st::historyEditSaveIconOver
|
? st::historyEditSaveIconOver
|
||||||
|
|
|
@ -26,6 +26,7 @@ public:
|
||||||
Schedule,
|
Schedule,
|
||||||
Save,
|
Save,
|
||||||
Record,
|
Record,
|
||||||
|
Round,
|
||||||
Cancel,
|
Cancel,
|
||||||
Slowmode,
|
Slowmode,
|
||||||
};
|
};
|
||||||
|
@ -47,6 +48,7 @@ private:
|
||||||
[[nodiscard]] bool isSlowmode() const;
|
[[nodiscard]] bool isSlowmode() const;
|
||||||
|
|
||||||
void paintRecord(QPainter &p, bool over);
|
void paintRecord(QPainter &p, bool over);
|
||||||
|
void paintRound(QPainter &p, bool over);
|
||||||
void paintSave(QPainter &p, bool over);
|
void paintSave(QPainter &p, bool over);
|
||||||
void paintCancel(QPainter &p, bool over);
|
void paintCancel(QPainter &p, bool over);
|
||||||
void paintSend(QPainter &p, bool over);
|
void paintSend(QPainter &p, bool over);
|
||||||
|
|
Loading…
Add table
Reference in a new issue