Skip to content

Commit

Permalink
[mythtv-cmyth] Combine channels with same channum and callsign
Browse files Browse the repository at this point in the history
GetChannels filters out channels with the same channum and callsign.
OpenLiveStream starts a channel by it's channum.

GetSources has been replaced by GetLiveTVRecorderSourceList to query
a pair of recorder ids and source ids for a given channum.
  • Loading branch information
fetzerch committed Dec 20, 2012
1 parent 03c3536 commit 2e365b1
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 87 deletions.
29 changes: 11 additions & 18 deletions addons/pvr.mythtv.cmyth/src/cppmyth/MythDatabase.cpp
Expand Up @@ -108,9 +108,9 @@ ProgramList MythDatabase::GetGuide(time_t starttime, time_t endtime)
return retval;
}

ChannelMap MythDatabase::GetChannels()
ChannelIdMap MythDatabase::GetChannels()
{
ChannelMap retval;
ChannelIdMap retval;
cmyth_chanlist_t channels = NULL;
CMYTH_DB_CALL(channels, channels == NULL, cmyth_mysql_get_chanlist(*m_database_t));
int channelCount = cmyth_chanlist_get_count(channels);
Expand Down Expand Up @@ -138,40 +138,33 @@ ChannelGroupMap MythDatabase::GetChannelGroups()

for (int i = 0; i < channelGroupCount; i++)
{
MythChannelGroup channelGroup;
channelGroup.first = channelGroups[i].name;

// cmyth_mysql_get_channelids_in_group writes the list of all channel IDs to its third parameter (int**)
unsigned long *channelIDs = 0;
int channelCount = cmyth_mysql_get_channelids_in_group(*m_database_t, channelGroups[i].grpid, &channelIDs);
if (channelCount > 0)
{
channelGroup.second = std::vector<int>(channelIDs, channelIDs + channelCount);
retval.insert(std::make_pair(channelGroups[i].name, std::vector<int>(channelIDs, channelIDs + channelCount)));
ref_release(channelIDs);
}
else
channelGroup.second = std::vector<int>();

retval.insert(channelGroup);
{
retval.insert(std::make_pair(channelGroups[i].name, std::vector<int>()));
}
}
ref_release(channelGroups);
return retval;
}

SourceMap MythDatabase::GetSources()
RecorderSourceList MythDatabase::GetLiveTVRecorderSourceList(const CStdString &channum)
{
SourceMap retval;
RecorderSourceList retval;
cmyth_recorder_source_t *recorders = 0;
int recorderCount = 0;
CMYTH_DB_CALL(recorderCount, recorderCount < 0, cmyth_mysql_get_recorder_source_list(*m_database_t, &recorders));
CMYTH_DB_CALL(recorderCount, recorderCount < 0, cmyth_mysql_get_recorder_source_channum(*m_database_t, const_cast<char*>(channum.c_str()), &recorders));

for (int i = 0; i < recorderCount; i++)
for (int i = 0; i < recorderCount; ++i)
{
SourceMap::iterator it = retval.find(recorders[i].sourceid);
if (it != retval.end())
it->second.push_back(recorders[i].recid);
else
retval[recorders[i].sourceid] = std::vector<int>(1, recorders[i].recid);
retval.push_back(std::make_pair(recorders[i].recid, recorders[i].sourceid));
}
ref_release(recorders);
return retval;
Expand Down
12 changes: 7 additions & 5 deletions addons/pvr.mythtv.cmyth/src/cppmyth/MythDatabase.h
Expand Up @@ -39,11 +39,12 @@ template <class T> class MythPointerThreadSafe;
typedef cmyth_program_t MythProgram;
typedef std::vector<MythProgram> ProgramList;

typedef std::map<int, MythChannel> ChannelMap;
typedef std::pair<CStdString, std::vector<int> > MythChannelGroup;
typedef std::map<int, MythChannel> ChannelIdMap;
typedef std::multimap<CStdString, MythChannel> ChannelNumberMap;
typedef std::map<CStdString, std::vector<int> > ChannelGroupMap;

typedef std::map<int, std::vector<int> > SourceMap;
typedef std::vector<std::pair<int, int> > RecorderSourceList;

typedef std::map<int, MythRecordingRule> RecordingRuleMap;

// TODO: Rework MythRecordingProfile
Expand Down Expand Up @@ -72,9 +73,10 @@ class MythDatabase
bool FindProgram(time_t starttime, int channelid, const CStdString &title, MythProgram* pprogram);
ProgramList GetGuide(time_t starttime, time_t endtime);

ChannelMap GetChannels();
ChannelIdMap GetChannels();
ChannelGroupMap GetChannelGroups();
SourceMap GetSources();

RecorderSourceList GetLiveTVRecorderSourceList(const CStdString &channum);

RecordingRuleMap GetRecordingRules();
int AddRecordingRule(const MythRecordingRule &timer);
Expand Down
142 changes: 81 additions & 61 deletions addons/pvr.mythtv.cmyth/src/pvrclient-mythtv.cpp
Expand Up @@ -24,6 +24,7 @@
#include "tools.h"

#include <time.h>
#include <set>

using namespace ADDON;
using namespace PLATFORM;
Expand Down Expand Up @@ -318,27 +319,35 @@ int PVRClientMythTV::GetNumChannels()
if (g_bExtraDebug)
XBMC->Log(LOG_DEBUG, "%s", __FUNCTION__);

// Load channels if necessary
if (m_channels.empty())
m_channels = m_db.GetChannels();
LoadChannelsAndChannelGroups();

return m_channels.size();
return m_channelsById.size();
}

