Snapshot 2007-03-20

This commit is contained in:
Frank Schmirler
2010-12-02 08:53:01 +01:00
commit 5e30711bfd
97 changed files with 21652 additions and 0 deletions

125
client/assembler.c Normal file
View File

@@ -0,0 +1,125 @@
/*
* $Id: assembler.c,v 1.2 2005/01/25 14:14:43 lordjaxom Exp $
*/
#include "client/assembler.h"
#include "common.h"
#include "tools/socket.h"
#include "tools/select.h"
#include <vdr/tools.h>
#include <vdr/device.h>
#include <vdr/ringbuffer.h>
#include <unistd.h>
cStreamdevAssembler::cStreamdevAssembler(cTBSocket *Socket)
#if VDRVERSNUM >= 10300
:cThread("Streamdev: UDP-TS Assembler")
#endif
{
m_Socket = Socket;
if (pipe(m_Pipe) != 0) {
esyslog("streamdev-client: Couldn't open assembler pipe: %m");
return;
}
fcntl(m_Pipe[0], F_SETFL, O_NONBLOCK);
fcntl(m_Pipe[1], F_SETFL, O_NONBLOCK);
m_Mutex.Lock();
Start();
}
cStreamdevAssembler::~cStreamdevAssembler() {
if (m_Active) {
m_Active = false;
/* WakeUp();*/
Cancel(3);
}
close(m_Pipe[0]);
close(m_Pipe[1]);
}
void cStreamdevAssembler::Action(void) {
cTBSelect sel;
uchar buffer[2048];
bool fillup = true;
const int rbsize = TS_SIZE * 5600;
const int rbmargin = TS_SIZE * 2;
const int rbminfill = rbmargin * 50;
cRingBufferLinear ringbuf(rbsize, rbmargin, true);
#if VDRVERSNUM < 10300
isyslog("streamdev-client: UDP-TS Assembler thread started (pid=%d)",
getpid());
#endif
m_Mutex.Lock();
m_Active = true;
while (m_Active) {
sel.Clear();
if (ringbuf.Available() < rbsize * 80 / 100)
sel.Add(*m_Socket, false);
if (ringbuf.Available() > rbminfill) {
if (fillup) {
Dprintf("giving signal\n");
m_WaitFill.Broadcast();
m_Mutex.Unlock();
fillup = false;
}
sel.Add(m_Pipe[1], true);
}
if (sel.Select(1500) < 0) {
if (!m_Active) // Exit was requested
break;
esyslog("streamdev-client: Fatal error: %m");
Dprintf("streamdev-client: select failed (%m)\n");
m_Active = false;
break;
}
if (sel.CanRead(*m_Socket)) {
int b;
if ((b = m_Socket->Read(buffer, sizeof(buffer))) < 0) {
esyslog("streamdev-client: Couldn't read from server: %m");
Dprintf("streamdev-client: read failed (%m)\n");
m_Active = false;
break;
}
if (b == 0)
m_Active = false;
else
ringbuf.Put(buffer, b);
}
if (sel.CanWrite(m_Pipe[1])) {
int recvd;
const uchar *block = ringbuf.Get(recvd);
if (block && recvd > 0) {
int result;
if (recvd > ringbuf.Available() - rbminfill)
recvd = ringbuf.Available() - rbminfill;
if ((result = write(m_Pipe[1], block, recvd)) == -1) {
esyslog("streamdev-client: Couldn't write to VDR: %m"); // TODO
Dprintf("streamdev-client: write failed (%m)\n");
m_Active = false;
break;
}
ringbuf.Del(result);
}
}
}
#if VDRVERSNUM < 10300
isyslog("streamdev-client: UDP-TS Assembler thread stopped", getpid());
#endif
}
void cStreamdevAssembler::WaitForFill(void) {
m_WaitFill.Wait(m_Mutex);
m_Mutex.Unlock();
}

32
client/assembler.h Normal file
View File

@@ -0,0 +1,32 @@
/*
* $Id: assembler.h,v 1.1.1.1 2004/12/30 22:44:04 lordjaxom Exp $
*/
#ifndef VDR_STREAMDEV_ASSEMBLER_H
#define VDR_STREAMDEV_ASSEMBLER_H
#include <vdr/config.h>
#include <vdr/thread.h>
class cTBSocket;
class cStreamdevAssembler: public cThread {
private:
cTBSocket *m_Socket;
cMutex m_Mutex;
cCondVar m_WaitFill;
int m_Pipe[2];
bool m_Active;
protected:
virtual void Action(void);
public:
cStreamdevAssembler(cTBSocket *Socket);
virtual ~cStreamdevAssembler();
int ReadPipe(void) const { return m_Pipe[0]; }
void WaitForFill(void);
};
#endif // VDR_STREAMDEV_ASSEMBLER_H

188
client/device.c Normal file
View File

@@ -0,0 +1,188 @@
/*
* $Id: device.c,v 1.8 2007/01/15 12:15:12 schmirl Exp $
*/
#include "client/device.h"
#include "client/setup.h"
#include "client/assembler.h"
#include "client/filter.h"
#include "tools/select.h"
#include <vdr/channels.h>
#include <vdr/ringbuffer.h>
#include <vdr/eit.h>
#include <vdr/timers.h>
#include <time.h>
#include <iostream>
using namespace std;
#define VIDEOBUFSIZE MEGABYTE(3)
cStreamdevDevice *cStreamdevDevice::m_Device = NULL;
cStreamdevDevice::cStreamdevDevice(void) {
m_Channel = NULL;
m_TSBuffer = NULL;
m_Assembler = NULL;
#if VDRVERSNUM < 10300
# if defined(HAVE_AUTOPID)
(void)new cSIProcessor(new cSectionsScanner(""));
# else
(void)new cSIProcessor("");
# endif
cSIProcessor::Read();
#else
m_Filters = new cStreamdevFilters;
StartSectionHandler();
cSchedules::Read();
#endif
m_Device = this;
if (StreamdevClientSetup.SyncEPG)
ClientSocket.SynchronizeEPG();
}
cStreamdevDevice::~cStreamdevDevice() {
Dprintf("Device gets destructed\n");
m_Device = NULL;
delete m_TSBuffer;
delete m_Assembler;
#if VDRVERSNUM >= 10300
delete m_Filters;
#endif
}
bool cStreamdevDevice::ProvidesSource(int Source) const {
Dprintf("ProvidesSource, Source=%d\n", Source);
return false;
}
bool cStreamdevDevice::ProvidesTransponder(const cChannel *Channel) const
{
Dprintf("ProvidesTransponder\n");
return false;
}
bool cStreamdevDevice::ProvidesChannel(const cChannel *Channel, int Priority,
bool *NeedsDetachReceivers) const {
bool res = false;
bool prio = Priority < 0 || Priority > this->Priority();
bool ndr = false;
Dprintf("ProvidesChannel, Channel=%s, Prio=%d\n", Channel->Name(), Priority);
if (ClientSocket.DataSocket(siLive) != NULL
&& TRANSPONDER(Channel, m_Channel))
res = true;
else {
res = prio && ClientSocket.ProvidesChannel(Channel, Priority);
ndr = true;
}
if (NeedsDetachReceivers)
*NeedsDetachReceivers = ndr;
Dprintf("prov res = %d, ndr = %d\n", res, ndr);
return res;
}
bool cStreamdevDevice::SetChannelDevice(const cChannel *Channel,
bool LiveView) {
Dprintf("SetChannelDevice Channel: %s, LiveView: %s\n", Channel->Name(),
LiveView ? "true" : "false");
if (LiveView)
return false;
if (ClientSocket.DataSocket(siLive) != NULL
&& TRANSPONDER(Channel, m_Channel))
return true;
#if VDRVERSNUM < 10338
DetachAll(pidHandles[ptAudio].pid);
DetachAll(pidHandles[ptVideo].pid);
DetachAll(pidHandles[ptPcr].pid);
DetachAll(pidHandles[ptTeletext].pid);
DelPid(pidHandles[ptAudio].pid);
DelPid(pidHandles[ptVideo].pid);
DelPid(pidHandles[ptPcr].pid, ptPcr);
DelPid(pidHandles[ptTeletext].pid);
DelPid(pidHandles[ptDolby].pid);
#else
DetachAllReceivers();
#endif
m_Channel = Channel;
bool r = ClientSocket.SetChannelDevice(m_Channel);
Dprintf("setchanneldevice r=%d\n", r);
return r;
}
bool cStreamdevDevice::SetPid(cPidHandle *Handle, int Type, bool On) {
Dprintf("SetPid, Pid=%d, Type=%d, On=%d, used=%d\n", Handle->pid, Type, On,
Handle->used);
if (Handle->pid && (On || !Handle->used))
return ClientSocket.SetPid(Handle->pid, On);
return true;
}
bool cStreamdevDevice::OpenDvr(void) {
Dprintf("OpenDvr\n");
CloseDvr();
if (ClientSocket.CreateDataConnection(siLive)) {
//m_Assembler = new cStreamdevAssembler(ClientSocket.DataSocket(siLive));
//m_TSBuffer = new cTSBuffer(m_Assembler->ReadPipe(), MEGABYTE(2), CardIndex() + 1);
m_TSBuffer = new cTSBuffer(*ClientSocket.DataSocket(siLive), MEGABYTE(2), CardIndex() + 1);
Dprintf("waiting\n");
//m_Assembler->WaitForFill();
Dprintf("resuming\n");
return true;
}
return false;
}
void cStreamdevDevice::CloseDvr(void) {
Dprintf("CloseDvr\n");
//DELETENULL(m_Assembler);
DELETENULL(m_TSBuffer);
ClientSocket.CloseDvr();
}
bool cStreamdevDevice::GetTSPacket(uchar *&Data) {
if (m_TSBuffer) {
Data = m_TSBuffer->Get();
return true;
}
return false;
}
#if VDRVERSNUM >= 10300
int cStreamdevDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask) {
Dprintf("OpenFilter\n");
if (StreamdevClientSetup.StreamFilters
&& ClientSocket.SetFilter(Pid, Tid, Mask, true)) {
return m_Filters->OpenFilter(Pid, Tid, Mask);
} else
return -1;
}
#endif
bool cStreamdevDevice::Init(void) {
if (m_Device == NULL && StreamdevClientSetup.StartClient)
new cStreamdevDevice;
return true;
}
bool cStreamdevDevice::ReInit(void) {
ClientSocket.Quit();
ClientSocket.Reset();
if (m_Device != NULL) {
DELETENULL(m_Device->m_TSBuffer);
DELETENULL(m_Device->m_Assembler);
}
return StreamdevClientSetup.StartClient ? Init() : true;
}

