From b72003117bc6322414a01a4c8148575cfa089a9f Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Mardelle Date: Fri, 3 Sep 2010 16:59:31 +0000 Subject: [PATCH] Speedup: only convert displayed frame to QImage if necessary (for on monitor scene or scopes) svn path=/trunk/kdenlive/; revision=4826 --- src/abstractscopewidget.cpp | 85 +++++++++++++++++++++++-------------- src/abstractscopewidget.h | 6 +++ src/mainwindow.cpp | 59 +++++++++++++++++++++++++ src/mainwindow.h | 10 +++++ src/monitor.cpp | 8 +++- src/monitor.h | 4 ++ src/renderer.cpp | 15 ++++++- src/renderer.h | 5 ++- 8 files changed, 157 insertions(+), 35 deletions(-) diff --git a/src/abstractscopewidget.cpp b/src/abstractscopewidget.cpp index a94a921e..bbc5761a 100644 --- a/src/abstractscopewidget.cpp +++ b/src/abstractscopewidget.cpp @@ -23,28 +23,28 @@ const int REALTIME_FPS = 30; const QColor light(250, 238, 226, 255); -const QColor dark ( 40, 40, 39, 255); -const QColor dark2( 25, 25, 23, 255); +const QColor dark(40, 40, 39, 255); +const QColor dark2(25, 25, 23, 255); -const QPen AbstractScopeWidget::penThick(QBrush(QColor(250,250,250)), 2, Qt::SolidLine); -const QPen AbstractScopeWidget::penThin (QBrush(QColor(250,250,250)), 1, Qt::SolidLine); -const QPen AbstractScopeWidget::penLight(QBrush(QColor(200,200,250,150)), 1, Qt::SolidLine); -const QPen AbstractScopeWidget::penDark (QBrush(QColor(0,0,20,250)), 1, Qt::SolidLine); +const QPen AbstractScopeWidget::penThick(QBrush(QColor(250, 250, 250)), 2, Qt::SolidLine); +const QPen AbstractScopeWidget::penThin(QBrush(QColor(250, 250, 250)), 1, Qt::SolidLine); +const QPen AbstractScopeWidget::penLight(QBrush(QColor(200, 200, 250, 150)), 1, Qt::SolidLine); +const QPen AbstractScopeWidget::penDark(QBrush(QColor(0, 0, 20, 250)), 1, Qt::SolidLine); AbstractScopeWidget::AbstractScopeWidget(Monitor *projMonitor, Monitor *clipMonitor, bool trackMouse, QWidget *parent) : - QWidget(parent), - m_projMonitor(projMonitor), - m_clipMonitor(clipMonitor), - m_mousePos(0,0), - m_mouseWithinWidget(false), - offset(5), - m_accelFactorHUD(1), - m_accelFactorScope(1), - m_accelFactorBackground(1), - m_semaphoreHUD(1), - m_semaphoreScope(1), - m_semaphoreBackground(1), - initialDimensionUpdateDone(false) + QWidget(parent), + m_projMonitor(projMonitor), + m_clipMonitor(clipMonitor), + m_mousePos(0, 0), + m_mouseWithinWidget(false), + offset(5), + m_accelFactorHUD(1), + m_accelFactorScope(1), + m_accelFactorBackground(1), + m_semaphoreHUD(1), + m_semaphoreScope(1), + m_semaphoreBackground(1), + initialDimensionUpdateDone(false) { m_scopePalette = QPalette(); @@ -74,12 +74,12 @@ AbstractScopeWidget::AbstractScopeWidget(Monitor *projMonitor, Monitor *clipMoni bool b = true; b &= connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint))); - b &= connect(m_activeRender, SIGNAL(rendererPosition(int)), this, SLOT(slotRenderZoneUpdated())); + //b &= connect(m_activeRender, SIGNAL(rendererPosition(int)), this, SLOT(slotRenderZoneUpdated())); b &= connect(m_activeRender, SIGNAL(frameUpdated(QImage)), this, SLOT(slotRenderZoneUpdated(QImage))); - b &= connect(this, SIGNAL(signalHUDRenderingFinished(uint,uint)), this, SLOT(slotHUDRenderingFinished(uint,uint))); - b &= connect(this, SIGNAL(signalScopeRenderingFinished(uint,uint)), this, SLOT(slotScopeRenderingFinished(uint,uint))); - b &= connect(this, SIGNAL(signalBackgroundRenderingFinished(uint,uint)), this, SLOT(slotBackgroundRenderingFinished(uint,uint))); + b &= connect(this, SIGNAL(signalHUDRenderingFinished(uint, uint)), this, SLOT(slotHUDRenderingFinished(uint, uint))); + b &= connect(this, SIGNAL(signalScopeRenderingFinished(uint, uint)), this, SLOT(slotScopeRenderingFinished(uint, uint))); + b &= connect(this, SIGNAL(signalBackgroundRenderingFinished(uint, uint)), this, SLOT(slotBackgroundRenderingFinished(uint, uint))); b &= connect(m_aRealtime, SIGNAL(toggled(bool)), this, SLOT(slotResetRealtimeFactor(bool))); b &= connect(m_aAutoRefresh, SIGNAL(toggled(bool)), this, SLOT(slotAutoRefreshToggled(bool))); Q_ASSERT(b); @@ -123,7 +123,10 @@ void AbstractScopeWidget::writeConfig() scopeConfig.sync(); } -QString AbstractScopeWidget::configName() { return "Scope_" + m_widgetName; } +QString AbstractScopeWidget::configName() +{ + return "Scope_" + m_widgetName; +} void AbstractScopeWidget::prodHUDThread() { @@ -195,7 +198,9 @@ void AbstractScopeWidget::prodBackgroundThread() void AbstractScopeWidget::forceUpdate(bool doUpdate) { // qDebug() << "Force update called in " << widgetName() << ". Arg: " << doUpdate; - if (!doUpdate) { return; } + if (!doUpdate) { + return; + } m_newHUDUpdates.fetchAndAddRelaxed(1); m_newScopeUpdates.fetchAndAddRelaxed(1); m_newBackgroundUpdates.fetchAndAddRelaxed(1); @@ -227,6 +232,7 @@ void AbstractScopeWidget::forceUpdateBackground() void AbstractScopeWidget::mouseReleaseEvent(QMouseEvent *event) { + if (!m_aAutoRefresh->isChecked()) m_activeRender->sendFrameUpdate(); prodHUDThread(); prodScopeThread(); prodBackgroundThread(); @@ -274,9 +280,18 @@ void AbstractScopeWidget::customContextMenuRequested(const QPoint &pos) m_menu->exec(this->mapToGlobal(pos)); } -uint AbstractScopeWidget::calculateAccelFactorHUD(uint oldMseconds, uint) { return ceil((float)oldMseconds*REALTIME_FPS/1000 ); } -uint AbstractScopeWidget::calculateAccelFactorScope(uint oldMseconds, uint) { return ceil((float)oldMseconds*REALTIME_FPS/1000 ); } -uint AbstractScopeWidget::calculateAccelFactorBackground(uint oldMseconds, uint) { return ceil((float)oldMseconds*REALTIME_FPS/1000 ); } +uint AbstractScopeWidget::calculateAccelFactorHUD(uint oldMseconds, uint) +{ + return ceil((float)oldMseconds*REALTIME_FPS / 1000); +} +uint AbstractScopeWidget::calculateAccelFactorScope(uint oldMseconds, uint) +{ + return ceil((float)oldMseconds*REALTIME_FPS / 1000); +} +uint AbstractScopeWidget::calculateAccelFactorBackground(uint oldMseconds, uint) +{ + return ceil((float)oldMseconds*REALTIME_FPS / 1000); +} ///// Slots ///// @@ -299,7 +314,7 @@ void AbstractScopeWidget::slotHUDRenderingFinished(uint mseconds, uint oldFactor m_accelFactorHUD = accel; } - if ( (m_newHUDFrames > 0 && m_aAutoRefresh->isChecked()) || m_newHUDUpdates > 0) { + if ((m_newHUDFrames > 0 && m_aAutoRefresh->isChecked()) || m_newHUDUpdates > 0) { // qDebug() << "Trying to start a new HUD thread for " << m_widgetName // << ". New frames/updates: " << m_newHUDFrames << "/" << m_newHUDUpdates; prodHUDThread();; @@ -333,7 +348,7 @@ void AbstractScopeWidget::slotScopeRenderingFinished(uint mseconds, uint oldFact m_accelFactorScope = accel; } - if ( (m_newScopeFrames > 0 && m_aAutoRefresh->isChecked()) || m_newScopeUpdates > 0) { + if ((m_newScopeFrames > 0 && m_aAutoRefresh->isChecked()) || m_newScopeUpdates > 0) { // qDebug() << "Trying to start a new scope thread for " << m_widgetName // << ". New frames/updates: " << m_newScopeFrames << "/" << m_newScopeUpdates; prodScopeThread(); @@ -358,7 +373,7 @@ void AbstractScopeWidget::slotBackgroundRenderingFinished(uint mseconds, uint ol m_accelFactorBackground = accel; } - if ( (m_newBackgroundFrames > 0 && m_aAutoRefresh->isChecked()) || m_newBackgroundUpdates > 0) { + if ((m_newBackgroundFrames > 0 && m_aAutoRefresh->isChecked()) || m_newBackgroundUpdates > 0) { // qDebug() << "Trying to start a new background thread for " << m_widgetName // << ". New frames/updates: " << m_newBackgroundFrames << "/" << m_newBackgroundUpdates; prodBackgroundThread();; @@ -374,7 +389,7 @@ void AbstractScopeWidget::slotActiveMonitorChanged(bool isClipMonitor) m_activeRender = (isClipMonitor) ? m_clipMonitor->render : m_projMonitor->render; - b &= connect(m_activeRender, SIGNAL(rendererPosition(int)), this, SLOT(slotRenderZoneUpdated())); + //b &= connect(m_activeRender, SIGNAL(rendererPosition(int)), this, SLOT(slotRenderZoneUpdated())); b &= connect(m_activeRender, SIGNAL(frameUpdated(QImage)), this, SLOT(slotRenderZoneUpdated(QImage))); Q_ASSERT(b); @@ -419,8 +434,14 @@ void AbstractScopeWidget::slotResetRealtimeFactor(bool realtimeChecked) } } +bool AbstractScopeWidget::autoRefreshEnabled() +{ + return m_aAutoRefresh->isChecked(); +} + void AbstractScopeWidget::slotAutoRefreshToggled(bool autoRefresh) { + if (isVisible()) emit requestAutoRefresh(autoRefresh); // TODO only if depends on input if (autoRefresh) { forceUpdate(); diff --git a/src/abstractscopewidget.h b/src/abstractscopewidget.h index 140f127a..2d38c30e 100644 --- a/src/abstractscopewidget.h +++ b/src/abstractscopewidget.h @@ -72,6 +72,9 @@ public: Has to be called in the implementing object. */ void init(); + /** Does this scope have auto-refresh enabled */ + bool autoRefreshEnabled(); + ///// Unimplemented ///// virtual QString widgetName() const = 0; @@ -200,6 +203,9 @@ signals: To check whether the mouse has leaved the widget, see m_mouseWithinWidget. */ void signalMousePositionChanged(); + /** Do we need the renderer to send its frames to us? */ + void requestAutoRefresh(bool); + private: /** Counts the number of frames that have been rendered in the active monitor. diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 592627b3..7deb3717 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -222,6 +222,9 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, QWidget *parent m_vectorscopeDock->setWidget(m_vectorscope); addDockWidget(Qt::TopDockWidgetArea, m_vectorscopeDock); connect(m_vectorscopeDock, SIGNAL(visibilityChanged(bool)), m_vectorscope, SLOT(forceUpdate(bool))); + connect(m_vectorscopeDock, SIGNAL(visibilityChanged(bool)), this, SLOT(slotUpdateScopeFrameRequest())); + connect(m_vectorscope, SIGNAL(requestAutoRefresh(bool)), this, SLOT(slotUpdateScopeFrameRequest())); + m_scopesList.append(m_vectorscopeDock); m_waveform = new Waveform(m_projectMonitor, m_clipMonitor, this); m_waveformDock = new QDockWidget(i18n("Waveform"), this); @@ -229,6 +232,9 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, QWidget *parent m_waveformDock->setWidget(m_waveform); addDockWidget(Qt::TopDockWidgetArea, m_waveformDock); connect(m_waveformDock, SIGNAL(visibilityChanged(bool)), m_waveform, SLOT(forceUpdate(bool))); + connect(m_waveformDock, SIGNAL(visibilityChanged(bool)), this, SLOT(slotUpdateScopeFrameRequest())); + connect(m_waveform, SIGNAL(requestAutoRefresh(bool)), this, SLOT(slotUpdateScopeFrameRequest())); + m_scopesList.append(m_waveformDock); m_RGBParade = new RGBParade(m_projectMonitor, m_clipMonitor, this); m_RGBParadeDock = new QDockWidget(i18n("RGB Parade"), this); @@ -236,6 +242,9 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, QWidget *parent m_RGBParadeDock->setWidget(m_RGBParade); addDockWidget(Qt::TopDockWidgetArea, m_RGBParadeDock); connect(m_RGBParadeDock, SIGNAL(visibilityChanged(bool)), m_RGBParade, SLOT(forceUpdate(bool))); + connect(m_RGBParadeDock, SIGNAL(visibilityChanged(bool)), this, SLOT(slotUpdateScopeFrameRequest())); + connect(m_RGBParade, SIGNAL(requestAutoRefresh(bool)), this, SLOT(slotUpdateScopeFrameRequest())); + m_scopesList.append(m_RGBParadeDock); m_histogram = new Histogram(m_projectMonitor, m_clipMonitor, this); m_histogramDock = new QDockWidget(i18n("Histogram"), this); @@ -243,6 +252,9 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, QWidget *parent m_histogramDock->setWidget(m_histogram); addDockWidget(Qt::TopDockWidgetArea, m_histogramDock); connect(m_histogramDock, SIGNAL(visibilityChanged(bool)), m_histogram, SLOT(forceUpdate(bool))); + connect(m_histogramDock, SIGNAL(visibilityChanged(bool)), this, SLOT(slotUpdateScopeFrameRequest())); + connect(m_histogram, SIGNAL(requestAutoRefresh(bool)), this, SLOT(slotUpdateScopeFrameRequest())); + m_scopesList.append(m_histogramDock); m_undoViewDock = new QDockWidget(i18n("Undo History"), this); @@ -763,6 +775,8 @@ void MainWindow::slotConnectMonitors() connect(m_clipMonitor, SIGNAL(adjustMonitorSize()), this, SLOT(slotAdjustClipMonitor())); connect(m_projectMonitor, SIGNAL(adjustMonitorSize()), this, SLOT(slotAdjustProjectMonitor())); + connect(m_projectMonitor, SIGNAL(requestFrameForAnalysis(bool)), this, SLOT(slotMonitorRequestRenderFrame(bool))); + connect(m_clipMonitor, SIGNAL(saveZone(Render *, QPoint)), this, SLOT(slotSaveZone(Render *, QPoint))); connect(m_projectMonitor, SIGNAL(saveZone(Render *, QPoint)), this, SLOT(slotSaveZone(Render *, QPoint))); } @@ -3706,5 +3720,50 @@ QString MainWindow::getMimeType() return mimetype; } +void MainWindow::slotMonitorRequestRenderFrame(bool request) +{ + if (request) { + m_projectMonitor->render->sendFrameForAnalysis = true; + return; + } else { + for (int i = 0; i < m_scopesList.count(); i++) { + if (m_scopesList.at(i)->isVisible() && tabifiedDockWidgets(m_scopesList.at(i)).isEmpty() && static_cast(m_scopesList.at(i)->widget())->autoRefreshEnabled()) { + request = true; + break; + } + } + } + if (!request) { + m_projectMonitor->render->sendFrameForAnalysis = false; + } +} + +void MainWindow::slotUpdateScopeFrameRequest() +{ + // We need a delay to make sure widgets are hidden after a close event for example + QTimer::singleShot(500, this, SLOT(slotDoUpdateScopeFrameRequest())); +} + +void MainWindow::slotDoUpdateScopeFrameRequest() +{ + // Check scopes + bool request = false; + for (int i = 0; i < m_scopesList.count(); i++) { + if (!m_scopesList.at(i)->widget()->visibleRegion().isEmpty() && static_cast(m_scopesList.at(i)->widget())->autoRefreshEnabled()) { + kDebug() << "SCOPE VISIBLE: " << static_cast(m_scopesList.at(i)->widget())->widgetName(); + request = true; + break; + } + } + if (!request) { + if (!m_projectMonitor->effectSceneDisplayed()) + m_projectMonitor->render->sendFrameForAnalysis = false; + m_clipMonitor->render->sendFrameForAnalysis = false; + } else { + m_projectMonitor->render->sendFrameForAnalysis = true; + m_clipMonitor->render->sendFrameForAnalysis = true; + } +} + #include "mainwindow.moc" diff --git a/src/mainwindow.h b/src/mainwindow.h index 694c0337..4aa19a94 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -67,6 +67,7 @@ class Waveform; class RGBParade; class KActionCollection; + class MainWindow : public KXmlGuiWindow { Q_OBJECT @@ -173,6 +174,9 @@ private: KComboBox *m_timecodeFormat; + /** This list holds all the scopes used in Kdenlive, allowing to manage some global settings */ + QList m_scopesList; + QMenu *m_videoEffectsMenu; QMenu *m_audioEffectsMenu; QMenu *m_customEffectsMenu; @@ -462,6 +466,12 @@ private slots: void slotShowTitleBars(bool show); void slotSwitchTitles(); + /** @brief The monitor informs that it needs (or not) to have frames sent by the renderer. */ + void slotMonitorRequestRenderFrame(bool request); + /** @brief Check if someone needs the render frame sent. */ + void slotUpdateScopeFrameRequest(); + void slotDoUpdateScopeFrameRequest(); + signals: Q_SCRIPTABLE void abortRenderJob(const QString &url); }; diff --git a/src/monitor.cpp b/src/monitor.cpp index 82a4f29e..b7f8f143 100644 --- a/src/monitor.cpp +++ b/src/monitor.cpp @@ -849,6 +849,7 @@ void Monitor::slotEffectScene(bool show) m_monitorRefresh->setVisible(!show); #endif m_effectView->setVisible(show); + emit requestFrameForAnalysis(show); if (show) { render->doRefresh(); m_effectScene->slotZoomFit(); @@ -861,7 +862,12 @@ MonitorScene * Monitor::getEffectScene() return m_effectScene; } -MonitorRefresh::MonitorRefresh(QWidget* parent) : \ +bool Monitor::effectSceneDisplayed() +{ + return m_effectView->isVisible(); +} + +MonitorRefresh::MonitorRefresh(QWidget* parent) : QWidget(parent), m_renderer(NULL) { diff --git a/src/monitor.h b/src/monitor.h index afdc611b..ff6b88fb 100644 --- a/src/monitor.h +++ b/src/monitor.h @@ -178,6 +178,7 @@ public slots: void setTimePos(const QString &pos); QStringList getZoneInfo() const; void slotEffectScene(bool show = true); + bool effectSceneDisplayed(); signals: void renderPosition(int); @@ -186,6 +187,9 @@ signals: void adjustMonitorSize(); void zoneUpdated(QPoint); void saveZone(Render *, QPoint); + /** @brief Editing transitions / effects over the monitor requires thr renderer to send frames as QImage. + * This causes a major slowdown, so we only enable it if required */ + void requestFrameForAnalysis(bool); }; #endif diff --git a/src/renderer.cpp b/src/renderer.cpp index c6086450..1ec3d2a0 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -66,8 +66,9 @@ static void consumer_frame_show(mlt_consumer, Render * self, mlt_frame frame_ptr #endif self->emitFrameNumber(mlt_frame_get_position(frame_ptr)); - if (frame_ptr->convert_image) + if (self->sendFrameForAnalysis && frame_ptr->convert_image) { self->emitFrameUpdated(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) { @@ -80,6 +81,7 @@ static void consumer_frame_show(mlt_consumer, Render * self, mlt_frame frame_ptr Render::Render(const QString & rendererName, int winid, int /* extid */, QString profile, QWidget *parent) : QObject(parent), m_isBlocked(0), + sendFrameForAnalysis(false), m_name(rendererName), m_mltConsumer(NULL), m_mltProducer(NULL), @@ -4082,5 +4084,16 @@ QString Render::updateSceneListFps(double current_fps, double new_fps, QString s return doc.toString(); } + +void Render::sendFrameUpdate() +{ + if (m_mltProducer) { + Mlt::Frame * frame = m_mltProducer->get_frame(); + emitFrameUpdated(*frame); + delete frame; + } +} + + #include "renderer.moc" diff --git a/src/renderer.h b/src/renderer.h index 693dd405..bb513144 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -244,7 +244,7 @@ Q_OBJECT public: * * It creates a new "framebuffer" producer, which must have its "resource" * property set to "video.mpg?0.6", where "video.mpg" is the path to the - * clip and "0.6" is the speed in percentile. The newly created producer + * clip and "0.6" is the speed in percentage. The newly created producer * will have its "id" property set to "slowmotion:parentid:speed", where * "parentid" is the id of the original clip in the ClipManager list and * "speed" is the current speed. */ @@ -257,7 +257,10 @@ Q_OBJECT public: #ifdef Q_WS_MAC void showFrame(Mlt::Frame&); #endif + /** @brief This property is used to decide if the renderer should convert it's frames to QImage for use in other Kdenlive widgets. */ + bool sendFrameForAnalysis; QList checkTrackSequence(int); + void sendFrameUpdate(); private: -- 2.39.2