MainWorkflow.cpp 14.4 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
25
26
27
/*****************************************************************************
 * MainWorkflow.cpp : Will query all of the track workflows to render the final
 *                    image
 *****************************************************************************
 * 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>

#include "MainWorkflow.h"

28
unsigned char*  MainWorkflow::blackOutput = NULL;
29
MainWorkflow*   MainWorkflow::m_instance = NULL;
30

31
MainWorkflow::MainWorkflow( int trackCount ) :
32
        m_length( 0 ),
33
34
        m_trackCount( trackCount ),
        m_renderStarted( false )
35
{
36
37
38
39
40
41
    Q_ASSERT_X( MainWorkflow::m_instance == NULL,
                "MainWorkflow constructor", "Can't have more than one MainWorkflow instance" );
    m_instance = this;

    MainWorkflow::blackOutput = new unsigned char[VIDEOHEIGHT * VIDEOWIDTH * 3];
    memset( MainWorkflow::blackOutput, 0, VIDEOHEIGHT * VIDEOWIDTH * 3 );
42
43

    m_tracks = new Toggleable<TrackWorkflow*>[trackCount];
44
    for ( int i = 0; i < trackCount; ++i )
45
46
47
    {
        m_tracks[i].setPtr( new TrackWorkflow( i ) );
        connect( m_tracks[i], SIGNAL( trackEndReached( unsigned int ) ), this, SLOT( trackEndReached(unsigned int) ) );
48
        connect( m_tracks[i], SIGNAL( trackPaused() ), this, SLOT( trackPaused() ) );
49
        connect( m_tracks[i], SIGNAL( trackUnpaused() ), this, SLOT( trackUnpaused() ) );
50
        connect( m_tracks[i], SIGNAL( renderCompleted( unsigned int ) ), this,  SLOT( tracksRenderCompleted( unsigned int ) ), Qt::QueuedConnection );
51
    }
52
    m_renderStartedLock = new QReadWriteLock;
53
    m_renderMutex = new QMutex;
54
55
56
    m_highestTrackNumberMutex = new QMutex;
    m_synchroneRenderWaitCondition = new QWaitCondition;
    m_synchroneRenderWaitConditionMutex = new QMutex;
57
58
59
60
}

MainWorkflow::~MainWorkflow()
{
61
62
    stop();

63
64
65
66
    delete m_synchroneRenderWaitConditionMutex;
    delete m_synchroneRenderWaitCondition;
    delete m_highestTrackNumberMutex;
    delete m_renderMutex;
67
68
69
70
71
    delete m_renderStartedLock;
    for (unsigned int i = 0; i < m_trackCount; ++i)
        delete m_tracks[i];
    delete[] m_tracks;
    delete[] blackOutput;
72
73
}

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
74
void        MainWorkflow::addClip( Clip* clip, unsigned int trackId, qint64 start )
75
{
76
    Q_ASSERT_X( trackId < m_trackCount, "MainWorkflow::addClip",
77
78
                "The specified trackId isn't valid, for it's higher than the number of tracks");

79
80
    m_tracks[trackId]->addClip( clip, start );
    //if the track is deactivated, we need to reactivate it.
81
    if ( m_tracks[trackId].deactivated() == true )
82
        activateTrack( trackId );
83
84

    //Now check if this clip addition has changed something about the workflow's length
85
86
    if ( m_tracks[trackId]->getLength() > m_length )
        m_length = m_tracks[trackId]->getLength();
87

88
    //Inform the GUI
89
    emit clipAdded( clip, trackId, start );
90
}
91

92
void            MainWorkflow::computeLength()
93
{
94
95
96
97
98
99
100
101
    qint64      maxLength = 0;

    for ( unsigned int i = 0; i < m_trackCount; ++i )
    {
        if ( m_tracks[i]->getLength() > maxLength )
            maxLength = m_tracks[i]->getLength();
    }
    m_length = maxLength;
102
103
}

104
105
106
void    MainWorkflow::startRender()
{
    m_renderStarted = true;
107
    m_paused = false;
108
109
110
    m_currentFrame = 0;
    emit frameChanged( 0 );
    for ( unsigned int i = 0; i < m_trackCount; ++i )
111
        activateTrack( i );
112
113
114
    computeLength();
}

115
void                MainWorkflow::getOutput()
116
{
117
    QReadLocker     lock( m_renderStartedLock );
118
    QMutexLocker    lock2( m_renderMutex );
119

120
121
122
123
124
125
    {
        QMutexLocker    lockHighestTrackNumber( m_highestTrackNumberMutex );
        m_highestTrackNumber = 0;
    }
    m_nbTracksToRender = 0;
    m_synchroneRenderingBuffer = NULL;
126
    if ( m_renderStarted == true )
127
    {
128
        for ( unsigned int i = 0; i < m_trackCount; ++i )
129
        {
130
131
            if ( m_tracks[i].activated() == false )
                continue ;
132

133
            m_nbTracksToRender.fetchAndAddAcquire( 1 );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
134
            m_tracks[i]->getOutput( m_currentFrame );
135
        }
136
137
        if ( m_paused == false )
            nextFrame();
138
    }
139
}
140

141
142
void        MainWorkflow::pause()
{
143
144
    QMutexLocker    lock( m_renderMutex );

145
    m_nbTracksToPause = 0;
146
147
148
    for ( unsigned int i = 0; i < m_trackCount; ++i )
    {
        if ( m_tracks[i].activated() == true )
149
150
        {
            m_nbTracksToPause.fetchAndAddAcquire( 1 );
151
            m_tracks[i]->pause();
152
        }
153
154
155
    }
}

156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
void        MainWorkflow::unpause()
{
    QMutexLocker    lock( m_renderMutex );

    m_nbTracksToUnpause = 0;
    for ( unsigned int i = 0; i < m_trackCount; ++i )
    {
        if ( m_tracks[i].activated() == true )
        {
            m_nbTracksToUnpause.fetchAndAddAcquire( 1 );
            m_tracks[i]->unpause();
        }
    }
}

171
172
173
174
175
176
177
178
179
180
181
182
183
184
void        MainWorkflow::nextFrame()
{
    ++m_currentFrame;
    emit frameChanged( m_currentFrame );
    emit positionChanged( (float)m_currentFrame / (float)m_length );
}

void        MainWorkflow::previousFrame()
{
    --m_currentFrame;
    emit frameChanged( m_currentFrame );
    emit positionChanged( (float)m_currentFrame / (float)m_length );
}

185
186
void        MainWorkflow::setPosition( float pos )
{
187
188
    if ( m_renderStarted == false )
        return ;
189
190
191
    //Since any track can be reactivated, we reactivate all of them, and let them
    //unable themself if required.
    for ( unsigned int i = 0; i < m_trackCount; ++i)
192
        activateTrack( i );
193

chouquette's avatar
chouquette committed
194
    qint64  frame = static_cast<qint64>( (float)m_length * pos );
195
196
    m_currentFrame = frame;
    emit frameChanged( frame );
197
//    cancelSynchronisation();
198
    //Do not emit a signal for the RenderWidget, since it's the one that triggered that call...
199
200
201
202
203
204
}

qint64      MainWorkflow::getLength() const
{
    return m_length;
}
205

206
207
208
209
210
211
212
qint64      MainWorkflow::getClipPosition( const QUuid& uuid, unsigned int trackId ) const
{
    Q_ASSERT( trackId < m_trackCount );

    return m_tracks[trackId]->getClipPosition( uuid );
}

213
void        MainWorkflow::trackEndReached( unsigned int trackId )
214
{
215
216
217
218
219
220
221
    m_tracks[trackId].deactivate();

    for ( unsigned int i = 0; i < m_trackCount; ++i)
    {
        if ( m_tracks[i].activated() == true )
            return ;
    }
222
    emit mainWorkflowEndReached();
223
224
225
    m_renderStarted = false;
    m_currentFrame = 0;
    emit frameChanged( 0 );
226
}
227
228
229
230
231

unsigned int    MainWorkflow::getTrackCount() const
{
    return m_trackCount;
}
232
233
234
235
236
237
238
239

void            MainWorkflow::stop()
{
    QWriteLocker    lock( m_renderStartedLock );

    m_renderStarted = false;
    for (unsigned int i = 0; i < m_trackCount; ++i)
    {
240
241
        if ( m_tracks[i].activated() == true )
            m_tracks[i]->stop();
242
243
244
245
    }
    m_currentFrame = 0;
    emit frameChanged( 0 );
}
246
247
248
249
250
251

MainWorkflow*   MainWorkflow::getInstance()
{
    Q_ASSERT( m_instance != NULL );
    return m_instance;
}
252

253
254
255
256
257
258
259
260
261
void            MainWorkflow::deleteInstance()
{
    if ( m_instance != NULL )
    {
        delete m_instance;
        m_instance = NULL;
    }
}

262
263
void           MainWorkflow::moveClip( const QUuid& clipUuid, unsigned int oldTrack,
                                       unsigned int newTrack, qint64 startingFrame, bool undoRedoCommand /*= false*/ )
