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