mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
- 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.
263 lines
12 KiB
C
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;
|
|
}
|
|
}
|