diff --git a/stream/dvb_tune.c b/stream/dvb_tune.c index 673109152a..873ac91ec5 100644 --- a/stream/dvb_tune.c +++ b/stream/dvb_tune.c @@ -187,6 +187,74 @@ int dvb_set_ts_filt(dvb_priv_t *priv, int fd, uint16_t pid, dmx_pes_type_t pesty return 1; } +int dvb_get_pmt_pid(dvb_priv_t *priv, int card, int service_id) { + /* We need special filters on the demux, + so open one locally, and close also here. */ + char demux_dev[32]; + sprintf(demux_dev, "/dev/dvb/adapter%d/demux0", card); + + struct dmx_sct_filter_params fparams; + + memset(&fparams, 0, sizeof(fparams)); + fparams.pid = 0; + fparams.filter.filter[0] = 0x00; + fparams.filter.mask[0] = 0xff; + fparams.timeout = 0; + fparams.flags = DMX_IMMEDIATE_START | DMX_CHECK_CRC; + + int pat_fd; + if ((pat_fd = open(demux_dev, O_RDWR)) < 0) { + MP_ERR(priv, "Opening PAT DEMUX failed, error: %d", errno); + return -1; + } + + if (ioctl(pat_fd, DMX_SET_FILTER, &fparams) == -1) { + MP_ERR(priv, "ioctl DMX_SET_FILTER failed, error: %d", errno); + close(pat_fd); + return -1; + } + + int bytes_read; + int section_length; + unsigned char buft[4096]; + unsigned char *bufptr = buft; + + int pmt_pid = -1; + + bool pat_read = false; + while (!pat_read) { + if (((bytes_read = read(pat_fd, bufptr, sizeof(buft))) < 0) && errno == EOVERFLOW) + bytes_read = read(pat_fd, bufptr, sizeof(buft)); + if (bytes_read < 0) { + MP_ERR(priv, "PAT: read_sections: read error: %d", errno); + close(pat_fd); + return -1; + } + + section_length = ((bufptr[1] & 0x0f) << 8) | bufptr[2]; + if (bytes_read != section_length + 3) { + continue; + } + + bufptr += 8; + section_length -= 8; + + /* assumes one section contains the whole pat */ + pat_read = true; + while (section_length > 0) { + int this_service_id = (bufptr[0] << 8) | bufptr[1]; + if (this_service_id == service_id) { + pmt_pid = ((bufptr[2] & 0x1f) << 8) | bufptr[3]; + section_length = 0; + } + bufptr += 4; + section_length -= 4; + } + } + close(pat_fd); + + return pmt_pid; +} int dvb_demux_stop(int fd) { diff --git a/stream/dvb_tune.h b/stream/dvb_tune.h index 2d2f4e6589..50c598f810 100644 --- a/stream/dvb_tune.h +++ b/stream/dvb_tune.h @@ -27,6 +27,7 @@ int dvb_get_tuner_type(int fe_fd, struct mp_log *log); int dvb_open_devices(dvb_priv_t *priv, int n, int demux_cnt); int dvb_fix_demuxes(dvb_priv_t *priv, int cnt); int dvb_set_ts_filt(dvb_priv_t *priv, int fd, uint16_t pid, dmx_pes_type_t pestype); +int dvb_get_pmt_pid(dvb_priv_t *priv, int card, int service_id); int dvb_demux_stop(int fd); int dvb_demux_start(int fd); int dvb_tune(dvb_priv_t *priv, int freq, char pol, int srate, int diseqc, diff --git a/stream/dvbin.h b/stream/dvbin.h index 0b2cc27537..61bc0b1e18 100644 --- a/stream/dvbin.h +++ b/stream/dvbin.h @@ -65,6 +65,7 @@ typedef struct { int tpid, dpid1, dpid2, progid, ca, pids[DMX_FILTER_SIZE], pids_cnt; bool is_dvb_s2; int stream_id; + int service_id; fe_spectral_inversion_t inv; fe_modulation_t mod; fe_transmit_mode_t trans; diff --git a/stream/stream_dvb.c b/stream/stream_dvb.c index 7ebcd8fc1b..6a50bb66f5 100644 --- a/stream/stream_dvb.c +++ b/stream/stream_dvb.c @@ -214,7 +214,7 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log, char *filename, i const char *ter_conf = "%d:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]\n"; const char *atsc_conf = "%d:%255[^:]:%255[^:]:%255[^:]\n"; - const char *vdr_conf = "%d:%255[^:]:%255[^:]:%d:%255[^:]:%255[^:]:%255[^:]:%*255[^:]:%*d:%*d:%*d:%*d\n%n"; + const char *vdr_conf = "%d:%255[^:]:%255[^:]:%d:%255[^:]:%255[^:]:%255[^:]:%*255[^:]:%d:%*d:%*d:%*d\n%n"; mp_verbose(log, "CONFIG_READ FILE: %s, type: %d\n", filename, type); if((f=fopen(filename, "r"))==NULL) @@ -267,13 +267,14 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log, char *filename, i ptr->pids_cnt = 0; ptr->freq = 0; ptr->is_dvb_s2 = false; + ptr->service_id = -1; ptr->stream_id = NO_STREAM_ID_FILTER; ptr->inv = INVERSION_AUTO; // Check if VDR-type channels.conf-line - then full line is consumed by the scan. int num_chars = 0; fields = sscanf(&line[k], vdr_conf, - &ptr->freq, vdr_par_str, vdr_loc_str, &ptr->srate, vpid_str, apid_str, tpid_str, &num_chars); + &ptr->freq, vdr_par_str, vdr_loc_str, &ptr->srate, vpid_str, apid_str, tpid_str, &ptr->service_id, &num_chars); if (num_chars == strlen(&line[k])) { // It's a VDR-style config line. @@ -302,9 +303,9 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log, char *filename, i } } - mp_verbose(log, "SAT, NUM: %d, NUM_FIELDS: %d, NAME: %s, FREQ: %d, SRATE: %d, POL: %c, DISEQC: %d, S2: %s, StreamID: %d", + mp_verbose(log, "SAT, NUM: %d, NUM_FIELDS: %d, NAME: %s, FREQ: %d, SRATE: %d, POL: %c, DISEQC: %d, S2: %s, StreamID: %d, SID: %d", list->NUM_CHANNELS, fields, ptr->name, ptr->freq, ptr->srate, ptr->pol, ptr->diseqc, - ptr->is_dvb_s2 ? "yes" : "no", ptr->stream_id); + ptr->is_dvb_s2 ? "yes" : "no", ptr->stream_id, ptr->service_id); } else { mp_verbose(log, "VDR, NUM: %d, NUM_FIELDS: %d, NAME: %s, FREQ: %d, SRATE: %d", list->NUM_CHANNELS, fields, ptr->name, ptr->freq, ptr->srate); @@ -355,12 +356,14 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log, char *filename, i if (parse_pid_string(log, apid_str, ptr)) { fields++; } - /* - // FIXME: Teletext PID excluded for now, seems misdetected as mp3. - if (parse_pid_string(log, tpid_str, ptr)) { - fields++; + /* If we do not know the service_id, PMT can not be extracted. + Teletext decoding will fail without PMT. */ + if (ptr->service_id != -1) { + if (parse_pid_string(log, tpid_str, ptr)) { + fields++; + } } - */ + if((fields < 2) || (ptr->pids_cnt <= 0) || (ptr->freq == 0) || (strlen(ptr->name) == 0)) continue; @@ -383,6 +386,15 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log, char *filename, i ptr->pids[ptr->pids_cnt] = 0; //PID 0 is the PAT ptr->pids_cnt++; } + + if (ptr->service_id != -1) { + /* We have the PMT-PID in addition. + This will be found later, when we tune to the channel. + Push back here to create the additional demux. */ + ptr->pids[ptr->pids_cnt] = -1; // Placeholder. + ptr->pids_cnt++; + } + mp_verbose(log, " PIDS: "); for(cnt = 0; cnt < ptr->pids_cnt; cnt++) mp_verbose(log, " %d ", ptr->pids[cnt]); @@ -663,13 +675,32 @@ int dvb_set_channel(stream_t *stream, int card, int n) priv->last_freq = channel->freq; priv->is_on = 1; + + if (channel->service_id != -1) { + /* We need the PMT-PID in addition. + If it has not yet beem resolved, do it now. */ + for (i = 0; i < channel->pids_cnt; i++) { + if (channel->pids[i] == -1) { + MP_VERBOSE(stream, "DVB_SET_CHANNEL: PMT-PID for service %d not resolved yet, parsing PAT...\n", channel->service_id); + int pmt_pid = dvb_get_pmt_pid(priv, card, channel->service_id); + MP_VERBOSE(stream, "DVB_SET_CHANNEL: Found PMT-PID: %d\n", pmt_pid); + channel->pids[i] = pmt_pid; + } + } + } - //sets demux filters and restart the stream + // sets demux filters and restart the stream for(i = 0; i < channel->pids_cnt; i++) { - if (!dvb_set_ts_filt(priv,priv->demux_fds[i], channel->pids[i], DMX_PES_OTHER)) - return 0; + if (channel->pids[i] == -1) { + // In case PMT was not resolved, skip it here. + MP_ERR(stream, "DVB_SET_CHANNEL: PMT-PID not found, teletext-decoding may fail.\n"); + } else { + if (!dvb_set_ts_filt(priv,priv->demux_fds[i], channel->pids[i], DMX_PES_OTHER)) + return 0; + } } + return 1; }