Save last camera image as a placeholder.

This commit is contained in:
John Preston 2024-10-23 22:34:12 +04:00
parent 9ce6636c6a
commit ccc6c6daa5
5 changed files with 113 additions and 22 deletions

View file

@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/player/media_player_instance.h"
#include "media/streaming/media_streaming_instance.h"
#include "media/streaming/media_streaming_round_preview.h"
#include "storage/storage_account.h"
#include "ui/controls/round_video_recorder.h"
#include "ui/controls/send_button.h"
#include "ui/effects/animation_value.h"
@ -2362,7 +2363,13 @@ bool VoiceRecordBar::createVideoRecorder() {
.hidden = hidden,
.capturer = std::move(capturer),
.track = std::move(track),
.placeholder = _show->session().local().readRoundPlaceholder(),
});
_videoRecorder->placeholderUpdates(
) | rpl::start_with_next([=](QImage &&placeholder) {
_show->session().local().writeRoundPlaceholder(placeholder);
}, _videoCapturerLifetime);
return true;
}

View file

@ -94,6 +94,7 @@ enum { // Local Storage Keys
lskCustomEmojiKeys = 0x17, // no data
lskSearchSuggestions = 0x18, // no data
lskWebviewTokens = 0x19, // data: QByteArray bots, QByteArray other
lskRoundPlaceholder = 0x1a, // no data
};
auto EmptyMessageDraftSources()
@ -220,6 +221,7 @@ base::flat_set<QString> Account::collectGoodNames() const {
_featuredCustomEmojiKey,
_archivedCustomEmojiKey,
_searchSuggestionsKey,
_roundPlaceholderKey,
};
auto result = base::flat_set<QString>{
"map0",
@ -306,6 +308,7 @@ Account::ReadMapResult Account::readMapWith(
quint64 legacyBackgroundKeyDay = 0, legacyBackgroundKeyNight = 0;
quint64 userSettingsKey = 0, recentHashtagsAndBotsKey = 0, exportSettingsKey = 0;
quint64 searchSuggestionsKey = 0;
quint64 roundPlaceholderKey = 0;
QByteArray webviewStorageTokenBots, webviewStorageTokenOther;
while (!map.stream.atEnd()) {
quint32 keyType;
@ -415,6 +418,9 @@ Account::ReadMapResult Account::readMapWith(
case lskSearchSuggestions: {
map.stream >> searchSuggestionsKey;
} break;
case lskRoundPlaceholder: {
map.stream >> roundPlaceholderKey;
} break;
case lskWebviewTokens: {
map.stream
>> webviewStorageTokenBots
@ -456,6 +462,7 @@ Account::ReadMapResult Account::readMapWith(
_recentHashtagsAndBotsKey = recentHashtagsAndBotsKey;
_exportSettingsKey = exportSettingsKey;
_searchSuggestionsKey = searchSuggestionsKey;
_roundPlaceholderKey = roundPlaceholderKey;
_oldMapVersion = mapData.version;
_webviewStorageIdBots.token = webviewStorageTokenBots;
_webviewStorageIdOther.token = webviewStorageTokenOther;
@ -570,6 +577,7 @@ void Account::writeMap() {
+ Serialize::bytearraySize(_webviewStorageIdBots.token)
+ Serialize::bytearraySize(_webviewStorageIdOther.token);
}
if (_roundPlaceholderKey) mapSize += sizeof(quint32) + sizeof(quint64);
EncryptedDescriptor mapData(mapSize);
if (!self.isEmpty()) {
@ -640,6 +648,10 @@ void Account::writeMap() {
<< _webviewStorageIdBots.token
<< _webviewStorageIdOther.token;
}
if (_roundPlaceholderKey) {
mapData.stream << quint32(lskRoundPlaceholder);
mapData.stream << quint64(_roundPlaceholderKey);
}
map.writeEncrypted(mapData, _localKey);
_mapChanged = false;
@ -669,6 +681,7 @@ void Account::reset() {
_legacyBackgroundKeyDay = _legacyBackgroundKeyNight = 0;
_settingsKey = _recentHashtagsAndBotsKey = _exportSettingsKey = 0;
_searchSuggestionsKey = 0;
_roundPlaceholderKey = 0;
_oldMapVersion = 0;
_fileLocations.clear();
_fileLocationPairs.clear();
@ -3178,6 +3191,52 @@ Webview::StorageId Account::resolveStorageIdOther() {
return _webviewStorageIdOther;
}
QImage Account::readRoundPlaceholder() {
if (!_roundPlaceholder.isNull()) {
return _roundPlaceholder;
} else if (!_roundPlaceholderKey) {
return QImage();
}
FileReadDescriptor placeholder;
if (!ReadEncryptedFile(
placeholder,
_roundPlaceholderKey,
_basePath,
_localKey)) {
ClearKey(_roundPlaceholderKey, _basePath);
_roundPlaceholderKey = 0;
writeMapDelayed();
return QImage();
}
auto bytes = QByteArray();
placeholder.stream >> bytes;
_roundPlaceholder = Images::Read({ .content = bytes }).image;
return _roundPlaceholder;
}
void Account::writeRoundPlaceholder(const QImage &placeholder) {
if (placeholder.isNull()) {
return;
}
_roundPlaceholder = placeholder;
auto bytes = QByteArray();
auto buffer = QBuffer(&bytes);
placeholder.save(&buffer, "JPG", 87);
quint32 size = Serialize::bytearraySize(bytes);
if (!_roundPlaceholderKey) {
_roundPlaceholderKey = GenerateKey(_basePath);
writeMapQueued();
}
EncryptedDescriptor data(size);
data.stream << bytes;
FileWriteDescriptor file(_roundPlaceholderKey, _basePath);
file.writeEncrypted(data, _localKey);
}
bool Account::encrypt(
const void *src,
void *dst,

View file

@ -174,6 +174,9 @@ public:
[[nodiscard]] Webview::StorageId resolveStorageIdBots();
[[nodiscard]] Webview::StorageId resolveStorageIdOther();
[[nodiscard]] QImage readRoundPlaceholder();
void writeRoundPlaceholder(const QImage &placeholder);
[[nodiscard]] bool encrypt(
const void *src,
void *dst,
@ -302,6 +305,7 @@ private:
FileKey _featuredCustomEmojiKey = 0;
FileKey _archivedCustomEmojiKey = 0;
FileKey _searchSuggestionsKey = 0;
FileKey _roundPlaceholderKey = 0;
qint64 _cacheTotalSizeLimit = 0;
qint64 _cacheBigFileTotalSizeLimit = 0;
@ -325,6 +329,8 @@ private:
bool _mapChanged = false;
bool _locationsChanged = false;
QImage _roundPlaceholder;
};
[[nodiscard]] Webview::StorageId TonSiteStorageId();

View file

@ -1052,6 +1052,10 @@ Fn<void(Media::Capture::Chunk)> RoundVideoRecorder::audioChunkProcessor() {
};
}
rpl::producer<QImage> RoundVideoRecorder::placeholderUpdates() const {
return _placeholderUpdates.events();
}
int RoundVideoRecorder::previewSize() const {
return _side;
}
@ -1096,6 +1100,31 @@ void RoundVideoRecorder::progressTo(float64 progress) {
_preview->update();
}
void RoundVideoRecorder::preparePlaceholder(const QImage &placeholder) {
const auto ratio = style::DevicePixelRatio();
const auto full = QSize(_side, _side) * ratio;
if (!placeholder.isNull()) {
_framePlaceholder = Images::Circle(
placeholder.scaled(
full,
Qt::KeepAspectRatio,
Qt::SmoothTransformation));
_framePlaceholder.setDevicePixelRatio(ratio);
} else {
_framePlaceholder = QImage(
full,
QImage::Format_ARGB32_Premultiplied);
_framePlaceholder.fill(Qt::transparent);
_framePlaceholder.setDevicePixelRatio(ratio);
auto p = QPainter(&_framePlaceholder);
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(Qt::black);
p.drawEllipse(0, 0, _side, _side);
}
}
void RoundVideoRecorder::prepareFrame(bool blurred) {
if (_frameOriginal.isNull()) {
return;
@ -1124,18 +1153,14 @@ void RoundVideoRecorder::prepareFrame(bool blurred) {
const auto ratio = style::DevicePixelRatio();
if (blurred) {
static constexpr auto kRadius = 16;
_framePlaceholder = Images::Circle(
Images::BlurLargeImage(
copy.scaled(
QSize(kBlurredSize, kBlurredSize),
Qt::KeepAspectRatio,
Qt::FastTransformation),
kRadius
).scaled(
QSize(_side, _side) * ratio,
auto image = Images::BlurLargeImage(
copy.scaled(
QSize(kBlurredSize, kBlurredSize),
Qt::KeepAspectRatio,
Qt::SmoothTransformation));
_framePlaceholder.setDevicePixelRatio(ratio);
Qt::FastTransformation),
kRadius);
preparePlaceholder(image);
_placeholderUpdates.fire(std::move(image));
} else {
_framePrepared = Images::Circle(copy.scaled(
QSize(_side, _side) * ratio,
@ -1147,17 +1172,7 @@ void RoundVideoRecorder::prepareFrame(bool blurred) {
void RoundVideoRecorder::createImages() {
const auto ratio = style::DevicePixelRatio();
_framePlaceholder = QImage(
QSize(_side, _side) * ratio,
QImage::Format_ARGB32_Premultiplied);
_framePlaceholder.fill(Qt::transparent);
_framePlaceholder.setDevicePixelRatio(ratio);
auto p = QPainter(&_framePlaceholder);
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(Qt::black);
p.drawEllipse(0, 0, _side, _side);
preparePlaceholder(_descriptor.placeholder);
const auto side = _side + 2 * _extent;
_shadow = QImage(

View file

@ -38,6 +38,7 @@ struct RoundVideoRecorderDescriptor {
Fn<void(not_null<RoundVideoRecorder*>)> hidden;
std::shared_ptr<tgcalls::VideoCaptureInterface> capturer;
std::shared_ptr<Webrtc::VideoTrack> track;
QImage placeholder;
};
struct RoundVideoResult {
@ -62,6 +63,7 @@ public:
[[nodiscard]] int previewSize() const;
[[nodiscard]] Fn<void(Media::Capture::Chunk)> audioChunkProcessor();
[[nodiscard]] rpl::producer<QImage> placeholderUpdates() const;
void pause(Fn<void(RoundVideoResult)> done = nullptr);
void resume(RoundVideoPartial partial);
@ -84,6 +86,7 @@ private:
void setup();
void prepareFrame(bool blurred = false);
void preparePlaceholder(const QImage &placeholder);
void createImages();
void progressTo(float64 progress);
void fade(bool visible);
@ -100,6 +103,7 @@ private:
Ui::Animations::Simple _progressAnimation;
Ui::Animations::Simple _fadeAnimation;
Ui::Animations::Simple _fadeContentAnimation;
rpl::event_stream<QImage> _placeholderUpdates;
std::shared_ptr<Ui::DynamicImage> _silentPreview;
std::shared_ptr<Ui::DynamicImage> _soundedPreview;