MetaDataWorker.cpp 10.5 KB
Newer Older
Christophe Courtaut's avatar
Christophe Courtaut committed
1
/*****************************************************************************
2
 * MetaDataWorker.cpp: Implement the thread that will get the media informations
Christophe Courtaut's avatar
Christophe Courtaut committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *****************************************************************************
 * 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.
 *****************************************************************************/
22

Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
23
#include <QtDebug>
24 25 26
#include <QPainter>
#include <QLabel>
#include <QImage>
27
#include "vlmc.h"
28
#include "MetaDataWorker.h"
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
29 30
#include "Library.h"

31 32 33
#include <QThreadPool>
#include <QRunnable>

Geoffroy Lacarriere's avatar
Geoffroy Lacarriere committed
34
class AudioSpectrumHelper : public QRunnable
35 36 37 38 39 40 41 42 43
{

    private:
        QList<int>*     m_audioValueList;
        QImage*         m_image;
        QPainter*       m_painter;
        QPainterPath    m_path;

    public:
Geoffroy Lacarriere's avatar
Geoffroy Lacarriere committed
44
        AudioSpectrumHelper(QList<int>* audioValueList) : m_audioValueList( audioValueList )
45
        {
46 47
            int imageHeight = 50;
            m_image = new QImage( m_audioValueList->count(), imageHeight, QImage::Format_RGB32 );
48
            m_image->fill( 0 );
49

50 51 52 53 54 55 56
            m_painter = new QPainter( m_image );
            m_painter->setRenderHint( QPainter::Antialiasing, true );
            m_painter->setPen( QPen( QColor( 79, 106, 25 ), 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin ) );
        }

        void    run()
        {
57 58 59 60 61 62 63 64 65 66
            int averageValue = 0;
            qreal max = 0;
            for( int i = 0; i < m_audioValueList->count(); i++ )
            {
                averageValue += m_audioValueList->at(i);
                if ( m_audioValueList->at(i) > max )
                    max = m_audioValueList->at(i);
            }
            averageValue /= m_audioValueList->count();

67 68
            for( int x = 0; x < m_audioValueList->count(); x++ )
            {
69 70 71
                qreal y = ( (qreal)m_audioValueList->at(x) / max ) * 500;
                y -= 365;
                m_path.lineTo( x, y );
72 73 74 75 76
            }
            m_painter->drawPath( m_path );
        }
};

77 78 79 80
MetaDataWorker::MetaDataWorker( LibVLCpp::MediaPlayer* mediaPlayer, Media* media, MetaDataWorker::MetaDataType type ) :
        m_mediaPlayer( mediaPlayer ),
        m_type( type ),
        m_media( media ),
81
        m_mediaIsPlaying( false),
82 83
        m_lengthHasChanged( false ),
        m_audioBuffer( NULL )
84 85 86
{
}

87
MetaDataWorker::~MetaDataWorker()
88
{
89
    delete m_audioBuffer;
90 91
}

92
void    MetaDataWorker::compute()
93
{
94
    if ( m_media->getFileType() == Media::Video )
95
    {
96
        if ( m_type == Audio )
97 98 99
            computeAudioMetaData();
        else
            computeVideoMetaData();
100
    }
101
    else if ( m_media->getFileType() == Media::Image )
102 103 104
    {
        computeImageMetaData();
    }
105

106 107
    m_media->addConstantParam( ":vout=dummy" );
    m_mediaPlayer->setMedia( m_media->getVLCMedia() );
108 109
    if ( m_type != Audio )
        connect( m_mediaPlayer, SIGNAL( playing() ), this, SLOT( entrypointPlaying() ) );
110
    m_mediaPlayer->play();
111
    m_media->flushVolatileParameters();
112 113
}

114
void    MetaDataWorker::computeVideoMetaData()
115 116
{
    //Disabling audio for this specific use of the media
117
    m_media->addVolatileParam( ":no-audio", ":audio" );
118
    connect( m_mediaPlayer, SIGNAL( lengthChanged() ), this, SLOT( entrypointLengthChanged() ) );
119 120
}

121
void    MetaDataWorker::computeImageMetaData()
122
{
123 124
    m_media->addVolatileParam( ":access=fake", ":access=''" );
    m_media->addVolatileParam( ":fake-duration=10000", ":fake-duration=''" );
125 126
    //There can't be a length for an image file, so we don't have to wait for it to be updated.
    m_lengthHasChanged = true;
127 128
}

