📄 scan.c
字号:
/** * Simple MPEG parser to achieve network/service information. * * refered standards: * * ETSI EN 300 468 * ETSI TR 101 211 * ETSI ETR 211 * ITU-T H.222.0 */#include <stdlib.h>#include <stdio.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/ioctl.h>#include <sys/poll.h>#include <unistd.h>#include <fcntl.h>#include <time.h>#include <errno.h>#include <signal.h>#include <assert.h>#include <linux/dvb/frontend.h>#include <linux/dvb/dmx.h>#include "list.h"#include "diseqc.h"#include "dump-zap.h"#include "dump-vdr.h"#include "scan.h"static char demux_devname[80];static struct dvb_frontend_info fe_info = { .type = -1};int verbosity = 2;static int long_timeout;static int current_tp_only;static int get_other_nits;static int vdr_dump_provider;static int ca_select = 1;static int serv_select = 7;static int vdr_version = 2;static enum fe_spectral_inversion spectral_inversion = INVERSION_AUTO;enum table_type { PAT, PMT, SDT, NIT};enum format { OUTPUT_ZAP, OUTPUT_VDR, OUTPUT_PIDS};static enum format output_format = OUTPUT_ZAP;enum polarisation { POLARISATION_HORIZONTAL = 0x00, POLARISATION_VERTICAL = 0x01, POLARISATION_CIRCULAR_LEFT = 0x02, POLARISATION_CIRCULAR_RIGHT = 0x03};enum running_mode { RM_NOT_RUNNING = 0x01, RM_STARTS_SOON = 0x02, RM_PAUSING = 0x03, RM_RUNNING = 0x04};#define AUDIO_CHAN_MAX (32)#define CA_SYSTEM_ID_MAX (16)struct service { struct list_head list; int transport_stream_id; int service_id; char *provider_name; char *service_name; uint16_t pmt_pid; uint16_t pcr_pid; uint16_t video_pid; uint16_t audio_pid[AUDIO_CHAN_MAX]; char audio_lang[AUDIO_CHAN_MAX][4]; int audio_num; uint16_t ca_id[CA_SYSTEM_ID_MAX]; int ca_num; uint16_t teletext_pid; uint16_t subtitling_pid; uint16_t ac3_pid; unsigned int type : 8; unsigned int scrambled : 1; enum running_mode running; void *priv;};struct transponder { struct list_head list; struct list_head services; int network_id; int transport_stream_id; enum fe_type type; struct dvb_frontend_parameters param; enum polarisation polarisation; /* only for DVB-S */ int orbital_pos; /* only for DVB-S */ unsigned int we_flag : 1; /* West/East Flag - only for DVB-S */ unsigned int scan_done : 1; unsigned int last_tuning_failed : 1; unsigned int other_frequency_flag : 1; /* DVB-T */ int n_other_f; uint32_t *other_f; /* DVB-T freqeuency-list descriptor */};struct section_buf { struct list_head list; const char *dmx_devname; unsigned int run_once : 1; unsigned int segmented : 1; /* segmented by table_id_ext */ int fd; int pid; int table_id; int table_id_ext; int section_version_number; uint8_t section_done[32]; int sectionfilter_done; unsigned char buf[1024]; time_t timeout; time_t start_time; time_t running_time; struct section_buf *next_seg; /* this is used to handle * segmented tables (like NIT-other) */};static LIST_HEAD(scanned_transponders);static LIST_HEAD(new_transponders);static struct transponder *current_tp;static void dump_dvb_parameters (FILE *f, struct transponder *p);static void setup_filter (struct section_buf* s, const char *dmx_devname, int pid, int tid, int run_once, int segmented, int timeout);static void add_filter (struct section_buf *s);/* According to the DVB standards, the combination of network_id and * transport_stream_id should be unique, but in real life the satellite * operators and broadcasters don't care enough to coordinate * the numbering. Thus we identify TPs by frequency (scan handles only * one satellite at a time). Further complication: Different NITs on * one satellite sometimes list the same TP with slightly different * frequencies, so we have to search within some bandwidth. */static struct transponder *alloc_transponder(uint32_t frequency){ struct transponder *tp = calloc(1, sizeof(*tp)); tp->param.frequency = frequency; INIT_LIST_HEAD(&tp->list); INIT_LIST_HEAD(&tp->services); list_add_tail(&tp->list, &new_transponders); return tp;}static int is_same_transponder(uint32_t f1, uint32_t f2){ uint32_t diff; if (f1 == f2) return 1; diff = (f1 > f2) ? (f1 - f2) : (f2 - f1); //FIXME: use symbolrate etc. to estimate bandwidth if (diff < 2000) { debug("f1 = %u is same TP as f2 = %u\n", f1, f2); return 1; } return 0;}static struct transponder *find_transponder(uint32_t frequency){ struct list_head *pos; struct transponder *tp; list_for_each(pos, &scanned_transponders) { tp = list_entry(pos, struct transponder, list); if (is_same_transponder(tp->param.frequency, frequency)) return tp; } list_for_each(pos, &new_transponders) { tp = list_entry(pos, struct transponder, list); if (is_same_transponder(tp->param.frequency, frequency)) return tp; } return NULL;}static void copy_transponder(struct transponder *d, struct transponder *s){ d->network_id = s->network_id; d->transport_stream_id = s->transport_stream_id; d->type = s->type; memcpy(&d->param, &s->param, sizeof(d->param)); d->polarisation = s->polarisation; d->orbital_pos = s->orbital_pos; d->we_flag = s->we_flag; d->scan_done = s->scan_done; d->last_tuning_failed = s->last_tuning_failed; d->other_frequency_flag = s->other_frequency_flag; d->n_other_f = s->n_other_f; if (d->n_other_f) { d->other_f = calloc(d->n_other_f, sizeof(uint32_t)); memcpy(d->other_f, s->other_f, d->n_other_f * sizeof(uint32_t)); } else d->other_f = NULL;}/* service_ids are guaranteed to be unique within one TP * (the DVB standards say theay should be unique within one * network, but in real life...) */static struct service *alloc_service(struct transponder *tp, int service_id){ struct service *s = calloc(1, sizeof(*s)); INIT_LIST_HEAD(&s->list); s->service_id = service_id; list_add_tail(&s->list, &tp->services); return s;}static struct service *find_service(struct transponder *tp, int service_id){ struct list_head *pos; struct service *s; list_for_each(pos, &tp->services) { s = list_entry(pos, struct service, list); if (s->service_id == service_id) return s; } return NULL;}static void parse_ca_identifier_descriptor (const unsigned char *buf, struct service *s){ unsigned char len = buf [1]; int i; buf += 2; if (len > sizeof(s->ca_id)) { len = sizeof(s->ca_id); warning("too many CA system ids\n"); } memcpy(s->ca_id, buf, len); for (i = 0; i < len / sizeof(s->ca_id[0]); i++) moreverbose(" CA ID 0x%04x\n", s->ca_id[i]);}static void parse_iso639_language_descriptor (const unsigned char *buf, struct service *s){ unsigned char len = buf [1]; buf += 2; if (len >= 4) { debug(" LANG=%.3s %d\n", buf, buf[3]); memcpy(s->audio_lang[s->audio_num], buf, 3);#if 0 /* seems like the audio_type is wrong all over the place */ //if (buf[3] == 0) -> normal if (buf[3] == 1) s->audio_lang[s->audio_num][3] = '!'; /* clean effects (no language) */ else if (buf[3] == 2) s->audio_lang[s->audio_num][3] = '?'; /* for the hearing impaired */ else if (buf[3] == 3) s->audio_lang[s->audio_num][3] = '+'; /* visually impaired commentary */#endif }}static void parse_network_name_descriptor (const unsigned char *buf, void *dummy){ unsigned char len = buf [1]; info("Network Name '%.*s'\n", len, buf + 2);}static long bcd32_to_cpu (const int b0, const int b1, const int b2, const int b3){ return ((b0 >> 4) & 0x0f) * 10000000 + (b0 & 0x0f) * 1000000 + ((b1 >> 4) & 0x0f) * 100000 + (b1 & 0x0f) * 10000 + ((b2 >> 4) & 0x0f) * 1000 + (b2 & 0x0f) * 100 + ((b3 >> 4) & 0x0f) * 10 + (b3 & 0x0f);}static const fe_code_rate_t fec_tab [8] = { FEC_AUTO, FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, FEC_NONE, FEC_NONE};static const fe_modulation_t qam_tab [6] = { QAM_AUTO, QAM_16, QAM_32, QAM_64, QAM_128, QAM_256};static void parse_cable_delivery_system_descriptor (const unsigned char *buf, struct transponder *t){ t->type = FE_QAM; t->param.frequency = bcd32_to_cpu (buf[2], buf[3], buf[4], buf[5]); t->param.frequency *= 100; t->param.u.qam.fec_inner = fec_tab[buf[12] & 0x07]; t->param.u.qam.symbol_rate = 10 * bcd32_to_cpu (buf[9], buf[10], buf[11], buf[12] & 0xf0); if ((buf[8] & 0x0f) > 5) t->param.u.qam.modulation = QAM_AUTO; else t->param.u.qam.modulation = qam_tab[buf[8] & 0x0f]; t->param.inversion = spectral_inversion; if (verbosity >= 5) { debug("0x%#04x/0x%#04x ", t->network_id, t->transport_stream_id); dump_dvb_parameters (stderr, t); if (t->scan_done) dprintf(5, " (done)"); if (t->last_tuning_failed) dprintf(5, " (tuning failed)"); dprintf(5, "\n"); }}static void parse_satellite_delivery_system_descriptor (const unsigned char *buf, struct transponder *t){ t->type = FE_QPSK; t->param.frequency = 10 * bcd32_to_cpu (buf[2], buf[3], buf[4], buf[5]); t->param.u.qpsk.fec_inner = fec_tab[buf[12] & 0x07]; t->param.u.qpsk.symbol_rate = 10 * bcd32_to_cpu (buf[9], buf[10], buf[11], buf[12] & 0xf0); t->polarisation = (buf[8] >> 5) & 0x03; t->param.inversion = spectral_inversion; t->orbital_pos = bcd32_to_cpu (0x00, 0x00, buf[6], buf[7]); t->we_flag = buf[8] >> 7; if (verbosity >= 5) { debug("0x%#04x/0x%#04x ", t->network_id, t->transport_stream_id); dump_dvb_parameters (stderr, t); if (t->scan_done) dprintf(5, " (done)"); if (t->last_tuning_failed) dprintf(5, " (tuning failed)"); dprintf(5, "\n"); }}static void parse_terrestrial_delivery_system_descriptor (const unsigned char *buf, struct transponder *t){ static const fe_modulation_t m_tab [] = { QPSK, QAM_16, QAM_64, QAM_AUTO }; static const fe_code_rate_t ofec_tab [8] = { FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8 }; struct dvb_ofdm_parameters *o = &t->param.u.ofdm; t->type = FE_OFDM; t->param.frequency = (buf[2] << 24) | (buf[3] << 16); t->param.frequency |= (buf[4] << 8) | buf[5]; t->param.frequency *= 10; t->param.inversion = spectral_inversion; o->bandwidth = BANDWIDTH_8_MHZ + ((buf[6] >> 5) & 0x3); o->constellation = m_tab[(buf[7] >> 6) & 0x3]; o->hierarchy_information = HIERARCHY_NONE + ((buf[7] >> 3) & 0x3); if ((buf[7] & 0x7) > 4) o->code_rate_HP = FEC_AUTO; else o->code_rate_HP = ofec_tab [buf[7] & 0x7]; if (((buf[8] >> 5) & 0x7) > 4) o->code_rate_LP = FEC_AUTO; else o->code_rate_LP = ofec_tab [(buf[8] >> 5) & 0x7]; o->guard_interval = GUARD_INTERVAL_1_32 + ((buf[8] >> 3) & 0x3); o->transmission_mode = (buf[8] & 0x2) ? TRANSMISSION_MODE_8K : TRANSMISSION_MODE_2K; t->other_frequency_flag = (buf[8] & 0x01); if (verbosity >= 5) { debug("0x%#04x/0x%#04x ", t->network_id, t->transport_stream_id); dump_dvb_parameters (stderr, t); if (t->scan_done) dprintf(5, " (done)"); if (t->last_tuning_failed) dprintf(5, " (tuning failed)"); dprintf(5, "\n"); }}static void parse_frequency_list_descriptor (const unsigned char *buf, struct transponder *t){ int n, i; typeof(*t->other_f) f; if (t->other_f) return; n = (buf[1] - 1) / 4; if (n < 1 || (buf[2] & 0x03) != 3) return; t->other_f = calloc(n, sizeof(*t->other_f)); t->n_other_f = n; buf += 3; for (i = 0; i < n; i++) { f = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; t->other_f[i] = f * 10; buf += 4; }}static void parse_service_descriptor (const unsigned char *buf, struct service *s){ unsigned char len; unsigned char *src, *dest; s->type = buf[2]; buf += 3; len = *buf; buf++; if (s->provider_name) free (s->provider_name); s->provider_name = malloc (len + 1); memcpy (s->provider_name, buf, len); s->provider_name[len] = '\0'; /* remove control characters (FIXME: handle short/long name) */ /* FIXME: handle character set correctly (e.g. via iconv) * c.f. EN 300 468 annex A */ for (src = dest = s->provider_name; *src; src++) if (*src >= 0x20 && (*src < 0x80 || *src > 0x9f)) *dest++ = *src; *dest = '\0'; if (!s->provider_name[0]) { /* zap zero length names */ free (s->provider_name); s->provider_name = 0; } if (s->service_name) free (s->service_name); buf += len; len = *buf; buf++; s->service_name = malloc (len + 1); memcpy (s->service_name, buf, len); s->service_name[len] = '\0'; /* remove control characters (FIXME: handle short/long name) */ /* FIXME: handle character set correctly (e.g. via iconv) * c.f. EN 300 468 annex A */ for (src = dest = s->service_name; *src; src++) if (*src >= 0x20 && (*src < 0x80 || *src > 0x9f)) *dest++ = *src; *dest = '\0'; if (!s->service_name[0]) { /* zap zero length names */ free (s->service_name); s->service_name = 0; } info("0x%04x 0x%04x: pmt_pid 0x%04x %s -- %s (%s%s)\n", s->transport_stream_id, s->service_id, s->pmt_pid, s->provider_name, s->service_name, s->running == RM_NOT_RUNNING ? "not running" : s->running == RM_STARTS_SOON ? "starts soon" : s->running == RM_PAUSING ? "pausing" : s->running == RM_RUNNING ? "running" : "???", s->scrambled ? ", scrambled" : "");}static int find_descriptor(uint8_t tag, const unsigned char *buf, int descriptors_loop_len, const unsigned char **desc, int *desc_len){ while (descriptors_loop_len > 0) { unsigned char descriptor_tag = buf[0]; unsigned char descriptor_len = buf[1] + 2; if (!descriptor_len) { warning("descriptor_tag == 0x%02x, len is 0\n", descriptor_tag); break; } if (tag == descriptor_tag) { if (desc) *desc = buf; if (desc_len) *desc_len = descriptor_len; return 1; } buf += descriptor_len; descriptors_loop_len -= descriptor_len; } return 0;}static void parse_descriptors(enum table_type t, const unsigned char *buf, int descriptors_loop_len, void *data){ while (descriptors_loop_len > 0) { unsigned char descriptor_tag = buf[0]; unsigned char descriptor_len = buf[1] + 2; if (!descriptor_len) { warning("descriptor_tag == 0x%02x, len is 0\n", descriptor_tag); break; } switch (descriptor_tag) { case 0x0a: if (t == PMT) parse_iso639_language_descriptor (buf, data); break; case 0x40: if (t == NIT) parse_network_name_descriptor (buf, data); break; case 0x43: if (t == NIT) parse_satellite_delivery_system_descriptor (buf, data); break; case 0x44: if (t == NIT) parse_cable_delivery_system_descriptor (buf, data); break; case 0x48: if (t == SDT) parse_service_descriptor (buf, data); break; case 0x53: if (t == SDT) parse_ca_identifier_descriptor (buf, data); break; case 0x5a: if (t == NIT) parse_terrestrial_delivery_system_descriptor (buf, data); break; case 0x62: if (t == NIT) parse_frequency_list_descriptor (buf, data); break; default: verbosedebug("skip descriptor 0x%02x\n", descriptor_tag); };
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -