glwidget.cpp 53.6 KB
Newer Older
1
/*
2 3 4
 * Copyright (c) 2011-2016 Meltytech, LLC
 * Original author: Dan Dennedy <dan@dennedy.org>
 * Modified for Kdenlive: Jean-Baptiste Mardelle
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * GL shader based on BSD licensed code from Peter Bengtsson:
 * http://www.fourcc.org/source/YUV420P-OpenGL-GLSLang.c
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <QOpenGLFunctions_3_2_Core>
24
#include <QQuickItem>
Laurent Montel's avatar
Laurent Montel committed
25 26
#include <QApplication>
#include <QPainter>
27 28 29 30

#include <mlt++/Mlt.h>
#include "glwidget.h"
#include "core.h"
31
#include "qml/qmlaudiothumb.h"
32 33 34
#include "kdenlivesettings.h"
#include "mltcontroller/bincontroller.h"

35 36 37 38 39 40 41 42
#ifndef GL_UNPACK_ROW_LENGTH
# ifdef GL_UNPACK_ROW_LENGTH_EXT
#  define GL_UNPACK_ROW_LENGTH GL_UNPACK_ROW_LENGTH_EXT
# else
#  error GL_UNPACK_ROW_LENGTH undefined
# endif
#endif

43 44 45
#ifdef QT_NO_DEBUG
#define check_error(fn) {}
#else
Laurent Montel's avatar
Laurent Montel committed
46
#define check_error(fn) { int err = fn->glGetError(); if (err != GL_NO_ERROR) { qCCritical(KDENLIVE_LOG) << "GL error"  << hex << err << dec << "at" << __FILE__ << ":" << __LINE__; } }
47
#endif
48 49 50 51 52 53

#ifndef GL_TIMEOUT_IGNORED
#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull
#endif

#ifndef Q_OS_WIN
Laurent Montel's avatar
Laurent Montel committed
54
typedef GLenum(*ClientWaitSync_fp)(GLsync sync, GLbitfield flags, GLuint64 timeout);
Laurent Montel's avatar
Laurent Montel committed
55
static ClientWaitSync_fp ClientWaitSync = nullptr;
56 57 58 59
#endif

using namespace Mlt;

60
GLWidget::GLWidget(int id, QObject *parent)
Laurent Montel's avatar
Laurent Montel committed
61
    : QQuickView((QWindow *) parent)
Vincent Pinon's avatar
Vincent Pinon committed
62
    , sendFrameForAnalysis(false)
63
    , m_id(id)
Laurent Montel's avatar
Laurent Montel committed
64 65 66 67
    , m_shader(nullptr)
    , m_glslManager(nullptr)
    , m_consumer(nullptr)
    , m_producer(nullptr)
68
    , m_initSem(0)
69
    , m_analyseSem(1)
70
    , m_isInitialized(false)
Laurent Montel's avatar
Laurent Montel committed
71 72 73 74 75 76
    , m_threadStartEvent(nullptr)
    , m_threadStopEvent(nullptr)
    , m_threadCreateEvent(nullptr)
    , m_threadJoinEvent(nullptr)
    , m_displayEvent(nullptr)
    , m_frameRenderer(nullptr)
77 78 79 80 81
    , m_projectionLocation(0)
    , m_modelViewLocation(0)
    , m_vertexLocation(0)
    , m_texCoordLocation(0)
    , m_colorspaceLocation(0)
82
    , m_zoom(1.0f)
83
    , m_openGLSync(false)
84
    , m_sendFrame(false)
85
    , m_offset(QPoint(0, 0))
Laurent Montel's avatar
Laurent Montel committed
86
    , m_shareContext(nullptr)
87
    , m_audioWaveDisplayed(false)
Laurent Montel's avatar
Laurent Montel committed
88
    , m_fbo(nullptr)
89 90 91 92 93
{
    m_texture[0] = m_texture[1] = m_texture[2] = 0;
    qRegisterMetaType<Mlt::Frame>("Mlt::Frame");
    qRegisterMetaType<SharedFrame>("SharedFrame");

94
    qmlRegisterType<QmlAudioThumb>("AudioThumb", 1, 0, "QmlAudioThumb");
95 96 97 98
    setPersistentOpenGLContext(true);
    setPersistentSceneGraph(true);
    setClearBeforeRendering(false);
    setResizeMode(QQuickView::SizeRootObjectToView);
99

100
    m_monitorProfile = new Mlt::Profile();
101

Laurent Montel's avatar
Laurent Montel committed
102
    if (KdenliveSettings::gpu_accel()) {
103
        m_glslManager = new Mlt::Filter(*m_monitorProfile, "glsl.manager");
Laurent Montel's avatar
Laurent Montel committed
104
    }
105 106
    if ((m_glslManager && !m_glslManager->is_valid())) {
        delete m_glslManager;
Laurent Montel's avatar
Laurent Montel committed
107
        m_glslManager = nullptr;
108 109
        KdenliveSettings::setGpu_accel(false);
        // Need to destroy MLT global reference to prevent filters from trying to use GPU.
Laurent Montel's avatar
Laurent Montel committed
110
        mlt_properties_set_data(mlt_global_properties(), "glslManager", nullptr, 0, nullptr, nullptr);
111
        emit gpuNotSupported();
112
    }
Laurent Montel's avatar
Laurent Montel committed
113 114
    connect(this, &QQuickWindow::sceneGraphInitialized, this, &GLWidget::initializeGL, Qt::DirectConnection);
    connect(this, &QQuickWindow::beforeRendering, this, &GLWidget::paintGL, Qt::DirectConnection);
115 116 117 118 119 120 121 122 123
}

GLWidget::~GLWidget()
{
    delete m_glslManager;
    delete m_threadStartEvent;
    delete m_threadStopEvent;
    delete m_threadCreateEvent;
    delete m_threadJoinEvent;
124
    delete m_displayEvent;
125 126 127 128 129 130 131 132 133
    if (m_frameRenderer) {
        if (m_frameRenderer->isRunning()) {
            QMetaObject::invokeMethod(m_frameRenderer, "cleanup");
            m_frameRenderer->quit();
            m_frameRenderer->wait();
            m_frameRenderer->deleteLater();
        } else {
            delete m_frameRenderer;
        }
134
    }
135
    delete m_shareContext;
136
    delete m_shader;
137
    delete m_monitorProfile;
138 139
}

140
void GLWidget::updateAudioForAnalysis()
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
141
{
Laurent Montel's avatar
Laurent Montel committed
142 143 144
    if (m_frameRenderer) {
        m_frameRenderer->sendAudioForAnalysis = KdenliveSettings::monitor_audio();
    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
145 146
}

147
void GLWidget::initializeGL()
148
{
149
    if (m_isInitialized || !isVisible() || !openglContext()) return;
150 151 152
    if (!m_offscreenSurface.isValid()) {
        m_offscreenSurface.setFormat(openglContext()->format());
        m_offscreenSurface.create();
153
        openglContext()->makeCurrent(this);
154
    }
155
    initializeOpenGLFunctions();
Laurent Montel's avatar
Laurent Montel committed
156 157 158 159 160
    qCDebug(KDENLIVE_LOG) << "OpenGL vendor: " << QString::fromUtf8((const char *) glGetString(GL_VENDOR));
    qCDebug(KDENLIVE_LOG) << "OpenGL renderer: " << QString::fromUtf8((const char *) glGetString(GL_RENDERER));
    qCDebug(KDENLIVE_LOG) << "OpenGL Threaded: " << openglContext()->supportsThreadedOpenGL();
    qCDebug(KDENLIVE_LOG) << "OpenGL ARG_SYNC: " << openglContext()->hasExtension("GL_ARB_sync");
    qCDebug(KDENLIVE_LOG) << "OpenGL OpenGLES: " << openglContext()->isOpenGLES();
161

162 163
    if (m_glslManager && openglContext()->isOpenGLES()) {
        delete m_glslManager;
Laurent Montel's avatar
Laurent Montel committed
164
        m_glslManager = nullptr;
165 166
        KdenliveSettings::setGpu_accel(false);
        // Need to destroy MLT global reference to prevent filters from trying to use GPU.
Laurent Montel's avatar
Laurent Montel committed
167
        mlt_properties_set_data(mlt_global_properties(), "glslManager", nullptr, 0, nullptr, nullptr);
168 169
        emit gpuNotSupported();
    }
170 171
    createShader();

172
#if !defined(Q_OS_WIN)
173 174
    // getProcAddress is not working for me on Windows.
    if (KdenliveSettings::gpu_accel()) {
175
        m_openGLSync = false;
176 177
        if (m_glslManager && openglContext()->hasExtension("GL_ARB_sync")) {
            ClientWaitSync = (ClientWaitSync_fp) openglContext()->getProcAddress("glClientWaitSync");
178 179
            if (ClientWaitSync) {
                m_openGLSync = true;
180
            } else {
Laurent Montel's avatar
Laurent Montel committed
181
                qCDebug(KDENLIVE_LOG) << "  / / // NO GL SYNC, ERROR";
182 183
                emit gpuNotSupported();
                delete m_glslManager;
Laurent Montel's avatar
Laurent Montel committed
184
                m_glslManager = nullptr;
185
            }
186 187 188 189 190
        }
    }
#endif

    openglContext()->doneCurrent();
191 192 193 194 195 196 197 198 199 200
    if (m_glslManager) {
        // Create a context sharing with this context for the RenderThread context.
        // This is needed because openglContext() is active in another thread
        // at the time that RenderThread is created.
        // See this Qt bug for more info: https://bugreports.qt.io/browse/QTBUG-44677
        m_shareContext = new QOpenGLContext;
        m_shareContext->setFormat(openglContext()->format());
        m_shareContext->setShareContext(openglContext());
        m_shareContext->create();
    }
201
    m_frameRenderer = new FrameRenderer(openglContext(), &m_offscreenSurface);
202
    m_frameRenderer->sendAudioForAnalysis = KdenliveSettings::monitor_audio();
203
    openglContext()->makeCurrent(this);
204
    //openglContext()->blockSignals(false);
Laurent Montel's avatar
Laurent Montel committed
205
    connect(m_frameRenderer, &FrameRenderer::frameDisplayed, this, &GLWidget::frameDisplayed, Qt::QueuedConnection);
Laurent Montel's avatar
Laurent Montel committed
206
    if (KdenliveSettings::gpu_accel() || openglContext()->supportsThreadedOpenGL()) {
Laurent Montel's avatar
Laurent Montel committed
207
        connect(m_frameRenderer, &FrameRenderer::textureReady, this, &GLWidget::updateTexture, Qt::DirectConnection);
Laurent Montel's avatar
Laurent Montel committed
208
    } else {
Laurent Montel's avatar
Laurent Montel committed
209
        connect(m_frameRenderer, &FrameRenderer::frameDisplayed, this, &GLWidget::onFrameDisplayed, Qt::QueuedConnection);
Laurent Montel's avatar
Laurent Montel committed
210
    }
211

Laurent Montel's avatar
Laurent Montel committed
212
    connect(m_frameRenderer, &FrameRenderer::audioSamplesSignal, this, &GLWidget::audioSamplesSignal, Qt::QueuedConnection);
213
    connect(this, &GLWidget::textureUpdated, this, &GLWidget::update, Qt::QueuedConnection);
214
    m_initSem.release();
215
    m_isInitialized = true;
216 217 218 219 220 221
}

void GLWidget::resizeGL(int width, int height)
{
    int x, y, w, h;
    double this_aspect = (double) width / height;
222
    double video_aspect = m_monitorProfile->dar();
223 224 225

    // Special case optimisation to negate odd effect of sample aspect ratio
    // not corresponding exactly with image resolution.
Laurent Montel's avatar
Laurent Montel committed
226
    if ((int)(this_aspect * 1000) == (int)(video_aspect * 1000)) {
227 228 229 230
        w = width;
        h = height;
    }
    // Use OpenGL to normalise sample aspect ratio
Laurent Montel's avatar
Laurent Montel committed
231
    else if (height * video_aspect > width) {
232 233
        w = width;
        h = width / video_aspect;
Laurent Montel's avatar
Laurent Montel committed
234
    } else {
235 236 237 238 239 240
        w = height * video_aspect;
        h = height;
    }
    x = (width - w) / 2;
    y = (height - h) / 2;
    m_rect.setRect(x, y, w, h);
241 242
    double scalex = (double) m_rect.width() / m_monitorProfile->width() * m_zoom;
    double scaley = (double) m_rect.width() / ((double) m_monitorProfile->height() * m_monitorProfile->dar() / m_monitorProfile->width()) / m_monitorProfile->width() * m_zoom;
243
    QPoint center = m_rect.center();
Laurent Montel's avatar
Laurent Montel committed
244
    QQuickItem *rootQml = rootObject();
245 246
    if (rootQml) {
        rootQml->setProperty("center", center);
247 248
        rootQml->setProperty("scalex", scalex);
        rootQml->setProperty("scaley", scaley);
249
        if (rootQml->objectName() == QLatin1String("rootsplit")) {
250 251 252
            // Adjust splitter pos
            rootQml->setProperty("splitterPos", x + (rootQml->property("realpercent").toDouble() * w));
        }
253
    }
254 255 256
    emit rectChanged();
}

Laurent Montel's avatar
Laurent Montel committed
257
void GLWidget::resizeEvent(QResizeEvent *event)
258 259 260 261 262 263 264 265 266
{
    QQuickView::resizeEvent(event);
    resizeGL(event->size().width(), event->size().height());
}

void GLWidget::createShader()
{
    m_shader = new QOpenGLShaderProgram;
    m_shader->addShaderFromSourceCode(QOpenGLShader::Vertex,
Laurent Montel's avatar
Laurent Montel committed
267
                                      "uniform highp mat4 projection;"
268
                                      "uniform highp mat4 modelView;"
Laurent Montel's avatar
Laurent Montel committed
269 270 271 272 273 274 275
                                      "attribute highp vec4 vertex;"
                                      "attribute highp vec2 texCoord;"
                                      "varying highp vec2 coordinates;"
                                      "void main(void) {"
                                      "  gl_Position = projection * modelView * vertex;"
                                      "  coordinates = texCoord;"
                                      "}");
276 277 278
    if (m_glslManager) {
        m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment,
                                          "uniform sampler2D tex;"
279
                                          "varying highp vec2 coordinates;"
280 281 282 283 284 285 286 287
                                          "void main(void) {"
                                          "  gl_FragColor = texture2D(tex, coordinates);"
                                          "}");
        m_shader->link();
        m_textureLocation[0] = m_shader->uniformLocation("tex");
    } else {
        m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment,
                                          "uniform sampler2D Ytex, Utex, Vtex;"
288 289
                                          "uniform lowp int colorspace;"
                                          "varying highp vec2 coordinates;"
290
                                          "void main(void) {"
291
                                          "  mediump vec3 texel;"
292 293 294
                                          "  texel.r = texture2D(Ytex, coordinates).r - 0.0625;" // Y
                                          "  texel.g = texture2D(Utex, coordinates).r - 0.5;"    // U
                                          "  texel.b = texture2D(Vtex, coordinates).r - 0.5;"    // V
295
                                          "  mediump mat3 coefficients;"
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
                                          "  if (colorspace == 601) {"
                                          "    coefficients = mat3("
                                          "      1.1643,  1.1643,  1.1643," // column 1
                                          "      0.0,    -0.39173, 2.017," // column 2
                                          "      1.5958, -0.8129,  0.0);" // column 3
                                          "  } else {" // ITU-R 709
                                          "    coefficients = mat3("
                                          "      1.1643, 1.1643, 1.1643," // column 1
                                          "      0.0,   -0.213,  2.112," // column 2
                                          "      1.793, -0.533,  0.0);" // column 3
                                          "  }"
                                          "  gl_FragColor = vec4(coefficients * texel, 1.0);"
                                          "}");
        m_shader->link();
        m_textureLocation[0] = m_shader->uniformLocation("Ytex");
        m_textureLocation[1] = m_shader->uniformLocation("Utex");
        m_textureLocation[2] = m_shader->uniformLocation("Vtex");
        m_colorspaceLocation = m_shader->uniformLocation("colorspace");
    }
    m_projectionLocation = m_shader->uniformLocation("projection");
    m_modelViewLocation = m_shader->uniformLocation("modelView");
    m_vertexLocation = m_shader->attributeLocation("vertex");
    m_texCoordLocation = m_shader->attributeLocation("texCoord");
}

Laurent Montel's avatar
Laurent Montel committed
321
static void uploadTextures(QOpenGLContext *context, const SharedFrame &frame, GLuint texture[])
322 323 324
{
    int width = frame.get_image_width();
    int height = frame.get_image_height();
Laurent Montel's avatar
Laurent Montel committed
325 326
    const uint8_t *image = frame.get_image();
    QOpenGLFunctions *f = context->functions();
327 328

    // Upload each plane of YUV to a texture.
Laurent Montel's avatar
Laurent Montel committed
329
    if (texture[0]) {
330
        f->glDeleteTextures(3, texture);
Laurent Montel's avatar
Laurent Montel committed
331
    }
332 333 334 335
    check_error(f);
    f->glGenTextures(3, texture);
    check_error(f);

Laurent Montel's avatar
Laurent Montel committed
336
    f->glBindTexture(GL_TEXTURE_2D, texture[0]);
337 338 339 340 341 342 343 344 345
    check_error(f);
    f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    check_error(f);
    f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    check_error(f);
    f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    check_error(f);
    f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    check_error(f);
Laurent Montel's avatar
Laurent Montel committed
346
    f->glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0,
Vincent Pinon's avatar
Vincent Pinon committed
347
                    GL_LUMINANCE, GL_UNSIGNED_BYTE, image);
348 349
    check_error(f);

Laurent Montel's avatar
Laurent Montel committed
350
    f->glBindTexture(GL_TEXTURE_2D, texture[1]);
351 352 353 354 355 356 357 358 359
    check_error(f);
    f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    check_error(f);
    f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    check_error(f);
    f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    check_error(f);
    f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    check_error(f);
Laurent Montel's avatar
Laurent Montel committed
360
    f->glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width / 2, height / 2, 0,
Vincent Pinon's avatar
Vincent Pinon committed
361
                    GL_LUMINANCE, GL_UNSIGNED_BYTE, image + width * height);
362 363
    check_error(f);

Laurent Montel's avatar
Laurent Montel committed
364
    f->glBindTexture(GL_TEXTURE_2D, texture[2]);
365 366 367 368 369 370 371 372 373
    check_error(f);
    f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    check_error(f);
    f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    check_error(f);
    f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    check_error(f);
    f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    check_error(f);
Laurent Montel's avatar
Laurent Montel committed
374 375
    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);
376 377 378
    check_error(f);
}

379 380 381 382 383 384
void GLWidget::clear()
{
    stopGlsl();
    update();
}

385 386 387 388 389
void GLWidget::releaseAnalyse()
{
    m_analyseSem.release();
}

390 391
void GLWidget::paintGL()
{
Laurent Montel's avatar
Laurent Montel committed
392 393 394 395
    if (m_glslManager && !m_texture[0]) {
        return;
    }
    QOpenGLFunctions *f = openglContext()->functions();
396 397 398
    int width = this->width() * devicePixelRatio();
    int height = this->height() * devicePixelRatio();

399 400 401 402
    f->glDisable(GL_BLEND);
    f->glDisable(GL_DEPTH_TEST);
    f->glDepthMask(GL_FALSE);
    f->glViewport(0, 0, width, height);
403
    check_error(f);
404
    QColor color(KdenliveSettings::window_background());
405 406
    f->glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
    f->glClear(GL_COLOR_BUFFER_BIT);
407
    check_error(f);
408

409
    if (!(m_glslManager || openglContext()->supportsThreadedOpenGL())) {
410 411 412 413 414 415 416
        m_mutex.lock();
        if (!m_sharedFrame.is_valid()) {
            m_mutex.unlock();
            return;
        }
        uploadTextures(openglContext(), m_sharedFrame, m_texture);
        m_mutex.unlock();
417
    }
418 419 420 421

    // Bind textures.
    for (int i = 0; i < 3; ++i) {
        if (m_texture[i]) {
422 423
            f->glActiveTexture(GL_TEXTURE0 + i);
            f->glBindTexture(GL_TEXTURE_2D, m_texture[i]);
424
            check_error(f);
425 426 427 428 429 430 431 432 433 434
        }
    }
    // Init shader program.
    m_shader->bind();
    if (m_glslManager) {
        m_shader->setUniformValue(m_textureLocation[0], 0);
    } else {
        m_shader->setUniformValue(m_textureLocation[0], 0);
        m_shader->setUniformValue(m_textureLocation[1], 1);
        m_shader->setUniformValue(m_textureLocation[2], 2);
435
        m_shader->setUniformValue(m_colorspaceLocation, m_monitorProfile->colorspace());
436
    }
437
    check_error(f);
438 439 440 441 442

    // Setup an orthographic projection.
    QMatrix4x4 projection;
    projection.scale(2.0f / width, 2.0f / height);
    m_shader->setUniformValue(m_projectionLocation, projection);
443
    check_error(f);
444 445 446

    // Set model view.
    QMatrix4x4 modelView;
447
    if (m_zoom != 1.0) {
448 449
        if (offset().x() || offset().y())
            modelView.translate(-offset().x() * devicePixelRatio(),
Laurent Montel's avatar
Laurent Montel committed
450
                                offset().y() * devicePixelRatio());
451 452 453
        modelView.scale(zoom(), zoom());
    }
    m_shader->setUniformValue(m_modelViewLocation, modelView);
454
    check_error(f);
455 456 457 458 459

    // Provide vertices of triangle strip.
    QVector<QVector2D> vertices;
    width = m_rect.width() * devicePixelRatio();
    height = m_rect.height() * devicePixelRatio();
Laurent Montel's avatar
Laurent Montel committed
460 461 462 463
    vertices << QVector2D(float(-width) / 2.0f, float(-height) / 2.0f);
    vertices << QVector2D(float(-width) / 2.0f, float(height) / 2.0f);
    vertices << QVector2D(float(width) / 2.0f, float(-height) / 2.0f);
    vertices << QVector2D(float(width) / 2.0f, float(height) / 2.0f);
464
    m_shader->enableAttributeArray(m_vertexLocation);
465
    check_error(f);
466
    m_shader->setAttributeArray(m_vertexLocation, vertices.constData());
467
    check_error(f);
468 469 470 471 472 473 474 475

    // Provide texture coordinates.
    QVector<QVector2D> texCoord;
    texCoord << QVector2D(0.0f, 1.0f);
    texCoord << QVector2D(0.0f, 0.0f);
    texCoord << QVector2D(1.0f, 1.0f);
    texCoord << QVector2D(1.0f, 0.0f);
    m_shader->enableAttributeArray(m_texCoordLocation);
476
    check_error(f);
477
    m_shader->setAttributeArray(m_texCoordLocation, texCoord.constData());
478
    check_error(f);
479 480

    // Render
481
    f->glDrawArrays(GL_TRIANGLE_STRIP, 0, vertices.size());
482
    check_error(f);
483

484
    if (m_sendFrame && m_analyseSem.tryAcquire(1)) {
485 486 487 488
        // Render RGB frame for analysis
        int fullWidth = m_monitorProfile->width();
        int fullHeight = m_monitorProfile->height();
        if (!m_fbo || m_fbo->size() != QSize(fullWidth, fullHeight)) {
489
            delete m_fbo;
490
            QOpenGLFramebufferObjectFormat fmt;
491
            fmt.setSamples(1);
492 493
            fmt.setInternalTextureFormat(GL_RGB); //GL_RGBA32F);  // which one is the fastest ?
            m_fbo = new QOpenGLFramebufferObject(fullWidth, fullHeight, fmt); //GL_TEXTURE_2D);
494 495
        }
        m_fbo->bind();
496
        f->glViewport(0, 0, fullWidth, fullHeight);
497 498 499 500 501

        QMatrix4x4 projection2;
        projection2.scale(2.0f / width, 2.0f / height);
        m_shader->setUniformValue(m_projectionLocation, projection2);

502
        f->glDrawArrays(GL_TRIANGLE_STRIP, 0, vertices.size());
503
        check_error(f);
504 505
        m_fbo->release();
        emit analyseFrame(m_fbo->toImage());
506
        m_sendFrame = false;
507
    }
508 509 510 511 512 513
    // Cleanup
    m_shader->disableAttributeArray(m_vertexLocation);
    m_shader->disableAttributeArray(m_texCoordLocation);
    m_shader->release();
    for (int i = 0; i < 3; ++i) {
        if (m_texture[i]) {
514 515
            f->glActiveTexture(GL_TEXTURE0 + i);
            f->glBindTexture(GL_TEXTURE_2D, 0);
516
            check_error(f);
517 518
        }
    }
519
    f->glActiveTexture(GL_TEXTURE0);
520
    check_error(f);
521 522
}

523 524 525 526 527 528 529 530 531 532 533 534 535
void GLWidget::slotZoomScene(double value)
{
    if (value >= 3) {
        setZoom(value - 2.0f);
    } else if (value == 2) {
        setZoom(0.5);
    } else if (value == 1) {
        setZoom(0.25);
    } else if (value == 0) {
        setZoom(0.125);
    }
}

Laurent Montel's avatar
Laurent Montel committed
536
void GLWidget::wheelEvent(QWheelEvent *event)
537
{
538 539 540 541
    if (event->modifiers() & Qt::ControlModifier && event->modifiers() & Qt::ShiftModifier) {
        if (event->delta() > 0) {
            if (m_zoom == 1.0f) {
                setZoom(2.0f);
Laurent Montel's avatar
Laurent Montel committed
542
            } else if (m_zoom == 2.0f) {
543
                setZoom(3.0f);
Laurent Montel's avatar
Laurent Montel committed
544
            } else if (m_zoom < 1.0f) {
545 546
                setZoom(m_zoom * 2);
            }
Laurent Montel's avatar
Laurent Montel committed
547
        } else {
548 549
            if (m_zoom == 3.0f) {
                setZoom(2.0f);
Laurent Montel's avatar
Laurent Montel committed
550
            } else if (m_zoom == 2.0f) {
551
                setZoom(1.0f);
Laurent Montel's avatar
Laurent Montel committed
552
            } else if (m_zoom > 0.2) {
553 554 555 556 557
                setZoom(m_zoom / 2);
            }
        }
        return;
    }
558
    emit mouseSeek(event->delta(), (int) event->modifiers());
559 560 561
    event->accept();
}

Laurent Montel's avatar
Laurent Montel committed
562
void GLWidget::mousePressEvent(QMouseEvent *event)
563
{
564
    if (rootObject() && rootObject()->objectName() != QLatin1String("root") && !(event->modifiers() & Qt::ControlModifier) && !(event->buttons() & Qt::MiddleButton)) {
565
        event->ignore();
566
        QQuickView::mousePressEvent(event);
567 568
        return;
    }
569
    if (event->button() & Qt::LeftButton) {
570 571 572 573 574 575 576
        if (event->modifiers() & Qt::ControlModifier) {
            // Pan view
            m_panStart = event->pos();
            setCursor(Qt::ClosedHandCursor);
        } else {
            m_dragStart = event->pos();
        }
577 578 579
    }
    else if (event->button() & Qt::RightButton) {
        emit showContextMenu(event->globalPos());
580 581 582
    } else if (event->button() & Qt::MiddleButton) {
        m_panStart = event->pos();
        setCursor(Qt::ClosedHandCursor);
583
    }
584
    event->accept();
585
    QQuickView::mousePressEvent(event);
586 587
}

Laurent Montel's avatar
Laurent Montel committed
588
void GLWidget::mouseMoveEvent(QMouseEvent *event)
589
{
590
    if (rootObject() && rootObject()->objectName() != QLatin1String("root") && !(event->modifiers() & Qt::ControlModifier) && !(event->buttons() & Qt::MiddleButton)) {
591
        event->ignore();
592
        QQuickView::mouseMoveEvent(event);
593 594
        return;
    }
595
    /*    if (event->modifiers() == Qt::ShiftModifier && m_producer) {
596 597 598
        emit seekTo(m_producer->get_length() *  event->x() / width());
        return;
    }*/
