MainWorkflow.cpp 12.6 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 28 29 30
#include "Clip.h"
#include "EffectsEngine.h"
#include "Library.h"
#include "LightVideoFrame.h"
31
#include "MainWorkflow.h"
32 33
#include "TrackWorkflow.h"
#include "TrackHandler.h"
34
#include "SettingsManager.h"
35 36

#include <QDomElement>
37
#include <QXmlStreamWriter>
38

39
LightVideoFrame     *MainWorkflow::blackOutput = NULL;
40

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

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

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

MainWorkflow::~MainWorkflow()
{
69
    delete m_effectEngine;
70
    delete m_renderStartedMutex;
71
    delete m_currentFrameLock;
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
72
    delete m_currentFrame;
73
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
74
        delete m_tracks[i];
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
75
    delete[] m_tracks;
76 77
}

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

84 85
void
MainWorkflow::addClip( Clip *clip, unsigned int trackId,
86
                                        qint64 start, MainWorkflow::TrackType trackType )
87
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
88
    m_tracks[trackType]->addClip( clip, trackId, start );
89
    computeLength();
90
    //Inform the GUI
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
91
    emit clipAdded( clip, trackId, start, trackType );
92
}
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 105 106 107 108 109
    if ( m_lengthFrame != maxLength )
    {
        m_lengthFrame = maxLength;
        emit lengthChanged( m_lengthFrame );
    }

110 111
}

112
void
113
MainWorkflow::startRender( quint32 width, quint32 height )
114 115
{
    m_renderStarted = true;
116 117
    m_width = width;
    m_height = height;
118 119 120 121 122
    if ( blackOutput != NULL )
        delete blackOutput;
    blackOutput = new LightVideoFrame( m_width, m_height );
    // FIX ME vvvvvv , It doesn't update meta info (nbpixels, nboctets, etc.
    memset( (*blackOutput)->frame.octets, 0, (*blackOutput)->nboctets );
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
MainWorkflow::OutputBuffers*
129
MainWorkflow::getOutput( TrackType trackType, bool paused )
130
{
131
    QMutexLocker        lock( m_renderStartedMutex );
132

133
    if ( m_renderStarted == true )
134
    {
135
        QReadLocker         lock2( m_currentFrameLock );
136

137
        m_tracks[trackType]->getOutput( m_currentFrame[VideoTrack],
138
                                        m_currentFrame[trackType], paused );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
139
        if ( trackType == MainWorkflow::VideoTrack )
140
        {
141 142
            m_effectEngine->render();
            const LightVideoFrame &tmp = m_effectEngine->getVideoOutput( 1 );
143 144 145 146
            if ( tmp->nboctets == 0 )
                m_outputBuffers->video = blackOutput;
            else
                m_outputBuffers->video = &tmp;
147
        }
148 149
        else
        {
150 151
            m_outputBuffers->audio =
                    m_tracks[MainWorkflow::AudioTrack]->getTmpAudioBuffer();
152
        }
153
    }
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
154
    return m_outputBuffers;
155
}
156

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

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
162 163 164
    ++m_currentFrame[trackType];
    if ( trackType == MainWorkflow::VideoTrack )
        emit frameChanged( m_currentFrame[MainWorkflow::VideoTrack], Renderer );
165 166
}

167 168
void
MainWorkflow::previousFrame( MainWorkflow::TrackType trackType )
169
{
170
    QWriteLocker    lock( m_currentFrameLock );
171

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
172 173 174
    --m_currentFrame[trackType];
    if ( trackType == MainWorkflow::VideoTrack )
        emit frameChanged( m_currentFrame[MainWorkflow::VideoTrack], Renderer );
175 176
}

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

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

190 191
void
MainWorkflow::stop()
192
{
193
    QMutexLocker    lock( m_renderStartedMutex );
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
    {
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
199
        m_tracks[i]->stop();
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
200 201
        m_currentFrame[i] = 0;
    }
202
    emit frameChanged( 0, Renderer );
203
}
204

205 206 207 208 209
void
MainWorkflow::moveClip( const QUuid &clipUuid, unsigned int oldTrack,
                        unsigned int newTrack, qint64 startingFrame,
                        MainWorkflow::TrackType trackType,
                        bool undoRedoCommand /*= false*/ )
210
{
211
    m_tracks[trackType]->moveClip( clipUuid, oldTrack, newTrack, startingFrame );
212
    computeLength();
213

214 215 216 217
    if ( undoRedoCommand == true )
    {
        emit clipMoved( clipUuid, newTrack, startingFrame, trackType );
    }
218
}
219

220 221 222
Clip*
MainWorkflow::removeClip( const QUuid &uuid, unsigned int trackId,
                          MainWorkflow::TrackType trackType )
223
{
224
    Clip *clip = m_tracks[trackType]->removeClip( uuid, trackId );
225 226 227 228 229
    if ( clip != NULL )
    {
        computeLength();
        emit clipRemoved( clip, trackId, trackType );
    }
230
    return clip;
231 232
}

233 234
void
MainWorkflow::muteTrack( unsigned int trackId, MainWorkflow::TrackType trackType )
235
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
236
    m_tracks[trackType]->muteTrack( trackId );
237 238
}

239 240
void
MainWorkflow::unmuteTrack( unsigned int trackId, MainWorkflow::TrackType trackType )
241
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
242
    m_tracks[trackType]->unmuteTrack( trackId );
243
}
244

245 246 247 248
void
MainWorkflow::muteClip( const QUuid& uuid, unsigned int trackId,
                        MainWorkflow::TrackType trackType )
{
249
    m_tracks[trackType]->muteClip( uuid, trackId );
250 251 252 253 254 255
}

