/* * libzvbi test * * Copyright (C) 2000-2002, 2004 Michael H. Schimek * Copyright (C) 2003 James Mastros * * This program 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. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* $Id: osc.c,v 1.29 2006/10/08 06:19:48 mschimek Exp $ */ #undef NDEBUG #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #ifdef HAVE_GETOPT_LONG #include #endif #include "src/io.h" #include "src/decoder.h" #include "src/misc.h" #include "src/hamm.h" #include "src/io-sim.h" #include "src/raw_decoder.h" /* _vbi_service_table[] */ #ifndef X_DISPLAY_MISSING #include #include #include vbi_capture * cap; vbi_raw_decoder * par; int src_w, src_h; vbi_sliced * sliced; int slines; vbi_bool quit; int do_sim; int ignore_error; int desync; Display * display; int screen; Colormap cmap; Window window; int dst_w, dst_h; GC gc; XEvent event; XImage * ximage; void * ximgdata; unsigned char *raw1, *raw2; int palette[256]; int depth; int draw_row, draw_offset; int draw_count = -1; int cur_x, cur_y; extern void vbi_capture_set_log_fp (vbi_capture * capture, FILE * fp); extern vbi_bool vbi_capture_force_read_mode; #define PIL(day, mon, hour, min) \ (((day) << 15) + ((mon) << 11) + ((hour) << 6) + ((min) << 0)) /* Return value must be free()d by caller! */ static char * decode_ttx(uint8_t *buf, int line) { char *text, *text_start; int packet_address; int magazine, packet; int j; text_start = text = malloc(255); memset(text, 0, 255); packet_address = vbi_unham16p (buf + 0); if (packet_address < 0) return text; /* hamming error */ magazine = packet_address & 7; packet = packet_address >> 3; text += sprintf(text, "pg %x%02d ln %03d >", magazine, packet, line); for (j = 0; j < 42; j++) { char c = _vbi_to_ascii (buf[j]); *text = c; text++; } *text='<'; *text=0; return text_start; } static char * dump_pil(int pil) { int day, mon, hour, min; static char text[255]; memset(text, 0, 255); day = pil >> 15; mon = (pil >> 11) & 0xF; hour = (pil >> 6) & 0x1F; min = pil & 0x3F; if (pil == PIL(0, 15, 31, 63)) sprintf(text, " PDC: Timer-control (no PDC)\n"); else if (pil == PIL(0, 15, 30, 63)) sprintf(text, " PDC: Recording inhibit/terminate\n"); else if (pil == PIL(0, 15, 29, 63)) sprintf(text, " PDC: Interruption\n"); else if (pil == PIL(0, 15, 28, 63)) sprintf(text, " PDC: Continue\n"); else if (pil == PIL(31, 15, 31, 63)) sprintf(text, " PDC: No time\n"); else sprintf(text, " PDC: %05x, 200X-%02d-%02d %02d:%02d\n", pil, mon, day, hour, min); return text; } static char * decode_vps(uint8_t *buf) { char *text, *text_start; static char pr_label[20]; static char label[20]; static int l = 0; int cni, pcs, pty, pil; int c; text_start=text=malloc(255); memset(text, 0, 255); text += sprintf(text, "VPS: "); c = vbi_rev8 (buf[1]); if ((int8_t) c < 0) { label[l] = 0; memcpy(pr_label, label, sizeof(pr_label)); l = 0; } c &= 0x7F; label[l] = _vbi_to_ascii (c); l = (l + 1) % 16; text += sprintf(text, " 3-10: %02x %02x %02x %02x %02x %02x %02x %02x (\"%s\")\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], pr_label); pcs = buf[2] >> 6; cni = + ((buf[10] & 3) << 10) + ((buf[11] & 0xC0) << 2) + ((buf[8] & 0xC0) << 0) + (buf[11] & 0x3F); pil = ((buf[8] & 0x3F) << 14) + (buf[9] << 6) + (buf[10] >> 2); pty = buf[12]; text += sprintf(text, " CNI: %04x PCS: %d PTY: %d ", cni, pcs, pty); text += sprintf(text, " %s", dump_pil(pil)); return(text_start); } /* End from capture.c */ static void draw(unsigned char *raw) { int rem = src_w - draw_offset; char buf[256]; unsigned char *data = raw; int i, v, h0, field, end, line; XTextItem xti; int x; if (draw_count == 0) return; if (draw_count > 0) draw_count--; memcpy(raw2, raw, src_w * src_h); if (depth == 24) { unsigned int *p = ximgdata; for (i = src_w * src_h; i >= 0; i--) *p++ = palette[(int) *data++]; } else { unsigned short *p = ximgdata; // 64 bit safe? for (i = src_w * src_h; i >= 0; i--) *p++ = palette[(int) *data++]; } XPutImage(display, window, gc, ximage, draw_offset, 0, 0, 0, rem, src_h); //XSync(display, False); XSetForeground(display, gc, 0); //XSync(display, False); if (rem < dst_w) { // fprintf(stderr, "%u: %p %u %u %u %u %u %u\n",__LINE__, // display, window, gc, // rem, 0, dst_w, src_h); XFillRectangle(display, window, gc, rem, 0, dst_w, src_h); //XSync(display, False); } if ((v = dst_h - src_h) <= 0) return; XSetForeground(display, gc, 0); //XSync(display, False); // fprintf(stderr, "%u: %p %u %u %u %u %u %u\n",__LINE__, //display, window, gc,0, src_h, dst_w, dst_h); XFillRectangle(display, window, gc, 0, src_h, dst_w, dst_h); //XSync(display, False); XSetForeground(display, gc, ~0); //XSync(display, False); field = (draw_row >= par->count[0]); if (par->start[field] < 0) { xti.nchars = snprintf(buf, 255, "Row %d Line ?", draw_row); line = -1; } else if (field == 0) { line = draw_row + par->start[0]; xti.nchars = snprintf(buf, 255, "Row %d Line %d", draw_row, line); } else { line = draw_row - par->count[0] + par->start[1]; xti.nchars = snprintf(buf, 255, "Row %d Line %d", draw_row, line); } for (i = 0; i < slines; i++) if (sliced[i].line == (unsigned int) line) break; if (i < slines) { int svc_idx=0; while (_vbi_service_table[svc_idx].id !=0 && _vbi_service_table[svc_idx].id != sliced[i].id) svc_idx++; if (_vbi_service_table[svc_idx].id == sliced[i].id) { struct _vbi_service_par service; service = _vbi_service_table[svc_idx]; xti.nchars += snprintf(buf + xti.nchars, 255 - xti.nchars, " %s (%x) +%dns", service.label, service.id, service.offset ); if (service.id & VBI_SLICED_TELETEXT_B) { char *text = decode_ttx(sliced[i].data, sliced[i].line); xti.nchars += snprintf(buf + xti.nchars, 255 - xti.nchars, ": %s", text); free(text); } else if (service.id & VBI_SLICED_VPS) { char *text = decode_vps(sliced[i].data); xti.nchars += snprintf(buf + xti.nchars, 255 - xti.nchars, ": %s", text); free(text); } } else { xti.nchars += snprintf(buf + xti.nchars, 255 - xti.nchars, " %s (%d)", vbi_sliced_name(sliced[i].id) ?: "???", sliced[i].id); } } else { int s = 0, sd = 0; data = raw + draw_row * src_w; for (i = 0; i < src_w; i++) s += data[i]; s /= src_w; for (i = 0; i < src_w; i++) sd += abs(data[i] - s); sd /= src_w; xti.nchars += snprintf(buf + xti.nchars, 255 - xti.nchars, (sd < 5) ? " Blank" : " Unknown signal"); xti.nchars += snprintf(buf + xti.nchars, 255 - xti.nchars, " (%d)", sd); } /* XSetForeground(display, gc, 0x00FFFF00); XFillRectangle(display, window, gc, 0xc0-draw_offset, src_h, 1, dst_h); XFillRectangle(display, window, gc, 0x19b-draw_offset, src_h, 1, dst_h); */ /* 50% grey */ XSetForeground(display, gc, 0xAAAAAAAA); //XSync(display, False); x=draw_offset; while (x 0) draw_row--; goto redraw; case XK_Down: if (draw_row < (src_h - 1)) draw_row++; goto redraw; case XK_Left: if (draw_offset > 0) draw_offset -= 10; goto redraw; case XK_Right: if (draw_offset < (src_w - 10)) draw_offset += 10; goto redraw; } break; } case ConfigureNotify: dst_w = event.xconfigurerequest.width; dst_h = event.xconfigurerequest.height; redraw: if (draw_count == 0) { draw_count = 1; draw(raw2); } break; case MotionNotify: cur_x = event.xmotion.x; cur_y = event.xmotion.y; // printf("Got MotionNotify: (%d, %d)\n", event.xmotion.x, event.xmotion.y); break; case ClientMessage: exit(EXIT_SUCCESS); } } } static void init_window(int ac, char **av, const char *dev_name) { char buf[256]; Atom delete_window_atom; XWindowAttributes wa; int i; ac = ac; av = av; if (!(display = XOpenDisplay(NULL))) { fprintf(stderr, "No display\n"); exit(EXIT_FAILURE); } screen = DefaultScreen(display); cmap = DefaultColormap(display, screen); window = XCreateSimpleWindow(display, RootWindow(display, screen), 0, 0, // x, y dst_w = 768, dst_h = src_h + 110, // w, h 2, // borderwidth 0xffffffff, // fgd 0x00000000); // bgd if (!window) { fprintf(stderr, "No window\n"); exit(EXIT_FAILURE); } XGetWindowAttributes(display, window, &wa); depth = wa.depth; if (depth != 15 && depth != 16 && depth != 24) { fprintf(stderr, "Sorry, cannot run at colour depth %d\n", depth); exit(EXIT_FAILURE); } for (i = 0; i < 256; i++) { switch (depth) { case 15: palette[i] = ((i & 0xF8) << 7) + ((i & 0xF8) << 2) + ((i & 0xF8) >> 3); break; case 16: palette[i] = ((i & 0xF8) << 8) + ((i & 0xFC) << 3) + ((i & 0xF8) >> 3); break; case 24: palette[i] = (i << 16) + (i << 8) + i; break; } } if (depth == 24) { if (!(ximgdata = malloc(src_w * src_h * 4))) { fprintf(stderr, "Virtual memory exhausted\n"); exit(EXIT_FAILURE); } } else { if (!(ximgdata = malloc(src_w * src_h * 2))) { fprintf(stderr, "Virtual memory exhausted\n"); exit(EXIT_FAILURE); } } if (!(raw1 = malloc(src_w * src_h))) { fprintf(stderr, "Virtual memory exhausted\n"); exit(EXIT_FAILURE); } if (!(raw2 = malloc(src_w * src_h))) { fprintf(stderr, "Virtual memory exhausted\n"); exit(EXIT_FAILURE); } ximage = XCreateImage(display, DefaultVisual(display, screen), DefaultDepth(display, screen), ZPixmap, 0, (char *) ximgdata, src_w, src_h, 8, 0); if (!ximage) { fprintf(stderr, "No ximage\n"); exit(EXIT_FAILURE); } delete_window_atom = XInternAtom(display, "WM_DELETE_WINDOW", False); XSelectInput(display, window, PointerMotionMask | KeyPressMask | ExposureMask | StructureNotifyMask); XSetWMProtocols(display, window, &delete_window_atom, 1); snprintf(buf, sizeof(buf) - 1, "%s - [cursor] [g]rab [l]ive", dev_name); XStoreName(display, window, buf); gc = XCreateGC(display, window, 0, NULL); XMapWindow(display, window); XSync(display, False); } static void mainloop(void) { double timestamp; struct timeval tv; tv.tv_sec = 2; tv.tv_usec = 0; assert((sliced = malloc(sizeof(vbi_sliced) * src_h))); for (quit = FALSE; !quit;) { int r; r = vbi_capture_read(cap, raw1, sliced, &slines, ×tamp, &tv); switch (r) { case -1: fprintf(stderr, "VBI read error: %d, %s%s\n", errno, strerror(errno), ignore_error ? " (ignored)" : ""); if (ignore_error) continue; else exit(EXIT_FAILURE); case 0: fprintf(stderr, "VBI read timeout%s\n", ignore_error ? " (ignored)" : ""); if (ignore_error) continue; else exit(EXIT_FAILURE); case 1: break; default: assert(!"reached"); } draw(raw1); /* printf("raw: %f; sliced: %d\n", timestamp, slines); */ xevent(); } } static const char short_options[] = "123cd:enpsv"; #ifdef HAVE_GETOPT_LONG static const struct option long_options[] = { { "desync", no_argument, NULL, 'c' }, { "device", required_argument, NULL, 'd' }, { "ignore-error", no_argument, NULL, 'e' }, { "ntsc", no_argument, NULL, 'n' }, { "pal", no_argument, NULL, 'p' }, { "sim", no_argument, NULL, 's' }, { "v4l", no_argument, NULL, '1' }, { "v4l2-read", no_argument, NULL, '2' }, { "v4l2-mmap", no_argument, NULL, '3' }, { "verbose", no_argument, NULL, 'v' }, { 0, 0, 0, 0 } }; #else #define getopt_long(ac, av, s, l, i) getopt(ac, av, s) #endif int main(int argc, char **argv) { const char *dev_name = "/dev/vbi"; char *errstr; unsigned int services; int scanning = 625; int strict; int verbose = 0; int interface = 0; int c, index; while ((c = getopt_long(argc, argv, short_options, long_options, &index)) != -1) switch (c) { case 0: /* set flag */ break; case '2': /* Preliminary hack for tests. */ vbi_capture_force_read_mode = TRUE; /* fall through */ case '1': case '3': interface = c - '0'; break; case 'c': desync ^= TRUE; break; case 'd': dev_name = strdup (optarg); break; case 'e': ignore_error ^= TRUE; break; case 'n': scanning = 525; break; case 'p': scanning = 625; break; case 's': do_sim ^= TRUE; break; case 'v': ++verbose; break; default: fprintf(stderr, "Unknown option\n"); exit(EXIT_FAILURE); } services = VBI_SLICED_VBI_525 | VBI_SLICED_VBI_625 | VBI_SLICED_TELETEXT_B | VBI_SLICED_CAPTION_525 | VBI_SLICED_CAPTION_625 | VBI_SLICED_VPS | VBI_SLICED_WSS_625 | VBI_SLICED_WSS_CPR1204; strict = 0; if (do_sim) { cap = vbi_capture_sim_new (scanning, &services, /* interlaced */ FALSE, !desync); assert ((par = vbi_capture_parameters(cap))); } else { do { if (1 != interface) { cap = vbi_capture_v4l2k_new (dev_name, /* fd */ -1, /* buffers */ 5, &services, strict, &errstr, /* trace */ !!verbose); if (cap) break; fprintf (stderr, "Cannot capture vbi data " "with v4l2k interface:\n%s\n", errstr); free (errstr); cap = vbi_capture_v4l2_new (dev_name, /* buffers */ 5, &services, strict, &errstr, /* trace */ !!verbose); if (cap) break; fprintf (stderr, "Cannot capture vbi data " "with v4l2 interface:\n%s\n", errstr); free (errstr); } if (interface < 2) { cap = vbi_capture_v4l_new (dev_name, scanning, &services, strict, &errstr, /* trace */ !!verbose); if (cap) break; fprintf (stderr, "Cannot capture vbi data " "with v4l interface:\n%s\n", errstr); free (errstr); } /* BSD interface */ if (1) { cap = vbi_capture_bktr_new (dev_name, scanning, &services, strict, &errstr, /* trace */ !!verbose); if (cap) break; fprintf (stderr, "Cannot capture vbi data " "with bktr interface:\n%s\n", errstr); free (errstr); } exit(EXIT_FAILURE); } while (0); assert ((par = vbi_capture_parameters(cap))); } if (verbose > 1) { vbi_capture_set_log_fp (cap, stderr); } assert (par->sampling_format == VBI_PIXFMT_YUV420); src_w = par->bytes_per_line / 1; src_h = par->count[0] + par->count[1]; init_window(argc, argv, dev_name); mainloop(); if (!do_sim) vbi_capture_delete(cap); exit(EXIT_SUCCESS); } #else /* X_DISPLAY_MISSING */ int main(int argc, char **argv) { printf("Could not find X11 or has been disabled at configuration time\n"); exit(EXIT_FAILURE); } #endif