DTSQTUtilities.c

/*
    File:       DTSUtilities.c
 
    Contains:   QuickTime functions.
 
    Written by: DTS
 
    Copyright:  © 1994-1995 by Apple Computer, Inc., all rights reserved.
 
    Change History (most recent first):
 
       <1>      12/17/94    khs     first file
       
*/
 
 
// INCLUDES
#include "DTSQTUtilities.h"
 
 
// MOVIE TOOLBOX FUNCTIONS
 
/*______________________________________________________________________
    QTUIsQuickTimeInstalled - Test if QuickTime is installed.
 
pascal Boolean    QTUIsQuickTimeInstalled(void) 
 
DESCRIPTION
    InitQuickTime will test if QuickTime is present. We are not interested in the QuickTime
    version.
 
ISSUES
    You could combine this function with the QTUGetQTVersion so you could also fetch the
    version level of QT at the same time.
*/
 
pascal Boolean QTUIsQuickTimeInstalled(void) 
{
    OSErr   anErr = noErr;
    long        qtVersion;
 
    anErr = Gestalt(gestaltQuickTime, &qtVersion); DebugAssert(anErr == noErr);
    if (anErr != noErr)
        return false;       // no QT present
    else
        return true;
}
 
 
/*______________________________________________________________________
    QTUIsQuickTimeCFMInstalled - Test if the QuickTime CFM libraries are installed and in the 
    right place.
 
pascal Boolean    QTUIsQuickTimeCFMInstalled(void) 
 
DESCRIPTION
    QTUIsQuickTimeCFMInstalled will test if the CFM QuickTime libraries are present (QuickTime 
    PowerPlug, for instance), and if the libraries are still present (this because the libraries are 
    registered once when Gestalt finds then during runtime, and the end user might delete these, 
    or move them to another location later)(.
*/
 
#ifdef powerc
pascal Boolean QTUIsQuickTimeCFMInstalled(void) 
{
    OSErr   anErr = noErr;
    long        qtFeatures = 0L; 
 
// Test if the library is registered.
    anErr = Gestalt(gestaltQuickTimeFeatures, &qtFeatures); DebugAssert(anErr == noErr);
    
    if (!(  (anErr == noErr)  &&  (qtFeatures & (1 << gestaltPPCQuickTimeLibPresent))  )) // not true
          return false;
          
// Test if a function is available (the library is not moved from the Extension folder),  this is the 
// trick to be used concerning testing if a function is available via CFM.
 
    if   ( ! CompressImage )
        return false;     
    else 
        return true;
}
#endif // powerc
 
 
/*______________________________________________________________________
    QTUGetQTVersion - Return the current QuickTime version number.
 
pascal long QTUGetQTVersion()
 
DESCRIPTION
    QTUGetQTVersion is a simple function that will return the current QuickTime version number,
    and if QuickTime is not installed it will return 0L.  The high order word defines the version number, 
    for instance 0x0161 defines version 1.6.1.
    
    You could also directly assign a boolean value stating if a certain version is true by using this
    kind of an expression:
    
    Boolean gHasQT2.0 = (( QTUGetQTVersion() >>  16) & 0xFFFF) >= 0x200;
    
EXAMPLE
    if( (QTUGetQTVersion() >> 16) < 0x150 ) return; // need to work with QT 1.5 or higher.
*/
 
pascal long QTUGetQTVersion()
{
    long version = 0L;
    
    if(Gestalt(gestaltQuickTime, &version) == noErr)
        return version;
    else
        return 0L;
}
 
 
/*______________________________________________________________________
    QTUAreQuickTimeMusicInstrumentsPresent - Test if the Musical Instruments Extension is 
    installed.
 
pascal Boolean QTUAreQuickTimeMusicInstrumentsPresent(void)
 
DESCRIPTION
    QTUAreQuickTimeMusicInstrumentsPresent tests if the QuickTime Musical Instruments
    extension (actually a component) is registered. If this is not the case, then most likely
    the extension was never placed into the extension folder, and the end user should be
    informed about this.
*/
 
pascal Boolean QTUAreQuickTimeMusicInstrumentsPresent(void)
{
    ComponentDescription aCD;
    
    aCD.componentType = 'inst';
    aCD.componentSubType = 'ss  ';
    aCD.componentManufacturer = 'appl';
    
    if(FindNextComponent((Component)0, &aCD) != NULL)
        return true;
    else
        return false;
}
 
 
/*______________________________________________________________________
     QTUPrerollMovie - Preroll the movie before you start the movie.
 
pascal OSErr QTUPrerollMovie(Movie theMovie)
 
theMovie                the destination movie for this operation
 
DESCRIPTION
    QTUPrerollMovie will get the movie time,  duration and preferred rate, and Preroll the movie 
    based on this information. Note that StartMovie already does a PrerollMovie so in that case this 
    is not needed, this is also true of the standard controller that handles the start of movie
    when the keyboard or mouse is used.
*/
 
pascal OSErr QTUPrerollMovie(Movie theMovie) 
{
    OSErr               anErr = noErr;
    TimeValue       aTimeValue;
    TimeValue           aMovieDur;
    Fixed               aPreferredRate;
 
    aTimeValue       = GetMovieTime(theMovie, NULL);
    aMovieDur        = GetMovieDuration(theMovie);
    aPreferredRate  = GetMoviePreferredRate(theMovie);
 
    if(aTimeValue == aMovieDur) aTimeValue = 0;
 
    anErr = PrerollMovie(theMovie, aTimeValue, aPreferredRate); DebugAssert(anErr == noErr);
    
    return anErr;
}
 
 
pascal Boolean QTUFileFilter(ParmBlkPtr theParamBlock);
 
 
/*______________________________________________________________________
    QTUGetMovie - Get a Movie from a specific file.
 
pascal Movie QTUGetMovie(FSSpec *theFSSpec, short *theRefNum, short *theResID)
 
theFSSpec               the specific FSSpec used, if NULL the system will use a standard dialog
                            box for the end user to select a file
theRefNum           this is the specific file ref num we want to use later
theResID                this is the specific resource ID we want to use later
 
DESCRIPTION
    QTUGetMovie will get a movie resource out from a specified file, if the FSSpec is not provided
    then the function will use a StandardGetFilePreview to select the movie.
*/
 
pascal Movie QTUGetMovie(FSSpec *theFSSpec, short *theRefNum, short *theResID)
{
    OSErr                   anErr = noErr;
    SFTypeList          aTypeList = {MovieFileType, 0, 0, 0};
    StandardFileReply   aReply;
    Movie                   aMovie = NULL;
 
// If we are provided with an FSSpec then use it, otherwise do a standardgetfile dialog box and 
// ask the end user to get it.
    if(theFSSpec == NULL || theFSSpec->vRefNum == 0)
    {   
        StandardGetFilePreview( NewFileFilterProc(QTUFileFilter), 1, aTypeList, &aReply);
        if(! aReply.sfGood)
            return NULL;
        
        *theFSSpec = aReply.sfFile;
    }
 
    // We should have now a usable FSSpec, just double check this once again before continuing.
    DebugAssert(theFSSpec != NULL); if(theFSSpec == NULL) return NULL;
    
    anErr = OpenMovieFile(theFSSpec, theRefNum, fsRdPerm); DebugAssert(anErr == noErr);
    // Note we define fsRdPerm, you could use another flag if needed.
 
    if(anErr == noErr)
    {
        Str255  aMovieName;
        Boolean wasChanged;
        
        *theResID = 0;                  // want first movie
        
        anErr = NewMovieFromFile(&aMovie, *theRefNum, theResID,
                                                    aMovieName, newMovieActive, &wasChanged);
 
        DebugAssert(anErr == noErr);
 
        CloseMovieFile(*theRefNum);
    }
    
    if(anErr != noErr)
        return NULL;
    else
        return aMovie;
}
 
 
/*______________________________________________________________________
    QTUSimpleGetMovie - Get a Movie from a specific file (simpler version)
 
pascal OSErr QTUSimpleGetMovie(Movie *theMovie)
 
theMovie                will contain the selected movie when function exits.
 
DESCRIPTION
    QTUSimpleGetMovie is a simplified version of getting a movie from a file, no need for
    returning refnums, res IDs of keeping track of FSSpecs (compared with QTUGetMovie)
*/
 
