use PyObject_Callback instead of PyEval_Callback (memleak fix?)
[enigma2.git] / lib / dvb / epgcache.cpp
index 3b40efcb41bc57e5c55cce748c2c401e33ac71e6..60eee05a2369627b272a6e2d13900f8f264405f1 100644 (file)
@@ -8,6 +8,7 @@
 #include <sys/vfs.h> // for statfs
 // #include <libmd5sum.h>
 #include <lib/base/eerror.h>
+#include <Python.h>
 
 int eventData::CacheSize=0;
 descriptorMap eventData::descriptors;
@@ -15,7 +16,7 @@ __u8 eventData::data[4108];
 extern const uint32_t crc32_table[256];
 
 eventData::eventData(const eit_event_struct* e, int size, int type)
-       :ByteSize(size), type(type)
+       :ByteSize(size&0xFF), type(type&0xFF)
 {
        if (!e)
                return;
@@ -188,9 +189,10 @@ void eEPGCache::DVBChannelAdded(eDVBChannel *chan)
 {
        if ( chan )
        {
-               eDebug("[eEPGCache] add channel %p", chan);
+//             eDebug("[eEPGCache] add channel %p", chan);
                channel_data *data = new channel_data(this);
                data->channel = chan;
+               data->prevChannelState = -1;
                singleLock s(channel_map_lock);
                m_knownChannels.insert( std::pair<iDVBChannel*, channel_data* >(chan, data) );
                chan->connectStateChange(slot(*this, &eEPGCache::DVBChannelStateChanged), data->m_stateChangedConn);
@@ -256,31 +258,31 @@ void eEPGCache::DVBChannelStateChanged(iDVBChannel *chan)
        {
                int state=0;
                chan->getState(state);
-               switch (state)
+               if ( it->second->prevChannelState != state )
                {
-                       case iDVBChannel::state_idle:
-                               break;
-                       case iDVBChannel::state_tuning:
-                               break;
-                       case iDVBChannel::state_unavailable:
-                               break;
-                       case iDVBChannel::state_ok:
-                       {
-                               eDebug("[eEPGCache] channel %p running", chan);
-                               DVBChannelRunning(chan);
-                               break;
-                       }
-                       case iDVBChannel::state_release:
+                       switch (state)
                        {
-                               eDebug("[eEPGCache] remove channel %p", chan);
-                               messages.send(Message(Message::leaveChannel, chan));
-                               while(!it->second->can_delete)
-                                       usleep(1000);
-                               delete it->second;
-                               m_knownChannels.erase(it);
-                               // -> gotMessage -> abortEPG
-                               break;
+                               case iDVBChannel::state_ok:
+                               {
+                                       eDebug("[eEPGCache] channel %p running", chan);
+                                       DVBChannelRunning(chan);
+                                       break;
+                               }
+                               case iDVBChannel::state_release:
+                               {
+                                       eDebug("[eEPGCache] remove channel %p", chan);
+                                       messages.send(Message(Message::leaveChannel, chan));
+                                       while(!it->second->can_delete)
+                                               usleep(1000);
+                                       delete it->second;
+                                       m_knownChannels.erase(it);
+                                       // -> gotMessage -> abortEPG
+                                       break;
+                               }
+                               default: // ignore all other events
+                                       return;
                        }
+                       it->second->prevChannelState = state;
                }
        }
 }
@@ -627,7 +629,6 @@ void eEPGCache::thread()
 
 void eEPGCache::load()
 {
-#if 0
        FILE *f = fopen("/hdd/epg.dat", "r");
        if (f)
        {
@@ -636,6 +637,7 @@ void eEPGCache::load()
                int size=0;
                int cnt=0;
                bool md5ok=false;
+#if 0
                if (!md5_file("/hdd/epg.dat", 1, md5))
                {
                        FILE *f = fopen("/hdd/epg.dat.md5", "r");
@@ -648,6 +650,7 @@ void eEPGCache::load()
                        }
                }
                if ( md5ok )
+#endif
                {
                        char text1[13];
                        fread( text1, 13, 1, f);
@@ -687,12 +690,10 @@ void eEPGCache::load()
                        fclose(f);
                }
        }
-#endif
 }
 
 void eEPGCache::save()
 {
-#if 0
        struct statfs s;
        off64_t tmp;
        if (statfs("/hdd", &s)<0)
@@ -739,6 +740,7 @@ void eEPGCache::save()
                eDebug("%d events written to /hdd/epg.dat", cnt);
                eventData::save(f);
                fclose(f);
+#if 0
                unsigned char md5[16];
                if (!md5_file("/hdd/epg.dat", 1, md5))
                {
@@ -749,16 +751,8 @@ void eEPGCache::save()
                                fclose(f);
                        }
                }
-       }
 #endif
