Set device occupied when streamdev switches away LiveTV on the server, to

reduce the risk that the VDR main loop immediately switches back, resulting
in a black screen on the client (reported by hummel99)
This commit is contained in:
Frank Schmirler 2013-11-01 15:33:19 +01:00
parent 458a21a62a
commit 1d4a7e06b4
7 changed files with 39 additions and 19 deletions

View File

@ -223,4 +223,5 @@ hivdr
for suggesting to add the HTTP "Server" header for suggesting to add the HTTP "Server" header
hummel99 hummel99
for helping to debug channel switch issues with priority > 0 for reporting and helping to debug channel switch issues with priority > 0
for reporting a race condition when switching the server's LiveTV device

View File

@ -1,7 +1,10 @@
VDR Plugin 'streamdev' Revision History VDR Plugin 'streamdev' Revision History
--------------------------------------- ---------------------------------------
- Fixed channel switch issues with priority > 0 - Set device occupied when streamdev switches away LiveTV on the server, to
reduce the risk that the VDR main loop immediately switches back, resulting
in a black screen on the client (reported by hummel99)
- Fixed channel switch issues with priority > 0 (reported by hummel99)
- Removed noisy debug messages - Removed noisy debug messages
- Fixed HTTP menu destruction - Fixed HTTP menu destruction
- API change of VDR 2.1.2 - API change of VDR 2.1.2

View File

@ -14,6 +14,11 @@
#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),
@ -23,6 +28,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_OccupiedDev(NULL),
m_SwitchTo(NULL) m_SwitchTo(NULL)
{ {
} }
@ -204,7 +210,7 @@ bool cServerConnection::UsedByLiveTV(cDevice *device)
(device->IsPrimaryDevice() && device->HasDecoder() && !device->Replaying()); (device->IsPrimaryDevice() && device->HasDecoder() && !device->Replaying());
} }
cDevice *cServerConnection::GetDevice(const cChannel *Channel, int Priority) cDevice *cServerConnection::SwitchDevice(const cChannel *Channel, int Priority)
{ {
// turn off the streams of this connection // turn off the streams of this connection
Detach(); Detach();
@ -216,9 +222,23 @@ cDevice *cServerConnection::GetDevice(const cChannel *Channel, int Priority)
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()); 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)) { 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 // switched away live TV
m_OccupiedDev = device;
m_SwitchTo = Channel; 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; return device;
} }
@ -240,6 +260,7 @@ void cServerConnection::MainThreadHook()
Channels.SwitchTo(m_SwitchTo->Number()); Channels.SwitchTo(m_SwitchTo->Number());
Skins.Message(mtInfo, tr("Streaming active")); Skins.Message(mtInfo, tr("Streaming active"));
} }
m_OccupiedDev->SetOccupied(0);
m_SwitchTo = NULL; m_SwitchTo = NULL;
} }
} }

View File

@ -34,6 +34,8 @@ private:
uint m_WriteBytes; uint m_WriteBytes;
uint m_WriteIndex; uint m_WriteIndex;
/* Set to occupied device when live TV was interrupted */
cDevice *m_OccupiedDev;
/* Set to this connection's current channel when live TV was interrupted */ /* Set to this connection's current channel when live TV was interrupted */
const cChannel *m_SwitchTo; const cChannel *m_SwitchTo;
@ -106,10 +108,8 @@ public:
channel. This call has no side effects. */ channel. This call has no side effects. */
static cDevice *CheckDevice(const cChannel *Channel, int Priority, bool LiveView, const cDevice *AvoidDevice = NULL); static cDevice *CheckDevice(const cChannel *Channel, int Priority, bool LiveView, const cDevice *AvoidDevice = NULL);
/* Will retrieve an unused device for transmitting data. Receivers have /* Find a suitable device and tune it to the requested channel. */
already been attached from the device if necessary. Use the returned cDevice *SwitchDevice(const cChannel *Channel, int Priority);
cDevice in a following call to StartTransfer */
cDevice *GetDevice(const cChannel *Channel, int Priority);
/* Test if a call to GetDevice would return a usable device. */ /* Test if a call to GetDevice would return a usable device. */
bool ProvidesChannel(const cChannel *Channel, int Priority); bool ProvidesChannel(const cChannel *Channel, int Priority);

View File

@ -177,9 +177,8 @@ bool cConnectionHTTP::ProcessRequest(void)
else if (m_Channel != NULL) { else if (m_Channel != NULL) {
cDevice *device = NULL; cDevice *device = NULL;
if (ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority)) if (ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority))
device = GetDevice(m_Channel, StreamdevServerSetup.HTTPPriority); device = SwitchDevice(m_Channel, StreamdevServerSetup.HTTPPriority);
if (device != NULL) { if (device != NULL) {
device->SwitchChannel(m_Channel, false);
cStreamdevLiveStreamer* liveStreamer = new cStreamdevLiveStreamer(StreamdevServerSetup.HTTPPriority, this); cStreamdevLiveStreamer* liveStreamer = new cStreamdevLiveStreamer(StreamdevServerSetup.HTTPPriority, this);
m_Streamer = liveStreamer; m_Streamer = liveStreamer;
if (liveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL)) { if (liveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL)) {

View File

@ -44,9 +44,8 @@ void cConnectionIGMP::Welcome()
{ {
cDevice *device = NULL; cDevice *device = NULL;
if (ProvidesChannel(m_Channel, StreamdevServerSetup.IGMPPriority)) if (ProvidesChannel(m_Channel, StreamdevServerSetup.IGMPPriority))
device = GetDevice(m_Channel, StreamdevServerSetup.IGMPPriority); device = SwitchDevice(m_Channel, StreamdevServerSetup.IGMPPriority);
if (device != NULL) { if (device != NULL) {
device->SwitchChannel(m_Channel, false);
m_LiveStreamer = new cStreamdevLiveStreamer(StreamdevServerSetup.IGMPPriority, this); m_LiveStreamer = new cStreamdevLiveStreamer(StreamdevServerSetup.IGMPPriority, this);
if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType)) { if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType)) {
m_LiveStreamer->SetDevice(device); m_LiveStreamer->SetDevice(device);
@ -61,7 +60,7 @@ void cConnectionIGMP::Welcome()
} }
} }
else else
esyslog("streamdev-server IGMP: GetDevice failed"); esyslog("streamdev-server IGMP: SwitchDevice failed");
} }
bool cConnectionIGMP::Close() bool cConnectionIGMP::Close()

View File

@ -1118,11 +1118,8 @@ bool cConnectionVTP::CmdTUNE(char *Opts)
if (!ProvidesChannel(chan, prio)) if (!ProvidesChannel(chan, prio))
return Respond(560, "Channel not available (ProvidesChannel)"); return Respond(560, "Channel not available (ProvidesChannel)");
} }
if ((dev = GetDevice(chan, prio)) == NULL) if ((dev = SwitchDevice(chan, prio)) == NULL)
return Respond(560, "Channel not available (GetDevice)"); return Respond(560, "Channel not available (SwitchDevice)");
if (!dev->SwitchChannel(chan, false))
return Respond(560, "Channel not available (SwitchChannel)");
delete m_LiveStreamer; delete m_LiveStreamer;
m_LiveStreamer = new cStreamdevLiveStreamer(prio, this); m_LiveStreamer = new cStreamdevLiveStreamer(prio, this);