Common Files/QTUtilities.c

//////////
//
//  File:       QTUtilities.c
//
//  Contains:   Some utilities for working with QuickTime movies.
//              All utilities start with the prefix "QTUtils_".
//
//  Written by: Tim Monroe
//              Based on the DTSQTUtilities package by Apple DTS.
//              This began as essentially a subset of that package, revised for cross-platform use.
//
//  Copyright:  © 1996-1999 by Apple Computer, Inc., all rights reserved.
//
//  Change History (most recent first):
//
//     <35>     09/29/00    rtm     added QTUtils_IsAutoPlayMovie
//     <34>     04/28/00    rtm     fixed bug in QTUtils_AddUserDataTextToMovie (had a script system, not a region code)
//     <33>     03/08/00    rtm     removed QTUtils_SaveMovie and QTUtils_PrintMoviePICT
//     <32>     03/02/00    rtm     added QTUtils_SelectNoneMovie
//     <31>     01/10/00    rtm     tweaked QTUtils_IsQuickTimeInstalled
//     <30>     12/20/99    rtm     reworked QTUtils_IsMovieFile, to first test for files of type kQTFileTypeMovie
//     <29>     12/09/99    rtm     added QTUtils_PutControllerBarOnTop
//     <28>     12/08/99    rtm     reworked controller button functions to support custom buttom (its mcFlags constant
//                                  is a "Use" constant and not a "Suppress" constant)
//     <27>     11/26/99    rtm     added endian adjustments to _GetMovieFileLoopingInfo and _GetWindowPositionFromFile
//     <26>     11/17/99    rtm     added QTUtils_GetWindowPositionFromFile
//     <25>     05/19/99    rtm     removed QTUtils_GetMovie
//     <24>     05/10/99    rtm     added QTUtils_UpdateMovieVolumeSetting
//     <23>     03/22/99    rtm     updated connection speed code to use constants/data types now in Movies.h
//     <22>     03/11/99    rtm     moved _GetControllerType and _SetControllerType from QTVRUtilities to here;
//                                  added QTUtils_ChangeControllerType
//     <21>     02/03/99    rtm     moved non-QTVR-specific utilities from QTVRUtilities to here
//     <20>     01/26/99    rtm     added QTUtils_ConvertCToPascalString; removed "\p" from any constant strings
//     <19>     01/25/99    rtm     #define'd away QTUtils_GetUsersContentRating and siblings, since content rating
//                                  capability is not in latest feature set
//     <18>     12/09/98    rtm     added QTUtils_GetUsersContentRating
//     <17>     11/18/98    rtm     added QTUtils_GetFrameCount
//     <16>     10/27/98    rtm     added QTUtils_MakeMovieLoop
//     <15>     09/14/98    rtm     added QTUtils_GetUsersConnectionSpeed and QTUtils_SetUsersConnectionSpeed
//     <14>     06/24/98    rtm     added QTUtils_GetMaxWindowDepth and QTUtils_GetMaxScreenDepth
//     <13>     06/04/98    rtm     added QTUtils_DeleteAllReferencesToTrack
//     <12>     05/28/98    rtm     added some typecasting to minimize MSDev compiler warnings
//     <11>     05/19/98    rtm     added QTUtils_MovieHasTimeCodeTrack
//     <10>     05/04/98    rtm     added QTUtils_GetTrackName, QTUtils_SetTrackName, QTUtils_MakeTrackNameByType,
//                                  QTUtils_IsImageFile, and QTUtils_IsMovieFile
//     <9>      02/28/98    rtm     fixed QTUtils_GetMovieFileLoopingInfo and the like
//     <8>      01/14/98    rtm     added QTUtils_ConvertFloatToBigEndian
//     <7>      12/19/97    rtm     added QTUtils_AddUserDataTextToMovie and associated routines;
//                                  added QTUtils_GetMovieFileLoopingInfo and the like
//     <6>      11/06/97    rtm     added QTUtils_MakeSampleDescription
//     <5>      10/29/97    rtm     modified QTUtils_IsMediaTypeInMovie and similar routines to use GetMovieIndTrackType
//     <4>      10/27/97    rtm     added QTUtils_HasQuickTimeVideoEffects
//     <3>      10/17/97    rtm     added QTUtils_MovieHasSoundTrack
//     <2>      09/23/97    rtm     added endian adjustment to QTUtils_PrintMoviePICT
//     <1>      09/10/97    rtm     first file
//     
//////////
 
//////////
//
// header files
//
//////////
 
#ifndef __QTUtilities__
#include "QTUtilities.h"
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// General utilities.
//
// Use these functions to get information about the availability/features of QuickTime or other services.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// QTUtils_TrapAvailable
// Check to see whether a given trap is implemented. This is based on IM: Operating System Utilities (p. 8-22).
//
//////////
 
#if TARGET_OS_MAC && !TARGET_API_MAC_CARBON
Boolean QTUtils_TrapAvailable (short theTrapWord)
{
    TrapType        myTrapType;
    short           myNumToolboxTraps;
    
    // determine whether this is a Toolbox or an Operating System trap
    if ((theTrapWord & 0x0800) > 0)
        myTrapType = ToolTrap;
    else
        myTrapType = OSTrap;
 
    if (myTrapType == ToolTrap) {
        theTrapWord = theTrapWord & 0x07FF;
        
        if (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xAA6E, ToolTrap))
            myNumToolboxTraps = 0x0200;
        else
            myNumToolboxTraps = 0x0400;
            
        if (theTrapWord >= myNumToolboxTraps)
            theTrapWord = _Unimplemented;
    }
 
    return(NGetTrapAddress(theTrapWord, myTrapType) != NGetTrapAddress(_Unimplemented, ToolTrap));
}
#endif
 
 
//////////
//
// QTUtils_IsQuickTimeInstalled
// Is QuickTime installed?
//
//////////
 
Boolean QTUtils_IsQuickTimeInstalled (void) 
{
    long        myAttrs;
    OSErr       myErr = noErr;
 
    myErr = Gestalt(gestaltQuickTime, &myAttrs);
 
    return(myErr == noErr);
}
 
 
//////////
//
// QTUtils_IsQuickTimeCFMInstalled
// Are the QuickTime CFM libraries installed?
//
//////////
 
#if TARGET_CPU_PPC
Boolean QTUtils_IsQuickTimeCFMInstalled (void) 
{
    Boolean     myQTCFMAvail = false;
    long        myAttrs;
    OSErr       myErr = noErr;
 
    // test whether the PowerPC QuickTime glue library is present
    myErr = Gestalt(gestaltQuickTimeFeatures, &myAttrs);
    if (myErr == noErr)
        if (myAttrs & (1L << gestaltPPCQuickTimeLibPresent))
            myQTCFMAvail = true;
 
    // test whether a function is available (the library is not moved from the Extension folder);
    // this is the trick to be used when testing if a function is available via CFM
    if (!CompressImage)
        myQTCFMAvail = false;     
 
    return(myQTCFMAvail);
}
#endif
 
 
//////////
//
// QTUtils_GetQTVersion
// Get the version of QuickTime installed.
// The high-order word of the returned long integer contains the version number,
// so you can test a version like this:
//
//      if (((QTUtils_GetQTVersion() >> 16) & 0xffff) >= 0x0210)        // we require QT 2.1 or greater
//          return;
//
//////////
 
long QTUtils_GetQTVersion (void) 
{
    long        myVersion = 0L;
    OSErr       myErr = noErr;
 
    myErr = Gestalt(gestaltQuickTime, &myVersion);
    if (myErr == noErr)
        return(myVersion);
    else
        return(0L);
}
 
 
//////////
//
// QTUtils_HasQuickTimeVideoEffects
// Does the installed version of QuickTime support video effects?
//
// Note: this is a pretty cheesy way of determining whether video effects are available.
//
//////////
 