64
client/device.h Normal file
View File

@@ -0,0 +1,64 @@
/*
* $Id: device.h,v 1.3 2005/02/08 15:21:19 lordjaxom Exp $
*/
#ifndef VDR_STREAMDEV_DEVICE_H
#define VDR_STREAMDEV_DEVICE_H
#include <vdr/device.h>
#include "client/socket.h"
#include "client/assembler.h"
#include "client/filter.h"
class cTBString;
#define CMD_LOCK_OBJ(x) cMutexLock CmdLock((cMutex*)&(x)->m_Mutex)
class cStreamdevDevice: public cDevice {
friend class cRemoteRecordings;
private:
const cChannel *m_Channel;
cTSBuffer *m_TSBuffer;
cStreamdevAssembler *m_Assembler;
#if VDRVERSNUM >= 10307
cStreamdevFilters *m_Filters;
#endif
static cStreamdevDevice *m_Device;
protected:
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
virtual bool HasLock(int TimeoutMs)
{
//printf("HasLock is %d\n", (ClientSocket.DataSocket(siLive) != NULL));
//return ClientSocket.DataSocket(siLive) != NULL;
return true;
}
virtual bool SetPid(cPidHandle *Handle, int Type, bool On);
virtual bool OpenDvr(void);
virtual void CloseDvr(void);
virtual bool GetTSPacket(uchar *&Data);
#if VDRVERSNUM >= 10300
virtual int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
#endif
public:
cStreamdevDevice(void);
virtual ~cStreamdevDevice();
virtual bool ProvidesSource(int Source) const;
virtual bool ProvidesTransponder(const cChannel *Channel) const;
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1,
bool *NeedsDetachReceivers = NULL) const;
static bool Init(void);
static bool ReInit(void);
static cStreamdevDevice *GetDevice(void) { return m_Device; }
};
#endif // VDR_STREAMDEV_DEVICE_H

119
client/filter.c Normal file
View File

@@ -0,0 +1,119 @@
/*
* $Id: filter.c,v 1.3 2005/11/06 16:43:58 lordjaxom Exp $
*/
#include "client/filter.h"
#include "client/socket.h"
#include "tools/select.h"
#include "common.h"
#include <vdr/ringbuffer.h>
#include <vdr/device.h>
#if VDRVERSNUM >= 10300
cStreamdevFilter::cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask) {
m_Used = 0;
m_Pid = Pid;
m_Tid = Tid;
m_Mask = Mask;
if (pipe(m_Pipe) != 0 || fcntl(m_Pipe[0], F_SETFL, O_NONBLOCK) != 0) {
esyslog("streamev-client: coudln't open section filter pipe: %m");
m_Pipe[0] = m_Pipe[1] = -1;
}
}
cStreamdevFilter::~cStreamdevFilter() {
Dprintf("~cStreamdevFilter %p\n", this);
if (m_Pipe[0] >= 0)
close(m_Pipe[0]);
if (m_Pipe[1] >= 0)
close(m_Pipe[1]);
}
bool cStreamdevFilter::PutSection(const uchar *Data, int Length) {
if (m_Used + Length >= (int)sizeof(m_Buffer)) {
esyslog("ERROR: Streamdev: Section handler buffer overflow (%d bytes lost)",
Length);
m_Used = 0;
return true;
}
memcpy(m_Buffer + m_Used, Data, Length);
m_Used += Length;
if (m_Used > 3) {
int length = (((m_Buffer[1] & 0x0F) << 8) | m_Buffer[2]) + 3;
if (m_Used == length) {
if (write(m_Pipe[1], m_Buffer, length) < 0)
return false;
m_Used = 0;
}
}
return true;
}
cStreamdevFilters::cStreamdevFilters(void):
cThread("streamdev-client: sections assembler") {
m_Active = false;
m_RingBuffer = new cRingBufferLinear(MEGABYTE(1), TS_SIZE * 2, true);
Start();
}
cStreamdevFilters::~cStreamdevFilters() {
if (m_Active) {
m_Active = false;
Cancel(3);
}
delete m_RingBuffer;
}
int cStreamdevFilters::OpenFilter(u_short Pid, u_char Tid, u_char Mask) {
cStreamdevFilter *f = new cStreamdevFilter(Pid, Tid, Mask);
Add(f);
return f->ReadPipe();
}
cStreamdevFilter *cStreamdevFilters::Matches(u_short Pid, u_char Tid) {
for (cStreamdevFilter *f = First(); f; f = Next(f)) {
if (f->Matches(Pid, Tid))
return f;
}
return NULL;
}
void cStreamdevFilters::Put(const uchar *Data) {
int p = m_RingBuffer->Put(Data, TS_SIZE);
if (p != TS_SIZE)
m_RingBuffer->ReportOverflow(TS_SIZE - p);
}
void cStreamdevFilters::Action(void) {
m_Active = true;
while (m_Active) {
int recvd;
const uchar *block = m_RingBuffer->Get(recvd);
if (block && recvd > 0) {
cStreamdevFilter *f;
u_short pid = (((u_short)block[1] & PID_MASK_HI) << 8) | block[2];
u_char tid = block[3];
if ((f = Matches(pid, tid)) != NULL) {
int len = block[4];
if (!f->PutSection(block + 5, len)) {
if (errno != EPIPE) {
esyslog("streamdev-client: couldn't send section packet: %m");
Dprintf("FATAL ERROR: %m\n");
}
ClientSocket.SetFilter(f->Pid(), f->Tid(), f->Mask(), false);
Del(f);
}
}
m_RingBuffer->Del(TS_SIZE);
} else
usleep(1);
}
}
#endif // VDRVERSNUM >= 10300

64
client/filter.h Normal file
View File

