1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
|
/*
Common/Screen/PsychVideoCaptureSupport.c
PLATFORMS:
This is the OS independent version (for now: Should work on OS-X and Windows)
A GNU/Linux specific version is stored in the /Linux/ folder. It has the
same API - and therefore the same header file, but a pretty different
implementation.
AUTHORS:
Mario Kleiner mk mario.kleiner@tuebingen.mpg.de
HISTORY:
DESCRIPTION:
Psychtoolbox functions for dealing with video capture devices.
NOTES:
*/
#include "Screen.h"
#include <float.h>
// Forward declaration of internal helper function:
void PsychDeleteAllCaptureDevices(void);
// Record which defines capture engine independent state for a capture device:
typedef struct {
int engineId; // Type of capture engine: -1 == Free slot, 0 == Quicktime, 1 == LibDC, 2 == ARVideo, 3 == GStreamer
} PsychMasterVidcapRecordType;
static PsychMasterVidcapRecordType mastervidcapRecordBANK[PSYCH_MAX_CAPTUREDEVICES];
static int numCaptureRecords = 0;
static psych_bool firsttime = TRUE;
/*
* PsychVideoCaptureInit() -- Initialize video capture subsystem.
* This routine is called by Screen's RegisterProject.c PsychModuleInit()
* routine at Screen load-time. It clears out the vidcapRecordBANK to
* bring the subsystem into a clean initial state.
*/
void PsychVideoCaptureInit(void)
{
// Initialize mastervidcapRecordBANK with NULL-entries:
int i;
for (i=0; i < PSYCH_MAX_CAPTUREDEVICES; i++) {
mastervidcapRecordBANK[i].engineId = -1;
}
numCaptureRecords = 0;
// Initialize the different capture engines:
#ifdef PTBVIDEOCAPTURE_LIBDC
PsychDCVideoCaptureInit();
#endif
#ifdef PTB_USE_GSTREAMER
PsychGSVideoCaptureInit();
#endif
return;
}
void PsychEnumerateVideoSources(int engineId, int outPos)
{
psych_bool dispatched = FALSE;
#ifdef PTBVIDEOCAPTURE_LIBDC
if (engineId == 1) {
PsychDCEnumerateVideoSources(outPos);
dispatched = TRUE;
}
#endif
#ifdef PTB_USE_GSTREAMER
if (engineId == 3) {
// GStreamer device enumeration:
PsychGSEnumerateVideoSources(outPos, -1, NULL, NULL);
dispatched = TRUE;
}
#endif
// Unsupported engine requested?
if (!dispatched) PsychErrorExitMsg(PsychError_user, "The requested video capture engine is not supported on your system, either not at all, or has been disabled at compile time.");
return;
}
/*
* PsychOpenVideoCaptureDevice() -- Create a video capture object.
*
* This function tries to open a video capture device and return the associated captureHandle for it.
*
* engineId = Type of video capture engine to use: 0 == Quicktime sequence grabbers, 1 == LibDC1394-V2 IIDC Firewire.
* win = Pointer to window record of associated onscreen window.
* deviceIndex = Index of the grabber device. (Currently ignored)
* capturehandle = handle to the new capture object.
* capturerectangle = If non-NULL a ptr to a PsychRectangle which contains the ROI for capture.
* The following arguments are currently ignored on Windows and OS-X:
* reqdepth = Number of layers for captured output textures. (0=Don't care, 1=LUMINANCE8, 2=LUMINANCE8_ALPHA8, 3=RGB8, 4=RGBA8)
* num_dmabuffers = Number of buffers in the ringbuffer queue (e.g., DMA buffers) - This is OS specific. Zero = Don't care.
* allow_lowperf_fallback = If set to 1 then PTB can use a slower, low-performance fallback path to get nasty devices working.
* targetmoviefilename = NULL == Only live capture, non-NULL == Pointer to char-string with name of target QT file for video recording.
* recordingflags = Only used for recording: Request audio recording, ram recording vs. disk recording and such...
* // Query optional movie recording flags:
* // 0 = Record video, stream to disk immediately (slower, but unlimited recording duration).
* // 1 = Record video, stream to memory, then at end of recording to disk (limited duration by RAM size, but faster).
* // 2 = Record audio as well.
*
* bitdepth = Number of bits per color component / channel, aka bpc.
*
*/
psych_bool PsychOpenVideoCaptureDevice(int engineId, PsychWindowRecordType *win, int deviceIndex, int* capturehandle, double* capturerectangle,
int reqdepth, int num_dmabuffers, int allow_lowperf_fallback, char* targetmoviefilename, unsigned int recordingflags, int bitdepth)
{
int i, slotid;
psych_bool dispatched = FALSE;
*capturehandle = -1;
// Sanity checking:
if (!PsychIsOnscreenWindow(win)) {
PsychErrorExitMsg(PsychError_user, "Provided windowPtr is not an onscreen window.");
}
if (numCaptureRecords >= PSYCH_MAX_CAPTUREDEVICES) {
PsychErrorExitMsg(PsychError_user, "Allowed maximum number of simultaneously open capture devices exceeded!");
}
// Search first free slot in mastervidcapRecordBANK:
for (i=0; (i < PSYCH_MAX_CAPTUREDEVICES) && (mastervidcapRecordBANK[i].engineId != -1); i++) {};
if (i>=PSYCH_MAX_CAPTUREDEVICES) {
PsychErrorExitMsg(PsychError_user, "Allowed maximum number of simultaneously open capture devices exceeded!");
}
// Slot slotid will contain the record for our new capture object:
slotid=i;
// Decide which engine to use and dispatch into proper open function:
#ifdef PTBVIDEOCAPTURE_LIBDC
if (engineId == 1) {
// LibDC1394 video capture:
if (!PsychDCOpenVideoCaptureDevice(slotid, win, deviceIndex, capturehandle, capturerectangle, reqdepth,
num_dmabuffers, allow_lowperf_fallback, targetmoviefilename, recordingflags, bitdepth)) {
// Probably won't ever reach this point due to error handling triggered in subfunction... anyway...
return(FALSE);
}
dispatched = TRUE;
}
#endif
#ifdef PTB_USE_GSTREAMER
if (engineId == 3) {
// GStreamer video capture:
if (!PsychGSOpenVideoCaptureDevice(slotid, win, deviceIndex, capturehandle, capturerectangle, reqdepth,
num_dmabuffers, allow_lowperf_fallback, targetmoviefilename, recordingflags, bitdepth)) {
// Probably won't ever reach this point due to error handling triggered in subfunction... anyway...
return(FALSE);
}
dispatched = TRUE;
}
#endif
// Unsupported engine requested?
if (!dispatched) PsychErrorExitMsg(PsychError_user, "The requested video capture engine is not supported on your system, either not at all, or has been disabled at compile time.");
// Ok, new capture device for requested capture engine created...
// Assign new record:
mastervidcapRecordBANK[slotid].engineId = engineId;
// Assign final handle:
*capturehandle = slotid;
// Increase counter:
numCaptureRecords++;
// Ready.
return(TRUE);
}
/*
* PsychCloseVideoCaptureDevice() -- Close a capture device and release all associated ressources.
*/
void PsychCloseVideoCaptureDevice(int capturehandle)
{
if (capturehandle < 0 || capturehandle >= PSYCH_MAX_CAPTUREDEVICES) {
PsychErrorExitMsg(PsychError_user, "Invalid capturehandle provided!");
}
if (mastervidcapRecordBANK[capturehandle].engineId == -1) {
PsychErrorExitMsg(PsychError_user, "Invalid capturehandle provided. No capture device associated with this handle !!!");
}
// Call engine specific method:
#ifdef PTBVIDEOCAPTURE_LIBDC
if (mastervidcapRecordBANK[capturehandle].engineId == 1) PsychDCCloseVideoCaptureDevice(capturehandle);
#endif
#ifdef PTB_USE_GSTREAMER
if (mastervidcapRecordBANK[capturehandle].engineId == 3) PsychGSCloseVideoCaptureDevice(capturehandle);
#endif
// Release record:
mastervidcapRecordBANK[capturehandle].engineId = -1;
// Decrease counter:
if (numCaptureRecords>0) numCaptureRecords--;
return;
}
/*
* PsychDeleteAllCaptureDevices() -- Delete all capture objects and release all associated ressources.
*/
void PsychDeleteAllCaptureDevices(void)
{
int i;
for (i=0; i<PSYCH_MAX_CAPTUREDEVICES; i++) {
if (mastervidcapRecordBANK[i].engineId !=-1) PsychCloseVideoCaptureDevice(i);
}
return;
}
/*
* PsychGetTextureFromCapture() -- Create an OpenGL texturemap from a specific videoframe from given capture object.
*
* win = Window pointer of onscreen window for which a OpenGL texture should be created.
* capturehandle = Handle to the capture object.
* checkForImage = >0 == Just check if new image available, 0 == really retrieve the image, blocking if necessary.
* 2 == Check for new image, block inside this function (if possible) if no image available.
* timeindex = This parameter is currently ignored and reserved for future use.
* out_texture = Pointer to the Psychtoolbox texture-record where the new texture should be stored.
* presentation_timestamp = A ptr to a double variable, where the presentation timestamp of the returned frame should be stored.
* summed_intensity = An optional ptr to a double variable. If non-NULL, then sum of intensities over all channels is calculated and returned.
* outrawbuffer = An optional ptr to a memory buffer of sufficient size. If non-NULL, the buffer will be filled with the captured raw image data, e.g., for use inside Matlab or whatever...
* Returns Number of pending or dropped frames after fetch on success (>=0), -1 if no new image available yet, -2 if no new image available and there won't be any in future.
*/
int PsychGetTextureFromCapture(PsychWindowRecordType *win, int capturehandle, int checkForImage, double timeindex, PsychWindowRecordType *out_texture, double *presentation_timestamp, double* summed_intensity, rawcapimgdata* outrawbuffer)
{
// Sanity checks:
if (capturehandle < 0 || capturehandle >= PSYCH_MAX_CAPTUREDEVICES || mastervidcapRecordBANK[capturehandle].engineId == -1) {
PsychErrorExitMsg(PsychError_user, "Invalid capturehandle provided.");
}
// Call engine specific method:
#ifdef PTBVIDEOCAPTURE_LIBDC
if (mastervidcapRecordBANK[capturehandle].engineId == 1) return(PsychDCGetTextureFromCapture(win, capturehandle, checkForImage, timeindex, out_texture, presentation_timestamp, summed_intensity, outrawbuffer));
#endif
#ifdef PTB_USE_GSTREAMER
if (mastervidcapRecordBANK[capturehandle].engineId == 3) return(PsychGSGetTextureFromCapture(win, capturehandle, checkForImage, timeindex, out_texture, presentation_timestamp, summed_intensity, outrawbuffer));
#endif
return(-2);
}
/*
* PsychVideoCaptureRate() - Start- and stop video capture.
*
* capturehandle = Grabber to start-/stop.
* playbackrate = zero == Stop capture, non-zero == Capture
* dropframes = At 'start': Decide if low latency capture shall be used. At 'stop' If zero, don't
* discard pending buffers in internal capture queue.
* startattime = Deadline (in system time) to wait for before real start of capture.
* Returns Number of dropped frames during capture.
*/
int PsychVideoCaptureRate(int capturehandle, double capturerate, int dropframes, double* startattime)
{
if (capturehandle < 0 || capturehandle >= PSYCH_MAX_CAPTUREDEVICES || mastervidcapRecordBANK[capturehandle].engineId == -1) {
PsychErrorExitMsg(PsychError_user, "Invalid capturehandle provided!");
}
// Call engine specific method:
#ifdef PTBVIDEOCAPTURE_LIBDC
if (mastervidcapRecordBANK[capturehandle].engineId == 1) return(PsychDCVideoCaptureRate(capturehandle, capturerate, dropframes, startattime));
#endif
#ifdef PTB_USE_GSTREAMER
if (mastervidcapRecordBANK[capturehandle].engineId == 3) return(PsychGSVideoCaptureRate(capturehandle, capturerate, dropframes, startattime));
#endif
return(0);
}
/* Set capture device specific parameters:
* On OS-X and Windows (and therefore in this implementation) this is currently a no-op, until
* we find out how to do this with the Sequence-Grabber API.
*/
double PsychVideoCaptureSetParameter(int capturehandle, const char* pname, double value)
{
// Valid handle provided? -1 is a special "carte blanche" handle.
if (capturehandle < -1 || capturehandle >= PSYCH_MAX_CAPTUREDEVICES) {
PsychErrorExitMsg(PsychError_user, "Invalid capturehandle provided!");
}
if ((capturehandle != -1) && (mastervidcapRecordBANK[capturehandle].engineId == -1)) {
PsychErrorExitMsg(PsychError_user, "Invalid capturehandle provided! No such capture device open.");
}
// Call engine specific method: capturehandle == -1 calls *all* engines:
#ifdef PTBVIDEOCAPTURE_LIBDC
if ((capturehandle == -1) || (mastervidcapRecordBANK[capturehandle].engineId == 1)) {
if (capturehandle != -1) {
return(PsychDCVideoCaptureSetParameter(capturehandle, pname, value));
}
else PsychDCVideoCaptureSetParameter(capturehandle, pname, value);
}
#endif
#ifdef PTB_USE_GSTREAMER
if ((capturehandle == -1) || (mastervidcapRecordBANK[capturehandle].engineId == 3)) {
if (capturehandle != -1) {
return(PsychGSVideoCaptureSetParameter(capturehandle, pname, value));
}
else PsychGSVideoCaptureSetParameter(capturehandle, pname, value);
}
#endif
return(0);
}
/*
* void PsychExitVideoCapture() - Shutdown handler.
*
* This routine is called by Screen('CloseAll') and on clear Screen time to
* do final cleanup. It deletes all capture objects
*
*/
void PsychExitVideoCapture(void)
{
// Release all capture devices:
PsychDeleteAllCaptureDevices();
// Call engine specific method:
#ifdef PTBVIDEOCAPTURE_LIBDC
PsychDCExitVideoCapture();
#endif
#ifdef PTB_USE_GSTREAMER
PsychGSExitVideoCapture();
#endif
firsttime = TRUE;
return;
}
|