MainWorkflow.cpp 13.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*****************************************************************************
 * MainWorkflow.cpp : Will query all of the track workflows to render the final
 *                    image
 *****************************************************************************
 * Copyright (C) 2008-2009 the VLMC team
 *
 * 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
        m_renderStarted( false )
43
{
44
    m_currentFrameLock = new QReadWriteLock;
45
    m_renderStartedLock = new QReadWriteLock;
46

47 48 49 50
    const SettingValue  *width = SettingsManager::getInstance()->getValue( "project",
                                                                    "VideoProjectWidth" );
    connect( width,     SIGNAL( changed( QVariant ) ),
             this,      SLOT( widthChanged( QVariant ) ) );
51
    m_width = width->get().toUInt();
52 53 54 55
    const SettingValue  *height = SettingsManager::getInstance()->getValue( "project",
                                                                "VideoProjectHeight" );
    connect( height, SIGNAL( changed( QVariant ) ),
             this, SLOT( widthChanged( QVariant ) ) );
56 57
    m_height = height->get().toUInt();

58 59 60
    m_effectEngine = new EffectsEngine;
    m_effectEngine->disable();

61
    m_tracks = new TrackHandler*[MainWorkflow::NbTrackType];
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
62
    m_currentFrame = new qint64[MainWorkflow::NbTrackType];
63
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
64
    {
65 66
        MainWorkflow::TrackType trackType =
                (i == 0 ? MainWorkflow::VideoTrack : MainWorkflow::AudioTrack );
67
        m_tracks[i] = new TrackHandler( trackCount, trackType, m_effectEngine );
68 69
        connect( m_tracks[i], SIGNAL( tracksEndReached() ),
                 this, SLOT( tracksEndReached() ) );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
70
	m_currentFrame[i] = 0;
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
71 72
    }
    m_outputBuffers = new OutputBuffers;
73 74 75

    blackOutput = new LightVideoFrame( m_width * m_height * Pixel::NbComposantes );
    memset( (*blackOutput)->frame.octets, 0, (*blackOutput)->nboctets );
76 77 78 79
}

MainWorkflow::~MainWorkflow()
{
80
    //FIXME: this is probably useless, since already done by the renderer
81 82
    stop();

83
    delete m_effectEngine;
84
    delete m_renderStartedLock;
85
    delete m_currentFrameLock;
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
86
    delete m_currentFrame;
87
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
88
        delete m_tracks[i];
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
89
    delete[] m_tracks;
90 91
}

92 93
EffectsEngine*
MainWorkflow::getEffectsEngine()
94
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
95
    return m_effectEngine;
96
}
97

98 99
void
MainWorkflow::addClip( Clip *clip, unsigned int trackId,
100
                                        qint64 start, MainWorkflow::TrackType trackType )
101
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
102
    m_tracks[trackType]->addClip( clip, trackId, start );
103
    computeLength();
104
    //Inform the GUI
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
105
    emit clipAdded( clip, trackId, start, trackType );
106
}
107

108 109
void
MainWorkflow::computeLength()
110
{
111 112
    qint64      maxLength = 0;

113
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
114 115 116 117
    {
        if ( m_tracks[i]->getLength() > maxLength )
            maxLength = m_tracks[i]->getLength();
    }
118
    m_lengthFrame = maxLength;
119 120
}

121 122
void
MainWorkflow::startRender()
123 124
{
    m_renderStarted = true;
125
    m_paused = false;
126
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
127
        m_tracks[i]->startRender();
128 129 130
    computeLength();
}

131 132
MainWorkflow::OutputBuffers*
MainWorkflow::getOutput( TrackType trackType )
133
{
134
    QReadLocker         lock( m_renderStartedLock );
135

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

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

160 161
void
MainWorkflow::pause()
162
{
163 164
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
        m_tracks[i]->pause();
165
    emit mainWorkflowPaused();
166 167
}

168 169
void
MainWorkflow::unpause()
170
{
171 172
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
        m_tracks[i]->unpause();
173
    emit mainWorkflowUnpaused();
174 175
}

176 177
void
MainWorkflow::goToNextFrame( MainWorkflow::TrackType trackType )
178 179
{
    if ( m_paused == false )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
180
        nextFrame( trackType );
181 182
}

183 184
void
MainWorkflow::nextFrame( MainWorkflow::TrackType trackType )
185
{
186
    QWriteLocker    lock( m_currentFrameLock );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
187

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
188 189 190
    ++m_currentFrame[trackType];
    if ( trackType == MainWorkflow::VideoTrack )
        emit frameChanged( m_currentFrame[MainWorkflow::VideoTrack], Renderer );
191 192
}

193 194
void
MainWorkflow::previousFrame( MainWorkflow::TrackType trackType )
195
{
196
    QWriteLocker    lock( m_currentFrameLock );
197

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
198 199 200
    --m_currentFrame[trackType];
    if ( trackType == MainWorkflow::VideoTrack )
        emit frameChanged( m_currentFrame[MainWorkflow::VideoTrack], Renderer );
201 202
}

203 204
qint64
MainWorkflow::getLengthFrame() const
205
{
206
    return m_lengthFrame;
207
}
208

209 210 211
qint64
MainWorkflow::getClipPosition( const QUuid& uuid, unsigned int trackId,
                               MainWorkflow::TrackType trackType ) const
212
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
213
    return m_tracks[trackType]->getClipPosition( uuid, trackId );
214
}
215