599 600 601 602 603 604 605 606
    QQuickView::mouseMoveEvent(event);
    if (!m_panStart.isNull()) {
        emit panView(m_panStart - event->pos());
        m_panStart = event->pos();
        event->accept();
        QQuickView::mouseMoveEvent(event);
        return;
    }
Laurent Montel's avatar
Laurent Montel committed
607
    if (!(event->buttons() & Qt::LeftButton)) {
608
        QQuickView::mouseMoveEvent(event);
609
        return;
Laurent Montel's avatar
Laurent Montel committed
610
    }
611
    if (!event->isAccepted() && !m_dragStart.isNull() && (event->pos() - m_dragStart).manhattanLength() >= QApplication::startDragDistance()) {
612 613 614
        m_dragStart = QPoint();
        emit startDrag();
    }
615 616
}

Laurent Montel's avatar
Laurent Montel committed
617
void GLWidget::keyPressEvent(QKeyEvent *event)
618
{
619
    QQuickView::keyPressEvent(event);
620 621 622
    if (!event->isAccepted()) {
        emit passKeyEvent(event);
    }
623 624 625 626 627 628
}

void GLWidget::createThread(RenderThread **thread, thread_function_t function, void *data)
{
#ifdef Q_OS_WIN
    // On Windows, MLT event consumer-thread-create is fired from the Qt main thread.
Laurent Montel's avatar
Laurent Montel committed
629
    while (!m_isInitialized) {
630
        qApp->processEvents();
Laurent Montel's avatar
Laurent Montel committed
631
    }
632 633 634 635 636
#else
    if (!m_isInitialized) {
        m_initSem.acquire();
    }
#endif
637
    (*thread) = new RenderThread(function, data, m_shareContext, &m_offscreenSurface);
638 639 640
    (*thread)->start();
}

