/* * PsychAsyncCreateMovie() -- Open a movie file in the background. * * This function is called by SCREENOpenMovie as main-function of * a new Posix-Thread for background movie loading. It simply calls * PsychCreateMovie(), waiting for it to return a new moviehandle. * Then it returns those info and terminates. * -> By calling PsychCreateMovie from the run-function of a dedicated * Posix-Thread which runs independent of the main Matlab/PTB Thread with * non-realtime priority, we can do the work of opening a Quicktime movie * in the background, hopefully not affecting the timing of the main PTB * thread too much. */ void* PsychAsyncCreateMovie(void* mi) { struct sched_param sp; int rc; // Get a pointer to the info-struct: asyncmovieinfo* movieinfo = (asyncmovieinfo*) mi; // The special value -1000 tells PsychCreateMovie to not output any error- // messages as this could easily crash Matlab. int mymoviehandle=-1000; // Reduce our scheduling priority to the minimum value of zero, so // we do not interfere too much with the PTB main thread: sp.sched_priority = 0; if ((rc=pthread_setschedparam(pthread_self(), SCHED_OTHER, &sp))!=0) { printf("PTB-ERROR: In PsychAsyncCreateMovie(): PTHREAD ERROR %i ...", rc); } // Tell QT subsystem that this thread wants to use it as well: // MK: Do we need this? Defaults seem to be ok... EnterMoviesOnThread(<#UInt32 inFlags#>); // Execute our normal OpenMovie function: This does the hard work: PsychCreateMovie(&(movieinfo->windowRecord), movieinfo->moviename, movieinfo->preloadSecs, &mymoviehandle); // Disable QT subsystem for this thread: // ExitMoviesOnThread(); // Ok, either we have a moviehandle to a valid movie, or we failed, which would // be signalled to the calling function via some negative moviehandle: movieinfo->moviehandle = mymoviehandle; // Return moviehandle. movieinfo->asyncstate = 2; // Set state to "Completed" // Exit from the routine. This will automatically terminate our Posix-Thread. return(NULL); }
/* * PsychAsyncCreateMovie() -- Open a movie file in the background. * * This function is called by SCREENOpenMovie as main-function of * a new Posix-Thread for background movie loading. It simply calls * PsychCreateMovie(), waiting for it to return a new moviehandle. * Then it returns those info and terminates. * -> By calling PsychCreateMovie from the run-function of a dedicated * Posix-Thread which runs independent of the main Matlab/PTB Thread with * non-realtime priority, we can do the work of opening a Quicktime movie * in the background, hopefully not affecting the timing of the main PTB * thread too much. */ void* PsychAsyncCreateMovie(void* inmovieinfo) { int rc; PsychAsyncMovieInfo* movieinfo = (PsychAsyncMovieInfo*) inmovieinfo; // The special value -1000 tells PsychCreateMovie to not output any error- // messages as this could easily crash Matlab/Octave. int mymoviehandle=-1000; // Reduce our scheduling priority to the minimum value of zero, so // we do not interfere too much with the PTB main thread: if ((rc=PsychSetThreadPriority(NULL, 0, 0))!=0) { printf("PTB-WARNING: In PsychAsyncCreateMovie(): Failed to lower my priority to non-realtime [System errorcode %i]. Expect timing problems for movie playback!", rc); } // Execute our normal OpenMovie function: This does the hard work: PsychCreateMovie(&(movieinfo->windowRecord), movieinfo->moviename, movieinfo->preloadSecs, &mymoviehandle, movieinfo->asyncFlag, movieinfo->specialFlags1); // Ok, either we have a moviehandle to a valid movie, or we failed, which would // be signalled to the calling function via some negative moviehandle: movieinfo->moviehandle = mymoviehandle; // Return moviehandle. movieinfo->asyncstate = 2; // Set state to "Completed" // Exit from the routine. This will automatically terminate our Thread. return(NULL); }
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); }
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); }