264
{
265
    Q_ASSERT( newTrack < m_trackCount && oldTrack < m_trackCount );
266
267
268
269
270

    if ( oldTrack == newTrack )
    {
        //And now, just move the clip.
        m_tracks[newTrack]->moveClip( clipUuid, startingFrame );
271
        activateTrack( newTrack );
272
273
274
275
276
    }
    else
    {
        Clip* clip = m_tracks[oldTrack]->removeClip( clipUuid );
        m_tracks[newTrack]->addClip( clip, startingFrame );
277
278
        activateTrack( oldTrack );
        activateTrack( newTrack );
279
    }
280
    computeLength();
281
282
283
284
    if ( undoRedoCommand == true )
    {
        emit clipMoved( clipUuid, newTrack, startingFrame );
    }
285
}
286

287
288
289
290
291
Clip*       MainWorkflow::removeClip( const QUuid& uuid, unsigned int trackId )
{
    Q_ASSERT( trackId < m_trackCount );

    Clip* clip = m_tracks[trackId]->removeClip( uuid );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
292
    activateTrack( trackId );
293
294
    emit clipRemoved( uuid, trackId );
    return clip;
295
}
296

297
void        MainWorkflow::trackPaused()
298
{
299
    m_nbTracksToPause.fetchAndAddAcquire( -1 );
300
    if ( m_nbTracksToPause <= 0 )
301
    {
302
        m_paused = true;
303
        emit mainWorkflowPaused();
304
305
    }
}
306

