]> git.sesse.net Git - kdenlive/blobdiff - src/renderer.cpp
Complete rewrite of the video4linux capture to use MLT, in progress.
[kdenlive] / src / renderer.cpp
index 91bf29bcfbb1490304bcc60796d632bdef157651..e6324a1f4d01a3201271f9a41557c1f0a67d5044 100644 (file)
@@ -94,10 +94,9 @@ static void consumer_gl_frame_show(mlt_consumer, Render * self, mlt_frame frame_
 }
 
 Render::Render(const QString & rendererName, int winid, QString profile, QWidget *parent) :
-    QObject(parent),
+    AbstractRender(parent),
     m_isBlocked(0),
     analyseAudio(KdenliveSettings::monitor_audio()),
-    sendFrameForAnalysis(false),
     m_name(rendererName),
     m_mltConsumer(NULL),
     m_mltProducer(NULL),
@@ -122,6 +121,7 @@ Render::~Render()
 {
     m_isBlocked = 1;
     closeMlt();
+    delete m_mltProfile;
 }
 
 
@@ -156,6 +156,8 @@ void Render::closeMlt()
                 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
                 if (trackPlaylist.type() == playlist_type) trackPlaylist.clear();
             }
+            delete field;
+            field = NULL;
         }
         mlt_service_unlock(service.get_service());
     }
@@ -181,7 +183,9 @@ void Render::buildConsumer(const QString profileName)
     m_blackClip = NULL;
 
     //TODO: uncomment following line when everything is clean
-    //if (m_mltProfile) delete m_mltProfile;
+    // uncommented Feb 2011 --Granjow
+    if (m_mltProfile) delete m_mltProfile;
+
     m_mltProfile = new Mlt::Profile(tmp);
     m_mltProfile->get_profile()->is_explicit = 1;
     delete[] tmp;
@@ -193,22 +197,25 @@ void Render::buildConsumer(const QString profileName)
     if (KdenliveSettings::external_display() && m_name != "clip") {
         // Use blackmagic card for video output
         QMap< QString, QString > profileProperties = ProfilesDialog::getSettingsFromFile(profileName);
-        if (BMInterface::isSupportedProfile(KdenliveSettings::blackmagic_output_device(), profileProperties)) {
-            QString decklink = "decklink:" + QString::number(KdenliveSettings::blackmagic_output_device());
-            tmp = qstrdup(decklink.toUtf8().constData());
-            m_mltConsumer = new Mlt::Consumer(*m_mltProfile, tmp);
-            delete[] tmp;
-            if (m_mltConsumer->is_valid()) {
-                m_externalConsumer = true;
-                m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_frame_show);
-                m_mltConsumer->set("terminate_on_pause", 0);
-                m_mltConsumer->set("buffer", 12);
-                m_mltConsumer->set("deinterlace_method", "onefield");
-                m_mltConsumer->set("real_time", KdenliveSettings::mltthreads());
-                mlt_log_set_callback(kdenlive_callback);
-            }
-            if (m_mltConsumer && m_mltConsumer->is_valid()) return;
-        } else KMessageBox::informationList(qApp->activeWindow(), i18n("Your project's profile %1 is not compatible with the blackmagic output card. Please see supported profiles below. Switching to normal video display.", m_mltProfile->description()), BMInterface::supportedModes(KdenliveSettings::blackmagic_output_device()));
+        int device = KdenliveSettings::blackmagic_output_device();
+        if (device >= 0) {
+            if (BMInterface::isSupportedProfile(device, profileProperties)) {
+                QString decklink = "decklink:" + QString::number(KdenliveSettings::blackmagic_output_device());
+                tmp = qstrdup(decklink.toUtf8().constData());
+                m_mltConsumer = new Mlt::Consumer(*m_mltProfile, tmp);
+                delete[] tmp;
+                if (m_mltConsumer->is_valid()) {
+                    m_externalConsumer = true;
+                    m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_frame_show);
+                    m_mltConsumer->set("terminate_on_pause", 0);
+                    m_mltConsumer->set("buffer", 12);
+                    m_mltConsumer->set("deinterlace_method", "onefield");
+                    m_mltConsumer->set("real_time", KdenliveSettings::mltthreads());
+                    mlt_log_set_callback(kdenlive_callback);
+                }
+                if (m_mltConsumer && m_mltConsumer->is_valid()) return;
+            } else KMessageBox::informationList(qApp->activeWindow(), i18n("Your project's profile %1 is not compatible with the blackmagic output card. Please see supported profiles below. Switching to normal video display.", m_mltProfile->description()), BMInterface::supportedModes(KdenliveSettings::blackmagic_output_device()));
+        }
     }
     m_externalConsumer = false;
     QString videoDriver = KdenliveSettings::videodrivername();
