Revised class responsibilities: Moved live TV related functions to livestreamer

This commit is contained in:
Frank Schmirler 2014-06-07 00:24:27 +02:00
parent 7be0c81a81
commit e555017565
9 changed files with 168 additions and 137 deletions

View File

@ -9,17 +9,10 @@
#include <vdr/tools.h> #include <vdr/tools.h>
#include <vdr/thread.h> #include <vdr/thread.h>
#include <vdr/transfer.h>
#include <string.h> #include <string.h>
#include <stdarg.h> #include <stdarg.h>
#include <errno.h> #include <errno.h>
// device occupied timeout to prevent VDR main loop to immediately switch back
// when streamdev switched the live TV channel.
// Note that there is still a gap between the GetDevice() and SetOccupied()
// calls where the VDR main loop could strike
#define STREAMDEVTUNETIMEOUT 5
cServerConnection::cServerConnection(const char *Protocol, int Type): cServerConnection::cServerConnection(const char *Protocol, int Type):
cTBSocket(Type), cTBSocket(Type),
m_Protocol(Protocol), m_Protocol(Protocol),
@ -28,9 +21,7 @@ cServerConnection::cServerConnection(const char *Protocol, int Type):
m_ReadBytes(0), m_ReadBytes(0),
m_WriteBytes(0), m_WriteBytes(0),
m_WriteIndex(0), m_WriteIndex(0),
m_Streamer(NULL), m_Streamer(NULL)
m_OccupiedDev(NULL),
m_SwitchTo(NULL)
{ {
} }
@ -206,66 +197,5 @@ bool cServerConnection::Close()
return cTBSocket::Close(); return cTBSocket::Close();
} }
bool cServerConnection::UsedByLiveTV(cDevice *device)
{
return device == cTransferControl::ReceiverDevice() ||
(device->IsPrimaryDevice() && device->HasDecoder() && !device->Replaying());
}
cDevice *cServerConnection::SwitchDevice(const cChannel *Channel, int Priority)
{
// turn off the streams of this connection
Detach();
cDevice *device = cDevice::GetDevice(Channel, Priority, false);
if (!device) {
// can't switch - continue the current stream
Attach();
dsyslog("streamdev: GetDevice failed for channel %d (%s) at priority %d (PrimaryDevice=%d, ActualDevice=%d)", Channel->Number(), Channel->Name(), Priority, cDevice::PrimaryDevice()->CardIndex(), cDevice::ActualDevice()->CardIndex());
}
else if (!device->IsTunedToTransponder(Channel) && UsedByLiveTV(device)) {
// make sure VDR main loop doesn't switch back
device->SetOccupied(STREAMDEVTUNETIMEOUT);
if (device->SwitchChannel(Channel, false)) {
// switched away live TV
m_OccupiedDev = device;
m_SwitchTo = Channel;
}
else {
dsyslog("streamdev: SwitchChannel (live) failed for channel %d (%s) at priority %d (PrimaryDevice=%d, ActualDevice=%d, device=%d)", Channel->Number(), Channel->Name(), Priority, cDevice::PrimaryDevice()->CardIndex(), cDevice::ActualDevice()->CardIndex(), device->CardIndex());
device->SetOccupied(0);
device = NULL;
}
}
else if (!device->SwitchChannel(Channel, false)) {
dsyslog("streamdev: SwitchChannel failed for channel %d (%s) at priority %d (PrimaryDevice=%d, ActualDevice=%d, device=%d)", Channel->Number(), Channel->Name(), Priority, cDevice::PrimaryDevice()->CardIndex(), cDevice::ActualDevice()->CardIndex(), device->CardIndex());
device = NULL;
}
return device;
}
bool cServerConnection::ProvidesChannel(const cChannel *Channel, int Priority)
{
cDevice *device = cDevice::GetDevice(Channel, Priority, false, true);
if (!device)
dsyslog("streamdev: No device provides channel %d (%s) at priority %d", Channel->Number(), Channel->Name(), Priority);
return device;
}
void cServerConnection::MainThreadHook()
{
if (m_SwitchTo)
{
// switched away live TV. Try previous channel on other device first
if (!Channels.SwitchTo(cDevice::CurrentChannel())) {
// switch to streamdev channel otherwise
Channels.SwitchTo(m_SwitchTo->Number());
Skins.Message(mtInfo, tr("Streaming active"));
}
m_OccupiedDev->SetOccupied(0);
m_SwitchTo = NULL;
}
}
cString cServerConnection::ToText() const cString cServerConnection::ToText() const
{ return cString::sprintf("%s\t%s:%d", Protocol(), RemoteIp().c_str(), RemotePort()); } { return cString::sprintf("%s\t%s:%d", Protocol(), RemoteIp().c_str(), RemotePort()); }