129 130 131 132 133 134 135 136 137
void    MetaDataWorker::computeAudioMetaData()
{
    m_media->getVLCMedia()->addOption( ":no-sout-video" );
    m_media->getVLCMedia()->addOption( ":sout=#transcode{}:smem" );
    m_media->getVLCMedia()->setAudioDataCtx( this );
    m_media->getVLCMedia()->setAudioLockCallback( reinterpret_cast<void*>( lock ) );
    m_media->getVLCMedia()->setAudioUnlockCallback( reinterpret_cast<void*>( unlock ) );
    m_media->getVLCMedia()->addOption( ":sout-transcode-acodec=s16l" );
    m_media->getVLCMedia()->addOption( ":no-sout-smem-time-sync" );
138 139
    m_media->getVLCMedia()->addOption( ":no-sout-keep" );
    connect( m_mediaPlayer, SIGNAL( endReached() ), this, SLOT( generateAudioSpectrum() ), Qt::QueuedConnection );
140 141
}

142
void    MetaDataWorker::getMetaData()
143
{
144
    m_mediaIsPlaying = false;
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
145
    m_lengthHasChanged = false;
146

147
    //In order to wait for the VOUT to be ready:
148 149 150
    //Until we have a way of knowing when it is, both getWidth and getHeight method
    //will trigger exception... so we shut it up.
    LibVLCpp::Exception::setErrorCallback( LibVLCpp::Exception::silentExceptionHandler );
151
    while ( m_mediaPlayer->hasVout() == false )
152
    {
153
        SleepMS( 1 ); //Ugly isn't it :)
154
    }
155 156
    LibVLCpp::Exception::setErrorCallback( NULL );

157
    if ( m_type == MetaData )
158
    {
159 160 161 162 163 164 165
        m_media->setLength( m_mediaPlayer->getLength() );
        m_media->setWidth( m_mediaPlayer->getWidth() );
        m_media->setHeight( m_mediaPlayer->getHeight() );
        m_media->setFps( m_mediaPlayer->getFps() );
        if ( m_media->getFps() == .0f )
        {
            qWarning() << "Invalid FPS for media:" << m_media->getFileInfo()->absoluteFilePath();
166
            m_media->setFps( Clip::DefaultFPS );
167 168
        }
        m_media->setNbFrames( (m_media->getLengthMS() / 1000) * m_media->getFps() );
169 170
        if ( m_mediaPlayer->isPlaying() )
            m_mediaPlayer->stop();
171
        emit mediaPlayerIdle( m_mediaPlayer );
172
        m_media->emitMetaDataComputed( true );
173 174
        delete this;
        return;
175
    }
176
    else if ( m_type == Snapshot )
177
    {
178 179 180 181 182 183 184 185
        //Setting time for snapshot :
        if ( m_media->getFileType() == Media::Video )
        {
            connect( m_mediaPlayer, SIGNAL( positionChanged() ), this, SLOT( renderSnapshot() ) );
            m_mediaPlayer->setTime( m_mediaPlayer->getLength() / 3 );
        }
        else
            connect( this, SIGNAL( snapshotRequested() ), this, SLOT( renderSnapshot() ) );
186
    }
187
}
188

189
void    MetaDataWorker::renderSnapshot()
190
{
191
    if ( m_media->getFileType() == Media::Video )
192
        disconnect( m_mediaPlayer, SIGNAL( positionChanged() ), this, SLOT( renderSnapshot() ) );
193 194
    else
        disconnect( this, SIGNAL( snapshotRequested() ), this, SLOT( renderSnapshot() ) );
195 196 197
    QTemporaryFile tmp;
    tmp.setAutoRemove( false );
    tmp.open();
198
    m_tmpSnapshotFilename = tmp.fileName();
199

200
    connect( m_mediaPlayer, SIGNAL( snapshotTaken() ), this, SLOT( setSnapshot() ), Qt::QueuedConnection );
201 202

    //The slot should be triggered in this methode
203
    m_mediaPlayer->takeSnapshot( m_tmpSnapshotFilename.toStdString().c_str()
204
                                 , 0, 0 );
205 206 207
    //Snapshot slot should has been called (but maybe not in next version...)
}

