]> git.sesse.net Git - kdenlive/blobdiff - src/renderer.cpp
Fix jittering issues when sending GL textures cross-thread.
[kdenlive] / src / renderer.cpp
index 08a011f6bb12ca56ab0fbaf42ee43454446950c0..880a9653b21d3171ae16d3946057bb17a3fae283 100644 (file)
@@ -22,7 +22,6 @@
  *                                                                         *
  ***************************************************************************/
 
-
 #include "renderer.h"
 #include "kdenlivesettings.h"
 #include "kthumb.h"
@@ -35,7 +34,7 @@
 #include <KDebug>
 #include <KStandardDirs>
 #include <KMessageBox>
-#include <KLocale>
+#include <KLocalizedString>
 #include <KTemporaryFile>
 
 #include <QTimer>
 #include <QString>
 #include <QApplication>
 #include <QtConcurrentRun>
+#include <QGLWidget>
 
 #include <cstdlib>
 #include <cstdarg>
 
 #include <QDebug>
+#include <assert.h>
 
 #define SEEK_INACTIVE (-1)
 
@@ -81,6 +82,32 @@ void Render::consumer_frame_show(mlt_consumer, Render * self, mlt_frame frame_pt
     }
 }
 
+void Render::consumer_thread_started(mlt_consumer, Render * self, mlt_frame)
+{
+    pthread_t thr = pthread_self();
+    if (self->m_renderThreadGLContexts.count(thr) == 0) {
+        QGLWidget *ctx = new QGLWidget(0, self->m_mainGLContext);
+        ctx->resize(0, 0);
+        self->m_renderThreadGLContexts.insert(thr, ctx);
+    }
+    self->m_renderThreadGLContexts[thr]->makeCurrent();
+    self->m_glslManager->fire_event("init glsl");
+    if (!self->m_glslManager->get_int("glsl_supported")) {
+        QMessageBox::critical(NULL, i18n("Movit failed initialization"),
+                              i18n("Initialization of OpenGL filters failed. Exiting."));
+        qApp->quit();
+    }
+}
+
+void Render::consumer_thread_stopped(mlt_consumer, Render * self, mlt_frame)
+{
+    pthread_t thr = pthread_self();
+    assert(self->m_renderThreadGLContexts.count(thr) != 0);
+    self->m_renderThreadGLContexts[thr]->makeCurrent();
+    delete self->m_renderThreadGLContexts[thr];
+    self->m_renderThreadGLContexts.remove(thr);
+}
+
 /*
 static void consumer_paused(mlt_consumer, Render * self, mlt_frame frame_ptr)
 {
@@ -111,7 +138,7 @@ void Render::consumer_gl_frame_show(mlt_consumer consumer, Render * self, mlt_fr
     emit self->mltFrameReceived(new Mlt::Frame(frame_ptr));
 }
 
-Render::Render(Kdenlive::MONITORID rendererName, int winid, QString profile, QWidget *parent) :
+Render::Render(Kdenlive::MonitorId rendererName, int winid, QString profile, QWidget *parent, QGLWidget *mainGLContext) :
     AbstractRender(rendererName, parent),
     requestedSeekPosition(SEEK_INACTIVE),
     showFrameSemaphore(1),
@@ -121,6 +148,8 @@ Render::Render(Kdenlive::MONITORID rendererName, int winid, QString profile, QWi
     m_mltProducer(NULL),
     m_mltProfile(NULL),
     m_showFrameEvent(NULL),
+    m_consumerThreadStartedEvent(NULL),
+    m_consumerThreadStoppedEvent(NULL),
     m_pauseEvent(NULL),
     m_isZoneMode(false),
     m_isLoopMode(false),
@@ -128,7 +157,9 @@ Render::Render(Kdenlive::MONITORID rendererName, int winid, QString profile, QWi
     m_blackClip(NULL),
     m_winid(winid),
     m_paused(true),
-    m_isActive(false)
+    m_isActive(false),
+    m_mainGLContext(mainGLContext),
+    m_GLContext(NULL)
 {
     qRegisterMetaType<stringMap> ("stringMap");
     analyseAudio = KdenliveSettings::monitor_audio();
@@ -140,16 +171,21 @@ Render::Render(Kdenlive::MONITORID rendererName, int winid, QString profile, QWi
     m_mltProducer->set_speed(0.0);
     m_refreshTimer.setSingleShot(true);
     m_refreshTimer.setInterval(100);
+    m_glslManager = new Mlt::Filter(*m_mltProfile, "glsl.manager");
     connect(&m_refreshTimer, SIGNAL(timeout()), this, SLOT(refresh()));
     connect(this, SIGNAL(multiStreamFound(QString,QList<int>,QList<int>,stringMap)), this, SLOT(slotMultiStreamProducerFound(QString,QList<int>,QList<int>,stringMap)));
     connect(this, SIGNAL(checkSeeking()), this, SLOT(slotCheckSeeking()));
     connect(this, SIGNAL(mltFrameReceived(Mlt::Frame*)), this, SLOT(showFrame(Mlt::Frame*)), Qt::UniqueConnection);
+
+    m_GLContext = new QGLWidget(0, m_mainGLContext);
+    m_GLContext->resize(0, 0);
 }
 
 Render::~Render()
 {
     closeMlt();
     delete m_mltProfile;
+    delete m_GLContext;
 }
 
 
@@ -159,6 +195,8 @@ void Render::closeMlt()
     m_requestList.clear();
     m_infoThread.waitForFinished();
     delete m_showFrameEvent;
+    delete m_consumerThreadStartedEvent;
+    delete m_consumerThreadStoppedEvent;
     delete m_pauseEvent;
     delete m_mltConsumer;
     delete m_mltProducer;
@@ -227,7 +265,7 @@ void Render::buildConsumer(const QString &profileName)
     m_blackClip = new Mlt::Producer(*m_mltProfile, "colour:black");
     m_blackClip->set("id", "black");
     m_blackClip->set("mlt_type", "producer");
-    if (KdenliveSettings::external_display() && m_name != Kdenlive::clipMonitor && m_winid != 0) {
+    if (KdenliveSettings::external_display() && m_name != Kdenlive::ClipMonitor && m_winid != 0) {
         // Use blackmagic card for video output
         int device = KdenliveSettings::blackmagic_output_device();
         if (device >= 0) {
@@ -267,7 +305,7 @@ void Render::buildConsumer(const QString &profileName)
     if (m_winid == 0) {
         // OpenGL monitor
         if (!m_mltConsumer) {
-            if (KdenliveSettings::external_display() && m_name != Kdenlive::clipMonitor) {
+            if (KdenliveSettings::external_display() && m_name != Kdenlive::ClipMonitor) {
                 int device = KdenliveSettings::blackmagic_output_device();
                 if (device >= 0) {
                     QString decklink = "decklink:" + QString::number(KdenliveSettings::blackmagic_output_device());
@@ -285,7 +323,9 @@ void Render::buildConsumer(const QString &profileName)
                 m_mltConsumer->set("scrub_audio", 1);
                 m_mltConsumer->set("preview_off", 1);
                 m_mltConsumer->set("audio_buffer", 512);
-                m_mltConsumer->set("preview_format", mlt_image_rgb24a);
+                m_mltConsumer->set("mlt_image_format", "glsl");
+                m_consumerThreadStartedEvent = m_mltConsumer->listen("consumer-thread-started", this, (mlt_listener) consumer_thread_started);
+                m_consumerThreadStoppedEvent = m_mltConsumer->listen("consumer-thread-stopped", this, (mlt_listener) consumer_thread_stopped);
             }
             m_mltConsumer->set("buffer", "1");
             m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_gl_frame_show);
@@ -704,6 +744,10 @@ bool Render::isProcessing(const QString &id)
 
 void Render::processFileProperties()
 {
+    // We are in a new thread, so we need a new OpenGL context for the remainder of the function.
+    QGLWidget ctx(0, m_mainGLContext);
+    ctx.makeCurrent();
+
     requestClipInfo info;
     QLocale locale;
     while (!m_requestList.isEmpty()) {
@@ -731,10 +775,10 @@ void Render::processFileProperties()
         }
         KUrl url(path);
         Mlt::Producer *producer = NULL;
-        CLIPTYPE type = (CLIPTYPE)info.xml.attribute("type").toInt();
-        if (type == COLOR) {
+        ClipType type = (ClipType)info.xml.attribute("type").toInt();
+        if (type == Color) {
             producer = new Mlt::Producer(*m_mltProfile, 0, ("colour:" + info.xml.attribute("colour")).toUtf8().constData());
-        } else if (type == TEXT) {
+        } else if (type == Text) {
             producer = new Mlt::Producer(*m_mltProfile, 0, ("kdenlivetitle:" + info.xml.attribute("resource")).toUtf8().constData());
             if (producer && producer->is_valid() && info.xml.hasAttribute("xmldata"))
                 producer->set("xmldata", info.xml.attribute("xmldata").toUtf8().constData());
@@ -837,7 +881,7 @@ void Render::processFileProperties()
         if (info.xml.hasAttribute("out")) clipOut = info.xml.attribute("out").toInt();
 
         // setup length here as otherwise default length (currently 15000 frames in MLT) will be taken even if outpoint is larger
-        if (type == COLOR || type == TEXT || type == IMAGE || type == SLIDESHOW) {
+        if (type == Color || type == Text || type == Image || type == SlideShow) {
             int length;
             if (info.xml.hasAttribute("length")) {
                 length = info.xml.attribute("length").toInt();
@@ -892,7 +936,7 @@ void Render::processFileProperties()
         filePropertyMap["duration"] = QString::number(duration);
         //kDebug() << "///////  PRODUCER: " << url.path() << " IS: " << producer->get_playtime();
 
-        if (type == SLIDESHOW) {
+        if (type == SlideShow) {
             int ttl = info.xml.hasAttribute("ttl") ? info.xml.attribute("ttl").toInt() : 0;
             if (ttl) producer->set("ttl", ttl);
             if (!info.xml.attribute("animation").isEmpty()) {
@@ -1249,6 +1293,10 @@ void Render::startConsumer() {
         KMessageBox::error(qApp->activeWindow(), i18n("Could not create the video preview window.\nThere is something wrong with your Kdenlive install or your driver settings, please fix it."));
         if (m_showFrameEvent) delete m_showFrameEvent;
         m_showFrameEvent = NULL;
+        if (m_consumerThreadStartedEvent) delete m_consumerThreadStartedEvent;
+        m_consumerThreadStartedEvent = NULL;
+        if (m_consumerThreadStoppedEvent) delete m_consumerThreadStoppedEvent;
+        m_consumerThreadStoppedEvent = NULL;
         if (m_pauseEvent) delete m_pauseEvent;
         m_pauseEvent = NULL;
         delete m_mltConsumer;
@@ -1413,6 +1461,7 @@ void Render::checkMaxThreads()
 
 const QString Render::sceneList()
 {
+    if (!m_mltProducer) return QString();
     QString playlist;
     Mlt::Profile profile((mlt_profile) 0);
     Mlt::Consumer xmlConsumer(profile, "xml:kdenlive_playlist");
@@ -1459,7 +1508,7 @@ void Render::saveZone(KUrl url, QString desc, QPoint zone)
     Mlt::Consumer xmlConsumer(*m_mltProfile, ("xml:" + url.path()).toUtf8().constData());
     m_mltProducer->optimise();
     xmlConsumer.set("terminate_on_pause", 1);
-    if (m_name == Kdenlive::clipMonitor) {
+    if (m_name == Kdenlive::ClipMonitor) {
         Mlt::Producer *prod = m_mltProducer->cut(zone.x(), zone.y());
         Mlt::Playlist list;
         list.insert_at(0, prod, 0);
@@ -1524,7 +1573,7 @@ void Render::slotSetVolume(int volume)
 {
     if (!m_mltConsumer || !m_mltProducer) return;
     m_mltProducer->set("meta.volume", (double)volume / 100.0);
-    return;
+    //return;
     /*osdTimer->stop();
     m_mltConsumer->set("refresh", 0);
     // Attach filter for on screen display of timecode
@@ -1538,7 +1587,7 @@ void Render::slotSetVolume(int volume)
     mlt_properties_set_int( properties, "meta.attr.timecode", 0);
      if (m_mltProducer->attach(*m_osdInfo) == 1) kDebug()<<"////// error attaching filter";
     }*/
