QTDataRef.c

/*
    File:       QTDataRef.c
    
    Description: Document-specific code for thread-safety test application.
 
    Author:     QTEngineering, dts
 
    Copyright:  © Copyright 2003-2004 Apple Computer, Inc. All rights reserved.
    
    Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
                ("Apple") in consideration of your agreement to the following terms, and your
                use, installation, modification or redistribution of this Apple software
                constitutes acceptance of these terms.  If you do not agree with these terms,
                please do not use, install, modify or redistribute this Apple software.
 
                In consideration of your agreement to abide by the following terms, and subject
                to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
                copyrights in this original Apple software (the "Apple Software"), to use,
                reproduce, modify and redistribute the Apple Software, with or without
                modifications, in source and/or binary forms; provided that if you redistribute
                the Apple Software in its entirety and without modifications, you must retain
                this notice and the following text and disclaimers in all such redistributions of
                the Apple Software.  Neither the name, trademarks, service marks or logos of
                Apple Computer, Inc. may be used to endorse or promote products derived from the
                Apple Software without specific prior written permission from Apple.  Except as
                expressly stated in this notice, no other rights or licenses, express or implied,
                are granted by Apple herein, including but not limited to any patent rights that
                may be infringed by your derivative works or by other works in which the Apple
                Software may be incorporated.
 
                The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
                WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
                WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
                COMBINATION WITH YOUR PRODUCTS.
 
                IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
                CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
                GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
                OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
                (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
                ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                
    Change History (most recent first): <1> dts 11/16/03 initial release
 
*/
 
//////////
//
// header files
//
//////////
 
#include "QTDataRef.h"
 
 
//////////
//
// global variables
//
//////////
 
extern short                gAppResFile;                        // file reference number for this application's resource file
extern ModalFilterUPP       gModalFilterUPP;                    // UPP to our custom dialog event filter
 
Ptr                         gDataBuffer = NULL;                 // buffer that holds data being transferred
ComponentInstance           gDataReader = NULL;                 // the data handler that reads data from the URL
ComponentInstance           gDataWriter = NULL;                 // the data handler that writes data to a file
DataHCompletionUPP          gReadDataHCompletionUPP = NULL;
DataHCompletionUPP          gWriteDataHCompletionUPP = NULL;
long                        gBytesToTransfer = 0L;              // the number of bytes to transfer
long                        gBytesTransferred = 0L;             // the number of bytes already transferred
Boolean                     gDoneTransferring = false;          // are we done transferring data?
 
#if TARGET_OS_WIN32
UINT                        gTimerID;                           // ID of the timer that tasks the data handlers
#endif
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Data reference creation utilities.
//
// Use these functions to create data references.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// QTDR_MakeFileDataRef
// Return a file data reference for the specified file.
//
// The caller is responsible for disposing of the handle returned by this function (by calling DisposeHandle).
//
//////////
 
Handle QTDR_MakeFileDataRef (FSSpecPtr theFile)
{
    Handle          myDataRef = NULL;
 
    QTNewAlias(theFile, (AliasHandle *)&myDataRef, true);
 
    return(myDataRef);
}
 
 
//////////
//
// QTDR_MakeResourceDataRef
// Return a resource data reference for the specified file.
//
// The caller is responsible for disposing of the handle returned by this function (by calling DisposeHandle).
//
//////////
 
Handle QTDR_MakeResourceDataRef (FSSpecPtr theFile, OSType theResType, SInt16 theResID)
{
    Handle          myDataRef = NULL;
    OSType          myResType;
    SInt16          myResID;
    OSErr           myErr = noErr;
 
    myDataRef = QTDR_MakeFileDataRef(theFile);
    if (myDataRef == NULL)
        goto bail;
 
    // append the resource type and ID to the data reference
    myResType = EndianU32_NtoB(theResType);
    myResID = EndianS16_NtoB(theResID);
    
    myErr = PtrAndHand(&myResType, myDataRef, sizeof(myResType));
    if (myErr == noErr)
        myErr = PtrAndHand(&myResID, myDataRef, sizeof(myResID));
 
bail:
    if (myErr != noErr) {
        if (myDataRef != NULL)
            DisposeHandle(myDataRef);
        myDataRef = NULL;
    }
    
    return(myDataRef);
}
 
 
//////////
//
// QTDR_MakeHandleDataRef
// Return a handle data reference for the specified handle.
//
// The caller is responsible for disposing of the handle returned by this function (by calling DisposeHandle).
//
//////////
 
Handle QTDR_MakeHandleDataRef (Handle theHandle)
{
    Handle          myDataRef = NULL;
 
    myDataRef = NewHandleClear(sizeof(Handle));
    if (myDataRef != NULL)
        BlockMove(&theHandle, *myDataRef, sizeof(Handle));
 
    // the following single line can replace the preceding three lines
//  PtrToHand(&theHandle, &myDataRef, sizeof(Handle));
 
    return(myDataRef);
}
 
 
//////////
//
// QTDR_MakeURLDataRef
// Return a URL data reference for the specified URL.
//
// The caller is responsible for disposing of the handle returned by this function (by calling DisposeHandle).
//
//////////
 
