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

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

35 36
LightVideoFrame*     MainWorkflow::blackOutput = NULL;

37
MainWorkflow::MainWorkflow( int trackCount ) :
38
        m_currentFrame( 0 ),
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 48 49 50

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

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

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

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

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

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

98
void            MainWorkflow::computeLength()
99
{
100 101
    qint64      maxLength = 0;

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

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

119
void                    MainWorkflow::getOutput( TrackType trackType )
120
{
121 122
    QReadLocker         lock( m_renderStartedLock );
    QMutexLocker        lock2( m_renderMutex );
123

124
    if ( m_renderStarted == true )
125
    {
126 127
        QReadLocker         lock3( m_currentFrameLock );
        QMutexLocker        lock4( m_nbTrackHandlerToRenderMutex );
128

129 130 131
        if ( trackType == BothTrackType )
        {
            m_nbTrackHandlerToRender = MainWorkflow::NbTrackType;
132 133 134
            for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
                m_tracks[i]->getOutput( m_currentFrame );
        }
135 136 137 138 139
        else
        {
            m_nbTrackHandlerToRender = 1;
            m_tracks[trackType]->getOutput( m_currentFrame );
        }
140
    }
141
}
142

143 144
void        MainWorkflow::pause()
{
145 146
    QMutexLocker    lock( m_renderMutex );

147
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
148
        m_tracks[i]->pause();
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 161 162 163 164
void        MainWorkflow::goToNextFrame()
{
    if ( m_paused == false )
        nextFrame();
}

165 166
void        MainWorkflow::nextFrame()
{
167
    QWriteLocker    lock( m_currentFrameLock );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
168

169
    ++m_currentFrame;
170
    emit frameChanged( m_currentFrame, Renderer );
171 172 173 174
}

void        MainWorkflow::previousFrame()
{
175
    QWriteLocker    lock( m_currentFrameLock );
176 177

    --m_currentFrame;
178
    emit frameChanged( m_currentFrame, Renderer );
179 180
}

181
qint64      MainWorkflow::getLengthFrame() const
182
{
183
    return m_lengthFrame;
184
}
185

186
qint64      MainWorkflow::getClipPosition( const QUuid& uuid, unsigned int trackId, MainWorkflow::TrackType trackType ) const
187
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
188
    return m_tracks[trackType]->getClipPosition( uuid, trackId );
189
}
190 191 192 193

void            MainWorkflow::stop()
{
    QWriteLocker    lock( m_renderStartedLock );
194
    QWriteLocker    lock2( m_currentFrameLock );
195 196

    m_renderStarted = false;
197
    for (unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i)
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
198
        m_tracks[i]->stop();
199
    m_currentFrame = 0;
200
    emit frameChanged( 0, Renderer );
201
}
202

203
void           MainWorkflow::moveClip( const QUuid& clipUuid, unsigned int oldTrack,
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
204
                                       unsigned int newTrack, qint64 startingFrame,
205
                                       MainWorkflow::TrackType trackType, bool undoRedoCommand /*= false*/ )
206
{
207
    m_tracks[trackType]->moveClip( clipUuid, oldTrack, newTrack, startingFrame );
208
    computeLength();
209

210 211 212 213
    if ( undoRedoCommand == true )
    {
        emit clipMoved( clipUuid, newTrack, startingFrame, trackType );
    }
214
}
215

216
Clip*       MainWorkflow::removeClip( const QUuid& uuid, unsigned int trackId, MainWorkflow::TrackType trackType )
217
{
218 219
    Clip* clip = m_tracks[trackType]->removeClip( uuid, trackId );
    computeLength();
220
    emit clipRemoved( clip, trackId, trackType );
221
    return clip;
222 223
}

224
MainWorkflow::OutputBuffers*  MainWorkflow::getSynchroneOutput( MainWorkflow::TrackType trackType )
225 226
{
    m_synchroneRenderWaitConditionMutex->lock();
227
    getOutput( trackType );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
228
//    qDebug() << "Waiting for sync output";
229
    m_synchroneRenderWaitCondition->wait( m_synchroneRenderWaitConditionMutex );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
230
//    qDebug() << "Got it";
231 232 233
    if ( trackType == BothTrackType || trackType == VideoTrack )
    {
        m_effectEngine->render();
234 235 236 237
        if ( m_effectEngine->getOutputFrame( 0 )->nboctets == 0 )
            m_outputBuffers->video = MainWorkflow::blackOutput;
        else
            m_outputBuffers->video = &( m_effectEngine->getOutputFrame( 0 ) );
238 239
    }
    if ( trackType == BothTrackType || trackType == AudioTrack )
240
    {
241
        m_outputBuffers->audio = m_tracks[MainWorkflow::AudioTrack]->getTmpAudioBuffer();
242
    }
243
    m_synchroneRenderWaitConditionMutex->unlock();
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
244
    return m_outputBuffers;
245
}
246 247 248 249 250 251 252 253

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

255
void        MainWorkflow::muteTrack( unsigned int trackId, MainWorkflow::TrackType trackType )
256
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
257
    m_tracks[trackType]->muteTrack( trackId );
258 259
}

