[SCM] kodi-pvr-hts/master: [API 5.0.0] Added support for epg event state callback (async epg data transfer)

tiber-guest at users.alioth.debian.org tiber-guest at users.alioth.debian.org
Fri Nov 4 23:23:38 UTC 2016


The following commit has been merged in the master branch:
commit 0f4bbed384f008ebb5ca3863035e37316ba62399
Author: Kai Sommerfeld <kai.sommerfeld at gmx.com>
Date:   Thu Jan 14 14:28:27 2016 +0100

    [API 5.0.0] Added support for epg event state callback (async epg data transfer)

diff --git a/src/HTSPTypes.h b/src/HTSPTypes.h
index 950f45c..c1e49bc 100644
--- a/src/HTSPTypes.h
+++ b/src/HTSPTypes.h
@@ -27,6 +27,7 @@
 #include <map>
 #include <string>
 #include "client.h"
+#include "tvheadend/entity/Event.h"
 
 typedef enum {
   DVR_PRIO_IMPORTANT   = 0,
@@ -91,17 +92,27 @@ enum eHTSPEventType
 struct SHTSPEvent
 {
   eHTSPEventType m_type;
-  uint32_t       m_idx;
 
-  SHTSPEvent (eHTSPEventType type = HTSP_EVENT_NONE, uint32_t idx = 0) :
+  // params for HTSP_EVENT_EPG_UPDATE
+  tvheadend::entity::Event m_epg;
+  EPG_EVENT_STATE          m_state;
+
+  SHTSPEvent (eHTSPEventType type = HTSP_EVENT_NONE) :
     m_type(type),
-    m_idx (idx)
+    m_state(EPG_EVENT_CREATED)
   {
   }
-  
+
+  SHTSPEvent (eHTSPEventType type, const tvheadend::entity::Event &epg, EPG_EVENT_STATE state) :
+    m_type(type),
+    m_epg(epg),
+    m_state(state)
+  {
+  }
+
   bool operator==(const SHTSPEvent &right) const
   {
-    return m_type == right.m_type && m_idx == right.m_idx;
+    return m_type == right.m_type && m_epg == right.m_epg && m_state && right.m_state;
   }
 
   bool operator!=(const SHTSPEvent &right) const
diff --git a/src/Tvheadend.cpp b/src/Tvheadend.cpp
index 728c46a..c7b2646 100644
--- a/src/Tvheadend.cpp
+++ b/src/Tvheadend.cpp
@@ -1181,12 +1181,9 @@ PVR_ERROR CTvheadend::UpdateTimer ( const PVR_TIMER &timer )
  * EPG
  * *************************************************************************/
 
-/* Transfer schedule to XBMC */
-void CTvheadend::TransferEvent
-  ( ADDON_HANDLE handle, const Event &event )
+void CTvheadend::CreateEvent
+  ( const Event &event, EPG_TAG &epg )
 {
-  /* Build */
-  EPG_TAG epg;
   memset(&epg, 0, sizeof(EPG_TAG));
   epg.iUniqueBroadcastId  = event.GetId();
   epg.strTitle            = event.GetTitle().c_str();
@@ -1214,43 +1211,40 @@ void CTvheadend::TransferEvent
   epg.iEpisodePartNumber  = event.GetPart();
   epg.strEpisodeName      = event.GetSubtitle().c_str();
   epg.iFlags              = EPG_TAG_FLAG_UNDEFINED;
+}
+
+void CTvheadend::TransferEvent
+  ( const Event &event, EPG_EVENT_STATE state )
+{
+  /* Build */
+  EPG_TAG tag;
+  CreateEvent(event, tag);
+
+  /* Transfer event to Kodi */
+  PVR->EpgEventStateChange(&tag, event.GetChannel(), state);
+}
+
+void CTvheadend::TransferEvent
+  ( ADDON_HANDLE handle, const Event &event )
+{
+  /* Build */
+  EPG_TAG tag;
+  CreateEvent(event, tag);
 
-  /* Callback. */
-  PVR->TransferEpgEntry(handle, &epg);
+  /* Transfer event to Kodi */
+  PVR->TransferEpgEntry(handle, &tag);
 }
 
 PVR_ERROR CTvheadend::GetEpg
   ( ADDON_HANDLE handle, const PVR_CHANNEL &chn, time_t start, time_t end )
 {
   htsmsg_field_t *f;
-  int n = 0;
 
   Logger::Log(LogLevel::LEVEL_TRACE, "get epg channel %d start %ld stop %ld", chn.iUniqueId,
            (long long)start, (long long)end);
 
-  /* Async transfer */
-  if (Settings::GetInstance().GetAsyncEpg())
-  {
-    if (!m_asyncState.WaitForState(ASYNC_DONE))
-      return PVR_ERROR_FAILED;
-    
-    // Find the relevant events
-    Segment segment;
-    {
-      CLockObject lock(m_mutex);
-      auto sit = m_schedules.find(chn.iUniqueId);
-
-      if (sit != m_schedules.cend())
-        segment = sit->second.GetSegment(start, end);
-    }
-
-    // Transfer
-    for (const auto &event : segment)
-      TransferEvent(handle, event);
-
-  /* Synchronous transfer */
-  }
-  else
+  /* Note: Nothing to do if "async epg transfer" is enabled as all changes are pushed live to Kodi, then. */
+  if (!Settings::GetInstance().GetAsyncEpg())
   {
     /* Build message */
     htsmsg_t *msg = htsmsg_create_map();
@@ -1267,13 +1261,16 @@ PVR_ERROR CTvheadend::GetEpg
 
     /* Process */
     htsmsg_t *l;
-    
+
     if (!(l = htsmsg_get_list(msg, "events")))
     {
       htsmsg_destroy(msg);
       Logger::Log(LogLevel::LEVEL_ERROR, "malformed getEvents response: 'events' missing");
       return PVR_ERROR_SERVER_ERROR;
     }
+
+    int n = 0;
+
     HTSMSG_FOREACH(f, l)
     {
       Event event;
@@ -1288,10 +1285,8 @@ PVR_ERROR CTvheadend::GetEpg
       }
     }
     htsmsg_destroy(msg);
+    Logger::Log(LogLevel::LEVEL_TRACE, "get epg channel %d events %d", chn.iUniqueId, n);
   }
-
-  Logger::Log(LogLevel::LEVEL_TRACE, "get epg channel %d events %d", chn.iUniqueId, n);
-
   return PVR_ERROR_NO_ERROR;
 }
 
@@ -1490,7 +1485,7 @@ void* CTvheadend::Process ( void )
           PVR->TriggerRecordingUpdate();
           break;
         case HTSP_EVENT_EPG_UPDATE:
-          PVR->TriggerEpgUpdate(it->m_idx);
+          TransferEvent(it->m_epg, it->m_state);
           break;
         case HTSP_EVENT_NONE:
           break;
@@ -1588,23 +1583,44 @@ void CTvheadend::SyncEpgCompleted ( void )
     return;
 
   /* Schedules */
-  utilities::erase_if(m_schedules, [](const ScheduleMapEntry &entry)
+  std::vector<std::pair<uint32_t, uint32_t > > deletedEvents;
+  utilities::erase_if(m_schedules, [&](const ScheduleMapEntry &entry)
   {
-    return entry.second.IsDirty();
+    if (entry.second.IsDirty())
+    {
+      // all events are dirty too!
+      for (auto &evt : entry.second.GetEvents())
+        deletedEvents.push_back(
+          std::make_pair(evt.second.GetId() /* event uid */, entry.second.GetId() /* channel uid */));
+
+      return true;
+    }
+    return false;
   });
 
   /* Events */
   for (auto &entry : m_schedules)
   {
-    utilities::erase_if(entry.second.GetEvents(), [](const EventMapEntry &entry)
+    utilities::erase_if(entry.second.GetEvents(), [&](const EventUidsMapEntry &mapEntry)
     {
-      return entry.second.IsDirty();
+      if (mapEntry.second.IsDirty())
+      {
+        deletedEvents.push_back(
+          std::make_pair(mapEntry.second.GetId() /* event uid */, entry.second.GetId() /* channel uid */));
+        return true;
+      }
+      return false;
     });
   }
-  
-  /* Trigger updates */
-  for (const auto &entry : m_schedules)
-    TriggerEpgUpdate(entry.second.GetId());
+
+  Event evt;
+  for (auto &entry : deletedEvents)
+  {
+    /* Transfer event to Kodi (callback) */
+    evt.SetId(entry.first);
+    evt.SetChannel(entry.second);
+    PushEpgEventUpdate(evt, EPG_EVENT_DELETED);
+  }
 }
 
 void CTvheadend::ParseTagAddOrUpdate ( htsmsg_t *msg, bool bAdd )
@@ -2083,41 +2099,56 @@ bool CTvheadend::ParseEvent ( htsmsg_t *msg, bool bAdd, Event &evt )
 
 void CTvheadend::ParseEventAddOrUpdate ( htsmsg_t *msg, bool bAdd )
 {
-  Event tmp;
+  Event evt;
 
   /* Parse */
-  if (!ParseEvent(msg, bAdd, tmp))
+  if (!ParseEvent(msg, bAdd, evt))
     return;
 
-  /* Get event handle */
-  Schedule &sched  = m_schedules[tmp.GetChannel()];
-  Events   &events = sched.GetEvents();
-  Event    &evt    = events[tmp.GetId()];
-  Event comparison = evt;
-  sched.SetId(tmp.GetChannel());
+  /* create/update schedule */
+  Schedule &sched = m_schedules[evt.GetChannel()];
+  sched.SetId(evt.GetChannel());
   sched.SetDirty(false);
-  evt.SetId(tmp.GetId());
-  evt.SetDirty(false);
-  
-  /* Store */
-  evt = tmp;
 
-  /* Update */
-  if (evt != comparison)
+  /* create/update event */
+  EventUids &events = sched.GetEvents();
+
+  bool bUpdated(false);
+  if (bAdd && m_asyncState.GetState() < ASYNC_DONE)
   {
-    Logger::Log(LogLevel::LEVEL_TRACE, "event id:%d channel:%d start:%d stop:%d title:%s desc:%s",
-             evt.GetId(), evt.GetChannel(), (int)evt.GetStart(), (int)evt.GetStop(),
-             evt.GetTitle().c_str(), evt.GetDesc().c_str());
+    // After a reconnect, during processing of "enableAsyncMetadata" htsp
+    // method, tvheadend sends all events as "added". Check whether we
+    // announced the event already and in case send it as "updated" to Kodi.
+    auto it = events.find(evt.GetId());
+    if (it != events.end())
+    {
+      bUpdated = true;
 
-    if (m_asyncState.GetState() > ASYNC_EPG)
-      TriggerEpgUpdate(tmp.GetChannel());
+      Entity &ent = it->second;
+      ent.SetId(evt.GetId());
+      ent.SetDirty(false);
+    }
+  }
+
+  if (!bUpdated)
+  {
+    Entity &ent = events[evt.GetId()];
+    ent.SetId(evt.GetId());
+    ent.SetDirty(false);
   }
+
+  Logger::Log(LogLevel::LEVEL_TRACE, "event id:%d channel:%d start:%d stop:%d title:%s desc:%s",
+              evt.GetId(), evt.GetChannel(), (int)evt.GetStart(), (int)evt.GetStop(),
+              evt.GetTitle().c_str(), evt.GetDesc().c_str());
+
+  /* Transfer event to Kodi (callback) */
+  PushEpgEventUpdate(evt, (!bAdd || bUpdated) ? EPG_EVENT_UPDATED : EPG_EVENT_CREATED);
 }
 
 void CTvheadend::ParseEventDelete ( htsmsg_t *msg )
 {
   uint32_t u32;
-  
+
   /* Validate */
   if (htsmsg_get_u32(msg, "eventId", &u32))
   {
@@ -2125,12 +2156,12 @@ void CTvheadend::ParseEventDelete ( htsmsg_t *msg )
     return;
   }
   Logger::Log(LogLevel::LEVEL_TRACE, "delete event %u", u32);
-  
+
   /* Erase */
   for (auto &entry : m_schedules)
   {
-    Schedule &schedule = entry.second;
-    Events &events = schedule.GetEvents();
+    Schedule  &schedule = entry.second;
+    EventUids &events   = schedule.GetEvents();
 
     // Find the event so we can get the channel number
     auto eit = events.find(u32);
@@ -2139,7 +2170,12 @@ void CTvheadend::ParseEventDelete ( htsmsg_t *msg )
     {
       Logger::Log(LogLevel::LEVEL_TRACE, "deleted event %d from channel %d", u32, schedule.GetId());
       events.erase(eit);
-      TriggerEpgUpdate(schedule.GetId());
+
+      /* Transfer event to Kodi (callback) */
+      Event evt;
+      evt.SetId(u32);
+      evt.SetChannel(schedule.GetId());
+      PushEpgEventUpdate(evt, EPG_EVENT_DELETED);
       return;
     }
   }
diff --git a/src/Tvheadend.h b/src/Tvheadend.h
index 724bcd7..3c5dc5d 100644
--- a/src/Tvheadend.h
+++ b/src/Tvheadend.h
@@ -455,10 +455,10 @@ private:
   {
     m_events.push_back(SHTSPEvent(HTSP_EVENT_REC_UPDATE));
   }
-  inline void TriggerEpgUpdate ( uint32_t idx )
+  inline void PushEpgEventUpdate ( const tvheadend::entity::Event &epg, EPG_EVENT_STATE state )
   {
-    SHTSPEvent event = SHTSPEvent(HTSP_EVENT_EPG_UPDATE, idx);
-    
+    SHTSPEvent event = SHTSPEvent(HTSP_EVENT_EPG_UPDATE, epg, state);
+
     if (std::find(m_events.begin(), m_events.end(), event) == m_events.end())
       m_events.push_back(event);
   }
@@ -466,6 +466,8 @@ private:
   /*
    * Epg Handling
    */
+  void        CreateEvent     ( const tvheadend::entity::Event &event, EPG_TAG &epg );
+  void        TransferEvent   ( const tvheadend::entity::Event &event, EPG_EVENT_STATE state );
   void        TransferEvent   ( ADDON_HANDLE handle, const tvheadend::entity::Event &event );
 
   /*
diff --git a/src/tvheadend/entity/Schedule.cpp b/src/tvheadend/entity/Schedule.cpp
index 17c7293..410068b 100644
--- a/src/tvheadend/entity/Schedule.cpp
+++ b/src/tvheadend/entity/Schedule.cpp
@@ -35,22 +35,12 @@ void Schedule::SetDirty(bool dirty)
   }
 }
 
-Segment Schedule::GetSegment(time_t startTime, time_t endTime) const
+EventUids& Schedule::GetEvents()
 {
-  Segment segment;
-
-  for (const auto &entry : m_events)
-  {
-    const Event &event = entry.second;
-
-    if (event.GetStart() < endTime && event.GetStop() > startTime)
-      segment.push_back(event);
-  }
-
-  return segment;
+  return m_events;
 }
 
-Events& Schedule::GetEvents()
+const EventUids& Schedule::GetEvents() const
 {
   return m_events;
 }
diff --git a/src/tvheadend/entity/Schedule.h b/src/tvheadend/entity/Schedule.h
index 533d3a6..b0023b8 100644
--- a/src/tvheadend/entity/Schedule.h
+++ b/src/tvheadend/entity/Schedule.h
@@ -21,9 +21,9 @@
  *
  */
 
+#include <map>
 #include <vector>
 #include "Entity.h"
-#include "Event.h"
 
 namespace tvheadend
 {
@@ -32,7 +32,9 @@ namespace tvheadend
     class Schedule;
     typedef std::pair<int, Schedule> ScheduleMapEntry;
     typedef std::map<int, Schedule> Schedules;
-    typedef std::vector<Event> Segment;
+
+    typedef std::pair<uint32_t, Entity> EventUidsMapEntry;
+    typedef std::map<uint32_t, Entity> EventUids;
 
     /**
      * Represents a schedule. A schedule has a channel and a bunch of events. 
@@ -44,18 +46,17 @@ namespace tvheadend
       virtual void SetDirty(bool dirty);
 
       /**
-       * @return a segment containing the events that occur within the
-       * specified times
+       * @return read-write reference to the events in this schedule
        */
-      Segment GetSegment(time_t startTime, time_t endTime) const;
+      EventUids& GetEvents();
 
       /**
-       * @return read-write reference to the events in this schedule
+       * @return read-only reference to the events in this schedule
        */
-      Events& GetEvents();
+      const EventUids& GetEvents() const;
 
     private:
-      Events m_events;
+      EventUids m_events; // event uids
     };
   }
-}
\ No newline at end of file
+}

-- 
kodi-pvr-hts packaging



More information about the pkg-multimedia-commits mailing list