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