sources/UsherNew.c

/*
    File:       UsherNew.c
 
    Copyright:  © 2000-2001 by Apple Computer, Inc., all rights reserved.
 
 
*/
 
#include <ConditionalMacros.h>
#include <Files.h>
#include <Resources.h>
#include <TextUtils.h>
#include <ToolUtils.h>
 
#include <stdlib.h>
#include <string.h>
 
#include <QuicktimeComponents.h>
#include <MoviesFormat.h>
#include <Movies.h>
 
#include <QuickTimeStreaming.h>
#include <QTStreamingComponents.h>
 
#include "AppSupport.h"
#include "QTSSampleCodeUtils.h"
#include "UsherBroadcast.h"
#include "UsherCommands.h"
#include "UsherNew.h"
#include "WindowSupport.h"
 
// ----- source handlers we know about -----
 
#include "MovieSourcing.h"
 
 
// ---------------------------------------------------------------------------
//      D E F I N I T I O N S
// ---------------------------------------------------------------------------
 
 
struct UsherSettingsStuff {
    Boolean     doSourcerSwitching;
 
 
    // stored movie settings
    Boolean                     doStoredMovies;
    MovieSourcingSettings       movieSourcingSettings;
    
 
    UInt16                      width;
    UInt16                      height;
    
    
    Boolean                     haveSpatialSettings;
    SCSpatialSettings           spatialSettings;
    
    
    Boolean                     haveTemporalSettings;
    SCTemporalSettings          temporalSettings;
    
    Boolean                     haveDataRateSettings;
    SCDataRateSettings          dataRateSettings;
};
typedef struct UsherSettingsStuff UsherSettingsStuff;
 
 
// ---------------------------------------------------------------------------
//      P R O T O T Y P E S
// ---------------------------------------------------------------------------
 
 
static OSErr Usher_GetBroadcastSettingsFromFile(const FSSpec *inFileSpec,
                        UsherSettingsStuff *outSettingsStuff);
 
static OSErr Usher_MakeSourceHandlersFromSettings(UsherBroadcast *inBroadcast,
                UsherSettingsStuff *inSettingsStuff, SourceMediaParams *inMediaParams);
 
 
static OSErr GetFileExtension(const FSSpec *inSpec, OSType *outExtension);
static OSErr Usher_DoXMLStuff(Handle inDataHandle, UsherSettingsStuff *outSettingsStuff);
 
static pascal ComponentResult UsherXML_StartElementHandler(const char *inName,
                                            const char **inAttributes, long inRefcon);
 
static ComponentResult UsherXML_ParseDimensions(UsherSettingsStuff *inSettingsStuff, const char **inAttributes);
 
// ---------------------------------------------------------------------------
//      Usher_NewBroadcastFromFile
// ---------------------------------------------------------------------------
 
OSErr Usher_NewBroadcastFromFile(const FSSpec *inFileSpec, OSType inWindowType)
{
    OSErr               err = noErr;
    WindowPtr           window = NULL;
    UsherBroadcast      *broadcast = 0;
    UsherSettingsStuff  settingsStuff;
    SourceMediaParams   sourceMediaParams;
    OSErr               tempErr;
    Boolean             addedWindow = false;
    Boolean             useLargeSize = false;
 
    memset(&settingsStuff, 0, sizeof(settingsStuff));
    EXITIFERR( err = Usher_GetBroadcastSettingsFromFile(inFileSpec, &settingsStuff) );
    if ((settingsStuff.width != 0)  &&  (settingsStuff.height != 0))  {
        if ((settingsStuff.width > 176)  ||  (settingsStuff.height > 144))  {
            useLargeSize = true;
        }
    }
 
    memset(&sourceMediaParams, 0, sizeof(sourceMediaParams));
    EXITIFERR( err = WindowSupport_NewWindowOfType(inWindowType, useLargeSize, &window) );
 
    // if there's an error in here, the presentation will take care
    // of disposing the window
    tempErr = WindowSupport_HandleMessage(window, kMessage_GetPresBox, &sourceMediaParams.localDisplayRect);
    sourceMediaParams.gWorld = GetWindowPort(window);
    sourceMediaParams.gdHandle = GetMainDevice();   //@@@
 
    EXITIFERR( err = UsherBroadcast_NewFromFile(inFileSpec, sourceMediaParams.gWorld,
                sourceMediaParams.gdHandle, &sourceMediaParams.localDisplayRect, &broadcast) );
    
    EXITIFERR( err = Usher_MakeSourceHandlersFromSettings(broadcast, &settingsStuff, &sourceMediaParams) );
 
    EXITIFERR( err = WindowSupport_HandleMessage(window, kMessage_SetUsherBroadcast, (void*)broadcast) );
    UsherBroadcast_AddWindow(broadcast, window);
    addedWindow = true;
    MacShowWindow(window);
 
exit:
    if (err != noErr)  {
        char        tempString[256];
 
        if (broadcast != NULL)  {
            UsherBroadcast_Dispose(broadcast);
        }
        if (!addedWindow  &&  (window != NULL))  {
            WindowSupport_CloseWindow(window);
        }
        CopyPToCStr(inFileSpec->name, tempString);
        ProcessUserMessage(kProcessMessageFlag_Log | kProcessMessageFlag_ShowDialog,
                "Error (%ld) creating new broadcast from file '%s'", err, tempString);
 
    }
    return err;
}
 
 
// ---------------------------------------------------------------------------
//      Usher_MakeSourceHandlersFromSettings
// ---------------------------------------------------------------------------
 
