WorkflowRenderer.cpp 14.3 KB
Newer Older
1
/*****************************************************************************
2
 * WorkflowRenderer.cpp: Allow a current workflow preview
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *****************************************************************************
 * Copyright (C) 2008-2009 the VLMC team
 *
 * 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 "vlmc.h"
28
#include "WorkflowRenderer.h"
29
#include "timeline/Timeline.h"
30
#include "SettingsManager.h"
31
#include "LightVideoFrame.h"
32

33
uint8_t*            WorkflowRenderer::silencedAudioBuffer = NULL;
34

35 36
WorkflowRenderer::WorkflowRenderer() :
            m_mainWorkflow( MainWorkflow::getInstance() ),
37
            m_stopping( false )
38
{
39
    m_actionsMutex = new QMutex;
40 41 42
}

void    WorkflowRenderer::initializeRenderer()
43
{
44
    char        videoString[512];
45 46
    char        inputSlave[256];
    char        audioParameters[256];
47
    char        callbacks[64];
48

49
    m_waitCond = new QWaitCondition;
50 51
    m_renderVideoFrame = new unsigned char[m_mainWorkflow->getWidth()
                                           * m_mainWorkflow->getHeight() * Pixel::NbComposantes];
52 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 64

    sprintf( videoString, "width=%i:height=%i:dar=%s:fps=%s:data=%lld:codec=%s:cat=2:caching=0",
65
             m_mainWorkflow->getWidth(), m_mainWorkflow->getHeight(), "4/3", "30/1", (qint64)m_videoEsHandler, "RV24" );
66
    sprintf( audioParameters, "data=%lld:cat=1:codec=s16l:samplerate=%u:channels=%u:caching=0",
67
             (qint64)m_audioEsHandler, m_rate, m_nbChannels );
68 69
    strcpy( inputSlave, ":input-slave=imem://" );
    strcat( inputSlave, audioParameters );
70

71
    m_media = new LibVLCpp::Media( "imem://" + QString( videoString ) );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
72
    m_media->addOption( inputSlave );
73

74 75 76 77
    sprintf( callbacks, "imem-get=%lld", (qint64)WorkflowRenderer::lock );
    m_media->addOption( callbacks );
    sprintf( callbacks, ":imem-release=%lld", (qint64)WorkflowRenderer::unlock );
    m_media->addOption( callbacks );
78

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
79
     //Workflow part
80 81
    connect( m_mainWorkflow, SIGNAL( mainWorkflowPaused() ), this, SLOT( mainWorkflowPaused() ), Qt::QueuedConnection );
    connect( m_mainWorkflow, SIGNAL( mainWorkflowUnpaused() ), this, SLOT( mainWorkflowUnpaused() ), Qt::QueuedConnection );
82 83
    connect( m_mainWorkflow, SIGNAL( mainWorkflowEndReached() ), this, SLOT( __endReached() ) );
    connect( m_mainWorkflow, SIGNAL( frameChanged( qint64, MainWorkflow::FrameChangedReason ) ),
84
             this, SIGNAL( frameChanged( qint64, MainWorkflow::FrameChangedReason ) ) );
85 86
}

87
WorkflowRenderer::~WorkflowRenderer()
88
{
89
    stop();
90

91 92
    delete m_videoEsHandler;
    delete m_audioEsHandler;
93
    delete m_actionsMutex;
94
    delete m_media;
95
    delete m_waitCond;
96 97
}

98
int     WorkflowRenderer::lock( void *datas, int64_t *dts, int64_t *pts, unsigned int *flags, size_t *bufferSize, void **buffer )
99
{
100 101
    int             ret = 1;

102
    EsHandler*  handler = reinterpret_cast<EsHandler*>( datas );
103 104
    *dts = -1;
    *flags = 0;
105
    if ( handler->type == Video )
106 107
    {
        ret = lockVideo( handler->self, pts, bufferSize, buffer );
108
        handler->self->m_mainWorkflow->goToNextFrame( MainWorkflow::VideoTrack );
109
    }
110
    else if ( handler->type == Audio )
111 112
    {
        ret = lockAudio( handler->self, pts, bufferSize, buffer );
113
        handler->self->m_mainWorkflow->goToNextFrame( MainWorkflow::AudioTrack );
114 115 116
    }
    else
        qWarning() << "Invalid ES type";
117
//    qDebug() << "ES Type:" << handler->type << "pts:" << *pts;
118
    return ret;
119 120
}

121
int     WorkflowRenderer::lockVideo( WorkflowRenderer* self, int64_t *pts, size_t *bufferSize, void **buffer )
122
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
123
    qint64 ptsDiff = 0;
124

125
    if ( self->m_stopping == false && self->m_paused == false )
126
    {
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
127
        MainWorkflow::OutputBuffers* ret = self->m_mainWorkflow->getOutput( MainWorkflow::VideoTrack );
128
        memcpy( self->m_renderVideoFrame, (*(ret->video))->frame.octets, (*(ret->video))->nboctets );
129
        self->m_videoBuffSize = (*(ret->video))->nboctets;
130
        ptsDiff = (*(ret->video))->ptsDiff;
131
    }
132
    self->m_pts = *pts = ptsDiff + self->m_pts;
133
//    qDebug() << "Video pts" << self->m_pts << "diff" << ptsDiff;
134 135
    //*pts = qRound64( (float)( self->m_pts * 1000000.0f ) / self->m_outputFps );
    //++self->m_pts;
136 137 138 139 140
    *buffer = self->m_renderVideoFrame;
    *bufferSize = self->m_videoBuffSize;
    return 0;
}

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

145 146
    if ( self->m_stopping == false )
    {
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
147
        MainWorkflow::OutputBuffers* ret = self->m_mainWorkflow->getOutput( MainWorkflow::AudioTrack );
148 149
        self->m_renderAudioSample = ret->audio;
    }
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
150
    uint32_t    nbSample;
151 152
    if ( self->m_renderAudioSample != NULL )
    {
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
153
        nbSample = self->m_renderAudioSample->nbSample;
154 155
        *buffer = self->m_renderAudioSample->buff;
        *bufferSize = self->m_renderAudioSample->size;
156
        ptsDiff = self->m_renderAudioSample->ptsDiff;
157 158 159
    }
    else
    {
160
        nbSample = self->m_rate / self->m_outputFps;
161
        unsigned int    buffSize = self->m_nbChannels * 2 * nbSample;
162 163 164 165
        if ( WorkflowRenderer::silencedAudioBuffer == NULL )
            WorkflowRenderer::silencedAudioBuffer = new uint8_t[ buffSize ];
        memset( WorkflowRenderer::silencedAudioBuffer, 0, buffSize );
        *buffer = WorkflowRenderer::silencedAudioBuffer;
166
        *bufferSize = buffSize;
167
        ptsDiff = self->m_pts - self->m_audioPts;
168
    }
169
    self->m_audioPts = *pts = self->m_audioPts + ptsDiff;
170
//    qDebug() << "Audio pts" << self->m_audioPts << "diff" << ptsDiff;
171 172
    //*pts = self->m_audioPts * 1000000.0f / self->m_rate;
    //self->m_audioPts += nbSample * self->m_nbChannels;
173
    return 0;
174 175
}

176
void    WorkflowRenderer::unlock( void* datas, size_t, void* )
177
{
178 179
    EsHandler* handler = reinterpret_cast<EsHandler*>( datas );
    handler->self->checkActions();
180 181 182 183
}

void        WorkflowRenderer::checkActions()
{
184
    QMutexLocker    lock( m_actionsMutex );
185 186 187 188 189

    if ( m_actions.size() == 0 )
        return ;
    while ( m_actions.empty() == false )
    {
190
        Action::Generic*   act = m_actions.top();
191
        m_actions.pop();
192
        act->execute();
193
        delete act;
194
    }
195 196
}

197
void        WorkflowRenderer::startPreview()
198
{
199
    if ( m_mainWorkflow->getLengthFrame() <= 0 )
200
        return ;
201 202 203
    m_mediaPlayer->setMedia( m_media );

    //Media player part: to update PreviewWidget
204 205
    connect( m_mediaPlayer, SIGNAL( playing() ),    this,   SIGNAL( playing() ), Qt::DirectConnection );
    connect( m_mediaPlayer, SIGNAL( paused() ),     this,   SIGNAL( paused() ), Qt::DirectConnection );
206
    //FIXME:: check if this doesn't require Qt::QueuedConnection
207
    connect( m_mediaPlayer, SIGNAL( stopped() ),    this,   SIGNAL( endReached() ) );
208

209
    m_mainWorkflow->startRender();
210
    m_isRendering = true;
211
    m_paused = false;
212
    m_stopping = false;
213
    m_pts = 0;
214
    m_audioPts = 0;
215
    m_outputFps = SettingsManager::getInstance()->getValue( "VLMC", "VLMCOutPutFPS" )->get().toDouble();
216
    m_mediaPlayer->play();
217 218
}

219 220
void        WorkflowRenderer::nextFrame()
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
221 222
    //FIXME: won't work, just compile
    m_mainWorkflow->nextFrame( MainWorkflow::VideoTrack );
223 224
}

225
void        WorkflowRenderer::previousFrame()
226
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
227
    m_mainWorkflow->previousFrame( MainWorkflow::VideoTrack );
228 229
}

230 231
void        WorkflowRenderer::mainWorkflowPaused()
{
232
    m_paused = true;
233
    emit paused();
234 235
}

236 237 238 239 240 241
void        WorkflowRenderer::mainWorkflowUnpaused()
{
    m_paused = false;
    emit playing();
}

242
void        WorkflowRenderer::togglePlayPause( bool forcePause )
243
{
244
    if ( m_isRendering == false && forcePause == false )
245
        startPreview();
246 247 248 249 250 251 252 253
    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 )
254
    {
255 256
        if ( m_paused == true && forcePause == false )
        {
257
            Action::Generic*    act = new Action::Unpause( m_mainWorkflow );
258 259
            QMutexLocker        lock( m_actionsMutex );
            m_actions.addAction( act );
260
        }
261
        else
262
        {
263 264
            if ( m_paused == false )
            {
265
                Action::Generic*    act = new Action::Pause( m_mainWorkflow );
266
                QMutexLocker        lock( m_actionsMutex );
267
                m_actions.addAction( act );
268
            }
269
        }
270
    }
271
}
272

273
void        WorkflowRenderer::stop()
274 275
{
    m_isRendering = false;
276
    m_paused = false;
277
    m_stopping = true;
278
    m_mediaPlayer->stop();
279
    m_mainWorkflow->stop();
280 281
}

282 283 284 285 286 287 288
qint64      WorkflowRenderer::getCurrentFrame() const
{
    return m_mainWorkflow->getCurrentFrame();
}

qint64      WorkflowRenderer::getLengthMs() const
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
289
    return m_mainWorkflow->getLengthFrame() / getFps() * 1000;
290 291 292 293
}

float       WorkflowRenderer::getFps() const
{
294
    return m_outputFps;
295 296
}

297 298
void        WorkflowRenderer::removeClip( const QUuid& uuid, uint32_t trackId, MainWorkflow::TrackType trackType )
{
299 300
    if ( m_isRendering == true )
    {
301 302
        Action::Generic*    act = new Action::RemoveClip( m_mainWorkflow, trackId, trackType, uuid );
        QMutexLocker        lock( m_actionsMutex );
303
        m_actions.addAction( act );
304 305 306
    }
    else
        m_mainWorkflow->removeClip( uuid, trackId, trackType );
307 308
}

309
void        WorkflowRenderer::addClip( Clip* clip, uint32_t trackId, qint64 startingPos, MainWorkflow::TrackType trackType )
310 311 312
{
    if ( m_isRendering == true )
    {
313 314
        Action::Generic*    act = new Action::AddClip( m_mainWorkflow, trackId, trackType, clip, startingPos );
        QMutexLocker        lock( m_actionsMutex );
315
        m_actions.addAction( act );
316 317
    }
    else
318
        m_mainWorkflow->addClip( clip, trackId, startingPos, trackType );
319 320
}

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

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

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

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
336
Clip*       WorkflowRenderer::split( Clip* toSplit, Clip* newClip, uint32_t trackId, qint64 newClipPos, qint64 newClipBegin, MainWorkflow::TrackType trackType )
337
{
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
338 339
    if ( newClip == NULL )
        newClip = new Clip( toSplit, newClipBegin, toSplit->getEnd() );
340 341 342 343 344 345

    if ( m_isRendering == true )
    {
        //adding clip
        //We can NOT call addClip, as it would lock the action lock and then release it,
        //thus potentially breaking the synchrone way of doing this
346
        Action::Generic*    act = new Action::AddClip( m_mainWorkflow, trackId, trackType, newClip, newClipPos );
347
        //resizing it
348
        Action::Generic*    act2 = new Action::ResizeClip( toSplit, toSplit->getBegin(), newClipBegin, true );
349 350 351

        //Push the actions onto the action stack
        QMutexLocker    lock( m_actionsMutex );
352
        m_actions.addAction( act );
353
        m_actions.addAction( act2 );
354 355 356
    }
    else
    {
357
        toSplit->setEnd( newClipBegin, true );
358 359 360 361 362
        m_mainWorkflow->addClip( newClip, trackId, newClipPos, trackType );
    }
    return newClip;
}

363
void    WorkflowRenderer::unsplit( Clip* origin, Clip* splitted, uint32_t trackId, MainWorkflow::TrackType trackType )
364 365 366 367
{
    if ( m_isRendering == true )
    {
        //removing clip
368
        Action::Generic*    act = new Action::RemoveClip( m_mainWorkflow, trackId, trackType, splitted->getUuid() );
369
        //resizing it
370
        Action::Generic*    act2 = new Action::ResizeClip( origin, origin->getBegin(), splitted->getEnd(), true );
371
        //Push the actions onto the action stack
372
        QMutexLocker        lock( m_actionsMutex );
373 374
        m_actions.addAction( act );
        m_actions.addAction( act2 );
375 376 377
    }
    else
    {
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
378
        m_mainWorkflow->removeClip( splitted->getUuid(), trackId, trackType );
379
        origin->setEnd( splitted->getEnd(), true );
380 381 382
    }
}

383 384
void    WorkflowRenderer::resizeClip( Clip* clip, qint64 newBegin, qint64 newEnd,
                                      qint64 newPos, uint32_t trackId, MainWorkflow::TrackType trackType, bool undoRedoAction /*= false*/ )