Laurent Montel's avatar
Laurent Montel committed
641 642
static void onThreadCreate(mlt_properties owner, GLWidget *self,
                           RenderThread **thread, int *priority, thread_function_t function, void *data)
643 644 645
{
    Q_UNUSED(owner)
    Q_UNUSED(priority)
646
    //self->clearFrameRenderer();
647
    self->createThread(thread, function, data);
648
    self->lockMonitor();
649 650
}

Laurent Montel's avatar
Laurent Montel committed
651
static void onThreadJoin(mlt_properties owner, GLWidget *self, RenderThread *thread)
652 653 654 655 656 657
{
    Q_UNUSED(owner)
    if (thread) {
        thread->quit();
        thread->wait();
        delete thread;
658
        //self->clearFrameRenderer();
659
        self->releaseMonitor();
660 661 662 663 664 665
    }
}

void GLWidget::startGlsl()
{
    if (m_glslManager) {
666
        //clearFrameRenderer();
667 668 669
        m_glslManager->fire_event("init glsl");
        if (!m_glslManager->get_int("glsl_supported")) {
            delete m_glslManager;
Laurent Montel's avatar
Laurent Montel committed
670
            m_glslManager = nullptr;
671
            // Need to destroy MLT global reference to prevent filters from trying to use GPU.
Laurent Montel's avatar
Laurent Montel committed
672
            mlt_properties_set_data(mlt_global_properties(), "glslManager", nullptr, 0, nullptr, nullptr);
673
            emit gpuNotSupported();
Laurent Montel's avatar
Laurent Montel committed
674
        } else {
675 676 677 678 679
            emit started();
        }
    }
}

