|
1 /* |
|
2 * Copyright (c) 2009-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: Still image saving thread |
|
15 * |
|
16 */ |
|
17 |
|
18 #include <QPixmap> |
|
19 #include <QByteArray> |
|
20 #include <QCoreApplication> |
|
21 #include <QMutexLocker> |
|
22 |
|
23 #include "cxestillimage.h" |
|
24 #include "cxeimagedataitem.h" |
|
25 #include "cxeimagedataitemsymbian.h" |
|
26 #include "cxeharvestercontrolsymbian.h" |
|
27 #include "cxethumbnailmanagersymbian.h" |
|
28 #include "cxefilesavethreadsymbian.h" |
|
29 #include "cxutils.h" // debug |
|
30 |
|
31 namespace |
|
32 { |
|
33 static const int SLEEP_MS_FOR_SIGNALS = 100; |
|
34 // Id for "Captured" album |
|
35 static const quint32 MDS_CAPTURED_ALBUM_ID = 2; |
|
36 } |
|
37 |
|
38 |
|
39 /** |
|
40 * Implement factory method for CxeFileSaveThreadFactory. |
|
41 */ |
|
42 CxeFileSaveThread *CxeFileSaveThreadFactory::createFileSaveThread(QObject *parent) |
|
43 { |
|
44 return new CxeFileSaveThreadSymbian(parent); |
|
45 } |
|
46 |
|
47 |
|
48 /*! |
|
49 \class CxeFileSaveThreadSymbian |
|
50 \brief Still image saving thread |
|
51 */ |
|
52 |
|
53 |
|
54 // ======== MEMBER FUNCTIONS ======== |
|
55 |
|
56 CxeFileSaveThreadSymbian::CxeFileSaveThreadSymbian( QObject *parent ) |
|
57 : CxeFileSaveThread(parent), |
|
58 mExitThread(false), |
|
59 mSnapshots(), |
|
60 mExit(false), |
|
61 mThumbnailManager(NULL), |
|
62 mHarvesterControl(NULL) |
|
63 { |
|
64 start(IdlePriority); |
|
65 } |
|
66 |
|
67 CxeFileSaveThreadSymbian::~CxeFileSaveThreadSymbian() |
|
68 { |
|
69 CX_DEBUG_ENTER_FUNCTION(); |
|
70 mMutex.lock(); |
|
71 mExitThread = true; |
|
72 mDataToSave.wakeOne(); |
|
73 mMutex.unlock(); |
|
74 |
|
75 wait(); // until the thread has finished execution. |
|
76 qDeleteAll(mQueue); // Ensure destruction |
|
77 mQueue.clear(); |
|
78 |
|
79 mSnapshots.clear(); |
|
80 |
|
81 delete mThumbnailManager; |
|
82 delete mHarvesterControl; |
|
83 |
|
84 CX_DEBUG_EXIT_FUNCTION(); |
|
85 } |
|
86 |
|
87 void CxeFileSaveThreadSymbian::save(CxeImageDataItem *data) |
|
88 { |
|
89 CX_DEBUG_ENTER_FUNCTION(); |
|
90 |
|
91 // Ensure safe data adding. |
|
92 // Saving thread will wait if needed, in read method, until mutex is unlocked |
|
93 mMutex.lock(); |
|
94 mQueue.enqueue(data); |
|
95 // Wake up saving thread if it's sleeping |
|
96 mDataToSave.wakeOne(); |
|
97 mMutex.unlock(); |
|
98 |
|
99 CX_DEBUG_EXIT_FUNCTION(); |
|
100 } |
|
101 |
|
102 void CxeFileSaveThreadSymbian::read() |
|
103 { |
|
104 mMutex.lock(); |
|
105 mCount = mQueue.count(); |
|
106 mExit = mExitThread; |
|
107 mMutex.unlock(); |
|
108 } |
|
109 |
|
110 void CxeFileSaveThreadSymbian::run() |
|
111 { |
|
112 CX_DEBUG_ENTER_FUNCTION(); |
|
113 |
|
114 // Init Thumbnail Manager and Harvester Control. |
|
115 init(); |
|
116 mActiveHarvests = 0; |
|
117 |
|
118 // Check if there is data to save. |
|
119 // There should not be any, because the thread is just contructed |
|
120 read(); |
|
121 |
|
122 while (!mExit || mCount > 0) { // Complete save before exit |
|
123 CX_DEBUG(("CxeFileSaveThreadSymbian: mCount %d", mCount)); |
|
124 // Wait data |
|
125 if (!mExit && mCount == 0) { |
|
126 // If there isn't any data to save, put the thread sleeping |
|
127 mMutex.lock(); |
|
128 if(mActiveHarvests > 0) { |
|
129 // If we have active harvest requests, continue after a while to check if |
|
130 // there are signals waiting. |
|
131 CX_DEBUG(("CxeFileSaveThreadSymbian: %d harvesting requests active..", mActiveHarvests)); |
|
132 mDataToSave.wait(&mMutex, SLEEP_MS_FOR_SIGNALS); // waiting "wakeOne" |
|
133 QCoreApplication::processEvents(); |
|
134 } else { |
|
135 // If no active requests, and no data, |
|
136 // halt this thread until something to |
|
137 // save is available. |
|
138 CX_DEBUG(("CxeFileSaveThreadSymbian: set thread sleeping")); |
|
139 mDataToSave.wait(&mMutex); // waiting "wakeOne" |
|
140 } |
|
141 mMutex.unlock(); |
|
142 CX_DEBUG(("CxeFileSaveThreadSymbian: woken up")); |
|
143 |
|
144 } |
|
145 |
|
146 // There should be data now, because the thread is woken up |
|
147 read(); |
|
148 |
|
149 if (mCount > 0) { |
|
150 // Save one item now. |
|
151 saveNow(); |
|
152 } |
|
153 |
|
154 // If we did start harvesting, check if there's signal waiting |
|
155 // for harvesting completed already. |
|
156 if(mActiveHarvests > 0) { |
|
157 msleep(SLEEP_MS_FOR_SIGNALS); |
|
158 QCoreApplication::processEvents(); |
|
159 } |
|
160 |
|
161 // Saving takes some seconds, there might be new data available. |
|
162 read(); |
|
163 } |
|
164 |
|
165 // Cleanup in the same thread as init() was done. |
|
166 deinit(); |
|
167 |
|
168 CX_DEBUG_EXIT_FUNCTION(); |
|
169 } |
|
170 |
|
171 /** |
|
172 * Slot for saved video signal. |
|
173 */ |
|
174 void CxeFileSaveThreadSymbian::handleVideoSaved(CxeError::Id status, const QString& filename) { |
|
175 CX_DEBUG_ENTER_FUNCTION(); |
|
176 CX_DEBUG(("[INFO] current thread 0x%x", QThread::currentThreadId())); |
|
177 CX_DEBUG(("status = %d filename = %s", status, filename.toAscii().constData())); |
|
178 |
|
179 if (status == CxeError::None) { |
|
180 // Use a dummy "image data item" with empty data and saved state for videos. |
|
181 // We just need to harvest the file and provide snapshot to Thumbnail Manager. |
|
182 QByteArray empty; |
|
183 CxeImageDataItem* item = new CxeImageDataItemSymbian(empty, filename, CxeStillImage::INVALID_ID, CxeImageDataItem::Saved); |
|
184 if(item) { |
|
185 save(item); |
|
186 } |
|
187 } |
|
188 CX_DEBUG_EXIT_FUNCTION(); |
|
189 } |
|
190 |
|
191 void CxeFileSaveThreadSymbian::handleSnapshotReady(CxeError::Id status, const QPixmap& snapshot, const QString& filename) |
|
192 { |
|
193 CX_DEBUG_ENTER_FUNCTION(); |
|
194 CX_DEBUG(("[INFO] current thread 0x%x", QThread::currentThreadId())); |
|
195 CX_DEBUG(("status = %d filename = %s", status, filename.toAscii().constData())); |
|
196 |
|
197 if (status == CxeError::None) { |
|
198 // Store snapshot. |
|
199 if (!snapshot.isNull()) { |
|
200 // QMutexLocker handles locking and unlocking automaticly. |
|
201 QMutexLocker lock(&mSnapshotsMutex); |
|
202 |
|
203 //!@todo: Store as QImage once TNM API is fixed. |
|
204 mSnapshots.insert(filename, snapshot); |
|
205 } |
|
206 } |
|
207 |
|
208 CX_DEBUG_EXIT_FUNCTION(); |
|
209 } |
|
210 |
|
211 void CxeFileSaveThreadSymbian::handleSnapshotReady(CxeError::Id status, const QPixmap& snapshot, int id) |
|
212 { |
|
213 CX_DEBUG_ENTER_FUNCTION(); |
|
214 CX_DEBUG(("[INFO] current thread 0x%x", QThread::currentThreadId())); |
|
215 CX_DEBUG(("status = %d id = %d", status, id)); |
|
216 // Using id number as "filename" for images as filename is not available |
|
217 // at the time of snapshot for still images (and preparing the filename |
|
218 // would be slowing down showing the snapshot). |
|
219 handleSnapshotReady(status, snapshot, QString::number(id)); |
|
220 CX_DEBUG_EXIT_FUNCTION(); |
|
221 } |
|
222 |
|
223 /** |
|
224 * Slot to handle harvested file. |
|
225 * @param status Status of the harvesting. |
|
226 * @param filename Path of the file just harvested. |
|
227 */ |
|
228 void CxeFileSaveThreadSymbian::handleFileHarvested(CxeError::Id status, const QString& filename) |
|
229 { |
|
230 CX_DEBUG_ENTER_FUNCTION(); |
|
231 CX_DEBUG(("[INFO] current thread 0x%x", QThread::currentThreadId())); |
|
232 CX_DEBUG(("status = %d filename = %s", status, filename.toAscii().constData())); |
|
233 |
|
234 // Synchronize snapshots container access. |
|
235 QMutexLocker lock(&mSnapshotsMutex); |
|
236 |
|
237 // Decrease count to enable sleeping in main loop |
|
238 // if nothing to save is available. |
|
239 --mActiveHarvests; |
|
240 |
|
241 if (status == KErrNone && mThumbnailManager) { |
|
242 if (mSnapshots.contains(filename)) { |
|
243 // File has been successfully harvested, |
|
244 // let's provide the snapshot to Thumbnail Manager. |
|
245 mThumbnailManager->createThumbnail(filename, mSnapshots[filename]); |
|
246 } |
|
247 } |
|
248 |
|
249 // Releasing the snapshot if it exists. |
|
250 mSnapshots.remove(filename); |
|
251 |
|
252 CX_DEBUG_EXIT_FUNCTION(); |
|
253 } |
|
254 |
|
255 /** |
|
256 * Init the utility classes. |
|
257 */ |
|
258 void CxeFileSaveThreadSymbian::init() |
|
259 { |
|
260 // Create the thumbnail manager and harvester control objects in the new thread. |
|
261 if (!mThumbnailManager) { |
|
262 mThumbnailManager = new CxeThumbnailManagerSymbian(); |
|
263 } |
|
264 if (!mHarvesterControl) { |
|
265 mHarvesterControl = new CxeHarvesterControlSymbian(); |
|
266 connect(mHarvesterControl, SIGNAL(fileHarvested(CxeError::Id, const QString&)), |
|
267 this, SLOT(handleFileHarvested(CxeError::Id, const QString&)), |
|
268 Qt::DirectConnection); |
|
269 } |
|
270 } |
|
271 |
|
272 /** |
|
273 * Clean up the utility classes |
|
274 */ |
|
275 void CxeFileSaveThreadSymbian::deinit() |
|
276 { |
|
277 // Delete in the same thread where created. |
|
278 CX_DEBUG(("CxeFileSaveThreadSymbian: delete Thumbnail Manager")); |
|
279 delete mThumbnailManager; |
|
280 mThumbnailManager = NULL; |
|
281 CX_DEBUG(("CxeFileSaveThreadSymbian: delete Harvester Control")); |
|
282 delete mHarvesterControl; |
|
283 mHarvesterControl = NULL; |
|
284 } |
|
285 |
|
286 |
|
287 |
|
288 /** |
|
289 * Save the item now. |
|
290 */ |
|
291 void CxeFileSaveThreadSymbian::saveNow() |
|
292 { |
|
293 CX_DEBUG_ENTER_FUNCTION(); |
|
294 CX_DEBUG(("[INFO] current thread 0x%x", QThread::currentThreadId())); |
|
295 |
|
296 CxeImageDataItem* item(mQueue.dequeue()); |
|
297 if (item ) { |
|
298 // If item needs to be saved, do it now. |
|
299 if( item->state() == CxeImageDataItem::SavePending) { |
|
300 // Save the item. |
|
301 // Error ignored since we'll check the state. |
|
302 item->save(); |
|
303 } |
|
304 |
|
305 CX_DEBUG(("Item state after saving: %d", item->state())); |
|
306 // If item is saved ok, ask to harvest it now. |
|
307 if (item->state() == CxeImageDataItem::Saved) { |
|
308 |
|
309 QString path(item->path()); |
|
310 |
|
311 if (item->id() != CxeStillImage::INVALID_ID) { |
|
312 // Synchronize snapshots container access. |
|
313 QMutexLocker lock(&mSnapshotsMutex); |
|
314 |
|
315 // If snapshot was stored using id as a "filename", replace key with real filename now, |
|
316 // so we can find the snapshot when harvesting is ready. |
|
317 QString idString(QString::number(item->id())); |
|
318 if (mSnapshots.contains(idString)) { |
|
319 const QPixmap& snapshot(mSnapshots[idString]); |
|
320 mSnapshots.remove(idString); |
|
321 mSnapshots.insert(path, snapshot); |
|
322 } |
|
323 } |
|
324 |
|
325 harvestFile(path); |
|
326 } |
|
327 |
|
328 // Delete item, since we own it |
|
329 delete item; |
|
330 item = NULL; |
|
331 } |
|
332 |
|
333 CX_DEBUG_EXIT_FUNCTION(); |
|
334 } |
|
335 |
|
336 /** |
|
337 * Harvest one file. |
|
338 * @param filename Path of the file to be harvested. |
|
339 */ |
|
340 void CxeFileSaveThreadSymbian::harvestFile(const QString& filename) |
|
341 { |
|
342 CX_DEBUG_ENTER_FUNCTION(); |
|
343 if (mHarvesterControl) { |
|
344 // Synchronize snapshots container access. |
|
345 QMutexLocker lock(&mSnapshotsMutex); |
|
346 |
|
347 // harvest file ( filename, add to album, album id ) |
|
348 CX_DEBUG(("Requesting harvesting for file: %s", filename.toAscii().constData())); |
|
349 CxeError::Id status = mHarvesterControl->harvestFile(filename, false, MDS_CAPTURED_ALBUM_ID); |
|
350 CX_DEBUG(("Status for starting harvesting: %d", status)); |
|
351 |
|
352 // If there were errors, release any snapshot stored for this file. |
|
353 // Otherwise waiting for the harvesting to complete to |
|
354 // provide the snapshot to Thumbnail Manager. |
|
355 if(status != CxeError::None) { |
|
356 mSnapshots.remove(filename); |
|
357 } else { |
|
358 // Update count to process events in main loop. |
|
359 ++mActiveHarvests; |
|
360 } |
|
361 } |
|
362 CX_DEBUG_EXIT_FUNCTION(); |
|
363 } |