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