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