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