X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmltdevicecapture.cpp;h=425b81a6be28fedec74bc74e1f07a14197f3a1a0;hb=c3302003093710ee247ad84c0fe2ef3c579d417f;hp=f36fda0137733d8b4218efde4a03c94e7568222d;hpb=ac5f5c9f6d4bdffeb76f3f2098b80f27bc532606;p=kdenlive diff --git a/src/mltdevicecapture.cpp b/src/mltdevicecapture.cpp index f36fda01..425b81a6 100644 --- a/src/mltdevicecapture.cpp +++ b/src/mltdevicecapture.cpp @@ -19,9 +19,6 @@ #include "mltdevicecapture.h" #include "kdenlivesettings.h" #include "definitions.h" -//#include "recmonitor.h" -//#include "renderer.h" -#include "blackmagic/devices.h" #include @@ -35,6 +32,8 @@ #include #include #include +#include +#include #include #include @@ -50,12 +49,12 @@ static void consumer_gl_frame_show(mlt_consumer, MltDeviceCapture * self, mlt_fr self->showFrame(frame); } -static void rec_consumer_frame_show(mlt_consumer, MltDeviceCapture * self, mlt_frame frame_ptr) +/*static void rec_consumer_frame_show(mlt_consumer, MltDeviceCapture * self, mlt_frame frame_ptr) { Mlt::Frame frame(frame_ptr); if (!frame.is_valid()) return; self->gotCapturedFrame(frame); -} +}*/ static void rec_consumer_frame_preview(mlt_consumer, MltDeviceCapture * self, mlt_frame frame_ptr) { @@ -64,36 +63,42 @@ static void rec_consumer_frame_preview(mlt_consumer, MltDeviceCapture * self, ml if (self->sendFrameForAnalysis && frame_ptr->convert_image) { self->emitFrameUpdated(frame); } - if (self->doCapture) { - self->doCapture = false; - self->saveFrame(frame); + if (self->doCapture > 0) { + self->doCapture --; + if (self->doCapture == 0) self->saveFrame(frame); } -/* if (self->analyseAudio) { + //TODO: connect record monitor to audio scopes + + if (self->analyseAudio) { self->showAudio(frame); } - if (frame.get_double("_speed") == 0.0) { - self->emitConsumerStopped(); - } else if (frame.get_double("_speed") < 0.0 && mlt_frame_get_position(frame_ptr) <= 0) { - self->pause(); - self->emitConsumerStopped(); - }*/ + } -MltDeviceCapture::MltDeviceCapture(QString profile, VideoPreviewContainer *surface, QWidget *parent) : - AbstractRender(parent), - doCapture(false), +MltDeviceCapture::MltDeviceCapture(QString profile, VideoSurface *surface, QWidget *parent) : + AbstractRender(Kdenlive::recordMonitor, parent), + doCapture(0), sendFrameForAnalysis(false), + processingImage(false), m_mltConsumer(NULL), m_mltProducer(NULL), m_mltProfile(NULL), - m_captureDisplayWidget(surface), - m_winid((int) surface->winId()), - m_analyseAudio(KdenliveSettings::monitor_audio()) + m_showFrameEvent(NULL), + m_droppedFrames(0), + m_livePreview(KdenliveSettings::enable_recording_preview()), + m_winid((int) surface->winId()) { - if (profile.isEmpty()) profile = KdenliveSettings::current_profile(); + m_captureDisplayWidget = surface; + analyseAudio = KdenliveSettings::monitor_audio(); + if (profile.isEmpty()) + profile = KdenliveSettings::current_profile(); buildConsumer(profile); + connect(this, SIGNAL(unblockPreview()), this, SLOT(slotPreparePreview())); + m_droppedFramesTimer.setSingleShot(false); + m_droppedFramesTimer.setInterval(1000); + connect(&m_droppedFramesTimer, SIGNAL(timeout()), this, SLOT(slotCheckDroppedFrames())); } MltDeviceCapture::~MltDeviceCapture() @@ -112,7 +117,7 @@ void MltDeviceCapture::buildConsumer(const QString &profileName) char *tmp = qstrdup(m_activeProfile.toUtf8().constData()); setenv("MLT_PROFILE", tmp, 1); m_mltProfile = new Mlt::Profile(tmp); - m_mltProfile->get_profile()->is_explicit = 1; + m_mltProfile->set_explicit(true); delete[] tmp; QString videoDriver = KdenliveSettings::videodrivername(); @@ -125,23 +130,23 @@ void MltDeviceCapture::buildConsumer(const QString &profileName) } } setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 1); - + + if (m_winid == 0) { // OpenGL monitor m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "sdl_audio"); m_mltConsumer->set("preview_off", 1); - m_mltConsumer->set("preview_format", mlt_image_rgb24a); - m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_gl_frame_show); + m_mltConsumer->set("preview_format", mlt_image_rgb24); + m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_gl_frame_show); } else { m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "sdl_preview"); m_mltConsumer->set("window_id", m_winid); + m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) rec_consumer_frame_preview); } - m_mltConsumer->set("resize", 1); + //m_mltConsumer->set("resize", 1); //m_mltConsumer->set("terminate_on_pause", 1); m_mltConsumer->set("window_background", KdenliveSettings::window_background().name().toUtf8().constData()); - m_mltConsumer->set("rescale", "nearest"); - - m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) rec_consumer_frame_preview); + //m_mltConsumer->set("rescale", "nearest"); QString audioDevice = KdenliveSettings::audiodevicename(); if (!audioDevice.isEmpty()) @@ -155,27 +160,42 @@ void MltDeviceCapture::buildConsumer(const QString &profileName) if (!audioDriver.isEmpty()) m_mltConsumer->set("audio_driver", audioDriver.toUtf8().constData()); - //m_mltConsumer->set("progressive", 1); + //m_mltConsumer->set("progressive", 0); //m_mltConsumer->set("buffer", 1); //m_mltConsumer->set("real_time", 0); } +void MltDeviceCapture::pause() +{ + if (m_mltConsumer) { + m_mltConsumer->set("refresh", 0); + //m_mltProducer->set_speed(0.0); + m_mltConsumer->purge(); + } +} + void MltDeviceCapture::stop() { + m_droppedFramesTimer.stop(); + bool isPlaylist = false; + //disconnect(this, SIGNAL(imageReady(QImage)), this, SIGNAL(frameUpdated(QImage))); + //m_captureDisplayWidget->stop(); + + if (m_showFrameEvent) delete m_showFrameEvent; + m_showFrameEvent = NULL; + if (m_mltConsumer) { - m_mltConsumer->stop(); + m_mltConsumer->set("refresh", 0); + m_mltConsumer->purge(); + m_mltConsumer->stop(); //if (!m_mltConsumer->is_stopped()) m_mltConsumer->stop(); - delete m_mltConsumer; - m_mltConsumer = NULL; } - kDebug()<<"STOPPING cap"; if (m_mltProducer) { QList prods; Mlt::Service service(m_mltProducer->parent().get_service()); mlt_service_lock(service.get_service()); -kDebug()<<"STOPPING cap 2"; if (service.type() == tractor_type) { - kDebug()<<"STOPPING cap 3"; + isPlaylist = true; Mlt::Tractor tractor(service); mlt_tractor_close(tractor.get_tractor()); Mlt::Field *field = tractor.field(); @@ -194,59 +214,66 @@ kDebug()<<"STOPPING cap 2"; mlt_type = mlt_properties_get(properties, "mlt_type"); resource = mlt_properties_get(properties, "mlt_service"); } - for (int trackNb = tractor.count() - 1; trackNb >= 0; --trackNb) { - Mlt::Producer trackProducer(tractor.track(trackNb)); - Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service()); - if (trackPlaylist.type() == playlist_type) { - for (int i = 0; i < trackPlaylist.count();i++) { - // We need to manually decrease the ref count and close the producer, otherwise - // the video4linux device stays open, seems like a bug in MLT that is not cleaning properly - mlt_properties props = MLT_PRODUCER_PROPERTIES(trackPlaylist.get_clip(i)->get_parent()); - while (mlt_properties_ref_count(props) > 0) mlt_properties_dec_ref(props); - mlt_producer_close(trackPlaylist.get_clip(i)->get_parent()); - } - mlt_playlist_close(trackPlaylist.get_playlist()); - //trackPlaylist.clear(); - } - } delete field; field = NULL; } mlt_service_unlock(service.get_service()); delete m_mltProducer; - kDebug()<<"/// STOP REC PROD"; m_mltProducer = NULL; } + // For some reason, the consumer seems to be deleted by previous stuff when in playlist mode + if (!isPlaylist && m_mltConsumer) delete m_mltConsumer; + m_mltConsumer = NULL; } -void MltDeviceCapture::doRefresh() +void MltDeviceCapture::slotDoRefresh() { - if (m_mltConsumer) m_mltConsumer->set("refresh", 1); + QMutexLocker locker(&m_mutex); + if (!m_mltProducer) + return; + if (m_mltConsumer) { + if (m_mltConsumer->is_stopped()) m_mltConsumer->start(); + m_mltConsumer->purge(); + m_mltConsumer->set("refresh", 1); + } } void MltDeviceCapture::emitFrameUpdated(Mlt::Frame& frame) { - mlt_image_format format = mlt_image_rgb24a; + /* + //TEST: is it better to convert the frame in a thread outside of MLT?? + if (processingImage) return; + mlt_image_format format = (mlt_image_format) frame.get_int("format"); //mlt_image_rgb24; + int width = frame.get_int("width"); + int height = frame.get_int("height"); + unsigned char *buffer = (unsigned char *) frame.get_data("image"); + if (format == mlt_image_yuv422) { + QtConcurrent::run(this, &MltDeviceCapture::uyvy2rgb, (unsigned char *) buffer, width, height); + } + */ + + mlt_image_format format = mlt_image_rgb24; int width = 0; int height = 0; const uchar* image = frame.get_image(format, width, height); - QImage qimage(width, height, QImage::Format_ARGB32); - memcpy(qimage.bits(), image, width * height * 4); - emit frameUpdated(qimage.rgbSwapped()); + QImage qimage(width, height, QImage::Format_RGB888); + //QImage qimage(width, height, QImage::Format_ARGB32_Premultiplied); + memcpy(qimage.bits(), image, width * height * 3); + emit frameUpdated(qimage); } void MltDeviceCapture::showFrame(Mlt::Frame& frame) { - mlt_image_format format = mlt_image_rgb24a; + mlt_image_format format = mlt_image_rgb24; int width = 0; int height = 0; const uchar* image = frame.get_image(format, width, height); - QImage qimage(width, height, QImage::Format_ARGB32_Premultiplied); - memcpy(qimage.scanLine(0), image, width * height * 4); + QImage qimage(width, height, QImage::Format_RGB888); + memcpy(qimage.scanLine(0), image, width * height * 3); emit showImageSignal(qimage); - if (m_analyseAudio) showAudio(frame); + if (sendFrameForAnalysis && frame.get_frame()->convert_image) { emit frameUpdated(qimage.rgbSwapped()); } @@ -271,42 +298,26 @@ void MltDeviceCapture::showAudio(Mlt::Frame& frame) // So the vector is of size samples*channels. QVector sampleVector(samples*num_channels); memcpy(sampleVector.data(), data, samples*num_channels*sizeof(int16_t)); - if (samples > 0) { emit audioSamplesSignal(sampleVector, freq, num_channels, samples); } } -bool MltDeviceCapture::slotStartPreview(const QString &producer) +bool MltDeviceCapture::slotStartPreview(const QString &producer, bool xmlFormat) { - //stop(); - if (m_mltConsumer == NULL) buildConsumer(); - /*if (m_mltConsumer) delete m_mltConsumer; - if (m_mltProducer) delete m_mltProducer; - if (m_mltProfile) delete m_mltProfile; - - char *tmp = qstrdup(m_activeProfile.toUtf8().constData()); - setenv("MLT_PROFILE", tmp, 1); - m_mltProfile = new Mlt::Profile(tmp); - delete[] tmp; - m_mltProfile->get_profile()->is_explicit = 1; - - m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "sdl_preview"); - - m_mltConsumer->set("window_id", m_winid); - m_mltConsumer->set("real_time", 0); -// m_mltConsumer->set("buffer", 1); - m_mltConsumer->set("resize", 1); - m_mltConsumer->set("progressive", 1); - m_mltConsumer->set("rescale", "nearest");*/ - - //char *tmp = qstrdup(QString("avformat-novalidate:video4linux2:%1?frame_rate:%2&width:%3&height:%4").arg(KdenliveSettings::video4vdevice()).arg(m_mltProfile->fps()).arg(m_mltProfile->width()).arg(m_mltProfile->height()).toUtf8().constData()); - + if (m_mltConsumer == NULL) { + buildConsumer(); + } char *tmp = qstrdup(producer.toUtf8().constData()); - - m_mltProducer = new Mlt::Producer(*m_mltProfile, tmp); + if (xmlFormat) m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", tmp); + else m_mltProducer = new Mlt::Producer(*m_mltProfile, tmp); delete[] tmp; + if (m_mltProducer == NULL || !m_mltProducer->is_valid()) { + if (m_mltProducer) { + delete m_mltProducer; + m_mltProducer = NULL; + } kDebug()<<"//// ERROR CREATRING PROD"; return false; } @@ -316,63 +327,117 @@ bool MltDeviceCapture::slotStartPreview(const QString &producer) m_mltConsumer = NULL; return 0; } + m_droppedFramesTimer.start(); + //connect(this, SIGNAL(imageReady(QImage)), this, SIGNAL(frameUpdated(QImage))); return 1; } +void MltDeviceCapture::slotCheckDroppedFrames() +{ + if (m_mltProducer) { + int dropped = m_mltProducer->get_int("dropped"); + if (dropped > m_droppedFrames) { + m_droppedFrames = dropped; + emit droppedFrames(m_droppedFrames); + } + } +} + void MltDeviceCapture::gotCapturedFrame(Mlt::Frame& frame) { - mlt_image_format format = mlt_image_rgb24a; + if (m_mltProducer) { + int dropped = m_mltProducer->get_int("dropped"); + if (dropped > m_droppedFrames) { + m_droppedFrames = dropped; + emit droppedFrames(m_droppedFrames); + } + } + m_frameCount++; + if (!m_livePreview) return; + //if (m_livePreview == 0 && (m_frameCount % 10 > 0)) return; + mlt_image_format format = mlt_image_rgb24; int width = 0; int height = 0; - const uchar* image = frame.get_image(format, width, height); - QImage qimage(width, height, QImage::Format_ARGB32); - memcpy(qimage.bits(), image, width * height * 4); - m_captureDisplayWidget->setImage(qimage.rgbSwapped()); + uint8_t *data = frame.get_image(format, width, height, 0); + //QImage image(width, height, QImage::Format_RGB888); + //memcpy(image.bits(), data, width * height * 3); + QImage image((uchar *)data, width, height, QImage::Format_RGB888); + + //m_captureDisplayWidget->setImage(image); + + //TEST: is it better to process frame conversion ouside MLT??? + /* + if (!m_livePreview || processingImage) return; + + mlt_image_format format = (mlt_image_format) frame.get_int("format"); //mlt_image_rgb24a; + int width = frame.get_int("width"); + int height = frame.get_int("height"); + unsigned char *buffer = (unsigned char *) frame.get_data("image"); + //unsigned char *buffer = frame.get_image(format, width, height); + //convert from uyvy422 to rgba + if (format == mlt_image_yuv422) { + QtConcurrent::run(this, &MltDeviceCapture::uyvy2rgb, (unsigned char *) buffer, width, height); + //CaptureHandler::uyvy2rgb((uchar *)frameBytes, (uchar *)image.bits(), videoFrame->GetWidth(), videoFrame->GetHeight()); + }*/ } void MltDeviceCapture::saveFrame(Mlt::Frame& frame) { - mlt_image_format format = mlt_image_rgb24a; + mlt_image_format format = mlt_image_rgb24; int width = 0; int height = 0; const uchar* image = frame.get_image(format, width, height); - QImage qimage(width, height, QImage::Format_ARGB32); - memcpy(qimage.bits(), image, width * height * 4); - qimage.rgbSwapped().save(m_capturePath); + QImage qimage(width, height, QImage::Format_RGB888); + memcpy(qimage.bits(), image, width * height * 3); + + // Re-enable overlay + Mlt::Service service(m_mltProducer->parent().get_service()); + Mlt::Tractor tractor(service); + Mlt::Producer trackProducer(tractor.track(0)); + trackProducer.set("hide", 0); + + qimage.save(m_capturePath); emit frameSaved(m_capturePath); m_capturePath.clear(); } void MltDeviceCapture::captureFrame(const QString &path) { + if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return; + + // Hide overlay track before doing the capture + Mlt::Service service(m_mltProducer->parent().get_service()); + Mlt::Tractor tractor(service); + Mlt::Producer trackProducer(tractor.track(0)); + mlt_service_lock(service.get_service()); + trackProducer.set("hide", 1); + m_mltConsumer->purge(); + mlt_service_unlock(service.get_service()); m_capturePath = path; - doCapture = true; + // Wait for 5 frames before capture to make sure overlay is gone + doCapture = 5; } -bool MltDeviceCapture::slotStartCapture(const QString ¶ms, const QString &path, const QString &playlist) +bool MltDeviceCapture::slotStartCapture(const QString ¶ms, const QString &path, const QString &playlist, bool livePreview, bool xmlPlaylist) { stop(); + m_livePreview = livePreview; + m_frameCount = 0; + m_droppedFrames = 0; if (m_mltProfile) delete m_mltProfile; char *tmp = qstrdup(m_activeProfile.toUtf8().constData()); m_mltProfile = new Mlt::Profile(tmp); delete[] tmp; - m_mltProfile->get_profile()->is_explicit = 1; - kDebug()<<"-- CREATING CAP: "<get_profile()->is_explicit = 1; + + + /*kDebug()<<"-- CREATING CAP: "<set("terminate_on_pause", 1); - delete[] tmp; - - QStringList paramList = params.split(" ", QString::SkipEmptyParts); - char *tmp2; - for (int i = 0; i < paramList.count(); i++) { - tmp = qstrdup(paramList.at(i).section("=", 0, 0).toUtf8().constData()); - tmp2 = qstrdup(paramList.at(i).section("=", 1, 1).toUtf8().constData()); - m_mltConsumer->set(tmp, tmp2); - delete[] tmp; - delete[] tmp2; - } + m_mltConsumer->set("real_time", -KdenliveSettings::mltthreads()); + delete[] tmp;*/ + m_mltConsumer = new Mlt::Consumer(*m_mltProfile, "multi"); if (m_mltConsumer == NULL || !m_mltConsumer->is_valid()) { if (m_mltConsumer) { delete m_mltConsumer; @@ -381,26 +446,332 @@ bool MltDeviceCapture::slotStartCapture(const QString ¶ms, const QString &pa return false; } - // FIXME: the event object returned by the listen gets leaked... - m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) rec_consumer_frame_show); + m_winid = (int) m_captureDisplayWidget->winId(); + + // Create multi consumer setup + Mlt::Properties *renderProps = new Mlt::Properties; + renderProps->set("mlt_service", "avformat"); + renderProps->set("target", path.toUtf8().constData()); + renderProps->set("real_time", -KdenliveSettings::mltthreads()); + //renderProps->set("terminate_on_pause", 0); + renderProps->set("mlt_profile", m_activeProfile.toUtf8().constData()); + - //tmp = qstrdup(QString("avformat-novalidate:video4linux2:%1?frame_rate:%2&width:%3&height:%4").arg(KdenliveSettings::video4vdevice()).arg(m_mltProfile->fps()).arg(m_mltProfile->width()).arg(m_mltProfile->height()).toUtf8().constData()); - tmp = qstrdup(playlist.toUtf8().constData()); - m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", tmp); - delete[] tmp; + QStringList paramList = params.split(' ', QString::SkipEmptyParts); + char *tmp2; + for (int i = 0; i < paramList.count(); ++i) { + tmp = qstrdup(paramList.at(i).section('=', 0, 0).toUtf8().constData()); + QString value = paramList.at(i).section('=', 1, 1); + if (value == "%threads") value = QString::number(QThread::idealThreadCount()); + tmp2 = qstrdup(value.toUtf8().constData()); + renderProps->set(tmp, tmp2); + delete[] tmp; + delete[] tmp2; + } + mlt_properties consumerProperties = m_mltConsumer->get_properties(); + mlt_properties_set_data(consumerProperties, "0", renderProps->get_properties(), 0, (mlt_destructor) mlt_properties_close, NULL); + + if (m_livePreview) + { + // user wants live preview + Mlt::Properties *previewProps = new Mlt::Properties; + QString videoDriver = KdenliveSettings::videodrivername(); + if (!videoDriver.isEmpty()) { + if (videoDriver == "x11_noaccel") { + setenv("SDL_VIDEO_YUV_HWACCEL", "0", 1); + videoDriver = "x11"; + } else { + unsetenv("SDL_VIDEO_YUV_HWACCEL"); + } + } + setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 1); + + if (m_winid == 0) { + // OpenGL monitor + previewProps->set("mlt_service", "sdl_audio"); + previewProps->set("preview_off", 1); + previewProps->set("preview_format", mlt_image_rgb24); + previewProps->set("terminate_on_pause", 0); + m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) consumer_gl_frame_show); + } else { + previewProps->set("mlt_service", "sdl_preview"); + previewProps->set("window_id", m_winid); + previewProps->set("terminate_on_pause", 0); + //m_showFrameEvent = m_mltConsumer->listen("consumer-frame-show", this, (mlt_listener) rec_consumer_frame_preview); + } + //m_mltConsumer->set("resize", 1); + previewProps->set("window_background", KdenliveSettings::window_background().name().toUtf8().constData()); + QString audioDevice = KdenliveSettings::audiodevicename(); + if (!audioDevice.isEmpty()) + previewProps->set("audio_device", audioDevice.toUtf8().constData()); + + if (!videoDriver.isEmpty()) + previewProps->set("video_driver", videoDriver.toUtf8().constData()); + + QString audioDriver = KdenliveSettings::audiodrivername(); + + if (!audioDriver.isEmpty()) + previewProps->set("audio_driver", audioDriver.toUtf8().constData()); + + previewProps->set("real_time", "0"); + previewProps->set("mlt_profile", m_activeProfile.toUtf8().constData()); + mlt_properties_set_data(consumerProperties, "1", previewProps->get_properties(), 0, (mlt_destructor) mlt_properties_close, NULL); + //m_showFrameEvent = m_mltConsumer->listen("consumer-frame-render", this, (mlt_listener) rec_consumer_frame_show); + } + else { + + } + + if (xmlPlaylist) { + // create an xml producer + m_mltProducer = new Mlt::Producer(*m_mltProfile, "xml-string", playlist.toUtf8().constData()); + } + else { + // create a producer based on mltproducer parameter + m_mltProducer = new Mlt::Producer(*m_mltProfile, playlist.toUtf8().constData()); + } if (m_mltProducer == NULL || !m_mltProducer->is_valid()) { kDebug()<<"//// ERROR CREATRING PROD"; + if (m_mltConsumer) { + delete m_mltConsumer; + m_mltConsumer = NULL; + } + if (m_mltProducer) { + delete m_mltProducer; + m_mltProducer = NULL; + } return false; } - + m_mltConsumer->connect(*m_mltProducer); if (m_mltConsumer->start() == -1) { + if (m_showFrameEvent) delete m_showFrameEvent; + m_showFrameEvent = NULL; delete m_mltConsumer; m_mltConsumer = NULL; return 0; } + m_droppedFramesTimer.start(); return 1; } +void MltDeviceCapture::setOverlay(const QString &path) +{ + if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return; + Mlt::Producer parentProd(m_mltProducer->parent()); + if (parentProd.get_producer() == NULL) { + kDebug() << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////"; + return; + } + + Mlt::Service service(parentProd.get_service()); + if (service.type() != tractor_type) { + kWarning() << "// TRACTOR PROBLEM"; + return; + } + Mlt::Tractor tractor(service); + if ( tractor.count() < 2) { + kWarning() << "// TRACTOR PROBLEM"; + return; + } + mlt_service_lock(service.get_service()); + Mlt::Producer trackProducer(tractor.track(0)); + Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service()); + + trackPlaylist.remove(0); + if (path.isEmpty()) { + mlt_service_unlock(service.get_service()); + return; + } + + // Add overlay clip + char *tmp = qstrdup(path.toUtf8().constData()); + Mlt::Producer *clip = new Mlt::Producer (*m_mltProfile, "loader", tmp); + delete[] tmp; + clip->set_in_and_out(0, 99999); + trackPlaylist.insert_at(0, clip, 1); + + // Add transition + mlt_service serv = m_mltProducer->parent().get_service(); + mlt_service nextservice = mlt_service_get_producer(serv); + mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice); + QString mlt_type = mlt_properties_get(properties, "mlt_type"); + if (mlt_type != "transition") { + // transition does not exist, add it + Mlt::Field *field = tractor.field(); + Mlt::Transition *transition = new Mlt::Transition(*m_mltProfile, "composite"); + transition->set_in_and_out(0, 0); + transition->set("geometry", "0/0:100%x100%:70"); + transition->set("fill", 1); + transition->set("operator", "and"); + transition->set("a_track", 0); + transition->set("b_track", 1); + field->plant_transition(*transition, 0, 1); + } + mlt_service_unlock(service.get_service()); + //delete clip; +} + +void MltDeviceCapture::setOverlayEffect(const QString &tag, const QStringList ¶meters) +{ + if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return; + Mlt::Service service(m_mltProducer->parent().get_service()); + Mlt::Tractor tractor(service); + Mlt::Producer trackProducer(tractor.track(0)); + Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service()); + Mlt::Service trackService(trackProducer.get_service()); + + mlt_service_lock(service.get_service()); + + // delete previous effects + Mlt::Filter *filter; + filter = trackService.filter(0); + if (filter && !tag.isEmpty()) { + QString currentService = filter->get("mlt_service"); + if (currentService == tag) { + // Effect is already there + mlt_service_unlock(service.get_service()); + return; + } + } + while (filter) { + trackService.detach(*filter); + delete filter; + filter = trackService.filter(0); + } + + if (tag.isEmpty()) { + mlt_service_unlock(service.get_service()); + return; + } + + char *tmp = qstrdup(tag.toUtf8().constData()); + filter = new Mlt::Filter(*m_mltProfile, tmp); + delete[] tmp; + if (filter && filter->is_valid()) { + for (int j = 0; j < parameters.count(); j++) { + filter->set(parameters.at(j).section('=', 0, 0).toUtf8().constData(), parameters.at(j).section('=', 1, 1).toUtf8().constData()); + } + trackService.attach(*filter); + } + mlt_service_unlock(service.get_service()); +} + +void MltDeviceCapture::mirror(bool activate) +{ + if (m_mltProducer == NULL || !m_mltProducer->is_valid()) return; + Mlt::Service service(m_mltProducer->parent().get_service()); + Mlt::Tractor tractor(service); + Mlt::Producer trackProducer(tractor.track(1)); + Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service()); + Mlt::Service trackService(trackProducer.get_service()); + + mlt_service_lock(service.get_service()); + + // delete previous effects + Mlt::Filter *filter; + filter = trackService.filter(0); + while (filter) { + trackService.detach(*filter); + delete filter; + filter = trackService.filter(0); + } + + if (!activate) { + mlt_service_unlock(service.get_service()); + return; + } + + filter = new Mlt::Filter(*m_mltProfile, "mirror"); + if (filter && filter->is_valid()) { + filter->set("mirror", "flip"); + trackService.attach(*filter); + } + mlt_service_unlock(service.get_service()); +} + +void MltDeviceCapture::uyvy2rgb(unsigned char *yuv_buffer, int width, int height) +{ + processingImage = true; + QImage image(width, height, QImage::Format_RGB888); + unsigned char *rgb_buffer = image.bits(); + + int len; + int r, g, b; + int Y, U, V, Y2; + int rgb_ptr, y_ptr, t; + + len = width * height / 2; + + rgb_ptr = 0; + y_ptr = 0; + + for (t = 0; t < len; t++) { + + + Y = yuv_buffer[y_ptr]; + U = yuv_buffer[y_ptr+1]; + Y2 = yuv_buffer[y_ptr+2]; + V = yuv_buffer[y_ptr+3]; + y_ptr += 4; + + r = ((298 * (Y - 16) + 409 * (V - 128) + 128) >> 8); + + g = ((298 * (Y - 16) - 100 * (U - 128) - 208 * (V - 128) + 128) >> 8); + + b = ((298 * (Y - 16) + 516 * (U - 128) + 128) >> 8); + + if (r > 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + + if (r < 0) r = 0; + if (g < 0) g = 0; + if (b < 0) b = 0; + + rgb_buffer[rgb_ptr] = r; + rgb_buffer[rgb_ptr+1] = g; + rgb_buffer[rgb_ptr+2] = b; + rgb_ptr += 3; + + + r = ((298 * (Y2 - 16) + 409 * (V - 128) + 128) >> 8); + + g = ((298 * (Y2 - 16) - 100 * (U - 128) - 208 * (V - 128) + 128) >> 8); + + b = ((298 * (Y2 - 16) + 516 * (U - 128) + 128) >> 8); + + if (r > 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + + if (r < 0) r = 0; + if (g < 0) g = 0; + if (b < 0) b = 0; + + rgb_buffer[rgb_ptr] = r; + rgb_buffer[rgb_ptr+1] = g; + rgb_buffer[rgb_ptr+2] = b; + rgb_ptr += 3; + } + //emit imageReady(image); + //m_captureDisplayWidget->setImage(image); + emit unblockPreview(); + //processingImage = false; +} + +void MltDeviceCapture::slotPreparePreview() +{ + QTimer::singleShot(1000, this, SLOT(slotAllowPreview())); +} + +void MltDeviceCapture::slotAllowPreview() +{ + processingImage = false; +} + + + +#include "mltdevicecapture.moc"