mirror of
https://github.com/tuxbox-neutrino/neutrino.git
synced 2025-08-28 07:51:19 +02:00
4636 lines
137 KiB
C++
4636 lines
137 KiB
C++
//
|
|
// sectionsd.cpp (network daemon for SI-sections)
|
|
// (dbox-II-project)
|
|
//
|
|
// Copyright (C) 2001 by fnbrd
|
|
//
|
|
// Homepage: http://dbox2.elxsi.de
|
|
//
|
|
// Copyright (C) 2008, 2009 Stefan Seyfried
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 2 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
//
|
|
|
|
#include <config.h>
|
|
#include <malloc.h>
|
|
#include <dmxapi.h>
|
|
#include <dmx.h>
|
|
#include <debug.h>
|
|
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <pthread.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
|
|
#include <set>
|
|
#include <map>
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include <limits>
|
|
|
|
#include <sys/wait.h>
|
|
#include <sys/time.h>
|
|
|
|
#include <connection/basicsocket.h>
|
|
#include <connection/basicserver.h>
|
|
|
|
#include <xmltree/xmlinterface.h>
|
|
#include <configfile.h>
|
|
#include <zapit/client/zapittools.h>
|
|
|
|
#include <sectionsdclient/sectionsdMsg.h>
|
|
#include <sectionsdclient/sectionsdclient.h>
|
|
#include <eventserver.h>
|
|
#include <driver/abstime.h>
|
|
|
|
#include "eitd.h"
|
|
#include "edvbstring.h"
|
|
|
|
//#define ENABLE_SDT //FIXME
|
|
|
|
// 60 Minuten Zyklus...
|
|
#define TIME_EIT_SCHEDULED_PAUSE 60 * 60
|
|
// -- 5 Minutes max. pause should improve behavior (rasc, 2005-05-02)
|
|
// #define TIME_EIT_SCHEDULED_PAUSE 5* 60
|
|
// Zeit die fuer die gewartet wird, bevor der Filter weitergeschaltet wird, falls es automatisch nicht klappt
|
|
#define TIME_EIT_SKIPPING 90
|
|
|
|
#ifdef ENABLE_FREESATEPG
|
|
#endif
|
|
|
|
static bool sectionsd_ready = false;
|
|
/*static*/ bool reader_ready = true;
|
|
static unsigned int max_events;
|
|
|
|
//#define HOUSEKEEPING_SLEEP (5 * 60) // sleep 5 minutes
|
|
#define HOUSEKEEPING_SLEEP (30) // FIXME 1 min for testing
|
|
#define META_HOUSEKEEPING (24 * 60 * 60) / HOUSEKEEPING_SLEEP // meta housekeeping after XX housekeepings - every 24h -
|
|
|
|
// Timeout bei tcp/ip connections in ms
|
|
#define READ_TIMEOUT_IN_SECONDS 2
|
|
#define WRITE_TIMEOUT_IN_SECONDS 2
|
|
|
|
|
|
// Timeout in ms for reading from dmx in EIT threads. Dont make this too long
|
|
// since we are holding the start_stop lock during this read!
|
|
#define EIT_READ_TIMEOUT 100
|
|
// Number of DMX read timeouts, after which we check if there is an EIT at all
|
|
// for EIT and PPT threads...
|
|
#define CHECK_RESTART_DMX_AFTER_TIMEOUTS (2000 / EIT_READ_TIMEOUT) // 2 seconds
|
|
|
|
// Time in seconds we are waiting for an EIT version number
|
|
#define TIME_EIT_VERSION_WAIT 3
|
|
// number of timeouts after which we stop waiting for an EIT version number
|
|
#define TIMEOUTS_EIT_VERSION_WAIT (2 * CHECK_RESTART_DMX_AFTER_TIMEOUTS)
|
|
|
|
// the maximum length of a section (0x0fff) + header (3)
|
|
#define MAX_SECTION_LENGTH (0x0fff + 3)
|
|
|
|
static long secondsToCache;
|
|
static long secondsExtendedTextCache;
|
|
static long oldEventsAre;
|
|
static int scanning = 1;
|
|
|
|
std::string epg_filter_dir = "/var/tuxbox/config/zapit/epgfilter.xml";
|
|
static bool epg_filter_is_whitelist = false;
|
|
static bool epg_filter_except_current_next = false;
|
|
static bool messaging_zap_detected = false;
|
|
|
|
std::string dvbtime_filter_dir = "/var/tuxbox/config/zapit/dvbtimefilter.xml";
|
|
static bool dvb_time_update = false;
|
|
|
|
//NTP-Config
|
|
#define CONF_FILE "/var/tuxbox/config/neutrino.conf"
|
|
|
|
#ifdef USE_BB_NTPD
|
|
const std::string ntp_system_cmd_prefix = "/sbin/ntpd -q -p ";
|
|
#else
|
|
const std::string ntp_system_cmd_prefix = "/sbin/ntpdate ";
|
|
#endif
|
|
|
|
std::string ntp_system_cmd;
|
|
CConfigFile ntp_config(',');
|
|
std::string ntpserver;
|
|
int ntprefresh;
|
|
int ntpenable;
|
|
|
|
static int eit_update_fd = -1;
|
|
static bool update_eit = true;
|
|
|
|
/* messaging_current_servicekey does probably not need locking, since it is
|
|
changed from one place */
|
|
static t_channel_id messaging_current_servicekey = 0;
|
|
static bool channel_is_blacklisted = false;
|
|
// EVENTS...
|
|
|
|
static CEventServer *eventServer;
|
|
|
|
/*static*/ pthread_rwlock_t eventsLock = PTHREAD_RWLOCK_INITIALIZER; // Unsere (fast-)mutex, damit nicht gleichzeitig in die Menge events geschrieben und gelesen wird
|
|
static pthread_rwlock_t servicesLock = PTHREAD_RWLOCK_INITIALIZER; // Unsere (fast-)mutex, damit nicht gleichzeitig in die Menge services geschrieben und gelesen wird
|
|
static pthread_rwlock_t messagingLock = PTHREAD_RWLOCK_INITIALIZER;
|
|
|
|
static pthread_cond_t timeThreadSleepCond = PTHREAD_COND_INITIALIZER;
|
|
static pthread_mutex_t timeThreadSleepMutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
/* no matter how big the buffer, we will receive spurious POLLERR's in table 0x60,
|
|
but those are not a big deal, so let's save some memory */
|
|
static DMX dmxEIT(0x12, 3000 /*320*/);
|
|
static DMX dmxCN(0x12, 512, false, 1);
|
|
|
|
#ifdef ENABLE_FREESATEPG
|
|
// a little more time for freesat epg
|
|
#define TIME_FSEIT_SKIPPING 240
|
|
static DMX dmxFSEIT(3842, 320);
|
|
#endif
|
|
|
|
#ifdef ENABLE_SDT
|
|
#define TIME_SDT_NONEWDATA 5
|
|
#define RESTART_DMX_AFTER_TIMEOUTS 5
|
|
#define TIME_SDT_SCHEDULED_PAUSE 2* 60* 60
|
|
static DMX dmxSDT(0x11, 512, true, 0);
|
|
#endif
|
|
int sectionsd_stop = 0;
|
|
|
|
static bool slow_addevent = true;
|
|
|
|
inline void readLockServices(void)
|
|
{
|
|
pthread_rwlock_rdlock(&servicesLock);
|
|
}
|
|
|
|
inline void writeLockServices(void)
|
|
{
|
|
pthread_rwlock_wrlock(&servicesLock);
|
|
}
|
|
|
|
inline void unlockServices(void)
|
|
{
|
|
pthread_rwlock_unlock(&servicesLock);
|
|
}
|
|
|
|
inline void readLockMessaging(void)
|
|
{
|
|
pthread_rwlock_rdlock(&messagingLock);
|
|
}
|
|
|
|
inline void writeLockMessaging(void)
|
|
{
|
|
pthread_rwlock_wrlock(&messagingLock);
|
|
}
|
|
|
|
inline void unlockMessaging(void)
|
|
{
|
|
pthread_rwlock_unlock(&messagingLock);
|
|
}
|
|
|
|
inline void readLockEvents(void)
|
|
{
|
|
pthread_rwlock_rdlock(&eventsLock);
|
|
}
|
|
|
|
inline void writeLockEvents(void)
|
|
{
|
|
pthread_rwlock_wrlock(&eventsLock);
|
|
}
|
|
|
|
inline void unlockEvents(void)
|
|
{
|
|
pthread_rwlock_unlock(&eventsLock);
|
|
}
|
|
|
|
bool timeset = false;
|
|
bool bTimeCorrect = false;
|
|
pthread_cond_t timeIsSetCond = PTHREAD_COND_INITIALIZER;
|
|
pthread_mutex_t timeIsSetMutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
static int messaging_have_CN = 0x00; // 0x01 = CURRENT, 0x02 = NEXT
|
|
static int messaging_got_CN = 0x00; // 0x01 = CURRENT, 0x02 = NEXT
|
|
static time_t messaging_last_requested = time_monotonic();
|
|
static bool messaging_neutrino_sets_time = false;
|
|
|
|
inline bool waitForTimeset(void)
|
|
{
|
|
pthread_mutex_lock(&timeIsSetMutex);
|
|
while(!timeset)
|
|
pthread_cond_wait(&timeIsSetCond, &timeIsSetMutex);
|
|
pthread_mutex_unlock(&timeIsSetMutex);
|
|
/* we have time synchronization issues, at least on kernel 2.4, so
|
|
sometimes the time in the threads is still 1.1.1970, even after
|
|
waitForTimeset() returns. Let's hope that we work around this issue
|
|
with this sleep */
|
|
sleep(1);
|
|
writeLockMessaging();
|
|
messaging_last_requested = time_monotonic();
|
|
unlockMessaging();
|
|
return true;
|
|
}
|
|
|
|
static int64_t last_profile_call;
|
|
|
|
void showProfiling( std::string text )
|
|
{
|
|
struct timeval tv;
|
|
|
|
gettimeofday( &tv, NULL );
|
|
int64_t now = (int64_t) tv.tv_usec + (int64_t)((int64_t) tv.tv_sec * (int64_t) 1000000);
|
|
|
|
int64_t tmp = now - last_profile_call;
|
|
printf("--> '%s' %lld.%03lld\n", text.c_str(), tmp / 1000LL, tmp % 1000LL);
|
|
last_profile_call = now;
|
|
}
|
|
|
|
static const SIevent nullEvt; // Null-Event
|
|
|
|
// Wir verwalten die events in SmartPointers
|
|
// und nutzen verschieden sortierte Menge zum Zugriff
|
|
//------------------------------------------------------------
|
|
|
|
static MySIeventsOrderUniqueKey mySIeventsOrderUniqueKey;
|
|
|
|
static SIevent * myCurrentEvent = NULL;
|
|
static SIevent * myNextEvent = NULL;
|
|
|
|
// Mengen mit SIeventPtr sortiert nach Event-ID fuer NVOD-Events (mehrere Zeiten)
|
|
static MySIeventsOrderUniqueKey mySIeventsNVODorderUniqueKey;
|
|
/*static*/ MySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey;
|
|
|
|
static MySIeventsOrderFirstEndTimeServiceIDEventUniqueKey mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey;
|
|
|
|
// Hier landen alle Service-Ids von Meta-Events inkl. der zugehoerigen Event-ID (nvod)
|
|
// d.h. key ist der Unique Service-Key des Meta-Events und Data ist der unique Event-Key
|
|
static MySIeventUniqueKeysMetaOrderServiceUniqueKey mySIeventUniqueKeysMetaOrderServiceUniqueKey;
|
|
|
|
static MySIservicesOrderUniqueKey mySIservicesOrderUniqueKey;
|
|
static MySIservicesNVODorderUniqueKey mySIservicesNVODorderUniqueKey;
|
|
|
|
struct EPGFilter
|
|
{
|
|
t_original_network_id onid;
|
|
t_transport_stream_id tsid;
|
|
t_service_id sid;
|
|
EPGFilter *next;
|
|
};
|
|
|
|
struct ChannelBlacklist
|
|
{
|
|
t_channel_id chan;
|
|
t_channel_id mask;
|
|
ChannelBlacklist *next;
|
|
};
|
|
|
|
struct ChannelNoDVBTimelist
|
|
{
|
|
t_channel_id chan;
|
|
t_channel_id mask;
|
|
ChannelNoDVBTimelist *next;
|
|
};
|
|
|
|
EPGFilter *CurrentEPGFilter = NULL;
|
|
ChannelBlacklist *CurrentBlacklist = NULL;
|
|
ChannelNoDVBTimelist *CurrentNoDVBTime = NULL;
|
|
|
|
static bool checkEPGFilter(t_original_network_id onid, t_transport_stream_id tsid, t_service_id sid)
|
|
{
|
|
EPGFilter *filterptr = CurrentEPGFilter;
|
|
while (filterptr)
|
|
{
|
|
if (((filterptr->onid == onid) || (filterptr->onid == 0)) &&
|
|
((filterptr->tsid == tsid) || (filterptr->tsid == 0)) &&
|
|
((filterptr->sid == sid) || (filterptr->sid == 0)))
|
|
return true;
|
|
filterptr = filterptr->next;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool checkBlacklist(t_channel_id channel_id)
|
|
{
|
|
ChannelBlacklist *blptr = CurrentBlacklist;
|
|
while (blptr)
|
|
{
|
|
if (blptr->chan == (channel_id & blptr->mask))
|
|
return true;
|
|
blptr = blptr->next;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool checkNoDVBTimelist(t_channel_id channel_id)
|
|
{
|
|
ChannelNoDVBTimelist *blptr = CurrentNoDVBTime;
|
|
while (blptr)
|
|
{
|
|
if (blptr->chan == (channel_id & blptr->mask))
|
|
return true;
|
|
blptr = blptr->next;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void addEPGFilter(t_original_network_id onid, t_transport_stream_id tsid, t_service_id sid)
|
|
{
|
|
if (!checkEPGFilter(onid, tsid, sid))
|
|
{
|
|
dprintf("Add EPGFilter for onid=\"%04x\" tsid=\"%04x\" service_id=\"%04x\"\n", onid, tsid, sid);
|
|
EPGFilter *node = new EPGFilter;
|
|
node->onid = onid;
|
|
node->tsid = tsid;
|
|
node->sid = sid;
|
|
node->next = CurrentEPGFilter;
|
|
CurrentEPGFilter = node;
|
|
}
|
|
}
|
|
|
|
static void addBlacklist(t_original_network_id onid, t_transport_stream_id tsid, t_service_id sid)
|
|
{
|
|
t_channel_id channel_id =
|
|
CREATE_CHANNEL_ID_FROM_SERVICE_ORIGINALNETWORK_TRANSPORTSTREAM_ID(sid, onid, tsid);
|
|
t_channel_id mask =
|
|
CREATE_CHANNEL_ID_FROM_SERVICE_ORIGINALNETWORK_TRANSPORTSTREAM_ID(
|
|
(sid ? 0xFFFF : 0), (onid ? 0xFFFF : 0), (tsid ? 0xFFFF : 0)
|
|
);
|
|
if (!checkBlacklist(channel_id))
|
|
{
|
|
xprintf("Add Channel Blacklist for channel 0x%012llx, mask 0x%012llx\n", channel_id, mask);
|
|
ChannelBlacklist *node = new ChannelBlacklist;
|
|
node->chan = channel_id;
|
|
node->mask = mask;
|
|
node->next = CurrentBlacklist;
|
|
CurrentBlacklist = node;
|
|
}
|
|
}
|
|
|
|
static void addNoDVBTimelist(t_original_network_id onid, t_transport_stream_id tsid, t_service_id sid)
|
|
{
|
|
t_channel_id channel_id =
|
|
CREATE_CHANNEL_ID_FROM_SERVICE_ORIGINALNETWORK_TRANSPORTSTREAM_ID(sid, onid, tsid);
|
|
t_channel_id mask =
|
|
CREATE_CHANNEL_ID_FROM_SERVICE_ORIGINALNETWORK_TRANSPORTSTREAM_ID(
|
|
(sid ? 0xFFFF : 0), (onid ? 0xFFFF : 0), (tsid ? 0xFFFF : 0)
|
|
);
|
|
if (!checkNoDVBTimelist(channel_id))
|
|
{
|
|
xprintf("Add channel 0x%012llx, mask 0x%012llx to NoDVBTimelist\n", channel_id, mask);
|
|
ChannelNoDVBTimelist *node = new ChannelNoDVBTimelist;
|
|
node->chan = channel_id;
|
|
node->mask = mask;
|
|
node->next = CurrentNoDVBTime;
|
|
CurrentNoDVBTime = node;
|
|
}
|
|
}
|
|
|
|
// Loescht ein Event aus allen Mengen
|
|
static bool deleteEvent(const event_id_t uniqueKey)
|
|
{
|
|
writeLockEvents();
|
|
MySIeventsOrderUniqueKey::iterator e = mySIeventsOrderUniqueKey.find(uniqueKey);
|
|
|
|
if (e != mySIeventsOrderUniqueKey.end())
|
|
{
|
|
if (e->second->times.size())
|
|
{
|
|
mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.erase(e->second);
|
|
mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.erase(e->second);
|
|
}
|
|
|
|
mySIeventsOrderUniqueKey.erase(uniqueKey);
|
|
mySIeventsNVODorderUniqueKey.erase(uniqueKey);
|
|
|
|
// printf("Deleting: %04x\n", (int) uniqueKey);
|
|
unlockEvents();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
unlockEvents();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Fuegt ein Event in alle Mengen ein
|
|
/* if cn == true (if called by cnThread), then myCurrentEvent and myNextEvent is updated, too */
|
|
/*static*/ void addEvent(const SIevent &evt, const time_t zeit, bool cn = false)
|
|
{
|
|
bool EPG_filtered = checkEPGFilter(evt.original_network_id, evt.transport_stream_id, evt.service_id);
|
|
|
|
/* more readable in "plain english":
|
|
if current/next are not to be filtered and table_id is current/next -> continue
|
|
else {
|
|
if epg filter is blacklist and filter matched -> stop. (return)
|
|
if epg filter is whitelist and filter did not match -> stop also.
|
|
}
|
|
*/
|
|
if (!(epg_filter_except_current_next && (evt.table_id == 0x4e || evt.table_id == 0x4f)) &&
|
|
(evt.table_id != 0xFF)) {
|
|
if (!epg_filter_is_whitelist && EPG_filtered) {
|
|
//dprintf("addEvent: blacklist and filter did match\n");
|
|
return;
|
|
}
|
|
if (epg_filter_is_whitelist && !EPG_filtered) {
|
|
//dprintf("addEvent: whitelist and filter did not match\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (cn) { // current-next => fill current or next event...
|
|
readLockMessaging();
|
|
if (evt.get_channel_id() == messaging_current_servicekey && // but only if it is the current channel...
|
|
(messaging_got_CN != 0x03)) { // ...and if we don't have them already.
|
|
unlockMessaging();
|
|
SIevent *eptr = new SIevent(evt);
|
|
if (!eptr)
|
|
{
|
|
printf("[sectionsd::addEvent] new SIevent1 failed.\n");
|
|
return;
|
|
//throw std::bad_alloc();
|
|
}
|
|
|
|
SIeventPtr e(eptr);
|
|
|
|
writeLockEvents();
|
|
if (e->runningStatus() > 2) { // paused or currently running
|
|
if (!myCurrentEvent || (myCurrentEvent && (*myCurrentEvent).uniqueKey() != e->uniqueKey())) {
|
|
if (myCurrentEvent)
|
|
delete myCurrentEvent;
|
|
myCurrentEvent = new SIevent(evt);
|
|
writeLockMessaging();
|
|
messaging_got_CN |= 0x01;
|
|
if (myNextEvent && (*myNextEvent).uniqueKey() == e->uniqueKey()) {
|
|
dprintf("addevent-cn: removing next-event\n");
|
|
/* next got "promoted" to current => trigger re-read */
|
|
delete myNextEvent;
|
|
myNextEvent = NULL;
|
|
messaging_got_CN &= 0x01;
|
|
}
|
|
unlockMessaging();
|
|
dprintf("addevent-cn: added running (%d) event 0x%04x '%s'\n",
|
|
e->runningStatus(), e->eventID, e->getName().c_str());
|
|
} else {
|
|
writeLockMessaging();
|
|
messaging_got_CN |= 0x01;
|
|
unlockMessaging();
|
|
dprintf("addevent-cn: not add runn. (%d) event 0x%04x '%s'\n",
|
|
e->runningStatus(), e->eventID, e->getName().c_str());
|
|
}
|
|
} else {
|
|
if ((!myNextEvent || (myNextEvent && (*myNextEvent).uniqueKey() != e->uniqueKey() && (*myNextEvent).times.begin()->startzeit < e->times.begin()->startzeit)) &&
|
|
(!myCurrentEvent || (myCurrentEvent && (*myCurrentEvent).uniqueKey() != e->uniqueKey()))) {
|
|
if (myNextEvent)
|
|
delete myNextEvent;
|
|
myNextEvent = new SIevent(evt);
|
|
writeLockMessaging();
|
|
messaging_got_CN |= 0x02;
|
|
unlockMessaging();
|
|
dprintf("addevent-cn: added next (%d) event 0x%04x '%s'\n",
|
|
e->runningStatus(), e->eventID, e->getName().c_str());
|
|
} else {
|
|
dprintf("addevent-cn: not added next(%d) event 0x%04x '%s'\n",
|
|
e->runningStatus(), e->eventID, e->getName().c_str());
|
|
writeLockMessaging();
|
|
messaging_got_CN |= 0x02;
|
|
unlockMessaging();
|
|
}
|
|
}
|
|
unlockEvents();
|
|
} else
|
|
unlockMessaging();
|
|
}
|
|
|
|
readLockEvents();
|
|
MySIeventsOrderUniqueKey::iterator si = mySIeventsOrderUniqueKey.find(evt.uniqueKey());
|
|
bool already_exists = (si != mySIeventsOrderUniqueKey.end());
|
|
if (already_exists && (evt.table_id < si->second->table_id))
|
|
{
|
|
/* if the new event has a lower (== more recent) table ID, replace the old one */
|
|
already_exists = false;
|
|
dprintf("replacing event %016llx:%02x with %04x:%02x '%.40s'\n", si->second->uniqueKey(),
|
|
si->second->table_id, evt.eventID, evt.table_id, evt.getName().c_str());
|
|
}
|
|
else if (already_exists && ( (evt.table_id == 0x51 || evt.table_id == 0x50 || evt.table_id == 0x4e) && evt.table_id == si->second->table_id && evt.version != si->second->version ))
|
|
{
|
|
//replace event if new version
|
|
dprintf("replacing event version old 0x%02x new 0x%02x'\n", si->second->version, evt.version );
|
|
already_exists = false;
|
|
}
|
|
|
|
/* Check size of some descriptors of the new event before comparing
|
|
them with the old ones, because the same event can be complete
|
|
on one German Sky channel and incomplete on another one. So we
|
|
make sure to keep the complete event, if applicable. */
|
|
|
|
if (already_exists && (!evt.components.empty()) && (evt.components != si->second->components))
|
|
already_exists = false;
|
|
|
|
if (already_exists && (!evt.linkage_descs.empty()) && (evt.linkage_descs != si->second->linkage_descs))
|
|
already_exists = false;
|
|
|
|
if (already_exists && (!evt.ratings.empty()) && (evt.ratings != si->second->ratings))
|
|
already_exists = false;
|
|
|
|
if (already_exists && (evt.times != si->second->times))
|
|
already_exists = false;
|
|
|
|
if ((already_exists) && (SIlanguage::getMode() == CSectionsdClient::LANGUAGE_MODE_OFF)) {
|
|
si->second->contentClassification = evt.contentClassification;
|
|
si->second->userClassification = evt.userClassification;
|
|
si->second->itemDescription = evt.itemDescription;
|
|
si->second->item = evt.item;
|
|
si->second->vps = evt.vps;
|
|
if ((evt.getExtendedText().length() > 0) &&
|
|
(evt.times.begin()->startzeit < zeit + secondsExtendedTextCache))
|
|
si->second->setExtendedText("OFF",evt.getExtendedText().c_str());
|
|
if (evt.getText().length() > 0)
|
|
si->second->setText("OFF",evt.getText().c_str());
|
|
if (evt.getName().length() > 0)
|
|
si->second->setName("OFF",evt.getName().c_str());
|
|
}
|
|
else {
|
|
|
|
SIevent *eptr = new SIevent(evt);
|
|
|
|
if (!eptr)
|
|
{
|
|
printf("[sectionsd::addEvent] new SIevent failed.\n");
|
|
unlockEvents();
|
|
return;
|
|
// throw std::bad_alloc();
|
|
}
|
|
|
|
SIeventPtr e(eptr);
|
|
|
|
//Strip ExtendedDescription if too far in the future
|
|
if ((e->times.begin()->startzeit > zeit + secondsExtendedTextCache) &&
|
|
(SIlanguage::getMode() == CSectionsdClient::LANGUAGE_MODE_OFF) && (zeit != 0))
|
|
e->setExtendedText("OFF","");
|
|
|
|
/*
|
|
* this is test code, so indentation is deliberately wrong :-)
|
|
* we'll hopefully remove this if clause after testing is done
|
|
*/
|
|
if (slow_addevent)
|
|
{
|
|
std::vector<event_id_t> to_delete;
|
|
unsigned short eventID = e->eventID;
|
|
event_id_t e_key = e->uniqueKey();
|
|
t_channel_id e_chid = e->get_channel_id();
|
|
time_t start_time = e->times.begin()->startzeit;
|
|
time_t end_time = e->times.begin()->startzeit + (long)e->times.begin()->dauer;
|
|
/* create an event that's surely behind the one to check in the sort order */
|
|
e->eventID = 0xFFFF; /* lowest order sort criteria is eventID */
|
|
/* returns an iterator that's behind 'e' */
|
|
MySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey::iterator x =
|
|
mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.upper_bound(e);
|
|
e->eventID = eventID;
|
|
|
|
/* the first decrement of the iterator gives us an event that's a potential
|
|
* match *or* from a different channel, then no event for this channel is stored */
|
|
while (x != mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.begin())
|
|
{
|
|
--x;
|
|
if ((*x)->get_channel_id() != e_chid)
|
|
break;
|
|
else
|
|
{
|
|
event_id_t x_key = (*x)->uniqueKey();
|
|
/* do we need this check? */
|
|
if (x_key == e_key)
|
|
continue;
|
|
if ((*x)->times.begin()->startzeit >= end_time)
|
|
continue;
|
|
/* iterating backwards: if the endtime of the stored events
|
|
* is earlier than the starttime of the new one, we'll never
|
|
* find an identical one => bail out */
|
|
if ((*x)->times.begin()->startzeit + (long)(*x)->times.begin()->dauer <= start_time)
|
|
break;
|
|
/* here we have an overlapping event */
|
|
dprintf("%s: delete 0x%016llx.%02x time = 0x%016llx.%02x\n", __func__,
|
|
x_key, (*x)->table_id, e_key, e->table_id);
|
|
to_delete.push_back(x_key);
|
|
}
|
|
}
|
|
unlockEvents();
|
|
|
|
while (! to_delete.empty())
|
|
{
|
|
deleteEvent(to_delete.back());
|
|
to_delete.pop_back();
|
|
}
|
|
} else {
|
|
// Damit in den nicht nach Event-ID sortierten Mengen
|
|
// Mehrere Events mit gleicher ID sind, diese vorher loeschen
|
|
unlockEvents();
|
|
}
|
|
deleteEvent(e->uniqueKey());
|
|
readLockEvents();
|
|
if (mySIeventsOrderUniqueKey.size() >= max_events) {
|
|
MySIeventsOrderFirstEndTimeServiceIDEventUniqueKey::iterator lastEvent =
|
|
mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.begin();
|
|
|
|
/* if you don't want the new "delete old events first" method but
|
|
* the old-fashioned "delete future events always", invert this */
|
|
#if 0
|
|
bool back = true;
|
|
#else
|
|
time_t now = time(NULL);
|
|
bool back = false;
|
|
if ((*lastEvent)->times.size() == 1)
|
|
{
|
|
if ((*lastEvent)->times.begin()->startzeit + (long)(*lastEvent)->times.begin()->dauer >= now - oldEventsAre)
|
|
back = true;
|
|
} else
|
|
printf("[sectionsd] addevent: times.size != 1, please report\n");
|
|
#endif
|
|
if (back)
|
|
{
|
|
// fprintf(stderr, "<");
|
|
lastEvent = mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.end();
|
|
--lastEvent;
|
|
|
|
//preserve events of current channel
|
|
readLockMessaging();
|
|
while ((lastEvent != mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.begin()) &&
|
|
((*lastEvent)->get_channel_id() == messaging_current_servicekey)) {
|
|
--lastEvent;
|
|
}
|
|
unlockMessaging();
|
|
}
|
|
// else fprintf(stderr, ">");
|
|
unlockEvents();
|
|
deleteEvent((*lastEvent)->uniqueKey());
|
|
}
|
|
else
|
|
unlockEvents();
|
|
readLockEvents();
|
|
// Pruefen ob es ein Meta-Event ist
|
|
MySIeventUniqueKeysMetaOrderServiceUniqueKey::iterator i = mySIeventUniqueKeysMetaOrderServiceUniqueKey.find(e->get_channel_id());
|
|
|
|
if (i != mySIeventUniqueKeysMetaOrderServiceUniqueKey.end())
|
|
{
|
|
// ist ein MetaEvent, d.h. mit Zeiten fuer NVOD-Event
|
|
|
|
if (e->times.size())
|
|
{
|
|
// D.h. wir fuegen die Zeiten in das richtige Event ein
|
|
MySIeventsOrderUniqueKey::iterator ie = mySIeventsOrderUniqueKey.find(i->second);
|
|
|
|
if (ie != mySIeventsOrderUniqueKey.end())
|
|
{
|
|
|
|
// Event vorhanden
|
|
// Falls das Event in den beiden Mengen mit Zeiten nicht vorhanden
|
|
// ist, dieses dort einfuegen
|
|
MySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey::iterator i2 = mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.find(ie->second);
|
|
unlockEvents();
|
|
writeLockEvents();
|
|
|
|
if (i2 == mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.end())
|
|
{
|
|
// nicht vorhanden -> einfuegen
|
|
mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.insert(ie->second);
|
|
mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.insert(ie->second);
|
|
}
|
|
|
|
// Und die Zeiten im Event updaten
|
|
ie->second->times.insert(e->times.begin(), e->times.end());
|
|
}
|
|
}
|
|
}
|
|
unlockEvents();
|
|
writeLockEvents();
|
|
// printf("Adding: %04x\n", (int) e->uniqueKey());
|
|
|
|
// normales Event
|
|
mySIeventsOrderUniqueKey.insert(std::make_pair(e->uniqueKey(), e));
|
|
|
|
if (e->times.size())
|
|
{
|
|
// diese beiden Mengen enthalten nur Events mit Zeiten
|
|
mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.insert(e);
|
|
mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.insert(e);
|
|
}
|
|
}
|
|
unlockEvents();
|
|
}
|
|
|
|
static void addNVODevent(const SIevent &evt)
|
|
{
|
|
SIevent *eptr = new SIevent(evt);
|
|
|
|
if (!eptr)
|
|
{
|
|
printf("[sectionsd::addNVODevent] new SIevent failed.\n");
|
|
return;
|
|
//throw std::bad_alloc();
|
|
}
|
|
|
|
SIeventPtr e(eptr);
|
|
|
|
readLockEvents();
|
|
MySIeventsOrderUniqueKey::iterator e2 = mySIeventsOrderUniqueKey.find(e->uniqueKey());
|
|
|
|
if (e2 != mySIeventsOrderUniqueKey.end())
|
|
{
|
|
// bisher gespeicherte Zeiten retten
|
|
unlockEvents();
|
|
writeLockEvents();
|
|
e->times.insert(e2->second->times.begin(), e2->second->times.end());
|
|
}
|
|
unlockEvents();
|
|
|
|
// Damit in den nicht nach Event-ID sortierten Mengen
|
|
// mehrere Events mit gleicher ID sind, diese vorher loeschen
|
|
deleteEvent(e->uniqueKey());
|
|
readLockEvents();
|
|
if (mySIeventsOrderUniqueKey.size() >= max_events) {
|
|
//FIXME: Set Old Events to 0 if limit is reached...
|
|
MySIeventsOrderFirstEndTimeServiceIDEventUniqueKey::iterator lastEvent =
|
|
mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.end();
|
|
lastEvent--;
|
|
|
|
//preserve events of current channel
|
|
readLockMessaging();
|
|
while ((lastEvent != mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.begin()) &&
|
|
((*lastEvent)->get_channel_id() == messaging_current_servicekey)) {
|
|
--lastEvent;
|
|
}
|
|
unlockMessaging();
|
|
unlockEvents();
|
|
deleteEvent((*lastEvent)->uniqueKey());
|
|
}
|
|
else
|
|
unlockEvents();
|
|
writeLockEvents();
|
|
mySIeventsOrderUniqueKey.insert(std::make_pair(e->uniqueKey(), e));
|
|
|
|
mySIeventsNVODorderUniqueKey.insert(std::make_pair(e->uniqueKey(), e));
|
|
unlockEvents();
|
|
if (e->times.size())
|
|
{
|
|
// diese beiden Mengen enthalten nur Events mit Zeiten
|
|
writeLockEvents();
|
|
mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.insert(e);
|
|
mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.insert(e);
|
|
unlockEvents();
|
|
}
|
|
}
|
|
|
|
static void removeOldEvents(const long seconds)
|
|
{
|
|
bool goodtimefound;
|
|
std::vector<event_id_t> to_delete;
|
|
|
|
// Alte events loeschen
|
|
time_t zeit = time(NULL);
|
|
|
|
readLockEvents();
|
|
|
|
MySIeventsOrderFirstEndTimeServiceIDEventUniqueKey::iterator e = mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.begin();
|
|
|
|
while ((e != mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.end()) && (!messaging_zap_detected)) {
|
|
goodtimefound = false;
|
|
for (SItimes::iterator t = (*e)->times.begin(); t != (*e)->times.end(); ++t) {
|
|
if (t->startzeit + (long)t->dauer >= zeit - seconds) {
|
|
goodtimefound=true;
|
|
// one time found -> exit times loop
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (false == goodtimefound)
|
|
to_delete.push_back((*e)->uniqueKey());
|
|
++e;
|
|
}
|
|
unlockEvents();
|
|
|
|
for (std::vector<event_id_t>::iterator i = to_delete.begin(); i != to_delete.end(); ++i)
|
|
deleteEvent(*i);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* communication with sectionsdclient:
|
|
*/
|
|
|
|
inline bool readNbytes(int fd, char *buf, const size_t numberOfBytes, const time_t timeoutInSeconds)
|
|
{
|
|
timeval timeout;
|
|
timeout.tv_sec = timeoutInSeconds;
|
|
timeout.tv_usec = 0;
|
|
return receive_data(fd, buf, numberOfBytes, timeout);
|
|
}
|
|
|
|
inline bool writeNbytes(int fd, const char *buf, const size_t numberOfBytes, const time_t timeoutInSeconds)
|
|
{
|
|
timeval timeout;
|
|
timeout.tv_sec = timeoutInSeconds;
|
|
timeout.tv_usec = 0;
|
|
return send_data(fd, buf, numberOfBytes, timeout);
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------
|
|
// misc. functions
|
|
//------------------------------------------------------------
|
|
static const SIevent& findSIeventForEventUniqueKey(const event_id_t eventUniqueKey)
|
|
{
|
|
// Event (eventid) suchen
|
|
MySIeventsOrderUniqueKey::iterator e = mySIeventsOrderUniqueKey.find(eventUniqueKey);
|
|
|
|
if (e != mySIeventsOrderUniqueKey.end())
|
|
return *(e->second);
|
|
|
|
return nullEvt;
|
|
}
|
|
|
|
static const SIevent& findActualSIeventForServiceUniqueKey(const t_channel_id serviceUniqueKey, SItime& zeit, long plusminus = 0, unsigned *flag = 0)
|
|
{
|
|
time_t azeit = time(NULL);
|
|
|
|
if (flag != 0)
|
|
*flag = 0;
|
|
|
|
for (MySIeventsOrderFirstEndTimeServiceIDEventUniqueKey::iterator e = mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.begin(); e != mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.end(); ++e)
|
|
if ((*e)->get_channel_id() == serviceUniqueKey)
|
|
{
|
|
if (flag != 0)
|
|
*flag |= CSectionsdClient::epgflags::has_anything; // berhaupt was da...
|
|
|
|
// for (SItimes::reverse_iterator t = (*e)->times.rend(); t != (*e)->times.rbegin(); t--) {
|
|
for (SItimes::iterator t = (*e)->times.begin(); t != (*e)->times.end(); ++t) {
|
|
if ((long)(azeit + plusminus) < (long)(t->startzeit + t->dauer))
|
|
{
|
|
if (flag != 0)
|
|
*flag |= CSectionsdClient::epgflags::has_later; // later events are present...
|
|
|
|
if (t->startzeit <= (long)(azeit + plusminus))
|
|
{
|
|
//printf("azeit %d, startzeit+t->dauer %d \n", azeit, (long)(t->startzeit+t->dauer) );
|
|
|
|
if (flag != 0)
|
|
*flag |= CSectionsdClient::epgflags::has_current; // aktuelles event da...
|
|
|
|
zeit = *t;
|
|
|
|
return *(*e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullEvt;
|
|
}
|
|
|
|
static const SIevent& findNextSIeventForServiceUniqueKey(const t_channel_id serviceUniqueKey, SItime& zeit)
|
|
{
|
|
time_t azeit = time(NULL);
|
|
|
|
for (MySIeventsOrderFirstEndTimeServiceIDEventUniqueKey::iterator e = mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.begin(); e != mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.end(); ++e)
|
|
if ((*e)->get_channel_id() == serviceUniqueKey)
|
|
{
|
|
for (SItimes::iterator t = (*e)->times.begin(); t != (*e)->times.end(); ++t)
|
|
if ((long)(azeit) < (long)(t->startzeit + t->dauer))
|
|
{
|
|
zeit = *t;
|
|
return *(*e);
|
|
}
|
|
}
|
|
|
|
return nullEvt;
|
|
}
|
|
|
|
// Sucht das naechste Event anhand unique key und Startzeit
|
|
static const SIevent &findNextSIevent(const event_id_t uniqueKey, SItime &zeit)
|
|
{
|
|
MySIeventsOrderUniqueKey::iterator eFirst = mySIeventsOrderUniqueKey.find(uniqueKey);
|
|
|
|
if (eFirst != mySIeventsOrderUniqueKey.end())
|
|
{
|
|
SItimes::iterator nextnvodtimes = eFirst->second->times.end();
|
|
SItimes::iterator nexttimes = eFirst->second->times.end();
|
|
|
|
if (eFirst->second->times.size() > 1)
|
|
{
|
|
//find next nvod
|
|
nextnvodtimes = eFirst->second->times.begin();
|
|
while ( nextnvodtimes != eFirst->second->times.end() ) {
|
|
if ( nextnvodtimes->startzeit == zeit.startzeit )
|
|
break;
|
|
else
|
|
++nextnvodtimes;
|
|
}
|
|
}
|
|
|
|
MySIeventsOrderFirstEndTimeServiceIDEventUniqueKey::iterator eNext;
|
|
|
|
//if ((nextnvodtimes != eFirst->second->times.begin()) && (nextnvodtimes != eFirst->second->times.end())) {
|
|
//Startzeit not first - we can't use the ordered list...
|
|
for (MySIeventsOrderFirstEndTimeServiceIDEventUniqueKey::iterator e = mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.begin(); e !=
|
|
mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.end(); ++e ) {
|
|
if ((*e)->get_channel_id() == eFirst->second->get_channel_id()) {
|
|
for (SItimes::iterator t = (*e)->times.begin(); t != (*e)->times.end(); ++t) {
|
|
if (t->startzeit > zeit.startzeit) {
|
|
if (nexttimes != eFirst->second->times.end()) {
|
|
if (t->startzeit < nexttimes->startzeit) {
|
|
eNext = e;
|
|
nexttimes = t;
|
|
}
|
|
}
|
|
else {
|
|
eNext = e;
|
|
nexttimes = t;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* } else {
|
|
//find next normal
|
|
eNext = mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.find(eFirst->second);
|
|
eNext++;
|
|
|
|
if (eNext != mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.end())
|
|
{
|
|
if ((*eNext)->get_channel_id() == eFirst->second->get_channel_id())
|
|
nexttimes = (*eNext)->times.begin();
|
|
}
|
|
}
|
|
*/
|
|
if (nextnvodtimes != eFirst->second->times.end())
|
|
++nextnvodtimes;
|
|
//Compare
|
|
if (nexttimes != eFirst->second->times.end()) {
|
|
if (nextnvodtimes != eFirst->second->times.end()) {
|
|
//both times are set - take the first
|
|
if (nexttimes->startzeit < nextnvodtimes->startzeit) {
|
|
zeit = *nexttimes;
|
|
return *(*eNext);
|
|
|
|
} else {
|
|
zeit = *nextnvodtimes;
|
|
return *(eFirst->second);
|
|
}
|
|
} else {
|
|
//only nexttimes set
|
|
zeit = *nexttimes;
|
|
return *(*eNext);
|
|
}
|
|
} else if (nextnvodtimes != eFirst->second->times.end()) {
|
|
//only nextnvodtimes set
|
|
zeit = *nextnvodtimes;
|
|
return *(eFirst->second);
|
|
}
|
|
}
|
|
|
|
return nullEvt;
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// connection-thread
|
|
// handles incoming requests
|
|
//---------------------------------------------------------------------
|
|
|
|
static void commandPauseScanning(int connfd, char *data, const unsigned dataLength)
|
|
{
|
|
if (dataLength != 4)
|
|
return ;
|
|
|
|
int pause = *(int *)data;
|
|
|
|
if (pause && pause != 1)
|
|
return ;
|
|
|
|
dprintf("Request of %s scanning.\n", pause ? "stop" : "continue" );
|
|
|
|
if (scanning && pause)
|
|
{
|
|
dmxCN.request_pause();
|
|
dmxEIT.request_pause();
|
|
#ifdef ENABLE_FREESATEPG
|
|
dmxFSEIT.request_pause();
|
|
#endif
|
|
#ifdef ENABLE_SDT
|
|
dmxSDT.request_pause();
|
|
#endif
|
|
scanning = 0;
|
|
}
|
|
else if (!pause && !scanning)
|
|
{
|
|
dmxCN.request_unpause();
|
|
dmxEIT.request_unpause();
|
|
#ifdef ENABLE_FREESATEPG
|
|
dmxFSEIT.request_unpause();
|
|
#endif
|
|
#ifdef ENABLE_SDT
|
|
dmxSDT.request_unpause();
|
|
#endif
|
|
writeLockEvents();
|
|
if (myCurrentEvent) {
|
|
delete myCurrentEvent;
|
|
myCurrentEvent = NULL;
|
|
}
|
|
if (myNextEvent) {
|
|
delete myNextEvent;
|
|
myNextEvent = NULL;
|
|
}
|
|
unlockEvents();
|
|
writeLockMessaging();
|
|
messaging_have_CN = 0x00;
|
|
messaging_got_CN = 0x00;
|
|
unlockMessaging();
|
|
scanning = 1;
|
|
if (!bTimeCorrect && !ntpenable)
|
|
{
|
|
pthread_mutex_lock(&timeThreadSleepMutex);
|
|
pthread_cond_broadcast(&timeThreadSleepCond);
|
|
pthread_mutex_unlock(&timeThreadSleepMutex);
|
|
}
|
|
|
|
scanning = 1;
|
|
dmxCN.change(0);
|
|
dmxEIT.change(0);
|
|
#ifdef ENABLE_FREESATEPG
|
|
dmxFSEIT.change(0);
|
|
#endif
|
|
#ifdef ENABLE_SDT
|
|
dmxSDT.change(0);
|
|
#endif
|
|
}
|
|
|
|
struct sectionsd::msgResponseHeader msgResponse;
|
|
|
|
msgResponse.dataLength = 0;
|
|
|
|
writeNbytes(connfd, (const char *)&msgResponse, sizeof(msgResponse), WRITE_TIMEOUT_IN_SECONDS);
|
|
|
|
return ;
|
|
}
|
|
|
|
static void commandGetIsScanningActive(int connfd, char* /*data*/, const unsigned /*dataLength*/)
|
|
{
|
|
struct sectionsd::msgResponseHeader responseHeader;
|
|
|
|
responseHeader.dataLength = sizeof(scanning);
|
|
|
|
if (writeNbytes(connfd, (const char *)&responseHeader, sizeof(responseHeader), WRITE_TIMEOUT_IN_SECONDS) == true)
|
|
{
|
|
writeNbytes(connfd, (const char *)&scanning, responseHeader.dataLength, WRITE_TIMEOUT_IN_SECONDS);
|
|
}
|
|
else
|
|
dputs("[sectionsd] Fehler/Timeout bei write");
|
|
}
|
|
|
|
static void sendAllEvents(int connfd, t_channel_id serviceUniqueKey, bool oldFormat = true, char search = 0, std::string search_text = "")
|
|
{
|
|
#define MAX_SIZE_EVENTLIST 64*1024
|
|
char *evtList = new char[MAX_SIZE_EVENTLIST]; // 64kb should be enough and dataLength is unsigned short
|
|
char *liste;
|
|
long count=0;
|
|
struct sectionsd::msgResponseHeader responseHeader;
|
|
responseHeader.dataLength = 0;
|
|
// int laststart = 0;
|
|
|
|
if (!evtList)
|
|
{
|
|
fprintf(stderr, "low on memory!\n");
|
|
goto out;
|
|
}
|
|
|
|
dprintf("sendAllEvents for " PRINTF_CHANNEL_ID_TYPE "\n", serviceUniqueKey);
|
|
*evtList = 0;
|
|
liste = evtList;
|
|
|
|
if (serviceUniqueKey != 0)
|
|
{
|
|
// service Found
|
|
readLockEvents();
|
|
int serviceIDfound = 0;
|
|
|
|
if (search_text.length()) std::transform(search_text.begin(), search_text.end(), search_text.begin(), tolower);
|
|
for (MySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey::iterator e = mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.begin(); e != mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.end(); ++e)
|
|
{
|
|
if ((*e)->get_channel_id() == serviceUniqueKey)
|
|
{
|
|
serviceIDfound = 1;
|
|
|
|
bool copy = true;
|
|
if(search == 0); // nothing to do here
|
|
else if(search == 1)
|
|
{
|
|
std::string eName = (*e)->getName();
|
|
std::transform(eName.begin(), eName.end(), eName.begin(), tolower);
|
|
if(eName.find(search_text) == std::string::npos)
|
|
copy = false;
|
|
}
|
|
else if(search == 2)
|
|
{
|
|
std::string eText = (*e)->getText();
|
|
std::transform(eText.begin(), eText.end(), eText.begin(), tolower);
|
|
if(eText.find(search_text) == std::string::npos)
|
|
copy = false;
|
|
}
|
|
else if(search == 3)
|
|
{
|
|
std::string eExtendedText = (*e)->getExtendedText();
|
|
std::transform(eExtendedText.begin(), eExtendedText.end(), eExtendedText.begin(), tolower);
|
|
if(eExtendedText.find(search_text) == std::string::npos)
|
|
copy = false;
|
|
}
|
|
|
|
if(copy)
|
|
{
|
|
for (SItimes::iterator t = (*e)->times.begin(); t != (*e)->times.end(); ++t)
|
|
{
|
|
// if (t->startzeit > laststart) {
|
|
// laststart = t->startzeit;
|
|
if ( oldFormat )
|
|
{
|
|
#define MAX_SIZE_STRTIME 50
|
|
char strZeit[MAX_SIZE_STRTIME];
|
|
char strZeit2[MAX_SIZE_STRTIME];
|
|
struct tm *tmZeit;
|
|
|
|
tmZeit = localtime(&(t->startzeit));
|
|
count += snprintf(strZeit, MAX_SIZE_STRTIME, "%012llx ", (*e)->uniqueKey());
|
|
count += snprintf(strZeit2, MAX_SIZE_STRTIME, "%02d.%02d %02d:%02d %u ",
|
|
tmZeit->tm_mday, tmZeit->tm_mon + 1, tmZeit->tm_hour, tmZeit->tm_min, (*e)->times.begin()->dauer / 60);
|
|
count += (*e)->getName().length() + 1;
|
|
|
|
if (count < MAX_SIZE_EVENTLIST) {
|
|
strcat(liste, strZeit);
|
|
strcat(liste, strZeit2);
|
|
strcat(liste, (*e)->getName().c_str());
|
|
strcat(liste, "\n");
|
|
} else {
|
|
dprintf("warning: sendAllEvents eventlist cut\n");
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
count += sizeof(event_id_t) + 4 + 4 + (*e)->getName().length() + 1;
|
|
if (((*e)->getText()).empty())
|
|
{
|
|
count += (*e)->getExtendedText().substr(0, 50).length();
|
|
}
|
|
else
|
|
{
|
|
count += (*e)->getText().length();
|
|
}
|
|
count++;
|
|
|
|
if (count < MAX_SIZE_EVENTLIST) {
|
|
*((event_id_t *)liste) = (*e)->uniqueKey();
|
|
liste += sizeof(event_id_t);
|
|
*((unsigned *)liste) = t->startzeit;
|
|
liste += 4;
|
|
*((unsigned *)liste) = t->dauer;
|
|
liste += 4;
|
|
strcpy(liste, (*e)->getName().c_str());
|
|
liste += (*e)->getName().length();
|
|
liste++;
|
|
|
|
if (((*e)->getText()).empty())
|
|
{
|
|
strcpy(liste, (*e)->getExtendedText().substr(0, 50).c_str());
|
|
liste += strlen(liste);
|
|
}
|
|
else
|
|
{
|
|
strcpy(liste, (*e)->getText().c_str());
|
|
liste += (*e)->getText().length();
|
|
}
|
|
liste++;
|
|
} else {
|
|
dprintf("warning: sendAllEvents eventlist cut\n");
|
|
break;
|
|
}
|
|
}
|
|
// }
|
|
}
|
|
} // if = serviceID
|
|
}
|
|
else if ( serviceIDfound )
|
|
break; // sind nach serviceID und startzeit sortiert -> nicht weiter suchen
|
|
}
|
|
|
|
unlockEvents();
|
|
}
|
|
|
|
//printf("warning: [sectionsd] all events - response-size: 0x%x, count = %lx\n", liste - evtList, count);
|
|
if (liste - evtList > MAX_SIZE_EVENTLIST)
|
|
printf("warning: [sectionsd] all events - response-size: 0x%x\n", liste - evtList);
|
|
responseHeader.dataLength = liste - evtList;
|
|
|
|
dprintf("[sectionsd] all events - response-size: 0x%x\n", responseHeader.dataLength);
|
|
|
|
if ( responseHeader.dataLength == 1 )
|
|
responseHeader.dataLength = 0;
|
|
|
|
out:
|
|
if (writeNbytes(connfd, (const char *)&responseHeader, sizeof(responseHeader), WRITE_TIMEOUT_IN_SECONDS) == true)
|
|
{
|
|
if (responseHeader.dataLength)
|
|
writeNbytes(connfd, evtList, responseHeader.dataLength, WRITE_TIMEOUT_IN_SECONDS);
|
|
}
|
|
else
|
|
dputs("[sectionsd] Fehler/Timeout bei write");
|
|
|
|
if (evtList)
|
|
delete[] evtList;
|
|
|
|
return ;
|
|
}
|
|
|
|
static void commandAllEventsChannelID(int connfd, char *data, const unsigned dataLength)
|
|
{
|
|
if (dataLength != sizeof(t_channel_id))
|
|
return ;
|
|
|
|
t_channel_id serviceUniqueKey = *(t_channel_id *)data;
|
|
|
|
dprintf("Request of all events for " PRINTF_CHANNEL_ID_TYPE "\n", serviceUniqueKey);
|
|
|
|
sendAllEvents(connfd, serviceUniqueKey, false);
|
|
|
|
return ;
|
|
}
|
|
|
|
static void commandDumpStatusInformation(int /*connfd*/, char* /*data*/, const unsigned /*dataLength*/)
|
|
{
|
|
dputs("Request of status information");
|
|
|
|
readLockEvents();
|
|
|
|
unsigned anzEvents = mySIeventsOrderUniqueKey.size();
|
|
|
|
unsigned anzNVODevents = mySIeventsNVODorderUniqueKey.size();
|
|
|
|
unsigned anzMetaServices = mySIeventUniqueKeysMetaOrderServiceUniqueKey.size();
|
|
|
|
unlockEvents();
|
|
|
|
readLockServices();
|
|
|
|
unsigned anzServices = mySIservicesOrderUniqueKey.size();
|
|
|
|
unsigned anzNVODservices = mySIservicesNVODorderUniqueKey.size();
|
|
|
|
// unsigned anzServices=services.size();
|
|
unlockServices();
|
|
|
|
struct mallinfo speicherinfo = mallinfo();
|
|
|
|
// struct rusage resourceUsage;
|
|
// getrusage(RUSAGE_CHILDREN, &resourceUsage);
|
|
// getrusage(RUSAGE_SELF, &resourceUsage);
|
|
time_t zeit = time(NULL);
|
|
|
|
#define MAX_SIZE_STATI 2024
|
|
char stati[MAX_SIZE_STATI];
|
|
|
|
snprintf(stati, MAX_SIZE_STATI,
|
|
"$Id: sectionsd.cpp,v 1.305 2009/07/30 12:41:39 seife Exp $\n"
|
|
"Current time: %s"
|
|
"Hours to cache: %ld\n"
|
|
"Hours to cache extended text: %ld\n"
|
|
"Events are old %ldmin after their end time\n"
|
|
"Number of cached services: %u\n"
|
|
"Number of cached nvod-services: %u\n"
|
|
"Number of cached events: %u\n"
|
|
"Number of cached nvod-events: %u\n"
|
|
"Number of cached meta-services: %u\n"
|
|
// "Resource-usage: maxrss: %ld ixrss: %ld idrss: %ld isrss: %ld\n"
|
|
"Total size of memory occupied by chunks\n"
|
|
"handed out by malloc: %d (%dkb)\n"
|
|
"Total bytes memory allocated with `sbrk' by malloc,\n"
|
|
"in bytes: %d (%dkb)\n"
|
|
#ifdef ENABLE_FREESATEPG
|
|
"FreeSat enabled\n"
|
|
#else
|
|
""
|
|
#endif
|
|
,ctime(&zeit),
|
|
secondsToCache / (60*60L), secondsExtendedTextCache / (60*60L), oldEventsAre / 60, anzServices, anzNVODservices, anzEvents, anzNVODevents, anzMetaServices,
|
|
// resourceUsage.ru_maxrss, resourceUsage.ru_ixrss, resourceUsage.ru_idrss, resourceUsage.ru_isrss,
|
|
speicherinfo.uordblks, speicherinfo.uordblks / 1024,
|
|
speicherinfo.arena, speicherinfo.arena / 1024
|
|
);
|
|
printf("%s\n", stati);
|
|
return ;
|
|
}
|
|
|
|
static void commandComponentTagsUniqueKey(int connfd, char *data, const unsigned dataLength)
|
|
{
|
|
int nResultDataSize = 0;
|
|
char *pResultData = 0;
|
|
char *p;
|
|
struct sectionsd::msgResponseHeader responseHeader;
|
|
responseHeader.dataLength = 0;
|
|
MySIeventsOrderUniqueKey::iterator eFirst;
|
|
|
|
if (dataLength != 8)
|
|
return ;
|
|
|
|
event_id_t uniqueKey = *(event_id_t *)data;
|
|
|
|
dprintf("Request of ComponentTags for 0x%llx\n", uniqueKey);
|
|
|
|
readLockEvents();
|
|
|
|
nResultDataSize = sizeof(int); // num. Component-Tags
|
|
|
|
eFirst = mySIeventsOrderUniqueKey.find(uniqueKey);
|
|
|
|
if (eFirst != mySIeventsOrderUniqueKey.end())
|
|
{
|
|
//Found
|
|
dprintf("ComponentTags found.\n");
|
|
dprintf("components.size %d \n", eFirst->second->components.size());
|
|
|
|
for (SIcomponents::iterator cmp = eFirst->second->components.begin(); cmp != eFirst->second->components.end(); ++cmp)
|
|
{
|
|
dprintf(" %s \n", cmp->component.c_str());
|
|
nResultDataSize += cmp->component.length() + 1 + // name
|
|
sizeof(unsigned char) + // componentType
|
|
sizeof(unsigned char) + // componentTag
|
|
sizeof(unsigned char); // streamContent
|
|
}
|
|
}
|
|
|
|
pResultData = new char[nResultDataSize];
|
|
|
|
if (!pResultData)
|
|
{
|
|
fprintf(stderr, "low on memory!\n");
|
|
unlockEvents();
|
|
goto out;
|
|
}
|
|
|
|
p = pResultData;
|
|
|
|
if (eFirst != mySIeventsOrderUniqueKey.end())
|
|
{
|
|
*((int *)p) = eFirst->second->components.size();
|
|
p += sizeof(int);
|
|
|
|
for (SIcomponents::iterator cmp = eFirst->second->components.begin(); cmp != eFirst->second->components.end(); ++cmp)
|
|
{
|
|
|
|
strcpy(p, cmp->component.c_str());
|
|
p += cmp->component.length() + 1;
|
|
*((unsigned char *)p) = cmp->componentType;
|
|
p += sizeof(unsigned char);
|
|
*((unsigned char *)p) = cmp->componentTag;
|
|
p += sizeof(unsigned char);
|
|
*((unsigned char *)p) = cmp->streamContent;
|
|
p += sizeof(unsigned char);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*((int *)p) = 0;
|
|
p += sizeof(int);
|
|
}
|
|
|
|
unlockEvents();
|
|
|
|
responseHeader.dataLength = nResultDataSize;
|
|
|
|
out:
|
|
if (writeNbytes(connfd, (const char *)&responseHeader, sizeof(responseHeader), WRITE_TIMEOUT_IN_SECONDS) == true)
|
|
{
|
|
if (responseHeader.dataLength)
|
|
writeNbytes(connfd, pResultData, responseHeader.dataLength, WRITE_TIMEOUT_IN_SECONDS);
|
|
}
|
|
else
|
|
dputs("[sectionsd] Fehler/Timeout bei write");
|
|
|
|
if (pResultData)
|
|
delete[] pResultData;
|
|
|
|
return ;
|
|
}
|
|
|
|
static void commandLinkageDescriptorsUniqueKey(int connfd, char *data, const unsigned dataLength)
|
|
{
|
|
int nResultDataSize = 0;
|
|
char *pResultData = 0;
|
|
char *p;
|
|
MySIeventsOrderUniqueKey::iterator eFirst;
|
|
int countDescs = 0;
|
|
struct sectionsd::msgResponseHeader responseHeader;
|
|
responseHeader.dataLength = 0;
|
|
event_id_t uniqueKey;
|
|
|
|
if (dataLength != 8)
|
|
goto out;
|
|
|
|
uniqueKey = *(event_id_t *)data;
|
|
|
|
dprintf("Request of LinkageDescriptors for 0x%llx\n", uniqueKey);
|
|
|
|
readLockEvents();
|
|
|
|
nResultDataSize = sizeof(int); // num. Component-Tags
|
|
|
|
eFirst = mySIeventsOrderUniqueKey.find(uniqueKey);
|
|
|
|
if (eFirst != mySIeventsOrderUniqueKey.end())
|
|
{
|
|
//Found
|
|
dprintf("LinkageDescriptors found.\n");
|
|
dprintf("linkage_descs.size %d \n", eFirst->second->linkage_descs.size());
|
|
|
|
|
|
for (SIlinkage_descs::iterator linkage_desc = eFirst->second->linkage_descs.begin(); linkage_desc != eFirst->second->linkage_descs.end(); ++linkage_desc)
|
|
{
|
|
if (linkage_desc->linkageType == 0xB0)
|
|
{
|
|
countDescs++;
|
|
dprintf(" %s \n", linkage_desc->name.c_str());
|
|
nResultDataSize += linkage_desc->name.length() + 1 + // name
|
|
sizeof(t_transport_stream_id) + //transportStreamId
|
|
sizeof(t_original_network_id) + //originalNetworkId
|
|
sizeof(t_service_id); //serviceId
|
|
}
|
|
}
|
|
}
|
|
|
|
pResultData = new char[nResultDataSize];
|
|
|
|
if (!pResultData)
|
|
{
|
|
fprintf(stderr, "low on memory!\n");
|
|
unlockEvents();
|
|
goto out;
|
|
}
|
|
|
|
p = pResultData;
|
|
|
|
*((int *)p) = countDescs;
|
|
p += sizeof(int);
|
|
|
|
if (eFirst != mySIeventsOrderUniqueKey.end())
|
|
{
|
|
for (SIlinkage_descs::iterator linkage_desc = eFirst->second->linkage_descs.begin(); linkage_desc != eFirst->second->linkage_descs.end(); ++linkage_desc)
|
|
{
|
|
if (linkage_desc->linkageType == 0xB0)
|
|
{
|
|
strcpy(p, linkage_desc->name.c_str());
|
|
p += linkage_desc->name.length() + 1;
|
|
*((t_transport_stream_id *)p) = linkage_desc->transportStreamId;
|
|
p += sizeof(t_transport_stream_id);
|
|
*((t_original_network_id *)p) = linkage_desc->originalNetworkId;
|
|
p += sizeof(t_original_network_id);
|
|
*((t_service_id *)p) = linkage_desc->serviceId;
|
|
p += sizeof(t_service_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
unlockEvents();
|
|
|
|
responseHeader.dataLength = nResultDataSize;
|
|
|
|
out:
|
|
if (writeNbytes(connfd, (const char *)&responseHeader, sizeof(responseHeader), WRITE_TIMEOUT_IN_SECONDS) == true)
|
|
{
|
|
if (responseHeader.dataLength)
|
|
writeNbytes(connfd, pResultData, responseHeader.dataLength, WRITE_TIMEOUT_IN_SECONDS);
|
|
}
|
|
else
|
|
dputs("[sectionsd] Fehler/Timeout bei write");
|
|
|
|
if (pResultData)
|
|
delete[] pResultData;
|
|
|
|
return ;
|
|
}
|
|
/* messaging_eit_is_busy does not need locking, it is only written to from CN-Thread */
|
|
static bool messaging_eit_is_busy = false;
|
|
static bool messaging_need_eit_version = false;
|
|
|
|
std::string epg_dir("");
|
|
|
|
static void commandserviceChanged(int connfd, char *data, const unsigned dataLength)
|
|
{
|
|
t_channel_id *uniqueServiceKey;
|
|
if (dataLength != sizeof(sectionsd::commandSetServiceChanged))
|
|
goto out;
|
|
|
|
uniqueServiceKey = &(((sectionsd::commandSetServiceChanged *)data)->channel_id);
|
|
|
|
dprintf("[sectionsd] commandserviceChanged: Service changed to " PRINTF_CHANNEL_ID_TYPE "\n", *uniqueServiceKey);
|
|
|
|
messaging_last_requested = time_monotonic();
|
|
|
|
if(checkBlacklist(*uniqueServiceKey))
|
|
{
|
|
if (!channel_is_blacklisted) {
|
|
channel_is_blacklisted = true;
|
|
dmxCN.request_pause();
|
|
dmxEIT.request_pause();
|
|
#ifdef ENABLE_SDT
|
|
dmxSDT.request_pause();
|
|
#endif
|
|
}
|
|
xprintf("[sectionsd] commandserviceChanged: service is filtered!\n");
|
|
}
|
|
else
|
|
{
|
|
if (channel_is_blacklisted) {
|
|
channel_is_blacklisted = false;
|
|
dmxCN.request_unpause();
|
|
dmxEIT.request_unpause();
|
|
#ifdef ENABLE_SDT
|
|
dmxSDT.request_unpause();
|
|
#endif
|
|
xprintf("[sectionsd] commandserviceChanged: service is no longer filtered!\n");
|
|
}
|
|
}
|
|
|
|
if(checkNoDVBTimelist(*uniqueServiceKey))
|
|
{
|
|
if (dvb_time_update) {
|
|
dvb_time_update = false;
|
|
}
|
|
xprintf("[sectionsd] commandserviceChanged: DVB time update is blocked!\n");
|
|
}
|
|
else
|
|
{
|
|
if (!dvb_time_update) {
|
|
dvb_time_update = true;
|
|
xprintf("[sectionsd] commandserviceChanged: DVB time update is allowed!\n");
|
|
}
|
|
}
|
|
|
|
if (messaging_current_servicekey != *uniqueServiceKey)
|
|
{
|
|
//if (debug) showProfiling("[sectionsd] commandserviceChanged: before events lock");
|
|
writeLockEvents();
|
|
//if (debug) showProfiling("[sectionsd] commandserviceChanged: after events lock");
|
|
if (myCurrentEvent) {
|
|
delete myCurrentEvent;
|
|
myCurrentEvent = NULL;
|
|
}
|
|
if (myNextEvent) {
|
|
delete myNextEvent;
|
|
myNextEvent = NULL;
|
|
}
|
|
unlockEvents();
|
|
writeLockMessaging();
|
|
messaging_current_servicekey = *uniqueServiceKey;
|
|
messaging_have_CN = 0x00;
|
|
messaging_got_CN = 0x00;
|
|
messaging_zap_detected = true;
|
|
messaging_need_eit_version = false;
|
|
unlockMessaging();
|
|
dmxCN.setCurrentService(messaging_current_servicekey & 0xffff);
|
|
dmxEIT.setCurrentService(messaging_current_servicekey & 0xffff);
|
|
#ifdef ENABLE_FREESATEPG
|
|
dmxFSEIT.setCurrentService(messaging_current_servicekey & 0xffff);
|
|
#endif
|
|
#ifdef ENABLE_SDT
|
|
dmxSDT.setCurrentService(messaging_current_servicekey & 0xffff);
|
|
#endif
|
|
}
|
|
else
|
|
dprintf("[sectionsd] commandserviceChanged: no change...\n");
|
|
|
|
out:
|
|
struct sectionsd::msgResponseHeader msgResponse;
|
|
msgResponse.dataLength = 0;
|
|
writeNbytes(connfd, (const char *)&msgResponse, sizeof(msgResponse), WRITE_TIMEOUT_IN_SECONDS);
|
|
|
|
dprintf("[sectionsd] commandserviceChanged: END!!\n");
|
|
return ;
|
|
}
|
|
|
|
/* send back the current and next event for the channel id passed to it
|
|
* Works like that:
|
|
* - if the currently running program is requested, return myCurrentEvent and myNextEvent,
|
|
* if they are present (filled in by cnThread)
|
|
* - if one or both of those are not present, or if a different program than the currently
|
|
* running is requested, search the missing events in the list of events gathered by the
|
|
* EIT and PPT threads, based on the current time.
|
|
*
|
|
* TODO: the handling of "flag" should be vastly simplified.
|
|
*/
|
|
static void commandCurrentNextInfoChannelID(int connfd, char *data, const unsigned dataLength)
|
|
{
|
|
int nResultDataSize = 0;
|
|
char* pResultData = 0;
|
|
char* p;
|
|
SIevent currentEvt;
|
|
SIevent nextEvt;
|
|
unsigned flag = 0, flag2=0;
|
|
/* ugly hack: retry fetching current/next by restarting dmxCN if this is true */
|
|
bool change = false;
|
|
struct sectionsd::msgResponseHeader pmResponse;
|
|
|
|
t_channel_id * uniqueServiceKey = (t_channel_id *)data;
|
|
|
|
if (dataLength != sizeof(t_channel_id))
|
|
goto out;
|
|
|
|
dprintf("[sectionsd] Request of current/next information for " PRINTF_CHANNEL_ID_TYPE "\n", *uniqueServiceKey);
|
|
|
|
readLockEvents();
|
|
/* if the currently running program is requested... */
|
|
if (*uniqueServiceKey == messaging_current_servicekey) {
|
|
/* ...check for myCurrentEvent and myNextEvent */
|
|
if (!myCurrentEvent) {
|
|
dprintf("!myCurrentEvent ");
|
|
change = true;
|
|
flag |= CSectionsdClient::epgflags::not_broadcast;
|
|
} else {
|
|
currentEvt = *myCurrentEvent;
|
|
flag |= CSectionsdClient::epgflags::has_current; // aktuelles event da...
|
|
flag |= CSectionsdClient::epgflags::has_anything;
|
|
}
|
|
if (!myNextEvent) {
|
|
dprintf("!myNextEvent ");
|
|
change = true;
|
|
} else {
|
|
nextEvt = *myNextEvent;
|
|
if (flag & CSectionsdClient::epgflags::not_broadcast) {
|
|
dprintf("CSectionsdClient::epgflags::has_no_current\n");
|
|
flag = CSectionsdClient::epgflags::has_no_current;
|
|
}
|
|
flag |= CSectionsdClient::epgflags::has_next; // aktuelles event da...
|
|
flag |= CSectionsdClient::epgflags::has_anything;
|
|
}
|
|
}
|
|
|
|
//dprintf("flag: 0x%x, has_current: 0x%x has_next: 0x%x\n", flag, CSectionsdClient::epgflags::has_current, CSectionsdClient::epgflags::has_next);
|
|
/* if another than the currently running program is requested, then flag will still be 0
|
|
if either the current or the next event is not found, this condition will be true, too.
|
|
*/
|
|
if ((flag & (CSectionsdClient::epgflags::has_current|CSectionsdClient::epgflags::has_next)) !=
|
|
(CSectionsdClient::epgflags::has_current|CSectionsdClient::epgflags::has_next)) {
|
|
//dprintf("commandCurrentNextInfoChannelID: current or next missing!\n");
|
|
SItime zeitEvt1(0, 0);
|
|
if (!(flag & CSectionsdClient::epgflags::has_current)) {
|
|
currentEvt = findActualSIeventForServiceUniqueKey(*uniqueServiceKey, zeitEvt1, 0, &flag2);
|
|
} else {
|
|
zeitEvt1.startzeit = currentEvt.times.begin()->startzeit;
|
|
zeitEvt1.dauer = currentEvt.times.begin()->dauer;
|
|
}
|
|
SItime zeitEvt2(zeitEvt1);
|
|
|
|
if (currentEvt.getName().empty() && flag2 != 0)
|
|
{
|
|
dprintf("commandCurrentNextInfoChannelID change1\n");
|
|
change = true;
|
|
}
|
|
|
|
if (currentEvt.service_id != 0)
|
|
{ //Found
|
|
flag &= (CSectionsdClient::epgflags::has_no_current|CSectionsdClient::epgflags::not_broadcast)^(unsigned)-1;
|
|
flag |= CSectionsdClient::epgflags::has_current;
|
|
flag |= CSectionsdClient::epgflags::has_anything;
|
|
dprintf("[sectionsd] current EPG found. service_id: %x, flag: 0x%x\n",currentEvt.service_id, flag);
|
|
|
|
if (!(flag & CSectionsdClient::epgflags::has_next)) {
|
|
dprintf("*nextEvt not from cur/next V1!\n");
|
|
nextEvt = findNextSIevent(currentEvt.uniqueKey(), zeitEvt2);
|
|
}
|
|
}
|
|
else
|
|
{ // no current event...
|
|
if ( flag2 & CSectionsdClient::epgflags::has_anything )
|
|
{
|
|
flag |= CSectionsdClient::epgflags::has_anything;
|
|
if (!(flag & CSectionsdClient::epgflags::has_next)) {
|
|
dprintf("*nextEvt not from cur/next V2!\n");
|
|
nextEvt = findNextSIeventForServiceUniqueKey(*uniqueServiceKey, zeitEvt2);
|
|
}
|
|
|
|
if (nextEvt.service_id != 0)
|
|
{
|
|
MySIeventsOrderUniqueKey::iterator eFirst = mySIeventsOrderUniqueKey.find(*uniqueServiceKey);
|
|
|
|
if (eFirst != mySIeventsOrderUniqueKey.end())
|
|
{
|
|
// this is a race condition if first entry found is == mySIeventsOrderUniqueKey.begin()
|
|
// so perform a check
|
|
if (eFirst != mySIeventsOrderUniqueKey.begin())
|
|
--eFirst;
|
|
|
|
if (eFirst != mySIeventsOrderUniqueKey.begin())
|
|
{
|
|
time_t azeit = time(NULL);
|
|
|
|
if (eFirst->second->times.begin()->startzeit < azeit &&
|
|
eFirst->second->uniqueKey() == nextEvt.uniqueKey() - 1)
|
|
flag |= CSectionsdClient::epgflags::has_no_current;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (nextEvt.service_id != 0)
|
|
{
|
|
flag &= CSectionsdClient::epgflags::not_broadcast^(unsigned)-1;
|
|
dprintf("[sectionsd] next EPG found. service_id: %x, flag: 0x%x\n",nextEvt.service_id, flag);
|
|
flag |= CSectionsdClient::epgflags::has_next;
|
|
}
|
|
else if (flag != 0)
|
|
{
|
|
dprintf("commandCurrentNextInfoChannelID change2 flag: 0x%02x\n", flag);
|
|
change = true;
|
|
}
|
|
}
|
|
|
|
if (currentEvt.service_id != 0)
|
|
{
|
|
/* check for nvod linkage */
|
|
for (unsigned int i = 0; i < currentEvt.linkage_descs.size(); i++)
|
|
if (currentEvt.linkage_descs[i].linkageType == 0xB0)
|
|
{
|
|
fprintf(stderr,"[sectionsd] linkage in current EPG found.\n");
|
|
flag |= CSectionsdClient::epgflags::current_has_linkagedescriptors;
|
|
break;
|
|
}
|
|
} else
|
|
flag |= CSectionsdClient::epgflags::has_no_current;
|
|
|
|
nResultDataSize =
|
|
sizeof(event_id_t) + // Unique-Key
|
|
sizeof(CSectionsdClient::sectionsdTime) + // zeit
|
|
currentEvt.getName().length() + 1 + // name + '\0'
|
|
sizeof(event_id_t) + // Unique-Key
|
|
sizeof(CSectionsdClient::sectionsdTime) + // zeit
|
|
nextEvt.getName().length() + 1 + // name + '\0'
|
|
sizeof(unsigned) + // flags
|
|
1 // CurrentFSK
|
|
;
|
|
|
|
pResultData = new char[nResultDataSize];
|
|
time_t now;
|
|
|
|
if (!pResultData)
|
|
{
|
|
fprintf(stderr, "low on memory!\n");
|
|
unlockEvents();
|
|
nResultDataSize = 0; // send empty response
|
|
goto out;
|
|
}
|
|
|
|
dprintf("currentEvt: '%s' (%04x) nextEvt: '%s' (%04x) flag: 0x%02x\n",
|
|
currentEvt.getName().c_str(), currentEvt.eventID,
|
|
nextEvt.getName().c_str(), nextEvt.eventID, flag);
|
|
|
|
CSectionsdClient::sectionsdTime time_cur;
|
|
CSectionsdClient::sectionsdTime time_nxt;
|
|
now = time(NULL);
|
|
time_cur.startzeit = currentEvt.times.begin()->startzeit;
|
|
time_cur.dauer = currentEvt.times.begin()->dauer;
|
|
time_nxt.startzeit = nextEvt.times.begin()->startzeit;
|
|
time_nxt.dauer = nextEvt.times.begin()->dauer;
|
|
/* for nvod events that have multiple times, find the one that matches the current time... */
|
|
if (currentEvt.times.size() > 1) {
|
|
for (SItimes::iterator t = currentEvt.times.begin(); t != currentEvt.times.end(); ++t) {
|
|
if ((long)now < (long)(t->startzeit + t->dauer) && (long)now > (long)t->startzeit) {
|
|
time_cur.startzeit = t->startzeit;
|
|
time_cur.dauer =t->dauer;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* ...and the one after that. */
|
|
if (nextEvt.times.size() > 1) {
|
|
for (SItimes::iterator t = nextEvt.times.begin(); t != nextEvt.times.end(); ++t) {
|
|
if ((long)(time_cur.startzeit + time_cur.dauer) <= (long)(t->startzeit)) { // TODO: it's not "long", it's "time_t"
|
|
time_nxt.startzeit = t->startzeit;
|
|
time_nxt.dauer =t->dauer;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
p = pResultData;
|
|
*((event_id_t *)p) = currentEvt.uniqueKey();
|
|
p += sizeof(event_id_t);
|
|
*((CSectionsdClient::sectionsdTime *)p) = time_cur;
|
|
p += sizeof(CSectionsdClient::sectionsdTime);
|
|
strcpy(p, currentEvt.getName().c_str());
|
|
p += currentEvt.getName().length() + 1;
|
|
*((event_id_t *)p) = nextEvt.uniqueKey();
|
|
p += sizeof(event_id_t);
|
|
*((CSectionsdClient::sectionsdTime *)p) = time_nxt;
|
|
p += sizeof(CSectionsdClient::sectionsdTime);
|
|
strcpy(p, nextEvt.getName().c_str());
|
|
p += nextEvt.getName().length() + 1;
|
|
*((unsigned*)p) = flag;
|
|
p += sizeof(unsigned);
|
|
*p = currentEvt.getFSK();
|
|
p++;
|
|
|
|
unlockEvents();
|
|
|
|
//dprintf("change: %s, messaging_eit_busy: %s, last_request: %d\n", change?"true":"false", messaging_eit_is_busy?"true":"false",(time_monotonic() - messaging_last_requested));
|
|
if (change && !messaging_eit_is_busy && (time_monotonic() - messaging_last_requested) < 11) {
|
|
/* restart dmxCN, but only if it is not already running, and only for 10 seconds */
|
|
dprintf("change && !messaging_eit_is_busy => dmxCN.change(0)\n");
|
|
dmxCN.change(0);
|
|
}
|
|
|
|
// response
|
|
|
|
out:
|
|
pmResponse.dataLength = nResultDataSize;
|
|
bool rc = writeNbytes(connfd, (const char *)&pmResponse, sizeof(pmResponse), WRITE_TIMEOUT_IN_SECONDS);
|
|
|
|
if ( nResultDataSize > 0 )
|
|
{
|
|
if (rc == true)
|
|
writeNbytes(connfd, pResultData, nResultDataSize, WRITE_TIMEOUT_IN_SECONDS);
|
|
else
|
|
dputs("[sectionsd] Fehler/Timeout bei write");
|
|
|
|
delete[] pResultData;
|
|
}
|
|
else
|
|
{
|
|
dprintf("[sectionsd] current/next EPG not found!\n");
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
// Sendet ein EPG, unlocked die events, unpaused dmxEIT
|
|
|
|
static void sendEPG(int connfd, const SIevent& e, const SItime& t, int shortepg = 0)
|
|
{
|
|
|
|
struct sectionsd::msgResponseHeader responseHeader;
|
|
|
|
if (!shortepg)
|
|
{
|
|
// new format - 0 delimiters
|
|
responseHeader.dataLength =
|
|
sizeof(event_id_t) + // Unique-Key
|
|
e.getName().length() + 1 + // Name + del
|
|
e.getText().length() + 1 + // Text + del
|
|
e.getExtendedText().length() + 1 + // ext + del
|
|
// 21.07.2005 - rainerk
|
|
// Send extended events
|
|
e.itemDescription.length() + 1 + // Item Description + del
|
|
e.item.length() + 1 + // Item + del
|
|
e.contentClassification.length() + 1 + // Text + del
|
|
e.userClassification.length() + 1 + // ext + del
|
|
1 + // fsk
|
|
sizeof(CSectionsdClient::sectionsdTime); // zeit
|
|
}
|
|
else
|
|
responseHeader.dataLength =
|
|
e.getName().length() + 1 + // Name + del
|
|
e.getText().length() + 1 + // Text + del
|
|
e.getExtendedText().length() + 1 + 1; // ext + del + 0
|
|
|
|
char* msgData = new char[responseHeader.dataLength];
|
|
|
|
if (!msgData)
|
|
{
|
|
fprintf(stderr, "sendEPG: low on memory!\n");
|
|
unlockEvents();
|
|
responseHeader.dataLength = 0;
|
|
goto out;
|
|
}
|
|
|
|
if (!shortepg)
|
|
{
|
|
char *p = msgData;
|
|
*((event_id_t *)p) = e.uniqueKey();
|
|
p += sizeof(event_id_t);
|
|
|
|
strcpy(p, e.getName().c_str());
|
|
p += e.getName().length() + 1;
|
|
strcpy(p, e.getText().c_str());
|
|
p += e.getText().length() + 1;
|
|
strcpy(p, e.getExtendedText().c_str());
|
|
p += e.getExtendedText().length() + 1;
|
|
// 21.07.2005 - rainerk
|
|
// Send extended events
|
|
strcpy(p, e.itemDescription.c_str());
|
|
p += e.itemDescription.length() + 1;
|
|
strcpy(p, e.item.c_str());
|
|
p += e.item.length() + 1;
|
|
|
|
// strlen(userClassification.c_str()) is not equal to e.userClassification.length()
|
|
// because of binary data same is with contentClassification
|
|
// add length
|
|
*p = (unsigned char)e.contentClassification.length();
|
|
p++;
|
|
memmove(p, e.contentClassification.data(), e.contentClassification.length());
|
|
p += e.contentClassification.length();
|
|
|
|
*p = (unsigned char)e.userClassification.length();
|
|
p++;
|
|
memmove(p, e.userClassification.data(), e.userClassification.length());
|
|
p += e.userClassification.length();
|
|
|
|
*p = e.getFSK();
|
|
p++;
|
|
|
|
CSectionsdClient::sectionsdTime zeit;
|
|
zeit.startzeit = t.startzeit;
|
|
zeit.dauer = t.dauer;
|
|
*((CSectionsdClient::sectionsdTime *)p) = zeit;
|
|
p += sizeof(CSectionsdClient::sectionsdTime);
|
|
|
|
}
|
|
else
|
|
sprintf(msgData,
|
|
"%s\xFF%s\xFF%s\xFF",
|
|
e.getName().c_str(),
|
|
e.getText().c_str(),
|
|
e.getExtendedText().c_str()
|
|
);
|
|
|
|
unlockEvents();
|
|
|
|
out:
|
|
if (writeNbytes(connfd, (const char *)&responseHeader, sizeof(responseHeader), WRITE_TIMEOUT_IN_SECONDS)) {
|
|
if (responseHeader.dataLength)
|
|
writeNbytes(connfd, msgData, responseHeader.dataLength, WRITE_TIMEOUT_IN_SECONDS);
|
|
}
|
|
else
|
|
dputs("[sectionsd] Fehler/Timeout bei write");
|
|
|
|
if (msgData)
|
|
delete[] msgData;
|
|
}
|
|
|
|
static void commandActualEPGchannelID(int connfd, char *data, const unsigned dataLength)
|
|
{
|
|
if (dataLength != sizeof(t_channel_id))
|
|
return ;
|
|
|
|
t_channel_id * uniqueServiceKey = (t_channel_id *)data;
|
|
SIevent evt;
|
|
SItime zeit(0, 0);
|
|
|
|
dprintf("[commandActualEPGchannelID] Request of current EPG for " PRINTF_CHANNEL_ID_TYPE "\n", * uniqueServiceKey);
|
|
|
|
readLockEvents();
|
|
if (*uniqueServiceKey == messaging_current_servicekey) {
|
|
if (myCurrentEvent) {
|
|
evt = *myCurrentEvent;
|
|
zeit.startzeit = evt.times.begin()->startzeit;
|
|
zeit.dauer = evt.times.begin()->dauer;
|
|
if (evt.times.size() > 1) {
|
|
time_t now = time(NULL);
|
|
for (SItimes::iterator t = evt.times.begin(); t != evt.times.end(); ++t) {
|
|
if ((long)now < (long)(t->startzeit + t->dauer) && (long)now > (long)t->startzeit) {
|
|
zeit.startzeit = t->startzeit;
|
|
zeit.dauer = t->dauer;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (evt.service_id == 0)
|
|
{
|
|
dprintf("[commandActualEPGchannelID] evt.service_id == 0 ==> no myCurrentEvent!\n");
|
|
evt = findActualSIeventForServiceUniqueKey(*uniqueServiceKey, zeit);
|
|
}
|
|
|
|
if (evt.service_id != 0)
|
|
{
|
|
dprintf("EPG found.\n");
|
|
sendEPG(connfd, evt, zeit);
|
|
return;
|
|
}
|
|
|
|
unlockEvents();
|
|
dprintf("EPG not found!\n");
|
|
|
|
// out:
|
|
struct sectionsd::msgResponseHeader responseHeader;
|
|
responseHeader.dataLength = 0;
|
|
writeNbytes(connfd, (const char *)&responseHeader, sizeof(responseHeader), WRITE_TIMEOUT_IN_SECONDS);
|
|
|
|
return ;
|
|
}
|
|
|
|
bool channel_in_requested_list(t_channel_id * clist, t_channel_id chid, int len)
|
|
{
|
|
if(len == 0) return true;
|
|
for(int i = 0; i < len; i++) {
|
|
if(clist[i] == chid)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void sendEventList(int connfd, const unsigned char serviceTyp1, const unsigned char serviceTyp2 = 0, int sendServiceName = 1, t_channel_id * chidlist = NULL, int clen = 0)
|
|
{
|
|
#define MAX_SIZE_BIGEVENTLIST 128*1024
|
|
|
|
char *evtList = new char[MAX_SIZE_BIGEVENTLIST]; // 128k mssen reichen... schaut euch mal das Ergebnis fr loop an, jedesmal wenn die Senderliste aufgerufen wird
|
|
char *liste;
|
|
long count=0;
|
|
t_channel_id uniqueNow = 0;
|
|
t_channel_id uniqueOld = 0;
|
|
bool found_already = false;
|
|
time_t azeit = time(NULL);
|
|
std::string sname;
|
|
struct sectionsd::msgResponseHeader msgResponse;
|
|
msgResponse.dataLength = 0;
|
|
|
|
if (!evtList)
|
|
{
|
|
fprintf(stderr, "low on memory!\n");
|
|
goto out;
|
|
}
|
|
|
|
if(serviceTyp1 != serviceTyp2) { }
|
|
|
|
*evtList = 0;
|
|
liste = evtList;
|
|
|
|
readLockEvents();
|
|
|
|
/* !!! FIX ME: if the box starts on a channel where there is no EPG sent, it hangs!!! */
|
|
for (MySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey::iterator e = mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.begin(); e != mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.end(); ++e)
|
|
{
|
|
uniqueNow = (*e)->get_channel_id();
|
|
if (!channel_in_requested_list(chidlist, uniqueNow, clen)) continue;
|
|
if ( uniqueNow != uniqueOld )
|
|
{
|
|
found_already = false;
|
|
uniqueOld = uniqueNow;
|
|
}
|
|
|
|
if ( !found_already )
|
|
{
|
|
std::string eName = (*e)->getName();
|
|
std::string eText = (*e)->getText();
|
|
std::string eExtendedText = (*e)->getExtendedText();
|
|
|
|
for (SItimes::iterator t = (*e)->times.begin(); t != (*e)->times.end(); ++t)
|
|
{
|
|
if (t->startzeit <= azeit && azeit <= (long)(t->startzeit + t->dauer))
|
|
{
|
|
if (sendServiceName)
|
|
{
|
|
count += 13 + sname.length() + 1 + eName.length() + 1;
|
|
if (count < MAX_SIZE_BIGEVENTLIST) {
|
|
sprintf(liste, "%012llx\n", (*e)->uniqueKey());
|
|
liste += 13;
|
|
strcpy(liste, sname.c_str());
|
|
liste += sname.length();
|
|
*liste = '\n';
|
|
liste++;
|
|
strcpy(liste, eName.c_str());
|
|
liste += eName.length();
|
|
*liste = '\n';
|
|
liste++;
|
|
} else {
|
|
dprintf("warning: sendEventList - eventlist cut\n");
|
|
break;
|
|
}
|
|
|
|
} // if sendServiceName
|
|
else
|
|
{
|
|
count += sizeof(event_id_t) + 4 + 4 + eName.length() + 1;
|
|
if (eText.empty())
|
|
{
|
|
count += eExtendedText.substr(0, 50).length();
|
|
}
|
|
else
|
|
{
|
|
count += eText.length();
|
|
}
|
|
count++;
|
|
|
|
if (count < MAX_SIZE_BIGEVENTLIST) {
|
|
*((event_id_t *)liste) = (*e)->uniqueKey();
|
|
liste += sizeof(event_id_t);
|
|
*((unsigned *)liste) = t->startzeit;
|
|
liste += 4;
|
|
*((unsigned *)liste) = t->dauer;
|
|
liste += 4;
|
|
strcpy(liste, eName.c_str());
|
|
liste += eName.length();
|
|
liste++;
|
|
|
|
if (eText.empty())
|
|
{
|
|
strcpy(liste, eExtendedText.substr(0, 50).c_str());
|
|
liste += strlen(liste);
|
|
}
|
|
else
|
|
{
|
|
strcpy(liste, eText.c_str());
|
|
liste += eText.length();
|
|
}
|
|
liste++;
|
|
} else {
|
|
dprintf("warning: sendEventList - eventlist cut\n");
|
|
break;
|
|
}
|
|
} // else !sendServiceName
|
|
|
|
found_already = true;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sendServiceName && (count+1 < MAX_SIZE_BIGEVENTLIST))
|
|
{
|
|
*liste = 0;
|
|
liste++;
|
|
count++;
|
|
}
|
|
|
|
unlockEvents();
|
|
|
|
//printf("warning: [sectionsd] sendEventList - response-size: 0x%x, count = %lx\n", liste - evtList, count);
|
|
if (liste - evtList > MAX_SIZE_BIGEVENTLIST)
|
|
printf("warning: [sectionsd] sendEventList- response-size: 0x%x\n", liste - evtList);
|
|
msgResponse.dataLength = liste - evtList;
|
|
dprintf("[sectionsd] sendEventList - response-size: 0x%x\n", msgResponse.dataLength);
|
|
|
|
if ( msgResponse.dataLength == 1 )
|
|
msgResponse.dataLength = 0;
|
|
|
|
out:
|
|
if (writeNbytes(connfd, (const char *)&msgResponse, sizeof(msgResponse), WRITE_TIMEOUT_IN_SECONDS) == true)
|
|
{
|
|
if (msgResponse.dataLength)
|
|
writeNbytes(connfd, evtList, msgResponse.dataLength, WRITE_TIMEOUT_IN_SECONDS);
|
|
}
|
|
else
|
|
dputs("[sectionsd] Fehler/Timeout bei write");
|
|
|
|
if (evtList)
|
|
delete[] evtList;
|
|
}
|
|
|
|
static void commandEventListTVids(int connfd, char* data, const unsigned dataLength)
|
|
{
|
|
dputs("Request of TV event list (IDs).\n");
|
|
sendEventList(connfd, 0x01, 0x04, 0, (t_channel_id *) data, dataLength/sizeof(t_channel_id));
|
|
}
|
|
|
|
static void commandEventListRadioIDs(int connfd, char* data, const unsigned dataLength)
|
|
{
|
|
sendEventList(connfd, 0x02, 0, 0, (t_channel_id *) data, dataLength/sizeof(t_channel_id));
|
|
}
|
|
|
|
static void commandEPGepgID(int connfd, char *data, const unsigned dataLength)
|
|
{
|
|
struct sectionsd::msgResponseHeader pmResponse;
|
|
pmResponse.dataLength = 0;
|
|
|
|
if (dataLength != 8 + 4) {
|
|
writeNbytes(connfd, (const char *)&pmResponse, sizeof(pmResponse), WRITE_TIMEOUT_IN_SECONDS);
|
|
return;
|
|
}
|
|
|
|
event_id_t * epgID = (event_id_t *)data;
|
|
|
|
time_t* startzeit = (time_t *)(data + 8);
|
|
|
|
dprintf("Request of current EPG for 0x%llx 0x%lx\n", *epgID, *startzeit);
|
|
|
|
readLockEvents();
|
|
|
|
const SIevent& evt = findSIeventForEventUniqueKey(*epgID);
|
|
|
|
if (evt.service_id != 0)
|
|
{ // Event found
|
|
SItimes::iterator t = evt.times.begin();
|
|
|
|
for (; t != evt.times.end(); ++t)
|
|
if (t->startzeit == *startzeit)
|
|
break;
|
|
|
|
if (t != evt.times.end())
|
|
{
|
|
dputs("EPG found.");
|
|
// Sendet ein EPG, unlocked die events, unpaused dmxEIT
|
|
sendEPG(connfd, evt, *t);
|
|
// this call is made in sendEPG()
|
|
// unlockEvents();
|
|
return;
|
|
}
|
|
}
|
|
|
|
dputs("EPG not found!");
|
|
unlockEvents();
|
|
// response
|
|
|
|
writeNbytes(connfd, (const char *)&pmResponse, sizeof(pmResponse), WRITE_TIMEOUT_IN_SECONDS);
|
|
}
|
|
|
|
static void commandEPGepgIDshort(int connfd, char *data, const unsigned dataLength)
|
|
{
|
|
struct sectionsd::msgResponseHeader pmResponse;
|
|
pmResponse.dataLength = 0;
|
|
|
|
if (dataLength != 8) {
|
|
writeNbytes(connfd, (const char *)&pmResponse, sizeof(pmResponse), WRITE_TIMEOUT_IN_SECONDS);
|
|
return;
|
|
}
|
|
|
|
event_id_t * epgID = (event_id_t *)data;
|
|
|
|
dprintf("Request of current EPG for 0x%llx\n", *epgID);
|
|
|
|
readLockEvents();
|
|
|
|
const SIevent& evt = findSIeventForEventUniqueKey(*epgID);
|
|
|
|
if (evt.service_id != 0)
|
|
{ // Event found
|
|
dputs("EPG found.");
|
|
sendEPG(connfd, evt, SItime(0, 0), 1);
|
|
// this call is made in sendEPG()
|
|
// unlockEvents();
|
|
return;
|
|
}
|
|
|
|
dputs("EPG not found!");
|
|
unlockEvents();
|
|
// response
|
|
|
|
writeNbytes(connfd, (const char *)&pmResponse, sizeof(pmResponse), WRITE_TIMEOUT_IN_SECONDS);
|
|
}
|
|
|
|
static void commandTimesNVODservice(int connfd, char *data, const unsigned dataLength)
|
|
{
|
|
MySIservicesNVODorderUniqueKey::iterator si;
|
|
char *msgData = 0;
|
|
struct sectionsd::msgResponseHeader responseHeader;
|
|
responseHeader.dataLength = 0;
|
|
t_channel_id uniqueServiceKey;
|
|
|
|
if (dataLength != sizeof(t_channel_id))
|
|
goto out;
|
|
|
|
uniqueServiceKey = *(t_channel_id *)data;
|
|
|
|
dprintf("Request of NVOD times for " PRINTF_CHANNEL_ID_TYPE "\n", uniqueServiceKey);
|
|
|
|
readLockServices();
|
|
readLockEvents();
|
|
|
|
si = mySIservicesNVODorderUniqueKey.find(uniqueServiceKey);
|
|
|
|
if (si != mySIservicesNVODorderUniqueKey.end())
|
|
{
|
|
dprintf("NVODServices: %u\n", si->second->nvods.size());
|
|
|
|
if (si->second->nvods.size())
|
|
{
|
|
responseHeader.dataLength = (sizeof(t_service_id) + sizeof(t_original_network_id) + sizeof(t_transport_stream_id) + 4 + 4) * si->second->nvods.size();
|
|
msgData = new char[responseHeader.dataLength];
|
|
|
|
if (!msgData)
|
|
{
|
|
fprintf(stderr, "low on memory!\n");
|
|
unlockEvents();
|
|
unlockServices();
|
|
responseHeader.dataLength = 0; // empty response
|
|
goto out;
|
|
}
|
|
|
|
char *p = msgData;
|
|
// time_t azeit=time(NULL);
|
|
|
|
for (SInvodReferences::iterator ni = si->second->nvods.begin(); ni != si->second->nvods.end(); ++ni)
|
|
{
|
|
// Zeiten sind erstmal dummy, d.h. pro Service eine Zeit
|
|
ni->toStream(p); // => p += sizeof(t_service_id) + sizeof(t_original_network_id) + sizeof(t_transport_stream_id);
|
|
|
|
SItime zeitEvt1(0, 0);
|
|
// const SIevent &evt=
|
|
findActualSIeventForServiceUniqueKey(ni->uniqueKey(), zeitEvt1, 15*60);
|
|
*(time_t *)p = zeitEvt1.startzeit;
|
|
p += 4;
|
|
*(unsigned *)p = zeitEvt1.dauer;
|
|
p += 4;
|
|
}
|
|
}
|
|
}
|
|
unlockEvents();
|
|
unlockServices();
|
|
|
|
dprintf("data bytes: %u\n", responseHeader.dataLength);
|
|
|
|
out:
|
|
if (writeNbytes(connfd, (const char *)&responseHeader, sizeof(responseHeader), WRITE_TIMEOUT_IN_SECONDS))
|
|
{
|
|
if (responseHeader.dataLength)
|
|
writeNbytes(connfd, msgData, responseHeader.dataLength, WRITE_TIMEOUT_IN_SECONDS);
|
|
}
|
|
else
|
|
dputs("[sectionsd] Fehler/Timeout bei write");
|
|
|
|
if (msgData)
|
|
delete[] msgData;
|
|
}
|
|
|
|
|
|
static void commandGetIsTimeSet(int connfd, char* /*data*/, const unsigned /*dataLength*/)
|
|
{
|
|
sectionsd::responseIsTimeSet rmsg;
|
|
|
|
rmsg.IsTimeSet = timeset;
|
|
|
|
dprintf("Request of Time-Is-Set %d\n", rmsg.IsTimeSet);
|
|
|
|
struct sectionsd::msgResponseHeader responseHeader;
|
|
|
|
responseHeader.dataLength = sizeof(rmsg);
|
|
|
|
if (writeNbytes(connfd, (const char *)&responseHeader, sizeof(responseHeader), WRITE_TIMEOUT_IN_SECONDS) == true)
|
|
{
|
|
writeNbytes(connfd, (const char *)&rmsg, responseHeader.dataLength, WRITE_TIMEOUT_IN_SECONDS);
|
|
}
|
|
else
|
|
dputs("[sectionsd] Fehler/Timeout bei write");
|
|
}
|
|
|
|
|
|
static void commandRegisterEventClient(int /*connfd*/, char *data, const unsigned dataLength)
|
|
{
|
|
if (dataLength == sizeof(CEventServer::commandRegisterEvent))
|
|
{
|
|
eventServer->registerEvent2(((CEventServer::commandRegisterEvent*)data)->eventID, ((CEventServer::commandRegisterEvent*)data)->clientID, ((CEventServer::commandRegisterEvent*)data)->udsName);
|
|
|
|
if (((CEventServer::commandRegisterEvent*)data)->eventID == CSectionsdClient::EVT_TIMESET)
|
|
messaging_neutrino_sets_time = true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void commandUnRegisterEventClient(int /*connfd*/, char *data, const unsigned dataLength)
|
|
{
|
|
if (dataLength == sizeof(CEventServer::commandUnRegisterEvent))
|
|
eventServer->unRegisterEvent2(((CEventServer::commandUnRegisterEvent*)data)->eventID, ((CEventServer::commandUnRegisterEvent*)data)->clientID);
|
|
}
|
|
|
|
|
|
static void commandSetConfig(int connfd, char *data, const unsigned /*dataLength*/)
|
|
{
|
|
struct sectionsd::msgResponseHeader responseHeader;
|
|
struct sectionsd::commandSetConfig *pmsg;
|
|
|
|
pmsg = (struct sectionsd::commandSetConfig *)data;
|
|
|
|
if (secondsToCache != (long)(pmsg->epg_cache)*24*60L*60L) {
|
|
dprintf("new epg_cache = %d\n", pmsg->epg_cache);
|
|
writeLockEvents();
|
|
secondsToCache = (long)(pmsg->epg_cache)*24*60L*60L;
|
|
unlockEvents();
|
|
}
|
|
|
|
if (oldEventsAre != (long)(pmsg->epg_old_events)*60L*60L) {
|
|
dprintf("new epg_old_events = %d\n", pmsg->epg_old_events);
|
|
writeLockEvents();
|
|
oldEventsAre = (long)(pmsg->epg_old_events)*60L*60L;
|
|
unlockEvents();
|
|
}
|
|
if (secondsExtendedTextCache != (long)(pmsg->epg_extendedcache)*60L*60L) {
|
|
dprintf("new epg_extendedcache = %d\n", pmsg->epg_extendedcache);
|
|
// lockEvents();
|
|
writeLockEvents();
|
|
secondsExtendedTextCache = (long)(pmsg->epg_extendedcache)*60L*60L;
|
|
unlockEvents();
|
|
}
|
|
if (max_events != pmsg->epg_max_events) {
|
|
dprintf("new epg_max_events = %d\n", pmsg->epg_max_events);
|
|
writeLockEvents();
|
|
max_events = pmsg->epg_max_events;
|
|
unlockEvents();
|
|
}
|
|
|
|
if (ntprefresh != pmsg->network_ntprefresh) {
|
|
dprintf("new network_ntprefresh = %d\n", pmsg->network_ntprefresh);
|
|
pthread_mutex_lock(&timeThreadSleepMutex);
|
|
ntprefresh = pmsg->network_ntprefresh;
|
|
if (timeset) {
|
|
// wake up time thread
|
|
pthread_cond_broadcast(&timeThreadSleepCond);
|
|
}
|
|
pthread_mutex_unlock(&timeThreadSleepMutex);
|
|
}
|
|
|
|
if (ntpenable ^ (pmsg->network_ntpenable == 1)) {
|
|
dprintf("new network_ntpenable = %d\n", pmsg->network_ntpenable);
|
|
pthread_mutex_lock(&timeThreadSleepMutex);
|
|
ntpenable = (pmsg->network_ntpenable == 1);
|
|
if (timeset) {
|
|
// wake up time thread
|
|
pthread_cond_broadcast(&timeThreadSleepCond);
|
|
}
|
|
pthread_mutex_unlock(&timeThreadSleepMutex);
|
|
}
|
|
|
|
if (ntpserver.compare((std::string)&data[sizeof(struct sectionsd::commandSetConfig)])) {
|
|
ntpserver = (std::string)&data[sizeof(struct sectionsd::commandSetConfig)];
|
|
dprintf("new network_ntpserver = %s\n", ntpserver.c_str());
|
|
ntp_system_cmd = ntp_system_cmd_prefix + ntpserver;
|
|
}
|
|
|
|
if (epg_dir.compare((std::string)&data[sizeof(struct sectionsd::commandSetConfig) + strlen(&data[sizeof(struct sectionsd::commandSetConfig)]) + 1])) {
|
|
epg_dir= (std::string)&data[sizeof(struct sectionsd::commandSetConfig) + strlen(&data[sizeof(struct sectionsd::commandSetConfig)]) + 1];
|
|
dprintf("new epg_dir = %s\n", epg_dir.c_str());
|
|
}
|
|
|
|
responseHeader.dataLength = 0;
|
|
writeNbytes(connfd, (const char *)&responseHeader, sizeof(responseHeader), WRITE_TIMEOUT_IN_SECONDS);
|
|
return ;
|
|
}
|
|
|
|
static void deleteSIexceptEPG()
|
|
{
|
|
writeLockServices();
|
|
unlockServices();
|
|
dmxEIT.dropCachedSectionIDs();
|
|
#ifdef ENABLE_SDT
|
|
dmxSDT.dropCachedSectionIDs();
|
|
#endif
|
|
}
|
|
|
|
static void commandFreeMemory(int connfd, char * /*data*/, const unsigned /*dataLength*/)
|
|
{
|
|
deleteSIexceptEPG();
|
|
|
|
writeLockEvents();
|
|
mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.clear();
|
|
mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.clear();
|
|
mySIeventsOrderUniqueKey.clear();
|
|
mySIeventsNVODorderUniqueKey.clear();
|
|
unlockEvents();
|
|
|
|
struct sectionsd::msgResponseHeader responseHeader;
|
|
responseHeader.dataLength = 0;
|
|
writeNbytes(connfd, (const char *)&responseHeader, sizeof(responseHeader), WRITE_TIMEOUT_IN_SECONDS);
|
|
return ;
|
|
}
|
|
|
|
void *insertEventsfromFile(void * data);
|
|
static void commandReadSIfromXML(int connfd, char *data, const unsigned dataLength)
|
|
{
|
|
pthread_t thrInsert;
|
|
|
|
if (dataLength > 100)
|
|
return ;
|
|
|
|
writeLockMessaging();
|
|
data[dataLength] = '\0';
|
|
epg_dir = (std::string)data + "/";
|
|
unlockMessaging();
|
|
|
|
struct sectionsd::msgResponseHeader responseHeader;
|
|
responseHeader.dataLength = 0;
|
|
writeNbytes(connfd, (const char *)&responseHeader, sizeof(responseHeader), WRITE_TIMEOUT_IN_SECONDS);
|
|
|
|
pthread_attr_t attr;
|
|
pthread_attr_init(&attr);
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
|
|
if (pthread_create (&thrInsert, &attr, insertEventsfromFile, (void *)epg_dir.c_str() ))
|
|
{
|
|
perror("sectionsd: pthread_create()");
|
|
}
|
|
|
|
pthread_attr_destroy(&attr);
|
|
|
|
return ;
|
|
}
|
|
|
|
void writeEventsToFile(char *epgdir);
|
|
static void commandWriteSI2XML(int connfd, char *data, const unsigned dataLength)
|
|
{
|
|
char epgdir[100] = "";
|
|
|
|
struct sectionsd::msgResponseHeader responseHeader;
|
|
responseHeader.dataLength = 0;
|
|
writeNbytes(connfd, (const char *)&responseHeader, sizeof(responseHeader), WRITE_TIMEOUT_IN_SECONDS);
|
|
|
|
if (dataLength > 100)
|
|
return;
|
|
|
|
strncpy(epgdir, data, dataLength);
|
|
epgdir[dataLength] = '\0';
|
|
|
|
writeEventsToFile(epgdir);
|
|
|
|
eventServer->sendEvent(CSectionsdClient::EVT_WRITE_SI_FINISHED, CEventServer::INITID_SECTIONSD);
|
|
return;
|
|
}
|
|
|
|
#if 0
|
|
/* dummy1: do not send back anything */
|
|
static void commandDummy1(int, char *, const unsigned)
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/* dummy2: send back an empty response */
|
|
static void commandDummy2(int connfd, char *, const unsigned)
|
|
{
|
|
struct sectionsd::msgResponseHeader msgResponse;
|
|
msgResponse.dataLength = 0;
|
|
writeNbytes(connfd, (const char *)&msgResponse, sizeof(msgResponse), WRITE_TIMEOUT_IN_SECONDS);
|
|
return;
|
|
}
|
|
|
|
static void commandAllEventsChannelIDSearch(int connfd, char *data, const unsigned dataLength)
|
|
{
|
|
//dprintf("Request of commandAllEventsChannelIDSearch, %d\n",dataLength);
|
|
if (dataLength > 5)
|
|
{
|
|
char *data_ptr = data;
|
|
char search = 0;
|
|
std::string search_text;
|
|
|
|
t_channel_id channel_id = *(t_channel_id*)data_ptr;
|
|
data_ptr += sizeof(t_channel_id);
|
|
search = *data_ptr;
|
|
data_ptr += sizeof(char);
|
|
if(search != 0)
|
|
search_text = data_ptr;
|
|
sendAllEvents(connfd, channel_id, false, search, search_text);
|
|
}
|
|
return;
|
|
}
|
|
|
|
struct s_cmd_table
|
|
{
|
|
void (*cmd)(int connfd, char *, const unsigned);
|
|
std::string sCmd;
|
|
};
|
|
|
|
static s_cmd_table connectionCommands[sectionsd::numberOfCommands] = {
|
|
{ commandDumpStatusInformation, "commandDumpStatusInformation" },
|
|
{ commandAllEventsChannelIDSearch, "commandAllEventsChannelIDSearch" },
|
|
{ commandPauseScanning, "commandPauseScanning" },
|
|
{ commandGetIsScanningActive, "commandGetIsScanningActive" },
|
|
{ commandActualEPGchannelID, "commandActualEPGchannelID" },
|
|
{ commandEventListTVids, "commandEventListTVids" },
|
|
{ commandEventListRadioIDs, "commandEventListRadioIDs" },
|
|
{ commandCurrentNextInfoChannelID, "commandCurrentNextInfoChannelID" },
|
|
{ commandEPGepgID, "commandEPGepgID" },
|
|
{ commandEPGepgIDshort, "commandEPGepgIDshort" },
|
|
{ commandComponentTagsUniqueKey, "commandComponentTagsUniqueKey" },
|
|
{ commandAllEventsChannelID, "commandAllEventsChannelID" },
|
|
{ commandTimesNVODservice, "commandTimesNVODservice" },
|
|
{ commandGetIsTimeSet, "commandGetIsTimeSet" },
|
|
{ commandserviceChanged, "commandserviceChanged" },
|
|
{ commandLinkageDescriptorsUniqueKey, "commandLinkageDescriptorsUniqueKey" },
|
|
{ commandRegisterEventClient, "commandRegisterEventClient" },
|
|
{ commandUnRegisterEventClient, "commandUnRegisterEventClient" },
|
|
{ commandDummy2, "commandSetPrivatePid" },
|
|
{ commandFreeMemory, "commandFreeMemory" },
|
|
{ commandReadSIfromXML, "commandReadSIfromXML" },
|
|
{ commandWriteSI2XML, "commandWriteSI2XML" },
|
|
{ commandSetConfig, "commandSetConfig" },
|
|
};
|
|
|
|
bool sectionsd_parse_command(CBasicMessage::Header &rmsg, int connfd)
|
|
{
|
|
try
|
|
{
|
|
dprintf("Connection from UDS\n");
|
|
|
|
struct sectionsd::msgRequestHeader header;
|
|
|
|
memmove(&header, &rmsg, sizeof(CBasicMessage::Header));
|
|
memset(((char *)&header) + sizeof(CBasicMessage::Header), 0, sizeof(header) - sizeof(CBasicMessage::Header));
|
|
|
|
bool readbytes = readNbytes(connfd, ((char *)&header) + sizeof(CBasicMessage::Header), sizeof(header) - sizeof(CBasicMessage::Header), READ_TIMEOUT_IN_SECONDS);
|
|
|
|
if (readbytes == true)
|
|
{
|
|
dprintf("version: %hhd, cmd: %hhd, numbytes: %d\n", header.version, header.command, readbytes);
|
|
|
|
if (header.command < sectionsd::numberOfCommands)
|
|
{
|
|
dprintf("data length: %hd\n", header.dataLength);
|
|
char *data = new char[header.dataLength + 1];
|
|
|
|
if (!data)
|
|
fprintf(stderr, "low on memory!\n");
|
|
else
|
|
{
|
|
bool rc = true;
|
|
|
|
if (header.dataLength)
|
|
rc = readNbytes(connfd, data, header.dataLength, READ_TIMEOUT_IN_SECONDS);
|
|
|
|
if (rc == true)
|
|
{
|
|
dprintf("%s\n", connectionCommands[header.command].sCmd.c_str());
|
|
connectionCommands[header.command].cmd(connfd, data, header.dataLength);
|
|
}
|
|
|
|
delete[] data;
|
|
}
|
|
}
|
|
else
|
|
dputs("Unknown format or version of request!");
|
|
}
|
|
} // try
|
|
#ifdef WITH_EXCEPTIONS
|
|
catch (std::exception& e)
|
|
{
|
|
fprintf(stderr, "Caught std-exception in connection-thread %s!\n", e.what());
|
|
}
|
|
#endif
|
|
catch (...)
|
|
{
|
|
fprintf(stderr, "Caught exception in connection-thread!\n");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Time-thread
|
|
// updates system time according TOT every 30 minutes
|
|
//---------------------------------------------------------------------
|
|
|
|
static void *timeThread(void *)
|
|
{
|
|
UTC_t UTC;
|
|
time_t tim;
|
|
unsigned int seconds;
|
|
bool first_time = true; /* we don't sleep the first time (we try to get a TOT header) */
|
|
struct timespec restartWait;
|
|
struct timeval now;
|
|
bool time_ntp = false;
|
|
bool success = true;
|
|
|
|
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
|
|
|
|
dprintf("[%sThread] pid %d (%lu) start\n", "time", getpid(), pthread_self());
|
|
|
|
while(!sectionsd_stop)
|
|
{
|
|
while (!scanning || !reader_ready) {
|
|
if(sectionsd_stop)
|
|
break;
|
|
sleep(1);
|
|
}
|
|
if (bTimeCorrect == true) { // sectionsd started with parameter "-tc"
|
|
if (first_time == true) { // only do this once!
|
|
time_t actTime;
|
|
actTime=time(NULL);
|
|
pthread_mutex_lock(&timeIsSetMutex);
|
|
timeset = true;
|
|
pthread_cond_broadcast(&timeIsSetCond);
|
|
pthread_mutex_unlock(&timeIsSetMutex );
|
|
eventServer->sendEvent(CSectionsdClient::EVT_TIMESET, CEventServer::INITID_SECTIONSD, &actTime, sizeof(actTime) );
|
|
printf("[timeThread] Time is already set by system, no further timeThread work!\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
else if ( ntpenable && system( ntp_system_cmd.c_str() ) == 0)
|
|
{
|
|
time_t actTime;
|
|
actTime=time(NULL);
|
|
first_time = false;
|
|
pthread_mutex_lock(&timeIsSetMutex);
|
|
timeset = true;
|
|
time_ntp = true;
|
|
pthread_cond_broadcast(&timeIsSetCond);
|
|
pthread_mutex_unlock(&timeIsSetMutex );
|
|
eventServer->sendEvent(CSectionsdClient::EVT_TIMESET, CEventServer::INITID_SECTIONSD, &actTime, sizeof(actTime) );
|
|
} else {
|
|
if (dvb_time_update) {
|
|
success = getUTC(&UTC, first_time); // for first time, get TDT, then TOT
|
|
if (success)
|
|
{
|
|
tim = changeUTCtoCtime((const unsigned char *) &UTC);
|
|
|
|
if (tim) {
|
|
if ((!messaging_neutrino_sets_time) && (geteuid() == 0)) {
|
|
struct timeval tv;
|
|
tv.tv_sec = tim;
|
|
tv.tv_usec = 0;
|
|
if (settimeofday(&tv, NULL) < 0) {
|
|
perror("[sectionsd] settimeofday");
|
|
pthread_exit(NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
time_t actTime;
|
|
struct tm *tmTime;
|
|
actTime=time(NULL);
|
|
tmTime = localtime(&actTime);
|
|
xprintf("[%sThread] - %02d.%02d.%04d %02d:%02d:%02d, tim: %s", "time", tmTime->tm_mday, tmTime->tm_mon+1, tmTime->tm_year+1900, tmTime->tm_hour, tmTime->tm_min, tmTime->tm_sec, ctime(&tim));
|
|
pthread_mutex_lock(&timeIsSetMutex);
|
|
timeset = true;
|
|
time_ntp= false;
|
|
pthread_cond_broadcast(&timeIsSetCond);
|
|
pthread_mutex_unlock(&timeIsSetMutex );
|
|
eventServer->sendEvent(CSectionsdClient::EVT_TIMESET, CEventServer::INITID_SECTIONSD, &tim, sizeof(tim));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (timeset && dvb_time_update) {
|
|
if (first_time)
|
|
seconds = 5; /* retry a second time immediately */
|
|
else
|
|
seconds = ntprefresh * 60;
|
|
|
|
if(time_ntp) {
|
|
xprintf("[%sThread] Time set via NTP, going to sleep for %d seconds.\n", "time", seconds);
|
|
}
|
|
else {
|
|
xprintf("[%sThread] Time %sset via DVB(%s), going to sleep for %d seconds.\n",
|
|
"time", success?"":"not ", first_time?"TDT":"TOT", seconds);
|
|
}
|
|
first_time = false;
|
|
}
|
|
else {
|
|
if (!first_time) {
|
|
/* time was already set, no need to do it again soon when DVB time-blocked channel is tuned */
|
|
seconds = ntprefresh * 60;
|
|
}
|
|
else if (!scanning) {
|
|
seconds = 60;
|
|
}
|
|
else {
|
|
seconds = 1;
|
|
}
|
|
if (!dvb_time_update && !first_time) {
|
|
xprintf("[%sThread] Time NOT set via DVB due to blocked channel, going to sleep for %d seconds.\n", "time", seconds);
|
|
}
|
|
}
|
|
if(sectionsd_stop)
|
|
break;
|
|
|
|
gettimeofday(&now, NULL);
|
|
TIMEVAL_TO_TIMESPEC(&now, &restartWait);
|
|
restartWait.tv_sec += seconds;
|
|
pthread_mutex_lock( &timeThreadSleepMutex );
|
|
int ret = pthread_cond_timedwait( &timeThreadSleepCond, &timeThreadSleepMutex, &restartWait );
|
|
if (ret == ETIMEDOUT)
|
|
{
|
|
dprintf("TDT-Thread sleeping is over - no signal received\n");
|
|
}
|
|
else if (ret == EINTR)
|
|
{
|
|
dprintf("TDT-Thread sleeping interrupted\n");
|
|
}
|
|
// else if (ret == 0) //everything is fine :) e.g. timeThreadSleepCond maybe signalled @zap time to get a valid time
|
|
pthread_mutex_unlock( &timeThreadSleepMutex );
|
|
}
|
|
|
|
printf("[sectionsd] timeThread ended\n");
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
static cDemux * eitDmx;
|
|
int eit_set_update_filter(int *fd)
|
|
{
|
|
dprintf("eit_set_update_filter\n");
|
|
|
|
unsigned char cur_eit = dmxCN.get_eit_version();
|
|
xprintf("eit_set_update_filter, servicekey = 0x"
|
|
PRINTF_CHANNEL_ID_TYPE_NO_LEADING_ZEROS
|
|
", current version 0x%x got events %d\n",
|
|
messaging_current_servicekey, cur_eit, messaging_have_CN);
|
|
|
|
if (cur_eit == 0xff) {
|
|
*fd = -1;
|
|
return -1;
|
|
}
|
|
|
|
if(eitDmx == NULL) {
|
|
eitDmx = new cDemux(2);
|
|
eitDmx->Open(DMX_PSI_CHANNEL);
|
|
}
|
|
|
|
unsigned char filter[DMX_FILTER_SIZE];
|
|
unsigned char mask[DMX_FILTER_SIZE];
|
|
unsigned char mode[DMX_FILTER_SIZE];
|
|
memset(&filter, 0, DMX_FILTER_SIZE);
|
|
memset(&mask, 0, DMX_FILTER_SIZE);
|
|
memset(&mode, 0, DMX_FILTER_SIZE);
|
|
|
|
filter[0] = 0x4e; /* table_id */
|
|
filter[1] = (unsigned char)(messaging_current_servicekey >> 8);
|
|
filter[2] = (unsigned char)messaging_current_servicekey;
|
|
|
|
mask[0] = 0xFF;
|
|
mask[1] = 0xFF;
|
|
mask[2] = 0xFF;
|
|
|
|
int timeout = 0;
|
|
#if 1 //!HAVE_COOL_HARDWARE
|
|
filter[3] = (cur_eit << 1) | 0x01;
|
|
mask[3] = (0x1F << 1) | 0x01;
|
|
mode[3] = 0x1F << 1;
|
|
eitDmx->sectionFilter(0x12, filter, mask, 4, timeout, mode);
|
|
#else
|
|
/* coolstream drivers broken? */
|
|
filter[3] = (((cur_eit + 1) & 0x01) << 1) | 0x01;
|
|
mask[3] = (0x01 << 1) | 0x01;
|
|
eitDmx->sectionFilter(0x12, filter, mask, 4, timeout, NULL);
|
|
#endif
|
|
|
|
//printf("[sectionsd] start EIT update filter: current version %02X, filter %02X %02X %02X %02X, mask %02X mode %02X \n", cur_eit, dsfp.filter.filter[0], dsfp.filter.filter[1], dsfp.filter.filter[2], dsfp.filter.filter[3], dsfp.filter.mask[3], dsfp.filter.mode[3]);
|
|
*fd = 1;
|
|
return 0;
|
|
}
|
|
|
|
int eit_stop_update_filter(int *fd)
|
|
{
|
|
printf("[sectionsd] stop eit update filter\n");
|
|
if(eitDmx)
|
|
eitDmx->Stop();
|
|
|
|
*fd = -1;
|
|
return 0;
|
|
}
|
|
|
|
#ifdef ENABLE_FREESATEPG
|
|
//---------------------------------------------------------------------
|
|
// Freesat EIT-thread
|
|
// reads Freesat EPG-data
|
|
//---------------------------------------------------------------------
|
|
static void *fseitThread(void *)
|
|
{
|
|
|
|
/* we are holding the start_stop lock during this timeout, so don't
|
|
make it too long... */
|
|
unsigned timeoutInMSeconds = EIT_READ_TIMEOUT;
|
|
bool sendToSleepNow = false;
|
|
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
|
|
|
|
dmxFSEIT.addfilter(0x60, 0xfe); //other TS, scheduled, freesat epg is only broadcast using table_ids 0x60 (scheduled) and 0x61 (scheduled later)
|
|
|
|
if (sections_debug) {
|
|
int policy;
|
|
struct sched_param parm;
|
|
int rc = pthread_getschedparam(pthread_self(), &policy, &parm);
|
|
dprintf("freesatEitThread getschedparam: %d pol %d, prio %d\n", rc, policy, parm.sched_priority);
|
|
}
|
|
|
|
dprintf("[%sThread] pid %d (%lu) start\n", "fseit", getpid(), pthread_self());
|
|
int timeoutsDMX = 0;
|
|
uint8_t *static_buf = new uint8_t[MAX_SECTION_LENGTH];
|
|
int rc;
|
|
|
|
if (static_buf == NULL)
|
|
throw std::bad_alloc();
|
|
|
|
dmxFSEIT.start(); // -> unlock
|
|
if (!scanning)
|
|
dmxFSEIT.request_pause();
|
|
|
|
waitForTimeset();
|
|
dmxFSEIT.lastChanged = time_monotonic();
|
|
|
|
while(!sectionsd_stop) {
|
|
while (!scanning) {
|
|
if(sectionsd_stop)
|
|
break;
|
|
sleep(1);
|
|
}
|
|
if(sectionsd_stop)
|
|
break;
|
|
time_t zeit = time_monotonic();
|
|
|
|
rc = dmxFSEIT.getSection(static_buf, timeoutInMSeconds, timeoutsDMX);
|
|
|
|
if (rc < 0)
|
|
continue;
|
|
|
|
if (timeoutsDMX < 0)
|
|
{
|
|
if ( dmxFSEIT.filter_index + 1 < (signed) dmxFSEIT.filters.size() )
|
|
{
|
|
if (timeoutsDMX == -1)
|
|
dprintf("[freesatEitThread] skipping to next filter(%d) (> DMX_HAS_ALL_SECTIONS_SKIPPING)\n", dmxFSEIT.filter_index+1 );
|
|
if (timeoutsDMX == -2)
|
|
dprintf("[freesatEitThread] skipping to next filter(%d) (> DMX_HAS_ALL_CURRENT_SECTIONS_SKIPPING)\n", dmxFSEIT.filter_index+1 );
|
|
timeoutsDMX = 0;
|
|
dmxFSEIT.change(dmxFSEIT.filter_index + 1);
|
|
}
|
|
else {
|
|
sendToSleepNow = true;
|
|
timeoutsDMX = 0;
|
|
}
|
|
}
|
|
|
|
if (timeoutsDMX >= CHECK_RESTART_DMX_AFTER_TIMEOUTS && scanning)
|
|
{
|
|
if ( dmxFSEIT.filter_index + 1 < (signed) dmxFSEIT.filters.size() )
|
|
{
|
|
dprintf("[freesatEitThread] skipping to next filter(%d) (> DMX_TIMEOUT_SKIPPING)\n", dmxFSEIT.filter_index+1 );
|
|
dmxFSEIT.change(dmxFSEIT.filter_index + 1);
|
|
}
|
|
else
|
|
sendToSleepNow = true;
|
|
|
|
timeoutsDMX = 0;
|
|
}
|
|
|
|
if (sendToSleepNow)
|
|
{
|
|
sendToSleepNow = false;
|
|
|
|
if(sectionsd_stop)
|
|
break;
|
|
dmxFSEIT.real_pause();
|
|
pthread_mutex_lock( &dmxFSEIT.start_stop_mutex );
|
|
writeLockMessaging();
|
|
messaging_zap_detected = false;
|
|
unlockMessaging();
|
|
|
|
struct timespec abs_wait;
|
|
struct timeval now;
|
|
gettimeofday(&now, NULL);
|
|
TIMEVAL_TO_TIMESPEC(&now, &abs_wait);
|
|
abs_wait.tv_sec += TIME_EIT_SCHEDULED_PAUSE;
|
|
dprintf("dmxFSEIT: going to sleep for %d seconds...\n", TIME_EIT_SCHEDULED_PAUSE);
|
|
|
|
int rs = pthread_cond_timedwait( &dmxFSEIT.change_cond, &dmxFSEIT.start_stop_mutex, &abs_wait );
|
|
|
|
pthread_mutex_unlock( &dmxFSEIT.start_stop_mutex );
|
|
|
|
if (rs == ETIMEDOUT)
|
|
{
|
|
dprintf("dmxFSEIT: waking up again - timed out\n");
|
|
// must call dmxFSEIT.change after! unpause otherwise dev is not open,
|
|
// dmxFSEIT.lastChanged will not be set, and filter is advanced the next iteration
|
|
// maybe .change should imply .real_unpause()? -- seife
|
|
dprintf("New Filterindex: %d (ges. %d)\n", 2, (signed) dmxFSEIT.filters.size() );
|
|
dmxFSEIT.change(1); // -> restart
|
|
}
|
|
else if (rs == 0)
|
|
{
|
|
dprintf("dmxFSEIT: waking up again - requested from .change()\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf("dmxFSEIT: waking up again - unknown reason %d\n",rs);
|
|
}
|
|
// update zeit after sleep
|
|
zeit = time_monotonic();
|
|
}
|
|
else if (zeit > dmxFSEIT.lastChanged + TIME_FSEIT_SKIPPING )
|
|
{
|
|
readLockMessaging();
|
|
|
|
if ( dmxFSEIT.filter_index + 1 < (signed) dmxFSEIT.filters.size() )
|
|
{
|
|
dprintf("[freesatEitThread] skipping to next filter(%d) (> TIME_FSEIT_SKIPPING)\n", dmxFSEIT.filter_index+1 );
|
|
dmxFSEIT.change(dmxFSEIT.filter_index + 1);
|
|
}
|
|
else
|
|
sendToSleepNow = true;
|
|
|
|
unlockMessaging();
|
|
}
|
|
|
|
SIsectionEIT eit(static_buf);
|
|
// Houdini: if section is not parsed (too short) -> no need to check events
|
|
if (!eit.is_parsed())
|
|
continue;
|
|
|
|
//dprintf("[eitThread] adding %d events [table 0x%x] (begin)\n", eit.events().size(), eit.getTableId());
|
|
zeit = time(NULL);
|
|
// Nicht alle Events speichern
|
|
for (SIevents::iterator e = eit.events().begin(); e != eit.events().end(); ++e)
|
|
{
|
|
if (!(e->times.empty()))
|
|
{
|
|
if ( ( e->times.begin()->startzeit < zeit + secondsToCache ) &&
|
|
( ( e->times.begin()->startzeit + (long)e->times.begin()->dauer ) > zeit - oldEventsAre ) )
|
|
{
|
|
addEvent(*e, zeit);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// pruefen ob nvod event
|
|
readLockServices();
|
|
MySIservicesNVODorderUniqueKey::iterator si = mySIservicesNVODorderUniqueKey.find(e->get_channel_id());
|
|
|
|
if (si != mySIservicesNVODorderUniqueKey.end())
|
|
{
|
|
// Ist ein nvod-event
|
|
writeLockEvents();
|
|
|
|
for (SInvodReferences::iterator i = si->second->nvods.begin(); i != si->second->nvods.end(); ++i)
|
|
mySIeventUniqueKeysMetaOrderServiceUniqueKey.insert(std::make_pair(i->uniqueKey(), e->uniqueKey()));
|
|
|
|
unlockEvents();
|
|
addNVODevent(*e);
|
|
}
|
|
unlockServices();
|
|
}
|
|
} // for
|
|
//dprintf("[eitThread] added %d events (end)\n", eit.events().size());
|
|
} // for
|
|
delete[] static_buf;
|
|
dputs("[freesatEitThread] end");
|
|
|
|
pthread_exit(NULL);
|
|
}
|
|
#endif
|
|
|
|
//---------------------------------------------------------------------
|
|
// EIT-thread
|
|
// reads EPG-datas
|
|
//---------------------------------------------------------------------
|
|
static void *eitThread(void *)
|
|
{
|
|
|
|
/* we are holding the start_stop lock during this timeout, so don't
|
|
make it too long... */
|
|
unsigned timeoutInMSeconds = EIT_READ_TIMEOUT;
|
|
bool sendToSleepNow = false;
|
|
|
|
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
|
|
|
|
/* These filters are a bit tricky (index numbers):
|
|
- 0 Dummy filter, to make this thread sleep for some seconds
|
|
- 1 then get other TS's current/next (this TS's cur/next are
|
|
handled in dmxCN)
|
|
- 2/3 then get scheduled events on this TS
|
|
- 4 then get the other TS's scheduled events,
|
|
- 4ab (in two steps to reduce the POLLERRs on the DMX device)
|
|
*/
|
|
// -- set EIT filter 0x4e-0x6F
|
|
dmxEIT.addfilter(0x00, 0x00); //0 dummy filter
|
|
dmxEIT.addfilter(0x50, 0xf0); //1 current TS, scheduled
|
|
dmxEIT.addfilter(0x4f, 0xff); //2 other TS, current/next
|
|
#if 1
|
|
dmxEIT.addfilter(0x60, 0xf1); //3a other TS, scheduled, even
|
|
dmxEIT.addfilter(0x61, 0xf1); //3b other TS, scheduled, odd
|
|
#else
|
|
dmxEIT.addfilter(0x60, 0xf0); //3 other TS, scheduled
|
|
#endif
|
|
|
|
if (sections_debug) {
|
|
int policy;
|
|
struct sched_param parm;
|
|
int rc = pthread_getschedparam(pthread_self(), &policy, &parm);
|
|
dprintf("eitThread getschedparam: %d pol %d, prio %d\n", rc, policy, parm.sched_priority);
|
|
}
|
|
dprintf("[%sThread] pid %d (%lu) start\n", "eit", getpid(), pthread_self());
|
|
int timeoutsDMX = 0;
|
|
uint8_t *static_buf = new uint8_t[MAX_SECTION_LENGTH];
|
|
int rc;
|
|
|
|
if (static_buf == NULL)
|
|
{
|
|
xprintf("%s: could not allocate static_buf\n", __FUNCTION__);
|
|
pthread_exit(NULL);
|
|
//throw std::bad_alloc();
|
|
}
|
|
|
|
dmxEIT.start(); // -> unlock
|
|
if (!scanning)
|
|
dmxEIT.request_pause();
|
|
|
|
waitForTimeset();
|
|
dmxEIT.lastChanged = time_monotonic();
|
|
|
|
while (!sectionsd_stop) {
|
|
while (!scanning) {
|
|
if(sectionsd_stop)
|
|
break;
|
|
sleep(1);
|
|
}
|
|
if(sectionsd_stop)
|
|
break;
|
|
time_t zeit = time_monotonic();
|
|
|
|
rc = dmxEIT.getSection(static_buf, timeoutInMSeconds, timeoutsDMX);
|
|
if(sectionsd_stop)
|
|
break;
|
|
|
|
if (timeoutsDMX < 0 && !channel_is_blacklisted)
|
|
{
|
|
if (timeoutsDMX == -1)
|
|
dprintf("[eitThread] skipping to next filter(%d) (> DMX_HAS_ALL_SECTIONS_SKIPPING)\n", dmxEIT.filter_index+1 );
|
|
else if (timeoutsDMX == -2)
|
|
dprintf("[eitThread] skipping to next filter(%d) (> DMX_HAS_ALL_CURRENT_SECTIONS_SKIPPING)\n", dmxEIT.filter_index+1 );
|
|
else
|
|
dprintf("[eitThread] skipping to next filter(%d) (timeouts %d)\n", dmxEIT.filter_index+1, timeoutsDMX);
|
|
if ( dmxEIT.filter_index + 1 < (signed) dmxEIT.filters.size() )
|
|
{
|
|
timeoutsDMX = 0;
|
|
dmxEIT.change(dmxEIT.filter_index + 1);
|
|
}
|
|
else {
|
|
sendToSleepNow = true;
|
|
timeoutsDMX = 0;
|
|
}
|
|
}
|
|
|
|
if (timeoutsDMX >= CHECK_RESTART_DMX_AFTER_TIMEOUTS && scanning && !channel_is_blacklisted)
|
|
{
|
|
dprintf("[eitThread] skipping to next filter(%d) (> DMX_TIMEOUT_SKIPPING %d)\n", dmxEIT.filter_index+1, timeoutsDMX);
|
|
if ( dmxEIT.filter_index + 1 < (signed) dmxEIT.filters.size() )
|
|
{
|
|
dmxEIT.change(dmxEIT.filter_index + 1);
|
|
}
|
|
else
|
|
sendToSleepNow = true;
|
|
|
|
timeoutsDMX = 0;
|
|
}
|
|
|
|
if (sendToSleepNow || channel_is_blacklisted)
|
|
{
|
|
sendToSleepNow = false;
|
|
|
|
dmxEIT.real_pause();
|
|
writeLockMessaging();
|
|
messaging_zap_detected = false;
|
|
unlockMessaging();
|
|
int rs = 0;
|
|
do {
|
|
struct timespec abs_wait;
|
|
struct timeval now;
|
|
gettimeofday(&now, NULL);
|
|
TIMEVAL_TO_TIMESPEC(&now, &abs_wait);
|
|
abs_wait.tv_sec += TIME_EIT_SCHEDULED_PAUSE;
|
|
dprintf("dmxEIT: going to sleep for %d seconds...\n", TIME_EIT_SCHEDULED_PAUSE);
|
|
if(sectionsd_stop)
|
|
break;
|
|
|
|
pthread_mutex_lock( &dmxEIT.start_stop_mutex );
|
|
rs = pthread_cond_timedwait( &dmxEIT.change_cond, &dmxEIT.start_stop_mutex, &abs_wait );
|
|
pthread_mutex_unlock( &dmxEIT.start_stop_mutex );
|
|
} while (channel_is_blacklisted);
|
|
|
|
if (rs == ETIMEDOUT)
|
|
{
|
|
dprintf("dmxEIT: waking up again - timed out\n");
|
|
dprintf("New Filterindex: %d (ges. %d)\n", 2, (signed) dmxEIT.filters.size() );
|
|
dmxEIT.change(1); // -> restart
|
|
}
|
|
else if (rs == 0)
|
|
{
|
|
dprintf("dmxEIT: waking up again - requested from .change()\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf("dmxEIT: waking up again - unknown reason %d\n",rs);
|
|
dmxEIT.real_unpause();
|
|
}
|
|
// update zeit after sleep
|
|
zeit = time_monotonic();
|
|
}
|
|
else if (zeit > dmxEIT.lastChanged + TIME_EIT_SKIPPING )
|
|
{
|
|
readLockMessaging();
|
|
|
|
dprintf("[eitThread] skipping to next filter(%d) (> TIME_EIT_SKIPPING)\n", dmxEIT.filter_index+1 );
|
|
if ( dmxEIT.filter_index + 1 < (signed) dmxEIT.filters.size() )
|
|
{
|
|
dmxEIT.change(dmxEIT.filter_index + 1);
|
|
}
|
|
else
|
|
sendToSleepNow = true;
|
|
|
|
unlockMessaging();
|
|
}
|
|
|
|
if (rc < 0)
|
|
continue;
|
|
|
|
if(sectionsd_stop)
|
|
break;
|
|
|
|
SIsectionEIT eit(static_buf);
|
|
// Houdini: if section is not parsed (too short) -> no need to check events
|
|
if (!eit.is_parsed())
|
|
continue;
|
|
|
|
dprintf("[eitThread] adding %d events [table 0x%x] (begin)\n", eit.events().size(), eit.getTableId());
|
|
zeit = time(NULL);
|
|
// Nicht alle Events speichern
|
|
for (SIevents::iterator e = eit.events().begin(); e != eit.events().end(); ++e)
|
|
{
|
|
if (!(e->times.empty()))
|
|
{
|
|
if ( ( e->times.begin()->startzeit < zeit + secondsToCache ) &&
|
|
( ( e->times.begin()->startzeit + (long)e->times.begin()->dauer ) > zeit - oldEventsAre ) )
|
|
{
|
|
if(sectionsd_stop)
|
|
break;
|
|
//printf("Adding event 0x%llx table %x version %x running %d\n", e->uniqueKey(), eit.getTableId(), eit.getVersionNumber(), e->runningStatus());
|
|
addEvent(*e, zeit);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// pruefen ob nvod event
|
|
readLockServices();
|
|
MySIservicesNVODorderUniqueKey::iterator si = mySIservicesNVODorderUniqueKey.find(e->get_channel_id());
|
|
|
|
if (si != mySIservicesNVODorderUniqueKey.end())
|
|
{
|
|
// Ist ein nvod-event
|
|
writeLockEvents();
|
|
|
|
for (SInvodReferences::iterator i = si->second->nvods.begin(); i != si->second->nvods.end(); ++i)
|
|
mySIeventUniqueKeysMetaOrderServiceUniqueKey.insert(std::make_pair(i->uniqueKey(), e->uniqueKey()));
|
|
|
|
unlockEvents();
|
|
addNVODevent(*e);
|
|
}
|
|
unlockServices();
|
|
}
|
|
} // for
|
|
//dprintf("[eitThread] added %d events (end)\n", eit.events().size());
|
|
} // for
|
|
delete[] static_buf;
|
|
|
|
printf("[sectionsd] eitThread ended\n");
|
|
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// CN-thread: eit thread, but only current/next
|
|
//---------------------------------------------------------------------
|
|
static void *cnThread(void *)
|
|
{
|
|
/* we are holding the start_stop lock during this timeout, so don't
|
|
make it too long... */
|
|
unsigned timeoutInMSeconds = EIT_READ_TIMEOUT;
|
|
bool sendToSleepNow = false;
|
|
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
|
|
|
|
// -- set EIT filter 0x4e
|
|
dmxCN.addfilter(0x4e, 0xff); //0 current TS, current/next
|
|
|
|
dprintf("[%sThread] pid %d (%lu) start\n", "cn", getpid(), pthread_self());
|
|
t_channel_id time_trigger_last = 0;
|
|
int timeoutsDMX = 0;
|
|
uint8_t *static_buf = new uint8_t[MAX_SECTION_LENGTH];
|
|
int rc;
|
|
|
|
if (static_buf == NULL)
|
|
{
|
|
xprintf("%s: could not allocate static_buf\n", __FUNCTION__);
|
|
pthread_exit(NULL);
|
|
//throw std::bad_alloc();
|
|
}
|
|
|
|
dmxCN.start(); // -> unlock
|
|
if (!scanning)
|
|
dmxCN.request_pause();
|
|
|
|
writeLockMessaging();
|
|
messaging_eit_is_busy = true;
|
|
messaging_need_eit_version = false;
|
|
unlockMessaging();
|
|
|
|
waitForTimeset();
|
|
|
|
time_t eit_waiting_since = time_monotonic();
|
|
dmxCN.lastChanged = eit_waiting_since;
|
|
|
|
while(!sectionsd_stop)
|
|
{
|
|
while (!scanning) {
|
|
sleep(1);
|
|
if(sectionsd_stop)
|
|
break;
|
|
}
|
|
if(sectionsd_stop)
|
|
break;
|
|
|
|
rc = dmxCN.getSection(static_buf, timeoutInMSeconds, timeoutsDMX);
|
|
time_t zeit = time_monotonic();
|
|
if (update_eit) {
|
|
if (dmxCN.get_eit_version() != 0xff) {
|
|
writeLockMessaging();
|
|
messaging_need_eit_version = false;
|
|
unlockMessaging();
|
|
} else {
|
|
readLockMessaging();
|
|
if (!messaging_need_eit_version) {
|
|
unlockMessaging();
|
|
dprintf("waiting for eit_version...\n");
|
|
zeit = time_monotonic(); /* reset so that we don't get negative */
|
|
eit_waiting_since = zeit; /* and still compensate for getSection */
|
|
dmxCN.lastChanged = zeit; /* this is ugly - needs somehting better */
|
|
sendToSleepNow = false; /* reset after channel change */
|
|
writeLockMessaging();
|
|
messaging_need_eit_version = true;
|
|
}
|
|
unlockMessaging();
|
|
if (zeit - eit_waiting_since > TIME_EIT_VERSION_WAIT) {
|
|
dprintf("waiting for more than %d seconds - bail out...\n", TIME_EIT_VERSION_WAIT);
|
|
/* send event anyway, so that we know there is no EPG */
|
|
eventServer->sendEvent(CSectionsdClient::EVT_GOT_CN_EPG,
|
|
CEventServer::INITID_SECTIONSD,
|
|
&messaging_current_servicekey,
|
|
sizeof(messaging_current_servicekey));
|
|
writeLockMessaging();
|
|
messaging_need_eit_version = false;
|
|
unlockMessaging();
|
|
sendToSleepNow = true;
|
|
}
|
|
}
|
|
|
|
} // if (update_eit)
|
|
|
|
readLockMessaging();
|
|
if (messaging_got_CN != messaging_have_CN) // timeoutsDMX < -1)
|
|
{
|
|
unlockMessaging();
|
|
writeLockMessaging();
|
|
messaging_have_CN = messaging_got_CN;
|
|
unlockMessaging();
|
|
dprintf("[cnThread] got current_next (0x%x) - sending event!\n", messaging_have_CN);
|
|
eventServer->sendEvent(CSectionsdClient::EVT_GOT_CN_EPG,
|
|
CEventServer::INITID_SECTIONSD,
|
|
&messaging_current_servicekey,
|
|
sizeof(messaging_current_servicekey));
|
|
/* we received an event => reset timeout timer... */
|
|
eit_waiting_since = zeit;
|
|
dmxCN.lastChanged = zeit; /* this is ugly - needs somehting better */
|
|
readLockMessaging();
|
|
}
|
|
if (messaging_have_CN == 0x03) // current + next
|
|
{
|
|
unlockMessaging();
|
|
sendToSleepNow = true;
|
|
//timeoutsDMX = 0;
|
|
}
|
|
else {
|
|
unlockMessaging();
|
|
}
|
|
|
|
if ((sendToSleepNow && !messaging_need_eit_version) || channel_is_blacklisted)
|
|
{
|
|
sendToSleepNow = false;
|
|
|
|
dmxCN.real_pause();
|
|
dprintf("dmxCN: going to sleep...\n");
|
|
|
|
writeLockMessaging();
|
|
messaging_eit_is_busy = false;
|
|
unlockMessaging();
|
|
|
|
/* re-fetch time if transponder changed
|
|
Why I'm doing this here and not from commandserviceChanged?
|
|
commandserviceChanged is called on zap *start*, not after zap finished
|
|
this would lead to often actually fetching the time on the transponder
|
|
you are switching away from, not the one you are switching onto.
|
|
Doing it here at least gives us a good chance to have actually tuned
|
|
to the channel we want to get the time from...
|
|
*/
|
|
if (time_trigger_last != (messaging_current_servicekey & 0xFFFFFFFF0000ULL))
|
|
{
|
|
time_trigger_last = messaging_current_servicekey & 0xFFFFFFFF0000ULL;
|
|
pthread_mutex_lock(&timeThreadSleepMutex);
|
|
pthread_cond_broadcast(&timeThreadSleepCond);
|
|
pthread_mutex_unlock(&timeThreadSleepMutex);
|
|
}
|
|
|
|
int rs;
|
|
do {
|
|
pthread_mutex_lock( &dmxCN.start_stop_mutex );
|
|
if (!channel_is_blacklisted)
|
|
eit_set_update_filter(&eit_update_fd);
|
|
rs = pthread_cond_wait(&dmxCN.change_cond, &dmxCN.start_stop_mutex);
|
|
eit_stop_update_filter(&eit_update_fd);
|
|
pthread_mutex_unlock(&dmxCN.start_stop_mutex);
|
|
} while (channel_is_blacklisted);
|
|
|
|
writeLockMessaging();
|
|
messaging_need_eit_version = false;
|
|
messaging_eit_is_busy = true;
|
|
unlockMessaging();
|
|
|
|
if (rs == 0)
|
|
{
|
|
dprintf("dmxCN: waking up again - requested from .change()\n");
|
|
// fix EPG problems on IPBox
|
|
// http://tuxbox-forum.dreambox-fan.de/forum/viewtopic.php?p=367937#p367937
|
|
#if HAVE_IPBOX_HARDWARE
|
|
dmxCN.change(0);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
printf("dmxCN: waking up again - unknown reason %d\n",rs);
|
|
dmxCN.real_unpause();
|
|
}
|
|
zeit = time_monotonic();
|
|
}
|
|
else if (zeit > dmxCN.lastChanged + TIME_EIT_VERSION_WAIT && !messaging_need_eit_version)
|
|
{
|
|
xprintf("zeit > dmxCN.lastChanged + TIME_EIT_VERSION_WAIT\n");
|
|
sendToSleepNow = true;
|
|
/* we can get here if we got the EIT version but no events */
|
|
/* send a "no epg" event anyway before going to sleep */
|
|
if (messaging_have_CN == 0x00)
|
|
eventServer->sendEvent(CSectionsdClient::EVT_GOT_CN_EPG,
|
|
CEventServer::INITID_SECTIONSD,
|
|
&messaging_current_servicekey,
|
|
sizeof(messaging_current_servicekey));
|
|
continue;
|
|
}
|
|
|
|
if (rc < 0)
|
|
continue;
|
|
|
|
SIsectionEIT eit(static_buf);
|
|
// Houdini: if section is not parsed (too short) -> no need to check events
|
|
if (!eit.is_parsed())
|
|
continue;
|
|
|
|
//dprintf("[cnThread] adding %d events [table 0x%x] (begin)\n", eit.events().size(), eit.getTableId());
|
|
zeit = time(NULL);
|
|
// Nicht alle Events speichern
|
|
for (SIevents::iterator e = eit.events().begin(); e != eit.events().end(); ++e)
|
|
{
|
|
if (!(e->times.empty()))
|
|
{
|
|
addEvent(*e, zeit, true); /* cn = true => fill in current / next event */
|
|
}
|
|
} // for
|
|
//dprintf("[cnThread] added %d events (end)\n", eit.events().size());
|
|
} // for
|
|
delete[] static_buf;
|
|
|
|
printf("[sectionsd] cnThread ended\n");
|
|
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
#ifdef ENABLE_SDT
|
|
static bool addService(const SIservice &s, const int is_actual)
|
|
{
|
|
bool already_exists;
|
|
bool is_new = false;
|
|
|
|
readLockServices();
|
|
MySIservicesOrderUniqueKey::iterator si = mySIservicesOrderUniqueKey.find(s.uniqueKey());
|
|
already_exists = (si != mySIservicesOrderUniqueKey.end());
|
|
unlockServices();
|
|
|
|
if ( (!already_exists) || ((is_actual & 7) && (!si->second->is_actual)) ) {
|
|
|
|
if (already_exists)
|
|
{
|
|
writeLockServices();
|
|
mySIservicesOrderUniqueKey.erase(s.uniqueKey());
|
|
unlockServices();
|
|
}
|
|
|
|
SIservice *sp = new SIservice(s);
|
|
|
|
if (!sp)
|
|
{
|
|
printf("[sectionsd::addService] new SIservice failed.\n");
|
|
return false;
|
|
}
|
|
|
|
SIservicePtr sptr(sp);
|
|
|
|
#if 0
|
|
#define MAX_SIZE_SERVICENAME 50
|
|
char servicename[MAX_SIZE_SERVICENAME];
|
|
|
|
if (sptr->serviceName.empty()) {
|
|
sprintf(servicename, "%04x", sptr->service_id);
|
|
servicename[sizeof(servicename) - 1] = 0;
|
|
sptr->serviceName = servicename;
|
|
}
|
|
#endif
|
|
sptr->is_actual = is_actual;
|
|
|
|
writeLockServices();
|
|
mySIservicesOrderUniqueKey.insert(std::make_pair(sptr->uniqueKey(), sptr));
|
|
unlockServices();
|
|
|
|
if (sptr->nvods.size())
|
|
{
|
|
writeLockServices();
|
|
mySIservicesNVODorderUniqueKey.insert(std::make_pair(sptr->uniqueKey(), sptr));
|
|
unlockServices();
|
|
}
|
|
is_new = true;
|
|
}
|
|
|
|
return is_new;
|
|
}
|
|
|
|
static void *sdtThread(void *)
|
|
{
|
|
const unsigned timeoutInMSeconds = 2500;
|
|
t_transponder_id tid = 0;
|
|
time_t lastData = 0;
|
|
time_t zeit = 0;
|
|
int rs = 0;
|
|
int is_actual = 0;
|
|
|
|
//FIXME
|
|
dmxSDT.addfilter(0x42, 0xf3 ); //SDT actual = 0x42 + SDT other = 0x46 + BAT = 0x4A
|
|
|
|
dprintf("[%sThread] pid %d (%lu) start\n", "sdt", getpid(), pthread_self());
|
|
|
|
int timeoutsDMX = 0;
|
|
uint8_t *static_buf = new uint8_t[MAX_SECTION_LENGTH];
|
|
int rc;
|
|
|
|
if (static_buf == NULL)
|
|
{
|
|
xprintf("%s: could not allocate static_buf\n", __FUNCTION__);
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
dmxSDT.start(); // -> unlock
|
|
|
|
if (!scanning)
|
|
dmxSDT.request_pause();
|
|
|
|
bool startup = true;
|
|
|
|
waitForTimeset();
|
|
|
|
while (!sectionsd_stop) {
|
|
while (!scanning) {
|
|
if(sectionsd_stop)
|
|
break;
|
|
sleep(1);
|
|
}
|
|
zeit = time_monotonic();
|
|
|
|
if(sectionsd_stop)
|
|
break;
|
|
|
|
readLockMessaging();
|
|
if (messaging_zap_detected)
|
|
startup = true;
|
|
unlockMessaging();
|
|
|
|
if ((zeit > lastData + TIME_SDT_NONEWDATA) || (startup))
|
|
{
|
|
struct timespec abs_wait;
|
|
struct timeval now;
|
|
|
|
gettimeofday(&now, NULL);
|
|
TIMEVAL_TO_TIMESPEC(&now, &abs_wait);
|
|
abs_wait.tv_sec += (TIME_SDT_SCHEDULED_PAUSE);
|
|
|
|
dmxSDT.real_pause();
|
|
/* this is the "last" thread. Means: if this one goes to sleep, sectionsd
|
|
* sleeps mostly. Worth printing. */
|
|
printdate_ms(stdout);
|
|
printf("sdtThread: going to sleep...\n");
|
|
|
|
writeLockMessaging();
|
|
messaging_zap_detected = false;
|
|
unlockMessaging();
|
|
|
|
pthread_mutex_lock( &dmxSDT.start_stop_mutex );
|
|
rs = pthread_cond_timedwait( &dmxSDT.change_cond, &dmxSDT.start_stop_mutex, &abs_wait );
|
|
pthread_mutex_unlock( &dmxSDT.start_stop_mutex );
|
|
|
|
if(sectionsd_stop)
|
|
break;
|
|
|
|
if (rs == ETIMEDOUT)
|
|
{
|
|
dprintf("dmxSDT: waking up again - looking for new services :)\n");
|
|
dmxSDT.change( 0 ); // -> restart
|
|
}
|
|
else if (rs == 0)
|
|
{
|
|
dprintf("dmxSDT: waking up again - requested from .change()\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf("dmxSDT: waking up again - unknown reason?!\n");
|
|
dmxSDT.real_unpause();
|
|
}
|
|
// update zeit after sleep
|
|
|
|
startup = false;
|
|
zeit = time_monotonic();
|
|
timeoutsDMX = 0;
|
|
lastData = zeit;
|
|
}
|
|
|
|
if (timeoutsDMX >= RESTART_DMX_AFTER_TIMEOUTS && scanning)
|
|
{
|
|
timeoutsDMX = 0;
|
|
dmxSDT.stop();
|
|
dmxSDT.start(); // leaves unlocked
|
|
dputs("\n !!! dmxSDT restarted !!!\n");
|
|
}
|
|
|
|
rc = dmxSDT.getSection(static_buf, timeoutInMSeconds, timeoutsDMX);
|
|
|
|
if (rc < 0)
|
|
continue;
|
|
|
|
LongSection sec(static_buf);
|
|
uint8_t table_id = sec.getTableId();
|
|
if ((table_id == 0x42) || (table_id == 0x46))
|
|
{
|
|
SIsectionSDT sdt(static_buf);
|
|
|
|
is_actual = (sdt.getTableId() == 0x42) ? 1 : 0;
|
|
|
|
if (is_actual && !sdt.getLastSectionNumber())
|
|
is_actual = 2;
|
|
|
|
bool is_new = false;
|
|
is_actual = (is_actual | 8);
|
|
|
|
for (SIservices::iterator s = sdt.services().begin(); s != sdt.services().end(); ++s) {
|
|
if (addService(*s, is_actual)) {
|
|
is_new = true;
|
|
tid = CREATE_TRANSPONDER_ID_FROM_ORIGINALNETWORK_TRANSPORTSTREAM_ID(s->original_network_id,
|
|
s->transport_stream_id);
|
|
}
|
|
}
|
|
|
|
if (is_new) {
|
|
lastData = time_monotonic();
|
|
|
|
dprintf("[sdtThread] added %d services [table 0x%x TID: %08x]\n",
|
|
sdt.services().size(), table_id, tid);
|
|
}
|
|
}
|
|
} // for
|
|
|
|
delete[] static_buf;
|
|
printf("[sectionsd] sdt-thread ended\n");
|
|
|
|
pthread_exit(NULL);
|
|
}
|
|
#endif
|
|
|
|
/* helper function for the housekeeping-thread */
|
|
static void print_meminfo(void)
|
|
{
|
|
if (!sections_debug)
|
|
return;
|
|
|
|
struct mallinfo meminfo = mallinfo();
|
|
dprintf("total size of memory occupied by chunks handed out by malloc: %d\n"
|
|
"total bytes memory allocated with `sbrk' by malloc, in bytes: %d (%dkB)\n",
|
|
meminfo.uordblks, meminfo.arena, meminfo.arena / 1024);
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// housekeeping-thread
|
|
// does cleaning on fetched datas
|
|
//---------------------------------------------------------------------
|
|
static void *houseKeepingThread(void *)
|
|
{
|
|
int count = 0;
|
|
|
|
dprintf("housekeeping-thread started.\n");
|
|
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
|
|
|
|
while (!sectionsd_stop)
|
|
{
|
|
if (count == META_HOUSEKEEPING) {
|
|
dprintf("meta housekeeping - deleting all transponders, services, bouquets.\n");
|
|
deleteSIexceptEPG();
|
|
count = 0;
|
|
}
|
|
|
|
int rc = HOUSEKEEPING_SLEEP;
|
|
|
|
while (rc)
|
|
rc = sleep(rc);
|
|
|
|
while (!scanning) {
|
|
sleep(1); // wait for streaming to end...
|
|
if(sectionsd_stop)
|
|
break;
|
|
}
|
|
|
|
dprintf("housekeeping.\n");
|
|
|
|
// TODO: maybe we need to stop scanning here?...
|
|
|
|
readLockEvents();
|
|
|
|
unsigned anzEventsAlt = mySIeventsOrderUniqueKey.size();
|
|
dprintf("before removeoldevents\n");
|
|
unlockEvents();
|
|
|
|
removeOldEvents(oldEventsAre); // alte Events
|
|
dprintf("after removeoldevents\n");
|
|
readLockEvents();
|
|
printf("[sectionsd] Removed %d old events (%d left).\n", anzEventsAlt - mySIeventsOrderUniqueKey.size(), mySIeventsOrderUniqueKey.size());
|
|
if (mySIeventsOrderUniqueKey.size() != anzEventsAlt)
|
|
{
|
|
print_meminfo();
|
|
dprintf("Removed %d old events.\n", anzEventsAlt - mySIeventsOrderUniqueKey.size());
|
|
}
|
|
anzEventsAlt = mySIeventsOrderUniqueKey.size();
|
|
unlockEvents();
|
|
|
|
readLockEvents();
|
|
if (mySIeventsOrderUniqueKey.size() != anzEventsAlt)
|
|
{
|
|
print_meminfo();
|
|
dprintf("Removed %d waste events.\n", anzEventsAlt - mySIeventsOrderUniqueKey.size());
|
|
}
|
|
|
|
dprintf("Number of sptr events (event-ID): %u\n", mySIeventsOrderUniqueKey.size());
|
|
dprintf("Number of sptr events (service-id, start time, event-id): %u\n", mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.size());
|
|
dprintf("Number of sptr events (end time, service-id, event-id): %u\n", mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.size());
|
|
dprintf("Number of sptr nvod events (event-ID): %u\n", mySIeventsNVODorderUniqueKey.size());
|
|
dprintf("Number of cached meta-services: %u\n", mySIeventUniqueKeysMetaOrderServiceUniqueKey.size());
|
|
|
|
unlockEvents();
|
|
|
|
print_meminfo();
|
|
|
|
count++;
|
|
|
|
} // for endlos
|
|
dprintf("housekeeping-thread ended.\n");
|
|
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
static void readEPGFilter(void)
|
|
{
|
|
xmlDocPtr filter_parser = parseXmlFile(epg_filter_dir.c_str());
|
|
|
|
t_original_network_id onid = 0;
|
|
t_transport_stream_id tsid = 0;
|
|
t_service_id sid = 0;
|
|
|
|
if (filter_parser != NULL)
|
|
{
|
|
dprintf("Reading EPGFilters\n");
|
|
|
|
xmlNodePtr filter = xmlDocGetRootElement(filter_parser);
|
|
if (xmlGetNumericAttribute(filter, "is_whitelist", 10) == 1)
|
|
epg_filter_is_whitelist = true;
|
|
if (xmlGetNumericAttribute(filter, "except_current_next", 10) == 1)
|
|
epg_filter_except_current_next = true;
|
|
filter = filter->xmlChildrenNode;
|
|
|
|
while (filter) {
|
|
|
|
onid = xmlGetNumericAttribute(filter, "onid", 16);
|
|
tsid = xmlGetNumericAttribute(filter, "tsid", 16);
|
|
sid = xmlGetNumericAttribute(filter, "serviceID", 16);
|
|
if (xmlGetNumericAttribute(filter, "blacklist", 10) == 1)
|
|
addBlacklist(onid, tsid, sid);
|
|
else
|
|
addEPGFilter(onid, tsid, sid);
|
|
|
|
filter = filter->xmlNextNode;
|
|
}
|
|
}
|
|
xmlFreeDoc(filter_parser);
|
|
}
|
|
|
|
static void readDVBTimeFilter(void)
|
|
{
|
|
xmlDocPtr filter_parser = parseXmlFile(dvbtime_filter_dir.c_str());
|
|
|
|
t_original_network_id onid = 0;
|
|
t_transport_stream_id tsid = 0;
|
|
t_service_id sid = 0;
|
|
|
|
if (filter_parser != NULL)
|
|
{
|
|
dprintf("Reading DVBTimeFilters\n");
|
|
|
|
xmlNodePtr filter = xmlDocGetRootElement(filter_parser);
|
|
filter = filter->xmlChildrenNode;
|
|
|
|
while (filter) {
|
|
|
|
onid = xmlGetNumericAttribute(filter, "onid", 16);
|
|
tsid = xmlGetNumericAttribute(filter, "tsid", 16);
|
|
sid = xmlGetNumericAttribute(filter, "serviceID", 16);
|
|
addNoDVBTimelist(onid, tsid, sid);
|
|
|
|
filter = filter->xmlNextNode;
|
|
}
|
|
xmlFreeDoc(filter_parser);
|
|
}
|
|
else
|
|
{
|
|
dvb_time_update = true;
|
|
}
|
|
}
|
|
|
|
extern cDemux * dmxUTC;
|
|
|
|
void sectionsd_main_thread(void * /*data*/)
|
|
{
|
|
pthread_t threadTOT, threadEIT, threadCN, threadHouseKeeping;
|
|
#ifdef ENABLE_FREESATEPG
|
|
pthread_t threadFSEIT;
|
|
#endif
|
|
#ifdef ENABLE_SDT
|
|
pthread_t threadSDT;
|
|
#endif
|
|
int rc;
|
|
|
|
struct sched_param parm;
|
|
|
|
printf("$Id: sectionsd.cpp,v 1.305 2009/07/30 12:41:39 seife Exp $\n");
|
|
printf("SIevent size: %d\n", sizeof(SIevent));
|
|
|
|
/* "export NO_SLOW_ADDEVENT=true" to disable this */
|
|
slow_addevent = (getenv("NO_SLOW_ADDEVENT") == NULL);
|
|
if (slow_addevent)
|
|
printf("====> USING SLOW ADDEVENT. export 'NO_SLOW_ADDEVENT=1' to avoid <===\n");
|
|
|
|
/* for debugging / benchmarking, "export STARTUP_WAIT=true" to wait with startup for
|
|
* the EPG loading to finish
|
|
* this wil fail badly if no EPG saving / loading is configured! */
|
|
reader_ready = (getenv("STARTUP_WAIT") == NULL);
|
|
if (!reader_ready)
|
|
printf("====> sectionsd waiting with startup until saved EPG is read <===\n");
|
|
|
|
SIlanguage::loadLanguages();
|
|
|
|
tzset(); // TZ auswerten
|
|
|
|
CBasicServer sectionsd_server;
|
|
|
|
//NTP-Config laden
|
|
if (!ntp_config.loadConfig(CONF_FILE))
|
|
{
|
|
/* set defaults if no configuration file exists */
|
|
printf("[sectionsd] %s not found\n", CONF_FILE);
|
|
}
|
|
|
|
ntpserver = ntp_config.getString("network_ntpserver", "de.pool.ntp.org");
|
|
ntprefresh = atoi(ntp_config.getString("network_ntprefresh","30").c_str() );
|
|
ntpenable = ntp_config.getBool("network_ntpenable", false);
|
|
ntp_system_cmd = ntp_system_cmd_prefix + ntpserver;
|
|
|
|
//EPG Einstellungen laden
|
|
secondsToCache = (atoi(ntp_config.getString("epg_cache_time","14").c_str() ) *24*60L*60L); //Tage
|
|
secondsExtendedTextCache = (atoi(ntp_config.getString("epg_extendedcache_time","360").c_str() ) *60L*60L); //Stunden
|
|
oldEventsAre = (atoi(ntp_config.getString("epg_old_events","1").c_str() ) *60L*60L); //Stunden
|
|
max_events= atoi(ntp_config.getString("epg_max_events","50000").c_str() );
|
|
|
|
printf("[sectionsd] Caching max %d events\n", max_events);
|
|
printf("[sectionsd] Caching %ld days\n", secondsToCache / (24*60*60L));
|
|
printf("[sectionsd] Caching %ld hours Extended Text\n", secondsExtendedTextCache / (60*60L));
|
|
printf("[sectionsd] Events are old %ldmin after their end time\n", oldEventsAre / 60);
|
|
|
|
readEPGFilter();
|
|
readDVBTimeFilter();
|
|
readEncodingFile();
|
|
|
|
if (!sectionsd_server.prepare(SECTIONSD_UDS_NAME)) {
|
|
fprintf(stderr, "[sectionsd] failed to prepare basic server\n");
|
|
return;
|
|
}
|
|
|
|
eventServer = new CEventServer;
|
|
|
|
// time-Thread starten
|
|
rc = pthread_create(&threadTOT, 0, timeThread, 0);
|
|
|
|
if (rc) {
|
|
fprintf(stderr, "[sectionsd] failed to create time-thread (rc=%d)\n", rc);
|
|
return;
|
|
}
|
|
|
|
// EIT-Thread starten
|
|
rc = pthread_create(&threadEIT, 0, eitThread, 0);
|
|
|
|
if (rc) {
|
|
fprintf(stderr, "[sectionsd] failed to create eit-thread (rc=%d)\n", rc);
|
|
return;
|
|
}
|
|
|
|
// EIT-Thread2 starten
|
|
rc = pthread_create(&threadCN, 0, cnThread, 0);
|
|
|
|
if (rc) {
|
|
fprintf(stderr, "[sectionsd] failed to create eit-thread (rc=%d)\n", rc);
|
|
return;
|
|
}
|
|
|
|
#ifdef ENABLE_FREESATEPG
|
|
// EIT-Thread3 starten
|
|
rc = pthread_create(&threadFSEIT, 0, fseitThread, 0);
|
|
|
|
if (rc) {
|
|
fprintf(stderr, "[sectionsd] failed to create fseit-thread (rc=%d)\n", rc);
|
|
return;
|
|
}
|
|
#endif
|
|
#ifdef ENABLE_SDT
|
|
printf("\n\n\n[sectionsd] starting SDT thread\n");
|
|
rc = pthread_create(&threadSDT, 0, sdtThread, 0);
|
|
|
|
if (rc) {
|
|
fprintf(stderr, "[sectionsd] failed to create sdt-thread (rc=%d)\n", rc);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// housekeeping-Thread starten
|
|
rc = pthread_create(&threadHouseKeeping, 0, houseKeepingThread, 0);
|
|
|
|
if (rc) {
|
|
fprintf(stderr, "[sectionsd] failed to create housekeeping-thread (rc=%d)\n", rc);
|
|
return;
|
|
}
|
|
|
|
if (sections_debug) {
|
|
int policy;
|
|
rc = pthread_getschedparam(pthread_self(), &policy, &parm);
|
|
dprintf("mainloop getschedparam %d policy %d prio %d\n", rc, policy, parm.sched_priority);
|
|
}
|
|
sectionsd_ready = true;
|
|
|
|
while (sectionsd_server.run(sectionsd_parse_command, sectionsd::ACTVERSION, true)) {
|
|
sched_yield();
|
|
if (eit_update_fd != -1) {
|
|
unsigned char buf[MAX_SECTION_LENGTH];
|
|
int ret = eitDmx->Read(buf, MAX_SECTION_LENGTH, 10);
|
|
|
|
if (ret > 0) {
|
|
|
|
LongSection section(buf);
|
|
printdate_ms(stdout);
|
|
printf("EIT Update Filter: new version 0x%x, Activate cnThread\n", section.getVersionNumber());
|
|
|
|
writeLockMessaging();
|
|
messaging_have_CN = 0x00;
|
|
messaging_got_CN = 0x00;
|
|
messaging_last_requested = time_monotonic();
|
|
unlockMessaging();
|
|
|
|
sched_yield();
|
|
dmxCN.change(0);
|
|
sched_yield();
|
|
}
|
|
}
|
|
if(sectionsd_stop)
|
|
break;
|
|
|
|
sched_yield();
|
|
/* 10 ms is the minimal timeslice anyway (HZ = 100), so let's
|
|
wait 20 ms at least to lower the CPU load */
|
|
usleep(20000);
|
|
}
|
|
|
|
printf("[sectionsd] stopping...\n");
|
|
scanning = 0;
|
|
timeset = true;
|
|
printf("broadcasting...\n");
|
|
pthread_mutex_lock(&timeIsSetMutex);
|
|
pthread_cond_broadcast(&timeIsSetCond);
|
|
pthread_mutex_unlock(&timeIsSetMutex);
|
|
pthread_mutex_lock(&timeThreadSleepMutex);
|
|
pthread_cond_broadcast(&timeThreadSleepCond);
|
|
pthread_mutex_unlock(&timeThreadSleepMutex);
|
|
pthread_mutex_lock(&dmxEIT.start_stop_mutex);
|
|
pthread_cond_broadcast(&dmxEIT.change_cond);
|
|
pthread_mutex_unlock(&dmxEIT.start_stop_mutex);
|
|
pthread_mutex_lock(&dmxCN.start_stop_mutex);
|
|
pthread_cond_broadcast(&dmxCN.change_cond);
|
|
pthread_mutex_unlock(&dmxCN.start_stop_mutex);
|
|
#ifdef ENABLE_SDT
|
|
pthread_mutex_lock(&dmxSDT.start_stop_mutex);
|
|
pthread_cond_broadcast(&dmxSDT.change_cond);
|
|
pthread_mutex_unlock(&dmxSDT.start_stop_mutex);
|
|
#endif
|
|
|
|
printf("pausing...\n");
|
|
dmxEIT.request_pause();
|
|
dmxCN.request_pause();
|
|
#ifdef ENABLE_FREESATEPG
|
|
dmxFSEIT.request_pause();
|
|
#endif
|
|
#ifdef ENABLE_SDT
|
|
dmxSDT.request_pause();
|
|
#endif
|
|
pthread_cancel(threadHouseKeeping);
|
|
|
|
if(dmxUTC) dmxUTC->Stop();
|
|
|
|
pthread_cancel(threadTOT);
|
|
|
|
printf("join 1\n");
|
|
pthread_join(threadTOT, NULL);
|
|
if(dmxUTC) delete dmxUTC;
|
|
printf("join 2\n");
|
|
pthread_join(threadEIT, NULL);
|
|
printf("join 3\n");
|
|
pthread_join(threadCN, NULL);
|
|
#ifdef ENABLE_SDT
|
|
printf("join 4\n");
|
|
pthread_join(threadSDT, NULL);
|
|
#endif
|
|
|
|
eit_stop_update_filter(&eit_update_fd);
|
|
if(eitDmx)
|
|
delete eitDmx;
|
|
|
|
printf("close 1\n");
|
|
dmxEIT.close();
|
|
printf("close 3\n");
|
|
dmxCN.close();
|
|
#ifdef ENABLE_FREESATEPG
|
|
dmxFSEIT.close();
|
|
#endif
|
|
#ifdef ENABLE_SDT
|
|
dmxSDT.close();
|
|
#endif
|
|
printf("[sectionsd] ended\n");
|
|
|
|
return;
|
|
}
|
|
/* was: commandAllEventsChannelID sendAllEvents */
|
|
void sectionsd_getEventsServiceKey(t_channel_id serviceUniqueKey, CChannelEventList &eList, char search = 0, std::string search_text = "")
|
|
{
|
|
dprintf("sendAllEvents for " PRINTF_CHANNEL_ID_TYPE "\n", serviceUniqueKey);
|
|
|
|
if ((serviceUniqueKey& 0xFFFFFFFFFFFFULL) != 0) { //0xFFFFFFFFFFFFULL for CREATE_CHANNEL_ID64
|
|
// service Found
|
|
readLockEvents();
|
|
int serviceIDfound = 0;
|
|
|
|
if (search_text.length()) std::transform(search_text.begin(), search_text.end(), search_text.begin(), tolower);
|
|
for (MySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey::iterator e = mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.begin(); e != mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.end(); ++e)
|
|
{
|
|
if ((*e)->get_channel_id() == (serviceUniqueKey& 0xFFFFFFFFFFFFULL)) { //0xFFFFFFFFFFFFULL for CREATE_CHANNEL_ID64
|
|
serviceIDfound = 1;
|
|
|
|
bool copy = true;
|
|
if(search == 0); // nothing to do here
|
|
else if(search == 1) {
|
|
std::string eName = (*e)->getName();
|
|
std::transform(eName.begin(), eName.end(), eName.begin(), tolower);
|
|
if(eName.find(search_text) == std::string::npos)
|
|
copy = false;
|
|
}
|
|
else if(search == 2) {
|
|
std::string eText = (*e)->getText();
|
|
std::transform(eText.begin(), eText.end(), eText.begin(), tolower);
|
|
if(eText.find(search_text) == std::string::npos)
|
|
copy = false;
|
|
}
|
|
else if(search == 3) {
|
|
std::string eExtendedText = (*e)->getExtendedText();
|
|
std::transform(eExtendedText.begin(), eExtendedText.end(), eExtendedText.begin(), tolower);
|
|
if(eExtendedText.find(search_text) == std::string::npos)
|
|
copy = false;
|
|
}
|
|
|
|
if(copy) {
|
|
for (SItimes::iterator t = (*e)->times.begin(); t != (*e)->times.end(); ++t)
|
|
{
|
|
CChannelEvent aEvent;
|
|
aEvent.eventID = (*e)->uniqueKey();
|
|
aEvent.startTime = t->startzeit;
|
|
aEvent.duration = t->dauer;
|
|
aEvent.description = (*e)->getName();
|
|
if (((*e)->getText()).empty())
|
|
aEvent.text = (*e)->getExtendedText().substr(0, 120);
|
|
else
|
|
aEvent.text = (*e)->getText();
|
|
aEvent.channelID = serviceUniqueKey;
|
|
eList.push_back(aEvent);
|
|
}
|
|
} // if = serviceID
|
|
}
|
|
else if ( serviceIDfound )
|
|
break; // sind nach serviceID und startzeit sortiert -> nicht weiter suchen
|
|
}
|
|
|
|
unlockEvents();
|
|
}
|
|
}
|
|
/* was: commandCurrentNextInfoChannelID */
|
|
void sectionsd_getCurrentNextServiceKey(t_channel_id uniqueServiceKey, CSectionsdClient::responseGetCurrentNextInfoChannelID& current_next )
|
|
{
|
|
dprintf("[sectionsd] Request of current/next information for " PRINTF_CHANNEL_ID_TYPE "\n", uniqueServiceKey);
|
|
|
|
SIevent currentEvt;
|
|
SIevent nextEvt;
|
|
unsigned flag = 0, flag2=0;
|
|
/* ugly hack: retry fetching current/next by restarting dmxCN if this is true */
|
|
bool change = false;
|
|
|
|
//t_channel_id * uniqueServiceKey = (t_channel_id *)data;
|
|
|
|
readLockEvents();
|
|
/* if the currently running program is requested... */
|
|
if (uniqueServiceKey == messaging_current_servicekey) {
|
|
/* ...check for myCurrentEvent and myNextEvent */
|
|
if (!myCurrentEvent) {
|
|
dprintf("!myCurrentEvent ");
|
|
change = true;
|
|
flag |= CSectionsdClient::epgflags::not_broadcast;
|
|
} else {
|
|
currentEvt = *myCurrentEvent;
|
|
flag |= CSectionsdClient::epgflags::has_current; // aktuelles event da...
|
|
flag |= CSectionsdClient::epgflags::has_anything;
|
|
}
|
|
if (!myNextEvent) {
|
|
dprintf("!myNextEvent ");
|
|
change = true;
|
|
} else {
|
|
nextEvt = *myNextEvent;
|
|
if (flag & CSectionsdClient::epgflags::not_broadcast) {
|
|
dprintf("CSectionsdClient::epgflags::has_no_current\n");
|
|
flag = CSectionsdClient::epgflags::has_no_current;
|
|
}
|
|
flag |= CSectionsdClient::epgflags::has_next; // aktuelles event da...
|
|
flag |= CSectionsdClient::epgflags::has_anything;
|
|
}
|
|
}
|
|
|
|
//dprintf("flag: 0x%x, has_current: 0x%x has_next: 0x%x\n", flag, CSectionsdClient::epgflags::has_current, CSectionsdClient::epgflags::has_next);
|
|
/* if another than the currently running program is requested, then flag will still be 0
|
|
if either the current or the next event is not found, this condition will be true, too.
|
|
*/
|
|
if ((flag & (CSectionsdClient::epgflags::has_current|CSectionsdClient::epgflags::has_next)) !=
|
|
(CSectionsdClient::epgflags::has_current|CSectionsdClient::epgflags::has_next)) {
|
|
//dprintf("commandCurrentNextInfoChannelID: current or next missing!\n");
|
|
SItime zeitEvt1(0, 0);
|
|
if (!(flag & CSectionsdClient::epgflags::has_current)) {
|
|
currentEvt = findActualSIeventForServiceUniqueKey(uniqueServiceKey, zeitEvt1, 0, &flag2);
|
|
} else {
|
|
zeitEvt1.startzeit = currentEvt.times.begin()->startzeit;
|
|
zeitEvt1.dauer = currentEvt.times.begin()->dauer;
|
|
}
|
|
SItime zeitEvt2(zeitEvt1);
|
|
|
|
if (currentEvt.getName().empty() && flag2 != 0)
|
|
{
|
|
dprintf("commandCurrentNextInfoChannelID change1\n");
|
|
change = true;
|
|
}
|
|
|
|
if (currentEvt.service_id != 0)
|
|
{ //Found
|
|
flag &= (CSectionsdClient::epgflags::has_no_current|CSectionsdClient::epgflags::not_broadcast)^(unsigned)-1;
|
|
flag |= CSectionsdClient::epgflags::has_current;
|
|
flag |= CSectionsdClient::epgflags::has_anything;
|
|
dprintf("[sectionsd] current EPG found. service_id: %x, flag: 0x%x\n",currentEvt.service_id, flag);
|
|
|
|
if (!(flag & CSectionsdClient::epgflags::has_next)) {
|
|
dprintf("*nextEvt not from cur/next V1!\n");
|
|
nextEvt = findNextSIevent(currentEvt.uniqueKey(), zeitEvt2);
|
|
}
|
|
}
|
|
else
|
|
{ // no current event...
|
|
if ( flag2 & CSectionsdClient::epgflags::has_anything )
|
|
{
|
|
flag |= CSectionsdClient::epgflags::has_anything;
|
|
if (!(flag & CSectionsdClient::epgflags::has_next)) {
|
|
dprintf("*nextEvt not from cur/next V2!\n");
|
|
nextEvt = findNextSIeventForServiceUniqueKey(uniqueServiceKey, zeitEvt2);
|
|
}
|
|
|
|
if (nextEvt.service_id != 0)
|
|
{
|
|
MySIeventsOrderUniqueKey::iterator eFirst = mySIeventsOrderUniqueKey.find(uniqueServiceKey);
|
|
|
|
if (eFirst != mySIeventsOrderUniqueKey.end())
|
|
{
|
|
// this is a race condition if first entry found is == mySIeventsOrderUniqueKey.begin()
|
|
// so perform a check
|
|
if (eFirst != mySIeventsOrderUniqueKey.begin())
|
|
--eFirst;
|
|
|
|
if (eFirst != mySIeventsOrderUniqueKey.begin())
|
|
{
|
|
time_t azeit = time(NULL);
|
|
|
|
if (eFirst->second->times.begin()->startzeit < azeit &&
|
|
eFirst->second->uniqueKey() == nextEvt.uniqueKey() - 1)
|
|
flag |= CSectionsdClient::epgflags::has_no_current;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (nextEvt.service_id != 0)
|
|
{
|
|
flag &= CSectionsdClient::epgflags::not_broadcast^(unsigned)-1;
|
|
dprintf("[sectionsd] next EPG found. service_id: %x, flag: 0x%x\n",nextEvt.service_id, flag);
|
|
flag |= CSectionsdClient::epgflags::has_next;
|
|
}
|
|
else if (flag != 0)
|
|
{
|
|
dprintf("commandCurrentNextInfoChannelID change2 flag: 0x%02x\n", flag);
|
|
change = true;
|
|
}
|
|
}
|
|
|
|
if (currentEvt.service_id != 0)
|
|
{
|
|
/* check for nvod linkage */
|
|
for (unsigned int i = 0; i < currentEvt.linkage_descs.size(); i++)
|
|
if (currentEvt.linkage_descs[i].linkageType == 0xB0)
|
|
{
|
|
fprintf(stderr,"[sectionsd] linkage in current EPG found.\n");
|
|
flag |= CSectionsdClient::epgflags::current_has_linkagedescriptors;
|
|
break;
|
|
}
|
|
} else
|
|
flag |= CSectionsdClient::epgflags::has_no_current;
|
|
|
|
time_t now;
|
|
|
|
dprintf("currentEvt: '%s' (%04x) nextEvt: '%s' (%04x) flag: 0x%02x\n",
|
|
currentEvt.getName().c_str(), currentEvt.eventID,
|
|
nextEvt.getName().c_str(), nextEvt.eventID, flag);
|
|
|
|
CSectionsdClient::sectionsdTime time_cur;
|
|
CSectionsdClient::sectionsdTime time_nxt;
|
|
now = time(NULL);
|
|
time_cur.startzeit = currentEvt.times.begin()->startzeit;
|
|
time_cur.dauer = currentEvt.times.begin()->dauer;
|
|
time_nxt.startzeit = nextEvt.times.begin()->startzeit;
|
|
time_nxt.dauer = nextEvt.times.begin()->dauer;
|
|
/* for nvod events that have multiple times, find the one that matches the current time... */
|
|
if (currentEvt.times.size() > 1) {
|
|
for (SItimes::iterator t = currentEvt.times.begin(); t != currentEvt.times.end(); ++t) {
|
|
if ((long)now < (long)(t->startzeit + t->dauer) && (long)now > (long)t->startzeit) {
|
|
time_cur.startzeit = t->startzeit;
|
|
time_cur.dauer =t->dauer;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* ...and the one after that. */
|
|
if (nextEvt.times.size() > 1) {
|
|
for (SItimes::iterator t = nextEvt.times.begin(); t != nextEvt.times.end(); ++t) {
|
|
if ((long)(time_cur.startzeit + time_cur.dauer) <= (long)(t->startzeit)) { // TODO: it's not "long", it's "time_t"
|
|
time_nxt.startzeit = t->startzeit;
|
|
time_nxt.dauer =t->dauer;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
current_next.current_uniqueKey = currentEvt.uniqueKey();
|
|
current_next.current_zeit.startzeit = time_cur.startzeit;
|
|
current_next.current_zeit.dauer = time_cur.dauer;
|
|
current_next.current_name = currentEvt.getName();
|
|
|
|
current_next.next_uniqueKey = nextEvt.uniqueKey();
|
|
current_next.next_zeit.startzeit = time_nxt.startzeit;
|
|
current_next.next_zeit.dauer = time_nxt.dauer;
|
|
current_next.next_name = nextEvt.getName();
|
|
|
|
current_next.flags = flag;
|
|
current_next.current_fsk = currentEvt.getFSK();
|
|
|
|
unlockEvents();
|
|
|
|
//dprintf("change: %s, messaging_eit_busy: %s, last_request: %d\n", change?"true":"false", messaging_eit_is_busy?"true":"false",(time_monotonic() - messaging_last_requested));
|
|
if (change && !messaging_eit_is_busy && (time_monotonic() - messaging_last_requested) < 11) {
|
|
/* restart dmxCN, but only if it is not already running, and only for 10 seconds */
|
|
dprintf("change && !messaging_eit_is_busy => dmxCN.change(0)\n");
|
|
dmxCN.change(0);
|
|
}
|
|
}
|
|
/* commandEPGepgIDshort */
|
|
bool sectionsd_getEPGidShort(event_id_t epgID, CShortEPGData * epgdata)
|
|
{
|
|
bool ret = false;
|
|
dprintf("Request of current EPG for 0x%llx\n", epgID);
|
|
|
|
readLockEvents();
|
|
|
|
const SIevent& e = findSIeventForEventUniqueKey(epgID);
|
|
|
|
if (e.service_id != 0)
|
|
{ // Event found
|
|
dputs("EPG found.");
|
|
epgdata->title = e.getName();
|
|
epgdata->info1 = e.getText();
|
|
epgdata->info2 = e.getExtendedText();
|
|
ret = true;
|
|
} else
|
|
dputs("EPG not found!");
|
|
|
|
unlockEvents();
|
|
return ret;
|
|
}
|
|
|
|
/*was getEPGid commandEPGepgID(int connfd, char *data, const unsigned dataLength) */
|
|
/* TODO item / itemDescription */
|
|
bool sectionsd_getEPGid(const event_id_t epgID, const time_t startzeit, CEPGData * epgdata)
|
|
{
|
|
bool ret = false;
|
|
dprintf("Request of actual EPG for 0x%llx 0x%lx\n", epgID, startzeit);
|
|
|
|
const SIevent& evt = findSIeventForEventUniqueKey(epgID);
|
|
|
|
epgdata->itemDescriptions.clear();
|
|
epgdata->items.clear();
|
|
|
|
readLockEvents();
|
|
if (evt.service_id != 0) { // Event found
|
|
SItimes::iterator t = evt.times.begin();
|
|
|
|
for (; t != evt.times.end(); ++t)
|
|
if (t->startzeit == startzeit)
|
|
break;
|
|
|
|
if (t == evt.times.end()) {
|
|
dputs("EPG not found!");
|
|
} else {
|
|
dputs("EPG found.");
|
|
epgdata->eventID = evt.uniqueKey();
|
|
epgdata->title = evt.getName();
|
|
epgdata->info1 = evt.getText();
|
|
epgdata->info2 = evt.getExtendedText();
|
|
/* FIXME printf("itemDescription: %s\n", evt.itemDescription.c_str()); */
|
|
epgdata->contentClassification = std::string(evt.contentClassification.data(), evt.contentClassification.length());
|
|
epgdata->userClassification = std::string(evt.userClassification.data(), evt.userClassification.length());
|
|
epgdata->fsk = evt.getFSK();
|
|
epgdata->table_id = evt.table_id;
|
|
|
|
epgdata->epg_times.startzeit = t->startzeit;
|
|
epgdata->epg_times.dauer = t->dauer;
|
|
|
|
ret = true;
|
|
}
|
|
} else {
|
|
dputs("EPG not found!");
|
|
}
|
|
unlockEvents();
|
|
return ret;
|
|
}
|
|
/* was commandActualEPGchannelID(int connfd, char *data, const unsigned dataLength) */
|
|
bool sectionsd_getActualEPGServiceKey(const t_channel_id uniqueServiceKey, CEPGData * epgdata)
|
|
{
|
|
bool ret = false;
|
|
SIevent evt;
|
|
SItime zeit(0, 0);
|
|
|
|
dprintf("[commandActualEPGchannelID] Request of current EPG for " PRINTF_CHANNEL_ID_TYPE "\n", uniqueServiceKey);
|
|
|
|
readLockEvents();
|
|
if (uniqueServiceKey == messaging_current_servicekey) {
|
|
if (myCurrentEvent) {
|
|
evt = *myCurrentEvent;
|
|
zeit.startzeit = evt.times.begin()->startzeit;
|
|
zeit.dauer = evt.times.begin()->dauer;
|
|
if (evt.times.size() > 1) {
|
|
time_t now = time(NULL);
|
|
for (SItimes::iterator t = evt.times.begin(); t != evt.times.end(); ++t) {
|
|
if ((long)now < (long)(t->startzeit + t->dauer) && (long)now > (long)t->startzeit) {
|
|
zeit.startzeit = t->startzeit;
|
|
zeit.dauer = t->dauer;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (evt.service_id == 0)
|
|
{
|
|
dprintf("[commandActualEPGchannelID] evt.service_id == 0 ==> no myCurrentEvent!\n");
|
|
evt = findActualSIeventForServiceUniqueKey(uniqueServiceKey, zeit);
|
|
}
|
|
|
|
if (evt.service_id != 0)
|
|
{
|
|
dprintf("EPG found.\n");
|
|
epgdata->eventID = evt.uniqueKey();
|
|
epgdata->title = evt.getName();
|
|
epgdata->info1 = evt.getText();
|
|
epgdata->info2 = evt.getExtendedText();
|
|
/* FIXME printf("itemDescription: %s\n", evt.itemDescription.c_str());*/
|
|
epgdata->contentClassification = std::string(evt.contentClassification.data(), evt.contentClassification.length());
|
|
epgdata->userClassification = std::string(evt.userClassification.data(), evt.userClassification.length());
|
|
epgdata->fsk = evt.getFSK();
|
|
epgdata->table_id = evt.table_id;
|
|
|
|
epgdata->epg_times.startzeit = zeit.startzeit;
|
|
epgdata->epg_times.dauer = zeit.dauer;
|
|
|
|
ret = true;
|
|
} else
|
|
dprintf("EPG not found!\n");
|
|
|
|
unlockEvents();
|
|
return ret;
|
|
}
|
|
/* was static void sendEventList(int connfd, const unsigned char serviceTyp1, const unsigned char serviceTyp2 = 0, int sendServiceName = 1, t_channel_id * chidlist = NULL, int clen = 0) */
|
|
void sectionsd_getChannelEvents(CChannelEventList &eList, const bool tv_mode = true, t_channel_id *chidlist = NULL, int clen = 0)
|
|
{
|
|
clen = clen / sizeof(t_channel_id);
|
|
|
|
t_channel_id uniqueNow = 0;
|
|
t_channel_id uniqueOld = 0;
|
|
bool found_already = false;
|
|
time_t azeit = time(NULL);
|
|
|
|
if(tv_mode) {}
|
|
readLockEvents();
|
|
|
|
/* !!! FIX ME: if the box starts on a channel where there is no EPG sent, it hangs!!! */
|
|
for (MySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey::iterator e = mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.begin(); e != mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.end(); ++e)
|
|
{
|
|
uniqueNow = (*e)->get_channel_id();
|
|
|
|
if (uniqueNow != uniqueOld)
|
|
{
|
|
uniqueOld = uniqueNow;
|
|
if (!channel_in_requested_list(chidlist, uniqueNow, clen))
|
|
continue;
|
|
found_already = false;
|
|
}
|
|
|
|
if (!found_already)
|
|
{
|
|
for (SItimes::iterator t = (*e)->times.begin(); t != (*e)->times.end(); ++t)
|
|
{
|
|
if (t->startzeit <= azeit && azeit <= (long)(t->startzeit + t->dauer))
|
|
{
|
|
CChannelEvent aEvent;
|
|
aEvent.eventID = (*e)->uniqueKey();
|
|
aEvent.startTime = t->startzeit;
|
|
aEvent.duration = t->dauer;
|
|
aEvent.description = (*e)->getName();
|
|
if (((*e)->getText()).empty())
|
|
aEvent.text = (*e)->getExtendedText().substr(0, 120);
|
|
else
|
|
aEvent.text = (*e)->getText();
|
|
eList.push_back(aEvent);
|
|
|
|
found_already = true;
|
|
break;
|
|
}
|
|
}
|
|
if(clen == (int) eList.size())
|
|
break;
|
|
}
|
|
}
|
|
|
|
unlockEvents();
|
|
}
|
|
/*was static void commandComponentTagsUniqueKey(int connfd, char *data, const unsigned dataLength) */
|
|
bool sectionsd_getComponentTagsUniqueKey(const event_id_t uniqueKey, CSectionsdClient::ComponentTagList& tags)
|
|
{
|
|
bool ret = false;
|
|
dprintf("Request of ComponentTags for 0x%llx\n", uniqueKey);
|
|
|
|
tags.clear();
|
|
|
|
readLockEvents();
|
|
|
|
MySIeventsOrderUniqueKey::iterator eFirst = mySIeventsOrderUniqueKey.find(uniqueKey);
|
|
|
|
if (eFirst != mySIeventsOrderUniqueKey.end()) {
|
|
CSectionsdClient::responseGetComponentTags response;
|
|
ret = true;
|
|
|
|
for (SIcomponents::iterator cmp = eFirst->second->components.begin(); cmp != eFirst->second->components.end(); ++cmp) {
|
|
response.component = cmp->component;
|
|
response.componentType = cmp->componentType;
|
|
response.componentTag = cmp->componentTag;
|
|
response.streamContent = cmp->streamContent;
|
|
|
|
tags.insert(tags.end(), response);
|
|
}
|
|
}
|
|
|
|
unlockEvents();
|
|
return ret;
|
|
|
|
}
|
|
/* was static void commandLinkageDescriptorsUniqueKey(int connfd, char *data, const unsigned dataLength) */
|
|
bool sectionsd_getLinkageDescriptorsUniqueKey(const event_id_t uniqueKey, CSectionsdClient::LinkageDescriptorList& descriptors)
|
|
{
|
|
bool ret = false;
|
|
dprintf("Request of LinkageDescriptors for 0x%llx\n", uniqueKey);
|
|
|
|
descriptors.clear();
|
|
readLockEvents();
|
|
|
|
MySIeventsOrderUniqueKey::iterator eFirst = mySIeventsOrderUniqueKey.find(uniqueKey);
|
|
|
|
if (eFirst != mySIeventsOrderUniqueKey.end()) {
|
|
for (SIlinkage_descs::iterator linkage_desc = eFirst->second->linkage_descs.begin(); linkage_desc != eFirst->second->linkage_descs.end(); ++linkage_desc)
|
|
{
|
|
if (linkage_desc->linkageType == 0xB0) {
|
|
|
|
CSectionsdClient::responseGetLinkageDescriptors response;
|
|
response.name = linkage_desc->name.c_str();
|
|
response.transportStreamId = linkage_desc->transportStreamId;
|
|
response.originalNetworkId = linkage_desc->originalNetworkId;
|
|
response.serviceId = linkage_desc->serviceId;
|
|
descriptors.insert( descriptors.end(), response);
|
|
ret = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
unlockEvents();
|
|
return ret;
|
|
|
|
}
|
|
/* was static void commandTimesNVODservice(int connfd, char *data, const unsigned dataLength) */
|
|
bool sectionsd_getNVODTimesServiceKey(const t_channel_id uniqueServiceKey, CSectionsdClient::NVODTimesList& nvod_list)
|
|
{
|
|
bool ret = false;
|
|
dprintf("Request of NVOD times for " PRINTF_CHANNEL_ID_TYPE "\n", uniqueServiceKey);
|
|
|
|
nvod_list.clear();
|
|
|
|
readLockServices();
|
|
readLockEvents();
|
|
|
|
MySIservicesNVODorderUniqueKey::iterator si = mySIservicesNVODorderUniqueKey.find(uniqueServiceKey);
|
|
if (si != mySIservicesNVODorderUniqueKey.end())
|
|
{
|
|
dprintf("NVODServices: %u\n", si->second->nvods.size());
|
|
|
|
if (si->second->nvods.size()) {
|
|
for (SInvodReferences::iterator ni = si->second->nvods.begin(); ni != si->second->nvods.end(); ++ni) {
|
|
SItime zeitEvt1(0, 0);
|
|
findActualSIeventForServiceUniqueKey(ni->uniqueKey(), zeitEvt1, 15*60);
|
|
|
|
CSectionsdClient::responseGetNVODTimes response;
|
|
|
|
response.service_id = ni->service_id;
|
|
response.original_network_id = ni->original_network_id;
|
|
response.transport_stream_id = ni->transport_stream_id;
|
|
response.zeit.startzeit = zeitEvt1.startzeit;
|
|
response.zeit.dauer = zeitEvt1.dauer;
|
|
|
|
nvod_list.insert( nvod_list.end(), response);
|
|
ret = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
unlockEvents();
|
|
unlockServices();
|
|
return ret;
|
|
}
|
|
|
|
void sectionsd_setPrivatePid(unsigned short /*pid*/)
|
|
{
|
|
}
|
|
|
|
void sectionsd_set_languages(const std::vector<std::string>& newLanguages)
|
|
{
|
|
SIlanguage::setLanguages(newLanguages);
|
|
SIlanguage::saveLanguages();
|
|
}
|
|
|
|
bool sectionsd_isReady(void)
|
|
{
|
|
return sectionsd_ready;
|
|
}
|