pascal OSErr QTUSimpleGetMovie(Movie *theMovie)
{
    OSErr                   anErr = noErr;
    SFTypeList          aTypeList = {MovieFileType, 0, 0, 0};
    short                   resFile = 0;
    short                   resID = 0;
    StandardFileReply   aReply;
    Str255                  movieName;
    Boolean                 wasChanged;
 
    StandardGetFilePreview(NewFileFilterProc(QTUFileFilter), 1, aTypeList, &aReply);
    if(aReply.sfGood)
    {
        anErr = OpenMovieFile(&aReply.sfFile, &resFile, fsRdPerm); DebugAssert(anErr == noErr);
        if(anErr == noErr)
        {
            anErr = NewMovieFromFile(theMovie, resFile, &resID, movieName, newMovieActive, &wasChanged);
            DebugAssert(anErr == noErr);
 
            CloseMovieFile(resFile);
        }
    }
    return anErr;
}
 
 
/*______________________________________________________________________
    QTUFileFilter - Skeleton file filter to be used with various MovieToolbox standard dialog utilities.
 
pascal Boolean QTUFileFilter(ParmBlkPtr theParamBlock)
 
theParamBlock       specifies a particular ParmBlockPtr
 
DESCRIPTION
    QTUFileFilter is a skeleton file filter to be used with various MovieToolbox standard dialog utilities
    The function will return a boolean false if it encounters any errors from the Movie toolbox.
*/
 
pascal Boolean QTUFileFilter(ParmBlkPtr /*theParamBlock */)
{
    return false;
}
 
 
/*______________________________________________________________________
    QTUSaveMovie - Save and flatten a movie resource into a  file.
 
pascal OSErr QTUSaveMovie(Movie theMovie)
 
theMovie            defines the movie to be saved into a file
 
DESCRIPTION
    QTUSaveMovie will provide a user dialog asking for a file name, and will then save the movie
    into this file. Note that this function will also automatically flatten the movie so that  it's 
    self-contained, and also make it cross-platform (by adding any possible resource forks to
    the end of the data fork. The default name of the movie is also NEWMOVIE.MOV, this reflects
    the way movie file names should be named for cross-platform support (Windows). The default
    creator type is also 'TVOD' so that MoviePlayer will be the default application that opens the
    movie file. If there's an existing movie file with the same name, it will be deleted.
*/
 
 
pascal OSErr QTUSaveMovie(Movie theMovie)
{
    OSErr                   anErr = noErr;
    StandardFileReply   anSFReply;
    
    DebugAssert(theMovie != NULL); if(theMovie == NULL) return invalidMovie;
    
    StandardPutFile("\pSave Movie as:" , "\pNEWMOVIE.MOV", &anSFReply); 
    if(anSFReply.sfGood)
    {
 
        FlattenMovieData(theMovie, flattenAddMovieToDataFork, &anSFReply.sfFile, 
                                        'TVOD', smSystemScript, createMovieFileDeleteCurFile );
        anErr = GetMoviesError(); DebugAssert(anErr == noErr);
    }
        return anErr;
}
 
 
/*______________________________________________________________________
    QTUFlattenMovieFile - Flatten a movie into a specified file.
 
pascal OSErr QTUFlattenMovieFile(Movie theMovie, FSSpec *theFile)
 
theMovie                defines the movie to be flattened
theFile                 defines the target file
 
DESCRIPTION
    FlattenMovie file will take an existing movie, flatten it into a temp file, and then move the
    contents of the temp file into the specified FSSpec. This because there are cases where we 
    can't flatten a movie in place. We will use TickCount as a temp file name.  
    
    Note that we need to dispose the movie inside this function? Why? Well, the file is open as
    long as there's a pointer to it from the movie resource. And we need to delete the original 
    movie file as part of the operation of swapping the files. 
*/
 
pascal OSErr QTUFlattenMovieFile(Movie theMovie, FSSpec *theFile)
{
    OSErr       anErr = noErr;
    FSSpec      tempFile;
    Str255  tempFileName;
    
    DebugAssert(theMovie != NULL); if(theMovie == NULL) return invalidMovie;
    
    // Create the needed temp file.
    NumToString(TickCount(), tempFileName);
        anErr = FSMakeFSSpec(theFile->vRefNum, theFile->parID, tempFileName, &tempFile);
    if(anErr != fnfErr) return anErr;
    
    // Flatten the movie.
    FlattenMovie(theMovie, flattenAddMovieToDataFork, &tempFile, 'TVOD', smSystemScript, 
                            createMovieFileDeleteCurFile, 0, NULL);
    anErr = GetMoviesError();
    if(anErr != noErr)
    {
        FSpDelete(&tempFile);       // remove the temp file
        return anErr;
    }
    
    DisposeMovie(theMovie);
    anErr = FSpDelete(theFile);  ReturnIfError(anErr);
    anErr = FSpRename(&tempFile, theFile->name); ReturnIfError(anErr);
    
    return anErr;
}
 
 
// TRACKS AND MEDIA
 
/*______________________________________________________________________
    QTUMediaTypeInTrack - Check if a particular media type is present in the movie.
 
pascal Boolean QTUMediaTypeInTrack(Movie theMovie, OSType theMediaType)
 
theMovie                    movie to be tested about the media type
theMediaType            media type we want to test about
 
DESCRIPTION
    QTUMediaTypeInTrack could be used to scan if a possible media type is present in the movie 
    (video,sound, other media types).
*/
 
pascal Boolean QTUMediaTypeInTrack(Movie theMovie, OSType theMediaType)
{
    Track       aTrack = NULL;
    long            aTrackCount = 0;
    long            index;
    OSType      aMediaType;
    Boolean     haveMediaType = false;
    
    aTrackCount = GetMovieTrackCount(theMovie);
    if(aTrackCount == 0)
        return false;               // no tracks in movie
    
    for(index = 1; index <= aTrackCount; index++)
    {
        aTrack = GetMovieIndTrack(theMovie, index);
        GetMediaHandlerDescription( GetTrackMedia(aTrack), &aMediaType, NULL, NULL);
        
        haveMediaType = ( aMediaType == theMediaType);
        if(haveMediaType == true)
            return true;
    }
    return false;           // didn't find the media type track in the movie
}
 
 
/*______________________________________________________________________
    QTUGetTrackRect - Get the Rect of a specified  track.
 
pascal Rect QTUGetTrackRect(Track theTrack)
 
theTrack                track we are interested in concerning the rect information
 
DESCRIPTION
    QTUMediaTypeInTrack will take a (visual) track and return the track's Rect boundaries that 
    could be used later for various calculations of the visual track geometries. Note that
    this Rect is meaningful with video tracks (and any other tracks that have geometrical
    dimensions, otherwise this function will return a rect with zero values.
*/
 
pascal OSErr QTUGetTrackRect(Track theTrack, Rect *theRect)
{
    OSErr   anErr = noErr;
    Fixed   aTrackHeight;
    Fixed   aTrackWidth;
 
    theRect->top = 0; theRect->left = 0; theRect->bottom = 0; theRect->right = 0;
    
    DebugAssert(theTrack != NULL);
    if(theTrack == NULL)
        return invalidTrack;
        
    GetTrackDimensions(theTrack, &aTrackHeight, &aTrackWidth);
    anErr = GetMoviesError(); DebugAssert(anErr == noErr);
    if(anErr != noErr)
        return anErr;
    
    theRect->right = Fix2Long(aTrackWidth);
    theRect->bottom = Fix2Long(aTrackHeight);
    
    return anErr;
}
 
 
/*______________________________________________________________________
    QTUGetVideoMediaPixelDepth - Return the pixel depth of the video media (sample).
 
pascal short QTUGetVideoMediaPixelDepth(Media theMedia,short index)
 
theMedia            visual media we want to test concerning pixel depths
index               index into the media sample we are interested in
 
DESCRIPTION
    QTUGetVideoMediaPixelDepth will take a specified video media and an index into the media 
    samples, and look up the pixel depth for the video sample.
*/
 
