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