WorkflowRenderer.cpp 11.7 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

26
#include "vlmc.h"
27
#include "WorkflowRenderer.h"
28
#include "Timeline.h"
29
#include "SettingsManager.h"
30

31 32
WorkflowRenderer::WorkflowRenderer() :
            m_mainWorkflow( MainWorkflow::getInstance() ),
33
            m_stopping( false )
34
{
35 36
    char        buffer[64];

37
    m_actionsMutex = new QMutex;
38 39 40
    m_media = new LibVLCpp::Media( "fake://" );

    sprintf( buffer, ":invmem-width=%i", VIDEOWIDTH );
41
    m_media->addOption( ":codec=invmem" );
42 43 44
    m_media->addOption( buffer );
    sprintf( buffer, ":invmem-height=%i", VIDEOHEIGHT );
    m_media->addOption( buffer );
45
    sprintf( buffer, ":invmem-lock=%lld", (qint64)WorkflowRenderer::lock );
46
    m_media->addOption( buffer );
47
    sprintf( buffer, ":invmem-unlock=%lld", (qint64)WorkflowRenderer::unlock );
48 49 50
    m_media->addOption( buffer );
    sprintf( buffer, ":invmem-data=%lld", (qint64)this );
    m_media->addOption( buffer );
51 52 53 54
    sprintf( buffer, ":width=%i", VIDEOWIDTH );
    m_media->addOption( buffer );
    sprintf( buffer, ":height=%i", VIDEOHEIGHT );
    m_media->addOption( buffer );
55

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
56 57 58 59 60
    m_media->addOption( ":no-audio" );
//    sprintf( buffer, ":inamem-data=%lld", (qint64)this );
//    m_media->addOption( buffer );
//    sprintf( buffer, ":inamem-callback=%lld", (qint64)WorkflowRenderer::lock );
//    m_media->addOption( buffer );
61

62 63
    m_condMutex = new QMutex;
    m_waitCond = new QWaitCondition;
64

65
    m_renderVideoFrame = new unsigned char[VIDEOHEIGHT * VIDEOWIDTH * Pixel::NbComposantes];
66

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
67
     //Workflow part
68 69
    connect( m_mainWorkflow, SIGNAL( mainWorkflowPaused() ), this, SLOT( mainWorkflowPaused() ) );
    connect( m_mainWorkflow, SIGNAL( mainWorkflowUnpaused() ), this, SLOT( mainWorkflowUnpaused() ) );
70 71 72
    connect( m_mainWorkflow, SIGNAL( mainWorkflowEndReached() ), this, SLOT( __endReached() ) );
    connect( m_mainWorkflow, SIGNAL( frameChanged( qint64, MainWorkflow::FrameChangedReason ) ),
             this, SLOT( __frameChanged( qint64, MainWorkflow::FrameChangedReason ) ) );
73 74
}

75

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

80
    delete m_actionsMutex;
81
    delete m_media;
82 83
    delete m_condMutex;
    delete m_waitCond;
84 85
}

86 87 88 89 90
void*   WorkflowRenderer::lockAudio( void* datas )
{
    WorkflowRenderer* self = reinterpret_cast<WorkflowRenderer*>( datas );

    qDebug() << "Injecting audio data";
91
    return self->m_renderAudioSample;
92 93
}

94
void*   WorkflowRenderer::lock( void* datas )
95
{
96
    WorkflowRenderer* self = reinterpret_cast<WorkflowRenderer*>( datas );
97

98 99
    if ( self->m_stopping == false )
    {
100 101 102
        MainWorkflow::OutputBuffers* ret = self->m_mainWorkflow->getSynchroneOutput();
        memcpy( self->m_renderVideoFrame, (*(ret->video))->frame.octets, (*(ret->video))->nboctets );
        self->m_renderAudioSample = ret->audio;
103
    }
104
    return self->m_renderVideoFrame;
105 106
}

107
void    WorkflowRenderer::unlock( void* datas )
108
{
109
    WorkflowRenderer* self = reinterpret_cast<WorkflowRenderer*>( datas );
110 111 112 113 114
    self->checkActions();
}

