1 /***************************************************************************
2 mltdevicecapture.cpp - description
4 begin : Sun May 21 2011
5 copyright : (C) 2011 by Jean-Baptiste Mardelle (jb@kdenlive.org)
7 ***************************************************************************/
9 /***************************************************************************
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. *
16 ***************************************************************************/
19 #include "mltdevicecapture.h"
20 #include "kdenlivesettings.h"
21 #include "definitions.h"
23 #include <mlt++/Mlt.h>
26 #include <KStandardDirs>
27 #include <KMessageBox>
29 #include <KTemporaryFile>
34 #include <QApplication>
44 static void consumer_gl_frame_show(mlt_consumer, MltDeviceCapture * self, mlt_frame frame_ptr)
46 // detect if the producer has finished playing. Is there a better way to do it?
47 Mlt::Frame frame(frame_ptr);
48 self->showFrame(frame);
51 /*static void rec_consumer_frame_show(mlt_consumer, MltDeviceCapture * self, mlt_frame frame_ptr)
53 Mlt::Frame frame(frame_ptr);
54 if (!frame.is_valid()) return;
55 self->gotCapturedFrame(frame);
58 static void rec_consumer_frame_preview(mlt_consumer, MltDeviceCapture * self, mlt_frame frame_ptr)
60 Mlt::Frame frame(frame_ptr);
61 if (!frame.is_valid()) return;
62 if (self->sendFrameForAnalysis && frame_ptr->convert_image) {
63 self->emitFrameUpdated(frame);
65 if (self->doCapture > 0) {
67 if (self->doCapture == 0) self->saveFrame(frame);
70 //TODO: connect record monitor to audio scopes
72 if (self->analyseAudio) {
73 self->showAudio(frame);
79 MltDeviceCapture::MltDeviceCapture(QString profile, VideoSurface *surface, QWidget *parent) :
80 AbstractRender(Kdenlive::recordMonitor, parent),
82 sendFrameForAnalysis(false),
83 processingImage(false),
87 m_showFrameEvent(NULL),
89 m_livePreview(KdenliveSettings::enable_recording_preview()),
90 m_winid((int) surface->winId())
92 m_captureDisplayWidget = surface;
93 analyseAudio = KdenliveSettings::monitor_audio();
94 if (profile.isEmpty()) profile = KdenliveSettings::current_profile();
95 buildConsumer(profile);
96 connect(this, SIGNAL(unblockPreview()), this, SLOT(slotPreparePreview()));
97 m_droppedFramesTimer.setSingleShot(false);
98 m_droppedFramesTimer.setInterval(1000);
99 connect(&m_droppedFramesTimer, SIGNAL(timeout()), this, SLOT(slotCheckDroppedFrames()));
102 MltDeviceCapture::~MltDeviceCapture()
104 if (m_mltConsumer) delete m_mltConsumer;
105 if (m_mltProducer) delete m_mltProducer;
106 if (m_mltProfile) delete m_mltProfile;
109 void MltDeviceCapture::buildConsumer(const QString &profileName)
111 if (!profileName.isEmpty()) m_activeProfile = profileName;
113 if (m_mltProfile) delete m_mltProfile;
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);
121 QString videoDriver = KdenliveSettings::videodrivername();
122 if (!videoDriver.isEmpty()) {
123 if (videoDriver == "x11_noaccel") {
124 setenv("SDL_VIDEO_YUV_HWACCEL", "0", 1);
127 unsetenv("SDL_VIDEO_YUV_HWACCEL");
130 setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 1);
135 m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "sdl_audio");
136 m_mltConsumer->set("preview_off", 1);
137 m_mltConsumer->set("preview_format", mlt_image_rgb24);
138 m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_gl_frame_show);
140 m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "sdl_preview");
141 m_mltConsumer->set("window_id", m_winid);
142 m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) rec_consumer_frame_preview);
144 //m_mltConsumer->set("resize", 1);
145 //m_mltConsumer->set("terminate_on_pause", 1);
146 m_mltConsumer->set("window_background", KdenliveSettings::window_background().name().toUtf8().constData());
147 //m_mltConsumer->set("rescale", "nearest");
149 QString audioDevice = KdenliveSettings::audiodevicename();
150 if (!audioDevice.isEmpty())
151 m_mltConsumer->set("audio_device", audioDevice.toUtf8().constData());
153 if (!videoDriver.isEmpty())
154 m_mltConsumer->set("video_driver", videoDriver.toUtf8().constData());
156 QString audioDriver = KdenliveSettings::audiodrivername();
158 if (!audioDriver.isEmpty())
159 m_mltConsumer->set("audio_driver", audioDriver.toUtf8().constData());
161 //m_mltConsumer->set("progressive", 0);
162 //m_mltConsumer->set("buffer", 1);
163 //m_mltConsumer->set("real_time", 0);
166 void MltDeviceCapture::pause()
169 m_mltConsumer->set("refresh", 0);
170 //m_mltProducer->set_speed(0.0);
171 m_mltConsumer->purge();
175 void MltDeviceCapture::stop()
177 m_droppedFramesTimer.stop();
178 bool isPlaylist = false;
179 //disconnect(this, SIGNAL(imageReady(QImage)), this, SIGNAL(frameUpdated(QImage)));
180 //m_captureDisplayWidget->stop();
182 if (m_showFrameEvent) delete m_showFrameEvent;
183 m_showFrameEvent = NULL;
186 m_mltConsumer->set("refresh", 0);
187 m_mltConsumer->stop();
188 //if (!m_mltConsumer->is_stopped()) m_mltConsumer->stop();
191 QList <Mlt::Producer *> prods;
192 Mlt::Service service(m_mltProducer->parent().get_service());
193 mlt_service_lock(service.get_service());
194 if (service.type() == tractor_type) {
196 Mlt::Tractor tractor(service);
197 mlt_tractor_close(tractor.get_tractor());
198 Mlt::Field *field = tractor.field();
199 mlt_service nextservice = mlt_service_get_producer(service.get_service());
200 mlt_service nextservicetodisconnect;
201 mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
202 QString mlt_type = mlt_properties_get(properties, "mlt_type");
203 QString resource = mlt_properties_get(properties, "mlt_service");
204 // Delete all transitions
205 while (mlt_type == "transition") {
206 nextservicetodisconnect = nextservice;
207 nextservice = mlt_service_producer(nextservice);
208 mlt_field_disconnect_service(field->get_field(), nextservicetodisconnect);
209 if (nextservice == NULL) break;
210 properties = MLT_SERVICE_PROPERTIES(nextservice);
211 mlt_type = mlt_properties_get(properties, "mlt_type");
212 resource = mlt_properties_get(properties, "mlt_service");
217 mlt_service_unlock(service.get_service());
218 delete m_mltProducer;
219 m_mltProducer = NULL;
221 // For some reason, the consumer seems to be deleted by previous stuff when in playlist mode
222 if (!isPlaylist && m_mltConsumer) delete m_mltConsumer;
223 m_mltConsumer = NULL;
227 void MltDeviceCapture::slotDoRefresh()
229 QMutexLocker locker(&m_mutex);
233 if (m_mltConsumer->is_stopped()) m_mltConsumer->start();
234 m_mltConsumer->purge();
235 m_mltConsumer->set("refresh", 1);
240 void MltDeviceCapture::emitFrameUpdated(Mlt::Frame& frame)
243 //TEST: is it better to convert the frame in a thread outside of MLT??
244 if (processingImage) return;
245 mlt_image_format format = (mlt_image_format) frame.get_int("format"); //mlt_image_rgb24;
246 int width = frame.get_int("width");
247 int height = frame.get_int("height");
248 unsigned char *buffer = (unsigned char *) frame.get_data("image");
249 if (format == mlt_image_yuv422) {
250 QtConcurrent::run(this, &MltDeviceCapture::uyvy2rgb, (unsigned char *) buffer, width, height);
254 mlt_image_format format = mlt_image_rgb24a;
257 const uchar* image = frame.get_image(format, width, height);
258 QImage qimage(width, height, QImage::Format_ARGB32_Premultiplied);
259 memcpy(qimage.bits(), image, width * height * 4);
260 emit frameUpdated(qimage.rgbSwapped());
263 void MltDeviceCapture::showFrame(Mlt::Frame& frame)
265 mlt_image_format format = mlt_image_rgb24;
268 const uchar* image = frame.get_image(format, width, height);
269 QImage qimage(width, height, QImage::Format_RGB888);
270 memcpy(qimage.scanLine(0), image, width * height * 3);
271 emit showImageSignal(qimage);
273 if (sendFrameForAnalysis && frame.get_frame()->convert_image) {
274 emit frameUpdated(qimage.rgbSwapped());
278 void MltDeviceCapture::showAudio(Mlt::Frame& frame)
280 if (!frame.is_valid() || frame.get_int("test_audio") != 0) {
283 mlt_audio_format audio_format = mlt_audio_s16;
285 int num_channels = 0;
287 int16_t* data = (int16_t*)frame.get_audio(audio_format, freq, num_channels, samples);
293 // Data format: [ c00 c10 c01 c11 c02 c12 c03 c13 ... c0{samples-1} c1{samples-1} for 2 channels.
294 // So the vector is of size samples*channels.
295 QVector<int16_t> sampleVector(samples*num_channels);
296 memcpy(sampleVector.data(), data, samples*num_channels*sizeof(int16_t));
298 emit audioSamplesSignal(sampleVector, freq, num_channels, samples);
302 bool MltDeviceCapture::slotStartPreview(const QString &producer, bool xmlFormat)
304 if (m_mltConsumer == NULL) {
307 char *tmp = qstrdup(producer.toUtf8().constData());
308 if (xmlFormat) m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", tmp);
309 else m_mltProducer = new Mlt::Producer(*m_mltProfile, tmp);
312 if (m_mltProducer == NULL || !m_mltProducer->is_valid()) {
314 delete m_mltProducer;
315 m_mltProducer = NULL;
317 kDebug()<<"//// ERROR CREATRING PROD";
320 m_mltConsumer->connect(*m_mltProducer);
321 if (m_mltConsumer->start() == -1) {
322 delete m_mltConsumer;
323 m_mltConsumer = NULL;
326 m_droppedFramesTimer.start();
327 //connect(this, SIGNAL(imageReady(QImage)), this, SIGNAL(frameUpdated(QImage)));
331 void MltDeviceCapture::slotCheckDroppedFrames()
334 int dropped = m_mltProducer->get_int("dropped");
335 if (dropped > m_droppedFrames) {
336 m_droppedFrames = dropped;
337 emit droppedFrames(m_droppedFrames);
342 void MltDeviceCapture::gotCapturedFrame(Mlt::Frame& frame)
345 int dropped = m_mltProducer->get_int("dropped");
346 if (dropped > m_droppedFrames) {
347 m_droppedFrames = dropped;
348 emit droppedFrames(m_droppedFrames);
352 if (!m_livePreview) return;
353 //if (m_livePreview == 0 && (m_frameCount % 10 > 0)) return;
354 mlt_image_format format = mlt_image_rgb24;
357 uint8_t *data = frame.get_image(format, width, height, 0);
358 //QImage image(width, height, QImage::Format_RGB888);
359 //memcpy(image.bits(), data, width * height * 3);
360 QImage image((uchar *)data, width, height, QImage::Format_RGB888);
362 //m_captureDisplayWidget->setImage(image);
364 //TEST: is it better to process frame conversion ouside MLT???
366 if (!m_livePreview || processingImage) return;
368 mlt_image_format format = (mlt_image_format) frame.get_int("format"); //mlt_image_rgb24a;
369 int width = frame.get_int("width");
370 int height = frame.get_int("height");
371 unsigned char *buffer = (unsigned char *) frame.get_data("image");
372 //unsigned char *buffer = frame.get_image(format, width, height);
373 //convert from uyvy422 to rgba
374 if (format == mlt_image_yuv422) {
375 QtConcurrent::run(this, &MltDeviceCapture::uyvy2rgb, (unsigned char *) buffer, width, height);
376 //CaptureHandler::uyvy2rgb((uchar *)frameBytes, (uchar *)image.bits(), videoFrame->GetWidth(), videoFrame->GetHeight());
380 void MltDeviceCapture::saveFrame(Mlt::Frame& frame)
382 mlt_image_format format = mlt_image_rgb24;
385 const uchar* image = frame.get_image(format, width, height);
386 QImage qimage(width, height, QImage::Format_RGB888);
387 memcpy(qimage.bits(), image, width * height * 3);
390 Mlt::Service service(m_mltProducer->parent().get_service());
391 Mlt::Tractor tractor(service);
392 Mlt::Producer trackProducer(tractor.track(0));
393 trackProducer.set("hide", 0);
395 qimage.save(m_capturePath);
396 emit frameSaved(m_capturePath);
397 m_capturePath.clear();
400 void MltDeviceCapture::captureFrame(const QString &path)
402 if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return;
404 // Hide overlay track before doing the capture
405 Mlt::Service service(m_mltProducer->parent().get_service());
406 Mlt::Tractor tractor(service);
407 Mlt::Producer trackProducer(tractor.track(0));
408 mlt_service_lock(service.get_service());
409 trackProducer.set("hide", 1);
410 m_mltConsumer->purge();
411 mlt_service_unlock(service.get_service());
412 m_capturePath = path;
413 // Wait for 5 frames before capture to make sure overlay is gone
417 bool MltDeviceCapture::slotStartCapture(const QString ¶ms, const QString &path, const QString &playlist, bool livePreview, bool xmlPlaylist)
420 m_livePreview = livePreview;
423 if (m_mltProfile) delete m_mltProfile;
424 char *tmp = qstrdup(m_activeProfile.toUtf8().constData());
425 m_mltProfile = new Mlt::Profile(tmp);
427 //m_mltProfile->get_profile()->is_explicit = 1;
430 /*kDebug()<<"-- CREATING CAP: "<<params<<", PATH: "<<path;
431 tmp = qstrdup(QString("avformat:" + path).toUtf8().constData());
432 m_mltConsumer = new Mlt::Consumer(*m_mltProfile, tmp);
433 m_mltConsumer->set("real_time", -KdenliveSettings::mltthreads());
436 m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "multi");
437 if (m_mltConsumer == NULL || !m_mltConsumer->is_valid()) {
439 delete m_mltConsumer;
440 m_mltConsumer = NULL;
445 m_winid = (int) m_captureDisplayWidget->winId();
447 // Create multi consumer setup
448 Mlt::Properties *renderProps = new Mlt::Properties;
449 renderProps->set("mlt_service", "avformat");
450 renderProps->set("target", path.toUtf8().constData());
451 renderProps->set("real_time", -KdenliveSettings::mltthreads());
452 renderProps->set("terminate_on_pause", 0);
453 renderProps->set("mlt_profile", m_activeProfile.toUtf8().constData());
456 QStringList paramList = params.split(' ', QString::SkipEmptyParts);
458 for (int i = 0; i < paramList.count(); i++) {
459 tmp = qstrdup(paramList.at(i).section('=', 0, 0).toUtf8().constData());
460 QString value = paramList.at(i).section('=', 1, 1);
461 if (value == "%threads") value = QString::number(QThread::idealThreadCount());
462 tmp2 = qstrdup(value.toUtf8().constData());
463 renderProps->set(tmp, tmp2);
467 mlt_properties consumerProperties = m_mltConsumer->get_properties();
468 mlt_properties_set_data(consumerProperties, "0", renderProps->get_properties(), 0, (mlt_destructor) mlt_properties_close, NULL);
472 // user wants live preview
473 Mlt::Properties *previewProps = new Mlt::Properties;
474 QString videoDriver = KdenliveSettings::videodrivername();
475 if (!videoDriver.isEmpty()) {
476 if (videoDriver == "x11_noaccel") {
477 setenv("SDL_VIDEO_YUV_HWACCEL", "0", 1);
480 unsetenv("SDL_VIDEO_YUV_HWACCEL");
483 setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 1);
487 previewProps->set("mlt_service", "sdl_audio");
488 previewProps->set("preview_off", 1);
489 previewProps->set("preview_format", mlt_image_rgb24);
490 previewProps->set("terminate_on_pause", 0);
491 m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_gl_frame_show);
493 previewProps->set("mlt_service", "sdl_preview");
494 previewProps->set("window_id", m_winid);
495 previewProps->set("terminate_on_pause", 0);
496 //m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) rec_consumer_frame_preview);
498 //m_mltConsumer->set("resize", 1);
499 previewProps->set("window_background", KdenliveSettings::window_background().name().toUtf8().constData());
500 QString audioDevice = KdenliveSettings::audiodevicename();
501 if (!audioDevice.isEmpty())
502 previewProps->set("audio_device", audioDevice.toUtf8().constData());
504 if (!videoDriver.isEmpty())
505 previewProps->set("video_driver", videoDriver.toUtf8().constData());
507 QString audioDriver = KdenliveSettings::audiodrivername();
509 if (!audioDriver.isEmpty())
510 previewProps->set("audio_driver", audioDriver.toUtf8().constData());
512 previewProps->set("real_time", "0");
513 previewProps->set("mlt_profile", m_activeProfile.toUtf8().constData());
514 mlt_properties_set_data(consumerProperties, "1", previewProps->get_properties(), 0, (mlt_destructor) mlt_properties_close, NULL);
515 //m_showFrameEvent = m_mltConsumer->listen("consumer-frame-render", this, (mlt_listener) rec_consumer_frame_show);
521 tmp = qstrdup(playlist.toUtf8().constData());
523 // create an xml producer
524 m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", tmp);
527 // create a producer based on mltproducer parameter
528 m_mltProducer = new Mlt::Producer(*m_mltProfile, tmp);
532 if (m_mltProducer == NULL || !m_mltProducer->is_valid()) {
533 kDebug()<<"//// ERROR CREATRING PROD";
537 m_mltConsumer->connect(*m_mltProducer);
538 if (m_mltConsumer->start() == -1) {
539 if (m_showFrameEvent) delete m_showFrameEvent;
540 m_showFrameEvent = NULL;
541 delete m_mltConsumer;
542 m_mltConsumer = NULL;
545 m_droppedFramesTimer.start();
550 void MltDeviceCapture::setOverlay(const QString &path)
552 if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return;
553 Mlt::Producer parentProd(m_mltProducer->parent());
554 if (parentProd.get_producer() == NULL) {
555 kDebug() << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////";
559 Mlt::Service service(parentProd.get_service());
560 if (service.type() != tractor_type) {
561 kWarning() << "// TRACTOR PROBLEM";
564 Mlt::Tractor tractor(service);
565 if ( tractor.count() < 2) {
566 kWarning() << "// TRACTOR PROBLEM";
569 mlt_service_lock(service.get_service());
570 Mlt::Producer trackProducer(tractor.track(0));
571 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
573 trackPlaylist.remove(0);
574 if (path.isEmpty()) {
575 mlt_service_unlock(service.get_service());
580 char *tmp = qstrdup(path.toUtf8().constData());
581 Mlt::Producer *clip = new Mlt::Producer (*m_mltProfile, "loader", tmp);
583 clip->set_in_and_out(0, 99999);
584 trackPlaylist.insert_at(0, clip, 1);
587 mlt_service serv = m_mltProducer->parent().get_service();
588 mlt_service nextservice = mlt_service_get_producer(serv);
589 mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
590 QString mlt_type = mlt_properties_get(properties, "mlt_type");
591 if (mlt_type != "transition") {
592 // transition does not exist, add it
593 Mlt::Field *field = tractor.field();
594 Mlt::Transition *transition = new Mlt::Transition(*m_mltProfile, "composite");
595 transition->set_in_and_out(0, 0);
596 transition->set("geometry", "0/0:100%x100%:70");
597 transition->set("fill", 1);
598 transition->set("operator", "and");
599 transition->set("a_track", 0);
600 transition->set("b_track", 1);
601 field->plant_transition(*transition, 0, 1);
603 mlt_service_unlock(service.get_service());
607 void MltDeviceCapture::setOverlayEffect(const QString &tag, QStringList parameters)
609 if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return;
610 Mlt::Service service(m_mltProducer->parent().get_service());
611 Mlt::Tractor tractor(service);
612 Mlt::Producer trackProducer(tractor.track(0));
613 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
614 Mlt::Service trackService(trackProducer.get_service());
616 mlt_service_lock(service.get_service());
618 // delete previous effects
620 filter = trackService.filter(0);
621 if (filter && !tag.isEmpty()) {
622 QString currentService = filter->get("mlt_service");
623 if (currentService == tag) {
624 // Effect is already there
625 mlt_service_unlock(service.get_service());
630 trackService.detach(*filter);
632 filter = trackService.filter(0);
636 mlt_service_unlock(service.get_service());
640 char *tmp = qstrdup(tag.toUtf8().constData());
641 filter = new Mlt::Filter(*m_mltProfile, tmp);
643 if (filter && filter->is_valid()) {
644 for (int j = 0; j < parameters.count(); j++) {
645 filter->set(parameters.at(j).section('=', 0, 0).toUtf8().constData(), parameters.at(j).section('=', 1, 1).toUtf8().constData());
647 trackService.attach(*filter);
649 mlt_service_unlock(service.get_service());
652 void MltDeviceCapture::mirror(bool activate)
654 if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return;
655 Mlt::Service service(m_mltProducer->parent().get_service());
656 Mlt::Tractor tractor(service);
657 Mlt::Producer trackProducer(tractor.track(1));
658 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
659 Mlt::Service trackService(trackProducer.get_service());
661 mlt_service_lock(service.get_service());
663 // delete previous effects
665 filter = trackService.filter(0);
667 trackService.detach(*filter);
669 filter = trackService.filter(0);
673 mlt_service_unlock(service.get_service());
677 filter = new Mlt::Filter(*m_mltProfile, "mirror");
678 if (filter && filter->is_valid()) {
679 filter->set("mirror", "flip");
680 trackService.attach(*filter);
682 mlt_service_unlock(service.get_service());
685 void MltDeviceCapture::uyvy2rgb(unsigned char *yuv_buffer, int width, int height)
687 processingImage = true;
688 QImage image(width, height, QImage::Format_RGB888);
689 unsigned char *rgb_buffer = image.bits();
694 int rgb_ptr, y_ptr, t;
696 len = width * height / 2;
701 for (t = 0; t < len; t++) {
704 Y = yuv_buffer[y_ptr];
705 U = yuv_buffer[y_ptr+1];
706 Y2 = yuv_buffer[y_ptr+2];
707 V = yuv_buffer[y_ptr+3];
710 r = ((298 * (Y - 16) + 409 * (V - 128) + 128) >> 8);
712 g = ((298 * (Y - 16) - 100 * (U - 128) - 208 * (V - 128) + 128) >> 8);
714 b = ((298 * (Y - 16) + 516 * (U - 128) + 128) >> 8);
716 if (r > 255) r = 255;
717 if (g > 255) g = 255;
718 if (b > 255) b = 255;
724 rgb_buffer[rgb_ptr] = r;
725 rgb_buffer[rgb_ptr+1] = g;
726 rgb_buffer[rgb_ptr+2] = b;
730 r = ((298 * (Y2 - 16) + 409 * (V - 128) + 128) >> 8);
732 g = ((298 * (Y2 - 16) - 100 * (U - 128) - 208 * (V - 128) + 128) >> 8);
734 b = ((298 * (Y2 - 16) + 516 * (U - 128) + 128) >> 8);
736 if (r > 255) r = 255;
737 if (g > 255) g = 255;
738 if (b > 255) b = 255;
744 rgb_buffer[rgb_ptr] = r;
745 rgb_buffer[rgb_ptr+1] = g;
746 rgb_buffer[rgb_ptr+2] = b;
749 //emit imageReady(image);
750 //m_captureDisplayWidget->setImage(image);
751 emit unblockPreview();
752 //processingImage = false;
755 void MltDeviceCapture::slotPreparePreview()
757 QTimer::singleShot(1000, this, SLOT(slotAllowPreview()));
760 void MltDeviceCapture::slotAllowPreview()
762 processingImage = false;