Handle QTDR_MakeURLDataRef (char *theURL)
{
    Handle          myDataRef = NULL;
    Size            mySize = 0;
 
    // get the size of the URL, plus the terminating null byte
    mySize = (Size)strlen(theURL) + 1;
    if (mySize == 1)
        goto bail;
    
    // allocate a new handle and copy the URL into the handle
    myDataRef = NewHandleClear(mySize);
    if (myDataRef != NULL)
        BlockMove(theURL, *myDataRef, mySize);
 
bail:
    return(myDataRef);
}
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Movie-retrieval utilities.
//
// Use these functions to get movies specified using data references.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// QTDR_GetMovieFromFile
// Get the movie stored in the specified file.
//
//////////
 
Movie QTDR_GetMovieFromFile (FSSpecPtr theFile)
{
    Movie       myMovie = NULL;
    Handle      myDataRef = NULL;
                    
    myDataRef = QTDR_MakeFileDataRef(theFile);
    if (myDataRef != NULL) {
        NewMovieFromDataRef(&myMovie, newMovieActive, NULL, myDataRef, rAliasType);
        DisposeHandle(myDataRef);
    }
        
    return(myMovie);
}
 
 
//////////
//
// QTDR_GetMovieFromHandle
// Get the movie stored in the specified block of memory.
//
//////////
 
Movie QTDR_GetMovieFromHandle (Handle theHandle)
{
    Movie       myMovie = NULL;
    Handle      myDataRef = NULL;
                    
    myDataRef = QTDR_MakeHandleDataRef(theHandle);
    if (myDataRef != NULL) {
        NewMovieFromDataRef(&myMovie, newMovieActive, NULL, myDataRef, HandleDataHandlerSubType);
        DisposeHandle(myDataRef);
    }
        
    return(myMovie);
}
 
 
//////////
//
// QTDR_GetMovieFromResource
// Get the movie stored in the specified resource.
//
//////////
 
Movie QTDR_GetMovieFromResource (FSSpecPtr theFile, OSType theResType, SInt16 theResID)
{
    Movie       myMovie = NULL;
    Handle      myDataRef = NULL;
                    
    myDataRef = QTDR_MakeResourceDataRef(theFile, theResType, theResID);
    if (myDataRef != NULL) {
        NewMovieFromDataRef(&myMovie, newMovieActive, NULL, myDataRef, ResourceDataHandlerSubType);
        DisposeHandle(myDataRef);
    }
        
    return(myMovie);
}
 
 
//////////
//
// QTDR_GetMovieFromURL
// Get the movie in the file referenced by the specified uniform resource locator (URL).
//
//////////
 
Movie QTDR_GetMovieFromURL (char *theURL)
{
    Movie       myMovie = NULL;
    Handle      myDataRef = NULL;
    
    myDataRef = QTDR_MakeURLDataRef(theURL);
    if (myDataRef != NULL) {
        NewMovieFromDataRef(&myMovie, newMovieActive, NULL, myDataRef, URLDataHandlerSubType);
        DisposeHandle(myDataRef);
    }
    
    return(myMovie);
}
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// URL utilities.
//
// Use these functions to elicit URLs from the user and get the basename of a URL.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// QTDR_GetURLFromUser
// Display a dialog box to elicit a URL from the user; return a C string that contains the text in the
// dialog box when the user clicks the OK button; otherwise, return NULL.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
// Note that the URLs handled here are limited to 255 characters; supporting longer URLs is left as an
// exercise for the reader.
//
//////////
 
char *QTDR_GetURLFromUser (short thePromptStringIndex)
{
    short           myItem;
    short           mySavedResFile;
    GrafPtr         mySavedPort;
    DialogPtr       myDialog = NULL;
    short           myItemKind;
    Handle          myItemHandle;
    Rect            myItemRect;
    Str255          myString;
    char            *myURL = NULL;
    OSErr           myErr = noErr;
 
    //////////
    //
    // save the current resource file and graphics port
    //
    //////////
 
    mySavedResFile = CurResFile();
    GetPort(&mySavedPort);
 
    // set the application's resource file
    UseResFile(gAppResFile);
 
    //////////
    //
    // create the dialog box in which the user will enter a URL
    //
    //////////
 
    myDialog = GetNewDialog(kGetURL_DLOGID, NULL, (WindowPtr)-1L);
    if (myDialog == NULL)
        goto bail;
 
    QTFrame_ActivateController(QTFrame_GetFrontMovieWindow(), false);
    
    MacSetPort(GetDialogPort(myDialog));
    
    SetDialogDefaultItem(myDialog, kGetURL_OKButton);
    SetDialogCancelItem(myDialog, kGetURL_CancelButton);
    
    // set the prompt string    
    GetIndString(myString, kTextKindsResourceID, thePromptStringIndex);
 
    GetDialogItem(myDialog, kGetURL_URLLabelItem, &myItemKind, &myItemHandle, &myItemRect);
    SetDialogItemText(myItemHandle, myString);
    
    MacShowWindow(GetDialogWindow(myDialog));
    
    //////////
    //
    // display and handle events in the dialog box until the user clicks OK or Cancel
    //
    //////////
    
    do {
        ModalDialog(gModalFilterUPP, &myItem);
    } while ((myItem != kGetURL_OKButton) && (myItem != kGetURL_CancelButton));
    
    //////////
    //
    // handle the selected button
    //
    //////////
    
    if (myItem != kGetURL_OKButton) {
        myErr = userCanceledErr;
        goto bail;
    }
    
    // retrieve the edited text
    GetDialogItem(myDialog, kGetURL_URLTextItem, &myItemKind, &myItemHandle, &myItemRect);
    GetDialogItemText(myItemHandle, myString);
    myURL = QTUtils_ConvertPascalToCString(myString);
    
bail:
    // restore the previous resource file and graphics port
    MacSetPort(mySavedPort);
    UseResFile(mySavedResFile);
    
    if (myDialog != NULL)
        DisposeDialog(myDialog);
 
    return(myURL);
}
 
 
//////////
//
// QTDR_GetURLBasename
// Return the basename of the specified URL.
//
// The basename of a URL is the portion of the URL following the rightmost URL separator. This function
// is useful for setting window titles of movies opened using the URL data handler to the basename of a
// URL (just like QuickTime Player does).
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////
 