Boolean QTUtils_HasQuickTimeVideoEffects (void) 
{
    return(((QTUtils_GetQTVersion() >> 16) & 0xffff) >= kQTVideoEffectsMinVers);
}
 
 
//////////
//
// QTUtils_HasFullScreenSupport
// Does the installed version of QuickTime support the full-screen routines?
//
// Note: this is a pretty cheesy way of determining whether full-screen routines are available.
//
//////////
 
Boolean QTUtils_HasFullScreenSupport (void) 
{
    return(((QTUtils_GetQTVersion() >> 16) & 0xffff) >= kQTFullScreenMinVers);
}
 
 
//////////
//
// QTUtils_HasWiredSprites
// Does the installed version of QuickTime support wired sprites?
//
// Note: this is a pretty cheesy way of determining whether wired sprites are available.
//
//////////
 
Boolean QTUtils_HasWiredSprites (void) 
{
    return(((QTUtils_GetQTVersion() >> 16) & 0xffff) >= kQTWiredSpritesMinVers);
}
 
 
//////////
//
// QTUtils_IsQTVRMovie
// Is the specified movie a QTVR movie?
//
// WARNING: This function is intended for use ONLY when you want to determine if you've got a QTVR movie
// but you don't want to use the QuickTime VR API (perhaps QTVR isn't installed...). The preferred way to
// determine if a movie is a QTVR movie is to call QTVRGetQTVRTrack and then QTVRGetQTVRInstance; if you
// get back a non-NULL instance, you've got a QTVR movie.
//
//////////
 
Boolean QTUtils_IsQTVRMovie (Movie theMovie) 
{
    Boolean         myIsQTVRMovie = false;
    OSType          myType;
    
    // QTVR movies have a special piece of user data identifying the movie controller type
    myType = QTUtils_GetControllerType(theMovie);
    
    if ((myType == kQTVRQTVRType) || (myType == kQTVROldPanoType) || (myType == kQTVROldObjectType))
        myIsQTVRMovie = true; 
        
    return(myIsQTVRMovie);
}
 
 
//////////
//
// QTUtils_IsStreamedMovie
// Is the specified movie a streamed movie?
//
//////////
 
Boolean QTUtils_IsStreamedMovie (Movie theMovie) 
{
    return(GetMovieIndTrackType(theMovie, 1, kQTSStreamMediaType, movieTrackMediaType | movieTrackEnabledOnly) != NULL);
}
 
 
//////////
//
// QTUtils_IsAutoPlayMovie
// Get the autoplay state of a movie file.
//
// A movie file can have information about its autoplay state in a user data item of type 'play'.
// If the movie doesn't contain an item of this type, then we'll assume that it doesn't autoplay.
// If it does contain an item of this type, then the item data (a Boolean) is 0 for normal play
// and 1 for autoplay.
//
//////////
 
Boolean QTUtils_IsAutoPlayMovie (Movie theMovie)
{
    UserData        myUserData = NULL;
    Boolean         myAutoPlay = false;
    OSErr           myErr = paramErr;
 
    // make sure we've got a movie
    if (theMovie == NULL)
        return(myAutoPlay);
        
    // get the movie's user data list
    myUserData = GetMovieUserData(theMovie);
    if (myUserData != NULL) {
        myErr = GetUserDataItem(myUserData, &myAutoPlay, sizeof(myAutoPlay), FOUR_CHAR_CODE('play'), 0);
        if (myErr != noErr)
            myAutoPlay = false;
    }
 
    return(myAutoPlay);
}
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Controller bar utilities.
//
// Use these functions to manipulate the controller bar, its buttons, and the help text displayed in it.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// QTUtils_IsControllerBarVisible
// Is the controller bar currently visible?
//
//////////
 
Boolean QTUtils_IsControllerBarVisible (MovieController theMC) 
{
    return((Boolean)MCGetVisible(theMC));
}
 
 
//////////
//
// QTUtils_GetControllerBarHeight
// Return the height of the controller bar displayed by the movie controller.
//
// Note that MCGetControllerBoundsRect returns the rectangle of bar and movie, if attached;
// so we need to detach the controller bar first.
//
//////////
 
short QTUtils_GetControllerBarHeight (MovieController theMC) 
{
    Boolean     wasAttached = false;
    Rect        myRect;
    
    // if the controller bar is attached, detach it (and remember we did so)
    if (MCIsControllerAttached(theMC) == 1) {
        wasAttached = true;
        MCSetControllerAttached(theMC, false);
    }
    
    // get the rectangle of the controller
    MCGetControllerBoundsRect(theMC, &myRect);
    
    // now reattach the controller bar, if it was originally attached
    if (wasAttached)
        MCSetControllerAttached(theMC, true);
    
    return(myRect.bottom - myRect.top);
}
 
 
//////////
//
// QTUtils_HideControllerBar
// Hide the controller bar provided by the movie controller.
//
//////////
 
void QTUtils_HideControllerBar (MovieController theMC) 
{
    MCSetVisible(theMC, false);
}
 
 
//////////
//
// QTUtils_ShowControllerBar
// Show the controller bar provided by the movie controller.
//
//////////
 
void QTUtils_ShowControllerBar (MovieController theMC) 
{
    MCSetVisible(theMC, true);
}
 
 
//////////
//
// QTUtils_ToggleControllerBar
// Toggle the state of the controller bar provided by the movie controller.
//
//////////
 
void QTUtils_ToggleControllerBar (MovieController theMC) 
{
    if (QTUtils_IsControllerBarVisible(theMC))
        QTUtils_HideControllerBar(theMC);
    else
        QTUtils_ShowControllerBar(theMC);
}
 
 
//////////
//
// QTUtils_PutControllerBarOnTop
// Put the movie controller bar at the top of the movie window.
//
//////////
 
void QTUtils_PutControllerBarOnTop (MovieController theMC) 
{
    if (theMC == NULL)
        return;
        
    if (MCIsControllerAttached(theMC) == 1) {
        Rect        myMCRect;
        Rect        myMovieRect;
                        
        MCGetControllerBoundsRect(theMC, &myMCRect);
        myMovieRect = myMCRect;
        myMCRect.bottom = myMCRect.top + QTUtils_GetControllerBarHeight(theMC);
        myMovieRect.top = myMCRect.bottom + 1;
        
        MCSetControllerAttached(theMC, false);
        
        MCPositionController(theMC, &myMovieRect, &myMCRect, 0L);
    }
}
 
 
//////////
//
// QTUtils_HideControllerButton
// Hide the specified button in the controller bar.
//
// Some explanation is probably useful here: the first thing to understand is that every VR movie has 
// TWO sets of movie controller flags: (1) a set of "control flags" and (2) a set of "explicit flags".
//
// The control flags work as documented in IM: QuickTime Components (pp. 2-20f) and in VRPWQTVR2.0 (pp. 2-23f):
// if a bit in the set of control flags is set (that is, equal to 1), then the associated action or property is
// enabled. For instance, bit 17 (mcFlagQTVRSuppressZoomBtns) means to suppress the zoom buttons. So, if that
// bit is set in a VR movie's control flags, the zoom buttons are NOT displayed. If that bit is clear, the zoom
// buttons are displayed.
//
// However, the QuickTime VR movie controller sometimes suppresses buttons even when those buttons 
// have not been explicitly suppressed in the control flags. For example, if a particular VR movie does not
// contain a sound track, then the movie controller automatically suppresses the speaker/volume button. Likewise,
// if a movie does contain a sound track, then the speaker/volume button is automatically displayed, again without
// regard to the actual value of bit 2 in the control flags.
//
// This might not be what you'd like to happen. For instance, if your application is playing a sound that it
// loaded from a sound resource, you might want the user to be able to adjust the sound's volume using the volume
// control. To do that, you need a way to *force* the speaker/volume button to appear. For this reason, the
// explicit flags were introduced.
//
// The explicit flags indicate which bits in the control flags are to be used explicitly (that is, taken at
// face value). If a certain bit is set in a movie's explicit flags, then the corresponding bit in the control
// flags is interpreted as the desired setting for the feature (and the movie controller will not attempt to
// do anything "clever"). In other words, if bit 17 is set in a movie's explicit flags and bit 17 is clear in
// that movie's control flags, then the zoom buttons are displayed. Similarly, if bit 2 is set in a movie's 
// explicit flags and bit 2 is clear in that movie's control flags, then the speaker/volume button is displayed,
// whether or not the movie contains a sound track.
//
// The final thing to understand: to get or set a bit in a movie's explicit flags, you must set the flag 
// mcFlagQTVRExplicitFlagSet in your call to mcActionGetFlags or mcActionSetFlags. To get or set a bit in a 
// movie's control flags, you must clear the flag mcFlagQTVRExplicitFlagSet in your call to mcActionGetFlags 
// or mcActionSetFlags. Note that when you use the defined constants to set values in the explicit flags, the 
// constant names might be confusing. For instance, setting the bit mcFlagSuppressSpeakerButton in a movie's
// explicit flags doesn't cause the speaker to be suppressed; it just means: "use the actual value of the
// mcFlagSuppressSpeakerButton bit in the control flags".
//
// Whew! Any questions? Okay, then now you'll understand how to hide or show a button in the controller bar:
// set the appropriate explicit flag to 1 and set the corresponding control flag to the desired value. And
// you'll understand how to let the movie controller do its "clever" work: clear the appropriate explicit flag.
//
// There is one final twist to this story: the mcFlag bit for the custom controller button indicates that the
// custom button should be shown, not (like all other button flags) that the button should be suppressed. So
// we need to treat the custom button specially. Sigh.
//
//////////
 
