X-Git-Url: https://git.cweiske.de/enigma2.git/blobdiff_plain/8cada4eb31be58a3fa3efa09afbf431b3b62ca08..97d46aee1158e549c110d66a0e1c4185c3cbbdf3:/lib/dvb/epgcache.cpp diff --git a/lib/dvb/epgcache.cpp b/lib/dvb/epgcache.cpp index 6a00b6be..48cbfbfd 100644 --- a/lib/dvb/epgcache.cpp +++ b/lib/dvb/epgcache.cpp @@ -12,15 +12,40 @@ #include // for statfs // #include #include +#include #include #include -#include +#include +#include int eventData::CacheSize=0; descriptorMap eventData::descriptors; __u8 eventData::data[4108]; extern const uint32_t crc32_table[256]; +const eServiceReference &handleGroup(const eServiceReference &ref) +{ + if (ref.flags & eServiceReference::isGroup) + { + ePtr res; + if (!eDVBResourceManager::getInstance(res)) + { + ePtr db; + if (!res->getChannelList(db)) + { + eBouquet *bouquet=0; + if (!db->getBouquet(ref, bouquet)) + { + std::list::iterator it(bouquet->m_services.begin()); + if (it != bouquet->m_services.end()) + return *it; + } + } + } + } + return ref; +} + eventData::eventData(const eit_event_struct* e, int size, int type) :ByteSize(size&0xFF), type(type&0xFF) { @@ -31,48 +56,67 @@ eventData::eventData(const eit_event_struct* e, int size, int type) __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 ) + int ptr=12; + size -= 12; + + while(size > 1) { __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() ) + int descr_len = descr[1]; + descr_len += 2; + if (size >= descr_len) { - CacheSize+=descr_len; - __u8 *d = new __u8[descr_len]; - memcpy(d, descr, descr_len); - descriptors[crc] = descriptorPair(1, d); + switch (descr[0]) + { + case EXTENDED_EVENT_DESCRIPTOR: + case SHORT_EVENT_DESCRIPTOR: + case LINKAGE_DESCRIPTOR: + case COMPONENT_DESCRIPTOR: + { + __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; + break; + } + default: // do not cache all other descriptors + ptr += descr_len; + break; + } + size -= descr_len; } else - ++it->second.first; - - *pdescr++=crc; - descriptors_length -= descr_len; + break; } - ByteSize = 12+((pdescr-descr)*4); + ASSERT(pdescr <= &descr[65]); + ByteSize = 10+((pdescr-descr)*4); EITdata = new __u8[ByteSize]; CacheSize+=ByteSize; - memcpy(EITdata, (__u8*) e, 12); - memcpy(EITdata+12, descr, ByteSize-12); + memcpy(EITdata, (__u8*) e, 10); + memcpy(EITdata+10, descr, ByteSize-10); } 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) + int tmp = ByteSize-10; + memcpy(data, EITdata, 10); + int descriptors_length=0; + __u32 *p = (__u32*)(EITdata+10); + while(tmp>3) { descriptorMap::iterator it = descriptors.find(*p++); @@ -81,21 +125,26 @@ const eit_event_struct* eventData::get() const int b = it->second.second[1]+2; memcpy(data+pos, it->second.second, b ); pos += b; + descriptors_length += b; } + else + eFatal("LINE %d descriptor not found in descriptor cache %08x!!!!!!", __LINE__, *(p-1)); tmp-=4; } - - return (const eit_event_struct*)data; + ASSERT(pos <= 4108); + data[10] = (descriptors_length >> 8) & 0x0F; + data[11] = descriptors_length & 0xFF; + return (eit_event_struct*)data; } eventData::~eventData() { if ( ByteSize ) { - CacheSize-=ByteSize; - ByteSize-=12; - __u32 *d = (__u32*)(EITdata+12); - while(ByteSize) + CacheSize -= ByteSize; + __u32 *d = (__u32*)(EITdata+10); + ByteSize -= 10; + while(ByteSize>3) { descriptorMap::iterator it = descriptors.find(*d++); @@ -109,7 +158,9 @@ eventData::~eventData() descriptors.erase(it); // remove entry from descriptor map } } - ByteSize-=4; + else + eFatal("LINE %d descriptor not found in descriptor cache %08x!!!!!!", __LINE__, *(d-1)); + ByteSize -= 4; } delete [] EITdata; } @@ -162,20 +213,24 @@ pthread_mutex_t eEPGCache::channel_map_lock= DEFINE_REF(eEPGCache) eEPGCache::eEPGCache() - :messages(this,1), cleanTimer(this)//, paused(0) + :messages(this,1), cleanTimer(eTimer::create(this))//, paused(0) { eDebug("[EPGC] Initialized EPGCache"); CONNECT(messages.recv_msg, eEPGCache::gotMessage); CONNECT(eDVBLocalTimeHandler::getInstance()->m_timeUpdated, eEPGCache::timeUpdated); - CONNECT(cleanTimer.timeout, eEPGCache::cleanLoop); + 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); + if (eDVBLocalTimeHandler::getInstance()->ready()) + timeUpdated(); + } instance=this; } @@ -199,6 +254,11 @@ void eEPGCache::DVBChannelAdded(eDVBChannel *chan) data->prevChannelState = -1; #ifdef ENABLE_PRIVATE_EPG data->m_PrivatePid = -1; +#endif +#ifdef ENABLE_MHW_EPG + data->m_mhw2_channel_pid = 0x231; // defaults for astra 19.2 D+ + data->m_mhw2_title_pid = 0x234; // defaults for astra 19.2 D+ + data->m_mhw2_summary_pid = 0x236; // defaults for astra 19.2 D+ #endif singleLock s(channel_map_lock); m_knownChannels.insert( std::pair(chan, data) ); @@ -248,6 +308,13 @@ void eEPGCache::DVBChannelRunning(iDVBChannel *chan) eDebug("[eEPGCache] couldnt initialize schedule other reader!!"); return; } + + res = demux->createSectionReader( this, data.m_ViasatReader ); + if ( res ) + { + eDebug("[eEPGCache] couldnt initialize viasat reader!!"); + return; + } #ifdef ENABLE_PRIVATE_EPG res = demux->createSectionReader( this, data.m_PrivateReader ); if ( res ) @@ -263,6 +330,12 @@ void eEPGCache::DVBChannelRunning(iDVBChannel *chan) eDebug("[eEPGCache] couldnt initialize mhw reader!!"); return; } + res = demux->createSectionReader( this, data.m_MHWReader2 ); + if ( res ) + { + eDebug("[eEPGCache] couldnt initialize mhw reader!!"); + return; + } #endif messages.send(Message(Message::startChannel, chan)); // -> gotMessage -> changedService @@ -311,12 +384,20 @@ void eEPGCache::DVBChannelStateChanged(iDVBChannel *chan) } } -void eEPGCache::FixOverlapping(std::pair &servicemap, time_t TM, int duration, const timeMap::iterator &tm_it, const uniqueEPGKey &service) +bool eEPGCache::FixOverlapping(std::pair &servicemap, time_t TM, int duration, const timeMap::iterator &tm_it, const uniqueEPGKey &service) { + bool ret = false; timeMap::iterator tmp = tm_it; while ((tmp->first+tmp->second->getDuration()-300) > TM) { - if(tmp->first != TM && tmp->second->type != PRIVATE) + if(tmp->first != TM +#ifdef ENABLE_PRIVATE_EPG + && tmp->second->type != PRIVATE +#endif +#ifdef ENABLE_MHW + && tmp->second->type != MHW +#endif + ) { __u16 event_id = tmp->second->getEventID(); servicemap.first.erase(event_id); @@ -338,6 +419,7 @@ void eEPGCache::FixOverlapping(std::pair &servicemap, time_t T } else servicemap.second.erase(tmp--); + ret = true; } else { @@ -366,12 +448,14 @@ void eEPGCache::FixOverlapping(std::pair &servicemap, time_t T #endif delete tmp->second; servicemap.second.erase(tmp++); + ret = true; } else ++tmp; if (tmp == servicemap.second.end()) break; } + return ret; } void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel) @@ -388,13 +472,25 @@ void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel) if ( data[ptr-1] < 0x40 ) --ptr; - uniqueEPGKey service( HILO(eit->service_id), HILO(eit->original_network_id), HILO(eit->transport_stream_id) ); + // Cablecom HACK .. tsid / onid in eit data are incorrect.. so we use + // it from running channel (just for current transport stream eit data) + bool use_transponder_chid = source == SCHEDULE || (source == NOWNEXT && data[0] == 0x4E); + eDVBChannelID chid = channel->channel->getChannelID(); + uniqueEPGKey service( HILO(eit->service_id), + use_transponder_chid ? chid.original_network_id.get() : HILO(eit->original_network_id), + use_transponder_chid ? chid.transport_stream_id.get() : HILO(eit->transport_stream_id)); + eit_event_struct* eit_event = (eit_event_struct*) (data+ptr); int eit_event_size; 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); - time_t now = eDVBLocalTimeHandler::getInstance()->nowTime(); + 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); + time_t now = ::time(0); if ( TM != 3599 && TM > -1) channel->haveData |= source; @@ -408,6 +504,7 @@ void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel) while (ptrdescriptors_loop_length)+EIT_LOOP_SIZE; duration = fromBCD(eit_event->duration_1)*3600+fromBCD(eit_event->duration_2)*60+fromBCD(eit_event->duration_3); @@ -416,7 +513,8 @@ void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel) eit_event->start_time_2, eit_event->start_time_3, eit_event->start_time_4, - eit_event->start_time_5); + eit_event->start_time_5, + &event_hash); if ( TM == 3599 ) goto next; @@ -427,16 +525,24 @@ void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel) if ( now <= (TM+duration) || TM == 3599 /*NVOD Service*/ ) // old events should not be cached { __u16 event_id = HILO(eit_event->event_id); -// eDebug("event_id is %d sid is %04x", event_id, service.sid); - eventData *evt = 0; int ev_erase_count = 0; int tm_erase_count = 0; + if (event_id == 0) { + // hack for some polsat services on 13.0E..... but this also replaces other valid event_ids with value 0.. + // but we dont care about it... + event_id = event_hash; + eit_event->event_id_hi = event_hash >> 8; + eit_event->event_id_lo = event_hash & 0xFF; + } + // search in eventmap eventMap::iterator ev_it = servicemap.first.find(event_id); +// eDebug("event_id is %d sid is %04x", event_id, service.sid); + // entry with this event_id is already exist ? if ( ev_it != servicemap.first.end() ) { @@ -452,10 +558,15 @@ void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel) if ( tm_it_tmp->first == TM ) // just update eventdata { // exempt memory - delete ev_it->second; + eventData *tmp = ev_it->second; ev_it->second = tm_it_tmp->second = new eventData(eit_event, eit_event_size, source); - FixOverlapping(servicemap, TM, duration, tm_it_tmp, service); + if (FixOverlapping(servicemap, TM, duration, tm_it_tmp, service)) + { + prevEventIt = servicemap.first.end(); + prevTimeIt = servicemap.second.end(); + } + delete tmp; goto next; } else // event has new event begin time @@ -492,7 +603,6 @@ void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel) prevEventIt=servicemap.first.end(); } } - evt = new eventData(eit_event, eit_event_size, source); #ifdef EPG_DEBUG bool consistencyCheck=true; @@ -528,8 +638,6 @@ void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel) tm_it=prevTimeIt=servicemap.second.insert( prevTimeIt, std::pair( TM, evt ) ); } - FixOverlapping(servicemap, TM, duration, tm_it, service); - #ifdef EPG_DEBUG if ( consistencyCheck ) { @@ -549,6 +657,11 @@ void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel) ev_it->first, event_id ); } #endif + if (FixOverlapping(servicemap, TM, duration, tm_it, service)) + { + prevEventIt = servicemap.first.end(); + prevTimeIt = servicemap.second.end(); + } } next: #ifdef EPG_DEBUG @@ -639,7 +752,7 @@ void eEPGCache::cleanLoop() { eDebug("[EPGC] start cleanloop"); - time_t now = eDVBLocalTimeHandler::getInstance()->nowTime(); + time_t now = ::time(0); for (eventCache::iterator DBIt = eventDB.begin(); DBIt != eventDB.end(); DBIt++) { @@ -674,7 +787,7 @@ void eEPGCache::cleanLoop() content_time_tables.find( DBIt->first ); if ( x != content_time_tables.end() ) { - timeMap &tmMap = eventDB[DBIt->first].second; + timeMap &tmMap = DBIt->second.second; for ( contentMap::iterator i = x->second.begin(); i != x->second.end(); ) { for ( contentTimeMap::iterator it(i->second.begin()); @@ -697,7 +810,7 @@ void eEPGCache::cleanLoop() 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() @@ -741,6 +854,7 @@ void eEPGCache::gotMessage( const Message &msg ) #ifdef ENABLE_PRIVATE_EPG case Message::got_private_pid: { + singleLock s(channel_map_lock); for (channelMapIterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it) { eDVBChannel *channel = (eDVBChannel*) it->first; @@ -753,11 +867,14 @@ void eEPGCache::gotMessage( const Message &msg ) data->m_PrevVersion = -1; data->m_PrivatePid = msg.pid; data->m_PrivateService = msg.service; - updateMap::iterator It = channelLastUpdated.find( channel->getChannelID() ); - int update = ( It != channelLastUpdated.end() ? ( UPDATE_INTERVAL - ( (eDVBLocalTimeHandler::getInstance()->nowTime()-It->second) * 1000 ) ) : ZAP_DELAY ); + int onid = chid.original_network_id.get(); + onid |= 0x80000000; // we use highest bit as private epg indicator + chid.original_network_id = onid; + updateMap::iterator It = channelLastUpdated.find( chid ); + int update = ( It != channelLastUpdated.end() ? ( UPDATE_INTERVAL - ( (::time(0)-It->second) * 1000 ) ) : ZAP_DELAY ); if (update < ZAP_DELAY) update = ZAP_DELAY; - data->startPrivateTimer.start(update, 1); + data->startPrivateTimer->start(update, 1); if (update >= 60000) eDebug("[EPGC] next private update in %i min", update/60000); else if (update >= 1000) @@ -767,6 +884,62 @@ void eEPGCache::gotMessage( const Message &msg ) } break; } +#endif +#ifdef ENABLE_MHW_EPG + case Message::got_mhw2_channel_pid: + { + singleLock s(channel_map_lock); + 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_mhw2_channel_pid = msg.pid; + eDebug("[EPGC] got mhw2 channel pid %04x", msg.pid); + break; + } + } + break; + } + case Message::got_mhw2_title_pid: + { + singleLock s(channel_map_lock); + 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_mhw2_title_pid = msg.pid; + eDebug("[EPGC] got mhw2 title pid %04x", msg.pid); + break; + } + } + break; + } + case Message::got_mhw2_summary_pid: + { + singleLock s(channel_map_lock); + 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_mhw2_summary_pid = msg.pid; + eDebug("[EPGC] got mhw2 summary pid %04x", msg.pid); + break; + } + } + break; + } #endif case Message::timeChanged: cleanLoop(); @@ -789,10 +962,10 @@ void eEPGCache::thread() void eEPGCache::load() { - singleLock s(cache_lock); FILE *f = fopen("/hdd/epg.dat", "r"); if (f) { + unlink("/hdd/epg.dat"); int size=0; int cnt=0; #if 0 @@ -818,14 +991,15 @@ void eEPGCache::load() fread( &magic, sizeof(int), 1, f); if (magic != 0x98765432) { - eDebug("epg file has incorrect byte order.. dont read it"); + eDebug("[EPGC] epg file has incorrect byte order.. dont read it"); fclose(f); return; } char text1[13]; fread( text1, 13, 1, f); - if ( !strncmp( text1, "ENIGMA_EPG_V5", 13) ) + if ( !strncmp( text1, "ENIGMA_EPG_V7", 13) ) { + singleLock s(cache_lock); fread( &size, sizeof(int), 1, f); while(size--) { @@ -853,7 +1027,7 @@ void eEPGCache::load() eventDB[key]=std::pair(evMap,tmMap); } eventData::load(f); - eDebug("%d events read from /hdd/epg.dat", cnt); + eDebug("[EPGC] %d events read from /hdd/epg.dat", cnt); #ifdef ENABLE_PRIVATE_EPG char text2[11]; fread( text2, 11, 1, f); @@ -927,7 +1101,7 @@ void eEPGCache::save() { unsigned int magic = 0x98765432; fwrite( &magic, sizeof(int), 1, f); - const char *text = "ENIGMA_EPG_V5"; + const char *text = "UNFINISHED_V7"; fwrite( text, 13, 1, f ); int size = eventDB.size(); fwrite( &size, sizeof(int), 1, f ); @@ -946,7 +1120,7 @@ void eEPGCache::save() ++cnt; } } - eDebug("%d events written to /hdd/epg.dat", cnt); + eDebug("[EPGC] %d events written to /hdd/epg.dat", cnt); eventData::save(f); #ifdef ENABLE_PRIVATE_EPG const char* text3 = "PRIVATE_EPG"; @@ -974,6 +1148,11 @@ void eEPGCache::save() } } #endif + // write version string after binary data + // has been written to disk. + fsync(fileno(f)); + fseek(f, sizeof(int), SEEK_SET); + fwrite("ENIGMA_EPG_V7", 13, 1, f); fclose(f); #if 0 unsigned char md5[16]; @@ -992,22 +1171,22 @@ void eEPGCache::save() eEPGCache::channel_data::channel_data(eEPGCache *ml) :cache(ml) - ,abortTimer(ml), zapTimer(ml), state(0) + ,abortTimer(eTimer::create(ml)), zapTimer(eTimer::create(ml)), state(0) ,isRunning(0), haveData(0) #ifdef ENABLE_PRIVATE_EPG - ,startPrivateTimer(ml) + ,startPrivateTimer(eTimer::create(ml)) #endif #ifdef ENABLE_MHW_EPG - ,m_MHWTimeoutTimer(ml) + ,m_MHWTimeoutTimer(eTimer::create(ml)) #endif { #ifdef ENABLE_MHW_EPG - CONNECT(m_MHWTimeoutTimer.timeout, eEPGCache::channel_data::MHWTimeout); + CONNECT(m_MHWTimeoutTimer->timeout, eEPGCache::channel_data::MHWTimeout); #endif - CONNECT(zapTimer.timeout, eEPGCache::channel_data::startEPG); - CONNECT(abortTimer.timeout, eEPGCache::channel_data::abortNonAvail); + CONNECT(zapTimer->timeout, eEPGCache::channel_data::startEPG); + CONNECT(abortTimer->timeout, eEPGCache::channel_data::abortNonAvail); #ifdef ENABLE_PRIVATE_EPG - CONNECT(startPrivateTimer.timeout, eEPGCache::channel_data::startPrivateReader); + CONNECT(startPrivateTimer->timeout, eEPGCache::channel_data::startPrivateReader); #endif pthread_mutex_init(&channel_active, 0); } @@ -1016,16 +1195,16 @@ bool eEPGCache::channel_data::finishEPG() { if (!isRunning) // epg ready { - eDebug("[EPGC] stop caching events(%ld)", eDVBLocalTimeHandler::getInstance()->nowTime()); - zapTimer.start(UPDATE_INTERVAL, 1); + eDebug("[EPGC] stop caching events(%ld)", ::time(0)); + zapTimer->start(UPDATE_INTERVAL, 1); eDebug("[EPGC] next update in %i min", UPDATE_INTERVAL / 60000); - for (int i=0; i < 3; ++i) + for (unsigned int i=0; i < sizeof(seenSections)/sizeof(tidMap); ++i) { seenSections[i].clear(); calcedSections[i].clear(); } singleLock l(cache->cache_lock); - cache->channelLastUpdated[channel->getChannelID()] = eDVBLocalTimeHandler::getInstance()->nowTime(); + cache->channelLastUpdated[channel->getChannelID()] = ::time(0); #ifdef ENABLE_MHW_EPG cleanup(); #endif @@ -1036,10 +1215,10 @@ bool eEPGCache::channel_data::finishEPG() void eEPGCache::channel_data::startEPG() { - eDebug("[EPGC] start caching events(%ld)", eDVBLocalTimeHandler::getInstance()->nowTime()); + eDebug("[EPGC] start caching events(%ld)", ::time(0)); state=0; haveData=0; - for (int i=0; i < 3; ++i) + for (unsigned int i=0; i < sizeof(seenSections)/sizeof(tidMap); ++i) { seenSections[i].clear(); calcedSections[i].clear(); @@ -1056,6 +1235,19 @@ void eEPGCache::channel_data::startEPG() m_MHWReader->start(mask); isRunning |= MHW; memcpy(&m_MHWFilterMask, &mask, sizeof(eDVBSectionFilterMask)); + + mask.pid = m_mhw2_channel_pid; + mask.data[0] = 0xC8; + mask.mask[0] = 0xFF; + mask.data[1] = 0; + mask.mask[1] = 0xFF; + m_MHWReader2->connectRead(slot(*this, &eEPGCache::channel_data::readMHWData2), m_MHWConn2); + m_MHWReader2->start(mask); + isRunning |= MHW; + memcpy(&m_MHWFilterMask2, &mask, sizeof(eDVBSectionFilterMask)); + mask.data[1] = 0; + mask.mask[1] = 0; + m_MHWTimeoutet=false; #endif mask.pid = 0x12; @@ -1074,12 +1266,19 @@ void eEPGCache::channel_data::startEPG() 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); + mask.pid = 0x39; + + mask.data[0] = 0x40; + mask.mask[0] = 0x40; + m_ViasatReader->connectRead(slot(*this, &eEPGCache::channel_data::readDataViasat), m_ViasatConn); + m_ViasatReader->start(mask); + isRunning |= VIASAT; + + abortTimer->start(7000,true); } void eEPGCache::channel_data::abortNonAvail() @@ -1102,11 +1301,18 @@ void eEPGCache::channel_data::abortNonAvail() } if ( !(haveData&SCHEDULE_OTHER) && (isRunning&SCHEDULE_OTHER) ) { - eDebug("[EPGC] abort non avail schedule_other reading"); + eDebug("[EPGC] abort non avail schedule other reading"); isRunning &= ~SCHEDULE_OTHER; m_ScheduleOtherReader->stop(); m_ScheduleOtherConn=0; } + if ( !(haveData&VIASAT) && (isRunning&VIASAT) ) + { + eDebug("[EPGC] abort non avail viasat reading"); + isRunning &= ~VIASAT; + m_ViasatReader->stop(); + m_ViasatConn=0; + } #ifdef ENABLE_MHW_EPG if ( !(haveData&MHW) && (isRunning&MHW) ) { @@ -1114,14 +1320,18 @@ void eEPGCache::channel_data::abortNonAvail() isRunning &= ~MHW; m_MHWReader->stop(); m_MHWConn=0; + m_MHWReader2->stop(); + m_MHWConn2=0; } #endif - if ( isRunning ) - abortTimer.start(90000, true); + if ( isRunning & VIASAT ) + abortTimer->start(300000, true); + else if ( isRunning ) + abortTimer->start(90000, true); else { ++state; - for (int i=0; i < 3; ++i) + for (unsigned int i=0; i < sizeof(seenSections)/sizeof(tidMap); ++i) { seenSections[i].clear(); calcedSections[i].clear(); @@ -1136,12 +1346,12 @@ void eEPGCache::channel_data::startChannel() pthread_mutex_lock(&channel_active); updateMap::iterator It = cache->channelLastUpdated.find( channel->getChannelID() ); - int update = ( It != cache->channelLastUpdated.end() ? ( UPDATE_INTERVAL - ( (eDVBLocalTimeHandler::getInstance()->nowTime()-It->second) * 1000 ) ) : ZAP_DELAY ); + int update = ( It != cache->channelLastUpdated.end() ? ( UPDATE_INTERVAL - ( (::time(0)-It->second) * 1000 ) ) : ZAP_DELAY ); if (update < ZAP_DELAY) update = ZAP_DELAY; - zapTimer.start(update, 1); + zapTimer->start(update, 1); if (update >= 60000) eDebug("[EPGC] next update in %i min", update/60000); else if (update >= 1000) @@ -1150,13 +1360,13 @@ void eEPGCache::channel_data::startChannel() void eEPGCache::channel_data::abortEPG() { - for (int i=0; i < 3; ++i) + for (unsigned int i=0; i < sizeof(seenSections)/sizeof(tidMap); ++i) { seenSections[i].clear(); calcedSections[i].clear(); } - abortTimer.stop(); - zapTimer.stop(); + abortTimer->stop(); + zapTimer->stop(); if (isRunning) { eDebug("[EPGC] abort caching events !!"); @@ -1178,12 +1388,20 @@ void eEPGCache::channel_data::abortEPG() m_ScheduleOtherReader->stop(); m_ScheduleOtherConn=0; } + if (isRunning & VIASAT) + { + isRunning &= ~VIASAT; + m_ViasatReader->stop(); + m_ViasatConn=0; + } #ifdef ENABLE_MHW_EPG if (isRunning & MHW) { isRunning &= ~MHW; m_MHWReader->stop(); m_MHWConn=0; + m_MHWReader2->stop(); + m_MHWConn2=0; } #endif } @@ -1196,93 +1414,107 @@ void eEPGCache::channel_data::abortEPG() pthread_mutex_unlock(&channel_active); } + +void eEPGCache::channel_data::readDataViasat( const __u8 *data) +{ + __u8 *d=0; + memcpy(&d, &data, sizeof(__u8*)); + d[0] |= 0x80; + readData(data); +} + void eEPGCache::channel_data::readData( const __u8 *data) { - if (!data) - eDebug("get Null pointer from section reader !!"); - else + int source; + int map; + iDVBSectionReader *reader=NULL; + switch(data[0]) + { + case 0x4E ... 0x4F: + reader=m_NowNextReader; + source=NOWNEXT; + map=0; + break; + case 0x50 ... 0x5F: + reader=m_ScheduleReader; + source=SCHEDULE; + map=1; + break; + case 0x60 ... 0x6F: + reader=m_ScheduleOtherReader; + source=SCHEDULE_OTHER; + map=2; + break; + case 0xD0 ... 0xDF: + case 0xE0 ... 0xEF: + reader=m_ViasatReader; + source=VIASAT; + map=3; + 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 ) { - int source; - int map; - iDVBSectionReader *reader=NULL; - switch(data[0]) + eDebugNoNewLine("[EPGC] "); + switch (source) { - case 0x4E ... 0x4F: - reader=m_NowNextReader; - source=NOWNEXT; - map=0; + case NOWNEXT: + m_NowNextConn=0; + eDebugNoNewLine("nownext"); break; - case 0x50 ... 0x5F: - reader=m_ScheduleReader; - source=SCHEDULE; - map=1; + case SCHEDULE: + m_ScheduleConn=0; + eDebugNoNewLine("schedule"); break; - case 0x60 ... 0x6F: - reader=m_ScheduleOtherReader; - source=SCHEDULE_OTHER; - map=2; + case SCHEDULE_OTHER: + m_ScheduleOtherConn=0; + eDebugNoNewLine("schedule other"); 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 NOWNEXT: - m_NowNextConn=0; - eDebugNoNewLine("nownext"); - break; - case SCHEDULE: - m_ScheduleConn=0; - eDebugNoNewLine("schedule"); - break; - case SCHEDULE_OTHER: - m_ScheduleOtherConn=0; - eDebugNoNewLine("schedule other"); - break; - default: eDebugNoNewLine("unknown");break; - } - eDebug(" finished(%ld)", eDVBLocalTimeHandler::getInstance()->nowTime()); - if ( reader ) - reader->stop(); - isRunning &= ~source; - if (!isRunning) - finishEPG(); + case VIASAT: + m_ViasatConn=0; + eDebugNoNewLine("viasat"); + break; + default: eDebugNoNewLine("unknown");break; } - else - { - eit_t *eit = (eit_t*) data; - __u32 sectionNo = data[0] << 24; - sectionNo |= data[3] << 16; - sectionNo |= data[4] << 8; - sectionNo |= eit->section_number; + eDebug(" finished(%ld)", ::time(0)); + 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); + tidMap::iterator it = + seenSections.find(sectionNo); - if ( it == seenSections.end() ) + 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 ) { - 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 ) { - 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)); + for (int x=i; x <= eit->segment_last_section_number; ++x) + calcedSections.insert(tmpval|(x&0xFF)); } - cache->sectionRead(data, source, this); + else + calcedSections.insert(tmpval|(i&0xFF)); } + cache->sectionRead(data, source, this); } } } @@ -1291,25 +1523,25 @@ RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, co // if t == -1 we search the current event... { singleLock s(cache_lock); - uniqueEPGKey key(service); + uniqueEPGKey key(handleGroup(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 = eDVBLocalTimeHandler::getInstance()->nowTime(); + t = ::time(0); 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) ) + if ( direction < 0 || (direction == 0 && i->first > t) ) { timeMap::iterator x = i; --x; if ( x != It->second.second.end() ) { - time_t start_time = x->second->getStartTime(); + time_t start_time = x->first; if (direction >= 0) { if (t < start_time) @@ -1367,7 +1599,7 @@ RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, eP RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, const eventData *&result ) { singleLock s(cache_lock); - uniqueEPGKey key( service ); + uniqueEPGKey key(handleGroup(service)); eventCache::iterator It = eventDB.find( key ); if ( It != eventDB.end() && !It->second.first.empty() ) // entrys cached? @@ -1381,7 +1613,7 @@ RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, else { result = 0; - eDebug("event %04x not found in epgcache", event_id); + eDebug("[EPGC] event %04x not found in epgcache", event_id); } } return -1; @@ -1424,33 +1656,36 @@ RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, RESULT eEPGCache::startTimeQuery(const eServiceReference &service, time_t begin, int minutes) { - eventCache::iterator It = eventDB.find( service ); + singleLock s(cache_lock); + const eServiceReferenceDVB &ref = (const eServiceReferenceDVB&)handleGroup(service); + if (begin == -1) + begin = ::time(0); + eventCache::iterator It = eventDB.find(ref); 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() ) { - m_timemap_cursor = It->second.second.lower_bound(begin); - if ( m_timemap_cursor != It->second.second.end() ) + if ( m_timemap_cursor->first != begin ) { - if ( m_timemap_cursor->second->getStartTime() != begin ) + timeMap::iterator x = m_timemap_cursor; + --x; + if ( x != It->second.second.end() ) { - 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; - } + time_t start_time = x->first; + if ( begin > start_time && begin < (start_time+x->second->getDuration())) + m_timemap_cursor = x; } } } + + if (minutes != -1) + m_timemap_end = It->second.second.lower_bound(begin+minutes*60); else - m_timemap_cursor = It->second.second.begin(); - const eServiceReferenceDVB &ref = (const eServiceReferenceDVB&)service; + m_timemap_end = It->second.second.end(); + currentQueryTsidOnid = (ref.getTransportStreamID().get()<<16) | ref.getOriginalNetworkID().get(); - return 0; + return m_timemap_cursor == m_timemap_end ? -1 : 0; } return -1; } @@ -1496,35 +1731,36 @@ RESULT eEPGCache::getNextTimeEntry(ePtr &result) return -1; } -void fillTuple(PyObject *tuple, char *argstring, int argcount, PyObject *service, ePtr &ptr, PyObject *nowTime, PyObject *service_name ) +void fillTuple(ePyObject tuple, const char *argstring, int argcount, ePyObject service, eServiceEvent *ptr, ePyObject nowTime, ePyObject service_name ) { - PyObject *tmp=NULL; - int pos=0; - while(pos < argcount) + ePyObject tmp; + int spos=0, tpos=0; + char c; + while(spos < argcount) { bool inc_refcount=false; - switch(argstring[pos]) + switch((c=argstring[spos++])) { case '0': // PyLong 0 tmp = PyLong_FromLong(0); break; case 'I': // Event Id - tmp = ptr ? PyLong_FromLong(ptr->getEventId()) : NULL; + tmp = ptr ? PyLong_FromLong(ptr->getEventId()) : ePyObject(); break; case 'B': // Event Begin Time - tmp = ptr ? PyLong_FromLong(ptr->getBeginTime()) : NULL; + tmp = ptr ? PyLong_FromLong(ptr->getBeginTime()) : ePyObject(); break; case 'D': // Event Duration - tmp = ptr ? PyLong_FromLong(ptr->getDuration()) : NULL; + tmp = ptr ? PyLong_FromLong(ptr->getDuration()) : ePyObject(); break; case 'T': // Event Title - tmp = ptr ? PyString_FromString(ptr->getEventName().c_str()) : NULL; + tmp = ptr ? PyString_FromString(ptr->getEventName().c_str()) : ePyObject(); break; case 'S': // Event Short Description - tmp = ptr ? PyString_FromString(ptr->getShortDescription().c_str()) : NULL; + tmp = ptr ? PyString_FromString(ptr->getShortDescription().c_str()) : ePyObject(); break; case 'E': // Event Extended Description - tmp = ptr ? PyString_FromString(ptr->getExtendedDescription().c_str()) : NULL; + tmp = ptr ? PyString_FromString(ptr->getExtendedDescription().c_str()) : ePyObject(); break; case 'C': // Current Time tmp = nowTime; @@ -1534,9 +1770,17 @@ void fillTuple(PyObject *tuple, char *argstring, int argcount, PyObject *service tmp = service; inc_refcount = true; break; + case 'n': // short service name case 'N': // service name tmp = service_name; inc_refcount = true; + break; + case 'X': + ++argcount; + continue; + default: // ignore unknown + tmp = ePyObject(); + eDebug("fillTuple unknown '%c'... insert 'None' in result", c); } if (!tmp) { @@ -1545,17 +1789,17 @@ void fillTuple(PyObject *tuple, char *argstring, int argcount, PyObject *service } if (inc_refcount) Py_INCREF(tmp); - PyTuple_SET_ITEM(tuple, pos++, tmp); + PyTuple_SET_ITEM(tuple, tpos++, tmp); } } -PyObject *handleEvent(ePtr &ptr, PyObject *dest_list, char* argstring, int argcount, PyObject *service, PyObject *nowTime, PyObject *service_name, PyObject *convertFunc, PyObject *convertFuncArgs) +int handleEvent(eServiceEvent *ptr, ePyObject dest_list, const char* argstring, int argcount, ePyObject service, ePyObject nowTime, ePyObject service_name, ePyObject convertFunc, ePyObject convertFuncArgs) { if (convertFunc) { fillTuple(convertFuncArgs, argstring, argcount, service, ptr, nowTime, service_name); - PyObject *result = PyObject_CallObject(convertFunc, convertFuncArgs); - if (result == NULL) + ePyObject result = PyObject_CallObject(convertFunc, convertFuncArgs); + if (!result) { if (service_name) Py_DECREF(service_name); @@ -1563,14 +1807,17 @@ PyObject *handleEvent(ePtr &ptr, PyObject *dest_list, char* argst Py_DECREF(nowTime); Py_DECREF(convertFuncArgs); Py_DECREF(dest_list); - return result; + PyErr_SetString(PyExc_StandardError, + "error in convertFunc execute"); + eDebug("error in convertFunc execute"); + return -1; } PyList_Append(dest_list, result); Py_DECREF(result); } else { - PyObject *tuple = PyTuple_New(argcount); + ePyObject tuple = PyTuple_New(argcount); fillTuple(tuple, argstring, argcount, service, ptr, nowTime, service_name); PyList_Append(dest_list, tuple); Py_DECREF(tuple); @@ -1590,6 +1837,10 @@ PyObject *handleEvent(ePtr &ptr, PyObject *dest_list, char* argst // C = Current Time // R = Service Reference // N = Service Name +// n = Short Service Name +// X = Return a minimum of one tuple per service in the result list... even when no event was found. +// The returned tuple is filled with all available infos... non avail is filled as None +// The position and existence of 'X' in the format string has no influence on the result tuple... its completely ignored.. // then for each service follows a tuple // first tuple entry is the servicereference (as string... use the ref.toString() function) // the second is the type of query @@ -1599,14 +1850,14 @@ PyObject *handleEvent(ePtr &ptr, PyObject *dest_list, char* argst // +1 = event after given start_time // 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 ) +// when type is time then it is the start_time ( -1 for now_time ) // the fourth is the end_time .. ( optional .. for query all events in time range) -PyObject *eEPGCache::lookupEvent(PyObject *list, PyObject *convertFunc) +PyObject *eEPGCache::lookupEvent(ePyObject list, ePyObject convertFunc) { - PyObject *convertFuncArgs=NULL; + ePyObject convertFuncArgs; int argcount=0; - char *argstring=NULL; + const char *argstring=NULL; if (!PyList_Check(list)) { PyErr_SetString(PyExc_StandardError, @@ -1625,7 +1876,7 @@ PyObject *eEPGCache::lookupEvent(PyObject *list, PyObject *convertFunc) } else { - PyObject *argv=PyList_GET_ITEM(list, 0); // borrowed reference! + ePyObject argv=PyList_GET_ITEM(list, 0); // borrowed reference! if (PyString_Check(argv)) { argstring = PyString_AS_STRING(argv); @@ -1636,6 +1887,11 @@ PyObject *eEPGCache::lookupEvent(PyObject *list, PyObject *convertFunc) argcount = strlen(argstring); // eDebug("have %d args('%s')", argcount, argstring); } + + bool forceReturnOne = strchr(argstring, 'X') ? true : false; + if (forceReturnOne) + --argcount; + if (convertFunc) { if (!PyCallable_Check(convertFunc)) @@ -1648,17 +1904,17 @@ PyObject *eEPGCache::lookupEvent(PyObject *list, PyObject *convertFunc) convertFuncArgs = PyTuple_New(argcount); } - PyObject *nowTime = strchr(argstring, 'C') ? - PyLong_FromLong(eDVBLocalTimeHandler::getInstance()->nowTime()) : - NULL; + ePyObject nowTime = strchr(argstring, 'C') ? + PyLong_FromLong(::time(0)) : + ePyObject(); - bool must_get_service_name = strchr(argstring, 'N') ? true : false; + int must_get_service_name = strchr(argstring, 'N') ? 1 : strchr(argstring, 'n') ? 2 : 0; // create dest list - PyObject *dest_list=PyList_New(0); + ePyObject dest_list=PyList_New(0); while(listSize > listIt) { - PyObject *item=PyList_GET_ITEM(list, listIt++); // borrowed reference! + ePyObject item=PyList_GET_ITEM(list, listIt++); // borrowed reference! if (PyTuple_Check(item)) { bool service_changed=false; @@ -1668,10 +1924,10 @@ PyObject *eEPGCache::lookupEvent(PyObject *list, PyObject *convertFunc) int minutes=0; int tupleSize=PyTuple_Size(item); int tupleIt=0; - PyObject *service=NULL; + ePyObject service; while(tupleSize > tupleIt) // parse query args { - PyObject *entry=PyTuple_GET_ITEM(item, tupleIt); // borrowed reference! + ePyObject entry=PyTuple_GET_ITEM(item, tupleIt); // borrowed reference! switch(tupleIt++) { case 0: @@ -1703,7 +1959,11 @@ PyObject *eEPGCache::lookupEvent(PyObject *list, PyObject *convertFunc) break; } } - eServiceReference ref(PyString_AS_STRING(service)); + + if (minutes && stime == -1) + stime = ::time(0); + + eServiceReference ref(handleGroup(eServiceReference(PyString_AS_STRING(service)))); if (ref.type != eServiceReference::idDVB) { eDebug("service reference for epg query is not valid"); @@ -1727,7 +1987,7 @@ PyObject *eEPGCache::lookupEvent(PyObject *list, PyObject *convertFunc) } } - PyObject *service_name=NULL; + ePyObject service_name; if (must_get_service_name) { ePtr sptr; @@ -1740,6 +2000,19 @@ PyObject *eEPGCache::lookupEvent(PyObject *list, PyObject *convertFunc) { std::string name; sptr->getName(ref, name); + + if (must_get_service_name == 1) + { + size_t pos; + // filter short name brakets + while((pos = name.find("\xc2\x86")) != std::string::npos) + name.erase(pos,2); + while((pos = name.find("\xc2\x87")) != std::string::npos) + name.erase(pos,2); + } + else + name = buildShortName(name); + if (name.length()) service_name = PyString_FromString(name.c_str()); } @@ -1749,32 +2022,46 @@ PyObject *eEPGCache::lookupEvent(PyObject *list, PyObject *convertFunc) } if (minutes) { - Lock(); + singleLock s(cache_lock); if (!startTimeQuery(ref, stime, minutes)) { - ePtr ptr; - while (!getNextTimeEntry(ptr)) + while ( m_timemap_cursor != m_timemap_end ) { - PyObject *ret = handleEvent(ptr, dest_list, argstring, argcount, service, nowTime, service_name, convertFunc, convertFuncArgs); - if (ret) - return ret; + Event ev((uint8_t*)m_timemap_cursor++->second->get()); + eServiceEvent evt; + evt.parseFrom(&ev, currentQueryTsidOnid); + if (handleEvent(&evt, dest_list, argstring, argcount, service, nowTime, service_name, convertFunc, convertFuncArgs)) + return 0; // error } } - Unlock(); + else if (forceReturnOne && handleEvent(0, dest_list, argstring, argcount, service, nowTime, service_name, convertFunc, convertFuncArgs)) + return 0; // error } else { - ePtr ptr; + eServiceEvent evt; + const eventData *ev_data=0; if (stime) { + singleLock s(cache_lock); if (type == 2) - lookupEventId(ref, event_id, ptr); + lookupEventId(ref, event_id, ev_data); else - lookupEventTime(ref, stime, ptr, type); + lookupEventTime(ref, stime, ev_data, type); + if (ev_data) + { + const eServiceReferenceDVB &dref = (const eServiceReferenceDVB&)ref; + Event ev((uint8_t*)ev_data->get()); + evt.parseFrom(&ev, (dref.getTransportStreamID().get()<<16)|dref.getOriginalNetworkID().get()); + } + } + if (ev_data) + { + if (handleEvent(&evt, dest_list, argstring, argcount, service, nowTime, service_name, convertFunc, convertFuncArgs)) + return 0; // error } - PyObject *ret = handleEvent(ptr, dest_list, argstring, argcount, service, nowTime, service_name, convertFunc, convertFuncArgs); - if (ret) - return ret; + else if (forceReturnOne && handleEvent(0, dest_list, argstring, argcount, service, nowTime, service_name, convertFunc, convertFuncArgs)) + return 0; // error } if (service_changed) Py_DECREF(service); @@ -1791,9 +2078,9 @@ skip_entry: return dest_list; } -void fillTuple2(PyObject *tuple, const char *argstring, int argcount, eventData *evData, ePtr &ptr, PyObject *service_name, PyObject *service_reference) +void fillTuple2(ePyObject tuple, const char *argstring, int argcount, eventData *evData, eServiceEvent *ptr, ePyObject service_name, ePyObject service_reference) { - PyObject *tmp=NULL; + ePyObject tmp; int pos=0; while(pos < argcount) { @@ -1808,33 +2095,37 @@ void fillTuple2(PyObject *tuple, const char *argstring, int argcount, eventData break; case 'B': // Event Begin Time if (ptr) - tmp = ptr ? PyLong_FromLong(ptr->getBeginTime()) : NULL; + tmp = ptr ? PyLong_FromLong(ptr->getBeginTime()) : ePyObject(); else tmp = PyLong_FromLong(evData->getStartTime()); break; case 'D': // Event Duration if (ptr) - tmp = ptr ? PyLong_FromLong(ptr->getDuration()) : NULL; + tmp = ptr ? PyLong_FromLong(ptr->getDuration()) : ePyObject(); else tmp = PyLong_FromLong(evData->getDuration()); break; case 'T': // Event Title - tmp = ptr ? PyString_FromString(ptr->getEventName().c_str()) : NULL; + tmp = ptr ? PyString_FromString(ptr->getEventName().c_str()) : ePyObject(); break; case 'S': // Event Short Description - tmp = ptr ? PyString_FromString(ptr->getShortDescription().c_str()) : NULL; + tmp = ptr ? PyString_FromString(ptr->getShortDescription().c_str()) : ePyObject(); break; case 'E': // Event Extended Description - tmp = ptr ? PyString_FromString(ptr->getExtendedDescription().c_str()) : NULL; + tmp = ptr ? PyString_FromString(ptr->getExtendedDescription().c_str()) : ePyObject(); break; case 'R': // service reference string tmp = service_reference; inc_refcount = true; break; + case 'n': // short service name case 'N': // service name tmp = service_name; inc_refcount = true; break; + default: // ignore unknown + tmp = ePyObject(); + eDebug("fillTuple2 unknown '%c'... insert None in Result", argstring[pos]); } if (!tmp) { @@ -1857,6 +2148,7 @@ void fillTuple2(PyObject *tuple, const char *argstring, int argcount, eventData // E = Event Extended Description // R = Service Reference // N = Service Name +// n = Short Service Name // the second tuple entry is the MAX matches value // the third tuple entry is the type of query // 0 = search for similar broadcastings (SIMILAR_BROADCASTINGS_SEARCH) @@ -1871,9 +2163,9 @@ void fillTuple2(PyObject *tuple, const char *argstring, int argcount, eventData // 0 = case sensitive (CASE_CHECK) // 1 = case insensitive (NO_CASECHECK) -PyObject *eEPGCache::search(PyObject *arg) +PyObject *eEPGCache::search(ePyObject arg) { - PyObject *ret = 0; + ePyObject ret; int descridx = -1; __u32 descr[512]; int eventid = -1; @@ -1883,16 +2175,22 @@ PyObject *eEPGCache::search(PyObject *arg) int querytype=-1; bool needServiceEvent=false; int maxmatches=0; + int must_get_service_name = 0; + bool must_get_service_reference = false; if (PyTuple_Check(arg)) { int tuplesize=PyTuple_Size(arg); if (tuplesize > 0) { - PyObject *obj = PyTuple_GET_ITEM(arg,0); + ePyObject obj = PyTuple_GET_ITEM(arg,0); if (PyString_Check(obj)) { +#if PY_VERSION_HEX < 0x02060000 argcount = PyString_GET_SIZE(obj); +#else + argcount = PyString_Size(obj); +#endif argstring = PyString_AS_STRING(obj); for (int i=0; i < argcount; ++i) switch(argstring[i]) @@ -1901,6 +2199,16 @@ PyObject *eEPGCache::search(PyObject *arg) case 'E': case 'T': needServiceEvent=true; + break; + case 'N': + must_get_service_name = 1; + break; + case 'n': + must_get_service_name = 2; + break; + case 'R': + must_get_service_reference = true; + break; default: break; } @@ -1920,7 +2228,7 @@ PyObject *eEPGCache::search(PyObject *arg) querytype = PyLong_AsLong(PyTuple_GET_ITEM(arg, 2)); if (tuplesize > 4 && querytype == 0) { - PyObject *obj = PyTuple_GET_ITEM(arg, 3); + ePyObject obj = PyTuple_GET_ITEM(arg, 3); if (PyString_Check(obj)) { refstr = PyString_AS_STRING(obj); @@ -1934,10 +2242,10 @@ PyObject *eEPGCache::search(PyObject *arg) if (evData) { __u8 *data = evData->EITdata; - int tmp = evData->ByteSize-12; - __u32 *p = (__u32*)(data+12); + int tmp = evData->ByteSize-10; + __u32 *p = (__u32*)(data+10); // search short and extended event descriptors - while(tmp>0) + while(tmp>3) { __u32 crc = *p++; descriptorMap::iterator it = @@ -1961,28 +2269,30 @@ PyObject *eEPGCache::search(PyObject *arg) } else { - PyErr_SetString(PyExc_StandardError, - "type error"); + PyErr_SetString(PyExc_StandardError, "type error"); eDebug("tuple arg 4 is not a valid service reference string"); return NULL; } } else { - PyErr_SetString(PyExc_StandardError, - "type error"); + PyErr_SetString(PyExc_StandardError, "type error"); eDebug("tuple arg 4 is not a string"); return NULL; } } else if (tuplesize > 4 && (querytype == 1 || querytype == 2) ) { - PyObject *obj = PyTuple_GET_ITEM(arg, 3); + ePyObject obj = PyTuple_GET_ITEM(arg, 3); if (PyString_Check(obj)) { int casetype = PyLong_AsLong(PyTuple_GET_ITEM(arg, 4)); const char *str = PyString_AS_STRING(obj); +#if PY_VERSION_HEX < 0x02060000 int textlen = PyString_GET_SIZE(obj); +#else + int textlen = PyString_Size(obj); +#endif if (querytype == 1) eDebug("lookup for events with '%s' as title(%s)", str, casetype?"ignore case":"case sensitive"); else @@ -1997,22 +2307,32 @@ PyObject *eEPGCache::search(PyObject *arg) int title_len = data[5]; if ( querytype == 1 ) { - if (title_len > textlen) - continue; - else if (title_len < textlen) + int offs = 6; + // skip DVB-Text Encoding! + if (data[6] == 0x10) + { + offs+=3; + title_len-=3; + } + else if(data[6] > 0 && data[6] < 0x20) + { + offs+=1; + title_len-=1; + } + if (title_len != textlen) continue; if ( casetype ) { - if ( !strncasecmp((const char*)data+6, str, title_len) ) + if ( !strncasecmp((const char*)data+offs, str, title_len) ) { -// std::string s((const char*)data+6, title_len); +// std::string s((const char*)data+offs, title_len); // eDebug("match1 %s %s", str, s.c_str() ); descr[++descridx] = it->first; } } - else if ( !strncmp((const char*)data+6, str, title_len) ) + else if ( !strncmp((const char*)data+offs, str, title_len) ) { -// std::string s((const char*)data+6, title_len); +// std::string s((const char*)data+offs, title_len); // eDebug("match2 %s %s", str, s.c_str() ); descr[++descridx] = it->first; } @@ -2031,13 +2351,13 @@ PyObject *eEPGCache::search(PyObject *arg) // eDebug("match 3 %s %s", str, s.c_str() ); break; } - else if (!strncmp((const char*)data+6+idx, str, textlen) ) - { - descr[++descridx] = it->first; -// std::string s((const char*)data+6, title_len); -// eDebug("match 4 %s %s", str, s.c_str() ); - break; - } + } + else if (!strncmp((const char*)data+6+idx, str, textlen) ) + { + descr[++descridx] = it->first; +// std::string s((const char*)data+6, title_len); +// eDebug("match 4 %s %s", str, s.c_str() ); + break; } ++idx; } @@ -2080,7 +2400,7 @@ PyObject *eEPGCache::search(PyObject *arg) if (descridx > -1) { int maxcount=maxmatches; - eServiceReferenceDVB ref(refstr?refstr:""); + eServiceReferenceDVB ref(refstr?(const eServiceReferenceDVB&)handleGroup(eServiceReference(refstr)):eServiceReferenceDVB("")); // ref is only valid in SIMILAR_BROADCASTING_SEARCH // in this case we start searching with the base service bool first = ref.valid() ? true : false; @@ -2094,20 +2414,21 @@ PyObject *eEPGCache::search(PyObject *arg) ++cit; continue; } - PyObject *service_name=0; - PyObject *service_reference=0; + ePyObject service_name; + ePyObject service_reference; timeMap &evmap = cit->second.second; // check all events for (timeMap::iterator evit(evmap.begin()); evit != evmap.end() && maxcount; ++evit) { - if (evit->second->getEventID() == eventid) + int evid = evit->second->getEventID(); + if ( evid == eventid) continue; __u8 *data = evit->second->EITdata; - int tmp = evit->second->ByteSize-12; - __u32 *p = (__u32*)(data+12); + int tmp = evit->second->ByteSize-10; + __u32 *p = (__u32*)(data+10); // check if any of our descriptor used by this event int cnt=-1; - while(tmp>0) + while(tmp>3) { __u32 crc32 = *p++; for ( int i=0; i <= descridx; ++i) @@ -2126,15 +2447,21 @@ PyObject *eEPGCache::search(PyObject *arg) if (ref.valid()) { // create servive event - ePtr ptr; + eServiceEvent ptr; + const eventData *ev_data=0; if (needServiceEvent) { - lookupEventId(ref, evit->first, ptr); - if (!ptr) + if (lookupEventId(ref, evid, ev_data)) eDebug("event not found !!!!!!!!!!!"); + else + { + const eServiceReferenceDVB &dref = (const eServiceReferenceDVB&)ref; + Event ev((uint8_t*)ev_data->get()); + ptr.parseFrom(&ev, (dref.getTransportStreamID().get()<<16)|dref.getOriginalNetworkID().get()); + } } // create service name - if (!service_name && strchr(argstring,'N')) + if (must_get_service_name && !service_name) { ePtr sptr; eServiceCenterPtr service_center; @@ -2146,6 +2473,19 @@ PyObject *eEPGCache::search(PyObject *arg) { std::string name; sptr->getName(ref, name); + + if (must_get_service_name == 1) + { + size_t pos; + // filter short name brakets + while((pos = name.find("\xc2\x86")) != std::string::npos) + name.erase(pos,2); + while((pos = name.find("\xc2\x87")) != std::string::npos) + name.erase(pos,2); + } + else + name = buildShortName(name); + if (name.length()) service_name = PyString_FromString(name.c_str()); } @@ -2154,15 +2494,15 @@ PyObject *eEPGCache::search(PyObject *arg) service_name = PyString_FromString(""); } // create servicereference string - if (!service_reference && strchr(argstring,'R')) + if (must_get_service_reference && !service_reference) service_reference = PyString_FromString(ref.toString().c_str()); // create list if (!ret) ret = PyList_New(0); // create tuple - PyObject *tuple = PyTuple_New(argcount); + ePyObject tuple = PyTuple_New(argcount); // fill tuple - fillTuple2(tuple, argstring, argcount, evit->second, ptr, service_name, service_reference); + fillTuple2(tuple, argstring, argcount, evit->second, ev_data ? &ptr : 0, service_name, service_reference); PyList_Append(ret, tuple); Py_DECREF(tuple); --maxcount; @@ -2185,10 +2525,7 @@ PyObject *eEPGCache::search(PyObject *arg) } if (!ret) - { - Py_INCREF(Py_None); - ret=Py_None; - } + Py_RETURN_NONE; return ret; } @@ -2214,6 +2551,50 @@ void eEPGCache::PMTready(eDVBServicePMTHandler *pmthandler) int tmp=0; switch ((*es)->getType()) { + case 0xC1: // user private + for (DescriptorConstIterator desc = (*es)->getDescriptors()->begin(); + desc != (*es)->getDescriptors()->end(); ++desc) + { + switch ((*desc)->getTag()) + { + case 0xC2: // user defined + if ((*desc)->getLength() == 8) + { + __u8 buffer[10]; + (*desc)->writeToBuffer(buffer); + if (!strncmp((unsigned char*)buffer+2, "EPGDATA", 7)) + { + eServiceReferenceDVB ref; + if (!pmthandler->getServiceReference(ref)) + { + int pid = (*es)->getPid(); + messages.send(Message(Message::got_mhw2_channel_pid, ref, pid)); + } + } + else if(!strncmp((unsigned char*)buffer+2, "FICHAS", 6)) + { + eServiceReferenceDVB ref; + if (!pmthandler->getServiceReference(ref)) + { + int pid = (*es)->getPid(); + messages.send(Message(Message::got_mhw2_summary_pid, ref, pid)); + } + } + else if(!strncmp((unsigned char*)buffer+2, "GENEROS", 7)) + { + eServiceReferenceDVB ref; + if (!pmthandler->getServiceReference(ref)) + { + int pid = (*es)->getPid(); + messages.send(Message(Message::got_mhw2_title_pid, ref, pid)); + } + } + } + break; + default: + break; + } + } case 0x05: // private for (DescriptorConstIterator desc = (*es)->getDescriptors()->begin(); desc != (*es)->getDescriptors()->end(); ++desc) @@ -2331,54 +2712,71 @@ void eEPGCache::privateSectionRead(const uniqueEPGKey ¤t_service, const __ int descriptors_length = (data[ptr++]&0x0F) << 8; descriptors_length |= data[ptr++]; - while ( descriptors_length > 0 ) + while ( descriptors_length > 1 ) { 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++]; - -// WORKAROUND for wrong transmitted epg data - if ( onid == 0x85 && tsid == 0x11 && sid == 0xd3 ) // premiere sends wrong tsid here - tsid = 0x1; - else if ( onid == 0x85 && tsid == 0x3 && sid == 0xf5 ) // premiere sends wrong sid here - sid = 0xdc; -//////////////////////////////////////////// - - uniqueEPGKey service( sid, onid, tsid ); - descr_len -= 6; - while( descr_len > 0 ) + descriptors_length -= 2; + if (descriptors_length >= descr_len) + { + descriptors_length -= descr_len; + if ( descr_type == 0xf2 && descr_len > 5) { - __u8 datetime[5]; - datetime[0] = data[ptr++]; - datetime[1] = data[ptr++]; - int tmp_len = data[ptr++]; - descr_len -= 3; - while( tmp_len > 0 ) + 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++]; + +// WORKAROUND for wrong transmitted epg data (01.10.2007) + if ( onid == 0x85 ) + { + switch( (tsid << 16) | sid ) + { + case 0x01030b: sid = 0x1b; tsid = 4; break; // Premiere Win + case 0x0300f0: sid = 0xe0; tsid = 2; break; + case 0x0300f1: sid = 0xe1; tsid = 2; break; + case 0x0300f5: sid = 0xdc; break; + case 0x0400d2: sid = 0xe2; tsid = 0x11; break; + case 0x1100d3: sid = 0xe3; break; + case 0x0100d4: sid = 0xe4; tsid = 4; break; + } + } +//////////////////////////////////////////// + + uniqueEPGKey service( sid, onid, tsid ); + descr_len -= 6; + while( descr_len > 2 ) { - memcpy(datetime+2, data+ptr, 3); - ptr+=3; + __u8 datetime[5]; + datetime[0] = data[ptr++]; + datetime[1] = data[ptr++]; + int tmp_len = data[ptr++]; descr_len -= 3; - tmp_len -= 3; - start_times[datetime].push_back(service); + if (descr_len >= tmp_len) + { + descr_len -= tmp_len; + while( tmp_len > 2 ) + { + memcpy(datetime+2, data+ptr, 3); + ptr += 3; + tmp_len -= 3; + start_times[datetime].push_back(service); + } + } } } - } - else - { - *pdescr++=data+ptr; - ptr += 2; - ptr += descr_len; + else + { + *pdescr++=data+ptr; + ptr += 2; + ptr += descr_len; + } } } + ASSERT(pdescr <= &descriptors[65]); __u8 event[4098]; eit_event_struct *ev_struct = (eit_event_struct*) event; ev_struct->running_status = 0; @@ -2392,9 +2790,10 @@ void eEPGCache::privateSectionRead(const uniqueEPGKey ¤t_service, const __ ptr+=(*d++)[1]; ptr+=2; } + ASSERT(ptr <= 4098); for ( std::map< date_time, std::list >::iterator it(start_times.begin()); it != start_times.end(); ++it ) { - time_t now = eDVBLocalTimeHandler::getInstance()->nowTime(); + time_t now = ::time(0); if ( (it->first.tm + duration_sec) < now ) continue; memcpy(event+2, it->first.data, 5); @@ -2431,6 +2830,7 @@ void eEPGCache::privateSectionRead(const uniqueEPGKey ¤t_service, const __ eventData *d = new eventData( ev_struct, bptr, PRIVATE ); evMap[event_id] = d; tmMap[stime] = d; + ASSERT(bptr <= 4098); } } @@ -2442,7 +2842,7 @@ void eEPGCache::channel_data::startPrivateReader() mask.flags = eDVBSectionFilterMask::rfCRC; mask.data[0] = 0xA0; mask.mask[0] = 0xFF; - eDebug("start privatefilter for pid %04x and version %d", m_PrivatePid, m_PrevVersion); + eDebug("[EPGC] start privatefilter for pid %04x and version %d", m_PrivatePid, m_PrevVersion); if (m_PrevVersion != -1) { mask.data[3] = m_PrevVersion << 1; @@ -2457,21 +2857,21 @@ void eEPGCache::channel_data::startPrivateReader() 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() ) { - if ( seenPrivateSections.find( data[6] ) == seenPrivateSections.end() ) - { - cache->privateSectionRead(m_PrivateService, data); - seenPrivateSections.insert(data[6]); - } - if ( seenPrivateSections.size() == (unsigned int)(data[7] + 1) ) - { - eDebug("[EPGC] private finished"); - m_PrevVersion = (data[5] & 0x3E) >> 1; - startPrivateReader(); - } + cache->privateSectionRead(m_PrivateService, data); + seenPrivateSections.insert(data[6]); + } + if ( seenPrivateSections.size() == (unsigned int)(data[7] + 1) ) + { + eDebug("[EPGC] private finished"); + eDVBChannelID chid = channel->getChannelID(); + int tmp = chid.original_network_id.get(); + tmp |= 0x80000000; // we use highest bit as private epg indicator + chid.original_network_id = tmp; + cache->channelLastUpdated[chid] = ::time(0); + m_PrevVersion = (data[5] & 0x3E) >> 1; + startPrivateReader(); } } @@ -2519,6 +2919,7 @@ void eEPGCache::channel_data::timeMHW2DVB( int minutes, u_char *return_time) void eEPGCache::channel_data::timeMHW2DVB( u_char day, u_char hours, u_char minutes, u_char *return_time) // For date plus time of day { + char tz_saved[1024]; // Remove offset in mhw time. __u8 local_hours = hours; if ( hours >= 16 ) @@ -2528,54 +2929,62 @@ void eEPGCache::channel_data::timeMHW2DVB( u_char day, u_char hours, u_char minu // As far as we know all mhw time data is sent in central Europe time zone. // So, temporarily set timezone to western europe - time_t dt = eDVBLocalTimeHandler::getInstance()->nowTime(); + time_t dt = ::time(0); char *old_tz = getenv( "TZ" ); + if (old_tz) + strcpy(tz_saved, old_tz); putenv("TZ=CET-1CEST,M3.5.0/2,M10.5.0/3"); tzset(); - tm *localnow = localtime( &dt ); + tm localnow; + localtime_r(&dt, &localnow); if (day == 7) day = 0; - if ( day + 1 < localnow->tm_wday ) // day + 1 to prevent old events to show for next week. + if ( day + 1 < localnow.tm_wday ) // day + 1 to prevent old events to show for next week. day += 7; if (local_hours <= 5) day++; - dt += 3600*24*(day - localnow->tm_wday); // Shift dt to the recording date (local time zone). - dt += 3600*(local_hours - localnow->tm_hour); // Shift dt to the recording hour. + dt += 3600*24*(day - localnow.tm_wday); // Shift dt to the recording date (local time zone). + dt += 3600*(local_hours - localnow.tm_hour); // Shift dt to the recording hour. - tm *recdate = gmtime( &dt ); // This will also take care of DST. + tm recdate; + gmtime_r( &dt, &recdate ); // This will also take care of DST. if ( old_tz == NULL ) unsetenv( "TZ" ); else - putenv( old_tz ); + setenv("TZ", tz_saved, 1); tzset(); // Calculate MJD according to annex in ETSI EN 300 468 int l=0; - if ( recdate->tm_mon <= 1 ) // Jan or Feb + if ( recdate.tm_mon <= 1 ) // Jan or Feb l=1; - int mjd = 14956 + recdate->tm_mday + int( (recdate->tm_year - l) * 365.25) + - int( (recdate->tm_mon + 2 + l * 12) * 30.6001); + int mjd = 14956 + recdate.tm_mday + int( (recdate.tm_year - l) * 365.25) + + int( (recdate.tm_mon + 2 + l * 12) * 30.6001); return_time[0] = (mjd & 0xFF00)>>8; return_time[1] = mjd & 0xFF; - timeMHW2DVB( recdate->tm_hour, minutes, return_time+2 ); + timeMHW2DVB( recdate.tm_hour, minutes, return_time+2 ); } void eEPGCache::channel_data::storeTitle(std::map<__u32, mhw_title_t>::iterator itTitle, std::string sumText, const __u8 *data) // data is borrowed from calling proc to save memory space. { + __u8 name[34]; + // For each title a separate EIT packet will be sent to eEPGCache::sectionRead() - __u8 name[24]; + bool isMHW2 = itTitle->second.mhw2_mjd_hi || itTitle->second.mhw2_mjd_lo || + itTitle->second.mhw2_duration_hi || itTitle->second.mhw2_duration_lo; eit_t *packet = (eit_t *) data; packet->table_id = 0x50; packet->section_syntax_indicator = 1; + packet->service_id_hi = m_channels[ itTitle->second.channel_id - 1 ].channel_id_hi; packet->service_id_lo = m_channels[ itTitle->second.channel_id - 1 ].channel_id_lo; packet->version_number = 0; // eEPGCache::sectionRead() will dig this for the moment @@ -2589,7 +2998,8 @@ void eEPGCache::channel_data::storeTitle(std::map<__u32, mhw_title_t>::iterator packet->segment_last_section_number = 0; // eEPGCache::sectionRead() will dig this for the moment packet->segment_last_table_id = 0x50; - std::string prog_title = (char *) delimitName( itTitle->second.title, name, 23 ); + __u8 *title = isMHW2 ? ((__u8*)(itTitle->second.title))-4 : (__u8*)itTitle->second.title; + std::string prog_title = (char *) delimitName( title, name, isMHW2 ? 35 : 23 ); int prog_title_length = prog_title.length(); int packet_length = EIT_SIZE + EIT_LOOP_SIZE + EIT_SHORT_EVENT_DESCRIPTOR_SIZE + @@ -2599,9 +3009,22 @@ void eEPGCache::channel_data::storeTitle(std::map<__u32, mhw_title_t>::iterator event_data->event_id_hi = (( itTitle->first ) >> 8 ) & 0xFF; event_data->event_id_lo = ( itTitle->first ) & 0xFF; - timeMHW2DVB( itTitle->second.day, itTitle->second.hours, itTitle->second.minutes, + if (isMHW2) + { + u_char *data = (u_char*) event_data; + data[2] = itTitle->second.mhw2_mjd_hi; + data[3] = itTitle->second.mhw2_mjd_lo; + data[4] = itTitle->second.mhw2_hours; + data[5] = itTitle->second.mhw2_minutes; + data[6] = itTitle->second.mhw2_seconds; + timeMHW2DVB( HILO(itTitle->second.mhw2_duration), data+7 ); + } + else + { + timeMHW2DVB( itTitle->second.dh.day, itTitle->second.dh.hours, itTitle->second.ms.minutes, (u_char *) event_data + 2 ); - timeMHW2DVB( HILO(itTitle->second.duration), (u_char *) event_data+7 ); + timeMHW2DVB( HILO(itTitle->second.duration), (u_char *) event_data+7 ); + } event_data->running_status = 0; event_data->free_CA_mode = 0; @@ -2616,9 +3039,8 @@ void eEPGCache::channel_data::storeTitle(std::map<__u32, mhw_title_t>::iterator short_event_descriptor->language_code_2 = 'n'; short_event_descriptor->language_code_3 = 'g'; short_event_descriptor->event_name_length = prog_title_length; - delimitName( itTitle->second.title, name, 23 ); u_char *event_name = (u_char *) short_event_descriptor + EIT_SHORT_EVENT_DESCRIPTOR_SIZE; - memcpy(event_name, name, prog_title_length); + memcpy(event_name, prog_title.c_str(), prog_title_length); // Set text length event_name[prog_title_length] = 0; @@ -2665,22 +3087,26 @@ void eEPGCache::channel_data::storeTitle(std::map<__u32, mhw_title_t>::iterator } } } - // Add content descriptor - u_char *descriptor = (u_char *) data + packet_length; - packet_length += 4; - descr_ll += 4; - int content_id = 0; - std::string content_descr = (char *) delimitName( m_themes[itTitle->second.theme_id].name, name, 15 ); - if ( content_descr.find( "FILM" ) != std::string::npos ) - content_id = 0x10; - else if ( content_descr.find( "SPORT" ) != std::string::npos ) - content_id = 0x40; + if (!isMHW2) + { + // Add content descriptor + u_char *descriptor = (u_char *) data + packet_length; + packet_length += 4; + descr_ll += 4; + + int content_id = 0; + std::string content_descr = (char *) delimitName( m_themes[itTitle->second.theme_id].name, name, 15 ); + if ( content_descr.find( "FILM" ) != std::string::npos ) + content_id = 0x10; + else if ( content_descr.find( "SPORT" ) != std::string::npos ) + content_id = 0x40; - descriptor[0] = 0x54; - descriptor[1] = 2; - descriptor[2] = content_id; - descriptor[3] = 0; + descriptor[0] = 0x54; + descriptor[1] = 2; + descriptor[2] = content_id; + descriptor[3] = 0; + } event_data->descriptors_loop_length_hi = (descr_ll & 0xf00)>>8; event_data->descriptors_loop_length_lo = (descr_ll & 0xff); @@ -2694,7 +3120,7 @@ void eEPGCache::channel_data::storeTitle(std::map<__u32, mhw_title_t>::iterator void eEPGCache::channel_data::startTimeout(int msec) { - m_MHWTimeoutTimer.start(msec,true); + m_MHWTimeoutTimer->start(msec,true); m_MHWTimeoutet=false; } @@ -2706,11 +3132,33 @@ void eEPGCache::channel_data::startMHWReader(__u16 pid, __u8 tid) // eDebug("start 0x%02x 0x%02x", pid, tid); } +void eEPGCache::channel_data::startMHWReader2(__u16 pid, __u8 tid, int ext) +{ + m_MHWFilterMask2.pid = pid; + m_MHWFilterMask2.data[0] = tid; + if (ext != -1) + { + m_MHWFilterMask2.data[1] = ext; + m_MHWFilterMask2.mask[1] = 0xFF; +// eDebug("start 0x%03x 0x%02x 0x%02x", pid, tid, ext); + } + else + { + m_MHWFilterMask2.data[1] = 0; + m_MHWFilterMask2.mask[1] = 0; +// eDebug("start 0x%02x 0x%02x", pid, tid); + } + m_MHWReader2->start(m_MHWFilterMask2); +} + void eEPGCache::channel_data::readMHWData(const __u8 *data) { + if ( m_MHWReader2 ) + m_MHWReader2->stop(); + if ( state > 1 || // aborted // have si data.. so we dont read mhw data - (haveData & (SCHEDULE|SCHEDULE_OTHER)) ) + (haveData & (SCHEDULE|SCHEDULE_OTHER|VIASAT)) ) { eDebug("[EPGC] mhw aborted %d", state); } @@ -2721,14 +3169,15 @@ void eEPGCache::channel_data::readMHWData(const __u8 *data) int record_size = sizeof( mhw_channel_name_t ); int nbr_records = int (len/record_size); + m_channels.resize(nbr_records); for ( int i = 0; i < nbr_records; i++ ) { mhw_channel_name_t *channel = (mhw_channel_name_t*) &data[4 + i*record_size]; - m_channels.push_back( *channel ); + m_channels[i]=*channel; } haveData |= MHW; - eDebug("channels finished %d found", m_channels.size()); + eDebug("[EPGC] mhw %d channels found", m_channels.size()); // Channels table has been read, start reading the themes table. startMHWReader(0xD3, 0x92); @@ -2759,7 +3208,7 @@ void eEPGCache::channel_data::readMHWData(const __u8 *data) m_themes[idx+sub_idx] = *theme; } - eDebug("themes finished %d found", m_themes.size()); + eDebug("[EPGC] mhw %d themes found", m_themes.size()); // Themes table has been read, start reading the titles table. startMHWReader(0xD2, 0x90); startTimeout(4000); @@ -2775,18 +3224,22 @@ void eEPGCache::channel_data::readMHWData(const __u8 *data) else { // Create unique key per title - __u32 title_id = ((title->channel_id)<<16)|((title->day)<<13)|((title->hours)<<8)| - (title->minutes); + __u32 title_id = ((title->channel_id)<<16)|((title->dh.day)<<13)|((title->dh.hours)<<8)| + (title->ms.minutes); __u32 program_id = ((title->program_id_hi)<<24)|((title->program_id_mh)<<16)| ((title->program_id_ml)<<8)|(title->program_id_lo); if ( m_titles.find( title_id ) == m_titles.end() ) { startTimeout(4000); + title->mhw2_mjd_hi = 0; + title->mhw2_mjd_lo = 0; + title->mhw2_duration_hi = 0; + title->mhw2_duration_lo = 0; m_titles[ title_id ] = *title; - if ( (title->summary_available) && (m_program_ids.find(program_id) == m_program_ids.end()) ) + if ( (title->ms.summary_available) && (m_program_ids.find(program_id) == m_program_ids.end()) ) // program_ids will be used to gather summaries. - m_program_ids[ program_id ] = title_id; + m_program_ids.insert(std::pair<__u32,__u32>(program_id,title_id)); return; // Continue reading of the current table. } else if (!checkTimeout()) @@ -2797,7 +3250,7 @@ void eEPGCache::channel_data::readMHWData(const __u8 *data) // Titles table has been read, there are summaries to read. // Start reading summaries, store corresponding titles on the fly. startMHWReader(0xD3, 0x90); - eDebug("titles finished %d titles, %d with summary", + eDebug("[EPGC] mhw %d titles(%d with summary) found", m_titles.size(), m_program_ids.size()); startTimeout(4000); @@ -2819,7 +3272,7 @@ void eEPGCache::channel_data::readMHWData(const __u8 *data) memcpy(&tmp, &data, sizeof(void*)); tmp[len+3] = 0; // Terminate as a string. - std::map<__u32, __u32>::iterator itProgid( m_program_ids.find( program_id ) ); + std::multimap<__u32, __u32>::iterator itProgid( m_program_ids.find( program_id ) ); if ( itProgid == m_program_ids.end() ) { /* This part is to prevent to looping forever if some summaries are not received yet. There is a timeout of 4 sec. after the last successfully read summary. */ @@ -2847,13 +3300,13 @@ void eEPGCache::channel_data::readMHWData(const __u8 *data) return; // Continue reading of the current table. } } + eDebug("[EPGC] mhw finished(%ld) %d summaries not found", + ::time(0), + m_program_ids.size()); // Summaries have been read, titles that have summaries have been stored. // Now store titles that do not have summaries. for (std::map<__u32, mhw_title_t>::iterator itTitle(m_titles.begin()); itTitle != m_titles.end(); itTitle++) storeTitle( itTitle, "", data ); - eDebug("[EPGC] mhw finished(%ld) %d summaries left", - eDVBLocalTimeHandler::getInstance()->nowTime(), - m_program_ids.size()); isRunning &= ~MHW; m_MHWConn=0; if ( m_MHWReader ) @@ -2862,4 +3315,317 @@ void eEPGCache::channel_data::readMHWData(const __u8 *data) finishEPG(); } +void eEPGCache::channel_data::readMHWData2(const __u8 *data) +{ + int dataLen = (((data[1]&0xf) << 8) | data[2]) + 3; + + if ( m_MHWReader ) + m_MHWReader->stop(); + + if ( state > 1 || // aborted + // have si data.. so we dont read mhw data + (haveData & (SCHEDULE|SCHEDULE_OTHER|VIASAT)) ) + { + eDebug("[EPGC] mhw2 aborted %d", state); + } + else if (m_MHWFilterMask2.pid == m_mhw2_channel_pid && m_MHWFilterMask2.data[0] == 0xC8 && m_MHWFilterMask2.data[1] == 0) + // Channels table + { + int num_channels = data[120]; + m_channels.resize(num_channels); + if(dataLen > 120) + { + int ptr = 121 + 8 * num_channels; + if( dataLen > ptr ) + { + for( int chid = 0; chid < num_channels; ++chid ) + { + ptr += ( data[ptr] & 0x0f ) + 1; + if( dataLen < ptr ) + goto abort; + } + } + else + goto abort; + } + else + goto abort; + // data seems consistent... + const __u8 *tmp = data+121; + for (int i=0; i < num_channels; ++i) + { + mhw_channel_name_t channel; + channel.network_id_hi = *(tmp++); + channel.network_id_lo = *(tmp++); + channel.transport_stream_id_hi = *(tmp++); + channel.transport_stream_id_lo = *(tmp++); + channel.channel_id_hi = *(tmp++); + channel.channel_id_lo = *(tmp++); + m_channels[i]=channel; +// eDebug("%d(%02x) %04x: %02x %02x", i, i, (channel.channel_id_hi << 8) | channel.channel_id_lo, *tmp, *(tmp+1)); + tmp+=2; + } + for (int i=0; i < num_channels; ++i) + { + mhw_channel_name_t &channel = m_channels[i]; + int channel_name_len=*(tmp++)&0x0f; + int x=0; + for (; x < channel_name_len; ++x) + channel.name[x]=*(tmp++); + channel.name[x+1]=0; +// eDebug("%d(%02x) %s", i, i, channel.name); + } + haveData |= MHW; + eDebug("[EPGC] mhw2 %d channels found", m_channels.size()); + } + else if (m_MHWFilterMask2.pid == m_mhw2_channel_pid && m_MHWFilterMask2.data[0] == 0xC8 && m_MHWFilterMask2.data[1] == 1) + { + // Themes table + eDebug("[EPGC] mhw2 themes nyi"); + } + else if (m_MHWFilterMask2.pid == m_mhw2_title_pid && m_MHWFilterMask2.data[0] == 0xe6) + // Titles table + { + int pos=18; + bool valid=false; + bool finish=false; + +// eDebug("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", +// data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], +// data[11], data[12], data[13], data[14], data[15], data[16], data[17] ); + + while( pos < dataLen && !valid) + { + pos += 18; + pos += (data[pos] & 0x3F) + 4; + if( pos == dataLen ) + valid = true; + } + + if (!valid) + { + if (dataLen > 18) + eDebug("mhw2 title table invalid!!"); + if (checkTimeout()) + goto abort; + if (!m_MHWTimeoutTimer->isActive()) + startTimeout(5000); + return; // continue reading + } + + // data seems consistent... + mhw_title_t title; + pos = 18; + while (pos < dataLen) + { +// eDebugNoNewLine(" [%02x] %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x [%02x %02x %02x %02x %02x %02x %02x] LL - DESCR - ", +// data[pos], data[pos+1], data[pos+2], data[pos+3], data[pos+4], data[pos+5], data[pos+6], data[pos+7], +// data[pos+8], data[pos+9], data[pos+10], data[pos+11], data[pos+12], data[pos+13], data[pos+14], data[pos+15], data[pos+16], data[pos+17]); + title.channel_id = data[pos]+1; + title.mhw2_mjd_hi = data[pos+11]; + title.mhw2_mjd_lo = data[pos+12]; + title.mhw2_hours = data[pos+13]; + title.mhw2_minutes = data[pos+14]; + title.mhw2_seconds = data[pos+15]; + int duration = ((data[pos+16] << 8)|data[pos+17]) >> 4; + title.mhw2_duration_hi = (duration&0xFF00) >> 8; + title.mhw2_duration_lo = duration&0xFF; + + // Create unique key per title + __u32 title_id = (data[pos+7] << 24) | (data[pos+8] << 16) | (data[pos+9] << 8) | data[pos+10]; + + __u8 slen = data[pos+18] & 0x3f; + __u8 *dest = ((__u8*)title.title)-4; + memcpy(dest, &data[pos+19], slen>35 ? 35 : slen); + memset(dest+slen, 0, 35-slen); + pos += 19 + slen; +// eDebug("%02x [%02x %02x]: %s", data[pos], data[pos+1], data[pos+2], dest); + +// not used theme id (data[7] & 0x3f) + (data[pos] & 0x3f); + __u32 summary_id = (data[pos+1] << 8) | data[pos+2]; + +// if (title.channel_id > m_channels.size()) +// eDebug("channel_id(%d %02x) to big!!", title.channel_id); + +// eDebug("pos %d prog_id %02x %02x chid %02x summary_id %04x dest %p len %d\n", +// pos, title.program_id_ml, title.program_id_lo, title.channel_id, summary_id, dest, slen); + +// eDebug("title_id %08x -> summary_id %04x\n", title_id, summary_id); + + pos += 3; + + std::map<__u32, mhw_title_t>::iterator it = m_titles.find( title_id ); + if ( it == m_titles.end() ) + { + startTimeout(5000); + m_titles[ title_id ] = title; + if (summary_id != 0xFFFF) + { + bool add=true; + std::multimap<__u32, __u32>::iterator it(m_program_ids.lower_bound(summary_id)); + while (it != m_program_ids.end() && it->first == summary_id) + { + if (it->second == title_id) { + add=false; + break; + } + ++it; + } + if (add) + m_program_ids.insert(std::pair<__u32,__u32>(summary_id,title_id)); + } + } + else + { + if ( !checkTimeout() ) + continue; // Continue reading of the current table. + finish=true; + break; + } + } +start_summary: + if (finish) + { + eDebug("[EPGC] mhw2 %d titles(%d with summary) found", m_titles.size(), m_program_ids.size()); + if (!m_program_ids.empty()) + { + // Titles table has been read, there are summaries to read. + // Start reading summaries, store corresponding titles on the fly. + startMHWReader2(m_mhw2_summary_pid, 0x96); + startTimeout(15000); + return; + } + } + else + return; + } + else if (m_MHWFilterMask2.pid == m_mhw2_summary_pid && m_MHWFilterMask2.data[0] == 0x96) + // Summaries table + { + if (!checkTimeout()) + { + int len, loop, pos, lenline; + bool valid; + valid = true; + if( dataLen > 15 ) + { + loop = data[14]; + pos = 15 + loop; + if( dataLen > pos ) + { + loop = data[pos] & 0x0f; + pos += 1; + if( dataLen > pos ) + { + len = 0; + for( ; loop > 0; --loop ) + { + if( dataLen > (pos+len) ) + { + lenline = data[pos+len]; + len += lenline + 1; + } + else + valid=false; + } + } + } + } + else + return; // continue reading + + if (valid) + { + // data seems consistent... + __u32 summary_id = (data[3]<<8)|data[4]; +// eDebug ("summary id %04x\n", summary_id); +// eDebug("[%02x %02x] %02x %02x %02x %02x %02x %02x %02x %02x XX\n", data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11], data[12], data[13] ); + + // ugly workaround to convert const __u8* to char* + char *tmp=0; + memcpy(&tmp, &data, sizeof(void*)); + + len = 0; + loop = data[14]; + pos = 15 + loop; + loop = tmp[pos] & 0x0f; + pos += 1; + for( ; loop > 0; loop -- ) + { + lenline = tmp[pos+len]; + tmp[pos+len] = ' '; + len += lenline + 1; + } + if( len > 0 ) + tmp[pos+len] = 0; + else + tmp[pos+1] = 0; + + std::multimap<__u32, __u32>::iterator itProgId( m_program_ids.lower_bound(summary_id) ); + if ( itProgId == m_program_ids.end() || itProgId->first != summary_id) + { /* This part is to prevent to looping forever if some summaries are not received yet. + There is a timeout of 4 sec. after the last successfully read summary. */ + if ( !m_program_ids.empty() ) + return; // Continue reading of the current table. + } + else + { + startTimeout(15000); + std::string the_text = (char *) (data + pos + 1); + +// eDebug ("summary id %04x : %s\n", summary_id, data+pos+1); + + while( itProgId != m_program_ids.end() && itProgId->first == summary_id ) + { +// eDebug("."); + // Find corresponding title, store title and summary in epgcache. + std::map<__u32, mhw_title_t>::iterator itTitle( m_titles.find( itProgId->second ) ); + if ( itTitle != m_titles.end() ) + { + storeTitle( itTitle, the_text, data ); + m_titles.erase( itTitle ); + } + m_program_ids.erase( itProgId++ ); + } + if ( !m_program_ids.empty() ) + return; // Continue reading of the current table. + } + } + else + return; // continue reading + } + } + if (isRunning & eEPGCache::MHW) + { + if ( m_MHWFilterMask2.pid == m_mhw2_channel_pid && m_MHWFilterMask2.data[0] == 0xC8 && m_MHWFilterMask2.data[1] == 0) + { + // Channels table has been read, start reading the themes table. + startMHWReader2(m_mhw2_channel_pid, 0xC8, 1); + return; + } + else if ( m_MHWFilterMask2.pid == m_mhw2_channel_pid && m_MHWFilterMask2.data[0] == 0xC8 && m_MHWFilterMask2.data[1] == 1) + { + // Themes table has been read, start reading the titles table. + startMHWReader2(m_mhw2_title_pid, 0xe6); + return; + } + else + { + // Summaries have been read, titles that have summaries have been stored. + // Now store titles that do not have summaries. + for (std::map<__u32, mhw_title_t>::iterator itTitle(m_titles.begin()); itTitle != m_titles.end(); itTitle++) + storeTitle( itTitle, "", data ); + eDebug("[EPGC] mhw2 finished(%ld) %d summaries not found", + ::time(0), + m_program_ids.size()); + } + } +abort: + isRunning &= ~MHW; + m_MHWConn2=0; + if ( m_MHWReader2 ) + m_MHWReader2->stop(); + if (haveData) + finishEPG(); +} #endif