WorkflowRenderer.cpp 11.6 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
    m_esHandler->fps = fps;
94 95


96 97 98 99
    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() );
100 101
}

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

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

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

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

145
    ret = static_cast<const Workflow::Frame*>( m_mainWorkflow->getOutput( Workflow::VideoTrack, m_paused ) );
146
    ptsDiff = ret->ptsDiff;
147 148 149 150 151
    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)
152
        ptsDiff = 1000000 / handler->fps;
153
    }
154 155
    m_effectFrame = applyFilters( ret, m_mainWorkflow->getCurrentFrame(),
                                      m_mainWorkflow->getCurrentFrame() * 1000.0 / handler->fps );
156
    m_pts = *pts = ptsDiff + m_pts;
157 158 159 160
    if ( m_effectFrame != NULL )
        *buffer = m_effectFrame;
    else
        *buffer = ret->buffer();
161
    *bufferSize = ret->size();
162
    vlmcDebug() << __func__ << "Rendered frame. pts:" << m_pts;
163 164 165
    return 0;
}

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

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

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

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

226
    setupRenderer( m_width, m_height, m_outputFps );
227

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

238 239
void
WorkflowRenderer::nextFrame()
240
{
241 242
    if ( m_paused == true )
        m_mainWorkflow->renderOneFrame();
243 244
}

245 246
void
WorkflowRenderer::previousFrame()
247
{
248
    if ( m_paused == true )
249
        m_mainWorkflow->previousFrame( Workflow::VideoTrack );
250 251
}

252
void
253
WorkflowRenderer::togglePlayPause()
254
{
255
    if ( m_isRendering == false )
256
        startPreview();
257
    else
258
        m_paused = !m_paused;
259
}
260

261 262
void
WorkflowRenderer::stop()
263
{
264

265
    m_isRendering = false;
266
    m_paused = false;
267
    m_stopping = true;
268
    m_mainWorkflow->stopFrameComputing();
269 270
    if ( m_sourceRenderer != NULL )
        m_sourceRenderer->stop();
271
    m_mainWorkflow->stop();
272 273
    delete[] m_silencedAudioBuffer;
    m_silencedAudioBuffer = NULL;
274 275
}

276 277 278
int
WorkflowRenderer::getVolume() const
{
279
    return m_sourceRenderer->volume();
280 281
}

282
void WorkflowRenderer::setVolume( int volume )
283 284
{
    //Returns 0 if the volume was set, -1 if it was out of range
285
    m_sourceRenderer->setVolume( volume );
286 287 288 289
}

qint64
WorkflowRenderer::getCurrentFrame() const
290 291 292 293
{
    return m_mainWorkflow->getCurrentFrame();
}

294 295
qint64
WorkflowRenderer::length() const
296 297 298 299
{
    return qRound64( (qreal)getLengthMs() / 1000.0 * (qreal)getFps() );
}

300 301
qint64
WorkflowRenderer::getLengthMs() const
302
{
303
    return m_mainWorkflow->getLengthFrame() / getFps() * 1000;
304 305
}

306 307
float
WorkflowRenderer::getFps() const
308
{
309
    return m_outputFps;
310 311
}

312 313
void
WorkflowRenderer::timelineCursorChanged( qint64 newFrame )
314
{
315
    m_mainWorkflow->setCurrentFrame( newFrame, Vlmc::TimelineCursor );
316 317
}

318 319
void
WorkflowRenderer::previewWidgetCursorChanged( qint64 newFrame )
320
{
321
    m_mainWorkflow->setCurrentFrame( newFrame, Vlmc::PreviewCursor );
322 323
}

324 325
void
WorkflowRenderer::rulerCursorChanged( qint64 newFrame )
326
{
327
    m_mainWorkflow->setCurrentFrame( newFrame, Vlmc::RulerCursor );
328 329
}

330
Backend::ISourceRenderer::MemoryInputLockCallback WorkflowRenderer::getLockCallback()
331
{
332
    return &WorkflowRenderer::lock;
333 334
}

335
Backend::ISourceRenderer::MemoryInputUnlockCallback WorkflowRenderer::getUnlockCallback()
336
{
337
    return WorkflowRenderer::unlock;
338 339
}

340 341 342
quint32
WorkflowRenderer::width() const
{
343
    return VLMC_PROJECT_GET_UINT( "video/VideoProjectWidth" );
344 345 346 347 348
}

quint32
WorkflowRenderer::height() const
{
349
    return VLMC_PROJECT_GET_UINT( "video/VideoProjectHeight" );
350 351
}

352 353 354
float
WorkflowRenderer::outputFps() const
{
355
    return VLMC_PROJECT_GET_DOUBLE( "video/VLMCOutputFPS" );
356 357
}

358 359 360 361 362 363
const QString
WorkflowRenderer::aspectRatio() const
{
    return VLMC_PROJECT_GET_STRING("video/AspectRatio");
}

364
bool
365
WorkflowRenderer::paramsHasChanged( quint32 width, quint32 height, double fps, QString aspect )
366
{
367 368
    quint32             newWidth = this->width();
    quint32             newHeight = this->height();
369
    float               newOutputFps = outputFps();
370
    const QString       newAspectRatio = aspectRatio();
371

372
    return ( newWidth != width || newHeight != height ||
373
         newOutputFps != fps || newAspectRatio != aspect );
374 375
}

376 377 378 379
void
WorkflowRenderer::saveProject( QXmlStreamWriter &project ) const
{
    project.writeStartElement( "renderer" );
380
    saveFilters( project );
381 382 383
    project.writeEndElement();
}

384 385 386 387 388 389
void
WorkflowRenderer::loadProject( const QDomElement &project )
{
    QDomElement     renderer = project.firstChildElement( "renderer" );
    if ( renderer.isNull() == true )
        return ;
390
    loadEffects( renderer );
391 392
}

393 394 395 396
/////////////////////////////////////////////////////////////////////
/////SLOTS :
/////////////////////////////////////////////////////////////////////

397
void
398
WorkflowRenderer::mainWorkflowLenghtChanged( qint64 /*newLength*/ )
399
{
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
//    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;
415
}