mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Show mini-thumbnails when pausing recording.
This commit is contained in:
parent
c8d4818d22
commit
9514b6eecd
4 changed files with 178 additions and 42 deletions
|
@ -203,6 +203,44 @@ void PaintWaveform(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FillWithMinithumbs(
|
||||||
|
QPainter &p,
|
||||||
|
not_null<const Ui::RoundVideoResult*> data,
|
||||||
|
QRect rect,
|
||||||
|
float64 progress) {
|
||||||
|
if (!data->minithumbsCount || !data->minithumbSize || rect.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto size = rect.height();
|
||||||
|
const auto single = data->minithumbSize;
|
||||||
|
const auto perrow = data->minithumbs.width() / single;
|
||||||
|
const auto thumbs = (rect.width() + size - 1) / size;
|
||||||
|
if (!thumbs || !perrow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (auto i = 0; i != thumbs - 1; ++i) {
|
||||||
|
const auto index = (i * data->minithumbsCount) / thumbs;
|
||||||
|
p.drawImage(
|
||||||
|
QRect(rect.x() + i * size, rect.y(), size, size),
|
||||||
|
data->minithumbs,
|
||||||
|
QRect(
|
||||||
|
(index % perrow) * single,
|
||||||
|
(index / perrow) * single,
|
||||||
|
single,
|
||||||
|
single));
|
||||||
|
}
|
||||||
|
const auto last = rect.width() - (thumbs - 1) * size;
|
||||||
|
const auto index = ((thumbs - 1) * data->minithumbsCount) / thumbs;
|
||||||
|
p.drawImage(
|
||||||
|
QRect(rect.x() + (thumbs - 1) * size, rect.y(), last, size),
|
||||||
|
data->minithumbs,
|
||||||
|
QRect(
|
||||||
|
(index % perrow) * single,
|
||||||
|
(index / perrow) * single,
|
||||||
|
(last * single) / size,
|
||||||
|
single));
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] QRect DrawLockCircle(
|
[[nodiscard]] QRect DrawLockCircle(
|
||||||
QPainter &p,
|
QPainter &p,
|
||||||
const QRect &widgetRect,
|
const QRect &widgetRect,
|
||||||
|
@ -428,7 +466,7 @@ public:
|
||||||
not_null<Ui::RpWidget*> parent,
|
not_null<Ui::RpWidget*> parent,
|
||||||
const style::RecordBar &st,
|
const style::RecordBar &st,
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
::Media::Capture::Result *data,
|
not_null<Ui::RoundVideoResult*> data,
|
||||||
const style::font &font);
|
const style::font &font);
|
||||||
|
|
||||||
void requestPaintProgress(float64 progress);
|
void requestPaintProgress(float64 progress);
|
||||||
|
@ -456,7 +494,7 @@ private:
|
||||||
const not_null<DocumentData*> _document;
|
const not_null<DocumentData*> _document;
|
||||||
const std::unique_ptr<VoiceData> _voiceData;
|
const std::unique_ptr<VoiceData> _voiceData;
|
||||||
const std::shared_ptr<Data::DocumentMedia> _mediaView;
|
const std::shared_ptr<Data::DocumentMedia> _mediaView;
|
||||||
const not_null<::Media::Capture::Result*> _data;
|
const not_null<Ui::RoundVideoResult*> _data;
|
||||||
const base::unique_qptr<Ui::IconButton> _delete;
|
const base::unique_qptr<Ui::IconButton> _delete;
|
||||||
const style::font &_durationFont;
|
const style::font &_durationFont;
|
||||||
const QString _duration;
|
const QString _duration;
|
||||||
|
@ -486,7 +524,7 @@ ListenWrap::ListenWrap(
|
||||||
not_null<Ui::RpWidget*> parent,
|
not_null<Ui::RpWidget*> parent,
|
||||||
const style::RecordBar &st,
|
const style::RecordBar &st,
|
||||||
not_null<Main::Session*> session,
|
not_null<Main::Session*> session,
|
||||||
::Media::Capture::Result *data,
|
not_null<Ui::RoundVideoResult*> data,
|
||||||
const style::font &font)
|
const style::font &font)
|
||||||
: _parent(parent)
|
: _parent(parent)
|
||||||
, _st(st)
|
, _st(st)
|
||||||
|
@ -604,20 +642,27 @@ void ListenWrap::init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Waveform paint.
|
// Waveform paint.
|
||||||
{
|
const auto waveformRect = (progress == 1.)
|
||||||
const auto rect = (progress == 1.)
|
? _waveformFgRect
|
||||||
? _waveformFgRect
|
: computeWaveformRect(bgCenterRect);
|
||||||
: computeWaveformRect(bgCenterRect);
|
if (!waveformRect.isEmpty()) {
|
||||||
if (rect.width() > 0) {
|
const auto playProgress = _playProgress.current();
|
||||||
p.translate(rect.topLeft());
|
if (_data->minithumbs.isNull()) {
|
||||||
|
p.translate(waveformRect.topLeft());
|
||||||
PaintWaveform(
|
PaintWaveform(
|
||||||
p,
|
p,
|
||||||
_voiceData.get(),
|
_voiceData.get(),
|
||||||
rect.width(),
|
waveformRect.width(),
|
||||||
_activeWaveformBar,
|
_activeWaveformBar,
|
||||||
_inactiveWaveformBar,
|
_inactiveWaveformBar,
|
||||||
_playProgress.current());
|
playProgress);
|
||||||
p.resetTransform();
|
p.resetTransform();
|
||||||
|
} else {
|
||||||
|
FillWithMinithumbs(
|
||||||
|
p,
|
||||||
|
_data,
|
||||||
|
waveformRect,
|
||||||
|
playProgress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -631,9 +676,11 @@ void ListenWrap::initPlayButton() {
|
||||||
using namespace ::Media::Player;
|
using namespace ::Media::Player;
|
||||||
using State = TrackState;
|
using State = TrackState;
|
||||||
|
|
||||||
_mediaView->setBytes(_data->bytes);
|
_mediaView->setBytes(_data->content);
|
||||||
_document->size = _data->bytes.size();
|
_document->size = _data->content.size();
|
||||||
_document->type = _data->video ? RoundVideoDocument : VoiceDocument;
|
_document->type = _data->minithumbs.isNull()
|
||||||
|
? VoiceDocument
|
||||||
|
: RoundVideoDocument;
|
||||||
|
|
||||||
const auto &play = _playPauseSt.playOuter;
|
const auto &play = _playPauseSt.playOuter;
|
||||||
const auto &width = _waveformBgFinalCenterRect.height();
|
const auto &width = _waveformBgFinalCenterRect.height();
|
||||||
|
@ -1688,10 +1735,7 @@ void VoiceRecordBar::startRecording() {
|
||||||
instance()->pause(false, nullptr);
|
instance()->pause(false, nullptr);
|
||||||
if (_videoRecorder) {
|
if (_videoRecorder) {
|
||||||
_videoRecorder->resume({
|
_videoRecorder->resume({
|
||||||
.video = {
|
.video = std::move(_data),
|
||||||
.content = _data.bytes,
|
|
||||||
.duration = _data.duration,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1836,12 +1880,7 @@ void VoiceRecordBar::stopRecording(StopType type, bool ttlBeforeHide) {
|
||||||
window()->activateWindow();
|
window()->activateWindow();
|
||||||
|
|
||||||
_paused = true;
|
_paused = true;
|
||||||
_data = ::Media::Capture::Result{
|
_data = std::move(data);
|
||||||
.bytes = std::move(data.content),
|
|
||||||
//.waveform = std::move(data.waveform),
|
|
||||||
.duration = data.duration,
|
|
||||||
.video = true,
|
|
||||||
};
|
|
||||||
_listen = std::make_unique<ListenWrap>(
|
_listen = std::make_unique<ListenWrap>(
|
||||||
this,
|
this,
|
||||||
_st,
|
_st,
|
||||||
|
@ -1861,7 +1900,11 @@ void VoiceRecordBar::stopRecording(StopType type, bool ttlBeforeHide) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_paused = true;
|
_paused = true;
|
||||||
_data = std::move(data);
|
_data = Ui::RoundVideoResult{
|
||||||
|
.content = std::move(data.bytes),
|
||||||
|
.waveform = std::move(data.waveform),
|
||||||
|
.duration = data.duration,
|
||||||
|
};
|
||||||
|
|
||||||
window()->raise();
|
window()->raise();
|
||||||
window()->activateWindow();
|
window()->activateWindow();
|
||||||
|
@ -1906,7 +1949,11 @@ void VoiceRecordBar::stopRecording(StopType type, bool ttlBeforeHide) {
|
||||||
stop(false);
|
stop(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_data = std::move(data);
|
_data = Ui::RoundVideoResult{
|
||||||
|
.content = std::move(data.bytes),
|
||||||
|
.waveform = std::move(data.waveform),
|
||||||
|
.duration = data.duration,
|
||||||
|
};
|
||||||
|
|
||||||
window()->raise();
|
window()->raise();
|
||||||
window()->activateWindow();
|
window()->activateWindow();
|
||||||
|
@ -1916,7 +1963,7 @@ void VoiceRecordBar::stopRecording(StopType type, bool ttlBeforeHide) {
|
||||||
: 0),
|
: 0),
|
||||||
};
|
};
|
||||||
_sendVoiceRequests.fire({
|
_sendVoiceRequests.fire({
|
||||||
_data.bytes,
|
_data.content,
|
||||||
_data.waveform,
|
_data.waveform,
|
||||||
_data.duration,
|
_data.duration,
|
||||||
options,
|
options,
|
||||||
|
@ -1983,7 +2030,7 @@ void VoiceRecordBar::requestToSendWithOptions(Api::SendOptions options) {
|
||||||
options.ttlSeconds = std::numeric_limits<int>::max();
|
options.ttlSeconds = std::numeric_limits<int>::max();
|
||||||
}
|
}
|
||||||
_sendVoiceRequests.fire({
|
_sendVoiceRequests.fire({
|
||||||
_data.bytes,
|
_data.content,
|
||||||
_data.waveform,
|
_data.waveform,
|
||||||
_data.duration,
|
_data.duration,
|
||||||
options,
|
options,
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/timer.h"
|
#include "base/timer.h"
|
||||||
#include "history/view/controls/compose_controls_common.h"
|
#include "history/view/controls/compose_controls_common.h"
|
||||||
#include "media/audio/media_audio_capture_common.h"
|
#include "media/audio/media_audio_capture_common.h"
|
||||||
|
#include "ui/controls/round_video_recorder.h"
|
||||||
#include "ui/effects/animations.h"
|
#include "ui/effects/animations.h"
|
||||||
#include "ui/round_rect.h"
|
#include "ui/round_rect.h"
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
|
@ -170,7 +171,7 @@ private:
|
||||||
std::unique_ptr<Ui::AbstractButton> _ttlButton;
|
std::unique_ptr<Ui::AbstractButton> _ttlButton;
|
||||||
std::unique_ptr<ListenWrap> _listen;
|
std::unique_ptr<ListenWrap> _listen;
|
||||||
|
|
||||||
::Media::Capture::Result _data;
|
Ui::RoundVideoResult _data;
|
||||||
rpl::variable<bool> _paused;
|
rpl::variable<bool> _paused;
|
||||||
|
|
||||||
base::Timer _startTimer;
|
base::Timer _startTimer;
|
||||||
|
|
|
@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#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"
|
||||||
|
#include "styles/style_chat.h"
|
||||||
#include "styles/style_chat_helpers.h"
|
#include "styles/style_chat_helpers.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
@ -30,6 +31,8 @@ constexpr auto kMinDuration = crl::time(200);
|
||||||
constexpr auto kMaxDuration = 60 * crl::time(1000);
|
constexpr auto kMaxDuration = 60 * crl::time(1000);
|
||||||
constexpr auto kInitTimeout = 5 * crl::time(1000);
|
constexpr auto kInitTimeout = 5 * crl::time(1000);
|
||||||
constexpr auto kBlurredSize = 64;
|
constexpr auto kBlurredSize = 64;
|
||||||
|
constexpr auto kMinithumbsPerSecond = 5;
|
||||||
|
constexpr auto kMinithumbsInRow = 16;
|
||||||
|
|
||||||
using namespace FFmpeg;
|
using namespace FFmpeg;
|
||||||
|
|
||||||
|
@ -66,11 +69,19 @@ struct ReadBytesWrap {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] int MinithumbSize() {
|
||||||
|
const auto full = st::historySendSize.height();
|
||||||
|
const auto margin = st::historyRecordWaveformBgMargins;
|
||||||
|
const auto outer = full - margin.top() - margin.bottom();
|
||||||
|
const auto inner = outer - 2 * st::msgWaveformMin;
|
||||||
|
return inner * style::DevicePixelRatio();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class RoundVideoRecorder::Private final {
|
class RoundVideoRecorder::Private final {
|
||||||
public:
|
public:
|
||||||
Private(crl::weak_on_queue<Private> weak);
|
Private(crl::weak_on_queue<Private> weak, int minithumbSize);
|
||||||
~Private();
|
~Private();
|
||||||
|
|
||||||
void push(int64 mcstimestamp, const QImage &frame);
|
void push(int64 mcstimestamp, const QImage &frame);
|
||||||
|
@ -103,6 +114,13 @@ private:
|
||||||
int64_t seek(int64_t offset, int whence);
|
int64_t seek(int64_t offset, int whence);
|
||||||
|
|
||||||
void initEncoding();
|
void initEncoding();
|
||||||
|
void initCircleMask();
|
||||||
|
void initMinithumbsCanvas();
|
||||||
|
void maybeSaveMinithumb(
|
||||||
|
not_null<AVFrame*> frame,
|
||||||
|
const QImage &original,
|
||||||
|
QRect crop);
|
||||||
|
|
||||||
bool initVideo();
|
bool initVideo();
|
||||||
bool initAudio();
|
bool initAudio();
|
||||||
void notifyFinished();
|
void notifyFinished();
|
||||||
|
@ -122,7 +140,6 @@ private:
|
||||||
void updateResultDuration(int64 pts, AVRational timeBase);
|
void updateResultDuration(int64 pts, AVRational timeBase);
|
||||||
|
|
||||||
void cutCircleFromYUV420P(not_null<AVFrame*> frame);
|
void cutCircleFromYUV420P(not_null<AVFrame*> frame);
|
||||||
void initCircleMask();
|
|
||||||
|
|
||||||
[[nodiscard]] RoundVideoResult appendToPrevious(RoundVideoResult video);
|
[[nodiscard]] RoundVideoResult appendToPrevious(RoundVideoResult video);
|
||||||
[[nodiscard]] static FormatPointer OpenInputContext(
|
[[nodiscard]] static FormatPointer OpenInputContext(
|
||||||
|
@ -169,6 +186,11 @@ private:
|
||||||
crl::time _lastUpdateDuration = 0;
|
crl::time _lastUpdateDuration = 0;
|
||||||
rpl::event_stream<Update, Error> _updates;
|
rpl::event_stream<Update, Error> _updates;
|
||||||
|
|
||||||
|
crl::time _minithumbNextTimestamp = 0;
|
||||||
|
const int _minithumbSize = 0;
|
||||||
|
int _minithumbsCount = 0;
|
||||||
|
QImage _minithumbs;
|
||||||
|
|
||||||
crl::time _maxDuration = 0;
|
crl::time _maxDuration = 0;
|
||||||
RoundVideoResult _previous;
|
RoundVideoResult _previous;
|
||||||
|
|
||||||
|
@ -185,12 +207,16 @@ RoundVideoRecorder::Private::CopyContext::CopyContext() {
|
||||||
ranges::fill(lastDts, std::numeric_limits<int64>::min());
|
ranges::fill(lastDts, std::numeric_limits<int64>::min());
|
||||||
}
|
}
|
||||||
|
|
||||||
RoundVideoRecorder::Private::Private(crl::weak_on_queue<Private> weak)
|
RoundVideoRecorder::Private::Private(
|
||||||
|
crl::weak_on_queue<Private> weak,
|
||||||
|
int minithumbSize)
|
||||||
: _weak(std::move(weak))
|
: _weak(std::move(weak))
|
||||||
|
, _minithumbSize(minithumbSize)
|
||||||
, _maxDuration(kMaxDuration)
|
, _maxDuration(kMaxDuration)
|
||||||
, _timeoutTimer(_weak, [=] { timeout(); }) {
|
, _timeoutTimer(_weak, [=] { timeout(); }) {
|
||||||
initEncoding();
|
initEncoding();
|
||||||
initCircleMask();
|
initCircleMask();
|
||||||
|
initMinithumbsCanvas();
|
||||||
|
|
||||||
_timeoutTimer.callOnce(kInitTimeout);
|
_timeoutTimer.callOnce(kInitTimeout);
|
||||||
}
|
}
|
||||||
|
@ -452,8 +478,11 @@ RoundVideoResult RoundVideoRecorder::Private::finish() {
|
||||||
finishEncoding();
|
finishEncoding();
|
||||||
auto result = appendToPrevious({
|
auto result = appendToPrevious({
|
||||||
.content = base::take(_result),
|
.content = base::take(_result),
|
||||||
.waveform = QByteArray(),
|
|
||||||
.duration = base::take(_resultDuration),
|
.duration = base::take(_resultDuration),
|
||||||
|
//.waveform = {},
|
||||||
|
.minithumbs = base::take(_minithumbs),
|
||||||
|
.minithumbsCount = base::take(_minithumbsCount),
|
||||||
|
.minithumbSize = _minithumbSize,
|
||||||
});
|
});
|
||||||
if (result.duration < kMinDuration) {
|
if (result.duration < kMinDuration) {
|
||||||
return {};
|
return {};
|
||||||
|
@ -523,11 +552,9 @@ RoundVideoResult RoundVideoRecorder::Private::appendToPrevious(
|
||||||
fail(Error::Encoding);
|
fail(Error::Encoding);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return RoundVideoResult{
|
video.content = base::take(_result);
|
||||||
.content = base::take(_result),
|
video.duration += _previous.duration;
|
||||||
.waveform = QByteArray(),
|
return video;
|
||||||
.duration = _previous.duration + video.duration,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FormatPointer RoundVideoRecorder::Private::OpenInputContext(
|
FormatPointer RoundVideoRecorder::Private::OpenInputContext(
|
||||||
|
@ -605,7 +632,11 @@ void RoundVideoRecorder::Private::restart(RoundVideoPartial partial) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_previous = std::move(partial.video);
|
_previous = std::move(partial.video);
|
||||||
|
_minithumbs = std::move(_previous.minithumbs);
|
||||||
|
_minithumbsCount = _previous.minithumbsCount;
|
||||||
|
Assert(_minithumbSize == _previous.minithumbSize);
|
||||||
_maxDuration = kMaxDuration - _previous.duration;
|
_maxDuration = kMaxDuration - _previous.duration;
|
||||||
|
_minithumbNextTimestamp = 0;
|
||||||
_finished = false;
|
_finished = false;
|
||||||
initEncoding();
|
initEncoding();
|
||||||
_timeoutTimer.callOnce(kInitTimeout);
|
_timeoutTimer.callOnce(kInitTimeout);
|
||||||
|
@ -720,6 +751,7 @@ void RoundVideoRecorder::Private::encodeVideoFrame(
|
||||||
cutCircleFromYUV420P(_videoFrame.get());
|
cutCircleFromYUV420P(_videoFrame.get());
|
||||||
|
|
||||||
_videoFrame->pts = mcstimestamp - _videoFirstTimestamp;
|
_videoFrame->pts = mcstimestamp - _videoFirstTimestamp;
|
||||||
|
maybeSaveMinithumb(_videoFrame.get(), frame, crop);
|
||||||
if (_videoFrame->pts >= _maxDuration * int64(1000)) {
|
if (_videoFrame->pts >= _maxDuration * int64(1000)) {
|
||||||
notifyFinished();
|
notifyFinished();
|
||||||
return;
|
return;
|
||||||
|
@ -728,6 +760,49 @@ void RoundVideoRecorder::Private::encodeVideoFrame(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RoundVideoRecorder::Private::maybeSaveMinithumb(
|
||||||
|
not_null<AVFrame*> frame,
|
||||||
|
const QImage &original,
|
||||||
|
QRect crop) {
|
||||||
|
if (frame->pts < _minithumbNextTimestamp * 1000) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_minithumbNextTimestamp += crl::time(1000) / kMinithumbsPerSecond;
|
||||||
|
const auto perline = original.bytesPerLine();
|
||||||
|
const auto perpixel = original.depth() / 8;
|
||||||
|
const auto cropped = QImage(
|
||||||
|
original.constBits() + (crop.y() * perline) + (crop.x() * perpixel),
|
||||||
|
crop.width(),
|
||||||
|
crop.height(),
|
||||||
|
perline,
|
||||||
|
original.format()
|
||||||
|
).scaled(
|
||||||
|
_minithumbSize,
|
||||||
|
_minithumbSize,
|
||||||
|
Qt::IgnoreAspectRatio,
|
||||||
|
Qt::SmoothTransformation);
|
||||||
|
|
||||||
|
const auto row = _minithumbsCount / kMinithumbsInRow;
|
||||||
|
const auto column = _minithumbsCount % kMinithumbsInRow;
|
||||||
|
const auto fromPerLine = cropped.bytesPerLine();
|
||||||
|
auto from = cropped.constBits();
|
||||||
|
const auto toPerLine = _minithumbs.bytesPerLine();
|
||||||
|
const auto toPerPixel = _minithumbs.depth() / 8;
|
||||||
|
auto to = _minithumbs.bits()
|
||||||
|
+ (row * _minithumbSize * toPerLine)
|
||||||
|
+ (column * _minithumbSize * toPerPixel);
|
||||||
|
|
||||||
|
Assert(toPerPixel == perpixel);
|
||||||
|
for (auto y = 0; y != _minithumbSize; ++y) {
|
||||||
|
Assert(to + toPerLine - _minithumbs.constBits()
|
||||||
|
<= _minithumbs.bytesPerLine() * _minithumbs.height());
|
||||||
|
memcpy(to, from, _minithumbSize * toPerPixel);
|
||||||
|
from += fromPerLine;
|
||||||
|
to += toPerLine;
|
||||||
|
}
|
||||||
|
++_minithumbsCount;
|
||||||
|
}
|
||||||
|
|
||||||
void RoundVideoRecorder::Private::initCircleMask() {
|
void RoundVideoRecorder::Private::initCircleMask() {
|
||||||
const auto width = kSide;
|
const auto width = kSide;
|
||||||
const auto height = kSide;
|
const auto height = kSide;
|
||||||
|
@ -747,6 +822,16 @@ void RoundVideoRecorder::Private::initCircleMask() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RoundVideoRecorder::Private::initMinithumbsCanvas() {
|
||||||
|
const auto width = kMinithumbsInRow * _minithumbSize;
|
||||||
|
const auto seconds = (kMaxDuration + 999) / 1000;
|
||||||
|
const auto persecond = kMinithumbsPerSecond;
|
||||||
|
const auto frames = (seconds + persecond - 1) * persecond;
|
||||||
|
const auto rows = (frames + kMinithumbsInRow - 1) / kMinithumbsInRow;
|
||||||
|
const auto height = rows * _minithumbSize;
|
||||||
|
_minithumbs = QImage(width, height, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
}
|
||||||
|
|
||||||
void RoundVideoRecorder::Private::cutCircleFromYUV420P(
|
void RoundVideoRecorder::Private::cutCircleFromYUV420P(
|
||||||
not_null<AVFrame*> frame) {
|
not_null<AVFrame*> frame) {
|
||||||
const auto width = frame->width;
|
const auto width = frame->width;
|
||||||
|
@ -888,9 +973,9 @@ bool RoundVideoRecorder::Private::writeFrame(
|
||||||
while (true) {
|
while (true) {
|
||||||
error = AvErrorWrap(avcodec_receive_packet(codec.get(), pkt));
|
error = AvErrorWrap(avcodec_receive_packet(codec.get(), pkt));
|
||||||
if (error.code() == AVERROR(EAGAIN)) {
|
if (error.code() == AVERROR(EAGAIN)) {
|
||||||
return true; // Need more input
|
return true; // Need more input
|
||||||
} else if (error.code() == AVERROR_EOF) {
|
} else if (error.code() == AVERROR_EOF) {
|
||||||
return true; // Encoding finished
|
return true; // Encoding finished
|
||||||
} else if (error) {
|
} else if (error) {
|
||||||
LogError("avcodec_receive_packet", error);
|
LogError("avcodec_receive_packet", error);
|
||||||
fail(Error::Encoding);
|
fail(Error::Encoding);
|
||||||
|
@ -945,7 +1030,7 @@ RoundVideoRecorder::RoundVideoRecorder(
|
||||||
RoundVideoRecorderDescriptor &&descriptor)
|
RoundVideoRecorderDescriptor &&descriptor)
|
||||||
: _descriptor(std::move(descriptor))
|
: _descriptor(std::move(descriptor))
|
||||||
, _preview(std::make_unique<RpWidget>(_descriptor.container))
|
, _preview(std::make_unique<RpWidget>(_descriptor.container))
|
||||||
, _private() {
|
, _private(MinithumbSize()) {
|
||||||
setup();
|
setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,8 +40,11 @@ struct RoundVideoRecorderDescriptor {
|
||||||
|
|
||||||
struct RoundVideoResult {
|
struct RoundVideoResult {
|
||||||
QByteArray content;
|
QByteArray content;
|
||||||
QByteArray waveform;
|
QVector<signed char> waveform;
|
||||||
crl::time duration = 0;
|
crl::time duration = 0;
|
||||||
|
QImage minithumbs;
|
||||||
|
int minithumbsCount = 0;
|
||||||
|
int minithumbSize = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RoundVideoPartial {
|
struct RoundVideoPartial {
|
||||||
|
|
Loading…
Add table
Reference in a new issue