X-Git-Url: https://git.cweiske.de/enigma2.git/blobdiff_plain/aa3d1162bef321bd2d0effdafce6eb080660a7ca..223c807af74a9a402fa5a649c3e7acafb8caa8e3:/lib/dvb/epgcache.cpp diff --git a/lib/dvb/epgcache.cpp b/lib/dvb/epgcache.cpp index 634aead3..7b94a1a7 100644 --- a/lib/dvb/epgcache.cpp +++ b/lib/dvb/epgcache.cpp @@ -6,32 +6,170 @@ #include #include // for usleep #include // for statfs -#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= PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; +pthread_mutex_t eEPGCache::channel_map_lock= + PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; DEFINE_REF(eEPGCache) eEPGCache::eEPGCache() - :messages(this,1), back_messages(this,1) ,paused(0) - ,CleanTimer(this), zapTimer(this), abortTimer(this) + :messages(this,1), cleanTimer(this)//, paused(0) { eDebug("[EPGC] Initialized EPGCache"); - isRunning=0; CONNECT(messages.recv_msg, eEPGCache::gotMessage); - CONNECT(back_messages.recv_msg, eEPGCache::gotBackMessage); -// CONNECT(eDVB::getInstance()->switchedService, eEPGCache::enterService); -// CONNECT(eDVB::getInstance()->leaveService, eEPGCache::leaveService); CONNECT(eDVBLocalTimeHandler::getInstance()->m_timeUpdated, eEPGCache::timeUpdated); - CONNECT(zapTimer.timeout, eEPGCache::startEPG); - CONNECT(CleanTimer.timeout, eEPGCache::cleanLoop); - CONNECT(abortTimer.timeout, eEPGCache::abortNonAvail); + CONNECT(cleanTimer.timeout, eEPGCache::cleanLoop); + + ePtr res_mgr; + eDVBResourceManager::getInstance(res_mgr); + if (!res_mgr) + eDebug("[eEPGCache] no resource manager !!!!!!!"); + else + res_mgr->connectChannelAdded(slot(*this,&eEPGCache::DVBChannelAdded), m_chanAddedConn); instance=this; } @@ -46,16 +184,110 @@ void eEPGCache::timeUpdated() messages.send(Message(Message::timeChanged)); } -int eEPGCache::sectionRead(const __u8 *data, int source) +void eEPGCache::DVBChannelAdded(eDVBChannel *chan) +{ + if ( chan ) + { +// eDebug("[eEPGCache] add channel %p", chan); + channel_data *data = new channel_data(this); + data->channel = chan; + singleLock s(channel_map_lock); + m_knownChannels.insert( std::pair(chan, data) ); + chan->connectStateChange(slot(*this, &eEPGCache::DVBChannelStateChanged), data->m_stateChangedConn); + } +} + +void eEPGCache::DVBChannelRunning(iDVBChannel *chan) +{ + singleLock s(channel_map_lock); + channelMapIterator it = + m_knownChannels.find(chan); + if ( it == m_knownChannels.end() ) + eDebug("[eEPGCache] will start non existing channel %p !!!", chan); + else + { + channel_data &data = *it->second; + ePtr res_mgr; + if ( eDVBResourceManager::getInstance( res_mgr ) ) + eDebug("[eEPGCache] no res manager!!"); + else + { + ePtr demux; + if ( data.channel->getDemux(demux, 0) ) + { + eDebug("[eEPGCache] no demux!!"); + return; + } + else + { + RESULT res = demux->createSectionReader( this, data.m_NowNextReader ); + if ( res ) + { + eDebug("[eEPGCache] couldnt initialize nownext reader!!"); + return; + } + + res = demux->createSectionReader( this, data.m_ScheduleReader ); + if ( res ) + { + eDebug("[eEPGCache] couldnt initialize schedule reader!!"); + return; + } + + res = demux->createSectionReader( this, data.m_ScheduleOtherReader ); + if ( res ) + { + eDebug("[eEPGCache] couldnt initialize schedule other reader!!"); + return; + } + + messages.send(Message(Message::startChannel, chan)); + // -> gotMessage -> changedService + } + } + } +} + +void eEPGCache::DVBChannelStateChanged(iDVBChannel *chan) +{ + channelMapIterator it = + m_knownChannels.find(chan); + if ( it != m_knownChannels.end() ) + { + int state=0; + chan->getState(state); + switch (state) + { + 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; + } + } + } +} + +void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel) { eit_t *eit = (eit_t*) data; int len=HILO(eit->section_length)-1;//+3-4; int ptr=EIT_SIZE; if ( ptr >= len ) - return 0; + return; - // // This fixed the EPG on the Multichoice irdeto systems // the EIT packet is non-compliant.. their EIT packet stinks if ( data[ptr-1] < 0x40 ) @@ -67,26 +299,12 @@ int eEPGCache::sectionRead(const __u8 *data, int source) int duration; time_t TM = 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); -// FIXME !!! TIME CORRECTION ! time_t now = time(0)+eDVBLocalTimeHandler::getInstance()->difference(); if ( TM != 3599 && TM > -1) - { - switch(source) - { - case NOWNEXT: - haveData |= 2; - break; - case SCHEDULE: - haveData |= 1; - break; - case SCHEDULE_OTHER: - haveData |= 4; - break; - } - } + channel->haveData |= source; - Lock(); + singleLock s(cache_lock); // hier wird immer eine eventMap zurück gegeben.. entweder eine vorhandene.. // oder eine durch [] erzeugte std::pair &servicemap = eventDB[service]; @@ -120,7 +338,7 @@ int eEPGCache::sectionRead(const __u8 *data, int source) int ev_erase_count = 0; int tm_erase_count = 0; -// search in eventmap + // search in eventmap eventMap::iterator ev_it = servicemap.first.find(event_id); @@ -129,7 +347,8 @@ int eEPGCache::sectionRead(const __u8 *data, int source) { if ( source > ev_it->second->type ) // update needed ? goto next; // when not.. the skip this entry -// search this event in timemap + + // search this event in timemap timeMap::iterator tm_it_tmp = servicemap.second.find(ev_it->second->getStartTime()); @@ -154,21 +373,20 @@ int eEPGCache::sectionRead(const __u8 *data, int source) } } -// search in timemap, for check of a case if new time has coincided with time of other event -// or event was is not found in eventmap + // search in timemap, for check of a case if new time has coincided with time of other event + // or event was is not found in eventmap timeMap::iterator tm_it = servicemap.second.find(TM); if ( tm_it != servicemap.second.end() ) { - -// i think, if event is not found on eventmap, but found on timemap updating nevertheless demands + // i think, if event is not found on eventmap, but found on timemap updating nevertheless demands #if 0 if ( source > tm_it->second->type && tm_erase_count == 0 ) // update needed ? goto next; // when not.. the skip this entry #endif -// search this time in eventmap + // search this time in eventmap eventMap::iterator ev_it_tmp = servicemap.first.find(tm_it->second->getEventID()); @@ -250,8 +468,8 @@ next: i=0; for (timeMap::iterator it(servicemap.second.begin()) ; it != servicemap.second.end(); ++it ) - fprintf(f, "%d(key %d) -> time %d, event_id %d, data %p\n", - i++, (int)it->first, (int)it->second->getStartTime(), (int)it->second->getEventID(), it->second ); + fprintf(f, "%d(key %d) -> time %d, event_id %d, data %p\n", + i++, (int)it->first, (int)it->second->getStartTime(), (int)it->second->getEventID(), it->second ); fclose(f); eFatal("(1)map sizes not equal :( sid %04x tsid %04x onid %04x size %d size2 %d", @@ -262,68 +480,12 @@ next: ptr += eit_event_size; eit_event=(eit_event_struct*)(((__u8*)eit_event)+eit_event_size); } - - tmpMap::iterator it = temp.find( service ); - if ( it != temp.end() ) - { - if ( source > it->second.second ) - { - it->second.first=now; - it->second.second=source; - } - } - else - temp[service] = std::pair< time_t, int> (now, source); - - Unlock(); - - return 0; -} - -bool eEPGCache::finishEPG() -{ - if (!isRunning) // epg ready - { - eDebug("[EPGC] stop caching events"); - zapTimer.start(UPDATE_INTERVAL, 1); - eDebug("[EPGC] next update in %i min", UPDATE_INTERVAL / 60000); - - singleLock l(cache_lock); - tmpMap::iterator It = temp.begin(); - abortTimer.stop(); - - while (It != temp.end()) - { -// eDebug("sid = %02x, onid = %02x, type %d", It->first.sid, It->first.onid, It->second.second ); - if ( It->second.second == SCHEDULE - || ( It->second.second == NOWNEXT && !(haveData&1) ) - ) - { -// eDebug("ADD to last updated Map"); - serviceLastUpdated[It->first]=It->second.first; - } - if ( eventDB.find( It->first ) == eventDB.end() ) - { -// eDebug("REMOVE from update Map"); - temp.erase(It++); - } - else - It++; - } - if (!eventDB[current_service].first.empty()) - /*emit*/ EPGAvail(1); - - /*emit*/ EPGUpdated(); - - return true; - } - return false; } void eEPGCache::flushEPG(const uniqueEPGKey & s) { eDebug("[EPGC] flushEPG %d", (int)(bool)s); - Lock(); + singleLock l(cache_lock); if (s) // clear only this service { eventCache::iterator it = eventDB.find(s); @@ -336,10 +498,8 @@ void eEPGCache::flushEPG(const uniqueEPGKey & s) delete i->second; evMap.clear(); eventDB.erase(it); - updateMap::iterator u = serviceLastUpdated.find(s); - if ( u != serviceLastUpdated.end() ) - serviceLastUpdated.erase(u); - startEPG(); + + // TODO .. search corresponding channel for removed service and remove this channel from lastupdated map } } else // clear complete EPG Cache @@ -354,40 +514,29 @@ void eEPGCache::flushEPG(const uniqueEPGKey & s) evMap.clear(); tmMap.clear(); } - serviceLastUpdated.clear(); eventDB.clear(); - startEPG(); + channelLastUpdated.clear(); + singleLock m(channel_map_lock); + for (channelMapIterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it) + it->second->startEPG(); } eDebug("[EPGC] %i bytes for cache used", eventData::CacheSize); - Unlock(); } void eEPGCache::cleanLoop() { singleLock s(cache_lock); - if ( isRunning ) - { - CleanTimer.start(5000,true); - eDebug("[EPGC] schedule cleanloop"); - return; - } - if (!eventDB.empty() && !paused ) + 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++) { 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())); @@ -403,555 +552,651 @@ void eEPGCache::cleanLoop() delete It->second; DBIt->second.second.erase(It++); // eDebug("[EPGC] delete old event (timeMap)"); - - // add this (changed) service to temp map... - if ( temp.find(DBIt->first) == temp.end() ) - temp[DBIt->first]=std::pair(now, NOWNEXT); } else ++It; } - if ( DBIt->second.second.size() < 2 ) - // less than two events for this service in cache.. - { - updateMap::iterator u = serviceLastUpdated.find(DBIt->first); - if ( u != serviceLastUpdated.end() ) - { - // remove from lastupdated map.. - serviceLastUpdated.erase(u); - // current service? - if ( DBIt->first == current_service ) - { - // immediate .. after leave cleanloop - // update epgdata for this service - zapTimer.start(0,true); - } - } - } } - - if (temp.size()) - /*emit*/ EPGUpdated(); - eDebug("[EPGC] stop cleanloop"); eDebug("[EPGC] %i bytes for cache used", eventData::CacheSize); } - CleanTimer.start(CLEAN_INTERVAL,true); + cleanTimer.start(CLEAN_INTERVAL,true); } eEPGCache::~eEPGCache() { messages.send(Message::quit); kill(); // waiting for thread shutdown - Lock(); + singleLock s(cache_lock); for (eventCache::iterator evIt = eventDB.begin(); evIt != eventDB.end(); evIt++) for (eventMap::iterator It = evIt->second.first.begin(); It != evIt->second.first.end(); It++) delete It->second; - Unlock(); } -Event *eEPGCache::lookupEvent(const eServiceReferenceDVB &service, int event_id, bool plain) +void eEPGCache::gotMessage( const Message &msg ) { - singleLock s(cache_lock); - uniqueEPGKey key( service ); - - eventCache::iterator It = eventDB.find( key ); - if ( It != eventDB.end() && !It->second.first.empty() ) // entrys cached? + switch (msg.type) { - eventMap::iterator i( It->second.first.find( event_id )); - if ( i != It->second.first.end() ) + case Message::flush: + flushEPG(msg.service); + break; + case Message::startChannel: { - 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*/ ); + singleLock s(channel_map_lock); + channelMapIterator channel = + m_knownChannels.find(msg.channel); + if ( channel != m_knownChannels.end() ) + channel->second->startChannel(); + break; } - else - eDebug("event %04x not found in epgcache", event_id); + case Message::leaveChannel: + { + singleLock s(channel_map_lock); + channelMapIterator channel = + m_knownChannels.find(msg.channel); + if ( channel != m_knownChannels.end() ) + channel->second->abortEPG(); + break; + } + case Message::quit: + quit(0); + break; + case Message::timeChanged: + cleanLoop(); + break; + default: + eDebug("unhandled EPGCache Message!!"); + break; } - return 0; } -Event *eEPGCache::lookupEvent(const eServiceReferenceDVB &service, time_t t, bool plain ) -// if t == 0 we search the current event... +void eEPGCache::thread() { - singleLock s(cache_lock); - uniqueEPGKey key(service); + nice(4); + load(); + cleanLoop(); + exec(); + save(); +} - // check if EPG for this service is ready... - eventCache::iterator It = eventDB.find( key ); - if ( It != eventDB.end() && !It->second.first.empty() ) // entrys cached ? +void eEPGCache::load() +{ +#if 0 + FILE *f = fopen("/hdd/epg.dat", "r"); + if (f) { - if (!t) - t = time(0)+eDVBLocalTimeHandler::getInstance()->difference(); - - timeMap::iterator i = It->second.second.lower_bound(t); - if ( i != It->second.second.end() ) + unsigned char md5_saved[16]; + unsigned char md5[16]; + int size=0; + int cnt=0; + bool md5ok=false; + if (!md5_file("/hdd/epg.dat", 1, md5)) { - i--; - if ( i != It->second.second.end() ) + FILE *f = fopen("/hdd/epg.dat.md5", "r"); + if (f) { - 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 */ ); - } + fread( md5_saved, 16, 1, f); + fclose(f); + if ( !memcmp(md5_saved, md5, 16) ) + md5ok=true; } } - - for ( eventMap::iterator i( It->second.first.begin() ); i != It->second.first.end(); i++) + if ( md5ok ) { - 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 + char text1[13]; + fread( text1, 13, 1, f); + if ( !strncmp( text1, "ENIGMA_EPG_V4", 13) ) { - 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*/ ); + fread( &size, sizeof(int), 1, f); + while(size--) + { + 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); + } + eventData::load(f); + eDebug("%d events read from /hdd/epg.dat", cnt); } + else + eDebug("[EPGC] don't read old epg database"); + fclose(f); } } - return 0; -} - -void eEPGCache::pauseEPG() -{ - if (!paused) - { - abortEPG(); - eDebug("[EPGC] paused]"); - paused=1; - } +#endif } -void eEPGCache::restartEPG() +void eEPGCache::save() { - if (paused) +#if 0 + struct statfs s; + off64_t tmp; + if (statfs("/hdd", &s)<0) + tmp=0; + else { - isRunning=0; - eDebug("[EPGC] restarted"); - paused--; - if (paused) - { - paused = 0; - startEPG(); // updateEPG - } - cleanLoop(); + tmp=s.f_blocks; + tmp*=s.f_bsize; } -} -void eEPGCache::startEPG() -{ - if (paused) // called from the updateTimer during pause... - { - paused++; + // prevent writes to builtin flash + if ( tmp < 1024*1024*50 ) // storage size < 50MB return; - } - if (eDVBLocalTimeHandler::getInstance()->ready()) - { - Lock(); - temp.clear(); - Unlock(); - eDebug("[EPGC] start caching events"); - state=0; - haveData=0; - eDVBSectionFilterMask mask; - memset(&mask, 0, sizeof(mask)); - mask.pid = 0x12; - mask.flags = eDVBSectionFilterMask::rfCRC; - - mask.data[0] = 0x4E; - mask.mask[0] = 0xFE; - m_NowNextReader->start(mask); - isRunning |= 1; + // check for enough free space on storage + tmp=s.f_bfree; + tmp*=s.f_bsize; + if ( tmp < (eventData::CacheSize*12)/10 ) // 20% overhead + return; - mask.data[0] = 0x50; - mask.mask[0] = 0xF0; - m_ScheduleReader->start(mask); - isRunning |= 2; + FILE *f = fopen("/hdd/epg.dat", "w"); + 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) + { + timeMap &timemap = service_it->second.second; + fwrite( &service_it->first, sizeof(uniqueEPGKey), 1, f); + size = timemap.size(); + fwrite( &size, sizeof(int), 1, f); + for (timeMap::iterator time_it(timemap.begin()); time_it != timemap.end(); ++time_it) + { + __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); + fclose(f); + unsigned char md5[16]; + if (!md5_file("/hdd/epg.dat", 1, md5)) + { + FILE *f = fopen("/hdd/epg.dat.md5", "w"); + if (f) + { + fwrite( md5, 16, 1, f); + fclose(f); + } + } + } +#endif +} + +RESULT eEPGCache::getInstance(eEPGCache *&ptr) +{ + ptr = instance; + if (!ptr) + return -1; + return 0; +} - mask.data[0] = 0x60; - mask.mask[0] = 0xF0; - m_ScheduleOtherReader->start(mask); - isRunning |= 4; +eEPGCache::channel_data::channel_data(eEPGCache *ml) + :cache(ml) + ,abortTimer(ml), zapTimer(ml) + ,state(0), isRunning(0), haveData(0), can_delete(1) +{ + CONNECT(zapTimer.timeout, eEPGCache::channel_data::startEPG); + CONNECT(abortTimer.timeout, eEPGCache::channel_data::abortNonAvail); +} - abortTimer.start(5000,true); +bool eEPGCache::channel_data::finishEPG() +{ + if (!isRunning) // epg ready + { + eDebug("[EPGC] stop caching events(%d)", time(0)+eDVBLocalTimeHandler::getInstance()->difference()); + zapTimer.start(UPDATE_INTERVAL, 1); + eDebug("[EPGC] next update in %i min", UPDATE_INTERVAL / 60000); + for (int i=0; i < 3; ++i) + { + seenSections[i].clear(); + calcedSections[i].clear(); + } + singleLock l(cache->cache_lock); + cache->channelLastUpdated[channel->getChannelID()] = time(0)+eDVBLocalTimeHandler::getInstance()->difference(); + can_delete=1; + return true; } - else + return false; +} + +void eEPGCache::channel_data::startEPG() +{ + eDebug("[EPGC] start caching events(%d)", eDVBLocalTimeHandler::getInstance()->difference()+time(0)); + state=0; + haveData=0; + can_delete=0; + for (int i=0; i < 3; ++i) { - eDebug("[EPGC] wait for clock update"); - zapTimer.start(1000, 1); // restart Timer + seenSections[i].clear(); + calcedSections[i].clear(); } + + eDVBSectionFilterMask mask; + memset(&mask, 0, sizeof(mask)); + mask.pid = 0x12; + mask.flags = eDVBSectionFilterMask::rfCRC; + + 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; + + abortTimer.start(7000,true); } -void eEPGCache::abortNonAvail() +void eEPGCache::channel_data::abortNonAvail() { if (!state) { - if ( !(haveData&2) && (isRunning&2) ) + if ( !(haveData&eEPGCache::NOWNEXT) && (isRunning&eEPGCache::NOWNEXT) ) { eDebug("[EPGC] abort non avail nownext reading"); - isRunning &= ~2; - if ( m_NowNextReader ) - m_NowNextReader->stop(); + isRunning &= ~eEPGCache::NOWNEXT; + m_NowNextReader->stop(); + m_NowNextConn=0; } - if ( !(haveData&1) && (isRunning&1) ) + if ( !(haveData&eEPGCache::SCHEDULE) && (isRunning&eEPGCache::SCHEDULE) ) { eDebug("[EPGC] abort non avail schedule reading"); - isRunning &= ~1; + isRunning &= ~SCHEDULE; m_ScheduleReader->stop(); + m_ScheduleConn=0; } - if ( !(haveData&4) && (isRunning&4) ) + if ( !(haveData&eEPGCache::SCHEDULE_OTHER) && (isRunning&eEPGCache::SCHEDULE_OTHER) ) { eDebug("[EPGC] abort non avail schedule_other reading"); - isRunning &= ~4; + isRunning &= ~SCHEDULE_OTHER; m_ScheduleOtherReader->stop(); + m_ScheduleOtherConn=0; + } + if ( isRunning ) + abortTimer.start(90000, true); + else + { + ++state; + for (int i=0; i < 3; ++i) + { + seenSections[i].clear(); + calcedSections[i].clear(); + } + can_delete=1; } - abortTimer.start(20000, true); } ++state; } -void eEPGCache::startCache(const eServiceReferenceDVB& ref) +void eEPGCache::channel_data::startChannel() +{ + updateMap::iterator It = cache->channelLastUpdated.find( channel->getChannelID() ); + + int update = ( It != cache->channelLastUpdated.end() ? ( UPDATE_INTERVAL - ( (time(0)+eDVBLocalTimeHandler::getInstance()->difference()-It->second) * 1000 ) ) : ZAP_DELAY ); + + if (update < ZAP_DELAY) + update = ZAP_DELAY; + + zapTimer.start(update, 1); + if (update >= 60000) + eDebug("[EPGC] next update in %i min", update/60000); + else if (update >= 1000) + eDebug("[EPGC] next update in %i sec", update/1000); +} + +void eEPGCache::channel_data::abortEPG() { - if ( m_currentChannel ) + for (int i=0; i < 3; ++i) { - next_service = ref; - leaveChannel(m_currentChannel); - return; + seenSections[i].clear(); + calcedSections[i].clear(); } - eDVBChannelID chid; - ref.getChannelID( chid ); - ePtr res_mgr; - if ( eDVBResourceManager::getInstance( res_mgr ) ) - eDebug("[eEPGCache] no res manager!!"); - else + abortTimer.stop(); + zapTimer.stop(); + if (isRunning) { - ePtr demux; -// res_mgr->allocateChannel(chid, m_currentChannel); - if ( m_currentChannel->getDemux(demux) ) + eDebug("[EPGC] abort caching events !!"); + if (isRunning & eEPGCache::SCHEDULE) { - eDebug("[eEPGCache] no demux!!"); - goto error4; + isRunning &= ~eEPGCache::SCHEDULE; + m_ScheduleReader->stop(); + m_ScheduleConn=0; } - else + if (isRunning & eEPGCache::NOWNEXT) { - RESULT res; - m_NowNextReader = new eDVBSectionReader( demux, this, res ); - if ( res ) - { - eDebug("[eEPGCache] couldnt initialize nownext reader!!"); - goto error3; - } - m_NowNextReader->connectRead(slot(*this, &eEPGCache::readNowNextData), m_NowNextConn); - m_ScheduleReader = new eDVBSectionReader( demux, this, res ); - if ( res ) - { - eDebug("[eEPGCache] couldnt initialize schedule reader!!"); - goto error2; - } - m_ScheduleReader->connectRead(slot(*this, &eEPGCache::readScheduleData), m_ScheduleConn); - m_ScheduleOtherReader = new eDVBSectionReader( demux, this, res ); - if ( res ) - { - eDebug("[eEPGCache] couldnt initialize schedule other reader!!"); - goto error1; - } - m_ScheduleOtherReader->connectRead(slot(*this, &eEPGCache::readScheduleOtherData), m_ScheduleOtherConn); - messages.send(Message(Message::startService, ref)); - // -> gotMessage -> changedService + isRunning &= ~eEPGCache::NOWNEXT; + m_NowNextReader->stop(); + m_NowNextConn=0; } + if (isRunning & SCHEDULE_OTHER) + { + isRunning &= ~eEPGCache::SCHEDULE_OTHER; + m_ScheduleOtherReader->stop(); + m_ScheduleOtherConn=0; + } + can_delete=1; } - return; -error1: - m_ScheduleOtherReader=0; - m_ScheduleOtherConn=0; -error2: - m_ScheduleReader=0; - m_ScheduleConn=0; -error3: - m_NowNextReader=0; - m_NowNextConn=0; -error4: - m_currentChannel=0; } -void eEPGCache::leaveChannel(iDVBChannel * chan) +void eEPGCache::channel_data::readData( const __u8 *data) { - if ( chan && chan == m_currentChannel ) + if (!data) + eDebug("get Null pointer from section reader !!"); + else { - messages.send(Message(Message::leaveChannel, chan)); - // -> gotMessage -> abortEPG + int source; + int map; + iDVBSectionReader *reader=NULL; + switch(data[0]) + { + case 0x4E ... 0x4F: + reader=m_NowNextReader; + source=eEPGCache::NOWNEXT; + map=0; + break; + case 0x50 ... 0x5F: + reader=m_ScheduleReader; + source=eEPGCache::SCHEDULE; + map=1; + break; + case 0x60 ... 0x6F: + reader=m_ScheduleOtherReader; + source=eEPGCache::SCHEDULE_OTHER; + map=2; + break; + default: + eDebug("[EPGC] unknown table_id !!!"); + return; + } + tidMap &seenSections = this->seenSections[map]; + tidMap &calcedSections = this->calcedSections[map]; + if ( state == 1 && calcedSections == seenSections || state > 1 ) + { + eDebugNoNewLine("[EPGC] "); + switch (source) + { + 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()); + if ( reader ) + reader->stop(); + isRunning &= ~source; + if (!isRunning) + finishEPG(); + } + else + { + eit_t *eit = (eit_t*) data; + __u32 sectionNo = data[0] << 24; + sectionNo |= data[3] << 16; + sectionNo |= data[4] << 8; + sectionNo |= eit->section_number; + + tidMap::iterator it = + seenSections.find(sectionNo); + + 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 ) + { + if ( i == eit->section_number ) + { + for (int x=i; x <= eit->segment_last_section_number; ++x) + calcedSections.insert(tmpval|(x&0xFF)); + } + else + calcedSections.insert(tmpval|(i&0xFF)); + } + cache->sectionRead(data, source, this); + } + } } } -void eEPGCache::changedService(const uniqueEPGKey &service) +RESULT eEPGCache::lookupEvent(const eServiceReference &service, time_t t, const eventData *&result ) +// if t == 0 we search the current event... { - current_service = service; - updateMap::iterator It = serviceLastUpdated.find( current_service ); - - int update; - -// check if this is a subservice and this is only a dbox2 -// then we dont start epgcache on subservice change.. -// ever and ever.. + singleLock s(cache_lock); + uniqueEPGKey key(service); -// if ( !err || err == -ENOCASYS ) + // check if EPG for this service is ready... + eventCache::iterator It = eventDB.find( key ); + if ( It != eventDB.end() && !It->second.first.empty() ) // entrys cached ? { - update = ( It != serviceLastUpdated.end() ? ( UPDATE_INTERVAL - ( (time(0)+eDVBLocalTimeHandler::getInstance()->difference()-It->second) * 1000 ) ) : ZAP_DELAY ); + if (!t) + t = time(0)+eDVBLocalTimeHandler::getInstance()->difference(); - if (update < ZAP_DELAY) - update = ZAP_DELAY; +// TODO: optimize this.. why we here search first in timemap.. and then in eventmap?? + timeMap::iterator i = It->second.second.lower_bound(t); + if ( i != It->second.second.end() ) + { + if ( i != It->second.second.end() ) + { + if ( t <= i->first+i->second->getDuration() ) + { + result = i->second; + return 0; + } + } + } - zapTimer.start(update, 1); - if (update >= 60000) - eDebug("[EPGC] next update in %i min", update/60000); - else if (update >= 1000) - eDebug("[EPGC] next update in %i sec", update/1000); + for ( eventMap::iterator i( It->second.first.begin() ); i != It->second.first.end(); i++) + { + int duration = i->second->getDuration(); + time_t begTime = i->second->getStartTime(); + if ( t >= begTime && t <= begTime+duration) // then we have found + { + result = i->second; + return 0; + } + } } + return -1; +} + +RESULT eEPGCache::lookupEvent(const eServiceReference &service, time_t t, const eit_event_struct *&result ) +{ + singleLock s(cache_lock); + const eventData *data=0; + RESULT ret = lookupEvent(service, t, data); + if ( !ret && data ) + result = data->get(); + return ret; +} - Lock(); - bool empty=eventDB[current_service].first.empty(); - Unlock(); +RESULT eEPGCache::lookupEvent(const eServiceReference &service, time_t t, Event *& result ) +{ + singleLock s(cache_lock); + const eventData *data=0; + RESULT ret = lookupEvent(service, t, data); + if ( !ret && data ) + result = new Event((uint8_t*)data->get()); + return ret; +} - if (!empty) - { - eDebug("[EPGC] yet cached"); - /*emit*/ EPGAvail(1); - } - else +RESULT eEPGCache::lookupEvent(const eServiceReference &service, time_t t, ePtr &result ) +{ + singleLock s(cache_lock); + const eventData *data=0; + RESULT ret = lookupEvent(service, t, data); + if ( !ret && data ) { - eDebug("[EPGC] not cached yet"); - /*emit*/ EPGAvail(0); + Event ev((uint8_t*)data->get()); + result = new eServiceEvent(); + ret = result->parseFrom(&ev); } + return ret; } -void eEPGCache::abortEPG() +RESULT eEPGCache::lookupEvent(const eServiceReference &service, int event_id, const eventData *&result ) { - abortTimer.stop(); - zapTimer.stop(); - if (isRunning) + singleLock s(cache_lock); + uniqueEPGKey key( service ); + + eventCache::iterator It = eventDB.find( key ); + if ( It != eventDB.end() && !It->second.first.empty() ) // entrys cached? { - if (isRunning & 1) - { - isRunning &= ~1; - if ( m_ScheduleReader ) - m_ScheduleReader->stop(); - } - if (isRunning & 2) + eventMap::iterator i( It->second.first.find( event_id )); + if ( i != It->second.first.end() ) { - isRunning &= ~2; - if ( m_NowNextReader ) - m_NowNextReader->stop(); + result = i->second; + return 0; } - if (isRunning & 4) + else { - isRunning &= ~4; - if ( m_ScheduleOtherReader ) - m_ScheduleOtherReader->stop(); + result = 0; + eDebug("event %04x not found in epgcache", event_id); } - eDebug("[EPGC] abort caching events !!"); - Lock(); - temp.clear(); - Unlock(); } + return -1; } -void eEPGCache::gotMessage( const Message &msg ) +RESULT eEPGCache::lookupEvent(const eServiceReference &service, int event_id, const eit_event_struct *&result) { - switch (msg.type) - { - case Message::flush: - flushEPG(msg.service); - break; - case Message::startService: - changedService(msg.service); - break; - case Message::leaveChannel: - abortEPG(); - back_messages.send(Message(Message::leaveChannelFinished)); - break; - case Message::pause: - pauseEPG(); - break; - case Message::restart: - restartEPG(); - break; - case Message::quit: - quit(0); - break; - case Message::timeChanged: - cleanLoop(); - break; - default: - eDebug("unhandled EPGCache Message!!"); - break; - } + singleLock s(cache_lock); + const eventData *data=0; + RESULT ret = lookupEvent(service, event_id, data); + if ( !ret && data ) + result = data->get(); + return ret; } -void eEPGCache::gotBackMessage( const Message &msg ) +RESULT eEPGCache::lookupEvent(const eServiceReference &service, int event_id, Event *& result) { - switch (msg.type) - { - case Message::leaveChannelFinished: - m_ScheduleOtherReader=0; - m_ScheduleOtherConn=0; - m_ScheduleReader=0; - m_ScheduleConn=0; - m_NowNextReader=0; - m_NowNextConn=0; - m_currentChannel=0; - eDebug("[eEPGC] channel leaved"); - if (next_service) - { - startCache(next_service); - next_service = eServiceReferenceDVB(); - } - break; - default: - eDebug("unhandled EPGCache BackMessage!!"); - break; - } + singleLock s(cache_lock); + const eventData *data=0; + RESULT ret = lookupEvent(service, event_id, data); + if ( !ret && data ) + result = new Event((uint8_t*)data->get()); + return ret; } -void eEPGCache::thread() +RESULT eEPGCache::lookupEvent(const eServiceReference &service, int event_id, ePtr &result) { - nice(4); - load(); - cleanLoop(); - exec(); - save(); + singleLock s(cache_lock); + const eventData *data=0; + RESULT ret = lookupEvent(service, event_id, data); + if ( !ret && data ) + { + Event ev((uint8_t*)data->get()); + result = new eServiceEvent(); + ret = result->parseFrom(&ev); + } + return ret; } -void eEPGCache::load() +RESULT eEPGCache::startTimeQuery(const eServiceReference &service, time_t begin, int minutes) { - FILE *f = fopen("/hdd/epg.dat", "r"); - if (f) + eventCache::iterator It = eventDB.find( service ); + if ( It != eventDB.end() && It->second.second.size() ) { - unsigned char md5_saved[16]; - unsigned char md5[16]; - int size=0; - int cnt=0; - bool md5ok=false; - if (!md5_file("/hdd/epg.dat", 1, md5)) + m_timemap_end = minutes != -1 ? It->second.second.upper_bound(begin+minutes*60) : It->second.second.end(); + if ( begin != -1 ) { - FILE *f = fopen("/hdd/epg.dat.md5", "r"); - if (f) + m_timemap_cursor = It->second.second.lower_bound(begin); + if ( m_timemap_cursor != It->second.second.end() && m_timemap_cursor != It->second.second.begin() ) { - fread( md5_saved, 16, 1, f); - fclose(f); - if ( !memcmp(md5_saved, md5, 16) ) - md5ok=true; + timeMap::iterator it = m_timemap_cursor; + --it; + if ( (it->second->getStartTime() + it->second->getDuration()) > begin ) + m_timemap_cursor = it; } } - if ( md5ok ) - { - fread( &size, sizeof(int), 1, f); - while(size--) - { - 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; - } - eventDB[key]=std::pair(evMap,tmMap); - } - eDebug("%d events read from /hdd/epg.dat.md5", cnt); - } - fclose(f); + else + m_timemap_cursor = It->second.second.begin(); + return 0; } + return -1; } -void eEPGCache::save() +RESULT eEPGCache::getNextTimeEntry(const eventData *& result) { - struct statfs s; - off64_t tmp; - if (statfs("/hdd", &s)<0) - tmp=0; - else + if ( m_timemap_cursor != m_timemap_end ) { - tmp=s.f_blocks; - tmp*=s.f_bsize; + result = m_timemap_cursor++->second; + return 0; } + return -1; +} - // prevent writes to builtin flash - if ( tmp < 1024*1024*50 ) // storage size < 50MB - return; - - // check for enough free space on storage - tmp=s.f_bfree; - tmp*=s.f_bsize; - if ( tmp < (eventData::CacheSize*12)/10 ) // 20% overhead - return; - - FILE *f = fopen("/hdd/epg.dat", "w"); - int cnt=0; - if ( f ) +RESULT eEPGCache::getNextTimeEntry(const eit_event_struct *&result) +{ + if ( m_timemap_cursor != m_timemap_end ) { - int size = eventDB.size(); - fwrite( &size, sizeof(int), 1, f ); - for (eventCache::iterator service_it(eventDB.begin()); service_it != eventDB.end(); ++service_it) - { - timeMap &timemap = service_it->second.second; - fwrite( &service_it->first, sizeof(uniqueEPGKey), 1, f); - size = timemap.size(); - 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); - fwrite( time_it->second->EITdata, len, 1, f); - ++cnt; - } - } - eDebug("%d events written to /hdd/epg.dat", cnt); - fclose(f); - unsigned char md5[16]; - if (!md5_file("/hdd/epg.dat", 1, md5)) - { - FILE *f = fopen("/hdd/epg.dat.md5", "w"); - if (f) - { - fwrite( md5, 16, 1, f); - fclose(f); - } - } + result = m_timemap_cursor++->second->get(); + return 0; } + return -1; } -RESULT eEPGCache::getInstance(ePtr &ptr) +RESULT eEPGCache::getNextTimeEntry(Event *&result) { - ptr = instance; - if (!ptr) - return -1; - return 0; + 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); + } + return -1; +}