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];
51
    m_currentFrame = new qint64[MainWorkflow::NbTrackType];
52
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
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;
60 61
    }
    m_outputBuffers = new OutputBuffers;
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 < MainWorkflow::NbTrackType; ++i )
70
        delete m_tracks[i];
71
    delete[] m_tracks;
72
    delete MainWorkflow::blackOutput;
73 74
}

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

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

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

101 102
}

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

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

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

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

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

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

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

161 162 163
    --m_currentFrame[trackType];
    if ( trackType == MainWorkflow::VideoTrack )
        emit frameChanged( m_currentFrame[MainWorkflow::VideoTrack], Renderer );
164 165
}

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

172 173 174
qint64
MainWorkflow::getClipPosition( const QUuid& uuid, unsigned int trackId,
                               MainWorkflow::TrackType trackType ) const
175
{
176
    return m_tracks[trackType]->getClipPosition( uuid, trackId );
177
}
178

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

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

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

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

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

222 223
void
MainWorkflow::muteTrack( unsigned int trackId, MainWorkflow::TrackType trackType )
224
{
225
    m_tracks[trackType]->muteTrack( trackId );
226 227
}

228 229
void
MainWorkflow::unmuteTrack( unsigned int trackId, MainWorkflow::TrackType trackType )
230
{
231
    m_tracks[trackType]->unmuteTrack( trackId );
232
}
233

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

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

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

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

265 266 267
Clip*
MainWorkflow::getClip( const QUuid &uuid, unsigned int trackId,
                       MainWorkflow::TrackType trackType )
268
{
269
    return m_tracks[trackType]->getClip( uuid, trackId );
270
}
271

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

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

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

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

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

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

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

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

365 366
int
MainWorkflow::getTrackCount( MainWorkflow::TrackType trackType ) const
367 368 369
{
    return m_tracks[trackType]->getTrackCount();
}
370

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

376
    return m_currentFrame[MainWorkflow::VideoTrack];
377
}
378

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

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

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

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

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

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

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

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

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

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