sources/MovieSourcing.c

/*
    File:       MovieSourcing.c
 
    Copyright:  © 2000-2001 by Apple Computer, Inc., all rights reserved.
 
 
*/
 
#include <ConditionalMacros.h>
 
#include <string.h>
 
#include <QuickTimeStreaming.h>
#include <QTStreamingComponents.h>
 
#include "AppSupport.h"
#include "MovieSourcing.h"
#include "QTSSampleCodeUtils.h"
#include "UsherCommands.h"
    
// ---------------------------------------------------------------------------
//      D E F I N I T I O N S
// ---------------------------------------------------------------------------
 
 
// ---------------------------------------------------------------------------
//      P R O T O T Y P E S
// ---------------------------------------------------------------------------
 
static OSErr MovieSourcing_GetNextMovie(MovieSourcing *inHandler);
 
// flags for MovieSourcing_CloseCurrentMovie
enum  {
    kStoredMovieSupportCloseFlag_DontTellSourcers   = 0x00000001
};
 
static void MovieSourcing_CloseCurrentMovie(MovieSourcing *inHandler, SInt32 inFlags);
 
static OSErr MovieSourcing_SourcerDone(MovieSourcing *inHandler,
                    QTSSourcerDoneParams *inParams, Boolean *outSendChangedMessage);
 
static OSErr MovieSourcing_SkipToNext(MovieSourcing *inHandler);
 
static OSErr MovieSourcing_SetupTrackSourcerForStream(MovieSourcing *inHandler, QTSStream inStream);
static OSErr MovieSourcing_SetupNextMovie(MovieSourcing *inHandler);
static Boolean MovieSourcing_FindAndSetupSourcer(MovieSourcing *inHandler,
                        Track inTrack, OSType inTrackType);
 
static pascal ComponentResult MovieSourcing_CallbackProc(ComponentResult inErr,
                OSType inSelector, void *ioParams, void *inRefCon);
 
 
QTSNotificationUPP gMovieSourcingCallbackUPP = NULL;
 
 
// ---------------------------------------------------------------------------
//      MovieSourcing_New
// ---------------------------------------------------------------------------
 
OSErr MovieSourcing_New(QTSPresentation inPresentation, SourceMediaParams *inSourceMediaParams,
                MovieSourcingSettings *inSettingsStuff, MovieSourcing **outHandler)
{
#pragma unused(inSourceMediaParams)
    OSErr           err = noErr;
    MovieSourcing   *handler = NULL;
    UInt32          numStreams;
    UInt32          i;
    QTSStream       stream;
    UInt32          numSources;
 
    *outHandler = NULL;
 
    handler = (MovieSourcing*)QTSNewPtrClear(sizeof(MovieSourcing));
    EXITIFERR( err = MemError() );
 
    handler->standard.signature = kSignature_MovieSourcing;
    handler->standard.standardSignature = kSignature_StandardSourceHandler;
    handler->presentation = inPresentation;
 
    if (inSettingsStuff != NULL)  {
        CInfoPBRec      pb;
        FSSpec          folderSpec;
        
        memset(&pb, 0, sizeof(pb));
        handler->loop = inSettingsStuff->loop;
        handler->selectRandomly = inSettingsStuff->selectRandomly;
        if (inSettingsStuff->haveFileSpec)  {
            BlockMoveData(&inSettingsStuff->spec, &handler->currentMovieSpec, sizeof(FSSpec));
            handler->currentFileIndex = kMovieSourcingUseOneFixedFile;
        }  else  {
 
            if (inSettingsStuff->haveFolderSpec)  {
                BlockMoveData(&inSettingsStuff->spec, &folderSpec, sizeof(FSSpec));
            }  else  {
#define kMovieFolderName        "\pmovies"          
                err = GetAppRelativeFolderSpec(kMovieFolderName, &folderSpec);
                if (err != noErr)  {
                    if (err == fnfErr)  {
                        ProcessUserMessage(kProcessMessageFlag_Log | kProcessMessageFlag_ShowDialog,
                                    "Couldn't find the Movies folder");
                    }  else  {
                        ProcessUserMessage(kProcessMessageFlag_Log | kProcessMessageFlag_ShowDialog,
                                    "Couldn't get the Movies folder spec (error %ld)", err);
                    }
                    EXITERR( err );
                }
            }
            
            pb.dirInfo.ioNamePtr = folderSpec.name;
            pb.dirInfo.ioVRefNum = folderSpec.vRefNum;
            pb.dirInfo.ioDrDirID = folderSpec.parID;
            pb.dirInfo.ioFDirIndex = 0;
            
            err = PBGetCatInfoSync(&pb);
            if (err != noErr)  {
                char        tempString[256];
                CopyPToCStr(folderSpec.name, tempString);
                ProcessUserMessage(kProcessMessageFlag_Log | kProcessMessageFlag_ShowDialog,
                            "Error (%ld) while PBGetCatInfoSync on the folder '%s'", err, tempString);
                EXITERR( err );
            }
            handler->folderVRefNum = pb.dirInfo.ioVRefNum;
            //handler->folderDirID = folderSpec.parID;
            handler->folderDirID = pb.dirInfo.ioDrDirID;
            handler->numFilesInFolder = pb.dirInfo.ioDrNmFls;
 
        }
    }   
 
    // for each stream, if there is no sourcer, make one
    numStreams = QTSPresGetNumStreams(handler->presentation);
    for (i=1; i<=numStreams; ++i)  {
        stream = QTSPresGetIndStream(handler->presentation, i);
        if (stream != kQTSInvalidStream)  {
            numSources = QTSPresGetNumSourcers(handler->presentation, stream);
            if (numSources == 0)  {
                EXITIFERR( err = MovieSourcing_SetupTrackSourcerForStream(handler, stream) );
            }
        }
    }
        
    if (handler->numTrackSourcers > 0)  {
        err = MovieSourcing_SetupNextMovie(handler);
    }
 
    *outHandler = handler;
exit:
    if (err != noErr)  {
        if (handler != NULL)  {
            MovieSourcing_Dispose(handler); 
        }
    }
    return err;
}
 
