mirror of
https://projects.vdr-developer.org/git/vdr-plugin-streamdev.git
synced 2023-10-10 19:16:51 +02:00
Revised class responsibilities: Moved live TV related functions to livestreamer
This commit is contained in:
parent
7be0c81a81
commit
e555017565
@ -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()); }
|
||||||
|
@ -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; }
|
||||||
|
|
||||||
|
@ -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) {
|
|
||||||
cStreamdevLiveStreamer* liveStreamer = new cStreamdevLiveStreamer(StreamdevServerSetup.HTTPPriority, this);
|
|
||||||
SetStreamer(liveStreamer);
|
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))) {
|
||||||
|
@ -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) {
|
|
||||||
cStreamdevLiveStreamer * liveStreamer = new cStreamdevLiveStreamer(StreamdevServerSetup.IGMPPriority, this);
|
|
||||||
SetStreamer(liveStreamer);
|
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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user