MetaDataWorker.cpp 12.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>

34 35 36
static int      count = 0;

class AudioSpectrum : public QRunnable
37 38 39 40 41 42 43 44 45
{

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

    public:
46
        AudioSpectrum(QList<int>* audioValueList) : m_audioValueList( audioValueList )
47
        {
48 49
            int imageHeight = 50;
            m_image = new QImage( m_audioValueList->count(), imageHeight, QImage::Format_RGB32 );
50
            m_image->fill( 0 );
51

52 53 54 55 56 57 58
            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()
        {
59 60 61 62 63 64 65 66 67 68 69 70
            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();
            //qDebug() << "averageValue: " << averageValue;
            //qDebug() << "Max: " << max;

71 72
            for( int x = 0; x < m_audioValueList->count(); x++ )
            {
73 74
                qreal y = ( (qreal)m_audioValueList->at(x) / max ) * 500;
                y -= 365;
75
                //qDebug() << y;
76
                m_path.lineTo( x, y );
77 78
            }
            m_painter->drawPath( m_path );
79
            //m_image->save( "/home/geoff/testSpectrum/" + QString::number(count++) + ".png", "png", 100 );
80 81 82
        }
};

83 84 85 86
MetaDataWorker::MetaDataWorker( LibVLCpp::MediaPlayer* mediaPlayer, Media* media, MetaDataWorker::MetaDataType type ) :
        m_mediaPlayer( mediaPlayer ),
        m_type( type ),
        m_media( media ),
87
        m_mediaIsPlaying( false),
88 89
        m_lengthHasChanged( false ),
        m_audioBuffer( NULL )
90 91 92
{
}

93
MetaDataWorker::~MetaDataWorker()
94
{
95
    delete m_audioBuffer;
96 97
    //if ( m_mediaPlayer->isPlaying() )
    //    m_mediaPlayer->stop();
98 99
}

100
void    MetaDataWorker::compute()
101
{
102
    if ( m_media->getFileType() == Media::Video )
103
    {
104
        if ( m_type == Audio )
105 106 107
            computeAudioMetaData();
        else
            computeVideoMetaData();
108
    }
109
    else if ( m_media->getFileType() == Media::Image )
110 111 112
    {
        computeImageMetaData();
    }
113

114
    //qDebug() << "Preparing computation for metadata. Type:" << m_type;
115
    m_media->addConstantParam( ":vout=dummy" );
116
    //qDebug() << "Setting media:" << (void*)m_media;
117
    m_mediaPlayer->setMedia( m_media->getVLCMedia() );
118 119 120
    if ( m_type != Audio )
        connect( m_mediaPlayer, SIGNAL( playing() ), this, SLOT( entrypointPlaying() ) );
    //qDebug() << "Starting playback." << (void*)this;
121
    m_mediaPlayer->play();
122
    m_media->flushVolatileParameters();
123 124
}

125
void    MetaDataWorker::computeVideoMetaData()
126 127
{
    //Disabling audio for this specific use of the media
128
    m_media->addVolatileParam( ":no-audio", ":audio" );
129
    connect( m_mediaPlayer, SIGNAL( lengthChanged() ), this, SLOT( entrypointLengthChanged() ) );
130 131
}

132
void    MetaDataWorker::computeImageMetaData()
133
{
134 135
    m_media->addVolatileParam( ":access=fake", ":access=''" );
    m_media->addVolatileParam( ":fake-duration=10000", ":fake-duration=''" );
136 137
    //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;
138 139
}

140 141 142 143
void    MetaDataWorker::computeAudioMetaData()
{
    m_media->getVLCMedia()->addOption( ":no-sout-video" );
    m_media->getVLCMedia()->addOption( ":sout=#transcode{}:smem" );
144
    //qDebug() << "setting audio ctx to" << (void*)this;
145 146 147 148 149
    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" );
150 151
    m_media->getVLCMedia()->addOption( ":no-sout-keep" );
    connect( m_mediaPlayer, SIGNAL( endReached() ), this, SLOT( generateAudioSpectrum() ), Qt::QueuedConnection );
152 153
}

154
void    MetaDataWorker::getMetaData()
155
{
156
    m_mediaIsPlaying = false;
Hugo Beauzee-Luyssen's avatar
Hugo Beauzee-Luyssen committed
157
    m_lengthHasChanged = false;
158

159
    //In order to wait for the VOUT to be ready:
160 161 162
    //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 );
163
    while ( m_mediaPlayer->hasVout() == false )
164
    {
165
        SleepMS( 1 ); //Ugly isn't it :)
166
    }
167 168
    LibVLCpp::Exception::setErrorCallback( NULL );

169
    if ( m_type == MetaData )
170
    {
171 172 173 174 175 176 177
        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();
178
            m_media->setFps( Clip::DefaultFPS );
179 180
        }
        m_media->setNbFrames( (m_media->getLengthMS() / 1000) * m_media->getFps() );
181
//        connect( m_mediaPlayer, SIGNAL( stopped () ), this, SLOT( mediaPlayerStopped() ), Qt::QueuedConnection );
182 183
        if ( m_mediaPlayer->isPlaying() )
            m_mediaPlayer->stop();
184
        emit mediaPlayerIdle( m_mediaPlayer );
185
        m_media->emitMetaDataComputed( true );
186 187
        delete this;
        return;
188
    }
189
    else if ( m_type == Snapshot )
190
    {
191 192 193 194 195 196 197 198
        //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() ) );
199
    }
200
}
201

