videocollection/videocollectionwrapper/src/videothumbnaildata_p.cpp
changeset 30 4f111d64a341
child 34 bbb98528c666
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/videocollection/videocollectionwrapper/src/videothumbnaildata_p.cpp	Thu Apr 01 22:38:49 2010 +0300
@@ -0,0 +1,572 @@
+/*
+* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  VideoThumbnailDataPrivate class implementation
+*
+*/
+
+// INCLUDE FILES
+#include <QApplication>
+#include <QPixmap>
+#include <QTimer>
+#include <mpxmediageneraldefs.h>
+#include <thumbnailmanager_qt.h>
+
+#include "videothumbnaildata_p.h"
+#include "videocollectionwrapper.h"
+#include "videosortfilterproxymodel.h"
+
+// Maximum thumbnails kept in memory.
+const int THUMBNAIL_CACHE_SIZE = 60;
+// Maximum of thumbnail fetches done at one background fetch round.
+const int THUMBNAIL_BACKGROUND_FETCH_AMOUNT = 20;
+// Milliseconds for the background fetch timer.
+const int THUMBNAIL_BACKGROUND_TIMEOUT = 100;
+// Maximum simultaneous thumbnail fetches.
+const int THUMBNAIL_MAX_SIMULTANEOUS_FETCHES = THUMBNAIL_BACKGROUND_FETCH_AMOUNT * 10;
+// Milliseconds while thumbnail ready events are gathered before they 
+// are signaled.
+const int THUMBNAIL_READY_SIGNAL_TIMEOUT = 50;
+// Priority for background thumbnail fetches.
+const int BACKGROUND_FETCH_PRIORITY = 3000;
+
+/**
+ * global qHash function required fo creating hash values for TMPXItemId -keys
+ */
+inline uint qHash(TMPXItemId key) 
+{ 
+    QPair<uint, uint> keyPair(key.iId1, key.iId2); 
+
+    return qHash(keyPair);
+}
+
+// ================= MEMBER FUNCTIONS =======================
+//
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::VideoThumbnailDataPrivate()
+// -----------------------------------------------------------------------------
+//
+VideoThumbnailDataPrivate::VideoThumbnailDataPrivate() :
+    mThumbnailManager(0),
+    mDefaultTnVideo(0),
+    mDefaultTnCategory(0),
+    mCollectionWrapper(VideoCollectionWrapper::instance()),
+    mModel(0),
+    mCurrentFetchIndex(0),
+    mCurrentBackgroundFetchCount(0),
+    mBgFetchTimer(0),
+    mTbnReportTimer(0),
+    mSignalsConnected(false),
+    mBackgroundFetchingEnabled(true)
+{
+    initialize();
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::~VideoThumbnailDataPrivate()
+// -----------------------------------------------------------------------------
+//
+VideoThumbnailDataPrivate::~VideoThumbnailDataPrivate()
+{
+    cleanup();
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::initialize()
+// -----------------------------------------------------------------------------
+//
+int VideoThumbnailDataPrivate::initialize()
+{
+    mThumbnailData.setMaxCost(THUMBNAIL_CACHE_SIZE);
+    
+    if(!mCollectionWrapper)
+    {
+        return -1;
+    }   
+    
+    if(!mThumbnailManager)
+    {
+        mThumbnailManager = new ThumbnailManager();
+        mThumbnailManager->setThumbnailSize( ThumbnailManager::ThumbnailMedium );
+        mThumbnailManager->setQualityPreference( ThumbnailManager::OptimizeForPerformance );
+    }
+
+    mModel = mCollectionWrapper->getModel();
+    if(!mModel)
+    {
+        cleanup();
+        return -1;
+    }
+
+    if(!mBgFetchTimer)
+    {
+        mBgFetchTimer = new QTimer();
+    }
+    
+    if(!mTbnReportTimer)
+    {
+        mTbnReportTimer = new QTimer();
+    }
+
+    if(connectSignals() < 0)
+    {
+        cleanup();
+        return -1;
+    }
+    
+    return 0;
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::cleanup()
+// -----------------------------------------------------------------------------
+//
+void VideoThumbnailDataPrivate::cleanup()
+{
+    if(mCollectionWrapper)
+    {
+        mCollectionWrapper->decreaseReferenceCount();
+        mCollectionWrapper = 0;
+    }
+    
+    disconnectSignals();
+
+    freeThumbnailData();
+    
+    if(mTbnReportTimer)
+    {
+        mTbnReportTimer->stop();
+        delete mTbnReportTimer;
+        mTbnReportTimer = 0;
+    }
+    
+    if(mBgFetchTimer)
+    {
+        mBgFetchTimer->stop();
+        delete mBgFetchTimer;
+        mBgFetchTimer = 0;
+    }
+
+    delete mThumbnailManager;
+    mThumbnailManager = 0;
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::disconnectSignals()
+// -----------------------------------------------------------------------------
+//
+void VideoThumbnailDataPrivate::disconnectSignals()
+{
+    if(mSignalsConnected)
+    {
+        disconnect(mThumbnailManager, SIGNAL(thumbnailReady( QPixmap , void * , int , int )),
+                    this, SLOT(thumbnailReadySlot( QPixmap , void * , int , int )));
+        disconnect(mModel, SIGNAL(layoutChanged()), this, SLOT(layoutChangedSlot()));
+        disconnect(mModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+                    this, SLOT(rowsInsertedSlot(const QModelIndex &, int, int)));
+        disconnect(mBgFetchTimer, SIGNAL(timeout()), this, SLOT(doBackgroundFetching()));
+        disconnect(mTbnReportTimer, SIGNAL(timeout()), this, SLOT(reportThumbnailsReadySlot()));
+    }
+    mSignalsConnected = false;
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::connectSignals()
+// -----------------------------------------------------------------------------
+//
+int VideoThumbnailDataPrivate::connectSignals()
+{
+    if(!mSignalsConnected)
+    {
+        if(!connect(mThumbnailManager, SIGNAL(thumbnailReady( QPixmap , void * , int , int )),
+                    this, SLOT(thumbnailReadySlot( QPixmap , void * , int , int ))) ||
+           !connect(mModel, SIGNAL(layoutChanged()), this, SLOT(layoutChangedSlot())) ||
+           !connect(mModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+                    this, SLOT(rowsInsertedSlot(const QModelIndex &, int, int))) ||
+           !connect(mBgFetchTimer, SIGNAL(timeout()), this, SLOT(doBackgroundFetching())) ||
+           !connect(mTbnReportTimer, SIGNAL(timeout()), this, SLOT(reportThumbnailsReadySlot())))
+        {
+            return -1;
+        }
+        
+        QApplication *app = qApp;
+        if(!connect(app, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuitSlot())))
+        {
+            return -1;
+        }
+        
+        mSignalsConnected = true;
+    }
+    return 0;
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::getThumbnail()
+// -----------------------------------------------------------------------------
+//
+const QIcon* VideoThumbnailDataPrivate::getThumbnail(TMPXItemId mediaId)
+{
+    const QIcon *thumbnail = mThumbnailData[mediaId];
+    if(!thumbnail)
+    {
+        return defaultThumbnail(mediaId);
+    }
+    return thumbnail;
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::startFetchingThumbnails()
+// -----------------------------------------------------------------------------
+//
+int VideoThumbnailDataPrivate::startFetchingThumbnails(const QList<QModelIndex> &indexes, int priority)
+{
+    if(!mModel || !mThumbnailManager)
+    {
+        return -1;
+    }
+    if(indexes.count() == 0)
+    {
+        return 0;
+    }
+    
+    // Now we fetch the requested thumbnails with higher priority than any of the current fetches.
+    // TODO: Better would be to cancel the current fetches but it causes crashes in 10.1.   
+    
+    int fetchCountBefore = mFetchList.count();
+    
+    // Fetch the thumbnails in ascending priority.
+
+    int startPriority = fetchCountBefore + priority + indexes.count();
+    
+    for(int i = 0; i < indexes.count(); i++)
+    {
+        startFetchingThumbnail(mModel->getMediaIdAtIndex(indexes[i]), startPriority-i);
+    }
+    
+    return mFetchList.count() - fetchCountBefore;
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::startFetchingThumbnail()
+// -----------------------------------------------------------------------------
+//
+int VideoThumbnailDataPrivate::startFetchingThumbnail(TMPXItemId mediaId, int priority)
+{
+    if(!mModel || !mThumbnailManager)
+    {
+        return -1;
+    }
+    if(mFetchList.count() >= THUMBNAIL_MAX_SIMULTANEOUS_FETCHES)
+    {
+        return -1;
+    }
+
+    // Check that it's not fetched before.
+    if(mThumbnailData.contains(mediaId))
+    {
+        return 0;
+    }
+
+    QString fileName = mModel->getMediaFilePathForId(mediaId);
+
+    // object containing media id to be passed throught
+    // thumbnail generation process.
+    TMPXItemId *internal = new TMPXItemId(mediaId.iId1, mediaId.iId2);
+    
+    int tnId = -1;
+    // mThumbnailManager signals into thumbnailReadySlot when thumbnail ready
+    if(fileName.length() > 0)
+    {
+        tnId = mThumbnailManager->getThumbnail(fileName, internal, priority);
+    }
+
+    if( tnId != -1 )
+    {
+        // add to fetching list to indicate we're fetching this tn.
+        mFetchList.insert(tnId);
+    }
+    else
+    {
+        // tn getting starting failed for some reason
+        delete internal;
+    }
+    return tnId;
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::doBackgroundFetching()
+// -----------------------------------------------------------------------------
+//
+void VideoThumbnailDataPrivate::doBackgroundFetching()
+{
+    if(!mModel)
+    {
+        return;
+    }
+
+    if(mCurrentBackgroundFetchCount >= THUMBNAIL_CACHE_SIZE)
+    {
+        return;
+    }
+    
+    int maxIndex = mModel->rowCount();
+    if(maxIndex == 0)
+    {
+        return;
+    }
+
+    // Delta to UI index where fetch has been done already.  
+    int currentDelta = mCurrentBackgroundFetchCount/2;
+    
+    // How many will be fetched.  
+    const int fetchAmount = THUMBNAIL_BACKGROUND_FETCH_AMOUNT/2;
+
+    QList<QModelIndex> indexes;
+
+    // Items before the current fetch index.
+    int startIndex = mCurrentFetchIndex-currentDelta-fetchAmount;
+    int endIndex = mCurrentFetchIndex-currentDelta;
+    getModelIndexes(indexes, startIndex, endIndex);
+
+    // Items after the current fetch index.
+    startIndex = mCurrentFetchIndex+currentDelta;
+    endIndex = mCurrentFetchIndex+currentDelta+fetchAmount;
+    getModelIndexes(indexes, startIndex, endIndex);
+
+    mCurrentBackgroundFetchCount += THUMBNAIL_BACKGROUND_FETCH_AMOUNT;
+
+    int fetchesStarted = startFetchingThumbnails(indexes, BACKGROUND_FETCH_PRIORITY);
+    
+    // No thumbnails to fetch, start again.
+    if(fetchesStarted == 0)
+    {
+        continueBackgroundFetch();
+    }
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::getModelIndexes()
+// -----------------------------------------------------------------------------
+//
+void VideoThumbnailDataPrivate::getModelIndexes(QList<QModelIndex> &indexes, int startIndex, int endIndex)
+{
+    QModelIndex index;
+    for(int i = startIndex; i < endIndex; i++)
+    {
+        if(i >= 0)
+        {
+            index = mModel->index(i, 0);
+            if(index.isValid())
+            {
+                indexes.append(index);
+            }
+        }
+    }
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::thumbnailReadySlot()
+// -----------------------------------------------------------------------------
+//
+void VideoThumbnailDataPrivate::thumbnailReadySlot(QPixmap tnData, void *internal , int id, int error)
+{
+    // Tn ready, either failed or not it must be removed from the fetch list.
+    // It's not stored if it's not found from the list. 
+    if(!removeFromFetchList(id))
+    {
+        if(internal)
+            delete internal;
+        return;
+    }
+    
+    TMPXItemId mediaId(0, 0);
+    if(internal)
+    {
+        mediaId = *(static_cast<TMPXItemId*>(internal));
+        delete internal;
+    }
+    else
+    {
+        return;
+    }
+
+    if(!error && !tnData.isNull())
+    {
+        mThumbnailData.insert(mediaId, new QIcon(tnData));
+        
+        // Gather list of media ids and emit thumbnailReady signals in larger set
+        // when timer goes off.
+        if(mTbnReportTimer && !mTbnReportTimer->isActive())
+        {
+            mTbnReportTimer->setSingleShot(true);
+            mTbnReportTimer->start(THUMBNAIL_READY_SIGNAL_TIMEOUT);
+        }
+
+        // Save the media id for the signal.  
+        mReadyThumbnailMediaIds.append(mediaId);
+    }
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::reportThumbnailsReadySlot()
+// -----------------------------------------------------------------------------
+//
+void VideoThumbnailDataPrivate::reportThumbnailsReadySlot()
+{
+    emit thumbnailsFetched(mReadyThumbnailMediaIds);
+    mReadyThumbnailMediaIds.clear();
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::layoutChangedSlot()
+// -----------------------------------------------------------------------------
+//
+void VideoThumbnailDataPrivate::layoutChangedSlot()
+{
+    startBackgroundFetching(mCurrentFetchIndex);
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::rowsInsertedSlot()
+// -----------------------------------------------------------------------------
+//
+void VideoThumbnailDataPrivate::rowsInsertedSlot(const QModelIndex & /* parent */, int /* start */, int /* end */)
+{
+    startBackgroundFetching(mCurrentFetchIndex);
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::defaultThumbnail()
+// -----------------------------------------------------------------------------
+//
+const QIcon* VideoThumbnailDataPrivate::defaultThumbnail(TMPXItemId mediaId)
+{
+    // Is thumbnail for a video or a category.
+    if(mediaId.iId2 == 0)
+    {
+        if(!mDefaultTnVideo)
+            mDefaultTnVideo = new QIcon(":/icons/default_thumbnail_video.svg");
+        return mDefaultTnVideo;
+    }
+    else
+    {
+        if(!mDefaultTnCategory)
+            mDefaultTnCategory = new QIcon(":/icons/default_thumbnail_collection.svg");
+        return mDefaultTnCategory;
+    }
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::removeThumbnail()
+// -----------------------------------------------------------------------------
+//
+bool VideoThumbnailDataPrivate::removeThumbnail(TMPXItemId mediaId)
+{
+    return mThumbnailData.remove(mediaId);
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::enableBackgroundFetching()
+// -----------------------------------------------------------------------------
+//
+void VideoThumbnailDataPrivate::enableBackgroundFetching(bool enable)
+{
+    mBackgroundFetchingEnabled = enable;
+    startBackgroundFetching(0);
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::freeThumbnailData()
+// -----------------------------------------------------------------------------
+//
+void VideoThumbnailDataPrivate::freeThumbnailData()
+{
+    // Stop timers.
+    if(mBgFetchTimer)
+        mBgFetchTimer->stop();
+    
+    if(mTbnReportTimer)
+        mTbnReportTimer->stop();
+
+    // Clear data.
+    mFetchList.clear();
+    mReadyThumbnailMediaIds.clear();
+    mThumbnailData.clear();
+    
+    delete mDefaultTnVideo;
+    mDefaultTnVideo = 0;
+    
+    delete mDefaultTnCategory;
+    mDefaultTnCategory = 0;
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::removeFromFetchList()
+// -----------------------------------------------------------------------------
+//
+bool VideoThumbnailDataPrivate::removeFromFetchList(int tnId)
+{
+    if(mFetchList.isEmpty())
+    {
+        return false;
+    }
+    bool removed = mFetchList.remove(tnId);
+    if(mFetchList.isEmpty())
+    {
+        continueBackgroundFetch();
+    }
+    return removed;
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::startBackgroundFetching()
+// -----------------------------------------------------------------------------
+//
+void VideoThumbnailDataPrivate::startBackgroundFetching(int fetchIndex)
+{
+    if(!mBackgroundFetchingEnabled)
+        return;
+
+    mCurrentFetchIndex = fetchIndex;
+    mCurrentBackgroundFetchCount = 0;
+    doBackgroundFetching();
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::continueBackgroundFetch()
+// -----------------------------------------------------------------------------
+//
+void VideoThumbnailDataPrivate::continueBackgroundFetch()
+{
+    if(!mBackgroundFetchingEnabled)
+        return;
+
+    if(mBgFetchTimer)
+    {
+        mBgFetchTimer->stop();
+        mBgFetchTimer->setSingleShot(true);
+        mBgFetchTimer->start(THUMBNAIL_BACKGROUND_TIMEOUT);
+    }
+}
+
+// -----------------------------------------------------------------------------
+// VideoThumbnailDataPrivate::aboutToQuitSlot()
+// -----------------------------------------------------------------------------
+//
+void VideoThumbnailDataPrivate::aboutToQuitSlot()
+{
+    cleanup();
+}
+
+// End of file