void QTUtils_HideControllerButton (MovieController theMC, long theButton) 
{
    long    myControllerFlags;
    
    // handle the custom button separately
    if (theButton == kQTUtilsCustomButton) {
        MCDoAction(theMC, mcActionGetFlags, &myControllerFlags);
        MCDoAction(theMC, mcActionSetFlags, (void *)(myControllerFlags & ~theButton));
    } else {
        // get the current explicit flags and set the explicit flag for the specified button
        myControllerFlags = mcFlagQTVRExplicitFlagSet;
        MCDoAction(theMC, mcActionGetFlags, &myControllerFlags);
        MCDoAction(theMC, mcActionSetFlags, (void *)((myControllerFlags | theButton) | mcFlagQTVRExplicitFlagSet));
        
        // get the current control flags and set the suppress flag for the specified button
        myControllerFlags = 0;
        MCDoAction(theMC, mcActionGetFlags, &myControllerFlags);
        MCDoAction(theMC, mcActionSetFlags, (void *)((myControllerFlags | theButton) & ~mcFlagQTVRExplicitFlagSet));
    }
}
 
 
//////////
//
// QTUtils_ShowControllerButton
// Show the specified button in the controller bar.
//
//////////
 
void QTUtils_ShowControllerButton (MovieController theMC, long theButton) 
{
    long    myControllerFlags;
    
    // handle the custom button separately
    if (theButton == kQTUtilsCustomButton) {
        MCDoAction(theMC, mcActionGetFlags, &myControllerFlags);
        MCDoAction(theMC, mcActionSetFlags, (void *)(myControllerFlags | theButton));
    } else {
        // get the current explicit flags and set the explicit flag for the specified button
        myControllerFlags = mcFlagQTVRExplicitFlagSet;
        MCDoAction(theMC, mcActionGetFlags, &myControllerFlags);
        MCDoAction(theMC, mcActionSetFlags, (void *)((myControllerFlags | theButton) | mcFlagQTVRExplicitFlagSet));
        
        // get the current control flags and clear the suppress flag for the specified button
        myControllerFlags = 0;
        MCDoAction(theMC, mcActionGetFlags, &myControllerFlags);
        MCDoAction(theMC, mcActionSetFlags, (void *)(myControllerFlags & ~theButton & ~mcFlagQTVRExplicitFlagSet));
    }
}
 
 
//////////
//
// QTUtils_ToggleControllerButton
// Toggle the state of the specified button in the controller bar.
//
//////////
 
void QTUtils_ToggleControllerButton (MovieController theMC, long theButton) 
{
    if (QTUtils_IsControllerButtonVisible(theMC, theButton))    
        QTUtils_HideControllerButton(theMC, theButton);
    else
        QTUtils_ShowControllerButton(theMC, theButton);
}
 
 
//////////
//
// QTUtils_ResetControllerButton
// Remove any explicit setting of the specified button in the controller bar.
// (This allows the QuickTime VR movie controller to be as clever as it knows how to be.)
//
//////////
 
void QTUtils_ResetControllerButton (MovieController theMC, long theButton) 
{
    long    myControllerFlags = mcFlagQTVRExplicitFlagSet;
    
    // get the current explicit flags and clear the explicit flag for the specified button
    MCDoAction(theMC, mcActionGetFlags, &myControllerFlags);
    MCDoAction(theMC, mcActionSetFlags, (void *)((myControllerFlags | theButton) & ~mcFlagQTVRExplicitFlagSet));
}
 
 
//////////
//
// QTUtils_IsControllerButtonVisible
// Is the specified button in the controller bar currently visible?
//
//////////
 
Boolean QTUtils_IsControllerButtonVisible (MovieController theMC, long theButton) 
{
    long        myControllerFlags;
 
    // get the current control flags
    myControllerFlags = 0;
    MCDoAction(theMC, mcActionGetFlags, &myControllerFlags);
 
    // the speaker button requires some additional logic, because the QTVR movie controller treats it special;
    // be advised that that controller's special behavior could change in the future, so you might need to tweak this code
    if (theButton == mcFlagSuppressSpeakerButton) {
        long    myExplicitFlags;
        
        // get the current explicit flags
        myExplicitFlags = mcFlagQTVRExplicitFlagSet;
        MCDoAction(theMC, mcActionGetFlags, &myExplicitFlags);
    
        // the speaker button is not showing if the movie has no sound track and the explicit flag is not set
        if (!QTUtils_MovieHasSoundTrack(MCGetMovie(theMC)) && !(myExplicitFlags & theButton))
            return(false);
    }
    
    // the custom button requires some different treatment, since it doesn't have a "Suppress" button constant
    if (theButton == mcFlagsUseCustomButton)
        if (myControllerFlags & theButton)
            return(true);
        else
            return(false);
    
    // examine the suppress flag for the specified button
    if (myControllerFlags & theButton)              // if the button is currently suppressed...
        return(false);
    else
        return(true);
}
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Media utilities.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// QTUtils_IsMediaTypeInMovie
// Determine whether a specific media type is in a movie.
// 
//////////
 
Boolean QTUtils_IsMediaTypeInMovie (Movie theMovie, OSType theMediaType)
{
    return(GetMovieIndTrackType(theMovie, 1, theMediaType, movieTrackMediaType | movieTrackEnabledOnly) != NULL);
}
 
 
//////////
//
// QTUtils_MovieHasTimeCodeTrack
// Determine whether a movie contains a timecode track.
// 
//////////
 
Boolean QTUtils_MovieHasTimeCodeTrack (Movie theMovie)
{
    return(GetMovieIndTrackType(theMovie, 1, TimeCodeMediaType, movieTrackMediaType) != NULL);
}
 
 
//////////
//
// QTUtils_MovieHasSoundTrack
// Determine whether a movie contains a sound track.
// 
//////////
 
