]> git.sesse.net Git - kdenlive/blobdiff - src/docclipbase.cpp
Hide the "avformat-novalidate" trick for faster loading, caused crash:
[kdenlive] / src / docclipbase.cpp
index f75b0041fb373369068dd9e912ecefeb33293b2f..6004a1300d25e9d810501aeb785aac3c7880f473 100644 (file)
 #include "kdenlivesettings.h"
 #include "kthumb.h"
 #include "clipmanager.h"
+#include "slideshowclip.h"
 
 #include <KIO/NetAccess>
+#include <KStandardDirs>
 #include <KDebug>
 
 #include <QCryptographicHash>
+#include <QtConcurrentRun>
 
 #include <cstdio>
 
@@ -84,35 +87,17 @@ DocClipBase::DocClipBase(ClipManager *clipManager, QDomElement xml, const QStrin
 
     if (!m_properties.contains("name")) m_properties.insert("name", url.fileName());
 
-    //if (!url.isEmpty() && QFile::exists(url.path()))
-    {
-        m_thumbProd = new KThumb(clipManager, url, m_id, m_properties.value("file_hash"));
-        if (m_clipType == AV || m_clipType == AUDIO || m_clipType == PLAYLIST) slotCreateAudioTimer();
-    }
-    //kDebug() << "type is video" << (m_clipType == AV) << " " << m_clipType;
+    m_thumbProd = new KThumb(clipManager, url, m_id, m_properties.value("file_hash"));
+    if (m_clipType == AV || m_clipType == AUDIO || m_clipType == PLAYLIST) slotCreateAudioTimer();
 }
 
-/*DocClipBase & DocClipBase::operator=(const DocClipBase & clip) {
-    DocClipBase::operator=(clip);
-    m_id = clip.getId();
-    m_clipType = clip.clipType();
-    m_name = clip.name();
-    m_duration = clip.duration();
-    m_audioThumbCreated = clip.audioThumbCreated();
-    m_properties = clip.properties();
-    return *this;
-}*/
-
 DocClipBase::~DocClipBase()
 {
-    kDebug() << "CLIP " << m_id << " DELETED******************************";
     delete m_thumbProd;
     if (m_audioTimer) {
         m_audioTimer->stop();
         delete m_audioTimer;
     }
-    /*kDebug() <<" * * *CNT "<<m_baseTrackProducers.count();
-    if (m_baseTrackProducers.count() > 0) kDebug()<<"YOYO: "<<m_baseTrackProducers.at(0)->get_out()<<", CUT: "<<m_baseTrackProducers.at(0)->is_cut();*/
     qDeleteAll(m_baseTrackProducers);
     m_baseTrackProducers.clear();
     qDeleteAll(m_audioTrackProducers);
@@ -426,26 +411,9 @@ QString DocClipBase::markerComment(GenTime t)
     return QString();
 }
 
-void DocClipBase::clearProducers()
-{
-    m_baseTrackProducers.clear();
-}
-
 void DocClipBase::deleteProducers(bool clearThumbCreator)
 {
-    kDebug() << "// CLIP KILL PRODS ct: " << m_baseTrackProducers.count();
     if (clearThumbCreator && m_thumbProd) m_thumbProd->clearProducer();
-    /*kDebug()<<"// CLIP KILL PRODS ct: "<<m_baseTrackProducers.count();
-    int max = m_baseTrackProducers.count();
-    for (int i = 0; i < max; i++) {
-        kDebug()<<"// CLIP KILL PROD "<<i;
-    Mlt::Producer *p = m_baseTrackProducers.takeAt(i);
-    if (p != NULL) {
-     delete p;
-     p = NULL;
-    }
-    m_baseTrackProducers.insert(i, NULL);
-    }*/
 
     delete m_videoOnlyProducer;
     m_videoOnlyProducer = NULL;
@@ -461,16 +429,34 @@ void DocClipBase::setValid()
     m_placeHolder = false;
 }
 