// ---------------------------------------------------------------------------
//      MovieSourcing_Dispose
// ---------------------------------------------------------------------------
 
void MovieSourcing_Dispose(MovieSourcing *inHandler)
{
    if (inHandler != NULL)  {
        MovieSourcing_CloseCurrentMovie(inHandler, kStoredMovieSupportCloseFlag_DontTellSourcers);
 
        DisposePtr((Ptr)inHandler);
    }
}
 
 
// ---------------------------------------------------------------------------
//      MovieSourcing_Reset
// ---------------------------------------------------------------------------
 
static void MovieSourcing_Reset(MovieSourcing *inHandler)
{
    MovieSourcing_CloseCurrentMovie(inHandler, kStoredMovieSupportCloseFlag_DontTellSourcers);
 
}
 
 
// ---------------------------------------------------------------------------
//      MovieSourcing_SetupTrackSourcerForStream
// ---------------------------------------------------------------------------
 
static OSErr MovieSourcing_SetupTrackSourcerForStream(MovieSourcing *inHandler, QTSStream inStream)
{
    OSErr                       err = noErr;
    ComponentInstance           sourcer = 0;
    OSType                      trackType = SoundMediaType;
    MovieSourcingSourcerInfo    *sourcerInfo;
    QTSLoopParams               loopParams;
    OSErr                       tempErr;
    ComponentDescription        cd;
    Component                   component;
    
    if (inHandler->numTrackSourcers >= kMovieSourcingMaxTrackSourcers)  {
        ProcessUserMessage(kProcessMessageFlag_Log | kProcessMessageFlag_ShowDialog,
                    "Too many (>%ld) folder movie tracks in this broadcast", kMovieSourcingMaxTrackSourcers);
        EXITERR( err = qtsBadStateErr );
    }
 
    // ask the presentation if it already has a media type
    tempErr = QTSPresGetInfo(inHandler->presentation, inStream, kQTSMediaTypeInfo, &trackType);
    if (tempErr != noErr)  {
        trackType = SoundMediaType;
    }
 
    memset(&cd, 0, sizeof(cd));
    cd.componentType = kQTSSourcerType;
    cd.componentSubType = kQTSMovieTrackSourcerType;
    cd.componentManufacturer = trackType;
    component = FindNextComponent(0, &cd);
    if (component == 0)  {
        ProcessUserMessage(kProcessMessageFlag_Log | kProcessMessageFlag_ShowDialog,
                    "Couldn't open track sourcer of type '%.4s'", &trackType);
        EXITERR( err = paramErr );
    }
    
    EXITIFERR( err = OpenAComponent(component, &sourcer) );
    EXITIFERR( err = QTSSourcerInitialize(sourcer, NULL) );
 
    if (inHandler->loop)  {
        memset(&loopParams, 0, sizeof(loopParams));
        loopParams.loopFlags = kQTSLoopFlag_Loop;
        loopParams.flagsMask = kQTSLoopFlag_Loop;
        QTSSourcerSetInfo(sourcer, kQTSInfo_Loop, &loopParams);
    }
 
 
    {
        QTSSourcerCallbackProcParams    cParams;
        
        memset(&cParams, 0, sizeof(cParams));
        cParams.version = kQTSSourcerCallbackProcParamsVersion1;
        if (gMovieSourcingCallbackUPP == NULL)  {
            gMovieSourcingCallbackUPP = NewQTSNotificationUPP(MovieSourcing_CallbackProc);
        }
        cParams.proc = gMovieSourcingCallbackUPP;
        cParams.refCon = inHandler;
    
        EXITIFERR( err = QTSSourcerSetInfo(sourcer, kQTSInfo_SourcerCallbackProc, &cParams) );
    }
 
    // the presentation now owns the sourcer and will dispose of it unless you 
    // set the right flags
    EXITIFERR( err = QTSPresAddSourcer(inHandler->presentation, inStream, sourcer, 0L) );
 
    sourcerInfo = &(inHandler->sourcerInfo[inHandler->numTrackSourcers]);
    sourcerInfo->sourcer = sourcer;
    sourcerInfo->stream = inStream;
    sourcerInfo->trackType = trackType;
    ++inHandler->numTrackSourcers;
 
exit:
    if (err != noErr)  {
        DEBUGF(("MovieSourcing_SetupTrackSourcerForStream $%.8x-err %ld", inHandler, err));
        if (sourcer != 0)  {
            CloseComponent(sourcer);
        }
    }
    return err;
}
 
