Skip to content

Commit

Permalink
Add subscription conflict handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Glenn-1990 committed Oct 21, 2015
1 parent 631916b commit 3382f47
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 10 deletions.
1 change: 1 addition & 0 deletions pvr.hts/addon.xml.in
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<c-pluff version="0.1"/>
<import addon="xbmc.pvr" version="4.1.0"/>
<import addon="xbmc.codec" version="1.0.1"/>
<import addon="kodi.guilib" version="5.8.0"/>
</requires>
<extension
point="xbmc.pvrclient"
Expand Down
33 changes: 33 additions & 0 deletions pvr.hts/resources/language/resource.language.en_gb/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,36 @@ msgstr ""
msgctxt "#30456"
msgid "Subscription error"
msgstr ""

msgctxt "#30457"
msgid "Failed to hijack an adapter"
msgstr ""

#empty strings from id 30458 to 30499

#. Subscription conflict dialog

msgctxt "#30500"
msgid "Subscription conflict"
msgstr ""

msgctxt "#30501"
msgid "Streaming aborted, adapter stolen by an other subscription"
msgstr ""

msgctxt "#30502"
msgid "Do you want to increase the priority to hijack an adapter?"
msgstr ""

msgctxt "#30503"
msgid "WARNING: this can abort an active recording or live stream"
msgstr ""

msgctxt "#30504"
msgid "Ignore"
msgstr ""

msgctxt "#30505"
msgid "Increase priority"
msgstr ""

18 changes: 11 additions & 7 deletions src/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

#include "client.h"
#include "kodi/xbmc_pvr_dll.h"
#include "kodi/libKODI_guilib.h"
#include "platform/util/util.h"
#include "Tvheadend.h"
#include "tvheadend/Settings.h"
Expand All @@ -45,11 +44,12 @@ bool m_bAlertHtspVersionMismatch = true;
* Globals
*/
CMutex g_mutex;
CHelper_libXBMC_addon *XBMC = NULL;
CHelper_libXBMC_pvr *PVR = NULL;
CHelper_libXBMC_codec *CODEC = NULL;
PVR_MENUHOOK *menuHook = NULL;
CTvheadend *tvh = NULL;
CHelper_libXBMC_addon *XBMC = NULL;
CHelper_libXBMC_pvr *PVR = NULL;
CHelper_libXBMC_codec *CODEC = NULL;
CHelper_libKODI_guilib *GUI = NULL;
PVR_MENUHOOK *menuHook = NULL;
CTvheadend *tvh = NULL;

/* **************************************************************************
* ADDON setup
Expand All @@ -71,13 +71,15 @@ ADDON_STATUS ADDON_Create(void* hdl, void* _unused(props))
XBMC = new CHelper_libXBMC_addon;
CODEC = new CHelper_libXBMC_codec;
PVR = new CHelper_libXBMC_pvr;
GUI = new CHelper_libKODI_guilib;

if (!XBMC->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;
}

Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down
2 changes: 2 additions & 0 deletions src/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
121 changes: 120 additions & 1 deletion src/tvheadend/Subscription.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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))
Expand Down Expand Up @@ -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<uint32_t>(SUBSCRIPTION_WEIGHT_PRETUNING) ||
GetWeight() == static_cast<uint32_t>(SUBSCRIPTION_WEIGHT_POSTTUNING))
{
tvhinfo("demux conflict dialog: abort, subscription was changed to pre or posttuning");
return false;
}

return true;
}

uint32_t Subscription::GetNextId()
Expand Down
25 changes: 23 additions & 2 deletions src/tvheadend/Subscription.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*/

#include "platform/threads/mutex.h"
#include <thread>

extern "C"
{
Expand All @@ -43,22 +44,29 @@ 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 */
SUBSCRIPTION_USERLIMIT = 7, /* userlimit is reached, so we could not start this subscription */
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
{
Expand Down Expand Up @@ -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
*/
Expand Down

0 comments on commit 3382f47

Please sign in to comment.