Commit 4993f48c authored by Hugo Beauzee-Luyssen's avatar Hugo Beauzee-Luyssen

Big ClipWorkflow refactoring. It's really much cleaner now...

parent 0ad33aa1
......@@ -24,91 +24,60 @@
#include "ClipWorkflow.h"
ClipWorkflow::ClipWorkflow( Clip::Clip* clip, QMutex* condMutex, QWaitCondition* waitCond ) :
ClipWorkflow::ClipWorkflow( Clip::Clip* clip, QMutex* renderMutex,
QMutex* condMutex, QWaitCondition* waitCond ) :
m_clip( clip ),
m_renderComplete( false ),
m_buffer( NULL ),
m_renderMutex( renderMutex ),
m_condMutex( condMutex ),
m_waitCond( waitCond ),
m_mediaPlayer(NULL),
m_isReady( false ),
m_endReached( false ),
m_stopScheduled( false )
m_state( ClipWorkflow::Stopped )
{
m_renderCompleteMutex = new QReadWriteLock();
m_buffer = new unsigned char[VIDEOHEIGHT * VIDEOWIDTH * 4];
m_initMutex = new QReadWriteLock();
m_endReachedLock = new QReadWriteLock();
m_stopScheduledMutex = new QReadWriteLock();
m_stateLock = new QReadWriteLock();
}
ClipWorkflow::~ClipWorkflow()
{
delete[] m_buffer;
delete m_renderCompleteMutex;
delete m_initMutex;
delete m_endReachedLock;
delete m_stopScheduledMutex;
delete m_stateLock;
}
bool ClipWorkflow::renderComplete() const
{
QReadLocker lock( m_renderCompleteMutex );
QReadLocker lock2( m_endReachedLock );
return ( m_renderComplete || ( m_endReached == true) );
}
void ClipWorkflow::scheduleStop()
{
QWriteLocker lock( m_stopScheduledMutex );
m_stopScheduled = true;
}
//void ClipWorkflow::scheduleStop()
//{
// QWriteLocker lock( m_state );
// m_state = Stopping;
//}
unsigned char* ClipWorkflow::getOutput()
{
QMutexLocker lock( m_renderMutex );
return m_buffer;
}
void ClipWorkflow::lock( ClipWorkflow* clipWorkflow, void** pp_ret )
{
//It doesn't seems necessary to lock anything here, since the scheduler
//will wait until the frame is ready to use it, and doesn't use it after
//it has asked for a new one.
//In any case, we give vlc a buffer to render in...
//If we don't, segmentation fault will catch us and eat our brain !! ahem...
//If we don't, segmentation fault will catch us and eat our brains !! ahem...
// qDebug() << "Locking in ClipWorkflow::lock";
clipWorkflow->m_renderMutex->lock();
// qDebug() << clipWorkflow->getState();
*pp_ret = clipWorkflow->m_buffer;
}
void ClipWorkflow::unlock( ClipWorkflow* clipWorkflow )
{
if ( clipWorkflow->m_isReady == true )
clipWorkflow->m_renderMutex->unlock();
clipWorkflow->m_stateLock->lockForRead();
if ( clipWorkflow->m_state == Rendering )
{
QMutexLocker lock5( clipWorkflow->m_condMutex );
{
QWriteLocker lock( clipWorkflow->m_endReachedLock );
if ( clipWorkflow->m_endReached == true )
{
qDebug() << "UnLocking in ClipWorkflow::unlock (endReached == true)";
return ;
}
QReadLocker lock2( clipWorkflow->m_stopScheduledMutex );
if ( clipWorkflow->m_stopScheduled == true )
{
qDebug() << "UnLocking in ClipWorkflow::unlock (stopScheduled == true)";
return ;
}
QWriteLocker lock3( clipWorkflow->m_renderCompleteMutex );
clipWorkflow->m_renderComplete = true;
if ( clipWorkflow->m_mediaPlayer->getPosition() > clipWorkflow->m_clip->getEnd() )
clipWorkflow->m_endReached = true;
}
clipWorkflow->m_stateLock->unlock();
clipWorkflow->m_waitCond->wait( clipWorkflow->m_condMutex );
}
clipWorkflow->m_stateLock->unlock();
// qDebug() << "UnLocking in ClipWorkflow::unlock";
}
......@@ -135,20 +104,18 @@ void ClipWorkflow::setVmem()
void ClipWorkflow::initialize( LibVLCpp::MediaPlayer* mediaPlayer )
{
reinitFlags();
setState( Initializing );
setVmem();
m_mediaPlayer = mediaPlayer;
m_mediaPlayer->setMedia( m_clip->getParent()->getVLCMedia() );
connect( m_mediaPlayer, SIGNAL( playing() ), this, SLOT( setPositionAfterPlayback() ), Qt::DirectConnection );
connect( m_mediaPlayer, SIGNAL( endReached() ), this, SLOT( endReached() ), Qt::DirectConnection );
// qDebug() << "Launching playback";
m_mediaPlayer->play();
}
void ClipWorkflow::setPositionAfterPlayback()
{
// qDebug() << "Setting position";
disconnect( m_mediaPlayer, SIGNAL( playing() ), this, SLOT( setPositionAfterPlayback() ) );
connect( m_mediaPlayer, SIGNAL( positionChanged() ), this, SLOT( pauseAfterPlaybackStarted() ), Qt::DirectConnection );
m_mediaPlayer->setPosition( m_clip->getBegin() );
......@@ -160,7 +127,6 @@ void ClipWorkflow::pauseAfterPlaybackStarted()
disconnect( m_mediaPlayer, SIGNAL( playing() ), this, SLOT( pauseAfterPlaybackStarted() ) );
connect( m_mediaPlayer, SIGNAL( paused() ), this, SLOT( pausedMediaPlayer() ), Qt::DirectConnection );
// qDebug() << "pausing media";
m_mediaPlayer->pause();
}
......@@ -168,44 +134,46 @@ void ClipWorkflow::pauseAfterPlaybackStarted()
void ClipWorkflow::pausedMediaPlayer()
{
disconnect( m_mediaPlayer, SIGNAL( paused() ), this, SLOT( pausedMediaPlayer() ) );
QWriteLocker lock( m_initMutex);
m_isReady = true;
QWriteLocker lock( m_stateLock );
m_state = ClipWorkflow::Ready;
}
bool ClipWorkflow::isReady() const
{
QReadLocker lock( m_initMutex );
return m_isReady;
QReadLocker lock( m_stateLock );
return m_state == ClipWorkflow::Ready;
}
bool ClipWorkflow::isEndReached() const
{
QReadLocker lock( m_endReachedLock );
return m_endReached;
QReadLocker lock( m_stateLock );
return m_state == ClipWorkflow::EndReached;
}
bool ClipWorkflow::isStopped() const
{
QReadLocker lock( m_stateLock );
return m_state == ClipWorkflow::Stopped;
}
ClipWorkflow::State ClipWorkflow::getState() const
{
QReadLocker lock( m_stateLock );
return m_state;
}
void ClipWorkflow::startRender()
{
bool isReady;
{
QReadLocker lock( m_initMutex );
isReady = m_isReady;
}
while ( isReady == false )
{
usleep( 150 );
{
QReadLocker lock( m_initMutex );
isReady = m_isReady;
}
}
while ( isReady() == false )
usleep( 50 );
m_mediaPlayer->play();
setState( Rendering );
}
void ClipWorkflow::endReached()
{
QWriteLocker lock2( m_endReachedLock );
m_endReached = true;
qDebug() << "End reached";
setState( EndReached );
}
const Clip* ClipWorkflow::getClip() const
......@@ -215,13 +183,12 @@ const Clip* ClipWorkflow::getClip() const
void ClipWorkflow::stop()
{
qDebug() << "Calling mediaPlayer::stop()";
qDebug() << "ClipWorkflow::stop()";
m_mediaPlayer->stop();
qDebug() << "Succesfully called mediaPlayer::stop()";
Q_ASSERT( m_mediaPlayer != NULL );
reinitFlags();
m_mediaPlayer = NULL;
qDebug() << "Stoped ClipWorkflow";
setState( Stopped );
}
void ClipWorkflow::setPosition( float pos )
......@@ -229,27 +196,14 @@ void ClipWorkflow::setPosition( float pos )
m_mediaPlayer->setPosition( pos );
}
bool ClipWorkflow::isStopped() const
bool ClipWorkflow::isRendering() const
{
return ( m_mediaPlayer == NULL );
QReadLocker lock( m_stateLock );
return m_state == ClipWorkflow::Rendering;
}
void ClipWorkflow::reinitFlags()
void ClipWorkflow::setState( State state )
{
{
QWriteLocker lock2( m_renderCompleteMutex );
m_renderComplete = false;
}
{
QWriteLocker lock2( m_endReachedLock );
m_endReached = false;
}
{
QWriteLocker lock( m_initMutex);
m_isReady = false;
}
{
QWriteLocker lock( m_stopScheduledMutex );
m_stopScheduled = false;
}
QWriteLocker lock( m_stateLock );
m_state = state;
}
......@@ -42,20 +42,61 @@ class ClipWorkflow : public QObject
Q_OBJECT
public:
ClipWorkflow( Clip* clip, QMutex* condMutex, QWaitCondition* waitCond );
enum State
{
Stopped,
Initializing,
Ready,
Rendering,
EndReached
};
ClipWorkflow( Clip* clip, QMutex* renderMutex, QMutex* condMutex, QWaitCondition* waitCond );
virtual ~ClipWorkflow();
/**
* Will return true if the render is completed or the end of the clip has been
* reached.
* If the workflow is still rendering, this will return false.
* This method returns the current frame. It locks the renderMutex,
* therefore, you can call this method blindly, without taking care
* of the rendering process advancement.
*/
bool renderComplete() const;
unsigned char* getOutput();
void initialize( LibVLCpp::MediaPlayer* mediaPlayer );
/**
* Return true ONLY if the state is equal to Ready.
* If the state is Rendering, EndReached or anything else, this will
* return false.
*/
bool isReady() const;
/**
* Return true ONLY if the state is equal to EndReached.
* In any other cases, this will return false.
*/
bool isEndReached() const;
/**
* Return true ONLY if the state is equal to Stopped.
* In any other cases, this will return false.
*/
bool isStopped() const;
/**
* Return true ONLY if the state is equal to Stopped.
* In any other cases, this will return false.
*/
bool isRendering() const;
/**
* Returns the current workflow state.
*/
State getState() const;
/**
* This method start the effective render, ie calling the play() method
* on the media player.
* If the media player isn't ready, this method waits.
*/
void startRender();
/**
\brief Returns the Clip this workflow instance is based
uppon, so that you can query information on it.
......@@ -67,36 +108,28 @@ class ClipWorkflow : public QObject
*/
void stop();
void setPosition( float pos );
/**
* \return true if the workflow has already been stopped.
*/
bool isStopped() const;
void scheduleStop();
// void scheduleStop();
private:
static void lock( ClipWorkflow* clipWorkflow, void** pp_ret );
static void unlock( ClipWorkflow* clipWorkflow );
void setVmem();
void reinitFlags();
void setState( State state );
private:
Clip* m_clip;
QReadWriteLock* m_renderCompleteMutex;
bool m_renderComplete;
unsigned char* m_buffer;
QMutex* m_renderMutex;
QMutex* m_condMutex;
QWaitCondition* m_waitCond;
LibVLCpp::MediaPlayer* m_mediaPlayer;
QReadWriteLock* m_initMutex;
bool m_isReady;
QReadWriteLock* m_endReachedLock;
bool m_endReached;
QReadWriteLock* m_stopScheduledMutex;
bool m_stopScheduled;
State m_state;
QReadWriteLock* m_stateLock;
public slots:
void pauseAfterPlaybackStarted();
......
......@@ -31,7 +31,7 @@ TrackWorkflow::TrackWorkflow()
m_condMutex = new QMutex;
m_waitCondition = new QWaitCondition;
m_mediaPlayer = new LibVLCpp::MediaPlayer();
m_currentLock = new QReadWriteLock();
m_renderMutex = new QMutex;
if ( TrackWorkflow::blackOutput == NULL )
{
//TODO: this ain't free !
......@@ -45,13 +45,13 @@ TrackWorkflow::~TrackWorkflow()
delete m_condMutex;
delete m_waitCondition;
delete m_mediaPlayer;
delete m_currentLock;
delete m_renderMutex;
}
void TrackWorkflow::addClip( Clip* clip, qint64 start )
{
qDebug() << "Inserting clip at frame nb" << start;
ClipWorkflow* cw = new ClipWorkflow( clip, m_condMutex, m_waitCondition );
ClipWorkflow* cw = new ClipWorkflow( clip, m_renderMutex, m_condMutex, m_waitCondition );
m_clips.insert( start, cw );
computeLength();
}
......@@ -65,16 +65,10 @@ void TrackWorkflow::startRender()
//If the first frame is to be render soon, we should play it now.
if ( m_clips.begin().key() < TrackWorkflow::nbFrameBeforePreload )
{
// qDebug() << "Next clip is less than" << nbFrameBeforePreload<< "frame ahead";
m_clips.begin().value()->initialize( m_mediaPlayer );
// qDebug() << "Waiting for the first clip to be ready";
//We wait to be sure the ClipWorkflow will be ready when we really start to render.
while ( m_clips.begin().value()->isReady() == false )
usleep( 20 );
if ( m_current.key() == 0 )
{
m_current = m_clips.begin();
// qDebug() << "Clip workflow is at first frame";
m_current.value()->startRender();
}
}
......@@ -83,9 +77,10 @@ void TrackWorkflow::startRender()
bool TrackWorkflow::checkNextClip( qint64 currentFrame )
{
QMap<qint64, ClipWorkflow*>::iterator next;
const QMap<qint64, ClipWorkflow*>::const_iterator end = m_clips.end();
//Picking next clip :
if ( m_current == m_clips.end() )
if ( m_current == end )
{
//Checking if there is a clip in the first place...
if ( m_clips.count() == 0 )
......@@ -95,22 +90,23 @@ bool TrackWorkflow::checkNextClip( qint64 currentFrame )
else
{
next = m_clips.begin() + 1;
if ( next == m_clips.end() )
if ( next == end )
return false;
}
//If it's about to be used, initialize it
if ( next.key() == currentFrame + TrackWorkflow::nbFrameBeforePreload )
if ( next.key() == currentFrame + TrackWorkflow::nbFrameBeforePreload )
{
qDebug() << "Initializing next clipWorkflow";
//Don't do anything if the current media player is still in use
//But for it to be in use, we should have a current media :
if ( m_current != end && m_current.value()->isRendering() == false )
qDebug() << "Preloading media";
next.value()->initialize( m_mediaPlayer );
}
//This ClipWorkflow must start at this frame :
else if ( next.key() == currentFrame )
{
while ( next.value()->isReady() == false )
usleep( 20 );
qDebug() << "Switching current clip workflow";
//Using it as the current clip from now on.
qDebug() << "Starting rendering";
m_current = next;
m_current.value()->startRender();
}
......@@ -135,159 +131,147 @@ unsigned char* TrackWorkflow::getOutput( qint64 currentFrame )
unsigned char* ret = TrackWorkflow::blackOutput;
bool clipsRemaining;
QReadLocker lock( m_currentLock );
qDebug() << "Frame nb" << currentFrame;
// qDebug() << "Frame nb" << currentFrame;
clipsRemaining = checkNextClip( currentFrame );
//This is true only before the first render.
if ( m_current == m_clips.end() )
{
//If the track was empty, then its end is reached
if ( clipsRemaining == false )
{
qDebug() << "End Reached";
emit endReached();
}
// qDebug() << "Stil no clip at this time, going to the next frame";
return ret;
//Else, we return a black screen.
return TrackWorkflow::blackOutput;
}
// qDebug() << "All waked"
if ( m_current.value()->isStopped() == false )
//We proceed to the render only if the ClipWorkflow is in rendering mode.
if ( m_current.value()->isRendering() == true )
{
m_waitCondition->wakeAll();
// We wait until the end of the render. If the clip reach end, this
// will also return true.
while ( m_current.value()->renderComplete() == false )
usleep( 20 );
if ( m_current.value()->isEndReached() == false )
ret = m_current.value()->getOutput();
ret = m_current.value()->getOutput();
return ret;
}
qDebug() << "End reached : stopping current clipworkflow";
stopCurrentClipWorkflow();
if ( clipsRemaining == false )
else if ( m_current.value()->getState() == ClipWorkflow::EndReached )
{
qDebug() <<"End reached";
emit endReached();
//First, we stop the current ClipWorkflow so that it won't
//enter the lock/unlock cycle anymore.
stopCurrentClipWorkflow();
//Then, if there's no remaining clip, end of track is reached.
if ( clipsRemaining == false )
emit endReached();
}
return ret;
}
void TrackWorkflow::stopCurrentClipWorkflow()
{
qDebug() << "About to stopCurrentClipWorkflow()";
//Awaking renderer thread to avoid dead lock
m_current.value()->scheduleStop();
m_waitCondition->wakeAll();
if ( m_current.value()->isStopped() == false )
m_current.value()->stop();
qDebug() << "Stopped stopCurrentClipWorkflow();";
Q_ASSERT( m_current.value()->isEndReached() );
m_current.value()->stop();
}
void TrackWorkflow::initializeClipWorkflow( ClipWorkflow* cw )
{
//>Launching the initialization
qDebug()<< "Initializing clip workflow (initializeClipWorkflow())";
cw->initialize( m_mediaPlayer );
//And then wait for it to be ready
while ( cw->isReady() == false )
usleep( 20 );
//Once it is, we actually start the render
cw->startRender();
qDebug() << "End of clip workflow reinitialization";
}
void TrackWorkflow::setPosition( float pos )
{
qint64 frame = (float)m_length * pos;
QMap<qint64, ClipWorkflow*>::iterator it = m_clips.begin();
const QMap<qint64, ClipWorkflow*>::iterator end = m_clips.end();
QMap<qint64, ClipWorkflow*>::iterator next = end;
QWriteLocker lock( m_currentLock );
if ( frame > m_length )
{
// qDebug() << "setting position after the end of this track";
if ( m_current != end )
{
stopCurrentClipWorkflow();
m_current = end;
return ;
}
}
//Locate the new clip workflow
while ( it != end )
{
if ( it.key() <= frame &&
( it.key() + it.value()->getClip()->getLength() ) > frame )
{
// qDebug() << "Found new current clip workflow";
break;
}
else if ( next == m_clips.end() && it.key() > frame )
{
// If this clip doesn't match, but starts AFTER the frame we aim,
// we can assume that it's the next clip.
// We can break, and put it to end() in order to simulate the
// normal end of the loop.
next = it;
if ( next != m_clips.begin() )
{
// qDebug() << "Next clip isn't the first one";
next = next - 1; //Since the iterator must point to the previous video
}
else
{
next = end;
// qDebug() << "Next clip is the first of the track";
}
// in order to checkNextClip() to work.
it = end;
break ;
}
++it;
}
qDebug() << "I don't think so...";
return ;
//No clip was found :
if ( it == end )
{
//We should use the next clip, however, we use the clip just before
//the next.
//We also stop the current clip if it was started.
if ( m_current != end )
{
stopCurrentClipWorkflow();
}
m_current = next;
}
// If the clip found is the current, we just change the position of the
// media player
else if ( it == m_current )
{
//The clip may have been stoped (if we reached end but came back at it)
if ( it.value()->isStopped() )
{
initializeClipWorkflow( it.value() );
}
qDebug() << "Changing the position of the current clip";
it.value()->setPosition( (float)( frame - it.key() ) / (float)(it.value()->getClip()->getLength()) );
//Awaking renderers to avoid them to be stuck inside of the lock...
// qDebug() << "Waking all renderer threads";
m_waitCondition->wakeAll();
}
// Else, we found a clip that is not the current one.
else
{
// qDebug() << "Switching to other Clip";
//First, we stop the current workflow.
if ( m_current != end )
{
stopCurrentClipWorkflow();
}
//We initialize the new workflow
initializeClipWorkflow( it.value() );
//And this is now our current clip
m_current = it;
// qDebug() << "Switched current clip workflow";
// qint64 frame = (float)m_length * pos;
// QMap<qint64, ClipWorkflow*>::iterator it = m_clips.begin();
// const QMap<qint64, ClipWorkflow*>::iterator end = m_clips.end();
// QMap<qint64, ClipWorkflow*>::iterator next = end;
//
// QWriteLocker lock( m_currentLock );
// if ( frame > m_length )
// {
//// qDebug() << "setting position after the end of this track";
// if ( m_current != end )
// {
// stopCurrentClipWorkflow();
// m_current = end;
// return ;
// }
// }
//
// //Locate the new clip workflow
// while ( it != end )
// {