TrackWorkflow.cpp 15.1 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
27
#include "TrackWorkflow.h"

28
29
TrackWorkflow::TrackWorkflow( unsigned int trackId ) :
        m_trackId( trackId ),
30
        m_length( 0 ),
31
        m_forceRepositionning( false ),
32
33
        m_paused( false ),
        m_synchroneRenderBuffer( NULL )
34
{
35
    m_forceRepositionningMutex = new QMutex;
36
    m_clipsLock = new QReadWriteLock;
37
38
}

39
40
TrackWorkflow::~TrackWorkflow()
{
41
42
    QMap<qint64, ClipWorkflow*>::iterator       it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::iterator       end = m_clips.end();
43
44
45
46
47
48
49

    while ( it != end )
    {
        stopClipWorkflow( it.value() );
        delete it.value();
        it = m_clips.erase( it );
    }
50
    delete m_clipsLock;
51
    delete m_forceRepositionningMutex;
52
53
}

54
void    TrackWorkflow::addClip( Clip* clip, qint64 start )
55
{
56
    ClipWorkflow* cw = new ClipWorkflow( clip );
57
    addClip( cw, start );
58
59
}

60
61
62
void    TrackWorkflow::addClip( ClipWorkflow* cw, qint64 start )
{
    QWriteLocker    lock( m_clipsLock );
63
    connect( cw, SIGNAL( renderComplete( ClipWorkflow* ) ), this, SLOT( clipWorkflowRenderCompleted( ClipWorkflow* ) ), Qt::DirectConnection );
64
65
    connect( cw, SIGNAL( paused() ), this, SLOT( clipWorkflowPaused() ) );
    connect( cw, SIGNAL( unpaused() ), this, SLOT( clipWorkflowUnpaused() ) );
66
67
68
69
    m_clips.insert( start, cw );
    computeLength();
}

70
//Must be called from a thread safe method (m_clipsLock locked)
71
72
73
void                TrackWorkflow::computeLength()
{
    if ( m_clips.count() == 0 )
74
    {
75
        m_length = 0;
76
77
        return ;
    }
78
    QMap<qint64, ClipWorkflow*>::const_iterator it = m_clips.end() - 1;
79
80
81
82
83
84
85
86
    m_length = (it.key() + it.value()->getClip()->getLength() );
}

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

87
qint64              TrackWorkflow::getClipPosition( const QUuid& uuid ) const
88
{
89
90
    QMap<qint64, ClipWorkflow*>::const_iterator     it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::const_iterator     end = m_clips.end();
91
92
93

    while ( it != end )
    {
94
        if ( it.value()->getClip()->getUuid() == uuid )
95
96
97
98
            return it.key();
        ++it;
    }
    return -1;
99
100
}

101
102
103
104
105
106
107
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 )
    {
108
        if ( it.value()->getClip()->getUuid() == uuid )
109
110
111
112
113
114
            return it.value()->getClip();
        ++it;
    }
    return NULL;
}

115
void        TrackWorkflow::renderClip( ClipWorkflow* cw, qint64 currentFrame,
116
                                        qint64 start , bool needRepositioning )
