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, VideoPreviewContainer *surface, QWidget *parent) :
80 AbstractRender("capture", parent),
82 sendFrameForAnalysis(false),
83 processingImage(false),
87 m_showFrameEvent(NULL),
89 m_livePreview(KdenliveSettings::enable_recording_preview()),
90 m_captureDisplayWidget(surface),
91 m_winid((int) surface->winId())
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::stop()
168 m_droppedFramesTimer.stop();
169 bool isPlaylist = false;
170 disconnect(this, SIGNAL(imageReady(QImage)), this, SIGNAL(frameUpdated(QImage)));
171 m_captureDisplayWidget->stop();
173 if (m_showFrameEvent) delete m_showFrameEvent;
174 m_showFrameEvent = NULL;
177 m_mltConsumer->set("refresh", 0);
178 m_mltConsumer->stop();
179 //if (!m_mltConsumer->is_stopped()) m_mltConsumer->stop();
182 QList <Mlt::Producer *> prods;
183 Mlt::Service service(m_mltProducer->parent().get_service());
184 mlt_service_lock(service.get_service());
185 if (service.type() == tractor_type) {
187 Mlt::Tractor tractor(service);
188 mlt_tractor_close(tractor.get_tractor());
189 Mlt::Field *field = tractor.field();
190 mlt_service nextservice = mlt_service_get_producer(service.get_service());
191 mlt_service nextservicetodisconnect;
192 mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
193 QString mlt_type = mlt_properties_get(properties, "mlt_type");
194 QString resource = mlt_properties_get(properties, "mlt_service");
195 // Delete all transitions
196 while (mlt_type == "transition") {
197 nextservicetodisconnect = nextservice;
198 nextservice = mlt_service_producer(nextservice);
199 mlt_field_disconnect_service(field->get_field(), nextservicetodisconnect);
200 if (nextservice == NULL) break;
201 properties = MLT_SERVICE_PROPERTIES(nextservice);
202 mlt_type = mlt_properties_get(properties, "mlt_type");
203 resource = mlt_properties_get(properties, "mlt_service");
208 mlt_service_unlock(service.get_service());
209 delete m_mltProducer;
210 m_mltProducer = NULL;
212 // For some reason, the consumer seems to be deleted by previous stuff when in playlist mode
213 if (!isPlaylist && m_mltConsumer) delete m_mltConsumer;
214 m_mltConsumer = NULL;
218 void MltDeviceCapture::doRefresh()
220 if (m_mltConsumer) m_mltConsumer->set("refresh", 1);
224 void MltDeviceCapture::emitFrameUpdated(Mlt::Frame& frame)
227 //TEST: is it better to convert the frame in a thread outside of MLT??
228 if (processingImage) return;
229 mlt_image_format format = (mlt_image_format) frame.get_int("format"); //mlt_image_rgb24;
230 int width = frame.get_int("width");
231 int height = frame.get_int("height");
232 unsigned char *buffer = (unsigned char *) frame.get_data("image");
233 if (format == mlt_image_yuv422) {
234 QtConcurrent::run(this, &MltDeviceCapture::uyvy2rgb, (unsigned char *) buffer, width, height);
238 mlt_image_format format = mlt_image_rgb24;
241 const uchar* image = frame.get_image(format, width, height);
242 QImage qimage(width, height, QImage::Format_ARGB32);
243 memcpy(qimage.bits(), image, width * height * 3);
244 emit frameUpdated(qimage.rgbSwapped());
247 void MltDeviceCapture::showFrame(Mlt::Frame& frame)
249 mlt_image_format format = mlt_image_rgb24;
252 const uchar* image = frame.get_image(format, width, height);
253 QImage qimage(width, height, QImage::Format_RGB888);
254 memcpy(qimage.scanLine(0), image, width * height * 3);
255 emit showImageSignal(qimage);
257 if (sendFrameForAnalysis && frame.get_frame()->convert_image) {
258 emit frameUpdated(qimage.rgbSwapped());
262 void MltDeviceCapture::showAudio(Mlt::Frame& frame)
264 if (!frame.is_valid() || frame.get_int("test_audio") != 0) {
267 mlt_audio_format audio_format = mlt_audio_s16;
269 int num_channels = 0;
271 int16_t* data = (int16_t*)frame.get_audio(audio_format, freq, num_channels, samples);
277 // Data format: [ c00 c10 c01 c11 c02 c12 c03 c13 ... c0{samples-1} c1{samples-1} for 2 channels.
278 // So the vector is of size samples*channels.
279 QVector<int16_t> sampleVector(samples*num_channels);
280 memcpy(sampleVector.data(), data, samples*num_channels*sizeof(int16_t));
282 emit audioSamplesSignal(sampleVector, freq, num_channels, samples);
286 bool MltDeviceCapture::slotStartPreview(const QString &producer, bool xmlFormat)
288 if (m_mltConsumer == NULL) {
291 char *tmp = qstrdup(producer.toUtf8().constData());
292 if (xmlFormat) m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", tmp);
293 else m_mltProducer = new Mlt::Producer(*m_mltProfile, tmp);
296 if (m_mltProducer == NULL || !m_mltProducer->is_valid()) {
298 delete m_mltProducer;
299 m_mltProducer = NULL;
301 kDebug()<<"//// ERROR CREATRING PROD";
304 m_mltConsumer->connect(*m_mltProducer);
305 if (m_mltConsumer->start() == -1) {
306 delete m_mltConsumer;
307 m_mltConsumer = NULL;
310 m_droppedFramesTimer.start();
311 connect(this, SIGNAL(imageReady(QImage)), this, SIGNAL(frameUpdated(QImage)));
315 void MltDeviceCapture::slotCheckDroppedFrames()
318 int dropped = m_mltProducer->get_int("dropped");
319 if (dropped > m_droppedFrames) {
320 m_droppedFrames = dropped;
321 emit droppedFrames(m_droppedFrames);
326 void MltDeviceCapture::gotCapturedFrame(Mlt::Frame& frame)
329 int dropped = m_mltProducer->get_int("dropped");
330 if (dropped > m_droppedFrames) {
331 m_droppedFrames = dropped;
332 emit droppedFrames(m_droppedFrames);
336 if (!m_livePreview) return;
337 //if (m_livePreview == 0 && (m_frameCount % 10 > 0)) return;
338 mlt_image_format format = mlt_image_rgb24;
341 uint8_t *data = frame.get_image(format, width, height, 0);
342 //QImage image(width, height, QImage::Format_RGB888);
343 //memcpy(image.bits(), data, width * height * 3);
344 QImage image((uchar *)data, width, height, QImage::Format_RGB888);
346 m_captureDisplayWidget->setImage(image);
348 //TEST: is it better to process frame conversion ouside MLT???
350 if (!m_livePreview || processingImage) return;
352 mlt_image_format format = (mlt_image_format) frame.get_int("format"); //mlt_image_rgb24a;
353 int width = frame.get_int("width");
354 int height = frame.get_int("height");
355 unsigned char *buffer = (unsigned char *) frame.get_data("image");
356 //unsigned char *buffer = frame.get_image(format, width, height);
357 //convert from uyvy422 to rgba
358 if (format == mlt_image_yuv422) {
359 QtConcurrent::run(this, &MltDeviceCapture::uyvy2rgb, (unsigned char *) buffer, width, height);
360 //CaptureHandler::uyvy2rgb((uchar *)frameBytes, (uchar *)image.bits(), videoFrame->GetWidth(), videoFrame->GetHeight());
364 void MltDeviceCapture::saveFrame(Mlt::Frame& frame)
366 mlt_image_format format = mlt_image_rgb24;
369 const uchar* image = frame.get_image(format, width, height);
370 QImage qimage(width, height, QImage::Format_RGB888);
371 memcpy(qimage.bits(), image, width * height * 3);
374 Mlt::Service service(m_mltProducer->parent().get_service());
375 Mlt::Tractor tractor(service);
376 Mlt::Producer trackProducer(tractor.track(0));
377 trackProducer.set("hide", 0);
379 qimage.save(m_capturePath);
380 emit frameSaved(m_capturePath);
381 m_capturePath.clear();
384 void MltDeviceCapture::captureFrame(const QString &path)
386 if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return;
388 // Hide overlay track before doing the capture
389 Mlt::Service service(m_mltProducer->parent().get_service());
390 Mlt::Tractor tractor(service);
391 Mlt::Producer trackProducer(tractor.track(0));
392 mlt_service_lock(service.get_service());
393 trackProducer.set("hide", 1);
394 m_mltConsumer->purge();
395 mlt_service_unlock(service.get_service());
396 m_capturePath = path;
397 // Wait for 5 frames before capture to make sure overlay is gone
401 bool MltDeviceCapture::slotStartCapture(const QString ¶ms, const QString &path, const QString &playlist, bool livePreview, bool xmlPlaylist)
404 m_livePreview = livePreview;
407 if (m_mltProfile) delete m_mltProfile;
408 char *tmp = qstrdup(m_activeProfile.toUtf8().constData());
409 m_mltProfile = new Mlt::Profile(tmp);
411 //m_mltProfile->get_profile()->is_explicit = 1;
414 /*kDebug()<<"-- CREATING CAP: "<<params<<", PATH: "<<path;
415 tmp = qstrdup(QString("avformat:" + path).toUtf8().constData());
416 m_mltConsumer = new Mlt::Consumer(*m_mltProfile, tmp);
417 m_mltConsumer->set("real_time", -KdenliveSettings::mltthreads());
420 m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "multi");
421 if (m_mltConsumer == NULL || !m_mltConsumer->is_valid()) {
423 delete m_mltConsumer;
424 m_mltConsumer = NULL;
429 m_winid = (int) m_captureDisplayWidget->winId();
431 // Create multi consumer setup
432 Mlt::Properties *renderProps = new Mlt::Properties;
433 renderProps->set("mlt_service", "avformat");
434 renderProps->set("target", path.toUtf8().constData());
435 renderProps->set("real_time", -KdenliveSettings::mltthreads());
436 renderProps->set("terminate_on_pause", 0);
437 renderProps->set("mlt_profile", m_activeProfile.toUtf8().constData());
440 QStringList paramList = params.split(" ", QString::SkipEmptyParts);
442 for (int i = 0; i < paramList.count(); i++) {
443 tmp = qstrdup(paramList.at(i).section("=", 0, 0).toUtf8().constData());
444 QString value = paramList.at(i).section("=", 1, 1);
445 if (value == "%threads") value = QString::number(QThread::idealThreadCount());
446 tmp2 = qstrdup(value.toUtf8().constData());
447 renderProps->set(tmp, tmp2);
451 mlt_properties consumerProperties = m_mltConsumer->get_properties();
452 mlt_properties_set_data(consumerProperties, "0", renderProps->get_properties(), 0, (mlt_destructor) mlt_properties_close, NULL);
456 // user wants live preview
457 Mlt::Properties *previewProps = new Mlt::Properties;
458 QString videoDriver = KdenliveSettings::videodrivername();
459 if (!videoDriver.isEmpty()) {
460 if (videoDriver == "x11_noaccel") {
461 setenv("SDL_VIDEO_YUV_HWACCEL", "0", 1);
464 unsetenv("SDL_VIDEO_YUV_HWACCEL");
467 setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 1);
471 previewProps->set("mlt_service", "sdl_audio");
472 previewProps->set("preview_off", 1);
473 previewProps->set("preview_format", mlt_image_rgb24);
474 previewProps->set("terminate_on_pause", 0);
475 m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_gl_frame_show);
477 previewProps->set("mlt_service", "sdl_preview");
478 previewProps->set("window_id", m_winid);
479 previewProps->set("terminate_on_pause", 0);
480 //m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) rec_consumer_frame_preview);
482 //m_mltConsumer->set("resize", 1);
483 previewProps->set("window_background", KdenliveSettings::window_background().name().toUtf8().constData());
484 QString audioDevice = KdenliveSettings::audiodevicename();
485 if (!audioDevice.isEmpty())
486 previewProps->set("audio_device", audioDevice.toUtf8().constData());
488 if (!videoDriver.isEmpty())
489 previewProps->set("video_driver", videoDriver.toUtf8().constData());
491 QString audioDriver = KdenliveSettings::audiodrivername();
493 if (!audioDriver.isEmpty())
494 previewProps->set("audio_driver", audioDriver.toUtf8().constData());
496 previewProps->set("real_time", "0");
497 previewProps->set("mlt_profile", m_activeProfile.toUtf8().constData());
498 mlt_properties_set_data(consumerProperties, "1", previewProps->get_properties(), 0, (mlt_destructor) mlt_properties_close, NULL);
499 //m_showFrameEvent = m_mltConsumer->listen("consumer-frame-render", this, (mlt_listener) rec_consumer_frame_show);
505 tmp = qstrdup(playlist.toUtf8().constData());
507 // create an xml producer
508 m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", tmp);
511 // create a producer based on mltproducer parameter
512 m_mltProducer = new Mlt::Producer(*m_mltProfile, tmp);
516 if (m_mltProducer == NULL || !m_mltProducer->is_valid()) {
517 kDebug()<<"//// ERROR CREATRING PROD";
521 m_mltConsumer->connect(*m_mltProducer);
522 if (m_mltConsumer->start() == -1) {
523 if (m_showFrameEvent) delete m_showFrameEvent;
524 m_showFrameEvent = NULL;
525 delete m_mltConsumer;
526 m_mltConsumer = NULL;
529 m_droppedFramesTimer.start();
534 void MltDeviceCapture::setOverlay(const QString &path)
536 if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return;
537 Mlt::Producer parentProd(m_mltProducer->parent());
538 if (parentProd.get_producer() == NULL) {
539 kDebug() << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////";
543 Mlt::Service service(parentProd.get_service());
544 if (service.type() != tractor_type) {
545 kWarning() << "// TRACTOR PROBLEM";
548 Mlt::Tractor tractor(service);
549 if ( tractor.count() < 2) {
550 kWarning() << "// TRACTOR PROBLEM";
553 mlt_service_lock(service.get_service());
554 Mlt::Producer trackProducer(tractor.track(0));
555 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
557 trackPlaylist.remove(0);
558 if (path.isEmpty()) {
559 mlt_service_unlock(service.get_service());
564 char *tmp = qstrdup(path.toUtf8().constData());
565 Mlt::Producer *clip = new Mlt::Producer (*m_mltProfile, "loader", tmp);
567 clip->set_in_and_out(0, 99999);
568 trackPlaylist.insert_at(0, clip, 1);
571 mlt_service serv = m_mltProducer->parent().get_service();
572 mlt_service nextservice = mlt_service_get_producer(serv);
573 mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
574 QString mlt_type = mlt_properties_get(properties, "mlt_type");
575 if (mlt_type != "transition") {
576 // transition does not exist, add it
577 Mlt::Field *field = tractor.field();
578 Mlt::Transition *transition = new Mlt::Transition(*m_mltProfile, "composite");
579 transition->set_in_and_out(0, 0);
580 transition->set("geometry", "0/0:100%x100%:70");
581 transition->set("fill", 1);
582 transition->set("operator", "and");
583 transition->set("a_track", 0);
584 transition->set("b_track", 1);
585 field->plant_transition(*transition, 0, 1);
587 mlt_service_unlock(service.get_service());
591 void MltDeviceCapture::setOverlayEffect(const QString &tag, QStringList parameters)
593 if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return;
594 Mlt::Service service(m_mltProducer->parent().get_service());
595 Mlt::Tractor tractor(service);
596 Mlt::Producer trackProducer(tractor.track(0));
597 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
598 Mlt::Service trackService(trackProducer.get_service());
600 mlt_service_lock(service.get_service());
602 // delete previous effects
604 filter = trackService.filter(0);
605 if (filter && !tag.isEmpty()) {
606 QString currentService = filter->get("mlt_service");
607 if (currentService == tag) {
608 // Effect is already there
609 mlt_service_unlock(service.get_service());
614 trackService.detach(*filter);
616 filter = trackService.filter(0);
620 mlt_service_unlock(service.get_service());
624 char *tmp = qstrdup(tag.toUtf8().constData());
625 filter = new Mlt::Filter(*m_mltProfile, tmp);
627 if (filter && filter->is_valid()) {
628 for (int j = 0; j < parameters.count(); j++) {
629 filter->set(parameters.at(j).section("=", 0, 0).toUtf8().constData(), parameters.at(j).section("=", 1, 1).toUtf8().constData());
631 trackService.attach(*filter);
633 mlt_service_unlock(service.get_service());
636 void MltDeviceCapture::mirror(bool activate)
638 if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return;
639 Mlt::Service service(m_mltProducer->parent().get_service());
640 Mlt::Tractor tractor(service);
641 Mlt::Producer trackProducer(tractor.track(1));
642 Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
643 Mlt::Service trackService(trackProducer.get_service());
645 mlt_service_lock(service.get_service());
647 // delete previous effects
649 filter = trackService.filter(0);
651 trackService.detach(*filter);
653 filter = trackService.filter(0);
657 mlt_service_unlock(service.get_service());
661 filter = new Mlt::Filter(*m_mltProfile, "mirror");
662 if (filter && filter->is_valid()) {
663 filter->set("mirror", "flip");
664 trackService.attach(*filter);
666 mlt_service_unlock(service.get_service());
669 void MltDeviceCapture::uyvy2rgb(unsigned char *yuv_buffer, int width, int height)
671 processingImage = true;
672 QImage image(width, height, QImage::Format_RGB888);
673 unsigned char *rgb_buffer = image.bits();
678 int rgb_ptr, y_ptr, t;
680 len = width * height / 2;
685 for (t = 0; t < len; t++) {
688 Y = yuv_buffer[y_ptr];
689 U = yuv_buffer[y_ptr+1];
690 Y2 = yuv_buffer[y_ptr+2];
691 V = yuv_buffer[y_ptr+3];
694 r = ((298 * (Y - 16) + 409 * (V - 128) + 128) >> 8);
696 g = ((298 * (Y - 16) - 100 * (U - 128) - 208 * (V - 128) + 128) >> 8);
698 b = ((298 * (Y - 16) + 516 * (U - 128) + 128) >> 8);
700 if (r > 255) r = 255;
701 if (g > 255) g = 255;
702 if (b > 255) b = 255;
708 rgb_buffer[rgb_ptr] = r;
709 rgb_buffer[rgb_ptr+1] = g;
710 rgb_buffer[rgb_ptr+2] = b;
714 r = ((298 * (Y2 - 16) + 409 * (V - 128) + 128) >> 8);
716 g = ((298 * (Y2 - 16) - 100 * (U - 128) - 208 * (V - 128) + 128) >> 8);
718 b = ((298 * (Y2 - 16) + 516 * (U - 128) + 128) >> 8);
720 if (r > 255) r = 255;
721 if (g > 255) g = 255;
722 if (b > 255) b = 255;
728 rgb_buffer[rgb_ptr] = r;
729 rgb_buffer[rgb_ptr+1] = g;
730 rgb_buffer[rgb_ptr+2] = b;
733 //emit imageReady(image);
734 m_captureDisplayWidget->setImage(image);
735 emit unblockPreview();
736 //processingImage = false;
739 void MltDeviceCapture::slotPreparePreview()
741 QTimer::singleShot(1000, this, SLOT(slotAllowPreview()));
744 void MltDeviceCapture::slotAllowPreview()
746 processingImage = false;