function VideoCaptureLatencyTest(texfetch, fullscreen, depth) % VideoCaptureLatencyTest([texfetch=0][, fullscreen=1][, depth=1]) % % This routine tests the latency of Videocapture for % a given Camera -> Computer -> PTB -> gfx-card -> display combo. % % Working principle and usage: % % 1. Point camera to monitor so that cameras field of view captures monitor % display area. % % 2. Calibration: First a black screen is shown to the cam and captured to % find the average intensity of an image of a black display. % Then (over ten trials), the screen turns white and the script measures % the time delay between onset of white screen and arrival of a captured % image with at least 25% more intensity than the 'black'-image -- our % white image. This procedure is repeated 10 times and latencies averaged. % % -> Rough estimate of delay from scene change to arrival of frame. % -> You have to add the display onset delay if captured image should be % shown to subject for video feedback loops. % % Parameters: % % texfetch == 0 Measure only capture latency. 1 == Measure texture fetch % latency as well. 2 == Measure drawing and onset latency as well. % % History: % 2/9/06 mk Written. % Running OpenGL-PTB? This is required. AssertOpenGL; rate = 30; % Find stimulus display: screen=max(Screen('Screens')); if nargin < 1 texfetch=0; end if nargin < 2 fullscreen=1; end if nargin < 3 depth = 1; end if IsOctave more off; ignore_function_time_stamp('all'); end % Open display with default settings: if fullscreen<1 win=Screen('OpenWindow', screen, 0, [0 0 800 600]); else win=Screen('OpenWindow', screen, 0); end [~, h]=Screen('WindowSize', win); % Setup font and textsize: 24 is also a good size for Linux. Screen('TextSize', win, 24); Screen('TextStyle', win, 1); % Show initial blank screen: Screen('Flip',win); % Open default video capture device: grabber = Screen('OpenVideoCapture', win, 0, [0 0 640 480], depth); % Start video capture: We request at least a 30 Hz capture rate and lowlatency (=1) mode: % Requested start time is 4 seconds from now: [fps, capturestarttime]=Screen('StartVideoCapture', grabber, rate, 1, GetSecs + 4); fps %#ok<*NOPRT> oldpts = 0; count = 0; t=GetSecs; while (GetSecs - t) < 600 if KbCheck break; end [tex, pts, nrdropped, intensity]=Screen('GetCapturedImage', win, grabber, 0); % fprintf('tex = %i pts = %f\n', tex, pts); if tex>0 Screen('DrawText', win, 'Point camera to center of Display - Then press any key', 10, h-50); Screen('DrawText', win, ['Capture timestamp (s): ' sprintf('%.4f', pts)], 0, 0, 255); if count>0 % Compute delta: delta = (pts - oldpts) * 1000; oldpts = pts; Screen('DrawText', win, ['Delta since last frame (ms): ' sprintf('%.4f', delta)], 0, 24, 255); Screen('DrawText', win, ['Delta start to first frame (ms): ' sprintf('%.4f', camstartlatency)], 0, 96, 255); else % First image in video fifo. camstartlatency = (pts - capturestarttime) * 1000.0 end % Compute and print intensity: Screen('DrawText', win, ['Mean intensity: ' sprintf('%.2f', intensity)], 0, 48, 255); % Print count of dropped frames since last fetch: Screen('DrawText', win, ['Dropped frames: ' sprintf('%i', nrdropped)], 0, 72, 255); % New texture from framegrabber. Screen('DrawTexture', win, tex); Screen('Flip', win, 0, 0, 2); Screen('Close', tex); count = count + 1; end end calcedlatency = PsychCamSettings('EstimateLatency', grabber) * 1000.0; fprintf('Estimated camera latency from cam or lut: %f msecs.', calcedlatency); tminimum = 10000000000.0; tminimum2 = 10000000000.0; tcount = 1; for reps=1:5 % Ok, preview finished. Cam should now point to the screen and % we can start the latency measurement procedure: ntrials = 10; tavgdelay = 0; for i=1:ntrials % Make screen black: Screen('FillRect', win, 0); Screen('Flip', win); % Capture at least 20 frames to make sure we're really black... for j=1:20 Screen('GetCapturedImage', win, grabber, 2); end % Capture a black frame: tex=0; while tex==0 [tex, pts, ~, intensity]=Screen('GetCapturedImage', win, grabber, 0); end if tex > 0 Screen('Close', tex); end blackintensity = intensity; threshold = blackintensity * 1.02; % Eat up all enqueued images, if any. tex=1; while tex > 0 [tex, newpts]=Screen('GetCapturedImage', win, grabber, 0); if tex > 0 Screen('Close', tex); pts = newpts; end end % Make display white: Screen('FillRect', win, 255); % Show it and take onset timestamp: tonset = Screen('Flip', win); % Now we loop and capture until intensity exceeds threshold: tex=0; intensity=0; lagframes=0; ptsblack = pts; while tex >= 0 && intensity < threshold if texfetch > 0 waitmode=1; else waitmode=2; end [tex, pts, ~, intensity]=Screen('GetCapturedImage', win, grabber, waitmode); % Captured! Take timestamp: tdelay=(GetSecs - tonset) * 1000; if tex > 0 lagframes=lagframes+1; if texfetch>1 && intensity>=threshold Screen('DrawTexture',win,tex,[],[],[],0); tdelay = (Screen('Flip',win) - tonset) * 1000; end Screen('Close', tex); tex=0; end end tdelay lagframes ptslag = (pts - ptsblack) * 1000; tavgdelay=tavgdelay + tdelay; tminimum2=min(tminimum2, tdelay); ttotal(tcount)=tdelay; %#ok<*AGROW> tcount = tcount + 1; % Next trial... end % Stop and restart capture loop: Screen('StopVideoCapture', grabber); % Restart with a random delay between 0.0 and 1.0 seconds... Screen('StartVideoCapture', grabber, rate, 1, GetSecs + rand); tavgdelay = tavgdelay / ntrials; fprintf('Average delay in block number %i: %f msecs.', reps, tavgdelay); tminimum = min(tminimum, tavgdelay); tdelays(reps) = tavgdelay; end % Shutdown capture loop, close screens... Screen('CloseVideoCapture', grabber); Screen('CloseAll'); fprintf('Minimum average delay from the 5 blocks of trials: %f msecs.\n', tminimum) fprintf('Absolute minimum delay over all trials: %f msecs.\n', tminimum2) figure; plot(tdelays); title('Average delay in each block [msecs]') figure; plot(ttotal); title('Delay for each trial [msecs]') return;