WorkflowRenderer.cpp 12.3 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 "WorkflowRenderer.h"
31
#include "timeline/Timeline.h"
32
#include "SettingsManager.h"
33
#include "LightVideoFrame.h"
34
35
36
#include "MainWorkflow.h"
#include "GenericRenderer.h"
#include "AudioClipWorkflow.h"
37
38
#include "VLCMedia.h"
#include "Clip.h"
39
#include "VLCMediaPlayer.h"
40

41
42
WorkflowRenderer::WorkflowRenderer() :
            m_mainWorkflow( MainWorkflow::getInstance() ),
43
            m_media( NULL ),
44
            m_stopping( false ),
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
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
53
54
}

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

59
    m_nbChannels = 2;
60
    m_rate = 48000;
61

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
62
     //Workflow part
63
64
    connect( m_mainWorkflow, SIGNAL( mainWorkflowEndReached() ), this, SLOT( __endReached() ) );
    connect( m_mainWorkflow, SIGNAL( frameChanged( qint64, MainWorkflow::FrameChangedReason ) ),
65
             this, SIGNAL( frameChanged( qint64, MainWorkflow::FrameChangedReason ) ) );
66
67
    connect( m_mainWorkflow, SIGNAL( lengthChanged( qint64 ) ),
             this, SLOT(mainWorkflowLenghtChanged(qint64) ) );
68
69
70
    //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 );
71
    connect( m_mediaPlayer, SIGNAL( errorEncountered() ), this, SLOT( errorEncountered() ) );
72
73
    //FIXME:: check if this doesn't require Qt::QueuedConnection
    connect( m_mediaPlayer, SIGNAL( stopped() ),    this,   SIGNAL( endReached() ) );
74
75
}

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

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

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

96
    m_esHandler->fps = fps;
97
    //Clean any previous render.
98

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

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

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

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

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

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

159
160
161
    if ( m_stopping == true )
        return 1;
    else
162
    {
163
        ret = m_mainWorkflow->getOutput( MainWorkflow::VideoTrack, m_paused );
164
        m_videoBuffSize = (*(ret->video))->nboctets;
165
        ptsDiff = (*(ret->video))->ptsDiff;
166
    }
167
168
169
170
171
    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)
172
        ptsDiff = 1000000 / handler->fps;
173
    }
174
    m_pts = *pts = ptsDiff + m_pts;
175
    *buffer = (*(ret->video))->frame.octets;
176
    *bufferSize = m_videoBuffSize;
177
178
179
    return 0;
}

180
int
181
WorkflowRenderer::lockAudio( EsHandler *handler, qint64 *pts, size_t *bufferSize, void **buffer )
182
{
183
    qint64                              ptsDiff;
184
    quint32                             nbSample;
185
    AudioClipWorkflow::AudioSample      *renderAudioSample;
186

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

218
void    WorkflowRenderer::unlock( void*, const char*, size_t, void* )
219
{
220
221
}

222
void        WorkflowRenderer::startPreview()
223
{
224
    if ( m_mainWorkflow->getLengthFrame() <= 0 )
225
        return ;
226
227
228
229
230
231
232
    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 );
    }
233
    //Deactivating vlc's keyboard inputs.
234
    m_mediaPlayer->setKeyInput( false );
235
236
    m_mediaPlayer->setMedia( m_media );

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

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

253
void        WorkflowRenderer::previousFrame()
254
{
255
    if ( m_paused == true )
256
        m_mainWorkflow->previousFrame( MainWorkflow::VideoTrack );
257
258
}

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

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

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

310
311
312
313
314
qint64      WorkflowRenderer::getCurrentFrame() const
{
    return m_mainWorkflow->getCurrentFrame();
}

315
316
317
318
319
qint64      WorkflowRenderer::getLength() const
{
    return qRound64( (qreal)getLengthMs() / 1000.0 * (qreal)getFps() );
}

320
321
qint64      WorkflowRenderer::getLengthMs() const
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
322
    return m_mainWorkflow->getLengthFrame() / getFps() * 1000;
323
324
325
326
}

float       WorkflowRenderer::getFps() const
{
327
    return m_outputFps;
328
329
}

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

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

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

345
346
347
348
349
350
351
352
353
354
void*   WorkflowRenderer::getLockCallback()
{
    return (void*)&WorkflowRenderer::lock;
}

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

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

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

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
367
368
369
float
WorkflowRenderer::outputFps() const
{
370
    return VLMC_PROJECT_GET_DOUBLE( "video/VLMCOutputFPS" );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
371
372
}

373
bool
374
WorkflowRenderer::paramsHasChanged( quint32 width, quint32 height, double fps )
375
{
376
377
    quint32             newWidth = this->width();
    quint32             newHeight = this->height();
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
378
379
    float               newOutputFps = outputFps();

380
381
    return ( newWidth != width || newHeight != height ||
         newOutputFps != fps );
382
383
}

384
385
386
387
/////////////////////////////////////////////////////////////////////
/////SLOTS :
/////////////////////////////////////////////////////////////////////

388
void        WorkflowRenderer::__endReached()
389
{
390
    stop();
391
    emit endReached();
392
393
}

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

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