Boolean QTUtils_MovieHasSoundTrack (Movie theMovie)
{
    return(GetMovieIndTrackType(theMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly) != NULL);
}
 
 
//////////
//
// QTUtils_GetSoundMediaHandler
// Return the sound media handler for a movie.
// 
//////////
 
MediaHandler QTUtils_GetSoundMediaHandler (Movie theMovie)
{
    Track       myTrack;
    Media       myMedia;
 
    myTrack = GetMovieIndTrackType(theMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
    if (myTrack != NULL) {
        myMedia = GetTrackMedia(myTrack);
        return(GetMediaHandler(myMedia));
    } 
        
    return(NULL);
}
 
 
//////////
//
// QTUtils_UpdateMovieVolumeSetting
// Update the preferred volume setting of the specified movie.
// 
//////////
 
OSErr QTUtils_UpdateMovieVolumeSetting (Movie theMovie)
{
    short           myPrefVolume;
    short           myCurrVolume;
    OSErr           myErr = noErr;
    
    myPrefVolume = GetMoviePreferredVolume(theMovie);
    myCurrVolume = GetMovieVolume(theMovie);
    myCurrVolume = abs(myCurrVolume);
    
    if (myPrefVolume != myCurrVolume) {
        SetMoviePreferredVolume(theMovie, myCurrVolume);
        myErr = GetMoviesError();
    }
    
    return(myErr);
}
 
 
//////////
//
// QTUtils_SelectAllMovie
// Select the entire movie associated with the specified movie controller.
// 
//////////
 
OSErr QTUtils_SelectAllMovie (MovieController theMC)
{
    TimeRecord          myTimeRecord;
    Movie               myMovie = NULL;
    ComponentResult     myErr = noErr;
    
    if (theMC == NULL)
        return(paramErr);
    
    myMovie = MCGetMovie(theMC);
    if (myMovie == NULL)
        return(paramErr);
    
    myTimeRecord.value.hi = 0;
    myTimeRecord.value.lo = 0;
    myTimeRecord.base = 0;
    myTimeRecord.scale = GetMovieTimeScale(myMovie);    
    myErr = MCDoAction(theMC, mcActionSetSelectionBegin, &myTimeRecord);
    if (myErr != noErr)
        return((OSErr)myErr);
    
    myTimeRecord.value.hi = 0;
    myTimeRecord.value.lo = GetMovieDuration(myMovie);  
    myTimeRecord.base = 0;
    myTimeRecord.scale = GetMovieTimeScale(myMovie);    
    myErr = MCDoAction(theMC, mcActionSetSelectionDuration, &myTimeRecord);
    
    return((OSErr)myErr);
}
 
 
//////////
//
// QTUtils_SelectNoneMovie
// Select none of the movie associated with the specified movie controller.
// 
//////////
 
OSErr QTUtils_SelectNoneMovie (MovieController theMC)
{
    TimeRecord          myTimeRecord;
    Movie               myMovie = NULL;
    ComponentResult     myErr = noErr;
    
    if (theMC == NULL)
        return(paramErr);
    
    myMovie = MCGetMovie(theMC);
    if (myMovie == NULL)
        return(paramErr);
    
    myTimeRecord.value.hi = 0;
    myTimeRecord.value.lo = 0;  
    myTimeRecord.base = 0;
    myTimeRecord.scale = GetMovieTimeScale(myMovie);    
    myErr = MCDoAction(theMC, mcActionSetSelectionDuration, &myTimeRecord);
    
    return((OSErr)myErr);
}
 
 
//////////
//
// QTUtils_MakeSampleDescription
// Return a new image description with default and specified values.
// 
//////////
 
ImageDescriptionHandle QTUtils_MakeSampleDescription (long theEffectType, short theWidth, short theHeight)
{
    ImageDescriptionHandle      mySampleDesc = NULL;
 
    // create a new sample description
    mySampleDesc = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription));
    if (mySampleDesc == NULL)
        return(NULL);
    
    // fill in the fields of the sample description
    (**mySampleDesc).idSize = sizeof(ImageDescription);
    (**mySampleDesc).cType = theEffectType;
    (**mySampleDesc).vendor = kAppleManufacturer;
    (**mySampleDesc).temporalQuality = codecNormalQuality;
    (**mySampleDesc).spatialQuality = codecNormalQuality;
    (**mySampleDesc).width = theWidth;
    (**mySampleDesc).height = theHeight;
    (**mySampleDesc).hRes = 72L << 16;
    (**mySampleDesc).vRes = 72L << 16;
    (**mySampleDesc).dataSize = 0L;
    (**mySampleDesc).frameCount = 1;
    (**mySampleDesc).depth = 24;
    (**mySampleDesc).clutID = -1;
    
    return(mySampleDesc);
}
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// User data utilities.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
#if CONTENT_RATING_AVAIL
//////////
//
// QTUtils_GetContentRatingFromMovie
// Get the content rating from a movie; return an error if it has none. In any case, return a meaningful
// content rating.
//
//////////
 
OSErr QTUtils_GetContentRatingFromMovie (Movie theMovie, UInt16 *theRating, UInt32 *theReasons)
{
    UserData                    myUserData = NULL;
    QTAltContentRatingRecord    myContentRec;
    OSErr                       myErr = paramErr;
 
    *theRating = kQTContentTVYRating;
    *theReasons = 0L;
 
    // make sure we've got a movie
    if (theMovie == NULL)
        return(myErr);
        
    // get the movie's user data list
    myUserData = GetMovieUserData(theMovie);
    if (myUserData != NULL) {
        myErr = GetUserDataItem(myUserData, &myContentRec, sizeof(myContentRec), FOUR_CHAR_CODE('crat'), 0);
        if (myErr == noErr) {
            *theRating = EndianU16_BtoN(myContentRec.ratings);
            *theReasons = EndianU32_BtoN(myContentRec.contentType);
        }
    }
 
    return(myErr);
}
 
 
//////////
//
// QTUtils_AddContentRatingToMovie
// Add a content rating to a movie.
//
// A content rating is stored as a user data item that indicates both the general rating
// (for example, TV-MA [mature audiences only]) and any additional information about the
// content (for example, coarse language). Let's call this additional information the
// "reasons" for the rating. Both the rating and the reasons are specified using constants
// in the file MoviesFormat.h.
//
// This function adds the specified content rating to the movie's user data;
// the updated user data is written to the movie file when the movie is next updated
// (by calling UpdateMovieResource).
// 
//////////
 
OSErr QTUtils_AddContentRatingToMovie (Movie theMovie, UInt16 theRating, UInt32 theReasons)
{
    UserData                    myUserData = NULL;
    QTAltContentRatingRecord    myContentRec;
    OSErr                       myErr = noErr;
 
    // get the movie's user data list
    myUserData = GetMovieUserData(theMovie);
    if (myUserData == NULL)
        return(paramErr);
    
    myContentRec.flags = 0;
    myContentRec.contentType = EndianU32_NtoB(theReasons);
    myContentRec.ratings = EndianU16_NtoB(theRating);
    
    // for simplicity, we assume that we want only one content rating in the movie;
    // as a result, we won't worry about overwriting any existing item of that type
 
    // add the data to the movie's user data
    myErr = SetUserDataItem(myUserData, &myContentRec, sizeof(myContentRec), FOUR_CHAR_CODE('crat'), 0);
 
    return(myErr);
}
#endif  // #if CONTENT_RATING_AVAIL
 
 
//////////
//
// QTUtils_AddUserDataTextToMovie
// Add a user data item, of the specified type, containing the specified text to a movie.
//
// This function adds the specified text to the movie's user data;
// the updated user data is written to the movie file when the movie is next updated
// (by calling UpdateMovieResource).
// 
//////////
 
