PsychError SCREENOpenMovie(void)
{
    PsychWindowRecordType                   *windowRecord;
    char                                    *moviefile;
    char                                    *movieOptions;
    char                                    dummmyOptions[1];
    int                                     moviehandle = -1;
    int                                     framecount;
    double                                  durationsecs;
    double                                  framerate;
    double                                  aspectRatio;
    int                                     width;
    int                                     height;
    int                                     asyncFlag = 0;
    int                                     specialFlags1 = 0;
    static psych_bool                       firstTime = TRUE;
    double                                  preloadSecs = 1;
    int                                     rc;
    int                                     pixelFormat = 4;
    int                                     maxNumberThreads = -1;

    if (firstTime) {
        // Setup asyncopeninfo on first invocation:
        firstTime = FALSE;
        asyncmovieinfo.asyncstate = 0; // State = No async open in progress.
    }

    // All sub functions should have these two lines
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()) {PsychGiveHelp(); return(PsychError_none);};

    PsychErrorExit(PsychCapNumInputArgs(8));            // Max. 8 input args.
    PsychErrorExit(PsychRequireNumInputArgs(1));        // Min. 1 input args required.
    PsychErrorExit(PsychCapNumOutputArgs(7));           // Max. 7 output args.

    // Get the window record from the window record argument and get info from the window record
    windowRecord = NULL;
    PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, FALSE, &windowRecord);

    // Only onscreen windows allowed:
    if(windowRecord && !PsychIsOnscreenWindow(windowRecord)) {
        PsychErrorExitMsg(PsychError_user, "OpenMovie called on something else than an onscreen window.");
    }

    // Get the movie name string:
    moviefile = NULL;
    PsychAllocInCharArg(2, kPsychArgRequired, &moviefile);

    // Get the (optional) asyncFlag:
    PsychCopyInIntegerArg(3, FALSE, &asyncFlag);

    PsychCopyInDoubleArg(4, FALSE, &preloadSecs);
    if (preloadSecs < 0 && preloadSecs!= -1 && preloadSecs!= -2) PsychErrorExitMsg(PsychError_user, "OpenMovie called with invalid (negative, but not equal -1) 'preloadSecs' argument!");

    // Get the (optional) specialFlags1:
    PsychCopyInIntegerArg(5, FALSE, &specialFlags1);
    if (specialFlags1 < 0) PsychErrorExitMsg(PsychError_user, "OpenMovie called with invalid 'specialFlags1' setting! Only positive values allowed.");

    // Get the (optional) pixelFormat:
    PsychCopyInIntegerArg(6, FALSE, &pixelFormat);
    if (pixelFormat < 1 || pixelFormat > 10) PsychErrorExitMsg(PsychError_user, "OpenMovie called with invalid 'pixelFormat' setting! Only values 1 to 10 are allowed.");

    // Get the (optional) maxNumberThreads:
    PsychCopyInIntegerArg(7, FALSE, &maxNumberThreads);
    if (maxNumberThreads < -1) PsychErrorExitMsg(PsychError_user, "OpenMovie called with invalid 'maxNumberThreads' setting! Only values of -1 or greater are allowed.");

    // Get the (optional) movie options string: As PsychAllocInCharArg() no-ops if
    // the optional string isn't provided, we need to point movieOptions to an empty
    // 0-terminated string by default, so we don't have a dangling pointer:
    dummmyOptions[0] = 0;
    movieOptions = &dummmyOptions[0];
    PsychAllocInCharArg(8, FALSE, &movieOptions);

    // Queueing of a new movie for seamless playback requested?
    if (asyncFlag & 2) {
        // Yes. Do a special call, just passing the moviename of the next
        // movie to play. Pass the relevant moviehandle as retrieved from
        // preloadSecs:
        moviehandle = (int) preloadSecs;
        preloadSecs = 0;
        PsychCreateMovie(windowRecord, moviefile, preloadSecs, &moviehandle, asyncFlag, specialFlags1, pixelFormat, maxNumberThreads, movieOptions);
        if (moviehandle == -1) PsychErrorExitMsg(PsychError_user, "Could not queue new moviefile for gapless playback.");
        return(PsychError_none);
    }

    // Asynchronous Open operation in progress or requested?
    if ((asyncmovieinfo.asyncstate == 0) && !(asyncFlag & 1)) {
        // No. We should just synchronously open the movie:

        // Try to open the named 'moviefile' and create & initialize a corresponding movie object.
        // A handle to the movie object is returned upon successfull operation.
        PsychCreateMovie(windowRecord, moviefile, preloadSecs, &moviehandle, asyncFlag, specialFlags1, pixelFormat, maxNumberThreads, movieOptions);
    }
    else {
        // Asynchronous open operation requested or running:
        switch(asyncmovieinfo.asyncstate) {
            case 0: // No async open running, but async open requested
                // Fill all information needed for opening the movie into the info struct:
                asyncmovieinfo.asyncstate = 1; // Mark state as "Operation in progress"
                asyncmovieinfo.moviename = strdup(moviefile);
                asyncmovieinfo.preloadSecs = preloadSecs;
                asyncmovieinfo.asyncFlag = asyncFlag;
                asyncmovieinfo.specialFlags1 = specialFlags1;
                asyncmovieinfo.pixelFormat = pixelFormat;
                asyncmovieinfo.maxNumberThreads = maxNumberThreads;
                asyncmovieinfo.movieOptions = strdup(movieOptions);

                if (windowRecord) {
                    memcpy(&asyncmovieinfo.windowRecord, windowRecord, sizeof(PsychWindowRecordType));
                } else {
                    memset(&asyncmovieinfo.windowRecord, 0, sizeof(PsychWindowRecordType));
                }

                asyncmovieinfo.moviehandle = -1;

                // Increase our scheduling priority to basic RT priority: This way we should get
                // more cpu time for our PTB main thread than the async. background prefetch-thread:
                // On Windows we must not go higher than basePriority 1 (HIGH PRIORITY) or bad interference can happen.
                // On OS/X we use basePriority 2 for robust realtime, using up to (4+1) == 5 msecs of time in every 10 msecs slice, allowing for up to 1 msec jitter/latency for ops.
                // On Linux we just use standard basePriority 2 RT-FIFO scheduling and trust the os to do the right thing.
                if ((rc=PsychSetThreadPriority(NULL, ((PSYCH_SYSTEM == PSYCH_WINDOWS) ? 1 : 2), ((PSYCH_SYSTEM == PSYCH_OSX) ? 4 : 0)))!=0) {
                    printf("PTB-WARNING: In OpenMovie(): Failed to raise priority of main thread [System error %i]. Expect movie timing problems.\n", rc);
                }

                // Start our own movie loader Posix-Thread:
                PsychCreateThread(&asyncmovieinfo.pid, NULL, PsychAsyncCreateMovie, &asyncmovieinfo);

                // Async movie open initiated. We return control to host environment:
                return(PsychError_none);
            break;

            case 1: // Async open operation in progress, but not yet finished.
                // Should we wait for completion or just return?
                if (asyncFlag & 1) {
                    // Async poll requested. We just return -1 to signal that open isn't finished yet:
                    PsychCopyOutDoubleArg(1, TRUE, -1);
                    return(PsychError_none);
                }
                // We fall through to case 2 - Wait for "Load operation successfully finished."
                // Fall through.
            case 2: // Async open operation finished. Parse asyncinfo struct and return it to host environment:
                // We need to join our terminated worker thread to release its ressources. If the worker-thread
                // isn't done yet (fallthrough from case 1 for sync. wait), this join will block us until worker
                // completes:
                PsychDeleteThread(&asyncmovieinfo.pid);

                asyncmovieinfo.asyncstate = 0; // Reset state to idle:
                moviehandle = asyncmovieinfo.moviehandle;

                // Release options string:
                free(asyncmovieinfo.movieOptions);

                // Movie successfully opened?
                if (moviehandle < 0) {
                    // Movie loading failed for some reason.
                    printf("PTB-ERROR: When trying to asynchronously load movie '%s', the operation failed! Reasons given above.\n", asyncmovieinfo.moviename);
                    free(asyncmovieinfo.moviename);
                    PsychErrorExitMsg(PsychError_user, "Asynchronous loading of the movie failed.");
                }

                free(asyncmovieinfo.moviename);

                // We can fall out of the switch statement and continue with the standard synchronous load code as if
                // the movie had been loaded synchronously.
            break;
            default:
                PsychErrorExitMsg(PsychError_internal, "Unhandled async movie state condition encountered! BUG!!");
        }
    }

    // Upon sucessfull completion, we'll have a valid handle in 'moviehandle'.
    PsychCopyOutDoubleArg(1, TRUE, (double) moviehandle);

    // Retrieve infos about new movie:

    // Is the "count" output argument (total number of frames) requested by user?
    if (PsychGetNumOutputArgs() > 5) {
        // Yes. Query the framecount (expensive!) and return it:
        PsychGetMovieInfos(moviehandle, &width, &height, &framecount, &durationsecs, &framerate, NULL, &aspectRatio);
        PsychCopyOutDoubleArg(6, TRUE, (double) framecount);
    }
    else {
        // No. Don't compute and return it.
        PsychGetMovieInfos(moviehandle, &width, &height, NULL, &durationsecs, &framerate, NULL, &aspectRatio);
    }

    PsychCopyOutDoubleArg(2, FALSE, (double) durationsecs);
    PsychCopyOutDoubleArg(3, FALSE, (double) framerate);
    PsychCopyOutDoubleArg(4, FALSE, (double) width);
    PsychCopyOutDoubleArg(5, FALSE, (double) height);
    PsychCopyOutDoubleArg(7, FALSE, (double) aspectRatio);

    // Ready!
    return(PsychError_none);
}
Пример #2
0
PsychError SCREENOpenMovie(void) 
{
        PsychWindowRecordType					*windowRecord;
        char                                    *moviefile;
        int                                     moviehandle = -1;
        int                                     framecount;
        double                                  durationsecs;
        double                                  framerate;
        int                                     width;
        int                                     height;
        int                                     asyncFlag = 0;
        static psych_bool                       firstTime = TRUE;
		double									preloadSecs = 1;
        int										rc;

        if (firstTime) {
            // Setup asyncopeninfo on first invocation:
            firstTime = FALSE;
            asyncmovieinfo.asyncstate = 0; // State = No async open in progress.
        }
        
		// All sub functions should have these two lines
		PsychPushHelp(useString, synopsisString, seeAlsoString);
		if(PsychIsGiveHelp()) {PsychGiveHelp(); return(PsychError_none);};

        PsychErrorExit(PsychCapNumInputArgs(4));            // Max. 4 input args.
        PsychErrorExit(PsychRequireNumInputArgs(1));        // Min. 1 input args required.
        PsychErrorExit(PsychCapNumOutputArgs(6));           // Max. 6 output args.
        
        // Get the window record from the window record argument and get info from the window record
		windowRecord = NULL;
        PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, FALSE, &windowRecord);
        // Only onscreen windows allowed:
        if(windowRecord && !PsychIsOnscreenWindow(windowRecord)) {
            PsychErrorExitMsg(PsychError_user, "OpenMovie called on something else than an onscreen window.");
        }
        
        // Get the movie name string:
        moviefile = NULL;
        PsychAllocInCharArg(2, kPsychArgRequired, &moviefile);

        // Get the (optional) asyncFlag:
        PsychCopyInIntegerArg(3, FALSE, &asyncFlag);

		PsychCopyInDoubleArg(4, FALSE, &preloadSecs);
		if (preloadSecs < 0 && preloadSecs!= -1) PsychErrorExitMsg(PsychError_user, "OpenMovie called with invalid (negative, but not equal -1) 'preloadSecs' argument!");

        // Asynchronous Open operation in progress or requested?
        if ((asyncmovieinfo.asyncstate == 0) && (asyncFlag == 0)) {
            // No. We should just synchronously open the movie:

            // Try to open the named 'moviefile' and create & initialize a corresponding movie object.
            // A MATLAB handle to the movie object is returned upon successfull operation.
            PsychCreateMovie(windowRecord, moviefile, preloadSecs, &moviehandle);
        }
        else {
            // Asynchronous open operation requested or running:
            switch(asyncmovieinfo.asyncstate) {
                case 0: // No async open running, but async open requested
                    // Fill all information needed for opening the movie into the info struct:
                    asyncmovieinfo.asyncstate = 1; // Mark state as "Operation in progress"
                    asyncmovieinfo.moviename = strdup(moviefile);
					asyncmovieinfo.preloadSecs = preloadSecs;
                    if (windowRecord) {
						memcpy(&asyncmovieinfo.windowRecord, windowRecord, sizeof(PsychWindowRecordType));
					} else {
						memcpy(&asyncmovieinfo.windowRecord, 0, sizeof(PsychWindowRecordType));
					}

                    asyncmovieinfo.moviehandle = -1;

                    // Increase our scheduling priority to maximum FIFO priority: This way we should get
                    // more cpu time for our PTB main thread than the async. background prefetch-thread:
                    if ((rc=PsychSetThreadPriority(NULL, 1, 0))!=0) {
                        printf("PTB-WARNING: In OpenMovie(): Failed to raise priority of main thread [System error %i]. Expect movie timing problems.\n", rc);
                    }

                    // Start our own movie loader Posix-Thread:
                    PsychCreateThread(&asyncmovieinfo.pid, NULL, PsychAsyncCreateMovie, &asyncmovieinfo);
                    
                    // Async movie open initiated. We return control to host environment:
                    return(PsychError_none);
                break;
                    
                case 1: // Async open operation in progress, but not yet finished.
                    // Should we wait for completion or just return?
                    if (asyncFlag) {
                        // Async poll requested. We just return -1 to signal that open isn't finished yet:
                        PsychCopyOutDoubleArg(1, TRUE, -1);
                        return(PsychError_none);
                    }
                    // We fall through to case 2 - Wait for "Load operation successfully finished."
                
                case 2: // Async open operation successfully finished. Parse asyncinfo struct and return it to host environment:
                    // We need to join our terminated worker thread to release its ressources. If the worker-thread
                    // isn't done yet (fallthrough from case 1 for sync. wait), this join will block us until worker
                    // completes:
					PsychDeleteThread(&asyncmovieinfo.pid);
					
                    // Reset our priority to "normal" after async prefetch completion:
                    if ((rc=PsychSetThreadPriority(NULL, 0, 0))!=0) {
                        printf("PTB-WARNING: In OpenMovie(): Failed to lower priority of main thread [System error %i]. Expect movie timing problems.\n", rc);
                    }

                    asyncmovieinfo.asyncstate = 0; // Reset state to idle:
                    moviehandle = asyncmovieinfo.moviehandle;
                    
                    // Movie successfully opened?
                    if (moviehandle < 0) {
                        // Movie loading failed for some reason.
                        printf("PTB-ERROR: When trying to asynchronously load movie %s, the operation failed: ", asyncmovieinfo.moviename);
                        switch(moviehandle) {
                            case -2000:
                            case -50:
                            case -43:
                                printf("File not found.");
                                break;
                                
                            case -2048:
                                printf("This is not a file that Quicktime understands.");
                                break;
                                
                            case -2003:
                                printf("Can't find media handler (codec) for this movie.");
                                break;
                                
                            case -2:
                                printf("Maximum allowed number of simultaneously open movie files exceeded!");
                                break;
                                
                            case -1:
                                printf("Internal error: Failure in PTB's movie playback engine!");
                                break;
                                
                            default:
                                printf("Unknown error (Quicktime error %i): Check http://developer.apple.com/documentation/QuickTime/APIREF/ErrorCodes.htm#//apple_ref/doc/constant_group/Error_Codes", moviehandle);
                        }
                        printf("\n\n");
                        
                        PsychErrorExitMsg(PsychError_user, "Asynchronous loading of the Quicktime movie failed.");
                    }
                    
                    // We can fall out of the switch statement and continue with the standard synchronous load code as if
                    // the movie had been loaded synchronously.
                break;
                default:
                    PsychErrorExitMsg(PsychError_internal, "Unhandled async movie state condition encountered! BUG!!");
            }
        }

        // Upon sucessfull completion, we'll have a valid handle in 'moviehandle'.
        // Return it to Matlab-world:
        PsychCopyOutDoubleArg(1, TRUE, (double) moviehandle);

        // Retrieve infos about new movie:
        
        // Is the "count" output argument (total number of frames) requested by user?
        if (PsychGetNumOutputArgs() > 5) {
            // Yes. Query the framecount (expensive!) and return it:
            PsychGetMovieInfos(moviehandle, &width, &height, &framecount, &durationsecs, &framerate, NULL);
            PsychCopyOutDoubleArg(6, TRUE, (double) framecount);
        }
        else {
            // No. Don't compute and return it.
            PsychGetMovieInfos(moviehandle, &width, &height, NULL, &durationsecs, &framerate, NULL);
        }

        PsychCopyOutDoubleArg(2, FALSE, (double) durationsecs);
        PsychCopyOutDoubleArg(3, FALSE, (double) framerate);
        PsychCopyOutDoubleArg(4, FALSE, (double) width);
        PsychCopyOutDoubleArg(5, FALSE, (double) height);

		// Ready!
		return(PsychError_none);
}