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