OSErr QTUtils_AddUserDataTextToMovie (Movie theMovie, char *theText, OSType theType)
{
    UserData                    myUserData = NULL;
    Handle                      myHandle = NULL;
    long                        myLength = strlen(theText);
    OSErr                       myErr = noErr;
 
    // get the movie's user data list
    myUserData = GetMovieUserData(theMovie);
    if (myUserData == NULL)
        return(paramErr);
    
    // copy the specified text into a new handle
    myHandle = NewHandleClear(myLength);
    if (myHandle == NULL)
        return(MemError());
 
    BlockMoveData(theText, *myHandle, myLength);
 
    // for simplicity, we assume that we want only one user data item of the specified type in the movie;
    // as a result, we won't worry about overwriting any existing item of that type....
    //
    // if you need multiple user data items of a given type (for example, a copyright notice
    // in several different languages), you would need to modify this code; this is left as an exercise
    // for the reader....
 
    // add the data to the movie's user data
    myErr = AddUserDataText(myUserData, myHandle, theType, 1, GetScriptManagerVariable(smRegionCode));
 
    // clean up
    DisposeHandle(myHandle);
    return(myErr);
}
 
 
//////////
//
// QTUtils_AddCopyrightToMovie
// Add a user data item containing the specified copyright text to a movie.
//
//////////
 
OSErr QTUtils_AddCopyrightToMovie (Movie theMovie, char *theText)
{
    return(QTUtils_AddUserDataTextToMovie(theMovie, theText, kUserDataTextCopyright));
}
 
 
//////////
//
// QTUtils_AddMovieNameToMovie
// Add a user data item containing the specified name to a movie.
//
//////////
 
OSErr QTUtils_AddMovieNameToMovie (Movie theMovie, char *theText)
{
    return(QTUtils_AddUserDataTextToMovie(theMovie, theText, kUserDataTextFullName));
}
 
 
//////////
//
// QTUtils_AddMovieInfoToMovie
// Add a user data item containing the specified information to a movie.
//
//////////
 
OSErr QTUtils_AddMovieInfoToMovie (Movie theMovie, char *theText)
{
    return(QTUtils_AddUserDataTextToMovie(theMovie, theText, kUserDataTextInformation));
}
 
 
//////////
//
// QTUtils_GetMovieFileLoopingInfo
// Get the looping state of a movie file.
//
// A movie file can have information about its looping state in a user data item of type 'LOOP'.
// If the movie doesn't contain an item of this type, then we'll assume that it isn't looping.
// If it does contain an item of this type, then the item data (a long integer) is 0 for normal
// looping and 1 for palindrome looping. Accordingly, this function returns the following values
// in the theLoopInfo parameter:
//
//      0 == normal looping
//      1 == palindrome looping
//      2 == no looping
//
// Return an error if the movie has no looping state. In any case, return a meaningful looping state.
//
//////////
 
OSErr QTUtils_GetMovieFileLoopingInfo (Movie theMovie, long *theLoopInfo)
{
    UserData        myUserData = NULL;
    long            myLoopInfo = kNoLooping;
    OSErr           myErr = paramErr;
 
    // make sure we've got a movie
    if (theMovie == NULL)
        goto bail;
        
    // get the movie's user data list
    myUserData = GetMovieUserData(theMovie);
    if (myUserData != NULL) {
        myErr = GetUserDataItem(myUserData, &myLoopInfo, sizeof(myLoopInfo), FOUR_CHAR_CODE('LOOP'), 0);
        if (myErr == noErr)
            myLoopInfo = EndianS32_BtoN(myLoopInfo);
    }
 
bail:
    *theLoopInfo = myLoopInfo;
 
    return(myErr);
}
 
 
//////////
//
// QTUtils_SetMovieFileLoopingInfo
// Set the looping state for a movie file.
//
//////////
 
OSErr QTUtils_SetMovieFileLoopingInfo (Movie theMovie, long theLoopInfo)
{
    UserData        myUserData = NULL;
    long            myLoopInfo;
    short           myCount = 0;
    OSErr           myErr = paramErr;
 
    // get the movie's user data
    myUserData = GetMovieUserData(theMovie);
    if (myUserData == NULL)
        goto bail;
 
    // we want to end up with at most one user data item of type 'LOOP',
    // so let's remove any existing ones
    myCount = CountUserDataType(myUserData, FOUR_CHAR_CODE('LOOP'));
    while (myCount--)
        RemoveUserData(myUserData, FOUR_CHAR_CODE('LOOP'), 1);
 
    // make sure we're writing big-endian data
    myLoopInfo = EndianU32_NtoB(theLoopInfo);
    
    switch (theLoopInfo) {
        case kNormalLooping:
        case kPalindromeLooping:
            myErr = SetUserDataItem(myUserData, &myLoopInfo, sizeof(long), FOUR_CHAR_CODE('LOOP'), 0);
            break;
 
        case kNoLooping:
        default:
            myErr = noErr;
            break;
    }
 
bail:
    return(myErr);
}
 
 
//////////
//
// QTUtils_SetLoopingStateFromFile
// Set the looping state for a movie based on the looping information in the movie file.
//
//////////
 
OSErr QTUtils_SetLoopingStateFromFile (Movie theMovie, MovieController theMC)
{
    long            myLoopInfo = kNoLooping;
    OSErr           myErr = noErr;
 
    myErr = QTUtils_GetMovieFileLoopingInfo(theMovie, &myLoopInfo);
    switch (myLoopInfo) {
 
        case kNormalLooping:
            MCDoAction(theMC, mcActionSetLooping, (void *)true);
            MCDoAction(theMC, mcActionSetLoopIsPalindrome, (void *)false);
            break;
 
        case kPalindromeLooping:
            MCDoAction(theMC, mcActionSetLooping, (void *)true);
            MCDoAction(theMC, mcActionSetLoopIsPalindrome, (void *)true);
            break;
 
        case kNoLooping:
        default:
            MCDoAction(theMC, mcActionSetLooping, (void *)false);
            MCDoAction(theMC, mcActionSetLoopIsPalindrome, (void *)false);
            break;
    }
 
    return(myErr);
}
 
 
//////////
//
// QTUtils_MakeMovieLoop
// Set the specified movie to loop.
//
// Based on the function MakeMovieLoop by Kevin Marks.
//
//////////
 
OSErr QTUtils_MakeMovieLoop (Movie theMovie, Boolean isPalindrome)
{
    TimeBase        myTimeBase = NULL;
    long            myFlags = 0L;
    OSErr           myErr = paramErr;
 
    // make sure we've got a movie
    if (theMovie == NULL)
        goto bail;
    
    myErr = noErr;
        
    // set the movie's play hints to enhance looping performance
    SetMoviePlayHints(theMovie, hintsLoop, hintsLoop);
    
    // set the looping flag of the movie's time base
    myTimeBase = GetMovieTimeBase(theMovie);
    myFlags = GetTimeBaseFlags(myTimeBase);
    myFlags |= loopTimeBase;
    
    // set or clear the palindrome flag, depending on the specified setting
    if (isPalindrome)
        myFlags |= palindromeLoopTimeBase;
    else
        myFlags &= ~palindromeLoopTimeBase;
        
    SetTimeBaseFlags(myTimeBase, myFlags);
 
bail:
    return(myErr);
}
 
 
//////////
//
// QTUtils_GetWindowPositionFromFile
// Return, through thePoint, the stored position of the specified movie.
//
// Return an error if the movie has no stored position. In any case, return a meaningful position.
//
//////////
 