PVR_ERROR PVRClientMythTV::GetChannels(ADDON_HANDLE handle, bool bRadio)
{
if (g_bExtraDebug)
XBMC->Log(LOG_DEBUG, "%s - radio: %i", __FUNCTION__, bRadio);

// Load channels if necessary
if (m_channels.empty())
m_channels = m_db.GetChannels();
LoadChannelsAndChannelGroups();

// Create a set<channum, callsign> to merge channels with same channum and callsign
std::set<std::pair<CStdString, CStdString> > channelIdentifiers;

// Transfer channels of the requested type (radio / tv)
for (ChannelMap::iterator it = m_channels.begin(); it != m_channels.end(); ++it)
for (ChannelIdMap::iterator it = m_channelsById.begin(); it != m_channelsById.end(); ++it)
{
if (it->second.IsRadio() == bRadio && !it->second.IsNull())
{
// Skip channels with same channum and callsign
std::pair<CStdString, CStdString> channelIdentifier = make_pair(it->second.Number(), it->second.Callsign());
if (channelIdentifiers.find(channelIdentifier) != channelIdentifiers.end())
{
XBMC->Log(LOG_DEBUG, "%s - skipping channel: %d", __FUNCTION__, it->second.ID());
continue;
}
channelIdentifiers.insert(channelIdentifier);

PVR_CHANNEL tag;
memset(&tag, 0, sizeof(PVR_CHANNEL));

Expand Down Expand Up @@ -371,9 +380,7 @@ int PVRClientMythTV::GetChannelGroupsAmount()
if (g_bExtraDebug)
XBMC->Log(LOG_DEBUG, "%s", __FUNCTION__);

// Load channel groups if necessary
if (m_channelGroups.empty())
m_channelGroups = m_db.GetChannelGroups();
LoadChannelsAndChannelGroups();

return m_channelGroups.size();
}
Expand All @@ -383,13 +390,7 @@ PVR_ERROR PVRClientMythTV::GetChannelGroups(ADDON_HANDLE handle, bool bRadio)
if (g_bExtraDebug)
XBMC->Log(LOG_DEBUG, "%s - radio: %i", __FUNCTION__, bRadio);

// Load channel groups if necessary
if (m_channelGroups.empty())
m_channelGroups = m_db.GetChannelGroups();

// Load channels if necessary
if (m_channels.empty())
m_channels = m_db.GetChannels();
LoadChannelsAndChannelGroups();

// Transfer channel groups of the given type (radio / tv)
for (ChannelGroupMap::iterator channelGroupsIt = m_channelGroups.begin(); channelGroupsIt != m_channelGroups.end(); ++channelGroupsIt)
Expand All @@ -403,8 +404,8 @@ PVR_ERROR PVRClientMythTV::GetChannelGroups(ADDON_HANDLE handle, bool bRadio)
// Only add the group if we have at least one channel of the correct type
for (std::vector<int>::iterator channelGroupIt = channelGroupsIt->second.begin(); channelGroupIt != channelGroupsIt->second.end(); ++channelGroupIt)
{
ChannelMap::iterator channelIt = m_channels.find(*channelGroupIt);
if (channelIt != m_channels.end() && channelIt->second.IsRadio() == bRadio)
ChannelIdMap::iterator channelIt = m_channelsById.find(*channelGroupIt);
if (channelIt != m_channelsById.end() && channelIt->second.IsRadio() == bRadio)
{
PVR->TransferChannelGroup(handle, &tag);
break;
Expand All @@ -423,26 +424,21 @@ PVR_ERROR PVRClientMythTV::GetChannelGroupMembers(ADDON_HANDLE handle, const PVR
if (g_bExtraDebug)
XBMC->Log(LOG_DEBUG, "%s - group: %s", __FUNCTION__, group.strGroupName);

// Make sure the channel group exists
if (m_channelGroups.empty())
m_channelGroups = m_db.GetChannelGroups();
LoadChannelsAndChannelGroups();

ChannelGroupMap::iterator channelGroupsIt = m_channelGroups.find(group.strGroupName);
if (channelGroupsIt == m_channelGroups.end())
{
XBMC->Log(LOG_ERROR,"%s - Channel group not found", __FUNCTION__);
return PVR_ERROR_INVALID_PARAMETERS;
}

// Load channels if necessary
if (m_channels.empty())
m_channels = m_db.GetChannels();

// Transfer the channel group members for the requested group
int channelNumber = 0;
for (std::vector<int>::iterator channelGroupIt = channelGroupsIt->second.begin(); channelGroupIt != channelGroupsIt->second.end(); ++channelGroupIt)
{
ChannelMap::iterator channelIt = m_channels.find(*channelGroupIt);
if (channelIt != m_channels.end() && channelIt->second.IsRadio() == group.bIsRadio)
ChannelIdMap::iterator channelIt = m_channelsById.find(*channelGroupIt);
if (channelIt != m_channelsById.end() && channelIt->second.IsRadio() == group.bIsRadio)
{
PVR_CHANNEL_GROUP_MEMBER tag;
memset(&tag, 0, sizeof(PVR_CHANNEL_GROUP_MEMBER));
Expand All @@ -460,6 +456,19 @@ PVR_ERROR PVRClientMythTV::GetChannelGroupMembers(ADDON_HANDLE handle, const PVR
return PVR_ERROR_NO_ERROR;
}

void PVRClientMythTV::LoadChannelsAndChannelGroups()
{
if (!m_channelsById.empty())
return;

m_channelsById = m_db.GetChannels();

for (ChannelIdMap::iterator channelIt = m_channelsById.begin(); channelIt != m_channelsById.end(); ++channelIt)
m_channelsByNumber.insert(std::make_pair(channelIt->second.Number(), channelIt->second));

m_channelGroups = m_db.GetChannelGroups();
}

int PVRClientMythTV::GetRecordingsAmount(void)
{
int res = 0;
Expand Down Expand Up @@ -1122,8 +1131,8 @@ void PVRClientMythTV::PVRtoMythRecordingRule(const PVR_TIMER timer, MythRecordin
rule.SetInactive(timer.state == PVR_TIMER_STATE_ABORTED || timer.state == PVR_TIMER_STATE_CANCELLED);
rule.SetPriority(timer.iPriority);

ChannelMap::iterator channelIt = m_channels.find(timer.iClientChannelUid);
if (channelIt != m_channels.end())
ChannelIdMap::iterator channelIt = m_channelsById.find(timer.iClientChannelUid);
if (channelIt != m_channelsById.end())
rule.SetCallsign(channelIt->second.Callsign());

if (timer.bIsRepeating)
Expand Down Expand Up @@ -1238,25 +1247,7 @@ bool PVRClientMythTV::OpenLiveStream(const PVR_CHANNEL &channel)
CLockObject lock(m_lock);
if (m_rec.IsNull())
{
// Make sure the channel exists
if (m_channels.empty())
m_channels = m_db.GetChannels();
ChannelMap::iterator channelIt = m_channels.find(channel.iUniqueId);
if (channelIt == m_channels.end())
{
XBMC->Log(LOG_ERROR,"%s - Channel not found", __FUNCTION__);
return false;
}

// Make sure the channel sources exists
if (m_sources.empty())
m_sources = m_db.GetSources();
SourceMap::iterator sourcesIt = m_sources.find(channelIt->second.SourceID());
if (sourcesIt == m_sources.end())
{
XBMC->Log(LOG_ERROR,"%s - Channel sources not found", __FUNCTION__);
return false;
}
LoadChannelsAndChannelGroups();

// Suspend fileOps to avoid connection hang
if (m_fileOps->IsRunning())
Expand All @@ -1266,26 +1257,55 @@ bool PVRClientMythTV::OpenLiveStream(const PVR_CHANNEL &channel)
if (m_pEventHandler)
m_pEventHandler->EnablePlayback();

for (std::vector<int>::iterator it = sourcesIt->second.begin(); it != sourcesIt->second.end(); ++it)
// First we have to get the channum of the selected channel
// Due to the merged view (same channum+callsign) this might not yet be the preferred channel on the preferred source to switch to
ChannelIdMap::iterator channelByIdIt = m_channelsById.find(channel.iUniqueId);
if (channelByIdIt == m_channelsById.end())
{
XBMC->Log(LOG_ERROR,"%s - Channel not found", __FUNCTION__);
return false;
}

// Retreive a list of recorders and sources for the given channum (ordered by LiveTV priority)
RecorderSourceList recorderSourceList = m_db.GetLiveTVRecorderSourceList(channelByIdIt->second.Number());
for (RecorderSourceList::iterator recorderSourceIt = recorderSourceList.begin(); recorderSourceIt != recorderSourceList.end(); ++recorderSourceIt)
{
m_rec = m_con.GetRecorder(*it);
if (m_rec.ID() > 0 && !m_rec.IsRecording() && m_rec.IsTunable(channelIt->second))
// Get the recorder from the recorder source list and check if it's available
m_rec = m_con.GetRecorder(recorderSourceIt->first);
if (m_rec.ID() <= 0)
{
if (g_bExtraDebug)
XBMC->Log(LOG_DEBUG,"%s: Opening new recorder %i", __FUNCTION__, m_rec.ID());
XBMC->Log(LOG_ERROR,"%s - Recorder not found: %d", __FUNCTION__, recorderSourceIt->first);
continue;
}
if (m_rec.IsRecording())
{
XBMC->Log(LOG_ERROR,"%s - Recorder is busy: %d", __FUNCTION__, recorderSourceIt->first);
continue;
}

if (m_pEventHandler)
// Get the channel that matches the channum and the source id
std::pair<ChannelNumberMap::iterator, ChannelNumberMap::iterator> channelsByNumber = m_channelsByNumber.equal_range(channelByIdIt->second.Number());
for (ChannelNumberMap::iterator channelByNumberIt = channelsByNumber.first; channelByNumberIt != channelsByNumber.second; ++channelByNumberIt)
{
if ((*channelByNumberIt).second.SourceID() == recorderSourceIt->second)
{
m_pEventHandler->SetRecorder(m_rec);
// Check if the recorder is able to tune to that channel (virtual recorders might be locked to a multiplex ID)
if (m_rec.IsTunable((*channelByNumberIt).second))
{
XBMC->Log(LOG_DEBUG,"%s: Opening new recorder %i", __FUNCTION__, m_rec.ID());

if (m_pEventHandler)
m_pEventHandler->SetRecorder(m_rec);

if (m_rec.SpawnLiveTV((*channelByNumberIt).second))
return true;
}
}
if (m_rec.SpawnLiveTV(channelIt->second))
return true;
}

m_rec = MythRecorder();
if (m_pEventHandler)
{
m_pEventHandler->SetRecorder(m_rec); // Redundant
}
}

// Disable playback mode: Allow all
Expand Down
6 changes: 3 additions & 3 deletions addons/pvr.mythtv.cmyth/src/pvrclient-mythtv.h
Expand Up @@ -140,13 +140,13 @@ class PVRClientMythTV
ProgramList m_EPG;

// Channels
ChannelMap m_channels;
SourceMap m_sources;
ChannelIdMap m_channelsById;
ChannelNumberMap m_channelsByNumber;
ChannelGroupMap m_channelGroups;
void LoadChannelsAndChannelGroups();

// Recordings
ProgramInfoMap m_recordings;

PLATFORM::CMutex m_recordingsLock;
void EventUpdateRecordings();
void ForceUpdateRecording(ProgramInfoMap::iterator it);
Expand Down

0 comments on commit 2e365b1

Please sign in to comment.