Laurent Montel's avatar
Laurent Montel committed
680
static void onThreadStarted(mlt_properties owner, GLWidget *self)
681 682 683 684 685
{
    Q_UNUSED(owner)
    self->startGlsl();
}

686 687 688 689 690 691 692 693 694 695
void GLWidget::releaseMonitor()
{
    emit lockMonitor(false);
}

void GLWidget::lockMonitor()
{
    emit lockMonitor(true);
}

696 697
void GLWidget::stopGlsl()
{
Laurent Montel's avatar
Laurent Montel committed
698
    if (m_consumer) {
699
        m_consumer->purge();
Laurent Montel's avatar
Laurent Montel committed
700 701
    }
    if (m_frameRenderer) {
702
        m_frameRenderer->clearFrame();
Laurent Montel's avatar
Laurent Montel committed
703
    }
704

705 706 707 708 709
    //TODO This is commented out for now because it is causing crashes.
    //Technically, this should be the correct thing to do, but it appears
    //some changes have created regression (see shotcut)
    //with respect to restarting the consumer in GPU mode.
    //m_glslManager->fire_event("close glsl");
710 711 712
    m_texture[0] = 0;
}

Laurent Montel's avatar
Laurent Montel committed
713
static void onThreadStopped(mlt_properties owner, GLWidget *self)
714 715 716 717 718
{
    Q_UNUSED(owner)
    self->stopGlsl();
}