char *QTDR_GetURLBasename (char *theURL)
{
    char    *myBasename = NULL;
    short   myLength = 0;
    short   myIndex;
 
    // make sure we got a URL passed in
    if (theURL == NULL)
        goto bail;
        
    // get the length of the URL
    myLength = strlen(theURL);
    
    // find the position of the rightmost URL separator in theURL
    if (strchr(theURL, kURLSeparator) != NULL) {
 
        myIndex = myLength - 1;
        while (theURL[myIndex] != kURLSeparator)
            myIndex--;
            
        // calculate the length of the basename
        myLength = myLength - myIndex - 1;
 
    } else {
        // there is no rightmost URL separator in theURL;
        // set myIndex so that myIndex + 1 == 0, for the call to BlockMove below
        myIndex = -1;
    }
    
    // allocate space to hold the string that we return to the caller
    myBasename = malloc(myLength + 1);
    if (myBasename == NULL)
        goto bail;
        
    // copy into myBasename the substring of theURL from myIndex + 1 to the end
    BlockMove(&theURL[myIndex + 1], myBasename, myLength);
    myBasename[myLength] = '\0';
    
bail:   
    return(myBasename);
}
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Data reference extension functions.
//
// Use these functions to add extensions to data references.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// QTDR_AddFilenamingExtension
// Add a filenaming extension to a data reference. If theStringPtr is NULL, add a 0-length filename.
//
// A filenaming extension is a Pascal string.
//
//////////
 
OSErr QTDR_AddFilenamingExtension (Handle theDataRef, StringPtr theFileName)
{
    unsigned char   myChar = 0; 
    OSErr           myErr = noErr;
 
    if (theFileName == NULL)
        myErr = PtrAndHand(&myChar, theDataRef, sizeof(myChar));
    else
        myErr = PtrAndHand(theFileName, theDataRef, theFileName[0] + 1);
        
    return(myErr);
}
 
 
//////////
//
// QTDR_AddMacOSFileTypeDataRefExtension
// Add a Macintosh file type as a data reference extension.
//
// A Macintosh file type data extension is an atom whose data is a 4-byte OSType.
//
//////////
 
OSErr QTDR_AddMacOSFileTypeDataRefExtension (Handle theDataRef, OSType theType)
{
    unsigned long   myAtomHeader[2];
    OSType          myType;
    OSErr           myErr = noErr;
    
    myAtomHeader[0] = EndianU32_NtoB(sizeof(myAtomHeader) + sizeof(theType));
    myAtomHeader[1] = EndianU32_NtoB(kDataRefExtensionMacOSFileType);
    
    myType = EndianU32_NtoB(theType);
    
    myErr = PtrAndHand(myAtomHeader, theDataRef, sizeof(myAtomHeader));
    if (myErr == noErr)
        myErr = PtrAndHand(&myType, theDataRef, sizeof(myType));
        
    return(myErr);
}
 
 
//////////
//
// QTDR_AddMIMETypeDataRefExtension
// Add a MIME type as a data reference extension.
//
// A MIME type data extension is an atom whose data is a Pascal string.
//
//////////
 
OSErr QTDR_AddMIMETypeDataRefExtension (Handle theDataRef, StringPtr theMIMEType)
{
    unsigned long   myAtomHeader[2];
    OSErr           myErr = noErr;
    
    if (theMIMEType == NULL)
        return(paramErr);
        
    myAtomHeader[0] = EndianU32_NtoB(sizeof(myAtomHeader) + theMIMEType[0] + 1);
    myAtomHeader[1] = EndianU32_NtoB(kDataRefExtensionMIMEType);
    
    myErr = PtrAndHand(myAtomHeader, theDataRef, sizeof(myAtomHeader));
    if (myErr == noErr)
        myErr = PtrAndHand(theMIMEType, theDataRef, theMIMEType[0] + 1);
    
    return(myErr);
}
 
 
//////////
//
// QTDR_AddInitDataDataRefExtension
// Add some initialization data as a data reference extension.
//
// An initialization data data extension is an atom whose data is any block of data.
//
//////////
 
