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