117
{
118
    cw->getStateLock()->lockForRead();
119

120
    if ( cw->getState() == ClipWorkflow::Rendering )
121
    {
122
123
124
        //The rendering state meens... whell it means that the frame is
        //beeing rendered, so we wait.
        cw->getStateLock()->unlock();
125
        cw->waitForCompleteRender();
126
        //This way we can trigger the appropriate if just below.
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
127
128
129
        //by restoring the initial state of the function, and just pretend that
        //nothing happened.
        cw->getStateLock()->lockForRead();
130
    }
131

132
    //If frame has been rendered :
133
    if ( cw->getState() == ClipWorkflow::Sleeping )
134
    {
135
        cw->getStateLock()->unlock();
136

137
138
        if ( needRepositioning == true )
        {
139
            float   pos = ( (float)( currentFrame - start ) / (float)(cw->getClip()->getLength()) );
140
            pos = pos * ( cw->getClip()->getEnd() - cw->getClip()->getBegin() ) + cw->getClip()->getBegin();
141
142
            cw->setPosition( pos );
        }
143
        QMutexLocker    lock( cw->getSleepMutex() );
144
        cw->wake();
145
    }
146
    else if ( cw->getState() == ClipWorkflow::Stopped )
147
    {
148
        cw->getStateLock()->unlock();
149
150
        cw->initialize();
        cw->startRender( m_paused );
151
152
153
        if ( start != currentFrame ) //Clip was not started as its real begining
        {
            float   pos = ( (float)( currentFrame - start ) / (float)(cw->getClip()->getLength()) );
154
            pos = pos * ( cw->getClip()->getEnd() - cw->getClip()->getBegin() ) + cw->getClip()->getBegin();
155
156
            cw->setPosition( pos );
        }
157
158
        if ( m_paused == true )
            clipWorkflowRenderCompleted( cw );
159
    }
160
161
    else if ( cw->getState() == ClipWorkflow::Ready ||
              cw->getState() == ClipWorkflow::Initializing )
162
    {
163
164
165
        //If the state is Initializing, then the workflow will wait.
        //Otherwise, it will start directly.
        cw->getStateLock()->unlock();
166
        cw->startRender( false );
167

168
169
170
        if ( needRepositioning == true )
        {
            float   pos = ( (float)( currentFrame - start ) / (float)(cw->getClip()->getLength()) );
171
            pos = pos * ( cw->getClip()->getEnd() - cw->getClip()->getBegin() ) + cw->getClip()->getBegin();
172
173
            cw->setPosition( pos );
        }
174
    }
175
176
177
    else if ( cw->getState() == ClipWorkflow::EndReached )
    {
        cw->getStateLock()->unlock();
178
        clipWorkflowRenderCompleted( cw );
179
180
        //The stopClipWorkflow() method will take care of that.
    }
181
182
183
184
185
186
187
188
189
190
    else if ( cw->getState() == ClipWorkflow::Paused )
    {
        cw->getStateLock()->unlock();
        if ( needRepositioning == true )
        {
            float   pos = ( (float)( currentFrame - start ) / (float)(cw->getClip()->getLength()) );
            cw->setPosition( pos );
        }
        clipWorkflowRenderCompleted( cw );
    }
191
    else
192
    {
193
        cw->getStateLock()->unlock();
194
    }
195
}
196

197
void                TrackWorkflow::preloadClip( ClipWorkflow* cw )
198
{
199
    cw->getStateLock()->lockForRead();
200

201
    if ( cw->getState() == ClipWorkflow::Stopped )
202
    {
203
        cw->getStateLock()->unlock();
204
        cw->initialize();
205
        return ;
206
    }
207
    cw->getStateLock()->unlock();
208
209
}

210
void                TrackWorkflow::stopClipWorkflow( ClipWorkflow* cw )
211
{
212
    cw->getStateLock()->lockForRead();
213

214
    if ( cw->getState() == ClipWorkflow::Stopped )
215
    {
216
217
        cw->getStateLock()->unlock();
        return ;
218
    }
219
    qDebug() << "Stopping clip. state:" << cw->getState();
220
221
222
    if ( cw->getState() == ClipWorkflow::Sleeping ||
         cw->getState() == ClipWorkflow::Ready ||
         cw->getState() == ClipWorkflow::EndReached )
223
    {
224
        cw->getStateLock()->unlock();
225
226
227
228
        {
            QMutexLocker    lock( cw->getSleepMutex() );
            cw->queryStateChange( ClipWorkflow::Stopping );
        }
229
230
        cw->wake();
        cw->stop();
231
    }
232
    else if ( cw->getState() == ClipWorkflow::Rendering )
233
    {
234
        cw->getStateLock()->unlock();
235
        cw->waitForCompleteRender();
236
237
238
239
        {
            QMutexLocker    lock( cw->getSleepMutex() );
            cw->queryStateChange( ClipWorkflow::Stopping );
        }
240
241
        cw->wake();
        cw->stop();
242
    }
243
    else if ( cw->getState() == ClipWorkflow::Initializing )
244
    {
245
        cw->getStateLock()->unlock();
246
        cw->waitForCompleteInit();
247
        cw->stop();
248
    }
249
250
251
252
    else if ( cw->getState() == ClipWorkflow::Paused )
    {
        cw->getStateLock()->unlock();
        cw->queryStateChange( ClipWorkflow::Stopping );
253
        cw->wake();
254
255
        cw->stop();
    }
256
257
    else
    {
258
        qDebug() << "Unexpected ClipWorkflow::State when stopping :" << cw->getState();
259
        cw->getStateLock()->unlock();
260
261
262
    }
}