719 720 721 722 723 724 725 726 727 728 729 730 731 732
void GLWidget::slotSwitchAudioOverlay(bool enable)
{
    KdenliveSettings::setDisplayAudioOverlay(enable);
    if (m_audioWaveDisplayed && enable == false) {
        if (m_producer && m_producer->get_int("video_index") != -1) {
            // We have a video producer, disable filter
            removeAudioOverlay();
        }
    }
    if (enable && !m_audioWaveDisplayed) {
        createAudioOverlay(m_producer->get_int("video_index") == -1);
    }
}

Laurent Montel's avatar
Laurent Montel committed
733
int GLWidget::setProducer(Mlt::Producer *producer)
734 735 736
{
    int error = 0;//Controller::setProducer(producer, isMulti);
    m_producer = producer;
Vincent Pinon's avatar
Vincent Pinon committed
737
    if (m_producer) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
738
        error = reconfigure();
739 740 741 742
        if (!error) {
            // The profile display aspect ratio may have changed.
            resizeGL(width(), height());
        }
Laurent Montel's avatar
Laurent Montel committed
743 744 745 746 747 748
    } else {
        return error;
    }
    if (!m_consumer) {
        return error;
    }
749
    if (m_producer->get_int("video_index") == -1) {
750 751 752
        // This is an audio only clip, attach visualization filter. Currently, the filter crashes MLT when Movit accel is used
        if (!m_audioWaveDisplayed) {
            createAudioOverlay(true);
Laurent Montel's avatar
Laurent Montel committed
753 754 755 756 757 758
        } else if (m_consumer) {
            if (KdenliveSettings::gpu_accel()) {
                removeAudioOverlay();
            } else {
                adjustAudioOverlay(true);
            }
759
        }
Laurent Montel's avatar
Laurent Montel committed
760
    } else if (m_audioWaveDisplayed && m_consumer) {
761 762 763
        // This is not an audio clip, hide wave
        if (KdenliveSettings::displayAudioOverlay()) {
            adjustAudioOverlay(m_producer->get_int("video_index") == -1);
Laurent Montel's avatar
Laurent Montel committed
764
        } else {
765 766
            removeAudioOverlay();
        }
Laurent Montel's avatar
Laurent Montel committed
767
    } else if (KdenliveSettings::displayAudioOverlay()) {
768 769
        createAudioOverlay(false);
    }
