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

27
#include "WorkflowRenderer.h"
28
#include "timeline/Timeline.h"
29
#include "SettingsManager.h"
30
#include "LightVideoFrame.h"
31 32 33
#include "MainWorkflow.h"
#include "GenericRenderer.h"
#include "AudioClipWorkflow.h"
34 35
#include "VLCMedia.h"
#include "Clip.h"
36
#include "VLCMediaPlayer.h"
37

38 39
WorkflowRenderer::WorkflowRenderer() :
            m_mainWorkflow( MainWorkflow::getInstance() ),
40
            m_stopping( false ),
41
            m_outputFps( 0.0f ),
42 43
            m_oldLength( 0 ),
            m_renderVideoFrame( NULL ),
44 45
            m_media( NULL ),
            m_width( 0 ),
46 47
            m_height( 0 ),
            m_silencedAudioBuffer( NULL )
48
{
49 50 51
}

void    WorkflowRenderer::initializeRenderer()
52
{
53 54 55 56 57 58 59
    m_videoEsHandler = new EsHandler;
    m_videoEsHandler->self = this;
    m_videoEsHandler->type = Video;
    m_audioEsHandler = new EsHandler;
    m_audioEsHandler->self = this;
    m_audioEsHandler->type = Audio;

60
    m_nbChannels = 2;
61
    m_rate = 48000;
62

63
     //Workflow part
64 65
    connect( m_mainWorkflow, SIGNAL( mainWorkflowEndReached() ), this, SLOT( __endReached() ) );
    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
WorkflowRenderer::~WorkflowRenderer()
72
{
73
    killRenderer();
74

75 76
    delete m_videoEsHandler;
    delete m_audioEsHandler;
77 78 79
    delete m_media;
}

80
void
81
WorkflowRenderer::setupRenderer( quint32 width, quint32 height, double fps )
82 83 84 85 86 87 88 89
{
    char        videoString[512];
    char        inputSlave[256];
    char        audioParameters[256];
    char        callbacks[64];

    if ( m_renderVideoFrame != NULL )
        delete m_renderVideoFrame;
90
    m_renderVideoFrame = new unsigned char[width * height * Pixel::NbComposantes];
91 92
    m_audioEsHandler->fps = fps;
    m_videoEsHandler->fps = fps;
93 94
    //Clean any previous render.
    memset( m_renderVideoFrame, 0, m_width * m_height * Pixel::NbComposantes );
95 96

    sprintf( videoString, "width=%i:height=%i:dar=%s:fps=%s:data=%lld:codec=%s:cat=2:caching=0",
97
             width, height, "16/9", "30/1",
98
             (qint64)m_videoEsHandler, "RV24" );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
99
    sprintf( audioParameters, "data=%lld:cat=1:codec=f32l:samplerate=%u:channels=%u:caching=0",
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
             (qint64)m_audioEsHandler, m_rate, m_nbChannels );
    strcpy( inputSlave, ":input-slave=imem://" );
    strcat( inputSlave, audioParameters );

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

    sprintf( callbacks, "imem-get=%lld", (qint64)getLockCallback() );
    m_media->addOption( callbacks );
    sprintf( callbacks, ":imem-release=%lld", (qint64)getUnlockCallback() );
    m_media->addOption( callbacks );
    m_media->addOption( ":text-renderer dummy" );
}

116 117 118
int
WorkflowRenderer::lock( void *datas, qint64 *dts, qint64 *pts, quint32 *flags,
                        size_t *bufferSize, void **buffer )
119
{
120
    int             ret = 1;
121 122
    EsHandler*      handler = reinterpret_cast<EsHandler*>( datas );
    bool            paused = handler->self->m_paused;
123

124 125
    *dts = -1;
    *flags = 0;
126
    if ( handler->type == Video )
127
    {
128
        ret = handler->self->lockVideo( handler, pts, bufferSize, buffer );
129
        if ( paused == false )
130
            handler->self->m_mainWorkflow->nextFrame( MainWorkflow::VideoTrack );
131
    }
132
    else if ( handler->type == Audio )
133
    {
134
        ret = handler->self->lockAudio( handler, pts, bufferSize, buffer );
135
        if ( paused == false )
136
            handler->self->m_mainWorkflow->nextFrame( MainWorkflow::AudioTrack );
137 138
    }
    else
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
139
        qCritical() << "Invalid ES type";
140
    return ret;
141 142
}

143
int
144
WorkflowRenderer::lockVideo( EsHandler *handler, qint64 *pts, size_t *bufferSize, void **buffer )
145
{
146
    qint64 ptsDiff = 0;
147

148
    if ( m_stopping == false )
149
    {
150
        MainWorkflow::OutputBuffers* ret =
151
                m_mainWorkflow->getOutput( MainWorkflow::VideoTrack, m_paused );
152 153 154
        memcpy( m_renderVideoFrame,
                (*(ret->video))->frame.octets,
                (*(ret->video))->nboctets );
155
        m_videoBuffSize = (*(ret->video))->nboctets;
156
        ptsDiff = (*(ret->video))->ptsDiff;
157
    }
158 159 160 161 162
    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)
163
        ptsDiff = 1000000 / handler->fps;
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
164
    } 
