|
1 /* |
|
2 * Copyright (c) 2002-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: This class is used for playing animated images. |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 // INCLUDE FILES |
|
20 #include <jdebug.h> |
|
21 |
|
22 // For Image Handling Library (IHL) |
|
23 #include "IHLImageFactory.h" |
|
24 #include "MIHLFileImage.h" |
|
25 #include "IHLViewerFactory.h" |
|
26 #include "MIHLImageViewer.h" |
|
27 #include "MIHLBitmap.h" |
|
28 #include "IHLBitmapUtil.h" |
|
29 |
|
30 // MMAPI includes |
|
31 #include "mmmadisplay.h" |
|
32 |
|
33 // Class header |
|
34 #include "cmmaanimationplayer.h" |
|
35 #include "cmmaanimationwindow.h" |
|
36 |
|
37 namespace |
|
38 { |
|
39 const TInt64 KMMATimeUnknown = -1; |
|
40 _LIT(KMMAAnimationContentType, "image/gif"); |
|
41 |
|
42 // Approximated minimum showing time of each frame is 0.12s |
|
43 // this value basically depends on how fast device can load frames |
|
44 // it is not quaranteed anyway that the media time is equal to |
|
45 // clock time, so this needed to be only somewhat close |
|
46 |
|
47 _LIT(KVideoControlName, "VideoControl"); |
|
48 } |
|
49 |
|
50 CMMAAnimationPlayer* CMMAAnimationPlayer::NewLC() |
|
51 { |
|
52 DEBUG("MMA::CMMAAnimationPlayer::NewLC"); |
|
53 CMMAAnimationPlayer* self = new(ELeave) CMMAAnimationPlayer(); |
|
54 CleanupStack::PushL(self); |
|
55 self->ConstructL(); |
|
56 return self; |
|
57 } |
|
58 |
|
59 CMMAAnimationPlayer* CMMAAnimationPlayer::NewLC(const TDesC& aFileName) |
|
60 { |
|
61 CMMAAnimationPlayer* self = NewLC(); |
|
62 self->iFileName = aFileName.AllocL(); |
|
63 return self; |
|
64 } |
|
65 |
|
66 CMMAAnimationPlayer::~CMMAAnimationPlayer() |
|
67 { |
|
68 DEBUG("MMA::CMMAAnimationPlayer::~CMMAAnimationPlayer +"); |
|
69 if (iViewer && iViewer->IsPlaying()) |
|
70 { |
|
71 iViewer->Stop(); |
|
72 } |
|
73 delete iWindow; |
|
74 delete iSnapshotBitmap; |
|
75 delete iViewer; |
|
76 // viewer has reference to iImage, |
|
77 // thus deletion order is important. |
|
78 delete iBitmap; |
|
79 delete iImage; |
|
80 |
|
81 delete iFileName; |
|
82 |
|
83 iFSession.Close(); |
|
84 DEBUG("MMA::CMMAAnimationPlayer::~CMMAAnimationPlayer -"); |
|
85 } |
|
86 |
|
87 CMMAAnimationPlayer::CMMAAnimationPlayer() |
|
88 : iFrameCount(0), iMediaTime(KMMATimeUnknown), iEndReached(EFalse), |
|
89 iSendEndOfMediaOnNextFrame(EFalse) |
|
90 { |
|
91 } |
|
92 |
|
93 void CMMAAnimationPlayer::ConstructL() |
|
94 { |
|
95 DEBUG("MMA::CMMAAnimationPlayer::ConstructL +"); |
|
96 CMMAPlayer::ConstructL(); |
|
97 HBufC* contentType = KMMAAnimationContentType().AllocL(); |
|
98 SetContentType(contentType); |
|
99 |
|
100 // Connect to file session, needed also when playing from data |
|
101 User::LeaveIfError(iFSession.Connect()); |
|
102 |
|
103 // File session must be share protected for IHL |
|
104 User::LeaveIfError(iFSession.ShareProtected()); |
|
105 |
|
106 DEBUG("MMA::CMMAAnimationPlayer::ConstructL -"); |
|
107 } |
|
108 |
|
109 void CMMAAnimationPlayer::SetPlayerListenerObjectL( |
|
110 jobject aListenerObject, |
|
111 JNIEnv* aJni, |
|
112 MMMAEventPoster* aEventPoster) |
|
113 { |
|
114 CMMAPlayer::SetPlayerListenerObjectL(aListenerObject, |
|
115 aJni, |
|
116 aEventPoster); |
|
117 |
|
118 // this method must be called only ones |
|
119 __ASSERT_DEBUG(!iWindow, User::Invariant()); |
|
120 |
|
121 // create window for animationplayer |
|
122 // event poster is always CMMAEventSource type. |
|
123 iWindow = CMMAAnimationWindow::NewL( |
|
124 static_cast< CMMAEventSource* >(iEventPoster)); |
|
125 } |
|
126 |
|
127 void CMMAAnimationPlayer::RealizeL() |
|
128 { |
|
129 DEBUG("MMA::CMMAAnimationPlayer::RealizeL"); |
|
130 // For file locator file must be prefetched here because |
|
131 // FramePositioningControl must know duration of media |
|
132 // in realized state |
|
133 if (iFileName) |
|
134 { |
|
135 TRAPD(err, PrefetchFileL()); |
|
136 if (err != KErrNone) |
|
137 { |
|
138 User::Leave(err); |
|
139 } |
|
140 } |
|
141 CMMAPlayer::RealizeL(); |
|
142 } |
|
143 |
|
144 void CMMAAnimationPlayer::PrefetchL() |
|
145 { |
|
146 DEBUG("MMA::CMMAAnimationPlayer::PrefetchL +"); |
|
147 __ASSERT_DEBUG((iSourceStreams.Count() > 0) || iFileName, User::Invariant()); |
|
148 |
|
149 if (iFileName) |
|
150 { |
|
151 // file data is already fetched in when realizing |
|
152 |
|
153 // If initDisplayMode was called before prefetch, |
|
154 // then the display must notified about source size. |
|
155 if (iDisplay) |
|
156 { |
|
157 iDisplay->SourceSizeChanged(iSourceSize); |
|
158 NotifyWithStringEvent(CMMAPlayerEvent::ESizeChanged, KVideoControlName); |
|
159 } |
|
160 |
|
161 ChangeState(EPrefetched); |
|
162 PostActionCompleted(KErrNone); |
|
163 } |
|
164 else |
|
165 { |
|
166 // Using TDes -- load the whole animation |
|
167 iSourceStreams[ 0 ]->ReadAllL(); |
|
168 } |
|
169 |
|
170 // CMMASourceStream will notify with ReadCompleted |
|
171 DEBUG("MMA::CMMAAnimationPlayer::PrefetchL -"); |
|
172 } |
|
173 |
|
174 void CMMAAnimationPlayer::StartL() |
|
175 { |
|
176 DEBUG("MMA::CMMAAnimationPlayer::StartL +"); |
|
177 |
|
178 // If end of media has been reached, then |
|
179 // start from beginning |
|
180 if (iEndReached) |
|
181 { |
|
182 iEndReached = EFalse; |
|
183 iViewer->SetAnimationFrame(0); |
|
184 iMediaTime = 0; |
|
185 } |
|
186 PostLongEvent(CMMAPlayerEvent::EStarted, iMediaTime); |
|
187 |
|
188 // process current frame |
|
189 ProcessCurrentFrameL(); |
|
190 |
|
191 // progress to next frame (start playback) only if rate is not zero |
|
192 if (iCurrentRate > 0) |
|
193 { |
|
194 iViewer->Play(); |
|
195 } |
|
196 ChangeState(EStarted); |
|
197 PostActionCompleted(KErrNone); // java start return |
|
198 |
|
199 DEBUG("MMA::CMMAAnimationPlayer::StartL -"); |
|
200 } |
|
201 |
|
202 void CMMAAnimationPlayer::ProcessCurrentFrameL() |
|
203 { |
|
204 if (iSendEndOfMediaOnNextFrame) |
|
205 { |
|
206 iSendEndOfMediaOnNextFrame = EFalse; |
|
207 // we are reached the end |
|
208 if (!iRepeatForever) |
|
209 { |
|
210 iRepeatCount++; |
|
211 if (iRepeatCount >= iRepeatNumberOfTimes) |
|
212 { |
|
213 DEBUG("CMMAAnimationPlayer:ProcessCurrentFrameL: Reached repeat count, Stopping"); |
|
214 // end looping, do not send stopped event |
|
215 StopL(EFalse); |
|
216 iViewer->SetAnimationFrame(iFrameCount - 1); |
|
217 SetLoopCount(iRepeatNumberOfTimes); // reset loop count |
|
218 |
|
219 // Signal that end of media has been reached so on next |
|
220 // start playback will be started from beginning. This is needed |
|
221 // because if user sets media time to end of media, then start |
|
222 // should not start from beginning but just deliver end of media. |
|
223 // After that, the next start should start from beginning. |
|
224 iEndReached = ETrue; |
|
225 } |
|
226 } |
|
227 PostLongEvent(CMMAPlayerEvent::EEndOfMedia, iMediaTime); |
|
228 DEBUG("CMMAAnimationPlayer:ProcessCurrentFrameL: sent END_OF_MEDIA"); |
|
229 |
|
230 // Prevents this frame from being viewed if playback has terminated |
|
231 // (e.g. not looping) |
|
232 if (iEndReached) |
|
233 { |
|
234 return; |
|
235 } |
|
236 } |
|
237 |
|
238 // draw current frame to display if we have it |
|
239 if (iDisplay) |
|
240 { |
|
241 const CFbsBitmap& bitmap = iBitmap->Bitmap(); |
|
242 iDisplay->DrawFrameL(&bitmap); |
|
243 } |
|
244 |
|
245 TInt currentFrame = iViewer->AnimationFrame(); |
|
246 if (currentFrame == 0) |
|
247 { |
|
248 DEBUG("CMMAAnimationPlayer:ProcessCurrentFrameL: Reset mediatime"); |
|
249 // reset media time when looping |
|
250 iMediaTime = 0; |
|
251 } |
|
252 iMediaTime += FrameDuration(currentFrame).Int(); |
|
253 |
|
254 // Media time has gone over duration if user has |
|
255 // set media time explicitely to duration. |
|
256 if (iMediaTime > iDuration) |
|
257 { |
|
258 iMediaTime = iDuration; |
|
259 } |
|
260 |
|
261 if (currentFrame == (iFrameCount - 1)) |
|
262 { |
|
263 // End has been reached, so EndOfMedia is sent when |
|
264 // duration of last frame has passed |
|
265 iSendEndOfMediaOnNextFrame = ETrue; |
|
266 } |
|
267 |
|
268 // inform observer |
|
269 if (iAnimationObserver) |
|
270 { |
|
271 iAnimationObserver->AnimationAdvancedL(iViewer->AnimationFrame(), iMediaTime); |
|
272 } |
|
273 } |
|
274 |
|
275 void CMMAAnimationPlayer::StopL(TBool aPostEvent) |
|
276 { |
|
277 DEBUG("MMA::CMMAAnimationPlayer::StopL +"); |
|
278 iViewer->Stop(); |
|
279 // adjust mediatime |
|
280 if (aPostEvent) |
|
281 { |
|
282 PostLongEvent(CMMAPlayerEvent::EStopped, iMediaTime); |
|
283 } |
|
284 ChangeState(EPrefetched); |
|
285 DEBUG("MMA::CMMAAnimationPlayer::StopL -"); |
|
286 } |
|
287 |
|
288 void CMMAAnimationPlayer::DeallocateL() |
|
289 { |
|
290 DEBUG("MMA::CMMAAnimationPlayer::DeallocateL +"); |
|
291 // If player is in starte state when deallocate is called, |
|
292 // player is stopped from java side -> state is changed to |
|
293 // prefetched. |
|
294 if (iViewer) |
|
295 { |
|
296 if (iViewer->IsPlaying()) |
|
297 iViewer->Stop(); |
|
298 |
|
299 delete iViewer; |
|
300 iViewer = NULL; |
|
301 } |
|
302 |
|
303 if (iState == EPrefetched) |
|
304 { |
|
305 ResetSourceStreams(); |
|
306 iEndReached = EFalse; |
|
307 iSendEndOfMediaOnNextFrame = EFalse; |
|
308 ChangeState(ERealized); |
|
309 } |
|
310 DEBUG("MMA::CMMAAnimationPlayer::DeallocateL -"); |
|
311 } |
|
312 |
|
313 void CMMAAnimationPlayer::GetDuration(TInt64* aDuration) |
|
314 { |
|
315 *aDuration = iDuration; |
|
316 } |
|
317 |
|
318 TInt CMMAAnimationPlayer::FindFrame(TInt64 aTime) |
|
319 { |
|
320 __ASSERT_DEBUG(iImage, User::Invariant()); |
|
321 |
|
322 // if we are out of bounds |
|
323 if (aTime > iDuration) |
|
324 { |
|
325 return KErrNotFound; |
|
326 } |
|
327 |
|
328 TInt64 time = 0; |
|
329 TInt fIndex = 0; |
|
330 while ((time < aTime) && (fIndex < iFrameCount)) |
|
331 { |
|
332 time += FrameDuration(fIndex++).Int(); |
|
333 } |
|
334 |
|
335 // adjust to previous frame |
|
336 if (fIndex > 0) |
|
337 { |
|
338 fIndex--; |
|
339 } |
|
340 |
|
341 return fIndex; |
|
342 } |
|
343 |
|
344 TInt64 CMMAAnimationPlayer::MediaTimeForFrame(TInt aFrameIndex) |
|
345 { |
|
346 __ASSERT_DEBUG((aFrameIndex <= iFrameCount) && (aFrameIndex >= 0), |
|
347 User::Invariant()); |
|
348 |
|
349 TInt64 time = 0; |
|
350 for (TInt fIndex = 0; fIndex < aFrameIndex; fIndex++) |
|
351 { |
|
352 time += FrameDuration(fIndex).Int(); |
|
353 } |
|
354 return time; |
|
355 } |
|
356 |
|
357 TTimeIntervalMicroSeconds32 CMMAAnimationPlayer::FrameDuration(TInt aFrameIndex) |
|
358 { |
|
359 __ASSERT_DEBUG(iImage, User::Invariant()); |
|
360 TTimeIntervalMicroSeconds32 fDur = iImage->AnimationFrameDelay(aFrameIndex); |
|
361 const TTimeIntervalMicroSeconds32 KMMAMinimumFrameTime = 120000; |
|
362 |
|
363 if (fDur < KMMAMinimumFrameTime) |
|
364 { |
|
365 fDur = KMMAMinimumFrameTime; |
|
366 } |
|
367 return fDur; |
|
368 } |
|
369 |
|
370 void CMMAAnimationPlayer::SetMediaTimeL(TInt64* aTime) |
|
371 { |
|
372 if (!iImage && !iViewer) |
|
373 { |
|
374 // not yet prefetched |
|
375 *aTime = KErrNotSupported; |
|
376 } |
|
377 else |
|
378 { |
|
379 // Media time of last frame is not the same as duration of |
|
380 // media, so if media time of duration is requested, then it must |
|
381 // be given out altough media time of last frame is lower than that. |
|
382 if (*aTime >= iDuration) |
|
383 { |
|
384 User::LeaveIfError(iViewer->SetAnimationFrame(iFrameCount - 1)); |
|
385 iMediaTime = iDuration; |
|
386 } |
|
387 else |
|
388 { |
|
389 TInt frame = FindFrame(*aTime); |
|
390 User::LeaveIfError(iViewer->SetAnimationFrame(frame)); |
|
391 iMediaTime = MediaTimeForFrame(frame); |
|
392 } |
|
393 *aTime = iMediaTime; |
|
394 iEndReached = EFalse; |
|
395 iSendEndOfMediaOnNextFrame = EFalse; |
|
396 } |
|
397 |
|
398 } |
|
399 |
|
400 void CMMAAnimationPlayer::GetMediaTime(TInt64* aMediaTime) |
|
401 { |
|
402 *aMediaTime = iMediaTime; |
|
403 } |
|
404 |
|
405 const TDesC& CMMAAnimationPlayer::Type() |
|
406 { |
|
407 return KMMAVideoPlayer; |
|
408 } |
|
409 |
|
410 void CMMAAnimationPlayer::ReadCompletedL(TInt aStatus, const TDesC8& aData) |
|
411 { |
|
412 if (aStatus < KErrNone) |
|
413 { |
|
414 PostActionCompleted(aStatus); |
|
415 } |
|
416 else |
|
417 { |
|
418 TRAPD(err, PrefetchDataL(aData)); |
|
419 if (err == KErrNone) |
|
420 { |
|
421 ChangeState(EPrefetched); |
|
422 } |
|
423 PostActionCompleted(err); |
|
424 } |
|
425 } |
|
426 |
|
427 void CMMAAnimationPlayer::PrefetchFileL() |
|
428 { |
|
429 iImage = IHLImageFactory::OpenFileImageL(iFSession, *iFileName); |
|
430 PrepareViewerL(); |
|
431 } |
|
432 |
|
433 void CMMAAnimationPlayer::PrefetchDataL(const TDesC8& aData) |
|
434 { |
|
435 DEBUG_INT("MMA::CMMAAnimationPlayer::PrefetchDataL aData size %d", |
|
436 aData.Size()); |
|
437 |
|
438 // Create source image from data |
|
439 iImage = IHLImageFactory::OpenBufferedFileImageL(iFSession, aData); |
|
440 PrepareViewerL(); |
|
441 } |
|
442 |
|
443 TBool CMMAAnimationPlayer::IsFilePlayer() |
|
444 { |
|
445 if (iFileName != NULL) |
|
446 { |
|
447 return ETrue; |
|
448 } |
|
449 return EFalse; |
|
450 } |
|
451 |
|
452 void CMMAAnimationPlayer::PrepareViewerL() |
|
453 { |
|
454 // Non-animated gifs are not supported |
|
455 if (!(iImage->IsAnimation())) |
|
456 { |
|
457 User::Leave(KErrNotSupported); |
|
458 } |
|
459 |
|
460 // Store image dimensions |
|
461 iSourceSize = iImage->Size(); |
|
462 |
|
463 // Create destination bitmap |
|
464 iBitmap = IHLBitmap::CreateL(); |
|
465 User::LeaveIfError(iBitmap->Create(iSourceSize, iImage->DisplayMode())); |
|
466 |
|
467 // Create image viewer |
|
468 iViewer = IHLViewerFactory::CreateImageViewerL( |
|
469 iSourceSize, |
|
470 *iImage, // source |
|
471 *iBitmap, // destination |
|
472 *this); // reference to MIHLViewerObserver |
|
473 |
|
474 // Set viewer for window |
|
475 iWindow->SetViewer(iViewer); |
|
476 |
|
477 // Store animation frame count locally |
|
478 iFrameCount = iViewer->AnimationFrameCount(); |
|
479 |
|
480 // calculate duration |
|
481 iDuration = MediaTimeForFrame(iFrameCount); |
|
482 |
|
483 // set media time to begin |
|
484 iMediaTime = 0; |
|
485 |
|
486 // If init has been already done |
|
487 if (iDisplay) |
|
488 { |
|
489 iDisplay->SourceSizeChanged(iSourceSize); |
|
490 NotifyWithStringEvent(CMMAPlayerEvent::ESizeChanged, KVideoControlName); |
|
491 } |
|
492 } |
|
493 |
|
494 MIHLImageViewer* CMMAAnimationPlayer::Viewer() |
|
495 { |
|
496 return iViewer; |
|
497 } |
|
498 |
|
499 void CMMAAnimationPlayer::SetAnimationObserver(MMMAAnimationObserver* aAnimationObserver) |
|
500 { |
|
501 iAnimationObserver = aAnimationObserver; |
|
502 } |
|
503 |
|
504 TInt CMMAAnimationPlayer::SetRateL(TInt aRate) |
|
505 { |
|
506 DEBUG("MMA:CMMAAnimationPlayer::SetRateL"); |
|
507 if ((iState == EStarted) && (iCurrentRate != aRate)) |
|
508 { |
|
509 if (aRate <= 0) |
|
510 { |
|
511 iViewer->Stop(); |
|
512 } |
|
513 else |
|
514 { |
|
515 iViewer->Play(); |
|
516 } |
|
517 } |
|
518 iCurrentRate = aRate; |
|
519 return iCurrentRate; |
|
520 } |
|
521 |
|
522 TInt CMMAAnimationPlayer::RateL() |
|
523 { |
|
524 DEBUG("MMA:CMMAAnimationPlayer::RateL"); |
|
525 return iCurrentRate; |
|
526 } |
|
527 |
|
528 void CMMAAnimationPlayer::SetDisplayL(MMMADisplay* aDisplay) |
|
529 { |
|
530 // now it is ready to draw |
|
531 iDisplay = aDisplay; |
|
532 iDisplay->SetWindowL(iWindow); |
|
533 |
|
534 // if state < prefeteched then we dont know actual source size yet |
|
535 // and it will be set after prefetch |
|
536 if (iState >= EPrefetched || |
|
537 (iFileName && iState == ERealized)) |
|
538 { |
|
539 iDisplay->SourceSizeChanged(iSourceSize); |
|
540 NotifyWithStringEvent(CMMAPlayerEvent::ESizeChanged, KVideoControlName); |
|
541 } |
|
542 } |
|
543 |
|
544 TSize CMMAAnimationPlayer::SourceSize() |
|
545 { |
|
546 return iSourceSize; |
|
547 } |
|
548 |
|
549 void CMMAAnimationPlayer::NotifyWithStringEvent( |
|
550 CMMAPlayerEvent::TEventType aEventType, |
|
551 const TDesC& aStringEventData) |
|
552 { |
|
553 PostStringEvent(aEventType, aStringEventData); |
|
554 } |
|
555 |
|
556 MMMASnapshot* CMMAAnimationPlayer::SnapshoterL() |
|
557 { |
|
558 return this; |
|
559 } |
|
560 |
|
561 MMMASnapshot::TEncoding CMMAAnimationPlayer::TakeSnapshotL(TRequestStatus* aStatus, |
|
562 const TSize& /*aSize*/, |
|
563 const CMMAImageSettings& /*aSettings*/) |
|
564 { |
|
565 if (iBitmap) |
|
566 { |
|
567 // Bitmap has to be copied to get ownership of the bitmap instance. |
|
568 iSnapshotBitmap = IHLBitmapUtil::CopyBitmapL(iBitmap->Bitmap()); |
|
569 } |
|
570 else |
|
571 { |
|
572 // When content comes from a stream, iBitmap is not available |
|
573 // until prefetched state is entered. In this case an empty bitmap |
|
574 // is returned instead. |
|
575 iSnapshotBitmap = new(ELeave) CFbsBitmap(); |
|
576 } |
|
577 // notify the caller, error code or KErrNone |
|
578 User::RequestComplete(aStatus, KErrNone); |
|
579 |
|
580 // Return raw bitmap encoding and thus SnapshotEncoded() should not |
|
581 // get called later on. |
|
582 return EBitmap; |
|
583 } |
|
584 |
|
585 CFbsBitmap* CMMAAnimationPlayer::SnapshotBitmap() |
|
586 { |
|
587 CFbsBitmap* bitmap = iSnapshotBitmap; |
|
588 // ownership is transferred to caller |
|
589 iSnapshotBitmap = NULL; |
|
590 return bitmap; |
|
591 } |
|
592 |
|
593 HBufC8* CMMAAnimationPlayer::SnapshotEncoded() |
|
594 { |
|
595 // This method should never be called. |
|
596 // Asserted in debug build to be sure. |
|
597 __ASSERT_DEBUG(EFalse, User::Invariant()); |
|
598 |
|
599 return NULL; |
|
600 } |
|
601 |
|
602 void CMMAAnimationPlayer::ViewerBitmapChangedL() |
|
603 { |
|
604 if (iState == EStarted) |
|
605 { |
|
606 ProcessCurrentFrameL(); |
|
607 } |
|
608 } |
|
609 |
|
610 void CMMAAnimationPlayer::ViewerError(TInt /*aError*/) |
|
611 { |
|
612 // Not implemented currently because |
|
613 // not implemented by IHL either. |
|
614 } |
|
615 |
|
616 // END OF FILE |