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