static OSErr Usher_MakeSourceHandlersFromSettings(UsherBroadcast *inBroadcast,
                UsherSettingsStuff *inSettingsStuff, SourceMediaParams *inMediaParams)
{
    OSErr               err = noErr;
    void                *sourceHandler = NULL;
    QTSPresentation     presentation;
 
#define kDefaultMoviePlayInterval       (30 * 60)
#define kDefaultPushFramePlayInterval   (5 * 60)
 
    presentation = UsherBroadcast_GetQTSPresentation(inBroadcast);
 
 
    if (inSettingsStuff->doStoredMovies)  {
        EXITIFERR( err = MovieSourcing_New(presentation, inMediaParams, &inSettingsStuff->movieSourcingSettings, (MovieSourcing**)&sourceHandler) );    
    }
 
    if (sourceHandler != NULL)  {
        EXITIFERR( err = UsherBroadcast_AddSourceHandler(inBroadcast, sourceHandler) );
    }
 
exit:
    return err;
}
 
// ---------------------------------------------------------------------------
//      Usher_GetBroadcastSettingsFromFile
// ---------------------------------------------------------------------------
 
static OSErr Usher_GetBroadcastSettingsFromFile(const FSSpec *inFileSpec,
                        UsherSettingsStuff *outSettingsStuff)
{
    OSErr       err = noErr;
    OSErr       tempErr = noErr;
    Handle      dataHandle = NULL;
    Ptr         dataPtr;
    UInt32      maxLength;
    const char  *current;
    const char  *more;
 
#define kMaxContentsToScan      (5*1024)
    
    memset(outSettingsStuff, 0, sizeof(UsherSettingsStuff));
    EXITIFERR( tempErr = GetFileContents(inFileSpec, kMaxContentsToScan, &dataHandle, NULL) );
    HLock(dataHandle);
    dataPtr = *dataHandle;
    maxLength = GetHandleSize(dataHandle);
 
 
 
#define kSendPresSDPString_DoStoredMovies       "a=x-usher-play-stored-movies"
#define kSendPresSDPString_LoopMovie            "a=x-usher-loop-movie"
#define kSendPresSDPString_MovieFolder          "a=x-usher-movie-folder:"
#define kSendPresSDPString_MovieSelection       "a=x-usher-movie-selection:"
#define kSendPresMovieSelectionParams_Random    "random"
#define kSendPresMovieSelectionParams_InOrder   "inorder"
 
    // ----- stored movie settings 
    
    current = ScanToString(dataPtr, maxLength, kSendPresSDPString_DoStoredMovies, strlen(kSendPresSDPString_DoStoredMovies));
    if ((current != NULL)  &&
        ( (current == dataPtr)  ||  IsEOLChar(current[-1]) ) )  {
    
        outSettingsStuff->doStoredMovies = true;
 
        current = ScanToString(dataPtr, maxLength, kSendPresSDPString_LoopMovie, strlen(kSendPresSDPString_LoopMovie));
        if ((current != NULL)  && ((current == dataPtr)  ||  IsEOLChar(current[-1])))  {
            outSettingsStuff->movieSourcingSettings.loop = true;
        }
        
        current = ScanToString(dataPtr, maxLength, kSendPresSDPString_MovieFolder, strlen(kSendPresSDPString_MovieFolder));
        if ((current != NULL)  && ((current == dataPtr)  ||  IsEOLChar(current[-1])))  {
            // the next thing is the folder path
            current += strlen(kSendPresSDPString_MovieFolder);
            more = GetLineEnd(current, maxLength - (current-dataPtr));
            if ((more != NULL)  &&  (more != current))  {
                // the folder path is relative to the app; aliases are ok
            
            
            }
        }
    
        current = ScanToString(dataPtr, maxLength, kSendPresSDPString_MovieSelection, strlen(kSendPresSDPString_MovieSelection));
        if ((current != NULL)  && ((current == dataPtr)  ||  IsEOLChar(current[-1])))  {
            current += strlen(kSendPresSDPString_MovieSelection);
            more = GetLineEnd(current, maxLength - (current-dataPtr));
            if ((more != NULL)  &&  (more != current))  {
                // this must match 
                if (StartsWith(more, maxLength - (more-dataPtr), kSendPresMovieSelectionParams_Random, strlen(kSendPresMovieSelectionParams_Random)))  {
                    outSettingsStuff->movieSourcingSettings.selectRandomly = true;
                }
            }
        }
    }
    
 
    {
        OSType      extension = 0;
        
        if (GetFileExtension(inFileSpec, &extension) == noErr)  {
            if ((extension == 'xml ')  ||  (extension == 'XML '))  {
                Usher_DoXMLStuff(dataHandle, outSettingsStuff);
            }
        }
    }
 
exit:
    DisposeHandle(dataHandle);
    return err;
}
 