// ---------------------------------------------------------------------------
//      MovieSourcing_SetupNextMovie
// ---------------------------------------------------------------------------
 
static OSErr MovieSourcing_SetupNextMovie(MovieSourcing *inHandler)
{
    OSErr       err = noErr;
    long        numMovieTracks;
    long        i;
    Track       track;
    Media       media;
    OSType      trackType;
    
    if (inHandler->currentFileIndex == kMovieSourcingUseOneFixedFile)  {
        if (inHandler->currentMovie == 0) {
            EXITIFERR( err = MovieSourcing_GetNextMovie(inHandler) );
        }
    }  else  {
        MovieSourcing_CloseCurrentMovie(inHandler, 0L);
        EXITIFERR( err = MovieSourcing_GetNextMovie(inHandler) );
    }
 
    numMovieTracks = GetMovieTrackCount(inHandler->currentMovie);
    for (i=1; i<=numMovieTracks; ++i)  {
        track = GetMovieIndTrack(inHandler->currentMovie, i);
        if (!GetTrackEnabled(track))  {
            // skip any disabled tracks
            continue;
        }
        media = GetTrackMedia(track);
        GetMediaHandlerDescription(media, &trackType, NULL, NULL);
        MovieSourcing_FindAndSetupSourcer(inHandler, track, trackType);
    }
 
exit:
    return err;
}
 
// ---------------------------------------------------------------------------
//      MovieSourcing_FindAndSetupSourcer
// ---------------------------------------------------------------------------
 
static Boolean MovieSourcing_FindAndSetupSourcer(MovieSourcing *inHandler,
                        Track inTrack, OSType inTrackType)
{
    QTSTrackParams              trackParams;
    MovieSourcingSourcerInfo    *sourcerInfo;
    Boolean                     found = false;
    UInt32                      i;
    OSErr                       err = noErr;
    
    memset(&trackParams, 0, sizeof(trackParams));
    for (i=0; i<inHandler->numTrackSourcers; ++i)  {
        sourcerInfo = &(inHandler->sourcerInfo[i]);
        if (sourcerInfo->sourcer == 0)  {
            // this should never happen
            DEBUGF(("MovieSourcing_FindAndSetupSourcer $%.8s-info $%.8x sourcer=0", inHandler, sourcerInfo));
            break;
        }
        
        if ((sourcerInfo->track == 0)  &&  (sourcerInfo->trackType == inTrackType))  {
            trackParams.track = inTrack;
            EXITIFERR( err = QTSSourcerSetInfo(sourcerInfo->sourcer, kQTSInfo_Track, &trackParams) );
            sourcerInfo->track = inTrack;
            found = true;
            break;
        }
    }
exit:
    return found;
}
 
// ---------------------------------------------------------------------------
//      MovieSourcing_GetNextMovie
// ---------------------------------------------------------------------------
 