307
void        MainWorkflow::trackUnpaused()
308
{
309
310
    m_nbTracksToUnpause.fetchAndAddAcquire( -1 );
    if ( m_nbTracksToUnpause <= 0 )
311
    {
312
        m_paused = false;
313
        emit mainWorkflowUnpaused();
314
315
    }
}
316
317
318
319
320
321
322

void        MainWorkflow::tracksRenderCompleted( unsigned int trackId )
{
    m_nbTracksToRender.fetchAndAddAcquire( -1 );

    {
        QMutexLocker    lock( m_highestTrackNumberMutex );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
323
324
325

        unsigned char* buff = m_tracks[trackId]->getSynchroneOutput();
        if ( m_highestTrackNumber <= trackId && buff != NULL )
326
327
        {
            m_highestTrackNumber = trackId;
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
328
            m_synchroneRenderingBuffer = buff;;
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
        }
    }
    //We check for minus or equal, since we can have 0 frame to compute,
    //therefore, m_nbTracksToRender will be equal to -1
    if ( m_nbTracksToRender <= 0 )
    {
        //Just a synchronisation barriere
        {
            QMutexLocker    lock( m_synchroneRenderWaitConditionMutex );
        }
        m_synchroneRenderWaitCondition->wakeAll();
    }
}

unsigned char*  MainWorkflow::getSynchroneOutput()
{
    m_synchroneRenderWaitConditionMutex->lock();
    getOutput();
347
//    qDebug() << "Waiting for sync output";
348
    m_synchroneRenderWaitCondition->wait( m_synchroneRenderWaitConditionMutex );
349
//    qDebug() << "Got it";
350
351
352
353
354
    m_synchroneRenderWaitConditionMutex->unlock();
    if ( m_synchroneRenderingBuffer == NULL )
        return MainWorkflow::blackOutput;
    return m_synchroneRenderingBuffer;
}
355
356
357
358
359
360
361
362

