diff --git a/src/zapit/include/zapit/scanpmt.h b/src/zapit/include/zapit/scanpmt.h new file mode 100644 index 000000000..192e8a884 --- /dev/null +++ b/src/zapit/include/zapit/scanpmt.h @@ -0,0 +1,49 @@ +/* + * $Id: pmt.h,v 1.10 2003/08/15 23:34:09 obi Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __zapit_scan_pmt_h__ +#define __zapit_scan_pmt_h__ + +#include "channel.h" +#include + +int pmt_set_update_filter(CZapitChannel * const channel, int * fd); +int pmt_stop_update_filter(int * fd); + +#define PMT_SECTION_SIZE 1024 +class CPmt +{ + private: + int dmxnum; + unsigned char buffer[PMT_SECTION_SIZE]; + + bool Read(unsigned short pid, unsigned short sid); + void MakeCAMap(casys_map_t &camap); + bool ParseEsInfo(ElementaryStreamInfo *esinfo, CZapitChannel * const channel); + public: + CPmt(int dnum = 0); + ~CPmt(); + + bool Parse(CZapitChannel * const channel); + bool haveCaSys(int pmtpid, int service_id); +}; + +#endif /* __zapit_scan_pmt_h__ */ diff --git a/src/zapit/src/scanpmt.cpp b/src/zapit/src/scanpmt.cpp new file mode 100644 index 000000000..5e2185c9e --- /dev/null +++ b/src/zapit/src/scanpmt.cpp @@ -0,0 +1,395 @@ +/* + * $Id: pmt.cpp,v 1.40 2004/04/04 20:46:17 obi Exp $ + * + * (C) 2002 by Andreas Oberritter + * (C) 2002 by Frank Bormann + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include +#include +#include + +/* zapit */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG_PMT +//#define DEBUG_PMT_UNUSED + +CPmt::CPmt(int dnum) +{ + dmxnum = dnum; +} + +CPmt::~CPmt() +{ +} + +bool CPmt::Read(unsigned short pid, unsigned short sid) +{ + bool ret = true; + unsigned char filter[DMX_FILTER_SIZE]; + unsigned char mask[DMX_FILTER_SIZE]; + + cDemux * dmx = new cDemux(dmxnum); + dmx->Open(DMX_PSI_CHANNEL); + + memset(filter, 0x00, DMX_FILTER_SIZE); + memset(mask, 0x00, DMX_FILTER_SIZE); + + filter[0] = 0x02; /* table_id */ + filter[1] = sid >> 8; + filter[2] = sid; + filter[3] = 0x01; /* current_next_indicator */ + filter[4] = 0x00; /* section_number */ + mask[0] = 0xFF; + mask[1] = 0xFF; + mask[2] = 0xFF; + mask[3] = 0x01; + mask[4] = 0xFF; + if ((dmx->sectionFilter(pid, filter, mask, 5) < 0) || (dmx->Read(buffer, PMT_SECTION_SIZE) < 0)) { + printf("CPmt::Read: pid %x failed\n", pid); + ret = false; + } + delete dmx; + return ret; +} + +bool CPmt::Parse(CZapitChannel * const channel) +{ + printf("[zapit] parsing pmt pid 0x%X (%s)\n", channel->getPmtPid(), channel->getName().c_str()); + + if (channel->getPmtPid() == 0) + return false; + + if(!Read(channel->getPmtPid(), channel->getServiceId())) + return false; + + ProgramMapSection pmt(buffer); + + printf("[pmt] pcr pid: old 0x%x new 0x%x\n", channel->getPcrPid(), pmt.getPcrPid()); + + channel->resetPids(); + + channel->setPmtVersion(pmt.getVersionNumber()); + channel->setPcrPid(pmt.getPcrPid()); + + const ElementaryStreamInfoList * eslist = pmt.getEsInfo(); + ElementaryStreamInfoConstIterator it; + for (it = eslist->begin(); it != eslist->end(); ++it) { + ElementaryStreamInfo *esinfo = *it; + ParseEsInfo(esinfo, channel); + } + + casys_map_t camap; + MakeCAMap(camap); + channel->scrambled = !camap.empty(); + + if(CServiceScan::getInstance()->Scanning()) { + channel->setRawPmt(NULL); + } else { + channel->camap = camap; + int pmtlen= pmt.getSectionLength() + 3; + unsigned char * p = new unsigned char[pmtlen]; + memmove(p, buffer, pmtlen); + channel->setRawPmt(p, pmtlen); + } + + channel->setPidsFlag(); + return true; +} + +bool CPmt::ParseEsInfo(ElementaryStreamInfo *esinfo, CZapitChannel * const channel) +{ + std::string description = ""; + unsigned char componentTag = 0xFF; + CZapitAudioChannel::ZapitAudioChannelType audio_type = CZapitAudioChannel::UNKNOWN; + + uint8_t stream_type = esinfo->getType(); + + bool audio = false; + + const DescriptorList * dlist = esinfo->getDescriptors(); + DescriptorConstIterator dit; + for (dit = dlist->begin(); dit != dlist->end(); ++dit) { + Descriptor * d = *dit; + switch (d->getTag()) { + case REGISTRATION_DESCRIPTOR: + { + RegistrationDescriptor *sd = (RegistrationDescriptor*) d; + switch (sd->getFormatIdentifier()) { + case 0x44545331 ... 0x44545333: + audio_type = CZapitAudioChannel::DTS; + break; + case 0x41432d33: + audio_type = CZapitAudioChannel::AC3; + break; + default: +#ifdef DEBUG_PMT + printf("PMT: REGISTRATION_DESCRIPTOR: %x\n", sd->getFormatIdentifier()); +#endif + break; + } + } + break; + case ISO_639_LANGUAGE_DESCRIPTOR: + { + Iso639LanguageDescriptor * sd = (Iso639LanguageDescriptor*) d; + const Iso639LanguageList *languages = sd->getIso639Languages(); + Iso639LanguageConstIterator it; + for (it = languages->begin(); it != languages->end(); ++it) { + description = (*it)->getIso639LanguageCode(); + break; + } + } + break; + case STREAM_IDENTIFIER_DESCRIPTOR: + { + StreamIdentifierDescriptor *sd = (StreamIdentifierDescriptor*) d; + componentTag = sd->getComponentTag(); + } + break; + case TELETEXT_DESCRIPTOR: + { + TeletextDescriptor *td = (TeletextDescriptor*)d; + const VbiTeletextList *vbilist = td->getVbiTeletexts(); + VbiTeletextConstIterator it; + printf("[pmt] teletext pid %04x\n", esinfo->getPid()); + for (it = vbilist->begin(); it != vbilist->end(); ++it) { + VbiTeletext * vbi = *it; + + std::string lang = vbi->getIso639LanguageCode(); + uint8_t page = vbi->getTeletextPageNumber(); + uint8_t magazine = vbi->getTeletextMagazineNumber(); + printf("[pmt] teletext type %d mag %d page %d lang [%s]\n", + vbi->getTeletextType(), magazine, page, lang.c_str()); + if (vbi->getTeletextType() == 0x01) + channel->setTeletextLang(lang); + else if (vbi->getTeletextType() == 0x02) + channel->addTTXSubtitle(esinfo->getPid(), lang, magazine, page); + else if (vbi->getTeletextType() == 0x05) + channel->addTTXSubtitle(esinfo->getPid(), lang, magazine, page, true); + } + channel->setTeletextPid(esinfo->getPid()); + } + break; + case SUBTITLING_DESCRIPTOR: + if(stream_type == 0x06) { + SubtitlingDescriptor *sd = (SubtitlingDescriptor*) d; + const SubtitlingList *sublist = sd->getSubtitlings(); + SubtitlingConstIterator it; + for (it = sublist->begin(); it != sublist->end(); ++it) { + Subtitling * sub = *it; + + uint16_t composition_page_id = sub->getCompositionPageId(); + uint16_t ancillary_page_id = sub->getAncillaryPageId(); + std::string lang = sub->getIso639LanguageCode(); + + channel->addDVBSubtitle(esinfo->getPid(), lang, sub->getSubtitlingType(), + composition_page_id, ancillary_page_id); + } + } + break; + case AC3_DESCRIPTOR: + audio_type = CZapitAudioChannel::AC3; + break; + case DTS_DESCRIPTOR: + audio_type = CZapitAudioChannel::DTS; + break; + case AAC_DESCRIPTOR: + audio_type = CZapitAudioChannel::AAC; + break; + case 0xC5: /* User Private descriptor - Canal+ Radio */ + if(d->getLength() >= 25) { + uint8_t buf[2 + d->getLength()]; + d->writeToBuffer(buf); + int tsidonid = channel->getTransportStreamId() << 16 | channel->getOriginalNetworkId(); + description = convertDVBUTF8((const char*)&buf[3], 24, 2, tsidonid); + } + break; + case AUDIO_STREAM_DESCRIPTOR: + break; + case VIDEO_STREAM_DESCRIPTOR: + break; + case CA_DESCRIPTOR: + break; + default: + { +#ifdef DEBUG_PMT_UNUSED + printf("PMT: descriptor %02x: ", d->getTag()); + uint8_t len = 2+d->getLength(); + uint8_t buf[len]; + d->writeToBuffer(buf); + for(uint8_t i = 0; i < len; i++) + printf("%02x ", buf[i]); + printf("\n"); +#endif + } + break; + } + } + switch (stream_type) { + case 0x01: + case 0x02: + case 0x1b: // AVC Video Stream (MPEG4 H264) + channel->setVideoPid(esinfo->getPid()); + channel->type = (stream_type == 0x1b); //FIXME + printf("[pmt] vpid %04x stream %d type %d\n", esinfo->getPid(), stream_type, channel->type); + break; + case 0x03: + case 0x04: + audio_type = CZapitAudioChannel::MPEG; + audio = true; + break; + case 0x06: + if(audio_type != CZapitAudioChannel::UNKNOWN) + audio = true; + break; + case 0x0F: // AAC ADTS + case 0x11: // AAC LATM + audio_type = CZapitAudioChannel::AAC; + audio = true; + break; + case 0x81: + audio_type = CZapitAudioChannel::AC3; + audio = true; + break; + default: +#ifdef DEBUG_PMT_UNUSED + printf("PMT: pid %04x stream_type: %02x\n", esinfo->getPid(), stream_type); +#endif + break; + } + if(audio) { + if(description.empty()) { + char str[DESC_MAX_LEN]; + snprintf(str, DESC_MAX_LEN, "Unknown 0x%04x", esinfo->getPid()); + description = str; + } + printf("[pmt] apid %04x stream %02x type %d [%s]\n", esinfo->getPid(), stream_type, + (int) audio_type, description.c_str()); + if(CServiceScan::getInstance()->Scanning()) { + if(channel->getPreAudioPid() == 0) + channel->setAudioPid(esinfo->getPid()); + } else + channel->addAudioChannel(esinfo->getPid(), audio_type, description, componentTag); + } + return true; +} + +void CPmt::MakeCAMap(casys_map_t &camap) +{ + ProgramMapSection pmt(buffer); + CaProgramMapSection capmt(&pmt, 0x03, 0x01); + DescriptorConstIterator dit; + for (dit = capmt.getDescriptors()->begin(); dit != capmt.getDescriptors()->end(); ++dit) { + if ((*dit)->getTag() == CA_DESCRIPTOR ) { + CaDescriptor * d = (CaDescriptor*) *dit; + //printf("%02X: casys %04X capid %04X, ", d->getTag(), d->getCaSystemId(), d->getCaPid()); + camap.insert(d->getCaSystemId()); + } + } +} + +bool CPmt::haveCaSys(int pmtpid, int service_id ) +{ + if(!Read(pmtpid, service_id)) + return false; + + casys_map_t camap; + MakeCAMap(camap); + return !camap.empty(); +} + +cDemux * pmtDemux; + +int pmt_set_update_filter(CZapitChannel * const channel, int * fd) +{ + unsigned char filter[DMX_FILTER_SIZE]; + unsigned char mask[DMX_FILTER_SIZE]; + unsigned char mode[DMX_FILTER_SIZE]; + + if(pmtDemux == NULL) { + pmtDemux = new cDemux(0); + pmtDemux->Open(DMX_PSI_CHANNEL); + } + + if (channel->getPmtPid() == 0) + return -1; + + memset(filter, 0x00, DMX_FILTER_SIZE); + memset(mask, 0x00, DMX_FILTER_SIZE); + memset(mode, 0x00, DMX_FILTER_SIZE); + + filter[0] = 0x02; /* table_id */ + filter[1] = channel->getServiceId() >> 8; + filter[2] = channel->getServiceId(); + filter[4] = 0x00; /* section_number */ + + mask[0] = 0xFF; + mask[1] = 0xFF; + mask[2] = 0xFF; + mask[4] = 0xFF; + +#if 0 //HAVE_COOL_HARDWARE + printf("[pmt] set update filter, sid 0x%x pid 0x%x version %x\n", channel->getServiceId(), channel->getPmtPid(), channel->getCaPmt()->version_number); + filter[3] = (((channel->getCaPmt()->version_number + 1) & 0x01) << 1) | 0x01; + mask[3] = (0x01 << 1) | 0x01; + pmtDemux->sectionFilter(channel->getPmtPid(), filter, mask, 5); +#else + printf("[pmt] set update filter, sid 0x%x pid 0x%x version %x\n", channel->getServiceId(), channel->getPmtPid(), channel->getPmtVersion()); + filter[3] = (channel->getPmtVersion() << 1) | 0x01; + mask[3] = (0x1F << 1) | 0x01; + mode[3] = 0x1F << 1; + pmtDemux->sectionFilter(channel->getPmtPid(), filter, mask, 5, 0, mode); +#endif + + *fd = 1; + return 0; +} + +int pmt_stop_update_filter(int * fd) +{ + printf("[pmt] stop update filter\n"); +#if HAVE_TRIPLEDRAGON + if (pmtDemux) + delete pmtDemux; + /* apparently a close/reopen is needed on TD... */ + pmtDemux = NULL; +#else + if(pmtDemux) + pmtDemux->Stop(); +#endif + + *fd = -1; + return 0; +}