| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- /******************************************************************************
- Copyright (C) 2025 by Taylor Giampaolo <[email protected]>
- Lain Bailey <[email protected]>
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- ******************************************************************************/
- #include "ThumbnailManager.hpp"
- #include <utility/ScreenshotObj.hpp>
- #include <widgets/OBSBasic.hpp>
- #include "display-helpers.hpp"
- #include <QImageWriter>
- constexpr int MIN_THUMBNAIL_UPDATE_INTERVAL_MS = 100;
- constexpr int MIN_SOURCE_UPDATE_INTERVAL_MS = 5000;
- ThumbnailItem::ThumbnailItem(std::string uuid, OBSSource source) : uuid(uuid), weakSource(OBSGetWeakRef(source)) {}
- void ThumbnailItem::init(std::weak_ptr<ThumbnailItem> weakActiveItem)
- {
- auto thumbnailManager = OBSBasic::Get()->thumbnails();
- if (!thumbnailManager) {
- return;
- }
- auto it = thumbnailManager->cachedThumbnails.find(uuid);
- if (it != thumbnailManager->cachedThumbnails.end()) {
- auto &cachedItem = it->second;
- pixmap = cachedItem.pixmap.value_or(QPixmap());
- cachedItem.pixmap.reset();
- cachedItem.weakActiveItem = std::move(weakActiveItem);
- }
- }
- ThumbnailItem::~ThumbnailItem()
- {
- auto thumbnailManager = OBSBasic::Get()->thumbnails();
- if (!thumbnailManager) {
- return;
- }
- auto &cachedItem = thumbnailManager->cachedThumbnails[uuid];
- cachedItem.pixmap = pixmap;
- cachedItem.weakActiveItem.reset();
- }
- void ThumbnailItem::imageUpdated(QImage image)
- {
- QPixmap newPixmap;
- if (!image.isNull()) {
- newPixmap = QPixmap::fromImage(image);
- }
- pixmap = newPixmap;
- emit updateThumbnail(pixmap);
- }
- void Thumbnail::thumbnailUpdated(QPixmap pixmap)
- {
- emit updateThumbnail(pixmap);
- }
- ThumbnailManager::ThumbnailManager(QObject *parent) : QObject(parent)
- {
- connect(&updateTimer, &QTimer::timeout, this, &ThumbnailManager::updateTick);
- }
- ThumbnailManager::~ThumbnailManager() {}
- std::shared_ptr<Thumbnail> ThumbnailManager::getThumbnail(OBSSource source)
- {
- std::string uuid = obs_source_get_uuid(source);
- for (auto it = thumbnails.begin(); it != thumbnails.end(); ++it) {
- auto item = it->lock();
- if (item && item->uuid == uuid) {
- return std::make_shared<Thumbnail>(item);
- }
- }
- std::shared_ptr<Thumbnail> thumbnail;
- if ((obs_source_get_output_flags(source) & OBS_SOURCE_VIDEO) != 0) {
- auto item = std::make_shared<ThumbnailItem>(uuid, source);
- item->init(std::weak_ptr<ThumbnailItem>(item));
- thumbnail = std::make_shared<Thumbnail>(item);
- connect(item.get(), &ThumbnailItem::updateThumbnail, thumbnail.get(), &Thumbnail::thumbnailUpdated);
- newThumbnails.push_back(std::weak_ptr<ThumbnailItem>(item));
- }
- updateIntervalChanged(thumbnails.size());
- return thumbnail;
- }
- bool ThumbnailManager::updatePixmap(std::shared_ptr<ThumbnailItem> &sharedPointerItem)
- {
- ThumbnailItem *item = sharedPointerItem.get();
- OBSSource source = OBSGetStrongRef(item->weakSource);
- if (!source) {
- return true;
- }
- QPixmap pixmap;
- item->pixmap = pixmap;
- if (source) {
- uint32_t sourceWidth = obs_source_get_width(source);
- uint32_t sourceHeight = obs_source_get_height(source);
- if (sourceWidth == 0 || sourceHeight == 0) {
- return true;
- }
- auto obj = new ScreenshotObj(source);
- obj->setSaveToFile(false);
- obj->setSize(Thumbnail::size);
- connect(obj, &ScreenshotObj::imageReady, item, &ThumbnailItem::imageUpdated);
- }
- return true;
- }
- void ThumbnailManager::updateIntervalChanged(size_t newCount)
- {
- int intervalMS = MIN_THUMBNAIL_UPDATE_INTERVAL_MS;
- if (newThumbnails.size() == 0 && newCount > 0) {
- int count = (int)newCount;
- intervalMS = MIN_SOURCE_UPDATE_INTERVAL_MS / count;
- if (intervalMS < MIN_THUMBNAIL_UPDATE_INTERVAL_MS) {
- intervalMS = MIN_THUMBNAIL_UPDATE_INTERVAL_MS;
- }
- }
- updateTimer.start(intervalMS);
- }
- void ThumbnailManager::updateTick()
- {
- std::shared_ptr<ThumbnailItem> item;
- bool changed = false;
- bool newThumbnail = false;
- while (newThumbnails.size() > 0) {
- changed = true;
- item = newThumbnails.front().lock();
- newThumbnails.pop_front();
- if (item) {
- newThumbnail = true;
- break;
- }
- }
- if (!item) {
- while (thumbnails.size() > 0) {
- item = thumbnails.front().lock();
- thumbnails.pop_front();
- if (item) {
- break;
- } else {
- changed = true;
- }
- }
- }
- if (changed && newThumbnails.size() == 0) {
- updateIntervalChanged(thumbnails.size() + (item ? 1 : 0));
- }
- if (!item) {
- return;
- }
- if (updatePixmap(item)) {
- thumbnails.push_back(std::weak_ptr<ThumbnailItem>(item));
- } else {
- thumbnails.push_front(std::weak_ptr<ThumbnailItem>(item));
- }
- }
- std::optional<QPixmap> ThumbnailManager::getCachedThumbnail(OBSSource source)
- {
- if (!source) {
- return std::nullopt;
- }
- std::string uuid = obs_source_get_uuid(source);
- auto it = cachedThumbnails.find(uuid);
- if (it != cachedThumbnails.end()) {
- auto &cachedItem = it->second;
- if (cachedItem.pixmap.has_value()) {
- return cachedItem.pixmap;
- }
- auto activeItem = cachedItem.weakActiveItem.lock();
- return activeItem ? std::make_optional(activeItem->pixmap) : std::nullopt;
- }
- return std::nullopt;
- }
- void ThumbnailManager::preloadThumbnail(OBSSource source, QObject *object, std::function<void(QPixmap)> callback)
- {
- if (!source) {
- return;
- }
- std::string uuid = obs_source_get_uuid(source);
- if (cachedThumbnails.find(uuid) == cachedThumbnails.end()) {
- uint32_t sourceWidth = obs_source_get_width(source);
- uint32_t sourceHeight = obs_source_get_height(source);
- cachedThumbnails[uuid].pixmap = QPixmap();
- if (sourceWidth == 0 || sourceHeight == 0) {
- return;
- }
- auto obj = new ScreenshotObj(source);
- obj->setSaveToFile(false);
- obj->setSize(Thumbnail::size);
- QPointer<QObject> safeObject = qobject_cast<QObject *>(object);
- connect(obj, &ScreenshotObj::imageReady, this, [=](QImage image) {
- QPixmap pixmap;
- if (!image.isNull()) {
- pixmap = QPixmap::fromImage(image);
- }
- cachedThumbnails[uuid].pixmap = pixmap;
- QMetaObject::invokeMethod(
- safeObject,
- [safeObject, callback, pixmap]() {
- if (safeObject) {
- callback(pixmap);
- }
- },
- Qt::QueuedConnection);
- });
- }
- }
|