mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Prepare for round record pause/resume.
This commit is contained in:
parent
e8d87d37bb
commit
d7ffdbd78d
10 changed files with 163 additions and 71 deletions
BIN
Telegram/Resources/icons/voice_lock/input_round_s.png
Normal file
BIN
Telegram/Resources/icons/voice_lock/input_round_s.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 498 B |
BIN
Telegram/Resources/icons/voice_lock/input_round_s@2x.png
Normal file
BIN
Telegram/Resources/icons/voice_lock/input_round_s@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/voice_lock/input_round_s@3x.png
Normal file
BIN
Telegram/Resources/icons/voice_lock/input_round_s@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
|
@ -1210,6 +1210,7 @@ historyRecordLockBody: icon {{ "voice_lock/record_lock_body", historyToDownBg }}
|
||||||
historyRecordLockMargin: margins(4px, 4px, 4px, 4px);
|
historyRecordLockMargin: margins(4px, 4px, 4px, 4px);
|
||||||
historyRecordLockArrow: icon {{ "voice_lock/voice_arrow", historyToDownFg }};
|
historyRecordLockArrow: icon {{ "voice_lock/voice_arrow", historyToDownFg }};
|
||||||
historyRecordLockInput: icon {{ "voice_lock/input_mic_s", historyToDownFg }};
|
historyRecordLockInput: icon {{ "voice_lock/input_mic_s", historyToDownFg }};
|
||||||
|
historyRecordLockRound: icon {{ "voice_lock/input_round_s", historyToDownFg }};
|
||||||
historyRecordLockRippleMargin: margins(6px, 6px, 6px, 6px);
|
historyRecordLockRippleMargin: margins(6px, 6px, 6px, 6px);
|
||||||
|
|
||||||
historyRecordDelete: IconButton(historyAttach) {
|
historyRecordDelete: IconButton(historyAttach) {
|
||||||
|
|
|
@ -497,8 +497,7 @@ ListenWrap::ListenWrap(
|
||||||
, _data(data)
|
, _data(data)
|
||||||
, _delete(base::make_unique_q<Ui::IconButton>(parent, _st.remove))
|
, _delete(base::make_unique_q<Ui::IconButton>(parent, _st.remove))
|
||||||
, _durationFont(font)
|
, _durationFont(font)
|
||||||
, _duration(Ui::FormatDurationText(
|
, _duration(Ui::FormatDurationText(_data->duration / 1000))
|
||||||
float64(_data->samples) / ::Media::Player::kDefaultFrequency))
|
|
||||||
, _durationWidth(_durationFont->width(_duration))
|
, _durationWidth(_durationFont->width(_duration))
|
||||||
, _playPauseSt(st::mediaPlayerButton)
|
, _playPauseSt(st::mediaPlayerButton)
|
||||||
, _playPauseButton(base::make_unique_q<Ui::AbstractButton>(parent))
|
, _playPauseButton(base::make_unique_q<Ui::AbstractButton>(parent))
|
||||||
|
@ -634,7 +633,7 @@ void ListenWrap::initPlayButton() {
|
||||||
|
|
||||||
_mediaView->setBytes(_data->bytes);
|
_mediaView->setBytes(_data->bytes);
|
||||||
_document->size = _data->bytes.size();
|
_document->size = _data->bytes.size();
|
||||||
_document->type = VoiceDocument;
|
_document->type = _data->video ? RoundVideoDocument : VoiceDocument;
|
||||||
|
|
||||||
const auto &play = _playPauseSt.playOuter;
|
const auto &play = _playPauseSt.playOuter;
|
||||||
const auto &width = _waveformBgFinalCenterRect.height();
|
const auto &width = _waveformBgFinalCenterRect.height();
|
||||||
|
@ -833,6 +832,7 @@ public:
|
||||||
void requestPaintLockToStopProgress(float64 progress);
|
void requestPaintLockToStopProgress(float64 progress);
|
||||||
void requestPaintPauseToInputProgress(float64 progress);
|
void requestPaintPauseToInputProgress(float64 progress);
|
||||||
void setVisibleTopPart(int part);
|
void setVisibleTopPart(int part);
|
||||||
|
void setRecordingVideo(bool value);
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<> locks() const;
|
[[nodiscard]] rpl::producer<> locks() const;
|
||||||
[[nodiscard]] bool isLocked() const;
|
[[nodiscard]] bool isLocked() const;
|
||||||
|
@ -861,6 +861,7 @@ private:
|
||||||
float64 _pauseToInputProgress = 0.;
|
float64 _pauseToInputProgress = 0.;
|
||||||
rpl::variable<float64> _progress = 0.;
|
rpl::variable<float64> _progress = 0.;
|
||||||
int _visibleTopPart = -1;
|
int _visibleTopPart = -1;
|
||||||
|
bool _recordingVideo = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -884,6 +885,10 @@ void RecordLock::setVisibleTopPart(int part) {
|
||||||
_visibleTopPart = part;
|
_visibleTopPart = part;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RecordLock::setRecordingVideo(bool value) {
|
||||||
|
_recordingVideo = value;
|
||||||
|
}
|
||||||
|
|
||||||
void RecordLock::init() {
|
void RecordLock::init() {
|
||||||
shownValue(
|
shownValue(
|
||||||
) | rpl::start_with_next([=](bool shown) {
|
) | rpl::start_with_next([=](bool shown) {
|
||||||
|
@ -974,9 +979,10 @@ void RecordLock::drawProgress(QPainter &p) {
|
||||||
p.setBrush(_st.fg);
|
p.setBrush(_st.fg);
|
||||||
if (_pauseToInputProgress > 0.) {
|
if (_pauseToInputProgress > 0.) {
|
||||||
p.setOpacity(_pauseToInputProgress);
|
p.setOpacity(_pauseToInputProgress);
|
||||||
st::historyRecordLockInput.paintInCenter(
|
const auto &icon = _recordingVideo
|
||||||
p,
|
? st::historyRecordLockRound
|
||||||
blockRect.toRect());
|
: st::historyRecordLockInput;
|
||||||
|
icon.paintInCenter(p, blockRect.toRect());
|
||||||
p.setOpacity(1. - _pauseToInputProgress);
|
p.setOpacity(1. - _pauseToInputProgress);
|
||||||
}
|
}
|
||||||
p.drawRoundedRect(
|
p.drawRoundedRect(
|
||||||
|
@ -1533,6 +1539,7 @@ void VoiceRecordBar::init() {
|
||||||
}
|
}
|
||||||
_recordingTipRequire = crl::now();
|
_recordingTipRequire = crl::now();
|
||||||
_recordingVideo = (_send->type() == Ui::SendButton::Type::Round);
|
_recordingVideo = (_send->type() == Ui::SendButton::Type::Round);
|
||||||
|
_lock->setRecordingVideo(_recordingVideo);
|
||||||
_startTimer.callOnce(st::universalDuration);
|
_startTimer.callOnce(st::universalDuration);
|
||||||
} else if (e->type() == QEvent::MouseButtonRelease) {
|
} else if (e->type() == QEvent::MouseButtonRelease) {
|
||||||
checkTipRequired();
|
checkTipRequired();
|
||||||
|
@ -1680,7 +1687,9 @@ void VoiceRecordBar::startRecording() {
|
||||||
_paused = false;
|
_paused = false;
|
||||||
instance()->pause(false, nullptr);
|
instance()->pause(false, nullptr);
|
||||||
if (_videoRecorder) {
|
if (_videoRecorder) {
|
||||||
_videoRecorder->setPaused(false);
|
_videoRecorder->resume({
|
||||||
|
.content = _data.bytes,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
instance()->start(_videoRecorder
|
instance()->start(_videoRecorder
|
||||||
|
@ -1809,7 +1818,6 @@ void VoiceRecordBar::stopRecording(StopType type, bool ttlBeforeHide) {
|
||||||
using namespace ::Media::Capture;
|
using namespace ::Media::Capture;
|
||||||
if (type == StopType::Cancel) {
|
if (type == StopType::Cancel) {
|
||||||
if (_videoRecorder) {
|
if (_videoRecorder) {
|
||||||
_videoRecorder->setPaused(true);
|
|
||||||
_videoRecorder->hide();
|
_videoRecorder->hide();
|
||||||
}
|
}
|
||||||
instance()->stop(crl::guard(this, [=](Result &&data) {
|
instance()->stop(crl::guard(this, [=](Result &&data) {
|
||||||
|
@ -1817,29 +1825,54 @@ void VoiceRecordBar::stopRecording(StopType type, bool ttlBeforeHide) {
|
||||||
}));
|
}));
|
||||||
} else if (type == StopType::Listen) {
|
} else if (type == StopType::Listen) {
|
||||||
if (_videoRecorder) {
|
if (_videoRecorder) {
|
||||||
_videoRecorder->setPaused(true);
|
const auto weak = Ui::MakeWeak(this);
|
||||||
|
_videoRecorder->pause([=](Ui::RoundVideoResult data) {
|
||||||
|
crl::on_main([=, data = std::move(data)]() mutable {
|
||||||
|
if (weak) {
|
||||||
|
window()->raise();
|
||||||
|
window()->activateWindow();
|
||||||
|
|
||||||
|
_paused = true;
|
||||||
|
_data = ::Media::Capture::Result{
|
||||||
|
.bytes = std::move(data.content),
|
||||||
|
//.waveform = std::move(data.waveform),
|
||||||
|
.duration = data.duration,
|
||||||
|
.video = true,
|
||||||
|
};
|
||||||
|
_listen = std::make_unique<ListenWrap>(
|
||||||
|
this,
|
||||||
|
_st,
|
||||||
|
&_show->session(),
|
||||||
|
&_data,
|
||||||
|
_cancelFont);
|
||||||
|
_listenChanges.fire({});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
instance()->pause(true);
|
||||||
|
} else {
|
||||||
|
instance()->pause(true, crl::guard(this, [=](Result &&data) {
|
||||||
|
if (data.bytes.isEmpty()) {
|
||||||
|
// Close everything.
|
||||||
|
stop(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_paused = true;
|
||||||
|
_data = std::move(data);
|
||||||
|
|
||||||
|
window()->raise();
|
||||||
|
window()->activateWindow();
|
||||||
|
_listen = std::make_unique<ListenWrap>(
|
||||||
|
this,
|
||||||
|
_st,
|
||||||
|
&_show->session(),
|
||||||
|
&_data,
|
||||||
|
_cancelFont);
|
||||||
|
_listenChanges.fire({});
|
||||||
|
|
||||||
|
// _lockShowing = false;
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
instance()->pause(true, crl::guard(this, [=](Result &&data) {
|
|
||||||
if (data.bytes.isEmpty()) {
|
|
||||||
// Close everything.
|
|
||||||
stop(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_paused = true;
|
|
||||||
_data = std::move(data);
|
|
||||||
|
|
||||||
window()->raise();
|
|
||||||
window()->activateWindow();
|
|
||||||
_listen = std::make_unique<ListenWrap>(
|
|
||||||
this,
|
|
||||||
_st,
|
|
||||||
&_show->session(),
|
|
||||||
&_data,
|
|
||||||
_cancelFont);
|
|
||||||
_listenChanges.fire({});
|
|
||||||
|
|
||||||
// _lockShowing = false;
|
|
||||||
}));
|
|
||||||
} else if (type == StopType::Send) {
|
} else if (type == StopType::Send) {
|
||||||
if (_videoRecorder) {
|
if (_videoRecorder) {
|
||||||
const auto weak = Ui::MakeWeak(this);
|
const auto weak = Ui::MakeWeak(this);
|
||||||
|
@ -1882,7 +1915,7 @@ void VoiceRecordBar::stopRecording(StopType type, bool ttlBeforeHide) {
|
||||||
_sendVoiceRequests.fire({
|
_sendVoiceRequests.fire({
|
||||||
_data.bytes,
|
_data.bytes,
|
||||||
_data.waveform,
|
_data.waveform,
|
||||||
Duration(_data.samples),
|
_data.duration,
|
||||||
options,
|
options,
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
@ -1949,7 +1982,7 @@ void VoiceRecordBar::requestToSendWithOptions(Api::SendOptions options) {
|
||||||
_sendVoiceRequests.fire({
|
_sendVoiceRequests.fire({
|
||||||
_data.bytes,
|
_data.bytes,
|
||||||
_data.waveform,
|
_data.waveform,
|
||||||
Duration(_data.samples),
|
_data.duration,
|
||||||
options,
|
options,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,13 +170,15 @@ void Instance::stop(Fn<void(Result&&)> callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Instance::pause(bool value, Fn<void(Result&&)> callback) {
|
void Instance::pause(bool value, Fn<void(Result&&)> callback) {
|
||||||
Expects(callback != nullptr || !value);
|
|
||||||
InvokeQueued(_inner.get(), [=] {
|
InvokeQueued(_inner.get(), [=] {
|
||||||
_inner->pause(value, [=](Result &&result) {
|
auto done = callback
|
||||||
crl::on_main([=, result = std::move(result)]() mutable {
|
? [=](Result &&result) {
|
||||||
callback(std::move(result));
|
crl::on_main([=, result = std::move(result)]() mutable {
|
||||||
});
|
callback(std::move(result));
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
: std::move(callback);
|
||||||
|
_inner->pause(value, std::move(done));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,11 +483,16 @@ void Instance::Inner::pause(bool value, Fn<void(Result&&)> callback) {
|
||||||
if (!_paused) {
|
if (!_paused) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
callback({
|
if (callback) {
|
||||||
d->fullSamples ? d->data : QByteArray(),
|
callback({
|
||||||
d->fullSamples ? CollectWaveform(d->waveform) : VoiceWaveform(),
|
.bytes = d->fullSamples ? d->data : QByteArray(),
|
||||||
qint32(d->fullSamples),
|
.waveform = (d->fullSamples
|
||||||
});
|
? CollectWaveform(d->waveform)
|
||||||
|
: VoiceWaveform()),
|
||||||
|
.duration = ((d->fullSamples * crl::time(1000))
|
||||||
|
/ int64(kCaptureFrequency)),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Instance::Inner::stop(Fn<void(Result&&)> callback) {
|
void Instance::Inner::stop(Fn<void(Result&&)> callback) {
|
||||||
|
@ -620,7 +627,11 @@ void Instance::Inner::stop(Fn<void(Result&&)> callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needResult) {
|
if (needResult) {
|
||||||
callback({ result, waveform, samples });
|
callback({
|
||||||
|
.bytes = result,
|
||||||
|
.waveform = waveform,
|
||||||
|
.duration = (samples * crl::time(1000)) / kCaptureFrequency,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ public:
|
||||||
|
|
||||||
void start(Fn<void(Chunk)> externalProcessing = nullptr);
|
void start(Fn<void(Chunk)> externalProcessing = nullptr);
|
||||||
void stop(Fn<void(Result&&)> callback = nullptr);
|
void stop(Fn<void(Result&&)> callback = nullptr);
|
||||||
void pause(bool value, Fn<void(Result&&)> callback);
|
void pause(bool value, Fn<void(Result&&)> callback = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Inner;
|
class Inner;
|
||||||
|
|
|
@ -12,7 +12,8 @@ namespace Media::Capture {
|
||||||
struct Result {
|
struct Result {
|
||||||
QByteArray bytes;
|
QByteArray bytes;
|
||||||
VoiceWaveform waveform;
|
VoiceWaveform waveform;
|
||||||
int samples = 0;
|
crl::time duration;
|
||||||
|
bool video = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Media::Capture
|
} // namespace Media::Capture
|
||||||
|
|
|
@ -46,6 +46,7 @@ public:
|
||||||
[[nodiscard]] rpl::producer<Update, Error> updated() const;
|
[[nodiscard]] rpl::producer<Update, Error> updated() const;
|
||||||
|
|
||||||
[[nodiscard]] RoundVideoResult finish();
|
[[nodiscard]] RoundVideoResult finish();
|
||||||
|
void restart(RoundVideoPartial partial);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int Write(void *opaque, uint8_t *buf, int buf_size);
|
static int Write(void *opaque, uint8_t *buf, int buf_size);
|
||||||
|
@ -111,6 +112,10 @@ private:
|
||||||
crl::time _lastUpdateDuration = 0;
|
crl::time _lastUpdateDuration = 0;
|
||||||
rpl::event_stream<Update, Error> _updates;
|
rpl::event_stream<Update, Error> _updates;
|
||||||
|
|
||||||
|
crl::time _maxDuration = 0;
|
||||||
|
crl::time _previousPartsDuration = 0;
|
||||||
|
QByteArray _previousContent;
|
||||||
|
|
||||||
std::vector<bool> _circleMask; // Always nice to use vector<bool>! :D
|
std::vector<bool> _circleMask; // Always nice to use vector<bool>! :D
|
||||||
|
|
||||||
base::ConcurrentTimer _timeoutTimer;
|
base::ConcurrentTimer _timeoutTimer;
|
||||||
|
@ -119,6 +124,7 @@ private:
|
||||||
|
|
||||||
RoundVideoRecorder::Private::Private(crl::weak_on_queue<Private> weak)
|
RoundVideoRecorder::Private::Private(crl::weak_on_queue<Private> weak)
|
||||||
: _weak(std::move(weak))
|
: _weak(std::move(weak))
|
||||||
|
, _maxDuration(kMaxDuration)
|
||||||
, _timeoutTimer(_weak, [=] { timeout(); }) {
|
, _timeoutTimer(_weak, [=] { timeout(); }) {
|
||||||
initEncoding();
|
initEncoding();
|
||||||
initCircleMask();
|
initCircleMask();
|
||||||
|
@ -385,15 +391,31 @@ RoundVideoResult RoundVideoRecorder::Private::finish() {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
finishEncoding();
|
finishEncoding();
|
||||||
if (_resultDuration < kMinDuration) {
|
auto result = RoundVideoResult{
|
||||||
|
.content = base::take(_result),
|
||||||
|
.waveform = QByteArray(),
|
||||||
|
.duration = base::take(_resultDuration),
|
||||||
|
};
|
||||||
|
if (result.duration < kMinDuration) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return {
|
_previousPartsDuration += result.duration;
|
||||||
.content = _result,
|
_maxDuration -= result.duration;
|
||||||
.waveform = QByteArray(),
|
return result;
|
||||||
.duration = _resultDuration,
|
}
|
||||||
};
|
|
||||||
};
|
void RoundVideoRecorder::Private::restart(RoundVideoPartial partial) {
|
||||||
|
if (_format) {
|
||||||
|
return;
|
||||||
|
} else if (_maxDuration <= 0) {
|
||||||
|
notifyFinished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_previousContent = std::move(partial.content);
|
||||||
|
_finished = false;
|
||||||
|
initEncoding();
|
||||||
|
_timeoutTimer.callOnce(kInitTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
void RoundVideoRecorder::Private::fail(Error error) {
|
void RoundVideoRecorder::Private::fail(Error error) {
|
||||||
deinitEncoding();
|
deinitEncoding();
|
||||||
|
@ -421,7 +443,17 @@ void RoundVideoRecorder::Private::deinitEncoding() {
|
||||||
|
|
||||||
_videoFirstTimestamp = -1;
|
_videoFirstTimestamp = -1;
|
||||||
_videoPts = 0;
|
_videoPts = 0;
|
||||||
|
_audioTail = QByteArray();
|
||||||
_audioPts = 0;
|
_audioPts = 0;
|
||||||
|
_audioChannels = 0;
|
||||||
|
|
||||||
|
_firstAudioChunkFinished = 0;
|
||||||
|
_firstVideoFrameTime = 0;
|
||||||
|
|
||||||
|
_resultOffset = 0;
|
||||||
|
|
||||||
|
_maxLevelSinceLastUpdate = 0;
|
||||||
|
_lastUpdateDuration = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoundVideoRecorder::Private::push(
|
void RoundVideoRecorder::Private::push(
|
||||||
|
@ -494,7 +526,7 @@ void RoundVideoRecorder::Private::encodeVideoFrame(
|
||||||
cutCircleFromYUV420P(_videoFrame.get());
|
cutCircleFromYUV420P(_videoFrame.get());
|
||||||
|
|
||||||
_videoFrame->pts = mcstimestamp - _videoFirstTimestamp;
|
_videoFrame->pts = mcstimestamp - _videoFirstTimestamp;
|
||||||
if (_videoFrame->pts >= kMaxDuration * int64(1000)) {
|
if (_videoFrame->pts >= _maxDuration * int64(1000)) {
|
||||||
notifyFinished();
|
notifyFinished();
|
||||||
return;
|
return;
|
||||||
} else if (!writeFrame(_videoFrame, _videoCodec, _videoStream)) {
|
} else if (!writeFrame(_videoFrame, _videoCodec, _videoStream)) {
|
||||||
|
@ -611,7 +643,7 @@ void RoundVideoRecorder::Private::encodeAudioFrame(
|
||||||
|
|
||||||
_audioFrame->pts = _audioPts;
|
_audioFrame->pts = _audioPts;
|
||||||
_audioPts += _audioFrame->nb_samples;
|
_audioPts += _audioFrame->nb_samples;
|
||||||
if (_audioPts >= kMaxDuration * int64(kAudioFrequency) / 1000) {
|
if (_audioPts >= _maxDuration * int64(kAudioFrequency) / 1000) {
|
||||||
notifyFinished();
|
notifyFinished();
|
||||||
return;
|
return;
|
||||||
} else if (!writeFrame(_audioFrame, _audioCodec, _audioStream)) {
|
} else if (!writeFrame(_audioFrame, _audioCodec, _audioStream)) {
|
||||||
|
@ -632,7 +664,7 @@ void RoundVideoRecorder::Private::encodeAudioFrame(
|
||||||
void RoundVideoRecorder::Private::notifyFinished() {
|
void RoundVideoRecorder::Private::notifyFinished() {
|
||||||
_finished = true;
|
_finished = true;
|
||||||
_updates.fire({
|
_updates.fire({
|
||||||
.samples = int(_resultDuration * 48),
|
.samples = int((_previousPartsDuration + _resultDuration) * 48),
|
||||||
.level = base::take(_maxLevelSinceLastUpdate),
|
.level = base::take(_maxLevelSinceLastUpdate),
|
||||||
.finished = true,
|
.finished = true,
|
||||||
});
|
});
|
||||||
|
@ -709,7 +741,7 @@ void RoundVideoRecorder::Private::updateResultDuration(
|
||||||
if (initial || (_lastUpdateDuration + kUpdateEach < _resultDuration)) {
|
if (initial || (_lastUpdateDuration + kUpdateEach < _resultDuration)) {
|
||||||
_lastUpdateDuration = _resultDuration;
|
_lastUpdateDuration = _resultDuration;
|
||||||
_updates.fire({
|
_updates.fire({
|
||||||
.samples = int(_resultDuration * 48),
|
.samples = int((_previousPartsDuration + _resultDuration) * 48),
|
||||||
.level = base::take(_maxLevelSinceLastUpdate),
|
.level = base::take(_maxLevelSinceLastUpdate),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -744,13 +776,7 @@ auto RoundVideoRecorder::updated() -> rpl::producer<Update, Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoundVideoRecorder::hide(Fn<void(RoundVideoResult)> done) {
|
void RoundVideoRecorder::hide(Fn<void(RoundVideoResult)> done) {
|
||||||
if (done) {
|
pause(std::move(done));
|
||||||
_private.with([done = std::move(done)](Private &that) {
|
|
||||||
done(that.finish());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setPaused(true);
|
|
||||||
|
|
||||||
_preview->hide();
|
_preview->hide();
|
||||||
if (const auto onstack = _descriptor.hidden) {
|
if (const auto onstack = _descriptor.hidden) {
|
||||||
|
@ -934,16 +960,29 @@ void RoundVideoRecorder::fade(bool visible) {
|
||||||
crl::time(200));
|
crl::time(200));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoundVideoRecorder::setPaused(bool paused) {
|
void RoundVideoRecorder::pause(Fn<void(RoundVideoResult)> done) {
|
||||||
if (_paused == paused) {
|
if (_paused) {
|
||||||
return;
|
return;
|
||||||
|
} else if (done) {
|
||||||
|
_private.with([done = std::move(done)](Private &that) {
|
||||||
|
done(that.finish());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
_paused = paused;
|
_paused = true;
|
||||||
_descriptor.track->setState(paused
|
_descriptor.track->setState(Webrtc::VideoState::Inactive);
|
||||||
? Webrtc::VideoState::Inactive
|
|
||||||
: Webrtc::VideoState::Active);
|
|
||||||
_preview->update();
|
_preview->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RoundVideoRecorder::resume(RoundVideoPartial partial) {
|
||||||
|
if (!_paused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_private.with([partial = std::move(partial)](Private &that) mutable {
|
||||||
|
that.restart(std::move(partial));
|
||||||
|
});
|
||||||
|
_paused = false;
|
||||||
|
_descriptor.track->setState(Webrtc::VideoState::Active);
|
||||||
|
_preview->update();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
|
@ -44,6 +44,12 @@ struct RoundVideoResult {
|
||||||
crl::time duration = 0;
|
crl::time duration = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct RoundVideoPartial {
|
||||||
|
QByteArray content;
|
||||||
|
crl::time from = 0;
|
||||||
|
crl::time till = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class RoundVideoRecorder final : public base::has_weak_ptr {
|
class RoundVideoRecorder final : public base::has_weak_ptr {
|
||||||
public:
|
public:
|
||||||
explicit RoundVideoRecorder(RoundVideoRecorderDescriptor &&descriptor);
|
explicit RoundVideoRecorder(RoundVideoRecorderDescriptor &&descriptor);
|
||||||
|
@ -51,7 +57,8 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] Fn<void(Media::Capture::Chunk)> audioChunkProcessor();
|
[[nodiscard]] Fn<void(Media::Capture::Chunk)> audioChunkProcessor();
|
||||||
|
|
||||||
void setPaused(bool paused);
|
void pause(Fn<void(RoundVideoResult)> done = nullptr);
|
||||||
|
void resume(RoundVideoPartial partial);
|
||||||
void hide(Fn<void(RoundVideoResult)> done = nullptr);
|
void hide(Fn<void(RoundVideoResult)> done = nullptr);
|
||||||
|
|
||||||
using Update = Media::Capture::Update;
|
using Update = Media::Capture::Update;
|
||||||
|
|
Loading…
Add table
Reference in a new issue