summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjaromil <jaromil@383723c8-4afa-0310-b8a8-b1afb83214fc>2001-07-16 19:53:08 (GMT)
committer jaromil <jaromil@383723c8-4afa-0310-b8a8-b1afb83214fc>2001-07-16 19:53:08 (GMT)
commitdab00ffba3e0032ea1db4df7683cc4160da7befa (patch)
tree94e5d2f353398f2f3442e24f4fd99b99d665aa09
parent83cbda8e4d0b812d7977b2ff13378b4b0f007100 (diff)
Initial revision
git-svn-id: svn://dyne.org/rastasoft/hasciicam@499 383723c8-4afa-0310-b8a8-b1afb83214fc
-rw-r--r--Hasciicam 0.9/hasciicam.1121
-rw-r--r--Hasciicam 0.9/hasciicam.c1015
2 files changed, 1136 insertions, 0 deletions
diff --git a/Hasciicam 0.9/hasciicam.1 b/Hasciicam 0.9/hasciicam.1
new file mode 100644
index 0000000..e0f6860
--- /dev/null
+++ b/Hasciicam 0.9/hasciicam.1
@@ -0,0 +1,121 @@
+.TH hasciicam 1 "July 16, 2001" "hasciicam"
+.SH NAME
+hasciicam \- (h)ascii for the masses!
+.SH SYNOPSIS
+.B hasciicam
+[ \fB-hvq\fR ]
+[ \fB-m \fImode\fR ]
+[ \fB-d \fIdevice\fR ]
+[ \fB-i \fIinput\fR ]
+[ \fB-n \fInorm\fR ]
+[ \fB-s \fIsize\fR ]
+[ \fB-o \fIoutput\fR ]
+[ \fB-f \fIuser@ftp.foo.org:/dir\fR ]
+[ \fBrendering\-options\fR ]
+.SH DESCRIPTION
+.B hasciicam
+makes it possible to have live asciivideo on the web. It takes video from a capture device (like tv cards or quickcams) and renders it into ascii, formatting the output into an \fBhtml page\fP with a refresh tag or in a \fBlive ascii\fP window or in a simple \fBtext file\fP as well, giving the possiblity to publish on the web a live asciivideo feed browsable without any need for browser's plugins.
+
+.B hasciicam
+grabs frames using \fBVideo4Linux\fP api, renders them into ascii using \fBAA-lib\fP engine and is capable of armoring them with auto-refreshing
+.SM HTML
+code. It's being used also \fBjpeglib\fP for optionally refresh a small jpeg image, so that it can work also as a simple webcam (or showing both, jpeg and asciivideo).
+.SH OPTIONS
+.B
+.IP "-h --help"
+Display a help text and quit.
+.B
+.IP "-v --version"
+Display version and quit.
+.B
+.IP "-q --quiet"
+Be quiet and don't show hardware detection.
+.B
+.IP "-m --mode"
+Selects the mode in wich hasciicam will run:
+.br
+.B live\fP will use the console (or eventually open one under X display) to show full speed ascii video.
+.br
+.B html\fP will write html formatted ascii video every \fIrefresh\fP seconds into file \fIaafile\fP.
+.br
+.B text\fP will write plain text ascii video every \fIrefresh\fP seconds into file \fIaafile\fP.
+.br
+Default mode is \fBlive\fP. Mode selection is XOR'ed.
+.B
+.IP "-d --device"
+Use specified capture device. Default is \fB/dev/video\fP.
+.B
+.IP "-i --input"
+Selects the \fBdevice\fP's input channel to be used for grabbing frames. Default is \fB1\fP.
+.B
+.IP "-n --norm"
+Selects video format to be used on tuner: \fBpal\fP | \fBntsc\fP | \fBsecam\fP | \fBauto\fP. Default is \fBauto\fP.
+.B
+.IP "-s --size"
+Sets rendered ascii frames size to \fBWxH\fP characters. Grabbed frame size is computed from this value, resulting into (W<<1)x(H<<1). Default is \fB96x72\fP, or the minimum grabbing size supported by the device if greater.
+.B
+.IP "-o --aafile"
+Defines the file where to save rendered ascii, overwrited every \fIrefresh\fP seconds. Default is \fBhasciicam.html\fP when in \fBhtml\fP mode, \fBhasciicam.txt\fP when in \fBtext\fP mode (unuseful when in \fBlive\fP mode).
+.B
+.IP "-f --ftp"
+Ftp pushes the selected output file to an ftp account specified within an expression like \fBuser@dyne.org:/home/user/www\fP, hasciicam asks then for the account password on stdin (hidden while typing). Remote file is refreshed depending on \fBrefresh\fP rate and connection bandwidth, a \fIscolopendro\fP temporary file is created to keep clients on the other side refreshing smooth.
+.B
+.IP "-D --daemon"
+Forks to background and runs in daemon mode.
+
+.SH RENDERING-OPTIONS
+.B
+.IP "-S --font-size"
+Selects a font size from \fB1\fP to \fB4\fP used in html rendering. default is \fB1\fP (only useful when in \fBhtml\fP mode).
+.B
+.IP "-a --font-face"
+Selects a font face to be used in html rendering, it should be fixed size for better results. default is \fBcourier\fP (only useful when in \fBhtml\fP mode).
+.B
+.IP "-r --refresh"
+Specifies the refresh interval in seconds between each grabbed frame. Default is \fB2\fP (unuseful when in \fBlive\fP mode).
+.B
+.IP "-b --aabright"
+Specifies brightness level for the aa_render, from 0 to 100. Default is \fB50\fP.
+.B
+.IP "-c --aacontrast"
+Specifies contrast level for the aa_render, from 0 to 100. Default is \fB10\fP.
+.B
+.IP "-g --aagamma"
+Specifies gamma correction level for the aa_render, from 0 to 100. Default is \fB10\fP.
+.B
+.IP "-I --invert"
+Invert and render the resulting negative ascii.
+.B
+.IP "-B --background"
+Specifies the background color to be used in the form of a \fIhex RGB triplet\fR (without the leading #). Default is \fB000000\fP for black (only useful when in \fBhtml\fP mode).
+.B
+.IP "-F --foreground"
+Specifies the foreground color to be used in the form of a \fIhex RGB triplet\fR (without the leading #). Default is \fB00FF00\fP for green (only useful when in \fBhtml\fP mode).
+.B
+.IP "-O --jpgfile"
+Specifies the name of a jpeg file to be dumped at the same time of the ascii file, as a jpeg rendering of the last grabbed frame. By default this option is deactivated (unuseful when in \fBlive\fP mode).
+.B
+.IP "-Q --jpgqual"
+Specifies the quality of the jpeg rendering.
+.SH EXAMPLES
+.B hasciicam -m html -o watchme.html
+.br
+puts your \fIhtml\fP formatted ascii into watchme.html
+.br
+.B hasciicam -m text -s79x24 -o ~/.plan
+.br
+puts your ascii into your local .plan (fingercam)
+.SH NOTES
+When using a usb webcam, a supported size needs to be specified. The minimum or maximum detected size should work, also a size of 160x120 mostly gives good results, with unsupported sizes you will get unexpected results.
+.SH BUGS
+Report bugs to hasciicam@dyne.org. Please include a complete, self-contained example that will allow the bug to be reproduced, informations about which version of hasciicam you are using, which operative system and the complete device detection output.
+.SH AUTHOR
+jaromil [aka Denis Roio] <\fIjaromil@dyne.org\fR>
+.SH COPYING
+Copyright (c) 2001 jaromil@dyne.org
+.br
+Permission is granted to copy, distribute and/or modify this manual under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with the Invariant Sections being NAME, SYNOPSIS, DESCRIPTION, AUTHOR and with the Front-Cover Texts including "hasciicam - (h)ascii for the masses! - manual pages". Permission is granted to make and distribute verbatim copies of this manual page provided the above copyright notice and this permission notice are preserved on all copies.
+.SH AVAILABILITY
+The most recent version of hasciicam sourcecode is always available for download from \fIhttp://ascii.dyne.org\fR.
+.SH SEE ALSO
+aalib
diff --git a/Hasciicam 0.9/hasciicam.c b/Hasciicam 0.9/hasciicam.c
new file mode 100644
index 0000000..f959a41
--- /dev/null
+++ b/Hasciicam 0.9/hasciicam.c
@@ -0,0 +1,1015 @@
+/* HasciiCam 0.9
+ * (c) 2000-2001 Denis Roio aka jaromil
+ *
+ * This source code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This source code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * Please refer to the GNU Public License for more details.
+ *
+ * You should have received a copy of the GNU Public License along with
+ * this source code; if not, write to:
+ * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * CONTRIBUTORS :
+ * Diego Torres aka rapid <rapid@ivworlds.org>
+ * jpgdump, security issues, 24rgb->greyscale conversion, bugfixes
+ * Alessandro Preite Martinez <vsg@arcetri.astro.it>
+ * SGI IRIX support, image resampling routines
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <signal.h>
+#include "config.h"
+
+#ifdef HAVE_LINUX
+#include <linux/types.h>
+#include <linux/videodev.h>
+#endif
+
+#ifdef HAVE_SGI
+#include <dmedia/vl.h>
+#endif
+
+#include <jpeglib.h>
+#include <aalib.h>
+
+#include "ftp.h"
+
+#define TEMPFILE "/tmp/hasciitmp.jpg"
+
+/* mmapped buffer */
+unsigned char *image = NULL;
+
+/* declare the sighandler */
+void quitproc (int Sig);
+
+/* line command stuff */
+
+char *version =
+ "%s %s - (h)ascii 4 the masses!\n"
+ "(c)2000-2001 denis roio aka jaromil <jaromil@dyne.org>\n"
+ "[ http://ascii.dyne.org ]\n\n";
+
+char *help =
+/* "\x1B" "c" <--- SCREEN CLEANING ESCAPE CODE
+ why here? just a reminder */
+"Usage: hasciicam [options]\n"
+"options:\n"
+"-h --help this help\n"
+"-v --version version information\n"
+"-q --quiet be quiet\n"
+"-m --mode mode: live|html|text - default live\n"
+"-d --device video grabbing device - default /dev/video\n"
+"-i --input input channel number - default 1\n"
+"-n --norm norm: pal|ntsc|secam|auto - default auto\n"
+"-s --size ascii image size WxH - default 96x72\n"
+"-o --aafile dumped file - default hasciicam.[txt|html]\n"
+"-f --ftp ie: user@ftp.host.it:/dir - default none\n"
+"-D --daemon run in background - default foregrond\n"
+"-U --uid setuid (int) - default current\n"
+"-G --gid setgid (int) - default current\n"
+"rendering options:\n"
+"-S --font-size html font size (1-4) - default 1\n"
+"-a --font-face html font to use - default courier\n"
+"-r --refresh refresh delay - default 2\n"
+"-b --aabright ascii brightness - default 50\n"
+"-c --aacontrast ascii contrast - default 10\n"
+"-g --aagamma ascii gamma - default 10\n"
+"-I --invert invert colors - default off\n"
+"-B --background background color (hex) - default 000000\n"
+"-F --foreground foreground color (hex) - default 00FF00\n"
+"-O --jpgfile jpeg file - default none\n"
+"-Q --jpgqual jpeg rendering quality - default 65\n"
+"\n";
+
+#ifndef sgi
+static const struct option long_options[] = {
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'v'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"mode", required_argument, NULL, 'm'},
+ {"device", required_argument, NULL, 'd'},
+ {"input", required_argument, NULL, 'i'},
+ {"norm", required_argument, NULL, 'n'},
+ {"size", required_argument, NULL, 's'},
+ {"aafile", required_argument, NULL, 'o'},
+ {"ftp", required_argument, NULL, 'f'},
+ {"daemon", no_argument, NULL, 'D'},
+ {"font-size", required_argument, NULL, 'S'},
+ {"font-face", required_argument, NULL, 'a'},
+ {"refresh", required_argument, NULL, 'r'},
+ {"aabright", required_argument, NULL, 'b'},
+ {"aacontrast", required_argument, NULL, 'c'},
+ {"aagamma", required_argument, NULL, 'g'},
+ {"invert", no_argument, NULL, 'I'},
+ {"background", required_argument, NULL, 'B'},
+ {"foreground", required_argument, NULL, 'F'},
+ {"jpgfile", required_argument, NULL, 'O'},
+ {"jpgqual", required_argument, NULL, 'Q'},
+ {"uid", required_argument, NULL, 'U'},
+ {"gid", required_argument, NULL, 'G'},
+ // { "bttvbright", required_argument, NULL, 'B' },
+ // { "bttvcontrast", required_argument, NULL, 'C' },
+ // { "bttvgamma", required_argument, NULL, 'G' },
+ {0, 0, 0, 0}
+};
+#endif
+
+char *short_options = "hvqm:d:i:n:s:f:DS:a:r:o:b:c:g:IB:F:O:Q:U:G:";
+
+/* default configuration */
+
+volatile sig_atomic_t userbreak;
+
+int quiet = 0;
+int mode = 0;
+int useftp = 0;
+int input = 1;
+int jpgdump = 0;
+int daemon_mode = 0;
+
+#ifndef HAVE_SGI
+int norm = VIDEO_MODE_AUTO;
+#endif
+
+int width = 96;
+int height = 72;
+
+int refresh = 2;
+int fontsize = 1;
+char *device = "/dev/video";
+char *aafile;
+char *jpgfile;
+
+char *fontface = "courier";
+char *ftp;
+char *ftp_user;
+char *ftp_host;
+char *ftp_dir;
+char *ftp_pass;
+
+int jpgqual = 65;
+int uid = -1;
+int gid = -1;
+
+int aabright = 50;
+int aacontrast = 10;
+int aagamma = 10;
+int invert = 0;
+char *background = "000000";
+char *foreground = "00FF00";
+
+
+/* if width&height have been manually changed */
+int whchanged = 0;
+
+/* if the device has a tuner */
+int have_tuner = 0;
+
+/* ascii context & html formatting stuff*/
+aa_context *ascii_img;
+
+char html_header[512];
+
+static char *html_escapes[] =
+ { "<", "&lt;", ">", "&gt;", "&", "&amp;", NULL };
+
+struct aa_format aa_html_format = {
+ 79, 25,
+ 0, 0,
+ 0,
+ AA_NORMAL_MASK | AA_BOLD_MASK | AA_BOLDFONT_MASK,
+ NULL,
+ "Pure html",
+ ".html",
+ html_header,
+ "</PRE>\n</FONT>\n</BODY>\n</HTML>\n",
+ "\n",
+ /*The order is:normal, dim, bold, boldfont, reverse, special */
+ {"%s", "%s", "%s", "%s", "%s",},
+ {"", "", "<B>", "", "<B>"},
+ {"", "", "</B>", "", "</B>"},
+ html_escapes
+};
+
+/* jpeg */
+
+void
+swap_rgb24 (char *mem, int n)
+{
+ char c;
+ char *p = mem;
+ int i = n;
+
+ while (--i)
+ {
+ c = p[0];
+ p[0] = p[2];
+ p[2] = c;
+ p += 3;
+ }
+}
+
+void
+resample_8bit (unsigned char *mem, int n, unsigned char *dest)
+{
+ int c;
+ int cc = 0;
+ for (c = 0; c < n; c += 3)
+ {
+ dest[cc] =
+ (int) rint ((mem[c]) * 0.3 + (mem[c + 1]) * 0.59 +
+ (mem[c + 2]) * 0.11);
+ cc++;
+ }
+}
+
+int
+write_jpeg (char *filename, char *data, int w, int h)
+{
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ FILE *fp;
+ char *dest;
+
+ int i;
+ unsigned char *line;
+
+ if (NULL == (fp = fopen (TEMPFILE, "w")))
+ {
+ fprintf (stderr, "can't open %s for writing: %s\n",
+ TEMPFILE, strerror (errno));
+ return -1;
+ }
+
+ cinfo.err = jpeg_std_error (&jerr);
+ jpeg_create_compress (&cinfo);
+ jpeg_stdio_dest (&cinfo, fp);
+ cinfo.image_width = w;
+ cinfo.image_height = h;
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
+ jpeg_set_defaults (&cinfo);
+ jpeg_set_quality (&cinfo, jpgqual, TRUE);
+ jpeg_start_compress (&cinfo, TRUE);
+
+ for (i = 0, line = data; i < h; i++, line += w * 3)
+ jpeg_write_scanlines (&cinfo, &line, 1);
+
+ jpeg_finish_compress (&(cinfo));
+ jpeg_destroy_compress (&(cinfo));
+ fclose (fp);
+
+ // 3 for the "mv " + source file + 1 for " " + dest file + NULL
+ dest =
+ (char *) calloc (3 + strlen (TEMPFILE) + 1 + strlen (filename) + 1,
+ sizeof (char));
+ strcpy (dest, "mv ");
+ strcat (dest, TEMPFILE);
+ strcat (dest, " ");
+ strcat (dest, filename);
+ system (dest);
+ free (dest);
+ return 0;
+}
+
+#ifdef HAVE_LINUX
+/* v4l */
+
+static struct video_capability grab_cap;
+static struct video_mmap grab_buf;
+static struct video_channel grab_chan;
+struct video_picture grab_pic;
+static int grab_fd = -1;
+static int grab_size;
+
+void
+grab_detect ()
+{
+ int counter, res;
+ char *capabilities[] = {
+ " VID_TYPE_CAPTURE can capture to memory",
+ " VID_TYPE_TUNER has a tuner of some form",
+ " VID_TYPE_TELETEXT has teletext capability",
+ " VID_TYPE_OVERLAY can overlay its image onto the frame buffer",
+ " VID_TYPE_CHROMAKEY overlay is chromakeyed",
+ " VID_TYPE_CLIPPING overlay clipping supported",
+ " VID_TYPE_FRAMERAM overlay overwrites frame buffer memory",
+ " VID_TYPE_SCALES supports image scaling",
+ " VID_TYPE_MONOCHROME image capture is grey scale only",
+ " VID_TYPE_SUBCAPTURE capture can be of only part of the image"
+ };
+
+ res = ioctl (grab_fd, VIDIOCGCAP, &grab_cap);
+ if (res < 0)
+ {
+ perror ("Error in VIDIOCGCAP ");
+ exit (1);
+ }
+
+ fprintf (stderr, "-- detecting hardware\n");
+ fprintf (stderr, "Device used is %s\n", device);
+ fprintf (stderr, "%s\n", grab_cap.name);
+ fprintf (stderr, "%u channels detected\n", grab_cap.channels);
+ fprintf (stderr, "maximum capture size is %ux%u\n", grab_cap.maxwidth,
+ grab_cap.maxheight);
+ fprintf (stderr, "minimum capture size is %ux%u\n", grab_cap.minwidth,
+ grab_cap.minheight);
+ fprintf (stderr, "Video capabilities:\n");
+ for (counter = 0; counter < 11; counter++)
+ if (grab_cap.type & (1 << counter))
+ fprintf (stderr, "%s\n", capabilities[counter]);
+
+ if (-1 == ioctl (grab_fd, VIDIOCGPICT, &grab_pic))
+ {
+ perror ("ioctl VIDIOCGPICT ");
+ exit (1);
+ }
+
+ if (strncmp (grab_cap.name, "OV511", 5) == 0)
+ /* the device is a USB camera, chipset OV511, wich has only one input
+ channel (afaik, alltough i don't have one) so we force that chan */
+ input = 0;
+
+ if (grab_cap.type & VID_TYPE_TUNER)
+ /* the device does'nt has any tuner, so we avoid some ioctl
+ this should be a fix for Creative Webcam II. thanks to Ben Wilson */
+ have_tuner = 1;
+
+ /* we set the minwidth and minheight as default size values if they are
+ greater than the normal default */
+ if (whchanged == 0)
+ {
+ if (((grab_cap.minwidth / 2) > width)
+ || ((grab_cap.minheight / 2) > height))
+ {
+ /* remember width & height are the ASCII size */
+ width = grab_cap.minwidth / 2;
+ height = grab_cap.minheight / 2;
+ }
+ }
+
+ fprintf (stderr, "\n-- going capture\n");
+}
+
+unsigned char *
+grab_init ()
+{
+ unsigned char *grab_data;
+ int linespace = 5;
+
+ if (-1 == (grab_fd = open (device, O_RDWR)))
+ {
+ perror ("Error in opening video capture device");
+ exit (1);
+ }
+
+ if (quiet == 0)
+ grab_detect ();
+
+ /* set image source and TV norm */
+ if (grab_cap.channels >= input)
+ grab_chan.channel = input;
+ else
+ grab_chan.channel = 0;
+
+ if (have_tuner)
+ {
+ /* does this only if the device has a tuner */
+ if (-1 == ioctl (grab_fd, VIDIOCGCHAN, &grab_chan))
+ {
+ perror ("ioctl VIDIOCGCHAN ");
+ exit (1);
+ }
+ if (-1 == ioctl (grab_fd, VIDIOCSCHAN, &grab_chan))
+ {
+ perror ("ioctl VIDIOCSCHAN ");
+ exit (1);
+ }
+ }
+
+
+ grab_buf.height = height*2;
+ grab_buf.width = width*2;
+
+ grab_buf.format = VIDEO_PALETTE_RGB24;
+ grab_buf.frame = 0;
+ grab_size = grab_buf.width * grab_buf.height * 3;
+ grab_data =
+ mmap (0, grab_size, PROT_READ | PROT_WRITE, MAP_SHARED, grab_fd, 0);
+ if (-1 == (int) grab_data)
+ {
+ perror ("Cannot allocate video surface ");
+ exit (1);
+ }
+
+ /* untested with new code restructuration */
+ switch (fontsize)
+ {
+ case 1:
+ linespace = 5;
+ break;
+ case 2:
+ linespace = 10;
+ break;
+ case 3:
+ linespace = 11;
+ break;
+ case 4:
+ linespace = 13;
+ break;
+ }
+
+ /* init the html header */
+ snprintf (&html_header[0], 512,
+ "<HTML>\n <HEAD> <TITLE>wow! (h)ascii 4 the masses!</TITLE>\n<META HTTP-EQUIV=\"refresh\" CONTENT=\"%u\"; url=\"%s\">\n<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">\n<STYLE TYPE=\"text/css\">\n<!--\npre {\nletter-spacing: 1px;\nlayer-background-color: Black;\nleft : auto;\nline-height : %upx;\n}\n-->\n</STYLE>\n</HEAD>\n<BODY bgcolor=\"#%s\" text=\"#%s\">\n<FONT SIZE=%u face=\"%s\">\n<PRE>\n",
+ refresh, aafile, linespace, background, foreground, fontsize,
+ fontface);
+ return (grab_data);
+}
+
+int
+grab_one ()
+{
+
+ if (ioctl (grab_fd, VIDIOCMCAPTURE, &grab_buf) == -1)
+ perror ("ioctl VIDIOCMCAPTURE");
+ else if (ioctl (grab_fd, VIDIOCSYNC, &grab_buf) != -1)
+ /* i comment this out to not make complain hasciicam when exiting
+ perror ("ioctl VIDIOCSYNC"); */
+ return 1;
+
+ return -1;
+}
+#endif
+
+#ifdef sgi
+/* Use IRIX DMedia layer */
+
+#define VIDEO_PALETTE_RGB24 0
+
+VLServer svr;
+VLPath path;
+VLNode src, drn;
+VLBuffer buffer;
+VLControlValue val;
+VLInfoPtr info;
+
+struct grab_buf_s
+{
+ int width, height;
+}
+grab_buf;
+
+void
+strip_alpha (char *ptr, int count)
+{
+ char *out = ptr;
+ while (count--)
+ {
+ ++ptr;
+ *out++ = *ptr++;
+ *out++ = *ptr++;
+ *out++ = *ptr++;
+ }
+}
+
+unsigned char *
+grab_init ()
+{
+ int linespace = 5;
+
+ if (!(svr = vlOpenVideo ("")))
+ {
+ vlPerror ("hasciicam");
+ exit (1);
+ }
+
+ /* set up a drain node in memory and a source node */
+ drn = vlGetNode (svr, VL_DRN, VL_MEM, VL_ANY);
+ src = vlGetNode (svr, VL_SRC, VL_VIDEO, VL_ANY);
+
+ /* create a path using the first supporting device */
+ path = vlCreatePath (svr, VL_ANY, src, drn);
+
+ /* setup the hardware for the path */
+ if ((vlSetupPaths (svr, (VLPathList) & path, 1, VL_SHARE, VL_SHARE)) < 0)
+ {
+ vlPerror ("vlSetupPaths");
+ exit (2);
+ }
+
+ /* why even if I specify RGB packing the data gets
+ out in RGBA ? */
+ val.intVal = VL_PACKING_RGB_8;
+ vlSetControl (svr, path, drn, VL_PACKING, &val);
+
+ /* create and register a 1 frame buffer */
+ buffer = vlCreateBuffer (svr, path, drn, 1);
+ if (buffer == NULL)
+ {
+ vlPerror ("vlCreateBuffer");
+ exit (1);
+ }
+ vlRegisterBuffer (svr, path, drn, buffer);
+
+ vlGetControl (svr, path, drn, VL_SIZE, &val);
+ grab_buf.width = val.xyVal.x;
+ grab_buf.height = val.xyVal.y;
+
+ switch (fontsize)
+ {
+ case 1:
+ linespace = 5;
+ break;
+ case 2:
+ linespace = 10;
+ break;
+ case 3:
+ linespace = 11;
+ break;
+ case 4:
+ linespace = 13;
+ break;
+ }
+
+ /* init the html header */
+ snprintf (&html_header[0], 512,
+ "<HTML>\n <HEAD> <TITLE>wow! (h)ascii 4 the masses!</TITLE>\n<META HTTP-EQUIV=\"refresh\" CONTENT=\"%u\"; url=\"%s\">\n<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">\n<STYLE TYPE=\"text/css\">\n<!--\npre {\nletter-spacing: 1px;\nlayer-background-color: Black;\nleft : auto;\nline-height : %upx;\n}\n-->\n</STYLE>\n</HEAD>\n<BODY bgcolor=\"#%s\" text=\"#%s\">\n<FONT SIZE=%u face=\"%s\">\n<PRE>\n",
+ refresh, aafile, linespace, background, foreground, fontsize,
+ fontface);
+
+ return malloc (width * height * 4);
+}
+
+int
+grab_one (int width, int height, int pal)
+{
+ unsigned char *frameData;
+ if (vlBeginTransfer (svr, path, 0, NULL))
+ {
+ vlPerror ("vlBeginTransfer");
+ exit (1);
+ }
+ do
+ {
+ info = vlGetNextValid (svr, buffer);
+ }
+ while (!info);
+ frameData = vlGetActiveRegion (svr, buffer, info);
+ strip_alpha (frameData, grab_buf.width * grab_buf.height);
+ resample_8bit (frameData, grab_buf.width * grab_buf.height);
+ zoom (frameData, grab_buf.width, grab_buf.height,
+ image, width * 2, height * 2);
+ vlEndTransfer (svr, path);
+ vlPutFree (svr, buffer);
+}
+
+void
+grab_close (void)
+{
+ vlDeregisterBuffer (svr, path, drn, buffer);
+ vlDestroyBuffer (svr, buffer);
+ vlDestroyPath (svr, path);
+ vlCloseVideo (svr);
+}
+
+#endif
+
+void
+config_init (int argc, char *argv[])
+{
+ int res;
+
+ do
+ {
+#ifdef HAVE_SGI
+ res = getopt (argc, argv, short_options);
+#else
+ res = getopt_long (argc, argv, short_options, long_options, NULL);
+#endif
+ switch (res)
+ {
+ case 'h':
+ fprintf (stderr, "%s", help);
+ exit (0);
+ break;
+ case 'v':
+ exit (0);
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'm':
+ if (strcasecmp (optarg, "live") == 0)
+ {
+ if (useftp)
+ {
+ fprintf (stderr,"heek, ftp option makes no sense with live mode!\n");
+ useftp = 0;
+ }
+ mode = 0;
+ }
+ else if (strcasecmp (optarg, "html") == 0)
+ {
+ mode = 1;
+ aafile = strdup ("hasciicam.html");
+ }
+ else if (strcasecmp (optarg, "text") == 0)
+ {
+ mode = 2;
+ aafile = strdup ("hasciicam.asc");
+ }
+ else
+ fprintf (stderr, "!! invalid mode selected, using live\n");
+ break;
+ case 'd':
+ device = strdup (optarg);
+ break;
+ case 'i':
+ input = atoi (optarg);
+ /*
+ here we assume that capture cards have maximum 3 channels
+ (usually the 4th, when present, is the radio tuner)
+ */
+ if (input > 3)
+ {
+ fprintf (stderr, "invalid input selected\n");
+ exit (1);
+ }
+ break;
+ case 'n':
+#ifdef HAVE_LINUX
+ if (strcmp (optarg, "pal") == 0)
+ norm = VIDEO_MODE_PAL;
+ else if (strcmp (optarg, "ntsc") == 0)
+ norm = VIDEO_MODE_NTSC;
+ else if (strcmp (optarg, "secam") == 0)
+ norm = VIDEO_MODE_SECAM;
+ else if (strcmp (optarg, "auto") == 0)
+ norm = VIDEO_MODE_AUTO;
+ else
+ fprintf (stderr, "!! invalid video norm selected, using auto\n");
+ break;
+#endif
+ case 's':
+ {
+ char *t;
+ char *tt;
+ t = optarg;
+ while (isdigit (*t))
+ t++;
+ *t = 0;
+ width = atoi (optarg);
+ tt = ++t;
+ while (isdigit (*tt))
+ tt++;
+ *tt = 0;
+ height = atoi (t);
+ whchanged = 1;
+ }
+ break;
+ case 'S':
+ fontsize = atoi (optarg);
+ break;
+ case 'a':
+ fontface = strdup (optarg);
+ break;
+ case 'r':
+ refresh = atoi (optarg);
+ break;
+ case 'o':
+ if(mode>0) {
+ free (aafile);
+ aafile = strdup (optarg);
+ }
+ break;
+ case 'f':
+ if (mode>0) {
+ ftp = strdup (optarg);
+ useftp = 1;
+ }
+ else
+ fprintf (stderr,
+ "heek, ftp option makes no sense with live mode!\n");
+ break;
+ case 'D':
+ daemon_mode = 1;
+ break;
+ case 'b':
+ aabright = atoi (optarg);
+ break;
+ case 'c':
+ aacontrast = atoi (optarg);
+ break;
+ case 'g':
+ aagamma = atoi (optarg);
+ break;
+ case 'I':
+ invert = 1;
+ break;
+ case 'B':
+ background = strdup (optarg);
+ break;
+ case 'F':
+ foreground = strdup (optarg);
+ break;
+ case 'O':
+ if(mode>0) {
+ jpgfile = strdup (optarg);
+ jpgdump = 1;
+ } else
+ fprintf (stderr,"heek, jpeg dump becomes dangerous with live mode!\n");
+ break;
+ case 'Q':
+ jpgqual = atoi (optarg);
+ break;
+ case 'U':
+ uid = atoi (optarg);
+ break;
+ case 'G':
+ gid = atoi (optarg);
+ break;
+ /*
+ case 'B':
+ bttvbright = atoi(optarg);
+ break;
+ case 'C':
+ bttvcontrast = atoi(optarg);
+ break;
+ case 'G':
+ bttvgamma = atoi(optarg);
+ break;
+ */
+ }
+ }
+ while (res > 0);
+
+ if ((width == 0) || (height == 0))
+ {
+ printf ("Error: invalid size %dx%d\n", width, height);
+ exit (0);
+ }
+
+ if (useftp)
+ {
+ char *p, *pp;
+ p = ftp;
+
+ while (*p != '@')
+ {
+ if ((ftp - p) < 32)
+ p++;
+ else
+ {
+ printf ("Error: malformed ftp command: %s\n", ftp);
+ exit (0);
+ }
+ }
+ *p = '\0';
+ ftp_user = strdup(ftp);
+ p++;
+ pp = p;
+
+ while (*p != ':')
+ {
+ if ((pp - p) < 64)
+ p++;
+ else
+ {
+ printf ("Error: malformed ftp command: %s\n", ftp);
+ exit (0);
+ }
+ }
+
+ *p = '\0';
+ ftp_host = strdup(pp);
+ p++;
+ pp = p;
+
+ while (*p != '\0')
+ {
+ if ((pp - p) < 64)
+ p++;
+ else
+ {
+ printf ("Error: malformed ftp command: %s\n", ftp);
+ exit (0);
+ }
+ }
+ if((pp-p)==0) ftp_dir = strdup(".");
+ else ftp_dir = strdup(pp);
+ }
+}
+
+/* here we go (chmicl broz rlz! :)*/
+
+int
+main (int argc, char **argv)
+{
+
+ /*
+ reminder:
+ !!! grabbing height & width should be
+ double the ascii context width and height !!!
+ */
+
+ /* aalib vars */
+ int awidth, aheight;
+ struct aa_renderparams ascii_parms;
+ struct aa_hardware_params ascii_surf;
+ struct aa_savedata ascii_save;
+
+ /* image downsampling */
+ unsigned char *grey;
+
+ /* register signal traps */
+ if (signal (SIGINT, quitproc) == SIG_ERR)
+ {
+ perror ("Couldn't install SIGINT handler");
+ exit (1);
+ }
+
+ fprintf (stderr, version, PACKAGE, VERSION);
+
+ /* default values */
+ uid = getuid ();
+ gid = getgid ();
+
+
+ /* gathering line commands */
+ config_init (argc, argv);
+
+ /* initialize aalib default params */
+ memcpy (&ascii_surf, &aa_defparams, sizeof (struct aa_hardware_params));
+
+ /* sets the default size on live mode */
+ if (whchanged == 0 && mode == 0)
+ {
+ width = 80;
+ height = 40;
+ }
+
+ /* bttv grabber init */
+ image = grab_init ();
+
+ /* width/height image setup */
+ ascii_surf.width = width;
+ ascii_surf.height = height;
+
+ if (!quiet)
+ fprintf (stderr, "grabbed image is %dx%d", grab_buf.width,
+ grab_buf.height);
+
+ setuid (uid);
+ setgid (gid);
+
+ switch (mode)
+ {
+ case 0:
+ if (!quiet)
+ {
+ fprintf (stderr, " - ascii context is %dx%d\n", width, height);
+ fprintf (stderr, "using LIVE mode\n");
+ }
+ break;
+
+ case 1:
+ ascii_save.name = aafile;
+ ascii_save.format = &aa_html_format;
+ ascii_save.file = NULL;
+ if (!quiet)
+ {
+ fprintf (stderr, " - ascii context is %dx%d\n", width, height);
+ fprintf (stderr, "using HTML mode dumping to file %s\n", aafile);
+ if (useftp)
+ fprintf (stderr, " ftp-pushing on %s%s\n", ftp_host, ftp_dir);
+ }
+ break;
+
+ case 2:
+ ascii_save.name = aafile;
+ ascii_save.format = &aa_text_format;
+ ascii_save.file = NULL;
+ if (!quiet)
+ {
+ fprintf (stderr, " - ascii context is %dx%d\n", width, height);
+ fprintf (stderr, "using TEXT mode dumping to file %s\n", aafile);
+ if (useftp)
+ fprintf (stderr, " ftp-pushing on ftp://%s%s\n", ftp_host,
+ ftp_dir);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if(jpgdump)
+ fprintf(stderr," dumping jpeg image on %s\n", jpgfile);
+
+ fprintf(stderr,"\n");
+
+ /* aalib init */
+ if (mode > 0)
+ ascii_img = aa_init (&save_d, &ascii_surf, &ascii_save);
+ else
+ ascii_img = aa_autoinit (&ascii_surf);
+
+/* ftp init *//* untested with new code changes */
+ if (useftp)
+ {
+ char temp[160];
+ ftp_init (0);
+ sprintf (temp, "password for %s@%s : ", ftp_user, ftp_host);
+ ftp_pass = getpass (temp);
+ ftp_connect (ftp_host, ftp_user, ftp_pass, ftp_dir);
+ }
+
+ /* setting ascii rendering parameters */
+ ascii_parms.bright = aabright;
+ ascii_parms.contrast = aacontrast;
+ ascii_parms.gamma = aagamma;
+ ascii_parms.dither = AA_FLOYD_S;
+ ascii_parms.inversion = invert;
+ ascii_parms.randomval = 0;
+
+ grey =
+ (unsigned char *) calloc (width * height * 4, sizeof (unsigned char));
+
+ if (daemon_mode)
+ daemon (0, 1);
+
+ /* cycle until ctrl-c */
+
+ while (userbreak < 1) {
+ grab_one ();
+
+ resample_8bit (image, grab_buf.width * grab_buf.height * 3, grey);
+ memcpy (aa_image (ascii_img), grey, width * height * 4);
+ aa_render (ascii_img, &ascii_parms, 0, 0, width, height);
+ aa_flush (ascii_img);
+ if(jpgdump) {
+ swap_rgb24 (image, grab_buf.width * grab_buf.height);
+ write_jpeg (jpgfile, image, grab_buf.width, grab_buf.height);
+ }
+
+ if (useftp)
+ {
+ if (!ftp_connected)
+ ftp_connect (ftp_host, ftp_user, ftp_pass, ftp_dir);
+ /* scolopendro is the tmp file being renamed
+ this is here for hystorical reasons
+ feel free to change it - be hacker ;) */
+ ftp_upload (aafile, aafile, "scolopendro");
+ if(jpgdump) ftp_upload (jpgfile, jpgfile, "scolopendro");
+ }
+
+ if (mode > 0)
+ sleep (refresh);
+ }
+
+ if(jpgdump) free (jpgfile);
+ if(mode>0) free (aafile);
+ free (grey);
+#ifdef HAVE_SGI
+ grab_close ();
+#else
+ if (grab_fd > 0)
+ close (grab_fd);
+
+ if (image != NULL)
+ munmap (image, grab_size);
+#endif
+
+ fprintf (stderr, "cya!\n");
+ exit (0);
+}
+
+/* signal handling */
+void
+quitproc (int Sig)
+{
+
+ fprintf (stderr, "interrupt caught, exiting.\n");
+ ftp_close();
+ userbreak = 1;
+
+}