--- alevt-1.6.1/alevt-cap.c.dvb 2010-02-19 11:28:15.000000000 +0100 +++ alevt-1.6.1/alevt-cap.c 2010-02-19 11:28:15.000000000 +0100 @@ -44,12 +44,21 @@ " -name <filename>\t\tttext-%%s.%%e\n" " -format <fmt[,options]>\tascii\n" " -format help\n" + " -progname name\n" + " -pid pid\n" "\n" " ppp[.ss] stands for a page number and an\n" " optional subpage number (ie 123.4). If\n" " the subpage number is omitted the first\n" " transmitted subpage is captured.\n" "\n" + "\n" + " To use the DVB interface, use\n" + " -vbi /dev/dvb/adapter0/demux0\n" + "\n" + " The -progname or -pid option specify\n" + " the program or teletext PID if using\n" + " the DVB interface.\n" ); exit(exitval); } @@ -130,6 +139,8 @@ { "-format", "-fmt", 1 }, { "-name", "-o", 1 }, { "-timeout", "-t", 1 }, + { "-progname", "-pn", 1 }, + { "-pid", "--pid", 1 }, }; int i; @@ -204,6 +215,8 @@ struct vbi *vbi; struct req *req; struct dl_head reqs[2]; // simple linear lists of requests & captures + char *progname = NULL; + int txtpid = -1; struct stat statbuf; if (stat(vbi_name, &statbuf) == -1) @@ -270,6 +283,12 @@ if (timeout < 1 || timeout > 999999) fatal("bad timeout value", timeout); break; + case 13: // progname + progname = arg; + break; + case 14: // pid + txtpid = strtoul(arg, NULL, 0); + break; case -1: // non-option arg if (not fmt) fmt = export_open(out_fmt); @@ -289,7 +308,7 @@ fatal("no pages requested"); // setup device - if (not(vbi = vbi_open(vbi_name, 0, fine_tune, newbttv))) + if (not(vbi = vbi_open(vbi_name, 0, fine_tune, newbttv, progname, txtpid))) fatal("cannot open %s", vbi_name); vbi_add_handler(vbi, event, reqs); // register event handler --- alevt-1.6.1/alevt-date.c.dvb 2010-02-19 11:28:15.000000000 +0100 +++ alevt-1.6.1/alevt-date.c 2010-02-19 11:28:15.000000000 +0100 @@ -137,6 +137,8 @@ { "--help", "-h", 0 }, { "-newbttv", "-new", 0 }, { "-oldbttv", "-old", 0 }, + { "-progname", "-pn", 1 }, + { "-pid", "--pid", 1 }, }; int i; @@ -173,6 +175,8 @@ struct vbi *vbi; int opt, ind; char *arg; + char *progname = NULL; + int txtpid = -1; struct stat statbuf; if (stat(vbi_name, &statbuf) == -1) @@ -221,6 +225,12 @@ case 9: // oldbttv big_buf = 0; break; + case 10: // progname + progname = arg; + break; + case 11: // pid + txtpid = strtoul(arg, NULL, 0); + break; case -1: usage(stderr, 1); break; @@ -234,7 +244,7 @@ alarm(timeout); } - vbi = vbi_open(vbi_name, 0, 1, big_buf); // open device + vbi = vbi_open(vbi_name, 0, 1, big_buf, progname, txtpid); // open device if (not vbi) fatal_ioerror(vbi_name); --- alevt-1.6.1/main.c.dvb 2010-02-19 11:28:15.000000000 +0100 +++ alevt-1.6.1/main.c 2010-02-19 11:28:15.000000000 +0100 @@ -22,7 +22,8 @@ static int fine_tune = 1; // auto = 999; static int erc = 1; static int newbttv = -1; - +static char *progname; +static int txtpid = -1; static void usage(FILE *fp, int exitval) @@ -42,7 +43,9 @@ //" -oldbttv\t\t(for bttv <0.5.20)\n" " -[no]erc\t\t\tenabled\n" " -[no]bell\t\t\tenabled\n" - " -charset latin-1/2/koi8-r\tlatin-1\n" + " -charset latin-1/2\t\tlatin-1\n" + " -progname name\n" + " -pid pid\n" "\n" " Order is important! Each page number\n" " opens a new window with the previously\n" @@ -56,6 +59,13 @@ " The -child option requires a parent\n" " window. So, it must be preceeded by\n" " a parent or another child window.\n" + "\n" + " To use the DVB interface, use\n" + " -vbi /dev/dvb/adapter0/demux0\n" + "\n" + " The -progname or -pid option specify\n" + " the program or teletext PID if using\n" + " the DVB interface.\n" ); exit(exitval); } @@ -86,7 +96,7 @@ start(int argc, char **argv, struct vtwin *parent, int pgno, int subno) { if (vbi == 0) - vbi = vbi_open(vbi_name, cache_open(), fine_tune, newbttv); + vbi = vbi_open(vbi_name, cache_open(), fine_tune, newbttv, progname, txtpid); if (vbi == 0) fatal("cannot open %s", vbi_name); if (vbi->cache) @@ -126,6 +136,8 @@ { "-bell", "-b", 0 }, { "-nobell", "-nb", 0 }, { "-charset", "-latin", 1 }, + { "-progname", "-pn", 1 }, + { "-pid", "--pid", 1 }, }; int i; @@ -244,6 +256,12 @@ case 12: // debug debug++; break; + case 19: // progname + progname = arg; + break; + case 20: // pid + txtpid = strtoul(arg, NULL, 0); + break; case 6: // parent case -1: // non-option arg pgno = arg_pgno(arg, &subno); --- alevt-1.6.1/vbi.c.dvb 2010-02-19 11:28:15.000000000 +0100 +++ alevt-1.6.1/vbi.c 2010-02-19 11:28:15.000000000 +0100 @@ -1,9 +1,12 @@ +#define _GNU_SOURCE #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <stdio.h> +#include <errno.h> #include <sys/ioctl.h> +#include <sys/poll.h> #include "os.h" #include "vt.h" #include "misc.h" @@ -12,6 +15,10 @@ #include "hamm.h" #include "lang.h" + +static int vbi_dvb_open(struct vbi *vbi, const char *vbi_name, const char *progname, int txtpid); +static void dvb_handler(struct vbi *vbi, int fd); + #define FAC (1<<16) // factor for fix-point arithmetic static u8 *rawbuf; // one common buffer for raw vbi data. @@ -580,7 +587,7 @@ struct vbi * -vbi_open(char *vbi_name, struct cache *ca, int fine_tune, int big_buf) +vbi_open(char *vbi_name, struct cache *ca, int fine_tune, int big_buf, const char *progname, int txtpid) { static int inited = 0; struct vbi *vbi; @@ -595,17 +602,29 @@ goto fail1; } - if ((vbi->fd = open(vbi_name, O_RDONLY)) == -1) - { - ioerror(vbi_name); - goto fail2; - } + if (!vbi_dvb_open(vbi, vbi_name, progname, txtpid)) { + vbi->cache = ca; + dl_init(vbi->clients); + vbi->seq = 0; + out_of_sync(vbi); + vbi->ppage = vbi->rpage; + + //vbi_pll_reset(vbi, fine_tune); + fdset_add_fd(fds, vbi->fd, dvb_handler, vbi); + return vbi; + } + if ((vbi->fd = open(vbi_name, O_RDONLY)) == -1) + { + ioerror(vbi_name); + goto fail2; + } + if (big_buf != -1) - error("-oldbttv/-newbttv is obsolete. option ignored."); - + error("-oldbttv/-newbttv is obsolete. option ignored."); + if (setup_dev(vbi) == -1) - goto fail3; + goto fail3; vbi->cache = ca; @@ -663,3 +682,394 @@ vbi->cache->op->reset(vbi->cache); vbi_send(vbi, EV_RESET, 0, 0, 0, 0); } + + + +/* + * Starting from here: DVB + */ + +/* DVB API */ +#include <linux/dvb/dmx.h> +#include <linux/dvb/frontend.h> +/*#include "dvb/sec.h"*/ +#include <linux/dvb/video.h> + +static int dvb_get_table(int fd, u_int16_t pid, u_int8_t tblid, u_int8_t *buf, size_t bufsz) +{ + struct dmx_sct_filter_params sctFilterParams; + struct pollfd pfd; + int r; + + memset(&sctFilterParams, 0, sizeof(sctFilterParams)); + sctFilterParams.pid = pid; + sctFilterParams.timeout = 10000; + sctFilterParams.flags = DMX_ONESHOT | DMX_IMMEDIATE_START | DMX_CHECK_CRC; + sctFilterParams.filter.filter[0] = tblid; + sctFilterParams.filter.mask[0] = 0xff; + if (ioctl(fd, DMX_SET_FILTER, &sctFilterParams)) { + perror("DMX_SET_FILTER"); + return -1; + } + pfd.fd = fd; + pfd.events = POLLIN; + r = poll(&pfd, 1, 10000); + if (r < 0) { + perror("poll"); + goto out; + } + if (r > 0) { + r = read(fd, buf, bufsz); + if (r < 0) { + perror("read"); + goto out; + } + } + out: + ioctl(fd, DMX_STOP, 0); + return r; +} + +static const u_int8_t byterev8[256] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff +}; + +static void dvb_handle_pes_payload(struct vbi *vbi, const u_int8_t *buf, unsigned int len) +{ + unsigned int p, i; + u_int8_t data[42]; + + if (buf[0] < 0x10 || buf[0] > 0x1f) + return; /* no EBU teletext data */ + for (p = 1; p < len; p += /*6 + 40*/ 2 + buf[p + 1]) { +#if 0 + printf("Txt Line:\n" + " data_unit_id 0x%02x\n" + " data_unit_length 0x%02x\n" + " reserved_for_future_use 0x%01x\n" + " field_parity 0x%01x\n" + " line_offset 0x%02x\n" + " framing_code 0x%02x\n" + " magazine_and_packet_addr 0x%04x\n" + " data_block 0x%02x 0x%02x 0x%02x 0x%02x\n", + buf[p], buf[p+1], + buf[p+2] >> 6, + (buf[p+2] >> 5) & 1, + buf[p+2] & 0x1f, + buf[p+3], + (buf[p+4] << 8) | buf[p+5], + buf[p+6], buf[p+7], buf[p+8], buf[p+9]); +#endif + for (i = 0; i < sizeof(data); i++) + data[i] = byterev8[buf[p+4+i]]; + /* + * note: we should probably check for missing lines and then + * call out_of_sync(vbi); and/or vbi_reset(vbi); + */ + vt_line(vbi, data); + } +} + +static unsigned int rawptr; + +static void dvb_handler(struct vbi *vbi, int fd) +{ + /* PES packet start code prefix and stream_id == private_stream_1 */ + static const u_int8_t peshdr[4] = { 0x00, 0x00, 0x01, 0xbd }; + u_int8_t *bp; + int n; + unsigned int p, i, len; + u_int16_t rpid; + u_int32_t crc, crccomp; + + if (rawptr >= (unsigned int)rawbuf_size) + rawptr = 0; + n = read(vbi->fd, rawbuf + rawptr, rawbuf_size - rawptr); + if (n <= 0) + return; + rawptr += n; + if (rawptr < 6) + return; + if (memcmp(rawbuf, peshdr, sizeof(peshdr))) { + bp = memmem(rawbuf, rawptr, peshdr, sizeof(peshdr)); + if (!bp) + return; + rawptr -= (bp - rawbuf); + memmove(rawbuf, bp, rawptr); + if (rawptr < 6) + return; + } + len = (rawbuf[4] << 8) | rawbuf[5]; + if (len < 9) { + rawptr = 0; + return; + } + if (rawptr < len + 6) + return; + p = 9 + rawbuf[8]; +#if 0 + for (i = 0; i < len - p; i++) { + if (!(i & 15)) + printf("\n%04x:", i); + printf(" %02x", rawbuf[p + i]); + } + printf("\n"); +#endif + if (!dl_empty(vbi->clients)) + dvb_handle_pes_payload(vbi, rawbuf + p, len - p); + rawptr -= len; + if (rawptr) + memmove(rawbuf, rawbuf + len, rawptr); +} + +static int vbi_dvb_open(struct vbi *vbi, const char *vbi_name, const char *progname, int txtpid) +{ + struct { + u_int16_t pmtpid; + u_int16_t txtpid; + u_int16_t service_id; + u_int8_t service_type; + char service_provider_name[64]; + char service_name[64]; + u_int8_t txtlang[3]; + u_int8_t txttype; + u_int8_t txtmagazine; + u_int8_t txtpage; + } progtbl[16], *progp; + u_int8_t tbl[4096]; + int r; + unsigned int i, j, k, l, progcnt = 0; + struct dmx_pes_filter_params filterpar; + + /* open DVB demux device */ + if (!vbi_name) + vbi_name = "/dev/dvb/adapter0/demux0"; + if ((vbi->fd = open(vbi_name, O_RDWR)) == -1) { + error("cannot open demux device %s", vbi_name); + return -1; + } + memset(progtbl, 0, sizeof(progtbl)); + if (txtpid >= 0x15 && txtpid < 0x1fff) { + vbi->txtpid = txtpid; + printf("Using command line specified teletext PID 0x%x\n", vbi->txtpid); + goto txtpidfound; + } + /* parse PAT to enumerate services and to find the PMT PIDs */ + r = dvb_get_table(vbi->fd, 0, 0, tbl, sizeof(tbl)); + if (r == -1) + goto outerr; + if (!(tbl[5] & 1)) { + error("PAT not active (current_next_indicator == 0)"); + goto outerr; + } + if (tbl[6] != 0 || tbl[7] != 0) { + error("PAT has multiple sections"); + goto outerr; + } + if (r < 13) { + error("PAT too short\n"); + goto outerr; + } + r -= 13; + for (i = 0; i < (unsigned)r; i += 4) { + if (progcnt >= sizeof(progtbl)/sizeof(progtbl[0])) { + error("Program table overflow"); + goto outerr; + } + progtbl[progcnt].service_id = (tbl[8 + i] << 8) | tbl[9 + i]; + if (!progtbl[progcnt].service_id) /* this is the NIT pointer */ + continue; + progtbl[progcnt].pmtpid = ((tbl[10 + i] << 8) | tbl[11 + i]) & 0x1fff; + progcnt++; + } + /* find the SDT to get the station names */ + r = dvb_get_table(vbi->fd, 0x11, 0x42, tbl, sizeof(tbl)); + if (r == -1) + goto outerr; + if (!(tbl[5] & 1)) { + error("SDT not active (current_next_indicator == 0)"); + goto outerr; + } + if (tbl[6] != 0 || tbl[7] != 0) { + error("SDT has multiple sections"); + goto outerr; + } + if (r < 12) { + error("SDT too short\n"); + goto outerr; + } + i = 11; + while (i < (unsigned)r - 1) { + k = (tbl[i] << 8) | tbl[i+1]; /* service ID */ + progp = NULL; + for (j = 0; j < progcnt; j++) + if (progtbl[j].service_id == k) { + progp = &progtbl[j]; + break; + } + j = i + 5; + i = j + (((tbl[i+3] << 8) | tbl[i+4]) & 0x0fff); + if (!progp) { + error("SDT: service_id 0x%x not in PAT\n", k); + continue; + } + while (j < i) { + switch (tbl[j]) { + case 0x48: /* service descriptor */ + k = j + 4 + tbl[j + 3]; + progp->service_type = tbl[j+2]; + snprintf(progp->service_provider_name, sizeof(progp->service_provider_name), + "%.*s", tbl[j+3], tbl + j + 4); + snprintf(progp->service_name, sizeof(progp->service_name), + "%.*s", tbl[k], tbl + k + 1); + break; + } + j += 2 + tbl[j + 1]; + } + } + /* parse PMT's to find Teletext Services */ + for (l = 0; l < progcnt; l++) { + progtbl[l].txtpid = 0x1fff; + if (progtbl[l].service_type != 0x01 || /* service is not digital TV */ + progtbl[l].pmtpid < 0x15 || /* PMT PID sanity check */ + progtbl[l].pmtpid >= 0x1fff) + continue; + r = dvb_get_table(vbi->fd, progtbl[l].pmtpid, 0x02, tbl, sizeof(tbl)); + if (r == -1) + goto outerr; + if (!(tbl[5] & 1)) { + error("PMT pid 0x%x not active (current_next_indicator == 0)", progtbl[l].pmtpid); + goto outerr; + } + if (tbl[6] != 0 || tbl[7] != 0) { + error("PMT pid 0x%x has multiple sections", progtbl[l].pmtpid); + goto outerr; + } + if (r < 13) { + error("PMT pid 0x%x too short\n", progtbl[l].pmtpid); + goto outerr; + } + i = 12 + (((tbl[10] << 8) | tbl[11]) & 0x0fff); /* skip program info section */ + while (i <= (unsigned)r-6) { + j = i + 5; + i = j + (((tbl[i + 3] << 8) | tbl[i + 4]) & 0x0fff); + if (tbl[j - 5] != 0x06) /* teletext streams have type 0x06 */ + continue; + k = ((tbl[j - 4] << 8) | tbl[j - 3]) & 0x1fff; /* elementary PID - save until we know if it's the teletext PID */ + while (j < i) { + switch (tbl[j]) { + case 0x56: /* EBU teletext descriptor */ + progtbl[l].txtlang[0] = tbl[j + 2]; + progtbl[l].txtlang[1] = tbl[j + 3]; + progtbl[l].txtlang[2] = tbl[j + 4]; + progtbl[l].txttype = tbl[j + 5] >> 3; + progtbl[l].txtmagazine = tbl[j + 5] & 7; + progtbl[l].txtpage = tbl[j + 6]; + progtbl[l].txtpid = k; + break; + } + j += 2 + tbl[j + 1]; + } + } + } + + for (i = 0; i < progcnt; i++) { + printf("Service ID 0x%04x Type 0x%02x Provider Name \"%s\" Name \"%s\"\n" + " PMT PID 0x%04x TXT: PID 0x%04x lang %.3s type 0x%02x magazine %1u page %3u\n", + progtbl[i].service_id, progtbl[i].service_type, progtbl[i].service_provider_name, + progtbl[i].service_name, progtbl[i].pmtpid, progtbl[i].txtpid, progtbl[i].txtlang, + progtbl[i].txttype, progtbl[i].txtmagazine, progtbl[i].txtpage); + } + + progp = NULL; + if (progname) { + j = strlen(progname); + for (i = 0; i < progcnt; i++) + if (!strncmp(progtbl[i].service_name, progname, j) && progtbl[i].txtpid != 0x1fff) { + progp = &progtbl[i]; + break; + } + } + if (progname && !progp) { + j = strlen(progname); + for (i = 0; i < progcnt; i++) + if (!strncasecmp(progtbl[i].service_name, progname, j) && progtbl[i].txtpid != 0x1fff) { + progp = &progtbl[i]; + break; + } + } + if (!progp) { + for (i = 0; i < progcnt; i++) + if (progtbl[i].txtpid != 0x1fff) { + progp = &progtbl[i]; + break; + } + } + + printf("Using: Service ID 0x%04x Type 0x%02x Provider Name \"%s\" Name \"%s\"\n" + " PMT PID 0x%04x TXT: PID 0x%04x lang %.3s type 0x%02x magazine %1u page %3u\n", + progp->service_id, progp->service_type, progp->service_provider_name, + progp->service_name, progp->pmtpid, progp->txtpid, progp->txtlang, + progp->txttype, progp->txtmagazine, progp->txtpage); + + vbi->txtpid = progp->txtpid; + txtpidfound: + rawbuf = malloc(rawbuf_size = 8192); + if (!rawbuf) + goto outerr; + rawptr = 0; +#if 0 + close(vbi->fd); + if ((vbi->fd = open(vbi_name, O_RDWR)) == -1) { + error("cannot open demux device %s", vbi_name); + return -1; + } +#endif + memset(&filterpar, 0, sizeof(filterpar)); + filterpar.pid = vbi->txtpid; + filterpar.input = DMX_IN_FRONTEND; + filterpar.output = DMX_OUT_TAP; + filterpar.pes_type = DMX_PES_OTHER; + filterpar.flags = DMX_IMMEDIATE_START; + if (ioctl(vbi->fd, DMX_SET_PES_FILTER, &filterpar) < 0) { + error("ioctl: DMX_SET_PES_FILTER %s (%u)", strerror(errno), errno); + goto outerr; + } + return 0; + + outerr: + close(vbi->fd); + vbi->fd = -1; + return -1; +} --- alevt-1.6.1/vbi.h.dvb 2000-09-21 20:48:13.000000000 +0200 +++ alevt-1.6.1/vbi.h 2010-02-19 11:28:15.000000000 +0100 @@ -36,6 +36,11 @@ int bpb; // bytes per bit * 2^16 int bp8bl, bp8bh; // bytes per 8-bit low/high int soc, eoc; // start/end of clock run-in + + // DVB stuff + unsigned int txtpid; + + }; struct vbi_client @@ -46,7 +51,7 @@ }; struct vbi *vbi_open(char *vbi_dev_name, struct cache *ca, int fine_tune, - int big_buf); + int big_buf, const char *progname, int txtpid); void vbi_close(struct vbi *vbi); void vbi_reset(struct vbi *vbi); int vbi_add_handler(struct vbi *vbi, void *handler, void *data);