File indexing completed on 2024-05-05 04:53:41

0001 /*
0002     SPDX-FileCopyrightText: 2011-2016 Meltytech LLC
0003     SPDX-FileCopyrightText: Dan Dennedy <dan@dennedy.org>
0004     SPDX-FileCopyrightText: Jean-Baptiste Mardelle
0005 
0006     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007 
0008     GL shader based on BSD licensed code from Peter Bengtsson:
0009     https://www.fourcc.org/source/YUV420P-OpenGL-GLSLang.c
0010     SPDX-FileCopyrightText: 2004 Peter Bengtsson
0011 
0012     SPDX-License-Identifier: BSD-3-Clause
0013 */
0014 
0015 #include <QApplication>
0016 #include <QFontDatabase>
0017 #include <QOpenGLContext>
0018 #include <QOpenGLFunctions_3_2_Core>
0019 #include <QPainter>
0020 #include <QQmlContext>
0021 #include <QQuickItem>
0022 #include <QtGlobal>
0023 #include <memory>
0024 
0025 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0026 #include "kdeclarative_version.h"
0027 #endif
0028 #if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) || KDECLARATIVE_VERSION > QT_VERSION_CHECK(5, 98, 0)
0029 #include <KQuickIconProvider>
0030 #else
0031 #include <KDeclarative/KDeclarative>
0032 #endif
0033 #include <KLocalizedContext>
0034 #include <KLocalizedString>
0035 #include <KMessageBox>
0036 
0037 #include "bin/model/markersortmodel.h"
0038 #include "core.h"
0039 #include "monitorproxy.h"
0040 #include "profiles/profilemodel.hpp"
0041 #include "timeline2/view/qml/timelineitems.h"
0042 #include "timeline2/view/qmltypes/thumbnailprovider.h"
0043 #include "videowidget.h"
0044 #include <lib/localeHandling.h>
0045 #include <mlt++/Mlt.h>
0046 
0047 #ifndef GL_UNPACK_ROW_LENGTH
0048 #ifdef GL_UNPACK_ROW_LENGTH_EXT
0049 #define GL_UNPACK_ROW_LENGTH GL_UNPACK_ROW_LENGTH_EXT
0050 #else
0051 #error GL_UNPACK_ROW_LENGTH undefined
0052 #endif
0053 #endif
0054 
0055 #if 1
0056 #define check_error(fn)                                                                                                                                        \
0057     {                                                                                                                                                          \
0058     }
0059 #else
0060 #define check_error(fn)                                                                                                                                        \
0061     {                                                                                                                                                          \
0062         int err = fn->glGetError();                                                                                                                            \
0063         if (err != GL_NO_ERROR) {                                                                                                                              \
0064             qCritical(KDENLIVE_LOG) << "GL error" << hex << err << dec << "at" << __FILE__ << ":" << __LINE__;                                                 \
0065         }                                                                                                                                                      \
0066     }
0067 #endif
0068 
0069 #ifndef GL_TIMEOUT_IGNORED
0070 #define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull
0071 #endif
0072 
0073 using namespace Mlt;
0074 
0075 VideoWidget::VideoWidget(int id, QObject *parent)
0076     : QQuickWidget((QWidget *)parent)
0077     , sendFrameForAnalysis(false)
0078     , m_consumer(nullptr)
0079     , m_producer(nullptr)
0080     , m_id(id)
0081     , m_rulerHeight(int(QFontInfo(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)).pixelSize() * 1.5))
0082     , m_initSem(0)
0083     , m_analyseSem(1)
0084     , m_isInitialized(false)
0085     , m_maxProducerPosition(0)
0086     , m_glslManager(nullptr)
0087     , m_frameRenderer(nullptr)
0088     , m_zoom(1.0f)
0089     , m_profileSize(1920, 1080)
0090     , m_colorSpace(601)
0091     , m_dar(1.78)
0092     , m_sendFrame(false)
0093     , m_isZoneMode(false)
0094     , m_isLoopMode(false)
0095     , m_loopIn(0)
0096     , m_offset(QPoint(0, 0))
0097 {
0098 #if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) || KDECLARATIVE_VERSION > QT_VERSION_CHECK(5, 98, 0)
0099     engine()->addImageProvider(QStringLiteral("icon"), new KQuickIconProvider);
0100 #else
0101     KDeclarative::KDeclarative kdeclarative;
0102     kdeclarative.setDeclarativeEngine(engine());
0103     kdeclarative.setupEngine(engine());
0104 #endif
0105     engine()->rootContext()->setContextObject(new KLocalizedContext(this));
0106     qRegisterMetaType<Mlt::Frame>("Mlt::Frame");
0107     qRegisterMetaType<SharedFrame>("SharedFrame");
0108     setAcceptDrops(true);
0109     setClearColor(KdenliveSettings::window_background());
0110 
0111     if (m_id == Kdenlive::ClipMonitor && !(KdenliveSettings::displayClipMonitorInfo() & 0x01)) {
0112         m_rulerHeight = 0;
0113     } else if (!(KdenliveSettings::displayProjectMonitorInfo() & 0x01)) {
0114         m_rulerHeight = 0;
0115     }
0116     m_displayRulerHeight = m_rulerHeight;
0117     if (!initGPUAccel()) {
0118         m_glslManager.reset();
0119     }
0120     quickWindow()->setPersistentGraphics(true);
0121     quickWindow()->setPersistentSceneGraph(true);
0122     setResizeMode(QQuickWidget::SizeRootObjectToView);
0123 
0124     m_refreshTimer.setSingleShot(true);
0125     m_refreshTimer.setInterval(10);
0126     m_blackClip.reset(new Mlt::Producer(pCore->getProjectProfile(), "color:0"));
0127     m_blackClip->set("mlt_image_format", "rgba");
0128     m_blackClip->set("kdenlive:id", "black");
0129     m_blackClip->set("out", 3);
0130     connect(&m_refreshTimer, &QTimer::timeout, this, &VideoWidget::refresh);
0131     m_producer = m_blackClip;
0132     rootContext()->setContextProperty("markersModel", nullptr);
0133     connect(pCore.get(), &Core::switchTimelineRecord, this, &VideoWidget::switchRecordState);
0134 
0135     registerTimelineItems();
0136     m_proxy = new MonitorProxy(this);
0137     rootContext()->setContextProperty("controller", m_proxy);
0138     engine()->addImageProvider(QStringLiteral("thumbnail"), new ThumbnailProvider);
0139 }
0140 
0141 VideoWidget::~VideoWidget()
0142 {
0143     stop();
0144     if (m_frameRenderer && m_frameRenderer->isRunning()) {
0145         m_frameRenderer->quit();
0146         m_frameRenderer->wait();
0147         m_frameRenderer->deleteLater();
0148     }
0149     m_blackClip.reset();
0150 }
0151 
0152 void VideoWidget::updateAudioForAnalysis()
0153 {
0154     if (m_frameRenderer) {
0155         m_frameRenderer->requestImage();
0156     }
0157 }
0158 
0159 void VideoWidget::initialize()
0160 {
0161     m_frameRenderer = new FrameRenderer();
0162     connect(m_frameRenderer, &FrameRenderer::frameDisplayed, this, &VideoWidget::onFrameDisplayed, Qt::QueuedConnection);
0163     connect(m_frameRenderer, &FrameRenderer::frameDisplayed, this, &VideoWidget::frameDisplayed, Qt::QueuedConnection);
0164     connect(m_frameRenderer, SIGNAL(imageReady()), SIGNAL(imageReady()));
0165     m_initSem.release();
0166     m_isInitialized = true;
0167 }
0168 
0169 void VideoWidget::renderVideo() {}
0170 
0171 const QStringList VideoWidget::getGPUInfo()
0172 {
0173     return {};
0174 }
0175 
0176 void VideoWidget::resizeVideo(int width, int height)
0177 {
0178     int x, y, w, h;
0179     height -= m_displayRulerHeight;
0180     double this_aspect = double(width) / height;
0181 
0182     // Special case optimization to negate odd effect of sample aspect ratio
0183     // not corresponding exactly with image resolution.
0184     if (int(this_aspect * 1000) == int(m_dar * 1000)) {
0185         w = width;
0186         h = height;
0187     }
0188     // Use OpenGL to normalise sample aspect ratio
0189     else if (height * m_dar > width) {
0190         w = width;
0191         h = int(width / m_dar);
0192     } else {
0193         w = int(height * m_dar);
0194         h = height;
0195     }
0196     x = (width - w) / 2;
0197     y = (height - h) / 2;
0198     m_rect.setRect(x, y, w, h);
0199     QQuickItem *rootQml = rootObject();
0200     if (rootQml) {
0201         QSize s = pCore->getCurrentFrameSize();
0202         double scalex = double(m_rect.width() * m_zoom) / s.width();
0203         double scaley = double(m_rect.height() * m_zoom) / s.height();
0204         rootQml->setProperty("center", m_rect.center());
0205         rootQml->setProperty("scalex", scalex);
0206         rootQml->setProperty("scaley", scaley);
0207         if (rootQml->objectName() == QLatin1String("rootsplit")) {
0208             // Adjust splitter pos
0209             rootQml->setProperty("splitterPos", x + (rootQml->property("percentage").toDouble() * w));
0210         }
0211     }
0212     Q_EMIT rectChanged();
0213 }
0214 
0215 void VideoWidget::resizeEvent(QResizeEvent *event)
0216 {
0217     QQuickWidget::resizeEvent(event);
0218     if (refreshZoom) {
0219         setZoom(m_zoom, true);
0220         refreshZoom = false;
0221     }
0222     resizeVideo(event->size().width(), event->size().height());
0223 }
0224 
0225 void VideoWidget::clear()
0226 {
0227     stopGlsl();
0228     quickWindow()->update();
0229 }
0230 
0231 void VideoWidget::releaseAnalyse()
0232 {
0233     m_analyseSem.release();
0234 }
0235 
0236 bool VideoWidget::initGPUAccel()
0237 {
0238     if (!KdenliveSettings::gpu_accel()) return false;
0239 
0240     m_glslManager.reset(new Mlt::Filter(pCore->getProjectProfile(), "glsl.manager"));
0241     return m_glslManager->is_valid();
0242 }
0243 
0244 void VideoWidget::disableGPUAccel()
0245 {
0246     m_glslManager.reset();
0247     KdenliveSettings::setGpu_accel(false);
0248     // Need to destroy MLT global reference to prevent filters from trying to use GPU.
0249     mlt_properties_set_data(mlt_global_properties(), "glslManager", nullptr, 0, nullptr, nullptr);
0250     Q_EMIT gpuNotSupported();
0251 }
0252 
0253 void VideoWidget::slotZoom(bool zoomIn)
0254 {
0255     if (zoomIn) {
0256         if (m_zoom < 12.0f) {
0257             setZoom(m_zoom * 1.2);
0258         }
0259     } else if (m_zoom > 0.2f) {
0260         setZoom(m_zoom / 1.2f);
0261     }
0262 }
0263 
0264 void VideoWidget::updateRulerHeight(int addedHeight)
0265 {
0266     m_displayRulerHeight =
0267         m_rulerHeight > 0 ? int(QFontInfo(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)).pixelSize() * 1.5) + addedHeight : 0;
0268     resizeVideo(width(), height());
0269 }
0270 
0271 bool VideoWidget::isReady() const
0272 {
0273     return m_consumer != nullptr;
0274 }
0275 
0276 void VideoWidget::requestSeek(int position, bool noAudioScrub)
0277 {
0278     m_producer->seek(position);
0279     if (!qFuzzyIsNull(m_producer->get_speed())) {
0280         m_consumer->purge();
0281     }
0282     restartConsumer();
0283     m_consumer->set("refresh", 1);
0284     if (KdenliveSettings::audio_scrub() && !noAudioScrub) {
0285         m_consumer->set("scrub_audio", 1);
0286     } else {
0287         m_consumer->set("scrub_audio", 0);
0288     }
0289 }
0290 
0291 void VideoWidget::requestRefresh()
0292 {
0293     if (m_producer && qFuzzyIsNull(m_producer->get_speed())) {
0294         m_consumer->set("scrub_audio", 0);
0295         m_refreshTimer.start();
0296     }
0297 }
0298 
0299 QString VideoWidget::frameToTime(int frames) const
0300 {
0301     return m_consumer ? m_consumer->frames_to_time(frames, mlt_time_smpte_df) : QStringLiteral("-");
0302 }
0303 
0304 void VideoWidget::refresh()
0305 {
0306     m_refreshTimer.stop();
0307     QMutexLocker locker(&m_mltMutex);
0308     if (m_consumer) {
0309         restartConsumer();
0310         m_consumer->set("refresh", 1);
0311     }
0312 }
0313 
0314 bool VideoWidget::checkFrameNumber(int pos, bool isPlaying)
0315 {
0316     const double speed = m_producer->get_speed();
0317     m_proxy->positionFromConsumer(pos, isPlaying);
0318     if (m_isLoopMode || m_isZoneMode) {
0319         // not sure why we need to check against pos + 1 but otherwise the
0320         // playback shows one frame after the intended out frame
0321         if (isPlaying && pos + 1 >= m_loopOut) {
0322             m_consumer->purge();
0323             if (!m_isLoopMode) {
0324                 // end play zone mode
0325                 m_isZoneMode = false;
0326                 m_producer->set_speed(0);
0327                 m_proxy->setSpeed(0);
0328                 m_consumer->set("refresh", 0);
0329                 m_proxy->setPosition(m_loopOut);
0330                 m_producer->seek(m_loopOut);
0331                 m_loopOut = 0;
0332                 return false;
0333             }
0334             m_producer->seek(m_isZoneMode ? m_proxy->zoneIn() : m_loopIn);
0335             m_producer->set_speed(1.0);
0336             m_proxy->setSpeed(1.);
0337             m_consumer->set("refresh", 1);
0338             return true;
0339         }
0340         return true;
0341     } else if (isPlaying) {
0342         if (pos > m_maxProducerPosition - 2 && !(speed < 0.)) {
0343             // Playing past last clip, pause
0344             m_producer->set_speed(0);
0345             m_proxy->setSpeed(0);
0346             m_consumer->set("refresh", 0);
0347             m_consumer->purge();
0348             m_proxy->setPosition(qMax(0, m_maxProducerPosition));
0349             m_producer->seek(qMax(0, m_maxProducerPosition));
0350             return false;
0351         } else if (pos <= 0 && speed < 0.) {
0352             // rewinding reached 0, pause
0353             m_producer->set_speed(0);
0354             m_proxy->setSpeed(0);
0355             m_consumer->set("refresh", 0);
0356             m_consumer->purge();
0357             m_proxy->setPosition(0);
0358             m_producer->seek(0);
0359             return false;
0360         }
0361     }
0362     return isPlaying;
0363 }
0364 
0365 void VideoWidget::mousePressEvent(QMouseEvent *event)
0366 {
0367     if ((rootObject() != nullptr) && rootObject()->property("captureRightClick").toBool() && !(event->modifiers() & Qt::ControlModifier) &&
0368         !(event->buttons() & Qt::MiddleButton)) {
0369         event->ignore();
0370         QQuickWidget::mousePressEvent(event);
0371         return;
0372     }
0373     QQuickWidget::mousePressEvent(event);
0374     // For some reason, on Qt6 in mouseReleaseEvent, the event is always accepted, so use this m_qmlEvent bool to track if the event is accepted in qml
0375     m_qmlEvent = event->isAccepted();
0376     if (rootObject() != nullptr && rootObject()->property("captureRightClick").toBool()) {
0377         // The event has been handled in qml
0378         m_swallowDrop = true;
0379     }
0380     // event->accept();
0381     if ((event->button() & Qt::LeftButton) != 0u) {
0382         if ((event->modifiers() & Qt::ControlModifier) != 0u) {
0383             // Pan view
0384             m_panStart = event->pos();
0385             setCursor(Qt::ClosedHandCursor);
0386         } else {
0387             m_dragStart = event->pos();
0388         }
0389     } else if ((event->button() & Qt::RightButton) != 0u) {
0390 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0391         Q_EMIT showContextMenu(event->globalPos());
0392 #else
0393         Q_EMIT showContextMenu(event->globalPosition().toPoint());
0394 #endif
0395     } else if ((event->button() & Qt::MiddleButton) != 0u) {
0396         m_panStart = event->pos();
0397         setCursor(Qt::ClosedHandCursor);
0398     }
0399 }
0400 
0401 void VideoWidget::mouseReleaseEvent(QMouseEvent *event)
0402 {
0403     QQuickWidget::mouseReleaseEvent(event);
0404     /*if (m_dragStart.isNull() && m_panStart.isNull() && (rootObject() != nullptr) && rootObject()->objectName() != QLatin1String("root") &&
0405         !(event->modifiers() & Qt::ControlModifier)) {
0406         event->accept();
0407         qDebug()<<"::::::: MOUSE RELEASED B IGNORED";
0408         return;
0409     }*/
0410     if (event->modifiers() & Qt::ControlModifier || m_qmlEvent) {
0411         event->accept();
0412         return;
0413     }
0414     if (!m_dragStart.isNull() && m_panStart.isNull() && ((event->button() & Qt::LeftButton) != 0u) && !m_swallowDrop) {
0415         event->accept();
0416         Q_EMIT monitorPlay();
0417     }
0418     m_swallowDrop = false;
0419     m_dragStart = QPoint();
0420     m_panStart = QPoint();
0421     setCursor(Qt::ArrowCursor);
0422 }
0423 
0424 void VideoWidget::mouseMoveEvent(QMouseEvent *event)
0425 {
0426     if ((rootObject() != nullptr) && rootObject()->objectName() != QLatin1String("root") && !(event->modifiers() & Qt::ControlModifier) &&
0427         !(event->buttons() & Qt::MiddleButton)) {
0428         event->ignore();
0429         QQuickWidget::mouseMoveEvent(event);
0430         return;
0431     }
0432     QQuickWidget::mouseMoveEvent(event);
0433     if (!(event->buttons() & Qt::LeftButton)) {
0434         event->accept();
0435         return;
0436     }
0437     if (!m_panStart.isNull()) {
0438         Q_EMIT panView(m_panStart - event->pos());
0439         m_panStart = event->pos();
0440         event->accept();
0441         return;
0442     }
0443 
0444     if (!event->isAccepted() && !m_dragStart.isNull() && (event->pos() - m_dragStart).manhattanLength() >= QApplication::startDragDistance()) {
0445         m_dragStart = QPoint();
0446         Q_EMIT startDrag();
0447     }
0448     event->accept();
0449 }
0450 
0451 void VideoWidget::keyPressEvent(QKeyEvent *event)
0452 {
0453     QQuickWidget::keyPressEvent(event);
0454     if (!event->isAccepted()) {
0455         Q_EMIT passKeyEvent(event);
0456     }
0457 }
0458 
0459 void VideoWidget::createThread(RenderThread **thread, thread_function_t function, void *data)
0460 {
0461 #ifdef Q_OS_WIN
0462     // On Windows, MLT event consumer-thread-create is fired from the Qt main thread.
0463     while (!m_isInitialized) {
0464         qApp->processEvents();
0465     }
0466 #else
0467     if (!m_isInitialized) {
0468         m_initSem.acquire();
0469     }
0470 #endif
0471     if (!m_renderThread) {
0472         m_renderThread.reset(new RenderThread(function, data));
0473         (*thread) = m_renderThread.get();
0474         (*thread)->start();
0475     } else {
0476         m_renderThread->start();
0477     }
0478 }
0479 
0480 static void onThreadCreate(mlt_properties owner, VideoWidget *self, mlt_event_data data)
0481 {
0482     Q_UNUSED(owner)
0483     auto threadData = (mlt_event_data_thread *)Mlt::EventData(data).to_object();
0484     if (threadData) {
0485         auto renderThread = (RenderThread *)threadData->thread;
0486         self->createThread(&renderThread, threadData->function, threadData->data);
0487         // TODO: useless ?
0488         // self->lockMonitor();
0489     }
0490 }
0491 
0492 static void onThreadJoin(mlt_properties owner, VideoWidget *self, mlt_event_data data)
0493 {
0494     Q_UNUSED(owner)
0495     Q_UNUSED(self)
0496     auto threadData = (mlt_event_data_thread *)Mlt::EventData(data).to_object();
0497     if (threadData) {
0498         auto renderThread = (RenderThread *)threadData->thread;
0499         if (renderThread) {
0500             renderThread->quit();
0501             renderThread->wait();
0502             delete renderThread;
0503             // TODO: useless ?
0504             // self->releaseMonitor();
0505         }
0506     }
0507 }
0508 
0509 void VideoWidget::startGlsl()
0510 {
0511     // C & D
0512     if (m_glslManager) {
0513         // clearFrameRenderer();
0514         m_glslManager->fire_event("init glsl");
0515         if (m_glslManager->get_int("glsl_supported") == 0) {
0516             disableGPUAccel();
0517         } else {
0518             Q_EMIT started();
0519         }
0520     }
0521 }
0522 
0523 static void onThreadStarted(mlt_properties owner, VideoWidget *self, mlt_event_data)
0524 {
0525     Q_UNUSED(owner)
0526     self->startGlsl();
0527 }
0528 
0529 void VideoWidget::releaseMonitor()
0530 {
0531     Q_EMIT lockMonitor(false);
0532 }
0533 
0534 void VideoWidget::lockMonitor()
0535 {
0536     Q_EMIT lockMonitor(true);
0537 }
0538 
0539 void VideoWidget::stopGlsl()
0540 {
0541     if (m_consumer) {
0542         m_consumer->purge();
0543     }
0544 }
0545 
0546 static void onThreadStopped(mlt_properties owner, VideoWidget *self, mlt_event_data)
0547 {
0548     Q_UNUSED(owner)
0549     self->stopGlsl();
0550 }
0551 
0552 int VideoWidget::setProducer(const QString &file)
0553 {
0554     if (m_producer) {
0555         m_producer.reset();
0556     }
0557     m_producer = std::make_shared<Mlt::Producer>(new Mlt::Producer(pCore->getProjectProfile(), nullptr, file.toUtf8().constData()));
0558     if (!m_producer || !m_producer->is_valid()) {
0559         m_producer.reset();
0560         m_producer = m_blackClip;
0561     }
0562     if (m_consumer) {
0563         // m_consumer->stop();
0564         if (!m_consumer->is_stopped()) {
0565             m_consumer->stop();
0566         }
0567     }
0568     int error = reconfigure();
0569     if (error == 0) {
0570         // The profile display aspect ratio may have changed.
0571         resizeVideo(width(), height());
0572         startConsumer();
0573     }
0574     return error;
0575 }
0576 
0577 int VideoWidget::setProducer(const std::shared_ptr<Mlt::Producer> &producer, bool isActive, int position)
0578 {
0579     int error = 0;
0580     QString currentId;
0581     int consumerPosition = 0;
0582     if (m_producer) {
0583         currentId = m_producer->parent().get("kdenlive:id");
0584     }
0585     if (m_consumer) {
0586         consumerPosition = m_consumer->position();
0587     }
0588     stop();
0589     if (producer) {
0590         m_producer = producer;
0591     } else {
0592         if (currentId == QLatin1String("black")) {
0593             return 0;
0594         }
0595         m_producer = m_blackClip;
0596         // Reset markersModel
0597         rootContext()->setContextProperty("markersModel", nullptr);
0598     }
0599     m_producer->set_speed(0);
0600     m_proxy->setSpeed(0);
0601     error = reconfigure();
0602     if (error == 0) {
0603         // The profile display aspect ratio may have changed.
0604         resizeVideo(width(), height());
0605     } else {
0606         return error;
0607     }
0608     if (!m_consumer) {
0609         return error;
0610     }
0611     if (position == -1 && m_producer->parent().get("kdenlive:id") == currentId) {
0612         position = consumerPosition;
0613     }
0614     if (isActive) {
0615         startConsumer();
0616         if (position != -2) {
0617             m_proxy->resetPosition();
0618         }
0619     }
0620     m_consumer->set("scrub_audio", 0);
0621     if (position != -2) {
0622         m_proxy->setPositionAdvanced(position > 0 ? position : m_producer->position(), true);
0623     }
0624     return error;
0625 }
0626 
0627 int VideoWidget::droppedFrames() const
0628 {
0629     return (m_consumer ? m_consumer->get_int("drop_count") : 0);
0630 }
0631 
0632 void VideoWidget::resetDrops()
0633 {
0634     if (m_consumer) {
0635         m_consumer->set("drop_count", 0);
0636     }
0637 }
0638 
0639 void VideoWidget::stopCapture()
0640 {
0641     if (strcmp(m_consumer->get("mlt_service"), "multi") == 0) {
0642         m_consumer->set("refresh", 0);
0643         m_consumer->purge();
0644         m_consumer->stop();
0645     }
0646 }
0647 
0648 int VideoWidget::reconfigure()
0649 {
0650     int error = 0;
0651     // use SDL for audio, OpenGL for video
0652     QString serviceName = property("mlt_service").toString();
0653     if ((m_consumer == nullptr) || !m_consumer->is_valid() || strcmp(m_consumer->get("mlt_service"), "multi") == 0) {
0654         if (m_consumer) {
0655             m_consumer->purge();
0656             m_consumer->stop();
0657             m_consumer.reset();
0658         }
0659         QString audioBackend = (KdenliveSettings::external_display()) ? QString("decklink:%1").arg(KdenliveSettings::blackmagic_output_device())
0660                                                                       : KdenliveSettings::audiobackend();
0661         if (m_consumer == nullptr || serviceName.isEmpty() || serviceName != audioBackend) {
0662             m_consumer.reset(new Mlt::FilteredConsumer(pCore->getMonitorProfile(), audioBackend.toLatin1().constData()));
0663             if (m_consumer->is_valid()) {
0664                 serviceName = audioBackend;
0665             } else {
0666                 // Warning, audio backend unavailable on system
0667                 m_consumer.reset();
0668                 QStringList backends = {"sdl2_audio", "sdl_audio", "rtaudio"};
0669                 for (const QString &bk : backends) {
0670                     if (bk == audioBackend) {
0671                         // Already tested
0672                         continue;
0673                     }
0674                     m_consumer.reset(new Mlt::FilteredConsumer(pCore->getMonitorProfile(), bk.toLatin1().constData()));
0675                     if (m_consumer->is_valid()) {
0676                         if (audioBackend == KdenliveSettings::sdlAudioBackend()) {
0677                             // switch sdl audio backend
0678                             KdenliveSettings::setSdlAudioBackend(bk);
0679                         }
0680                         KdenliveSettings::setAudiobackend(bk);
0681                         serviceName = bk;
0682                         break;
0683                     } else {
0684                         m_consumer.reset();
0685                     }
0686                 }
0687             }
0688             if (!m_consumer || !m_consumer->is_valid()) {
0689                 qWarning() << "no audio backend found";
0690                 return -1;
0691             }
0692             setProperty("mlt_service", serviceName);
0693             if (KdenliveSettings::external_display()) {
0694                 m_consumer->set("terminate_on_pause", 0);
0695             }
0696             m_consumer->set("width", m_profileSize.width());
0697             m_consumer->set("height", m_profileSize.height());
0698             m_colorSpace = pCore->getCurrentProfile()->colorspace();
0699             m_dar = pCore->getCurrentDar();
0700         }
0701         m_threadStartEvent.reset();
0702         m_threadStopEvent.reset();
0703         m_threadCreateEvent.reset();
0704         m_threadJoinEvent.reset();
0705 
0706         if (m_glslManager) {
0707             m_threadCreateEvent.reset(m_consumer->listen("consumer-thread-create", this, mlt_listener(onThreadCreate)));
0708             m_threadJoinEvent.reset(m_consumer->listen("consumer-thread-join", this, mlt_listener(onThreadJoin)));
0709         }
0710     }
0711     if (m_consumer->is_valid()) {
0712         // Connect the producer to the consumer - tell it to "run" later
0713         if (m_producer) {
0714             m_consumer->connect(*m_producer.get());
0715             // m_producer->set_speed(0.0);
0716         }
0717 
0718         int dropFrames = 1;
0719         if (!KdenliveSettings::monitor_dropframes()) {
0720             dropFrames = -dropFrames;
0721         }
0722         m_consumer->set("real_time", dropFrames);
0723         m_consumer->set("channels", pCore->audioChannels());
0724         if (KdenliveSettings::previewScaling() > 1) {
0725             m_consumer->set("scale", 1.0 / KdenliveSettings::previewScaling());
0726         }
0727         // C & D
0728         if (m_glslManager) {
0729             if (!m_threadStartEvent) {
0730                 m_threadStartEvent.reset(m_consumer->listen("consumer-thread-started", this, mlt_listener(onThreadStarted)));
0731             }
0732             if (!m_threadStopEvent) {
0733                 m_threadStopEvent.reset(m_consumer->listen("consumer-thread-stopped", this, mlt_listener(onThreadStopped)));
0734             }
0735             if (!serviceName.startsWith(QLatin1String("decklink"))) {
0736                 m_consumer->set("mlt_image_format", "glsl");
0737             }
0738         } else {
0739             // A & B
0740             m_consumer->set("mlt_image_format", "yuv422");
0741         }
0742         m_displayEvent.reset(m_consumer->listen("consumer-frame-show", this, mlt_listener(on_frame_show)));
0743 
0744         int volume = KdenliveSettings::volume();
0745         if (serviceName.startsWith(QLatin1String("sdl"))) {
0746             QString audioDevice = KdenliveSettings::audiodevicename();
0747             if (!audioDevice.isEmpty()) {
0748                 m_consumer->set("audio_device", audioDevice.toUtf8().constData());
0749             }
0750 
0751             QString audioDriver = KdenliveSettings::audiodrivername();
0752             if (!audioDriver.isEmpty()) {
0753                 m_consumer->set("audio_driver", audioDriver.toUtf8().constData());
0754             }
0755         }
0756         if (!pCore->getProjectProfile().progressive()) {
0757             m_consumer->set("progressive", KdenliveSettings::monitor_progressive());
0758         }
0759         m_consumer->set("volume", volume / 100.0);
0760         // m_consumer->set("progressive", 1);
0761         m_consumer->set("rescale", KdenliveSettings::mltinterpolation().toUtf8().constData());
0762         m_consumer->set("deinterlacer", KdenliveSettings::mltdeinterlacer().toUtf8().constData());
0763         /*
0764 #ifdef Q_OS_WIN
0765         m_consumer->set("audio_buffer", 2048);
0766 #else
0767         m_consumer->set("audio_buffer", 512);
0768 #endif
0769         */
0770         int fps = qRound(pCore->getCurrentFps());
0771         m_consumer->set("buffer", qMax(25, fps));
0772         m_consumer->set("prefill", 6);
0773         m_consumer->set("drop_max", fps / 4);
0774         if (KdenliveSettings::audio_scrub()) {
0775             m_consumer->set("scrub_audio", 1);
0776         } else {
0777             m_consumer->set("scrub_audio", 0);
0778         }
0779         if (KdenliveSettings::monitor_gamma() == 0) {
0780             m_consumer->set("color_trc", "iec61966_2_1");
0781         } else {
0782             m_consumer->set("color_trc", "bt709");
0783         }
0784     } else {
0785         // Cleanup on error
0786         error = 2;
0787     }
0788     return error;
0789 }
0790 
0791 float VideoWidget::zoom() const
0792 {
0793     return m_zoom;
0794 }
0795 
0796 void VideoWidget::reloadProfile()
0797 {
0798     // The profile display aspect ratio may have changed.
0799     bool existingConsumer = false;
0800     if (m_consumer) {
0801         // Make sure to delete and rebuild consumer to match profile
0802         m_consumer->purge();
0803         m_consumer->stop();
0804         m_consumer.reset();
0805         existingConsumer = true;
0806     }
0807     m_blackClip.reset(new Mlt::Producer(pCore->getProjectProfile(), "color:0"));
0808     m_blackClip->set("kdenlive:id", "black");
0809     m_blackClip->set("mlt_image_format", "rgba");
0810     if (existingConsumer) {
0811         reconfigure();
0812     }
0813     resizeVideo(width(), height());
0814     refreshSceneLayout();
0815 }
0816 
0817 QSize VideoWidget::profileSize() const
0818 {
0819     return m_profileSize;
0820 }
0821 
0822 QRect VideoWidget::displayRect() const
0823 {
0824     return m_rect;
0825 }
0826 
0827 QPoint VideoWidget::offset() const
0828 {
0829     return {m_offset.x() - static_cast<int>(width() * m_zoom / 2), m_offset.y() - static_cast<int>(height() * m_zoom / 2)};
0830 }
0831 
0832 void VideoWidget::setZoom(float zoom, bool force)
0833 {
0834     if (!force && m_zoom == zoom) {
0835         return;
0836     }
0837     double zoomRatio = double(zoom / m_zoom);
0838     m_zoom = zoom;
0839     Q_EMIT zoomChanged(zoomRatio);
0840     if (rootObject()) {
0841         rootObject()->setProperty("zoom", m_zoom);
0842         double scalex = rootObject()->property("scalex").toDouble() * zoomRatio;
0843         rootObject()->setProperty("scalex", scalex);
0844         double scaley = rootObject()->property("scaley").toDouble() * zoomRatio;
0845         rootObject()->setProperty("scaley", scaley);
0846     }
0847     resizeVideo(width(), height());
0848 }
0849 
0850 void VideoWidget::onFrameDisplayed(const SharedFrame &frame)
0851 {
0852     m_mutex.lock();
0853     m_sharedFrame = frame;
0854     m_sendFrame = sendFrameForAnalysis;
0855     m_mutex.unlock();
0856     quickWindow()->update();
0857 }
0858 
0859 void VideoWidget::purgeCache()
0860 {
0861     if (m_consumer) {
0862         // m_consumer->set("buffer", 1);
0863         m_consumer->purge();
0864         m_producer->seek(m_proxy->getPosition() + 1);
0865     }
0866 }
0867 
0868 void VideoWidget::mouseDoubleClickEvent(QMouseEvent *event)
0869 {
0870     QQuickWidget::mouseDoubleClickEvent(event);
0871     if (event->isAccepted()) {
0872         return;
0873     }
0874     if ((rootObject() == nullptr) || rootObject()->objectName() != QLatin1String("rooteffectscene")) {
0875         Q_EMIT switchFullScreen();
0876     }
0877     event->accept();
0878 }
0879 
0880 void VideoWidget::setOffsetX(int x, int max)
0881 {
0882     m_offset.setX(x);
0883     if (rootObject()) {
0884         rootObject()->setProperty("offsetx", m_zoom > 1.0f ? x - max / 2.0f + 10 * m_zoom : 0);
0885     }
0886     quickWindow()->update();
0887 }
0888 
0889 void VideoWidget::setOffsetY(int y, int max)
0890 {
0891     m_offset.setY(y);
0892     if (rootObject()) {
0893         rootObject()->setProperty("offsety", m_zoom > 1.0f ? y - max / 2.0f + 10 * m_zoom : 0);
0894     }
0895     quickWindow()->update();
0896 }
0897 
0898 std::shared_ptr<Mlt::Consumer> VideoWidget::consumer()
0899 {
0900     return m_consumer;
0901 }
0902 
0903 Mlt::Producer *VideoWidget::producer()
0904 {
0905     return m_producer.get();
0906 }
0907 
0908 void VideoWidget::resetConsumer(bool fullReset)
0909 {
0910     if (fullReset && m_consumer) {
0911         m_consumer->purge();
0912         m_consumer->stop();
0913         m_consumer.reset();
0914     }
0915     reconfigure();
0916 }
0917 
0918 void VideoWidget::on_frame_show(mlt_consumer, VideoWidget *widget, mlt_event_data data)
0919 {
0920     auto frame = Mlt::EventData(data).to_frame();
0921     if (frame.is_valid() && frame.get_int("rendered")) {
0922         int timeout = (widget->consumer()->get_int("real_time") > 0) ? 0 : 1000;
0923         if ((widget->m_frameRenderer != nullptr) && widget->m_frameRenderer->semaphore()->tryAcquire(1, timeout)) {
0924             QMetaObject::invokeMethod(widget->m_frameRenderer, "showFrame", Qt::QueuedConnection, Q_ARG(Mlt::Frame, frame));
0925         }
0926     }
0927 }
0928 
0929 RenderThread::RenderThread(thread_function_t function, void *data)
0930     : QThread(nullptr)
0931     , m_function(function)
0932     , m_data(data)
0933     , m_context{new QOpenGLContext}
0934     , m_surface{new QOffscreenSurface}
0935 {
0936     QSurfaceFormat format;
0937     format.setProfile(QSurfaceFormat::CoreProfile);
0938     format.setMajorVersion(3);
0939     format.setMinorVersion(2);
0940     format.setDepthBufferSize(0);
0941     format.setStencilBufferSize(0);
0942     m_context->setFormat(format);
0943     m_context->create();
0944     m_context->moveToThread(this);
0945     m_surface->setFormat(format);
0946     m_surface->create();
0947 }
0948 
0949 RenderThread::~RenderThread()
0950 {
0951     m_surface->destroy();
0952 }
0953 
0954 // TODO: missing some exception handling?
0955 void RenderThread::run()
0956 {
0957     Q_ASSERT(m_context->isValid());
0958     m_context->makeCurrent(m_surface.get());
0959     m_function(m_data);
0960     m_context->doneCurrent();
0961 }
0962 
0963 FrameRenderer::FrameRenderer()
0964     : QThread(nullptr)
0965     , m_semaphore(3)
0966     , m_imageRequested(false)
0967 {
0968     setObjectName(QStringLiteral("FrameRenderer"));
0969     moveToThread(this);
0970     start();
0971 }
0972 
0973 FrameRenderer::~FrameRenderer() {}
0974 
0975 void FrameRenderer::requestImage()
0976 {
0977     m_imageRequested = true;
0978 }
0979 
0980 void FrameRenderer::showFrame(Mlt::Frame frame)
0981 {
0982     // Save this frame for future use and to keep a reference to the GL Texture.
0983     m_displayFrame = SharedFrame(frame);
0984     Q_EMIT frameDisplayed(m_displayFrame);
0985     if (m_imageRequested) {
0986         m_imageRequested = false;
0987         Q_EMIT imageReady();
0988     }
0989 
0990     m_semaphore.release();
0991 }
0992 
0993 SharedFrame FrameRenderer::getDisplayFrame()
0994 {
0995     return m_displayFrame;
0996 }
0997 
0998 void VideoWidget::refreshSceneLayout()
0999 {
1000     if (!rootObject()) {
1001         return;
1002     }
1003     QSize s = pCore->getCurrentFrameSize();
1004     Q_EMIT m_proxy->profileChanged();
1005     rootObject()->setProperty("scalex", double(m_rect.width() * m_zoom) / s.width());
1006     rootObject()->setProperty("scaley", double(m_rect.height() * m_zoom) / s.height());
1007 }
1008 
1009 bool VideoWidget::switchPlay(bool play, double speed)
1010 {
1011     if (!m_producer || !m_consumer) {
1012         return false;
1013     }
1014     if (m_isZoneMode || m_isLoopMode) {
1015         resetZoneMode();
1016     }
1017     if (play) {
1018         if (m_consumer->position() >= m_maxProducerPosition && speed > 0) {
1019             // We are at the end of the clip / timeline
1020             if (m_id == Kdenlive::ClipMonitor || (m_id == Kdenlive::ProjectMonitor && KdenliveSettings::jumptostart())) {
1021                 m_producer->seek(0);
1022             } else {
1023                 return false;
1024             }
1025         }
1026         qDebug() << "pos: " << m_consumer->position() << "out: " << m_producer->get_playtime() - 1;
1027         double current_speed = m_producer->get_speed();
1028         m_producer->set_speed(speed);
1029         m_proxy->setSpeed(speed);
1030         if (qFuzzyCompare(speed, 1.0) || speed < -6. || speed > 6.) {
1031             m_consumer->set("scrub_audio", 0);
1032         } else if (KdenliveSettings::audio_scrub()) {
1033             m_consumer->set("scrub_audio", 1);
1034         }
1035         if (qFuzzyIsNull(current_speed)) {
1036             m_consumer->start();
1037             m_consumer->set("refresh", 1);
1038             m_consumer->set("volume", KdenliveSettings::volume() / 100.);
1039         } else {
1040             // Speed change, purge to reduce latency
1041             m_consumer->purge();
1042             m_producer->seek(m_consumer->position() + (speed > 1. ? 1 : 0));
1043         }
1044     } else {
1045         Q_EMIT paused();
1046         m_producer->set_speed(0);
1047         m_consumer->set("volume", 0);
1048         m_proxy->setSpeed(0);
1049         m_producer->seek(m_consumer->position() + 1);
1050         m_consumer->purge();
1051         m_consumer->start();
1052         m_consumer->set("scrub_audio", 0);
1053     }
1054     return true;
1055 }
1056 
1057 bool VideoWidget::playZone(bool loop)
1058 {
1059     if (!m_producer || m_proxy->zoneOut() <= m_proxy->zoneIn()) {
1060         pCore->displayMessage(i18n("Select a zone to play"), ErrorMessage, 500);
1061         return false;
1062     }
1063     double current_speed = m_producer->get_speed();
1064     m_producer->set_speed(0);
1065     m_proxy->setSpeed(0);
1066     m_loopOut = m_proxy->zoneOut();
1067     m_loopIn = m_proxy->zoneIn();
1068     if (qFuzzyIsNull(current_speed)) {
1069         m_producer->seek(m_proxy->zoneIn());
1070         m_consumer->start();
1071         m_producer->set_speed(1.0);
1072         m_consumer->set("scrub_audio", 0);
1073         m_consumer->set("refresh", 1);
1074         m_consumer->set("volume", KdenliveSettings::volume() / 100.);
1075     } else {
1076         // Speed change, purge to reduce latency
1077         m_consumer->set("refresh", 0);
1078         m_producer->seek(m_loopIn);
1079         m_consumer->purge();
1080         m_producer->set_speed(1.0);
1081         m_consumer->set("refresh", 1);
1082     }
1083     m_isZoneMode = true;
1084     m_isLoopMode = loop;
1085     return true;
1086 }
1087 
1088 bool VideoWidget::restartConsumer()
1089 {
1090     int result = 0;
1091     if (m_consumer->is_stopped()) {
1092         // When restarting the consumer, we need to restore the preview scaling
1093         int cWidth = m_consumer->get_int("width");
1094         int cHeigth = m_consumer->get_int("height");
1095         result = m_consumer->start();
1096         if (cWidth > 0) {
1097             m_consumer->set("width", cWidth);
1098             m_consumer->set("height", cHeigth);
1099         }
1100     }
1101     return result != -1;
1102 }
1103 
1104 bool VideoWidget::loopClip(QPoint inOut)
1105 {
1106     if (!m_producer || inOut.y() <= inOut.x()) {
1107         pCore->displayMessage(i18n("Select a clip to play"), ErrorMessage, 500);
1108         return false;
1109     }
1110     m_loopIn = inOut.x();
1111     double current_speed = m_producer->get_speed();
1112     m_producer->set_speed(0);
1113     m_proxy->setSpeed(0);
1114     m_loopOut = inOut.y();
1115     if (qFuzzyIsNull(current_speed)) {
1116         m_producer->seek(m_loopIn);
1117         m_consumer->start();
1118         m_consumer->set("scrub_audio", 0);
1119         m_consumer->set("refresh", 1);
1120         m_consumer->set("volume", KdenliveSettings::volume() / 100.);
1121     } else {
1122         // Speed change, purge to reduce latency
1123         m_consumer->set("refresh", 0);
1124         m_consumer->purge();
1125         m_producer->seek(m_loopIn);
1126         m_producer->set_speed(1.0);
1127         m_consumer->set("refresh", 1);
1128     }
1129     m_isZoneMode = false;
1130     m_isLoopMode = true;
1131     return true;
1132 }
1133 
1134 void VideoWidget::resetZoneMode()
1135 {
1136     if (!m_isZoneMode && !m_isLoopMode) {
1137         return;
1138     }
1139     m_loopIn = 0;
1140     m_loopOut = 0;
1141     m_isZoneMode = false;
1142     m_isLoopMode = false;
1143 }
1144 
1145 MonitorProxy *VideoWidget::getControllerProxy()
1146 {
1147     return m_proxy;
1148 }
1149 
1150 int VideoWidget::getCurrentPos() const
1151 {
1152     return m_proxy->getPosition();
1153 }
1154 
1155 void VideoWidget::setRulerInfo(int duration, const std::shared_ptr<MarkerSortModel> &model)
1156 {
1157     m_maxProducerPosition = duration;
1158     rootObject()->setProperty("duration", duration);
1159     if (model != nullptr) {
1160         // we are resetting marker/snap model, reset zone
1161         rootContext()->setContextProperty("markersModel", model.get());
1162     }
1163 }
1164 
1165 void VideoWidget::switchRecordState(bool on)
1166 {
1167     if (on) {
1168         if (m_maxProducerPosition == 0x7fffffff) {
1169             // We are already in rec mode
1170             return;
1171         }
1172         m_bckpMax = m_maxProducerPosition;
1173         m_maxProducerPosition = 0x7fffffff;
1174     } else {
1175         m_maxProducerPosition = m_bckpMax;
1176     }
1177 }
1178 
1179 void VideoWidget::startConsumer()
1180 {
1181     if (m_consumer == nullptr) {
1182         return;
1183     }
1184     if (!restartConsumer()) {
1185         // ARGH CONSUMER BROKEN!!!!
1186         KMessageBox::error(
1187             qApp->activeWindow(),
1188             i18n("Could not create the video preview window.\nThere is something wrong with your Kdenlive install or your driver settings, please fix it."));
1189         m_displayEvent.reset();
1190         m_consumer.reset();
1191         return;
1192     }
1193     m_consumer->set("refresh", 1);
1194 }
1195 
1196 void VideoWidget::stop()
1197 {
1198     m_refreshTimer.stop();
1199     // why this lock?
1200     QMutexLocker locker(&m_mltMutex);
1201     if (m_producer) {
1202         if (m_isZoneMode || m_isLoopMode) {
1203             resetZoneMode();
1204         }
1205         m_producer->set_speed(0.0);
1206         m_proxy->setSpeed(0);
1207     }
1208     if (m_consumer) {
1209         m_consumer->purge();
1210         if (!m_consumer->is_stopped()) {
1211             m_consumer->stop();
1212         }
1213     }
1214 }
1215 
1216 double VideoWidget::playSpeed() const
1217 {
1218     if (m_producer) {
1219         return m_producer->get_speed();
1220     }
1221     return 0.0;
1222 }
1223 
1224 void VideoWidget::restart()
1225 {
1226     // why this lock?
1227     if (m_consumer) {
1228         // Make sure to delete and rebuild consumer to match profile
1229         m_consumer->purge();
1230         m_consumer->stop();
1231         reconfigure();
1232     }
1233 }
1234 
1235 int VideoWidget::volume() const
1236 {
1237     if ((!m_consumer) || (!m_producer)) {
1238         return -1;
1239     }
1240     if (m_consumer->get("mlt_service") == QStringLiteral("multi")) {
1241         return (int(100 * m_consumer->get_double("0.volume")));
1242     }
1243     return (int(100 * m_consumer->get_double("volume")));
1244 }
1245 
1246 void VideoWidget::setVolume(double volume)
1247 {
1248     if (m_consumer) {
1249         if (m_consumer->get("mlt_service") == QStringLiteral("multi")) {
1250             m_consumer->set("0.volume", volume);
1251         } else {
1252             m_consumer->set("volume", volume);
1253         }
1254     }
1255 }
1256 
1257 int VideoWidget::duration() const
1258 {
1259     if (!m_producer) {
1260         return 0;
1261     }
1262     return m_producer->get_playtime();
1263 }
1264 
1265 void VideoWidget::setConsumerProperty(const QString &name, const QString &value)
1266 {
1267     QMutexLocker locker(&m_mltMutex);
1268     if (m_consumer) {
1269         m_consumer->set(name.toUtf8().constData(), value.toUtf8().constData());
1270         if (m_consumer->start() == -1) {
1271             qCWarning(KDENLIVE_LOG) << "ERROR, Cannot start monitor";
1272         }
1273     }
1274 }
1275 
1276 bool VideoWidget::updateScaling()
1277 {
1278     int previewHeight = pCore->getCurrentFrameSize().height();
1279     switch (KdenliveSettings::previewScaling()) {
1280     case 2:
1281         previewHeight = qMin(previewHeight, 720);
1282         break;
1283     case 4:
1284         previewHeight = qMin(previewHeight, 540);
1285         break;
1286     case 8:
1287         previewHeight = qMin(previewHeight, 360);
1288         break;
1289     case 16:
1290         previewHeight = qMin(previewHeight, 270);
1291         break;
1292     default:
1293         break;
1294     }
1295     int pWidth = int(previewHeight * pCore->getCurrentDar() / pCore->getCurrentSar());
1296     if (pWidth % 2 > 0) {
1297         pWidth++;
1298     }
1299     QSize profileSize(pWidth, previewHeight);
1300     if (profileSize == m_profileSize) {
1301         return false;
1302     }
1303     m_profileSize = profileSize;
1304     pCore->getMonitorProfile().set_width(m_profileSize.width());
1305     pCore->getMonitorProfile().set_height(m_profileSize.height());
1306     if (m_consumer) {
1307         m_consumer->set("width", m_profileSize.width());
1308         m_consumer->set("height", m_profileSize.height());
1309         resizeVideo(width(), height());
1310     }
1311     return true;
1312 }
1313 
1314 void VideoWidget::switchRuler(bool show)
1315 {
1316     m_rulerHeight = show ? int(QFontInfo(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)).pixelSize() * 1.5) : 0;
1317     m_displayRulerHeight = m_rulerHeight;
1318     resizeVideo(width(), height());
1319     Q_EMIT m_proxy->rulerHeightChanged();
1320 }