This chapter explains how to capture video using the IndigoVideo board. The IndigoVideo Library provides these three methods for capturing video:
single frame
burst mode
continuous mode
In this chapter:
“Captured Video Data Formats” provides a brief introduction to the data formats for capturing video.
“Capturing a Single Video Frame” describes the single-frame capture method. You can easily capture a single frame of video with just one call to the convenience routine svCaptureOneFrame().
“Capturing Video Frames in Burst Mode” describes the burst-mode capture method. In burst mode, IndigoVideo captures a buffer full of sequential frames at full frame rate in most formats. You can use burst mode capture in conjunction with a computer-controllable video device to read in segments of video and stop the deck while your program processes the buffer full of frames.
“Capturing Video Frames in Continuous Mode” describes the continuous-mode capture method. In continuous mode, IndigoVideo captures frames of video into a queue at less than full frame rate. In this mode, you can capture reduced size and reduced frame rate video directly to disk, allowing you to collect images for a movie without a computer-controllable video device. In continuous capture mode, you can capture frames at no more than half the normal frame rate.
“Using Data Conversion Routines” describes how to convert video data for graphics display.
This section describes the data formats used by the IndigoVideo Library capture routines. For more information on these formats, see “IndigoVideo Data Formats” in Chapter 17. The data formats are listed below, along with the symbolic constants that the IndigoVideo Library uses to identify them.
| SV_RGB8_FRAMES |
| |
| SV_YUV411_FRAMES_AND_BLANKING_BUFFER |
| |
| SV_RGB32_FRAMES |
| |
| SV_YUV411_FRAMES |
|
Table 20-1 summarizes the storage requirements for the various data formats:
Several of the capture routines take an svCaptureInfo structure as an argument. The fields in the svCaptureInfo structure are listed in Table 20-2.
Table 20-2. Fields in the svCaptureInfo Structure
Field | Value |
|---|---|
format | format (one of the symbolic constants listed above) |
width | width of captured frames, in pixels |
height | height of captured frames, in pixels |
size | size of the capture buffer, in frames |
samplingrate | used for continuous capture |
Note that the width and height members of the svCaptureInfo structure are input-output parameters; that is, if you set them to an unsupported size such as 321 by 243 pixels, they will be set to the nearest approximation of the requested size by the capture routines.
If you are capturing 8-bit RGB frames, and you do not specify width and height, the IndigoVideo Library will use the width and height of the live video input window, if one is active. The video capture routines set the size of the video image to the requested size, so if you have a live video input window active, you may have to reset the size of the video image after capturing frames.
The IndigoVideo Library provides a convenient way to capture a single frame of video in any of the supported formats. The svCaptureOneFrame() routine captures data into a user-allocated buffer. This buffer must be big enough to hold a single frame in the specified format.
You can use the svQueryCaptureBufferSize() routine to determine the size of buffer required by svCaptureOneFrame(), then use malloc() to reserve a buffer of the appropriate size, as demonstrated in Example 20-1.
Example 20-1. Determining the Capture Buffer Size
SVhandle V;
svCaptureInfo capInfo;
char *buffer;
int width, height, bufSize;
/* ... */
capInfo.format = SV_RGB8_FRAMES;
capInfo.width = width;
capInfo.height = height;
(void) svQueryCaptureBufferSize(V, &capInfo, &bufSize);
buffer = malloc(bufSize);
|
Example 20-2 contains a listing of the /usr/people/4Dgifts/examples/dmedia/video/indigovideo program rgbgrab.c , which demonstrates the use of svCaptureOneFrame(). This program lets the user click the left mouse button to grab a frame of 8-bit RGB data and display it in a window. See oneframe.c for an example of capturing other data formats.
Example 20-2. Grabbing a Single Frame of 8-bit RGB data: rgbgrab.c
/*
* Simple frame grabbing using video capture.
*
* To use: click on the left mouse button in either window
* to grab a frame and display it.
*/
#include <stdio.h>
#include <stdlib.h>
#include <svideo.h>
#include <gl/gl.h>
#include <gl/device.h>
#define RGBBUFSIZE (SV_NTSC_XMAX*SV_NTSC_YMAX)
static char captureData[RGBBUFSIZE], rgbbuf[RGBBUFSIZE];
main()
{
SVhandle V;
long dev, live_win, still_win;
short val;
int w, h;
/* Open window */
foreground();
prefsize(SV_NTSC_XMAX, SV_NTSC_YMAX);
still_win = winopen("Grabbed frame");
RGBmode();
gconfig();
pixmode(PM_SIZE, 8);
prefsize(SV_NTSC_XMAX, SV_NTSC_YMAX);
live_win = winopen("Live video");
/* Open video device */
if ((V = svOpenVideo()) == NULL) {
svPerror("open");
exit(1);
}
/* Associate video input with this window */
if (svBindGLWindow(V, live_win, SV_IN_REPLACE) < 0) {
svPerror("bindwindow");
svCloseVideo(V);
exit(1);
}
printf("Use leftmouse to grab frame\n");
/* Event loop */
qdevice(LEFTMOUSE);
qdevice(WINQUIT);
qdevice(WINSHUT);
qdevice(ESCKEY);
while (1) {
dev = qread(&val);
switch (dev) {
case LEFTMOUSE:
if (val != 1) /* button-press */
break;
w = SV_NTSC_XMAX;
h = SV_NTSC_YMAX;
if (svCaptureOneFrame(V, SV_RGB8_FRAMES,
&w, &h, (char *)captureData) < 0) {
svPerror("captureburst");
exit(-1);
}
svInterleaveFields(TRUE, captureData, rgbbuf, w, h);
winset(still_win);
lrectwrite(0, 0, w-1, h-1, (unsigned long *) rgbbuf);
winset(live_win);
break;
case ESCKEY:
if (val) /* exit on key up */
break;
case WINQUIT:
case WINSHUT:
svCloseVideo(V);
winclose(live_win);
winclose(still_win);
exit(0);
break;
}
}
}
|
Use the svCaptureBurst() routine to capture a contiguous series of frames into a previously allocated buffer. In burst mode, you can capture full-sized YUV frames with blanking buffers or 8-bit RGB frames at full or reduced size. Initiating a burst capture puts the IndigoVideo board into exclusive mode, which remains set until the capture is complete or an error occurs.
You must pass svCaptureBurst() a pointer to an svCaptureInfo structure, which determines the number and type of frames to be captured. This svCaptureInfo structure can also be passed to svQueryCaptureBufferSize() to determine how much memory to allocate for the capture buffer.
The fields in the svCaptureInfo structure are listed in Table 20-2. Remember that the width and height members of the svCaptureInfo structure are input-output parameters; if you set them to an unsupported size, they will be set to the nearest approximation of the requested size when you call svQueryCaptureBufferSize() or svCaptureBurst().
The final argument to svCaptureBurst() is an optional pointer to a bitvector for the SV_RGB8_FRAMES format, which can be used to determine whether any fields have been dropped during the capture. Fields must occasionally be dropped during capture to avoid visible tearing in the image due to scan rate conversion. The SV_FIELDDROP parameter controls whether fields are dropped, see svGetParam(3) for details.
Every captured frame is represented by a pair of status bits (one for each field) in the bitvector. Each bit is set to either SV_EVEN_FIELD or SV_ODD_FIELD. Every frame should consist of an even field followed by an odd field; if this is not true, then one or more fields have been dropped during the capture. Use the SV_GET_FIELD macro, which is defined in svideo.h, to determine the even or odd value for a specific field.
The code fragment in Example 20-3 prints the even/odd values for the fields that make up the frames from a captured burst.
Example 20-3. Using the SV_GET_FIELD Macro
for (f = 0; f < info->size; f++) {
printf("%s-%s ",
SV_GET_FIELD(framevec, 2*f) == SV_EVEN_FIELD ? "even" : "odd",
SV_GET_FIELD(framevec, 2*f+1) == SV_EVEN_FIELD ? "even" : "odd");
}
putchar('\n');
|
The status bits are filled in starting with the least significant bit in the first byte of the bitvector. You must allocate one byte of bitvector for every four frames or fraction thereof. Use the SV_BITVEC_SIZE macro to calculate the size of the buffer in bytes for a given number of frames.
The format can be either 8-bit RGB or YUV with blanking buffer. This is because data is transferred directly from IndigoVideo to memory, and the IndigoVideo hardware produces only these two formats.
If you need 32-bit RGB frames, you can capture YUV frames and use svFindVisibleRegion() and svYUVtoRGB() to convert the data, as discussed in “Converting YUV Data to RGB”.
See the burstcapt.c sample program in /usr/people/4Dgifts/examples/dmedia/video/indigovideo for a demonstration of how to capture and display a burst of frames in either format.
The number of frames that can be captured in burst mode is limited to what will fit in the memory buffer, which has a maximum size of 8 MB. The frame size affects how many frames will fit in the buffer, because larger frames have more data.
![]() | Note: Currently, svCaptureBurst() can only capture YUV frames with blanking data at half the full frame rate. |
The code fragment in Example 20-4 shows how to use svCaptureBurst().
Example 20-4. Capturing Frames in Burst Mode
SVhandle V;
char *buffer, *bitVector;
int numberOfFrames=8, bufSize, bitVectorSize;
svCaptureInfo capInfo;
/* ... */
capInfo.format = SV_YUV411_FRAMES;
capInfo.width = SV_PAL_XMAX;
capInfo.height = SV_PAL_YMAX;
capInfo.size = numberOfFrames;
(void) svQueryCaptureBufferSize(V, &capInfo, &bufSize);
buffer = malloc(bufSize);
bitVectorSize = numberOfFrames / 4 + 1;
bitVector = malloc(bitVectorSize);
if (svCaptureBurst(V, &capInfo, buffer, bitVector) < 0) {
svPerror("capture burst");
} else { /* process frames */
}
|
In continuous capture mode, IndigoVideo writes frames of video into a queue while your program reads frames out of the queue. To enter continuous capture mode, call svInitContinousCapture(), which allocates memory for the capture queue and begins capturing frames. You can read frames from the queue using the svGetCaptureData() routine. Once you are done with a frame, you must release it using svUnlockCaptureData(). When you are done capturing frames, call svEndContinuousCapture() to leave continuous capture mode.
Continuous capture mode does not give you full frame rate—the maximum frame rate achievable in this mode is one half the normal frame rate. The samplingrate member of the svCaptureInfo structure specifies the number of frames seen for each frame captured. Thus, a sampling rate of two captures gives you every other frame, and a sampling rate of four captures gives you every fourth frame.
Use svInitContinousCapture() to enter continuous capture mode.
The code fragment in Example 20-5 demonstrates how to initialize continuous capture. See the contcapt.c sample program in /usr/people/4Dgifts/examples/dmedia/video/indigovideo for a complete program using continuous capture.
Example 20-5. Initializing Continuous Capture Mode
SVhandle V; int success; int width=320, height=240, queueSize=16, samplingRate=2; svCaptureInfo capInfo; /* ... */ capInfo.format = SV_RGB8_FRAMES; capInfo.width = width; capInfo.height = height; capInfo.size = queueSize; capInfo.samplingrate = samplingRate; success = svInitContinuousCapture(V, capInfo); |
svInitContinousCapture() allocates the frame capture queue and takes control of the IndigoVideo board using the svUseExclusive() routine. If another program already has exclusive use of the board, svInitContinousCapture() returns -1; if it succeeds, svInitContinuousCapture() returns 0. In this example, 8-bit RGB frames are being captured at half size and half frame rate.
Call svEndContinuousCapture() after you have completed video capture, to release control of the board. If you release control of the video board using svUseExclusive() during continuous capture mode, continuous capture mode is automatically terminated.
To access the next frame in the video capture queue, use the svGetCaptureData() routine. When you're done with the frame, release it using svUnlockCaptureData(), so that IndigoVideo can reuse the memory.
The code fragment in Example 20-6 demonstrates how to access and release captured frames.
Example 20-6. Accessing and Releasing Captured Frames
SVhandle V;
void *data;
long fieldID;
long curFrame, maxFrames;
/* ... */
while (TRUE) {
svGetCaptureData(V, &data, &fieldID);
if (data == NULL) {
/* no frame available yet */
sginap(1);
} else {
/* process data */
svUnlockCaptureData(V, data);
curFrame++;
if (curFrame == maxFrames) break;
}
}
|
The data parameter is pointed to the next frame in the queue, and the fieldID parameter is filled in to indicate the ID of the first field in the frame. If there are no frames in the queue, the data parameter is set to NULL (this will happen if you are processing frames faster than they are captured).
Because there are two fields in each frame, the fieldID value increases by 2 for each video frame. Therefore, for a sampling rate of S, the fieldID should increase by 2 × S between captured frames. If the fieldID increases by more than 2 × S between captured frames, you have missed one or more frames. This could be because the system load is high, or because the queue has filled up, forcing the IndigoVideo board to drop frames.
Although you don't have to unlock a frame before you get the next one, it's a good idea; if you keep a lot of frames locked, you will probably force the IndigoVideo software to drop frames. At any rate, you should unlock frames in the same order in which you received them. The IndigoVideo software writes the frames in order, and if it encounters a locked frame, it will block until the frame is unlocked.
The data parameter points to memory in the queue, which may be overwritten as soon as you release the element. Once you have the queue element, you'll probably want to write it to disk or copy it to a location where it won't be overwritten.
This section describes the convenience routines for converting data from IndigoVideo into other formats. For more information on the various data formats that IndigoVideo uses, see “IndigoVideo Data Formats” in Chapter 17.
The IndigoVideo Library provides a utility routine, svYUVtoRGB(), for converting YUV frames to RGB format. This routine produces 32-bit pixels, as used by the IRIS GL lrectwrite() routine. Each pixel contains 24 bits of RGB data and 8 bits of unused space. (In the YUV format, a pixel takes up only 16 bits, so if you're trying to record frames to disk as fast as possible, you should store the YUV data directly and convert it later.)
Rows of pixels in the YUV frames are ordered top-to-bottom. This differs from the default ordering used by the IRIS GL lrectwrite() routine, which is bottom-to-top. If you set the invert parameter to TRUE, the svYUVtoRGB() routine will return an RGB frame with lines ordered bottom-to-top. If invert is FALSE, svYUVtoRGB() will not perform this inversion (this is useful because the X Window System expects the top-to-bottom ordering).
Alternately, if you want to display the frame, you can set up lrectwrite() to use top-to-bottom ordering by using the IRIS GL pixmode() routine, as shown in Example 20-7.
Example 20-7. Setting Top-to-Bottom pixmode for YUV
boolean invert = FALSE;
int width, height;
long *yuv_buf;
long rgb_buf;
/* ... */
rgb_buf = malloc(width*height*sizeof(*rgbbuf));
if (svYUVtoRGB(invert, yuv_buf, rgb_buf, width, height)==-1) {
/* error */
}
pixmode(PM_TTOB, 1); /* pixel ordering top-to-bottom */
lrectwrite(0, 0, width-1, height-1, rgb_buf);
|
![]() | Note: This code may not work on some older IRIS workstations. |
If you capture YUV frames with blanking data, you can use svFindVisibleRegion() to locate the start of the YUV image data, as demonstrated in Example 20-8.
Example 20-8. Finding Image Data in YUV with Blanking Frames
SVhandle V; void *frame_with_blanking, *frame; long fieldID; /* ... */ svGetCaptureData(V, &frame_with_blanking, &fieldID); svFindVisibleRegion(V, frame_with_blanking, &frame); svUnlockCaptureData(frame_with_blanking); /* process frame */ |
Example 20-9 contains a listing of vgrab.c , in /usr/people/4Dgifts/examples/dmedia/video/indigovideo, which demonstrates how to convert grabbed YUV frames to RGB images.
Example 20-9. Grabbing YUV Frames to Save as RGB Images: vgrab.c
/*
* vgrab.c
* Grab YUV frames, save as SGI RGB images
*/
#include <stdio.h>
#include <gl/gl.h>
#include <gl/image.h>
#include <gl/device.h>
#include <svideo.h>
#define GRABFILE "out.rgb"
#define RGBBUFSIZE (SV_PAL_XMAX*SV_PAL_YMAX*sizeof(long))
static char rgbbuf[RGBBUFSIZE];
/*
* Dump rgb data to image file
*/
void
dumpImage(char *data, int xsize, int ysize)
{
IMAGE *image;
short rbuf[SV_PAL_XMAX];
short gbuf[SV_PAL_XMAX];
short bbuf[SV_PAL_XMAX];
int x, y, z;
image = iopen(GRABFILE, "w", RLE(1), 3, xsize, ysize, 3);
for (y=0;y<ysize;y++) {
for(x=0;x<xsize;x++) {
bbuf[x] = *(data+1);
gbuf[x] = *(data+2);
rbuf[x] = *(data+3);
data += 4;
}
putrow(image, rbuf, y, 0);
putrow(image, gbuf, y, 1);
putrow(image, bbuf, y, 2);
}
iclose(image);
}
main(int argc, char **argv)
{
short val;
long livewin, stillwin, x, y;
int width, height;
SVhandle V;
long param[2];
int videoon = 1;
/* Open video device */
if ((V = svOpenVideo()) == NULL) {
svPerror("open");
exit(1);
}
/* Determine window size based on signal standard */
param[0] = SV_BROADCAST;
svGetParam(V, param, 2);
if (param[1] == SV_PAL) {
width = SV_PAL_XMAX;
height = SV_PAL_YMAX;
} else {
width = SV_NTSC_XMAX;
height = SV_NTSC_YMAX;
}
/* Open windows */
foreground();
prefsize(width, height);
stillwin = winopen("Grabbed frame");
RGBmode();
gconfig();
/* Set video window background to black */
cpack(0x0);
clear();
maxsize(width, height);
keepaspect(width, height);
stepunit(8, 6);
livewin = winopen("video in");
RGBmode();
gconfig();
getsize(&x, &y);
svSetSize(V, x, y);
/* Associate video input with livewin */
if (svBindGLWindow(V, livewin, SV_IN_REPLACE) < 0) {
svPerror("bindwindow");
exit(1);
}
printf("Click on left mouse button to grab frame\n");
qdevice(LEFTMOUSE);
qdevice(WINQUIT);
qdevice(WINSHUT);
qdevice(ESCKEY);
while (1) {
switch (qread(&val)) {
case LEFTMOUSE:
if (val != 1)
break;
svCaptureOneFrame(V, SV_RGB32_FRAMES, &width,
&height, rgbbuf);
winset(stillwin);
lrectwrite(0, 0, width-1, height-1,
(unsigned long *) rgbbuf);
winset(livewin);
if (svSetSize(V, x, y) < 0) {
svPerror("setsize");
exit(1);
}
/* Re-bind window to re-scale output */
if (svBindGLWindow(V, livewin,
SV_IN_REPLACE) < 0) {
svPerror("bindwindow");
exit(1);
}
dumpImage(rgbbuf, width, height);
printf("saved image to file %s\n", GRABFILE);
break;
case REDRAW:
reshapeviewport();
getsize(&x, &y);
svSetSize(V, x, y);
/* Re-bind window to re-scale output */
if (svBindGLWindow(V, livewin,
SV_IN_REPLACE) < 0) {
svPerror("bindwindow");
exit(1);
}
break;
case ESCKEY:
if (val) /* exit on key up */
break;
case WINQUIT:
case WINSHUT:
winclose(stillwin);
winclose(livewin);
svCloseVideo(V);
exit(0);
break;
}
}
}
|
The fields in a frame of 8-bit RGB data captured with svGetCaptureData() are not interleaved; all the even rows of pixels are stored before all the odd rows of pixels. In addition, the rows within the fields are ordered top-to-bottom. The IndigoVideo Library provides a convenience routine, svInterleaveFields(), to interleave, and optionally invert, the fields. It produces 8-bit RGB data rather than SV_RGB_FRAMES data.
Example 20-10 demonstrates how to invert fields and interleave them.
Example 20-10. Interleaving 8-bit RGB Fields with Inversion
boolean invert = TRUE; char *fields, *rgb8frame; int width, height; /* ... */ frame = malloc(width*height); svInterleaveFields(invert, fields, rgb8frame,width, height) |
Once interleaved, you can display the 8-bit RGB data directly on an Indigo workstation with Entry graphics that has the svideo software installed, by using the RGBmode(), pixmode(), and lrectwrite() routines, as demonstrated in Example 20-11.
Example 20-11. Displaying Interleaved 8-bit RGB Data
char *rgb8frame;
int width, height;
/* ... */
RGBmode();
gconfig();
pixmode(PM_SIZE, 8);
lrectwrite(0, width-1, 0, height-1, rgb8frame);
|
To display the 8-bit RGB data on other systems, convert it to the more common 32-bit RGB format by using the svRGB8toRGB32() routine, which converts the data to 32-bit in addition to interleaving, and, optionally, inverting the fields, as demonstrated in Example 20-12.
Example 20-12. Converting 8-bit RGB Capture Data to 32-bit RGB
char *rgb8fields;
long *rgb32frame;
int width, height;
boolean invert = TRUE;
/* ... */
rgb32frame = malloc(width*height*sizeof(*rgb32frame));
svRGB8toRGB32(invert, rgb8fields, rgb32frame, width, height);
|
The svRGB8toRGB32() works on 8-bit RGB field data only in the format captured by the IndigoVideo board. Like svInterleaveFields(), it will interleave, and, optionally, invert the data (bottom-to-top) if the invert parameter is true.
If you don't want to convert the 8-bit RGB data, you can display it on any system by treating the 8-bit pixels as 8-bit color index values. In Example 20-13, the code fragment, from the /usr/people/4Dgifts/examples/dmedia/video/indigovideo program vmirror.c , sets up the IRIS GL color map to display 8-bit RGB data:
Example 20-13. Setting up the IRIS GL Color Map to Display 8-bit RGB
/* Change GL color map to display IndigoVideo RGB8 data */
static void
makevideomap(void)
{
int r, g, b;
for (r=0; r<8; r++) {
for (b=0; b<4; b++) {
for (g=0; g<8; g++) {
mapcolor((r<<5)|(b<<3)|g,
(r<<5)|(r<<2)|(r>>1),
(g<<5)|(g<<2)|(g>>1),
(b<<6)|(b<<4)|(b<<2)|b);
}
}
}
gflush();
}
|