void        WorkflowRenderer::checkActions()
{
115
    QMutexLocker    lock( m_actionsMutex );
116 117 118 119 120

    if ( m_actions.size() == 0 )
        return ;
    while ( m_actions.empty() == false )
    {
121
        Action::Generic*   act = m_actions.top();
122
        m_actions.pop();
123
        act->execute();
124
        delete act;
125
    }
126 127
}

128
void        WorkflowRenderer::startPreview()
129
{
130
    if ( m_mainWorkflow->getLengthFrame() <= 0 )
131
        return ;
132 133 134 135 136
    m_mediaPlayer->setMedia( m_media );

    //Media player part: to update PreviewWidget
    connect( m_mediaPlayer, SIGNAL( playing() ),    this,   SLOT( __videoPlaying() ), Qt::DirectConnection );
    connect( m_mediaPlayer, SIGNAL( paused() ),     this,   SLOT( __videoPaused() ), Qt::DirectConnection );
137
    //FIXME:: check if this doesn't require Qt::QueuedConnection
138 139
    connect( m_mediaPlayer, SIGNAL( stopped() ),    this,   SLOT( __videoStopped() ) );

140
    m_mainWorkflow->setFullSpeedRender( false );
141
    m_mainWorkflow->startRender();
142
    m_isRendering = true;
143
    m_paused = false;
144
    m_stopping = false;
145
    m_outputFps = SettingsManager::getInstance()->getValue( "VLMC", "VLMCOutPutFPS" )->get().toDouble();
146
    m_mediaPlayer->play();
147 148
}

149 150
void        WorkflowRenderer::nextFrame()
{
151
    m_mainWorkflow->nextFrame();
152 153
}

154
void        WorkflowRenderer::previousFrame()
155
{
156
    m_mainWorkflow->previousFrame();
157 158
}

159 160
void        WorkflowRenderer::mainWorkflowPaused()
{
161
    m_paused = true;
162 163 164
    {
        QMutexLocker    lock( m_condMutex );
    }
165
    emit paused();
166 167
}

168 169 170 171 172 173
void        WorkflowRenderer::mainWorkflowUnpaused()
{
    m_paused = false;
    emit playing();
}

174
void        WorkflowRenderer::togglePlayPause( bool forcePause )
175
{
176
    if ( m_isRendering == false && forcePause == false )
177
        startPreview();
178 179 180 181 182 183 184 185
    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 )
186
    {
187 188
        if ( m_paused == true && forcePause == false )
        {
189
            Action::Generic*    act = new Action::Unpause( m_mainWorkflow );
190 191
            QMutexLocker        lock( m_actionsMutex );
            m_actions.addAction( act );
192
        }
193
        else
194
        {
195 196
            if ( m_paused == false )
            {
197
                Action::Generic*    act = new Action::Pause( m_mainWorkflow );
198
                QMutexLocker        lock( m_actionsMutex );
199
                m_actions.addAction( act );
200
            }
201
        }
202
    }
203
}
204

205
void        WorkflowRenderer::stop()
206 207
{
    m_isRendering = false;
208
    m_paused = false;
209
    m_stopping = true;
210
    m_mainWorkflow->cancelSynchronisation();
211
    m_mediaPlayer->stop();
212
    m_mainWorkflow->stop();
213 214
}

215 216 217 218 219 220 221
qint64      WorkflowRenderer::getCurrentFrame() const
{
    return m_mainWorkflow->getCurrentFrame();
}

qint64      WorkflowRenderer::getLengthMs() const
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
222
    return m_mainWorkflow->getLengthFrame() / getFps() * 1000;
223 224 225 226
}

float       WorkflowRenderer::getFps() const
{
227
    return m_outputFps;
228 229
}

230 231
void        WorkflowRenderer::removeClip( const QUuid& uuid, uint32_t trackId, MainWorkflow::TrackType trackType )
{
232 233
    if ( m_isRendering == true )
    {
234 235
        Action::Generic*    act = new Action::RemoveClip( m_mainWorkflow, trackId, trackType, uuid );
        QMutexLocker        lock( m_actionsMutex );
236
        m_actions.addAction( act );
237 238 239
    }
    else
        m_mainWorkflow->removeClip( uuid, trackId, trackType );
240 241
}

