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