MainWorkflow.cpp 14 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_currentFrame( 0 ),
40
        m_lengthFrame( 0 ),
41
        m_renderStarted( false )
42
{
43
    m_currentFrameLock = new QReadWriteLock;
44
    m_renderStartedLock = new QReadWriteLock;
45
    m_renderMutex = new QMutex;
46 47
    m_synchroneRenderWaitCondition = new QWaitCondition;
    m_synchroneRenderWaitConditionMutex = new QMutex;
48
    m_pauseWaitCond = new WaitCondition;
49 50 51 52

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

53 54
    m_tracks = new TrackHandler*[MainWorkflow::NbTrackType];
    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 63
    }
    m_outputBuffers = new OutputBuffers;
64 65 66

    blackOutput = new LightVideoFrame( VIDEOHEIGHT * VIDEOWIDTH * Pixel::NbComposantes );
    memset( (*blackOutput)->frame.octets, 0, (*blackOutput)->nboctets );
67 68 69 70
}

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

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

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
86
EffectsEngine*          MainWorkflow::getEffectsEngine()
87
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
88
    return m_effectEngine;
89
}
90

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

100
void            MainWorkflow::computeLength()
101
{
102 103
    qint64      maxLength = 0;

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

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

121
void                    MainWorkflow::getOutput()
122
{
123 124
    QReadLocker         lock( m_renderStartedLock );
    QMutexLocker        lock2( m_renderMutex );
125

126
    if ( m_renderStarted == true )
127
    {
128 129 130 131 132 133
        {
            QReadLocker         lock3( m_currentFrameLock );

            for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
                m_tracks[i]->getOutput( m_currentFrame );
        }
134 135
        if ( m_paused == false )
            nextFrame();
136
    }
137
}
138

139 140
void        MainWorkflow::pause()
{
141 142 143 144
    //Just wait for the current render to finish
    m_renderMutex->lock();
    QMutexLocker    lock( m_pauseWaitCond->getMutex() );
    m_renderMutex->unlock();
145

146
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
147
        m_tracks[i]->pause();
148
    m_pauseWaitCond->waitLocked();
149 150
}

151 152 153 154
void        MainWorkflow::unpause()
{
    QMutexLocker    lock( m_renderMutex );

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

159 160
void        MainWorkflow::nextFrame()
{
161
    QWriteLocker    lock( m_currentFrameLock );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
162

163
    ++m_currentFrame;
164
    emit frameChanged( m_currentFrame, Renderer );
165 166 167 168
}

void        MainWorkflow::previousFrame()
{
169 170
    QWriteLocker    lock( m_currentFrameLock );

171
    --m_currentFrame;
172
    emit frameChanged( m_currentFrame, Renderer );
173 174
}

175
qint64      MainWorkflow::getLengthFrame() const
176
{
177
    return m_lengthFrame;
178
}
179

180
qint64      MainWorkflow::getClipPosition( const QUuid& uuid, unsigned int trackId, MainWorkflow::TrackType trackType ) const
181
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
182
    return m_tracks[trackType]->getClipPosition( uuid, trackId );
183
}
184 185 186 187

void            MainWorkflow::stop()
{
    QWriteLocker    lock( m_renderStartedLock );
188
    QWriteLocker    lock2( m_currentFrameLock );
189 190

    m_renderStarted = false;
191
    for (unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i)
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
192
        m_tracks[i]->stop();
193
    m_currentFrame = 0;
194
    emit frameChanged( 0, Renderer );
195
}
196

197
void           MainWorkflow::moveClip( const QUuid& clipUuid, unsigned int oldTrack,
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
198
                                       unsigned int newTrack, qint64 startingFrame,
199
                                       MainWorkflow::TrackType trackType, bool undoRedoCommand /*= false*/ )
200
{
201
    m_tracks[trackType]->moveClip( clipUuid, oldTrack, newTrack, startingFrame );
202
    computeLength();
203

204 205 206 207
    if ( undoRedoCommand == true )
    {
        emit clipMoved( clipUuid, newTrack, startingFrame, trackType );
    }
208
}
209

210
Clip*       MainWorkflow::removeClip( const QUuid& uuid, unsigned int trackId, MainWorkflow::TrackType trackType )
211
{
212 213
    Clip* clip = m_tracks[trackType]->removeClip( uuid, trackId );
    computeLength();
214
    emit clipRemoved( clip, trackId, trackType );
215
    return clip;
216 217
}

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
218
MainWorkflow::OutputBuffers*  MainWorkflow::getSynchroneOutput()
219 220 221
{
    m_synchroneRenderWaitConditionMutex->lock();
    getOutput();
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
222
//    qDebug() << "Waiting for sync output";
223
    m_synchroneRenderWaitCondition->wait( m_synchroneRenderWaitConditionMutex );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
224
//    qDebug() << "Got it";
225
    m_effectEngine->render();
226 227 228 229 230
    if ( m_effectEngine->getOutputFrame( 0 )->nboctets == 0 )
        m_outputBuffers->video = MainWorkflow::blackOutput;
    else
        m_outputBuffers->video = &( m_effectEngine->getOutputFrame( 0 ) );

231
    m_synchroneRenderWaitConditionMutex->unlock();
232 233

    //    m_outputBuffers->video = reinterpret_cast<LightVideoFrame*>( m_tracks[TrackWorkflow::Video]->getSynchroneOutput() );
234
//    m_outputBuffers->audio = reinterpret_cast<unsigned char*>( m_tracks[TrackWorkflow::Audio]->getSynchroneOutput() );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
235
    return m_outputBuffers;
236
}
237 238 239 240 241 242 243 244

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

246
void        MainWorkflow::muteTrack( unsigned int trackId, MainWorkflow::TrackType trackType )
247
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
248
    m_tracks[trackType]->muteTrack( trackId );
249 250
}