242
void        WorkflowRenderer::addClip( Clip* clip, uint32_t trackId, qint64 startingPos, MainWorkflow::TrackType trackType )
243 244 245
{
    if ( m_isRendering == true )
    {
246 247
        Action::Generic*    act = new Action::AddClip( m_mainWorkflow, trackId, trackType, clip, startingPos );
        QMutexLocker        lock( m_actionsMutex );
248
        m_actions.addAction( act );
249 250
    }
    else
251
        m_mainWorkflow->addClip( clip, trackId, startingPos, trackType );
252 253
}

254 255 256 257 258 259 260 261 262 263
void        WorkflowRenderer::timelineCursorChanged( qint64 newFrame )
{
    m_mainWorkflow->setCurrentFrame( newFrame, MainWorkflow::TimelineCursor );
}

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

264 265 266 267 268
void        WorkflowRenderer::rulerCursorChanged( qint64 newFrame )
{
    m_mainWorkflow->setCurrentFrame( newFrame, MainWorkflow::RulerCursor );
}

269 270 271 272 273 274 275 276 277
Clip*       WorkflowRenderer::split( Clip* toSplit, uint32_t trackId, qint64 newClipPos, qint64 newClipBegin, MainWorkflow::TrackType trackType )
{
    Clip*   newClip = new Clip( toSplit, newClipBegin, toSplit->getEnd() );

    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
278
        Action::Generic*    act = new Action::AddClip( m_mainWorkflow, trackId, trackType, newClip, newClipPos );
279
        //resizing it
280
        Action::Generic*    act2 = new Action::ResizeClip( toSplit, toSplit->getBegin(), newClipBegin );
281 282 283

        //Push the actions onto the action stack
        QMutexLocker    lock( m_actionsMutex );
284
        m_actions.addAction( act );
285 286 287 288 289 290 291 292 293 294
        m_actions.push( act2 );
    }
    else
    {
        toSplit->setEnd( newClipBegin );
        m_mainWorkflow->addClip( newClip, trackId, newClipPos, trackType );
    }
    return newClip;
}

295 296 297 298 299
void    WorkflowRenderer::unsplit( Clip* origin, Clip* splitted, uint32_t trackId, qint64 oldEnd, MainWorkflow::TrackType trackType )
{
    if ( m_isRendering == true )
    {
        //removing clip
300
        Action::Generic*    act = new Action::RemoveClip( m_mainWorkflow, trackId, trackType, splitted->getUuid() );
301
        //resizing it
302
        Action::Generic*    act2 = new Action::ResizeClip( origin, splitted->getBegin(), oldEnd );
303
        //Push the actions onto the action stack
304
        QMutexLocker        lock( m_actionsMutex );
305 306
        m_actions.addAction( act );
        m_actions.addAction( act2 );
307 308 309 310 311 312 313 314
    }
    else
    {
        delete m_mainWorkflow->removeClip( splitted->getUuid(), trackId, trackType );
        origin->setEnd( oldEnd );
    }
}

315 316
void    WorkflowRenderer::resizeClip( Clip* clip, qint64 newBegin, qint64 newEnd,
                                      qint64 newPos, uint32_t trackId, MainWorkflow::TrackType trackType, bool undoRedoAction /*= false*/ )
317
{
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
//    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
//    {
333 334 335 336
        if ( newBegin != clip->getBegin() )
        {
            m_mainWorkflow->moveClip( clip->getUuid(), trackId, trackId, newPos, trackType, undoRedoAction );
        }
337
        clip->setBoundaries( newBegin, newEnd );
338
//    }
339 340
}

341 342 343 344
/////////////////////////////////////////////////////////////////////
/////SLOTS :
/////////////////////////////////////////////////////////////////////

345
void        WorkflowRenderer::__endReached()
346
{
347
    stop();
348
    emit endReached();
349 350
}

351
void        WorkflowRenderer::__frameChanged( qint64 frame, MainWorkflow::FrameChangedReason reason )
352
{
353
    emit frameChanged( frame, reason );
354 355
}

356
void        WorkflowRenderer::__videoPlaying()
357
{
358
    emit playing();
359
}
360

361
void        WorkflowRenderer::__videoStopped()
362 363 364
{
    emit endReached();
}
365 366 367 368 369

void        WorkflowRenderer::__videoPaused()
{
    emit paused();
}