OSErr QTDR_AddInitDataDataRefExtension (Handle theDataRef, Ptr theInitDataPtr)
{
    unsigned long   myAtomHeader[2];
    OSErr           myErr = noErr;
    
    if (theInitDataPtr == NULL)
        return(paramErr);
        
    myAtomHeader[0] = EndianU32_NtoB(sizeof(myAtomHeader) + GetPtrSize(theInitDataPtr));
    myAtomHeader[1] = EndianU32_NtoB(kDataRefExtensionInitializationData);
    
    myErr = PtrAndHand(myAtomHeader, theDataRef, sizeof(myAtomHeader));
    if (myErr == noErr)
        myErr = PtrAndHand(theInitDataPtr, theDataRef, GetPtrSize(theInitDataPtr));
        
    return(myErr);
}
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// File-transfer functions.
//
// These functions implement our file-transfer capability.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// QTDR_CopyRemoteFileToLocalFile
// Copy a remote file (located at the specified URL) into a local file.
//
//////////
 
OSErr QTDR_CopyRemoteFileToLocalFile (char *theURL, FSSpecPtr theFile)
{
    Handle              myReaderRef = NULL;         // data reference for the remote file
    Handle              myWriterRef = NULL;         // data reference for the local file
    ComponentResult     myErr = badComponentType;
 
    //////////
    //
    // create the local file with the desired type and creator
    //
    //////////
    
    // delete the target local file, if it already exists;
    // if it doesn't exist yet, we'll get an error (fnfErr), which we just ignore
    FSpDelete(theFile);
    
    myErr = FSpCreate(theFile, kTransFileCreator, kTransFileType, smSystemScript);
    if (myErr != noErr)
        goto bail;
    
    //////////
    //
    // create data references for the remote file and the local file
    //
    //////////
    
    myReaderRef = QTDR_MakeURLDataRef(theURL);
    if (myReaderRef == NULL)
        goto bail;
 
    myWriterRef = QTDR_MakeFileDataRef(theFile);
    if (myWriterRef == NULL)
        goto bail;
 
    //////////
    //
    // find and open the URL and file data handlers; connect the data references to them
    //
    //////////
    
    gDataReader = OpenComponent(GetDataHandler(myReaderRef, URLDataHandlerSubType, kDataHCanRead));
    if (gDataReader == NULL)
        goto bail;
 
    gDataWriter = OpenComponent(GetDataHandler(myWriterRef, rAliasType, kDataHCanWrite));
    if (gDataWriter == NULL)
        goto bail;
        
    // set the data reference for the URL data handler
    myErr = DataHSetDataRef(gDataReader, myReaderRef);
    if (myErr != noErr)
        goto bail;
    
    // set the data reference for the file data handler
    myErr = DataHSetDataRef(gDataWriter, myWriterRef);
    if (myErr != noErr)
        goto bail;
    
    //////////
    //
    // allocate a data buffer; the URL data handler copies data into this buffer,
    // and the file data handler copies data out of it
    //
    //////////
    
    gDataBuffer = NewPtrClear(kDataBufferSize);
    myErr = MemError();
    if (myErr != noErr)
        goto bail;
        
    //////////
    //
    // connect to the remote and local files
    //
    //////////
    
    // open a read-only path to the remote data reference
    myErr = DataHOpenForRead(gDataReader);
    if (myErr != noErr)
        goto bail;
 
    // get the size of the remote file
    myErr = DataHGetFileSize(gDataReader, &gBytesToTransfer); 
    if (myErr != noErr)
        goto bail;
    
    // open a write-only path to the local data reference
    myErr = DataHOpenForWrite(gDataWriter);
    if (myErr != noErr)
        goto bail;
        
    //////////
    //
    // start reading and writing data
    //
    //////////
    
    gDoneTransferring = false;
    gBytesTransferred = 0L;
    
    gReadDataHCompletionUPP = NewDataHCompletionUPP(QTDR_ReadDataCompletionProc);
    gWriteDataHCompletionUPP = NewDataHCompletionUPP(QTDR_WriteDataCompletionProc);
        
    // start retrieving the data; we do this by calling our own write completion routine,
    // pretending that we've just successfully finished writing 0 bytes of data
    QTDR_WriteDataCompletionProc(gDataBuffer, 0L, noErr);
 
bail:
    // if we encountered any error, close the data handler components
    if (myErr != noErr)
        QTDR_CloseDownHandlers();
    
    return((OSErr)myErr);
}
 
 
//////////
//
// QTDR_ReadDataCompletionProc
// This procedure is called when the data handler has completed a read operation.
//
// The theRefCon parameter contains the number of bytes just read.
//
//////////
 
PASCAL_RTN void QTDR_ReadDataCompletionProc (Ptr theRequest, long theRefCon, OSErr theErr)
{
#pragma unused(theErr)
 
    // we just finished reading some data, so schedule a write operation            
    DataHWrite( gDataWriter,
                theRequest,                     // the data buffer
                gBytesTransferred,              // write from the current offset
                theRefCon,                      // the number of bytes to write
                gWriteDataHCompletionUPP,
                theRefCon);
}
 
 
//////////
//
// QTDR_WriteDataCompletionProc
// This procedure is called when the data handler has completed a write operation.
//
// The theRefCon parameter contains the number of bytes just written.
//
//////////
 
