File indexing completed on 2024-04-21 04:51:17

0001 /*
0002    SPDX-FileCopyrightText: 2011 Jean-Baptiste Mardelle <jb@kdenlive.org>
0003    SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 */
0005 
0006 #include "mltdevicecapture.h"
0007 
0008 #include "kdenlivesettings.h"
0009 
0010 #include <mlt++/Mlt.h>
0011 
0012 #include "kdenlive_debug.h"
0013 
0014 #include <QString>
0015 #include <QThread>
0016 
0017 #include <cstdarg>
0018 #include <cstdlib>
0019 
0020 static void consumer_gl_frame_show(mlt_consumer /*unused*/, MltDeviceCapture *self, mlt_frame frame_ptr)
0021 {
0022     // detect if the producer has finished playing. Is there a better way to do it?
0023     Mlt::Frame frame(frame_ptr);
0024     self->showFrame(frame);
0025 }
0026 
0027 MltDeviceCapture::MltDeviceCapture(const QString &profile, /*VideoSurface *surface, */ QWidget *parent)
0028     : AbstractRender(Kdenlive::RecordMonitor, parent)
0029     , doCapture(0)
0030     , processingImage(false)
0031     , m_mltConsumer(nullptr)
0032     , m_mltProducer(nullptr)
0033     , m_mltProfile(nullptr)
0034     , m_showFrameEvent(nullptr)
0035     , m_droppedFrames(0)
0036     , m_livePreview(KdenliveSettings::enable_recording_preview())
0037 {
0038     analyseAudio = KdenliveSettings::monitor_audio();
0039     if (profile.isEmpty()) {
0040         // profile = KdenliveSettings::current_profile();
0041     }
0042     buildConsumer(profile);
0043     connect(this, &MltDeviceCapture::unblockPreview, this, &MltDeviceCapture::slotPreparePreview);
0044     m_droppedFramesTimer.setSingleShot(false);
0045     m_droppedFramesTimer.setInterval(1000);
0046     connect(&m_droppedFramesTimer, &QTimer::timeout, this, &MltDeviceCapture::slotCheckDroppedFrames);
0047 }
0048 
0049 MltDeviceCapture::~MltDeviceCapture()
0050 {
0051     delete m_mltConsumer;
0052     delete m_mltProducer;
0053     delete m_mltProfile;
0054 }
0055 
0056 bool MltDeviceCapture::buildConsumer(const QString &profileName)
0057 {
0058     if (!profileName.isEmpty()) {
0059         m_activeProfile = profileName;
0060     }
0061 
0062     delete m_mltProfile;
0063 
0064     char *tmp = qstrdup(m_activeProfile.toUtf8().constData());
0065     qputenv("MLT_PROFILE", tmp);
0066     m_mltProfile = new Mlt::Profile(tmp);
0067     m_mltProfile->set_explicit(1);
0068     delete[] tmp;
0069 
0070     QString videoDriver = KdenliveSettings::videodrivername();
0071     if (!videoDriver.isEmpty()) {
0072         if (videoDriver == QLatin1String("x11_noaccel")) {
0073             qputenv("SDL_VIDEO_YUV_HWACCEL", "0");
0074             videoDriver = QStringLiteral("x11");
0075         } else {
0076             qunsetenv("SDL_VIDEO_YUV_HWACCEL");
0077         }
0078     }
0079     qputenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1");
0080     // OpenGL monitor
0081     m_mltConsumer = new Mlt::Consumer(*m_mltProfile, KdenliveSettings::audiobackend().toUtf8().constData());
0082     m_mltConsumer->set("preview_off", 1);
0083     m_mltConsumer->set("preview_format", mlt_image_rgb);
0084     m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, mlt_listener(consumer_gl_frame_show));
0085     // m_mltConsumer->set("resize", 1);
0086     // m_mltConsumer->set("terminate_on_pause", 1);
0087     m_mltConsumer->set("window_background", KdenliveSettings::window_background().name().toUtf8().constData());
0088     // m_mltConsumer->set("rescale", "nearest");
0089 
0090     QString audioDevice = KdenliveSettings::audiodevicename();
0091     if (!audioDevice.isEmpty()) {
0092         m_mltConsumer->set("audio_device", audioDevice.toUtf8().constData());
0093     }
0094 
0095     if (!videoDriver.isEmpty()) {
0096         m_mltConsumer->set("video_driver", videoDriver.toUtf8().constData());
0097     }
0098 
0099     QString audioDriver = KdenliveSettings::audiodrivername();
0100 
0101     if (!audioDriver.isEmpty()) {
0102         m_mltConsumer->set("audio_driver", audioDriver.toUtf8().constData());
0103     }
0104 
0105     // m_mltConsumer->set("progressive", 0);
0106     // m_mltConsumer->set("buffer", 1);
0107     // m_mltConsumer->set("real_time", 0);
0108     if (!m_mltConsumer->is_valid()) {
0109         delete m_mltConsumer;
0110         m_mltConsumer = nullptr;
0111         return false;
0112     }
0113     return true;
0114 }
0115 
0116 void MltDeviceCapture::pause()
0117 {
0118     if (m_mltConsumer) {
0119         m_mltConsumer->set("refresh", 0);
0120         // m_mltProducer->set_speed(0.0);
0121         m_mltConsumer->purge();
0122     }
0123 }
0124 
0125 void MltDeviceCapture::stop()
0126 {
0127     m_droppedFramesTimer.stop();
0128     bool isPlaylist = false;
0129     // disconnect(this, SIGNAL(imageReady(QImage)), this, SIGNAL(frameUpdated(QImage)));
0130     // m_captureDisplayWidget->stop();
0131 
0132     delete m_showFrameEvent;
0133     m_showFrameEvent = nullptr;
0134 
0135     if (m_mltConsumer) {
0136         m_mltConsumer->set("refresh", 0);
0137         m_mltConsumer->purge();
0138         m_mltConsumer->stop();
0139         // if (!m_mltConsumer->is_stopped()) m_mltConsumer->stop();
0140     }
0141     if (m_mltProducer) {
0142         Mlt::Service service(m_mltProducer->parent().get_service());
0143         mlt_service_lock(service.get_service());
0144         if (service.type() == mlt_service_tractor_type) {
0145             isPlaylist = true;
0146             Mlt::Tractor tractor(service);
0147             mlt_tractor_close(tractor.get_tractor());
0148             Mlt::Field *field = tractor.field();
0149             mlt_service nextservice = mlt_service_get_producer(service.get_service());
0150             mlt_service nextservicetodisconnect;
0151             mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
0152             QString mlt_type = mlt_properties_get(properties, "mlt_type");
0153             QString resource = mlt_properties_get(properties, "mlt_service");
0154             // Delete all transitions
0155             while (mlt_type == QLatin1String("transition")) {
0156                 nextservicetodisconnect = nextservice;
0157                 nextservice = mlt_service_producer(nextservice);
0158                 mlt_field_disconnect_service(field->get_field(), nextservicetodisconnect);
0159                 if (nextservice == nullptr) {
0160                     break;
0161                 }
0162                 properties = MLT_SERVICE_PROPERTIES(nextservice);
0163                 mlt_type = mlt_properties_get(properties, "mlt_type");
0164                 resource = mlt_properties_get(properties, "mlt_service");
0165             }
0166             delete field;
0167             field = nullptr;
0168         }
0169         mlt_service_unlock(service.get_service());
0170         delete m_mltProducer;
0171         m_mltProducer = nullptr;
0172     }
0173     // For some reason, the consumer seems to be deleted by previous stuff when in playlist mode
0174     if (!isPlaylist && (m_mltConsumer != nullptr)) {
0175         delete m_mltConsumer;
0176     }
0177     m_mltConsumer = nullptr;
0178 }
0179 
0180 void MltDeviceCapture::emitFrameUpdated(Mlt::Frame &frame)
0181 {
0182     /*
0183     //TEST: is it better to convert the frame in a thread outside of MLT??
0184     if (processingImage) return;
0185     mlt_image_format format = (mlt_image_format) frame.get_int("format"); //mlt_image_rgb;
0186     int width = frame.get_int("width");
0187     int height = frame.get_int("height");
0188     unsigned char *buffer = (unsigned char *) frame.get_data("image");
0189     if (format == mlt_image_yuv422) {
0190         QtConcurrent::run(this, &MltDeviceCapture::uyvy2rgb, (unsigned char *) buffer, width, height);
0191     }
0192     */
0193 
0194     mlt_image_format format = mlt_image_rgb;
0195     int width = 0;
0196     int height = 0;
0197     const uchar *image = frame.get_image(format, width, height);
0198     QImage qimage(width, height, QImage::Format_RGB888);
0199     // QImage qimage(width, height, QImage::Format_ARGB32_Premultiplied);
0200     memcpy(qimage.bits(), image, size_t(width * height * 3));
0201     Q_EMIT frameUpdated(qimage);
0202 }
0203 
0204 void MltDeviceCapture::showFrame(Mlt::Frame &frame)
0205 {
0206     mlt_image_format format = mlt_image_rgb;
0207     int width = 0;
0208     int height = 0;
0209     const uchar *image = frame.get_image(format, width, height);
0210     QImage qimage(width, height, QImage::Format_RGB888);
0211     memcpy(qimage.scanLine(0), image, static_cast<size_t>(width * height * 3));
0212     Q_EMIT showImageSignal(qimage);
0213 
0214     if (sendFrameForAnalysis && (frame.get_frame()->convert_image != nullptr)) {
0215         Q_EMIT frameUpdated(qimage.rgbSwapped());
0216     }
0217 }
0218 
0219 void MltDeviceCapture::showAudio(Mlt::Frame &frame)
0220 {
0221     if (!frame.is_valid() || frame.get_int("test_audio") != 0) {
0222         return;
0223     }
0224     mlt_audio_format audio_format = mlt_audio_s16;
0225     int freq = 0;
0226     int num_channels = 0;
0227     int samples = 0;
0228     auto *data = static_cast<qint16 *>(frame.get_audio(audio_format, freq, num_channels, samples));
0229 
0230     if (!data) {
0231         return;
0232     }
0233 
0234     // Data format: [ c00 c10 c01 c11 c02 c12 c03 c13 ... c0{samples-1} c1{samples-1} for 2 channels.
0235     // So the vector is of size samples*channels.
0236     audioShortVector sampleVector(samples * num_channels);
0237     memcpy(sampleVector.data(), data, size_t(samples * num_channels) * sizeof(qint16));
0238     if (samples > 0) {
0239         Q_EMIT audioSamplesSignal(sampleVector, freq, num_channels, samples);
0240     }
0241 }
0242 
0243 bool MltDeviceCapture::slotStartPreview(const QString &producer, bool xmlFormat)
0244 {
0245     if (m_mltConsumer == nullptr) {
0246         if (!buildConsumer()) {
0247             return false;
0248         }
0249     }
0250     char *tmp = qstrdup(producer.toUtf8().constData());
0251     if (xmlFormat) {
0252         m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", tmp);
0253     } else {
0254         m_mltProducer = new Mlt::Producer(*m_mltProfile, tmp);
0255     }
0256     delete[] tmp;
0257 
0258     if (m_mltProducer == nullptr || !m_mltProducer->is_valid()) {
0259         if (m_mltProducer) {
0260             delete m_mltProducer;
0261             m_mltProducer = nullptr;
0262         }
0263         // qCDebug(KDENLIVE_LOG)<<"//// ERROR CREATRING PROD";
0264         return false;
0265     }
0266     m_mltConsumer->connect(*m_mltProducer);
0267     if (m_mltConsumer->start() == -1) {
0268         delete m_mltConsumer;
0269         m_mltConsumer = nullptr;
0270         return false;
0271     }
0272     m_droppedFramesTimer.start();
0273     // connect(this, SIGNAL(imageReady(QImage)), this, SIGNAL(frameUpdated(QImage)));
0274     return true;
0275 }
0276 
0277 void MltDeviceCapture::slotCheckDroppedFrames()
0278 {
0279     if (m_mltProducer) {
0280         int dropped = m_mltProducer->get_int("dropped");
0281         if (dropped > m_droppedFrames) {
0282             m_droppedFrames = dropped;
0283             Q_EMIT droppedFrames(m_droppedFrames);
0284         }
0285     }
0286 }
0287 
0288 void MltDeviceCapture::saveFrame(Mlt::Frame &frame)
0289 {
0290     mlt_image_format format = mlt_image_rgb;
0291     int width = 0;
0292     int height = 0;
0293     const uchar *image = frame.get_image(format, width, height);
0294     QImage qimage(width, height, QImage::Format_RGB888);
0295     memcpy(qimage.bits(), image, static_cast<size_t>(width * height * 3));
0296 
0297     // Re-enable overlay
0298     Mlt::Service service(m_mltProducer->parent().get_service());
0299     Mlt::Tractor tractor(service);
0300     Mlt::Producer trackProducer(tractor.track(0));
0301     trackProducer.set("hide", 0);
0302 
0303     qimage.save(m_capturePath);
0304     Q_EMIT frameSaved(m_capturePath);
0305     m_capturePath.clear();
0306 }
0307 
0308 void MltDeviceCapture::captureFrame(const QString &path)
0309 {
0310     if (m_mltProducer == nullptr || !m_mltProducer->is_valid()) {
0311         return;
0312     }
0313 
0314     // Hide overlay track before doing the capture
0315     Mlt::Service service(m_mltProducer->parent().get_service());
0316     Mlt::Tractor tractor(service);
0317     Mlt::Producer trackProducer(tractor.track(0));
0318     mlt_service_lock(service.get_service());
0319     trackProducer.set("hide", 1);
0320     m_mltConsumer->purge();
0321     mlt_service_unlock(service.get_service());
0322     m_capturePath = path;
0323     // Wait for 5 frames before capture to make sure overlay is gone
0324     doCapture = 5;
0325 }
0326 
0327 bool MltDeviceCapture::slotStartCapture(const QString &params, const QString &path, const QString &playlist, bool livePreview, bool xmlPlaylist)
0328 {
0329     stop();
0330     m_livePreview = livePreview;
0331     m_frameCount = 0;
0332     m_droppedFrames = 0;
0333     delete m_mltProfile;
0334     char *tmp = qstrdup(m_activeProfile.toUtf8().constData());
0335     m_mltProfile = new Mlt::Profile(tmp);
0336     delete[] tmp;
0337 
0338     m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "multi");
0339     if (m_mltConsumer == nullptr || !m_mltConsumer->is_valid()) {
0340         delete m_mltConsumer;
0341         m_mltConsumer = nullptr;
0342         return false;
0343     }
0344 
0345     // Create multi consumer setup
0346     auto *renderProps = new Mlt::Properties;
0347     renderProps->set("mlt_service", "avformat");
0348     renderProps->set("target", path.toUtf8().constData());
0349     renderProps->set("real_time", -1);
0350     renderProps->set("terminate_on_pause", 0); // was commented out. restoring it  fixes mantis#3415 - FFmpeg recording freezes
0351     // without this line a call to mlt_properties_get_int(terminate on pause) for in mlt/src/modules/core/consumer_multi.c is returning 1
0352     // and going into and endless loop.
0353     renderProps->set("mlt_profile", m_activeProfile.toUtf8().constData());
0354     const QStringList paramList = params.split(' ', Qt::SkipEmptyParts);
0355     for (int i = 0; i < paramList.count(); ++i) {
0356         tmp = qstrdup(paramList.at(i).section(QLatin1Char('='), 0, 0).toUtf8().constData());
0357         QString value = paramList.at(i).section(QLatin1Char('='), 1, 1);
0358         if (value == QLatin1String("%threads")) {
0359             value = QString::number(QThread::idealThreadCount());
0360         }
0361         char *tmp2 = qstrdup(value.toUtf8().constData());
0362         renderProps->set(tmp, tmp2);
0363         delete[] tmp;
0364         delete[] tmp2;
0365     }
0366     mlt_properties consumerProperties = m_mltConsumer->get_properties();
0367     mlt_properties_set_data(consumerProperties, "0", renderProps->get_properties(), 0, mlt_destructor(mlt_properties_close), nullptr);
0368 
0369     if (m_livePreview) {
0370         // user wants live preview
0371         auto *previewProps = new Mlt::Properties;
0372         QString videoDriver = KdenliveSettings::videodrivername();
0373         if (!videoDriver.isEmpty()) {
0374             if (videoDriver == QLatin1String("x11_noaccel")) {
0375                 qputenv("SDL_VIDEO_YUV_HWACCEL", "0");
0376                 videoDriver = QStringLiteral("x11");
0377             } else {
0378                 qunsetenv("SDL_VIDEO_YUV_HWACCEL");
0379             }
0380         }
0381         qputenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1");
0382 
0383         // OpenGL monitor
0384         previewProps->set("mlt_service", KdenliveSettings::audiobackend().toUtf8().constData());
0385         previewProps->set("preview_off", 1);
0386         previewProps->set("preview_format", mlt_image_rgb);
0387         previewProps->set("terminate_on_pause", 0);
0388         m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, mlt_listener(consumer_gl_frame_show));
0389         // m_mltConsumer->set("resize", 1);
0390         previewProps->set("window_background", KdenliveSettings::window_background().name().toUtf8().constData());
0391         QString audioDevice = KdenliveSettings::audiodevicename();
0392         if (!audioDevice.isEmpty()) {
0393             previewProps->set("audio_device", audioDevice.toUtf8().constData());
0394         }
0395 
0396         if (!videoDriver.isEmpty()) {
0397             previewProps->set("video_driver", videoDriver.toUtf8().constData());
0398         }
0399 
0400         QString audioDriver = KdenliveSettings::audiodrivername();
0401 
0402         if (!audioDriver.isEmpty()) {
0403             previewProps->set("audio_driver", audioDriver.toUtf8().constData());
0404         }
0405 
0406         previewProps->set("real_time", "0");
0407         previewProps->set("mlt_profile", m_activeProfile.toUtf8().constData());
0408         mlt_properties_set_data(consumerProperties, "1", previewProps->get_properties(), 0, mlt_destructor(mlt_properties_close), nullptr);
0409         // m_showFrameEvent = m_mltConsumer->listen("consumer-frame-render", this, (mlt_listener) rec_consumer_frame_show);
0410     } else {
0411     }
0412 
0413     if (xmlPlaylist) {
0414         // create an xml producer
0415         m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", playlist.toUtf8().constData());
0416     } else {
0417         // create a producer based on mltproducer parameter
0418         m_mltProducer = new Mlt::Producer(*m_mltProfile, playlist.toUtf8().constData());
0419     }
0420 
0421     if (m_mltProducer == nullptr || !m_mltProducer->is_valid()) {
0422         // qCDebug(KDENLIVE_LOG)<<"//// ERROR CREATRING PROD";
0423         delete m_mltConsumer;
0424         m_mltConsumer = nullptr;
0425         delete m_mltProducer;
0426         m_mltProducer = nullptr;
0427         return false;
0428     }
0429 
0430     m_mltConsumer->connect(*m_mltProducer);
0431     if (m_mltConsumer->start() == -1) {
0432         delete m_showFrameEvent;
0433         m_showFrameEvent = nullptr;
0434         delete m_mltConsumer;
0435         m_mltConsumer = nullptr;
0436         return false;
0437     }
0438     m_droppedFramesTimer.start();
0439     return true;
0440 }
0441 
0442 void MltDeviceCapture::setOverlay(const QString &path)
0443 {
0444     if (m_mltProducer == nullptr || !m_mltProducer->is_valid()) {
0445         return;
0446     }
0447     Mlt::Producer parentProd(m_mltProducer->parent());
0448     if (parentProd.get_producer() == nullptr) {
0449         // qCDebug(KDENLIVE_LOG) << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////";
0450         return;
0451     }
0452 
0453     Mlt::Service service(parentProd.get_service());
0454     if (service.type() != mlt_service_tractor_type) {
0455         qCWarning(KDENLIVE_LOG) << "// TRACTOR PROBLEM";
0456         return;
0457     }
0458     Mlt::Tractor tractor(service);
0459     if (tractor.count() < 2) {
0460         qCWarning(KDENLIVE_LOG) << "// TRACTOR PROBLEM";
0461         return;
0462     }
0463     mlt_service_lock(service.get_service());
0464     Mlt::Producer trackProducer(tractor.track(0));
0465     Mlt::Playlist trackPlaylist(mlt_playlist(trackProducer.get_service()));
0466 
0467     trackPlaylist.remove(0);
0468     if (path.isEmpty()) {
0469         mlt_service_unlock(service.get_service());
0470         return;
0471     }
0472 
0473     // Add overlay clip
0474     char *tmp = qstrdup(path.toUtf8().constData());
0475     auto *clip = new Mlt::Producer(*m_mltProfile, "loader", tmp);
0476     delete[] tmp;
0477     clip->set_in_and_out(0, 99999);
0478     trackPlaylist.insert_at(0, clip, 1);
0479 
0480     // Add transition
0481     mlt_service serv = m_mltProducer->parent().get_service();
0482     mlt_service nextservice = mlt_service_get_producer(serv);
0483     mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
0484     QString mlt_type = mlt_properties_get(properties, "mlt_type");
0485     if (mlt_type != QLatin1String("transition")) {
0486         // transition does not exist, add it
0487         Mlt::Field *field = tractor.field();
0488         auto *transition = new Mlt::Transition(*m_mltProfile, "composite");
0489         transition->set_in_and_out(0, 0);
0490         transition->set("geometry", "0/0:100%x100%:70");
0491         transition->set("fill", 1);
0492         transition->set("operator", "and");
0493         transition->set("a_track", 0);
0494         transition->set("b_track", 1);
0495         field->plant_transition(*transition, 0, 1);
0496     }
0497     mlt_service_unlock(service.get_service());
0498     // delete clip;
0499 }
0500 
0501 void MltDeviceCapture::setOverlayEffect(const QString &tag, const QStringList &parameters)
0502 {
0503     if (m_mltProducer == nullptr || !m_mltProducer->is_valid()) {
0504         return;
0505     }
0506     Mlt::Service service(m_mltProducer->parent().get_service());
0507     Mlt::Tractor tractor(service);
0508     Mlt::Producer trackProducer(tractor.track(0));
0509     Mlt::Playlist trackPlaylist(mlt_playlist(trackProducer.get_service()));
0510     Mlt::Service trackService(trackProducer.get_service());
0511 
0512     mlt_service_lock(service.get_service());
0513 
0514     // delete previous effects
0515     Mlt::Filter *filter;
0516     filter = trackService.filter(0);
0517     if ((filter != nullptr) && !tag.isEmpty()) {
0518         QString currentService = filter->get("mlt_service");
0519         if (currentService == tag) {
0520             // Effect is already there
0521             mlt_service_unlock(service.get_service());
0522             return;
0523         }
0524     }
0525     while (filter != nullptr) {
0526         trackService.detach(*filter);
0527         delete filter;
0528         filter = trackService.filter(0);
0529     }
0530 
0531     if (tag.isEmpty()) {
0532         mlt_service_unlock(service.get_service());
0533         return;
0534     }
0535 
0536     char *tmp = qstrdup(tag.toUtf8().constData());
0537     filter = new Mlt::Filter(*m_mltProfile, tmp);
0538     delete[] tmp;
0539     if ((filter != nullptr) && filter->is_valid()) {
0540         for (int j = 0; j < parameters.count(); ++j) {
0541             filter->set(parameters.at(j).section(QLatin1Char('='), 0, 0).toUtf8().constData(),
0542                         parameters.at(j).section(QLatin1Char('='), 1, 1).toUtf8().constData());
0543         }
0544         trackService.attach(*filter);
0545     }
0546     mlt_service_unlock(service.get_service());
0547 }
0548 
0549 void MltDeviceCapture::mirror(bool activate)
0550 {
0551     if (m_mltProducer == nullptr || !m_mltProducer->is_valid()) {
0552         return;
0553     }
0554     Mlt::Service service(m_mltProducer->parent().get_service());
0555     Mlt::Tractor tractor(service);
0556     Mlt::Producer trackProducer(tractor.track(1));
0557     Mlt::Playlist trackPlaylist(mlt_playlist(trackProducer.get_service()));
0558     Mlt::Service trackService(trackProducer.get_service());
0559 
0560     mlt_service_lock(service.get_service());
0561 
0562     // delete previous effects
0563     Mlt::Filter *filter;
0564     filter = trackService.filter(0);
0565     while (filter != nullptr) {
0566         trackService.detach(*filter);
0567         delete filter;
0568         filter = trackService.filter(0);
0569     }
0570 
0571     if (!activate) {
0572         mlt_service_unlock(service.get_service());
0573         return;
0574     }
0575 
0576     filter = new Mlt::Filter(*m_mltProfile, "mirror");
0577     if ((filter != nullptr) && filter->is_valid()) {
0578         filter->set("mirror", "flip");
0579         trackService.attach(*filter);
0580     }
0581     mlt_service_unlock(service.get_service());
0582 }
0583 
0584 void MltDeviceCapture::uyvy2rgb(const unsigned char *yuv_buffer, int width, int height)
0585 {
0586     processingImage = true;
0587     QImage image(width, height, QImage::Format_RGB888);
0588     unsigned char *rgb_buffer = image.bits();
0589 
0590     int rgb_ptr = 0, y_ptr = 0;
0591     int len = width * height / 2;
0592 
0593     for (int t = 0; t < len; ++t) {
0594         int Y = yuv_buffer[y_ptr];
0595         int U = yuv_buffer[y_ptr + 1];
0596         int Y2 = yuv_buffer[y_ptr + 2];
0597         int V = yuv_buffer[y_ptr + 3];
0598         y_ptr += 4;
0599 
0600         int r = ((298 * (Y - 16) + 409 * (V - 128) + 128) >> 8);
0601 
0602         int g = ((298 * (Y - 16) - 100 * (U - 128) - 208 * (V - 128) + 128) >> 8);
0603 
0604         int b = ((298 * (Y - 16) + 516 * (U - 128) + 128) >> 8);
0605 
0606         if (r > 255) {
0607             r = 255;
0608         }
0609         if (g > 255) {
0610             g = 255;
0611         }
0612         if (b > 255) {
0613             b = 255;
0614         }
0615 
0616         if (r < 0) {
0617             r = 0;
0618         }
0619         if (g < 0) {
0620             g = 0;
0621         }
0622         if (b < 0) {
0623             b = 0;
0624         }
0625 
0626         rgb_buffer[rgb_ptr] = static_cast<uchar>(r);
0627         rgb_buffer[rgb_ptr + 1] = static_cast<uchar>(g);
0628         rgb_buffer[rgb_ptr + 2] = static_cast<uchar>(b);
0629         rgb_ptr += 3;
0630 
0631         r = ((298 * (Y2 - 16) + 409 * (V - 128) + 128) >> 8);
0632         g = ((298 * (Y2 - 16) - 100 * (U - 128) - 208 * (V - 128) + 128) >> 8);
0633         b = ((298 * (Y2 - 16) + 516 * (U - 128) + 128) >> 8);
0634 
0635         if (r > 255) {
0636             r = 255;
0637         }
0638         if (g > 255) {
0639             g = 255;
0640         }
0641         if (b > 255) {
0642             b = 255;
0643         }
0644 
0645         if (r < 0) {
0646             r = 0;
0647         }
0648         if (g < 0) {
0649             g = 0;
0650         }
0651         if (b < 0) {
0652             b = 0;
0653         }
0654 
0655         rgb_buffer[rgb_ptr] = static_cast<uchar>(r);
0656         rgb_buffer[rgb_ptr + 1] = static_cast<uchar>(g);
0657         rgb_buffer[rgb_ptr + 2] = static_cast<uchar>(b);
0658         rgb_ptr += 3;
0659     }
0660     // Q_EMIT imageReady(image);
0661     // m_captureDisplayWidget->setImage(image);
0662     Q_EMIT unblockPreview();
0663     // processingImage = false;
0664 }
0665 
0666 void MltDeviceCapture::slotPreparePreview()
0667 {
0668     QTimer::singleShot(1000, this, &MltDeviceCapture::slotAllowPreview);
0669 }
0670 
0671 void MltDeviceCapture::slotAllowPreview()
0672 {
0673     processingImage = false;
0674 }