camerauis/cameraxui/cxengine/src/cxefilesavethreadsymbian.cpp
changeset 19 d9aefe59d544
child 37 64817133cd1d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/camerauis/cameraxui/cxengine/src/cxefilesavethreadsymbian.cpp	Fri Apr 16 14:51:30 2010 +0300
@@ -0,0 +1,363 @@
+/*
+* Copyright (c) 2009-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:  Still image saving thread
+*
+*/
+
+#include <QPixmap>
+#include <QByteArray>
+#include <QCoreApplication>
+#include <QMutexLocker>
+
+#include "cxestillimage.h"
+#include "cxeimagedataitem.h"
+#include "cxeimagedataitemsymbian.h"
+#include "cxeharvestercontrolsymbian.h"
+#include "cxethumbnailmanagersymbian.h"
+#include "cxefilesavethreadsymbian.h"
+#include "cxutils.h" // debug
+
+namespace
+{
+    static const int SLEEP_MS_FOR_SIGNALS = 100;
+    // Id for "Captured" album
+    static const quint32 MDS_CAPTURED_ALBUM_ID = 2;
+}
+
+
+/**
+* Implement factory method for CxeFileSaveThreadFactory.
+*/
+CxeFileSaveThread *CxeFileSaveThreadFactory::createFileSaveThread(QObject *parent)
+{
+    return new CxeFileSaveThreadSymbian(parent);
+}
+
+
+/*!
+    \class CxeFileSaveThreadSymbian
+    \brief Still image saving thread
+*/
+
+
+// ======== MEMBER FUNCTIONS ========
+
+CxeFileSaveThreadSymbian::CxeFileSaveThreadSymbian( QObject *parent )
+    : CxeFileSaveThread(parent),
+      mExitThread(false),
+      mSnapshots(),
+      mExit(false),
+      mThumbnailManager(NULL),
+      mHarvesterControl(NULL)
+{
+    start(IdlePriority);
+}
+
+CxeFileSaveThreadSymbian::~CxeFileSaveThreadSymbian()
+{
+    CX_DEBUG_ENTER_FUNCTION();
+    mMutex.lock();
+    mExitThread = true;
+    mDataToSave.wakeOne();
+    mMutex.unlock();
+
+    wait(); // until the thread has finished execution.
+    qDeleteAll(mQueue);  // Ensure destruction
+    mQueue.clear();
+
+    mSnapshots.clear();
+
+    delete mThumbnailManager;
+    delete mHarvesterControl;
+
+    CX_DEBUG_EXIT_FUNCTION();
+}
+
+void CxeFileSaveThreadSymbian::save(CxeImageDataItem *data)
+{
+    CX_DEBUG_ENTER_FUNCTION();
+
+    // Ensure safe data adding.
+    // Saving thread will wait if needed, in read method, until mutex is unlocked
+    mMutex.lock();
+    mQueue.enqueue(data);
+    // Wake up saving thread if it's sleeping
+    mDataToSave.wakeOne();
+    mMutex.unlock();
+
+    CX_DEBUG_EXIT_FUNCTION();
+}
+
+void CxeFileSaveThreadSymbian::read()
+{
+    mMutex.lock();
+    mCount = mQueue.count();
+    mExit = mExitThread;
+    mMutex.unlock();
+}
+
+void CxeFileSaveThreadSymbian::run()
+{
+    CX_DEBUG_ENTER_FUNCTION();
+
+    // Init Thumbnail Manager and Harvester Control.
+    init();
+    mActiveHarvests = 0;
+
+    // Check if there is data to save.
+    // There should not be any, because the thread is just contructed
+    read();
+
+    while (!mExit || mCount > 0) { // Complete save before exit
+        CX_DEBUG(("CxeFileSaveThreadSymbian: mCount %d", mCount));
+        // Wait data
+        if (!mExit && mCount == 0) {
+            // If there isn't any data to save, put the thread sleeping
+            mMutex.lock();
+            if(mActiveHarvests > 0) {
+                // If we have active harvest requests, continue after a while to check if
+                // there are signals waiting.
+                CX_DEBUG(("CxeFileSaveThreadSymbian: %d harvesting requests active..", mActiveHarvests));
+                mDataToSave.wait(&mMutex, SLEEP_MS_FOR_SIGNALS); // waiting "wakeOne"
+                QCoreApplication::processEvents();
+            } else {
+                // If no active requests, and no data,
+                // halt this thread until something to
+                // save is available.
+                CX_DEBUG(("CxeFileSaveThreadSymbian: set thread sleeping"));
+                mDataToSave.wait(&mMutex); // waiting "wakeOne"
+            }
+            mMutex.unlock();
+            CX_DEBUG(("CxeFileSaveThreadSymbian: woken up"));
+
+        }
+
+        // There should be data now, because the thread is woken up
+        read();
+
+        if (mCount > 0) {
+            // Save one item now.
+            saveNow();
+        }
+
+        // If we did start harvesting, check if there's signal waiting
+        // for harvesting completed already.
+        if(mActiveHarvests > 0) {
+            msleep(SLEEP_MS_FOR_SIGNALS);
+            QCoreApplication::processEvents();
+        }
+
+        // Saving takes some seconds, there might be new data available.
+        read();
+    }
+
+    // Cleanup in the same thread as init() was done.
+    deinit();
+
+    CX_DEBUG_EXIT_FUNCTION();
+}
+
+/**
+* Slot for saved video signal.
+*/
+void CxeFileSaveThreadSymbian::handleVideoSaved(CxeError::Id status, const QString& filename) {
+    CX_DEBUG_ENTER_FUNCTION();
+    CX_DEBUG(("[INFO] current thread 0x%x", QThread::currentThreadId()));
+    CX_DEBUG(("status = %d filename = %s", status, filename.toAscii().constData()));
+
+    if (status == CxeError::None) {
+        // Use a dummy "image data item" with empty data and saved state for videos.
+        // We just need to harvest the file and provide snapshot to Thumbnail Manager.
+        QByteArray empty;
+        CxeImageDataItem* item = new CxeImageDataItemSymbian(empty, filename, CxeStillImage::INVALID_ID, CxeImageDataItem::Saved);
+        if(item) {
+            save(item);
+        }
+    }
+    CX_DEBUG_EXIT_FUNCTION();
+}
+
+void CxeFileSaveThreadSymbian::handleSnapshotReady(CxeError::Id status, const QPixmap& snapshot, const QString& filename)
+{
+    CX_DEBUG_ENTER_FUNCTION();
+    CX_DEBUG(("[INFO] current thread 0x%x", QThread::currentThreadId()));
+    CX_DEBUG(("status = %d filename = %s", status, filename.toAscii().constData()));
+
+    if (status == CxeError::None) {
+        // Store snapshot.
+        if (!snapshot.isNull()) {
+            // QMutexLocker handles locking and unlocking automaticly.
+            QMutexLocker lock(&mSnapshotsMutex);
+
+            //!@todo: Store as QImage once TNM API is fixed.
+            mSnapshots.insert(filename, snapshot);
+        }
+    }
+
+    CX_DEBUG_EXIT_FUNCTION();
+}
+
+void CxeFileSaveThreadSymbian::handleSnapshotReady(CxeError::Id status, const QPixmap& snapshot, int id)
+{
+    CX_DEBUG_ENTER_FUNCTION();
+    CX_DEBUG(("[INFO] current thread 0x%x", QThread::currentThreadId()));
+    CX_DEBUG(("status = %d id = %d", status, id));
+    // Using id number as "filename" for images as filename is not available
+    // at the time of snapshot for still images (and preparing the filename
+    // would be slowing down showing the snapshot).
+    handleSnapshotReady(status, snapshot, QString::number(id));
+    CX_DEBUG_EXIT_FUNCTION();
+}
+
+/**
+* Slot to handle harvested file.
+* @param status Status of the harvesting.
+* @param filename Path of the file just harvested.
+*/
+void CxeFileSaveThreadSymbian::handleFileHarvested(CxeError::Id status, const QString& filename)
+{
+    CX_DEBUG_ENTER_FUNCTION();
+    CX_DEBUG(("[INFO] current thread 0x%x", QThread::currentThreadId()));
+    CX_DEBUG(("status = %d filename = %s", status, filename.toAscii().constData()));
+
+    // Synchronize snapshots container access.
+    QMutexLocker lock(&mSnapshotsMutex);
+
+    // Decrease count to enable sleeping in main loop
+    // if nothing to save is available.
+    --mActiveHarvests;
+
+    if (status == KErrNone && mThumbnailManager) {
+        if (mSnapshots.contains(filename)) {
+            // File has been successfully harvested,
+            // let's provide the snapshot to Thumbnail Manager.
+            mThumbnailManager->createThumbnail(filename, mSnapshots[filename]);
+        }
+    }
+
+    // Releasing the snapshot if it exists.
+    mSnapshots.remove(filename);
+
+    CX_DEBUG_EXIT_FUNCTION();
+}
+
+/**
+* Init the utility classes.
+*/
+void CxeFileSaveThreadSymbian::init()
+{
+    // Create the thumbnail manager and harvester control objects in the new thread.
+    if (!mThumbnailManager) {
+        mThumbnailManager = new CxeThumbnailManagerSymbian();
+    }
+    if (!mHarvesterControl) {
+        mHarvesterControl = new CxeHarvesterControlSymbian();
+        connect(mHarvesterControl, SIGNAL(fileHarvested(CxeError::Id, const QString&)),
+                this, SLOT(handleFileHarvested(CxeError::Id, const QString&)),
+                Qt::DirectConnection);
+    }
+}
+
+/**
+* Clean up the utility classes
+*/
+void CxeFileSaveThreadSymbian::deinit()
+{
+    // Delete in the same thread where created.
+    CX_DEBUG(("CxeFileSaveThreadSymbian: delete Thumbnail Manager"));
+    delete mThumbnailManager;
+    mThumbnailManager = NULL;
+    CX_DEBUG(("CxeFileSaveThreadSymbian: delete Harvester Control"));
+    delete mHarvesterControl;
+    mHarvesterControl = NULL;
+}
+
+
+
+/**
+* Save the item now.
+*/
+void CxeFileSaveThreadSymbian::saveNow()
+{
+    CX_DEBUG_ENTER_FUNCTION();
+    CX_DEBUG(("[INFO] current thread 0x%x", QThread::currentThreadId()));
+
+    CxeImageDataItem* item(mQueue.dequeue());
+    if (item ) {
+        // If item needs to be saved, do it now.
+        if( item->state() == CxeImageDataItem::SavePending) {
+            // Save the item.
+            // Error ignored since we'll check the state.
+            item->save();
+        }
+
+        CX_DEBUG(("Item state after saving: %d", item->state()));
+        // If item is saved ok, ask to harvest it now.
+        if (item->state() == CxeImageDataItem::Saved) {
+
+            QString path(item->path());
+
+            if (item->id() != CxeStillImage::INVALID_ID) {
+                // Synchronize snapshots container access.
+                QMutexLocker lock(&mSnapshotsMutex);
+
+                // If snapshot was stored using id as a "filename", replace key with real filename now,
+                // so we can find the snapshot when harvesting is ready.
+                QString idString(QString::number(item->id()));
+                if (mSnapshots.contains(idString)) {
+                    const QPixmap& snapshot(mSnapshots[idString]);
+                    mSnapshots.remove(idString);
+                    mSnapshots.insert(path, snapshot);
+                }
+            }
+
+            harvestFile(path);
+        }
+
+        // Delete item, since we own it
+        delete item;
+        item = NULL;
+    }
+
+    CX_DEBUG_EXIT_FUNCTION();
+}
+
+/**
+* Harvest one file.
+* @param filename Path of the file to be harvested.
+*/
+void CxeFileSaveThreadSymbian::harvestFile(const QString& filename)
+{
+    CX_DEBUG_ENTER_FUNCTION();
+    if (mHarvesterControl) {
+        // Synchronize snapshots container access.
+        QMutexLocker lock(&mSnapshotsMutex);
+
+        // harvest file ( filename, add to album, album id )
+        CX_DEBUG(("Requesting harvesting for file: %s", filename.toAscii().constData()));
+        CxeError::Id status = mHarvesterControl->harvestFile(filename, false, MDS_CAPTURED_ALBUM_ID);
+        CX_DEBUG(("Status for starting harvesting: %d", status));
+
+        // If there were errors, release any snapshot stored for this file.
+        // Otherwise waiting for the harvesting to complete to
+        // provide the snapshot to Thumbnail Manager.
+        if(status != CxeError::None) {
+            mSnapshots.remove(filename);
+        } else {
+            // Update count to process events in main loop.
+            ++mActiveHarvests;
+        }
+    }
+    CX_DEBUG_EXIT_FUNCTION();
+}