]> git.sesse.net Git - kdenlive/blob - src/mltdevicecapture.cpp
Switch to rgb24 instead of rbg24a when requesting frames from MLT. Fixes: http:/...
[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->purge();
188         m_mltConsumer->stop();
189         //if (!m_mltConsumer->is_stopped()) m_mltConsumer->stop();
190     }
191     if (m_mltProducer) {
192         QList <Mlt::Producer *> prods;
193         Mlt::Service service(m_mltProducer->parent().get_service());
194         mlt_service_lock(service.get_service());
195         if (service.type() == tractor_type) {
196             isPlaylist = true;
197             Mlt::Tractor tractor(service);
198             mlt_tractor_close(tractor.get_tractor());
199             Mlt::Field *field = tractor.field();
200             mlt_service nextservice = mlt_service_get_producer(service.get_service());
201             mlt_service nextservicetodisconnect;
202             mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
203             QString mlt_type = mlt_properties_get(properties, "mlt_type");
204             QString resource = mlt_properties_get(properties, "mlt_service");
205             // Delete all transitions
206             while (mlt_type == "transition") {
207                 nextservicetodisconnect = nextservice;
208                 nextservice = mlt_service_producer(nextservice);
209                 mlt_field_disconnect_service(field->get_field(), nextservicetodisconnect);
210                 if (nextservice == NULL) break;
211                 properties = MLT_SERVICE_PROPERTIES(nextservice);
212                 mlt_type = mlt_properties_get(properties, "mlt_type");
213                 resource = mlt_properties_get(properties, "mlt_service");
214             }
215             delete field;
216             field = NULL;
217         }
218         mlt_service_unlock(service.get_service());
219         delete m_mltProducer;
220         m_mltProducer = NULL;
221     }
222     // For some reason, the consumer seems to be deleted by previous stuff when in playlist mode
223     if (!isPlaylist && m_mltConsumer) delete m_mltConsumer;
224     m_mltConsumer = NULL;
225 }
226
227
228 void MltDeviceCapture::slotDoRefresh()
229 {
230     QMutexLocker locker(&m_mutex);
231     if (!m_mltProducer)
232         return;
233     if (m_mltConsumer) {
234         if (m_mltConsumer->is_stopped()) m_mltConsumer->start();
235         m_mltConsumer->purge();
236         m_mltConsumer->set("refresh", 1);
237     }
238 }
239
240
241 void MltDeviceCapture::emitFrameUpdated(Mlt::Frame& frame)
242 {
243     /*
244     //TEST: is it better to convert the frame in a thread outside of MLT??
245     if (processingImage) return;
246     mlt_image_format format = (mlt_image_format) frame.get_int("format"); //mlt_image_rgb24;
247     int width = frame.get_int("width");
248     int height = frame.get_int("height");
249     unsigned char *buffer = (unsigned char *) frame.get_data("image");
250     if (format == mlt_image_yuv422) {
251         QtConcurrent::run(this, &MltDeviceCapture::uyvy2rgb, (unsigned char *) buffer, width, height);
252     }
253     */
254
255     mlt_image_format format = mlt_image_rgb24;
256     int width = 0;
257     int height = 0;
258     const uchar* image = frame.get_image(format, width, height);
259     QImage qimage(width, height, QImage::Format_RGB888);
260     //QImage qimage(width, height, QImage::Format_ARGB32_Premultiplied);
261     memcpy(qimage.bits(), image, width * height * 3);
262     emit frameUpdated(qimage);
263 }
264
265 void MltDeviceCapture::showFrame(Mlt::Frame& frame)
266 {
267     mlt_image_format format = mlt_image_rgb24;
268     int width = 0;
269     int height = 0;
270     const uchar* image = frame.get_image(format, width, height);
271     QImage qimage(width, height, QImage::Format_RGB888);
272     memcpy(qimage.scanLine(0), image, width * height * 3);
273     emit showImageSignal(qimage);
274
275     if (sendFrameForAnalysis && frame.get_frame()->convert_image) {
276         emit frameUpdated(qimage.rgbSwapped());
277     }
278 }
279
280 void MltDeviceCapture::showAudio(Mlt::Frame& frame)
281 {
282     if (!frame.is_valid() || frame.get_int("test_audio") != 0) {
283         return;
284     }
285     mlt_audio_format audio_format = mlt_audio_s16;
286     int freq = 0;
287     int num_channels = 0;
288     int samples = 0;
289     int16_t* data = (int16_t*)frame.get_audio(audio_format, freq, num_channels, samples);
290
291     if (!data) {
292         return;
293     }
294
295     // Data format: [ c00 c10 c01 c11 c02 c12 c03 c13 ... c0{samples-1} c1{samples-1} for 2 channels.
296     // So the vector is of size samples*channels.
297     QVector<int16_t> sampleVector(samples*num_channels);
298     memcpy(sampleVector.data(), data, samples*num_channels*sizeof(int16_t));
299     if (samples > 0) {
300         emit audioSamplesSignal(sampleVector, freq, num_channels, samples);
301     }
302 }
303
304 bool MltDeviceCapture::slotStartPreview(const QString &producer, bool xmlFormat)
305 {
306     if (m_mltConsumer == NULL) {
307         buildConsumer();
308     }
309     char *tmp = qstrdup(producer.toUtf8().constData());
310     if (xmlFormat) m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", tmp);
311     else m_mltProducer = new Mlt::Producer(*m_mltProfile, tmp);
312     delete[] tmp;
313
314     if (m_mltProducer == NULL || !m_mltProducer->is_valid()) {
315         if (m_mltProducer) {
316             delete m_mltProducer;
317             m_mltProducer = NULL;
318         }
319         kDebug()<<"//// ERROR CREATRING PROD";
320         return false;
321     }
322     m_mltConsumer->connect(*m_mltProducer);
323     if (m_mltConsumer->start() == -1) {
324         delete m_mltConsumer;
325         m_mltConsumer = NULL;
326         return 0;
327     }
328     m_droppedFramesTimer.start();
329     //connect(this, SIGNAL(imageReady(QImage)), this, SIGNAL(frameUpdated(QImage)));
330     return 1;
331 }
332
333 void MltDeviceCapture::slotCheckDroppedFrames()
334 {
335     if (m_mltProducer) {
336         int dropped = m_mltProducer->get_int("dropped");
337         if (dropped > m_droppedFrames) {
338             m_droppedFrames = dropped;
339             emit droppedFrames(m_droppedFrames);
340         }
341     }
342 }
343
344 void MltDeviceCapture::gotCapturedFrame(Mlt::Frame& frame)
345 {
346     if (m_mltProducer) {
347         int dropped = m_mltProducer->get_int("dropped");
348         if (dropped > m_droppedFrames) {
349             m_droppedFrames = dropped;
350             emit droppedFrames(m_droppedFrames);
351         }
352     }
353     m_frameCount++;
354     if (!m_livePreview) return;
355     //if (m_livePreview == 0 && (m_frameCount % 10 > 0)) return;
356     mlt_image_format format = mlt_image_rgb24;
357     int width = 0;
358     int height = 0;
359     uint8_t *data = frame.get_image(format, width, height, 0);
360     //QImage image(width, height, QImage::Format_RGB888);
361     //memcpy(image.bits(), data, width * height * 3);
362     QImage image((uchar *)data, width, height, QImage::Format_RGB888);
363
364     //m_captureDisplayWidget->setImage(image);
365
366     //TEST: is it better to process frame conversion ouside MLT???
367     /*
368     if (!m_livePreview || processingImage) return;
369
370     mlt_image_format format = (mlt_image_format) frame.get_int("format"); //mlt_image_rgb24a;
371     int width = frame.get_int("width");
372     int height = frame.get_int("height");
373     unsigned char *buffer = (unsigned char *) frame.get_data("image");
374     //unsigned char *buffer = frame.get_image(format, width, height);
375     //convert from uyvy422 to rgba
376     if (format == mlt_image_yuv422) {
377         QtConcurrent::run(this, &MltDeviceCapture::uyvy2rgb, (unsigned char *) buffer, width, height);
378         //CaptureHandler::uyvy2rgb((uchar *)frameBytes, (uchar *)image.bits(), videoFrame->GetWidth(), videoFrame->GetHeight());
379     }*/
380 }
381
382 void MltDeviceCapture::saveFrame(Mlt::Frame& frame)
383 {
384     mlt_image_format format = mlt_image_rgb24;
385     int width = 0;
386     int height = 0;
387     const uchar* image = frame.get_image(format, width, height);
388     QImage qimage(width, height, QImage::Format_RGB888);
389     memcpy(qimage.bits(), image, width * height * 3);
390
391     // Re-enable overlay
392     Mlt::Service service(m_mltProducer->parent().get_service());
393     Mlt::Tractor tractor(service);
394     Mlt::Producer trackProducer(tractor.track(0));
395     trackProducer.set("hide", 0);
396     
397     qimage.save(m_capturePath);
398     emit frameSaved(m_capturePath);
399     m_capturePath.clear();
400 }
401
402 void MltDeviceCapture::captureFrame(const QString &path)
403 {
404     if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return;
405
406     // Hide overlay track before doing the capture
407     Mlt::Service service(m_mltProducer->parent().get_service());
408     Mlt::Tractor tractor(service);
409     Mlt::Producer trackProducer(tractor.track(0));
410     mlt_service_lock(service.get_service());
411     trackProducer.set("hide", 1);
412     m_mltConsumer->purge();
413     mlt_service_unlock(service.get_service());
414     m_capturePath = path;
415     // Wait for 5 frames before capture to make sure overlay is gone
416     doCapture = 5;
417 }
418
419 bool MltDeviceCapture::slotStartCapture(const QString &params, const QString &path, const QString &playlist, bool livePreview, bool xmlPlaylist)
420 {
421     stop();
422     m_livePreview = livePreview;
423     m_frameCount = 0;
424     m_droppedFrames = 0;
425     if (m_mltProfile) delete m_mltProfile;
426     char *tmp = qstrdup(m_activeProfile.toUtf8().constData());
427     m_mltProfile = new Mlt::Profile(tmp);
428     delete[] tmp;
429     //m_mltProfile->get_profile()->is_explicit = 1;
430     
431     
432     /*kDebug()<<"-- CREATING CAP: "<<params<<", PATH: "<<path;
433     tmp = qstrdup(QString("avformat:" + path).toUtf8().constData());
434     m_mltConsumer = new Mlt::Consumer(*m_mltProfile, tmp);
435     m_mltConsumer->set("real_time", -KdenliveSettings::mltthreads());
436     delete[] tmp;*/
437     
438     m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "multi");
439     if (m_mltConsumer == NULL || !m_mltConsumer->is_valid()) {
440         if (m_mltConsumer) {
441             delete m_mltConsumer;
442             m_mltConsumer = NULL;
443         }
444         return false;
445     }
446     
447     m_winid = (int) m_captureDisplayWidget->winId();
448     
449     // Create multi consumer setup
450     Mlt::Properties *renderProps = new Mlt::Properties;
451     renderProps->set("mlt_service", "avformat");
452     renderProps->set("target", path.toUtf8().constData());
453     renderProps->set("real_time", -KdenliveSettings::mltthreads());
454     //renderProps->set("terminate_on_pause", 0);
455     renderProps->set("mlt_profile", m_activeProfile.toUtf8().constData());
456     
457
458     QStringList paramList = params.split(' ', QString::SkipEmptyParts);
459     char *tmp2;
460     for (int i = 0; i < paramList.count(); i++) {
461         tmp = qstrdup(paramList.at(i).section('=', 0, 0).toUtf8().constData());
462         QString value = paramList.at(i).section('=', 1, 1);
463         if (value == "%threads") value = QString::number(QThread::idealThreadCount());
464         tmp2 = qstrdup(value.toUtf8().constData());
465         renderProps->set(tmp, tmp2);
466         delete[] tmp;
467         delete[] tmp2;
468     }
469     mlt_properties consumerProperties = m_mltConsumer->get_properties();
470     mlt_properties_set_data(consumerProperties, "0", renderProps->get_properties(), 0, (mlt_destructor) mlt_properties_close, NULL);
471     
472     if (m_livePreview) 
473     {
474         // user wants live preview
475         Mlt::Properties *previewProps = new Mlt::Properties;
476         QString videoDriver = KdenliveSettings::videodrivername();
477         if (!videoDriver.isEmpty()) {
478             if (videoDriver == "x11_noaccel") {
479                 setenv("SDL_VIDEO_YUV_HWACCEL", "0", 1);
480                 videoDriver = "x11";
481             } else {
482                 unsetenv("SDL_VIDEO_YUV_HWACCEL");
483             }
484         }
485         setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 1);
486         
487         if (m_winid == 0) {
488             // OpenGL monitor
489             previewProps->set("mlt_service", "sdl_audio");
490             previewProps->set("preview_off", 1);
491             previewProps->set("preview_format", mlt_image_rgb24);
492             previewProps->set("terminate_on_pause", 0);
493             m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_gl_frame_show);
494         } else {
495             previewProps->set("mlt_service", "sdl_preview");
496             previewProps->set("window_id", m_winid);
497             previewProps->set("terminate_on_pause", 0);
498             //m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) rec_consumer_frame_preview);
499         }
500         //m_mltConsumer->set("resize", 1);
501         previewProps->set("window_background", KdenliveSettings::window_background().name().toUtf8().constData());
502         QString audioDevice = KdenliveSettings::audiodevicename();
503         if (!audioDevice.isEmpty())
504             previewProps->set("audio_device", audioDevice.toUtf8().constData());
505
506         if (!videoDriver.isEmpty())
507             previewProps->set("video_driver", videoDriver.toUtf8().constData());
508
509         QString audioDriver = KdenliveSettings::audiodrivername();
510
511         if (!audioDriver.isEmpty())
512             previewProps->set("audio_driver", audioDriver.toUtf8().constData());
513         
514         previewProps->set("real_time", "0");
515         previewProps->set("mlt_profile", m_activeProfile.toUtf8().constData());
516         mlt_properties_set_data(consumerProperties, "1", previewProps->get_properties(), 0, (mlt_destructor) mlt_properties_close, NULL);
517         //m_showFrameEvent = m_mltConsumer->listen("consumer-frame-render", this, (mlt_listener) rec_consumer_frame_show);
518     }
519     else {
520         
521     }
522     
523     if (xmlPlaylist) {
524         // create an xml producer
525         m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", playlist.toUtf8().constData());
526     }
527     else {
528         // create a producer based on mltproducer parameter
529         m_mltProducer = new Mlt::Producer(*m_mltProfile, playlist.toUtf8().constData());
530     }
531
532     if (m_mltProducer == NULL || !m_mltProducer->is_valid()) {
533         kDebug()<<"//// ERROR CREATRING PROD";
534         if (m_mltConsumer) {
535             delete m_mltConsumer;
536             m_mltConsumer = NULL;
537         }
538         if (m_mltProducer) {
539             delete m_mltProducer;
540             m_mltProducer = NULL;
541         }
542         return false;
543     }
544     
545     m_mltConsumer->connect(*m_mltProducer);
546     if (m_mltConsumer->start() == -1) {
547         if (m_showFrameEvent) delete m_showFrameEvent;
548         m_showFrameEvent = NULL;
549         delete m_mltConsumer;
550         m_mltConsumer = NULL;
551         return 0;
552     }
553     m_droppedFramesTimer.start();
554     return 1;
555 }
556
557
558 void MltDeviceCapture::setOverlay(const QString &path)
559 {
560     if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return;
561     Mlt::Producer parentProd(m_mltProducer->parent());
562     if (parentProd.get_producer() == NULL) {
563         kDebug() << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////";
564         return;
565     }
566
567     Mlt::Service service(parentProd.get_service());
568     if (service.type() != tractor_type) {
569         kWarning() << "// TRACTOR PROBLEM";
570         return;
571     }
572     Mlt::Tractor tractor(service);
573     if ( tractor.count() < 2) {
574         kWarning() << "// TRACTOR PROBLEM";
575         return;
576     }
577     mlt_service_lock(service.get_service());
578     Mlt::Producer trackProducer(tractor.track(0));
579     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
580
581     trackPlaylist.remove(0);
582     if (path.isEmpty()) {
583         mlt_service_unlock(service.get_service());
584         return;
585     }
586
587     // Add overlay clip
588     char *tmp = qstrdup(path.toUtf8().constData());
589     Mlt::Producer *clip = new Mlt::Producer (*m_mltProfile, "loader", tmp);
590     delete[] tmp;
591     clip->set_in_and_out(0, 99999);
592     trackPlaylist.insert_at(0, clip, 1);
593
594     // Add transition
595     mlt_service serv = m_mltProducer->parent().get_service();
596     mlt_service nextservice = mlt_service_get_producer(serv);
597     mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
598     QString mlt_type = mlt_properties_get(properties, "mlt_type");
599     if (mlt_type != "transition") {
600         // transition does not exist, add it
601         Mlt::Field *field = tractor.field();
602         Mlt::Transition *transition = new Mlt::Transition(*m_mltProfile, "composite");
603         transition->set_in_and_out(0, 0);
604         transition->set("geometry", "0/0:100%x100%:70");
605         transition->set("fill", 1);
606         transition->set("operator", "and");
607         transition->set("a_track", 0);
608         transition->set("b_track", 1);
609         field->plant_transition(*transition, 0, 1);
610     }
611     mlt_service_unlock(service.get_service());
612     //delete clip;
613 }
614
615 void MltDeviceCapture::setOverlayEffect(const QString &tag, QStringList parameters)
616 {
617     if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return;
618     Mlt::Service service(m_mltProducer->parent().get_service());
619     Mlt::Tractor tractor(service);
620     Mlt::Producer trackProducer(tractor.track(0));
621     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
622     Mlt::Service trackService(trackProducer.get_service());
623
624     mlt_service_lock(service.get_service());
625
626     // delete previous effects
627     Mlt::Filter *filter;
628     filter = trackService.filter(0);
629     if (filter && !tag.isEmpty()) {
630         QString currentService = filter->get("mlt_service");
631         if (currentService == tag) {
632             // Effect is already there
633             mlt_service_unlock(service.get_service());
634             return;
635         }
636     }
637     while (filter) {
638         trackService.detach(*filter);
639         delete filter;
640         filter = trackService.filter(0);
641     }
642     
643     if (tag.isEmpty()) {
644         mlt_service_unlock(service.get_service());
645         return;
646     }
647     
648     char *tmp = qstrdup(tag.toUtf8().constData());
649     filter = new Mlt::Filter(*m_mltProfile, tmp);
650     delete[] tmp;
651     if (filter && filter->is_valid()) {
652         for (int j = 0; j < parameters.count(); j++) {
653             filter->set(parameters.at(j).section('=', 0, 0).toUtf8().constData(), parameters.at(j).section('=', 1, 1).toUtf8().constData());
654         }
655         trackService.attach(*filter);
656     }
657     mlt_service_unlock(service.get_service());
658 }
659
660 void MltDeviceCapture::mirror(bool activate)
661 {
662     if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return;
663     Mlt::Service service(m_mltProducer->parent().get_service());
664     Mlt::Tractor tractor(service);
665     Mlt::Producer trackProducer(tractor.track(1));
666     Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
667     Mlt::Service trackService(trackProducer.get_service());
668
669     mlt_service_lock(service.get_service());
670
671     // delete previous effects
672     Mlt::Filter *filter;
673     filter = trackService.filter(0);
674     while (filter) {
675         trackService.detach(*filter);
676         delete filter;
677         filter = trackService.filter(0);
678     }
679
680     if (!activate) {
681         mlt_service_unlock(service.get_service());
682         return;
683     }
684
685     filter = new Mlt::Filter(*m_mltProfile, "mirror");
686     if (filter && filter->is_valid()) {
687         filter->set("mirror", "flip");
688         trackService.attach(*filter);
689     }
690     mlt_service_unlock(service.get_service());
691 }
692
693 void MltDeviceCapture::uyvy2rgb(unsigned char *yuv_buffer, int width, int height)
694 {
695     processingImage = true;
696     QImage image(width, height, QImage::Format_RGB888);
697     unsigned char *rgb_buffer = image.bits();    
698
699     int len;
700     int r, g, b;
701     int Y, U, V, Y2;
702     int rgb_ptr, y_ptr, t;
703
704     len = width * height / 2;
705
706     rgb_ptr = 0;
707     y_ptr = 0;
708
709     for (t = 0; t < len; t++) { 
710       
711
712         Y = yuv_buffer[y_ptr];
713         U = yuv_buffer[y_ptr+1];
714         Y2 = yuv_buffer[y_ptr+2];
715         V = yuv_buffer[y_ptr+3];
716         y_ptr += 4;
717
718         r = ((298 * (Y - 16)               + 409 * (V - 128) + 128) >> 8);
719
720         g = ((298 * (Y - 16) - 100 * (U - 128) - 208 * (V - 128) + 128) >> 8);
721
722         b = ((298 * (Y - 16) + 516 * (U - 128)               + 128) >> 8);
723
724         if (r > 255) r = 255;
725         if (g > 255) g = 255;
726         if (b > 255) b = 255;
727
728         if (r < 0) r = 0;
729         if (g < 0) g = 0;
730         if (b < 0) b = 0;
731
732         rgb_buffer[rgb_ptr] = r;
733         rgb_buffer[rgb_ptr+1] = g;
734         rgb_buffer[rgb_ptr+2] = b;
735         rgb_ptr += 3;
736
737
738         r = ((298 * (Y2 - 16)               + 409 * (V - 128) + 128) >> 8);
739
740         g = ((298 * (Y2 - 16) - 100 * (U - 128) - 208 * (V - 128) + 128) >> 8);
741
742         b = ((298 * (Y2 - 16) + 516 * (U - 128)               + 128) >> 8);
743
744         if (r > 255) r = 255;
745         if (g > 255) g = 255;
746         if (b > 255) b = 255;
747
748         if (r < 0) r = 0;
749         if (g < 0) g = 0;
750         if (b < 0) b = 0;
751
752         rgb_buffer[rgb_ptr] = r;
753         rgb_buffer[rgb_ptr+1] = g;
754         rgb_buffer[rgb_ptr+2] = b;
755         rgb_ptr += 3;
756     }
757     //emit imageReady(image);
758     //m_captureDisplayWidget->setImage(image);
759     emit unblockPreview();
760     //processingImage = false;
761 }
762
763 void MltDeviceCapture::slotPreparePreview()
764 {
765     QTimer::singleShot(1000, this, SLOT(slotAllowPreview()));
766 }
767
768 void MltDeviceCapture::slotAllowPreview()
769 {
770     processingImage = false;
771 }
772
773