1 /***************************************************************************
2 krender.cpp - description
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
14 ***************************************************************************/
16 /***************************************************************************
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. *
23 ***************************************************************************/
27 #include "kdenlivesettings.h"
29 #include "definitions.h"
30 #include "slideshowclip.h"
31 #include "profilesdialog.h"
32 #include "blackmagic/devices.h"
34 #include <mlt++/Mlt.h>
37 #include <KStandardDirs>
38 #include <KMessageBox>
40 #include <KTemporaryFile>
45 #include <QApplication>
52 static void kdenlive_callback(void* /*ptr*/, int level, const char* fmt, va_list vl)
54 if (level > MLT_LOG_ERROR) return;
56 QApplication::postEvent(qApp->activeWindow(), new MltErrorEvent(error.vsprintf(fmt, vl).simplified()));
61 static void consumer_frame_show(mlt_consumer, Render * self, mlt_frame frame_ptr)
63 // detect if the producer has finished playing. Is there a better way to do it?
64 if (self->m_isBlocked) return;
65 Mlt::Frame frame(frame_ptr);
66 if (!frame.is_valid()) return;
67 self->emitFrameNumber(mlt_frame_get_position(frame_ptr));
68 if (self->sendFrameForAnalysis && frame_ptr->convert_image) {
69 self->emitFrameUpdated(frame);
71 if (self->analyseAudio) {
72 self->showAudio(frame);
74 if (frame.get_double("_speed") < 0.0 && mlt_frame_get_position(frame_ptr) <= 0) {
76 self->emitConsumerStopped();
81 static void consumer_paused(mlt_consumer, Render * self, mlt_frame /*frame_ptr*/)
83 // detect if the producer has finished playing. Is there a better way to do it?
84 if (self->m_isBlocked) return;
85 self->emitConsumerStopped();
89 static void consumer_gl_frame_show(mlt_consumer, Render * self, mlt_frame frame_ptr)
91 // detect if the producer has finished playing. Is there a better way to do it?
92 if (self->m_isBlocked) return;
93 Mlt::Frame frame(frame_ptr);
94 self->showFrame(frame);
95 if (frame.get_double("_speed") == 0.0) {
96 self->emitConsumerStopped();
97 } else if (frame.get_double("_speed") < 0.0 && mlt_frame_get_position(frame_ptr) <= 0) {
99 self->emitConsumerStopped();
103 Render::Render(const QString & rendererName, int winid, QString profile, QWidget *parent) :
104 AbstractRender(rendererName, parent),
106 analyseAudio(KdenliveSettings::monitor_audio()),
107 m_name(rendererName),
112 m_externalConsumer(false),
115 m_isSplitView(false),
119 if (profile.isEmpty()) profile = KdenliveSettings::current_profile();
120 buildConsumer(profile);
122 m_mltProducer = m_blackClip->cut(0, 50);
123 m_mltConsumer->connect(*m_mltProducer);
124 m_mltProducer->set_speed(0.0);
135 void Render::closeMlt()
139 Mlt::Service service(m_mltProducer->parent().get_service());
140 mlt_service_lock(service.get_service());
142 if (service.type() == tractor_type) {
143 Mlt::Tractor tractor(service);
144 Mlt::Field *field = tractor.field();
145 mlt_service nextservice = mlt_service_get_producer(service.get_service());
146 mlt_service nextservicetodisconnect;
147 mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
148 QString mlt_type = mlt_properties_get(properties, "mlt_type");
149 QString resource = mlt_properties_get(properties, "mlt_service");
150 // Delete all transitions
151 while (mlt_type == "transition") {
152 nextservicetodisconnect = nextservice;
153 nextservice = mlt_service_producer(nextservice);
154 mlt_field_disconnect_service(field->get_field(), nextservicetodisconnect);
155 if (nextservice == NULL) break;
156 properties = MLT_SERVICE_PROPERTIES(nextservice);
157 mlt_type = mlt_properties_get(properties, "mlt_type");
158 resource = mlt_properties_get(properties, "mlt_service");
161 for (int trackNb = tractor.count() - 1; trackNb >= 0; --trackNb) {
162 Mlt::Producer trackProducer(tractor.track(trackNb));
163 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
164 if (trackPlaylist.type() == playlist_type) trackPlaylist.clear();
169 mlt_service_unlock(service.get_service());
172 kDebug() << "// // // CLOSE RENDERER " << m_name;
173 delete m_mltConsumer;
174 delete m_mltProducer;
179 void Render::slotSwitchFullscreen()
181 if (m_mltConsumer) m_mltConsumer->set("full_screen", 1);
184 void Render::buildConsumer(const QString profileName)
189 //TODO: uncomment following line when everything is clean
190 // uncommented Feb 2011 --Granjow
191 if (m_mltProfile) delete m_mltProfile;
192 m_activeProfile = profileName;
193 char *tmp = qstrdup(m_activeProfile.toUtf8().constData());
194 setenv("MLT_PROFILE", tmp, 1);
195 m_mltProfile = new Mlt::Profile(tmp);
196 m_mltProfile->set_explicit(true);
199 m_blackClip = new Mlt::Producer(*m_mltProfile, "colour", "black");
200 m_blackClip->set("id", "black");
201 m_blackClip->set("mlt_type", "producer");
203 if (KdenliveSettings::external_display() && m_name != "clip") {
204 // Use blackmagic card for video output
205 QMap< QString, QString > profileProperties = ProfilesDialog::getSettingsFromFile(profileName);
206 int device = KdenliveSettings::blackmagic_output_device();
208 if (BMInterface::isSupportedProfile(device, profileProperties)) {
209 QString decklink = "decklink:" + QString::number(KdenliveSettings::blackmagic_output_device());
210 tmp = qstrdup(decklink.toUtf8().constData());
211 m_mltConsumer = new Mlt::Consumer(*m_mltProfile, tmp);
213 if (m_mltConsumer->is_valid()) {
214 m_externalConsumer = true;
215 m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_frame_show);
216 m_mltConsumer->set("terminate_on_pause", 0);
217 m_mltConsumer->set("buffer", 12);
218 m_mltConsumer->set("deinterlace_method", "onefield");
219 m_mltConsumer->set("real_time", KdenliveSettings::mltthreads());
220 mlt_log_set_callback(kdenlive_callback);
222 if (m_mltConsumer && m_mltConsumer->is_valid()) return;
223 } else KMessageBox::informationList(qApp->activeWindow(), i18n("Your project's profile %1 is not compatible with the blackmagic output card. Please see supported profiles below. Switching to normal video display.", m_mltProfile->description()), BMInterface::supportedModes(KdenliveSettings::blackmagic_output_device()));
226 m_externalConsumer = false;
227 QString videoDriver = KdenliveSettings::videodrivername();
228 if (!videoDriver.isEmpty()) {
229 if (videoDriver == "x11_noaccel") {
230 setenv("SDL_VIDEO_YUV_HWACCEL", "0", 1);
233 unsetenv("SDL_VIDEO_YUV_HWACCEL");
236 setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 1);
238 //m_mltConsumer->set("fullscreen", 1);
241 m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "sdl_audio");
242 m_mltConsumer->set("preview_off", 1);
243 m_mltConsumer->set("preview_format", mlt_image_rgb24a);
244 m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_gl_frame_show);
246 m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "sdl_preview");
247 // FIXME: the event object returned by the listen gets leaked...
248 m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_frame_show);
249 m_mltConsumer->listen("consumer-sdl-paused", this, (mlt_listener) consumer_paused);
250 m_mltConsumer->set("window_id", m_winid);
252 m_mltConsumer->set("resize", 1);
253 m_mltConsumer->set("terminate_on_pause", 1);
254 m_mltConsumer->set("window_background", KdenliveSettings::window_background().name().toUtf8().constData());
255 m_mltConsumer->set("rescale", "nearest");
256 mlt_log_set_callback(kdenlive_callback);
258 QString audioDevice = KdenliveSettings::audiodevicename();
259 if (!audioDevice.isEmpty())
260 m_mltConsumer->set("audio_device", audioDevice.toUtf8().constData());
262 if (!videoDriver.isEmpty())
263 m_mltConsumer->set("video_driver", videoDriver.toUtf8().constData());
265 QString audioDriver = KdenliveSettings::audiodrivername();
268 // Disabled because the "auto" detected driver was sometimes wrong
269 if (audioDriver.isEmpty())
270 audioDriver = KdenliveSettings::autoaudiodrivername();
273 if (!audioDriver.isEmpty())
274 m_mltConsumer->set("audio_driver", audioDriver.toUtf8().constData());
276 m_mltConsumer->set("progressive", 1);
277 m_mltConsumer->set("audio_buffer", 1024);
278 m_mltConsumer->set("frequency", 48000);
279 m_mltConsumer->set("real_time", KdenliveSettings::mltthreads());
282 Mlt::Producer *Render::invalidProducer(const QString &id)
284 Mlt::Producer *clip = new Mlt::Producer(*m_mltProfile, "colour", "red");
285 clip->set("id", id.toUtf8().constData());
286 clip->set("mlt_type", "producer");
290 int Render::resetProfile(const QString profileName, bool dropSceneList)
293 if (!dropSceneList) scene = sceneList();
295 if (m_externalConsumer == KdenliveSettings::external_display()) {
296 if (KdenliveSettings::external_display() && m_activeProfile == profileName) return 1;
297 QString videoDriver = KdenliveSettings::videodrivername();
298 QString currentDriver = m_mltConsumer->get("video_driver");
299 if (getenv("SDL_VIDEO_YUV_HWACCEL") != NULL && currentDriver == "x11") currentDriver = "x11_noaccel";
300 QString background = KdenliveSettings::window_background().name();
301 QString currentBackground = m_mltConsumer->get("window_background");
302 if (m_activeProfile == profileName && currentDriver == videoDriver && background == currentBackground) {
303 kDebug() << "reset to same profile, nothing to do";
308 if (m_isSplitView) slotSplitView(false);
309 if (!m_mltConsumer->is_stopped()) m_mltConsumer->stop();
310 m_mltConsumer->purge();
311 delete m_mltConsumer;
312 m_mltConsumer = NULL;
315 double current_fps = m_mltProfile->fps();
316 double current_dar = m_mltProfile->dar();
321 pos = m_mltProducer->position();
323 Mlt::Service service(m_mltProducer->get_service());
324 if (service.type() == tractor_type) {
325 Mlt::Tractor tractor(service);
326 for (int trackNb = tractor.count() - 1; trackNb >= 0; --trackNb) {
327 Mlt::Producer trackProducer(tractor.track(trackNb));
328 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
329 trackPlaylist.clear();
333 delete m_mltProducer;
335 m_mltProducer = NULL;
336 buildConsumer(profileName);
337 double new_fps = m_mltProfile->fps();
338 double new_dar = m_mltProfile->dar();
340 if (!dropSceneList) {
341 // We need to recover our playlist
342 if (current_fps != new_fps) {
343 // fps changed, we must update the scenelist positions
344 scene = updateSceneListFps(current_fps, new_fps, scene);
346 setSceneList(scene, pos);
347 // producers have changed (different profile), so reset them...
348 emit refreshDocumentProducers(new_dar != current_dar, current_fps != new_fps);
353 void Render::seek(GenTime time)
358 m_mltProducer->seek((int)(time.frames(m_fps)));
362 void Render::seek(int time)
367 m_mltProducer->seek(time);
372 /*QPixmap Render::frameThumbnail(Mlt::Frame *frame, int width, int height, bool border) {
373 QPixmap pix(width, height);
375 mlt_image_format format = mlt_image_rgb24a;
376 uint8_t *thumb = frame->get_image(format, width, height);
377 QImage image(thumb, width, height, QImage::Format_ARGB32);
379 if (!image.isNull()) {
380 pix = pix.fromImage(image);
382 QPainter painter(&pix);
383 painter.drawRect(0, 0, width - 1, height - 1);
385 } else pix.fill(Qt::black);
389 int Render::frameRenderWidth() const
391 return m_mltProfile->width();
394 int Render::renderWidth() const
396 return (int)(m_mltProfile->height() * m_mltProfile->dar() + 0.5);
399 int Render::renderHeight() const
401 return m_mltProfile->height();
404 QImage Render::extractFrame(int frame_position, QString path, int width, int height)
407 width = renderWidth();
408 height = renderHeight();
409 } else if (width % 2 == 1) width++;
411 if (!path.isEmpty()) {
412 Mlt::Producer *producer = new Mlt::Producer(*m_mltProfile, path.toUtf8().constData());
414 if (producer->is_valid()) {
415 QImage img = KThumb::getFrame(producer, frame_position, width, height);
419 else delete producer;
423 if (!m_mltProducer || !path.isEmpty()) {
424 QImage pix(width, height, QImage::Format_RGB32);
428 return KThumb::getFrame(m_mltProducer, frame_position, width, height);
431 QPixmap Render::getImageThumbnail(KUrl url, int /*width*/, int /*height*/)
435 if (url.fileName().startsWith(".all.")) { // check for slideshow
436 QString fileType = url.fileName().right(3);
438 QStringList::Iterator it;
440 QDir dir(url.directory());
442 filter << "*." + fileType;
443 filter << "*." + fileType.toUpper();
444 more = dir.entryList(filter, QDir::Files);
445 im.load(url.directory() + '/' + more.at(0));
446 } else im.load(url.path());
447 //pixmap = im.scaled(width, height);
451 double Render::consumerRatio() const
453 if (!m_mltConsumer) return 1.0;
454 return (m_mltConsumer->get_double("aspect_ratio_num") / m_mltConsumer->get_double("aspect_ratio_den"));
458 int Render::getLength()
462 // kDebug()<<"////// LENGTH: "<<mlt_producer_get_playtime(m_mltProducer->get_producer());
463 return mlt_producer_get_playtime(m_mltProducer->get_producer());
468 bool Render::isValid(KUrl url)
470 Mlt::Producer producer(*m_mltProfile, url.path().toUtf8().constData());
471 if (producer.is_blank())
477 double Render::dar() const
479 return m_mltProfile->dar();
482 double Render::sar() const
484 return m_mltProfile->sar();
487 void Render::slotSplitView(bool doit)
489 m_isSplitView = doit;
490 Mlt::Service service(m_mltProducer->parent().get_service());
491 Mlt::Tractor tractor(service);
492 if (service.type() != tractor_type || tractor.count() < 2) return;
493 Mlt::Field *field = tractor.field();
495 for (int i = 1, screen = 0; i < tractor.count() && screen < 4; i++) {
496 Mlt::Producer trackProducer(tractor.track(i));
497 kDebug() << "// TRACK: " << i << ", HIDE: " << trackProducer.get("hide");
498 if (QString(trackProducer.get("hide")).toInt() != 1) {
499 kDebug() << "// ADIDNG TRACK: " << i;
500 Mlt::Transition *transition = new Mlt::Transition(*m_mltProfile, "composite");
501 transition->set("mlt_service", "composite");
502 transition->set("a_track", 0);
503 transition->set("b_track", i);
504 transition->set("distort", 1);
505 transition->set("internal_added", "200");
512 tmp = "50%,0:50%x50%";
515 tmp = "0,50%:50%x50%";
519 tmp = "50%,50%:50%x50%";
522 transition->set("geometry", tmp);
523 transition->set("always_active", "1");
524 field->plant_transition(*transition, 0, i);
529 m_mltConsumer->set("refresh", 1);
531 mlt_service serv = m_mltProducer->parent().get_service();
532 mlt_service nextservice = mlt_service_get_producer(serv);
533 mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
534 QString mlt_type = mlt_properties_get(properties, "mlt_type");
535 QString resource = mlt_properties_get(properties, "mlt_service");
537 while (mlt_type == "transition") {
538 QString added = mlt_properties_get(MLT_SERVICE_PROPERTIES(nextservice), "internal_added");
539 if (added == "200") {
540 mlt_field_disconnect_service(field->get_field(), nextservice);
542 nextservice = mlt_service_producer(nextservice);
543 if (nextservice == NULL) break;
544 properties = MLT_SERVICE_PROPERTIES(nextservice);
545 mlt_type = mlt_properties_get(properties, "mlt_type");
546 resource = mlt_properties_get(properties, "mlt_service");
547 m_mltConsumer->set("refresh", 1);
552 void Render::getFileProperties(const QDomElement xml, const QString &clipId, int imageHeight, bool replaceProducer, bool selectClip)
556 if (xml.hasAttribute("proxy") && xml.attribute("proxy") != "-") {
557 path = xml.attribute("proxy");
558 proxyProducer = true;
561 path = xml.attribute("resource");
562 proxyProducer = false;
565 KUrl url = KUrl(path);
566 Mlt::Producer *producer = NULL;
567 CLIPTYPE type = (CLIPTYPE)xml.attribute("type").toInt();
568 //kDebug() << "PROFILE WIDT: "<< xml.attribute("mlt_service") << ": "<< m_mltProfile->width() << "\n...................\n\n";
569 /*if (xml.attribute("type").toInt() == TEXT && !QFile::exists(url.path())) {
570 emit replyGetFileProperties(clipId, producer, QMap < QString, QString >(), QMap < QString, QString >(), replaceProducer);
575 producer = new Mlt::Producer(*m_mltProfile, 0, ("colour:" + xml.attribute("colour")).toUtf8().constData());
576 } else if (type == TEXT) {
577 producer = new Mlt::Producer(*m_mltProfile, 0, ("kdenlivetitle:" + xml.attribute("resource")).toUtf8().constData());
578 if (producer && producer->is_valid() && xml.hasAttribute("xmldata"))
579 producer->set("xmldata", xml.attribute("xmldata").toUtf8().constData());
580 } else if (url.isEmpty()) {
582 QDomElement mlt = doc.createElement("mlt");
583 QDomElement play = doc.createElement("playlist");
584 doc.appendChild(mlt);
585 mlt.appendChild(play);
586 play.appendChild(doc.importNode(xml, true));
587 producer = new Mlt::Producer(*m_mltProfile, "xml-string", doc.toString().toUtf8().constData());
589 producer = new Mlt::Producer(*m_mltProfile, url.path().toUtf8().constData());
593 if (producer == NULL || producer->is_blank() || !producer->is_valid()) {
594 kDebug() << " / / / / / / / / ERROR / / / / // CANNOT LOAD PRODUCER: ";
596 // Proxy file is corrupted
597 emit removeInvalidProxy(clipId, false);
599 else emit removeInvalidClip(clipId, replaceProducer);
604 if (proxyProducer && xml.hasAttribute("proxy_out")) {
605 producer->set("length", xml.attribute("proxy_out").toInt() + 1);
606 producer->set("out", xml.attribute("proxy_out").toInt());
607 if (producer->get_out() != xml.attribute("proxy_out").toInt()) {
608 // Proxy file length is different than original clip length, this will corrupt project so disable this proxy clip
609 emit removeInvalidProxy(clipId, true);
615 if (xml.hasAttribute("force_aspect_ratio")) {
616 double aspect = xml.attribute("force_aspect_ratio").toDouble();
617 if (aspect > 0) producer->set("force_aspect_ratio", aspect);
620 if (xml.hasAttribute("force_aspect_num") && xml.hasAttribute("force_aspect_den")) {
621 int width = xml.attribute("frame_size").section('x', 0, 0).toInt();
622 int height = xml.attribute("frame_size").section('x', 1, 1).toInt();
623 int aspectNumerator = xml.attribute("force_aspect_num").toInt();
624 int aspectDenominator = xml.attribute("force_aspect_den").toInt();
625 if (aspectDenominator != 0 && width != 0)
626 producer->set("force_aspect_ratio", double(height) * aspectNumerator / aspectDenominator / width);
629 if (xml.hasAttribute("force_fps")) {
630 double fps = xml.attribute("force_fps").toDouble();
631 if (fps > 0) producer->set("force_fps", fps);
634 if (xml.hasAttribute("force_progressive")) {
636 int progressive = xml.attribute("force_progressive").toInt(&ok);
637 if (ok) producer->set("force_progressive", progressive);
639 if (xml.hasAttribute("force_tff")) {
641 int fieldOrder = xml.attribute("force_tff").toInt(&ok);
642 if (ok) producer->set("force_tff", fieldOrder);
644 if (xml.hasAttribute("threads")) {
645 int threads = xml.attribute("threads").toInt();
646 if (threads != 1) producer->set("threads", threads);
648 if (xml.hasAttribute("video_index")) {
649 int vindex = xml.attribute("video_index").toInt();
650 if (vindex != 0) producer->set("video_index", vindex);
652 if (xml.hasAttribute("audio_index")) {
653 int aindex = xml.attribute("audio_index").toInt();
654 if (aindex != 0) producer->set("audio_index", aindex);
656 if (xml.hasAttribute("force_colorspace")) {
657 int colorspace = xml.attribute("force_colorspace").toInt();
658 if (colorspace != 0) producer->set("force_colorspace", colorspace);
660 if (xml.hasAttribute("full_luma")) {
661 int full_luma = xml.attribute("full_luma").toInt();
662 if (full_luma != 0) producer->set("set.force_full_luma", full_luma);
667 if (xml.hasAttribute("out")) clipOut = xml.attribute("out").toInt();
669 // setup length here as otherwise default length (currently 15000 frames in MLT) will be taken even if outpoint is larger
670 if (type == COLOR || type == TEXT || type == IMAGE || type == SLIDESHOW) {
672 if (xml.hasAttribute("length")) {
673 if (clipOut > 0) duration = clipOut + 1;
674 length = xml.attribute("length").toInt();
675 clipOut = length - 1;
677 else length = xml.attribute("out").toInt() - xml.attribute("in").toInt();
678 producer->set("length", length);
681 if (clipOut > 0) producer->set_in_and_out(xml.attribute("in").toInt(), clipOut);
683 producer->set("id", clipId.toUtf8().constData());
685 if (xml.hasAttribute("templatetext"))
686 producer->set("templatetext", xml.attribute("templatetext").toUtf8().constData());
688 if ((!replaceProducer && xml.hasAttribute("file_hash")) || xml.hasAttribute("proxy")) {
689 // Clip already has all properties
690 emit replyGetFileProperties(clipId, producer, QMap < QString, QString >(), QMap < QString, QString >(), replaceProducer, selectClip);
694 int width = (int)(imageHeight * m_mltProfile->dar() + 0.5);
695 QMap < QString, QString > filePropertyMap;
696 QMap < QString, QString > metadataPropertyMap;
698 int frameNumber = xml.attribute("thumbnail", "0").toInt();
699 if (frameNumber != 0) producer->seek(frameNumber);
701 filePropertyMap["duration"] = QString::number(duration > 0 ? duration : producer->get_playtime());
702 //kDebug() << "/////// PRODUCER: " << url.path() << " IS: " << producer->get_playtime();
704 Mlt::Frame *frame = producer->get_frame();
706 if (type == SLIDESHOW) {
707 int ttl = xml.hasAttribute("ttl") ? xml.attribute("ttl").toInt() : 0;
708 if (ttl) producer->set("ttl", ttl);
709 if (!xml.attribute("animation").isEmpty()) {
710 Mlt::Filter *filter = new Mlt::Filter(*m_mltProfile, "affine");
711 if (filter && filter->is_valid()) {
713 QString geometry = SlideshowClip::animationToGeometry(xml.attribute("animation"), cycle);
714 if (!geometry.isEmpty()) {
715 if (xml.attribute("animation").contains("low-pass")) {
716 Mlt::Filter *blur = new Mlt::Filter(*m_mltProfile, "boxblur");
717 if (blur && blur->is_valid())
718 producer->attach(*blur);
720 filter->set("transition.geometry", geometry.toUtf8().data());
721 filter->set("transition.cycle", cycle);
722 producer->attach(*filter);
726 if (xml.attribute("fade") == "1") {
727 // user wants a fade effect to slideshow
728 Mlt::Filter *filter = new Mlt::Filter(*m_mltProfile, "luma");
729 if (filter && filter->is_valid()) {
730 if (ttl) filter->set("cycle", ttl);
731 if (xml.hasAttribute("luma_duration") && !xml.attribute("luma_duration").isEmpty()) filter->set("duration", xml.attribute("luma_duration").toInt());
732 if (xml.hasAttribute("luma_file") && !xml.attribute("luma_file").isEmpty()) {
733 filter->set("luma.resource", xml.attribute("luma_file").toUtf8().constData());
734 if (xml.hasAttribute("softness")) {
735 int soft = xml.attribute("softness").toInt();
736 filter->set("luma.softness", (double) soft / 100.0);
739 producer->attach(*filter);
742 if (xml.attribute("crop") == "1") {
743 // user wants to center crop the slides
744 Mlt::Filter *filter = new Mlt::Filter(*m_mltProfile, "crop");
745 if (filter && filter->is_valid()) {
746 filter->set("center", 1);
747 producer->attach(*filter);
752 if (producer->get_double("meta.media.frame_rate_den") > 0) {
753 filePropertyMap["fps"] = QString::number(producer->get_double("meta.media.frame_rate_num") / producer->get_double("meta.media.frame_rate_den"));
754 } else filePropertyMap["fps"] = producer->get("source_fps");
756 if (frame && frame->is_valid()) {
757 filePropertyMap["frame_size"] = QString::number(frame->get_int("width")) + 'x' + QString::number(frame->get_int("height"));
758 filePropertyMap["frequency"] = QString::number(frame->get_int("frequency"));
759 filePropertyMap["channels"] = QString::number(frame->get_int("channels"));
760 filePropertyMap["aspect_ratio"] = frame->get("aspect_ratio");
762 if (frame->get_int("test_image") == 0) {
763 if (url.path().endsWith(".mlt") || url.path().endsWith(".westley") || url.path().endsWith(".kdenlive")) {
764 filePropertyMap["type"] = "playlist";
765 metadataPropertyMap["comment"] = QString::fromUtf8(producer->get("title"));
766 } else if (frame->get_int("test_audio") == 0)
767 filePropertyMap["type"] = "av";
769 filePropertyMap["type"] = "video";
772 mlt_image_format format = mlt_image_rgb24a;
773 int frame_width = width;
774 int frame_height = imageHeight;
778 uint8_t *data = frame->get_image(format, frame_width, frame_height, 0);
779 QImage image((uchar *)data, frame_width, frame_height, QImage::Format_ARGB32_Premultiplied);
781 if (!image.isNull()) {
782 if (frame_width > (2 * width)) {
783 // there was a scaling problem, do it manually
784 QImage scaled = image.scaled(width, imageHeight);
785 pix = QPixmap::fromImage(scaled.rgbSwapped());
786 } else pix = QPixmap::fromImage(image.rgbSwapped());
787 variance = KThumb::imageVariance(image);
791 if (frameNumber == 0 && variance < 6) {
792 // Thumbnail is not interesting (for example all black, seek to fetch better thumb
794 producer->seek(frameNumber);
796 frame = producer->get_frame();
799 } while (variance == -1);
800 emit replyGetImage(clipId, pix);
802 } else if (frame->get_int("test_audio") == 0) {
803 QPixmap pixmap = KIcon("audio-x-generic").pixmap(QSize(width, imageHeight));
804 emit replyGetImage(clipId, pixmap);
805 filePropertyMap["type"] = "audio";
809 // Retrieve audio / video codec name
813 if (producer->get_int("video_index") > -1) {
814 /*if (context->duration == AV_NOPTS_VALUE) {
815 kDebug() << " / / / / / / / /ERROR / / / CLIP HAS UNKNOWN DURATION";
816 emit removeInvalidClip(clipId);
820 // Get the video_index
821 int default_video = producer->get_int("video_index");
823 int default_audio = producer->get_int("audio_index");
826 // Find maximum stream index values
827 for (int ix = 0; ix < producer->get_int("meta.media.nb_streams"); ix++) {
828 snprintf(property, sizeof(property), "meta.media.%d.stream.type", ix);
829 QString type = producer->get(property);
832 else if (type == "audio")
835 filePropertyMap["default_video"] = QString::number(default_video);
836 filePropertyMap["video_max"] = QString::number(video_max);
837 filePropertyMap["default_audio"] = QString::number(default_audio);
838 filePropertyMap["audio_max"] = QString::number(audio_max);
840 snprintf(property, sizeof(property), "meta.media.%d.codec.long_name", default_video);
841 if (producer->get(property)) {
842 filePropertyMap["videocodec"] = producer->get(property);
844 snprintf(property, sizeof(property), "meta.media.%d.codec.name", default_video);
845 if (producer->get(property))
846 filePropertyMap["videocodec"] = producer->get(property);
849 query = QString("meta.media.%1.codec.pix_fmt").arg(default_video);
850 filePropertyMap["pix_fmt"] = producer->get(query.toUtf8().constData());
851 filePropertyMap["colorspace"] = producer->get("meta.media.colorspace");
853 } else kDebug() << " / / / / /WARNING, VIDEO CONTEXT IS NULL!!!!!!!!!!!!!!";
854 if (producer->get_int("audio_index") > -1) {
855 // Get the audio_index
856 int index = producer->get_int("audio_index");
858 snprintf(property, sizeof(property), "meta.media.%d.codec.long_name", index);
859 if (producer->get(property)) {
860 filePropertyMap["audiocodec"] = producer->get(property);
862 snprintf(property, sizeof(property), "meta.media.%d.codec.name", index);
863 if (producer->get(property))
864 filePropertyMap["audiocodec"] = producer->get(property);
869 Mlt::Properties metadata;
870 metadata.pass_values(*producer, "meta.attr.");
871 int count = metadata.count();
872 for (int i = 0; i < count; i ++) {
873 QString name = metadata.get_name(i);
874 QString value = QString::fromUtf8(metadata.get(i));
875 if (name.endsWith("markup") && !value.isEmpty())
876 metadataPropertyMap[ name.section('.', 0, -2)] = value;
879 emit replyGetFileProperties(clipId, producer, filePropertyMap, metadataPropertyMap, replaceProducer, selectClip);
880 // FIXME: should delete this to avoid a leak...
886 /** Create the producer from the MLT XML QDomDocument */
887 void Render::initSceneList()
889 kDebug() << "-------- INIT SCENE LIST ------_";
891 QDomElement mlt = doc.createElement("mlt");
892 doc.appendChild(mlt);
893 QDomElement prod = doc.createElement("producer");
894 prod.setAttribute("resource", "colour");
895 prod.setAttribute("colour", "red");
896 prod.setAttribute("id", "black");
897 prod.setAttribute("in", "0");
898 prod.setAttribute("out", "0");
900 QDomElement tractor = doc.createElement("tractor");
901 QDomElement multitrack = doc.createElement("multitrack");
903 QDomElement playlist1 = doc.createElement("playlist");
904 playlist1.appendChild(prod);
905 multitrack.appendChild(playlist1);
906 QDomElement playlist2 = doc.createElement("playlist");
907 multitrack.appendChild(playlist2);
908 QDomElement playlist3 = doc.createElement("playlist");
909 multitrack.appendChild(playlist3);
910 QDomElement playlist4 = doc.createElement("playlist");
911 multitrack.appendChild(playlist4);
912 QDomElement playlist5 = doc.createElement("playlist");
913 multitrack.appendChild(playlist5);
914 tractor.appendChild(multitrack);
915 mlt.appendChild(tractor);
916 // kDebug()<<doc.toString();
918 QString tmp = QString("<mlt><producer resource=\"colour\" colour=\"red\" id=\"red\" /><tractor><multitrack><playlist></playlist><playlist></playlist><playlist /><playlist /><playlist></playlist></multitrack></tractor></mlt>");*/
919 setSceneList(doc, 0);
923 int Render::setProducer(Mlt::Producer *producer, int position)
925 if (m_winid == -1) return -1;
928 m_mltConsumer->stop();
931 m_mltConsumer->purge();
934 m_mltProducer->set_speed(0);
935 delete m_mltProducer;
936 m_mltProducer = NULL;
940 m_mltProducer = new Mlt::Producer(producer->get_producer());
941 } else m_mltProducer = m_blackClip->cut(0, 50);
943 if (!m_mltProducer || !m_mltProducer->is_valid()) {
944 kDebug() << " WARNING - - - - -INVALID PLAYLIST: ";
947 int volume = KdenliveSettings::volume();
948 m_mltProducer->set("meta.volume", (double)volume / 100);
949 m_fps = m_mltProducer->get_fps();
950 int error = connectPlaylist();
952 if (position != -1) {
953 m_mltProducer->seek(position);
954 emit rendererPosition(position);
955 } else emit rendererPosition((int) m_mltProducer->position());
960 int Render::setSceneList(QDomDocument list, int position)
962 return setSceneList(list.toString(), position);
965 int Render::setSceneList(QString playlist, int position)
967 if (m_winid == -1) return -1;
971 //kDebug() << "////// RENDER, SET SCENE LIST: " << playlist;
973 // Remove previous profile info
975 doc.setContent(playlist);
976 QDomElement profile = doc.documentElement().firstChildElement("profile");
977 doc.documentElement().removeChild(profile);
978 playlist = doc.toString();
981 if (!m_mltConsumer->is_stopped()) {
982 m_mltConsumer->stop();
984 m_mltConsumer->set("refresh", 0);
986 kWarning() << "/////// ERROR, TRYING TO USE NULL MLT CONSUMER";
991 m_mltProducer->set_speed(0);
992 //if (KdenliveSettings::osdtimecode() && m_osdInfo) m_mltProducer->detach(*m_osdInfo);
994 Mlt::Service service(m_mltProducer->parent().get_service());
995 mlt_service_lock(service.get_service());
997 if (service.type() == tractor_type) {
998 Mlt::Tractor tractor(service);
999 Mlt::Field *field = tractor.field();
1000 mlt_service nextservice = mlt_service_get_producer(service.get_service());
1001 mlt_service nextservicetodisconnect;
1002 mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
1003 QString mlt_type = mlt_properties_get(properties, "mlt_type");
1004 QString resource = mlt_properties_get(properties, "mlt_service");
1005 // Delete all transitions
1006 while (mlt_type == "transition") {
1007 nextservicetodisconnect = nextservice;
1008 nextservice = mlt_service_producer(nextservice);
1009 mlt_field_disconnect_service(field->get_field(), nextservicetodisconnect);
1010 if (nextservice == NULL) break;
1011 properties = MLT_SERVICE_PROPERTIES(nextservice);
1012 mlt_type = mlt_properties_get(properties, "mlt_type");
1013 resource = mlt_properties_get(properties, "mlt_service");
1017 for (int trackNb = tractor.count() - 1; trackNb >= 0; --trackNb) {
1018 Mlt::Producer trackProducer(tractor.track(trackNb));
1019 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1020 if (trackPlaylist.type() == playlist_type) trackPlaylist.clear();
1024 mlt_service_unlock(service.get_service());
1026 qDeleteAll(m_slowmotionProducers.values());
1027 m_slowmotionProducers.clear();
1029 delete m_mltProducer;
1030 m_mltProducer = NULL;
1035 // TODO: Better way to do this
1036 if (KdenliveSettings::projectloading_avformatnovalidate())
1037 playlist.replace(">avformat</property>", ">avformat-novalidate</property>");
1039 playlist.replace(">avformat-novalidate</property>", ">avformat</property>");
1040 m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", playlist.toUtf8().constData());
1041 if (!m_mltProducer || !m_mltProducer->is_valid()) {
1042 kDebug() << " WARNING - - - - -INVALID PLAYLIST: " << playlist.toUtf8().constData();
1043 m_mltProducer = m_blackClip->cut(0, 50);
1046 int volume = KdenliveSettings::volume();
1047 m_mltProducer->set("meta.volume", (double)volume / 100);
1048 m_mltProducer->optimise();
1050 /*if (KdenliveSettings::osdtimecode()) {
1051 // Attach filter for on screen display of timecode
1053 QString attr = "attr_check";
1054 mlt_filter filter = mlt_factory_filter( "data_feed", (char*) attr.ascii() );
1055 mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "_loader", 1 );
1056 mlt_producer_attach( m_mltProducer->get_producer(), filter );
1057 mlt_filter_close( filter );
1059 m_osdInfo = new Mlt::Filter("data_show");
1060 m_osdInfo->set("resource", m_osdProfile.toUtf8().constData());
1061 mlt_properties properties = MLT_PRODUCER_PROPERTIES(m_mltProducer->get_producer());
1062 mlt_properties_set_int( properties, "meta.attr.timecode", 1);
1063 mlt_properties_set( properties, "meta.attr.timecode.markup", "#timecode#");
1064 m_osdInfo->set("dynamic", "1");
1066 if (m_mltProducer->attach(*m_osdInfo) == 1) kDebug()<<"////// error attaching filter";
1068 m_osdInfo->set("dynamic", "0");
1071 m_fps = m_mltProducer->get_fps();
1072 if (position != 0) {
1073 // Seek to correct place after opening project.
1074 m_mltProducer->seek(position);
1077 kDebug() << "// NEW SCENE LIST DURATION SET TO: " << m_mltProducer->get_playtime();
1078 if (error == 0) error = connectPlaylist();
1079 else connectPlaylist();
1080 fillSlowMotionProducers();
1082 m_isBlocked = false;
1083 blockSignals(false);
1086 //kDebug()<<"// SETSCN LST, POS: "<<position;
1087 //if (position != 0) emit rendererPosition(position);
1090 const QString Render::sceneList()
1093 Mlt::Profile profile((mlt_profile) 0);
1094 Mlt::Consumer xmlConsumer(profile, "xml:kdenlive_playlist");
1095 m_mltProducer->optimise();
1096 xmlConsumer.set("terminate_on_pause", 1);
1097 Mlt::Producer prod(m_mltProducer->get_producer());
1098 bool split = m_isSplitView;
1099 if (split) slotSplitView(false);
1100 xmlConsumer.connect(prod);
1101 xmlConsumer.start();
1102 while (!xmlConsumer.is_stopped()) {}
1103 playlist = QString::fromUtf8(xmlConsumer.get("kdenlive_playlist"));
1104 if (split) slotSplitView(true);
1108 bool Render::saveSceneList(QString path, QDomElement kdenliveData)
1112 doc.setContent(sceneList(), false);
1113 if (!kdenliveData.isNull()) {
1114 // add Kdenlive specific tags
1115 QDomNode mlt = doc.elementsByTagName("mlt").at(0);
1116 mlt.appendChild(doc.importNode(kdenliveData, true));
1118 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
1119 kWarning() << "////// ERROR writing to file: " << path;
1122 file.write(doc.toString().toUtf8());
1123 if (file.error() != QFile::NoError) {
1131 void Render::saveZone(KUrl url, QString desc, QPoint zone)
1133 kDebug() << "// SAVING CLIP ZONE, RENDER: " << m_name;
1134 Mlt::Consumer xmlConsumer(*m_mltProfile, ("xml:" + url.path()).toUtf8().constData());
1135 m_mltProducer->optimise();
1136 xmlConsumer.set("terminate_on_pause", 1);
1137 if (m_name == "clip") {
1138 Mlt::Producer *prod = m_mltProducer->cut(zone.x(), zone.y());
1140 list.insert_at(0, prod, 0);
1142 list.set("title", desc.toUtf8().constData());
1143 xmlConsumer.connect(list);
1146 //TODO: not working yet, save zone from timeline
1147 Mlt::Producer *p1 = new Mlt::Producer(m_mltProducer->get_producer());
1148 /* Mlt::Service service(p1->parent().get_service());
1149 if (service.type() != tractor_type) kWarning() << "// TRACTOR PROBLEM";*/
1151 //Mlt::Producer *prod = p1->cut(zone.x(), zone.y());
1152 //prod->set("title", desc.toUtf8().constData());
1153 xmlConsumer.connect(*p1); //list);
1156 xmlConsumer.start();
1159 double Render::fps() const
1164 int Render::connectPlaylist()
1166 if (!m_mltConsumer) return -1;
1167 //m_mltConsumer->set("refresh", "0");
1168 m_mltConsumer->connect(*m_mltProducer);
1169 m_mltProducer->set_speed(0);
1170 if (m_mltConsumer->start() == -1) {
1171 // ARGH CONSUMER BROKEN!!!!
1172 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."));
1173 delete m_mltConsumer;
1174 m_mltConsumer = NULL;
1177 emit durationChanged(m_mltProducer->get_playtime());
1182 void Render::refreshDisplay()
1185 if (!m_mltProducer) return;
1186 //m_mltConsumer->set("refresh", 0);
1188 //mlt_properties properties = MLT_PRODUCER_PROPERTIES(m_mltProducer->get_producer());
1189 /*if (KdenliveSettings::osdtimecode()) {
1190 mlt_properties_set_int( properties, "meta.attr.timecode", 1);
1191 mlt_properties_set( properties, "meta.attr.timecode.markup", "#timecode#");
1192 m_osdInfo->set("dynamic", "1");
1193 m_mltProducer->attach(*m_osdInfo);
1196 m_mltProducer->detach(*m_osdInfo);
1197 m_osdInfo->set("dynamic", "0");
1202 int Render::volume() const
1204 if (!m_mltConsumer || !m_mltProducer) return -1;
1205 return ((int) 100 * m_mltProducer->get_double("meta.volume"));
1208 void Render::slotSetVolume(int volume)
1210 if (!m_mltConsumer || !m_mltProducer) return;
1211 m_mltProducer->set("meta.volume", (double)volume / 100.0);
1214 m_mltConsumer->set("refresh", 0);
1215 // Attach filter for on screen display of timecode
1216 mlt_properties properties = MLT_PRODUCER_PROPERTIES(m_mltProducer->get_producer());
1217 mlt_properties_set_double( properties, "meta.volume", volume );
1218 mlt_properties_set_int( properties, "meta.attr.osdvolume", 1);
1219 mlt_properties_set( properties, "meta.attr.osdvolume.markup", i18n("Volume: ") + QString::number(volume * 100));
1221 if (!KdenliveSettings::osdtimecode()) {
1222 m_mltProducer->detach(*m_osdInfo);
1223 mlt_properties_set_int( properties, "meta.attr.timecode", 0);
1224 if (m_mltProducer->attach(*m_osdInfo) == 1) kDebug()<<"////// error attaching filter";
1227 //m_osdTimer->setSingleShot(2500);
1230 void Render::slotOsdTimeout()
1232 mlt_properties properties = MLT_PRODUCER_PROPERTIES(m_mltProducer->get_producer());
1233 mlt_properties_set_int(properties, "meta.attr.osdvolume", 0);
1234 mlt_properties_set(properties, "meta.attr.osdvolume.markup", NULL);
1235 //if (!KdenliveSettings::osdtimecode()) m_mltProducer->detach(*m_osdInfo);
1239 void Render::start()
1241 kDebug() << "----- STARTING MONITOR: " << m_name;
1242 if (m_winid == -1) {
1243 kDebug() << "----- BROKEN MONITOR: " << m_name << ", RESTART";
1246 if (m_mltConsumer && m_mltConsumer->is_stopped()) {
1247 kDebug() << "----- MONITOR: " << m_name << " WAS STOPPED";
1248 if (m_mltConsumer->start() == -1) {
1249 //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."));
1250 kDebug(QtWarningMsg) << "/ / / / CANNOT START MONITOR";
1252 kDebug() << "----- MONITOR: " << m_name << " REFRESH";
1253 m_isBlocked = false;
1257 m_isBlocked = false;
1262 if (m_mltProducer == NULL) return;
1263 if (m_mltConsumer && !m_mltConsumer->is_stopped()) {
1264 kDebug() << "///////////// RENDER STOPPED: " << m_name;
1265 //m_mltConsumer->set("refresh", 0);
1266 m_mltConsumer->stop();
1267 // delete m_mltConsumer;
1268 // m_mltConsumer = NULL;
1270 kDebug() << "///////////// RENDER STOP2-------";
1273 if (m_mltProducer) {
1274 if (m_isZoneMode) resetZoneMode();
1275 m_mltProducer->set_speed(0.0);
1276 //m_mltProducer->set("out", m_mltProducer->get_length() - 1);
1277 //kDebug() << m_mltProducer->get_length();
1279 kDebug() << "///////////// RENDER STOP3-------";
1282 void Render::stop(const GenTime & startTime)
1285 kDebug() << "///////////// RENDER STOP-------2";
1286 if (m_mltProducer) {
1287 if (m_isZoneMode) resetZoneMode();
1288 m_mltProducer->set_speed(0.0);
1289 m_mltProducer->seek((int) startTime.frames(m_fps));
1291 m_mltConsumer->purge();
1294 void Render::pause()
1296 if (!m_mltProducer || !m_mltConsumer)
1298 if (m_mltProducer->get_speed() == 0.0) return;
1299 if (m_isZoneMode) resetZoneMode();
1301 m_mltConsumer->set("refresh", 0);
1302 m_mltProducer->set_speed(0.0);
1304 The 2 lines below create a flicker loop
1305 emit rendererPosition(m_framePosition);
1306 m_mltProducer->seek(m_framePosition);*/
1307 m_mltConsumer->purge();
1310 void Render::switchPlay(bool play)
1312 if (!m_mltProducer || !m_mltConsumer)
1314 if (m_isZoneMode) resetZoneMode();
1315 if (play && m_mltProducer->get_speed() == 0.0) {
1316 m_isBlocked = false;
1317 if (m_name == "clip" && m_framePosition == (int) m_mltProducer->get_out()) m_mltProducer->seek(0);
1318 m_mltProducer->set_speed(1.0);
1319 m_mltConsumer->set("refresh", 1);
1322 m_mltConsumer->set("refresh", 0);
1323 m_mltProducer->set_speed(0.0);
1324 //emit rendererPosition(m_framePosition);
1325 m_mltProducer->seek(m_framePosition);
1326 //m_mltConsumer->purge();
1330 void Render::play(double speed)
1334 // if (speed == 0.0) m_mltProducer->set("out", m_mltProducer->get_length() - 1);
1335 m_isBlocked = false;
1336 m_mltProducer->set_speed(speed);
1337 /*if (speed == 0.0) {
1338 m_mltProducer->seek((int) m_framePosition + 1);
1339 m_mltConsumer->purge();
1344 void Render::play(const GenTime & startTime)
1346 if (!m_mltProducer || !m_mltConsumer)
1348 m_isBlocked = false;
1349 m_mltProducer->seek((int)(startTime.frames(m_fps)));
1350 m_mltProducer->set_speed(1.0);
1351 m_mltConsumer->set("refresh", 1);
1354 void Render::loopZone(const GenTime & startTime, const GenTime & stopTime)
1356 if (!m_mltProducer || !m_mltConsumer)
1358 //m_mltProducer->set("eof", "loop");
1359 m_isLoopMode = true;
1360 m_loopStart = startTime;
1361 playZone(startTime, stopTime);
1364 void Render::playZone(const GenTime & startTime, const GenTime & stopTime)
1366 if (!m_mltProducer || !m_mltConsumer)
1368 m_isBlocked = false;
1369 if (!m_isZoneMode) m_originalOut = m_mltProducer->get_playtime() - 1;
1370 m_mltProducer->set("out", (int)(stopTime.frames(m_fps)));
1371 m_mltProducer->seek((int)(startTime.frames(m_fps)));
1372 m_mltProducer->set_speed(1.0);
1373 m_mltConsumer->set("refresh", 1);
1374 m_isZoneMode = true;
1377 void Render::resetZoneMode()
1379 if (!m_isZoneMode && !m_isLoopMode) return;
1380 m_mltProducer->set("out", m_originalOut);
1381 //m_mltProducer->set("eof", "pause");
1382 m_isZoneMode = false;
1383 m_isLoopMode = false;
1386 void Render::seekToFrame(int pos)
1388 //kDebug()<<" ********* RENDER SEEK TO POS";
1391 m_isBlocked = false;
1393 m_mltProducer->seek(pos);
1397 void Render::seekToFrameDiff(int diff)
1399 //kDebug()<<" ********* RENDER SEEK TO POS";
1402 m_isBlocked = false;
1404 m_mltProducer->seek(m_mltProducer->position() + diff);
1408 void Render::doRefresh()
1410 // Use a Timer so that we don't refresh too much
1411 if (!m_isBlocked && m_mltConsumer) m_mltConsumer->set("refresh", 1);
1414 void Render::refresh()
1416 if (!m_mltProducer || m_isBlocked)
1418 if (m_mltConsumer) {
1419 m_mltConsumer->set("refresh", 1);
1423 void Render::setDropFrames(bool show)
1425 if (m_mltConsumer) {
1426 int dropFrames = KdenliveSettings::mltthreads();
1427 if (show == false) dropFrames = -dropFrames;
1428 m_mltConsumer->stop();
1430 m_mltConsumer->set("real_time", dropFrames);
1432 m_mltConsumer->set("play.real_time", dropFrames);
1434 if (m_mltConsumer->start() == -1) {
1435 kDebug(QtWarningMsg) << "ERROR, Cannot start monitor";
1441 double Render::playSpeed()
1443 if (m_mltProducer) return m_mltProducer->get_speed();
1447 GenTime Render::seekPosition() const
1449 if (m_mltProducer) return GenTime((int) m_mltProducer->position(), m_fps);
1450 else return GenTime();
1453 int Render::seekFramePosition() const
1455 if (m_mltProducer) return (int) m_mltProducer->position();
1459 const QString & Render::rendererName() const
1464 void Render::emitFrameUpdated(Mlt::Frame& frame)
1466 mlt_image_format format = mlt_image_rgb24a;
1469 const uchar* image = frame.get_image(format, width, height);
1470 QImage qimage(width, height, QImage::Format_ARGB32);
1471 memcpy(qimage.bits(), image, width * height * 4);
1472 emit frameUpdated(qimage.rgbSwapped());
1475 void Render::emitFrameNumber(double position)
1477 m_framePosition = position;
1478 emit rendererPosition((int) position);
1481 void Render::emitConsumerStopped()
1483 // This is used to know when the playing stopped
1484 if (m_mltProducer) {
1485 double pos = m_mltProducer->position();
1486 if (m_isLoopMode) play(m_loopStart);
1487 else if (m_isZoneMode) resetZoneMode();
1488 emit rendererStopped((int) pos);
1492 void Render::exportFileToFirewire(QString /*srcFileName*/, int /*port*/, GenTime /*startTime*/, GenTime /*endTime*/)
1494 KMessageBox::sorry(0, i18n("Firewire is not enabled on your system.\n Please install Libiec61883 and recompile Kdenlive"));
1497 void Render::exportCurrentFrame(KUrl url, bool /*notify*/)
1499 if (!m_mltProducer) {
1500 KMessageBox::sorry(qApp->activeWindow(), i18n("There is no clip, cannot extract frame."));
1504 //int height = 1080;//KdenliveSettings::defaultheight();
1505 //int width = 1940; //KdenliveSettings::displaywidth();
1507 QPixmap pix; // = KThumb::getFrame(m_mltProducer, -1, width, height);
1509 QPixmap pix(width, height);
1510 Mlt::Filter m_convert(*m_mltProfile, "avcolour_space");
1511 m_convert.set("forced", mlt_image_rgb24a);
1512 m_mltProducer->attach(m_convert);
1513 Mlt::Frame * frame = m_mltProducer->get_frame();
1514 m_mltProducer->detach(m_convert);
1516 pix = frameThumbnail(frame, width, height);
1519 pix.save(url.path(), "PNG");
1520 //if (notify) QApplication::postEvent(qApp->activeWindow(), new UrlEvent(url, 10003));
1524 void Render::showFrame(Mlt::Frame& frame)
1526 m_framePosition = qMax(frame.get_int("_position"), 0);
1527 emit rendererPosition((int) m_framePosition);
1528 mlt_image_format format = mlt_image_rgb24a;
1531 const uchar* image = frame.get_image(format, width, height);
1532 QImage qimage(width, height, QImage::Format_ARGB32_Premultiplied);
1533 memcpy(qimage.scanLine(0), image, width * height * 4);
1534 emit showImageSignal(qimage);
1535 if (analyseAudio) showAudio(frame);
1536 if (sendFrameForAnalysis && frame.get_frame()->convert_image) {
1537 emit frameUpdated(qimage.rgbSwapped());
1541 void Render::showAudio(Mlt::Frame& frame)
1543 if (!frame.is_valid() || frame.get_int("test_audio") != 0) {
1546 mlt_audio_format audio_format = mlt_audio_s16;
1548 int num_channels = 0;
1550 int16_t* data = (int16_t*)frame.get_audio(audio_format, freq, num_channels, samples);
1556 // Data format: [ c00 c10 c01 c11 c02 c12 c03 c13 ... c0{samples-1} c1{samples-1} for 2 channels.
1557 // So the vector is of size samples*channels.
1558 QVector<int16_t> sampleVector(samples*num_channels);
1559 memcpy(sampleVector.data(), data, samples*num_channels*sizeof(int16_t));
1562 emit audioSamplesSignal(sampleVector, freq, num_channels, samples);
1567 * MLT playlist direct manipulation.
1570 void Render::mltCheckLength(Mlt::Tractor *tractor)
1572 //kDebug()<<"checking track length: "<<track<<"..........";
1574 int trackNb = tractor->count();
1578 Mlt::Producer trackProducer(tractor->track(0));
1579 duration = trackProducer.get_playtime() - 1;
1580 m_mltProducer->set("out", duration);
1581 emit durationChanged(duration);
1584 while (trackNb > 1) {
1585 Mlt::Producer trackProducer(tractor->track(trackNb - 1));
1586 trackDuration = trackProducer.get_playtime() - 1;
1587 // kDebug() << " / / /DURATON FOR TRACK " << trackNb - 1 << " = " << trackDuration;
1588 if (trackDuration > duration) duration = trackDuration;
1592 Mlt::Producer blackTrackProducer(tractor->track(0));
1594 if (blackTrackProducer.get_playtime() - 1 != duration) {
1595 Mlt::Playlist blackTrackPlaylist((mlt_playlist) blackTrackProducer.get_service());
1596 Mlt::Producer *blackclip = blackTrackPlaylist.get_clip(0);
1597 if (blackclip && blackclip->is_blank()) {
1602 if (blackclip == NULL || blackTrackPlaylist.count() != 1) {
1603 blackTrackPlaylist.clear();
1604 m_blackClip->set("length", duration + 1);
1605 m_blackClip->set("out", duration);
1606 blackclip = m_blackClip->cut(0, duration);
1607 blackTrackPlaylist.insert_at(0, blackclip, 1);
1609 if (duration > blackclip->parent().get_length()) {
1610 blackclip->parent().set("length", duration + 1);
1611 blackclip->parent().set("out", duration);
1612 blackclip->set("length", duration + 1);
1614 blackTrackPlaylist.resize_clip(0, 0, duration);
1618 m_mltProducer->set("out", duration);
1619 emit durationChanged(duration);
1623 Mlt::Producer *Render::checkSlowMotionProducer(Mlt::Producer *prod, QDomElement element)
1625 if (element.attribute("speed", "1.0").toDouble() == 1.0 && element.attribute("strobe", "1").toInt() == 1) return prod;
1627 // We want a slowmotion producer
1628 double speed = element.attribute("speed", "1.0").toDouble();
1629 int strobe = element.attribute("strobe", "1").toInt();
1630 QString url = QString::fromUtf8(prod->get("resource"));
1631 url.append('?' + QString::number(speed));
1632 if (strobe > 1) url.append("&strobe=" + QString::number(strobe));
1633 Mlt::Producer *slowprod = m_slowmotionProducers.value(url);
1634 if (!slowprod || slowprod->get_producer() == NULL) {
1635 slowprod = new Mlt::Producer(*m_mltProfile, 0, ("framebuffer:" + url).toUtf8().constData());
1636 if (strobe > 1) slowprod->set("strobe", strobe);
1637 QString id = prod->get("id");
1638 if (id.contains('_')) id = id.section('_', 0, 0);
1639 QString producerid = "slowmotion:" + id + ':' + QString::number(speed);
1640 if (strobe > 1) producerid.append(':' + QString::number(strobe));
1641 slowprod->set("id", producerid.toUtf8().constData());
1642 m_slowmotionProducers.insert(url, slowprod);
1647 int Render::mltInsertClip(ItemInfo info, QDomElement element, Mlt::Producer *prod, bool overwrite, bool push)
1649 if (m_mltProducer == NULL) {
1650 kDebug() << "PLAYLIST NOT INITIALISED //////";
1654 kDebug() << "Cannot insert clip without producer //////";
1657 Mlt::Producer parentProd(m_mltProducer->parent());
1658 if (parentProd.get_producer() == NULL) {
1659 kDebug() << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////";
1663 Mlt::Service service(parentProd.get_service());
1664 if (service.type() != tractor_type) {
1665 kWarning() << "// TRACTOR PROBLEM";
1668 Mlt::Tractor tractor(service);
1669 if (info.track > tractor.count() - 1) {
1670 kDebug() << "ERROR TRYING TO INSERT CLIP ON TRACK " << info.track << ", at POS: " << info.startPos.frames(25);
1673 mlt_service_lock(service.get_service());
1674 Mlt::Producer trackProducer(tractor.track(info.track));
1675 int trackDuration = trackProducer.get_playtime() - 1;
1676 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1677 //kDebug()<<"/// INSERT cLIP: "<<info.cropStart.frames(m_fps)<<", "<<info.startPos.frames(m_fps)<<"-"<<info.endPos.frames(m_fps);
1678 prod = checkSlowMotionProducer(prod, element);
1679 if (prod == NULL || !prod->is_valid()) {
1680 mlt_service_unlock(service.get_service());
1684 int cutPos = (int) info.cropStart.frames(m_fps);
1685 if (cutPos < 0) cutPos = 0;
1686 int insertPos = (int) info.startPos.frames(m_fps);
1687 int cutDuration = (int)(info.endPos - info.startPos).frames(m_fps) - 1;
1688 Mlt::Producer *clip = prod->cut(cutPos, cutDuration + cutPos);
1689 if (overwrite && (insertPos < trackDuration)) {
1690 // Replace zone with blanks
1691 //trackPlaylist.split_at(insertPos, true);
1692 trackPlaylist.remove_region(insertPos, cutDuration + 1);
1693 int clipIndex = trackPlaylist.get_clip_index_at(insertPos);
1694 trackPlaylist.insert_blank(clipIndex, cutDuration);
1696 trackPlaylist.split_at(insertPos, true);
1697 int clipIndex = trackPlaylist.get_clip_index_at(insertPos);
1698 trackPlaylist.insert_blank(clipIndex, cutDuration);
1700 int newIndex = trackPlaylist.insert_at(insertPos, clip, 1);
1702 /*if (QString(prod->get("transparency")).toInt() == 1)
1703 mltAddClipTransparency(info, info.track - 1, QString(prod->get("id")).toInt());*/
1705 if (info.track != 0 && (newIndex + 1 == trackPlaylist.count())) mltCheckLength(&tractor);
1706 mlt_service_unlock(service.get_service());
1707 /*tractor.multitrack()->refresh();
1708 tractor.refresh();*/
1713 void Render::mltCutClip(int track, GenTime position)
1717 Mlt::Service service(m_mltProducer->parent().get_service());
1718 if (service.type() != tractor_type) {
1719 kWarning() << "// TRACTOR PROBLEM";
1723 Mlt::Tractor tractor(service);
1724 Mlt::Producer trackProducer(tractor.track(track));
1725 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1728 /* // Display playlist info
1729 kDebug()<<"//////////// BEFORE";
1730 for (int i = 0; i < trackPlaylist.count(); i++) {
1731 int blankStart = trackPlaylist.clip_start(i);
1732 int blankDuration = trackPlaylist.clip_length(i) - 1;
1734 if (trackPlaylist.is_blank(i)) blk = "(blank)";
1735 kDebug()<<"CLIP "<<i<<": ("<<blankStart<<'x'<<blankStart + blankDuration<<")"<<blk;
1738 int cutPos = (int) position.frames(m_fps);
1740 int clipIndex = trackPlaylist.get_clip_index_at(cutPos);
1741 if (trackPlaylist.is_blank(clipIndex)) {
1742 kDebug() << "// WARNING, TRYING TO CUT A BLANK";
1743 m_isBlocked = false;
1746 mlt_service_lock(service.get_service());
1747 int clipStart = trackPlaylist.clip_start(clipIndex);
1748 trackPlaylist.split(clipIndex, cutPos - clipStart - 1);
1749 mlt_service_unlock(service.get_service());
1751 // duplicate effects
1752 Mlt::Producer *original = trackPlaylist.get_clip_at(clipStart);
1753 Mlt::Producer *clip = trackPlaylist.get_clip_at(cutPos);
1755 if (original == NULL || clip == NULL) {
1756 kDebug() << "// ERROR GRABBING CLIP AFTER SPLIT";
1758 Mlt::Service clipService(original->get_service());
1759 Mlt::Service dupService(clip->get_service());
1763 Mlt::Filter *filter = clipService.filter(ct);
1765 // Only duplicate Kdenlive filters, and skip the fade in effects
1766 if (filter->is_valid() && strcmp(filter->get("kdenlive_id"), "") && strcmp(filter->get("kdenlive_id"), "fadein") && strcmp(filter->get("kdenlive_id"), "fade_from_black")) {
1767 // looks like there is no easy way to duplicate a filter,
1768 // so we will create a new one and duplicate its properties
1769 Mlt::Filter *dup = new Mlt::Filter(*m_mltProfile, filter->get("mlt_service"));
1770 if (dup && dup->is_valid()) {
1771 Mlt::Properties entries(filter->get_properties());
1772 for (int i = 0; i < entries.count(); i++) {
1773 dup->set(entries.get_name(i), entries.get(i));
1775 dupService.attach(*dup);
1779 filter = clipService.filter(ct);
1782 /* // Display playlist info
1783 kDebug()<<"//////////// AFTER";
1784 for (int i = 0; i < trackPlaylist.count(); i++) {
1785 int blankStart = trackPlaylist.clip_start(i);
1786 int blankDuration = trackPlaylist.clip_length(i) - 1;
1788 if (trackPlaylist.is_blank(i)) blk = "(blank)";
1789 kDebug()<<"CLIP "<<i<<": ("<<blankStart<<'x'<<blankStart + blankDuration<<")"<<blk;
1792 m_isBlocked = false;
1795 bool Render::mltUpdateClip(ItemInfo info, QDomElement element, Mlt::Producer *prod)
1799 kDebug() << "Cannot update clip with null producer //////";
1802 Mlt::Service service(m_mltProducer->parent().get_service());
1803 if (service.type() != tractor_type) {
1804 kWarning() << "// TRACTOR PROBLEM";
1807 Mlt::Tractor tractor(service);
1808 Mlt::Producer trackProducer(tractor.track(tractor.count() - 1 - info.track));
1809 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1810 int startPos = info.startPos.frames(m_fps);
1811 int clipIndex = trackPlaylist.get_clip_index_at(startPos);
1812 if (trackPlaylist.is_blank(clipIndex)) {
1813 kDebug() << "// WARNING, TRYING TO REMOVE A BLANK: " << startPos;
1816 mlt_service_lock(service.get_service());
1817 Mlt::Producer *clip = trackPlaylist.get_clip(clipIndex);
1819 QList <Mlt::Filter *> filtersList;
1820 Mlt::Service sourceService(clip->get_service());
1822 Mlt::Filter *filter = sourceService.filter(ct);
1824 if (filter->get_int("kdenlive_ix") != 0) {
1825 filtersList.append(filter);
1828 filter = sourceService.filter(ct);
1831 trackPlaylist.replace_with_blank(clipIndex);
1833 //if (!mltRemoveClip(info.track, info.startPos)) return false;
1834 prod = checkSlowMotionProducer(prod, element);
1835 if (prod == NULL || !prod->is_valid()) {
1836 mlt_service_unlock(service.get_service());
1840 Mlt::Producer *clip2 = prod->cut(info.cropStart.frames(m_fps), (info.cropDuration + info.cropStart).frames(m_fps) - 1);
1841 trackPlaylist.insert_at(info.startPos.frames(m_fps), clip2, 1);
1845 //if (mltInsertClip(info, element, prod) == -1) return false;
1846 if (!filtersList.isEmpty()) {
1847 clipIndex = trackPlaylist.get_clip_index_at(startPos);
1848 Mlt::Producer *destclip = trackPlaylist.get_clip(clipIndex);
1849 Mlt::Service destService(destclip->get_service());
1851 for (int i = 0; i < filtersList.count(); i++)
1852 destService.attach(*(filtersList.at(i)));
1854 mlt_service_unlock(service.get_service());
1859 bool Render::mltRemoveClip(int track, GenTime position)
1861 Mlt::Service service(m_mltProducer->parent().get_service());
1862 if (service.type() != tractor_type) {
1863 kWarning() << "// TRACTOR PROBLEM";
1867 Mlt::Tractor tractor(service);
1868 mlt_service_lock(service.get_service());
1869 Mlt::Producer trackProducer(tractor.track(track));
1870 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1871 int clipIndex = trackPlaylist.get_clip_index_at((int) position.frames(m_fps));
1873 // Display playlist info
1874 //kDebug() << "//// BEFORE -( " << position.frames(m_fps) << " )-------------------------------";
1875 /*for (int i = 0; i < trackPlaylist.count(); i++) {
1876 int blankStart = trackPlaylist.clip_start(i);
1877 int blankDuration = trackPlaylist.clip_length(i) - 1;
1879 if (trackPlaylist.is_blank(i)) blk = "(blank)";
1880 kDebug()<<"CLIP "<<i<<": ("<<blankStart<<'x'<<blankStart + blankDuration<<")"<<blk;
1882 if (trackPlaylist.is_blank(clipIndex)) {
1883 kDebug() << "// WARNING, TRYING TO REMOVE A BLANK: " << position.frames(25);
1884 mlt_service_unlock(service.get_service());
1887 //kDebug()<<"//// Deleting at: "<< (int) position.frames(m_fps) <<" --------------------------------------";
1889 Mlt::Producer *clip = trackPlaylist.replace_with_blank(clipIndex);
1890 if (clip) delete clip;
1891 trackPlaylist.consolidate_blanks(0);
1892 /*if (QString(clip.parent().get("transparency")).toInt() == 1)
1893 mltDeleteTransparency((int) position.frames(m_fps), track, QString(clip.parent().get("id")).toInt());*/
1895 /* // Display playlist info
1896 kDebug()<<"//// AFTER";
1897 for (int i = 0; i < trackPlaylist.count(); i++) {
1898 int blankStart = trackPlaylist.clip_start(i);
1899 int blankDuration = trackPlaylist.clip_length(i) - 1;
1901 if (trackPlaylist.is_blank(i)) blk = "(blank)";
1902 kDebug()<<"CLIP "<<i<<": ("<<blankStart<<'x'<<blankStart + blankDuration<<")"<<blk;
1904 mlt_service_unlock(service.get_service());
1905 if (track != 0 && trackPlaylist.count() <= clipIndex) mltCheckLength(&tractor);
1906 m_isBlocked = false;
1910 int Render::mltGetSpaceLength(const GenTime pos, int track, bool fromBlankStart)
1912 if (!m_mltProducer) {
1913 kDebug() << "PLAYLIST NOT INITIALISED //////";
1916 Mlt::Producer parentProd(m_mltProducer->parent());
1917 if (parentProd.get_producer() == NULL) {
1918 kDebug() << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////";
1922 Mlt::Service service(parentProd.get_service());
1923 Mlt::Tractor tractor(service);
1924 int insertPos = pos.frames(m_fps);
1926 Mlt::Producer trackProducer(tractor.track(track));
1927 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1928 int clipIndex = trackPlaylist.get_clip_index_at(insertPos);
1929 if (clipIndex == trackPlaylist.count()) {
1930 // We are after the end of the playlist
1933 if (!trackPlaylist.is_blank(clipIndex)) return 0;
1934 if (fromBlankStart) return trackPlaylist.clip_length(clipIndex);
1935 return trackPlaylist.clip_length(clipIndex) + trackPlaylist.clip_start(clipIndex) - insertPos;
1938 int Render::mltTrackDuration(int track)
1940 if (!m_mltProducer) {
1941 kDebug() << "PLAYLIST NOT INITIALISED //////";
1944 Mlt::Producer parentProd(m_mltProducer->parent());
1945 if (parentProd.get_producer() == NULL) {
1946 kDebug() << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////";
1950 Mlt::Service service(parentProd.get_service());
1951 Mlt::Tractor tractor(service);
1953 Mlt::Producer trackProducer(tractor.track(track));
1954 return trackProducer.get_playtime() - 1;
1957 void Render::mltInsertSpace(QMap <int, int> trackClipStartList, QMap <int, int> trackTransitionStartList, int track, const GenTime duration, const GenTime timeOffset)
1959 if (!m_mltProducer) {
1960 kDebug() << "PLAYLIST NOT INITIALISED //////";
1963 Mlt::Producer parentProd(m_mltProducer->parent());
1964 if (parentProd.get_producer() == NULL) {
1965 kDebug() << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////";
1968 //kDebug()<<"// CLP STRT LST: "<<trackClipStartList;
1969 //kDebug()<<"// TRA STRT LST: "<<trackTransitionStartList;
1971 Mlt::Service service(parentProd.get_service());
1972 Mlt::Tractor tractor(service);
1973 mlt_service_lock(service.get_service());
1974 int diff = duration.frames(m_fps);
1975 int offset = timeOffset.frames(m_fps);
1979 // insert space in one track only
1980 Mlt::Producer trackProducer(tractor.track(track));
1981 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
1982 insertPos = trackClipStartList.value(track);
1983 if (insertPos != -1) {
1984 insertPos += offset;
1985 int clipIndex = trackPlaylist.get_clip_index_at(insertPos);
1987 trackPlaylist.insert_blank(clipIndex, diff - 1);
1989 if (!trackPlaylist.is_blank(clipIndex)) clipIndex --;
1990 if (!trackPlaylist.is_blank(clipIndex)) {
1991 kDebug() << "//// ERROR TRYING TO DELETE SPACE FROM " << insertPos;
1993 int position = trackPlaylist.clip_start(clipIndex);
1994 int blankDuration = trackPlaylist.clip_length(clipIndex);
1995 if (blankDuration + diff == 0) {
1996 trackPlaylist.remove(clipIndex);
1997 } else trackPlaylist.remove_region(position, -diff);
1999 trackPlaylist.consolidate_blanks(0);
2001 // now move transitions
2002 mlt_service serv = m_mltProducer->parent().get_service();
2003 mlt_service nextservice = mlt_service_get_producer(serv);
2004 mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
2005 QString mlt_type = mlt_properties_get(properties, "mlt_type");
2006 QString resource = mlt_properties_get(properties, "mlt_service");
2008 while (mlt_type == "transition") {
2009 mlt_transition tr = (mlt_transition) nextservice;
2010 int currentTrack = mlt_transition_get_b_track(tr);
2011 int currentIn = (int) mlt_transition_get_in(tr);
2012 int currentOut = (int) mlt_transition_get_out(tr);
2013 insertPos = trackTransitionStartList.value(track);
2014 if (insertPos != -1) {
2015 insertPos += offset;
2016 if (track == currentTrack && currentOut > insertPos && resource != "mix") {
2017 mlt_transition_set_in_and_out(tr, currentIn + diff, currentOut + diff);
2020 nextservice = mlt_service_producer(nextservice);
2021 if (nextservice == NULL) break;
2022 properties = MLT_SERVICE_PROPERTIES(nextservice);
2023 mlt_type = mlt_properties_get(properties, "mlt_type");
2024 resource = mlt_properties_get(properties, "mlt_service");
2027 for (int trackNb = tractor.count() - 1; trackNb >= 1; --trackNb) {
2028 Mlt::Producer trackProducer(tractor.track(trackNb));
2029 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2031 //int clipNb = trackPlaylist.count();
2032 insertPos = trackClipStartList.value(trackNb);
2033 if (insertPos != -1) {
2034 insertPos += offset;
2036 /* kDebug()<<"-------------\nTRACK "<<trackNb<<" HAS "<<clipNb<<" CLPIS";
2037 kDebug() << "INSERT SPACE AT: "<<insertPos<<", DIFF: "<<diff<<", TK: "<<trackNb;
2038 for (int i = 0; i < clipNb; i++) {
2039 kDebug()<<"CLIP "<<i<<", START: "<<trackPlaylist.clip_start(i)<<", END: "<<trackPlaylist.clip_start(i) + trackPlaylist.clip_length(i);
2040 if (trackPlaylist.is_blank(i)) kDebug()<<"++ BLANK ++ ";
2041 kDebug()<<"-------------";
2043 kDebug()<<"END-------------";*/
2046 int clipIndex = trackPlaylist.get_clip_index_at(insertPos);
2048 trackPlaylist.insert_blank(clipIndex, diff - 1);
2050 if (!trackPlaylist.is_blank(clipIndex)) {
2053 if (!trackPlaylist.is_blank(clipIndex)) {
2054 kDebug() << "//// ERROR TRYING TO DELETE SPACE FROM " << insertPos;
2056 int position = trackPlaylist.clip_start(clipIndex);
2057 int blankDuration = trackPlaylist.clip_length(clipIndex);
2058 if (diff + blankDuration == 0) {
2059 trackPlaylist.remove(clipIndex);
2060 } else trackPlaylist.remove_region(position, - diff);
2062 trackPlaylist.consolidate_blanks(0);
2065 // now move transitions
2066 mlt_service serv = m_mltProducer->parent().get_service();
2067 mlt_service nextservice = mlt_service_get_producer(serv);
2068 mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
2069 QString mlt_type = mlt_properties_get(properties, "mlt_type");
2070 QString resource = mlt_properties_get(properties, "mlt_service");
2072 while (mlt_type == "transition") {
2073 mlt_transition tr = (mlt_transition) nextservice;
2074 int currentIn = (int) mlt_transition_get_in(tr);
2075 int currentOut = (int) mlt_transition_get_out(tr);
2076 int currentTrack = mlt_transition_get_b_track(tr);
2077 insertPos = trackTransitionStartList.value(currentTrack);
2078 if (insertPos != -1) {
2079 insertPos += offset;
2080 if (currentOut > insertPos && resource != "mix") {
2081 mlt_transition_set_in_and_out(tr, currentIn + diff, currentOut + diff);
2084 nextservice = mlt_service_producer(nextservice);
2085 if (nextservice == NULL) break;
2086 properties = MLT_SERVICE_PROPERTIES(nextservice);
2087 mlt_type = mlt_properties_get(properties, "mlt_type");
2088 resource = mlt_properties_get(properties, "mlt_service");
2091 mlt_service_unlock(service.get_service());
2092 mltCheckLength(&tractor);
2093 m_mltConsumer->set("refresh", 1);
2097 void Render::mltPasteEffects(Mlt::Producer *source, Mlt::Producer *dest)
2099 if (source == dest) return;
2100 Mlt::Service sourceService(source->get_service());
2101 Mlt::Service destService(dest->get_service());
2103 // move all effects to the correct producer
2105 Mlt::Filter *filter = sourceService.filter(ct);
2107 if (filter->get_int("kdenlive_ix") != 0) {
2108 sourceService.detach(*filter);
2109 destService.attach(*filter);
2111 filter = sourceService.filter(ct);
2115 int Render::mltChangeClipSpeed(ItemInfo info, ItemInfo speedIndependantInfo, double speed, double /*oldspeed*/, int strobe, Mlt::Producer *prod)
2119 Mlt::Service service(m_mltProducer->parent().get_service());
2120 if (service.type() != tractor_type) {
2121 kWarning() << "// TRACTOR PROBLEM";
2124 //kDebug() << "Changing clip speed, set in and out: " << info.cropStart.frames(m_fps) << " to " << (info.endPos - info.startPos).frames(m_fps) - 1;
2125 Mlt::Tractor tractor(service);
2126 Mlt::Producer trackProducer(tractor.track(info.track));
2127 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2128 int startPos = info.startPos.frames(m_fps);
2129 int clipIndex = trackPlaylist.get_clip_index_at(startPos);
2130 int clipLength = trackPlaylist.clip_length(clipIndex);
2132 Mlt::Producer *original = trackPlaylist.get_clip(clipIndex);
2133 if (original == NULL) {
2136 if (!original->is_valid() || original->is_blank()) {
2141 Mlt::Producer clipparent = original->parent();
2142 if (!clipparent.is_valid() || clipparent.is_blank()) {
2148 QString serv = clipparent.get("mlt_service");
2149 QString id = clipparent.get("id");
2150 if (speed <= 0 && speed > -1) speed = 1.0;
2151 //kDebug() << "CLIP SERVICE: " << serv;
2152 if ((serv == "avformat" || serv == "avformat-novalidate") && (speed != 1.0 || strobe > 1)) {
2153 mlt_service_lock(service.get_service());
2154 QString url = QString::fromUtf8(clipparent.get("resource"));
2155 url.append('?' + QString::number(speed));
2156 if (strobe > 1) url.append("&strobe=" + QString::number(strobe));
2157 Mlt::Producer *slowprod = m_slowmotionProducers.value(url);
2158 if (!slowprod || slowprod->get_producer() == NULL) {
2159 slowprod = new Mlt::Producer(*m_mltProfile, 0, ("framebuffer:" + url).toUtf8().constData());
2160 if (strobe > 1) slowprod->set("strobe", strobe);
2161 QString producerid = "slowmotion:" + id + ':' + QString::number(speed);
2162 if (strobe > 1) producerid.append(':' + QString::number(strobe));
2163 slowprod->set("id", producerid.toUtf8().constData());
2164 // copy producer props
2165 double ar = original->parent().get_double("force_aspect_ratio");
2166 if (ar != 0.0) slowprod->set("force_aspect_ratio", ar);
2167 double fps = original->parent().get_double("force_fps");
2168 if (fps != 0.0) slowprod->set("force_fps", fps);
2169 int threads = original->parent().get_int("threads");
2170 if (threads != 0) slowprod->set("threads", threads);
2171 if (original->parent().get("force_progressive"))
2172 slowprod->set("force_progressive", original->parent().get_int("force_progressive"));
2173 if (original->parent().get("force_tff"))
2174 slowprod->set("force_tff", original->parent().get_int("force_tff"));
2175 int ix = original->parent().get_int("video_index");
2176 if (ix != 0) slowprod->set("video_index", ix);
2177 int colorspace = original->parent().get_int("force_colorspace");
2178 if (colorspace != 0) slowprod->set("force_colorspace", colorspace);
2179 int full_luma = original->parent().get_int("set.force_full_luma");
2180 if (full_luma != 0) slowprod->set("set.force_full_luma", full_luma);
2181 m_slowmotionProducers.insert(url, slowprod);
2183 Mlt::Producer *clip = trackPlaylist.replace_with_blank(clipIndex);
2184 trackPlaylist.consolidate_blanks(0);
2186 // Check that the blank space is long enough for our new duration
2187 clipIndex = trackPlaylist.get_clip_index_at(startPos);
2188 int blankEnd = trackPlaylist.clip_start(clipIndex) + trackPlaylist.clip_length(clipIndex);
2190 if (clipIndex + 1 < trackPlaylist.count() && (startPos + clipLength / speed > blankEnd)) {
2191 GenTime maxLength = GenTime(blankEnd, m_fps) - info.startPos;
2192 cut = slowprod->cut((int)(info.cropStart.frames(m_fps) / speed), (int)(info.cropStart.frames(m_fps) / speed + maxLength.frames(m_fps) - 1));
2193 } else cut = slowprod->cut((int)(info.cropStart.frames(m_fps) / speed), (int)((info.cropStart.frames(m_fps) + clipLength) / speed - 1));
2195 // move all effects to the correct producer
2196 mltPasteEffects(clip, cut);
2197 trackPlaylist.insert_at(startPos, cut, 1);
2200 clipIndex = trackPlaylist.get_clip_index_at(startPos);
2201 newLength = trackPlaylist.clip_length(clipIndex);
2202 mlt_service_unlock(service.get_service());
2203 } else if (speed == 1.0 && strobe < 2) {
2204 mlt_service_lock(service.get_service());
2206 Mlt::Producer *clip = trackPlaylist.replace_with_blank(clipIndex);
2207 trackPlaylist.consolidate_blanks(0);
2209 // Check that the blank space is long enough for our new duration
2210 clipIndex = trackPlaylist.get_clip_index_at(startPos);
2211 int blankEnd = trackPlaylist.clip_start(clipIndex) + trackPlaylist.clip_length(clipIndex);
2214 int originalStart = (int)(speedIndependantInfo.cropStart.frames(m_fps));
2215 if (clipIndex + 1 < trackPlaylist.count() && (info.startPos + speedIndependantInfo.cropDuration).frames(m_fps) > blankEnd) {
2216 GenTime maxLength = GenTime(blankEnd, m_fps) - info.startPos;
2217 cut = prod->cut(originalStart, (int)(originalStart + maxLength.frames(m_fps) - 1));
2218 } else cut = prod->cut(originalStart, (int)(originalStart + speedIndependantInfo.cropDuration.frames(m_fps)) - 1);
2220 // move all effects to the correct producer
2221 mltPasteEffects(clip, cut);
2223 trackPlaylist.insert_at(startPos, cut, 1);
2226 clipIndex = trackPlaylist.get_clip_index_at(startPos);
2227 newLength = trackPlaylist.clip_length(clipIndex);
2228 mlt_service_unlock(service.get_service());
2230 } else if (serv == "framebuffer") {
2231 mlt_service_lock(service.get_service());
2232 QString url = QString::fromUtf8(clipparent.get("resource"));
2233 url = url.section('?', 0, 0);
2234 url.append('?' + QString::number(speed));
2235 if (strobe > 1) url.append("&strobe=" + QString::number(strobe));
2236 Mlt::Producer *slowprod = m_slowmotionProducers.value(url);
2237 if (!slowprod || slowprod->get_producer() == NULL) {
2238 slowprod = new Mlt::Producer(*m_mltProfile, 0, ("framebuffer:" + url).toUtf8().constData());
2239 slowprod->set("strobe", strobe);
2240 QString producerid = "slowmotion:" + id.section(':', 1, 1) + ':' + QString::number(speed);
2241 if (strobe > 1) producerid.append(':' + QString::number(strobe));
2242 slowprod->set("id", producerid.toUtf8().constData());
2243 // copy producer props
2244 double ar = original->parent().get_double("force_aspect_ratio");
2245 if (ar != 0.0) slowprod->set("force_aspect_ratio", ar);
2246 double fps = original->parent().get_double("force_fps");
2247 if (fps != 0.0) slowprod->set("force_fps", fps);
2248 if (original->parent().get("force_progressive"))
2249 slowprod->set("force_progressive", original->parent().get_int("force_progressive"));
2250 if (original->parent().get("force_tff"))
2251 slowprod->set("force_tff", original->parent().get_int("force_tff"));
2252 int threads = original->parent().get_int("threads");
2253 if (threads != 0) slowprod->set("threads", threads);
2254 int ix = original->parent().get_int("video_index");
2255 if (ix != 0) slowprod->set("video_index", ix);
2256 int colorspace = original->parent().get_int("force_colorspace");
2257 if (colorspace != 0) slowprod->set("force_colorspace", colorspace);
2258 int full_luma = original->parent().get_int("set.force_full_luma");
2259 if (full_luma != 0) slowprod->set("set.force_full_luma", full_luma);
2260 m_slowmotionProducers.insert(url, slowprod);
2262 Mlt::Producer *clip = trackPlaylist.replace_with_blank(clipIndex);
2263 trackPlaylist.consolidate_blanks(0);
2265 GenTime duration = speedIndependantInfo.cropDuration / speed;
2266 int originalStart = (int)(speedIndependantInfo.cropStart.frames(m_fps) / speed);
2268 // Check that the blank space is long enough for our new duration
2269 clipIndex = trackPlaylist.get_clip_index_at(startPos);
2270 int blankEnd = trackPlaylist.clip_start(clipIndex) + trackPlaylist.clip_length(clipIndex);
2273 if (clipIndex + 1 < trackPlaylist.count() && (info.startPos + duration).frames(m_fps) > blankEnd) {
2274 GenTime maxLength = GenTime(blankEnd, m_fps) - info.startPos;
2275 cut = slowprod->cut(originalStart, (int)(originalStart + maxLength.frames(m_fps) - 1));
2276 } else cut = slowprod->cut(originalStart, (int)(originalStart + duration.frames(m_fps)) - 1);
2278 // move all effects to the correct producer
2279 mltPasteEffects(clip, cut);
2281 trackPlaylist.insert_at(startPos, cut, 1);
2284 clipIndex = trackPlaylist.get_clip_index_at(startPos);
2285 newLength = trackPlaylist.clip_length(clipIndex);
2287 mlt_service_unlock(service.get_service());
2290 if (clipIndex + 1 == trackPlaylist.count()) mltCheckLength(&tractor);
2291 m_isBlocked = false;
2295 bool Render::mltRemoveTrackEffect(int track, QString index, bool updateIndex)
2297 Mlt::Service service(m_mltProducer->parent().get_service());
2298 bool success = false;
2299 Mlt::Tractor tractor(service);
2300 Mlt::Producer trackProducer(tractor.track(track));
2301 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2302 Mlt::Service clipService(trackPlaylist.get_service());
2305 mlt_service_lock(service.get_service());
2307 Mlt::Filter *filter = clipService.filter(ct);
2309 if ((index == "-1" && strcmp(filter->get("kdenlive_id"), "")) || filter->get_int("kdenlive_ix") == index.toInt()) {
2310 if (clipService.detach(*filter) == 0) success = true;
2311 } else if (updateIndex) {
2312 // Adjust the other effects index
2313 if (filter->get_int("kdenlive_ix") > index.toInt()) filter->set("kdenlive_ix", filter->get_int("kdenlive_ix") - 1);
2316 filter = clipService.filter(ct);
2318 m_isBlocked = false;
2319 mlt_service_unlock(service.get_service());
2324 bool Render::mltRemoveEffect(int track, GenTime position, QString index, bool updateIndex, bool doRefresh)
2326 if (position < GenTime()) {
2327 // Remove track effect
2328 return mltRemoveTrackEffect(track, index, updateIndex);
2330 Mlt::Service service(m_mltProducer->parent().get_service());
2331 bool success = false;
2332 Mlt::Tractor tractor(service);
2333 Mlt::Producer trackProducer(tractor.track(track));
2334 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2336 int clipIndex = trackPlaylist.get_clip_index_at((int) position.frames(m_fps));
2337 Mlt::Producer *clip = trackPlaylist.get_clip(clipIndex);
2339 kDebug() << " / / / CANNOT FIND CLIP TO REMOVE EFFECT";
2343 Mlt::Service clipService(clip->get_service());
2344 int duration = clip->get_playtime();
2346 // Check if clip is visible in monitor
2347 int diff = trackPlaylist.clip_start(clipIndex) + duration - m_mltProducer->position();
2348 if (diff < 0 || diff > duration) doRefresh = false;
2352 // if (tag.startsWith("ladspa")) tag = "ladspa";
2354 mlt_service_lock(service.get_service());
2356 Mlt::Filter *filter = clipService.filter(ct);
2358 if ((index == "-1" && strcmp(filter->get("kdenlive_id"), "")) || filter->get_int("kdenlive_ix") == index.toInt()) {// && filter->get("kdenlive_id") == id) {
2359 if (clipService.detach(*filter) == 0) success = true;
2360 //kDebug()<<"Deleted filter id:"<<filter->get("kdenlive_id")<<", ix:"<<filter->get("kdenlive_ix")<<", SERVICE:"<<filter->get("mlt_service");
2361 } else if (updateIndex) {
2362 // Adjust the other effects index
2363 if (filter->get_int("kdenlive_ix") > index.toInt()) filter->set("kdenlive_ix", filter->get_int("kdenlive_ix") - 1);
2366 filter = clipService.filter(ct);
2368 m_isBlocked = false;
2369 mlt_service_unlock(service.get_service());
2370 if (doRefresh) refresh();
2374 bool Render::mltAddTrackEffect(int track, EffectsParameterList params)
2376 Mlt::Service service(m_mltProducer->parent().get_service());
2377 Mlt::Tractor tractor(service);
2378 Mlt::Producer trackProducer(tractor.track(track));
2379 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2380 Mlt::Service trackService(trackProducer.get_service()); //trackPlaylist
2381 return mltAddEffect(trackService, params, trackProducer.get_playtime() - 1, true);
2385 bool Render::mltAddEffect(int track, GenTime position, EffectsParameterList params, bool doRefresh)
2388 Mlt::Service service(m_mltProducer->parent().get_service());
2390 Mlt::Tractor tractor(service);
2391 Mlt::Producer trackProducer(tractor.track(track));
2392 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2394 int clipIndex = trackPlaylist.get_clip_index_at((int) position.frames(m_fps));
2395 Mlt::Producer *clip = trackPlaylist.get_clip(clipIndex);
2400 Mlt::Service clipService(clip->get_service());
2401 int duration = clip->get_playtime();
2403 // Check if clip is visible in monitor
2404 int diff = trackPlaylist.clip_start(clipIndex) + duration - m_mltProducer->position();
2405 if (diff < 0 || diff > duration) doRefresh = false;
2408 return mltAddEffect(clipService, params, duration, doRefresh);
2411 bool Render::mltAddEffect(Mlt::Service service, EffectsParameterList params, int duration, bool doRefresh)
2413 bool updateIndex = false;
2414 const int filter_ix = params.paramValue("kdenlive_ix").toInt();
2415 const QString region = params.paramValue("region");
2418 mlt_service_lock(service.get_service());
2420 Mlt::Filter *filter = service.filter(ct);
2422 if (filter->get_int("kdenlive_ix") == filter_ix) {
2423 // A filter at that position already existed, so we will increase all indexes later
2428 filter = service.filter(ct);
2431 if (params.paramValue("id") == "speed") {
2432 // special case, speed effect is not really inserted, we just update the other effects index (kdenlive_ix)
2434 filter = service.filter(ct);
2436 if (filter->get_int("kdenlive_ix") >= filter_ix) {
2437 if (updateIndex) filter->set("kdenlive_ix", filter->get_int("kdenlive_ix") + 1);
2440 filter = service.filter(ct);
2442 m_isBlocked = false;
2443 mlt_service_unlock(service.get_service());
2444 if (doRefresh) refresh();
2449 // temporarily remove all effects after insert point
2450 QList <Mlt::Filter *> filtersList;
2452 filter = service.filter(ct);
2454 if (filter->get_int("kdenlive_ix") >= filter_ix) {
2455 filtersList.append(filter);
2456 service.detach(*filter);
2458 filter = service.filter(ct);
2462 QString tag = params.paramValue("tag");
2463 kDebug() << " / / INSERTING EFFECT: " << tag << ", REGI: " << region;
2464 char *filterTag = qstrdup(tag.toUtf8().constData());
2465 char *filterId = qstrdup(params.paramValue("id").toUtf8().constData());
2466 QHash<QString, QString>::Iterator it;
2467 QString kfr = params.paramValue("keyframes");
2469 if (!kfr.isEmpty()) {
2470 QStringList keyFrames = kfr.split(';', QString::SkipEmptyParts);
2471 kDebug() << "// ADDING KEYFRAME EFFECT: " << params.paramValue("keyframes");
2472 char *starttag = qstrdup(params.paramValue("starttag", "start").toUtf8().constData());
2473 char *endtag = qstrdup(params.paramValue("endtag", "end").toUtf8().constData());
2474 kDebug() << "// ADDING KEYFRAME TAGS: " << starttag << ", " << endtag;
2475 //double max = params.paramValue("max").toDouble();
2476 double min = params.paramValue("min").toDouble();
2477 double factor = params.paramValue("factor", "1").toDouble();
2478 params.removeParam("starttag");
2479 params.removeParam("endtag");
2480 params.removeParam("keyframes");
2481 params.removeParam("min");
2482 params.removeParam("max");
2483 params.removeParam("factor");
2485 // Special case, only one keyframe, means we want a constant value
2486 if (keyFrames.count() == 1) {
2487 Mlt::Filter *filter = new Mlt::Filter(*m_mltProfile, filterTag);
2488 if (filter && filter->is_valid()) {
2489 filter->set("kdenlive_id", filterId);
2490 int x1 = keyFrames.at(0).section(':', 0, 0).toInt();
2491 double y1 = keyFrames.at(0).section(':', 1, 1).toDouble();
2492 for (int j = 0; j < params.count(); j++) {
2493 filter->set(params.at(j).name().toUtf8().constData(), params.at(j).value().toUtf8().constData());
2495 filter->set("in", x1);
2496 //kDebug() << "// ADDING KEYFRAME vals: " << min<<" / "<<max<<", "<<y1<<", factor: "<<factor;
2497 filter->set(starttag, QString::number((min + y1) / factor).toUtf8().data());
2498 service.attach(*filter);
2500 } else for (int i = 0; i < keyFrames.size() - 1; ++i) {
2501 Mlt::Filter *filter = new Mlt::Filter(*m_mltProfile, filterTag);
2502 if (filter && filter->is_valid()) {
2503 filter->set("kdenlive_id", filterId);
2504 int x1 = keyFrames.at(i).section(':', 0, 0).toInt() + offset;
2505 double y1 = keyFrames.at(i).section(':', 1, 1).toDouble();
2506 int x2 = keyFrames.at(i + 1).section(':', 0, 0).toInt();
2507 double y2 = keyFrames.at(i + 1).section(':', 1, 1).toDouble();
2508 if (x2 == -1) x2 = duration;
2510 for (int j = 0; j < params.count(); j++) {
2511 filter->set(params.at(j).name().toUtf8().constData(), params.at(j).value().toUtf8().constData());
2514 filter->set("in", x1);
2515 filter->set("out", x2);
2516 //kDebug() << "// ADDING KEYFRAME vals: " << min<<" / "<<max<<", "<<y1<<", factor: "<<factor;
2517 filter->set(starttag, QString::number((min + y1) / factor).toUtf8().data());
2518 filter->set(endtag, QString::number((min + y2) / factor).toUtf8().data());
2519 service.attach(*filter);
2526 Mlt::Filter *filter;
2528 if (!region.isEmpty()) {
2529 filter = new Mlt::Filter(*m_mltProfile, "region");
2530 } else filter = new Mlt::Filter(*m_mltProfile, filterTag);
2531 if (filter && filter->is_valid()) {
2532 filter->set("kdenlive_id", filterId);
2533 if (!region.isEmpty()) {
2534 filter->set("resource", region.toUtf8().constData());
2535 filter->set("kdenlive_ix", params.paramValue("kdenlive_ix").toUtf8().constData());
2536 filter->set("filter0", filterTag);
2537 prefix = "filter0.";
2538 params.removeParam("id");
2539 params.removeParam("region");
2540 params.removeParam("kdenlive_ix");
2543 kDebug() << "filter is NULL";
2544 m_isBlocked = false;
2545 mlt_service_unlock(service.get_service());
2548 params.removeParam("kdenlive_id");
2550 for (int j = 0; j < params.count(); j++) {
2551 filter->set((prefix + params.at(j).name()).toUtf8().constData(), params.at(j).value().toUtf8().constData());
2555 QString effectArgs = params.paramValue("id").section('_', 1);
2557 params.removeParam("id");
2558 params.removeParam("kdenlive_ix");
2559 params.removeParam("tag");
2560 params.removeParam("disable");
2561 params.removeParam("region");
2563 for (int j = 0; j < params.count(); j++) {
2564 effectArgs.append(' ' + params.at(j).value());
2566 //kDebug() << "SOX EFFECTS: " << effectArgs.simplified();
2567 filter->set("effect", effectArgs.simplified().toUtf8().constData());
2570 if (params.paramValue("id") == "pan_zoom") {
2571 filter->set_in_and_out(service.get_int("in"), service.get_int("out") + 1);
2574 // attach filter to the clip
2575 service.attach(*filter);
2580 // re-add following filters
2581 for (int i = 0; i < filtersList.count(); i++) {
2582 Mlt::Filter *filter = filtersList.at(i);
2584 filter->set("kdenlive_ix", filter->get_int("kdenlive_ix") + 1);
2585 service.attach(*filter);
2587 m_isBlocked = false;
2588 mlt_service_unlock(service.get_service());
2589 if (doRefresh) refresh();
2593 bool Render::mltEditTrackEffect(int track, EffectsParameterList params)
2595 Mlt::Service service(m_mltProducer->parent().get_service());
2596 Mlt::Tractor tractor(service);
2597 Mlt::Producer trackProducer(tractor.track(track));
2598 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2599 Mlt::Service clipService(trackPlaylist.get_service());
2602 QString index = params.paramValue("kdenlive_ix");
2603 QString tag = params.paramValue("tag");
2605 Mlt::Filter *filter = clipService.filter(ct);
2607 if (filter->get_int("kdenlive_ix") == index.toInt()) {
2611 filter = clipService.filter(ct);
2615 kDebug() << "WARINIG, FILTER FOR EDITING NOT FOUND, ADDING IT! " << index << ", " << tag;
2616 // filter was not found, it was probably a disabled filter, so add it to the correct place...
2618 bool success = false;//mltAddTrackEffect(track, params);
2619 m_isBlocked = false;
2623 QString ser = filter->get("mlt_service");
2624 if (ser == "region") prefix = "filter0.";
2625 mlt_service_lock(service.get_service());
2626 for (int j = 0; j < params.count(); j++) {
2627 filter->set((prefix + params.at(j).name()).toUtf8().constData(), params.at(j).value().toUtf8().constData());
2629 mlt_service_unlock(service.get_service());
2631 m_isBlocked = false;
2637 bool Render::mltEditEffect(int track, GenTime position, EffectsParameterList params)
2639 QString index = params.paramValue("kdenlive_ix");
2640 QString tag = params.paramValue("tag");
2642 if (!params.paramValue("keyframes").isEmpty() || /*it.key().startsWith("#") || */tag.startsWith("ladspa") || tag == "sox" || tag == "autotrack_rectangle" || params.hasParam("region")) {
2643 // This is a keyframe effect, to edit it, we remove it and re-add it.
2644 bool success = mltRemoveEffect(track, position, index, false);
2645 // if (!success) kDebug() << "// ERROR Removing effect : " << index;
2646 if (position < GenTime())
2647 success = mltAddTrackEffect(track, params);
2649 success = mltAddEffect(track, position, params);
2650 // if (!success) kDebug() << "// ERROR Adding effect : " << index;
2653 if (position < GenTime()) {
2654 return mltEditTrackEffect(track, params);
2657 Mlt::Service service(m_mltProducer->parent().get_service());
2658 Mlt::Tractor tractor(service);
2659 Mlt::Producer trackProducer(tractor.track(track));
2660 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2662 int clipIndex = trackPlaylist.get_clip_index_at((int) position.frames(m_fps));
2663 Mlt::Producer *clip = trackPlaylist.get_clip(clipIndex);
2665 kDebug() << "WARINIG, CANNOT FIND CLIP ON track: " << track << ", AT POS: " << position.frames(m_fps);
2669 int duration = clip->get_playtime();
2670 bool doRefresh = true;
2671 // Check if clip is visible in monitor
2672 int diff = trackPlaylist.clip_start(clipIndex) + duration - m_mltProducer->position();
2673 if (diff < 0 || diff > duration)
2678 Mlt::Filter *filter = clip->filter(ct);
2680 if (filter->get_int("kdenlive_ix") == index.toInt()) {
2684 filter = clip->filter(ct);
2688 kDebug() << "WARINIG, FILTER FOR EDITING NOT FOUND, ADDING IT! " << index << ", " << tag;
2689 // filter was not found, it was probably a disabled filter, so add it to the correct place...
2691 bool success = mltAddEffect(track, position, params);
2692 m_isBlocked = false;
2696 QString ser = filter->get("mlt_service");
2697 if (ser == "region") prefix = "filter0.";
2698 mlt_service_lock(service.get_service());
2699 for (int j = 0; j < params.count(); j++) {
2700 filter->set((prefix + params.at(j).name()).toUtf8().constData(), params.at(j).value().toUtf8().constData());
2703 if (params.paramValue("id") == "pan_zoom")
2704 filter->set_in_and_out(clip->get_in(), clip->get_out() + 1);
2707 mlt_service_unlock(service.get_service());
2709 m_isBlocked = false;
2710 if (doRefresh) refresh();
2714 void Render::mltUpdateEffectPosition(int track, GenTime position, int oldPos, int newPos)
2716 Mlt::Service service(m_mltProducer->parent().get_service());
2717 Mlt::Tractor tractor(service);
2718 Mlt::Producer trackProducer(tractor.track(track));
2719 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2721 int clipIndex = trackPlaylist.get_clip_index_at((int) position.frames(m_fps));
2722 Mlt::Producer *clip = trackPlaylist.get_clip(clipIndex);
2724 kDebug() << "WARINIG, CANNOT FIND CLIP ON track: " << track << ", AT POS: " << position.frames(m_fps);
2728 Mlt::Service clipService(clip->get_service());
2729 int duration = clip->get_playtime();
2730 bool doRefresh = true;
2731 // Check if clip is visible in monitor
2732 int diff = trackPlaylist.clip_start(clipIndex) + duration - m_mltProducer->position();
2733 if (diff < 0 || diff > duration) doRefresh = false;
2738 Mlt::Filter *filter = clipService.filter(ct);
2740 int pos = filter->get_int("kdenlive_ix");
2741 if (pos == oldPos) {
2742 filter->set("kdenlive_ix", newPos);
2744 filter = clipService.filter(ct);
2747 m_isBlocked = false;
2748 if (doRefresh) refresh();
2751 void Render::mltMoveEffect(int track, GenTime position, int oldPos, int newPos)
2753 if (position < GenTime()) {
2754 mltMoveTrackEffect(track, oldPos, newPos);
2757 Mlt::Service service(m_mltProducer->parent().get_service());
2758 Mlt::Tractor tractor(service);
2759 Mlt::Producer trackProducer(tractor.track(track));
2760 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2762 int clipIndex = trackPlaylist.get_clip_index_at((int) position.frames(m_fps));
2763 Mlt::Producer *clip = trackPlaylist.get_clip(clipIndex);
2765 kDebug() << "WARINIG, CANNOT FIND CLIP ON track: " << track << ", AT POS: " << position.frames(m_fps);
2769 Mlt::Service clipService(clip->get_service());
2770 int duration = clip->get_playtime();
2771 bool doRefresh = true;
2772 // Check if clip is visible in monitor
2773 int diff = trackPlaylist.clip_start(clipIndex) + duration - m_mltProducer->position();
2774 if (diff < 0 || diff > duration) doRefresh = false;
2779 QList <Mlt::Filter *> filtersList;
2780 Mlt::Filter *filter = clipService.filter(ct);
2782 if (newPos > oldPos) {
2784 if (!found && filter->get_int("kdenlive_ix") == oldPos) {
2785 filter->set("kdenlive_ix", newPos);
2786 filtersList.append(filter);
2787 clipService.detach(*filter);
2788 filter = clipService.filter(ct);
2789 while (filter && filter->get_int("kdenlive_ix") <= newPos) {
2790 filter->set("kdenlive_ix", filter->get_int("kdenlive_ix") - 1);
2792 filter = clipService.filter(ct);
2796 if (filter && filter->get_int("kdenlive_ix") > newPos) {
2797 filtersList.append(filter);
2798 clipService.detach(*filter);
2800 filter = clipService.filter(ct);
2804 if (filter->get_int("kdenlive_ix") == oldPos) {
2805 filter->set("kdenlive_ix", newPos);
2806 filtersList.append(filter);
2807 clipService.detach(*filter);
2809 filter = clipService.filter(ct);
2813 filter = clipService.filter(ct);
2815 int pos = filter->get_int("kdenlive_ix");
2816 if (pos >= newPos) {
2817 if (pos < oldPos) filter->set("kdenlive_ix", pos + 1);
2818 filtersList.append(filter);
2819 clipService.detach(*filter);
2821 filter = clipService.filter(ct);
2825 for (int i = 0; i < filtersList.count(); i++) {
2826 clipService.attach(*(filtersList.at(i)));
2829 m_isBlocked = false;
2830 if (doRefresh) refresh();
2833 void Render::mltMoveTrackEffect(int track, int oldPos, int newPos)
2835 Mlt::Service service(m_mltProducer->parent().get_service());
2836 Mlt::Tractor tractor(service);
2837 Mlt::Producer trackProducer(tractor.track(track));
2838 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2840 Mlt::Service clipService(trackPlaylist.get_service());
2844 QList <Mlt::Filter *> filtersList;
2845 Mlt::Filter *filter = clipService.filter(ct);
2847 if (newPos > oldPos) {
2849 if (!found && filter->get_int("kdenlive_ix") == oldPos) {
2850 filter->set("kdenlive_ix", newPos);
2851 filtersList.append(filter);
2852 clipService.detach(*filter);
2853 filter = clipService.filter(ct);
2854 while (filter && filter->get_int("kdenlive_ix") <= newPos) {
2855 filter->set("kdenlive_ix", filter->get_int("kdenlive_ix") - 1);
2857 filter = clipService.filter(ct);
2861 if (filter && filter->get_int("kdenlive_ix") > newPos) {
2862 filtersList.append(filter);
2863 clipService.detach(*filter);
2865 filter = clipService.filter(ct);
2869 if (filter->get_int("kdenlive_ix") == oldPos) {
2870 filter->set("kdenlive_ix", newPos);
2871 filtersList.append(filter);
2872 clipService.detach(*filter);
2874 filter = clipService.filter(ct);
2878 filter = clipService.filter(ct);
2880 int pos = filter->get_int("kdenlive_ix");
2881 if (pos >= newPos) {
2882 if (pos < oldPos) filter->set("kdenlive_ix", pos + 1);
2883 filtersList.append(filter);
2884 clipService.detach(*filter);
2886 filter = clipService.filter(ct);
2890 for (int i = 0; i < filtersList.count(); i++) {
2891 clipService.attach(*(filtersList.at(i)));
2893 m_isBlocked = false;
2897 bool Render::mltResizeClipEnd(ItemInfo info, GenTime clipDuration)
2900 Mlt::Service service(m_mltProducer->parent().get_service());
2901 Mlt::Tractor tractor(service);
2902 Mlt::Producer trackProducer(tractor.track(info.track));
2903 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
2905 /* // Display playlist info
2906 kDebug()<<"//////////// BEFORE RESIZE";
2907 for (int i = 0; i < trackPlaylist.count(); i++) {
2908 int blankStart = trackPlaylist.clip_start(i);
2909 int blankDuration = trackPlaylist.clip_length(i) - 1;
2911 if (trackPlaylist.is_blank(i)) blk = "(blank)";
2912 kDebug()<<"CLIP "<<i<<": ("<<blankStart<<'x'<<blankStart + blankDuration<<")"<<blk;
2915 if (trackPlaylist.is_blank_at((int) info.startPos.frames(m_fps))) {
2916 kDebug() << "//////// ERROR RSIZING BLANK CLIP!!!!!!!!!!!";
2917 m_isBlocked = false;
2920 mlt_service_lock(service.get_service());
2921 int clipIndex = trackPlaylist.get_clip_index_at((int) info.startPos.frames(m_fps));
2922 //kDebug() << "// SELECTED CLIP START: " << trackPlaylist.clip_start(clipIndex);
2923 Mlt::Producer *clip = trackPlaylist.get_clip(clipIndex);
2925 int previousStart = clip->get_in();
2926 int newDuration = (int) clipDuration.frames(m_fps) - 1;
2927 int diff = newDuration - (trackPlaylist.clip_length(clipIndex) - 1);
2929 int currentOut = newDuration + previousStart;
2930 if (currentOut > clip->get_length()) {
2931 clip->parent().set("length", currentOut + 1);
2932 clip->parent().set("out", currentOut);
2933 clip->set("length", currentOut + 1);
2936 /*if (newDuration > clip->get_out()) {
2937 clip->parent().set_in_and_out(0, newDuration + 1);
2938 clip->set_in_and_out(0, newDuration + 1);
2941 trackPlaylist.resize_clip(clipIndex, previousStart, newDuration + previousStart);
2942 trackPlaylist.consolidate_blanks(0);
2943 // skip to next clip
2945 //kDebug() << "//////// RESIZE CLIP: " << clipIndex << "( pos: " << info.startPos.frames(25) << "), DIFF: " << diff << ", CURRENT DUR: " << previousDuration << ", NEW DUR: " << newDuration << ", IX: " << clipIndex << ", MAX: " << trackPlaylist.count();
2947 // clip was made longer, trim next blank if there is one.
2948 if (clipIndex < trackPlaylist.count()) {
2949 // If this is not the last clip in playlist
2950 if (trackPlaylist.is_blank(clipIndex)) {
2951 int blankStart = trackPlaylist.clip_start(clipIndex);
2952 int blankDuration = trackPlaylist.clip_length(clipIndex);
2953 if (diff > blankDuration) {
2954 kDebug() << "// ERROR blank clip is not large enough to get back required space!!!";
2956 if (diff - blankDuration == 0) {
2957 trackPlaylist.remove(clipIndex);
2958 } else trackPlaylist.remove_region(blankStart, diff);
2960 kDebug() << "/// RESIZE ERROR, NXT CLIP IS NOT BLK: " << clipIndex;
2963 } else if (clipIndex != trackPlaylist.count()) trackPlaylist.insert_blank(clipIndex, 0 - diff - 1);
2964 trackPlaylist.consolidate_blanks(0);
2965 mlt_service_unlock(service.get_service());
2967 if (info.track != 0 && clipIndex == trackPlaylist.count()) mltCheckLength(&tractor);
2968 /*if (QString(clip->parent().get("transparency")).toInt() == 1) {
2969 //mltResizeTransparency(previousStart, previousStart, previousStart + newDuration, track, QString(clip->parent().get("id")).toInt());
2970 mltDeleteTransparency(info.startPos.frames(m_fps), info.track, QString(clip->parent().get("id")).toInt());
2971 ItemInfo transpinfo;
2972 transpinfo.startPos = info.startPos;
2973 transpinfo.endPos = info.startPos + clipDuration;
2974 transpinfo.track = info.track;
2975 mltAddClipTransparency(transpinfo, info.track - 1, QString(clip->parent().get("id")).toInt());
2977 m_isBlocked = false;
2978 m_mltConsumer->set("refresh", 1);
2982 void Render::mltChangeTrackState(int track, bool mute, bool blind)
2984 Mlt::Service service(m_mltProducer->parent().get_service());
2985 Mlt::Tractor tractor(service);
2986 Mlt::Producer trackProducer(tractor.track(track));
2988 // Make sure muting will not produce problems with our audio mixing transition,
2989 // because audio mixing is done between each track and the lowest one
2990 bool audioMixingBroken = false;
2991 if (mute && trackProducer.get_int("hide") < 2 ) {
2992 // We mute a track with sound
2993 if (track == getLowestNonMutedAudioTrack(tractor)) audioMixingBroken = true;
2995 else if (!mute && trackProducer.get_int("hide") > 1 ) {
2996 // We un-mute a previously muted track
2997 if (track < getLowestNonMutedAudioTrack(tractor)) audioMixingBroken = true;
3001 if (blind) trackProducer.set("hide", 3);
3002 else trackProducer.set("hide", 2);
3004 trackProducer.set("hide", 1);
3006 trackProducer.set("hide", 0);
3008 if (audioMixingBroken) fixAudioMixing(tractor);
3010 tractor.multitrack()->refresh();
3015 int Render::getLowestNonMutedAudioTrack(Mlt::Tractor tractor)
3017 for (int i = 1; i < tractor.count(); i++) {
3018 Mlt::Producer trackProducer(tractor.track(i));
3019 if (trackProducer.get_int("hide") < 2) return i;
3021 return tractor.count() - 1;
3024 void Render::fixAudioMixing(Mlt::Tractor tractor)
3026 // Make sure the audio mixing transitions are applied to the lowest audible (non muted) track
3027 int lowestTrack = getLowestNonMutedAudioTrack(tractor);
3029 mlt_service serv = m_mltProducer->parent().get_service();
3030 Mlt::Field *field = tractor.field();
3031 mlt_service_lock(serv);
3034 mlt_service nextservice = mlt_service_get_producer(serv);
3035 mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
3036 QString mlt_type = mlt_properties_get(properties, "mlt_type");
3037 QString resource = mlt_properties_get(properties, "mlt_service");
3039 mlt_service nextservicetodisconnect;
3040 // Delete all audio mixing transitions
3041 while (mlt_type == "transition") {
3042 if (resource == "mix") {
3043 nextservicetodisconnect = nextservice;
3044 nextservice = mlt_service_producer(nextservice);
3045 mlt_field_disconnect_service(field->get_field(), nextservicetodisconnect);
3047 else nextservice = mlt_service_producer(nextservice);
3048 if (nextservice == NULL) break;
3049 properties = MLT_SERVICE_PROPERTIES(nextservice);
3050 mlt_type = mlt_properties_get(properties, "mlt_type");
3051 resource = mlt_properties_get(properties, "mlt_service");
3054 // Re-add correct audio transitions
3055 for (int i = lowestTrack + 1; i < tractor.count(); i++) {
3056 Mlt::Transition *transition = new Mlt::Transition(*m_mltProfile, "mix");
3057 transition->set("always_active", 1);
3058 transition->set("combine", 1);
3059 transition->set("internal_added", 237);
3060 field->plant_transition(*transition, lowestTrack, i);
3062 mlt_service_unlock(serv);
3066 bool Render::mltResizeClipCrop(ItemInfo info, GenTime diff)
3068 Mlt::Service service(m_mltProducer->parent().get_service());
3069 int frameOffset = (int) diff.frames(m_fps);
3070 Mlt::Tractor tractor(service);
3071 Mlt::Producer trackProducer(tractor.track(info.track));
3072 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
3073 if (trackPlaylist.is_blank_at(info.startPos.frames(m_fps))) {
3074 kDebug() << "//////// ERROR RSIZING BLANK CLIP!!!!!!!!!!!";
3077 mlt_service_lock(service.get_service());
3078 int clipIndex = trackPlaylist.get_clip_index_at(info.startPos.frames(m_fps));
3079 Mlt::Producer *clip = trackPlaylist.get_clip(clipIndex);
3081 kDebug() << "//////// ERROR RSIZING NULL CLIP!!!!!!!!!!!";
3082 mlt_service_unlock(service.get_service());
3085 int previousStart = clip->get_in();
3086 int previousOut = clip->get_out();
3089 trackPlaylist.resize_clip(clipIndex, previousStart + frameOffset, previousOut + frameOffset);
3090 m_isBlocked = false;
3091 mlt_service_unlock(service.get_service());
3092 m_mltConsumer->set("refresh", 1);
3096 bool Render::mltResizeClipStart(ItemInfo info, GenTime diff)
3098 //kDebug() << "//////// RSIZING CLIP from: "<<info.startPos.frames(25)<<" to "<<diff.frames(25);
3099 Mlt::Service service(m_mltProducer->parent().get_service());
3100 int moveFrame = (int) diff.frames(m_fps);
3101 Mlt::Tractor tractor(service);
3102 Mlt::Producer trackProducer(tractor.track(info.track));
3103 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
3104 if (trackPlaylist.is_blank_at(info.startPos.frames(m_fps))) {
3105 kDebug() << "//////// ERROR RSIZING BLANK CLIP!!!!!!!!!!!";
3108 mlt_service_lock(service.get_service());
3109 int clipIndex = trackPlaylist.get_clip_index_at(info.startPos.frames(m_fps));
3110 Mlt::Producer *clip = trackPlaylist.get_clip(clipIndex);
3111 if (clip == NULL || clip->is_blank()) {
3112 kDebug() << "//////// ERROR RSIZING NULL CLIP!!!!!!!!!!!";
3113 mlt_service_unlock(service.get_service());
3116 int previousStart = clip->get_in();
3117 int previousOut = clip->get_out();
3120 previousStart += moveFrame;
3122 if (previousStart < 0) {
3123 // this is possible for images and color clips
3124 previousOut -= previousStart;
3128 int length = previousOut + 1;
3129 if (length > clip->get_length()) {
3130 clip->parent().set("length", length + 1);
3131 clip->parent().set("out", length);
3132 clip->set("length", length + 1);
3136 // kDebug() << "RESIZE, new start: " << previousStart << ", " << previousOut;
3137 trackPlaylist.resize_clip(clipIndex, previousStart, previousOut);
3138 if (moveFrame > 0) {
3139 trackPlaylist.insert_blank(clipIndex, moveFrame - 1);
3141 //int midpos = info.startPos.frames(m_fps) + moveFrame - 1;
3142 int blankIndex = clipIndex - 1;
3143 int blankLength = trackPlaylist.clip_length(blankIndex);
3144 // kDebug() << " + resizing blank length " << blankLength << ", SIZE DIFF: " << moveFrame;
3145 if (! trackPlaylist.is_blank(blankIndex)) {
3146 kDebug() << "WARNING, CLIP TO RESIZE IS NOT BLANK";
3148 if (blankLength + moveFrame == 0)
3149 trackPlaylist.remove(blankIndex);
3151 trackPlaylist.resize_clip(blankIndex, 0, blankLength + moveFrame - 1);
3153 trackPlaylist.consolidate_blanks(0);
3154 /*if (QString(clip->parent().get("transparency")).toInt() == 1) {
3155 //mltResizeTransparency(previousStart, (int) moveEnd.frames(m_fps), (int) (moveEnd + out - in).frames(m_fps), track, QString(clip->parent().get("id")).toInt());
3156 mltDeleteTransparency(info.startPos.frames(m_fps), info.track, QString(clip->parent().get("id")).toInt());
3157 ItemInfo transpinfo;
3158 transpinfo.startPos = info.startPos + diff;
3159 transpinfo.endPos = info.startPos + diff + (info.endPos - info.startPos);
3160 transpinfo.track = info.track;
3161 mltAddClipTransparency(transpinfo, info.track - 1, QString(clip->parent().get("id")).toInt());
3163 m_isBlocked = false;
3164 //m_mltConsumer->set("refresh", 1);
3165 mlt_service_unlock(service.get_service());
3166 m_mltConsumer->set("refresh", 1);
3170 bool Render::mltMoveClip(int startTrack, int endTrack, GenTime moveStart, GenTime moveEnd, Mlt::Producer *prod, bool overwrite, bool insert)
3172 return mltMoveClip(startTrack, endTrack, (int) moveStart.frames(m_fps), (int) moveEnd.frames(m_fps), prod, overwrite, insert);
3176 bool Render::mltUpdateClipProducer(int track, int pos, Mlt::Producer *prod)
3178 if (prod == NULL || !prod->is_valid()) {
3179 kDebug() << "// Warning, CLIP on track " << track << ", at: " << pos << " is invalid, cannot update it!!!";
3182 kDebug() << "NEW PROD ID: " << prod->get("id");
3184 kDebug() << "// TRYING TO UPDATE CLIP at: " << pos << ", TK: " << track;
3185 Mlt::Service service(m_mltProducer->parent().get_service());
3186 if (service.type() != tractor_type) {
3187 kWarning() << "// TRACTOR PROBLEM";
3190 mlt_service_lock(service.get_service());
3191 Mlt::Tractor tractor(service);
3192 Mlt::Producer trackProducer(tractor.track(track));
3193 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
3194 int clipIndex = trackPlaylist.get_clip_index_at(pos);
3195 Mlt::Producer *clipProducer = trackPlaylist.replace_with_blank(clipIndex);
3196 if (clipProducer == NULL || clipProducer->is_blank()) {
3197 kDebug() << "// ERROR UPDATING CLIP PROD";
3198 delete clipProducer;
3199 mlt_service_unlock(service.get_service());
3203 Mlt::Producer *clip = prod->cut(clipProducer->get_in(), clipProducer->get_out());
3204 if (!clip || !clip->is_valid()) {
3205 if (clip) delete clip;
3206 delete clipProducer;
3207 mlt_service_unlock(service.get_service());
3211 // move all effects to the correct producer
3212 mltPasteEffects(clipProducer, clip);
3213 trackPlaylist.insert_at(pos, clip, 1);
3215 delete clipProducer;
3216 mlt_service_unlock(service.get_service());
3221 bool Render::mltMoveClip(int startTrack, int endTrack, int moveStart, int moveEnd, Mlt::Producer *prod, bool overwrite, bool /*insert*/)
3225 Mlt::Service service(m_mltProducer->parent().get_service());
3226 if (service.type() != tractor_type) {
3227 kWarning() << "// TRACTOR PROBLEM";
3231 Mlt::Tractor tractor(service);
3232 mlt_service_lock(service.get_service());
3233 Mlt::Producer trackProducer(tractor.track(startTrack));
3234 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
3235 int clipIndex = trackPlaylist.get_clip_index_at(moveStart);
3236 kDebug() << "////// LOOKING FOR CLIP TO MOVE, INDEX: " << clipIndex;
3237 bool checkLength = false;
3238 if (endTrack == startTrack) {
3239 Mlt::Producer *clipProducer = trackPlaylist.replace_with_blank(clipIndex);
3240 if ((!overwrite && !trackPlaylist.is_blank_at(moveEnd)) || !clipProducer || !clipProducer->is_valid() || clipProducer->is_blank()) {
3241 // error, destination is not empty
3243 if (!trackPlaylist.is_blank_at(moveEnd) && clipProducer->is_valid()) trackPlaylist.insert_at(moveStart, clipProducer, 1);
3244 delete clipProducer;
3246 //int ix = trackPlaylist.get_clip_index_at(moveEnd);
3247 kDebug() << "// ERROR MOVING CLIP TO : " << moveEnd;
3248 mlt_service_unlock(service.get_service());
3252 trackPlaylist.consolidate_blanks(0);
3254 trackPlaylist.remove_region(moveEnd, clipProducer->get_playtime());
3255 int clipIndex = trackPlaylist.get_clip_index_at(moveEnd);
3256 trackPlaylist.insert_blank(clipIndex, clipProducer->get_playtime() - 1);
3258 int newIndex = trackPlaylist.insert_at(moveEnd, clipProducer, 1);
3259 trackPlaylist.consolidate_blanks(1);
3260 delete clipProducer;
3261 /*if (QString(clipProducer.parent().get("transparency")).toInt() == 1) {
3262 mltMoveTransparency(moveStart, moveEnd, startTrack, endTrack, QString(clipProducer.parent().get("id")).toInt());
3264 if (newIndex + 1 == trackPlaylist.count()) checkLength = true;
3266 //mlt_service_unlock(service.get_service());
3268 Mlt::Producer destTrackProducer(tractor.track(endTrack));
3269 Mlt::Playlist destTrackPlaylist((mlt_playlist) destTrackProducer.get_service());
3270 if (!overwrite && !destTrackPlaylist.is_blank_at(moveEnd)) {
3271 // error, destination is not empty
3272 mlt_service_unlock(service.get_service());
3276 Mlt::Producer *clipProducer = trackPlaylist.replace_with_blank(clipIndex);
3277 if (!clipProducer || clipProducer->is_blank()) {
3278 // error, destination is not empty
3279 //int ix = trackPlaylist.get_clip_index_at(moveEnd);
3280 if (clipProducer) delete clipProducer;
3281 kDebug() << "// ERROR MOVING CLIP TO : " << moveEnd;
3282 mlt_service_unlock(service.get_service());
3286 trackPlaylist.consolidate_blanks(0);
3287 destTrackPlaylist.consolidate_blanks(1);
3288 Mlt::Producer *clip;
3289 // check if we are moving a slowmotion producer
3290 QString serv = clipProducer->parent().get("mlt_service");
3291 QString currentid = clipProducer->parent().get("id");
3292 if (serv == "framebuffer" || currentid.endsWith("_video")) {
3293 clip = clipProducer;
3296 // Special case: prod is null when using placeholder clips.
3297 // in that case, use the producer existing in playlist. Note that
3298 // it will bypass the one producer per track logic and might cause
3299 // Sound cracks if clip is moved so that it overlaps another copy of itself
3300 clip = clipProducer->cut(clipProducer->get_in(), clipProducer->get_out());
3301 } else clip = prod->cut(clipProducer->get_in(), clipProducer->get_out());
3304 // move all effects to the correct producer
3305 mltPasteEffects(clipProducer, clip);
3308 destTrackPlaylist.remove_region(moveEnd, clip->get_playtime());
3309 int clipIndex = destTrackPlaylist.get_clip_index_at(moveEnd);
3310 destTrackPlaylist.insert_blank(clipIndex, clip->get_playtime() - 1);
3313 int newIndex = destTrackPlaylist.insert_at(moveEnd, clip, 1);
3315 if (clip == clipProducer) {
3320 delete clipProducer;
3322 destTrackPlaylist.consolidate_blanks(0);
3323 /*if (QString(clipProducer.parent().get("transparency")).toInt() == 1) {
3324 kDebug() << "//////// moving clip transparency";
3325 mltMoveTransparency(moveStart, moveEnd, startTrack, endTrack, QString(clipProducer.parent().get("id")).toInt());
3327 if (clipIndex > trackPlaylist.count()) checkLength = true;
3328 else if (newIndex + 1 == destTrackPlaylist.count()) checkLength = true;
3331 mlt_service_unlock(service.get_service());
3332 if (checkLength) mltCheckLength(&tractor);
3335 //m_mltConsumer->set("refresh", 1);
3340 QList <int> Render::checkTrackSequence(int track)
3343 Mlt::Service service(m_mltProducer->parent().get_service());
3344 if (service.type() != tractor_type) {
3345 kWarning() << "// TRACTOR PROBLEM";
3348 Mlt::Tractor tractor(service);
3349 mlt_service_lock(service.get_service());
3350 Mlt::Producer trackProducer(tractor.track(track));
3351 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
3352 int clipNb = trackPlaylist.count();
3353 //kDebug() << "// PARSING SCENE TRACK: " << t << ", CLIPS: " << clipNb;
3354 for (int i = 0; i < clipNb; i++) {
3355 Mlt::Producer *c = trackPlaylist.get_clip(i);
3356 int pos = trackPlaylist.clip_start(i);
3357 if (!list.contains(pos)) list.append(pos);
3358 pos += c->get_playtime();
3359 if (!list.contains(pos)) list.append(pos);
3365 bool Render::mltMoveTransition(QString type, int startTrack, int newTrack, int newTransitionTrack, GenTime oldIn, GenTime oldOut, GenTime newIn, GenTime newOut)
3367 int new_in = (int)newIn.frames(m_fps);
3368 int new_out = (int)newOut.frames(m_fps) - 1;
3369 if (new_in >= new_out) return false;
3370 int old_in = (int)oldIn.frames(m_fps);
3371 int old_out = (int)oldOut.frames(m_fps) - 1;
3373 Mlt::Service service(m_mltProducer->parent().get_service());
3374 Mlt::Tractor tractor(service);
3375 Mlt::Field *field = tractor.field();
3377 bool doRefresh = true;
3378 // Check if clip is visible in monitor
3379 int diff = old_out - m_mltProducer->position();
3380 if (diff < 0 || diff > old_out - old_in) doRefresh = false;
3382 diff = new_out - m_mltProducer->position();
3383 if (diff < 0 || diff > new_out - new_in) doRefresh = false;
3387 mlt_service_lock(service.get_service());
3389 mlt_service nextservice = mlt_service_get_producer(service.get_service());
3390 mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
3391 QString mlt_type = mlt_properties_get(properties, "mlt_type");
3392 QString resource = mlt_properties_get(properties, "mlt_service");
3393 int old_pos = (int)(old_in + old_out) / 2;
3396 while (mlt_type == "transition") {
3397 Mlt::Transition transition((mlt_transition) nextservice);
3398 int currentTrack = transition.get_b_track();
3399 int currentIn = (int) transition.get_in();
3400 int currentOut = (int) transition.get_out();
3402 if (resource == type && startTrack == currentTrack && currentIn <= old_pos && currentOut >= old_pos) {
3404 if (newTrack - startTrack != 0) {
3405 Mlt::Properties trans_props(transition.get_properties());
3406 Mlt::Transition new_transition(*m_mltProfile, transition.get("mlt_service"));
3407 Mlt::Properties new_trans_props(new_transition.get_properties());
3408 new_trans_props.inherit(trans_props);
3409 new_transition.set_in_and_out(new_in, new_out);
3410 field->disconnect_service(transition);
3411 mltPlantTransition(field, new_transition, newTransitionTrack, newTrack);
3412 //field->plant_transition(new_transition, newTransitionTrack, newTrack);
3413 } else transition.set_in_and_out(new_in, new_out);
3416 nextservice = mlt_service_producer(nextservice);
3417 if (nextservice == NULL) break;
3418 properties = MLT_SERVICE_PROPERTIES(nextservice);
3419 mlt_type = mlt_properties_get(properties, "mlt_type");
3420 resource = mlt_properties_get(properties, "mlt_service");
3422 mlt_service_unlock(service.get_service());
3424 if (doRefresh) refresh();
3425 //if (m_isBlocked == 0) m_mltConsumer->set("refresh", 1);
3430 void Render::mltPlantTransition(Mlt::Field *field, Mlt::Transition &tr, int a_track, int b_track)
3432 mlt_service nextservice = mlt_service_get_producer(field->get_service());
3433 mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
3434 QString mlt_type = mlt_properties_get(properties, "mlt_type");
3435 QString resource = mlt_properties_get(properties, "mlt_service");
3436 QList <Mlt::Transition *> trList;
3438 while (mlt_type == "transition") {
3439 Mlt::Transition transition((mlt_transition) nextservice);
3440 int aTrack = transition.get_a_track();
3441 int bTrack = transition.get_b_track();
3442 if (resource != "mix" && (aTrack < a_track || (aTrack == a_track && bTrack > b_track))) {
3443 Mlt::Properties trans_props(transition.get_properties());
3444 Mlt::Transition *cp = new Mlt::Transition(*m_mltProfile, transition.get("mlt_service"));
3445 Mlt::Properties new_trans_props(cp->get_properties());
3446 new_trans_props.inherit(trans_props);
3448 field->disconnect_service(transition);
3450 //else kDebug() << "// FOUND TRANS OK, "<<resource<< ", A_: " << aTrack << ", B_ "<<bTrack;
3452 nextservice = mlt_service_producer(nextservice);
3453 if (nextservice == NULL) break;
3454 properties = MLT_SERVICE_PROPERTIES(nextservice);
3455 mlt_type = mlt_properties_get(properties, "mlt_type");
3456 resource = mlt_properties_get(properties, "mlt_service");
3458 field->plant_transition(tr, a_track, b_track);
3460 // re-add upper transitions
3461 for (int i = trList.count() - 1; i >= 0; i--) {
3462 //kDebug()<< "REPLANT ON TK: "<<trList.at(i)->get_a_track()<<", "<<trList.at(i)->get_b_track();
3463 field->plant_transition(*trList.at(i), trList.at(i)->get_a_track(), trList.at(i)->get_b_track());
3467 void Render::mltUpdateTransition(QString oldTag, QString tag, int a_track, int b_track, GenTime in, GenTime out, QDomElement xml, bool force)
3469 if (oldTag == tag && !force) mltUpdateTransitionParams(tag, a_track, b_track, in, out, xml);
3471 kDebug()<<"// DELETING TRANS: "<<a_track<<"-"<<b_track;
3472 mltDeleteTransition(oldTag, a_track, b_track, in, out, xml, false);
3473 mltAddTransition(tag, a_track, b_track, in, out, xml, false);
3476 if (m_mltProducer->position() >= in.frames(m_fps) && m_mltProducer->position() <= out.frames(m_fps)) refresh();
3479 void Render::mltUpdateTransitionParams(QString type, int a_track, int b_track, GenTime in, GenTime out, QDomElement xml)
3481 mlt_service serv = m_mltProducer->parent().get_service();
3482 mlt_service_lock(serv);
3485 mlt_service nextservice = mlt_service_get_producer(serv);
3486 mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
3487 QString mlt_type = mlt_properties_get(properties, "mlt_type");
3488 QString resource = mlt_properties_get(properties, "mlt_service");
3489 int in_pos = (int) in.frames(m_fps);
3490 int out_pos = (int) out.frames(m_fps) - 1;
3492 while (mlt_type == "transition") {
3493 mlt_transition tr = (mlt_transition) nextservice;
3494 int currentTrack = mlt_transition_get_b_track(tr);
3495 int currentBTrack = mlt_transition_get_a_track(tr);
3496 int currentIn = (int) mlt_transition_get_in(tr);
3497 int currentOut = (int) mlt_transition_get_out(tr);
3499 // kDebug()<<"Looking for transition : " << currentIn <<'x'<<currentOut<< ", OLD oNE: "<<in_pos<<'x'<<out_pos;
3500 if (resource == type && b_track == currentTrack && currentIn == in_pos && currentOut == out_pos) {
3501 QMap<QString, QString> map = mltGetTransitionParamsFromXml(xml);
3502 QMap<QString, QString>::Iterator it;
3504 mlt_properties transproperties = MLT_TRANSITION_PROPERTIES(tr);
3506 QString currentId = mlt_properties_get(transproperties, "kdenlive_id");
3507 if (currentId != xml.attribute("id")) {
3508 // The transition ID is not the same, so reset all properties
3509 mlt_properties_set(transproperties, "kdenlive_id", xml.attribute("id").toUtf8().constData());
3510 // Cleanup previous properties
3511 QStringList permanentProps;
3512 permanentProps << "factory" << "kdenlive_id" << "mlt_service" << "mlt_type" << "in";
3513 permanentProps << "out" << "a_track" << "b_track";
3514 for (int i = 0; i < mlt_properties_count(transproperties); i++) {
3515 QString propName = mlt_properties_get_name(transproperties, i);
3516 if (!propName.startsWith('_') && ! permanentProps.contains(propName)) {
3517 mlt_properties_set(transproperties, propName.toUtf8().constData(), "");
3522 mlt_properties_set_int(transproperties, "force_track", xml.attribute("force_track").toInt());
3523 mlt_properties_set_int(transproperties, "automatic", xml.attribute("automatic", "0").toInt());
3525 if (currentBTrack != a_track) {
3526 mlt_properties_set_int(transproperties, "a_track", a_track);
3528 for (it = map.begin(); it != map.end(); ++it) {
3530 mlt_properties_set(transproperties, key.toUtf8().constData(), it.value().toUtf8().constData());
3531 //kDebug() << " ------ UPDATING TRANS PARAM: " << key.toUtf8().constData() << ": " << it.value().toUtf8().constData();
3532 //filter->set("kdenlive_id", id);
3536 nextservice = mlt_service_producer(nextservice);
3537 if (nextservice == NULL) break;
3538 properties = MLT_SERVICE_PROPERTIES(nextservice);
3539 mlt_type = mlt_properties_get(properties, "mlt_type");
3540 resource = mlt_properties_get(properties, "mlt_service");
3542 mlt_service_unlock(serv);
3545 //if (m_isBlocked == 0) m_mltConsumer->set("refresh", 1);
3548 void Render::mltDeleteTransition(QString tag, int /*a_track*/, int b_track, GenTime in, GenTime out, QDomElement /*xml*/, bool /*do_refresh*/)
3550 mlt_service serv = m_mltProducer->parent().get_service();
3552 mlt_service_lock(serv);
3554 Mlt::Service service(serv);
3555 Mlt::Tractor tractor(service);
3556 Mlt::Field *field = tractor.field();
3558 //if (do_refresh) m_mltConsumer->set("refresh", 0);
3560 mlt_service nextservice = mlt_service_get_producer(serv);
3561 mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
3562 QString mlt_type = mlt_properties_get(properties, "mlt_type");
3563 QString resource = mlt_properties_get(properties, "mlt_service");
3565 const int old_pos = (int)((in + out).frames(m_fps) / 2);
3566 kDebug() << " del trans pos: " << in.frames(25) << "-" << out.frames(25);
3568 while (mlt_type == "transition") {
3569 mlt_transition tr = (mlt_transition) nextservice;
3570 int currentTrack = mlt_transition_get_b_track(tr);
3571 int currentIn = (int) mlt_transition_get_in(tr);
3572 int currentOut = (int) mlt_transition_get_out(tr);
3573 //kDebug() << "// FOUND EXISTING TRANS, IN: " << currentIn << ", OUT: " << currentOut << ", TRACK: " << currentTrack;
3575 if (resource == tag && b_track == currentTrack && currentIn <= old_pos && currentOut >= old_pos) {
3576 mlt_field_disconnect_service(field->get_field(), nextservice);
3579 nextservice = mlt_service_producer(nextservice);
3580 if (nextservice == NULL) break;
3581 properties = MLT_SERVICE_PROPERTIES(nextservice);
3582 mlt_type = mlt_properties_get(properties, "mlt_type");
3583 resource = mlt_properties_get(properties, "mlt_service");
3585 mlt_service_unlock(serv);
3588 //if (m_isBlocked == 0) m_mltConsumer->set("refresh", 1);
3591 QMap<QString, QString> Render::mltGetTransitionParamsFromXml(QDomElement xml)
3593 QDomNodeList attribs = xml.elementsByTagName("parameter");
3594 QMap<QString, QString> map;
3595 for (int i = 0; i < attribs.count(); i++) {
3596 QDomElement e = attribs.item(i).toElement();
3597 QString name = e.attribute("name");
3598 //kDebug()<<"-- TRANSITION PARAM: "<<name<<" = "<< e.attribute("name")<<" / " << e.attribute("value");
3599 map[name] = e.attribute("default");
3600 if (!e.attribute("value").isEmpty()) {
3601 map[name] = e.attribute("value");
3603 if (e.attribute("type") != "addedgeometry" && !e.attribute("factor").isEmpty() && e.attribute("factor").toDouble() > 0) {
3604 map[name] = QString::number(map.value(name).toDouble() / e.attribute("factor").toDouble());
3605 //map[name]=map[name].replace(".",","); //FIXME how to solve locale conversion of . ,
3608 if (e.attribute("namedesc").contains(';')) {
3609 QString format = e.attribute("format");
3610 QStringList separators = format.split("%d", QString::SkipEmptyParts);
3611 QStringList values = e.attribute("value").split(QRegExp("[,:;x]"));
3613 QTextStream txtNeu(&neu);
3614 if (values.size() > 0)
3615 txtNeu << (int)values[0].toDouble();
3617 for (i = 0; i < separators.size() && i + 1 < values.size(); i++) {
3618 txtNeu << separators[i];
3619 txtNeu << (int)(values[i+1].toDouble());
3621 if (i < separators.size())
3622 txtNeu << separators[i];
3623 map[e.attribute("name")] = neu;
3630 void Render::mltAddClipTransparency(ItemInfo info, int transitiontrack, int id)
3632 kDebug() << "///////// ADDING CLIP TRANSPARENCY AT: " << info.startPos.frames(25);
3633 Mlt::Service service(m_mltProducer->parent().get_service());
3634 Mlt::Tractor tractor(service);
3635 Mlt::Field *field = tractor.field();
3637 Mlt::Transition *transition = new Mlt::Transition(*m_mltProfile, "composite");
3638 transition->set_in_and_out((int) info.startPos.frames(m_fps), (int) info.endPos.frames(m_fps) - 1);
3639 transition->set("transparency", id);
3640 transition->set("fill", 1);
3641 transition->set("internal_added", 237);
3642 field->plant_transition(*transition, transitiontrack, info.track);
3646 void Render::mltDeleteTransparency(int pos, int track, int id)
3648 Mlt::Service service(m_mltProducer->parent().get_service());
3649 Mlt::Tractor tractor(service);
3650 Mlt::Field *field = tractor.field();
3652 //if (do_refresh) m_mltConsumer->set("refresh", 0);
3653 mlt_service serv = m_mltProducer->parent().get_service();
3655 mlt_service nextservice = mlt_service_get_producer(serv);
3656 mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
3657 QString mlt_type = mlt_properties_get(properties, "mlt_type");
3658 QString resource = mlt_properties_get(properties, "mlt_service");
3660 while (mlt_type == "transition") {
3661 mlt_transition tr = (mlt_transition) nextservice;
3662 int currentTrack = mlt_transition_get_b_track(tr);
3663 int currentIn = (int) mlt_transition_get_in(tr);
3664 int currentOut = (int) mlt_transition_get_out(tr);
3665 int transitionId = QString(mlt_properties_get(properties, "transparency")).toInt();
3666 kDebug() << "// FOUND EXISTING TRANS, IN: " << currentIn << ", OUT: " << currentOut << ", TRACK: " << currentTrack;
3668 if (resource == "composite" && track == currentTrack && currentIn == pos && transitionId == id) {
3669 //kDebug() << " / / / / /DELETE TRANS DOOOMNE";
3670 mlt_field_disconnect_service(field->get_field(), nextservice);
3673 nextservice = mlt_service_producer(nextservice);
3674 if (nextservice == NULL) break;
3675 properties = MLT_SERVICE_PROPERTIES(nextservice);
3676 mlt_type = mlt_properties_get(properties, "mlt_type");
3677 resource = mlt_properties_get(properties, "mlt_service");
3679 //if (do_refresh) m_mltConsumer->set("refresh", 1);
3682 void Render::mltResizeTransparency(int oldStart, int newStart, int newEnd, int track, int id)
3684 Mlt::Service service(m_mltProducer->parent().get_service());
3685 Mlt::Tractor tractor(service);
3687 mlt_service_lock(service.get_service());
3688 m_mltConsumer->set("refresh", 0);
3691 mlt_service serv = m_mltProducer->parent().get_service();
3692 mlt_service nextservice = mlt_service_get_producer(serv);
3693 mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
3694 QString mlt_type = mlt_properties_get(properties, "mlt_type");
3695 QString resource = mlt_properties_get(properties, "mlt_service");
3696 kDebug() << "// resize transpar from: " << oldStart << ", TO: " << newStart << 'x' << newEnd << ", " << track << ", " << id;
3697 while (mlt_type == "transition") {
3698 mlt_transition tr = (mlt_transition) nextservice;
3699 int currentTrack = mlt_transition_get_b_track(tr);
3700 int currentIn = (int) mlt_transition_get_in(tr);
3701 //mlt_properties props = MLT_TRANSITION_PROPERTIES(tr);
3702 int transitionId = QString(mlt_properties_get(properties, "transparency")).toInt();
3703 kDebug() << "// resize transpar current in: " << currentIn << ", Track: " << currentTrack << ", id: " << id << 'x' << transitionId ;
3704 if (resource == "composite" && track == currentTrack && currentIn == oldStart && transitionId == id) {
3705 kDebug() << " / / / / /RESIZE TRANS TO: " << newStart << 'x' << newEnd;
3706 mlt_transition_set_in_and_out(tr, newStart, newEnd);
3709 nextservice = mlt_service_producer(nextservice);
3710 if (nextservice == NULL) break;
3711 properties = MLT_SERVICE_PROPERTIES(nextservice);
3712 mlt_type = mlt_properties_get(properties, "mlt_type");
3713 resource = mlt_properties_get(properties, "mlt_service");
3715 mlt_service_unlock(service.get_service());
3717 if (m_isBlocked == 0) m_mltConsumer->set("refresh", 1);
3721 void Render::mltMoveTransparency(int startTime, int endTime, int startTrack, int endTrack, int id)
3723 Mlt::Service service(m_mltProducer->parent().get_service());
3724 Mlt::Tractor tractor(service);
3726 mlt_service_lock(service.get_service());
3727 m_mltConsumer->set("refresh", 0);
3730 mlt_service serv = m_mltProducer->parent().get_service();
3731 mlt_service nextservice = mlt_service_get_producer(serv);
3732 mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
3733 QString mlt_type = mlt_properties_get(properties, "mlt_type");
3734 QString resource = mlt_properties_get(properties, "mlt_service");
3736 while (mlt_type == "transition") {
3737 mlt_transition tr = (mlt_transition) nextservice;
3738 int currentTrack = mlt_transition_get_b_track(tr);
3739 int currentaTrack = mlt_transition_get_a_track(tr);
3740 int currentIn = (int) mlt_transition_get_in(tr);
3741 int currentOut = (int) mlt_transition_get_out(tr);
3742 //mlt_properties properties = MLT_TRANSITION_PROPERTIES(tr);
3743 int transitionId = QString(mlt_properties_get(properties, "transparency")).toInt();
3744 //kDebug()<<" + TRANSITION "<<id<<" == "<<transitionId<<", START TMIE: "<<currentIn<<", LOOK FR: "<<startTime<<", TRACK: "<<currentTrack<<'x'<<startTrack;
3745 if (resource == "composite" && transitionId == id && startTime == currentIn && startTrack == currentTrack) {
3746 kDebug() << "//////MOVING";
3747 mlt_transition_set_in_and_out(tr, endTime, endTime + currentOut - currentIn);
3748 if (endTrack != startTrack) {
3749 mlt_properties properties = MLT_TRANSITION_PROPERTIES(tr);
3750 mlt_properties_set_int(properties, "a_track", currentaTrack + endTrack - currentTrack);
3751 mlt_properties_set_int(properties, "b_track", endTrack);
3755 nextservice = mlt_service_producer(nextservice);
3756 if (nextservice == NULL) break;
3757 properties = MLT_SERVICE_PROPERTIES(nextservice);
3758 mlt_type = mlt_properties_get(properties, "mlt_type");
3759 resource = mlt_properties_get(properties, "mlt_service");
3762 mlt_service_unlock(service.get_service());
3763 m_mltConsumer->set("refresh", 1);
3767 bool Render::mltAddTransition(QString tag, int a_track, int b_track, GenTime in, GenTime out, QDomElement xml, bool do_refresh)
3769 if (in >= out) return false;
3770 QMap<QString, QString> args = mltGetTransitionParamsFromXml(xml);
3771 Mlt::Service service(m_mltProducer->parent().get_service());
3773 Mlt::Tractor tractor(service);
3774 Mlt::Field *field = tractor.field();
3776 Mlt::Transition *transition = new Mlt::Transition(*m_mltProfile, tag.toUtf8().constData());
3777 if (out != GenTime())
3778 transition->set_in_and_out((int) in.frames(m_fps), (int) out.frames(m_fps) - 1);
3780 if (do_refresh && (m_mltProducer->position() < in.frames(m_fps) || m_mltProducer->position() > out.frames(m_fps))) do_refresh = false;
3781 QMap<QString, QString>::Iterator it;
3783 if (xml.attribute("automatic") == "1") transition->set("automatic", 1);
3784 //kDebug() << " ------ ADDING TRANSITION PARAMs: " << args.count();
3785 if (xml.hasAttribute("id"))
3786 transition->set("kdenlive_id", xml.attribute("id").toUtf8().constData());
3787 if (xml.hasAttribute("force_track"))
3788 transition->set("force_track", xml.attribute("force_track").toInt());
3790 for (it = args.begin(); it != args.end(); ++it) {
3792 if (!it.value().isEmpty())
3793 transition->set(key.toUtf8().constData(), it.value().toUtf8().constData());
3794 //kDebug() << " ------ ADDING TRANS PARAM: " << key << ": " << it.value();
3796 // attach transition
3798 mlt_service_lock(service.get_service());
3799 mltPlantTransition(field, *transition, a_track, b_track);
3800 // field->plant_transition(*transition, a_track, b_track);
3801 mlt_service_unlock(service.get_service());
3803 if (do_refresh) refresh();
3807 void Render::mltSavePlaylist()
3809 kWarning() << "// UPDATING PLAYLIST TO DISK++++++++++++++++";
3810 Mlt::Consumer fileConsumer(*m_mltProfile, "xml");
3811 fileConsumer.set("resource", "/tmp/playlist.mlt");
3813 Mlt::Service service(m_mltProducer->get_service());
3815 fileConsumer.connect(service);
3816 fileConsumer.start();
3819 const QList <Mlt::Producer *> Render::producersList()
3821 QList <Mlt::Producer *> prods;
3822 if (m_mltProducer == NULL) return prods;
3823 Mlt::Service service(m_mltProducer->parent().get_service());
3824 if (service.type() != tractor_type) return prods;
3825 Mlt::Tractor tractor(service);
3828 int trackNb = tractor.count();
3829 for (int t = 1; t < trackNb; t++) {
3830 Mlt::Producer *tt = tractor.track(t);
3831 Mlt::Producer trackProducer(tt);
3833 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
3834 int clipNb = trackPlaylist.count();
3835 for (int i = 0; i < clipNb; i++) {
3836 Mlt::Producer *c = trackPlaylist.get_clip(i);
3837 Mlt::Producer *nprod = new Mlt::Producer(c->get_parent());
3839 QString prodId = nprod->get("id");
3840 if (!prodId.startsWith("slowmotion") && !prodId.isEmpty() && !nprod->is_blank() && !ids.contains(prodId)) {
3841 ids.append(nprod->get("id"));
3842 prods.append(nprod);
3843 } else delete nprod;
3851 void Render::fillSlowMotionProducers()
3853 if (m_mltProducer == NULL) return;
3854 Mlt::Service service(m_mltProducer->parent().get_service());
3855 if (service.type() != tractor_type) return;
3857 Mlt::Tractor tractor(service);
3859 int trackNb = tractor.count();
3860 for (int t = 1; t < trackNb; t++) {
3861 Mlt::Producer *tt = tractor.track(t);
3862 Mlt::Producer trackProducer(tt);
3864 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
3865 int clipNb = trackPlaylist.count();
3866 for (int i = 0; i < clipNb; i++) {
3867 Mlt::Producer *c = trackPlaylist.get_clip(i);
3868 Mlt::Producer *nprod = new Mlt::Producer(c->get_parent());
3870 QString id = nprod->get("id");
3871 if (id.startsWith("slowmotion:") && !nprod->is_blank()) {
3872 // this is a slowmotion producer, add it to the list
3873 QString url = QString::fromUtf8(nprod->get("resource"));
3874 int strobe = nprod->get_int("strobe");
3875 if (strobe > 1) url.append("&strobe=" + QString::number(strobe));
3876 if (!m_slowmotionProducers.contains(url)) {
3877 m_slowmotionProducers.insert(url, nprod);
3879 } else delete nprod;
3886 void Render::mltInsertTrack(int ix, bool videoTrack)
3891 Mlt::Service service(m_mltProducer->parent().get_service());
3892 mlt_service_lock(service.get_service());
3893 if (service.type() != tractor_type) {
3894 kWarning() << "// TRACTOR PROBLEM";
3898 Mlt::Tractor tractor(service);
3900 Mlt::Playlist playlist;
3901 int ct = tractor.count();
3903 kDebug() << "// ERROR, TRYING TO insert TRACK " << ix << ", max: " << ct;
3909 Mlt::Producer *prodToMove = new Mlt::Producer(tractor.track(pos));
3910 tractor.set_track(playlist, pos);
3911 Mlt::Producer newProd(tractor.track(pos));
3912 if (!videoTrack) newProd.set("hide", 1);
3914 for (; pos <= ct; pos++) {
3915 Mlt::Producer *prodToMove2 = new Mlt::Producer(tractor.track(pos));
3916 tractor.set_track(*prodToMove, pos);
3917 prodToMove = prodToMove2;
3920 tractor.set_track(playlist, ix);
3921 Mlt::Producer newProd(tractor.track(ix));
3922 if (!videoTrack) newProd.set("hide", 1);
3926 mlt_service serv = m_mltProducer->parent().get_service();
3927 mlt_service nextservice = mlt_service_get_producer(serv);
3928 mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
3929 QString mlt_type = mlt_properties_get(properties, "mlt_type");
3930 QString resource = mlt_properties_get(properties, "mlt_service");
3932 while (mlt_type == "transition") {
3933 if (resource != "mix") {
3934 mlt_transition tr = (mlt_transition) nextservice;
3935 int currentTrack = mlt_transition_get_b_track(tr);
3936 int currentaTrack = mlt_transition_get_a_track(tr);
3937 mlt_properties properties = MLT_TRANSITION_PROPERTIES(tr);
3939 if (currentTrack >= ix) {
3940 mlt_properties_set_int(properties, "b_track", currentTrack + 1);
3941 mlt_properties_set_int(properties, "a_track", currentaTrack + 1);
3944 nextservice = mlt_service_producer(nextservice);
3945 if (nextservice == NULL) break;
3946 properties = MLT_SERVICE_PROPERTIES(nextservice);
3947 mlt_type = mlt_properties_get(properties, "mlt_type");
3948 resource = mlt_properties_get(properties, "mlt_service");
3951 // Add audio mix transition to last track
3952 Mlt::Field *field = tractor.field();
3953 Mlt::Transition *transition = new Mlt::Transition(*m_mltProfile, "mix");
3954 transition->set("a_track", 1);
3955 transition->set("b_track", ct);
3956 transition->set("always_active", 1);
3957 transition->set("internal_added", 237);
3958 transition->set("combine", 1);
3959 field->plant_transition(*transition, 1, ct);
3960 //mlt_service_unlock(m_mltConsumer->get_service());
3961 mlt_service_unlock(service.get_service());
3962 //tractor.multitrack()->refresh();
3963 //tractor.refresh();
3965 blockSignals(false);
3969 void Render::mltDeleteTrack(int ix)
3972 doc.setContent(sceneList(), false);
3973 int tracksCount = doc.elementsByTagName("track").count() - 1;
3974 QDomNode track = doc.elementsByTagName("track").at(ix);
3975 QDomNode tractor = doc.elementsByTagName("tractor").at(0);
3976 QDomNodeList transitions = doc.elementsByTagName("transition");
3977 for (int i = 0; i < transitions.count(); i++) {
3978 QDomElement e = transitions.at(i).toElement();
3979 QDomNodeList props = e.elementsByTagName("property");
3980 QMap <QString, QString> mappedProps;
3981 for (int j = 0; j < props.count(); j++) {
3982 QDomElement f = props.at(j).toElement();
3983 mappedProps.insert(f.attribute("name"), f.firstChild().nodeValue());
3985 if (mappedProps.value("mlt_service") == "mix" && mappedProps.value("b_track").toInt() == tracksCount) {
3986 tractor.removeChild(transitions.at(i));
3988 } else if (mappedProps.value("mlt_service") != "mix" && (mappedProps.value("b_track").toInt() >= ix || mappedProps.value("a_track").toInt() >= ix)) {
3989 // Transition needs to be moved
3990 int a_track = mappedProps.value("a_track").toInt();
3991 int b_track = mappedProps.value("b_track").toInt();
3992 if (a_track > 0 && a_track >= ix) a_track --;
3993 if (b_track == ix) {
3994 // transition was on the deleted track, so remove it
3995 tractor.removeChild(transitions.at(i));
3999 if (b_track > 0 && b_track > ix) b_track --;
4000 for (int j = 0; j < props.count(); j++) {
4001 QDomElement f = props.at(j).toElement();
4002 if (f.attribute("name") == "a_track") f.firstChild().setNodeValue(QString::number(a_track));
4003 else if (f.attribute("name") == "b_track") f.firstChild().setNodeValue(QString::number(b_track));
4008 tractor.removeChild(track);
4009 //kDebug() << "/////////// RESULT SCENE: \n" << doc.toString();
4010 setSceneList(doc.toString(), m_framePosition);
4011 emit refreshDocumentProducers(false, false);
4015 void Render::updatePreviewSettings()
4017 kDebug() << "////// RESTARTING CONSUMER";
4018 if (!m_mltConsumer || !m_mltProducer) return;
4019 if (m_mltProducer->get_playtime() == 0) return;
4020 Mlt::Service service(m_mltProducer->parent().get_service());
4021 if (service.type() != tractor_type) return;
4023 //m_mltConsumer->set("refresh", 0);
4024 if (!m_mltConsumer->is_stopped()) m_mltConsumer->stop();
4025 m_mltConsumer->purge();
4026 QString scene = sceneList();
4028 if (m_mltProducer) {
4029 pos = m_mltProducer->position();
4032 setSceneList(scene, pos);
4036 QString Render::updateSceneListFps(double current_fps, double new_fps, QString scene)
4038 // Update all frame positions to the new fps value
4039 //WARNING: there are probably some effects or other that hold a frame value
4040 // as parameter and will also need to be updated here!
4042 doc.setContent(scene);
4044 double factor = new_fps / current_fps;
4045 QDomNodeList producers = doc.elementsByTagName("producer");
4046 for (int i = 0; i < producers.count(); i++) {
4047 QDomElement prod = producers.at(i).toElement();
4048 prod.removeAttribute("in");
4049 prod.removeAttribute("out");
4051 QDomNodeList props = prod.childNodes();
4052 for (int j = 0; j < props.count(); j++) {
4053 QDomElement param = props.at(j).toElement();
4054 QString paramName = param.attribute("name");
4055 if (paramName.startsWith("meta.") || paramName == "length") {
4056 prod.removeChild(props.at(j));
4062 QDomNodeList entries = doc.elementsByTagName("entry");
4063 for (int i = 0; i < entries.count(); i++) {
4064 QDomElement entry = entries.at(i).toElement();
4065 int in = entry.attribute("in").toInt();
4066 int out = entry.attribute("out").toInt();
4067 in = factor * in + 0.5;
4068 out = factor * out + 0.5;
4069 entry.setAttribute("in", in);
4070 entry.setAttribute("out", out);
4073 QDomNodeList blanks = doc.elementsByTagName("blank");
4074 for (int i = 0; i < blanks.count(); i++) {
4075 QDomElement blank = blanks.at(i).toElement();
4076 int length = blank.attribute("length").toInt();
4077 length = factor * length + 0.5;
4078 blank.setAttribute("length", QString::number(length));
4081 QDomNodeList filters = doc.elementsByTagName("filter");
4082 for (int i = 0; i < filters.count(); i++) {
4083 QDomElement filter = filters.at(i).toElement();
4084 int in = filter.attribute("in").toInt();
4085 int out = filter.attribute("out").toInt();
4086 in = factor * in + 0.5;
4087 out = factor * out + 0.5;
4088 filter.setAttribute("in", in);
4089 filter.setAttribute("out", out);
4092 QDomNodeList transitions = doc.elementsByTagName("transition");
4093 for (int i = 0; i < transitions.count(); i++) {
4094 QDomElement transition = transitions.at(i).toElement();
4095 int in = transition.attribute("in").toInt();
4096 int out = transition.attribute("out").toInt();
4097 in = factor * in + 0.5;
4098 out = factor * out + 0.5;
4099 transition.setAttribute("in", in);
4100 transition.setAttribute("out", out);
4101 QDomNodeList props = transition.childNodes();
4102 for (int j = 0; j < props.count(); j++) {
4103 QDomElement param = props.at(j).toElement();
4104 QString paramName = param.attribute("name");
4105 if (paramName == "geometry") {
4106 QString geom = param.firstChild().nodeValue();
4107 QStringList keys = geom.split(';');
4108 QStringList newKeys;
4109 for (int k = 0; k < keys.size(); ++k) {
4110 if (keys.at(k).contains('=')) {
4111 int pos = keys.at(k).section('=', 0, 0).toInt();
4112 pos = factor * pos + 0.5;
4113 newKeys.append(QString::number(pos) + '=' + keys.at(k).section('=', 1));
4114 } else newKeys.append(keys.at(k));
4116 param.firstChild().setNodeValue(newKeys.join(";"));
4120 QDomElement tractor = doc.elementsByTagName("tractor").at(0).toElement();
4121 int out = tractor.attribute("out").toInt();
4122 out = factor * out + 0.5;
4123 tractor.setAttribute("out", out);
4124 emit durationChanged(out);
4126 //kDebug() << "///////////////////////////// " << out << " \n" << doc.toString() << "\n-------------------------";
4127 return doc.toString();
4131 void Render::sendFrameUpdate()
4133 if (m_mltProducer) {
4134 Mlt::Frame * frame = m_mltProducer->get_frame();
4135 emitFrameUpdated(*frame);
4140 Mlt::Producer* Render::getProducer()
4142 return m_mltProducer;
4146 #include "renderer.moc"