From 50fcdc7d26c788ba10249bdf3c3bd039e9339b0b Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 5 Mar 2018 16:35:53 +0100 Subject: [PATCH] dvb psi: move PMT code to dvb_psi_pmt.c --- Makefile | 1 + src/input/mpegts.h | 2 + src/input/mpegts/dvb_psi.c | 610 +---------------------------- src/input/mpegts/dvb_psi_pmt.c | 616 ++++++++++++++++++++++++++++++ src/input/mpegts/dvb_psi_pmt.h | 49 +++ src/input/mpegts/mpegts_service.c | 14 + 6 files changed, 684 insertions(+), 608 deletions(-) create mode 100644 src/input/mpegts/dvb_psi_pmt.c create mode 100644 src/input/mpegts/dvb_psi_pmt.h diff --git a/Makefile b/Makefile index 24b4abe56..c92cf9a3e 100644 --- a/Makefile +++ b/Makefile @@ -383,6 +383,7 @@ SRCS-MPEGTS = \ src/input/mpegts/mpegts_table.c \ src/input/mpegts/dvb_support.c \ src/input/mpegts/dvb_charset.c \ + src/input/mpegts/dvb_psi_pmt.c \ src/input/mpegts/dvb_psi.c \ src/input/mpegts/fastscan.c \ src/input/mpegts/mpegts_mux_sched.c \ diff --git a/src/input/mpegts.h b/src/input/mpegts.h index 9868b63d1..6473b373f 100644 --- a/src/input/mpegts.h +++ b/src/input/mpegts.h @@ -1146,6 +1146,8 @@ mpegts_service_find_e2 mpegts_service_t * mpegts_service_find_by_pid ( mpegts_mux_t *mm, int pid ); +void mpegts_service_autoenable( mpegts_service_t *s, const char *where ); + void mpegts_service_update_slave_pids ( mpegts_service_t *t, mpegts_service_t *master_filter, int del ); diff --git a/src/input/mpegts/dvb_psi.c b/src/input/mpegts/dvb_psi.c index 4ffb5ce21..6c0f81c84 100644 --- a/src/input/mpegts/dvb_psi.c +++ b/src/input/mpegts/dvb_psi.c @@ -19,6 +19,7 @@ #include "tvheadend.h" #include "input.h" #include "dvb.h" +#include "dvb_psi_pmt.h" #include "tsdemux.h" #include "parsers.h" #include "lang_codes.h" @@ -87,9 +88,6 @@ typedef struct dvb_bat { int dvb_bouquets_parse = 1; -static int -psi_parse_pmt(mpegts_table_t *mt, mpegts_service_t *t, - const uint8_t *ptr, int len, int *_update); static inline uint16_t extract_2byte(const uint8_t *ptr) @@ -206,17 +204,6 @@ dvb_bouquet_comment ( bouquet_t *bq, mpegts_mux_t *mm ) bouquet_change_comment(bq, mm->mm_nicename, 0); } -static void -dvb_service_autoenable( mpegts_service_t *s, const char *where ) -{ - if (!s->s_enabled && s->s_auto == SERVICE_AUTO_PAT_MISSING) { - tvhinfo(LS_MPEGTS, "enabling service %s [sid %04X/%d] (found in %s)", - s->s_nicename, s->s_dvb_service_id, s->s_dvb_service_id, where); - service_set_enabled((service_t *)s, 1, SERVICE_AUTO_NORMAL); - } - s->s_dvb_check_seen = gclk(); -} - #if ENABLE_MPEGTS_DVB static mpegts_mux_t * dvb_fs_mux_find ( mpegts_mux_t *mm, uint16_t onid, uint16_t tsid ) @@ -1105,74 +1092,6 @@ dvb_cat_callback return dvb_table_end((mpegts_psi_table_t *)mt, st, sect); } -/* - * PMT processing - */ - -/* PMT update reason flags */ -#define PMT_UPDATE_PCR (1<<0) -#define PMT_UPDATE_NEW_STREAM (1<<1) -#define PMT_UPDATE_STREAM_CHANGE (1<<2) -#define PMT_UPDATE_STREAM_DELETED (1<<3) -#define PMT_UPDATE_LANGUAGE (1<<4) -#define PMT_UPDATE_AUDIO_TYPE (1<<5) -#define PMT_UPDATE_AUDIO_VERSION (1<<6) -#define PMT_UPDATE_FRAME_DURATION (1<<7) -#define PMT_UPDATE_COMPOSITION_ID (1<<8) -#define PMT_UPDATE_ANCILLARY_ID (1<<9) -#define PMT_UPDATE_NEW_CA_STREAM (1<<10) -#define PMT_UPDATE_NEW_CAID (1<<11) -#define PMT_UPDATE_CA_PROVIDER_CHANGE (1<<12) -#define PMT_UPDATE_PARENT_PID (1<<13) -#define PMT_UPDATE_CAID_DELETED (1<<14) -#define PMT_UPDATE_CAID_PID (1<<15) -#define PMT_REORDERED (1<<16) - -int -dvb_pmt_callback - (mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid) -{ - int r, sect, last, ver, update; - uint16_t sid; - mpegts_mux_t *mm = mt->mt_mux; - mpegts_service_t *s; - mpegts_psi_table_state_t *st = NULL; - - /* Start */ - if (len < 2) return -1; - sid = extract_svcid(ptr); - r = dvb_table_begin((mpegts_psi_table_t *)mt, ptr, len, - tableid, sid, 9, &st, §, &last, &ver, 0); - if (r != 1) return r; - if (mm->mm_sid_filter > 0 && sid != mm->mm_sid_filter) - goto end; - - /* Find service */ - LIST_FOREACH(s, &mm->mm_services, s_dvb_mux_link) - if (s->s_dvb_service_id == sid) break; - if (!s) return -1; - - /* Process */ - tvhdebug(mt->mt_subsys, "%s: sid %04X (%d)", mt->mt_name, sid, sid); - update = 0; - pthread_mutex_lock(&s->s_stream_mutex); - r = psi_parse_pmt(mt, s, ptr, len, &update); - pthread_mutex_unlock(&s->s_stream_mutex); - if (r) - service_restart((service_t*)s); - else if (update & (PMT_UPDATE_NEW_CA_STREAM|PMT_UPDATE_NEW_CAID| - PMT_UPDATE_CAID_DELETED|PMT_UPDATE_CAID_PID)) - descrambler_caid_changed((service_t *)s); - -#if ENABLE_LINUXDVB_CA - dvbcam_pmt_data(s, ptr, len); -#endif - - /* Finish */ -end: - return dvb_table_end((mpegts_psi_table_t *)mt, st, sect); -} - /* * NIT/BAT processing (because its near identical) */ @@ -1707,7 +1626,7 @@ dvb_sdt_mux charset = dvb_charset_find(mn, mm, s); if (s) - dvb_service_autoenable(s, "SDT"); + mpegts_service_autoenable(s, "SDT"); /* Descriptor loop */ DVB_DESC_EACH(mt, lptr, llen, dtag, dlen, dptr) { @@ -2226,531 +2145,6 @@ dvb_fs_sdt_callback } #endif -/** - * Add a CA descriptor - */ -static int -psi_desc_add_ca - (mpegts_table_t *mt, mpegts_service_t *t, - uint16_t caid, uint32_t provid, uint16_t pid) -{ - elementary_stream_t *st; - caid_t *c; - int r = 0; - - tvhdebug(mt->mt_subsys, "%s: caid %04X (%s) provider %08X pid %04X", - mt->mt_name, caid, caid2name(caid), provid, pid); - - st = elementary_stream_find(&t->s_components, pid); - if (st == NULL || st->es_type != SCT_CA) { - st = elementary_stream_create(&t->s_components, pid, SCT_CA, - t->s_status == SERVICE_RUNNING); - r |= PMT_UPDATE_NEW_CA_STREAM; - } - - st->es_delete_me = 0; - - st->es_position = 0x40000; - - LIST_FOREACH(c, &st->es_caids, link) { - if(c->caid == caid) { - if (c->pid > 0 && c->pid != pid) - r |= PMT_UPDATE_CAID_PID; - c->pid = pid; - c->delete_me = 0; - - if(c->providerid != provid) { - c->providerid = provid; - r |= PMT_UPDATE_CA_PROVIDER_CHANGE; - } - return r; - } - } - - c = malloc(sizeof(caid_t)); - - c->caid = caid; - c->providerid = provid; - c->use = 1; - c->pid = pid; - c->delete_me = 0; - c->filter = 0; - LIST_INSERT_HEAD(&st->es_caids, c, link); - r |= PMT_UPDATE_NEW_CAID; - return r; -} - -/** - * Parser for CA descriptors - */ -static int -psi_desc_ca(mpegts_table_t *mt, mpegts_service_t *t, const uint8_t *buffer, int size) -{ - int r = 0; - int i; - uint32_t provid = 0; - uint16_t caid, pid; - - if (size < 4) - return 0; - - caid = extract_2byte(buffer); - pid = extract_pid(buffer + 2); - - switch (caid & 0xFF00) { - case 0x0100: // SECA/Mediaguard - if (size < 6) - return 0; - provid = extract_2byte(buffer + 4); - - //Add extra providers, if any - for (i = 17; i < size; i += 15){ - uint16_t xpid = extract_pid(buffer + i); - uint16_t xprovid = extract_2byte(buffer + i + 2); - - r |= psi_desc_add_ca(mt, t, caid, xprovid, xpid); - } - break; - case 0x0500:// Viaccess - for (i = 4; i + 5 <= size;) { - uint8_t nano = buffer[i++]; - uint8_t nanolen = buffer[i++]; - - if (nano == 0x14) { - provid = (buffer[i] << 16) | (buffer[i + 1] << 8) | (buffer[i + 2] & 0xf0); - break; - } - - i += nanolen; - } - break; - case 0x4a00: - if (caid == 0x4ad2)//streamguard - provid=0; - if (caid != 0x4aee && caid != 0x4ad2) { // Bulcrypt - provid = size < 5 ? 0 : buffer[4]; - } - break; - case 0x1800: // Nagra - if (size == 0x7) - provid = extract_2byte(buffer + 5); - else - provid = 0; - break; - default: - provid = 0; - break; - } - - r |= psi_desc_add_ca(mt, t, caid, provid, pid); - - return r; -} - -/** - * Parser for teletext descriptor - */ -static int -psi_desc_teletext(mpegts_service_t *t, const uint8_t *ptr, int size, - int parent_pid, int *position) -{ - int r = 0; - const char *lang; - elementary_stream_t *st; - - while(size >= 5) { - int page = (ptr[3] & 0x7 ?: 8) * 100 + (ptr[4] >> 4) * 10 + (ptr[4] & 0xf); - int type = ptr[3] >> 3; - - if(type == 2 || type == 5) { - // 2 = subtitle page, 5 = subtitle page [hearing impaired] - - // We put the teletext subtitle driven streams on a list of pids - // higher than normal MPEG TS (0x2000 ++) - int pid = DVB_TELETEXT_BASE + page; - - st = elementary_stream_find(&t->s_components, pid); - if (st == NULL || st->es_type != SCT_TEXTSUB) { - r |= PMT_UPDATE_NEW_STREAM; - st = elementary_stream_create(&t->s_components, pid, SCT_TEXTSUB, - t->s_status == SERVICE_RUNNING); - } - - lang = lang_code_get2((const char*)ptr, 3); - if(memcmp(st->es_lang,lang,3)) { - r |= PMT_UPDATE_LANGUAGE; - memcpy(st->es_lang, lang, 4); - } - - if(st->es_parent_pid != parent_pid) { - r |= PMT_UPDATE_PARENT_PID; - st->es_parent_pid = parent_pid; - } - - // Check es_delete_me so we only compute position once per PMT update - if(st->es_position != *position && st->es_delete_me) { - st->es_position = *position; - r |= PMT_REORDERED; - } - st->es_delete_me = 0; - (*position)++; - } - ptr += 5; - size -= 5; - } - return r; -} - -/** - * PMT parser, from ISO 13818-1 and ETSI EN 300 468 - */ -static int -psi_parse_pmt - (mpegts_table_t *mt, mpegts_service_t *t, const uint8_t *ptr, int len, int *_update) -{ - int ret = 0; - uint16_t pcr_pid, pid; - uint8_t estype; - int dllen; - uint8_t dtag, dlen; - streaming_component_type_t hts_stream_type; - elementary_stream_t *st, *next; - int update = 0; - int composition_id; - int ancillary_id; - int version; - int position; - int tt_position; - int video_stream; - int pcr_shared = 0; - const char *lang; - uint8_t audio_type, audio_version; - mpegts_mux_t *mux = mt->mt_mux; - caid_t *c, *cn; - - lock_assert(&t->s_stream_mutex); - - version = (ptr[2] >> 1) & 0x1f; - pcr_pid = extract_pid(ptr + 5); - dllen = (ptr[7] & 0xf) << 8 | ptr[8]; - - if(t->s_pcr_pid != pcr_pid) { - t->s_pcr_pid = pcr_pid; - update |= PMT_UPDATE_PCR; - } - tvhdebug(mt->mt_subsys, "%s: pcr_pid %04X", mt->mt_name, pcr_pid); - - ptr += 9; - len -= 9; - - /* Mark all streams for deletion */ - TAILQ_FOREACH(st, &t->s_components.set_all, es_link) { - st->es_delete_me = 1; - - LIST_FOREACH(c, &st->es_caids, link) - c->delete_me = 1; - } - - // Common descriptors - while(dllen > 1) { - dtag = ptr[0]; - dlen = ptr[1]; - - tvhlog_hexdump(mt->mt_subsys, ptr, dlen + 2); - len -= 2; ptr += 2; dllen -= 2; - if(dlen > len) - break; - - switch(dtag) { - case DVB_DESC_CA: - update |= psi_desc_ca(mt, t, ptr, dlen); - break; - - default: - break; - } - len -= dlen; ptr += dlen; dllen -= dlen; - } - - while(len >= 5) { - estype = ptr[0]; - pid = extract_pid(ptr + 1); - dllen = (ptr[3] & 0xf) << 8 | ptr[4]; - tvhdebug(mt->mt_subsys, "%s: pid %04X estype %d", mt->mt_name, pid, estype); - tvhlog_hexdump(mt->mt_subsys, ptr, 5); - - ptr += 5; - len -= 5; - - hts_stream_type = SCT_UNKNOWN; - composition_id = -1; - ancillary_id = -1; - position = 0; - tt_position = 1000; - lang = NULL; - audio_type = 0; - audio_version = 0; - video_stream = 0; - - switch(estype) { - case 0x01: - case 0x02: - case 0x80: // 0x80 is DigiCipher II (North American cable) encrypted MPEG-2 - hts_stream_type = SCT_MPEG2VIDEO; - break; - - case 0x03: - case 0x04: - hts_stream_type = SCT_MPEG2AUDIO; - audio_version = 2; /* Assume Layer 2 */ - break; - - case 0x05: - if (config.hbbtv) - hts_stream_type = SCT_HBBTV; - break; - - case 0x06: - /* 0x06 is Chinese Cable TV AC-3 audio track */ - /* but mark it so only when no more descriptors exist */ - if (dllen > 1 || mux->mm_pmt_ac3 != MM_AC3_PMT_06) - break; - /* fall through to SCT_AC3 */ - case 0x81: - hts_stream_type = SCT_AC3; - break; - - case 0x0f: - hts_stream_type = SCT_MP4A; - break; - - case 0x11: - hts_stream_type = SCT_AAC; - break; - - case 0x1b: - hts_stream_type = SCT_H264; - break; - - case 0x24: - hts_stream_type = SCT_HEVC; - break; - - default: - break; - } - - while(dllen > 1) { - dtag = ptr[0]; - dlen = ptr[1]; - - tvhlog_hexdump(mt->mt_subsys, ptr, dlen + 2); - len -= 2; ptr += 2; dllen -= 2; - if(dlen > len) - break; - - switch(dtag) { - case DVB_DESC_CA: - update |= psi_desc_ca(mt, t, ptr, dlen); - break; - - case DVB_DESC_VIDEO_STREAM: - video_stream = dlen > 0 && SCT_ISVIDEO(hts_stream_type); - break; - - case DVB_DESC_REGISTRATION: - if(mux->mm_pmt_ac3 != MM_AC3_PMT_N05 && dlen == 4 && - ptr[0] == 'A' && ptr[1] == 'C' && ptr[2] == '-' && ptr[3] == '3') - hts_stream_type = SCT_AC3; - /* seen also these formats: */ - /* LU-A, ADV1 */ - break; - - case DVB_DESC_LANGUAGE: - lang = lang_code_get2((const char*)ptr, 3); - audio_type = ptr[3]; - break; - - case DVB_DESC_TELETEXT: - if(estype == 0x06) - hts_stream_type = SCT_TELETEXT; - - update |= psi_desc_teletext(t, ptr, dlen, pid, &tt_position); - break; - - case DVB_DESC_AC3: - if(estype == 0x06 || estype == 0x81) - hts_stream_type = SCT_AC3; - break; - - case DVB_DESC_AAC: - if(estype == 0x0f) - hts_stream_type = SCT_MP4A; - else if(estype == 0x11) - hts_stream_type = SCT_AAC; - break; - - case DVB_DESC_SUBTITLE: - if(dlen < 8 || video_stream) - break; - - lang = lang_code_get2((const char*)ptr, 3); - composition_id = extract_2byte(ptr + 4); - ancillary_id = extract_2byte(ptr + 6); - hts_stream_type = SCT_DVBSUB; - break; - - case DVB_DESC_EAC3: - if(estype == 0x06 || estype == 0x81) - hts_stream_type = SCT_EAC3; - break; - - default: - break; - } - len -= dlen; ptr += dlen; dllen -= dlen; - } - - if (hts_stream_type != SCT_UNKNOWN) { - - st = elementary_stream_find(&t->s_components, pid); - if (st == NULL || st->es_type != hts_stream_type) { - update |= PMT_UPDATE_NEW_STREAM; - st = elementary_stream_create(&t->s_components, pid, hts_stream_type, - t->s_status == SERVICE_RUNNING); - } - - if (st->es_type != hts_stream_type) { - update |= PMT_UPDATE_STREAM_CHANGE; - st->es_type = hts_stream_type; - st->es_audio_version = audio_version; - } - - st->es_delete_me = 0; - - tvhdebug(mt->mt_subsys, "%s: type %s position %d", - mt->mt_name, streaming_component_type2txt(st->es_type), position); - if (lang) - tvhdebug(mt->mt_subsys, "%s: language %s", mt->mt_name, lang); - if (composition_id != -1) - tvhdebug(mt->mt_subsys, "%s: composition_id %d", mt->mt_name, composition_id); - if (ancillary_id != -1) - tvhdebug(mt->mt_subsys, "%s: ancillary_id %d", mt->mt_name, ancillary_id); - - if(st->es_position != position) { - update |= PMT_REORDERED; - st->es_position = position; - } - - if(lang && memcmp(st->es_lang, lang, 3)) { - update |= PMT_UPDATE_LANGUAGE; - memcpy(st->es_lang, lang, 4); - } - - if(st->es_audio_type != audio_type) { - update |= PMT_UPDATE_AUDIO_TYPE; - st->es_audio_type = audio_type; - st->es_audio_version = audio_version; - } - - /* FIXME: it might make sense that PMT info has greater priority */ - /* but we use this field only for MPEG1/2/3 audio which */ - /* is detected in the parser code */ - if(audio_version && !st->es_audio_version) { - update |= PMT_UPDATE_AUDIO_VERSION; - st->es_audio_version = audio_version; - } - - if(composition_id != -1 && st->es_composition_id != composition_id) { - st->es_composition_id = composition_id; - update |= PMT_UPDATE_COMPOSITION_ID; - } - - if(ancillary_id != -1 && st->es_ancillary_id != ancillary_id) { - st->es_ancillary_id = ancillary_id; - update |= PMT_UPDATE_ANCILLARY_ID; - } - - if (st->es_pid == t->s_pcr_pid) - pcr_shared = 1; - } - position++; - } - - /* Handle PCR 'elementary stream' */ - if (!pcr_shared) { - st = elementary_stream_type_modify(&t->s_components, t->s_pcr_pid, SCT_PCR, - t->s_status == SERVICE_RUNNING); - st->es_delete_me = 0; - } - - /* Scan again to see if any streams should be deleted */ - for(st = TAILQ_FIRST(&t->s_components.set_all); st != NULL; st = next) { - next = TAILQ_NEXT(st, es_link); - - for(c = LIST_FIRST(&st->es_caids); c != NULL; c = cn) { - cn = LIST_NEXT(c, link); - if (c->delete_me) { - LIST_REMOVE(c, link); - free(c); - update |= PMT_UPDATE_CAID_DELETED; - } - } - - if(st->es_delete_me) { - elementary_set_stream_destroy(&t->s_components, st); - update |= PMT_UPDATE_STREAM_DELETED; - } - } - - if(update & PMT_REORDERED) - elementary_set_sort_streams(&t->s_components); - - if(update) { - tvhdebug(mt->mt_subsys, "%s: Service \"%s\" PMT (version %d) updated" - "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", - mt->mt_name, - service_nicename((service_t*)t), version, - update&PMT_UPDATE_PCR ? ", PCR PID changed":"", - update&PMT_UPDATE_NEW_STREAM ? ", New elementary stream":"", - update&PMT_UPDATE_STREAM_CHANGE ? ", Changed elementary stream":"", - update&PMT_UPDATE_STREAM_DELETED ? ", Stream deleted":"", - update&PMT_UPDATE_LANGUAGE ? ", Language changed":"", - update&PMT_UPDATE_AUDIO_TYPE ? ", Audio type changed":"", - update&PMT_UPDATE_AUDIO_VERSION ? ", Audio version changed":"", - update&PMT_UPDATE_FRAME_DURATION ? ", Frame duration changed":"", - update&PMT_UPDATE_COMPOSITION_ID ? ", Composition ID changed":"", - update&PMT_UPDATE_ANCILLARY_ID ? ", Ancillary ID changed":"", - update&PMT_UPDATE_NEW_CA_STREAM ? ", New CA stream":"", - update&PMT_UPDATE_NEW_CAID ? ", New CAID":"", - update&PMT_UPDATE_CA_PROVIDER_CHANGE? ", CA provider changed":"", - update&PMT_UPDATE_PARENT_PID ? ", Parent PID changed":"", - update&PMT_UPDATE_CAID_DELETED ? ", CAID deleted":"", - update&PMT_UPDATE_CAID_PID ? ", CAID PID changed":"", - update&PMT_REORDERED ? ", PIDs reordered":""); - - service_request_save((service_t*)t); - - // Only restart if something that our clients worry about did change - if(update & ~(PMT_UPDATE_NEW_CA_STREAM | - PMT_UPDATE_NEW_CAID | - PMT_UPDATE_CA_PROVIDER_CHANGE | - PMT_UPDATE_CAID_DELETED | - PMT_UPDATE_CAID_PID)) { - if(t->s_status == SERVICE_RUNNING) - ret = 1; - } - } - - if (elementary_stream_has_audio_or_video(&t->s_components)) { - dvb_service_autoenable(t, "PAT and PMT"); - t->s_verified = 1; - } - - *_update = update; - return ret; -} - /** * TDT parser, from ISO 13818-1 and ETSI EN 300 468 */ diff --git a/src/input/mpegts/dvb_psi_pmt.c b/src/input/mpegts/dvb_psi_pmt.c new file mode 100644 index 000000000..5211818cf --- /dev/null +++ b/src/input/mpegts/dvb_psi_pmt.c @@ -0,0 +1,616 @@ +/* + * MPEG TS Program Specific Information code + * Copyright (C) 2007 - 2010 Andreas Öman + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "tvheadend.h" +#include "config.h" +#include "input.h" +#include "lang_codes.h" +#include "descrambler/caid.h" +#include "descrambler/dvbcam.h" +#include "dvb_psi_pmt.h" + +/* + * PMT processing + */ + +static inline uint16_t +extract_2byte(const uint8_t *ptr) +{ + return (((uint16_t)ptr[0]) << 8) | ptr[1]; +} + +static inline uint16_t +extract_pid(const uint8_t *ptr) +{ + return ((ptr[0] & 0x1f) << 8) | ptr[1]; +} + +static inline uint16_t +extract_svcid(const uint8_t *ptr) +{ + return (ptr[0] << 8) | ptr[1]; +} + +/** + * Add a CA descriptor + */ +static int +psi_desc_add_ca + (mpegts_table_t *mt, elementary_set_t *set, + uint16_t caid, uint32_t provid, uint16_t pid, int running) +{ + elementary_stream_t *st; + caid_t *c; + int r = 0; + + tvhdebug(mt->mt_subsys, "%s: caid %04X (%s) provider %08X pid %04X", + mt->mt_name, caid, caid2name(caid), provid, pid); + + st = elementary_stream_find(set, pid); + if (st == NULL || st->es_type != SCT_CA) { + st = elementary_stream_create(set, pid, SCT_CA, running); + r |= PMT_UPDATE_NEW_CA_STREAM; + } + + st->es_delete_me = 0; + + st->es_position = 0x40000; + + LIST_FOREACH(c, &st->es_caids, link) { + if(c->caid == caid) { + if (c->pid > 0 && c->pid != pid) + r |= PMT_UPDATE_CAID_PID; + c->pid = pid; + c->delete_me = 0; + + if(c->providerid != provid) { + c->providerid = provid; + r |= PMT_UPDATE_CA_PROVIDER_CHANGE; + } + return r; + } + } + + c = malloc(sizeof(caid_t)); + + c->caid = caid; + c->providerid = provid; + c->use = 1; + c->pid = pid; + c->delete_me = 0; + c->filter = 0; + LIST_INSERT_HEAD(&st->es_caids, c, link); + r |= PMT_UPDATE_NEW_CAID; + return r; +} + +/** + * Parser for CA descriptors + */ +static int +psi_desc_ca + (mpegts_table_t *mt, elementary_set_t *set, const uint8_t *buffer, + int size, int running) +{ + int r = 0; + int i; + uint32_t provid = 0; + uint16_t caid, pid; + + if (size < 4) + return 0; + + caid = extract_2byte(buffer); + pid = extract_pid(buffer + 2); + + switch (caid & 0xFF00) { + case 0x0100: // SECA/Mediaguard + if (size < 6) + return 0; + provid = extract_2byte(buffer + 4); + + //Add extra providers, if any + for (i = 17; i < size; i += 15){ + uint16_t xpid = extract_pid(buffer + i); + uint16_t xprovid = extract_2byte(buffer + i + 2); + + r |= psi_desc_add_ca(mt, set, caid, xprovid, xpid, running); + } + break; + case 0x0500:// Viaccess + for (i = 4; i + 5 <= size;) { + uint8_t nano = buffer[i++]; + uint8_t nanolen = buffer[i++]; + + if (nano == 0x14) { + provid = (buffer[i] << 16) | (buffer[i + 1] << 8) | (buffer[i + 2] & 0xf0); + break; + } + + i += nanolen; + } + break; + case 0x4a00: + if (caid == 0x4ad2)//streamguard + provid=0; + if (caid != 0x4aee && caid != 0x4ad2) { // Bulcrypt + provid = size < 5 ? 0 : buffer[4]; + } + break; + case 0x1800: // Nagra + if (size == 0x7) + provid = extract_2byte(buffer + 5); + else + provid = 0; + break; + default: + provid = 0; + break; + } + + r |= psi_desc_add_ca(mt, set, caid, provid, pid, running); + + return r; +} + +/** + * Parser for teletext descriptor + */ +static int +psi_desc_teletext(elementary_set_t *set, const uint8_t *ptr, int size, + int parent_pid, int *position, int running) +{ + int r = 0; + const char *lang; + elementary_stream_t *st; + + while(size >= 5) { + int page = (ptr[3] & 0x7 ?: 8) * 100 + (ptr[4] >> 4) * 10 + (ptr[4] & 0xf); + int type = ptr[3] >> 3; + + if(type == 2 || type == 5) { + // 2 = subtitle page, 5 = subtitle page [hearing impaired] + + // We put the teletext subtitle driven streams on a list of pids + // higher than normal MPEG TS (0x2000 ++) + int pid = DVB_TELETEXT_BASE + page; + + st = elementary_stream_find(set, pid); + if (st == NULL || st->es_type != SCT_TEXTSUB) { + r |= PMT_UPDATE_NEW_STREAM; + st = elementary_stream_create(set, pid, SCT_TEXTSUB, running); + } + + lang = lang_code_get2((const char*)ptr, 3); + if(memcmp(st->es_lang,lang,3)) { + r |= PMT_UPDATE_LANGUAGE; + memcpy(st->es_lang, lang, 4); + } + + if(st->es_parent_pid != parent_pid) { + r |= PMT_UPDATE_PARENT_PID; + st->es_parent_pid = parent_pid; + } + + // Check es_delete_me so we only compute position once per PMT update + if(st->es_position != *position && st->es_delete_me) { + st->es_position = *position; + r |= PMT_REORDERED; + } + st->es_delete_me = 0; + (*position)++; + } + ptr += 5; + size -= 5; + } + return r; +} + +/** + * PMT parser, from ISO 13818-1 and ETSI EN 300 468 + */ +uint32_t +dvb_psi_parse_pmt + (mpegts_table_t *mt, const char *nicename, elementary_set_t *set, + const uint8_t *ptr, int len, uint16_t *pcr, int running) +{ + uint16_t pcr_pid, pid; + uint8_t estype; + int dllen; + uint8_t dtag, dlen; + streaming_component_type_t hts_stream_type; + elementary_stream_t *st, *next; + uint32_t update = 0; + int composition_id; + int ancillary_id; + int version; + int position; + int tt_position; + int video_stream; + int pcr_shared = 0; + const char *lang; + uint8_t audio_type, audio_version; + mpegts_mux_t *mux = mt->mt_mux; + caid_t *c, *cn; + + version = (ptr[2] >> 1) & 0x1f; + pcr_pid = extract_pid(ptr + 5); + dllen = (ptr[7] & 0xf) << 8 | ptr[8]; + + if(*pcr != pcr_pid) { + *pcr = pcr_pid; + update |= PMT_UPDATE_PCR; + } + tvhdebug(mt->mt_subsys, "%s: pcr_pid %04X", mt->mt_name, pcr_pid); + + ptr += 9; + len -= 9; + + /* Mark all streams for deletion */ + TAILQ_FOREACH(st, &set->set_all, es_link) { + st->es_delete_me = 1; + + LIST_FOREACH(c, &st->es_caids, link) + c->delete_me = 1; + } + + // Common descriptors + while(dllen > 1) { + dtag = ptr[0]; + dlen = ptr[1]; + + tvhlog_hexdump(mt->mt_subsys, ptr, dlen + 2); + len -= 2; ptr += 2; dllen -= 2; + if(dlen > len) + break; + + switch(dtag) { + case DVB_DESC_CA: + update |= psi_desc_ca(mt, set, ptr, dlen, running); + break; + + default: + break; + } + len -= dlen; ptr += dlen; dllen -= dlen; + } + + while(len >= 5) { + estype = ptr[0]; + pid = extract_pid(ptr + 1); + dllen = (ptr[3] & 0xf) << 8 | ptr[4]; + tvhdebug(mt->mt_subsys, "%s: pid %04X estype %d", mt->mt_name, pid, estype); + tvhlog_hexdump(mt->mt_subsys, ptr, 5); + + ptr += 5; + len -= 5; + + hts_stream_type = SCT_UNKNOWN; + composition_id = -1; + ancillary_id = -1; + position = 0; + tt_position = 1000; + lang = NULL; + audio_type = 0; + audio_version = 0; + video_stream = 0; + + switch(estype) { + case 0x01: + case 0x02: + case 0x80: // 0x80 is DigiCipher II (North American cable) encrypted MPEG-2 + hts_stream_type = SCT_MPEG2VIDEO; + break; + + case 0x03: + case 0x04: + hts_stream_type = SCT_MPEG2AUDIO; + audio_version = 2; /* Assume Layer 2 */ + break; + + case 0x05: + if (config.hbbtv) + hts_stream_type = SCT_HBBTV; + break; + + case 0x06: + /* 0x06 is Chinese Cable TV AC-3 audio track */ + /* but mark it so only when no more descriptors exist */ + if (dllen > 1 || mux->mm_pmt_ac3 != MM_AC3_PMT_06) + break; + /* fall through to SCT_AC3 */ + case 0x81: + hts_stream_type = SCT_AC3; + break; + + case 0x0f: + hts_stream_type = SCT_MP4A; + break; + + case 0x11: + hts_stream_type = SCT_AAC; + break; + + case 0x1b: + hts_stream_type = SCT_H264; + break; + + case 0x24: + hts_stream_type = SCT_HEVC; + break; + + default: + break; + } + + while(dllen > 1) { + dtag = ptr[0]; + dlen = ptr[1]; + + tvhlog_hexdump(mt->mt_subsys, ptr, dlen + 2); + len -= 2; ptr += 2; dllen -= 2; + if(dlen > len) + break; + + switch(dtag) { + case DVB_DESC_CA: + update |= psi_desc_ca(mt, set, ptr, dlen, running); + break; + + case DVB_DESC_VIDEO_STREAM: + video_stream = dlen > 0 && SCT_ISVIDEO(hts_stream_type); + break; + + case DVB_DESC_REGISTRATION: + if(mux->mm_pmt_ac3 != MM_AC3_PMT_N05 && dlen == 4 && + ptr[0] == 'A' && ptr[1] == 'C' && ptr[2] == '-' && ptr[3] == '3') + hts_stream_type = SCT_AC3; + /* seen also these formats: */ + /* LU-A, ADV1 */ + break; + + case DVB_DESC_LANGUAGE: + lang = lang_code_get2((const char*)ptr, 3); + audio_type = ptr[3]; + break; + + case DVB_DESC_TELETEXT: + if(estype == 0x06) + hts_stream_type = SCT_TELETEXT; + + update |= psi_desc_teletext(set, ptr, dlen, pid, &tt_position, running); + break; + + case DVB_DESC_AC3: + if(estype == 0x06 || estype == 0x81) + hts_stream_type = SCT_AC3; + break; + + case DVB_DESC_AAC: + if(estype == 0x0f) + hts_stream_type = SCT_MP4A; + else if(estype == 0x11) + hts_stream_type = SCT_AAC; + break; + + case DVB_DESC_SUBTITLE: + if(dlen < 8 || video_stream) + break; + + lang = lang_code_get2((const char*)ptr, 3); + composition_id = extract_2byte(ptr + 4); + ancillary_id = extract_2byte(ptr + 6); + hts_stream_type = SCT_DVBSUB; + break; + + case DVB_DESC_EAC3: + if(estype == 0x06 || estype == 0x81) + hts_stream_type = SCT_EAC3; + break; + + default: + break; + } + len -= dlen; ptr += dlen; dllen -= dlen; + } + + if (hts_stream_type != SCT_UNKNOWN) { + + st = elementary_stream_find(set, pid); + if (st == NULL || st->es_type != hts_stream_type) { + update |= PMT_UPDATE_NEW_STREAM; + st = elementary_stream_create(set, pid, hts_stream_type, running); + } + + if (st->es_type != hts_stream_type) { + update |= PMT_UPDATE_STREAM_CHANGE; + st->es_type = hts_stream_type; + st->es_audio_version = audio_version; + } + + st->es_delete_me = 0; + + tvhdebug(mt->mt_subsys, "%s: type %s position %d", + mt->mt_name, streaming_component_type2txt(st->es_type), position); + if (lang) + tvhdebug(mt->mt_subsys, "%s: language %s", mt->mt_name, lang); + if (composition_id != -1) + tvhdebug(mt->mt_subsys, "%s: composition_id %d", mt->mt_name, composition_id); + if (ancillary_id != -1) + tvhdebug(mt->mt_subsys, "%s: ancillary_id %d", mt->mt_name, ancillary_id); + + if(st->es_position != position) { + update |= PMT_REORDERED; + st->es_position = position; + } + + if(lang && memcmp(st->es_lang, lang, 3)) { + update |= PMT_UPDATE_LANGUAGE; + memcpy(st->es_lang, lang, 4); + } + + if(st->es_audio_type != audio_type) { + update |= PMT_UPDATE_AUDIO_TYPE; + st->es_audio_type = audio_type; + st->es_audio_version = audio_version; + } + + /* FIXME: it might make sense that PMT info has greater priority */ + /* but we use this field only for MPEG1/2/3 audio which */ + /* is detected in the parser code */ + if(audio_version && !st->es_audio_version) { + update |= PMT_UPDATE_AUDIO_VERSION; + st->es_audio_version = audio_version; + } + + if(composition_id != -1 && st->es_composition_id != composition_id) { + st->es_composition_id = composition_id; + update |= PMT_UPDATE_COMPOSITION_ID; + } + + if(ancillary_id != -1 && st->es_ancillary_id != ancillary_id) { + st->es_ancillary_id = ancillary_id; + update |= PMT_UPDATE_ANCILLARY_ID; + } + + if (st->es_pid == *pcr) + pcr_shared = 1; + } + position++; + } + + /* Handle PCR 'elementary stream' */ + if (!pcr_shared) { + st = elementary_stream_type_modify(set, *pcr, SCT_PCR, running); + st->es_delete_me = 0; + } + + /* Scan again to see if any streams should be deleted */ + for(st = TAILQ_FIRST(&set->set_all); st != NULL; st = next) { + next = TAILQ_NEXT(st, es_link); + + for(c = LIST_FIRST(&st->es_caids); c != NULL; c = cn) { + cn = LIST_NEXT(c, link); + if (c->delete_me) { + LIST_REMOVE(c, link); + free(c); + update |= PMT_UPDATE_CAID_DELETED; + } + } + + if(st->es_delete_me) { + elementary_set_stream_destroy(set, st); + update |= PMT_UPDATE_STREAM_DELETED; + } + } + + if(update & PMT_REORDERED) + elementary_set_sort_streams(set); + + if (update) { + tvhdebug(mt->mt_subsys, "%s: Service \"%s\" PMT (version %d) updated" + "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + mt->mt_name, + nicename, version, + update&PMT_UPDATE_PCR ? ", PCR PID changed":"", + update&PMT_UPDATE_NEW_STREAM ? ", New elementary stream":"", + update&PMT_UPDATE_STREAM_CHANGE ? ", Changed elementary stream":"", + update&PMT_UPDATE_STREAM_DELETED ? ", Stream deleted":"", + update&PMT_UPDATE_LANGUAGE ? ", Language changed":"", + update&PMT_UPDATE_AUDIO_TYPE ? ", Audio type changed":"", + update&PMT_UPDATE_AUDIO_VERSION ? ", Audio version changed":"", + update&PMT_UPDATE_FRAME_DURATION ? ", Frame duration changed":"", + update&PMT_UPDATE_COMPOSITION_ID ? ", Composition ID changed":"", + update&PMT_UPDATE_ANCILLARY_ID ? ", Ancillary ID changed":"", + update&PMT_UPDATE_NEW_CA_STREAM ? ", New CA stream":"", + update&PMT_UPDATE_NEW_CAID ? ", New CAID":"", + update&PMT_UPDATE_CA_PROVIDER_CHANGE? ", CA provider changed":"", + update&PMT_UPDATE_PARENT_PID ? ", Parent PID changed":"", + update&PMT_UPDATE_CAID_DELETED ? ", CAID deleted":"", + update&PMT_UPDATE_CAID_PID ? ", CAID PID changed":"", + update&PMT_REORDERED ? ", PIDs reordered":""); + } + + return update; +} + +int +dvb_pmt_callback + (mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid) +{ + int r, sect, last, ver, restart; + uint32_t update; + uint16_t sid; + mpegts_mux_t *mm = mt->mt_mux; + mpegts_service_t *s; + mpegts_psi_table_state_t *st = NULL; + + /* Start */ + if (len < 2) return -1; + sid = extract_svcid(ptr); + r = dvb_table_begin((mpegts_psi_table_t *)mt, ptr, len, + tableid, sid, 9, &st, §, &last, &ver, 0); + if (r != 1) return r; + if (mm->mm_sid_filter > 0 && sid != mm->mm_sid_filter) + goto end; + + /* Find service */ + LIST_FOREACH(s, &mm->mm_services, s_dvb_mux_link) + if (s->s_dvb_service_id == sid) break; + if (!s) return -1; + + /* Process */ + tvhdebug(mt->mt_subsys, "%s: sid %04X (%d)", mt->mt_name, sid, sid); + update = 0; + pthread_mutex_lock(&s->s_stream_mutex); + update = dvb_psi_parse_pmt(mt, service_nicename((service_t *)s), + &s->s_components, ptr, len, &s->s_pcr_pid, + s->s_status == SERVICE_RUNNING); + if (update) + service_request_save((service_t*)s); + /* Only restart if something that our clients worry about did change */ + restart = 0; + if (update) { + if (update & ~(PMT_UPDATE_NEW_CA_STREAM | + PMT_UPDATE_NEW_CAID | + PMT_UPDATE_CA_PROVIDER_CHANGE | + PMT_UPDATE_CAID_DELETED | + PMT_UPDATE_CAID_PID)) { + restart = s->s_status == SERVICE_RUNNING; + } + } + /* autoenable */ + if (elementary_stream_has_audio_or_video(&s->s_components)) { + mpegts_service_autoenable(s, "PAT and PMT"); + s->s_verified = 1; + } + pthread_mutex_unlock(&s->s_stream_mutex); + if (restart) + service_restart((service_t*)s); + if (update & (PMT_UPDATE_NEW_CA_STREAM|PMT_UPDATE_NEW_CAID| + PMT_UPDATE_CAID_DELETED|PMT_UPDATE_CAID_PID)) + descrambler_caid_changed((service_t *)s); + +#if ENABLE_LINUXDVB_CA + dvbcam_pmt_data(s, ptr, len); +#endif + + /* Finish */ +end: + return dvb_table_end((mpegts_psi_table_t *)mt, st, sect); +} diff --git a/src/input/mpegts/dvb_psi_pmt.h b/src/input/mpegts/dvb_psi_pmt.h new file mode 100644 index 000000000..e039d6baa --- /dev/null +++ b/src/input/mpegts/dvb_psi_pmt.h @@ -0,0 +1,49 @@ +/* + * MPEG TS Program Specific Information code + * Copyright (C) 2007 - 2010 Andreas Öman + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __DVB_PSI_PMT_H +#define __DVB_PSI_PMT_H 1 + +/* + * PMT processing + */ + +/* PMT update reason flags */ +#define PMT_UPDATE_PCR (1<<0) +#define PMT_UPDATE_NEW_STREAM (1<<1) +#define PMT_UPDATE_STREAM_CHANGE (1<<2) +#define PMT_UPDATE_STREAM_DELETED (1<<3) +#define PMT_UPDATE_LANGUAGE (1<<4) +#define PMT_UPDATE_AUDIO_TYPE (1<<5) +#define PMT_UPDATE_AUDIO_VERSION (1<<6) +#define PMT_UPDATE_FRAME_DURATION (1<<7) +#define PMT_UPDATE_COMPOSITION_ID (1<<8) +#define PMT_UPDATE_ANCILLARY_ID (1<<9) +#define PMT_UPDATE_NEW_CA_STREAM (1<<10) +#define PMT_UPDATE_NEW_CAID (1<<11) +#define PMT_UPDATE_CA_PROVIDER_CHANGE (1<<12) +#define PMT_UPDATE_PARENT_PID (1<<13) +#define PMT_UPDATE_CAID_DELETED (1<<14) +#define PMT_UPDATE_CAID_PID (1<<15) +#define PMT_REORDERED (1<<16) + +uint32_t dvb_psi_parse_pmt + (mpegts_table_t *mt, const char *nicename, elementary_set_t *set, + const uint8_t *ptr, int len, uint16_t *pcr, int running); + +#endif diff --git a/src/input/mpegts/mpegts_service.c b/src/input/mpegts/mpegts_service.c index a391d77b4..06f63dc40 100644 --- a/src/input/mpegts/mpegts_service.c +++ b/src/input/mpegts/mpegts_service.c @@ -930,6 +930,20 @@ ok: return s; } +/* + * Auto-enable service + */ +void +mpegts_service_autoenable( mpegts_service_t *s, const char *where ) +{ + if (!s->s_enabled && s->s_auto == SERVICE_AUTO_PAT_MISSING) { + tvhinfo(LS_MPEGTS, "enabling service %s [sid %04X/%d] (found in %s)", + s->s_nicename, s->s_dvb_service_id, s->s_dvb_service_id, where); + service_set_enabled((service_t *)s, 1, SERVICE_AUTO_NORMAL); + } + s->s_dvb_check_seen = gclk(); +} + /* * Raw MPEGTS Service */ -- 2.47.2