263
264
265
266
267
268
269
270
271
272
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 );
}

273
274
void                    TrackWorkflow::stop()
{
275
276
    QMap<qint64, ClipWorkflow*>::iterator       it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::iterator       end = m_clips.end();
277
278
279
280
281
282
283
284

    while ( it != end )
    {
        stopClipWorkflow( it.value() );
        ++it;
    }
}

285
bool                TrackWorkflow::getOutput( qint64 currentFrame )
286
{
287
288
    QReadLocker     lock( m_clipsLock );

289
290
291
292
293
    QMap<qint64, ClipWorkflow*>::iterator       it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::iterator       end = m_clips.end();
    static  qint64                              lastFrame = 0;
    bool                                        needRepositioning;
    bool                                        hasRendered = false;
294

295
296
297
298
299
    if ( checkEnd( currentFrame ) == true )
    {
        emit trackEndReached( m_trackId );
        //We continue, as there can be ClipWorkflow that required to be stopped.
    }
300
301
302
303
304
305
306
    {
        QMutexLocker    lock( m_forceRepositionningMutex );
        if ( m_forceRepositionning == true )
        {
            needRepositioning = true;
            m_forceRepositionning = false;
        }
307
        else if ( m_paused == true && currentFrame != lastFrame )
308
            needRepositioning = true;
309
        else
310
            needRepositioning = ( abs( currentFrame - lastFrame ) > 1 ) ? true : false;
311
    }
312
    m_nbClipToRender = 0;
313

314
    while ( it != end )
315
    {
316
317
318
319
        qint64          start = it.key();
        ClipWorkflow*   cw = it.value();
        //Is the clip supposed to render now ?
        if ( start <= currentFrame && currentFrame <= start + cw->getClip()->getLength() )
320
        {
321
            m_nbClipToRender.fetchAndAddAcquire( 1 );
322
            renderClip( cw, currentFrame, start, needRepositioning );
323
            lastFrame = currentFrame;
324
            hasRendered = true;
325
        }
326
327
328
        //Is it about to be rendered ?
        else if ( start > currentFrame &&
                start - currentFrame < TrackWorkflow::nbFrameBeforePreload )
329
        {
330
            preloadClip( cw );
331
        }
332
333
        //Is it supposed to be stopped ?
        else
334
        {
335
            stopClipWorkflow( cw );
336
        }
337
338

        ++it;
339
    }
340
    if ( hasRendered == false )
341
342
343
    {
        clipWorkflowRenderCompleted( NULL );
    }
344
    return hasRendered;
345
346
}

347
void                TrackWorkflow::pause()
348
349
350
{
    QReadLocker     lock( m_clipsLock );

351
352
353
    QMap<qint64, ClipWorkflow*>::iterator       it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::iterator       end = m_clips.end();
    bool                                        pauseRequired = false;
354

355
    m_nbClipToPause = 0;
356
    for ( ; it != end; ++it )
357
358
359
    {
        ClipWorkflow*   cw = it.value();

360
        cw->getStateLock()->lockForRead();
361
362
363
364
365
        if ( cw->getState() == ClipWorkflow::Stopped )
        {
            cw->getStateLock()->unlock();
            continue ;
        }
366
        else if ( cw->getState() != ClipWorkflow::Paused )
367
        {
368
            cw->getStateLock()->unlock();
369
            m_nbClipToPause.fetchAndAddAcquire( 1 );
370
            cw->pause();
371
            pauseRequired = true;
372
373
374
        }
        else
        {
375
            //This should never be used.
376
            qDebug() << "Asking to pause in an already paused state";
377
            cw->getStateLock()->unlock();
378
379
        }
    }
380
381
382
383
    if ( pauseRequired == false )
    {
        clipWorkflowPaused();
    }
384
385
}