#pragma mark -
 
//-----------------------------------------------------------------------------
//      GetFileExtension
//-----------------------------------------------------------------------------
 
static OSErr GetFileExtension(const FSSpec *inSpec, OSType *outExtension)
{
    UInt32          current;
    UInt32          length;
    Boolean         foundPeriod = false;
    OSErr           err = paramErr;
    
    #define kPeriodChar     '.'
    #define kSpacePadding   0x20202020
 
    // parse backwards until you get to a period
    *outExtension = 0;
    length = inSpec->name[0];
    current = length;
    while (current > 0)  {
        if (inSpec->name[current] == kPeriodChar)  {
            // found a period - stop
            foundPeriod = true;
            break;
        }
        --current;
    }
    // if you found one and it was within 5 of the end of the name, you
    // have an extension
    if (foundPeriod)  {
        if (current >= length - sizeof(OSType))  {
            *outExtension = kSpacePadding;
            BlockMoveData(inSpec->name + current + 1,
                            outExtension, (long)(length-current));
            *outExtension = EndianU32_NtoB( *outExtension );
            err = noErr;
        }
    }
    return err;
}
 
static QTSNotificationUPP sXMLStartElementUPP = NULL;
 
 
// ---------------------------------------------------------------------------
//      Usher_DoXMLStuff
// ---------------------------------------------------------------------------
 
static OSErr Usher_DoXMLStuff(Handle inDataHandle, UsherSettingsStuff *outSettingsStuff)
{
    OSErr                   err = noErr;
    ComponentInstance       xmlParser = 0;
    Handle                  hdataref = NULL;
    
    if (sXMLStartElementUPP == NULL)  {
        sXMLStartElementUPP = (StartElementHandlerUPP)NewStartElementHandlerUPP(UsherXML_StartElementHandler);
    }
 
    EXITIFERR( err = OpenADefaultComponent(xmlParseComponentType, xmlParseComponentSubType, &xmlParser) );
    XMLParseSetEventParseRefCon(xmlParser, (long)(outSettingsStuff));
    XMLParseSetStartElementHandler(xmlParser, sXMLStartElementUPP);
 
    hdataref = NewHandle(sizeof(HandleDataRefRecord));
    if (hdataref != NULL)  {
        (**((HandleDataRef)hdataref)).dataHndl = inDataHandle;
        EXITIFERR( err = XMLParseDataRef(xmlParser, hdataref,
                    HandleDataHandlerSubType, xmlParseFlagEventParseOnly, NULL) );
    }
 
exit:
    if (xmlParser != 0)  {
        CloseComponent(xmlParser);
    }
    if (hdataref != NULL)  {
        DisposeHandle(hdataref);
    }
    return err;
}
 
 
// ---------------------------------------------------------------------------
//      XMLStreamImport_StartElementHandler
// ---------------------------------------------------------------------------
 
static pascal ComponentResult UsherXML_StartElementHandler(const char *inName,
                                            const char **inAttributes, long inRefcon)
{
    ComponentResult     err = noErr;
    UsherSettingsStuff      *settingsStuff = (UsherSettingsStuff*)inRefcon;
    long                    nameLength;
 
 
#define kXMLElement_Dimensions      "dimensions"
#define kXMLElement_Compression     "compression"
 
    nameLength = strlen(inName);
    if (EqualChars(kXMLElement_Dimensions, strlen(kXMLElement_Dimensions), inName, nameLength))  {
        err = UsherXML_ParseDimensions(settingsStuff, inAttributes);
 
    }
 
exit:
    return err;
}
 
// ---------------------------------------------------------------------------
//      UsherXML_ParseDimensions
// ---------------------------------------------------------------------------
 
static ComponentResult UsherXML_ParseDimensions(UsherSettingsStuff *inSettingsStuff, const char **inAttributes)
{
    ComponentResult     err = noErr;
    UInt32              index;
    const char          *attribute;
    const char          *value;
    long                attrLength;
    
#define kXMLAttr_Width      "width"
#define kXMLAttr_Height     "height"    
        
    index = 0;
    while (inAttributes[index] != NULL)  {
        attribute = inAttributes[index];
        value = inAttributes[index+1];
        attrLength = strlen(attribute);
        
        if (EqualChars(kXMLAttr_Width, strlen(kXMLAttr_Width), attribute, attrLength))  {
            inSettingsStuff->width = atol(value);
        }  else if (EqualChars(kXMLAttr_Height, strlen(kXMLAttr_Height), attribute, attrLength))  {
            inSettingsStuff->height = atol(value);
        }  else  {
            //DEBUGF(("XMLStreamImport_ParseRTPMap $%.8x-unhandled '%s'", inInfo, attribute));
        }
        index += 2;
    }
exit:
    return err;
}