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:
50
server/component.c
Normal file
50
server/component.c
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* $Id: component.c,v 1.1 2004/12/30 22:44:18 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include "server/component.h"
|
||||
#include "server/connection.h"
|
||||
|
||||
#include <vdr/tools.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
cServerComponent::cServerComponent(const char *Protocol, const char *ListenIp,
|
||||
uint ListenPort) {
|
||||
m_Protocol = Protocol;
|
||||
m_ListenIp = ListenIp;
|
||||
m_ListenPort = ListenPort;
|
||||
}
|
||||
|
||||
cServerComponent::~cServerComponent() {
|
||||
}
|
||||
|
||||
bool cServerComponent::Init(void) {
|
||||
if (!m_Listen.Listen(m_ListenIp, m_ListenPort, 5)) {
|
||||
esyslog("Streamdev: Couldn't listen (%s) %s:%d: %s", m_Protocol, m_ListenIp,
|
||||
m_ListenPort, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
isyslog("Streamdev: Listening (%s) on port %d", m_Protocol, m_ListenPort);
|
||||
return true;
|
||||
}
|
||||
|
||||
void cServerComponent::Exit(void) {
|
||||
m_Listen.Close();
|
||||
}
|
||||
|
||||
cServerConnection *cServerComponent::CanAct(const cTBSelect &Select) {
|
||||
if (Select.CanRead(m_Listen)) {
|
||||
cServerConnection *client = NewConnection();
|
||||
if (client->Accept(m_Listen)) {
|
||||
isyslog("Streamdev: Accepted new client (%s) %s:%d", m_Protocol,
|
||||
(const char*)client->RemoteIp(), client->RemotePort());
|
||||
return client;
|
||||
} else {
|
||||
esyslog("Streamdev: Couldn't accept (%s): %s", m_Protocol,
|
||||
strerror(errno));
|
||||
delete client;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
50
server/component.h
Normal file
50
server/component.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* $Id: component.h,v 1.1 2004/12/30 22:44:18 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_SERVERS_COMPONENT_H
|
||||
#define VDR_STREAMDEV_SERVERS_COMPONENT_H
|
||||
|
||||
#include "tools/socket.h"
|
||||
#include "tools/select.h"
|
||||
|
||||
#include <vdr/tools.h>
|
||||
|
||||
class cServerConnection;
|
||||
|
||||
/* Basic TCP listen server, all functions virtual if a derivation wants to do
|
||||
things different */
|
||||
|
||||
class cServerComponent: public cListObject {
|
||||
private:
|
||||
cTBSocket m_Listen;
|
||||
const char *m_Protocol;
|
||||
const char *m_ListenIp;
|
||||
uint m_ListenPort;
|
||||
|
||||
public:
|
||||
cServerComponent(const char *Protocol, const char *ListenIp, uint ListenPort);
|
||||
virtual ~cServerComponent();
|
||||
|
||||
/* Starts listening on the specified Port, override if you want to do things
|
||||
different */
|
||||
virtual bool Init(void);
|
||||
|
||||
/* Stops listening, override if you want to do things different */
|
||||
virtual void Exit(void);
|
||||
|
||||
/* Adds the listening socket to the Select object */
|
||||
virtual void AddSelect(cTBSelect &Select) const { Select.Add(m_Listen); }
|
||||
|
||||
/* Accepts the connection on a NewConnection() object and calls the
|
||||
Welcome() on it, override if you want to do things different */
|
||||
virtual cServerConnection *CanAct(const cTBSelect &Select);
|
||||
|
||||
/* Returns a new connection object for CanAct */
|
||||
virtual cServerConnection *NewConnection(void) const = 0;
|
||||
};
|
||||
|
||||
class cServerComponents: public cList<cServerComponent> {
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_SERVERS_COMPONENT_H
|
15
server/componentHTTP.c
Normal file
15
server/componentHTTP.c
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* $Id: componentHTTP.c,v 1.1 2004/12/30 22:44:19 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include "server/componentHTTP.h"
|
||||
#include "server/connectionHTTP.h"
|
||||
#include "server/setup.h"
|
||||
|
||||
cComponentHTTP::cComponentHTTP(void):
|
||||
cServerComponent("HTTP", StreamdevServerSetup.HTTPBindIP,
|
||||
StreamdevServerSetup.HTTPServerPort) {
|
||||
}
|
||||
|
||||
cComponentHTTP::~cComponentHTTP() {
|
||||
}
|
26
server/componentHTTP.h
Normal file
26
server/componentHTTP.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* $Id: componentHTTP.h,v 1.1 2004/12/30 22:44:19 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_HTTPSERVER_H
|
||||
#define VDR_STREAMDEV_HTTPSERVER_H
|
||||
|
||||
#include "server/component.h"
|
||||
#include "server/connectionHTTP.h"
|
||||
|
||||
#include <tools/socket.h>
|
||||
#include <tools/select.h>
|
||||
|
||||
class cComponentHTTP: public cServerComponent {
|
||||
public:
|
||||
cComponentHTTP(void);
|
||||
~cComponentHTTP(void);
|
||||
|
||||
virtual cServerConnection *NewConnection(void) const;
|
||||
};
|
||||
|
||||
inline cServerConnection *cComponentHTTP::NewConnection(void) const {
|
||||
return new cConnectionHTTP;
|
||||
}
|
||||
|
||||
#endif // VDR_STREAMDEV_HTTPSERVER_H
|
15
server/componentVTP.c
Normal file
15
server/componentVTP.c
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* $Id: componentVTP.c,v 1.1 2004/12/30 22:44:19 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include "server/componentVTP.h"
|
||||
#include "server/connectionVTP.h"
|
||||
#include "server/setup.h"
|
||||
|
||||
cComponentVTP::cComponentVTP(void):
|
||||
cServerComponent("VTP", StreamdevServerSetup.VTPBindIP,
|
||||
StreamdevServerSetup.VTPServerPort) {
|
||||
}
|
||||
|
||||
cComponentVTP::~cComponentVTP() {
|
||||
}
|
29
server/componentVTP.h
Normal file
29
server/componentVTP.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* $Id: componentVTP.h,v 1.1 2004/12/30 22:44:19 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_SERVERS_SERVERVTP_H
|
||||
#define VDR_STREAMDEV_SERVERS_SERVERVTP_H
|
||||
|
||||
#include "server/component.h"
|
||||
#include "server/connectionVTP.h"
|
||||
|
||||
#include <tools/socket.h>
|
||||
#include <tools/select.h>
|
||||
|
||||
class cComponentVTP: public cServerComponent {
|
||||
private:
|
||||
cTBSocket m_Listen;
|
||||
|
||||
public:
|
||||
cComponentVTP(void);
|
||||
virtual ~cComponentVTP();
|
||||
|
||||
virtual cServerConnection *NewConnection(void) const;
|
||||
};
|
||||
|
||||
inline cServerConnection *cComponentVTP::NewConnection(void) const {
|
||||
return new cConnectionVTP;
|
||||
}
|
||||
|
||||
#endif // VDR_STREAMDEV_SERVERS_SERVERVTP_H
|
172
server/connection.c
Normal file
172
server/connection.c
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* $Id: connection.c,v 1.1 2004/12/30 22:44:19 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include "server/connection.h"
|
||||
#include "server/setup.h"
|
||||
#include "server/suspend.h"
|
||||
#include "common.h"
|
||||
|
||||
#include <vdr/tools.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
cServerConnection::cServerConnection(const char *Protocol) {
|
||||
m_RdBytes = 0;
|
||||
m_WrBytes = 0;
|
||||
m_WrOffs = 0;
|
||||
m_DeferClose = false;
|
||||
m_Protocol = Protocol;
|
||||
}
|
||||
|
||||
cServerConnection::~cServerConnection() {
|
||||
}
|
||||
|
||||
bool cServerConnection::CanAct(const cTBSelect &Select) {
|
||||
if (Select.CanRead(*this)) {
|
||||
int b;
|
||||
if ((b = Read(m_RdBuf + m_RdBytes, sizeof(m_RdBuf) - m_RdBytes - 1)) < 0) {
|
||||
esyslog("Streamdev: Read from client (%s) %s:%d failed: %s", m_Protocol,
|
||||
(const char*)RemoteIp(), RemotePort(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (b == 0) {
|
||||
isyslog("Streamdev: Client (%s) %s:%d closed connection", m_Protocol,
|
||||
(const char*)RemoteIp(), RemotePort());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_RdBytes += b;
|
||||
m_RdBuf[m_RdBytes] = '\0';
|
||||
return ParseBuffer();
|
||||
}
|
||||
|
||||
if (Select.CanWrite(*this)) {
|
||||
int b;
|
||||
if ((b = Write(m_WrBuf + m_WrOffs, m_WrBytes - m_WrOffs)) < 0) {
|
||||
esyslog("Streamdev: Write to client (%s) %s:%d failed: %s", m_Protocol,
|
||||
(const char*)RemoteIp(), RemotePort(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
m_WrOffs += b;
|
||||
if (m_WrOffs == m_WrBytes) {
|
||||
m_WrBytes = 0;
|
||||
m_WrOffs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_WrBytes == 0) {
|
||||
if (m_DeferClose)
|
||||
return false;
|
||||
Flushed();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cServerConnection::ParseBuffer(void) {
|
||||
char *ep;
|
||||
bool res;
|
||||
|
||||
while ((ep = strchr(m_RdBuf, '\012')) != NULL) {
|
||||
*ep = '\0';
|
||||
if (ep > m_RdBuf && *(ep-1) == '\015')
|
||||
*(ep-1) = '\0';
|
||||
|
||||
Dprintf("IN: |%s|\n", m_RdBuf);
|
||||
res = Command(m_RdBuf);
|
||||
m_RdBytes -= ++ep - m_RdBuf;
|
||||
if (m_RdBytes > 0)
|
||||
memmove(m_RdBuf, ep, m_RdBytes);
|
||||
if (res == false)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cServerConnection::Respond(const char *Message) {
|
||||
uint len = strlen(Message);
|
||||
if (m_WrBytes + len + 2 > sizeof(m_WrBuf)) {
|
||||
esyslog("Streamdev: Output buffer overflow (%s) for %s:%d", m_Protocol,
|
||||
(const char*)RemoteIp(), RemotePort());
|
||||
return false;
|
||||
}
|
||||
Dprintf("OUT: |%s|\n", Message);
|
||||
memcpy(m_WrBuf + m_WrBytes, Message, len);
|
||||
m_WrBuf[m_WrBytes + len] = '\015';
|
||||
m_WrBuf[m_WrBytes + len + 1] = '\012';
|
||||
m_WrBytes += len + 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
cDevice *cServerConnection::GetDevice(const cChannel *Channel, int Priority) {
|
||||
cDevice *device = NULL;
|
||||
|
||||
/*Dprintf("+ Statistics:\n");
|
||||
Dprintf("+ Current Channel: %d\n", cDevice::CurrentChannel());
|
||||
Dprintf("+ Current Device: %d\n", cDevice::ActualDevice()->CardIndex());
|
||||
Dprintf("+ Transfer Mode: %s\n", cDevice::ActualDevice()
|
||||
== cDevice::PrimaryDevice() ? "false" : "true");
|
||||
Dprintf("+ Replaying: %s\n", cDevice::PrimaryDevice()->Replaying() ? "true"
|
||||
: "false");*/
|
||||
|
||||
Dprintf(" * GetDevice(const cChannel*, int)\n");
|
||||
Dprintf(" * -------------------------------\n");
|
||||
|
||||
device = cDevice::GetDevice(Channel, Priority);
|
||||
|
||||
Dprintf(" * Found following device: %p (%d)\n", device,
|
||||
device ? device->CardIndex() + 1 : 0);
|
||||
if (device == cDevice::ActualDevice())
|
||||
Dprintf(" * is actual device\n");
|
||||
if (!cSuspendCtl::IsActive() && StreamdevServerSetup.SuspendMode != smAlways)
|
||||
Dprintf(" * NOT suspended\n");
|
||||
|
||||
if (!device || (device == cDevice::ActualDevice()
|
||||
&& !cSuspendCtl::IsActive()
|
||||
&& StreamdevServerSetup.SuspendMode != smAlways)) {
|
||||
// mustn't switch actual device
|
||||
// maybe a device would be free if THIS connection did turn off its streams?
|
||||
Dprintf(" * trying again...\n");
|
||||
const cChannel *current = Channels.GetByNumber(cDevice::CurrentChannel());
|
||||
isyslog("streamdev-server: Detaching current receiver");
|
||||
Detach();
|
||||
device = cDevice::GetDevice(Channel, Priority);
|
||||
Attach();
|
||||
Dprintf(" * Found following device: %p (%d)\n", device,
|
||||
device ? device->CardIndex() + 1 : 0);
|
||||
if (device == cDevice::ActualDevice())
|
||||
Dprintf(" * is actual device\n");
|
||||
if (!cSuspendCtl::IsActive()
|
||||
&& StreamdevServerSetup.SuspendMode != smAlways)
|
||||
Dprintf(" * NOT suspended\n");
|
||||
if (current && !TRANSPONDER(Channel, current))
|
||||
Dprintf(" * NOT same transponder\n");
|
||||
if (device && (device == cDevice::ActualDevice()
|
||||
&& !cSuspendCtl::IsActive()
|
||||
&& StreamdevServerSetup.SuspendMode != smAlways
|
||||
&& current != NULL
|
||||
&& !TRANSPONDER(Channel, current))) {
|
||||
// now we would have to switch away live tv...let's see if live tv
|
||||
// can be handled by another device
|
||||
cDevice *newdev = NULL;
|
||||
for (int i = 0; i < cDevice::NumDevices(); ++i) {
|
||||
cDevice *dev = cDevice::GetDevice(i);
|
||||
if (dev->ProvidesChannel(current, 0) && dev != device) {
|
||||
newdev = dev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Dprintf(" * Found device for live tv: %p (%d)\n", newdev,
|
||||
newdev ? newdev->CardIndex() + 1 : 0);
|
||||
if (newdev == NULL || newdev == device)
|
||||
// no suitable device to continue live TV, giving up...
|
||||
device = NULL;
|
||||
else
|
||||
newdev->SwitchChannel(current, true);
|
||||
}
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
87
server/connection.h
Normal file
87
server/connection.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* $Id: connection.h,v 1.1 2004/12/30 22:44:19 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_SERVER_CONNECTION_H
|
||||
#define VDR_STREAMDEV_SERVER_CONNECTION_H
|
||||
|
||||
#include "tools/socket.h"
|
||||
#include "tools/select.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
class cChannel;
|
||||
class cDevice;
|
||||
|
||||
/* Basic capabilities of a straight text-based protocol, most functions
|
||||
virtual to support more complicated protocols */
|
||||
|
||||
class cServerConnection: public cListObject, public cTBSocket {
|
||||
private:
|
||||
char m_RdBuf[8192];
|
||||
uint m_RdBytes;
|
||||
|
||||
char m_WrBuf[8192];
|
||||
uint m_WrBytes;
|
||||
uint m_WrOffs;
|
||||
|
||||
const char *m_Protocol;
|
||||
|
||||
bool m_DeferClose;
|
||||
|
||||
public:
|
||||
/* If you derive, specify a short string such as HTTP for Protocol, which
|
||||
will be displayed in error messages */
|
||||
cServerConnection(const char *Protocol);
|
||||
virtual ~cServerConnection();
|
||||
|
||||
/* Gets called if the client has been accepted by the core */
|
||||
virtual void Welcome(void) { }
|
||||
|
||||
/* Gets called if the client has been rejected by the core */
|
||||
virtual void Reject(void) { DeferClose(); }
|
||||
|
||||
/* Adds itself to the Select object, if data can be received or if data is
|
||||
to be sent. Override if necessary */
|
||||
virtual void AddSelect(cTBSelect &Select) const;
|
||||
|
||||
/* Receives incoming data and calls ParseBuffer on it. Also writes queued
|
||||
output data if possible. Override if necessary */
|
||||
virtual bool CanAct(const cTBSelect &Select);
|
||||
|
||||
/* Called by CanAct(), parses the input buffer for full lines (terminated
|
||||
either by '\012' or '\015\012') and calls Command on them, if any */
|
||||
virtual bool ParseBuffer(void);
|
||||
|
||||
/* Will be called when a command terminated by a newline has been received */
|
||||
virtual bool Command(char *Cmd) = 0;
|
||||
|
||||
/* Will put Message into the response queue, which will be sent in the next
|
||||
server cycle. Note that Message will be line-terminated by Respond */
|
||||
bool Respond(const char *Message);
|
||||
|
||||
/* Will make the socket close after sending all queued output data */
|
||||
void DeferClose(void) { m_DeferClose = true; }
|
||||
|
||||
/* Will retrieve an unused device for transmitting data. Use the returned
|
||||
cDevice in a following call to StartTransfer */
|
||||
cDevice *GetDevice(const cChannel *Channel, int Priority);
|
||||
|
||||
virtual void Flushed(void) {}
|
||||
|
||||
virtual void Detach(void) = 0;
|
||||
virtual void Attach(void) = 0;
|
||||
};
|
||||
|
||||
class cServerConnections: public cList<cServerConnection> {
|
||||
};
|
||||
|
||||
inline void cServerConnection::AddSelect(cTBSelect &Select) const {
|
||||
if (m_WrBytes > 0)
|
||||
Select.Add(*this, true);
|
||||
|
||||
if (m_WrBytes == 0 && m_RdBytes < sizeof(m_RdBuf) - 1)
|
||||
Select.Add(*this, false);
|
||||
}
|
||||
|
||||
#endif // VDR_STREAMDEV_SERVER_CONNECTION_H
|
180
server/connectionHTTP.c
Normal file
180
server/connectionHTTP.c
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* $Id: connectionHTTP.c,v 1.1 2004/12/30 22:44:19 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include "server/connectionHTTP.h"
|
||||
#include "server/livestreamer.h"
|
||||
#include "server/setup.h"
|
||||
|
||||
cConnectionHTTP::cConnectionHTTP(void): cServerConnection("HTTP") {
|
||||
m_Channel = NULL;
|
||||
m_ListChannel = NULL;
|
||||
m_LiveStreamer = NULL;
|
||||
m_Status = hsRequest;
|
||||
m_StreamType = (eStreamType)StreamdevServerSetup.HTTPStreamType;
|
||||
m_Startup = false;
|
||||
}
|
||||
|
||||
cConnectionHTTP::~cConnectionHTTP() {
|
||||
if (m_LiveStreamer != NULL) delete m_LiveStreamer;
|
||||
}
|
||||
|
||||
void cConnectionHTTP::Detach(void) {
|
||||
if (m_LiveStreamer != NULL) m_LiveStreamer->Detach();
|
||||
}
|
||||
|
||||
void cConnectionHTTP::Attach(void) {
|
||||
if (m_LiveStreamer != NULL) m_LiveStreamer->Attach();
|
||||
}
|
||||
|
||||
bool cConnectionHTTP::Command(char *Cmd) {
|
||||
switch (m_Status) {
|
||||
case hsRequest:
|
||||
if (strncmp(Cmd, "GET ", 4) == 0) return CmdGET(Cmd + 4);
|
||||
else {
|
||||
DeferClose();
|
||||
m_Status = hsTransfer; // Ignore following lines
|
||||
return Respond("HTTP/1.0 400 Bad Request");
|
||||
}
|
||||
break;
|
||||
|
||||
case hsHeaders:
|
||||
if (*Cmd == '\0') {
|
||||
if (m_ListChannel != NULL) {
|
||||
m_Status = hsListing;
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: text/html")
|
||||
&& Respond("")
|
||||
&& Respond("<html><head><title>VDR Channel Listing</title></head>")
|
||||
&& Respond("<body><ul>");
|
||||
} else if (m_Channel == NULL) {
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 404 not found");
|
||||
}
|
||||
m_Status = hsTransfer;
|
||||
m_LiveStreamer = new cStreamdevLiveStreamer(0);
|
||||
cDevice *device = GetDevice(m_Channel, 0);
|
||||
if (device != NULL) {
|
||||
device->SwitchChannel(m_Channel, false);
|
||||
m_LiveStreamer->SetDevice(device);
|
||||
if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType, false)) {
|
||||
m_Startup = true;
|
||||
if (m_StreamType == stES && (m_Channel->Vpid() == 0
|
||||
|| m_Channel->Vpid() == 1 || m_Channel->Vpid() == 0x1FFF)) {
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: audio/mpeg")
|
||||
&& Respond((cTBString)"icy-name: " + m_Channel->Name())
|
||||
&& Respond("");
|
||||
} else {
|
||||
return Respond("HTTP/1.0 200 OK")
|
||||
&& Respond("Content-Type: video/mpeg")
|
||||
&& Respond("");
|
||||
}
|
||||
}
|
||||
}
|
||||
DELETENULL(m_LiveStreamer);
|
||||
DeferClose();
|
||||
return Respond("HTTP/1.0 409 Channel not available");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void cConnectionHTTP::Flushed(void) {
|
||||
if (m_Status == hsListing) {
|
||||
cTBString line;
|
||||
|
||||
if (m_ListChannel == NULL) {
|
||||
Respond("</ul></body></html>");
|
||||
DeferClose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_ListChannel->GroupSep())
|
||||
line.Format("<li>--- %s ---</li>", m_ListChannel->Name());
|
||||
else
|
||||
line.Format("<li><a href=\"http://%s:%d/%s\">%s</a></li>",
|
||||
(const char*)LocalIp(), StreamdevServerSetup.HTTPServerPort,
|
||||
m_ListChannel->GetChannelID().ToString(), m_ListChannel->Name());
|
||||
if (!Respond(line))
|
||||
DeferClose();
|
||||
m_ListChannel = Channels.Next(m_ListChannel);
|
||||
} else if (m_Startup) {
|
||||
Dprintf("streamer start\n");
|
||||
m_LiveStreamer->Start(this);
|
||||
m_Startup = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool cConnectionHTTP::CmdGET(char *Opts) {
|
||||
cChannel *chan;
|
||||
char *ep;
|
||||
|
||||
Opts = skipspace(Opts);
|
||||
while (*Opts == '/')
|
||||
++Opts;
|
||||
|
||||
if (strncasecmp(Opts, "PS/", 3) == 0) {
|
||||
m_StreamType = stPS;
|
||||
Opts+=3;
|
||||
} else if (strncasecmp(Opts, "PES/", 4) == 0) {
|
||||
m_StreamType = stPES;
|
||||
Opts+=4;
|
||||
} else if (strncasecmp(Opts, "TS/", 3) == 0) {
|
||||
m_StreamType = stTS;
|
||||
Opts+=3;
|
||||
} else if (strncasecmp(Opts, "ES/", 3) == 0) {
|
||||
m_StreamType = stES;
|
||||
Opts+=3;
|
||||
}
|
||||
|
||||
while (*Opts == '/')
|
||||
++Opts;
|
||||
for (ep = Opts + strlen(Opts); ep >= Opts && !isspace(*ep); --ep)
|
||||
;
|
||||
*ep = '\0';
|
||||
|
||||
if (strncmp(Opts, "channels.htm", 12) == 0) {
|
||||
m_ListChannel = Channels.First();
|
||||
m_Status = hsHeaders;
|
||||
} else if ((chan = ChannelFromString(Opts)) != NULL) {
|
||||
m_Channel = chan;
|
||||
m_Status = hsHeaders;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool cHTTPConnection::Listing(void) {
|
||||
cChannel *chan;
|
||||
cTBString line;
|
||||
|
||||
Respond(200, "OK");
|
||||
Respond("Content-Type: text/html");
|
||||
Respond("");
|
||||
Respond("<html><head><title>VDR Channel Listing</title></head>");
|
||||
Respond("<body><ul>");
|
||||
|
||||
for (chan = Channels.First(); chan != NULL; chan = Channels.Next(chan)) {
|
||||
if (chan->GroupSep() && !*chan->Name())
|
||||
continue;
|
||||
|
||||
if (chan->GroupSep())
|
||||
line.Format("<li>--- %s ---</li>", chan->Name());
|
||||
else
|
||||
line.Format("<li><a href=\"http://%s:%d/%s\">%s</a></li>",
|
||||
(const char*)m_Socket.LocalIp(), StreamdevServerSetup.HTTPServerPort,
|
||||
chan->GetChannelID().ToString(), chan->Name());
|
||||
Respond(line);
|
||||
}
|
||||
|
||||
Respond("</ul></body></html>");
|
||||
|
||||
m_DeferClose = true;
|
||||
return true;
|
||||
}
|
||||
#endif
|
44
server/connectionHTTP.h
Normal file
44
server/connectionHTTP.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* $Id: connectionHTTP.h,v 1.1 2004/12/30 22:44:18 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_SERVERS_CONNECTIONHTTP_H
|
||||
#define VDR_STREAMDEV_SERVERS_CONNECTIONHTTP_H
|
||||
|
||||
#include "connection.h"
|
||||
|
||||
#include <tools/select.h>
|
||||
|
||||
class cChannel;
|
||||
class cStreamdevLiveStreamer;
|
||||
|
||||
class cConnectionHTTP: public cServerConnection {
|
||||
private:
|
||||
enum eHTTPStatus {
|
||||
hsRequest,
|
||||
hsHeaders,
|
||||
hsTransfer,
|
||||
hsListing,
|
||||
};
|
||||
|
||||
cChannel *m_Channel;
|
||||
cChannel *m_ListChannel;
|
||||
cStreamdevLiveStreamer *m_LiveStreamer;
|
||||
eStreamType m_StreamType;
|
||||
eHTTPStatus m_Status;
|
||||
bool m_Startup;
|
||||
|
||||
public:
|
||||
cConnectionHTTP(void);
|
||||
virtual ~cConnectionHTTP();
|
||||
|
||||
virtual void Detach(void);
|
||||
virtual void Attach(void);
|
||||
|
||||
virtual bool Command(char *Cmd);
|
||||
bool CmdGET(char *Opts);
|
||||
|
||||
virtual void Flushed(void);
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
|
553
server/connectionVTP.c
Normal file
553
server/connectionVTP.c
Normal file
@@ -0,0 +1,553 @@
|
||||
/*
|
||||
* $Id: connectionVTP.c,v 1.1 2004/12/30 22:44:21 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include "server/connectionVTP.h"
|
||||
#include "server/livestreamer.h"
|
||||
#include "server/suspend.h"
|
||||
#include "setup.h"
|
||||
|
||||
#include <vdr/tools.h>
|
||||
#include <tools/select.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* VTP Response codes:
|
||||
220: Service ready
|
||||
221: Service closing connection
|
||||
500: Syntax error or Command unrecognized
|
||||
501: Wrong parameters or missing parameters
|
||||
550: Action not done
|
||||
551: Data connection not accepted
|
||||
560: Channel not available currently
|
||||
561: Capability not known
|
||||
562: Pid not available currently
|
||||
563: Recording not available (currently?)
|
||||
*/
|
||||
|
||||
cConnectionVTP::cConnectionVTP(void): cServerConnection("VTP") {
|
||||
m_StreamPIDS = false;
|
||||
m_LiveStreamer = NULL;
|
||||
m_ClientCaps = stTS;
|
||||
|
||||
memset(m_DataSockets, 0, sizeof(cTBSocket*) * si_Count);
|
||||
}
|
||||
|
||||
cConnectionVTP::~cConnectionVTP() {
|
||||
if (m_LiveStreamer != NULL) delete m_LiveStreamer;
|
||||
|
||||
for (int idx = 0; idx < si_Count; ++idx)
|
||||
if (m_DataSockets[idx] != NULL) delete m_DataSockets[idx];
|
||||
}
|
||||
|
||||
void cConnectionVTP::Welcome(void) {
|
||||
Respond(220, "Welcome to Video Disk Recorder (VTP)");
|
||||
}
|
||||
|
||||
void cConnectionVTP::Reject(void) {
|
||||
Respond(221, "Too many clients or client not allowed to connect");
|
||||
cServerConnection::Reject();
|
||||
}
|
||||
|
||||
void cConnectionVTP::Detach(void) {
|
||||
if (m_LiveStreamer != NULL) m_LiveStreamer->Detach();
|
||||
}
|
||||
|
||||
void cConnectionVTP::Attach(void) {
|
||||
if (m_LiveStreamer != NULL) m_LiveStreamer->Attach();
|
||||
}
|
||||
|
||||
bool cConnectionVTP::Command(char *Cmd) {
|
||||
char *ep;
|
||||
if ((ep = strchr(Cmd, ' ')) != NULL)
|
||||
*(ep++) = '\0';
|
||||
else
|
||||
ep = Cmd + strlen(Cmd);
|
||||
|
||||
if (strcasecmp(Cmd, "CAPS") == 0) return CmdCAPS(ep);
|
||||
else if (strcasecmp(Cmd, "PROV") == 0) return CmdPROV(ep);
|
||||
else if (strcasecmp(Cmd, "PORT") == 0) return CmdPORT(ep);
|
||||
else if (strcasecmp(Cmd, "TUNE") == 0) return CmdTUNE(ep);
|
||||
else if (strcasecmp(Cmd, "ADDP") == 0) return CmdADDP(ep);
|
||||
else if (strcasecmp(Cmd, "DELP") == 0) return CmdDELP(ep);
|
||||
else if (strcasecmp(Cmd, "ADDF") == 0) return CmdADDF(ep);
|
||||
else if (strcasecmp(Cmd, "DELF") == 0) return CmdDELF(ep);
|
||||
else if (strcasecmp(Cmd, "ABRT") == 0) return CmdABRT(ep);
|
||||
else if (strcasecmp(Cmd, "QUIT") == 0) return CmdQUIT(ep);
|
||||
else if (strcasecmp(Cmd, "SUSP") == 0) return CmdSUSP(ep);
|
||||
// Commands adopted from SVDRP
|
||||
else if (strcasecmp(Cmd, "LSTE") == 0) return CmdLSTE(ep);
|
||||
else if (strcasecmp(Cmd, "LSTR") == 0) return CmdLSTR(ep);
|
||||
else if (strcasecmp(Cmd, "DELR") == 0) return CmdDELR(ep);
|
||||
else if (strcasecmp(Cmd, "LSTT") == 0) return CmdLSTT(ep);
|
||||
else if (strcasecmp(Cmd, "MODT") == 0) return CmdMODT(ep);
|
||||
else if (strcasecmp(Cmd, "NEWT") == 0) return CmdNEWT(ep);
|
||||
else if (strcasecmp(Cmd, "DELT") == 0) return CmdDELT(ep);
|
||||
else
|
||||
return Respond(500, (cTBString)"Unknown Command '" + Cmd + "'");
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdCAPS(char *Opts) {
|
||||
if (strcasecmp(Opts, "TSPIDS") == 0) m_StreamPIDS = true;
|
||||
else {
|
||||
int idx = 0;
|
||||
while (idx < st_Count && strcasecmp(Opts, StreamTypes[idx]) != 0)
|
||||
++idx;
|
||||
|
||||
if (idx == st_Count)
|
||||
return Respond(561, (cTBString)"Capability \"" + Opts + "\" not known");
|
||||
|
||||
m_ClientCaps = (eStreamType)idx;
|
||||
}
|
||||
return Respond(220, (cTBString)"Capability \"" + Opts + "\" accepted");
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdPROV(char *Opts) {
|
||||
cChannel *chan;
|
||||
int prio;
|
||||
char *ep;
|
||||
|
||||
prio = strtol(Opts, &ep, 10);
|
||||
if (ep == Opts || !isspace(*ep))
|
||||
return Respond(501, "Use: PROV Priority Channel");
|
||||
|
||||
Opts = skipspace(ep);
|
||||
if ((chan = ChannelFromString(Opts)) == NULL)
|
||||
return Respond(550, (cTBString)"Undefined channel \"" + Opts + "\"");
|
||||
|
||||
return GetDevice(chan, prio) != NULL
|
||||
? Respond(220, "Channel available")
|
||||
: Respond(560, "Channel not available");
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdPORT(char *Opts) {
|
||||
uint id, dataport = 0;
|
||||
char dataip[20];
|
||||
char *ep, *ipoffs;
|
||||
int n;
|
||||
|
||||
id = strtoul(Opts, &ep, 10);
|
||||
if (ep == Opts || !isspace(*ep))
|
||||
return Respond(500, "Use: PORT Id Destination");
|
||||
|
||||
if (id >= si_Count)
|
||||
return Respond(501, "Wrong connection id " + cTBString::Number(id));
|
||||
|
||||
Opts = skipspace(ep);
|
||||
n = 0;
|
||||
ipoffs = dataip;
|
||||
while ((ep = strchr(Opts, ',')) != NULL) {
|
||||
if (n < 4) {
|
||||
memcpy(ipoffs, Opts, ep - Opts);
|
||||
ipoffs += ep - Opts;
|
||||
if (n < 3) *(ipoffs++) = '.';
|
||||
} else if (n == 4) {
|
||||
*ep = 0;
|
||||
dataport = strtoul(Opts, NULL, 10) << 8;
|
||||
} else
|
||||
break;
|
||||
Opts = ep + 1;
|
||||
++n;
|
||||
}
|
||||
*ipoffs = '\0';
|
||||
|
||||
if (n != 5)
|
||||
return Respond(501, "Argument count invalid (must be 6 values)");
|
||||
|
||||
dataport |= strtoul(Opts, NULL, 10);
|
||||
|
||||
isyslog("Streamdev: Setting data connection to %s:%d", dataip, dataport);
|
||||
|
||||
m_DataSockets[id] = new cTBSocket(SOCK_STREAM);
|
||||
if (!m_DataSockets[id]->Connect(dataip, dataport)) {
|
||||
esyslog("ERROR: Streamdev: Couldn't open data connection to %s:%d: %s",
|
||||
dataip, dataport, strerror(errno));
|
||||
DELETENULL(m_DataSockets[id]);
|
||||
return Respond(551, "Couldn't open data connection");
|
||||
}
|
||||
|
||||
if (id == siLive)
|
||||
m_LiveStreamer->Start(m_DataSockets[id]);
|
||||
|
||||
return Respond(220, "Port command ok, data connection opened");
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdTUNE(char *Opts) {
|
||||
const cChannel *chan;
|
||||
cDevice *dev;
|
||||
|
||||
if ((chan = ChannelFromString(Opts)) == NULL)
|
||||
return Respond(550, (cTBString)"Undefined channel \"" + Opts + "\"");
|
||||
|
||||
if ((dev = GetDevice(chan, 0)) == NULL)
|
||||
return Respond(560, "Channel not available");
|
||||
|
||||
if (!dev->SwitchChannel(chan, false))
|
||||
return Respond(560, "Channel not available");
|
||||
|
||||
delete m_LiveStreamer;
|
||||
m_LiveStreamer = new cStreamdevLiveStreamer(1);
|
||||
m_LiveStreamer->SetChannel(chan, m_ClientCaps, m_StreamPIDS);
|
||||
m_LiveStreamer->SetDevice(dev);
|
||||
|
||||
return Respond(220, "Channel tuned");
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdADDP(char *Opts) {
|
||||
int pid;
|
||||
char *end;
|
||||
|
||||
pid = strtoul(Opts, &end, 10);
|
||||
if (end == Opts || (*end != '\0' && *end != ' '))
|
||||
return Respond(500, "Use: ADDP Pid");
|
||||
|
||||
return m_LiveStreamer && m_LiveStreamer->SetPid(pid, true)
|
||||
? Respond(220, "Pid " + cTBString::Number(pid) + " available")
|
||||
: Respond(560, "Pid " + cTBString::Number(pid) + " not available");
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdDELP(char *Opts) {
|
||||
int pid;
|
||||
char *end;
|
||||
|
||||
pid = strtoul(Opts, &end, 10);
|
||||
if (end == Opts || (*end != '\0' && *end != ' '))
|
||||
return Respond(500, "Use: DELP Pid");
|
||||
|
||||
return m_LiveStreamer && m_LiveStreamer->SetPid(pid, false)
|
||||
? Respond(220, "Pid " + cTBString::Number(pid) + " stopped")
|
||||
: Respond(560, "Pid " + cTBString::Number(pid) + " not transferring");
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdADDF(char *Opts) {
|
||||
#if VDRVERSNUM >= 10300
|
||||
int pid, tid, mask;
|
||||
char *ep;
|
||||
|
||||
if (m_LiveStreamer == NULL)
|
||||
return Respond(560, "Can't set filters without a stream");
|
||||
|
||||
pid = strtol(Opts, &ep, 10);
|
||||
if (ep == Opts || (*ep != ' '))
|
||||
return Respond(500, "Use: ADDF Pid Tid Mask");
|
||||
Opts = skipspace(ep);
|
||||
tid = strtol(Opts, &ep, 10);
|
||||
if (ep == Opts || (*ep != ' '))
|
||||
return Respond(500, "Use: ADDF Pid Tid Mask");
|
||||
Opts = skipspace(ep);
|
||||
mask = strtol(Opts, &ep, 10);
|
||||
if (ep == Opts || (*ep != '\0' && *ep != ' '))
|
||||
return Respond(500, "Use: ADDF Pid Tid Mask");
|
||||
|
||||
return m_LiveStreamer->SetFilter(pid, tid, mask, true)
|
||||
? Respond(220, "Filter " + cTBString::Number(pid) + " transferring")
|
||||
: Respond(560, "Filter " + cTBString::Number(pid) + " not available");
|
||||
#else
|
||||
return Respond(500, "ADDF known but unimplemented with VDR < 1.3.0");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdDELF(char *Opts) {
|
||||
#if VDRVERSNUM >= 10307
|
||||
int pid, tid, mask;
|
||||
char *ep;
|
||||
|
||||
if (m_LiveStreamer == NULL)
|
||||
return Respond(560, "Can't delete filters without a stream");
|
||||
|
||||
pid = strtol(Opts, &ep, 10);
|
||||
if (ep == Opts || (*ep != ' '))
|
||||
return Respond(500, "Use: DELF Pid Tid Mask");
|
||||
Opts = skipspace(ep);
|
||||
tid = strtol(Opts, &ep, 10);
|
||||
if (ep == Opts || (*ep != ' '))
|
||||
return Respond(500, "Use: DELF Pid Tid Mask");
|
||||
Opts = skipspace(ep);
|
||||
mask = strtol(Opts, &ep, 10);
|
||||
if (ep == Opts || (*ep != '\0' && *ep != ' '))
|
||||
return Respond(500, "Use: DELF Pid Tid Mask");
|
||||
|
||||
return m_LiveStreamer->SetFilter(pid, tid, mask, false)
|
||||
? Respond(220, "Filter " + cTBString::Number(pid) + " stopped")
|
||||
: Respond(560, "Filter " + cTBString::Number(pid) + " not transferring");
|
||||
#else
|
||||
return Respond(500, "DELF known but unimplemented with VDR < 1.3.0");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdABRT(char *Opts) {
|
||||
uint id;
|
||||
char *ep;
|
||||
|
||||
id = strtoul(Opts, &ep, 10);
|
||||
if (ep == Opts || (*ep != '\0' && *ep != ' '))
|
||||
return Respond(500, "Use: ABRT Id");
|
||||
|
||||
time_t st = time_ms();
|
||||
if (id == siLive)
|
||||
DELETENULL(m_LiveStreamer);
|
||||
|
||||
Dprintf("ABRT took %ld ms\n", time_ms() - st);
|
||||
DELETENULL(m_DataSockets[id]);
|
||||
return Respond(220, "Data connection closed");
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdQUIT(char *Opts) {
|
||||
if (!Respond(221, "Video Disk Recorder closing connection"))
|
||||
return false;
|
||||
DeferClose();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdSUSP(char *Opts) {
|
||||
if (StreamdevServerSetup.SuspendMode == smAlways || cSuspendCtl::IsActive())
|
||||
return Respond(220, "Server is suspended");
|
||||
else if (StreamdevServerSetup.SuspendMode == smOffer
|
||||
&& StreamdevServerSetup.AllowSuspend) {
|
||||
cControl::Launch(new cSuspendCtl);
|
||||
return Respond(220, "Server is suspended");
|
||||
} else
|
||||
return Respond(550, "Client may not suspend server");
|
||||
}
|
||||
|
||||
// Functions adopted from SVDRP
|
||||
#define INIT_WRAPPER() bool _res = true
|
||||
#define Reply(x...) _res &= ReplyWrapper(x)
|
||||
#define EXIT_WRAPPER() return _res
|
||||
|
||||
bool cConnectionVTP::ReplyWrapper(int Code, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
char *buffer;
|
||||
vasprintf(&buffer, fmt, ap);
|
||||
int npos;
|
||||
if (buffer[npos = strlen(buffer)-1] == '\n')
|
||||
buffer[npos] = '\0';
|
||||
bool res = Respond(Code, buffer);
|
||||
free(buffer);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdLSTE(char *Option) {
|
||||
#if VDRVERSNUM < 10300
|
||||
cMutexLock MutexLock;
|
||||
#else
|
||||
cSchedulesLock MutexLock;
|
||||
#endif
|
||||
INIT_WRAPPER();
|
||||
/* we need to create a blocking copy of the socket here */
|
||||
int dupfd = dup(*this);
|
||||
fcntl(dupfd, F_SETFL, fcntl(dupfd, F_GETFL) & ~O_NONBLOCK);
|
||||
#if VDRVERSNUM < 10300
|
||||
const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
|
||||
#else
|
||||
const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
|
||||
#endif
|
||||
if (Schedules) {
|
||||
FILE *f = fdopen(dupfd, "w");
|
||||
if (f) {
|
||||
Schedules->Dump(f, "215-");
|
||||
fflush(f);
|
||||
Reply(215, "End of EPG data");
|
||||
fclose(f);
|
||||
}
|
||||
else
|
||||
Reply(451, "Can't open file connection");
|
||||
}
|
||||
else
|
||||
Reply(451, "Can't get EPG data");
|
||||
EXIT_WRAPPER();
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdLSTR(char *Option) {
|
||||
INIT_WRAPPER();
|
||||
bool recordings = Recordings.Load();
|
||||
Recordings.Sort();
|
||||
if (*Option) {
|
||||
if (isnumber(Option)) {
|
||||
cRecording *recording = Recordings.Get(strtol(Option, NULL, 10) - 1);
|
||||
if (recording) {
|
||||
if (recording->Summary()) {
|
||||
char *summary = strdup(recording->Summary());
|
||||
Reply(250, "%s", strreplace(summary,'\n','|'));
|
||||
free(summary);
|
||||
}
|
||||
else
|
||||
Reply(550, "No summary availabe");
|
||||
}
|
||||
else
|
||||
Reply(550, "Recording \"%s\" not found", Option);
|
||||
}
|
||||
else
|
||||
Reply(501, "Error in recording number \"%s\"", Option);
|
||||
}
|
||||
else if (recordings) {
|
||||
cRecording *recording = Recordings.First();
|
||||
while (recording) {
|
||||
Reply(recording == Recordings.Last() ? 250 : -250, "%d %s", recording->Index() + 1, recording->Title(' ', true));
|
||||
recording = Recordings.Next(recording);
|
||||
}
|
||||
}
|
||||
else
|
||||
Reply(550, "No recordings available");
|
||||
EXIT_WRAPPER();
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdDELR(char *Option) {
|
||||
INIT_WRAPPER();
|
||||
if (*Option) {
|
||||
if (isnumber(Option)) {
|
||||
cRecording *recording = Recordings.Get(strtol(Option, NULL, 10) - 1);
|
||||
if (recording) {
|
||||
if (recording->Delete())
|
||||
Reply(250, "Recording \"%s\" deleted", Option);
|
||||
else
|
||||
Reply(554, "Error while deleting recording!");
|
||||
}
|
||||
else
|
||||
Reply(550, "Recording \"%s\" not found%s", Option, Recordings.Count() ? "" : " (use LSTR before deleting)");
|
||||
}
|
||||
else
|
||||
Reply(501, "Error in recording number \"%s\"", Option);
|
||||
}
|
||||
else
|
||||
Reply(501, "Missing recording number");
|
||||
EXIT_WRAPPER();
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdLSTT(char *Option) {
|
||||
INIT_WRAPPER();
|
||||
if (*Option) {
|
||||
if (isnumber(Option)) {
|
||||
cTimer *timer = Timers.Get(strtol(Option, NULL, 10) - 1);
|
||||
if (timer)
|
||||
Reply(250, "%d %s", timer->Index() + 1, timer->ToText(true));
|
||||
else
|
||||
Reply(501, "Timer \"%s\" not defined", Option);
|
||||
}
|
||||
else
|
||||
Reply(501, "Error in timer number \"%s\"", Option);
|
||||
}
|
||||
else if (Timers.Count()) {
|
||||
for (int i = 0; i < Timers.Count(); i++) {
|
||||
cTimer *timer = Timers.Get(i);
|
||||
if (timer)
|
||||
Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, timer->ToText(true));
|
||||
else
|
||||
Reply(501, "Timer \"%d\" not found", i + 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
Reply(550, "No timers defined");
|
||||
EXIT_WRAPPER();
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdMODT(char *Option) {
|
||||
INIT_WRAPPER();
|
||||
if (*Option) {
|
||||
char *tail;
|
||||
int n = strtol(Option, &tail, 10);
|
||||
if (tail && tail != Option) {
|
||||
tail = skipspace(tail);
|
||||
cTimer *timer = Timers.Get(n - 1);
|
||||
if (timer) {
|
||||
cTimer t = *timer;
|
||||
if (strcasecmp(tail, "ON") == 0)
|
||||
#if VDRVERSNUM < 10300
|
||||
t.SetActive(taActive);
|
||||
#else
|
||||
t.SetFlags(tfActive);
|
||||
#endif
|
||||
else if (strcasecmp(tail, "OFF") == 0)
|
||||
#if VDRVERSNUM < 10300
|
||||
t.SetActive(taInactive);
|
||||
#else
|
||||
t.ClrFlags(tfActive);
|
||||
#endif
|
||||
else if (!t.Parse(tail)) {
|
||||
Reply(501, "Error in timer settings");
|
||||
EXIT_WRAPPER();
|
||||
}
|
||||
*timer = t;
|
||||
Timers.Save();
|
||||
#if VDRVERSNUM < 10300
|
||||
isyslog("timer %d modified (%s)", timer->Index() + 1,
|
||||
timer->Active() ? "active" : "inactive");
|
||||
#else
|
||||
isyslog("timer %d modified (%s)", timer->Index() + 1,
|
||||
timer->HasFlags(tfActive) ? "active" : "inactive");
|
||||
#endif
|
||||
Reply(250, "%d %s", timer->Index() + 1, timer->ToText(true));
|
||||
}
|
||||
else
|
||||
Reply(501, "Timer \"%d\" not defined", n);
|
||||
}
|
||||
else
|
||||
Reply(501, "Error in timer number");
|
||||
}
|
||||
else
|
||||
Reply(501, "Missing timer settings");
|
||||
EXIT_WRAPPER();
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdNEWT(char *Option) {
|
||||
INIT_WRAPPER();
|
||||
if (*Option) {
|
||||
cTimer *timer = new cTimer;
|
||||
if (timer->Parse(Option)) {
|
||||
cTimer *t = Timers.GetTimer(timer);
|
||||
if (!t) {
|
||||
Timers.Add(timer);
|
||||
Timers.Save();
|
||||
isyslog("timer %d added", timer->Index() + 1);
|
||||
Reply(250, "%d %s", timer->Index() + 1, timer->ToText(true));
|
||||
EXIT_WRAPPER();
|
||||
}
|
||||
else
|
||||
Reply(550, "Timer already defined: %d %s", t->Index() + 1, t->ToText(true));
|
||||
}
|
||||
else
|
||||
Reply(501, "Error in timer settings");
|
||||
delete timer;
|
||||
}
|
||||
else
|
||||
Reply(501, "Missing timer settings");
|
||||
EXIT_WRAPPER();
|
||||
}
|
||||
|
||||
bool cConnectionVTP::CmdDELT(char *Option) {
|
||||
INIT_WRAPPER();
|
||||
if (*Option) {
|
||||
if (isnumber(Option)) {
|
||||
cTimer *timer = Timers.Get(strtol(Option, NULL, 10) - 1);
|
||||
if (timer) {
|
||||
if (!timer->Recording()) {
|
||||
Timers.Del(timer);
|
||||
Timers.Save();
|
||||
isyslog("timer %s deleted", Option);
|
||||
Reply(250, "Timer \"%s\" deleted", Option);
|
||||
}
|
||||
else
|
||||
Reply(550, "Timer \"%s\" is recording", Option);
|
||||
}
|
||||
else
|
||||
Reply(501, "Timer \"%s\" not defined", Option);
|
||||
}
|
||||
else
|
||||
Reply(501, "Error in timer number \"%s\"", Option);
|
||||
}
|
||||
else
|
||||
Reply(501, "Missing timer number");
|
||||
EXIT_WRAPPER();
|
||||
}
|
||||
|
||||
bool cConnectionVTP::Respond(int Code, const char *Message) {
|
||||
cTBString pkt;
|
||||
if (Code < 0)
|
||||
pkt.Format("%03d-%s", -Code, Message);
|
||||
else
|
||||
pkt.Format("%03d %s", Code, Message);
|
||||
return cServerConnection::Respond((const char*)pkt);
|
||||
}
|
||||
|
57
server/connectionVTP.h
Normal file
57
server/connectionVTP.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#ifndef VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
|
||||
#define VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
|
||||
|
||||
#include "server/connection.h"
|
||||
|
||||
class cDevice;
|
||||
class cTBSocket;
|
||||
class cStreamdevLiveStreamer;
|
||||
|
||||
class cConnectionVTP: public cServerConnection {
|
||||
private:
|
||||
cTBSocket *m_DataSockets[si_Count];
|
||||
eStreamType m_ClientCaps;
|
||||
bool m_StreamPIDS;
|
||||
|
||||
cStreamdevLiveStreamer *m_LiveStreamer;
|
||||
|
||||
// Members adopted from SVDRP
|
||||
cRecordings Recordings;
|
||||
|
||||
public:
|
||||
cConnectionVTP(void);
|
||||
virtual ~cConnectionVTP();
|
||||
|
||||
virtual void Welcome(void);
|
||||
virtual void Reject(void);
|
||||
|
||||
virtual void Detach(void);
|
||||
virtual void Attach(void);
|
||||
|
||||
bool Command(char *Cmd);
|
||||
bool CmdCAPS(char *Opts);
|
||||
bool CmdPROV(char *Opts);
|
||||
bool CmdPORT(char *Opts);
|
||||
bool CmdTUNE(char *Opts);
|
||||
bool CmdADDP(char *Opts);
|
||||
bool CmdDELP(char *Opts);
|
||||
bool CmdADDF(char *Opts);
|
||||
bool CmdDELF(char *Opts);
|
||||
bool CmdABRT(char *Opts);
|
||||
bool CmdQUIT(char *Opts);
|
||||
bool CmdSUSP(char *Opts);
|
||||
|
||||
// Commands adopted from SVDRP
|
||||
bool ReplyWrapper(int Code, const char *fmt, ...);
|
||||
bool CmdLSTE(char *Opts);
|
||||
bool CmdLSTR(char *Opts);
|
||||
bool CmdDELR(char *Opts);
|
||||
bool CmdLSTT(char *Opts);
|
||||
bool CmdMODT(char *Opts);
|
||||
bool CmdNEWT(char *Opts);
|
||||
bool CmdDELT(char *Opts);
|
||||
|
||||
bool Respond(int Code, const char *Message);
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
|
64
server/livefilter.c
Normal file
64
server/livefilter.c
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* $Id: livefilter.c,v 1.1 2004/12/30 22:44:27 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include "server/livefilter.h"
|
||||
#include "server/livestreamer.h"
|
||||
#include "common.h"
|
||||
|
||||
#if VDRVERSNUM >= 10300
|
||||
|
||||
cStreamdevLiveFilter::cStreamdevLiveFilter(cStreamdevLiveStreamer *Streamer) {
|
||||
m_Streamer = Streamer;
|
||||
}
|
||||
|
||||
cStreamdevLiveFilter::~cStreamdevLiveFilter() {
|
||||
}
|
||||
|
||||
void cStreamdevLiveFilter::Process(u_short Pid, u_char Tid, const u_char *Data,
|
||||
int Length) {
|
||||
static time_t firsterr = 0;
|
||||
static int errcnt = 0;
|
||||
static bool showerr = true;
|
||||
|
||||
uchar buffer[TS_SIZE];
|
||||
int length = Length;
|
||||
int pos = 0;
|
||||
|
||||
while (length > 0) {
|
||||
int chunk = min(length, TS_SIZE - 5);
|
||||
buffer[0] = TS_SYNC_BYTE;
|
||||
buffer[1] = (Pid >> 8) & 0xff;
|
||||
buffer[2] = Pid & 0xff;
|
||||
buffer[3] = Tid;
|
||||
buffer[4] = (uchar)chunk;
|
||||
memcpy(buffer + 5, Data + pos, chunk);
|
||||
length -= chunk;
|
||||
pos += chunk;
|
||||
|
||||
int p = m_Streamer->Put(buffer, 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // VDRVERSNUM >= 10300
|
29
server/livefilter.h
Normal file
29
server/livefilter.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* $Id: livefilter.h,v 1.1 2004/12/30 22:44:27 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMEV_LIVEFILTER_H
|
||||
#define VDR_STREAMEV_LIVEFILTER_H
|
||||
|
||||
#include <vdr/config.h>
|
||||
|
||||
# if VDRVERSNUM >= 10300
|
||||
|
||||
#include <vdr/filter.h>
|
||||
|
||||
class cStreamdevLiveFilter: public cFilter {
|
||||
friend class cStreamdevLiveStreamer;
|
||||
|
||||
private:
|
||||
cStreamdevLiveStreamer *m_Streamer;
|
||||
|
||||
protected:
|
||||
virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
|
||||
|
||||
public:
|
||||
cStreamdevLiveFilter(cStreamdevLiveStreamer *Streamer);
|
||||
virtual ~cStreamdevLiveFilter();
|
||||
};
|
||||
|
||||
# endif // VDRVERSNUM >= 10300
|
||||
#endif // VDR_STREAMEV_LIVEFILTER_H
|
242
server/livestreamer.c
Normal file
242
server/livestreamer.c
Normal file
@@ -0,0 +1,242 @@
|
||||
#include <vdr/ringbuffer.h>
|
||||
|
||||
#include "server/livestreamer.h"
|
||||
#include "remux/ts2ps.h"
|
||||
#include "remux/ts2es.h"
|
||||
#include "common.h"
|
||||
|
||||
cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevLiveStreamer *Streamer,
|
||||
int Ca, int Priority,
|
||||
int Pid1, int Pid2, int Pid3, int Pid4,
|
||||
int Pid5, int Pid6, int Pid7, int Pid8,
|
||||
int Pid9, int Pid10, int Pid11, int Pid12,
|
||||
int Pid13, int Pid14, int Pid15, int Pid16):
|
||||
cReceiver(Ca, Priority, 16,
|
||||
Pid1, Pid2, Pid3, Pid4, Pid5, Pid6, Pid7, Pid8,
|
||||
Pid9, Pid10, Pid11, Pid12, Pid13, Pid14, Pid15, Pid16) {
|
||||
m_Streamer = Streamer;
|
||||
}
|
||||
|
||||
cStreamdevLiveReceiver::~cStreamdevLiveReceiver() {
|
||||
Dprintf("Killing live receiver\n");
|
||||
Detach();
|
||||
}
|
||||
|
||||
void cStreamdevLiveReceiver::Receive(uchar *Data, int Length) {
|
||||
static time_t firsterr = 0;
|
||||
static int errcnt = 0;
|
||||
static bool showerr = true;
|
||||
|
||||
int p = m_Streamer->Put(Data, Length);
|
||||
if (p != Length) {
|
||||
++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)", Length - p);
|
||||
else
|
||||
firsterr = time_ms();
|
||||
}
|
||||
}
|
||||
|
||||
cStreamdevLiveStreamer::cStreamdevLiveStreamer(int Priority):
|
||||
cStreamdevStreamer("Live streamer") {
|
||||
m_Priority = Priority;
|
||||
m_Channel = NULL;
|
||||
m_Device = NULL;
|
||||
m_Receiver = NULL;
|
||||
m_Remux = NULL;
|
||||
m_Buffer = NULL;
|
||||
m_Sequence = 0;
|
||||
#if VDRVERSNUM >= 10300
|
||||
m_Filter = NULL;
|
||||
#endif
|
||||
memset(m_Pids, 0, sizeof(m_Pids));
|
||||
}
|
||||
|
||||
cStreamdevLiveStreamer::~cStreamdevLiveStreamer() {
|
||||
Dprintf("Desctructing Live streamer\n");
|
||||
delete m_Receiver;
|
||||
delete m_Remux;
|
||||
#if VDRVERSNUM >= 10300
|
||||
delete m_Filter;
|
||||
#endif
|
||||
free(m_Buffer);
|
||||
}
|
||||
|
||||
void cStreamdevLiveStreamer::Detach(void) {
|
||||
m_Device->Detach(m_Receiver);
|
||||
}
|
||||
|
||||
void cStreamdevLiveStreamer::Attach(void) {
|
||||
m_Device->AttachReceiver(m_Receiver);
|
||||
}
|
||||
|
||||
void cStreamdevLiveStreamer::Start(cTBSocket *Socket) {
|
||||
Dprintf("LIVESTREAMER START\n");
|
||||
cStreamdevStreamer::Start(Socket);
|
||||
}
|
||||
|
||||
bool cStreamdevLiveStreamer::SetPid(int Pid, bool On) {
|
||||
int idx;
|
||||
bool haspids = false;
|
||||
|
||||
if (On) {
|
||||
for (idx = 0; idx < MAXRECEIVEPIDS; ++idx) {
|
||||
if (m_Pids[idx] == Pid)
|
||||
return true; // No change needed
|
||||
else if (m_Pids[idx] == 0) {
|
||||
m_Pids[idx] = Pid;
|
||||
haspids = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx == MAXRECEIVEPIDS) {
|
||||
esyslog("ERROR: Streamdev: No free slot to receive pid %d\n", Pid);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
for (idx = 0; idx < MAXRECEIVEPIDS; ++idx) {
|
||||
if (m_Pids[idx] == Pid)
|
||||
m_Pids[idx] = 0;
|
||||
else if (m_Pids[idx] != 0)
|
||||
haspids = true;
|
||||
}
|
||||
}
|
||||
|
||||
DELETENULL(m_Receiver);
|
||||
if (haspids) {
|
||||
Dprintf("Creating Receiver to respect changed pids\n");
|
||||
m_Receiver = new cStreamdevLiveReceiver(this, m_Channel->Ca(), m_Priority,
|
||||
m_Pids[0], m_Pids[1], m_Pids[2], m_Pids[3],
|
||||
m_Pids[4], m_Pids[5], m_Pids[6], m_Pids[7],
|
||||
m_Pids[8], m_Pids[9], m_Pids[10], m_Pids[11],
|
||||
m_Pids[12], m_Pids[13], m_Pids[14], m_Pids[15]);
|
||||
if (m_Device != NULL) {
|
||||
Dprintf("Attaching new receiver\n");
|
||||
m_Device->AttachReceiver(m_Receiver);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, int StreamType,
|
||||
bool StreamPIDS) {
|
||||
Dprintf("Initializing Remuxer for full channel transfer\n");
|
||||
printf("ca pid: %d\n", Channel->Ca());
|
||||
m_Channel = Channel;
|
||||
switch (StreamType) {
|
||||
case stES:
|
||||
{
|
||||
int pid = ISRADIO(Channel) ? Channel->Apid1() : Channel->Vpid();
|
||||
m_Remux = new cTS2ESRemux(pid);
|
||||
return SetPid(pid, true);
|
||||
}
|
||||
|
||||
case stPES:
|
||||
m_Remux = new cTS2PSRemux(Channel->Vpid(), Channel->Apid1(),
|
||||
Channel->Apid2(), Channel->Dpid1(), 0, false);
|
||||
return SetPid(Channel->Vpid(), true)
|
||||
&& SetPid(Channel->Apid1(), true)
|
||||
&& SetPid(Channel->Apid2(), true)
|
||||
&& SetPid(Channel->Dpid1(), true);
|
||||
break;
|
||||
|
||||
case stPS:
|
||||
m_Remux = new cTS2PSRemux(Channel->Vpid(), Channel->Apid1(), 0, 0, 0, true);
|
||||
return SetPid(Channel->Vpid(), true)
|
||||
&& SetPid(Channel->Apid1(), true);
|
||||
break;
|
||||
|
||||
case stTS:
|
||||
if (!StreamPIDS) {
|
||||
return SetPid(Channel->Vpid(), true)
|
||||
&& SetPid(Channel->Apid1(), true)
|
||||
&& SetPid(Channel->Apid2(), true)
|
||||
&& SetPid(Channel->Dpid1(), true);
|
||||
}
|
||||
Dprintf("pid streaming mode\n");
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cStreamdevLiveStreamer::SetFilter(u_short Pid, u_char Tid, u_char Mask,
|
||||
bool On) {
|
||||
#if VDRVERSNUM >= 10300
|
||||
Dprintf("setting filter\n");
|
||||
if (On) {
|
||||
if (m_Filter == NULL) {
|
||||
m_Filter = new cStreamdevLiveFilter(this);
|
||||
Dprintf("attaching filter to device\n");
|
||||
m_Device->AttachFilter(m_Filter);
|
||||
}
|
||||
m_Filter->Set(Pid, Tid, Mask);
|
||||
} else if (m_Filter != NULL)
|
||||
m_Filter->Del(Pid, Tid, Mask);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
uchar *cStreamdevLiveStreamer::Process(const uchar *Data, int &Count,
|
||||
int &Result) {
|
||||
uchar *remuxed = m_Remux != NULL ? m_Remux->Process(Data, Count, Result)
|
||||
: cStreamdevStreamer::Process(Data, Count, Result);
|
||||
if (remuxed) {
|
||||
/*if (Socket()->Type() == SOCK_DGRAM) {
|
||||
free(m_Buffer);
|
||||
Result += 12;
|
||||
m_Buffer = MALLOC(uchar, Result);
|
||||
m_Buffer[0] = 0x01;
|
||||
m_Buffer[1] = 0x02;
|
||||
m_Buffer[2] = 0x03;
|
||||
m_Buffer[3] = 0x04;
|
||||
m_Buffer[4] = (Result & 0xff000000) >> 24;
|
||||
m_Buffer[5] = (Result & 0xff0000) >> 16;
|
||||
m_Buffer[6] = (Result & 0xff00) >> 8;
|
||||
m_Buffer[7] = (Result & 0xff);
|
||||
m_Buffer[8] = (m_Sequence & 0xff000000) >> 24;
|
||||
m_Buffer[9] = (m_Sequence & 0xff0000) >> 16;
|
||||
m_Buffer[10] = (m_Sequence & 0xff00) >> 8;
|
||||
m_Buffer[11] = (m_Sequence & 0xff);
|
||||
memcpy(m_Buffer + 12, Data, Result - 12);
|
||||
if (m_Sequence++ == 0x7fffffff)
|
||||
m_Sequence = 0;
|
||||
return m_Buffer;
|
||||
}*/
|
||||
return remuxed;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cTBString cStreamdevLiveStreamer::Report(void) {
|
||||
cTBString result;
|
||||
|
||||
if (m_Device != NULL)
|
||||
result += "+- Device is " + cTBString::Number(m_Device->CardIndex()) + "\n";
|
||||
if (m_Receiver != NULL)
|
||||
result += "+- Receiver is allocated\n";
|
||||
|
||||
result += "+- Pids are ";
|
||||
for (int i = 0; i < MAXRECEIVEPIDS; ++i)
|
||||
if (m_Pids[i] != 0)
|
||||
result += cTBString::Number(m_Pids[i]) + ", ";
|
||||
result += "\n";
|
||||
return result;
|
||||
}
|
70
server/livestreamer.h
Normal file
70
server/livestreamer.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifndef VDR_STREAMDEV_LIVESTREAMER_H
|
||||
#define VDR_STREAMDEV_LIVESTREAMER_H
|
||||
|
||||
#include <vdr/config.h>
|
||||
#include <vdr/receiver.h>
|
||||
|
||||
#include "server/streamer.h"
|
||||
#include "server/livefilter.h"
|
||||
#include "common.h"
|
||||
|
||||
#if MAXRECEIVEPIDS < 16
|
||||
# error Too few receiver pids allowed! Please contact sascha@akv-soft.de!
|
||||
#endif
|
||||
|
||||
class cTSRemux;
|
||||
|
||||
class cStreamdevLiveReceiver: public cReceiver {
|
||||
friend class cStreamdevLiveStreamer;
|
||||
|
||||
private:
|
||||
cStreamdevLiveStreamer *m_Streamer;
|
||||
|
||||
protected:
|
||||
virtual void Receive(uchar *Data, int Length);
|
||||
|
||||
public:
|
||||
cStreamdevLiveReceiver(cStreamdevLiveStreamer *Streamer, int Priority, int Ca,
|
||||
int Pid1 = 0, int Pid2 = 0, int Pid3 = 0, int Pid4 = 0,
|
||||
int Pid5 = 0, int Pid6 = 0, int Pid7 = 0, int Pid8 = 0,
|
||||
int Pid9 = 0, int Pid10 = 0, int Pid11 = 0, int Pid12 = 0,
|
||||
int Pid13 = 0, int Pid14 = 0, int Pid15 = 0, int Pid16 = 0);
|
||||
virtual ~cStreamdevLiveReceiver();
|
||||
};
|
||||
|
||||
class cStreamdevLiveStreamer: public cStreamdevStreamer {
|
||||
private:
|
||||
int m_Priority;
|
||||
int m_Pids[MAXRECEIVEPIDS];
|
||||
const cChannel *m_Channel;
|
||||
cDevice *m_Device;
|
||||
cStreamdevLiveReceiver *m_Receiver;
|
||||
cTSRemux *m_Remux;
|
||||
uchar *m_Buffer;
|
||||
int m_Sequence;
|
||||
#if VDRVERSNUM >= 10300
|
||||
cStreamdevLiveFilter *m_Filter;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual uchar *Process(const uchar *Data, int &Count, int &Result);
|
||||
|
||||
public:
|
||||
cStreamdevLiveStreamer(int Priority);
|
||||
virtual ~cStreamdevLiveStreamer();
|
||||
|
||||
void SetDevice(cDevice *Device) { m_Device = Device; }
|
||||
bool SetPid(int Pid, bool On);
|
||||
bool SetChannel(const cChannel *Channel, int StreamType, bool StreamPIDS);
|
||||
bool SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On);
|
||||
|
||||
virtual void Detach(void);
|
||||
virtual void Attach(void);
|
||||
|
||||
virtual void Start(cTBSocket *Socket);
|
||||
|
||||
// Statistical purposes:
|
||||
virtual cTBString Report(void);
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_LIVESTREAMER_H
|
148
server/server.c
Normal file
148
server/server.c
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* $Id: server.c,v 1.1 2004/12/30 22:44:20 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include "server/server.h"
|
||||
#include "server/componentVTP.h"
|
||||
#include "server/componentHTTP.h"
|
||||
#include "server/setup.h"
|
||||
|
||||
#include <vdr/tools.h>
|
||||
#include <tools/select.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
cSVDRPhosts StreamdevHosts;
|
||||
|
||||
cStreamdevServer *cStreamdevServer::m_Instance = NULL;
|
||||
|
||||
cStreamdevServer::cStreamdevServer(void)
|
||||
#if VDRVERSNUM >= 10300
|
||||
: cThread("Streamdev: server")
|
||||
#endif
|
||||
{
|
||||
m_Active = false;
|
||||
|
||||
StreamdevHosts.Load(AddDirectory(cPlugin::ConfigDirectory(),
|
||||
"streamdevhosts.conf"), true);
|
||||
}
|
||||
|
||||
cStreamdevServer::~cStreamdevServer() {
|
||||
if (m_Active) Stop();
|
||||
}
|
||||
|
||||
void cStreamdevServer::Init(void) {
|
||||
if (m_Instance == NULL) {
|
||||
m_Instance = new cStreamdevServer;
|
||||
if (StreamdevServerSetup.StartVTPServer)
|
||||
m_Instance->Register(new cComponentVTP);
|
||||
if (StreamdevServerSetup.StartHTTPServer)
|
||||
m_Instance->Register(new cComponentHTTP);
|
||||
m_Instance->Start();
|
||||
}
|
||||
}
|
||||
|
||||
void cStreamdevServer::Exit(void) {
|
||||
if (m_Instance != NULL) {
|
||||
m_Instance->Stop();
|
||||
DELETENULL(m_Instance);
|
||||
}
|
||||
}
|
||||
|
||||
void cStreamdevServer::Stop(void) {
|
||||
m_Active = false;
|
||||
Cancel(3);
|
||||
}
|
||||
|
||||
void cStreamdevServer::Register(cServerComponent *Server) {
|
||||
m_Servers.Add(Server);
|
||||
}
|
||||
|
||||
void cStreamdevServer::Action(void) {
|
||||
cTBSelect select;
|
||||
|
||||
#if VDRVERSNUM < 10300
|
||||
isyslog("Streamdev: Server thread started (pid=%d)", getpid());
|
||||
#endif
|
||||
|
||||
m_Active = true;
|
||||
|
||||
/* Initialize Server components, deleting those that failed */
|
||||
for (cServerComponent *c = m_Servers.First(); c;) {
|
||||
cServerComponent *next = m_Servers.Next(c);
|
||||
if (!c->Init())
|
||||
m_Servers.Del(c);
|
||||
c = next;
|
||||
}
|
||||
|
||||
if (!m_Servers.Count()) {
|
||||
esyslog("Streamdev: No server components registered, exiting");
|
||||
m_Active = false;
|
||||
}
|
||||
|
||||
while (m_Active) {
|
||||
select.Clear();
|
||||
|
||||
/* Ask all Server components to register to the selector */
|
||||
for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c))
|
||||
c->AddSelect(select);
|
||||
|
||||
/* Ask all Client connections to register to the selector */
|
||||
for (cServerConnection *s = m_Clients.First(); s; s = m_Clients.Next(s))
|
||||
s->AddSelect(select);
|
||||
|
||||
if (select.Select(1000) < 0) {
|
||||
if (!m_Active) // Exit was requested while polling
|
||||
continue;
|
||||
esyslog("Streamdev: Fatal error, server exiting: %s", strerror(errno));
|
||||
m_Active = false;
|
||||
}
|
||||
|
||||
/* Ask all Server components to act on signalled sockets */
|
||||
for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c)) {
|
||||
cServerConnection *client;
|
||||
if ((client = c->CanAct(select)) != NULL) {
|
||||
m_Clients.Add(client);
|
||||
|
||||
if (m_Clients.Count() > StreamdevServerSetup.MaxClients) {
|
||||
esyslog("Streamdev: Too many clients, rejecting %s:%d",
|
||||
(const char*)client->RemoteIp(), client->RemotePort());
|
||||
client->Reject();
|
||||
} else if (!StreamdevHosts.Acceptable(client->RemoteIpAddr())) {
|
||||
esyslog("Streamdev: Client from %s:%d not allowed to connect",
|
||||
(const char*)client->RemoteIp(), client->RemotePort());
|
||||
client->Reject();
|
||||
} else
|
||||
client->Welcome();
|
||||
}
|
||||
}
|
||||
|
||||
/* Ask all Client connections to act on signalled sockets */
|
||||
for (cServerConnection *s = m_Clients.First(); s;) {
|
||||
cServerConnection *next = m_Clients.Next(s);
|
||||
if (!s->CanAct(select)) {
|
||||
isyslog("Streamdev: Closing connection to %s:%d",
|
||||
(const char*)s->RemoteIp(), s->RemotePort());
|
||||
s->Close();
|
||||
m_Clients.Del(s);
|
||||
}
|
||||
s = next;
|
||||
}
|
||||
}
|
||||
|
||||
while (m_Clients.Count()) {
|
||||
cServerConnection *client = m_Clients.First();
|
||||
client->Close();
|
||||
m_Clients.Del(client);
|
||||
}
|
||||
|
||||
while (m_Servers.Count()) {
|
||||
cServerComponent *server = m_Servers.First();
|
||||
server->Exit();
|
||||
m_Servers.Del(server);
|
||||
}
|
||||
|
||||
#if VDRVERSNUM < 10300
|
||||
isyslog("Streamdev: Server thread stopped");
|
||||
#endif
|
||||
}
|
44
server/server.h
Normal file
44
server/server.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* $Id: server.h,v 1.1 2004/12/30 22:44:21 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_SERVER_H
|
||||
#define VDR_STREAMDEV_SERVER_H
|
||||
|
||||
#include <vdr/thread.h>
|
||||
|
||||
#include "server/component.h"
|
||||
#include "server/connection.h"
|
||||
|
||||
class cStreamdevServer: public cThread {
|
||||
private:
|
||||
bool m_Active;
|
||||
|
||||
cServerComponents m_Servers;
|
||||
cServerConnections m_Clients;
|
||||
|
||||
static cStreamdevServer *m_Instance;
|
||||
|
||||
protected:
|
||||
virtual void Action(void);
|
||||
|
||||
void Stop(void);
|
||||
|
||||
public:
|
||||
cStreamdevServer(void);
|
||||
virtual ~cStreamdevServer();
|
||||
|
||||
void Register(cServerComponent *Server);
|
||||
|
||||
static void Init(void);
|
||||
static void Exit(void);
|
||||
static bool Active(void);
|
||||
};
|
||||
|
||||
inline bool cStreamdevServer::Active(void) {
|
||||
return m_Instance && m_Instance->m_Clients.Count() > 0;
|
||||
}
|
||||
|
||||
extern cSVDRPhosts StreamdevHosts;
|
||||
|
||||
#endif // VDR_STREAMDEV_SERVER_H
|
94
server/setup.c
Normal file
94
server/setup.c
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* $Id: setup.c,v 1.1 2004/12/30 22:44:21 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include <vdr/menuitems.h>
|
||||
|
||||
#include "server/setup.h"
|
||||
#include "server/server.h"
|
||||
#include "i18n.h"
|
||||
|
||||
cStreamdevServerSetup StreamdevServerSetup;
|
||||
|
||||
cStreamdevServerSetup::cStreamdevServerSetup(void) {
|
||||
MaxClients = 5;
|
||||
StartVTPServer = true;
|
||||
VTPServerPort = 2004;
|
||||
StartHTTPServer = true;
|
||||
HTTPServerPort = 3000;
|
||||
HTTPStreamType = stPES;
|
||||
SuspendMode = smOffer;
|
||||
AllowSuspend = false;
|
||||
strcpy(VTPBindIP, "0.0.0.0");
|
||||
strcpy(HTTPBindIP, "0.0.0.0");
|
||||
}
|
||||
|
||||
bool cStreamdevServerSetup::SetupParse(const char *Name, const char *Value) {
|
||||
if (strcmp(Name, "MaxClients") == 0) MaxClients = atoi(Value);
|
||||
else if (strcmp(Name, "StartServer") == 0) StartVTPServer = atoi(Value);
|
||||
else if (strcmp(Name, "ServerPort") == 0) VTPServerPort = atoi(Value);
|
||||
else if (strcmp(Name, "VTPBindIP") == 0) strcpy(VTPBindIP, Value);
|
||||
else if (strcmp(Name, "StartHTTPServer") == 0) StartHTTPServer = atoi(Value);
|
||||
else if (strcmp(Name, "HTTPServerPort") == 0) HTTPServerPort = atoi(Value);
|
||||
else if (strcmp(Name, "HTTPStreamType") == 0) HTTPStreamType = atoi(Value);
|
||||
else if (strcmp(Name, "HTTPBindIP") == 0) strcpy(HTTPBindIP, Value);
|
||||
else if (strcmp(Name, "SuspendMode") == 0) SuspendMode = atoi(Value);
|
||||
else if (strcmp(Name, "AllowSuspend") == 0) AllowSuspend = atoi(Value);
|
||||
else return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
cStreamdevServerMenuSetupPage::cStreamdevServerMenuSetupPage(void) {
|
||||
m_NewSetup = StreamdevServerSetup;
|
||||
|
||||
AddCategory (tr("Common Settings"));
|
||||
AddRangeEdit(tr("Maximum Number of Clients"), m_NewSetup.MaxClients, 0, 100);
|
||||
AddSuspEdit (tr("Suspend behaviour"), m_NewSetup.SuspendMode);
|
||||
AddBoolEdit (tr("Client may suspend"), m_NewSetup.AllowSuspend);
|
||||
|
||||
AddCategory (tr("VDR-to-VDR Server"));
|
||||
AddBoolEdit (tr("Start VDR-to-VDR Server"), m_NewSetup.StartVTPServer);
|
||||
AddShortEdit(tr("VDR-to-VDR Server Port"), m_NewSetup.VTPServerPort);
|
||||
AddIpEdit (tr("Bind to IP"), m_NewSetup.VTPBindIP);
|
||||
|
||||
AddCategory (tr("HTTP Server"));
|
||||
AddBoolEdit (tr("Start HTTP Server"), m_NewSetup.StartHTTPServer);
|
||||
AddShortEdit(tr("HTTP Server Port"), m_NewSetup.HTTPServerPort);
|
||||
AddTypeEdit (tr("HTTP Streamtype"), m_NewSetup.HTTPStreamType);
|
||||
AddIpEdit (tr("Bind to IP"), m_NewSetup.HTTPBindIP);
|
||||
|
||||
SetCurrent(Get(1));
|
||||
}
|
||||
|
||||
cStreamdevServerMenuSetupPage::~cStreamdevServerMenuSetupPage() {
|
||||
}
|
||||
|
||||
void cStreamdevServerMenuSetupPage::Store(void) {
|
||||
bool restart = false;
|
||||
if (m_NewSetup.StartVTPServer != StreamdevServerSetup.StartVTPServer
|
||||
|| m_NewSetup.VTPServerPort != StreamdevServerSetup.VTPServerPort
|
||||
|| strcmp(m_NewSetup.VTPBindIP, StreamdevServerSetup.VTPBindIP) != 0
|
||||
|| m_NewSetup.StartHTTPServer != StreamdevServerSetup.StartHTTPServer
|
||||
|| m_NewSetup.HTTPServerPort != StreamdevServerSetup.HTTPServerPort
|
||||
|| strcmp(m_NewSetup.HTTPBindIP, StreamdevServerSetup.HTTPBindIP) != 0) {
|
||||
restart = true;
|
||||
cStreamdevServer::Exit();
|
||||
}
|
||||
|
||||
SetupStore("MaxClients", m_NewSetup.MaxClients);
|
||||
SetupStore("StartServer", m_NewSetup.StartVTPServer);
|
||||
SetupStore("ServerPort", m_NewSetup.VTPServerPort);
|
||||
SetupStore("VTPBindIP", m_NewSetup.VTPBindIP);
|
||||
SetupStore("StartHTTPServer", m_NewSetup.StartHTTPServer);
|
||||
SetupStore("HTTPServerPort", m_NewSetup.HTTPServerPort);
|
||||
SetupStore("HTTPStreamType", m_NewSetup.HTTPStreamType);
|
||||
SetupStore("HTTPBindIP", m_NewSetup.HTTPBindIP);
|
||||
SetupStore("SuspendMode", m_NewSetup.SuspendMode);
|
||||
SetupStore("AllowSuspend", m_NewSetup.AllowSuspend);
|
||||
|
||||
StreamdevServerSetup = m_NewSetup;
|
||||
|
||||
if (restart)
|
||||
cStreamdevServer::Init();
|
||||
}
|
||||
|
41
server/setup.h
Normal file
41
server/setup.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* $Id: setup.h,v 1.1 2004/12/30 22:44:21 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_SETUPSERVER_H
|
||||
#define VDR_STREAMDEV_SETUPSERVER_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
struct cStreamdevServerSetup {
|
||||
cStreamdevServerSetup(void);
|
||||
|
||||
bool SetupParse(const char *Name, const char *Value);
|
||||
|
||||
int MaxClients;
|
||||
int StartVTPServer;
|
||||
int VTPServerPort;
|
||||
char VTPBindIP[20];
|
||||
int StartHTTPServer;
|
||||
int HTTPServerPort;
|
||||
int HTTPStreamType;
|
||||
char HTTPBindIP[20];
|
||||
int SuspendMode;
|
||||
int AllowSuspend;
|
||||
};
|
||||
|
||||
extern cStreamdevServerSetup StreamdevServerSetup;
|
||||
|
||||
class cStreamdevServerMenuSetupPage: public cStreamdevMenuSetupPage {
|
||||
private:
|
||||
cStreamdevServerSetup m_NewSetup;
|
||||
|
||||
protected:
|
||||
virtual void Store(void);
|
||||
|
||||
public:
|
||||
cStreamdevServerMenuSetupPage(void);
|
||||
virtual ~cStreamdevServerMenuSetupPage();
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_SETUPSERVER_H
|
99
server/streamer.c
Normal file
99
server/streamer.c
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* $Id: streamer.c,v 1.1 2004/12/30 22:44:21 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include <vdr/ringbuffer.h>
|
||||
#include <vdr/device.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "server/streamer.h"
|
||||
#include "server/suspend.h"
|
||||
#include "server/setup.h"
|
||||
#include "tools/socket.h"
|
||||
#include "common.h"
|
||||
|
||||
#define VIDEOBUFSIZE MEGABYTE(3)
|
||||
#define MAXBLOCKSIZE TS_SIZE*10
|
||||
|
||||
cStreamdevStreamer::cStreamdevStreamer(const char *Name)
|
||||
#if VDRVERSNUM >= 10300
|
||||
:cThread("Streamdev: " + (cTBString)Name)
|
||||
#endif
|
||||
{
|
||||
m_Active = false;
|
||||
m_Receivers = 0;
|
||||
m_Buffer = NULL;
|
||||
m_Name = Name;
|
||||
m_Socket = NULL;
|
||||
m_RingBuffer = new cRingBufferLinear(VIDEOBUFSIZE, TS_SIZE * 2, true);
|
||||
}
|
||||
|
||||
cStreamdevStreamer::~cStreamdevStreamer() {
|
||||
Stop();
|
||||
if (m_Buffer != NULL) delete[] m_Buffer;
|
||||
delete m_RingBuffer;
|
||||
}
|
||||
|
||||
void cStreamdevStreamer::Start(cTBSocket *Socket) {
|
||||
m_Socket = Socket;
|
||||
Attach();
|
||||
if (!m_Active)
|
||||
cThread::Start();
|
||||
}
|
||||
|
||||
void cStreamdevStreamer::Stop(void) {
|
||||
if (m_Active) {
|
||||
Dprintf("stopping live streamer\n");
|
||||
m_Active = false;
|
||||
Cancel(3);
|
||||
}
|
||||
}
|
||||
|
||||
uchar *cStreamdevStreamer::Process(const uchar *Data, int &Count, int &Result) {
|
||||
if (m_Buffer == NULL)
|
||||
m_Buffer = new uchar[MAXBLOCKSIZE];
|
||||
|
||||
if (Count > MAXBLOCKSIZE)
|
||||
Count = MAXBLOCKSIZE;
|
||||
memcpy(m_Buffer, Data, Count);
|
||||
Result = Count;
|
||||
return m_Buffer;
|
||||
}
|
||||
|
||||
void cStreamdevStreamer::Action(void) {
|
||||
int max = 0;
|
||||
|
||||
#if VDRVERSNUM < 10300
|
||||
isyslog("Streamdev: %s thread started (pid=%d)", m_Name, getpid());
|
||||
#endif
|
||||
|
||||
m_Active = true;
|
||||
while (m_Active) {
|
||||
int recvd;
|
||||
const uchar *block = m_RingBuffer->Get(recvd);
|
||||
|
||||
if (block && recvd > 0) {
|
||||
int result = 0;
|
||||
uchar *sendBlock = Process(block, recvd, result);
|
||||
|
||||
m_RingBuffer->Del(recvd);
|
||||
if (result > max) max = result;
|
||||
|
||||
if (!m_Socket->TimedWrite(sendBlock, result, 150)) {
|
||||
if (errno != ETIMEDOUT) {
|
||||
esyslog("ERROR: Streamdev: Couldn't write data: %s", strerror(errno));
|
||||
m_Active = false;
|
||||
}
|
||||
}
|
||||
} else
|
||||
usleep(1); // this keeps the CPU load low (XXX: waiting buffers)
|
||||
}
|
||||
|
||||
Dprintf("Max. Transmit Blocksize was: %d\n", max);
|
||||
|
||||
#if VDRVERSNUM < 10300
|
||||
isyslog("Streamdev: %s thread stopped", m_Name);
|
||||
#endif
|
||||
}
|
||||
|
43
server/streamer.h
Normal file
43
server/streamer.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* $Id: streamer.h,v 1.1 2004/12/30 22:44:21 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_STREAMER_H
|
||||
#define VDR_STREAMDEV_STREAMER_H
|
||||
|
||||
#include <vdr/thread.h>
|
||||
#include <vdr/ringbuffer.h>
|
||||
#include <vdr/tools.h>
|
||||
|
||||
class cTBSocket;
|
||||
|
||||
class cStreamdevStreamer: public cThread {
|
||||
private:
|
||||
bool m_Active;
|
||||
int m_Receivers;
|
||||
uchar *m_Buffer;
|
||||
const char *m_Name;
|
||||
cTBSocket *m_Socket;
|
||||
cRingBufferLinear *m_RingBuffer;
|
||||
|
||||
protected:
|
||||
virtual uchar *Process(const uchar *Data, int &Count, int &Result);
|
||||
virtual void Action(void);
|
||||
|
||||
const cTBSocket *Socket(void) const { return m_Socket; }
|
||||
|
||||
public:
|
||||
cStreamdevStreamer(const char *Name);
|
||||
virtual ~cStreamdevStreamer();
|
||||
|
||||
virtual void Start(cTBSocket *Socket);
|
||||
virtual void Stop(void);
|
||||
|
||||
int Put(uchar *Data, int Length) { return m_RingBuffer->Put(Data, Length); }
|
||||
|
||||
virtual void Detach(void) = 0;
|
||||
virtual void Attach(void) = 0;
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_STREAMER_H
|
||||
|
69
server/suspend.c
Normal file
69
server/suspend.c
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* $Id: suspend.c,v 1.1 2004/12/30 22:44:21 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#include "server/suspend.h"
|
||||
#include "server/suspend.dat"
|
||||
#include "common.h"
|
||||
|
||||
cSuspendLive::cSuspendLive(void)
|
||||
#if VDRVERSNUM >= 10300
|
||||
: cThread("Streamdev: server suspend")
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
cSuspendLive::~cSuspendLive() {
|
||||
Detach();
|
||||
}
|
||||
|
||||
void cSuspendLive::Activate(bool On) {
|
||||
Dprintf("Activate cSuspendLive %d\n", On);
|
||||
if (On)
|
||||
Start();
|
||||
else
|
||||
Stop();
|
||||
}
|
||||
|
||||
void cSuspendLive::Stop(void) {
|
||||
if (m_Active) {
|
||||
m_Active = false;
|
||||
Cancel(3);
|
||||
}
|
||||
}
|
||||
|
||||
void cSuspendLive::Action(void) {
|
||||
#if VDRVERSNUM < 10300
|
||||
isyslog("Streamdev: Suspend Live thread started (pid = %d)", getpid());
|
||||
#endif
|
||||
|
||||
m_Active = true;
|
||||
while (m_Active) {
|
||||
DeviceStillPicture(suspend_mpg, sizeof(suspend_mpg));
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
#if VDRVERSNUM < 10300
|
||||
isyslog("Streamdev: Suspend Live thread stopped");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool cSuspendCtl::m_Active = false;
|
||||
|
||||
cSuspendCtl::cSuspendCtl(void):
|
||||
cControl(m_Suspend = new cSuspendLive) {
|
||||
m_Active = true;
|
||||
}
|
||||
|
||||
cSuspendCtl::~cSuspendCtl() {
|
||||
m_Active = false;
|
||||
DELETENULL(m_Suspend);
|
||||
}
|
||||
|
||||
eOSState cSuspendCtl::ProcessKey(eKeys Key) {
|
||||
if (!m_Suspend->IsActive() || Key == kBack) {
|
||||
DELETENULL(m_Suspend);
|
||||
return osEnd;
|
||||
}
|
||||
return osContinue;
|
||||
}
|
1206
server/suspend.dat
Normal file
1206
server/suspend.dat
Normal file
File diff suppressed because it is too large
Load Diff
41
server/suspend.h
Normal file
41
server/suspend.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* $Id: suspend.h,v 1.1 2004/12/30 22:44:26 lordjaxom Exp $
|
||||
*/
|
||||
|
||||
#ifndef VDR_STREAMDEV_SUSPEND_H
|
||||
#define VDR_STREAMDEV_SUSPEND_H
|
||||
|
||||
#include <vdr/player.h>
|
||||
|
||||
class cSuspendLive: public cPlayer, cThread {
|
||||
private:
|
||||
bool m_Active;
|
||||
|
||||
protected:
|
||||
virtual void Activate(bool On);
|
||||
virtual void Action(void);
|
||||
|
||||
void Stop(void);
|
||||
|
||||
public:
|
||||
cSuspendLive(void);
|
||||
virtual ~cSuspendLive();
|
||||
|
||||
bool IsActive(void) const { return m_Active; }
|
||||
};
|
||||
|
||||
class cSuspendCtl: public cControl {
|
||||
private:
|
||||
cSuspendLive *m_Suspend;
|
||||
static bool m_Active;
|
||||
|
||||
public:
|
||||
cSuspendCtl(void);
|
||||
virtual ~cSuspendCtl();
|
||||
virtual void Hide(void) {}
|
||||
virtual eOSState ProcessKey(eKeys Key);
|
||||
|
||||
static bool IsActive(void) { return m_Active; }
|
||||
};
|
||||
|
||||
#endif // VDR_STREAMDEV_SUSPEND_H
|
Reference in New Issue
Block a user