PASCAL_RTN void QTDR_WriteDataCompletionProc (Ptr theRequest, long theRefCon, OSErr theErr)
{
#pragma unused(theErr)
 
    long        myNumBytesToRead;
    wide        myWide;
 
    // increment our tally of the number of bytes written so far
    gBytesTransferred += theRefCon;
 
    if (gBytesTransferred < gBytesToTransfer) {
        // there is still data to read and write, so schedule a read operation
    
        // determine how big a chunk to read
        if (gBytesToTransfer - gBytesTransferred > kDataBufferSize)
            myNumBytesToRead = kDataBufferSize;
        else
            myNumBytesToRead = gBytesToTransfer - gBytesTransferred;
 
        myWide.lo = gBytesTransferred;          // read from the current offset 
        myWide.hi = 0;
        
        // schedule a read operation
        DataHReadAsync(gDataReader,
                        theRequest,             // the data buffer
                        myNumBytesToRead,
                        &myWide,
                        gReadDataHCompletionUPP,
                        myNumBytesToRead);
 
    } else {
        // we've transferred all the data, so set a flag to tell us to close down the data handlers
        gDoneTransferring = true;
    }
    
}
 
 
//////////
//
// QTDR_CloseDownHandlers
// Close our read/write access to our data references and then close down the read/write data handlers.
//
//////////
 
void QTDR_CloseDownHandlers (void)
{
    if (gDataReader != NULL) {
        DataHCloseForRead(gDataReader);
        CloseComponent(gDataReader);
        gDataReader = NULL;
    }
 
    if (gDataWriter != NULL) {
        DataHCloseForWrite(gDataWriter);
        CloseComponent(gDataWriter);
        gDataWriter = NULL;
    }
    
    // dispose of the data buffer
    if (gDataBuffer != NULL)
        DisposePtr(gDataBuffer);
        
    // dispose of the routine descriptors
    if (gReadDataHCompletionUPP != NULL)
        DisposeDataHCompletionUPP(gReadDataHCompletionUPP);
        
    if (gWriteDataHCompletionUPP != NULL)
        DisposeDataHCompletionUPP(gWriteDataHCompletionUPP);
    
    gDoneTransferring = false;
    
#if TARGET_OS_WIN32
    // kill the timer that tasks the data handlers
    KillTimer(NULL, gTimerID);
#endif
}
 
 
//////////
//
// QTDR_TimerProc
// Handle timer messages to task the data handlers.
//
//////////
 
#if TARGET_OS_WIN32
void CALLBACK QTDR_TimerProc (HWND theWnd, UINT theMessage, UINT theID, DWORD theTime)
{
#pragma unused(theWnd, theMessage, theID, theTime)
    QTApp_HandleEvent(NULL);
}
#endif
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Other functions.
//
// These functions are called by ComApplication.c.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// QTDR_CreateReferenceCopy
// Create a copy of the given file that is a reference movie (that is, a movie file and a separate
// media file).
//
//////////
 
OSErr QTDR_CreateReferenceCopy (Movie theSrcMovie, FSSpecPtr theDstMovieFile, FSSpecPtr theDstMediaFile)
{
    Track           mySrcTrack = NULL;
    Media           mySrcMedia = NULL;
    Movie           myDstMovie = NULL;
    Track           myDstTrack = NULL;
    Media           myDstMedia = NULL;
    Handle          myMediaRef = NULL;          // data reference for the media file
#if !USE_ADDEMPTYTRACKTOMOVIE
    Fixed           myWidth, myHeight;
    OSType          myType;
#endif
    long            myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile;
    short           myResRefNum = 0;
    short           myResID = movieInDataForkResID;
    OSErr           myErr = paramErr;
                    
    // get the first video track and media in the source movie
    mySrcTrack = GetMovieIndTrackType(theSrcMovie, 1, VideoMediaType, movieTrackMediaType);
    if (mySrcTrack == NULL)
        goto bail;
    
    mySrcMedia = GetTrackMedia(mySrcTrack);
    if (mySrcMedia == NULL)
        goto bail;
 
    // create a file data reference for the new media file
    myMediaRef = QTDR_MakeFileDataRef(theDstMediaFile);
    if (myMediaRef == NULL)
        goto bail;
 
    // create a file for the destination movie data
    myErr = FSpCreate(theDstMediaFile, sigMoviePlayer, MovieFileType, 0);
    if (myErr != noErr)
        goto bail;
    
    // create a file for the destination movie atom and create an empty movie
    myErr = CreateMovieFile(theDstMovieFile, sigMoviePlayer, smCurrentScript, myFlags, &myResRefNum, &myDstMovie);
    if (myErr != noErr)
        goto bail;
        
    // assign the default progress proc to the destination movie
    SetMovieProgressProc(myDstMovie, (MovieProgressUPP)-1, 0);
 
#if USE_ADDEMPTYTRACKTOMOVIE
    myErr = AddEmptyTrackToMovie(mySrcTrack, myDstMovie, myMediaRef, rAliasType, &myDstTrack);
    if (myErr != noErr)
        goto bail;
    
    myDstMedia = GetTrackMedia(myDstTrack);
    myErr = GetMoviesError();
    if (myErr != noErr)
        goto bail;
#else
    // get some information about the source track and media
    GetTrackDimensions(mySrcTrack, &myWidth, &myHeight);
    GetMediaHandlerDescription(mySrcMedia, &myType, 0, 0);
 
    // create the destination movie track and media
    myDstTrack = NewMovieTrack(myDstMovie, myWidth, myHeight, kNoVolume);
    myErr = GetMoviesError();
    if (myErr != noErr)
        goto bail;
        
    myDstMedia = NewTrackMedia(myDstTrack, myType, GetMediaTimeScale(mySrcMedia), myMediaRef, rAliasType);
    myErr = GetMoviesError();
    if (myErr != noErr)
        goto bail;
 
    CopyTrackSettings(mySrcTrack, myDstTrack);
#endif
 
    // copy the entire source track into the destination track; this copies the track's media
    // samples into the destination media file
    myErr = BeginMediaEdits(myDstMedia);
    if (myErr != noErr)
        goto bail;
 
    myErr = InsertTrackSegment(mySrcTrack, myDstTrack, 0, GetTrackDuration(mySrcTrack), 0);
    if (myErr != noErr)
        goto bail;
 
    myErr = EndMediaEdits(myDstMedia);
    if (myErr != noErr)
        goto bail;
    
    // add the movie atom to the data fork of the movie file
    myErr = AddMovieResource(myDstMovie, myResRefNum, &myResID, NULL);
 
bail:
    return(myErr);
}
 
 
//////////
//
// QTDR_PlayMovieFromRAM
// Play a movie.
//
//////////
 
