Commit ceff8337 authored by Hugo Beauzée-Luyssen's avatar Hugo Beauzée-Luyssen

Effects: Refactorize effects handling.

Effects are now handled by an EffectUser class. This class handle
loading, saving, adding, initializing and applicating of any effect that
will be added to a class that inherits EffectUser.
parent 5c8b473d
......@@ -8,6 +8,7 @@ SET(VLMC_SRCS
Commands/Commands.cpp
EffectsEngine/EffectsEngine.cpp
EffectsEngine/Effect.cpp
EffectsEngine/EffectUser.cpp
EffectsEngine/EffectInstance.cpp
EffectsEngine/EffectSettingValue.cpp
Library/Library.cpp
......@@ -60,6 +61,7 @@ ENDIF(WIN32)
SET (VLMC_HDRS
EffectsEngine/EffectsEngine.h
EffectsEngine/Effect.h
EffectsEngine/EffectUser.h
EffectsEngine/EffectInstance.h
EffectsEngine/EffectSettingValue.h
Library/Library.h
......
/*****************************************************************************
* EffectUser.cpp: Handles effects list and application
*****************************************************************************
* Copyright (C) 2008-2010 VideoLAN
*
* Authors: Hugo Beauzée-Luyssen <beauze.h@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "EffectUser.h"
#include "EffectInstance.h"
#include "Types.h"
#include <QDomElement>
#include <QReadWriteLock>
#include <QtDebug>
EffectUser::EffectUser() :
m_isRendering( false ),
m_width( 0 ),
m_height( 0 )
{
m_effectsLock = new QReadWriteLock;
}
EffectUser::~EffectUser()
{
delete m_effectsLock;
}
EffectsEngine::EffectHelper*
EffectUser::addEffect( Effect *effect, qint64 start /*= 0*/, qint64 end /*= -1*/ )
{
//FIXME: Check it the effect type is supported
EffectInstance *effectInstance = effect->createInstance();
if ( m_isRendering == true )
effectInstance->init( m_width, m_height );
EffectsEngine::EffectHelper *ret = new EffectsEngine::EffectHelper( effectInstance, start, end );
QWriteLocker lock( m_effectsLock );
if ( effect->type() == Effect::Filter )
m_filters.push_back( ret );
else
m_mixers.push_back( ret );
return ret;
}
quint32*
EffectUser::applyFilters( const Workflow::Frame* frame,
qint64 currentFrame, double time )
{
QReadLocker lock( m_effectsLock );
if ( m_filters.size() == 0 )
return NULL;
EffectsEngine::EffectList::const_iterator it = m_filters.constBegin();
EffectsEngine::EffectList::const_iterator ite = m_filters.constEnd();
quint32 *buff1 = NULL;
quint32 *buff2 = NULL;
const quint32 *input = frame->buffer();
bool firstBuff = true;
while ( it != ite )
{
if ( (*it)->start < currentFrame &&
( (*it)->end < 0 || (*it)->end > currentFrame ) )
{
quint32 **buff;
if ( firstBuff == true )
buff = &buff1;
else
buff = &buff2;
if ( *buff == NULL )
*buff = new quint32[frame->nbPixels()];
EffectInstance *effect = (*it)->effect;
effect->process( time, input, *buff );
input = *buff;
firstBuff = !firstBuff;
}
++it;
}
if ( buff1 != NULL || buff2 != NULL )
{
if ( firstBuff == true )
{
delete[] buff1;
return buff2;
}
else
{
delete[] buff2;
return buff1;
}
}
return NULL;
}
void
EffectUser::initFilters()
{
QReadLocker lock( m_effectsLock );
EffectsEngine::EffectList::const_iterator it = m_filters.begin();
EffectsEngine::EffectList::const_iterator ite = m_filters.end();
while ( it != ite )
{
(*it)->effect->init( m_width, m_height );
++it;
}
}
void
EffectUser::initMixers()
{
QReadLocker lock( m_effectsLock );
EffectsEngine::EffectList::const_iterator it = m_mixers.begin();
EffectsEngine::EffectList::const_iterator ite = m_mixers.end();
while ( it != ite )
{
(*it)->effect->init( m_width, m_height );
++it;
}
}
EffectsEngine::EffectHelper*
EffectUser::getMixer( qint64 currentFrame )
{
QReadLocker lock( m_effectsLock );
EffectsEngine::EffectList::const_iterator it = m_mixers.constBegin();
EffectsEngine::EffectList::const_iterator ite = m_mixers.constEnd();
while ( it != ite )
{
if ( (*it)->start <= currentFrame && currentFrame <= (*it)->end )
{
Q_ASSERT( (*it)->effect->effect()->type() == Effect::Mixer2 );
return (*it);
}
++it;
}
return NULL;
}
void
EffectUser::loadEffects( const QDomElement &parent )
{
QDomElement effects = parent.firstChildElement( "effects" );
if ( effects.isNull() == true )
return ;
QDomElement effect = effects.firstChildElement( "effect" );
while ( effect.isNull() == false )
{
if ( effect.hasAttribute( "name" ) == true &&
effect.hasAttribute( "start" ) == true &&
effect.hasAttribute( "end" ) == true )
{
Effect *e = EffectsEngine::getInstance()->effect( effect.attribute( "name" ) );
if ( e != NULL )
addEffect( e, effect.attribute( "start" ).toLongLong(),
effect.attribute( "end" ).toLongLong() );
else
qCritical() << "Renderer: Can't load effect" << effect.attribute( "name" );
}
effect = effect.nextSiblingElement();
}
}
void
EffectUser::saveFilters( QXmlStreamWriter &project ) const
{
QReadLocker lock( m_effectsLock );
if ( m_filters.size() <= 0 )
return ;
project.writeStartElement( "effects" );
EffectsEngine::EffectList::const_iterator it = m_filters.begin();
EffectsEngine::EffectList::const_iterator ite = m_filters.end();
while ( it != ite )
{
project.writeStartElement( "effect" );
project.writeAttribute( "name", (*it)->effect->effect()->name() );
project.writeAttribute( "start", QString::number( (*it)->start ) );
project.writeAttribute( "end", QString::number( (*it)->end ) );
project.writeEndElement();
++it;
}
project.writeEndElement();
}
/*****************************************************************************
* EffectUser.h: Handles effects list and application
*****************************************************************************
* Copyright (C) 2008-2010 VideoLAN
*
* Authors: Hugo Beauzée-Luyssen <beauze.h@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef EFFECTUSER_H
#define EFFECTUSER_H
#include "EffectsEngine.h"
#include <QObject>
#include <QList>
#include <QXmlStreamWriter>
class QDomElement;
class QReadWriteLock;
class EffectUser : public QObject
{
public:
/**
* \brief Add an effect to the TrackWorkflow
*
* \param effect The effect instance. Can be either mixer or filter.
*/
EffectsEngine::EffectHelper *addEffect( Effect *effect, qint64 start = 0, qint64 end = -1 );
protected:
EffectUser();
~EffectUser();
void initFilters();
void initMixers();
//Filters:
quint32 *applyFilters( const Workflow::Frame *frame,
qint64 currentFrame, double time );
void loadEffects( const QDomElement &project );
void saveFilters( QXmlStreamWriter &project ) const;
//Mixers methods:
EffectsEngine::EffectHelper *getMixer( qint64 currentFrame );
protected:
/**
* \brief Will be equal to true if a render has been started, even if it paused.
*
* If this is true, when an effect is loaded, it has to be initialized
* immediately,
*/
bool m_isRendering;
quint32 m_width;
quint32 m_height;
QReadWriteLock *m_effectsLock;
EffectsEngine::EffectList m_mixers;
EffectsEngine::EffectList m_filters;
};
#endif // EFFECTUSER_H
......@@ -116,106 +116,6 @@ EffectsEngine::browseDirectory( const QString &path )
}
}
quint32*
EffectsEngine::applyFilters( const EffectList &effects, const Workflow::Frame* frame,
qint64 currentFrame, double time )
{
if ( effects.size() == 0 )
return NULL;
EffectList::const_iterator it = effects.constBegin();
EffectList::const_iterator ite = effects.constEnd();
quint32 *buff1 = NULL;
quint32 *buff2 = NULL;
const quint32 *input = frame->buffer();
bool firstBuff = true;
while ( it != ite )
{
if ( (*it)->start < currentFrame &&
( (*it)->end < 0 || (*it)->end > currentFrame ) )
{
quint32 **buff;
if ( firstBuff == true )
buff = &buff1;
else
buff = &buff2;
if ( *buff == NULL )
*buff = new quint32[frame->nbPixels()];
EffectInstance *effect = (*it)->effect;
effect->process( time, input, *buff );
input = *buff;
firstBuff = !firstBuff;
}
++it;
}
if ( buff1 != NULL || buff2 != NULL )
{
if ( firstBuff == true )
{
delete[] buff1;
return buff2;
}
else
{
delete[] buff2;
return buff1;
}
}
return NULL;
}
void
EffectsEngine::saveFilters( const EffectList &effects, QXmlStreamWriter &project )
{
if ( effects.size() <= 0 )
return ;
EffectsEngine::EffectList::const_iterator it = effects.begin();
EffectsEngine::EffectList::const_iterator ite = effects.end();
project.writeStartElement( "effects" );
while ( it != ite )
{
project.writeStartElement( "effect" );
project.writeAttribute( "name", (*it)->effect->effect()->name() );
project.writeAttribute( "start", QString::number( (*it)->start ) );
project.writeAttribute( "end", QString::number( (*it)->end ) );
project.writeEndElement();
++it;
}
project.writeEndElement();
}
void
EffectsEngine::initEffects( const EffectList &effects, quint32 width, quint32 height )
{
EffectsEngine::EffectList::const_iterator it = effects.begin();
EffectsEngine::EffectList::const_iterator ite = effects.end();
while ( it != ite )
{
(*it)->effect->init( width, height );
++it;
}
}
EffectsEngine::EffectHelper*
EffectsEngine::getMixer( const EffectList &mixers, qint64 currentFrame )
{
EffectList::const_iterator it = mixers.constBegin();
EffectList::const_iterator ite = mixers.constEnd();
while ( it != ite )
{
if ( (*it)->start <= currentFrame && currentFrame <= (*it)->end )
{
Q_ASSERT( (*it)->effect->effect()->type() == Effect::Mixer2 );
return (*it);
}
++it;
}
return NULL;
}
void
EffectsEngine::loadEffects()
{
......
......@@ -78,15 +78,6 @@ class EffectsEngine : public QObject, public Singleton<EffectsEngine>
bool loadEffect( const QString& fileName );
void loadEffects();
static void initEffects( const EffectList &effects, quint32 width, quint32 height );
//Filters methods:
static quint32 *applyFilters( const EffectList &effects,
const Workflow::Frame *frame, qint64 currentFrame, double time );
static void saveFilters( const EffectList &effects, QXmlStreamWriter &project );
//Mixers methods:
static EffectHelper *getMixer( const EffectList &mixers, qint64 currentFrame );
private:
EffectsEngine();
~EffectsEngine();
......
......@@ -777,7 +777,7 @@ TracksView::dropEvent( QDropEvent *event )
{
foreach ( AbstractGraphicsMediaItem *item, clips )
{
item->clipHelper()->clipWorkflow()->appendEffect( m_dragEffect );
item->clipHelper()->clipWorkflow()->addEffect( m_dragEffect );
}
}
else
......
......@@ -24,8 +24,7 @@
#include "VLCMediaPlayer.h"
GenericRenderer::GenericRenderer() :
m_paused( false ),
m_isRendering( false )
m_paused( false )
{
m_mediaPlayer = new LibVLCpp::MediaPlayer();
}
......
......@@ -30,6 +30,7 @@
# include <QWidget>
#endif
#include "EffectUser.h"
#include "Types.h"
class Clip;
......@@ -43,7 +44,7 @@ namespace LibVLCpp
/**
* \class Common base for every renderer.
*/
class GenericRenderer : public QObject
class GenericRenderer : public EffectUser
{
Q_OBJECT
Q_DISABLE_COPY( GenericRenderer );
......@@ -156,12 +157,6 @@ protected:
*/
bool m_paused;
/**
* \brief Will be equal to true if a render has been started, even if it paused.
* \sa isRendering()
*/
bool m_isRendering;
/**
* \brief The QWidget on which we will render.
* \sa setRenderWidget( QWidget* );
......
......@@ -53,7 +53,6 @@ WorkflowRenderer::WorkflowRenderer() :
m_oldLength( 0 ),
m_effectFrame( NULL )
{
m_effectsLock = new QReadWriteLock;
}
void WorkflowRenderer::initializeRenderer()
......@@ -88,7 +87,6 @@ WorkflowRenderer::~WorkflowRenderer()
delete m_media;
if ( m_silencedAudioBuffer )
delete m_silencedAudioBuffer;
delete m_effectsLock;
}
void
......@@ -174,12 +172,8 @@ WorkflowRenderer::lockVideo( EsHandler *handler, qint64 *pts, size_t *bufferSize
//this is a bit hackish though... (especially regarding the "no frame computed" detection)
ptsDiff = 1000000 / handler->fps;
}
{
QReadLocker lock( m_effectsLock );
m_effectFrame = EffectsEngine::applyFilters( m_filters, ret,
m_mainWorkflow->getCurrentFrame(),
m_mainWorkflow->getCurrentFrame() * 1000.0 / handler->fps );
}
m_effectFrame = applyFilters( ret, m_mainWorkflow->getCurrentFrame(),
m_mainWorkflow->getCurrentFrame() * 1000.0 / handler->fps );
m_pts = *pts = ptsDiff + m_pts;
if ( m_effectFrame != NULL )
*buffer = m_effectFrame;
......@@ -244,8 +238,7 @@ void WorkflowRenderer::startPreview()
m_outputFps = outputFps();
setupRenderer( m_width, m_height, m_outputFps );
}
QReadLocker lock( m_effectsLock );
EffectsEngine::initEffects( m_filters, m_width, m_height );
initFilters();
//Deactivating vlc's keyboard inputs.
m_mediaPlayer->setKeyInput( false );
......@@ -398,32 +391,11 @@ WorkflowRenderer::paramsHasChanged( quint32 width, quint32 height, double fps )
newOutputFps != fps );
}
EffectsEngine::EffectHelper*
WorkflowRenderer::appendEffect( Effect *effect, qint64 start, qint64 end )
{
if ( effect->type() != Effect::Filter )
{
qWarning() << "WorkflowRenderer does not handle non filter effects.";
return NULL;
}
EffectInstance *effectInstance = effect->createInstance();
if ( isRendering() == true )
effectInstance->init( m_width, m_height );
QWriteLocker lock( m_effectsLock );
EffectsEngine::EffectHelper *ret = new EffectsEngine::EffectHelper( effectInstance, start, end );
m_filters.push_back( ret );
return ret;
}
void
WorkflowRenderer::saveProject( QXmlStreamWriter &project ) const
{
project.writeStartElement( "renderer" );
{
QReadLocker lock( m_effectsLock );
EffectsEngine::saveFilters( m_filters, project );
}
saveFilters( project );
project.writeEndElement();
}
......@@ -433,25 +405,7 @@ WorkflowRenderer::loadProject( const QDomElement &project )
QDomElement renderer = project.firstChildElement( "renderer" );
if ( renderer.isNull() == true )
return ;
QDomElement effects = renderer.firstChildElement( "effects" );
if ( effects.isNull() == true )
return ;
QDomElement effect = effects.firstChildElement( "effect" );
while ( effect.isNull() == false )
{
if ( effect.hasAttribute( "name" ) == true &&
effect.hasAttribute( "start" ) == true &&
effect.hasAttribute( "end" ) == true )
{
Effect *e = EffectsEngine::getInstance()->effect( effect.attribute( "name" ) );
if ( e != NULL )
appendEffect( e, effect.attribute( "start" ).toLongLong(),
effect.attribute( "end" ).toLongLong() );
else
qCritical() << "Renderer: Can't load effect" << effect.attribute( "name" );
}
effect = effect.nextSiblingElement();
}
loadEffects( renderer );
}
/////////////////////////////////////////////////////////////////////
......
......@@ -23,11 +23,9 @@
#ifndef WORKFLOWRENDERER_H
#define WORKFLOWRENDERER_H
#include "EffectsEngine/EffectsEngine.h"
#include "GenericRenderer.h"
#include "MainWorkflow.h"
#include <QObject>
class Clip;
......@@ -131,9 +129,6 @@ class WorkflowRenderer : public GenericRenderer
*/
void killRenderer();
EffectsEngine::EffectHelper *appendEffect( Effect* effect, qint64 start = 0,
qint64 end = -1 );
void saveProject( QXmlStreamWriter &project ) const;
void loadProject( const QDomElement& project );
private:
......@@ -284,7 +279,6 @@ class WorkflowRenderer : public GenericRenderer
*/
qint64 m_oldLength;
QReadWriteLock *m_effectsLock;
EffectsEngine::EffectList m_filters;
quint32 *m_effectFrame;
......
......@@ -43,8 +43,6 @@ class AudioClipWorkflow : public ClipWorkflow
void *getLockCallback() const;
void *getUnlockCallback() const;
virtual Workflow::OutputBuffer *getOutput( ClipWorkflow::GetMode mode );
virtual void saveEffects( QXmlStreamWriter & ) const {} //Nothing to do here now.
virtual EffectsEngine::EffectHelper *appendEffect( Effect *, qint64, qint64 ) { return NULL; } //Nothing to do here now.
protected:
virtual quint32 getNbComputedBuffers() const;
virtual quint32 getMaxComputedBuffers() const;
......
......@@ -83,6 +83,7 @@ ClipWorkflow::loadingComplete()
connect( m_mediaPlayer, SIGNAL( playing() ), this, SLOT( mediaPlayerUnpaused() ), Qt::DirectConnection );
connect( m_mediaPlayer, SIGNAL( paused() ), this, SLOT( mediaPlayerPaused() ), Qt::DirectConnection );
QMutexLocker lock( m_initWaitCond->getMutex() );
m_isRendering = true;
setState( Rendering );
m_initWaitCond->wake();
}
......@@ -114,6 +115,7 @@ void
ClipWorkflow::stop()
{
stopRenderer();
m_isRendering = false;
flushComputedBuffers();
releasePrealocated();
}
......@@ -324,5 +326,5 @@ ClipWorkflow::save( QXmlStreamWriter &project ) const
project.writeAttribute( "begin", QString::number( m_clipHelper->begin() ) );
project.writeAttribute( "end", QString::number( m_clipHelper->end() ) );
project.writeAttribute( "helper", m_clipHelper->uuid().toString() );
saveEffects( project );
saveFilters( project );
}
......@@ -24,7 +24,8 @@
#define CLIPWORKFLOW_H
#include "mdate.h"
#include "EffectsEngine.h"
#include "EffectUser.h"
#include "ClipHelper.h"
#include "Types.h"
......@@ -51,7 +52,7 @@ namespace LibVLCpp
class Media;
}
class ClipWorkflow : public QObject
class ClipWorkflow : public EffectUser
{
Q_OBJECT
......@@ -202,9 +203,6 @@ class ClipWorkflow : public QObject
void stopRenderer();
void save( QXmlStreamWriter& project ) const;
virtual void saveEffects( QXmlStreamWriter& ) const = 0;
virtual EffectsEngine::EffectHelper *appendEffect( Effect *effect, qint64 start = 0,
qint64 end = -1 ) = 0;
private:
void setState( State state );
......
......@@ -40,7 +40,6 @@ ImageClipWorkflow::ImageClipWorkflow( ClipHelper *ch ) :
//from vlc's input thread (well it can but it will deadlock)
connect( this, SIGNAL( computedFinished() ),
this, SLOT( stopComputation() ), Qt::QueuedConnection );
m_effectsLock = new QReadWriteLock;
m_effectFrame = new Workflow::Frame;
}
......@@ -49,7 +48,6 @@ ImageClipWorkflow::~ImageClipWorkflow()
stop();
qDeleteAll( m_filters );
delete m_effectFrame;
delete m_effectsLock;
}
void
......@@ -66,11 +64,11 @@ ImageClipWorkflow::initVlcOutput()
m_vlcMedia->addOption( ":sout-transcode-vcodec=RV32" );
m_vlcMedia->addOption( ":sout-smem-time-sync" );
sprintf( buffer, ":sout-transcode-width=%i",
MainWorkflow::getInstance()->getWidth() );
m_width = MainWorkflow::getInstance()->getWidth();
sprintf( buffer, ":sout-transcode-width=%i", m_width );
m_vlcMedia->addOption( buffer );
sprintf( buffer, ":sout-transcode-height=%i",
MainWorkflow::getInstance()->getHeight() );
m_height = MainWorkflow::getInstance()->getHeight();
sprintf( buffer, ":sout-transcode-height=%i", m_height );
m_vlcMedia->addOption( buffer );
sprintf( buffer, ":sout-transcode-fps=%f", (float)Clip::DefaultFPS );
m_vlcMedia->addOption( buffer );
......@@ -78,6 +76,7 @@ ImageClipWorkflow::initVlcOutput()
m_vlcMedia->addOption( buffer );
sprintf( buffer, ":fake-fps=%f", m_clipHelper->clip()->getMedia()->fps() );
m_vlcMedia->addOption( buffer );
m_isRendering = true;
m_effectFrame->resize( MainWorkflow::getInstance()->getWidth(),
MainWorkflow::getInstance()->getHeight() );
......@@ -100,10 +99,9 @@ ImageClipWorkflow::getOutput( ClipWorkflow::GetMode )
{
QMutexLocker lock( m_renderLock );
QReadLocker lock2( m_effectsLock );
qint64 currentFrame = MainWorkflow::getInstance()->getCurrentFrame( false );
quint32 *buff = EffectsEngine::applyFilters( m_filters, m_buffer, currentFrame,
currentFrame * 1000.0 / clip()->getMedia()->fps() );
quint32 *buff = applyFilters( m_buffer, currentFrame,
currentFrame * 1000.0 / clip()->getMedia()->fps() );
if ( buff != NULL )
{
m_effectFrame->setBuffer( buff );
......@@ -157,18 +155,3 @@ void
ImageClipWorkflow::flushComputedBuffers()
{
}
EffectsEngine::EffectHelper*
ImageClipWorkflow::appendEffect( Effect *effect, qint64 start, qint64 end )
{
if ( effect->type() != Effect::Filter )