TrackWorkflow.cpp 14.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*****************************************************************************
 * TrackWorkflow.cpp : Will query the Clip workflow for each successive clip in the track
 *****************************************************************************
 * Copyright (C) 2008-2009 the VLMC team
 *
 * Authors: Hugo Beauzee-Luyssen <hugo@vlmc.org>
 *
 * 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 <QtDebug>

25
#include "vlmc.h"
26
#include "TrackWorkflow.h"
27
#include "VideoClipWorkflow.h"
28
#include "ImageClipWorkflow.h"
29
#include "AudioClipWorkflow.h"
30

31
TrackWorkflow::TrackWorkflow( unsigned int trackId, MainWorkflow::TrackType type  ) :
32
        m_trackId( trackId ),
33
        m_length( 0 ),
34
        m_forceRepositionning( false ),
35
        m_paused( false ),
36
        m_synchroneRenderBuffer( NULL ),
37 38
        m_trackType( type ),
        m_lastFrame( 0 )
39
{
40
    m_forceRepositionningMutex = new QMutex;
41
    m_clipsLock = new QReadWriteLock;
42 43
}

44 45
TrackWorkflow::~TrackWorkflow()
{
46 47
    QMap<qint64, ClipWorkflow*>::iterator       it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::iterator       end = m_clips.end();
48 49 50 51 52 53 54

    while ( it != end )
    {
        stopClipWorkflow( it.value() );
        delete it.value();
        it = m_clips.erase( it );
    }
55
    delete m_clipsLock;
56
    delete m_forceRepositionningMutex;
57 58
}

59
void    TrackWorkflow::addClip( Clip* clip, qint64 start )
60
{
61
    ClipWorkflow* cw;
62
    if ( m_trackType == MainWorkflow::VideoTrack )
63 64 65 66 67 68
    {
        if ( clip->getParent()->getFileType() == Media::Video )
            cw = new VideoClipWorkflow( clip );
        else
            cw = new ImageClipWorkflow( clip );
    }
69 70
    else
        cw = new AudioClipWorkflow( clip );
71
    addClip( cw, start );
72 73
}

74 75 76
void    TrackWorkflow::addClip( ClipWorkflow* cw, qint64 start )
{
    QWriteLocker    lock( m_clipsLock );
77
    connect( cw, SIGNAL( renderComplete( ClipWorkflow* ) ), this, SLOT( clipWorkflowRenderCompleted( ClipWorkflow* ) ), Qt::DirectConnection );
78 79
    connect( cw, SIGNAL( paused() ), this, SLOT( clipWorkflowPaused() ) );
    connect( cw, SIGNAL( unpaused() ), this, SLOT( clipWorkflowUnpaused() ) );
80 81 82 83
    m_clips.insert( start, cw );
    computeLength();
}

84
//Must be called from a thread safe method (m_clipsLock locked)
85 86 87
void                TrackWorkflow::computeLength()
{
    if ( m_clips.count() == 0 )
88
    {
89
        m_length = 0;
90 91
        return ;
    }
92
    QMap<qint64, ClipWorkflow*>::const_iterator it = m_clips.end() - 1;
93 94 95 96 97 98 99 100
    m_length = (it.key() + it.value()->getClip()->getLength() );
}

qint64              TrackWorkflow::getLength() const
{
    return m_length;
}

101
qint64              TrackWorkflow::getClipPosition( const QUuid& uuid ) const
102
{
103 104
    QMap<qint64, ClipWorkflow*>::const_iterator     it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::const_iterator     end = m_clips.end();
105 106 107

    while ( it != end )
    {
108
        if ( it.value()->getClip()->getUuid() == uuid )
109 110 111 112
            return it.key();
        ++it;
    }
    return -1;
113 114
}

115 116 117 118 119 120 121
Clip*               TrackWorkflow::getClip( const QUuid& uuid )
{
    QMap<qint64, ClipWorkflow*>::const_iterator     it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::const_iterator     end = m_clips.end();

    while ( it != end )
    {
122
        if ( it.value()->getClip()->getUuid() == uuid )
123 124 125 126 127 128
            return it.value()->getClip();
        ++it;
    }
    return NULL;
}

129
void        TrackWorkflow::renderClip( ClipWorkflow* cw, qint64 currentFrame,
130
                                        qint64 start , bool needRepositioning )
