]> git.sesse.net Git - kdenlive/blob - src/renderer.cpp
correctly load / save text clips
[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 // ffmpeg Header files
26
27 extern "C" {
28 #include <avformat.h>
29 }
30
31 #include <QTimer>
32 #include <QDir>
33 #include <QApplication>
34 #include <QPainter>
35
36 #include <KDebug>
37 #include <KStandardDirs>
38 #include <KMessageBox>
39 #include <KLocale>
40 #include <KTemporaryFile>
41
42 #include "renderer.h"
43 #include "kdenlivesettings.h"
44 #include "kthumb.h"
45 #include "definitions.h"
46
47 #include <mlt++/Mlt.h>
48
49 #if LIBAVCODEC_VERSION_MAJOR > 51 || (LIBAVCODEC_VERSION_MAJOR > 50 && LIBAVCODEC_VERSION_MINOR > 54)
50 // long_name was added in FFmpeg avcodec version 51.55
51 #define ENABLE_FFMPEG_CODEC_DESCRIPTION 1
52 #endif
53
54 static void consumer_frame_show(mlt_consumer, Render * self, mlt_frame frame_ptr) {
55     // detect if the producer has finished playing. Is there a better way to do it ?
56     if (self->m_isBlocked) return;
57     if (mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame_ptr), "_speed") == 0.0) {
58         self->emitConsumerStopped();
59     } else {
60         self->emitFrameNumber(mlt_frame_get_position(frame_ptr));
61     }
62 }
63
64 Render::Render(const QString & rendererName, int winid, int extid, QWidget *parent): QObject(parent), m_name(rendererName), m_mltConsumer(NULL), m_mltProducer(NULL), m_mltTextProducer(NULL), m_winid(-1), m_framePosition(0), m_generateScenelist(false), m_isBlocked(true) {
65     kDebug() << "//////////  USING PROFILE: " << (char *)KdenliveSettings::current_profile().toUtf8().data();
66     m_mltProfile = new Mlt::Profile((char*) KdenliveSettings::current_profile().data());
67     refreshTimer = new QTimer(this);
68     connect(refreshTimer, SIGNAL(timeout()), this, SLOT(refresh()));
69
70     if (rendererName == "project") m_monitorId = 10000;
71     else m_monitorId = 10001;
72     osdTimer = new QTimer(this);
73     connect(osdTimer, SIGNAL(timeout()), this, SLOT(slotOsdTimeout()));
74
75     m_osdProfile =   KStandardDirs::locate("data", "kdenlive/profiles/metadata.properties");
76     //if (rendererName == "clip")
77     {
78         //Mlt::Consumer *consumer = new Mlt::Consumer( profile , "sdl_preview");
79         m_mltConsumer = new Mlt::Consumer(*m_mltProfile , "sdl_preview"); //consumer;
80         m_mltConsumer->set("resize", 1);
81         m_mltConsumer->set("window_id", winid);
82         m_mltConsumer->set("terminate_on_pause", 1);
83         m_mltConsumer->set("rescale", "nearest");
84         m_mltConsumer->set("progressive", 1);
85         m_mltConsumer->set("audio_buffer", 1024);
86         m_mltConsumer->set("frequency", 48000);
87         m_externalwinid = extid;
88         m_winid = winid;
89         m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_frame_show);
90         Mlt::Producer *producer = new Mlt::Producer(*m_mltProfile , "westley-xml", "<westley><playlist><producer mlt_service=\"colour\" colour=\"blue\" in=\"0\" out=\"25\" /></playlist></westley>");
91         m_mltProducer = producer;
92         m_mltConsumer->connect(*m_mltProducer);
93         m_mltProducer->set_speed(0.0);
94
95         //m_mltConsumer->start();
96         //refresh();
97         //initSceneList();
98     }
99     /*m_osdInfo = new Mlt::Filter("data_show");
100     char *tmp = decodedString(m_osdProfile);
101     m_osdInfo->set("resource", tmp);
102     delete[] tmp;*/
103     //      Does it do anything usefull? I mean, RenderThread doesn't do anything useful at the moment
104     //      (except being cpu hungry :)
105
106     /*      if(!s_renderThread) {
107     s_renderThread = new RenderThread;
108     s_renderThread->start();
109     } */
110 }
111
112 Render::~Render() {
113     closeMlt();
114 }
115
116
117 void Render::closeMlt() {
118     delete osdTimer;
119     delete refreshTimer;
120     if (m_mltConsumer)
121         delete m_mltConsumer;
122     if (m_mltProducer)
123         delete m_mltProducer;
124     while (! m_producersList.isEmpty()) delete m_producersList.takeFirst();
125     //delete m_osdInfo;
126 }
127
128
129
130 int Render::resetProfile(QString profile) {
131
132
133     if (!m_mltConsumer) return 0;
134     if (!m_mltConsumer->is_stopped()) m_mltConsumer->stop();
135     m_mltConsumer->purge();
136     delete m_mltConsumer;
137
138     m_mltConsumer = NULL;
139     QString scene = sceneList();
140     if (m_mltProducer) delete m_mltProducer;
141     m_mltProducer = NULL;
142     if (m_mltProfile) delete m_mltProfile;
143     m_mltProfile = NULL;
144
145     char *tmp = decodedString(profile);
146     m_mltProfile = new Mlt::Profile(tmp);
147     delete[] tmp;
148     m_mltConsumer = new Mlt::Consumer(*m_mltProfile , "sdl_preview"); //consumer;
149     m_mltConsumer->set("resize", 1);
150     m_mltConsumer->set("window_id", m_winid);
151     m_mltConsumer->set("terminate_on_pause", 1);
152     m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_frame_show);
153     m_mltConsumer->set("rescale", "nearest");
154     m_mltConsumer->set("progressive", 1);
155     m_mltConsumer->set("audio_buffer", 1024);
156     m_mltConsumer->set("frequency", 48000);
157
158     kDebug() << "//RESET WITHSCENE: " << scene;
159     setSceneList(scene);
160
161     tmp = decodedString(scene);
162     Mlt::Producer *producer = new Mlt::Producer(*m_mltProfile , "westley-xml", tmp);
163     delete[] tmp;
164     m_mltProducer = producer;
165     m_mltProducer->optimise();
166     m_mltProducer->set_speed(0);
167     connectPlaylist();
168
169     //delete m_mltProfile;
170     // mlt_properties properties = MLT_CONSUMER_PROPERTIES(m_mltConsumer->get_consumer());
171     //mlt_profile prof = m_mltProfile->get_profile();
172     //mlt_properties_set_data(properties, "_profile", prof, 0, (mlt_destructor)mlt_profile_close, NULL);
173     //mlt_properties_set(properties, "profile", "hdv_1080_50i");
174     //m_mltConsumer->set("profile", (char *) profile.toUtf8().data());
175     //m_mltProfile = new Mlt::Profile((char*) profile.toUtf8().data());
176     kDebug() << " ++++++++++ RESET CONSUMER WITH PROFILE: " << profile << ", WIDTH: " << m_mltProfile->width();
177
178     //apply_profile_properties( m_mltProfile, m_mltConsumer->get_consumer(), properties );
179     //refresh();
180     return 1;
181 }
182
183 /** Wraps the VEML command of the same name; Seeks the renderer clip to the given time. */
184 void Render::seek(GenTime time) {
185     sendSeekCommand(time);
186     //emit positionChanged(time);
187 }
188
189 //static
190 char *Render::decodedString(QString str) {
191     /*QCString fn = QFile::encodeName(str);
192     char *t = new char[fn.length() + 1];
193     strcpy(t, (const char *)fn);*/
194
195     return (char *) qstrdup(str.toUtf8().data());   //toLatin1
196 }
197
198 //static
199 /*QPixmap Render::frameThumbnail(Mlt::Frame *frame, int width, int height, bool border) {
200     QPixmap pix(width, height);
201
202     mlt_image_format format = mlt_image_rgb24a;
203     uint8_t *thumb = frame->get_image(format, width, height);
204     QImage image(thumb, width, height, QImage::Format_ARGB32);
205
206     if (!image.isNull()) {
207         pix = pix.fromImage(image);
208         if (border) {
209             QPainter painter(&pix);
210             painter.drawRect(0, 0, width - 1, height - 1);
211         }
212     } else pix.fill(Qt::black);
213     return pix;
214 }
215 */
216 const int Render::renderWidth() const {
217     return (int)(m_mltProfile->height() * m_mltProfile->dar());
218 }
219
220 const int Render::renderHeight() const {
221     return m_mltProfile->height();
222 }
223
224 QPixmap Render::extractFrame(int frame_position, int width, int height) {
225     if (width == -1) {
226         width = renderWidth();
227         height = renderHeight();
228     }
229     QPixmap pix(width, height);
230     if (!m_mltProducer) {
231         pix.fill(Qt::black);
232         return pix;
233     }
234     return KThumb::getFrame(*m_mltProducer, frame_position, width, height);
235 }
236
237 QPixmap Render::getImageThumbnail(KUrl url, int width, int height) {
238     QImage im;
239     QPixmap pixmap;
240     if (url.fileName().startsWith(".all.")) {  //  check for slideshow
241         QString fileType = url.fileName().right(3);
242         QStringList more;
243         QStringList::Iterator it;
244
245         QDir dir(url.directory());
246         more = dir.entryList(QDir::Files);
247
248         for (it = more.begin() ; it != more.end() ; ++it) {
249             if ((*it).endsWith("." + fileType, Qt::CaseInsensitive)) {
250                 im.load(url.directory() + "/" + *it);
251                 break;
252             }
253         }
254     } else im.load(url.path());
255     //pixmap = im.scaled(width, height);
256     return pixmap;
257 }
258 /*
259 //static
260 QPixmap Render::getVideoThumbnail(char *profile, QString file, int frame_position, int width, int height) {
261     QPixmap pix(width, height);
262     char *tmp = decodedString(file);
263     Mlt::Profile *prof = new Mlt::Profile(profile);
264     Mlt::Producer m_producer(*prof, tmp);
265     delete[] tmp;
266     if (m_producer.is_blank()) {
267         pix.fill(Qt::black);
268         return pix;
269     }
270
271     Mlt::Filter m_convert(*prof, "avcolour_space");
272     m_convert.set("forced", mlt_image_rgb24a);
273     m_producer.attach(m_convert);
274     m_producer.seek(frame_position);
275     Mlt::Frame * frame = m_producer.get_frame();
276     if (frame) {
277         pix = frameThumbnail(frame, width, height, true);
278         delete frame;
279     }
280     if (prof) delete prof;
281     return pix;
282 }
283 */
284 /*
285 void Render::getImage(KUrl url, int frame_position, QPoint size)
286 {
287     char *tmp = decodedString(url.path());
288     Mlt::Producer m_producer(tmp);
289     delete[] tmp;
290     if (m_producer.is_blank()) {
291  return;
292     }
293     Mlt::Filter m_convert("avcolour_space");
294     m_convert.set("forced", mlt_image_rgb24a);
295     m_producer.attach(m_convert);
296     m_producer.seek(frame_position);
297
298     Mlt::Frame * frame = m_producer.get_frame();
299
300     if (frame) {
301  QPixmap pix = frameThumbnail(frame, size.x(), size.y(), true);
302  delete frame;
303  emit replyGetImage(url, frame_position, pix, size.x(), size.y());
304     }
305 }*/
306
307 /* Create thumbnail for color */
308 /*void Render::getImage(int id, QString color, QPoint size)
309 {
310     QPixmap pixmap(size.x() - 2, size.y() - 2);
311     color = color.replace(0, 2, "#");
312     color = color.left(7);
313     pixmap.fill(QColor(color));
314     QPixmap result(size.x(), size.y());
315     result.fill(Qt::black);
316     //copyBlt(&result, 1, 1, &pixmap, 0, 0, size.x() - 2, size.y() - 2);
317     emit replyGetImage(id, result, size.x(), size.y());
318
319 }*/
320
321 /* Create thumbnail for image */
322 /*void Render::getImage(KUrl url, QPoint size)
323 {
324     QImage im;
325     QPixmap pixmap;
326     if (url.fileName().startsWith(".all.")) {  //  check for slideshow
327      QString fileType = url.fileName().right(3);
328          QStringList more;
329          QStringList::Iterator it;
330
331             QDir dir( url.directory() );
332             more = dir.entryList( QDir::Files );
333             for ( it = more.begin() ; it != more.end() ; ++it ) {
334                 if ((*it).endsWith("."+fileType, Qt::CaseInsensitive)) {
335    if (!im.load(url.directory() + "/" + *it))
336        kDebug()<<"++ ERROR LOADIN IMAGE: "<<url.directory() + "/" + *it;
337    break;
338   }
339      }
340     }
341     else im.load(url.path());
342
343     //pixmap = im.smoothScale(size.x() - 2, size.y() - 2);
344     QPixmap result(size.x(), size.y());
345     result.fill(Qt::black);
346     //copyBlt(&result, 1, 1, &pixmap, 0, 0, size.x() - 2, size.y() - 2);
347     emit replyGetImage(url, 1, result, size.x(), size.y());
348 }*/
349
350
351 double Render::consumerRatio() const {
352     if (!m_mltConsumer) return 1.0;
353     return (m_mltConsumer->get_double("aspect_ratio_num") / m_mltConsumer->get_double("aspect_ratio_den"));
354 }
355
356
357 int Render::getLength() {
358
359     if (m_mltProducer) {
360         // kDebug()<<"//////  LENGTH: "<<mlt_producer_get_playtime(m_mltProducer->get_producer());
361         return mlt_producer_get_playtime(m_mltProducer->get_producer());
362     }
363     return 0;
364 }
365
366 bool Render::isValid(KUrl url) {
367     char *tmp = decodedString(url.path());
368     Mlt::Producer producer(*m_mltProfile, tmp);
369     delete[] tmp;
370     if (producer.is_blank())
371         return false;
372
373     return true;
374 }
375
376 const double Render::dar() const {
377     return m_mltProfile->dar();
378 }
379
380 void Render::getFileProperties(const QDomElement &xml, int clipId) {
381     int height = 50;
382     int width = (int)(height  * m_mltProfile->dar());
383     QMap < QString, QString > filePropertyMap;
384     QMap < QString, QString > metadataPropertyMap;
385
386     KUrl url = KUrl(xml.attribute("resource", QString::null));
387     bool newProducer = false;
388
389     Mlt::Producer *producer = getProducerById(QString::number(clipId));
390     if (producer == NULL) {
391         if (true /*url.isEmpty()*/) {
392             QDomDocument doc;
393             QDomElement westley = doc.createElement("westley");
394             QDomElement play = doc.createElement("playlist");
395             doc.appendChild(westley);
396             westley.appendChild(play);
397             play.appendChild(doc.importNode(xml, true));
398             char *tmp = decodedString(doc.toString());
399             producer = new Mlt::Producer(*m_mltProfile, "westley-xml", tmp);
400             delete[] tmp;
401         } else {
402             char *tmp = decodedString(url.path());
403             producer = new Mlt::Producer(*m_mltProfile, tmp);
404             delete[] tmp;
405         }
406
407         if (producer->is_blank()) {
408             kDebug() << " / / / / / / / /ERRROR / / / / // CANNOT LOAD PRODUCER: ";
409             return;
410         }
411         m_producersList.append(producer);
412         newProducer = true;
413     }
414
415
416
417     int frameNumber = xml.attribute("thumbnail", "0").toInt();
418     if (frameNumber != 0) producer->seek(frameNumber);
419     mlt_properties properties = MLT_PRODUCER_PROPERTIES(producer->get_producer());
420
421     filePropertyMap["filename"] = url.path();
422     filePropertyMap["duration"] = QString::number(producer->get_playtime());
423     //kDebug() << "///////  PRODUCER: " << url.path() << " IS: " << producer.get_playtime();
424
425     Mlt::Frame * frame = producer->get_frame();
426     filePropertyMap["fps"] = producer->get("source_fps");
427
428     if (frame && frame->is_valid()) {
429         filePropertyMap["frame_size"] = QString::number(frame->get_int("width")) + "x" + QString::number(frame->get_int("height"));
430         filePropertyMap["frequency"] = QString::number(frame->get_int("frequency"));
431         filePropertyMap["channels"] = QString::number(frame->get_int("channels"));
432         filePropertyMap["aspect_ratio"] = frame->get("aspect_ratio");
433
434         if (frame->get_int("test_image") == 0) {
435             if (url.path().endsWith(".westley") || url.path().endsWith(".kdenlive")) {
436                 filePropertyMap["type"] = "playlist";
437                 metadataPropertyMap["comment"] = QString::fromUtf8(mlt_properties_get(MLT_SERVICE_PROPERTIES(producer->get_service()), "title"));
438             } else if (frame->get_int("test_audio") == 0)
439                 filePropertyMap["type"] = "av";
440             else
441                 filePropertyMap["type"] = "video";
442
443             mlt_image_format format = mlt_image_yuv422;
444             int frame_width = 0;
445             int frame_height = 0;
446             //frame->set("rescale.interp", "hyper");
447             frame->set("normalised_height", height);
448             frame->set("normalised_width", width);
449             QPixmap pix(width, height);
450
451             uint8_t *data = frame->get_image(format, frame_width, frame_height, 0);
452             uint8_t *new_image = (uint8_t *)mlt_pool_alloc(frame_width * (frame_height + 1) * 4);
453             mlt_convert_yuv422_to_rgb24a((uint8_t *)data, new_image, frame_width * frame_height);
454             QImage image((uchar *)new_image, frame_width, frame_height, QImage::Format_ARGB32);
455
456             if (!image.isNull()) {
457                 pix = pix.fromImage(image.rgbSwapped());
458             } else
459                 pix.fill(Qt::black);
460
461             mlt_pool_release(new_image);
462             emit replyGetImage(clipId, 0, pix, width, height);
463
464         } else if (frame->get_int("test_audio") == 0) {
465             QPixmap pixmap(KStandardDirs::locate("appdata", "graphics/music.png"));
466             emit replyGetImage(clipId, 0, pixmap, width, height);
467             filePropertyMap["type"] = "audio";
468         }
469     }
470
471     // Retrieve audio / video codec name
472
473     // Fetch the video_context
474 #if 1
475
476     AVFormatContext *context = (AVFormatContext *) mlt_properties_get_data(properties, "video_context", NULL);
477     if (context != NULL) {
478         // Get the video_index
479         int index = mlt_properties_get_int(properties, "video_index");
480
481 #if ENABLE_FFMPEG_CODEC_DESCRIPTION
482         if (context->streams && context->streams [index] && context->streams[ index ]->codec && context->streams[ index ]->codec->codec->long_name) {
483             filePropertyMap["videocodec"] = context->streams[ index ]->codec->codec->long_name;
484         } else
485 #endif
486             if (context->streams && context->streams [index] && context->streams[ index ]->codec && context->streams[ index ]->codec->codec->name) {
487                 filePropertyMap["videocodec"] = context->streams[ index ]->codec->codec->name;
488             }
489     } else kDebug() << " / / / / /WARNING, VIDEO CONTEXT IS NULL!!!!!!!!!!!!!!";
490     context = (AVFormatContext *) mlt_properties_get_data(properties, "audio_context", NULL);
491     if (context != NULL) {
492         // Get the audio_index
493         int index = mlt_properties_get_int(properties, "audio_index");
494
495 #if ENABLE_FFMPEG_CODEC_DESCRIPTION
496         if (context->streams && context->streams [index] && context->streams[ index ]->codec && context->streams[ index ]->codec->codec->long_name)
497             filePropertyMap["audiocodec"] = context->streams[ index ]->codec->codec->long_name;
498         else
499 #endif
500             if (context->streams && context->streams [index] && context->streams[ index ]->codec && context->streams[ index ]->codec->codec->name)
501                 filePropertyMap["audiocodec"] = context->streams[ index ]->codec->codec->name;
502     }
503 #endif
504     // metadata
505
506     mlt_properties metadata = mlt_properties_new();
507     mlt_properties_pass(metadata, properties, "meta.attr.");
508     int count = mlt_properties_count(metadata);
509     for (int i = 0; i < count; i ++) {
510         QString name = mlt_properties_get_name(metadata, i);
511         QString value = QString::fromUtf8(mlt_properties_get_value(metadata, i));
512         if (name.endsWith("markup") && !value.isEmpty())
513             metadataPropertyMap[ name.section(".", 0, -2)] = value;
514     }
515
516     emit replyGetFileProperties(clipId, filePropertyMap, metadataPropertyMap);
517     kDebug() << "REquested fuile info for: " << url.path();
518     if (frame) delete frame;
519     if (!newProducer && producer) delete producer;
520 }
521
522 /** Create the producer from the Westley QDomDocument */
523 #if 0
524 void Render::initSceneList() {
525     kDebug() << "--------  INIT SCENE LIST ------_";
526     QDomDocument doc;
527     QDomElement westley = doc.createElement("westley");
528     doc.appendChild(westley);
529     QDomElement prod = doc.createElement("producer");
530     prod.setAttribute("resource", "colour");
531     prod.setAttribute("colour", "red");
532     prod.setAttribute("id", "black");
533     prod.setAttribute("in", "0");
534     prod.setAttribute("out", "0");
535
536     QDomElement tractor = doc.createElement("tractor");
537     QDomElement multitrack = doc.createElement("multitrack");
538
539     QDomElement playlist1 = doc.createElement("playlist");
540     playlist1.appendChild(prod);
541     multitrack.appendChild(playlist1);
542     QDomElement playlist2 = doc.createElement("playlist");
543     multitrack.appendChild(playlist2);
544     QDomElement playlist3 = doc.createElement("playlist");
545     multitrack.appendChild(playlist3);
546     QDomElement playlist4 = doc.createElement("playlist");
547     multitrack.appendChild(playlist4);
548     QDomElement playlist5 = doc.createElement("playlist");
549     multitrack.appendChild(playlist5);
550     tractor.appendChild(multitrack);
551     westley.appendChild(tractor);
552     // kDebug()<<doc.toString();
553     /*
554        QString tmp = QString("<westley><producer resource=\"colour\" colour=\"red\" id=\"red\" /><tractor><multitrack><playlist></playlist><playlist></playlist><playlist /><playlist /><playlist></playlist></multitrack></tractor></westley>");*/
555     setSceneList(doc, 0);
556 }
557 #endif
558 /** Create the producer from the Westley QDomDocument */
559 void Render::setSceneList(QDomDocument list, int position) {
560     setSceneList(list.toString(), position);
561 }
562
563 /** Create the producer from the Westley QDomDocument */
564 void Render::setSceneList(QString playlist, int position) {
565     if (m_winid == -1) return;
566     m_generateScenelist = true;
567
568     kWarning() << "//////  RENDER, SET SCENE LIST: " << playlist;
569
570
571     /*
572         if (!clip.is_valid()) {
573      kWarning()<<" ++++ WARNING, UNABLE TO CREATE MLT PRODUCER";
574      m_generateScenelist = false;
575      return;
576         }*/
577
578     if (m_mltConsumer) {
579         m_mltConsumer->stop();
580         //m_mltConsumer->set("refresh", 0);
581     } else return;
582
583     if (m_mltProducer) {
584         m_mltProducer->set_speed(0);
585         //if (KdenliveSettings::osdtimecode() && m_osdInfo) m_mltProducer->detach(*m_osdInfo);
586
587         delete m_mltProducer;
588         m_mltProducer = NULL;
589         emit stopped();
590     }
591
592     char *tmp = decodedString(playlist);
593     m_mltProducer = new Mlt::Producer(*m_mltProfile, "westley-xml", tmp);
594     delete[] tmp;
595     if (!m_mltProducer || !m_mltProducer->is_valid()) kDebug() << " WARNING - - - - -INVALID PLAYLIST: " << tmp;
596     m_mltProducer->optimise();
597
598     /*if (KdenliveSettings::osdtimecode()) {
599     // Attach filter for on screen display of timecode
600     delete m_osdInfo;
601     QString attr = "attr_check";
602     mlt_filter filter = mlt_factory_filter( "data_feed", (char*) attr.ascii() );
603     mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "_fezzik", 1 );
604     mlt_producer_attach( m_mltProducer->get_producer(), filter );
605     mlt_filter_close( filter );
606
607       m_osdInfo = new Mlt::Filter("data_show");
608     tmp = decodedString(m_osdProfile);
609       m_osdInfo->set("resource", tmp);
610     delete[] tmp;
611     mlt_properties properties = MLT_PRODUCER_PROPERTIES(m_mltProducer->get_producer());
612     mlt_properties_set_int( properties, "meta.attr.timecode", 1);
613     mlt_properties_set( properties, "meta.attr.timecode.markup", "#timecode#");
614     m_osdInfo->set("dynamic", "1");
615
616       if (m_mltProducer->attach(*m_osdInfo) == 1) kDebug()<<"////// error attaching filter";
617     } else {
618     m_osdInfo->set("dynamic", "0");
619     }*/
620
621     m_fps = m_mltProducer->get_fps();
622     kDebug() << "// NEW SCENE LIST DURATION SET TO: " << m_mltProducer->get_playtime();
623     connectPlaylist();
624     if (position != 0) {
625         m_mltProducer->seek(position);
626         emit rendererPosition(position);
627     }
628     m_generateScenelist = false;
629
630 }
631
632 /** Create the producer from the Westley QDomDocument */
633 QString Render::sceneList() {
634     KTemporaryFile temp;
635     QString result;
636
637     if (temp.open()) {
638         saveSceneList(temp.fileName());
639         QFile file(temp.fileName());
640         if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
641             kWarning() << "++++++++++++++++   CANNOT READ TMP SCENELIST FILE";
642             return QString();
643         }
644         QTextStream in(&file);
645         while (!in.atEnd()) {
646             result.append(in.readLine());
647         }
648     }
649     return result;
650 }
651
652 void Render::saveSceneList(QString path, QDomElement kdenliveData) {
653
654     char *tmppath = decodedString("westley:" + path);
655     Mlt::Consumer westleyConsumer(*m_mltProfile , tmppath);
656     m_mltProducer->optimise();
657     delete[] tmppath;
658     westleyConsumer.set("terminate_on_pause", 1);
659     Mlt::Producer prod(m_mltProducer->get_producer());
660     westleyConsumer.connect(prod);
661     //prod.set("title", "kdenlive document");
662     //westleyConsumer.listen("consumer-frame-show", this, (mlt_listener) consumer_frame_show);
663     westleyConsumer.start();
664     while (!westleyConsumer.is_stopped()) {}
665     if (!kdenliveData.isNull()) {
666         // add Kdenlive specific tags
667         QFile file(path);
668         QDomDocument doc;
669         doc.setContent(&file, false);
670         QDomNode wes = doc.elementsByTagName("westley").at(0);
671         wes.appendChild(doc.importNode(kdenliveData, true));
672         file.close();
673         if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
674             kWarning() << "//////  ERROR writing to file: " << path;
675             return;
676         }
677         QTextStream out(&file);
678         out << doc.toString();
679         file.close();
680     }
681 }
682
683
684 const double Render::fps() const {
685     return m_fps;
686 }
687
688 void Render::connectPlaylist() {
689     if (!m_mltConsumer) return;
690     //m_mltConsumer->set("refresh", "0");
691     m_mltConsumer->connect(*m_mltProducer);
692     m_mltProducer->set_speed(0);
693     m_mltConsumer->start();
694     parsePlaylistForClips();
695     emit durationChanged(m_mltProducer->get_playtime());
696     //refresh();
697     /*
698      if (m_mltConsumer->start() == -1) {
699           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."));
700           m_mltConsumer = NULL;
701      }
702      else {
703              refresh();
704      }*/
705 }
706
707 void Render::refreshDisplay() {
708
709     if (!m_mltProducer) return;
710     //m_mltConsumer->set("refresh", 0);
711
712     mlt_properties properties = MLT_PRODUCER_PROPERTIES(m_mltProducer->get_producer());
713     /*if (KdenliveSettings::osdtimecode()) {
714         mlt_properties_set_int( properties, "meta.attr.timecode", 1);
715         mlt_properties_set( properties, "meta.attr.timecode.markup", "#timecode#");
716         m_osdInfo->set("dynamic", "1");
717         m_mltProducer->attach(*m_osdInfo);
718     }
719     else {
720         m_mltProducer->detach(*m_osdInfo);
721         m_osdInfo->set("dynamic", "0");
722     }*/
723     refresh();
724 }
725
726 void Render::setVolume(double volume) {
727     if (!m_mltConsumer || !m_mltProducer) return;
728     /*osdTimer->stop();
729     m_mltConsumer->set("refresh", 0);
730     // Attach filter for on screen display of timecode
731     mlt_properties properties = MLT_PRODUCER_PROPERTIES(m_mltProducer->get_producer());
732     mlt_properties_set_double( properties, "meta.volume", volume );
733     mlt_properties_set_int( properties, "meta.attr.osdvolume", 1);
734     mlt_properties_set( properties, "meta.attr.osdvolume.markup", i18n("Volume: ") + QString::number(volume * 100));
735
736     if (!KdenliveSettings::osdtimecode()) {
737     m_mltProducer->detach(*m_osdInfo);
738     mlt_properties_set_int( properties, "meta.attr.timecode", 0);
739      if (m_mltProducer->attach(*m_osdInfo) == 1) kDebug()<<"////// error attaching filter";
740     }*/
741     refresh();
742     osdTimer->setSingleShot(2500);
743 }
744
745 void Render::slotOsdTimeout() {
746     mlt_properties properties = MLT_PRODUCER_PROPERTIES(m_mltProducer->get_producer());
747     mlt_properties_set_int(properties, "meta.attr.osdvolume", 0);
748     mlt_properties_set(properties, "meta.attr.osdvolume.markup", NULL);
749     //if (!KdenliveSettings::osdtimecode()) m_mltProducer->detach(*m_osdInfo);
750     refresh();
751 }
752
753 void Render::start() {
754     kDebug() << "-----  STARTING MONITOR: " << m_name;
755     if (m_winid == -1) {
756         kDebug() << "-----  BROKEN MONITOR: " << m_name << ", RESTART";
757         return;
758     }
759
760     if (m_mltConsumer->is_stopped()) {
761         kDebug() << "-----  MONITOR: " << m_name << " WAS STOPPED";
762         if (m_mltConsumer->start() == -1) {
763             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."));
764             m_mltConsumer = NULL;
765             return;
766         } else {
767             kDebug() << "-----  MONITOR: " << m_name << " REFRESH";
768             m_isBlocked = false;
769             refresh();
770         }
771     }
772     m_isBlocked = false;
773 }
774
775 void Render::clear() {
776     kDebug() << " *********  RENDER CLEAR";
777     if (m_mltConsumer) {
778         //m_mltConsumer->set("refresh", 0);
779         if (!m_mltConsumer->is_stopped()) m_mltConsumer->stop();
780     }
781
782     if (m_mltProducer) {
783         //if (KdenliveSettings::osdtimecode() && m_osdInfo) m_mltProducer->detach(*m_osdInfo);
784         m_mltProducer->set_speed(0.0);
785         delete m_mltProducer;
786         m_mltProducer = NULL;
787         emit stopped();
788     }
789 }
790
791 void Render::stop() {
792     if (m_mltConsumer && !m_mltConsumer->is_stopped()) {
793         kDebug() << "/////////////   RENDER STOPPED: " << m_name;
794         //m_mltConsumer->set("refresh", 0);
795         m_mltConsumer->stop();
796     }
797     kDebug() << "/////////////   RENDER STOP2-------";
798     m_isBlocked = true;
799
800     if (m_mltProducer) {
801         m_mltProducer->set_speed(0.0);
802         m_mltProducer->set("out", m_mltProducer->get_length() - 1);
803         kDebug() << m_mltProducer->get_length();
804     }
805     kDebug() << "/////////////   RENDER STOP3-------";
806 }
807
808 void Render::stop(const GenTime & startTime) {
809
810     kDebug() << "/////////////   RENDER STOP-------2";
811     if (m_mltProducer) {
812         m_mltProducer->set_speed(0.0);
813         m_mltProducer->seek((int) startTime.frames(m_fps));
814     }
815     m_mltConsumer->purge();
816 }
817
818 void Render::switchPlay() {
819     if (!m_mltProducer)
820         return;
821     if (m_mltProducer->get_speed() == 0.0) {
822         m_isBlocked = false;
823         m_mltProducer->set_speed(1.0);
824         m_mltConsumer->set("refresh", 1);
825         kDebug() << " *********  RENDER PLAY: " << m_mltProducer->get_speed();
826     } else {
827         //m_isBlocked = true;
828         m_mltConsumer->set("refresh", 0);
829         m_mltProducer->set_speed(0.0);
830         m_isBlocked = true;
831         m_mltProducer->seek((int) m_framePosition);
832         //kDebug()<<" *********  RENDER PAUSE: "<<m_mltProducer->get_speed();
833         //m_mltConsumer->set("refresh", 0);
834         /*mlt_position position = mlt_producer_position( m_mltProducer->get_producer() );
835         m_mltProducer->set_speed(0);
836         m_mltProducer->seek( position );
837                //m_mltProducer->seek((int) m_framePosition);
838                m_isBlocked = false;*/
839     }
840     /*if (speed == 0.0) {
841     m_mltProducer->seek((int) m_framePosition + 1);
842         m_mltConsumer->purge();
843     }*/
844     //refresh();
845 }
846
847 void Render::play(double speed) {
848     kDebug() << " *********  REDNER PLAY";
849     if (!m_mltProducer)
850         return;
851     // if (speed == 0.0) m_mltProducer->set("out", m_mltProducer->get_length() - 1);
852     m_mltProducer->set_speed(speed);
853     /*if (speed == 0.0) {
854     m_mltProducer->seek((int) m_framePosition + 1);
855         m_mltConsumer->purge();
856     }*/
857     refresh();
858 }
859
860 void Render::play(double speed, const GenTime & startTime) {
861     kDebug() << "/////////////   RENDER PLAY2-------" << speed;
862     if (!m_mltProducer)
863         return;
864     //m_mltProducer->set("out", m_mltProducer->get_length() - 1);
865     //if (speed == 0.0) m_mltConsumer->set("refresh", 0);
866     m_mltProducer->set_speed(speed);
867     m_mltProducer->seek((int)(startTime.frames(m_fps)));
868     //m_mltConsumer->purge();
869     //refresh();
870 }
871
872 void Render::play(double speed, const GenTime & startTime,
873                   const GenTime & stopTime) {
874     kDebug() << "/////////////   RENDER PLAY3-------" << speed << stopTime.frames(m_fps);
875     if (!m_mltProducer)
876         return;
877     m_mltProducer->set("out", stopTime.frames(m_fps));
878     m_mltProducer->seek((int)(startTime.frames(m_fps)));
879     m_mltConsumer->purge();
880     m_mltProducer->set_speed(speed);
881     refresh();
882 }
883
884
885 void Render::sendSeekCommand(GenTime time) {
886     //kDebug()<<" *********  RENDER SEND SEEK";
887     if (!m_mltProducer)
888         return;
889     //kDebug()<<"//////////  KDENLIVE SEEK: "<<(int) (time.frames(m_fps));
890     m_mltProducer->seek((int)(time.frames(m_fps)));
891     m_mltConsumer->set("refresh", 1);
892     refresh();
893 }
894
895 void Render::seekToFrame(int pos) {
896     //kDebug()<<" *********  RENDER SEEK TO POS";
897     if (!m_mltProducer)
898         return;
899     //kDebug()<<"//////////  KDENLIVE SEEK: "<<(int) (time.frames(m_fps));
900     m_mltProducer->seek(pos);
901     refresh();
902 }
903
904 void Render::askForRefresh() {
905     // Use a Timer so that we don't refresh too much
906     refreshTimer->start(200);
907 }
908
909 void Render::doRefresh() {
910     // Use a Timer so that we don't refresh too much
911     m_mltConsumer->set("refresh", 1);
912 }
913
914 void Render::refresh() {
915     if (!m_mltProducer || m_isBlocked)
916         return;
917     refreshTimer->stop();
918     if (m_mltConsumer) {
919         m_mltConsumer->set("refresh", 1);
920     }
921 }
922
923 /** Sets the description of this renderer to desc. */
924 void Render::setDescription(const QString & description) {
925     m_description = description;
926 }
927
928 /** Returns the description of this renderer */
929 QString Render::description() {
930     return m_description;
931 }
932
933
934 double Render::playSpeed() {
935     if (m_mltProducer) return m_mltProducer->get_speed();
936     return 0.0;
937 }
938
939 GenTime Render::seekPosition() const {
940     if (m_mltProducer) return GenTime((int) m_mltProducer->position(), m_fps);
941     else return GenTime();
942 }
943
944
945 const QString & Render::rendererName() const {
946     return m_name;
947 }
948
949
950 void Render::emitFrameNumber(double position) {
951     if (m_generateScenelist) return;
952     m_framePosition = position;
953     emit rendererPosition((int) position);
954     //if (qApp->activeWindow()) QApplication::postEvent(qApp->activeWindow(), new PositionChangeEvent( GenTime((int) position, m_fps), m_monitorId));
955 }
956
957 void Render::emitConsumerStopped() {
958     // This is used to know when the playing stopped
959     if (m_mltProducer && !m_generateScenelist) {
960         double pos = m_mltProducer->position();
961         emit rendererStopped((int) pos);
962         //if (qApp->activeWindow()) QApplication::postEvent(qApp->activeWindow(), new PositionChangeEvent(GenTime((int) pos, m_fps), m_monitorId + 100));
963         //new QCustomEvent(10002));
964     }
965 }
966
967
968
969 void Render::exportFileToFirewire(QString srcFileName, int port, GenTime startTime, GenTime endTime) {
970     KMessageBox::sorry(0, i18n("Firewire is not enabled on your system.\n Please install Libiec61883 and recompile Kdenlive"));
971 }
972
973
974 void Render::exportCurrentFrame(KUrl url, bool notify) {
975     if (!m_mltProducer) {
976         KMessageBox::sorry(qApp->activeWindow(), i18n("There is no clip, cannot extract frame."));
977         return;
978     }
979
980     int height = 1080;//KdenliveSettings::defaultheight();
981     int width = 1940; //KdenliveSettings::displaywidth();
982     //TODO: rewrite
983     QPixmap pix; // = KThumb::getFrame(m_mltProducer, -1, width, height);
984     /*
985        QPixmap pix(width, height);
986        Mlt::Filter m_convert(*m_mltProfile, "avcolour_space");
987        m_convert.set("forced", mlt_image_rgb24a);
988        m_mltProducer->attach(m_convert);
989        Mlt::Frame * frame = m_mltProducer->get_frame();
990        m_mltProducer->detach(m_convert);
991        if (frame) {
992            pix = frameThumbnail(frame, width, height);
993            delete frame;
994        }*/
995     pix.save(url.path(), "PNG");
996     //if (notify) QApplication::postEvent(qApp->activeWindow(), new UrlEvent(url, 10003));
997 }
998
999 /** MLT PLAYLIST DIRECT MANIPULATON  **/
1000
1001
1002 void Render::mltCheckLength(bool reload) {
1003     //kDebug()<<"checking track length: "<<track<<"..........";
1004
1005     Mlt::Service service(m_mltProducer->get_service());
1006     Mlt::Tractor tractor(service);
1007
1008     int trackNb = tractor.count();
1009     double duration = 0;
1010     double trackDuration;
1011     if (trackNb == 1) {
1012         Mlt::Producer trackProducer(tractor.track(0));
1013         Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1014         duration = Mlt::Producer(trackPlaylist.get_producer()).get_playtime() - 1;
1015         m_mltProducer->set("out", duration);
1016         emit durationChanged((int) duration);
1017         return;
1018     }
1019     while (trackNb > 1) {
1020         Mlt::Producer trackProducer(tractor.track(trackNb - 1));
1021         Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1022         trackDuration = Mlt::Producer(trackPlaylist.get_producer()).get_playtime() - 1;
1023
1024         //kDebug() << " / / /DURATON FOR TRACK " << trackNb - 1 << " = " << trackDuration;
1025         if (trackDuration > duration) duration = trackDuration;
1026         trackNb--;
1027     }
1028
1029     Mlt::Producer blackTrackProducer(tractor.track(0));
1030     Mlt::Playlist blackTrackPlaylist((mlt_playlist) blackTrackProducer.get_service());
1031     double blackDuration = Mlt::Producer(blackTrackPlaylist.get_producer()).get_playtime() - 1;
1032
1033     if (blackDuration != duration) {
1034         blackTrackPlaylist.remove_region(0, (int)blackDuration);
1035         int i = 0;
1036         int dur = (int)duration;
1037         QDomDocument doc;
1038         QDomElement black = doc.createElement("producer");
1039         black.setAttribute("mlt_service", "colour");
1040         black.setAttribute("colour", "black");
1041         black.setAttribute("id", "black");
1042         ItemInfo info;
1043         info.track = 0;
1044         while (dur > 14000) {
1045             info.startPos = GenTime(i * 14000, m_fps);
1046             info.endPos = info.startPos + GenTime(13999, m_fps);
1047             mltInsertClip(info, black);
1048             dur = dur - 14000;
1049             i++;
1050         }
1051         if (dur > 0) {
1052             info.startPos = GenTime(i * 14000, m_fps);
1053             info.endPos = info.startPos + GenTime(dur, m_fps);
1054             mltInsertClip(info, black);
1055         }
1056         m_mltProducer->set("out", duration);
1057         emit durationChanged((int)duration);
1058     }
1059 }
1060
1061 void Render::mltInsertClip(ItemInfo info, QDomElement element) {
1062     if (!m_mltProducer) {
1063         kDebug() << "PLAYLIST NOT INITIALISED //////";
1064         return;
1065     }
1066     Mlt::Producer parentProd(m_mltProducer->parent());
1067     if (parentProd.get_producer() == NULL) {
1068         kDebug() << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////";
1069         return;
1070     }
1071
1072     Mlt::Service service(parentProd.get_service());
1073     Mlt::Tractor tractor(service);
1074     mlt_service_lock(service.get_service());
1075     Mlt::Producer trackProducer(tractor.track(info.track));
1076     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1077
1078     Mlt::Producer *prod = getProducerById(element.attribute("id"));
1079     if (prod == NULL) {
1080         // clip was never used yet
1081         QDomDocument doc;
1082         doc.appendChild(doc.importNode(element, true));
1083         QString resource = doc.toString();
1084         kDebug() << "// INSERTING CLIP: " << resource;
1085         char *tmp = decodedString(resource);
1086         prod = new Mlt::Producer(*m_mltProfile, "westley-xml", tmp);
1087         delete[] tmp;
1088         m_producersList.append(prod);
1089     }
1090
1091     Mlt::Producer *clip = prod->cut(info.cropStart.frames(m_fps), (info.endPos - info.startPos).frames(m_fps));
1092     trackPlaylist.insert_at((int) info.startPos.frames(m_fps), *clip, 1);
1093
1094     mlt_service_unlock(service.get_service());
1095
1096     if (info.track != 0) mltCheckLength();
1097     //tractor.multitrack()->refresh();
1098     //tractor.refresh();
1099 }
1100
1101 Mlt::Producer *Render::getProducerById(const QString &id) {
1102     for (int i = 0; i < m_producersList.count(); i++) {
1103         if (m_producersList.at(i)->get("id") == id) return m_producersList.at(i);
1104     }
1105     return NULL;
1106 }
1107
1108 void Render::parsePlaylistForClips() {
1109     // clear current producers list
1110     while (! m_producersList.isEmpty()) delete m_producersList.takeFirst();
1111
1112     //parse entire playlists to find all the different clips
1113     Mlt::Producer parentProd(m_mltProducer->parent());
1114     if (parentProd.get_producer() == NULL) {
1115         kDebug() << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////";
1116         return;
1117     }
1118     Mlt::Service service(parentProd.get_service());
1119     if (service.type() != tractor_type) return;
1120     Mlt::Tractor tractor(service);
1121     mlt_service_lock(service.get_service());
1122     for (int i = 0; i < tractor.count(); i++) {
1123         Mlt::Producer trackProducer(tractor.track(i));
1124         Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1125         for (int j = 0; j < trackPlaylist.count(); j++) {
1126             if (!trackPlaylist.is_blank(j)) {
1127                 Mlt::Producer *clip = trackPlaylist.get_clip(j);
1128                 if (clip) {
1129                     if (getProducerById(clip->get("id")) == NULL)
1130                         m_producersList.append(new Mlt::Producer(clip->get_parent()));
1131                 }
1132             }
1133         }
1134     }
1135 }
1136
1137 void Render::mltCutClip(int track, GenTime position) {
1138     m_isBlocked = true;
1139
1140     Mlt::Service service(m_mltProducer->parent().get_service());
1141     if (service.type() != tractor_type) kWarning() << "// TRACTOR PROBLEM";
1142
1143     Mlt::Tractor tractor(service);
1144     Mlt::Producer trackProducer(tractor.track(track));
1145     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1146     trackPlaylist.split_at((int) position.frames(m_fps));
1147     trackPlaylist.consolidate_blanks(0);
1148     m_isBlocked = false;
1149 }
1150
1151 void Render::mltUpdateClip(ItemInfo info, QDomElement element) {
1152     // TODO: optimize
1153     mltRemoveClip(info.track, info.startPos);
1154     mltInsertClip(info, element);
1155 }
1156
1157
1158 void Render::mltRemoveClip(int track, GenTime position) {
1159     m_isBlocked = true;
1160
1161     Mlt::Service service(m_mltProducer->parent().get_service());
1162     if (service.type() != tractor_type) kWarning() << "// TRACTOR PROBLEM";
1163
1164     Mlt::Tractor tractor(service);
1165     Mlt::Producer trackProducer(tractor.track(track));
1166     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1167     int clipIndex = trackPlaylist.get_clip_index_at((int) position.frames(m_fps));
1168     trackPlaylist.replace_with_blank(clipIndex);
1169     trackPlaylist.consolidate_blanks(0);
1170     if (track != 0) mltCheckLength();
1171     //emit durationChanged();
1172     m_isBlocked = false;
1173 }
1174
1175 bool Render::mltRemoveEffect(int track, GenTime position, QString index, bool doRefresh) {
1176
1177     Mlt::Service service(m_mltProducer->parent().get_service());
1178     bool success = false;
1179     Mlt::Tractor tractor(service);
1180     Mlt::Producer trackProducer(tractor.track(track));
1181     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1182     //int clipIndex = trackPlaylist.get_clip_index_at(position.frames(m_fps));
1183     Mlt::Producer *clip = trackPlaylist.get_clip_at((int) position.frames(m_fps));
1184     if (!clip) {
1185         kDebug() << " / / / CANNOT FIND CLIP TO REMOVE EFFECT";
1186         return success;
1187     }
1188     Mlt::Service clipService(clip->get_service());
1189 //    if (tag.startsWith("ladspa")) tag = "ladspa";
1190     m_isBlocked = true;
1191     int ct = 0;
1192     Mlt::Filter *filter = clipService.filter(ct);
1193     while (filter) {
1194         if (index == "-1" || filter->get("kdenlive_ix") == index) {// && filter->get("kdenlive_id") == id) {
1195             if (clipService.detach(*filter) == 0) success = true;
1196             kDebug() << " / / / DLEETED EFFECT: " << ct;
1197         } else ct++;
1198         filter = clipService.filter(ct);
1199     }
1200     m_isBlocked = false;
1201     if (doRefresh) refresh();
1202     return success;
1203 }
1204
1205
1206 bool Render::mltAddEffect(int track, GenTime position, QMap <QString, QString> args, bool doRefresh) {
1207
1208     Mlt::Service service(m_mltProducer->parent().get_service());
1209
1210     Mlt::Tractor tractor(service);
1211     Mlt::Producer trackProducer(tractor.track(track));
1212     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1213
1214     Mlt::Producer *clip = trackPlaylist.get_clip_at((int) position.frames(m_fps));
1215     if (!clip) {
1216         return false;
1217     }
1218     Mlt::Service clipService(clip->get_service());
1219     m_isBlocked = true;
1220     // create filter
1221     QString tag = args.value("tag");
1222     kDebug() << " / / INSERTING EFFECT: " << tag;
1223     if (tag.startsWith("ladspa")) tag = "ladspa";
1224     char *filterTag = decodedString(tag);
1225     char *filterId = decodedString(args.value("id"));
1226     QMap<QString, QString>::Iterator it;
1227     QString kfr = args.value("keyframes");
1228
1229     if (!kfr.isEmpty()) {
1230         QStringList keyFrames = kfr.split(";", QString::SkipEmptyParts);
1231         kDebug() << "// ADDING KEYFRAME EFFECT: " << args.value("keyframes");
1232         char *starttag = decodedString(args.value("starttag", "start"));
1233         char *endtag = decodedString(args.value("endtag", "end"));
1234         kDebug() << "// ADDING KEYFRAME TAGS: " << starttag << ", " << endtag;
1235         int duration = clip->get_playtime();
1236         double max = args.value("max").toDouble();
1237         double min = args.value("min").toDouble();
1238         double factor = args.value("factor", "1").toDouble();
1239         args.remove("starttag");
1240         args.remove("endtag");
1241         args.remove("keyframes");
1242         args.remove("min");
1243         args.remove("max");
1244         args.remove("factor");
1245         int offset = 0;
1246         for (int i = 0; i < keyFrames.size() - 1; ++i) {
1247             Mlt::Filter *filter = new Mlt::Filter(*m_mltProfile, filterTag);
1248             filter->set("kdenlive_id", filterId);
1249             int x1 = keyFrames.at(i).section(":", 0, 0).toInt() + offset;
1250             double y1 = keyFrames.at(i).section(":", 1, 1).toDouble();
1251             int x2 = keyFrames.at(i + 1).section(":", 0, 0).toInt();
1252             double y2 = keyFrames.at(i + 1).section(":", 1, 1).toDouble();
1253             if (x2 == -1) x2 = duration;
1254             for (it = args.begin(); it != args.end(); ++it) {
1255                 char *name = decodedString(it.key());
1256                 char *value = decodedString(it.value());
1257                 filter->set(name, value);
1258                 delete[] name;
1259                 delete[] value;
1260             }
1261
1262             filter->set("in", x1);
1263             filter->set("out", x2);
1264             //kDebug() << "// ADDING KEYFRAME vals: " << min<<" / "<<max<<", "<<y1<<", factor: "<<factor;
1265             filter->set(starttag, QString::number((min + y1) / factor).toUtf8().data());
1266             filter->set(endtag, QString::number((min + y2) / factor).toUtf8().data());
1267             clipService.attach(*filter);
1268             offset = 1;
1269         }
1270         delete[] starttag;
1271         delete[] endtag;
1272     } else {
1273         Mlt::Filter *filter = new Mlt::Filter(*m_mltProfile, filterTag);
1274         if (filter && filter->is_valid())
1275             filter->set("kdenlive_id", filterId);
1276         else {
1277             kDebug() << "filter is NULL";
1278             m_isBlocked = false;
1279             return false;
1280         }
1281
1282         for (it = args.begin(); it != args.end(); ++it) {
1283             char *name = decodedString(it.key());
1284             char *value = decodedString(it.value());
1285             filter->set(name, value);
1286             delete[] name;
1287             delete[] value;
1288         }
1289         // attach filter to the clip
1290         clipService.attach(*filter);
1291     }
1292     delete[] filterId;
1293     delete[] filterTag;
1294     m_isBlocked = false;
1295     if (doRefresh) refresh();
1296     return true;
1297 }
1298
1299 bool Render::mltEditEffect(int track, GenTime position, QMap <QString, QString> args) {
1300     QString index = args.value("kdenlive_ix");
1301     QString tag =  args.value("tag");
1302     QMap<QString, QString>::Iterator it = args.begin();
1303     if (!args.value("keyframes").isEmpty() || /*it.key().startsWith("#") || */tag.startsWith("ladspa") || tag == "sox" || tag == "autotrack_rectangle") {
1304         // This is a keyframe effect, to edit it, we remove it and re-add it.
1305         mltRemoveEffect(track, position, index);
1306         bool success = mltAddEffect(track, position, args);
1307         return success;
1308     }
1309
1310     // create filter
1311     Mlt::Service service(m_mltProducer->parent().get_service());
1312
1313     Mlt::Tractor tractor(service);
1314     Mlt::Producer trackProducer(tractor.track(track));
1315     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1316     //int clipIndex = trackPlaylist.get_clip_index_at(position.frames(m_fps));
1317     Mlt::Producer *clip = trackPlaylist.get_clip_at((int) position.frames(m_fps));
1318     if (!clip) {
1319         kDebug() << "WARINIG, CANNOT FIND CLIP ON track: " << track << ", AT POS: " << position.frames(m_fps);
1320         return false;
1321     }
1322     Mlt::Service clipService(clip->get_service());
1323     m_isBlocked = true;
1324     int ct = 0;
1325     Mlt::Filter *filter = clipService.filter(ct);
1326     while (filter) {
1327         if (filter->get("kdenlive_ix") == index) {
1328             break;
1329         }
1330         ct++;
1331         filter = clipService.filter(ct);
1332     }
1333
1334     if (!filter) {
1335         kDebug() << "WARINIG, FILTER FOR EDITING NOT FOUND, ADDING IT!!!!!";
1336         // filter was not found, it was probably a disabled filter, so add it to the correct place...
1337         int ct = 0;
1338         filter = clipService.filter(ct);
1339         QList <Mlt::Filter *> filtersList;
1340         while (filter) {
1341             if (QString(filter->get("kdenlive_ix")).toInt() > index.toInt()) {
1342                 filtersList.append(filter);
1343                 clipService.detach(*filter);
1344             } else ct++;
1345             filter = clipService.filter(ct);
1346         }
1347         bool success = mltAddEffect(track, position, args);
1348
1349         for (int i = 0; i < filtersList.count(); i++) {
1350             clipService.attach(*(filtersList.at(i)));
1351         }
1352
1353         m_isBlocked = false;
1354         return success;
1355     }
1356
1357     for (it = args.begin(); it != args.end(); ++it) {
1358         kDebug() << " / / EDITING EFFECT ARGS: " << it.key() << ": " << it.value();
1359         char *name = decodedString(it.key());
1360         char *value = decodedString(it.value());
1361         filter->set(name, value);
1362         delete[] name;
1363         delete[] value;
1364     }
1365     m_isBlocked = false;
1366     refresh();
1367     return true;
1368 }
1369
1370 void Render::mltMoveEffect(int track, GenTime position, int oldPos, int newPos) {
1371
1372     kDebug() << "MOVING EFFECT FROM " << oldPos << ", TO: " << newPos;
1373     Mlt::Service service(m_mltProducer->parent().get_service());
1374
1375     Mlt::Tractor tractor(service);
1376     Mlt::Producer trackProducer(tractor.track(track));
1377     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1378     //int clipIndex = trackPlaylist.get_clip_index_at(position.frames(m_fps));
1379     Mlt::Producer *clip = trackPlaylist.get_clip_at((int) position.frames(m_fps));
1380     if (!clip) {
1381         kDebug() << "WARINIG, CANNOT FIND CLIP ON track: " << track << ", AT POS: " << position.frames(m_fps);
1382         return;
1383     }
1384     Mlt::Service clipService(clip->get_service());
1385     m_isBlocked = true;
1386     int ct = 0;
1387     QList <Mlt::Filter *> filtersList;
1388     Mlt::Filter *filter = clipService.filter(ct);
1389     bool found = false;
1390     if (newPos > oldPos) {
1391         while (filter) {
1392             if (!found && QString(filter->get("kdenlive_ix")).toInt() == oldPos) {
1393                 filter->set("kdenlive_ix", newPos);
1394                 filtersList.append(filter);
1395                 clipService.detach(*filter);
1396                 filter = clipService.filter(ct);
1397                 while (filter && QString(filter->get("kdenlive_ix")).toInt() <= newPos) {
1398                     filter->set("kdenlive_ix", QString(filter->get("kdenlive_ix")).toInt() - 1);
1399                     ct++;
1400                     filter = clipService.filter(ct);
1401                 }
1402                 found = true;
1403             }
1404             if (filter && QString(filter->get("kdenlive_ix")).toInt() > newPos) {
1405                 filtersList.append(filter);
1406                 clipService.detach(*filter);
1407             } else ct++;
1408             filter = clipService.filter(ct);
1409         }
1410     } else {
1411         while (filter) {
1412             if (QString(filter->get("kdenlive_ix")).toInt() == oldPos) {
1413                 filter->set("kdenlive_ix", newPos);
1414                 filtersList.append(filter);
1415                 clipService.detach(*filter);
1416             } else ct++;
1417             filter = clipService.filter(ct);
1418         }
1419
1420         ct = 0;
1421         filter = clipService.filter(ct);
1422         while (filter) {
1423             int pos = QString(filter->get("kdenlive_ix")).toInt();
1424             if (pos >= newPos) {
1425                 if (pos < oldPos) filter->set("kdenlive_ix", QString(filter->get("kdenlive_ix")).toInt() + 1);
1426                 filtersList.append(filter);
1427                 clipService.detach(*filter);
1428             } else ct++;
1429             filter = clipService.filter(ct);
1430         }
1431     }
1432
1433     for (int i = 0; i < filtersList.count(); i++) {
1434         clipService.attach(*(filtersList.at(i)));
1435     }
1436
1437     m_isBlocked = false;
1438     refresh();
1439 }
1440
1441 void Render::mltResizeClipEnd(int track, GenTime pos, GenTime in, GenTime out) {
1442     m_isBlocked = true;
1443
1444     Mlt::Service service(m_mltProducer->parent().get_service());
1445
1446     Mlt::Tractor tractor(service);
1447     Mlt::Producer trackProducer(tractor.track(track));
1448     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1449     if (trackPlaylist.is_blank_at((int) pos.frames(m_fps) + 1))
1450         kDebug() << "////////  ERROR RSIZING BLANK CLIP!!!!!!!!!!!";
1451     int clipIndex = trackPlaylist.get_clip_index_at((int) pos.frames(m_fps) + 1);
1452
1453     int previousDuration = trackPlaylist.clip_length(clipIndex) - 1;
1454     int newDuration = (int) out.frames(m_fps) - 1;
1455
1456     kDebug() << " ** RESIZING CLIP END:" << clipIndex << " on track:" << track << ", mid pos: " << pos.frames(25) << ", in: " << in.frames(25) << ", out: " << out.frames(25) << ", PREVIOUS duration: " << previousDuration;
1457     trackPlaylist.resize_clip(clipIndex, (int) in.frames(m_fps), newDuration);
1458     trackPlaylist.consolidate_blanks(0);
1459     if (previousDuration < newDuration) {
1460         // clip was made longer, trim next blank if there is one.
1461         if (trackPlaylist.is_blank(clipIndex + 1)) {
1462             trackPlaylist.split(clipIndex + 1, newDuration - previousDuration);
1463             trackPlaylist.remove(clipIndex + 1);
1464         }
1465     } else trackPlaylist.insert_blank(clipIndex + 1, previousDuration - newDuration - 1);
1466
1467     trackPlaylist.consolidate_blanks(0);
1468     tractor.multitrack()->refresh();
1469     tractor.refresh();
1470     if (track != 0) mltCheckLength();
1471     m_isBlocked = false;
1472 }
1473
1474 void Render::mltChangeTrackState(int track, bool mute, bool blind) {
1475     Mlt::Service service(m_mltProducer->parent().get_service());
1476     Mlt::Tractor tractor(service);
1477     Mlt::Producer trackProducer(tractor.track(track));
1478     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1479     if (mute) {
1480         if (blind) trackProducer.set("hide", 3);
1481         else trackProducer.set("hide", 2);
1482     } else if (blind) {
1483         trackProducer.set("hide", 1);
1484     } else {
1485         trackProducer.set("hide", 0);
1486     }
1487     tractor.multitrack()->refresh();
1488     tractor.refresh();
1489     refresh();
1490 }
1491
1492 void Render::mltResizeClipStart(int track, GenTime pos, GenTime moveEnd, GenTime moveStart, GenTime in, GenTime out) {
1493     m_isBlocked = true;
1494
1495     Mlt::Service service(m_mltProducer->parent().get_service());
1496
1497     int moveFrame = (int)(moveEnd - moveStart).frames(m_fps);
1498
1499     Mlt::Tractor tractor(service);
1500     Mlt::Producer trackProducer(tractor.track(track));
1501     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1502     if (trackPlaylist.is_blank_at((int) pos.frames(m_fps) - 1))
1503         kDebug() << "////////  ERROR RSIZING BLANK CLIP!!!!!!!!!!!";
1504     int clipIndex = trackPlaylist.get_clip_index_at((int) pos.frames(m_fps) - 1);
1505     kDebug() << " ** RESIZING CLIP START:" << clipIndex << " on track:" << track << ", mid pos: " << pos.frames(25) << ", moving: " << moveFrame << ", in: " << in.frames(25) << ", out: " << out.frames(25);
1506
1507     trackPlaylist.resize_clip(clipIndex, (int) in.frames(m_fps), (int) out.frames(m_fps));
1508     if (moveFrame > 0) trackPlaylist.insert_blank(clipIndex, moveFrame - 1);
1509     else {
1510         int midpos = (int)moveStart.frames(m_fps) - 1; //+ (moveFrame / 2)
1511         int blankIndex = trackPlaylist.get_clip_index_at(midpos);
1512         int blankLength = trackPlaylist.clip_length(blankIndex);
1513
1514         kDebug() << " + resizing blank: " << blankIndex << ", Mid: " << midpos << ", Length: " << blankLength << ", SIZE DIFF: " << moveFrame;
1515
1516         if (blankLength + moveFrame == 0) trackPlaylist.remove(blankIndex);
1517         else trackPlaylist.resize_clip(blankIndex, 0, blankLength + moveFrame - 1);
1518     }
1519     trackPlaylist.consolidate_blanks(0);
1520     m_isBlocked = false;
1521 }
1522
1523 bool Render::mltMoveClip(int startTrack, int endTrack, GenTime moveStart, GenTime moveEnd) {
1524     return mltMoveClip(startTrack, endTrack, (int) moveStart.frames(m_fps), (int) moveEnd.frames(m_fps));
1525 }
1526
1527
1528 bool Render::mltMoveClip(int startTrack, int endTrack, int moveStart, int moveEnd) {
1529     m_isBlocked = true;
1530
1531     m_mltConsumer->set("refresh", 0);
1532     mlt_service_lock(m_mltConsumer->get_service());
1533     Mlt::Service service(m_mltProducer->parent().get_service());
1534     if (service.type() != tractor_type) kWarning() << "// TRACTOR PROBLEM";
1535
1536     Mlt::Tractor tractor(service);
1537     Mlt::Producer trackProducer(tractor.track(startTrack));
1538     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1539     int clipIndex = trackPlaylist.get_clip_index_at(moveStart + 1);
1540
1541     if (endTrack == startTrack) {
1542         //mlt_service_lock(service.get_service());
1543         Mlt::Producer clipProducer(trackPlaylist.replace_with_blank(clipIndex));
1544         trackPlaylist.consolidate_blanks(0);
1545         if (!trackPlaylist.is_blank_at(moveEnd)) {
1546             // error, destination is not empty
1547             //int ix = trackPlaylist.get_clip_index_at(moveEnd);
1548             mlt_service_unlock(m_mltConsumer->get_service());
1549             m_isBlocked = false;
1550             return false;
1551         } else {
1552             trackPlaylist.insert_at(moveEnd, clipProducer, 1);
1553             trackPlaylist.consolidate_blanks(0);
1554         }
1555         //mlt_service_unlock(service.get_service());
1556     } else {
1557         Mlt::Producer destTrackProducer(tractor.track(endTrack));
1558         Mlt::Playlist destTrackPlaylist((mlt_playlist) destTrackProducer.get_service());
1559         if (!destTrackPlaylist.is_blank_at(moveEnd)) {
1560             // error, destination is not empty
1561             mlt_service_unlock(m_mltConsumer->get_service());
1562             m_isBlocked = false;
1563             return false;
1564         } else {
1565             Mlt::Producer clipProducer(trackPlaylist.replace_with_blank(clipIndex));
1566             trackPlaylist.consolidate_blanks(0);
1567             destTrackPlaylist.consolidate_blanks(1);
1568             destTrackPlaylist.insert_at(moveEnd, clipProducer, 1);
1569             destTrackPlaylist.consolidate_blanks(0);
1570         }
1571     }
1572     mltCheckLength();
1573     mlt_service_unlock(m_mltConsumer->get_service());
1574     m_isBlocked = false;
1575     m_mltConsumer->set("refresh", 1);
1576     return true;
1577 }
1578
1579 void Render::mltMoveTransition(QString type, int startTrack, int newTrack, int newTransitionTrack, GenTime oldIn, GenTime oldOut, GenTime newIn, GenTime newOut) {
1580
1581     Mlt::Service service(m_mltProducer->parent().get_service());
1582     Mlt::Tractor tractor(service);
1583     Mlt::Field *field = tractor.field();
1584
1585     mlt_service_lock(service.get_service());
1586     m_mltConsumer->set("refresh", 0);
1587     m_isBlocked = true;
1588
1589     mlt_service serv = m_mltProducer->parent().get_service();
1590     mlt_service nextservice = mlt_service_get_producer(serv);
1591     mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
1592     QString mlt_type = mlt_properties_get(properties, "mlt_type");
1593     QString resource = mlt_properties_get(properties, "mlt_service");
1594     int old_pos = (int)(oldIn.frames(m_fps) + oldOut.frames(m_fps)) / 2;
1595
1596     int new_in = (int)newIn.frames(m_fps);
1597     int new_out = (int)newOut.frames(m_fps);
1598
1599     while (mlt_type == "transition") {
1600         mlt_transition tr = (mlt_transition) nextservice;
1601         int currentTrack = mlt_transition_get_b_track(tr);
1602         int currentIn = (int) mlt_transition_get_in(tr);
1603         int currentOut = (int) mlt_transition_get_out(tr);
1604
1605         if (resource == type && startTrack == currentTrack && currentIn <= old_pos && currentOut >= old_pos) {
1606             mlt_transition_set_in_and_out(tr, new_in, new_out);
1607             if (newTrack - startTrack != 0) {
1608                 kDebug() << "///// TRANSITION CHANGE TRACK. CUrrent (b): " << currentTrack << "x" << mlt_transition_get_a_track(tr) << ", NEw: " << newTrack << "x" << newTransitionTrack;
1609
1610                 mlt_properties properties = MLT_TRANSITION_PROPERTIES(tr);
1611                 mlt_properties_set_int(properties, "a_track", newTransitionTrack);
1612                 mlt_properties_set_int(properties, "b_track", newTrack);
1613                 //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);
1614             }
1615             break;
1616         }
1617         nextservice = mlt_service_producer(nextservice);
1618         if (nextservice == NULL) break;
1619         properties = MLT_SERVICE_PROPERTIES(nextservice);
1620         mlt_type = mlt_properties_get(properties, "mlt_type");
1621         resource = mlt_properties_get(properties, "mlt_service");
1622     }
1623     m_isBlocked = false;
1624     mlt_service_unlock(service.get_service());
1625     m_mltConsumer->set("refresh", 1);
1626 }
1627
1628 void Render::mltUpdateTransition(QString oldTag, QString tag, int a_track, int b_track, GenTime in, GenTime out, QDomElement xml) {
1629     // kDebug() << "update transition"  << tag << " at pos " << in.frames(25);
1630     if (oldTag == tag) mltUpdateTransitionParams(tag, a_track, b_track, in, out, xml);
1631     else {
1632         mltDeleteTransition(oldTag, a_track, b_track, in, out, xml, false);
1633         mltAddTransition(tag, a_track, b_track, in, out, xml);
1634     }
1635     //mltSavePlaylist();
1636 }
1637
1638 void Render::mltUpdateTransitionParams(QString type, int a_track, int b_track, GenTime in, GenTime out, QDomElement xml) {
1639     m_isBlocked = true;
1640
1641     Mlt::Service service(m_mltProducer->parent().get_service());
1642     Mlt::Tractor tractor(service);
1643     Mlt::Field *field = tractor.field();
1644
1645     //m_mltConsumer->set("refresh", 0);
1646     mlt_service serv = m_mltProducer->parent().get_service();
1647
1648     mlt_service nextservice = mlt_service_get_producer(serv);
1649     mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
1650     QString mlt_type = mlt_properties_get(properties, "mlt_type");
1651     QString resource = mlt_properties_get(properties, "mlt_service");
1652     int in_pos = (int) in.frames(m_fps);
1653     int out_pos = (int) out.frames(m_fps);
1654
1655     while (mlt_type == "transition") {
1656         mlt_transition tr = (mlt_transition) nextservice;
1657         int currentTrack = mlt_transition_get_b_track(tr);
1658         int currentIn = (int) mlt_transition_get_in(tr);
1659         int currentOut = (int) mlt_transition_get_out(tr);
1660
1661         // kDebug()<<"Looking for transition : " << currentIn <<"x"<<currentOut<< ", OLD oNE: "<<in_pos<<"x"<<out_pos;
1662
1663         if (resource == type && b_track == currentTrack && currentIn == in_pos && currentOut == out_pos) {
1664             QMap<QString, QString> map = mltGetTransitionParamsFromXml(xml);
1665             QMap<QString, QString>::Iterator it;
1666             QString key;
1667             mlt_properties transproperties = MLT_TRANSITION_PROPERTIES(tr);
1668
1669             for (it = map.begin(); it != map.end(); ++it) {
1670                 key = it.key();
1671                 char *name = decodedString(key);
1672                 char *value = decodedString(it.value());
1673                 mlt_properties_set(transproperties, name, value);
1674                 kDebug() << " ------  UPDATING TRANS PARAM: " << name << ": " << value;
1675                 //filter->set("kdenlive_id", id);
1676                 delete[] name;
1677                 delete[] value;
1678             }
1679             break;
1680         }
1681         nextservice = mlt_service_producer(nextservice);
1682         if (nextservice == NULL) break;
1683         properties = MLT_SERVICE_PROPERTIES(nextservice);
1684         mlt_type = mlt_properties_get(properties, "mlt_type");
1685         resource = mlt_properties_get(properties, "mlt_service");
1686     }
1687     m_isBlocked = false;
1688     m_mltConsumer->set("refresh", 1);
1689 }
1690
1691 void Render::mltDeleteTransition(QString tag, int a_track, int b_track, GenTime in, GenTime out, QDomElement xml, bool do_refresh) {
1692     Mlt::Service service(m_mltProducer->parent().get_service());
1693     Mlt::Tractor tractor(service);
1694     Mlt::Field *field = tractor.field();
1695
1696     if (do_refresh) m_mltConsumer->set("refresh", 0);
1697     mlt_service serv = m_mltProducer->parent().get_service();
1698
1699     mlt_service nextservice = mlt_service_get_producer(serv);
1700     mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
1701     QString mlt_type = mlt_properties_get(properties, "mlt_type");
1702     QString resource = mlt_properties_get(properties, "mlt_service");
1703     int old_pos = (int)((in + out).frames(m_fps) / 2);
1704
1705     while (mlt_type == "transition") {
1706         mlt_transition tr = (mlt_transition) nextservice;
1707         int currentTrack = mlt_transition_get_b_track(tr);
1708         int currentIn = (int) mlt_transition_get_in(tr);
1709         int currentOut = (int) mlt_transition_get_out(tr);
1710         kDebug() << "// FOUND EXISTING TRANS, IN: " << currentIn << ", OUT: " << currentOut << ", TRACK: " << currentTrack;
1711
1712         if (resource == tag && b_track == currentTrack && currentIn <= old_pos && currentOut >= old_pos) {
1713             //kDebug() << " / / / / /DELETE TRANS DOOOMNE";
1714             mlt_field_disconnect_service(field->get_field(), nextservice);
1715             break;
1716         }
1717         nextservice = mlt_service_producer(nextservice);
1718         if (nextservice == NULL) break;
1719         properties = MLT_SERVICE_PROPERTIES(nextservice);
1720         mlt_type = mlt_properties_get(properties, "mlt_type");
1721         resource = mlt_properties_get(properties, "mlt_service");
1722     }
1723     if (do_refresh) m_mltConsumer->set("refresh", 1);
1724 }
1725
1726 QMap<QString, QString> Render::mltGetTransitionParamsFromXml(QDomElement xml) {
1727     QDomNodeList attribs = xml.elementsByTagName("parameter");
1728     QMap<QString, QString> map;
1729     for (int i = 0;i < attribs.count();i++) {
1730         QDomElement e = attribs.item(i).toElement();
1731         QString name = e.attribute("name");
1732         //kDebug()<<"-- TRANSITION PARAM: "<<name<<" = "<< e.attribute("name")<<" / " << e.attribute("value");
1733         map[name] = e.attribute("default");
1734         if (!e.attribute("value").isEmpty()) {
1735             map[name] = e.attribute("value");
1736         }
1737         if (!e.attribute("factor").isEmpty() && e.attribute("factor").toDouble() > 0) {
1738             map[name] = QString::number(map[name].toDouble() / e.attribute("factor").toDouble());
1739             //map[name]=map[name].replace(".",","); //FIXME how to solve locale conversion of . ,
1740         }
1741
1742         if (e.attribute("namedesc").contains(";")) {
1743             QString format = e.attribute("format");
1744             QStringList separators = format.split("%d", QString::SkipEmptyParts);
1745             QStringList values = e.attribute("value").split(QRegExp("[,:;x]"));
1746             QString neu;
1747             QTextStream txtNeu(&neu);
1748             if (values.size() > 0)
1749                 txtNeu << (int)values[0].toDouble();
1750             int i = 0;
1751             for (i = 0;i < separators.size() && i + 1 < values.size();i++) {
1752                 txtNeu << separators[i];
1753                 txtNeu << (int)(values[i+1].toDouble());
1754             }
1755             if (i < separators.size())
1756                 txtNeu << separators[i];
1757             map[e.attribute("name")] = neu;
1758         }
1759
1760     }
1761     return map;
1762 }
1763
1764 void Render::mltAddTransition(QString tag, int a_track, int b_track, GenTime in, GenTime out, QDomElement xml, bool do_refresh) {
1765     QMap<QString, QString> args = mltGetTransitionParamsFromXml(xml);
1766
1767
1768     Mlt::Service service(m_mltProducer->parent().get_service());
1769
1770     Mlt::Tractor tractor(service);
1771     Mlt::Field *field = tractor.field();
1772
1773     char *transId = decodedString(tag);
1774     Mlt::Transition *transition = new Mlt::Transition(*m_mltProfile, transId);
1775     transition->set_in_and_out((int) in.frames(m_fps), (int) out.frames(m_fps));
1776     QMap<QString, QString>::Iterator it;
1777     QString key;
1778
1779     kDebug() << " ------  ADDING TRANSITION PARAMs: " << args.count();
1780
1781     for (it = args.begin(); it != args.end(); ++it) {
1782         key = it.key();
1783         char *name = decodedString(key);
1784         char *value = decodedString(it.value());
1785         transition->set(name, value);
1786         kDebug() << " ------  ADDING TRANS PARAM: " << name << ": " << value;
1787         //filter->set("kdenlive_id", id);
1788         delete[] name;
1789         delete[] value;
1790     }
1791     // attach filter to the clip
1792     field->plant_transition(*transition, a_track, b_track);
1793     delete[] transId;
1794     refresh();
1795 }
1796
1797 void Render::mltSavePlaylist() {
1798     kWarning() << "// UPDATING PLAYLIST TO DISK++++++++++++++++";
1799     Mlt::Consumer fileConsumer(*m_mltProfile, "westley");
1800     fileConsumer.set("resource", "/tmp/playlist.westley");
1801
1802     Mlt::Service service(m_mltProducer->get_service());
1803
1804     fileConsumer.connect(service);
1805     fileConsumer.start();
1806
1807 }
1808
1809 #include "renderer.moc"
1810