216 217
void
MainWorkflow::stop()
218 219
{
    QWriteLocker    lock( m_renderStartedLock );
220
    QWriteLocker    lock2( m_currentFrameLock );
221 222

    m_renderStarted = false;
223
    for (unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i)
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
224
    {
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
225
        m_tracks[i]->stop();
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
226 227
        m_currentFrame[i] = 0;
    }
228
    emit frameChanged( 0, Renderer );
229
}
230

231 232 233 234 235
void
MainWorkflow::moveClip( const QUuid &clipUuid, unsigned int oldTrack,
                        unsigned int newTrack, qint64 startingFrame,
                        MainWorkflow::TrackType trackType,
                        bool undoRedoCommand /*= false*/ )
236
{
237
    m_tracks[trackType]->moveClip( clipUuid, oldTrack, newTrack, startingFrame );
238
    computeLength();
239

240 241 242 243
    if ( undoRedoCommand == true )
    {
        emit clipMoved( clipUuid, newTrack, startingFrame, trackType );
    }
244
}
245

246 247 248
Clip*
MainWorkflow::removeClip( const QUuid &uuid, unsigned int trackId,
                          MainWorkflow::TrackType trackType )
249
{
250
    Clip *clip = m_tracks[trackType]->removeClip( uuid, trackId );
251
    computeLength();
252
    emit clipRemoved( clip, trackId, trackType );
253
    return clip;
254 255
}

256 257
void
MainWorkflow::muteTrack( unsigned int trackId, MainWorkflow::TrackType trackType )
258
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
259
    m_tracks[trackType]->muteTrack( trackId );
260 261
}

262 263
void
MainWorkflow::unmuteTrack( unsigned int trackId, MainWorkflow::TrackType trackType )
264
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
265
    m_tracks[trackType]->unmuteTrack( trackId );
266
}
267

268 269 270 271
void toggleBreakPoint()
{
}

272 273
void
MainWorkflow::setCurrentFrame( qint64 currentFrame, MainWorkflow::FrameChangedReason reason )
274
{
275 276
    QWriteLocker    lock( m_currentFrameLock );

277
    toggleBreakPoint();
278 279 280 281 282 283 284
    if ( m_renderStarted == true )
    {
        //Since any track can be reactivated, we reactivate all of them, and let them
        //unable themself if required.
        for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i)
            m_tracks[i]->activateAll();
    }
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
285 286 287
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i)
        m_currentFrame[i] = currentFrame;
    emit frameChanged( currentFrame, reason );
288
}
289

290 291 292
Clip*
MainWorkflow::getClip( const QUuid &uuid, unsigned int trackId,
                       MainWorkflow::TrackType trackType )
293
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
294
    return m_tracks[trackType]->getClip( uuid, trackId );
295
}
296

297
/**
298
 *  \warning    The mainworkflow is expected to be already cleared by the ProjectManager
299
 */
300 301
void
MainWorkflow::loadProject( const QDomElement &project )
302
{
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
    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;
331
            MainWorkflow::TrackType     trackType = MainWorkflow::VideoTrack;
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 362 363 364 365 366 367 368

            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" )
                {
369 370
                    trackType = static_cast<MainWorkflow::TrackType>(
                                                    clipProperty.text().toUInt( &ok ) );
371 372 373 374 375 376 377 378 379 380 381 382
                    if ( ok == false )
                    {
                        qWarning() << "Invalid track type starting frame";
                        return ;
                    }
                }
                else
                    qDebug() << "Unknown field" << clipProperty.tagName();

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

383
            if ( Library::getInstance()->media( parent ) != NULL )
384
            {
385
                Clip        *c = new Clip( parent, begin, end );
386 387
                addClip( c, trackId, startPos, trackType );
            }
388 389 390 391 392

            clip = clip.nextSibling().toElement();
        }
        elem = elem.nextSibling().toElement();
    }
393 394
}

395 396
void
MainWorkflow::saveProject( QDomDocument& doc, QDomElement& rootNode )
397
{
398
    QDomElement project = doc.createElement( "timeline" );
399
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
400 401 402 403
    {
        m_tracks[i]->save( doc, project );
    }
    rootNode.appendChild( project );
404
}
405

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

414 415
void
MainWorkflow::tracksEndReached()
416 417
{
    for ( unsigned int i = 0; i < MainWorkflow::NbTrackType; ++i )
418
    {
419 420
        if ( m_tracks[i]->endIsReached() == false )
            return ;
421
    }
422 423 424
    emit mainWorkflowEndReached();
}

425 426
int
MainWorkflow::getTrackCount( MainWorkflow::TrackType trackType ) const
427 428 429
{
    return m_tracks[trackType]->getTrackCount();
}
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
430

431 432
qint64
MainWorkflow::getCurrentFrame() const
433
{
434 435
    QReadLocker     lock( m_currentFrameLock );

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
436
    return m_currentFrame[MainWorkflow::VideoTrack];
437
}
438

439 440
void
MainWorkflow::widthChanged( const QVariant &width )
441 442 443 444
{
    m_width = width.toUInt();
}

445 446
void
MainWorkflow::heightChanged( const QVariant &height )
447 448 449 450
{
    m_height = height.toUInt();
}

451 452
uint32_t
MainWorkflow::getWidth() const
453 454 455 456
{
    return m_width;
}

457 458
uint32_t
MainWorkflow::getHeight() const
459 460 461
{
    return m_height;
}