131
{
132
    cw->getStateLock()->lockForRead();
133

134
//    qDebug() << "Rendering clip" << cw << "state:" << cw->getState() << "Type:" << m_trackType;
135
    if ( cw->getState() == ClipWorkflow::Rendering )
136
    {
137 138
        cw->getStateLock()->unlock();
        if ( needRepositioning == true )
139
            adjustClipTime( currentFrame, start, cw );
140
    }
141
    else if ( cw->getState() == ClipWorkflow::Stopped )
142
    {
143
        cw->getStateLock()->unlock();
144
        cw->initialize();
145
        if ( start != currentFrame || cw->getClip()->getBegin() != 0 ) //Clip was not started as its real begining
146
        {
147
            adjustClipTime( currentFrame, start, cw );
148
        }
149
        //FIXME: comment this part ?!
150 151
        if ( m_paused == true )
            clipWorkflowRenderCompleted( cw );
152
    }
153 154 155
    else if ( cw->getState() == ClipWorkflow::EndReached )
    {
        cw->getStateLock()->unlock();
156
        clipWorkflowRenderCompleted( cw );
157 158
        //The stopClipWorkflow() method will take care of that.
    }
159
    else
160
    {
161
        cw->getStateLock()->unlock();
162
    }
163
}
164

165
void                TrackWorkflow::preloadClip( ClipWorkflow* cw )
166
{
167
    cw->getStateLock()->lockForRead();
168

169
    if ( cw->getState() == ClipWorkflow::Stopped )
170
    {
171
        cw->getStateLock()->unlock();
172
        cw->initialize();
173
        return ;
174
    }
175
    cw->getStateLock()->unlock();
176 177
}

178
void                TrackWorkflow::stopClipWorkflow( ClipWorkflow* cw )
179
{
180
//    qDebug() << "Stopping clip workflow";
181
    cw->getStateLock()->lockForRead();
182

183
    if ( cw->getState() == ClipWorkflow::Stopped )
184
    {
185 186
        cw->getStateLock()->unlock();
        return ;
187
    }
188 189
    if ( cw->getState() == ClipWorkflow::EndReached ||
         cw->getState() == ClipWorkflow::Rendering )
190
    {
191
        cw->getStateLock()->unlock();
192 193
        cw->stop();
    }
194 195
    else
    {
196
        qCritical() << "Unexpected ClipWorkflow::State when stopping :" << cw->getState();
197
        cw->getStateLock()->unlock();
198 199 200
    }
}

201 202 203 204 205 206 207 208 209 210
bool                TrackWorkflow::checkEnd( qint64 currentFrame ) const
{
    if ( m_clips.size() == 0 )
        return true;
    //This is the last video by chronological order :
    QMap<qint64, ClipWorkflow*>::const_iterator   it = m_clips.end() - 1;
    //If it ends before the current frame, we reached end.
    return ( it.value()->getClip()->getLength() + it.key() < currentFrame );
}

211 212
void                    TrackWorkflow::stop()
{
213 214
    QMap<qint64, ClipWorkflow*>::iterator       it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::iterator       end = m_clips.end();
215 216 217 218 219 220

    while ( it != end )
    {
        stopClipWorkflow( it.value() );
        ++it;
    }
221
    m_lastFrame = 0;
222 223
}

