Cleanup screenshot helper

This commit is contained in:
WarmUpTill 2024-09-22 10:09:03 +02:00 committed by WarmUpTill
parent c39ea0854a
commit 55ab096e49
9 changed files with 165 additions and 205 deletions

View File

@ -105,7 +105,7 @@ void AdvSceneSwitcher::on_getScreenshot_clicked()
}
auto source = obs_weak_source_get_source(s->videoSource);
auto screenshotData = std::make_unique<ScreenshotHelper>(source);
auto screenshotData = std::make_unique<Screenshot>(source);
obs_source_release(source);
QString filePath = QFileDialog::getSaveFileName(this);
@ -121,16 +121,16 @@ void AdvSceneSwitcher::on_getScreenshot_clicked()
// During selection of the save path enough time should usually have
// passed already
// Add this just in case ...
if (!screenshotData->done) {
if (!screenshotData->IsDone()) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
if (!screenshotData->done) {
if (!screenshotData->IsDone()) {
DisplayMessage("Failed to get screenshot of source!");
return;
}
screenshotData->image.save(file.fileName());
screenshotData->GetImage().save(file.fileName());
sw->SetFilePath(file.fileName());
}
@ -262,7 +262,7 @@ void VideoSwitch::load(obs_data_t *obj)
void VideoSwitch::getScreenshot()
{
auto source = obs_weak_source_get_source(videoSource);
screenshotData = std::make_unique<ScreenshotHelper>(source);
screenshotData = std::make_unique<Screenshot>(source);
obs_source_release(source);
}
@ -293,56 +293,55 @@ bool VideoSwitch::checkMatch()
bool match = false;
if (screenshotData) {
if (screenshotData->done) {
bool conditionMatch = false;
switch (condition) {
case videoSwitchType::MATCH:
conditionMatch = screenshotData->image ==
matchImage;
break;
case videoSwitchType::DIFFER:
conditionMatch = screenshotData->image !=
matchImage;
break;
case videoSwitchType::HAS_NOT_CHANGED:
conditionMatch = screenshotData->image ==
matchImage;
break;
case videoSwitchType::HAS_CHANGED:
conditionMatch = screenshotData->image !=
matchImage;
break;
default:
break;
}
if (conditionMatch) {
currentMatchDuration +=
std::chrono::duration_cast<
std::chrono::milliseconds>(
screenshotData->time -
previousTime);
} else {
currentMatchDuration = {};
}
bool durationMatch = currentMatchDuration.count() >=
duration * 1000;
if (conditionMatch && durationMatch) {
match = true;
}
if (!requiresFileInput(condition)) {
matchImage = std::move(screenshotData->image);
}
previousTime = std::move(screenshotData->time);
screenshotData.reset(nullptr);
}
if (!screenshotData) {
getScreenshot();
return match;
}
if (!screenshotData->IsDone()) {
getScreenshot();
return match;
}
bool conditionMatch = false;
switch (condition) {
case videoSwitchType::MATCH:
conditionMatch = screenshotData->GetImage() == matchImage;
break;
case videoSwitchType::DIFFER:
conditionMatch = screenshotData->GetImage() != matchImage;
break;
case videoSwitchType::HAS_NOT_CHANGED:
conditionMatch = screenshotData->GetImage() == matchImage;
break;
case videoSwitchType::HAS_CHANGED:
conditionMatch = screenshotData->GetImage() != matchImage;
break;
default:
break;
}
if (conditionMatch) {
currentMatchDuration +=
std::chrono::duration_cast<std::chrono::milliseconds>(
screenshotData->GetScreenshotTime() -
previousTime);
} else {
currentMatchDuration = {};
}
bool durationMatch = currentMatchDuration.count() >= duration * 1000;
if (conditionMatch && durationMatch) {
match = true;
}
if (!requiresFileInput(condition)) {
matchImage = screenshotData->GetImage();
}
previousTime = screenshotData->GetScreenshotTime();
screenshotData.reset(nullptr);
getScreenshot();
return match;

View File

@ -32,7 +32,7 @@ struct VideoSwitch : virtual SceneSwitcherEntry {
double duration = 0;
bool ignoreInactiveSource = false;
std::unique_ptr<ScreenshotHelper> screenshotData = nullptr;
std::unique_ptr<Screenshot> screenshotData = nullptr;
std::chrono::high_resolution_clock::time_point previousTime{};
QImage matchImage;

View File

@ -5,12 +5,10 @@
namespace advss {
static void ScreenshotTick(void *param, float);
ScreenshotHelper::ScreenshotHelper(obs_source_t *source, const QRect &subarea,
bool blocking, int timeout, bool saveToFile,
std::string path)
: weakSource(OBSGetWeakRef(source)),
Screenshot::Screenshot(obs_source_t *source, const QRect &subarea,
bool blocking, int timeout, bool saveToFile,
std::string path)
: _weakSource(OBSGetWeakRef(source)),
_subarea(subarea),
_blocking(blocking),
_saveToFile(saveToFile),
@ -35,12 +33,12 @@ ScreenshotHelper::ScreenshotHelper(obs_source_t *source, const QRect &subarea,
}
}
ScreenshotHelper::~ScreenshotHelper()
Screenshot::~Screenshot()
{
if (_initDone) {
obs_enter_graphics();
gs_stagesurface_destroy(stagesurf);
gs_texrender_destroy(texrender);
gs_stagesurface_destroy(_stagesurf);
gs_texrender_destroy(_texrender);
obs_leave_graphics();
}
obs_remove_tick_callback(ScreenshotTick, this);
@ -49,21 +47,21 @@ ScreenshotHelper::~ScreenshotHelper()
}
}
void ScreenshotHelper::Screenshot()
void Screenshot::CreateScreenshot()
{
OBSSource source = OBSGetStrongRef(weakSource);
OBSSource source = OBSGetStrongRef(_weakSource);
if (source) {
cx = obs_source_get_base_width(source);
cy = obs_source_get_base_height(source);
_cx = obs_source_get_base_width(source);
_cy = obs_source_get_base_height(source);
} else {
obs_video_info ovi;
obs_get_video_info(&ovi);
cx = ovi.base_width;
cy = ovi.base_height;
_cx = ovi.base_width;
_cy = ovi.base_height;
}
QRect renderArea(0, 0, cx, cy);
QRect renderArea(0, 0, _cx, _cy);
if (!_subarea.isEmpty()) {
renderArea &= _subarea;
}
@ -73,19 +71,19 @@ void ScreenshotHelper::Screenshot()
"Cannot screenshot \"%s\", invalid target size",
obs_source_get_name(source));
obs_remove_tick_callback(ScreenshotTick, this);
done = true;
_done = true;
return;
}
cx = renderArea.width();
cy = renderArea.height();
_cx = renderArea.width();
_cy = renderArea.height();
texrender = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
stagesurf = gs_stagesurface_create(renderArea.width(),
renderArea.height(), GS_RGBA);
_texrender = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
_stagesurf = gs_stagesurface_create(renderArea.width(),
renderArea.height(), GS_RGBA);
gs_texrender_reset(texrender);
if (gs_texrender_begin(texrender, renderArea.width(),
gs_texrender_reset(_texrender);
if (gs_texrender_begin(_texrender, renderArea.width(),
renderArea.height())) {
vec4 zero;
vec4_zero(&zero);
@ -108,48 +106,48 @@ void ScreenshotHelper::Screenshot()
}
gs_blend_state_pop();
gs_texrender_end(texrender);
gs_texrender_end(_texrender);
}
}
void ScreenshotHelper::Download()
void Screenshot::Download()
{
gs_stage_texture(stagesurf, gs_texrender_get_texture(texrender));
gs_stage_texture(_stagesurf, gs_texrender_get_texture(_texrender));
}
void ScreenshotHelper::Copy()
void Screenshot::Copy()
{
uint8_t *videoData = nullptr;
uint32_t videoLinesize = 0;
image = QImage(cx, cy, QImage::Format::Format_RGBA8888);
_image = QImage(_cx, _cy, QImage::Format::Format_RGBA8888);
if (gs_stagesurface_map(stagesurf, &videoData, &videoLinesize)) {
int linesize = image.bytesPerLine();
for (int y = 0; y < (int)cy; y++)
memcpy(image.scanLine(y),
if (gs_stagesurface_map(_stagesurf, &videoData, &videoLinesize)) {
int linesize = _image.bytesPerLine();
for (int y = 0; y < (int)_cy; y++)
memcpy(_image.scanLine(y),
videoData + (y * videoLinesize), linesize);
gs_stagesurface_unmap(stagesurf);
gs_stagesurface_unmap(_stagesurf);
}
}
void ScreenshotHelper::MarkDone()
void Screenshot::MarkDone()
{
time = std::chrono::high_resolution_clock::now();
done = true;
_time = std::chrono::high_resolution_clock::now();
_done = true;
std::unique_lock<std::mutex> lock(_mutex);
_cv.notify_all();
}
void ScreenshotHelper::WriteToFile()
void Screenshot::WriteToFile()
{
if (!_saveToFile) {
return;
}
_saveThread = std::thread([this]() {
if (image.save(QString::fromStdString(_path))) {
if (_image.save(QString::fromStdString(_path))) {
vblog(LOG_INFO, "Wrote screenshot to \"%s\"",
_path.c_str());
} else {
@ -165,19 +163,19 @@ void ScreenshotHelper::WriteToFile()
#define STAGE_COPY_AND_SAVE 2
#define STAGE_FINISH 3
static void ScreenshotTick(void *param, float)
void Screenshot::ScreenshotTick(void *param, float)
{
ScreenshotHelper *data = reinterpret_cast<ScreenshotHelper *>(param);
Screenshot *data = reinterpret_cast<Screenshot *>(param);
if (data->stage == STAGE_FINISH) {
if (data->_stage == STAGE_FINISH) {
return;
}
obs_enter_graphics();
switch (data->stage) {
switch (data->_stage) {
case STAGE_SCREENSHOT:
data->Screenshot();
data->CreateScreenshot();
break;
case STAGE_DOWNLOAD:
data->Download();
@ -193,7 +191,7 @@ static void ScreenshotTick(void *param, float)
obs_leave_graphics();
data->stage++;
data->_stage++;
}
} // namespace advss

View File

@ -9,36 +9,42 @@
namespace advss {
class ScreenshotHelper {
public:
EXPORT ScreenshotHelper() = default;
EXPORT ScreenshotHelper(obs_source_t *source,
const QRect &subarea = QRect(),
bool blocking = false, int timeout = 1000,
bool saveToFile = false, std::string path = "");
EXPORT ScreenshotHelper &operator=(const ScreenshotHelper &) = delete;
EXPORT ScreenshotHelper(const ScreenshotHelper &) = delete;
EXPORT ~ScreenshotHelper();
class Screenshot {
using TimePoint = std::chrono::high_resolution_clock::time_point;
void Screenshot();
public:
EXPORT Screenshot() = default;
EXPORT Screenshot(obs_source_t *source, const QRect &subarea = QRect(),
bool blocking = false, int timeout = 1000,
bool saveToFile = false, std::string path = "");
EXPORT Screenshot &operator=(const Screenshot &) = delete;
EXPORT Screenshot(const Screenshot &) = delete;
EXPORT ~Screenshot();
EXPORT bool IsDone() const { return _done; }
EXPORT QImage &GetImage() { return _image; }
EXPORT TimePoint GetScreenshotTime() const { return _time; }
private:
static void ScreenshotTick(void *param, float);
void CreateScreenshot();
void Download();
void Copy();
void MarkDone();
void WriteToFile();
gs_texrender_t *texrender = nullptr;
gs_stagesurf_t *stagesurf = nullptr;
OBSWeakSource weakSource;
QImage image;
uint32_t cx = 0;
uint32_t cy = 0;
gs_texrender_t *_texrender = nullptr;
gs_stagesurf_t *_stagesurf = nullptr;
OBSWeakSource _weakSource;
QImage _image;
uint32_t _cx = 0;
uint32_t _cy = 0;
int stage = 0;
int _stage = 0;
bool done = false;
std::chrono::high_resolution_clock::time_point time;
bool _done = false;
TimePoint _time;
private:
std::atomic_bool _initDone = false;
QRect _subarea = QRect();
bool _blocking = false;

View File

@ -32,8 +32,8 @@ void MacroActionScreenshot::CustomScreenshot(OBSWeakSource &source)
return;
}
auto s = obs_weak_source_get_source(source);
_screenshot.~ScreenshotHelper();
new (&_screenshot) ScreenshotHelper(s, QRect(), false, 0, true, _path);
_screenshot.~Screenshot();
new (&_screenshot) Screenshot(s, QRect(), false, 0, true, _path);
obs_source_release(s);
}
@ -230,11 +230,7 @@ void MacroActionScreenshotEdit::UpdateEntryData()
void MacroActionScreenshotEdit::SceneChanged(const SceneSelection &s)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_scene = s;
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));

View File

@ -42,7 +42,7 @@ private:
void FrontendScreenshot(OBSWeakSource &);
void CustomScreenshot(OBSWeakSource &);
ScreenshotHelper _screenshot;
Screenshot _screenshot;
static bool _registered;
static const std::string id;

View File

@ -1,4 +1,5 @@
#include "macro-condition-video.hpp"
#include "screenshot-dialog.hpp"
#include <layout-helpers.hpp>
#include <macro-condition-edit.hpp>
@ -147,12 +148,12 @@ bool MacroConditionVideo::CheckCondition()
GetScreenshot(true);
}
if (_screenshotData.done) {
if (_screenshotData.IsDone()) {
match = Compare();
_lastMatchResult = match;
if (!requiresFileInput(_condition)) {
_matchImage = std::move(_screenshotData.image);
_matchImage = std::move(_screenshotData.GetImage());
}
_getNextScreenshot = true;
} else {
@ -225,7 +226,7 @@ std::string MacroConditionVideo::GetShortDesc() const
void MacroConditionVideo::GetScreenshot(bool blocking)
{
auto source = obs_weak_source_get_source(_video.GetVideo());
_screenshotData.~ScreenshotHelper();
_screenshotData.~Screenshot();
QRect screenshotArea;
if (_areaParameters.enable && _condition != VideoCondition::NO_IMAGE) {
screenshotArea.setRect(_areaParameters.area.x,
@ -233,8 +234,8 @@ void MacroConditionVideo::GetScreenshot(bool blocking)
_areaParameters.area.width,
_areaParameters.area.height);
}
new (&_screenshotData) ScreenshotHelper(source, screenshotArea,
blocking, GetIntervalValue());
new (&_screenshotData) Screenshot(source, screenshotArea, blocking,
GetIntervalValue());
obs_source_release(source);
_getNextScreenshot = false;
}
@ -293,7 +294,7 @@ void MacroConditionVideo::SetCondition(VideoCondition condition)
bool MacroConditionVideo::ScreenshotContainsPattern()
{
cv::Mat result;
MatchPattern(_screenshotData.image, _patternImageData,
MatchPattern(_screenshotData.GetImage(), _patternImageData,
_patternMatchParameters.threshold, result, nullptr,
_patternMatchParameters.useAlphaAsMask,
_patternMatchParameters.matchMode);
@ -319,12 +320,12 @@ bool MacroConditionVideo::FileInputIsUpToDate() const
bool MacroConditionVideo::OutputChanged()
{
if (!_patternMatchParameters.useForChangedCheck) {
return _screenshotData.image != _matchImage;
return _screenshotData.GetImage() != _matchImage;
}
cv::Mat result;
_patternImageData = CreatePatternData(_matchImage);
MatchPattern(_screenshotData.image, _patternImageData,
MatchPattern(_screenshotData.GetImage(), _patternImageData,
_patternMatchParameters.threshold, result, nullptr,
_patternMatchParameters.useAlphaAsMask,
_patternMatchParameters.matchMode);
@ -336,7 +337,7 @@ bool MacroConditionVideo::OutputChanged()
bool MacroConditionVideo::ScreenshotContainsObject()
{
auto objects = MatchObject(_screenshotData.image,
auto objects = MatchObject(_screenshotData.GetImage(),
_objMatchParameters.cascade,
_objMatchParameters.scaleFactor,
_objMatchParameters.minNeighbors,
@ -349,7 +350,8 @@ bool MacroConditionVideo::ScreenshotContainsObject()
bool MacroConditionVideo::CheckBrightnessThreshold()
{
_currentBrightness = GetAvgBrightness(_screenshotData.image) / 255.;
_currentBrightness =
GetAvgBrightness(_screenshotData.GetImage()) / 255.;
SetTempVarValue("brightness", std::to_string(_currentBrightness));
return _currentBrightness > _brightnessThreshold;
}
@ -360,7 +362,7 @@ bool MacroConditionVideo::CheckOCR()
return false;
}
auto text = RunOCR(_ocrParameters.GetOCR(), _screenshotData.image,
auto text = RunOCR(_ocrParameters.GetOCR(), _screenshotData.GetImage(),
_ocrParameters.color, _ocrParameters.colorThreshold);
SetVariableValue(text);
SetTempVarValue("text", text);
@ -373,14 +375,14 @@ bool MacroConditionVideo::CheckOCR()
bool MacroConditionVideo::CheckColor()
{
const bool ret = ContainsPixelsInColorRange(
_screenshotData.image, _colorParameters.color,
_screenshotData.GetImage(), _colorParameters.color,
_colorParameters.colorThreshold,
_colorParameters.matchThreshold);
// Way too slow for now
//SetTempVarValue("dominantColor", GetDominantColor(_screenshotData.image, 3)
// .name(QColor::HexArgb)
// .toStdString());
SetTempVarValue("color", GetAverageColor(_screenshotData.image)
SetTempVarValue("color", GetAverageColor(_screenshotData.GetImage())
.name(QColor::HexArgb)
.toStdString());
return ret;
@ -394,15 +396,15 @@ bool MacroConditionVideo::Compare()
switch (_condition) {
case VideoCondition::MATCH:
return _screenshotData.image == _matchImage;
return _screenshotData.GetImage() == _matchImage;
case VideoCondition::DIFFER:
return _screenshotData.image != _matchImage;
return _screenshotData.GetImage() != _matchImage;
case VideoCondition::HAS_CHANGED:
return OutputChanged();
case VideoCondition::HAS_NOT_CHANGED:
return !OutputChanged();
case VideoCondition::NO_IMAGE:
return _screenshotData.image.isNull();
return _screenshotData.GetImage().isNull();
case VideoCondition::PATTERN:
return ScreenshotContainsPattern();
case VideoCondition::OBJECT:
@ -1418,11 +1420,6 @@ void MacroConditionVideoEdit::ImageBrowseButtonClicked()
}
} else {
auto source = obs_weak_source_get_source(
_entryData->_video.GetVideo());
ScreenshotHelper screenshot(source);
obs_source_release(source);
path = QFileDialog::getSaveFileName(
this, "",
FileSelection::ValidPathOrDesktop(
@ -1435,22 +1432,13 @@ void MacroConditionVideoEdit::ImageBrowseButtonClicked()
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return;
}
if (!screenshot.done) { // Screenshot usually completed by now
std::this_thread::sleep_for(std::chrono::seconds(1));
}
if (!screenshot.done) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.condition.video.screenshotFail"));
auto source = OBSGetStrongRef(_entryData->_video.GetVideo());
auto screenshot = ScreenshotDialog::AskForScreenshot(source);
if (!screenshot) {
return;
}
if (_entryData->_areaParameters.enable) {
screenshot.image = screenshot.image.copy(
_entryData->_areaParameters.area.x,
_entryData->_areaParameters.area.y,
_entryData->_areaParameters.area.width,
_entryData->_areaParameters.area.height);
}
screenshot.image.save(path);
screenshot->save(path);
}
_imagePath->SetPath(path);
ImagePathChanged(path);
@ -1458,11 +1446,7 @@ void MacroConditionVideoEdit::ImageBrowseButtonClicked()
void MacroConditionVideoEdit::UsePatternForChangedCheckChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_patternMatchParameters.useForChangedCheck = value;
SetWidgetVisibility();
}
@ -1470,11 +1454,7 @@ void MacroConditionVideoEdit::UsePatternForChangedCheckChanged(int value)
void MacroConditionVideoEdit::PatternThresholdChanged(
const DoubleVariable &value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_patternMatchParameters.threshold = value;
_previewDialog.PatternMatchParametersChanged(
_entryData->_patternMatchParameters);
@ -1482,21 +1462,13 @@ void MacroConditionVideoEdit::PatternThresholdChanged(
void MacroConditionVideoEdit::ReduceLatencyChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_blockUntilScreenshotDone = value;
}
void MacroConditionVideoEdit::UseAlphaAsMaskChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_patternMatchParameters.useAlphaAsMask = value;
_entryData->LoadImageFromFile();
_previewDialog.PatternMatchParametersChanged(
@ -1505,11 +1477,7 @@ void MacroConditionVideoEdit::UseAlphaAsMaskChanged(int value)
void MacroConditionVideoEdit::PatternMatchModeChanged(int idx)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_patternMatchParameters.matchMode =
static_cast<cv::TemplateMatchModes>(
_patternMatchMode->itemData(idx).toInt());
@ -1519,22 +1487,14 @@ void MacroConditionVideoEdit::PatternMatchModeChanged(int idx)
void MacroConditionVideoEdit::ThrottleEnableChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_throttleEnabled = value;
_throttleCount->setEnabled(value);
}
void MacroConditionVideoEdit::ThrottleCountChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
GUARD_LOADING_AND_LOCK();
_entryData->_throttleCount = value / GetIntervalValue();
}

View File

@ -90,7 +90,7 @@ private:
VideoCondition _condition = VideoCondition::MATCH;
bool _getNextScreenshot = true;
ScreenshotHelper _screenshotData;
Screenshot _screenshotData;
QImage _matchImage;
PatternImageData _patternImageData;

View File

@ -277,17 +277,18 @@ void PreviewImage::CreateImage(const VideoInput &video, PreviewType type,
areaParams.area.width,
areaParams.area.height);
}
ScreenshotHelper screenshot(source, screenshotArea, true);
Screenshot screenshot(source, screenshotArea, true);
obs_source_release(source);
if (!video.ValidSelection() || !screenshot.done) {
if (!video.ValidSelection() || !screenshot.IsDone()) {
emit StatusUpdate(obs_module_text(
"AdvSceneSwitcher.condition.video.screenshotFail"));
emit ImageReady(QPixmap());
return;
}
if (screenshot.image.width() == 0 || screenshot.image.height() == 0) {
if (screenshot.GetImage().width() == 0 ||
screenshot.GetImage().height() == 0) {
emit StatusUpdate(obs_module_text(
"AdvSceneSwitcher.condition.video.screenshotEmpty"));
emit ImageReady(QPixmap());
@ -297,14 +298,14 @@ void PreviewImage::CreateImage(const VideoInput &video, PreviewType type,
if (type == PreviewType::SHOW_MATCH) {
std::unique_lock<std::mutex> lock(_mtx);
// Will emit status label update
MarkMatch(screenshot.image, patternMatchParams,
MarkMatch(screenshot.GetImage(), patternMatchParams,
patternImageData, objDetectParams, ocrParams,
condition);
} else {
emit StatusUpdate(obs_module_text(
"AdvSceneSwitcher.condition.video.selectArea.status"));
}
emit ImageReady(QPixmap::fromImage(screenshot.image));
emit ImageReady(QPixmap::fromImage(screenshot.GetImage()));
}
void PreviewImage::MarkMatch(QImage &screenshot,