示例#1
0
void AbstractHost::processPackage()
{
	Package *constructedPackage = new Package(parseBuffer);
	if(!constructedPackage->isValid())
	{
		sendPackage(constructAcknowledgementPackage(constructedPackage, "invalid"));
		delete constructedPackage;
	}
	else if(!server->packageRouterInstance()->isAllowed(type, constructedPackage))
	{
		sendPackageAndDelete(constructAcknowledgementPackage(constructedPackage, "forbidden"));
		delete constructedPackage;
	}
	else if(constructedPackage->getType() == ConnectionManagement)
	{
		updateNextPackageID(constructedPackage->getID());
		handlePackage(constructedPackage);
	}
	else if(state == Established)
	{
		updateNextPackageID(constructedPackage->getID());
		server->packageRouterInstance()->processPackage(this, constructedPackage);
	}
	else
	{
		sendPackage(constructAcknowledgementPackage(constructedPackage, "invalid"));
		delete constructedPackage;
	}


	parseBuffer.clear();
}
示例#2
0
int main(int argc, const char* argv[])
{
    int cmdlnIndex = 1;
    string filenamePrefix = g_filenamePrefix;
    bool createAAFGroup = false;
    bool createAAFGroupOnly = false;
    bool createMultiCam = false;
    bool aafxml = false;
    bool fcpxml = false;
    bool audioEdits = false;
    Date fromDate;
    Date toDate;
    int64_t fromTimecode = 0;
    int64_t toTimecode = 0;
    string dbHostName = g_databaseHostName;
    string dbName = g_databaseName;
    string dbUserName = g_databaseUserName;
    string dbPassword = g_databasePassword;
    string tagName;
    string tagValue;
    string suffix;
    string projName;
    vector<string> filenames;
    int videoResolutionID = DV50_MATERIAL_RESOLUTION;
    bool verbose = false;
    bool noTSSuffix = false;
    string mcCutsFilename;
    bool includeMCCutsSequence = false;
    CutsDatabase* mcCutsDatabase = 0;
    Timestamp fromCreationDate = g_nullTimestamp;
    string fcpPath;
    bool isPAL = true;
    const char* toString = 0;
    const char* fromString = 0;
    Rational targetEditRate = g_palEditRate;
    
    std::vector<long> package_ids;
    long pid;
    
    std::vector<std::string> pkgid;
    
    string xmlCommandPath;
    
    Timestamp t = generateTimestampStartToday();
    fromDate.year = t.year;
    fromDate.month = t.month;
    fromDate.day = t.day;
    t = generateTimestampStartTomorrow();
    toDate.year = t.year;
    toDate.month = t.month;
    toDate.day = t.day;


    // parse command line arguments
    try
    {
        while (cmdlnIndex < argc)
        {
            if (strcmp(argv[cmdlnIndex], "-h") == 0 ||
                strcmp(argv[cmdlnIndex], "--help") == 0)
            {
                usage(argv[0]);
                return 0;
            }
            else if (strcmp(argv[cmdlnIndex], "--xml-command") == 0)
            {
                if (cmdlnIndex + 1 >= argc)
                {
                    usage(argv[0]);
                    fprintf(stderr, "Missing argument for %s\n", argv[cmdlnIndex]);
                    return 1;
                }
                xmlCommandPath = argv[cmdlnIndex + 1];
                cmdlnIndex += 2;
            }
            else if (strcmp(argv[cmdlnIndex], "-v") == 0 ||
                strcmp(argv[cmdlnIndex], "--verbose") == 0)
            {
                verbose = true;
                cmdlnIndex++;
            }
            else if (strcmp(argv[cmdlnIndex], "-p") == 0 ||
                strcmp(argv[cmdlnIndex], "--prefix") == 0)
            {
                if (cmdlnIndex + 1 >= argc)
                {
                    usage(argv[0]);
                    fprintf(stderr, "Missing argument for %s\n", argv[cmdlnIndex]);
                    return 1;
                }
                filenamePrefix = argv[cmdlnIndex + 1];
                cmdlnIndex += 2;
            }
            else if (strcmp(argv[cmdlnIndex], "-r") == 0 ||
                strcmp(argv[cmdlnIndex], "--resolution") == 0)
            {
                if (cmdlnIndex + 1 >= argc)
                {
                    usage(argv[0]);
                    fprintf(stderr, "Missing argument for --resolution\n");
                    return 1;
                }
                if (sscanf(argv[cmdlnIndex + 1], "%d\n", &videoResolutionID) != 1)
                {
                    usage(argv[0]);
                    fprintf(stderr, "Invalid argument for --resolution\n");
                    return 1;
                }
                cmdlnIndex += 2;
            }
            else if (strcmp(argv[cmdlnIndex], "-g") == 0 ||
                strcmp(argv[cmdlnIndex], "--group") == 0)
            {
                createAAFGroup = true;
                cmdlnIndex += 1;
            }
            else if (strcmp(argv[cmdlnIndex], "-o") == 0 ||
                strcmp(argv[cmdlnIndex], "--grouponly") == 0)
            {
                createAAFGroupOnly = true;
                createAAFGroup = true;
                cmdlnIndex += 1;
            }
            else if (strcmp(argv[cmdlnIndex], "--no-ts-suffix") == 0)
            {
                noTSSuffix = true;
                cmdlnIndex++;
            }
            else if (strcmp(argv[cmdlnIndex], "-m") == 0 ||
                strcmp(argv[cmdlnIndex], "--multicam") == 0)
            {
                createMultiCam = true;
                cmdlnIndex += 1;
            }
            else if (strcmp(argv[cmdlnIndex], "--ntsc") == 0)
            {
                isPAL = false;
                cmdlnIndex += 1;
            }
            else if (strcmp(argv[cmdlnIndex], "-f") == 0 ||
                strcmp(argv[cmdlnIndex], "--from") == 0)
            {
                if (cmdlnIndex + 1 >= argc)
                {
                    usage(argv[0]);
                    fprintf(stderr, "Missing argument for %s\n", argv[cmdlnIndex]);
                    return 1;
                }
                fromString = argv[cmdlnIndex + 1];
                cmdlnIndex += 2;
            }
            else if (strcmp(argv[cmdlnIndex], "-t") == 0 ||
                strcmp(argv[cmdlnIndex], "--to") == 0)
            {
                if (cmdlnIndex + 1 >= argc)
                {
                    usage(argv[0]);
                    fprintf(stderr, "Missing argument for %s\n", argv[cmdlnIndex]);
                    return 1;
                }
                toString = argv[cmdlnIndex + 1];
                cmdlnIndex += 2;
            }
            else if (strcmp(argv[cmdlnIndex], "-c") == 0 ||
                strcmp(argv[cmdlnIndex], "--from-cd") == 0)
            {
                if (cmdlnIndex + 1 >= argc)
                {
                    usage(argv[0]);
                    fprintf(stderr, "Missing argument for %s\n", argv[cmdlnIndex]);
                    return 1;
                }
                parseTimestamp(argv[cmdlnIndex + 1], &fromCreationDate);
                cmdlnIndex += 2;
            }
   //????         
            else if (strcmp(argv[cmdlnIndex], "-w") == 0 ||
                strcmp(argv[cmdlnIndex], "--from-web") == 0)
            {
                if (cmdlnIndex + 1 >= argc)
                {
                    usage(argv[0]);
                    fprintf(stderr, "Missing argument for %s\n", argv[cmdlnIndex]);
                    return 1;
                }
                pid = atol(argv[cmdlnIndex + 1]);
                package_ids.push_back(pid);
                //parseWeb(argv[cmdlnIndex + 1], package_ids, pid);
                cmdlnIndex += 2;
   //????    
            }
            else if (strcmp(argv[cmdlnIndex], "--tag") == 0)
            {
                if (cmdlnIndex + 1 >= argc)
                {
                    usage(argv[0]);
                    fprintf(stderr, "Missing argument for %s\n", argv[cmdlnIndex]);
                    return 1;
                }
                parseTag(argv[cmdlnIndex + 1], tagName, tagValue);
                cmdlnIndex += 2;
            }
            else if (strcmp(argv[cmdlnIndex], "-n") == 0 ||
                strcmp(argv[cmdlnIndex], "--pro-name") == 0)
            {
                if (cmdlnIndex + 1 >= argc)
                {
                    usage(argv[0]);
                    fprintf(stderr, "Missing argument for %s\n", argv[cmdlnIndex]);
                    return 1;
                }
                projName = argv[cmdlnIndex + 1];
                cmdlnIndex += 2;
            }
            else if (strcmp(argv[cmdlnIndex], "--mc-cuts") == 0)
            {
                if (cmdlnIndex + 1 >= argc)
                {
                    usage(argv[0]);
                    fprintf(stderr, "Missing argument for %s\n", argv[cmdlnIndex]);
                    return 1;
                }
                mcCutsFilename = argv[cmdlnIndex + 1];
                includeMCCutsSequence = true;
                cmdlnIndex += 2;
            }
            else if (strcmp(argv[cmdlnIndex], "--mc-cuts-db") == 0)
            {
                includeMCCutsSequence = true;
                cmdlnIndex += 2;
            }
            else if (strcmp(argv[cmdlnIndex], "--dbhost") == 0)
            {
                if (cmdlnIndex + 1 >= argc)
                {
                    usage(argv[0]);
                    fprintf(stderr, "Missing argument for %s\n", argv[cmdlnIndex]);
                    return 1;
                }
                dbHostName = argv[cmdlnIndex + 1];
                cmdlnIndex += 2;
            }
            else if (strcmp(argv[cmdlnIndex], "-d") == 0 ||
                strcmp(argv[cmdlnIndex], "--dbname") == 0)
            {
                if (cmdlnIndex + 1 >= argc)
                {
                    usage(argv[0]);
                    fprintf(stderr, "Missing argument for %s\n", argv[cmdlnIndex]);
                    return 1;
                }
                dbName = argv[cmdlnIndex + 1];
                cmdlnIndex += 2;
            }
            else if (strcmp(argv[cmdlnIndex], "-u") == 0 ||
                strcmp(argv[cmdlnIndex], "--dbuser") == 0)
            {
                if (cmdlnIndex + 1 >= argc)
                {
                    usage(argv[0]);
                    fprintf(stderr, "Missing argument for %s\n", argv[cmdlnIndex]);
                    return 1;
                }
                dbUserName = argv[cmdlnIndex + 1];
                cmdlnIndex += 2;
            }
            else if (strcmp(argv[cmdlnIndex], "--dbpassword") == 0)
            {
                if (cmdlnIndex + 1 >= argc)
                {
                    usage(argv[0]);
                    fprintf(stderr, "Missing argument for %s\n", argv[cmdlnIndex]);
                    return 1;
                }
                dbPassword = argv[cmdlnIndex + 1];
                cmdlnIndex += 2;
            }
            else if (strcmp(argv[cmdlnIndex], "--audio-edits") == 0)
            {
                audioEdits = true;
                cmdlnIndex++;
            }
            else if (strcmp(argv[cmdlnIndex], "--aaf-xml") == 0)
            {
                aafxml = true;
                cmdlnIndex++;
            }
            else if (strcmp(argv[cmdlnIndex], "--fcp-xml") == 0)
            {
                fcpxml = true;
                cmdlnIndex++;
            }
            else if (strcmp(argv[cmdlnIndex], "--fcp-path") == 0)
            {
                if (cmdlnIndex + 1 >= argc)
                {
                    usage(argv[0]);
                    fprintf(stderr, "Missing argument for %s\n", argv[cmdlnIndex]);
                    return 1;
                }
                fcpPath = argv[cmdlnIndex + 1];
                cmdlnIndex += 2;
            }
            else
            {
                usage(argv[0]);
                fprintf(stderr, "Unknown argument '%s'\n", argv[cmdlnIndex]);
                return 1;
            }
        }
    }
    catch (const char*& ex)
    {
        usage(argv[0]);
        fprintf(stderr, "\nFailed to parse arguments:\n%s\n", ex);
        return 1;
    }
    catch (...)
    {
        usage(argv[0]);
        fprintf(stderr, "\nFailed to parse arguments:\nUnknown exception thrown\n");
        return 1;
    }
    
    if (xmlCommandPath.size() != 0)
    {    
        
        ConfigReader appConfig;
        appConfig.readConfigFile(xmlCommandPath);
        cout << "Application verbose= " << appConfig.getVerbose() << endl;
        cout << "Application prefix= "  << appConfig.getPrefix()  << endl;
                            
        std::string test = "TRUE";
        ostringstream test_ss;
        test_ss << test;

        if (appConfig.getVerbose() == test_ss.str())
        {
            verbose = true;
        }
        if (appConfig.getGroupOnly() == test_ss.str())
        {
            createAAFGroupOnly = true;
        }
        if (appConfig.getGroup() == test_ss.str())
        {
            createAAFGroup = true;
        }
	if (appConfig.getMultiCam() == test_ss.str())
        {
	    createMultiCam = true;
        }
        if (appConfig.getNTSC() == test_ss.str())
        {
            isPAL = false;
        }
        if (appConfig.getFCP() == test_ss.str())
        {
            fcpxml = true;
            fcpPath = appConfig.getEditPath();
        }
	if (appConfig.getDirDB() == test_ss.str())
	{
	    includeMCCutsSequence = true;
	}
        if (appConfig.getDirCut() == test_ss.str())
        {
            includeMCCutsSequence = true;
            mcCutsFilename = appConfig.getDirSource();
        }
        if (appConfig.getAudioEdit() == test_ss.str())
        {
            audioEdits = true;
        }
        
        
        dbHostName = appConfig.getDBHostName();
        dbName = appConfig.getDBName();
        dbUserName = appConfig.getUser();
        dbPassword = appConfig.getPassword();
        filenamePrefix = appConfig.getPrefix();

        pkgid = appConfig.getPkgID();
        
        for (std::vector<std::string>::const_iterator it = pkgid.begin(); it != pkgid.end(); ++it)
        {
                ostringstream index_ss;
                index_ss << *it;
                long index = atol(index_ss.str().c_str());
                cout << "Package IDs= " << index << endl;
                package_ids.push_back(index);
        }
        
        if (verbose)
        {
                printf("VERBOSE IS TRUE \n");
        }
    }
    targetEditRate = (isPAL ? g_palEditRate : g_ntscEditRate);
    
    // parse --to and --from now that we know whether it is PAL or NTSC
    if (fromString != 0)
    {
        parseDateAndTimecode(fromString, isPAL, &fromDate, &fromTimecode);
    }
    if (toString != 0)
    {
        parseDateAndTimecode(toString, isPAL, &toDate, &toTimecode);
    }

    if (tagName.size() > 0)
    {
        suffix = createTagSuffix();
    }
    if (package_ids.size() > 0) //???? STILL TODO
    {
        suffix = createIdSuffix();
        //need to make prefix and filename??
    }
    else
    {
        suffix = createDateAndTimecodeSuffix(fromDate, fromTimecode, toDate, toTimecode, isPAL);
    }
    
    
    // initialise the prodauto database
    try
    {
        Database::initialise(dbHostName, dbName, dbUserName, dbPassword, 1, 3);
        if (verbose)
        {
            printf("Initialised the database connection\n");
        }
    }
    catch (DBException& ex)
    {
        fprintf(stderr, "Failed to connect to database:\n  %s\n", ex.getMessage().c_str());
        return 1;
    }

    // open the cuts database file
    if (!mcCutsFilename.empty())
    {
        try
        {
            mcCutsDatabase = new CutsDatabase(mcCutsFilename);
        }
        catch (const ProdAutoException& ex)
        {
            fprintf(stderr, "Failed to open cuts database '%s':\n  %s\n", mcCutsFilename.c_str(), ex.getMessage().c_str());
            return 1;
        }
    }

    // load the material    
    Database* database = Database::getInstance();
    auto_ptr<EditorsFile> editorsFile;
    MaterialHolder material;
    VectorGuard<MCClipDef> mcClipDefs;
    mcClipDefs.get() = database->loadAllMultiCameraClipDefs();
    try
    {
        if (tagName.size() != 0)
        {
            // load all material with tagged value name = tagName and value = tagValue
            database->loadMaterial(tagName.c_str(), tagValue.c_str(), &material.topPackages, &material.packages);
            if (verbose)
            {
                printf("Loaded %d clips from the database based on the tag %s=%s\n", (int)material.topPackages.size(), tagName.c_str(), tagValue.c_str());
            }
        }
        if (package_ids.size() != 0)
        {
            // load all material with material packate database ids
            database->loadMaterial(package_ids, &material.topPackages, &material.packages);
            if (verbose)
            {
                //printf("Loaded %d clips from the database based on the web IDs file %s\n", (int)material.topPackages.size());
                printf("Loaded clip based on package ID from command line.\n");
            }
        }
        else if (fromCreationDate.year != 0)
        {
            Timestamp endDayTo = g_nullTimestamp;
            endDayTo.year = fromCreationDate.year;
            endDayTo.month = fromCreationDate.month;
            endDayTo.day = fromCreationDate.day;
            endDayTo.hour = 23;
            endDayTo.min = 59;
            endDayTo.sec = 59;
            endDayTo.qmsec = 249;
            
            // load all material created >= fromCreationDate until the end of the day
            database->loadMaterial(fromCreationDate, endDayTo, &material.topPackages, &material.packages);

            if (verbose)
            {
                printf("Loaded %d clips from the database from creation date %s\n", (int)material.topPackages.size(), timestampString(fromCreationDate).c_str());
            }
        }
        else
        {
            Timestamp startDayFrom = g_nullTimestamp;
            startDayFrom.year = fromDate.year;
            startDayFrom.month = fromDate.month;
            startDayFrom.day = fromDate.day;
            Timestamp endDayTo = g_nullTimestamp;
            endDayTo.year = toDate.year;
            endDayTo.month = toDate.month;
            endDayTo.day = toDate.day;
            endDayTo.hour = 23;
            endDayTo.min = 59;
            endDayTo.sec = 59;
            endDayTo.qmsec = 249;
            
            // load all material created >= (start of the day) from and < (end of the day) to
            database->loadMaterial(startDayFrom, endDayTo, &material.topPackages, &material.packages);

            if (verbose)
            {
                printf("Loaded %d clips from the database based on the dates alone\n", (int)material.topPackages.size());
            }


           // remove all packages that are < fromTimecode (at fromDate) and > toTimecode (at toDate) and remove all packages that != project name
            vector<prodauto::MaterialPackage *> packagesToErase;
            MaterialPackageSet::const_iterator it;
            for (it = material.topPackages.begin(); it != material.topPackages.end(); ++it)
            {
                MaterialPackage* topPackage = *it;
                
                if (topPackage->creationDate.year == fromDate.year && 
                    topPackage->creationDate.month == fromDate.month && 
                    topPackage->creationDate.day == fromDate.day &&
                    getStartTime(topPackage, material.packages, targetEditRate) < fromTimecode)
                {
                    packagesToErase.push_back(topPackage);
                }
                else if (topPackage->creationDate.year == toDate.year && 
                    topPackage->creationDate.month == toDate.month && 
                    topPackage->creationDate.day == toDate.day &&
                    getStartTime(topPackage, material.packages, targetEditRate) >= toTimecode)
                {
                    packagesToErase.push_back(topPackage);
                }
                // packages != project name
                else if (projName.size() != 0)
                {
                    if (topPackage->projectName.name != projName)
                    {
                        packagesToErase.push_back(topPackage);
                    }
                } 
            }
            vector<prodauto::MaterialPackage*>::const_iterator it2;
            for (it2 = packagesToErase.begin(); it2 != packagesToErase.end(); it2++)
            {
                material.topPackages.erase(*it2);
            }
            if (verbose)
            {
                if (packagesToErase.size() > 0)
                {
                    printf("Removed %zd clips from those loaded that did not match the start timecode range\n", packagesToErase.size());
                }
                else
                {
                    printf("All clips match the start timecode range\n");
                }
            }
        }
    }
    catch (const char*& ex)
    {
        fprintf(stderr, "\nFailed to create:\n%s\n", ex);
        Database::close();
        return 1;
    }
    catch (ProdAutoException& ex)
    {
        fprintf(stderr, "\nFailed to create:\n%s\n", ex.getMessage().c_str());
        Database::close();    
        return 1;
    }
    catch (...)
    {
        fprintf(stderr, "\nFailed to create:\nUnknown exception thrown\n");
        Database::close();    
        return 1;
    }
    
    
    // remove any packages with operational pattern != OP-Atom when creating AAF and
    // an edit rate != the target edit rate and
    // go through the material package -> file package and remove any that reference
    // a file package with a non-zero videoResolutionID and !=  targetVideoResolutionID
    if (package_ids.size() == 0)
    {        
        vector<prodauto::MaterialPackage *> packagesToErase;
        MaterialPackageSet::const_iterator iter1;
        
        for (iter1 = material.topPackages.begin(); iter1 != material.topPackages.end(); iter1++)
        {
            MaterialPackage* topPackage = *iter1;
        
            // check operational pattern
            if (!fcpxml && topPackage->op != OPERATIONAL_PATTERN_ATOM)
            {
                packagesToErase.push_back(topPackage);
                continue;
            }
            
            // check package edit rate        
            Rational packageEditRate = getVideoEditRate(topPackage, material.packages);
            if (packageEditRate != targetEditRate && packageEditRate != g_nullRational)
            {
                packagesToErase.push_back(topPackage);
                continue;
            }
        
            // check video resolution IDs
            vector<Track*>::const_iterator iter2;
            for (iter2 = topPackage->tracks.begin(); iter2 != topPackage->tracks.end(); iter2++)
            {
                Track* track = *iter2;
                
                SourcePackage dummy;
                dummy.uid = track->sourceClip->sourcePackageUID;
                PackageSet::iterator result = material.packages.find(&dummy);
                if (result != material.packages.end())
                {
                    Package* package = *result;
                    
                    if (package->getType() != SOURCE_PACKAGE || package->tracks.size() == 0)
                    {
                        continue;
                    }
                    SourcePackage* sourcePackage = dynamic_cast<SourcePackage*>(package);
                    if (sourcePackage->descriptor->getType() != FILE_ESSENCE_DESC_TYPE)
                    {
                        continue;
                    }
                    
                    FileEssenceDescriptor* fileDescriptor = dynamic_cast<FileEssenceDescriptor*>(
                        sourcePackage->descriptor);
                    if (fileDescriptor->videoResolutionID != 0 && 
                        fileDescriptor->videoResolutionID != videoResolutionID)
                    {
                        // material package has wrong video resolution
                        packagesToErase.push_back(topPackage);
                        break; // break out of track loop
                    }
                }
        
            }
        }    
        vector<prodauto::MaterialPackage *>::const_iterator it;
        for (it = packagesToErase.begin(); it != packagesToErase.end(); it++)
        {
            material.topPackages.erase(*it);
        }
        
        if (verbose)
        {
            if (packagesToErase.size() > 0)
            {
                printf("Removed %zd clips from those loaded that did not match the video resolution\n", packagesToErase.size());
            }
            else
            {
                printf("All clips either match the video resolution or are audio only\n");
            }
        }
    }
    // So we now have the relevant MaterialPackages in...    material.topPackages
    // and relevant SourcePackages in...                     material.packages
        
    
    int totalClips = 0;
    int totalMulticamGroups = 0;
    int totalDirectorsCutsSequences = 0;

    
    // create AAF file if there is material    
    if (material.topPackages.size() == 0)
    {
        // Results
        
        printf("\n%s\n", g_resultsPrefix);
        printf("%d\n%d\n%d\n", totalClips, totalMulticamGroups, totalDirectorsCutsSequences);
    }
    else
    {
        try
        {
            // create FCP or AAF group files
            if (fcpxml)
            {
                editorsFile = auto_ptr<EditorsFile>(new FCPFile(addFilename(filenames, createXMLFilename(filenamePrefix, suffix)), fcpPath));
            }
            else
            {
                if (createAAFGroup)
                {
                    if (createAAFGroupOnly && noTSSuffix)
                    {
                        editorsFile = auto_ptr<EditorsFile>(new AAFFile(
                            addFilename(filenames, createUniqueFilename(filenamePrefix)), targetEditRate, aafxml, audioEdits));
                    }
                    else if (createMultiCam)
                    {
                        editorsFile = auto_ptr<EditorsFile>(new AAFFile(
                            addFilename(filenames, createAAFGroupMCFilename(filenamePrefix, suffix)), targetEditRate, aafxml, audioEdits));
                    }
                    else
                    {
                        editorsFile = auto_ptr<EditorsFile>(new AAFFile(
                            addFilename(filenames, createAAFGroupSingleFilename(filenamePrefix, suffix)), targetEditRate, aafxml, audioEdits));
                    }
                }
            }

            
            // create clips
            
            MaterialPackageSet::iterator iter1;
            int index = 0;
            for (iter1 = material.topPackages.begin(); iter1 != material.topPackages.end(); iter1++)
            {
                MaterialPackage* topPackage = *iter1;
                
                if (!fcpxml && !createAAFGroupOnly)
                {
                    AAFFile aafFile(addFilename(filenames, createSingleClipFilename(filenamePrefix, suffix, index++)), targetEditRate, aafxml, audioEdits);
                    aafFile.addClip(topPackage, material.packages);
                    aafFile.save();
                }
    
                if (createAAFGroup)
                {
                    editorsFile->addClip(topPackage, material.packages);                
                }

                totalClips++;
            }
            
            
            // create multi-camera clips
            // mult-camera clips group packages together which have the same start time and creation date
            
            if (createMultiCam)
            {          
                // load multi-camera clip definitions
                MaterialPackageSet donePackages;
                
                for (iter1 = material.topPackages.begin(); iter1 != material.topPackages.end(); iter1++)
                {
                    MaterialPackage* topPackage1 = *iter1;
        
                    if (!donePackages.insert(topPackage1).second)
                    {
                        // already done this package
                        continue;
                    }
                    
                    MaterialPackageSet materialPackages;
                    materialPackages.insert(topPackage1);

                    int64_t startTimecode = 0;
                    Date startDate;
                    int64_t endTimecode = 0;
                    Date endDate;

                    getStartAndEndTimes(topPackage1, material.packages, targetEditRate,
                        startTimecode, startDate, endTimecode, endDate);
                    
                    // add material to group that has same start time and creation date
                    MaterialPackageSet::iterator iter2;
                    for (iter2 = iter1, iter2++; iter2 != material.topPackages.end(); iter2++)
                    {
                        MaterialPackage* topPackage2 = *iter2;
                        
                        if (topPackage2->creationDate.year == topPackage1->creationDate.year &&
                            topPackage2->creationDate.month == topPackage1->creationDate.month &&
                            topPackage2->creationDate.day == topPackage1->creationDate.day &&
                            getStartTime(topPackage1, material.packages, targetEditRate) == 
                                getStartTime(topPackage2, material.packages, targetEditRate))
                        {
                            materialPackages.insert(topPackage2);
                            donePackages.insert(topPackage2);
                        }
                    }

                    // materialPackages now contains all MaterialPackages with same start time and creation date

                    // Go through all the mc clip defs
                    int index = 0;
            
                    for (std::vector<MCClipDef *>::const_iterator
                        it = mcClipDefs.get().begin(); it != mcClipDefs.get().end(); ++it)
                    {
                        MCClipDef * mcClipDef = *it;
                        vector<CutInfo> sequence;
                        
                        // exclude clip defs referencing sources with wrong video edit rate
                        Rational mcClipDefEditRate = getVideoEditRate(mcClipDef);
                        if (mcClipDefEditRate != targetEditRate && mcClipDefEditRate != g_nullRational)
                        {
                            continue;
                        }
                        
                        if (includeMCCutsSequence && mcCutsDatabase)
                        {
                            // TODO: director's cut database doesn't yet have flag indicating PAL/NTSC
                            sequence = getDirectorsCutsFromFile(mcCutsDatabase, mcClipDef, materialPackages, material.packages, 
                                topPackage1->creationDate, startTimecode);
                        }
                        else if (includeMCCutsSequence)
                        {
                            sequence = getDirectorsCuts(database, mcClipDef, startDate, startTimecode, endDate, endTimecode);
                        }

                        if (!fcpxml && !createAAFGroupOnly)
                        {
                            AAFFile aafFile(addFilename(filenames, createMCClipFilename(filenamePrefix, getStartTime(topPackage1, material.packages, targetEditRate), suffix, index++)), targetEditRate, aafxml, audioEdits); 
                            if (aafFile.addMCClip(mcClipDef, materialPackages, material.packages, sequence))
                            {
                                totalMulticamGroups++;
                            }
                            aafFile.save();
                        }       

                        if (fcpxml || createAAFGroup)
                        {
                            if (editorsFile->addMCClip(mcClipDef, materialPackages, material.packages, sequence))
                            {
                                totalMulticamGroups++;
                            }
                        }
                        
                        totalDirectorsCutsSequences = sequence.empty() ? totalDirectorsCutsSequences : totalDirectorsCutsSequences + 1;
                        printf("Directors cut sequence size = %zd\n", sequence.size());
                    }
                } // iterate over material.topPackages
            }
            
            if (createAAFGroup)
            {
                editorsFile->save();
                delete editorsFile.release();
            }
            // Results
            
            printf("\n%s\n", g_resultsPrefix);
            printf("%d\n%d\n%d\n", totalClips, totalMulticamGroups, totalDirectorsCutsSequences);
            
            vector<string>::const_iterator filenamesIter;
            for (filenamesIter = filenames.begin(); filenamesIter != filenames.end(); filenamesIter++)
            {
                printf("%s\n", (*filenamesIter).c_str());
            }
        }
        catch (const char*& ex)
        {
            fprintf(stderr, "\nFailed to create:\n%s\n", ex);
            Database::close();
            return 1;
        }
        catch (ProdAutoException& ex)
        {
            fprintf(stderr, "\nFailed to create:\n%s\n", ex.getMessage().c_str());
            Database::close();
            return 1;
        }
        catch (...)
        {
            fprintf(stderr, "\nFailed to create:\nUnknown exception thrown\n");
            Database::close();
            return 1;
        }
    }
    Database::close();
    return 0;
}