Clip.cpp 8.11 KB
Newer Older
1 2 3
/*****************************************************************************
 * Clip.cpp : Represents a basic container for media informations.
 *****************************************************************************
4
 * Copyright (C) 2008-2016 VideoLAN
5
 *
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
6
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
7
 *          Yikei Lu    <luyikei.qmltu@gmail.com>
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 *
 * 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.
 *****************************************************************************/

/** \file
  * This file contains the Clip class implementation.
  */

28 29 30 31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

32
#include "Clip.h"
luyikei's avatar
luyikei committed
33
#include "Main/Core.h"
luyikei's avatar
luyikei committed
34
#include "Backend/MLT/MLTInput.h"
35 36 37
#include "Library/Library.h"
#include "Media/Media.h"
#include "Project/Workspace.h"
38
#include "EffectsEngine/EffectHelper.h"
39
#include "Tools/VlmcDebug.h"
luyikei's avatar
luyikei committed
40
#include <QVariant>
41

42
Clip::Clip( QSharedPointer<Media> media, qint64 begin /*= 0*/, qint64 end /*= Backend::IInput::EndOfMedia */, const QString& uuid /*= QString()*/ ) :
luyikei's avatar
luyikei committed
43
        Workflow::Helper( uuid ),
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
44
        m_media( media ),
luyikei's avatar
luyikei committed
45
        m_input( std::move( m_media->input()->cut( begin, end ) ) ),
luyikei's avatar
luyikei committed
46 47
        m_parent( media->baseClip() ),
        m_isLinked( false )
48
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
49
    m_rootClip = media->baseClip();
luyikei's avatar
luyikei committed
50 51 52 53 54 55
    Formats f;
    if ( media->input()->hasAudio() == true )
        f |= Clip::Audio;
    if ( media->input()->hasVideo() == true )
        f |= Clip::Video;
    setFormats( f );
56 57
}

58
Clip::Clip( Clip *parent, qint64 begin /*= -1*/, qint64 end /*= -2*/,
59
            const QString &uuid /*= QString()*/ ) :
luyikei's avatar
luyikei committed
60
        Workflow::Helper( uuid ),
luyikei's avatar
luyikei committed
61
        m_media( parent->media() ),
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
62
        m_rootClip( parent->rootClip() ),
63
        m_parent( parent )
64
{
65 66 67 68 69
    if ( begin == -1 )
        begin = parent->begin();
    else
        begin = parent->begin() + begin;

luyikei's avatar
luyikei committed
70
    if ( end == Backend::IInput::EndOfParent )
71 72 73
        end = parent->end();
    else
        end = parent->begin() + end;
luyikei's avatar
luyikei committed
74
    m_input = parent->input()->cut( begin, end );
luyikei's avatar
luyikei committed
75
    setFormats( parent->formats() );
76 77
}

78
Clip::~Clip()
79
{
80
    emit unloaded( this );
81 82
}

83
QSharedPointer<Media>
luyikei's avatar
luyikei committed
84
Clip::media()
85
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
86
    return m_media;
87 88
}

89
QSharedPointer<const Media>
luyikei's avatar
luyikei committed
90
Clip::media() const
91 92 93 94
{
    return m_media;
}

95 96
qint64
Clip::lengthSecond() const
97
{
luyikei's avatar
luyikei committed
98
    return qRound64( m_input->playableLength() / m_input->fps() );
99
}
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
100

101 102
const QStringList&
Clip::metaTags() const
103 104 105 106
{
    return m_metaTags;
}

107 108
void
Clip::setMetaTags( const QStringList &tags )
109 110 111 112
{
    m_metaTags = tags;
}

113 114
bool
Clip::matchMetaTag( const QString &tag ) const
115 116 117
{
    if ( tag.length() == 0 )
        return true;
118
    if ( m_parent && m_parent->matchMetaTag( tag ) == true )
119
        return true;
120 121 122 123 124 125 126 127
    QString metaTag;
    foreach ( metaTag, m_metaTags )
    {
        if ( metaTag.startsWith( tag, Qt::CaseInsensitive ) == true )
            return true;
    }
    return false;
}
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
128

129 130
const QString&
Clip::notes() const
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
131 132 133 134
{
    return m_notes;
}

135 136
void
Clip::setNotes( const QString &notes )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
137 138 139
{
    m_notes = notes;
}
140

141 142
const QUuid&
Clip::uuid() const
143
{
144 145
    Q_ASSERT( m_uuid.isNull() == false );
    return m_uuid;
146 147
}

148 149 150
void
Clip::setUuid( const QUuid &uuid )
{
151
    m_uuid = uuid;
152 153
}

154 155 156
qint64
Clip::begin() const
{
luyikei's avatar
luyikei committed
157
    return m_input->begin();
158 159 160 161 162
}

qint64
Clip::end() const
{
luyikei's avatar
luyikei committed
163
    return m_input->end();
164 165 166 167 168
}