@@ -0,0 +1,64 @@
/*
* $Id: filter.h,v 1.1.1.1 2004/12/30 22:44:04 lordjaxom Exp $
*/
#ifndef VDR_STREAMDEV_FILTER_H
#define VDR_STREAMDEV_FILTER_H
#include <vdr/config.h>
# if VDRVERSNUM >= 10300
#include <vdr/tools.h>
#include <vdr/thread.h>
class cRingBufferFrame;
class cRingBufferLinear;
class cStreamdevFilter: public cListObject {
private:
uchar m_Buffer[4096];
int m_Used;
int m_Pipe[2];
u_short m_Pid;
u_char m_Tid;
u_char m_Mask;
cRingBufferFrame *m_RingBuffer;
public:
cStreamdevFilter(u_short Pid, u_char Tid, u_char Mask);
virtual ~cStreamdevFilter();
bool Matches(u_short Pid, u_char Tid);
bool PutSection(const uchar *Data, int Length);
int ReadPipe(void) const { return m_Pipe[0]; }
u_short Pid(void) const { return m_Pid; }
u_char Tid(void) const { return m_Tid; }
u_char Mask(void) const { return m_Mask; }
};
inline bool cStreamdevFilter::Matches(u_short Pid, u_char Tid) {
return m_Pid == Pid && m_Tid == (Tid & m_Mask);
}
class cStreamdevFilters: public cList<cStreamdevFilter>, public cThread {
private:
bool m_Active;
cRingBufferLinear *m_RingBuffer;
protected:
virtual void Action(void);
public:
cStreamdevFilters(void);
virtual ~cStreamdevFilters();
int OpenFilter(u_short Pid, u_char Tid, u_char Mask);
cStreamdevFilter *Matches(u_short Pid, u_char Tid);
void Put(const uchar *Data);
};
# endif // VDRVERSNUM >= 10300
#endif // VDR_STREAMDEV_FILTER_H

1045
client/menu.c Normal file

File diff suppressed because it is too large Load Diff

144
client/menu.h Normal file
View File

@@ -0,0 +1,144 @@
/*
* $Id: menu.h,v 1.1.1.1 2004/12/30 22:44:02 lordjaxom Exp $
*/
#ifndef VDR_STREAMDEV_MENU_H
#define VDR_STREAMDEV_MENU_H
#include <vdr/osd.h>
#include "client/remote.h"
class cStreamdevMenuRecordingItem;
// --- cStreamdevMenu --------------------------------------------------------
class cStreamdevMenu: public cOsdMenu {
private:
enum eSubmenus {
sub_Start = os_User,
subSchedule,
subTimers,
subRecordings,
subSuspend,
subSyncEPG
};
protected:
void SuspendServer(void);
public:
cStreamdevMenu(void);
virtual ~cStreamdevMenu(void);
virtual eOSState ProcessKey(eKeys Key);
};
// --- cStreamdevMenuSchedule ------------------------------------------------
class cStreamdevMenuSchedule: public cOsdMenu {
private:
bool m_Now;
bool m_Next;
int m_OtherChannel;
const cSchedules *m_Schedules;
#if VDRVERSNUM < 10300
cMutexLock m_Lock;
#else
cSchedulesLock m_Lock;
#endif
protected:
void PrepareSchedule(cChannel *Channel);
eOSState Switch(void);
eOSState Record(void);
public:
cStreamdevMenuSchedule(void);
virtual ~cStreamdevMenuSchedule(void);
virtual eOSState ProcessKey(eKeys Key);
};
// --- cStreamdevMenuWhatsOn -------------------------------------------------
class cStreamdevMenuWhatsOn: public cOsdMenu {
private:
static int m_CurrentChannel;
#if VDRVERSNUM < 10300
static const cEventInfo *m_ScheduleEventInfo;
#else
static const cEvent *m_ScheduleEventInfo;
#endif
protected:
eOSState Switch(void);
eOSState Record(void);
public:
cStreamdevMenuWhatsOn(const cSchedules *Schedules, bool Now,
int CurrentChannel);
static int CurrentChannel(void) { return m_CurrentChannel; }
static void SetCurrentChannel(int Channel) { m_CurrentChannel = Channel; }
#if VDRVERSNUM < 10300
static const cEventInfo *ScheduleEventInfo(void);
#else
static const cEvent *ScheduleEventInfo(void);
#endif
virtual eOSState ProcessKey(eKeys Key);
};
// --- cStreamdevMenuRecordings ----------------------------------------------
class cStreamdevMenuRecordings: public cOsdMenu {
private:
char *m_Base;
int m_Level;
static int HelpKeys;
static cRemoteRecordings Recordings;
protected:
bool Open(bool OpenSubMenus = false);
void SetHelpKeys();
cRemoteRecording *cStreamdevMenuRecordings::GetRecording(
cStreamdevMenuRecordingItem *Item);
eOSState Select(void);
eOSState Delete(void);
eOSState Summary(void);
public:
cStreamdevMenuRecordings(const char *Base = NULL, int Level = 0,
bool OpenSubMenus = false);
virtual ~cStreamdevMenuRecordings();
virtual eOSState ProcessKey(eKeys Key);
};
// --- cStreamdevMenuTimers --------------------------------------------------
class cStreamdevMenuTimers: public cOsdMenu {
protected:
eOSState Edit(void);
eOSState New(void);
eOSState Delete(void);
eOSState OnOff(void);
eOSState Summary(void);
cRemoteTimer *CurrentTimer(void);
void Refresh(void);
public:
cStreamdevMenuTimers(void);
virtual ~cStreamdevMenuTimers();
virtual eOSState ProcessKey(eKeys Key);
};
#endif // VDR_STREAMDEV_MENU_H

476
client/remote.c Normal file
View File