OSErr QTDR_PlayMovieFromRAM (Movie theMovie)
{
    WindowPtr               myWindow = NULL;
    Rect                    myBounds = {50, 50, 100, 100};
    Rect                    myRect;
    StringPtr               myTitle = QTUtils_ConvertCToPascalString(kWindowTitle);
    OSErr                   myErr = memFullErr;
 
    myWindow = NewCWindow(NULL, &myBounds, myTitle, false, 0, (WindowPtr)-1, false, 0);
    if (myWindow == NULL)
        goto bail;
        
    myErr = noErr;
    
    MacSetPort((GrafPtr)GetWindowPort(myWindow));
 
    GetMovieBox(theMovie, &myRect);
    MacOffsetRect(&myRect, -myRect.left, -myRect.top);
    SetMovieBox(theMovie, &myRect);
 
    if (!EmptyRect(&myRect))
        SizeWindow(myWindow, myRect.right, myRect.bottom, false);
    else
        SizeWindow(myWindow, 200, 0, false);
        
    MacShowWindow(myWindow);
 
    SetMovieGWorld(theMovie, GetWindowPort(myWindow), NULL);
    GoToBeginningOfMovie(theMovie);
    MoviesTask(theMovie, 0);
    StartMovie(theMovie);
    
    myErr = GetMoviesError();
    if (myErr != noErr)
        goto bail;
    
    while (!IsMovieDone(theMovie))
        MoviesTask(theMovie, 0);
 
bail:
    free(myTitle);
    
    if (theMovie != NULL)
        DisposeMovie(theMovie);
 
    if (myWindow != NULL)
        DisposeWindow(myWindow);
        
    return(myErr);
}
 
 
//////////
//
// QTDR_CreateMovieInRAM
// Create a movie in RAM.
//
//////////
 
OSErr QTDR_CreateMovieInRAM (void)
{
    Movie                   myMovie = NULL;
    Track                   myTrack = NULL;
    Media                   myMedia = NULL;
    short                   myResRefNum = 0;
    short                   myResID = 0;
    Handle                  myDataRef = NULL;
    Handle                  myHandle = NULL;
    FSSpec                  myFSSpec;
    OSErr                   myErr = noErr;
    
    // create a new handle to hold the media data
    myHandle = NewHandleClear(0);
    if (myHandle == NULL)
        goto bail;
    
    // create a data reference to that handle
    myDataRef = QTDR_MakeHandleDataRef(myHandle);
    if (myDataRef == NULL)
        goto bail;
        
    myMovie = NewMovie(newMovieActive);
    if (myMovie == NULL)
        goto bail;
    
    myErr = SetMovieDefaultDataRef(myMovie, myDataRef, HandleDataHandlerSubType);
    if (myErr != noErr)
        goto bail;
 
    // create the movie track and media
    myTrack = NewMovieTrack(myMovie, FixRatio(kVideoTrackWidth, 1), FixRatio(kVideoTrackHeight, 1), kNoVolume);
    myErr = GetMoviesError();
    if (myErr != noErr)
        goto bail;
        
    myMedia = NewTrackMedia(myTrack, VideoMediaType, kVideoTimeScale, NULL, 0);
    myErr = GetMoviesError();
    if (myErr != noErr)
        goto bail;
 
    // create the media samples
    myErr = BeginMediaEdits(myMedia);
    if (myErr != noErr)
        goto bail;
 
    myErr = QTDR_AddVideoSamplesToMedia(myMedia, kVideoTrackWidth, kVideoTrackHeight);
    if (myErr != noErr)
        goto bail;
 
    myErr = EndMediaEdits(myMedia);
    if (myErr != noErr)
        goto bail;
    
    // add the media to the track
    myErr = InsertMediaIntoTrack(myTrack, 0, 0, GetMediaDuration(myMedia), fixed1);
    if (myErr != noErr)
        goto bail;
    
    // add the movie atom to the movie file
    myErr = AddMovieResource(myMovie, myResRefNum, &myResID, NULL);
 
    myFSSpec.name[0] = (unsigned char)0;
    myFSSpec.parID = 0;
    myFSSpec.vRefNum = 0;
    
    QTFrame_OpenMovieInWindow(myMovie, &myFSSpec);
 
bail:
    if (myDataRef != NULL)
        DisposeHandle(myDataRef);
        
    return(myErr);
}
 
 
//////////
//
// QTDR_CreateTrackInRAM
// Create a track in RAM, with the media data stored in an initialization data data reference extension.
//
//////////
 