void
Clip::setBegin( qint64 begin )
{
luyikei's avatar
luyikei committed
169
    m_input->setBegin( begin );
170 171 172 173 174
}

void
Clip::setEnd( qint64 end )
{
luyikei's avatar
luyikei committed
175
    m_input->setEnd( end );
176 177 178 179 180
}

qint64
Clip::length() const
{
luyikei's avatar
luyikei committed
181
    return m_input->playableLength();
182 183 184 185 186
}

void
Clip::setBoundaries( qint64 begin, qint64 end )
{
luyikei's avatar
luyikei committed
187
    m_input->setBoundaries( begin, end );
188 189
}

luyikei's avatar
luyikei committed
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
void
Clip::setLinkedClipUuid( const QUuid& uuid )
{
    m_linkedClipUuid = uuid;
}

const QUuid&
Clip::linkedClipUuid() const
{
    return m_linkedClipUuid;
}

void
Clip::setLinked( bool isLinked )
{
    m_isLinked = isLinked;
}

bool
Clip::isLinked() const
{
    return m_isLinked;
}

214 215 216
Clip*
Clip::rootClip()
{
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
217
    if ( m_rootClip == nullptr )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
218
        return this;
219 220 221
    return m_rootClip;
}

222
bool
223
Clip::isRootClip() const
224
{
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
225
    return ( m_rootClip == nullptr );
226
}
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
227 228

Clip*
luyikei's avatar
luyikei committed
229
Clip::parent()
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
230 231 232 233 234
{
    return m_parent;
}

const Clip*
luyikei's avatar
luyikei committed
235
Clip::parent() const
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
236 237 238 239
{
    return m_parent;
}

240
bool
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
241 242
Clip::addSubclip( Clip *clip )
{
243 244 245 246
    if ( m_subclips.contains( clip->uuid() ) == true )
        return false;
    m_subclips[clip->uuid()] = clip;
    emit subclipAdded( clip );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
247 248 249 250 251
}

void
Clip::clear()
{
252
    m_subclips.clear();
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
253
}
254

luyikei's avatar
luyikei committed
255 256 257 258 259 260
QVariant
Clip::toVariant() const
{
    QVariantHash h = {
        { "uuid", m_uuid.toString() },
        { "metatags", m_metaTags },
luyikei's avatar
luyikei committed
261 262
        { "notes", m_notes },
        { "formats", (int)formats() }
luyikei's avatar
luyikei committed
263 264 265 266 267 268
    };
    if ( isRootClip() )
        h.insert( "media", m_media->toVariant() );
    else
    {
        h.insert( "parent", m_parent->uuid().toString() );
269 270
        h.insert( "begin", begin() );
        h.insert( "end", end() );
luyikei's avatar
luyikei committed
271
    }
luyikei's avatar
luyikei committed
272 273 274 275 276 277 278
    if ( isLinked() == true )
    {
        h.insert( "linkedClip", m_linkedClipUuid );
        h.insert( "linked", true );
    }
    else
        h.insert( "linked", false );
luyikei's avatar
luyikei committed
279
    h.insert( "filters", EffectHelper::toVariant( m_input.get() ) );
luyikei's avatar
luyikei committed
280 281 282 283 284 285 286 287
    return QVariant( h );

}

QVariant
Clip::toVariantFull() const
{
    QVariantHash h = toVariant().toHash();
288 289 290 291 292 293
    if ( m_subclips.isEmpty() == true )
        return h;
    QVariantList l;
    for ( const auto& c : m_subclips.values() )
        l << c->toVariant();
    h.insert( "subClips", l );
luyikei's avatar
luyikei committed
294 295 296
    return h;
}

luyikei's avatar
luyikei committed
297 298 299 300 301 302 303 304 305 306 307 308 309 310
Clip::Formats
Clip::formats() const
{
    return m_formats;
}

void
Clip::setFormats( Formats formats )
{
    if ( formats.testFlag( Clip::None ) )
        m_formats = Clip::None;
    m_formats = formats;
}

luyikei's avatar
luyikei committed
311 312
Backend::IInput*
Clip::input()
luyikei's avatar
luyikei committed
313
{
luyikei's avatar
luyikei committed
314
    return m_input.get();
luyikei's avatar
luyikei committed
315 316
}

317 318 319 320 321 322 323 324 325 326 327
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;
    }

328 329
    auto mediaId = m["media"].toLongLong();
    if ( mediaId == 0 )
330 331 332 333 334 335 336 337 338 339 340 341
    {
        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;
    }

342
    auto media = Core::instance()->library()->media( mediaId );
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
    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 )
{
    if ( m.contains( "subClips" ) )
    {
        auto children = m["subClips"].toList();
        for ( const auto& clipMap : children )
389
            addSubclip( fromVariant( clipMap, this ) );
390 391 392 393 394 395 396 397
    }
    if ( m.contains( "filters" ) )
    {
        const auto& filters = m["filters"].toList();
        for ( const auto& f : filters )
            EffectHelper::loadFromVariant( f, input() );
    }
}