Decode desktop image previews in bounded background jobs
This commit is contained in:
1 parent
08e9b62e16
commit
c6fdf52582
2 files changed
+51
-1
No files matched your search
@@ -358,6 +358,13 @@ MessageList::MessageList(DbManager* db, AudioRecorder* recorder, QWidget *parent
|
||||
, m_recorder(recorder)
|
||||
{
|
||||
auto *layout = new QVBoxLayout(this);
|
||||
m_imagePreviewTimer.setSingleShot(true);
|
||||
connect(&m_imagePreviewTimer, &QTimer::timeout, this, &MessageList::prepareImagePreviews);
|
||||
connect(m_model, &QAbstractItemModel::rowsInserted, this, [this]() { m_imagePreviewTimer.start(0); });
|
||||
connect(m_model, &QAbstractItemModel::dataChanged, this,
|
||||
[this](const QModelIndex&, const QModelIndex&, const QVector<int>& roles) {
|
||||
if (roles.isEmpty() || roles.contains(MsgVoiceFileRole) || roles.contains(MsgContentTypeRole)) m_imagePreviewTimer.start(0);
|
||||
});
|
||||
if (auto* manager = AvatarManager::instance()) connect(manager, &AvatarManager::changed, this,
|
||||
[this, manager](const QString& channel, quint64 owner) {
|
||||
if (channel != m_currentChannelId && !isDmChannel(m_currentChannelId)) return;
|
||||
@@ -1230,6 +1237,43 @@ void MessageList::onAttachmentDownloaded(const QString& chId, int64_t msgId) {
|
||||
}
|
||||
|
||||
/* Общие превью для каналов/PM; не более двух decoder workers, без запуска из paint. */
|
||||
void MessageList::prepareImagePreviews() {
|
||||
for (int row = 0; row < m_model->rowCount(); ++row) {
|
||||
auto* item = m_model->item(row);
|
||||
if (!item->data(MsgContentTypeRole).toString().startsWith("image/")) continue;
|
||||
const QString path = item->data(MsgVoiceFileRole).toString();
|
||||
if (path.isEmpty() || item->data(MsgImagePreviewRole).isValid() || item->data(MsgImageErrorRole).toBool()) continue;
|
||||
if (const auto* image = m_imagePreviews.object(path)) {
|
||||
item->setData(*image, MsgImagePreviewRole); item->setData(image->isNull(), MsgImageErrorRole); continue;
|
||||
}
|
||||
if (m_imageJobs.contains(path)) continue;
|
||||
m_imageJobs.insert(path); m_thumbnailPool.setMaxThreadCount(2);
|
||||
QPointer<MessageList> self(this);
|
||||
QObject* dispatcher = QCoreApplication::instance();
|
||||
m_thumbnailPool.start([self, dispatcher, path]() {
|
||||
QImageReader reader(path); reader.setAutoTransform(true);
|
||||
const QSize size = reader.size();
|
||||
QImage image;
|
||||
if (size.isValid() && size.width() <= 16384 && size.height() <= 16384) {
|
||||
reader.setScaledSize(size.scaled(600, 600, Qt::KeepAspectRatio)); image = reader.read();
|
||||
}
|
||||
if (image.isNull()) DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "image preview failed path=%s dimensions=%dx%d error=%s",
|
||||
qPrintable(path), size.width(), size.height(), qPrintable(reader.errorString()));
|
||||
else DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "image preview ready path=%s dimensions=%dx%d", qPrintable(path), image.width(), image.height());
|
||||
QMetaObject::invokeMethod(dispatcher, [self, path, image]() {
|
||||
if (!self) return;
|
||||
self->m_imageJobs.remove(path);
|
||||
self->m_imagePreviews.insert(path, new QImage(image), qMax(1, (int)image.sizeInBytes()));
|
||||
for (int r = 0; r < self->m_model->rowCount(); ++r) {
|
||||
auto* current = self->m_model->item(r);
|
||||
if (current->data(MsgVoiceFileRole).toString() != path) continue;
|
||||
current->setData(image, MsgImagePreviewRole); current->setData(image.isNull(), MsgImageErrorRole);
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void MessageList::prepareVideoThumbnail(const QString& path) {
|
||||
if (path.isEmpty() || m_thumbnailJobs.contains(path) || !QFile::exists(path) || QFile::exists(path + ".jpg")) return;
|
||||
m_thumbnailPool.setMaxThreadCount(2);
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <QThreadPool>
|
||||
#include <QJsonObject>
|
||||
#include <QPointer>
|
||||
#include <QCache>
|
||||
#include <QImage>
|
||||
#include "stream_sources.h"
|
||||
|
||||
class ChatView;
|
||||
@@ -138,8 +140,12 @@ private:
|
||||
QList<LiveStreamSource> m_streamSources; // Проверенный полный снимок выбранной группы.
|
||||
RadioPanel* m_radioPanel = nullptr; /* верхняя панель устройств, VAD и PTT */
|
||||
bool m_radioOn = false; // Локальный audio engine успешно включён для текущей группы.
|
||||
QThreadPool m_thumbnailPool; // До двух FFmpeg workers; деструктор pool дожидается jobs.
|
||||
QThreadPool m_thumbnailPool; // До двух media workers; деструктор pool дожидается jobs.
|
||||
QSet<QString> m_thumbnailJobs; // Пути незавершённых jobs, без дублей.
|
||||
QCache<QString, QImage> m_imagePreviews{32 * 1024 * 1024}; // Ограниченный кеш decoded-превью по неизменному пути.
|
||||
QSet<QString> m_imageJobs; // Незавершённые image jobs; GUI изменяет, workers получают копию пути.
|
||||
QTimer m_imagePreviewTimer; // Объединяет изменения модели до одного обхода видимой истории.
|
||||
void prepareImagePreviews(); // Асинхронное декодирование локальных изображений и обновление актуальных строк модели.
|
||||
QHash<QString, QJsonObject> m_feedback; // message_id hex → последний полный снимок реакций/опроса.
|
||||
QPointer<QDialog> m_pollDialog; // Один диалог голосования; уничтожение owner безопасно для exec.
|
||||
QString m_pollDialogMessage, m_pendingPoll; // Открытый message_id и ожидаемый запрос состояния.
|
||||
|
||||
Reference in new issue
Block a user