1 /* xscreensaver, Copyright (c) 1997 Jamie Zawinski <jwz@netscape.com>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
12 /* This file contains code for grabbing a frame from one of the video inputs
13 on an SGI. It returns it on a Drawable where it can be hacked at will.
14 This code checks all available video devices for the first one with a
17 It checks the deviced whose number comes from the `videoDevice' resource
18 first, then the default video device, then all the devices in order.
20 The intensity of the video signal is increased by the value of the
21 `videoGain' resource (a float) defaulting to 2.2, since NTSC video tends
22 to appear kind of dim on computer screens.
24 The video image is expanded to fit the window (while preserving the aspect
25 ratio.) This is done by simply replicating pixels, not dithering. That
26 turns out to look good enough most of the time.
28 If the target window is not TrueColor, the grabbed image will be quantized
29 to fit. This also is done without dithering, but in this case, dithering
30 would help a lot, because it looks like crap. So use TrueColor if you care.
35 #include "resources.h"
37 #ifdef HAVE_SGI_VIDEO /* whole file */
41 #include <X11/Xutil.h>
44 extern char *progname;
48 # include <dmedia/vl.h>
50 static Bool dark_image_p(unsigned long *image, int width, int height);
51 static Bool install_video_frame(unsigned long *image, int width, int height,
52 Screen *screen, Visual *visual, Drawable dest);
55 grab_frame_1(Screen *screen, Visual *visual, Drawable dest, int camera)
67 VLTransferDescriptor trans;
68 unsigned long *image = 0;
70 server = vlOpenVideo (NULL);
74 fprintf (stderr, "%s: unable to open video server\n", progname);
79 input = vlGetNode (server, VL_SRC, VL_VIDEO, camera);
80 output = vlGetNode (server, VL_DRN, VL_MEM, VL_ANY);
82 if (input == -1 || output == -1)
85 fprintf (stderr, "%s: unable to get video I/O nodes: %d\n",
91 path = vlCreatePath (server, VL_ANY, input, output);
95 fprintf (stderr, "%s: unable to get video path: %d\n",
101 if (vlSetupPaths (server, (VLPathList) &path, 1, VL_SHARE, VL_SHARE) == -1)
104 fprintf (stderr, "%s: unable to set up video path: %d\n",
110 ctl.intVal = VL_CAPTURE_INTERLEAVED;
111 if (vlSetControl (server, path, output, VL_CAP_TYPE, &ctl) == -1)
115 "%s: unable to set video capture type to interleaved: %d\n",
121 ctl.intVal = VL_PACKING_RGBA_8;
122 if (vlSetControl (server, path, output, VL_PACKING, &ctl) == -1)
125 fprintf (stderr, "%s: unable to set video packing to RGB8: %d\n",
131 buffer = vlCreateBuffer (server, path, output, 3);
135 fprintf (stderr, "%s: unable to create video buffer: %d\n",
141 vlRegisterBuffer (server, path, output, buffer);
143 memset(&trans, 0, sizeof(trans));
144 trans.trigger = VLTriggerImmediate;
145 trans.mode = VL_TRANSFER_MODE_DISCRETE;
148 if (vlBeginTransfer (server, path, 1, &trans) == -1)
151 fprintf (stderr, "%s: unable to begin video transfer: %d\n",
158 /* try to get a frame; don't try more than a zillion times.
159 I strongly suspect this isn't the best way to do this...
163 for (i = 0; i < 1000; i++)
165 info = vlGetLatestValid (server, buffer);
167 usleep(10000); /* 1/100th second (a bit more than half a field) */
174 fprintf (stderr, "%s: unable to get video info: %d\n",
180 image = vlGetActiveRegion (server, buffer, info);
184 fprintf (stderr, "%s: unable to grab video frame: %d\n",
190 if (vlGetControl (server, path, input, VL_SIZE, &ctl) != -1)
193 height = ctl.xyVal.y;
198 fprintf (stderr, "%s: unable to get video image size: %d\n",
204 if (dark_image_p(image, width, height))
207 status = install_video_frame(image, width, height, screen, visual, dest);
211 /* I think `image' is freed as a result of destroying buffer. */
214 vlDestroyBuffer(server, buffer);
216 vlDestroyPath(server, path);
218 vlCloseVideo (server);
225 dark_image_p(unsigned long *image, int width, int height)
230 int pixels = (width * height);
232 int mr = 0, mg = 0, mb = 0;
234 for (i = pixels-1; i >= 0; i--)
236 unsigned long pixel = image[i];
237 unsigned int r = (pixel & 0x0000FF);
238 unsigned int g = (pixel & 0x00FF00) >> 8;
239 unsigned int b = (pixel & 0xFF0000) >> 16;
245 total += ((r * (0.3086 / 0xFF)) + /* gamma 1.0 intensity values */
246 (g * (0.6094 / 0xFF)) +
247 (b * (0.0820 / 0xFF)));
251 fprintf(stderr, "%s: %sdark %f (max rgb: %d %d %d)\n", progname,
252 (total < max ? "" : "not "),
255 return (total < max);
260 grab_video_frame(Screen *screen, Visual *visual, Drawable dest)
262 char *def_camera = get_string_resource("videoDevice", "Integer");
263 if (def_camera && *def_camera)
267 int ok = (1 == sscanf(def_camera, " %d %c", &cam, &c));
269 if (ok && grab_frame_1(screen, visual, dest, cam))
273 if (grab_frame_1(screen, visual, dest, VL_ANY))
278 for (i = 0; i < 5; i++) /* if we get all black images, retry up to
281 VLServer server = vlOpenVideo (NULL);
284 vlGetDeviceList(server, &dl);
285 vlCloseVideo (server);
286 for (j = 0; j < dl.numDevices; j++)
288 VLDevice *d = &dl.devices[j];
290 for (k = 0; k < d->numNodes; k++)
291 if (d->nodes[k].type == VL_SRC &&
292 d->nodes[k].kind == VL_VIDEO)
293 if (grab_frame_1(screen, visual, dest, d->nodes[k].number))
296 /* nothing yet? go around and try again... */
300 fprintf (stderr, "%s: images on all video feeds are too dark.\n",
308 install_video_frame(unsigned long *image, int width, int height,
309 Screen *screen, Visual *visual, Drawable dest)
311 Display *dpy = DisplayOfScreen(screen);
313 unsigned int w, h, b, d;
318 Bool free_data = False;
319 int vblank_kludge = 3; /* lose the closed-captioning blips... */
322 char c, *G = get_string_resource("videoGain", "Float");
323 if (!G || (1 != sscanf (G, " %lf %c", &gain, &c)))
324 /* default to the usual NTSC gamma value. Is this the right thing to do?
325 (Yeah, "gain" isn't quite "gamma", but it's close enough...) */
329 XGetGeometry(dpy, dest, &root, &x, &y, &w, &h, &b, &d);
331 gcv.function = GXcopy;
332 gcv.foreground = BlackPixelOfScreen(screen);
333 gc = XCreateGC (dpy, dest, GCFunction|GCForeground, &gcv);
335 ximage = XCreateImage (dpy, visual, 32, ZPixmap, 0, (char *) image,
336 width, height, 8, 0);
341 if (ximage->depth == 32 && d == 24)
345 if (gain > 0.0) /* Pump up the volume */
347 unsigned char *end = (unsigned char *) (image + (width * height));
348 unsigned char *s = (unsigned char *) image;
351 unsigned int r = s[1] * gain;
352 unsigned int g = s[2] * gain;
353 unsigned int b = s[3] * gain;
354 s[1] = (r > 255 ? 255 : r);
355 s[2] = (g > 255 ? 255 : g);
356 s[3] = (b > 255 ? 255 : b);
361 /* If the drawable is not of truecolor depth, we need to convert the
362 grabbed bits to match the depth by clipping off the less significant
363 bit-planes of each color component.
365 if (d != 24 && d != 32)
368 /* We use the same ximage->data in both images -- that's ok, because
369 since we're reading from B and writing to A, and B uses more bytes
370 per pixel than A, the write pointer won't overrun the read pointer.
372 XImage *ximage2 = XCreateImage (dpy, visual, d, ZPixmap, 0,
374 width, height, 8, 0);
378 XDestroyImage(ximage);
383 fprintf(stderr, "%s: converting from depth %d to depth %d\n",
384 progname, ximage->depth, ximage2->depth);
387 for (y = 0; y < ximage->height; y++)
388 for (x = 0; x < ximage->width; x++)
390 unsigned long pixel = XGetPixel(ximage, x, y);
391 unsigned int r = (pixel & 0x0000FF);
392 unsigned int g = (pixel & 0x00FF00) >> 8;
393 unsigned int b = (pixel & 0xFF0000) >> 16;
396 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
398 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
400 pixel = ((r >> 3) | ((g >> 3) << 5) | ((b >> 3) << 10));
404 XPutPixel(ximage2, x, y, pixel);
407 XDestroyImage(ximage);
411 if (width < w && height < h) /* Stretch the image to fit the window. */
413 double dw = (((double) w) / ((double) width));
414 double dh = (((double) h) / ((double) height));
415 double d = (dw > dh ? dh : dw);
416 int width2 = d * width;
417 int height2 = d * height;
419 XImage *ximage2 = XCreateImage (dpy, visual, ximage->depth, ZPixmap,
421 width2, height2, 8, 0);
423 ximage2->data = (char *) malloc(width2 * height2 * 4);
427 fprintf(stderr, "%s: stretching video image by %f (%d %d -> %d %d)\n",
428 progname, d, width, height, width2, height2);
430 for (y = 0; y < height2; y++)
432 int y2 = (int) (y / d);
433 for (x = 0; x < width2; x++)
434 XPutPixel(ximage2, x, y, XGetPixel(ximage, (int) (x / d), y2));
437 XDestroyImage(ximage);
444 XFillRectangle(dpy, dest, gc, 0, 0, w, h);
445 XPutImage(dpy, dest, gc, ximage, 0, vblank_kludge,
448 width, height - vblank_kludge);
454 XDestroyImage(ximage);
459 #endif /* HAVE_SGI_VIDEO */