pascal short QTUGetVideoMediaPixelDepth(Media theMedia,short index)
{
    OSErr                               anErr = noErr;
    short                               aPixelDepth;
    SampleDescriptionHandle anImageDesc = NULL;
    OSType                              mediaType;
    
    DebugAssert(theMedia != NULL);
    DebugAssert(index > 0);
    
    // Test if we are indeed dealing with video media.
    GetMediaHandlerDescription(theMedia, &mediaType, NULL, NULL);
    if(mediaType != VideoMediaType)
        return 0;
        
    anImageDesc = (SampleDescriptionHandle)NewHandle(sizeof(Handle)); DebugAssert(anImageDesc != NULL);
    if(anImageDesc == NULL)
        return 0;
    
    GetMediaSampleDescription(theMedia, index, anImageDesc);
    anErr = GetMoviesError(); DebugAssert(anErr == noErr);
    
    aPixelDepth = (* (ImageDescriptionHandle)anImageDesc)->depth;
    
    DisposeHandle((Handle)anImageDesc);
    
    return aPixelDepth;
}
 
 
/*______________________________________________________________________
    QTUCountMediaSamples - Count the amount of known media samples in a movie.
 
pascal long QTUCountMediaSamples(Movie theMovie, OSType theMediaType)
 
theMovie                    the movie with the track(tracks).   
theMediaType            the type of media we are interested in (video, sound and so on)
 
DESCRIPTION
    QTUCountMediaSamples will take a specified movie and a media type, and calculate the amount 
    of samples of this particular type. It could be used to find the total amount of video frames in a 
    movie, or sound samples and so on.
 
    Note that if the movie is long, it will take a long time to go through all the samples, especially 
    in the case of sound samples.
 
EXAMPLE:
    nFrames = QTUCountMediaSamples(aSourceMovie, VideoMediaType); 
 
ISSUES
    This function could be modified to count other types of samples by changing the flags definitions 
    (nextTimeSyncSample for key frames and so on).
*/
 
pascal long QTUCountMediaSamples(Movie theMovie, OSType theMediaType)
{
    long                numFrames = 0;
    short           flags = nextTimeMediaSample + nextTimeEdgeOK;
    TimeValue       aDuration = 0;
    TimeValue   theTime = 0;
    
    GetMovieNextInterestingTime(theMovie, flags, 1, &theMediaType, theTime, 0, &theTime, &aDuration);
    if(theTime == -1) return numFrames;
 
    flags = nextTimeMediaSample; // Don't include the  nudge after the first interesting time.
    
    while(theTime != -1)  // When the returned time equals -1, then there were no more interesting times.
    {
        numFrames++;
        GetMovieNextInterestingTime(theMovie, flags, 1, &theMediaType, theTime, 0, &theTime, &aDuration);
    }
    
    return numFrames;
}
 
 
/*______________________________________________________________________
    QTUGetDurationOfFirstMovieSample - Return the time value of the first sample of a certain 
    media type.
 
pascal TimeValue  QTUGetDurationOfFirstMovieSample(Movie theMovie, OSType theMediaType)
 
theMovie                        the movie with the media track
theMediaType                specified media type (VideoMediaType, SoundMediaType and so on)
 
DESCRIPTION
    QTUGetDurationOfFirstMovieSample returns the duration of the first sample of a certain media 
    in the movie. If there is no such sample, the duration is 0.
 
    This function could be used in known cases where all the samples are assumed to be of the same 
    duration. For instance in such cases the frame count could be calculated as:
 
    framecount =
             GetMovieDuration(theMovie)/QTUGetDurationofFirstMovieSample(theMovie, VideoMediaType);
 
*/
 
pascal TimeValue  QTUGetDurationOfFirstMovieSample(Movie theMovie, OSType theMediaType)
{
    OSErr           anErr = noErr;
    TimeValue       interestingDuration = 0;
    short           timeFlags = nextTimeMediaSample+nextTimeEdgeOK;
 
    GetMovieNextInterestingTime(theMovie, timeFlags, (TimeValue)1, &theMediaType, 0, 
                                                    fixed1, NULL, &interestingDuration);
    anErr = GetMoviesError(); DebugAssert(anErr == noErr);
 
    return interestingDuration;
}
 
 
/*______________________________________________________________________
    QTUCountMaxSoundRate - Calculate the max sound data rate of a possible sound track
   in the movie.
 
pascal OSErr QTUCountMaxSoundRate(Movie theMovie,long *theMaxSoundRate)
 
theMovie                        the movie with the sound track(tracks). 
theMaxSoundRate         the final returned value
 
DESCRIPTION
    QTUCountMaxSoundRate (taken from the ConvertToMovieJr file) is a simple function that tries
    to figure out the maximum sound track rate. This is done by looking at all of the sound tracks
    in the source movie, and using the one with the highest sample rate (11khz, 22khz and so on),
 
    This number could then be used for calculating the maximum data rate by extracting the sound rate and 
    this way we get a loose estimation how much is left for the video data rate.
    
    This is just an approximation, and a better function should take into account non-overlapping
    sound tracks, stereo sound data rates, compressed sound tracks and so on.
*/
 
pascal OSErr QTUCountMaxSoundRate(Movie theMovie,long *theMaxSoundRate)
{
    OSErr   anErr = noErr;
    short   index, trackCount;
    
    DebugAssert(theMovie != NULL); if(theMovie == NULL) return invalidMovie;
    *theMaxSoundRate = 0; // just for security we place this value in here
    
    trackCount = GetMovieTrackCount(theMovie);
    
    for(index = 1; index <= trackCount; index++)
    {
        OSType  aTrackType;
        Track       aTrack = NULL;
        Media       aMedia = NULL;
        
        aTrack = GetMovieIndTrack(theMovie, index);  DebugAssert(aTrack != NULL);
        aMedia = GetTrackMedia(aTrack); DebugAssert(aMedia != NULL);
        anErr = GetMoviesError();  DebugAssert(anErr == noErr);
        if(anErr != noErr) return anErr;
        
        GetMediaHandlerDescription(aMedia, &aTrackType, 0, 0);
        if(aTrackType == SoundMediaType)
        {
            long aRate;
            SampleDescriptionHandle aDesc = NULL;
            
            aDesc = (SampleDescriptionHandle)NewHandle(sizeof(SampleDescription)); DebugAssert(aDesc != NULL);
            GetMediaSampleDescription(aMedia, 1, aDesc);
            anErr = GetMoviesError(); DebugAssert(anErr == noErr);
            if(anErr != noErr)
            {
                DisposeHandle((Handle)aDesc);
                continue;
            }
            
            aRate = (*(SoundDescriptionHandle)aDesc)->sampleRate >> 16;
            if(aRate > *theMaxSoundRate)
                *theMaxSoundRate = aRate;
        }
    }
    return anErr;
}
 
 
 
/*______________________________________________________________________
    QTUGetMovieFrameCount - Return the amount of frames in the movie based on frame rate estimate.
 
pascal long QTUGetMovieFrameCount(Movie theMovie, long theFrameRate)
 
theMovie                    the movie we want to calculate the frame count for.         
theFrameRate            the expected frame rate of the movie
 
DESCRIPTION
    QTUGetMovieFrameCount is a simple operation that takes into account the duration of the movie,
    the time scale and a suggested frame rate, and based on this will calculate the 
    amount of frames needed in the movie. We assume that the frame rate will be uniform in the movie.
*/
 
pascal long QTUGetMovieFrameCount(Movie theMovie, long theFrameRate)
{
    long        frameCount, duration, timescale;
    float   exactFrames;
    
    DebugAssert(theMovie != NULL); if(theMovie == NULL) return invalidMovie;
 
    duration        = GetMovieDuration(theMovie);
    timescale       = GetMovieTimeScale(theMovie);
    exactFrames = (float)duration * theFrameRate;
    
    frameCount  = exactFrames / timescale / 65536;
    
    if(frameCount == 0)
        frameCount = 1;         // we got to have at least one frame
    
    return frameCount;
}
 
 
/*______________________________________________________________________
    QTUCopySoundTracks - Copy any sound track from the source movie to the destination movie.
 
pascal OSErr QTUCopySoundTracks(Movie theSrcMovie, Movie theDestMovie)
 
aSourceMovie                movie from which to copy the sound tracks           
aDestinationMovie           movie to which we will copy the sound tracks.   
 
DESCRIPTION
    QTUCopySoundTracks will take any sound tracks from the source movie, and copy these over to the
    destination movie. The destination movie might have no sound track, or then these tracks are 
    added to the existing sound tracks.
*/
 