770 771 772
    return error;
}

773 774 775 776 777 778 779
int GLWidget::droppedFrames() const
{
    return (m_consumer ? m_consumer->get_int("drop_count") : 0);
}

void GLWidget::resetDrops()
{
Laurent Montel's avatar
Laurent Montel committed
780 781 782
    if (m_consumer) {
        m_consumer->set("drop_count", 0);
    }
783 784
}

785 786
void GLWidget::createAudioOverlay(bool isAudio)
{
Laurent Montel's avatar
Laurent Montel committed
787 788 789
    if (!m_consumer) {
        return;
    }
790 791 792 793
    if (isAudio && KdenliveSettings::gpu_accel()) {
        // Audiowaveform filter crashes on Movit + audio clips)
        return;
    }
794
    Mlt::Filter f(*m_monitorProfile, "audiowaveform");
795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818
    if (f.is_valid()) {
        //f.set("show_channel", 1);
        f.set("color.1", "0xffff0099");
        f.set("fill", 1);
        if (isAudio) {
            // Fill screen
            f.set("rect", "0,0,100%,100%");
        } else {
            // Overlay on lower part of the screen
            f.set("rect", "0,80%,100%,20%");
        }
        m_consumer->attach(f);
        m_audioWaveDisplayed = true;
    }
}