OSErr QTDR_CreateTrackInRAM (Movie theMovie)
{
    Track                   myTrack = NULL;
    Media                   myMedia = NULL;
    Handle                  myDataRef = NULL;
    unsigned long           myAtomHeader[2];
    OSErr                   myErr = noErr;
 
    if (theMovie == NULL)
        return(paramErr);
    
    myDataRef = NewHandleClear(sizeof(Handle) + sizeof(char));
    if (myDataRef == NULL)
        return(MemError());
    
    myAtomHeader[0] = EndianU32_NtoB(sizeof(myAtomHeader));
    myAtomHeader[1] = EndianU32_NtoB(kDataRefExtensionInitializationData);
 
    myErr = PtrAndHand(myAtomHeader, myDataRef, sizeof(myAtomHeader));
    if (myErr != noErr)
        goto bail;
        
    // create the movie track and media
    myTrack = NewMovieTrack(theMovie, FixRatio(kVideoTrackWidth, 1), FixRatio(kVideoTrackHeight, 1), kNoVolume);
    myErr = GetMoviesError();
    if (myErr != noErr)
        goto bail;
        
    myMedia = NewTrackMedia(myTrack, VideoMediaType, kVideoTimeScale, myDataRef, HandleDataHandlerSubType);
    myErr = GetMoviesError();
    if (myErr != noErr)
        goto bail;
 
    // create the media samples
    myErr = BeginMediaEdits(myMedia);
    if (myErr != noErr)
        goto bail;
 
    myErr = QTDR_AddVideoSamplesToMedia(myMedia, kVideoTrackWidth, kVideoTrackHeight);
    if (myErr != noErr)
        goto bail;
 
    myErr = EndMediaEdits(myMedia);
    if (myErr != noErr)
        goto bail;
    
    // add the media to the track
    myErr = InsertMediaIntoTrack(myTrack, 0, 0, GetMediaDuration(myMedia), fixed1);
    
bail:
    if (myDataRef != NULL)
        DisposeHandle(myDataRef);
 
    return(myErr);
}
 
 
//////////
//
// QTDR_AddVideoSamplesToMedia
// Add video media samples to the specified media.
//
//////////
 
static OSErr QTDR_AddVideoSamplesToMedia (Media theMedia, short theTrackWidth, short theTrackHeight)
{
    GWorldPtr                   myGWorld = NULL;
    PixMapHandle                myPixMap = NULL;
    CodecType                   myCodecType = kJPEGCodecType;
    long                        myNumSample;
    long                        myMaxComprSize = 0L;
    Handle                      myComprDataHdl = NULL;
    Ptr                         myComprDataPtr = NULL;
    ImageDescriptionHandle      myImageDesc = NULL;
    CGrafPtr                    mySavedPort = NULL;
    GDHandle                    mySavedDevice = NULL;
    Rect                        myRect;
    OSErr                       myErr = noErr;
 
    MacSetRect(&myRect, 0, 0, theTrackWidth, theTrackHeight);
 
    myErr = NewGWorld(&myGWorld, kPixelDepth, &myRect, NULL, NULL, (GWorldFlags)0);
    if (myErr != noErr)
        goto bail;
 
    myPixMap = GetGWorldPixMap(myGWorld);
    if (myPixMap == NULL)
        goto bail;
 
    LockPixels(myPixMap);
    myErr = GetMaxCompressionSize(  myPixMap,
                                    &myRect, 
                                    0,                          // let ICM choose depth
                                    codecNormalQuality, 
                                    myCodecType, 
                                    (CompressorComponent)anyCodec,
                                    &myMaxComprSize);
    if (myErr != noErr)
        goto bail;
 
    myComprDataHdl = NewHandle(myMaxComprSize);
    if (myComprDataHdl == NULL)
        goto bail;
 
    HLockHi(myComprDataHdl);
#if TARGET_CPU_68K
    myComprDataPtr = StripAddress(*myComprDataHdl);
#else
    myComprDataPtr = *myComprDataHdl;
#endif
 
    myImageDesc = (ImageDescriptionHandle)NewHandle(4);
    if (myImageDesc == NULL)
        goto bail;
 
    GetGWorld(&mySavedPort, &mySavedDevice);
    SetGWorld(myGWorld, NULL);
 
    for (myNumSample = 1; myNumSample <= kNumVideoFrames; myNumSample++) {
        EraseRect(&myRect);
        
        QTDR_DrawFrame(theTrackWidth, theTrackHeight, myNumSample, myGWorld);
 
        myErr = CompressImage(  myPixMap, 
                                &myRect, 
                                codecNormalQuality,
                                myCodecType,
                                myImageDesc, 
                                myComprDataPtr);
        if (myErr != noErr)
            goto bail;
 
        myErr = AddMediaSample( theMedia, 
                                myComprDataHdl,
                                0,                              // no offset in data
                                (**myImageDesc).dataSize, 
                                kVideoFrameDuration,            // frame duration
                                (SampleDescriptionHandle)myImageDesc, 
                                1,                              // one sample
                                0,                              // self-contained samples
                                NULL);
        if (myErr != noErr)
            goto bail;
    }
 
bail:
    SetGWorld(mySavedPort, mySavedDevice);
 
    if (myImageDesc != NULL)
        DisposeHandle((Handle)myImageDesc);
 
    if (myComprDataHdl != NULL)
        DisposeHandle(myComprDataHdl);
 
    if (myGWorld != NULL)
        DisposeGWorld(myGWorld);
        
    return(myErr);
}
 
 
//////////
//
// QTDR_DrawFrame
// Draw a frame of video.
//
//////////
 
