llmediaimplquicktime.cpp

Go to the documentation of this file.
00001 
00032 #include "llmediaimplquicktime.h"
00033 
00034 #if LL_QUICKTIME_ENABLED
00035 
00036 #include "llmediamanager.h"
00037 #include "llmediaimplregister.h"
00038 
00039 #if LL_WINDOWS
00040 #include <windows.h>
00041 #endif
00042 
00043 #include <iostream>
00044 #include <sstream>
00045 
00046 // register this impl with media manager factory
00047 static LLMediaImplRegister sLLMediaImplQuickTimeReg( "LLMediaImplQuickTime", new LLMediaImplQuickTimeMaker() );
00048 
00050 //
00051 LLMediaImplQuickTimeMaker::LLMediaImplQuickTimeMaker()
00052 {
00053         // Register to handle the scheme
00054         mSchema.push_back( "rtsp" );
00055 
00056         // Register to handle the category
00057         mMimeTypeCategories.push_back( "video" );
00058         mMimeTypeCategories.push_back( "audio" );
00059         mMimeTypeCategories.push_back( "image" );
00060 }
00061 
00063 //
00064 LLMediaImplQuickTime::LLMediaImplQuickTime() :
00065         mMovieHandle( 0 ),
00066         mGWorldHandle( 0 ),
00067         mMovieController( 0 ),
00068         mMinWidth( 32 ),
00069         mMaxWidth( 2048 ),
00070         mMinHeight( 32 ),
00071         mMaxHeight( 2048 ),
00072         mCurVolume( 0 )
00073 {
00074 }
00075 
00077 //
00078 LLMediaImplQuickTime::~LLMediaImplQuickTime()
00079 {
00080         unload();
00081 }
00082 
00084 // (static) super-initialization - called once at application startup
00085 bool LLMediaImplQuickTime::startup( LLMediaManagerData* init_data )
00086 {
00087 #ifdef WIN32
00088         if ( InitializeQTML( 0L ) != noErr )
00089         {
00090                 return false;
00091         };
00092 #endif
00093 
00094         EnterMovies();
00095 
00096         return true;
00097 }
00098 
00100 // (static) super-uninitialization - called once at application closedown
00101 bool LLMediaImplQuickTime::closedown()
00102 {
00103         ExitMovies();
00104 
00105 #ifdef WIN32
00106         TerminateQTML();
00107 #endif
00108 
00109         return true;
00110 }
00111 
00113 // private
00114 bool LLMediaImplQuickTime::load( const std::string url )
00115 {
00116         if ( url.empty() )
00117                 return false;
00118 
00119         Handle handle = NewHandleClear( ( Size )( url.length() + 1 ) );
00120     if ( NULL == handle )
00121                 return false;
00122 
00123         BlockMove( url.c_str(), *handle, ( Size )( url.length() + 1 ) );
00124 
00125         //std::cout << "LLMediaImplQuickTime::load( " << url << " )" << std::endl;
00126 
00127         // TODO: supposed to use NewMovieFromDataParams now
00128         OSErr err = NewMovieFromDataRef( &mMovieHandle, newMovieActive | newMovieDontInteractWithUser | newMovieAsyncOK | newMovieIdleImportOK, nil, handle, URLDataHandlerSubType );
00129         DisposeHandle( handle );
00130         if ( noErr != err )
00131                 return false;
00132 
00133         // do pre-roll actions (typically fired for streaming movies but not always)
00134         PrePrerollMovie( mMovieHandle, 0, GetMoviePreferredRate( mMovieHandle ), moviePrePrerollCompleteCallback, ( void * )this );
00135 
00136         // get movie rect (and check for min/max)
00137         Rect movie_rect;
00138         setMovieBoxEnhanced( &movie_rect );
00139 
00140         // make a new movie controller
00141         mMovieController = NewMovieController( mMovieHandle, &movie_rect, mcNotVisible | mcTopLeftMovie );
00142 
00143 #if defined(__APPLE__) || defined(MACOSX)
00144         setMediaDepth( 4 );
00145 #else
00146         setMediaDepth( 3 );
00147 #endif
00148 
00149         // tell manager about the media size
00150         setMediaSize( movie_rect.right - movie_rect.left, movie_rect.bottom - movie_rect.top);
00151 
00152         // movie controller
00153         MCSetActionFilterWithRefCon( mMovieController, mcActionFilterCallBack, ( long )this );
00154 
00155         SetMoviePlayHints( mMovieHandle, hintsAllowDynamicResize, hintsAllowDynamicResize );
00156 
00157         // function that gets called when a frame is drawn
00158         SetMovieDrawingCompleteProc( mMovieHandle, movieDrawingCallWhenChanged, movieDrawingCompleteCallback, ( long )this );
00159 
00160         // emit an event to say that a media source was loaded
00161         LLMediaEvent event( this );
00162         mEventEmitter.update( &LLMediaObserver::onMediaLoaded, event );
00163 
00164         // set up inital state
00165         sizeChanged();
00166 
00167         return true;
00168 }
00169 
00171 // virtual
00172 std::string LLMediaImplQuickTime::getVersion()
00173 {
00174         long version;
00175         Gestalt( gestaltQuickTimeVersion, &version );
00176 
00177         std::ostringstream codec( "" );
00178         codec << "[";
00179         codec << sLLMediaImplQuickTimeReg.getImplName();
00180         codec << "] - ";
00181         codec << "QuickTime: " << std::hex << version;
00182 
00183         return codec.str();
00184 }
00185 
00187 // virtual
00188 bool LLMediaImplQuickTime::navigateTo( const std::string url )
00189 {
00190         // tell engine what we're doing
00191         setStatus( LLMediaBase::STATUS_NAVIGATING );
00192 
00193         // remove the movie we were looking at
00194         unload();
00195 
00196         // load the new one (no real 'go to this url' function in QT)
00197         load( url );
00198 
00199         return true;
00200 }
00201 
00203 // virtual
00204 bool LLMediaImplQuickTime::sizeChanged()
00205 {
00206         if ( ! mMovieHandle )
00207                 return false;
00208 
00209         // sanitize size of movie
00210         Rect movie_rect;
00211         setMovieBoxEnhanced( &movie_rect );
00212 
00213         // we need this later
00214         int width = ( movie_rect.right - movie_rect.left );
00215         int height = ( movie_rect.bottom - movie_rect.top );
00216 
00217         std::cout << "LLMEDIA> size changed to " << width << " x " << height << std::endl;
00218 
00219         setMediaSize( width, height );
00220 
00221         // media depth won't change
00222         int depth_bits = getMediaDepth() * 8;
00223 
00224         GWorldPtr old_gworld_handle = mGWorldHandle;
00225 
00226         if (old_gworld_handle)
00227         {
00228                 GWorldFlags result = UpdateGWorld( &mGWorldHandle, depth_bits, &movie_rect, NULL, NULL, 0 );
00229                 if ( gwFlagErr == result )
00230                 {
00231                         // TODO: unrecoverable?? throw exception?  return something?
00232                         return false;
00233                 }
00234         }
00235         else
00236         {
00237                 OSErr result = NewGWorld( &mGWorldHandle, depth_bits, &movie_rect, NULL, NULL, keepLocal | pixelsLocked );
00238                 if ( noErr != result )
00239                 {
00240                         // ATODO: unrecoverable??  throw exception?  return something?
00241                         return false;
00242                 }
00243 
00244                 // clear memory in GWorld to avoid random screen visual fuzz from uninitialized texture data
00245                 if ( mGWorldHandle )
00246                 {
00247                         PixMapHandle pix_map_handle = GetGWorldPixMap( mGWorldHandle );
00248                         unsigned char* ptr = ( unsigned char* )GetPixBaseAddr( pix_map_handle );
00249                         memset( ptr, 0x00, height * QTGetPixMapHandleRowBytes( pix_map_handle ) );
00250                 }
00251         }
00252 
00253         // point movie at GWorld if it's new
00254         if ( mMovieHandle && ! old_gworld_handle )
00255         {
00256                 SetMovieGWorld( mMovieHandle, mGWorldHandle, GetGWorldDevice ( mGWorldHandle ) );
00257         }
00258 
00259         // update movie controller
00260         if ( mMovieController )
00261         {
00262                 MCSetControllerPort( mMovieController, mGWorldHandle );
00263                 MCPositionController( mMovieController, &movie_rect, &movie_rect,
00264                                                           mcTopLeftMovie | mcPositionDontInvalidate );
00265                 MCMovieChanged( mMovieController, mMovieHandle );
00266         }
00267 
00268         // Emit event with size change so the calling app knows about it too
00269         LLMediaEvent event( this );
00270         mEventEmitter.update( &LLMediaObserver::onMediaSizeChange, event );
00271 
00272         return true;
00273 }
00274 
00276 // static
00277 Boolean LLMediaImplQuickTime::mcActionFilterCallBack( MovieController mc, short action, void *params, long ref )
00278 {
00279         Boolean result = false;
00280 
00281         LLMediaImplQuickTime* self = ( LLMediaImplQuickTime* )ref;
00282 
00283         switch( action )
00284         {
00285                 // handle window resizing
00286                 case mcActionControllerSizeChanged:
00287                         self->sizeChanged();
00288                         break;
00289 
00290                 // Block any movie controller actions that open URLs.
00291                 case mcActionLinkToURL:
00292                 case mcActionGetNextURL:
00293                 case mcActionLinkToURLExtended:
00294                         // Prevent the movie controller from handling the message
00295                         result = true;
00296                         break;
00297 
00298                 default:
00299                         break;
00300         };
00301 
00302         return result;
00303 }
00304 
00306 // private
00307 bool LLMediaImplQuickTime::unload()
00308 {
00309         if ( mMovieHandle )
00310         {
00311                 StopMovie( mMovieHandle );
00312                 if ( mMovieController )
00313                 {
00314                         MCMovieChanged( mMovieController, mMovieHandle );
00315                 };
00316         };
00317 
00318         if ( mMovieController )
00319         {
00320                 MCSetActionFilterWithRefCon( mMovieController, NULL, (long)this );
00321                 DisposeMovieController( mMovieController );
00322                 mMovieController = NULL;
00323         };
00324 
00325         if ( mMovieHandle )
00326         {
00327                 SetMovieDrawingCompleteProc( mMovieHandle, movieDrawingCallWhenChanged, nil, ( long )this );
00328                 DisposeMovie ( mMovieHandle );
00329                 mMovieHandle = NULL;
00330         };
00331 
00332         if ( mGWorldHandle )
00333         {
00334                 DisposeGWorld( mGWorldHandle );
00335                 mGWorldHandle = NULL;
00336         };
00337 
00338         return true;
00339 }
00340 
00342 // static
00343 OSErr LLMediaImplQuickTime::movieDrawingCompleteCallback( Movie call_back_movie, long ref )
00344 {
00345         LLMediaImplQuickTime* self = ( LLMediaImplQuickTime* )ref;
00346 
00347         // IMPORTANT: typically, a consumer who is observing this event will set a flag
00348         // when this event is fired then render later. Be aware that the media stream
00349         // can change during this period - dimensions, depth, format etc.
00350         LLMediaEvent event( self );
00351         self->mEventEmitter.update( &LLMediaObserver::onMediaContentsChange, event );
00352 
00353         return noErr;
00354 }
00355 
00357 // static
00358 void LLMediaImplQuickTime::moviePrePrerollCompleteCallback( Movie movie, OSErr preroll_err, void *ref )
00359 {
00360         LLMediaImplQuickTime* self = ( LLMediaImplQuickTime* )ref;
00361 
00362         LLMediaEvent event( self );
00363         self->mEventEmitter.update( &LLMediaObserver::onMediaPreroll, event );
00364 }
00365 
00367 // used for stop / loop
00368 void LLMediaImplQuickTime::rewind()
00369 {
00370         GoToBeginningOfMovie ( mMovieHandle );
00371 
00372         MCMovieChanged( mMovieController, mMovieHandle );
00373 }
00374 
00376 //
00377 bool LLMediaImplQuickTime::processState()
00378 {
00379         // start stream
00380         if ( nextCommand() == LLMediaBase::COMMAND_START )
00381         {
00382                 // valid when we are in these states
00383                 if ( getStatus() == LLMediaBase::STATUS_NAVIGATING|| getStatus() == LLMediaBase::STATUS_STOPPED || getStatus() == LLMediaBase::STATUS_PAUSED )
00384                 {
00385                         // it appears that the movie must be in a loaded state before we do this command
00386                         if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK )
00387                         {
00388                                 MCDoAction( mMovieController, mcActionPrerollAndPlay, (void*)GetMoviePreferredRate( mMovieHandle ) );
00389 
00390                                 MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume );
00391 
00392                                 setStatus( LLMediaBase::STATUS_STARTED );
00393 
00394                                 clearCommand();
00395                         }
00396                 }
00397         }
00398         else
00399         if ( nextCommand() == LLMediaBase::COMMAND_STOP )
00400         {
00401                 // valid when we are in these states
00402                 if ( getStatus() == LLMediaBase::STATUS_NAVIGATING || getStatus() == LLMediaBase::STATUS_STARTED || getStatus() == LLMediaBase::STATUS_PAUSED )
00403                 {
00404                         // it appears that the movie must be in a loaded state before we do this command
00405                         if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK )
00406                         {
00407                                 // stop playing
00408                                 Fixed rate = X2Fix( 0.0 );
00409                                 MCDoAction( mMovieController, mcActionPlay, (void*)rate );
00410 
00411                                 // go back to start
00412                                 rewind();
00413 
00414                                 setStatus( LLMediaBase::STATUS_STOPPED );
00415                                 clearCommand();
00416                         };
00417                 };
00418         }
00419         else
00420         if ( nextCommand() == LLMediaBase::COMMAND_PAUSE )
00421         {
00422                 // valid when we are in these states
00423                 if ( getStatus() == LLMediaBase::STATUS_NAVIGATING || getStatus() == LLMediaBase::STATUS_STARTED || getStatus() == LLMediaBase::STATUS_STOPPED )
00424                 {
00425                         // it appears that the movie must be in a loaded state before we do this command
00426                         if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK )
00427                         {
00428                                 // stop playing
00429                                 Fixed rate = X2Fix( 0.0 );
00430                                 MCDoAction( mMovieController, mcActionPlay, (void*)rate );
00431 
00432                                 setStatus( LLMediaBase::STATUS_PAUSED );
00433                                 clearCommand();
00434                         };
00435                 };
00436         };
00437 
00438         return true;
00439 }
00440 
00442 // virtual
00443 bool LLMediaImplQuickTime::setMovieBoxEnhanced( Rect* rect )
00444 {
00445         // get movie rect
00446         GetMovieNaturalBoundsRect( mMovieHandle, rect );
00447 
00448         int natural_width  = ( rect->right - rect->left );
00449         int natural_height = ( rect->bottom - rect->top );
00450 
00451         int width  = natural_width;
00452         int height = natural_height;
00453         
00454         // if the user has requested a specific size, use it:
00455         if ((mMediaRequestedWidth != 0) && (mMediaRequestedHeight != 0))
00456         {
00457                 width = mMediaRequestedWidth;
00458                 height = mMediaRequestedHeight;
00459         }
00460 
00461         // if the user has requested, resize media to exactly fit texture
00462         if (mAutoScaled)
00463         {
00464                 width = LLMediaManager::textureWidthFromMediaWidth( width );
00465                 height = LLMediaManager::textureHeightFromMediaHeight( height );
00466         }
00467 
00468         // make sure it falls in valid range
00469         if ( width < mMinWidth )
00470                 width = mMinWidth;
00471 
00472         if ( width > mMaxWidth )
00473                 width = mMaxWidth;
00474 
00475         if ( height < mMinHeight )
00476                 height = mMinHeight;
00477 
00478         if ( height > mMaxHeight )
00479                 height = mMaxHeight;
00480 
00481         
00482         // scale movie to fit rect and invert vertically to match opengl image format
00483         MatrixRecord transform;
00484         SetIdentityMatrix( &transform );        // transforms are additive so start from identify matrix
00485         double scaleX = (double) width / natural_width;
00486         double scaleY = -1.0 * (double) height / natural_height;
00487         double centerX = width / 2.0;
00488         double centerY = height / 2.0;
00489         ScaleMatrix( &transform, X2Fix ( scaleX ), X2Fix ( scaleY ), X2Fix ( centerX ), X2Fix ( centerY ) );
00490         SetMovieMatrix( mMovieHandle, &transform );
00491 
00492         // return the new rect
00493         rect->right = width;
00494         rect->bottom = height;
00495         rect->left = 0;
00496         rect->top = 0;
00497 
00498         return true;
00499 }
00500 
00502 // virtual
00503 bool LLMediaImplQuickTime::updateMedia()
00504 {
00505         if ( ! mMovieHandle )
00506                 return false;
00507 
00508         if ( ! mMovieController )
00509                 return false;
00510 
00511         if ( ! mGWorldHandle )
00512                 return false;
00513 
00514         // service QuickTime
00515         MoviesTask( mMovieHandle, 0 );
00516         MCIdle( mMovieController );
00517 
00518         // update state machine (deals with transport controls for example)
00519         processState();
00520 
00521         // special code for looping - need to rewind at the end of the movie
00522 
00523         if ( isLooping() )
00524         {
00525                 // QT call to see if we are at the end - can't do with controller
00526                 if ( IsMovieDone( mMovieHandle ) )
00527                 {
00528                         // go back to start
00529                         rewind();
00530 
00531                         // kick off new play
00532                         MCDoAction( mMovieController, mcActionPrerollAndPlay, (void*)GetMoviePreferredRate( mMovieHandle ) );
00533 
00534                         // set the volume
00535                         MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume );
00536                 }
00537         }
00538 
00539         return true;
00540 }
00541 
00543 // virtual
00544 unsigned char* LLMediaImplQuickTime::getMediaData()
00545 {
00546         unsigned char* ptr = NULL;
00547 
00548         if ( mGWorldHandle )
00549         {
00550                 PixMapHandle pix_map_handle = GetGWorldPixMap( mGWorldHandle );
00551 
00552                 ptr = ( unsigned char* )GetPixBaseAddr( pix_map_handle );
00553         };
00554 
00555         return ptr;
00556 }
00557 
00559 // virtual
00560 int LLMediaImplQuickTime::getMediaDataWidth() const
00561 {
00562         if ( mGWorldHandle )
00563         {
00564                 int depth = getMediaDepth();
00565 
00566                 if (depth < 1)
00567                         depth = 1;
00568 
00569                 // ALWAYS use the row bytes from the PixMap if we have a GWorld because
00570                 // sometimes it's not the same as mMediaDepth * mMediaWidth !
00571                 PixMapHandle pix_map_handle = GetGWorldPixMap( mGWorldHandle );
00572                 return QTGetPixMapHandleRowBytes( pix_map_handle ) / depth;
00573         }
00574         else
00575         {
00576                 return LLMediaImplCommon::getMediaDataWidth();
00577         }
00578 }
00579 
00581 // virtual
00582 int LLMediaImplQuickTime::getTextureFormatPrimary() const
00583 {
00584 #if defined(__APPLE__) || defined(MACOSX)
00585         return LL_MEDIA_BGRA;
00586 #else
00587         return LL_MEDIA_RGB;
00588 #endif
00589 }
00590 
00592 // virtual
00593 int LLMediaImplQuickTime::getTextureFormatType() const
00594 {
00595 #if defined(__APPLE__) || defined(MACOSX)
00596         #ifdef __BIG_ENDIAN__
00597                 return LL_MEDIA_UNSIGNED_INT_8_8_8_8_REV;
00598         #else
00599                 return LL_MEDIA_UNSIGNED_INT_8_8_8_8;
00600         #endif
00601 #else
00602         return LL_MEDIA_UNSIGNED_BYTE;
00603 #endif
00604 }
00605 
00607 // virtual
00608 bool LLMediaImplQuickTime::seek( double time )
00609 {
00610         if ( mMovieController )
00611         {
00612                 TimeRecord when;
00613                 when.scale = GetMovieTimeScale( mMovieHandle );
00614                 when.base = 0;
00615 
00616                 // 'time' is in (floating point) seconds.  The timebase time will be in 'units', where
00617                 // there are 'scale' units per second.
00618                 SInt64 raw_time = ( SInt64 )( time * (double)( when.scale ) );
00619 
00620                 when.value.hi = ( SInt32 )( raw_time >> 32 );
00621                 when.value.lo = ( SInt32 )( ( raw_time & 0x00000000FFFFFFFF ) );
00622 
00623                 MCDoAction( mMovieController, mcActionGoToTime, &when );
00624 
00625                 return true;
00626         }
00627 
00628         return false;
00629 }
00630 
00632 // virtual
00633 bool LLMediaImplQuickTime::setVolume( float volume )
00634 {
00635         mCurVolume = (short)(volume * ( double ) 0x100 );
00636 
00637         if ( mMovieController )
00638         {
00639                 MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume );
00640 
00641                 return true;
00642         }
00643 
00644         return false;
00645 }
00646 
00647 #endif // _3DNOW_InstructionExtensions/ LL_QUICKTIME_ENABLED
00648 

Generated on Fri May 16 08:32:21 2008 for SecondLife by  doxygen 1.5.5