performance fix
[enigma2.git] / lib / dvb / epgcache.cpp
1 #include <lib/dvb/epgcache.h>
2 #include <lib/dvb/dvb.h>
3
4 #undef EPG_DEBUG  
5
6 #include <time.h>
7 #include <unistd.h>  // for usleep
8 #include <sys/vfs.h> // for statfs
9 // #include <libmd5sum.h>
10 #include <lib/base/eerror.h>
11
12 int eventData::CacheSize=0;
13
14 eEPGCache* eEPGCache::instance;
15 pthread_mutex_t eEPGCache::cache_lock=
16         PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
17 pthread_mutex_t eEPGCache::channel_map_lock=
18         PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
19
20 DEFINE_REF(eEPGCache)
21
22 eEPGCache::eEPGCache()
23         :messages(this,1), cleanTimer(this)//, paused(0)
24 {
25         eDebug("[EPGC] Initialized EPGCache");
26
27         CONNECT(messages.recv_msg, eEPGCache::gotMessage);
28         CONNECT(eDVBLocalTimeHandler::getInstance()->m_timeUpdated, eEPGCache::timeUpdated);
29         CONNECT(cleanTimer.timeout, eEPGCache::cleanLoop);
30
31         ePtr<eDVBResourceManager> res_mgr;
32         eDVBResourceManager::getInstance(res_mgr);
33         if (!res_mgr)
34                 eDebug("[eEPGCache] no resource manager !!!!!!!");
35         else
36                 res_mgr->connectChannelAdded(slot(*this,&eEPGCache::DVBChannelAdded), m_chanAddedConn);
37         instance=this;
38 }
39
40 void eEPGCache::timeUpdated()
41 {
42         if ( !thread_running() )
43         {
44                 eDebug("[EPGC] time updated.. start EPG Mainloop");
45                 run();
46         }
47         else
48                 messages.send(Message(Message::timeChanged));
49 }
50
51 void eEPGCache::DVBChannelAdded(eDVBChannel *chan)
52 {
53         if ( chan )
54         {
55                 eDebug("[eEPGCache] add channel %p", chan);
56                 channel_data *data = new channel_data(this);
57                 data->channel = chan;
58                 singleLock s(channel_map_lock);
59                 m_knownChannels.insert( std::pair<iDVBChannel*, channel_data* >(chan, data) );
60                 chan->connectStateChange(slot(*this, &eEPGCache::DVBChannelStateChanged), data->m_stateChangedConn);
61         }
62 }
63
64 void eEPGCache::DVBChannelRunning(iDVBChannel *chan)
65 {
66         singleLock s(channel_map_lock);
67         channelMapIterator it =
68                 m_knownChannels.find(chan);
69         if ( it == m_knownChannels.end() )
70                 eDebug("[eEPGCache] will start non existing channel %p !!!", chan);
71         else
72         {
73                 channel_data &data = *it->second;
74                 ePtr<eDVBResourceManager> res_mgr;
75                 if ( eDVBResourceManager::getInstance( res_mgr ) )
76                         eDebug("[eEPGCache] no res manager!!");
77                 else
78                 {
79                         ePtr<iDVBDemux> demux;
80                         if ( data.channel->getDemux(demux) )
81                         {
82                                 eDebug("[eEPGCache] no demux!!");
83                                 return;
84                         }
85                         else
86                         {
87                                 RESULT res = demux->createSectionReader( this, data.m_NowNextReader );
88                                 if ( res )
89                                 {
90                                         eDebug("[eEPGCache] couldnt initialize nownext reader!!");
91                                         return;
92                                 }
93                                 data.m_NowNextReader->connectRead(slot(data, &eEPGCache::channel_data::readData), data.m_NowNextConn);
94                                 res = demux->createSectionReader( this, data.m_ScheduleReader );
95                                 if ( res )
96                                 {
97                                         eDebug("[eEPGCache] couldnt initialize schedule reader!!");
98                                         return;
99                                 }
100                                 data.m_ScheduleReader->connectRead(slot(data, &eEPGCache::channel_data::readData), data.m_ScheduleConn);
101                                 res = demux->createSectionReader( this, data.m_ScheduleOtherReader );
102                                 if ( res )
103                                 {
104                                         eDebug("[eEPGCache] couldnt initialize schedule other reader!!");
105                                         return;
106                                 }
107                                 data.m_ScheduleOtherReader->connectRead(slot(data, &eEPGCache::channel_data::readData), data.m_ScheduleOtherConn);
108                                 messages.send(Message(Message::startChannel, chan));
109                                 // -> gotMessage -> changedService
110                         }
111                 }
112         }
113 }
114
115 void eEPGCache::DVBChannelStateChanged(iDVBChannel *chan)
116 {
117         channelMapIterator it =
118                 m_knownChannels.find(chan);
119         if ( it != m_knownChannels.end() )
120         {
121                 int state=0;
122                 chan->getState(state);
123                 switch (state)
124                 {
125                         case iDVBChannel::state_idle:
126                                 break;
127                         case iDVBChannel::state_tuning:
128                                 break;
129                         case iDVBChannel::state_unavailable:
130                                 break;
131                         case iDVBChannel::state_ok:
132                         {
133                                 eDebug("[eEPGCache] channel %p running", chan);
134                                 DVBChannelRunning(chan);
135                                 break;
136                         }
137                         case iDVBChannel::state_release:
138                         {
139                                 eDebug("[eEPGCache] remove channel %p", chan);
140                                 messages.send(Message(Message::leaveChannel, chan));
141                                 while(!it->second->can_delete)
142                                         usleep(1000);
143                                 delete it->second;
144                                 m_knownChannels.erase(it);
145                                 // -> gotMessage -> abortEPG
146                                 break;
147                         }
148                 }
149         }
150 }
151
152 void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel)
153 {
154         eit_t *eit = (eit_t*) data;
155
156         int len=HILO(eit->section_length)-1;//+3-4;
157         int ptr=EIT_SIZE;
158         if ( ptr >= len )
159                 return;
160
161         // This fixed the EPG on the Multichoice irdeto systems
162         // the EIT packet is non-compliant.. their EIT packet stinks
163         if ( data[ptr-1] < 0x40 )
164                 --ptr;
165
166         uniqueEPGKey service( HILO(eit->service_id), HILO(eit->original_network_id), HILO(eit->transport_stream_id) );
167         eit_event_struct* eit_event = (eit_event_struct*) (data+ptr);
168         int eit_event_size;
169         int duration;
170
171         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);
172         time_t now = time(0)+eDVBLocalTimeHandler::getInstance()->difference();
173
174         if ( TM != 3599 && TM > -1)
175                 channel->haveData |= source;
176
177         singleLock s(cache_lock);
178         // hier wird immer eine eventMap zurück gegeben.. entweder eine vorhandene..
179         // oder eine durch [] erzeugte
180         std::pair<eventMap,timeMap> &servicemap = eventDB[service];
181         eventMap::iterator prevEventIt = servicemap.first.end();
182         timeMap::iterator prevTimeIt = servicemap.second.end();
183
184         while (ptr<len)
185         {
186                 eit_event_size = HILO(eit_event->descriptors_loop_length)+EIT_LOOP_SIZE;
187
188                 duration = fromBCD(eit_event->duration_1)*3600+fromBCD(eit_event->duration_2)*60+fromBCD(eit_event->duration_3);
189                 TM = parseDVBtime(
190                         eit_event->start_time_1,
191                         eit_event->start_time_2,
192                         eit_event->start_time_3,
193                         eit_event->start_time_4,
194                         eit_event->start_time_5);
195
196                 if ( TM == 3599 )
197                         goto next;
198
199                 if ( TM != 3599 && (TM+duration < now || TM > now+14*24*60*60) )
200                         goto next;
201
202                 if ( now <= (TM+duration) || TM == 3599 /*NVOD Service*/ )  // old events should not be cached
203                 {
204                         __u16 event_id = HILO(eit_event->event_id);
205 //                      eDebug("event_id is %d sid is %04x", event_id, service.sid);
206
207                         eventData *evt = 0;
208                         int ev_erase_count = 0;
209                         int tm_erase_count = 0;
210
211                         // search in eventmap
212                         eventMap::iterator ev_it =
213                                 servicemap.first.find(event_id);
214
215                         // entry with this event_id is already exist ?
216                         if ( ev_it != servicemap.first.end() )
217                         {
218                                 if ( source > ev_it->second->type )  // update needed ?
219                                         goto next; // when not.. the skip this entry
220
221                                 // search this event in timemap
222                                 timeMap::iterator tm_it_tmp = 
223                                         servicemap.second.find(ev_it->second->getStartTime());
224
225                                 if ( tm_it_tmp != servicemap.second.end() )
226                                 {
227                                         if ( tm_it_tmp->first == TM ) // correct eventData
228                                         {
229                                                 // exempt memory
230                                                 delete ev_it->second;
231                                                 evt = new eventData(eit_event, eit_event_size, source);
232                                                 ev_it->second=evt;
233                                                 tm_it_tmp->second=evt;
234                                                 goto next;
235                                         }
236                                         else
237                                         {
238                                                 tm_erase_count++;
239                                                 // delete the found record from timemap
240                                                 servicemap.second.erase(tm_it_tmp);
241                                                 prevTimeIt=servicemap.second.end();
242                                         }
243                                 }
244                         }
245
246                         // search in timemap, for check of a case if new time has coincided with time of other event 
247                         // or event was is not found in eventmap
248                         timeMap::iterator tm_it =
249                                 servicemap.second.find(TM);
250
251                         if ( tm_it != servicemap.second.end() )
252                         {
253                                 // i think, if event is not found on eventmap, but found on timemap updating nevertheless demands
254 #if 0
255                                 if ( source > tm_it->second->type && tm_erase_count == 0 ) // update needed ?
256                                         goto next; // when not.. the skip this entry
257 #endif
258
259                                 // search this time in eventmap
260                                 eventMap::iterator ev_it_tmp = 
261                                         servicemap.first.find(tm_it->second->getEventID());
262
263                                 if ( ev_it_tmp != servicemap.first.end() )
264                                 {
265                                         ev_erase_count++;                               
266                                         // delete the found record from eventmap
267                                         servicemap.first.erase(ev_it_tmp);
268                                         prevEventIt=servicemap.first.end();
269                                 }
270                         }
271                         
272                         evt = new eventData(eit_event, eit_event_size, source);
273 #if EPG_DEBUG
274                         bool consistencyCheck=true;
275 #endif
276                         if (ev_erase_count > 0 && tm_erase_count > 0) // 2 different pairs have been removed
277                         {
278                                 // exempt memory
279                                 delete ev_it->second; 
280                                 delete tm_it->second;
281                                 ev_it->second=evt;
282                                 tm_it->second=evt;
283                         }
284                         else if (ev_erase_count == 0 && tm_erase_count > 0) 
285                         {
286                                 // exempt memory
287                                 delete ev_it->second;
288                                 tm_it=prevTimeIt=servicemap.second.insert( prevTimeIt, std::pair<const time_t, eventData*>( TM, evt ) );
289                                 ev_it->second=evt;
290                         }
291                         else if (ev_erase_count > 0 && tm_erase_count == 0)
292                         {
293                                 // exempt memory
294                                 delete tm_it->second;
295                                 ev_it=prevEventIt=servicemap.first.insert( prevEventIt, std::pair<const __u16, eventData*>( event_id, evt) );
296                                 tm_it->second=evt;
297                         }
298                         else // added new eventData
299                         {
300 #if EPG_DEBUG
301                                 consistencyCheck=false;
302 #endif
303                                 prevEventIt=servicemap.first.insert( prevEventIt, std::pair<const __u16, eventData*>( event_id, evt) );
304                                 prevTimeIt=servicemap.second.insert( prevTimeIt, std::pair<const time_t, eventData*>( TM, evt ) );
305                         }
306 #if EPG_DEBUG
307                         if ( consistencyCheck )
308                         {
309                                 if ( tm_it->second != evt || ev_it->second != evt )
310                                         eFatal("tm_it->second != ev_it->second");
311                                 else if ( tm_it->second->getStartTime() != tm_it->first )
312                                         eFatal("event start_time(%d) non equal timemap key(%d)", 
313                                                 tm_it->second->getStartTime(), tm_it->first );
314                                 else if ( tm_it->first != TM )
315                                         eFatal("timemap key(%d) non equal TM(%d)", 
316                                                 tm_it->first, TM);
317                                 else if ( ev_it->second->getEventID() != ev_it->first )
318                                         eFatal("event_id (%d) non equal event_map key(%d)",
319                                                 ev_it->second->getEventID(), ev_it->first);
320                                 else if ( ev_it->first != event_id )
321                                         eFatal("eventmap key(%d) non equal event_id(%d)", 
322                                                 ev_it->first, event_id );
323                         }
324 #endif
325                 }
326 next:
327 #if EPG_DEBUG
328                 if ( servicemap.first.size() != servicemap.second.size() )
329                 {
330                         FILE *f = fopen("/hdd/event_map.txt", "w+");
331                         int i=0;
332                         for (eventMap::iterator it(servicemap.first.begin())
333                                 ; it != servicemap.first.end(); ++it )
334                                 fprintf(f, "%d(key %d) -> time %d, event_id %d, data %p\n", 
335                                         i++, (int)it->first, (int)it->second->getStartTime(), (int)it->second->getEventID(), it->second );
336                         fclose(f);
337                         f = fopen("/hdd/time_map.txt", "w+");
338                         i=0;
339                         for (timeMap::iterator it(servicemap.second.begin())
340                                 ; it != servicemap.second.end(); ++it )
341                                         fprintf(f, "%d(key %d) -> time %d, event_id %d, data %p\n", 
342                                                 i++, (int)it->first, (int)it->second->getStartTime(), (int)it->second->getEventID(), it->second );
343                         fclose(f);
344
345                         eFatal("(1)map sizes not equal :( sid %04x tsid %04x onid %04x size %d size2 %d", 
346                                 service.sid, service.tsid, service.onid, 
347                                 servicemap.first.size(), servicemap.second.size() );
348                 }
349 #endif
350                 ptr += eit_event_size;
351                 eit_event=(eit_event_struct*)(((__u8*)eit_event)+eit_event_size);
352         }
353 }
354
355 void eEPGCache::flushEPG(const uniqueEPGKey & s)
356 {
357         eDebug("[EPGC] flushEPG %d", (int)(bool)s);
358         singleLock l(cache_lock);
359         if (s)  // clear only this service
360         {
361                 eventCache::iterator it = eventDB.find(s);
362                 if ( it != eventDB.end() )
363                 {
364                         eventMap &evMap = it->second.first;
365                         timeMap &tmMap = it->second.second;
366                         tmMap.clear();
367                         for (eventMap::iterator i = evMap.begin(); i != evMap.end(); ++i)
368                                 delete i->second;
369                         evMap.clear();
370                         eventDB.erase(it);
371
372                         // TODO .. search corresponding channel for removed service and remove this channel from lastupdated map
373                 }
374         }
375         else // clear complete EPG Cache
376         {
377                 for (eventCache::iterator it(eventDB.begin());
378                         it != eventDB.end(); ++it)
379                 {
380                         eventMap &evMap = it->second.first;
381                         timeMap &tmMap = it->second.second;
382                         for (eventMap::iterator i = evMap.begin(); i != evMap.end(); ++i)
383                                 delete i->second;
384                         evMap.clear();
385                         tmMap.clear();
386                 }
387                 eventDB.clear();
388                 channelLastUpdated.clear();
389                 singleLock m(channel_map_lock);
390                 for (channelMapIterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it)
391                         it->second->startEPG();
392         }
393         eDebug("[EPGC] %i bytes for cache used", eventData::CacheSize);
394 }
395
396 void eEPGCache::cleanLoop()
397 {
398         singleLock s(cache_lock);
399         if (!eventDB.empty())
400         {
401                 eDebug("[EPGC] start cleanloop");
402                 const eit_event_struct* cur_event;
403                 int duration;
404
405 // FIXME !!! TIME_CORRECTION
406                 time_t now = time(0)+eDVBLocalTimeHandler::getInstance()->difference();
407
408                 for (eventCache::iterator DBIt = eventDB.begin(); DBIt != eventDB.end(); DBIt++)
409                 {
410                         for (timeMap::iterator It = DBIt->second.second.begin(); It != DBIt->second.second.end() && It->first < now;)
411                         {
412                                 cur_event = (*It->second).get();
413                                 duration = fromBCD( cur_event->duration_1)*3600 + fromBCD(cur_event->duration_2)*60 + fromBCD(cur_event->duration_3);
414
415                                 if ( now > (It->first+duration) )  // outdated normal entry (nvod references to)
416                                 {
417                                         // remove entry from eventMap
418                                         eventMap::iterator b(DBIt->second.first.find(It->second->getEventID()));
419                                         if ( b != DBIt->second.first.end() )
420                                         {
421                                                 // release Heap Memory for this entry   (new ....)
422 //                                              eDebug("[EPGC] delete old event (evmap)");
423                                                 DBIt->second.first.erase(b);
424                                         }
425
426                                         // remove entry from timeMap
427 //                                      eDebug("[EPGC] release heap mem");
428                                         delete It->second;
429                                         DBIt->second.second.erase(It++);
430 //                                      eDebug("[EPGC] delete old event (timeMap)");
431                                 }
432                                 else
433                                         ++It;
434                         }
435                 }
436                 eDebug("[EPGC] stop cleanloop");
437                 eDebug("[EPGC] %i bytes for cache used", eventData::CacheSize);
438         }
439         cleanTimer.start(CLEAN_INTERVAL,true);
440 }
441
442 eEPGCache::~eEPGCache()
443 {
444         messages.send(Message::quit);
445         kill(); // waiting for thread shutdown
446         singleLock s(cache_lock);
447         for (eventCache::iterator evIt = eventDB.begin(); evIt != eventDB.end(); evIt++)
448                 for (eventMap::iterator It = evIt->second.first.begin(); It != evIt->second.first.end(); It++)
449                         delete It->second;
450 }
451
452 Event *eEPGCache::lookupEvent(const eServiceReferenceDVB &service, int event_id, bool plain)
453 {
454         singleLock s(cache_lock);
455         uniqueEPGKey key( service );
456
457         eventCache::iterator It = eventDB.find( key );
458         if ( It != eventDB.end() && !It->second.first.empty() ) // entrys cached?
459         {
460                 eventMap::iterator i( It->second.first.find( event_id ));
461                 if ( i != It->second.first.end() )
462                 {
463                         if ( service.getServiceType() == 4 ) // nvod ref
464                                 return lookupEvent( service, i->second->getStartTime(), plain );
465                         else if ( plain )
466                 // get plain data... not in Event Format !!!
467                 // before use .. cast it to eit_event_struct*
468                                 return (Event*) i->second->get();
469                         else
470                                 return new Event( (uint8_t*)i->second->get() /*, (It->first.tsid<<16)|It->first.onid*/ );
471                 }
472                 else
473                         eDebug("event %04x not found in epgcache", event_id);
474         }
475         return 0;
476 }
477
478 Event *eEPGCache::lookupEvent(const eServiceReferenceDVB &service, time_t t, bool plain )
479 // if t == 0 we search the current event...
480 {
481         singleLock s(cache_lock);
482         uniqueEPGKey key(service);
483
484         // check if EPG for this service is ready...
485         eventCache::iterator It = eventDB.find( key );
486         if ( It != eventDB.end() && !It->second.first.empty() ) // entrys cached ?
487         {
488                 if (!t)
489                         t = time(0)+eDVBLocalTimeHandler::getInstance()->difference();
490
491                 timeMap::iterator i = It->second.second.lower_bound(t);
492                 if ( i != It->second.second.end() )
493                 {
494                         i--;
495                         if ( i != It->second.second.end() )
496                         {
497                                 const eit_event_struct* eit_event = i->second->get();
498                                 int duration = fromBCD(eit_event->duration_1)*3600+fromBCD(eit_event->duration_2)*60+fromBCD(eit_event->duration_3);
499                                 if ( t <= i->first+duration )
500                                 {
501                                         if ( plain )
502                                                 // get plain data... not in Event Format !!!
503                                                 // before use .. cast it to eit_event_struct*
504                                                 return (Event*) i->second->get();
505                                         return new Event( (uint8_t*)i->second->get() /*, (It->first.tsid<<16)|It->first.onid */ );
506                                 }
507                         }
508                 }
509
510                 for ( eventMap::iterator i( It->second.first.begin() ); i != It->second.first.end(); i++)
511                 {
512                         const eit_event_struct* eit_event = i->second->get();
513                         int duration = fromBCD(eit_event->duration_1)*3600+fromBCD(eit_event->duration_2)*60+fromBCD(eit_event->duration_3);
514                         time_t begTime = parseDVBtime( eit_event->start_time_1, eit_event->start_time_2,        eit_event->start_time_3, eit_event->start_time_4,       eit_event->start_time_5);
515                         if ( t >= begTime && t <= begTime+duration) // then we have found
516                         {
517                                 if ( plain )
518                                         // get plain data... not in Event Format !!!
519                                         // before use .. cast it to eit_event_struct*
520                                         return (Event*) i->second->get();
521                                 return new Event( (uint8_t*)i->second->get()/*, (It->first.tsid<<16)|It->first.onid*/ );
522                         }
523                 }
524         }
525         return 0;
526 }
527
528 void eEPGCache::gotMessage( const Message &msg )
529 {
530         switch (msg.type)
531         {
532                 case Message::flush:
533                         flushEPG(msg.service);
534                         break;
535                 case Message::startChannel:
536                 {
537                         singleLock s(channel_map_lock);
538                         channelMapIterator channel =
539                                 m_knownChannels.find(msg.channel);
540                         if ( channel != m_knownChannels.end() )
541                                 channel->second->startChannel();
542                         break;
543                 }
544                 case Message::leaveChannel:
545                 {
546                         singleLock s(channel_map_lock);
547                         channelMapIterator channel =
548                                 m_knownChannels.find(msg.channel);
549                         if ( channel != m_knownChannels.end() )
550                                 channel->second->abortEPG();
551                         break;
552                 }
553                 case Message::quit:
554                         quit(0);
555                         break;
556                 case Message::timeChanged:
557                         cleanLoop();
558                         break;
559                 default:
560                         eDebug("unhandled EPGCache Message!!");
561                         break;
562         }
563 }
564
565 void eEPGCache::thread()
566 {
567         nice(4);
568         load();
569         cleanLoop();
570         exec();
571         save();
572 }
573
574 void eEPGCache::load()
575 {
576 #if 0
577         FILE *f = fopen("/hdd/epg.dat", "r");
578         if (f)
579         {
580                 unsigned char md5_saved[16];
581                 unsigned char md5[16];
582                 int size=0;
583                 int cnt=0;
584                 bool md5ok=false;
585                 if (!md5_file("/hdd/epg.dat", 1, md5))
586                 {
587                         FILE *f = fopen("/hdd/epg.dat.md5", "r");
588                         if (f)
589                         {
590                                 fread( md5_saved, 16, 1, f);
591                                 fclose(f);
592                                 if ( !memcmp(md5_saved, md5, 16) )
593                                         md5ok=true;
594                         }
595                 }
596                 if ( md5ok )
597                 {
598                         fread( &size, sizeof(int), 1, f);
599                         while(size--)
600                         {
601                                 uniqueEPGKey key;
602                                 eventMap evMap;
603                                 timeMap tmMap;
604                                 int size=0;
605                                 fread( &key, sizeof(uniqueEPGKey), 1, f);
606                                 fread( &size, sizeof(int), 1, f);
607                                 while(size--)
608                                 {
609                                         int len=0;
610                                         int type=0;
611                                         eventData *event=0;
612                                         fread( &type, sizeof(int), 1, f);
613                                         fread( &len, sizeof(int), 1, f);
614                                         event = new eventData(0, len, type);
615                                         fread( event->EITdata, len, 1, f);
616                                         evMap[ event->getEventID() ]=event;
617                                         tmMap[ event->getStartTime() ]=event;
618                                         ++cnt;
619                                 }
620                                 eventDB[key]=std::pair<eventMap,timeMap>(evMap,tmMap);
621                         }
622                         eDebug("%d events read from /hdd/epg.dat.md5", cnt);
623                 }
624                 fclose(f);
625         }
626 #endif
627 }
628
629 void eEPGCache::save()
630 {
631 #if 0
632         struct statfs s;
633         off64_t tmp;
634         if (statfs("/hdd", &s)<0)
635                 tmp=0;
636         else
637         {
638                 tmp=s.f_blocks;
639                 tmp*=s.f_bsize;
640         }
641
642         // prevent writes to builtin flash
643         if ( tmp < 1024*1024*50 ) // storage size < 50MB
644                 return;
645
646         // check for enough free space on storage
647         tmp=s.f_bfree;
648         tmp*=s.f_bsize;
649         if ( tmp < (eventData::CacheSize*12)/10 ) // 20% overhead
650                 return;
651
652         FILE *f = fopen("/hdd/epg.dat", "w");
653         int cnt=0;
654         if ( f )
655         {
656                 int size = eventDB.size();
657                 fwrite( &size, sizeof(int), 1, f );
658                 for (eventCache::iterator service_it(eventDB.begin()); service_it != eventDB.end(); ++service_it)
659                 {
660                         timeMap &timemap = service_it->second.second;
661                         fwrite( &service_it->first, sizeof(uniqueEPGKey), 1, f);
662                         size = timemap.size();
663                         fwrite( &size, sizeof(int), 1, f);
664                         for (timeMap::iterator time_it(timemap.begin()); time_it != timemap.end(); ++time_it)
665                         {
666                                 int len = time_it->second->ByteSize;
667                                 fwrite( &time_it->second->type, sizeof(int), 1, f );
668                                 fwrite( &len, sizeof(int), 1, f);
669                                 fwrite( time_it->second->EITdata, len, 1, f);
670                                 ++cnt;
671                         }
672                 }
673                 eDebug("%d events written to /hdd/epg.dat", cnt);
674                 fclose(f);
675                 unsigned char md5[16];
676                 if (!md5_file("/hdd/epg.dat", 1, md5))
677                 {
678                         FILE *f = fopen("/hdd/epg.dat.md5", "w");
679                         if (f)
680                         {
681                                 fwrite( md5, 16, 1, f);
682                                 fclose(f);
683                         }
684                 }
685         }
686 #endif
687 }
688
689 RESULT eEPGCache::getInstance(ePtr<eEPGCache> &ptr)
690 {
691         ptr = instance;
692         if (!ptr)
693                 return -1;
694         return 0;
695 }
696
697 eEPGCache::channel_data::channel_data(eEPGCache *ml)
698         :cache(ml)
699         ,abortTimer(ml), zapTimer(ml)
700         ,state(0), isRunning(0), haveData(0), can_delete(1)
701 {
702         CONNECT(zapTimer.timeout, eEPGCache::channel_data::startEPG);
703         CONNECT(abortTimer.timeout, eEPGCache::channel_data::abortNonAvail);
704 }
705
706 bool eEPGCache::channel_data::finishEPG()
707 {
708         if (!isRunning)  // epg ready
709         {
710                 eDebug("[EPGC] stop caching events(%d)", time(0)+eDVBLocalTimeHandler::getInstance()->difference());
711                 zapTimer.start(UPDATE_INTERVAL, 1);
712                 eDebug("[EPGC] next update in %i min", UPDATE_INTERVAL / 60000);
713                 for (int i=0; i < 3; ++i)
714                 {
715                         seenSections[i].clear();
716                         calcedSections[i].clear();
717                 }
718                 singleLock l(cache->cache_lock);
719                 cache->channelLastUpdated[channel->getChannelID()] = time(0)+eDVBLocalTimeHandler::getInstance()->difference();
720                 can_delete=1;
721                 return true;
722         }
723         return false;
724 }
725
726 void eEPGCache::channel_data::startEPG()
727 {
728         eDebug("[EPGC] start caching events(%d)", eDVBLocalTimeHandler::getInstance()->difference()+time(0));
729         state=0;
730         haveData=0;
731         can_delete=0;
732         for (int i=0; i < 3; ++i)
733         {
734                 seenSections[i].clear();
735                 calcedSections[i].clear();
736         }
737
738         eDVBSectionFilterMask mask;
739         memset(&mask, 0, sizeof(mask));
740         mask.pid = 0x12;
741         mask.flags = eDVBSectionFilterMask::rfCRC;
742
743         mask.data[0] = 0x4E;
744         mask.mask[0] = 0xFE;
745         m_NowNextReader->start(mask);
746         isRunning |= NOWNEXT;
747
748         mask.data[0] = 0x50;
749         mask.mask[0] = 0xF0;
750         m_ScheduleReader->start(mask);
751         isRunning |= SCHEDULE;
752
753         mask.data[0] = 0x60;
754         mask.mask[0] = 0xF0;
755         m_ScheduleOtherReader->start(mask);
756         isRunning |= SCHEDULE_OTHER;
757
758         abortTimer.start(7000,true);
759 }
760
761 void eEPGCache::channel_data::abortNonAvail()
762 {
763         if (!state)
764         {
765                 if ( !(haveData&eEPGCache::NOWNEXT) && (isRunning&eEPGCache::NOWNEXT) )
766                 {
767                         eDebug("[EPGC] abort non avail nownext reading");
768                         isRunning &= ~eEPGCache::NOWNEXT;
769                         if ( m_NowNextReader )
770                                 m_NowNextReader->stop();
771                 }
772                 if ( !(haveData&eEPGCache::SCHEDULE) && (isRunning&eEPGCache::SCHEDULE) )
773                 {
774                         eDebug("[EPGC] abort non avail schedule reading");
775                         isRunning &= ~SCHEDULE;
776                         m_ScheduleReader->stop();
777                 }
778                 if ( !(haveData&eEPGCache::SCHEDULE_OTHER) && (isRunning&eEPGCache::SCHEDULE_OTHER) )
779                 {
780                         eDebug("[EPGC] abort non avail schedule_other reading");
781                         isRunning &= ~SCHEDULE_OTHER;
782                         m_ScheduleOtherReader->stop();
783                 }
784                 if ( isRunning )
785                         abortTimer.start(50000, true);
786                 else
787                 {
788                         ++state;
789                         for (int i=0; i < 3; ++i)
790                         {
791                                 seenSections[i].clear();
792                                 calcedSections[i].clear();
793                         }
794                         can_delete=1;
795                 }
796         }
797         ++state;
798 }
799
800 void eEPGCache::channel_data::startChannel()
801 {
802         updateMap::iterator It = cache->channelLastUpdated.find( channel->getChannelID() );
803
804         int update = ( It != cache->channelLastUpdated.end() ? ( UPDATE_INTERVAL - ( (time(0)+eDVBLocalTimeHandler::getInstance()->difference()-It->second) * 1000 ) ) : ZAP_DELAY );
805
806         if (update < ZAP_DELAY)
807                 update = ZAP_DELAY;
808
809         zapTimer.start(update, 1);
810         if (update >= 60000)
811                 eDebug("[EPGC] next update in %i min", update/60000);
812         else if (update >= 1000)
813                 eDebug("[EPGC] next update in %i sec", update/1000);
814 }
815
816 void eEPGCache::channel_data::abortEPG()
817 {
818         for (int i=0; i < 3; ++i)
819         {
820                 seenSections[i].clear();
821                 calcedSections[i].clear();
822         }
823         abortTimer.stop();
824         zapTimer.stop();
825         if (isRunning)
826         {
827                 eDebug("[EPGC] abort caching events !!");
828                 if (isRunning & eEPGCache::SCHEDULE)
829                 {
830                         isRunning &= eEPGCache::SCHEDULE;
831                         if ( m_ScheduleReader )
832                                 m_ScheduleReader->stop();
833                 }
834                 if (isRunning & eEPGCache::NOWNEXT)
835                 {
836                         isRunning &= ~eEPGCache::NOWNEXT;
837                         if ( m_NowNextReader )
838                                 m_NowNextReader->stop();
839                 }
840                 if (isRunning & SCHEDULE_OTHER)
841                 {
842                         isRunning &= ~eEPGCache::SCHEDULE_OTHER;
843                         if ( m_ScheduleOtherReader )
844                                 m_ScheduleOtherReader->stop();
845                 }
846                 can_delete=1;
847         }
848 }
849
850 void eEPGCache::channel_data::readData( const __u8 *data)
851 {
852         if (!data)
853                 eDebug("get Null pointer from section reader !!");
854         else
855         {
856                 int source;
857                 int map;
858                 iDVBSectionReader *reader=NULL;
859                 switch(data[0])
860                 {
861                         case 0x4E ... 0x4F:
862                                 reader=m_NowNextReader;
863                                 source=eEPGCache::NOWNEXT;
864                                 map=0;
865                                 break;
866                         case 0x50 ... 0x5F:
867                                 reader=m_ScheduleReader;
868                                 source=eEPGCache::SCHEDULE;
869                                 map=1;
870                                 break;
871                         case 0x60 ... 0x6F:
872                                 reader=m_ScheduleOtherReader;
873                                 source=eEPGCache::SCHEDULE_OTHER;
874                                 map=2;
875                                 break;
876                 }
877                 tidMap &seenSections = this->seenSections[map];
878                 tidMap &calcedSections = this->calcedSections[map];
879                 if ( state == 1 && calcedSections == seenSections || state > 1 )
880                 {
881                         eDebugNoNewLine("[EPGC] ");
882                         switch (source)
883                         {
884                                 case eEPGCache::NOWNEXT: eDebugNoNewLine("nownext");break;
885                                 case eEPGCache::SCHEDULE: eDebugNoNewLine("schedule");break;
886                                 case eEPGCache::SCHEDULE_OTHER: eDebugNoNewLine("schedule other");break;
887                                 default: eDebugNoNewLine("unknown");break;
888                         }
889                         eDebug(" finished(%d)", time(0)+eDVBLocalTimeHandler::getInstance()->difference());
890                         reader->stop();
891                         isRunning &= ~source;
892                         if (!isRunning)
893                                 finishEPG();
894                 }
895                 else
896                 {
897                         eit_t *eit = (eit_t*) data;
898                         __u32 sectionNo = data[0] << 24;
899                         sectionNo |= data[3] << 16;
900                         sectionNo |= data[4] << 8;
901                         sectionNo |= eit->section_number;
902
903                         tidMap::iterator it =
904                                 seenSections.find(sectionNo);
905
906                         if ( it == seenSections.end() )
907                         {
908                                 seenSections.insert(sectionNo);
909                                 __u32 tmpval = sectionNo & 0xFFFFFF00;
910                                 __u8 incr = source == NOWNEXT ? 1 : 8;
911                                 for ( int i = 0; i <= eit->last_section_number; i+=incr )
912                                 {
913                                         if ( i == eit->section_number )
914                                         {
915                                                 for (int x=i; x <= eit->segment_last_section_number; ++x)
916                                                         calcedSections.insert(tmpval|(x&0xFF));
917                                         }
918                                         else
919                                                 calcedSections.insert(tmpval|(i&0xFF));
920                                 }
921                                 cache->sectionRead(data, source, this);
922                         }
923                 }
924         }
925 }