@@ -0,0 +1,476 @@
/*
* $Id: remote.c,v 1.4 2005/04/24 16:26:14 lordjaxom Exp $
*/
#include <ctype.h>
#include "client/remote.h"
#include "client/device.h"
#include "common.h"
cRemoteTimers RemoteTimers;
// --- cRemoteRecording ------------------------------------------------------
cRemoteRecording::cRemoteRecording(const char *Text) {
m_IsValid = false;
m_Index = -1;
m_IsNew = false;
m_TitleBuffer = NULL;
char *ptr;
char *timestr;
int idx;
Dprintf("text: %s\n", Text);
m_Index = strtoul(Text, &ptr, 10);
Dprintf("index: %d\n", m_Index);
if (*ptr == '\0' || *++ptr == '\0' ) return;
timestr = ptr;
while (*ptr != '\0' && !isspace(*ptr)) ++ptr;
if (*ptr == '\0' || *++ptr == '\0') return;
while (*ptr != '\0' && *ptr != '*' && !isspace(*ptr)) ++ptr;
if (*ptr == '*') m_IsNew = true;
Dprintf("new: %d\n", m_IsNew);
*(ptr++) = '\0';
m_StartTime = timestr;
idx = -1;
while ((idx = m_StartTime.find(' ', idx + 1)) != -1) m_StartTime[idx] = '\t';
Dprintf("m_Start: %s\n", m_StartTime.c_str());
if (*ptr == 0) return;
if (isspace(*ptr)) ++ptr;
if (*ptr == 0) return;
m_Name = ptr;
Dprintf("file: %s\n", m_Name.c_str());
m_IsValid = true;
}
cRemoteRecording::~cRemoteRecording(void) {
}
bool cRemoteRecording::operator==(const cRemoteRecording &Recording) {
return m_IsValid == Recording.m_IsValid
&& m_Index == Recording.m_Index
&& m_StartTime == Recording.m_StartTime
&& m_Name == Recording.m_Name;
}
void cRemoteRecording::ParseInfo(const char *Text) {
m_Summary = strreplace(strdup(Text), '|', '\n');
}
const char *cRemoteRecording::Title(char Delimiter, bool NewIndicator,
int Level) {
char New = NewIndicator && IsNew() ? '*' : ' ';
if (m_TitleBuffer != NULL) {
free(m_TitleBuffer);
m_TitleBuffer = NULL;
}
if (Level < 0 || Level == HierarchyLevels()) {
char *s;
const char *t;
if (Level > 0 && (t = strrchr(m_Name.c_str(), '~')) != NULL)
t++;
else
t = m_Name.c_str();
asprintf(&m_TitleBuffer, "%s%c%c%s", m_StartTime.c_str(), New, Delimiter, t);
// let's not display a trailing '~':
stripspace(m_TitleBuffer);
s = &m_TitleBuffer[strlen(m_TitleBuffer) - 1];
if (*s == '~')
*s = 0;
} else if (Level < HierarchyLevels()) {
const char *s = m_Name.c_str();
const char *p = s;
while (*++s) {
if (*s == '~') {
if (Level--)
p = s + 1;
else
break;
}
}
m_TitleBuffer = MALLOC(char, s - p + 3);
*m_TitleBuffer = Delimiter;
*(m_TitleBuffer + 1) = Delimiter;
strn0cpy(m_TitleBuffer + 2, p, s - p + 1);
} else
return "";
return m_TitleBuffer;
}
int cRemoteRecording::HierarchyLevels(void)
{
const char *s = m_Name.c_str();
int level = 0;
while (*++s) {
if (*s == '~') ++level;
}
return level;
}
// --- cRemoteRecordings -----------------------------------------------------
bool cRemoteRecordings::Load(void) {
Clear();
return ClientSocket.LoadRecordings(*this);
}
cRemoteRecording *cRemoteRecordings::GetByName(const char *Name) {
for (cRemoteRecording *r = First(); r; r = Next(r))
if (strcmp(r->Name(), Name) == 0)
return r;
return NULL;
}
// --- cRemoteTimer ----------------------------------------------------------
cRemoteTimer::cRemoteTimer(const char *Text) {
m_IsValid = false;
m_Index = -1;
m_Active = -1;
m_Day = -1;
m_Start = -1;
m_Stop = -1;
m_StartTime = 0;
m_StopTime = 0;
m_Priority = -1;
m_Lifetime = -1;
m_File[0] = '\0';
m_FirstDay = 0;
m_Buffer = NULL;
m_Channel = NULL;
char *tmpbuf;
char *ptr;
Dprintf("text: %s\n", Text);
m_Index = strtoul(Text, &ptr, 10);
Dprintf("index: %d\n", m_Index);
if (*ptr == '\0' || *++ptr == '\0') return;
m_Active = strtoul(ptr, &ptr, 10);
Dprintf("m_Active: %d\n", m_Active);
if (*ptr == '\0' || *++ptr == '\0') return;
tmpbuf = ptr;
while (*ptr != '\0' && *ptr != ':') ++ptr;
if (*ptr == '\0') return;
*(ptr++)= '\0';
if (isnumber(tmpbuf))
m_Channel = Channels.GetByNumber(strtoul(tmpbuf, NULL, 10));
else
m_Channel = Channels.GetByChannelID(tChannelID::FromString(tmpbuf));
Dprintf("channel no.: %d\n", m_Channel->Number());
tmpbuf = ptr;
while (*ptr != '\0' && *ptr != ':') ++ptr;
if (*ptr == '\0') return;
*(ptr++) = '\0';
m_Day = ParseDay(tmpbuf, &m_FirstDay);
Dprintf("Day: %d\n", m_Day);
m_Start = strtoul(ptr, &ptr, 10);
Dprintf("Start: %d\n", m_Start);
if (*ptr == '\0' || *++ptr == '\0') return;
m_Stop = strtoul(ptr, &ptr, 10);
Dprintf("Stop: %d\n", m_Stop);
if (*ptr == '\0' || *++ptr == '\0') return;
m_Priority = strtoul(ptr, &ptr, 10);
Dprintf("Prio: %d\n", m_Priority);
if (*ptr == '\0' || *++ptr == '\0') return;
m_Lifetime = strtoul(ptr, &ptr, 10);
Dprintf("Lifetime: %d\n", m_Lifetime);
if (*ptr == '\0' || *++ptr == '\0') return;
tmpbuf = ptr;
while (*ptr != '\0' && *ptr != ':') ++ptr;
if (*ptr == '\0') return;
*(ptr++) = '\0';
strncpy(m_File, tmpbuf, MaxFileName);
Dprintf("file: %s\n", m_File);
if (*ptr != '\0') m_Summary = ptr;
Dprintf("summary: %s\n", m_Summary.c_str());
m_IsValid = true;
}
#if VDRVERSNUM < 10300
cRemoteTimer::cRemoteTimer(const cEventInfo *EventInfo) {
time_t tstart = EventInfo->GetTime();
time_t tstop = tstart + EventInfo->GetDuration() + Setup.MarginStop * 60;
tstart -= Setup.MarginStart * 60;
struct tm tm_r;
struct tm *time = localtime_r(&tstart, &tm_r);
const char *title = EventInfo->GetTitle();
cChannel *channel = Channels.GetByChannelID(EventInfo->GetChannelID(), true);
#else
cRemoteTimer::cRemoteTimer(const cEvent *Event) {
time_t tstart = Event->StartTime();
time_t tstop = tstart + Event->Duration() + Setup.MarginStop * 60;
tstart -= Setup.MarginStart * 60;
struct tm tm_r;
struct tm *time = localtime_r(&tstart, &tm_r);
const char *title = Event->Title();
cChannel *channel = Channels.GetByChannelID(Event->ChannelID(), true);
#endif
m_IsValid = true;
m_Index = -1;
m_Active = true;
m_Day = time->tm_mday;
m_Start = time->tm_hour * 100 + time->tm_min;
time = localtime_r(&tstop, &tm_r);
m_Stop = time->tm_hour * 100 + time->tm_min;
m_StartTime = 0;
m_StopTime = 0;
if (m_Stop >= 2400) m_Stop -= 2400;
m_Priority = Setup.DefaultPriority;
m_Lifetime = Setup.DefaultLifetime;
m_File[0] = '\0';
if (!isempty(title))
strn0cpy(m_File, title, sizeof(m_File));
m_FirstDay = 0;
m_Channel = channel;
}
cRemoteTimer::cRemoteTimer(void) {
time_t t = time(NULL);
struct tm tm_r;
struct tm *now = localtime_r(&t, &tm_r);
m_IsValid = true;
m_Index = -1;
m_Active = -1;
m_Day = now->tm_mday;
m_Start = now->tm_hour * 100 + now->tm_min;
m_Stop = now->tm_hour * 60 + now->tm_min + Setup.InstantRecordTime;
m_Stop = (m_Stop / 60) * 100 + (m_Stop % 60);
if (m_Stop >= 2400) m_Stop -= 2400;
m_StartTime = 0;
m_StopTime = 0;
m_Priority = Setup.DefaultPriority;
m_Lifetime = Setup.DefaultLifetime;
m_File[0] = '\0';
m_FirstDay = 0;
m_Buffer = NULL;
m_Channel = Channels.GetByNumber(cDevice::CurrentChannel());
}
cRemoteTimer::~cRemoteTimer() {
if (m_Buffer != NULL) free(m_Buffer);
}
cRemoteTimer &cRemoteTimer::operator=(const cRemoteTimer &Timer) {
Dprintf("\n\n\n\nOP<EFBFBD>ERATHVBD<EFBFBD>LJVG\n\n\n");
m_IsValid = Timer.m_IsValid;
m_Index = Timer.m_Index;
m_Active = Timer.m_Active;
m_Day = Timer.m_Day;
m_Start = Timer.m_Start;
m_Stop = Timer.m_Stop;
m_Priority = Timer.m_Priority;
m_Lifetime = Timer.m_Lifetime;
m_FirstDay = Timer.m_FirstDay;
m_Channel = Timer.m_Channel;
m_Summary = Timer.m_Summary;
return *this;
}
bool cRemoteTimer::operator==(const cRemoteTimer &Timer) {
return m_IsValid == Timer.m_IsValid
&& m_Index == Timer.m_Index
&& m_Active == Timer.m_Active
&& m_Day == Timer.m_Day
&& m_Start == Timer.m_Start
&& m_Stop == Timer.m_Stop
&& m_Priority == Timer.m_Priority
&& m_Lifetime == Timer.m_Lifetime
&& m_FirstDay == Timer.m_FirstDay
&& m_Channel == Timer.m_Channel
&& strcmp(m_File, Timer.m_File) == 0
&& m_Summary == Timer.m_Summary;
}
int cRemoteTimer::ParseDay(const char *s, time_t *FirstDay) {
char *tail;
int d = strtol(s, &tail, 10);
if (FirstDay)
*FirstDay = 0;
if (tail && *tail) {
d = 0;
if (tail == s) {
const char *first = strchr(s, '@');
int l = first ? first - s : strlen(s);
if (l == 7) {
for (const char *p = s + 6; p >= s; p--) {
d <<= 1;
d |= (*p != '-');
}
d |= 0x80000000;
}
if (FirstDay && first) {
++first;
if (strlen(first) == 10) {
struct tm tm_r;
if (3 == sscanf(first, "%d-%d-%d", &tm_r.tm_year, &tm_r.tm_mon, &tm_r.tm_mday)) {
tm_r.tm_year -= 1900;
tm_r.tm_mon--;
tm_r.tm_hour = tm_r.tm_min = tm_r.tm_sec = 0;
tm_r.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
*FirstDay = mktime(&tm_r);
}
}
else
d = 0;
}
}
}
else if (d < 1 || d > 31)
d = 0;
return d;
}
const char *cRemoteTimer::PrintDay(int d, time_t FirstDay) {
#define DAYBUFFERSIZE 32
static char buffer[DAYBUFFERSIZE];
if ((d & 0x80000000) != 0) {
char *b = buffer;
const char *w = tr("MTWTFSS");
while (*w) {
*b++ = (d & 1) ? *w : '-';
d >>= 1;
w++;
}
if (FirstDay) {
struct tm tm_r;
localtime_r(&FirstDay, &tm_r);
b += strftime(b, DAYBUFFERSIZE - (b - buffer), "@%Y-%m-%d", &tm_r);
}
*b = 0;
}
else
sprintf(buffer, "%d", d);
return buffer;
}
const char *cRemoteTimer::PrintFirstDay(void) const {
if (m_FirstDay) {
const char *s = PrintDay(m_Day, m_FirstDay);
if (strlen(s) == 18)
return s + 8;
}
return ""; // not NULL, so the caller can always use the result
}
void cRemoteTimer::OnOff(void) {
if (IsSingleEvent())
m_Active = !m_Active;
else if (m_FirstDay) {
m_FirstDay = 0;
m_Active = false;
}
else if (m_Active)
Skip();
else
m_Active = true;
Matches(); // refresh m_Start and end time
}
time_t cRemoteTimer::SetTime(time_t t, int SecondsFromMidnight) {
struct tm tm_r;
tm tm = *localtime_r(&t, &tm_r);
tm.tm_hour = SecondsFromMidnight / 3600;
tm.tm_min = (SecondsFromMidnight % 3600) / 60;
tm.tm_sec = SecondsFromMidnight % 60;
tm.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
return mktime(&tm);
}
bool cRemoteTimer::Matches(time_t t) {
m_StartTime = m_StopTime = 0;
if (t == 0)
t = time(NULL);
int begin = TimeToInt(m_Start); // seconds from midnight
int length = TimeToInt(m_Stop) - begin;
if (length < 0)
length += SECSINDAY;
int DaysToCheck = IsSingleEvent() ? 61 : 7; // 61 to handle months with 31/30/31
for (int i = -1; i <= DaysToCheck; i++) {
time_t t0 = IncDay(t, i);
if (DayMatches(t0)) {
time_t a = SetTime(t0, begin);
time_t b = a + length;
if ((!m_FirstDay || a >= m_FirstDay) && t <= b) {
m_StartTime = a;
m_StopTime = b;
break;
}
}
}
if (!m_StartTime)
m_StartTime = m_FirstDay; // just to have something that's more than a week in the future
else if (t > m_StartTime || t > m_FirstDay + SECSINDAY + 3600) // +3600 in case of DST change
m_FirstDay = 0;
return m_Active && m_StartTime <= t && t < m_StopTime; // must m_Stop *before* m_StopTime to allow adjacent timers
}
bool cRemoteTimer::DayMatches(time_t t) {
return IsSingleEvent()
? GetMDay(t) == m_Day
: (m_Day & (1 << GetWDay(t))) != 0;
}
int cRemoteTimer::GetMDay(time_t t)
{
struct tm tm_r;
return localtime_r(&t, &tm_r)->tm_mday;
}
int cRemoteTimer::GetWDay(time_t t)
{
struct tm tm_r;
int weekday = localtime_r(&t, &tm_r)->tm_wday;
return weekday == 0 ? 6 : weekday - 1; // we start with monday==0!
}
time_t cRemoteTimer::IncDay(time_t t, int Days) {
struct tm tm_r;
tm tm = *localtime_r(&t, &tm_r);
tm.tm_mday += Days; // now tm_mday may be out of its valid range
int h = tm.tm_hour; // save original hour to compensate for DST change
tm.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
t = mktime(&tm); // normalize all values
tm.tm_hour = h; // compensate for DST change
return mktime(&tm); // calculate final result
}
const char *cRemoteTimer::ToText(void) {
char *summary = NULL;
if (m_Buffer != NULL) free(m_Buffer);
strreplace(m_File, ':', '|');
if (m_Summary != "")
summary = strreplace(strdup(m_Summary.c_str()), ':', '|');
asprintf(&m_Buffer, "%d:%s:%s:%04d:%04d:%d:%d:%s:%s", m_Active,
(const char*)Channel()->GetChannelID().ToString(), PrintDay(m_Day, m_FirstDay),
m_Start, m_Stop, m_Priority, m_Lifetime, m_File, summary ? summary : "");
if (summary != NULL)
free(summary);
strreplace(m_File, '|', ':');
return m_Buffer;
}
// --- cRemoteTimers ---------------------------------------------------------
bool cRemoteTimers::Load(void) {
Clear();
return ClientSocket.LoadTimers(*this);
}

131
client/remote.h Normal file
View File

@@ -0,0 +1,131 @@
/*
* $Id: remote.h,v 1.2 2005/02/08 17:22:35 lordjaxom Exp $
*/
#ifndef VDR_STREAMDEV_REMOTE_H
#define VDR_STREAMDEV_REMOTE_H
#include <vdr/config.h>
#include <string>
#if VDRVERSNUM < 10300
class cEventInfo;
#else
class cEvent;
#endif
class cChannel;
class cRemoteRecording: public cListObject {
private:
bool m_IsValid;
int m_Index;
bool m_IsNew;
char *m_TitleBuffer;
std::string m_StartTime;
std::string m_Name;
std::string m_Summary;
public:
cRemoteRecording(const char *Text);
~cRemoteRecording();
bool operator==(const cRemoteRecording &Recording);
bool operator!=(const cRemoteRecording &Recording);
void ParseInfo(const char *Text);
bool IsValid(void) const { return m_IsValid; }
int Index(void) const { return m_Index; }
const char *StartTime(void) const { return m_StartTime.c_str(); }
bool IsNew(void) const { return m_IsNew; }
const char *Name(void) const { return m_Name.c_str(); }
const char *Summary(void) const { return m_Summary.c_str(); }
const char *Title(char Delimiter, bool NewIndicator, int Level);
int HierarchyLevels(void);
};
inline bool cRemoteRecording::operator!=(const cRemoteRecording &Recording) {
return !operator==(Recording);
}
class cRemoteRecordings: public cList<cRemoteRecording> {
public:
bool Load(void);
cRemoteRecording *GetByName(const char *Name);
};
class cRemoteTimer: public cListObject {
friend class cStreamdevMenuEditTimer;
private:
bool m_IsValid;
int m_Index;
int m_Active;
int m_Day;
int m_Start;
int m_Stop;
time_t m_StartTime;
time_t m_StopTime;
int m_Priority;
int m_Lifetime;
char m_File[MaxFileName];
time_t m_FirstDay;
std::string m_Summary;
char *m_Buffer;
const cChannel *m_Channel;
public:
cRemoteTimer(const char *Text);
#if VDRVERSNUM < 10300
cRemoteTimer(const cEventInfo *EventInfo);
#else
cRemoteTimer(const cEvent *Event);
#endif
cRemoteTimer(void);
~cRemoteTimer();
cRemoteTimer &operator=(const cRemoteTimer &Timer);
bool operator==(const cRemoteTimer &Timer);
bool operator!=(const cRemoteTimer &Timer) { return !operator==(Timer); }
static int ParseDay(const char *s, time_t *FirstDay);
static const char *PrintDay(int d, time_t FirstDay = 0);
static time_t SetTime(time_t t, int SecondsFromMidnight);
static time_t IncDay(time_t t, int Days);
static int TimeToInt(int t) { return (t / 100 * 60 + t % 100) * 60; }
const char *PrintFirstDay(void) const;
void OnOff(void);
bool IsSingleEvent(void) const { return (m_Day & 0x80000000) == 0; }
void Skip(void) { m_FirstDay = IncDay(SetTime(StartTime(), 0), 1); }
bool Matches(time_t t = 0);
bool DayMatches(time_t t = 0);
int GetMDay(time_t t);
int GetWDay(time_t t);
bool IsValid(void) const { return m_IsValid; }
int Index(void) const { return m_Index; }
int Active(void) const { return m_Active; }
int Day(void) const { return m_Day; }
int Start(void) const { return m_Start; }
int Stop(void) const { return m_Stop; }
time_t StartTime(void) { if (!m_StartTime) Matches(); return m_StartTime; }
time_t StopTime(void) { if (!m_StopTime) Matches(); return m_StopTime; }
int Priority(void) const { return m_Priority; }
int Lifetime(void) const { return m_Lifetime; }
const char *File(void) const { return m_File; }
time_t FirstDay(void) const { return m_FirstDay; }
const std::string &Summary(void) const { return m_Summary; }
const cChannel *Channel(void) const { return m_Channel; }
const char *ToText(void);
};
class cRemoteTimers: public cList<cRemoteTimer> {
public:
bool Load(void);
};
extern cRemoteTimers RemoteTimers;
#endif // VDR_STREAMDEV_REMOTE_H

79
client/setup.c Normal file
View File

@@ -0,0 +1,79 @@
/*
* $Id: setup.c,v 1.2 2005/02/08 15:34:38 lordjaxom Exp $
*/
#include <vdr/menuitems.h>
#include "client/setup.h"
#include "client/device.h"
#include "i18n.h"
cStreamdevClientSetup StreamdevClientSetup;
cStreamdevClientSetup::cStreamdevClientSetup(void) {
StartClient = false;
RemotePort = 2004;
#if VDRVERSNUM >= 10300
StreamFilters = false;
#endif
SyncEPG = false;
strcpy(RemoteIp, "");
}
bool cStreamdevClientSetup::SetupParse(const char *Name, const char *Value) {
if (strcmp(Name, "StartClient") == 0) StartClient = atoi(Value);
else if (strcmp(Name, "RemoteIp") == 0) {
if (strcmp(Value, "-none-") == 0)
strcpy(RemoteIp, "");
else
strcpy(RemoteIp, Value);
}
else if (strcmp(Name, "RemotePort") == 0) RemotePort = atoi(Value);
#if VDRVERSNUM >= 10300
else if (strcmp(Name, "StreamFilters") == 0) StreamFilters = atoi(Value);
#endif
else if (strcmp(Name, "SyncEPG") == 0) SyncEPG = atoi(Value);
else return false;
return true;
}
cStreamdevClientMenuSetupPage::cStreamdevClientMenuSetupPage(void) {
m_NewSetup = StreamdevClientSetup;
AddBoolEdit (tr("Start Client"), m_NewSetup.StartClient);
AddIpEdit (tr("Remote IP"), m_NewSetup.RemoteIp);
AddShortEdit(tr("Remote Port"), m_NewSetup.RemotePort);
#if VDRVERSNUM >= 10300
AddBoolEdit (tr("Filter Streaming"), m_NewSetup.StreamFilters);
#endif
AddBoolEdit (tr("Synchronize EPG"), m_NewSetup.SyncEPG);
SetCurrent(Get(0));
}
cStreamdevClientMenuSetupPage::~cStreamdevClientMenuSetupPage() {
}
void cStreamdevClientMenuSetupPage::Store(void) {
if (m_NewSetup.StartClient != StreamdevClientSetup.StartClient) {
if (m_NewSetup.StartClient)
cStreamdevDevice::Init();
else
INFO(tr("Please restart VDR to activate changes"));
}
SetupStore("StartClient", m_NewSetup.StartClient);
if (strcmp(m_NewSetup.RemoteIp, "") == 0)
SetupStore("RemoteIp", "-none-");
else
SetupStore("RemoteIp", m_NewSetup.RemoteIp);
SetupStore("RemotePort", m_NewSetup.RemotePort);
#if VDRVERSNUM >= 10300
SetupStore("StreamFilters", m_NewSetup.StreamFilters);
#endif
SetupStore("SyncEPG", m_NewSetup.SyncEPG);
StreamdevClientSetup = m_NewSetup;
cStreamdevDevice::ReInit();
}

38
client/setup.h Normal file
View File

@@ -0,0 +1,38 @@
/*
* $Id: setup.h,v 1.2 2005/02/08 15:34:38 lordjaxom Exp $
*/
#ifndef VDR_STREAMDEV_SETUPCLIENT_H
#define VDR_STREAMDEV_SETUPCLIENT_H
#include "common.h"
struct cStreamdevClientSetup {
cStreamdevClientSetup(void);
bool SetupParse(const char *Name, const char *Value);
int StartClient;
char RemoteIp[20];
int RemotePort;
#if VDRVERSNUM >= 10300
int StreamFilters;
#endif
int SyncEPG;
};
extern cStreamdevClientSetup StreamdevClientSetup;
class cStreamdevClientMenuSetupPage: public cStreamdevMenuSetupPage {
private:
cStreamdevClientSetup m_NewSetup;
protected:
virtual void Store(void);
public:
cStreamdevClientMenuSetupPage(void);
virtual ~cStreamdevClientMenuSetupPage();
};
#endif // VDR_STREAMDEV_SETUPCLIENT_H

587
client/socket.c Normal file
View File

@@ -0,0 +1,587 @@
/*
* $Id: socket.c,v 1.7 2007/01/15 11:45:48 schmirl Exp $
*/
#include <tools/select.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#define MINLOGREPEAT 10 //don't log connect failures too often (seconds)
#include "client/socket.h"
#include "client/setup.h"
#include "client/remote.h"
#include "common.h"
#include "i18n.h"
cClientSocket ClientSocket;
cClientSocket::cClientSocket(void)
{
memset(m_DataSockets, 0, sizeof(cTBSocket*) * si_Count);
Reset();
}
cClientSocket::~cClientSocket()
{
Reset();
if (IsOpen()) Quit();
}
void cClientSocket::Reset(void)
{
for (int it = 0; it < si_Count; ++it) {
if (m_DataSockets[it] != NULL)
DELETENULL(m_DataSockets[it]);
}
}
cTBSocket *cClientSocket::DataSocket(eSocketId Id) const {
return m_DataSockets[Id];
}
bool cClientSocket::Command(const std::string &Command, uint Expected, uint TimeoutMs)
{
errno = 0;
std::string pkt = Command + "\015\012";
Dprintf("OUT: |%s|\n", Command.c_str());
cTimeMs starttime;
if (!TimedWrite(pkt.c_str(), pkt.size(), TimeoutMs)) {
esyslog("Streamdev: Lost connection to %s:%d: %s", RemoteIp().c_str(), RemotePort(),
strerror(errno));
Close();
return false;
}
uint64_t elapsed = starttime.Elapsed();
if (Expected != 0) { // XXX+ What if elapsed > TimeoutMs?
TimeoutMs -= elapsed;
return Expect(Expected, NULL, TimeoutMs);
}
return true;
}
bool cClientSocket::Expect(uint Expected, std::string *Result, uint TimeoutMs) {
char *endptr;
int bufcount;
bool res;
errno = 0;
if ((bufcount = ReadUntil(m_Buffer, sizeof(m_Buffer) - 1, "\012", TimeoutMs)) == -1) {
esyslog("Streamdev: Lost connection to %s:%d: %s", RemoteIp().c_str(), RemotePort(),
strerror(errno));
Close();
return false;
}
if (m_Buffer[bufcount - 1] == '\015')
--bufcount;
m_Buffer[bufcount] = '\0';
Dprintf("IN: |%s|\n", m_Buffer);
if (Result != NULL)
*Result = m_Buffer;
res = strtoul(m_Buffer, &endptr, 10) == Expected;
return res;
}
bool cClientSocket::CheckConnection(void) {
CMD_LOCK;
if (IsOpen()) {
cTBSelect select;
Dprintf("connection open\n");
// XXX+ check if connection is still alive (is there a better way?)
// There REALLY shouldn't be anything readable according to PROTOCOL here
// If there is, assume it's an eof signal (subseq. read would return 0)
select.Add(*this, false);
int res;
if ((res = select.Select(0)) == 0) {
Dprintf("select said nothing happened\n");
return true;
}
Dprintf("closing connection (res was %d)", res);
Close();
}
if (!Connect(StreamdevClientSetup.RemoteIp, StreamdevClientSetup.RemotePort)){
static time_t lastTime = 0;
if (time(NULL) - lastTime > MINLOGREPEAT) {
esyslog("ERROR: Streamdev: Couldn't connect to %s:%d: %s",
(const char*)StreamdevClientSetup.RemoteIp,
StreamdevClientSetup.RemotePort, strerror(errno));
lastTime = time(NULL);
}
return false;
}
if (!Expect(220)) {
if (errno == 0)
esyslog("ERROR: Streamdev: Didn't receive greeting from %s:%d",
RemoteIp().c_str(), RemotePort());
Close();
return false;
}
if (!Command("CAPS TSPIDS", 220)) {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't negotiate capabilities on %s:%d",
RemoteIp().c_str(), RemotePort());
Close();
return false;
}
isyslog("Streamdev: Connected to server %s:%d using capabilities TSPIDS",
RemoteIp().c_str(), RemotePort());
return true;
}
bool cClientSocket::ProvidesChannel(const cChannel *Channel, int Priority) {
if (!CheckConnection()) return false;
CMD_LOCK;
std::string command = (std::string)"PROV " + (const char*)itoa(Priority) + " "
+ (const char*)Channel->GetChannelID().ToString();
if (!Command(command))
return false;
std::string buffer;
if (!Expect(220, &buffer)) {
if (buffer.substr(0, 3) != "560" && errno == 0)
esyslog("ERROR: Streamdev: Couldn't check if %s:%d provides channel %s",
RemoteIp().c_str(), RemotePort(), Channel->Name());
return false;
}
return true;
}
bool cClientSocket::CreateDataConnection(eSocketId Id) {
cTBSocket listen(SOCK_STREAM);
if (!CheckConnection()) return false;
if (m_DataSockets[Id] != NULL)
DELETENULL(m_DataSockets[Id]);
if (!listen.Listen(LocalIp(), 0, 1)) {
esyslog("ERROR: Streamdev: Couldn't create data connection: %s",
strerror(errno));
return false;
}
std::string command = (std::string)"PORT " + (const char*)itoa(Id) + " "
+ LocalIp().c_str() + ","
+ (const char*)itoa((listen.LocalPort() >> 8) & 0xff) + ","
+ (const char*)itoa(listen.LocalPort() & 0xff);
size_t idx = 4;
while ((idx = command.find('.', idx + 1)) != (size_t)-1)
command[idx] = ',';
CMD_LOCK;
if (!Command(command, 220)) {
Dprintf("error: %m\n");
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't establish data connection to %s:%d",
RemoteIp().c_str(), RemotePort());
return false;
}
/* The server SHOULD do the following:
* - get PORT command
* - connect to socket
* - return 220
*/
m_DataSockets[Id] = new cTBSocket;
if (!m_DataSockets[Id]->Accept(listen)) {
esyslog("ERROR: Streamdev: Couldn't establish data connection to %s:%d%s%s",
RemoteIp().c_str(), RemotePort(), errno == 0 ? "" : ": ",
errno == 0 ? "" : strerror(errno));
DELETENULL(m_DataSockets[Id]);
return false;
}
return true;
}
bool cClientSocket::SetChannelDevice(const cChannel *Channel) {
if (!CheckConnection()) return false;
CMD_LOCK;
std::string command = (std::string)"TUNE "
+ (const char*)Channel->GetChannelID().ToString();
if (!Command(command, 220)) {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't tune %s:%d to channel %s",
RemoteIp().c_str(), RemotePort(), Channel->Name());
return false;
}
return true;
}
bool cClientSocket::SetPid(int Pid, bool On) {
if (!CheckConnection()) return false;
CMD_LOCK;
std::string command = (std::string)(On ? "ADDP " : "DELP ") + (const char*)itoa(Pid);
if (!Command(command, 220)) {
if (errno == 0)
esyslog("Streamdev: Pid %d not available from %s:%d", Pid, LocalIp().c_str(),
LocalPort());
return false;
}
return true;
}
#if VDRVERSNUM >= 10300
bool cClientSocket::SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On) {
if (!CheckConnection()) return false;
CMD_LOCK;
std::string command = (std::string)(On ? "ADDF " : "DELF ") + (const char*)itoa(Pid)
+ " " + (const char*)itoa(Tid) + " " + (const char*)itoa(Mask);
if (!Command(command, 220)) {
if (errno == 0)
esyslog("Streamdev: Filter %hu, %hhu, %hhu not available from %s:%d",
Pid, Tid, Mask, LocalIp().c_str(), LocalPort());
return false;
}
return true;
}
#endif
bool cClientSocket::CloseDvr(void) {
if (!CheckConnection()) return false;
CMD_LOCK;
if (m_DataSockets[siLive] != NULL) {
std::string command = (std::string)"ABRT " + (const char*)itoa(siLive);
if (!Command(command, 220)) {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't cleanly close data connection");
return false;
}
DELETENULL(m_DataSockets[siLive]);
}
return true;
}
bool cClientSocket::SynchronizeEPG(void) {
std::string buffer;
bool result;
FILE *epgfd;
if (!CheckConnection()) return false;
isyslog("Streamdev: Synchronizing EPG from server\n");
CMD_LOCK;
if (!Command("LSTE"))
return false;
if ((epgfd = tmpfile()) == NULL) {
esyslog("ERROR: Streamdev: Error while processing EPG data: %s",
strerror(errno));
return false;
}
while ((result = Expect(215, &buffer))) {
if (buffer[3] == ' ') break;
fputs(buffer.c_str() + 4, epgfd);
fputc('\n', epgfd);
}
if (!result) {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't fetch EPG data from %s:%d",
RemoteIp().c_str(), RemotePort());
fclose(epgfd);
return false;
}
rewind(epgfd);
if (cSchedules::Read(epgfd))
#if VDRVERSNUM < 10300
cSIProcessor::TriggerDump();
#else
cSchedules::Cleanup(true);
#endif
else {
esyslog("ERROR: Streamdev: Parsing EPG data failed");
fclose(epgfd);
return false;
}
fclose(epgfd);
return true;
}
bool cClientSocket::Quit(void) {
bool res;
if (!CheckConnection()) return false;
if (!(res = Command("QUIT", 221))) {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't quit command connection to %s:%d",
RemoteIp().c_str(), RemotePort());
}
Close();
return res;
}
bool cClientSocket::LoadRecordings(cRemoteRecordings &Recordings) {
bool res;
if (!CheckConnection()) return false;
CMD_LOCK;
if (!Command("LSTR"))
return false;
std::string buffer;
while ((res = Expect(250, &buffer))) {
cRemoteRecording *rec = new cRemoteRecording(buffer.c_str() + 4);
Dprintf("recording valid: %d\n", rec->IsValid());
if (rec->IsValid())
Recordings.Add(rec);
else
delete rec;
if (buffer[3] == ' ') break;
}
if (!res && buffer.substr(0, 3) != "550") {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't fetch recordings from %s:%d",
RemoteIp().c_str(), RemotePort());
return false;
}
for (cRemoteRecording *r = Recordings.First(); r; r = Recordings.Next(r)) {
std::string command = (std::string)"LSTR " + (const char*)itoa(r->Index());
if (!Command(command))
return false;
if (Expect(250, &buffer))
r->ParseInfo(buffer.c_str() + 4);
else if (buffer.substr(0, 3) != "550") {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't fetch details for recording from %s:%d",
RemoteIp().c_str(), RemotePort());
return false;
}
Dprintf("recording complete: %d\n", r->Index());
}
return res;
}
bool cClientSocket::StartReplay(const char *Filename) {
if (!CheckConnection()) return false;
CMD_LOCK;
std::string command = (std::string)"PLAY " + Filename;
if (!Command(command, 220)) {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't replay \"%s\" from %s:%d",
Filename, RemoteIp().c_str(), RemotePort());
return false;
}
return true;
}
bool cClientSocket::AbortReplay(void) {
if (!CheckConnection()) return false;
CMD_LOCK;
if (m_DataSockets[siReplay] != NULL) {
std::string command = (std::string)"ABRT " + (const char*)itoa(siReplay);
if (!Command(command, 220)) {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't cleanly close data connection");
return false;
}
DELETENULL(m_DataSockets[siReplay]);
}
return true;
}
bool cClientSocket::DeleteRecording(cRemoteRecording *Recording) {
bool res;
cRemoteRecording *rec = NULL;
if (!CheckConnection())
return false;
CMD_LOCK;
if (!Command("LSTR"))
return false;
std::string buffer;
while ((res = Expect(250, &buffer))) {
if (rec == NULL) {
rec = new cRemoteRecording(buffer.c_str() + 4);
if (!rec->IsValid() || rec->Index() != Recording->Index())
DELETENULL(rec);
}
if (buffer[3] == ' ') break;
}
if (!res && buffer.substr(0, 3) != "550") {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't fetch recordings from %s:%d",
RemoteIp().c_str(), RemotePort());
if (rec != NULL) delete rec;
return false;
}
if (rec == NULL || *rec != *Recording) {
ERROR(tr("Recordings not in sync! Try again..."));
return false;
}
std::string command = (std::string)"DELR " + (const char*)itoa(Recording->Index());
if (!Command(command, 250)) {
ERROR(tr("Couldn't delete recording! Try again..."));
return false;
}
return true;
}
bool cClientSocket::SuspendServer(void) {
if (!CheckConnection()) return false;
CMD_LOCK;
if (!Command("SUSP", 220)) {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't suspend server");
return false;
}
return true;
}
bool cClientSocket::LoadTimers(cRemoteTimers &Timers) {
if (!CheckConnection()) return false;
CMD_LOCK;
if (!Command("LSTT"))
return false;
bool res;
std::string buffer;
while ((res = Expect(250, &buffer))) {
cRemoteTimer *timer = new cRemoteTimer(buffer.c_str() + 4);
Dprintf("timer valid: %d\n", timer->IsValid());
if (timer->IsValid())
Timers.Add(timer);
if (buffer[3] == ' ') break;
}
if (!res && buffer.substr(0, 3) != "550") {
if (errno == 0)
esyslog("ERROR: Streamdev: Couldn't fetch recordings from %s:%d",
RemoteIp().c_str(), RemotePort());
return false;
}
return res;
}
bool cClientSocket::SaveTimer(cRemoteTimer *Old, cRemoteTimer &New) {
if (!CheckConnection()) return false;
CMD_LOCK;
if (New.Index() == -1) { // New timer
std::string command = (std::string)"NEWT " + (const char*)New.ToText();
if (!Command(command, 250)) {
ERROR(tr("Couldn't save timer! Try again..."));
return false;
}
} else { // Modified timer
std::string command = (std::string)"LSTT " + (const char*)itoa(New.Index());
if (!Command(command))
return false;
std::string buffer;
if (!Expect(250, &buffer)) {
if (errno == 0)
ERROR(tr("Timers not in sync! Try again..."));
else
ERROR(tr("Server error! Try again..."));
return false;
}
cRemoteTimer oldstate(buffer.c_str() + 4);
if (oldstate != *Old) {
/*Dprintf("old timer: %d,%d,%d,%d,%d,%d,%s,%d,%s,%d\n", oldstate.m_Index,
oldstate.m_Active,oldstate.m_Day,oldstate.m_Start,oldstate.m_StartTime,oldstate.m_Priority,oldstate.m_File,oldstate.m_FirstDay,(const char*)oldstate.m_Summary,oldstate.m_Channel->Number());
Dprintf("new timer: %d,%d,%d,%d,%d,%d,%s,%d,%s,%d\n", Old->m_Index,
Old->m_Active,Old->m_Day,Old->m_Start,Old->m_StartTime,Old->m_Priority,Old->m_File,Old->m_FirstDay,(const char*)Old->m_Summary,Old->m_Channel->Number());*/
ERROR(tr("Timers not in sync! Try again..."));
return false;
}
command = (std::string)"MODT " + (const char*)itoa(New.Index()) + " "
+ (const char*)New.ToText();
if (!Command(command, 250)) {
ERROR(tr("Couldn't save timer! Try again..."));
return false;
}
}
return true;
}
bool cClientSocket::DeleteTimer(cRemoteTimer *Timer) {
if (!CheckConnection()) return false;
CMD_LOCK;
std::string command = (std::string)"LSTT " + (const char*)itoa(Timer->Index());
if (!Command(command))
return false;
std::string buffer;
if (!Expect(250, &buffer)) {
if (errno == 0)
ERROR(tr("Timers not in sync! Try again..."));
else
ERROR(tr("Server error! Try again..."));
return false;
}
cRemoteTimer oldstate(buffer.c_str() + 4);
if (oldstate != *Timer) {
ERROR(tr("Timers not in sync! Try again..."));
return false;
}
command = (std::string)"DELT " + (const char*)itoa(Timer->Index());
if (!Command(command, 250)) {
ERROR(tr("Couldn't delete timer! Try again..."));
return false;
}
return true;
}