386
void            TrackWorkflow::moveClip( const QUuid& id, qint64 startingFrame )
387
{
388
389
    QWriteLocker    lock( m_clipsLock );

390
391
    QMap<qint64, ClipWorkflow*>::iterator       it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::iterator       end = m_clips.end();
392
393
394

    while ( it != end )
    {
395
        if ( it.value()->getClip()->getUuid() == id )
396
397
398
399
400
401
        {
            ClipWorkflow* cw = it.value();
            m_clips.erase( it );
            m_clips[startingFrame] = cw;
            QMutexLocker    lock( m_forceRepositionningMutex );
            m_forceRepositionning = true;
402
            computeLength();
403
404
405
406
407
408
409
            return ;
        }
        ++it;
    }
    qDebug() << "Track" << m_trackId << "was asked to move clip" << id << "to position" << startingFrame
            << "but this clip doesn't exist in this track";
}
410
411
412
413
414

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

415
416
    QMap<qint64, ClipWorkflow*>::iterator       it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::iterator       end = m_clips.end();
417
418
419

    while ( it != end )
    {
420
        if ( it.value()->getClip()->getUuid() == id )
421
422
        {
            ClipWorkflow*   cw = it.value();
423
            Clip*           clip = cw->getClip();
424
            m_clips.erase( it );
425
            computeLength();
426
            return clip;
427
428
429
430
431
        }
        ++it;
    }
    return NULL;
}
432

433
434
void        TrackWorkflow::clipWorkflowRenderCompleted( ClipWorkflow* cw )
{
435
    if ( cw != NULL )
436
    {
437
        m_synchroneRenderBuffer = cw->getOutput();
438
    }
439
    else
440
    {
441
        m_synchroneRenderBuffer = NULL;
442
    }
443
    m_nbClipToRender.fetchAndAddAcquire( -1 );
444
445
446
    //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 )
447
    {
448
        emit renderCompleted( m_trackId );
449
450
    }
}
451
452
453
454
455

unsigned char*  TrackWorkflow::getSynchroneOutput()
{
    return m_synchroneRenderBuffer;
}
456
457
458
459
460
461

void    TrackWorkflow::clipWorkflowPaused()
{
    m_nbClipToPause.fetchAndAddAcquire( -1 );
    if ( m_nbClipToPause <= 0 )
    {
462
        m_paused = true;
463
464
465
466
467
468
469
470
        emit trackPaused();
    }
}

void    TrackWorkflow::unpause()
{
    QReadLocker     lock( m_clipsLock );

471
472
    QMap<qint64, ClipWorkflow*>::iterator       it = m_clips.begin();
    QMap<qint64, ClipWorkflow*>::iterator       end = m_clips.end();
473
    bool                                        unpauseRequired = false;
474
475
476
477
478
479
480
481
482
483
484
485

    m_nbClipToUnpause = 0;
    for ( ; it != end; ++it )
    {
        ClipWorkflow*   cw = it.value();

        cw->getStateLock()->lockForRead();
        if ( cw->getState() == ClipWorkflow::Paused )
        {
            cw->getStateLock()->unlock();
            m_nbClipToUnpause.fetchAndAddAcquire( 1 );
            cw->unpause();
486
            unpauseRequired = true;
487
488
489
490
491
492
        }
        else
        {
            cw->getStateLock()->unlock();
        }
    }
493
494
    if ( unpauseRequired == false )
        clipWorkflowUnpaused();
495
496
497
498
499
500
501
}

void    TrackWorkflow::clipWorkflowUnpaused()
{
    m_nbClipToUnpause.fetchAndAddAcquire( -1 );
    if ( m_nbClipToUnpause <= 0 )
    {
502
        m_paused = false;
503
504
505
        emit trackUnpaused();
    }
}