WorkflowRenderer.cpp 13.1 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 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * 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.
 *****************************************************************************/

23
#include <QtDebug>
24
#include <QThread>
25
#include <QWaitCondition>
26
//Allow PRId64 to be defined:
27 28
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
29

30
#include "Clip.h"
31
#include "EffectInstance.h"
32 33 34 35
#include "GenericRenderer.h"
#include "MainWorkflow.h"
#include "SettingsManager.h"
#include "VLCMedia.h"
36
#include "VLCMediaPlayer.h"
37 38 39
#include "WorkflowRenderer.h"
#include "Workflow/Types.h"
#include "timeline/Timeline.h"
40

41 42
WorkflowRenderer::WorkflowRenderer() :
            m_mainWorkflow( MainWorkflow::getInstance() ),
43
            m_media( NULL ),
44
            m_stopping( false ),
45
            m_outputFps( 0.0f ),
46
            m_width( 0 ),
47
            m_height( 0 ),
48
            m_silencedAudioBuffer( NULL ),
49
            m_esHandler( NULL ),
50
            m_oldLength( 0 )
51
{
52
    m_effectsLock = new QReadWriteLock;
53 54 55
}

void    WorkflowRenderer::initializeRenderer()
56
{
57 58
    m_esHandler = new EsHandler;
    m_esHandler->self = this;
59

60
    m_nbChannels = 2;
61
    m_rate = 48000;
62

63
     //Workflow part
64
    connect( m_mainWorkflow, SIGNAL( mainWorkflowEndReached() ), this, SLOT( __endReached() ), Qt::QueuedConnection );
65
    connect( m_mainWorkflow, SIGNAL( frameChanged( qint64, MainWorkflow::FrameChangedReason ) ),
66
             this, SIGNAL( frameChanged( qint64, MainWorkflow::FrameChangedReason ) ) );
67 68
    connect( m_mainWorkflow, SIGNAL( lengthChanged( qint64 ) ),
             this, SLOT(mainWorkflowLenghtChanged(qint64) ) );
69 70 71
    //Media player part: to update PreviewWidget
    connect( m_mediaPlayer, SIGNAL( playing() ),    this,   SIGNAL( playing() ), Qt::DirectConnection );
    connect( m_mediaPlayer, SIGNAL( paused() ),     this,   SIGNAL( paused() ), Qt::DirectConnection );
72
    connect( m_mediaPlayer, SIGNAL( errorEncountered() ), this, SLOT( errorEncountered() ) );
73 74
    //FIXME:: check if this doesn't require Qt::QueuedConnection
    connect( m_mediaPlayer, SIGNAL( stopped() ),    this,   SIGNAL( endReached() ) );
75 76
}

77
WorkflowRenderer::~WorkflowRenderer()
78
{
79
    killRenderer();
80

81 82
    if ( m_esHandler )
        delete m_esHandler;
83 84
    if ( m_media )
        delete m_media;
85 86
    if ( m_silencedAudioBuffer )
        delete m_silencedAudioBuffer;
87
    delete m_effectsLock;
88 89
}

90
void
91
WorkflowRenderer::setupRenderer( quint32 width, quint32 height, double fps )
92
{
93 94 95
    char        videoString[512];
    char        inputSlave[256];
    char        audioParameters[256];
96
    char        buffer[64];
97

98
    m_esHandler->fps = fps;
99
    //Clean any previous render.
100

101
    sprintf( videoString, "width=%i:height=%i:dar=%s:fps=%s:cookie=0:codec=%s:cat=2:caching=0",
102
             width, height, "16/9", "30/1", "RV32" );
103 104
    sprintf( audioParameters, "cookie=1:cat=1:codec=f32l:samplerate=%u:channels=%u:caching=0",
                m_rate, m_nbChannels );
105 106
    strcpy( inputSlave, ":input-slave=imem://" );
    strcat( inputSlave, audioParameters );
107 108 109

    if ( m_media != NULL )
        delete m_media;
110 111
    m_media = new LibVLCpp::Media( "imem://" + QString( videoString ) );
    m_media->addOption( inputSlave );
112

113
    sprintf( buffer, "imem-get=%"PRId64, (intptr_t)getLockCallback() );
114
    m_media->addOption( buffer );
115
    sprintf( buffer, ":imem-release=%"PRId64, (intptr_t)getUnlockCallback() );
116
    m_media->addOption( buffer );
117
    sprintf( buffer, ":imem-data=%"PRId64, (intptr_t)m_esHandler );
118
    m_media->addOption( buffer );
119 120 121
    m_media->addOption( ":text-renderer dummy" );
}

122
int
123
WorkflowRenderer::lock( void *datas, const char* cookie, qint64 *dts, qint64 *pts,
124
                        quint32 *flags, size_t *bufferSize, const void **buffer )
