vdr/eit.c
Klaus Schmidinger 3fc2965975 Version 1.3.4
- Fixed handling language codes in case there is no audio or Dolby PID.
- Fixed handling CA ids (was broken in 1.3.3).
- Fixed the SVDRP command 'STAT DISK' to avoid a 'division by 0' in case the
  disk is full (thanks to Jens Rosenboom).
- Fixed handling bitmap indexes for 256 color mode (thanks to Andreas Regel).
- Now handling "linked services" (based on the 'autopid' patch from Andreas
  Schultz). Linked channels are detected and added to 'channels.conf', but
  currently they are not yet presented to the user other than being in the
  normal channel list (this will come later).
- Preliminary fix for the "Unknown picture type error" (thanks to Sascha
  Volkenandt for his support in debugging this one). This may slow down switching
  between channels on different transponders for now, but a better solution will
  come later.
- Fixed the validity check for channel IDs, because some providers use TIDs with
  value 0 (thanks to Thomas Bergwinkl).
- Enabled switching to a channel even if it has no Vpid or Apid set, because these
  might be automatically set when tuned to that transponder.
- No longer closing the Channels menu after trying to switch to a channel that
  is currently not available.
- Removed the now obsolete CaCaps stuff. The Setup/CICAM menu now displays the
  actual CAM type as reported by the CAM. The 'ca.conf' file has been stripped
  down to the values 0..4.
2004-02-08 18:00:00 +01:00

263 lines
12 KiB
C