260
void        MainWorkflow::unmuteTrack( unsigned int trackId, MainWorkflow::TrackType trackType )
261
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
262
    m_tracks[trackType]->unmuteTrack( trackId );
263
}
264

265
void        MainWorkflow::setCurrentFrame( qint64 currentFrame, MainWorkflow::FrameChangedReason reason )
266
{
267 268
    QWriteLocker    lock( m_currentFrameLock );

269 270 271 272 273 274 275
    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();
    }
276
    m_currentFrame = currentFrame;
277
    emit frameChanged( m_currentFrame, reason );
278
}
279

280
Clip*       MainWorkflow::getClip( const QUuid& uuid, unsigned int trackId, MainWorkflow::TrackType trackType )
281
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
282
    return m_tracks[trackType]->getClip( uuid, trackId );
283
}
284 285 286

void        MainWorkflow::loadProject( const QDomElement& project )
{
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
    if ( project.isNull() == true || project.tagName() != "timeline" )
    {
        qWarning() << "Invalid timeline node (" << project.tagName() << ')';
        return ;
    }

    clear();

    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;
317
            MainWorkflow::TrackType     trackType = MainWorkflow::VideoTrack;
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 353 354

            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" )
                {
355
                    trackType = static_cast<MainWorkflow::TrackType>( clipProperty.text().toUInt( &ok ) );
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
                    if ( ok == false )
                    {
                        qWarning() << "Invalid track type starting frame";
                        return ;
                    }
                }
                else
                    qDebug() << "Unknown field" << clipProperty.tagName();

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

            Clip*       c = new Clip( parent, begin, end );
            addClip( c, trackId, startPos, trackType );

            clip = clip.nextSibling().toElement();
        }
        elem = elem.nextSibling().toElement();
    }
375 376
}

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

void        MainWorkflow::clear()
{
389
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
390
        m_tracks[i]->clear();
391
    emit cleared();
392
}
393 394 395

void        MainWorkflow::setFullSpeedRender( bool value )
{
396
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
397 398
        m_tracks[i]->setFullSpeedRender( value );
}
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
399 400 401

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

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

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
426 427
void        MainWorkflow::tracksRenderCompleted()
{
428 429 430 431 432
    {
        QMutexLocker    lock( m_nbTrackHandlerToRenderMutex );

        --m_nbTrackHandlerToRender;
        if ( m_nbTrackHandlerToRender > 0 )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
433
            return ;
434
    }
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
435 436 437 438 439
    {
        QMutexLocker    lock( m_synchroneRenderWaitConditionMutex );
    }
    m_synchroneRenderWaitCondition->wakeAll();
}
440

441
int         MainWorkflow::getTrackCount( MainWorkflow::TrackType trackType ) const
442 443 444
{
    return m_tracks[trackType]->getTrackCount();
}
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
445

446 447
qint64      MainWorkflow::getCurrentFrame() const
{
448 449
    QReadLocker     lock( m_currentFrameLock );

450 451
    return m_currentFrame;
}