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