diff options
Diffstat (limited to 'siinfo.c')
-rw-r--r-- | siinfo.c | 583 |
1 files changed, 583 insertions, 0 deletions
diff --git a/siinfo.c b/siinfo.c new file mode 100644 index 0000000..f878be7 --- /dev/null +++ b/siinfo.c @@ -0,0 +1,583 @@ +#include <sys/ioctl.h> +#include <stdio.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <time.h> +#include <sys/poll.h> +#include <time.h> +#include <netinet/in.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include "linux/dvb/dmx.h" +#include "siinfo.h" + +#define DESCR_TELETEXT 0x56 +#define DESCR_DVBSUBTITLES 0x59 + + +#define PACK __attribute__ ((__packed__)) + + +// ETSI 300468 6.2.40 +struct ttxt_descr { + uint8_t tag PACK; + uint8_t length PACK; + struct { + uint8_t lang[3] PACK; + uint8_t type_mag PACK; // 5 bits type, 3 bits mag + uint8_t page_no PACK; + } d[1] PACK; +}; + +struct sect_header { + uint8_t table_id PACK; + uint16_t syntax_len PACK; + uint16_t transport_stream_id PACK; + uint8_t ver_cur PACK; + uint8_t section_number PACK; + uint8_t last_section_number PACK; +}; + +// H.222.0 2.4.4.3, Table 2-25 +struct PAT_sect { + uint8_t table_id PACK; + uint16_t syntax_len PACK; + uint16_t transport_stream_id PACK; + uint8_t vers_curr PACK; + uint8_t sect_no PACK; + uint8_t last_sect PACK; + struct { + uint16_t program_number PACK; + uint16_t res_PMTPID PACK; + } d[1] PACK; +}; + +// H.222.0 2.4.4.8, Table 2-28 +struct PMT_stream { + uint8_t stream_type PACK; + uint16_t res_PID PACK; + uint16_t res_ES_info_len PACK; + uint8_t descrs[2] PACK; +}; + +struct PMT_sect { + uint8_t table_id PACK; + uint16_t syntax_len PACK; + uint16_t program_number PACK; + uint8_t vers_curr PACK; + uint8_t sect_no PACK; + uint8_t last_sect PACK; + uint16_t res_pcr PACK; + uint16_t res_program_info_length PACK; + struct PMT_stream s PACK; +}; + + +void dump_hex(char *msg, uint8_t *p, int len) +{ + int i; + printf("%s:", msg); + for(i = 0; i < len; i++) { + printf(" %02x", p[i]); + } + printf("\n"); + + printf("%s:", msg); + for(i = 0; i < len; i++) { + printf(" %c", isprint(p[i]) ? p[i] : '.'); + } + printf("\n"); +} + + +static int SetSectFilt(int fd, uint16_t pid, uint8_t tnr, uint8_t mask) +{ + int ret; + + struct dmx_sct_filter_params p; + + memset(&p, 0, sizeof(p)); + + p.filter.filter[0] = tnr; + p.filter.mask[0] = mask; + p.pid = pid; + p.flags = DMX_IMMEDIATE_START | DMX_CHECK_CRC; + + if ((ret = ioctl(fd, DMX_SET_FILTER, &p)) < 0) + perror("DMX SET FILTER:"); + + return ret; +} + + +/* + * PID - pid to collect on + * table_id - table id to filter out (H.222.0 table 2-26) + * sects - pointer to char *[256], points to array of table sections, initially empty + * numsects - number of sections in table + * returns - 0 if everything ok + */ +#define SECTSIZE 1024 +static int CollectSections(int card_no, int pid, int table_id, char **sects, int *numsects) +{ + int fd; + int ret = -1; + int last_section = 0; + int done = 0; + char *p = NULL; + int n; + char name[100]; + + snprintf(name, sizeof(name), "/dev/dvb/adapter%d/demux0", card_no); + + memset(sects, 0, sizeof(char*) * 256); + + if((fd = open(name, O_RDWR)) < 0){ + perror("DEMUX DEVICE 1: "); + return -1; + } + + if(SetSectFilt(fd, pid, table_id, 0xff)) { + ret = -1; + goto bail; + } + + do { + struct sect_header *h; + int i; + + if(p == NULL) + p = (char *) malloc(SECTSIZE); + + n = read(fd, p, SECTSIZE); + if(n < 8) + continue; + + h = (struct sect_header *) p; + + if(n != ((ntohs(h->syntax_len) & 0xfff) + 3)) { + fprintf(stderr, "bad section length: %x / %x!\n", n, ntohs(h->syntax_len) & 0xfff); + continue; + } + + if(!(h->ver_cur & 0x01)) // if not current + continue; + + last_section = h->last_section_number; + + if(!sects[h->section_number]) { // section_number + sects[h->section_number] = p; + p = NULL; + } + + for(i = 0; i <= last_section; i++) { + if(!sects[i]) { + break; + } + + if(i == last_section) { + *numsects = last_section + 1; + ret = 0; + done = 1; + } + } + + } while (!done); + + bail: + close(fd); + return ret; +} + +static void FreeSects(char **sects) +{ + int i; + + for(i = 0; i < 256; i++) { + if(sects[i]) + free(sects[i]); + } +} + + +static void addpageinfo(struct ttxtinfo *info, uint16_t pid, struct ttxt_descr *descr, int descr_index) +{ + struct ttxtpidinfo *pd; + struct ttxtpageinfo *pa; + + if(!info->p) { + info->p = (struct ttxtpidinfo *) malloc(sizeof(*(info->p))); + memset((char *) info->p, 0, sizeof(*(info->p))); + info->p[0].pid = pid; + info->pidcount++; + } + + if(pid != info->p[info->pidcount - 1].pid) { + info->pidcount++; + info->p = (struct ttxtpidinfo *) realloc(info->p, sizeof(info->p[0]) * info->pidcount); + memset((char *) &(info->p[info->pidcount - 1]), 0, sizeof(info->p[0])); + info->p[info->pidcount - 1].pid = pid; + } + + pd = &(info->p[info->pidcount - 1]); + + pd->pagecount++; + if(!pd->i) { + pd->i = (struct ttxtpageinfo *) malloc(sizeof(pd->i[0])); + } else { + pd->i = (struct ttxtpageinfo *) realloc(pd->i, sizeof(pd->i[0]) * pd->pagecount); + } + memset((char *) &(pd->i[pd->pagecount - 1]), 0, sizeof(pd->i[0])); + + pa = &(pd->i[pd->pagecount - 1]); + + pa->lang[0] = descr->d[descr_index].lang[0]; + pa->lang[1] = descr->d[descr_index].lang[1]; + pa->lang[2] = descr->d[descr_index].lang[2]; + pa->type = descr->d[descr_index].type_mag >> 3; + pa->mag = descr->d[descr_index].type_mag & 0x7; + pa->page = descr->d[descr_index].page_no; +} + + +static int HasVPID(int vpid, char **pmtsects, int numsects) +{ + int found = 0; + int i; + + for(i = 0; i < numsects && !found; i++) { + struct PMT_sect *psect; + char *sp; + char *end; + + psect = (struct PMT_sect *) pmtsects[i]; + end = pmtsects[i] + (psect->syntax_len & 0x3ff) - 7; + // skip extra program info + sp = ((char *) &(psect->s)) + (ntohs(psect->res_program_info_length) & 0xfff); + + while(sp < end && !found) { + struct PMT_stream *s = (struct PMT_stream *) sp; + + if(s->stream_type == 1 || s->stream_type == 2) { // Video stream + if((ntohs(s->res_PID) & 0x1fff) == vpid) { + //printf("Found ttxt by vpid: %d/%d\n", s->stream_type, (ntohs(s->res_PID) & 0x1fff)); + found = 1; + break; + } + } + sp += (ntohs(s->res_ES_info_len) & 0xfff) + 5; + } + } + + return found; +} + +static void ExtractTtxtInfo(char **pmtsects, int numsects, struct ttxtinfo *info, int *foundinfo) +{ + int i; + + for(i = 0; i < numsects; i++) { + struct PMT_sect *psect; + char *sp; + char *end; + + psect = (struct PMT_sect *) pmtsects[i]; + end = pmtsects[i] + (psect->syntax_len & 0x3ff) - 7; + // skip extra program info + sp = ((char *) &(psect->s)) + (ntohs(psect->res_program_info_length) & 0xfff); + + while(sp < end) { + struct PMT_stream *s = (struct PMT_stream *) sp; + + if(s->stream_type == 6) { // PES private data + uint8_t *descr; + + for(descr = s->descrs; descr < s->descrs + (ntohs(s->res_ES_info_len) & 0xfff); descr += descr[1] + 2) { + if(descr[0] == DESCR_TELETEXT) { + struct ttxt_descr *t = (struct ttxt_descr *) descr; + int i, count = t->length / sizeof(t->d[0]); + for(i = 0; i < count; i++) { + addpageinfo(info, ntohs(s->res_PID) & 0x1fff, t, i); + //printf("%c%c%c: type %d, page: %01x%02x\n", t->d[i].lang[0], t->d[i].lang[1], t->d[i].lang[2], t->d[i].type_mag >> 3, t->d[i].type_mag & 0x7, t->d[i].page_no); + *foundinfo = 1; + } + } + } + } + sp += (ntohs(s->res_ES_info_len) & 0xfff) + 5; + } + } +} + +/* + * if vpid != 0, we first search if we can find the vpid in this PMT, + * and if so we go get the ttxt pid + * return <> 0 on error + */ +static int FindTtxtInfoInPMT(int card_no, int pid, int vpid, struct ttxtinfo *info, int *foundinfo) +{ + int ret = -1; + char *pmtsects[256]; + int numsects; + + ret = CollectSections(card_no, pid, 0x02, pmtsects, &numsects); + if(ret) + goto bail; + + if(vpid != 0) { + if(!HasVPID(vpid, pmtsects, numsects)) { + goto bail; + } + } + + ExtractTtxtInfo(pmtsects, numsects, info, foundinfo); + + bail: + FreeSects(pmtsects); + return ret; +} + + +/* + * find the ttxt_info in the PMT via the PAT, try first with the SID + * and if that fails with the VPID + * return <> 0 on error; + */ +int GetTtxtInfo(int card_no, uint16_t sid, uint16_t vpid, struct ttxtinfo *info) +{ + int ret; + char *patsects[256]; + int numsects; + int i; + int j; + uint16_t pmt_pid = 0; + int foundinfo = 0; + + memset((char *) info, 0, sizeof(*info)); + + ret = CollectSections(card_no, 0, 0, patsects, &numsects); + if(ret) + goto bail; + + if(sid != 0) { + int found; + + for(i = 0, found = 0; i < numsects && !found; i++) { + int numdescrs; + struct PAT_sect *s = (struct PAT_sect *) patsects[i]; + + numdescrs = ((ntohs(s->syntax_len) & 0x3FF) - 7) / 4; + + for(j = 0; j < numdescrs && !found; j++) { + uint16_t pno = ntohs(s->d[j].program_number); + + if(pno == 0) + continue; // network pid + + if(pno == sid) { + pmt_pid = ntohs(s->d[j].res_PMTPID) & 0x1fff; + found = 1; + } + } + } + } + + if(pmt_pid != 0) { + ret = FindTtxtInfoInPMT(card_no, pmt_pid, 0, info, &foundinfo); + } else { + // SID not found, try searching VID in all SIDS + if(vpid != 0) { + int done; + for(i = 0, done = 0; i < numsects && !done; i++) { + int numdescrs; + struct PAT_sect *s = (struct PAT_sect *) patsects[i]; + + numdescrs = ((ntohs(s->syntax_len) & 0x3FF) - 7) / 4; + + for(j = 0; j < numdescrs && !done; j++) { + uint16_t pno = ntohs(s->d[j].program_number); + + if(pno == 0) + continue; // network pid + + pmt_pid = ntohs(s->d[j].res_PMTPID) & 0x1fff; + + ret = FindTtxtInfoInPMT(card_no, pmt_pid, vpid, info, &foundinfo); + if(ret) { + done = 1; + } + if(foundinfo) + done = 1; + } + } + } + } + +bail: + FreeSects(patsects); + return ret; +} + + +void FreeTtxtInfoData(struct ttxtinfo *info) +{ + int i; + + for(i = 0; i < info->pidcount; i++) { + free((void *) info->p[i].i); + } + + free((void *) info->p); +} + + +struct ttxtpidinfo *FindSubs(struct ttxtinfo *info, char *lang, int HI, int *pid, int *pageno) +{ + struct ttxtpidinfo *foundNonHIInfo = NULL; + int foundNonHIPid = 0; + int foundNonHIPage = -1; + + for(int i = 0; i < info->pidcount; i++) { + for(int j = 0; j < info->p[i].pagecount; j++) { + if(info->p[i].i[j].lang[0] == lang[0] && + info->p[i].i[j].lang[1] == lang[1] && + info->p[i].i[j].lang[2] == lang[2]) { + if((!HI && info->p[i].i[j].type == TTXT_SUBTITLE_PAGE) || + (HI && info->p[i].i[j].type == TTXT_SUBTITLE_HEARING_IMPAIRED_PAGE)) { + *pid = info->p[i].pid; + *pageno = info->p[i].i[j].mag * 0x100 + info->p[i].i[j].page; + fprintf(stderr, "ttxtsubs: Found selected subtitles on PID %d, page %03x\n", *pid, + *pageno < 0x100 ? *pageno + 0x800 : *pageno); + return &(info->p[i]); + } else if(HI && info->p[i].i[j].type == TTXT_SUBTITLE_PAGE) { + foundNonHIPid = info->p[i].pid; + foundNonHIPage = info->p[i].i[j].mag * 0x100 + info->p[i].i[j].page; + foundNonHIInfo = &(info->p[i]); + } + } + } + } + + if(foundNonHIInfo) { + *pid = foundNonHIPid; + *pageno = foundNonHIPage; + fprintf(stderr, "ttxtsubs: Found non HI subtitles on PID %d, page %03x\n", *pid, + *pageno < 0x100 ? *pageno + 0x800 : *pageno); + return foundNonHIInfo; + } + + if(info->pidcount == 0) + fprintf(stderr, "ttxtsubs: No teletext subtitles on channel.\n"); + else { + fprintf(stderr, "ttxtsubs: Subtitles for language \"%c%c%c\" not found on channel, available languages:\n", lang[0], lang[1], lang[2]); + for(int i = 0; i < info->pidcount; i++) { + for(int j = 0; j < info->p[i].pagecount; j++) { + int page = info->p[i].i[j].mag * 0x100 + info->p[i].i[j].page; + int type = info->p[i].i[j].type; + if(page < 0x100) + page += 0x800; + fprintf(stderr, " %03x: %c%c%c %s\n", page, info->p[i].i[j].lang[0], info->p[i].i[j].lang[1], info->p[i].i[j].lang[2], + type == TTXT_INITIAL_PAGE ? "(Initial Page)" : + type == TTXT_SUBTITLE_PAGE ? "(Subtitles)" : + type == TTXT_ADDITIONAL_INFO_PAGE ? "(Additional Info Page)" : + type == TTXT_PROGRAMME_SCHEDULE_PAGE ? "(Programme Schedule Page)" : + type == TTXT_SUBTITLE_HEARING_IMPAIRED_PAGE ? "(Hearing Impaired)" : "(Unknown type)"); + } + } + } + + *pid = 0; + *pageno = -1; + return NULL; +} + + +#if 0 +int XX_GetTtxtSubtitleInfo(uint16_t pid, int card_no, struct ttxtinfo *info) +{ + int len; + char buf[4096]; + struct PMT_sect *p = (struct PMT_sect *) buf; + char *sp; + + memset((char *) info, 0, sizeof(*info)); + + len = GetSection(pid, 2, 0xFF, card_no, buf, sizeof(buf)); + + //dump_hex("Sect: ", (uint8_t *) buf, len); + + if(len < 0) + return len; + + if(len != ((ntohs(p->syntax_len) & 0xfff) + 3)) { + fprintf(stderr, "bad lengt: %x / %x!\n", len, ntohs(p->syntax_len)); + return -1; + } + + // also skip extra program info + sp = ((char *) &(p->s)) + (ntohs(p->res_program_info_length) & 0xfff); + + while(sp < buf + len - 4) { + struct PMT_stream *s = (struct PMT_stream *) sp; + + //printf("type: %d, pid: %d, len: %d\n", s->stream_type, ntohs(s->res_PID) & 0x1fff, ntohs(s->res_ES_info_len) & 0xfff); + //dump_hex("descr: ", sp, ntohs(s->res_ES_info_len) & 0xfff); + + if(s->stream_type == 6) { // PES private data + uint8_t *descr; + + for(descr = s->descrs; descr < s->descrs + (ntohs(s->res_ES_info_len) & 0xfff); descr += descr[1] + 2) { + //dump_hex("descr: ", descr, descr[1] + 2); + if(descr[0] == DESCR_TELETEXT) { + struct ttxt_descr *t = (struct ttxt_descr *) descr; + int i, count = t->length / sizeof(t->d[0]); + for(i = 0; i < count; i++) { + addpageinfo(info, ntohs(s->res_PID) & 0x1fff, t, i); + //printf("%c%c%c: type %d, page: %01x%02x\n", t->d[i].lang[0], t->d[i].lang[1], t->d[i].lang[2], t->d[i].type_mag >> 3, + //t->d[i].type_mag & 0x7, t->d[i].page_no); + } + } + } + } + + sp += (ntohs(s->res_ES_info_len) & 0xfff) + 5; + } + + return 0; +} +#endif + +#if 0 +//XXX temporary for testing! +int get_subs(char *lang, int sid, int vpid, int cardno, int *pid, int *page) { + struct ttxtinfo info; + int i, j; + int result = -1; + + GetTtxtInfo(cardno, sid, vpid, &info); + + for(i = 0; i < info.pidcount; i++) { + //printf("PID: %d ", info.p[i].pid); + for(j = 0; j < info.p[i].pagecount; j++) { + //printf(" %c%c%c: %01x%02x (%d) ", + // info.p[i].i[j].lang[0], info.p[i].i[j].lang[1], info.p[i].i[j].lang[2], + // info.p[i].i[j].mag, info.p[i].i[j].page, info.p[i].i[j].type); + if(info.p[i].i[j].lang[0] == lang[0] && info.p[i].i[j].lang[1] == lang[1] && + info.p[i].i[j].lang[2] == lang[2] && + (info.p[i].i[j].type == TTXT_SUBTITLE_PAGE || info.p[i].i[j].type == TTXT_SUBTITLE_HEARING_IMPAIRED_PAGE)) { + *pid = info.p[i].pid; + *page = info.p[i].i[j].mag * 0x100 + info.p[i].i[j].page; + result = 0; + } + } + //printf("\n"); + } + + return result; +} +#endif |