Commit 4583fe54 authored by luyikei's avatar luyikei

EffectsEngine: Migrate to MLT

parent 48714786
......@@ -75,12 +75,8 @@ SET(VLMC_SRCS
Backend/MLT/MLTFilter.cpp
Backend/MLT/MLTTransition.cpp
Backend/MLT/MLTTractor.cpp
EffectsEngine/EffectsEngine.cpp
EffectsEngine/Effect.cpp
EffectsEngine/EffectUser.cpp
EffectsEngine/EffectHelper.cpp
EffectsEngine/EffectInstance.cpp
EffectsEngine/EffectSettingValue.cpp
Library/Library.cpp
Library/MediaContainer.cpp
Main/Core.cpp
......
......@@ -28,9 +28,10 @@
#include "Main/Core.h"
#include "Media/Clip.h"
#include "EffectsEngine/EffectHelper.h"
#include "EffectsEngine/EffectInstance.h"
#include "Workflow/TrackWorkflow.h"
#include "AbstractUndoStack.h"
#include "Backend/IService.h"
#include "Backend/IFilter.h"
void
Commands::trigger( Generic* command )
......@@ -278,9 +279,9 @@ Commands::Clip::Split::internalUndo()
m_toSplit->setEnd( m_oldEnd );
}
Commands::Effect::Add::Add( EffectHelper *helper, EffectUser *target ) :
m_helper( helper ),
m_target( target )
Commands::Effect::Add::Add( EffectHelper* helper, Backend::IService* target )
: m_helper( helper )
, m_target( target )
{
retranslate();
}
......@@ -288,73 +289,73 @@ Commands::Effect::Add::Add( EffectHelper *helper, EffectUser *target ) :
void
Commands::Effect::Add::retranslate()
{
setText( tr( "Adding effect %1" ).arg( m_helper->effectInstance()->effect()->name() ) );
// setText( tr( "Adding effect %1" ).arg( m_helper->effectInstance()->effect()->name() ) );
}
void
Commands::Effect::Add::internalRedo()
{
m_target->addEffect( m_helper );
m_target->attach( *m_helper->filter() );
m_helper->filter()->connect( *m_target );
}
void
Commands::Effect::Add::internalUndo()
{
m_target->removeEffect( m_helper );
m_target->detach( *m_helper->filter() );
}
Commands::Effect::Move::Move( EffectHelper *helper, EffectUser *old, EffectUser *newUser,
qint64 pos) :
m_helper( helper ),
m_old( old ),
m_new( newUser ),
m_newPos( pos )
Commands::Effect::Move::Move( EffectHelper* helper, Backend::IService* from, Backend::IService* to,
qint64 pos)
: m_helper( helper )
, m_from( from )
, m_to( to )
, m_newPos( pos )
{
m_oldPos = helper->begin();
m_oldEnd = helper->end();
m_newEnd = m_helper->end() - ( m_helper->begin() - pos );
m_newEnd = helper->end() - helper->begin() + pos;
retranslate();
}
void
Commands::Effect::Move::retranslate()
{
setText( tr( "Moving effect %1" ).arg( m_helper->effectInstance()->effect()->name() ) );
// setText( tr( "Moving effect %1" ).arg( m_helper->effectInstance()->effect()->name() ) );
}
void
Commands::Effect::Move::internalRedo()
{
if ( m_old != m_new )
if ( m_from != m_to )
{
m_old->removeEffect( m_helper );
m_from->detach( *m_helper->filter() );
m_helper->setBoundaries( m_newPos, m_newEnd );
m_new->addEffect( m_helper );
m_to->attach( *m_helper->filter() );
}
else
m_new->moveEffect( m_helper, m_newPos );
m_helper->setBoundaries( m_newPos, m_newEnd );
}
void
Commands::Effect::Move::internalUndo()
{
if ( m_old != m_new )
if ( m_from != m_to )
{
m_new->removeEffect( m_helper );
m_to->detach( *m_helper->filter() );
m_helper->setBoundaries( m_oldPos, m_oldEnd );
//This must be called after setting boundaries, as the effect's begin is its begin boundary
m_old->addEffect( m_helper );
m_from->attach( *m_helper->filter() );
}
else
m_new->moveEffect( m_helper, m_oldPos );
m_helper->setBoundaries( m_oldPos, m_oldEnd );
}
Commands::Effect::Resize::Resize( EffectUser *target, EffectHelper *helper, qint64 newBegin, qint64 newEnd ) :
m_target( target ),
m_helper( helper ),
m_newBegin( newBegin ),
m_newEnd( newEnd )
Commands::Effect::Resize::Resize( EffectHelper* helper, qint64 newBegin, qint64 newEnd )
: m_helper( helper )
, m_newBegin( newBegin )
, m_newEnd( newEnd )
{
m_oldBegin = helper->begin();
m_oldEnd = helper->end();
......@@ -364,46 +365,43 @@ Commands::Effect::Resize::Resize( EffectUser *target, EffectHelper *helper, qint
void
Commands::Effect::Resize::retranslate()
{
setText( tr( "Resizing effect %1" ).arg( m_helper->effectInstance()->effect()->name() ) );
// setText( tr( "Resizing effect %1" ).arg( m_helper->effectInstance()->effect()->name() ) );
}
void
Commands::Effect::Resize::internalRedo()
{
if ( m_newBegin != m_oldBegin )
m_target->moveEffect( m_helper, m_newBegin );
m_helper->setBoundaries( m_newBegin, m_newEnd );
}
void
Commands::Effect::Resize::internalUndo()
{
if ( m_oldBegin != m_newBegin )
m_target->moveEffect( m_helper, m_oldBegin );
m_helper->setBoundaries( m_oldBegin, m_oldEnd );
}
Commands::Effect::Remove::Remove( EffectHelper *helper, EffectUser *user ) :
m_helper( helper ),
m_user( user )
Commands::Effect::Remove::Remove( EffectHelper* helper, Backend::IService* target )
: m_helper( helper )
, m_target( target )
{
retranslate();
}
void
Commands::Effect::Remove::retranslate()
{
setText( tr( "Deleting effect %1" ).arg( m_helper->effectInstance()->effect()->name() ) );
// setText( tr( "Deleting effect %1" ).arg( m_helper->effectInstance()->effect()->name() ) );
}
void
Commands::Effect::Remove::internalRedo()
{
m_user->removeEffect( m_helper );
m_target->detach( *m_helper->filter() );
}
void
Commands::Effect::Remove::internalUndo()
{
m_user->addEffect( m_helper );
m_target->attach( *m_helper->filter() );
m_helper->filter()->connect( *m_target );
}
......@@ -34,7 +34,12 @@
#include "Workflow/MainWorkflow.h"
class Clip;
class EffectUser;
namespace Backend
{
class IService;
}
class EffectHelper;
namespace Commands
{
......@@ -178,13 +183,13 @@ namespace Commands
Q_OBJECT
public:
Add( EffectHelper *helper, EffectUser *target );
Add( EffectHelper *helper, Backend::IService* target );
virtual void internalRedo();
virtual void internalUndo();
virtual void retranslate();
private:
EffectHelper *m_helper;
EffectUser *m_target;
Backend::IService* m_target;
};
class Move : public Generic
......@@ -192,14 +197,14 @@ namespace Commands
Q_OBJECT
public:
Move( EffectHelper *helper, EffectUser *old, EffectUser *newUser, qint64 pos );
Move( EffectHelper* helper, Backend::IService* from, Backend::IService* to, qint64 pos );
virtual void internalRedo();
virtual void internalUndo();
virtual void retranslate();
private:
EffectHelper *m_helper;
EffectUser *m_old;
EffectUser *m_new;
Backend::IService *m_from;
Backend::IService *m_to;
qint64 m_oldPos;
qint64 m_newPos;
qint64 m_newEnd;
......@@ -211,13 +216,12 @@ namespace Commands
Q_OBJECT
public:
Resize( EffectUser *target, EffectHelper *helper, qint64 newBegin, qint64 newEnd );
Resize( EffectHelper* helper, qint64 newBegin, qint64 newEnd );
virtual void internalRedo();
virtual void internalUndo();
virtual void retranslate();
private:
EffectUser *m_target;
EffectHelper *m_helper;
EffectHelper* m_helper;
qint64 m_newBegin;
qint64 m_newEnd;
qint64 m_oldBegin;
......@@ -229,13 +233,13 @@ namespace Commands
Q_OBJECT
public:
Remove( EffectHelper *helper, EffectUser *user );
Remove( EffectHelper *helper, Backend::IService* target );
virtual void internalRedo();
virtual void internalUndo();
virtual void retranslate();
private:
EffectHelper *m_helper;
EffectUser *m_user;
EffectHelper* m_helper;
Backend::IService* m_target;
};
}
}
......
......@@ -3,7 +3,8 @@
*****************************************************************************
* Copyright (C) 2008-2016 VideoLAN
*
* Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
* Authors: Yikei Lu <luyikei.qmltu@gmail.com>
* Hugo Beauzée-Luyssen <hugo@beauzee.fr>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
......@@ -21,48 +22,214 @@
*****************************************************************************/
#include "EffectsEngine/EffectHelper.h"
#include "EffectsEngine/EffectUser.h"
#include "Main/Core.h"
#include "Project/Project.h"
#include "Workflow/MainWorkflow.h"
#include "Backend/MLT/MLTFilter.h"
#include "Backend/IBackend.h"
#include <mlt++/MltProperties.h>
EffectHelper::EffectHelper( EffectInstance *effectInstance, qint64 begin, qint64 end,
QVariant
conv( std::string str, SettingValue::Type type )
{
if ( str.empty() == true )
return QVariant( QVariant::Invalid );
if ( type == SettingValue::Double )
return QVariant( std::stod( str ) );
else if ( type == SettingValue::Int )
return QVariant( std::stoi( str ) );
else
return QVariant( QString::fromStdString( str ) );
};
EffectHelper::EffectHelper( const char* id, qint64 begin, qint64 end,
const QString &uuid ) :
Helper( begin, end, uuid ),
m_effectInstance( effectInstance ),
m_target( nullptr )
m_filter( new Backend::MLT::MLTFilter( id ) ),
m_service( nullptr ),
m_filterInfo( nullptr )
{
if ( Core::instance()->workflow()->getLengthFrame() > 0 )
m_end = Core::instance()->workflow()->getLengthFrame();
else
m_end = Effect::TrackEffectDefaultLength;
if ( m_filter->isValid() == false )
return;
m_filter->setBoundaries( begin, end );
initParams();
}
EffectInstance*
EffectHelper::effectInstance()
EffectHelper::EffectHelper( const QString& id, qint64 begin, qint64 end, const QString& uuid )
: EffectHelper( qPrintable( id ), begin, end, uuid )
{
return m_effectInstance;
}
const EffectInstance*
EffectHelper::effectInstance() const
EffectHelper::EffectHelper( Backend::IFilter *filter, const QString& uuid )
: Helper( filter->begin(), filter->end(), uuid )
, m_filter( dynamic_cast<Backend::MLT::MLTFilter*>( filter ) )
, m_service( nullptr )
, m_filterInfo( nullptr )
{
return m_effectInstance;
if ( m_filter->isValid() == false )
return;
initParams();
}
EffectUser*
EffectHelper::target()
EffectHelper::~EffectHelper()
{
return m_target;
delete m_filter;
}
void
EffectHelper::setTarget( EffectUser *target )
EffectHelper::initParams()
{
m_target = target;
if ( target != nullptr )
for ( Backend::IParameterInfo* paramInfo : filterInfo()->paramInfos() )
{
if ( target->length() > 0 && target->length() < m_end )
m_end = target->length();
SettingValue::Type type;
// It treats as double internally
if ( paramInfo->type() == "float" ||
paramInfo->type() == "double" )
type = SettingValue::Double;
else if ( paramInfo->type() == "integer" )
type = SettingValue::Int;
else if ( paramInfo->type() == "boolean" )
type = SettingValue::Bool;
else
type = SettingValue::String;
SettingValue::Flags flags = SettingValue::Nothing;
auto maxV = conv( paramInfo->maxValue(), type );
auto minV = conv( paramInfo->maxValue(), type );
if ( maxV != QVariant( QVariant::Invalid ) ||
minV != QVariant( QVariant::Invalid ) )
flags = SettingValue::Clamped;
auto val = m_settings.createVar( type, QString::fromStdString( paramInfo->identifier() ),
defaultValue( paramInfo->identifier().c_str(), type ), paramInfo->name().c_str(),
paramInfo->description().c_str(), flags );
connect( val, &SettingValue::changed, this, [this, val]( const QVariant& variant ) { set( val, variant ); } );
}
}
Backend::IFilter*
EffectHelper::filter()
{
return m_filter;
}
const Backend::IFilter*
EffectHelper::filter() const
{
return m_filter;
}
void
EffectHelper::set( SettingValue* value, const QVariant& variant )
{
auto key = value->key();
switch ( value->type() )
{
case SettingValue::Double:
m_filter->properties()->set( qPrintable( key ), variant.toDouble() );
break;
case SettingValue::Int:
m_filter->properties()->set( qPrintable( key ), variant.toInt() );
break;
case SettingValue::Bool:
m_filter->properties()->set( qPrintable( key ), variant.toBool() );
break;
default:
m_filter->properties()->set( qPrintable( key ), qPrintable( variant.toString() ) );
break;
} ;
}
QVariant
EffectHelper::defaultValue( const char *id, SettingValue::Type type )
{
switch ( type )
{
case SettingValue::Double:
return QVariant( m_filter->properties()->get_double( id ) );
case SettingValue::Int:
return QVariant( m_filter->properties()->get_int( id ) );
case SettingValue::Bool:
return QVariant( (bool)m_filter->properties()->get_int( id ) );
default:
return QVariant( QString( m_filter->properties()->get( id ) ) );
} ;
}
SettingValue*
EffectHelper::value( const QString& key )
{
return m_settings.value( key );
}
qint64
EffectHelper::begin() const
{
return m_filter->begin();
}
qint64
EffectHelper::end() const
{
return m_filter->end();
}
void
EffectHelper::setBegin( qint64 begin )
{
m_filter->setBoundaries( begin, end() );
}
void
EffectHelper::setEnd( qint64 end )
{
m_filter->setBoundaries( begin(), end );
}
qint64
EffectHelper::length() const
{
return m_filter->length();
}
void
EffectHelper::setBoundaries( qint64 begin, qint64 end )
{
m_filter->setBoundaries( begin, end );
}
void
EffectHelper::setTarget( Backend::IService* service )
{
if ( m_service )
m_service->detach( *m_filter );
m_service = service;
m_service->attach( *m_filter );
m_filter->connect( *m_service );
}
Backend::IService*
EffectHelper::target()
{
return m_service;
}
Backend::IFilterInfo*
EffectHelper::filterInfo()
{
if ( m_filterInfo == nullptr )
m_filterInfo = Backend::instance()->filterInfo( m_filter->identifier() );
return m_filterInfo;
}
QString
EffectHelper::identifier()
{
return QString::fromStdString( m_filter->identifier() );
}
......@@ -3,7 +3,8 @@
*****************************************************************************
* Copyright (C) 2008-2016 VideoLAN
*
* Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
* Authors: Yikei Lu <luyikei.qmltu@gmail.com>
* Hugo Beauzée-Luyssen <hugo@beauzee.fr>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
......@@ -23,33 +24,69 @@
#ifndef EFFECTHELPER_H
#define EFFECTHELPER_H
class EffectInstance;
class EffectUser;
#include <QObject>
#include <QMetaType>
#include "Settings/Settings.h"
#include "Backend/IFilter.h"
#include "Workflow/Helper.h"
namespace Backend
{
namespace MLT
{
class MLTFilter;
}
class IFilter;
class IService;
}
class Effect;
class EffectHelper : public Workflow::Helper
{
Q_OBJECT
public:
EffectHelper( EffectInstance *effectInstance, qint64 begin = 0, qint64 end = -1,
EffectHelper( const char* id, qint64 begin = 0, qint64 end = -1,
const QString& uuid = QString() );
EffectHelper( const QString& id, qint64 begin = 0, qint64 end = -1,
const QString& uuid = QString() );
EffectHelper( Backend::IFilter* filter,
const QString& uuid = QString() );
~EffectHelper();
virtual qint64 begin() const override;
virtual qint64 end() const override;
virtual void setBegin(qint64 begin) override;
virtual void setEnd(qint64 end) override;
virtual qint64 length() const override;
virtual void setBoundaries( qint64 begin, qint64 end ) override;
EffectInstance *effectInstance();
const EffectInstance *effectInstance() const;
EffectUser *target();
void setTarget( EffectUser *target );
void setTarget( Backend::IService* service );
Backend::IService* target();
Backend::IFilterInfo* filterInfo();
QString identifier();
Backend::IFilter* filter();
const Backend::IFilter* filter() const;
SettingValue* value( const QString& key );
private:
EffectInstance *m_effectInstance;
EffectUser *m_target;
Backend::MLT::MLTFilter* m_filter;
Backend::IService* m_service;
Backend::IFilterInfo* m_filterInfo;
Settings m_settings;
void set( SettingValue* value, const QVariant& variant );
QVariant defaultValue( const char* id, SettingValue::Type type );
void initParams();
};
Q_DECLARE_METATYPE( EffectHelper* );
Q_DECLARE_METATYPE( Backend::IFilter* );
#endif // EFFECTHELPER_H
......@@ -41,304 +41,5 @@ EffectUser::EffectUser() :
EffectUser::~EffectUser()
{
cleanEffects();
delete m_effectsLock;
}
EffectHelper*
EffectUser::addEffect( Effect *effect, qint64 start /*= 0*/, qint64 end /*= -1*/ )
{
//Check that effect type is one of the supported ones
if ( effect->type() == Effect::Filter || effect->type() == Effect::Mixer2 )
{
EffectInstance *effectInstance = effect->createInstance();
EffectHelper *ret = new EffectHelper( effectInstance, start, end );
addEffect( ret );
return ret;
}
return nullptr;
}
void
EffectUser::addEffect( EffectHelper *effectHelper )
{
if ( m_isRendering == true )
effectHelper->effectInstance()->init( m_width, m_height );
QWriteLocker lock( m_effectsLock );
if ( effectHelper->effectInstance()->effect()->type() == Effect::Filter )
m_filters.push_back( effectHelper );
else
m_mixers.push_back( effectHelper );
effectHelper->setTarget( this );
emit effectAdded( effectHelper, effectHelper->begin() );
}
quint32*
EffectUser::applyFilters( const Workflow::Frame* frame, qint64 currentFrame )
{
QReadLocker lock( m_effectsLock );
if ( m_filters.size() == 0 )
return nullptr;
EffectsEngine::EffectList::const_iterator it = m_filters.constBegin();
EffectsEngine::EffectList::const_iterator ite = m_filters.constEnd();
quint32 *buff1 = nullptr;
quint32 *buff2 = nullptr;
const quint32 *input = frame->buffer();
bool firstBuff = true;
while ( it != ite )
{
if ( (*it)->begin() < currentFrame &&
( (*it)->end() < 0 || (*it)->end() > currentFrame ) )
{
quint32 **buff;
if ( firstBuff == true )
buff = &buff1;
else
buff = &buff2;
if ( *buff == nullptr )
*buff = new quint32[m_width * m_height];
EffectInstance *effect = (*it)->effectInstance();
effect->process( input, *buff );
input = *buff;
firstBuff = !firstBuff;
}
++it;
}
if ( buff1 != nullptr || buff2 != nullptr )
{
if ( firstBuff == true )
{
delete[] buff1;
return buff2;
}
else
{
delete[] buff2;
return buff1;
}
}
return nullptr;
}
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)->effectInstance()->init( m_width, m_height );
++it;
}
}
void
EffectUser::initMixers()