-void DocClipBase::setProducer(Mlt::Producer *producer, bool reset)
+void DocClipBase::setProducer(Mlt::Producer *producer, bool reset, bool readPropertiesFromProducer)
 {
-    if (producer == NULL || (m_placeHolder && !reset)) return;
-    if (m_thumbProd && (reset || !m_thumbProd->hasProducer())) m_thumbProd->setProducer(producer);
+    if (producer == NULL) return;
+    if (reset) QMutexLocker locker(&m_producerMutex);
+    if (m_placeHolder || !producer->is_valid()) {
+        char *tmp = qstrdup(i18n("Missing clip").toUtf8().constData());
+        producer->set("markup", tmp);
+        producer->set("bgcolour", "0xff0000ff");
+        producer->set("pad", "10");
+        delete[] tmp;
+    }
+    QString id = producer->get("id");
+    if (m_thumbProd) {
+        if (reset) m_thumbProd->setProducer(NULL);
+        if (!m_thumbProd->hasProducer()) {
+            if (m_clipType != AUDIO) {
+                if (!id.endsWith("_audio"))
+                    m_thumbProd->setProducer(producer);
+            }
+            else m_thumbProd->setProducer(producer);
+        }
+    }
     if (reset) {
         // Clear all previous producers
         kDebug() << "/+++++++++++++++   DELETE ALL PRODS " << producer->get("id");
         deleteProducers(false);
     }
-    QString id = producer->get("id");
+    bool updated = false;
     if (id.contains('_')) {
         // this is a subtrack producer, insert it at correct place
         id = id.section('_', 1);
@@ -481,10 +467,16 @@ void DocClipBase::setProducer(Mlt::Producer *producer, bool reset)
                     m_audioTrackProducers.append(NULL);
                 }
             }
-            if (m_audioTrackProducers.at(pos) == NULL) m_audioTrackProducers[pos] = producer;
+            if (m_audioTrackProducers.at(pos) == NULL) {
+                m_audioTrackProducers[pos] = producer;
+                updated = true;
+            }
             return;
         } else if (id.endsWith("video")) {
-            m_videoOnlyProducer = producer;
+            if (m_videoOnlyProducer == NULL) {
+                m_videoOnlyProducer = producer;
+                updated = true;
+            }
             return;
         }
         int pos = id.toInt();
@@ -493,69 +485,113 @@ void DocClipBase::setProducer(Mlt::Producer *producer, bool reset)
                 m_baseTrackProducers.append(NULL);
             }
         }
-        if (m_baseTrackProducers.at(pos) == NULL) m_baseTrackProducers[pos] = producer;
+        if (m_baseTrackProducers.at(pos) == NULL) {
+            m_baseTrackProducers[pos] = producer;
+            updated = true;
+        }
     } else {
-        if (m_baseTrackProducers.isEmpty()) m_baseTrackProducers.append(producer);
-        else if (m_baseTrackProducers.at(0) == NULL) m_baseTrackProducers[0] = producer;
+        if (m_baseTrackProducers.isEmpty()) {
+            m_baseTrackProducers.append(producer);
+            updated = true;
+        }
+        else if (m_baseTrackProducers.at(0) == NULL) {
+            m_baseTrackProducers[0] = producer;
+            updated = true;
+        }
     }