pascal OSErr QTUCopySoundTracks(Movie theSrcMovie, Movie theDestMovie)
{
    OSErr   anErr = noErr;
    long        trackCount, index;
    
    DebugAssert(theSrcMovie != NULL); if(theSrcMovie == NULL) return invalidMovie;
    DebugAssert(theDestMovie != NULL); if(theDestMovie == NULL) return invalidMovie;
 
    trackCount = GetMovieTrackCount(theSrcMovie);
    
    // Loop through each track, look for sound tracks.
    for(index = 1; index <= trackCount; index++)
    {
        OSType aTrackType;
        Track   aSrcTrack, aDestTrack;
        Media   aSrcMedia, aDestMedia;
        
        aSrcTrack = GetMovieIndTrack(theSrcMovie, index);           // get next track and media
        aSrcMedia = GetTrackMedia(aSrcTrack);
        anErr = GetMoviesError(); DebugAssert(anErr == noErr);
        if(anErr != noErr) return anErr;
        
        // try to find sound tracks/media
        GetMediaHandlerDescription(aSrcMedia, &aTrackType, 0, 0);
        if(aTrackType == SoundMediaType)
        {
            // Create the track for the sound media.
            aDestTrack = NewMovieTrack(theDestMovie, 0, 0, GetTrackVolume(aSrcTrack));
            anErr = GetMoviesError(); DebugAssert(anErr == noErr);
            if(anErr != noErr) return anErr;
            
            // Create a media for the sound track and prepare this media for editing.
            aDestMedia = NewTrackMedia(aDestTrack, SoundMediaType, GetMediaTimeScale(aSrcMedia), 0, 0);
            anErr = GetMoviesError(); DebugAssert(anErr == noErr);
            if(anErr != noErr) return anErr;
            
            anErr = BeginMediaEdits(aDestMedia); DebugAssert(anErr == noErr);
            if(anErr != noErr) return anErr;
            
            // Insert the new track into the destination movie starting at time zero and
            // lasting for the entire duration of the movie.
            InsertTrackSegment(aSrcTrack, aDestTrack, 0 , GetTrackDuration(aSrcTrack), 0);
            anErr = GetMoviesError(); DebugAssert(anErr == noErr);
            if(anErr != noErr) return anErr;
            
            // We've done editing the media
            EndMediaEdits(aDestMedia);
        }
    }
    return anErr;
}
 
 
 
/*______________________________________________________________________
    QTUPrintMoviePICT - Print the existing movie frame pict.
 
pascal Boolean QTUPrintMoviePICTr(Movie theMovie, short x, short y,  long PICTUsed)
 
theMovie                    movie that has the poster       
x,y                         starting point coordinates where to place the poster on paper
 
DESCRIPTION
    QTUPrintMoviePICT is a simple function showing how to print movie posters. 
 
ISSUES
    Note that in a real application we should put the PrStlDialog code into the Print SetupÉ menu
    function. The reason it's inside this function is that we use this code for quick testing of 
    printing.
*/
 
pascal OSErr QTUPrintMoviePICT(Movie theMovie, short x, short y, long PICTUsed)
{
    OSErr       anErr = noErr;
    PicHandle   aPictHandle = NULL;
    THPrint     aTHPrint = NULL;
    GrafPtr     aSavedPort;
    TPPrPort    aPrintPort;
    Boolean     aResult;
    Boolean     isPrinting = false;
    Rect            aPictRect;
    
    DebugAssert(theMovie != NULL); if(theMovie == NULL) return invalidMovie;
    
    GetPort(&aSavedPort);
 
// Get the PICT to be printed, either the poster pict or the current frame pict.
    switch(PICTUsed)
    {
        case kPrintFrame:
            aPictHandle = GetMoviePict(theMovie, GetMovieTime(theMovie, 0L));
            break;
            
 
        case kPrintPoster:
            aPictHandle = GetMoviePosterPict(theMovie); 
            break;
 
        default:
            DebugAssert("Should not happen, incorrect constant used"); goto Closure;
    }
 
    if(aPictHandle == NULL) goto Closure;
 
 
// Get the Print record.
    aTHPrint = (THPrint) NewHandleClear(sizeof(TPrint)); DebugAssert(aTHPrint != NULL);
    if(aTHPrint == NULL) goto Closure;
 
    PrOpen(); isPrinting = true;
    anErr = PrError(); DebugAssert(anErr == noErr);
        if(anErr != noErr) goto Closure;
 
    PrintDefault(aTHPrint);
 
// Move this to Print SetupÉif you want to make this look really cool.  
    aResult = PrStlDialog(aTHPrint); DebugAssert(aResult == true);
    if(!aResult) goto Closure;
    
    aResult = PrJobDialog(aTHPrint); DebugAssert(aResult == true);
    if(!aResult) goto Closure;
    
    aPrintPort = PrOpenDoc(aTHPrint, NULL, NULL); DebugAssert(aPrintPort != NULL);
    PrOpenPage(aPrintPort, NULL);
    anErr = PrError(); DebugAssert(anErr == noErr);
    if(anErr != noErr) goto Closure;
    
// Print at x,y position
    aPictRect =  (*aPictHandle)->picFrame;
    OffsetRect(&aPictRect, x - aPictRect.left,  y  - aPictRect.top);
    
    DrawPicture(aPictHandle, &aPictRect);
 
// If you want to do additional drawing, do it here.
    
    PrClosePage(aPrintPort);
    PrCloseDoc(aPrintPort);
    anErr = PrError(); DebugAssert(anErr == noErr);
    if(anErr != noErr) goto Closure;
    
    if(( *aTHPrint)->prJob.bJDocLoop == bSpoolLoop)
        PrPicFile(aTHPrint, NULL, NULL, NULL, NULL);
    
// Our closure handling.
Closure:
    SetPort(aSavedPort);
    
    if(isPrinting) PrClose();
    if(aPictHandle) KillPicture(aPictHandle);
    if(aTHPrint) DisposeHandle((Handle)aTHPrint);
    return anErr;
}
 
 
/*______________________________________________________________________
    QTUCalculateMovieMemorySize - Calculate how much memory a movie takes in the app heap.
 
pascal OSErr QTUCalculateMovieMemorySize(Movie theMovie, long *theSize)
 
theMovie                        movie we want to know the size of in the current application heap
theSize                         pointer to a long that will contain the movie size in bytes
 
DESCRIPTION
    QTUCalculateMovieMemorySize will return the amount of bytes it is allocating as a handle
    in the current application heap, if there's not enough space for a temp handle, or if anything
    else fails, the function will return 0L in theSize (and the OSErr).
 
ISSUES
    Note that possible movie controllers associated with the movie and other constructs will eat up
    memory. What you could do is to do a MacsBug HT before the movie or movies are opened, 
    check the amount of free space, and HT after the movies are opened, figure out the movie sizes 
    using the function below, and calculate the delta from these values.
*/
 
pascal OSErr QTUCalculateMovieMemorySize(Movie theMovie, long *theSize)
{
    OSErr   anErr = noErr;
    Handle  tempHandle = NULL;
 
    DebugAssert(theMovie != NULL); if(theMovie == NULL) return invalidMovie;
 
    *theSize = 0L;
 
    tempHandle = NewHandle(sizeof(Movie)); DebugAssert(tempHandle != NULL);
    anErr = MemError(); DebugAssert(anErr == noErr);
    if(anErr != noErr || tempHandle == NULL) goto Closure;
 
    anErr =  PutMovieIntoHandle(theMovie, tempHandle); DebugAssert(anErr == noErr);
 
    if(anErr == noErr)  
            *theSize = GetHandleSize(tempHandle);
 
Closure:
    if(tempHandle != NULL) DisposeHandle(tempHandle);
 
    return anErr;
}
 
 
/*______________________________________________________________________
    QTULoadWholeMovieToRAM - Load the entire active segment (movie) into RAM
 
pascal OSErr    QTULoadWholeMovieToRAM(Movie theMovie)
 
theMovie                        movie we want to know the size of in the current application heap.
 
DESCRIPTION
    QTULoadWholeMovieToRAM is an example of how to load movie information into RAM. In
    this case we will load the entire movie, or in other words all the active segments.
 
ISSUES
    The most likely error returned from this function is due to lack of memory.  You could 
    also fine tune this function by loading partial data based on time or track specifications.
 
    Loading whole movies is OK if the movies are small, have few tracks with little info (text
    tracks, music tracks and so on), there's a certain performance need  why it makes sense 
    to keep the movie in RAM (looping, other issues), and in general if you know why it's needed.
*/
 