-}
-
-RESULT eEPGCache::getInstance(ePtr<eEPGCache> &ptr)
-{
-       ptr = instance;
-       if (!ptr)
-               return -1;
-       return 0;
+       }
 }
 
 eEPGCache::channel_data::channel_data(eEPGCache *ml)
@@ -1010,8 +1004,8 @@ void eEPGCache::channel_data::readData( const __u8 *data)
        }
 }
 
-RESULT eEPGCache::lookupEvent(const eServiceReferenceDVB &service, time_t t, const eventData *&result )
-// if t == 0 we search the current event...
+RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, const eventData *&result )
+// if t == -1 we search the current event...
 {
        singleLock s(cache_lock);
        uniqueEPGKey key(service);
@@ -1020,72 +1014,70 @@ RESULT eEPGCache::lookupEvent(const eServiceReferenceDVB &service, time_t t, con
        eventCache::iterator It = eventDB.find( key );
        if ( It != eventDB.end() && !It->second.first.empty() ) // entrys cached ?
        {
-               if (!t)
+               if (t==-1)
                        t = time(0)+eDVBLocalTimeHandler::getInstance()->difference();
-
-// TODO: optimize this.. why we here search first in timemap.. and then in eventmap??
-               timeMap::iterator i = It->second.second.lower_bound(t);
+               timeMap::iterator i = It->second.second.lower_bound(t);  // find > or equal
                if ( i != It->second.second.end() )
                {
-                       if ( i != It->second.second.end() )
+                       if ( i->second->getStartTime() != t )
                        {
-                               if ( t <= i->first+i->second->getDuration() )
+                               timeMap::iterator x = i;
+                               --x;
+                               if ( x != It->second.second.end() )
                                {
-                                       result = i->second;
-                                       return 0;
+                                       time_t start_time = x->second->getStartTime();
+                                       if (t < start_time)
+                                               return -1;
+                                       if (t > (start_time+x->second->getDuration()))
+                                               return -1;
+                                       i = x;
                                }
+                               else
+                                       return -1;
                        }
-               }
-
-               for ( eventMap::iterator i( It->second.first.begin() ); i != It->second.first.end(); i++)
-               {
-                       int duration = i->second->getDuration();
-                       time_t begTime = i->second->getStartTime();
-                       if ( t >= begTime && t <= begTime+duration) // then we have found
-                       {
-                               result = i->second;
-                               return 0;
-                       }
+                       result = i->second;
+                       return 0;
                }
        }
        return -1;
 }
 
-RESULT eEPGCache::lookupEvent(const eServiceReferenceDVB &service, time_t t, const eit_event_struct *&result )
+RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, const eit_event_struct *&result )
 {
        singleLock s(cache_lock);
        const eventData *data=0;
-       RESULT ret = lookupEvent(service, t, data);
+       RESULT ret = lookupEventTime(service, t, data);
        if ( !ret && data )
                result = data->get();
        return ret;
 }
 
-RESULT eEPGCache::lookupEvent(const eServiceReferenceDVB &service, time_t t, Event *& result )
+RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, Event *& result )
 {
        singleLock s(cache_lock);
        const eventData *data=0;
-       RESULT ret = lookupEvent(service, t, data);
+       RESULT ret = lookupEventTime(service, t, data);
        if ( !ret && data )
                result = new Event((uint8_t*)data->get());
        return ret;
 }
 
-RESULT eEPGCache::lookupEvent(const eServiceReferenceDVB &service, time_t t, ePtr<eServiceEvent> &result )
+RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, ePtr<eServiceEvent> &result )
 {
        singleLock s(cache_lock);
        const eventData *data=0;
-       RESULT ret = lookupEvent(service, t, data);
+       RESULT ret = lookupEventTime(service, t, data);
        if ( !ret && data )
        {
                Event ev((uint8_t*)data->get());
                result = new eServiceEvent();
-               ret = result->parseFrom(&ev);
+               const eServiceReferenceDVB &ref = (const eServiceReferenceDVB&)service;
+               ret = result->parseFrom(&ev, (ref.getTransportStreamID().get()<<16)|ref.getOriginalNetworkID().get());
        }
        return ret;
 }
 
