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

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

74 75
void
MainWorkflow::addClip( Clip *clip, unsigned int trackId,
76
                                        qint64 start, MainWorkflow::TrackType trackType )
77
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
78
    m_tracks[trackType]->addClip( clip, trackId, start );
79
    computeLength();
80
    //Inform the GUI
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
81
    emit clipAdded( clip, trackId, start, trackType );
82
}
83

84 85
void
MainWorkflow::computeLength()
86
{
87 88
    qint64      maxLength = 0;

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

100 101
}

102
void
103
MainWorkflow::startRender( quint32 width, quint32 height )
104 105
{
    m_renderStarted = true;
106 107
    m_width = width;
    m_height = height;
108 109 110 111 112
    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 );
113
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
114
        m_tracks[i]->startRender();
115 116 117
    computeLength();
}

118
MainWorkflow::OutputBuffers*
119
MainWorkflow::getOutput( TrackType trackType, bool paused )
120
{
121
    QMutexLocker        lock( m_renderStartedMutex );
122

123
    if ( m_renderStarted == true )
124
    {
125
        QReadLocker         lock2( m_currentFrameLock );
126

127
        void*   ret = m_tracks[trackType]->getOutput( m_currentFrame[VideoTrack],
128
                                        m_currentFrame[trackType], paused );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
129
        if ( trackType == MainWorkflow::VideoTrack )
130
        {
131 132 133
            LightVideoFrame*    frame = static_cast<LightVideoFrame*>( ret );
            if ( frame == NULL )
                m_outputBuffers->video = MainWorkflow::blackOutput;
134
            else
135
                m_outputBuffers->video = frame;
136
        }
137 138
        else
        {
139
            m_outputBuffers->audio = static_cast<AudioClipWorkflow::AudioSample*>( ret );
140
        }
141
    }
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
142
    return m_outputBuffers;
143
}
144

145 146
void
MainWorkflow::nextFrame( MainWorkflow::TrackType trackType )
147
{
148
    QWriteLocker    lock( m_currentFrameLock );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
149

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
150 151 152
    ++m_currentFrame[trackType];
    if ( trackType == MainWorkflow::VideoTrack )
        emit frameChanged( m_currentFrame[MainWorkflow::VideoTrack], Renderer );
153 154
}

155 156
void
MainWorkflow::previousFrame( MainWorkflow::TrackType trackType )
157
{
158
    QWriteLocker    lock( m_currentFrameLock );
159

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

165 166
qint64
MainWorkflow::getLengthFrame() const
167
{
168
    return m_lengthFrame;
169
}
170

171 172 173
qint64
MainWorkflow::getClipPosition( const QUuid& uuid, unsigned int trackId,
                               MainWorkflow::TrackType trackType ) const
174
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
175
    return m_tracks[trackType]->getClipPosition( uuid, trackId );
176
}
177

178 179
void
MainWorkflow::stop()
180
{
181
    QMutexLocker    lock( m_renderStartedMutex );
182
    QWriteLocker    lock2( m_currentFrameLock );
183 184

    m_renderStarted = false;
185
    for (unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i)
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
186
    {
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
187
        m_tracks[i]->stop();
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
188 189
        m_currentFrame[i] = 0;
    }
190
    emit frameChanged( 0, Renderer );
191
}
192

193 194 195 196 197
void
MainWorkflow::moveClip( const QUuid &clipUuid, unsigned int oldTrack,
                        unsigned int newTrack, qint64 startingFrame,
                        MainWorkflow::TrackType trackType,
                        bool undoRedoCommand /*= false*/ )
198
{
199
    m_tracks[trackType]->moveClip( clipUuid, oldTrack, newTrack, startingFrame );
200
    computeLength();
201

202 203 204 205
    if ( undoRedoCommand == true )
    {
        emit clipMoved( clipUuid, newTrack, startingFrame, trackType );
    }
206
}
207

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

221 222
void
MainWorkflow::muteTrack( unsigned int trackId, MainWorkflow::TrackType trackType )
223
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
224
    m_tracks[trackType]->muteTrack( trackId );
225 226
}

227 228
void
MainWorkflow::unmuteTrack( unsigned int trackId, MainWorkflow::TrackType trackType )
229
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
230
    m_tracks[trackType]->unmuteTrack( trackId );
231
}
232

