TrackWorkflow.cpp 14.7 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_trackType( type ),
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
37 38 39
        m_lastFrame( 0 ),
        m_videoStackedBuffer( NULL ),
        m_audioStackedBuffer( NULL )
40
{
41
    m_forceRepositionningMutex = new QMutex;
42
    m_clipsLock = new QReadWriteLock;
43 44
}

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

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

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

75 76 77 78 79 80 81
void    TrackWorkflow::addClip( ClipWorkflow* cw, qint64 start )
{
    QWriteLocker    lock( m_clipsLock );
    m_clips.insert( start, cw );
    computeLength();
}

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

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

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

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

113 114 115 116 117 118 119
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 )
    {
120
        if ( it.value()->getClip()->getUuid() == uuid )
121 122 123 124 125 126
            return it.value()->getClip();
        ++it;
    }
    return NULL;
}

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

132 133 134 135 136
    qDebug() << "Rendering clip" << cw << "state:" << cw->getState() << "Type:" << m_trackType;
    if ( cw->getState() == ClipWorkflow::Rendering ||
         cw->getState() == ClipWorkflow::Paused ||
         cw->getState() == ClipWorkflow::PauseRequired ||
         cw->getState() == ClipWorkflow::UnpauseRequired )
137
    {
138 139
        cw->getStateLock()->unlock();
        if ( needRepositioning == true )
140
            adjustClipTime( currentFrame, start, cw );
141
        return cw->getOutput( ClipWorkflow::Pop );
142
    }
143
    else if ( cw->getState() == ClipWorkflow::Stopped )
144
    {
145
        cw->getStateLock()->unlock();
146
        cw->initialize();
147
        if ( start != currentFrame || cw->getClip()->getBegin() != 0 ) //Clip was not started as its real begining
148
        {
149
            adjustClipTime( currentFrame, start, cw );
150
        }
151 152
        cw->waitForCompleteInit();
        return cw->getOutput( ClipWorkflow::Pop );
153
    }
154 155
    else if ( cw->getState() == ClipWorkflow::EndReached )
    {
156
        qDebug() << "State is endreached";
157 158 159
        cw->getStateLock()->unlock();
        //The stopClipWorkflow() method will take care of that.
    }
160
    else
161
    {
162
        qCritical() << "Unexpected state:" << cw->getState();
163
        cw->getStateLock()->unlock();
164
    }
165
    return NULL;
166
}
167

168
void                TrackWorkflow::preloadClip( ClipWorkflow* cw )
169
{
170
    cw->getStateLock()->lockForRead();
171

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

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

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

204 205 206 207 208 209 210 211 212 213
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 );
}

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

    while ( it != end )
    {
        stopClipWorkflow( it.value() );
        ++it;
    }
224
    m_lastFrame = 0;
225 226
}

227 228 229 230 231 232 233 234
void                TrackWorkflow::releasePreviousRender()
{
    if ( m_audioStackedBuffer != NULL )
        m_audioStackedBuffer->release();
    if ( m_videoStackedBuffer != NULL )
        m_videoStackedBuffer->release();
}

235
void*               TrackWorkflow::getOutput( qint64 currentFrame, qint64 subFrame )
236
{
237
    releasePreviousRender();
238 239
    QReadLocker     lock( m_clipsLock );

240 241 242
    QMap<qint64, ClipWorkflow*>::iterator       it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::iterator       end = m_clips.end();
    bool                                        needRepositioning;
243
    void*                                       ret = NULL;
244

245 246 247
    if ( checkEnd( currentFrame ) == true )
    {
        emit trackEndReached( m_trackId );
248
        //We continue, as there can be ClipWorkflow that requires to be stopped.
249
    }
250 251 252 253 254 255 256
    {
        QMutexLocker    lock( m_forceRepositionningMutex );
        if ( m_forceRepositionning == true )
        {
            needRepositioning = true;
            m_forceRepositionning = false;
        }
257
        else if ( m_paused == true && subFrame != m_lastFrame )
258
            needRepositioning = true;
259
        else
260
            needRepositioning = ( abs( subFrame - m_lastFrame ) > 1 ) ? true : false;
261
    }
262

263
    while ( it != end )
264
    {
265 266 267
        qint64          start = it.key();
        ClipWorkflow*   cw = it.value();
        //Is the clip supposed to render now ?
268
//        qDebug() << "Start:" << start << "Current Frame:" << currentFrame;
269
        if ( start <= currentFrame && currentFrame <= start + cw->getClip()->getLength() )
270
        {
271
            qDebug() << "Rendering clip... type:" << m_trackType;
272 273 274
            if ( ret != NULL )
                qCritical() << "There's more than one clip to render here. Undefined behaviour !";
            ret = renderClip( cw, currentFrame, start, needRepositioning );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
275 276 277 278
            if ( m_trackType == MainWorkflow::VideoTrack )
                m_videoStackedBuffer = reinterpret_cast<StackedBuffer<LightVideoFrame*>*>( ret );
            else
                m_audioStackedBuffer = reinterpret_cast<StackedBuffer<AudioClipWorkflow::AudioSample*>*>( ret );
279
            qDebug() << "Rendered buffer. Type:" << m_trackType << "ret:" << (void*)ret;
280
        }
281 282 283
        //Is it about to be rendered ?
        else if ( start > currentFrame &&
                start - currentFrame < TrackWorkflow::nbFrameBeforePreload )
284
        {
285
            qDebug() << "Preloading clip.";
286
            preloadClip( cw );
287
        }
288 289
        //Is it supposed to be stopped ?
        else
290
        {
291
            qDebug() << "Stopping clip";
292
            stopClipWorkflow( cw );
293
        }
294
        ++it;
295
    }
296
    m_lastFrame = subFrame;
297
    return ret;
298 299
}

