X-Git-Url: https://git.cweiske.de/enigma2.git/blobdiff_plain/9276c315058a82481938d336912151f7ef58df1b..4074e687a61dd3de7bcb7319f023fd08af402cf3:/lib/dvb/epgcache.cpp diff --git a/lib/dvb/epgcache.cpp b/lib/dvb/epgcache.cpp index 28cd855d..59b88107 100644 --- a/lib/dvb/epgcache.cpp +++ b/lib/dvb/epgcache.cpp @@ -8,8 +8,146 @@ #include // for statfs // #include #include +#include +#include int eventData::CacheSize=0; +descriptorMap eventData::descriptors; +__u8 eventData::data[4108]; +extern const uint32_t crc32_table[256]; + +eventData::eventData(const eit_event_struct* e, int size, int type) + :ByteSize(size&0xFF), type(type&0xFF) +{ + if (!e) + return; + + __u32 descr[65]; + __u32 *pdescr=descr; + + __u8 *data = (__u8*)e; + int ptr=10; + int descriptors_length = (data[ptr++]&0x0F) << 8; + descriptors_length |= data[ptr++]; + while ( descriptors_length > 0 ) + { + __u8 *descr = data+ptr; + int descr_len = descr[1]+2; + + __u32 crc = 0; + int cnt=0; + while(cnt++ < descr_len) + crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ data[ptr++]) & 0xFF]; + + descriptorMap::iterator it = + descriptors.find(crc); + if ( it == descriptors.end() ) + { + CacheSize+=descr_len; + __u8 *d = new __u8[descr_len]; + memcpy(d, descr, descr_len); + descriptors[crc] = descriptorPair(1, d); + } + else + ++it->second.first; + + *pdescr++=crc; + descriptors_length -= descr_len; + } + ByteSize = 12+((pdescr-descr)*4); + EITdata = new __u8[ByteSize]; + CacheSize+=ByteSize; + memcpy(EITdata, (__u8*) e, 12); + memcpy(EITdata+12, descr, ByteSize-12); +} + +const eit_event_struct* eventData::get() const +{ + int pos = 12; + int tmp = ByteSize-12; + + memcpy(data, EITdata, 12); + __u32 *p = (__u32*)(EITdata+12); + while(tmp>0) + { + descriptorMap::iterator it = + descriptors.find(*p++); + if ( it != descriptors.end() ) + { + int b = it->second.second[1]+2; + memcpy(data+pos, it->second.second, b ); + pos += b; + } + tmp-=4; + } + + return (const eit_event_struct*)data; +} + +eventData::~eventData() +{ + if ( ByteSize ) + { + CacheSize-=ByteSize; + ByteSize-=12; + __u32 *d = (__u32*)(EITdata+12); + while(ByteSize) + { + descriptorMap::iterator it = + descriptors.find(*d++); + if ( it != descriptors.end() ) + { + descriptorPair &p = it->second; + if (!--p.first) // no more used descriptor + { + CacheSize -= it->second.second[1]; + delete [] it->second.second; // free descriptor memory + descriptors.erase(it); // remove entry from descriptor map + } + } + ByteSize-=4; + } + delete [] EITdata; + } +} + +void eventData::load(FILE *f) +{ + int size=0; + int id=0; + __u8 header[2]; + descriptorPair p; + fread(&size, sizeof(int), 1, f); + while(size) + { + fread(&id, sizeof(__u32), 1, f); + fread(&p.first, sizeof(int), 1, f); + fread(header, 2, 1, f); + int bytes = header[1]+2; + p.second = new __u8[bytes]; + p.second[0] = header[0]; + p.second[1] = header[1]; + fread(p.second+2, bytes-2, 1, f); + descriptors[id]=p; + --size; + CacheSize+=bytes; + } +} + +void eventData::save(FILE *f) +{ + int size=descriptors.size(); + descriptorMap::iterator it(descriptors.begin()); + fwrite(&size, sizeof(int), 1, f); + while(size) + { + fwrite(&it->first, sizeof(__u32), 1, f); + fwrite(&it->second.first, sizeof(int), 1, f); + fwrite(it->second.second, it->second.second[1]+2, 1, f); + ++it; + --size; + } +} eEPGCache* eEPGCache::instance; pthread_mutex_t eEPGCache::cache_lock= @@ -52,9 +190,13 @@ void eEPGCache::DVBChannelAdded(eDVBChannel *chan) { if ( chan ) { - eDebug("[eEPGCache] add channel %p", chan); +// eDebug("[eEPGCache] add channel %p", chan); channel_data *data = new channel_data(this); data->channel = chan; + data->prevChannelState = -1; +#ifdef ENABLE_PRIVATE_EPG + data->m_PrivatePid = -1; +#endif singleLock s(channel_map_lock); m_knownChannels.insert( std::pair(chan, data) ); chan->connectStateChange(slot(*this, &eEPGCache::DVBChannelStateChanged), data->m_stateChangedConn); @@ -77,7 +219,7 @@ void eEPGCache::DVBChannelRunning(iDVBChannel *chan) else { ePtr demux; - if ( data.channel->getDemux(demux) ) + if ( data.channel->getDemux(demux, 0) ) { eDebug("[eEPGCache] no demux!!"); return; @@ -90,21 +232,28 @@ void eEPGCache::DVBChannelRunning(iDVBChannel *chan) eDebug("[eEPGCache] couldnt initialize nownext reader!!"); return; } - data.m_NowNextReader->connectRead(slot(data, &eEPGCache::channel_data::readData), data.m_NowNextConn); + res = demux->createSectionReader( this, data.m_ScheduleReader ); if ( res ) { eDebug("[eEPGCache] couldnt initialize schedule reader!!"); return; } - data.m_ScheduleReader->connectRead(slot(data, &eEPGCache::channel_data::readData), data.m_ScheduleConn); + res = demux->createSectionReader( this, data.m_ScheduleOtherReader ); if ( res ) { eDebug("[eEPGCache] couldnt initialize schedule other reader!!"); return; } - data.m_ScheduleOtherReader->connectRead(slot(data, &eEPGCache::channel_data::readData), data.m_ScheduleOtherConn); +#ifdef ENABLE_PRIVATE_EPG + res = demux->createSectionReader( this, data.m_PrivateReader ); + if ( res ) + { + eDebug("[eEPGCache] couldnt initialize private reader!!"); + return; + } +#endif messages.send(Message(Message::startChannel, chan)); // -> gotMessage -> changedService } @@ -120,31 +269,31 @@ void eEPGCache::DVBChannelStateChanged(iDVBChannel *chan) { int state=0; chan->getState(state); - switch (state) + if ( it->second->prevChannelState != state ) { - case iDVBChannel::state_idle: - break; - case iDVBChannel::state_tuning: - break; - case iDVBChannel::state_unavailable: - break; - case iDVBChannel::state_ok: - { - eDebug("[eEPGCache] channel %p running", chan); - DVBChannelRunning(chan); - break; - } - case iDVBChannel::state_release: + switch (state) { - eDebug("[eEPGCache] remove channel %p", chan); - messages.send(Message(Message::leaveChannel, chan)); - while(!it->second->can_delete) - usleep(1000); - delete it->second; - m_knownChannels.erase(it); - // -> gotMessage -> abortEPG - break; + case iDVBChannel::state_ok: + { + eDebug("[eEPGCache] channel %p running", chan); + DVBChannelRunning(chan); + break; + } + case iDVBChannel::state_release: + { + eDebug("[eEPGCache] remove channel %p", chan); + messages.send(Message(Message::leaveChannel, chan)); + while(!it->second->can_delete) + usleep(1000); + delete it->second; + m_knownChannels.erase(it); + // -> gotMessage -> abortEPG + break; + } + default: // ignore all other events + return; } + it->second->prevChannelState = state; } } } @@ -370,6 +519,15 @@ void eEPGCache::flushEPG(const uniqueEPGKey & s) eventDB.erase(it); // TODO .. search corresponding channel for removed service and remove this channel from lastupdated map +#ifdef ENABLE_PRIVATE_EPG + contentMaps::iterator it = + content_time_tables.find(s); + if ( it != content_time_tables.end() ) + { + it->second.clear(); + content_time_tables.erase(it); + } +#endif } } else // clear complete EPG Cache @@ -385,6 +543,9 @@ void eEPGCache::flushEPG(const uniqueEPGKey & s) tmMap.clear(); } eventDB.clear(); +#ifdef ENABLE_PRIVATE_EPG + content_time_tables.clear(); +#endif channelLastUpdated.clear(); singleLock m(channel_map_lock); for (channelMapIterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it) @@ -399,20 +560,15 @@ void eEPGCache::cleanLoop() if (!eventDB.empty()) { eDebug("[EPGC] start cleanloop"); - const eit_event_struct* cur_event; - int duration; -// FIXME !!! TIME_CORRECTION time_t now = time(0)+eDVBLocalTimeHandler::getInstance()->difference(); for (eventCache::iterator DBIt = eventDB.begin(); DBIt != eventDB.end(); DBIt++) { + bool updated = false; for (timeMap::iterator It = DBIt->second.second.begin(); It != DBIt->second.second.end() && It->first < now;) { - cur_event = (*It->second).get(); - duration = fromBCD( cur_event->duration_1)*3600 + fromBCD(cur_event->duration_2)*60 + fromBCD(cur_event->duration_3); - - if ( now > (It->first+duration) ) // outdated normal entry (nvod references to) + if ( now > (It->first+It->second->getDuration()) ) // outdated normal entry (nvod references to) { // remove entry from eventMap eventMap::iterator b(DBIt->second.first.find(It->second->getEventID())); @@ -428,10 +584,37 @@ void eEPGCache::cleanLoop() delete It->second; DBIt->second.second.erase(It++); // eDebug("[EPGC] delete old event (timeMap)"); + updated = true; } else ++It; } +#ifdef ENABLE_PRIVATE_EPG + if ( updated ) + { + contentMaps::iterator x = + content_time_tables.find( DBIt->first ); + if ( x != content_time_tables.end() ) + { + timeMap &tmMap = eventDB[DBIt->first].second; + for ( contentMap::iterator i = x->second.begin(); i != x->second.end(); ) + { + for ( contentTimeMap::iterator it(i->second.begin()); + it != i->second.end(); ) + { + if ( tmMap.find(it->second.first) == tmMap.end() ) + i->second.erase(it++); + else + ++it; + } + if ( i->second.size() ) + ++i; + else + x->second.erase(i++); + } + } + } +#endif } eDebug("[EPGC] stop cleanloop"); eDebug("[EPGC] %i bytes for cache used", eventData::CacheSize); @@ -449,82 +632,6 @@ eEPGCache::~eEPGCache() delete It->second; } -Event *eEPGCache::lookupEvent(const eServiceReferenceDVB &service, int event_id, bool plain) -{ - singleLock s(cache_lock); - uniqueEPGKey key( service ); - - eventCache::iterator It = eventDB.find( key ); - if ( It != eventDB.end() && !It->second.first.empty() ) // entrys cached? - { - eventMap::iterator i( It->second.first.find( event_id )); - if ( i != It->second.first.end() ) - { - if ( service.getServiceType() == 4 ) // nvod ref - return lookupEvent( service, i->second->getStartTime(), plain ); - else if ( plain ) - // get plain data... not in Event Format !!! - // before use .. cast it to eit_event_struct* - return (Event*) i->second->get(); - else - return new Event( (uint8_t*)i->second->get() /*, (It->first.tsid<<16)|It->first.onid*/ ); - } - else - eDebug("event %04x not found in epgcache", event_id); - } - return 0; -} - -Event *eEPGCache::lookupEvent(const eServiceReferenceDVB &service, time_t t, bool plain ) -// if t == 0 we search the current event... -{ - singleLock s(cache_lock); - uniqueEPGKey key(service); - - // check if EPG for this service is ready... - eventCache::iterator It = eventDB.find( key ); - if ( It != eventDB.end() && !It->second.first.empty() ) // entrys cached ? - { - if (!t) - t = time(0)+eDVBLocalTimeHandler::getInstance()->difference(); - - timeMap::iterator i = It->second.second.lower_bound(t); - if ( i != It->second.second.end() ) - { - i--; - if ( i != It->second.second.end() ) - { - const eit_event_struct* eit_event = i->second->get(); - int duration = fromBCD(eit_event->duration_1)*3600+fromBCD(eit_event->duration_2)*60+fromBCD(eit_event->duration_3); - if ( t <= i->first+duration ) - { - if ( plain ) - // get plain data... not in Event Format !!! - // before use .. cast it to eit_event_struct* - return (Event*) i->second->get(); - return new Event( (uint8_t*)i->second->get() /*, (It->first.tsid<<16)|It->first.onid */ ); - } - } - } - - for ( eventMap::iterator i( It->second.first.begin() ); i != It->second.first.end(); i++) - { - const eit_event_struct* eit_event = i->second->get(); - int duration = fromBCD(eit_event->duration_1)*3600+fromBCD(eit_event->duration_2)*60+fromBCD(eit_event->duration_3); - time_t begTime = parseDVBtime( eit_event->start_time_1, eit_event->start_time_2, eit_event->start_time_3, eit_event->start_time_4, eit_event->start_time_5); - if ( t >= begTime && t <= begTime+duration) // then we have found - { - if ( plain ) - // get plain data... not in Event Format !!! - // before use .. cast it to eit_event_struct* - return (Event*) i->second->get(); - return new Event( (uint8_t*)i->second->get()/*, (It->first.tsid<<16)|It->first.onid*/ ); - } - } - } - return 0; -} - void eEPGCache::gotMessage( const Message &msg ) { switch (msg.type) @@ -553,6 +660,27 @@ void eEPGCache::gotMessage( const Message &msg ) case Message::quit: quit(0); break; +#ifdef ENABLE_PRIVATE_EPG + case Message::got_private_pid: + { + for (channelMapIterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it) + { + eDVBChannel *channel = (eDVBChannel*) it->first; + channel_data *data = it->second; + eDVBChannelID chid = channel->getChannelID(); + if ( chid.transport_stream_id.get() == msg.service.tsid && + chid.original_network_id.get() == msg.service.onid && + data->m_PrivatePid == -1 ) + { + data->m_PrivatePid = msg.pid; + data->m_PrivateService = msg.service; + data->startPrivateReader(msg.pid, -1); + break; + } + } + break; + } +#endif case Message::timeChanged: cleanLoop(); break; @@ -567,21 +695,22 @@ void eEPGCache::thread() nice(4); load(); cleanLoop(); - exec(); + runLoop(); save(); } void eEPGCache::load() { -#if 0 FILE *f = fopen("/hdd/epg.dat", "r"); if (f) { - unsigned char md5_saved[16]; - unsigned char md5[16]; int size=0; int cnt=0; +#if 0 + unsigned char md5_saved[16]; + unsigned char md5[16]; bool md5ok=false; + if (!md5_file("/hdd/epg.dat", 1, md5)) { FILE *f = fopen("/hdd/epg.dat.md5", "r"); @@ -594,41 +723,82 @@ void eEPGCache::load() } } if ( md5ok ) +#endif { - fread( &size, sizeof(int), 1, f); - while(size--) + char text1[13]; + fread( text1, 13, 1, f); + if ( !strncmp( text1, "ENIGMA_EPG_V4", 13) ) { - uniqueEPGKey key; - eventMap evMap; - timeMap tmMap; - int size=0; - fread( &key, sizeof(uniqueEPGKey), 1, f); fread( &size, sizeof(int), 1, f); while(size--) { - int len=0; - int type=0; - eventData *event=0; - fread( &type, sizeof(int), 1, f); - fread( &len, sizeof(int), 1, f); - event = new eventData(0, len, type); - fread( event->EITdata, len, 1, f); - evMap[ event->getEventID() ]=event; - tmMap[ event->getStartTime() ]=event; - ++cnt; + uniqueEPGKey key; + eventMap evMap; + timeMap tmMap; + int size=0; + fread( &key, sizeof(uniqueEPGKey), 1, f); + fread( &size, sizeof(int), 1, f); + while(size--) + { + __u8 len=0; + __u8 type=0; + eventData *event=0; + fread( &type, sizeof(__u8), 1, f); + fread( &len, sizeof(__u8), 1, f); + event = new eventData(0, len, type); + event->EITdata = new __u8[len]; + eventData::CacheSize+=len; + fread( event->EITdata, len, 1, f); + evMap[ event->getEventID() ]=event; + tmMap[ event->getStartTime() ]=event; + ++cnt; + } + eventDB[key]=std::pair(evMap,tmMap); } - eventDB[key]=std::pair(evMap,tmMap); + eventData::load(f); + eDebug("%d events read from /hdd/epg.dat", cnt); +#ifdef ENABLE_PRIVATE_EPG + char text2[11]; + fread( text2, 11, 1, f); + if ( !strncmp( text2, "PRIVATE_EPG", 11) ) + { + size=0; + fread( &size, sizeof(int), 1, f); + while(size--) + { + int size=0; + uniqueEPGKey key; + fread( &key, sizeof(uniqueEPGKey), 1, f); + fread( &size, sizeof(int), 1, f); + while(size--) + { + int size; + int content_id; + fread( &content_id, sizeof(int), 1, f); + fread( &size, sizeof(int), 1, f); + while(size--) + { + time_t time1, time2; + __u16 event_id; + fread( &time1, sizeof(time_t), 1, f); + fread( &time2, sizeof(time_t), 1, f); + fread( &event_id, sizeof(__u16), 1, f); + content_time_tables[key][content_id][time1]=std::pair(time2, event_id); + } + } + } + } +#endif // ENABLE_PRIVATE_EPG } - eDebug("%d events read from /hdd/epg.dat.md5", cnt); + else + eDebug("[EPGC] don't read old epg database"); + fclose(f); } - fclose(f); } -#endif } void eEPGCache::save() { -#if 0 struct statfs s; off64_t tmp; if (statfs("/hdd", &s)<0) @@ -653,6 +823,8 @@ void eEPGCache::save() int cnt=0; if ( f ) { + const char *text = "ENIGMA_EPG_V4"; + fwrite( text, 13, 1, f ); int size = eventDB.size(); fwrite( &size, sizeof(int), 1, f ); for (eventCache::iterator service_it(eventDB.begin()); service_it != eventDB.end(); ++service_it) @@ -663,15 +835,43 @@ void eEPGCache::save() fwrite( &size, sizeof(int), 1, f); for (timeMap::iterator time_it(timemap.begin()); time_it != timemap.end(); ++time_it) { - int len = time_it->second->ByteSize; - fwrite( &time_it->second->type, sizeof(int), 1, f ); - fwrite( &len, sizeof(int), 1, f); + __u8 len = time_it->second->ByteSize; + fwrite( &time_it->second->type, sizeof(__u8), 1, f ); + fwrite( &len, sizeof(__u8), 1, f); fwrite( time_it->second->EITdata, len, 1, f); ++cnt; } } eDebug("%d events written to /hdd/epg.dat", cnt); + eventData::save(f); +#ifdef ENABLE_PRIVATE_EPG + const char* text3 = "PRIVATE_EPG"; + fwrite( text3, 11, 1, f ); + size = content_time_tables.size(); + fwrite( &size, sizeof(int), 1, f); + for (contentMaps::iterator a = content_time_tables.begin(); a != content_time_tables.end(); ++a) + { + contentMap &content_time_table = a->second; + fwrite( &a->first, sizeof(uniqueEPGKey), 1, f); + int size = content_time_table.size(); + fwrite( &size, sizeof(int), 1, f); + for (contentMap::iterator i = content_time_table.begin(); i != content_time_table.end(); ++i ) + { + int size = i->second.size(); + fwrite( &i->first, sizeof(int), 1, f); + fwrite( &size, sizeof(int), 1, f); + for ( contentTimeMap::iterator it(i->second.begin()); + it != i->second.end(); ++it ) + { + fwrite( &it->first, sizeof(time_t), 1, f); + fwrite( &it->second.first, sizeof(time_t), 1, f); + fwrite( &it->second.second, sizeof(__u16), 1, f); + } + } + } +#endif fclose(f); +#if 0 unsigned char md5[16]; if (!md5_file("/hdd/epg.dat", 1, md5)) { @@ -682,16 +882,8 @@ void eEPGCache::save() fclose(f); } } - } #endif -} - -RESULT eEPGCache::getInstance(ePtr &ptr) -{ - ptr = instance; - if (!ptr) - return -1; - return 0; + } } eEPGCache::channel_data::channel_data(eEPGCache *ml) @@ -717,6 +909,9 @@ bool eEPGCache::channel_data::finishEPG() } singleLock l(cache->cache_lock); cache->channelLastUpdated[channel->getChannelID()] = time(0)+eDVBLocalTimeHandler::getInstance()->difference(); +#ifdef ENABLE_PRIVATE_EPG + if (seenPrivateSections.empty()) +#endif can_delete=1; return true; } @@ -742,16 +937,19 @@ void eEPGCache::channel_data::startEPG() mask.data[0] = 0x4E; mask.mask[0] = 0xFE; + m_NowNextReader->connectRead(slot(*this, &eEPGCache::channel_data::readData), m_NowNextConn); m_NowNextReader->start(mask); isRunning |= NOWNEXT; mask.data[0] = 0x50; mask.mask[0] = 0xF0; + m_ScheduleReader->connectRead(slot(*this, &eEPGCache::channel_data::readData), m_ScheduleConn); m_ScheduleReader->start(mask); isRunning |= SCHEDULE; mask.data[0] = 0x60; mask.mask[0] = 0xF0; + m_ScheduleOtherReader->connectRead(slot(*this, &eEPGCache::channel_data::readData), m_ScheduleOtherConn); m_ScheduleOtherReader->start(mask); isRunning |= SCHEDULE_OTHER; @@ -766,23 +964,25 @@ void eEPGCache::channel_data::abortNonAvail() { eDebug("[EPGC] abort non avail nownext reading"); isRunning &= ~eEPGCache::NOWNEXT; - if ( m_NowNextReader ) - m_NowNextReader->stop(); + m_NowNextReader->stop(); + m_NowNextConn=0; } if ( !(haveData&eEPGCache::SCHEDULE) && (isRunning&eEPGCache::SCHEDULE) ) { eDebug("[EPGC] abort non avail schedule reading"); isRunning &= ~SCHEDULE; m_ScheduleReader->stop(); + m_ScheduleConn=0; } if ( !(haveData&eEPGCache::SCHEDULE_OTHER) && (isRunning&eEPGCache::SCHEDULE_OTHER) ) { eDebug("[EPGC] abort non avail schedule_other reading"); isRunning &= ~SCHEDULE_OTHER; m_ScheduleOtherReader->stop(); + m_ScheduleOtherConn=0; } if ( isRunning ) - abortTimer.start(50000, true); + abortTimer.start(90000, true); else { ++state; @@ -791,6 +991,9 @@ void eEPGCache::channel_data::abortNonAvail() seenSections[i].clear(); calcedSections[i].clear(); } +#ifdef ENABLE_PRIVATE_EPG + if (seenPrivateSections.empty()) +#endif can_delete=1; } } @@ -827,24 +1030,30 @@ void eEPGCache::channel_data::abortEPG() eDebug("[EPGC] abort caching events !!"); if (isRunning & eEPGCache::SCHEDULE) { - isRunning &= eEPGCache::SCHEDULE; - if ( m_ScheduleReader ) - m_ScheduleReader->stop(); + isRunning &= ~eEPGCache::SCHEDULE; + m_ScheduleReader->stop(); + m_ScheduleConn=0; } if (isRunning & eEPGCache::NOWNEXT) { isRunning &= ~eEPGCache::NOWNEXT; - if ( m_NowNextReader ) - m_NowNextReader->stop(); + m_NowNextReader->stop(); + m_NowNextConn=0; } if (isRunning & SCHEDULE_OTHER) { isRunning &= ~eEPGCache::SCHEDULE_OTHER; - if ( m_ScheduleOtherReader ) - m_ScheduleOtherReader->stop(); + m_ScheduleOtherReader->stop(); + m_ScheduleOtherConn=0; } - can_delete=1; } +#ifdef ENABLE_PRIVATE_EPG + if (m_PrivateReader) + m_PrivateReader->stop(); + if (m_PrivateConn) + m_PrivateConn=0; +#endif + can_delete=1; } void eEPGCache::channel_data::readData( const __u8 *data) @@ -884,9 +1093,18 @@ void eEPGCache::channel_data::readData( const __u8 *data) eDebugNoNewLine("[EPGC] "); switch (source) { - case eEPGCache::NOWNEXT: eDebugNoNewLine("nownext");break; - case eEPGCache::SCHEDULE: eDebugNoNewLine("schedule");break; - case eEPGCache::SCHEDULE_OTHER: eDebugNoNewLine("schedule other");break; + case eEPGCache::NOWNEXT: + m_NowNextConn=0; + eDebugNoNewLine("nownext"); + break; + case eEPGCache::SCHEDULE: + m_ScheduleConn=0; + eDebugNoNewLine("schedule"); + break; + case eEPGCache::SCHEDULE_OTHER: + m_ScheduleOtherConn=0; + eDebugNoNewLine("schedule other"); + break; default: eDebugNoNewLine("unknown");break; } eDebug(" finished(%d)", time(0)+eDVBLocalTimeHandler::getInstance()->difference()); @@ -910,6 +1128,7 @@ void eEPGCache::channel_data::readData( const __u8 *data) if ( it == seenSections.end() ) { seenSections.insert(sectionNo); + calcedSections.insert(sectionNo); __u32 tmpval = sectionNo & 0xFFFFFF00; __u8 incr = source == NOWNEXT ? 1 : 8; for ( int i = 0; i <= eit->last_section_number; i+=incr ) @@ -927,3 +1146,772 @@ void eEPGCache::channel_data::readData( const __u8 *data) } } } + +RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, const eventData *&result, int direction) +// if t == -1 we search the current event... +{ + singleLock s(cache_lock); + uniqueEPGKey key(service); + + // check if EPG for this service is ready... + eventCache::iterator It = eventDB.find( key ); + if ( It != eventDB.end() && !It->second.first.empty() ) // entrys cached ? + { + if (t==-1) + t = time(0)+eDVBLocalTimeHandler::getInstance()->difference(); + timeMap::iterator i = direction <= 0 ? It->second.second.lower_bound(t) : // find > or equal + It->second.second.upper_bound(t); // just > + if ( i != It->second.second.end() ) + { + if ( direction < 0 || (direction == 0 && i->second->getStartTime() > t) ) + { + timeMap::iterator x = i; + --x; + if ( x != It->second.second.end() ) + { + time_t start_time = x->second->getStartTime(); + if (direction >= 0) + { + if (t < start_time) + return -1; + if (t > (start_time+x->second->getDuration())) + return -1; + } + i = x; + } + else + return -1; + } + result = i->second; + return 0; + } + } + return -1; +} + +RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, const eit_event_struct *&result, int direction) +{ + singleLock s(cache_lock); + const eventData *data=0; + RESULT ret = lookupEventTime(service, t, data, direction); + if ( !ret && data ) + result = data->get(); + return ret; +} + +RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, Event *& result, int direction) +{ + singleLock s(cache_lock); + const eventData *data=0; + RESULT ret = lookupEventTime(service, t, data, direction); + if ( !ret && data ) + result = new Event((uint8_t*)data->get()); + return ret; +} + +RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, ePtr &result, int direction) +{ + singleLock s(cache_lock); + const eventData *data=0; + RESULT ret = lookupEventTime(service, t, data, direction); + if ( !ret && data ) + { + Event ev((uint8_t*)data->get()); + result = new eServiceEvent(); + const eServiceReferenceDVB &ref = (const eServiceReferenceDVB&)service; + ret = result->parseFrom(&ev, (ref.getTransportStreamID().get()<<16)|ref.getOriginalNetworkID().get()); + } + return ret; +} + +RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, const eventData *&result ) +{ + singleLock s(cache_lock); + uniqueEPGKey key( service ); + + eventCache::iterator It = eventDB.find( key ); + if ( It != eventDB.end() && !It->second.first.empty() ) // entrys cached? + { + eventMap::iterator i( It->second.first.find( event_id )); + if ( i != It->second.first.end() ) + { + result = i->second; + return 0; + } + else + { + result = 0; + eDebug("event %04x not found in epgcache", event_id); + } + } + return -1; +} + +RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, const eit_event_struct *&result) +{ + singleLock s(cache_lock); + const eventData *data=0; + RESULT ret = lookupEventId(service, event_id, data); + if ( !ret && data ) + result = data->get(); + return ret; +} + +RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, Event *& result) +{ + singleLock s(cache_lock); + const eventData *data=0; + RESULT ret = lookupEventId(service, event_id, data); + if ( !ret && data ) + result = new Event((uint8_t*)data->get()); + return ret; +} + +RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, ePtr &result) +{ + singleLock s(cache_lock); + const eventData *data=0; + RESULT ret = lookupEventId(service, event_id, data); + if ( !ret && data ) + { + Event ev((uint8_t*)data->get()); + result = new eServiceEvent(); + const eServiceReferenceDVB &ref = (const eServiceReferenceDVB&)service; + ret = result->parseFrom(&ev, (ref.getTransportStreamID().get()<<16)|ref.getOriginalNetworkID().get()); + } + return ret; +} + +RESULT eEPGCache::startTimeQuery(const eServiceReference &service, time_t begin, int minutes) +{ + eventCache::iterator It = eventDB.find( service ); + if ( It != eventDB.end() && It->second.second.size() ) + { + m_timemap_end = minutes != -1 ? It->second.second.upper_bound(begin+minutes*60) : It->second.second.end(); + if ( begin != -1 ) + { + m_timemap_cursor = It->second.second.lower_bound(begin); + if ( m_timemap_cursor != It->second.second.end() ) + { + if ( m_timemap_cursor->second->getStartTime() != begin ) + { + timeMap::iterator x = m_timemap_cursor; + --x; + if ( x != It->second.second.end() ) + { + time_t start_time = x->second->getStartTime(); + if ( begin > start_time && begin < (start_time+x->second->getDuration())) + m_timemap_cursor = x; + } + } + } + } + else + m_timemap_cursor = It->second.second.begin(); + const eServiceReferenceDVB &ref = (const eServiceReferenceDVB&)service; + currentQueryTsidOnid = (ref.getTransportStreamID().get()<<16) | ref.getOriginalNetworkID().get(); + return 0; + } + return -1; +} + +RESULT eEPGCache::getNextTimeEntry(const eventData *& result) +{ + if ( m_timemap_cursor != m_timemap_end ) + { + result = m_timemap_cursor++->second; + return 0; + } + return -1; +} + +RESULT eEPGCache::getNextTimeEntry(const eit_event_struct *&result) +{ + if ( m_timemap_cursor != m_timemap_end ) + { + result = m_timemap_cursor++->second->get(); + return 0; + } + return -1; +} + +RESULT eEPGCache::getNextTimeEntry(Event *&result) +{ + if ( m_timemap_cursor != m_timemap_end ) + { + result = new Event((uint8_t*)m_timemap_cursor++->second->get()); + return 0; + } + return -1; +} + +RESULT eEPGCache::getNextTimeEntry(ePtr &result) +{ + if ( m_timemap_cursor != m_timemap_end ) + { + Event ev((uint8_t*)m_timemap_cursor++->second->get()); + result = new eServiceEvent(); + return result->parseFrom(&ev, currentQueryTsidOnid); + } + return -1; +} + +void fillTuple(PyObject *tuple, char *argstring, int argcount, PyObject *service, ePtr &ptr, PyObject *nowTime, PyObject *service_name ) +{ + PyObject *tmp=NULL; + int pos=0; + while(pos < argcount) + { + bool inc_refcount=false; + switch(argstring[pos]) + { + case 'I': // Event Id + tmp = ptr ? PyLong_FromLong(ptr->getEventId()) : NULL; + break; + case 'B': // Event Begin Time + tmp = ptr ? PyLong_FromLong(ptr->getBeginTime()) : NULL; + break; + case 'D': // Event Duration + tmp = ptr ? PyLong_FromLong(ptr->getDuration()) : NULL; + break; + case 'T': // Event Title + tmp = ptr ? PyString_FromString(ptr->getEventName().c_str()) : NULL; + break; + case 'S': // Event Short Description + tmp = ptr ? PyString_FromString(ptr->getShortDescription().c_str()) : NULL; + break; + case 'E': // Event Extended Description + tmp = ptr ? PyString_FromString(ptr->getExtendedDescription().c_str()) : NULL; + break; + case 'C': // Current Time + tmp = nowTime; + inc_refcount = true; + break; + case 'R': // service reference string + tmp = service; + inc_refcount = true; + break; + case 'N': // service name + tmp = service_name; + inc_refcount = true; + } + if (!tmp) + { + tmp = Py_None; + inc_refcount = true; + } + if (inc_refcount) + Py_INCREF(tmp); + PyTuple_SET_ITEM(tuple, pos++, tmp); + } +} + +PyObject *handleEvent(ePtr &ptr, PyObject *dest_list, char* argstring, int argcount, PyObject *service, PyObject *nowTime, PyObject *service_name, PyObject *convertFunc, PyObject *convertFuncArgs) +{ + if (convertFunc) + { + fillTuple(convertFuncArgs, argstring, argcount, service, ptr, nowTime, service_name); + PyObject *result = PyObject_CallObject(convertFunc, convertFuncArgs); + if (result == NULL) + { + if (service_name) + Py_DECREF(service_name); + if (nowTime) + Py_DECREF(nowTime); + Py_DECREF(convertFuncArgs); + Py_DECREF(dest_list); + return result; + } + PyList_Append(dest_list, result); + Py_DECREF(result); + } + else + { + PyObject *tuple = PyTuple_New(argcount); + fillTuple(tuple, argstring, argcount, service, ptr, nowTime, service_name); + PyList_Append(dest_list, tuple); + Py_DECREF(tuple); + } + return 0; +} + +// here we get a list with tuples +// first tuple entry is the servicereference +// the second is the type of query (0 = time, 1 = event_id) +// the third +// when type is eventid it is the event_id +// when type is time then it is the start_time ( 0 for now_time ) +// the fourth is the end_time .. ( optional ) + +/* argv is a python string + I = Event Id + B = Event Begin Time + D = Event Duration + T = Event Title + S = Event Short Description + E = Event Extended Description + C = Current Time + R = Service Reference + N = Service Name +*/ + +PyObject *eEPGCache::lookupEvent(PyObject *list, PyObject *convertFunc) +{ + PyObject *convertFuncArgs=NULL; + int argcount=0; + char *argstring=NULL; + if (!PyList_Check(list)) + { + PyErr_SetString(PyExc_StandardError, + "type error"); + eDebug("no list"); + return NULL; + } + int listIt=0; + int listSize=PyList_Size(list); + if (!listSize) + { + PyErr_SetString(PyExc_StandardError, + "not params given"); + eDebug("not params given"); + return NULL; + } + else + { + PyObject *argv=PyList_GET_ITEM(list, 0); // borrowed reference! + if (PyString_Check(argv)) + { + argstring = PyString_AS_STRING(argv); + ++listIt; + } + else + argstring = "I"; // just event id as default + argcount = strlen(argstring); +// eDebug("have %d args('%s')", argcount, argstring); + } + if (convertFunc) + { + if (!PyCallable_Check(convertFunc)) + { + PyErr_SetString(PyExc_StandardError, + "convertFunc must be callable"); + eDebug("convertFunc is not callable"); + return NULL; + } + convertFuncArgs = PyTuple_New(argcount); + } + + PyObject *nowTime = strchr(argstring, 'C') ? + PyLong_FromLong(time(0)+eDVBLocalTimeHandler::getInstance()->difference()) : + NULL; + + bool must_get_service_name = strchr(argstring, 'N') ? true : false; + + // create dest list + PyObject *dest_list=PyList_New(0); + while(listSize > listIt) + { + PyObject *item=PyList_GET_ITEM(list, listIt++); // borrowed reference! + if (PyTuple_Check(item)) + { + int type=0; + long event_id=-1; + time_t stime=-1; + int minutes=0; + int tupleSize=PyTuple_Size(item); + int tupleIt=0; + PyObject *service=NULL; + while(tupleSize > tupleIt) // parse query args + { + PyObject *entry=PyTuple_GET_ITEM(item, tupleIt); // borrowed reference! + switch(tupleIt++) + { + case 0: + { + if (!PyString_Check(entry)) + { + eDebug("tuple entry 0 is no a string"); + continue; + } + service = entry; + break; + } + case 1: + type=PyInt_AsLong(entry); + if (type < -1 || type > 2) + { + eDebug("unknown type %d", type); + continue; + } + break; + case 2: + event_id=stime=PyInt_AsLong(entry); + break; + case 3: + minutes=PyInt_AsLong(entry); + break; + default: + eDebug("unneeded extra argument"); + break; + } + } + eServiceReference ref(PyString_AS_STRING(service)); + if (ref.type != eServiceReference::idDVB) + { + eDebug("service reference for epg query is not valid"); + continue; + } + PyObject *service_name=NULL; + if (must_get_service_name) + { + ePtr sptr; + eServiceCenterPtr service_center; + eServiceCenter::getPrivInstance(service_center); + if (service_center) + { + service_center->info(ref, sptr); + if (sptr) + { + std::string name; + sptr->getName(ref, name); + if (name.length()) + service_name = PyString_FromString(name.c_str()); + } + } + if (!service_name) + service_name = PyString_FromString(""); + } + if (minutes) + { + Lock(); + if (!startTimeQuery(ref, stime, minutes)) + { + ePtr ptr; + while (!getNextTimeEntry(ptr)) + { + PyObject *ret = handleEvent(ptr, dest_list, argstring, argcount, service, nowTime, service_name, convertFunc, convertFuncArgs); + if (ret) + return ret; + } + } + Unlock(); + } + else + { + ePtr ptr; + if (stime) + { + if (type == 2) + lookupEventId(ref, event_id, ptr); + else + lookupEventTime(ref, stime, ptr, type); + } + PyObject *ret = handleEvent(ptr, dest_list, argstring, argcount, service, nowTime, service_name, convertFunc, convertFuncArgs); + if (ret) + return ret; + } + if (service_name) + Py_DECREF(service_name); + } + } + if (convertFuncArgs) + Py_DECREF(convertFuncArgs); + if (nowTime) + Py_DECREF(nowTime); + return dest_list; +} + +#ifdef ENABLE_PRIVATE_EPG +#include +#include +#include + +void eEPGCache::PMTready(eDVBServicePMTHandler *pmthandler) +{ + ePtr > ptr; + if (!pmthandler->getPMT(ptr) && ptr) + { + std::vector::const_iterator i; + for (i = ptr->getSections().begin(); i != ptr->getSections().end(); ++i) + { + const ProgramMapSection &pmt = **i; + + ElementaryStreamInfoConstIterator es; + for (es = pmt.getEsInfo()->begin(); es != pmt.getEsInfo()->end(); ++es) + { + int tmp=0; + switch ((*es)->getType()) + { + case 0x05: // private + for (DescriptorConstIterator desc = (*es)->getDescriptors()->begin(); + desc != (*es)->getDescriptors()->end(); ++desc) + { + switch ((*desc)->getTag()) + { + case PRIVATE_DATA_SPECIFIER_DESCRIPTOR: + if (((PrivateDataSpecifierDescriptor*)(*desc))->getPrivateDataSpecifier() == 190) + tmp |= 1; + break; + case 0x90: + { + UnknownDescriptor *descr = (UnknownDescriptor*)*desc; + int descr_len = descr->getLength(); + if (descr_len == 4) + { + uint8_t data[descr_len+2]; + descr->writeToBuffer(data); + if ( !data[2] && !data[3] && data[4] == 0xFF && data[5] == 0xFF ) + tmp |= 2; + } + break; + } + default: + break; + } + } + default: + break; + } + if (tmp==3) + { + eServiceReferenceDVB ref; + if (!pmthandler->getService(ref)) + { + int pid = (*es)->getPid(); + messages.send(Message(Message::got_private_pid, ref, pid)); + return; + } + } + } + } + } + else + eDebug("PMTready but no pmt!!"); +} + +struct date_time +{ + __u8 data[5]; + time_t tm; + date_time( const date_time &a ) + { + memcpy(data, a.data, 5); + tm = a.tm; + } + date_time( const __u8 data[5]) + { + memcpy(this->data, data, 5); + tm = parseDVBtime(data[0], data[1], data[2], data[3], data[4]); + } + date_time() + { + } + const __u8& operator[](int pos) const + { + return data[pos]; + } +}; + +struct less_datetime +{ + bool operator()( const date_time &a, const date_time &b ) const + { + return abs(a.tm-b.tm) < 360 ? false : a.tm < b.tm; + } +}; + +void eEPGCache::privateSectionRead(const uniqueEPGKey ¤t_service, const __u8 *data) +{ + contentMap &content_time_table = content_time_tables[current_service]; + singleLock s(cache_lock); + std::map< date_time, std::list, less_datetime > start_times; + eventMap &evMap = eventDB[current_service].first; + timeMap &tmMap = eventDB[current_service].second; + int ptr=8; + int content_id = data[ptr++] << 24; + content_id |= data[ptr++] << 16; + content_id |= data[ptr++] << 8; + content_id |= data[ptr++]; + + contentTimeMap &time_event_map = + content_time_table[content_id]; + for ( contentTimeMap::iterator it( time_event_map.begin() ); + it != time_event_map.end(); ++it ) + { + eventMap::iterator evIt( evMap.find(it->second.second) ); + if ( evIt != evMap.end() ) + { + delete evIt->second; + evMap.erase(evIt); + } + tmMap.erase(it->second.first); + } + time_event_map.clear(); + + __u8 duration[3]; + memcpy(duration, data+ptr, 3); + ptr+=3; + int duration_sec = + fromBCD(duration[0])*3600+fromBCD(duration[1])*60+fromBCD(duration[2]); + + const __u8 *descriptors[65]; + const __u8 **pdescr = descriptors; + + int descriptors_length = (data[ptr++]&0x0F) << 8; + descriptors_length |= data[ptr++]; + while ( descriptors_length > 0 ) + { + int descr_type = data[ptr]; + int descr_len = data[ptr+1]; + descriptors_length -= (descr_len+2); + if ( descr_type == 0xf2 ) + { + ptr+=2; + int tsid = data[ptr++] << 8; + tsid |= data[ptr++]; + int onid = data[ptr++] << 8; + onid |= data[ptr++]; + int sid = data[ptr++] << 8; + sid |= data[ptr++]; + uniqueEPGKey service( sid, onid, tsid ); + descr_len -= 6; + while( descr_len > 0 ) + { + __u8 datetime[5]; + datetime[0] = data[ptr++]; + datetime[1] = data[ptr++]; + int tmp_len = data[ptr++]; + descr_len -= 3; + while( tmp_len > 0 ) + { + memcpy(datetime+2, data+ptr, 3); + ptr+=3; + descr_len -= 3; + tmp_len -= 3; + start_times[datetime].push_back(service); + } + } + } + else + { + *pdescr++=data+ptr; + ptr += 2; + ptr += descr_len; + } + } + __u8 event[4098]; + eit_event_struct *ev_struct = (eit_event_struct*) event; + ev_struct->running_status = 0; + ev_struct->free_CA_mode = 1; + memcpy(event+7, duration, 3); + ptr = 12; + const __u8 **d=descriptors; + while ( d < pdescr ) + { + memcpy(event+ptr, *d, ((*d)[1])+2); + ptr+=(*d++)[1]; + ptr+=2; + } + for ( std::map< date_time, std::list >::iterator it(start_times.begin()); it != start_times.end(); ++it ) + { + time_t now = eDVBLocalTimeHandler::getInstance()->nowTime(); + if ( (it->first.tm + duration_sec) < now ) + continue; + memcpy(event+2, it->first.data, 5); + int bptr = ptr; + int cnt=0; + for (std::list::iterator i(it->second.begin()); i != it->second.end(); ++i) + { + event[bptr++] = 0x4A; + __u8 *len = event+(bptr++); + event[bptr++] = (i->tsid & 0xFF00) >> 8; + event[bptr++] = (i->tsid & 0xFF); + event[bptr++] = (i->onid & 0xFF00) >> 8; + event[bptr++] = (i->onid & 0xFF); + event[bptr++] = (i->sid & 0xFF00) >> 8; + event[bptr++] = (i->sid & 0xFF); + event[bptr++] = 0xB0; + bptr += sprintf((char*)(event+bptr), "Option %d", ++cnt); + *len = ((event+bptr) - len)-1; + } + int llen = bptr - 12; + ev_struct->descriptors_loop_length_hi = (llen & 0xF00) >> 8; + ev_struct->descriptors_loop_length_lo = (llen & 0xFF); + + time_t stime = it->first.tm; + while( tmMap.find(stime) != tmMap.end() ) + ++stime; + event[6] += (stime - it->first.tm); + __u16 event_id = 0; + while( evMap.find(event_id) != evMap.end() ) + ++event_id; + event[0] = (event_id & 0xFF00) >> 8; + event[1] = (event_id & 0xFF); + time_event_map[it->first.tm]=std::pair(stime, event_id); + eventData *d = new eventData( ev_struct, bptr, eEPGCache::SCHEDULE ); + evMap[event_id] = d; + tmMap[stime] = d; + } +} + +void eEPGCache::channel_data::startPrivateReader(int pid, int version) +{ + eDVBSectionFilterMask mask; + memset(&mask, 0, sizeof(mask)); + mask.pid = pid; + mask.flags = eDVBSectionFilterMask::rfCRC; + mask.data[0] = 0xA0; + mask.mask[0] = 0xFF; + eDebug("start privatefilter for pid %04x and version %d", pid, version); + if (version != -1) + { + mask.data[3] = version << 1; + mask.mask[3] = 0x3E; + mask.mode[3] = 0x3E; + } + seenPrivateSections.clear(); + m_PrivateReader->connectRead(slot(*this, &eEPGCache::channel_data::readPrivateData), m_PrivateConn); + m_PrivateReader->start(mask); +#ifdef NEED_DEMUX_WORKAROUND + m_PrevVersion=version; +#endif +} + +void eEPGCache::channel_data::readPrivateData( const __u8 *data) +{ + if (!data) + eDebug("get Null pointer from section reader !!"); + else + { + if ( seenPrivateSections.find( data[6] ) == seenPrivateSections.end() ) + { +#ifdef NEED_DEMUX_WORKAROUND + int version = data[5]; + version = ((version & 0x3E) >> 1); + can_delete = 0; + if ( m_PrevVersion != version ) + { + cache->privateSectionRead(m_PrivateService, data); + seenPrivateSections.insert(data[6]); + } + else + eDebug("ignore"); +#else + can_delete = 0; + cache->privateSectionRead(m_PrivateService, data); + seenPrivateSections.insert(data[6]); +#endif + } + if ( seenPrivateSections.size() == (unsigned int)(data[7] + 1) ) + { + eDebug("[EPGC] private finished"); + if (!isRunning) + can_delete = 1; + int version = data[5]; + version = ((version & 0x3E) >> 1); + startPrivateReader(m_PrivatePid, version); + } + } +} + +#endif // ENABLE_PRIVATE_EPG