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);
321 for (i = 0; i < 5; i++) /* if we get all black images, retry up to
326 vlGetDeviceList(server, &dl);
327 vlCloseVideo(server);
328 for (j = 0; j < dl.numDevices; j++)
330 VLDevice *d = &dl.devices[j];
332 for (k = 0; k < d->numNodes; k++)
333 if (d->nodes[k].type == VL_SRC &&
334 d->nodes[k].kind == VL_VIDEO)
335 if (grab_frame_1(screen, visual, dest, d->nodes[k].number))
337 /* nothing yet? go around and try again... */
342 fprintf (stderr, "%s: images on all video feeds are too dark.\n",
350 install_video_frame(unsigned long *image, int width, int height,
351 Screen *screen, Visual *visual, Drawable dest)
353 Display *dpy = DisplayOfScreen(screen);
355 unsigned int w, h, b, d;
361 Bool free_data = False;
362 int vblank_kludge = 3; /* lose the closed-captioning blips... */
365 char c, *G = get_string_resource("videoGain", "Float");
366 if (!G || (1 != sscanf (G, " %lf %c", &gain, &c)))
367 /* default to the usual NTSC gamma value. Is this the right thing to do?
368 (Yeah, "gain" isn't quite "gamma", but it's close enough...) */
372 XGetGeometry(dpy, dest, &root, &x, &y, &w, &h, &b, &d);
374 gcv.function = GXcopy;
375 gcv.foreground = BlackPixelOfScreen(screen);
376 gc = XCreateGC (dpy, dest, GCFunction|GCForeground, &gcv);
378 image_depth = visual_depth(screen, visual);
379 if (image_depth < 24)
380 image_depth = 24; /* We'll dither */
382 ximage = XCreateImage (dpy, visual, image_depth, ZPixmap, 0, (char *) image,
383 width, height, 8, 0);
388 if (gain > 0.0) /* Pump up the volume */
390 unsigned char *end = (unsigned char *) (image + (width * height));
391 unsigned char *s = (unsigned char *) image;
394 unsigned int r = s[1] * gain;
395 unsigned int g = s[2] * gain;
396 unsigned int b = s[3] * gain;
397 s[1] = (r > 255 ? 255 : r);
398 s[2] = (g > 255 ? 255 : g);
399 s[3] = (b > 255 ? 255 : b);
404 /* If the drawable is not of truecolor depth, we need to convert the
405 grabbed bits to match the depth by clipping off the less significant
406 bit-planes of each color component.
408 if (d != 24 && d != 32)
411 /* We use the same ximage->data in both images -- that's ok, because
412 since we're reading from B and writing to A, and B uses more bytes
413 per pixel than A, the write pointer won't overrun the read pointer.
415 XImage *ximage2 = XCreateImage (dpy, visual, d, ZPixmap, 0,
417 width, height, 8, 0);
421 XDestroyImage(ximage);
426 fprintf(stderr, "%s: converting from depth %d to depth %d\n",
427 progname, ximage->depth, ximage2->depth);
430 for (y = 0; y < ximage->height; y++)
431 for (x = 0; x < ximage->width; x++)
433 unsigned long pixel = XGetPixel(ximage, x, y);
434 unsigned int r = (pixel & 0x0000FF);
435 unsigned int g = (pixel & 0x00FF00) >> 8;
436 unsigned int b = (pixel & 0xFF0000) >> 16;
439 pixel = ((r >> 5) | ((g >> 5) << 3) | ((b >> 6) << 6));
441 pixel = ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8));
443 pixel = ((r >> 3) | ((g >> 3) << 5) | ((b >> 3) << 10));
447 XPutPixel(ximage2, x, y, pixel);
450 XDestroyImage(ximage);
454 if (width < w && height < h) /* Stretch the image to fit the window. */
456 double dw = (((double) w) / ((double) width));
457 double dh = (((double) h) / ((double) height));
458 double d = (dw > dh ? dh : dw);
459 int width2 = d * width;
460 int height2 = d * height;
462 XImage *ximage2 = XCreateImage (dpy, visual, ximage->depth, ZPixmap,
464 width2, height2, 8, 0);
466 ximage2->data = (char *) malloc(width2 * height2 * 4);
470 fprintf(stderr, "%s: stretching video image by %f (%d %d -> %d %d)\n",
471 progname, d, width, height, width2, height2);
473 for (y = 0; y < height2; y++)
475 int y2 = (int) (y / d);
476 for (x = 0; x < width2; x++)
477 XPutPixel(ximage2, x, y, XGetPixel(ximage, (int) (x / d), y2));
480 XDestroyImage(ximage);
487 XFillRectangle(dpy, dest, gc, 0, 0, w, h);
488 XPutImage(dpy, dest, gc, ximage, 0, vblank_kludge,
491 width, height - vblank_kludge);
497 XDestroyImage(ximage);
502 #endif /* HAVE_SGI_VIDEO */