]> git.sesse.net Git - kdenlive/blobdiff - src/renderer.cpp
Fix tracks issues (load & save of tracks with hidden audio or video), also fix
[kdenlive] / src / renderer.cpp
index 36df5e29c2d2032c37126ac16c83ddf5cd4cbce9..dc8a2ed81031b7038943b0ad4bc7446afe94519a 100644 (file)
@@ -33,7 +33,6 @@ extern "C" {
 #include <QTimer>
 #include <QDir>
 #include <QApplication>
-#include <QPainter>
 
 #include <KDebug>
 #include <KStandardDirs>
@@ -126,6 +125,7 @@ void Render::buildConsumer() {
     m_mltConsumer->set("resize", 1);
     m_mltConsumer->set("window_id", m_winid);
     m_mltConsumer->set("terminate_on_pause", 1);
+    //m_mltConsumer->set("fullscreen", 1);
     m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_frame_show);
     m_mltConsumer->set("rescale", "nearest");
 
@@ -174,7 +174,7 @@ int Render::resetProfile() {
     m_mltProfile = NULL;
     buildConsumer();
 
-    kDebug() << "//RESET WITHSCENE: " << scene;
+    //kDebug() << "//RESET WITHSCENE: " << scene;
     setSceneList(scene);
 
     char *tmp = decodedString(scene);
@@ -416,7 +416,7 @@ void Render::slotSplitView(bool doit) {
                 transition->set("b_track", i);
                 transition->set("distort", 1);
                 transition->set("internal_added", "200");
-                char *tmp;
+                const char *tmp;
                 switch (screen) {
                 case 0:
                     tmp = "0,0:50%x50%";
@@ -518,13 +518,15 @@ void Render::getFileProperties(const QDomElement &xml, const QString &clipId) {
         }
         //}
     }
-    if (xml.hasAttribute("out")) producer->set_in_and_out(xml.attribute("in").toInt(), xml.attribute("out").toInt());
 
-    if (producer->is_blank()) {
+    if (producer == NULL || producer->is_blank() || !producer->is_valid()) {
         kDebug() << " / / / / / / / /ERRROR / / / / // CANNOT LOAD PRODUCER: ";
         emit removeInvalidClip(clipId);
         return;
     }
+
+    if (xml.hasAttribute("out")) producer->set_in_and_out(xml.attribute("in").toInt(), xml.attribute("out").toInt());
+
     char *tmp = decodedString(clipId);
     producer->set("id", tmp);
     delete[] tmp;
@@ -770,12 +772,15 @@ void Render::setSceneList(QString playlist, int position) {
     if (m_winid == -1) return;
     m_isBlocked = true;
 
-    kWarning() << "//////  RENDER, SET SCENE LIST: " << playlist;
+    //kWarning() << "//////  RENDER, SET SCENE LIST: " << playlist;
 
     if (m_mltConsumer) {
         m_mltConsumer->stop();
         //m_mltConsumer->set("refresh", 0);
-    } else return;
+    } else {
+        m_isBlocked = false;
+        return;
+    }
 
     if (m_mltProducer) {
         m_mltProducer->set_speed(0);
@@ -786,6 +791,7 @@ void Render::setSceneList(QString playlist, int position) {
         emit stopped();
     }
 
+    blockSignals(true);
     char *tmp = decodedString(playlist);
     m_mltProducer = new Mlt::Producer(*m_mltProfile, "westley-xml", tmp);
     delete[] tmp;
@@ -826,11 +832,11 @@ void Render::setSceneList(QString playlist, int position) {
     if (position != 0) {
         //TODO: seek to correct place after opening project.
         //  Needs to be done from another place since it crashes here.
-        //m_mltProducer->seek(position);
-        //emit rendererPosition(position);
+        m_mltProducer->seek(position);
     }
     m_isBlocked = false;
-
+    blockSignals(false);
+    emit rendererPosition(position);
 }
 
 /** Create the producer from the Westley QDomDocument */
@@ -845,7 +851,7 @@ QString Render::sceneList() {
     westleyConsumer.connect(prod);
     westleyConsumer.start();
     while (!westleyConsumer.is_stopped()) {}
-    playlist = westleyConsumer.get("kdenlive_playlist");
+    playlist = QString::fromUtf8(westleyConsumer.get("kdenlive_playlist"));
     if (split) slotSplitView(true);
     return playlist;
 }
@@ -1445,7 +1451,120 @@ bool Render::mltRemoveClip(int track, GenTime position) {
     return true;
 }
 
-int Render::mltChangeClipSpeed(ItemInfo info, double speed, Mlt::Producer *prod) {
+int Render::mltGetSpaceLength(const GenTime pos, int track) {
+    if (!m_mltProducer) {
+        kDebug() << "PLAYLIST NOT INITIALISED //////";
+        return -1;
+    }
+    Mlt::Producer parentProd(m_mltProducer->parent());
+    if (parentProd.get_producer() == NULL) {
+        kDebug() << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////";
+        return -1;
+    }
+
+    Mlt::Service service(parentProd.get_service());
+    Mlt::Tractor tractor(service);
+    int insertPos = pos.frames(m_fps);
+
+    Mlt::Producer trackProducer(tractor.track(track));
+    Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
+    int clipIndex = trackPlaylist.get_clip_index_at(insertPos);
+    if (!trackPlaylist.is_blank(clipIndex)) return -1;
+    return trackPlaylist.clip_length(clipIndex);
+}
+
+
+void Render::mltInsertSpace(const GenTime pos, int track, const GenTime duration) {
+    if (!m_mltProducer) {
+        kDebug() << "PLAYLIST NOT INITIALISED //////";
+        return;
+    }
+    Mlt::Producer parentProd(m_mltProducer->parent());
+    if (parentProd.get_producer() == NULL) {
+        kDebug() << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////";
+        return;
+    }
+
+    Mlt::Service service(parentProd.get_service());
+    Mlt::Tractor tractor(service);
+    mlt_service_lock(service.get_service());
+    int insertPos = pos.frames(m_fps);
+    int diff = duration.frames(m_fps);
+
+    if (track != -1) {
+        // insert space in one track only
+        Mlt::Producer trackProducer(tractor.track(track));
+        Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
+        int clipIndex = trackPlaylist.get_clip_index_at(insertPos);
+        if (diff > 0) trackPlaylist.insert_blank(clipIndex, diff - 1);
+        else {
+            int position = trackPlaylist.clip_start(clipIndex);
+            trackPlaylist.remove_region(position, -diff - 1);
+        }
+        trackPlaylist.consolidate_blanks(0);
+
+        // now move transitions
+        mlt_service serv = m_mltProducer->parent().get_service();
+        mlt_service nextservice = mlt_service_get_producer(serv);
+        mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
+        QString mlt_type = mlt_properties_get(properties, "mlt_type");
+        QString resource = mlt_properties_get(properties, "mlt_service");
+
+        while (mlt_type == "transition") {
+            mlt_transition tr = (mlt_transition) nextservice;
+            int currentTrack = mlt_transition_get_b_track(tr);
+            int currentIn = (int) mlt_transition_get_in(tr);
+            int currentOut = (int) mlt_transition_get_out(tr);
+
+            if (track == currentTrack && currentOut > insertPos && resource != "mix") {
+                mlt_transition_set_in_and_out(tr, currentIn + diff, currentOut + diff);
+            }
+            nextservice = mlt_service_producer(nextservice);
+            if (nextservice == NULL) break;
+            properties = MLT_SERVICE_PROPERTIES(nextservice);
+            mlt_type = mlt_properties_get(properties, "mlt_type");
+            resource = mlt_properties_get(properties, "mlt_service");
+        }
+    } else {
+        int trackNb = tractor.count();
+        while (trackNb > 1) {
+            Mlt::Producer trackProducer(tractor.track(trackNb - 1));
+            Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
+            int clipIndex = trackPlaylist.get_clip_index_at(insertPos);
+            if (diff > 0) trackPlaylist.insert_blank(clipIndex, diff - 1);
+            else {
+                int position = trackPlaylist.clip_start(clipIndex);
+                trackPlaylist.remove_region(position, -diff - 1);
+            }
+            trackPlaylist.consolidate_blanks(0);
+            trackNb--;
+        }
+        // now move transitions
+        mlt_service serv = m_mltProducer->parent().get_service();
+        mlt_service nextservice = mlt_service_get_producer(serv);
+        mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
+        QString mlt_type = mlt_properties_get(properties, "mlt_type");
+        QString resource = mlt_properties_get(properties, "mlt_service");
+
+        while (mlt_type == "transition") {
+            mlt_transition tr = (mlt_transition) nextservice;
+            int currentIn = (int) mlt_transition_get_in(tr);
+            int currentOut = (int) mlt_transition_get_out(tr);
+
+            if (currentOut > insertPos && resource != "mix") {
+                mlt_transition_set_in_and_out(tr, currentIn + diff, currentOut + diff);
+            }
+            nextservice = mlt_service_producer(nextservice);
+            if (nextservice == NULL) break;
+            properties = MLT_SERVICE_PROPERTIES(nextservice);
+            mlt_type = mlt_properties_get(properties, "mlt_type");
+            resource = mlt_properties_get(properties, "mlt_service");
+        }
+    }
+    mlt_service_unlock(service.get_service());
+}
+
+int Render::mltChangeClipSpeed(ItemInfo info, double speed, double oldspeed, Mlt::Producer *prod) {
     m_isBlocked = true;
     int newLength = 0;
     Mlt::Service service(m_mltProducer->parent().get_service());
@@ -1454,7 +1573,10 @@ int Render::mltChangeClipSpeed(ItemInfo info, double speed, Mlt::Producer *prod)
     Mlt::Tractor tractor(service);
     Mlt::Producer trackProducer(tractor.track(info.track));
     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
-    int clipIndex = trackPlaylist.get_clip_index_at((int) info.startPos.frames(m_fps));
+    int startPos = info.startPos.frames(m_fps);
+    int clipIndex = trackPlaylist.get_clip_index_at(startPos);
+    int clipLength = trackPlaylist.clip_length(clipIndex);
+
     Mlt::Producer clip(trackPlaylist.get_clip(clipIndex));
     QString serv = clip.parent().get("mlt_service");
     QString id = clip.parent().get("id");
@@ -1474,20 +1596,42 @@ int Render::mltChangeClipSpeed(ItemInfo info, double speed, Mlt::Producer *prod)
             delete[] tmp;
             m_slowmotionProducers.insert(url, slowprod);
         }
-        Mlt::Producer *cut = slowprod->cut(info.cropStart.frames(m_fps), (info.endPos - info.startPos).frames(m_fps) - 1);
-        newLength = cut->get_length();
         trackPlaylist.replace_with_blank(clipIndex);
         trackPlaylist.consolidate_blanks(0);
-        trackPlaylist.insert_at((int) info.startPos.frames(m_fps), *cut, 1);
+        // Check that the blank space is long enough for our new duration
+        clipIndex = trackPlaylist.get_clip_index_at(startPos);
+        int blankEnd = trackPlaylist.clip_start(clipIndex) + trackPlaylist.clip_length(clipIndex);
+        Mlt::Producer *cut;
+        if (clipIndex + 1 < trackPlaylist.count() && (startPos + clipLength / speed > blankEnd)) {
+            GenTime maxLength = GenTime(blankEnd, m_fps) - info.startPos;
+            cut = slowprod->cut((int)(info.cropStart.frames(m_fps) / speed), (int)(info.cropStart.frames(m_fps) / speed + maxLength.frames(m_fps) - 1));
+        } else cut = slowprod->cut((int)(info.cropStart.frames(m_fps) / speed), (int)((info.cropStart.frames(m_fps) + clipLength) / speed - 1));
+        trackPlaylist.insert_at(startPos, *cut, 1);
+        clipIndex = trackPlaylist.get_clip_index_at(startPos);
+        newLength = trackPlaylist.clip_length(clipIndex);
         mlt_service_unlock(service.get_service());
     } else if (speed == 1.0) {
         mlt_service_lock(service.get_service());
-        Mlt::Producer *cut = prod->cut(info.cropStart.frames(m_fps), (info.endPos - info.startPos).frames(m_fps) - 1);
+
         trackPlaylist.replace_with_blank(clipIndex);
-        newLength = cut->get_length();
         trackPlaylist.consolidate_blanks(0);
-        trackPlaylist.insert_at((int) info.startPos.frames(m_fps), *cut, 1);
+
+        // Check that the blank space is long enough for our new duration
+        clipIndex = trackPlaylist.get_clip_index_at(startPos);
+        int blankEnd = trackPlaylist.clip_start(clipIndex) + trackPlaylist.clip_length(clipIndex);
+
+        Mlt::Producer *cut;
+        GenTime oldDuration = GenTime(clipLength, m_fps);
+        GenTime newDuration = oldDuration * oldspeed;
+        if (clipIndex + 1 < trackPlaylist.count() && (info.startPos + newDuration).frames(m_fps) > blankEnd) {
+            GenTime maxLength = GenTime(blankEnd, m_fps) - info.startPos;
+            cut = prod->cut((int)(info.cropStart.frames(m_fps)), (int)(info.cropStart.frames(m_fps) + maxLength.frames(m_fps) - 1));
+        } else cut = prod->cut((int)(info.cropStart.frames(m_fps)), (int)((info.cropStart + newDuration).frames(m_fps)) - 1);
+        trackPlaylist.insert_at(startPos, *cut, 1);
+        clipIndex = trackPlaylist.get_clip_index_at(startPos);
+        newLength = trackPlaylist.clip_length(clipIndex);
         mlt_service_unlock(service.get_service());
+
     } else if (serv == "framebuffer") {
         mlt_service_lock(service.get_service());
         QString url = clip.parent().get("resource");
@@ -1504,15 +1648,29 @@ int Render::mltChangeClipSpeed(ItemInfo info, double speed, Mlt::Producer *prod)
             delete[] tmp;
             m_slowmotionProducers.insert(url, slowprod);
         }
-        Mlt::Producer *cut = slowprod->cut(info.cropStart.frames(m_fps), (info.endPos - info.startPos).frames(m_fps) - 1);
-        newLength = cut->get_length();
         trackPlaylist.replace_with_blank(clipIndex);
         trackPlaylist.consolidate_blanks(0);
-        trackPlaylist.insert_at((int) info.startPos.frames(m_fps), *cut, 1);
+
+        GenTime oldDuration = GenTime(clipLength, m_fps);
+        GenTime newDuration = oldDuration * oldspeed / speed;
+
+        // Check that the blank space is long enough for our new duration
+        clipIndex = trackPlaylist.get_clip_index_at(startPos);
+        int blankEnd = trackPlaylist.clip_start(clipIndex) + trackPlaylist.clip_length(clipIndex);
+
+        Mlt::Producer *cut;
+        if (clipIndex + 1 < trackPlaylist.count() && (info.startPos + newDuration).frames(m_fps) > blankEnd) {
+            GenTime maxLength = GenTime(blankEnd, m_fps) - info.startPos;
+            cut = slowprod->cut((int)(info.cropStart.frames(m_fps)), (int)(info.cropStart.frames(m_fps) + maxLength.frames(m_fps) - 1));
+        } else cut = slowprod->cut((int)(info.cropStart.frames(m_fps) / speed), (int)((info.cropStart / speed + newDuration).frames(m_fps) - 1));
+
+        trackPlaylist.insert_at(startPos, *cut, 1);
+        clipIndex = trackPlaylist.get_clip_index_at(startPos);
+        newLength = trackPlaylist.clip_length(clipIndex);
+
         mlt_service_unlock(service.get_service());
-        kDebug() << "AVFORMAT CLIP!!!:";
     }
-
+    if (clipIndex + 1 == trackPlaylist.count()) mltCheckLength();
     m_isBlocked = false;
     return newLength;
 }
@@ -1873,7 +2031,7 @@ void Render::mltChangeTrackState(int track, bool mute, bool blind) {
     Mlt::Service service(m_mltProducer->parent().get_service());
     Mlt::Tractor tractor(service);
     Mlt::Producer trackProducer(tractor.track(track));
-    Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
+
     if (mute) {
         if (blind) trackProducer.set("hide", 3);
         else trackProducer.set("hide", 2);
@@ -1943,12 +2101,52 @@ bool Render::mltResizeClipStart(ItemInfo info, GenTime diff) {
     return true;
 }
 
-bool Render::mltMoveClip(int startTrack, int endTrack, GenTime moveStart, GenTime moveEnd) {
-    return mltMoveClip(startTrack, endTrack, (int) moveStart.frames(m_fps), (int) moveEnd.frames(m_fps));
+bool Render::mltMoveClip(int startTrack, int endTrack, GenTime moveStart, GenTime moveEnd, Mlt::Producer *prod) {
+    return mltMoveClip(startTrack, endTrack, (int) moveStart.frames(m_fps), (int) moveEnd.frames(m_fps), prod);
 }
 
 
-bool Render::mltMoveClip(int startTrack, int endTrack, int moveStart, int moveEnd) {
+void Render::mltUpdateClipProducer(int track, int pos, Mlt::Producer *prod) {
+    kDebug() << "NEW PROD ID: " << prod->get("id");
+    m_mltConsumer->set("refresh", 0);
+    kDebug() << "// TRYING TO UPDATE CLIP at: " << pos << ", TK: " << track;
+    mlt_service_lock(m_mltConsumer->get_service());
+    Mlt::Service service(m_mltProducer->parent().get_service());
+    if (service.type() != tractor_type) kWarning() << "// TRACTOR PROBLEM";
+
+    Mlt::Tractor tractor(service);
+    Mlt::Producer trackProducer(tractor.track(track));
+    Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
+    int clipIndex = trackPlaylist.get_clip_index_at(pos + 1);
+    Mlt::Producer clipProducer(trackPlaylist.replace_with_blank(clipIndex));
+    if (clipProducer.is_blank()) {
+        kDebug() << "// ERROR UPDATING CLIP PROD";
+        mlt_service_unlock(m_mltConsumer->get_service());
+        m_isBlocked = false;
+        return;
+    }
+    Mlt::Producer *clip = prod->cut(clipProducer.get_in(), clipProducer.get_out());
+
+    // move all effects to the correct producer
+    Mlt::Service clipService(clipProducer.get_service());
+    Mlt::Service newClipService(clip->get_service());
+
+    int ct = 0;
+    Mlt::Filter *filter = clipService.filter(ct);
+    while (filter) {
+        if (filter->get("kdenlive_ix") != 0) {
+            clipService.detach(*filter);
+            newClipService.attach(*filter);
+        } else ct++;
+        filter = clipService.filter(ct);
+    }
+
+    trackPlaylist.insert_at(pos, clip, 1);
+    mlt_service_unlock(m_mltConsumer->get_service());
+    m_isBlocked = false;
+}
+
+bool Render::mltMoveClip(int startTrack, int endTrack, int moveStart, int moveEnd, Mlt::Producer *prod) {
     m_isBlocked = true;
 
     m_mltConsumer->set("refresh", 0);
@@ -1965,8 +2163,7 @@ bool Render::mltMoveClip(int startTrack, int endTrack, int moveStart, int moveEn
     if (endTrack == startTrack) {
         //mlt_service_lock(service.get_service());
         Mlt::Producer clipProducer(trackPlaylist.replace_with_blank(clipIndex));
-
-        if (!trackPlaylist.is_blank_at(moveEnd)) {
+        if (!trackPlaylist.is_blank_at(moveEnd) || clipProducer.is_blank()) {
             // error, destination is not empty
             //int ix = trackPlaylist.get_clip_index_at(moveEnd);
             kDebug() << "// ERROR MOVING CLIP TO : " << moveEnd;
@@ -1977,7 +2174,7 @@ bool Render::mltMoveClip(int startTrack, int endTrack, int moveStart, int moveEn
             trackPlaylist.consolidate_blanks(0);
             int newIndex = trackPlaylist.insert_at(moveEnd, clipProducer, 1);
             /*if (QString(clipProducer.parent().get("transparency")).toInt() == 1) {
-                mltMoveTransparency(moveStart, moveEnd, startTrack, endTrack, QString(clipProducer.parent().get("id")).toInt());
+            mltMoveTransparency(moveStart, moveEnd, startTrack, endTrack, QString(clipProducer.parent().get("id")).toInt());
             }*/
             if (newIndex + 1 == trackPlaylist.count()) checkLength = true;
         }
@@ -1992,9 +2189,38 @@ bool Render::mltMoveClip(int startTrack, int endTrack, int moveStart, int moveEn
             return false;
         } else {
             Mlt::Producer clipProducer(trackPlaylist.replace_with_blank(clipIndex));
+            if (clipProducer.is_blank()) {
+                // error, destination is not empty
+                //int ix = trackPlaylist.get_clip_index_at(moveEnd);
+                kDebug() << "// ERROR MOVING CLIP TO : " << moveEnd;
+                mlt_service_unlock(m_mltConsumer->get_service());
+                m_isBlocked = false;
+                return false;
+            }
             trackPlaylist.consolidate_blanks(0);
             destTrackPlaylist.consolidate_blanks(1);
-            int newIndex = destTrackPlaylist.insert_at(moveEnd, clipProducer, 1);
+            Mlt::Producer *clip;
+            // check if we are moving a slowmotion producer
+            QString serv = clipProducer.parent().get("mlt_service");
+            if (serv == "framebuffer") {
+                clip = &clipProducer;
+            } else clip = prod->cut(clipProducer.get_in(), clipProducer.get_out());
+
+            // move all effects to the correct producer
+            Mlt::Service clipService(clipProducer.get_service());
+            Mlt::Service newClipService(clip->get_service());
+
+            int ct = 0;
+            Mlt::Filter *filter = clipService.filter(ct);
+            while (filter) {
+                if (filter->get("kdenlive_ix") != 0) {
+                    clipService.detach(*filter);
+                    newClipService.attach(*filter);
+                } else ct++;
+                filter = clipService.filter(ct);
+            }
+
+            int newIndex = destTrackPlaylist.insert_at(moveEnd, clip, 1);
             destTrackPlaylist.consolidate_blanks(0);
             /*if (QString(clipProducer.parent().get("transparency")).toInt() == 1) {
                 kDebug() << "//////// moving clip transparency";
@@ -2013,7 +2239,6 @@ bool Render::mltMoveClip(int startTrack, int endTrack, int moveStart, int moveEn
 }
 
 void Render::mltMoveTransition(QString type, int startTrack, int newTrack, int newTransitionTrack, GenTime oldIn, GenTime oldOut, GenTime newIn, GenTime newOut) {
-
     Mlt::Service service(m_mltProducer->parent().get_service());
     Mlt::Tractor tractor(service);
     Mlt::Field *field = tractor.field();
@@ -2030,7 +2255,7 @@ void Render::mltMoveTransition(QString type, int startTrack, int newTrack, int n
     int old_pos = (int)(oldIn.frames(m_fps) + oldOut.frames(m_fps)) / 2;
 
     int new_in = (int)newIn.frames(m_fps);
-    int new_out = (int)newOut.frames(m_fps);
+    int new_out = (int)newOut.frames(m_fps) - 1;
 
     while (mlt_type == "transition") {
         mlt_transition tr = (mlt_transition) nextservice;
@@ -2073,7 +2298,6 @@ void Render::mltUpdateTransition(QString oldTag, QString tag, int a_track, int b
 
 void Render::mltUpdateTransitionParams(QString type, int a_track, int b_track, GenTime in, GenTime out, QDomElement xml) {
     m_isBlocked = true;
-
     Mlt::Service service(m_mltProducer->parent().get_service());
     Mlt::Tractor tractor(service);
     Mlt::Field *field = tractor.field();
@@ -2086,11 +2310,12 @@ void Render::mltUpdateTransitionParams(QString type, int a_track, int b_track, G
     QString mlt_type = mlt_properties_get(properties, "mlt_type");
     QString resource = mlt_properties_get(properties, "mlt_service");
     int in_pos = (int) in.frames(m_fps);
-    int out_pos = (int) out.frames(m_fps);
+    int out_pos = (int) out.frames(m_fps) - 1;
 
     while (mlt_type == "transition") {
         mlt_transition tr = (mlt_transition) nextservice;
         int currentTrack = mlt_transition_get_b_track(tr);
+        int currentBTrack = mlt_transition_get_a_track(tr);
         int currentIn = (int) mlt_transition_get_in(tr);
         int currentOut = (int) mlt_transition_get_out(tr);
 
@@ -2101,7 +2326,10 @@ void Render::mltUpdateTransitionParams(QString type, int a_track, int b_track, G
             QMap<QString, QString>::Iterator it;
             QString key;
             mlt_properties transproperties = MLT_TRANSITION_PROPERTIES(tr);
-
+            mlt_properties_set_int(transproperties, "force_track", xml.attribute("force_track").toInt());
+            if (currentBTrack != a_track) {
+                mlt_properties_set_int(properties, "a_track", a_track);
+            }
             for (it = map.begin(); it != map.end(); ++it) {
                 key = it.key();
                 char *name = decodedString(key);
@@ -2343,7 +2571,7 @@ void Render::mltAddTransition(QString tag, int a_track, int b_track, GenTime in,
     char *transId = decodedString(tag);
     Mlt::Transition *transition = new Mlt::Transition(*m_mltProfile, transId);
     if (out != GenTime())
-        transition->set_in_and_out((int) in.frames(m_fps), (int) out.frames(m_fps));
+        transition->set_in_and_out((int) in.frames(m_fps), (int) out.frames(m_fps) - 1);
     QMap<QString, QString>::Iterator it;
     QString key;
     if (xml.attribute("automatic") == "1") transition->set("automatic", 1);
@@ -2377,11 +2605,10 @@ void Render::mltSavePlaylist() {
 }
 
 QList <Mlt::Producer *> Render::producersList() {
-    QList <Mlt::Producer *> prods;
+    QList <Mlt::Producer *> prods = QList <Mlt::Producer *> ();
     QStringList ids;
     Mlt::Service service(m_mltProducer->parent().get_service());
     Mlt::Tractor tractor(service);
-    Mlt::Field *field = tractor.field();
 
     int trackNb = tractor.count();
     for (int t = 1; t < trackNb; t++) {
@@ -2402,5 +2629,186 @@ QList <Mlt::Producer *> Render::producersList() {
     return prods;
 }
 
+void Render::mltInsertTrack(int ix, bool videoTrack) {
+    blockSignals(true);
+    m_isBlocked = true;
+
+    m_mltConsumer->set("refresh", 0);
+    mlt_service_lock(m_mltConsumer->get_service());
+    Mlt::Service service(m_mltProducer->parent().get_service());
+    if (service.type() != tractor_type) kWarning() << "// TRACTOR PROBLEM";
+
+    Mlt::Tractor tractor(service);
+
+    Mlt::Playlist *playlist = new Mlt::Playlist();
+    int ct = tractor.count();
+    // kDebug() << "// TRACK INSERT: " << ix << ", MAX: " << ct;
+    int pos = ix;
+    if (pos < ct) {
+        Mlt::Producer *prodToMove = new Mlt::Producer(tractor.track(pos));
+        tractor.set_track(*playlist, pos);
+        Mlt::Producer newProd(tractor.track(pos));
+        if (!videoTrack) newProd.set("hide", 1);
+        pos++;
+        for (; pos <= ct; pos++) {
+            Mlt::Producer *prodToMove2 = new Mlt::Producer(tractor.track(pos));
+            tractor.set_track(*prodToMove, pos);
+            prodToMove = prodToMove2;
+        }
+    } else {
+        tractor.set_track(*playlist, ix);
+        Mlt::Producer newProd(tractor.track(ix));
+        if (!videoTrack) newProd.set("hide", 1);
+    }
+
+    // Move transitions
+    mlt_service serv = m_mltProducer->parent().get_service();
+    mlt_service nextservice = mlt_service_get_producer(serv);
+    mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
+    QString mlt_type = mlt_properties_get(properties, "mlt_type");
+    QString resource = mlt_properties_get(properties, "mlt_service");
+
+    while (mlt_type == "transition") {
+        if (resource != "mix") {
+            mlt_transition tr = (mlt_transition) nextservice;
+            int currentTrack = mlt_transition_get_b_track(tr);
+            int currentaTrack = mlt_transition_get_a_track(tr);
+            mlt_properties properties = MLT_TRANSITION_PROPERTIES(tr);
+
+            if (currentTrack >= ix) {
+                mlt_properties_set_int(properties, "b_track", currentTrack + 1);
+                mlt_properties_set_int(properties, "a_track", currentaTrack + 1);
+            }
+        }
+        nextservice = mlt_service_producer(nextservice);
+        if (nextservice == NULL) break;
+        properties = MLT_SERVICE_PROPERTIES(nextservice);
+        mlt_type = mlt_properties_get(properties, "mlt_type");
+        resource = mlt_properties_get(properties, "mlt_service");
+    }
+
+    // Add audio mix transition to last track
+    Mlt::Field *field = tractor.field();
+    Mlt::Transition *transition = new Mlt::Transition(*m_mltProfile, "mix");
+    //transition->set("mlt_service", "mix");
+    transition->set("a_track", 1);
+    transition->set("b_track", ct);
+    transition->set("always_active", 1);
+    transition->set("internal_added", 237);
+    transition->set("combine", 1);
+    field->plant_transition(*transition, 1, ct);
+
+    mlt_service_unlock(m_mltConsumer->get_service());
+    m_isBlocked = false;
+    blockSignals(false);
+}
+
+
+void Render::mltDeleteTrack(int ix) {
+    QDomDocument doc;
+    doc.setContent(sceneList(), false);
+    int tracksCount = doc.elementsByTagName("track").count() - 1;
+    QDomNode track = doc.elementsByTagName("track").at(ix);
+    QDomNode tractor = doc.elementsByTagName("tractor").at(0);
+    QDomNodeList transitions = doc.elementsByTagName("transition");
+    for (int i = 0; i < transitions.count(); i++) {
+        QDomElement e = transitions.at(i).toElement();
+        QDomNodeList props = e.elementsByTagName("property");
+        QMap <QString, QString> mappedProps;
+        for (int j = 0; j < props.count(); j++) {
+            QDomElement f = props.at(j).toElement();
+            mappedProps.insert(f.attribute("name"), f.firstChild().nodeValue());
+        }
+        if (mappedProps.value("mlt_service") == "mix" && mappedProps.value("b_track").toInt() == tracksCount) {
+            tractor.removeChild(transitions.at(i));
+        } else if (mappedProps.value("mlt_service") != "mix" && mappedProps.value("b_track").toInt() >= ix) {
+            // Transition needs to be moved
+            int a_track = mappedProps.value("a_track").toInt();
+            int b_track = mappedProps.value("b_track").toInt();
+            if (a_track > 0) a_track --;
+            if (b_track > 0) b_track --;
+            for (int j = 0; j < props.count(); j++) {
+                QDomElement f = props.at(j).toElement();
+                if (f.attribute("name") == "a_track") f.firstChild().setNodeValue(QString::number(a_track));
+                else if (f.attribute("name") == "b_track") f.firstChild().setNodeValue(QString::number(b_track));
+            }
+
+        }
+    }
+    tractor.removeChild(track);
+    setSceneList(doc.toString(), m_framePosition);
+    return;
+
+    blockSignals(true);
+    m_isBlocked = true;
+
+    m_mltConsumer->set("refresh", 0);
+    mlt_service_lock(m_mltConsumer->get_service());
+    Mlt::Service service(m_mltProducer->parent().get_service());
+    if (service.type() != tractor_type) kWarning() << "// TRACTOR PROBLEM";
+
+    /*Mlt::Tractor tractor(service);
+
+
+    Mlt::Multitrack *multi = tractor.multitrack();
+
+
+    int ct = tractor.count();
+    kDebug() << "// TRACK REMOVE: " << ix << ", MAX: " << ct;
+    int pos = ix;
+    for (; pos < ct ; pos++) {
+    Mlt::Service *lastTrack = new Mlt::Service(tractor.track(pos)->get_service());
+    //mlt_service_close(lastTrack->get_service());
+    delete lastTrack;
+    Mlt::Producer *prodToMove = new Mlt::Producer(tractor.track(pos + 1));
+    Mlt::Producer *prodToClose = new Mlt::Producer(tractor.track(pos));
+    mlt_service_close(prodToMove->get_service());
+    mlt_service_close(prodToClose->get_service());
+    tractor.set_track(*prodToMove, pos);
+    }*/
+
+    // Move transitions
+    /*mlt_service serv = m_mltProducer->parent().get_service();
+    mlt_service nextservice = mlt_service_get_producer(serv);
+    mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
+    QString mlt_type = mlt_properties_get(properties, "mlt_type");
+    QString resource = mlt_properties_get(properties, "mlt_service");
+
+    while (mlt_type == "transition") {
+        if (resource != "mix") {
+            mlt_transition tr = (mlt_transition) nextservice;
+            int currentTrack = mlt_transition_get_b_track(tr);
+            int currentaTrack = mlt_transition_get_a_track(tr);
+            mlt_properties properties = MLT_TRANSITION_PROPERTIES(tr);
+
+            if (currentTrack >= ix) {
+                mlt_properties_set_int(properties, "b_track", currentTrack + 1);
+                mlt_properties_set_int(properties, "a_track", currentaTrack + 1);
+            }
+        }
+        nextservice = mlt_service_producer(nextservice);
+        if (nextservice == NULL) break;
+        properties = MLT_SERVICE_PROPERTIES(nextservice);
+        mlt_type = mlt_properties_get(properties, "mlt_type");
+        resource = mlt_properties_get(properties, "mlt_service");
+    }
+
+    // Add audio mix transition to last track
+    Mlt::Field *field = tractor.field();
+    Mlt::Transition *transition = new Mlt::Transition(*m_mltProfile, "mix");
+    //transition->set("mlt_service", "mix");
+    transition->set("a_track", 1);
+    transition->set("b_track", ct);
+    transition->set("always_active", 1);
+    transition->set("internal_added", 237);
+    transition->set("combine", 1);
+    field->plant_transition(*transition, 1, ct);
+    */
+
+    mlt_service_unlock(m_mltConsumer->get_service());
+    m_isBlocked = false;
+    blockSignals(false);
+}
+
 #include "renderer.moc"