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