OSErr QTUtils_GetWindowPositionFromFile (Movie theMovie, Point *thePoint)
{
    UserData        myUserData = NULL;
    Point           myPoint = {kDefaultWindowX, kDefaultWindowY};
    OSErr           myErr = paramErr;
 
    // make sure we've got a movie
    if (theMovie == NULL)
        goto bail;
        
    // get the movie's user data list
    myUserData = GetMovieUserData(theMovie);
    if (myUserData != NULL) {
        myErr = GetUserDataItem(myUserData, &myPoint, sizeof(Point), FOUR_CHAR_CODE('WLOC'), 0);
        if (myErr == noErr) {
            myPoint.v = EndianS16_BtoN(myPoint.v);
            myPoint.h = EndianS16_BtoN(myPoint.h);
        }
    }
 
bail:
    *thePoint = myPoint;
 
    return(myErr);
}
 
 
//////////
//
// QTUtils_GetTrackName
// Get the name of the specified movie track.
//
// This routine is modelled on the one contained in Dispatch 2 from the Ice Floe;
// I've modified it to return a C string instead of a Pascal string.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
char *QTUtils_GetTrackName (Track theTrack)
{
    UserData            myUserData = NULL;
    char                *myString = NULL;
    OSErr               myErr = noErr;
 
    // make sure we've got a track
    if (theTrack == NULL)
        return(NULL);
        
    // a track's name (if it has one) is stored in the track's user data
    myUserData = GetTrackUserData(theTrack);
    if (myUserData != NULL) {
        Handle          myHandle = NewHandle(0);
 
        // get the user data item of type kUserDataName;
        // the handle we pass to GetUserData is resized to contain the track name
        myErr = GetUserData(myUserData, myHandle, kUserDataName, 1);
        if (myErr == noErr) {
            long        myLength = GetHandleSize(myHandle);
 
            if (myLength > 0) {
                myString = malloc(myLength + 1);
                if (myString != NULL) {
                    memcpy(myString, *myHandle, myLength);
                    myString[myLength] = '\0';
                }
            }           
        }   
 
        DisposeHandle(myHandle);
    }
 
    return(myString);
}
 
 
//////////
//
// QTUtils_SetTrackName
// Set the name of the specified movie track.
//
// This function adds the specified text to the track's user data;
// the updated user data is written to the movie file when the movie is next updated
// (by calling UpdateMovieResource).
//
//////////
 
OSErr QTUtils_SetTrackName (Track theTrack, char *theText)
{
    UserData            myUserData = NULL;
    OSErr               myErr = noErr;
 
    // make sure we've got a track and a name
    if ((theTrack == NULL) || (theText == NULL))
        return(paramErr);
        
    // get the track's user data list
    myUserData = GetTrackUserData(theTrack);
    if (myUserData == NULL)
        return(paramErr);
    
    // remove any existing track name
    while (RemoveUserData(myUserData, kUserDataName, 1) == noErr)
        ;
 
    myErr = SetUserDataItem(myUserData, theText, strlen(theText), kUserDataName, 0);
 
    return(myErr);
}
 
 
//////////
//
// QTUtils_MakeTrackNameByType
// Create a (unique) name for the specified track, based on the track's type.
//
// Given a movie track, this routine constructs a name for that track based on
// the media type of that track. For instance, if the track is a sound track,
// this routine returns the name "Sound". However, if there is more than one
// track of that media type, then this routine numbers the track names. So, if
// there are two sound tracks, this routine names them "Sound 1" and "Sound 2".
//
// This routine is modelled on the one contained in Dispatch 2 from the Ice Floe;
// I've modified it to return a C string instead of a Pascal string.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
char *QTUtils_MakeTrackNameByType (Track theTrack)
{
    Media               myMedia;
    char                *myString = NULL;
    ComponentResult     myErr = noErr;
 
    // make sure we've got a track
    if (theTrack == NULL)
        return(NULL);
    
    myMedia = GetTrackMedia(theTrack);
    if (myMedia != NULL) {
        MediaHandler    myMediaHandler = GetMediaHandler(myMedia);
        OSType          myMediaType;
        Str255          myName;
 
        // get the media type of the track
        GetMediaHandlerDescription(myMedia, &myMediaType, NULL, NULL);
        
        // get the text of the media type
        myErr = MediaGetName(myMediaHandler, myName, 0, NULL);
        if (myErr == noErr) {
            
            // determine whether there's more than one track with this media type
            if (GetMovieIndTrackType(GetTrackMovie(theTrack), 2, myMediaType, movieTrackMediaType) != NULL) {
 
                // add an index number to the track type string we constructed above
                long    myIndex = 1;
                Str255  myNumString;
 
                while (GetMovieIndTrackType(GetTrackMovie(theTrack), myIndex, myMediaType, movieTrackMediaType) != theTrack)
                   myIndex++;
 
                NumToString(myIndex, myNumString);
                myName[++myName[0]] = ' ';
                BlockMoveData(&myNumString[1], &myName[myName[0] + 1], myNumString[0]);
                myName[0] += myNumString[0];
            }
 
            // now copy the string data from the Pascal string to a C string
            if (myName[0] > 0) {
                myString = malloc(myName[0] + 1);
                if (myString != NULL) {
                    memcpy(myString, &myName[1], myName[0]);
                    myString[myName[0]] = '\0';
                }
            }           
        }
    }
 
    return(myString);
}
 
 
//////////
//
// QTUtils_IsImageFile
// Is the specified file an image file?
//
//////////
 
Boolean QTUtils_IsImageFile (FSSpec *theFSSpec)
{   
    GraphicsImportComponent     myImporter = NULL;
 
    GetGraphicsImporterForFile(theFSSpec, &myImporter);
    if (myImporter != NULL)
        CloseComponent(myImporter);
 
    return(myImporter != NULL);
}
 
 
//////////
//
// QTUtils_IsMovieFile
// Is the specified file a file that can be opened by QuickTime as a movie?
//
//////////
 
Boolean QTUtils_IsMovieFile (FSSpec *theFSSpec)
{   
    Boolean                     isMovieFile = false;
    AliasHandle                 myAlias = NULL;
    Component                   myImporter = NULL;
    FInfo                       myFinderInfo;
    OSErr                       myErr = noErr;
            
    // see whether the file type is MovieFileType; to do this, get the Finder information
    myErr = FSpGetFInfo(theFSSpec, &myFinderInfo);  
    if (myErr == noErr)
        if (myFinderInfo.fdType == kQTFileTypeMovie)
            return(true);
 
    // if it isn't a movie file, see whether the file can be imported as a movie
    myErr = QTNewAlias(theFSSpec, &myAlias, true);
    if (myErr == noErr) {
        if (myAlias != NULL) {
            myErr = GetMovieImporterForDataRef(rAliasType, (Handle)myAlias, kGetMovieImporterDontConsiderGraphicsImporters, &myImporter);
            DisposeHandle((Handle)myAlias);
        }
    }
        
    if ((myErr == noErr) && (myImporter != NULL))       // this file is a movie file
        isMovieFile = true;
 
    return(isMovieFile);
}
 
 
//////////
//
// QTUtils_ConvertFloatToBigEndian
// Convert the specified floating-point number to big-endian format.
//
//////////
 
void QTUtils_ConvertFloatToBigEndian (float *theFloat)
{
    unsigned long       *myLongPtr;
    
    myLongPtr = (unsigned long *)theFloat;
    *myLongPtr = EndianU32_NtoB(*myLongPtr);
}
 
 
//////////
//
// QTUtils_ConvertCToPascalString
// Convert a C string into a Pascal string.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
StringPtr QTUtils_ConvertCToPascalString (char *theString)
{
    StringPtr   myString = malloc(strlen(theString) + 1);
    short       myIndex = 0;
 
    while (theString[myIndex] != '\0') {
        myString[myIndex + 1] = theString[myIndex];
        myIndex++;
    }
    
    myString[0] = (unsigned char)myIndex;
    
    return(myString);
}
 
 
//////////
//
// QTUtils_ConvertPascalToCString
// Convert a Pascal string into a C string.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
char *QTUtils_ConvertPascalToCString (StringPtr theString)
{
    char        *myString = malloc(theString[0] + 1);
    short       myIndex = 0;
 
    for (myIndex = 0; myIndex < theString[0]; myIndex++)
        myString[myIndex] = theString[myIndex + 1];
    
    myString[theString[0]] = '\0';
    
    return(myString);
}
 
 
//////////
//
// QTUtils_DeleteAllReferencesToTrack
// Delete all existing track references to the specified track.
//
//////////
 
