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