mirror of
https://projects.vdr-developer.org/git/vdr-plugin-streamdev.git
synced 2023-10-10 17:16:51 +00:00
Initial revision
This commit is contained in:
125
client/assembler.c
Normal file
125
client/assembler.c
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* $Id: assembler.c,v 1.1 2004/12/30 22:44:04 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
32
client/assembler.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* $Id: assembler.h,v 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
|
||||
|
185
client/device.c
Normal file
185
client/device.c
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* $Id: device.c,v 1.1 2004/12/30 22:44:00 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include "client/device.h"
|
||||
#include "client/setup.h"
|
||||
#include "client/assembler.h"
|
||||
#include "client/filter.h"
|
||||
|
||||
#include "tools/select.h"
|
||||
#include "tools/string.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::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;
|
||||
|
||||
m_Channel = Channel;
|
||||
return ClientSocket.SetChannelDevice(m_Channel);
|
||||
}
|
||||
|
||||
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");
|
||||
if (ClientSocket.CreateDataConnection(siLive)) {
|
||||
m_Assembler = new cStreamdevAssembler(ClientSocket.DataSocket(siLive));
|
||||
m_TSBuffer = new cTSBuffer(m_Assembler->ReadPipe(), MEGABYTE(2),
|
||||
CardIndex() + 1);
|
||||
Dprintf("waiting\n");
|
||||
m_Assembler->WaitForFill();
|
||||
Dprintf("resuming\n");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cStreamdevDevice::CloseDvr(void) {
|
||||
Dprintf("CloseDvr\n");
|
||||
|
||||
ClientSocket.CloseDvr();
|
||||
DELETENULL(m_TSBuffer);
|
||||
DELETENULL(m_Assembler);
|
||||
}
|
||||
|
||||
bool cStreamdevDevice::GetTSPacket(uchar *&Data) {
|
||||
if (m_TSBuffer) {
|
||||
int r;
|
||||
while ((r = m_TSBuffer->Read()) >= 0) {
|
||||
Data = m_TSBuffer->Get();
|
||||
#if VDRVERSNUM >= 10300
|
||||
if (Data != NULL) {
|
||||
u_short pid = (((u_char)Data[1] & PID_MASK_HI) << 8) | Data[2];
|
||||
u_char tid = Data[3];
|
||||
if (m_Filters->Matches(pid, tid)) {
|
||||
m_Filters->Put(Data);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
if (FATALERRNO) {
|
||||
LOG_ERROR;
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
58
client/device.h
Normal file
58
client/device.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* $Id: device.h,v 1.1 2004/12/30 22:44:00 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(void) { return m_TSBuffer != NULL; }
|
||||
|
||||
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 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
|
141
client/filter.c
Normal file
141
client/filter.c
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* $Id: filter.c,v 1.1 2004/12/30 22:44:04 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include "client/filter.h"
|
||||
#include "client/socket.h"
|
||||
#include "tools/select.h"
|
||||
#include "common.h"
|
||||
|
||||
#include <vdr/ringbuffer.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) {
|
||||
static time_t firsterr = 0;
|
||||
static int errcnt = 0;
|
||||
static bool showerr = true;
|
||||
|
||||
int p = m_RingBuffer->Put(Data, TS_SIZE);
|
||||
if (p != TS_SIZE) {
|
||||
++errcnt;
|
||||
if (showerr) {
|
||||
if (firsterr == 0)
|
||||
firsterr = time_ms();
|
||||
else if (firsterr + BUFOVERTIME > time_ms() && errcnt > BUFOVERCOUNT) {
|
||||
esyslog("ERROR: too many buffer overflows, logging stopped");
|
||||
showerr = false;
|
||||
firsterr = time_ms();
|
||||
}
|
||||
} else if (firsterr + BUFOVERTIME < time_ms()) {
|
||||
showerr = true;
|
||||
firsterr = 0;
|
||||
errcnt = 0;
|
||||
}
|
||||
|
||||
if (showerr)
|
||||
esyslog("ERROR: ring buffer overflow (%d bytes dropped)", TS_SIZE - p);
|
||||
else
|
||||
firsterr = time_ms();
|
||||
}
|
||||
}
|
||||
|
||||
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
64
client/filter.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* $Id: filter.h,v 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
|
1047
client/menu.c
Normal file
1047
client/menu.c
Normal file
File diff suppressed because it is too large
Load Diff
144
client/menu.h
Normal file
144
client/menu.h
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* $Id: menu.h,v 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
|
||||
|
475
client/remote.c
Normal file
475
client/remote.c
Normal file
@@ -0,0 +1,475 @@
|
||||
/*
|
||||
* $Id: remote.c,v 1.1 2004/12/30 22:44:02 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#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", (const char*)m_StartTime);
|
||||
if (*ptr == 0) return;
|
||||
if (isspace(*ptr)) ++ptr;
|
||||
if (*ptr == 0) return;
|
||||
m_Name = ptr;
|
||||
Dprintf("file: %s\n", (const char*)m_Name);
|
||||
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, '~')) != NULL)
|
||||
t++;
|
||||
else
|
||||
t = (const char*)m_Name;
|
||||
|
||||
asprintf(&m_TitleBuffer, "%s%c%c%s", (const char*)m_StartTime, 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;
|
||||
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;
|
||||
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", (const char*)m_Summary);
|
||||
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.IsNull())
|
||||
summary = strreplace(strdup(m_Summary), ':', '|');
|
||||
|
||||
asprintf(&m_Buffer, "%d:%s:%s:%04d:%04d:%d:%d:%s:%s", m_Active,
|
||||
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);
|
||||
}
|
||||
|
132
client/remote.h
Normal file
132
client/remote.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* $Id: remote.h,v 1.1 2004/12/30 22:44:03 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_REMOTE_H
|
||||
#define VDR_STREAMDEV_REMOTE_H
|
||||
|
||||
#include <vdr/config.h>
|
||||
|
||||
#include "tools/string.h"
|
||||
|
||||
#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;
|
||||
cTBString m_StartTime;
|
||||
cTBString m_Name;
|
||||
cTBString 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; }
|
||||
bool IsNew(void) const { return m_IsNew; }
|
||||
const char *Name(void) const { return m_Name; }
|
||||
const char *Summary(void) const { return m_Summary; }
|
||||
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;
|
||||
cTBString 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 cTBString &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
|
83
client/setup.c
Normal file
83
client/setup.c
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* $Id: setup.c,v 1.1 2004/12/30 22:44:03 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;
|
||||
StreamPIDS = true;
|
||||
#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);
|
||||
else if (strcmp(Name, "StreamPIDS") == 0) StreamPIDS = 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);
|
||||
AddBoolEdit (tr("MultiPID Streaming"), m_NewSetup.StreamPIDS);
|
||||
#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);
|
||||
SetupStore("StreamPIDS", m_NewSetup.StreamPIDS);
|
||||
#if VDRVERSNUM >= 10300
|
||||
SetupStore("StreamFilters", m_NewSetup.StreamFilters);
|
||||
#endif
|
||||
SetupStore("SyncEPG", m_NewSetup.SyncEPG);
|
||||
|
||||
StreamdevClientSetup = m_NewSetup;
|
||||
|
||||
cStreamdevDevice::ReInit();
|
||||
}
|
||||
|
39
client/setup.h
Normal file
39
client/setup.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* $Id: setup.h,v 1.1 2004/12/30 22:44:03 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;
|
||||
int StreamPIDS;
|
||||
#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
|
591
client/socket.c
Normal file
591
client/socket.c
Normal file
@@ -0,0 +1,591 @@
|
||||
/*
|
||||
* $Id: socket.c,v 1.1 2004/12/30 22:44:04 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include <tools/select.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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) {
|
||||
m_StreamPIDS = false;
|
||||
|
||||
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 cTBString &Command, uint Expected,
|
||||
uint TimeoutMs) {
|
||||
cTBString pkt;
|
||||
time_t st;
|
||||
|
||||
errno = 0;
|
||||
|
||||
pkt = Command + "\015\012";
|
||||
Dprintf("OUT: |%s|\n", (const char*)Command);
|
||||
|
||||
st = time_ms();
|
||||
if (!TimedWrite((const char*)pkt, pkt.Length(), TimeoutMs)) {
|
||||
esyslog("Streamdev: Lost connection to %s:%d: %s",
|
||||
(const char*)RemoteIp(), RemotePort(), strerror(errno));
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Expected != 0) {
|
||||
TimeoutMs -= time_ms() - st;
|
||||
return Expect(Expected, NULL, TimeoutMs);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::Expect(uint Expected, cTBString *Result, uint TimeoutMs) {
|
||||
char *buffer;
|
||||
char *endptr;
|
||||
int bufcount;
|
||||
bool res;
|
||||
|
||||
errno = 0;
|
||||
|
||||
buffer = new char[BUFSIZ + 1];
|
||||
|
||||
if ((bufcount = ReadUntil(buffer, BUFSIZ, "\012", TimeoutMs))
|
||||
== -1) {
|
||||
esyslog("Streamdev: Lost connection to %s:%d: %s",
|
||||
(const char*)RemoteIp(), RemotePort(), strerror(errno));
|
||||
Close();
|
||||
delete[] buffer;
|
||||
return false;
|
||||
}
|
||||
if (buffer[bufcount - 1] == '\015')
|
||||
--bufcount;
|
||||
buffer[bufcount] = '\0';
|
||||
Dprintf("IN: |%s|\n", buffer);
|
||||
|
||||
if (Result != NULL)
|
||||
*Result = buffer;
|
||||
|
||||
res = strtoul(buffer, &endptr, 10) == Expected;
|
||||
delete[] buffer;
|
||||
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)){
|
||||
esyslog("ERROR: Streamdev: Couldn't connect to %s:%d: %s",
|
||||
(const char*)StreamdevClientSetup.RemoteIp,
|
||||
StreamdevClientSetup.RemotePort, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Expect(220)) {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Didn't receive greeting from %s:%d",
|
||||
(const char*)RemoteIp(), RemotePort());
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Command((cTBString)"CAPS TS", 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't negotiate capabilities on %s:%d",
|
||||
(const char*)RemoteIp(), RemotePort());
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StreamdevClientSetup.StreamPIDS) {
|
||||
if (!Command("CAPS TSPIDS", 220)) {
|
||||
if (errno != 0) {
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
esyslog("ERROR: Streamdev: Server %s:%d isn't capable of PID streaming",
|
||||
(const char*)RemoteIp(), RemotePort());
|
||||
} else
|
||||
m_StreamPIDS = true;
|
||||
}
|
||||
|
||||
isyslog("Streamdev: Connected to server %s:%d using capabilities TS%s",
|
||||
(const char*)RemoteIp(), RemotePort(), m_StreamPIDS ? ", TSPIDS" : "");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::ProvidesChannel(const cChannel *Channel, int Priority) {
|
||||
cTBString buffer;
|
||||
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (!Command("PROV " + cTBString::Number(Priority) + " "
|
||||
+ Channel->GetChannelID().ToString()))
|
||||
return false;
|
||||
|
||||
if (!Expect(220, &buffer)) {
|
||||
if (buffer.Left(3) != "560" && errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't check if %s:%d provides channel %s",
|
||||
(const char*)RemoteIp(), RemotePort(), Channel->Name());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::CreateDataConnection(eSocketId Id) {
|
||||
int idx;
|
||||
cTBSocket listen(SOCK_STREAM);
|
||||
cTBString buffer;
|
||||
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
if (m_DataSockets[Id] != NULL)
|
||||
DELETENULL(m_DataSockets[Id]);
|
||||
|
||||
if (!listen.Listen((const char*)LocalIp(), 0, 1)) {
|
||||
esyslog("ERROR: Streamdev: Couldn't create data connection: %s",
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer.Format("PORT %d %s,%d,%d", Id, (const char*)LocalIp(),
|
||||
(listen.LocalPort() >> 8) & 0xff, listen.LocalPort() & 0xff);
|
||||
idx = 5;
|
||||
while ((idx = buffer.Find('.', idx + 1)) != -1)
|
||||
buffer[idx] = ',';
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (!Command(buffer, 220)) {
|
||||
Dprintf("error: %m\n");
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't establish data connection to %s:%d",
|
||||
(const char*)RemoteIp(), 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",
|
||||
(const char*)RemoteIp(), 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;
|
||||
|
||||
if (!Command((cTBString)"TUNE " + Channel->GetChannelID().ToString(), 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't tune %s:%d to channel %s",
|
||||
(const char*)RemoteIp(), RemotePort(), Channel->Name());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::SetPid(int Pid, bool On) {
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
if (m_StreamPIDS) {
|
||||
Dprintf("m_StreamPIDS is ON\n");
|
||||
CMD_LOCK;
|
||||
|
||||
if (!Command((On ? "ADDP " : "DELP ") + cTBString::Number(Pid), 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("Streamdev: Pid %d not available from %s:%d", Pid,
|
||||
(const char*)LocalIp(), LocalPort());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if VDRVERSNUM >= 10300
|
||||
bool cClientSocket::SetFilter(ushort Pid, uchar Tid, uchar Mask, bool On) {
|
||||
cTBString cmd;
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
cmd.Format("%s %hu %hhu %hhu", On ? "ADDF" : "DELF", Pid, Tid, Mask);
|
||||
if (!Command(cmd, 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("Streamdev: Filter %hu, %hhu, %hhu not available from %s:%d",
|
||||
Pid, Tid, Mask, (const char*)LocalIp(), LocalPort());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool cClientSocket::CloseDvr(void) {
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (m_DataSockets[siLive] != NULL) {
|
||||
if (!Command("ABRT " + cTBString::Number(siLive), 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) {
|
||||
cTBString buffer;
|
||||
bool res;
|
||||
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 ((res = Expect(215, &buffer))) {
|
||||
if (buffer[3] == ' ') break;
|
||||
fputs((const char*)buffer + 4, epgfd);
|
||||
fputc('\n', epgfd);
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't fetch EPG data from %s:%d",
|
||||
(const char*)RemoteIp(), 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",
|
||||
(const char*)RemoteIp(), RemotePort());
|
||||
}
|
||||
Close();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool cClientSocket::LoadRecordings(cRemoteRecordings &Recordings) {
|
||||
cTBString buffer;
|
||||
bool res;
|
||||
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (!Command("LSTR"))
|
||||
return false;
|
||||
|
||||
while ((res = Expect(250, &buffer))) {
|
||||
cRemoteRecording *rec = new cRemoteRecording((const char*)buffer + 4);
|
||||
Dprintf("recording valid: %d\n", rec->IsValid());
|
||||
if (rec->IsValid())
|
||||
Recordings.Add(rec);
|
||||
else
|
||||
delete rec;
|
||||
if (buffer[3] == ' ') break;
|
||||
}
|
||||
|
||||
if (!res && buffer.Left(3) != "550") {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't fetch recordings from %s:%d",
|
||||
(const char*)RemoteIp(), RemotePort());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (cRemoteRecording *r = Recordings.First(); r; r = Recordings.Next(r)) {
|
||||
if (!Command("LSTR " + cTBString::Number(r->Index())))
|
||||
return false;
|
||||
|
||||
if (Expect(250, &buffer))
|
||||
r->ParseInfo((const char*)buffer + 4);
|
||||
else if (buffer.Left(3) != "550") {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't fetch details for recording from "
|
||||
"%s:%d", (const char*)RemoteIp(), RemotePort());
|
||||
return false;
|
||||
}
|
||||
Dprintf("recording complete: %d\n", r->Index());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool cClientSocket::StartReplay(const char *Filename) {
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (!Command((cTBString)"PLAY " + Filename, 220)) {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't replay \"%s\" from %s:%d",
|
||||
Filename, (const char*)RemoteIp(), RemotePort());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::AbortReplay(void) {
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (m_DataSockets[siReplay] != NULL) {
|
||||
if (!Command("ABRT " + cTBString::Number(siReplay), 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;
|
||||
cTBString buffer;
|
||||
cRemoteRecording *rec = NULL;
|
||||
|
||||
if (!CheckConnection())
|
||||
return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (!Command("LSTR"))
|
||||
return false;
|
||||
|
||||
while ((res = Expect(250, &buffer))) {
|
||||
if (rec == NULL) {
|
||||
rec = new cRemoteRecording((const char*)buffer + 4);
|
||||
if (!rec->IsValid() || rec->Index() != Recording->Index())
|
||||
DELETENULL(rec);
|
||||
}
|
||||
if (buffer[3] == ' ') break;
|
||||
}
|
||||
|
||||
if (!res && buffer.Left(3) != "550") {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't fetch recordings from %s:%d",
|
||||
(const char*)RemoteIp(), RemotePort());
|
||||
if (rec != NULL) delete rec;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rec == NULL || *rec != *Recording) {
|
||||
ERROR(tr("Recordings not in sync! Try again..."));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Command("DELR " + cTBString::Number(Recording->Index()), 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) {
|
||||
cTBString buffer;
|
||||
bool res;
|
||||
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (!Command("LSTT"))
|
||||
return false;
|
||||
|
||||
while ((res = Expect(250, &buffer))) {
|
||||
cRemoteTimer *timer = new cRemoteTimer((const char*)buffer + 4);
|
||||
Dprintf("timer valid: %d\n", timer->IsValid());
|
||||
if (timer->IsValid())
|
||||
Timers.Add(timer);
|
||||
if (buffer[3] == ' ') break;
|
||||
}
|
||||
|
||||
if (!res && buffer.Left(3) != "550") {
|
||||
if (errno == 0)
|
||||
esyslog("ERROR: Streamdev: Couldn't fetch recordings from %s:%d",
|
||||
(const char*)RemoteIp(), RemotePort());
|
||||
return false;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool cClientSocket::SaveTimer(cRemoteTimer *Old, cRemoteTimer &New) {
|
||||
cTBString buffer;
|
||||
|
||||
if (!CheckConnection()) return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (New.Index() == -1) { // New timer
|
||||
if (!Command((cTBString)"NEWT " + New.ToText(), 250)) {
|
||||
ERROR(tr("Couldn't save timer! Try again..."));
|
||||
return false;
|
||||
}
|
||||
} else { // Modified timer
|
||||
if (!Command("LSTT " + cTBString::Number(New.Index())))
|
||||
return false;
|
||||
|
||||
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((const char*)buffer + 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;
|
||||
}
|
||||
|
||||
if (!Command("MODT " + cTBString::Number(New.Index()) + " "
|
||||
+ New.ToText(), 250)) {
|
||||
ERROR(tr("Couldn't save timer! Try again..."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cClientSocket::DeleteTimer(cRemoteTimer *Timer) {
|
||||
cTBString buffer;
|
||||
|
||||
if (!CheckConnection())
|
||||
return false;
|
||||
|
||||
CMD_LOCK;
|
||||
|
||||
if (!Command("LSTT " + cTBString::Number(Timer->Index())))
|
||||
return false;
|
||||
|
||||
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((const char*)buffer + 4);
|
||||
|
||||
if (oldstate != *Timer) {
|
||||
ERROR(tr("Timers not in sync! Try again..."));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Command("DELT " + cTBString::Number(Timer->Index()), 250)) {
|
||||
ERROR(tr("Couldn't delete timer! Try again..."));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
71
client/socket.h
Normal file
71
client/socket.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* $Id: socket.h,v 1.1 2004/12/30 22:44:04 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_CLIENT_CONNECTION_H
|
||||
#define VDR_STREAMDEV_CLIENT_CONNECTION_H
|
||||
|
||||
#include <tools/socket.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define CMD_LOCK cMutexLock CmdLock((cMutex*)&m_Mutex)
|
||||
|
||||
class cRemoteRecordings;
|
||||
class cRemoteRecording;
|
||||
class cRemoteTimers;
|
||||
class cRemoteTimer;
|
||||
class cPES2TSRemux;
|
||||
|
||||
class cClientSocket: public cTBSocket {
|
||||
private:
|
||||
bool m_StreamPIDS;
|
||||
cTBSocket *m_DataSockets[si_Count];
|
||||
cMutex m_Mutex;
|
||||
|
||||
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 cTBString &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, cTBString *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
|
Reference in New Issue
Block a user