From 1d4a7e06b4e5cc5a1f115d9d361c2c7b60699ff9 Mon Sep 17 00:00:00 2001 From: Frank Schmirler Date: Fri, 1 Nov 2013 15:33:19 +0100 Subject: [PATCH] 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) --- CONTRIBUTORS | 3 ++- HISTORY | 5 ++++- server/connection.c | 27 ++++++++++++++++++++++++--- server/connection.h | 8 ++++---- server/connectionHTTP.c | 3 +-- server/connectionIGMP.c | 5 ++--- server/connectionVTP.c | 7 ++----- 7 files changed, 39 insertions(+), 19 deletions(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index cba51c1..ae9918f 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -223,4 +223,5 @@ hivdr for suggesting to add the HTTP "Server" header 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 diff --git a/HISTORY b/HISTORY index a80137b..8b2ec48 100644 --- a/HISTORY +++ b/HISTORY @@ -1,7 +1,10 @@ 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 - Fixed HTTP menu destruction - API change of VDR 2.1.2 diff --git a/server/connection.c b/server/connection.c index 9c7ae31..e0e7f88 100644 --- a/server/connection.c +++ b/server/connection.c @@ -14,6 +14,11 @@ #include #include +// 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): cTBSocket(Type), @@ -23,6 +28,7 @@ cServerConnection::cServerConnection(const char *Protocol, int Type): m_ReadBytes(0), m_WriteBytes(0), m_WriteIndex(0), + m_OccupiedDev(NULL), m_SwitchTo(NULL) { } @@ -204,7 +210,7 @@ bool cServerConnection::UsedByLiveTV(cDevice *device) (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 Detach(); @@ -216,8 +222,22 @@ 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()); } else if (!device->IsTunedToTransponder(Channel) && UsedByLiveTV(device)) { - // switched away live TV - m_SwitchTo = Channel; + // 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; } @@ -240,6 +260,7 @@ void cServerConnection::MainThreadHook() Channels.SwitchTo(m_SwitchTo->Number()); Skins.Message(mtInfo, tr("Streaming active")); } + m_OccupiedDev->SetOccupied(0); m_SwitchTo = NULL; } } diff --git a/server/connection.h b/server/connection.h index ba1f7bb..e7fb21b 100644 --- a/server/connection.h +++ b/server/connection.h @@ -34,6 +34,8 @@ private: uint m_WriteBytes; 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 */ const cChannel *m_SwitchTo; @@ -106,10 +108,8 @@ public: channel. This call has no side effects. */ static cDevice *CheckDevice(const cChannel *Channel, int Priority, bool LiveView, const cDevice *AvoidDevice = NULL); - /* Will retrieve an unused device for transmitting data. Receivers have - already been attached from the device if necessary. Use the returned - cDevice in a following call to StartTransfer */ - cDevice *GetDevice(const cChannel *Channel, int Priority); + /* 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); diff --git a/server/connectionHTTP.c b/server/connectionHTTP.c index e00083e..ef8b6ed 100644 --- a/server/connectionHTTP.c +++ b/server/connectionHTTP.c @@ -177,9 +177,8 @@ bool cConnectionHTTP::ProcessRequest(void) else if (m_Channel != NULL) { cDevice *device = NULL; if (ProvidesChannel(m_Channel, StreamdevServerSetup.HTTPPriority)) - device = GetDevice(m_Channel, StreamdevServerSetup.HTTPPriority); + device = SwitchDevice(m_Channel, StreamdevServerSetup.HTTPPriority); if (device != NULL) { - device->SwitchChannel(m_Channel, false); cStreamdevLiveStreamer* liveStreamer = new cStreamdevLiveStreamer(StreamdevServerSetup.HTTPPriority, this); m_Streamer = liveStreamer; if (liveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid[0] ? m_Apid : NULL, m_Dpid[0] ? m_Dpid : NULL)) { diff --git a/server/connectionIGMP.c b/server/connectionIGMP.c index 1f8f3d8..ab80a6a 100644 --- a/server/connectionIGMP.c +++ b/server/connectionIGMP.c @@ -44,9 +44,8 @@ void cConnectionIGMP::Welcome() { cDevice *device = NULL; if (ProvidesChannel(m_Channel, StreamdevServerSetup.IGMPPriority)) - device = GetDevice(m_Channel, StreamdevServerSetup.IGMPPriority); + device = SwitchDevice(m_Channel, StreamdevServerSetup.IGMPPriority); if (device != NULL) { - device->SwitchChannel(m_Channel, false); m_LiveStreamer = new cStreamdevLiveStreamer(StreamdevServerSetup.IGMPPriority, this); if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType)) { m_LiveStreamer->SetDevice(device); @@ -61,7 +60,7 @@ void cConnectionIGMP::Welcome() } } else - esyslog("streamdev-server IGMP: GetDevice failed"); + esyslog("streamdev-server IGMP: SwitchDevice failed"); } bool cConnectionIGMP::Close() diff --git a/server/connectionVTP.c b/server/connectionVTP.c index b3be57e..087d02d 100644 --- a/server/connectionVTP.c +++ b/server/connectionVTP.c @@ -1118,11 +1118,8 @@ bool cConnectionVTP::CmdTUNE(char *Opts) if (!ProvidesChannel(chan, prio)) return Respond(560, "Channel not available (ProvidesChannel)"); } - if ((dev = GetDevice(chan, prio)) == NULL) - return Respond(560, "Channel not available (GetDevice)"); - - if (!dev->SwitchChannel(chan, false)) - return Respond(560, "Channel not available (SwitchChannel)"); + if ((dev = SwitchDevice(chan, prio)) == NULL) + return Respond(560, "Channel not available (SwitchDevice)"); delete m_LiveStreamer; m_LiveStreamer = new cStreamdevLiveStreamer(prio, this);