2
PsychSourceGL/Source/Common/Screen/PsychVideoCaptureSupportLibARVideo.c
6
GNU/Linux, Apple MacOS/X and MS-Windows
10
Mario Kleiner mk mario.kleiner@tuebingen.mpg.de
14
18.04.2009 Created initial version.
15
27.03.2011 Disabled. It was not ever implemented on Linux and useless
16
on OS/X. Its limited functionality (on Windows) is replaced by
17
the much more capable GStreamer based video capture engine.
18
Removing this component from the build also allows us the
19
planned change of software license.
23
We should probably remove the implementation files as there aren't any plans
24
to ever resurrect this now entirely obsolete component.
28
This is the videocapture engine based on the free, open-source (GPL'ed)
29
LibARVideo library, part of the ARToolkit. It only supports video capture,
30
no sound capture and no recording of video or sound.
32
On MacOS/X it is implemented via the Quicktime Sequencegrabber-API, ie., it
33
uses the same engine as our original Quicktime video capture engine. Therefore
34
it doesn't provide any additional functionality or advantages over our default
35
engine. It is only implemented here as a reference to compare against our own
36
engine and for simpler cross-platform development. Maybe we can learn something
39
On GNU/Linux it is implemented via the free LGPL'ed GStreamer-Multimedia plugin
40
framework. As such, it should be able to support any video source and video
41
preprocessing operation provided by GStreamers plugins: We have at least support
42
for video4linux and video4linux2 video devices (webcams, DVB, TV tuners, analog
43
cameras and framegrabbers and whatnot), DV firewire consumer cameras, IIDC machine
44
vision cameras, network video (GigE etc in the future), decoded video from video
47
On MS-Windows, it is implemented as a DirectShow filtergraph and should therefore
48
be able to access any device supported by a DirectShow Videofilter, ie., pretty
49
much any device on MS-Windows.
61
#ifdef PTBVIDEOCAPTURE_ARVIDEO
63
// These are the includes for ARToolkit and ARVideo:
66
#ifndef DC1394_SUCCESS
67
#define DC1394_SUCCESS 0
70
// Record which defines all state for a capture device:
72
int valid; // Is this a valid device record? zero == Invalid.
73
AR2VideoParamT *camera; // Ptr to a ARVideo camera object that holds the internal state for such cams.
74
ARUint8 *frame; // Ptr to a psych_uint8 matrix which contains the most recently captured/dequeued frame.
75
int dropframes; // 1 == Always deliver most recent frame in FIFO, even if dropping of frames is neccessary.
76
unsigned char* scratchbuffer; // Scratch buffer for YUV->RGB conversion.
77
int reqpixeldepth; // Requested depth of single pixel in output texture.
78
int pixeldepth; // Depth of single pixel from grabber in bits.
79
int num_dmabuffers; // Number of DMA ringbuffers to use in DMA capture.
80
int nrframes; // Total count of decompressed images.
81
double fps; // Acquisition framerate of capture device.
82
int width; // Width x height of captured images.
84
double last_pts; // Capture timestamp of previous frame.
85
double current_pts; // Capture timestamp of current frame.
86
int current_dropped; // Dropped count for this fetch cycle...
87
int nr_droppedframes; // Counter for dropped frames.
88
int frame_ready; // Signals availability of new frames for conversion into GL-Texture.
89
int grabber_active; // Grabber running?
90
PsychRectType roirect; // Region of interest rectangle - denotes subarea of full video capture area.
91
double avg_decompresstime; // Average time spent in Quicktime/Sequence Grabber decompressor.
92
double avg_gfxtime; // Average time spent in GWorld --> OpenGL texture conversion and statistics.
93
int nrgfxframes; // Count of fetched textures.
94
} PsychVidcapRecordType;
96
static PsychVidcapRecordType vidcapRecordBANK[PSYCH_MAX_CAPTUREDEVICES];
97
static int numCaptureRecords = 0;
98
static psych_bool firsttime = TRUE;
100
// Forward declaration of internal helper function:
101
void PsychARDeleteAllCaptureDevices(void);
104
/* PsychGetARVidcapRecord() -- Given a handle, return ptr to video capture record.
105
* --> Internal helper function of PsychVideoCaptureSupport.
107
PsychVidcapRecordType* PsychGetARVidcapRecord(int deviceIndex)
110
if (deviceIndex < 0) {
111
PsychErrorExitMsg(PsychError_user, "Invalid (negative) deviceIndex for video capture device passed!");
114
if (numCaptureRecords >= PSYCH_MAX_CAPTUREDEVICES) {
115
PsychErrorExitMsg(PsychError_user, "Invalid deviceIndex for video capture device passed. Index exceeds number of registered devices!");
118
if (!vidcapRecordBANK[deviceIndex].valid) {
119
PsychErrorExitMsg(PsychError_user, "Invalid deviceIndex for video capture device passed. No such device open!");
122
// Ok, we have a valid device record, return a ptr to it:
123
return(&vidcapRecordBANK[deviceIndex]);
127
* PsychARVideoCaptureInit() -- Initialize video capture subsystem.
128
* This routine is called by Screen's RegisterProject.c PsychModuleInit()
129
* routine at Screen load-time. It clears out the vidcapRecordBANK to
130
* bring the subsystem into a clean initial state.
132
void PsychARVideoCaptureInit(void)
134
// Initialize vidcapRecordBANK with NULL-entries:
136
for (i=0; i < PSYCH_MAX_CAPTUREDEVICES; i++) {
137
vidcapRecordBANK[i].valid = 0;
139
numCaptureRecords = 0;
145
* void PsychARExitVideoCapture() - Shutdown handler.
147
* This routine is called by Screen('CloseAll') and on clear Screen time to
148
* do final cleanup. It deletes all capture objects
151
void PsychARExitVideoCapture(void)
153
// Release all capture devices
154
PsychARDeleteAllCaptureDevices();
156
// Reset firsttime flag to get a cold restart on next invocation of Screen:
162
* PsychARDeleteAllCaptureDevices() -- Delete all capture objects and release all associated ressources.
164
void PsychARDeleteAllCaptureDevices(void)
167
for (i=0; i<PSYCH_MAX_CAPTUREDEVICES; i++) {
168
if (vidcapRecordBANK[i].valid) PsychARCloseVideoCaptureDevice(i);
174
* PsychARCloseVideoCaptureDevice() -- Close a capture device and release all associated ressources.
176
void PsychARCloseVideoCaptureDevice(int capturehandle)
178
// Retrieve device record for handle:
179
PsychVidcapRecordType* capdev = PsychGetARVidcapRecord(capturehandle);
181
// Stop capture immediately if it is still running:
182
PsychARVideoCaptureRate(capturehandle, 0, 0, NULL);
184
// Close & Shutdown camera, release ressources:
185
ar2VideoClose(capdev->camera);
186
capdev->camera = NULL;
188
// Invalidate device record to free up this slot in the array:
191
// Decrease counter of open capture devices:
192
if (numCaptureRecords>0) numCaptureRecords--;
199
* PsychAROpenVideoCaptureDevice() -- Create a video capture object.
201
* This function tries to open and initialize a connection to a camera
202
* and returns the associated captureHandle for it.
204
* slotid = Number of slot in vidcapRecordBANK[] array to use for this camera.
205
* win = Pointer to window record of associated onscreen window.
206
* deviceIndex = Index of the grabber device. (Currently ignored)
207
* capturehandle = handle to the new capture object.
208
* capturerectangle = If non-NULL a ptr to a PsychRectangle which contains the ROI for capture.
209
* reqdepth = Number of layers for captured output textures. (0=Don't care, 1=LUMINANCE8, 2=LUMINANCE8_ALPHA8, 3=RGB8, 4=RGBA8)
210
* num_dmabuffers = Number of buffers in the ringbuffer queue (e.g., DMA buffers) - This is OS specific. Zero = Don't care.
211
* allow_lowperf_fallback = If set to 1 then PTB can use a slower, low-performance fallback path to get nasty devices working.
212
* targetmoviefilename and recordingflags are currently ignored, they would refer to video harddics recording capabilities.
214
psych_bool PsychAROpenVideoCaptureDevice(int slotid, PsychWindowRecordType *win, int deviceIndex, int* capturehandle, double* capturerectangle,
215
int reqdepth, int num_dmabuffers, int allow_lowperf_fallback, char* targetmoviefilename, unsigned int recordingflags)
217
PsychVidcapRecordType *capdev = NULL;
226
// Default camera config:
227
#if PSYCH_SYSTEM == PSYCH_WINDOWS
228
//strcat(config, "<?xml version=\"1.0\" encoding=\"UTF-8\"?><dsvl_input><camera show_format_dialog=\"false\" friendly_name=\"\"><pixel_format><RGB32 flip_h=\"false\" flip_v=\"true\"/></pixel_format></camera></dsvl_input>");
231
strcat(config, "<?xml version=\"1.0\" encoding=\"UTF-8\"?><dsvl_input><camera show_format_dialog=\"false\" ");
233
// Specific deviceIndex requested, instead of auto-select?
234
if (deviceIndex >= 1 && deviceIndex <= 3) {
235
// Fetch optional moviename parameter as name spec string:
236
if (targetmoviefilename == NULL) PsychErrorExitMsg(PsychError_user, "You set 'deviceIndex' to a value of 1, 2 or 3, but didn't provide the required device name string in the 'moviename' argument! Aborted.");
237
switch(deviceIndex) {
239
sprintf(tmpstr, "friendly_name=\"%s\" ", targetmoviefilename);
243
sprintf(tmpstr, "device_name=\"%s\" ", targetmoviefilename);
247
sprintf(tmpstr, "ieee1394id=\"%s\" ", targetmoviefilename);
251
strcat(config, tmpstr);
254
// Default device index: Just pass through as default device:
255
strcat(config, "friendly_name=\"\" ");
260
#if PSYCH_SYSTEM == PSYCH_OSX
261
char *defaultcamconfig = "";
264
#if PSYCH_SYSTEM == PSYCH_LINUX
265
char *defaultcamconfig = "-dev=/dev/video0 -channel=0 -palette=YUV420P -width=320 -height=240";
268
// Init capturehandle to none:
272
// First time invocation:
274
#if PSYCH_SYSTEM == PSYCH_WINDOWS
275
// On Windows, we need to delay-load the libARvideo.dll DLL. This loading
276
// and linking will automatically happen downstream. However, if delay loading
277
// would fail, we would end up with a crash! For that reason, we try here to
278
// load the DLL, just to probe if the real load/link/bind op later on will
279
// likely succeed. If the following LoadLibrary() call fails and returns NULL,
280
// then we know we would end up crashing. Therefore we'll output some helpful
281
// error-message instead:
282
if (NULL == LoadLibrary("libARvideo.dll")) {
284
printf("\n\nPTB-ERROR: Tried to startup video capture engine type 2 (ARVideo). This didn't work,\n");
285
printf("PTB-ERROR: because one of the required helper DLL libraries failed to load. Probably because they\n");
286
printf("PTB-ERROR: could not be found or could not be accessed (e.g., due to permission problems).\n\n");
287
printf("PTB-ERROR: Please read the online help by typing 'help ARVideoCapture' for troubleshooting instructions.\n\n");
288
PsychErrorExitMsg(PsychError_user, "Unable to start Videocapture engine ARVideo due to DLL loading problems. Aborted.");
295
// Slot 'slotid' will contain the record for our new capture object:
297
// Initialize new record:
298
vidcapRecordBANK[slotid].valid = 1;
300
// Retrieve device record for slotid:
301
capdev = PsychGetARVidcapRecord(slotid);
303
capdev->camera = NULL;
304
capdev->grabber_active = 0;
305
capdev->scratchbuffer = NULL;
307
// ROI rectangle specified?
308
if (capturerectangle) {
309
// Extract wanted width and height:
310
w = (int) PsychGetWidthFromRect(capturerectangle);
311
h = (int) PsychGetHeightFromRect(capturerectangle);
313
#if PSYCH_SYSTEM == PSYCH_OSX
314
sprintf(tmpstr, " -width=%i -height=%i", w, h);
317
#if PSYCH_SYSTEM == PSYCH_WINDOWS
318
sprintf(tmpstr, " frame_width=\"%i\" frame_height=\"%i\" ", w, h);
321
#if PSYCH_SYSTEM == PSYCH_LINUX
325
strcat(config, tmpstr);
328
if (num_dmabuffers > 0) {
329
#if PSYCH_SYSTEM == PSYCH_WINDOWS
330
// Get framerate from num_dmabuffers argument:
331
sprintf(tmpstr, " frame_rate=\"%i\" ", num_dmabuffers);
332
strcat(config, tmpstr);
336
#if PSYCH_SYSTEM == PSYCH_OSX
337
// Disable setup dialog:
338
strcat(config, " -nodialog");
340
// Specific deviceIndex requested, instead of auto-select?
341
if (deviceIndex > 0) {
342
sprintf(tmpstr, " -grabber=%i", deviceIndex + 1);
343
strcat(config, tmpstr);
351
// A no-go: Instead we use 1 channel luminance8:
352
if (PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Video capture engine doesn't support requested Luminance+Alpha format. Will revert to pure Luminance instead...\n");
355
sprintf(tmpstr, " -pixelformat=40");
360
sprintf(tmpstr, " -pixelformat=24");
370
sprintf(tmpstr, " -pixelformat=ARGB");
375
PsychErrorExitMsg(PsychError_user, "You requested an invalid image depths (not one of 0, 1, 2, 3 or 4). Aborted.");
378
strcat(config, tmpstr);
381
#if PSYCH_SYSTEM == PSYCH_WINDOWS
382
if (reqdepth == 4 || reqdepth == 0) {
383
// Default is RGB32 bit:
385
sprintf(tmpstr, "><pixel_format><RGB32 flip_h=\"false\" flip_v=\"true\"/></pixel_format></camera></dsvl_input>");
388
// Only other supported format is RGB24 bit:
391
if (PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Video capture engine doesn't support requested Luminance format. Will revert to RGB color instead...\n");
395
// A no-go: Instead we use 1 channel luminance8:
396
if (PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Video capture engine doesn't support requested Luminance+Alpha format. Will revert to RGB color instead...\n");
404
PsychErrorExitMsg(PsychError_user, "You requested an invalid image depths (not one of 0, 1, 2, 3 or 4). Aborted.");
408
sprintf(tmpstr, "><pixel_format><RGB24 flip_h=\"false\" flip_v=\"true\"/></pixel_format></camera></dsvl_input>");
411
strcat(config, tmpstr);
413
if (deviceIndex == 4) {
414
// Fetch optional moviename parameter as override configuration string:
415
if (targetmoviefilename == NULL) PsychErrorExitMsg(PsychError_user, "You set 'deviceIndex' to a value of 4, but didn't provide the required override configuration string in the 'moviename' argument! Aborted.");
417
// Reset config string:
420
// And load the moviename argument as override string:
421
strcat(config, targetmoviefilename);
424
// End of MS-Windows specific setup.
427
#if PSYCH_SYSTEM == PSYCH_LINUX
428
// Specific deviceIndex requested, instead of auto-select?
429
if (deviceIndex!=-1) {
430
sprintf(tmpstr, " -dev=/dev/video%i", deviceIndex);
431
strcat(config, tmpstr);
440
// Requested output texture pixel depth in layers:
441
capdev->reqpixeldepth = reqdepth;
442
capdev->pixeldepth = reqdepth * 8;
444
// Number of DMA ringbuffers to use in DMA capture mode: If no number provided (==0), set it to 8 buffers...
445
#if PSYCH_SYSTEM == PSYCH_OSX
446
if (num_dmabuffers == 1) {
447
// Use single-buffering instead of triple buffering:
448
strcat(config, " -singlebuffer");
454
capdev->num_dmabuffers = num_dmabuffers;
456
capdev->num_dmabuffers = num_dmabuffers;
459
if (PsychPrefStateGet_Verbosity()>4) printf("PTB-INFO: Final configuration string passed to ARVideo library is:\n%s\n", config);
461
// Prepare error message in case its needed below:
462
sprintf(msgerr, "PTB-ERROR: Opening the %i. camera (deviceIndex=%i) failed!\n", deviceIndex + 1, deviceIndex);
464
// Try to open and initialize camera according to given settings:
465
capdev->camera = ar2VideoOpen(config);
467
// Error abort if camera init failed:
468
if(capdev->camera == NULL) {
471
PsychErrorExitMsg(PsychError_user, msgerr);
474
// Our camera should be ready: Assign final handle.
475
*capturehandle = slotid;
477
// Increase counter of open capture devices:
480
// Set zero framerate:
484
ar2VideoInqSize(capdev->camera, &(capdev->width), &(capdev->height));
486
// Create capture ROI corresponding to width and height of video image:
487
PsychMakeRect(capdev->roirect, 0, 0, capdev->width, capdev->height);
489
// Reset framecounter:
490
capdev->nrframes = 0;
491
capdev->grabber_active = 0;
493
printf("PTB-INFO: Camera successfully opened...\n");
499
* PsychARVideoCaptureRate() - Start- and stop video capture.
501
* capturehandle = Grabber to start-/stop.
502
* playbackrate = zero == Stop capture, non-zero == Capture
503
* dropframes = 0 - Always deliver oldest frame in DMA ringbuffer. 1 - Always deliver newest frame.
504
* --> 1 == drop frames in ringbuffer if behind -- low-latency capture.
505
* startattime = Deadline (in system time) for which to wait before real start of capture.
506
* Returns Number of dropped frames during capture.
508
int PsychARVideoCaptureRate(int capturehandle, double capturerate, int dropframes, double* startattime)
513
// Retrieve device record for handle:
514
PsychVidcapRecordType* capdev = PsychGetARVidcapRecord(capturehandle);
516
// Start- or stop capture?
517
if (capturerate > 0) {
519
if (capdev->grabber_active) PsychErrorExitMsg(PsychError_user, "You tried to start video capture, but capture is already started!");
522
capdev->last_pts = -1.0;
523
capdev->nr_droppedframes = 0;
524
capdev->frame_ready = 0;
526
// Framedropping is not supported by libARVideo, so we implement it ourselves.
527
// Store the 'dropframes' flag in our capdev struct, so the PsychARGetTextureFromCapture()
528
// knows how to handle this:
529
capdev->dropframes = (dropframes > 0) ? 1 : 0;
531
// Ready to go! Now we just need to tell the camera to start its capture cycle:
533
// Wait until start deadline reached:
534
if (*startattime != 0) PsychWaitUntilSeconds(*startattime);
536
// Start DMA driven isochronous data transfer:
537
if(PsychPrefStateGet_Verbosity()>5) printf("PTB-DEBUG: Starting capture...\n"); fflush(NULL);
539
// Start the video capture for this camera.
540
if (ar2VideoCapStart(capdev->camera) !=DC1394_SUCCESS) {
542
PsychErrorExitMsg(PsychError_user, "Unable to start capture on camera via ar2VideoCapStart() - Start of video capture failed!");
545
// Record real start time:
546
PsychGetAdjustedPrecisionTimerSeconds(startattime);
548
if(PsychPrefStateGet_Verbosity()>5) printf("PTB-DEBUG: Capture engine fully running...\n"); fflush(NULL);
550
// Query framerate and convert to floating point value and assign it:
551
#if PSYCH_SYSTEM == PSYCH_WINDOWS
552
ar2VideoInqFreq(capdev->camera, &framerate);
554
// TODO: Implement for non-Win32:
555
framerate = (float) capturerate;
558
capdev->fps = (double) framerate;
560
// Ok, capture is now started:
561
capdev->grabber_active = 1;
563
// Allocate conversion buffer if needed for YUV->RGB conversions.
564
if (capdev->pixeldepth == -1) {
565
// Not used at the moment!!
566
// Software conversion of YUV -> RGB needed. Allocate a proper scratch-buffer:
567
capdev->scratchbuffer = malloc(capdev->width * capdev->height * 3);
570
if(PsychPrefStateGet_Verbosity()>1) {
571
printf("PTB-INFO: Capture started on device %i - Width x Height = %i x %i - Framerate: %f fps.\n", capturehandle, capdev->width, capdev->height, capdev->fps);
576
if (capdev->grabber_active) {
577
// Stop isochronous data transfer from camera:
578
if (ar2VideoCapStop(capdev->camera) !=DC1394_SUCCESS) {
579
PsychErrorExitMsg(PsychError_user, "Unable to stop video transfer on camera! (ar2VideoCapStop() failed)!");
582
// Ok, capture is now stopped.
583
capdev->frame_ready = 0;
584
capdev->grabber_active = 0;
586
if (capdev->scratchbuffer) {
587
// Release scratch-buffer:
588
free(capdev->scratchbuffer);
589
capdev->scratchbuffer = NULL;
592
if(PsychPrefStateGet_Verbosity()>1){
593
// Output count of dropped frames:
594
if ((dropped=capdev->nr_droppedframes) > 0) {
595
printf("PTB-INFO: Video capture dropped %i frames on device %i to keep capture running in sync with realtime.\n", dropped, capturehandle);
598
if (capdev->nrframes>0) capdev->avg_decompresstime/= (double) capdev->nrframes;
599
printf("PTB-INFO: Average time spent in video decompressor (waiting/polling for new frames) was %f milliseconds.\n", (float) capdev->avg_decompresstime * 1000.0f);
600
if (capdev->nrgfxframes>0) capdev->avg_gfxtime/= (double) capdev->nrgfxframes;
601
printf("PTB-INFO: Average time spent in GetCapturedImage (intensity calculation Video->OpenGL texture conversion) was %f milliseconds.\n", (float) capdev->avg_gfxtime * 1000.0f);
608
// Reset framecounters and statistics:
609
capdev->nrframes = 0;
610
capdev->avg_decompresstime = 0;
611
capdev->nrgfxframes = 0;
612
capdev->avg_gfxtime = 0;
614
// Return either the real capture framerate (at start of capture) or count of dropped frames - at end of capture.
615
return((capturerate!=0) ? (int) (capdev->fps + 0.5) : dropped);
620
* PsychARGetTextureFromCapture() -- Create an OpenGL texturemap from a specific videoframe from given capture object.
622
* win = Window pointer of onscreen window for which a OpenGL texture should be created.
623
* capturehandle = Handle to the capture object.
624
* checkForImage = >0 == Just check if new image available, 0 == really retrieve the image, blocking if necessary.
625
* 2 == Check for new image, block inside this function (if possible) if no image available.
627
* timeindex = This parameter is currently ignored and reserved for future use.
628
* out_texture = Pointer to the Psychtoolbox texture-record where the new texture should be stored.
629
* presentation_timestamp = A ptr to a double variable, where the presentation timestamp of the returned frame should be stored.
630
* summed_intensity = An optional ptr to a double variable. If non-NULL, then sum of intensities over all channels is calculated and returned.
631
* 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...
632
* 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.
634
int PsychARGetTextureFromCapture(PsychWindowRecordType *win, int capturehandle, int checkForImage, double timeindex,
635
PsychWindowRecordType *out_texture, double *presentation_timestamp, double* summed_intensity, rawcapimgdata* outrawbuffer)
639
double targetdelta, realdelta, frames;
640
unsigned int intensity = 0;
641
unsigned int count, i, bpp;
642
unsigned char* pixptr;
643
psych_bool newframe = FALSE;
645
unsigned int pixval, alphacount;
648
unsigned char* input_image = NULL;
650
// Retrieve device record for handle:
651
PsychVidcapRecordType* capdev = PsychGetARVidcapRecord(capturehandle);
653
// Compute width and height for later creation of textures etc. Need to do this here,
654
// so we can return the values for raw data retrieval:
658
// Size of a single pixel in bytes:
659
bpp = capdev->reqpixeldepth;
661
// If a outrawbuffer struct is provided, we fill it with info needed to allocate a
662
// sufficient memory buffer for returned raw image data later on:
666
outrawbuffer->depth = bpp;
669
// int waitforframe = (checkForImage > 1) ? 1:0; // Blocking wait for new image requested?
671
// A checkForImage 4 means "no op" with the ARVideo capture engine: This is meant to drive
672
// a movie recording engine, ie., grant processing time to it. Our ARVideo engine doesn't
673
// support movie recording, so this is a no-op:
674
if (checkForImage == 4) return(0);
676
// Take start timestamp for timing stats:
677
PsychGetAdjustedPrecisionTimerSeconds(&tstart);
679
// Should we just check for new image?
681
// Reset current dropped count to zero:
682
capdev->current_dropped = 0;
684
if (capdev->grabber_active == 0) {
685
// Grabber stopped. We'll never get a new image:
689
// Check for image in polling mode: We capture in non-blocking mode:
690
capdev->frame = ar2VideoGetImage(capdev->camera);
692
// Ok, call succeeded. If the 'frame' pointer is non-NULL then there's a new frame ready and dequeued from DMA
693
// ringbuffer. We'll return it on next non-poll invocation. Otherwise no new video data ready yet:
694
capdev->frame_ready = (capdev->frame != NULL) ? 1 : 0;
697
if (capdev->frame_ready) {
698
// Store count of currently queued frames (in addition to the one just fetched).
699
// This is an indication of how well the users script is keeping up with the video stream,
700
// technically the number of frames that would need to be dropped to keep in sync with the
702
// TODO: Think about this. ARVideo doesn't support a query for pending/dropped frames, so
703
// we either need to live without this feature or think up something clever...
704
capdev->current_dropped = (int) 0;
706
// Ok, at least one new frame ready. If more than one frame has queued up and
707
// we are in 'dropframes' mode, ie. we should always deliver the most recent available
708
// frame, then we quickly fetch & discard all queued frames except the last one.
709
while((capdev->dropframes) && ((int) capdev->current_dropped > 0)) {
710
// We just poll - fetch the frames. As we know there are some queued frames, it
711
// doesn't matter if we poll or block, but polling sounds like a bit less overhead
714
// First enqueue the recently dequeued buffer...
715
if (ar2VideoCapNext(capdev->camera) != DC1394_SUCCESS) {
716
PsychErrorExitMsg(PsychError_system, "Requeuing of discarded video frame failed while dropping frames (dropframes=1)!!!");
719
// Then fetch the next one:
720
if ((capdev->frame = ar2VideoGetImage(capdev->camera)) == NULL) {
721
// Polling failed for some reason...
722
PsychErrorExitMsg(PsychError_system, "Polling for new video frame failed while dropping frames (dropframes=1)!!!");
727
// Update stats for decompression:
728
PsychGetAdjustedPrecisionTimerSeconds(&tend);
730
// Increase counter of decompressed frames:
733
// Update avg. decompress time:
734
capdev->avg_decompresstime+=(tend - tstart);
736
// Query capture timestamp in seconds:
737
// TODO: ARVideo doesn't provide such a timestamp. For now we just return the current
738
// system time as a lame replacement...
739
// On Windows there would be uint64 capdev->camera->g_Timestamp
740
PsychGetAdjustedPrecisionTimerSeconds(&(capdev->current_pts));
743
// Return availability status: 0 = new frame ready for retrieval. -1 = No new frame ready yet.
744
return((capdev->frame_ready) ? 0 : -1);
747
// This point is only reached if checkForImage == FALSE, which only happens
748
// if a new frame is available in our buffer:
750
// Presentation timestamp requested?
751
if (presentation_timestamp) {
753
*presentation_timestamp = capdev->current_pts;
756
// Synchronous texture fetch: Copy content of capture buffer into a texture:
757
// =========================================================================
759
// input_image points to the image buffer in our cam:
760
input_image = (unsigned char*) (capdev->frame);
762
// Do we want to do something with the image data and have a
763
// scratch buffer for color conversion alloc'ed?
764
if ((capdev->scratchbuffer) && ((out_texture) || (summed_intensity) || (outrawbuffer))) {
765
// Yes. Perform color-conversion YUV->RGB from cameras DMA buffer
766
// into the scratch buffer and set scratch buffer as source for
767
// all further operations:
769
memcpy(capdev->scratchbuffer, input_image, capdev->width * capdev->height * bpp);
771
// Ok, at this point we should have a RGB8 texture image ready in scratch_buffer.
772
// Set scratch buffer as our new image source for all further processing:
773
input_image = (unsigned char*) capdev->scratchbuffer;
776
// Only setup if really a texture is requested (non-benchmarking mode):
778
PsychMakeRect(out_texture->rect, 0, 0, w, h);
780
// Set NULL - special texture object as part of the PTB texture record:
781
out_texture->targetSpecific.QuickTimeGLTexture = NULL;
783
// Set texture orientation as if it were an inverted Offscreen window: Upside-down.
784
out_texture->textureOrientation = 3;
786
#if PSYCH_SYSTEM == PSYCH_WINDOWS
787
// On Windows in non RGB32 bit modes, set orientation to Upright:
788
out_texture->textureOrientation = (capdev->reqpixeldepth == 4) ? 3 : 2;
791
// Setup a pointer to our buffer as texture data pointer: Setting memsize to zero
792
// prevents unwanted free() operation in PsychDeleteTexture...
793
out_texture->textureMemorySizeBytes = 0;
795
// Set texture depth: Could be 8, 16, 24 or 32 bpp.
796
out_texture->depth = capdev->reqpixeldepth * 8;
798
// This will retrieve an OpenGL compatible pointer to the pixel data and assign it to our texmemptr:
799
out_texture->textureMemory = (GLuint*) input_image;
801
// Let PsychCreateTexture() do the rest of the job of creating, setting up and
802
// filling an OpenGL texture with content:
803
PsychCreateTexture(out_texture);
805
// Ready to use the texture...
808
// Sum of pixel intensities requested?
809
if(summed_intensity) {
810
pixptr = (unsigned char*) input_image;
812
for (i=0; i<count; i++) intensity+=(unsigned int) pixptr[i];
813
*summed_intensity = ((double) intensity) / w / h / bpp;
816
// Raw data requested?
821
outrawbuffer->depth = bpp;
822
count = (w * h * outrawbuffer->depth);
823
memcpy(outrawbuffer->data, (const void *) input_image, count);
826
// Release the capture buffer. Return it to the DMA ringbuffer pool:
827
if (ar2VideoCapNext(capdev->camera) != DC1394_SUCCESS) {
828
PsychErrorExitMsg(PsychError_system, "Re-Enqueuing processed video frame failed.");
831
// Update total count of dropped (or pending) frames:
832
capdev->nr_droppedframes += capdev->current_dropped;
833
nrdropped = capdev->current_dropped;
834
capdev->current_dropped = 0;
837
PsychGetAdjustedPrecisionTimerSeconds(&tend);
839
// Increase counter of retrieved textures:
840
capdev->nrgfxframes++;
842
// Update average time spent in texture conversion:
843
capdev->avg_gfxtime+=(tend - tstart);
845
// We're successfully done! Return number of dropped (or pending in DMA ringbuffer) frames:
850
/* Set capture device specific parameters:
851
* Currently, the named parameters are a subset of the parameters supported by the
852
* IIDC specification, mapped to more convenient names.
854
* Input: pname = Name string to specify the parameter.
855
* value = Either DBL_MAX to not set but only query the parameter, or some other
856
* value, that we try to set in the Firewire camera.
858
* Returns: Old value of the setting
860
double PsychARVideoCaptureSetParameter(int capturehandle, const char* pname, double value)
862
unsigned int minval, maxval, intval, oldintval;
865
double oldvalue = DBL_MAX; // Initialize return value to the "unknown/unsupported" default.
866
psych_bool assigned = false;
867
psych_bool present = false;
869
// Retrieve device record for handle:
870
PsychVidcapRecordType* capdev = PsychGetARVidcapRecord(capturehandle);
872
oldintval = 0xFFFFFFFF;
874
// Round value to integer:
875
intval = (int) (value + 0.5);
877
// Check parameter name pname and call the appropriate subroutine:
878
if (strcmp(pname, "TriggerCount")==0 || strcmp(pname, "WaitTriggerCount")==0) {
879
// Query of cameras internal trigger counter or waiting for a specific
880
// value in the counter requested. Trigger counters are special features,
881
// (so called "Smart Features" or "Advanced Features" in the IIDC spec)
882
// which are only available on selected cameras.
883
// We currently only know how to do this on Basler cameras.
887
if (strcmp(pname, "PrintParameters")==0) {
888
// Special command: List and print all features...
889
printf("PTB-INFO: The camera provides the following information and featureset:\n");
890
ar2VideoDispOption();
894
// Return current framerate:
895
if (strcmp(pname, "GetFramerate")==0) {
896
PsychCopyOutDoubleArg(1, FALSE, capdev->fps);
900
// Return current ROI of camera, as requested (and potentially modified during
901
// PsychOpenCaptureDevice(). This is a read-only parameter, as the ROI can
902
// only be set during Screen('OpenVideoCapture').
903
if (strcmp(pname, "GetROI")==0) {
904
PsychCopyOutRectArg(1, FALSE, capdev->roirect);
908
// Return vendor name string:
909
if (strcmp(pname, "GetVendorname")==0) {
910
PsychCopyOutCharArg(1, FALSE, "Unknown Vendor");
914
// Return model name string:
915
if (strcmp(pname, "GetModelname")==0) {
916
PsychCopyOutCharArg(1, FALSE, "Unknown Model");
920
// if (strstr(pname, "Brightness")!=0) {
922
// feature = DC1394_FEATURE_BRIGHTNESS;
925
// if (strstr(pname, "Gain")!=0) {
927
// feature = DC1394_FEATURE_GAIN;
930
// if (strstr(pname, "Exposure")!=0) {
932
// feature = DC1394_FEATURE_EXPOSURE;
935
// if (strstr(pname, "Shutter")!=0) {
937
// feature = DC1394_FEATURE_SHUTTER;
940
// if (strstr(pname, "Sharpness")!=0) {
942
// feature = DC1394_FEATURE_SHARPNESS;
945
// if (strstr(pname, "Saturation")!=0) {
947
// feature = DC1394_FEATURE_SATURATION;
950
// if (strstr(pname, "Gamma")!=0) {
952
// feature = DC1394_FEATURE_GAMMA;
955
// Check if feature is present on this camera:
956
// Not supported yet:
959
// if (dc1394_feature_is_present(capdev->camera, feature, &present)!=DC1394_SUCCESS) {
960
// if(PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Failed to query presence of feature %s on camera %i! Ignored.\n", pname, capturehandle);
966
// Feature is available:
968
// Retrieve current value:
969
if (dc1394_feature_get_value(capdev->camera, feature, &oldintval)!=DC1394_SUCCESS) {
970
if(PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Failed to query value of feature %s on camera %i! Ignored.\n", pname, capturehandle);
974
// Do we want to set the value?
975
if (value != DBL_MAX) {
976
// Query allowed bounds for its value:
977
if (dc1394_feature_get_boundaries(capdev->camera, feature, &minval, &maxval)!=DC1394_SUCCESS) {
978
if(PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Failed to query valid value range for feature %s on camera %i! Ignored.\n", pname, capturehandle);
982
// Sanity check against range:
983
if (intval < minval || intval > maxval) {
984
if(PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Requested setting %i for parameter %s not in allowed range (%i - %i) for camera %i. Ignored.\n",
985
intval, pname, minval, maxval, capturehandle);
989
// Ok intval is valid for this feature: Can we manually set this feature?
990
// Switch feature to manual control mode:
991
if (dc1394_feature_set_mode(capdev->camera, feature, DC1394_FEATURE_MODE_MANUAL)!=DC1394_SUCCESS) {
992
if(PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Failed to set feature %s on camera %i to manual control! Ignored.\n", pname, capturehandle);
996
// Ok, try to set the features new value:
997
if (dc1394_feature_set_value(capdev->camera, feature, intval)!=DC1394_SUCCESS) {
998
if(PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Failed to set value of feature %s on camera %i to %i! Ignored.\n", pname, capturehandle,
1007
// Don't want to set new value. Do we want to reset feature into auto-mode?
1008
// Prefixing a parameter name with "Auto"
1009
// does not switch the parameter into manual
1010
// control mode + set its value, as normal,
1011
// but it switches the parameter into automatic
1012
// mode, if automatic mode is supported by the
1014
if (strstr(pname, "Auto")!=0) {
1015
// Switch to automatic control requested - Try it:
1016
if (dc1394_feature_set_mode(capdev->camera, feature, DC1394_FEATURE_MODE_AUTO)!=DC1394_SUCCESS) {
1017
if(PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Failed to set feature %s on camera %i to automatic control! Ignored.\n", pname, capturehandle);
1026
if(PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Requested capture device setting %s not available on cam %i. Ignored.\n", pname, capturehandle);
1030
// Output a warning on unknown parameters:
1032
if(PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Screen('SetVideoCaptureParameter', ...) called with unknown parameter %s. Ignored...\n",
1037
if (assigned && oldintval!=0xFFFFFFFF) oldvalue = (double) oldintval;
1039
// Return the old value. Could be DBL_MAX if parameter was unknown or not accepted for some reason.