OSErr QTUtils_DeleteAllReferencesToTrack (Track theTrack)
{
    Movie               myMovie = NULL;
    Track               myTrack = NULL;
    long                myTrackCount = 0L;
    long                myTrRefCount = 0L;
    long                myTrackIndex;
    long                myTrRefIndex;
    OSErr               myErr = noErr;
 
    myMovie = GetTrackMovie(theTrack);
    if (myMovie == NULL)
        return(paramErr);
 
    // iterate thru all the tracks in the movie (that are different from the specified track)
    myTrackCount = GetMovieTrackCount(myMovie);
    for (myTrackIndex = 1; myTrackIndex <= myTrackCount; myTrackIndex++) {
        myTrack = GetMovieIndTrack(myMovie, myTrackIndex);
        if ((myTrack != NULL) && (myTrack != theTrack)) {
            OSType      myType = 0L;
    
            // iterate thru all track reference types contained in the current track
            myType = GetNextTrackReferenceType(myTrack, myType);
            while (myType != 0L) {
 
                // iterate thru all track references of the current type;
                // note that we count down to 1, since DeleteTrackReference will cause
                // any higher-indexed track references to be renumbered
                myTrRefCount = GetTrackReferenceCount(myTrack, myType);
                for (myTrRefIndex = myTrRefCount; myTrRefIndex >= 1; myTrRefIndex--) {
                    Track   myRefTrack = NULL;
 
                    myRefTrack = GetTrackReference(myTrack, myType, myTrRefIndex);
                    if (myRefTrack == theTrack)
                        myErr = DeleteTrackReference(myTrack, myType, myTrRefIndex);
                }
 
                myType = GetNextTrackReferenceType(myTrack, myType);
            }
        }
    }
 
    return(myErr);
}
 
 
//////////
//
// QTUtils_GetFrameDuration
// Get the duration of the first sample frame in the specified movie track.
//
//////////
 
TimeValue QTUtils_GetFrameDuration (Track theTrack)
{   
    TimeValue   mySampleDuration;
    OSErr       myErr = noErr;
 
    myErr = GetMediaSample( GetTrackMedia(theTrack),
                            NULL,       // don't return sample data
                            0,
                            NULL,       // don't return number of bytes of sample data returned
                            0,
                            NULL,
                            &mySampleDuration,
                            NULL,       // don't return sample description
                            NULL,       // don't return sample description index
                            0,          // no max number of samples
                            NULL,       // don't return number of samples returned
                            NULL);      // don't return sample flags
 
    // make sure we return a legitimate value even if GetMediaSample encounters an error
    if (myErr != noErr)
        mySampleDuration = 0;
 
    return(mySampleDuration);
}
 
 
//////////
//
// QTUtils_GetFrameCount
// Get the number of frames in the specified movie track. We return the value -1 if
// an error occurs and we cannot determine the number of frames in the track.
//
// Based (loosely) on frame-counting code in ConvertToMovie Jr.c.
// 
// We count the frames in the track by stepping through all of its interesting times
// (the places where the track displays a new sample).
//
//////////
 
long QTUtils_GetFrameCount (Track theTrack)
{   
    long        myCount = -1;
    short       myFlags;
    TimeValue   myTime = 0;
    
    if (theTrack == NULL)
        goto bail;
        
    // we want to begin with the first frame (sample) in the track
    myFlags = nextTimeMediaSample + nextTimeEdgeOK;
 
    while (myTime >= 0) {
        myCount++;
        
        // look for the next frame in the track; when there are no more frames,
        // myTime is set to -1, so we'll exit the while loop
        GetTrackNextInterestingTime(theTrack, myFlags, myTime, fixed1, &myTime, NULL);
        
        // after the first interesting time, don't include the time we're currently at
        myFlags = nextTimeStep;
    }
 
bail:
    return(myCount);
}
 
 
//////////
//
// QTUtils_GetMaxWindowDepth
// Get the deepest pixel type and size of the screen area intersected by a specified window.
//
//////////
 
#if TARGET_OS_MAC
void QTUtils_GetMaxWindowDepth (CWindowPtr theWindow, short *thePixelType, short *thePixelSize)
{
    Rect            myRect;
    Point           myPoint = {0, 0};
    
    // initialize returned values 
    *thePixelType = k1MonochromePixelFormat;
    *thePixelSize = 0;
 
    if (theWindow == NULL)
        return;
 
    GetWindowPortBounds(theWindow, &myRect);
 
    // assume the window is the current port
    LocalToGlobal(&myPoint);
 
    // offset the rectangle to global coordinates
    MacOffsetRect(&myRect, myPoint.h, myPoint.v);
    
    // get the max data for the global rectangle
    QTUtils_GetMaxScreenDepth(&myRect, thePixelType, thePixelSize);
}
#endif
 
//////////
//
// QTUtils_GetMaxScreenDepth
// Get the deepest pixel type and size of the screen area intersected by a specified rectangle.
//
//////////
 
void QTUtils_GetMaxScreenDepth (Rect *theGlobalRect, short *thePixelType, short *thePixelSize)
{
    GDHandle        myGDevice = NULL;
    PixMapHandle    myPixMap = NULL;
 
    myGDevice = GetMaxDevice(theGlobalRect);    // get the max device
    if (myGDevice != NULL) {
        // get the pixmap for the max device
        myPixMap = (**myGDevice).gdPMap;
        if (myPixMap != NULL) {
            // extract the interesting info from the pixmap of the device
            *thePixelType = (**myPixMap).pixelType;
            *thePixelSize = (**myPixMap).pixelSize;
        }
    }
}
 
 
//////////
//
// QTUtils_GetUsersConnectionSpeed
// Return the connection speed selected by the user in the QuickTime Settings control panel;
// return kDataRate288ModemRate if the user's QuickTime preferences cannot be read of if some
// other error occurs.
//
// Based on code in Ice Floe Dispatch 17 by Mike Dodd.
//
//////////
 
long QTUtils_GetUsersConnectionSpeed (void)
{
    QTAtomContainer                 myPrefsContainer = NULL;
    QTAtom                          myPrefsAtom = 0;
    ConnectionSpeedPrefsRecord      myPrefsRec;
    long                            myDataSize = 0L;
    long                            mySpeed = kDataRate288ModemRate;
    Ptr                             myAtomData = NULL;
    OSErr                           myErr = noErr;
 
    // you can retrieve the user's QuickTime preferences by calling GetQuickTimePreference;
    // the first parameter indicates the type of preference you want information about, and
    // the second parameter is an atom container that contains the returned preference data 
    myErr = GetQuickTimePreference(ConnectionSpeedPrefsType, &myPrefsContainer);
    if (myErr == noErr) {
            
        // find the atom of the desired type
        myPrefsAtom = QTFindChildByID(myPrefsContainer, kParentAtomIsContainer, ConnectionSpeedPrefsType, 1, NULL);
        if (myPrefsAtom != 0) {
            // we found the desired atom in the returned atom container;
            // read the data contained in that atom and verify that the data is of the
            // size we are expecting
            QTGetAtomDataPtr(myPrefsContainer, myPrefsAtom, &myDataSize, &myAtomData);
            
            if (myDataSize == sizeof(ConnectionSpeedPrefsRecord)) {
                // read the connection speed
                
                // NOTE: the data in this atom is native-endian, so we do not need to
                // perform any endian-swapping when extracting the speed from the atom.
                // (This is an exception to the rule that data in atom containers is
                // always big-endian.)
                myPrefsRec = *(ConnectionSpeedPrefsRecord *)myAtomData;
                mySpeed = myPrefsRec.connectionSpeed;
            }
        }
 
        QTDisposeAtomContainer(myPrefsContainer);
    }
    
    return(mySpeed);
}
 
 
//////////
//
// QTUtils_SetUsersConnectionSpeed
// Set the connection speed in the QuickTime Settings control panel to the specified value.
//
// NOTE: In general, you should let the user decide the connection speed (using the QuickTime
// Settings control panel). In some cases, however, you might need to do this programmatically.
// Also, you should in general use values for theSpeed that are enumerated in the header file
// MoviesFormat.h.
//
// Based on code in Ice Floe Dispatch 17 by Mike Dodd.
//
//////////
 