static OSErr MovieSourcing_GetNextMovie(MovieSourcing *inHandler)
{
    OSErr       err = noErr;
    Movie       myMovie = 0;
    FSSpec      mySpec;
    short       refNum = kFileNotOpen;
    UInt32      originalIndex;
    Boolean     cycledAround = false;
    Boolean     foundFile = false;
    Boolean     foundMovie = false;
    CInfoPBRec      pb;
    OSErr           tempErr;
    
 
    if (inHandler->currentFileIndex == kMovieSourcingUseOneFixedFile)  {
        BlockMoveData(&inHandler->currentMovieSpec, &mySpec, sizeof(mySpec));
        err = OpenMovieFile(&mySpec, &refNum, fsRdPerm);
        if (err == noErr)  {
            err = NewMovieFromFile(&myMovie, refNum, NULL, NULL, 0L, NULL) ;
        }
        if (err != noErr)  {
            if (err == fnfErr)  {
                ProcessUserMessage(kProcessMessageFlag_Log | kProcessMessageFlag_ShowDialog,
                        "The file 'switch.mov' in the application directory could not be found");
            } else  {
                ProcessUserMessage(kProcessMessageFlag_Log | kProcessMessageFlag_ShowDialog,
                        "Error (%ld) trying to use the file switch.mov in the application directory", err);
            }
            EXITERR( err );
        }
    }  else  {
        mySpec.vRefNum = inHandler->folderVRefNum;
        mySpec.parID = inHandler->folderDirID;
        originalIndex = inHandler->currentFileIndex;
        while (!foundMovie)  {
 
            memset(&pb, 0, sizeof(pb));
            while (!foundFile)  {
                ++inHandler->currentFileIndex;
                if (cycledAround)  {
                    DEBUGF(("MovieSourcing_GetNextMovie $%.8x-no valid movies in folder?", inHandler));
                    EXITERR( err = fnfErr );
                }
                if (inHandler->currentFileIndex == originalIndex)  {
                    // we wrapped around without finding any movies
                    cycledAround = true;
                }
 
                mySpec.name[0] = 0;
                pb.hFileInfo.ioNamePtr = mySpec.name;
                pb.hFileInfo.ioVRefNum = mySpec.vRefNum;
                pb.hFileInfo.ioDirID = mySpec.parID;
                pb.hFileInfo.ioFDirIndex = inHandler->currentFileIndex;
                
                tempErr = PBGetCatInfoSync(&pb);
                
                // @@@ need to stop if the folder contains only directories
                if (tempErr == noErr)  {
                    long        length;
 
                    if (pb.dirInfo.ioFlAttrib & kioFlAttribDirMask)  {
                        // this is a directory
                        continue;
                    }
 
                    // it has a /n at the end of the name, but MPW and CW interpret /n to be different
                    #define kIconFileNameBase       "Icon"
 
                    // skip the hidden icon file that seems to be made all the time
                    length = strlen(kIconFileNameBase);
                    if (mySpec.name[0] == (length+1))  {
                        if (EqualChars((char*)&(mySpec.name[1]), mySpec.name[0], kIconFileNameBase, length))  {
                            if ((mySpec.name[length] == '\r')  ||  (mySpec.name[length] == '\n'))  {
                                continue;
                            }
                        }
                    }
                    foundFile = true;
                }  else  {
                    if (tempErr == fnfErr)  {
                        if (inHandler->currentFileIndex < 2)  {
                            ProcessUserMessage(kProcessMessageFlag_Log | kProcessMessageFlag_ShowDialog,
                                    "Warning - Couldn't find any valid movie files in the movie folder.");
                            EXITERR( err );
                        }
                        inHandler->currentFileIndex = 0;
                    }  else  {
                        ProcessUserMessage(kProcessMessageFlag_Log | kProcessMessageFlag_ShowDialog,
                                    "Error (%ld) getting info about the movie folder.", tempErr);
                        EXITERR( err );
                    }
                }
            }
        
            // get the next file in the list and make a movie out of it
            EXITIFERR( err = OpenMovieFile(&mySpec, &refNum, fsRdPerm) );
            tempErr = NewMovieFromFile(&myMovie, refNum, NULL, NULL, 0L, NULL);
            if (tempErr == noErr)  {
                foundMovie = true;
            }  else  {
                foundFile = false;
                //EXITERR( err = tempErr );
            }
        }
    }
    
    BlockMoveData(&mySpec, &inHandler->currentMovieSpec, sizeof(mySpec));
    inHandler->movieFileRefNum = refNum;
    inHandler->currentMovie = myMovie;
 
exit:
    if (err != noErr)  {
        DEBUGF(("MovieSourcing_GetNextMovie $%.8x-err %ld", inHandler, err));
        if (myMovie != 0)  {
            DisposeMovie(myMovie);
        }
        if ((refNum != kFileNotOpen)  &&  (refNum != 0))  {
            CloseMovieFile(refNum);
        }
    }
    return err;
}
 
