WorkflowRenderer.cpp 11.9 KB
Newer Older
1
/*****************************************************************************
2
 * WorkflowRenderer.cpp: Allow a current workflow preview
3
 *****************************************************************************
Ludovic Fauvet's avatar
Ludovic Fauvet committed
4
 * Copyright (C) 2008-2010 VideoLAN
5
 *
6
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * 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.
 *****************************************************************************/

23
//Allow PRId64 to be defined:
24
#define __STDC_FORMAT_MACROS
25 26

#include "WorkflowRenderer.h"
27

28
#include "Clip.h"
29
#include "EffectInstance.h"
30
#include "GenericRenderer.h"
31 32
#include "IBackend.h"
#include "ISource.h"
33
#include "MainWorkflow.h"
34
#include "RenderWidget.h"
35 36
#include "SettingsManager.h"
#include "VLCMedia.h"
37
#include "VLCMediaPlayer.h"
38
#include "VlmcDebug.h"
39 40
#include "Workflow/Types.h"
#include "timeline/Timeline.h"
41

42 43 44 45 46
#include <QDomElement>
#include <QThread>
#include <QWaitCondition>
#include <inttypes.h>

47
WorkflowRenderer::WorkflowRenderer( Backend::IBackend* backend ) :
48
            m_mainWorkflow( MainWorkflow::getInstance() ),
49
            m_stopping( false ),
50
            m_outputFps( 0.0f ),
51
            m_aspectRatio( "" ),
52
            m_silencedAudioBuffer( NULL ),
53
            m_esHandler( NULL ),
54 55
            m_oldLength( 0 ),
            m_effectFrame( NULL )
56
{
57
    m_source = backend->createMemorySource();
58 59
}

60 61
void
WorkflowRenderer::initializeRenderer()
62
{
63 64
    m_esHandler = new EsHandler;
    m_esHandler->self = this;
65

66
    m_nbChannels = 2;
67
    m_rate = 48000;
68

69 70
    connect( m_mainWorkflow, SIGNAL( frameChanged( qint64, Vlmc::FrameChangedReason ) ),
             this, SIGNAL( frameChanged( qint64, Vlmc::FrameChangedReason ) ) );
71 72
    connect( m_mainWorkflow, SIGNAL( lengthChanged( qint64 ) ),
             this, SLOT(mainWorkflowLenghtChanged(qint64) ) );
73 74
}

75
WorkflowRenderer::~WorkflowRenderer()
76
{
77
    stop();
78

79 80 81
    delete m_esHandler;
    delete m_silencedAudioBuffer;
    delete m_source;
82 83
}

84
void
85
WorkflowRenderer::setupRenderer( quint32 width, quint32 height, double fps )
86
{
87 88 89 90 91 92
    m_source->setWidth( width );
    m_source->setHeight( height );
    m_source->setFps( fps );
    m_source->setAspectRatio( qPrintable( aspectRatio() ) );
    m_source->setNumberChannels( m_nbChannels );
    m_source->setSampleRate( m_rate );
93 94


95 96 97 98
    delete m_sourceRenderer;
    m_sourceRenderer = m_source->createRenderer( m_eventWatcher );
    m_sourceRenderer->enableMemoryInput( m_esHandler, getLockCallback(), getUnlockCallback() );
    m_sourceRenderer->setOutputWidget( (void *) static_cast< RenderWidget* >( m_renderWidget )->id() );
99 100
}

101
int
102 103
WorkflowRenderer::lock( void *data, const char* cookie, int64_t *dts, int64_t *pts,
                        unsigned int *flags, size_t *bufferSize, const void **buffer )
104
{
105
    int             ret = 1;
106
    EsHandler*      handler = reinterpret_cast<EsHandler*>( data );
107
    bool            paused = handler->self->m_paused;
108

109 110
    *dts = -1;
    *flags = 0;
111 112 113
    if ( cookie == NULL || ( cookie[0] != WorkflowRenderer::VideoCookie &&
                             cookie[0] != WorkflowRenderer::AudioCookie ) )
    {
114
        vlmcCritical() << "Invalid imem input cookie";
115 116 117
        return ret;
    }
    if ( cookie[0] == WorkflowRenderer::VideoCookie )
118
    {
119
        ret = handler->self->lockVideo( handler, pts, bufferSize, buffer );
120
        if ( paused == false )
121
            handler->self->m_mainWorkflow->nextFrame( Workflow::VideoTrack );
122
    }
123
    else if ( cookie[0] == WorkflowRenderer::AudioCookie )
124
    {
125
        ret = handler->self->lockAudio( handler, pts, bufferSize, buffer );
126
        if ( paused == false )
127
            handler->self->m_mainWorkflow->nextFrame( Workflow::AudioTrack );
128 129
    }
    else
130
        vlmcCritical() << "Invalid imem cookie";
131
    return ret;
132 133
}