-    refresh();
+    //refresh();
     //m_osdTimer->setSingleShot(2500);
 }
 
@@ -1632,7 +1681,7 @@ void Render::switchPlay(bool play)
         return;
     if (m_isZoneMode) resetZoneMode();
     if (play && m_paused) {
-        if (m_name == Kdenlive::clipMonitor && m_mltConsumer->position() == m_mltProducer->get_out()) m_mltProducer->seek(0);
+        if (m_name == Kdenlive::ClipMonitor && m_mltConsumer->position() == m_mltProducer->get_out()) m_mltProducer->seek(0);
         m_paused = false;
         m_mltProducer->set_speed(1.0);
         if (m_mltConsumer->is_stopped()) {
@@ -1813,12 +1862,12 @@ int Render::seekFramePosition() const
 
 void Render::emitFrameUpdated(Mlt::Frame& frame)
 {
-    mlt_image_format format = mlt_image_rgb24;
+    mlt_image_format format = mlt_image_rgb24a;
     int width = 0;
     int height = 0;
     const uchar* image = frame.get_image(format, width, height);
-    QImage qimage(width, height, QImage::Format_RGB888);  //Format_ARGB32_Premultiplied);
-    memcpy(qimage.scanLine(0), image, width * height * 3);
+    QImage qimage(width, height, QImage::Format_ARGB32_Premultiplied);
+    memcpy(qimage.scanLine(0), image, width * height * 4);
     emit frameUpdated(qimage);
 }
 
@@ -1896,18 +1945,25 @@ void Render::showFrame(Mlt::Frame* frame)
     if (currentPos == requestedSeekPosition) requestedSeekPosition = SEEK_INACTIVE;
     emit rendererPosition(currentPos);
     if (frame->is_valid()) {
-        mlt_image_format format = mlt_image_rgb24;
+        mlt_image_format format = mlt_image_glsl_texture;
         int width = 0;
         int height = 0;
-        const uchar* image = frame->get_image(format, width, height);
-        QImage qimage(width, height, QImage::Format_RGB888); //Format_ARGB32_Premultiplied);
-        memcpy(qimage.scanLine(0), image, width * height * 3);
-        if (analyseAudio) showAudio(*frame);
-        delete frame;
-        emit showImageSignal(qimage);
-        if (sendFrameForAnalysis) {
-            emit frameUpdated(qimage);
-        }
+        m_GLContext->makeCurrent();
+        frame->set("movit.convert.use_texture", 1);
+        const uint8_t* image = frame->get_image(format, width, height);
+        const GLuint* texnum = (GLuint *)image;
+        if (format == mlt_image_glsl_texture) {
+          emit showImageSignal(frame, *texnum);
+        } else {
+          QImage qimage(width, height, QImage::Format_ARGB32_Premultiplied);
+          memcpy(qimage.scanLine(0), image, width * height * 4);
+          if (analyseAudio) showAudio(*frame);
+          delete frame;
+          emit showImageSignal(qimage);
+          if (sendFrameForAnalysis) {
+              emit frameUpdated(qimage);
+          }
+       }
     } else delete frame;
     showFrameSemaphore.release();
     emit checkSeeking();
@@ -3037,7 +3093,7 @@ bool Render::mltEditTrackEffect(int track, EffectsParameterList params)
     return true;
 }
 
-bool Render::mltEditEffect(int track, GenTime position, EffectsParameterList params)
+bool Render::mltEditEffect(int track, const GenTime &position, EffectsParameterList params)
 {
     int index = params.paramValue("kdenlive_ix").toInt();
     QString tag =  params.paramValue("tag");
@@ -3117,7 +3173,8 @@ bool Render::mltEditEffect(int track, GenTime position, EffectsParameterList par
         delete clip;
         service.unlock();
 
-        if (doRefresh) refresh();
+        if (doRefresh)
+            refresh();
         return true;
     }
     if (params.hasParam("_sync_in_out")) {
@@ -3126,22 +3183,23 @@ bool Render::mltEditEffect(int track, GenTime position, EffectsParameterList par
         filter->set_in_and_out(clip->get_in(), clip->get_out());
     }
 
-    for (int j = 0; j < params.count(); j++) {
+    for (int j = 0; j < params.count(); ++j) {
         filter->set(params.at(j).name().toUtf8().constData(), params.at(j).value().toUtf8().constData());
     }
     
-    for (int j = 0; j < filtersList.count(); j++) {
+    for (int j = 0; j < filtersList.count(); ++j) {
         clip->attach(*(filtersList.at(j)));
     }
 
     delete clip;
     service.unlock();
 
-    if (doRefresh) refresh();
+    if (doRefresh)
+        refresh();
     return true;
 }
 
-bool Render::mltEnableEffects(int track, GenTime position, QList <int> effectIndexes, bool disable)
+bool Render::mltEnableEffects(int track, const GenTime &position, const QList <int> &effectIndexes, bool disable)
 {
     if (position < GenTime()) {
         return mltEnableTrackEffects(track, effectIndexes, disable);
@@ -3168,6 +3226,7 @@ bool Render::mltEnableEffects(int track, GenTime position, QList <int> effectInd
     int ct = 0;
 
     Mlt::Filter *filter = clip->filter(ct);
+    service.lock();
     while (filter) {
         if (effectIndexes.contains(filter->get_int("kdenlive_ix"))) {
             filter->set("disable", (int) disable);
@@ -3183,7 +3242,7 @@ bool Render::mltEnableEffects(int track, GenTime position, QList <int> effectInd
     return true;
 }
 
-bool Render::mltEnableTrackEffects(int track, QList <int> effectIndexes, bool disable)
+bool Render::mltEnableTrackEffects(int track, const QList <int> &effectIndexes, bool disable)
 {
     Mlt::Service service(m_mltProducer->parent().get_service());
     Mlt::Tractor tractor(service);
@@ -3193,6 +3252,7 @@ bool Render::mltEnableTrackEffects(int track, QList <int> effectIndexes, bool di
     int ct = 0;
 
     Mlt::Filter *filter = clipService.filter(ct);
+    service.lock();
     while (filter) {
         if (effectIndexes.contains(filter->get_int("kdenlive_ix"))) {
             filter->set("disable", (int) disable);
@@ -3206,7 +3266,7 @@ bool Render::mltEnableTrackEffects(int track, QList <int> effectIndexes, bool di
     return true;
 }
 
-void Render::mltUpdateEffectPosition(int track, GenTime position, int oldPos, int newPos)
+void Render::mltUpdateEffectPosition(int track, const GenTime &position, int oldPos, int newPos)
 {
     Mlt::Service service(m_mltProducer->parent().get_service());
     Mlt::Tractor tractor(service);
@@ -3240,7 +3300,7 @@ void Render::mltUpdateEffectPosition(int track, GenTime position, int oldPos, in
     if (doRefresh) refresh();
 }
 
-void Render::mltMoveEffect(int track, GenTime position, int oldPos, int newPos)
+void Render::mltMoveEffect(int track, const GenTime &position, int oldPos, int newPos)
 {
     if (position < GenTime()) {
         mltMoveTrackEffect(track, oldPos, newPos);
@@ -4088,7 +4148,7 @@ void Render::mltDeleteTransition(QString tag, int /*a_track*/, int b_track, GenT
     //if (m_isBlocked == 0) m_mltConsumer->set("refresh", 1);
 }
 
-QMap<QString, QString> Render::mltGetTransitionParamsFromXml(QDomElement xml)
+QMap<QString, QString> Render::mltGetTransitionParamsFromXml(const QDomElement &xml)
 {
     QDomNodeList attribs = xml.elementsByTagName("parameter");
     QMap<QString, QString> map;
@@ -4564,7 +4624,7 @@ void Render::updatePreviewSettings()
 }
 
 
-QString Render::updateSceneListFps(double current_fps, double new_fps, QString scene)
+QString Render::updateSceneListFps(double current_fps, double new_fps, const QString &scene)
 {
     // Update all frame positions to the new fps value
     //WARNING: there are probably some effects or other that hold a frame value
@@ -4731,7 +4791,7 @@ bool Render::getBlackMagicOutputDeviceList(KComboBox *devicelist, bool force)
     return true;
 }
 
-void Render::slotMultiStreamProducerFound(const QString path, QList<int> audio_list, QList<int> video_list, stringMap data)
+void Render::slotMultiStreamProducerFound(const QString &path, QList<int> audio_list, QList<int> video_list, stringMap data)
 { 
     if (KdenliveSettings::automultistreams()) {
         for (int i = 1; i < video_list.count(); ++i) {