125
{
126
    int             ret = 1;
127 128
    EsHandler*      handler = reinterpret_cast<EsHandler*>( datas );
    bool            paused = handler->self->m_paused;
129

130 131
    *dts = -1;
    *flags = 0;
132 133 134 135 136 137 138
    if ( cookie == NULL || ( cookie[0] != WorkflowRenderer::VideoCookie &&
                             cookie[0] != WorkflowRenderer::AudioCookie ) )
    {
        qCritical() << "Invalid imem input cookie";
        return ret;
    }
    if ( cookie[0] == WorkflowRenderer::VideoCookie )
139
    {
140
        ret = handler->self->lockVideo( handler, pts, bufferSize, buffer );
141
        if ( paused == false )
142
            handler->self->m_mainWorkflow->nextFrame( MainWorkflow::VideoTrack );
143
    }
144
    else if ( cookie[0] == WorkflowRenderer::AudioCookie )
145
    {
146
        ret = handler->self->lockAudio( handler, pts, bufferSize, buffer );
147
        if ( paused == false )
148
            handler->self->m_mainWorkflow->nextFrame( MainWorkflow::AudioTrack );
149 150
    }
    else
151
        qCritical() << "Invalid imem cookie";
152
    return ret;
153 154
}

155
int
156
WorkflowRenderer::lockVideo( EsHandler *handler, qint64 *pts, size_t *bufferSize, const void **buffer )
157
{
158 159
    qint64                          ptsDiff = 0;
    MainWorkflow::OutputBuffers*    ret;
160

161 162
    if ( m_stopping == true )
        return 1;
163 164 165

    ret = m_mainWorkflow->getOutput( MainWorkflow::VideoTrack, m_paused );
    ptsDiff = ret->video->ptsDiff;
166 167 168 169 170
    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)
171
        ptsDiff = 1000000 / handler->fps;
172
    }
173 174
    {
        QReadLocker lock( m_effectsLock );
175
        EffectsEngine::applyEffects( m_effects, ret->video, m_mainWorkflow->getCurrentFrame() );
176
    }
177
    m_pts = *pts = ptsDiff + m_pts;
178
    *buffer = ret->video->buffer();
179
    *bufferSize = ret->video->size();
180 181 182
    return 0;
}

183
int
184
WorkflowRenderer::lockAudio( EsHandler *handler, qint64 *pts, size_t *bufferSize, const void ** buffer )
185
{
186
    qint64                              ptsDiff;
187
    quint32                             nbSample;
188
    Workflow::AudioSample               *renderAudioSample;
189

190
    if ( m_stopping == false && m_paused == false )
191
    {
192 193
        MainWorkflow::OutputBuffers* ret = m_mainWorkflow->getOutput( MainWorkflow::AudioTrack,
                                                                      m_paused );
194
        renderAudioSample = ret->audio;
195
    }
196 197 198
    else
        renderAudioSample = NULL;
    if ( renderAudioSample != NULL )
199
    {
200
//        qDebug() << "pts diff:" << renderAudioSample->ptsDiff;
201 202 203 204
        nbSample = renderAudioSample->nbSample;
        *buffer = renderAudioSample->buff;
        *bufferSize = renderAudioSample->size;
        ptsDiff = renderAudioSample->ptsDiff;
205 206 207
    }
    else
    {
208
        nbSample = m_rate / handler->fps;
209
        unsigned int    buffSize = m_nbChannels * 2 * nbSample;
210 211 212 213
        if ( m_silencedAudioBuffer == NULL )
            m_silencedAudioBuffer = new uint8_t[ buffSize ];
        memset( m_silencedAudioBuffer, 0, buffSize );
        *buffer = m_silencedAudioBuffer;
214
        *bufferSize = buffSize;
215
        ptsDiff = m_pts - m_audioPts;
216
    }
217
    m_audioPts = *pts = m_audioPts + ptsDiff;
218
    return 0;
219 220
}

221
void    WorkflowRenderer::unlock( void*, const char*, size_t, void* )
222
{
223 224
}

225
void        WorkflowRenderer::startPreview()
226
{
227
    if ( m_mainWorkflow->getLengthFrame() <= 0 )
228
        return ;
229 230 231 232 233 234 235
    if ( paramsHasChanged( m_width, m_height, m_outputFps ) == true )
    {
        m_width = width();
        m_height = height();
        m_outputFps = outputFps();
        setupRenderer( m_width, m_height, m_outputFps );
    }
236
    //Deactivating vlc's keyboard inputs.
237
    m_mediaPlayer->setKeyInput( false );
238 239
    m_mediaPlayer->setMedia( m_media );

240
    m_mainWorkflow->setFullSpeedRender( false );
241
    m_mainWorkflow->startRender( m_width, m_height );
242
    m_isRendering = true;
243
    m_paused = false;
244
    m_stopping = false;
245
    m_pts = 0;
246
    m_audioPts = 0;
247
    m_mediaPlayer->play();
248 249
}