pascal OSErr    QTULoadWholeMovieToRAM(Movie theMovie)
{
    OSErr anErr = noErr;
 
    DebugAssert(theMovie != NULL); if(theMovie == NULL) return invalidMovie;
 
    GoToBeginningOfMovie(theMovie);
    anErr = GetMoviesError(); DebugAssert(anErr == noErr);
    if (anErr != noErr) return anErr;
 
    anErr = LoadMovieIntoRam(theMovie, GetMovieTime(theMovie, NULL), GetMovieDuration(theMovie), 0);
 
    return anErr;
}
 
 
/*______________________________________________________________________
    QTUPlayMovieSound - Play the movie sound track using the Sound Manager.
 
pascal OSErr QTUPlayMovieSound(Movie theMovie)
 
theMovie                        movie wherefrom we extract the sound resource
 
DESCRIPTION
    QTUPlayMovieSound is an example of how to extract the 'snd ' sound resource from the 
    first sound track in a movie, and play this track back using the Sound Manager. This
    sound resource could also be retrieved, or otherwised used in other instances. Note that
    this function is more of an example of how to retrieve sound from a movie; you might
    want to control the start point, duration, and sound track extracted.
 
*/
 
pascal OSErr QTUPlayMovieSound(Movie theMovie)
{
    OSErr   anErr = noErr;
    Handle  tempHandle  = NewHandle(1);
    
    DebugAssert(theMovie != NULL); if(theMovie == NULL) return invalidMovie;
 
    // Extract first sound track.
    anErr = PutMovieIntoTypedHandle(theMovie, (Track)0, 'snd ', tempHandle, 0, GetMovieDuration(theMovie),
                                                        0, (ComponentInstance)0); DebugAssert(anErr == noErr);
    if(anErr != noErr) goto Closure;
    anErr = MemError(); DebugAssert(anErr == noErr);
    if(anErr != noErr) goto Closure;
 
    // Play sound resource async.
    anErr = SndPlay(0L, (SndListHandle)tempHandle, TRUE);    DebugAssert(anErr == noErr);
 
Closure:
    if(tempHandle) DisposeHandle(tempHandle);
 
    return anErr;
}
 
 
/*______________________________________________________________________
    QTUDrawVideoFrameAtTime - Display a movie video frame at specified movie time.
 
pascal OSErr QTUDrawVideoFrameAtTime(Movie theMovie, TimeValue atTime)
 
theMovie                        movie we are using
atTime                          time value in the movie for the video frame we want to display
 
DESCRIPTION
    QTUDrawVideoFrameAtTime will display a specific video sample (or frame) at a specified time.
    In other words if we want to draw a frame at time point 600, the nearest video frame
    corresponding to this time value will be shown. 
 
    We assume that the movie is properly set, and is using a correct portRect or GWorld.
 
*/
 
pascal OSErr QTUDrawVideoFrameAtTime(Movie theMovie, TimeValue atTime)
{
    TimeValue totalTime;
    
    OSErr anErr = noErr;
    DebugAssert(theMovie != NULL); if(theMovie == NULL) return paramErr;
    
    totalTime = GetMovieDuration(theMovie);
    if(atTime > totalTime) return paramErr;
    
    if(atTime == 0L) {
        GoToBeginningOfMovie(theMovie); anErr = GetMoviesError(); DebugAssert(anErr == noErr);
        if(anErr) goto Closure;
    }
    else {
        SetMovieTimeValue(theMovie, atTime); anErr = GetMoviesError(); DebugAssert(anErr == noErr);
        if(anErr) goto Closure;
    }
    
    anErr = UpdateMovie(theMovie);  DebugAssert(anErr == noErr);
    if(anErr) goto Closure;
    
    MoviesTask(theMovie, 0L); anErr = GetMoviesError(); DebugAssert(anErr == noErr);
 
Closure:    
    return anErr;
}
 
 
 
/*______________________________________________________________________
    QTUScrollToNextVideoSample -  Scroll offscreen from one video sample to the next.
 
pascal OSErr QTUScrollToNextVideoSample(Movie theMovie, TimeValue fromTimePoint, TimeValue toTimePoint)
 
fromTimePoint                       starting time value of the first video sample (frame)
toTimePoint                         end time value for the second video sample (frame)
 
DESCRIPTION
    QTUScrollToNextVideoSample will scroll from one video sample to the other one using offscreen
    GWorlds where the effect is created. 
 
    We assume that the movie is properly set, and that the movie will use a proper portRect or GWorld.
 
CREDITS
    Presto Studios for the core idea and some of the code below.
 
*/
 
pascal OSErr QTUScrollToNextVideoSample(Movie theMovie, TimeValue fromTimePoint, TimeValue toTimePoint) 
{
    OSErr               anErr = noErr;
    GWorldPtr           frameGWorld1 = NULL;
    GWorldPtr           frameGWorld2 = NULL;
    PixMapHandle    pixMap1 = NULL;
    PixMapHandle    pixMap2 = NULL;
    CGrafPtr            aSavedPort, moviePort;
    GDHandle            aSavedGDevice, movieGDevice;
    CTabHandle      colorTable;
    short               screenDepth = 0;
    short               screenSize = 0;
    Rect                    movieRect, sourceRect, destinationRect;
    RgnHandle           scrollRegion = NULL;
    RgnHandle           clipRegion = NULL;
    short               nSteps;
        
    DebugAssert(theMovie != NULL); if(theMovie == NULL) goto Closure;
    
    //¥ Store away current portrect and Gdevice, get pixel sizes and color table for GWorld creation purposes.
    GetGWorld(&aSavedPort, &aSavedGDevice);
    
    GetMovieGWorld(theMovie, &moviePort, &movieGDevice);
    screenDepth = (**(**aSavedGDevice).gdPMap).pixelSize;
    colorTable = (**(**aSavedGDevice).gdPMap).pmTable;
    
    //¥ Adjust the movie box.
     GetMovieBox(theMovie, &movieRect);  // If you want to offset by 10,10: OffsetRect(&movieRect, 10 -movieRect.left, 10 - movieRect.top);
     SetMovieBox(theMovie, &movieRect);
    
    //¥ Create two GWorlds for dual screen writing and possible scrolling transition effects. Lock down pixmaps.
    anErr = NewGWorld(&frameGWorld1, screenDepth, &movieRect, colorTable, NULL, 0); DebugAssert(anErr == noErr);
    if(anErr != noErr) goto Closure;
    anErr = NewGWorld(&frameGWorld2, screenDepth, &movieRect, colorTable, NULL, 0); DebugAssert(anErr == noErr);
    if(anErr != noErr) goto Closure;
    
    pixMap1 = GetGWorldPixMap(frameGWorld1);  if(!LockPixels(pixMap1)) goto Closure;
    pixMap2 = GetGWorldPixMap(frameGWorld2);  if(!LockPixels(pixMap2)) goto Closure;
    
    //¥ Draw first video sample (frame) to GWorld number 1.
    SetMovieGWorld(theMovie, frameGWorld1, GetGWorldDevice(frameGWorld1));
    SetMovieTimeValue(theMovie, fromTimePoint);
    UpdateMovie(theMovie); MoviesTask(theMovie, 0);
    
    //¥ Draw second video sample (frame) to GWorld number 2.
    SetMovieGWorld(theMovie, frameGWorld2, GetGWorldDevice(frameGWorld2));
    SetMovieTimeValue(theMovie, toTimePoint); 
    UpdateMovie(theMovie); MoviesTask(theMovie, 0);
    
    //¥ Create scroll region and store away the current clip region.
    scrollRegion = NewRgn(); DebugAssert(scrollRegion != NULL);
    if(scrollRegion == NULL) goto Closure;
    
    clipRegion = NewRgn(); DebugAssert(clipRegion != NULL);
    if(clipRegion == NULL) goto Closure;
    GetClip(clipRegion); ClipRect(&movieRect);
    
    //¥ Create the scroll effect.
    screenSize = movieRect.right - movieRect.left;
    
    for(nSteps = 10; nSteps <= screenSize; nSteps += 10)  {
        SetGWorld( frameGWorld1, NULL);
        
        ScrollRect(&movieRect, -10, 0, scrollRegion);
        SetRect(&sourceRect, movieRect.left, movieRect.top, 
                movieRect.left + nSteps, movieRect.bottom);
        SetRect(&destinationRect, movieRect.right - nSteps,
                movieRect.top, movieRect.right, movieRect.bottom);
        
        CopyBits( (BitMap *) *pixMap2, (BitMap *) *pixMap1, &sourceRect, &destinationRect,
                    srcCopy, NULL );                // blit from frameGWorld2 to frameGWorld1
        DebugAssert(QDError() == noErr);
        
        SetGWorld(aSavedPort, aSavedGDevice);
        CopyBits( (BitMap *) *pixMap1, (BitMap *) &aSavedPort->portPixMap, &movieRect,
                    &movieRect, srcCopy, NULL );    // blit from frameGWorld1 to screen pixmap
        DebugAssert(QDError() == noErr);
    }
 
    //¥ Unlock pixels, restore the original clip region.    
    UnlockPixels(pixMap1); UnlockPixels(pixMap2);
    SetClip(clipRegion);
    
    //¥ Closure. Clean up if we have handles.
Closure:    
    if(frameGWorld1 != NULL)        DisposeGWorld(frameGWorld1);
    if(frameGWorld2 != NULL)        DisposeGWorld(frameGWorld2);
    if(scrollRegion != NULL)            DisposeRgn(scrollRegion);
    if(clipRegion != NULL)              DisposeRgn(clipRegion);
 
    SetMovieGWorld(theMovie, moviePort, movieGDevice);
    SetGWorld(aSavedPort, aSavedGDevice);   
 
    return anErr;
}
 
 
/*______________________________________________________________________
    QTUGetStartPointOfFirstVideoSample -  Get time value of first sample in the movie.
 
TimeValue QTUGetStartPointOfFirstVideoSample(Movie theMovie) 
 
theMovie                    movie we are interested in
startPoint                  will contain the value of the start point, if the function fails it will contain
                                -1.
 
DESCRIPTION
    QTUGetStartPointOfFirstVideoSample will return the time value of the first video sample found in the
    movie in the startPoint parameter. If the function fails, startPoint will contain -1 and the OSErr is 
    also returned.
*/
 