-    //m_clipProducer = producer;
-    //m_clipProducer->set("transparency", m_properties.value("transparency").toInt());
+    if (updated && readPropertiesFromProducer && (m_clipType != COLOR && m_clipType != IMAGE && m_clipType != TEXT))
+        setDuration(GenTime(producer->get_length(), KdenliveSettings::project_fps()));
+}
+
+static double getPixelAspect(QMap<QString, QString>& props) {
+    int width = props.value("frame_size").section('x', 0, 0).toInt();
+    int height = props.value("frame_size").section('x', 1, 1).toInt();
+    int aspectNumerator = props.value("force_aspect_num").toInt();
+    int aspectDenominator = props.value("force_aspect_den").toInt();
+    if (aspectDenominator != 0 && width != 0)
+        return double(height) * aspectNumerator / aspectDenominator / width;    
+    else
+        return 1.0;
 }
 
 Mlt::Producer *DocClipBase::audioProducer(int track)
 {
+    QMutexLocker locker(&m_producerMutex);
     if (m_audioTrackProducers.count() <= track) {
         while (m_audioTrackProducers.count() - 1 < track) {
             m_audioTrackProducers.append(NULL);
         }
     }
     if (m_audioTrackProducers.at(track) == NULL) {
-        Mlt::Producer *base = producer();
-        m_audioTrackProducers[track] = new Mlt::Producer(*(base->profile()), base->get("resource"));
-        if (m_properties.contains("force_aspect_ratio")) m_audioTrackProducers.at(track)->set("force_aspect_ratio", m_properties.value("force_aspect_ratio").toDouble());
-        if (m_properties.contains("force_fps")) m_audioTrackProducers.at(track)->set("force_fps", m_properties.value("force_fps").toDouble());
-        if (m_properties.contains("force_progressive")) m_audioTrackProducers.at(track)->set("force_progressive", m_properties.value("force_progressive").toInt());
-        if (m_properties.contains("threads")) m_audioTrackProducers.at(track)->set("threads", m_properties.value("threads").toInt());
-        m_audioTrackProducers.at(track)->set("video_index", -1);
-        if (m_properties.contains("audio_index")) m_audioTrackProducers.at(track)->set("audio_index", m_properties.value("audio_index").toInt());
-        char *tmp = (char *) qstrdup(QString(getId() + '_' + QString::number(track) + "_audio").toUtf8().data());
-        m_audioTrackProducers.at(track)->set("id", tmp);
-        delete[] tmp;
+        int i;
+        for (i = 0; i < m_audioTrackProducers.count(); i++)
+            if (m_audioTrackProducers.at(i) != NULL) break;
+        Mlt::Producer *base;
+        if (i >= m_audioTrackProducers.count()) {
+            // Could not find a valid producer for that clip
+            locker.unlock();
+            base = producer();
+            if (base == NULL) {
+                return NULL;
+            }
+            locker.relock();
+        }
+        else base = m_audioTrackProducers.at(i);
+        m_audioTrackProducers[track] = cloneProducer(base);
+        adjustProducerProperties(m_audioTrackProducers.at(track), QString(getId() + '_' + QString::number(track) + "_audio"), false, true);
     }
     return m_audioTrackProducers.at(track);
 }
 
