| /* |
| Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com> |
| All rights reserved. |
| |
| This file is part of x11vnc. |
| |
| x11vnc is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2 of the License, or (at |
| your option) any later version. |
| |
| x11vnc 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. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with x11vnc; if not, write to the Free Software |
| Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA |
| or see <http://www.gnu.org/licenses/>. |
| |
| In addition, as a special exception, Karl J. Runge |
| gives permission to link the code of its release of x11vnc with the |
| OpenSSL project's "OpenSSL" library (or with modified versions of it |
| that use the same license as the "OpenSSL" library), and distribute |
| the linked executables. You must obey the GNU General Public License |
| in all respects for all of the code used other than "OpenSSL". If you |
| modify this file, you may extend this exception to your version of the |
| file, but you are not obligated to do so. If you do not wish to do |
| so, delete this exception statement from your version. |
| */ |
| |
| /* -- v4l.c -- */ |
| |
| #include "x11vnc.h" |
| #include "cleanup.h" |
| #include "scan.h" |
| #include "xinerama.h" |
| #include "screen.h" |
| #include "connections.h" |
| #include "keyboard.h" |
| #include "allowed_input_t.h" |
| |
| #if LIBVNCSERVER_HAVE_LINUX_VIDEODEV_H |
| #if LIBVNCSERVER_HAVE_SYS_IOCTL_H |
| #include <sys/ioctl.h> |
| #define CONFIG_VIDEO_V4L1_COMPAT |
| #include <linux/videodev.h> |
| #ifdef __LINUX_VIDEODEV2_H |
| # ifndef HAVE_V4L2 |
| # define HAVE_V4L2 1 |
| # endif |
| #endif |
| #define V4L_OK |
| #endif |
| #endif |
| |
| char *v4l_guess(char *str, int *fd); |
| void v4l_key_command(rfbBool down, rfbKeySym keysym, rfbClientPtr client); |
| void v4l_pointer_command(int mask, int x, int y, rfbClientPtr client); |
| |
| static int v4l1_val(int pct); |
| static int v4l1_width(int w); |
| static int v4l1_height(int h); |
| static int v4l1_resize(int fd, int w, int h); |
| static void v4l1_setfreq(int fd, unsigned long freq, int verb); |
| static void v4l1_set_input(int fd, int which); |
| static int v4l1_setfmt(int fd, char *fmt); |
| static void apply_settings(char *dev, char *settings, int *fd); |
| static int v4l1_dpct(int old, int d); |
| static void v4l_requery(void); |
| static void v4l_br(int b); |
| static void v4l_hu(int b); |
| static void v4l_co(int b); |
| static void v4l_cn(int b); |
| static void v4l_sz(int b); |
| static void v4l_sta(int sta); |
| static void v4l_inp(int inp); |
| static void v4l_fmt(char *fmt); |
| static int colon_n(char *line); |
| static char *colon_str(char *line); |
| static char *colon_tag(char *line); |
| static void lookup_rgb(char *g_fmt, int *g_b, int *mask_rev); |
| static char *v4l1_lu_palette(unsigned short palette); |
| static unsigned short v4l1_lu_palette_str(char *name, int *bits, int *rev); |
| static char *v4l2_lu_palette(unsigned int palette); |
| static unsigned int v4l2_lu_palette_str(char *name, int *bits, int *rev); |
| static int v4l1_query(int fd, int verbose); |
| static int v4l2_query(int fd, int verbose); |
| static int open_dev(char *dev); |
| static char *guess_via_v4l(char *dev, int *fd); |
| static char *guess_via_v4l_info(char *dev, int *fd); |
| static void parse_str(char *str, char **dev, char **settings, char **atparms); |
| static unsigned long lookup_freqtab(int sta); |
| static unsigned long lookup_freq(int sta); |
| static int lookup_station(unsigned long freq); |
| static void init_freqtab(char *file); |
| static void init_freqs(void); |
| static void init_ntsc_cable(void); |
| |
| #define C_VIDEO_CAPTURE 1 |
| #define C_PICTURE 2 |
| #define C_WINDOW 3 |
| |
| #ifdef V4L_OK |
| static struct video_capability v4l1_capability; |
| static struct video_channel v4l1_channel; |
| static struct video_tuner v4l1_tuner; |
| static struct video_picture v4l1_picture; |
| static struct video_window v4l1_window; |
| |
| #if HAVE_V4L2 |
| static struct v4l2_capability v4l2_capability; |
| static struct v4l2_input v4l2_input; |
| static struct v4l2_tuner v4l2_tuner; |
| static struct v4l2_fmtdesc v4l2_fmtdesc; |
| static struct v4l2_format v4l2_format; |
| /*static struct v4l2_framebuffer v4l2_fbuf; */ |
| /*static struct v4l2_queryctrl v4l2_qctrl; */ |
| #endif |
| #endif |
| |
| static int v4l1_cap = -1; |
| static int v4l2_cap = -1; |
| #define V4L1_MAX 65535 |
| |
| #define CHANNEL_MAX 500 |
| static unsigned long ntsc_cable[CHANNEL_MAX]; |
| static unsigned long custom_freq[CHANNEL_MAX]; |
| |
| static unsigned long last_freq = 0; |
| static int last_channel = 0; |
| |
| static int v4l1_val(int pct) { |
| /* pct is % */ |
| int val, max = V4L1_MAX; |
| if (pct < 0) { |
| return 0; |
| } else if (pct > 100) { |
| return max; |
| } |
| val = (pct * max)/100; |
| |
| return val; |
| } |
| static int v4l1_width(int w) { |
| #ifdef V4L_OK |
| int min = v4l1_capability.minwidth; |
| int max = v4l1_capability.maxwidth; |
| if (w < min) { |
| w = min; |
| } |
| if (w > max) { |
| w = max; |
| } |
| #endif |
| return w; |
| } |
| static int v4l1_height(int h) { |
| #ifdef V4L_OK |
| int min = v4l1_capability.minheight; |
| int max = v4l1_capability.maxheight; |
| if (h < min) { |
| h = min; |
| } |
| if (h > max) { |
| h = max; |
| } |
| #endif |
| return h; |
| } |
| |
| static int v4l1_resize(int fd, int w, int h) { |
| #ifdef V4L_OK |
| int dowin = 0; |
| |
| memset(&v4l1_window, 0, sizeof(v4l1_window)); |
| if (ioctl(fd, VIDIOCGWIN, &v4l1_window) == -1) { |
| return 0; |
| } |
| |
| if (w > 0) w = v4l1_width(w); |
| |
| if (w > 0 && w != (int) v4l1_window.width) { |
| dowin = 1; |
| } |
| |
| if (h > 0) h = v4l1_height(h); |
| |
| if (h > 0 && h != (int) v4l1_window.height) { |
| dowin = 1; |
| } |
| |
| if (dowin) { |
| v4l1_window.x = 0; |
| v4l1_window.y = 0; |
| ioctl(fd, VIDIOCSWIN, &v4l1_window); |
| if (w > 0) v4l1_window.width = w; |
| if (h > 0) v4l1_window.height = h; |
| fprintf(stderr, "calling V4L_1: VIDIOCSWIN\n"); |
| fprintf(stderr, "trying new size %dx%d\n", |
| v4l1_window.width, v4l1_window.height); |
| if (ioctl(fd, VIDIOCSWIN, &v4l1_window) == -1) { |
| perror("ioctl VIDIOCSWIN"); |
| return 0; |
| } |
| } |
| #else |
| if (!fd || !w || !h) {} |
| #endif |
| return 1; |
| } |
| |
| static void v4l1_setfreq(int fd, unsigned long freq, int verb) { |
| #ifdef V4L_OK |
| unsigned long f0, f1; |
| f1 = (freq * 16) / 1000; |
| ioctl(fd, VIDIOCGFREQ, &f0); |
| if (verb) fprintf(stderr, "read freq: %d\n", (int) f0); |
| if (freq > 0) { |
| if (ioctl(fd, VIDIOCSFREQ, &f1) == -1) { |
| perror("ioctl VIDIOCSFREQ"); |
| } else { |
| ioctl(fd, VIDIOCGFREQ, &f0); |
| if (verb) fprintf(stderr, "read freq: %d\n", (int) f0); |
| last_freq = freq; |
| } |
| } |
| #else |
| if (!fd || !freq || !verb) {} |
| #endif |
| } |
| |
| static void v4l1_set_input(int fd, int which) { |
| #ifdef V4L_OK |
| if (which != -1) { |
| memset(&v4l1_channel, 0, sizeof(v4l1_channel)); |
| v4l1_channel.channel = which; |
| if (ioctl(fd, VIDIOCGCHAN, &v4l1_channel) != -1) { |
| v4l1_channel.channel = which; |
| fprintf(stderr, "setting input channel to %d: %s\n", |
| which, v4l1_channel.name); |
| last_channel = which; |
| ioctl(fd, VIDIOCSCHAN, &v4l1_channel); |
| } |
| } |
| #else |
| if (!fd || !which) {} |
| #endif |
| } |
| |
| static int v4l1_setfmt(int fd, char *fmt) { |
| #ifdef V4L_OK |
| unsigned short fnew; |
| int bnew, rnew; |
| |
| fnew = v4l1_lu_palette_str(fmt, &bnew, &rnew); |
| if (fnew) { |
| v4l1_picture.depth = bnew; |
| v4l1_picture.palette = fnew; |
| } |
| fprintf(stderr, "calling V4L_1: VIDIOCSPICT\n"); |
| if (ioctl(fd, VIDIOCSPICT, &v4l1_picture) == -1) { |
| perror("ioctl VIDIOCSPICT"); |
| return 0; |
| } |
| if (raw_fb_pixfmt) { |
| free(raw_fb_pixfmt); |
| } |
| raw_fb_pixfmt = strdup(fmt); |
| #else |
| if (!fd || !fmt) {} |
| #endif |
| return 1; |
| } |
| |
| static int ignore_all = 0; |
| |
| static void apply_settings(char *dev, char *settings, int *fd) { |
| #ifdef V4L_OK |
| char *str, *p, *fmt = NULL, *tun = NULL, *inp = NULL; |
| int br = -1, co = -1, cn = -1, hu = -1; |
| int w = -1, h = -1, b = -1; |
| int sta = -1; |
| int setcnt = 0; |
| if (! settings || settings[0] == '\0') { |
| return; |
| } |
| str = strdup(settings); |
| p = strtok(str, ","); |
| while (p) { |
| if (strstr(p, "br=") == p) { |
| br = atoi(p+3); |
| if (br >= 0) setcnt++; |
| } else if (strstr(p, "co=") == p) { |
| co = atoi(p+3); |
| if (co >= 0) setcnt++; |
| } else if (strstr(p, "cn=") == p) { |
| cn = atoi(p+3); |
| if (cn >= 0) setcnt++; |
| } else if (strstr(p, "hu=") == p) { |
| hu = atoi(p+3); |
| if (hu >= 0) setcnt++; |
| } else if (strstr(p, "w=") == p) { |
| w = atoi(p+2); |
| if (w > 0) setcnt++; |
| } else if (strstr(p, "h=") == p) { |
| h = atoi(p+2); |
| if (h > 0) setcnt++; |
| } else if (strstr(p, "bpp=") == p) { |
| b = atoi(p+4); |
| if (b > 0) setcnt++; |
| } else if (strstr(p, "fmt=") == p) { |
| fmt = strdup(p+4); |
| setcnt++; |
| } else if (strstr(p, "tun=") == p) { |
| tun = strdup(p+4); |
| setcnt++; |
| } else if (strstr(p, "inp=") == p) { |
| inp = strdup(p+4); |
| setcnt++; |
| } else if (strstr(p, "sta=") == p) { |
| sta = atoi(p+4); |
| setcnt++; |
| } |
| p = strtok(NULL, ","); |
| } |
| free(str); |
| if (! setcnt) { |
| return; |
| } |
| if (*fd < 0) { |
| *fd = open_dev(dev); |
| } |
| if (*fd < 0) { |
| return; |
| } |
| v4l1_cap = v4l1_query(*fd, 1); |
| v4l2_cap = v4l2_query(*fd, 1); |
| |
| if (v4l1_cap && ! ignore_all) { |
| if (br >= 0) v4l1_picture.brightness = v4l1_val(br); |
| if (hu >= 0) v4l1_picture.hue = v4l1_val(hu); |
| if (co >= 0) v4l1_picture.colour = v4l1_val(co); |
| if (cn >= 0) v4l1_picture.contrast = v4l1_val(cn); |
| |
| fprintf(stderr, "calling V4L_1: VIDIOCSPICT\n"); |
| if (ioctl(*fd, VIDIOCSPICT, &v4l1_picture) == -1) { |
| perror("ioctl VIDIOCSPICT"); |
| } |
| |
| if (fmt) { |
| v4l1_setfmt(*fd, fmt); |
| } else if (b > 0 && b != v4l1_picture.depth) { |
| if (b == 8) { |
| v4l1_setfmt(*fd, "HI240"); |
| } else if (b == 16) { |
| v4l1_setfmt(*fd, "RGB565"); |
| } else if (b == 24) { |
| v4l1_setfmt(*fd, "RGB24"); |
| } else if (b == 32) { |
| v4l1_setfmt(*fd, "RGB32"); |
| } |
| } |
| |
| v4l1_resize(*fd, w, h); |
| |
| if (tun) { |
| int mode = -1; |
| if (!strcasecmp(tun, "PAL")) { |
| mode = VIDEO_MODE_PAL; |
| } else if (!strcasecmp(tun, "NTSC")) { |
| mode = VIDEO_MODE_NTSC; |
| } else if (!strcasecmp(tun, "SECAM")) { |
| mode = VIDEO_MODE_SECAM; |
| } else if (!strcasecmp(tun, "AUTO")) { |
| mode = VIDEO_MODE_AUTO; |
| } |
| if (mode != -1) { |
| int i; |
| for (i=0; i< v4l1_capability.channels; i++) { |
| memset(&v4l1_channel, 0, sizeof(v4l1_channel)); |
| v4l1_channel.channel = i; |
| if (ioctl(*fd, VIDIOCGCHAN, &v4l1_channel) == -1) { |
| continue; |
| } |
| if (! v4l1_channel.tuners) { |
| continue; |
| } |
| if (v4l1_channel.norm == mode) { |
| continue; |
| } |
| v4l1_channel.norm = mode; |
| ioctl(*fd, VIDIOCSCHAN, &v4l1_channel); |
| } |
| } |
| } |
| if (inp) { |
| char s[2]; |
| int i, chan = -1; |
| |
| s[0] = inp[0]; |
| s[1] = '\0'; |
| if (strstr("0123456789", s)) { |
| chan = atoi(inp); |
| } else { |
| for (i=0; i< v4l1_capability.channels; i++) { |
| memset(&v4l1_channel, 0, sizeof(v4l1_channel)); |
| v4l1_channel.channel = i; |
| if (ioctl(*fd, VIDIOCGCHAN, &v4l1_channel) == -1) { |
| continue; |
| } |
| if (!strcmp(v4l1_channel.name, inp)) { |
| chan = i; |
| break; |
| } |
| } |
| } |
| v4l1_set_input(*fd, chan); |
| } |
| if (sta >= 0) { |
| unsigned long freq = lookup_freq(sta); |
| v4l1_setfreq(*fd, freq, 1); |
| } |
| } |
| v4l1_cap = v4l1_query(*fd, 1); |
| v4l2_cap = v4l2_query(*fd, 1); |
| #else |
| if (!dev || !settings || !fd) {} |
| return; |
| #endif |
| } |
| |
| static double dval = 0.05; |
| |
| static int v4l1_dpct(int old, int d) { |
| int newval, max = V4L1_MAX; |
| |
| /* -1 and 1 are special cases for "small increments" */ |
| if (d == -1) { |
| newval = old - (int) (dval * max); |
| } else if (d == 1) { |
| newval = old + (int) (dval * max); |
| } else { |
| newval = (d * max)/100; |
| } |
| if (newval < 0) { |
| newval = 0; |
| } |
| if (newval > max) { |
| newval = max; |
| } |
| return newval; |
| } |
| |
| static void v4l_requery(void) { |
| if (raw_fb_fd < 0) { |
| return; |
| } |
| v4l1_cap = v4l1_query(raw_fb_fd, 1); |
| v4l2_cap = v4l2_query(raw_fb_fd, 1); |
| } |
| |
| static void v4l_br(int b) { |
| #ifdef V4L_OK |
| int old = v4l1_picture.brightness; |
| |
| v4l1_picture.brightness = v4l1_dpct(old, b); |
| ioctl(raw_fb_fd, VIDIOCSPICT, &v4l1_picture); |
| v4l_requery(); |
| #else |
| if (!b) {} |
| #endif |
| } |
| |
| static void v4l_hu(int b) { |
| #ifdef V4L_OK |
| int old = v4l1_picture.hue; |
| |
| v4l1_picture.hue = v4l1_dpct(old, b); |
| ioctl(raw_fb_fd, VIDIOCSPICT, &v4l1_picture); |
| v4l_requery(); |
| #else |
| if (!b) {} |
| #endif |
| } |
| |
| static void v4l_co(int b) { |
| #ifdef V4L_OK |
| int old = v4l1_picture.colour; |
| |
| v4l1_picture.colour = v4l1_dpct(old, b); |
| ioctl(raw_fb_fd, VIDIOCSPICT, &v4l1_picture); |
| v4l_requery(); |
| #else |
| if (!b) {} |
| #endif |
| } |
| |
| static void v4l_cn(int b) { |
| #ifdef V4L_OK |
| int old = v4l1_picture.contrast; |
| |
| v4l1_picture.contrast = v4l1_dpct(old, b); |
| ioctl(raw_fb_fd, VIDIOCSPICT, &v4l1_picture); |
| v4l_requery(); |
| #else |
| if (!b) {} |
| #endif |
| } |
| |
| static void v4l_sz(int b) { |
| #ifdef V4L_OK |
| int w_old = v4l1_window.width; |
| int h_old = v4l1_window.height; |
| int w, h; |
| |
| if (w_old == 0) { |
| w_old = 160; |
| } |
| if (h_old == 0) { |
| h_old = 120; |
| } |
| |
| if (b == 1) { |
| w = w_old + (int) (0.15 * w_old); |
| h = h_old + (int) (0.15 * h_old); |
| } else if (b == -1) { |
| w = w_old - (int) (0.15 * w_old); |
| h = h_old - (int) (0.15 * h_old); |
| } else { |
| return; |
| } |
| |
| if (! v4l1_resize(raw_fb_fd, w, h)) { |
| return; |
| } |
| |
| v4l_requery(); |
| |
| push_black_screen(4); |
| |
| ignore_all = 1; |
| do_new_fb(1); |
| ignore_all = 0; |
| #else |
| if (!b) {} |
| #endif |
| } |
| |
| static void v4l_sta(int sta) { |
| #ifdef V4L_OK |
| unsigned long freq = 0; |
| int cur = lookup_station(last_freq); |
| |
| if (! last_freq) { |
| if (sta == 0 || sta == -1) { |
| sta = 11; |
| } |
| } |
| |
| if (sta == -1) { |
| while (cur > 0) { |
| freq = lookup_freq(--cur); |
| if (freq) { |
| break; |
| } |
| } |
| } else if (sta == 0) { |
| while (cur < CHANNEL_MAX - 1) { |
| freq = lookup_freq(++cur); |
| if (freq) { |
| break; |
| } |
| } |
| } else { |
| freq = lookup_freq(sta); |
| cur = sta; |
| } |
| fprintf(stderr, "to station %d / %d\n", cur, (int) freq); |
| v4l1_setfreq(raw_fb_fd, freq, 0); |
| #else |
| if (!sta) {} |
| #endif |
| } |
| |
| static void v4l_inp(int inp) { |
| #ifdef V4L_OK |
| int next = -1; |
| if (inp == -1) { |
| inp = last_channel + 1; |
| if (inp >= v4l1_capability.channels) { |
| inp = 0; |
| } |
| next = inp; |
| } else if (inp == -2) { |
| inp = last_channel - 1; |
| if (inp < 0) { |
| inp = v4l1_capability.channels - 1; |
| } |
| next = inp; |
| } else { |
| next = inp; |
| } |
| v4l1_set_input(raw_fb_fd, next); |
| #else |
| if (!inp) {} |
| #endif |
| } |
| |
| static void v4l_fmt(char *fmt) { |
| if (v4l1_setfmt(raw_fb_fd, fmt)) { |
| v4l_requery(); |
| |
| ignore_all = 1; |
| do_new_fb(1); |
| ignore_all = 0; |
| } |
| } |
| |
| void v4l_key_command(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { |
| allowed_input_t input; |
| |
| if (raw_fb_fd < 0) { |
| return; |
| } |
| if (! down) { |
| return; |
| } |
| if (view_only) { |
| return; |
| } |
| get_allowed_input(client, &input); |
| if (! input.keystroke) { |
| return; |
| } |
| |
| if (keysym == XK_b) { |
| v4l_br(-1); |
| } else if (keysym == XK_B) { |
| v4l_br(+1); |
| } else if (keysym == XK_h) { |
| v4l_hu(-1); |
| } else if (keysym == XK_H) { |
| v4l_hu(+1); |
| } else if (keysym == XK_c) { |
| v4l_co(-1); |
| } else if (keysym == XK_C) { |
| v4l_co(+1); |
| } else if (keysym == XK_n) { |
| v4l_cn(-1); |
| } else if (keysym == XK_N) { |
| v4l_cn(+1); |
| } else if (keysym == XK_s) { |
| v4l_sz(-1); |
| } else if (keysym == XK_S) { |
| v4l_sz(+1); |
| } else if (keysym == XK_i) { |
| v4l_inp(-1); |
| } else if (keysym == XK_I) { |
| v4l_inp(-2); |
| } else if (keysym == XK_Up) { |
| v4l_sta(+0); |
| } else if (keysym == XK_Down) { |
| v4l_sta(-1); |
| } else if (keysym == XK_F1) { |
| v4l_fmt("HI240"); |
| } else if (keysym == XK_F2) { |
| v4l_fmt("RGB565"); |
| } else if (keysym == XK_F3) { |
| v4l_fmt("RGB24"); |
| } else if (keysym == XK_F4) { |
| v4l_fmt("RGB32"); |
| } else if (keysym == XK_F5) { |
| v4l_fmt("RGB555"); |
| } else if (keysym == XK_F6) { |
| v4l_fmt("GREY"); |
| } |
| if (client) {} |
| } |
| |
| |
| void v4l_pointer_command(int mask, int x, int y, rfbClientPtr client) { |
| /* do not forget viewonly perms */ |
| if (mask || x || y || client) {} |
| } |
| |
| static int colon_n(char *line) { |
| char *q; |
| int n; |
| q = strrchr(line, ':'); |
| if (! q) { |
| return 0; |
| } |
| q = lblanks(q+1); |
| if (sscanf(q, "%d", &n) == 1) { |
| return n; |
| } |
| return 0; |
| } |
| |
| static char *colon_str(char *line) { |
| char *q, *p, *t; |
| q = strrchr(line, ':'); |
| if (! q) { |
| return strdup(""); |
| } |
| q = lblanks(q+1); |
| p = strpbrk(q, " \t\n"); |
| if (p) { |
| *p = '\0'; |
| } |
| t = strdup(q); |
| *p = '\n'; |
| return t; |
| } |
| |
| static char *colon_tag(char *line) { |
| char *q, *p, *t; |
| q = strrchr(line, '['); |
| if (! q) { |
| return strdup(""); |
| } |
| q++; |
| p = strrchr(q, ']'); |
| if (! p) { |
| return strdup(""); |
| } |
| *p = '\0'; |
| t = strdup(q); |
| *p = ']'; |
| return t; |
| } |
| |
| static void lookup_rgb(char *fmt, int *bits, int *rev) { |
| int tb, tr; |
| |
| if (v4l2_lu_palette_str(fmt, &tb, &tr)) { |
| *bits = tb; |
| *rev = tr; |
| return; |
| } |
| if (v4l1_lu_palette_str(fmt, &tb, &tr)) { |
| *bits = tb; |
| *rev = tr; |
| return; |
| } |
| } |
| |
| static char *v4l1_lu_palette(unsigned short palette) { |
| switch(palette) { |
| #ifdef V4L_OK |
| case VIDEO_PALETTE_GREY: return "GREY"; |
| case VIDEO_PALETTE_HI240: return "HI240"; |
| case VIDEO_PALETTE_RGB565: return "RGB565"; |
| case VIDEO_PALETTE_RGB24: return "RGB24"; |
| case VIDEO_PALETTE_RGB32: return "RGB32"; |
| case VIDEO_PALETTE_RGB555: return "RGB555"; |
| case VIDEO_PALETTE_YUV422: return "YUV422"; |
| case VIDEO_PALETTE_YUYV: return "YUYV"; |
| case VIDEO_PALETTE_UYVY: return "UYVY"; |
| case VIDEO_PALETTE_YUV420: return "YUV420"; |
| case VIDEO_PALETTE_YUV411: return "YUV411"; |
| case VIDEO_PALETTE_RAW: return "RAW"; |
| case VIDEO_PALETTE_YUV422P: return "YUV422P"; |
| case VIDEO_PALETTE_YUV411P: return "YUV411P"; |
| case VIDEO_PALETTE_YUV420P: return "YUV420P"; |
| case VIDEO_PALETTE_YUV410P: return "YUV410P"; |
| #endif |
| default: return "unknown"; |
| } |
| } |
| |
| static unsigned short v4l1_lu_palette_str(char *name, int *bits, int *rev) { |
| #ifdef V4L_OK |
| *rev = 0; |
| if (!strcmp(name, "RGB555")) { |
| *bits = 16; |
| return VIDEO_PALETTE_RGB555; |
| } else if (!strcmp(name, "RGB565")) { |
| *bits = 16; |
| return VIDEO_PALETTE_RGB565; |
| } else if (!strcmp(name, "RGB24")) { |
| *bits = 24; |
| return VIDEO_PALETTE_RGB24; |
| } else if (!strcmp(name, "RGB32")) { |
| *bits = 32; |
| return VIDEO_PALETTE_RGB32; |
| } else if (!strcmp(name, "HI240")) { |
| *bits = 8; |
| return VIDEO_PALETTE_HI240; |
| } else if (!strcmp(name, "GREY")) { |
| *bits = 8; |
| return VIDEO_PALETTE_GREY; |
| } |
| #else |
| if (!name || !bits || !rev) {} |
| #endif |
| return 0; |
| } |
| |
| static char *v4l2_lu_palette(unsigned int fmt) { |
| switch(fmt) { |
| #if defined(V4L_OK) && HAVE_V4L2 |
| case V4L2_PIX_FMT_RGB332: return "RGB332"; |
| case V4L2_PIX_FMT_RGB555: return "RGB555"; |
| case V4L2_PIX_FMT_RGB565: return "RGB565"; |
| case V4L2_PIX_FMT_RGB555X: return "RGB555X"; |
| case V4L2_PIX_FMT_RGB565X: return "RGB565X"; |
| case V4L2_PIX_FMT_BGR24: return "BGR24"; |
| case V4L2_PIX_FMT_RGB24: return "RGB24"; |
| case V4L2_PIX_FMT_BGR32: return "BGR32"; |
| case V4L2_PIX_FMT_RGB32: return "RGB32"; |
| case V4L2_PIX_FMT_GREY: return "GREY"; |
| case V4L2_PIX_FMT_YVU410: return "YVU410"; |
| case V4L2_PIX_FMT_YVU420: return "YVU420"; |
| case V4L2_PIX_FMT_YUYV: return "YUYV"; |
| case V4L2_PIX_FMT_UYVY: return "UYVY"; |
| case V4L2_PIX_FMT_YUV422P: return "YUV422P"; |
| case V4L2_PIX_FMT_YUV411P: return "YUV411P"; |
| case V4L2_PIX_FMT_Y41P: return "Y41P"; |
| case V4L2_PIX_FMT_NV12: return "NV12"; |
| case V4L2_PIX_FMT_NV21: return "NV21"; |
| case V4L2_PIX_FMT_YUV410: return "YUV410"; |
| case V4L2_PIX_FMT_YUV420: return "YUV420"; |
| case V4L2_PIX_FMT_YYUV: return "YYUV"; |
| case V4L2_PIX_FMT_HI240: return "HI240"; |
| case V4L2_PIX_FMT_MJPEG: return "MJPEG"; |
| case V4L2_PIX_FMT_JPEG: return "JPEG"; |
| case V4L2_PIX_FMT_DV: return "DV"; |
| case V4L2_PIX_FMT_MPEG: return "MPEG"; |
| #endif |
| default: return "unknown"; |
| } |
| } |
| |
| static unsigned int v4l2_lu_palette_str(char *name, int *bits, int *rev) { |
| #if defined(V4L_OK) && HAVE_V4L2 |
| if (!strcmp(name, "RGB1") || !strcmp(name, "RGB332")) { |
| *bits = 8; |
| *rev = 0; |
| return V4L2_PIX_FMT_RGB332; |
| } else if (!strcmp(name, "RGBO") || !strcmp(name, "RGB555")) { |
| *bits = 16; |
| *rev = 0; |
| return V4L2_PIX_FMT_RGB555; |
| } else if (!strcmp(name, "RGBP") || !strcmp(name, "RGB565")) { |
| *bits = 16; |
| *rev = 0; |
| return V4L2_PIX_FMT_RGB565; |
| } else if (!strcmp(name, "RGBQ") || !strcmp(name, "RGB555X")) { |
| *bits = 16; |
| *rev = 1; |
| return V4L2_PIX_FMT_RGB555X; |
| } else if (!strcmp(name, "RGBR") || !strcmp(name, "RGB565X")) { |
| *bits = 16; |
| *rev = 1; |
| return V4L2_PIX_FMT_RGB565X; |
| } else if (!strcmp(name, "BGR3") || !strcmp(name, "BGR24")) { |
| *bits = 24; |
| *rev = 1; |
| return V4L2_PIX_FMT_BGR24; |
| } else if (!strcmp(name, "RGB3") || !strcmp(name, "RGB24")) { |
| *bits = 24; |
| *rev = 0; |
| return V4L2_PIX_FMT_RGB24; |
| } else if (!strcmp(name, "BGR4") || !strcmp(name, "BGR32")) { |
| *bits = 32; |
| *rev = 1; |
| return V4L2_PIX_FMT_BGR32; |
| } else if (!strcmp(name, "RGB4") || !strcmp(name, "RGB32")) { |
| *bits = 32; |
| *rev = 0; |
| return V4L2_PIX_FMT_RGB32; |
| } else if (!strcmp(name, "GREY")) { |
| *bits = 8; |
| *rev = 0; |
| return V4L2_PIX_FMT_GREY; |
| } |
| #else |
| if (!name || !bits || !rev) {} |
| #endif |
| return 0; |
| } |
| |
| static int v4l1_query(int fd, int v) { |
| #ifdef V4L_OK |
| unsigned int i; |
| |
| memset(&v4l1_capability, 0, sizeof(v4l1_capability)); |
| memset(&v4l1_channel, 0, sizeof(v4l1_channel)); |
| memset(&v4l1_tuner, 0, sizeof(v4l1_tuner)); |
| memset(&v4l1_picture, 0, sizeof(v4l1_picture)); |
| memset(&v4l1_window, 0, sizeof(v4l1_window)); |
| |
| if (v) fprintf(stderr, "\nV4L_1 query:\n"); |
| #ifdef VIDIOCGCAP |
| if (ioctl(fd, VIDIOCGCAP, &v4l1_capability) == -1) { |
| perror("ioctl VIDIOCGCAP"); |
| fprintf(stderr, "\n"); |
| return 0; |
| } |
| #else |
| return 0; |
| #endif |
| if (v) fprintf(stderr, "v4l-1 capability:\n"); |
| if (v) fprintf(stderr, " name: %s\n", v4l1_capability.name); |
| if (v) fprintf(stderr, " channels: %d\n", v4l1_capability.channels); |
| if (v) fprintf(stderr, " audios: %d\n", v4l1_capability.audios); |
| if (v) fprintf(stderr, " maxwidth: %d\n", v4l1_capability.maxwidth); |
| if (v) fprintf(stderr, " maxheight: %d\n", v4l1_capability.maxheight); |
| if (v) fprintf(stderr, " minwidth: %d\n", v4l1_capability.minwidth); |
| if (v) fprintf(stderr, " minheight: %d\n", v4l1_capability.minheight); |
| |
| for (i=0; (int) i < v4l1_capability.channels; i++) { |
| char *type = "unknown"; |
| memset(&v4l1_channel, 0, sizeof(v4l1_channel)); |
| v4l1_channel.channel = i; |
| if (ioctl(fd, VIDIOCGCHAN, &v4l1_channel) == -1) { |
| perror("ioctl VIDIOCGCHAN"); |
| continue; |
| } |
| if (v4l1_channel.type == VIDEO_TYPE_TV) { |
| type = "TV"; |
| } else if (v4l1_channel.type == VIDEO_TYPE_CAMERA) { |
| type = "CAMERA"; |
| } |
| if (v) fprintf(stderr, " channel[%d]: %s\ttuners: %d norm: %d type: %d %s\n", |
| i, v4l1_channel.name, v4l1_channel.tuners, v4l1_channel.norm, |
| v4l1_channel.type, type); |
| } |
| |
| memset(&v4l1_tuner, 0, sizeof(v4l1_tuner)); |
| if (ioctl(fd, VIDIOCGTUNER, &v4l1_tuner) != -1) { |
| char *mode = "unknown"; |
| if (v4l1_tuner.mode == VIDEO_MODE_PAL) { |
| mode = "PAL"; |
| } else if (v4l1_tuner.mode == VIDEO_MODE_NTSC) { |
| mode = "NTSC"; |
| } else if (v4l1_tuner.mode == VIDEO_MODE_SECAM) { |
| mode = "SECAM"; |
| } else if (v4l1_tuner.mode == VIDEO_MODE_AUTO) { |
| mode = "AUTO"; |
| } |
| |
| if (v) fprintf(stderr, " tuner[%d]: %s\tflags: 0x%x mode: %s\n", |
| v4l1_tuner.tuner, v4l1_tuner.name, v4l1_tuner.flags, mode); |
| |
| } |
| |
| if (ioctl(fd, VIDIOCGPICT, &v4l1_picture) == -1) { |
| perror("ioctl VIDIOCGCHAN"); |
| return 0; |
| } |
| if (v) fprintf(stderr, "v4l-1 picture:\n"); |
| if (v) fprintf(stderr, " brightness: %d\n", v4l1_picture.brightness); |
| if (v) fprintf(stderr, " hue: %d\n", v4l1_picture.hue); |
| if (v) fprintf(stderr, " colour: %d\n", v4l1_picture.colour); |
| if (v) fprintf(stderr, " contrast: %d\n", v4l1_picture.contrast); |
| if (v) fprintf(stderr, " whiteness: %d\n", v4l1_picture.whiteness); |
| if (v) fprintf(stderr, " depth: %d\n", v4l1_picture.depth); |
| if (v) fprintf(stderr, " palette: %d %s\n", v4l1_picture.palette, |
| v4l1_lu_palette(v4l1_picture.palette)); |
| |
| if (ioctl(fd, VIDIOCGWIN, &v4l1_window) == -1) { |
| perror("ioctl VIDIOCGWIN"); |
| if (v) fprintf(stderr, "\n"); |
| return 0; |
| } |
| if (v) fprintf(stderr, "v4l-1 window:\n"); |
| if (v) fprintf(stderr, " x: %d\n", v4l1_window.x); |
| if (v) fprintf(stderr, " y: %d\n", v4l1_window.y); |
| if (v) fprintf(stderr, " width: %d\n", v4l1_window.width); |
| if (v) fprintf(stderr, " height: %d\n", v4l1_window.height); |
| if (v) fprintf(stderr, " chromakey: %d\n", v4l1_window.chromakey); |
| if (v) fprintf(stderr, "\n"); |
| |
| return 1; |
| #else |
| if (!fd || !v) {} |
| return 0; |
| #endif /* V4L_OK */ |
| |
| } |
| static int v4l2_query(int fd, int v) { |
| #if defined(V4L_OK) && HAVE_V4L2 |
| unsigned int i; |
| |
| memset(&v4l2_capability, 0, sizeof(v4l2_capability)); |
| memset(&v4l2_input, 0, sizeof(v4l2_input)); |
| memset(&v4l2_tuner, 0, sizeof(v4l2_tuner)); |
| memset(&v4l2_fmtdesc, 0, sizeof(v4l2_fmtdesc)); |
| memset(&v4l2_format, 0, sizeof(v4l2_format)); |
| |
| if (v) fprintf(stderr, "\nV4L_2 query:\n"); |
| #ifdef VIDIOC_QUERYCAP |
| if (ioctl(fd, VIDIOC_QUERYCAP, &v4l2_capability) == -1) { |
| perror("ioctl VIDIOC_QUERYCAP"); |
| if (v) fprintf(stderr, "\n"); |
| return 0; |
| } |
| #else |
| return 0; |
| #endif |
| |
| if (v) fprintf(stderr, "v4l-2 capability:\n"); |
| if (v) fprintf(stderr, " driver: %s\n", v4l2_capability.driver); |
| if (v) fprintf(stderr, " card: %s\n", v4l2_capability.card); |
| if (v) fprintf(stderr, " bus_info: %s\n", v4l2_capability.bus_info); |
| if (v) fprintf(stderr, " version: %d\n", v4l2_capability.version); |
| if (v) fprintf(stderr, " capabilities: %u\n", v4l2_capability.capabilities); |
| |
| for (i=0; ; i++) { |
| memset(&v4l2_input, 0, sizeof(v4l2_input)); |
| v4l2_input.index = i; |
| if (ioctl(fd, VIDIOC_ENUMINPUT, &v4l2_input) == -1) { |
| break; |
| } |
| if (v) fprintf(stderr, " input[%d]: %s\ttype: %d tuner: %d\n", |
| i, v4l2_input.name, v4l2_input.type, v4l2_input.tuner); |
| } |
| if (v4l2_capability.capabilities & V4L2_CAP_TUNER) { |
| for (i=0; ; i++) { |
| memset(&v4l2_tuner, 0, sizeof(v4l2_tuner)); |
| v4l2_tuner.index = i; |
| if (ioctl(fd, VIDIOC_G_TUNER, &v4l2_tuner) == -1) { |
| break; |
| } |
| if (v) fprintf(stderr, " tuner[%d]: %s\ttype: %d\n", |
| i, v4l2_tuner.name, v4l2_tuner.type); |
| } |
| } |
| if (v4l2_capability.capabilities & V4L2_CAP_VIDEO_CAPTURE) { |
| for (i=0; ; i++) { |
| memset(&v4l2_fmtdesc, 0, sizeof(v4l2_fmtdesc)); |
| v4l2_fmtdesc.index = i; |
| v4l2_fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| |
| if (ioctl(fd, VIDIOC_ENUM_FMT, &v4l2_fmtdesc) == -1) { |
| break; |
| } |
| if (v) fprintf(stderr, " fmtdesc[%d]: %s\ttype: %d" |
| " pixelformat: %d\n", |
| i, v4l2_fmtdesc.description, v4l2_fmtdesc.type, |
| v4l2_fmtdesc.pixelformat); |
| } |
| v4l2_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| if (ioctl(fd, VIDIOC_G_FMT, &v4l2_format) == -1) { |
| perror("ioctl VIDIOC_G_FMT"); |
| } else { |
| if (v) fprintf(stderr, " width: %d\n", v4l2_format.fmt.pix.width); |
| if (v) fprintf(stderr, " height: %d\n", v4l2_format.fmt.pix.height); |
| if (v) fprintf(stderr, " format: %u %s\n", |
| v4l2_format.fmt.pix.pixelformat, |
| v4l2_lu_palette(v4l2_format.fmt.pix.pixelformat)); |
| } |
| } |
| |
| return 1; |
| #else |
| if (!fd || !v) {} |
| return 0; |
| #endif /* V4L_OK && HAVE_V4L2 */ |
| |
| } |
| |
| static int open_dev(char *dev) { |
| int dfd = -1; |
| if (! dev) { |
| return dfd; |
| } |
| dfd = open(dev, O_RDWR); |
| if (dfd < 0) { |
| rfbLog("failed to rawfb file: %s O_RDWR\n", dev); |
| rfbLogPerror("open"); |
| dfd = open(dev, O_RDONLY); |
| } |
| if (dfd < 0) { |
| rfbLog("failed to rawfb file: %s\n", dev); |
| rfbLog("failed to rawfb file: %s O_RDONLY\n", dev); |
| rfbLogPerror("open"); |
| } |
| return dfd; |
| } |
| |
| static char *guess_via_v4l(char *dev, int *fd) { |
| #ifdef V4L_OK |
| int dfd; |
| |
| if (*fd < 0) { |
| dfd = open_dev(dev); |
| *fd = dfd; |
| } |
| dfd = *fd; |
| if (dfd < 0) { |
| return NULL; |
| } |
| if (v4l1_cap < 0) { |
| v4l1_cap = v4l1_query(dfd, 1); |
| } |
| if (v4l2_cap < 0) { |
| v4l2_cap = v4l2_query(dfd, 1); |
| } |
| |
| if (v4l2_cap) { |
| #if HAVE_V4L2 |
| int g_w = v4l2_format.fmt.pix.width; |
| int g_h = v4l2_format.fmt.pix.height; |
| int g_d = 0, g_rev; |
| |
| if (v4l2_format.fmt.pix.pixelformat) { |
| char *str = v4l2_lu_palette(v4l2_format.fmt.pix.pixelformat); |
| if (strcmp(str, "unknown")) { |
| v4l2_lu_palette_str(str, &g_d, &g_rev); |
| } |
| } |
| |
| if (g_w > 0 && g_h > 0 && g_d > 0) { |
| char *atparms = (char *) malloc(200); |
| char *pal = v4l2_lu_palette(v4l2_format.fmt.pix.pixelformat); |
| sprintf(atparms, "%dx%dx%d", g_w, g_h, g_d); |
| if (strstr(pal, "RGB555")) { |
| strcat(atparms, ":7c00/3e0/1f"); |
| } |
| *fd = dfd; |
| return atparms; |
| } |
| #endif |
| } |
| if (v4l1_cap) { |
| int g_w = v4l1_window.width; |
| int g_h = v4l1_window.height; |
| int g_d = v4l1_picture.depth; |
| int g_rev; |
| if (g_d == 0) { |
| char *str = v4l1_lu_palette(v4l1_picture.palette); |
| if (strcmp(str, "unknown")) { |
| v4l1_lu_palette_str(str, &g_d, &g_rev); |
| } |
| } |
| if (0) fprintf(stderr, "v4l1: %d %d %d\n", g_w, g_h, g_d); |
| if (g_w > 0 && g_h > 0 && g_d > 0) { |
| char *atparms = (char *) malloc(200); |
| char *pal = v4l1_lu_palette(v4l1_picture.palette); |
| fprintf(stderr, "palette: %s\n", pal); |
| sprintf(atparms, "%dx%dx%d", g_w, g_h, g_d); |
| if (strstr(pal, "RGB555")) { |
| strcat(atparms, ":7c00/3e0/1f"); |
| } |
| *fd = dfd; |
| return atparms; |
| } |
| } |
| |
| /* failure */ |
| close(dfd); |
| return NULL; |
| #else |
| if (!dev || !fd) {} |
| return NULL; |
| #endif |
| } |
| |
| static char *guess_via_v4l_info(char *dev, int *fd) { |
| char *atparms, *cmd; |
| char line[1024], tmp[] = "/tmp/x11vnc-tmp.XXXXXX"; |
| FILE *out; |
| int tmp_fd, len, rc, curr = 0; |
| int g_w = 0, g_h = 0, g_b = 0, mask_rev = 0; |
| char *g_fmt = NULL; |
| |
| if (*fd) {} |
| |
| /* v4l-info */ |
| if (no_external_cmds || !cmd_ok("v4l-info")) { |
| rfbLog("guess_via_v4l_info: cannot run external " |
| "command: v4l-info\n"); |
| return NULL; |
| } |
| |
| if (strchr(dev, '\'')) { |
| rfbLog("guess_via_v4l_info: bad dev string: %s\n", dev); |
| return NULL; |
| } |
| |
| tmp_fd = mkstemp(tmp); |
| if (tmp_fd < 0) { |
| return NULL; |
| } |
| |
| len = strlen("v4l-info")+1+1+strlen(dev)+1+1+1+1+strlen(tmp)+1; |
| cmd = (char *) malloc(len); |
| rfbLog("guess_via_v4l_info running: v4l-info '%s'\n", dev); |
| sprintf(cmd, "v4l-info '%s' > %s", dev, tmp); |
| |
| close(tmp_fd); |
| close_exec_fds(); |
| rc = system(cmd); |
| if (rc != 0) { |
| unlink(tmp); |
| return NULL; |
| } |
| |
| out = fopen(tmp, "r"); |
| if (out == NULL) { |
| unlink(tmp); |
| return NULL; |
| } |
| |
| curr = 0; |
| while (fgets(line, 1024, out) != NULL) { |
| char *lb = lblanks(line); |
| if (strstr(line, "video capture") == line) { |
| curr = C_VIDEO_CAPTURE; |
| } else if (strstr(line, "picture") == line) { |
| curr = C_PICTURE; |
| } else if (strstr(line, "window") == line) { |
| curr = C_WINDOW; |
| } |
| |
| if (0) fprintf(stderr, "lb: %s", lb); |
| |
| if (curr == C_VIDEO_CAPTURE) { |
| if (strstr(lb, "pixelformat ") == lb) { |
| fprintf(stderr, "%s", line); |
| } else if (strstr(lb, "fmt.pix.width ") == lb) { |
| if (! g_w) { |
| g_w = colon_n(line); |
| } |
| } else if (strstr(lb, "fmt.pix.height ") == lb) { |
| if (! g_h) { |
| g_h = colon_n(line); |
| } |
| } else if (strstr(lb, "fmt.pix.pixelformat ") == lb) { |
| if (! g_fmt) { |
| g_fmt = colon_tag(line); |
| } |
| } |
| } else if (curr == C_PICTURE) { |
| if (strstr(lb, "depth ") == lb) { |
| if (! g_b) { |
| g_b = colon_n(line); |
| } |
| } else if (strstr(lb, "palette ") == lb) { |
| if (! g_fmt) { |
| g_fmt = colon_str(line); |
| } |
| } |
| } else if (curr == C_WINDOW) { |
| if (strstr(lb, "width ") == lb) { |
| if (! g_w) { |
| g_w = colon_n(line); |
| } |
| } else if (strstr(lb, "height ") == lb) { |
| if (! g_h) { |
| g_h = colon_n(line); |
| } |
| } |
| } |
| } |
| fclose(out); |
| unlink(tmp); |
| |
| if (! g_w) { |
| rfbLog("could not guess device width.\n"); |
| return NULL; |
| } |
| rfbLog("guessed device width: %d\n", g_w); |
| |
| if (! g_h) { |
| rfbLog("could not guess device height.\n"); |
| return NULL; |
| } |
| rfbLog("guessed device height: %d\n", g_h); |
| |
| if (g_fmt) { |
| rfbLog("guessed pixel fmt: %s\n", g_fmt); |
| lookup_rgb(g_fmt, &g_b, &mask_rev); |
| } |
| if (! g_b) { |
| rfbLog("could not guess device bpp.\n"); |
| return NULL; |
| } |
| rfbLog("guessed device bpp: %d\n", g_b); |
| |
| atparms = (char *) malloc(100); |
| sprintf(atparms, "%dx%dx%d", g_w, g_h, g_b); |
| return atparms; |
| } |
| |
| static void parse_str(char *str, char **dev, char **settings, char **atparms) { |
| char *p, *q, *s = NULL; |
| |
| q = strchr(str, '@'); |
| if (q && strlen(q+1) > 0) { |
| /* ends @WxHXB... */ |
| *atparms = strdup(q+1); |
| *q = '\0'; |
| } |
| |
| q = strchr(str, ':'); |
| if (q && strlen(q+1) > 0) { |
| /* ends :br=N,w=N... */ |
| s = strdup(q+1); |
| *settings = s; |
| *q = '\0'; |
| } |
| |
| if (s != NULL) { |
| /* see if fn=filename */ |
| q = strstr(s, "fn="); |
| if (q) { |
| q += strlen("fn="); |
| p = strchr(q, ','); |
| if (p) { |
| *p = '\0'; |
| *dev = strdup(q); |
| *p = ','; |
| } else { |
| *dev = strdup(q); |
| } |
| rfbLog("set video device to: '%s'\n", *dev); |
| } |
| } |
| |
| if (*dev == NULL) { |
| struct stat sbuf; |
| s = (char *) malloc(strlen("/dev/") + strlen(str) + 2); |
| if (strstr(str, "/dev/") == str) { |
| sprintf(s, "%s", str); |
| } else { |
| sprintf(s, "/dev/%s", str); |
| } |
| rfbLog("Checking existence of '%s'\n", s); |
| if (stat(s, &sbuf) != 0) { |
| rfbLogPerror("stat"); |
| strcat(s, "0"); |
| rfbLog("switching to '%s'\n", s); |
| } |
| if (stat(s, &sbuf) != 0) { |
| rfbLogPerror("stat"); |
| rfbLog("You will need to specify the video device more explicity.\n"); |
| } |
| |
| *dev = s; |
| rfbLog("set video device to: '%s'\n", *dev); |
| } |
| } |
| |
| char *v4l_guess(char *str, int *fd) { |
| char *dev = NULL, *settings = NULL, *atparms = NULL; |
| |
| parse_str(str, &dev, &settings, &atparms); |
| |
| init_freqs(); |
| |
| v4l1_cap = -1; |
| v4l2_cap = -1; |
| *fd = -1; |
| |
| if (dev == NULL) { |
| rfbLog("v4l_guess: could not find device in: %s\n", str); |
| return NULL; |
| } |
| |
| if (settings) { |
| apply_settings(dev, settings, fd); |
| } |
| |
| if (atparms) { |
| /* use user's parameters. */ |
| char *t = (char *) malloc(5+strlen(dev)+1+strlen(atparms)+1); |
| sprintf(t, "snap:%s@%s", dev, atparms); |
| return t; |
| } |
| |
| /* try to query the device for parameters. */ |
| atparms = guess_via_v4l(dev, fd); |
| if (atparms == NULL) { |
| /* try again with v4l-info(1) */ |
| atparms = guess_via_v4l_info(dev, fd); |
| } |
| |
| if (atparms == NULL) { |
| /* bad news */ |
| if (*fd >= 0) { |
| close(*fd); |
| } |
| *fd = -1; |
| return NULL; |
| } else { |
| char *t = (char *) malloc(5+strlen(dev)+1+strlen(atparms)+1); |
| sprintf(t, "snap:%s@%s", dev, atparms); |
| return t; |
| } |
| } |
| |
| static unsigned long lookup_freqtab(int sta) { |
| |
| if (sta >= CHANNEL_MAX) { |
| return (unsigned long) sta; |
| } |
| if (sta < 0 || sta >= CHANNEL_MAX) { |
| return 0; |
| } |
| return custom_freq[sta]; |
| } |
| |
| static unsigned long lookup_freq(int sta) { |
| if (freqtab) { |
| return lookup_freqtab(sta); |
| } |
| if (sta >= CHANNEL_MAX) { |
| return (unsigned long) sta; |
| } |
| if (sta < 1 || sta > 125) { |
| return 0; |
| } |
| return ntsc_cable[sta]; |
| } |
| |
| static int lookup_station(unsigned long freq) { |
| int i; |
| if (freqtab) { |
| for (i = 0; i < CHANNEL_MAX; i++) { |
| if (0) fprintf(stderr, "%lu %lu\n", freq, custom_freq[i]); |
| if (freq == custom_freq[i]) { |
| return i; |
| } |
| } |
| } else { |
| for (i = 1; i <= 125; i++) { |
| if (freq == ntsc_cable[i]) { |
| return i; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| static void init_freqtab(char *file) { |
| char *p, *q, *dir, *file2; |
| char line[1024], inc[1024]; |
| char *text, *str; |
| int size = 0, maxn, extra, currn; |
| FILE *in1, *in2; |
| static int v = 1; |
| if (quiet) { |
| v = 0; |
| } |
| |
| /* YUCK */ |
| |
| dir = strdup(file); |
| q = strrchr(dir, '/'); |
| if (q) { |
| *(q+1) = '\0'; |
| } else { |
| free(dir); |
| dir = strdup("./"); |
| } |
| file2 = (char *) malloc(strlen(dir) + 1024 + 1); |
| in1 = fopen(file, "r"); |
| if (in1 == NULL) { |
| rfbLog("error opening freqtab: %s\n", file); |
| clean_up_exit(1); |
| } |
| if (v) fprintf(stderr, "loading frequencies from: %s\n", file); |
| while (fgets(line, 1024, in1) != NULL) { |
| char *lb; |
| char line2[1024]; |
| size += strlen(line); |
| lb = lblanks(line); |
| if (strstr(lb, "#include") == lb && |
| sscanf(lb, "#include %s", inc) == 1) { |
| char *q, *s = inc; |
| if (s[0] == '"') { |
| s++; |
| } |
| q = strrchr(s, '"'); |
| if (q) { |
| *q = '\0'; |
| } |
| sprintf(file2, "%s%s", dir, s); |
| in2 = fopen(file2, "r"); |
| if (in2 == NULL) { |
| rfbLog("error opening freqtab include: %s %s\n", line, file2); |
| clean_up_exit(1); |
| } |
| if (v) fprintf(stderr, "loading frequencies from: %s\n", file2); |
| while (fgets(line2, 1024, in2) != NULL) { |
| size += strlen(line2); |
| } |
| fclose(in2); |
| } |
| } |
| fclose(in1); |
| |
| size = 4*(size + 10000); |
| |
| text = (char *) malloc(size); |
| |
| text[0] = '\0'; |
| |
| in1 = fopen(file, "r"); |
| if (in1 == NULL) { |
| rfbLog("error opening freqtab: %s\n", file); |
| clean_up_exit(1); |
| } |
| while (fgets(line, 1024, in1) != NULL) { |
| char *lb; |
| char line2[1024]; |
| lb = lblanks(line); |
| if (lb[0] == '[') { |
| strcat(text, lb); |
| } else if (strstr(lb, "freq")) { |
| strcat(text, lb); |
| } else if (strstr(lb, "#include") == lb && |
| sscanf(lb, "#include %s", inc) == 1) { |
| char *lb2; |
| char *q, *s = inc; |
| if (s[0] == '"') { |
| s++; |
| } |
| q = strrchr(s, '"'); |
| if (q) { |
| *q = '\0'; |
| } |
| sprintf(file2, "%s%s", dir, s); |
| in2 = fopen(file2, "r"); |
| if (in2 == NULL) { |
| rfbLog("error opening freqtab include: %s %s\n", line, file2); |
| clean_up_exit(1); |
| } |
| while (fgets(line2, 1024, in2) != NULL) { |
| lb2 = lblanks(line2); |
| if (lb2[0] == '[') { |
| strcat(text, lb2); |
| } else if (strstr(lb2, "freq")) { |
| strcat(text, lb2); |
| } |
| if ((int) strlen(text) > size/2) { |
| break; |
| } |
| } |
| fclose(in2); |
| } |
| if ((int) strlen(text) > size/2) { |
| break; |
| } |
| } |
| fclose(in1); |
| |
| if (0) fprintf(stderr, "%s", text); |
| |
| str = strdup(text); |
| p = strtok(str, "\n"); |
| maxn = -1; |
| extra = 0; |
| while (p) { |
| if (p[0] == '[') { |
| int ok = 1; |
| q = p+1; |
| while (*q) { |
| if (*q == ']') { |
| break; |
| } |
| if (! isdigit((unsigned char) (*q))) { |
| if (0) fprintf(stderr, "extra: %s\n", p); |
| extra++; |
| ok = 0; |
| break; |
| } |
| q++; |
| } |
| if (ok) { |
| int n; |
| if (sscanf(p, "[%d]", &n) == 1) { |
| if (n > maxn) { |
| maxn = n; |
| } |
| if (0) fprintf(stderr, "maxn: %d %d\n", maxn, n); |
| } |
| } |
| |
| } |
| p = strtok(NULL, "\n"); |
| } |
| free(str); |
| |
| str = strdup(text); |
| p = strtok(str, "\n"); |
| extra = 0; |
| currn = 0; |
| if (v) fprintf(stderr, "\nname\tstation\tfreq (KHz)\n"); |
| while (p) { |
| if (p[0] == '[') { |
| int ok = 1; |
| strncpy(line, p, 100); |
| q = p+1; |
| while (*q) { |
| if (*q == ']') { |
| break; |
| } |
| if (! isdigit((unsigned char) (*q))) { |
| extra++; |
| currn = maxn + extra; |
| ok = 0; |
| break; |
| } |
| q++; |
| } |
| if (ok) { |
| int n; |
| if (sscanf(p, "[%d]", &n) == 1) { |
| currn = n; |
| } |
| } |
| } |
| if (strstr(p, "freq") && (q = strchr(p, '=')) != NULL) { |
| int n; |
| q = lblanks(q+1); |
| if (sscanf(q, "%d", &n) == 1) { |
| if (currn >= 0 && currn < CHANNEL_MAX) { |
| if (v) fprintf(stderr, "%s\t%d\t%d\n", line, currn, n); |
| custom_freq[currn] = (unsigned long) n; |
| if (last_freq == 0) { |
| last_freq = custom_freq[currn]; |
| } |
| } |
| } |
| } |
| p = strtok(NULL, "\n"); |
| } |
| if (v) fprintf(stderr, "\n"); |
| v = 0; |
| free(str); |
| free(text); |
| free(dir); |
| free(file2); |
| } |
| |
| static void init_freqs(void) { |
| int i; |
| for (i=0; i<CHANNEL_MAX; i++) { |
| ntsc_cable[i] = 0; |
| custom_freq[i] = 0; |
| } |
| |
| init_ntsc_cable(); |
| last_freq = ntsc_cable[1]; |
| |
| if (freqtab) { |
| init_freqtab(freqtab); |
| } |
| } |
| |
| static void init_ntsc_cable(void) { |
| ntsc_cable[1] = 73250; |
| ntsc_cable[2] = 55250; |
| ntsc_cable[3] = 61250; |
| ntsc_cable[4] = 67250; |
| ntsc_cable[5] = 77250; |
| ntsc_cable[6] = 83250; |
| ntsc_cable[7] = 175250; |
| ntsc_cable[8] = 181250; |
| ntsc_cable[9] = 187250; |
| ntsc_cable[10] = 193250; |
| ntsc_cable[11] = 199250; |
| ntsc_cable[12] = 205250; |
| ntsc_cable[13] = 211250; |
| ntsc_cable[14] = 121250; |
| ntsc_cable[15] = 127250; |
| ntsc_cable[16] = 133250; |
| ntsc_cable[17] = 139250; |
| ntsc_cable[18] = 145250; |
| ntsc_cable[19] = 151250; |
| ntsc_cable[20] = 157250; |
| ntsc_cable[21] = 163250; |
| ntsc_cable[22] = 169250; |
| ntsc_cable[23] = 217250; |
| ntsc_cable[24] = 223250; |
| ntsc_cable[25] = 229250; |
| ntsc_cable[26] = 235250; |
| ntsc_cable[27] = 241250; |
| ntsc_cable[28] = 247250; |
| ntsc_cable[29] = 253250; |
| ntsc_cable[30] = 259250; |
| ntsc_cable[31] = 265250; |
| ntsc_cable[32] = 271250; |
| ntsc_cable[33] = 277250; |
| ntsc_cable[34] = 283250; |
| ntsc_cable[35] = 289250; |
| ntsc_cable[36] = 295250; |
| ntsc_cable[37] = 301250; |
| ntsc_cable[38] = 307250; |
| ntsc_cable[39] = 313250; |
| ntsc_cable[40] = 319250; |
| ntsc_cable[41] = 325250; |
| ntsc_cable[42] = 331250; |
| ntsc_cable[43] = 337250; |
| ntsc_cable[44] = 343250; |
| ntsc_cable[45] = 349250; |
| ntsc_cable[46] = 355250; |
| ntsc_cable[47] = 361250; |
| ntsc_cable[48] = 367250; |
| ntsc_cable[49] = 373250; |
| ntsc_cable[50] = 379250; |
| ntsc_cable[51] = 385250; |
| ntsc_cable[52] = 391250; |
| ntsc_cable[53] = 397250; |
| ntsc_cable[54] = 403250; |
| ntsc_cable[55] = 409250; |
| ntsc_cable[56] = 415250; |
| ntsc_cable[57] = 421250; |
| ntsc_cable[58] = 427250; |
| ntsc_cable[59] = 433250; |
| ntsc_cable[60] = 439250; |
| ntsc_cable[61] = 445250; |
| ntsc_cable[62] = 451250; |
| ntsc_cable[63] = 457250; |
| ntsc_cable[64] = 463250; |
| ntsc_cable[65] = 469250; |
| ntsc_cable[66] = 475250; |
| ntsc_cable[67] = 481250; |
| ntsc_cable[68] = 487250; |
| ntsc_cable[69] = 493250; |
| ntsc_cable[70] = 499250; |
| ntsc_cable[71] = 505250; |
| ntsc_cable[72] = 511250; |
| ntsc_cable[73] = 517250; |
| ntsc_cable[74] = 523250; |
| ntsc_cable[75] = 529250; |
| ntsc_cable[76] = 535250; |
| ntsc_cable[77] = 541250; |
| ntsc_cable[78] = 547250; |
| ntsc_cable[79] = 553250; |
| ntsc_cable[80] = 559250; |
| ntsc_cable[81] = 565250; |
| ntsc_cable[82] = 571250; |
| ntsc_cable[83] = 577250; |
| ntsc_cable[84] = 583250; |
| ntsc_cable[85] = 589250; |
| ntsc_cable[86] = 595250; |
| ntsc_cable[87] = 601250; |
| ntsc_cable[88] = 607250; |
| ntsc_cable[89] = 613250; |
| ntsc_cable[90] = 619250; |
| ntsc_cable[91] = 625250; |
| ntsc_cable[92] = 631250; |
| ntsc_cable[93] = 637250; |
| ntsc_cable[94] = 643250; |
| ntsc_cable[95] = 91250; |
| ntsc_cable[96] = 97250; |
| ntsc_cable[97] = 103250; |
| ntsc_cable[98] = 109250; |
| ntsc_cable[99] = 115250; |
| ntsc_cable[100] = 649250; |
| ntsc_cable[101] = 655250; |
| ntsc_cable[102] = 661250; |
| ntsc_cable[103] = 667250; |
| ntsc_cable[104] = 673250; |
| ntsc_cable[105] = 679250; |
| ntsc_cable[106] = 685250; |
| ntsc_cable[107] = 691250; |
| ntsc_cable[108] = 697250; |
| ntsc_cable[109] = 703250; |
| ntsc_cable[110] = 709250; |
| ntsc_cable[111] = 715250; |
| ntsc_cable[112] = 721250; |
| ntsc_cable[113] = 727250; |
| ntsc_cable[114] = 733250; |
| ntsc_cable[115] = 739250; |
| ntsc_cable[116] = 745250; |
| ntsc_cable[117] = 751250; |
| ntsc_cable[118] = 757250; |
| ntsc_cable[119] = 763250; |
| ntsc_cable[120] = 769250; |
| ntsc_cable[121] = 775250; |
| ntsc_cable[122] = 781250; |
| ntsc_cable[123] = 787250; |
| ntsc_cable[124] = 793250; |
| ntsc_cable[125] = 799250; |
| } |
| |