165 166 167
    m_pts = *pts = ptsDiff + m_pts;
    *buffer = m_renderVideoFrame;
    *bufferSize = m_videoBuffSize;
168 169 170
    return 0;
}

171
int
172
WorkflowRenderer::lockAudio( EsHandler *handler, qint64 *pts, size_t *bufferSize, void **buffer )
173
{
174 175 176
    qint64                              ptsDiff;
    uint32_t                            nbSample;
    AudioClipWorkflow::AudioSample      *renderAudioSample;
177

178
    if ( m_stopping == false && m_paused == false )
179
    {
180 181
        MainWorkflow::OutputBuffers* ret = m_mainWorkflow->getOutput( MainWorkflow::AudioTrack,
                                                                      m_paused );
182
        renderAudioSample = ret->audio;
183
    }
184 185 186
    else
        renderAudioSample = NULL;
    if ( renderAudioSample != NULL )
187
    {
188 189 190 191
        nbSample = renderAudioSample->nbSample;
        *buffer = renderAudioSample->buff;
        *bufferSize = renderAudioSample->size;
        ptsDiff = renderAudioSample->ptsDiff;
192 193 194
    }
    else
    {
195
        nbSample = m_rate / handler->fps;
196
        unsigned int    buffSize = m_nbChannels * 2 * nbSample;
197 198 199 200
        if ( m_silencedAudioBuffer == NULL )
            m_silencedAudioBuffer = new uint8_t[ buffSize ];
        memset( m_silencedAudioBuffer, 0, buffSize );
        *buffer = m_silencedAudioBuffer;
201
        *bufferSize = buffSize;
202
        ptsDiff = m_pts - m_audioPts;
203
    }
204
    m_audioPts = *pts = m_audioPts + ptsDiff;
205
    return 0;
206 207
}

208
void    WorkflowRenderer::unlock( void*, size_t, void* )
209
{
210 211
}

212
void        WorkflowRenderer::startPreview()
213
{
214
    if ( m_mainWorkflow->getLengthFrame() <= 0 )
215
        return ;
216 217 218 219 220 221 222
    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 );
    }
223 224 225
    m_mediaPlayer->setMedia( m_media );

    //Media player part: to update PreviewWidget
226 227
    connect( m_mediaPlayer, SIGNAL( playing() ),    this,   SIGNAL( playing() ), Qt::DirectConnection );
    connect( m_mediaPlayer, SIGNAL( paused() ),     this,   SIGNAL( paused() ), Qt::DirectConnection );
228
    //FIXME:: check if this doesn't require Qt::QueuedConnection
229
    connect( m_mediaPlayer, SIGNAL( stopped() ),    this,   SIGNAL( endReached() ) );
230