300
void                TrackWorkflow::pause()
301
{
302
    m_paused = true;
303 304
}

305
void            TrackWorkflow::moveClip( const QUuid& id, qint64 startingFrame )
306
{
307 308
    QWriteLocker    lock( m_clipsLock );

309 310
    QMap<qint64, ClipWorkflow*>::iterator       it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::iterator       end = m_clips.end();
311 312 313

    while ( it != end )
    {
314
        if ( it.value()->getClip()->getUuid() == id )
315 316 317 318 319 320
        {
            ClipWorkflow* cw = it.value();
            m_clips.erase( it );
            m_clips[startingFrame] = cw;
            QMutexLocker    lock( m_forceRepositionningMutex );
            m_forceRepositionning = true;
321
            computeLength();
322 323 324 325 326 327 328
            return ;
        }
        ++it;
    }
    qDebug() << "Track" << m_trackId << "was asked to move clip" << id << "to position" << startingFrame
            << "but this clip doesn't exist in this track";
}
329 330 331 332 333

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

334 335
    QMap<qint64, ClipWorkflow*>::iterator       it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::iterator       end = m_clips.end();
336 337 338

    while ( it != end )
    {
339
        if ( it.value()->getClip()->getUuid() == id )
340 341
        {
            ClipWorkflow*   cw = it.value();
342
            Clip*           clip = cw->getClip();
343
            m_clips.erase( it );
344
            stopClipWorkflow( cw );
345
            computeLength();
346
            disconnectClipWorkflow( cw );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
347
            delete cw;
348 349
            if ( m_length == 0 )
                emit trackEndReached( m_trackId );
350
            return clip;
351 352 353 354 355
        }
        ++it;
    }
    return NULL;
}
356

357 358 359 360 361 362 363 364 365 366 367 368
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();
369
            disconnectClipWorkflow( cw );
370 371 372 373 374 375 376 377 378 379
            m_clips.erase( it );
            computeLength();
            return cw;

        }
        ++it;
    }
    return NULL;
}

380 381
void    TrackWorkflow::unpause()
{
382
    m_paused = false;
383
}
384 385 386 387 388 389 390 391 392 393 394 395 396 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

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 );
        }
424 425 426 427 428 429 430
        {
            QDomElement     trackType = doc.createElement( "trackType" );

            QDomCharacterData   text = doc.createTextNode( QString::number( m_trackType ) );
            trackType.appendChild( text );
            clipNode.appendChild( trackType );
        }
431 432 433 434
        trackNode.appendChild( clipNode );
    }
}

435 436 437 438 439 440 441 442
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 )
    {
443
        //The clip contained in the trackworkflow will be delete by the undo stack.
444 445 446 447 448 449
        ClipWorkflow*   cw = it.value();
        delete cw;
    }
    m_clips.clear();
    m_length = 0;
}
450 451 452 453

void    TrackWorkflow::adjustClipTime( qint64 currentFrame, qint64 start, ClipWorkflow* cw )
{
    qint64  nbMs = ( currentFrame - start ) / cw->getClip()->getParent()->getFps() * 1000;
454 455
    qint64  beginInMs = cw->getClip()->getBegin() / cw->getClip()->getParent()->getFps() * 1000;
    qint64  startFrame = beginInMs + nbMs;
456 457
    cw->setTime( startFrame );
}
458

459 460 461 462 463 464
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() ) );
}
465 466 467 468 469 470

void    TrackWorkflow::forceRepositionning()
{
    QMutexLocker    lock( m_forceRepositionningMutex );
    m_forceRepositionning = true;
}