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