ClipWorkflow.cpp 9.3 KB
Newer Older
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*****************************************************************************
 * ClipWorkflow.cpp : Clip workflow. Will extract a single frame from a VLCMedia
 *****************************************************************************
 * 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 "vlmc.h"
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
24
#include "ClipWorkflow.h"
25
#include "MemoryPool.hpp"
26
#include "LightVideoFrame.h"
27 28 29 30 31
#include "Clip.h"
#include "WaitCondition.hpp"

#include <QWaitCondition>
#include <QtDebug>
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
32

33
ClipWorkflow::ClipWorkflow( Clip::Clip* clip ) :
34
                m_mediaPlayer(NULL),
35
                m_requiredState( ClipWorkflow::None ),
36
                m_clip( clip ),
37
                m_state( ClipWorkflow::Stopped )
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
38
{
39 40
    m_stateLock = new QReadWriteLock;
    m_requiredStateLock = new QMutex;
41
    m_initWaitCond = new WaitCondition;
42
    m_pausingStateWaitCond = new WaitCondition;
43
    m_renderLock = new QMutex;
44
    m_feedingCondWait = new WaitCondition;
45 46
    m_availableBuffersMutex = new QMutex;
    m_computedBuffersMutex = new QMutex;
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
47 48 49 50
}

ClipWorkflow::~ClipWorkflow()
{
51
    delete m_renderLock;
52
    delete m_pausingStateWaitCond;
53
    delete m_initWaitCond;
54 55
    delete m_requiredStateLock;
    delete m_stateLock;
56 57
    delete m_availableBuffersMutex;
    delete m_computedBuffersMutex;
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
58 59
}

60 61 62
void    ClipWorkflow::checkStateChange()
{
    QMutexLocker    lock( m_requiredStateLock );
63
    QWriteLocker    lock2( m_stateLock );
64 65 66
    if ( m_requiredState != ClipWorkflow::None )
    {
        m_state = m_requiredState;
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
67
//        qDebug() << '[' << (void*)this << "] Applying required state change:" << m_state;
68 69 70 71
        m_requiredState = ClipWorkflow::None;
    }
}

72
void    ClipWorkflow::initialize()
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
73
{
74
    if ( m_clip->getParent()->getFileType() == Media::Image )
Ludovic Fauvet's avatar
Ludovic Fauvet committed
75
        m_vlcMedia = new LibVLCpp::Media( "fake:///" + m_clip->getParent()->getFileInfo()->absoluteFilePath() );
76
    else
Ludovic Fauvet's avatar
Ludovic Fauvet committed
77
        m_vlcMedia = new LibVLCpp::Media( "file:///" + m_clip->getParent()->getFileInfo()->absoluteFilePath() );
78 79
    m_currentPts = -1;
    m_previousPts = -1;
80
    m_pauseDuration = -1;
81
    initVlcOutput();
82
    m_mediaPlayer = MemoryPool<LibVLCpp::MediaPlayer>::getInstance()->get();
83
    m_mediaPlayer->setMedia( m_vlcMedia );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
84

85
    connect( m_mediaPlayer, SIGNAL( playing() ), this, SLOT( loadingComplete() ), Qt::DirectConnection );
86
    connect( m_mediaPlayer, SIGNAL( endReached() ), this, SLOT( clipEndReached() ), Qt::DirectConnection );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
87 88 89
    m_mediaPlayer->play();
}

90 91
void    ClipWorkflow::loadingComplete()
{
92
    adjustBegin();
93
    disconnect( m_mediaPlayer, SIGNAL( playing() ), this, SLOT( loadingComplete() ) );
94 95
    connect( m_mediaPlayer, SIGNAL( playing() ), this, SLOT( mediaPlayerUnpaused() ) );
    connect( m_mediaPlayer, SIGNAL( paused() ), this, SLOT( mediaPlayerPaused() ) );
96 97 98
    QMutexLocker    lock( m_initWaitCond->getMutex() );
    setState( Rendering );
    m_initWaitCond->wake();
99 100
}

101 102
void    ClipWorkflow::adjustBegin()
{
103 104
    if ( m_clip->getParent()->getFileType() == Media::Video || m_clip->getParent()->getFileType() == Media::Audio )
        m_mediaPlayer->setTime( m_clip->getBegin() / m_clip->getParent()->getFps() * 1000 );
105 106
}

