Skip to content

Commit

Permalink
Add setting to enable streaming conflict management
Browse files Browse the repository at this point in the history
  • Loading branch information
Glenn-1990 committed Oct 28, 2015
1 parent 79dba3b commit e7eda9b
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 13 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
40 changes: 40 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 @@ -241,3 +241,43 @@ msgstr ""
msgctxt "#30502"
msgid "Streaming profile %s is not available"
msgstr ""

#empty strings from id 30503 to 30549

#. Streaming conflict management

msgctxt "#30550"
msgid "TV conflict"
msgstr ""

msgctxt "#30551"
msgid "All adapters are in use."
msgstr ""

msgctxt "#30552"
msgid "To keep watching TV, you can interrupt the lowest priority service."
msgstr ""

msgctxt "#30553"
msgid "To watch TV, you can interrupt the lowest priority service."
msgstr ""

msgctxt "#30554"
msgid "This can be an active recording or an other TV client."
msgstr ""

msgctxt "#30555"
msgid "Do nothing"
msgstr ""

msgctxt "#30556"
msgid "Interrupt service"
msgstr ""

msgctxt "#30557"
msgid "Interrupting service... %i%%"
msgstr ""

msgctxt "#30558"
msgid "Streaming conflict management"
msgstr ""
1 change: 1 addition & 0 deletions pvr.hts/resources/settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

<setting label="30500" type="lsep"/>
<setting id="streaming_profile" type="text" label="30501" default="" />
<setting id="streaming_conflict" type="bool" label="30558" default="false"/>

<setting label="30400" type="lsep"/>
<setting id="pretuner_enabled" type="bool" label="30403" default="false" />
Expand Down
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
6 changes: 6 additions & 0 deletions src/tvheadend/Settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const int Settings::DEFAULT_PRETUNER_CLOSEDELAY = 10; // secs
const int Settings::DEFAULT_AUTOREC_MAXDIFF = 15; // mins. Maximum difference between real and approximate start time for auto recordings
const int Settings::DEFAULT_APPROX_TIME = 0; // mins
const std::string Settings::DEFAULT_STREAMING_PROFILE = "";
const bool Settings::DEFAULT_STREAMING_CONFLICT = false;

void Settings::ReadSettings()
{
Expand Down Expand Up @@ -70,6 +71,9 @@ void Settings::ReadSettings()

/* Streaming */
SetStreamingProfile(ReadStringSetting("streaming_profile", DEFAULT_STREAMING_PROFILE));

/* Subscription conflict management */
SetStreamingConflict(ReadBoolSetting("streaming_conflict", DEFAULT_STREAMING_CONFLICT));
}