224
bool                TrackWorkflow::getOutput( qint64 currentFrame )
225
{
226 227
    QReadLocker     lock( m_clipsLock );

228 229 230 231
    QMap<qint64, ClipWorkflow*>::iterator       it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::iterator       end = m_clips.end();
    bool                                        needRepositioning;
    bool                                        hasRendered = false;
232

233 234 235
    if ( checkEnd( currentFrame ) == true )
    {
        emit trackEndReached( m_trackId );
236
        //We continue, as there can be ClipWorkflow that requires to be stopped.
237
    }
238 239 240 241 242 243 244
    {
        QMutexLocker    lock( m_forceRepositionningMutex );
        if ( m_forceRepositionning == true )
        {
            needRepositioning = true;
            m_forceRepositionning = false;
        }
245
        else if ( m_paused == true && currentFrame != m_lastFrame )
246
            needRepositioning = true;
247
        else
248
            needRepositioning = ( abs( currentFrame - m_lastFrame ) > 1 ) ? true : false;
249
    }
250
    m_nbClipToRender = 0;
251

252
    while ( it != end )
253
    {
254 255 256
        qint64          start = it.key();
        ClipWorkflow*   cw = it.value();
        //Is the clip supposed to render now ?
257
//        qDebug() << "Start:" << start << "Current Frame:" << currentFrame;
258
        if ( start <= currentFrame && currentFrame <= start + cw->getClip()->getLength() )
259
        {
260
            m_nbClipToRender.fetchAndAddAcquire( 1 );
261
            renderClip( cw, currentFrame, start, needRepositioning );
262
            hasRendered = true;
263
        }
264 265 266
        //Is it about to be rendered ?
        else if ( start > currentFrame &&
                start - currentFrame < TrackWorkflow::nbFrameBeforePreload )
267
        {
268
            preloadClip( cw );
269
        }
270 271
        //Is it supposed to be stopped ?
        else
272
        {
273
            stopClipWorkflow( cw );
274
        }
275 276

        ++it;
277
    }
278
    if ( hasRendered == false )
279 280 281
    {
        clipWorkflowRenderCompleted( NULL );
    }
282
    m_lastFrame = currentFrame;
283
    return hasRendered;
284 285
}

286
void                TrackWorkflow::pause()
287
{
288
    m_paused = true;
289 290
}

291
void            TrackWorkflow::moveClip( const QUuid& id, qint64 startingFrame )
292
{
293 294
    QWriteLocker    lock( m_clipsLock );

295 296
    QMap<qint64, ClipWorkflow*>::iterator       it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::iterator       end = m_clips.end();
297 298 299

    while ( it != end )
    {
300
        if ( it.value()->getClip()->getUuid() == id )
301 302 303 304 305 306
        {
            ClipWorkflow* cw = it.value();
            m_clips.erase( it );
            m_clips[startingFrame] = cw;
            QMutexLocker    lock( m_forceRepositionningMutex );
            m_forceRepositionning = true;
307
            computeLength();
308 309 310 311 312 313 314
            return ;
        }
        ++it;
    }
    qDebug() << "Track" << m_trackId << "was asked to move clip" << id << "to position" << startingFrame
            << "but this clip doesn't exist in this track";
}
315 316 317 318 319

Clip*       TrackWorkflow::removeClip( const QUuid& id )
{
    QWriteLocker    lock( m_clipsLock );

320 321
    QMap<qint64, ClipWorkflow*>::iterator       it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::iterator       end = m_clips.end();
322 323 324

    while ( it != end )
    {
325
        if ( it.value()->getClip()->getUuid() == id )
326 327
        {
            ClipWorkflow*   cw = it.value();
328
            Clip*           clip = cw->getClip();
329
            m_clips.erase( it );
330
            stopClipWorkflow( cw );
331
            computeLength();
332
            disconnectClipWorkflow( cw );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
333
            delete cw;
334 335
            if ( m_length == 0 )
                emit trackEndReached( m_trackId );
336
            return clip;
337 338 339 340 341
        }
        ++it;
    }
    return NULL;
}
342

343 344 345 346 347 348 349 350 351 352 353 354
ClipWorkflow*       TrackWorkflow::removeClipWorkflow( const QUuid& id )
{
    QWriteLocker    lock( m_clipsLock );

    QMap<qint64, ClipWorkflow*>::iterator       it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::iterator       end = m_clips.end();

    while ( it != end )
    {
        if ( it.value()->getClip()->getUuid() == id )
        {
            ClipWorkflow*   cw = it.value();
355
            disconnectClipWorkflow( cw );
356 357 358 359 360 361 362 363 364 365
            m_clips.erase( it );
            computeLength();
            return cw;

        }
        ++it;
    }
    return NULL;
}

366 367
void        TrackWorkflow::clipWorkflowRenderCompleted( ClipWorkflow* cw )
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
368
//    qDebug() << "Clip [" << QObject::sender() << "] render is completed on track" << m_trackId;
369
    if ( cw != NULL )
370
    {
371
        m_synchroneRenderBuffer = cw->getOutput( ClipWorkflow::Pop );
372
    }
373
    else
374
    {
375
        m_synchroneRenderBuffer = NULL;
376
    }
