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

Remove Clip hierarchy

Media is now the sole owner of clips. A clip doesn't have any child.
The project file now saves the media & clips as a tree.

This breaks the loading of sequence workflows and is likely to crash
until all Clips are being exposed as shared_ptr/QSharedPointer
parent 10103c37
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "Project/Project.h" #include "Project/Project.h"
#include "Main/Core.h" #include "Main/Core.h"
#include "Media/Clip.h" #include "Media/Clip.h"
#include "Media/Media.h"
#include "EffectsEngine/EffectHelper.h" #include "EffectsEngine/EffectHelper.h"
#include "Workflow/SequenceWorkflow.h" #include "Workflow/SequenceWorkflow.h"
#include "Workflow/MainWorkflow.h" #include "Workflow/MainWorkflow.h"
...@@ -306,7 +307,8 @@ Commands::Clip::Split::Split( std::shared_ptr<SequenceWorkflow> const& workflow, ...@@ -306,7 +307,8 @@ Commands::Clip::Split::Split( std::shared_ptr<SequenceWorkflow> const& workflow,
retranslate(); retranslate();
return; return;
} }
m_newClip = std::make_shared<::Clip>( m_toSplit.get(), newClipBegin - m_toSplit->begin(), m_toSplit->end() - m_toSplit->begin() ); m_newClip = std::shared_ptr<::Clip>( m_toSplit->media()->cut( newClipBegin - m_toSplit->begin(),
m_toSplit->end() - m_toSplit->begin() ) );
m_oldEnd = m_toSplit->end(); m_oldEnd = m_toSplit->end();
retranslate(); retranslate();
} }
......
...@@ -252,11 +252,7 @@ PreviewWidget::createNewClipFromMarkers() ...@@ -252,11 +252,7 @@ PreviewWidget::createNewClipFromMarkers()
return ; return ;
beg = beg < 0 ? 0 : beg; beg = beg < 0 ? 0 : beg;
Clip* part = media->cut( beg, end ); media->cut( beg, end );
//Adding the newly created clip to the media
if ( clip->addSubclip( part ) == false )
delete part;
} }
void void
......
...@@ -47,7 +47,6 @@ Library::Library( Settings *projectSettings ) ...@@ -47,7 +47,6 @@ Library::Library( Settings *projectSettings )
, m_settings( new Settings ) , m_settings( new Settings )
{ {
m_settings->createVar( SettingValue::List, QString( "medias" ), QVariantList(), "", "", SettingValue::Nothing ); m_settings->createVar( SettingValue::List, QString( "medias" ), QVariantList(), "", "", SettingValue::Nothing );
m_settings->createVar( SettingValue::List, QString( "clips" ), QVariantList(), "", "", SettingValue::Nothing );
connect( m_settings, &Settings::postLoad, this, &Library::postLoad, Qt::DirectConnection ); connect( m_settings, &Settings::postLoad, this, &Library::postLoad, Qt::DirectConnection );
connect( m_settings, &Settings::preSave, this, &Library::preSave, Qt::DirectConnection ); connect( m_settings, &Settings::preSave, this, &Library::preSave, Qt::DirectConnection );
...@@ -61,10 +60,6 @@ Library::preSave() ...@@ -61,10 +60,6 @@ Library::preSave()
for ( auto val : m_media ) for ( auto val : m_media )
l << val->toVariant(); l << val->toVariant();
m_settings->value( "medias" )->set( l ); m_settings->value( "medias" )->set( l );
l.clear();
for ( auto val : m_clips )
l << val->toVariantFull();
m_settings->value( "clips" )->set( l );
setCleanState( true ); setCleanState( true );
} }
...@@ -76,13 +71,6 @@ Library::postLoad() ...@@ -76,13 +71,6 @@ Library::postLoad()
auto m = Media::fromVariant( var ); auto m = Media::fromVariant( var );
addMedia( m ); addMedia( m );
} }
for ( const auto& var : m_settings->value( "clips" )->get().toList() )
{
auto c = Clip::fromVariant( var );
if ( c != nullptr )
addClip( c );
}
} }
Library::~Library() Library::~Library()
......
...@@ -39,14 +39,13 @@ ...@@ -39,14 +39,13 @@
#include "Tools/VlmcDebug.h" #include "Tools/VlmcDebug.h"
#include <QVariant> #include <QVariant>
Clip::Clip( QSharedPointer<Media> media, qint64 begin /*= 0*/, qint64 end /*= Backend::IInput::EndOfMedia */, const QString& uuid /*= QString()*/ ) : Clip::Clip( QSharedPointer<Media> media, qint64 begin /*= 0*/, qint64 end /*= Backend::IInput::EndOfMedia */, const QUuid& uuid /*= QString()*/ ) :
Workflow::Helper( uuid ), //FIXME: uuid -> QString conversion should be removed, since the helper stores the UUID as a QUuid
Workflow::Helper( uuid.toString() ),
m_media( media ), m_media( media ),
m_input( std::move( m_media->input()->cut( begin, end ) ) ), m_input( std::move( m_media->input()->cut( begin, end ) ) ),
m_parent( media->baseClip() ),
m_isLinked( false ) m_isLinked( false )
{ {
m_rootClip = media->baseClip();
Formats f; Formats f;
if ( media->input()->hasAudio() == true ) if ( media->input()->hasAudio() == true )
f |= Clip::Audio; f |= Clip::Audio;
...@@ -55,26 +54,6 @@ Clip::Clip( QSharedPointer<Media> media, qint64 begin /*= 0*/, qint64 end /*= Ba ...@@ -55,26 +54,6 @@ Clip::Clip( QSharedPointer<Media> media, qint64 begin /*= 0*/, qint64 end /*= Ba
setFormats( f ); setFormats( f );
} }
Clip::Clip( Clip *parent, qint64 begin /*= -1*/, qint64 end /*= -2*/,
const QString &uuid /*= QString()*/ ) :
Workflow::Helper( uuid ),
m_media( parent->media() ),
m_rootClip( parent->rootClip() ),
m_parent( parent )
{
if ( begin == -1 )
begin = parent->begin();
else
begin = parent->begin() + begin;
if ( end == Backend::IInput::EndOfParent )
end = parent->end();
else
end = parent->begin() + end;
m_input = parent->input()->cut( begin, end );
setFormats( parent->formats() );
}
Clip::~Clip() Clip::~Clip()
{ {
emit unloaded( this ); emit unloaded( this );
...@@ -115,8 +94,6 @@ Clip::matchMetaTag( const QString &tag ) const ...@@ -115,8 +94,6 @@ Clip::matchMetaTag( const QString &tag ) const
{ {
if ( tag.length() == 0 ) if ( tag.length() == 0 )
return true; return true;
if ( m_parent && m_parent->matchMetaTag( tag ) == true )
return true;
QString metaTag; QString metaTag;
foreach ( metaTag, m_metaTags ) foreach ( metaTag, m_metaTags )
{ {
...@@ -211,47 +188,6 @@ Clip::isLinked() const ...@@ -211,47 +188,6 @@ Clip::isLinked() const
return m_isLinked; return m_isLinked;
} }
Clip*
Clip::rootClip()
{
if ( m_rootClip == nullptr )
return this;
return m_rootClip;
}
bool
Clip::isRootClip() const
{
return ( m_rootClip == nullptr );
}
Clip*
Clip::parent()
{
return m_parent;
}
const Clip*
Clip::parent() const
{
return m_parent;
}
bool
Clip::addSubclip( Clip *clip )
{
if ( m_subclips.contains( clip->uuid() ) == true )
return false;
m_subclips[clip->uuid()] = clip;
emit subclipAdded( clip );
}
void
Clip::clear()
{
m_subclips.clear();
}
QVariant QVariant
Clip::toVariant() const Clip::toVariant() const
{ {
...@@ -261,14 +197,8 @@ Clip::toVariant() const ...@@ -261,14 +197,8 @@ Clip::toVariant() const
{ "notes", m_notes }, { "notes", m_notes },
{ "formats", (int)formats() } { "formats", (int)formats() }
}; };
if ( isRootClip() ) h.insert( "begin", begin() );
h.insert( "media", m_media->toVariant() ); h.insert( "end", end() );
else
{
h.insert( "parent", m_parent->uuid().toString() );
h.insert( "begin", begin() );
h.insert( "end", end() );
}
if ( isLinked() == true ) if ( isLinked() == true )
{ {
h.insert( "linkedClip", m_linkedClipUuid ); h.insert( "linkedClip", m_linkedClipUuid );
...@@ -281,19 +211,6 @@ Clip::toVariant() const ...@@ -281,19 +211,6 @@ Clip::toVariant() const
} }
QVariant
Clip::toVariantFull() const
{
QVariantHash h = toVariant().toHash();
if ( m_subclips.isEmpty() == true )
return h;
QVariantList l;
for ( const auto& c : m_subclips.values() )
l << c->toVariant();
h.insert( "subClips", l );
return h;
}
Clip::Formats Clip::Formats
Clip::formats() const Clip::formats() const
{ {
...@@ -314,80 +231,9 @@ Clip::input() ...@@ -314,80 +231,9 @@ Clip::input()
return m_input.get(); return m_input.get();
} }
Clip*
Clip::fromVariant( const QVariant& v )
{
auto m = v.toMap();
if ( m.contains( "parent" ) )
{
vlmcWarning() << "Refusing to load a root clip with a parent field";
return nullptr;
}
auto mediaId = m["media"].toLongLong();
if ( mediaId == 0 )
{
vlmcWarning() << "Refusing to load an invalid root clip with no base media";
return nullptr;
}
auto uuid = m["uuid"].toString();
if ( uuid.isEmpty() == true )
{
vlmcWarning() << "Refusing to load an invalid root clip with no UUID";
return nullptr;
}
auto media = Core::instance()->library()->media( mediaId );
auto clip = new Clip( media, 0, -1, uuid );
clip->loadVariant( m );
return clip;
}
Clip*
Clip::fromVariant( const QVariant& v, Clip* parent )
{
auto m = v.toMap();
if ( m.contains( "parent" ) == false )
{
vlmcWarning() << "Refusing to load a subclip with no parent field";
return nullptr;
}
auto mediaMrl = m["media"].toString();
if ( mediaMrl.isEmpty() == true )
{
vlmcWarning() << "Refusing to load an invalid root clip with no base media";
return nullptr;
}
auto uuid = m["uuid"].toString();
if ( uuid.isEmpty() == true )
{
vlmcWarning() << "Refusing to load an invalid root clip with no UUID";
return nullptr;
}
auto begin = m["begin"].toLongLong();
auto end = m["end"].toLongLong();
auto clip = new Clip( parent, begin, end, uuid );
clip->loadVariant( m );
return clip;
}
void void
Clip::loadVariant( const QVariantMap& m ) Clip::loadFilters( const QVariantMap& m )
{ {
if ( m.contains( "subClips" ) )
{
auto children = m["subClips"].toList();
for ( const auto& clipMap : children )
addSubclip( fromVariant( clipMap, this ) );
}
if ( m.contains( "filters" ) ) if ( m.contains( "filters" ) )
{ {
const auto& filters = m["filters"].toList(); const auto& filters = m["filters"].toList();
......
...@@ -64,17 +64,8 @@ class Clip : public Workflow::Helper ...@@ -64,17 +64,8 @@ class Clip : public Workflow::Helper
* the end of the parent will be used. * the end of the parent will be used.
* \param uuid A unique identifier. If not given, one will be generated. * \param uuid A unique identifier. If not given, one will be generated.
*/ */
Clip( QSharedPointer<Media> parent, qint64 begin = 0, qint64 end = Backend::IInput::EndOfMedia, const QString &uuid = QStringLiteral() ); Clip( QSharedPointer<Media> parent, qint64 begin = 0, qint64 end = Backend::IInput::EndOfMedia, const QUuid &uuid = QStringLiteral() );
/**
* \brief Clones a Clip, potentially with a new begin and end.
*
* \param creator The clip to clone.
* \param begin The clip beginning (in frames, from the parent's beginning).
* If not given, 0 is assumed.
* \param end The end, in frames, from the parent's beginning. If not given,
* the end of the parent will be used.
*/
Clip( Clip *creator, qint64 begin = -1, qint64 end = Backend::IInput::EndOfParent, const QString& uuid = QStringLiteral() );
virtual ~Clip(); virtual ~Clip();
/** /**
...@@ -88,9 +79,6 @@ class Clip : public Workflow::Helper ...@@ -88,9 +79,6 @@ class Clip : public Workflow::Helper
QSharedPointer<Media> media(); QSharedPointer<Media> media();
QSharedPointer<const Media> media() const; QSharedPointer<const Media> media() const;
Clip *parent();
const Clip *parent() const;
/** /**
\brief Returns an unique Uuid for this clip (which is NOT the \brief Returns an unique Uuid for this clip (which is NOT the
parent's Uuid). parent's Uuid).
...@@ -120,29 +108,15 @@ class Clip : public Workflow::Helper ...@@ -120,29 +108,15 @@ class Clip : public Workflow::Helper
const QString &notes() const; const QString &notes() const;
void setNotes( const QString &notes ); void setNotes( const QString &notes );
bool isRootClip() const;
Clip* rootClip();
/**
* \brief Clear all the clip subclips recursively.
*/
void clear();
bool addSubclip( Clip* clip );
QVariant toVariant() const; QVariant toVariant() const;
QVariant toVariantFull() const;
Formats formats() const; Formats formats() const;
void setFormats( Formats formats ); void setFormats( Formats formats );
Backend::IInput* input(); Backend::IInput* input();
static Clip* fromVariant( const QVariant& v ); //FIXME: This shouldn't be represented in the Library
void loadFilters(const QVariantMap& v );
private:
static Clip* fromVariant( const QVariant& v, Clip* parent );
void loadVariant(const QVariantMap& v );
private: private:
QSharedPointer<Media> m_media; QSharedPointer<Media> m_media;
...@@ -151,17 +125,6 @@ class Clip : public Workflow::Helper ...@@ -151,17 +125,6 @@ class Clip : public Workflow::Helper
QStringList m_metaTags; QStringList m_metaTags;
QString m_notes; QString m_notes;
/**
* \brief Return the root clip.
*
* The root clip is the base clip for the parent media.
*/
Clip* m_rootClip;
QHash<QUuid, Clip*> m_subclips;
Clip* m_parent;
QUuid m_linkedClipUuid; QUuid m_linkedClipUuid;
bool m_isLinked; bool m_isLinked;
...@@ -172,19 +135,6 @@ class Clip : public Workflow::Helper ...@@ -172,19 +135,6 @@ class Clip : public Workflow::Helper
* \brief Act just like QObject::destroyed(), but before the clip deletion. * \brief Act just like QObject::destroyed(), but before the clip deletion.
*/ */
void unloaded( Clip* ); void unloaded( Clip* );
/**
* \brief This signal should be emitted to tell a new sublip have been added
* \param Clip The newly added subclip
*/
void subclipAdded( Clip* );
/**
* \brief This signal should be emiteted when a subclip has been removed
* This signal pass a QUuid as the clip may be deleted when the signal reaches its
* slot.
* \param uuid The removed clip uuid
*/
void subclipRemoved( const QUuid& );
}; };
Q_DECLARE_OPERATORS_FOR_FLAGS( Clip::Formats ) Q_DECLARE_OPERATORS_FOR_FLAGS( Clip::Formats )
......
...@@ -63,7 +63,7 @@ const QString Media::streamPrefix = "stream://"; ...@@ -63,7 +63,7 @@ const QString Media::streamPrefix = "stream://";
QPixmap* Media::defaultSnapshot = nullptr; QPixmap* Media::defaultSnapshot = nullptr;
#endif #endif
Media::Media( medialibrary::MediaPtr media ) Media::Media( medialibrary::MediaPtr media, const QUuid& uuid /* = QUuid() */ )
: m_input( nullptr ) : m_input( nullptr )
, m_mlMedia( media ) , m_mlMedia( media )
{ {
...@@ -80,7 +80,7 @@ Media::Media( medialibrary::MediaPtr media ) ...@@ -80,7 +80,7 @@ Media::Media( medialibrary::MediaPtr media )
if ( m_mlFile == nullptr ) if ( m_mlFile == nullptr )
vlmcFatal( "No file representing media %s", media->title().c_str(), "was found" ); vlmcFatal( "No file representing media %s", media->title().c_str(), "was found" );
m_input.reset( new Backend::MLT::MLTInput( m_mlFile->mrl().c_str() ) ); m_input.reset( new Backend::MLT::MLTInput( m_mlFile->mrl().c_str() ) );
m_baseClip = new Clip( sharedFromThis() ); m_baseClip = new Clip( sharedFromThis(), 0, Backend::IInput::EndOfMedia, uuid );
} }
QString QString
...@@ -119,13 +119,35 @@ Media::id() const ...@@ -119,13 +119,35 @@ Media::id() const
Clip* Clip*
Media::cut(qint64 begin, qint64 end) Media::cut(qint64 begin, qint64 end)
{ {
return new Clip( m_baseClip, begin, end ); auto clip = new Clip( sharedFromThis(), begin, end );
m_clips[clip->uuid()] = clip;
emit subclipAdded( clip );
return clip;
}
void
Media::removeSubclip(const QUuid& uuid)
{
if ( m_clips.remove( uuid ) == 0 )
return;
emit subclipRemoved( uuid );
} }
QVariant QVariant
Media::toVariant() const Media::toVariant() const
{ {
return QVariant( static_cast<qlonglong>( m_mlMedia->id() ) ); QVariantHash h = {
{ "uuid", m_baseClip->uuid() },
{ "mlId", static_cast<qlonglong>( m_mlMedia->id() ) }
};
if ( m_clips.isEmpty() == false )
{
QVariantList l;
for ( const auto& c : m_clips.values() )
l << c->toVariant();
h.insert( "clips", l );
}
return QVariant( h );
} }
Backend::IInput* Backend::IInput*
...@@ -143,13 +165,40 @@ Media::input() const ...@@ -143,13 +165,40 @@ Media::input() const
QSharedPointer<Media> QSharedPointer<Media>
Media::fromVariant( const QVariant& v ) Media::fromVariant( const QVariant& v )
{ {
bool ok = false; /**
auto mediaId = v.toLongLong( &ok ); * The media is stored as such:
if ( ok == false ) * media: {
return QSharedPointer<Media>{}; * mlId: <id> // The media library ID
* uuid: <uuid> // The root clip UUID
* clips: [
* <clip 1>, // The subclips
* ...
* ]
* }
*/
const auto& m = v.toMap();
if ( m.contains( "mlId" ) == false || m.contains( "uuid" ) == false )
{
vlmcWarning() << "Invalid clip provided:" << m << "Missing 'mlId' and/or 'uuid' field(s)";
return {};
}
auto mediaId = m["mlId"].toLongLong();
auto uuid = m["uuid"].toUuid();
auto mlMedia = Core::instance()->mediaLibrary()->media( mediaId ); auto mlMedia = Core::instance()->mediaLibrary()->media( mediaId );
//FIXME: Is QSharedPointer exception safe in case its constructor throws an exception? //FIXME: Is QSharedPointer exception safe in case its constructor throws an exception?
return QSharedPointer<Media>( new Media( mlMedia ) ); auto media = QSharedPointer<Media>( new Media( mlMedia, uuid ) );
// Now load the subclips:
if ( m.contains( "clips" ) == false )
return media;
auto subclips = m["clips"].toList();
for ( const auto& c : subclips )
{
media->loadSubclip( c.toMap() );
}
return media;
} }
#ifdef HAVE_GUI #ifdef HAVE_GUI
...@@ -171,4 +220,24 @@ Media::snapshot() ...@@ -171,4 +220,24 @@ Media::snapshot()
return m_snapshot.isNull() ? *Media::defaultSnapshot : m_snapshot; return m_snapshot.isNull() ? *Media::defaultSnapshot : m_snapshot;
} }
Clip*
Media::loadSubclip( const QVariantMap& m )
{
if ( m.contains( "uuid" ) == false || m.contains( "begin" ) == false || m.contains( "end" ) == false )
{
vlmcWarning() << "Invalid clip provided:" << m;
return nullptr;
}
const auto& uuid = m["uuid"].toUuid();
const auto begin = m["begin"].toLongLong();
const auto end = m["end"].toLongLong();
auto clip = new Clip( sharedFromThis(), begin, end, uuid );
//FIXME: This shouldn't be loaded from the library
clip->loadFilters( m );
m_clips[uuid] = clip;
emit subclipAdded( clip );
return clip;
}