251
void        MainWorkflow::unmuteTrack( unsigned int trackId, MainWorkflow::TrackType trackType )
252
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
253
    m_tracks[trackType]->unmuteTrack( trackId );
254
}
255

256
void        MainWorkflow::setCurrentFrame( qint64 currentFrame, MainWorkflow::FrameChangedReason reason )
257
{
258 259
    QWriteLocker    lock( m_currentFrameLock );

260 261 262 263 264 265 266
    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();
    }
267
    m_currentFrame = currentFrame;
268
    emit frameChanged( m_currentFrame, reason );
269
}
270

271
Clip*       MainWorkflow::getClip( const QUuid& uuid, unsigned int trackId, MainWorkflow::TrackType trackType )
272
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
273
    return m_tracks[trackType]->getClip( uuid, trackId );
274
}
275

276 277 278
/**
 *  \warning    The mainworkflow is expected to be cleared already by the ProjectManager
 */
279 280
void        MainWorkflow::loadProject( const QDomElement& project )
{
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
    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;
309
            MainWorkflow::TrackType     trackType = MainWorkflow::VideoTrack;
310 311 312 313 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

            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" )
                {
347
                    trackType = static_cast<MainWorkflow::TrackType>( clipProperty.text().toUInt( &ok ) );
348 349 350 351 352 353 354 355 356 357 358 359
                    if ( ok == false )
                    {
                        qWarning() << "Invalid track type starting frame";
                        return ;
                    }
                }
                else
                    qDebug() << "Unknown field" << clipProperty.tagName();

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

360 361 362 363 364
            if ( Library::getInstance()->getMedia( parent ) != NULL )
            {
                Clip*       c = new Clip( parent, begin, end );
                addClip( c, trackId, startPos, trackType );
            }
365 366 367 368 369

            clip = clip.nextSibling().toElement();
        }
        elem = elem.nextSibling().toElement();
    }
370 371
}

372
void        MainWorkflow::saveProject( QDomDocument& doc, QDomElement& rootNode )
373
{
374
    QDomElement project = doc.createElement( "timeline" );
375
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
376 377 378 379
    {
        m_tracks[i]->save( doc, project );
    }
    rootNode.appendChild( project );
380
}
381 382 383

void        MainWorkflow::clear()
{
384
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
385
        m_tracks[i]->clear();
386
    emit cleared();
387
}
388 389 390

void        MainWorkflow::setFullSpeedRender( bool value )
{
391
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
392 393
        m_tracks[i]->setFullSpeedRender( value );
}
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
394 395 396

void        MainWorkflow::tracksPaused()
{
397
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
398 399
        if ( m_tracks[i]->isPaused() == false )
            return ;
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
400
    m_paused = true;
401 402 403 404
    {
        QMutexLocker    lock( m_pauseWaitCond->getMutex() );
        m_pauseWaitCond->wake();
    }
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
405 406 407
    emit mainWorkflowPaused();
}

408 409 410 411 412 413 414 415
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
416 417
void        MainWorkflow::tracksUnpaused()
{
418
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
419 420 421 422 423 424
        if ( m_tracks[i]->isPaused() == true )
            return ;
    m_paused = false;
    emit mainWorkflowUnpaused();
}

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
425 426
void        MainWorkflow::tracksRenderCompleted()
{
427
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
428 429 430 431 432 433 434
        if ( m_tracks[i]->allTracksRendered() == false )
            return ;
    {
        QMutexLocker    lock( m_synchroneRenderWaitConditionMutex );
    }
    m_synchroneRenderWaitCondition->wakeAll();
}
435

436
int         MainWorkflow::getTrackCount( MainWorkflow::TrackType trackType ) const
437 438 439
{
    return m_tracks[trackType]->getTrackCount();
}
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
440

441 442
qint64      MainWorkflow::getCurrentFrame() const
{
443 444
    QReadLocker     lock( m_currentFrameLock );

445 446
    return m_currentFrame;
}