// ---------------------------------------------------------------------------
//      MovieSourcing_CloseCurrentMovie
// ---------------------------------------------------------------------------
 
static void MovieSourcing_CloseCurrentMovie(MovieSourcing *inHandler, SInt32 inFlags)
{
    // unset all the tracks in the sourcers     
    UInt32                      i;
    QTSTrackParams              trackParams;
    MovieSourcingSourcerInfo    *sourcerInfo;
    
    memset(&trackParams, 0, sizeof(trackParams));
    for (i=0; i<inHandler->numTrackSourcers; ++i)  {
        sourcerInfo = &(inHandler->sourcerInfo[i]);
 
        if (!(inFlags & kStoredMovieSupportCloseFlag_DontTellSourcers))  {
            if ((sourcerInfo->sourcer != 0)  &&  (sourcerInfo->track != 0))  {
                QTSSourcerSetInfo(sourcerInfo->sourcer, kQTSInfo_Track, &trackParams);
            }  else  {
                if (sourcerInfo->sourcer == 0)  {
                    DEBUGF(("MovieSourcing_CloseCurrentMovie $%.8x-index %ld no sourcer", inHandler, i));
                }
            }
        }
        sourcerInfo->track = 0;
    }
 
    if (inHandler->currentMovie != 0)  {
        DisposeMovie(inHandler->currentMovie);
        inHandler->currentMovie = 0;
    }
    if ((inHandler->movieFileRefNum != kFileNotOpen)  &&  (inHandler->movieFileRefNum != 0))  {
        CloseMovieFile(inHandler->movieFileRefNum);
        inHandler->movieFileRefNum = kFileNotOpen;
    }
}
 
// ---------------------------------------------------------------------------
//      MovieSourcing_SkipToNext
// ---------------------------------------------------------------------------
 
static OSErr MovieSourcing_SkipToNext(MovieSourcing *inHandler)
{
    return MovieSourcing_SetupNextMovie(inHandler);
}
 
 
// ---------------------------------------------------------------------------
//      MovieSourcing_Idle
// ---------------------------------------------------------------------------
 
void MovieSourcing_Idle(MovieSourcing *inHandler, Boolean inPlaying)
{
#pragma unused(inHandler, inPlaying)
}
 
// ---------------------------------------------------------------------------
//      MovieSourcing_SetEnable
// ---------------------------------------------------------------------------
 
void MovieSourcing_SetEnable(MovieSourcing *inHandler, Boolean inEnable)
{
    UInt32                      i;
    MovieSourcingSourcerInfo    *sourcerInfo;
 
    for (i=0; i<inHandler->numTrackSourcers; ++i)  {
        sourcerInfo = &(inHandler->sourcerInfo[i]);
        if (sourcerInfo->sourcer != 0)  {
            QTSSourcerSetEnable(sourcerInfo->sourcer, inEnable, 0);
        }
    }
}
 
// ---------------------------------------------------------------------------
//      MovieSourcing_HasCharacteristic
// ---------------------------------------------------------------------------
 
void MovieSourcing_HasCharacteristic(MovieSourcing *inHandler, OSType inCharacteristic, Boolean *outHasIt)
{
#pragma unused(inHandler)
    switch (inCharacteristic)  {
    
        default:
            *outHasIt = false;
            break;
    }
}
 
#pragma mark -
 
// ---------------------------------------------------------------------------
//      MovieSourcing_HandleNotification
// ---------------------------------------------------------------------------
 
void MovieSourcing_HandleNotification(MovieSourcing *inHandler, OSType inNotification, void *inParams)
{
#pragma unused(inHandler, inParams)
    Boolean         changed = false;
 
    switch (inNotification)  {
    
        default:
            break;
    }
}
 
// ---------------------------------------------------------------------------
//      MovieSourcing_CallbackProc
// ---------------------------------------------------------------------------
 