@@ -398,14 +405,26 @@ int Render::renderHeight() const
     return m_mltProfile->height();
 }
 
-QImage Render::extractFrame(int frame_position, int width, int height)
+QImage Render::extractFrame(int frame_position, QString path, int width, int height)
 {
     if (width == -1) {
         width = renderWidth();
         height = renderHeight();
     } else if (width % 2 == 1) width++;
 
-    if (!m_mltProducer) {
+    if (!path.isEmpty()) {
+        Mlt::Producer *producer = new Mlt::Producer(*m_mltProfile, path.toUtf8().constData());
+        if (producer) {
+            if (producer->is_valid()) {
+                QImage img = KThumb::getFrame(producer, frame_position, width, height);
+                delete producer;
+                return img;
+            }
+            else delete producer;
+        }
+    }
+    
+    if (!m_mltProducer || !path.isEmpty()) {
         QImage pix(width, height, QImage::Format_RGB32);
         pix.fill(Qt::black);
         return pix;
@@ -537,10 +556,17 @@ void Render::slotSplitView(bool doit)
 void Render::getFileProperties(const QDomElement xml, const QString &clipId, int imageHeight, bool replaceProducer, bool selectClip)
 {
     QString path;
-    if (xml.hasAttribute("proxy") && xml.attribute("proxy") != "-") path = xml.attribute("proxy");
-    else path = xml.attribute("resource");
-    
-    
+    bool proxyProducer;
+    if (xml.hasAttribute("proxy") && xml.attribute("proxy") != "-") {
+        path = xml.attribute("proxy");
+        proxyProducer = true;
+    }
+    else {
+        path = xml.attribute("resource");
+        proxyProducer = false;
+    }
+
+
     KUrl url = KUrl(path);
     Mlt::Producer *producer = NULL;
     CLIPTYPE type = (CLIPTYPE)xml.attribute("type").toInt();
@@ -568,17 +594,28 @@ void Render::getFileProperties(const QDomElement xml, const QString &clipId, int
         producer = new Mlt::Producer(*m_mltProfile, url.path().toUtf8().constData());
     }
 
+
     if (producer == NULL || producer->is_blank() || !producer->is_valid()) {
         kDebug() << " / / / / / / / / ERROR / / / / // CANNOT LOAD PRODUCER: ";
-        if (xml.hasAttribute("proxy") && xml.attribute("proxy") != "-") {
+        if (proxyProducer) {
             // Proxy file is corrupted
-            emit removeInvalidProxy(clipId);
+            emit removeInvalidProxy(clipId, false);
         }
         else emit removeInvalidClip(clipId, replaceProducer);
         delete producer;
         return;
     }
 
+    if (proxyProducer && xml.hasAttribute("proxy_out")) {
+        producer->set("out", xml.attribute("proxy_out").toInt());
+        if (producer->get_out() != xml.attribute("proxy_out").toInt()) {
+            // Proxy file length is different than original clip length, this will corrupt project so disable this proxy clip
+            emit removeInvalidProxy(clipId, true);
+            delete producer;
+            return;
+        }
+    }
+
     if (xml.hasAttribute("force_aspect_ratio")) {
         double aspect = xml.attribute("force_aspect_ratio").toDouble();
         if (aspect > 0) producer->set("force_aspect_ratio", aspect);
@@ -628,11 +665,11 @@ void Render::getFileProperties(const QDomElement xml, const QString &clipId, int
         int full_luma = xml.attribute("full_luma").toInt();
         if (full_luma != 0) producer->set("set.force_full_luma", full_luma);
     }
-    
+
     int clipOut = 0;
     int duration = 0;
     if (xml.hasAttribute("out")) clipOut = 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) {
         int length;
@@ -735,22 +772,35 @@ void Render::getFileProperties(const QDomElement xml, const QString &clipId, int
             else
                 filePropertyMap["type"] = "video";
 
+            int variance;
             mlt_image_format format = mlt_image_rgb24a;
             int frame_width = width;
             int frame_height = imageHeight;
-            uint8_t *data = frame->get_image(format, frame_width, frame_height, 0);
-            QImage image((uchar *)data, frame_width, frame_height, QImage::Format_ARGB32_Premultiplied);
             QPixmap pix;
-
-            if (!image.isNull()) {
-                if (frame_width > (2 * width)) {
-                    // there was a scaling problem, do it manually
-                    QImage scaled = image.scaled(width, imageHeight);
-                    pix = QPixmap::fromImage(scaled.rgbSwapped());
-                } else pix = QPixmap::fromImage(image.rgbSwapped());
-            } else
-                pix.fill(Qt::black);
-
+            do {
+                variance = 100;
+                uint8_t *data = frame->get_image(format, frame_width, frame_height, 0);
+                QImage image((uchar *)data, frame_width, frame_height, QImage::Format_ARGB32_Premultiplied);
+
+                if (!image.isNull()) {
+                    if (frame_width > (2 * width)) {
+                        // there was a scaling problem, do it manually
+                        QImage scaled = image.scaled(width, imageHeight);
+                        pix = QPixmap::fromImage(scaled.rgbSwapped());
+                    } else pix = QPixmap::fromImage(image.rgbSwapped());
+                    variance = KThumb::imageVariance(image);
+                } else
+                    pix.fill(Qt::black);
+
+                if (frameNumber == 0 && variance < 6) {
+                    // Thumbnail is not interesting (for example all black, seek to fetch better thumb
+                    frameNumber = 100;
+                    producer->seek(frameNumber);
+                    delete frame;
+                    frame = producer->get_frame();
+                    variance = -1;
+                }
+            } while (variance == -1);
             emit replyGetImage(clipId, pix);
 
         } else if (frame->get_int("test_audio") == 0) {
@@ -1216,7 +1266,6 @@ void Render::stop()
     if (m_mltProducer == NULL) return;
     if (m_mltConsumer && !m_mltConsumer->is_stopped()) {
         kDebug() << "/////////////   RENDER STOPPED: " << m_name;
-        m_isBlocked = true;
         //m_mltConsumer->set("refresh", 0);
         m_mltConsumer->stop();
         // delete m_mltConsumer;
@@ -1599,7 +1648,7 @@ Mlt::Producer *Render::checkSlowMotionProducer(Mlt::Producer *prod, QDomElement
     if (strobe > 1) url.append("&strobe=" + QString::number(strobe));
     Mlt::Producer *slowprod = m_slowmotionProducers.value(url);
     if (!slowprod || slowprod->get_producer() == NULL) {
-        slowprod = new Mlt::Producer(*m_mltProfile, "framebuffer", url.toUtf8().constData());
+        slowprod = new Mlt::Producer(*m_mltProfile, 0, ("framebuffer:" + url).toUtf8().constData());
         if (strobe > 1) slowprod->set("strobe", strobe);
         QString id = prod->get("id");
         if (id.contains('_')) id = id.section('_', 0, 0);
@@ -2428,7 +2477,6 @@ bool Render::mltAddEffect(Mlt::Service service, EffectsParameterList params, int
     // create filter
     QString tag =  params.paramValue("tag");
     kDebug() << " / / INSERTING EFFECT: " << tag << ", REGI: " << region;
-    if (tag.startsWith("ladspa")) tag = "ladspa";
     char *filterTag = qstrdup(tag.toUtf8().constData());
     char *filterId = qstrdup(params.paramValue("id").toUtf8().constData());
     QHash<QString, QString>::Iterator it;
@@ -2610,10 +2658,12 @@ bool Render::mltEditEffect(int track, GenTime position, EffectsParameterList par
     if (!params.paramValue("keyframes").isEmpty() || /*it.key().startsWith("#") || */tag.startsWith("ladspa") || tag == "sox" || tag == "autotrack_rectangle" || params.hasParam("region")) {
         // This is a keyframe effect, to edit it, we remove it and re-add it.
         bool success = mltRemoveEffect(track, position, index, false);
-        if (!success) kDebug() << "// ERROR Removing effect : " << index;
-        if (position < GenTime()) success = mltAddTrackEffect(track, params);
-        else success = mltAddEffect(track, position, params);
-        if (!success) kDebug() << "// ERROR Adding effect : " << index;
+//         if (!success) kDebug() << "// ERROR Removing effect : " << index;
+        if (position < GenTime())
+            success = mltAddTrackEffect(track, params);
+        else
+            success = mltAddEffect(track, position, params);
+//         if (!success) kDebug() << "// ERROR Adding effect : " << index;
         return success;
     }
     if (position < GenTime()) {
@@ -2632,33 +2682,22 @@ bool Render::mltEditEffect(int track, GenTime position, EffectsParameterList par
         return false;
     }
 
-    Mlt::Service clipService(clip->get_service());
     int duration = clip->get_playtime();
     bool doRefresh = true;
     // Check if clip is visible in monitor
     int diff = trackPlaylist.clip_start(clipIndex) + duration - m_mltProducer->position();
-    if (diff < 0 || diff > duration) doRefresh = false;
-    delete clip;
+    if (diff < 0 || diff > duration)
+        doRefresh = false;
     m_isBlocked = true;
     int ct = 0;
-    /* kDebug() << "EDITING FILTER: "<<index <<", "<<tag;
-    kDebug() << "EFFect stack: ++++++++++++++++++++++++++";
-    while (filter) {
-        kDebug() << "Filter: "<< filter->get("kdenlive_id") <<", IX: "<<filter->get("kdenlive_ix");
-        ct++;
-        filter = clipService.filter(ct);
-    }
-    kDebug() << "++++++++++++++++++++++++++";
-    ct = 0;
-    filter = clipService.filter(ct); */
 
-    Mlt::Filter *filter = clipService.filter(ct);
+    Mlt::Filter *filter = clip->filter(ct);
     while (filter) {
         if (filter->get_int("kdenlive_ix") == index.toInt()) {
             break;
         }
         ct++;
-        filter = clipService.filter(ct);
+        filter = clip->filter(ct);
     }
 
     if (!filter) {
@@ -2676,6 +2715,11 @@ bool Render::mltEditEffect(int track, GenTime position, EffectsParameterList par
     for (int j = 0; j < params.count(); j++) {
         filter->set((prefix + params.at(j).name()).toUtf8().constData(), params.at(j).value().toUtf8().constData());
     }
+
+    if (params.paramValue("id") == "pan_zoom")
+        filter->set_in_and_out(clip->get_in(), clip->get_out() + 1);
+
+    delete clip;
     mlt_service_unlock(service.get_service());
 
     m_isBlocked = false;
@@ -3028,7 +3072,7 @@ void Render::fixAudioMixing(Mlt::Tractor tractor)
         Mlt::Transition *transition = new Mlt::Transition(*m_mltProfile, "mix");
         transition->set("always_active", 1);
         transition->set("combine", 1);
-        transition->set("internal_added", 238);
+        transition->set("internal_added", 237);
         field->plant_transition(*transition, lowestTrack, i);
     }
     mlt_service_unlock(serv);
@@ -3573,8 +3617,8 @@ QMap<QString, QString> Render::mltGetTransitionParamsFromXml(QDomElement xml)
         if (!e.attribute("value").isEmpty()) {
             map[name] = e.attribute("value");
         }
-        if (!e.attribute("factor").isEmpty() && e.attribute("factor").toDouble() > 0) {
-            map[name] = QString::number(map[name].toDouble() / e.attribute("factor").toDouble());
+        if (e.attribute("type") != "addedgeometry" && !e.attribute("factor").isEmpty() && e.attribute("factor").toDouble() > 0) {
+            map[name] = QString::number(map.value(name).toDouble() / e.attribute("factor").toDouble());
             //map[name]=map[name].replace(".",","); //FIXME how to solve locale conversion of . ,
         }
 
@@ -4118,6 +4162,11 @@ void Render::sendFrameUpdate()
     }
 }
 
+Mlt::Producer* Render::getProducer()
+{
+    return m_mltProducer;
+}
+
 
 #include "renderer.moc"