MainWorkflow.cpp 14.3 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

38
LightVideoFrame     *MainWorkflow::blackOutput = NULL;
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_effectEngine = new EffectsEngine;
    m_effectEngine->disable();

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

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

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

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

101
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
102 103 104 105
    {
        if ( m_tracks[i]->getLength() > maxLength )
            maxLength = m_tracks[i]->getLength();
    }
106 107 108 109 110 111
    if ( m_lengthFrame != maxLength )
    {
        m_lengthFrame = maxLength;
        emit lengthChanged( m_lengthFrame );
    }

112 113
}

114
void
115
MainWorkflow::startRender( quint32 width, quint32 height )
116 117
{
    m_renderStarted = true;
118 119
    m_width = width;
    m_height = height;
120 121 122 123 124
    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 );
125
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
126
        m_tracks[i]->startRender();
127 128 129
    computeLength();
}

130
MainWorkflow::OutputBuffers*
131
MainWorkflow::getOutput( TrackType trackType, bool paused )
132
{
133
    QMutexLocker        lock( m_renderStartedMutex );
134

135
    if ( m_renderStarted == true )
136
    {
137
        QReadLocker         lock2( m_currentFrameLock );
138

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

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

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

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

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

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

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

192 193
void
MainWorkflow::stop()
194
{
195
    QMutexLocker    lock( m_renderStartedMutex );
196
    QWriteLocker    lock2( m_currentFrameLock );
197 198

    m_renderStarted = false;
199
    for (unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i)
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
200
    {
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
201
        m_tracks[i]->stop();
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
202 203
        m_currentFrame[i] = 0;
    }
204
    emit frameChanged( 0, Renderer );
205
}
206

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

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

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

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

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

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

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

261 262 263 264
void toggleBreakPoint()
{
}

265 266
void
MainWorkflow::setCurrentFrame( qint64 currentFrame, MainWorkflow::FrameChangedReason reason )
267
{
268 269
    QWriteLocker    lock( m_currentFrameLock );

270
    toggleBreakPoint();
271 272 273
    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
274
        //disable themself if required.
275 276 277
        for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i)
            m_tracks[i]->activateAll();
    }
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
278 279 280
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i)
        m_currentFrame[i] = currentFrame;
    emit frameChanged( currentFrame, reason );
281
}
282

283 284 285
Clip*
MainWorkflow::getClip( const QUuid &uuid, unsigned int trackId,
                       MainWorkflow::TrackType trackType )
286
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
287
    return m_tracks[trackType]->getClip( uuid, trackId );
288
}
289

290
/**
291
 *  \warning    The mainworkflow is expected to be already cleared by the ProjectManager
292
 */
293 294
void
MainWorkflow::loadProject( const QDomElement &project )
295
{
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
    if ( project.isNull() == true || project.tagName() != "timeline" )
    {
        qWarning() << "Invalid timeline node (" << project.tagName() << ')';
        return ;
    }

    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;
324
            MainWorkflow::TrackType     trackType = MainWorkflow::VideoTrack;
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361

            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" )
                {
362 363
                    trackType = static_cast<MainWorkflow::TrackType>(
                                                    clipProperty.text().toUInt( &ok ) );
364 365 366 367 368 369 370 371 372 373 374 375
                    if ( ok == false )
                    {
                        qWarning() << "Invalid track type starting frame";
                        return ;
                    }
                }
                else
                    qDebug() << "Unknown field" << clipProperty.tagName();

                clipProperty = clipProperty.nextSibling().toElement();
            }

376
            if ( Library::getInstance()->media( parent ) != NULL )
377
            {
378
                Clip        *c = new Clip( parent, begin, end );
379 380
                addClip( c, trackId, startPos, trackType );
            }
381 382 383 384 385

            clip = clip.nextSibling().toElement();
        }
        elem = elem.nextSibling().toElement();
    }
386 387
}

388 389
void
MainWorkflow::saveProject( QDomDocument& doc, QDomElement& rootNode )
390
{
391
    QDomElement project = doc.createElement( "timeline" );
392
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
393 394 395 396
    {
        m_tracks[i]->save( doc, project );
    }
    rootNode.appendChild( project );
397
}
398

399 400
void
MainWorkflow::clear()
401
{
402
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
403
        m_tracks[i]->clear();
404
    emit cleared();
405
}
406

407 408
void
MainWorkflow::tracksEndReached()
409 410
{
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
411
    {
412 413
        if ( m_tracks[i]->endIsReached() == false )
            return ;
414
    }
415 416 417
    emit mainWorkflowEndReached();
}

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

424 425
qint64
MainWorkflow::getCurrentFrame() const
426
{
427 428
    QReadLocker     lock( m_currentFrameLock );

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
429
    return m_currentFrame[MainWorkflow::VideoTrack];
430
}
431

432
quint32
433
MainWorkflow::getWidth() const
434
{
435
    Q_ASSERT( m_width != 0 );
436 437 438
    return m_width;
}

439
quint32
440
MainWorkflow::getHeight() const
441
{
442
    Q_ASSERT( m_height != 0 );
443 444
    return m_height;
}
445 446 447 448 449 450 451 452 453

void
MainWorkflow::renderOneFrame()
{
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
        m_tracks[i]->renderOneFrame();
    nextFrame( VideoTrack );
    nextFrame( AudioTrack );
}
454 455 456 457 458 459 460

void
MainWorkflow::setFullSpeedRender( bool val )
{
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
        m_tracks[i]->setFullSpeedRender( val );
}
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497

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

    if ( newClip == NULL )
        newClip = new Clip( toSplit, newClipBegin, toSplit->getEnd() );

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

    if ( newBegin != clip->getBegin() )
    {
        moveClip( clip->getUuid(), trackId, trackId, newPos, trackType, undoRedoAction );
    }
    clip->setBoundaries( newBegin, newEnd );
}

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

    removeClip( splitted->getUuid(), trackId, trackType );
    origin->setEnd( splitted->getEnd(), true );
}