File indexing completed on 2024-05-12 16:35:05

0001 /* This file is part of the KDE project
0002 * Copyright (C) 2012 Gopalakrishna Bhat A <gopalakbhat@gmail.com>
0003 * Copyright (C) 2006-2009 Marco Gulino <marco.gulino@gmail.com>
0004 *
0005 * This library is free software; you can redistribute it and/or
0006 * modify it under the terms of the GNU Library General Public
0007 * License as published by the Free Software Foundation; either
0008 * version 2 of the License, or (at your option) any later version.
0009 *
0010 * This library is distributed in the hope that it will be useful,
0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013 * Library General Public License for more details.
0014 *
0015 * You should have received a copy of the GNU Library General Public License
0016 * along with this library; see the file COPYING.LIB.  If not, write to
0017 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018 * Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "VideoThumbnailer.h"
0022 
0023 #include "VideoData.h"
0024 #include "VideoDebug.h"
0025 
0026 #include <phonon/experimental/videoframe2.h>
0027 #include <QTime>
0028 #include <QVBoxLayout>
0029 #include <QVarLengthArray>
0030 
0031 
0032 #define THRESHOLD_FRAME_VARIANCE 40.0
0033 
0034 VideoThumbnailer::VideoThumbnailer()
0035     : QObject()
0036 {
0037     m_vdata.setRunning(true);
0038     Phonon::createPath(&m_media, &m_vdata);
0039     connect(&m_media, SIGNAL(stateChanged(Phonon::State,Phonon::State)), this,
0040         SLOT(stateChanged(Phonon::State,Phonon::State)));
0041     connect(this,SIGNAL(signalCreateThumbnail(VideoData*,QSize)),
0042             this, SLOT(slotCreateThumbnail(VideoData*,QSize)), Qt::QueuedConnection);
0043 }
0044 
0045 VideoThumbnailer::~VideoThumbnailer()
0046 {
0047 }
0048 
0049 void VideoThumbnailer::createThumbnail(VideoData *videoData, const QSize &size)
0050 {
0051     emit signalCreateThumbnail(videoData, size);
0052 }
0053 
0054 void VideoThumbnailer::slotCreateThumbnail(VideoData *videoData, const QSize &size)
0055 {
0056         m_media.setCurrentSource(videoData->playableUrl());
0057         m_media.play();
0058 
0059         m_thumbnailSize = size;
0060 
0061 
0062         int retcode = 0;
0063         for (int i = 0; i < 50; i++) {
0064             retcode = m_eventLoop.exec();
0065             if (retcode == 0) {
0066                 break;
0067             }
0068             debugVideo << "Seeking to " << (i * 3);
0069             m_media.seek(i * 3);
0070         }
0071         if (retcode) {
0072                warnVideo << "Unable to generate thumbnail for ";
0073                m_media.stop();
0074                return;
0075         }
0076         m_media.stop();
0077 
0078         emit thumbnailReady();
0079 }
0080 
0081 void VideoThumbnailer::frameReady(const Phonon::Experimental::VideoFrame2 &frame)
0082 {
0083     QImage thumb = frame.qImage().scaled(m_thumbnailSize.width(), m_thumbnailSize.height(), Qt::KeepAspectRatio);
0084     if (isFrameInteresting(thumb)) {
0085         m_thumbnailImage = thumb;
0086         m_vdata.disconnect(SIGNAL(frameReadySignal(Phonon::Experimental::VideoFrame2)),
0087             this, SLOT(frameReady(Phonon::Experimental::VideoFrame2)));
0088         m_eventLoop.quit();
0089         return;
0090     }
0091     m_eventLoop.exit(1);
0092 }
0093 
0094 void VideoThumbnailer::stateChanged(Phonon::State newState, Phonon::State oldState)
0095 {
0096     Q_UNUSED(oldState);
0097     if (newState == Phonon::PlayingState) {
0098         connect(&m_vdata, SIGNAL(frameReadySignal(Phonon::Experimental::VideoFrame2)),
0099             this, SLOT(frameReady(Phonon::Experimental::VideoFrame2)));
0100         m_eventLoop.exit(1);
0101     }
0102 }
0103 
0104 QImage VideoThumbnailer::thumbnail()
0105 {
0106     return m_thumbnailImage;
0107 }
0108 
0109 bool VideoThumbnailer::isFrameInteresting(const QImage &frame)
0110 {
0111     float variance = 0;
0112     //taken from mplayerthumbs
0113     uint delta=0;
0114     uint avg=0;
0115     uint bytes=frame.numBytes();
0116     uint STEPS=bytes/2;
0117     QVarLengthArray<uchar> pivot(STEPS);
0118 
0119     const uchar *bits=frame.bits();
0120     // First pass: get pivots and taking average
0121     for( uint i=0; i<STEPS ; i++ ){
0122         pivot[i]=bits[i*(bytes/STEPS)];
0123         avg+=pivot[i];
0124     }
0125     avg=avg/STEPS;
0126     // Second Step: calculate delta (average?)
0127     for (uint i=0; i<STEPS; i++)
0128     {
0129         int curdelta=abs(int(avg-pivot[i]));
0130         delta+=curdelta;
0131     }
0132     variance= delta/STEPS;
0133 
0134     return variance > THRESHOLD_FRAME_VARIANCE;
0135 }
0136 
0137