WorkflowRenderer.cpp 12.2 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
quint8*         WorkflowRenderer::silencedAudioBuffer = NULL;
39

40
41
WorkflowRenderer::WorkflowRenderer() :
            m_mainWorkflow( MainWorkflow::getInstance() ),
42
            m_stopping( false ),
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
43
            m_outputFps( 0.0f ),
44
45
            m_oldLength( 0 ),
            m_renderVideoFrame( NULL ),
46
47
48
            m_media( NULL ),
            m_width( 0 ),
            m_height( 0 )
49
{
50
51
52
}

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

61
    m_nbChannels = 2;
62
    m_rate = 48000;
63

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
64
     //Workflow part
65
66
    connect( m_mainWorkflow, SIGNAL( mainWorkflowEndReached() ), this, SLOT( __endReached() ) );
    connect( m_mainWorkflow, SIGNAL( frameChanged( qint64, MainWorkflow::FrameChangedReason ) ),
67
             this, SIGNAL( frameChanged( qint64, MainWorkflow::FrameChangedReason ) ) );
68
69
    connect( m_mainWorkflow, SIGNAL( lengthChanged( qint64 ) ),
             this, SLOT(mainWorkflowLenghtChanged(qint64) ) );
70
71
}

72
WorkflowRenderer::~WorkflowRenderer()
73
{
74
    killRenderer();
75

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

81
82
83
84
85
86
87
88
89
90
void
WorkflowRenderer::setupRenderer()
{
    char        videoString[512];
    char        inputSlave[256];
    char        audioParameters[256];
    char        callbacks[64];

    if ( m_renderVideoFrame != NULL )
        delete m_renderVideoFrame;
91
    m_renderVideoFrame = new unsigned char[m_width * m_height * Pixel::NbComposantes];
92
93

    sprintf( videoString, "width=%i:height=%i:dar=%s:fps=%s:data=%lld:codec=%s:cat=2:caching=0",
94
             m_width, m_height, "16/9", "30/1",
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
             (qint64)m_videoEsHandler, "RV24" );
    sprintf( audioParameters, "data=%lld:cat=1:codec=fl32:samplerate=%u:channels=%u:caching=0",
             (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" );
}

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

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

140
141
int
WorkflowRenderer::lockVideo( qint64 *pts, size_t *bufferSize, void **buffer )
142
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
143
    qint64 ptsDiff = 0;
144

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

168
169
int
WorkflowRenderer::lockAudio( qint64 *pts, size_t *bufferSize, void **buffer )
170
{
171
172
173
    qint64                              ptsDiff;
    uint32_t                            nbSample;
    AudioClipWorkflow::AudioSample      *renderAudioSample;
174

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

205
void    WorkflowRenderer::unlock( void*, size_t, void* )
206
{
207
208
}

209
void        WorkflowRenderer::startPreview()
210
{
211
    if ( m_mainWorkflow->getLengthFrame() <= 0 )
212
        return ;
213
214
    if ( parametersChanged() == true )
        setupRenderer();
215
216
217
    m_mediaPlayer->setMedia( m_media );

    //Media player part: to update PreviewWidget
218
219
    connect( m_mediaPlayer, SIGNAL( playing() ),    this,   SIGNAL( playing() ), Qt::DirectConnection );
    connect( m_mediaPlayer, SIGNAL( paused() ),     this,   SIGNAL( paused() ), Qt::DirectConnection );
220
    //FIXME:: check if this doesn't require Qt::QueuedConnection
221
    connect( m_mediaPlayer, SIGNAL( stopped() ),    this,   SIGNAL( endReached() ) );
222

223
224
225
226
227
    //Clean any previous render.
    memcpy( m_renderVideoFrame,
            (*MainWorkflow::blackOutput)->frame.octets,
            (*MainWorkflow::blackOutput)->nboctets );

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

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

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

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

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

void
WorkflowRenderer::killRenderer()
290
291
{
    m_isRendering = false;
292
    m_paused = false;
293
    m_stopping = true;
294
    m_mediaPlayer->stop();
295
    m_mainWorkflow->stop();
296
297
}

298
299
300
301
302
qint64      WorkflowRenderer::getCurrentFrame() const
{
    return m_mainWorkflow->getCurrentFrame();
}

303
304
305
306
307
qint64      WorkflowRenderer::getLength() const
{
    return qRound64( (qreal)getLengthMs() / 1000.0 * (qreal)getFps() );
}

308
309
qint64      WorkflowRenderer::getLengthMs() const
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
310
    return m_mainWorkflow->getLengthFrame() / getFps() * 1000;
311
312
313
314
}

float       WorkflowRenderer::getFps() const
{
315
    return m_outputFps;
316
317
}

318
319
320
321
322
323
324
325
326
327
void        WorkflowRenderer::timelineCursorChanged( qint64 newFrame )
{
    m_mainWorkflow->setCurrentFrame( newFrame, MainWorkflow::TimelineCursor );
}

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

328
329
330
void        WorkflowRenderer::rulerCursorChanged( qint64 newFrame )
{
    m_mainWorkflow->setCurrentFrame( newFrame, MainWorkflow::RulerCursor );
331
332
}

333
334
335
336
337
338
339
340
341
342
void*   WorkflowRenderer::getLockCallback()
{
    return (void*)&WorkflowRenderer::lock;
}

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

343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
quint32
WorkflowRenderer::width() const
{
    const SettingValue  *width = SettingsManager::getInstance()->getValue( "project",
                                                                    "VideoProjectWidth" );
    return width->get().toUInt();
}

quint32
WorkflowRenderer::height() const
{
    const SettingValue  *height = SettingsManager::getInstance()->getValue( "project",
                                                                    "VideoProjectHeight" );
    return height->get().toUInt();
}

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
359
360
361
362
363
364
365
366
float
WorkflowRenderer::outputFps() const
{
    const SettingValue  *outputFps = SettingsManager::getInstance()->getValue( "VLMC",
                                                                               "VLMCOutPutFPS" );
    return outputFps->get().toDouble();
}

367
368
369
370
371
bool
WorkflowRenderer::parametersChanged()
{
    quint32             newWidth = width();
    quint32             newHeight = height();
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
372
373
    float               newOutputFps = outputFps();

374
375
376
377
378
379
380
381
382
383
384
    if ( newWidth != m_width || newHeight != m_height ||
         newOutputFps != m_outputFps )
    {
        m_width = newWidth;
        m_height = newHeight;
        m_outputFps = newOutputFps;
        return true;
    }
    return false;
}

385
386
387
388
/////////////////////////////////////////////////////////////////////
/////SLOTS :
/////////////////////////////////////////////////////////////////////

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

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