1 /* xscreensaver, Copyright (c) 1997, 1998 Jamie Zawinski <jwz@jwz.org>
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"
38 #ifdef HAVE_SGI_VIDEO /* whole file */
42 #include <X11/Xutil.h>
45 extern char *progname;
49 # include <dmedia/vl.h>
51 static Bool dark_image_p(unsigned long *image, int width, int height);
52 static Bool install_video_frame(unsigned long *image, int width, int height,
53 Screen *screen, Visual *visual, Drawable dest);
57 describe_input(const char *prefix, VLServer server, int camera)
64 printf("%s: %s VL_ANY\n", progname, prefix);
68 vlGetDeviceList(server, &dl);
69 for (i = 0; i < dl.numDevices; i++)
71 VLDevice *d = &dl.devices[i];
72 for (j = 0; j < d->numNodes; j++)
73 if (d->nodes[j].number == camera)
75 printf("%s: %s %d, \"%s\"\n", progname, prefix,
83 printf("%s: %s %d (???)\n", progname, prefix, camera);
89 grab_frame_1(Screen *screen, Visual *visual, Drawable dest, int camera)
101 VLTransferDescriptor trans;
102 unsigned long *image = 0;
104 server = vlOpenVideo (NULL);
108 fprintf (stderr, "%s: unable to open video server\n", progname);
114 describe_input("trying device", server, camera);
117 input = vlGetNode (server, VL_SRC, VL_VIDEO, camera);
118 output = vlGetNode (server, VL_DRN, VL_MEM, VL_ANY);
120 if (input == -1 || output == -1)
123 fprintf (stderr, "%s: unable to get video I/O nodes: %d\n",
129 path = vlCreatePath (server, VL_ANY, input, output);
133 fprintf (stderr, "%s: unable to get video path: %d\n",
139 if (vlSetupPaths (server, (VLPathList) &path, 1, VL_SHARE, VL_SHARE) == -1)
142 fprintf (stderr, "%s: unable to set up video path: %d\n",
148 ctl.intVal = VL_CAPTURE_INTERLEAVED;
149 if (vlSetControl (server, path, output, VL_CAP_TYPE, &ctl) == -1)
153 "%s: unable to set video capture type to interleaved: %d\n",
159 ctl.intVal = VL_PACKING_RGBA_8;
160 if (vlSetControl (server, path, output, VL_PACKING, &ctl) == -1)
163 fprintf (stderr, "%s: unable to set video packing to RGB8: %d\n",
169 buffer = vlCreateBuffer (server, path, output, 3);
173 fprintf (stderr, "%s: unable to create video buffer: %d\n",
179 vlRegisterBuffer (server, path, output, buffer);
181 memset(&trans, 0, sizeof(trans));
182 trans.trigger = VLTriggerImmediate;
183 trans.mode = VL_TRANSFER_MODE_DISCRETE;
186 if (vlBeginTransfer (server, path, 1, &trans) == -1)
189 fprintf (stderr, "%s: unable to begin video transfer: %d\n",
196 /* try to get a frame; don't try more than a zillion times.
197 I strongly suspect this isn't the best way to do this...
201 for (i = 0; i < 1000; i++)
203 info = vlGetLatestValid (server, buffer);
205 usleep(10000); /* 1/100th second (a bit more than half a field) */
212 fprintf (stderr, "%s: unable to get video info: %d\n",
218 image = vlGetActiveRegion (server, buffer, info);
222 fprintf (stderr, "%s: unable to grab video frame: %d\n",
228 if (vlGetControl (server, path, input, VL_SIZE, &ctl) != -1)
231 height = ctl.xyVal.y;
236 fprintf (stderr, "%s: unable to get video image size: %d\n",
243 describe_input("read device", server, camera);
246 if (dark_image_p(image, width, height))
249 status = install_video_frame(image, width, height, screen, visual, dest);
253 /* I think `image' is freed as a result of destroying buffer. */
256 vlDestroyBuffer(server, buffer);
258 vlDestroyPath(server, path);
260 vlCloseVideo (server);
267 dark_image_p(unsigned long *image, int width, int height)
272 int pixels = (width * height);
274 int mr = 0, mg = 0, mb = 0;
276 for (i = pixels-1; i >= 0; i--)
278 unsigned long pixel = image[i];
279 unsigned int r = (pixel & 0x0000FF);
280 unsigned int g = (pixel & 0x00FF00) >> 8;
281 unsigned int b = (pixel & 0xFF0000) >> 16;
287 total += ((r * (0.3086 / 0xFF)) + /* gamma 1.0 intensity values */
288 (g * (0.6094 / 0xFF)) +
289 (b * (0.0820 / 0xFF)));
293 fprintf(stderr, "%s: %sdark %f (max rgb: %d %d %d)\n", progname,
294 (total < max ? "" : "not "),
297 return (total < max);
302 grab_video_frame(Screen *screen, Visual *visual, Drawable dest)
304 char *def_camera = get_string_resource("videoDevice", "Integer");
305 if (def_camera && *def_camera)
309 int ok = (1 == sscanf(def_camera, " %d %c", &cam, &c));
311 if (ok && grab_frame_1(screen, visual, dest, cam))
315 if (grab_frame_1(screen, visual, dest, VL_ANY))
320 VLServer server = vlOpenVideo (NULL);
322 if (!server) return False;
324 for (i = 0; i < 5; i++) /* if we get all black images, retry up to
330 j = vlGetDeviceList(server, &dl);
331 vlCloseVideo(server);
332 if (j < 0) return False;
334 for (j = 0; j < dl.numDevices; j++)
336 VLDevice *d = &dl.devices[j];
338 for (k = 0; k < d->numNodes; k++)
339 if (d->nodes[k].type == VL_SRC &&
340 d->nodes[k].kind == VL_VIDEO)
341 if (grab_frame_1(screen, visual, dest, d->nodes[k].number))
343 /* nothing yet? go around and try again... */
348 fprintf (stderr, "%s: images on all video feeds are too dark.\n",
356 install_video_frame(unsigned long *image, int width, int height,
357 Screen *screen, Visual *visual, Drawable dest)
359 Display *dpy = DisplayOfScreen(screen);
361 unsigned int w, h, b, d;
367 Bool free_data = False;
368 int vblank_kludge = 3; /* lose the closed-captioning blips... */
371 char c, *G = get_string_resource("videoGain", "Float");
372 if (!G || (1 != sscanf (G, " %lf %c", &gain, &c)))
373 /* default to the usual NTSC gamma value. Is this the right thing to do?
374 (Yeah, "gain" isn't quite "gamma", but it's close enough...) */
378 XGetGeometry(dpy, dest, &root, &x, &y, &w, &h, &b, &d);
380 gcv.function = GXcopy;
381 gcv.foreground = BlackPixelOfScreen(screen);
382 gc = XCreateGC (dpy, dest, GCFunction|GCForeground, &gcv);
384 image_depth = visual_depth(screen, visual);
385 if (image_depth < 24)
386 image_depth = 24; /* We'll dither */
388 ximage = XCreateImage (dpy, visual, image_depth, ZPixmap, 0, (char *) image,
389 width, height, 8, 0);
394 if (gain > 0.0) /* Pump up the volume */
396 unsigned char *end = (unsigned char *) (image + (width * height));
397 unsigned char *s = (unsigned char *) image;
400 unsigned int r = s[1] * gain;
401 unsigned int g = s[2] * gain;
402 unsigned int b = s[3] * gain;
403 s[1] = (r > 255 ? 255 : r);
404 s[2] = (g > 255 ? 255 : g);
405 s[3] = (b > 255 ? 255 : b);
410 /* If the drawable is not of truecolor depth, we need to convert the
411 grabbed bits to match the depth by clipping off the less significant
412 bit-planes of each color component.
414 if (d != 24 && d != 32)
417 /* We use the same ximage->data in both images -- that's ok, because
418 since we're reading from B and writing to A, and B uses more bytes
419 per pixel than A, the write pointer won't overrun the read pointer.
421 XImage *ximage2 = XCreateImage (dpy, visual, d, ZPixmap, 0,
423 width, height, 8, 0);
427 XDestroyImage(ximage);
432 fprintf(stderr, "%s: converting from depth %d to depth %d\n",
433 progname, ximage->depth, ximage2->depth);
436 for (y = 0; y < ximage->height; y++)
437 for (x = 0; x < ximage->width; x++)
439 unsigned long pixel = XGetPixel(ximage, x, y);
440 unsigned int r = (pixel & 0x0000FF);
441 unsigned int g = (pixel & 0x00FF00) >> 8;
442 unsigned int b = (pixel & 0xFF0000) >> 16;
445 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
447 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
449 pixel = ((r >> 3) | ((g >> 3) << 5) | ((b >> 3) << 10));
453 XPutPixel(ximage2, x, y, pixel);
456 XDestroyImage(ximage);
460 if (width < w && height < h) /* Stretch the image to fit the window. */
462 double dw = (((double) w) / ((double) width));
463 double dh = (((double) h) / ((double) height));
464 double d = (dw > dh ? dh : dw);
465 int width2 = d * width;
466 int height2 = d * height;
468 XImage *ximage2 = XCreateImage (dpy, visual, ximage->depth, ZPixmap,
470 width2, height2, 8, 0);
472 ximage2->data = (char *) malloc(width2 * height2 * 4);
476 fprintf(stderr, "%s: stretching video image by %f (%d %d -> %d %d)\n",
477 progname, d, width, height, width2, height2);
479 for (y = 0; y < height2; y++)
481 int y2 = (int) (y / d);
482 for (x = 0; x < width2; x++)
483 XPutPixel(ximage2, x, y, XGetPixel(ximage, (int) (x / d), y2));
486 XDestroyImage(ximage);
493 XFillRectangle(dpy, dest, gc, 0, 0, w, h);
494 XPutImage(dpy, dest, gc, ximage, 0, vblank_kludge,
497 width, height - vblank_kludge);
503 XDestroyImage(ximage);
508 #endif /* HAVE_SGI_VIDEO */