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