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