MainWorkflow.cpp 12.5 KB
Newer Older
1 2 3 4
/*****************************************************************************
 * MainWorkflow.cpp : Will query all of the track workflows to render the final
 *                    image
 *****************************************************************************
Ludovic Fauvet's avatar
Ludovic Fauvet committed
5
 * Copyright (C) 2008-2010 VideoLAN
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *
 * 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>

Ludovic Fauvet's avatar
Ludovic Fauvet committed
26
#include "vlmc.h"
27
#include "Clip.h"
28
#include "ClipHelper.h"
29
#include "ClipWorkflow.h"
30
#include "Library.h"
31
#include "MainWorkflow.h"
32 33
#include "TrackWorkflow.h"
#include "TrackHandler.h"
34
#include "SettingsManager.h"
35
#include "Workflow/Types.h"
36 37

#include <QDomElement>
38
#include <QMutex>
39

40
MainWorkflow::MainWorkflow( int trackCount ) :
41
        m_lengthFrame( 0 ),
42 43 44
        m_renderStarted( false ),
        m_width( 0 ),
        m_height( 0 )
45
{
46
    m_currentFrameLock = new QReadWriteLock;
47
    m_renderStartedMutex = new QMutex;
48

49 50 51
    m_tracks = new TrackHandler*[Workflow::NbTrackType];
    m_currentFrame = new qint64[Workflow::NbTrackType];
    for ( unsigned int i = 0; i < Workflow::NbTrackType; ++i )
52
    {
53 54
        Workflow::TrackType trackType =
                (i == 0 ? Workflow::VideoTrack : Workflow::AudioTrack );
55
        m_tracks[i] = new TrackHandler( trackCount, trackType );
56 57
        connect( m_tracks[i], SIGNAL( tracksEndReached() ),
                 this, SLOT( tracksEndReached() ) );
58 59
        connect( m_tracks[i], SIGNAL( lengthChanged(qint64) ),
                 this, SLOT( lengthUpdated( qint64 ) ) );
60
        m_currentFrame[i] = 0;
61
    }
62 63 64 65
}

MainWorkflow::~MainWorkflow()
{
66
    delete m_renderStartedMutex;
67
    delete m_currentFrameLock;
68
    delete m_currentFrame;
69
    for ( unsigned int i = 0; i < Workflow::NbTrackType; ++i )
70
        delete m_tracks[i];
71
    delete[] m_tracks;
72
    delete m_blackOutput;
73 74
}

75 76
EffectsEngine::EffectHelper*
MainWorkflow::addEffect( Effect *effect, quint32 trackId, const QUuid &uuid, Workflow::TrackType type )
77
{
78
    return m_tracks[type]->addEffect( effect, trackId, uuid );
79 80
}

81 82
void
MainWorkflow::computeLength()
83
{
84 85
    qint64      maxLength = 0;

86
    for ( unsigned int i = 0; i < Workflow::NbTrackType; ++i )
87 88 89 90
    {
        if ( m_tracks[i]->getLength() > maxLength )
            maxLength = m_tracks[i]->getLength();
    }
91 92 93 94 95 96
    if ( m_lengthFrame != maxLength )
    {
        m_lengthFrame = maxLength;
        emit lengthChanged( m_lengthFrame );
    }

97 98
}

99
void
100
MainWorkflow::startRender( quint32 width, quint32 height, double fps )
101
{
102
    //Reinit the effects in case the width/height has change
103
    m_renderStarted = true;
104 105
    m_width = width;
    m_height = height;
106 107 108 109
    if ( m_blackOutput != NULL )
        delete m_blackOutput;
    m_blackOutput = new Workflow::Frame( m_width, m_height );
    memset( m_blackOutput->buffer(), 0, m_blackOutput->size() );
110
    for ( unsigned int i = 0; i < Workflow::NbTrackType; ++i )
111
        m_tracks[i]->startRender( width, height, fps );
112 113 114
    computeLength();
}

115
const Workflow::OutputBuffer*
116
MainWorkflow::getOutput( Workflow::TrackType trackType, bool paused )
117
{
118
    QMutexLocker        lock( m_renderStartedMutex );
119

120
    if ( m_renderStarted == true )
121
    {
122
        QReadLocker         lock2( m_currentFrameLock );
123

124 125
        Workflow::OutputBuffer  *ret = m_tracks[trackType]->getOutput( m_currentFrame[Workflow::VideoTrack],
                                                                        m_currentFrame[trackType], paused );
126
        if ( trackType == Workflow::VideoTrack )
127
        {
128
            if ( ret == NULL )
129
                return m_blackOutput;
130
        }
131
        return ret;
132
    }
133
    return NULL;
134
}
135

136
void
137
MainWorkflow::nextFrame( Workflow::TrackType trackType )
138
{
139
    QWriteLocker    lock( m_currentFrameLock );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
140

141
    ++m_currentFrame[trackType];
142
    if ( trackType == Workflow::VideoTrack )
143
        emit frameChanged( m_currentFrame[Workflow::VideoTrack], Vlmc::Renderer );
144 145
}

146
void
147
MainWorkflow::previousFrame( Workflow::TrackType trackType )
148
{
149
    QWriteLocker    lock( m_currentFrameLock );
150

151
    --m_currentFrame[trackType];
152
    if ( trackType == Workflow::VideoTrack )
153
        emit frameChanged( m_currentFrame[Workflow::VideoTrack], Vlmc::Renderer );
154 155
}

156 157
qint64
MainWorkflow::getLengthFrame() const
158
{
159
    return m_lengthFrame;
160
}
161

162 163
qint64
MainWorkflow::getClipPosition( const QUuid& uuid, unsigned int trackId,
164
                               Workflow::TrackType trackType ) const
165
{
166
    return m_tracks[trackType]->getClipPosition( uuid, trackId );
167
}
168

169 170
void
MainWorkflow::stop()
171
{
172 173 174 175 176
    /*
        Assume the method can be called without locking anything, since the workflow won't
        be queried by the renderer (When stopping the renderer, it stops its media player
        before stopping the mainworkflow.
    */
