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