diff --git a/pvr.hts/addon.xml.in b/pvr.hts/addon.xml.in
index e2b27340..fc54ab35 100644
--- a/pvr.hts/addon.xml.in
+++ b/pvr.hts/addon.xml.in
@@ -8,6 +8,7 @@
+
RegisterMe(hdl) ||
+ if (!XBMC->RegisterMe(hdl) || !GUI->RegisterMe(hdl) ||
!CODEC->RegisterMe(hdl) || !PVR->RegisterMe(hdl))
{
SAFE_DELETE(PVR);
SAFE_DELETE(CODEC);
SAFE_DELETE(XBMC);
+ SAFE_DELETE(GUI);
return ADDON_STATUS_PERMANENT_FAILURE;
}
@@ -107,6 +109,7 @@ ADDON_STATUS ADDON_Create(void* hdl, void* _unused(props))
SAFE_DELETE(PVR);
SAFE_DELETE(CODEC);
SAFE_DELETE(XBMC);
+ SAFE_DELETE(GUI);
return ADDON_STATUS_LOST_CONNECTION;
}
@@ -133,6 +136,7 @@ void ADDON_Destroy()
SAFE_DELETE(PVR);
SAFE_DELETE(CODEC);
SAFE_DELETE(XBMC);
+ SAFE_DELETE(GUI);
SAFE_DELETE(menuHook);
m_CurStatus = ADDON_STATUS_UNKNOWN;
}
diff --git a/src/client.h b/src/client.h
index 3a5f950e..8196a07f 100644
--- a/src/client.h
+++ b/src/client.h
@@ -25,10 +25,12 @@
#include "kodi/libXBMC_addon.h"
#include "kodi/libXBMC_pvr.h"
#include "kodi/libXBMC_codec.h"
+#include "kodi/libKODI_guilib.h"
extern ADDON::CHelper_libXBMC_addon* XBMC;
extern CHelper_libXBMC_pvr* PVR;
extern CHelper_libXBMC_codec* CODEC;
+extern CHelper_libKODI_guilib* GUI;
/* timer type ids */
#define TIMER_ONCE_MANUAL (PVR_TIMER_TYPE_NONE + 1)
diff --git a/src/tvheadend/Subscription.cpp b/src/tvheadend/Subscription.cpp
index 9c94ad66..a0f63aa0 100644
--- a/src/tvheadend/Subscription.cpp
+++ b/src/tvheadend/Subscription.cpp
@@ -227,6 +227,10 @@ void Subscription::ParseSubscriptionStatus ( htsmsg_t *m )
return;
}
+ /* Switched from preTuning or postTuning to an active subscription, initiate a virtual start */
+ if (GetState() == SUBSCRIPTION_PREPOSTTUNING)
+ SetState(SUBSCRIPTION_STARTING);
+
const char *status = htsmsg_get_str(m, "status");
if (status != NULL)
@@ -247,7 +251,22 @@ void Subscription::ParseSubscriptionStatus ( htsmsg_t *m )
else if (!strcmp("userLimit", error))
SetState(SUBSCRIPTION_USERLIMIT);
else if (!strcmp("noFreeAdapter", error))
- SetState(SUBSCRIPTION_NOFREEADAPTER);
+ {
+ /* No free adapter, AKA subscription conflict */
+ if (GetState() == SUBSCRIPTION_RUNNING ||
+ GetState() == SUBSCRIPTION_STARTING)
+ {
+ /* Show conflict dialog only if it was running before or if it's a new subscription */
+ /* NOTE: the dialog will set the state to 'SUBSCRIPTION_NOFREEADAPTER' so we don't have to do this here */
+ std::thread(&Subscription::ShowConflictDialog, this).detach();
+ }
+ else
+ {
+ /* Do not overwrite 'SUBSCRIPTION_NOFREEADAPTER_SOLVE_FAILED' */
+ if (GetState() != SUBSCRIPTION_NOFREEADAPTER_SOLVE_FAILED)
+ SetState(SUBSCRIPTION_NOFREEADAPTER);
+ }
+ }
else if (!strcmp("tuningFailed", error))
SetState(SUBSCRIPTION_TUNINGFAILED);
else if (!strcmp("userAccess", error))
@@ -292,6 +311,106 @@ void Subscription::ShowStateNotification(void)
XBMC->QueueNotification(ADDON::QUEUE_WARNING, XBMC->GetLocalizedString(30455));
else if (GetState() == SUBSCRIPTION_UNKNOWN)
XBMC->QueueNotification(ADDON::QUEUE_WARNING, XBMC->GetLocalizedString(30456));
+ else if (GetState() == SUBSCRIPTION_NOFREEADAPTER_SOLVE_FAILED)
+ XBMC->QueueNotification(ADDON::QUEUE_WARNING, XBMC->GetLocalizedString(30457));
+}
+
+void Subscription::ShowConflictDialog(void)
+{
+ tvhdebug("demux conflict dialog: open, state: %i, subscription id: %i", GetState(), GetId());
+
+ /* Save the initial subscription id */
+ uint32_t initialId = GetId();
+
+ /* Save the initial subscription state */
+ eSubsriptionState initialState = GetState();
+
+ /* As we saved the initial state, we can change this now */
+ SetState(SUBSCRIPTION_NOFREEADAPTER);
+
+ /*
+ * Conflict case 1: (initialState == SUBSCRIPTION_RUNNING)
+ * Subscription was running before, but the adapter got stolen by an other subscription
+ * Ask user if he wants to continue watching by hijacking an adapter back (increase weight)
+ *
+ * Conflict case 2: (initialState == SUBSCRIPTION_STARTING)
+ * No free adapter found to start this channel from the beginning on
+ * Ask user if he wants to hijack an adapter (increase weight) after 'DIALOG_NOSTART_DELAY' seconds of idling
+ * This time of idling is needed to prevent the dialog from popping up when zapping
+ */
+
+ if (initialState == SUBSCRIPTION_STARTING)
+ {
+ for (int i = 0; i < DIALOG_NOSTART_DELAY; i++)
+ {
+ sleep(1);
+
+ /* Break if the conflict isn't valid anymore */
+ if (!IsConflictStillValid(initialId))
+ return;
+ }
+ }
+
+ bool bDialogForceStart = GUI->Dialog_YesNo_ShowAndGetInput(
+ XBMC->GetLocalizedString(30500), XBMC->GetLocalizedString(initialState == SUBSCRIPTION_STARTING ? 30450 : 30501),
+ XBMC->GetLocalizedString(30502), XBMC->GetLocalizedString(30503),
+ XBMC->GetLocalizedString(30504), XBMC->GetLocalizedString(30505));
+
+ if (bDialogForceStart)
+ {
+ while (GetWeight() <= SUBSCRIPTION_WEIGHT_MAX)
+ {
+ /* Break if the conflict isn't valid anymore */
+ if (!IsConflictStillValid(initialId))
+ return;
+
+ /* Set first to minimum and then increase in steps */
+ if (GetWeight() < SUBSCRIPTION_WEIGHT_MIN)
+ SendWeight(SUBSCRIPTION_WEIGHT_MIN);
+ else
+ SendWeight(GetWeight() + SUBSCRIPTION_WEIGHT_STEPSIZE);
+
+ sleep(1);
+ }
+
+ /* Still not running, we failed to solve this conflict */
+ if (GetState() != SUBSCRIPTION_RUNNING)
+ SetState(SUBSCRIPTION_NOFREEADAPTER_SOLVE_FAILED);
+ }
+}
+
+bool Subscription::IsConflictStillValid(uint32_t id)
+{
+ /* Check if the current id is different -> channel was switched meanwhile*/
+ if (GetId() != id)
+ {
+ tvhinfo("demux conflict dialog: abort, channel was switched");
+ return false;
+ }
+
+ /* Check if the conflict was solved meanwhile */
+ if (GetState() == SUBSCRIPTION_RUNNING)
+ {
+ tvhinfo("demux conflict dialog: abort, conflict was solved!");
+ return false;
+ }
+
+ /* Check if the state was changed meanwhile */
+ if (GetState() != SUBSCRIPTION_NOFREEADAPTER)
+ {
+ tvhinfo("demux conflict dialog: abort, subscription state was changed");
+ return false;
+ }
+
+ /* Check if the subscription changed to preTuning or postTuning meanwhile */
+ if (GetWeight() == static_cast(SUBSCRIPTION_WEIGHT_PRETUNING) ||
+ GetWeight() == static_cast(SUBSCRIPTION_WEIGHT_POSTTUNING))
+ {
+ tvhinfo("demux conflict dialog: abort, subscription was changed to pre or posttuning");
+ return false;
+ }
+
+ return true;
}
uint32_t Subscription::GetNextId()
diff --git a/src/tvheadend/Subscription.h b/src/tvheadend/Subscription.h
index cea83aa4..d2365a4e 100644
--- a/src/tvheadend/Subscription.h
+++ b/src/tvheadend/Subscription.h
@@ -22,6 +22,7 @@
*/
#include "platform/threads/mutex.h"
+#include
extern "C"
{
@@ -43,12 +44,17 @@ namespace tvheadend
SUBSCRIPTION_WEIGHT_SERVERCONF = 0,
};
+ /* Used when gradually increasing the subscription weight to solve a conflict */
+ static const int SUBSCRIPTION_WEIGHT_MIN = 50;
+ static const int SUBSCRIPTION_WEIGHT_MAX = 600;
+ static const int SUBSCRIPTION_WEIGHT_STEPSIZE = 50;
+
enum eSubsriptionState
{
SUBSCRIPTION_STOPPED = 0, /* subscription is stopped or not started yet */
SUBSCRIPTION_STARTING = 1, /* subscription is starting */
SUBSCRIPTION_RUNNING = 2, /* subscription is running normal */
- SUBSCRIPTION_NOFREEADAPTER = 3, /* subscription has no free adapter to use */
+ SUBSCRIPTION_NOFREEADAPTER = 3, /* subscription has no free adapter to use, AKA subscription conflict */
SUBSCRIPTION_SCRAMBLED = 4, /* subscription is not running because the channel is scrambled */
SUBSCRIPTION_NOSIGNAL = 5, /* subscription is not running because of a weak/no input signal */
SUBSCRIPTION_TUNINGFAILED = 6, /* subscription could not be started because of a tuning error */
@@ -56,9 +62,11 @@ namespace tvheadend
SUBSCRIPTION_NOACCESS = 8, /* we have no rights to watch this channel */
SUBSCRIPTION_UNKNOWN = 9, /* subscription state is unknown, also used for pretuning and posttuning subscriptions */
SUBSCRIPTION_PREPOSTTUNING = 10, /* used for pre and posttuning subscriptions (we do not care what the actual state is) */
+ SUBSCRIPTION_NOFREEADAPTER_SOLVE_FAILED = 11, /* our subscription was in conflict (=SUBSCRIPTION_NOFREEADAPTER) and we tried to so solve it with increasing the weight, but we failed */
};
- static const int PACKET_QUEUE_DEPTH = 2000000;
+ static const int PACKET_QUEUE_DEPTH = 2000000;
+ static const int DIALOG_NOSTART_DELAY = 5;
class Subscription
{
@@ -124,6 +132,19 @@ namespace tvheadend
*/
void ShowStateNotification();
+ /**
+ * Show a dialog to the user on a subscription conflict
+ * Ask him to increase the subscription weight as this might solve the conflict
+ */
+ void ShowConflictDialog();
+
+ /**
+ * Check if the subscription is in conflict and that the conflict is still valid
+ * @param id the subscription id of the subscription to check
+ * @return true if the conflict is still valid, false otherwise
+ */
+ bool IsConflictStillValid(uint32_t id);
+
/**
* Get the next unique subscription Id
*/