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