72
client/socket.h Normal file
View File

@@ -0,0 +1,72 @@
/*
* $Id: socket.h,v 1.3 2005/02/08 17:22:35 lordjaxom Exp $
*/
#ifndef VDR_STREAMDEV_CLIENT_CONNECTION_H
#define VDR_STREAMDEV_CLIENT_CONNECTION_H
#include <tools/socket.h>
#include "common.h"
#include <string>
#define CMD_LOCK cMutexLock CmdLock((cMutex*)&m_Mutex)
class cRemoteRecordings;
class cRemoteRecording;
class cRemoteTimers;
class cRemoteTimer;
class cPES2TSRemux;
class cClientSocket: public cTBSocket {
private:
cTBSocket *m_DataSockets[si_Count];
cMutex m_Mutex;
char m_Buffer[BUFSIZ + 1]; // various uses
protected:
/* Send Command, and return true if the command results in Expected.
Returns false on failure, setting errno appropriately if it has been
a system failure. If Expected is zero, returns immediately after
sending the command. */
bool Command(const std::string &Command, uint Expected = 0, uint TimeoutMs = 1500);
/* Fetch results from an ongoing Command called with Expected == 0. Returns
true if the response has the code Expected, returning an internal buffer
in the array pointer pointed to by Result. Returns false on failure,
setting errno appropriately if it has been a system failure. */
bool Expect(uint Expected, std::string *Result = NULL, uint TimeoutMs = 1500);
public:
cClientSocket(void);
virtual ~cClientSocket();
void Reset(void);
bool CheckConnection(void);
bool ProvidesChannel(const cChannel *Channel, int Priority);
bool CreateDataConnection(eSocketId Id);
bool SetChannelDevice(const cChannel *Channel);
bool SetPid(int Pid, bool On);
#if VDRVERSNUM >= 10300
bool SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On);
#endif
bool CloseDvr(void);
bool SynchronizeEPG(void);
bool LoadRecordings(cRemoteRecordings &Recordings);
bool StartReplay(const char *Filename);
bool AbortReplay(void);
bool DeleteRecording(cRemoteRecording *Recording);
bool LoadTimers(cRemoteTimers &Timers);
bool SaveTimer(cRemoteTimer *Old, cRemoteTimer &New);
bool DeleteTimer(cRemoteTimer *Timer);
bool SuspendServer(void);
bool Quit(void);
cTBSocket *DataSocket(eSocketId Id) const;
};
extern class cClientSocket ClientSocket;
#endif // VDR_STREAMDEV_CLIENT_CONNECTION_H