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