aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llmedia/llmediaimplquicktime.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llmedia/llmediaimplquicktime.cpp
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/llmedia/llmediaimplquicktime.cpp')
-rw-r--r--linden/indra/llmedia/llmediaimplquicktime.cpp911
1 files changed, 911 insertions, 0 deletions
diff --git a/linden/indra/llmedia/llmediaimplquicktime.cpp b/linden/indra/llmedia/llmediaimplquicktime.cpp
new file mode 100644
index 0000000..74b6cd9
--- /dev/null
+++ b/linden/indra/llmedia/llmediaimplquicktime.cpp
@@ -0,0 +1,911 @@
1/**
2 * @file llmediaimplquicktime.cpp
3 * @brief implementation that supports Apple QuickTime media.
4 *
5 * Copyright (c) 2005-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "linden_common.h"
29
30#if LL_QUICKTIME_ENABLED
31
32#include <iostream>
33
34#include "llmediaimplquicktime.h"
35
36#include "llgl.h"
37#include "llglheaders.h" // For gl texture modes
38
39///////////////////////////////////////////////////////////////////////////////
40//
41LLMediaImplQuickTime::
42LLMediaImplQuickTime () :
43 theController ( NULL ),
44 currentMode ( ModeIdle ),
45 theGWorld ( 0 ),
46 theMovie ( 0 ),
47 mediaData ( 0 ),
48 loopsLeft ( 0 ),
49 ownBuffer ( TRUE ),
50 curVolume ( 0 ),
51 sizeChangeInProgress ( FALSE ),
52 initialStartDone ( FALSE ),
53 autoScaled ( FALSE )
54{
55// These should probably be in the initializer list above, but that seemed uglier...
56#if LL_DARWIN
57 // Mac OS -- gworld will be xRGB (4 byte pixels, like ARGB, but QuickDraw doesn't actually do alpha...)
58 mMediaDepthBytes = 4;
59 mTextureDepth = 4;
60 mTextureFormatInternal = GL_RGB8;
61 mTextureFormatPrimary = GL_BGRA;
62#ifdef LL_BIG_ENDIAN
63 mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV;
64#else
65 mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8;
66#endif
67
68#else
69 // Windows -- GWorld will be RGB (3 byte pixels)
70 mMediaDepthBytes = 3;
71 mTextureDepth = 3;
72 mTextureFormatInternal = GL_RGB8;
73 mTextureFormatPrimary = GL_RGB;
74 mTextureFormatType = GL_UNSIGNED_BYTE;
75#endif
76};
77
78///////////////////////////////////////////////////////////////////////////////
79//
80LLMediaImplQuickTime::
81~LLMediaImplQuickTime ()
82{
83 unload();
84}
85
86///////////////////////////////////////////////////////////////////////////////
87//
88BOOL
89LLMediaImplQuickTime::
90setBuffer ( U8* bufferIn )
91{
92 OSErr err = noErr;
93
94 // If we're waiting for a size change, we just got one.
95 sizeChangeInProgress = FALSE;
96
97 // Since we've pointed QuickTime at the old media data buffer directly, we need to be somewhat careful deleting it...
98 U8* oldMediaData = mediaData;
99 BOOL ownedMediaData = ownBuffer;
100#if LL_DARWIN
101 GWorldPtr oldGWorld = theGWorld;
102#endif
103
104 if(bufferIn == NULL)
105 {
106 // Passing NULL to this function requests that the object allocate its own buffer.
107 mediaData = new unsigned char [ mMediaHeight * mMediaRowbytes ];
108 ownBuffer = TRUE;
109 }
110 else
111 {
112 // Use the supplied buffer.
113 mediaData = bufferIn;
114 ownBuffer = FALSE;
115 }
116
117 if(mediaData == NULL)
118 {
119 // This is bad.
120 llerrs << "LLMediaImplQuickTime::setBuffer: mediaData is NULL" << llendl;
121 // NOTE: This case doesn't clean up properly. This assert is fatal, so this isn't a huge problem,
122 // but if this assert is ever removed the code should be fixed to clean up correctly.
123 return FALSE;
124 }
125
126 err = NewGWorldFromPtr ( &theGWorld, mMediaDepthBytes * 8, &movieRect, NULL, NULL, 0, (Ptr)mediaData, mMediaRowbytes);
127 if(err == noErr)
128 {
129 if(theMovie)
130 {
131 // tell the movie about it
132 SetMovieGWorld ( theMovie, theGWorld, GetGWorldDevice ( theGWorld ) );
133 }
134
135 if(theController)
136 {
137 // and tell the movie controller about it.
138 MCSetControllerPort(theController, theGWorld);
139 }
140
141#if LL_DARWIN
142// NOTE: (CP) This call ultimately leads to a crash in NewGWorldFromPtr on Windows (only)
143// Not calling DisposeGWorld doesn't appear to leak anything significant and stops the crash occuring.
144// This will eventually be fixed but for now, leaking slightly is better than crashing.
145 if ( oldGWorld != NULL )
146 {
147 // Delete the old GWorld
148 DisposeGWorld ( oldGWorld );
149 oldGWorld = NULL;
150 }
151#endif
152 }
153 else
154 {
155 // Hmm... this may be bad. Assert here?
156 llerrs << "LLMediaImplQuickTime::setBuffer: NewGWorldFromPtr failed" << llendl;
157 theGWorld = NULL;
158 return FALSE;
159 }
160
161 // Delete the old media data buffer iff we owned it.
162 if ( ownedMediaData )
163 {
164 if ( oldMediaData )
165 {
166 delete [] oldMediaData;
167 }
168 }
169
170 // build event and emit it
171
172 return TRUE;
173}
174
175///////////////////////////////////////////////////////////////////////////////
176//
177BOOL
178LLMediaImplQuickTime::
179init ()
180{
181 // movied to main application initialization for now because it's non-trivial and only needs to be done once
182 // (even though it goes against the media framework design)
183 //if ( InitializeQTML ( 0L ) != 0 )
184 //{
185 // return FALSE;
186 //};
187
188 //if ( EnterMovies () != 0 )
189 //{
190 // return FALSE;
191 //};
192
193 return LLMediaMovieBase::init();
194}
195
196///////////////////////////////////////////////////////////////////////////////
197//
198void
199LLMediaImplQuickTime::
200updateMediaSize()
201{
202 if((theController == NULL) && (!isQTLoaded()))
203 {
204 // The movie's not loaded enough to get info about it yet.
205 // Set up a dummy buffer.
206 movieRect.left = movieRect.top = 0;
207 movieRect.right = movieRect.bottom = 64;
208 mMediaRowbytes = mMediaDepthBytes * 64;
209 mMediaWidth = 64;
210 mMediaHeight = 64;
211 mTextureWidth = 64;
212 mTextureHeight = 64;
213
214 return;
215 }
216
217 // pick up the size of the movie
218 GetMovieBox ( theMovie, &movieRect );
219
220 // save the size of the media so consumer of media class can use it
221 mMediaWidth = movieRect.right - movieRect.left;
222 mMediaHeight = movieRect.bottom - movieRect.top;
223
224 // Giant media could make us try to use textures bigger than the opengl implementation can handle.
225 // Pin the maximum X or Y dimension to 1024.
226 // NOTE: 1024x1024 may still hurt a lot, but it shouldn't cause opengl to flame out.
227 if(mMediaWidth > 1024)
228 {
229 mMediaWidth = 1024;
230 }
231 if(mMediaHeight > 1024)
232 {
233 mMediaHeight = 1024;
234 }
235
236 // calculate the texture size required to hold media of this size (next power of 2 bigger)
237 for ( mTextureWidth = 1; mTextureWidth < mMediaWidth; mTextureWidth <<= 1 )
238 {
239 };
240
241 for ( mTextureHeight = 1; mTextureHeight < mMediaHeight; mTextureHeight <<= 1 )
242 {
243 };
244
245// llinfos << "Media texture size will be " << mTextureWidth << " x " << mTextureHeight << llendl;
246
247 // if autoscale is on we simply make the media & texture sizes the same and quicktime does all the hard work
248 if ( autoScaled )
249 {
250 // Stretch the movie to fill the texture.
251 mMediaWidth = mTextureWidth;
252 mMediaHeight = mTextureHeight;
253
254 // scale movie using high quality but slow algorithm.
255 // NOTE: this results in close to same quality as texture scaling option but with (perhaps) significant
256 // loss of performance (e.g. my machine, release build, frame rate goes from 92 -> 82 fps
257 // To revert to original behaviour, just remove the line below.
258
259 // MBW -- There seems to be serious drop in performance above a particular size, on both low and high end machines.
260 // 512x256 is fine, while 512x512 is unusable. I theorize that this is due to CPU cache getting broken around that size.
261 if((mTextureWidth * mTextureHeight) <= (512 * 256))
262 {
263// llinfos << "Setting high-quality hint." << llendl;
264 SetMoviePlayHints ( theMovie, hintsHighQuality, hintsHighQuality );
265 }
266 };
267
268 // always flip movie using quicktime (little performance impact and no loss in quality)
269 if ( TRUE )
270 {
271 // Invert the movie in the Y directon to match the expected orientation of GL textures.
272 MatrixRecord transform;
273 GetMovieMatrix ( theMovie, &transform );
274
275 double centerX = mMediaWidth / 2.0;
276 double centerY = mMediaHeight / 2.0;
277 ScaleMatrix ( &transform, X2Fix ( 1.0 ), X2Fix ( -1.0 ), X2Fix ( centerX ), X2Fix ( centerY ) );
278
279 SetMovieMatrix ( theMovie, &transform );
280 };
281
282 movieRect.left = 0;
283 movieRect.top = 0;
284 movieRect.right = mMediaWidth;
285 movieRect.bottom = mMediaHeight;
286
287 // Calculate the rowbytes of the texture
288 mMediaRowbytes = mMediaWidth * mMediaDepthBytes;
289
290 SetMovieBox(theMovie, &movieRect);
291
292 if(theController == NULL)
293 {
294 SetGWorld(theGWorld, NULL);
295
296 // Create a movie controller for the movie
297 theController = NewMovieController(theMovie, &movieRect, mcNotVisible|mcTopLeftMovie);
298
299 MCSetActionFilterWithRefCon(theController, myMCActionFilterProc, (long)this);
300
301 // Allow the movie to dynamically resize (may be necessary for streaming movies to work right...)
302 SetMoviePlayHints(theMovie, hintsAllowDynamicResize, hintsAllowDynamicResize);
303 }
304 else
305 {
306 MCPositionController(theController, &movieRect, &movieRect, mcTopLeftMovie|mcPositionDontInvalidate);
307 }
308}
309
310///////////////////////////////////////////////////////////////////////////////
311//
312void
313LLMediaImplQuickTime::
314setupDummyBuffer()
315{
316 // Used when the movie can't be drawn for some reason. This sets up a buffer that keeps callers from getting annoyed.
317 movieRect.left = movieRect.top = 0;
318 movieRect.right = movieRect.bottom = 64;
319 mMediaRowbytes = mMediaDepthBytes * 64;
320 mMediaWidth = 64;
321 mMediaHeight = 64;
322 mTextureWidth = 64;
323 mTextureHeight = 64;
324
325 setBuffer ( NULL );
326
327 memset(mediaData, 0, mMediaRowbytes * mMediaHeight );
328}
329
330///////////////////////////////////////////////////////////////////////////////
331//
332BOOL
333LLMediaImplQuickTime::
334load ( const LLString& urlIn )
335{
336 // The movie may do things to the current port when it's created. Make sure we have a valid port set.
337 setupDummyBuffer();
338 SetGWorld(theGWorld, NULL);
339
340 Size mySize = ( Size ) urlIn.length () + 1;
341 if ( mySize == 0 )
342 return FALSE;
343
344 Handle myHandle = NewHandleClear ( mySize );
345 if ( myHandle == NULL )
346 return FALSE;
347
348 BlockMove ( urlIn.c_str (), *myHandle, mySize );
349
350 // Might be able to make this asynchronous with (newMovieActive|newMovieAsyncOK|newMovieIdleImportOK)?
351 OSErr err = NewMovieFromDataRef ( &theMovie, newMovieActive|newMovieDontInteractWithUser|newMovieAsyncOK|newMovieIdleImportOK, NULL, myHandle, URLDataHandlerSubType );
352
353 if ( err != noErr )
354 return false;
355
356 // function that gets called when a frame is drawn
357 SetMovieDrawingCompleteProc ( theMovie, movieDrawingCallWhenChanged, myFrameDrawnCallback, ( long ) this );
358
359 if(isQTLoaded())
360 {
361 updateMediaSize();
362 setBuffer(NULL);
363 }
364
365 // Tell the controller to play the movie. This also deals with the movie still loading.
366 //play();
367
368 return LLMediaMovieBase::load(urlIn);
369}
370
371///////////////////////////////////////////////////////////////////////////////
372//
373OSErr
374LLMediaImplQuickTime::
375myFrameDrawnCallback ( Movie callbackMovie, long refCon )
376{
377 LLMediaImplQuickTime* myQtRenderer = ( LLMediaImplQuickTime* ) refCon;
378
379 // The gworld quicktime is playing back into is now wrapped around myQtRenderer->mediaData,
380 // so there's no need to copy any data here.
381#if 0
382 Ptr pixels = GetPixBaseAddr ( myQtRenderer->pixmapHandle );
383
384 LockPixels ( myQtRenderer->pixmapHandle );
385
386 memcpy ( ( U8* ) myQtRenderer->mediaData, pixels, myQtRenderer->getMediaBufferSize () );
387
388 UnlockPixels ( myQtRenderer->pixmapHandle );
389#endif
390
391 myQtRenderer->bufferChanged();
392
393 return 0;
394}
395
396///////////////////////////////////////////////////////////////////////////////
397Boolean
398LLMediaImplQuickTime::myMCActionFilterProc (MovieController theMC, short theAction, void *theParams, long theRefCon)
399{
400 Boolean result = false;
401 LLMediaImplQuickTime *self = (LLMediaImplQuickTime*)theRefCon;
402
403 switch ( theAction )
404 {
405 // handle window resizing
406 case mcActionControllerSizeChanged:
407 self->sizeChanged();
408 break;
409
410 // Block any movie controller actions that open URLs.
411 case mcActionLinkToURL:
412 case mcActionGetNextURL:
413 case mcActionLinkToURLExtended:
414 // Prevent the movie controller from handling the message
415 result = true;
416 break;
417
418 default:
419 break;
420 };
421
422 return ( result );
423}
424
425///////////////////////////////////////////////////////////////////////////////
426//
427void
428LLMediaImplQuickTime::rewind()
429{
430 // MBW -- XXX -- I don't see an easy way to do this via the movie controller.
431 GoToBeginningOfMovie ( theMovie );
432
433 // Call this afterwards so the movie controller can sync itself with the movie.
434 MCMovieChanged(theController, theMovie);
435
436#if 0
437 // Maybe something like this?
438 TimeRecord when;
439 when.value.hi = 0;
440 when.value.lo = 0;
441 when.scale = GetMovieTimeScale(theMovie);
442
443 // This seems like the obvious thing, but a tech note (http://developer.apple.com/technotes/qt/qt_510.html) says otherwise...
444// when.base = GetMovieTimeBase(theMovie);
445 when.base = 0;
446
447 MCDoAction(theController, mcActionGoToTime, &when);
448#endif
449}
450
451///////////////////////////////////////////////////////////////////////////////
452//
453void
454LLMediaImplQuickTime::sizeChanged()
455{
456 // Set the movie to render (well, actually NOT render) to an internal buffer until the size change can be handled.
457 setupDummyBuffer();
458
459 // Make the next call to updateMedia request a size change.
460 sizeChangeInProgress = true;
461
462 // Recalculate the values that depend on the movie rect.
463 updateMediaSize();
464}
465
466///////////////////////////////////////////////////////////////////////////////
467//
468BOOL
469LLMediaImplQuickTime::
470isQTLoaded()
471{
472 BOOL result = false;
473
474 if(theMovie)
475 {
476 if(GetMovieLoadState(theMovie) >= kMovieLoadStateLoaded)
477 {
478 result = true;
479 }
480 }
481
482 return result;
483}
484
485///////////////////////////////////////////////////////////////////////////////
486//
487BOOL
488LLMediaImplQuickTime::
489isQTPlaythroughOK()
490{
491 BOOL result = false;
492
493 if(theMovie)
494 {
495 if(GetMovieLoadState(theMovie) >= kMovieLoadStatePlaythroughOK)
496 {
497 result = true;
498 }
499 }
500
501 return result;
502}
503
504///////////////////////////////////////////////////////////////////////////////
505//
506BOOL
507LLMediaImplQuickTime::
508unload ()
509{
510
511 if( theController )
512 {
513 // Slight paranoia...
514 MCSetActionFilterWithRefCon(theController, NULL, (long)this);
515
516 DisposeMovieController( theController );
517 theController = NULL;
518 };
519
520 if ( theMovie )
521 {
522 // Slight paranoia...
523 SetMovieDrawingCompleteProc ( theMovie, movieDrawingCallWhenChanged, nil, ( long ) this );
524
525 DisposeMovie ( theMovie );
526 theMovie = NULL;
527 };
528
529 if ( theGWorld )
530 {
531 DisposeGWorld ( theGWorld );
532 theGWorld = NULL;
533 };
534
535 if ( mediaData )
536 {
537 if ( ownBuffer )
538 {
539 delete mediaData;
540 mediaData = NULL;
541 };
542 };
543
544 return TRUE;
545}
546
547///////////////////////////////////////////////////////////////////////////////
548//
549S32
550LLMediaImplQuickTime::
551updateMedia ()
552{
553 if(!theController)
554 {
555 if(isQTLoaded())
556 {
557 // Movie has finished loading. Request a size change to update buffers, etc.
558 // We MUST update the media size here, so it will be correct before the size change request.
559 updateMediaSize();
560 return updateMediaNeedsSizeChange;
561 }
562 else
563 {
564 // Movie is still loading.
565 MoviesTask ( theMovie, 0 );
566 }
567 }
568
569 if(theController)
570 {
571 switch(currentMode)
572 {
573 case ModePlaying:
574 case ModeLooping:
575 if(!initialStartDone)
576 {
577 if(isQTPlaythroughOK())
578 {
579 // The movie is loaded enough to start playing. Start it now.
580 MCDoAction(theController, mcActionPrerollAndPlay, (void*)GetMoviePreferredRate(theMovie));
581
582 MCDoAction(theController, mcActionSetVolume, (void*)curVolume );
583
584 initialStartDone = TRUE;
585 }
586 }
587 break;
588 }
589
590// // This function may copy decompressed movie frames into our media data pointer. JC
591// if (!mediaData)
592// {
593// llwarns << "LLMediaImplQuickTime::updateMedia() about to update with null media pointer" << llendl;
594// }
595// else
596// {
597// // try writing to the pointer to see if it's valid
598// *mediaData = 0;
599// }
600
601 MCIdle(theController);
602 }
603
604 // If we need a size change, that takes precedence.
605 if(sizeChangeInProgress)
606 {
607 return updateMediaNeedsSizeChange;
608 }
609
610 BOOL updated = getBufferChanged();
611
612 resetBufferChanged();
613
614 if(updated)
615 return updateMediaNeedsUpdate;
616
617 // don't use movie controller for looping - appears to be broken on PCs (volume issue)
618 if ( currentMode == ModeLooping )
619 if ( IsMovieDone ( theMovie ) )
620 loop ( 0 );
621
622 return updateMediaNoChanges;
623}
624
625///////////////////////////////////////////////////////////////////////////////
626//
627void
628LLMediaImplQuickTime::
629setAutoScaled ( BOOL autoScaledIn )
630{
631 autoScaled = autoScaledIn;
632}
633
634///////////////////////////////////////////////////////////////////////////////
635//
636BOOL
637LLMediaImplQuickTime::
638stop ()
639{
640 currentMode = ModeStopped;
641
642 if(theController)
643 {
644 Fixed rate = X2Fix(0.0);
645 MCDoAction(theController, mcActionPlay, (void*)rate);
646
647 rewind();
648 }
649
650 return LLMediaMovieBase::stop();
651};
652
653///////////////////////////////////////////////////////////////////////////////
654//
655BOOL
656LLMediaImplQuickTime::
657play ()
658{
659 currentMode = ModePlaying;
660
661 if(theController)
662 {
663 if ( IsMovieDone ( theMovie ) )
664 {
665 rewind();
666 };
667
668 MCDoAction(theController, mcActionPrerollAndPlay, (void*)GetMoviePreferredRate(theMovie));
669
670 MCDoAction(theController, mcActionSetVolume, (void*)curVolume );
671 }
672
673 return LLMediaMovieBase::play();
674};
675
676///////////////////////////////////////////////////////////////////////////////
677//
678BOOL
679LLMediaImplQuickTime::
680loop ( S32 howMany )
681{
682 currentMode = ModeLooping;
683
684 // MBW -- XXX -- This may be harder to do with a movie controller...
685// loopsLeft = howMany;
686
687 if ( theController )
688 {
689 // Movie is loaded and set up.
690 if ( IsMovieDone ( theMovie ) )
691 {
692 rewind();
693 };
694
695 MCDoAction(theController, mcActionPrerollAndPlay, (void*)GetMoviePreferredRate(theMovie));
696
697 MCDoAction(theController, mcActionSetVolume, (void*)curVolume );
698 }
699
700 return LLMediaMovieBase::loop(howMany);
701};
702
703///////////////////////////////////////////////////////////////////////////////
704//
705BOOL
706LLMediaImplQuickTime::
707pause ()
708{
709 currentMode = ModePaused;
710
711 if(theController)
712 {
713 // Movie is loaded and set up.
714 Fixed rate = X2Fix(0.0);
715 MCDoAction(theController, mcActionPlay, (void*)rate);
716 }
717
718 return LLMediaMovieBase::pause();
719};
720
721///////////////////////////////////////////////////////////////////////////////
722//
723BOOL
724LLMediaImplQuickTime::
725setVolume ( F32 volumeIn )
726{
727 // Fixed point signed short, 8.8
728 curVolume = (short)(volumeIn * ( F32 ) 0x100);
729
730 if(theController != NULL)
731 {
732 MCDoAction(theController, mcActionSetVolume, (void*)curVolume);
733 }
734
735 return TRUE;
736}
737
738///////////////////////////////////////////////////////////////////////////////
739//
740F32
741LLMediaImplQuickTime::
742getVolume ()
743{
744 return ( ( F32 ) curVolume ) / ( F32 ) 0x100;
745}
746
747///////////////////////////////////////////////////////////////////////////////
748//
749BOOL
750LLMediaImplQuickTime::
751isIdle () const
752{
753 return ( currentMode == ModeIdle );
754};
755
756///////////////////////////////////////////////////////////////////////////////
757//
758BOOL
759LLMediaImplQuickTime::
760isError () const
761{
762 return ( currentMode == ModeError );
763};
764
765///////////////////////////////////////////////////////////////////////////////
766//
767BOOL
768LLMediaImplQuickTime::
769isBuffering () const
770{
771 return ( currentMode == ModeBuffering );
772};
773
774///////////////////////////////////////////////////////////////////////////////
775//
776BOOL
777LLMediaImplQuickTime::
778isLoaded () const
779{
780 // Only tell the caller the movie is loaded if we've had a chance to set up the movie controller.
781
782 return (theController != NULL);
783};
784
785///////////////////////////////////////////////////////////////////////////////
786//
787BOOL
788LLMediaImplQuickTime::
789isPlaying () const
790{
791 return ( currentMode == ModePlaying );
792};
793
794///////////////////////////////////////////////////////////////////////////////
795//
796BOOL
797LLMediaImplQuickTime::
798isLooping () const
799{
800 return ( currentMode == ModeLooping );
801};
802
803///////////////////////////////////////////////////////////////////////////////
804//
805BOOL
806LLMediaImplQuickTime::
807isPaused () const
808{
809 return ( currentMode == ModePaused );
810};
811
812///////////////////////////////////////////////////////////////////////////////
813//
814BOOL
815LLMediaImplQuickTime::
816isStopped () const
817{
818 return ( currentMode == ModeStopped );
819};
820
821///////////////////////////////////////////////////////////////////////////////
822//
823U8*
824LLMediaImplQuickTime::
825getMediaData ()
826{
827 return mediaData;
828}
829
830///////////////////////////////////////////////////////////////////////////////
831//
832BOOL
833LLMediaImplQuickTime::
834seek ( F64 time )
835{
836 // MBW -- XXX -- This should stash the time if theController is NULL, and seek to there when the movie's loaded.
837 // Do this later.
838 if(theController != NULL)
839 {
840 TimeRecord when;
841 when.scale = GetMovieTimeScale(theMovie);
842 when.base = 0;
843
844 // 'time' is in (floating point) seconds. The timebase time will be in 'units', where
845 // there are 'scale' units per second.
846 S64 rawTime = (S64)(time * (F64)(when.scale));
847
848 when.value.hi = ( SInt32 ) ( rawTime >> 32 );
849 when.value.lo = ( SInt32 ) ( ( rawTime & 0x00000000FFFFFFFF ) );
850
851 MCDoAction(theController, mcActionGoToTime, &when);
852 }
853
854 return TRUE;
855}
856
857///////////////////////////////////////////////////////////////////////////////
858//
859F64
860LLMediaImplQuickTime::
861getTime () const
862{
863 F64 result = 0;
864
865 if(theController != NULL)
866 {
867 TimeValue time;
868 TimeScale scale = 0;
869
870 time = MCGetCurrentTime(theController, &scale);
871 if(scale != 0)
872 {
873 result = ((F64)time) / ((F64)scale);
874 }
875 }
876
877 return result;
878}
879
880///////////////////////////////////////////////////////////////////////////////
881//
882F64
883LLMediaImplQuickTime::
884getMediaDuration () const
885{
886 F64 result = 0;
887 TimeScale scale = GetMovieTimeScale(theMovie);
888 TimeValue duration = GetMovieDuration(theMovie);
889
890 if(duration == kQTSUnknownDuration)
891 {
892 // Hmph.
893 // Return 0 in this case.
894 }
895 else if(duration == kQTSInfiniteDuration)
896 {
897 // This is the magic number for "indefinite duration", i.e. a live stream.
898 // Note that the docs claim this value is 0x7FFFFFF, while the symbolic constant is 0x7FFFFFFF. Go figure.
899 // Return 0 in this case.
900 }
901 else if(scale != 0)
902 {
903 // Timescale is a useful number. Convert to seconds.
904 result = (F64)duration;
905 result /= (F64)scale;
906 }
907
908 return result;
909}
910
911#endif