107 108
bool    ClipWorkflow::isEndReached() const
{
109 110 111 112 113 114 115 116 117 118 119 120 121
    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
{
    return m_state;
122
}
123

124
void    ClipWorkflow::clipEndReached()
125
{
126
    setState( EndReached );
127
}
128

129
Clip*     ClipWorkflow::getClip()
130 131 132
{
    return m_clip;
}
133 134 135

void            ClipWorkflow::stop()
{
136 137 138 139
    if ( m_mediaPlayer )
    {
        m_mediaPlayer->stop();
        disconnect( m_mediaPlayer, SIGNAL( endReached() ), this, SLOT( clipEndReached() ) );
140
        MemoryPool<LibVLCpp::MediaPlayer>::getInstance()->release( m_mediaPlayer );
141 142 143 144
        m_mediaPlayer = NULL;
        setState( Stopped );
        QMutexLocker    lock( m_requiredStateLock );
        m_requiredState = ClipWorkflow::None;
145
        delete m_vlcMedia;
146 147 148
    }
    else
        qDebug() << "ClipWorkflow has already been stopped";
149 150
}

151
void            ClipWorkflow::setTime( qint64 time )
152
{
153
    qDebug() << "setting clipworkflow time to:" << time;
154
    m_mediaPlayer->setTime( time );
155
    flushComputedBuffers();
156
}
157

158
bool            ClipWorkflow::isRendering() const
159
{
160 161
    QReadLocker lock( m_stateLock );
    return m_state == ClipWorkflow::Rendering;
162
}
163

164
void            ClipWorkflow::setState( State state )
165
{
166
    QWriteLocker    lock( m_stateLock );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
167
//        qDebug() << '[' << (void*)this << "] Setting state to" << state;
168
    m_state = state;
169
}
170 171 172 173 174 175

void            ClipWorkflow::queryStateChange( State newState )
{
    QMutexLocker    lock( m_requiredStateLock );
    m_requiredState = newState;
}
176 177 178 179 180 181

QReadWriteLock* ClipWorkflow::getStateLock()
{
    return m_stateLock;
}

182 183
void        ClipWorkflow::waitForCompleteInit()
{
184
    if ( isRendering() == false )
185 186 187 188
    {
        QMutexLocker    lock( m_initWaitCond->getMutex() );
        m_initWaitCond->waitLocked();
    }
189 190
}

191 192 193 194
LibVLCpp::MediaPlayer*       ClipWorkflow::getMediaPlayer()
{
    return m_mediaPlayer;
}
195

196 197 198 199
void        ClipWorkflow::preGetOutput()
{
    QMutexLocker    lock( m_feedingCondWait->getMutex() );

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
200
    //Computed buffer mutex is already locked by underlying clipworkflow getoutput method
201
    if ( getNbComputedBuffers() == 0 )
202
    {
203
        qWarning() << "Waiting for buffer to be fed";
204
        m_renderLock->unlock();
205
        m_computedBuffersMutex->unlock();
206
        qDebug() << "Unlocked render lock, entering cond wait";
207
        m_feedingCondWait->waitLocked();
208
        m_computedBuffersMutex->lock();
209 210 211 212
        m_renderLock->lock();
    }
}

213
void        ClipWorkflow::postGetOutput()
214
{
215
    //If we're running out of computed buffers, refill our stack.
216 217
    if ( debugType == 1 )
        qDebug() << "AudioClipWorkflow::postGetOutput";
218
    if ( getNbComputedBuffers() < getMaxComputedBuffers() / 3 )
219
    {
220 221
        QWriteLocker        lock( m_stateLock );
        if ( m_state == ClipWorkflow::Paused )
222
        {
223
            qWarning() << "Unpausing media player. type:" << debugType;
224
            m_state = ClipWorkflow::UnpauseRequired;
225
//            This will act like an "unpause";
226 227
            m_mediaPlayer->pause();
        }
228 229
        else
            qCritical() << "Running out of computed buffers !";
230
    }
231 232
}

233 234
void        ClipWorkflow::commonUnlock()
{
235 236
    //Don't test using availableBuffer, as it may evolve if a buffer is required while
    //no one is available : we would spawn a new buffer, thus modifying the number of available buffers
237
    if ( getNbComputedBuffers() == getMaxComputedBuffers() )
238
    {
239
//        qWarning() << "Pausing clip workflow. Type:" << debugType;
240
        setState( ClipWorkflow::PauseRequired );
241
        m_mediaPlayer->pause();
242
    }
243
    if ( getNbComputedBuffers() == 1 )
244
    {
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
245
//        qDebug() << "Waking feeding cont wait... acquiring lock. Type:" << debugType;
246
        QMutexLocker    lock( m_feedingCondWait->getMutex() );
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
247
//        qDebug() << "feeding cont wait mutex acquired. Type:" << debugType;
248 249
        m_feedingCondWait->wake();
    }
250 251
    checkStateChange();
}
252 253 254

void    ClipWorkflow::computePtsDiff( qint64 pts )
{
255 256 257 258 259
//    if ( debugType == 1 )
//    {
//        qDebug() << "in computePtsDiff, Before << : pts:" << pts << "previousPts:" << m_previousPts
//                << "currentPts:" << m_currentPts;
//    }
260 261 262 263
    if ( m_previousPts == -1 )
        m_previousPts = pts;
    if ( m_currentPts == -1 )
        m_currentPts = pts;
264 265
    if ( m_pauseDuration != -1 )
    {
266
//        qDebug() << "In pause mode";
267 268 269 270 271
        m_previousPts = m_currentPts + m_pauseDuration;
        m_pauseDuration = -1;
    }
    else
        m_previousPts = m_currentPts;
272 273 274 275 276 277 278 279 280
//    if ( debugType == 1 )
//    {
//        qDebug() << "in computePtsDiff, After >> : pts:" << pts << "previousPts:" << m_previousPts
//                << "currentPts:" << m_currentPts;
//    }
//    if ( debugType == 1 && pts < m_previousPts )
//    {
//        qCritical() << "New PTS is lower than previous PTS !<<<<<<<<<<<<<<<<<<<<<<<<<<";
//    }
281 282
    m_currentPts = qMax( pts, m_previousPts );
}
283 284 285

void    ClipWorkflow::mediaPlayerPaused()
{
286
    qWarning() << "\n\nMedia player paused, waiting for buffers to be consumed.Type:" << debugType << "\n\n";
287
    setState( ClipWorkflow::Paused );
288 289
    m_beginPausePts = mdate();
//    qDebug() << "got pause pts:" << m_beginPausePts;
290 291 292 293
}

void    ClipWorkflow::mediaPlayerUnpaused()
{
294
    qWarning() << "Media player unpaused. Go back to rendering. Type:" << debugType;
295
    setState( ClipWorkflow::Rendering );
296
    m_pauseDuration = mdate() - m_beginPausePts;
297
}