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