177
    m_renderStarted = false;
178
    for (unsigned int i = 0; i < Workflow::NbTrackType; ++i)
179
    {
180
        m_tracks[i]->stop();
181 182
        m_currentFrame[i] = 0;
    }
183
    emit frameChanged( 0, Vlmc::Renderer );
184
}
185

186 187 188
void
MainWorkflow::stopFrameComputing()
{
189
    for ( qint32 type = 0; type < Workflow::NbTrackType; ++type )
190 191 192
        m_tracks[type]->stopFrameComputing();
}

193
void
194
MainWorkflow::muteTrack( unsigned int trackId, Workflow::TrackType trackType )
195
{
196
    m_tracks[trackType]->muteTrack( trackId );
197 198
}

199
void
200
MainWorkflow::unmuteTrack( unsigned int trackId, Workflow::TrackType trackType )
201
{
202
    m_tracks[trackType]->unmuteTrack( trackId );
203
}
204

205 206
void
MainWorkflow::muteClip( const QUuid& uuid, unsigned int trackId,
207
                        Workflow::TrackType trackType )
208
{
209
    m_tracks[trackType]->muteClip( uuid, trackId );
210 211 212 213
}

void
MainWorkflow::unmuteClip( const QUuid& uuid, unsigned int trackId,
214
                          Workflow::TrackType trackType )
215
{
216
    m_tracks[trackType]->unmuteClip( uuid, trackId );
217 218
}

219
void
220
MainWorkflow::setCurrentFrame( qint64 currentFrame, Vlmc::FrameChangedReason reason )
221
{
222 223
    QWriteLocker    lock( m_currentFrameLock );

224
    for ( unsigned int i = 0; i < Workflow::NbTrackType; ++i)
225 226
        m_currentFrame[i] = currentFrame;
    emit frameChanged( currentFrame, reason );
227
}
228

229 230 231
ClipHelper*
MainWorkflow::getClipHelper( const QUuid &uuid, unsigned int trackId,
                                Workflow::TrackType trackType )
232
{
233
    return m_tracks[trackType]->getClipHelper( uuid, trackId );
234
}
235

236
/**
237
 *  \warning    The mainworkflow is expected to be already cleared by the ProjectManager
238
 */
239
void
240
MainWorkflow::loadProject( const QDomElement &root )
241
{
242
    QDomElement     project = root.firstChildElement( "workflow" );
243
    if ( project.isNull() == true )
244 245
        return ;

246
    QDomElement elem = project.firstChildElement();
247 248 249 250 251 252 253 254 255 256 257

    while ( elem.isNull() == false )
    {
        bool    ok;

        unsigned int trackId = elem.attribute( "id" ).toUInt( &ok );
        if ( ok == false )
        {
            qWarning() << "Invalid track number in project file";
            return ;
        }
258
        Workflow::TrackType     type;
259
        int utype = elem.attribute( "type" ).toInt( &ok );
260
        if ( ok == false || (utype < 0 && utype >= Workflow::NbTrackType ) )
261 262 263 264
        {
            qWarning() << "Invalid track type";
            return ;
        }
265
        type = static_cast<Workflow::TrackType>( utype );
266

267
        QDomElement clip = elem.firstChildElement();
268 269 270
        while ( clip.isNull() == false )
        {
            //Iterate over clip fields:
271
            QString                     uuid;
272 273
            QString                     begin;
            QString                     end;
274
            QString                     startFrame;
275
            QString                     chUuid;
276 277

            uuid = clip.attribute( "uuid" );
278 279
            begin = clip.attribute( "begin" );
            end = clip.attribute( "end" );
280
            startFrame = clip.attribute( "startFrame" );
281
            chUuid = clip.attribute( "helper" );
282 283

            if ( uuid.isEmpty() == true || startFrame.isEmpty() == true )
284
            {
285 286
                qWarning() << "Invalid clip node";
                return ;
287 288
            }

289 290
            Clip* c = Library::getInstance()->clip( uuid );
            if ( c != NULL )
291
            {
292 293
                ClipHelper  *ch = new ClipHelper( c, begin.toLongLong(),
                                                  end.toLongLong(), chUuid );
294
                track( type, trackId )->addClip( ch, startFrame.toLongLong() );
295

296 297
                QDomElement     effects = clip.firstChildElement( "effects" );
                if ( effects.isNull() == false )
298
                {
299 300
                    QDomElement effect = effects.firstChildElement( "effect" );
                    while ( effect.isNull() == false )
301
                    {
302 303 304 305 306 307 308 309 310 311 312 313 314
                        if ( effect.hasAttribute( "name" ) == true &&
                             effect.hasAttribute( "start" ) == true &&
                             effect.hasAttribute( "end" ) == true )
                        {
                            Effect  *e = EffectsEngine::getInstance()->effect( effect.attribute( "name" ) );
                            qint64  start = effect.attribute( "start" ).toLongLong();
                            qint64  end = effect.attribute( "end" ).toLongLong();
                            if ( e != NULL )
                                ch->clipWorkflow()->appendEffect( e, start, end );
                            else
                                qCritical() << "Workflow: Can't load effect" << effect.attribute( "name" );
                        }
                        effect = effect.nextSiblingElement();
315 316 317
                    }
                }
            }
318
            clip = clip.nextSiblingElement();
319
        }
320
        elem = elem.nextSiblingElement();
321
    }
322 323
}

