File indexing completed on 2024-04-21 04:51:47
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 "glwidget.h" 0040 #include "monitorproxy.h" 0041 #include "profiles/profilemodel.hpp" 0042 #include "timeline2/view/qml/timelineitems.h" 0043 #include "timeline2/view/qmltypes/thumbnailprovider.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, QWidget *parent) 0076 : QQuickWidget(parent) 0077 , sendFrameForAnalysis(false) 0078 , m_glslManager(nullptr) 0079 , m_consumer(nullptr) 0080 , m_producer(nullptr) 0081 , m_id(id) 0082 , m_rulerHeight(int(QFontInfo(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)).pixelSize() * 1.5)) 0083 , m_bgColor(KdenliveSettings::window_background()) 0084 , m_shader(nullptr) 0085 , m_initSem(0) 0086 , m_analyseSem(1) 0087 , m_isInitialized(false) 0088 , m_maxProducerPosition(0) 0089 , m_threadStartEvent(nullptr) 0090 , m_threadStopEvent(nullptr) 0091 , m_threadCreateEvent(nullptr) 0092 , m_threadJoinEvent(nullptr) 0093 , m_displayEvent(nullptr) 0094 , m_frameRenderer(nullptr) 0095 , m_projectionLocation(0) 0096 , m_modelViewLocation(0) 0097 , m_vertexLocation(0) 0098 , m_texCoordLocation(0) 0099 , m_colorspaceLocation(0) 0100 , m_zoom(1.0f) 0101 , m_profileSize(1920, 1080) 0102 , m_colorSpace(601) 0103 , m_dar(1.78) 0104 , m_sendFrame(false) 0105 , m_isZoneMode(false) 0106 , m_isLoopMode(false) 0107 , m_loopIn(0) 0108 , m_offset(QPoint(0, 0)) 0109 , m_fbo(nullptr) 0110 , m_shareContext(nullptr) 0111 , m_openGLSync(false) 0112 , m_ClientWaitSync(nullptr) 0113 { 0114 #if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) || KDECLARATIVE_VERSION > QT_VERSION_CHECK(5, 98, 0) 0115 engine()->addImageProvider(QStringLiteral("icon"), new KQuickIconProvider); 0116 #else 0117 KDeclarative::KDeclarative kdeclarative; 0118 kdeclarative.setDeclarativeEngine(engine()); 0119 kdeclarative.setupEngine(engine()); 0120 #endif 0121 engine()->rootContext()->setContextObject(new KLocalizedContext(this)); 0122 0123 m_texture[0] = m_texture[1] = m_texture[2] = 0; 0124 qRegisterMetaType<Mlt::Frame>("Mlt::Frame"); 0125 qRegisterMetaType<SharedFrame>("SharedFrame"); 0126 setAcceptDrops(true); 0127 0128 if (m_id == Kdenlive::ClipMonitor && !(KdenliveSettings::displayClipMonitorInfo() & 0x01)) { 0129 m_rulerHeight = 0; 0130 } else if (!(KdenliveSettings::displayProjectMonitorInfo() & 0x01)) { 0131 m_rulerHeight = 0; 0132 } 0133 m_displayRulerHeight = m_rulerHeight; 0134 0135 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0136 quickWindow()->setPersistentOpenGLContext(true); 0137 quickWindow()->setPersistentSceneGraph(true); 0138 quickWindow()->setClearBeforeRendering(false); 0139 #else 0140 // TODO: qt6 0141 quickWindow()->setPersistentGraphics(true); 0142 quickWindow()->setPersistentSceneGraph(true); 0143 //quickWindow()->setClearBeforeRendering(false); 0144 #endif 0145 setResizeMode(QQuickWidget::SizeRootObjectToView); 0146 auto fmt = QOpenGLContext::globalShareContext()->format(); 0147 fmt.setDepthBufferSize(format().depthBufferSize()); 0148 fmt.setStencilBufferSize(format().stencilBufferSize()); 0149 m_offscreenSurface.setFormat(fmt); 0150 m_offscreenSurface.create(); 0151 0152 m_refreshTimer.setSingleShot(true); 0153 m_refreshTimer.setInterval(10); 0154 m_blackClip.reset(new Mlt::Producer(pCore->getProjectProfile(), "color:0")); 0155 m_blackClip->set("mlt_image_format", "rgba"); 0156 m_blackClip->set("kdenlive:id", "black"); 0157 m_blackClip->set("out", 3); 0158 connect(&m_refreshTimer, &QTimer::timeout, this, &VideoWidget::refresh); 0159 m_producer = m_blackClip; 0160 rootContext()->setContextProperty("markersModel", nullptr); 0161 if (!initGPUAccel()) { 0162 disableGPUAccel(); 0163 } 0164 0165 connect(quickWindow(), &QQuickWindow::sceneGraphInitialized, this, &VideoWidget::initializeGL, Qt::DirectConnection); 0166 connect(quickWindow(), &QQuickWindow::beforeRendering, this, &VideoWidget::paintGL, Qt::DirectConnection); 0167 // connect(pCore.get(), &Core::updateMonitorProfile, this, &VideoWidget::reloadProfile); 0168 connect(pCore.get(), &Core::switchTimelineRecord, this, &VideoWidget::switchRecordState); 0169 0170 registerTimelineItems(); 0171 m_proxy = new MonitorProxy(this); 0172 rootContext()->setContextProperty("controller", m_proxy); 0173 engine()->addImageProvider(QStringLiteral("thumbnail"), new ThumbnailProvider); 0174 } 0175 0176 VideoWidget::~VideoWidget() 0177 { 0178 // C & D 0179 delete m_glslManager; 0180 delete m_threadStartEvent; 0181 delete m_threadStopEvent; 0182 delete m_threadCreateEvent; 0183 delete m_threadJoinEvent; 0184 delete m_displayEvent; 0185 if (m_frameRenderer) { 0186 if (m_frameRenderer->isRunning()) { 0187 QMetaObject::invokeMethod(m_frameRenderer, "cleanup"); 0188 m_frameRenderer->quit(); 0189 m_frameRenderer->wait(); 0190 m_frameRenderer->deleteLater(); 0191 } else { 0192 delete m_frameRenderer; 0193 } 0194 } 0195 m_blackClip.reset(); 0196 delete m_shareContext; 0197 delete m_shader; 0198 // delete pCore->getCurrentProfile(); 0199 } 0200 0201 void VideoWidget::updateAudioForAnalysis() 0202 { 0203 if (m_frameRenderer) { 0204 m_frameRenderer->sendAudioForAnalysis = KdenliveSettings::monitor_audio(); 0205 } 0206 } 0207 0208 void VideoWidget::initializeGL() 0209 { 0210 if (m_isInitialized) return; 0211 0212 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0213 quickWindow()->openglContext()->makeCurrent(&m_offscreenSurface); 0214 #else 0215 QOpenGLContext &context = *static_cast< QOpenGLContext *>(quickWindow()->rendererInterface()->getResource(quickWindow(), QSGRendererInterface::OpenGLContextResource)); 0216 context.makeCurrent(&m_offscreenSurface); 0217 #endif 0218 initializeOpenGLFunctions(); 0219 0220 // C & D 0221 if (onlyGLESGPUAccel()) { 0222 disableGPUAccel(); 0223 } 0224 0225 createShader(); 0226 0227 m_openGLSync = initGPUAccelSync(); 0228 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0229 quickWindow()->openglContext()->doneCurrent(); 0230 #else 0231 context.doneCurrent(); 0232 #endif 0233 0234 // C & D 0235 if (m_glslManager) { 0236 // Create a context sharing with this context for the RenderThread context. 0237 // This is needed because openglContext() is active in another thread 0238 // at the time that RenderThread is created. 0239 // See this Qt bug for more info: https://bugreports.qt.io/browse/QTBUG-44677 0240 // TODO: QTBUG-44677 is closed. still applicable? 0241 m_shareContext = new QOpenGLContext; 0242 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0243 m_shareContext->setFormat(quickWindow()->openglContext()->format()); 0244 m_shareContext->setShareContext(quickWindow()->openglContext()); 0245 #else 0246 m_shareContext->setFormat(context.format()); 0247 m_shareContext->setShareContext(&context); 0248 #endif 0249 m_shareContext->create(); 0250 } 0251 0252 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0253 m_frameRenderer = new FrameRenderer(quickWindow()->openglContext(), &m_offscreenSurface, m_ClientWaitSync); 0254 #else 0255 m_frameRenderer = new FrameRenderer(&context, &m_offscreenSurface, m_ClientWaitSync); 0256 #endif 0257 0258 m_frameRenderer->sendAudioForAnalysis = KdenliveSettings::monitor_audio(); 0259 0260 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0261 quickWindow()->openglContext()->makeCurrent(quickWindow()); 0262 #else 0263 context.makeCurrent(quickWindow()); 0264 #endif 0265 connect(m_frameRenderer, &FrameRenderer::frameDisplayed, this, &VideoWidget::onFrameDisplayed, Qt::QueuedConnection); 0266 connect(m_frameRenderer, &FrameRenderer::frameDisplayed, this, &VideoWidget::frameDisplayed, Qt::QueuedConnection); 0267 connect(m_frameRenderer, &FrameRenderer::textureReady, this, &VideoWidget::updateTexture, Qt::DirectConnection); 0268 m_initSem.release(); 0269 m_isInitialized = true; 0270 QMetaObject::invokeMethod(this, "reconfigure", Qt::QueuedConnection); 0271 } 0272 0273 void VideoWidget::resizeGL(int width, int height) 0274 { 0275 int x, y, w, h; 0276 height -= m_displayRulerHeight; 0277 double this_aspect = double(width) / height; 0278 0279 // Special case optimization to negate odd effect of sample aspect ratio 0280 // not corresponding exactly with image resolution. 0281 if (int(this_aspect * 1000) == int(m_dar * 1000)) { 0282 w = width; 0283 h = height; 0284 } 0285 // Use OpenGL to normalise sample aspect ratio 0286 else if (height * m_dar > width) { 0287 w = width; 0288 h = int(width / m_dar); 0289 } else { 0290 w = int(height * m_dar); 0291 h = height; 0292 } 0293 x = (width - w) / 2; 0294 y = (height - h) / 2; 0295 m_rect.setRect(x, y, w, h); 0296 QQuickItem *rootQml = rootObject(); 0297 if (rootQml) { 0298 QSize s = pCore->getCurrentFrameSize(); 0299 double scalex = double(m_rect.width() * m_zoom) / s.width(); 0300 double scaley = double(m_rect.height() * m_zoom) / s.height(); 0301 rootQml->setProperty("center", m_rect.center()); 0302 rootQml->setProperty("scalex", scalex); 0303 rootQml->setProperty("scaley", scaley); 0304 if (rootQml->objectName() == QLatin1String("rootsplit")) { 0305 // Adjust splitter pos 0306 rootQml->setProperty("splitterPos", x + (rootQml->property("percentage").toDouble() * w)); 0307 } 0308 } 0309 Q_EMIT rectChanged(); 0310 } 0311 0312 void VideoWidget::resizeEvent(QResizeEvent *event) 0313 { 0314 QQuickWidget::resizeEvent(event); 0315 resizeGL(event->size().width(), event->size().height()); 0316 } 0317 0318 void VideoWidget::createGPUAccelFragmentProg() 0319 { 0320 m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment, "uniform sampler2D tex;" 0321 "varying highp vec2 coordinates;" 0322 "void main(void) {" 0323 " gl_FragColor = texture2D(tex, coordinates);" 0324 "}"); 0325 m_shader->link(); 0326 m_textureLocation[0] = m_shader->uniformLocation("tex"); 0327 } 0328 0329 void VideoWidget::createShader() 0330 { 0331 m_shader = new QOpenGLShaderProgram; 0332 m_shader->addShaderFromSourceCode(QOpenGLShader::Vertex, "uniform highp mat4 projection;" 0333 "uniform highp mat4 modelView;" 0334 "attribute highp vec4 vertex;" 0335 "attribute highp vec2 texCoord;" 0336 "varying highp vec2 coordinates;" 0337 "void main(void) {" 0338 " gl_Position = projection * modelView * vertex;" 0339 " coordinates = texCoord;" 0340 "}"); 0341 // C & D 0342 if (m_glslManager) { 0343 createGPUAccelFragmentProg(); 0344 } else { 0345 // A & B 0346 createYUVTextureProjectFragmentProg(); 0347 } 0348 0349 m_projectionLocation = m_shader->uniformLocation("projection"); 0350 m_modelViewLocation = m_shader->uniformLocation("modelView"); 0351 m_vertexLocation = m_shader->attributeLocation("vertex"); 0352 m_texCoordLocation = m_shader->attributeLocation("texCoord"); 0353 } 0354 0355 void VideoWidget::createYUVTextureProjectFragmentProg() 0356 { 0357 m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment, 0358 "uniform sampler2D Ytex, Utex, Vtex;" 0359 "uniform lowp int colorspace;" 0360 "varying highp vec2 coordinates;" 0361 "void main(void) {" 0362 " mediump vec3 texel;" 0363 " texel.r = texture2D(Ytex, coordinates).r - 16.0/255.0;" // Y 0364 " texel.g = texture2D(Utex, coordinates).r - 128.0/255.0;" // U 0365 " texel.b = texture2D(Vtex, coordinates).r - 128.0/255.0;" // V 0366 " mediump mat3 coefficients;" 0367 " if (colorspace == 601) {" 0368 " coefficients = mat3(" 0369 " 1.1643, 1.1643, 1.1643," // column 1 0370 " 0.0, -0.39173, 2.017," // column 2 0371 " 1.5958, -0.8129, 0.0);" // column 3 0372 " } else {" // ITU-R 709 0373 " coefficients = mat3(" 0374 " 1.1643, 1.1643, 1.1643," // column 1 0375 " 0.0, -0.213, 2.112," // column 2 0376 " 1.793, -0.533, 0.0);" // column 3 0377 " }" 0378 " gl_FragColor = vec4(coefficients * texel, 1.0);" 0379 "}"); 0380 m_shader->link(); 0381 m_textureLocation[0] = m_shader->uniformLocation("Ytex"); 0382 m_textureLocation[1] = m_shader->uniformLocation("Utex"); 0383 m_textureLocation[2] = m_shader->uniformLocation("Vtex"); 0384 m_colorspaceLocation = m_shader->uniformLocation("colorspace"); 0385 } 0386 0387 static void uploadTextures(QOpenGLContext *context, const SharedFrame &frame, GLuint texture[]) 0388 { 0389 int width = frame.get_image_width(); 0390 int height = frame.get_image_height(); 0391 const uint8_t *image = frame.get_image(mlt_image_yuv420p); 0392 QOpenGLFunctions *f = context->functions(); 0393 0394 // The planes of pixel data may not be a multiple of the default 4 bytes. 0395 f->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 0396 0397 // Upload each plane of YUV to a texture. 0398 if (texture[0] != 0u) { 0399 f->glDeleteTextures(3, texture); 0400 } 0401 check_error(f); 0402 f->glGenTextures(3, texture); 0403 check_error(f); 0404 0405 f->glBindTexture(GL_TEXTURE_2D, texture[0]); 0406 check_error(f); 0407 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 0408 check_error(f); 0409 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 0410 check_error(f); 0411 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 0412 check_error(f); 0413 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 0414 check_error(f); 0415 f->glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image); 0416 check_error(f); 0417 0418 f->glBindTexture(GL_TEXTURE_2D, texture[1]); 0419 check_error(f); 0420 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 0421 check_error(f); 0422 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 0423 check_error(f); 0424 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 0425 check_error(f); 0426 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 0427 check_error(f); 0428 f->glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width / 2, height / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image + width * height); 0429 check_error(f); 0430 0431 f->glBindTexture(GL_TEXTURE_2D, texture[2]); 0432 check_error(f); 0433 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 0434 check_error(f); 0435 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 0436 check_error(f); 0437 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 0438 check_error(f); 0439 f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 0440 check_error(f); 0441 f->glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width / 2, height / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image + width * height + width / 2 * height / 2); 0442 check_error(f); 0443 } 0444 0445 void VideoWidget::clear() 0446 { 0447 stopGlsl(); 0448 quickWindow()->update(); 0449 } 0450 0451 void VideoWidget::releaseAnalyse() 0452 { 0453 m_analyseSem.release(); 0454 } 0455 0456 bool VideoWidget::acquireSharedFrameTextures() 0457 { 0458 // A 0459 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0460 if ((m_glslManager == nullptr) && !quickWindow()->openglContext()->supportsThreadedOpenGL()) { 0461 QMutexLocker locker(&m_contextSharedAccess); 0462 if (!m_sharedFrame.is_valid()) { 0463 return false; 0464 } 0465 uploadTextures(quickWindow()->openglContext(), m_sharedFrame, m_texture); 0466 } else 0467 #else 0468 QOpenGLContext &context = *static_cast< QOpenGLContext *>(quickWindow()->rendererInterface()->getResource(quickWindow(), QSGRendererInterface::OpenGLContextResource)); 0469 if ((m_glslManager == nullptr) && !context.supportsThreadedOpenGL()) { 0470 QMutexLocker locker(&m_contextSharedAccess); 0471 if (!m_sharedFrame.is_valid()) { 0472 return false; 0473 } 0474 uploadTextures(&context, m_sharedFrame, m_texture); 0475 } else 0476 #endif 0477 if (m_glslManager) { 0478 // C & D 0479 m_contextSharedAccess.lock(); 0480 if (m_sharedFrame.is_valid()) { 0481 m_texture[0] = *(reinterpret_cast<const GLuint *>(m_sharedFrame.get_image(mlt_image_opengl_texture))); 0482 } 0483 } 0484 0485 if (!m_texture[0]) { 0486 // C & D 0487 if (m_glslManager) m_contextSharedAccess.unlock(); 0488 return false; 0489 } 0490 0491 return true; 0492 } 0493 0494 void VideoWidget::bindShaderProgram() 0495 { 0496 m_shader->bind(); 0497 0498 // C & D 0499 if (m_glslManager) { 0500 m_shader->setUniformValue(m_textureLocation[0], 0); 0501 } else { 0502 // A & B 0503 m_shader->setUniformValue(m_textureLocation[0], 0); 0504 m_shader->setUniformValue(m_textureLocation[1], 1); 0505 m_shader->setUniformValue(m_textureLocation[2], 2); 0506 m_shader->setUniformValue(m_colorspaceLocation, m_colorSpace); 0507 } 0508 } 0509 0510 void VideoWidget::releaseSharedFrameTextures() 0511 { 0512 // C & D 0513 if (m_glslManager) { 0514 glFinish(); 0515 m_contextSharedAccess.unlock(); 0516 } 0517 } 0518 0519 bool VideoWidget::initGPUAccel() 0520 { 0521 if (!KdenliveSettings::gpu_accel()) return false; 0522 0523 m_glslManager = new Mlt::Filter(pCore->getProjectProfile(), "glsl.manager"); 0524 return m_glslManager->is_valid(); 0525 } 0526 0527 // C & D 0528 // TODO: insure safe, idempotent on all pipelines. 0529 void VideoWidget::disableGPUAccel() 0530 { 0531 delete m_glslManager; 0532 m_glslManager = nullptr; 0533 KdenliveSettings::setGpu_accel(false); 0534 // Need to destroy MLT global reference to prevent filters from trying to use GPU. 0535 mlt_properties_set_data(mlt_global_properties(), "glslManager", nullptr, 0, nullptr, nullptr); 0536 Q_EMIT gpuNotSupported(); 0537 } 0538 0539 bool VideoWidget::onlyGLESGPUAccel() const 0540 { 0541 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0542 return (m_glslManager != nullptr) && quickWindow()->openglContext()->isOpenGLES(); 0543 #else 0544 QOpenGLContext &context = *static_cast< QOpenGLContext *>(quickWindow()->rendererInterface()->getResource(quickWindow(), QSGRendererInterface::OpenGLContextResource)); 0545 return (m_glslManager != nullptr) && context.isOpenGLES(); 0546 #endif 0547 } 0548 0549 #if defined(Q_OS_WIN) 0550 bool VideoWidget::initGPUAccelSync() 0551 { 0552 // no-op 0553 // TODO: getProcAddress is not working on Windows? 0554 return false; 0555 } 0556 #else 0557 bool VideoWidget::initGPUAccelSync() 0558 { 0559 if (!KdenliveSettings::gpu_accel()) return false; 0560 if (m_glslManager == nullptr) return false; 0561 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0562 if (!quickWindow()->openglContext()->hasExtension("GL_ARB_sync")) return false; 0563 0564 m_ClientWaitSync = ClientWaitSync_fp(quickWindow()->openglContext()->getProcAddress("glClientWaitSync")); 0565 #else 0566 QOpenGLContext &context = *static_cast< QOpenGLContext *>(quickWindow()->rendererInterface()->getResource(quickWindow(), QSGRendererInterface::OpenGLContextResource)); 0567 if (!context.hasExtension("GL_ARB_sync")) return false; 0568 0569 m_ClientWaitSync = ClientWaitSync_fp(context.getProcAddress("glClientWaitSync")); 0570 #endif 0571 if (m_ClientWaitSync) { 0572 return true; 0573 } else { 0574 qWarning() << "no GL sync"; 0575 // fallback on A || B 0576 // TODO: fallback on A || B || C? 0577 disableGPUAccel(); 0578 return false; 0579 } 0580 } 0581 #endif 0582 0583 void VideoWidget::paintGL() 0584 { 0585 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0586 QOpenGLFunctions *f = quickWindow()->openglContext()->functions(); 0587 #else 0588 QOpenGLContext &context = *static_cast< QOpenGLContext *>(quickWindow()->rendererInterface()->getResource(quickWindow(), QSGRendererInterface::OpenGLContextResource)); 0589 QOpenGLFunctions *f = context.functions(); 0590 #endif 0591 0592 float width = this->width() * devicePixelRatioF(); 0593 float height = this->height() * devicePixelRatioF(); 0594 f->glClearColor(float(m_bgColor.redF()), float(m_bgColor.greenF()), float(m_bgColor.blueF()), 1); 0595 f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 0596 f->glDisable(GL_BLEND); 0597 f->glDisable(GL_DEPTH_TEST); 0598 f->glDepthMask(GL_FALSE); 0599 f->glViewport(0, qRound(m_displayRulerHeight * devicePixelRatioF() * 0.5), int(width), int(height)); 0600 check_error(f); 0601 0602 if (!acquireSharedFrameTextures()) return; 0603 0604 // Bind textures. 0605 for (uint i = 0; i < 3; ++i) { 0606 if (m_texture[i] != 0u) { 0607 f->glActiveTexture(GL_TEXTURE0 + i); 0608 f->glBindTexture(GL_TEXTURE_2D, m_texture[i]); 0609 check_error(f); 0610 } 0611 } 0612 0613 bindShaderProgram(); 0614 check_error(f); 0615 0616 // Setup an orthographic projection. 0617 QMatrix4x4 projection; 0618 projection.scale(2.0f / width, 2.0f / height); 0619 m_shader->setUniformValue(m_projectionLocation, projection); 0620 check_error(f); 0621 0622 // Set model view. 0623 QMatrix4x4 modelView; 0624 if (!qFuzzyCompare(m_zoom, 1.0f)) { 0625 if ((offset().x() != 0) || (offset().y() != 0)) modelView.translate(-offset().x() * devicePixelRatioF(), offset().y() * devicePixelRatioF()); 0626 modelView.scale(zoom(), zoom()); 0627 } 0628 m_shader->setUniformValue(m_modelViewLocation, modelView); 0629 check_error(f); 0630 0631 // Provide vertices of triangle strip. 0632 QVector<QVector2D> vertices; 0633 width = m_rect.width() * devicePixelRatioF(); 0634 height = m_rect.height() * devicePixelRatioF(); 0635 vertices << QVector2D(-width / 2.0f, -height / 2.0f); 0636 vertices << QVector2D(-width / 2.0f, height / 2.0f); 0637 vertices << QVector2D(width / 2.0f, -height / 2.0f); 0638 vertices << QVector2D(width / 2.0f, height / 2.0f); 0639 m_shader->enableAttributeArray(m_vertexLocation); 0640 check_error(f); 0641 m_shader->setAttributeArray(m_vertexLocation, vertices.constData()); 0642 check_error(f); 0643 0644 // Provide texture coordinates. 0645 QVector<QVector2D> texCoord; 0646 texCoord << QVector2D(0.0f, 1.0f); 0647 texCoord << QVector2D(0.0f, 0.0f); 0648 texCoord << QVector2D(1.0f, 1.0f); 0649 texCoord << QVector2D(1.0f, 0.0f); 0650 m_shader->enableAttributeArray(m_texCoordLocation); 0651 check_error(f); 0652 m_shader->setAttributeArray(m_texCoordLocation, texCoord.constData()); 0653 check_error(f); 0654 0655 // Render 0656 glDrawArrays(GL_TRIANGLE_STRIP, 0, vertices.size()); 0657 check_error(f); 0658 0659 if (m_sendFrame && m_analyseSem.tryAcquire(1)) { 0660 // Render RGB frame for analysis 0661 if (!qFuzzyCompare(m_zoom, 1.0f)) { 0662 // Disable monitor zoom to render frame 0663 modelView = QMatrix4x4(); 0664 m_shader->setUniformValue(m_modelViewLocation, modelView); 0665 } 0666 if ((m_fbo == nullptr) || m_fbo->size() != m_profileSize) { 0667 delete m_fbo; 0668 QOpenGLFramebufferObjectFormat fmt; 0669 fmt.setSamples(1); 0670 m_fbo = new QOpenGLFramebufferObject(m_profileSize.width(), m_profileSize.height(), fmt); // GL_TEXTURE_2D); 0671 } 0672 m_fbo->bind(); 0673 glViewport(0, 0, m_profileSize.width(), m_profileSize.height()); 0674 0675 QMatrix4x4 projection2; 0676 projection2.scale(2.0f / width, 2.0f / height); 0677 m_shader->setUniformValue(m_projectionLocation, projection2); 0678 0679 glDrawArrays(GL_TRIANGLE_STRIP, 0, vertices.size()); 0680 check_error(f); 0681 m_fbo->release(); 0682 Q_EMIT analyseFrame(m_fbo->toImage()); 0683 m_sendFrame = false; 0684 } 0685 // Cleanup 0686 m_shader->disableAttributeArray(m_vertexLocation); 0687 m_shader->disableAttributeArray(m_texCoordLocation); 0688 m_shader->release(); 0689 for (uint i = 0; i < 3; ++i) { 0690 if (m_texture[i] != 0u) { 0691 f->glActiveTexture(GL_TEXTURE0 + i); 0692 f->glBindTexture(GL_TEXTURE_2D, 0); 0693 check_error(f); 0694 } 0695 } 0696 glActiveTexture(GL_TEXTURE0); 0697 check_error(f); 0698 0699 releaseSharedFrameTextures(); 0700 check_error(f); 0701 } 0702 0703 void VideoWidget::slotZoom(bool zoomIn) 0704 { 0705 if (zoomIn) { 0706 if (m_zoom < 12.0f) { 0707 setZoom(m_zoom * 1.2); 0708 } 0709 } else if (m_zoom > 0.2f) { 0710 setZoom(m_zoom / 1.2f); 0711 } 0712 } 0713 0714 void VideoWidget::updateRulerHeight(int addedHeight) 0715 { 0716 m_displayRulerHeight = 0717 m_rulerHeight > 0 ? int(QFontInfo(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)).pixelSize() * 1.5) + addedHeight : 0; 0718 resizeGL(width(), height()); 0719 } 0720 0721 bool VideoWidget::isReady() const 0722 { 0723 return m_consumer != nullptr; 0724 } 0725 0726 void VideoWidget::requestSeek(int position, bool noAudioScrub) 0727 { 0728 m_producer->seek(position); 0729 if (!qFuzzyIsNull(m_producer->get_speed())) { 0730 m_consumer->purge(); 0731 } 0732 restartConsumer(); 0733 m_consumer->set("refresh", 1); 0734 if (KdenliveSettings::audio_scrub() && !noAudioScrub) { 0735 m_consumer->set("scrub_audio", 1); 0736 } else { 0737 m_consumer->set("scrub_audio", 0); 0738 } 0739 } 0740 0741 void VideoWidget::requestRefresh() 0742 { 0743 if (m_producer && qFuzzyIsNull(m_producer->get_speed())) { 0744 m_consumer->set("scrub_audio", 0); 0745 m_refreshTimer.start(); 0746 } 0747 } 0748 0749 QString VideoWidget::frameToTime(int frames) const 0750 { 0751 return m_consumer ? m_consumer->frames_to_time(frames, mlt_time_smpte_df) : QStringLiteral("-"); 0752 } 0753 0754 void VideoWidget::refresh() 0755 { 0756 m_refreshTimer.stop(); 0757 QMutexLocker locker(&m_mltMutex); 0758 if (m_consumer) { 0759 restartConsumer(); 0760 m_consumer->set("refresh", 1); 0761 } 0762 } 0763 0764 bool VideoWidget::checkFrameNumber(int pos, bool isPlaying) 0765 { 0766 const double speed = m_producer->get_speed(); 0767 m_proxy->positionFromConsumer(pos, isPlaying); 0768 if (m_isLoopMode || m_isZoneMode) { 0769 // not sure why we need to check against pos + 1 but otherwise the 0770 // playback shows one frame after the intended out frame 0771 if (isPlaying && pos + 1 >= m_loopOut) { 0772 m_consumer->purge(); 0773 if (!m_isLoopMode) { 0774 // end play zone mode 0775 m_isZoneMode = false; 0776 m_producer->set_speed(0); 0777 m_proxy->setSpeed(0); 0778 m_consumer->set("refresh", 0); 0779 m_proxy->setPosition(m_loopOut); 0780 m_producer->seek(m_loopOut); 0781 m_loopOut = 0; 0782 return false; 0783 } 0784 m_producer->seek(m_isZoneMode ? m_proxy->zoneIn() : m_loopIn); 0785 m_producer->set_speed(1.0); 0786 m_proxy->setSpeed(1.); 0787 m_consumer->set("refresh", 1); 0788 return true; 0789 } 0790 return true; 0791 } else if (isPlaying) { 0792 if (pos > m_maxProducerPosition - 2 && !(speed < 0.)) { 0793 // Playing past last clip, pause 0794 m_producer->set_speed(0); 0795 m_proxy->setSpeed(0); 0796 m_consumer->set("refresh", 0); 0797 m_consumer->purge(); 0798 m_proxy->setPosition(qMax(0, m_maxProducerPosition)); 0799 m_producer->seek(qMax(0, m_maxProducerPosition)); 0800 return false; 0801 } else if (pos <= 0 && speed < 0.) { 0802 // rewinding reached 0, pause 0803 m_producer->set_speed(0); 0804 m_proxy->setSpeed(0); 0805 m_consumer->set("refresh", 0); 0806 m_consumer->purge(); 0807 m_proxy->setPosition(0); 0808 m_producer->seek(0); 0809 return false; 0810 } 0811 } 0812 return isPlaying; 0813 } 0814 0815 void VideoWidget::mousePressEvent(QMouseEvent *event) 0816 { 0817 if ((rootObject() != nullptr) && rootObject()->property("captureRightClick").toBool() && !(event->modifiers() & Qt::ControlModifier) && 0818 !(event->buttons() & Qt::MiddleButton)) { 0819 event->ignore(); 0820 QQuickWidget::mousePressEvent(event); 0821 return; 0822 } 0823 QQuickWidget::mousePressEvent(event); 0824 event->accept(); 0825 if ((event->button() & Qt::LeftButton) != 0u) { 0826 if ((event->modifiers() & Qt::ControlModifier) != 0u) { 0827 // Pan view 0828 m_panStart = event->pos(); 0829 setCursor(Qt::ClosedHandCursor); 0830 } else { 0831 m_dragStart = event->pos(); 0832 } 0833 } else if ((event->button() & Qt::RightButton) != 0u) { 0834 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0835 Q_EMIT showContextMenu(event->globalPos()); 0836 #else 0837 Q_EMIT showContextMenu(event->globalPosition().toPoint()); 0838 #endif 0839 } else if ((event->button() & Qt::MiddleButton) != 0u) { 0840 m_panStart = event->pos(); 0841 setCursor(Qt::ClosedHandCursor); 0842 } 0843 } 0844 0845 void VideoWidget::mouseMoveEvent(QMouseEvent *event) 0846 { 0847 if ((rootObject() != nullptr) && rootObject()->objectName() != QLatin1String("root") && !(event->modifiers() & Qt::ControlModifier) && 0848 !(event->buttons() & Qt::MiddleButton)) { 0849 event->ignore(); 0850 QQuickWidget::mouseMoveEvent(event); 0851 return; 0852 } 0853 QQuickWidget::mouseMoveEvent(event); 0854 if (!(event->buttons() & Qt::LeftButton)) { 0855 event->accept(); 0856 return; 0857 } 0858 if (!m_panStart.isNull()) { 0859 Q_EMIT panView(m_panStart - event->pos()); 0860 m_panStart = event->pos(); 0861 event->accept(); 0862 return; 0863 } 0864 0865 if (!event->isAccepted() && !m_dragStart.isNull() && (event->pos() - m_dragStart).manhattanLength() >= QApplication::startDragDistance()) { 0866 m_dragStart = QPoint(); 0867 Q_EMIT startDrag(); 0868 } 0869 event->accept(); 0870 } 0871 0872 void VideoWidget::keyPressEvent(QKeyEvent *event) 0873 { 0874 QQuickWidget::keyPressEvent(event); 0875 if (!event->isAccepted()) { 0876 Q_EMIT passKeyEvent(event); 0877 } 0878 } 0879 0880 void VideoWidget::createThread(RenderThread **thread, thread_function_t function, void *data) 0881 { 0882 #ifdef Q_OS_WIN 0883 // On Windows, MLT event consumer-thread-create is fired from the Qt main thread. 0884 while (!m_isInitialized) { 0885 qApp->processEvents(); 0886 } 0887 #else 0888 if (!m_isInitialized) { 0889 m_initSem.acquire(); 0890 } 0891 #endif 0892 (*thread) = new RenderThread(function, data, m_shareContext, &m_offscreenSurface); 0893 (*thread)->start(); 0894 } 0895 0896 static void onThreadCreate(mlt_properties owner, VideoWidget *self, mlt_event_data data) 0897 { 0898 Q_UNUSED(owner) 0899 auto threadData = (mlt_event_data_thread *)Mlt::EventData(data).to_object(); 0900 if (threadData) { 0901 auto renderThread = (RenderThread *)threadData->thread; 0902 self->createThread(&renderThread, threadData->function, threadData->data); 0903 // TODO: useless ? 0904 // self->lockMonitor(); 0905 } 0906 } 0907 0908 static void onThreadJoin(mlt_properties owner, VideoWidget *self, mlt_event_data data) 0909 { 0910 Q_UNUSED(owner) 0911 Q_UNUSED(self) 0912 auto threadData = (mlt_event_data_thread *)Mlt::EventData(data).to_object(); 0913 if (threadData) { 0914 auto renderThread = (RenderThread *)threadData->thread; 0915 if (renderThread) { 0916 renderThread->quit(); 0917 renderThread->wait(); 0918 delete renderThread; 0919 // TODO: useless ? 0920 // self->releaseMonitor(); 0921 } 0922 } 0923 } 0924 0925 void VideoWidget::startGlsl() 0926 { 0927 // C & D 0928 if (m_glslManager) { 0929 // clearFrameRenderer(); 0930 m_glslManager->fire_event("init glsl"); 0931 if (m_glslManager->get_int("glsl_supported") == 0) { 0932 disableGPUAccel(); 0933 } else { 0934 Q_EMIT started(); 0935 } 0936 } 0937 } 0938 0939 static void onThreadStarted(mlt_properties owner, VideoWidget *self, mlt_event_data) 0940 { 0941 Q_UNUSED(owner) 0942 self->startGlsl(); 0943 } 0944 0945 void VideoWidget::releaseMonitor() 0946 { 0947 Q_EMIT lockMonitor(false); 0948 } 0949 0950 void VideoWidget::lockMonitor() 0951 { 0952 Q_EMIT lockMonitor(true); 0953 } 0954 0955 void VideoWidget::stopGlsl() 0956 { 0957 if (m_consumer) { 0958 m_consumer->purge(); 0959 } 0960 0961 // C & D 0962 // TODO This is commented out for now because it is causing crashes. 0963 // Technically, this should be the correct thing to do, but it appears 0964 // some changes have created regression (see shotcut) 0965 // with respect to restarting the consumer in GPU mode. 0966 // m_glslManager->fire_event("close glsl"); 0967 m_texture[0] = 0; 0968 } 0969 0970 static void onThreadStopped(mlt_properties owner, VideoWidget *self, mlt_event_data) 0971 { 0972 Q_UNUSED(owner) 0973 self->stopGlsl(); 0974 } 0975 0976 int VideoWidget::setProducer(const QString &file) 0977 { 0978 if (m_producer) { 0979 m_producer.reset(); 0980 } 0981 m_producer = std::make_shared<Mlt::Producer>(new Mlt::Producer(pCore->getProjectProfile(), nullptr, file.toUtf8().constData())); 0982 if (!m_producer || !m_producer->is_valid()) { 0983 m_producer.reset(); 0984 m_producer = m_blackClip; 0985 } 0986 if (m_consumer) { 0987 // m_consumer->stop(); 0988 if (!m_consumer->is_stopped()) { 0989 m_consumer->stop(); 0990 } 0991 } 0992 int error = reconfigure(); 0993 if (error == 0) { 0994 // The profile display aspect ratio may have changed. 0995 resizeGL(width(), height()); 0996 startConsumer(); 0997 } 0998 return error; 0999 } 1000 1001 int VideoWidget::setProducer(const std::shared_ptr<Mlt::Producer> &producer, bool isActive, int position) 1002 { 1003 int error = 0; 1004 QString currentId; 1005 int consumerPosition = 0; 1006 if (m_producer) { 1007 currentId = m_producer->parent().get("kdenlive:id"); 1008 } 1009 if (m_consumer) { 1010 consumerPosition = m_consumer->position(); 1011 } 1012 stop(); 1013 if (producer) { 1014 m_producer = producer; 1015 } else { 1016 if (currentId == QLatin1String("black")) { 1017 return 0; 1018 } 1019 m_producer = m_blackClip; 1020 // Reset markersModel 1021 rootContext()->setContextProperty("markersModel", nullptr); 1022 } 1023 m_producer->set_speed(0); 1024 m_proxy->setSpeed(0); 1025 error = reconfigure(); 1026 if (error == 0) { 1027 // The profile display aspect ratio may have changed. 1028 resizeGL(width(), height()); 1029 } else { 1030 return error; 1031 } 1032 if (!m_consumer) { 1033 return error; 1034 } 1035 if (position == -1 && m_producer->parent().get("kdenlive:id") == currentId) { 1036 position = consumerPosition; 1037 } 1038 if (isActive) { 1039 startConsumer(); 1040 if (position != -2) { 1041 m_proxy->resetPosition(); 1042 } 1043 } 1044 m_consumer->set("scrub_audio", 0); 1045 if (position != -2) { 1046 m_proxy->setPositionAdvanced(position > 0 ? position : m_producer->position(), true); 1047 } 1048 return error; 1049 } 1050 1051 int VideoWidget::droppedFrames() const 1052 { 1053 return (m_consumer ? m_consumer->get_int("drop_count") : 0); 1054 } 1055 1056 void VideoWidget::resetDrops() 1057 { 1058 if (m_consumer) { 1059 m_consumer->set("drop_count", 0); 1060 } 1061 } 1062 1063 void VideoWidget::stopCapture() 1064 { 1065 if (strcmp(m_consumer->get("mlt_service"), "multi") == 0) { 1066 m_consumer->set("refresh", 0); 1067 m_consumer->purge(); 1068 m_consumer->stop(); 1069 } 1070 } 1071 1072 int VideoWidget::reconfigure() 1073 { 1074 int error = 0; 1075 // use SDL for audio, OpenGL for video 1076 QString serviceName = property("mlt_service").toString(); 1077 if ((m_consumer == nullptr) || !m_consumer->is_valid() || strcmp(m_consumer->get("mlt_service"), "multi") == 0) { 1078 if (m_consumer) { 1079 m_consumer->purge(); 1080 m_consumer->stop(); 1081 m_consumer.reset(); 1082 } 1083 QString audioBackend = (KdenliveSettings::external_display()) ? QString("decklink:%1").arg(KdenliveSettings::blackmagic_output_device()) 1084 : KdenliveSettings::audiobackend(); 1085 if (m_consumer == nullptr || serviceName.isEmpty() || serviceName != audioBackend) { 1086 m_consumer.reset(new Mlt::FilteredConsumer(pCore->getMonitorProfile(), audioBackend.toLatin1().constData())); 1087 if (m_consumer->is_valid()) { 1088 serviceName = audioBackend; 1089 } else { 1090 // Warning, audio backend unavailable on system 1091 m_consumer.reset(); 1092 QStringList backends = {"sdl2_audio", "sdl_audio", "rtaudio"}; 1093 for (const QString &bk : backends) { 1094 if (bk == audioBackend) { 1095 // Already tested 1096 continue; 1097 } 1098 m_consumer.reset(new Mlt::FilteredConsumer(pCore->getMonitorProfile(), bk.toLatin1().constData())); 1099 if (m_consumer->is_valid()) { 1100 if (audioBackend == KdenliveSettings::sdlAudioBackend()) { 1101 // switch sdl audio backend 1102 KdenliveSettings::setSdlAudioBackend(bk); 1103 } 1104 KdenliveSettings::setAudiobackend(bk); 1105 serviceName = bk; 1106 break; 1107 } else { 1108 m_consumer.reset(); 1109 } 1110 } 1111 } 1112 if (!m_consumer || !m_consumer->is_valid()) { 1113 qWarning() << "no audio backend found"; 1114 return -1; 1115 } 1116 setProperty("mlt_service", serviceName); 1117 if (KdenliveSettings::external_display()) { 1118 m_consumer->set("terminate_on_pause", 0); 1119 } 1120 m_consumer->set("width", m_profileSize.width()); 1121 m_consumer->set("height", m_profileSize.height()); 1122 m_colorSpace = pCore->getCurrentProfile()->colorspace(); 1123 m_dar = pCore->getCurrentDar(); 1124 } 1125 delete m_threadStartEvent; 1126 m_threadStartEvent = nullptr; 1127 delete m_threadStopEvent; 1128 m_threadStopEvent = nullptr; 1129 1130 delete m_threadCreateEvent; 1131 delete m_threadJoinEvent; 1132 if (m_glslManager) { 1133 m_threadCreateEvent = m_consumer->listen("consumer-thread-create", this, mlt_listener(onThreadCreate)); 1134 m_threadJoinEvent = m_consumer->listen("consumer-thread-join", this, mlt_listener(onThreadJoin)); 1135 } 1136 } 1137 if (m_consumer->is_valid()) { 1138 // Connect the producer to the consumer - tell it to "run" later 1139 if (m_producer) { 1140 m_consumer->connect(*m_producer.get()); 1141 // m_producer->set_speed(0.0); 1142 } 1143 1144 int dropFrames = 1; 1145 if (!KdenliveSettings::monitor_dropframes()) { 1146 dropFrames = -dropFrames; 1147 } 1148 m_consumer->set("real_time", dropFrames); 1149 m_consumer->set("channels", pCore->audioChannels()); 1150 if (KdenliveSettings::previewScaling() > 1) { 1151 m_consumer->set("scale", 1.0 / KdenliveSettings::previewScaling()); 1152 } 1153 // C & D 1154 if (m_glslManager) { 1155 if (!m_threadStartEvent) { 1156 m_threadStartEvent = m_consumer->listen("consumer-thread-started", this, mlt_listener(onThreadStarted)); 1157 } 1158 if (!m_threadStopEvent) { 1159 m_threadStopEvent = m_consumer->listen("consumer-thread-stopped", this, mlt_listener(onThreadStopped)); 1160 } 1161 if (!serviceName.startsWith(QLatin1String("decklink"))) { 1162 m_consumer->set("mlt_image_format", "glsl"); 1163 } 1164 } else { 1165 // A & B 1166 m_consumer->set("mlt_image_format", "yuv422"); 1167 } 1168 1169 delete m_displayEvent; 1170 // C & D 1171 if (m_glslManager) { 1172 m_displayEvent = m_consumer->listen("consumer-frame-show", this, mlt_listener(on_gl_frame_show)); 1173 } else { 1174 // A & B 1175 m_displayEvent = m_consumer->listen("consumer-frame-show", this, mlt_listener(on_frame_show)); 1176 } 1177 1178 int volume = KdenliveSettings::volume(); 1179 if (serviceName.startsWith(QLatin1String("sdl"))) { 1180 QString audioDevice = KdenliveSettings::audiodevicename(); 1181 if (!audioDevice.isEmpty()) { 1182 m_consumer->set("audio_device", audioDevice.toUtf8().constData()); 1183 } 1184 1185 QString audioDriver = KdenliveSettings::audiodrivername(); 1186 if (!audioDriver.isEmpty()) { 1187 m_consumer->set("audio_driver", audioDriver.toUtf8().constData()); 1188 } 1189 } 1190 if (!pCore->getProjectProfile().progressive()) { 1191 m_consumer->set("progressive", KdenliveSettings::monitor_progressive()); 1192 } 1193 m_consumer->set("volume", volume / 100.0); 1194 // m_consumer->set("progressive", 1); 1195 m_consumer->set("rescale", KdenliveSettings::mltinterpolation().toUtf8().constData()); 1196 m_consumer->set("deinterlacer", KdenliveSettings::mltdeinterlacer().toUtf8().constData()); 1197 /* 1198 #ifdef Q_OS_WIN 1199 m_consumer->set("audio_buffer", 2048); 1200 #else 1201 m_consumer->set("audio_buffer", 512); 1202 #endif 1203 */ 1204 int fps = qRound(pCore->getCurrentFps()); 1205 m_consumer->set("buffer", qMax(25, fps)); 1206 m_consumer->set("prefill", 6); 1207 m_consumer->set("drop_max", fps / 4); 1208 if (KdenliveSettings::audio_scrub()) { 1209 m_consumer->set("scrub_audio", 1); 1210 } else { 1211 m_consumer->set("scrub_audio", 0); 1212 } 1213 if (KdenliveSettings::monitor_gamma() == 0) { 1214 m_consumer->set("color_trc", "iec61966_2_1"); 1215 } else { 1216 m_consumer->set("color_trc", "bt709"); 1217 } 1218 } else { 1219 // Cleanup on error 1220 error = 2; 1221 } 1222 return error; 1223 } 1224 1225 float VideoWidget::zoom() const 1226 { 1227 return m_zoom; 1228 } 1229 1230 void VideoWidget::reloadProfile() 1231 { 1232 // The profile display aspect ratio may have changed. 1233 bool existingConsumer = false; 1234 if (m_consumer) { 1235 // Make sure to delete and rebuild consumer to match profile 1236 m_consumer->purge(); 1237 m_consumer->stop(); 1238 m_consumer.reset(); 1239 existingConsumer = true; 1240 } 1241 m_blackClip.reset(new Mlt::Producer(pCore->getProjectProfile(), "color:0")); 1242 m_blackClip->set("kdenlive:id", "black"); 1243 m_blackClip->set("mlt_image_format", "rgba"); 1244 if (existingConsumer) { 1245 reconfigure(); 1246 } 1247 resizeGL(width(), height()); 1248 refreshSceneLayout(); 1249 } 1250 1251 QSize VideoWidget::profileSize() const 1252 { 1253 return m_profileSize; 1254 } 1255 1256 QRect VideoWidget::displayRect() const 1257 { 1258 return m_rect; 1259 } 1260 1261 QPoint VideoWidget::offset() const 1262 { 1263 return {m_offset.x() - static_cast<int>(width() * m_zoom / 2), m_offset.y() - static_cast<int>(height() * m_zoom / 2)}; 1264 } 1265 1266 void VideoWidget::setZoom(float zoom, bool force) 1267 { 1268 if (!force && m_zoom == zoom) { 1269 return; 1270 } 1271 double zoomRatio = double(zoom / m_zoom); 1272 m_zoom = zoom; 1273 Q_EMIT zoomChanged(zoomRatio); 1274 if (rootObject()) { 1275 rootObject()->setProperty("zoom", m_zoom); 1276 double scalex = rootObject()->property("scalex").toDouble() * zoomRatio; 1277 rootObject()->setProperty("scalex", scalex); 1278 double scaley = rootObject()->property("scaley").toDouble() * zoomRatio; 1279 rootObject()->setProperty("scaley", scaley); 1280 } 1281 resizeGL(width(), height()); 1282 } 1283 1284 void VideoWidget::onFrameDisplayed(const SharedFrame &frame) 1285 { 1286 m_contextSharedAccess.lock(); 1287 m_sharedFrame = frame; 1288 m_sendFrame = sendFrameForAnalysis; 1289 m_contextSharedAccess.unlock(); 1290 quickWindow()->update(); 1291 } 1292 1293 void VideoWidget::mouseReleaseEvent(QMouseEvent *event) 1294 { 1295 QQuickWidget::mouseReleaseEvent(event); 1296 /*if (m_dragStart.isNull() && m_panStart.isNull() && (rootObject() != nullptr) && rootObject()->objectName() != QLatin1String("root") && 1297 !(event->modifiers() & Qt::ControlModifier)) { 1298 event->accept(); 1299 qDebug()<<"::::::: MOUSE RELEASED B IGNORED"; 1300 return; 1301 }*/ 1302 if ((event->modifiers() & Qt::ControlModifier)) { 1303 event->accept(); 1304 return; 1305 } 1306 if (!m_dragStart.isNull() && m_panStart.isNull() && ((event->button() & Qt::LeftButton) != 0u) && !event->isAccepted()) { 1307 event->accept(); 1308 Q_EMIT monitorPlay(); 1309 } 1310 m_dragStart = QPoint(); 1311 m_panStart = QPoint(); 1312 setCursor(Qt::ArrowCursor); 1313 } 1314 1315 void VideoWidget::purgeCache() 1316 { 1317 if (m_consumer) { 1318 // m_consumer->set("buffer", 1); 1319 m_consumer->purge(); 1320 m_producer->seek(m_proxy->getPosition() + 1); 1321 } 1322 } 1323 1324 void VideoWidget::mouseDoubleClickEvent(QMouseEvent *event) 1325 { 1326 QQuickWidget::mouseDoubleClickEvent(event); 1327 if (event->isAccepted()) { 1328 return; 1329 } 1330 if ((rootObject() == nullptr) || rootObject()->objectName() != QLatin1String("rooteffectscene")) { 1331 Q_EMIT switchFullScreen(); 1332 } 1333 event->accept(); 1334 } 1335 1336 void VideoWidget::setOffsetX(int x, int max) 1337 { 1338 m_offset.setX(x); 1339 if (rootObject()) { 1340 rootObject()->setProperty("offsetx", m_zoom > 1.0f ? x - max / 2.0f + 10 * m_zoom : 0); 1341 } 1342 quickWindow()->update(); 1343 } 1344 1345 void VideoWidget::setOffsetY(int y, int max) 1346 { 1347 m_offset.setY(y); 1348 if (rootObject()) { 1349 rootObject()->setProperty("offsety", m_zoom > 1.0f ? y - max / 2.0f + 10 * m_zoom : 0); 1350 } 1351 quickWindow()->update(); 1352 } 1353 1354 std::shared_ptr<Mlt::Consumer> VideoWidget::consumer() 1355 { 1356 return m_consumer; 1357 } 1358 1359 Mlt::Producer *VideoWidget::producer() 1360 { 1361 return m_producer.get(); 1362 } 1363 1364 void VideoWidget::resetConsumer(bool fullReset) 1365 { 1366 if (fullReset && m_consumer) { 1367 m_consumer->purge(); 1368 m_consumer->stop(); 1369 m_consumer.reset(); 1370 } 1371 reconfigure(); 1372 } 1373 1374 void VideoWidget::updateTexture(GLuint yName, GLuint uName, GLuint vName) 1375 { 1376 m_texture[0] = yName; 1377 m_texture[1] = uName; 1378 m_texture[2] = vName; 1379 } 1380 1381 void VideoWidget::on_frame_show(mlt_consumer, VideoWidget *widget, mlt_event_data data) 1382 { 1383 auto frame = Mlt::EventData(data).to_frame(); 1384 if (frame.is_valid() && frame.get_int("rendered")) { 1385 int timeout = (widget->consumer()->get_int("real_time") > 0) ? 0 : 1000; 1386 if ((widget->m_frameRenderer != nullptr) && widget->m_frameRenderer->semaphore()->tryAcquire(1, timeout)) { 1387 QMetaObject::invokeMethod(widget->m_frameRenderer, "showFrame", Qt::QueuedConnection, Q_ARG(Mlt::Frame, frame)); 1388 } 1389 } 1390 } 1391 1392 void VideoWidget::on_gl_nosync_frame_show(mlt_consumer, VideoWidget *widget, mlt_event_data data) 1393 { 1394 auto frame = Mlt::EventData(data).to_frame(); 1395 if (frame.get_int("rendered") != 0) { 1396 int timeout = (widget->consumer()->get_int("real_time") > 0) ? 0 : 1000; 1397 if ((widget->m_frameRenderer != nullptr) && widget->m_frameRenderer->semaphore()->tryAcquire(1, timeout)) { 1398 QMetaObject::invokeMethod(widget->m_frameRenderer, "showGLNoSyncFrame", Qt::QueuedConnection, Q_ARG(Mlt::Frame, frame)); 1399 } 1400 } 1401 } 1402 1403 void VideoWidget::on_gl_frame_show(mlt_consumer, VideoWidget *widget, mlt_event_data data) 1404 { 1405 auto frame = Mlt::EventData(data).to_frame(); 1406 if (frame.get_int("rendered") != 0) { 1407 int timeout = (widget->consumer()->get_int("real_time") > 0) ? 0 : 1000; 1408 if ((widget->m_frameRenderer != nullptr) && widget->m_frameRenderer->semaphore()->tryAcquire(1, timeout)) { 1409 QMetaObject::invokeMethod(widget->m_frameRenderer, "showGLFrame", Qt::QueuedConnection, Q_ARG(Mlt::Frame, frame)); 1410 } 1411 } 1412 } 1413 1414 RenderThread::RenderThread(thread_function_t function, void *data, QOpenGLContext *context, QSurface *surface) 1415 : QThread(nullptr) 1416 , m_function(function) 1417 , m_data(data) 1418 , m_context(nullptr) 1419 , m_surface(surface) 1420 { 1421 if (context) { 1422 m_context = new QOpenGLContext; 1423 m_context->setFormat(context->format()); 1424 m_context->setShareContext(context); 1425 m_context->create(); 1426 m_context->moveToThread(this); 1427 } 1428 } 1429 1430 RenderThread::~RenderThread() 1431 { 1432 // would otherwise leak if RenderThread is allocated with a context but not run. 1433 // safe post-run 1434 delete m_context; 1435 } 1436 1437 // TODO: missing some exception handling? 1438 void RenderThread::run() 1439 { 1440 if (m_context) { 1441 m_context->makeCurrent(m_surface); 1442 } 1443 m_function(m_data); 1444 if (m_context) { 1445 m_context->doneCurrent(); 1446 delete m_context; 1447 m_context = nullptr; 1448 } 1449 } 1450 1451 FrameRenderer::FrameRenderer(QOpenGLContext *shareContext, QSurface *surface, VideoWidget::ClientWaitSync_fp clientWaitSync) 1452 : QThread(nullptr) 1453 , m_semaphore(3) 1454 , m_context(nullptr) 1455 , m_surface(surface) 1456 , m_ClientWaitSync(clientWaitSync) 1457 , m_gl32(nullptr) 1458 , sendAudioForAnalysis(false) 1459 { 1460 Q_ASSERT(shareContext); 1461 m_renderTexture[0] = m_renderTexture[1] = m_renderTexture[2] = 0; 1462 m_displayTexture[0] = m_displayTexture[1] = m_displayTexture[2] = 0; 1463 // B & C & D 1464 if (KdenliveSettings::gpu_accel() || shareContext->supportsThreadedOpenGL()) { 1465 m_context = new QOpenGLContext; 1466 m_context->setFormat(shareContext->format()); 1467 m_context->setShareContext(shareContext); 1468 m_context->create(); 1469 m_context->moveToThread(this); 1470 } 1471 setObjectName(QStringLiteral("FrameRenderer")); 1472 moveToThread(this); 1473 start(); 1474 } 1475 1476 FrameRenderer::~FrameRenderer() 1477 { 1478 delete m_context; 1479 delete m_gl32; 1480 } 1481 1482 void FrameRenderer::showFrame(Mlt::Frame frame) 1483 { 1484 // Save this frame for future use and to keep a reference to the GL Texture. 1485 m_displayFrame = SharedFrame(frame); 1486 1487 if ((m_context != nullptr) && m_context->isValid()) { 1488 m_context->makeCurrent(m_surface); 1489 // Upload each plane of YUV to a texture. 1490 QOpenGLFunctions *f = m_context->functions(); 1491 uploadTextures(m_context, m_displayFrame, m_renderTexture); 1492 f->glBindTexture(GL_TEXTURE_2D, 0); 1493 check_error(f); 1494 f->glFinish(); 1495 1496 for (int i = 0; i < 3; ++i) { 1497 std::swap(m_renderTexture[i], m_displayTexture[i]); 1498 } 1499 Q_EMIT textureReady(m_displayTexture[0], m_displayTexture[1], m_displayTexture[2]); 1500 m_context->doneCurrent(); 1501 } 1502 // The frame is now done being modified and can be shared with the rest 1503 // of the application. 1504 Q_EMIT frameDisplayed(m_displayFrame); 1505 m_semaphore.release(); 1506 } 1507 1508 void FrameRenderer::showGLFrame(Mlt::Frame frame) 1509 { 1510 if ((m_context != nullptr) && m_context->isValid()) { 1511 m_context->makeCurrent(m_surface); 1512 pipelineSyncToFrame(frame); 1513 1514 m_context->functions()->glFinish(); 1515 m_context->doneCurrent(); 1516 1517 // Save this frame for future use and to keep a reference to the GL Texture. 1518 m_displayFrame = SharedFrame(frame); 1519 } 1520 // The frame is now done being modified and can be shared with the rest 1521 // of the application. 1522 Q_EMIT frameDisplayed(m_displayFrame); 1523 m_semaphore.release(); 1524 } 1525 1526 void FrameRenderer::showGLNoSyncFrame(Mlt::Frame frame) 1527 { 1528 if ((m_context != nullptr) && m_context->isValid()) { 1529 1530 frame.set("movit.convert.use_texture", 1); 1531 m_context->makeCurrent(m_surface); 1532 m_context->functions()->glFinish(); 1533 1534 m_context->doneCurrent(); 1535 1536 // Save this frame for future use and to keep a reference to the GL Texture. 1537 m_displayFrame = SharedFrame(frame); 1538 } 1539 // The frame is now done being modified and can be shared with the rest 1540 // of the application. 1541 Q_EMIT frameDisplayed(m_displayFrame); 1542 m_semaphore.release(); 1543 } 1544 1545 void FrameRenderer::cleanup() 1546 { 1547 if ((m_renderTexture[0] != 0u) && (m_renderTexture[1] != 0u) && (m_renderTexture[2] != 0u)) { 1548 m_context->makeCurrent(m_surface); 1549 m_context->functions()->glDeleteTextures(3, m_renderTexture); 1550 if ((m_displayTexture[0] != 0u) && (m_displayTexture[1] != 0u) && (m_displayTexture[2] != 0u)) { 1551 m_context->functions()->glDeleteTextures(3, m_displayTexture); 1552 } 1553 m_context->doneCurrent(); 1554 m_renderTexture[0] = m_renderTexture[1] = m_renderTexture[2] = 0; 1555 m_displayTexture[0] = m_displayTexture[1] = m_displayTexture[2] = 0; 1556 } 1557 } 1558 1559 // D 1560 void FrameRenderer::pipelineSyncToFrame(Mlt::Frame &frame) 1561 { 1562 auto sync = GLsync(frame.get_data("movit.convert.fence")); 1563 if (!sync) return; 1564 1565 #ifdef Q_OS_WIN 1566 // On Windows, use QOpenGLFunctions_3_2_Core instead of getProcAddress. 1567 // TODO: move to initialization of m_ClientWaitSync 1568 if (!m_gl32) { 1569 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 1570 // TODO: Qt6 1571 m_gl32 = m_context->versionFunctions<QOpenGLFunctions_3_2_Core>(); 1572 #endif 1573 if (m_gl32) { 1574 m_gl32->initializeOpenGLFunctions(); 1575 } 1576 } 1577 if (m_gl32) { 1578 m_gl32->glClientWaitSync(sync, 0, GL_TIMEOUT_IGNORED); 1579 check_error(m_context->functions()); 1580 } 1581 #else 1582 if (m_ClientWaitSync) { 1583 m_ClientWaitSync(sync, 0, GL_TIMEOUT_IGNORED); 1584 check_error(m_context->functions()); 1585 } 1586 #endif // Q_OS_WIN 1587 } 1588 1589 void VideoWidget::refreshSceneLayout() 1590 { 1591 if (!rootObject()) { 1592 return; 1593 } 1594 QSize s = pCore->getCurrentFrameSize(); 1595 Q_EMIT m_proxy->profileChanged(); 1596 rootObject()->setProperty("scalex", double(m_rect.width() * m_zoom) / s.width()); 1597 rootObject()->setProperty("scaley", double(m_rect.height() * m_zoom) / s.height()); 1598 } 1599 1600 bool VideoWidget::switchPlay(bool play, double speed) 1601 { 1602 if (!m_producer || !m_consumer) { 1603 return false; 1604 } 1605 if (m_isZoneMode || m_isLoopMode) { 1606 resetZoneMode(); 1607 } 1608 if (play) { 1609 if (m_consumer->position() >= m_maxProducerPosition && speed > 0) { 1610 // We are at the end of the clip / timeline 1611 if (m_id == Kdenlive::ClipMonitor || (m_id == Kdenlive::ProjectMonitor && KdenliveSettings::jumptostart())) { 1612 m_producer->seek(0); 1613 } else { 1614 return false; 1615 } 1616 } 1617 qDebug() << "pos: " << m_consumer->position() << "out: " << m_producer->get_playtime() - 1; 1618 double current_speed = m_producer->get_speed(); 1619 m_producer->set_speed(speed); 1620 m_proxy->setSpeed(speed); 1621 if (qFuzzyCompare(speed, 1.0) || speed < -6. || speed > 6.) { 1622 m_consumer->set("scrub_audio", 0); 1623 } else if (KdenliveSettings::audio_scrub()) { 1624 m_consumer->set("scrub_audio", 1); 1625 } 1626 if (qFuzzyIsNull(current_speed)) { 1627 m_consumer->start(); 1628 m_consumer->set("refresh", 1); 1629 m_consumer->set("volume", KdenliveSettings::volume() / 100.); 1630 } else { 1631 // Speed change, purge to reduce latency 1632 m_consumer->purge(); 1633 m_producer->seek(m_consumer->position() + (speed > 1. ? 1 : 0)); 1634 } 1635 } else { 1636 Q_EMIT paused(); 1637 m_producer->set_speed(0); 1638 m_consumer->set("volume", 0); 1639 m_proxy->setSpeed(0); 1640 m_producer->seek(m_consumer->position() + 1); 1641 m_consumer->purge(); 1642 m_consumer->start(); 1643 m_consumer->set("scrub_audio", 0); 1644 } 1645 return true; 1646 } 1647 1648 bool VideoWidget::playZone(bool loop) 1649 { 1650 if (!m_producer || m_proxy->zoneOut() <= m_proxy->zoneIn()) { 1651 pCore->displayMessage(i18n("Select a zone to play"), ErrorMessage, 500); 1652 return false; 1653 } 1654 double current_speed = m_producer->get_speed(); 1655 m_producer->set_speed(0); 1656 m_proxy->setSpeed(0); 1657 m_loopOut = m_proxy->zoneOut(); 1658 m_loopIn = m_proxy->zoneIn(); 1659 if (qFuzzyIsNull(current_speed)) { 1660 m_producer->seek(m_proxy->zoneIn()); 1661 m_consumer->start(); 1662 m_consumer->set("scrub_audio", 0); 1663 m_consumer->set("refresh", 1); 1664 m_consumer->set("volume", KdenliveSettings::volume() / 100.); 1665 m_producer->set_speed(1.0); 1666 } else { 1667 // Speed change, purge to reduce latency 1668 m_consumer->set("refresh", 0); 1669 m_producer->seek(m_loopIn); 1670 m_consumer->purge(); 1671 m_producer->set_speed(1.0); 1672 m_consumer->set("refresh", 1); 1673 } 1674 m_isZoneMode = true; 1675 m_isLoopMode = loop; 1676 return true; 1677 } 1678 1679 bool VideoWidget::restartConsumer() 1680 { 1681 int result = 0; 1682 if (m_consumer->is_stopped()) { 1683 // When restarting the consumer, we need to restore the preview scaling 1684 int cWidth = m_consumer->get_int("width"); 1685 int cHeigth = m_consumer->get_int("height"); 1686 result = m_consumer->start(); 1687 if (cWidth > 0) { 1688 m_consumer->set("width", cWidth); 1689 m_consumer->set("height", cHeigth); 1690 } 1691 } 1692 return result != -1; 1693 } 1694 1695 bool VideoWidget::loopClip(QPoint inOut) 1696 { 1697 if (!m_producer || inOut.y() <= inOut.x()) { 1698 pCore->displayMessage(i18n("Select a clip to play"), ErrorMessage, 500); 1699 return false; 1700 } 1701 m_loopIn = inOut.x(); 1702 double current_speed = m_producer->get_speed(); 1703 m_producer->set_speed(0); 1704 m_proxy->setSpeed(0); 1705 m_loopOut = inOut.y(); 1706 if (qFuzzyIsNull(current_speed)) { 1707 m_producer->seek(m_loopIn); 1708 m_consumer->start(); 1709 m_consumer->set("scrub_audio", 0); 1710 m_consumer->set("refresh", 1); 1711 m_consumer->set("volume", KdenliveSettings::volume() / 100.); 1712 m_producer->set_speed(1.0); 1713 } else { 1714 // Speed change, purge to reduce latency 1715 m_consumer->set("refresh", 0); 1716 m_consumer->purge(); 1717 m_producer->seek(m_loopIn); 1718 m_producer->set_speed(1.0); 1719 m_consumer->set("refresh", 1); 1720 } 1721 m_isZoneMode = false; 1722 m_isLoopMode = true; 1723 return true; 1724 } 1725 1726 void VideoWidget::resetZoneMode() 1727 { 1728 if (!m_isZoneMode && !m_isLoopMode) { 1729 return; 1730 } 1731 m_loopIn = 0; 1732 m_loopOut = 0; 1733 m_isZoneMode = false; 1734 m_isLoopMode = false; 1735 } 1736 1737 MonitorProxy *VideoWidget::getControllerProxy() 1738 { 1739 return m_proxy; 1740 } 1741 1742 int VideoWidget::getCurrentPos() const 1743 { 1744 return m_proxy->getPosition(); 1745 } 1746 1747 void VideoWidget::setRulerInfo(int duration, const std::shared_ptr<MarkerSortModel> &model) 1748 { 1749 m_maxProducerPosition = duration; 1750 rootObject()->setProperty("duration", duration); 1751 if (model != nullptr) { 1752 // we are resetting marker/snap model, reset zone 1753 rootContext()->setContextProperty("markersModel", model.get()); 1754 } 1755 } 1756 1757 void VideoWidget::switchRecordState(bool on) 1758 { 1759 if (on) { 1760 if (m_maxProducerPosition == 0x7fffffff) { 1761 // We are already in rec mode 1762 return; 1763 } 1764 m_bckpMax = m_maxProducerPosition; 1765 m_maxProducerPosition = 0x7fffffff; 1766 } else { 1767 m_maxProducerPosition = m_bckpMax; 1768 } 1769 } 1770 1771 void VideoWidget::startConsumer() 1772 { 1773 if (m_consumer == nullptr) { 1774 return; 1775 } 1776 if (!restartConsumer()) { 1777 // ARGH CONSUMER BROKEN!!!! 1778 KMessageBox::error( 1779 qApp->activeWindow(), 1780 i18n("Could not create the video preview window.\nThere is something wrong with your Kdenlive install or your driver settings, please fix it.")); 1781 if (m_displayEvent) { 1782 delete m_displayEvent; 1783 } 1784 m_displayEvent = nullptr; 1785 m_consumer.reset(); 1786 return; 1787 } 1788 m_consumer->set("refresh", 1); 1789 } 1790 1791 void VideoWidget::stop() 1792 { 1793 m_refreshTimer.stop(); 1794 // why this lock? 1795 QMutexLocker locker(&m_mltMutex); 1796 if (m_producer) { 1797 if (m_isZoneMode || m_isLoopMode) { 1798 resetZoneMode(); 1799 } 1800 m_producer->set_speed(0.0); 1801 m_proxy->setSpeed(0); 1802 } 1803 if (m_consumer) { 1804 m_consumer->purge(); 1805 if (!m_consumer->is_stopped()) { 1806 m_consumer->stop(); 1807 } 1808 } 1809 } 1810 1811 double VideoWidget::playSpeed() const 1812 { 1813 if (m_producer) { 1814 return m_producer->get_speed(); 1815 } 1816 return 0.0; 1817 } 1818 1819 void VideoWidget::restart() 1820 { 1821 // why this lock? 1822 if (m_consumer) { 1823 // Make sure to delete and rebuild consumer to match profile 1824 m_consumer->purge(); 1825 m_consumer->stop(); 1826 reconfigure(); 1827 } 1828 } 1829 1830 int VideoWidget::volume() const 1831 { 1832 if ((!m_consumer) || (!m_producer)) { 1833 return -1; 1834 } 1835 if (m_consumer->get("mlt_service") == QStringLiteral("multi")) { 1836 return (int(100 * m_consumer->get_double("0.volume"))); 1837 } 1838 return (int(100 * m_consumer->get_double("volume"))); 1839 } 1840 1841 void VideoWidget::setVolume(double volume) 1842 { 1843 if (m_consumer) { 1844 if (m_consumer->get("mlt_service") == QStringLiteral("multi")) { 1845 m_consumer->set("0.volume", volume); 1846 } else { 1847 m_consumer->set("volume", volume); 1848 } 1849 } 1850 } 1851 1852 int VideoWidget::duration() const 1853 { 1854 if (!m_producer) { 1855 return 0; 1856 } 1857 return m_producer->get_playtime(); 1858 } 1859 1860 void VideoWidget::setConsumerProperty(const QString &name, const QString &value) 1861 { 1862 QMutexLocker locker(&m_mltMutex); 1863 if (m_consumer) { 1864 m_consumer->set(name.toUtf8().constData(), value.toUtf8().constData()); 1865 if (m_consumer->start() == -1) { 1866 qCWarning(KDENLIVE_LOG) << "ERROR, Cannot start monitor"; 1867 } 1868 } 1869 } 1870 1871 bool VideoWidget::updateScaling() 1872 { 1873 int previewHeight = pCore->getCurrentFrameSize().height(); 1874 switch (KdenliveSettings::previewScaling()) { 1875 case 2: 1876 previewHeight = qMin(previewHeight, 720); 1877 break; 1878 case 4: 1879 previewHeight = qMin(previewHeight, 540); 1880 break; 1881 case 8: 1882 previewHeight = qMin(previewHeight, 360); 1883 break; 1884 case 16: 1885 previewHeight = qMin(previewHeight, 270); 1886 break; 1887 default: 1888 break; 1889 } 1890 int pWidth = int(previewHeight * pCore->getCurrentDar() / pCore->getCurrentSar()); 1891 if (pWidth % 2 > 0) { 1892 pWidth++; 1893 } 1894 QSize profileSize(pWidth, previewHeight); 1895 if (profileSize == m_profileSize) { 1896 return false; 1897 } 1898 m_profileSize = profileSize; 1899 pCore->getMonitorProfile().set_width(m_profileSize.width()); 1900 pCore->getMonitorProfile().set_height(m_profileSize.height()); 1901 if (m_consumer) { 1902 m_consumer->set("width", m_profileSize.width()); 1903 m_consumer->set("height", m_profileSize.height()); 1904 resizeGL(width(), height()); 1905 } 1906 return true; 1907 } 1908 1909 void VideoWidget::switchRuler(bool show) 1910 { 1911 m_rulerHeight = show ? int(QFontInfo(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)).pixelSize() * 1.5) : 0; 1912 m_displayRulerHeight = m_rulerHeight; 1913 resizeGL(width(), height()); 1914 Q_EMIT m_proxy->rulerHeightChanged(); 1915 } 1916 1917 const QStringList VideoWidget::getGPUInfo() 1918 { 1919 if (!m_isInitialized) { 1920 return {}; 1921 } 1922 return {QString::fromUtf8((const char *)glGetString(GL_VENDOR)), QString::fromUtf8((const char *)glGetString(GL_RENDERER))}; 1923 }