Commit 8a4a97b7 authored by Hugo Beauzee-Luyssen's avatar Hugo Beauzee-Luyssen

Merge branch 'chouquette_frame_by_frame'

Conflicts:
	src/Workflow/ClipWorkflow.cpp
	src/Workflow/MainWorkflow.cpp
	src/Workflow/MainWorkflow.h
	src/Workflow/TrackWorkflow.cpp
	src/Workflow/TrackWorkflow.h
	src/gui/MainWindow.cpp
	src/renderer/WorkflowRenderer.cpp
parents c972ec68 f192e84e
......@@ -28,14 +28,12 @@
ClipWorkflow::ClipWorkflow( Clip::Clip* clip ) :
m_clip( clip ),
m_buffer( NULL ),
//m_usingBackBuffer( false ),
m_mediaPlayer(NULL),
m_state( ClipWorkflow::Stopped ),
m_requiredState( ClipWorkflow::None )
m_requiredState( ClipWorkflow::None ),
m_rendering( false ),
m_initFlag( false )
{
m_buffer = new unsigned char[VIDEOHEIGHT * VIDEOWIDTH * 4];
// m_backBuffer = new unsigned char[VIDEOHEIGHT * VIDEOWIDTH * 4];
m_stateLock = new QReadWriteLock;
m_requiredStateLock = new QMutex;
m_waitCond = new QWaitCondition;
......@@ -43,13 +41,13 @@ ClipWorkflow::ClipWorkflow( Clip::Clip* clip ) :
m_initWaitCond = new WaitCondition;
m_renderWaitCond = new WaitCondition;
m_pausingStateWaitCond = new WaitCondition;
// m_backBufferLock = new QReadWriteLock;
m_renderLock = new QMutex;
m_buffer = new unsigned char[VIDEOHEIGHT * VIDEOWIDTH * 4];
}
ClipWorkflow::~ClipWorkflow()
{
// delete[] m_backBuffer;
// delete m_backBufferLock;
delete m_renderLock;
delete m_pausingStateWaitCond;
delete m_initWaitCond;
delete m_condMutex;
......@@ -61,10 +59,9 @@ ClipWorkflow::~ClipWorkflow()
unsigned char* ClipWorkflow::getOutput()
{
// QReadLocker lock( m_backBufferLock );
// if ( m_usingBackBuffer == true )
return m_buffer;
// return m_backBuffer;
QMutexLocker lock( m_renderLock );
return m_buffer;
}
void ClipWorkflow::checkStateChange()
......@@ -73,7 +70,6 @@ void ClipWorkflow::checkStateChange()
QWriteLocker lock2( m_stateLock );
if ( m_requiredState != ClipWorkflow::None )
{
// qDebug() << "Changed state from" << m_state << "to state" << m_requiredState;
m_state = m_requiredState;
m_requiredState = ClipWorkflow::None;
checkSynchronisation( m_state );
......@@ -82,55 +78,31 @@ void ClipWorkflow::checkStateChange()
void ClipWorkflow::lock( ClipWorkflow* cw, void** pp_ret )
{
// QReadLocker lock( cw->m_backBufferLock );
// if ( cw->m_usingBackBuffer )
// {
// *pp_ret = cw->m_backBuffer;
// }
// else
// {
*pp_ret = cw->m_buffer;
// }
cw->m_renderLock->lock();
*pp_ret = cw->m_buffer;
}
void ClipWorkflow::unlock( ClipWorkflow* cw )
{
cw->m_renderLock->unlock();
cw->m_stateLock->lockForWrite();
// if ( cw->m_oneFrameOnly )
// {
// qDebug() << "One frame only mode is ON :)";
// //Forcing pause after rendering a frame
// cw->m_oneFrameOnly = 0;
// cw->m_state = Paused;
// }
// else
// qDebug() << "One frame only mode is OFF :(";
if ( cw->m_state == Rendering )
{
QMutexLocker lock( cw->m_condMutex );
cw->m_state = Sleeping;
cw->m_stateLock->unlock();
//Signal that render has been completed.
cw->m_renderWaitCond->wake();
cw->emit renderComplete( cw );
// qDebug() << ">>>Entering condwait";
cw->m_waitCond->wait( cw->m_condMutex );
// qDebug() << "Leaved condwait";
// qDebug() << "<<<Leaving condwait";
cw->m_stateLock->lockForWrite();
cw->m_state = Rendering;
// {
// QWriteLocker lock2( cw->m_backBufferLock );
// cw->m_usingBackBuffer = !cw->m_usingBackBuffer;
// }
cw->m_stateLock->unlock();
}
else if ( cw->m_state == Paused )
{
cw->m_stateLock->unlock();
cw->m_waitCond->wait( cw->m_condMutex );
}
else
cw->m_stateLock->unlock();
......@@ -171,21 +143,23 @@ void ClipWorkflow::initialize()
m_mediaPlayer->play();
}
//FIXME: this step is probably useless, due to modification in the TrackWorkflow
void ClipWorkflow::setPositionAfterPlayback()
{
disconnect( m_mediaPlayer, SIGNAL( playing() ), this, SLOT( setPositionAfterPlayback() ) );
connect( m_mediaPlayer, SIGNAL( positionChanged() ), this, SLOT( pauseAfterPlaybackStarted() ), Qt::DirectConnection );
m_mediaPlayer->setPosition( m_clip->getBegin() );
}
void ClipWorkflow::pauseAfterPlaybackStarted()
{
disconnect( m_mediaPlayer, SIGNAL( positionChanged() ), this, SLOT( pauseAfterPlaybackStarted() ) );
//FIXME: it seems that this signal is never connected :o
disconnect( m_mediaPlayer, SIGNAL( playing() ), this, SLOT( pauseAfterPlaybackStarted() ) );
connect( m_mediaPlayer, SIGNAL( paused() ), this, SLOT( initializedMediaPlayer() ), Qt::DirectConnection );
m_mediaPlayer->pause();
m_mediaPlayer->pause();
}
void ClipWorkflow::initializedMediaPlayer()
......@@ -200,12 +174,6 @@ bool ClipWorkflow::isReady() const
return m_state == ClipWorkflow::Ready;
}
bool ClipWorkflow::isPausing() const
{
QReadLocker lock( m_stateLock );
return m_state == ClipWorkflow::Pausing;
}
bool ClipWorkflow::isEndReached() const
{
QReadLocker lock( m_stateLock );
......@@ -223,13 +191,20 @@ ClipWorkflow::State ClipWorkflow::getState() const
return m_state;
}
void ClipWorkflow::startRender()
void ClipWorkflow::startRender( bool startInPausedMode )
{
if ( isReady() == false )
m_initWaitCond->wait();
m_mediaPlayer->play();
setState( Rendering );
if ( startInPausedMode == false )
{
m_mediaPlayer->play();
setState( Rendering );
}
else
{
setState( Paused );
}
}
void ClipWorkflow::clipEndReached()
......@@ -254,6 +229,8 @@ void ClipWorkflow::stop()
QMutexLocker lock( m_requiredStateLock );
m_requiredState = ClipWorkflow::None;
delete m_vlcMedia;
m_initFlag = false;
m_rendering = false;
}
else
qDebug() << "ClipWorkflow has already been stopped";
......@@ -270,6 +247,12 @@ bool ClipWorkflow::isRendering() const
return m_state == ClipWorkflow::Rendering;
}
bool ClipWorkflow::isSleeping() const
{
QReadLocker lock( m_stateLock );
return m_state == ClipWorkflow::Sleeping;
}
void ClipWorkflow::checkSynchronisation( State newState )
{
switch ( newState )
......@@ -277,9 +260,6 @@ void ClipWorkflow::checkSynchronisation( State newState )
case Ready:
m_initWaitCond->wake();
break ;
case Pausing:
m_pausingStateWaitCond->wake();
break ;
default:
break ;
}
......@@ -289,6 +269,7 @@ void ClipWorkflow::setState( State state )
{
{
QWriteLocker lock( m_stateLock );
// qDebug() << "Changing from state" << m_state << "to state" << state;
m_state = state;
}
checkSynchronisation( state );
......@@ -320,32 +301,28 @@ void ClipWorkflow::reinitialize()
void ClipWorkflow::pause()
{
setState( Paused );
connect( m_mediaPlayer, SIGNAL( paused() ), this, SLOT( pausedMediaPlayer() ), Qt::DirectConnection );
setState( Pausing );
m_mediaPlayer->pause();
QMutexLocker lock( m_requiredStateLock );
m_requiredState = ClipWorkflow::None;
{
QMutexLocker sync( m_condMutex );
wake();
}
}
void ClipWorkflow::unpause( bool wakeRenderThread /*= true*/ )
void ClipWorkflow::unpause()
{
//Since VLC will detect that the media player is paused and unpause it, we can do this safely
setState( ClipWorkflow::Rendering );
queryStateChange( ClipWorkflow::Rendering );
connect( m_mediaPlayer, SIGNAL( playing() ), this, SLOT( unpausedMediaPlayer() ), Qt::DirectConnection );
m_mediaPlayer->pause();
// QMutexLocker lock( m_requiredStateLock );
// m_requiredState = ClipWorkflow::None;
if ( wakeRenderThread == true )
wake();
}
//void ClipWorkflow::activateOneFrameOnly()
//{
// qDebug() << "Activating one frame only";
// m_oneFrameOnly = 1;
//}
void ClipWorkflow::waitForCompleteRender()
void ClipWorkflow::waitForCompleteRender( bool dontCheckRenderStarted /*= false*/ )
{
if ( isRendering() == true )
if ( isRendering() == true || dontCheckRenderStarted == true )
m_renderWaitCond->wait();
}
......@@ -355,12 +332,6 @@ void ClipWorkflow::waitForCompleteInit()
m_initWaitCond->wait();
}
void ClipWorkflow::waitForPausingState()
{
if ( isPausing() == false )
m_pausingStateWaitCond->wait();
}
QMutex* ClipWorkflow::getSleepMutex()
{
return m_condMutex;
......@@ -370,3 +341,16 @@ LibVLCpp::MediaPlayer* ClipWorkflow::getMediaPlayer()
{
return m_mediaPlayer;
}
void ClipWorkflow::pausedMediaPlayer()
{
disconnect( m_mediaPlayer, SIGNAL( paused() ), this, SLOT( pausedMediaPlayer() ) );
setState( Paused );
emit paused();
}
void ClipWorkflow::unpausedMediaPlayer()
{
disconnect( m_mediaPlayer, SIGNAL( playing() ), this, SLOT( unpausedMediaPlayer() ) );
emit unpaused();
}
......@@ -33,6 +33,7 @@
#include <QMutex>
#include "WaitCondition.hpp"
#include <QObject>
#include <QQueue>
#include "Clip.h"
#include "VLCMediaPlayer.h"
......@@ -91,7 +92,7 @@ class ClipWorkflow : public QObject
*/
bool isRendering() const;
bool isPausing() const;
bool isSleeping() const;
/**
* Returns the current workflow state.
......@@ -106,7 +107,7 @@ class ClipWorkflow : public QObject
* on the media player.
* If the media player isn't ready, this method waits.
*/
void startRender();
void startRender( bool startInPausedMode );
/**
\brief Returns the Clip this workflow instance is based
......@@ -147,17 +148,14 @@ class ClipWorkflow : public QObject
*/
void reinitialize();
void unpause( bool wakeRenderThread = true );
void unpause();
void waitForCompleteInit();
void waitForCompleteRender();
void waitForPausingState();
void waitForCompleteRender( bool dontCheckRenderStarted = false );
QMutex* getSleepMutex();
LibVLCpp::MediaPlayer* getMediaPlayer();
// void activateOneFrameOnly();
private:
static void lock( ClipWorkflow* clipWorkflow, void** pp_ret );
static void unlock( ClipWorkflow* clipWorkflow );
......@@ -178,17 +176,7 @@ class ClipWorkflow : public QObject
LibVLCpp::Media* m_vlcMedia;
unsigned char* m_buffer;
//unsigned char* m_backBuffer;
/**
* This allow the render procedure to know in which buffer it should render.
* If true, then the render occurs in the back buffer, which means the
* returned buffer much be the "front" buffer.
* In other term :
* - When m_usingBackBuffer == false, lock() will return m_buffer, and getOutput() m_backBuffer
* - When m_usingBackBuffer == true, lock() will return m_backBuffer, and getOutput() m_buffer
*/
//bool m_usingBackBuffer;
//QReadWriteLock* m_backBufferLock;
QMutex* m_renderLock;
LibVLCpp::MediaPlayer* m_mediaPlayer;
......@@ -200,22 +188,36 @@ class ClipWorkflow : public QObject
State m_requiredState;
QMutex* m_requiredStateLock;
QAtomicInt m_oneFrameOnly;
WaitCondition* m_initWaitCond;
WaitCondition* m_renderWaitCond;
WaitCondition* m_pausingStateWaitCond;
/**
* While this flag is set to false, we will use the same buffer, to prevent
* having X buffers with the same picture (when media player is paused mainly)
*/
bool m_rendering;
/**
* This flag is here to avoid multiple connection to the mediaPlayer* slots.
* It's essentially a nasty hack due to the multiples calls to lock/unlock when
* the render is started, and that cannot really be avoided...
*/
bool m_initFlag;
private slots:
void pauseAfterPlaybackStarted();
void initializedMediaPlayer();
void setPositionAfterPlayback();
void pausedMediaPlayer();
void unpausedMediaPlayer();
public slots:
void clipEndReached();
signals:
void renderComplete( ClipWorkflow* );
void paused();
void unpaused();
};
#endif // CLIPWORKFLOW_H
......@@ -46,6 +46,7 @@ MainWorkflow::MainWorkflow( int trackCount ) :
m_tracks[i].setPtr( new TrackWorkflow( i ) );
connect( m_tracks[i], SIGNAL( trackEndReached( unsigned int ) ), this, SLOT( trackEndReached(unsigned int) ) );
connect( m_tracks[i], SIGNAL( trackPaused() ), this, SLOT( trackPaused() ) );
connect( m_tracks[i], SIGNAL( trackUnpaused() ), this, SLOT( trackUnpaused() ) );
connect( m_tracks[i], SIGNAL( renderCompleted( unsigned int ) ), this, SLOT( tracksRenderCompleted( unsigned int ) ), Qt::QueuedConnection );
}
m_renderStartedLock = new QReadWriteLock;
......@@ -105,7 +106,7 @@ void MainWorkflow::startRender()
computeLength();
}
unsigned char* MainWorkflow::getOutput()
void MainWorkflow::getOutput()
{
QReadLocker lock( m_renderStartedLock );
QMutexLocker lock2( m_renderMutex );
......@@ -118,27 +119,19 @@ unsigned char* MainWorkflow::getOutput()
m_synchroneRenderingBuffer = NULL;
if ( m_renderStarted == true )
{
unsigned char* ret;
for ( unsigned int i = 0; i < m_trackCount; ++i )
{
if ( m_tracks[i].activated() == false )
continue ;
if ( ( ret = m_tracks[i]->getOutput( m_currentFrame ) ) != NULL )
m_nbTracksToRender.fetchAndAddAcquire( 1 );
if ( m_tracks[i]->getOutput( m_currentFrame ) != false )
{
m_nbTracksToRender.fetchAndAddAcquire( 1 );
break ;
}
}
if ( ret == NULL )
ret = MainWorkflow::blackOutput;
nextFrame();
return ret;
}
else
{
return MainWorkflow::blackOutput;
if ( m_paused == false )
nextFrame();
}
}
......@@ -157,8 +150,24 @@ void MainWorkflow::pause()
}
}
void MainWorkflow::unpause()
{
QMutexLocker lock( m_renderMutex );
m_nbTracksToUnpause = 0;
for ( unsigned int i = 0; i < m_trackCount; ++i )
{
if ( m_tracks[i].activated() == true )
{
m_nbTracksToUnpause.fetchAndAddAcquire( 1 );
m_tracks[i]->unpause();
}
}
}
void MainWorkflow::nextFrame()
{
// qDebug() << "Going to the next frame";
++m_currentFrame;
//FIXME: This is probably a bit much...
emit frameChanged( m_currentFrame );
......@@ -167,6 +176,7 @@ void MainWorkflow::nextFrame()
void MainWorkflow::previousFrame()
{
// qDebug() << "Going to the previous frame";
--m_currentFrame;
//FIXME: This is probably a bit much...
emit frameChanged( m_currentFrame );
......@@ -175,16 +185,17 @@ void MainWorkflow::previousFrame()
void MainWorkflow::setPosition( float pos )
{
if ( m_renderStarted == false )
return ;
//Since any track can be reactivated, we reactivate all of them, and let them
//unable themself if required.
for ( unsigned int i = 0; i < m_trackCount; ++i)
m_tracks[i].activate();
if ( m_renderStarted == false )
return ;
qint64 frame = static_cast<qint64>( (float)m_length * pos );
m_currentFrame = frame;
emit frameChanged( frame );
// cancelSynchronisation();
//Do not emit a signal for the RenderWidget, since it's the one that triggered that call...
}
......@@ -283,22 +294,23 @@ Clip* MainWorkflow::removeClip( const QUuid& uuid, unsigned int trackId )
return clip;
}
void MainWorkflow::activateOneFrameOnly()
void MainWorkflow::trackPaused()
{
for (unsigned int i = 0; i < m_trackCount; ++i)
m_nbTracksToPause.fetchAndAddAcquire( -1 );
if ( m_nbTracksToPause <= 0 )
{
//FIXME: After debugging period, this should'nt be necessary --
if ( m_tracks[i].activated() == true )
m_tracks[i]->activateOneFrameOnly();
m_paused = true;
emit mainWorkflowPaused();
}
}
void MainWorkflow::trackPaused()
void MainWorkflow::trackUnpaused()
{
m_nbTracksToPause.fetchAndAddAcquire( -1 );
if ( m_nbTracksToPause == 0 )
m_nbTracksToUnpause.fetchAndAddAcquire( -1 );
if ( m_nbTracksToUnpause <= 0 )
{
emit mainWorkflowPaused();
m_paused = false;
emit mainWorkflowUnpaused();
}
}
......@@ -336,3 +348,11 @@ unsigned char* MainWorkflow::getSynchroneOutput()
return MainWorkflow::blackOutput;
return m_synchroneRenderingBuffer;
}
void MainWorkflow::cancelSynchronisation()
{
{
QMutexLocker lock( m_synchroneRenderWaitConditionMutex );
}
m_synchroneRenderWaitCondition->wakeAll();
}
......@@ -42,7 +42,7 @@ class MainWorkflow : public QObject, public Singleton<MainWorkflow>
void addClip( Clip* clip, unsigned int trackId, qint64 start );
void startRender();
unsigned char* getOutput();
void getOutput();
unsigned char* getSynchroneOutput();
/**
......@@ -71,13 +71,12 @@ class MainWorkflow : public QObject, public Singleton<MainWorkflow>
* Pause the main workflow and all its sub-workflows
*/
void pause();
void unpause();
static unsigned char* blackOutput;
void nextFrame();
void previousFrame();
void activateOneFrameOnly();
static MainWorkflow* getInstance();
static void deleteInstance();
Clip* removeClip( const QUuid& uuid, unsigned int trackId );
......@@ -85,6 +84,12 @@ class MainWorkflow : public QObject, public Singleton<MainWorkflow>
unsigned int newTrack, qint64 pos, bool undoRedoCommand = false );
qint64 getClipPosition( const QUuid& uuid, unsigned int trackId ) const;
/**
* \brief This method will wake every wait condition, so that threads won't
* be waiting anymore, thus avoiding dead locks.
*/
void cancelSynchronisation();
private:
static MainWorkflow* m_instance;
......@@ -104,16 +109,19 @@ class MainWorkflow : public QObject, public Singleton<MainWorkflow>
QMutex* m_renderMutex;
QAtomicInt m_nbTracksToPause;
QAtomicInt m_nbTracksToUnpause;
QAtomicInt m_nbTracksToRender;
QMutex* m_highestTrackNumberMutex;
unsigned int m_highestTrackNumber;
unsigned char* m_synchroneRenderingBuffer;
QWaitCondition* m_synchroneRenderWaitCondition;
QMutex* m_synchroneRenderWaitConditionMutex;
bool m_paused;
private slots:
void trackEndReached( unsigned int trackId );
void trackPaused();
void trackUnpaused();
void tracksRenderCompleted( unsigned int trackId );
signals:
......@@ -128,6 +136,7 @@ class MainWorkflow : public QObject, public Singleton<MainWorkflow>
void mainWorkflowEndReached();
void mainWorkflowPaused();
void mainWorkflowUnpaused();
void clipRemoved( QUuid, unsigned int );
void clipMoved( QUuid, unsigned int, qint64 );
};
......
......@@ -61,6 +61,8 @@ void TrackWorkflow::addClip( ClipWorkflow* cw, qint64 start )
{
QWriteLocker lock( m_clipsLock );
connect( cw, SIGNAL( renderComplete( ClipWorkflow* ) ), this, SLOT( clipWorkflowRenderCompleted( ClipWorkflow* ) ), Qt::DirectConnection );
connect( cw, SIGNAL( paused() ), this, SLOT( clipWorkflowPaused() ) );
connect( cw, SIGNAL( unpaused() ), this, SLOT( clipWorkflowUnpaused() ) );
m_clips.insert( start, cw );
computeLength();
}
......@@ -96,24 +98,11 @@ qint64 TrackWorkflow::getClipPosition( const QUuid& uuid ) const
return -1;
}
unsigned char* TrackWorkflow::renderClip( ClipWorkflow* cw, qint64 currentFrame,
qint64 start , bool needRepositioning,
bool pauseAfterRender )
void TrackWorkflow::renderClip( ClipWorkflow* cw, qint64 currentFrame,
qint64 start , bool needRepositioning )
{
unsigned char* ret = NULL;
cw->getStateLock()->lockForRead();
// qDebug() << "Rendering clip";
if ( cw->getState() == ClipWorkflow::Paused && pauseAfterRender == false )
{
cw->getStateLock()->unlock();
//If we must pause after render, we must NOT wake the renderer thread, or it could render more than one frame
// (since this is for the next/previous frame)
//However, if this is just for a classic unpause, with just don't give a shit :)
cw->unpause( true );
cw->getStateLock()->lockForRead();
}
if ( cw->getState() == ClipWorkflow::Rendering )
{
//The rendering state meens... whell it means that the frame is
......@@ -127,7 +116,7 @@ unsigned char* TrackWorkflow::renderClip( ClipWorkflow* cw, qint64 currentF
}
//If frame has been rendered :
if ( cw->getState() == ClipWorkflow::Sleeping || pauseAfterRender == true )
if ( cw->getState() == ClipWorkflow::Sleeping )
{
cw->getStateLock()->unlock();
......@@ -137,23 +126,14 @@ unsigned char* TrackWorkflow::renderClip( ClipWorkflow* cw, qint64 currentF
pos = pos * ( cw->getClip()->getEnd() - cw->getClip()->getBegin() ) + cw->getClip()->getBegin();
cw->setPosition( pos );
}
ret = cw->getOutput();
if ( pauseAfterRender == true )
{
cw->unpause( false );
// qDebug() << "Querying state back to pause after render";
cw->queryStateChange( ClipWorkflow::Paused );
}
{
QMutexLocker lock( cw->getSleepMutex() );