pascal OSErr QTUGetStartPointOfFirstVideoSample(Movie theMovie, TimeValue *startPoint) 
{
    OSErr   anErr = noErr;
    OSType  media = VideoMediaType;
    
    *startPoint = -1;
 
    GetMovieNextInterestingTime(theMovie, nextTimeMediaSample+nextTimeEdgeOK, (TimeValue)1, &media, 0, 
                                                    fixed1, startPoint, NULL);
    anErr = GetMoviesError(); DebugAssert(anErr == noErr);
 
    return anErr;
}
 
 
// IMAGE COMPRESSION MANAGER
 
/*______________________________________________________________________
    QTUHasCodecLossLessQuality - Test if a specific codec has a lossless mode in a specific bit depth.
 
Boolean QTUHasCodecLossLessQuality(CodecType theCodec, short thePixelDepth)
 
theCodec                        specifies the Codec Type ('jpeg, 'rle ' and so on).
thePixelDepth               specifies the bit depth (8, 24, 30 and so on). See NIM:QuickTime, page 
                                    3-70 for more details.
 
DESCRIPTION
    QTUHasCodecLossLessQuality will test if a specific codec has a lossless spatial compression 
    quality at a certain bit depth. Note that we are not testing the temporal compression qualities.
 
EXAMPLE OF USE:
    if(QTUHasCodecLossLessQuality('jpeg', 32))  
        printf("JPEG has lossless spatial compression\n");
    else
        printf("JPEG has NOT lossless spatial compression\n");
 
*/
 
Boolean QTUHasCodecLossLessQuality(CodecType theCodec, short thePixelDepth)
{
    OSErr   anErr = noErr;
    CodecQ  aSpatialQuality = codecLosslessQuality;
 
    anErr = GetCompressionTime(NULL, NULL, thePixelDepth, theCodec, anyCodec, &aSpatialQuality,
                        NULL, NULL); DebugAssert(anErr == noErr);
    
    if(aSpatialQuality == codecLosslessQuality) // still the same?
        return true;
    else
        return false;
}
 
 
 
// MOVIE CONTROLLER FUNCTIONS
 
 
/*______________________________________________________________________
    QTUPlayMovieWithMC - Play a specific movie when using movie controllers.
 
pascal OSErr QTUPlayMovieWithMC( MovieController mc)
 
mc                      specified movie controller to be used
 
DESCRIPTION
    Playmovie will start a movie using a moviecontroller and a specified movie. Note that it also  
    does a preroll of the movie for performance reasons. 
 
*/
 
pascal OSErr QTUPlayMovieWithMC(MovieController mc)
{
// Play normal speed forward, taking into account the possibility 
// of a movie with a nonstandard PreferredRate.
    OSErr   anErr = noErr;
    Fixed   aRate;
    Movie   aMovie;
 
    aMovie = MCGetMovie(mc);
 
    aRate= GetMoviePreferredRate(aMovie);
    anErr = QTUPrerollMovie(aMovie);  // Important: Preroll the movie here.
    DebugAssert(anErr == noErr);
 
    if(anErr == noErr)
    {
        MCDoAction(mc, mcActionPlay, (void *)aRate);  // note last value
    }
    
    return anErr;
}
 
 
/*______________________________________________________________________
    QTUDoIgnoreMCDrags - Disable Drag and Drop facilities of the movie controller environment.
 
pascal OSErr  QTUDoIgnoreMCDrags(MovieController  mc)
 
mc                      is the specified moviecontroller to be used
 
DESCRIPTION
    QTUDoIgnoreMCDrags will ensure that the Drag and Drop functionality is not handled within 
    the movie specified by the movie controller.
 
ISSUES
    Note that this is a workaround in QT 2.0 (test for QT 2.0 or higher if you want to use
    the drag-and-drop support in QT), and this function might not be needed in later QT versions.
*/
 
pascal OSErr QTUDoIgnoreMCDrags(MovieController  mc)
{
   OSErr        anErr = noErr;
   GWorldPtr  aTempGWorld;
   Rect             aTempRect = {0, 0, 20, 20};
   CGrafPtr     aPort;
 
   // First create a 1-bit small 20x20 offscreen.
   anErr = NewGWorld( &aTempGWorld, 1, &aTempRect, NULL, NULL, 0L );
   DebugAssert(anErr == noErr);
 
   if (anErr != noErr)
   {
        aPort = MCGetControllerPort(mc);                                    // get the current port
        MCSetControllerPort(mc, (CGrafPtr)aTempGWorld );        // set mc port to new offscreen
        MCDoAction(mc, mcActionSetDragEnabled, (void *)false); // don't want dragging
        MCSetControllerPort(mc, aPort);                                     // restore mc port
        DisposeGWorld(aTempGWorld);                                         // dispose offscreen
   }
   return anErr;
}
 
 
/*______________________________________________________________________
    QTUPointInMC - Test if a point is placed in the movie controller rect area or not.
 
pascal Boolean QTUPointInMC(MovieController mc, WindowRef theWindow, Point where)
 
mc                          is the specified moviecontroller to be used
theWindow               window used for testing for the hit point
where                       hit point
 
DESCRIPTION
    QTUPointInMC is a simple test to check where the mouse was clicked inside the window
    with a movie controller, returns true of the mouse click was inside the movie controller
    rect. See Peter Hoddie's article in develop# 18 for more details (code is from him as well).
*/
 
pascal Boolean QTUPointInMC(MovieController mc, WindowRef theWindow, Point where)
{
    RgnHandle       aRegion;
    Boolean         result = false;
    
    aRegion = MCGetWindowRgn(mc, theWindow);
    DebugAssert(aRegion != NULL);
    
    if(aRegion != NULL)
    {
        result = PtInRgn(where, aRegion);
        DisposeRgn(aRegion);
    }
    
    return result;
}
 
 
/*______________________________________________________________________
    QTUSelectAllMovie - Select the whole movie time duration with the controller.
 
pascal OSErr QTUSelectAllMovie(MovieController mc)
 
mc                      is the specified moviecontroller to be used
 
DESCRIPTION
    QTUSelectAllMovie is an example how to select the whole movie duration using the movie
    controller, this function could be used for Select All menu entries and similar cases.
*/
 
