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 ¶ms, 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 ¶meters) 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 }