MainWorkflow.cpp 10.6 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 103 104 105 106 107 108 109
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();
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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