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