231
    m_mainWorkflow->setFullSpeedRender( false );
232
    m_mainWorkflow->startRender( m_width, m_height );
233
    m_isRendering = true;
234
    m_paused = false;
235
    m_stopping = false;
236
    m_pts = 0;
237
    m_audioPts = 0;
238
    m_mediaPlayer->play();
239 240
}

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

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

253
void        WorkflowRenderer::togglePlayPause( bool forcePause )
254
{
255
    if ( m_isRendering == false && forcePause == false )
256
        startPreview();
257 258 259 260 261 262 263 264
    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 )
265
    {
266 267
        if ( m_paused == true && forcePause == false )
        {
268 269
            m_paused = false;
            emit playing();
270
        }
271
        else
272
        {
273 274
            if ( m_paused == false )
            {
275 276
                m_paused = true;
                emit paused();
277
            }
278
        }
279
    }
280
}
281

282
void        WorkflowRenderer::stop()
283 284 285
{
    //Since we want permanent render (to have a permanent render update, we shouldn't
    //stop, but pause
286 287 288
//    togglePlayPause( true );
//    m_mainWorkflow->setCurrentFrame( 0, MainWorkflow::Renderer );
    killRenderer();
289 290 291 292
}

void
WorkflowRenderer::killRenderer()
293 294
{
    m_isRendering = false;
295
    m_paused = false;
296
    m_stopping = true;
297
    m_mediaPlayer->stop();
298
    m_mainWorkflow->stop();
299 300
    delete[] m_silencedAudioBuffer;
    m_silencedAudioBuffer = NULL;
301 302
}

303 304 305 306 307
qint64      WorkflowRenderer::getCurrentFrame() const
{
    return m_mainWorkflow->getCurrentFrame();
}

308 309 310 311 312
qint64      WorkflowRenderer::getLength() const
{
    return qRound64( (qreal)getLengthMs() / 1000.0 * (qreal)getFps() );
}

313 314
qint64      WorkflowRenderer::getLengthMs() const
{
315
    return m_mainWorkflow->getLengthFrame() / getFps() * 1000;
316 317 318 319
}

float       WorkflowRenderer::getFps() const
{
320
    return m_outputFps;
321 322
}

323 324 325 326 327 328 329 330 331 332
void        WorkflowRenderer::timelineCursorChanged( qint64 newFrame )
{
    m_mainWorkflow->setCurrentFrame( newFrame, MainWorkflow::TimelineCursor );
}

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

333 334 335
void        WorkflowRenderer::rulerCursorChanged( qint64 newFrame )
{
    m_mainWorkflow->setCurrentFrame( newFrame, MainWorkflow::RulerCursor );
336 337
}

338 339 340 341 342 343 344 345 346 347
void*   WorkflowRenderer::getLockCallback()
{
    return (void*)&WorkflowRenderer::lock;
}

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

348 349 350
quint32
WorkflowRenderer::width() const
{
351
    return VLMC_GET_UINT( "project/VideoProjectWidth" );
352 353 354 355 356
}

quint32
WorkflowRenderer::height() const
{
357
    return VLMC_GET_UINT( "project/VideoProjectHeight" );
358 359
}

360 361 362
float
WorkflowRenderer::outputFps() const
{
363
    return VLMC_GET_DOUBLE( "global/VLMCOutPutFPS" );
364 365
}

366
bool
367
WorkflowRenderer::paramsHasChanged( quint32 width, quint32 height, double fps )
368
{
369 370
    quint32             newWidth = this->width();
    quint32             newHeight = this->height();
371 372
    float               newOutputFps = outputFps();

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

377 378 379 380
/////////////////////////////////////////////////////////////////////
/////SLOTS :
/////////////////////////////////////////////////////////////////////

381
void        WorkflowRenderer::__endReached()
382
{
383
    stop();
384
    emit endReached();
385 386
}

387 388 389
void
WorkflowRenderer::mainWorkflowLenghtChanged( qint64 newLength )
{
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
//    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;
405
}