MainWorkflow.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 25 26
/*****************************************************************************
 * 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"
27 28
#include "TrackWorkflow.h"
#include "TrackHandler.h"
29
#include "Library.h"
30

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
31 32 33 34 35
//JUST FOR THE DEFINES !
//TODO:
//FIXME: remove this !
#include "ClipWorkflow.h"

36 37
LightVideoFrame*     MainWorkflow::blackOutput = NULL;

38
MainWorkflow::MainWorkflow( int trackCount ) :
39
        m_lengthFrame( 0 ),
40
        m_renderStarted( false )
41
{
42
    m_currentFrameLock = new QReadWriteLock;
43
    m_renderStartedLock = new QReadWriteLock;
44
    m_renderMutex = new QMutex;
45 46
    m_synchroneRenderWaitCondition = new QWaitCondition;
    m_synchroneRenderWaitConditionMutex = new QMutex;
47
    m_pauseWaitCond = new WaitCondition;
48 49 50 51

    m_effectEngine = new EffectsEngine;
    m_effectEngine->disable();

52
    m_tracks = new TrackHandler*[MainWorkflow::NbTrackType];
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
53
    m_currentFrame = new qint64[MainWorkflow::NbTrackType];
54
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
55
    {
56
        MainWorkflow::TrackType trackType = (i == 0 ? MainWorkflow::VideoTrack : MainWorkflow::AudioTrack );
57
        m_tracks[i] = new TrackHandler( trackCount, trackType, m_effectEngine );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
58
        connect( m_tracks[i], SIGNAL( tracksPaused() ), this, SLOT( tracksPaused() ) );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
59
        connect( m_tracks[i], SIGNAL( tracksUnpaused() ), this, SLOT( tracksUnpaused() ) );
60
        connect( m_tracks[i], SIGNAL( allTracksRenderCompleted() ), this, SLOT( tracksRenderCompleted() ) );
61
        connect( m_tracks[i], SIGNAL( tracksEndReached() ), this, SLOT( tracksEndReached() ) );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
62
	m_currentFrame[i] = 0;
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
63 64
    }
    m_outputBuffers = new OutputBuffers;
65 66
    blackOutput = new LightVideoFrame( VIDEOHEIGHT * VIDEOWIDTH * Pixel::NbComposantes );
    memset( (*blackOutput)->frame.octets, 0, (*blackOutput)->nboctets );
67
    m_nbTrackHandlerToRenderMutex = new QMutex;
68 69 70 71
}

MainWorkflow::~MainWorkflow()
{
72
    //FIXME: this is probably useless, since already done by the renderer
73 74
    stop();

75
    delete m_nbTrackHandlerToRenderMutex;
76
    delete m_pauseWaitCond;
77
    delete m_effectEngine;
78 79 80
    delete m_synchroneRenderWaitConditionMutex;
    delete m_synchroneRenderWaitCondition;
    delete m_renderMutex;
81
    delete m_renderStartedLock;
82
    delete m_currentFrameLock;
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
83
    delete m_currentFrame;
84
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
85
        delete m_tracks[i];
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
86
    delete[] m_tracks;
87 88
}

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
89
EffectsEngine*          MainWorkflow::getEffectsEngine()
90
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
91
    return m_effectEngine;
92
}
93

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
94
void            MainWorkflow::addClip( Clip* clip, unsigned int trackId,
95
                                        qint64 start, MainWorkflow::TrackType trackType )
96
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
97
    m_tracks[trackType]->addClip( clip, trackId, start );
98
    computeLength();
99
    //Inform the GUI
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
100
    emit clipAdded( clip, trackId, start, trackType );
101
}
102

103
void            MainWorkflow::computeLength()
104
{
105 106
    qint64      maxLength = 0;

107
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
108 109 110 111
    {
        if ( m_tracks[i]->getLength() > maxLength )
            maxLength = m_tracks[i]->getLength();
    }
112
    m_lengthFrame = maxLength;
113 114
}

115 116 117
void    MainWorkflow::startRender()
{
    m_renderStarted = true;
118
    m_paused = false;
119
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
120
        m_tracks[i]->startRender();
121 122 123
    computeLength();
}

124
void                    MainWorkflow::getOutput( TrackType trackType )
125
{
126 127
    QReadLocker         lock( m_renderStartedLock );
    QMutexLocker        lock2( m_renderMutex );
128

129
    if ( m_renderStarted == true )
130
    {
131 132
        QReadLocker         lock3( m_currentFrameLock );
        QMutexLocker        lock4( m_nbTrackHandlerToRenderMutex );
133

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
134
	//This has probably no reason for existing... and therefore shouldn't
135 136 137
        if ( trackType == BothTrackType )
        {
            m_nbTrackHandlerToRender = MainWorkflow::NbTrackType;
138
            for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
139
                m_tracks[i]->getOutput( m_currentFrame[i] );
140
        }
141 142 143
        else
        {
            m_nbTrackHandlerToRender = 1;
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
144
            m_tracks[trackType]->getOutput( m_currentFrame[trackType] );
145
        }
146
    }
147
}
148

149 150
void        MainWorkflow::pause()
{
151 152 153 154
    //Just wait for the current render to finish
    m_renderMutex->lock();
    QMutexLocker    lock( m_pauseWaitCond->getMutex() );
    m_renderMutex->unlock();
155

156
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
157
        m_tracks[i]->pause();
158
    m_pauseWaitCond->waitLocked();
159 160
}

161 162 163 164
void        MainWorkflow::unpause()
{
    QMutexLocker    lock( m_renderMutex );

165
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
166
        m_tracks[i]->unpause();
167 168
}

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
169
void        MainWorkflow::goToNextFrame( MainWorkflow::TrackType trackType )
170 171
{
    if ( m_paused == false )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
172
        nextFrame( trackType );
173 174
}

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
175
void        MainWorkflow::nextFrame( MainWorkflow::TrackType trackType )
176
{
177
    QWriteLocker    lock( m_currentFrameLock );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
178

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
179 180 181
    ++m_currentFrame[trackType];
    if ( trackType == MainWorkflow::VideoTrack )
        emit frameChanged( m_currentFrame[MainWorkflow::VideoTrack], Renderer );
182 183
}

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
184
void        MainWorkflow::previousFrame( MainWorkflow::TrackType trackType )
185
{
186
    QWriteLocker    lock( m_currentFrameLock );
187

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
188 189 190
    --m_currentFrame[trackType];
    if ( trackType == MainWorkflow::VideoTrack )
        emit frameChanged( m_currentFrame[MainWorkflow::VideoTrack], Renderer );
191 192
}

193
qint64      MainWorkflow::getLengthFrame() const
194
{
195
    return m_lengthFrame;
196
}
197

198
qint64      MainWorkflow::getClipPosition( const QUuid& uuid, unsigned int trackId, MainWorkflow::TrackType trackType ) const
199
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
200
    return m_tracks[trackType]->getClipPosition( uuid, trackId );
201
}
202 203 204 205

void            MainWorkflow::stop()
{
    QWriteLocker    lock( m_renderStartedLock );
206
    QWriteLocker    lock2( m_currentFrameLock );
207 208

    m_renderStarted = false;
209
    for (unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i)
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
210
    {
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
211
        m_tracks[i]->stop();
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
212 213
        m_currentFrame[i] = 0;
    }
214
    emit frameChanged( 0, Renderer );
215
}
216

217
void           MainWorkflow::moveClip( const QUuid& clipUuid, unsigned int oldTrack,
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
218
                                       unsigned int newTrack, qint64 startingFrame,
219
                                       MainWorkflow::TrackType trackType, bool undoRedoCommand /*= false*/ )
