MainWorkflow.cpp 10.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 44 45 46 47

    m_tracks = new Toggleable<TrackWorkflow*>[trackCount];
    for (int i = 0; i < trackCount; ++i)
    {
        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
}

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 81
    //if the track is deactivated, we need to reactivate it :
    if ( m_tracks[trackId].deactivated() == true )
        m_tracks[trackId].activate();
82
    m_tracks[trackId]->addClip( clip, start );
83 84
    if ( m_tracks[trackId]->getLength() > m_length )
        m_length = m_tracks[trackId]->getLength();
85
}
86

87
void            MainWorkflow::computeLength()
88
{
89 90 91 92 93 94 95 96
    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;
97 98
}

99 100 101 102 103 104 105 106 107 108
void    MainWorkflow::startRender()
{
    m_renderStarted = true;
    m_currentFrame = 0;
    emit frameChanged( 0 );
    for ( unsigned int i = 0; i < m_trackCount; ++i )
        m_tracks[i].activate();
    computeLength();
}

109
void                MainWorkflow::getOutput()
110
{
111
    QReadLocker     lock( m_renderStartedLock );
112
    QMutexLocker    lock2( m_renderMutex );
113

114 115 116 117 118 119
    {
        QMutexLocker    lockHighestTrackNumber( m_highestTrackNumberMutex );
        m_highestTrackNumber = 0;
    }
    m_nbTracksToRender = 0;
    m_synchroneRenderingBuffer = NULL;
120
    if ( m_renderStarted == true )
121
    {
122
        for ( unsigned int i = 0; i < m_trackCount; ++i )
123
        {
124 125
            if ( m_tracks[i].activated() == false )
                continue ;
126

127
            m_nbTracksToRender.fetchAndAddAcquire( 1 );
128
            if ( m_tracks[i]->getOutput( m_currentFrame ) != false )
129
            {
130
                break ;
131
            }
132
        }
133 134
        if ( m_paused == false )
            nextFrame();
135
    }
136
}
137

138 139
void        MainWorkflow::pause()
{
140 141
    QMutexLocker    lock( m_renderMutex );

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

153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
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();
        }
    }
}

168 169
void        MainWorkflow::nextFrame()
{
170
//    qDebug() << "Going to the next frame";
171
    ++m_currentFrame;
172
    //FIXME: This is probably a bit much...
173 174 175 176 177 178
    emit frameChanged( m_currentFrame );
    emit positionChanged( (float)m_currentFrame / (float)m_length );
}

void        MainWorkflow::previousFrame()
{
179
//    qDebug() << "Going to the previous frame";
180
    --m_currentFrame;
181
    //FIXME: This is probably a bit much...
182 183 184 185
    emit frameChanged( m_currentFrame );
    emit positionChanged( (float)m_currentFrame / (float)m_length );
}

186 187
void        MainWorkflow::setPosition( float pos )
{
188 189
    if ( m_renderStarted == false )
        return ;
190 191 192 193 194
    //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)
        m_tracks[i].activate();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Clip* clip = m_tracks[trackId]->removeClip( uuid );
    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 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350

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

    {
        QMutexLocker    lock( m_highestTrackNumberMutex );
        if ( m_highestTrackNumber <= trackId )
        {
            m_highestTrackNumber = trackId;
            m_synchroneRenderingBuffer = m_tracks[trackId]->getSynchroneOutput();
        }
    }
    //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();
    m_synchroneRenderWaitCondition->wait( m_synchroneRenderWaitConditionMutex );
    m_synchroneRenderWaitConditionMutex->unlock();
    if ( m_synchroneRenderingBuffer == NULL )
        return MainWorkflow::blackOutput;
    return m_synchroneRenderingBuffer;
}
351 352 353 354 355 356 357 358

void        MainWorkflow::cancelSynchronisation()
{
    {
        QMutexLocker    lock( m_synchroneRenderWaitConditionMutex );
    }
    m_synchroneRenderWaitCondition->wakeAll();
}