/*
* eit.c: EIT section filter
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* Original version (as used in VDR before 1.3.0) written by
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
* Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg <marcel.wiesweg@gmx.de>.
*
* $Id: eit.c 1.86 2004/02/08 10:26:54 kls Exp $
*/
#include "eit.h"
#include "epg.h"
#include "i18n.h"
#include "libsi/section.h"
#include "libsi/descriptor.h"
// --- cEIT ------------------------------------------------------------------
class cEIT : public SI::EIT {
public:
cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data);
};
cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data)
: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
cEvent *rEvent = NULL;
cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channelID);
if (!pSchedule) {
pSchedule = new cSchedule(channelID);
Schedules->Add(pSchedule);
}
SI::EIT::Event SiEitEvent;
for (SI::Loop::Iterator it; eventLoop.hasNext(it); ) {
SiEitEvent = eventLoop.getNext(it);
cEvent *pEvent = (cEvent *)pSchedule->GetEvent(SiEitEvent.getEventId(), SiEitEvent.getStartTime());
if (!pEvent) {
// If we don't have that event ID yet, we create a new one.
// Otherwise we copy the information into the existing event anyway, because the data might have changed.
pEvent = pSchedule->AddEvent(new cEvent(channelID, SiEitEvent.getEventId()));
if (!pEvent)
continue;
pEvent->SetTableID(Tid);
}
else {
// We have found an existing event, either through its event ID or its start time.
// If the existing event has a zero table ID it was defined externally and shall
// not be overwritten.
if (pEvent->TableID() == 0x00)
continue;
// If the new event comes from a table that belongs to an "other TS" and the existing
// one comes from an "actual TS" table, let's skip it.
#define ISACTUALTS(tid) (tid == 0x4E || (tid & 0x50) == 0x50)
if (!ISACTUALTS(Tid) && ISACTUALTS(pEvent->TableID()))
continue;
// If the new event comes from a "schedule" table and the existing one comes from
// a "present/following" table, let's skip it (the p/f table usually contains more
// information, like e.g. a description).
if ((Tid & 0x50) == 0x50 && pEvent->TableID() == 0x4E || (Tid & 0x60) == 0x60 && pEvent->TableID() == 0x4F)
continue;
// If both events come from the same "schedule" table and the new event's table id is larger than the
// existing one's, let's skip it (higher tids mean "farther in the future" and usually have less information).
if (((Tid & 0x50) == 0x50 || (Tid & 0x60) == 0x60) && (pEvent->TableID() & 0xF0) == (Tid & 0xF0) && (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.
if (Tid == pEvent->TableID() && pEvent->Version() == getVersionNumber())
continue;
}
pEvent->SetVersion(getVersionNumber());
pEvent->SetTableID(Tid);
pEvent->SetEventID(SiEitEvent.getEventId()); // unfortunately some stations use different event ids for the same event in different tables :-(
int LanguagePreferenceShort = -1;
int LanguagePreferenceExt = -1;
bool UseExtendedEventDescriptor = false;
SI::Descriptor *d;
SI::ExtendedEventDescriptors *ExtendedEventDescriptors = NULL;
SI::ShortEventDescriptor *ShortEventDescriptor = NULL;
cLinkChannels *LinkChannels = NULL;
for (SI::Loop::Iterator it2; (d = SiEitEvent.eventDescriptors.getNext(it2)); ) {
switch (d->getDescriptorTag()) {
case SI::ExtendedEventDescriptorTag: {
SI::ExtendedEventDescriptor *eed = (SI::ExtendedEventDescriptor *)d;
if (I18nIsPreferredLanguage(Setup.EPGLanguages, I18nLanguageIndex(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, I18nLanguageIndex(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::TimeShiftedEventDescriptorTag: {
SI::TimeShiftedEventDescriptor *tsed = (SI::TimeShiftedEventDescriptor *)d;
cSchedule *rSchedule = (cSchedule *)Schedules->GetSchedule(tChannelID(Source, 0, 0, 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) {
cChannel *link = Channels.GetByChannelID(linkID);
if (link != channel) { // only link to other channels, not the same one
char linkName[ld->privateData.getLength() + 1];
strn0cpy(linkName, (const char *)ld->privateData.getData(), sizeof(linkName));
//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)
link->SetName(linkName);
}
else if (Setup.UpdateChannels >= 3) {
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));
}
}
}
}
}
break;
default: ;
}
delete d;
}
if (!rEvent) {
if (ShortEventDescriptor) {
char buffer[256];
pEvent->SetTitle(ShortEventDescriptor->name.getText(buffer));
pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer));
}
if (ExtendedEventDescriptors) {
char buffer[ExtendedEventDescriptors->getMaximumTextLength()];
pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer));
}
}
delete ExtendedEventDescriptors;
delete ShortEventDescriptor;
pEvent->SetStartTime(SiEitEvent.getStartTime());
pEvent->SetDuration(SiEitEvent.getDuration());
pEvent->FixEpgBugs();
if (isPresentFollowing()) {
if (SiEitEvent.getRunningStatus() == SI::RunningStatusPausing || SiEitEvent.getRunningStatus() == SI::RunningStatusRunning)
pSchedule->SetPresentEvent(pEvent);
else if (SiEitEvent.getRunningStatus() == SI::RunningStatusStartsInAFewSeconds)
pSchedule->SetFollowingEvent(pEvent);
}
if (LinkChannels)
channel->SetLinkChannels(LinkChannels);
}
}
// --- cTDT ------------------------------------------------------------------
class cTDT : public SI::TDT {
private:
static cMutex mutex;
public:
cTDT(const u_char *Data);
};
cMutex cTDT::mutex;
cTDT::cTDT(const u_char *Data)
:SI::TDT(Data, false)
{
CheckParse();
time_t sattim = getTime();
time_t loctim = time(NULL);
if (abs(sattim - loctim) > 2) {
mutex.Lock();
isyslog("System Time = %s (%ld)\n", ctime(&loctim), loctim);
isyslog("Local Time = %s (%ld)\n", ctime(&sattim), sattim);
if (stime(&sattim) < 0)
esyslog("ERROR while setting system time: %m");
mutex.Unlock();
}
}
// --- cEitFilter ------------------------------------------------------------
cEitFilter::cEitFilter(void)
{
Set(0x12, 0x4E, 0xFE); // event info, actual(0x4E)/other(0x4F) TS, present/following
Set(0x12, 0x50, 0xF0); // event info, actual TS, schedule(0x50)/schedule for future days(0x5X)
Set(0x12, 0x60, 0xF0); // event info, other TS, schedule(0x60)/schedule for future days(0x6X)
Set(0x14, 0x70); // TDT
}
void cEitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
{
switch (Pid) {
case 0x12: {
cSchedulesLock SchedulesLock(true, 10);
cSchedules *Schedules = (cSchedules *)cSchedules::Schedules(SchedulesLock);
if (Schedules)
cEIT EIT(Schedules, Source(), Tid, Data);
}
break;
case 0x14: {
if (Setup.SetSystemTime && Setup.TimeTransponder && ISTRANSPONDER(Transponder(), Setup.TimeTransponder))
cTDT TDT(Data);
}
break;
}
}