]> git.sesse.net Git - kdenlive/blob - src/renderer.cpp
Add animation feature to Slideshow Clip.
[kdenlive] / src / renderer.cpp
1 /***************************************************************************
2                         krender.cpp  -  description
3                            -------------------
4   begin                : Fri Nov 22 2002
5   copyright            : (C) 2002 by Jason Wood
6   email                : jasonwood@blueyonder.co.uk
7   copyright            : (C) 2005 Lucio Flavio Correa
8   email                : lucio.correa@gmail.com
9   copyright            : (C) Marco Gittler
10   email                : g.marco@freenet.de
11   copyright            : (C) 2006 Jean-Baptiste Mardelle
12   email                : jb@kdenlive.org
13
14 ***************************************************************************/
15
16 /***************************************************************************
17  *                                                                         *
18  *   This program is free software; you can redistribute it and/or modify  *
19  *   it under the terms of the GNU General Public License as published by  *
20  *   the Free Software Foundation; either version 2 of the License, or     *
21  *   (at your option) any later version.                                   *
22  *                                                                         *
23  ***************************************************************************/
24
25
26 #include "renderer.h"
27 #include "kdenlivesettings.h"
28 #include "kthumb.h"
29 #include "definitions.h"
30 #include "slideshowclip.h"
31
32 #include <mlt++/Mlt.h>
33
34 #include <KDebug>
35 #include <KStandardDirs>
36 #include <KMessageBox>
37 #include <KLocale>
38 #include <KTemporaryFile>
39
40 #include <QTimer>
41 #include <QDir>
42 #include <QString>
43 #include <QApplication>
44
45 #include <cstdlib>
46 #include <cstdarg>
47
48
49
50 static void kdenlive_callback(void* /*ptr*/, int level, const char* fmt, va_list vl)
51 {
52     if (level > MLT_LOG_ERROR) return;
53     QString error;
54     QApplication::postEvent(qApp->activeWindow() , new MltErrorEvent(error.vsprintf(fmt, vl).simplified()));
55     va_end(vl);
56 }
57
58
59 static void consumer_frame_show(mlt_consumer, Render * self, mlt_frame frame_ptr)
60 {
61     // detect if the producer has finished playing. Is there a better way to do it?
62     if (self->m_isBlocked) return;
63     Mlt::Frame frame(frame_ptr);
64 #ifdef Q_WS_MAC
65     self->showFrame(frame);
66 #endif
67
68     self->emitFrameNumber(mlt_frame_get_position(frame_ptr));
69     if (frame_ptr->convert_image)
70         self->emitFrameUpdated(frame);
71     if (frame.get_double("_speed") == 0.0) {
72         self->emitConsumerStopped();
73     } else if (frame.get_double("_speed") < 0.0 && mlt_frame_get_position(frame_ptr) <= 0) {
74         self->pause();
75         self->emitConsumerStopped();
76     }
77 }
78
79
80 Render::Render(const QString & rendererName, int winid, int /* extid */, QString profile, QWidget *parent) :
81         QObject(parent),
82         m_isBlocked(0),
83         m_name(rendererName),
84         m_mltConsumer(NULL),
85         m_mltProducer(NULL),
86         m_mltProfile(NULL),
87         m_framePosition(0),
88         m_isZoneMode(false),
89         m_isLoopMode(false),
90         m_isSplitView(false),
91         m_blackClip(NULL),
92         m_winid(winid)
93 #ifdef Q_WS_MAC
94         , m_glWidget(0)
95 #endif
96 {
97     /*if (rendererName == "project") m_monitorId = 10000;
98     else m_monitorId = 10001;*/
99     /*m_osdTimer = new QTimer(this);
100     connect(m_osdTimer, SIGNAL(timeout()), this, SLOT(slotOsdTimeout()));*/
101     if (profile.isEmpty()) profile = KdenliveSettings::current_profile();
102     buildConsumer(profile);
103
104     m_mltProducer = m_blackClip->cut(0, 50);
105     m_mltConsumer->connect(*m_mltProducer);
106     m_mltProducer->set_speed(0.0);
107 }
108
109 Render::~Render()
110 {
111     m_isBlocked = 1;
112     closeMlt();
113 }
114
115
116 void Render::closeMlt()
117 {
118     //delete m_osdTimer;
119     if (m_mltProducer) {
120         Mlt::Service service(m_mltProducer->parent().get_service());
121         mlt_service_lock(service.get_service());
122
123         if (service.type() == tractor_type) {
124             Mlt::Tractor tractor(service);
125             Mlt::Field *field = tractor.field();
126             mlt_service nextservice = mlt_service_get_producer(service.get_service());
127             mlt_service nextservicetodisconnect;
128             mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
129             QString mlt_type = mlt_properties_get(properties, "mlt_type");
130             QString resource = mlt_properties_get(properties, "mlt_service");
131             // Delete all transitions
132             while (mlt_type == "transition") {
133                 nextservicetodisconnect = nextservice;
134                 nextservice = mlt_service_producer(nextservice);
135                 mlt_field_disconnect_service(field->get_field(), nextservicetodisconnect);
136                 nextservice = mlt_service_producer(nextservice);
137                 if (nextservice == NULL) break;
138                 properties = MLT_SERVICE_PROPERTIES(nextservice);
139                 mlt_type = mlt_properties_get(properties, "mlt_type");
140                 resource = mlt_properties_get(properties, "mlt_service");
141             }
142
143             for (int trackNb = tractor.count() - 1; trackNb >= 0; --trackNb) {
144                 Mlt::Producer trackProducer(tractor.track(trackNb));
145                 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
146                 if (trackPlaylist.type() == playlist_type) trackPlaylist.clear();
147             }
148         }
149         mlt_service_unlock(service.get_service());
150     }
151
152     kDebug() << "// // // CLOSE RENDERER " << m_name;
153     delete m_mltConsumer;
154     delete m_mltProducer;
155     delete m_blackClip;
156     //delete m_osdInfo;
157 }
158
159 void Render::slotSwitchFullscreen()
160 {
161     if (m_mltConsumer) m_mltConsumer->set("full_screen", 1);
162 }
163
164 void Render::buildConsumer(const QString profileName)
165 {
166     char *tmp;
167     m_activeProfile = profileName;
168     tmp = decodedString(m_activeProfile);
169     setenv("MLT_PROFILE", tmp, 1);
170     delete m_blackClip;
171     m_blackClip = NULL;
172
173     //TODO: uncomment following line when everything is clean
174     //if (m_mltProfile) delete m_mltProfile;
175     m_mltProfile = new Mlt::Profile(tmp);
176     delete[] tmp;
177
178     QString videoDriver = KdenliveSettings::videodrivername();
179     if (!videoDriver.isEmpty()) {
180         if (videoDriver == "x11_noaccel") {
181             setenv("SDL_VIDEO_YUV_HWACCEL", "0", 1);
182             videoDriver = "x11";
183         } else {
184             unsetenv("SDL_VIDEO_YUV_HWACCEL");
185         }
186     }
187     setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 1);
188
189     //m_mltConsumer->set("fullscreen", 1);
190 #ifdef Q_WS_MAC
191     m_mltConsumer = new Mlt::Consumer(*m_mltProfile , "sdl_audio");
192     m_mltConsumer->set("preview_off", 1);
193     m_mltConsumer->set("preview_format", mlt_image_rgb24a);
194 #else
195     m_mltConsumer = new Mlt::Consumer(*m_mltProfile , "sdl_preview");
196 #endif
197     m_mltConsumer->set("resize", 1);
198     m_mltConsumer->set("window_id", m_winid);
199     m_mltConsumer->set("terminate_on_pause", 1);
200     tmp = decodedString(KdenliveSettings::window_background().name());
201     m_mltConsumer->set("window_background", tmp);
202     delete [] tmp;
203
204     // FIXME: the event object returned by the listen gets leaked...
205     m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_frame_show);
206     m_mltConsumer->set("rescale", "nearest");
207     mlt_log_set_callback(kdenlive_callback);
208
209     QString audioDevice = KdenliveSettings::audiodevicename();
210     if (!audioDevice.isEmpty()) {
211         tmp = decodedString(audioDevice);
212         m_mltConsumer->set("audio_device", tmp);
213         delete[] tmp;
214     }
215
216     if (!videoDriver.isEmpty()) {
217         tmp = decodedString(videoDriver);
218         m_mltConsumer->set("video_driver", tmp);
219         delete[] tmp;
220     }
221
222     QString audioDriver = KdenliveSettings::audiodrivername();
223
224     /*
225     // Disabled because the "auto" detected driver was sometimes wrong
226     if (audioDriver.isEmpty())
227         audioDriver = KdenliveSettings::autoaudiodrivername();
228     */
229
230     if (!audioDriver.isEmpty()) {
231         tmp = decodedString(audioDriver);
232         m_mltConsumer->set("audio_driver", tmp);
233         delete[] tmp;
234     }
235
236     int volume = KdenliveSettings::volume();
237     m_mltConsumer->set("volume", (float)volume / 100);
238
239     m_mltConsumer->set("progressive", 1);
240     m_mltConsumer->set("audio_buffer", 1024);
241     m_mltConsumer->set("frequency", 48000);
242
243     m_blackClip = new Mlt::Producer(*m_mltProfile , "colour", "black");
244     m_blackClip->set("id", "black");
245     m_blackClip->set("mlt_type", "producer");
246
247 }
248
249 Mlt::Producer *Render::invalidProducer(const QString &id)
250 {
251     Mlt::Producer *clip = new Mlt::Producer(*m_mltProfile , "colour", "red");
252     char *tmp = decodedString(id);
253     clip->set("id", tmp);
254     delete[] tmp;
255     clip->set("mlt_type", "producer");
256     return clip;
257 }
258
259 int Render::resetProfile(const QString profileName)
260 {
261     if (m_mltConsumer) {
262         QString videoDriver = KdenliveSettings::videodrivername();
263         QString currentDriver = m_mltConsumer->get("video_driver");
264         if (getenv("SDL_VIDEO_YUV_HWACCEL") != NULL && currentDriver == "x11") currentDriver = "x11_noaccel";
265         QString background = KdenliveSettings::window_background().name();
266         QString currentBackground = m_mltConsumer->get("window_background");
267         int volume = KdenliveSettings::volume();
268         int currentVolume = (int)(QString(m_mltConsumer->get("volume")).toDouble() * 100.0);
269         if (m_activeProfile == profileName && currentDriver == videoDriver && volume == currentVolume && background == currentBackground) {
270             kDebug() << "reset to same profile, nothing to do";
271             return 1;
272         }
273
274         if (m_isSplitView) slotSplitView(false);
275         if (!m_mltConsumer->is_stopped()) m_mltConsumer->stop();
276         m_mltConsumer->purge();
277         delete m_mltConsumer;
278         m_mltConsumer = NULL;
279     }
280     QString scene = sceneList();
281     int pos = 0;
282     double current_fps = m_mltProfile->fps();
283     delete m_blackClip;
284     m_blackClip = NULL;
285
286     if (m_mltProducer) {
287         pos = m_mltProducer->position();
288
289         Mlt::Service service(m_mltProducer->get_service());
290         if (service.type() == tractor_type) {
291             Mlt::Tractor tractor(service);
292             for (int trackNb = tractor.count() - 1; trackNb >= 0; --trackNb) {
293                 Mlt::Producer trackProducer(tractor.track(trackNb));
294                 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
295                 trackPlaylist.clear();
296             }
297         }
298
299         delete m_mltProducer;
300     }
301     m_mltProducer = NULL;
302
303     buildConsumer(profileName);
304     double new_fps = m_mltProfile->fps();
305     if (current_fps != new_fps) {
306         // fps changed, we must update the scenelist positions
307         scene = updateSceneListFps(current_fps, new_fps, scene);
308     }
309     //kDebug() << "//RESET WITHSCENE: " << scene;
310     setSceneList(scene, pos);
311     // producers have changed (different profile), so reset them...
312     emit refreshDocumentProducers();
313     /*char *tmp = decodedString(scene);
314     Mlt::Producer *producer = new Mlt::Producer(*m_mltProfile , "xml-string", tmp);
315     delete[] tmp;
316     m_mltProducer = producer;
317     m_blackClip = new Mlt::Producer(*m_mltProfile , "colour", "black");
318     m_mltProducer->optimise();
319     m_mltProducer->set_speed(0);
320     connectPlaylist();*/
321
322     //delete m_mltProfile;
323     // mlt_properties properties = MLT_CONSUMER_PROPERTIES(m_mltConsumer->get_consumer());
324     //mlt_profile prof = m_mltProfile->get_profile();
325     //mlt_properties_set_data(properties, "_profile", prof, 0, (mlt_destructor)mlt_profile_close, NULL);
326     //mlt_properties_set(properties, "profile", "hdv_1080_50i");
327     //m_mltConsumer->set("profile", (char *) profile.toUtf8().data());
328     //m_mltProfile = new Mlt::Profile((char*) profile.toUtf8().data());
329
330     //apply_profile_properties( m_mltProfile, m_mltConsumer->get_consumer(), properties );
331     //refresh();
332     return 1;
333 }
334
335 void Render::seek(GenTime time)
336 {
337     if (!m_mltProducer)
338         return;
339     m_isBlocked = false;
340     m_mltProducer->seek((int)(time.frames(m_fps)));
341     refresh();
342 }
343
344 //static
345 char *Render::decodedString(QString str)
346 {
347     /*QCString fn = QFile::encodeName(str);
348     char *t = new char[fn.length() + 1];
349     strcpy(t, (const char *)fn);*/
350
351     return (char *) qstrdup(str.toUtf8().data());   //toLatin1
352 }
353
354 //static
355 /*QPixmap Render::frameThumbnail(Mlt::Frame *frame, int width, int height, bool border) {
356     QPixmap pix(width, height);
357
358     mlt_image_format format = mlt_image_rgb24a;
359     uint8_t *thumb = frame->get_image(format, width, height);
360     QImage image(thumb, width, height, QImage::Format_ARGB32);
361
362     if (!image.isNull()) {
363         pix = pix.fromImage(image);
364         if (border) {
365             QPainter painter(&pix);
366             painter.drawRect(0, 0, width - 1, height - 1);
367         }
368     } else pix.fill(Qt::black);
369     return pix;
370 }
371 */
372 int Render::renderWidth() const
373 {
374     return (int)(m_mltProfile->height() * m_mltProfile->dar() + 0.5);
375 }
376
377 int Render::renderHeight() const
378 {
379     return m_mltProfile->height();
380 }
381
382 QImage Render::extractFrame(int frame_position, int width, int height)
383 {
384     if (width == -1) {
385         width = renderWidth();
386         height = renderHeight();
387     } else if (width % 2 == 1) width++;
388
389     if (!m_mltProducer) {
390         QImage pix(width, height, QImage::Format_RGB32);
391         pix.fill(Qt::black);
392         return pix;
393     }
394     return KThumb::getFrame(m_mltProducer, frame_position, width, height);
395 }
396
397 QPixmap Render::getImageThumbnail(KUrl url, int /*width*/, int /*height*/)
398 {
399     QImage im;
400     QPixmap pixmap;
401     if (url.fileName().startsWith(".all.")) {  //  check for slideshow
402         QString fileType = url.fileName().right(3);
403         QStringList more;
404         QStringList::Iterator it;
405
406         QDir dir(url.directory());
407         QStringList filter;
408         filter << "*." + fileType;
409         filter << "*." + fileType.toUpper();
410         more = dir.entryList(filter, QDir::Files);
411         im.load(url.directory() + '/' + more.at(0));
412     } else im.load(url.path());
413     //pixmap = im.scaled(width, height);
414     return pixmap;
415 }
416 /*
417 //static
418 QPixmap Render::getVideoThumbnail(char *profile, QString file, int frame_position, int width, int height) {
419     QPixmap pix(width, height);
420     char *tmp = decodedString(file);
421     Mlt::Profile *prof = new Mlt::Profile(profile);
422     Mlt::Producer m_producer(*prof, tmp);
423     delete[] tmp;
424     if (m_producer.is_blank()) {
425         pix.fill(Qt::black);
426         return pix;
427     }
428
429     Mlt::Filter m_convert(*prof, "avcolour_space");
430     m_convert.set("forced", mlt_image_rgb24a);
431     m_producer.attach(m_convert);
432     m_producer.seek(frame_position);
433     Mlt::Frame * frame = m_producer.get_frame();
434     if (frame) {
435         pix = frameThumbnail(frame, width, height, true);
436         delete frame;
437     }
438     if (prof) delete prof;
439     return pix;
440 }
441 */
442 /*
443 void Render::getImage(KUrl url, int frame_position, QPoint size)
444 {
445     char *tmp = decodedString(url.path());
446     Mlt::Producer m_producer(tmp);
447     delete[] tmp;
448     if (m_producer.is_blank()) {
449  return;
450     }
451     Mlt::Filter m_convert("avcolour_space");
452     m_convert.set("forced", mlt_image_rgb24a);
453     m_producer.attach(m_convert);
454     m_producer.seek(frame_position);
455
456     Mlt::Frame * frame = m_producer.get_frame();
457
458     if (frame) {
459  QPixmap pix = frameThumbnail(frame, size.x(), size.y(), true);
460  delete frame;
461  emit replyGetImage(url, frame_position, pix, size.x(), size.y());
462     }
463 }*/
464
465 /* Create thumbnail for color */
466 /*void Render::getImage(int id, QString color, QPoint size)
467 {
468     QPixmap pixmap(size.x() - 2, size.y() - 2);
469     color = color.replace(0, 2, "#");
470     color = color.left(7);
471     pixmap.fill(QColor(color));
472     QPixmap result(size.x(), size.y());
473     result.fill(Qt::black);
474     //copyBlt(&result, 1, 1, &pixmap, 0, 0, size.x() - 2, size.y() - 2);
475     emit replyGetImage(id, result, size.x(), size.y());
476
477 }*/
478
479 /* Create thumbnail for image */
480 /*void Render::getImage(KUrl url, QPoint size)
481 {
482     QImage im;
483     QPixmap pixmap;
484     if (url.fileName().startsWith(".all.")) {  //  check for slideshow
485      QString fileType = url.fileName().right(3);
486          QStringList more;
487          QStringList::Iterator it;
488
489             QDir dir( url.directory() );
490             more = dir.entryList( QDir::Files );
491             for ( it = more.begin() ; it != more.end() ; ++it ) {
492                 if ((*it).endsWith("."+fileType, Qt::CaseInsensitive)) {
493    if (!im.load(url.directory() + '/' + *it))
494        kDebug()<<"++ ERROR LOADIN IMAGE: "<<url.directory() + '/' + *it;
495    break;
496   }
497      }
498     }
499     else im.load(url.path());
500
501     //pixmap = im.smoothScale(size.x() - 2, size.y() - 2);
502     QPixmap result(size.x(), size.y());
503     result.fill(Qt::black);
504     //copyBlt(&result, 1, 1, &pixmap, 0, 0, size.x() - 2, size.y() - 2);
505     emit replyGetImage(url, 1, result, size.x(), size.y());
506 }*/
507
508
509 double Render::consumerRatio() const
510 {
511     if (!m_mltConsumer) return 1.0;
512     return (m_mltConsumer->get_double("aspect_ratio_num") / m_mltConsumer->get_double("aspect_ratio_den"));
513 }
514
515
516 int Render::getLength()
517 {
518
519     if (m_mltProducer) {
520         // kDebug()<<"//////  LENGTH: "<<mlt_producer_get_playtime(m_mltProducer->get_producer());
521         return mlt_producer_get_playtime(m_mltProducer->get_producer());
522     }
523     return 0;
524 }
525
526 bool Render::isValid(KUrl url)
527 {
528     char *tmp = decodedString(url.path());
529     Mlt::Producer producer(*m_mltProfile, tmp);
530     delete[] tmp;
531     if (producer.is_blank())
532         return false;
533
534     return true;
535 }
536
537 double Render::dar() const
538 {
539     return m_mltProfile->dar();
540 }
541
542 void Render::slotSplitView(bool doit)
543 {
544     m_isSplitView = doit;
545     Mlt::Service service(m_mltProducer->parent().get_service());
546     Mlt::Tractor tractor(service);
547     if (service.type() != tractor_type || tractor.count() < 2) return;
548     Mlt::Field *field = tractor.field();
549     if (doit) {
550         for (int i = 1, screen = 0; i < tractor.count() && screen < 4; i++) {
551             Mlt::Producer trackProducer(tractor.track(i));
552             kDebug() << "// TRACK: " << i << ", HIDE: " << trackProducer.get("hide");
553             if (QString(trackProducer.get("hide")).toInt() != 1) {
554                 kDebug() << "// ADIDNG TRACK: " << i;
555                 Mlt::Transition *transition = new Mlt::Transition(*m_mltProfile, "composite");
556                 transition->set("mlt_service", "composite");
557                 transition->set("a_track", 0);
558                 transition->set("b_track", i);
559                 transition->set("distort", 1);
560                 transition->set("internal_added", "200");
561                 const char *tmp;
562                 switch (screen) {
563                 case 0:
564                     tmp = "0,0:50%x50%";
565                     break;
566                 case 1:
567                     tmp = "50%,0:50%x50%";
568                     break;
569                 case 2:
570                     tmp = "0,50%:50%x50%";
571                     break;
572                 case 3:
573                 default:
574                     tmp = "50%,50%:50%x50%";
575                     break;
576                 }
577                 transition->set("geometry", tmp);
578                 transition->set("always_active", "1");
579                 field->plant_transition(*transition, 0, i);
580                 //delete[] tmp;
581                 screen++;
582             }
583         }
584         m_mltConsumer->set("refresh", 1);
585     } else {
586         mlt_service serv = m_mltProducer->parent().get_service();
587         mlt_service nextservice = mlt_service_get_producer(serv);
588         mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
589         QString mlt_type = mlt_properties_get(properties, "mlt_type");
590         QString resource = mlt_properties_get(properties, "mlt_service");
591
592         while (mlt_type == "transition") {
593             QString added = mlt_properties_get(MLT_SERVICE_PROPERTIES(nextservice), "internal_added");
594             if (added == "200") {
595                 mlt_field_disconnect_service(field->get_field(), nextservice);
596             }
597             nextservice = mlt_service_producer(nextservice);
598             if (nextservice == NULL) break;
599             properties = MLT_SERVICE_PROPERTIES(nextservice);
600             mlt_type = mlt_properties_get(properties, "mlt_type");
601             resource = mlt_properties_get(properties, "mlt_service");
602             m_mltConsumer->set("refresh", 1);
603         }
604     }
605 }
606
607 void Render::getFileProperties(const QDomElement xml, const QString &clipId, int imageHeight, bool replaceProducer)
608 {
609     KUrl url = KUrl(xml.attribute("resource", QString()));
610     Mlt::Producer *producer = NULL;
611     //kDebug() << "PROFILE WIDT: "<< xml.attribute("mlt_service") << ": "<< m_mltProfile->width() << "\n...................\n\n";
612     /*if (xml.attribute("type").toInt() == TEXT && !QFile::exists(url.path())) {
613         emit replyGetFileProperties(clipId, producer, QMap < QString, QString >(), QMap < QString, QString >(), replaceProducer);
614         return;
615     }*/
616     if (xml.attribute("type").toInt() == COLOR) {
617         char *tmp = decodedString("colour:" + xml.attribute("colour"));
618         producer = new Mlt::Producer(*m_mltProfile, 0, tmp);
619         delete[] tmp;
620     } else if (xml.attribute("type").toInt() == TEXT) {
621         char *tmp = decodedString("kdenlivetitle:" + xml.attribute("resource"));
622         producer = new Mlt::Producer(*m_mltProfile, 0, tmp);
623         delete[] tmp;
624         if (producer && xml.hasAttribute("xmldata")) {
625             tmp = decodedString(xml.attribute("xmldata"));
626             producer->set("xmldata", tmp);
627             delete[] tmp;
628         }
629     } else if (url.isEmpty()) {
630         QDomDocument doc;
631         QDomElement mlt = doc.createElement("mlt");
632         QDomElement play = doc.createElement("playlist");
633         doc.appendChild(mlt);
634         mlt.appendChild(play);
635         play.appendChild(doc.importNode(xml, true));
636         char *tmp = decodedString(doc.toString());
637         producer = new Mlt::Producer(*m_mltProfile, "xml-string", tmp);
638         delete[] tmp;
639     } else {
640         char *tmp = decodedString(url.path());
641         producer = new Mlt::Producer(*m_mltProfile, tmp);
642         delete[] tmp;
643     }
644
645     if (producer == NULL || producer->is_blank() || !producer->is_valid()) {
646         kDebug() << " / / / / / / / / ERROR / / / / // CANNOT LOAD PRODUCER: ";
647         emit removeInvalidClip(clipId, replaceProducer);
648         delete producer;
649         return;
650     }
651
652     if (xml.hasAttribute("force_aspect_ratio")) {
653         double aspect = xml.attribute("force_aspect_ratio").toDouble();
654         if (aspect > 0) producer->set("force_aspect_ratio", aspect);
655     }
656
657     if (xml.hasAttribute("force_fps")) {
658         double fps = xml.attribute("force_fps").toDouble();
659         if (fps > 0) producer->set("force_fps", fps);
660     }
661
662     if (xml.hasAttribute("force_progressive")) {
663         bool ok;
664         int progressive = xml.attribute("force_progressive").toInt(&ok);
665         if (ok) producer->set("force_progressive", progressive);
666     }
667     if (xml.hasAttribute("threads")) {
668         int threads = xml.attribute("threads").toInt();
669         if (threads != 1) producer->set("threads", threads);
670     }
671     if (xml.hasAttribute("video_index")) {
672         int vindex = xml.attribute("video_index").toInt();
673         if (vindex != 0) producer->set("video_index", vindex);
674     }
675     if (xml.hasAttribute("audio_index")) {
676         int aindex = xml.attribute("audio_index").toInt();
677         if (aindex != 0) producer->set("audio_index", aindex);
678     }
679
680     // setup length here as otherwise default length (currently 15000 frames in MLT) will be taken although outpoint is larger
681     if (xml.attribute("type").toInt() == COLOR || xml.attribute("type").toInt() == TEXT
682             || xml.attribute("type").toInt() == IMAGE || xml.attribute("type").toInt() == SLIDESHOW)
683         producer->set("length", xml.attribute("out").toInt() - xml.attribute("in").toInt() + 1);
684
685     if (xml.hasAttribute("out")) producer->set_in_and_out(xml.attribute("in").toInt(), xml.attribute("out").toInt());
686
687     char *tmp = decodedString(clipId);
688     producer->set("id", tmp);
689     delete[] tmp;
690
691     if (xml.hasAttribute("templatetext")) {
692         char *tmp = decodedString(xml.attribute("templatetext"));
693         producer->set("templatetext", tmp);
694         delete[] tmp;
695     }
696
697     if (!replaceProducer && xml.hasAttribute("file_hash")) {
698         // Clip  already has all properties
699         emit replyGetFileProperties(clipId, producer, QMap < QString, QString >(), QMap < QString, QString >(), replaceProducer);
700         return;
701     }
702
703     int width = (int)(imageHeight * m_mltProfile->dar() + 0.5);
704     QMap < QString, QString > filePropertyMap;
705     QMap < QString, QString > metadataPropertyMap;
706
707     int frameNumber = xml.attribute("thumbnail", "0").toInt();
708     if (frameNumber != 0) producer->seek(frameNumber);
709
710     filePropertyMap["duration"] = QString::number(producer->get_playtime());
711     //kDebug() << "///////  PRODUCER: " << url.path() << " IS: " << producer.get_playtime();
712
713     Mlt::Frame *frame = producer->get_frame();
714
715     if (xml.attribute("type").toInt() == SLIDESHOW) {
716         int ttl = xml.hasAttribute("ttl") ? xml.attribute("ttl").toInt() : 0;
717         if (ttl) producer->set("ttl", ttl);
718         if (!xml.attribute("animation").isEmpty()) {
719             Mlt::Filter *filter = new Mlt::Filter(*m_mltProfile, "affine");
720             if (filter && filter->is_valid()) {
721                 int cycle = ttl;
722                 QString geometry = SlideshowClip::animationToGeometry(xml.attribute("animation"), cycle);
723                 if (!geometry.isEmpty()) {
724                     if (xml.attribute("animation").contains("low-pass")) {
725                         Mlt::Filter *blur = new Mlt::Filter(*m_mltProfile, "boxblur");
726                         if (blur && blur->is_valid())
727                             producer->attach(*blur);
728                     }
729                     filter->set("transition.geometry", geometry.toUtf8().data());
730                     filter->set("transition.cycle", cycle);
731                     producer->attach(*filter);
732                 }
733             }
734         }
735         if (xml.attribute("fade") == "1") {
736             // user wants a fade effect to slideshow
737             Mlt::Filter *filter = new Mlt::Filter(*m_mltProfile, "luma");
738             if (filter && filter->is_valid()) {
739                 if (ttl) filter->set("cycle", ttl);
740                 if (xml.hasAttribute("luma_duration") && !xml.attribute("luma_duration").isEmpty()) filter->set("duration", xml.attribute("luma_duration").toInt());
741                 if (xml.hasAttribute("luma_file") && !xml.attribute("luma_file").isEmpty()) {
742                     char *tmp = decodedString(xml.attribute("luma_file"));
743                     filter->set("luma.resource", tmp);
744                     delete[] tmp;
745                     if (xml.hasAttribute("softness")) {
746                         int soft = xml.attribute("softness").toInt();
747                         filter->set("luma.softness", (double) soft / 100.0);
748                     }
749                 }
750                 producer->attach(*filter);
751             }
752         }
753         if (xml.attribute("crop") == "1") {
754             // user wants to center crop the slides
755             Mlt::Filter *filter = new Mlt::Filter(*m_mltProfile, "crop");
756             if (filter && filter->is_valid()) {
757                 filter->set("center", 1);
758                 producer->attach(*filter);
759             }
760         }
761     }
762
763
764     filePropertyMap["fps"] = producer->get("source_fps");
765
766     if (frame && frame->is_valid()) {
767         filePropertyMap["frame_size"] = QString::number(frame->get_int("width")) + 'x' + QString::number(frame->get_int("height"));
768         filePropertyMap["frequency"] = QString::number(frame->get_int("frequency"));
769         filePropertyMap["channels"] = QString::number(frame->get_int("channels"));
770         filePropertyMap["aspect_ratio"] = frame->get("aspect_ratio");
771
772         if (frame->get_int("test_image") == 0) {
773             if (url.path().endsWith(".mlt") || url.path().endsWith(".westley") || url.path().endsWith(".kdenlive")) {
774                 filePropertyMap["type"] = "playlist";
775                 metadataPropertyMap["comment"] = QString::fromUtf8(producer->get("title"));
776             } else if (frame->get_int("test_audio") == 0)
777                 filePropertyMap["type"] = "av";
778             else
779                 filePropertyMap["type"] = "video";
780
781             mlt_image_format format = mlt_image_rgb24a;
782             int frame_width = width;
783             int frame_height = imageHeight;
784             uint8_t *data = frame->get_image(format, frame_width, frame_height, 0);
785             QImage image((uchar *)data, frame_width, frame_height, QImage::Format_ARGB32);
786             QPixmap pix;
787
788             if (!image.isNull()) {
789                 if (frame_width > (2 * width)) {
790                     // there was a scaling problem, do it manually
791                     QImage scaled = image.scaled(width, imageHeight);
792                     pix = QPixmap::fromImage(scaled.rgbSwapped());
793                 } else pix = QPixmap::fromImage(image.rgbSwapped());
794             } else
795                 pix.fill(Qt::black);
796
797             emit replyGetImage(clipId, pix);
798
799         } else if (frame->get_int("test_audio") == 0) {
800             QPixmap pixmap = KIcon("audio-x-generic").pixmap(QSize(width, imageHeight));
801             emit replyGetImage(clipId, pixmap);
802             filePropertyMap["type"] = "audio";
803         }
804     }
805     delete frame;
806     // Retrieve audio / video codec name
807
808     // If there is a
809     char property[200];
810     if (producer->get_int("video_index") > -1) {
811         /*if (context->duration == AV_NOPTS_VALUE) {
812         kDebug() << " / / / / / / / /ERROR / / / CLIP HAS UNKNOWN DURATION";
813             emit removeInvalidClip(clipId);
814             delete producer;
815             return;
816         }*/
817         // Get the video_index
818         int default_video = producer->get_int("video_index");
819         int video_max = 0;
820         int default_audio = producer->get_int("audio_index");
821         int audio_max = 0;
822
823         // Find maximum stream index values
824         for (int ix = 0; ix < producer->get_int("meta.media.nb_streams"); ix++) {
825             snprintf(property, sizeof(property), "meta.media.%d.stream.type", ix);
826             QString type = producer->get(property);
827             if (type == "video")
828                 video_max = ix;
829             else if (type == "audio")
830                 audio_max = ix;
831         }
832         filePropertyMap["default_video"] = QString::number(default_video);
833         filePropertyMap["video_max"] = QString::number(video_max);
834         filePropertyMap["default_audio"] = QString::number(default_audio);
835         filePropertyMap["audio_max"] = QString::number(audio_max);
836
837         snprintf(property, sizeof(property), "meta.media.%d.codec.long_name", default_video);
838         if (producer->get(property)) {
839             filePropertyMap["videocodec"] = producer->get(property);
840         } else {
841             snprintf(property, sizeof(property), "meta.media.%d.codec.name", default_video);
842             if (producer->get(property))
843                 filePropertyMap["videocodec"] = producer->get(property);
844         }
845
846         if (KdenliveSettings::dropbframes()) {
847             kDebug() << "// LOOKING FOR H264 on: " << default_video;
848             snprintf(property, sizeof(property), "meta.media.%d.codec.name", default_video);
849             kDebug() << "PROP: " << property << " = " << producer->get(property);
850             if (producer->get(property) && strcmp(producer->get(property), "h264") == 0) {
851                 kDebug() << "// GOT H264 CLIP, SETTING FAST PROPS";
852                 producer->set("skip_loop_filter", "all");
853                 producer->set("skip_frame", "bidir");
854             }
855         }
856
857     } else kDebug() << " / / / / /WARNING, VIDEO CONTEXT IS NULL!!!!!!!!!!!!!!";
858     if (producer->get_int("audio_index") > -1) {
859         // Get the audio_index
860         int index = producer->get_int("audio_index");
861
862         snprintf(property, sizeof(property), "meta.media.%d.codec.long_name", index);
863         if (producer->get(property)) {
864             filePropertyMap["audiocodec"] = producer->get(property);
865         } else {
866             snprintf(property, sizeof(property), "meta.media.%d.codec.name", index);
867             if (producer->get(property))
868                 filePropertyMap["audiocodec"] = producer->get(property);
869         }
870     }
871
872     // metadata
873     Mlt::Properties metadata;
874     metadata.pass_values(*producer, "meta.attr.");
875     int count = metadata.count();
876     for (int i = 0; i < count; i ++) {
877         QString name = metadata.get_name(i);
878         QString value = QString::fromUtf8(metadata.get(i));
879         if (name.endsWith("markup") && !value.isEmpty())
880             metadataPropertyMap[ name.section('.', 0, -2)] = value;
881     }
882     producer->seek(0);
883     kDebug() << "REquested fuile info for: " << url.path();
884     emit replyGetFileProperties(clipId, producer, filePropertyMap, metadataPropertyMap, replaceProducer);
885     // FIXME: should delete this to avoid a leak...
886     //delete producer;
887 }
888
889
890 #if 0
891 /** Create the producer from the MLT XML QDomDocument */
892 void Render::initSceneList()
893 {
894     kDebug() << "--------  INIT SCENE LIST ------_";
895     QDomDocument doc;
896     QDomElement mlt = doc.createElement("mlt");
897     doc.appendChild(mlt);
898     QDomElement prod = doc.createElement("producer");
899     prod.setAttribute("resource", "colour");
900     prod.setAttribute("colour", "red");
901     prod.setAttribute("id", "black");
902     prod.setAttribute("in", "0");
903     prod.setAttribute("out", "0");
904
905     QDomElement tractor = doc.createElement("tractor");
906     QDomElement multitrack = doc.createElement("multitrack");
907
908     QDomElement playlist1 = doc.createElement("playlist");
909     playlist1.appendChild(prod);
910     multitrack.appendChild(playlist1);
911     QDomElement playlist2 = doc.createElement("playlist");
912     multitrack.appendChild(playlist2);
913     QDomElement playlist3 = doc.createElement("playlist");
914     multitrack.appendChild(playlist3);
915     QDomElement playlist4 = doc.createElement("playlist");
916     multitrack.appendChild(playlist4);
917     QDomElement playlist5 = doc.createElement("playlist");
918     multitrack.appendChild(playlist5);
919     tractor.appendChild(multitrack);
920     mlt.appendChild(tractor);
921     // kDebug()<<doc.toString();
922     /*
923        QString tmp = QString("<mlt><producer resource=\"colour\" colour=\"red\" id=\"red\" /><tractor><multitrack><playlist></playlist><playlist></playlist><playlist /><playlist /><playlist></playlist></multitrack></tractor></mlt>");*/
924     setSceneList(doc, 0);
925 }
926 #endif
927
928 int Render::setProducer(Mlt::Producer *producer, int position)
929 {
930     if (m_winid == -1) return -1;
931
932     if (m_mltConsumer) {
933         m_mltConsumer->stop();
934     } else return -1;
935
936     m_mltConsumer->purge();
937
938     m_isBlocked = true;
939     if (m_mltProducer) {
940         m_mltProducer->set_speed(0);
941         delete m_mltProducer;
942         m_mltProducer = NULL;
943         emit stopped();
944     }
945     if (producer) {
946         m_mltProducer = new Mlt::Producer(producer->get_producer());
947     } else m_mltProducer = m_blackClip->cut(0, 50);
948     /*if (KdenliveSettings::dropbframes()) {
949     m_mltProducer->set("skip_loop_filter", "all");
950         m_mltProducer->set("skip_frame", "bidir");
951     }*/
952     if (!m_mltProducer || !m_mltProducer->is_valid()) {
953         kDebug() << " WARNING - - - - -INVALID PLAYLIST: ";
954         return -1;
955     }
956
957     m_fps = m_mltProducer->get_fps();
958     int error = connectPlaylist();
959
960     if (position != -1) {
961         m_mltProducer->seek(position);
962         emit rendererPosition(position);
963     } else emit rendererPosition((int) m_mltProducer->position());
964     m_isBlocked = false;
965     return error;
966 }
967
968 int Render::setSceneList(QDomDocument list, int position)
969 {
970     return setSceneList(list.toString(), position);
971 }
972
973 int Render::setSceneList(QString playlist, int position)
974 {
975     if (m_winid == -1) return -1;
976     m_isBlocked = true;
977     int error = 0;
978
979     kDebug() << "//////  RENDER, SET SCENE LIST: " << playlist;
980
981     if (m_mltConsumer) {
982         if (!m_mltConsumer->is_stopped()) {
983             m_mltConsumer->stop();
984         }
985         m_mltConsumer->set("refresh", 0);
986     } else {
987         kWarning() << "///////  ERROR, TRYING TO USE NULL MLT CONSUMER";
988         error = -1;
989     }
990
991     if (m_mltProducer) {
992         m_mltProducer->set_speed(0);
993         //if (KdenliveSettings::osdtimecode() && m_osdInfo) m_mltProducer->detach(*m_osdInfo);
994
995
996         Mlt::Service service(m_mltProducer->parent().get_service());
997         mlt_service_lock(service.get_service());
998
999         if (service.type() == tractor_type) {
1000             Mlt::Tractor tractor(service);
1001             Mlt::Field *field = tractor.field();
1002             mlt_service nextservice = mlt_service_get_producer(service.get_service());
1003             mlt_service nextservicetodisconnect;
1004             mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
1005             QString mlt_type = mlt_properties_get(properties, "mlt_type");
1006             QString resource = mlt_properties_get(properties, "mlt_service");
1007             // Delete all transitions
1008             while (mlt_type == "transition") {
1009                 nextservicetodisconnect = nextservice;
1010                 nextservice = mlt_service_producer(nextservice);
1011                 mlt_field_disconnect_service(field->get_field(), nextservicetodisconnect);
1012                 if (nextservice == NULL) break;
1013                 properties = MLT_SERVICE_PROPERTIES(nextservice);
1014                 mlt_type = mlt_properties_get(properties, "mlt_type");
1015                 resource = mlt_properties_get(properties, "mlt_service");
1016             }
1017
1018             for (int trackNb = tractor.count() - 1; trackNb >= 0; --trackNb) {
1019                 Mlt::Producer trackProducer(tractor.track(trackNb));
1020                 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1021                 if (trackPlaylist.type() == playlist_type) trackPlaylist.clear();
1022             }
1023             delete field;
1024         }
1025         mlt_service_unlock(service.get_service());
1026
1027         qDeleteAll(m_slowmotionProducers.values());
1028         m_slowmotionProducers.clear();
1029
1030         delete m_mltProducer;
1031         m_mltProducer = NULL;
1032         emit stopped();
1033     }
1034
1035     blockSignals(true);
1036     char *tmp = decodedString(playlist);
1037     m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", tmp);
1038
1039     if (!m_mltProducer || !m_mltProducer->is_valid()) {
1040         kDebug() << " WARNING - - - - -INVALID PLAYLIST: " << tmp;
1041         m_mltProducer = m_blackClip->cut(0, 50);
1042         error = -1;
1043     }
1044     delete[] tmp;
1045
1046     m_mltProducer->optimise();
1047
1048     /*if (KdenliveSettings::osdtimecode()) {
1049     // Attach filter for on screen display of timecode
1050     delete m_osdInfo;
1051     QString attr = "attr_check";
1052     mlt_filter filter = mlt_factory_filter( "data_feed", (char*) attr.ascii() );
1053     mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "_loader", 1 );
1054     mlt_producer_attach( m_mltProducer->get_producer(), filter );
1055     mlt_filter_close( filter );
1056
1057       m_osdInfo = new Mlt::Filter("data_show");
1058     tmp = decodedString(m_osdProfile);
1059       m_osdInfo->set("resource", tmp);
1060     delete[] tmp;
1061     mlt_properties properties = MLT_PRODUCER_PROPERTIES(m_mltProducer->get_producer());
1062     mlt_properties_set_int( properties, "meta.attr.timecode", 1);
1063     mlt_properties_set( properties, "meta.attr.timecode.markup", "#timecode#");
1064     m_osdInfo->set("dynamic", "1");
1065
1066       if (m_mltProducer->attach(*m_osdInfo) == 1) kDebug()<<"////// error attaching filter";
1067     } else {
1068     m_osdInfo->set("dynamic", "0");
1069     }*/
1070
1071     m_fps = m_mltProducer->get_fps();
1072     if (position != 0) {
1073         // Seek to correct place after opening project.
1074         m_mltProducer->seek(position);
1075     }
1076
1077     kDebug() << "// NEW SCENE LIST DURATION SET TO: " << m_mltProducer->get_playtime();
1078     if (error == 0) error = connectPlaylist();
1079     else connectPlaylist();
1080     fillSlowMotionProducers();
1081
1082     m_isBlocked = false;
1083     blockSignals(false);
1084
1085     return error;
1086     //kDebug()<<"// SETSCN LST, POS: "<<position;
1087     //if (position != 0) emit rendererPosition(position);
1088 }
1089
1090 const QString Render::sceneList()
1091 {
1092     QString playlist;
1093     Mlt::Consumer xmlConsumer(*m_mltProfile , "xml:kdenlive_playlist");
1094     m_mltProducer->optimise();
1095     xmlConsumer.set("terminate_on_pause", 1);
1096     Mlt::Producer prod(m_mltProducer->get_producer());
1097     bool split = m_isSplitView;
1098     if (split) slotSplitView(false);
1099     xmlConsumer.connect(prod);
1100     xmlConsumer.start();
1101     while (!xmlConsumer.is_stopped()) {}
1102     playlist = QString::fromUtf8(xmlConsumer.get("kdenlive_playlist"));
1103     if (split) slotSplitView(true);
1104     return playlist;
1105 }
1106
1107 bool Render::saveSceneList(QString path, QDomElement kdenliveData)
1108 {
1109     QFile file(path);
1110     QDomDocument doc;
1111     doc.setContent(sceneList(), false);
1112     if (!kdenliveData.isNull()) {
1113         // add Kdenlive specific tags
1114         QDomNode mlt = doc.elementsByTagName("mlt").at(0);
1115         mlt.appendChild(doc.importNode(kdenliveData, true));
1116     }
1117     if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
1118         kWarning() << "//////  ERROR writing to file: " << path;
1119         return false;
1120     }
1121     file.write(doc.toString().toUtf8());
1122     if (file.error() != QFile::NoError) {
1123         file.close();
1124         return false;
1125     }
1126     file.close();
1127     return true;
1128 }
1129
1130 void Render::saveZone(KUrl url, QString desc, QPoint zone)
1131 {
1132     kDebug() << "// SAVING CLIP ZONE, RENDER: " << m_name;
1133     char *tmppath = decodedString("xml:" + url.path());
1134     Mlt::Consumer xmlConsumer(*m_mltProfile , tmppath);
1135     m_mltProducer->optimise();
1136     delete[] tmppath;
1137     xmlConsumer.set("terminate_on_pause", 1);
1138     if (m_name == "clip") {
1139         Mlt::Producer *prod = m_mltProducer->cut(zone.x(), zone.y());
1140         tmppath = decodedString(desc);
1141         Mlt::Playlist list;
1142         list.insert_at(0, prod, 0);
1143         delete prod;
1144         list.set("title", tmppath);
1145         delete[] tmppath;
1146         xmlConsumer.connect(list);
1147
1148     } else {
1149         //TODO: not working yet, save zone from timeline
1150         Mlt::Producer *p1 = new Mlt::Producer(m_mltProducer->get_producer());
1151         /* Mlt::Service service(p1->parent().get_service());
1152          if (service.type() != tractor_type) kWarning() << "// TRACTOR PROBLEM";*/
1153
1154         //Mlt::Producer *prod = p1->cut(zone.x(), zone.y());
1155         tmppath = decodedString(desc);
1156         //prod->set("title", tmppath);
1157         delete[] tmppath;
1158         xmlConsumer.connect(*p1); //list);
1159     }
1160
1161     xmlConsumer.start();
1162 }
1163
1164 double Render::fps() const
1165 {
1166     return m_fps;
1167 }
1168
1169 int Render::connectPlaylist()
1170 {
1171     if (!m_mltConsumer) return -1;
1172     //m_mltConsumer->set("refresh", "0");
1173     m_mltConsumer->connect(*m_mltProducer);
1174     m_mltProducer->set_speed(0);
1175     if (m_mltConsumer->start() == -1) {
1176         // ARGH CONSUMER BROKEN!!!!
1177         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."));
1178         delete m_mltConsumer;
1179         m_mltConsumer = NULL;
1180         return -1;
1181     }
1182     emit durationChanged(m_mltProducer->get_playtime());
1183     return 0;
1184     //refresh();
1185 }
1186
1187 void Render::refreshDisplay()
1188 {
1189
1190     if (!m_mltProducer) return;
1191     //m_mltConsumer->set("refresh", 0);
1192
1193     //mlt_properties properties = MLT_PRODUCER_PROPERTIES(m_mltProducer->get_producer());
1194     /*if (KdenliveSettings::osdtimecode()) {
1195         mlt_properties_set_int( properties, "meta.attr.timecode", 1);
1196         mlt_properties_set( properties, "meta.attr.timecode.markup", "#timecode#");
1197         m_osdInfo->set("dynamic", "1");
1198         m_mltProducer->attach(*m_osdInfo);
1199     }
1200     else {
1201         m_mltProducer->detach(*m_osdInfo);
1202         m_osdInfo->set("dynamic", "0");
1203     }*/
1204     refresh();
1205 }
1206
1207 void Render::setVolume(double /*volume*/)
1208 {
1209     if (!m_mltConsumer || !m_mltProducer) return;
1210     /*osdTimer->stop();
1211     m_mltConsumer->set("refresh", 0);
1212     // Attach filter for on screen display of timecode
1213     mlt_properties properties = MLT_PRODUCER_PROPERTIES(m_mltProducer->get_producer());
1214     mlt_properties_set_double( properties, "meta.volume", volume );
1215     mlt_properties_set_int( properties, "meta.attr.osdvolume", 1);
1216     mlt_properties_set( properties, "meta.attr.osdvolume.markup", i18n("Volume: ") + QString::number(volume * 100));
1217
1218     if (!KdenliveSettings::osdtimecode()) {
1219     m_mltProducer->detach(*m_osdInfo);
1220     mlt_properties_set_int( properties, "meta.attr.timecode", 0);
1221      if (m_mltProducer->attach(*m_osdInfo) == 1) kDebug()<<"////// error attaching filter";
1222     }*/
1223     refresh();
1224     //m_osdTimer->setSingleShot(2500);
1225 }
1226
1227 void Render::slotOsdTimeout()
1228 {
1229     mlt_properties properties = MLT_PRODUCER_PROPERTIES(m_mltProducer->get_producer());
1230     mlt_properties_set_int(properties, "meta.attr.osdvolume", 0);
1231     mlt_properties_set(properties, "meta.attr.osdvolume.markup", NULL);
1232     //if (!KdenliveSettings::osdtimecode()) m_mltProducer->detach(*m_osdInfo);
1233     refresh();
1234 }
1235
1236 void Render::start()
1237 {
1238     kDebug() << "-----  STARTING MONITOR: " << m_name;
1239     if (m_winid == -1) {
1240         kDebug() << "-----  BROKEN MONITOR: " << m_name << ", RESTART";
1241         return;
1242     }
1243     if (m_mltConsumer && m_mltConsumer->is_stopped()) {
1244         kDebug() << "-----  MONITOR: " << m_name << " WAS STOPPED";
1245         if (m_mltConsumer->start() == -1) {
1246             //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."));
1247             kDebug(QtWarningMsg) << "/ / / / CANNOT START MONITOR";
1248         } else {
1249             kDebug() << "-----  MONITOR: " << m_name << " REFRESH";
1250             m_isBlocked = false;
1251             refresh();
1252         }
1253     }
1254     m_isBlocked = false;
1255 }
1256
1257 void Render::stop()
1258 {
1259     if (m_mltProducer == NULL) return;
1260     if (m_mltConsumer && !m_mltConsumer->is_stopped()) {
1261         kDebug() << "/////////////   RENDER STOPPED: " << m_name;
1262         m_isBlocked = true;
1263         //m_mltConsumer->set("refresh", 0);
1264         m_mltConsumer->stop();
1265         // delete m_mltConsumer;
1266         // m_mltConsumer = NULL;
1267     }
1268     kDebug() << "/////////////   RENDER STOP2-------";
1269     m_isBlocked = true;
1270
1271     if (m_mltProducer) {
1272         if (m_isZoneMode) resetZoneMode();
1273         m_mltProducer->set_speed(0.0);
1274         //m_mltProducer->set("out", m_mltProducer->get_length() - 1);
1275         //kDebug() << m_mltProducer->get_length();
1276     }
1277     kDebug() << "/////////////   RENDER STOP3-------";
1278 }
1279
1280 void Render::stop(const GenTime & startTime)
1281 {
1282
1283     kDebug() << "/////////////   RENDER STOP-------2";
1284     if (m_mltProducer) {
1285         if (m_isZoneMode) resetZoneMode();
1286         m_mltProducer->set_speed(0.0);
1287         m_mltProducer->seek((int) startTime.frames(m_fps));
1288     }
1289     m_mltConsumer->purge();
1290 }
1291
1292 void Render::pause()
1293 {
1294     if (!m_mltProducer || !m_mltConsumer)
1295         return;
1296     if (m_mltProducer->get_speed() == 0.0) return;
1297     if (m_isZoneMode) resetZoneMode();
1298     m_isBlocked = true;
1299     m_mltConsumer->set("refresh", 0);
1300     m_mltProducer->set_speed(0.0);
1301     /*
1302     The 2 lines below create a flicker loop
1303     emit rendererPosition(m_framePosition);
1304     m_mltProducer->seek(m_framePosition);*/
1305     m_mltConsumer->purge();
1306 }
1307
1308 void Render::switchPlay()
1309 {
1310     if (!m_mltProducer || !m_mltConsumer)
1311         return;
1312     if (m_isZoneMode) resetZoneMode();
1313     if (m_mltProducer->get_speed() == 0.0) {
1314         m_isBlocked = false;
1315         if (m_name == "clip" && m_framePosition == (int) m_mltProducer->get_out()) m_mltProducer->seek(0);
1316         m_mltProducer->set_speed(1.0);
1317         m_mltConsumer->set("refresh", 1);
1318     } else {
1319         m_isBlocked = true;
1320         m_mltConsumer->set("refresh", 0);
1321         m_mltProducer->set_speed(0.0);
1322         //emit rendererPosition(m_framePosition);
1323         m_mltProducer->seek(m_framePosition);
1324         m_mltConsumer->purge();
1325         //kDebug()<<" *********  RENDER PAUSE: "<<m_mltProducer->get_speed();
1326         //m_mltConsumer->set("refresh", 0);
1327         /*mlt_position position = mlt_producer_position( m_mltProducer->get_producer() );
1328         m_mltProducer->set_speed(0);
1329         m_mltProducer->seek( position );
1330                //m_mltProducer->seek((int) m_framePosition);
1331                m_isBlocked = false;*/
1332     }
1333     /*if (speed == 0.0) {
1334     m_mltProducer->seek((int) m_framePosition + 1);
1335         m_mltConsumer->purge();
1336     }*/
1337     //refresh();
1338 }
1339
1340 void Render::play(double speed)
1341 {
1342     if (!m_mltProducer)
1343         return;
1344     // if (speed == 0.0) m_mltProducer->set("out", m_mltProducer->get_length() - 1);
1345     m_isBlocked = false;
1346     m_mltProducer->set_speed(speed);
1347     /*if (speed == 0.0) {
1348     m_mltProducer->seek((int) m_framePosition + 1);
1349         m_mltConsumer->purge();
1350     }*/
1351     refresh();
1352 }
1353
1354 void Render::play(const GenTime & startTime)
1355 {
1356     if (!m_mltProducer || !m_mltConsumer)
1357         return;
1358     m_isBlocked = false;
1359     m_mltProducer->seek((int)(startTime.frames(m_fps)));
1360     m_mltProducer->set_speed(1.0);
1361     m_mltConsumer->set("refresh", 1);
1362 }
1363
1364 void Render::loopZone(const GenTime & startTime, const GenTime & stopTime)
1365 {
1366     if (!m_mltProducer || !m_mltConsumer)
1367         return;
1368     //m_mltProducer->set("eof", "loop");
1369     m_isLoopMode = true;
1370     m_loopStart = startTime;
1371     playZone(startTime, stopTime);
1372 }
1373
1374 void Render::playZone(const GenTime & startTime, const GenTime & stopTime)
1375 {
1376     if (!m_mltProducer || !m_mltConsumer)
1377         return;
1378     m_isBlocked = false;
1379     if (!m_isZoneMode) m_originalOut = m_mltProducer->get_playtime() - 1;
1380     m_mltProducer->set("out", (int)(stopTime.frames(m_fps)));
1381     m_mltProducer->seek((int)(startTime.frames(m_fps)));
1382     m_mltProducer->set_speed(1.0);
1383     m_mltConsumer->set("refresh", 1);
1384     m_isZoneMode = true;
1385 }
1386
1387 void Render::resetZoneMode()
1388 {
1389     if (!m_isZoneMode && !m_isLoopMode) return;
1390     m_mltProducer->set("out", m_originalOut);
1391     //m_mltProducer->set("eof", "pause");
1392     m_isZoneMode = false;
1393     m_isLoopMode = false;
1394 }
1395
1396 void Render::seekToFrame(int pos)
1397 {
1398     //kDebug()<<" *********  RENDER SEEK TO POS";
1399     if (!m_mltProducer)
1400         return;
1401     m_isBlocked = false;
1402     resetZoneMode();
1403     m_mltProducer->seek(pos);
1404     refresh();
1405 }
1406
1407 void Render::seekToFrameDiff(int diff)
1408 {
1409     //kDebug()<<" *********  RENDER SEEK TO POS";
1410     if (!m_mltProducer)
1411         return;
1412     m_isBlocked = false;
1413     resetZoneMode();
1414     m_mltProducer->seek(m_mltProducer->position() + diff);
1415     refresh();
1416 }
1417
1418 void Render::doRefresh()
1419 {
1420     // Use a Timer so that we don't refresh too much
1421     if (!m_isBlocked && m_mltConsumer) m_mltConsumer->set("refresh", 1);
1422 }
1423
1424 void Render::refresh()
1425 {
1426     if (!m_mltProducer || m_isBlocked)
1427         return;
1428     if (m_mltConsumer) {
1429         m_mltConsumer->set("refresh", 1);
1430     }
1431 }
1432
1433 void Render::setDropFrames(bool show)
1434 {
1435     if (m_mltConsumer) {
1436         int dropFrames = 1;
1437         if (show == false) dropFrames = 0;
1438         m_mltConsumer->stop();
1439 #ifdef Q_WS_MAC
1440         m_mltConsumer->set("real_time", dropFrames);
1441 #else
1442         m_mltConsumer->set("play.real_time", dropFrames);
1443 #endif
1444         if (m_mltConsumer->start() == -1) {
1445             kDebug(QtWarningMsg) << "ERROR, Cannot start monitor";
1446         }
1447
1448     }
1449 }
1450
1451 double Render::playSpeed()
1452 {
1453     if (m_mltProducer) return m_mltProducer->get_speed();
1454     return 0.0;
1455 }
1456
1457 GenTime Render::seekPosition() const
1458 {
1459     if (m_mltProducer) return GenTime((int) m_mltProducer->position(), m_fps);
1460     else return GenTime();
1461 }
1462
1463 int Render::seekFramePosition() const
1464 {
1465     if (m_mltProducer) return (int) m_mltProducer->position();
1466     return 0;
1467 }
1468
1469 const QString & Render::rendererName() const
1470 {
1471     return m_name;
1472 }
1473
1474 void Render::emitFrameUpdated(Mlt::Frame& frame)
1475 {
1476     mlt_image_format format = mlt_image_rgb24a;
1477     int width = 0;
1478     int height = 0;
1479     const uchar* image = frame.get_image(format, width, height);
1480     QImage qimage(width, height, QImage::Format_ARGB32);
1481     memcpy(qimage.bits(), image, width * height * 4);
1482     emit frameUpdated(qimage.rgbSwapped());
1483 }
1484
1485 void Render::emitFrameNumber(double position)
1486 {
1487     m_framePosition = position;
1488     emit rendererPosition((int) position);
1489 }
1490
1491 void Render::emitConsumerStopped()
1492 {
1493     // This is used to know when the playing stopped
1494     if (m_mltProducer) {
1495         double pos = m_mltProducer->position();
1496         if (m_isLoopMode) play(m_loopStart);
1497         else if (m_isZoneMode) resetZoneMode();
1498         emit rendererStopped((int) pos);
1499         //if (qApp->activeWindow()) QApplication::postEvent(qApp->activeWindow(), new PositionChangeEvent(GenTime((int) pos, m_fps), m_monitorId + 100));
1500         //new QCustomEvent(10002));
1501     }
1502 }
1503
1504 void Render::exportFileToFirewire(QString /*srcFileName*/, int /*port*/, GenTime /*startTime*/, GenTime /*endTime*/)
1505 {
1506     KMessageBox::sorry(0, i18n("Firewire is not enabled on your system.\n Please install Libiec61883 and recompile Kdenlive"));
1507 }
1508
1509 void Render::exportCurrentFrame(KUrl url, bool /*notify*/)
1510 {
1511     if (!m_mltProducer) {
1512         KMessageBox::sorry(qApp->activeWindow(), i18n("There is no clip, cannot extract frame."));
1513         return;
1514     }
1515
1516     //int height = 1080;//KdenliveSettings::defaultheight();
1517     //int width = 1940; //KdenliveSettings::displaywidth();
1518     //TODO: rewrite
1519     QPixmap pix; // = KThumb::getFrame(m_mltProducer, -1, width, height);
1520     /*
1521        QPixmap pix(width, height);
1522        Mlt::Filter m_convert(*m_mltProfile, "avcolour_space");
1523        m_convert.set("forced", mlt_image_rgb24a);
1524        m_mltProducer->attach(m_convert);
1525        Mlt::Frame * frame = m_mltProducer->get_frame();
1526        m_mltProducer->detach(m_convert);
1527        if (frame) {
1528            pix = frameThumbnail(frame, width, height);
1529            delete frame;
1530        }*/
1531     pix.save(url.path(), "PNG");
1532     //if (notify) QApplication::postEvent(qApp->activeWindow(), new UrlEvent(url, 10003));
1533 }
1534
1535 #ifdef Q_WS_MAC
1536 void Render::showFrame(Mlt::Frame& frame)
1537 {
1538     mlt_image_format format = mlt_image_rgb24a;
1539     int width = 0;
1540     int height = 0;
1541     const uchar* image = frame.get_image(format, width, height);
1542     QImage qimage(width, height, QImage::Format_ARGB32);
1543     memcpy(qimage.scanLine(0), image, width * height * 4);
1544     emit showImageSignal(qimage);
1545 }
1546 #endif
1547
1548
1549 /*
1550  * MLT playlist direct manipulation.
1551  */
1552
1553 void Render::mltCheckLength(Mlt::Tractor *tractor)
1554 {
1555     //kDebug()<<"checking track length: "<<track<<"..........";
1556
1557     int trackNb = tractor->count();
1558     int duration = 0;
1559     int trackDuration;
1560     if (trackNb == 1) {
1561         Mlt::Producer trackProducer(tractor->track(0));
1562         duration = trackProducer.get_playtime() - 1;
1563         m_mltProducer->set("out", duration);
1564         emit durationChanged(duration);
1565         return;
1566     }
1567     while (trackNb > 1) {
1568         Mlt::Producer trackProducer(tractor->track(trackNb - 1));
1569         trackDuration = trackProducer.get_playtime() - 1;
1570         // kDebug() << " / / /DURATON FOR TRACK " << trackNb - 1 << " = " << trackDuration;
1571         if (trackDuration > duration) duration = trackDuration;
1572         trackNb--;
1573     }
1574
1575     Mlt::Producer blackTrackProducer(tractor->track(0));
1576
1577     if (blackTrackProducer.get_playtime() - 1 != duration) {
1578         Mlt::Playlist blackTrackPlaylist((mlt_playlist) blackTrackProducer.get_service());
1579         Mlt::Producer *blackclip = blackTrackPlaylist.get_clip(0);
1580         if (blackclip && blackclip->is_blank()) {
1581             delete blackclip;
1582             blackclip = NULL;
1583         }
1584
1585         if (blackclip == NULL || blackTrackPlaylist.count() != 1) {
1586             blackTrackPlaylist.clear();
1587             m_blackClip->set("length", duration + 1);
1588             m_blackClip->set("out", duration);
1589             blackclip = m_blackClip->cut(0, duration);
1590             blackTrackPlaylist.insert_at(0, blackclip, 1);
1591         } else {
1592             if (duration > blackclip->parent().get_length()) {
1593                 blackclip->parent().set("length", duration + 1);
1594                 blackclip->parent().set("out", duration);
1595                 blackclip->set("length", duration + 1);
1596             }
1597             blackTrackPlaylist.resize_clip(0, 0, duration);
1598         }
1599
1600         delete blackclip;
1601         m_mltProducer->set("out", duration);
1602         emit durationChanged(duration);
1603     }
1604 }
1605
1606 Mlt::Producer *Render::checkSlowMotionProducer(Mlt::Producer *prod, QDomElement element)
1607 {
1608     if (element.attribute("speed", "1.0").toDouble() == 1.0 && element.attribute("strobe", "1").toInt() == 1) return prod;
1609
1610     // We want a slowmotion producer
1611     double speed = element.attribute("speed", "1.0").toDouble();
1612     int strobe = element.attribute("strobe", "1").toInt();
1613     QString url = QString::fromUtf8(prod->get("resource"));
1614     url.append('?' + QString::number(speed));
1615     if (strobe > 1) url.append("&strobe=" + QString::number(strobe));
1616     Mlt::Producer *slowprod = m_slowmotionProducers.value(url);
1617     if (!slowprod || slowprod->get_producer() == NULL) {
1618         char *tmp = decodedString(url);
1619         slowprod = new Mlt::Producer(*m_mltProfile, "framebuffer", tmp);
1620         if (strobe > 1) slowprod->set("strobe", strobe);
1621         delete[] tmp;
1622         QString id = prod->get("id");
1623         if (id.contains('_')) id = id.section('_', 0, 0);
1624         QString producerid = "slowmotion:" + id + ':' + QString::number(speed);
1625         if (strobe > 1) producerid.append(':' + QString::number(strobe));
1626         tmp = decodedString(producerid);
1627         slowprod->set("id", tmp);
1628         delete[] tmp;
1629         m_slowmotionProducers.insert(url, slowprod);
1630     }
1631     return slowprod;
1632 }
1633
1634 int Render::mltInsertClip(ItemInfo info, QDomElement element, Mlt::Producer *prod, bool overwrite, bool push)
1635 {
1636     if (m_mltProducer == NULL) {
1637         kDebug() << "PLAYLIST NOT INITIALISED //////";
1638         return -1;
1639     }
1640     if (prod == NULL) {
1641         kDebug() << "Cannot insert clip without producer //////";
1642         return -1;
1643     }
1644     Mlt::Producer parentProd(m_mltProducer->parent());
1645     if (parentProd.get_producer() == NULL) {
1646         kDebug() << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////";
1647         return -1;
1648     }
1649
1650     Mlt::Service service(parentProd.get_service());
1651     if (service.type() != tractor_type) {
1652         kWarning() << "// TRACTOR PROBLEM";
1653         return -1;
1654     }
1655     Mlt::Tractor tractor(service);
1656     if (info.track > tractor.count() - 1) {
1657         kDebug() << "ERROR TRYING TO INSERT CLIP ON TRACK " << info.track << ", at POS: " << info.startPos.frames(25);
1658         return -1;
1659     }
1660     mlt_service_lock(service.get_service());
1661     Mlt::Producer trackProducer(tractor.track(info.track));
1662     int trackDuration = trackProducer.get_playtime() - 1;
1663     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1664     //kDebug()<<"/// INSERT cLIP: "<<info.cropStart.frames(m_fps)<<", "<<info.startPos.frames(m_fps)<<"-"<<info.endPos.frames(m_fps);
1665     prod = checkSlowMotionProducer(prod, element);
1666     if (prod == NULL || !prod->is_valid()) {
1667         mlt_service_unlock(service.get_service());
1668         return -1;
1669     }
1670
1671     int cutPos = (int) info.cropStart.frames(m_fps);
1672     if (cutPos < 0) cutPos = 0;
1673     int insertPos = (int) info.startPos.frames(m_fps);
1674     int cutDuration = (int)(info.endPos - info.startPos).frames(m_fps) - 1;
1675     Mlt::Producer *clip = prod->cut(cutPos, cutDuration + cutPos);
1676     if (overwrite && (insertPos < trackDuration)) {
1677         // Replace zone with blanks
1678         //trackPlaylist.split_at(insertPos, true);
1679         trackPlaylist.remove_region(insertPos, cutDuration + 1);
1680         int clipIndex = trackPlaylist.get_clip_index_at(insertPos);
1681         trackPlaylist.insert_blank(clipIndex, cutDuration);
1682     } else if (push) {
1683         trackPlaylist.split_at(insertPos, true);
1684         int clipIndex = trackPlaylist.get_clip_index_at(insertPos);
1685         trackPlaylist.insert_blank(clipIndex, cutDuration);
1686     }
1687     int newIndex = trackPlaylist.insert_at(insertPos, clip, 1);
1688     delete clip;
1689     /*if (QString(prod->get("transparency")).toInt() == 1)
1690         mltAddClipTransparency(info, info.track - 1, QString(prod->get("id")).toInt());*/
1691
1692     if (info.track != 0 && (newIndex + 1 == trackPlaylist.count())) mltCheckLength(&tractor);
1693     mlt_service_unlock(service.get_service());
1694     /*tractor.multitrack()->refresh();
1695     tractor.refresh();*/
1696     return 0;
1697 }
1698
1699
1700 void Render::mltCutClip(int track, GenTime position)
1701 {
1702     m_isBlocked = true;
1703
1704     Mlt::Service service(m_mltProducer->parent().get_service());
1705     if (service.type() != tractor_type) {
1706         kWarning() << "// TRACTOR PROBLEM";
1707         return;
1708     }
1709
1710     Mlt::Tractor tractor(service);
1711     Mlt::Producer trackProducer(tractor.track(track));
1712     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1713
1714
1715     /* // Display playlist info
1716     kDebug()<<"////////////  BEFORE";
1717     for (int i = 0; i < trackPlaylist.count(); i++) {
1718     int blankStart = trackPlaylist.clip_start(i);
1719     int blankDuration = trackPlaylist.clip_length(i) - 1;
1720     QString blk;
1721     if (trackPlaylist.is_blank(i)) blk = "(blank)";
1722     kDebug()<<"CLIP "<<i<<": ("<<blankStart<<'x'<<blankStart + blankDuration<<")"<<blk;
1723     }*/
1724
1725     int cutPos = (int) position.frames(m_fps);
1726
1727     int clipIndex = trackPlaylist.get_clip_index_at(cutPos);
1728     if (trackPlaylist.is_blank(clipIndex)) {
1729         kDebug() << "// WARNING, TRYING TO CUT A BLANK";
1730         m_isBlocked = false;
1731         return;
1732     }
1733     mlt_service_lock(service.get_service());
1734     int clipStart = trackPlaylist.clip_start(clipIndex);
1735     trackPlaylist.split(clipIndex, cutPos - clipStart - 1);
1736     mlt_service_unlock(service.get_service());
1737
1738     // duplicate effects
1739     Mlt::Producer *original = trackPlaylist.get_clip_at(clipStart);
1740     Mlt::Producer *clip = trackPlaylist.get_clip_at(cutPos);
1741
1742     if (original == NULL || clip == NULL) {
1743         kDebug() << "// ERROR GRABBING CLIP AFTER SPLIT";
1744     }
1745     Mlt::Service clipService(original->get_service());
1746     Mlt::Service dupService(clip->get_service());
1747     delete original;
1748     delete clip;
1749     int ct = 0;
1750     Mlt::Filter *filter = clipService.filter(ct);
1751     while (filter) {
1752         // Only duplicate Kdenlive filters, and skip the fade in effects
1753         if (filter->is_valid() && strcmp(filter->get("kdenlive_id"), "") && strcmp(filter->get("kdenlive_id"), "fadein") && strcmp(filter->get("kdenlive_id"), "fade_from_black")) {
1754             // looks like there is no easy way to duplicate a filter,
1755             // so we will create a new one and duplicate its properties
1756             Mlt::Filter *dup = new Mlt::Filter(*m_mltProfile, filter->get("mlt_service"));
1757             if (dup && dup->is_valid()) {
1758                 Mlt::Properties entries(filter->get_properties());
1759                 for (int i = 0; i < entries.count(); i++) {
1760                     dup->set(entries.get_name(i), entries.get(i));
1761                 }
1762                 dupService.attach(*dup);
1763             }
1764         }
1765         ct++;
1766         filter = clipService.filter(ct);
1767     }
1768
1769     /* // Display playlist info
1770     kDebug()<<"////////////  AFTER";
1771     for (int i = 0; i < trackPlaylist.count(); i++) {
1772     int blankStart = trackPlaylist.clip_start(i);
1773     int blankDuration = trackPlaylist.clip_length(i) - 1;
1774     QString blk;
1775     if (trackPlaylist.is_blank(i)) blk = "(blank)";
1776     kDebug()<<"CLIP "<<i<<": ("<<blankStart<<'x'<<blankStart + blankDuration<<")"<<blk;
1777     }*/
1778
1779     m_isBlocked = false;
1780 }
1781
1782 bool Render::mltUpdateClip(ItemInfo info, QDomElement element, Mlt::Producer *prod)
1783 {
1784     // TODO: optimize
1785     if (prod == NULL) {
1786         kDebug() << "Cannot update clip with null producer //////";
1787         return false;
1788     }
1789     Mlt::Service service(m_mltProducer->parent().get_service());
1790     if (service.type() != tractor_type) {
1791         kWarning() << "// TRACTOR PROBLEM";
1792         return false;
1793     }
1794     Mlt::Tractor tractor(service);
1795     Mlt::Producer trackProducer(tractor.track(info.track));
1796     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1797     int startPos = info.startPos.frames(m_fps);
1798     int clipIndex = trackPlaylist.get_clip_index_at(startPos);
1799     if (trackPlaylist.is_blank(clipIndex)) {
1800         kDebug() << "// WARNING, TRYING TO REMOVE A BLANK: " << startPos;
1801         return false;
1802     }
1803     mlt_service_lock(service.get_service());
1804     Mlt::Producer *clip = trackPlaylist.get_clip(clipIndex);
1805     // keep effects
1806     QList <Mlt::Filter *> filtersList;
1807     Mlt::Service sourceService(clip->get_service());
1808     int ct = 0;
1809     Mlt::Filter *filter = sourceService.filter(ct);
1810     while (filter) {
1811         if (filter->get_int("kdenlive_ix") != 0) {
1812             filtersList.append(filter);
1813         }
1814         ct++;
1815         filter = sourceService.filter(ct);
1816     }
1817
1818     trackPlaylist.replace_with_blank(clipIndex);
1819     delete clip;
1820     //if (!mltRemoveClip(info.track, info.startPos)) return false;
1821     prod = checkSlowMotionProducer(prod, element);
1822     if (prod == NULL || !prod->is_valid()) {
1823         mlt_service_unlock(service.get_service());
1824         return false;
1825     }
1826     Mlt::Producer *clip2 = prod->cut(info.cropStart.frames(m_fps), (info.cropDuration + info.cropStart).frames(m_fps));
1827     trackPlaylist.insert_at(info.startPos.frames(m_fps), clip2, 1);
1828     delete clip2;
1829
1830
1831     //if (mltInsertClip(info, element, prod) == -1) return false;
1832     if (!filtersList.isEmpty()) {
1833         clipIndex = trackPlaylist.get_clip_index_at(startPos);
1834         Mlt::Producer *destclip = trackPlaylist.get_clip(clipIndex);
1835         Mlt::Service destService(destclip->get_service());
1836         delete destclip;
1837         for (int i = 0; i < filtersList.count(); i++)
1838             destService.attach(*(filtersList.at(i)));
1839     }
1840     mlt_service_unlock(service.get_service());
1841     return true;
1842 }
1843
1844
1845 bool Render::mltRemoveClip(int track, GenTime position)
1846 {
1847     Mlt::Service service(m_mltProducer->parent().get_service());
1848     if (service.type() != tractor_type) {
1849         kWarning() << "// TRACTOR PROBLEM";
1850         return false;
1851     }
1852
1853     Mlt::Tractor tractor(service);
1854     mlt_service_lock(service.get_service());
1855     Mlt::Producer trackProducer(tractor.track(track));
1856     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1857     int clipIndex = trackPlaylist.get_clip_index_at((int) position.frames(m_fps));
1858
1859     // Display playlist info
1860     //kDebug() << "////  BEFORE -( " << position.frames(m_fps) << " )-------------------------------";
1861     /*for (int i = 0; i < trackPlaylist.count(); i++) {
1862     int blankStart = trackPlaylist.clip_start(i);
1863     int blankDuration = trackPlaylist.clip_length(i) - 1;
1864     QString blk;
1865     if (trackPlaylist.is_blank(i)) blk = "(blank)";
1866     kDebug()<<"CLIP "<<i<<": ("<<blankStart<<'x'<<blankStart + blankDuration<<")"<<blk;
1867     }*/
1868     if (trackPlaylist.is_blank(clipIndex)) {
1869         kDebug() << "// WARNING, TRYING TO REMOVE A BLANK: " << position.frames(25);
1870         mlt_service_unlock(service.get_service());
1871         return false;
1872     }
1873     //kDebug()<<"////  Deleting at: "<< (int) position.frames(m_fps) <<" --------------------------------------";
1874     m_isBlocked = true;
1875     Mlt::Producer *clip = trackPlaylist.replace_with_blank(clipIndex);
1876     if (clip) delete clip;
1877     trackPlaylist.consolidate_blanks(0);
1878     /*if (QString(clip.parent().get("transparency")).toInt() == 1)
1879         mltDeleteTransparency((int) position.frames(m_fps), track, QString(clip.parent().get("id")).toInt());*/
1880
1881     /* // Display playlist info
1882     kDebug()<<"////  AFTER";
1883     for (int i = 0; i < trackPlaylist.count(); i++) {
1884     int blankStart = trackPlaylist.clip_start(i);
1885     int blankDuration = trackPlaylist.clip_length(i) - 1;
1886     QString blk;
1887     if (trackPlaylist.is_blank(i)) blk = "(blank)";
1888     kDebug()<<"CLIP "<<i<<": ("<<blankStart<<'x'<<blankStart + blankDuration<<")"<<blk;
1889     }*/
1890     mlt_service_unlock(service.get_service());
1891     if (track != 0 && trackPlaylist.count() <= clipIndex) mltCheckLength(&tractor);
1892     m_isBlocked = false;
1893     return true;
1894 }
1895
1896 int Render::mltGetSpaceLength(const GenTime pos, int track, bool fromBlankStart)
1897 {
1898     if (!m_mltProducer) {
1899         kDebug() << "PLAYLIST NOT INITIALISED //////";
1900         return 0;
1901     }
1902     Mlt::Producer parentProd(m_mltProducer->parent());
1903     if (parentProd.get_producer() == NULL) {
1904         kDebug() << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////";
1905         return 0;
1906     }
1907
1908     Mlt::Service service(parentProd.get_service());
1909     Mlt::Tractor tractor(service);
1910     int insertPos = pos.frames(m_fps);
1911
1912     Mlt::Producer trackProducer(tractor.track(track));
1913     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1914     int clipIndex = trackPlaylist.get_clip_index_at(insertPos);
1915     if (clipIndex == trackPlaylist.count()) {
1916         // We are after the end of the playlist
1917         return -1;
1918     }
1919     if (!trackPlaylist.is_blank(clipIndex)) return 0;
1920     if (fromBlankStart) return trackPlaylist.clip_length(clipIndex);
1921     return trackPlaylist.clip_length(clipIndex) + trackPlaylist.clip_start(clipIndex) - insertPos;
1922 }
1923
1924 int Render::mltTrackDuration(int track)
1925 {
1926     if (!m_mltProducer) {
1927         kDebug() << "PLAYLIST NOT INITIALISED //////";
1928         return -1;
1929     }
1930     Mlt::Producer parentProd(m_mltProducer->parent());
1931     if (parentProd.get_producer() == NULL) {
1932         kDebug() << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////";
1933         return -1;
1934     }
1935
1936     Mlt::Service service(parentProd.get_service());
1937     Mlt::Tractor tractor(service);
1938
1939     Mlt::Producer trackProducer(tractor.track(track));
1940     return trackProducer.get_playtime() - 1;
1941 }
1942
1943 void Render::mltInsertSpace(QMap <int, int> trackClipStartList, QMap <int, int> trackTransitionStartList, int track, const GenTime duration, const GenTime timeOffset)
1944 {
1945     if (!m_mltProducer) {
1946         kDebug() << "PLAYLIST NOT INITIALISED //////";
1947         return;
1948     }
1949     Mlt::Producer parentProd(m_mltProducer->parent());
1950     if (parentProd.get_producer() == NULL) {
1951         kDebug() << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////";
1952         return;
1953     }
1954     //kDebug()<<"// CLP STRT LST: "<<trackClipStartList;
1955     //kDebug()<<"// TRA STRT LST: "<<trackTransitionStartList;
1956
1957     Mlt::Service service(parentProd.get_service());
1958     Mlt::Tractor tractor(service);
1959     mlt_service_lock(service.get_service());
1960     int diff = duration.frames(m_fps);
1961     int offset = timeOffset.frames(m_fps);
1962     int insertPos;
1963
1964     if (track != -1) {
1965         // insert space in one track only
1966         Mlt::Producer trackProducer(tractor.track(track));
1967         Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1968         insertPos = trackClipStartList.value(track);
1969         if (insertPos != -1) {
1970             insertPos += offset;
1971             int clipIndex = trackPlaylist.get_clip_index_at(insertPos);
1972             if (diff > 0) {
1973                 trackPlaylist.insert_blank(clipIndex, diff - 1);
1974             } else {
1975                 if (!trackPlaylist.is_blank(clipIndex)) clipIndex --;
1976                 if (!trackPlaylist.is_blank(clipIndex)) {
1977                     kDebug() << "//// ERROR TRYING TO DELETE SPACE FROM " << insertPos;
1978                 }
1979                 int position = trackPlaylist.clip_start(clipIndex);
1980                 int blankDuration = trackPlaylist.clip_length(clipIndex);
1981                 diff = -diff;
1982                 if (blankDuration - diff == 0) {
1983                     trackPlaylist.remove(clipIndex);
1984                 } else trackPlaylist.remove_region(position, diff);
1985             }
1986             trackPlaylist.consolidate_blanks(0);
1987         }
1988         // now move transitions
1989         mlt_service serv = m_mltProducer->parent().get_service();
1990         mlt_service nextservice = mlt_service_get_producer(serv);
1991         mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
1992         QString mlt_type = mlt_properties_get(properties, "mlt_type");
1993         QString resource = mlt_properties_get(properties, "mlt_service");
1994
1995         while (mlt_type == "transition") {
1996             mlt_transition tr = (mlt_transition) nextservice;
1997             int currentTrack = mlt_transition_get_b_track(tr);
1998             int currentIn = (int) mlt_transition_get_in(tr);
1999             int currentOut = (int) mlt_transition_get_out(tr);
2000             insertPos = trackTransitionStartList.value(track);
2001             if (insertPos != -1) {
2002                 insertPos += offset;
2003                 if (track == currentTrack && currentOut > insertPos && resource != "mix") {
2004                     mlt_transition_set_in_and_out(tr, currentIn + diff, currentOut + diff);
2005                 }
2006             }
2007             nextservice = mlt_service_producer(nextservice);
2008             if (nextservice == NULL) break;
2009             properties = MLT_SERVICE_PROPERTIES(nextservice);
2010             mlt_type = mlt_properties_get(properties, "mlt_type");
2011             resource = mlt_properties_get(properties, "mlt_service");
2012         }
2013     } else {
2014         for (int trackNb = tractor.count() - 1; trackNb >= 1; --trackNb) {
2015             Mlt::Producer trackProducer(tractor.track(trackNb));
2016             Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2017
2018             //int clipNb = trackPlaylist.count();
2019             insertPos = trackClipStartList.value(trackNb);
2020             if (insertPos != -1) {
2021                 insertPos += offset;
2022
2023                 /* kDebug()<<"-------------\nTRACK "<<trackNb<<" HAS "<<clipNb<<" CLPIS";
2024                  kDebug() << "INSERT SPACE AT: "<<insertPos<<", DIFF: "<<diff<<", TK: "<<trackNb;
2025                         for (int i = 0; i < clipNb; i++) {
2026                             kDebug()<<"CLIP "<<i<<", START: "<<trackPlaylist.clip_start(i)<<", END: "<<trackPlaylist.clip_start(i) + trackPlaylist.clip_length(i);
2027                      if (trackPlaylist.is_blank(i)) kDebug()<<"++ BLANK ++ ";
2028                      kDebug()<<"-------------";
2029                  }
2030                  kDebug()<<"END-------------";*/
2031
2032
2033                 int clipIndex = trackPlaylist.get_clip_index_at(insertPos);
2034                 if (diff > 0) {
2035                     trackPlaylist.insert_blank(clipIndex, diff - 1);
2036                 } else {
2037                     if (!trackPlaylist.is_blank(clipIndex)) {
2038                         clipIndex --;
2039                     }
2040                     if (!trackPlaylist.is_blank(clipIndex)) {
2041                         kDebug() << "//// ERROR TRYING TO DELETE SPACE FROM " << insertPos;
2042                     }
2043                     int position = trackPlaylist.clip_start(clipIndex);
2044                     int blankDuration = trackPlaylist.clip_length(clipIndex);
2045                     if (diff + blankDuration == 0) {
2046                         trackPlaylist.remove(clipIndex);
2047                     } else trackPlaylist.remove_region(position, - diff);
2048                 }
2049                 trackPlaylist.consolidate_blanks(0);
2050             }
2051         }
2052         // now move transitions
2053         mlt_service serv = m_mltProducer->parent().get_service();
2054         mlt_service nextservice = mlt_service_get_producer(serv);
2055         mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
2056         QString mlt_type = mlt_properties_get(properties, "mlt_type");
2057         QString resource = mlt_properties_get(properties, "mlt_service");
2058
2059         while (mlt_type == "transition") {
2060             mlt_transition tr = (mlt_transition) nextservice;
2061             int currentIn = (int) mlt_transition_get_in(tr);
2062             int currentOut = (int) mlt_transition_get_out(tr);
2063             int currentTrack = mlt_transition_get_b_track(tr);
2064             insertPos = trackTransitionStartList.value(currentTrack);
2065             if (insertPos != -1) {
2066                 insertPos += offset;
2067                 if (currentOut > insertPos && resource != "mix") {
2068                     mlt_transition_set_in_and_out(tr, currentIn + diff, currentOut + diff);
2069                 }
2070             }
2071             nextservice = mlt_service_producer(nextservice);
2072             if (nextservice == NULL) break;
2073             properties = MLT_SERVICE_PROPERTIES(nextservice);
2074             mlt_type = mlt_properties_get(properties, "mlt_type");
2075             resource = mlt_properties_get(properties, "mlt_service");
2076         }
2077     }
2078     mlt_service_unlock(service.get_service());
2079     mltCheckLength(&tractor);
2080     m_mltConsumer->set("refresh", 1);
2081 }
2082
2083
2084 void Render::mltPasteEffects(Mlt::Producer *source, Mlt::Producer *dest)
2085 {
2086     if (source == dest) return;
2087     Mlt::Service sourceService(source->get_service());
2088     Mlt::Service destService(dest->get_service());
2089
2090     // move all effects to the correct producer
2091     int ct = 0;
2092     Mlt::Filter *filter = sourceService.filter(ct);
2093     while (filter) {
2094         if (filter->get_int("kdenlive_ix") != 0) {
2095             sourceService.detach(*filter);
2096             destService.attach(*filter);
2097         } else ct++;
2098         filter = sourceService.filter(ct);
2099     }
2100 }
2101
2102 int Render::mltChangeClipSpeed(ItemInfo info, ItemInfo speedIndependantInfo, double speed, double /*oldspeed*/, int strobe, Mlt::Producer *prod)
2103 {
2104     m_isBlocked = true;
2105     int newLength = 0;
2106     Mlt::Service service(m_mltProducer->parent().get_service());
2107     if (service.type() != tractor_type) {
2108         kWarning() << "// TRACTOR PROBLEM";
2109         return -1;
2110     }
2111     //kDebug() << "Changing clip speed, set in and out: " << info.cropStart.frames(m_fps) << " to " << (info.endPos - info.startPos).frames(m_fps) - 1;
2112     Mlt::Tractor tractor(service);
2113     Mlt::Producer trackProducer(tractor.track(info.track));
2114     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2115     int startPos = info.startPos.frames(m_fps);
2116     int clipIndex = trackPlaylist.get_clip_index_at(startPos);
2117     int clipLength = trackPlaylist.clip_length(clipIndex);
2118
2119     Mlt::Producer *original = trackPlaylist.get_clip(clipIndex);
2120     if (original == NULL) {
2121         return -1;
2122     }
2123     if (!original->is_valid() || original->is_blank()) {
2124         // invalid clip
2125         delete original;
2126         return -1;
2127     }
2128     Mlt::Producer clipparent = original->parent();
2129     if (!clipparent.is_valid() || clipparent.is_blank()) {
2130         // invalid clip
2131         delete original;
2132         return -1;
2133     }
2134
2135     QString serv = clipparent.get("mlt_service");
2136     QString id = clipparent.get("id");
2137     if (speed <= 0 && speed > -1) speed = 1.0;
2138     //kDebug() << "CLIP SERVICE: " << serv;
2139     if (serv == "avformat" && (speed != 1.0 || strobe > 1)) {
2140         mlt_service_lock(service.get_service());
2141         QString url = QString::fromUtf8(clipparent.get("resource"));
2142         url.append('?' + QString::number(speed));
2143         if (strobe > 1) url.append("&strobe=" + QString::number(strobe));
2144         Mlt::Producer *slowprod = m_slowmotionProducers.value(url);
2145         if (!slowprod || slowprod->get_producer() == NULL) {
2146             char *tmp = decodedString("framebuffer:" + url);
2147             slowprod = new Mlt::Producer(*m_mltProfile, 0, tmp);
2148             if (strobe > 1) slowprod->set("strobe", strobe);
2149             delete[] tmp;
2150             QString producerid = "slowmotion:" + id + ':' + QString::number(speed);
2151             if (strobe > 1) producerid.append(':' + QString::number(strobe));
2152             tmp = decodedString(producerid);
2153             slowprod->set("id", tmp);
2154             delete[] tmp;
2155             // copy producer props
2156             double ar = original->parent().get_double("force_aspect_ratio");
2157             if (ar != 0.0) slowprod->set("force_aspect_ratio", ar);
2158             double fps = original->parent().get_double("force_fps");
2159             if (fps != 0.0) slowprod->set("force_fps", fps);
2160             int threads = original->parent().get_int("threads");
2161             if (threads != 0) slowprod->set("threads", threads);
2162             if (original->parent().get("force_progressive"))
2163                 slowprod->set("force_progressive", original->parent().get_int("force_progressive"));
2164             int ix = original->parent().get_int("video_index");
2165             if (ix != 0) slowprod->set("video_index", ix);
2166             m_slowmotionProducers.insert(url, slowprod);
2167         }
2168         Mlt::Producer *clip = trackPlaylist.replace_with_blank(clipIndex);
2169         trackPlaylist.consolidate_blanks(0);
2170
2171         // Check that the blank space is long enough for our new duration
2172         clipIndex = trackPlaylist.get_clip_index_at(startPos);
2173         int blankEnd = trackPlaylist.clip_start(clipIndex) + trackPlaylist.clip_length(clipIndex);
2174         Mlt::Producer *cut;
2175         if (clipIndex + 1 < trackPlaylist.count() && (startPos + clipLength / speed > blankEnd)) {
2176             GenTime maxLength = GenTime(blankEnd, m_fps) - info.startPos;
2177             cut = slowprod->cut((int)(info.cropStart.frames(m_fps) / speed), (int)(info.cropStart.frames(m_fps) / speed + maxLength.frames(m_fps) - 1));
2178         } else cut = slowprod->cut((int)(info.cropStart.frames(m_fps) / speed), (int)((info.cropStart.frames(m_fps) + clipLength) / speed - 1));
2179
2180         // move all effects to the correct producer
2181         mltPasteEffects(clip, cut);
2182         trackPlaylist.insert_at(startPos, cut, 1);
2183         delete cut;
2184         delete clip;
2185         clipIndex = trackPlaylist.get_clip_index_at(startPos);
2186         newLength = trackPlaylist.clip_length(clipIndex);
2187         mlt_service_unlock(service.get_service());
2188     } else if (speed == 1.0 && strobe < 2) {
2189         mlt_service_lock(service.get_service());
2190
2191         Mlt::Producer *clip = trackPlaylist.replace_with_blank(clipIndex);
2192         trackPlaylist.consolidate_blanks(0);
2193
2194         // Check that the blank space is long enough for our new duration
2195         clipIndex = trackPlaylist.get_clip_index_at(startPos);
2196         int blankEnd = trackPlaylist.clip_start(clipIndex) + trackPlaylist.clip_length(clipIndex);
2197
2198         Mlt::Producer *cut;
2199         int originalStart = (int)(speedIndependantInfo.cropStart.frames(m_fps));
2200         if (clipIndex + 1 < trackPlaylist.count() && (info.startPos + speedIndependantInfo.cropDuration).frames(m_fps) > blankEnd) {
2201             GenTime maxLength = GenTime(blankEnd, m_fps) - info.startPos;
2202             cut = prod->cut(originalStart, (int)(originalStart + maxLength.frames(m_fps) - 1));
2203         } else cut = prod->cut(originalStart, (int)(originalStart + speedIndependantInfo.cropDuration.frames(m_fps)) - 1);
2204
2205         // move all effects to the correct producer
2206         mltPasteEffects(clip, cut);
2207
2208         trackPlaylist.insert_at(startPos, cut, 1);
2209         delete cut;
2210         delete clip;
2211         clipIndex = trackPlaylist.get_clip_index_at(startPos);
2212         newLength = trackPlaylist.clip_length(clipIndex);
2213         mlt_service_unlock(service.get_service());
2214
2215     } else if (serv == "framebuffer") {
2216         mlt_service_lock(service.get_service());
2217         QString url = QString::fromUtf8(clipparent.get("resource"));
2218         url = url.section('?', 0, 0);
2219         url.append('?' + QString::number(speed));
2220         if (strobe > 1) url.append("&strobe=" + QString::number(strobe));
2221         Mlt::Producer *slowprod = m_slowmotionProducers.value(url);
2222         if (!slowprod || slowprod->get_producer() == NULL) {
2223             char *tmp = decodedString("framebuffer:" + url);
2224             slowprod = new Mlt::Producer(*m_mltProfile, 0, tmp);
2225             delete[] tmp;
2226             slowprod->set("strobe", strobe);
2227             QString producerid = "slowmotion:" + id.section(':', 1, 1) + ':' + QString::number(speed);
2228             if (strobe > 1) producerid.append(':' + QString::number(strobe));
2229             tmp = decodedString(producerid);
2230             slowprod->set("id", tmp);
2231             delete[] tmp;
2232             // copy producer props
2233             double ar = original->parent().get_double("force_aspect_ratio");
2234             if (ar != 0.0) slowprod->set("force_aspect_ratio", ar);
2235             double fps = original->parent().get_double("force_fps");
2236             if (fps != 0.0) slowprod->set("force_fps", fps);
2237             if (original->parent().get("force_progressive"))
2238                 slowprod->set("force_progressive", original->parent().get_int("force_progressive"));
2239             int threads = original->parent().get_int("threads");
2240             if (threads != 0) slowprod->set("threads", threads);
2241             int ix = original->parent().get_int("video_index");
2242             if (ix != 0) slowprod->set("video_index", ix);
2243             m_slowmotionProducers.insert(url, slowprod);
2244         }
2245         Mlt::Producer *clip = trackPlaylist.replace_with_blank(clipIndex);
2246         trackPlaylist.consolidate_blanks(0);
2247
2248         GenTime duration = speedIndependantInfo.cropDuration / speed;
2249         int originalStart = (int)(speedIndependantInfo.cropStart.frames(m_fps) / speed);
2250
2251         // Check that the blank space is long enough for our new duration
2252         clipIndex = trackPlaylist.get_clip_index_at(startPos);
2253         int blankEnd = trackPlaylist.clip_start(clipIndex) + trackPlaylist.clip_length(clipIndex);
2254
2255         Mlt::Producer *cut;
2256         if (clipIndex + 1 < trackPlaylist.count() && (info.startPos + duration).frames(m_fps) > blankEnd) {
2257             GenTime maxLength = GenTime(blankEnd, m_fps) - info.startPos;
2258             cut = slowprod->cut(originalStart, (int)(originalStart + maxLength.frames(m_fps) - 1));
2259         } else cut = slowprod->cut(originalStart, (int)(originalStart + duration.frames(m_fps)) - 1);
2260
2261         // move all effects to the correct producer
2262         mltPasteEffects(clip, cut);
2263
2264         trackPlaylist.insert_at(startPos, cut, 1);
2265         delete cut;
2266         delete clip;
2267         clipIndex = trackPlaylist.get_clip_index_at(startPos);
2268         newLength = trackPlaylist.clip_length(clipIndex);
2269
2270         mlt_service_unlock(service.get_service());
2271     }
2272     delete original;
2273     if (clipIndex + 1 == trackPlaylist.count()) mltCheckLength(&tractor);
2274     m_isBlocked = false;
2275     return newLength;
2276 }
2277
2278 bool Render::mltRemoveTrackEffect(int track, QString index, bool updateIndex)
2279 {
2280     Mlt::Service service(m_mltProducer->parent().get_service());
2281     bool success = false;
2282     Mlt::Tractor tractor(service);
2283     Mlt::Producer trackProducer(tractor.track(track));
2284     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2285     Mlt::Service clipService(trackPlaylist.get_service());
2286
2287     m_isBlocked = true;
2288     mlt_service_lock(service.get_service());
2289     int ct = 0;
2290     Mlt::Filter *filter = clipService.filter(ct);
2291     while (filter) {
2292         if ((index == "-1" && strcmp(filter->get("kdenlive_id"), ""))  || filter->get_int("kdenlive_ix") == index.toInt()) {
2293             if (clipService.detach(*filter) == 0) success = true;
2294         } else if (updateIndex) {
2295             // Adjust the other effects index
2296             if (filter->get_int("kdenlive_ix") > index.toInt()) filter->set("kdenlive_ix", filter->get_int("kdenlive_ix") - 1);
2297             ct++;
2298         } else ct++;
2299         filter = clipService.filter(ct);
2300     }
2301     m_isBlocked = false;
2302     mlt_service_unlock(service.get_service());
2303     refresh();
2304     return success;
2305 }
2306
2307 bool Render::mltRemoveEffect(int track, GenTime position, QString index, bool updateIndex, bool doRefresh)
2308 {
2309     if (position < GenTime()) {
2310         // Remove track effect
2311         return mltRemoveTrackEffect(track, index, updateIndex);
2312     }
2313     Mlt::Service service(m_mltProducer->parent().get_service());
2314     bool success = false;
2315     Mlt::Tractor tractor(service);
2316     Mlt::Producer trackProducer(tractor.track(track));
2317     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2318
2319     int clipIndex = trackPlaylist.get_clip_index_at((int) position.frames(m_fps));
2320     Mlt::Producer *clip = trackPlaylist.get_clip(clipIndex);
2321     if (!clip) {
2322         kDebug() << " / / / CANNOT FIND CLIP TO REMOVE EFFECT";
2323         return false;
2324     }
2325
2326     Mlt::Service clipService(clip->get_service());
2327     int duration = clip->get_playtime();
2328     if (doRefresh) {
2329         // Check if clip is visible in monitor
2330         int diff = trackPlaylist.clip_start(clipIndex) + duration - m_mltProducer->position();
2331         if (diff < 0 || diff > duration) doRefresh = false;
2332     }
2333     delete clip;
2334
2335 //    if (tag.startsWith("ladspa")) tag = "ladspa";
2336     m_isBlocked = true;
2337     mlt_service_lock(service.get_service());
2338     int ct = 0;
2339     Mlt::Filter *filter = clipService.filter(ct);
2340     while (filter) {
2341         if ((index == "-1" && strcmp(filter->get("kdenlive_id"), ""))  || filter->get_int("kdenlive_ix") == index.toInt()) {// && filter->get("kdenlive_id") == id) {
2342             if (clipService.detach(*filter) == 0) success = true;
2343             //kDebug()<<"Deleted filter id:"<<filter->get("kdenlive_id")<<", ix:"<<filter->get("kdenlive_ix")<<", SERVICE:"<<filter->get("mlt_service");
2344         } else if (updateIndex) {
2345             // Adjust the other effects index
2346             if (filter->get_int("kdenlive_ix") > index.toInt()) filter->set("kdenlive_ix", filter->get_int("kdenlive_ix") - 1);
2347             ct++;
2348         } else ct++;
2349         filter = clipService.filter(ct);
2350     }
2351     m_isBlocked = false;
2352     mlt_service_unlock(service.get_service());
2353     if (doRefresh) refresh();
2354     return success;
2355 }
2356
2357 bool Render::mltAddTrackEffect(int track, EffectsParameterList params)
2358 {
2359     Mlt::Service service(m_mltProducer->parent().get_service());
2360     Mlt::Tractor tractor(service);
2361     Mlt::Producer trackProducer(tractor.track(track));
2362     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2363     Mlt::Service trackService(trackProducer.get_service()); //trackPlaylist
2364     return mltAddEffect(trackService, params, 15000, true);
2365 }
2366
2367
2368 bool Render::mltAddEffect(int track, GenTime position, EffectsParameterList params, bool doRefresh)
2369 {
2370
2371     Mlt::Service service(m_mltProducer->parent().get_service());
2372
2373     Mlt::Tractor tractor(service);
2374     Mlt::Producer trackProducer(tractor.track(track));
2375     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2376
2377     int clipIndex = trackPlaylist.get_clip_index_at((int) position.frames(m_fps));
2378     Mlt::Producer *clip = trackPlaylist.get_clip(clipIndex);
2379     if (!clip) {
2380         return false;
2381     }
2382
2383     Mlt::Service clipService(clip->get_service());
2384     int duration = clip->get_playtime();
2385     if (doRefresh) {
2386         // Check if clip is visible in monitor
2387         int diff = trackPlaylist.clip_start(clipIndex) + duration - m_mltProducer->position();
2388         if (diff < 0 || diff > duration) doRefresh = false;
2389     }
2390     delete clip;
2391     return mltAddEffect(clipService, params, duration, doRefresh);
2392 }
2393
2394 bool Render::mltAddEffect(Mlt::Service service, EffectsParameterList params, int duration, bool doRefresh)
2395 {
2396     bool updateIndex = false;
2397     const int filter_ix = params.paramValue("kdenlive_ix").toInt();
2398     const QString region =  params.paramValue("region");
2399     int ct = 0;
2400     m_isBlocked = true;
2401     mlt_service_lock(service.get_service());
2402
2403     Mlt::Filter *filter = service.filter(ct);
2404     while (filter) {
2405         if (filter->get_int("kdenlive_ix") == filter_ix) {
2406             // A filter at that position already existed, so we will increase all indexes later
2407             updateIndex = true;
2408             break;
2409         }
2410         ct++;
2411         filter = service.filter(ct);
2412     }
2413
2414     if (params.paramValue("id") == "speed") {
2415         // special case, speed effect is not really inserted, we just update the other effects index (kdenlive_ix)
2416         ct = 0;
2417         filter = service.filter(ct);
2418         while (filter) {
2419             if (filter->get_int("kdenlive_ix") >= filter_ix) {
2420                 if (updateIndex) filter->set("kdenlive_ix", filter->get_int("kdenlive_ix") + 1);
2421             }
2422             ct++;
2423             filter = service.filter(ct);
2424         }
2425         m_isBlocked = false;
2426         mlt_service_unlock(service.get_service());
2427         if (doRefresh) refresh();
2428         return true;
2429     }
2430
2431
2432     // temporarily remove all effects after insert point
2433     QList <Mlt::Filter *> filtersList;
2434     ct = 0;
2435     filter = service.filter(ct);
2436     while (filter) {
2437         if (filter->get_int("kdenlive_ix") >= filter_ix) {
2438             filtersList.append(filter);
2439             service.detach(*filter);
2440         } else ct++;
2441         filter = service.filter(ct);
2442     }
2443
2444     // create filter
2445     QString tag =  params.paramValue("tag");
2446     kDebug() << " / / INSERTING EFFECT: " << tag << ", REGI: " << region;
2447     if (tag.startsWith("ladspa")) tag = "ladspa";
2448     char *filterTag = decodedString(tag);
2449     char *filterId = decodedString(params.paramValue("id"));
2450     QHash<QString, QString>::Iterator it;
2451     QString kfr = params.paramValue("keyframes");
2452
2453     if (!kfr.isEmpty()) {
2454         QStringList keyFrames = kfr.split(';', QString::SkipEmptyParts);
2455         kDebug() << "// ADDING KEYFRAME EFFECT: " << params.paramValue("keyframes");
2456         char *starttag = decodedString(params.paramValue("starttag", "start"));
2457         char *endtag = decodedString(params.paramValue("endtag", "end"));
2458         kDebug() << "// ADDING KEYFRAME TAGS: " << starttag << ", " << endtag;
2459         //double max = params.paramValue("max").toDouble();
2460         double min = params.paramValue("min").toDouble();
2461         double factor = params.paramValue("factor", "1").toDouble();
2462         params.removeParam("starttag");
2463         params.removeParam("endtag");
2464         params.removeParam("keyframes");
2465         params.removeParam("min");
2466         params.removeParam("max");
2467         params.removeParam("factor");
2468         int offset = 0;
2469         // Special case, only one keyframe, means we want a constant value
2470         if (keyFrames.count() == 1) {
2471             Mlt::Filter *filter = new Mlt::Filter(*m_mltProfile, filterTag);
2472             if (filter && filter->is_valid()) {
2473                 filter->set("kdenlive_id", filterId);
2474                 int x1 = keyFrames.at(0).section(':', 0, 0).toInt();
2475                 double y1 = keyFrames.at(0).section(':', 1, 1).toDouble();
2476                 for (int j = 0; j < params.count(); j++) {
2477                     char *name = decodedString(params.at(j).name());
2478                     char *value = decodedString(params.at(j).value());
2479                     filter->set(name, value);
2480                     delete[] name;
2481                     delete[] value;
2482                 }
2483                 filter->set("in", x1);
2484                 //kDebug() << "// ADDING KEYFRAME vals: " << min<<" / "<<max<<", "<<y1<<", factor: "<<factor;
2485                 filter->set(starttag, QString::number((min + y1) / factor).toUtf8().data());
2486                 service.attach(*filter);
2487             }
2488         } else for (int i = 0; i < keyFrames.size() - 1; ++i) {
2489                 Mlt::Filter *filter = new Mlt::Filter(*m_mltProfile, filterTag);
2490                 if (filter && filter->is_valid()) {
2491                     filter->set("kdenlive_id", filterId);
2492                     int x1 = keyFrames.at(i).section(':', 0, 0).toInt() + offset;
2493                     double y1 = keyFrames.at(i).section(':', 1, 1).toDouble();
2494                     int x2 = keyFrames.at(i + 1).section(':', 0, 0).toInt();
2495                     double y2 = keyFrames.at(i + 1).section(':', 1, 1).toDouble();
2496                     if (x2 == -1) x2 = duration;
2497
2498                     for (int j = 0; j < params.count(); j++) {
2499                         char *name = decodedString(params.at(j).name());
2500                         char *value = decodedString(params.at(j).value());
2501                         filter->set(name, value);
2502                         delete[] name;
2503                         delete[] value;
2504                     }
2505
2506                     filter->set("in", x1);
2507                     filter->set("out", x2);
2508                     //kDebug() << "// ADDING KEYFRAME vals: " << min<<" / "<<max<<", "<<y1<<", factor: "<<factor;
2509                     filter->set(starttag, QString::number((min + y1) / factor).toUtf8().data());
2510                     filter->set(endtag, QString::number((min + y2) / factor).toUtf8().data());
2511                     service.attach(*filter);
2512                     offset = 1;
2513                 }
2514             }
2515         delete[] starttag;
2516         delete[] endtag;
2517     } else {
2518         Mlt::Filter *filter;
2519         QString prefix;
2520         if (!region.isEmpty()) {
2521             filter = new Mlt::Filter(*m_mltProfile, "region");
2522         } else filter = new Mlt::Filter(*m_mltProfile, filterTag);
2523         if (filter && filter->is_valid()) {
2524             filter->set("kdenlive_id", filterId);
2525             if (!region.isEmpty()) {
2526                 char *tmp = decodedString(region);
2527                 filter->set("resource", tmp);
2528                 tmp = decodedString(params.paramValue("kdenlive_ix"));
2529                 filter->set("kdenlive_ix", tmp);
2530                 filter->set("filter0", filterTag);
2531                 prefix = "filter0.";
2532                 delete[] tmp;
2533                 params.removeParam("id");
2534                 params.removeParam("region");
2535                 params.removeParam("kdenlive_ix");
2536             }
2537         } else {
2538             kDebug() << "filter is NULL";
2539             m_isBlocked = false;
2540             mlt_service_unlock(service.get_service());
2541             return false;
2542         }
2543         params.removeParam("kdenlive_id");
2544
2545         for (int j = 0; j < params.count(); j++) {
2546             char *name = decodedString(prefix + params.at(j).name());
2547             char *value = decodedString(params.at(j).value());
2548             filter->set(name, value);
2549             delete[] name;
2550             delete[] value;
2551         }
2552
2553         if (tag == "sox") {
2554             QString effectArgs = params.paramValue("id").section('_', 1);
2555
2556             params.removeParam("id");
2557             params.removeParam("kdenlive_ix");
2558             params.removeParam("tag");
2559             params.removeParam("disable");
2560             params.removeParam("region");
2561
2562             for (int j = 0; j < params.count(); j++) {
2563                 effectArgs.append(' ' + params.at(j).value());
2564             }
2565             //kDebug() << "SOX EFFECTS: " << effectArgs.simplified();
2566             char *value = decodedString(effectArgs.simplified());
2567             filter->set("effect", value);
2568             delete[] value;
2569         }
2570
2571         if (params.paramValue("id") == "pan_zoom") {
2572             filter->set_in_and_out(service.get_int("in"), service.get_int("out") + 1);
2573         }
2574
2575         // attach filter to the clip
2576         service.attach(*filter);
2577     }
2578     delete[] filterId;
2579     delete[] filterTag;
2580
2581     // re-add following filters
2582     for (int i = 0; i < filtersList.count(); i++) {
2583         Mlt::Filter *filter = filtersList.at(i);
2584         if (updateIndex)
2585             filter->set("kdenlive_ix", filter->get_int("kdenlive_ix") + 1);
2586         service.attach(*filter);
2587     }
2588     m_isBlocked = false;
2589     mlt_service_unlock(service.get_service());
2590     if (doRefresh) refresh();
2591     return true;
2592 }
2593
2594 bool Render::mltEditTrackEffect(int track, EffectsParameterList params)
2595 {
2596     Mlt::Service service(m_mltProducer->parent().get_service());
2597     Mlt::Tractor tractor(service);
2598     Mlt::Producer trackProducer(tractor.track(track));
2599     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2600     Mlt::Service clipService(trackPlaylist.get_service());
2601     m_isBlocked = true;
2602     int ct = 0;
2603     QString index = params.paramValue("kdenlive_ix");
2604     QString tag =  params.paramValue("tag");
2605
2606     Mlt::Filter *filter = clipService.filter(ct);
2607     while (filter) {
2608         if (filter->get_int("kdenlive_ix") == index.toInt()) {
2609             break;
2610         }
2611         ct++;
2612         filter = clipService.filter(ct);
2613     }
2614
2615     if (!filter) {
2616         kDebug() << "WARINIG, FILTER FOR EDITING NOT FOUND, ADDING IT! " << index << ", " << tag;
2617         // filter was not found, it was probably a disabled filter, so add it to the correct place...
2618
2619         bool success = false;//mltAddTrackEffect(track, params);
2620         m_isBlocked = false;
2621         return success;
2622     }
2623     QString prefix;
2624     QString ser = filter->get("mlt_service");
2625     if (ser == "region") prefix = "filter0.";
2626     mlt_service_lock(service.get_service());
2627     for (int j = 0; j < params.count(); j++) {
2628         char *name = decodedString(prefix + params.at(j).name());
2629         char *value = decodedString(params.at(j).value());
2630         filter->set(name, value);
2631         delete[] name;
2632         delete[] value;
2633     }
2634     mlt_service_unlock(service.get_service());
2635
2636     m_isBlocked = false;
2637     refresh();
2638     return true;
2639
2640 }
2641
2642 bool Render::mltEditEffect(int track, GenTime position, EffectsParameterList params)
2643 {
2644     QString index = params.paramValue("kdenlive_ix");
2645     QString tag =  params.paramValue("tag");
2646
2647     if (!params.paramValue("keyframes").isEmpty() || /*it.key().startsWith("#") || */tag.startsWith("ladspa") || tag == "sox" || tag == "autotrack_rectangle" || params.hasParam("region")) {
2648         // This is a keyframe effect, to edit it, we remove it and re-add it.
2649         bool success = mltRemoveEffect(track, position, index, false);
2650         if (!success) kDebug() << "// ERROR Removing effect : " << index;
2651         if (position < GenTime()) success = mltAddTrackEffect(track, params);
2652         else success = mltAddEffect(track, position, params);
2653         if (!success) kDebug() << "// ERROR Adding effect : " << index;
2654         return success;
2655     }
2656     if (position < GenTime()) {
2657         return mltEditTrackEffect(track, params);
2658     }
2659     // find filter
2660     Mlt::Service service(m_mltProducer->parent().get_service());
2661     Mlt::Tractor tractor(service);
2662     Mlt::Producer trackProducer(tractor.track(track));
2663     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2664
2665     int clipIndex = trackPlaylist.get_clip_index_at((int) position.frames(m_fps));
2666     Mlt::Producer *clip = trackPlaylist.get_clip(clipIndex);
2667     if (!clip) {
2668         kDebug() << "WARINIG, CANNOT FIND CLIP ON track: " << track << ", AT POS: " << position.frames(m_fps);
2669         return false;
2670     }
2671
2672     Mlt::Service clipService(clip->get_service());
2673     int duration = clip->get_playtime();
2674     bool doRefresh = true;
2675     // Check if clip is visible in monitor
2676     int diff = trackPlaylist.clip_start(clipIndex) + duration - m_mltProducer->position();
2677     if (diff < 0 || diff > duration) doRefresh = false;
2678     delete clip;
2679     m_isBlocked = true;
2680     int ct = 0;
2681     /* kDebug() << "EDITING FILTER: "<<index <<", "<<tag;
2682     kDebug() << "EFFect stack: ++++++++++++++++++++++++++";
2683     while (filter) {
2684         kDebug() << "Filter: "<< filter->get("kdenlive_id") <<", IX: "<<filter->get("kdenlive_ix");
2685         ct++;
2686         filter = clipService.filter(ct);
2687     }
2688     kDebug() << "++++++++++++++++++++++++++";
2689     ct = 0;
2690     filter = clipService.filter(ct); */
2691
2692     Mlt::Filter *filter = clipService.filter(ct);
2693     while (filter) {
2694         if (filter->get_int("kdenlive_ix") == index.toInt()) {
2695             break;
2696         }
2697         ct++;
2698         filter = clipService.filter(ct);
2699     }
2700
2701     if (!filter) {
2702         kDebug() << "WARINIG, FILTER FOR EDITING NOT FOUND, ADDING IT! " << index << ", " << tag;
2703         // filter was not found, it was probably a disabled filter, so add it to the correct place...
2704
2705         bool success = mltAddEffect(track, position, params);
2706         m_isBlocked = false;
2707         return success;
2708     }
2709     QString prefix;
2710     QString ser = filter->get("mlt_service");
2711     if (ser == "region") prefix = "filter0.";
2712     mlt_service_lock(service.get_service());
2713     for (int j = 0; j < params.count(); j++) {
2714         char *name = decodedString(prefix + params.at(j).name());
2715         char *value = decodedString(params.at(j).value());
2716         filter->set(name, value);
2717         delete[] name;
2718         delete[] value;
2719     }
2720     mlt_service_unlock(service.get_service());
2721
2722     m_isBlocked = false;
2723     if (doRefresh) refresh();
2724     return true;
2725 }
2726
2727 void Render::mltUpdateEffectPosition(int track, GenTime position, int oldPos, int newPos)
2728 {
2729     Mlt::Service service(m_mltProducer->parent().get_service());
2730     Mlt::Tractor tractor(service);
2731     Mlt::Producer trackProducer(tractor.track(track));
2732     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2733
2734     int clipIndex = trackPlaylist.get_clip_index_at((int) position.frames(m_fps));
2735     Mlt::Producer *clip = trackPlaylist.get_clip(clipIndex);
2736     if (!clip) {
2737         kDebug() << "WARINIG, CANNOT FIND CLIP ON track: " << track << ", AT POS: " << position.frames(m_fps);
2738         return;
2739     }
2740
2741     Mlt::Service clipService(clip->get_service());
2742     int duration = clip->get_playtime();
2743     bool doRefresh = true;
2744     // Check if clip is visible in monitor
2745     int diff = trackPlaylist.clip_start(clipIndex) + duration - m_mltProducer->position();
2746     if (diff < 0 || diff > duration) doRefresh = false;
2747     delete clip;
2748
2749     m_isBlocked = true;
2750     int ct = 0;
2751     Mlt::Filter *filter = clipService.filter(ct);
2752     while (filter) {
2753         int pos = filter->get_int("kdenlive_ix");
2754         if (pos == oldPos) {
2755             filter->set("kdenlive_ix", newPos);
2756         } else ct++;
2757         filter = clipService.filter(ct);
2758     }
2759
2760     m_isBlocked = false;
2761     if (doRefresh) refresh();
2762 }
2763
2764 void Render::mltMoveEffect(int track, GenTime position, int oldPos, int newPos)
2765 {
2766     if (position < GenTime()) {
2767         mltMoveTrackEffect(track, oldPos, newPos);
2768         return;
2769     }
2770     Mlt::Service service(m_mltProducer->parent().get_service());
2771     Mlt::Tractor tractor(service);
2772     Mlt::Producer trackProducer(tractor.track(track));
2773     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2774
2775     int clipIndex = trackPlaylist.get_clip_index_at((int) position.frames(m_fps));
2776     Mlt::Producer *clip = trackPlaylist.get_clip(clipIndex);
2777     if (!clip) {
2778         kDebug() << "WARINIG, CANNOT FIND CLIP ON track: " << track << ", AT POS: " << position.frames(m_fps);
2779         return;
2780     }
2781
2782     Mlt::Service clipService(clip->get_service());
2783     int duration = clip->get_playtime();
2784     bool doRefresh = true;
2785     // Check if clip is visible in monitor
2786     int diff = trackPlaylist.clip_start(clipIndex) + duration - m_mltProducer->position();
2787     if (diff < 0 || diff > duration) doRefresh = false;
2788     delete clip;
2789
2790     m_isBlocked = true;
2791     int ct = 0;
2792     QList <Mlt::Filter *> filtersList;
2793     Mlt::Filter *filter = clipService.filter(ct);
2794     bool found = false;
2795     if (newPos > oldPos) {
2796         while (filter) {
2797             if (!found && filter->get_int("kdenlive_ix") == oldPos) {
2798                 filter->set("kdenlive_ix", newPos);
2799                 filtersList.append(filter);
2800                 clipService.detach(*filter);
2801                 filter = clipService.filter(ct);
2802                 while (filter && filter->get_int("kdenlive_ix") <= newPos) {
2803                     filter->set("kdenlive_ix", filter->get_int("kdenlive_ix") - 1);
2804                     ct++;
2805                     filter = clipService.filter(ct);
2806                 }
2807                 found = true;
2808             }
2809             if (filter && filter->get_int("kdenlive_ix") > newPos) {
2810                 filtersList.append(filter);
2811                 clipService.detach(*filter);
2812             } else ct++;
2813             filter = clipService.filter(ct);
2814         }
2815     } else {
2816         while (filter) {
2817             if (filter->get_int("kdenlive_ix") == oldPos) {
2818                 filter->set("kdenlive_ix", newPos);
2819                 filtersList.append(filter);
2820                 clipService.detach(*filter);
2821             } else ct++;
2822             filter = clipService.filter(ct);
2823         }
2824
2825         ct = 0;
2826         filter = clipService.filter(ct);
2827         while (filter) {
2828             int pos = filter->get_int("kdenlive_ix");
2829             if (pos >= newPos) {
2830                 if (pos < oldPos) filter->set("kdenlive_ix", pos + 1);
2831                 filtersList.append(filter);
2832                 clipService.detach(*filter);
2833             } else ct++;
2834             filter = clipService.filter(ct);
2835         }
2836     }
2837
2838     for (int i = 0; i < filtersList.count(); i++) {
2839         clipService.attach(*(filtersList.at(i)));
2840     }
2841
2842     m_isBlocked = false;
2843     if (doRefresh) refresh();
2844 }
2845
2846 void Render::mltMoveTrackEffect(int track, int oldPos, int newPos)
2847 {
2848     Mlt::Service service(m_mltProducer->parent().get_service());
2849     Mlt::Tractor tractor(service);
2850     Mlt::Producer trackProducer(tractor.track(track));
2851     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2852
2853     Mlt::Service clipService(trackPlaylist.get_service());
2854
2855     m_isBlocked = true;
2856     int ct = 0;
2857     QList <Mlt::Filter *> filtersList;
2858     Mlt::Filter *filter = clipService.filter(ct);
2859     bool found = false;
2860     if (newPos > oldPos) {
2861         while (filter) {
2862             if (!found && filter->get_int("kdenlive_ix") == oldPos) {
2863                 filter->set("kdenlive_ix", newPos);
2864                 filtersList.append(filter);
2865                 clipService.detach(*filter);
2866                 filter = clipService.filter(ct);
2867                 while (filter && filter->get_int("kdenlive_ix") <= newPos) {
2868                     filter->set("kdenlive_ix", filter->get_int("kdenlive_ix") - 1);
2869                     ct++;
2870                     filter = clipService.filter(ct);
2871                 }
2872                 found = true;
2873             }
2874             if (filter && filter->get_int("kdenlive_ix") > newPos) {
2875                 filtersList.append(filter);
2876                 clipService.detach(*filter);
2877             } else ct++;
2878             filter = clipService.filter(ct);
2879         }
2880     } else {
2881         while (filter) {
2882             if (filter->get_int("kdenlive_ix") == oldPos) {
2883                 filter->set("kdenlive_ix", newPos);
2884                 filtersList.append(filter);
2885                 clipService.detach(*filter);
2886             } else ct++;
2887             filter = clipService.filter(ct);
2888         }
2889
2890         ct = 0;
2891         filter = clipService.filter(ct);
2892         while (filter) {
2893             int pos = filter->get_int("kdenlive_ix");
2894             if (pos >= newPos) {
2895                 if (pos < oldPos) filter->set("kdenlive_ix", pos + 1);
2896                 filtersList.append(filter);
2897                 clipService.detach(*filter);
2898             } else ct++;
2899             filter = clipService.filter(ct);
2900         }
2901     }
2902
2903     for (int i = 0; i < filtersList.count(); i++) {
2904         clipService.attach(*(filtersList.at(i)));
2905     }
2906     m_isBlocked = false;
2907     refresh();
2908 }
2909
2910 bool Render::mltResizeClipEnd(ItemInfo info, GenTime clipDuration)
2911 {
2912     m_isBlocked = true;
2913     Mlt::Service service(m_mltProducer->parent().get_service());
2914     Mlt::Tractor tractor(service);
2915     Mlt::Producer trackProducer(tractor.track(info.track));
2916     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2917
2918     /* // Display playlist info
2919     kDebug()<<"////////////  BEFORE RESIZE";
2920     for (int i = 0; i < trackPlaylist.count(); i++) {
2921     int blankStart = trackPlaylist.clip_start(i);
2922     int blankDuration = trackPlaylist.clip_length(i) - 1;
2923     QString blk;
2924     if (trackPlaylist.is_blank(i)) blk = "(blank)";
2925     kDebug()<<"CLIP "<<i<<": ("<<blankStart<<'x'<<blankStart + blankDuration<<")"<<blk;
2926     }*/
2927
2928     if (trackPlaylist.is_blank_at((int) info.startPos.frames(m_fps))) {
2929         kDebug() << "////////  ERROR RSIZING BLANK CLIP!!!!!!!!!!!";
2930         m_isBlocked = false;
2931         return false;
2932     }
2933     mlt_service_lock(service.get_service());
2934     int clipIndex = trackPlaylist.get_clip_index_at((int) info.startPos.frames(m_fps));
2935     //kDebug() << "// SELECTED CLIP START: " << trackPlaylist.clip_start(clipIndex);
2936     Mlt::Producer *clip = trackPlaylist.get_clip(clipIndex);
2937     int previousStart = clip->get_in();
2938     int newDuration = (int) clipDuration.frames(m_fps) - 1;
2939     int currentOut = (int)(info.cropStart + info.cropDuration).frames(m_fps) - 1;
2940     int diff = newDuration - (trackPlaylist.clip_length(clipIndex) - 1);
2941     if (currentOut > clip->get_length()) {
2942         clip->parent().set("length", currentOut + 1);
2943         clip->parent().set("out", currentOut);
2944         clip->set("length", currentOut + 1);
2945     }
2946     /*if (newDuration > clip->get_out()) {
2947         clip->parent().set_in_and_out(0, newDuration + 1);
2948         clip->set_in_and_out(0, newDuration + 1);
2949     }*/
2950     delete clip;
2951     trackPlaylist.resize_clip(clipIndex, previousStart, newDuration + previousStart);
2952     trackPlaylist.consolidate_blanks(0);
2953     // skip to next clip
2954     clipIndex++;
2955     //kDebug() << "////////  RESIZE CLIP: " << clipIndex << "( pos: " << info.startPos.frames(25) << "), DIFF: " << diff << ", CURRENT DUR: " << previousDuration << ", NEW DUR: " << newDuration << ", IX: " << clipIndex << ", MAX: " << trackPlaylist.count();
2956     if (diff > 0) {
2957         // clip was made longer, trim next blank if there is one.
2958         if (clipIndex < trackPlaylist.count()) {
2959             // If this is not the last clip in playlist
2960             if (trackPlaylist.is_blank(clipIndex)) {
2961                 int blankStart = trackPlaylist.clip_start(clipIndex);
2962                 int blankDuration = trackPlaylist.clip_length(clipIndex);
2963                 if (diff > blankDuration) {
2964                     kDebug() << "// ERROR blank clip is not large enough to get back required space!!!";
2965                 }
2966                 if (diff - blankDuration == 0) {
2967                     trackPlaylist.remove(clipIndex);
2968                 } else trackPlaylist.remove_region(blankStart, diff);
2969             } else {
2970                 kDebug() << "/// RESIZE ERROR, NXT CLIP IS NOT BLK: " << clipIndex;
2971             }
2972         }
2973     } else if (clipIndex != trackPlaylist.count()) trackPlaylist.insert_blank(clipIndex, 0 - diff - 1);
2974     trackPlaylist.consolidate_blanks(0);
2975     mlt_service_unlock(service.get_service());
2976
2977     if (info.track != 0 && clipIndex == trackPlaylist.count()) mltCheckLength(&tractor);
2978     /*if (QString(clip->parent().get("transparency")).toInt() == 1) {
2979         //mltResizeTransparency(previousStart, previousStart, previousStart + newDuration, track, QString(clip->parent().get("id")).toInt());
2980         mltDeleteTransparency(info.startPos.frames(m_fps), info.track, QString(clip->parent().get("id")).toInt());
2981         ItemInfo transpinfo;
2982         transpinfo.startPos = info.startPos;
2983         transpinfo.endPos = info.startPos + clipDuration;
2984         transpinfo.track = info.track;
2985         mltAddClipTransparency(transpinfo, info.track - 1, QString(clip->parent().get("id")).toInt());
2986     }*/
2987     m_isBlocked = false;
2988     m_mltConsumer->set("refresh", 1);
2989     return true;
2990 }
2991
2992 void Render::mltChangeTrackState(int track, bool mute, bool blind)
2993 {
2994     Mlt::Service service(m_mltProducer->parent().get_service());
2995     Mlt::Tractor tractor(service);
2996     Mlt::Producer trackProducer(tractor.track(track));
2997
2998     if (mute) {
2999         if (blind) trackProducer.set("hide", 3);
3000         else trackProducer.set("hide", 2);
3001     } else if (blind) {
3002         trackProducer.set("hide", 1);
3003     } else {
3004         trackProducer.set("hide", 0);
3005     }
3006     tractor.multitrack()->refresh();
3007     tractor.refresh();
3008     refresh();
3009 }
3010
3011
3012 bool Render::mltResizeClipCrop(ItemInfo info, GenTime diff)
3013 {
3014     Mlt::Service service(m_mltProducer->parent().get_service());
3015     int frameOffset = (int) diff.frames(m_fps);
3016     Mlt::Tractor tractor(service);
3017     Mlt::Producer trackProducer(tractor.track(info.track));
3018     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
3019     if (trackPlaylist.is_blank_at(info.startPos.frames(m_fps))) {
3020         kDebug() << "////////  ERROR RSIZING BLANK CLIP!!!!!!!!!!!";
3021         return false;
3022     }
3023     mlt_service_lock(service.get_service());
3024     int clipIndex = trackPlaylist.get_clip_index_at(info.startPos.frames(m_fps));
3025     Mlt::Producer *clip = trackPlaylist.get_clip(clipIndex);
3026     if (clip == NULL) {
3027         kDebug() << "////////  ERROR RSIZING NULL CLIP!!!!!!!!!!!";
3028         mlt_service_unlock(service.get_service());
3029         return false;
3030     }
3031     int previousStart = clip->get_in();
3032     int previousOut = clip->get_out();
3033     delete clip;
3034     m_isBlocked = true;
3035     trackPlaylist.resize_clip(clipIndex, previousStart + frameOffset, previousOut + frameOffset);
3036     m_isBlocked = false;
3037     mlt_service_unlock(service.get_service());
3038     m_mltConsumer->set("refresh", 1);
3039     return true;
3040 }
3041
3042 bool Render::mltResizeClipStart(ItemInfo info, GenTime diff)
3043 {
3044     //kDebug() << "////////  RSIZING CLIP from: "<<info.startPos.frames(25)<<" to "<<diff.frames(25);
3045     Mlt::Service service(m_mltProducer->parent().get_service());
3046     int moveFrame = (int) diff.frames(m_fps);
3047     Mlt::Tractor tractor(service);
3048     Mlt::Producer trackProducer(tractor.track(info.track));
3049     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
3050     if (trackPlaylist.is_blank_at(info.startPos.frames(m_fps))) {
3051         kDebug() << "////////  ERROR RSIZING BLANK CLIP!!!!!!!!!!!";
3052         return false;
3053     }
3054     mlt_service_lock(service.get_service());
3055     int clipIndex = trackPlaylist.get_clip_index_at(info.startPos.frames(m_fps));
3056     Mlt::Producer *clip = trackPlaylist.get_clip(clipIndex);
3057     if (clip == NULL || clip->is_blank()) {
3058         kDebug() << "////////  ERROR RSIZING NULL CLIP!!!!!!!!!!!";
3059         mlt_service_unlock(service.get_service());
3060         return false;
3061     }
3062     int previousStart = clip->get_in();
3063     int previousOut = clip->get_out();
3064     delete clip;
3065     m_isBlocked = true;
3066     previousStart += moveFrame;
3067     if (previousStart < 0) {
3068         // special case, in point becoming negative (resizing images)
3069         previousOut -= previousStart;
3070         previousStart = 0;
3071     }
3072     // kDebug() << "RESIZE, new start: " << previousStart << ", " << previousOut;
3073     trackPlaylist.resize_clip(clipIndex, previousStart, previousOut);
3074     if (moveFrame > 0) trackPlaylist.insert_blank(clipIndex, moveFrame - 1);
3075     else {
3076         //int midpos = info.startPos.frames(m_fps) + moveFrame - 1;
3077         int blankIndex = clipIndex - 1;
3078         int blankLength = trackPlaylist.clip_length(blankIndex);
3079         // kDebug() << " + resizing blank length " <<  blankLength << ", SIZE DIFF: " << moveFrame;
3080         if (! trackPlaylist.is_blank(blankIndex)) {
3081             kDebug() << "WARNING, CLIP TO RESIZE IS NOT BLANK";
3082         }
3083         if (blankLength + moveFrame == 0) trackPlaylist.remove(blankIndex);
3084         else trackPlaylist.resize_clip(blankIndex, 0, blankLength + moveFrame - 1);
3085     }
3086     trackPlaylist.consolidate_blanks(0);
3087     /*if (QString(clip->parent().get("transparency")).toInt() == 1) {
3088         //mltResizeTransparency(previousStart, (int) moveEnd.frames(m_fps), (int) (moveEnd + out - in).frames(m_fps), track, QString(clip->parent().get("id")).toInt());
3089         mltDeleteTransparency(info.startPos.frames(m_fps), info.track, QString(clip->parent().get("id")).toInt());
3090         ItemInfo transpinfo;
3091         transpinfo.startPos = info.startPos + diff;
3092         transpinfo.endPos = info.startPos + diff + (info.endPos - info.startPos);
3093         transpinfo.track = info.track;
3094         mltAddClipTransparency(transpinfo, info.track - 1, QString(clip->parent().get("id")).toInt());
3095     }*/
3096     m_isBlocked = false;
3097     //m_mltConsumer->set("refresh", 1);
3098     mlt_service_unlock(service.get_service());
3099     m_mltConsumer->set("refresh", 1);
3100     return true;
3101 }
3102
3103 bool Render::mltMoveClip(int startTrack, int endTrack, GenTime moveStart, GenTime moveEnd, Mlt::Producer *prod, bool overwrite, bool insert)
3104 {
3105     return mltMoveClip(startTrack, endTrack, (int) moveStart.frames(m_fps), (int) moveEnd.frames(m_fps), prod, overwrite, insert);
3106 }
3107
3108
3109 bool Render::mltUpdateClipProducer(int track, int pos, Mlt::Producer *prod)
3110 {
3111     if (prod == NULL || !prod->is_valid()) {
3112         kDebug() << "// Warning, CLIP on track " << track << ", at: " << pos << " is invalid, cannot update it!!!";
3113         return false;
3114     }
3115     kDebug() << "NEW PROD ID: " << prod->get("id");
3116     m_isBlocked++;
3117     kDebug() << "// TRYING TO UPDATE CLIP at: " << pos << ", TK: " << track;
3118     Mlt::Service service(m_mltProducer->parent().get_service());
3119     if (service.type() != tractor_type) {
3120         kWarning() << "// TRACTOR PROBLEM";
3121         return false;
3122     }
3123     mlt_service_lock(service.get_service());
3124     Mlt::Tractor tractor(service);
3125     Mlt::Producer trackProducer(tractor.track(track));
3126     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
3127     int clipIndex = trackPlaylist.get_clip_index_at(pos);
3128     Mlt::Producer *clipProducer = trackPlaylist.replace_with_blank(clipIndex);
3129     if (clipProducer == NULL || clipProducer->is_blank()) {
3130         kDebug() << "// ERROR UPDATING CLIP PROD";
3131         delete clipProducer;
3132         mlt_service_unlock(service.get_service());
3133         m_isBlocked--;
3134         return false;
3135     }
3136     Mlt::Producer *clip = prod->cut(clipProducer->get_in(), clipProducer->get_out());
3137     if (!clip || !clip->is_valid()) {
3138         if (clip) delete clip;
3139         delete clipProducer;
3140         mlt_service_unlock(service.get_service());
3141         m_isBlocked--;
3142         return false;
3143     }
3144     // move all effects to the correct producer
3145     mltPasteEffects(clipProducer, clip);
3146     trackPlaylist.insert_at(pos, clip, 1);
3147     delete clip;
3148     delete clipProducer;
3149     mlt_service_unlock(service.get_service());
3150     m_isBlocked--;
3151     return true;
3152 }
3153
3154 bool Render::mltMoveClip(int startTrack, int endTrack, int moveStart, int moveEnd, Mlt::Producer *prod, bool overwrite, bool /*insert*/)
3155 {
3156     m_isBlocked++;
3157
3158     Mlt::Service service(m_mltProducer->parent().get_service());
3159     if (service.type() != tractor_type) {
3160         kWarning() << "// TRACTOR PROBLEM";
3161         return false;
3162     }
3163
3164     Mlt::Tractor tractor(service);
3165     mlt_service_lock(service.get_service());
3166     Mlt::Producer trackProducer(tractor.track(startTrack));
3167     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
3168     int clipIndex = trackPlaylist.get_clip_index_at(moveStart);
3169     kDebug() << "//////  LOOKING FOR CLIP TO MOVE, INDEX: " << clipIndex;
3170     bool checkLength = false;
3171     if (endTrack == startTrack) {
3172         Mlt::Producer *clipProducer = trackPlaylist.replace_with_blank(clipIndex);
3173         if ((!overwrite && !trackPlaylist.is_blank_at(moveEnd)) || !clipProducer || !clipProducer->is_valid() || clipProducer->is_blank()) {
3174             // error, destination is not empty
3175             if (clipProducer) {
3176                 if (!trackPlaylist.is_blank_at(moveEnd) && clipProducer->is_valid()) trackPlaylist.insert_at(moveStart, clipProducer, 1);
3177                 delete clipProducer;
3178             }
3179             //int ix = trackPlaylist.get_clip_index_at(moveEnd);
3180             kDebug() << "// ERROR MOVING CLIP TO : " << moveEnd;
3181             mlt_service_unlock(service.get_service());
3182             m_isBlocked--;
3183             return false;
3184         } else {
3185             trackPlaylist.consolidate_blanks(0);
3186             if (overwrite) {
3187                 trackPlaylist.remove_region(moveEnd, clipProducer->get_playtime());
3188                 int clipIndex = trackPlaylist.get_clip_index_at(moveEnd);
3189                 trackPlaylist.insert_blank(clipIndex, clipProducer->get_playtime() - 1);
3190             }
3191             int newIndex = trackPlaylist.insert_at(moveEnd, clipProducer, 1);
3192             trackPlaylist.consolidate_blanks(1);
3193             delete clipProducer;
3194             /*if (QString(clipProducer.parent().get("transparency")).toInt() == 1) {
3195             mltMoveTransparency(moveStart, moveEnd, startTrack, endTrack, QString(clipProducer.parent().get("id")).toInt());
3196             }*/
3197             if (newIndex + 1 == trackPlaylist.count()) checkLength = true;
3198         }
3199         //mlt_service_unlock(service.get_service());
3200     } else {
3201         Mlt::Producer destTrackProducer(tractor.track(endTrack));
3202         Mlt::Playlist destTrackPlaylist((mlt_playlist) destTrackProducer.get_service());
3203         if (!overwrite && !destTrackPlaylist.is_blank_at(moveEnd)) {
3204             // error, destination is not empty
3205             mlt_service_unlock(service.get_service());
3206             m_isBlocked--;
3207             return false;
3208         } else {
3209             Mlt::Producer *clipProducer = trackPlaylist.replace_with_blank(clipIndex);
3210             if (!clipProducer || clipProducer->is_blank()) {
3211                 // error, destination is not empty
3212                 //int ix = trackPlaylist.get_clip_index_at(moveEnd);
3213                 if (clipProducer) delete clipProducer;
3214                 kDebug() << "// ERROR MOVING CLIP TO : " << moveEnd;
3215                 mlt_service_unlock(service.get_service());
3216                 m_isBlocked--;
3217                 return false;
3218             }
3219             trackPlaylist.consolidate_blanks(0);
3220             destTrackPlaylist.consolidate_blanks(1);
3221             Mlt::Producer *clip;
3222             // check if we are moving a slowmotion producer
3223             QString serv = clipProducer->parent().get("mlt_service");
3224             QString currentid = clipProducer->parent().get("id");
3225             if (serv == "framebuffer" || currentid.endsWith("_video")) {
3226                 clip = clipProducer;
3227             } else {
3228                 if (prod == NULL) {
3229                     // Special case: prod is null when using placeholder clips.
3230                     // in that case, use the producer existing in playlist. Note that
3231                     // it will bypass the one producer per track logic and might cause
3232                     // Sound cracks if clip is moved so that it overlaps another copy of itself
3233                     clip = clipProducer->cut(clipProducer->get_in(), clipProducer->get_out());
3234                 } else clip = prod->cut(clipProducer->get_in(), clipProducer->get_out());
3235             }
3236
3237             // move all effects to the correct producer
3238             mltPasteEffects(clipProducer, clip);
3239
3240             if (overwrite) {
3241                 destTrackPlaylist.remove_region(moveEnd, clip->get_playtime());
3242                 int clipIndex = destTrackPlaylist.get_clip_index_at(moveEnd);
3243                 destTrackPlaylist.insert_blank(clipIndex, clip->get_playtime() - 1);
3244             }
3245
3246             int newIndex = destTrackPlaylist.insert_at(moveEnd, clip, 1);
3247
3248             if (clip == clipProducer) {
3249                 delete clip;
3250                 clip = NULL;
3251             } else {
3252                 delete clip;
3253                 delete clipProducer;
3254             }
3255             destTrackPlaylist.consolidate_blanks(0);
3256             /*if (QString(clipProducer.parent().get("transparency")).toInt() == 1) {
3257                 kDebug() << "//////// moving clip transparency";
3258                 mltMoveTransparency(moveStart, moveEnd, startTrack, endTrack, QString(clipProducer.parent().get("id")).toInt());
3259             }*/
3260             if (clipIndex > trackPlaylist.count()) checkLength = true;
3261             else if (newIndex + 1 == destTrackPlaylist.count()) checkLength = true;
3262         }
3263     }
3264     mlt_service_unlock(service.get_service());
3265     if (checkLength) mltCheckLength(&tractor);
3266     m_isBlocked--;
3267     //askForRefresh();
3268     //m_mltConsumer->set("refresh", 1);
3269     return true;
3270 }
3271
3272
3273 QList <int> Render::checkTrackSequence(int track)
3274 {
3275     QList <int> list;
3276     Mlt::Service service(m_mltProducer->parent().get_service());
3277     if (service.type() != tractor_type) {
3278         kWarning() << "// TRACTOR PROBLEM";
3279         return list;
3280     }
3281     Mlt::Tractor tractor(service);
3282     mlt_service_lock(service.get_service());
3283     Mlt::Producer trackProducer(tractor.track(track));
3284     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
3285     int clipNb = trackPlaylist.count();
3286     //kDebug() << "// PARSING SCENE TRACK: " << t << ", CLIPS: " << clipNb;
3287     for (int i = 0; i < clipNb; i++) {
3288         Mlt::Producer *c = trackPlaylist.get_clip(i);
3289         int pos = trackPlaylist.clip_start(i);
3290         if (!list.contains(pos)) list.append(pos);
3291         pos += c->get_playtime();
3292         if (!list.contains(pos)) list.append(pos);
3293         delete c;
3294     }
3295     return list;
3296 }
3297
3298 bool Render::mltMoveTransition(QString type, int startTrack, int newTrack, int newTransitionTrack, GenTime oldIn, GenTime oldOut, GenTime newIn, GenTime newOut)
3299 {
3300     int new_in = (int)newIn.frames(m_fps);
3301     int new_out = (int)newOut.frames(m_fps) - 1;
3302     if (new_in >= new_out) return false;
3303     int old_in = (int)oldIn.frames(m_fps);
3304     int old_out = (int)oldOut.frames(m_fps) - 1;
3305
3306     Mlt::Service service(m_mltProducer->parent().get_service());
3307     Mlt::Tractor tractor(service);
3308     Mlt::Field *field = tractor.field();
3309
3310     bool doRefresh = true;
3311     // Check if clip is visible in monitor
3312     int diff = old_out - m_mltProducer->position();
3313     if (diff < 0 || diff > old_out - old_in) doRefresh = false;
3314     if (doRefresh) {
3315         diff = new_out - m_mltProducer->position();
3316         if (diff < 0 || diff > new_out - new_in) doRefresh = false;
3317     }
3318
3319     m_isBlocked++;
3320     mlt_service_lock(service.get_service());
3321
3322     mlt_service nextservice = mlt_service_get_producer(service.get_service());
3323     mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
3324     QString mlt_type = mlt_properties_get(properties, "mlt_type");
3325     QString resource = mlt_properties_get(properties, "mlt_service");
3326     int old_pos = (int)(old_in + old_out) / 2;
3327     bool found = false;
3328
3329     while (mlt_type == "transition") {
3330         Mlt::Transition transition((mlt_transition) nextservice);
3331         int currentTrack = transition.get_b_track();
3332         int currentIn = (int) transition.get_in();
3333         int currentOut = (int) transition.get_out();
3334
3335         if (resource == type && startTrack == currentTrack && currentIn <= old_pos && currentOut >= old_pos) {
3336             found = true;
3337             if (newTrack - startTrack != 0) {
3338                 Mlt::Properties trans_props(transition.get_properties());
3339                 char *tmp = decodedString(transition.get("mlt_service"));
3340                 Mlt::Transition new_transition(*m_mltProfile, tmp);
3341                 delete[] tmp;
3342                 Mlt::Properties new_trans_props(new_transition.get_properties());
3343                 new_trans_props.inherit(trans_props);
3344                 new_transition.set_in_and_out(new_in, new_out);
3345                 field->disconnect_service(transition);
3346                 mltPlantTransition(field, new_transition, newTransitionTrack, newTrack);
3347                 //field->plant_transition(new_transition, newTransitionTrack, newTrack);
3348             } else transition.set_in_and_out(new_in, new_out);
3349             break;
3350         }
3351         nextservice = mlt_service_producer(nextservice);
3352         if (nextservice == NULL) break;
3353         properties = MLT_SERVICE_PROPERTIES(nextservice);
3354         mlt_type = mlt_properties_get(properties, "mlt_type");
3355         resource = mlt_properties_get(properties, "mlt_service");
3356     }
3357     mlt_service_unlock(service.get_service());
3358     m_isBlocked--;
3359     if (doRefresh) refresh();
3360     //if (m_isBlocked == 0) m_mltConsumer->set("refresh", 1);
3361     return found;
3362 }
3363
3364
3365 void Render::mltPlantTransition(Mlt::Field *field, Mlt::Transition &tr, int a_track, int b_track)
3366 {
3367     mlt_service nextservice = mlt_service_get_producer(field->get_service());
3368     mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
3369     QString mlt_type = mlt_properties_get(properties, "mlt_type");
3370     QString resource = mlt_properties_get(properties, "mlt_service");
3371     QList <Mlt::Transition *> trList;
3372
3373     while (mlt_type == "transition") {
3374         Mlt::Transition transition((mlt_transition) nextservice);
3375         int aTrack = transition.get_a_track();
3376         int bTrack = transition.get_b_track();
3377         if (resource != "mix" && (aTrack < a_track || (aTrack == a_track && bTrack > b_track))) {
3378             Mlt::Properties trans_props(transition.get_properties());
3379             char *tmp = decodedString(transition.get("mlt_service"));
3380             Mlt::Transition *cp = new Mlt::Transition(*m_mltProfile, tmp);
3381             delete[] tmp;
3382             Mlt::Properties new_trans_props(cp->get_properties());
3383             new_trans_props.inherit(trans_props);
3384             trList.append(cp);
3385             field->disconnect_service(transition);
3386         }
3387         //else kDebug() << "// FOUND TRANS OK, "<<resource<< ", A_: " << aTrack << ", B_ "<<bTrack;
3388
3389         nextservice = mlt_service_producer(nextservice);
3390         if (nextservice == NULL) break;
3391         properties = MLT_SERVICE_PROPERTIES(nextservice);
3392         mlt_type = mlt_properties_get(properties, "mlt_type");
3393         resource = mlt_properties_get(properties, "mlt_service");
3394     }
3395
3396     field->plant_transition(tr, a_track, b_track);
3397
3398     // re-add upper transitions
3399     for (int i = 0; i < trList.count(); i++) {
3400         // kDebug()<< "REPLANT ON TK: "<<trList.at(i)->get_a_track()<<", "<<trList.at(i)->get_b_track();
3401         field->plant_transition(*trList.at(i), trList.at(i)->get_a_track(), trList.at(i)->get_b_track());
3402     }
3403 }
3404
3405 void Render::mltUpdateTransition(QString oldTag, QString tag, int a_track, int b_track, GenTime in, GenTime out, QDomElement xml, bool force)
3406 {
3407     if (oldTag == tag && !force) mltUpdateTransitionParams(tag, a_track, b_track, in, out, xml);
3408     else {
3409         mltDeleteTransition(oldTag, a_track, b_track, in, out, xml, false);
3410         mltAddTransition(tag, a_track, b_track, in, out, xml, false);
3411     }
3412
3413     if (m_mltProducer->position() > in.frames(m_fps) && m_mltProducer->position() < out.frames(m_fps)) refresh();
3414 }
3415
3416 void Render::mltUpdateTransitionParams(QString type, int a_track, int b_track, GenTime in, GenTime out, QDomElement xml)
3417 {
3418     mlt_service serv = m_mltProducer->parent().get_service();
3419     mlt_service_lock(serv);
3420     m_isBlocked++;
3421
3422     mlt_service nextservice = mlt_service_get_producer(serv);
3423     mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
3424     QString mlt_type = mlt_properties_get(properties, "mlt_type");
3425     QString resource = mlt_properties_get(properties, "mlt_service");
3426     int in_pos = (int) in.frames(m_fps);
3427     int out_pos = (int) out.frames(m_fps) - 1;
3428
3429     while (mlt_type == "transition") {
3430         mlt_transition tr = (mlt_transition) nextservice;
3431         int currentTrack = mlt_transition_get_b_track(tr);
3432         int currentBTrack = mlt_transition_get_a_track(tr);
3433         int currentIn = (int) mlt_transition_get_in(tr);
3434         int currentOut = (int) mlt_transition_get_out(tr);
3435
3436         // kDebug()<<"Looking for transition : " << currentIn <<'x'<<currentOut<< ", OLD oNE: "<<in_pos<<'x'<<out_pos;
3437
3438         if (resource == type && b_track == currentTrack && currentIn == in_pos && currentOut == out_pos) {
3439             QMap<QString, QString> map = mltGetTransitionParamsFromXml(xml);
3440             QMap<QString, QString>::Iterator it;
3441             QString key;
3442             mlt_properties transproperties = MLT_TRANSITION_PROPERTIES(tr);
3443
3444             QString currentId = mlt_properties_get(transproperties, "kdenlive_id");
3445             if (currentId != xml.attribute("id")) {
3446                 // The transition ID is not the same, so reset all properties
3447                 char *tmp = decodedString(xml.attribute("id"));
3448                 mlt_properties_set(transproperties, "kdenlive_id", tmp);
3449                 delete[] tmp;
3450                 // Cleanup previous properties
3451                 QStringList permanentProps;
3452                 permanentProps << "factory" << "kdenlive_id" << "mlt_service" << "mlt_type" << "in";
3453                 permanentProps << "out" << "a_track" << "b_track";
3454                 for (int i = 0; i < mlt_properties_count(transproperties); i++) {
3455                     QString propName = mlt_properties_get_name(transproperties, i);
3456                     if (!propName.startsWith('_') && ! permanentProps.contains(propName)) {
3457                         tmp = decodedString(propName);
3458                         mlt_properties_set(transproperties, tmp, "");
3459                         delete[] tmp;
3460                     }
3461                 }
3462             }
3463
3464             mlt_properties_set_int(transproperties, "force_track", xml.attribute("force_track").toInt());
3465             mlt_properties_set_int(transproperties, "automatic", xml.attribute("automatic", "0").toInt());
3466
3467             if (currentBTrack != a_track) {
3468                 mlt_properties_set_int(transproperties, "a_track", a_track);
3469             }
3470             for (it = map.begin(); it != map.end(); ++it) {
3471                 key = it.key();
3472                 char *name = decodedString(key);
3473                 char *value = decodedString(it.value());
3474                 mlt_properties_set(transproperties, name, value);
3475                 //kDebug() << " ------  UPDATING TRANS PARAM: " << name << ": " << value;
3476                 //filter->set("kdenlive_id", id);
3477                 delete[] name;
3478                 delete[] value;
3479             }
3480             break;
3481         }
3482         nextservice = mlt_service_producer(nextservice);
3483         if (nextservice == NULL) break;
3484         properties = MLT_SERVICE_PROPERTIES(nextservice);
3485         mlt_type = mlt_properties_get(properties, "mlt_type");
3486         resource = mlt_properties_get(properties, "mlt_service");
3487     }
3488     mlt_service_unlock(serv);
3489     m_isBlocked--;
3490     //askForRefresh();
3491     //if (m_isBlocked == 0) m_mltConsumer->set("refresh", 1);
3492 }
3493
3494 void Render::mltDeleteTransition(QString tag, int /*a_track*/, int b_track, GenTime in, GenTime out, QDomElement /*xml*/, bool /*do_refresh*/)
3495 {
3496     mlt_service serv = m_mltProducer->parent().get_service();
3497     m_isBlocked++;
3498     mlt_service_lock(serv);
3499
3500     Mlt::Service service(serv);
3501     Mlt::Tractor tractor(service);
3502     Mlt::Field *field = tractor.field();
3503
3504     //if (do_refresh) m_mltConsumer->set("refresh", 0);
3505
3506     mlt_service nextservice = mlt_service_get_producer(serv);
3507     mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
3508     QString mlt_type = mlt_properties_get(properties, "mlt_type");
3509     QString resource = mlt_properties_get(properties, "mlt_service");
3510
3511     const int old_pos = (int)((in + out).frames(m_fps) / 2);
3512     kDebug() << " del trans pos: " << in.frames(25) << "-" << out.frames(25);
3513
3514     while (mlt_type == "transition") {
3515         mlt_transition tr = (mlt_transition) nextservice;
3516         int currentTrack = mlt_transition_get_b_track(tr);
3517         int currentIn = (int) mlt_transition_get_in(tr);
3518         int currentOut = (int) mlt_transition_get_out(tr);
3519         //kDebug() << "// FOUND EXISTING TRANS, IN: " << currentIn << ", OUT: " << currentOut << ", TRACK: " << currentTrack;
3520
3521         if (resource == tag && b_track == currentTrack && currentIn <= old_pos && currentOut >= old_pos) {
3522             mlt_field_disconnect_service(field->get_field(), nextservice);
3523             break;
3524         }
3525         nextservice = mlt_service_producer(nextservice);
3526         if (nextservice == NULL) break;
3527         properties = MLT_SERVICE_PROPERTIES(nextservice);
3528         mlt_type = mlt_properties_get(properties, "mlt_type");
3529         resource = mlt_properties_get(properties, "mlt_service");
3530     }
3531     mlt_service_unlock(serv);
3532     m_isBlocked--;
3533     //askForRefresh();
3534     //if (m_isBlocked == 0) m_mltConsumer->set("refresh", 1);
3535 }
3536
3537 QMap<QString, QString> Render::mltGetTransitionParamsFromXml(QDomElement xml)
3538 {
3539     QDomNodeList attribs = xml.elementsByTagName("parameter");
3540     QMap<QString, QString> map;
3541     for (int i = 0; i < attribs.count(); i++) {
3542         QDomElement e = attribs.item(i).toElement();
3543         QString name = e.attribute("name");
3544         //kDebug()<<"-- TRANSITION PARAM: "<<name<<" = "<< e.attribute("name")<<" / " << e.attribute("value");
3545         map[name] = e.attribute("default");
3546         if (!e.attribute("value").isEmpty()) {
3547             map[name] = e.attribute("value");
3548         }
3549         if (!e.attribute("factor").isEmpty() && e.attribute("factor").toDouble() > 0) {
3550             map[name] = QString::number(map[name].toDouble() / e.attribute("factor").toDouble());
3551             //map[name]=map[name].replace(".",","); //FIXME how to solve locale conversion of . ,
3552         }
3553
3554         if (e.attribute("namedesc").contains(';')) {
3555             QString format = e.attribute("format");
3556             QStringList separators = format.split("%d", QString::SkipEmptyParts);
3557             QStringList values = e.attribute("value").split(QRegExp("[,:;x]"));
3558             QString neu;
3559             QTextStream txtNeu(&neu);
3560             if (values.size() > 0)
3561                 txtNeu << (int)values[0].toDouble();
3562             int i = 0;
3563             for (i = 0; i < separators.size() && i + 1 < values.size(); i++) {
3564                 txtNeu << separators[i];
3565                 txtNeu << (int)(values[i+1].toDouble());
3566             }
3567             if (i < separators.size())
3568                 txtNeu << separators[i];
3569             map[e.attribute("name")] = neu;
3570         }
3571
3572     }
3573     return map;
3574 }
3575
3576 void Render::mltAddClipTransparency(ItemInfo info, int transitiontrack, int id)
3577 {
3578     kDebug() << "/////////  ADDING CLIP TRANSPARENCY AT: " << info.startPos.frames(25);
3579     Mlt::Service service(m_mltProducer->parent().get_service());
3580     Mlt::Tractor tractor(service);
3581     Mlt::Field *field = tractor.field();
3582
3583     Mlt::Transition *transition = new Mlt::Transition(*m_mltProfile, "composite");
3584     transition->set_in_and_out((int) info.startPos.frames(m_fps), (int) info.endPos.frames(m_fps) - 1);
3585     transition->set("transparency", id);
3586     transition->set("fill", 1);
3587     transition->set("internal_added", 237);
3588     field->plant_transition(*transition, transitiontrack, info.track);
3589     refresh();
3590 }
3591
3592 void Render::mltDeleteTransparency(int pos, int track, int id)
3593 {
3594     Mlt::Service service(m_mltProducer->parent().get_service());
3595     Mlt::Tractor tractor(service);
3596     Mlt::Field *field = tractor.field();
3597
3598     //if (do_refresh) m_mltConsumer->set("refresh", 0);
3599     mlt_service serv = m_mltProducer->parent().get_service();
3600
3601     mlt_service nextservice = mlt_service_get_producer(serv);
3602     mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
3603     QString mlt_type = mlt_properties_get(properties, "mlt_type");
3604     QString resource = mlt_properties_get(properties, "mlt_service");
3605
3606     while (mlt_type == "transition") {
3607         mlt_transition tr = (mlt_transition) nextservice;
3608         int currentTrack = mlt_transition_get_b_track(tr);
3609         int currentIn = (int) mlt_transition_get_in(tr);
3610         int currentOut = (int) mlt_transition_get_out(tr);
3611         int transitionId = QString(mlt_properties_get(properties, "transparency")).toInt();
3612         kDebug() << "// FOUND EXISTING TRANS, IN: " << currentIn << ", OUT: " << currentOut << ", TRACK: " << currentTrack;
3613
3614         if (resource == "composite" && track == currentTrack && currentIn == pos && transitionId == id) {
3615             //kDebug() << " / / / / /DELETE TRANS DOOOMNE";
3616             mlt_field_disconnect_service(field->get_field(), nextservice);
3617             break;
3618         }
3619         nextservice = mlt_service_producer(nextservice);
3620         if (nextservice == NULL) break;
3621         properties = MLT_SERVICE_PROPERTIES(nextservice);
3622         mlt_type = mlt_properties_get(properties, "mlt_type");
3623         resource = mlt_properties_get(properties, "mlt_service");
3624     }
3625     //if (do_refresh) m_mltConsumer->set("refresh", 1);
3626 }
3627
3628 void Render::mltResizeTransparency(int oldStart, int newStart, int newEnd, int track, int id)
3629 {
3630     Mlt::Service service(m_mltProducer->parent().get_service());
3631     Mlt::Tractor tractor(service);
3632
3633     mlt_service_lock(service.get_service());
3634     m_mltConsumer->set("refresh", 0);
3635     m_isBlocked++;
3636
3637     mlt_service serv = m_mltProducer->parent().get_service();
3638     mlt_service nextservice = mlt_service_get_producer(serv);
3639     mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
3640     QString mlt_type = mlt_properties_get(properties, "mlt_type");
3641     QString resource = mlt_properties_get(properties, "mlt_service");
3642     kDebug() << "// resize transpar from: " << oldStart << ", TO: " << newStart << 'x' << newEnd << ", " << track << ", " << id;
3643     while (mlt_type == "transition") {
3644         mlt_transition tr = (mlt_transition) nextservice;
3645         int currentTrack = mlt_transition_get_b_track(tr);
3646         int currentIn = (int) mlt_transition_get_in(tr);
3647         //mlt_properties props = MLT_TRANSITION_PROPERTIES(tr);
3648         int transitionId = QString(mlt_properties_get(properties, "transparency")).toInt();
3649         kDebug() << "// resize transpar current in: " << currentIn << ", Track: " << currentTrack << ", id: " << id << 'x' << transitionId ;
3650         if (resource == "composite" && track == currentTrack && currentIn == oldStart && transitionId == id) {
3651             kDebug() << " / / / / /RESIZE TRANS TO: " << newStart << 'x' << newEnd;
3652             mlt_transition_set_in_and_out(tr, newStart, newEnd);
3653             break;
3654         }
3655         nextservice = mlt_service_producer(nextservice);
3656         if (nextservice == NULL) break;
3657         properties = MLT_SERVICE_PROPERTIES(nextservice);
3658         mlt_type = mlt_properties_get(properties, "mlt_type");
3659         resource = mlt_properties_get(properties, "mlt_service");
3660     }
3661     mlt_service_unlock(service.get_service());
3662     m_isBlocked--;
3663     if (m_isBlocked == 0) m_mltConsumer->set("refresh", 1);
3664
3665 }
3666
3667 void Render::mltMoveTransparency(int startTime, int endTime, int startTrack, int endTrack, int id)
3668 {
3669     Mlt::Service service(m_mltProducer->parent().get_service());
3670     Mlt::Tractor tractor(service);
3671
3672     mlt_service_lock(service.get_service());
3673     m_mltConsumer->set("refresh", 0);
3674     m_isBlocked++;
3675
3676     mlt_service serv = m_mltProducer->parent().get_service();
3677     mlt_service nextservice = mlt_service_get_producer(serv);
3678     mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
3679     QString mlt_type = mlt_properties_get(properties, "mlt_type");
3680     QString resource = mlt_properties_get(properties, "mlt_service");
3681
3682     while (mlt_type == "transition") {
3683         mlt_transition tr = (mlt_transition) nextservice;
3684         int currentTrack = mlt_transition_get_b_track(tr);
3685         int currentaTrack = mlt_transition_get_a_track(tr);
3686         int currentIn = (int) mlt_transition_get_in(tr);
3687         int currentOut = (int) mlt_transition_get_out(tr);
3688         //mlt_properties properties = MLT_TRANSITION_PROPERTIES(tr);
3689         int transitionId = QString(mlt_properties_get(properties, "transparency")).toInt();
3690         //kDebug()<<" + TRANSITION "<<id<<" == "<<transitionId<<", START TMIE: "<<currentIn<<", LOOK FR: "<<startTime<<", TRACK: "<<currentTrack<<'x'<<startTrack;
3691         if (resource == "composite" && transitionId == id && startTime == currentIn && startTrack == currentTrack) {
3692             kDebug() << "//////MOVING";
3693             mlt_transition_set_in_and_out(tr, endTime, endTime + currentOut - currentIn);
3694             if (endTrack != startTrack) {
3695                 mlt_properties properties = MLT_TRANSITION_PROPERTIES(tr);
3696                 mlt_properties_set_int(properties, "a_track", currentaTrack + endTrack - currentTrack);
3697                 mlt_properties_set_int(properties, "b_track", endTrack);
3698             }
3699             break;
3700         }
3701         nextservice = mlt_service_producer(nextservice);
3702         if (nextservice == NULL) break;
3703         properties = MLT_SERVICE_PROPERTIES(nextservice);
3704         mlt_type = mlt_properties_get(properties, "mlt_type");
3705         resource = mlt_properties_get(properties, "mlt_service");
3706     }
3707     m_isBlocked--;
3708     mlt_service_unlock(service.get_service());
3709     m_mltConsumer->set("refresh", 1);
3710 }
3711
3712
3713 bool Render::mltAddTransition(QString tag, int a_track, int b_track, GenTime in, GenTime out, QDomElement xml, bool do_refresh)
3714 {
3715     if (in >= out) return false;
3716     QMap<QString, QString> args = mltGetTransitionParamsFromXml(xml);
3717     Mlt::Service service(m_mltProducer->parent().get_service());
3718
3719     Mlt::Tractor tractor(service);
3720     Mlt::Field *field = tractor.field();
3721
3722     char *transId = decodedString(tag);
3723     Mlt::Transition *transition = new Mlt::Transition(*m_mltProfile, transId);
3724     if (out != GenTime())
3725         transition->set_in_and_out((int) in.frames(m_fps), (int) out.frames(m_fps) - 1);
3726
3727     if (do_refresh && (m_mltProducer->position() < in.frames(m_fps) || m_mltProducer->position() > out.frames(m_fps))) do_refresh = false;
3728     QMap<QString, QString>::Iterator it;
3729     QString key;
3730     if (xml.attribute("automatic") == "1") transition->set("automatic", 1);
3731     //kDebug() << " ------  ADDING TRANSITION PARAMs: " << args.count();
3732     if (xml.hasAttribute("id"))
3733         transition->set("kdenlive_id", xml.attribute("id").toUtf8().constData());
3734
3735     for (it = args.begin(); it != args.end(); ++it) {
3736         key = it.key();
3737         char *name = decodedString(key);
3738         char *value = decodedString(it.value());
3739         if (it.value().isEmpty() == false) transition->set(name, value);
3740         //kDebug() << " ------  ADDING TRANS PARAM: " << name << ": " << value;
3741         delete[] name;
3742         delete[] value;
3743     }
3744     // attach transition
3745     mltPlantTransition(field, *transition, a_track, b_track);
3746     // field->plant_transition(*transition, a_track, b_track);
3747     delete[] transId;
3748     if (do_refresh) refresh();
3749     return true;
3750 }
3751
3752 void Render::mltSavePlaylist()
3753 {
3754     kWarning() << "// UPDATING PLAYLIST TO DISK++++++++++++++++";
3755     Mlt::Consumer fileConsumer(*m_mltProfile, "xml");
3756     fileConsumer.set("resource", "/tmp/playlist.mlt");
3757
3758     Mlt::Service service(m_mltProducer->get_service());
3759
3760     fileConsumer.connect(service);
3761     fileConsumer.start();
3762 }
3763
3764 const QList <Mlt::Producer *> Render::producersList()
3765 {
3766     QList <Mlt::Producer *> prods;
3767     if (m_mltProducer == NULL) return prods;
3768     Mlt::Service service(m_mltProducer->parent().get_service());
3769     if (service.type() != tractor_type) return prods;
3770     Mlt::Tractor tractor(service);
3771     QStringList ids;
3772
3773     int trackNb = tractor.count();
3774     for (int t = 1; t < trackNb; t++) {
3775         Mlt::Producer *tt = tractor.track(t);
3776         Mlt::Producer trackProducer(tt);
3777         delete tt;
3778         Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
3779         int clipNb = trackPlaylist.count();
3780         //kDebug() << "// PARSING SCENE TRACK: " << t << ", CLIPS: " << clipNb;
3781         for (int i = 0; i < clipNb; i++) {
3782             Mlt::Producer *c = trackPlaylist.get_clip(i);
3783             Mlt::Producer *nprod = new Mlt::Producer(c->get_parent());
3784             if (nprod) {
3785                 if (!nprod->is_blank() && !ids.contains(nprod->get("id"))) {
3786                     ids.append(nprod->get("id"));
3787                     prods.append(nprod);
3788                 } else delete nprod;
3789             }
3790             delete c;
3791         }
3792     }
3793     return prods;
3794 }
3795
3796 void Render::fillSlowMotionProducers()
3797 {
3798     if (m_mltProducer == NULL) return;
3799     Mlt::Service service(m_mltProducer->parent().get_service());
3800     if (service.type() != tractor_type) return;
3801
3802     Mlt::Tractor tractor(service);
3803
3804     int trackNb = tractor.count();
3805     for (int t = 1; t < trackNb; t++) {
3806         Mlt::Producer *tt = tractor.track(t);
3807         Mlt::Producer trackProducer(tt);
3808         delete tt;
3809         Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
3810         int clipNb = trackPlaylist.count();
3811         for (int i = 0; i < clipNb; i++) {
3812             Mlt::Producer *c = trackPlaylist.get_clip(i);
3813             Mlt::Producer *nprod = new Mlt::Producer(c->get_parent());
3814             if (nprod) {
3815                 QString id = nprod->get("id");
3816                 if (id.startsWith("slowmotion:") && !nprod->is_blank()) {
3817                     // this is a slowmotion producer, add it to the list
3818                     QString url = QString::fromUtf8(nprod->get("resource"));
3819                     int strobe = nprod->get_int("strobe");
3820                     if (strobe > 1) url.append("&strobe=" + QString::number(strobe));
3821                     if (!m_slowmotionProducers.contains(url)) {
3822                         m_slowmotionProducers.insert(url, nprod);
3823                     }
3824                 } else delete nprod;
3825             }
3826             delete c;
3827         }
3828     }
3829 }
3830
3831 void Render::mltInsertTrack(int ix, bool videoTrack)
3832 {
3833     blockSignals(true);
3834     m_isBlocked++;
3835
3836     Mlt::Service service(m_mltProducer->parent().get_service());
3837     mlt_service_lock(service.get_service());
3838     if (service.type() != tractor_type) {
3839         kWarning() << "// TRACTOR PROBLEM";
3840         return;
3841     }
3842
3843     Mlt::Tractor tractor(service);
3844
3845     Mlt::Playlist playlist;
3846     int ct = tractor.count();
3847     if (ix > ct) {
3848         kDebug() << "// ERROR, TRYING TO insert TRACK " << ix << ", max: " << ct;
3849         ix = ct;
3850     }
3851
3852     int pos = ix;
3853     if (pos < ct) {
3854         Mlt::Producer *prodToMove = new Mlt::Producer(tractor.track(pos));
3855         tractor.set_track(playlist, pos);
3856         Mlt::Producer newProd(tractor.track(pos));
3857         if (!videoTrack) newProd.set("hide", 1);
3858         pos++;
3859         for (; pos <= ct; pos++) {
3860             Mlt::Producer *prodToMove2 = new Mlt::Producer(tractor.track(pos));
3861             tractor.set_track(*prodToMove, pos);
3862             prodToMove = prodToMove2;
3863         }
3864     } else {
3865         tractor.set_track(playlist, ix);
3866         Mlt::Producer newProd(tractor.track(ix));
3867         if (!videoTrack) newProd.set("hide", 1);
3868     }
3869
3870     // Move transitions
3871     mlt_service serv = m_mltProducer->parent().get_service();
3872     mlt_service nextservice = mlt_service_get_producer(serv);
3873     mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
3874     QString mlt_type = mlt_properties_get(properties, "mlt_type");
3875     QString resource = mlt_properties_get(properties, "mlt_service");
3876
3877     while (mlt_type == "transition") {
3878         if (resource != "mix") {
3879             mlt_transition tr = (mlt_transition) nextservice;
3880             int currentTrack = mlt_transition_get_b_track(tr);
3881             int currentaTrack = mlt_transition_get_a_track(tr);
3882             mlt_properties properties = MLT_TRANSITION_PROPERTIES(tr);
3883
3884             if (currentTrack >= ix) {
3885                 mlt_properties_set_int(properties, "b_track", currentTrack + 1);
3886                 mlt_properties_set_int(properties, "a_track", currentaTrack + 1);
3887             }
3888         }
3889         nextservice = mlt_service_producer(nextservice);
3890         if (nextservice == NULL) break;
3891         properties = MLT_SERVICE_PROPERTIES(nextservice);
3892         mlt_type = mlt_properties_get(properties, "mlt_type");
3893         resource = mlt_properties_get(properties, "mlt_service");
3894     }
3895
3896     // Add audio mix transition to last track
3897     Mlt::Field *field = tractor.field();
3898     Mlt::Transition *transition = new Mlt::Transition(*m_mltProfile, "mix");
3899     transition->set("a_track", 1);
3900     transition->set("b_track", ct);
3901     transition->set("always_active", 1);
3902     transition->set("internal_added", 237);
3903     transition->set("combine", 1);
3904     field->plant_transition(*transition, 1, ct);
3905     //mlt_service_unlock(m_mltConsumer->get_service());
3906     mlt_service_unlock(service.get_service());
3907     //tractor.multitrack()->refresh();
3908     //tractor.refresh();
3909     m_isBlocked--;
3910     blockSignals(false);
3911 }
3912
3913
3914 void Render::mltDeleteTrack(int ix)
3915 {
3916     QDomDocument doc;
3917     doc.setContent(sceneList(), false);
3918     int tracksCount = doc.elementsByTagName("track").count() - 1;
3919     QDomNode track = doc.elementsByTagName("track").at(ix);
3920     QDomNode tractor = doc.elementsByTagName("tractor").at(0);
3921     QDomNodeList transitions = doc.elementsByTagName("transition");
3922     for (int i = 0; i < transitions.count(); i++) {
3923         QDomElement e = transitions.at(i).toElement();
3924         QDomNodeList props = e.elementsByTagName("property");
3925         QMap <QString, QString> mappedProps;
3926         for (int j = 0; j < props.count(); j++) {
3927             QDomElement f = props.at(j).toElement();
3928             mappedProps.insert(f.attribute("name"), f.firstChild().nodeValue());
3929         }
3930         if (mappedProps.value("mlt_service") == "mix" && mappedProps.value("b_track").toInt() == tracksCount) {
3931             tractor.removeChild(transitions.at(i));
3932             i--;
3933         } else if (mappedProps.value("mlt_service") != "mix" && (mappedProps.value("b_track").toInt() >= ix || mappedProps.value("a_track").toInt() >= ix)) {
3934             // Transition needs to be moved
3935             int a_track = mappedProps.value("a_track").toInt();
3936             int b_track = mappedProps.value("b_track").toInt();
3937             if (a_track > 0 && a_track >= ix) a_track --;
3938             if (b_track == ix) {
3939                 // transition was on the deleted track, so remove it
3940                 tractor.removeChild(transitions.at(i));
3941                 i--;
3942                 continue;
3943             }
3944             if (b_track > 0 && b_track > ix) b_track --;
3945             for (int j = 0; j < props.count(); j++) {
3946                 QDomElement f = props.at(j).toElement();
3947                 if (f.attribute("name") == "a_track") f.firstChild().setNodeValue(QString::number(a_track));
3948                 else if (f.attribute("name") == "b_track") f.firstChild().setNodeValue(QString::number(b_track));
3949             }
3950
3951         }
3952     }
3953     tractor.removeChild(track);
3954     //kDebug() << "/////////// RESULT SCENE: \n" << doc.toString();
3955     setSceneList(doc.toString(), m_framePosition);
3956
3957     /*    if (m_mltProducer != NULL) {
3958             Mlt::Producer parentProd(m_mltProducer->parent());
3959             if (parentProd.get_producer() != NULL) {
3960                 Mlt::Service service(parentProd.get_service());
3961                 if (service.type() == tractor_type) {
3962                     Mlt::Tractor tractor(service);
3963                     mltCheckLength(&tractor);
3964                 }
3965             }
3966         }*/
3967 }
3968
3969
3970 void Render::updatePreviewSettings()
3971 {
3972     kDebug() << "////// RESTARTING CONSUMER";
3973     if (!m_mltConsumer || !m_mltProducer) return;
3974     if (m_mltProducer->get_playtime() == 0) return;
3975     Mlt::Service service(m_mltProducer->parent().get_service());
3976     if (service.type() != tractor_type) return;
3977
3978     //m_mltConsumer->set("refresh", 0);
3979     if (!m_mltConsumer->is_stopped()) m_mltConsumer->stop();
3980     m_mltConsumer->purge();
3981     QString scene = sceneList();
3982     int pos = 0;
3983     if (m_mltProducer) {
3984         pos = m_mltProducer->position();
3985     }
3986
3987     setSceneList(scene, pos);
3988 }
3989
3990
3991 QString Render::updateSceneListFps(double current_fps, double new_fps, QString scene)
3992 {
3993     // Update all frame positions to the new fps value
3994     //WARNING: there are probably some effects or other that hold a frame value
3995     // as parameter and will also need to be updated here!
3996     QDomDocument doc;
3997     doc.setContent(scene);
3998
3999     double factor = new_fps / current_fps;
4000     QDomNodeList producers = doc.elementsByTagName("producer");
4001     for (int i = 0; i < producers.count(); i++) {
4002         QDomElement prod = producers.at(i).toElement();
4003         prod.removeAttribute("in");
4004         prod.removeAttribute("out");
4005
4006         QDomNodeList props = prod.childNodes();
4007         for (int j = 0; j < props.count(); j++) {
4008             QDomElement param =  props.at(j).toElement();
4009             QString paramName = param.attribute("name");
4010             if (paramName.startsWith("meta.") || paramName == "length") {
4011                 prod.removeChild(props.at(j));
4012                 j--;
4013             }
4014         }
4015     }
4016
4017     QDomNodeList entries = doc.elementsByTagName("entry");
4018     for (int i = 0; i < entries.count(); i++) {
4019         QDomElement entry = entries.at(i).toElement();
4020         int in = entry.attribute("in").toInt();
4021         int out = entry.attribute("out").toInt();
4022         in = factor * in + 0.5;
4023         out = factor * out + 0.5;
4024         entry.setAttribute("in", in);
4025         entry.setAttribute("out", out);
4026     }
4027
4028     QDomNodeList blanks = doc.elementsByTagName("blank");
4029     for (int i = 0; i < blanks.count(); i++) {
4030         QDomElement blank = blanks.at(i).toElement();
4031         int length = blank.attribute("length").toInt();
4032         length = factor * length + 0.5;
4033         blank.setAttribute("length", QString::number(length));
4034     }
4035
4036     QDomNodeList filters = doc.elementsByTagName("filter");
4037     for (int i = 0; i < filters.count(); i++) {
4038         QDomElement filter = filters.at(i).toElement();
4039         int in = filter.attribute("in").toInt();
4040         int out = filter.attribute("out").toInt();
4041         in = factor * in + 0.5;
4042         out = factor * out + 0.5;
4043         filter.setAttribute("in", in);
4044         filter.setAttribute("out", out);
4045     }
4046
4047     QDomNodeList transitions = doc.elementsByTagName("transition");
4048     for (int i = 0; i < transitions.count(); i++) {
4049         QDomElement transition = transitions.at(i).toElement();
4050         int in = transition.attribute("in").toInt();
4051         int out = transition.attribute("out").toInt();
4052         in = factor * in + 0.5;
4053         out = factor * out + 0.5;
4054         transition.setAttribute("in", in);
4055         transition.setAttribute("out", out);
4056         QDomNodeList props = transition.childNodes();
4057         for (int j = 0; j < props.count(); j++) {
4058             QDomElement param =  props.at(j).toElement();
4059             QString paramName = param.attribute("name");
4060             if (paramName == "geometry") {
4061                 QString geom = param.firstChild().nodeValue();
4062                 QStringList keys = geom.split(';');
4063                 QStringList newKeys;
4064                 for (int k = 0; k < keys.size(); ++k) {
4065                     if (keys.at(k).contains('=')) {
4066                         int pos = keys.at(k).section('=', 0, 0).toInt();
4067                         pos = factor * pos + 0.5;
4068                         newKeys.append(QString::number(pos) + '=' + keys.at(k).section('=', 1));
4069                     } else newKeys.append(keys.at(k));
4070                 }
4071                 param.firstChild().setNodeValue(newKeys.join(";"));
4072             }
4073         }
4074     }
4075     QDomElement tractor = doc.elementsByTagName("tractor").at(0).toElement();
4076     int out = tractor.attribute("out").toInt();
4077     out = factor * out + 0.5;
4078     tractor.setAttribute("out", out);
4079     emit durationChanged(out);
4080
4081     //kDebug() << "///////////////////////////// " << out << " \n" << doc.toString() << "\n-------------------------";
4082     return doc.toString();
4083 }
4084
4085 #include "renderer.moc"
4086