-RESULT eEPGCache::lookupEvent(const eServiceReferenceDVB &service, int event_id, const eventData *&result )
+RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, const eventData *&result )
 {
        singleLock s(cache_lock);
        uniqueEPGKey key( service );
@@ -1108,41 +1100,42 @@ RESULT eEPGCache::lookupEvent(const eServiceReferenceDVB &service, int event_id,
        return -1;
 }
 
-RESULT eEPGCache::lookupEvent(const eServiceReferenceDVB &service, int event_id, const eit_event_struct *&result)
+RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, const eit_event_struct *&result)
 {
        singleLock s(cache_lock);
        const eventData *data=0;
-       RESULT ret = lookupEvent(service, event_id, data);
+       RESULT ret = lookupEventId(service, event_id, data);
        if ( !ret && data )
                result = data->get();
        return ret;
 }
 
-RESULT eEPGCache::lookupEvent(const eServiceReferenceDVB &service, int event_id, Event *& result)
+RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, Event *& result)
 {
        singleLock s(cache_lock);
        const eventData *data=0;
-       RESULT ret = lookupEvent(service, event_id, data);
+       RESULT ret = lookupEventId(service, event_id, data);
        if ( !ret && data )
                result = new Event((uint8_t*)data->get());
        return ret;
 }
 
-RESULT eEPGCache::lookupEvent(const eServiceReferenceDVB &service, int event_id, ePtr<eServiceEvent> &result)
+RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, ePtr<eServiceEvent> &result)
 {
        singleLock s(cache_lock);
        const eventData *data=0;
-       RESULT ret = lookupEvent(service, event_id, data);
+       RESULT ret = lookupEventId(service, event_id, data);
        if ( !ret && data )
        {
                Event ev((uint8_t*)data->get());
                result = new eServiceEvent();
-               ret = result->parseFrom(&ev);
+               const eServiceReferenceDVB &ref = (const eServiceReferenceDVB&)service;
+               ret = result->parseFrom(&ev, (ref.getTransportStreamID().get()<<16)|ref.getOriginalNetworkID().get());
        }
        return ret;
 }
 
-RESULT eEPGCache::startTimeQuery(const eServiceReferenceDVB &service, time_t begin, int minutes)
+RESULT eEPGCache::startTimeQuery(const eServiceReference &service, time_t begin, int minutes)
 {
        eventCache::iterator It = eventDB.find( service );
        if ( It != eventDB.end() && It->second.second.size() )
@@ -1151,14 +1144,25 @@ RESULT eEPGCache::startTimeQuery(const eServiceReferenceDVB &service, time_t beg
                if ( begin != -1 )
                {
                        m_timemap_cursor = It->second.second.lower_bound(begin);
-                       if ( m_timemap_cursor != It->second.second.end() && m_timemap_cursor != It->second.second.begin() )
+                       if ( m_timemap_cursor != It->second.second.end() )
                        {
-                               timeMap::iterator it = m_timemap_cursor;
-                               --it;
-                               if ( (it->second->getStartTime() + it->second->getDuration()) > begin )
-                                       m_timemap_cursor = it;
+                               if ( m_timemap_cursor->second->getStartTime() != begin )
+                               {
+                                       timeMap::iterator x = m_timemap_cursor;
+                                       --x;
+                                       if ( x != It->second.second.end() )
+                                       {
+                                               time_t start_time = x->second->getStartTime();
+                                               if ( begin > start_time && begin < (start_time+x->second->getDuration()))
+                                                       m_timemap_cursor = x;
+                                       }
+                               }
                        }
                }
+               else
+                       m_timemap_cursor = It->second.second.begin();
+               const eServiceReferenceDVB &ref = (const eServiceReferenceDVB&)service;
+               currentQueryTsidOnid = (ref.getTransportStreamID().get()<<16) | ref.getOriginalNetworkID().get();
                return 0;
        }
        return -1;
@@ -1200,7 +1204,276 @@ RESULT eEPGCache::getNextTimeEntry(ePtr<eServiceEvent> &result)
        {
                Event ev((uint8_t*)m_timemap_cursor++->second->get());
                result = new eServiceEvent();
-               return result->parseFrom(&ev);
+               return result->parseFrom(&ev, currentQueryTsidOnid);
        }
        return -1;
 }
