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