diff -ru enigma2.orig/lib/dvb/epgcache.cpp enigma2/lib/dvb/epgcache.cpp --- enigma2.orig/lib/dvb/epgcache.cpp 2008-06-01 17:00:43.000000000 +0200 +++ enigma2/lib/dvb/epgcache.cpp 2008-06-01 09:21:42.000000000 +0200 @@ -1,6 +1,8 @@ #include #include +#include + #undef EPG_DEBUG #ifdef EPG_DEBUG @@ -2005,6 +2007,399 @@ } } +std::string getStringFromPython(ePyObject obj) +{ + std::string result; + if (PyString_Check(obj)) + { + char *str = PyString_AS_STRING(obj); + result = str; + } + return result; +} + +//here we get a python tuple of tuples ;) +// consider it an array of objects with the following data +// 1. start time +// 2. duration +// 3. event title +// 4. short description +// 5. extended description +// 6. event type +void eEPGCache::importEvent(ePyObject serviceReference, ePyObject list) +{ + if (!PyString_Check(serviceReference)) + { + eDebug("[EPG:import] serviceReference does not pass PyString_Check, aborting"); + return; + } + + char *refstr = PyString_AS_STRING(serviceReference); + if (!refstr) + { + eDebug("[EPG:import] serviceReference string is 0, aborting"); + return; + } + eServiceReferenceDVB ref(refstr); + if (!ref.valid()) + { + std::string s("[EPG:import] invalid reference: "); + s += refstr; + s += ", aborting"; + eDebug(s.c_str()); + return; + } + if (!PyTuple_Check(list)) + { + eDebug("[EPG:import] argument 'list' does not pas PyTuple_Check, aborting"); + return; + } + + int numberOfEvents = PyTuple_Size(list); + if (numberOfEvents < 1) + { + eDebug("[EPG:import] numberOfEvents less than 1, aborting"); + return; + } + + singleLock s(cache_lock); + for (int i = 0; i < numberOfEvents; ++i) + { + ePyObject singleEvent = PyTuple_GET_ITEM(list, i); + if (!PyTuple_Check(singleEvent)) + { + eDebug("[EPG:import] eventdata tuple does not pass PyTuple_Check, aborting"); + return; + } + int tupleSize = PyTuple_Size(singleEvent); + if (tupleSize < 5) + { + eDebug("[EPG:import] eventdata tuple does not contain enough fields, aborting"); + return; + } + + //Possible improvment: make this a bit less copy-intensive (pass around char pointers instead of std::strings) + UnpackedEventData eventData; + eventData.service = ref; + eventData.start = PyLong_AsLong(PyTuple_GET_ITEM(singleEvent, 0)); + eventData.duration = PyInt_AsLong(PyTuple_GET_ITEM(singleEvent, 1)); + eventData.title = getStringFromPython(PyTuple_GET_ITEM(singleEvent, 2)); + eventData.short_summary = getStringFromPython(PyTuple_GET_ITEM(singleEvent, 3)); + eventData.long_description = getStringFromPython(PyTuple_GET_ITEM(singleEvent, 4)); + eventData.type = (char) PyInt_AsLong(PyTuple_GET_ITEM(singleEvent, 5)); + + addNewEvent(eventData); + } +} + +__u16 generateEventId(const UnpackedEventData& eventInfo) +{ + return (eventInfo.start & 0xFFFF); +} +/* +int toBCD(int dec) +{ + if (dec < 0 || dec >99) + return -1; + return ( ((dec / 10) << 4) | (dec%10) ); +} +*/ +void fill_eit_start(eit_event_struct *evt, time_t t) +{ + tm *time = gmtime(&t); + + int l = 0; + int month = time->tm_mon + 1; + if (month == 1 || month == 2) + l = 1; + int mjd = 14956 + time->tm_mday + (int)((time->tm_year - l) * 365.25) + (int)((month + 1 + l*12) * 30.6001); + evt->start_time_1 = mjd >> 8; + evt->start_time_2 = mjd & 0xFF; + + evt->start_time_3 = toBCD(time->tm_hour); + evt->start_time_4 = toBCD(time->tm_min); + evt->start_time_5 = toBCD(time->tm_sec); + +} + +void fill_eit_duration(eit_event_struct *evt, int time) +{ + //time is given in second + //convert to hour, minutes, seconds + evt->duration_1 = toBCD(time / 3600); + evt->duration_2 = toBCD((time % 3600) / 60); + evt->duration_3 = toBCD((time % 3600) % 60); +} + +inline __u8 HI(int x) { return (__u8) ((x >> 8) & 0xFF); } +inline __u8 LO(int x) { return (__u8) (x & 0xFF); } + +// convert from set of strings to DVB format (EIT) +eventData* createEventData(const UnpackedEventData& eventInfo) +{ + eventData *evt = 0; + static const int EIT_LENGTH = 4108; + __u8 *data = new __u8[EIT_LENGTH]; + eit_event_struct *evt_struct = (eit_event_struct*) data; +//make sure length is less than 4108!!!!! + + __u16 eventId = generateEventId(eventInfo); + evt_struct->event_id_hi = HI(eventId); + evt_struct->event_id_lo = LO(eventId); + + //6 bytes start time, 3 bytes duration + fill_eit_start(evt_struct, eventInfo.start); + fill_eit_duration(evt_struct, eventInfo.duration); + + evt_struct->running_status = 0; + evt_struct->free_CA_mode = 0; + + //no support for different code pages, only DVB's latin1 character set + __u8 *x = (__u8 *) evt_struct; + x += EIT_LOOP_SIZE; + int nameLength = eventInfo.title.length(); + int descLength = eventInfo.short_summary.length(); + if (nameLength > 248) + { + nameLength = 248; + descLength = 0; + } + if (nameLength + descLength > 248) + { + descLength = 248 - nameLength; + } + eit_short_event_descriptor_struct *short_evt = (eit_short_event_descriptor_struct*) x; + short_evt->descriptor_tag = SHORT_EVENT_DESCRIPTOR; + short_evt->descriptor_length = EIT_SHORT_EVENT_DESCRIPTOR_SIZE + nameLength + descLength + 1 - 2; //+1 for length of short description, -2 for tag and length + short_evt->language_code_1 = 'e'; + short_evt->language_code_2 = 'n'; + short_evt->language_code_3 = 'g'; + short_evt->event_name_length = nameLength; + x = (__u8 *) short_evt; + x += EIT_SHORT_EVENT_DESCRIPTOR_SIZE; + memcpy(x, eventInfo.title.data(), nameLength); + x += nameLength; + *x = descLength; + x += 1; + if (descLength) //not sure if memcpy can handle length of zero.... + { + memcpy(x, eventInfo.short_summary.data(), descLength); + x += descLength; + } + + //Content type + if (eventInfo.type != 0) + { + x[0] = 0x54; + x[1] = 2; + x[2] = eventInfo.type; + x[3] = 0; + x += 4; + } + + //Long description + int currentLoopLength = x - (__u8*)short_evt; + int overheadPerDescriptor = 8; //increase if codepages are added!!! + int MAX_LEN = 255 - overheadPerDescriptor; + + int textLength = eventInfo.long_description.length(); + int lastDescriptorNumber = (textLength + MAX_LEN-1) / MAX_LEN - 1; + int remainingTextLength = textLength - lastDescriptorNumber * MAX_LEN; + //int descriptorLoopLength = lastDescriptorNumber * 255 + remainingTextLength + overheadPerDescriptor; + + //if long description is too long, just try to fill as many descriptors as possible + while ( (lastDescriptorNumber+1) * 255 + currentLoopLength > EIT_LENGTH) + { + lastDescriptorNumber--; + remainingTextLength = MAX_LEN; + } + + + for (int descrIndex = 0; descrIndex <= lastDescriptorNumber; ++descrIndex) + { + eit_extended_descriptor_struct *ext_evt = (eit_extended_descriptor_struct*) x; + ext_evt->descriptor_tag = EIT_EXTENDED_EVENT_DESCRIPOR; + //descriptor header length is 6, including the 2 tag and length bytes + //so the length field must be: stringlength + 1 (2 4-bits numbers) + 3 (lang code) + 2 bytes for item info length field and text length field + int currentTextLength = descrIndex < lastDescriptorNumber ? MAX_LEN : remainingTextLength; + ext_evt->descriptor_length = 6 + currentTextLength; + + ext_evt->descriptor_number = descrIndex; + ext_evt->last_descriptor_number = lastDescriptorNumber; + ext_evt->iso_639_2_language_code_1 = 'e'; + ext_evt->iso_639_2_language_code_2 = 'n'; + ext_evt->iso_639_2_language_code_3 = 'g'; + + x[6] = 0; //item information (car, year, director, etc. Unsupported for now) + x[7] = currentTextLength; //length of description string (part in this message) + memcpy(x + 8, &eventInfo.long_description[descrIndex*MAX_LEN], currentTextLength); + + x += 2 + ext_evt->descriptor_length; + currentLoopLength += 2 + ext_evt->descriptor_length; + } + + //TODO: add age and more + + int desc_loop_length = x - (data + EIT_LOOP_SIZE); + evt_struct->descriptors_loop_length_hi = HI(desc_loop_length); + evt_struct->descriptors_loop_length_lo = LO(desc_loop_length); + + evt = new eventData(evt_struct, desc_loop_length + 12, 0); + delete [] data; + return evt; +} + +void eEPGCache::addNewEvent(const UnpackedEventData& eventInfo) +{ + // This is essentially a copy op sectionRead. + // I left in sectionRead, because I don't have the time to profile the impact if sectionRead + // would use this function as well. + eServiceReferenceDVB service(eventInfo.service); + time_t start = eventInfo.start; + time_t duration = eventInfo.duration; + time_t now = ::time(0); + int source = 0xF1; + + std::pair &servicemap = eventDB[service]; + eventMap& events = servicemap.first; + timeMap& eventTimes = servicemap.second; + + eventMap::iterator prevEventIt = events.end(); + timeMap::iterator prevTimeIt = eventTimes.end(); + + //check if time is valid (only accept future start time, and max two weeks from now + if (start + duration < now || start > now + 14*24*60*60) + return; + + eventData *evt = 0; + int ev_erase_count = 0; + int tm_erase_count = 0; + __u16 event_id = generateEventId(eventInfo); + + // search in eventmap + eventMap::iterator ev_it = events.find(event_id); + + // entry with this event_id is already exist ? + if ( ev_it != events.end() ) + { + if ( source > ev_it->second->type ) // update needed ? + return; + + // search this event in timemap + timeMap::iterator tm_it_tmp = + eventTimes.find(ev_it->second->getStartTime()); + + if ( tm_it_tmp != eventTimes.end() ) + { + if ( tm_it_tmp->first == start ) // just update eventdata + { + // exempt memory + eventData *tmp = ev_it->second; + eventData *newData = createEventData(eventInfo); + ev_it->second = newData; + tm_it_tmp->second = newData; + + if (FixOverlapping(servicemap, start, duration, tm_it_tmp, service)) + { + prevEventIt = events.end(); + prevTimeIt = eventTimes.end(); + } + delete tmp; + return; + } + else // event has new event begin time + { + tm_erase_count++; + // delete the found record from timemap + eventTimes.erase(tm_it_tmp); + prevTimeIt=eventTimes.end(); + } + } + } + + // 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 = eventTimes.find(start); + + if ( tm_it != eventTimes.end() ) + { + // event with same start time but another event_id... + // if the existing event has a lower priority (higher source/type value), then replace it + if ( source > tm_it->second->type && ev_it == events.end() ) + return; + + // search this time in eventmap + eventMap::iterator ev_it_tmp = events.find(tm_it->second->getEventID()); + + if ( ev_it_tmp != events.end() ) + { + ev_erase_count++; + // delete the found record from eventmap + events.erase(ev_it_tmp); + prevEventIt=events.end(); + } + } + evt = createEventData(eventInfo); +#ifdef EPG_DEBUG + bool consistencyCheck=true; +#endif + if (ev_erase_count > 0 && tm_erase_count > 0) // 2 different pairs have been removed + { + // exempt memory + delete ev_it->second; + delete tm_it->second; + ev_it->second=evt; + tm_it->second=evt; + } + else if (ev_erase_count == 0 && tm_erase_count > 0) + { + // exempt memory + delete ev_it->second; + tm_it=prevTimeIt=eventTimes.insert( prevTimeIt, std::pair( start, evt ) ); + ev_it->second = evt; + } + else if (ev_erase_count > 0 && tm_erase_count == 0) + { + // exempt memory + delete tm_it->second; + ev_it = prevEventIt = events.insert( prevEventIt, std::pair( event_id, evt) ); + tm_it->second = evt; + } + else // added new eventData + { +#ifdef EPG_DEBUG + consistencyCheck=false; +#endif + ev_it=prevEventIt=events.insert( prevEventIt, std::pair( event_id, evt) ); + tm_it=prevTimeIt=eventTimes.insert( prevTimeIt, std::pair( start, evt ) ); + } + +#ifdef EPG_DEBUG + if ( consistencyCheck ) + { + if ( tm_it->second != evt || ev_it->second != evt ) + eFatal("tm_it->second != ev_it->second"); + else if ( tm_it->second->getStartTime() != tm_it->first ) + eFatal("event start_time(%d) non equal timemap key(%d)", + tm_it->second->getStartTime(), tm_it->first ); + else if ( tm_it->first != TM ) + eFatal("timemap key(%d) non equal TM(%d)", + tm_it->first, TM); + else if ( ev_it->second->getEventID() != ev_it->first ) + eFatal("event_id (%d) non equal event_map key(%d)", + ev_it->second->getEventID(), ev_it->first); + else if ( ev_it->first != event_id ) + eFatal("eventmap key(%d) non equal event_id(%d)", + ev_it->first, event_id ); + } +#endif + if (FixOverlapping(servicemap, start, duration, tm_it, service)) + { + prevEventIt = events.end(); + prevTimeIt = eventTimes.end(); + } +} + // here we get a python tuple // the first entry in the tuple is a python string to specify the format of the returned tuples (in a list) // I = Event Id @@ -2766,33 +3161,34 @@ 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) +//void eEPGCache::channel_data::storeTitle(std::map<__u32, mhw_title_t>::iterator itTitle, const std::string& sumText, const __u8 *data) +void eEPGCache::channel_data::storeTitle(__u32 eventId, const mhw_title_t& mhwTitle, const 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() - bool isMHW2 = itTitle->second.mhw2_mjd_hi || itTitle->second.mhw2_mjd_lo || - itTitle->second.mhw2_duration_hi || itTitle->second.mhw2_duration_lo; + bool isMHW2 = mhwTitle.mhw2_mjd_hi || mhwTitle.mhw2_mjd_lo || + mhwTitle.mhw2_duration_hi || mhwTitle.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->service_id_hi = m_channels[ mhwTitle.channel_id - 1 ].channel_id_hi; + packet->service_id_lo = m_channels[ mhwTitle.channel_id - 1 ].channel_id_lo; packet->version_number = 0; // eEPGCache::sectionRead() will dig this for the moment packet->current_next_indicator = 0; packet->section_number = 0; // eEPGCache::sectionRead() will dig this for the moment packet->last_section_number = 0; // eEPGCache::sectionRead() will dig this for the moment - packet->transport_stream_id_hi = m_channels[ itTitle->second.channel_id - 1 ].transport_stream_id_hi; - packet->transport_stream_id_lo = m_channels[ itTitle->second.channel_id - 1 ].transport_stream_id_lo; - packet->original_network_id_hi = m_channels[ itTitle->second.channel_id - 1 ].network_id_hi; - packet->original_network_id_lo = m_channels[ itTitle->second.channel_id - 1 ].network_id_lo; + packet->transport_stream_id_hi = m_channels[ mhwTitle.channel_id - 1 ].transport_stream_id_hi; + packet->transport_stream_id_lo = m_channels[ mhwTitle.channel_id - 1 ].transport_stream_id_lo; + packet->original_network_id_hi = m_channels[ mhwTitle.channel_id - 1 ].network_id_hi; + packet->original_network_id_lo = m_channels[ mhwTitle.channel_id - 1 ].network_id_lo; packet->segment_last_section_number = 0; // eEPGCache::sectionRead() will dig this for the moment packet->segment_last_table_id = 0x50; - __u8 *title = isMHW2 ? ((__u8*)(itTitle->second.title))-4 : (__u8*)itTitle->second.title; + __u8 *title = isMHW2 ? ((__u8*)(mhwTitle.title))-4 : (__u8*)mhwTitle.title; std::string prog_title = (char *) delimitName( title, name, isMHW2 ? 33 : 23 ); int prog_title_length = prog_title.length(); @@ -2800,24 +3196,24 @@ prog_title_length + 1; eit_event_t *event_data = (eit_event_t *) (data + EIT_SIZE); - event_data->event_id_hi = (( itTitle->first ) >> 8 ) & 0xFF; - event_data->event_id_lo = ( itTitle->first ) & 0xFF; + event_data->event_id_hi = (eventId >> 8 ) & 0xFF; + event_data->event_id_lo = eventId & 0xFF; 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 ); + data[2] = mhwTitle.mhw2_mjd_hi; + data[3] = mhwTitle.mhw2_mjd_lo; + data[4] = mhwTitle.mhw2_hours; + data[5] = mhwTitle.mhw2_minutes; + data[6] = mhwTitle.mhw2_seconds; + timeMHW2DVB( HILO(mhwTitle.mhw2_duration), data+7 ); } else { - timeMHW2DVB( itTitle->second.dh.day, itTitle->second.dh.hours, itTitle->second.ms.minutes, + timeMHW2DVB( mhwTitle.dh.day, mhwTitle.dh.hours, mhwTitle.ms.minutes, (u_char *) event_data + 2 ); - timeMHW2DVB( HILO(itTitle->second.duration), (u_char *) event_data+7 ); + timeMHW2DVB( HILO(mhwTitle.duration), (u_char *) event_data+7 ); } event_data->running_status = 0; @@ -2890,7 +3286,7 @@ descr_ll += 4; int content_id = 0; - std::string content_descr = (char *) delimitName( m_themes[itTitle->second.theme_id].name, name, 15 ); + std::string content_descr = (char *) delimitName( m_themes[mhwTitle.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 ) @@ -3079,14 +3475,14 @@ unsigned int pos=0; while((pos = the_text.find("\r\n")) != std::string::npos) - the_text.replace(pos, 2, " "); + the_text.replace(pos, 2, " "); //FIXME: insert DVB linebreak // 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() ) { startTimeout(4000); - storeTitle( itTitle, the_text, data ); + storeTitle( itTitle->first, itTitle->second, the_text, data ); m_titles.erase( itTitle ); } m_program_ids.erase( itProgid ); @@ -3100,7 +3491,7 @@ // 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 ); + storeTitle( itTitle->first, itTitle->second, "", data ); isRunning &= ~MHW; m_MHWConn=0; if ( m_MHWReader ) @@ -3366,7 +3762,7 @@ std::map<__u32, mhw_title_t>::iterator itTitle( m_titles.find( itProgId->second ) ); if ( itTitle != m_titles.end() ) { - storeTitle( itTitle, the_text, data ); + storeTitle( itTitle->first, itTitle->second, the_text, data ); m_titles.erase( itTitle ); } m_program_ids.erase( itProgId++ ); @@ -3398,9 +3794,9 @@ // 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 ); + storeTitle( itTitle->first, itTitle->second, "", data ); eDebug("[EPGC] mhw2 finished(%ld) %d summaries not found", ::time(0), m_program_ids.size()); } } diff -ru enigma2.orig/lib/dvb/epgcache.h enigma2/lib/dvb/epgcache.h --- enigma2.orig/lib/dvb/epgcache.h 2008-06-01 17:00:43.000000000 +0200 +++ enigma2/lib/dvb/epgcache.h 2008-06-01 08:13:51.000000000 +0200 @@ -146,6 +146,21 @@ }; #endif + + +struct UnpackedEventData +{ + eServiceReferenceDVB service; + time_t start; + time_t duration; + std::string title; + std::string short_summary; + std::string long_description; + char type; +// char language_code[3]; +// char codepage; +}; + class eEPGCache: public eMainloop, private eThread, public Object { #ifndef SWIG @@ -195,7 +210,7 @@ void timeMHW2DVB( u_char hours, u_char minutes, u_char *return_time); void timeMHW2DVB( int minutes, u_char *return_time); void timeMHW2DVB( u_char day, u_char hours, u_char minutes, u_char *return_time); - void storeTitle(std::map<__u32, mhw_title_t>::iterator itTitle, std::string sumText, const __u8 *data); + void storeTitle(__u32 eventId, const mhw_title_t& mhwTitle, const std::string& sumText, const __u8 *data); #endif void readData(const __u8 *data); void startChannel(); @@ -274,6 +289,8 @@ void flushEPG(const uniqueEPGKey & s=uniqueEPGKey()); void cleanLoop(); + void addNewEvent(const UnpackedEventData& eventData); + // called from main thread void timeUpdated(); void DVBChannelAdded(eDVBChannel*); @@ -342,6 +362,8 @@ SWIG_VOID(RESULT) lookupEventId(const eServiceReference &service, int event_id, ePtr &SWIG_OUTPUT); SWIG_VOID(RESULT) lookupEventTime(const eServiceReference &service, time_t, ePtr &SWIG_OUTPUT, int direction=0); SWIG_VOID(RESULT) getNextTimeEntry(ePtr &SWIG_OUTPUT); + void importEvent(SWIG_PYOBJECT(ePyObject) serviceReference, SWIG_PYOBJECT(ePyObject) list); + }; #ifndef SWIG