void        MainWorkflow::cancelSynchronisation()
{
    {
        QMutexLocker    lock( m_synchroneRenderWaitConditionMutex );
    }
    m_synchroneRenderWaitCondition->wakeAll();
}
363
364
365
366
367
368
369
370
371
372

void        MainWorkflow::muteTrack( unsigned int trackId )
{
    m_tracks[trackId].setHardDeactivation( true );
}

void        MainWorkflow::unmuteTrack( unsigned int trackId )
{
    m_tracks[trackId].setHardDeactivation( false );
}
373
374
375
376
377
378

void        MainWorkflow::setCurrentFrame( qint64 currentFrame )
{
    m_currentFrame = currentFrame;
    emit positionChanged( (float)m_currentFrame / (float)m_length );
}
379

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
380
381
382
383
384
385
386
void        MainWorkflow::activateTrack( unsigned int trackId )
{
    if ( m_tracks[trackId]->getLength() > 0 )
        m_tracks[trackId].activate();
    else
        m_tracks[trackId].deactivate();
}
387
388
389
390
391
392
393

Clip*       MainWorkflow::getClip( const QUuid& uuid, unsigned int trackId )
{
    Q_ASSERT( trackId < m_trackCount );

    return m_tracks[trackId]->getClip( uuid );
}
394
395
396

void        MainWorkflow::loadProject( const QDomElement& project )
{
397
398
399
400
401
402
    if ( project.isNull() == true || project.tagName() != "timeline" )
    {
        qWarning() << "Invalid timeline node (" << project.tagName() << ')';
        return ;
    }

403
404
    clear();

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
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
    QDomElement elem = project.firstChild().toElement();

    while ( elem.isNull() == false )
    {
        bool    ok;

        Q_ASSERT( elem.tagName() == "track" );
        unsigned int trackId = elem.attribute( "id" ).toUInt( &ok );
        if ( ok == false )
        {
            qWarning() << "Invalid track number in project file";
            return ;
        }
        QDomElement clip = elem.firstChild().toElement();
        while ( clip.isNull() == false )
        {
            //Iterate over clip fields:
            QDomElement clipProperty = clip.firstChild().toElement();
            QUuid       parent;
            float       begin;
            float       end;
            qint64      startPos;

            while ( clipProperty.isNull() == false )
            {
                QString tagName = clipProperty.tagName();
                bool    ok;

                if ( tagName == "parent" )
                    parent = QUuid( clipProperty.text() );
                else if ( tagName == "begin" )
                {
                    begin = clipProperty.text().toFloat( &ok );
                    if ( ok == false )
                    {
                        qWarning() << "Invalid clip begin";
                        return ;
                    }
                }
                else if ( tagName == "end" )
                {
                    end = clipProperty.text().toFloat( &ok );
                    if ( ok == false )
                    {
                        qWarning() << "Invalid clip end";
                        return ;
                    }
                }
                else if ( tagName == "startFrame" )
                {
                    startPos = clipProperty.text().toLongLong( &ok );
                    if ( ok == false )
                    {
                        qWarning() << "Invalid clip starting frame";
                        return ;
                    }
                }
                else
                    qDebug() << "Unknown field" << clipProperty.tagName();

                clipProperty = clipProperty.nextSibling().toElement();
            }

            Clip*       c = new Clip( parent, begin, end );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
469
            addClip( c, trackId, startPos );
470
471
472
473
474

            clip = clip.nextSibling().toElement();
        }
        elem = elem.nextSibling().toElement();
    }
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
}

void        MainWorkflow::saveProject( QDomDocument& doc )
{
    QDomElement project = doc.createElement( "timeline" );
    for ( unsigned int i = 0; i < m_trackCount; ++i )
    {
        if ( m_tracks[i]->getLength() > 0 )
        {
            QDomElement     trackNode = doc.createElement( "track" );

            trackNode.setAttribute( "id", i );

            m_tracks[i]->save( doc, trackNode );
            project.appendChild( trackNode );
        }
    }
    doc.appendChild( project );
}
494
495
496
497
498
499
500
501
502

void        MainWorkflow::clear()
{
    for ( unsigned int i = 0; i < m_trackCount; ++i )
    {
        m_tracks[i]->clear();
    }
    m_length = 0;
}