134
int
135
WorkflowRenderer::lockVideo( void* data, int64_t *pts, size_t *bufferSize, const void **buffer )
136
{
137 138 139
    EsHandler*              handler = reinterpret_cast<EsHandler*>( data );
    qint64                  ptsDiff = 0;
    const Workflow::Frame   *ret;
140

141 142
    if ( m_stopping == true )
        return 1;
143

144
    ret = static_cast<const Workflow::Frame*>( m_mainWorkflow->getOutput( Workflow::VideoTrack, m_paused ) );
145
    ptsDiff = ret->ptsDiff;
146 147 148 149 150
    if ( ptsDiff == 0 )
    {
        //If no ptsDiff has been computed, we have to fake it, so we compute
        //the theorical pts for one frame.
        //this is a bit hackish though... (especially regarding the "no frame computed" detection)
151
        ptsDiff = 1000000 / handler->fps;
152
    }
153 154
    m_effectFrame = applyFilters( ret, m_mainWorkflow->getCurrentFrame(),
                                      m_mainWorkflow->getCurrentFrame() * 1000.0 / handler->fps );
155
    m_pts = *pts = ptsDiff + m_pts;
156 157 158 159
    if ( m_effectFrame != NULL )
        *buffer = m_effectFrame;
    else
        *buffer = ret->buffer();
160
    *bufferSize = ret->size();
161 162 163
    return 0;
}

164
int
165
WorkflowRenderer::lockAudio( EsHandler *handler, int64_t *pts, size_t *bufferSize, const void ** buffer )
166
{
167
    qint64                              ptsDiff;
168
    quint32                             nbSample;
169
    const Workflow::AudioSample         *renderAudioSample;
170

171
    if ( m_stopping == false && m_paused == false )
172
    {
173
        renderAudioSample = static_cast<const Workflow::AudioSample*>( m_mainWorkflow->getOutput( Workflow::AudioTrack,
174
                                                                                           m_paused ) );
175
    }
176 177 178
    else
        renderAudioSample = NULL;
    if ( renderAudioSample != NULL )
179
    {
180
//        vlmcDebug() << "pts diff:" << renderAudioSample->ptsDiff;
181 182 183 184
        nbSample = renderAudioSample->nbSample;
        *buffer = renderAudioSample->buff;
        *bufferSize = renderAudioSample->size;
        ptsDiff = renderAudioSample->ptsDiff;
185 186 187
    }
    else
    {
188
        nbSample = m_rate / handler->fps;
189
        unsigned int    buffSize = m_nbChannels * 2 * nbSample;
190 191 192 193
        if ( m_silencedAudioBuffer == NULL )
            m_silencedAudioBuffer = new uint8_t[ buffSize ];
        memset( m_silencedAudioBuffer, 0, buffSize );
        *buffer = m_silencedAudioBuffer;
194
        *bufferSize = buffSize;
195
        ptsDiff = m_pts - m_audioPts;
196
    }
197
    m_audioPts = *pts = m_audioPts + ptsDiff;
198
    return 0;
199 200
}

201
void
202
WorkflowRenderer::unlock( void *data, const char*, size_t, void* )
203
{
204
    EsHandler*      handler = reinterpret_cast<EsHandler*>( data );
205 206
    delete[] handler->self->m_effectFrame;
    handler->self->m_effectFrame = NULL;
207 208
}

209 210
void
WorkflowRenderer::startPreview()
211
{
212
    if ( m_mainWorkflow->getLengthFrame() <= 0 )
213
        return ;
214
    if ( paramsHasChanged( m_width, m_height, m_outputFps, m_aspectRatio ) == true )
215 216 217 218
    {
        m_width = width();
        m_height = height();
        m_outputFps = outputFps();
219
        m_aspectRatio = aspectRatio();
220
    }
221
    initFilters();
222

223
    setupRenderer( m_width, m_height, m_outputFps );
224

225
    m_mainWorkflow->setFullSpeedRender( false );
226
    m_mainWorkflow->startRender( m_width, m_height, m_outputFps );
227
    m_isRendering = true;
228
    m_paused = false;
229
    m_stopping = false;
230
    m_pts = 0;
231
    m_audioPts = 0;
232
    m_sourceRenderer->start();
233 234
}

235 236
void
WorkflowRenderer::nextFrame()
237
{
238 239
    if ( m_paused == true )
        m_mainWorkflow->renderOneFrame();
240 241
}

242 243
void
WorkflowRenderer::previousFrame()
244
{
245
    if ( m_paused == true )
246
        m_mainWorkflow->previousFrame( Workflow::VideoTrack );
247 248
}

249 250
void
WorkflowRenderer::togglePlayPause( bool forcePause )
251
{
252
    if ( m_isRendering == false && forcePause == false )
253
        startPreview();
254 255 256 257
    else
        internalPlayPause( forcePause );
}

258 259
void
WorkflowRenderer::internalPlayPause( bool forcePause )
260 261 262
{
    //If force pause is true, we just ensure that this render is paused... no need to start it.
    if ( m_isRendering == true )
263
    {
264 265
        if ( m_paused == true && forcePause == false )
        {
266
            m_paused = false;
267
        }
268
        else
269
        {
270 271
            if ( m_paused == false )
            {
272
                m_paused = true;
273
            }
274
        }
275
    }
276
}
277

