Show recording progress.

This commit is contained in:
John Preston 2024-10-18 15:17:29 +04:00
parent f74dd3ca1e
commit e59e4afd3e
4 changed files with 105 additions and 20 deletions

View file

@ -1534,13 +1534,11 @@ void VoiceRecordBar::init() {
if (_startRecordingFilter && _startRecordingFilter()) { if (_startRecordingFilter && _startRecordingFilter()) {
return; return;
} }
_recordingTipRequired = true; _recordingTipRequire = crl::now();
_recordingVideo = (_send->type() == Ui::SendButton::Type::Round); _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)) { checkTipRequired();
_recordingTipRequests.fire({});
}
_startTimer.cancel(); _startTimer.cancel();
} }
}, lifetime()); }, lifetime());
@ -1694,7 +1692,6 @@ void VoiceRecordBar::startRecording() {
} }
instance()->updated( instance()->updated(
) | rpl::start_with_next_error([=](const Update &update) { ) | rpl::start_with_next_error([=](const Update &update) {
_recordingTipRequired = (update.samples < kMinSamples);
recordUpdated(update.level, update.samples); recordUpdated(update.level, update.samples);
}, [=] { }, [=] {
stop(false); stop(false);
@ -1702,10 +1699,9 @@ void VoiceRecordBar::startRecording() {
if (_videoRecorder) { if (_videoRecorder) {
_videoRecorder->updated( _videoRecorder->updated(
) | rpl::start_with_next_error([=](const Update &update) { ) | rpl::start_with_next_error([=](const Update &update) {
_recordingTipRequired = (update.samples < kMinSamples);
recordUpdated(update.level, update.samples); recordUpdated(update.level, update.samples);
if (update.finished) { if (update.finished) {
stop(true); stop(update.samples >= kMinSamples);
} }
}, [=] { }, [=] {
stop(false); stop(false);
@ -1742,14 +1738,22 @@ void VoiceRecordBar::startRecording() {
} }
computeAndSetLockProgress(mouse->globalPos()); computeAndSetLockProgress(mouse->globalPos());
} else if (type == QEvent::MouseButtonRelease) { } else if (type == QEvent::MouseButtonRelease) {
if (base::take(_recordingTipRequired)) { checkTipRequired();
_recordingTipRequests.fire({});
}
stop(_inField.current()); stop(_inField.current());
} }
}, _recordingLifetime); }, _recordingLifetime);
} }
void VoiceRecordBar::checkTipRequired() {
const auto require = base::take(_recordingTipRequire);
const auto duration = st::universalDuration
+ (kMinSamples * crl::time(1000)
/ ::Media::Player::kDefaultFrequency);
if (require && (require + duration > crl::now())) {
_recordingTipRequests.fire({});
}
}
void VoiceRecordBar::recordUpdated(quint16 level, int samples) { void VoiceRecordBar::recordUpdated(quint16 level, int samples) {
_level->requestPaintLevel(level); _level->requestPaintLevel(level);
_recordingSamples = samples; _recordingSamples = samples;

View file

@ -124,6 +124,7 @@ private:
void updateTTLGeometry(TTLAnimationType type, float64 progress); void updateTTLGeometry(TTLAnimationType type, float64 progress);
void recordUpdated(quint16 level, int samples); void recordUpdated(quint16 level, int samples);
void checkTipRequired();
void stop(bool send); void stop(bool send);
void stopRecording(StopType type, bool ttlBeforeHide = false); void stopRecording(StopType type, bool ttlBeforeHide = false);
@ -192,7 +193,7 @@ private:
float64 _redCircleProgress = 0.; float64 _redCircleProgress = 0.;
rpl::event_stream<> _recordingTipRequests; rpl::event_stream<> _recordingTipRequests;
bool _recordingTipRequired = false; crl::time _recordingTipRequire = 0;
bool _lockFromBottom = false; bool _lockFromBottom = false;
std::unique_ptr<Ui::RoundVideoRecorder> _videoRecorder; std::unique_ptr<Ui::RoundVideoRecorder> _videoRecorder;

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ffmpeg/ffmpeg_utility.h" #include "ffmpeg/ffmpeg_utility.h"
#include "media/audio/media_audio_capture.h" #include "media/audio/media_audio_capture.h"
#include "ui/image/image_prepare.h" #include "ui/image/image_prepare.h"
#include "ui/arc_angles.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "ui/rp_widget.h" #include "ui/rp_widget.h"
#include "webrtc/webrtc_video_track.h" #include "webrtc/webrtc_video_track.h"
@ -703,10 +704,14 @@ Fn<void(Media::Capture::Chunk)> RoundVideoRecorder::audioChunkProcessor() {
}; };
} }
auto RoundVideoRecorder::updated() const auto RoundVideoRecorder::updated()
-> rpl::producer<Update, rpl::empty_error> { -> rpl::producer<Update, rpl::empty_error> {
return _private.producer_on_main([](const Private &that) { return _private.producer_on_main([](const Private &that) {
return that.updated(); return that.updated();
}) | rpl::before_next([=](const Update &update) {
const auto duration = (update.samples * crl::time(1000))
/ kAudioFrequency;
progressTo(duration / (1. * kMaxDuration));
}); });
} }
@ -725,6 +730,19 @@ void RoundVideoRecorder::hide(Fn<void(RoundVideoResult)> done) {
} }
} }
void RoundVideoRecorder::progressTo(float64 progress) {
if (_progress == progress) {
return;
}
_progressAnimation.start(
[=] { _preview->update(); },
progress,
_progress,
kUpdateEach);
_progress = progress;
_preview->update();
}
void RoundVideoRecorder::prepareFrame() { void RoundVideoRecorder::prepareFrame() {
if (_frameOriginal.isNull() || _preparedIndex == _lastAddedIndex) { if (_frameOriginal.isNull() || _preparedIndex == _lastAddedIndex) {
return; return;
@ -755,29 +773,82 @@ void RoundVideoRecorder::prepareFrame() {
_framePrepared.setDevicePixelRatio(ratio); _framePrepared.setDevicePixelRatio(ratio);
} }
void RoundVideoRecorder::createImages() {
const auto ratio = style::DevicePixelRatio();
_framePrepared = QImage(
QSize(_side, _side) * ratio,
QImage::Format_ARGB32_Premultiplied);
_framePrepared.fill(Qt::transparent);
_framePrepared.setDevicePixelRatio(ratio);
auto p = QPainter(&_framePrepared);
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(Qt::black);
p.drawEllipse(0, 0, _side, _side);
const auto side = _side + 2 * _extent;
_shadow = QImage(
QSize(side, side) * ratio,
QImage::Format_ARGB32_Premultiplied);
_shadow.fill(Qt::transparent);
_shadow.setDevicePixelRatio(ratio);
auto sp = QPainter(&_shadow);
auto shq = PainterHighQualityEnabler(sp);
QRadialGradient gradient(
QPointF(_extent + _side / 2, _extent + _side / 2),
_side / 2 + _extent);
gradient.setColorAt(0, QColor(0, 0, 0, 128));
gradient.setColorAt(0.8, QColor(0, 0, 0, 64));
gradient.setColorAt(1, QColor(0, 0, 0, 0));
sp.setPen(Qt::NoPen);
sp.fillRect(0, 0, side, side, gradient);
sp.end();
}
void RoundVideoRecorder::setup() { void RoundVideoRecorder::setup() {
const auto raw = _preview.get(); const auto raw = _preview.get();
_side = style::ConvertScale(kSide * 3 / 4); _side = style::ConvertScale(kSide * 3 / 4);
_progressStroke = st::radialLine;
_extent = _progressStroke * 8;
createImages();
_descriptor.container->sizeValue( _descriptor.container->sizeValue(
) | rpl::start_with_next([=](QSize outer) { ) | rpl::start_with_next([=](QSize outer) {
const auto side = _side + 2 * _extent;
raw->setGeometry( raw->setGeometry(
style::centerrect( style::centerrect(
QRect(QPoint(), outer), QRect(QPoint(), outer),
QRect(0, 0, _side, _side))); QRect(0, 0, side, side)));
}, raw->lifetime()); }, raw->lifetime());
raw->paintRequest() | rpl::start_with_next([=] { raw->paintRequest() | rpl::start_with_next([=] {
prepareFrame(); prepareFrame();
auto p = QPainter(raw); auto p = QPainter(raw);
if (!_framePrepared.isNull()) { p.drawImage(raw->rect(), _shadow);
p.drawImage(QRect(0, 0, _side, _side), _framePrepared); const auto inner = QRect(_extent, _extent, _side, _side);
} else { p.drawImage(inner, _framePrepared);
if (_progress > 0.) {
auto hq = PainterHighQualityEnabler(p); auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen); p.setPen(QPen(
p.setBrush(QColor(0, 0, 0)); Qt::white,
p.drawEllipse(0, 0, _side, _side); _progressStroke,
Qt::SolidLine,
Qt::RoundCap));
p.setBrush(Qt::NoBrush);
const auto add = _progressStroke * 3 / 2.;
const auto full = arc::kFullLength;
const auto length = int(base::SafeRound(
_progressAnimation.value(_progress) * full));
p.drawArc(
QRectF(inner).marginsAdded({ add, add, add, add }),
(full / 4) - length,
length);
} }
}, raw->lifetime()); }, raw->lifetime());

View file

@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
#include "ui/effects/animations.h"
#include <crl/crl_object_on_queue.h> #include <crl/crl_object_on_queue.h>
namespace Media::Capture { namespace Media::Capture {
@ -51,22 +53,29 @@ public:
void hide(Fn<void(RoundVideoResult)> done = nullptr); void hide(Fn<void(RoundVideoResult)> done = nullptr);
using Update = Media::Capture::Update; using Update = Media::Capture::Update;
[[nodiscard]] rpl::producer<Update, rpl::empty_error> updated() const; [[nodiscard]] rpl::producer<Update, rpl::empty_error> updated();
private: private:
class Private; class Private;
void setup(); void setup();
void prepareFrame(); void prepareFrame();
void createImages();
void progressTo(float64 progress);
const RoundVideoRecorderDescriptor _descriptor; const RoundVideoRecorderDescriptor _descriptor;
std::unique_ptr<RpWidget> _preview; std::unique_ptr<RpWidget> _preview;
crl::object_on_queue<Private> _private; crl::object_on_queue<Private> _private;
Ui::Animations::Simple _progressAnimation;
float64 _progress = 0.;
QImage _frameOriginal; QImage _frameOriginal;
QImage _framePrepared; QImage _framePrepared;
QImage _shadow;
int _lastAddedIndex = 0; int _lastAddedIndex = 0;
int _preparedIndex = 0; int _preparedIndex = 0;
int _side = 0; int _side = 0;
int _progressStroke = 0;
int _extent = 0;
bool _paused = false; bool _paused = false;
}; };