diff options
Diffstat (limited to 'util/alevt/vbi.c')
-rw-r--r-- | util/alevt/vbi.c | 942 |
1 files changed, 942 insertions, 0 deletions
diff --git a/util/alevt/vbi.c b/util/alevt/vbi.c new file mode 100644 index 0000000..6a8a47e --- /dev/null +++ b/util/alevt/vbi.c @@ -0,0 +1,942 @@ +#define _GNU_SOURCE +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <errno.h> +#include <stdarg.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include "os.h" +#include "vt.h" +#include "misc.h" +#include "vbi.h" +#include "fdset.h" +#include "hamm.h" +#include "lang.h" +#include <libzvbi.h> + + +static vbi_capture * pZvbiCapt; +static vbi_raw_decoder * pZvbiRawDec; +static vbi_sliced * pZvbiData; +static vbi_proxy_client * pProxy; + +#define ZVBI_BUFFER_COUNT 10 +#define ZVBI_TRACE 0 + + +static int vbi_dvb_open(struct vbi *vbi, const char *vbi_name, + const char *channel, char *outfile, u_int16_t sid, int ttpid); +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 +static int rawbuf_size; // its current size +u_int16_t sid; +static char *vbi_names[] + = { "/dev/vbi", "/dev/vbi0", "/dev/video0", "/dev/dvb/adapter0/demux0", + NULL }; // default device names if none was given at the command line + + +static void out_of_sync(struct vbi *vbi) +{ + int i; // discard all in progress pages + for (i = 0; i < 8; ++i) + vbi->rpage[i].page->flags &= ~PG_ACTIVE; +} + + +// send an event to all clients +static void vbi_send(struct vbi *vbi, int type, int i1, int i2, int i3, void *p1) +{ + struct vt_event ev[1]; + struct vbi_client *cl, *cln; + ev->resource = vbi; + ev->type = type; + ev->i1 = i1; + ev->i2 = i2; + ev->i3 = i3; + ev->p1 = p1; + for (cl = PTR vbi->clients->first; cln = PTR cl->node->next; cl = cln) + cl->handler(cl->data, ev); +} + + +static void vbi_send_page(struct vbi *vbi, struct raw_page *rvtp, int page) +{ + struct vt_page *cvtp = 0; + + if (rvtp->page->flags & PG_ACTIVE) + { + if (rvtp->page->pgno % 256 != page) + { + rvtp->page->flags &= ~PG_ACTIVE; + enhance(rvtp->enh, rvtp->page); + if (vbi->cache) + cvtp = vbi->cache->op->put(vbi->cache, rvtp->page); + vbi_send(vbi, EV_PAGE, 0, 0, 0, cvtp ?: rvtp->page); + } + } +} + + +static void pll_add(struct vbi *vbi, int n, int err) +{ +} + + +// process one videotext packet +static int vt_line(struct vbi *vbi, u8 *p) +{ + struct vt_page *cvtp; + struct raw_page *rvtp; + int hdr, mag, mag8, pkt, i; + int err = 0; + + hdr = hamm16(p, &err); + if (err & 0xf000) + return -4; + mag = hdr & 7; + mag8 = mag?: 8; + pkt = (hdr >> 3) & 0x1f; + p += 2; + rvtp = vbi->rpage + mag; + cvtp = rvtp->page; + switch (pkt) + { + case 0: + { + int b1, b2, b3, b4; + b1 = hamm16(p, &err); // page number + b2 = hamm16(p+2, &err); // subpage number + flags + b3 = hamm16(p+4, &err); // subpage number + flags + b4 = hamm16(p+6, &err); // language code + more flags + if (vbi->ppage->page->flags & PG_MAGSERIAL) + vbi_send_page(vbi, vbi->ppage, b1); + vbi_send_page(vbi, rvtp, b1); + + if (err & 0xf000) + return 4; + + cvtp->errors = (err >> 8) + chk_parity(p + 8, 32);; + cvtp->pgno = mag8 * 256 + b1; + cvtp->subno = (b2 + b3 * 256) & 0x3f7f; + cvtp->lang = "\0\4\2\6\1\5\3\7"[b4 >> 5] + (latin1==LATIN1 ? 0 : 8); + cvtp->flags = b4 & 0x1f; + cvtp->flags |= b3 & 0xc0; + cvtp->flags |= (b2 & 0x80) >> 2; + cvtp->lines = 1; + cvtp->flof = 0; + vbi->ppage = rvtp; + pll_add(vbi, 1, cvtp->errors); + conv2latin(p + 8, 32, cvtp->lang); + vbi_send(vbi, EV_HEADER, cvtp->pgno, cvtp->subno, cvtp->flags, p); + + if (b1 == 0xff) + return 0; + cvtp->flags |= PG_ACTIVE; + init_enhance(rvtp->enh); + memcpy(cvtp->data[0]+0, p, 40); + memset(cvtp->data[0]+40, ' ', sizeof(cvtp->data)-40); + return 0; + } + + case 1 ... 24: + { + pll_add(vbi, 1, err = chk_parity(p, 40)); + + if (~cvtp->flags & PG_ACTIVE) + return 0; + + cvtp->errors += err; + cvtp->lines |= 1 << pkt; + conv2latin(p, 40, cvtp->lang); + memcpy(cvtp->data[pkt], p, 40); + return 0; + } + case 26: + { + int d, t[13]; + + if (~cvtp->flags & PG_ACTIVE) + return 0; + + d = hamm8(p, &err); + if (err & 0xf000) + return 4; + + for (i = 0; i < 13; ++i) + t[i] = hamm24(p + 1 + 3*i, &err); + if (err & 0xf000) + return 4; + + add_enhance(rvtp->enh, d, t); + return 0; + } + case 27: + { + int b1,b2,b3,x; + if (~cvtp->flags & PG_ACTIVE) + return 0; // -1 flushes all pages. We may never resync again + + b1 = hamm8(p, &err); + b2 = hamm8(p + 37, &err); + if (err & 0xf000) + return 4; + if (b1 != 0 || not(b2 & 8)) + return 0; + + for (i = 0; i < 6; ++i) + { + err = 0; + b1 = hamm16(p+1+6*i, &err); + b2 = hamm16(p+3+6*i, &err); + b3 = hamm16(p+5+6*i, &err); + if (err & 0xf000) + return 1; + x = (b2 >> 7) | ((b3 >> 5) & 0x06); + cvtp->link[i].pgno = ((mag ^ x) ?: 8) * 256 + b1; + cvtp->link[i].subno = (b2 + b3 * 256) & 0x3f7f; + } + cvtp->flof = 1; + return 0; + } + case 30: + { + if (mag8 != 8) + return 0; + p[0] = hamm8(p, &err); // designation code + p[1] = hamm16(p+1, &err); // initial page + p[3] = hamm16(p+3, &err); // initial subpage + mag + p[5] = hamm16(p+5, &err); // initial subpage + mag + if (err & 0xf000) + return 4; + err += chk_parity(p+20, 20); + conv2latin(p+20, 20, 0); + vbi_send(vbi, EV_XPACKET, mag8, pkt, err, p); + return 0; + } + default: + return 0; + } + return 0; +} + + +// called when new vbi data is waiting +static void vbi_handler(struct vbi *vbi, int fd) +{ + double timestamp; + struct timeval timeout; + int lineCount; + int line; + int res; + + timeout.tv_sec = 0; + timeout.tv_usec = 25000; + res = vbi_capture_read_sliced(pZvbiCapt, pZvbiData, &lineCount, ×tamp, + &timeout); + if (res > 0) + { + for (line=0; line < lineCount; line++) + { + if ((pZvbiData[line].id & VBI_SLICED_TELETEXT_B) != 0) + { + vt_line(vbi, pZvbiData[line].data); + } + } + } + else if (res < 0) + { + } +} + + +int vbi_add_handler(struct vbi *vbi, void *handler, void *data) +{ + struct vbi_client *cl; + + if (not(cl = malloc(sizeof(*cl)))) + return -1; + cl->handler = handler; + cl->data = data; + dl_insert_last(vbi->clients, cl->node); + return 0; +} + + +void vbi_del_handler(struct vbi *vbi, void *handler, void *data) +{ + struct vbi_client *cl; + + for (cl = PTR vbi->clients->first; cl->node->next; cl = PTR cl->node->next) + if (cl->handler == handler && cl->data == data) + { + dl_remove(cl->node); + break; + } + return; +} + + +struct vbi * vbi_open(char *vbi_name, struct cache *ca, + const char *channel, char *outfile, u_int16_t sid, int ttpid) +{ + static int inited = 0; + struct vbi *vbi; + char * pErrStr; + int services; + + if (vbi_name == NULL) + { + int i; + char *tried_devices = NULL; + char *old_tried_devices = NULL; + for (i = 0; vbi_names[i] != NULL; i++) + { + vbi_name = vbi_names[i]; + // collect device names for the error message below + if (old_tried_devices) + { + if (asprintf(&tried_devices, "%s, %s", old_tried_devices, vbi_name) < 0) + tried_devices = NULL; + free(old_tried_devices); + } + else if (asprintf(&tried_devices, "%s", vbi_name) < 0) + tried_devices = NULL; + if (tried_devices == NULL) + out_of_mem(-1); + old_tried_devices = tried_devices; + if (access(vbi_name, R_OK) != 0) + continue; + vbi = vbi_open(vbi_name, ca, channel, outfile, sid, ttpid); + if (vbi != NULL) + { + if (tried_devices != NULL) + free(tried_devices); + return vbi; + } + } + + error("could not open any of the standard devices (%s)", tried_devices); + free(tried_devices); + return NULL; + } + + if (not inited) + lang_init(); + inited = 1; + + if (not(vbi = malloc(sizeof(*vbi)))) + { + error("out of memory"); + goto fail1; + } + if (!vbi_dvb_open(vbi, vbi_name, channel, outfile, sid, ttpid)) { + vbi->cache = ca; + dl_init(vbi->clients); + out_of_sync(vbi); + vbi->ppage = vbi->rpage; + fdset_add_fd(fds, vbi->fd, dvb_handler, vbi); + return vbi; + } + + services = VBI_SLICED_TELETEXT_B; + pErrStr = NULL; + vbi->fd = -1; + + pProxy = vbi_proxy_client_create(vbi_name, "alevt", + VBI_PROXY_CLIENT_NO_STATUS_IND, &pErrStr, ZVBI_TRACE); + if (pProxy != NULL) + { + pZvbiCapt = vbi_capture_proxy_new(pProxy, ZVBI_BUFFER_COUNT, 0, + &services, 0, &pErrStr); + if (pZvbiCapt == NULL) + { + vbi_proxy_client_destroy(pProxy); + pProxy = NULL; + } + } + if (pZvbiCapt == NULL) + pZvbiCapt = vbi_capture_v4l2_new(vbi_name, ZVBI_BUFFER_COUNT, + &services, 0, &pErrStr, ZVBI_TRACE); + if (pZvbiCapt == NULL) + pZvbiCapt = vbi_capture_v4l_new(vbi_name, 0, &services, 0, &pErrStr, + ZVBI_TRACE); + + if (pZvbiCapt != NULL) + { + pZvbiRawDec = vbi_capture_parameters(pZvbiCapt); + if ((pZvbiRawDec != NULL) && ((services & VBI_SLICED_TELETEXT_B) != 0)) + { + pZvbiData = malloc((pZvbiRawDec->count[0] + pZvbiRawDec->count[1]) \ + * sizeof(*pZvbiData)); + + vbi->fd = vbi_capture_fd(pZvbiCapt); + } + else + vbi_capture_delete(pZvbiCapt); + } + + if (pErrStr != NULL) + { + fprintf(stderr, "libzvbi: %s\n", pErrStr); + free(pErrStr); + } + + if (vbi->fd == -1) + goto fail2; + vbi->cache = ca; + dl_init(vbi->clients); + out_of_sync(vbi); + vbi->ppage = vbi->rpage; + fdset_add_fd(fds, vbi->fd, vbi_handler, vbi); + return vbi; + +fail3: + close(vbi->fd); +fail2: + free(vbi); +fail1: + return 0; +} + + +void vbi_close(struct vbi *vbi) +{ + fdset_del_fd(fds, vbi->fd); + if (vbi->cache) + vbi->cache->op->close(vbi->cache); + + if (pZvbiData != NULL) + free(pZvbiData); + pZvbiData = NULL; + + if (pZvbiCapt != NULL) + { + vbi_capture_delete(pZvbiCapt); + pZvbiCapt = NULL; + } + if (pProxy != NULL) + { + vbi_proxy_client_destroy(pProxy); + pProxy = NULL; + } + free(vbi); +} + + +struct vt_page * vbi_query_page(struct vbi *vbi, int pgno, int subno) +{ + struct vt_page *vtp = 0; + if (vbi->cache) + vtp = vbi->cache->op->get(vbi->cache, pgno, subno); + if (vtp == 0) + { + return 0; + } + vbi_send(vbi, EV_PAGE, 1, 0, 0, vtp); + return vtp; +} + + +void vbi_reset(struct vbi *vbi) +{ + if (vbi->cache) + vbi->cache->op->reset(vbi->cache); + vbi_send(vbi, EV_RESET, 0, 0, 0, 0); +} + + +/* Starting from here: DVB API */ +#include <linux/dvb/dmx.h> +#include <linux/dvb/frontend.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 *channel, char *outfile, u_int16_t sid, int ttpid) +{ + struct { + u_int16_t pmtpid; + u_int16_t ttpid; + 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]; + u_int8_t * ppname, * psname, pncode, sncode, pnlen, snlen; + int r; + FILE *ofd; + 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 (ttpid >= 0x15 && ttpid < 0x1fff) { + vbi->ttpid = ttpid; + printf("Using command line specified teletext PID 0x%x\n", + vbi->ttpid); + goto ttpidfound; + } + /* 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]; + ppname = tbl+j+4 ; // points to 1st byte of provider_name + pncode = *ppname ; // 1st byte of provider_name + pnlen = tbl[j+3]; // length of provider_name + psname = tbl+k+1 ; // points to 1st byte of service_name + sncode = *psname ; // 1st byte of service_name + snlen = tbl[k] ; // length of service_name + if (pncode >= 0x20) { + pncode = 0 ; // default character set Latin alphabet fig.A.1 + } else { + ppname++ ; pnlen-- ; + // character code from table A.3 1st byte = ctrl-code + } + if (sncode >= 0x20) { + sncode = 0 ; // default character set Latin alphabet fig.A.1 + } else { + psname++ ; snlen-- ; + // character code from table A.3 ; 1st byte = ctrl-code + } + snprintf(progp->service_provider_name, + sizeof(progp->service_provider_name), "%.*s", pnlen, ppname); + snprintf(progp->service_name, + sizeof(progp->service_name), "%.*s", snlen, psname); break; + } + j += 2 + tbl[j + 1]; // next descriptor + } + } + /* parse PMT's to find Teletext Services */ + for (l = 0; l < progcnt; l++) { + progtbl[l].ttpid = 0x1fff; + if (progtbl[l].service_type != 0x01 || /* 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 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].ttpid = k; + break; + } + j += 2 + tbl[j + 1]; + } + } + } + + printf \ + ("sid:pmtpid:ttpid:type:provider:name:language:texttype:magazine:page\n\n"); + for (i = 0; i < progcnt; i++) { + printf("%d:%d:%d:%d:%s:%s:lang=%.3s:type=%d:magazine=%1u:page=%3u\n", + progtbl[i].service_id, progtbl[i].pmtpid, progtbl[i].ttpid, + progtbl[i].service_type, progtbl[i].service_provider_name, + progtbl[i].service_name, progtbl[i].txtlang, progtbl[i].txttype, + progtbl[i].txtmagazine, progtbl[i].txtpage); + } + + if (*outfile) { + ofd = fopen(outfile,"w") ; + if (ofd == NULL) { error("cannot open outfile\n"); goto outerr ; } + for (i = 0; i < progcnt; i++) { + if (progtbl[i].ttpid == 0x1fff) continue ; // service without teletext + fprintf(ofd,"%d:%d:%s:%s:lang=%.3s\n", + progtbl[i].service_id, progtbl[i].ttpid, progtbl[i].service_provider_name, + progtbl[i].service_name, progtbl[i].txtlang); + } + fclose(ofd) ; + } + + progp = NULL; + + if (channel) { + j = strlen(channel); + for (i = 0; i < progcnt; i++) + if (!strncmp(progtbl[i].service_name, channel, j) + && progtbl[i].ttpid != 0x1fff) { progp = &progtbl[i]; + break ; + } + } + + if (channel && !progp) { + j = strlen(channel); + for (i = 0; i < progcnt; i++) + if (!strncasecmp(progtbl[i].service_name, channel, j) + && progtbl[i].ttpid != 0x1fff) { progp = &progtbl[i]; + break ; + } + } + + if (sid) { + for (i = 0; i < progcnt; i++) { + if ((progtbl[i].service_id == sid) && (progtbl[i].ttpid != 0x1fff)) { + progp = &progtbl[i]; break ; } + } + } + + if (!progp) { + for (i = 0; i < progcnt; i++) + if (progtbl[i].ttpid != 0x1fff) { + progp = &progtbl[i]; break ; + } + } + + printf("\nUsing: Service ID = %d ; PMT PID = %d ; TXT PID = %d ;\n" + "Service type = %d ; Provider Name = %s ; Service name = %s ;\n" + "language = %.3s ; Text type = %d ; Text Magazine = %1u ; Text page = %3u\n", + progp->service_id, progp->pmtpid, progp->ttpid, progp->service_type, + progp->service_provider_name, progp->service_name, progp->txtlang, + progp->txttype, progp->txtmagazine, progp->txtpage); + vbi->ttpid = progp->ttpid; + + ttpidfound: + 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->ttpid; + 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; +} + + +struct vbi *open_null_vbi(struct cache *ca) +{ + static int inited = 0; + struct vbi *vbi; + + if (not inited) + lang_init(); + inited = 1; + + vbi = malloc(sizeof(*vbi)); + if (!vbi) + { + error("out of memory"); + goto fail1; + } + + vbi->fd = open("/dev/null", O_RDONLY); + if (vbi->fd == -1) + { + error("cannot open null device"); + goto fail2; + } + + vbi->ttpid = -1; + out_of_sync(vbi); + vbi->ppage = vbi->rpage; + fdset_add_fd(fds, vbi->fd, vbi_handler, vbi); + return vbi; + +fail3: + close(vbi->fd); +fail2: + free(vbi); +fail1: + return 0; +} + + +void send_errmsg(struct vbi *vbi, char *errmsg, ...) +{ + va_list args; + if (errmsg == NULL || *errmsg == '\0') + return; + va_start(args, errmsg); + char *buff = NULL; + if (vasprintf(&buff, errmsg, args) < 0) + buff = NULL; + va_end(args); + if(buff == NULL) + out_of_mem(-1); + vbi_send(vbi, EV_ERR, 0, 0, 0, buff); +} |