void GLWidget::removeAudioOverlay()
{
    Mlt::Service sourceService(m_consumer->get_service());
    // move all effects to the correct producer
    int ct = 0;
    Mlt::Filter *filter = sourceService.filter(ct);
    while (filter) {
        QString srv = filter->get("mlt_service");
819
        if (srv == QLatin1String("audiowaveform")) {
820 821 822
            sourceService.detach(*filter);
            delete filter;
            break;
Laurent Montel's avatar
Laurent Montel committed
823 824 825
        } else {
            ct++;
        }
826 827 828 829 830 831 832 833 834 835 836 837 838
        filter = sourceService.filter(ct);
    }
    m_audioWaveDisplayed = false;
}

void GLWidget::adjustAudioOverlay(bool isAudio)
{
    Mlt::Service sourceService(m_consumer->get_service());
    // move all effects to the correct producer
    int ct = 0;
    Mlt::Filter *filter = sourceService.filter(ct);
    while (filter) {
        QString srv = filter->get("mlt_service");
839
        if (srv == QLatin1String("audiowaveform")) {
840 841
            if (isAudio) {
                filter->set("rect", "0,0,100%,100%");
Laurent Montel's avatar
Laurent Montel committed
842
            } else {
843 844 845
                filter->set("rect", "0,80%,100%,20%");
            }
            break;
Laurent Montel's avatar
Laurent Montel committed
846 847 848
        } else {
            ct++;
        }
849 850 851 852
        filter = sourceService.filter(ct);
    }
}

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
853 854 855
void GLWidget::stopCapture()
{
    if (strcmp(m_consumer->get("mlt_service"), "multi") == 0) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
856
        m_consumer->set("refresh", 0);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
857 858 859 860
        m_consumer->purge();
        m_consumer->stop();
    }
}
861

Laurent Montel's avatar
Laurent Montel committed
862
int GLWidget::reconfigureMulti(const QString &params, const QString &path, Mlt::Profile *profile)
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
863 864 865
{
    QString serviceName = property("mlt_service").toString();
    if (!m_consumer || !m_consumer->is_valid() || strcmp(m_consumer->get("mlt_service"), "multi") != 0) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
866 867 868 869 870 871
        if (m_consumer) {
            m_consumer->purge();
            m_consumer->stop();
            delete m_consumer;
        }
        m_consumer = new Mlt::FilteredConsumer(*profile, "multi");
Laurent Montel's avatar
Laurent Montel committed
872
        delete m_threadStartEvent;
Laurent Montel's avatar
Laurent Montel committed
873
        m_threadStartEvent = nullptr;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
874
        delete m_threadStopEvent;
Laurent Montel's avatar
Laurent Montel committed
875
        m_threadStopEvent = nullptr;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
876 877 878 879 880 881 882 883 884

        delete m_threadCreateEvent;
        delete m_threadJoinEvent;
        if (m_consumer) {
            m_threadCreateEvent = m_consumer->listen("consumer-thread-create", this, (mlt_listener) onThreadCreate);
            m_threadJoinEvent = m_consumer->listen("consumer-thread-join", this, (mlt_listener) onThreadJoin);
        }
    }
    if (m_consumer->is_valid()) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
885 886 887 888 889
        // buid sub consumers
        //m_consumer->set("mlt_image_format", "yuv422");
        reloadProfile(*profile);
        int volume = KdenliveSettings::volume();
        m_consumer->set("0", serviceName.toUtf8().constData());
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
890 891
        m_consumer->set("0.mlt_image_format", "yuv422");
        m_consumer->set("0.terminate_on_pause", 0);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
892 893 894
        //m_consumer->set("0.preview_off", 1);
        m_consumer->set("0.real_time", 0);
        m_consumer->set("0.volume", (double)volume / 100);
895

896
        if (serviceName.startsWith(QLatin1String("sdl"))) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
897
#ifdef Q_OS_WIN
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
898
            m_consumer->set("0.audio_buffer", 2048);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
899
#else
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
900
            m_consumer->set("0.audio_buffer", 512);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
901
#endif
902
            QString audioDevice = KdenliveSettings::audiodevicename();
Laurent Montel's avatar
Laurent Montel committed
903
            if (!audioDevice.isEmpty()) {
904
                m_consumer->set("audio_device", audioDevice.toUtf8().constData());
Laurent Montel's avatar
Laurent Montel committed
905
            }
906 907

            QString audioDriver = KdenliveSettings::audiodrivername();
Laurent Montel's avatar
Laurent Montel committed
908
            if (!audioDriver.isEmpty()) {
909
                m_consumer->set("audio_driver", audioDriver.toUtf8().constData());
Laurent Montel's avatar
Laurent Montel committed
910
            }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
911
        }
912

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
913 914 915 916 917
        m_consumer->set("1", "avformat");
        m_consumer->set("1.target", path.toUtf8().constData());
        //m_consumer->set("1.real_time", -KdenliveSettings::mltthreads());
        m_consumer->set("terminate_on_pause", 0);
        m_consumer->set("1.terminate_on_pause", 0);
918

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
919 920 921
        //m_consumer->set("1.terminate_on_pause", 0);// was commented out. restoring it  fixes mantis#3415 - FFmpeg recording freezes
        QStringList paramList = params.split(' ', QString::SkipEmptyParts);
        for (int i = 0; i < paramList.count(); ++i) {
922 923
            QString key = "1." + paramList.at(i).section(QLatin1Char('='), 0, 0);
            QString value = paramList.at(i).section(QLatin1Char('='), 1, 1);
Laurent Montel's avatar
Laurent Montel committed
924 925 926
            if (value == QLatin1String("%threads")) {
                value = QString::number(QThread::idealThreadCount());
            }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
927
            m_consumer->set(key.toUtf8().constData(), value.toUtf8().constData());
928
        }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
929
        // Connect the producer to the consumer - tell it to "run" later
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
930
        delete m_displayEvent;
931
        if (m_glslManager) {
932 933
            if (m_openGLSync) {
                m_displayEvent = m_consumer->listen("consumer-frame-show", this, (mlt_listener) on_gl_frame_show);
Laurent Montel's avatar
Laurent Montel committed
934
            } else {
935 936
                m_displayEvent = m_consumer->listen("consumer-frame-show", this, (mlt_listener) on_gl_nosync_frame_show);
            }
937 938 939
        } else {
            m_displayEvent = m_consumer->listen("consumer-frame-show", this, (mlt_listener) on_frame_show);
        }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
