MainWorkflow.cpp 13.5 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_renderStartedLock = new QReadWriteLock;
43
    m_renderMutex = new QMutex;
44 45
    m_synchroneRenderWaitCondition = new QWaitCondition;
    m_synchroneRenderWaitConditionMutex = new QMutex;
46 47 48 49

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

50 51
    m_tracks = new TrackHandler*[MainWorkflow::NbTrackType];
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
52
    {
53
        MainWorkflow::TrackType trackType = (i == 0 ? MainWorkflow::VideoTrack : MainWorkflow::AudioTrack );
54
        m_tracks[i] = new TrackHandler( trackCount, trackType, m_effectEngine );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
55
        connect( m_tracks[i], SIGNAL( tracksPaused() ), this, SLOT( tracksPaused() ) );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
56
        connect( m_tracks[i], SIGNAL( tracksUnpaused() ), this, SLOT( tracksUnpaused() ) );
57
        connect( m_tracks[i], SIGNAL( allTracksRenderCompleted() ), this, SLOT( tracksRenderCompleted() ) );
58
        connect( m_tracks[i], SIGNAL( tracksEndReached() ), this, SLOT( tracksEndReached() ) );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
59 60
    }
    m_outputBuffers = new OutputBuffers;
61 62 63

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

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

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

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
81
EffectsEngine*          MainWorkflow::getEffectsEngine()
82
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
83
    return m_effectEngine;
84
}
85

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

95
void            MainWorkflow::computeLength()
96
{
97 98
    qint64      maxLength = 0;

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

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

116
void                    MainWorkflow::getOutput()
117
{
118 119
    QReadLocker         lock( m_renderStartedLock );
    QMutexLocker        lock2( m_renderMutex );
120

121
    if ( m_renderStarted == true )
122
    {
123
        for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
124
            m_tracks[i]->getOutput( m_currentFrame );
125 126
        if ( m_paused == false )
            nextFrame();
127
    }
128
}
129

130 131
void        MainWorkflow::pause()
{
132 133
    QMutexLocker    lock( m_renderMutex );

134
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
135
        m_tracks[i]->pause();
136 137
}

138 139 140 141
void        MainWorkflow::unpause()
{
    QMutexLocker    lock( m_renderMutex );

142
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
143
        m_tracks[i]->unpause();
144 145
}

146 147 148
void        MainWorkflow::nextFrame()
{
    ++m_currentFrame;
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
149

150
    emit frameChanged( m_currentFrame );
151
    emit positionChanged( (float)m_currentFrame / (float)m_lengthFrame );
152 153 154 155 156 157
}

void        MainWorkflow::previousFrame()
{
    --m_currentFrame;
    emit frameChanged( m_currentFrame );
158
    emit positionChanged( (float)m_currentFrame / (float)m_lengthFrame );
159 160
}

161 162
void        MainWorkflow::setPosition( float pos )
{
163 164 165 166
    if ( m_renderStarted == true )
    {
        //Since any track can be reactivated, we reactivate all of them, and let them
        //unable themself if required.
167
        for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i)
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
168
            m_tracks[i]->activateAll();
169
    }
170
    qint64  frame = static_cast<qint64>( (float)m_lengthFrame * pos );
171 172
    m_currentFrame = frame;
    emit frameChanged( frame );
173
    //Do not emit a signal for the RenderWidget, since it's the one that triggered that call...
174 175
}

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

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

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

    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 194
    m_currentFrame = 0;
    emit frameChanged( 0 );
195
    emit positionChanged( 0 );
196
}
197

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

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

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

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

232
    m_synchroneRenderWaitConditionMutex->unlock();
233 234

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

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

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

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

void        MainWorkflow::setCurrentFrame( qint64 currentFrame )
{
    m_currentFrame = currentFrame;
260
    emit positionChanged( (float)m_currentFrame / (float)m_lengthFrame );
261
}
262

263
Clip*       MainWorkflow::getClip( const QUuid& uuid, unsigned int trackId, MainWorkflow::TrackType trackType )
264
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
265
    return m_tracks[trackType]->getClip( uuid, trackId );
266
}
267 268 269

void        MainWorkflow::loadProject( const QDomElement& project )
{
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
    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;
300
            MainWorkflow::TrackType     trackType = MainWorkflow::VideoTrack;
301 302 303 304 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 333 334 335 336 337

            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" )
                {
338
                    trackType = static_cast<MainWorkflow::TrackType>( clipProperty.text().toUInt( &ok ) );
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
                    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();
    }
358 359
}

360
void        MainWorkflow::saveProject( QDomDocument& doc, QDomElement& rootNode )
361
{
362
    QDomElement project = doc.createElement( "timeline" );
363
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
364 365 366 367
    {
        m_tracks[i]->save( doc, project );
    }
    rootNode.appendChild( project );
368
}
369 370 371

void        MainWorkflow::clear()
{
372
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
373
        m_tracks[i]->clear();
374
    emit cleared();
375
}
376 377 378

void        MainWorkflow::setFullSpeedRender( bool value )
{
379
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
380 381
        m_tracks[i]->setFullSpeedRender( value );
}
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
382 383 384

void        MainWorkflow::tracksPaused()
{
385
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
386 387
        if ( m_tracks[i]->isPaused() == false )
            return ;
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
388
    m_paused = true;
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
389 390 391
    emit mainWorkflowPaused();
}

392 393 394 395 396 397 398 399
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
400 401
void        MainWorkflow::tracksUnpaused()
{
402
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
403 404 405 406 407 408
        if ( m_tracks[i]->isPaused() == true )
            return ;
    m_paused = false;
    emit mainWorkflowUnpaused();
}

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
409 410
void        MainWorkflow::tracksRenderCompleted()
{
411
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
412 413 414 415 416 417 418
        if ( m_tracks[i]->allTracksRendered() == false )
            return ;
    {
        QMutexLocker    lock( m_synchroneRenderWaitConditionMutex );
    }
    m_synchroneRenderWaitCondition->wakeAll();
}
419

420
int         MainWorkflow::getTrackCount( MainWorkflow::TrackType trackType ) const
421 422 423
{
    return m_tracks[trackType]->getTrackCount();
}
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
424

425 426 427 428
qint64      MainWorkflow::getCurrentFrame() const
{
    return m_currentFrame;
}