mirror of
https://github.com/tuxbox-neutrino/neutrino.git
synced 2025-08-26 23:13:13 +02:00
2993 lines
88 KiB
C++
2993 lines
88 KiB
C++
/*
|
|
* sectionsd.cpp (network daemon for SI-sections)
|
|
* (dbox-II-project)
|
|
*
|
|
* Copyright (C) 2001 by fnbrd (fnbrd@gmx.de)
|
|
* Homepage: http://dbox2.elxsi.de
|
|
*
|
|
* Copyright (C) 2008-2013 Stefan Seyfried
|
|
*
|
|
* Copyright (C) 2011-2012 CoolStream International Ltd
|
|
*
|
|
* License: GPLv2
|
|
*
|
|
* 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;
|
|
*
|
|
* 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 <unistd.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <pthread.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
#include <connection/basicsocket.h>
|
|
#include <connection/basicserver.h>
|
|
|
|
#include <sectionsdclient/sectionsdMsg.h>
|
|
#include <sectionsdclient/sectionsdclient.h>
|
|
#include <eventserver.h>
|
|
#include <driver/abstime.h>
|
|
#include <system/helpers.h>
|
|
#include <OpenThreads/ScopedLock>
|
|
|
|
#include "eitd.h"
|
|
#include "sectionsd.h"
|
|
#include "edvbstring.h"
|
|
#include "xmlutil.h"
|
|
#include "debug.h"
|
|
|
|
//#define ENABLE_SDT //FIXME
|
|
|
|
//#define DEBUG_SDT_THREAD
|
|
//#define DEBUG_TIME_THREAD
|
|
|
|
#define DEBUG_SECTION_THREADS
|
|
//#define DEBUG_CN_THREAD
|
|
|
|
/*static*/ bool reader_ready = true;
|
|
static unsigned int max_events;
|
|
static bool notify_complete = false;
|
|
|
|
/* period to remove old events */
|
|
#define HOUSEKEEPING_SLEEP (5 * 60) // sleep 5 minutes
|
|
//#define HOUSEKEEPING_SLEEP (30) // FIXME 1 min for testing
|
|
/* period to clean cached sections and force restart sections read */
|
|
#define META_HOUSEKEEPING_COUNT (24 * 60 * 60) / HOUSEKEEPING_SLEEP // meta housekeeping after XX housekeepings - every 24h -
|
|
#define STANDBY_HOUSEKEEPING_COUNT (60 * 60) / HOUSEKEEPING_SLEEP
|
|
#define EPG_SERVICE_FREQUENTLY_COUNT (60 * 60) / HOUSEKEEPING_SLEEP
|
|
|
|
// Timeout bei tcp/ip connections in ms
|
|
#define READ_TIMEOUT_IN_SECONDS 2
|
|
#define WRITE_TIMEOUT_IN_SECONDS 2
|
|
|
|
// Time in seconds we are waiting for an EIT version number
|
|
//#define TIME_EIT_VERSION_WAIT 3 // old
|
|
#define TIME_EIT_VERSION_WAIT 10
|
|
// number of timeouts after which we stop waiting for an EIT version number
|
|
#define TIMEOUTS_EIT_VERSION_WAIT (2 * CHECK_RESTART_DMX_AFTER_TIMEOUTS)
|
|
|
|
static unsigned int epg_save_frequently;
|
|
static unsigned int epg_read_frequently;
|
|
static long secondsToCache;
|
|
long int secondsExtendedTextCache = 0;
|
|
static long oldEventsAre;
|
|
static int scanning = 1;
|
|
|
|
extern bool epg_filter_is_whitelist;
|
|
extern bool epg_filter_except_current_next;
|
|
static bool xml_epg_filter;
|
|
|
|
static bool messaging_zap_detected = false;
|
|
/*static*/ bool dvb_time_update = false;
|
|
|
|
//NTP-Config
|
|
#define CONF_FILE CONFIGDIR "/neutrino.conf"
|
|
|
|
std::string ntp_system_cmd_prefix = find_executable("ntpdate") + " ";
|
|
|
|
std::string ntp_system_cmd;
|
|
std::string ntpserver;
|
|
int ntprefresh;
|
|
int ntpenable;
|
|
|
|
std::string epg_dir("");
|
|
|
|
/* messaging_current_servicekey does probably not need locking, since it is
|
|
changed from one place */
|
|
static t_channel_id messaging_current_servicekey = 0;
|
|
static t_channel_id current_channel_id = 0;
|
|
static bool channel_is_blacklisted = false;
|
|
|
|
bool timeset = false;
|
|
|
|
static int messaging_have_CN = 0x00; // 0x01 = CURRENT, 0x02 = NEXT
|
|
static int messaging_got_CN = 0x00; // 0x01 = CURRENT, 0x02 = NEXT
|
|
static bool messaging_neutrino_sets_time = 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;
|
|
OpenThreads::Mutex filter_mutex;
|
|
|
|
static CTimeThread threadTIME;
|
|
static CEitThread threadEIT;
|
|
static CCNThread threadCN;
|
|
|
|
#ifdef ENABLE_VIASATEPG
|
|
// ViaSAT uses pid 0x39 instead of 0x12
|
|
static CEitThread threadVSEIT("viasatThread", 0x39);
|
|
#endif
|
|
|
|
#ifdef ENABLE_FREESATEPG
|
|
static CFreeSatThread threadFSEIT;
|
|
#endif
|
|
|
|
#ifdef ENABLE_SDT
|
|
#define TIME_SDT_NONEWDATA 15
|
|
//#define RESTART_DMX_AFTER_TIMEOUTS 5
|
|
#define TIME_SDT_SCHEDULED_PAUSE 2* 60* 60
|
|
CSdtThread threadSDT;
|
|
#endif
|
|
|
|
static int sectionsd_stop = 0;
|
|
|
|
static bool slow_addevent = true;
|
|
|
|
inline void readLockServices(void)
|
|
{
|
|
pthread_rwlock_rdlock(&servicesLock);
|
|
}
|
|
#ifdef ENABLE_SDT
|
|
inline void writeLockServices(void)
|
|
{
|
|
pthread_rwlock_wrlock(&servicesLock);
|
|
}
|
|
#endif
|
|
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);
|
|
}
|
|
|
|
static const SIevent nullEvt; // Null-Event
|
|
|
|
static MySIeventsOrderUniqueKey mySIeventsOrderUniqueKey;
|
|
static MySIeventsOrderUniqueKey mySIeventsNVODorderUniqueKey;
|
|
/*static*/ MySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey;
|
|
static MySIeventsOrderFirstEndTimeServiceIDEventUniqueKey mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey;
|
|
|
|
static SIevent * myCurrentEvent = NULL;
|
|
static SIevent * myNextEvent = NULL;
|
|
|
|
// 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;
|
|
|
|
static bool deleteEvent(const event_id_t uniqueKey)
|
|
{
|
|
bool ret = false;
|
|
writeLockEvents();
|
|
MySIeventsOrderUniqueKey::iterator e = mySIeventsOrderUniqueKey.find(uniqueKey);
|
|
|
|
if (e != mySIeventsOrderUniqueKey.end()) {
|
|
if (!e->second->times.empty()) {
|
|
mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.erase(e->second);
|
|
mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.erase(e->second);
|
|
}
|
|
|
|
#ifndef USE_BOOST_SHARED_PTR
|
|
delete e->second;
|
|
#endif
|
|
mySIeventsOrderUniqueKey.erase(uniqueKey);
|
|
mySIeventsNVODorderUniqueKey.erase(uniqueKey);
|
|
ret = true;
|
|
}
|
|
unlockEvents();
|
|
return ret;
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
filter_mutex.lock();
|
|
bool EPG_filtered = checkEPGFilter(evt.original_network_id, evt.transport_stream_id, evt.service_id);
|
|
filter_mutex.unlock();
|
|
|
|
/* 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...
|
|
//xprintf("addEvent: current %012" PRIx64 " event %012" PRIx64 " messaging_got_CN %d\n", messaging_current_servicekey, evt.get_channel_id(), messaging_got_CN);
|
|
readLockMessaging();
|
|
// only if it is the current channel... and if we don't have them already.
|
|
if (evt.get_channel_id() == messaging_current_servicekey &&
|
|
(messaging_got_CN != 0x03)) {
|
|
xprintf("addEvent: ch %012" PRIx64 " running %d (%s) got_CN %d\n", evt.get_channel_id(), evt.runningStatus(), evt.runningStatus() > 2 ? "curr" : "next", messaging_got_CN);
|
|
|
|
unlockMessaging();
|
|
writeLockEvents();
|
|
if (evt.runningStatus() > 2) { // paused or currently running
|
|
//TODO myCurrentEvent/myNextEvent without pointers.
|
|
if (!myCurrentEvent || (myCurrentEvent && (*myCurrentEvent).uniqueKey() != evt.uniqueKey())) {
|
|
delete myCurrentEvent;
|
|
myCurrentEvent = new SIevent(evt);
|
|
writeLockMessaging();
|
|
messaging_got_CN |= 0x01;
|
|
if (myNextEvent && (*myNextEvent).uniqueKey() == evt.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",
|
|
evt.runningStatus(), evt.eventID, evt.getName().c_str());
|
|
} else {
|
|
writeLockMessaging();
|
|
messaging_got_CN |= 0x01;
|
|
unlockMessaging();
|
|
dprintf("addevent-cn: not add runn. (%d) event 0x%04x '%s'\n",
|
|
evt.runningStatus(), evt.eventID, evt.getName().c_str());
|
|
}
|
|
} else {
|
|
if ((!myNextEvent || (myNextEvent && (*myNextEvent).uniqueKey() != evt.uniqueKey() && (*myNextEvent).times.begin()->startzeit < evt.times.begin()->startzeit)) &&
|
|
(!myCurrentEvent || (myCurrentEvent && (*myCurrentEvent).uniqueKey() != evt.uniqueKey()))) {
|
|
delete myNextEvent;
|
|
myNextEvent = new SIevent(evt);
|
|
writeLockMessaging();
|
|
messaging_got_CN |= 0x02;
|
|
unlockMessaging();
|
|
dprintf("addevent-cn: added next (%d) event 0x%04x '%s'\n",
|
|
evt.runningStatus(), evt.eventID, evt.getName().c_str());
|
|
} else {
|
|
dprintf("addevent-cn: not added next(%d) event 0x%04x '%s'\n",
|
|
evt.runningStatus(), evt.eventID, evt.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 %012" PRIx64 ":%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->classifications = evt.classifications;
|
|
#ifdef USE_ITEM_DESCRIPTION
|
|
si->second->itemDescription = evt.itemDescription;
|
|
si->second->item = evt.item;
|
|
#endif
|
|
//si->second->vps = evt.vps;
|
|
if ((!evt.getExtendedText().empty()) && !evt.times.empty() &&
|
|
(evt.times.begin()->startzeit < zeit + secondsExtendedTextCache))
|
|
si->second->setExtendedText(0 /*"OFF"*/,evt.getExtendedText());
|
|
if (!evt.getText().empty())
|
|
si->second->setText(0 /*"OFF"*/,evt.getText());
|
|
if (!evt.getName().empty())
|
|
si->second->setName(0 /*"OFF"*/,evt.getName());
|
|
}
|
|
else {
|
|
|
|
SIevent *eptr = new SIevent(evt);
|
|
|
|
if (!eptr)
|
|
{
|
|
printf("[sectionsd::addEvent] new SIevent failed.\n");
|
|
unlockEvents();
|
|
return;
|
|
}
|
|
|
|
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(0 /*"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();
|
|
if (x_key == e_key)
|
|
{
|
|
/* the present event has a higher table_id than the new one
|
|
* => delete and insert the new one */
|
|
if ((*x)->table_id >= e->table_id)
|
|
continue;
|
|
/* else: keep the old event with the lower table_id */
|
|
unlockEvents();
|
|
delete eptr;
|
|
return;
|
|
}
|
|
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;
|
|
if ((*x)->table_id < e->table_id)
|
|
{
|
|
/* don't add the higher table_id */
|
|
dprintf("%s: don't replace 0x%012" PRIx64 ".%02x with 0x%012" PRIx64 ".%02x\n",
|
|
__func__, x_key, (*x)->table_id, e_key, e->table_id);
|
|
unlockEvents();
|
|
delete eptr;
|
|
return;
|
|
}
|
|
/* here we have an overlapping event */
|
|
dprintf("%s: delete 0x%012" PRIx64 ".%02x time = 0x%012" PRIx64 ".%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 ( !mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.empty() && mySIeventsOrderUniqueKey.size() >= max_events && max_events != 0 ) {
|
|
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();
|
|
}
|
|
event_id_t uniqueKey = (*lastEvent)->uniqueKey();
|
|
// else fprintf(stderr, ">");
|
|
unlockEvents();
|
|
deleteEvent(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.empty())
|
|
{
|
|
// 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.empty())
|
|
{
|
|
// 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;
|
|
}
|
|
|
|
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.empty() && mySIeventsOrderUniqueKey.size() >= max_events && max_events != 0 ) {
|
|
//TODO: 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.empty())
|
|
{
|
|
// diese beiden Mengen enthalten nur Events mit Zeiten
|
|
writeLockEvents();
|
|
mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.insert(e);
|
|
mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.insert(e);
|
|
unlockEvents();
|
|
}
|
|
}
|
|
|
|
static void removeOldEvents(const long seconds)
|
|
{
|
|
std::vector<event_id_t> to_delete;
|
|
|
|
// Alte events loeschen
|
|
time_t zeit = time(NULL);
|
|
|
|
readLockEvents();
|
|
unsigned total_events = mySIeventsOrderUniqueKey.size();
|
|
|
|
MySIeventsOrderFirstEndTimeServiceIDEventUniqueKey::iterator e = mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.begin();
|
|
|
|
while ((e != mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.end()) && (!messaging_zap_detected)) {
|
|
bool 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 (!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);
|
|
|
|
readLockEvents();
|
|
xprintf("[sectionsd] Removed %d old events (%d left), zap detected %d.\n", (int)(total_events - mySIeventsOrderUniqueKey.size()), (int)mySIeventsOrderUniqueKey.size(), messaging_zap_detected);
|
|
unlockEvents();
|
|
return;
|
|
}
|
|
|
|
//------------------------------------------------------------
|
|
// 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;
|
|
}
|
|
|
|
// Finds the next event based on unique key and start time
|
|
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;
|
|
|
|
SItimes::iterator nexttimes;
|
|
bool nextfound = false;
|
|
//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(nextfound)
|
|
{
|
|
if (t->startzeit < nexttimes->startzeit) {
|
|
eNext = e;
|
|
nexttimes = t;
|
|
}
|
|
}
|
|
else {
|
|
eNext = e;
|
|
nexttimes = t;
|
|
nextfound = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (nextnvodtimes != eFirst->second->times.end())
|
|
++nextnvodtimes;
|
|
//Compare
|
|
//if (nexttimes != eFirst->second->times.end())
|
|
if(nextfound)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
/* send back an empty response */
|
|
static void sendEmptyResponse(int connfd, char *, const unsigned)
|
|
{
|
|
struct sectionsd::msgResponseHeader msgResponse;
|
|
msgResponse.dataLength = 0;
|
|
writeNbytes(connfd, (const char *)&msgResponse, sizeof(msgResponse), WRITE_TIMEOUT_IN_SECONDS);
|
|
return;
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// connection-thread
|
|
// handles incoming requests
|
|
//---------------------------------------------------------------------
|
|
|
|
static void wakeupAll()
|
|
{
|
|
threadCN.change(0);
|
|
threadEIT.change(0);
|
|
#ifdef ENABLE_VIASATEPG
|
|
threadVSEIT.change(0);
|
|
#endif
|
|
|
|
#ifdef ENABLE_FREESATEPG
|
|
threadFSEIT.change(0);
|
|
#endif
|
|
#ifdef ENABLE_SDT
|
|
threadSDT.change(0);
|
|
#endif
|
|
}
|
|
|
|
static void commandPauseScanning(int connfd, char *data, const unsigned dataLength)
|
|
{
|
|
if (dataLength != sizeof(int))
|
|
return;
|
|
|
|
int pause = *(int *)data;
|
|
|
|
xprintf("Request of %s scanning (now %s).\n", pause ? "stop" : "continue", scanning ? "scanning" : "idle");
|
|
|
|
if (scanning && pause)
|
|
{
|
|
#if 0
|
|
threadCN.request_pause();
|
|
threadEIT.request_pause();
|
|
#ifdef ENABLE_FREESATEPG
|
|
threadFSEIT.request_pause();
|
|
#endif
|
|
#ifdef ENABLE_SDT
|
|
threadSDT.request_pause();
|
|
#endif
|
|
#endif
|
|
scanning = 0;
|
|
writeLockMessaging();
|
|
messaging_zap_detected = false;
|
|
unlockMessaging();
|
|
}
|
|
else if (!pause && !scanning)
|
|
{
|
|
#if 0
|
|
threadCN.request_unpause();
|
|
threadEIT.request_unpause();
|
|
#ifdef ENABLE_FREESATEPG
|
|
threadFSEIT.request_unpause();
|
|
#endif
|
|
#ifdef ENABLE_SDT
|
|
threadSDT.request_unpause();
|
|
#endif
|
|
#endif
|
|
writeLockEvents();
|
|
delete myCurrentEvent;
|
|
myCurrentEvent = NULL;
|
|
delete myNextEvent;
|
|
myNextEvent = NULL;
|
|
unlockEvents();
|
|
|
|
writeLockMessaging();
|
|
messaging_have_CN = 0x00;
|
|
messaging_got_CN = 0x00;
|
|
messaging_zap_detected = true;
|
|
unlockMessaging();
|
|
|
|
scanning = 1;
|
|
/* FIXME should we stop time updates if not scanning ? flag if ntp update was good ? */
|
|
//if (!ntpenable)
|
|
{
|
|
threadTIME.change(0);
|
|
}
|
|
wakeupAll();
|
|
}
|
|
sendEmptyResponse(connfd, NULL, 0);
|
|
}
|
|
|
|
static void commandserviceChanged(int connfd, char *data, const unsigned dataLength)
|
|
{
|
|
sendEmptyResponse(connfd, NULL, 0);
|
|
if (dataLength != sizeof(sectionsd::commandSetServiceChanged))
|
|
return;
|
|
|
|
sectionsd::commandSetServiceChanged * cmd = (sectionsd::commandSetServiceChanged *)data;
|
|
t_channel_id uniqueServiceKey = cmd->channel_id;
|
|
|
|
xprintf("[sectionsd] commandserviceChanged: Service change to " PRINTF_CHANNEL_ID_TYPE " demux #%d\n", uniqueServiceKey, cmd->dnum);
|
|
/* assume live demux always 0, other means background scan */
|
|
if (cmd->dnum) {
|
|
/* dont wakeup EIT, if we have max events allready */
|
|
if (max_events == 0 || (mySIeventsOrderUniqueKey.size() < max_events)) {
|
|
current_channel_id = uniqueServiceKey;
|
|
writeLockMessaging();
|
|
messaging_zap_detected = true;
|
|
unlockMessaging();
|
|
threadEIT.setDemux(cmd->dnum);
|
|
threadEIT.setCurrentService(uniqueServiceKey);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static t_channel_id time_trigger_last = 0;
|
|
|
|
if (current_channel_id != uniqueServiceKey) {
|
|
current_channel_id = uniqueServiceKey;
|
|
|
|
dvb_time_update = !checkNoDVBTimelist(uniqueServiceKey);
|
|
dprintf("[sectionsd] commandserviceChanged: DVB time update is %s\n", dvb_time_update ? "allowed" : "blocked!");
|
|
|
|
channel_is_blacklisted = checkBlacklist(uniqueServiceKey);
|
|
dprintf("[sectionsd] commandserviceChanged: service is %s\n", channel_is_blacklisted ? "filtered!" : "not filtered");
|
|
|
|
writeLockEvents();
|
|
delete myCurrentEvent;
|
|
myCurrentEvent = NULL;
|
|
delete myNextEvent;
|
|
myNextEvent = NULL;
|
|
unlockEvents();
|
|
|
|
writeLockMessaging();
|
|
messaging_current_servicekey = uniqueServiceKey & 0xFFFFFFFFFFFFULL;
|
|
messaging_have_CN = 0x00;
|
|
messaging_got_CN = 0x00;
|
|
messaging_zap_detected = true;
|
|
unlockMessaging();
|
|
|
|
threadCN.setCurrentService(messaging_current_servicekey);
|
|
threadEIT.setDemux(cmd->dnum);
|
|
threadEIT.setCurrentService(uniqueServiceKey /*messaging_current_servicekey*/);
|
|
#ifdef ENABLE_VIASATEPG
|
|
threadVSEIT.setCurrentService(messaging_current_servicekey);
|
|
#endif
|
|
#ifdef ENABLE_FREESATEPG
|
|
threadFSEIT.setCurrentService(messaging_current_servicekey);
|
|
#endif
|
|
#ifdef ENABLE_SDT
|
|
threadSDT.setCurrentService(messaging_current_servicekey);
|
|
#endif
|
|
if (time_trigger_last != (messaging_current_servicekey & 0xFFFFFFFF0000ULL)) {
|
|
time_trigger_last = messaging_current_servicekey & 0xFFFFFFFF0000ULL;
|
|
threadTIME.setCurrentService(messaging_current_servicekey);
|
|
}
|
|
}
|
|
else
|
|
dprintf("[sectionsd] commandserviceChanged: no change...\n");
|
|
|
|
dprintf("[sectionsd] commandserviceChanged: Service changed to " PRINTF_CHANNEL_ID_TYPE "\n", uniqueServiceKey);
|
|
}
|
|
|
|
static void commandserviceStopped(int connfd, char * /* data */, const unsigned /* dataLength */)
|
|
{
|
|
xprintf("[sectionsd] commandserviceStopped\n");
|
|
current_channel_id = 0;
|
|
sendEmptyResponse(connfd, NULL, 0);
|
|
threadEIT.stop();
|
|
threadCN.stop();
|
|
threadCN.stopUpdate();
|
|
xprintf("[sectionsd] commandserviceStopped done\n");
|
|
}
|
|
|
|
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 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 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,
|
|
"Current time: %s"
|
|
"Hours to cache: %ld\n"
|
|
"Hours to cache extended text: %ld\n"
|
|
"Events to cache: %u\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"
|
|
#ifdef ENABLE_FREESATEPG
|
|
"FreeSat enabled\n"
|
|
#else
|
|
""
|
|
#endif
|
|
,ctime(&zeit),
|
|
secondsToCache / (60*60L), secondsExtendedTextCache / (60*60L), max_events, oldEventsAre / 60, anzServices, anzNVODservices, anzEvents, anzNVODevents, anzMetaServices
|
|
// resourceUsage.ru_maxrss, resourceUsage.ru_ixrss, resourceUsage.ru_idrss, resourceUsage.ru_isrss,
|
|
);
|
|
printf("%s\n", stati);
|
|
#ifdef __UCLIBC__
|
|
malloc_stats(NULL);
|
|
#else
|
|
malloc_stats();
|
|
#endif
|
|
return ;
|
|
}
|
|
|
|
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*/)
|
|
{
|
|
sendEmptyResponse(connfd, NULL, 0);
|
|
|
|
struct sectionsd::commandSetConfig *pmsg;
|
|
|
|
pmsg = (struct sectionsd::commandSetConfig *)data;
|
|
|
|
/* writeLockEvents not needed because write lock will block if read lock active */
|
|
readLockEvents();
|
|
secondsToCache = (long)(pmsg->epg_cache)*24*60L*60L;
|
|
oldEventsAre = (long)(pmsg->epg_old_events)*60L*60L;
|
|
secondsExtendedTextCache = (long)(pmsg->epg_extendedcache)*60L*60L;
|
|
max_events = pmsg->epg_max_events;
|
|
epg_save_frequently = pmsg->epg_save_frequently;
|
|
epg_read_frequently = pmsg->epg_read_frequently;
|
|
|
|
unlockEvents();
|
|
|
|
bool time_wakeup = false;
|
|
if (ntpserver.compare((std::string)&data[sizeof(struct sectionsd::commandSetConfig)])) {
|
|
time_wakeup = true;
|
|
}
|
|
if (ntprefresh != pmsg->network_ntprefresh) {
|
|
dprintf("new network_ntprefresh = %d\n", pmsg->network_ntprefresh);
|
|
time_wakeup = true;
|
|
}
|
|
if (ntpenable ^ (pmsg->network_ntpenable == 1)) {
|
|
dprintf("new network_ntpenable = %d\n", pmsg->network_ntpenable);
|
|
time_wakeup = true;
|
|
}
|
|
|
|
if (time_wakeup) {
|
|
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;
|
|
ntprefresh = pmsg->network_ntprefresh;
|
|
ntpenable = (pmsg->network_ntpenable == 1);
|
|
if(timeset)
|
|
threadTIME.change(1);
|
|
}
|
|
|
|
epg_dir= (std::string)&data[sizeof(struct sectionsd::commandSetConfig) + strlen(&data[sizeof(struct sectionsd::commandSetConfig)]) + 1];
|
|
}
|
|
|
|
static void deleteSIexceptEPG()
|
|
{
|
|
threadCN.dropCachedSectionIDs();
|
|
threadEIT.dropCachedSectionIDs();
|
|
#ifdef ENABLE_SDT
|
|
writeLockServices();
|
|
mySIservicesOrderUniqueKey.clear();
|
|
unlockServices();
|
|
threadSDT.dropCachedSectionIDs();
|
|
#endif
|
|
}
|
|
|
|
static void FreeMemory()
|
|
{
|
|
xprintf("[sectionsd] free memory...\n");
|
|
deleteSIexceptEPG();
|
|
|
|
writeLockEvents();
|
|
|
|
#ifndef USE_BOOST_SHARED_PTR
|
|
std::set<SIeventPtr> allevents;
|
|
|
|
allevents.insert(mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.begin(), mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.end());
|
|
/* this probably not needed, but takes only additional ~2 seconds
|
|
* with even count > 70000 */
|
|
allevents.insert(mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.begin(), mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.end());
|
|
MySIeventsOrderUniqueKey::iterator it;
|
|
for(it = mySIeventsOrderUniqueKey.begin(); it != mySIeventsOrderUniqueKey.end(); ++it)
|
|
allevents.insert(it->second);
|
|
for(it = mySIeventsNVODorderUniqueKey.begin(); it != mySIeventsNVODorderUniqueKey.end(); ++it)
|
|
allevents.insert(it->second);
|
|
|
|
for(std::set<SIeventPtr>::iterator ait = allevents.begin(); ait != allevents.end(); ++ait)
|
|
delete (*ait);
|
|
#endif
|
|
mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.clear();
|
|
mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.clear();
|
|
mySIeventsOrderUniqueKey.clear();
|
|
mySIeventsNVODorderUniqueKey.clear();
|
|
|
|
unlockEvents();
|
|
|
|
#ifdef __UCLIBC__
|
|
malloc_stats(NULL);
|
|
#else
|
|
malloc_stats();
|
|
#endif
|
|
xprintf("[sectionsd] free memory done\n");
|
|
//wakeupAll(); //FIXME should we re-start eit here ?
|
|
}
|
|
|
|
static void commandFreeMemory(int connfd, char * /*data*/, const unsigned /*dataLength*/)
|
|
{
|
|
sendEmptyResponse(connfd, NULL, 0);
|
|
FreeMemory();
|
|
}
|
|
|
|
static void commandReadSIfromXML(int connfd, char *data, const unsigned dataLength)
|
|
{
|
|
pthread_t thrInsert;
|
|
|
|
sendEmptyResponse(connfd, NULL, 0);
|
|
|
|
if (dataLength > 100)
|
|
return ;
|
|
|
|
writeLockMessaging();
|
|
data[dataLength] = '\0';
|
|
static std::string epg_dir_tmp = (std::string)data + "/";
|
|
unlockMessaging();
|
|
|
|
|
|
pthread_attr_t attr;
|
|
pthread_attr_init(&attr);
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
|
|
if (pthread_create (&thrInsert, &attr, insertEventsfromFile, (void *)epg_dir_tmp.c_str() ))
|
|
{
|
|
perror("sectionsd: pthread_create()");
|
|
}
|
|
|
|
pthread_attr_destroy(&attr);
|
|
}
|
|
|
|
static void commandWriteSI2XML(int connfd, char *data, const unsigned dataLength)
|
|
{
|
|
sendEmptyResponse(connfd, NULL, 0);
|
|
if (mySIeventsOrderUniqueKey.empty() || (!reader_ready) || (dataLength > 100)){
|
|
eventServer->sendEvent(CSectionsdClient::EVT_WRITE_SI_FINISHED, CEventServer::INITID_SECTIONSD);
|
|
return;
|
|
}
|
|
|
|
data[dataLength] = '\0';
|
|
|
|
writeEventsToFile(data);
|
|
|
|
eventServer->sendEvent(CSectionsdClient::EVT_WRITE_SI_FINISHED, CEventServer::INITID_SECTIONSD);
|
|
}
|
|
|
|
struct s_cmd_table
|
|
{
|
|
void (*cmd)(int connfd, char *, const unsigned);
|
|
std::string sCmd;
|
|
};
|
|
|
|
static s_cmd_table connectionCommands[sectionsd::numberOfCommands] = {
|
|
{ commandDumpStatusInformation, "commandDumpStatusInformation" },
|
|
{ commandPauseScanning, "commandPauseScanning" },
|
|
{ commandGetIsScanningActive, "commandGetIsScanningActive" },
|
|
{ commandGetIsTimeSet, "commandGetIsTimeSet" },
|
|
{ commandserviceChanged, "commandserviceChanged" },
|
|
{ commandserviceStopped, "commandserviceStopped" },
|
|
{ commandRegisterEventClient, "commandRegisterEventClient" },
|
|
{ commandUnRegisterEventClient, "commandUnRegisterEventClient" },
|
|
{ commandFreeMemory, "commandFreeMemory" },
|
|
{ commandReadSIfromXML, "commandReadSIfromXML" },
|
|
{ commandWriteSI2XML, "commandWriteSI2XML" },
|
|
{ commandSetConfig, "commandSetConfig" },
|
|
};
|
|
|
|
bool sectionsd_parse_command(CBasicMessage::Header &rmsg, int connfd)
|
|
{
|
|
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());
|
|
if (connectionCommands[header.command].cmd == sendEmptyResponse)
|
|
printf("sectionsd_parse_command: UNUSED cmd used: %d (%s)\n", header.command, connectionCommands[header.command].sCmd.c_str());
|
|
connectionCommands[header.command].cmd(connfd, data, header.dataLength);
|
|
}
|
|
|
|
delete[] data;
|
|
}
|
|
}
|
|
else
|
|
dputs("Unknown format or version of request!");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void dump_sched_info(std::string label)
|
|
{
|
|
int policy;
|
|
struct sched_param parm;
|
|
int rc = pthread_getschedparam(pthread_self(), &policy, &parm);
|
|
printf("%s: getschedparam %d policy %d prio %d\n", label.c_str(), rc, policy, parm.sched_priority);
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Time-thread
|
|
// updates system time according TOT every 30 minutes
|
|
//---------------------------------------------------------------------
|
|
|
|
CTimeThread::CTimeThread()
|
|
: CSectionThread("timeThread", 0x14)
|
|
{
|
|
timeoutInMSeconds = 36000;
|
|
cache = false;
|
|
wait_for_time = false;
|
|
|
|
first_time = true;
|
|
time_ntp = false;
|
|
};
|
|
|
|
void CTimeThread::sendTimeEvent(bool ntp, time_t tim)
|
|
{
|
|
#if 0
|
|
time_t actTime = time(NULL);
|
|
if (!ntp) {
|
|
struct tm *tmTime = localtime(&actTime);
|
|
xprintf("%s: current: %02d.%02d.%04d %02d:%02d:%02d, dvb: %s", name.c_str(),
|
|
tmTime->tm_mday, tmTime->tm_mon+1, tmTime->tm_year+1900, tmTime->tm_hour, tmTime->tm_min, tmTime->tm_sec, ctime(&tim));
|
|
actTime = tim;
|
|
}
|
|
eventServer->sendEvent(CSectionsdClient::EVT_TIMESET, CEventServer::INITID_SECTIONSD, &actTime, sizeof(actTime) );
|
|
#endif
|
|
if(ntp || tim) {}
|
|
if (timediff)
|
|
eventServer->sendEvent(CSectionsdClient::EVT_TIMESET, CEventServer::INITID_SECTIONSD, &timediff, sizeof(timediff));
|
|
setTimeSet();
|
|
}
|
|
|
|
void CTimeThread::setTimeSet()
|
|
{
|
|
time_mutex.lock();
|
|
timeset = true;
|
|
time_cond.broadcast();
|
|
time_mutex.unlock();
|
|
}
|
|
|
|
void CTimeThread::waitForTimeset(void)
|
|
{
|
|
time_mutex.lock();
|
|
while(!timeset)
|
|
time_cond.wait(&time_mutex);
|
|
time_mutex.unlock();
|
|
}
|
|
|
|
void CTimeThread::setSystemTime(time_t tim)
|
|
{
|
|
struct timeval tv;
|
|
struct tm t;
|
|
|
|
gettimeofday(&tv, NULL);
|
|
timediff = int64_t(tim * 1000000 - (tv.tv_usec + tv.tv_sec * 1000000));
|
|
localtime_r(&tv.tv_sec, &t);
|
|
|
|
xprintf("%s: timediff %" PRId64 ", current: %02d.%02d.%04d %02d:%02d:%02d, dvb: %s",
|
|
name.c_str(), timediff,
|
|
t.tm_mday, t.tm_mon+1, t.tm_year+1900, t.tm_hour, t.tm_min, t.tm_sec, ctime(&tim));
|
|
#if 0
|
|
/* if new time less than current for less than 1 second, ignore */
|
|
if(timediff < 0 && timediff > (int64_t) -1000000) {
|
|
timediff = 0;
|
|
return;
|
|
}
|
|
#endif
|
|
if (timediff == 0) /* very unlikely... :-) */
|
|
return;
|
|
if (timeset && abs(tim - tv.tv_sec) < 120) { /* abs() is int */
|
|
struct timeval oldd;
|
|
tv.tv_sec = time_t(timediff / 1000000LL);
|
|
tv.tv_usec = suseconds_t(timediff % 1000000LL);
|
|
if (adjtime(&tv, &oldd))
|
|
xprintf("adjtime(%d, %d) failed: %m\n", (int)tv.tv_sec, (int)tv.tv_usec);
|
|
else {
|
|
xprintf("difference is < 120s, using adjtime(%d, %d). oldd(%d, %d)\n",
|
|
(int)tv.tv_sec, (int)tv.tv_usec, (int)oldd.tv_sec, (int)oldd.tv_usec);
|
|
timediff = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
tv.tv_sec = tim;
|
|
tv.tv_usec = 0;
|
|
if (settimeofday(&tv, NULL) < 0)
|
|
perror("[sectionsd] settimeofday");
|
|
}
|
|
|
|
void CTimeThread::addFilters()
|
|
{
|
|
addfilter(0x70, 0xff);
|
|
addfilter(0x73, 0xff);
|
|
}
|
|
|
|
void CTimeThread::run()
|
|
{
|
|
time_t dvb_time = 0;
|
|
xprintf("%s::run:: starting, pid %d (%lu)\n", name.c_str(), getpid(), pthread_self());
|
|
|
|
addFilters();
|
|
DMX::start();
|
|
|
|
while(running) {
|
|
if(sendToSleepNow) {
|
|
#ifdef DEBUG_TIME_THREAD
|
|
xprintf("%s: going to sleep %d seconds, running %d scanning %d\n",
|
|
name.c_str(), sleep_time, running, scanning);
|
|
#endif
|
|
do {
|
|
if(!scanning)
|
|
sleep_time = 0;
|
|
real_pause();
|
|
#ifndef DEBUG_TIME_THREAD
|
|
Sleep();
|
|
#else
|
|
int rs = Sleep();
|
|
xprintf("%s: wakeup, running %d scanning %d channel %" PRIx64 " reason %d\n",
|
|
name.c_str(), running, scanning, current_service, rs);
|
|
#endif
|
|
} while (running && !scanning);
|
|
|
|
if (!running)
|
|
break;
|
|
}
|
|
bool success = false;
|
|
time_ntp = false;
|
|
dvb_time = 0;
|
|
timediff = 0;
|
|
|
|
if (ntpenable && system( ntp_system_cmd.c_str() ) == 0) {
|
|
time_ntp = true;
|
|
success = true;
|
|
} else if (dvb_time_update) {
|
|
if(!first_time)
|
|
change(1);
|
|
else
|
|
change(0);
|
|
|
|
xprintf("%s: get DVB time ch 0x%012" PRIx64 " (isOpen %d)\n",
|
|
name.c_str(), current_service, isOpen());
|
|
int rc = dmx->Read(static_buf, MAX_SECTION_LENGTH, timeoutInMSeconds);
|
|
xprintf("%s: get DVB time ch 0x%012" PRIx64 " rc: %d neutrino_sets_time %d\n",
|
|
name.c_str(), current_service, rc, messaging_neutrino_sets_time);
|
|
if (rc > 0) {
|
|
SIsectionTIME st(static_buf);
|
|
if (st.is_parsed()) {
|
|
dvb_time = st.getTime();
|
|
success = true;
|
|
}
|
|
}
|
|
}
|
|
/* default sleep time */
|
|
sleep_time = ntprefresh * 60;
|
|
if(success) {
|
|
if(dvb_time) {
|
|
setSystemTime(dvb_time);
|
|
/* retry a second time immediately after start, to get TOT ? */
|
|
if(first_time)
|
|
sleep_time = 5;
|
|
}
|
|
/* in case of wrong TDT date, dont send time is set, 1325376000 - 01.01.2012 */
|
|
if(time_ntp || (dvb_time > (time_t) 1325376000)) {
|
|
sendTimeEvent(time_ntp, dvb_time);
|
|
xprintf("%s: Time set via %s, going to sleep for %d seconds.\n", name.c_str(),
|
|
time_ntp ? "NTP" : first_time ? "DVB (TDT)" : "DVB (TOT)", sleep_time);
|
|
}
|
|
first_time = false;
|
|
} else {
|
|
xprintf("%s: Time set FAILED (enabled: ntp %d dvb %d)\n", name.c_str(), ntpenable, dvb_time_update);
|
|
if(!timeset && first_time)
|
|
sleep_time = 1;
|
|
}
|
|
sendToSleepNow = true;
|
|
}
|
|
delete[] static_buf;
|
|
printf("[sectionsd] timeThread stopped\n");
|
|
}
|
|
|
|
/********************************************************************************/
|
|
/* abstract CSectionThread functions */
|
|
/********************************************************************************/
|
|
/* sleep for sleep_time seconds, forever if sleep_time = 0 */
|
|
int CSectionThread::Sleep()
|
|
{
|
|
int rs;
|
|
struct timespec abs_wait;
|
|
struct timeval now;
|
|
|
|
if (sleep_time) {
|
|
gettimeofday(&now, NULL);
|
|
TIMEVAL_TO_TIMESPEC(&now, &abs_wait);
|
|
abs_wait.tv_sec += sleep_time;
|
|
}
|
|
dprintf("%s: going to sleep for %d seconds...\n", name.c_str(), sleep_time);
|
|
pthread_mutex_lock(&start_stop_mutex);
|
|
|
|
beforeWait();
|
|
if (sleep_time)
|
|
rs = pthread_cond_timedwait( &change_cond, &start_stop_mutex, &abs_wait );
|
|
else
|
|
rs = pthread_cond_wait(&change_cond, &start_stop_mutex);
|
|
|
|
afterWait();
|
|
|
|
pthread_mutex_unlock( &start_stop_mutex );
|
|
return rs;
|
|
}
|
|
|
|
/* common thread main function */
|
|
void CSectionThread::run()
|
|
{
|
|
xprintf("%s::run:: starting, pid %d (%lu)\n", name.c_str(), getpid(), pthread_self());
|
|
if (sections_debug)
|
|
dump_sched_info(name);
|
|
|
|
addFilters();
|
|
|
|
real_pauseCounter = 1;
|
|
if (wait_for_time) {
|
|
threadTIME.waitForTimeset();
|
|
time_t now = time(NULL);
|
|
xprintf("%s::run:: time set: %s", name.c_str(), ctime(&now));
|
|
}
|
|
real_pauseCounter = 0;
|
|
|
|
DMX::start();
|
|
|
|
while (running) {
|
|
if (shouldSleep()) {
|
|
#ifdef DEBUG_SECTION_THREADS
|
|
xprintf("%s: going to sleep %d seconds, running %d scanning %d blacklisted %d events %d\n",
|
|
name.c_str(), sleep_time, running, scanning, channel_is_blacklisted, event_count);
|
|
#endif
|
|
event_count = 0;
|
|
|
|
beforeSleep();
|
|
int rs = 0;
|
|
do {
|
|
real_pause();
|
|
rs = Sleep();
|
|
#ifdef DEBUG_SECTION_THREADS
|
|
xprintf("%s: wakeup, running %d scanning %d blacklisted %d reason %d\n",
|
|
name.c_str(), running, scanning, channel_is_blacklisted, rs);
|
|
#endif
|
|
} while (checkSleep());
|
|
|
|
if (!running)
|
|
break;
|
|
|
|
afterSleep();
|
|
|
|
if (rs == ETIMEDOUT)
|
|
change(0); // -> restart, FIXME
|
|
|
|
sendToSleepNow = false;
|
|
}
|
|
|
|
processSection();
|
|
|
|
time_t zeit = time_monotonic();
|
|
bool need_change = false;
|
|
|
|
if (timeoutsDMX < 0 || timeoutsDMX >= skipTimeouts) {
|
|
#ifdef DEBUG_SECTION_THREADS
|
|
xprintf("%s: skipping to next filter %d from %d (timeouts %d)\n",
|
|
name.c_str(), filter_index+1, (int)filters.size(), timeoutsDMX);
|
|
#endif
|
|
if (timeoutsDMX == -3)
|
|
sendToSleepNow = true;
|
|
else
|
|
need_change = true;
|
|
|
|
timeoutsDMX = 0;
|
|
}
|
|
if (zeit > lastChanged + skipTime) {
|
|
#ifdef DEBUG_SECTION_THREADS
|
|
xprintf("%s: skipping to next filter %d from %d (seconds %d)\n",
|
|
name.c_str(), filter_index+1, (int)filters.size(), (int)(zeit - lastChanged));
|
|
#endif
|
|
need_change = true;
|
|
}
|
|
if (running && need_change && scanning) {
|
|
readLockMessaging();
|
|
if (!next_filter())
|
|
sendToSleepNow = true;
|
|
unlockMessaging();
|
|
}
|
|
} // while running
|
|
delete[] static_buf;
|
|
cleanup();
|
|
printf("[sectionsd] %s stopped\n", name.c_str());
|
|
}
|
|
|
|
/********************************************************************************/
|
|
/* abstract CEventsThread functions */
|
|
/********************************************************************************/
|
|
bool CEventsThread::addEvents()
|
|
{
|
|
SIsectionEIT eit(static_buf);
|
|
|
|
if (!eit.is_parsed())
|
|
return false;
|
|
|
|
dprintf("[%s] adding %d events (begin)\n", name.c_str(), (int)eit.events().size());
|
|
time_t zeit = time(NULL);
|
|
|
|
for (SIevents::const_iterator e = eit.events().begin(); e != eit.events().end(); ++e) {
|
|
if (!(e->times.empty())) {
|
|
#if 0
|
|
if ( ( e->times.begin()->startzeit < zeit + secondsToCache ) &&
|
|
( ( e->times.begin()->startzeit + (long)e->times.begin()->dauer ) > zeit - oldEventsAre ) &&
|
|
( e->times.begin()->dauer < 60 ) ) {
|
|
char x_startTime[10];
|
|
struct tm *x_tmStartTime = localtime(&e->times.begin()->startzeit);
|
|
strftime(x_startTime, sizeof(x_startTime)-1, "%H:%M", x_tmStartTime );
|
|
printf("####[%s - #%d] - startzeit: %s, dauer: %d, channel_id: 0x%llX\n", __FUNCTION__, __LINE__, x_startTime, e->times.begin()->dauer, e->get_channel_id());
|
|
}
|
|
#endif
|
|
if ( ( e->times.begin()->startzeit < zeit + secondsToCache ) &&
|
|
( ( e->times.begin()->startzeit + (long)e->times.begin()->dauer ) > zeit - oldEventsAre ) &&
|
|
( e->times.begin()->dauer > 1 ) )
|
|
{
|
|
addEvent(*e, wait_for_time ? zeit: 0, e->table_id == 0x4e);
|
|
event_count++;
|
|
}
|
|
} 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
|
|
return true;
|
|
}
|
|
|
|
bool CCNThread::shouldSleep()
|
|
{
|
|
if (!scanning || channel_is_blacklisted)
|
|
return true;
|
|
if (!sendToSleepNow)
|
|
return false;
|
|
if (eit_version != 0xff)
|
|
return true;
|
|
|
|
if (++eit_retry > 1) {
|
|
xprintf("%s::%s eit_retry > 1 (%d) -> going to sleep\n", name.c_str(), __func__, eit_retry);
|
|
return true;
|
|
}
|
|
sendToSleepNow = false;
|
|
return false;
|
|
}
|
|
|
|
/* default check if thread should go to sleep */
|
|
bool CEventsThread::shouldSleep()
|
|
{
|
|
return (sendToSleepNow || !scanning || channel_is_blacklisted);
|
|
}
|
|
|
|
/* default check if thread should continue to sleep */
|
|
bool CEventsThread::checkSleep()
|
|
{
|
|
return (running && (!scanning || channel_is_blacklisted));
|
|
}
|
|
|
|
/* default section process */
|
|
void CEventsThread::processSection()
|
|
{
|
|
int rc = getSection(static_buf, timeoutInMSeconds, timeoutsDMX);
|
|
if (rc <= 0)
|
|
return;
|
|
addEvents();
|
|
}
|
|
|
|
/********************************************************************************/
|
|
/* EIT thread to read other TS CN + all scheduled events */
|
|
/********************************************************************************/
|
|
CEitThread::CEitThread()
|
|
: CEventsThread("eitThread")
|
|
{
|
|
}
|
|
|
|
CEitThread::CEitThread(std::string tname, unsigned short pid)
|
|
: CEventsThread(tname, pid)
|
|
{
|
|
}
|
|
|
|
/* EIT thread hooks */
|
|
void CEitThread::addFilters()
|
|
{
|
|
/* 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)
|
|
*/
|
|
//addfilter(0x00, 0x00); //0 dummy filter
|
|
addfilter(0x50, 0xf0); //1 current TS, scheduled
|
|
addfilter(0x4f, 0xff); //2 other TS, current/next
|
|
#if 1
|
|
addfilter(0x60, 0xf1); //3a other TS, scheduled, even
|
|
addfilter(0x61, 0xf1); //3b other TS, scheduled, odd
|
|
#else
|
|
addfilter(0x60, 0xf0); //3 other TS, scheduled
|
|
#endif
|
|
}
|
|
|
|
void CEitThread::beforeSleep()
|
|
{
|
|
writeLockMessaging();
|
|
messaging_zap_detected = false;
|
|
unlockMessaging();
|
|
if (scanning && current_channel_id) {
|
|
eventServer->sendEvent(CSectionsdClient::EVT_EIT_COMPLETE,
|
|
CEventServer::INITID_SECTIONSD,
|
|
¤t_service,
|
|
sizeof(messaging_current_servicekey));
|
|
}
|
|
if(notify_complete)
|
|
system(CONFIGDIR "/epgdone.sh");
|
|
}
|
|
|
|
/********************************************************************************/
|
|
/* CN thread to read current TS CN events */
|
|
/********************************************************************************/
|
|
CCNThread::CCNThread()
|
|
: CEventsThread("cnThread")
|
|
{
|
|
sleep_time = 0;
|
|
cache = false;
|
|
skipTimeouts = 5000 / timeoutInMSeconds; // 5 seconds
|
|
skipTime = TIME_EIT_VERSION_WAIT;
|
|
|
|
updating = false;
|
|
eitDmx = new cDemux(0);
|
|
eit_retry = 0;
|
|
}
|
|
|
|
/* CN thread hooks */
|
|
void CCNThread::cleanup()
|
|
{
|
|
delete eitDmx;
|
|
}
|
|
|
|
void CCNThread::addFilters()
|
|
{
|
|
addfilter(0x4e, 0xff); //0 current TS, current/next
|
|
}
|
|
|
|
void CCNThread::beforeWait()
|
|
{
|
|
xprintf("%s: eit update filter, ch 0x%012" PRIx64 ", current ver 0x%02x got events %d (%s)\n",
|
|
name.c_str(), messaging_current_servicekey, eit_version, messaging_have_CN,
|
|
updating ? "active" : "not active");
|
|
|
|
if (updating || eit_version == 0xff)
|
|
return;
|
|
|
|
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)(current_service >> 8);
|
|
filter[2] = (unsigned char) current_service;
|
|
|
|
mask[0] = 0xFF;
|
|
mask[1] = 0xFF;
|
|
mask[2] = 0xFF;
|
|
|
|
filter[3] = (eit_version << 1) | 0x01;
|
|
mask[3] = (0x1F << 1) | 0x01;
|
|
mode[3] = 0x1F << 1;
|
|
|
|
update_mutex.lock();
|
|
eitDmx->Open(DMX_PSI_CHANNEL);
|
|
eitDmx->sectionFilter(0x12, filter, mask, 4, 0 /*timeout*/, mode);
|
|
updating = true;
|
|
update_mutex.unlock();
|
|
}
|
|
|
|
void CCNThread::stopUpdate()
|
|
{
|
|
xprintf("%s: stop eit update filter (%s)\n", name.c_str(), updating ? "active" : "not active");
|
|
update_mutex.lock();
|
|
if (updating) {
|
|
updating = false;
|
|
eitDmx->Close();
|
|
}
|
|
update_mutex.unlock();
|
|
}
|
|
|
|
void CCNThread::afterWait()
|
|
{
|
|
stopUpdate();
|
|
}
|
|
|
|
void CCNThread::beforeSleep()
|
|
{
|
|
if (sendToSleepNow && messaging_have_CN == 0x00) {
|
|
/* send a "no epg" event anyway before going to sleep */
|
|
sendCNEvent();
|
|
}
|
|
eit_retry = 0;
|
|
}
|
|
|
|
void CCNThread::processSection()
|
|
{
|
|
int rc = getSection(static_buf, timeoutInMSeconds, timeoutsDMX);
|
|
if (rc <= 0)
|
|
return;
|
|
|
|
addEvents();
|
|
readLockMessaging();
|
|
if (messaging_got_CN != messaging_have_CN) {
|
|
unlockMessaging();
|
|
writeLockMessaging();
|
|
messaging_have_CN = messaging_got_CN;
|
|
unlockMessaging();
|
|
|
|
#ifdef DEBUG_CN_THREAD
|
|
xprintf("%s: have CN: timeoutsDMX %d messaging_have_CN %x messaging_got_CN %x\n",
|
|
name.c_str(), timeoutsDMX, messaging_have_CN, messaging_got_CN);
|
|
#endif
|
|
dprintf("[cnThread] got current_next (0x%x) - sending event!\n", messaging_have_CN);
|
|
sendCNEvent();
|
|
lastChanged = time_monotonic();
|
|
}
|
|
else
|
|
unlockMessaging();
|
|
}
|
|
|
|
/* CN specific functions */
|
|
bool CCNThread::checkUpdate()
|
|
{
|
|
unsigned char buf[MAX_SECTION_LENGTH];
|
|
|
|
update_mutex.lock();
|
|
if (!updating) {
|
|
update_mutex.unlock();
|
|
return false;
|
|
}
|
|
|
|
int ret = eitDmx->Read(buf, MAX_SECTION_LENGTH, 10);
|
|
update_mutex.unlock();
|
|
|
|
if (ret > 0) {
|
|
LongSection section(buf);
|
|
xprintf("%s: eit update filter: ### new version 0x%02x ###, Activate thread\n",
|
|
name.c_str(), section.getVersionNumber());
|
|
|
|
writeLockMessaging();
|
|
messaging_have_CN = 0x00;
|
|
messaging_got_CN = 0x00;
|
|
unlockMessaging();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CCNThread::sendCNEvent()
|
|
{
|
|
eventServer->sendEvent(CSectionsdClient::EVT_GOT_CN_EPG,
|
|
CEventServer::INITID_SECTIONSD,
|
|
&messaging_current_servicekey,
|
|
sizeof(messaging_current_servicekey));
|
|
}
|
|
|
|
#ifdef ENABLE_FREESATEPG
|
|
/********************************************************************************/
|
|
/* Freesat EIT thread */
|
|
/********************************************************************************/
|
|
CFreeSatThread::CFreeSatThread()
|
|
: CEventsThread("freeSatThread", 3842)
|
|
{
|
|
skipTime = TIME_FSEIT_SKIPPING;
|
|
};
|
|
|
|
/* Freesat hooks */
|
|
void CFreeSatThread::addFilters()
|
|
{
|
|
//other TS, scheduled, freesat epg is only broadcast using table_ids 0x60 (scheduled) and 0x61 (scheduled later)
|
|
addfilter(0x60, 0xfe);
|
|
}
|
|
#endif
|
|
|
|
#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);
|
|
|
|
sptr->is_actual = is_actual;
|
|
|
|
writeLockServices();
|
|
mySIservicesOrderUniqueKey.insert(std::make_pair(sptr->uniqueKey(), sptr));
|
|
unlockServices();
|
|
|
|
if (!sptr->nvods.empty())
|
|
{
|
|
writeLockServices();
|
|
mySIservicesNVODorderUniqueKey.insert(std::make_pair(sptr->uniqueKey(), sptr));
|
|
unlockServices();
|
|
}
|
|
is_new = true;
|
|
}
|
|
return is_new;
|
|
}
|
|
|
|
/********************************************************************************/
|
|
/* SDT thread to cache actual/other TS service ids */
|
|
/********************************************************************************/
|
|
CSdtThread::CSdtThread()
|
|
: CSectionThread("sdtThread", 0x11)
|
|
{
|
|
skipTime = TIME_SDT_NONEWDATA;
|
|
sleep_time = TIME_SDT_SCHEDULED_PAUSE;
|
|
cache = false;
|
|
wait_for_time = false;
|
|
};
|
|
|
|
/* SDT thread hooks */
|
|
void CSdtThread::addFilters()
|
|
{
|
|
addfilter(0x42, 0xfb ); //SDT actual = 0x42 + SDT other = 0x46
|
|
}
|
|
|
|
bool CSdtThread::shouldSleep()
|
|
{
|
|
return (sendToSleepNow || !scanning);
|
|
}
|
|
|
|
bool CSdtThread::checkSleep()
|
|
{
|
|
return (running && !scanning);
|
|
}
|
|
|
|
void CSdtThread::processSection()
|
|
{
|
|
int rc = getSection(static_buf, timeoutInMSeconds, timeoutsDMX);
|
|
if (rc <= 0)
|
|
return;
|
|
|
|
if (addServices())
|
|
lastChanged = time_monotonic();
|
|
}
|
|
|
|
/* SDT specific */
|
|
bool CSdtThread::addServices()
|
|
{
|
|
bool is_new = false;
|
|
|
|
LongSection sec(static_buf);
|
|
uint8_t table_id = sec.getTableId();
|
|
if ((table_id == 0x42) || (table_id == 0x46)) {
|
|
SIsectionSDT sdt(static_buf);
|
|
|
|
bool is_actual = (sdt.getTableId() == 0x42) ? 1 : 0;
|
|
|
|
if (is_actual && !sdt.getLastSectionNumber())
|
|
is_actual = 2;
|
|
|
|
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;
|
|
event_count++;
|
|
}
|
|
}
|
|
return is_new;
|
|
}
|
|
#endif
|
|
|
|
/* helper function for the housekeeping-thread */
|
|
static void print_meminfo(void)
|
|
{
|
|
if (!sections_debug)
|
|
return;
|
|
|
|
#ifdef __UCLIBC__
|
|
malloc_stats(NULL);
|
|
#else
|
|
malloc_stats();
|
|
#endif
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// housekeeping-thread
|
|
// does cleaning on fetched datas
|
|
//---------------------------------------------------------------------
|
|
static void *houseKeepingThread(void *)
|
|
{
|
|
int count = 0, scount = 0, ecount = 0;
|
|
set_threadname("sd:housekeeping");
|
|
|
|
dprintf("housekeeping-thread started.\n");
|
|
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
|
|
|
|
while (!sectionsd_stop)
|
|
{
|
|
if (count == META_HOUSEKEEPING_COUNT) {
|
|
dprintf("meta housekeeping - deleting all transponders, services, bouquets.\n");
|
|
deleteSIexceptEPG();
|
|
count = 0;
|
|
}
|
|
|
|
int rc = HOUSEKEEPING_SLEEP;
|
|
|
|
while (rc)
|
|
rc = sleep(rc);
|
|
|
|
if (!scanning) {
|
|
scount++;
|
|
if (scount < STANDBY_HOUSEKEEPING_COUNT)
|
|
continue;
|
|
}
|
|
scount = 0;
|
|
|
|
dprintf("housekeeping.\n");
|
|
|
|
removeOldEvents(oldEventsAre); // alte Events
|
|
|
|
ecount++;
|
|
if (ecount == EPG_SERVICE_FREQUENTLY_COUNT)
|
|
{
|
|
if (epg_save_frequently > 0)
|
|
{
|
|
std::string d = epg_dir;
|
|
if (d.length() > 1)
|
|
{
|
|
std::string::iterator it = d.end() - 1;
|
|
if (*it == '/')
|
|
d.erase(it);
|
|
}
|
|
writeEventsToFile(d.c_str());
|
|
}
|
|
if (epg_read_frequently > 0)
|
|
{
|
|
pthread_t thrInsert;
|
|
pthread_attr_t attr;
|
|
pthread_attr_init(&attr);
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
std::string d = epg_dir + "/";
|
|
|
|
printf("[%s]: %s\n",__func__,d.c_str());
|
|
|
|
if (pthread_create (&thrInsert, &attr, insertEventsfromFile, (void *)d.c_str() ))
|
|
{
|
|
perror("sectionsd: pthread_create()");
|
|
}
|
|
pthread_attr_destroy(&attr);
|
|
}
|
|
ecount = 0;
|
|
}
|
|
|
|
readLockEvents();
|
|
dprintf("Number of sptr events (event-ID): %u\n", (unsigned)mySIeventsOrderUniqueKey.size());
|
|
dprintf("Number of sptr events (service-id, start time, event-id): %u\n", (unsigned)mySIeventsOrderServiceUniqueKeyFirstStartTimeEventUniqueKey.size());
|
|
dprintf("Number of sptr events (end time, service-id, event-id): %u\n", (unsigned)mySIeventsOrderFirstEndTimeServiceIDEventUniqueKey.size());
|
|
dprintf("Number of sptr nvod events (event-ID): %u\n", (unsigned)mySIeventsNVODorderUniqueKey.size());
|
|
dprintf("Number of cached meta-services: %u\n", (unsigned)mySIeventUniqueKeysMetaOrderServiceUniqueKey.size());
|
|
unlockEvents();
|
|
|
|
print_meminfo();
|
|
|
|
count++;
|
|
} // for endlos
|
|
dprintf("housekeeping-thread ended.\n");
|
|
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
CEitManager* CEitManager::manager = NULL;
|
|
OpenThreads::Mutex CEitManager::m;
|
|
|
|
CEitManager::CEitManager()
|
|
{
|
|
running = false;
|
|
}
|
|
|
|
CEitManager::~CEitManager()
|
|
{
|
|
}
|
|
|
|
CEitManager * CEitManager::getInstance()
|
|
{
|
|
m.lock();
|
|
if(manager == NULL)
|
|
manager = new CEitManager();
|
|
m.unlock();
|
|
return manager;
|
|
}
|
|
|
|
bool CEitManager::Start()
|
|
{
|
|
xprintf("[sectionsd] start\n");
|
|
if(running)
|
|
return false;
|
|
|
|
ntpserver = config.network_ntpserver;
|
|
ntprefresh = config.network_ntprefresh;
|
|
ntpenable = config.network_ntpenable;
|
|
ntp_system_cmd = ntp_system_cmd_prefix + ntpserver;
|
|
|
|
secondsToCache = config.epg_cache *24*60L*60L; //days
|
|
secondsExtendedTextCache = config.epg_extendedcache*60L*60L; //hours
|
|
oldEventsAre = config.epg_old_events*60L*60L; //hours
|
|
max_events = config.epg_max_events;
|
|
epg_save_frequently = config.epg_save_frequently;
|
|
epg_read_frequently = config.epg_read_frequently;
|
|
|
|
if (find_executable("ntpdate").empty()){
|
|
ntp_system_cmd_prefix = find_executable("ntpd");
|
|
if (!ntp_system_cmd_prefix.empty()){
|
|
ntp_system_cmd_prefix += " -n -q -p ";
|
|
ntp_system_cmd = ntp_system_cmd_prefix + ntpserver;
|
|
}
|
|
else{
|
|
printf("[sectionsd] NTP Error: time sync not possible, ntpdate/ntpd not found\n");
|
|
ntpenable = false;
|
|
}
|
|
}
|
|
|
|
printf("[sectionsd] Caching: %d days, %d hours Extended Text, max %d events, Events are old %d hours after end time\n",
|
|
config.epg_cache, config.epg_extendedcache, config.epg_max_events, config.epg_old_events);
|
|
printf("[sectionsd] NTP: %s, command %s\n", ntpenable ? "enabled" : "disabled", ntp_system_cmd.c_str());
|
|
|
|
xml_epg_filter = readEPGFilter();
|
|
|
|
if (!sectionsd_server.prepare(SECTIONSD_UDS_NAME)) {
|
|
fprintf(stderr, "[sectionsd] failed to prepare basic server\n");
|
|
return false;
|
|
}
|
|
|
|
eventServer = new CEventServer;
|
|
|
|
running = true;
|
|
return (OpenThreads::Thread::start() == 0);
|
|
}
|
|
|
|
bool CEitManager::Stop()
|
|
{
|
|
if(!running)
|
|
return false;
|
|
running = false;
|
|
int ret = (OpenThreads::Thread::join() == 0);
|
|
return ret;
|
|
}
|
|
|
|
void CEitManager::run()
|
|
{
|
|
pthread_t /*threadTOT,*/ threadHouseKeeping;
|
|
int rc;
|
|
|
|
xprintf("[sectionsd] starting\n");
|
|
printf("SIevent size: %d\n", (int)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
|
|
|
|
readDVBTimeFilter();
|
|
readEncodingFile();
|
|
|
|
/* threads start left here for now, if any problems found, will be moved to Start() */
|
|
threadTIME.Start();
|
|
threadEIT.Start();
|
|
threadCN.Start();
|
|
#ifdef ENABLE_VIASATEPG
|
|
threadVSEIT.Start();
|
|
#endif
|
|
|
|
#ifdef ENABLE_FREESATEPG
|
|
threadFSEIT.Start();
|
|
#endif
|
|
#ifdef ENABLE_SDT
|
|
threadSDT.Start();
|
|
#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)
|
|
dump_sched_info("main");
|
|
|
|
while (running && sectionsd_server.run(sectionsd_parse_command, sectionsd::ACTVERSION, true)) {
|
|
sched_yield();
|
|
if (threadCN.checkUpdate()) {
|
|
sched_yield();
|
|
threadCN.change(0);
|
|
sched_yield();
|
|
}
|
|
|
|
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");
|
|
|
|
threadEIT.StopRun();
|
|
threadCN.StopRun();
|
|
threadTIME.StopRun();
|
|
#ifdef ENABLE_VIASATEPG
|
|
threadVSEIT.StopRun();
|
|
#endif
|
|
#ifdef ENABLE_SDT
|
|
threadSDT.StopRun();
|
|
#endif
|
|
#ifdef ENABLE_FREESATEPG
|
|
threadFSEIT.StopRun();
|
|
#endif
|
|
|
|
xprintf("broadcasting...\n");
|
|
|
|
threadTIME.setTimeSet();
|
|
|
|
xprintf("pausing...\n");
|
|
|
|
pthread_cancel(threadHouseKeeping);
|
|
|
|
xprintf("join TOT\n");
|
|
threadTIME.Stop();
|
|
|
|
xprintf("join EIT\n");
|
|
threadEIT.Stop();
|
|
|
|
xprintf("join CN\n");
|
|
threadCN.Stop();
|
|
#ifdef ENABLE_VIASATEPG
|
|
xprintf("join VSEIT\n");
|
|
threadVSEIT.Stop();
|
|
#endif
|
|
|
|
#ifdef ENABLE_SDT
|
|
xprintf("join SDT\n");
|
|
threadSDT.Stop();
|
|
#endif
|
|
#ifdef ENABLE_FREESATEPG
|
|
xprintf("join FSEIT\n");
|
|
threadFSEIT.Stop();
|
|
#endif
|
|
#ifdef EXIT_CLEANUP
|
|
xprintf("[sectionsd] cleanup...\n");
|
|
delete myNextEvent;
|
|
delete myCurrentEvent;
|
|
FreeMemory();
|
|
#endif
|
|
xprintf("[sectionsd] stopped\n");
|
|
}
|
|
|
|
/* was: commandAllEventsChannelID sendAllEvents */
|
|
void CEitManager::getEventsServiceKey(t_channel_id serviceUniqueKey, CChannelEventList &eList, char search, std::string search_text,bool all_chann, int genre,int fsk)
|
|
{
|
|
dprintf("sendAllEvents for " PRINTF_CHANNEL_ID_TYPE "\n", serviceUniqueKey);
|
|
if(!eList.empty() && search == 0)//skip on search mode
|
|
eList.clear();
|
|
|
|
t_channel_id serviceUniqueKey64 = serviceUniqueKey& 0xFFFFFFFFFFFFULL; //0xFFFFFFFFFFFFULL for CREATE_CHANNEL_ID64
|
|
if(serviceUniqueKey64 == 0 && !all_chann)
|
|
return;
|
|
|
|
// service Found
|
|
readLockEvents();
|
|
int serviceIDfound = 0;
|
|
if (!search_text.empty())
|
|
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() == serviceUniqueKey64 || (all_chann)) {
|
|
serviceIDfound = 1;
|
|
|
|
bool copy = true;
|
|
if(search){
|
|
if ((search == 1 /*EventList::SEARCH_EPG_TITLE*/) || (search == 5 /*EventList::SEARCH_EPG_ALL*/))
|
|
{
|
|
std::string eName = (*e)->getName();
|
|
std::transform(eName.begin(), eName.end(), eName.begin(), tolower);
|
|
copy = (eName.find(search_text) != std::string::npos);
|
|
}
|
|
if ((search == 2 /*EventList::SEARCH_EPG_INFO1*/) || (!copy && (search == 5 /*EventList::SEARCH_EPG_ALL*/)))
|
|
{
|
|
std::string eText = (*e)->getText();
|
|
std::transform(eText.begin(), eText.end(), eText.begin(), tolower);
|
|
copy = (eText.find(search_text) != std::string::npos);
|
|
}
|
|
if ((search == 3 /*EventList::SEARCH_EPG_INFO2*/) || (!copy && (search == 5 /*EventList::SEARCH_EPG_ALL*/)))
|
|
{
|
|
std::string eExtendedText = (*e)->getExtendedText();
|
|
std::transform(eExtendedText.begin(), eExtendedText.end(), eExtendedText.begin(), tolower);
|
|
copy = (eExtendedText.find(search_text) != std::string::npos);
|
|
}
|
|
if(copy && genre != 0xFF)
|
|
{
|
|
if((*e)->classifications.content==0)
|
|
copy=false;
|
|
if(copy && ((*e)->classifications.content < (genre & 0xf0 ) || (*e)->classifications.content > genre))
|
|
copy=false;
|
|
}
|
|
if(copy && fsk != 0)
|
|
{
|
|
if(fsk<0)
|
|
{
|
|
if( (*e)->getFSK() > abs(fsk))
|
|
copy=false;
|
|
}else if( (*e)->getFSK() < fsk)
|
|
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();
|
|
if(all_chann)//hack for all channel search
|
|
aEvent.channelID = (*e)->get_channel_id();
|
|
else
|
|
aEvent.channelID = serviceUniqueKey;
|
|
eList.push_back(aEvent);
|
|
}
|
|
} // if = serviceID
|
|
}
|
|
else if ( serviceIDfound )
|
|
break; // sind nach serviceID und startzeit sortiert -> nicht weiter suchen
|
|
}
|
|
unlockEvents();
|
|
}
|
|
|
|
/* invalidate current/next events, if current event times expired */
|
|
void CEitManager::checkCurrentNextEvent(void)
|
|
{
|
|
time_t curtime = time(NULL);
|
|
writeLockEvents();
|
|
if (scanning || !myCurrentEvent || myCurrentEvent->times.empty()) {
|
|
unlockEvents();
|
|
return;
|
|
}
|
|
if ((long)(myCurrentEvent->times.begin()->startzeit + myCurrentEvent->times.begin()->dauer) < (long)curtime) {
|
|
delete myCurrentEvent;
|
|
myCurrentEvent = NULL;
|
|
delete myNextEvent;
|
|
myNextEvent = NULL;
|
|
}
|
|
unlockEvents();
|
|
}
|
|
|
|
/* 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.
|
|
*/
|
|
/* was: commandCurrentNextInfoChannelID */
|
|
void CEitManager::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;//TODO remove ?
|
|
|
|
uniqueServiceKey &= 0xFFFFFFFFFFFFULL;
|
|
|
|
checkCurrentNextEvent();
|
|
|
|
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 if(!currentEvt.times.empty()) {
|
|
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);
|
|
}
|
|
|
|
/* FIXME what this code should do ? why search channel id as event key ?? */
|
|
#if 0
|
|
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.empty() && eFirst->second->times.begin()->startzeit < azeit &&
|
|
eFirst->second->uniqueKey() == nextEvt.uniqueKey() - 1)
|
|
flag |= CSectionsdClient::epgflags::has_no_current;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
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 = time_cur.dauer = 0;
|
|
if(!currentEvt.times.empty()) {
|
|
time_cur.startzeit = currentEvt.times.begin()->startzeit;
|
|
time_cur.dauer = currentEvt.times.begin()->dauer;
|
|
}
|
|
time_nxt.startzeit = time_nxt.dauer = 0;
|
|
if(!nextEvt.times.empty()) {
|
|
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();
|
|
}
|
|
|
|
/* commandEPGepgIDshort */
|
|
bool CEitManager::getEPGidShort(event_id_t epgID, CShortEPGData * epgdata)
|
|
{
|
|
bool ret = false;
|
|
dprintf("Request of current EPG for 0x%" PRIx64 "\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 CEitManager::getEPGid(const event_id_t epgID, const time_t startzeit, CEPGData * epgdata)
|
|
{
|
|
bool ret = false;
|
|
dprintf("Request of actual EPG for 0x%" PRIx64 " 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()); */
|
|
#ifdef FULL_CONTENT_CLASSIFICATION
|
|
evt.classifications.get(epgdata->contentClassification, epgdata->userClassification);
|
|
#else
|
|
epgdata->contentClassification = evt.classifications.content;
|
|
epgdata->userClassification = evt.classifications.user;
|
|
#endif
|
|
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 CEitManager::getActualEPGServiceKey(const t_channel_id channel_id, CEPGData * epgdata)
|
|
{
|
|
bool ret = false;
|
|
SIevent evt;
|
|
SItime zeit(0, 0);
|
|
|
|
dprintf("[commandActualEPGchannelID] Request of current EPG for " PRINTF_CHANNEL_ID_TYPE "\n", channel_id);
|
|
|
|
t_channel_id uniqueServiceKey = channel_id & 0xFFFFFFFFFFFFULL;
|
|
checkCurrentNextEvent();
|
|
readLockEvents();
|
|
if (uniqueServiceKey == messaging_current_servicekey) {
|
|
if (myCurrentEvent) {
|
|
evt = *myCurrentEvent;
|
|
if(!evt.times.empty()) {
|
|
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());*/
|
|
#ifdef FULL_CONTENT_CLASSIFICATION
|
|
evt.classifications.get(epgdata->contentClassification, epgdata->userClassification);
|
|
#else
|
|
epgdata->contentClassification = evt.classifications.content;
|
|
epgdata->userClassification = evt.classifications.user;
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
|
|
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] & 0xFFFFFFFFFFFFULL) == chid)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* 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 CEitManager::getChannelEvents(CChannelEventList &eList, t_channel_id *chidlist, int clen)
|
|
{
|
|
t_channel_id uniqueNow = 0;
|
|
t_channel_id uniqueOld = 0;
|
|
bool found_already = true;
|
|
time_t azeit = time(NULL);
|
|
|
|
showProfiling("sectionsd_getChannelEvents start");
|
|
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))
|
|
{
|
|
//TODO CChannelEvent constructor from SIevent ?
|
|
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(found_already && clen && (clen == (int) eList.size()))
|
|
break;
|
|
}
|
|
}
|
|
|
|
showProfiling("sectionsd_getChannelEvents end");
|
|
unlockEvents();
|
|
}
|
|
|
|
/*was static void commandComponentTagsUniqueKey(int connfd, char *data, const unsigned dataLength) */
|
|
bool CEitManager::getComponentTagsUniqueKey(const event_id_t uniqueKey, CSectionsdClient::ComponentTagList& tags)
|
|
{
|
|
bool ret = false;
|
|
dprintf("Request of ComponentTags for 0x%" PRIx64 "\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->getComponentName();
|
|
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 CEitManager::getLinkageDescriptorsUniqueKey(const event_id_t uniqueKey, CSectionsdClient::LinkageDescriptorList& descriptors)
|
|
{
|
|
bool ret = false;
|
|
dprintf("Request of LinkageDescriptors for 0x%" PRIx64 "\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 CEitManager::getNVODTimesServiceKey(const t_channel_id channel_id, CSectionsdClient::NVODTimesList& nvod_list)
|
|
{
|
|
bool ret = false;
|
|
dprintf("Request of NVOD times for " PRINTF_CHANNEL_ID_TYPE "\n", channel_id);
|
|
|
|
t_channel_id uniqueServiceKey = channel_id & 0xFFFFFFFFFFFFULL;
|
|
nvod_list.clear();
|
|
|
|
readLockServices();
|
|
readLockEvents();
|
|
|
|
MySIservicesNVODorderUniqueKey::iterator si = mySIservicesNVODorderUniqueKey.find(uniqueServiceKey);
|
|
if (si != mySIservicesNVODorderUniqueKey.end())
|
|
{
|
|
dprintf("NVODServices: %u\n", (unsigned)si->second->nvods.size());
|
|
|
|
if (!si->second->nvods.empty()) {
|
|
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 CEitManager::setLanguages(const std::vector<std::string>& newLanguages)
|
|
{
|
|
SIlanguage::setLanguages(newLanguages);
|
|
SIlanguage::saveLanguages();
|
|
}
|
|
|
|
unsigned CEitManager::getEventsCount()
|
|
{
|
|
readLockEvents();
|
|
unsigned anzEvents = mySIeventsOrderUniqueKey.size();
|
|
unlockEvents();
|
|
return anzEvents;
|
|
}
|
|
|
|
void CEitManager::addChannelFilter(t_original_network_id onid, t_transport_stream_id tsid, t_service_id sid)
|
|
{
|
|
OpenThreads::ScopedLock<OpenThreads::Mutex> slock(filter_mutex);
|
|
if (xml_epg_filter)
|
|
return;
|
|
epg_filter_except_current_next = true;
|
|
epg_filter_is_whitelist = true;
|
|
addEPGFilter(onid, tsid, sid);
|
|
}
|
|
|
|
void CEitManager::clearChannelFilters()
|
|
{
|
|
OpenThreads::ScopedLock<OpenThreads::Mutex> slock(filter_mutex);
|
|
if (xml_epg_filter)
|
|
return;
|
|
clearEPGFilter();
|
|
epg_filter_is_whitelist = false;
|
|
}
|