Chapter 17, “Getting Started with the IndigoVideo Library,” showed you how to create a video input window. This chapter presents more details on the process and demonstrates how to control various aspects of the video display.
In this chapter:
“Setting Input Parameters” explains how to set input parameters that provide information about the input source and signal for the IndigoVideo board.
“Querying Video Parameters” describes how to obtain information about the IndigoVideo board status.
“Positioning and Scaling the Video Input” explains how to create and configure the video input window.
“Preventing Other Programs from Using Video” explains how to take control of the IndigoVideo board for exclusive use.
“Combining Video and Graphics” provides suggestions for using video with graphics, including overlays and underlays.
You can use the svSetParam() routine to set a number of different video parameters. This section describes some of the parameters that affect the video input window. See svGetParam(3V) for a complete list of parameters.
svSetParam() takes three arguments: a video handle, an array of long integers specifying parameters and values, and an argument specifying the length of the array. The values in the array are interpreted in pairs: the first member of each pair represents the parameter to be changed; the second member represents the new value for that parameter. The code fragment in Example 18-1 sets up the IndigoVideo board to receive PAL input over an S-Video connector.
Example 18-1. Setting up the IndigoVideo Board for PAL Input
SVhandle V; long param[4]; /* . . . */ param[0] = SV_BROADCAST; param[1] = SV_PAL; param[2] = SV_VIDEO_MODE; param[3] = SV_SVIDEO; svSetParam(V, param, 4); |
You can plug up to three video inputs into the IndigoVideo board and select between them by changing the SV_SOURCE parameter with svSetParam(). Set SV_SOURCE to SV_SOURCE1, SV_SOURCE2, or SV_SOURCE3. These constants correspond to the input connectors labeled 1, 2, and 3. The code fragment in Example 18-2 demonstrates setting up the input source.
Example 18-2. Selecting a Video Input Source
void setSource(SVhandle V, int source)
{
long param[2];
if (source < 1 || source > 3) {
/* error */
}
param[0] = SV_SOURCE;
switch (source) {
case 1: param[1] = SV_SOURCE1;
break;
case 2: param[1] = SV_SOURCE2;
break;
case 3: param[1] = SV_SOURCE3;
break;
}
svSetParam(V, param, 2);
}
|
To enable the IndigoVideo board to display video input correctly, you must set it to the correct broadcast standard and format. These may be set using svSetParam(). The broadcast standard parameter, SV_BROADCAST, may be set to one of two values: SV_NTSC for NTSC video, or SV_PAL for PAL video. The SV_VIDEO_MODE parameter controls input format; it can be set to SV_COMP for composite video input, or SV_SVIDEO for S-Video input.
You can also set the default video mode and broadcast standard for each input source using the Video Control Panel. This procedure is explained in Chapter 22, “Using the IndigoVideo Utilities.”
You can determine the value of a parameter by using the svGetParam() routine. This routine takes exactly the same arguments as svSetParam(), but instead of reading new parameter values from the array, it fills in the current parameter values in the appropriate places.
The code fragment in Example 18-3 displays the number of the currently selected input source.
Example 18-3. Getting the Input Source Number
SVhandle V;
long param[2];
int source;
/* ... */
param[0] = SV_SOURCE;
svGetParam(V, param, 2);
switch(param[1]) {
case SV_SOURCE1: source = 1;
break;
case SV_SOURCE2: source = 2;
break;
case SV_SOURCE3: source = 3;
break;
}
printf("Current video source is %d\n", source);
|
In addition to the input parameters already mentioned, two special read-only parameters can be used in conjunction with svGetParam() to get information about the video source (if any) connected to the current input:
| SV_SIGNAL_STD |
SV_NTSC for NTSC input | |
| SV_SIGNAL_COLOR |
|
By default, the IndigoVideo software displays video input at full resolution, with the origin of the video image at the upper left corner of the video input window. You can set the size and position of the video image using the svSetSize() and svWindowOffset() routines. If you call one of these routines after binding video to a window, you must rebind the video using either svBindWindow() or svBindGLWindow() for the change to take effect.
![]() | Note: The IndigoVideo positioning and scaling routines take arguments in pixels, not in IRIS GL coordinates. Because arguments to svSetSize() and svWindowOffset() are not in IRIS GL coordinates, they are not affected by the current IRIS GL transformation matrix. |
You can set the size of the video image within certain constraints:
The aspect ratio of the image (ratio of horizontal size to vertical size) must remain constant.
The image can be scaled only in increments of 8 pixels of horizontal size and 6 pixels of vertical size.
The image cannot be larger than the video frame size (640 by 480 pixels for NTSC, 768 by 576 pixels for PAL).
To allow the user to resize the video input window, you can use the IRIS GL minsize(), maxsize(), and stepunit() routines to constrain the window to a useful size.
Example 18-4 contains a listing of sizeinput.c , in /usr/people/4Dgifts/examples/dmedia/video/indigovideo, which implements a user-resizable video input window.
Example 18-4. Creating a Scalable Video Input Window: sizeinput.c
/*
* Scalable GL Video Input Window
*/
#include <stdlib.h>
#include <svideo.h>
#include <gl/gl.h>
#include <gl/device.h>
main()
{
short val;
long win, dev, x, y;
SVhandle V;
/* Open window */
minsize(80, 60);
stepunit(8, 6);
maxsize(SV_NTSC_XMAX, SV_NTSC_YMAX);
keepaspect(SV_NTSC_XMAX, SV_NTSC_YMAX);
win = winopen("video in");
if ((V = svOpenVideo()) == NULL) {
svPerror("open");
exit(1);
}
getsize(&x, &y);
svSetSize(V, x, y);
if (svBindGLWindow(V, win, SV_IN_REPLACE) < 0) {
svPerror("bindwindow");
svCloseVideo(V);
exit(1);
}
/* Event loop */
qdevice(ESCKEY);
qdevice(WINQUIT);
qdevice(WINSHUT);
while (1) {
dev = qread(&val);
switch (dev) {
case REDRAW:
getsize(&x, &y); /* may have been resized */
svSetSize(V, x, y);
/* Re-bind window to scale input */
if (svBindGLWindow(V, win, SV_IN_REPLACE) < 0){
svPerror("bindwindow");
svCloseVideo(V);
exit(1);
}
break;
case ESCKEY:
case WINQUIT:
case WINSHUT:
svCloseVideo(V);
winclose(win);
exit(0);
break;
}
}
}
|
In the preceding example, the window is constrained to sizes supported by the IndigoVideo software. The event loop handles the resizing of the video input. When the program receives a REDRAW event, which could indicate a size change, it determines the new size of the window, calls svSetSize() to scale the video input appropriately, and rebinds the video input to the window.
If you use svSetSize() to specify a size that IndigoVideo cannot produce, it will select the closest possible size. To determine what size will result from a given pair of arguments, use the svQuerySize() routine. The code fragment in Example 18-5 finds the closest match to the desired size and resizes the window accordingly.
Example 18-5. Approximating the Requested Video Window Size
SVhandle V; long win; int x, y, new_x, new_y; /* ... */ svQuerySize(V, x, y, &new_x, &new_y); prefsize(new_x, new_y); winconstraints(); svSetSize(V, new_x, new_y); svBindGLWindow(V, win, SV_IN_REPLACE); |
By default, the origin of the live video image is at the upper left corner of the live video input window. You can change this position using the svWindowOffset() routine. To do this, specify vertical and horizontal offsets, in pixels, from the upper left corner of the input window to the upper left corner of the video image. These values may be negative, meaning that you can use a small window to “pan” across the video image. The code fragment in Example 18-6 demonstrates the use of svWindowOffset():
Example 18-6. Specifying a Video Window Offset
SVhandle V; int xoffset, yoffset, win; /* ... */ svWindowOffset(V, xoffset, yoffset); svBindGLWindow(V, win, SV_IN_REPLACE); |
![]() | Note: The live video image cannot be positioned such that any part of the image is off the edge of the screen. Thus, if you have a window in the upper left corner of the screen, negative window offsets will be ignored. The entire video image must remain within the screen dimensions, even if you are viewing only a small portion of the image. |
To prevent other programs from changing video parameters while your program is running, you can request exclusive use of the IndigoVideo board by calling the svUseExclusive() routine with a value of TRUE for the onoff parameter, as demonstrated in Example 18-7.
Example 18-7. Getting Exclusive Use of the IndigoVideo Board
SVhandle V;
int status;
/* ... */
status = svUseExclusive(V, TRUE, SV_INPUT)
if (status == -1) {
svPerror("Couldn't get exclusive use");
/* error handling*/
}
|
While one process has exclusive use of the IndigoVideo board, any other process that makes a call to the IndigoVideo Library will receive an error.
To get out of exclusive use mode, call svUseExclusive() FALSE.
You can combine video and graphics in a window using either of two modes: video underlay or video overlay. In video replace mode, which has been used in this guide until now, all of the pixels in the video image are displayed. In video underlay and overlay modes, video pixels replace only selected graphics pixels. In video underlay mode, the decision whether to display a video pixel or a graphics pixel at a given location is based on the value of the graphics pixel. In video overlay mode, this decision is based on the value of the video pixel—meaning that in video underlay mode you specify which parts of your graphic image should be replaced by video; in video overlay mode you specify which parts of the video image should be replaced by graphics.
Video underlay mode is typically used for such applications as video titling, where you want to display text or graphics superimposed over video. See Figure 15-6 on page 368 in Chapter 15, “VL Blending,” for an illustration of this technique, but use the method described in this section for producing this effect with the IndigoVideo board.
You might use video overlay mode for a “TV weatherman” effect, superimposing a live video image over a computer-generated backdrop. See Figure 15-8 on page 370 in Chapter 15, “VL Blending,”for an illustration of this technique, but use the method described in this section for producing this effect with the IndigoVideo board.
In video underlay mode, video pixels replace graphics pixels that have a value of zero. In IRIS GL programs that use RGB mode, video pixels replace black graphics pixels. In X programs, and in IRIS GL programs that use color map mode, video replaces the color that is mapped to zero. There are advantages and disadvantages to both IRIS GL modes.
RGB mode allows you to take better advantage of the IRIS GL's special 3D effects such as lighting and shading; however, all black pixels are replaced by video, so you can't display black objects over video. Furthermore, in RGB mode, the Indigo workstation simulates 24-bit color using a dithering algorithm, which produces several black pixels in any expanse of a dark color. This means that dark objects appear partially transparent, which is usually the opposite of the desired effect. This effect is particularly noticeable in double-buffered mode. Getting the effect you want in RGB mode can require some experimentation.
A detailed description of IRIS GL lighting and shading routines is beyond the scope of this guide, but here are some hints for producing good-looking graphics in RGB mode:
When drawing Gouraud shaded polygons, use fairly light colors—they appear more opaque than darker colors because light colors do not dither to black.
When drawing lighted objects, use more than one light. With one light, objects are defined by the contrast between light and shadow; however, in video underlay mode, the shadows will look transparent. If you use two lights of contrasting colors on opposite sides of an object, you can define the object using the contrast between the two colors. For example, light a sphere from above using a white light, and fill the shadow by lighting it from below using a blue light. In some cases, using more than one light can affect the graphics performance.
The Graphics Library Programming Guide covers Gouraud shading and IRIS GL lighting in the chapters “Display and Color Modes,” and “Lighting.”
Color map mode provides a certain flexibility in that it allows you to use any color you want without the side effects dithering can produce; however, it is much more difficult to produce lighted or Gouraud shaded polygons in color map mode. For applications that do not require these effects, you're better off using color map mode. The Graphics Library Programming Guide chapters listed in the previous section also describe lighting and Gouraud shading in color map mode.
Example 18-8 demonstrates the use of video underlay mode with IRIS GL color map mode. The program effectively clips the video input to a circle by drawing a circle of color zero on a white background.
Example 18-8. Using IndigoVideo Underlay Mode
#include <gl/gl.h>
#include <svideo.h>
#include <gl/device.h>
void
drawScene(void)
{
color(7);
clear();
color(0);
arcf(320.0, 240.0, 200.0, 1.0, 0.0);
}
main(void)
{
long win, dev;
short val;
SV_nodeP V;
prefsize(640, 480);
win = winopen("Video underlay test");
ortho2(1, 640, 1, 480);
if ((V = svOpenVideo()) == NULL) {
svPerror("open video");
exit(1);
}
if (svBindGLWindow(V, win, SV_IN_UNDER) < 0) {
svPerror("bind window");
svCloseVideo(V);
exit(2);
}
drawScene();
qdevice(ESCKEY);
qdevice(WINQUIT);
qdevice(WINSHUT);
while(1) {
dev = qread(&val);
switch (dev) {
case ESCKEY:
case WINQUIT:
case WINSHUT:
svCloseVideo(V);
exit(0);
break;
case REDRAW:
drawScene();
break;
}
}
}
|
In video overlay mode, video pixel values can be “keyed” out. Video pixels replace graphics pixels, except where the value of a video pixel matches one of the keyed values. This allows you to select certain colors in the video image to be replaced by graphics.
The IndigoVideo board has a 256-entry array of chroma keys, called the chroma key map. This array is indexed by pixel value (the 8-bit RGB pixels are treated as 8-bit unsigned integers), so entry zero in the chroma key map corresponds to pixels of value zero (black pixels). If the value of this entry is 1, black video pixels will be keyed out. If the value is zero, black video pixels will be displayed.
You can load a new chroma key map by using the svLoadMap() routine. The chroma key map is passed to svLoadMap() as a 256-entry array of rgb_tuple structures, which are red, green, blue triplets. The red portion of the array is used for the chroma key map, and the rest of the array is ignored. The code fragment in Example 18-9 keys out black pixels by turning on the chroma key for pixel value zero.
Example 18-9. Using Chroma Keying to Key Out Black Pixels
rgb_tuple chromamap[256]; SVhandle V; /* ... */ chomamap.red[0] = 1; svLoadMap(V, SV_CHROMA_KEY_MAP, chromamap); |
Example 18-10 and Example 18-11 contain listings of two 4Dgifts programs that work together. Example 18-10, voverlay.c , demonstrates how to use IndigoVideo overlay mode. Example 18-11, chromamap.c , demonstrates how to use the chroma key map to set chroma key entries that voverlay.c can use.
Example 18-10. Using IndigoVideo Overlay Mode: voverlay.c
/*
* voverlay.c:
*
* "pool" ball that "bounces" around a 2-d "surface".
* RIGHTMOUSE stops ball
* MIDDLEMOUSE increases y velocity
* LEFTMOUSE increases x velocity
*
* Adapted to show IndigoVideo overlay mode. To use, also compile and run
* the chromamap.c example. As you set chroma key entries with chromamap,
* the graphics generated by this program will begin to appear.
*/
#include <stdio.h>
#include <svideo.h>
#include <gl/gl.h>
#include <gl/device.h>
long xmaxscrn, ymaxscrn; /* maximum size of screen in x and y */
#define XMIN 100
#define YMIN 100
#define XMAX 900
#define YMAX 700
long xvelocity = 0, yvelocity = 0;
main()
{
Device dev;
short val;
long sizex, sizey;
initialize();
while (TRUE) {
while (qtest()) {
dev = qread(&val);
switch (dev) {
case REDRAW: /* redraw window re: move/resize/push/pop */
reshapeviewport();
ortho2(XMIN - 0.5, XMAX + 0.5, YMIN - 0.5, YMAX + 0.5);
drawball();
break;
case LEFTMOUSE: /* increase xvelocity */
if (xvelocity >= 0)
xvelocity++;
else
xvelocity--;
break;
case MIDDLEMOUSE: /* increase yvelocity */
if (yvelocity >= 0)
yvelocity++;
else
yvelocity--;
break;
case RIGHTMOUSE: /* stop ball */
xvelocity = yvelocity = 0;
break;
case ESCKEY:
gexit();
exit(0);
}
}
drawball();
}
}
initialize() {
SVhandle V;
long win;
xmaxscrn = getgdesc(GD_XPMAX)-1;
ymaxscrn = getgdesc(GD_YPMAX)-1;
prefposition(xmaxscrn/4,xmaxscrn*3/4,ymaxscrn/4,ymaxscrn*3/4);
win = winopen("voverlay");
winconstraints();
doublebuffer();
gconfig();
shademodel(FLAT);
ortho2(XMIN - 0.5, XMAX + 0.5, YMIN - 0.5, YMAX + 0.5);
qdevice(ESCKEY);
qdevice(LEFTMOUSE);
qdevice(MIDDLEMOUSE);
qdevice(RIGHTMOUSE);
/* Open video device */
if ((V = svOpenVideo()) == NULL) {
svPerror("open");
exit(1);
}
/* Associate video input with this window */
if (svBindGLWindow(V, win, SV_IN_OVER) < 0) {
svPerror("bindwindow");
exit(1);
}
}
drawball() {
static xpos = 500,ypos = 500;
long radius = 50;
color(BLUE);
clear();
xpos += xvelocity;
ypos += yvelocity;
if (xpos > XMAX - radius ||
xpos < XMIN + radius) {
xpos -= xvelocity;
xvelocity = -xvelocity;
}
if (ypos > YMAX - radius ||
ypos < YMIN + radius) {
ypos -= yvelocity;
yvelocity = -yvelocity;
}
color(YELLOW);
circfi(xpos, ypos, radius);
swapbuffers();
}
|
Example 18-11 contains a listing of chromamap.c, a program that demonstrates how to manipulate the chroma key map for programs that use video overlay.
Example 18-11. Using the Chroma Key Map: chromamap.c
/*
* chromamap.c
*
* This program demonstrates how to manipulate the IndigoVideo
* chroma key map for programs that use the video overlay feature.
* It shows the colors that correspond to the 256 entries in the map,
* where 0 is the lower left corner and 255 is the upper right.
* Clicking the left mouse button over a color toggles the value for that
* entry. An X mark in a box means that color will be keyed out (i.e.,
* the underlying graphics will show through.) A pull-down menu can
* be used to clear or set all of the entries. The program initializes
* all entries to 0 when it starts up.
*
* To demonstrate keying, compile and run the voverlay program in
* this directory. As you set entries in the key map, the graphics
* in voverlay will begin to appear. If you set all of the entries,
* only the graphics will appear. If you clear all of the entries,
* only video will appear.
*/
#include <stdio.h>
#include <gl/gl.h>
#include <gl/device.h>
#include <svideo.h>
static SVhandle V;
static SVcolorMap keymap;
static long rgb8to32[256];
#define grey9() cpack(0x00E0E0E0)
#define grey7() cpack(0x00B0B0B0)
#define grey5() cpack(0x00808080)
#define bordercolor() grey5()
static long xsize, ysize;
static long xorg, yorg;
#define YSIZE 16
#define XSIZE 16
#define BORDERSIZE 0.25
#define MOUSEXMAP(x) ( ((XSIZE+2*BORDERSIZE)*((x)-xorg))/(xsize) )
#define MOUSEYMAP(y) ( ((YSIZE+2*BORDERSIZE)*((y)-yorg))/(ysize) )
static void
drawX(int i, int j)
{
grey7();
move2i(i,j);
draw2i(i+1,j+1);
move2i(i,j+1);
draw2i(i+1,j);
}
static void
drawborder(int i, int j)
{
bordercolor();
move2i(i,j);
draw2i(i+1,j);
draw2i(i+1,j+1);
draw2i(i,j+1);
draw2i(i,j);
}
static void
drawcolor(int i, int j)
{
cpack(rgb8to32[(j*XSIZE)+i]);
rectfi(i,j,i+1,j+1);
}
static void
showmap(void)
{
int i, j;
/* Clear background */
grey9();
clear();
ortho2(-BORDERSIZE, XSIZE+BORDERSIZE, -BORDERSIZE,YSIZE+BORDERSIZE);
/* Draw colored boxes for the 256 RGB colors */
for (j=0; j<YSIZE; j++) {
for (i=0; i<XSIZE; i++) {
drawcolor(i,j);
if (keymap[i+(j*XSIZE)].red) {
drawX(i,j);
}
}
}
/* Draw borders around all the boxes */
bordercolor();
if ((xsize/XSIZE)>4) {
for (j=0; j<=YSIZE; j++) {
move2i(0,j);
draw2i(XSIZE,j);
}
for (j=0; j<=XSIZE; j++) {
move2i(j,0);
draw2i(j,YSIZE);
}
}
}
static void
fillmap(int fill)
{
int i;
for (i = 0; i < SV_CMAP_SIZE; i++)
keymap[i].red = fill;
showmap();
if (svLoadMap(V, SV_CHROMA_KEY_MAP, keymap) < 0)
printf("load map failed\n");
}
main(void)
{
short val;
int menu;
int r, g, b;
float mx, my;
/* Open video device */
if ((V = svOpenVideo()) == NULL) {
svPerror("open");
exit(1);
}
/* Create mapping of 8-bit RGB to 32-bit equivalents */
for (r=0; r<8; r++) {
for (b=0; b<4; b++) {
for (g=0; g<8; g++) {
rgb8to32[(r<<5)|(b<<3)|g] =
((r<<5)|(r<<2)|(r>>1)) |
(((g<<5)|(g<<2)|(g>>1)) << 8 ) |
(((b<<6)|(b<<4)|(b<<2)|b) << 16);
}
}
}
keepaspect(XSIZE, YSIZE);
winopen("chromamap");
RGBmode();
gconfig();
qdevice(LEFTMOUSE);
qdevice(MOUSEX);
qdevice(MOUSEY);
qdevice(MENUBUTTON);
menu = defpup("chromamap %t|clear all|set all|exit");
getsize(&xsize,&ysize);
getorigin(&xorg,&yorg);
/* Put map in known state */
fillmap(0);
while (1) {
switch(qread(&val)) {
case REDRAW:
reshapeviewport();
getsize(&xsize,&ysize);
getorigin(&xorg,&yorg);
showmap();
break;
case MOUSEX:
mx = MOUSEXMAP(val) - .25;
if (mx < 0.0)
mx = 0.0;
else if (mx >= XSIZE)
mx = XSIZE-1;
break;
case MOUSEY:
my = MOUSEYMAP(val) - .25;
if (my < 0.0)
my = 0.0;
else if (my >= YSIZE)
my = YSIZE-1;
break;
case LEFTMOUSE:
/* Toggle the entry's key */
if (val) {
int i = (int)mx + (int)my * XSIZE;
keymap[i].red = !keymap[i].red;
drawcolor((int)mx, (int)my);
if (keymap[i].red) {
drawX((int)mx, (int)my);
}
drawborder((int)mx, (int)my);
if (svLoadMap(V, SV_CHROMA_KEY_MAP, keymap) < 0)
printf("load map failed\n");
}
break;
case MENUBUTTON:
if (val) {
switch (dopup(menu)) {
case 1:
fillmap(0);
break;
case 2:
fillmap(1);
break;
case 3:
exit(0);
}
}
break;
}
}
}
|