+
+void fillTuple(PyObject *tuple, char *argstring, int argcount, PyObject *service, ePtr<eServiceEvent> &ptr, PyObject *nowTime, PyObject *service_name )
+{
+       PyObject *tmp=NULL;
+       int pos=0;
+       while(pos < argcount)
+       {
+               bool inc_refcount=false;
+               switch(argstring[pos])
+               {
+                       case 'I': // Event Id
+                               tmp = ptr ? PyLong_FromLong(ptr->getEventId()) : NULL;
+                               break;
+                       case 'B': // Event Begin Time
+                               tmp = ptr ? PyLong_FromLong(ptr->getBeginTime()) : NULL;
+                               break;
+                       case 'D': // Event Duration
+                               tmp = ptr ? PyLong_FromLong(ptr->getDuration()) : NULL;
+                               break;
+                       case 'T': // Event Title
+                               tmp = ptr ? PyString_FromString(ptr->getEventName().c_str()) : NULL;
+                               break;
+                       case 'S': // Event Short Description
+                               tmp = ptr ? PyString_FromString(ptr->getShortDescription().c_str()) : NULL;
+                               break;
+                       case 'E': // Event Extended Description
+                               tmp = ptr ? PyString_FromString(ptr->getExtendedDescription().c_str()) : NULL;
+                               break;
+                       case 'C': // Current Time
+                               tmp = nowTime;
+                               inc_refcount = true;
+                               break;
+                       case 'R': // service reference string
+                               tmp = service;
+                               inc_refcount = true;
+                               break;
+                       case 'N': // service name
+                               tmp = service_name;
+                               inc_refcount = true;
+               }
+               if (!tmp)
+               {
+                       tmp = Py_None;
+                       inc_refcount = true;
+               }
+               if (inc_refcount)
+                       Py_INCREF(tmp);
+               PyTuple_SET_ITEM(tuple, pos++, tmp);
+       }
+}
+
+PyObject *handleEvent(ePtr<eServiceEvent> &ptr, PyObject *dest_list, char* argstring, int argcount, PyObject *service, PyObject *nowTime, PyObject *service_name, PyObject *convertFunc, PyObject *convertFuncArgs)
+{
+       if (convertFunc)
+       {
+               fillTuple(convertFuncArgs, argstring, argcount, service, ptr, nowTime, service_name);
+               PyObject *result = PyObject_CallObject(convertFunc, convertFuncArgs);
+               if (result == NULL)
+               {
+                       if (service_name)
+                               Py_DECREF(service_name);
+                       if (nowTime)
+                               Py_DECREF(nowTime);
+                       Py_DECREF(convertFuncArgs);
+                       Py_DECREF(dest_list);
+                       return result;
+               }
+               PyList_Append(dest_list, result);
+               Py_DECREF(result);
+       }
+       else
+       {
+               PyObject *tuple = PyTuple_New(argcount);
+               fillTuple(tuple, argstring, argcount, service, ptr, nowTime, service_name);
+               PyList_Append(dest_list, tuple);
+               Py_DECREF(tuple);
+       }
+       return 0;
+}
+
+// here we get a list with tuples
+// first tuple entry is the servicereference
+// the second is the type of query (0 = time, 1 = event_id)
+// the third
+//             when type is eventid it is the event_id
+//             when type is time then it is the start_time ( 0 for now_time )
+// the fourth is the end_time .. ( optional )
+
+/* argv is a python string
+   I = Event Id
+   B = Event Begin Time
+   D = Event Duration
+   T = Event Title
+   S = Event Short Description
+   E = Event Extended Description
+   C = Current Time
+   R = Service Reference
+   N = Service Name
+*/
+
+PyObject *eEPGCache::lookupEvent(PyObject *list, PyObject *convertFunc)
+{
+       PyObject *convertFuncArgs=NULL;
+       int argcount=0;
+       char *argstring=NULL;
+       if (!PyList_Check(list))
+       {
+               PyErr_SetString(PyExc_StandardError,
+                       "type error");
+               eDebug("no list");
+               return NULL;
+       }
+       int listIt=0;
+       int listSize=PyList_Size(list);
+       if (!listSize)
+       {
+               PyErr_SetString(PyExc_StandardError,
+                       "not params given");
+               eDebug("not params given");
+               return NULL;
+       }
+       else 
+       {
+               PyObject *argv=PyList_GET_ITEM(list, 0); // borrowed reference!
+               if (PyString_Check(argv))
+               {
+                       argstring = PyString_AS_STRING(argv);
+                       ++listIt;
+               }
+               else
+                       argstring = "I"; // just event id as default
+               argcount = strlen(argstring);
+//             eDebug("have %d args('%s')", argcount, argstring);
+       }
+       if (convertFunc)
+       {
+               if (!PyCallable_Check(convertFunc))
+               {
+                       PyErr_SetString(PyExc_StandardError,
+                               "convertFunc must be callable");
+                       eDebug("convertFunc is not callable");
+                       return NULL;
+               }
+               convertFuncArgs = PyTuple_New(argcount);
+       }
+
+       PyObject *nowTime = strchr(argstring, 'C') ?
+               PyLong_FromLong(time(0)+eDVBLocalTimeHandler::getInstance()->difference()) :
+               NULL;
+
+       bool must_get_service_name = strchr(argstring, 'N') ? true : false;
+
+       // create dest list
+       PyObject *dest_list=PyList_New(0);
+       while(listSize > listIt)
+       {
+               PyObject *item=PyList_GET_ITEM(list, listIt++); // borrowed reference!
+               if (PyTuple_Check(item))
+               {
+                       int type=0;
+                       long event_id=-1;
+                       time_t stime=-1;
+                       int minutes=0;
+                       int tupleSize=PyTuple_Size(item);
+                       int tupleIt=0;
+                       PyObject *service=NULL;
+                       while(tupleSize > tupleIt)  // parse query args
+                       {
+                               PyObject *entry=PyTuple_GET_ITEM(item, tupleIt); // borrowed reference!
+                               switch(tupleIt++)
+                               {
+                                       case 0:
+                                       {
+                                               if (!PyString_Check(entry))
+                                               {
+                                                       eDebug("tuple entry 0 is no a string");
+                                                       continue;
+                                               }
+                                               service = entry;
+                                               break;
+                                       }
+                                       case 1:
+                                               type=PyInt_AsLong(entry);
+                                               if (type < 0 || type > 1)
+                                               {
+                                                       eDebug("unknown type %d", type);
+                                                       continue;
+                                               }
+                                               break;
+                                       case 2:
+                                               event_id=stime=PyInt_AsLong(entry);
+                                               break;
+                                       case 3:
+                                               minutes=PyInt_AsLong(entry);
+                                               break;
+                                       default:
+                                               eDebug("unneeded extra argument");
+                                               break;
+                               }
+                       }
+                       eServiceReference ref(PyString_AS_STRING(service));
+                       if (ref.type != eServiceReference::idDVB)
+                       {
+                               eDebug("service reference for epg query is not valid");
+                               continue;
+                       }
+                       PyObject *service_name=NULL;
+                       if (must_get_service_name)
+                       {
+                               ePtr<iStaticServiceInformation> sptr;
+                               eServiceCenterPtr service_center;
+                               eServiceCenter::getPrivInstance(service_center);
+                               if (service_center)
+                               {
+                                       service_center->info(ref, sptr);
+                                       if (sptr)
+                                       {
+                                               std::string name;
+                                               sptr->getName(ref, name);
+                                               if (name.length())
+                                                       service_name = PyString_FromString(name.c_str());
+                                       }
+                               }
+                               if (!service_name)
+                                       service_name = PyString_FromString("<n/a>");
+                       }
+                       if (minutes)
+                       {
+                               Lock();
+                               if (!startTimeQuery(ref, stime, minutes))
+                               {
+                                       ePtr<eServiceEvent> ptr;
+                                       while (!getNextTimeEntry(ptr))
+                                       {
+                                               PyObject *ret = handleEvent(ptr, dest_list, argstring, argcount, service, nowTime, service_name, convertFunc, convertFuncArgs);
+                                               if (ret)
+                                                       return ret;
+                                       }
+                               }
+                               Unlock();
+                       }
+                       else
+                       {
+                               ePtr<eServiceEvent> ptr;
+                               if (stime)
+                               {
+                                       if (type == 0)
+                                               lookupEventTime(ref, stime, ptr);
+                                       else // type == 1
+                                               lookupEventId(ref, event_id, ptr);
+                               }
+                               PyObject *ret = handleEvent(ptr, dest_list, argstring, argcount, service, nowTime, service_name, convertFunc, convertFuncArgs);
+                               if (ret)
+                                       return ret;
+                       }
+                       if (service_name)
+                               Py_DECREF(service_name);
+               }
+       }
+       if (convertFuncArgs)
+               Py_DECREF(convertFuncArgs);
+       if (nowTime)
+               Py_DECREF(nowTime);
+       return dest_list;
+}
+
+
+
+