202
void    MetaDataWorker::renderSnapshot()
203
{
204
    if ( m_media->getFileType() == Media::Video )
205
        disconnect( m_mediaPlayer, SIGNAL( positionChanged() ), this, SLOT( renderSnapshot() ) );
206 207
    else
        disconnect( this, SIGNAL( snapshotRequested() ), this, SLOT( renderSnapshot() ) );
208 209 210
    QTemporaryFile tmp;
    tmp.setAutoRemove( false );
    tmp.open();
211
    m_tmpSnapshotFilename = tmp.fileName();
212

213
    connect( m_mediaPlayer, SIGNAL( snapshotTaken() ), this, SLOT( setSnapshot() ), Qt::QueuedConnection );
214 215

    //The slot should be triggered in this methode
216
    m_mediaPlayer->takeSnapshot( m_tmpSnapshotFilename.toStdString().c_str()
217
                                 , 0, 0 );
218 219 220
    //Snapshot slot should has been called (but maybe not in next version...)
}

221
void    MetaDataWorker::setSnapshot()
222
{
223 224 225 226
    QPixmap* pixmap = new QPixmap( m_tmpSnapshotFilename );
    if ( pixmap->isNull() )
        delete pixmap;
    else
227
        m_media->setSnapshot( pixmap );
228 229 230 231
    //TODO : we shouldn't have to do this... patch vlc to get a memory snapshot.
    QFile   tmp( m_tmpSnapshotFilename );
    tmp.remove();

232 233 234 235
    disconnect( m_mediaPlayer, SIGNAL( snapshotTaken() ), this, SLOT( setSnapshot() ) );

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

240 241 242 243
    if ( m_type == Snapshot )
        m_media->emitSnapshotComputed();
    else
        m_media->emitMetaDataComputed( true );
244

245 246 247
    delete this;
}

248
void    MetaDataWorker::entrypointLengthChanged()
249
{
250 251 252 253
    disconnect( m_mediaPlayer, SIGNAL( lengthChanged() ), this, SLOT( entrypointLengthChanged() ) );
    m_lengthHasChanged = true;
    if ( m_mediaIsPlaying == true )
        getMetaData();
254 255
}

256
void    MetaDataWorker::entrypointPlaying()
257
{
258 259 260 261
    disconnect( m_mediaPlayer, SIGNAL( playing() ), this, SLOT( entrypointPlaying() ) );
    m_mediaIsPlaying = true;
    if ( m_lengthHasChanged == true )
        getMetaData();
262 263
}

264
void        MetaDataWorker::lock( MetaDataWorker* metaDataWorker, uint8_t** pcm_buffer , unsigned int size )
265
{
266 267 268
    //qDebug() << "[" << (void*)metaDataWorker << "] toto";
//    qDebug() << metaDataWorker->m_media->getFileName();
//    qDebug() << "tata";
269 270 271
    if ( metaDataWorker->m_audioBuffer == NULL )
        metaDataWorker->m_audioBuffer = new unsigned char[size];
    *pcm_buffer = metaDataWorker->m_audioBuffer;
272 273
}

274 275 276 277
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 )
278
{
279
    //qDebug() << "Unlock";
280 281 282 283 284 285 286 287
    Q_UNUSED( rate );
    Q_UNUSED( size );
    Q_UNUSED( pts );

    int bytePerChannelPerSample = bits_per_sample / 8;

    int leftAverage = 0;
    int rightAverage = 0;
288

289
    int it = 0;
290
    for ( unsigned int i = 0; i < nb_samples; i++)
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
    {
        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 );
309
    //qDebug() << leftAverage;
310 311
}

312
void    MetaDataWorker::generateAudioSpectrum()
313
{
314 315 316 317 318 319 320 321
    disconnect( m_mediaPlayer, SIGNAL( endReached() ), this, SLOT( generateAudioSpectrum() ) );
    m_mediaPlayer->stop();
    emit mediaPlayerIdle( m_mediaPlayer );
    //qDebug() << "Audio data stocked in " << m_media->getFileName();
    AudioSpectrum* audioSpectrum = new AudioSpectrum( m_media->getAudioValues() );
    audioSpectrum->setAutoDelete( true );
    QThreadPool::globalInstance()->start( audioSpectrum );
    delete this;
322
}
323

324
void    MetaDataWorker::addAudioValue( int value )
325
{
326
    m_media->getAudioValues()->append( value );
327 328
}

329
void    MetaDataWorker::generateAudioPixmap()
330
{
331
    m_audioDebugWidget = new QLabel();
332 333
    m_audioDebugWidget->setFixedSize( m_media->getAudioValues()->count(), 500 );
    //m_audioDebugWidget->show();
334 335

    //QImage* image = new QImage( 300, 300, QImage::Format_RGB32);
336
    QPixmap image(m_media->getAudioValues()->count(), 500);
337 338 339 340 341
    QPainter painter(&image);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setPen( QPen( QColor( 79, 106, 25 ), 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin ) );

    QPainterPath path;
342 343 344 345 346
    int max = 0;
    for( int x = 0; x < m_media->getAudioValues()->count(); x++ )
        if( m_media->getAudioValues()->count() > max )
            max = m_media->getAudioValues()->at(x);
    for( int x = 0; x < m_media->getAudioValues()->count(); x++ )
347
    {
348 349 350 351
        qreal y = ( (qreal)m_media->getAudioValues()->at(x) / max ) * 500;
        y -= 700;
        //qDebug() << y;
        path.lineTo( x, y );
352 353 354 355
    }

    painter.drawPath(path);
    m_audioDebugWidget->setPixmap(image);
356
}