ADDON_STATUS Settings::SetSetting(const std::string &key, const void *value)
Expand Down Expand Up @@ -115,6 +119,8 @@ ADDON_STATUS Settings::SetSetting(const std::string &key, const void *value)
/* Streaming */
else if (key == "streaming_profile")
return SetStringSetting(GetStreamingProfile(), value);
else if (key == "streaming_conflict")
return SetBoolSetting(GetStreamingConflict(), value);
else
{
tvherror("Settings::SetSetting - unknown setting '%s'", key.c_str());
Expand Down
4 changes: 4 additions & 0 deletions src/tvheadend/Settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ namespace tvheadend {
static const int DEFAULT_AUTOREC_MAXDIFF; // mins. Maximum difference between real and approximate start time for auto recordings
static const int DEFAULT_APPROX_TIME; // mins
static const std::string DEFAULT_STREAMING_PROFILE;
static const bool DEFAULT_STREAMING_CONFLICT;

/**
* Singleton getter for the instance
Expand Down Expand Up @@ -86,6 +87,7 @@ namespace tvheadend {
bool GetAutorecApproxTime() const { return m_bAutorecApproxTime; }
int GetAutorecMaxDiff() const { return m_iPreTunerCloseDelay; }
std::string GetStreamingProfile() const { return m_strStreamingProfile; }
bool GetStreamingConflict() const { return m_bStreamingConflict; }

private:
Settings()
Expand Down Expand Up @@ -124,6 +126,7 @@ namespace tvheadend {
void SetAutorecApproxTime(bool value) { m_bAutorecApproxTime = value; }
void SetAutorecMaxDiff(int value) { m_iAutorecMaxDiff = value; }
void SetStreamingProfile(const std::string &value) { m_strStreamingProfile = value; }
void SetStreamingConflict(bool value) { m_bStreamingConflict = value; }

/**
* Read/Set values according to definition in settings.xml
Expand Down Expand Up @@ -151,6 +154,7 @@ namespace tvheadend {
bool m_bAutorecApproxTime;
int m_iAutorecMaxDiff;
std::string m_strStreamingProfile;
bool m_bStreamingConflict;
};

}
138 changes: 134 additions & 4 deletions src/tvheadend/Subscription.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ Subscription::Subscription(CHTSPConnection &conn) :
m_weight(SUBSCRIPTION_WEIGHT_NORMAL),
m_id(0),
m_state(SUBSCRIPTION_STOPPED),
m_prevState(SUBSCRIPTION_STOPPED),
m_startTime(time(NULL)),
m_conn(conn)
{
}
Expand Down Expand Up @@ -95,9 +97,19 @@ eSubsriptionState Subscription::GetState() const
return m_state;
}

eSubsriptionState Subscription::GetPrevState() const
{
CLockObject lock(m_mutex);
return m_prevState;
}

void Subscription::SetState(eSubsriptionState state)
{
CLockObject lock(m_mutex);
if (state == m_state)
return;

m_prevState = m_state;
m_state = state;
}

Expand All @@ -113,6 +125,18 @@ void Subscription::SetProfile(const std::string &profile)
m_profile = profile;
}

time_t Subscription::GetStartTime() const
{
CLockObject lock(m_mutex);
return m_startTime;
}

void Subscription::SetStartTime(time_t time)
{
CLockObject lock(m_mutex);
m_startTime = time;
}

void Subscription::SendSubscribe(uint32_t channelId, uint32_t weight, bool restart)
{
/* We don't want to change anything when restarting a subscription */
Expand All @@ -122,6 +146,7 @@ void Subscription::SendSubscribe(uint32_t channelId, uint32_t weight, bool resta
SetWeight(weight);
SetId(GetNextId());
SetSpeed(1000); //set back to normal
SetStartTime(time(NULL)); // now
}

/* Build message */
Expand Down Expand Up @@ -150,6 +175,10 @@ void Subscription::SendSubscribe(uint32_t channelId, uint32_t weight, bool resta
htsmsg_destroy(m);

SetState(SUBSCRIPTION_STARTING);

/* As this might be a pre- posttuning subscription */
UpdateStateFromWeight();

tvhdebug("demux successfully subscribed to channel id %d, subscription id %d", GetChannelId(), GetId());
}

Expand Down Expand Up @@ -232,17 +261,31 @@ void Subscription::SendWeight(uint32_t weight)
}
if (m)
htsmsg_destroy(m);

/* As this might be a pre- posttuning subscription now */
UpdateStateFromWeight();
}

void Subscription::ParseSubscriptionStatus ( htsmsg_t *m )
void Subscription::UpdateStateFromWeight()
{
/* Not for preTuning and postTuning subscriptions */
if (GetWeight() == static_cast<uint32_t>(SUBSCRIPTION_WEIGHT_PRETUNING) ||
GetWeight() == static_cast<uint32_t>(SUBSCRIPTION_WEIGHT_POSTTUNING))
{
SetState(SUBSCRIPTION_PREPOSTTUNING);
return;
}
else if (GetState() == SUBSCRIPTION_PREPOSTTUNING)
{
/* Switched from pre- posttuning to active, initiate a virtual start */
SetState(SUBSCRIPTION_STARTING);
SetStartTime(time(NULL));
}
}

void Subscription::ParseSubscriptionStatus ( htsmsg_t *m )
{
/* Not for preTuning and postTuning subscriptions */
if (GetState() == SUBSCRIPTION_PREPOSTTUNING)
return;

const char *status = htsmsg_get_str(m, "status");

Expand All @@ -264,7 +307,13 @@ void Subscription::ParseSubscriptionStatus ( htsmsg_t *m )
else if (!strcmp("userLimit", error))
SetState(SUBSCRIPTION_USERLIMIT);
else if (!strcmp("noFreeAdapter", error))
SetState(SUBSCRIPTION_NOFREEADAPTER);
{
/* If streaming conflict management enabled */
if (Settings::GetInstance().GetStreamingConflict())
HandleConflict(); // no free adapter, AKA conflict
else
SetState(SUBSCRIPTION_NOFREEADAPTER);
}
else if (!strcmp("tuningFailed", error))
SetState(SUBSCRIPTION_TUNINGFAILED);
else if (!strcmp("userAccess", error))
Expand Down Expand Up @@ -311,6 +360,87 @@ void Subscription::ShowStateNotification(void)
XBMC->QueueNotification(ADDON::QUEUE_WARNING, XBMC->GetLocalizedString(30456));
}