pascal OSErr QTUSelectAllMovie(MovieController mc)
{
    OSErr           anErr = noErr;
    TimeRecord  aTimeRecord;
    Movie           aMovie = NULL;
    
    DebugAssert(mc != NULL);
    if(mc == NULL) return paramErr;
    
    aMovie = MCGetMovie(mc); DebugAssert(aMovie != NULL);
    if(aMovie == NULL) return paramErr;
    
    aTimeRecord.value.hi = 0;
    aTimeRecord.value.lo = 0;
    aTimeRecord.base = 0;
    
    aTimeRecord.scale = GetMovieTimeScale(aMovie);
    anErr = GetMoviesError(); DebugAssert(anErr == noErr);
    if(anErr != noErr) return anErr;
    
    anErr = MCDoAction(mc, mcActionSetSelectionBegin, &aTimeRecord);
    DebugAssert(anErr == noErr);
    if(anErr != noErr) return anErr;
    
    aTimeRecord.value.lo = GetMovieDuration(aMovie);
    anErr = GetMoviesError(); DebugAssert(anErr == noErr);
    if(anErr != noErr) return anErr;
    
    anErr = MCDoAction(mc, mcActionSetSelectionDuration, &aTimeRecord);
    DebugAssert(anErr == noErr);
    
    return anErr;
}
 
 
/*______________________________________________________________________
     QTUResizeMCActionFilter - Example of a movie controller filter that will resize the window 
    where the movie is placed when the controllers themself change. 
 
pascal Boolean QTUResizeMCActionFilter(MovieController mc, short action, void *params, long refCon)
 
mc                          specified moviecontroller to be used.
action                      the action for the mc action filter
params                      parameters passed with the action
refCon                      additional long word that could be used for all kinds of purposes
 
DESCRIPTION
    QTUResizeMCActionFilter is an example of how to create a nice movie controller filter that 
    will handle resizing of the window with the movie, and this will happen every time the controllers
    themselves change. It's also an example of how to write other kinds of movie controller filters.
*/
 
pascal Boolean QTUResizeMCActionFilter(MovieController mc, short action, void* /*params*/ , long refCon)
{
    Rect aMovieBounds;
    
    switch(action)
    {
        case mcActionControllerSizeChanged:
            MCGetControllerBoundsRect(mc, &aMovieBounds);
            SizeWindow((WindowPtr) refCon, aMovieBounds.right - aMovieBounds.left,
                                aMovieBounds.bottom - aMovieBounds.top, true);
            break;
    }
        return false;
}
 
 
/*______________________________________________________________________
    QTUResizeMCWindow - Resize a window to either normal size, double size or  half of the movie rect size.
 
pascal Boolean QTUResizeMCWindow(MovieController mc, WindowPtr theWindow, long theMovieSize, Rect originalSize)
 
mc                              specified moviecontroller to be used
theWindow                   window that will be resized
theMovieSize                constant that defines what default size we are interested in,  kNormalSize, kHalfSize, kDoubleSize
originalSize                    the original size of the movie, we need to keep track of this one in order to handle the 
                                    ambient new sizes (half, double, normal).
 
DESCRIPTION
    QTUResizeMCWindow is an example of a function how to resize the movie window with the controllers.
    The most common cases is half size, normal size or double size. But nothing hinders to add more sizes
    into this function. Note that if the movie window is doubled, we will get pixel-doubling by the QuickTime
    engine.
*/
 
pascal OSErr QTUResizeMCWindow(MovieController mc, WindowPtr theWindow, long theMovieSize, Rect originalSize)
{
    OSErr       anErr = noErr;
    Rect            aMovieBounds;
    GrafPtr     aSavedPort;
    
    DebugAssert(mc != NULL); if(mc == NULL) return paramErr;
    DebugAssert(theWindow != NULL); if(theWindow == NULL) return paramErr;
    
    GetPort(&aSavedPort);
    SetPort((GrafPtr)theWindow);
    
    aMovieBounds.top = 0; aMovieBounds.left = 0;
 
    switch(theMovieSize)
    {
        case kNormalMovieSize:
                MCSetControllerBoundsRect(mc, &originalSize);
                SizeWindow(theWindow, originalSize.right, originalSize.bottom, true);
            break;
        
        case kHalfMovieSize:
                aMovieBounds.right = (originalSize.right - originalSize.left) / 2;
                aMovieBounds.bottom = (originalSize.bottom - originalSize.top) / 2;
                MCSetControllerBoundsRect(mc, &aMovieBounds);
                SizeWindow(theWindow, aMovieBounds.right, aMovieBounds.bottom, true);
            break;
        
        case kDoubleMovieSize:
                aMovieBounds.right = (originalSize.right - originalSize.left) * 2;
                aMovieBounds.bottom = (originalSize.bottom - originalSize.top) * 2;
                MCSetControllerBoundsRect(mc, &aMovieBounds);
                SizeWindow(theWindow, aMovieBounds.right, aMovieBounds.bottom, true);
            break;
        
        default:
            SetPort(aSavedPort);
            anErr = paramErr;
    }
    
    SetPort(aSavedPort);
    return anErr;
}
 
 
/*______________________________________________________________________
    QTUResizeMCWindow -Change the movie rate using the movie controller. 
 
pascal OSErr QTUMCSetMovieRate(MovieController mc, long theRate)
 
mc                          specified moviecontroller to be used
theRate                     new rate value, we are using specific constants, see the eQTUMovieRates enum
                                in the DTSQTUtilities.h file concerning the values.
 
DESCRIPTION
    QTUMCSetMovieRate will use an existing movie controller and change the rate. This is a very 
    simple function, but we do have a list of constants that shows the various values that could be used 
    (eQTUMovieRates, DTSQTUtilities.h), and also it shows that if the rate changes from 0 to something 
    else, then we need to preroll the movie. The Apple MM Tuner will make sure the movie is prerolled,
    but we can't assume that every Mac has this extension installed, that's why it's still very important
    to preroll.
    
ISSUES
    Note that movies have stored preferred rates, so if you want to compensate for this factor you need
    to read in this value as well before setting a double or half speed value.
*/
 
pascal OSErr QTUMCSetMovieRate(MovieController mc, long theRate)
{
    OSErr   anErr = noErr;
    Fixed   aRate;
    
    DebugAssert(mc != NULL);  
    if(mc == NULL)
    {
        anErr = paramErr; goto Closure;
    }
    
    // Test if the playrate changes from 0 to a non-zero value, if so then preroll the movie.
    MCDoAction(mc, mcActionGetPlayRate, &aRate);
    if( (aRate == 0) && (theRate != 0) )
    {
        anErr = QTUPrerollMovie(MCGetMovie(mc));            // we are using the DTSQTUtilities function
        DebugAssert(anErr == noErr);
        if(anErr != noErr) return anErr;
    }
        
    anErr = MCDoAction(mc, mcActionPlay, (Ptr) theRate); DebugAssert(anErr == noErr);
 
Closure:    
    return anErr;
}
 
// SEQUENCE GRABBER FUNCTIONS
/*______________________________________________________________________
    QTUCreateSequenceGrabber - Create an instance of a sequence grabber for specified window.
 
pascal SeqGrabComponent QTUCreateSequenceGrabber(WindowPtr theWindow)
 
theWindow                   window where the sequence grabber will operate
 
DESCRIPTION
    QTUCreateSequenceGrabber will try to open the default sequence grabber component and 
    make sure this component will work in the GWorld of a specified window. 
 
    If we don't find a suitable sequence grabber, or if we encounter problems, we will return NULL.
*/
 
 
pascal SeqGrabComponent QTUCreateSequenceGrabber(WindowPtr theWindow)
{
    OSErr anErr = noErr;
    SeqGrabComponent s = NULL;
 
    DebugAssert(theWindow != NULL); if(theWindow == NULL) goto Closure;
 
    s = OpenDefaultComponent(SeqGrabComponentType, 0);
 
    if(s) // we got a valid one
    {
        anErr = SGInitialize(s); DebugAssert(anErr == noErr);
        if(anErr != noErr) goto Closure;
 
        anErr = SGSetGWorld(s, (CGrafPtr)theWindow, NULL); DebugAssert(anErr == noErr);
        if(anErr != noErr) goto Closure;
    }
 
    return s;
Closure:
    return NULL;
}
 