View File

@ -16,7 +16,6 @@ typedef std::map<std::string,std::string> tStrStrMap;
typedef std::pair<std::string,std::string> tStrStr; typedef std::pair<std::string,std::string> tStrStr;
class cChannel; class cChannel;
class cDevice;
/* Basic capabilities of a straight text-based protocol, most functions /* Basic capabilities of a straight text-based protocol, most functions
virtual to support more complicated protocols */ virtual to support more complicated protocols */
@ -37,17 +36,8 @@ private:
cStreamdevStreamer *m_Streamer; cStreamdevStreamer *m_Streamer;
/* Set to occupied device when live TV was interrupted */
cDevice *m_OccupiedDev;
/* Set to this connection's current channel when live TV was interrupted */
const cChannel *m_SwitchTo;
tStrStrMap m_Headers; tStrStrMap m_Headers;
/* Test if device is in use as the transfer mode receiver device
or a FF card, displaying live TV from internal tuner */
static bool UsedByLiveTV(cDevice *device);
protected: protected:
/* Will be called when a command terminated by a newline has been /* Will be called when a command terminated by a newline has been
received */ received */
@ -113,24 +103,8 @@ public:
/* Close the socket */ /* Close the socket */
virtual bool Close(void); virtual bool Close(void);
/* Check if a device would be available for transferring the given
channel. This call has no side effects. */
static cDevice *CheckDevice(const cChannel *Channel, int Priority, bool LiveView, const cDevice *AvoidDevice = NULL);
/* Find a suitable device and tune it to the requested channel. */
cDevice *SwitchDevice(const cChannel *Channel, int Priority);
/* Test if a call to GetDevice would return a usable device. */
bool ProvidesChannel(const cChannel *Channel, int Priority);
/* Do things which must be done in VDR's main loop */
void MainThreadHook();
virtual void Flushed(void) {} virtual void Flushed(void) {}
virtual void Attach(void) { if (m_Streamer != NULL) m_Streamer->Attach(); }
virtual void Detach(void) { if (m_Streamer != NULL) m_Streamer->Detach(); }
/* This connections protocol name */ /* This connections protocol name */
virtual const char* Protocol(void) const { return m_Protocol; } virtual const char* Protocol(void) const { return m_Protocol; }

View File

