]> git.sesse.net Git - kdenlive/blob - src/stopmotion/stopmotion.cpp
Fix monitor id from last commit
[kdenlive] / src / stopmotion / stopmotion.cpp
1 /***************************************************************************
2                           stopmotion.cpp  -  description
3                              -------------------
4     begin                : Feb 28 2008
5     copyright            : (C) 2010 by Jean-Baptiste Mardelle
6     email                : jb@kdenlive.org
7  ***************************************************************************/
8
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17
18 #include "stopmotion.h"
19 #include "definitions.h"
20 #ifdef USE_BLACKMAGIC
21 #include "blackmagic/devices.h"
22 #endif
23 #ifdef USE_V4L
24 #include "v4l/v4lcapture.h"
25 #endif
26 #include "slideshowclip.h"
27 #include "profilesdialog.h"
28 #include "mltdevicecapture.h"
29 #include "recmonitor.h"
30 #include "monitormanager.h"
31 #include "ui_smconfig_ui.h"
32 #include "kdenlivesettings.h"
33
34 #include <KDebug>
35 #include <KGlobalSettings>
36 #include <KFileDialog>
37 #include <KStandardDirs>
38 #include <KMessageBox>
39 #include <kdeversion.h>
40 #include <KNotification>
41
42 #include <QtConcurrentRun>
43 #include <QInputDialog>
44 #include <QComboBox>
45 #include <QVBoxLayout>
46 #include <QTimer>
47 #include <QPainter>
48 #include <QAction>
49 #include <QWheelEvent>
50 #include <QMenu>
51
52 MyLabel::MyLabel(QWidget* parent) :
53     QLabel(parent)
54 {
55 }
56
57 void MyLabel::setImage(QImage img)
58 {
59     m_img = img;
60 }
61
62 //virtual
63 void MyLabel::wheelEvent(QWheelEvent* event)
64 {
65     if (event->delta() > 0) emit seek(true);
66     else emit seek(false);
67 }
68
69 //virtual
70 void MyLabel::mousePressEvent(QMouseEvent*)
71 {
72     emit switchToLive();
73 }
74
75 //virtual
76 void MyLabel::paintEvent(QPaintEvent* event)
77 {
78     Q_UNUSED(event);
79
80     QRect r(0, 0, width(), height());
81     QPainter p(this);
82     p.fillRect(r, QColor(KdenliveSettings::window_background()));
83     double aspect_ratio = (double) m_img.width() / m_img.height();
84     int pictureHeight = height();
85     int pictureWidth = width();
86     int calculatedWidth = aspect_ratio * height();
87     if (calculatedWidth > width()) pictureHeight = width() / aspect_ratio;
88     else {
89         int calculatedHeight = width() / aspect_ratio;
90         if (calculatedHeight > height()) pictureWidth = height() * aspect_ratio;
91     }
92     p.drawImage(QRect((width() - pictureWidth) / 2, (height() - pictureHeight) / 2, pictureWidth, pictureHeight), m_img, QRect(0, 0, m_img.width(), m_img.height()));
93     p.end();
94 }
95
96
97 StopmotionMonitor::StopmotionMonitor(QWidget *parent) :
98     AbstractMonitor(parent),
99     m_captureDevice(NULL)
100 {
101 }
102
103 StopmotionMonitor::~StopmotionMonitor()
104 {
105 }
106
107 void StopmotionMonitor::setRender(MltDeviceCapture *render)
108 {
109     m_captureDevice = render;
110 }
111
112 AbstractRender *StopmotionMonitor::abstractRender()
113 {
114     return m_captureDevice;
115 }
116
117 const QString StopmotionMonitor::name() const
118 {
119     return Kdenlive::stopmotionMonitor;
120 }
121
122
123 void StopmotionMonitor::stop()
124 {
125     if (m_captureDevice) m_captureDevice->stop();
126     emit stopCapture();
127 }
128
129 void StopmotionMonitor::start()
130 {
131 }
132
133 StopmotionWidget::StopmotionWidget(MonitorManager *manager, KUrl projectFolder, QList< QAction* > actions, QWidget* parent) :
134     QDialog(parent)
135     , Ui::Stopmotion_UI()
136     , m_projectFolder(projectFolder)
137     , m_captureDevice(NULL)
138     , m_sequenceFrame(0)
139     , m_animatedIndex(-1)
140     , m_animate(false)
141     , m_manager(manager)
142     , m_monitor(new StopmotionMonitor(this))
143 {
144     //setAttribute(Qt::WA_DeleteOnClose);
145     //HACK: the monitor widget is hidden, it is just used to control the capturedevice from monitormanager
146     m_monitor->setHidden(true);
147     connect(m_monitor, SIGNAL(stopCapture()), this, SLOT(slotStopCapture()));
148     m_manager->appendMonitor(m_monitor);
149     QAction* analyse = new QAction(i18n("Send frames to color scopes"), this);
150     analyse->setCheckable(true);
151     analyse->setChecked(KdenliveSettings::analyse_stopmotion());
152     connect(analyse, SIGNAL(triggered(bool)), this, SLOT(slotSwitchAnalyse(bool)));
153
154     QAction* mirror = new QAction(i18n("Mirror display"), this);
155     mirror->setCheckable(true);
156     //mirror->setChecked(KdenliveSettings::analyse_stopmotion());
157     connect(mirror, SIGNAL(triggered(bool)), this, SLOT(slotSwitchMirror(bool)));
158
159     addActions(actions);
160     setupUi(this);
161     setWindowTitle(i18n("Stop Motion Capture"));
162     setFont(KGlobalSettings::toolBarFont());
163
164     live_button->setIcon(KIcon("camera-photo"));
165
166     m_captureAction = actions.at(0);
167     connect(m_captureAction, SIGNAL(triggered()), this, SLOT(slotCaptureFrame()));
168     m_captureAction->setCheckable(true);
169     m_captureAction->setChecked(false);
170     capture_button->setDefaultAction(m_captureAction);
171
172     connect(actions.at(1), SIGNAL(triggered()), this, SLOT(slotSwitchLive()));
173
174     QAction *intervalCapture = new QAction(i18n("Interval capture"), this);
175     intervalCapture->setIcon(KIcon("chronometer"));
176     intervalCapture->setCheckable(true);
177     intervalCapture->setChecked(false);
178     capture_interval->setDefaultAction(intervalCapture);
179
180     preview_button->setIcon(KIcon("media-playback-start"));
181     capture_button->setEnabled(false);
182
183
184     // Build config menu
185     QMenu* confMenu = new QMenu;
186     m_showOverlay = actions.at(2);
187     connect(m_showOverlay, SIGNAL(triggered(bool)), this, SLOT(slotShowOverlay(bool)));
188     overlay_button->setDefaultAction(m_showOverlay);
189     //confMenu->addAction(m_showOverlay);
190
191     m_effectIndex = KdenliveSettings::stopmotioneffect();
192     QMenu* effectsMenu = new QMenu(i18n("Overlay effect"));
193     QActionGroup* effectGroup = new QActionGroup(this);
194     QAction* noEffect = new QAction(i18n("No Effect"), effectGroup);
195     noEffect->setData(0);
196     QAction* contrastEffect = new QAction(i18n("Contrast"), effectGroup);
197     contrastEffect->setData(1);
198     QAction* edgeEffect = new QAction(i18n("Edge detect"), effectGroup);
199     edgeEffect->setData(2);
200     QAction* brightEffect = new QAction(i18n("Brighten"), effectGroup);
201     brightEffect->setData(3);
202     QAction* invertEffect = new QAction(i18n("Invert"), effectGroup);
203     invertEffect->setData(4);
204     QAction* thresEffect = new QAction(i18n("Threshold"), effectGroup);
205     thresEffect->setData(5);
206
207     effectsMenu->addAction(noEffect);
208     effectsMenu->addAction(contrastEffect);
209     effectsMenu->addAction(edgeEffect);
210     effectsMenu->addAction(brightEffect);
211     effectsMenu->addAction(invertEffect);
212     effectsMenu->addAction(thresEffect);
213     QList <QAction*> list = effectsMenu->actions();
214     for (int i = 0; i < list.count(); i++) {
215         list.at(i)->setCheckable(true);
216         if (list.at(i)->data().toInt() == m_effectIndex) {
217             list.at(i)->setChecked(true);
218         }
219     }
220     connect(effectsMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotUpdateOverlayEffect(QAction*)));
221     confMenu->addMenu(effectsMenu);
222
223     QAction* showThumbs = new QAction(KIcon("image-x-generic"), i18n("Show sequence thumbnails"), this);
224     showThumbs->setCheckable(true);
225     showThumbs->setChecked(KdenliveSettings::showstopmotionthumbs());
226     connect(showThumbs, SIGNAL(triggered(bool)), this, SLOT(slotShowThumbs(bool)));
227
228     QAction* removeCurrent = new QAction(KIcon("edit-delete"), i18n("Delete current frame"), this);
229     removeCurrent->setShortcut(Qt::Key_Delete);
230     connect(removeCurrent, SIGNAL(triggered()), this, SLOT(slotRemoveFrame()));
231
232     QAction* conf = new QAction(KIcon("configure"), i18n("Configure"), this);
233     connect(conf, SIGNAL(triggered()), this, SLOT(slotConfigure()));
234
235     confMenu->addAction(showThumbs);
236     confMenu->addAction(removeCurrent);
237     confMenu->addAction(analyse);
238     confMenu->addAction(mirror);
239     confMenu->addAction(conf);
240     config_button->setIcon(KIcon("configure"));
241     config_button->setMenu(confMenu);
242
243     connect(sequence_name, SIGNAL(textChanged(const QString&)), this, SLOT(sequenceNameChanged(const QString&)));
244     connect(sequence_name, SIGNAL(currentIndexChanged(int)), live_button, SLOT(setFocus()));
245
246     // Video widget holder
247     QVBoxLayout *layout = new QVBoxLayout;
248     layout->setContentsMargins(0, 0, 0, 0);
249     layout->setSpacing(0);
250     m_videoBox = new VideoPreviewContainer();
251     m_videoBox->setContentsMargins(0, 0, 0, 0);
252     m_videoBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
253     //m_videoBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
254     m_videoBox->setLineWidth(4);
255     layout->addWidget(m_videoBox);
256
257 #ifdef USE_BLACKMAGIC
258     if (BMInterface::getBlackMagicDeviceList(capture_device)) {
259         // Found a BlackMagic device
260     }
261 #endif
262     if (QFile::exists(KdenliveSettings::video4vdevice())) {
263 #ifdef USE_V4L
264         // Video 4 Linux device detection
265         for (int i = 0; i < 10; i++) {
266             QString path = "/dev/video" + QString::number(i);
267             if (QFile::exists(path)) {
268                 QStringList deviceInfo = V4lCaptureHandler::getDeviceName(path);
269                 if (!deviceInfo.isEmpty()) {
270                     capture_device->addItem(deviceInfo.at(0), "v4l");
271                     capture_device->setItemData(capture_device->count() - 1, path, Qt::UserRole + 1);
272                     capture_device->setItemData(capture_device->count() - 1, deviceInfo.at(1), Qt::UserRole + 2);
273                     if (path == KdenliveSettings::video4vdevice()) capture_device->setCurrentIndex(capture_device->count() - 1);
274                 }
275             }
276         }
277 #endif /* USE_V4L */
278     }
279
280     connect(capture_device, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateDeviceHandler()));
281     /*if (m_bmCapture) {
282         connect(m_bmCapture, SIGNAL(frameSaved(const QString &)), this, SLOT(slotNewThumb(const QString &)));
283         connect(m_bmCapture, SIGNAL(gotFrame(QImage)), this, SIGNAL(gotFrame(QImage)));
284     } else live_button->setEnabled(false);*/
285
286     m_frame_preview = new MyLabel(this);
287     connect(m_frame_preview, SIGNAL(seek(bool)), this, SLOT(slotSeekFrame(bool)));
288     connect(m_frame_preview, SIGNAL(switchToLive()), this, SLOT(slotSwitchLive()));
289     layout->addWidget(m_frame_preview);
290     m_frame_preview->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
291     video_preview->setLayout(layout);
292
293     //kDebug()<<video_preview->winId();
294
295     QString profilePath;
296     // Create MLT producer data
297     if (capture_device->itemData(capture_device->currentIndex()) == "v4l") {
298         // Capture using a video4linux device
299         profilePath = KStandardDirs::locateLocal("appdata", "profiles/video4linux");
300     }
301     else {
302         // Decklink capture
303         profilePath = KdenliveSettings::current_profile();
304     }
305
306     m_captureDevice = new MltDeviceCapture(profilePath, m_videoBox, this);
307     m_captureDevice->sendFrameForAnalysis = KdenliveSettings::analyse_stopmotion();
308     m_monitor->setRender(m_captureDevice);
309     connect(m_captureDevice, SIGNAL(frameSaved(const QString &)), this, SLOT(slotNewThumb(const QString &)));
310
311     live_button->setChecked(false);
312     button_addsequence->setEnabled(false);
313     connect(live_button, SIGNAL(toggled(bool)), this, SLOT(slotLive(bool)));
314     connect(button_addsequence, SIGNAL(clicked(bool)), this, SLOT(slotAddSequence()));
315     connect(preview_button, SIGNAL(clicked(bool)), this, SLOT(slotPlayPreview(bool)));
316     connect(frame_list, SIGNAL(currentRowChanged(int)), this, SLOT(slotShowSelectedFrame()));
317     connect(frame_list, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(slotShowSelectedFrame()));
318     connect(this, SIGNAL(doCreateThumbs(QImage, int)), this, SLOT(slotCreateThumbs(QImage, int)));
319
320     frame_list->addAction(removeCurrent);
321     frame_list->setContextMenuPolicy(Qt::ActionsContextMenu);
322     frame_list->setHidden(!KdenliveSettings::showstopmotionthumbs());
323     parseExistingSequences();
324     QTimer::singleShot(500, this, SLOT(slotLive()));
325     connect(&m_intervalTimer, SIGNAL(timeout()), this, SLOT(slotCaptureFrame()));
326     m_intervalTimer.setSingleShot(true);
327     m_intervalTimer.setInterval(KdenliveSettings::captureinterval() * 1000);
328 }
329
330 StopmotionWidget::~StopmotionWidget()
331 {
332     m_manager->removeMonitor(m_monitor);
333     if (m_captureDevice) {
334         m_captureDevice->stop();
335         delete m_captureDevice;
336         m_captureDevice = NULL;
337     }
338
339     delete m_monitor;
340 }
341
342 void StopmotionWidget::slotUpdateOverlayEffect(QAction* act)
343 {
344     if (act) m_effectIndex = act->data().toInt();
345     KdenliveSettings::setStopmotioneffect(m_effectIndex);
346     slotUpdateOverlay();
347 }
348
349 void StopmotionWidget::closeEvent(QCloseEvent* e)
350 {
351     slotLive(false);
352     QDialog::closeEvent(e);
353 }
354
355 void StopmotionWidget::slotConfigure()
356 {
357     QDialog d;
358     Ui::SmConfig_UI ui;
359     ui.setupUi(&d);
360     d.setWindowTitle(i18n("Configure Stop Motion"));
361     ui.sm_interval->setValue(KdenliveSettings::captureinterval());
362     ui.sm_interval->setSuffix(ki18np(" second", " seconds"));
363     ui.sm_notifytime->setSuffix(ki18np(" second", " seconds"));
364     ui.sm_notifytime->setValue(KdenliveSettings::sm_notifytime());
365     connect(ui.sm_prenotify, SIGNAL(toggled(bool)), ui.sm_notifytime, SLOT(setEnabled(bool)));
366     ui.sm_prenotify->setChecked(KdenliveSettings::sm_prenotify());
367     ui.sm_loop->setChecked(KdenliveSettings::sm_loop());
368     ui.sm_framesplayback->setValue(KdenliveSettings::sm_framesplayback());
369
370     if (d.exec() == QDialog::Accepted) {
371         KdenliveSettings::setSm_loop(ui.sm_loop->isChecked());
372         KdenliveSettings::setCaptureinterval(ui.sm_interval->value());
373         KdenliveSettings::setSm_framesplayback(ui.sm_framesplayback->value());
374         KdenliveSettings::setSm_notifytime(ui.sm_notifytime->value());
375         KdenliveSettings::setSm_prenotify(ui.sm_prenotify->isChecked());
376         m_intervalTimer.setInterval(KdenliveSettings::captureinterval() * 1000);
377     }
378 }
379
380 void StopmotionWidget::slotShowThumbs(bool show)
381 {
382     KdenliveSettings::setShowstopmotionthumbs(show);
383     if (show) {
384         frame_list->clear();
385         sequenceNameChanged(sequence_name->currentText());
386     } else {
387         m_filesList.clear();
388         frame_list->clear();
389     }
390     frame_list->setHidden(!show);
391 }
392
393
394 void StopmotionWidget::slotUpdateDeviceHandler()
395 {
396     slotLive(false);
397     delete m_captureDevice;
398     m_captureDevice = NULL;
399     /*QString data = capture_device->itemData(capture_device->currentIndex()).toString();
400     slotLive(false);
401     if (m_bmCapture) {
402         delete m_bmCapture;
403     }
404     m_layout->removeWidget(m_frame_preview);
405     if (data == "v4l") {
406 #ifdef USE_V4L
407         m_bmCapture = new V4lCaptureHandler(m_layout);
408         m_bmCapture->setDevice(capture_device->itemData(capture_device->currentIndex(), Qt::UserRole + 1).toString(), capture_device->itemData(capture_device->currentIndex(), Qt::UserRole + 2).toString());
409 #endif
410     } else {
411         m_bmCapture = new BmdCaptureHandler(m_layout);
412         if (m_bmCapture) connect(m_bmCapture, SIGNAL(gotMessage(const QString&)), this, SLOT(slotGotHDMIMessage(const QString&)));
413     }
414     live_button->setEnabled(m_bmCapture != NULL);
415     m_layout->addWidget(m_frame_preview);*/
416 }
417
418 void StopmotionWidget::slotGotHDMIMessage(const QString& message)
419 {
420     log_box->insertItem(0, message);
421 }
422
423 void StopmotionWidget::parseExistingSequences()
424 {
425     sequence_name->clear();
426     sequence_name->addItem(QString());
427     QDir dir(m_projectFolder.path());
428     QStringList filters;
429     filters << "*_0000.png";
430     //dir.setNameFilters(filters);
431     QStringList sequences = dir.entryList(filters, QDir::Files, QDir::Name);
432     //kDebug()<<"PF: "<<<<", sm: "<<sequences;
433     foreach(QString sequencename, sequences) {
434         sequence_name->addItem(sequencename.section("_", 0, -2));
435     }
436 }
437
438 void StopmotionWidget::slotSwitchLive()
439 {
440     setUpdatesEnabled(false);
441     slotLive(!live_button->isChecked());
442     /*if (m_frame_preview->isHidden()) {
443         //if (m_bmCapture) m_bmCapture->hidePreview(true);
444         m_videoBox->setHidden(true);
445         m_frame_preview->setHidden(false);
446     } else {
447         m_frame_preview->setHidden(true);
448         //if (m_bmCapture) m_bmCapture->hidePreview(false);
449         m_videoBox->setHidden(false);
450         capture_button->setEnabled(true);
451     }*/
452     setUpdatesEnabled(true);
453 }
454
455 void StopmotionWidget::slotStopCapture()
456 {
457     slotLive(false);
458 }
459
460 void StopmotionWidget::slotLive(bool isOn)
461 {
462     live_button->blockSignals(true);
463     capture_button->setEnabled(false);
464     if (isOn) {
465         m_frame_preview->setHidden(true);
466         m_videoBox->setHidden(false);
467         QLocale locale;
468
469         MltVideoProfile profile;
470         QString resource;
471         QString service;
472         QString profilePath;
473         // Create MLT producer data
474         if (capture_device->itemData(capture_device->currentIndex()) == "v4l") {
475             // Capture using a video4linux device
476             profilePath = KStandardDirs::locateLocal("appdata", "profiles/video4linux");
477             profile = ProfilesDialog::getVideoProfile(profilePath);
478             service = "avformat-novalidate";
479             QString devicePath = capture_device->itemData(capture_device->currentIndex(), Qt::UserRole + 1).toString();
480             resource = QString("video4linux2:%1?width:%2&amp;height:%3&amp;frame_rate:%4").arg(devicePath).arg(profile.width).arg(profile.height).arg((double) profile.frame_rate_num / profile.frame_rate_den);
481         }
482         else {
483             // Decklink capture
484             profilePath = KdenliveSettings::current_profile();
485             profile = ProfilesDialog::getVideoProfile(profilePath);
486             service = "decklink";
487             resource = capture_device->itemData(capture_device->currentIndex(), Qt::UserRole + 1).toString();
488         }
489
490         if (m_captureDevice == NULL) {
491             m_captureDevice = new MltDeviceCapture(profilePath, m_videoBox, this);
492             m_captureDevice->sendFrameForAnalysis = KdenliveSettings::analyse_stopmotion();
493             m_monitor->setRender(m_captureDevice);
494             connect(m_captureDevice, SIGNAL(frameSaved(const QString &)), this, SLOT(slotNewThumb(const QString &)));
495         }
496
497         m_manager->activateMonitor(Kdenlive::stopmotionMonitor);
498         QString producer = createProducer(profile, service, resource);
499         if (m_captureDevice->slotStartPreview(producer, true)) {
500             if (m_showOverlay->isChecked()) {
501                 reloadOverlay();
502                 slotUpdateOverlay();
503             }
504             capture_button->setEnabled(true);
505             live_button->setChecked(true);
506             log_box->insertItem(-1, i18n("Playing %1x%2 (%3 fps)", profile.width, profile.height, locale.toString((double)profile.frame_rate_num/profile.frame_rate_den).rightJustified(2, '0')));
507             log_box->setCurrentIndex(0);
508         }
509         else {
510             kDebug()<<"// problem starting stopmo";
511             log_box->insertItem(-1, i18n("Failed to start device"));
512             log_box->setCurrentIndex(0);
513         }
514     }
515     else {
516         m_frame_preview->setHidden(false);
517         live_button->setChecked(false);
518         if (m_captureDevice) {
519             m_captureDevice->stop();
520             m_videoBox->setHidden(true);
521             log_box->insertItem(-1, i18n("Stopped"));
522             log_box->setCurrentIndex(0);
523             //delete m_captureDevice;
524             //m_captureDevice = NULL;
525         }
526     }
527
528     /*
529     if (isOn && m_bmCapture) {
530         //m_frame_preview->setImage(QImage());
531         m_frame_preview->setHidden(true);
532         m_bmCapture->startPreview(KdenliveSettings::hdmi_capturedevice(), KdenliveSettings::hdmi_capturemode(), false);
533         capture_button->setEnabled(true);
534         live_button->setChecked(true);
535     } else {
536         if (m_bmCapture) m_bmCapture->stopPreview();
537         m_frame_preview->setHidden(false);
538         capture_button->setEnabled(false);
539         live_button->setChecked(false);
540     }*/
541     live_button->blockSignals(false);
542 }
543
544 void StopmotionWidget::slotShowOverlay(bool isOn)
545 {
546     if (isOn) {
547         // Overlay last frame of the sequence
548         reloadOverlay();
549         slotUpdateOverlay();
550     }
551     else {
552         // Remove overlay
553         m_captureDevice->setOverlay(QString());
554     }
555 }
556
557 void StopmotionWidget::reloadOverlay()
558 {
559     QString path = getPathForFrame(m_sequenceFrame - 1);
560     if (!QFile::exists(path)) {
561         log_box->insertItem(-1, i18n("No previous frame found"));
562         log_box->setCurrentIndex(0);
563         return;
564     }
565     m_captureDevice->setOverlay(path);
566 }
567
568 void StopmotionWidget::slotUpdateOverlay()
569 {
570     if (m_captureDevice == NULL) return;
571
572     QString tag;
573     QStringList params;
574
575     switch (m_effectIndex) {
576     case 1:
577         tag = "frei0r.contrast0r";
578         params << "Contrast=1.2";
579         break;
580     case 2:
581         tag = "charcoal";
582         params << "x_scatter=4" << "y_scatter=4" << "scale=1" << "mix=0";
583         break;
584     case 3:
585         tag = "frei0r.brightness";
586         params << "Brightness=0.7";
587         break;
588     case 4:
589         tag = "invert";
590         break;
591     case 5:
592         tag = "threshold";
593         params << "midpoint=125";
594         break;
595     default:
596         break;
597
598     }
599     m_captureDevice->setOverlayEffect(tag, params);
600 }
601
602 void StopmotionWidget::sequenceNameChanged(const QString& name)
603 {
604     // Get rid of frames from previous sequence
605     disconnect(this, SIGNAL(doCreateThumbs(QImage, int)), this, SLOT(slotCreateThumbs(QImage, int)));
606     m_filesList.clear();
607     m_future.waitForFinished();
608     frame_list->clear();
609     if (name.isEmpty()) {
610         button_addsequence->setEnabled(false);
611     } else {
612         // Check if we are editing an existing sequence
613         QString pattern = SlideshowClip::selectedPath(getPathForFrame(0, sequence_name->currentText()), false, QString(), &m_filesList);
614         m_sequenceFrame = m_filesList.isEmpty() ? 0 : SlideshowClip::getFrameNumberFromPath(m_filesList.last()) + 1;
615         if (!m_filesList.isEmpty()) {
616             m_sequenceName = sequence_name->currentText();
617             connect(this, SIGNAL(doCreateThumbs(QImage, int)), this, SLOT(slotCreateThumbs(QImage, int)));
618             m_future = QtConcurrent::run(this, &StopmotionWidget::slotPrepareThumbs);
619             button_addsequence->setEnabled(true);
620         } else {
621             // new sequence
622             connect(this, SIGNAL(doCreateThumbs(QImage, int)), this, SLOT(slotCreateThumbs(QImage, int)));
623             button_addsequence->setEnabled(false);
624         }
625         capture_button->setEnabled(live_button->isChecked());
626     }
627 }
628
629 void StopmotionWidget::slotCaptureFrame()
630 {
631     if (m_captureDevice == NULL) return;
632     if (sequence_name->currentText().isEmpty()) {
633         QString seqName = QInputDialog::getText(this, i18n("Create New Sequence"), i18n("Enter sequence name"));
634         if (seqName.isEmpty()) {
635             m_captureAction->setChecked(false);
636             return;
637         }
638         sequence_name->blockSignals(true);
639         sequence_name->setItemText(sequence_name->currentIndex(), seqName);
640         sequence_name->blockSignals(false);
641     }
642     if (m_sequenceName != sequence_name->currentText()) {
643         m_sequenceName = sequence_name->currentText();
644         m_sequenceFrame = 0;
645     }
646     //capture_button->setEnabled(false);
647     if (m_intervalTimer.isActive()) {
648         // stop interval capture
649         m_intervalTimer.stop();
650         return;
651     }
652     QString currentPath = getPathForFrame(m_sequenceFrame);
653     m_captureDevice->captureFrame(currentPath);
654     KNotification::event("FrameCaptured", i18n("Frame Captured"), QPixmap(), this);
655     m_sequenceFrame++;
656     button_addsequence->setEnabled(true);
657     if (capture_interval->isChecked()) {
658         if (KdenliveSettings::sm_prenotify()) QTimer::singleShot((KdenliveSettings::captureinterval() - KdenliveSettings::sm_notifytime()) * 1000, this, SLOT(slotPreNotify()));
659         m_intervalTimer.start();
660     }
661     else m_captureAction->setChecked(false);
662 }
663
664 void StopmotionWidget::slotPreNotify()
665 {
666     if (m_captureAction->isChecked()) KNotification::event("ReadyToCapture", i18n("Going to Capture Frame"), QPixmap(), this);
667 }
668
669
670 void StopmotionWidget::slotNewThumb(const QString &path)
671 {
672     if (!KdenliveSettings::showstopmotionthumbs()) return;
673     m_filesList.append(path);
674     if (m_showOverlay->isChecked()) reloadOverlay();
675     if (!m_future.isRunning()) m_future = QtConcurrent::run(this, &StopmotionWidget::slotPrepareThumbs);
676
677 }
678
679 void StopmotionWidget::slotPrepareThumbs()
680 {
681     if (m_filesList.isEmpty()) return;
682     QString path = m_filesList.takeFirst();
683     emit doCreateThumbs(QImage(path), SlideshowClip::getFrameNumberFromPath(path));
684
685 }
686
687 void StopmotionWidget::slotCreateThumbs(QImage img, int ix)
688 {
689     if (img.isNull()) {
690         m_future = QtConcurrent::run(this, &StopmotionWidget::slotPrepareThumbs);
691         return;
692     }
693     int height = 90;
694     int width = height * img.width() / img.height();
695     frame_list->setIconSize(QSize(width, height));
696     QPixmap pix = QPixmap::fromImage(img).scaled(width, height);
697     QString nb = QString::number(ix);
698     QPainter p(&pix);
699     QFontInfo finfo(font());
700     p.fillRect(0, 0, finfo.pixelSize() * nb.count() + 6, finfo.pixelSize() + 6, QColor(80, 80, 80, 150));
701     p.setPen(Qt::white);
702     p.drawText(QPoint(3, finfo.pixelSize() + 3), nb);
703     p.end();
704     QIcon icon(pix);
705     QListWidgetItem* item = new QListWidgetItem(icon, QString(), frame_list);
706     item->setToolTip(getPathForFrame(ix, sequence_name->currentText()));
707     item->setData(Qt::UserRole, ix);
708     frame_list->blockSignals(true);
709     frame_list->setCurrentItem(item);
710     frame_list->blockSignals(false);
711     m_future = QtConcurrent::run(this, &StopmotionWidget::slotPrepareThumbs);
712 }
713
714 QString StopmotionWidget::getPathForFrame(int ix, QString seqName)
715 {
716     if (seqName.isEmpty()) seqName = m_sequenceName;
717     return m_projectFolder.path(KUrl::AddTrailingSlash) + seqName + "_" + QString::number(ix).rightJustified(4, '0', false) + ".png";
718 }
719
720 void StopmotionWidget::slotShowFrame(const QString& path)
721 {
722     //slotLive(false);
723     QImage img(path);
724     capture_button->setEnabled(false);
725     slotLive(false);
726     if (!img.isNull()) {
727         //m_videoBox->setHidden(true);
728
729         m_frame_preview->setImage(img);
730         m_frame_preview->setHidden(false);
731         m_frame_preview->update();
732     }
733 }
734
735 void StopmotionWidget::slotShowSelectedFrame()
736 {
737     QListWidgetItem* item = frame_list->currentItem();
738     if (item) {
739         //int ix = item->data(Qt::UserRole).toInt();;
740         slotShowFrame(item->toolTip());
741     }
742 }
743
744 void StopmotionWidget::slotAddSequence()
745 {
746     emit addOrUpdateSequence(getPathForFrame(0));
747 }
748
749 void StopmotionWidget::slotPlayPreview(bool animate)
750 {
751     m_animate = animate;
752     if (!animate) {
753         // stop animation
754         m_animationList.clear();
755         return;
756     }
757     if (KdenliveSettings::showstopmotionthumbs()) {
758         if (KdenliveSettings::sm_framesplayback() == 0) frame_list->setCurrentRow(0);
759         else frame_list->setCurrentRow(frame_list->count() - KdenliveSettings::sm_framesplayback());
760         QTimer::singleShot(200, this, SLOT(slotAnimate()));
761     } else {
762         SlideshowClip::selectedPath(getPathForFrame(0, sequence_name->currentText()), false, QString(), &m_animationList);
763         if (KdenliveSettings::sm_framesplayback() > 0) {
764             // only play the last x frames
765             while (m_animationList.count() > KdenliveSettings::sm_framesplayback() + 1) {
766                 m_animationList.removeFirst();
767             }
768         }
769         m_animatedIndex = 0;
770         slotAnimate();
771     }
772 }
773
774 void StopmotionWidget::slotAnimate()
775 {
776     if (m_animate) {
777         if (KdenliveSettings::showstopmotionthumbs()) {
778             int newRow = frame_list->currentRow() + 1;
779             if (KdenliveSettings::sm_loop() || newRow < frame_list->count()) {
780                 if (newRow >= frame_list->count()) {
781                     if (KdenliveSettings::sm_framesplayback() == 0) newRow = 0;
782                     else {
783                         // seek to correct frame
784                         newRow = frame_list->count() - KdenliveSettings::sm_framesplayback();
785                     }
786                 }
787                 frame_list->setCurrentRow(newRow);
788                 QTimer::singleShot(100, this, SLOT(slotAnimate()));
789                 return;
790             }
791         } else {
792             if (m_animatedIndex >= m_animationList.count()) {
793                 if (KdenliveSettings::sm_loop()) m_animatedIndex = 0;
794                 else m_animatedIndex = -1;
795             }
796             if (m_animatedIndex > -1) {
797                 slotShowFrame(m_animationList.at(m_animatedIndex));
798                 m_animatedIndex++;
799                 QTimer::singleShot(100, this, SLOT(slotAnimate()));
800                 return;
801             }
802         }
803     }
804     m_animate = false;
805     preview_button->setChecked(false);
806
807 }
808
809 QListWidgetItem* StopmotionWidget::getFrameFromIndex(int ix)
810 {
811     QListWidgetItem* item = NULL;
812     int pos = ix;
813     if (ix >= frame_list->count()) {
814         pos = frame_list->count() - 1;
815     }
816     if (ix < 0) pos = 0;
817     item = frame_list->item(pos);
818
819     int value = item->data(Qt::UserRole).toInt();
820     if (value == ix) return item;
821     else if (value < ix) {
822         pos++;
823         while (pos < frame_list->count()) {
824             item = frame_list->item(pos);
825             value = item->data(Qt::UserRole).toInt();
826             if (value == ix) return item;
827             pos++;
828         }
829     } else {
830         pos --;
831         while (pos >= 0) {
832             item = frame_list->item(pos);
833             value = item->data(Qt::UserRole).toInt();
834             if (value == ix) return item;
835             pos --;
836         }
837     }
838     return NULL;
839 }
840
841
842 void StopmotionWidget::selectFrame(int ix)
843 {
844     frame_list->blockSignals(true);
845     QListWidgetItem* item = getFrameFromIndex(ix);
846     if (!item) return;
847     frame_list->setCurrentItem(item);
848     frame_list->blockSignals(false);
849 }
850
851 void StopmotionWidget::slotSeekFrame(bool forward)
852 {
853     int ix = frame_list->currentRow();
854     if (forward) {
855         if (ix < frame_list->count() - 1) frame_list->setCurrentRow(ix + 1);
856     } else if (ix > 0) frame_list->setCurrentRow(ix - 1);
857 }
858
859 void StopmotionWidget::slotRemoveFrame()
860 {
861     if (frame_list->currentItem() == NULL) return;
862     QString path = frame_list->currentItem()->toolTip();
863     if (KMessageBox::questionYesNo(this, i18n("Delete frame %1 from disk?", path), i18n("Delete Frame")) != KMessageBox::Yes) return;
864     QFile f(path);
865     if (f.remove()) {
866         QListWidgetItem* item = frame_list->takeItem(frame_list->currentRow());
867         int ix = item->data(Qt::UserRole).toInt();
868         if (ix == m_sequenceFrame - 1) {
869             // We are removing the last frame, update counter
870             QListWidgetItem* item2 = frame_list->item(frame_list->count() - 1);
871             if (item2) m_sequenceFrame = item2->data(Qt::UserRole).toInt() + 1;
872         }
873         delete item;
874     }
875 }
876
877 void StopmotionWidget::slotSwitchAnalyse(bool isOn)
878 {
879     KdenliveSettings::setAnalyse_stopmotion(isOn);
880     if (m_captureDevice) m_captureDevice->sendFrameForAnalysis = isOn;
881 }
882
883 void StopmotionWidget::slotSwitchMirror(bool isOn)
884 {
885     //KdenliveSettings::setAnalyse_stopmotion(isOn);
886     if (m_captureDevice) m_captureDevice->mirror(isOn);
887 }
888
889 const QString StopmotionWidget::createProducer(MltVideoProfile profile, const QString &service, const QString &resource)
890 {
891     Q_UNUSED(profile)
892
893     QString playlist = "<mlt title=\"capture\"><producer id=\"producer0\" in=\"0\" out=\"99999\"><property name=\"mlt_type\">producer</property><property name=\"length\">100000</property><property name=\"eof\">pause</property><property name=\"resource\">" + resource + "</property><property name=\"mlt_service\">" + service + "</property></producer>";
894
895     // overlay track
896     playlist.append("<playlist id=\"playlist0\"></playlist>");
897
898     // video4linux track
899     playlist.append("<playlist id=\"playlist1\"><entry producer=\"producer0\" in=\"0\" out=\"99999\"/></playlist>");
900
901     playlist.append("<tractor id=\"tractor0\" title=\"video0\" global_feed=\"1\" in=\"0\" out=\"99999\">");
902     playlist.append("<track producer=\"playlist0\"/>");
903     playlist.append("<track producer=\"playlist1\"/>");
904     playlist.append("</tractor></mlt>");
905
906
907     return playlist;
908 }
909
910
911