377
    m_nbClipToRender.fetchAndAddAcquire( -1 );
378 379 380
    //When there is nothing to render, m_nbClipToRender will be equal to one here, so we check for minus
    //or equal to 0
    if ( m_nbClipToRender <= 0 )
381
    {
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
382
//        qDebug() << "Track render completed";
383
        emit renderCompleted( m_trackId );
384
    }
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
385
//    else
386
//        qDebug() << "Track render not completed yet";
387
}
388

389
void*       TrackWorkflow::getSynchroneOutput()
390 391 392
{
    return m_synchroneRenderBuffer;
}
393 394 395

void    TrackWorkflow::unpause()
{
396
    m_paused = false;
397
}
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437

void    TrackWorkflow::save( QDomDocument& doc, QDomElement& trackNode ) const
{
    QReadLocker     lock( m_clipsLock );

    QMap<qint64, ClipWorkflow*>::const_iterator     it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::const_iterator     end = m_clips.end();

    for ( ; it != end ; ++it )
    {
        QDomElement     clipNode = doc.createElement( "clip" );

        {
            QDomElement     parent = doc.createElement( "parent" );

            QDomCharacterData   text = doc.createTextNode( it.value()->getClip()->getParent()->getUuid().toString() );
            parent.appendChild( text );
            clipNode.appendChild( parent );
        }
        {
            QDomElement     startFrame = doc.createElement( "startFrame" );

            QDomCharacterData   text = doc.createTextNode( QString::number( it.key() ) );
            startFrame.appendChild( text );
            clipNode.appendChild( startFrame );
        }
        {
            QDomElement     begin = doc.createElement( "begin" );

            QDomCharacterData   text = doc.createTextNode( QString::number( it.value()->getClip()->getBegin() ) );
            begin.appendChild( text );
            clipNode.appendChild( begin );
        }
        {
            QDomElement     end = doc.createElement( "end" );

            QDomCharacterData   text = doc.createTextNode( QString::number( it.value()->getClip()->getEnd() ) );
            end.appendChild( text );
            clipNode.appendChild( end );
        }
438 439 440 441 442 443 444
        {
            QDomElement     trackType = doc.createElement( "trackType" );

            QDomCharacterData   text = doc.createTextNode( QString::number( m_trackType ) );
            trackType.appendChild( text );
            clipNode.appendChild( trackType );
        }
445 446 447 448
        trackNode.appendChild( clipNode );
    }
}

449 450 451 452 453 454 455 456
void    TrackWorkflow::clear()
{
    QWriteLocker    lock( m_clipsLock );
    QMap<qint64, ClipWorkflow*>::iterator       it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::iterator       end = m_clips.end();

    for ( ; it != end; ++it )
    {
457
        //The clip contained in the trackworkflow will be delete by the undo stack.
458 459 460 461 462 463
        ClipWorkflow*   cw = it.value();
        delete cw;
    }
    m_clips.clear();
    m_length = 0;
}
464 465 466 467

void    TrackWorkflow::adjustClipTime( qint64 currentFrame, qint64 start, ClipWorkflow* cw )
{
    qint64  nbMs = ( currentFrame - start ) / cw->getClip()->getParent()->getFps() * 1000;
468 469
    qint64  beginInMs = cw->getClip()->getBegin() / cw->getClip()->getParent()->getFps() * 1000;
    qint64  startFrame = beginInMs + nbMs;
470 471
    cw->setTime( startFrame );
}
472

473 474 475 476 477 478
void    TrackWorkflow::disconnectClipWorkflow( ClipWorkflow* cw )
{
    disconnect( cw, SIGNAL( renderComplete( ClipWorkflow* ) ), this, SLOT( clipWorkflowRenderCompleted( ClipWorkflow* ) ) );
    disconnect( cw, SIGNAL( paused() ), this, SLOT( clipWorkflowPaused() ) );
    disconnect( cw, SIGNAL( unpaused() ), this, SLOT( clipWorkflowUnpaused() ) );
}
479 480 481 482 483 484

void    TrackWorkflow::forceRepositionning()
{
    QMutexLocker    lock( m_forceRepositionningMutex );
    m_forceRepositionning = true;
}
485 486 487 488 489

void    TrackWorkflow::simulateBlackOutputRender()
{
    clipWorkflowRenderCompleted( NULL );
}