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 }