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