diff options
author | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
commit | 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch) | |
tree | adca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llmedia/llmediaimplquicktime.cpp | |
parent | README.txt (diff) | |
download | meta-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.cpp | 911 |
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 | // | ||
41 | LLMediaImplQuickTime:: | ||
42 | LLMediaImplQuickTime () : | ||
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 | // | ||
80 | LLMediaImplQuickTime:: | ||
81 | ~LLMediaImplQuickTime () | ||
82 | { | ||
83 | unload(); | ||
84 | } | ||
85 | |||
86 | /////////////////////////////////////////////////////////////////////////////// | ||
87 | // | ||
88 | BOOL | ||
89 | LLMediaImplQuickTime:: | ||
90 | setBuffer ( 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 | // | ||
177 | BOOL | ||
178 | LLMediaImplQuickTime:: | ||
179 | init () | ||
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 | // | ||
198 | void | ||
199 | LLMediaImplQuickTime:: | ||
200 | updateMediaSize() | ||
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 | // | ||
312 | void | ||
313 | LLMediaImplQuickTime:: | ||
314 | setupDummyBuffer() | ||
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 | // | ||
332 | BOOL | ||
333 | LLMediaImplQuickTime:: | ||
334 | load ( 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 | // | ||
373 | OSErr | ||
374 | LLMediaImplQuickTime:: | ||
375 | myFrameDrawnCallback ( 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 | /////////////////////////////////////////////////////////////////////////////// | ||
397 | Boolean | ||
398 | LLMediaImplQuickTime::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 | // | ||
427 | void | ||
428 | LLMediaImplQuickTime::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 | // | ||
453 | void | ||
454 | LLMediaImplQuickTime::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 | // | ||
468 | BOOL | ||
469 | LLMediaImplQuickTime:: | ||
470 | isQTLoaded() | ||
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 | // | ||
487 | BOOL | ||
488 | LLMediaImplQuickTime:: | ||
489 | isQTPlaythroughOK() | ||
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 | // | ||
506 | BOOL | ||
507 | LLMediaImplQuickTime:: | ||
508 | unload () | ||
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 | // | ||
549 | S32 | ||
550 | LLMediaImplQuickTime:: | ||
551 | updateMedia () | ||
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 | // | ||
627 | void | ||
628 | LLMediaImplQuickTime:: | ||
629 | setAutoScaled ( BOOL autoScaledIn ) | ||
630 | { | ||
631 | autoScaled = autoScaledIn; | ||
632 | } | ||
633 | |||
634 | /////////////////////////////////////////////////////////////////////////////// | ||
635 | // | ||
636 | BOOL | ||
637 | LLMediaImplQuickTime:: | ||
638 | stop () | ||
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 | // | ||
655 | BOOL | ||
656 | LLMediaImplQuickTime:: | ||
657 | play () | ||
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 | // | ||
678 | BOOL | ||
679 | LLMediaImplQuickTime:: | ||
680 | loop ( 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 | // | ||
705 | BOOL | ||
706 | LLMediaImplQuickTime:: | ||
707 | pause () | ||
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 | // | ||
723 | BOOL | ||
724 | LLMediaImplQuickTime:: | ||
725 | setVolume ( 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 | // | ||
740 | F32 | ||
741 | LLMediaImplQuickTime:: | ||
742 | getVolume () | ||
743 | { | ||
744 | return ( ( F32 ) curVolume ) / ( F32 ) 0x100; | ||
745 | } | ||
746 | |||
747 | /////////////////////////////////////////////////////////////////////////////// | ||
748 | // | ||
749 | BOOL | ||
750 | LLMediaImplQuickTime:: | ||
751 | isIdle () const | ||
752 | { | ||
753 | return ( currentMode == ModeIdle ); | ||
754 | }; | ||
755 | |||
756 | /////////////////////////////////////////////////////////////////////////////// | ||
757 | // | ||
758 | BOOL | ||
759 | LLMediaImplQuickTime:: | ||
760 | isError () const | ||
761 | { | ||
762 | return ( currentMode == ModeError ); | ||
763 | }; | ||
764 | |||
765 | /////////////////////////////////////////////////////////////////////////////// | ||
766 | // | ||
767 | BOOL | ||
768 | LLMediaImplQuickTime:: | ||
769 | isBuffering () const | ||
770 | { | ||
771 | return ( currentMode == ModeBuffering ); | ||
772 | }; | ||
773 | |||
774 | /////////////////////////////////////////////////////////////////////////////// | ||
775 | // | ||
776 | BOOL | ||
777 | LLMediaImplQuickTime:: | ||
778 | isLoaded () 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 | // | ||
787 | BOOL | ||
788 | LLMediaImplQuickTime:: | ||
789 | isPlaying () const | ||
790 | { | ||
791 | return ( currentMode == ModePlaying ); | ||
792 | }; | ||
793 | |||
794 | /////////////////////////////////////////////////////////////////////////////// | ||
795 | // | ||
796 | BOOL | ||
797 | LLMediaImplQuickTime:: | ||
798 | isLooping () const | ||
799 | { | ||
800 | return ( currentMode == ModeLooping ); | ||
801 | }; | ||
802 | |||
803 | /////////////////////////////////////////////////////////////////////////////// | ||
804 | // | ||
805 | BOOL | ||
806 | LLMediaImplQuickTime:: | ||
807 | isPaused () const | ||
808 | { | ||
809 | return ( currentMode == ModePaused ); | ||
810 | }; | ||
811 | |||
812 | /////////////////////////////////////////////////////////////////////////////// | ||
813 | // | ||
814 | BOOL | ||
815 | LLMediaImplQuickTime:: | ||
816 | isStopped () const | ||
817 | { | ||
818 | return ( currentMode == ModeStopped ); | ||
819 | }; | ||
820 | |||
821 | /////////////////////////////////////////////////////////////////////////////// | ||
822 | // | ||
823 | U8* | ||
824 | LLMediaImplQuickTime:: | ||
825 | getMediaData () | ||
826 | { | ||
827 | return mediaData; | ||
828 | } | ||
829 | |||
830 | /////////////////////////////////////////////////////////////////////////////// | ||
831 | // | ||
832 | BOOL | ||
833 | LLMediaImplQuickTime:: | ||
834 | seek ( 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 | // | ||
859 | F64 | ||
860 | LLMediaImplQuickTime:: | ||
861 | getTime () 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 | // | ||
882 | F64 | ||
883 | LLMediaImplQuickTime:: | ||
884 | getMediaDuration () 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 | ||