MainWorkflow.cpp 10.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 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
    muteTrack( 0 );
53
    m_renderStartedLock = new QReadWriteLock;
54
    m_renderMutex = new QMutex;
55 56 57
    m_highestTrackNumberMutex = new QMutex;
    m_synchroneRenderWaitCondition = new QWaitCondition;
    m_synchroneRenderWaitConditionMutex = new QMutex;
58 59 60 61
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

188 189
void        MainWorkflow::setPosition( float pos )
{
190 191
    if ( m_renderStarted == false )
        return ;
192 193 194 195 196
    //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
197
    qint64  frame = static_cast<qint64>( (float)m_length * pos );
198 199
    m_currentFrame = frame;
    emit frameChanged( frame );
200
//    cancelSynchronisation();
201
    //Do not emit a signal for the RenderWidget, since it's the one that triggered that call...
202 203 204 205 206 207
}

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

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

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

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

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

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

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

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

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

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

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

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

290 291 292 293 294 295 296
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;
297
}
298

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

309
void        MainWorkflow::trackUnpaused()
310
{
311 312
    m_nbTracksToUnpause.fetchAndAddAcquire( -1 );
    if ( m_nbTracksToUnpause <= 0 )
313
    {
314
        m_paused = false;
315
        emit mainWorkflowUnpaused();
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 351 352

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;
}
353 354 355 356 357 358 359 360

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

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

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