220
{
221
    m_tracks[trackType]->moveClip( clipUuid, oldTrack, newTrack, startingFrame );
222
    computeLength();
223

224 225 226 227
    if ( undoRedoCommand == true )
    {
        emit clipMoved( clipUuid, newTrack, startingFrame, trackType );
    }
228
}
229

230
Clip*       MainWorkflow::removeClip( const QUuid& uuid, unsigned int trackId, MainWorkflow::TrackType trackType )
231
{
232 233
    Clip* clip = m_tracks[trackType]->removeClip( uuid, trackId );
    computeLength();
234
    emit clipRemoved( clip, trackId, trackType );
235
    return clip;
236 237
}

238
MainWorkflow::OutputBuffers*  MainWorkflow::getSynchroneOutput( MainWorkflow::TrackType trackType )
239 240
{
    m_synchroneRenderWaitConditionMutex->lock();
241
    getOutput( trackType );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
242
//    qDebug() << "Waiting for sync output";
243
    m_synchroneRenderWaitCondition->wait( m_synchroneRenderWaitConditionMutex );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
244
//    qDebug() << "Got it";
245 246 247
    if ( trackType == BothTrackType || trackType == VideoTrack )
    {
        m_effectEngine->render();
248 249 250 251
        if ( m_effectEngine->getOutputFrame( 0 )->nboctets == 0 )
            m_outputBuffers->video = MainWorkflow::blackOutput;
        else
            m_outputBuffers->video = &( m_effectEngine->getOutputFrame( 0 ) );
252 253
    }
    if ( trackType == BothTrackType || trackType == AudioTrack )
254
    {
255
        m_outputBuffers->audio = m_tracks[MainWorkflow::AudioTrack]->getTmpAudioBuffer();
256
    }
257
    m_synchroneRenderWaitConditionMutex->unlock();
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
258
    return m_outputBuffers;
259
}
260 261 262 263 264 265 266 267

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

269
void        MainWorkflow::muteTrack( unsigned int trackId, MainWorkflow::TrackType trackType )
270
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
271
    m_tracks[trackType]->muteTrack( trackId );
272 273
}

274
void        MainWorkflow::unmuteTrack( unsigned int trackId, MainWorkflow::TrackType trackType )
275
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
276
    m_tracks[trackType]->unmuteTrack( trackId );
