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 ),
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
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

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
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
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
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
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
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_PROJECT_GET_UINT( "video/VideoProjectWidth" );
352
353
354
355
356
}

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

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
360
361
362
float
WorkflowRenderer::outputFps() const
{
363
    return VLMC_PROJECT_GET_DOUBLE( "video/VLMCOutputFPS" );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
364
365
}

366
bool
367
WorkflowRenderer::paramsHasChanged( quint32 width, quint32 height, double fps )
368
{
369
370
    quint32             newWidth = this->width();
    quint32             newHeight = this->height();
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
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
}