250 251
void        WorkflowRenderer::nextFrame()
{
252 253
    if ( m_paused == true )
        m_mainWorkflow->renderOneFrame();
254 255
}

256
void        WorkflowRenderer::previousFrame()
257
{
258
    if ( m_paused == true )
259
        m_mainWorkflow->previousFrame( MainWorkflow::VideoTrack );
260 261
}

262
void        WorkflowRenderer::togglePlayPause( bool forcePause )
263
{
264
    if ( m_isRendering == false && forcePause == false )
265
        startPreview();
266 267 268 269 270 271 272 273
    else
        internalPlayPause( forcePause );
}

void        WorkflowRenderer::internalPlayPause( bool forcePause )
{
    //If force pause is true, we just ensure that this render is paused... no need to start it.
    if ( m_isRendering == true )
274
    {
275 276
        if ( m_paused == true && forcePause == false )
        {
277 278
            m_paused = false;
            emit playing();
279
        }
280
        else
281
        {
282 283
            if ( m_paused == false )
            {
284 285
                m_paused = true;
                emit paused();
286
            }
287
        }
288
    }
289
}
290

291
void        WorkflowRenderer::stop()
292 293 294
{
    //Since we want permanent render (to have a permanent render update, we shouldn't
    //stop, but pause
295 296 297
//    togglePlayPause( true );
//    m_mainWorkflow->setCurrentFrame( 0, MainWorkflow::Renderer );
    killRenderer();
298 299 300 301
}

void
WorkflowRenderer::killRenderer()
302 303
{
    m_isRendering = false;
304
    m_paused = false;
305
    m_stopping = true;
306
    m_mainWorkflow->stopFrameComputing();
307
    m_mediaPlayer->stop();
308
    m_mainWorkflow->stop();
309 310
    delete[] m_silencedAudioBuffer;
    m_silencedAudioBuffer = NULL;
311 312
}

313 314 315 316 317
qint64      WorkflowRenderer::getCurrentFrame() const
{
    return m_mainWorkflow->getCurrentFrame();
}

318 319 320 321 322
qint64      WorkflowRenderer::getLength() const
{
    return qRound64( (qreal)getLengthMs() / 1000.0 * (qreal)getFps() );
}

323 324
qint64      WorkflowRenderer::getLengthMs() const
{
325
    return m_mainWorkflow->getLengthFrame() / getFps() * 1000;
326 327 328 329
}

float       WorkflowRenderer::getFps() const
{
330
    return m_outputFps;
331 332
}

333 334 335 336 337 338 339 340 341 342
void        WorkflowRenderer::timelineCursorChanged( qint64 newFrame )
{
    m_mainWorkflow->setCurrentFrame( newFrame, MainWorkflow::TimelineCursor );
}

void        WorkflowRenderer::previewWidgetCursorChanged( qint64 newFrame )
{
    m_mainWorkflow->setCurrentFrame( newFrame, MainWorkflow::PreviewCursor );
}

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

348 349 350 351 352 353 354 355 356 357
void*   WorkflowRenderer::getLockCallback()
{
    return (void*)&WorkflowRenderer::lock;
}

void*   WorkflowRenderer::getUnlockCallback()
{
    return (void*)&WorkflowRenderer::unlock;
}

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

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

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

376
bool
377
WorkflowRenderer::paramsHasChanged( quint32 width, quint32 height, double fps )
378
{
379 380
    quint32             newWidth = this->width();
    quint32             newHeight = this->height();
381 382
    float               newOutputFps = outputFps();

383 384
    return ( newWidth != width || newHeight != height ||
         newOutputFps != fps );
385 386
}

387 388 389
void
WorkflowRenderer::appendEffect( Effect *effect, qint64 start, qint64 end )
{
390
    EffectInstance  *effectInstance = effect->createInstance();
391
    effectInstance->init( m_width, m_height );
392
    QWriteLocker    lock( m_effectsLock );
393
    m_effects.push_back( new EffectsEngine::EffectHelper( effectInstance, start, end ) );
394 395
}

396 397 398 399 400 401 402 403 404 405 406
void
WorkflowRenderer::saveProject( QXmlStreamWriter &project ) const
{
    project.writeStartElement( "renderer" );
    {
        QReadLocker     lock( m_effectsLock );
        EffectsEngine::getInstance()->saveEffects( m_effects, project );
    }
    project.writeEndElement();
}

407 408 409 410
/////////////////////////////////////////////////////////////////////
/////SLOTS :
/////////////////////////////////////////////////////////////////////

411
void        WorkflowRenderer::__endReached()
412
{
413
    stop();
414
    emit endReached();
415 416
}

417
void
418
WorkflowRenderer::mainWorkflowLenghtChanged( qint64 /*newLength*/ )
419
{
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
//    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;
435
}
436 437 438 439 440 441 442

void
WorkflowRenderer::errorEncountered()
{
    stop();
    emit error();
}