278 279
void
WorkflowRenderer::stop()
280
{
281

282
    m_isRendering = false;
283
    m_paused = false;
284
    m_stopping = true;
285
    m_mainWorkflow->stopFrameComputing();
286 287
    if ( m_sourceRenderer != NULL )
        m_sourceRenderer->stop();
288
    m_mainWorkflow->stop();
289 290
    delete[] m_silencedAudioBuffer;
    m_silencedAudioBuffer = NULL;
291 292
}

293 294 295
int
WorkflowRenderer::getVolume() const
{
296
    return m_sourceRenderer->volume();
297 298
}

299
void WorkflowRenderer::setVolume( int volume )
300 301
{
    //Returns 0 if the volume was set, -1 if it was out of range
302
    m_sourceRenderer->setVolume( volume );
303 304 305 306
}

qint64
WorkflowRenderer::getCurrentFrame() const
307 308 309 310
{
    return m_mainWorkflow->getCurrentFrame();
}

311 312
qint64
WorkflowRenderer::length() const
313 314 315 316
{
    return qRound64( (qreal)getLengthMs() / 1000.0 * (qreal)getFps() );
}

317 318
qint64
WorkflowRenderer::getLengthMs() const
319
{
320
    return m_mainWorkflow->getLengthFrame() / getFps() * 1000;
321 322
}

323 324
float
WorkflowRenderer::getFps() const
325
{
326
    return m_outputFps;
327 328
}

329 330
void
WorkflowRenderer::timelineCursorChanged( qint64 newFrame )
331
{
332
    m_mainWorkflow->setCurrentFrame( newFrame, Vlmc::TimelineCursor );
333 334
}

335 336
void
WorkflowRenderer::previewWidgetCursorChanged( qint64 newFrame )
337
{
338
    m_mainWorkflow->setCurrentFrame( newFrame, Vlmc::PreviewCursor );
339 340
}

341 342
void
WorkflowRenderer::rulerCursorChanged( qint64 newFrame )
343
{
344
    m_mainWorkflow->setCurrentFrame( newFrame, Vlmc::RulerCursor );
345 346
}

347
Backend::ISourceRenderer::MemoryInputLockCallback WorkflowRenderer::getLockCallback()
348
{
349
    return &WorkflowRenderer::lock;
350 351
}

352
Backend::ISourceRenderer::MemoryInputUnlockCallback WorkflowRenderer::getUnlockCallback()
353
{
354
    return WorkflowRenderer::unlock;
355 356
}

357 358 359
quint32
WorkflowRenderer::width() const
{
360
    return VLMC_PROJECT_GET_UINT( "video/VideoProjectWidth" );
361 362 363 364 365
}

quint32
WorkflowRenderer::height() const
{
366
    return VLMC_PROJECT_GET_UINT( "video/VideoProjectHeight" );
367 368
}

369 370 371
float
WorkflowRenderer::outputFps() const
{
372
    return VLMC_PROJECT_GET_DOUBLE( "video/VLMCOutputFPS" );
373 374
}

375 376 377 378 379 380
const QString
WorkflowRenderer::aspectRatio() const
{
    return VLMC_PROJECT_GET_STRING("video/AspectRatio");
}

381
bool
382
WorkflowRenderer::paramsHasChanged( quint32 width, quint32 height, double fps, QString aspect )
383
{
384 385
    quint32             newWidth = this->width();
    quint32             newHeight = this->height();
386
    float               newOutputFps = outputFps();
387
    const QString       newAspectRatio = aspectRatio();
388

389
    return ( newWidth != width || newHeight != height ||
390
         newOutputFps != fps || newAspectRatio != aspect );
391 392
}

393 394 395 396
void
WorkflowRenderer::saveProject( QXmlStreamWriter &project ) const
{
    project.writeStartElement( "renderer" );
397
    saveFilters( project );
398 399 400
    project.writeEndElement();
}

401 402 403 404 405 406
void
WorkflowRenderer::loadProject( const QDomElement &project )
{
    QDomElement     renderer = project.firstChildElement( "renderer" );
    if ( renderer.isNull() == true )
        return ;
407
    loadEffects( renderer );
408 409
}

410 411 412 413
/////////////////////////////////////////////////////////////////////
/////SLOTS :
/////////////////////////////////////////////////////////////////////

414
void
415
WorkflowRenderer::mainWorkflowLenghtChanged( qint64 /*newLength*/ )
416
{
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
//    if ( newLength > 0 )
//    {
//        if ( m_oldLength == 0 )
//        {
//            if ( m_isRendering == false )
//                startPreview();
//            m_paused = false;
//            togglePlayPause( true );
//        }
//    }
//    else if ( newLength == 0 && m_isRendering == true )
//    {
//        stop();
//    }
//    m_oldLength = newLength;
432
}