208
void    MetaDataWorker::setSnapshot()
209
{
210 211 212 213
    QPixmap* pixmap = new QPixmap( m_tmpSnapshotFilename );
    if ( pixmap->isNull() )
        delete pixmap;
    else
214
        m_media->setSnapshot( pixmap );
215 216 217 218
    //TODO : we shouldn't have to do this... patch vlc to get a memory snapshot.
    QFile   tmp( m_tmpSnapshotFilename );
    tmp.remove();

219 220 221 222
    disconnect( m_mediaPlayer, SIGNAL( snapshotTaken() ), this, SLOT( setSnapshot() ) );

    //CHECKME:
    //This is synchrone, but it may become asynchrone in the future...
223
//    connect( m_mediaPlayer, SIGNAL( stopped () ), this, SLOT( mediaPlayerStopped() ), Qt::QueuedConnection );
224
    m_mediaPlayer->stop();
225
    emit mediaPlayerIdle( m_mediaPlayer );
226

227 228 229 230
    if ( m_type == Snapshot )
        m_media->emitSnapshotComputed();
    else
        m_media->emitMetaDataComputed( true );
231

232 233 234
    delete this;
}

235
void    MetaDataWorker::entrypointLengthChanged()
236
{
237 238 239 240
    disconnect( m_mediaPlayer, SIGNAL( lengthChanged() ), this, SLOT( entrypointLengthChanged() ) );
    m_lengthHasChanged = true;
    if ( m_mediaIsPlaying == true )
        getMetaData();
241 242
}

243
void    MetaDataWorker::entrypointPlaying()
244
{
245 246 247 248
    disconnect( m_mediaPlayer, SIGNAL( playing() ), this, SLOT( entrypointPlaying() ) );
    m_mediaIsPlaying = true;
    if ( m_lengthHasChanged == true )
        getMetaData();
249 250
}

251
void        MetaDataWorker::lock( MetaDataWorker* metaDataWorker, uint8_t** pcm_buffer , unsigned int size )
252
{
253 254 255
    if ( metaDataWorker->m_audioBuffer == NULL )
        metaDataWorker->m_audioBuffer = new unsigned char[size];
    *pcm_buffer = metaDataWorker->m_audioBuffer;
256 257
}

258 259 260 261
void        MetaDataWorker::unlock( MetaDataWorker* metaDataWorker, uint8_t* pcm_buffer,
                                      unsigned int channels, unsigned int rate,
                                      unsigned int nb_samples, unsigned int bits_per_sample,
                                      unsigned int size, int pts )
262
{
263 264 265 266 267 268 269 270
    Q_UNUSED( rate );
    Q_UNUSED( size );
    Q_UNUSED( pts );

    int bytePerChannelPerSample = bits_per_sample / 8;

    int leftAverage = 0;
    int rightAverage = 0;
271

272
    int it = 0;
273
    for ( unsigned int i = 0; i < nb_samples; i++)
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
    {
        int left = 0;
        int right = 0;
        for ( int u = 0 ; u < bytePerChannelPerSample; u++, it++ )
        {
            int increment = 0;
            if ( channels == 2 )
                increment = bytePerChannelPerSample;
            left <<= 8;
            left += pcm_buffer[ it ];
            right <<= 8;
            right += pcm_buffer[ it + increment ];
        }
        leftAverage += left;
        rightAverage += right;
    }
    leftAverage /= nb_samples;
    metaDataWorker->addAudioValue( leftAverage );
292 293
}

294
void    MetaDataWorker::generateAudioSpectrum()
295
{
296 297 298
    disconnect( m_mediaPlayer, SIGNAL( endReached() ), this, SLOT( generateAudioSpectrum() ) );
    m_mediaPlayer->stop();
    emit mediaPlayerIdle( m_mediaPlayer );
Geoffroy Lacarriere's avatar
Geoffroy Lacarriere committed
299
    AudioSpectrumHelper* audioSpectrum = new AudioSpectrumHelper( m_media->getAudioValues() );
300 301 302
    audioSpectrum->setAutoDelete( true );
    QThreadPool::globalInstance()->start( audioSpectrum );
    delete this;
303
}
304

305
void    MetaDataWorker::addAudioValue( int value )
306
{
307
    m_media->getAudioValues()->append( value );
308
}