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