static pascal ComponentResult MovieSourcing_CallbackProc(ComponentResult inErr,
                OSType inSelector, void *ioParams, void *inRefCon)
{
    ComponentResult     err = noErr;
    MovieSourcing       *handler = (MovieSourcing*)inRefCon;
    
    if (inErr != noErr)  {
        DEBUGF(("MovieSourcing_CallbackProc $%.8x-err %ld", inRefCon, inErr));
    }
 
    switch (inSelector)  {
        case kQTSSourcerCallback_Done:
            MovieSourcing_SourcerDone(handler, (QTSSourcerDoneParams*)ioParams, NULL);
            break;  
    
        default:
            // ignore any selectors you don't know about
            break;
            
    }
    return err;
}
 
// ---------------------------------------------------------------------------
//      MovieSourcing_SourcerDone
// ---------------------------------------------------------------------------
 
static OSErr MovieSourcing_SourcerDone(MovieSourcing *inHandler,
                    QTSSourcerDoneParams *inParams, Boolean *outSendChangedMessage)
{
    // find the right sourcer and mark it as done
    // when all streams are done, go to the next movie
    OSErr                       err = noErr;
    UInt32                      i;
    MovieSourcingSourcerInfo    *sourcerInfo;
    Boolean                     found = false;
    Boolean                     allDone = true;
    
    for (i=0; i<inHandler->numTrackSourcers; ++i)  {
        sourcerInfo = &(inHandler->sourcerInfo[i]);
        if (sourcerInfo->sourcer == inParams->sourcer)  {
            sourcerInfo->done = true;
            found = true;
            break;
        }  else  {
            if (!sourcerInfo->done)  {
                allDone = false;
            }
        }
    }
    
    if (!found) {
        goto exit;
    }
    
    if (allDone)  {
        err = MovieSourcing_SetupNextMovie(inHandler);
        if (err != noErr)  {
            DEBUGF(("MovieSourcing_SourcerDone $%.8x-err %ld starting next movie", inHandler, err));
        }
        if (outSendChangedMessage != NULL)  {
            *outSendChangedMessage = true;
        }
    }
 
exit:
    return err;
}
 
// ---------------------------------------------------------------------------
//      MovieSourcing_GetInfo
// ---------------------------------------------------------------------------
 
OSErr MovieSourcing_GetInfo(MovieSourcing *inHandler, OSType inSelector, void *ioParams)
{
    OSErr                       err = noErr;
    UInt32                      i;
    MovieSourcingSourcerInfo    *sourcerInfo;
    Boolean                     found = false;
    
    switch (inSelector)  {
        case kQTSInfo_SourcerTiming:
        {
            // find the first sourcer and ask it
            for (i=0; i<inHandler->numTrackSourcers; ++i)  {
                sourcerInfo = &(inHandler->sourcerInfo[i]);
                if (sourcerInfo->sourcer != 0)  {
                    err = QTSSourcerGetInfo(sourcerInfo->sourcer, kQTSInfo_SourcerTiming, ioParams);
                    found = true;
                    break;
                }
            }
            if (!found)  {
                err = qtsUnknownValueErr;
            }
            break;
        }
            
        default:
            err = qtsBadSelectorErr;
            break;
    }
    return err;
}
 
// ---------------------------------------------------------------------------
//      MovieSourcing_SetInfo
// ---------------------------------------------------------------------------
 
OSErr MovieSourcing_SetInfo(MovieSourcing *inHandler, OSType inSelector, void *ioParams)
{
#pragma unused(inHandler, ioParams)
    OSErr               err = noErr;
    
    switch (inSelector)  {
            
        default:
            err = qtsBadSelectorErr;
            break;
    }
    return err;
}
 
 
// ---------------------------------------------------------------------------
//      MovieSourcing_DoCommand
// ---------------------------------------------------------------------------
 
OSErr MovieSourcing_DoCommand(MovieSourcing *inHandler, OSType inCommand, void *ioCommandParams)
{
#pragma unused(ioCommandParams)
    OSErr                       err = noErr;
    
    switch (inCommand)  {
 
        case kCommand_GetStoredMovieInfo:
        {
            UsherStoredMovieParams  *mParams = (UsherStoredMovieParams*)ioCommandParams;
 
            memset(mParams, 0, sizeof(UsherStoredMovieParams));
            mParams->doingStoredMovies = true;
            BlockMoveData(&inHandler->currentMovieSpec, &mParams->currentMovieSpec, sizeof(FSSpec));
            break;
        }
 
 
        case kCommand_SkipToNextMovie:
            err = MovieSourcing_SkipToNext(inHandler);
            break;
            
        default:
            err = kErr_AppUnhandledCommand;
            break;
    }
    return err;
}