]> git.sesse.net Git - kdenlive/blob - src/mltdevicecapture.cpp
Use char for single character [krazy 1-44] by Mikko Rapeli
[kdenlive] / src / mltdevicecapture.cpp
1 /***************************************************************************
2                         mltdevicecapture.cpp  -  description
3                            -------------------
4    begin                : Sun May 21 2011
5    copyright            : (C) 2011 by Jean-Baptiste Mardelle (jb@kdenlive.org)
6
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
19 #include "mltdevicecapture.h"
20 #include "kdenlivesettings.h"
21 #include "definitions.h"
22
23 #include <mlt++/Mlt.h>
24
25 #include <KDebug>
26 #include <KStandardDirs>
27 #include <KMessageBox>
28 #include <KLocale>
29 #include <KTemporaryFile>
30
31 #include <QTimer>
32 #include <QDir>
33 #include <QString>
34 #include <QApplication>
35 #include <QThread>
36
37 #include <cstdlib>
38 #include <cstdarg>
39
40 #include <QDebug>
41
42
43
44 static void consumer_gl_frame_show(mlt_consumer, MltDeviceCapture * self, mlt_frame frame_ptr)
45 {
46     // detect if the producer has finished playing. Is there a better way to do it?
47     Mlt::Frame frame(frame_ptr);
48     self->showFrame(frame);
49 }
50
51 /*static void rec_consumer_frame_show(mlt_consumer, MltDeviceCapture * self, mlt_frame frame_ptr)
52 {
53     Mlt::Frame frame(frame_ptr);
54     if (!frame.is_valid()) return;
55     self->gotCapturedFrame(frame);
56 }*/
57
58 static void rec_consumer_frame_preview(mlt_consumer, MltDeviceCapture * self, mlt_frame frame_ptr)
59 {
60     Mlt::Frame frame(frame_ptr);
61     if (!frame.is_valid()) return;
62     if (self->sendFrameForAnalysis && frame_ptr->convert_image) {
63         self->emitFrameUpdated(frame);
64     }
65     if (self->doCapture > 0) {  
66         self->doCapture --;
67         if (self->doCapture == 0) self->saveFrame(frame);
68     }
69
70     //TODO: connect record monitor to audio scopes
71     
72     if (self->analyseAudio) {
73         self->showAudio(frame);
74     }
75     
76 }
77
78
79 MltDeviceCapture::MltDeviceCapture(QString profile, VideoSurface *surface, QWidget *parent) :
80     AbstractRender(Kdenlive::recordMonitor, parent),
81     doCapture(0),
82     sendFrameForAnalysis(false),
83     processingImage(false),
84     m_mltConsumer(NULL),
85     m_mltProducer(NULL),
86     m_mltProfile(NULL),
87     m_showFrameEvent(NULL),
88     m_droppedFrames(0),
89     m_livePreview(KdenliveSettings::enable_recording_preview()),
90     m_winid((int) surface->winId())
91 {
92     m_captureDisplayWidget = surface;
93     analyseAudio = KdenliveSettings::monitor_audio();
94     if (profile.isEmpty()) profile = KdenliveSettings::current_profile();
95     buildConsumer(profile);
96     connect(this, SIGNAL(unblockPreview()), this, SLOT(slotPreparePreview()));
97     m_droppedFramesTimer.setSingleShot(false);
98     m_droppedFramesTimer.setInterval(1000);
99     connect(&m_droppedFramesTimer, SIGNAL(timeout()), this, SLOT(slotCheckDroppedFrames()));
100 }
101
102 MltDeviceCapture::~MltDeviceCapture()
103 {
104     if (m_mltConsumer) delete m_mltConsumer;
105     if (m_mltProducer) delete m_mltProducer;
106     if (m_mltProfile) delete m_mltProfile;
107 }
108
109 void MltDeviceCapture::buildConsumer(const QString &profileName)
110 {
111     if (!profileName.isEmpty()) m_activeProfile = profileName;
112
113     if (m_mltProfile) delete m_mltProfile;
114
115     char *tmp = qstrdup(m_activeProfile.toUtf8().constData());
116     setenv("MLT_PROFILE", tmp, 1);
117     m_mltProfile = new Mlt::Profile(tmp);
118     m_mltProfile->set_explicit(true);
119     delete[] tmp;
120
121     QString videoDriver = KdenliveSettings::videodrivername();
122     if (!videoDriver.isEmpty()) {
123         if (videoDriver == "x11_noaccel") {
124             setenv("SDL_VIDEO_YUV_HWACCEL", "0", 1);
125             videoDriver = "x11";
126         } else {
127             unsetenv("SDL_VIDEO_YUV_HWACCEL");
128         }
129     }
130     setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 1);
131     
132   
133     if (m_winid == 0) {
134         // OpenGL monitor
135         m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "sdl_audio");
136         m_mltConsumer->set("preview_off", 1);
137         m_mltConsumer->set("preview_format", mlt_image_rgb24);
138         m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_gl_frame_show);
139     } else {
140         m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "sdl_preview");
141         m_mltConsumer->set("window_id", m_winid);
142         m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) rec_consumer_frame_preview);
143     }
144     //m_mltConsumer->set("resize", 1);
145     //m_mltConsumer->set("terminate_on_pause", 1);
146     m_mltConsumer->set("window_background", KdenliveSettings::window_background().name().toUtf8().constData());
147     //m_mltConsumer->set("rescale", "nearest");
148
149     QString audioDevice = KdenliveSettings::audiodevicename();
150     if (!audioDevice.isEmpty())
151         m_mltConsumer->set("audio_device", audioDevice.toUtf8().constData());
152
153     if (!videoDriver.isEmpty())
154         m_mltConsumer->set("video_driver", videoDriver.toUtf8().constData());
155
156     QString audioDriver = KdenliveSettings::audiodrivername();
157
158     if (!audioDriver.isEmpty())
159         m_mltConsumer->set("audio_driver", audioDriver.toUtf8().constData());
160
161     //m_mltConsumer->set("progressive", 0);
162     //m_mltConsumer->set("buffer", 1);
163     //m_mltConsumer->set("real_time", 0);
164 }
165
166 void MltDeviceCapture::pause()
167 {   
168     if (m_mltConsumer) {
169           m_mltConsumer->set("refresh", 0);
170           //m_mltProducer->set_speed(0.0);
171           m_mltConsumer->purge();
172     }
173 }
174
175 void MltDeviceCapture::stop()
176 {
177     m_droppedFramesTimer.stop();
178     bool isPlaylist = false;
179     //disconnect(this, SIGNAL(imageReady(QImage)), this, SIGNAL(frameUpdated(QImage)));
180     //m_captureDisplayWidget->stop();
181     
182     if (m_showFrameEvent) delete m_showFrameEvent;
183     m_showFrameEvent = NULL;
184     
185     if (m_mltConsumer) {
186         m_mltConsumer->set("refresh", 0);
187         m_mltConsumer->stop();
188         //if (!m_mltConsumer->is_stopped()) m_mltConsumer->stop();
189     }
190     if (m_mltProducer) {
191         QList <Mlt::Producer *> prods;
192         Mlt::Service service(m_mltProducer->parent().get_service());
193         mlt_service_lock(service.get_service());
194         if (service.type() == tractor_type) {
195             isPlaylist = true;
196             Mlt::Tractor tractor(service);
197             mlt_tractor_close(tractor.get_tractor());
198             Mlt::Field *field = tractor.field();
199             mlt_service nextservice = mlt_service_get_producer(service.get_service());
200             mlt_service nextservicetodisconnect;
201             mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
202             QString mlt_type = mlt_properties_get(properties, "mlt_type");
203             QString resource = mlt_properties_get(properties, "mlt_service");
204             // Delete all transitions
205             while (mlt_type == "transition") {
206                 nextservicetodisconnect = nextservice;
207                 nextservice = mlt_service_producer(nextservice);
208                 mlt_field_disconnect_service(field->get_field(), nextservicetodisconnect);
209                 if (nextservice == NULL) break;
210                 properties = MLT_SERVICE_PROPERTIES(nextservice);
211                 mlt_type = mlt_properties_get(properties, "mlt_type");
212                 resource = mlt_properties_get(properties, "mlt_service");
213             }
214             delete field;
215             field = NULL;
216         }
217         mlt_service_unlock(service.get_service());
218         delete m_mltProducer;
219         m_mltProducer = NULL;
220     }
221     // For some reason, the consumer seems to be deleted by previous stuff when in playlist mode
222     if (!isPlaylist && m_mltConsumer) delete m_mltConsumer;
223     m_mltConsumer = NULL;
224 }
225
226
227 void MltDeviceCapture::slotDoRefresh()
228 {
229     QMutexLocker locker(&m_mutex);
230     if (!m_mltProducer)
231         return;
232     if (m_mltConsumer) {
233         if (m_mltConsumer->is_stopped()) m_mltConsumer->start();
234         m_mltConsumer->purge();
235         m_mltConsumer->set("refresh", 1);
236     }
237 }
238
239
240 void MltDeviceCapture::emitFrameUpdated(Mlt::Frame& frame)
241 {
242     /*
243     //TEST: is it better to convert the frame in a thread outside of MLT??
244     if (processingImage) return;
245     mlt_image_format format = (mlt_image_format) frame.get_int("format"); //mlt_image_rgb24;
246     int width = frame.get_int("width");
247     int height = frame.get_int("height");
248     unsigned char *buffer = (unsigned char *) frame.get_data("image");
249     if (format == mlt_image_yuv422) {
250         QtConcurrent::run(this, &MltDeviceCapture::uyvy2rgb, (unsigned char *) buffer, width, height);
251     }
252     */
253
254     mlt_image_format format = mlt_image_rgb24a;
255     int width = 0;
256     int height = 0;
257     const uchar* image = frame.get_image(format, width, height);
258     QImage qimage(width, height, QImage::Format_ARGB32_Premultiplied);
259     memcpy(qimage.bits(), image, width * height * 4);
260     emit frameUpdated(qimage.rgbSwapped());
261 }
262
263 void MltDeviceCapture::showFrame(Mlt::Frame& frame)
264 {
265     mlt_image_format format = mlt_image_rgb24;
266     int width = 0;
267     int height = 0;
268     const uchar* image = frame.get_image(format, width, height);
269     QImage qimage(width, height, QImage::Format_RGB888);
270     memcpy(qimage.scanLine(0), image, width * height * 3);
271     emit showImageSignal(qimage);
272
273     if (sendFrameForAnalysis && frame.get_frame()->convert_image) {
274         emit frameUpdated(qimage.rgbSwapped());
275     }
276 }
277
278 void MltDeviceCapture::showAudio(Mlt::Frame& frame)
279 {
280     if (!frame.is_valid() || frame.get_int("test_audio") != 0) {
281         return;
282     }
283     mlt_audio_format audio_format = mlt_audio_s16;
284     int freq = 0;
285     int num_channels = 0;
286     int samples = 0;
287     int16_t* data = (int16_t*)frame.get_audio(audio_format, freq, num_channels, samples);
288
289     if (!data) {
290         return;
291     }
292
293     // Data format: [ c00 c10 c01 c11 c02 c12 c03 c13 ... c0{samples-1} c1{samples-1} for 2 channels.
294     // So the vector is of size samples*channels.
295     QVector<int16_t> sampleVector(samples*num_channels);
296     memcpy(sampleVector.data(), data, samples*num_channels*sizeof(int16_t));
297     if (samples > 0) {
298         emit audioSamplesSignal(sampleVector, freq, num_channels, samples);
299     }
300 }
301
302 bool MltDeviceCapture::slotStartPreview(const QString &producer, bool xmlFormat)
303 {
304     if (m_mltConsumer == NULL) {
305         buildConsumer();
306     }
307     char *tmp = qstrdup(producer.toUtf8().constData());
308     if (xmlFormat) m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", tmp);
309     else m_mltProducer = new Mlt::Producer(*m_mltProfile, tmp);
310     delete[] tmp;
311
312     if (m_mltProducer == NULL || !m_mltProducer->is_valid()) {
313         if (m_mltProducer) {
314             delete m_mltProducer;
315             m_mltProducer = NULL;
316         }
317         kDebug()<<"//// ERROR CREATRING PROD";
318         return false;
319     }
320     m_mltConsumer->connect(*m_mltProducer);
321     if (m_mltConsumer->start() == -1) {
322         delete m_mltConsumer;
323         m_mltConsumer = NULL;
324         return 0;
325     }
326     m_droppedFramesTimer.start();
327     //connect(this, SIGNAL(imageReady(QImage)), this, SIGNAL(frameUpdated(QImage)));
328     return 1;
329 }
330
331 void MltDeviceCapture::slotCheckDroppedFrames()
332 {
333     if (m_mltProducer) {
334         int dropped = m_mltProducer->get_int("dropped");
335         if (dropped > m_droppedFrames) {
336             m_droppedFrames = dropped;
337             emit droppedFrames(m_droppedFrames);
338         }
339     }
340 }
341
342 void MltDeviceCapture::gotCapturedFrame(Mlt::Frame& frame)
343 {
344     if (m_mltProducer) {
345         int dropped = m_mltProducer->get_int("dropped");
346         if (dropped > m_droppedFrames) {
347             m_droppedFrames = dropped;
348             emit droppedFrames(m_droppedFrames);
349         }
350     }
351     m_frameCount++;
352     if (!m_livePreview) return;
353     //if (m_livePreview == 0 && (m_frameCount % 10 > 0)) return;
354     mlt_image_format format = mlt_image_rgb24;
355     int width = 0;
356     int height = 0;
357     uint8_t *data = frame.get_image(format, width, height, 0);
358     //QImage image(width, height, QImage::Format_RGB888);
359     //memcpy(image.bits(), data, width * height * 3);
360     QImage image((uchar *)data, width, height, QImage::Format_RGB888);
361
362     //m_captureDisplayWidget->setImage(image);
363
364     //TEST: is it better to process frame conversion ouside MLT???
365     /*
366     if (!m_livePreview || processingImage) return;
367
368     mlt_image_format format = (mlt_image_format) frame.get_int("format"); //mlt_image_rgb24a;
369     int width = frame.get_int("width");
370     int height = frame.get_int("height");
371     unsigned char *buffer = (unsigned char *) frame.get_data("image");
372     //unsigned char *buffer = frame.get_image(format, width, height);
373     //convert from uyvy422 to rgba
374     if (format == mlt_image_yuv422) {
375         QtConcurrent::run(this, &MltDeviceCapture::uyvy2rgb, (unsigned char *) buffer, width, height);
376         //CaptureHandler::uyvy2rgb((uchar *)frameBytes, (uchar *)image.bits(), videoFrame->GetWidth(), videoFrame->GetHeight());
377     }*/
378 }
379
380 void MltDeviceCapture::saveFrame(Mlt::Frame& frame)
381 {
382     mlt_image_format format = mlt_image_rgb24;
383     int width = 0;
384     int height = 0;
385     const uchar* image = frame.get_image(format, width, height);
386     QImage qimage(width, height, QImage::Format_RGB888);
387     memcpy(qimage.bits(), image, width * height * 3);
388
389     // Re-enable overlay
390     Mlt::Service service(m_mltProducer->parent().get_service());
391     Mlt::Tractor tractor(service);
392     Mlt::Producer trackProducer(tractor.track(0));
393     trackProducer.set("hide", 0);
394     
395     qimage.save(m_capturePath);
396     emit frameSaved(m_capturePath);
397     m_capturePath.clear();
398 }
399
400 void MltDeviceCapture::captureFrame(const QString &path)
401 {
402     if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return;
403
404     // Hide overlay track before doing the capture
405     Mlt::Service service(m_mltProducer->parent().get_service());
406     Mlt::Tractor tractor(service);
407     Mlt::Producer trackProducer(tractor.track(0));
408     mlt_service_lock(service.get_service());
409     trackProducer.set("hide", 1);
410     m_mltConsumer->purge();
411     mlt_service_unlock(service.get_service());
412     m_capturePath = path;
413     // Wait for 5 frames before capture to make sure overlay is gone
414     doCapture = 5;
415 }
416
417 bool MltDeviceCapture::slotStartCapture(const QString &params, const QString &path, const QString &playlist, bool livePreview, bool xmlPlaylist)
418 {
419     stop();
420     m_livePreview = livePreview;
421     m_frameCount = 0;
422     m_droppedFrames = 0;
423     if (m_mltProfile) delete m_mltProfile;
424     char *tmp = qstrdup(m_activeProfile.toUtf8().constData());
425     m_mltProfile = new Mlt::Profile(tmp);
426     delete[] tmp;
427     //m_mltProfile->get_profile()->is_explicit = 1;
428     
429     
430     /*kDebug()<<"-- CREATING CAP: "<<params<<", PATH: "<<path;
431     tmp = qstrdup(QString("avformat:" + path).toUtf8().constData());
432     m_mltConsumer = new Mlt::Consumer(*m_mltProfile, tmp);
433     m_mltConsumer->set("real_time", -KdenliveSettings::mltthreads());
434     delete[] tmp;*/
435     
436     m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "multi");
437     if (m_mltConsumer == NULL || !m_mltConsumer->is_valid()) {
438         if (m_mltConsumer) {
439             delete m_mltConsumer;
440             m_mltConsumer = NULL;
441         }
442         return false;
443     }
444     
445     m_winid = (int) m_captureDisplayWidget->winId();
446     
447     // Create multi consumer setup
448     Mlt::Properties *renderProps = new Mlt::Properties;
449     renderProps->set("mlt_service", "avformat");
450     renderProps->set("target", path.toUtf8().constData());
451     renderProps->set("real_time", -KdenliveSettings::mltthreads());
452     renderProps->set("terminate_on_pause", 0);
453     renderProps->set("mlt_profile", m_activeProfile.toUtf8().constData());
454     
455
456     QStringList paramList = params.split(' ', QString::SkipEmptyParts);
457     char *tmp2;
458     for (int i = 0; i < paramList.count(); i++) {
459         tmp = qstrdup(paramList.at(i).section('=', 0, 0).toUtf8().constData());
460         QString value = paramList.at(i).section('=', 1, 1);
461         if (value == "%threads") value = QString::number(QThread::idealThreadCount());
462         tmp2 = qstrdup(value.toUtf8().constData());
463         renderProps->set(tmp, tmp2);
464         delete[] tmp;
465         delete[] tmp2;
466     }
467     mlt_properties consumerProperties = m_mltConsumer->get_properties();
468     mlt_properties_set_data(consumerProperties, "0", renderProps->get_properties(), 0, (mlt_destructor) mlt_properties_close, NULL);
469     
470     if (m_livePreview) 
471     {
472         // user wants live preview
473         Mlt::Properties *previewProps = new Mlt::Properties;
474         QString videoDriver = KdenliveSettings::videodrivername();
475         if (!videoDriver.isEmpty()) {
476             if (videoDriver == "x11_noaccel") {
477                 setenv("SDL_VIDEO_YUV_HWACCEL", "0", 1);
478                 videoDriver = "x11";
479             } else {
480                 unsetenv("SDL_VIDEO_YUV_HWACCEL");
481             }
482         }
483         setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 1);
484         
485         if (m_winid == 0) {
486             // OpenGL monitor
487             previewProps->set("mlt_service", "sdl_audio");
488             previewProps->set("preview_off", 1);
489             previewProps->set("preview_format", mlt_image_rgb24);
490             previewProps->set("terminate_on_pause", 0);
491             m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_gl_frame_show);
492         } else {
493             previewProps->set("mlt_service", "sdl_preview");
494             previewProps->set("window_id", m_winid);
495             previewProps->set("terminate_on_pause", 0);
496             //m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) rec_consumer_frame_preview);
497         }
498         //m_mltConsumer->set("resize", 1);
499         previewProps->set("window_background", KdenliveSettings::window_background().name().toUtf8().constData());
500         QString audioDevice = KdenliveSettings::audiodevicename();
501         if (!audioDevice.isEmpty())
502             previewProps->set("audio_device", audioDevice.toUtf8().constData());
503
504         if (!videoDriver.isEmpty())
505             previewProps->set("video_driver", videoDriver.toUtf8().constData());
506
507         QString audioDriver = KdenliveSettings::audiodrivername();
508
509         if (!audioDriver.isEmpty())
510             previewProps->set("audio_driver", audioDriver.toUtf8().constData());
511         
512         previewProps->set("real_time", "0");
513         previewProps->set("mlt_profile", m_activeProfile.toUtf8().constData());
514         mlt_properties_set_data(consumerProperties, "1", previewProps->get_properties(), 0, (mlt_destructor) mlt_properties_close, NULL);
515         //m_showFrameEvent = m_mltConsumer->listen("consumer-frame-render", this, (mlt_listener) rec_consumer_frame_show);
516     }
517     else {
518         
519     }
520     
521     tmp = qstrdup(playlist.toUtf8().constData());
522     if (xmlPlaylist) {
523         // create an xml producer
524         m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", tmp);
525     }
526     else {
527         // create a producer based on mltproducer parameter
528         m_mltProducer = new Mlt::Producer(*m_mltProfile, tmp);
529     }
530     delete[] tmp;
531
532     if (m_mltProducer == NULL || !m_mltProducer->is_valid()) {
533         kDebug()<<"//// ERROR CREATRING PROD";
534         return false;
535     }
536     
537     m_mltConsumer->connect(*m_mltProducer);
538     if (m_mltConsumer->start() == -1) {
539         if (m_showFrameEvent) delete m_showFrameEvent;
540         m_showFrameEvent = NULL;
541         delete m_mltConsumer;
542         m_mltConsumer = NULL;
543         return 0;
544     }
545     m_droppedFramesTimer.start();
546     return 1;
547 }
548
549
550 void MltDeviceCapture::setOverlay(const QString &path)
551 {
552     if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return;
553     Mlt::Producer parentProd(m_mltProducer->parent());
554     if (parentProd.get_producer() == NULL) {
555         kDebug() << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////";
556         return;
557     }
558
559     Mlt::Service service(parentProd.get_service());
560     if (service.type() != tractor_type) {
561         kWarning() << "// TRACTOR PROBLEM";
562         return;
563     }
564     Mlt::Tractor tractor(service);
565     if ( tractor.count() < 2) {
566         kWarning() << "// TRACTOR PROBLEM";
567         return;
568     }
569     mlt_service_lock(service.get_service());
570     Mlt::Producer trackProducer(tractor.track(0));
571     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
572
573     trackPlaylist.remove(0);
574     if (path.isEmpty()) {
575         mlt_service_unlock(service.get_service());
576         return;
577     }
578
579     // Add overlay clip
580     char *tmp = qstrdup(path.toUtf8().constData());
581     Mlt::Producer *clip = new Mlt::Producer (*m_mltProfile, "loader", tmp);
582     delete[] tmp;
583     clip->set_in_and_out(0, 99999);
584     trackPlaylist.insert_at(0, clip, 1);
585
586     // Add transition
587     mlt_service serv = m_mltProducer->parent().get_service();
588     mlt_service nextservice = mlt_service_get_producer(serv);
589     mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
590     QString mlt_type = mlt_properties_get(properties, "mlt_type");
591     if (mlt_type != "transition") {
592         // transition does not exist, add it
593         Mlt::Field *field = tractor.field();
594         Mlt::Transition *transition = new Mlt::Transition(*m_mltProfile, "composite");
595         transition->set_in_and_out(0, 0);
596         transition->set("geometry", "0/0:100%x100%:70");
597         transition->set("fill", 1);
598         transition->set("operator", "and");
599         transition->set("a_track", 0);
600         transition->set("b_track", 1);
601         field->plant_transition(*transition, 0, 1);
602     }
603     mlt_service_unlock(service.get_service());
604     //delete clip;
605 }
606
607 void MltDeviceCapture::setOverlayEffect(const QString &tag, QStringList parameters)
608 {
609     if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return;
610     Mlt::Service service(m_mltProducer->parent().get_service());
611     Mlt::Tractor tractor(service);
612     Mlt::Producer trackProducer(tractor.track(0));
613     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
614     Mlt::Service trackService(trackProducer.get_service());
615
616     mlt_service_lock(service.get_service());
617
618     // delete previous effects
619     Mlt::Filter *filter;
620     filter = trackService.filter(0);
621     if (filter && !tag.isEmpty()) {
622         QString currentService = filter->get("mlt_service");
623         if (currentService == tag) {
624             // Effect is already there
625             mlt_service_unlock(service.get_service());
626             return;
627         }
628     }
629     while (filter) {
630         trackService.detach(*filter);
631         delete filter;
632         filter = trackService.filter(0);
633     }
634     
635     if (tag.isEmpty()) {
636         mlt_service_unlock(service.get_service());
637         return;
638     }
639     
640     char *tmp = qstrdup(tag.toUtf8().constData());
641     filter = new Mlt::Filter(*m_mltProfile, tmp);
642     delete[] tmp;
643     if (filter && filter->is_valid()) {
644         for (int j = 0; j < parameters.count(); j++) {
645             filter->set(parameters.at(j).section('=', 0, 0).toUtf8().constData(), parameters.at(j).section('=', 1, 1).toUtf8().constData());
646         }
647         trackService.attach(*filter);
648     }
649     mlt_service_unlock(service.get_service());
650 }
651
652 void MltDeviceCapture::mirror(bool activate)
653 {
654     if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return;
655     Mlt::Service service(m_mltProducer->parent().get_service());
656     Mlt::Tractor tractor(service);
657     Mlt::Producer trackProducer(tractor.track(1));
658     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
659     Mlt::Service trackService(trackProducer.get_service());
660
661     mlt_service_lock(service.get_service());
662
663     // delete previous effects
664     Mlt::Filter *filter;
665     filter = trackService.filter(0);
666     while (filter) {
667         trackService.detach(*filter);
668         delete filter;
669         filter = trackService.filter(0);
670     }
671
672     if (!activate) {
673         mlt_service_unlock(service.get_service());
674         return;
675     }
676
677     filter = new Mlt::Filter(*m_mltProfile, "mirror");
678     if (filter && filter->is_valid()) {
679         filter->set("mirror", "flip");
680         trackService.attach(*filter);
681     }
682     mlt_service_unlock(service.get_service());
683 }
684
685 void MltDeviceCapture::uyvy2rgb(unsigned char *yuv_buffer, int width, int height)
686 {
687     processingImage = true;
688     QImage image(width, height, QImage::Format_RGB888);
689     unsigned char *rgb_buffer = image.bits();    
690
691     int len;
692     int r, g, b;
693     int Y, U, V, Y2;
694     int rgb_ptr, y_ptr, t;
695
696     len = width * height / 2;
697
698     rgb_ptr = 0;
699     y_ptr = 0;
700
701     for (t = 0; t < len; t++) { 
702       
703
704         Y = yuv_buffer[y_ptr];
705         U = yuv_buffer[y_ptr+1];
706         Y2 = yuv_buffer[y_ptr+2];
707         V = yuv_buffer[y_ptr+3];
708         y_ptr += 4;
709
710         r = ((298 * (Y - 16)               + 409 * (V - 128) + 128) >> 8);
711
712         g = ((298 * (Y - 16) - 100 * (U - 128) - 208 * (V - 128) + 128) >> 8);
713
714         b = ((298 * (Y - 16) + 516 * (U - 128)               + 128) >> 8);
715
716         if (r > 255) r = 255;
717         if (g > 255) g = 255;
718         if (b > 255) b = 255;
719
720         if (r < 0) r = 0;
721         if (g < 0) g = 0;
722         if (b < 0) b = 0;
723
724         rgb_buffer[rgb_ptr] = r;
725         rgb_buffer[rgb_ptr+1] = g;
726         rgb_buffer[rgb_ptr+2] = b;
727         rgb_ptr += 3;
728
729
730         r = ((298 * (Y2 - 16)               + 409 * (V - 128) + 128) >> 8);
731
732         g = ((298 * (Y2 - 16) - 100 * (U - 128) - 208 * (V - 128) + 128) >> 8);
733
734         b = ((298 * (Y2 - 16) + 516 * (U - 128)               + 128) >> 8);
735
736         if (r > 255) r = 255;
737         if (g > 255) g = 255;
738         if (b > 255) b = 255;
739
740         if (r < 0) r = 0;
741         if (g < 0) g = 0;
742         if (b < 0) b = 0;
743
744         rgb_buffer[rgb_ptr] = r;
745         rgb_buffer[rgb_ptr+1] = g;
746         rgb_buffer[rgb_ptr+2] = b;
747         rgb_ptr += 3;
748     }
749     //emit imageReady(image);
750     //m_captureDisplayWidget->setImage(image);
751     emit unblockPreview();
752     //processingImage = false;
753 }
754
755 void MltDeviceCapture::slotPreparePreview()
756 {
757     QTimer::singleShot(1000, this, SLOT(slotAllowPreview()));
758 }
759
760 void MltDeviceCapture::slotAllowPreview()
761 {
762     processingImage = false;
763 }
764
765