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