/*
* 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();
}