@ -173,14 +173,10 @@ bool cConnectionHTTP::ProcessRequest(void)
if (m_MenuList) if (m_MenuList)
return Respond("%s", true, m_MenuList->HttpHeader().c_str()); return Respond("%s", true, m_MenuList->HttpHeader().c_str());
else if (m_Channel != NULL) { else if (m_Channel != NULL) {
cDevice *device = NULL; if (cStreamdevLiveStreamer::ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority)) {
if (ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority)) cStreamdevLiveStreamer* liveStreamer = new cStreamdevLiveStreamer(this, m_Channel, StreamdevServerSetup.HTTPPriority, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
device = SwitchDevice(m_Channel, StreamdevServerSetup.HTTPPriority); if (liveStreamer->GetDevice()) {
if (device != NULL) { SetStreamer(liveStreamer);
cStreamdevLiveStreamer* liveStreamer = new cStreamdevLiveStreamer(StreamdevServerSetup.HTTPPriority, this);
SetStreamer(liveStreamer);
if (liveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL)) {
liveStreamer->SetDevice(device);
if (!SetDSCP()) if (!SetDSCP())
LOG_ERROR_STR("unable to set DSCP sockopt"); LOG_ERROR_STR("unable to set DSCP sockopt");
if (m_StreamType == stEXT) { if (m_StreamType == stEXT) {
@ -194,6 +190,7 @@ bool cConnectionHTTP::ProcessRequest(void)
} }
} }
SetStreamer(NULL); SetStreamer(NULL);
delete liveStreamer;
} }
return HttpResponse(503, true); return HttpResponse(503, true);
} }
@ -233,10 +230,9 @@ bool cConnectionHTTP::ProcessRequest(void)
return Respond("%s", true, m_MenuList->HttpHeader().c_str()); return Respond("%s", true, m_MenuList->HttpHeader().c_str());
} }
else if (m_Channel != NULL) { else if (m_Channel != NULL) {
if (ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority)) { if (cStreamdevLiveStreamer::ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority)) {
if (m_StreamType == stEXT) { if (m_StreamType == stEXT) {
cStreamdevLiveStreamer *liveStreamer = new cStreamdevLiveStreamer(StreamdevServerSetup.HTTPPriority, this); cStreamdevLiveStreamer *liveStreamer = new cStreamdevLiveStreamer(this, m_Channel, IDLEPRIORITY, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
liveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL);
SetStreamer(liveStreamer); SetStreamer(liveStreamer);
return Respond("HTTP/1.0 200 OK"); return Respond("HTTP/1.0 200 OK");
} else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) { } else if (m_StreamType == stES && (m_Apid[0] || m_Dpid[0] || ISRADIO(m_Channel))) {

View File

@ -40,22 +40,19 @@ bool cConnectionIGMP::SetChannel(cChannel *Channel, in_addr_t Dst)
void cConnectionIGMP::Welcome() void cConnectionIGMP::Welcome()
{ {
cDevice *device = NULL; if (cStreamdevLiveStreamer::ProvidesChannel(m_Channel, StreamdevServerSetup.IGMPPriority)) {
if (ProvidesChannel(m_Channel, StreamdevServerSetup.IGMPPriority)) cStreamdevLiveStreamer * liveStreamer = new cStreamdevLiveStreamer(this, m_Channel, StreamdevServerSetup.IGMPPriority, m_StreamType);
device = SwitchDevice(m_Channel, StreamdevServerSetup.IGMPPriority); if (liveStreamer->GetDevice()) {
if (device != NULL) { SetStreamer(liveStreamer);
cStreamdevLiveStreamer * liveStreamer = new cStreamdevLiveStreamer(StreamdevServerSetup.IGMPPriority, this);
SetStreamer(liveStreamer);
if (liveStreamer->SetChannel(m_Channel, m_StreamType)) {
liveStreamer->SetDevice(device);
if (!SetDSCP()) if (!SetDSCP())
LOG_ERROR_STR("unable to set DSCP sockopt"); LOG_ERROR_STR("unable to set DSCP sockopt");
Dprintf("streamer start\n"); Dprintf("streamer start\n");
liveStreamer->Start(this); liveStreamer->Start(this);
} }
else { else {
esyslog("streamdev-server IGMP: SetChannel failed");
SetStreamer(NULL); SetStreamer(NULL);
delete liveStreamer;
esyslog("streamdev-server IGMP: SetChannel failed");
} }
} }
else else

View File

@ -787,12 +787,12 @@ void cConnectionVTP::Reject(void)
void cConnectionVTP::Detach(void) void cConnectionVTP::Detach(void)
{ {
if (m_FilterStreamer) m_FilterStreamer->Detach(); if (m_FilterStreamer) m_FilterStreamer->Detach();
cServerConnection::Detach(); if (m_LiveSocket && Streamer()) ((cStreamdevLiveStreamer *) Streamer())->Detach();
} }
void cConnectionVTP::Attach(void) void cConnectionVTP::Attach(void)
{ {
cServerConnection::Attach(); if (m_LiveSocket && Streamer()) ((cStreamdevLiveStreamer *) Streamer())->Attach();
if (m_FilterStreamer) m_FilterStreamer->Attach(); if (m_FilterStreamer) m_FilterStreamer->Attach();
} }
@ -936,7 +936,7 @@ bool cConnectionVTP::CmdPROV(char *Opts)
LOOP_PREVENTION(chan); LOOP_PREVENTION(chan);
if (ProvidesChannel(chan, prio)) { if (cStreamdevLiveStreamer::ProvidesChannel(chan, prio)) {
m_TuneChannel = chan; m_TuneChannel = chan;
m_TunePriority = prio; m_TunePriority = prio;
return Respond(220, "Channel available"); return Respond(220, "Channel available");
@ -945,7 +945,7 @@ bool cConnectionVTP::CmdPROV(char *Opts)
// so get our own receiver temporarily out of the way // so get our own receiver temporarily out of the way
if (m_ClientVersion == 0) { if (m_ClientVersion == 0) {
Detach(); Detach();
bool provided = ProvidesChannel(chan, prio); bool provided = cStreamdevLiveStreamer::ProvidesChannel(chan, prio);
Attach(); Attach();
if (provided) { if (provided) {
m_TuneChannel = chan; m_TuneChannel = chan;
@ -1117,16 +1117,18 @@ bool cConnectionVTP::CmdTUNE(char *Opts)
if (chan != m_TuneChannel) { if (chan != m_TuneChannel) {
isyslog("streamdev-server TUNE %s: Priority unknown - using 0", Opts); isyslog("streamdev-server TUNE %s: Priority unknown - using 0", Opts);
prio = 0; prio = 0;
if (!ProvidesChannel(chan, prio)) if (!cStreamdevLiveStreamer::ProvidesChannel(chan, prio))
return Respond(560, "Channel not available (ProvidesChannel)"); return Respond(560, "Channel not available (ProvidesChannel)");
} }
if ((dev = SwitchDevice(chan, prio)) == NULL)
return Respond(560, "Channel not available (SwitchDevice)");
cStreamdevLiveStreamer* liveStreamer = new cStreamdevLiveStreamer(prio, this); cStreamdevLiveStreamer* liveStreamer = new cStreamdevLiveStreamer(this, chan, prio, m_StreamType);
if ((dev = liveStreamer->GetDevice()) == NULL) {
SetStreamer(NULL);
delete liveStreamer;
return Respond(560, "Channel not available (SwitchDevice)");
}
SetStreamer(liveStreamer); SetStreamer(liveStreamer);
liveStreamer->SetChannel(chan, m_StreamType);
liveStreamer->SetDevice(dev);
if(m_LiveSocket) if(m_LiveSocket)
liveStreamer->Start(m_LiveSocket); liveStreamer->Start(m_LiveSocket);

View File

@ -8,6 +8,7 @@
#include "remux/ts2es.h" #include "remux/ts2es.h"
#include "remux/extern.h" #include "remux/extern.h"
#include <vdr/transfer.h>
#include <vdr/ringbuffer.h> #include <vdr/ringbuffer.h>
#include "server/livestreamer.h" #include "server/livestreamer.h"
@ -16,6 +17,12 @@
using namespace Streamdev; using namespace Streamdev;
// device occupied timeout to prevent VDR main loop to immediately switch back
// when streamdev switched the live TV channel.
// Note that there is still a gap between the GetDevice() and SetOccupied()
// calls where the VDR main loop could strike
#define STREAMDEVTUNETIMEOUT 5
// --- cStreamdevLiveReceiver ------------------------------------------------- // --- cStreamdevLiveReceiver -------------------------------------------------
class cStreamdevLiveReceiver: public cReceiver { class cStreamdevLiveReceiver: public cReceiver {
@ -328,19 +335,28 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i
// --- cStreamdevLiveStreamer ------------------------------------------------- // --- cStreamdevLiveStreamer -------------------------------------------------
cStreamdevLiveStreamer::cStreamdevLiveStreamer(int Priority, const cServerConnection *Connection): cStreamdevLiveStreamer::cStreamdevLiveStreamer(const cServerConnection *Connection, const cChannel *Channel, int Priority, eStreamType StreamType, const int* Apid, const int *Dpid) :
cStreamdevStreamer("streamdev-livestreaming", Connection), cStreamdevStreamer("streamdev-livestreaming", Connection),
m_Priority(Priority), m_Priority(Priority),
m_NumPids(0), m_NumPids(0),
m_StreamType(stTSPIDS), m_StreamType(stTSPIDS),
m_Channel(NULL), m_Channel(Channel),
m_Device(NULL), m_Device(NULL),
m_Receiver(NULL), m_Receiver(NULL),
m_PatFilter(NULL), m_PatFilter(NULL),
m_Remux(NULL) m_Remux(NULL),
m_SwitchLive(false)
{ {
m_ReceiveBuffer = new cStreamdevBuffer(LIVEBUFSIZE, TS_SIZE *2, true, "streamdev-livestreamer"), m_ReceiveBuffer = new cStreamdevBuffer(LIVEBUFSIZE, TS_SIZE *2, true, "streamdev-livestreamer"),
m_ReceiveBuffer->SetTimeouts(0, 100); m_ReceiveBuffer->SetTimeouts(0, 100);
if (Priority == IDLEPRIORITY) {
SetChannel(Channel, StreamType, Apid, Dpid);
}
else {
m_Device = SwitchDevice(Channel, Priority);
if (m_Device)
SetChannel(Channel, StreamType, Apid, Dpid);
}
} }
cStreamdevLiveStreamer::~cStreamdevLiveStreamer() cStreamdevLiveStreamer::~cStreamdevLiveStreamer()
@ -613,6 +629,66 @@ void cStreamdevLiveStreamer::Detach(void)
} }
} }
bool cStreamdevLiveStreamer::UsedByLiveTV(cDevice *device)
{
return device == cTransferControl::ReceiverDevice() ||
(device->IsPrimaryDevice() && device->HasDecoder() && !device->Replaying());
}
cDevice *cStreamdevLiveStreamer::SwitchDevice(const cChannel *Channel, int Priority)
{
// turn off the streams of this connection
Detach();
cDevice *device = cDevice::GetDevice(Channel, Priority, false);
if (!device) {
// can't switch - continue the current stream
Attach();
dsyslog("streamdev: GetDevice failed for channel %d (%s) at priority %d (PrimaryDevice=%d, ActualDevice=%d)", Channel->Number(), Channel->Name(), Priority, cDevice::PrimaryDevice()->CardIndex(), cDevice::ActualDevice()->CardIndex());
}
else if (!device->IsTunedToTransponder(Channel) && UsedByLiveTV(device)) {
// make sure VDR main loop doesn't switch back
device->SetOccupied(STREAMDEVTUNETIMEOUT);
if (device->SwitchChannel(Channel, false)) {
// switched away live TV
m_SwitchLive = true;
}
else {
dsyslog("streamdev: SwitchChannel (live) failed for channel %d (%s) at priority %d (PrimaryDevice=%d, ActualDevice=%d, device=%d)", Channel->Number(), Channel->Name(), Priority, cDevice::PrimaryDevice()->CardIndex(), cDevice::ActualDevice()->CardIndex(), device->CardIndex());
device->SetOccupied(0);
device = NULL;
}
}
else if (!device->SwitchChannel(Channel, false)) {
dsyslog("streamdev: SwitchChannel failed for channel %d (%s) at priority %d (PrimaryDevice=%d, ActualDevice=%d, device=%d)", Channel->Number(), Channel->Name(), Priority, cDevice::PrimaryDevice()->CardIndex(), cDevice::ActualDevice()->CardIndex(), device->CardIndex());
device = NULL;
}
return device;
}
bool cStreamdevLiveStreamer::ProvidesChannel(const cChannel *Channel, int Priority)
{
cDevice *device = cDevice::GetDevice(Channel, Priority, false, true);
if (!device)
dsyslog("streamdev: No device provides channel %d (%s) at priority %d", Channel->Number(), Channel->Name(), Priority);
return device;
}
void cStreamdevLiveStreamer::MainThreadHook()
{
if (m_SwitchLive) {
// switched away live TV. Try previous channel on other device first
if (!Channels.SwitchTo(cDevice::CurrentChannel())) {
// switch to streamdev channel otherwise
Channels.SwitchTo(m_Channel->Number());
Skins.Message(mtInfo, tr("Streaming active"));
}
if (m_Device)
m_Device->SetOccupied(0);
m_SwitchLive = false;
}
}
std::string cStreamdevLiveStreamer::Report(void) std::string cStreamdevLiveStreamer::Report(void)
{ {
std::string result; std::string result;

View File

@ -5,6 +5,7 @@
#include <vdr/receiver.h> #include <vdr/receiver.h>
#include "server/streamer.h" #include "server/streamer.h"
#include "server/streamdev-server.h"
#include "common.h" #include "common.h"
#define LIVEBUFSIZE (20000 * TS_SIZE) #define LIVEBUFSIZE (20000 * TS_SIZE)
@ -17,7 +18,7 @@ class cStreamdevLiveReceiver;
// --- cStreamdevLiveStreamer ------------------------------------------------- // --- cStreamdevLiveStreamer -------------------------------------------------
class cStreamdevLiveStreamer: public cStreamdevStreamer { class cStreamdevLiveStreamer: public cStreamdevStreamer, public cMainThreadHookSubscriber {
private: private:
int m_Priority; int m_Priority;
int m_Pids[MAXRECEIVEPIDS + 1]; int m_Pids[MAXRECEIVEPIDS + 1];
@ -29,10 +30,22 @@ private:
cStreamdevBuffer *m_ReceiveBuffer; cStreamdevBuffer *m_ReceiveBuffer;
cStreamdevPatFilter *m_PatFilter; cStreamdevPatFilter *m_PatFilter;
Streamdev::cTSRemux *m_Remux; Streamdev::cTSRemux *m_Remux;
bool m_SwitchLive;
void StartReceiver(void); void StartReceiver(void);
bool HasPid(int Pid); bool HasPid(int Pid);
/* Test if device is in use as the transfer mode receiver device
or a FF card, displaying live TV from internal tuner */
static bool UsedByLiveTV(cDevice *device);
/* Find a suitable device and tune it to the requested channel. */
cDevice *SwitchDevice(const cChannel *Channel, int Priority);
void SetDevice(cDevice *Device) { m_Device = Device; }
bool SetChannel(const cChannel *Channel, eStreamType StreamType, const int* Apid = NULL, const int* Dpid = NULL);
protected: protected:
virtual uchar* GetFromReceiver(int &Count) { return m_ReceiveBuffer->Get(Count); } virtual uchar* GetFromReceiver(int &Count) { return m_ReceiveBuffer->Get(Count); }
virtual void DelFromReceiver(int Count) { m_ReceiveBuffer->Del(Count); } virtual void DelFromReceiver(int Count) { m_ReceiveBuffer->Del(Count); }
@ -41,13 +54,11 @@ protected:
virtual void Action(void); virtual void Action(void);
public: public:
cStreamdevLiveStreamer(int Priority, const cServerConnection *Connection); cStreamdevLiveStreamer(const cServerConnection *Connection, const cChannel *Channel, int Priority, eStreamType StreamType, const int* Apid = NULL, const int* Dpid = NULL);
virtual ~cStreamdevLiveStreamer(); virtual ~cStreamdevLiveStreamer();
void SetDevice(cDevice *Device) { m_Device = Device; }
bool SetPid(int Pid, bool On); bool SetPid(int Pid, bool On);
bool SetPids(int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL); bool SetPids(int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL);
bool SetChannel(const cChannel *Channel, eStreamType StreamType, const int* Apid = NULL, const int* Dpid = NULL);
void SetPriority(int Priority); void SetPriority(int Priority);
void GetSignal(int *DevNum, int *Strength, int *Quality) const; void GetSignal(int *DevNum, int *Strength, int *Quality) const;
virtual cString ToText() const; virtual cString ToText() const;
@ -61,6 +72,14 @@ public:
virtual void Attach(void); virtual void Attach(void);
virtual void Detach(void); virtual void Detach(void);
cDevice *GetDevice() const { return m_Device; }
/* Test if a call to GetDevice would return a usable device. */
static bool ProvidesChannel(const cChannel *Channel, int Priority);
/* Do things which must be done in VDR's main loop */
void MainThreadHook();
// Statistical purposes: // Statistical purposes:
virtual std::string Report(void); virtual std::string Report(void);
}; };

View File

@ -18,6 +18,29 @@
#error "VDR-1.7.25 or greater required to compile server! Use 'make client' to compile client only." #error "VDR-1.7.25 or greater required to compile server! Use 'make client' to compile client only."
#endif #endif
cList<cMainThreadHookSubscriber> cMainThreadHookSubscriber::m_Subscribers;
cMutex cMainThreadHookSubscriber::m_Mutex;
const cList<cMainThreadHookSubscriber>& cMainThreadHookSubscriber::Subscribers(cMutexLock& Lock)
{
Lock.Lock(&m_Mutex);
return m_Subscribers;
}
cMainThreadHookSubscriber::cMainThreadHookSubscriber()
{
m_Mutex.Lock();
m_Subscribers.Add(this);
m_Mutex.Unlock();
}
cMainThreadHookSubscriber::~cMainThreadHookSubscriber()
{
m_Mutex.Lock();
m_Subscribers.Del(this, false);
m_Mutex.Unlock();
}
const char *cPluginStreamdevServer::DESCRIPTION = trNOOP("VDR Streaming Server"); const char *cPluginStreamdevServer::DESCRIPTION = trNOOP("VDR Streaming Server");
cPluginStreamdevServer::cPluginStreamdevServer(void) cPluginStreamdevServer::cPluginStreamdevServer(void)
@ -139,9 +162,9 @@ void cPluginStreamdevServer::MainThreadHook(void)
m_Suspend = false; m_Suspend = false;
} }
cThreadLock lock; cMutexLock lock;
const cList<cServerConnection>& clients = cStreamdevServer::Clients(lock); const cList<cMainThreadHookSubscriber>& subs = cMainThreadHookSubscriber::Subscribers(lock);
for (cServerConnection *s = clients.First(); s; s = clients.Next(s)) for (cMainThreadHookSubscriber *s = subs.First(); s; s = subs.Next(s))
s->MainThreadHook(); s->MainThreadHook();
} }

View File

@ -7,8 +7,22 @@
#include "common.h" #include "common.h"
#include <vdr/tools.h>
#include <vdr/plugin.h> #include <vdr/plugin.h>
class cMainThreadHookSubscriber: public cListObject {
private:
static cList<cMainThreadHookSubscriber> m_Subscribers;
static cMutex m_Mutex;
public:
static const cList<cMainThreadHookSubscriber>& Subscribers(cMutexLock& Lock);
virtual void MainThreadHook() = 0;
cMainThreadHookSubscriber();
virtual ~cMainThreadHookSubscriber();
};
class cPluginStreamdevServer : public cPlugin { class cPluginStreamdevServer : public cPlugin {
private: private:
static const char *DESCRIPTION; static const char *DESCRIPTION;