void Subscription::HandleConflict(void)
{
if (GetState() != SUBSCRIPTION_NOFREEADAPTER_HANDLING)
SetState(SUBSCRIPTION_NOFREEADAPTER);

/*
* Conflict case 1: (GetPrevState() == SUBSCRIPTION_RUNNING)
* Subscription was running before, but the adapter got stolen by an other subscription
* Ask user if he wants to continue watching by interrupting an other subscription (weight based)
*
* Conflict case 2: (GetPrevState() == SUBSCRIPTION_STARTING)
* No free adapter found to start this channel from the beginning on
* Ask user if he wants to start watching by interrupting an other subscription (weight based)
* 'DIALOG_NOSTART_DELAY' is to prevent the dialog from popping up when zapping
*/

if (GetPrevState() == SUBSCRIPTION_RUNNING ||
(GetPrevState() == SUBSCRIPTION_STARTING && GetStartTime() + DIALOG_NOSTART_DELAY < time(NULL)))
{
std::thread(&Subscription::ShowConflictDialog, this).detach();
}
}

void Subscription::ShowConflictDialog(void)
{
tvhinfo("demux conflict dialog: open, state: %i, previous state: %i, weight: %i ,subscription id: %i", GetState(), GetPrevState(), GetWeight(), GetId());

if (GetWeight() >= SUBSCRIPTION_WEIGHT_MAX)
return;

/* Save the initial subscription id */
uint32_t initialId = GetId();

/* Make a copy before changing the state */
eSubsriptionState prevState = GetPrevState();

/* Mark this conflict as handling */
SetState(SUBSCRIPTION_NOFREEADAPTER_HANDLING);

/*
* Dialog Heading: TV conflict
* All adapters are in use.
* To keep watching TV, you can interrupt the lowest priority service.
* This can be an active recording or an other TV client.
*
* Interrupt service <--> Do nothing
*/

bool bDialogInterrrupt = GUI->Dialog_YesNo_ShowAndGetInput(
XBMC->GetLocalizedString(30550), XBMC->GetLocalizedString(30551),
XBMC->GetLocalizedString(prevState == SUBSCRIPTION_STARTING ? 30553 : 30552),
XBMC->GetLocalizedString(30554), XBMC->GetLocalizedString(30555), XBMC->GetLocalizedString(30556));

if (bDialogInterrrupt)
{
while (GetWeight() < SUBSCRIPTION_WEIGHT_MAX)
{
/* Channel changed or conflict solved */
if (GetId() != initialId || GetState() != SUBSCRIPTION_NOFREEADAPTER_HANDLING)
break;

/* Gradually increase weight between min and max */
if (GetWeight() < SUBSCRIPTION_WEIGHT_MIN)
SendWeight(SUBSCRIPTION_WEIGHT_MIN);
else if (GetWeight() + SUBSCRIPTION_WEIGHT_STEPSIZE > SUBSCRIPTION_WEIGHT_MAX)
SendWeight(SUBSCRIPTION_WEIGHT_MAX);
else
SendWeight(GetWeight() + SUBSCRIPTION_WEIGHT_STEPSIZE);

XBMC->QueueNotification(ADDON::QUEUE_INFO, XBMC->GetLocalizedString(30557),
(GetWeight()-SUBSCRIPTION_WEIGHT_MIN)/((SUBSCRIPTION_WEIGHT_MAX-SUBSCRIPTION_WEIGHT_MIN)/100)); // Interrupting service... %i%

sleep(1);
}
}

/* Handling done and still not running, set state back to original */
if (GetState() == SUBSCRIPTION_NOFREEADAPTER_HANDLING)
SetState(SUBSCRIPTION_NOFREEADAPTER);
}

uint32_t Subscription::GetNextId()
{
static uint32_t id = 0;
Expand Down
Loading

0 comments on commit e7eda9b

Please sign in to comment.