void
MainWorkflow::unmuteClip( const QUuid& uuid, unsigned int trackId,
                          MainWorkflow::TrackType trackType )
{
256
    m_tracks[trackType]->unmuteClip( uuid, trackId );
257 258
}

259 260
void
MainWorkflow::setCurrentFrame( qint64 currentFrame, MainWorkflow::FrameChangedReason reason )
261
{
262 263
    QWriteLocker    lock( m_currentFrameLock );

264 265 266
    if ( m_renderStarted == true )
    {
        //Since any track can be reactivated, we reactivate all of them, and let them
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
267
        //disable themself if required.
268 269 270
        for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i)
            m_tracks[i]->activateAll();
    }
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
271 272 273
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i)
        m_currentFrame[i] = currentFrame;
    emit frameChanged( currentFrame, reason );
274
}
275

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

283
/**
284
 *  \warning    The mainworkflow is expected to be already cleared by the ProjectManager
285
 */
286
void
287
MainWorkflow::loadProject( const QDomElement &root )
288
{
289 290
    QDomElement     project = root.firstChildElement( "timeline" );
    if ( project.isNull() == true )
291 292 293 294 295 296 297 298 299 300 301 302 303 304
        return ;

    QDomElement elem = project.firstChild().toElement();

    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 ;
        }
305 306 307 308 309 310 311 312 313
        MainWorkflow::TrackType     type;
        int utype = elem.attribute( "type" ).toInt( &ok );
        if ( ok == false || (utype < 0 && utype >= MainWorkflow::NbTrackType ) )
        {
            qWarning() << "Invalid track type";
            return ;
        }
        type = static_cast<MainWorkflow::TrackType>( utype );

314 315 316 317
        QDomElement clip = elem.firstChild().toElement();
        while ( clip.isNull() == false )
        {
            //Iterate over clip fields:
318 319 320 321 322 323 324
            QString                     uuid;
            QString                     startFrame;

            uuid = clip.attribute( "uuid" );
            startFrame = clip.attribute( "startFrame" );

            if ( uuid.isEmpty() == true || startFrame.isEmpty() == true )
325
            {
326 327
                qWarning() << "Invalid clip node";
                return ;
328 329
            }

330 331
            Clip* c = Library::getInstance()->clip( uuid );
            if ( c != NULL )
332
            {
333
                addClip( new Clip( c ), trackId, startFrame.toLongLong(), type );
334
            }
335 336 337 338
            clip = clip.nextSibling().toElement();
        }
        elem = elem.nextSibling().toElement();
    }
339 340
}

341
void
342
MainWorkflow::saveProject( QXmlStreamWriter& project ) const
343
{
344
    project.writeStartElement( "timeline" );
345
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
346
    {
347
        m_tracks[i]->save( project );
348
    }
349
    project.writeEndElement();
350
}
351

352 353
void
MainWorkflow::clear()
354
{
355
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
356
        m_tracks[i]->clear();
357
    emit cleared();
358
}
359

360 361
void
MainWorkflow::tracksEndReached()
362 363
{
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
364
    {
365 366
        if ( m_tracks[i]->endIsReached() == false )
            return ;
367
    }
368 369 370
    emit mainWorkflowEndReached();
}

371 372
int
MainWorkflow::getTrackCount( MainWorkflow::TrackType trackType ) const
373 374 375
{
    return m_tracks[trackType]->getTrackCount();
}
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
376

377 378
qint64
MainWorkflow::getCurrentFrame() const
379
{
380 381
    QReadLocker     lock( m_currentFrameLock );

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
382
    return m_currentFrame[MainWorkflow::VideoTrack];
383
}
384

385
quint32
386
MainWorkflow::getWidth() const
387
{
388
    Q_ASSERT( m_width != 0 );
389 390 391
    return m_width;
}

392
quint32
393
MainWorkflow::getHeight() const
394
{
395
    Q_ASSERT( m_height != 0 );
396 397
    return m_height;
}
398 399 400 401 402 403 404 405 406

void
MainWorkflow::renderOneFrame()
{
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
        m_tracks[i]->renderOneFrame();
    nextFrame( VideoTrack );
    nextFrame( AudioTrack );
}
407 408 409 410 411 412 413

void
MainWorkflow::setFullSpeedRender( bool val )
{
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
        m_tracks[i]->setFullSpeedRender( val );
}
414 415 416 417 418 419 420

Clip*
MainWorkflow::split( Clip* toSplit, Clip* newClip, quint32 trackId, qint64 newClipPos, qint64 newClipBegin, MainWorkflow::TrackType trackType )
{
    QMutexLocker    lock( m_renderStartedMutex );

    if ( newClip == NULL )
421
        newClip = new Clip( toSplit, newClipBegin, toSplit->end() );
422 423 424 425 426 427 428 429 430 431 432 433 434

    toSplit->setEnd( newClipBegin, true );
    addClip( newClip, trackId, newClipPos, trackType );
    return newClip;
}

void
MainWorkflow::resizeClip( Clip* clip, qint64 newBegin, qint64 newEnd, qint64 newPos,
                          quint32 trackId, MainWorkflow::TrackType trackType,
                                      bool undoRedoAction /*= false*/ )
{
    QMutexLocker    lock( m_renderStartedMutex );

435
    if ( newBegin != clip->begin() )
436
    {
437
        moveClip( clip->uuid(), trackId, trackId, newPos, trackType, undoRedoAction );
438 439 440 441 442 443 444 445 446 447
    }
    clip->setBoundaries( newBegin, newEnd );
}

void
MainWorkflow::unsplit( Clip* origin, Clip* splitted, quint32 trackId,
                       MainWorkflow::TrackType trackType )
{
    QMutexLocker    lock( m_renderStartedMutex );

448 449
    removeClip( splitted->uuid(), trackId, trackType );
    origin->setEnd( splitted->end(), true );
450
}