940
        m_consumer->connect(*m_producer);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
941 942
        m_consumer->start();
        return 0;
Laurent Montel's avatar
Laurent Montel committed
943 944
    } else {
        return -1;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
945 946 947 948
    }
}

int GLWidget::reconfigure(Mlt::Profile *profile)
949 950 951 952
{
    int error = 0;
    // use SDL for audio, OpenGL for video
    QString serviceName = property("mlt_service").toString();
Laurent Montel's avatar
Laurent Montel committed
953 954 955 956
    if (profile) {
        reloadProfile(*profile);
    }
    if (!m_consumer || !m_consumer->is_valid() || strcmp(m_consumer->get("mlt_service"), "multi") == 0) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
957 958 959 960 961
        if (m_consumer) {
            m_consumer->purge();
            m_consumer->stop();
            delete m_consumer;
        }
962
        // Force rtaudio backend for movit, because with SDL it crashes on stop/start
Laurent Montel's avatar
Laurent Montel committed
963
        //QString audioBackend = m_glslManager == nullptr ? KdenliveSettings::audiobackend() : QStringLiteral("rtaudio");
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
964
        QString audioBackend = KdenliveSettings::audiobackend();
965
        if (serviceName.isEmpty() || serviceName != audioBackend) {
966
            m_consumer = new Mlt::FilteredConsumer(*m_monitorProfile, audioBackend.toLatin1().constData());
Laurent Montel's avatar
Laurent Montel committed
967
            if (m_consumer->is_valid()) {
968
                serviceName = audioBackend;
969
                setProperty("mlt_service", serviceName);
Laurent Montel's avatar
Laurent Montel committed
970
            } else {
971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999
                // Warning, audio backend unavailable on system
                delete m_consumer;
                m_consumer = nullptr;
                QStringList backends = {"sdl2_audio", "sdl_audio", "rtaudio"};
                for (const QString &bk : backends) {
                    if (bk == audioBackend) {
                        // Already tested
                        continue;
                    }
                    m_consumer = new Mlt::FilteredConsumer(*m_monitorProfile, bk.toLatin1().constData());
                    if (m_consumer->is_valid()) {
                        if (audioBackend == KdenliveSettings::sdlAudioBackend()) {
                            // switch sdl audio backend
                            KdenliveSettings::setSdlAudioBackend(bk);
                        }
                        qDebug()<<"++++++++\nSwitching audio backend to: "<<bk<<"\n++++++++++";
                        KdenliveSettings::setAudiobackend(bk);
                        serviceName = bk;
                        setProperty("mlt_service", serviceName);
                        break;
                    } else {
                        delete m_consumer;
                        m_consumer = nullptr;
                    }
                }
                if (!m_consumer) {
                    qWarning() << "WARNING, NO AUDIO BACKEND FOUND";
                    return -1;
                }
1000
            }
1001 1002
        }
        delete m_threadStartEvent;
Laurent Montel's avatar
Laurent Montel committed
1003
        m_threadStartEvent = nullptr;
1004
        delete m_threadStopEvent;
Laurent Montel's avatar
Laurent Montel committed
1005
        m_threadStopEvent = nullptr;
1006 1007 1008

        delete m_threadCreateEvent;
        delete m_threadJoinEvent;
1009
        if (m_consumer) {
1010
            int dropFrames = realTime();
Laurent Montel's avatar
Laurent Montel committed
1011 1012 1013
            if (!KdenliveSettings::monitor_dropframes()) {
                dropFrames = -dropFrames;
            }
1014 1015 1016 1017
            m_consumer->set("real_time", dropFrames);
            m_threadCreateEvent = m_consumer->listen("consumer-thread-create", this, (mlt_listener) onThreadCreate);
            m_threadJoinEvent = m_consumer->listen("consumer-thread-join", this, (mlt_listener) onThreadJoin);
        }
1018 1019 1020
    }
    if (m_consumer->is_valid()) {
        // Connect the producer to the consumer - tell it to "run" later
Laurent Montel's avatar
Laurent Montel committed
1021 1022 1023
        if (m_producer) {
            m_consumer->connect(*m_producer);
        }
1024
        if (m_glslManager) {
Laurent Montel's avatar
Laurent Montel committed
1025
            if (!m_threadStartEvent) {
1026
                m_threadStartEvent = m_consumer->listen("consumer-thread-started", this, (mlt_listener) onThreadStarted);
Laurent Montel's avatar
Laurent Montel committed
1027 1028
            }
            if (!m_threadStopEvent) {
1029
                m_threadStopEvent = m_consumer->listen("consumer-thread-stopped", this, (mlt_listener) onThreadStopped);
Laurent Montel's avatar
Laurent Montel committed
1030 1031
            }
            if (!serviceName.startsWith(QLatin1String("decklink"))) {
1032
                m_consumer->set("mlt_image_format", "glsl");
Laurent Montel's avatar
Laurent Montel committed
1033
            }
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044
        } else {
            m_consumer->set("mlt_image_format", "yuv422");
        }

        delete m_displayEvent;
        if (m_glslManager) {
            m_displayEvent = m_consumer->listen("consumer-frame-show", this, (mlt_listener) on_gl_frame_show);
        } else {
            m_displayEvent = m_consumer->listen("consumer-frame-show", this, (mlt_listener) on_frame_show);
        }

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
1045
        int volume = KdenliveSettings::volume();
1046
        if (serviceName.startsWith(QLatin1String("sdl"))) {
1047
            QString audioDevice = KdenliveSettings::audiodevicename();
Laurent Montel's avatar
Laurent Montel committed
1048
            if (!audioDevice.isEmpty()) {
1049
                m_consumer->set("audio_device", audioDevice.toUtf8().constData());
Laurent Montel's avatar
Laurent Montel committed
1050
            }
1051 1052

            QString audioDriver = KdenliveSettings::audiodrivername();
Laurent Montel's avatar
Laurent Montel committed
1053
            if (!audioDriver.isEmpty()) {