385
{
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
//    if ( m_isRendering == true )
//    {
//        Action::Generic*    act = new Action::ResizeClip( clip, newBegin, newEnd );
//        Action::Generic*    act2 = new Action::MoveClip( m_mainWorkflow, clip->getUuid(), trackId, trackId, newPos, trackType, undoRedoAction );
//        QMutexLocker        lock( m_actionsMutex );
//        if ( newBegin != clip->getBegin() )
//        {
//            qDebug() << "Resizing to pos:" << newPos;
//            m_actions.addAction( act2 );
//        }
//        qDebug() << "setting boundaries: newEnd:" << newBegin << "newEnd:" << newEnd;
//        m_actions.addAction( act );
//    }
//    else
//    {
401 402 403 404
        if ( newBegin != clip->getBegin() )
        {
            m_mainWorkflow->moveClip( clip->getUuid(), trackId, trackId, newPos, trackType, undoRedoAction );
        }
405
        clip->setBoundaries( newBegin, newEnd );
406

407
//    }
408 409
}

410 411 412 413 414 415 416 417 418 419
void*   WorkflowRenderer::getLockCallback()
{
    return (void*)&WorkflowRenderer::lock;
}

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

420 421 422 423
/////////////////////////////////////////////////////////////////////
/////SLOTS :
/////////////////////////////////////////////////////////////////////

424
void        WorkflowRenderer::__endReached()
425
{
426
    stop();
427
    emit endReached();
428 429
}