static void QTDR_DrawFrame (short theTrackWidth, short theTrackHeight, long theNumSample, GWorldPtr theGWorld)
{
    Handle                              myHandle = NULL;
    char                                myData[kPICTFileHeaderSize];
    static PicHandle                    myPicture = NULL;
    static GWorldPtr                    myGWorld = NULL;
    static GraphicsImportComponent      myImporter = NULL;
    Rect                                myRect;
    RGBColor                            myColor;
    ComponentResult                     myErr = noErr;
 
    MacSetRect(&myRect, 0, 0, theTrackWidth, theTrackHeight);
 
    if (myPicture == NULL) {
        myErr = NewGWorld(&myGWorld, kPixelDepth, &myRect, NULL, NULL, (GWorldFlags)0);
        if (myErr != noErr)
            goto bail;
 
        // read a picture from our resource file
        myPicture = GetPicture(kPictureID);
        if (myPicture == NULL)
            goto bail;
 
        // use Munger to prepend a 512-byte header onto the picture data; this converts the PICT
        // resource data into in-memory PICT file data (see Ice Floe 14 for an explanation of this)
        myHandle = (Handle)myPicture;
        Munger(myHandle, 0, NULL, 0, myData, kPICTFileHeaderSize);
 
        // get a graphics importer for the picture
        myErr = OpenADefaultComponent(GraphicsImporterComponentType, kQTFileTypePicture, &myImporter); 
        if (myErr != noErr)
            goto bail;
                
        // configure the graphics importer
        myErr = GraphicsImportSetGWorld(myImporter, myGWorld, NULL);
        if (myErr != noErr)
            goto bail;
        
        myErr = GraphicsImportSetDataHandle(myImporter, myHandle);
        if (myErr != noErr)
            goto bail;
            
        myErr = GraphicsImportSetBoundsRect(myImporter, &myRect);
        if (myErr != noErr)
            goto bail;
            
        // draw the picture into the source GWorld
        myErr = GraphicsImportDraw(myImporter);
        if (myErr != noErr)
            goto bail;
    }
    
    // set the blend amount (0 = fully transparent; 0xffff = fully opaque)
    myColor.red = (theNumSample - 1) * (0xffff / kNumVideoFrames - 1);
    myColor.green = (theNumSample - 1) * (0xffff / kNumVideoFrames - 1);
    myColor.blue = (theNumSample - 1) * (0xffff / kNumVideoFrames - 1);
    OpColor(&myColor);
    
    // blend the picture (in the source GWorld) into the empty rectangle (in the destination GWorld)
    CopyBits((BitMapPtr)*GetGWorldPixMap(myGWorld),
             (BitMapPtr)*GetGWorldPixMap(theGWorld),
             &myRect,
             &myRect,
             blend,
             NULL);
 
    if (theNumSample == kNumVideoFrames)
        goto bail;
        
    return;
    
bail:
    if (myHandle != NULL)
        DisposeHandle(myHandle);
        
    if (myPicture != NULL)
        ReleaseResource((Handle)myPicture);
        
    if (myImporter != NULL)
        CloseComponent(myImporter);
} 
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Utility functions.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// QTDR_IsMovieSelfContained
// Is the specified movie a self-contained movie?
//
//////////
 
Boolean QTDR_IsMovieSelfContained (Movie theMovie)
{
    long        myTrackCount = 0L;
    long        myTrackIndex = 0L;
    short       myMediaRefCount = 0L;
    short       myMediaRefIndex = 0L;
    Media       myMedia = NULL;
    long        myAttrs = 0L;
    OSErr       myErr = noErr;
 
    myTrackCount = GetMovieTrackCount(theMovie);
    for (myTrackIndex = 1; myTrackIndex <= myTrackCount; myTrackIndex++) {
        myMedia = GetTrackMedia(GetMovieIndTrack(theMovie, myTrackIndex));
        if (myMedia != NULL) {
            myErr = GetMediaDataRefCount(myMedia, &myMediaRefCount);
            if (myErr == noErr) {
                for (myMediaRefIndex = 1; myMediaRefIndex <= myMediaRefCount; myMediaRefIndex++) {
                    myErr = GetMediaDataRef(myMedia, myMediaRefIndex, NULL, NULL, &myAttrs);
                    if (myErr == noErr) {
                        if (!(myAttrs & dataRefSelfReference))
                            return(false);
                    }
                }
            }
        }
    }
 
    return(true);
}