324
void
325
MainWorkflow::saveProject( QXmlStreamWriter& project ) const
326
{
327
    project.writeStartElement( "workflow" );
328
    for ( unsigned int i = 0; i < Workflow::NbTrackType; ++i )
329
    {
330
        m_tracks[i]->save( project );
331
    }
332
    project.writeEndElement();
333
}
334

335 336
void
MainWorkflow::clear()
337
{
338
    for ( unsigned int i = 0; i < Workflow::NbTrackType; ++i )
339
        m_tracks[i]->clear();
340
    emit cleared();
341
}
342

343 344
void
MainWorkflow::tracksEndReached()
345
{
346
    for ( unsigned int i = 0; i < Workflow::NbTrackType; ++i )
347
    {
348 349
        if ( m_tracks[i]->endIsReached() == false )
            return ;
350
    }
351 352 353
    emit mainWorkflowEndReached();
}

354
int
355
MainWorkflow::getTrackCount( Workflow::TrackType trackType ) const
356 357 358
{
    return m_tracks[trackType]->getTrackCount();
}
359

360
qint64
361
MainWorkflow::getCurrentFrame( bool lock /*= false*/ ) const
362
{
363 364 365 366 367
    if ( lock == true )
    {
        QReadLocker     lock( m_currentFrameLock );
        return m_currentFrame[Workflow::VideoTrack];
    }
368
    return m_currentFrame[Workflow::VideoTrack];
369
}
370

371
quint32
372
MainWorkflow::getWidth() const
373
{
374
    Q_ASSERT( m_width != 0 );
375 376 377
    return m_width;
}

378
quint32
379
MainWorkflow::getHeight() const
380
{
381
    Q_ASSERT( m_height != 0 );
382 383
    return m_height;
}
384 385 386 387

void
MainWorkflow::renderOneFrame()
{
388
    for ( unsigned int i = 0; i < Workflow::NbTrackType; ++i )
389
        m_tracks[i]->renderOneFrame();
390 391
    nextFrame( Workflow::VideoTrack );
    nextFrame( Workflow::AudioTrack );
392
}
393 394 395 396

void
MainWorkflow::setFullSpeedRender( bool val )
{
397
    for ( unsigned int i = 0; i < Workflow::NbTrackType; ++i )
398 399
        m_tracks[i]->setFullSpeedRender( val );
}
400

401 402 403
bool
MainWorkflow::contains( const QUuid &uuid ) const
{
404
    for ( qint32 type = 0; type < Workflow::NbTrackType; ++type )
405 406 407 408
        if ( m_tracks[type]->contains( uuid ) == true )
            return true;
    return false;
}
409 410 411 412 413 414

const Workflow::Frame*
MainWorkflow::blackOutput() const
{
    return m_blackOutput;
}
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431

void
MainWorkflow::lengthUpdated( qint64 )
{
    qint64  maxLength = 0;

    for ( unsigned int i = 0; i < Workflow::NbTrackType; ++i )
    {
        if ( m_tracks[i]->getLength() > maxLength )
            maxLength = m_tracks[i]->getLength();
    }
    if ( m_lengthFrame != maxLength )
    {
        m_lengthFrame = maxLength;
        emit lengthChanged( m_lengthFrame );
    }
}
432 433 434 435 436 437

TrackWorkflow*
MainWorkflow::track( Workflow::TrackType type, quint32 trackId )
{
    return m_tracks[type]->track( trackId );
}