277
}
278

279
void        MainWorkflow::setCurrentFrame( qint64 currentFrame, MainWorkflow::FrameChangedReason reason )
280
{
281 282
    QWriteLocker    lock( m_currentFrameLock );

283 284 285 286 287 288 289
    if ( m_renderStarted == true )
    {
        //Since any track can be reactivated, we reactivate all of them, and let them
        //unable themself if required.
        for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i)
            m_tracks[i]->activateAll();
    }
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
290 291 292
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i)
        m_currentFrame[i] = currentFrame;
    emit frameChanged( currentFrame, reason );
293
}
294

295
Clip*       MainWorkflow::getClip( const QUuid& uuid, unsigned int trackId, MainWorkflow::TrackType trackType )
296
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
297
    return m_tracks[trackType]->getClip( uuid, trackId );
298
}
299

300 301 302
/**
 *  \warning    The mainworkflow is expected to be cleared already by the ProjectManager
 */
303 304
void        MainWorkflow::loadProject( const QDomElement& project )
{
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
    if ( project.isNull() == true || project.tagName() != "timeline" )
    {
        qWarning() << "Invalid timeline node (" << project.tagName() << ')';
        return ;
    }

    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;
            qint64                      begin;
            qint64                      end;
            qint64                      startPos;
333
            MainWorkflow::TrackType     trackType = MainWorkflow::VideoTrack;
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370

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

                if ( tagName == "parent" )
                    parent = QUuid( clipProperty.text() );
                else if ( tagName == "begin" )
                {
                    begin = clipProperty.text().toLongLong( &ok );
                    if ( ok == false )
                    {
                        qWarning() << "Invalid clip begin";
                        return ;
                    }
                }
                else if ( tagName == "end" )
                {
                    end = clipProperty.text().toLongLong( &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 if ( tagName == "trackType" )
                {
371
                    trackType = static_cast<MainWorkflow::TrackType>( clipProperty.text().toUInt( &ok ) );
372 373 374 375 376 377 378 379 380 381 382 383
                    if ( ok == false )
                    {
                        qWarning() << "Invalid track type starting frame";
                        return ;
                    }
                }
                else
                    qDebug() << "Unknown field" << clipProperty.tagName();

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

384 385 386 387 388
            if ( Library::getInstance()->getMedia( parent ) != NULL )
            {
                Clip*       c = new Clip( parent, begin, end );
                addClip( c, trackId, startPos, trackType );
            }
389 390 391 392 393

            clip = clip.nextSibling().toElement();
        }
        elem = elem.nextSibling().toElement();
    }
394 395
}

396
void        MainWorkflow::saveProject( QDomDocument& doc, QDomElement& rootNode )
397
{
398
    QDomElement project = doc.createElement( "timeline" );
399
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
400 401 402 403
    {
        m_tracks[i]->save( doc, project );
    }
    rootNode.appendChild( project );
404
}
405 406 407

void        MainWorkflow::clear()
{
408
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
409
        m_tracks[i]->clear();
410
    emit cleared();
411
}
412 413 414

void        MainWorkflow::setFullSpeedRender( bool value )
{
415
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
416 417
        m_tracks[i]->setFullSpeedRender( value );
}
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
418 419 420

void        MainWorkflow::tracksPaused()
{
421
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
422 423
        if ( m_tracks[i]->isPaused() == false )
            return ;
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
424
    m_paused = true;
425 426 427 428
    {
        QMutexLocker    lock( m_pauseWaitCond->getMutex() );
        m_pauseWaitCond->wake();
    }
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
429 430 431
    emit mainWorkflowPaused();
}

432 433 434 435 436 437 438 439
void        MainWorkflow::tracksEndReached()
{
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
        if ( m_tracks[i]->endIsReached() == false )
            return ;
    emit mainWorkflowEndReached();
}

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
440 441
void        MainWorkflow::tracksUnpaused()
{
442
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
443 444 445 446 447 448
        if ( m_tracks[i]->isPaused() == true )
            return ;
    m_paused = false;
    emit mainWorkflowUnpaused();
}

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
449 450
void        MainWorkflow::tracksRenderCompleted()
{
451 452 453 454 455
    {
        QMutexLocker    lock( m_nbTrackHandlerToRenderMutex );

        --m_nbTrackHandlerToRender;
        if ( m_nbTrackHandlerToRender > 0 )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
456
            return ;
457
    }
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
458 459 460 461 462
    {
        QMutexLocker    lock( m_synchroneRenderWaitConditionMutex );
    }
    m_synchroneRenderWaitCondition->wakeAll();
}
463

464
int         MainWorkflow::getTrackCount( MainWorkflow::TrackType trackType ) const
465 466 467
{
    return m_tracks[trackType]->getTrackCount();
}
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
468

469 470
qint64      MainWorkflow::getCurrentFrame() const
{
471 472
    QReadLocker     lock( m_currentFrameLock );

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
473
    return m_currentFrame[MainWorkflow::VideoTrack];
474
}