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