+
+void DocClipBase::adjustProducerProperties(Mlt::Producer *prod, const QString &id, bool mute, bool blind)
+{
+        if (m_properties.contains("force_aspect_num") && m_properties.contains("force_aspect_den") && m_properties.contains("frame_size"))
+            prod->set("force_aspect_ratio", getPixelAspect(m_properties));
+        if (m_properties.contains("force_fps")) prod->set("force_fps", m_properties.value("force_fps").toDouble());
+        if (m_properties.contains("force_progressive")) prod->set("force_progressive", m_properties.value("force_progressive").toInt());
+        if (m_properties.contains("force_tff")) prod->set("force_tff", m_properties.value("force_tff").toInt());
+        if (m_properties.contains("threads")) prod->set("threads", m_properties.value("threads").toInt());
+        if (mute) prod->set("audio_index", -1);
+        else if (m_properties.contains("audio_index")) prod->set("audio_index", m_properties.value("audio_index").toInt());
+        if (blind) prod->set("video_index", -1);
+        else if (m_properties.contains("video_index")) prod->set("video_index", m_properties.value("video_index").toInt());
+        prod->set("id", id.toUtf8().data());
+        if (m_properties.contains("force_colorspace")) prod->set("force_colorspace", m_properties.value("force_colorspace").toInt());
+        if (m_properties.contains("full_luma")) prod->set("set.force_full_luma", m_properties.value("full_luma").toInt());
+        if (m_properties.contains("proxy_out")) {
+            // We have a proxy clip, make sure the proxy has same duration as original
+            prod->set("length", m_properties.value("duration").toInt());
+            prod->set("out", m_properties.value("proxy_out").toInt());
+        }
+
+}
+
 Mlt::Producer *DocClipBase::videoProducer()
 {
+    QMutexLocker locker(&m_producerMutex);
     if (m_videoOnlyProducer == NULL) {
         int i;
         for (i = 0; i < m_baseTrackProducers.count(); i++)
             if (m_baseTrackProducers.at(i) != NULL) break;
         if (i >= m_baseTrackProducers.count()) return NULL;
-        m_videoOnlyProducer = new Mlt::Producer(*m_baseTrackProducers.at(i)->profile(), m_baseTrackProducers.at(i)->get("resource"));
-        if (m_properties.contains("force_aspect_ratio")) m_videoOnlyProducer->set("force_aspect_ratio", m_properties.value("force_aspect_ratio").toDouble());
-        if (m_properties.contains("force_fps")) m_videoOnlyProducer->set("force_fps", m_properties.value("force_fps").toDouble());
-        if (m_properties.contains("force_progressive")) m_videoOnlyProducer->set("force_progressive", m_properties.value("force_progressive").toInt());
-        if (m_properties.contains("threads")) m_videoOnlyProducer->set("threads", m_properties.value("threads").toInt());
-        m_videoOnlyProducer->set("audio_index", -1);
-        if (m_properties.contains("video_index")) m_videoOnlyProducer->set("video_index", m_properties.value("video_index").toInt());
-        char *tmp = (char *) qstrdup(QString(getId() + "_video").toUtf8().data());
-        m_videoOnlyProducer->set("id", tmp);
-        delete[] tmp;
+        m_videoOnlyProducer = cloneProducer(m_baseTrackProducers.at(i));
+        adjustProducerProperties(m_videoOnlyProducer, QString(getId() + "_video"), true, false);
     }
     return m_videoOnlyProducer;
 }
 
 Mlt::Producer *DocClipBase::producer(int track)
 {
-    /*for (int i = 0; i < m_baseTrackProducers.count(); i++) {
-        if (m_baseTrackProducers.at(i)) kDebug() << "// PROD: " << i << ", ID: " << m_baseTrackProducers.at(i)->get("id");
-    }*/
-    if (track == -1 || (m_clipType != AUDIO && m_clipType != AV)) {
-        if (m_baseTrackProducers.count() == 0) return NULL;
+    QMutexLocker locker(&m_producerMutex);
+    if (track == -1 || (m_clipType != AUDIO && m_clipType != AV && m_clipType != PLAYLIST)) {
+        if (m_baseTrackProducers.count() == 0) {
+            return NULL;
+        }
         for (int i = 0; i < m_baseTrackProducers.count(); i++) {
-            if (m_baseTrackProducers.at(i) != NULL)
+            if (m_baseTrackProducers.at(i) != NULL) {
                 return m_baseTrackProducers.at(i);
+            }
         }
         return NULL;
     }
@@ -569,29 +605,43 @@ Mlt::Producer *DocClipBase::producer(int track)
         for (i = 0; i < m_baseTrackProducers.count(); i++)
             if (m_baseTrackProducers.at(i) != NULL) break;
 
-        if (i >= m_baseTrackProducers.count()) return NULL;
-
-        if (KIO::NetAccess::exists(KUrl(m_baseTrackProducers.at(i)->get("resource")), KIO::NetAccess::SourceSide, 0))
-            m_baseTrackProducers[track] = new Mlt::Producer(*m_baseTrackProducers.at(i)->profile(), m_baseTrackProducers.at(i)->get("resource"));
-        else { // special case for placeholder clips
-            m_baseTrackProducers[track] = NULL;
+        if (i >= m_baseTrackProducers.count()) {
+            // Could not find a valid producer for that clip, check in 
             return NULL;
         }
-        if (m_properties.contains("force_aspect_ratio")) m_baseTrackProducers[track]->set("force_aspect_ratio", m_properties.value("force_aspect_ratio").toDouble());
-        if (m_properties.contains("force_fps")) m_baseTrackProducers[track]->set("force_fps", m_properties.value("force_fps").toDouble());
-        if (m_properties.contains("force_progressive")) m_baseTrackProducers[track]->set("force_progressive", m_properties.value("force_progressive").toInt());
-        if (m_properties.contains("threads")) m_baseTrackProducers[track]->set("threads", m_properties.value("threads").toInt());
-        if (m_properties.contains("video_index")) m_baseTrackProducers[track]->set("video_index", m_properties.value("video_index").toInt());
-        if (m_properties.contains("audio_index")) m_baseTrackProducers[track]->set("audio_index", m_properties.value("audio_index").toInt());
-        char *tmp = (char *) qstrdup(QString(getId() + '_' + QString::number(track)).toUtf8().data());
-        m_baseTrackProducers[track]->set("id", tmp);
+        m_baseTrackProducers[track] = cloneProducer(m_baseTrackProducers.at(i));
+        adjustProducerProperties(m_baseTrackProducers.at(track), QString(getId() + '_' + QString::number(track)), false, false);
+    }
+    return m_baseTrackProducers.at(track);
+}
+
+
+Mlt::Producer *DocClipBase::cloneProducer(Mlt::Producer *source)
+{
+    Mlt::Producer *result = NULL;
+    QString url = source->get("resource");
+    if (KIO::NetAccess::exists(KUrl(url), KIO::NetAccess::SourceSide, 0)) {
+        char *tmp = qstrdup(url.toUtf8().constData());
+        result = new Mlt::Producer(*source->profile(), tmp);
         delete[] tmp;
-        if (KdenliveSettings::dropbframes() && m_baseTrackProducers.at(i)->get("skip_loop_filter") && strcmp(m_baseTrackProducers.at(i)->get("skip_loop_filter"), "all") == 0) {
-            m_baseTrackProducers[track]->set("skip_loop_filter", "all");
-            m_baseTrackProducers[track]->set("skip_frame", "bidir");
+    }
+    if (result == NULL || !result->is_valid()) {
+        // placeholder clip
+        QString txt = "+" + i18n("Missing clip") + ".txt";
+        char *tmp = qstrdup(txt.toUtf8().constData());
+        result = new Mlt::Producer(*source->profile(), tmp);
+        delete[] tmp;
+        if (result == NULL || !result->is_valid())
+            result = new Mlt::Producer(*source->profile(), "colour:red");
+        else {
+            result->set("bgcolour", "0xff0000ff");
+            result->set("pad", "10");
         }
     }
-    return m_baseTrackProducers.at(track);
+    Mlt::Properties props(result->get_properties());
+    Mlt::Properties src_props(source->get_properties());
+    props.inherit(src_props);
+    return result;
 }
 
 void DocClipBase::setProducerProperty(const char *name, int data)
@@ -640,16 +690,55 @@ const char *DocClipBase::producerProperty(const char *name) const
 void DocClipBase::slotRefreshProducer()
 {
     if (m_baseTrackProducers.count() == 0) return;
-    kDebug() << "////////////   REFRESH CLIP !!!!!!!!!!!!!!!!";
     if (m_clipType == SLIDESHOW) {
-        /*char *tmp = (char *) qstrdup(getProperty("resource").toUtf8().data());
-               Mlt::Producer producer(*(m_clipProducer->profile()), tmp);
-               delete[] tmp;
+        /*Mlt::Producer producer(*(m_clipProducer->profile()), getProperty("resource").toUtf8().data());
         delete m_clipProducer;
         m_clipProducer = new Mlt::Producer(producer.get_producer());
         if (!getProperty("out").isEmpty()) m_clipProducer->set_in_and_out(getProperty("in").toInt(), getProperty("out").toInt());*/
         setProducerProperty("ttl", getProperty("ttl").toInt());
         //m_clipProducer->set("id", getProperty("id"));
+        if (!getProperty("animation").isEmpty()) {
+            Mlt::Service clipService(m_baseTrackProducers.at(0)->get_service());
+            int ct = 0;
+            Mlt::Filter *filter = clipService.filter(ct);
+            while (filter) {
+                if (strcmp(filter->get("mlt_service"), "affine") == 0) {
+                    break;
+                } else if (strcmp(filter->get("mlt_service"), "boxblur") == 0) {
+                    clipService.detach(*filter);
+                } else ct++;
+                filter = clipService.filter(ct);
+            }
+
+            if (!filter || strcmp(filter->get("mlt_service"), "affine")) {
+                // filter does not exist, create it.
+                Mlt::Filter *filter = new Mlt::Filter(*(m_baseTrackProducers.at(0)->profile()), "affine");
+                if (filter && filter->is_valid()) {
+                    int cycle = getProperty("ttl").toInt();
+                    QString geometry = SlideshowClip::animationToGeometry(getProperty("animation"), cycle);
+                    if (!geometry.isEmpty()) {
+                        if (getProperty("animation").contains("low-pass")) {
+                            Mlt::Filter *blur = new Mlt::Filter(*(m_baseTrackProducers.at(0)->profile()), "boxblur");
+                            if (blur && blur->is_valid())
+                                clipService.attach(*blur);
+                        }
+                        filter->set("transition.geometry", geometry.toUtf8().data());
+                        filter->set("transition.cycle", cycle);
+                        clipService.attach(*filter);
+                    }
+                }
+            }
+        } else {
+            Mlt::Service clipService(m_baseTrackProducers.at(0)->get_service());
+            int ct = 0;
+            Mlt::Filter *filter = clipService.filter(0);
+            while (filter) {
+                if (strcmp(filter->get("mlt_service"), "affine") == 0 || strcmp(filter->get("mlt_service"), "boxblur") == 0) {
+                    clipService.detach(*filter);
+                } else ct++;
+                filter = clipService.filter(ct);
+            }
+        }
         if (getProperty("fade") == "1") {
             // we want a fade filter effect
             kDebug() << "////////////   FADE WANTED";
@@ -665,12 +754,9 @@ void DocClipBase::slotRefreshProducer()
             }
 
             if (filter && strcmp(filter->get("mlt_service"), "luma") == 0) {
-                filter->set("period", getProperty("ttl").toInt() - 1);
-                filter->set("luma.out", getProperty("luma_duration").toInt());
-                QString resource = getProperty("luma_file");
-                char *tmp = (char *) qstrdup(resource.toUtf8().data());
-                filter->set("luma.resource", tmp);
-                delete[] tmp;
+                filter->set("cycle", getProperty("ttl").toInt());
+                filter->set("duration", getProperty("luma_duration").toInt());
+                filter->set("luma.resource", getProperty("luma_file").toUtf8().data());
                 if (!getProperty("softness").isEmpty()) {
                     int soft = getProperty("softness").toInt();
                     filter->set("luma.softness", (double) soft / 100.0);
@@ -678,12 +764,9 @@ void DocClipBase::slotRefreshProducer()
             } else {
                 // filter does not exist, create it...
                 Mlt::Filter *filter = new Mlt::Filter(*(m_baseTrackProducers.at(0)->profile()), "luma");
-                filter->set("period", getProperty("ttl").toInt() - 1);
-                filter->set("luma.out", getProperty("luma_duration").toInt());
-                QString resource = getProperty("luma_file");
-                char *tmp = (char *) qstrdup(resource.toUtf8().data());
-                filter->set("luma.resource", tmp);
-                delete[] tmp;
+                filter->set("cycle", getProperty("ttl").toInt());
+                filter->set("duration", getProperty("luma_duration").toInt());
+                filter->set("luma.resource", getProperty("luma_file").toUtf8().data());
                 if (!getProperty("softness").isEmpty()) {
                     int soft = getProperty("softness").toInt();
                     filter->set("luma.softness", (double) soft / 100.0);
@@ -702,6 +785,36 @@ void DocClipBase::slotRefreshProducer()
                 filter = clipService.filter(ct);
             }
         }
+        if (getProperty("crop") == "1") {
+            // we want a center crop filter effect
+            Mlt::Service clipService(m_baseTrackProducers.at(0)->get_service());
+            int ct = 0;
+            Mlt::Filter *filter = clipService.filter(ct);
+            while (filter) {
+                if (strcmp(filter->get("mlt_service"), "crop") == 0) {
+                    break;
+                }
+                ct++;
+                filter = clipService.filter(ct);
+            }
+
+            if (!filter || strcmp(filter->get("mlt_service"), "crop")) {
+                // filter does not exist, create it...
+                Mlt::Filter *filter = new Mlt::Filter(*(m_baseTrackProducers.at(0)->profile()), "crop");
+                filter->set("center", 1);
+                clipService.attach(*filter);
+            }
+        } else {
+            Mlt::Service clipService(m_baseTrackProducers.at(0)->get_service());
+            int ct = 0;
+            Mlt::Filter *filter = clipService.filter(0);
+            while (filter) {
+                if (strcmp(filter->get("mlt_service"), "crop") == 0) {
+                    clipService.detach(*filter);
+                } else ct++;
+                filter = clipService.filter(ct);
+            }
+        }
     }
 }
 
@@ -712,12 +825,24 @@ void DocClipBase::setProperties(QMap <QString, QString> properties)
     QMapIterator<QString, QString> i(properties);
     bool refreshProducer = false;
     QStringList keys;
-    keys << "luma_duration" << "luma_file" << "fade" << "ttl" << "softness";
+    keys << "luma_duration" << "luma_file" << "fade" << "ttl" << "softness" << "crop" << "animation";
+    QString oldProxy = m_properties.value("proxy");
     while (i.hasNext()) {
         i.next();
         setProperty(i.key(), i.value());
         if (m_clipType == SLIDESHOW && keys.contains(i.key())) refreshProducer = true;
     }
+    if (properties.contains("proxy")) {
+        QString value = properties.value("proxy");
+        // If value is "-", that means user manually disabled proxy on this clip
+        if (value.isEmpty() || value == "-") {
+            // reset proxy
+            emit abortProxy(m_id, oldProxy);
+        }
+        else {
+            emit createProxy(m_id);
+        }
+    }
     if (refreshProducer) slotRefreshProducer();
 }
 
@@ -782,7 +907,11 @@ QString DocClipBase::getClipHash() const
     if (m_clipType == SLIDESHOW) hash = QCryptographicHash::hash(m_properties.value("resource").toAscii().data(), QCryptographicHash::Md5).toHex();
     else if (m_clipType == COLOR) hash = QCryptographicHash::hash(m_properties.value("colour").toAscii().data(), QCryptographicHash::Md5).toHex();
     else if (m_clipType == TEXT) hash = QCryptographicHash::hash(QString("title" + getId() + m_properties.value("xmldata")).toUtf8().data(), QCryptographicHash::Md5).toHex();
-    else hash = m_properties.value("file_hash");
+    else {
+        if (m_properties.contains("file_hash")) hash = m_properties.value("file_hash");
+        if (hash.isEmpty()) hash = getHash(fileURL().path());
+        
+    }
     return hash;
 }
 
@@ -828,24 +957,23 @@ void DocClipBase::setProperty(const QString &key, const QString &value)
     } else if (key == "out") setDuration(GenTime(value.toInt(), KdenliveSettings::project_fps()));
     //else if (key == "transparency") m_clipProducer->set("transparency", value.toInt());
     else if (key == "colour") {
-        char *tmp = (char *) qstrdup(value.toUtf8().data());
-        setProducerProperty("colour", tmp);
-        delete[] tmp;
+        setProducerProperty("colour", value.toUtf8().data());
     } else if (key == "templatetext") {
-        char *tmp = (char *) qstrdup(value.toUtf8().data());
-        setProducerProperty("templatetext", tmp);
-        delete[] tmp;
+        setProducerProperty("templatetext", value.toUtf8().data());
         setProducerProperty("force_reload", 1);
     } else if (key == "xmldata") {
-        char *tmp = (char *) qstrdup(value.toUtf8().data());
-        setProducerProperty("xmldata", tmp);
-        delete[] tmp;
+        setProducerProperty("xmldata", value.toUtf8().data());
         setProducerProperty("force_reload", 1);
-    } else if (key == "force_aspect_ratio") {
+    } else if (key == "force_aspect_num") {
         if (value.isEmpty()) {
-            m_properties.remove("force_aspect_ratio");
+            m_properties.remove("force_aspect_num");
             resetProducerProperty("force_aspect_ratio");
-        } else setProducerProperty("force_aspect_ratio", value.toDouble());
+        } else setProducerProperty("force_aspect_ratio", getPixelAspect(m_properties));
+    } else if (key == "force_aspect_den") {
+        if (value.isEmpty()) {
+            m_properties.remove("force_aspect_den");
+            resetProducerProperty("force_aspect_ratio");
+        } else setProducerProperty("force_aspect_ratio", getPixelAspect(m_properties));
     } else if (key == "force_fps") {
         if (value.isEmpty()) {
             m_properties.remove("force_fps");
@@ -856,6 +984,11 @@ void DocClipBase::setProperty(const QString &key, const QString &value)
             m_properties.remove("force_progressive");
             resetProducerProperty("force_progressive");
         } else setProducerProperty("force_progressive", value.toInt());
+    } else if (key == "force_tff") {
+        if (value.isEmpty()) {
+            m_properties.remove("force_tff");
+            resetProducerProperty("force_tff");
+        } else setProducerProperty("force_tff", value.toInt());
     } else if (key == "threads") {
         if (value.isEmpty()) {
             m_properties.remove("threads");
@@ -871,6 +1004,16 @@ void DocClipBase::setProperty(const QString &key, const QString &value)
             m_properties.remove("audio_index");
             setProducerProperty("audio_index", m_properties.value("default_audio").toInt());
         } else setProducerProperty("audio_index", value.toInt());
+    } else if (key == "force_colorspace") {
+        if (value.isEmpty()) {
+            m_properties.remove("force_colorspace");
+            resetProducerProperty("force_colorspace");
+        } else setProducerProperty("force_colorspace", value.toInt());
+    } else if (key == "full_luma") {
+        if (value.isEmpty()) {
+            m_properties.remove("full_luma");
+            resetProducerProperty("set.force_full_luma");
+        } else setProducerProperty("set.force_full_luma", value.toInt());
     }
 }
 
@@ -986,3 +1129,19 @@ bool DocClipBase::hasAudioCodec(const QString &codec) const
     return prod->get(property) == codec;
 }
 
+
+void DocClipBase::slotExtractImage(int frame, int frame2)
+{
+    if (m_thumbProd == NULL) return;
+    m_thumbProd->extractImage(frame, frame2);
+}
+
+QPixmap DocClipBase::extractImage(int frame, int width, int height)
+{
+    if (m_thumbProd == NULL) return QPixmap(width, height);
+    QMutexLocker locker(&m_producerMutex);
+    QPixmap p = m_thumbProd->extractImage(frame, width, height);
+    return p;
+}
+
+