/*______________________________________________________________________
    QTUCreateSGGrabChannels - Create SG channels, video and audio.
 
pascal OSErr QTUCreateSGGrabChannels(SeqGrabComponent s, const Rect *theBounds, long theUsage,
                                                                SGChannel *theVideoChannel, SGChannel *theSoundChannel)
 
s                           current active sequence grabber component instance
theBounds               the size of the video channel sequence grabber area
theUsage                any additional flags for the video SG
theVideoChannel pointer to the video channel we will receive
theSoundChannel pointer to the audio channel we will receive
 
 
DESCRIPTION
    QTUCreateSGGrabChannels will create video and audio SG channels (SGChannels) using the specified
    default SG component.
 
ISSUES
    We will terminate whenever we can't properly create  a channel (sound, audio), if you still want to
    retrieve a valid channel (let's say the sound one is OK while the we can't open an audio one), you
    could slightly rewrite this code.
*/
 
pascal OSErr QTUCreateSGGrabChannels(SeqGrabComponent s, const Rect *theBounds, long theUsage,
                                                                SGChannel *theVideoChannel, SGChannel *theSoundChannel)
{
    OSErr   anErr = noErr;
    long        sgUsage = seqGrabPreview;   // default at least this flag   
 
    DebugAssert(s != NULL); if(s == NULL) return badSGChannel;
 
    sgUsage |= theUsage;                        // add any other usage flag info now
 
    // Create Video Channel.
    anErr = SGNewChannel(s, VideoMediaType, theVideoChannel); DebugAssert(anErr == noErr);
    if(anErr != noErr) goto FailureHandling;
    
    anErr = SGSetChannelBounds(*theVideoChannel, theBounds); DebugAssert(anErr == noErr);
    if(anErr != noErr) goto FailureHandling;
 
    anErr = SGSetChannelUsage(*theVideoChannel, sgUsage); DebugAssert(anErr == noErr);
    if(anErr != noErr) goto FailureHandling;
 
    // Create Sound Channel.
    anErr = SGNewChannel(s, SoundMediaType, theSoundChannel); DebugAssert(anErr == noErr);
    if(anErr != noErr) goto FailureHandling;
 
    anErr = SGSetChannelUsage(*theSoundChannel, sgUsage); DebugAssert(anErr == noErr);
    if(anErr != noErr) goto FailureHandling;
 
 
    return anErr;
 
FailureHandling:
    SGDisposeChannel(s, *theVideoChannel); *theVideoChannel = NULL;
    SGDisposeChannel(s, *theSoundChannel); *theSoundChannel = NULL;
 
    return anErr;
}
 
 
/*______________________________________________________________________
    QTUDoesVDIGReceiveVideo - Test if vdig receives a live incoming video signal.
 
pascal Boolean QTUDoesVDIGReceiveVideo(SeqGrabComponent s)
 
s                   our sequence grabber component instance
 
DESCRIPTION
    QTUDoesVDIGReceiveVideo test if the currently active vdig is receiving an incoming, live 
    video signal. We assume that all well behaved vdigs set the digiInSignalLock flag.
 
*/
pascal Boolean QTUDoesVDIGReceiveVideo(SeqGrabComponent s)
{
    OSErr       anErr = noErr;
    long            inputFlags, outFlags;
 
    DebugAssert(s != NULL); if(s == NULL) goto Closure;
 
    anErr = VDGetCurrentFlags(s, &inputFlags, &outFlags); DebugAssert(anErr == noErr);
    if(anErr != noErr) goto Closure;
 
    if(inputFlags & digiInSignalLock)
        return true;
 
Closure:
    return false;
}
 
 
/*______________________________________________________________________
    QTUChangeSGWindowSize - Change window size of the video sequence grabber window.
 
pascal OSErr QTUChangeSGWindowSize(SeqGrabComponent s, SGChannel videoChannel, 
                            WindowPtr theWindow, long width, long height)
 
s                   our sequence grabber component instance
videoChannel    the specified (currently used) video channel
theWindow   window used for the digitizing sequence
width           new width of the digitizer rect
height          new height of the digitizer rect
 
DESCRIPTION
    QTUChangeSGWindowSize shows how to change the window size for the current digitizing sequence
    taking place in the window. This is more of an example function as there might be other issues
    to be taken into account (such as preference settings and similar issues) while changing the
    bounds of the digitizing rect.
 
*/
 
pascal OSErr QTUChangeSGWindowSize(SeqGrabComponent s, SGChannel videoChannel, WindowPtr theWindow, long width, long height)
{
    OSErr                               anErr = noErr;
 
    DebugAssert(theWindow != NULL); if(theWindow == NULL) return paramErr;
    DebugAssert(s != NULL); if(s == NULL) return badSGChannel;
    DebugAssert(videoChannel != NULL); if(videoChannel == NULL) return badSGChannel;
 
    anErr = SGPause(s, TRUE); DebugAssert(anErr == noErr); if(anErr != noErr) goto Closure;
 
    SizeWindow(theWindow, width, height, FALSE);
    
    anErr = SGSetChannelBounds(videoChannel, &theWindow->portRect); DebugAssert(anErr == noErr);
    if(anErr != noErr) goto Closure;
 
    anErr = SGPause(s, FALSE); DebugAssert(anErr == noErr); 
 
Closure:
    return anErr;
}
 
 
 
// COMPONENT FUNCTIONS
 
/*______________________________________________________________________
    QTUDoGetComponent - Get a specific component based on component type and component sub-type.
 
pascal Component QTUDoGetComponent(OSType theComponentType, OSType theSpecificComponent)
 
theComponentType                    the component type we are interested in
theSpecificComponent                the specific component sub-type we are interested int
 
DESCRIPTION
    QTUDoGetComponent will get a specific component based on the component type and sub-type.  We have 
    special code for particular components (for instance movieImporttype and movieExporttype), so
    if we specify such types, the function will narrow down the search further for the right components.
    
    The specificComponent is just the special component we want to search for, if the component type is 
    NULL, then the Specific component is the one and only we are interested in. Note that we don't care 
    about the manufacturer information in this function. 
    
    If we don't find a suitable component we will return NULL.
*/
 
pascal Component QTUDoGetComponent(OSType theComponentType, OSType theSpecificComponent)
{
    ComponentDescription    aCD;
    Component                   aComponent = NULL;
    
    aCD.componentType = theComponentType;
    aCD.componentSubType = theSpecificComponent;
    aCD.componentManufacturer = 0;
    
    // The following code is inserted for special handling of some known cases.
    if(theComponentType == MovieImportType)
    {
        aCD.componentFlags = canMovieImportFiles;
        aCD.componentFlagsMask = canMovieImportFiles;
    }
    else if(theComponentType == MovieExportType)
    {
        aCD.componentFlags = canMovieExportFiles;
        aCD.componentFlagsMask = canMovieExportFiles;
    }
 
    // OK, get the component.
    aComponent = FindNextComponent((Component)0, &aCD);
    
    return aComponent;
}
 
 
/*______________________________________________________________________
    QTUHasComponentType -Query for a specific component based on component type and 
    component sub-type.
 
pascal Boolean QTUHasComponentType(OSType theComponentType, OSType theSpecificComponent)
 
theComponentType                    the component type we are interested in
theSpecificComponent                the specific component sub-type we are interested int
 
DESCRIPTION
    QTUDoGetComponent will query for a specific component based on the component type and sub-type.  
    We have special code for particular components (for instance movieImporttype and movieExporttype),
    so if we query for such types, the function will narrow down the search further for the right 
    components.
    
    The specificComponent is just the special component we want to search for, if the component 
    type is NULL, then the Specific component is the one and only we are interested in. Note that we 
    don't care about the manufacturer information in this function. 
    
    If we don't find a suitable component we will return false, otherwise we will return true.
*/
 
pascal Boolean QTUHasComponentType(OSType theComponentType, OSType theSpecificComponent)
{
    ComponentDescription aCD;
    
    aCD.componentType = theComponentType;
    aCD.componentSubType = theSpecificComponent;
    aCD.componentManufacturer = 0;
    
    if(theComponentType == MovieImportType)
    {
        aCD.componentFlags = canMovieImportFiles;
        aCD.componentFlagsMask = canMovieImportFiles;
    }
    else if(theComponentType == MovieExportType)
    {
        aCD.componentFlags = canMovieExportFiles;
        aCD.componentFlagsMask = canMovieExportFiles;
    }
 
    if(FindNextComponent((Component)0, &aCD) != NULL)
        return true;
    else
        return false;
}
 
 
//______________________________________________________________________
// T H E    E N D