MainWorkflow.cpp 15.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
/*****************************************************************************
 * 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
#include "SettingsManager.h"
31

32
LightVideoFrame*    MainWorkflow::blackOutput = NULL;
33

34
MainWorkflow::MainWorkflow( int trackCount ) :
35
        m_lengthFrame( 0 ),
36
        m_renderStarted( false )
37
{
38
    m_currentFrameLock = new QReadWriteLock;
39
    m_renderStartedLock = new QReadWriteLock;
40
    m_renderMutex = new QMutex;
41 42
    m_synchroneRenderWaitCondition = new QWaitCondition;
    m_synchroneRenderWaitConditionMutex = new QMutex;
43
    m_pauseWaitCond = new WaitCondition;
44

45 46 47 48 49 50 51
    const SettingValue* width = SettingsManager::getInstance()->getValue( "project", "VideoProjectWidth" );
    connect( width, SIGNAL( changed( QVariant ) ), this, SLOT( widthChanged( QVariant ) ) );
    m_width = width->get().toUInt();
    const SettingValue* height = SettingsManager::getInstance()->getValue( "project", "VideoProjectHeight" );
    connect( height, SIGNAL( changed( QVariant ) ), this, SLOT( widthChanged( QVariant ) ) );
    m_height = height->get().toUInt();

52 53 54
    m_effectEngine = new EffectsEngine;
    m_effectEngine->disable();

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

    blackOutput = new LightVideoFrame( m_width * m_height * Pixel::NbComposantes );
    memset( (*blackOutput)->frame.octets, 0, (*blackOutput)->nboctets );
72 73 74 75
}

MainWorkflow::~MainWorkflow()
{
76
    //FIXME: this is probably useless, since already done by the renderer
77 78
    stop();

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

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
93
EffectsEngine*          MainWorkflow::getEffectsEngine()
94
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
95
    return m_effectEngine;
96
}
97

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

107
void            MainWorkflow::computeLength()
108
{
109 110
    qint64      maxLength = 0;

111
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
112 113 114 115
    {
        if ( m_tracks[i]->getLength() > maxLength )
            maxLength = m_tracks[i]->getLength();
    }
116
    m_lengthFrame = maxLength;
117 118
}

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

128
void                    MainWorkflow::getOutput( TrackType trackType )
129
{
130 131
    QReadLocker         lock( m_renderStartedLock );
    QMutexLocker        lock2( m_renderMutex );
132

133
    if ( m_renderStarted == true )
134
    {
135 136
        QReadLocker         lock3( m_currentFrameLock );
        QMutexLocker        lock4( m_nbTrackHandlerToRenderMutex );
137

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

153 154
void        MainWorkflow::pause()
{
155 156 157 158
    //Just wait for the current render to finish
    m_renderMutex->lock();
    QMutexLocker    lock( m_pauseWaitCond->getMutex() );
    m_renderMutex->unlock();
159

160
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
161
        m_tracks[i]->pause();
162
    m_pauseWaitCond->waitLocked();
163 164
}

165 166 167 168
void        MainWorkflow::unpause()
{
    QMutexLocker    lock( m_renderMutex );

169
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
170
        m_tracks[i]->unpause();
171 172
}

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
173
void        MainWorkflow::goToNextFrame( MainWorkflow::TrackType trackType )
174 175
{
    if ( m_paused == false )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
176
        nextFrame( trackType );
177 178
}

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
179
void        MainWorkflow::nextFrame( MainWorkflow::TrackType trackType )
180
{
181
    QWriteLocker    lock( m_currentFrameLock );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
182

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
183 184 185
    ++m_currentFrame[trackType];
    if ( trackType == MainWorkflow::VideoTrack )
        emit frameChanged( m_currentFrame[MainWorkflow::VideoTrack], Renderer );
186 187
}

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
188
void        MainWorkflow::previousFrame( MainWorkflow::TrackType trackType )
189
{
190
    QWriteLocker    lock( m_currentFrameLock );
191

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
192 193 194
    --m_currentFrame[trackType];
    if ( trackType == MainWorkflow::VideoTrack )
        emit frameChanged( m_currentFrame[MainWorkflow::VideoTrack], Renderer );
195 196
}

197
qint64      MainWorkflow::getLengthFrame() const
198
{
199
    return m_lengthFrame;
200
}
201

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

void            MainWorkflow::stop()
{
    QWriteLocker    lock( m_renderStartedLock );
210
    QWriteLocker    lock2( m_currentFrameLock );
211 212

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

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

228 229 230 231
    if ( undoRedoCommand == true )
    {
        emit clipMoved( clipUuid, newTrack, startingFrame, trackType );
    }
232
}
233

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

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

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

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

278
void        MainWorkflow::unmuteTrack( unsigned int trackId, MainWorkflow::TrackType trackType )
279
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
280
    m_tracks[trackType]->unmuteTrack( trackId );
281
}
282

283
void        MainWorkflow::setCurrentFrame( qint64 currentFrame, MainWorkflow::FrameChangedReason reason )
284
{
285 286
    QWriteLocker    lock( m_currentFrameLock );

287 288 289 290 291 292 293
    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
294 295 296
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i)
        m_currentFrame[i] = currentFrame;
    emit frameChanged( currentFrame, reason );
297
}
298

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

304 305 306
/**
 *  \warning    The mainworkflow is expected to be cleared already by the ProjectManager
 */
307 308
void        MainWorkflow::loadProject( const QDomElement& project )
{
309 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
    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;
337
            MainWorkflow::TrackType     trackType = MainWorkflow::VideoTrack;
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 371 372 373 374

            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" )
                {
375
                    trackType = static_cast<MainWorkflow::TrackType>( clipProperty.text().toUInt( &ok ) );
376 377 378 379 380 381 382 383 384 385 386 387
                    if ( ok == false )
                    {
                        qWarning() << "Invalid track type starting frame";
                        return ;
                    }
                }
                else
                    qDebug() << "Unknown field" << clipProperty.tagName();

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

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

            clip = clip.nextSibling().toElement();
        }
        elem = elem.nextSibling().toElement();
    }
398 399
}

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

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

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

430 431 432 433 434 435 436 437
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
438 439
void        MainWorkflow::tracksUnpaused()
{
440
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
441 442 443 444 445 446
        if ( m_tracks[i]->isPaused() == true )
            return ;
    m_paused = false;
    emit mainWorkflowUnpaused();
}

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

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

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

467 468
qint64      MainWorkflow::getCurrentFrame() const
{
469 470
    QReadLocker     lock( m_currentFrameLock );

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
471
    return m_currentFrame[MainWorkflow::VideoTrack];
472
}
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493


void        MainWorkflow::widthChanged( const QVariant& width )
{
    m_width = width.toUInt();
}

void        MainWorkflow::heightChanged( const QVariant& height )
{
    m_height = height.toUInt();
}

uint32_t    MainWorkflow::getWidth() const
{
    return m_width;
}

uint32_t    MainWorkflow::getHeight() const
{
    return m_height;
}