OSErr QTUtils_SetUsersConnectionSpeed (long theSpeed)
{
    QTAtomContainer                 myPrefsContainer = NULL;
    ConnectionSpeedPrefsRecord      myPrefsRec;
    OSErr                           myErr = noErr;
 
    myErr = QTNewAtomContainer(&myPrefsContainer);
    if (myErr == noErr) {
        // NOTE: the data in this atom is native-endian, so we do not need to
        // perform any endian-swapping when inserting the speed into the atom.
        // (This is an exception to the rule that data in atom containers is
        // always big-endian.)
        myPrefsRec.connectionSpeed = theSpeed;
        
        myErr = QTInsertChild(myPrefsContainer, kParentAtomIsContainer, ConnectionSpeedPrefsType, 1, 0, sizeof(ConnectionSpeedPrefsRecord), &myPrefsRec, NULL);
        if (myErr == noErr)
            myErr = SetQuickTimePreference(ConnectionSpeedPrefsType, myPrefsContainer);
            
        QTDisposeAtomContainer(myPrefsContainer);
    }
 
    return(myErr);
}
 
 
#if CONTENT_RATING_AVAIL
//////////
//
// QTUtils_GetUsersContentRating
// Return, through the function's parameters, the content rating and acceptable content types
// selected by the user in the QuickTime Settings control panel; return an error if the user's
// QuickTime preferences cannot be read.
//
// Based on QTUtils_GetUsersConnectionSpeed.
//
//////////
 
OSErr QTUtils_GetUsersContentRating (UInt32 *theType, UInt16 *theRating)
{
    QTAtomContainer                 myPrefsContainer = NULL;
    QTAtom                          myPrefsAtom = 0;
    ContentRatingPrefsRecord        myContentRec;
    long                            myDataSize = 0L;
    Ptr                             myAtomData = NULL;
    OSErr                           myErr = noErr;
 
    // you can retrieve the user's QuickTime preferences by calling GetQuickTimePreference;
    // the first parameter indicates the type of preference you want information about, and
    // the second parameter is an atom container that contains the returned preference data 
    myErr = GetQuickTimePreference(kContentRatingPrefsType, &myPrefsContainer);
    if (myErr == noErr) {
            
        // find the atom of the desired type
        myPrefsAtom = QTFindChildByID(myPrefsContainer, kParentAtomIsContainer, kContentRatingPrefsType, 1, NULL);
        if (myPrefsAtom == 0) {
            // we did not find any such atom in the returned atom container, so we'll
            // return default settings
            *theType = 0L;
            *theRating = kQTContentTVYRating;
        } else {
            // we found the desired atom in the returned atom container;
            // read the data contained in that atom and verify that the data is of the
            // size we are expecting
            myErr = QTGetAtomDataPtr(myPrefsContainer, myPrefsAtom, &myDataSize, &myAtomData);
            
            if (myDataSize != sizeof(ContentRatingPrefsRecord)) {
                // the data in the atom isn't the right size, so it must be corrupt;
                // return default settings
                *theType = 0L;
                *theRating = kQTContentTVYRating;
            } else {
                // everything is fine: read the content information
                
                // NOTE: the data in this atom is native-endian, so we do not need to
                // perform any endian-swapping when extracting the data from the atom.
                // (This is an exception to the rule that data in atom containers is
                // always big-endian.)
                
                // WARNING: the format of the data in a content rating atom is, to my
                // knowledge, currently undocumented; the following method of extracting
                // that info is based on empirical investigation.
                myContentRec = *(ContentRatingPrefsRecord *)myAtomData;
                *theType = (UInt32)(~(myContentRec.fContentTypes) & 0x00ff);
                *theRating = myContentRec.fContentRating;
            }
        }
 
        QTDisposeAtomContainer(myPrefsContainer);
    }
    
    return(myErr);
}
#endif  // #if CONTENT_RATING_AVAIL
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Controller utilities.
//
// Use these functions to manipulate movie controllers.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// QTUtils_GetControllerType
// Get the controller type of the specified movie.
//
//////////
 
OSType QTUtils_GetControllerType (Movie theMovie) 
{
    UserData        myUserData = NULL;
    OSType          myType = kUnknownType;
    OSErr           myErr = noErr;
    
    // make sure we've got a movie
    if (theMovie == NULL)
        return(myType);
        
    myUserData = GetMovieUserData(theMovie);
    if (myUserData != NULL) {
        myErr = GetUserDataItem(myUserData, &myType, sizeof(myType), kUserDataMovieControllerType, 0);
        if (myErr == noErr)
            myType = EndianU32_BtoN(myType);
    }
    
    return(myType);
}
 
 
//////////
//
// QTUtils_SetControllerType
// Set the controller type of the specified movie.
//
// This function adds an item to the movie's user data;
// the updated user data is written to the movie file when the movie is next updated
// (by calling AddMovieResource or UpdateMovieResource).
//
// NOTE: This function is intended to set the controller type of a movie you're building;
// to change the controller of an open movie, use QTUtils_ChangeControllerType.
//
//////////
 
OSErr QTUtils_SetControllerType (Movie theMovie, OSType theType)
{
    UserData        myUserData;
    OSErr           myErr = noErr;
 
    // make sure we've got a movie
    if (theMovie == NULL)
        return(paramErr);
        
    // get the movie's user data list
    myUserData = GetMovieUserData(theMovie);
    if (myUserData == NULL)
        return(paramErr);
    
    theType = EndianU32_NtoB(theType);
    myErr = SetUserDataItem(myUserData, &theType, sizeof(theType), kUserDataMovieControllerType, 0);
 
    return(myErr);
}
 
 
//////////
//
// QTUtils_ChangeControllerType
// Change the controller type of the movie that uses the specified controller, "on the fly",
// and return the new movie controller to the caller; if for some reason we cannot create a
// new movie controller, return NULL.
//
//////////
 
MovieController QTUtils_ChangeControllerType (MovieController theMC, OSType theType, long theFlags)
{
    MovieController     myMC = NULL;
    Movie               myMovie = NULL;
    Rect                myRect;
    OSErr               myErr = noErr;
 
    // make sure we've got a movie controller
    if (theMC == NULL)
        return(NULL);
    
    // get the movie associated with that controller
    myMovie = MCGetMovie(theMC);
    if (myMovie == NULL)
        return(NULL);
        
    GetMovieBox(myMovie, &myRect);
 
    // set the new controller type in the movie's user data list
    myErr = QTUtils_SetControllerType(myMovie, theType);
    if (myErr != noErr)
        return(NULL);
 
    // dispose of the existing controller
    DisposeMovieController(theMC);
    
    // create a new controller of the specified type
    myMC = NewMovieController(myMovie, &myRect, theFlags);
 
    return(myMC);
}
 
 
#endif  // ifndef __QTUtilities__