void cLivePatFilter::GetLanguage(SI::PMT::Stream& stream, char *langs) { SI::Descriptor *d; for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) { switch (d->getDescriptorTag()) { case SI::ISO639LanguageDescriptorTag: { SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d; strn0cpy(langs, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1); break; } default: ; } delete d; } }
void cLivePatFilter::GetLanguage(SI::PMT::Stream& stream, char *langs, uint8_t& type) { SI::Descriptor *d; for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) { switch (d->getDescriptorTag()) { case SI::ISO639LanguageDescriptorTag: { SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d; strn0cpy(langs, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1); SI::Loop::Iterator it; SI::ISO639LanguageDescriptor::Language first; type = 0; if (ld->languageLoop.getNext(first, it)) { type = first.getAudioType(); } break; } default: ; } delete d; } }
void cLivePatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length) { if (Pid == 0x00) { if (Tid == 0x00) { SI::PAT pat(Data, false); if (!pat.CheckCRCAndParse()) return; SI::PAT::Association assoc; for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); ) { if (!assoc.isNITPid()) { const cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId()); if (Channel && (Channel == m_Channel)) { int prevPmtPid = m_pmtPid; if (0 != (m_pmtPid = assoc.getPid())) { if (m_pmtPid != prevPmtPid) { m_pmtSid = assoc.getServiceId(); Add(m_pmtPid, 0x02); m_pmtVersion = -1; break; } return; } } } } } } else if (Pid == m_pmtPid && Tid == SI::TableIdPMT && Source() && Transponder()) { SI::PMT pmt(Data, false); if (!pmt.CheckCRCAndParse()) return; if (pmt.getServiceId() != m_pmtSid) return; // skip broken PMT records if (m_pmtVersion != -1) { if (m_pmtVersion != pmt.getVersionNumber()) { cFilter::Del(m_pmtPid, 0x02); m_pmtPid = 0; // this triggers PAT scan } return; } m_pmtVersion = pmt.getVersionNumber(); cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), pmt.getServiceId()); if (Channel) { // Scan the stream-specific loop: SI::PMT::Stream stream; int Vpid = 0; int Ppid = 0; int Vtype = 0; int Apids[MAXAPIDS + 1] = { 0 }; // these lists are zero-terminated int Atypes[MAXAPIDS + 1] = { 0 }; int Dpids[MAXDPIDS + 1] = { 0 }; int Dtypes[MAXDPIDS + 1] = { 0 }; int Spids[MAXSPIDS + 1] = { 0 }; uchar SubtitlingTypes[MAXSPIDS + 1] = { 0 }; uint16_t CompositionPageIds[MAXSPIDS + 1] = { 0 }; uint16_t AncillaryPageIds[MAXSPIDS + 1] = { 0 }; char ALangs[MAXAPIDS][MAXLANGCODE2] = { "" }; char DLangs[MAXDPIDS][MAXLANGCODE2] = { "" }; char SLangs[MAXSPIDS][MAXLANGCODE2] = { "" }; int Tpid = 0; int NumApids = 0; int NumDpids = 0; int NumSpids = 0; for (SI::Loop::Iterator it; pmt.streamLoop.getNext(stream, it); ) { bool ProcessCaDescriptors = false; int esPid = stream.getPid(); switch (stream.getStreamType()) { case 1: // STREAMTYPE_11172_VIDEO case 2: // STREAMTYPE_13818_VIDEO case 0x1B: // MPEG4 Vpid = esPid; Ppid = pmt.getPCRPid(); Vtype = stream.getStreamType(); ProcessCaDescriptors = true; break; case 3: // STREAMTYPE_11172_AUDIO case 4: // STREAMTYPE_13818_AUDIO case 0x0F: // ISO/IEC 13818-7 Audio with ADTS transport syntax case 0x11: // ISO/IEC 14496-3 Audio with LATM transport syntax { if (NumApids < MAXAPIDS) { Apids[NumApids] = esPid; Atypes[NumApids] = stream.getStreamType(); SI::Descriptor *d; for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) { switch (d->getDescriptorTag()) { case SI::ISO639LanguageDescriptorTag: { SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d; SI::ISO639LanguageDescriptor::Language l; char *s = ALangs[NumApids]; int n = 0; for (SI::Loop::Iterator it; ld->languageLoop.getNext(l, it); ) { if (*ld->languageCode != '-') { // some use "---" to indicate "none" if (n > 0) *s++ = '+'; strn0cpy(s, I18nNormalizeLanguageCode(l.languageCode), MAXLANGCODE1); s += strlen(s); if (n++ > 1) break; } } } break; default: ; } delete d; } NumApids++; } ProcessCaDescriptors = true; } break; case 5: // STREAMTYPE_13818_PRIVATE case 6: // STREAMTYPE_13818_PES_PRIVATE //XXX case 8: // STREAMTYPE_13818_DSMCC { int dpid = 0; int dtype = 0; char lang[MAXLANGCODE1] = { 0 }; SI::Descriptor *d; for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) { switch (d->getDescriptorTag()) { case SI::AC3DescriptorTag: case SI::EnhancedAC3DescriptorTag: dpid = esPid; dtype = d->getDescriptorTag(); ProcessCaDescriptors = true; break; case SI::SubtitlingDescriptorTag: if (NumSpids < MAXSPIDS) { Spids[NumSpids] = esPid; SI::SubtitlingDescriptor *sd = (SI::SubtitlingDescriptor *)d; SI::SubtitlingDescriptor::Subtitling sub; char *s = SLangs[NumSpids]; int n = 0; for (SI::Loop::Iterator it; sd->subtitlingLoop.getNext(sub, it); ) { if (sub.languageCode[0]) { SubtitlingTypes[NumSpids] = sub.getSubtitlingType(); CompositionPageIds[NumSpids] = sub.getCompositionPageId(); AncillaryPageIds[NumSpids] = sub.getAncillaryPageId(); if (n > 0) *s++ = '+'; strn0cpy(s, I18nNormalizeLanguageCode(sub.languageCode), MAXLANGCODE1); s += strlen(s); if (n++ > 1) break; } } NumSpids++; } break; case SI::TeletextDescriptorTag: Tpid = esPid; break; case SI::ISO639LanguageDescriptorTag: { SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d; strn0cpy(lang, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1); } break; default: ; } delete d; } if (dpid) { if (NumDpids < MAXDPIDS) { Dpids[NumDpids] = dpid; Dtypes[NumDpids] = dtype; strn0cpy(DLangs[NumDpids], lang, MAXLANGCODE1); NumDpids++; } } } break; case 0x80: // STREAMTYPE_USER_PRIVATE #if APIVERSNUM >= 10728 if (Setup.StandardCompliance == STANDARD_ANSISCTE) #endif { // DigiCipher II VIDEO (ANSI/SCTE 57) Vpid = esPid; Ppid = pmt.getPCRPid(); Vtype = 0x02; // compression based upon MPEG-2 ProcessCaDescriptors = true; break; } // fall through case 0x81: // STREAMTYPE_USER_PRIVATE #if APIVERSNUM >= 10728 if (Setup.StandardCompliance == STANDARD_ANSISCTE) #endif { // ATSC A/53 AUDIO (ANSI/SCTE 57) char lang[MAXLANGCODE1] = { 0 }; SI::Descriptor *d; for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) { switch (d->getDescriptorTag()) { case SI::ISO639LanguageDescriptorTag: { SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d; strn0cpy(lang, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1); } break; default: ; } delete d; } if (NumDpids < MAXDPIDS) { Dpids[NumDpids] = esPid; Dtypes[NumDpids] = SI::AC3DescriptorTag; strn0cpy(DLangs[NumDpids], lang, MAXLANGCODE1); NumDpids++; } ProcessCaDescriptors = true; break; } // fall through case 0x82: // STREAMTYPE_USER_PRIVATE #if APIVERSNUM >= 10728 if (Setup.StandardCompliance == STANDARD_ANSISCTE) #endif { // STANDARD SUBTITLE (ANSI/SCTE 27) //TODO break; } // fall through case 0x83 ... 0xFF: // STREAMTYPE_USER_PRIVATE { char lang[MAXLANGCODE1] = { 0 }; bool IsAc3 = false; SI::Descriptor *d; for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) { switch (d->getDescriptorTag()) { case SI::RegistrationDescriptorTag: { SI::RegistrationDescriptor *rd = (SI::RegistrationDescriptor *)d; // http://www.smpte-ra.org/mpegreg/mpegreg.html switch (rd->getFormatIdentifier()) { case 0x41432D33: // 'AC-3' IsAc3 = true; break; default: //printf("Format identifier: 0x%08X (pid: %d)\n", rd->getFormatIdentifier(), esPid); break; } } break; case SI::ISO639LanguageDescriptorTag: { SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d; strn0cpy(lang, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1); } break; default: ; } delete d; } if (IsAc3) { if (NumDpids < MAXDPIDS) { Dpids[NumDpids] = esPid; Dtypes[NumDpids] = SI::AC3DescriptorTag; strn0cpy(DLangs[NumDpids], lang, MAXLANGCODE1); NumDpids++; } ProcessCaDescriptors = true; } } break; default: ;//printf("PID: %5d %5d %2d %3d %3d\n", pmt.getServiceId(), stream.getPid(), stream.getStreamType(), pmt.getVersionNumber(), Channel->Number()); } } cChannel pmtChannel(*Channel); pmtChannel.SetPids(Vpid, Ppid, Vtype, Apids, Atypes, ALangs, Dpids, Dtypes, DLangs, Spids, SLangs, Tpid); pmtChannel.SetSubtitlingDescriptors(SubtitlingTypes, CompositionPageIds, AncillaryPageIds); m_Streamer->SetTpid(Tpid); cPatPmtGenerator patPmtGenerator(&pmtChannel); m_Streamer->m_bufferLock.Lock(); m_Streamer->Put(patPmtGenerator.GetPat(), TS_SIZE); int Index = 0; while (uchar *pmt = patPmtGenerator.GetPmt(Index)) m_Streamer->Put(pmt, TS_SIZE); m_Streamer->m_bufferLock.Unlock(); } }
void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length) { SI::NIT nit(Data, false); if (!nit.CheckCRCAndParse()) return; // Some broadcasters send more than one NIT, with no apparent way of telling which // one is the right one to use. This is an attempt to find the NIT that contains // the transponder it was transmitted on and use only that one: int ThisNIT = -1; if (!networkId) { for (int i = 0; i < numNits; i++) { if (nits[i].networkId == nit.getNetworkId()) { if (nit.getSectionNumber() == 0) { // all NITs have passed by for (int j = 0; j < numNits; j++) { if (nits[j].hasTransponder) { networkId = nits[j].networkId; //printf("taking NIT with network ID %d\n", networkId); //XXX what if more than one NIT contains this transponder??? break; } } if (!networkId) { //printf("none of the NITs contains transponder %d\n", Transponder()); return; } } else { ThisNIT = i; break; } } } if (!networkId && ThisNIT < 0 && numNits < MAXNITS) { if (nit.getSectionNumber() == 0) { *nits[numNits].name = 0; SI::Descriptor *d; for (SI::Loop::Iterator it; (d = nit.commonDescriptors.getNext(it)); ) { switch (d->getDescriptorTag()) { case SI::NetworkNameDescriptorTag: { SI::NetworkNameDescriptor *nnd = (SI::NetworkNameDescriptor *)d; #ifdef USE_PROVIDERCHARSET nnd->name.getText(nits[numNits].name, MAXNETWORKNAME, NULL); #else nnd->name.getText(nits[numNits].name, MAXNETWORKNAME); #endif } break; default: ; } delete d; } nits[numNits].networkId = nit.getNetworkId(); nits[numNits].hasTransponder = false; //printf("NIT[%d] %5d '%s'\n", numNits, nits[numNits].networkId, nits[numNits].name); ThisNIT = numNits; numNits++; } } } else if (networkId != nit.getNetworkId()) return; // ignore all other NITs else if (!sectionSyncer.Sync(nit.getVersionNumber(), nit.getSectionNumber(), nit.getLastSectionNumber())) return; if (!Channels.Lock(true, 10)) return; SI::NIT::TransportStream ts; for (SI::Loop::Iterator it; nit.transportStreamLoop.getNext(ts, it); ) { SI::Descriptor *d; SI::Loop::Iterator it2; SI::FrequencyListDescriptor *fld = (SI::FrequencyListDescriptor *)ts.transportStreamDescriptors.getNext(it2, SI::FrequencyListDescriptorTag); int NumFrequencies = fld ? fld->frequencies.getCount() + 1 : 1; int Frequencies[NumFrequencies]; if (fld) { int ct = fld->getCodingType(); if (ct > 0) { int n = 1; for (SI::Loop::Iterator it3; fld->frequencies.hasNext(it3); ) { int f = fld->frequencies.getNext(it3); switch (ct) { case 1: f = BCD2INT(f) / 100; break; case 2: f = BCD2INT(f) / 10; break; case 3: f = f * 10; break; default: ; } Frequencies[n++] = f; } } else NumFrequencies = 1; } delete fld; for (SI::Loop::Iterator it2; (d = ts.transportStreamDescriptors.getNext(it2)); ) { switch (d->getDescriptorTag()) { case SI::SatelliteDeliverySystemDescriptorTag: { SI::SatelliteDeliverySystemDescriptor *sd = (SI::SatelliteDeliverySystemDescriptor *)d; cDvbTransponderParameters dtp; int Source = cSource::FromData(cSource::stSat, BCD2INT(sd->getOrbitalPosition()), sd->getWestEastFlag()); int Frequency = Frequencies[0] = BCD2INT(sd->getFrequency()) / 100; static char Polarizations[] = { 'H', 'V', 'L', 'R' }; dtp.SetPolarization(Polarizations[sd->getPolarization()]); static int CodeRates[] = { FEC_NONE, FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, FEC_8_9, FEC_3_5, FEC_4_5, FEC_9_10, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_NONE }; dtp.SetCoderateH(CodeRates[sd->getFecInner()]); static int Modulations[] = { QAM_AUTO, QPSK, PSK_8, QAM_16 }; dtp.SetModulation(Modulations[sd->getModulationType()]); dtp.SetSystem(sd->getModulationSystem() ? SYS_DVBS2 : SYS_DVBS); static int RollOffs[] = { ROLLOFF_35, ROLLOFF_25, ROLLOFF_20, ROLLOFF_AUTO }; dtp.SetRollOff(sd->getModulationSystem() ? RollOffs[sd->getRollOff()] : ROLLOFF_AUTO); int SymbolRate = BCD2INT(sd->getSymbolRate()) / 10; if (ThisNIT >= 0) { for (int n = 0; n < NumFrequencies; n++) { if (ISTRANSPONDER(cChannel::Transponder(Frequencies[n], dtp.Polarization()), Transponder())) { nits[ThisNIT].hasTransponder = true; //printf("has transponder %d\n", Transponder()); break; } } break; } if (Setup.UpdateChannels >= 5) { bool found = false; bool forceTransponderUpdate = false; for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) { int transponder = Channel->Transponder(); found = true; if (!ISTRANSPONDER(cChannel::Transponder(Frequency, dtp.Polarization()), transponder)) { for (int n = 0; n < NumFrequencies; n++) { if (ISTRANSPONDER(cChannel::Transponder(Frequencies[n], dtp.Polarization()), transponder)) { Frequency = Frequencies[n]; break; } } } if (ISTRANSPONDER(cChannel::Transponder(Frequency, dtp.Polarization()), Transponder())) // only modify channels if we're actually receiving this transponder Channel->SetTransponderData(Source, Frequency, SymbolRate, dtp.ToString('S')); else if (Channel->Srate() != SymbolRate || strcmp(Channel->Parameters(), dtp.ToString('S'))) forceTransponderUpdate = true; // get us receiving this transponder } } if (!found || forceTransponderUpdate) { for (int n = 0; n < NumFrequencies; n++) { cChannel *Channel = new cChannel; Channel->SetId(ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0); if (Channel->SetTransponderData(Source, Frequencies[n], SymbolRate, dtp.ToString('S'))) EITScanner.AddTransponder(Channel); else delete Channel; } } } } break; case SI::CableDeliverySystemDescriptorTag: { SI::CableDeliverySystemDescriptor *sd = (SI::CableDeliverySystemDescriptor *)d; cDvbTransponderParameters dtp; int Source = cSource::FromData(cSource::stCable); int Frequency = Frequencies[0] = BCD2INT(sd->getFrequency()) / 10; //XXX FEC_outer??? static int CodeRates[] = { FEC_NONE, FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, FEC_8_9, FEC_3_5, FEC_4_5, FEC_9_10, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_NONE }; dtp.SetCoderateH(CodeRates[sd->getFecInner()]); static int Modulations[] = { QPSK, QAM_16, QAM_32, QAM_64, QAM_128, QAM_256, QAM_AUTO }; dtp.SetModulation(Modulations[min(sd->getModulation(), 6)]); int SymbolRate = BCD2INT(sd->getSymbolRate()) / 10; if (ThisNIT >= 0) { for (int n = 0; n < NumFrequencies; n++) { if (ISTRANSPONDER(Frequencies[n] / 1000, Transponder())) { nits[ThisNIT].hasTransponder = true; //printf("has transponder %d\n", Transponder()); break; } } break; } if (Setup.UpdateChannels >= 5) { bool found = false; bool forceTransponderUpdate = false; for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) { int transponder = Channel->Transponder(); found = true; if (!ISTRANSPONDER(Frequency / 1000, transponder)) { for (int n = 0; n < NumFrequencies; n++) { if (ISTRANSPONDER(Frequencies[n] / 1000, transponder)) { Frequency = Frequencies[n]; break; } } } if (ISTRANSPONDER(Frequency / 1000, Transponder())) // only modify channels if we're actually receiving this transponder Channel->SetTransponderData(Source, Frequency, SymbolRate, dtp.ToString('C')); else if (Channel->Srate() != SymbolRate || strcmp(Channel->Parameters(), dtp.ToString('C'))) forceTransponderUpdate = true; // get us receiving this transponder } } if (!found || forceTransponderUpdate) { for (int n = 0; n < NumFrequencies; n++) { cChannel *Channel = new cChannel; Channel->SetId(ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0); if (Channel->SetTransponderData(Source, Frequencies[n], SymbolRate, dtp.ToString('C'))) EITScanner.AddTransponder(Channel); else delete Channel; } } } } break; case SI::TerrestrialDeliverySystemDescriptorTag: { SI::TerrestrialDeliverySystemDescriptor *sd = (SI::TerrestrialDeliverySystemDescriptor *)d; cDvbTransponderParameters dtp; int Source = cSource::FromData(cSource::stTerr); int Frequency = Frequencies[0] = sd->getFrequency() * 10; static int Bandwidths[] = { 8000000, 7000000, 6000000, 0, 0, 0, 0, 0 }; dtp.SetBandwidth(Bandwidths[sd->getBandwidth()]); static int Constellations[] = { QPSK, QAM_16, QAM_64, QAM_AUTO }; dtp.SetModulation(Constellations[sd->getConstellation()]); static int Hierarchies[] = { HIERARCHY_NONE, HIERARCHY_1, HIERARCHY_2, HIERARCHY_4, HIERARCHY_AUTO, HIERARCHY_AUTO, HIERARCHY_AUTO, HIERARCHY_AUTO }; dtp.SetHierarchy(Hierarchies[sd->getHierarchy()]); static int CodeRates[] = { FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, FEC_AUTO, FEC_AUTO, FEC_AUTO }; dtp.SetCoderateH(CodeRates[sd->getCodeRateHP()]); dtp.SetCoderateL(CodeRates[sd->getCodeRateLP()]); static int GuardIntervals[] = { GUARD_INTERVAL_1_32, GUARD_INTERVAL_1_16, GUARD_INTERVAL_1_8, GUARD_INTERVAL_1_4 }; dtp.SetGuard(GuardIntervals[sd->getGuardInterval()]); static int TransmissionModes[] = { TRANSMISSION_MODE_2K, TRANSMISSION_MODE_8K, TRANSMISSION_MODE_AUTO, TRANSMISSION_MODE_AUTO }; dtp.SetTransmission(TransmissionModes[sd->getTransmissionMode()]); if (ThisNIT >= 0) { for (int n = 0; n < NumFrequencies; n++) { if (ISTRANSPONDER(Frequencies[n] / 1000000, Transponder())) { nits[ThisNIT].hasTransponder = true; //printf("has transponder %d\n", Transponder()); break; } } break; } if (Setup.UpdateChannels >= 5) { bool found = false; bool forceTransponderUpdate = false; for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) { int transponder = Channel->Transponder(); found = true; if (!ISTRANSPONDER(Frequency / 1000000, transponder)) { for (int n = 0; n < NumFrequencies; n++) { if (ISTRANSPONDER(Frequencies[n] / 1000000, transponder)) { Frequency = Frequencies[n]; break; } } } if (ISTRANSPONDER(Frequency / 1000000, Transponder())) // only modify channels if we're actually receiving this transponder Channel->SetTransponderData(Source, Frequency, 0, dtp.ToString('T')); else if (strcmp(Channel->Parameters(), dtp.ToString('T'))) forceTransponderUpdate = true; // get us receiving this transponder } } if (!found || forceTransponderUpdate) { for (int n = 0; n < NumFrequencies; n++) { cChannel *Channel = new cChannel; Channel->SetId(ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0); if (Channel->SetTransponderData(Source, Frequencies[n], 0, dtp.ToString('T'))) EITScanner.AddTransponder(Channel); else delete Channel; } } } } break; default: ; } delete d; } } Channels.Unlock(); }
cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bool OnlyRunningStatus) :SI::EIT(Data, false) { if (!CheckCRCAndParse()) return; tChannelID channelID(Source, getOriginalNetworkId(), getTransportStreamId(), getServiceId()); cChannel *channel = Channels.GetByChannelID(channelID, true); if (!channel) return; // only collect data for known channels cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channel, true); bool Empty = true; bool Modified = false; bool HasExternalData = false; time_t SegmentStart = 0; time_t SegmentEnd = 0; time_t Now = time(NULL); struct tm tm_r; struct tm t = *localtime_r(&Now, &tm_r); // this initializes the time zone in 't' if (Now < VALID_TIME) return; // we need the current time for handling PDC descriptors SI::EIT::Event SiEitEvent; for (SI::Loop::Iterator it; eventLoop.getNext(SiEitEvent, it); ) { bool ExternalData = false; time_t StartTime = SiEitEvent.getStartTime(); int Duration = SiEitEvent.getDuration(); // Drop bogus events - but keep NVOD reference events, where all bits of the start time field are set to 1, resulting in a negative number. if (StartTime == 0 || StartTime > 0 && Duration == 0) continue; Empty = false; if (!SegmentStart) SegmentStart = StartTime; SegmentEnd = StartTime + Duration; cEvent *newEvent = NULL; cEvent *rEvent = NULL; cEvent *pEvent = (cEvent *)pSchedule->GetEvent(SiEitEvent.getEventId(), StartTime); if (!pEvent) { if (OnlyRunningStatus) continue; // If we don't have that event yet, we create a new one. // Otherwise we copy the information into the existing event anyway, because the data might have changed. pEvent = newEvent = new cEvent(SiEitEvent.getEventId()); if (!pEvent) continue; } else { // We have found an existing event, either through its event ID or its start time. pEvent->SetSeen(); // If the existing event has a zero table ID it was defined externally and shall // not be overwritten. uchar TableID = pEvent->TableID(); if (TableID == 0x00) { if (pEvent->Version() == getVersionNumber()) continue; HasExternalData = ExternalData = true; } // If the new event has a higher table ID, let's skip it. // The lower the table ID, the more "current" the information. else if (Tid > TableID) continue; // If the new event comes from the same table and has the same version number // as the existing one, let's skip it to avoid unnecessary work. // Unfortunately some stations (like, e.g. "Premiere") broadcast their EPG data on several transponders (like // the actual Premiere transponder and the Sat.1/Pro7 transponder), but use different version numbers on // each of them :-( So if one DVB card is tuned to the Premiere transponder, while an other one is tuned // to the Sat.1/Pro7 transponder, events will keep toggling because of the bogus version numbers. else if (Tid == TableID && pEvent->Version() == getVersionNumber()) continue; } if (!ExternalData) { pEvent->SetEventID(SiEitEvent.getEventId()); // unfortunately some stations use different event ids for the same event in different tables :-( pEvent->SetTableID(Tid); pEvent->SetStartTime(StartTime); pEvent->SetDuration(Duration); } if (newEvent) pSchedule->AddEvent(newEvent); if (Tid == 0x4E) { // we trust only the present/following info on the actual TS if (SiEitEvent.getRunningStatus() >= SI::RunningStatusNotRunning) pSchedule->SetRunningStatus(pEvent, SiEitEvent.getRunningStatus(), channel); } if (OnlyRunningStatus) continue; // do this before setting the version, so that the full update can be done later pEvent->SetVersion(getVersionNumber()); int LanguagePreferenceShort = -1; int LanguagePreferenceExt = -1; bool UseExtendedEventDescriptor = false; SI::Descriptor *d; SI::ExtendedEventDescriptors *ExtendedEventDescriptors = NULL; SI::ShortEventDescriptor *ShortEventDescriptor = NULL; cLinkChannels *LinkChannels = NULL; cComponents *Components = NULL; for (SI::Loop::Iterator it2; (d = SiEitEvent.eventDescriptors.getNext(it2)); ) { if (ExternalData && d->getDescriptorTag() != SI::ComponentDescriptorTag) { delete d; continue; } switch (d->getDescriptorTag()) { case SI::ExtendedEventDescriptorTag: { SI::ExtendedEventDescriptor *eed = (SI::ExtendedEventDescriptor *)d; if (I18nIsPreferredLanguage(Setup.EPGLanguages, eed->languageCode, LanguagePreferenceExt) || !ExtendedEventDescriptors) { delete ExtendedEventDescriptors; ExtendedEventDescriptors = new SI::ExtendedEventDescriptors; UseExtendedEventDescriptor = true; } if (UseExtendedEventDescriptor) { ExtendedEventDescriptors->Add(eed); d = NULL; // so that it is not deleted } if (eed->getDescriptorNumber() == eed->getLastDescriptorNumber()) UseExtendedEventDescriptor = false; } break; case SI::ShortEventDescriptorTag: { SI::ShortEventDescriptor *sed = (SI::ShortEventDescriptor *)d; if (I18nIsPreferredLanguage(Setup.EPGLanguages, sed->languageCode, LanguagePreferenceShort) || !ShortEventDescriptor) { delete ShortEventDescriptor; ShortEventDescriptor = sed; d = NULL; // so that it is not deleted } } break; case SI::ContentDescriptorTag: { SI::ContentDescriptor *cd = (SI::ContentDescriptor *)d; SI::ContentDescriptor::Nibble Nibble; int NumContents = 0; uchar Contents[MAXEVCONTENTS] = { 0 }; for (SI::Loop::Iterator it3; cd->nibbleLoop.getNext(Nibble, it3); ) { if (NumContents < MAXEVCONTENTS) { Contents[NumContents] = ((Nibble.getContentNibbleLevel1() & 0xF) << 4) | (Nibble.getContentNibbleLevel2() & 0xF); NumContents++; } } pEvent->SetContents(Contents); } break; case SI::ParentalRatingDescriptorTag: { int LanguagePreferenceRating = -1; SI::ParentalRatingDescriptor *prd = (SI::ParentalRatingDescriptor *)d; SI::ParentalRatingDescriptor::Rating Rating; for (SI::Loop::Iterator it3; prd->ratingLoop.getNext(Rating, it3); ) { if (I18nIsPreferredLanguage(Setup.EPGLanguages, Rating.languageCode, LanguagePreferenceRating)) { int ParentalRating = (Rating.getRating() & 0xFF); switch (ParentalRating) { // values defined by the DVB standard (minimum age = rating + 3 years): case 0x01 ... 0x0F: ParentalRating += 3; break; // values defined by broadcaster CSAT (now why didn't they just use 0x07, 0x09 and 0x0D?): case 0x11: ParentalRating = 10; break; case 0x12: ParentalRating = 12; break; case 0x13: ParentalRating = 16; break; default: ParentalRating = 0; } pEvent->SetParentalRating(ParentalRating); } } } break; case SI::PDCDescriptorTag: { SI::PDCDescriptor *pd = (SI::PDCDescriptor *)d; t.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting int month = t.tm_mon; t.tm_mon = pd->getMonth() - 1; t.tm_mday = pd->getDay(); t.tm_hour = pd->getHour(); t.tm_min = pd->getMinute(); t.tm_sec = 0; if (month == 11 && t.tm_mon == 0) // current month is dec, but event is in jan t.tm_year++; else if (month == 0 && t.tm_mon == 11) // current month is jan, but event is in dec t.tm_year--; time_t vps = mktime(&t); pEvent->SetVps(vps); } break; case SI::TimeShiftedEventDescriptorTag: { SI::TimeShiftedEventDescriptor *tsed = (SI::TimeShiftedEventDescriptor *)d; cSchedule *rSchedule = (cSchedule *)Schedules->GetSchedule(tChannelID(Source, channel->Nid(), channel->Tid(), tsed->getReferenceServiceId())); if (!rSchedule) break; rEvent = (cEvent *)rSchedule->GetEvent(tsed->getReferenceEventId()); if (!rEvent) break; pEvent->SetTitle(rEvent->Title()); pEvent->SetShortText(rEvent->ShortText()); pEvent->SetDescription(rEvent->Description()); } break; case SI::LinkageDescriptorTag: { SI::LinkageDescriptor *ld = (SI::LinkageDescriptor *)d; tChannelID linkID(Source, ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId()); if (ld->getLinkageType() == 0xB0) { // Premiere World bool hit = StartTime <= Now && Now < StartTime + Duration; if (hit) { char linkName[ld->privateData.getLength() + 1]; strn0cpy(linkName, (const char *)ld->privateData.getData(), sizeof(linkName)); // TODO is there a standard way to determine the character set of this string? cChannel *link = Channels.GetByChannelID(linkID); if (link != channel) { // only link to other channels, not the same one //fprintf(stderr, "Linkage %s %4d %4d %5d %5d %5d %5d %02X '%s'\n", hit ? "*" : "", channel->Number(), link ? link->Number() : -1, SiEitEvent.getEventId(), ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId(), ld->getLinkageType(), linkName);//XXX if (link) { if (Setup.UpdateChannels == 1 || Setup.UpdateChannels >= 3) link->SetName(linkName, "", ""); } else if (Setup.UpdateChannels >= 4) { cChannel *transponder = channel; if (channel->Tid() != ld->getTransportStreamId()) transponder = Channels.GetByTransponderID(linkID); link = Channels.NewChannel(transponder, linkName, "", "", ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId()); //XXX patFilter->Trigger(); } if (link) { if (!LinkChannels) LinkChannels = new cLinkChannels; LinkChannels->Add(new cLinkChannel(link)); } } else channel->SetPortalName(linkName); } } } break; case SI::ComponentDescriptorTag: { SI::ComponentDescriptor *cd = (SI::ComponentDescriptor *)d; uchar Stream = cd->getStreamContent(); uchar Type = cd->getComponentType(); if (1 <= Stream && Stream <= 4 && Type != 0) { // 1=video, 2=audio, 3=subtitles, 4=AC3 if (!Components) Components = new cComponents; char buffer[Utf8BufSize(256)]; Components->SetComponent(Components->NumComponents(), Stream, Type, I18nNormalizeLanguageCode(cd->languageCode), cd->description.getText(buffer, sizeof(buffer))); } } break; default: ; } delete d; } if (!rEvent) { if (ShortEventDescriptor) { char buffer[Utf8BufSize(256)]; pEvent->SetTitle(ShortEventDescriptor->name.getText(buffer, sizeof(buffer))); pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer, sizeof(buffer))); } else if (!HasExternalData) { pEvent->SetTitle(NULL); pEvent->SetShortText(NULL); } if (ExtendedEventDescriptors) { char buffer[Utf8BufSize(ExtendedEventDescriptors->getMaximumTextLength(": ")) + 1]; pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer, sizeof(buffer), ": ")); } else if (!HasExternalData) pEvent->SetDescription(NULL); } delete ExtendedEventDescriptors; delete ShortEventDescriptor; pEvent->SetComponents(Components); if (!HasExternalData) pEvent->FixEpgBugs(); if (LinkChannels) channel->SetLinkChannels(LinkChannels); Modified = true; }
bool cLivePatFilter::GetStreamInfo(SI::PMT::Stream& stream, cStreamInfo& info) { SI::Descriptor *d; info.m_pid = stream.getPid(); if (!info.m_pid) return false; switch (stream.getStreamType()) { case 0x01: // ISO/IEC 11172 Video case 0x02: // ISO/IEC 13818-2 Video case 0x80: // ATSC Video MPEG2 (ATSC DigiCipher QAM) DEBUGLOG("PMT scanner adding PID %d (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()]); info.m_type = cStreamInfo::stMPEG2VIDEO; return true; case 0x03: // ISO/IEC 11172 Audio case 0x04: // ISO/IEC 13818-3 Audio info.m_type = cStreamInfo::stMPEG2AUDIO; GetLanguage(stream, info.m_language, info.m_audiotype); DEBUGLOG("PMT scanner adding PID %d (%s) (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], info.m_language); return true; case 0x0f: // ISO/IEC 13818-7 Audio with ADTS transport syntax info.m_type = cStreamInfo::stAAC; GetLanguage(stream, info.m_language, info.m_audiotype); DEBUGLOG("PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "AAC", info.m_language); return true; case 0x11: // ISO/IEC 14496-3 Audio with LATM transport syntax info.m_type = cStreamInfo::stLATM; GetLanguage(stream, info.m_language, info.m_audiotype); DEBUGLOG("PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "LATM", info.m_language); return true; #if 1 case 0x07: // ISO/IEC 13512 MHEG case 0x08: // ISO/IEC 13818-1 Annex A DSM CC case 0x0a: // ISO/IEC 13818-6 Multiprotocol encapsulation case 0x0b: // ISO/IEC 13818-6 DSM-CC U-N Messages case 0x0c: // ISO/IEC 13818-6 Stream Descriptors case 0x0d: // ISO/IEC 13818-6 Sections (any type, including private data) case 0x0e: // ISO/IEC 13818-1 auxiliary #endif case 0x10: // ISO/IEC 14496-2 Visual (MPEG-4) DEBUGLOG("PMT scanner: Not adding PID %d (%s) (skipped)\n", stream.getPid(), psStreamTypes[stream.getStreamType()]); break; case 0x1b: // ISO/IEC 14496-10 Video (MPEG-4 part 10/AVC, aka H.264) DEBUGLOG("PMT scanner adding PID %d (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()]); info.m_type = cStreamInfo::stH264; return true; case 0x05: // ISO/IEC 13818-1 private sections case 0x06: // ISO/IEC 13818-1 PES packets containing private data for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) { switch (d->getDescriptorTag()) { case SI::AC3DescriptorTag: info.m_type = cStreamInfo::stAC3; GetLanguage(stream, info.m_language, info.m_audiotype); DEBUGLOG("PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "AC3", info.m_language); delete d; return true; case SI::EnhancedAC3DescriptorTag: info.m_type = cStreamInfo::stEAC3; GetLanguage(stream, info.m_language, info.m_audiotype); DEBUGLOG("PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "EAC3", info.m_language); delete d; return true; /*case SI::DTSDescriptorTag: info.m_type = cStreamInfo::stDTS; GetLanguage(stream, info.m_language, info.m_audiotype); DEBUGLOG("PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "DTS", info.m_language); delete d; return true;*/ case SI::AACDescriptorTag: info.m_type = cStreamInfo::stAAC; GetLanguage(stream, info.m_language, info.m_audiotype); DEBUGLOG("PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "AAC", info.m_language); delete d; return true; case SI::TeletextDescriptorTag: info.m_type = cStreamInfo::stTELETEXT; DEBUGLOG("PMT scanner: adding PID %d (%s) %s\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "Teletext"); delete d; return true; case SI::SubtitlingDescriptorTag: { info.m_type = cStreamInfo::stDVBSUB; info.m_subtitlingtype = 0; info.m_compositionpageid = 0; info.m_ancillarypageid = 0; SI::SubtitlingDescriptor *sd = (SI::SubtitlingDescriptor *)d; SI::SubtitlingDescriptor::Subtitling sub; char *s = info.m_language; int n = 0; for (SI::Loop::Iterator it; sd->subtitlingLoop.getNext(sub, it); ) { if (sub.languageCode[0]) { info.m_subtitlingtype = sub.getSubtitlingType(); info.m_compositionpageid = sub.getCompositionPageId(); info.m_ancillarypageid = sub.getAncillaryPageId(); if (n > 0) *s++ = '+'; strn0cpy(s, I18nNormalizeLanguageCode(sub.languageCode), MAXLANGCODE1); s += strlen(s); if (n++ > 1) break; } info.m_parsed = true; } delete d; DEBUGLOG("PMT scanner: adding PID %d (%s) %s\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "DVBSUB"); return true; } default: DEBUGLOG("PMT scanner: NOT adding PID %d (%s) %s (%i)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "UNKNOWN", d->getDescriptorTag()); break; } delete d; } break; default: #if VDRVERSNUM >= 10728 // ATSC A/53 AUDIO if (stream.getStreamType() == 0x81 && Setup.StandardCompliance == STANDARD_ANSISCTE) { info.m_type = cStreamInfo::stAC3; GetLanguage(stream, info.m_language, info.m_audiotype); DEBUGLOG("PMT scanner: adding PID %d %s (%s)\n", stream.getPid(), "AC3", info.m_language); return true; } else #endif /* This following section handles all the cases where the audio track * info is stored in PMT user info with stream id >= 0x81 * we check the registration format identifier to see if it * holds "AC-3" */ if (stream.getStreamType() >= 0x81) { bool found = false; for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) { switch (d->getDescriptorTag()) { case SI::RegistrationDescriptorTag: /* unfortunately libsi does not implement RegistrationDescriptor */ if (d->getLength() >= 4) { found = true; SI::CharArray rawdata = d->getData(); if (/*rawdata[0] == 5 && rawdata[1] >= 4 && */ rawdata[2] == 'A' && rawdata[3] == 'C' && rawdata[4] == '-' && rawdata[5] == '3') { DEBUGLOG("PMT scanner: Adding pid %d (type 0x%x) RegDesc len %d (%c%c%c%c)\n", stream.getPid(), stream.getStreamType(), d->getLength(), rawdata[2], rawdata[3], rawdata[4], rawdata[5]); info.m_type = cStreamInfo::stAC3; delete d; return true; } } break; default: break; } delete d; } if (!found) { DEBUGLOG("NOT adding PID %d (type 0x%x) RegDesc not found -> UNKNOWN\n", stream.getPid(), stream.getStreamType()); } } DEBUGLOG("PMT scanner: NOT adding PID %d (%s) %s\n", stream.getPid(), psStreamTypes[stream.getStreamType()<0x1c?stream.getStreamType():0], "UNKNOWN"); break; } info.m_type = cStreamInfo::stNONE; return false; }
cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bool OnlyRunningStatus) :SI::EIT(Data, false) { if (!CheckCRCAndParse()) return; tChannelID channelID(Source, getOriginalNetworkId(), getTransportStreamId(), getServiceId()); cChannel *channel = Channels.GetByChannelID(channelID, true); if (!channel) return; // only collect data for known channels //M7X0 BEGIN AK eEpgMode em = EpgModes.GetModeByChannelID(&channelID)->GetMode(); if ((em == emNone) | ((em == emNowNext) & (Tid != 0x4e) & (Tid != 0x4f))) return; //M7X0 END AK cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channel, true); bool Empty = true; bool Modified = false; bool HasExternalData = false; time_t SegmentStart = 0; time_t SegmentEnd = 0; time_t now = time(NULL); SI::EIT::Event SiEitEvent; for (SI::Loop::Iterator it; eventLoop.getNext(SiEitEvent, it); ) { bool ExternalData = false; // Drop bogus events - but keep NVOD reference events, where all bits of the start time field are set to 1, resulting in a negative number. time_t si_stime = SiEitEvent.getStartTime(); time_t si_dur = SiEitEvent.getDuration(); if (si_stime == 0 || (si_stime > 0 && si_dur == 0)) continue; Empty = false; if (((!SegmentStart) | (si_stime < SegmentStart)) & (si_stime > 0)) SegmentStart = si_stime; if (si_stime + si_dur > SegmentEnd) SegmentEnd = si_stime + si_dur; cEvent *newEvent = NULL; cEvent *rEvent = NULL; cEvent *pEvent = (cEvent *)pSchedule->GetEvent(SiEitEvent.getEventId(), si_stime); if (!pEvent) { bool outdated = (si_stime > 0) & (si_stime + si_dur + Setup.EPGLinger * 60 + 3600 < now); if (OnlyRunningStatus | (em == emForeign) | outdated) continue; // If we don't have that event yet, we create a new one. // Otherwise we copy the information into the existing event anyway, because the data might have changed. pEvent = newEvent = new cEvent(SiEitEvent.getEventId()); if (!pEvent) continue; } else { // We have found an existing event, either through its event ID or its start time. pEvent->SetSeen(); // If the existing event has a zero table ID it was defined externally and shall // not be overwritten. if (pEvent->TableID() == 0x00) { if (pEvent->Version() == getVersionNumber()) continue; HasExternalData = ExternalData = true; } // If the new event has a higher table ID, let's skip it. // The lower the table ID, the more "current" the information. else if (Tid > pEvent->TableID()) continue; // If the new event comes from the same table and has the same version number // as the existing one, let's skip it to avoid unnecessary work. // Unfortunately some stations (like, e.g. "Premiere") broadcast their EPG data on several transponders (like // the actual Premiere transponder and the Sat.1/Pro7 transponder), but use different version numbers on // each of them :-( So if one DVB card is tuned to the Premiere transponder, while an other one is tuned // to the Sat.1/Pro7 transponder, events will keep toggling because of the bogus version numbers. else if (Tid == pEvent->TableID() && pEvent->Version() == getVersionNumber()) continue; } if ((!ExternalData) & (!OnlyRunningStatus)) { pEvent->SetEventID(SiEitEvent.getEventId()); // unfortunately some stations use different event ids for the same event in different tables :-( pEvent->SetTableID(Tid); pEvent->SetStartTime(si_stime); pEvent->SetDuration(si_dur); } if (newEvent) pSchedule->AddEvent(newEvent); if (Tid == 0x4E) { // we trust only the present/following info on the actual TS if (SiEitEvent.getRunningStatus() >= SI::RunningStatusNotRunning) pSchedule->SetRunningStatus(pEvent, SiEitEvent.getRunningStatus(), channel); } if (OnlyRunningStatus) continue; // do this before setting the version, so that the full update can be done later pEvent->SetVersion(getVersionNumber()); int LanguagePreferenceShort = -1; int LanguagePreferenceExt = -1; bool UseExtendedEventDescriptor = false; SI::Descriptor *d; SI::ExtendedEventDescriptors *ExtendedEventDescriptors = NULL; SI::ShortEventDescriptor *ShortEventDescriptor = NULL; cLinkChannels *LinkChannels = NULL; cComponents *Components = NULL; for (SI::Loop::Iterator it2; (d = SiEitEvent.eventDescriptors.getNext(it2)); ) { if (ExternalData && d->getDescriptorTag() != SI::ComponentDescriptorTag) { delete d; continue; } switch (d->getDescriptorTag()) { case SI::ExtendedEventDescriptorTag: { SI::ExtendedEventDescriptor *eed = (SI::ExtendedEventDescriptor *)d; if (I18nIsPreferredLanguage(Setup.EPGLanguages, eed->languageCode, LanguagePreferenceExt) || !ExtendedEventDescriptors) { delete ExtendedEventDescriptors; ExtendedEventDescriptors = new SI::ExtendedEventDescriptors; UseExtendedEventDescriptor = true; } if (UseExtendedEventDescriptor) { ExtendedEventDescriptors->Add(eed); d = NULL; // so that it is not deleted } if (eed->getDescriptorNumber() == eed->getLastDescriptorNumber()) UseExtendedEventDescriptor = false; } break; case SI::ShortEventDescriptorTag: { SI::ShortEventDescriptor *sed = (SI::ShortEventDescriptor *)d; if (I18nIsPreferredLanguage(Setup.EPGLanguages, sed->languageCode, LanguagePreferenceShort) || !ShortEventDescriptor) { delete ShortEventDescriptor; ShortEventDescriptor = sed; d = NULL; // so that it is not deleted } } break; case SI::ContentDescriptorTag: break; case SI::ParentalRatingDescriptorTag: break; case SI::PDCDescriptorTag: { SI::PDCDescriptor *pd = (SI::PDCDescriptor *)d; time_t now = time(NULL); struct tm tm_r; struct tm t = *localtime_r(&now, &tm_r); // this initializes the time zone in 't' t.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting int month = t.tm_mon; t.tm_mon = pd->getMonth() - 1; t.tm_mday = pd->getDay(); t.tm_hour = pd->getHour(); t.tm_min = pd->getMinute(); t.tm_sec = 0; if (month == 11 && t.tm_mon == 0) // current month is dec, but event is in jan t.tm_year++; else if (month == 0 && t.tm_mon == 11) // current month is jan, but event is in dec t.tm_year--; time_t vps = mktime(&t); pEvent->SetVps(vps); } break; case SI::TimeShiftedEventDescriptorTag: { SI::TimeShiftedEventDescriptor *tsed = (SI::TimeShiftedEventDescriptor *)d; cSchedule *rSchedule = (cSchedule *)Schedules->GetSchedule(tChannelID(Source, channel->Nid(), channel->Tid(), tsed->getReferenceServiceId())); if (!rSchedule) break; rEvent = (cEvent *)rSchedule->GetEvent(tsed->getReferenceEventId()); if (!rEvent) break; pEvent->SetTitle(rEvent->Title()); pEvent->SetShortText(rEvent->ShortText()); pEvent->SetDescription(rEvent->Description()); } break; case SI::LinkageDescriptorTag: { SI::LinkageDescriptor *ld = (SI::LinkageDescriptor *)d; tChannelID linkID(Source, ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId()); if (ld->getLinkageType() == 0xB0) { // Premiere World time_t now = time(NULL); bool hit = SiEitEvent.getStartTime() <= now && now < SiEitEvent.getStartTime() + SiEitEvent.getDuration(); if (hit) { char linkName[ld->privateData.getLength() + 1]; strn0cpy(linkName, (const char *)ld->privateData.getData(), sizeof(linkName)); cChannel *link = Channels.GetByChannelID(linkID); if (link != channel) { // only link to other channels, not the same one //fprintf(stderr, "Linkage %s %4d %4d %5d %5d %5d %5d %02X '%s'\n", hit ? "*" : "", channel->Number(), link ? link->Number() : -1, SiEitEvent.getEventId(), ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId(), ld->getLinkageType(), linkName);//XXX if (link) { if (Setup.UpdateChannels == 1 || Setup.UpdateChannels >= 3) link->SetName(linkName, "", ""); } else if (Setup.UpdateChannels >= 4) { link = Channels.NewChannel(channel, linkName, "", "", ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId()); //XXX patFilter->Trigger(); } if (link) { if (!LinkChannels) LinkChannels = new cLinkChannels; LinkChannels->Add(new cLinkChannel(link)); } } else channel->SetPortalName(linkName); } } } break; case SI::ComponentDescriptorTag: { SI::ComponentDescriptor *cd = (SI::ComponentDescriptor *)d; uchar Stream = cd->getStreamContent(); uchar Type = cd->getComponentType(); if (1 <= Stream && Stream <= 2 && Type != 0) { if (!Components) Components = new cComponents; char buffer[256]; Components->SetComponent(Components->NumComponents(), cd->getStreamContent(), cd->getComponentType(), I18nNormalizeLanguageCode(cd->languageCode), cd->description.getText(buffer, sizeof(buffer))); } } break; default: ; } delete d; } if (!rEvent) { if (ShortEventDescriptor) { char buffer[256]; pEvent->SetTitle(ShortEventDescriptor->name.getText(buffer, sizeof(buffer))); pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer, sizeof(buffer))); } else if (!HasExternalData) { pEvent->SetTitle(NULL); pEvent->SetShortText(NULL); } if (ExtendedEventDescriptors) { char buffer[ExtendedEventDescriptors->getMaximumTextLength(": ") + 1]; pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer, sizeof(buffer), ": ")); } else if (!HasExternalData) pEvent->SetDescription(NULL); } delete ExtendedEventDescriptors; delete ShortEventDescriptor; pEvent->SetComponents(Components); if (!HasExternalData) pEvent->FixEpgBugs(); if (LinkChannels) channel->SetLinkChannels(LinkChannels); Modified = true; } if (Empty && Tid == 0x4E && getSectionNumber() == 0) // ETR 211: an empty entry in section 0 of table 0x4E means there is currently no event running pSchedule->ClrRunningStatus(channel); if (Tid == 0x4E) pSchedule->SetPresentSeen(); if (OnlyRunningStatus) return; if (Modified) { pSchedule->Sort(); pSchedule->DropOutdated(SegmentStart, SegmentEnd, Tid, getVersionNumber()); Schedules->SetModified(pSchedule); } }
cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bool OnlyRunningStatus) :SI::EIT(Data, false) { if (!CheckCRCAndParse()) return; tChannelID channelID(Source, getOriginalNetworkId(), getTransportStreamId(), getServiceId()); cChannel *channel = Channels.GetByChannelID(channelID, true); if (!channel) return; // only collect data for known channels // Channel marked as not to receive EPG data (noEPG patch) if (!Channels.isEPGAllowed(channelID)) { return; } const char *charset; if(channelID == lastChannelID) { charset = lastCharset; } else { charset = ProviderCharsets.GetCharset(channel->Provider()); lastCharset = charset; } //::printf("======== %s charset: %s ==========\n", channel->Provider(), charset); cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channel, true); bool Empty = true; bool Modified = false; bool HasExternalData = false; time_t SegmentStart = 0; time_t SegmentEnd = 0; time_t now = time(NULL); struct tm tm_r; struct tm t = *localtime_r(&now, &tm_r); // this initializes the time zone in 't' if (now < VALID_TIME) return; // we need the current time for handling PDC descriptors SI::EIT::Event SiEitEvent; for (SI::Loop::Iterator it; eventLoop.getNext(SiEitEvent, it); ) { bool ExternalData = false; // Drop bogus events - but keep NVOD reference events, where all bits of the start time field are set to 1, resulting in a negative number. int startTime = SiEitEvent.getStartTime(); int duration = SiEitEvent.getDuration(); if (startTime == 0 || (startTime > 0 && duration == 0)) continue; Empty = false; if (!SegmentStart) SegmentStart = startTime; SegmentEnd = startTime + duration; cEvent *newEvent = NULL; cEvent *rEvent = NULL; cEvent *pEvent = (cEvent *)pSchedule->GetEvent(SiEitEvent.getEventId(), startTime); if (!pEvent) { if (OnlyRunningStatus) continue; // If we don't have that event yet, we create a new one. // Otherwise we copy the information into the existing event anyway, because the data might have changed. pEvent = newEvent = new cEvent(SiEitEvent.getEventId()); if (!pEvent) continue; } else { // We have found an existing event, either through its event ID or its start time. pEvent->SetSeen(); // If the existing event has a zero table ID it was defined externally and shall // not be overwritten. uchar tableID = pEvent->TableID(); if (tableID == 0x00) { if (pEvent->Version() == getVersionNumber()) continue; HasExternalData = ExternalData = true; } // If the new event has a higher table ID, let's skip it. // The lower the table ID, the more "current" the information. else if (Tid > tableID) continue; // If the new event comes from the same table and has the same version number // as the existing one, let's skip it to avoid unnecessary work. // Unfortunately some stations (like, e.g. "Premiere") broadcast their EPG data on several transponders (like // the actual Premiere transponder and the Sat.1/Pro7 transponder), but use different version numbers on // each of them :-( So if one DVB card is tuned to the Premiere transponder, while an other one is tuned // to the Sat.1/Pro7 transponder, events will keep toggling because of the bogus version numbers. else if (Tid == tableID && pEvent->Version() == getVersionNumber()) continue; } if (!ExternalData) { pEvent->SetEventID(SiEitEvent.getEventId()); // unfortunately some stations use different event ids for the same event in different tables :-( pEvent->SetTableID(Tid); pEvent->SetStartTime(startTime); pEvent->SetDuration(duration); } if (newEvent) pSchedule->AddEvent(newEvent); if (Tid == 0x4E) { // we trust only the present/following info on the actual TS if (SiEitEvent.getRunningStatus() >= SI::RunningStatusNotRunning) pSchedule->SetRunningStatus(pEvent, SiEitEvent.getRunningStatus(), channel); } if (OnlyRunningStatus) continue; // do this before setting the version, so that the full update can be done later pEvent->SetVersion(getVersionNumber()); int LanguagePreferenceShort = -1; int LanguagePreferenceExt = -1; bool UseExtendedEventDescriptor = false; SI::Descriptor *d; SI::ExtendedEventDescriptors *ExtendedEventDescriptors = NULL; SI::ShortEventDescriptor *ShortEventDescriptor = NULL; cLinkChannels *LinkChannels = NULL; cComponents *Components = NULL; for (SI::Loop::Iterator it2; (d = SiEitEvent.eventDescriptors.getNext(it2)); ) { if (ExternalData && d->getDescriptorTag() != SI::ComponentDescriptorTag) { delete d; continue; } switch (d->getDescriptorTag()) { case SI::ExtendedEventDescriptorTag: { SI::ExtendedEventDescriptor *eed = (SI::ExtendedEventDescriptor *)d; if (I18nIsPreferredLanguage(Setup.EPGLanguages, eed->languageCode, LanguagePreferenceExt) || !ExtendedEventDescriptors) { delete ExtendedEventDescriptors; ExtendedEventDescriptors = new SI::ExtendedEventDescriptors; UseExtendedEventDescriptor = true; } if (UseExtendedEventDescriptor) { ExtendedEventDescriptors->Add(eed); d = NULL; // so that it is not deleted } if (eed->getDescriptorNumber() == eed->getLastDescriptorNumber()) UseExtendedEventDescriptor = false; } break; case SI::ShortEventDescriptorTag: { SI::ShortEventDescriptor *sed = (SI::ShortEventDescriptor *)d; if (I18nIsPreferredLanguage(Setup.EPGLanguages, sed->languageCode, LanguagePreferenceShort) || !ShortEventDescriptor) { delete ShortEventDescriptor; ShortEventDescriptor = sed; d = NULL; // so that it is not deleted } } break; case SI::ContentDescriptorTag: break; case SI::ParentalRatingDescriptorTag: break; case SI::PDCDescriptorTag: { SI::PDCDescriptor *pd = (SI::PDCDescriptor *)d; t.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting int month = t.tm_mon; t.tm_mon = pd->getMonth() - 1; t.tm_mday = pd->getDay(); t.tm_hour = pd->getHour(); t.tm_min = pd->getMinute(); t.tm_sec = 0; if (month == 11 && t.tm_mon == 0) // current month is dec, but event is in jan t.tm_year++; else if (month == 0 && t.tm_mon == 11) // current month is jan, but event is in dec t.tm_year--; time_t vps = mktime(&t); pEvent->SetVps(vps); } break; case SI::TimeShiftedEventDescriptorTag: { SI::TimeShiftedEventDescriptor *tsed = (SI::TimeShiftedEventDescriptor *)d; cSchedule *rSchedule = (cSchedule *)Schedules->GetSchedule(tChannelID(Source, channel->Nid(), channel->Tid(), tsed->getReferenceServiceId())); if (!rSchedule) break; rEvent = (cEvent *)rSchedule->GetEvent(tsed->getReferenceEventId()); if (!rEvent) break; pEvent->SetTitle(rEvent->Title()); pEvent->SetShortText(rEvent->ShortText()); pEvent->SetDescription(rEvent->Description()); } break; case SI::LinkageDescriptorTag: { SI::LinkageDescriptor *ld = (SI::LinkageDescriptor *)d; tChannelID linkID(Source, ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId()); #ifdef DBG_EIT_LINKS dsyslog("[eit]: Linkage Type %X (0xB0) == Premiere ", ld->getLinkageType()); #endif if (ld->getLinkageType() == 0xB0) { // Premiere World bool hit = startTime <= now && now < startTime + duration; if (hit) { #ifdef DBG_EIT_LINKS dsyslog("[eit]: get hit \n"); #endif SI::String linkNameStr; linkNameStr.setData(ld->privateData, ld->privateData.getLength()); char linkName[Utf8BufSize(ld->privateData.getLength() + 1)]; linkNameStr.getText(linkName, sizeof(linkName), charset); cChannel *link = Channels.GetByChannelID(linkID); #ifdef DBG_EIT_LINKS dsyslog("[eit]: linkName %s \n", linkName); dsyslog("[eit]: linkName: %s; ID %X Channel: %s, ID %X \n", link->Name(),link->GetChannelID().Sid(), channel->Name(), channel->GetChannelID().Sid()); #endif if (link != channel) { // only link to other channels, not the same one //XXX ? #ifdef DBG_EIT_LINKS dsyslog("[eit] Linkage %s %4d %4d %5d %5d %5d %5d %02X '%s'\n", hit ? "*" : "", channel->Number(), link ? link->Number() : -1, SiEitEvent.getEventId(), ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId(), ld->getLinkageType(), linkName);//XXX #endif if (link) { if (Setup.UpdateChannels == 1 || Setup.UpdateChannels >= 3) // Update in any case! link->SetName(linkName, "", channel->Provider()); } else if (Setup.UpdateChannels >= 4) { if (channel->Tid() != ld->getTransportStreamId()) { cChannel *transponder = Channels.GetByTransponderID(linkID); link = Channels.NewChannel(transponder, linkName, "", channel->Provider(), ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId()); } else link = Channels.NewChannel(channel, linkName, "", channel->Provider(), ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId()); //XXX patFilter->Trigger(); } if (link) { if (!LinkChannels) LinkChannels = new cLinkChannels; #ifdef DBG_EIT_LINKS dsyslog("[eit]: Add new cLinkChannel(link)\n"); #endif LinkChannels->Add(new cLinkChannel(link)); } } else { channel->SetPortalName(linkName); #ifdef DBG_EIT_LINKS dsyslog("[eit]: Set portal name %s to %s hit \n",channel->Name(), linkName); #endif } } } } break; case SI::ComponentDescriptorTag: { SI::ComponentDescriptor *cd = (SI::ComponentDescriptor *)d; uchar Stream = cd->getStreamContent(); uchar Type = cd->getComponentType(); if (1 <= Stream && Stream <= 3 && Type != 0) { // 1=video, 2=audio, 3=subtitles if (!Components) Components = new cComponents; char buffer[Utf8BufSize(256)]; Components->SetComponent(Components->NumComponents(), cd->getStreamContent(), cd->getComponentType(), I18nNormalizeLanguageCode(cd->languageCode), cd->description.getText(buffer, sizeof(buffer), charset)); } } break; default: ; } delete d; } if (!rEvent) { if (ShortEventDescriptor) { char buffer[Utf8BufSize(256)]; pEvent->SetTitle(ShortEventDescriptor->name.getText(buffer, sizeof(buffer), charset)); pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer, sizeof(buffer), charset)); } else if (!HasExternalData) { pEvent->SetTitle(NULL); pEvent->SetShortText(NULL); } if (ExtendedEventDescriptors) { char buffer[Utf8BufSize(ExtendedEventDescriptors->getMaximumTextLength(": ") + 1)]; pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer, sizeof(buffer), charset, ": ")); } else if (!HasExternalData) pEvent->SetDescription(NULL); } delete ExtendedEventDescriptors; delete ShortEventDescriptor; pEvent->SetComponents(Components); if (!HasExternalData) pEvent->FixEpgBugs(); if (LinkChannels) channel->SetLinkChannels(LinkChannels); Modified = true; } if (Tid == 0x4E) { if (Empty && getSectionNumber() == 0) // ETR 211: an empty entry in section 0 of table 0x4E means there is currently no event running pSchedule->ClrRunningStatus(channel); pSchedule->SetPresentSeen(); } if (OnlyRunningStatus) return; if (Modified) { pSchedule->Sort(); if (!HasExternalData) pSchedule->DropOutdated(SegmentStart, SegmentEnd, Tid, getVersionNumber()); Schedules->SetModified(pSchedule); } }
void cSdtFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length) { if (!(Source() && Transponder())) return; SI::SDT sdt(Data, false); if (!sdt.CheckCRCAndParse()) return; if (!sectionSyncer.Sync(sdt.getVersionNumber(), sdt.getSectionNumber(), sdt.getLastSectionNumber())) return; if (!Channels.Lock(true, 10)) return; SI::SDT::Service SiSdtService; for (SI::Loop::Iterator it; sdt.serviceLoop.getNext(SiSdtService, it); ) { cChannel *channel = Channels.GetByChannelID(tChannelID(Source(), sdt.getOriginalNetworkId(), sdt.getTransportStreamId(), SiSdtService.getServiceId())); if (!channel) channel = Channels.GetByChannelID(tChannelID(Source(), 0, Transponder(), SiSdtService.getServiceId())); cLinkChannels *LinkChannels = NULL; SI::Descriptor *d; for (SI::Loop::Iterator it2; (d = SiSdtService.serviceDescriptors.getNext(it2)); ) { switch (d->getDescriptorTag()) { case SI::ServiceDescriptorTag: { SI::ServiceDescriptor *sd = (SI::ServiceDescriptor *)d; switch (sd->getServiceType()) { case 0x01: // digital television service case 0x02: // digital radio sound service case 0x04: // NVOD reference service case 0x05: // NVOD time-shifted service { char NameBuf[1024]; char ShortNameBuf[1024]; char ProviderNameBuf[1024]; sd->serviceName.getText(NameBuf, ShortNameBuf, sizeof(NameBuf), sizeof(ShortNameBuf)); char *pn = compactspace(NameBuf); char *ps = compactspace(ShortNameBuf); if (!*ps && cSource::IsCable(Source())) { // Some cable providers don't mark short channel names according to the // standard, but rather go their own way and use "name>short name" or // "name, short name": char *p = strchr(pn, '>'); // fix for UPC Wien if (!p) p = strchr(pn, ','); // fix for "Kabel Deutschland" if (p && p > pn) { *p++ = 0; strcpy(ShortNameBuf, skipspace(p)); } } sd->providerName.getText(ProviderNameBuf, sizeof(ProviderNameBuf)); char *pp = compactspace(ProviderNameBuf); if (channel) { channel->SetId(sdt.getOriginalNetworkId(), sdt.getTransportStreamId(), SiSdtService.getServiceId()); if (Setup.UpdateChannels == 1 || Setup.UpdateChannels >= 3) channel->SetName(pn, ps, pp); // Using SiSdtService.getFreeCaMode() is no good, because some // tv stations set this flag even for non-encrypted channels :-( // The special value 0xFFFF was supposed to mean "unknown encryption" // and would have been overwritten with real CA values later: // channel->SetCa(SiSdtService.getFreeCaMode() ? 0xFFFF : 0); } else if (*pn && Setup.UpdateChannels >= 4) { channel = Channels.NewChannel(Channel(), pn, ps, pp, sdt.getOriginalNetworkId(), sdt.getTransportStreamId(), SiSdtService.getServiceId()); patFilter->Trigger(); } } } } break; // Using the CaIdentifierDescriptor is no good, because some tv stations // just don't use it. The actual CA values are collected in pat.c: /* case SI::CaIdentifierDescriptorTag: { SI::CaIdentifierDescriptor *cid = (SI::CaIdentifierDescriptor *)d; if (channel) { for (SI::Loop::Iterator it; cid->identifiers.hasNext(it); ) channel->SetCa(cid->identifiers.getNext(it)); } } break; */ case SI::NVODReferenceDescriptorTag: { SI::NVODReferenceDescriptor *nrd = (SI::NVODReferenceDescriptor *)d; SI::NVODReferenceDescriptor::Service Service; for (SI::Loop::Iterator it; nrd->serviceLoop.getNext(Service, it); ) { cChannel *link = Channels.GetByChannelID(tChannelID(Source(), Service.getOriginalNetworkId(), Service.getTransportStream(), Service.getServiceId())); if (!link && Setup.UpdateChannels >= 4) { link = Channels.NewChannel(Channel(), "NVOD", "", "", Service.getOriginalNetworkId(), Service.getTransportStream(), Service.getServiceId()); patFilter->Trigger(); } if (link) { if (!LinkChannels) LinkChannels = new cLinkChannels; LinkChannels->Add(new cLinkChannel(link)); } } } break; default: ; } delete d; } if (LinkChannels) { if (channel) channel->SetLinkChannels(LinkChannels); else delete LinkChannels; } } Channels.Unlock(); }