233 234 235 236
void
MainWorkflow::muteClip( const QUuid& uuid, unsigned int trackId,
                        MainWorkflow::TrackType trackType )
{
237
    m_tracks[trackType]->muteClip( uuid, trackId );
238 239 240 241 242 243
}

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

247 248
void
MainWorkflow::setCurrentFrame( qint64 currentFrame, MainWorkflow::FrameChangedReason reason )
249
{
250 251
    QWriteLocker    lock( m_currentFrameLock );

252 253 254
    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
255
        //disable themself if required.
256 257 258
        for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i)
            m_tracks[i]->activateAll();
    }
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
259 260 261
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i)
        m_currentFrame[i] = currentFrame;
    emit frameChanged( currentFrame, reason );
262
}
263

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

271
/**
272
 *  \warning    The mainworkflow is expected to be already cleared by the ProjectManager
273
 */
274
void
275
MainWorkflow::loadProject( const QDomElement &root )
276
{
277 278
    QDomElement     project = root.firstChildElement( "timeline" );
    if ( project.isNull() == true )
279 280 281 282 283 284 285 286 287 288 289 290 291 292
        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 ;
        }
293 294 295 296 297 298 299 300 301
        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 );

302 303 304 305
        QDomElement clip = elem.firstChild().toElement();
        while ( clip.isNull() == false )
        {
            //Iterate over clip fields:
306
            QString                     uuid;
307 308
            QString                     begin;
            QString                     end;
309 310 311
            QString                     startFrame;

            uuid = clip.attribute( "uuid" );
312 313
            begin = clip.attribute( "begin" );
            end = clip.attribute( "end" );
314 315 316
            startFrame = clip.attribute( "startFrame" );

            if ( uuid.isEmpty() == true || startFrame.isEmpty() == true )
317
            {
318 319
                qWarning() << "Invalid clip node";
                return ;
320 321
            }

322 323
            Clip* c = Library::getInstance()->clip( uuid );
            if ( c != NULL )
324
            {
325 326
                addClip( new Clip( c, begin.toLongLong(), end.toLongLong() ),
                         trackId, startFrame.toLongLong(), type );
327
            }
328 329 330 331
            clip = clip.nextSibling().toElement();
        }
        elem = elem.nextSibling().toElement();
    }
332 333
}

334
void
335
MainWorkflow::saveProject( QXmlStreamWriter& project ) const
336
{
337
    project.writeStartElement( "timeline" );
338
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
339
    {
340
        m_tracks[i]->save( project );
341
    }
342
    project.writeEndElement();
343
}
344

345 346
void
MainWorkflow::clear()
347
{
348
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
349
        m_tracks[i]->clear();
350
    emit cleared();
351
}
352

353 354
void
MainWorkflow::tracksEndReached()
355 356
{
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
357
    {
358 359
        if ( m_tracks[i]->endIsReached() == false )
            return ;
360
    }
361 362 363
    emit mainWorkflowEndReached();
}

364 365
int
MainWorkflow::getTrackCount( MainWorkflow::TrackType trackType ) const
366 367 368
{
    return m_tracks[trackType]->getTrackCount();
}
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
369

370 371
qint64
MainWorkflow::getCurrentFrame() const
372
{
373 374
    QReadLocker     lock( m_currentFrameLock );

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
375
    return m_currentFrame[MainWorkflow::VideoTrack];
376
}
377

378
quint32
379
MainWorkflow::getWidth() const
380
{
381
    Q_ASSERT( m_width != 0 );
382 383 384
    return m_width;
}

385
quint32
386
MainWorkflow::getHeight() const
387
{
388
    Q_ASSERT( m_height != 0 );
389 390
    return m_height;
}
391 392 393 394 395 396 397 398 399

void
MainWorkflow::renderOneFrame()
{
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
        m_tracks[i]->renderOneFrame();
    nextFrame( VideoTrack );
    nextFrame( AudioTrack );
}
400 401 402 403 404 405 406

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

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

    if ( newClip == NULL )
414
        newClip = new Clip( toSplit, newClipBegin, toSplit->end() );
415 416 417 418 419 420 421 422 423 424 425 426 427

    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 );

428
    if ( newBegin != clip->begin() )
429
    {
430
        moveClip( clip->uuid(), trackId, trackId, newPos, trackType, undoRedoAction );
431 432 433 434 435 436 437 438 439 440
    }
    clip->setBoundaries( newBegin, newEnd );
}

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

441 442
    removeClip( splitted->uuid(), trackId, trackType );
    origin->setEnd( splitted->end(), true );
443
}