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 @@
#include "Project/Project.h"
#include "Main/Core.h"
#include "Media/Clip.h"
#include "Media/Media.h"
#include "EffectsEngine/EffectHelper.h"
#include "Workflow/SequenceWorkflow.h"
#include "Workflow/MainWorkflow.h"
......@@ -306,7 +307,8 @@ Commands::Clip::Split::Split( std::shared_ptr<SequenceWorkflow> const& workflow,
retranslate();
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();
retranslate();
}
......
......@@ -252,11 +252,7 @@ PreviewWidget::createNewClipFromMarkers()
return ;
beg = beg < 0 ? 0 : beg;
Clip* part = media->cut( beg, end );
//Adding the newly created clip to the media
if ( clip->addSubclip( part ) == false )
delete part;
media->cut( beg, end );
}
void
......
......@@ -47,7 +47,6 @@ Library::Library( Settings *projectSettings )
, m_settings( new Settings )
{
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::preSave, this, &Library::preSave, Qt::DirectConnection );
......@@ -61,10 +60,6 @@ Library::preSave()
for ( auto val : m_media )
l << val->toVariant();
m_settings->value( "medias" )->set( l );
l.clear();
for ( auto val : m_clips )
l << val->toVariantFull();
m_settings->value( "clips" )->set( l );
setCleanState( true );
}
......@@ -76,13 +71,6 @@ Library::postLoad()
auto m = Media::fromVariant( var );
addMedia( m );
}
for ( const auto& var : m_settings->value( "clips" )->get().toList() )
{
auto c = Clip::fromVariant( var );
if ( c != nullptr )
addClip( c );
}
}
Library::~Library()
......
......@@ -39,14 +39,13 @@
#include "Tools/VlmcDebug.h"
#include <QVariant>
Clip::Clip( QSharedPointer<Media> media, qint64 begin /*= 0*/, qint64 end /*= Backend::IInput::EndOfMedia */, const QString& uuid /*= QString()*/ ) :
Workflow::Helper( uuid ),
Clip::Clip( QSharedPointer<Media> media, qint64 begin /*= 0*/, qint64 end /*= Backend::IInput::EndOfMedia */, const QUuid& uuid /*= QString()*/ ) :
//FIXME: uuid -> QString conversion should be removed, since the helper stores the UUID as a QUuid
Workflow::Helper( uuid.toString() ),
m_media( media ),
m_input( std::move( m_media->input()->cut( begin, end ) ) ),
m_parent( media->baseClip() ),
m_isLinked( false )
{
m_rootClip = media->baseClip();
Formats f;
if ( media->input()->hasAudio() == true )
f |= Clip::Audio;
......@@ -55,26 +54,6 @@ Clip::Clip( QSharedPointer<Media> media, qint64 begin /*= 0*/, qint64 end /*= Ba
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()
{
emit unloaded( this );
......@@ -115,8 +94,6 @@ Clip::matchMetaTag( const QString &tag ) const
{
if ( tag.length() == 0 )
return true;
if ( m_parent && m_parent->matchMetaTag( tag ) == true )
return true;
QString metaTag;
foreach ( metaTag, m_metaTags )
{
......@@ -211,47 +188,6 @@ Clip::isLinked() const
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
Clip::toVariant() const
{
......@@ -261,14 +197,8 @@ Clip::toVariant() const
{ "notes", m_notes },
{ "formats", (int)formats() }
};
if ( isRootClip() )
h.insert( "media", m_media->toVariant() );
else
{
h.insert( "parent", m_parent->uuid().toString() );
h.insert( "begin", begin() );
h.insert( "end", end() );
}
h.insert( "begin", begin() );
h.insert( "end", end() );
if ( isLinked() == true )
{
h.insert( "linkedClip", m_linkedClipUuid );
......@@ -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() const
{
......@@ -314,80 +231,9 @@ Clip::input()
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
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" ) )
{
const auto& filters = m["filters"].toList();
......
......@@ -64,17 +64,8 @@ class Clip : public Workflow::Helper
* the end of the parent will be used.
* \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() );
/**
* \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() );
Clip( QSharedPointer<Media> parent, qint64 begin = 0, qint64 end = Backend::IInput::EndOfMedia, const QUuid &uuid = QStringLiteral() );
virtual ~Clip();
/**
......@@ -88,9 +79,6 @@ class Clip : public Workflow::Helper
QSharedPointer<Media> media();
QSharedPointer<const Media> media() const;
Clip *parent();
const Clip *parent() const;
/**
\brief Returns an unique Uuid for this clip (which is NOT the
parent's Uuid).
......@@ -120,29 +108,15 @@ class Clip : public Workflow::Helper
const QString &notes() const;
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 toVariantFull() const;
Formats formats() const;
void setFormats( Formats formats );
Backend::IInput* input();
static Clip* fromVariant( const QVariant& v );
private:
static Clip* fromVariant( const QVariant& v, Clip* parent );
void loadVariant(const QVariantMap& v );
//FIXME: This shouldn't be represented in the Library
void loadFilters(const QVariantMap& v );
private:
QSharedPointer<Media> m_media;
......@@ -151,17 +125,6 @@ class Clip : public Workflow::Helper
QStringList m_metaTags;
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;
bool m_isLinked;
......@@ -172,19 +135,6 @@ class Clip : public Workflow::Helper
* \brief Act just like QObject::destroyed(), but before the clip deletion.
*/
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 )
......
......@@ -63,7 +63,7 @@ const QString Media::streamPrefix = "stream://";
QPixmap* Media::defaultSnapshot = nullptr;
#endif
Media::Media( medialibrary::MediaPtr media )
Media::Media( medialibrary::MediaPtr media, const QUuid& uuid /* = QUuid() */ )
: m_input( nullptr )
, m_mlMedia( media )
{
......@@ -80,7 +80,7 @@ Media::Media( medialibrary::MediaPtr media )
if ( m_mlFile == nullptr )
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_baseClip = new Clip( sharedFromThis() );
m_baseClip = new Clip( sharedFromThis(), 0, Backend::IInput::EndOfMedia, uuid );
}
QString
......@@ -119,13 +119,35 @@ Media::id() const
Clip*
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
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*
......@@ -143,13 +165,40 @@ Media::input() const
QSharedPointer<Media>
Media::fromVariant( const QVariant& v )
{
bool ok = false;
auto mediaId = v.toLongLong( &ok );
if ( ok == false )
return QSharedPointer<Media>{};
/**
* The media is stored as such:
* 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 );
//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
......@@ -171,4 +220,24 @@ Media::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;
}
#endif
......@@ -35,8 +35,10 @@
#include <memory>
#include <QEnableSharedFromThis>
#include <QHash>
#include <QString>
#include <QObject>
#include <QUuid>
#include <QXmlStreamWriter>
#include "Backend/MLT/MLTInput.h"
......@@ -82,7 +84,7 @@ public:
static const QString ImageExtensions;
static const QString streamPrefix;
Media( medialibrary::MediaPtr media );
Media( medialibrary::MediaPtr media, const QUuid& uuid = QUuid() );
QString mrl() const;
FileType fileType() const;
......@@ -98,6 +100,7 @@ public:
* @return A new Clip, representing the media from [begin] to [end]
*/
Clip* cut( qint64 begin, qint64 end );
void removeSubclip( const QUuid& uuid );
QVariant toVariant() const;
......@@ -110,16 +113,35 @@ public:
// This has to be called from the GUI thread.
QPixmap& snapshot();
#endif
private:
Clip* loadSubclip( const QVariantMap& m );
protected:
std::unique_ptr<Backend::IInput> m_input;
medialibrary::MediaPtr m_mlMedia;
medialibrary::FilePtr m_mlFile;
Clip* m_baseClip;
QHash<QUuid, Clip*> m_clips;
#ifdef HAVE_GUI
static QPixmap* defaultSnapshot;
QPixmap m_snapshot;
#endif
signals:
/**
* \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& );
};
#endif // MEDIA_H__
......@@ -222,11 +222,8 @@ MainWorkflow::clipInfo( const QString& uuid )
h["name"] = lClip->media()->title();
h["audio"] = lClip->formats().testFlag( Clip::Audio );
h["video"] = lClip->formats().testFlag( Clip::Video );
if ( lClip->isRootClip() == true )
{
h["begin"] = lClip->begin();
h["end"] = lClip->end();
}
h["begin"] = lClip->begin();
h["end"] = lClip->end();
return QJsonObject::fromVariantHash( h );
}
......
......@@ -83,7 +83,8 @@ SequenceWorkflow::addClip( const QUuid& uuid, quint32 trackId, qint32 pos, bool
return QUuid().toString();
}
auto newClip = std::make_shared<Clip>( clip );
//FIXME: This will blow up:
auto newClip = std::shared_ptr<Clip>( clip );
if ( isAudioClip == true )
newClip->setFormats( Clip::Audio );
......@@ -224,29 +225,31 @@ SequenceWorkflow::loadFromVariant( const QVariant& variant )
{
for ( auto& var : variant.toMap()["clips"].toList() )
{
auto m = var.toMap();
auto parentClip = Core::instance()->library()->clip( m["parent"].toString() );
//FIXME!!!
if ( parentClip == nullptr )
{
vlmcCritical() << "Couldn't find an acceptable parent to be added.";
continue;
}
// auto m = var.toMap();
// auto parentClip = Core::instance()->library()->clip( m["parent"].toString() );
auto c = std::make_shared<Clip>( parentClip, m["begin"].toLongLong(), m["end"].toLongLong() );
c->setUuid( m["uuid"].toString() );
c->setFormats( (Clip::Formats)m["formats"].toInt() );
// if ( parentClip == nullptr )
// {
// vlmcCritical() << "Couldn't find an acceptable parent to be added.";
// continue;
// }
addClip( c, m["trackId"].toUInt(), m["position"].toLongLong() );
// auto c = std::make_shared<Clip>( parentClip, m["begin"].toLongLong(), m["end"].toLongLong() );
// c->setUuid( m["uuid"].toString() );
// c->setFormats( (Clip::Formats)m["formats"].toInt() );
auto isLinked = m["linked"].toBool();
c->setLinked( isLinked );
if ( isLinked == true )
c->setLinkedClipUuid( m["linkedClip"].toString() );
// addClip( c, m["trackId"].toUInt(), m["position"].toLongLong() );
EffectHelper::loadFromVariant( m["filters"], c->input() );
// auto isLinked = m["linked"].toBool();
// c->setLinked( isLinked );
// if ( isLinked == true )
// c->setLinkedClipUuid( m["linkedClip"].toString() );
emit Core::instance()->workflow()->clipAdded( c->uuid().toString() );
// EffectHelper::loadFromVariant( m["filters"], c->input() );
// emit Core::instance()->workflow()->clipAdded( c->uuid().toString() );
}
EffectHelper::loadFromVariant( variant.toMap()["filters"], m_multitrack );
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment