File: PsychVideoCaptureSupport.c

package info (click to toggle)
psychtoolbox-3 3.0.19.14.dfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 86,796 kB
  • sloc: ansic: 176,245; cpp: 20,103; objc: 5,393; sh: 2,753; python: 1,397; php: 384; makefile: 193; java: 113
file content (350 lines) | stat: -rw-r--r-- 13,514 bytes parent folder | download | duplicates (3)
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;
}