clang code cleanup

This commit is contained in:
Alwin Esch 2020-06-07 16:56:16 +02:00
parent 76259ba352
commit fc5b149f2c
9 changed files with 1474 additions and 1253 deletions

88
.clang-format Normal file
View File

@ -0,0 +1,88 @@
---
# BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: DontAlign
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: InlineOnly
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Allman
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 100
CommentPragmas: '^ IWYU pragma:'
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<[a-z0-9_]+>$'
Priority: 3
- Regex: '^<(assert|complex|ctype|errno|fenv|float|inttypes|iso646|limits|locale|math|setjmp|signal|stdalign|stdarg|stdatomic|stdbool|stddef|stdint|stdio|stdlib|stdnoreturn|string|tgmath|threads|time|uchar|wchar|wctype)\.h>$'
Priority: 3
- Regex: '^<'
Priority: 3
- Regex: '^["<](kodi|p8-platform)\/.*\.h[">]$'
Priority: 2
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '$'
IndentCaseLabels: true
IndentWidth: 2
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 2
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60000
PointerAlignment: Left
ReflowComments: false
SortIncludes: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 8
UseTab: Never
...

View File

@ -8,12 +8,11 @@
* *
*/ */
#include <sstream> #include "OctonetData.h"
#include <string>
#include <json/json.h> #include <json/json.h>
#include <sstream>
#include "OctonetData.h" #include <string>
#ifdef __WINDOWS__ #ifdef __WINDOWS__
#define timegm _mkgmtime #define timegm _mkgmtime
@ -23,341 +22,361 @@ using namespace ADDON;
OctonetData::OctonetData() OctonetData::OctonetData()
{ {
serverAddress = octonetAddress; m_serverAddress = octonetAddress;
channels.clear(); m_channels.clear();
groups.clear(); m_groups.clear();
lastEpgLoad = 0; m_lastEpgLoad = 0;
if (!loadChannelList()) if (!LoadChannelList())
libKodi->QueueNotification(QUEUE_ERROR, libKodi->GetLocalizedString(30001), channels.size()); libKodi->QueueNotification(QUEUE_ERROR, libKodi->GetLocalizedString(30001), m_channels.size());
} }
OctonetData::~OctonetData(void) OctonetData::~OctonetData(void)
{ {
channels.clear(); m_channels.clear();
groups.clear(); m_groups.clear();
} }
int64_t OctonetData::parseID(std::string id) int64_t OctonetData::ParseID(std::string id)
{ {
std::hash<std::string> hash_fn; std::hash<std::string> hash_fn;
int64_t nativeId = hash_fn(id); int64_t nativeId = hash_fn(id);
return nativeId; return nativeId;
} }
bool OctonetData::loadChannelList() bool OctonetData::LoadChannelList()
{ {
std::string jsonContent; std::string jsonContent;
void *f = libKodi->OpenFile(("http://" + serverAddress + "/channellist.lua?select=json").c_str(), 0); void* f =
if (!f) libKodi->OpenFile(("http://" + m_serverAddress + "/channellist.lua?select=json").c_str(), 0);
return false; if (!f)
return false;
char buf[1024]; char buf[1024];
while (int read = libKodi->ReadFile(f, buf, 1024)) while (int read = libKodi->ReadFile(f, buf, 1024))
jsonContent.append(buf, read); jsonContent.append(buf, read);
libKodi->CloseFile(f); libKodi->CloseFile(f);
Json::Value root; Json::Value root;
Json::Reader reader; Json::Reader reader;
if (!reader.parse(jsonContent, root, false)) if (!reader.parse(jsonContent, root, false))
return false; return false;
const Json::Value groupList = root["GroupList"]; const Json::Value groupList = root["GroupList"];
for (unsigned int i = 0; i < groupList.size(); i++) { for (unsigned int i = 0; i < groupList.size(); i++)
const Json::Value channelList = groupList[i]["ChannelList"]; {
OctonetGroup group; const Json::Value channelList = groupList[i]["ChannelList"];
OctonetGroup group;
group.name = groupList[i]["Title"].asString(); group.name = groupList[i]["Title"].asString();
group.radio = group.name.compare(0, 5, "Radio") ? false : true; group.radio = group.name.compare(0, 5, "Radio") ? false : true;
for (unsigned int j = 0; j < channelList.size(); j++) { for (unsigned int j = 0; j < channelList.size(); j++)
const Json::Value channel = channelList[j]; {
OctonetChannel chan; const Json::Value channel = channelList[j];
OctonetChannel chan;
chan.name = channel["Title"].asString(); chan.name = channel["Title"].asString();
chan.url = "rtsp://" + serverAddress + "/" + channel["Request"].asString(); chan.url = "rtsp://" + m_serverAddress + "/" + channel["Request"].asString();
chan.radio = group.radio; chan.radio = group.radio;
chan.nativeId = parseID(channel["ID"].asString()); chan.nativeId = ParseID(channel["ID"].asString());
chan.id = 1000 + channels.size(); chan.id = 1000 + m_channels.size();
group.members.push_back(channels.size()); group.members.push_back(m_channels.size());
channels.push_back(chan); m_channels.push_back(chan);
} }
groups.push_back(group); m_groups.push_back(group);
} }
return true; return true;
} }
OctonetChannel* OctonetData::findChannel(int64_t nativeId) OctonetChannel* OctonetData::FindChannel(int64_t nativeId)
{ {
std::vector<OctonetChannel>::iterator it; std::vector<OctonetChannel>::iterator it;
for (it = channels.begin(); it < channels.end(); ++it) { for (it = m_channels.begin(); it < m_channels.end(); ++it)
if (it->nativeId == nativeId) {
return &*it; if (it->nativeId == nativeId)
} return &*it;
}
return NULL; return NULL;
} }
time_t OctonetData::parseDateTime(std::string date) time_t OctonetData::ParseDateTime(std::string date)
{ {
struct tm timeinfo; struct tm timeinfo;
memset(&timeinfo, 0, sizeof(timeinfo)); memset(&timeinfo, 0, sizeof(timeinfo));
if (date.length() > 8) { if (date.length() > 8)
sscanf(date.c_str(), "%04d-%02d-%02dT%02d:%02d:%02dZ", {
&timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday, sscanf(date.c_str(), "%04d-%02d-%02dT%02d:%02d:%02dZ", &timeinfo.tm_year, &timeinfo.tm_mon,
&timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec); &timeinfo.tm_mday, &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec);
timeinfo.tm_mon -= 1; timeinfo.tm_mon -= 1;
timeinfo.tm_year -= 1900; timeinfo.tm_year -= 1900;
} else { }
sscanf(date.c_str(), "%02d:%02d:%02d", else
&timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec); {
timeinfo.tm_year = 70; // unix timestamps start 1970 sscanf(date.c_str(), "%02d:%02d:%02d", &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec);
timeinfo.tm_mday = 1; timeinfo.tm_year = 70; // unix timestamps start 1970
} timeinfo.tm_mday = 1;
}
timeinfo.tm_isdst = -1; timeinfo.tm_isdst = -1;
return timegm(&timeinfo); return timegm(&timeinfo);
} }
bool OctonetData::loadEPG(void) bool OctonetData::LoadEPG(void)
{ {
/* Reload at most every 30 seconds */ /* Reload at most every 30 seconds */
if (lastEpgLoad + 30 > time(NULL)) if (m_lastEpgLoad + 30 > time(NULL))
return false; return false;
std::string jsonContent; std::string jsonContent;
void *f = libKodi->OpenFile(("http://" + serverAddress + "/epg.lua?;#|encoding=gzip").c_str(), 0); void* f = libKodi->OpenFile(("http://" + m_serverAddress + "/epg.lua?;#|encoding=gzip").c_str(), 0);
if (!f) if (!f)
return false; return false;
char buf[1024]; char buf[1024];
while (int read = libKodi->ReadFile(f, buf, 1024)) while (int read = libKodi->ReadFile(f, buf, 1024))
jsonContent.append(buf, read); jsonContent.append(buf, read);
libKodi->CloseFile(f); libKodi->CloseFile(f);
Json::Value root; Json::Value root;
Json::Reader reader; Json::Reader reader;
if (!reader.parse(jsonContent, root, false)) if (!reader.parse(jsonContent, root, false))
return false; return false;
const Json::Value eventList = root["EventList"]; const Json::Value eventList = root["EventList"];
OctonetChannel *channel = NULL; OctonetChannel* channel = NULL;
for (unsigned int i = 0; i < eventList.size(); i++) { for (unsigned int i = 0; i < eventList.size(); i++)
const Json::Value event = eventList[i]; {
OctonetEpgEntry entry; const Json::Value event = eventList[i];
OctonetEpgEntry entry;
entry.start = parseDateTime(event["Time"].asString()); entry.start = ParseDateTime(event["Time"].asString());
entry.end = entry.start + parseDateTime(event["Duration"].asString()); entry.end = entry.start + ParseDateTime(event["Duration"].asString());
entry.title = event["Name"].asString(); entry.title = event["Name"].asString();
entry.subtitle = event["Text"].asString(); entry.subtitle = event["Text"].asString();
std::string channelId = event["ID"].asString(); std::string channelId = event["ID"].asString();
std::string epgId = channelId.substr(channelId.rfind(":") + 1); std::string epgId = channelId.substr(channelId.rfind(":") + 1);
channelId = channelId.substr(0, channelId.rfind(":")); channelId = channelId.substr(0, channelId.rfind(":"));
entry.channelId = parseID(channelId); entry.channelId = ParseID(channelId);
entry.id = atoi(epgId.c_str()); entry.id = atoi(epgId.c_str());
if (channel == NULL || channel->nativeId != entry.channelId) if (channel == NULL || channel->nativeId != entry.channelId)
channel = findChannel(entry.channelId); channel = FindChannel(entry.channelId);
if (channel == NULL) { if (channel == NULL)
libKodi->Log(LOG_ERROR, "EPG for unknown channel."); {
continue; libKodi->Log(LOG_ERROR, "EPG for unknown channel.");
} continue;
}
channel->epg.push_back(entry); channel->epg.push_back(entry);
} }
lastEpgLoad = time(NULL); m_lastEpgLoad = time(NULL);
return true; return true;
} }
void *OctonetData::Process(void) void* OctonetData::Process(void)
{ {
return NULL; return NULL;
} }
int OctonetData::getChannelCount(void) int OctonetData::getChannelCount(void)
{ {
return channels.size(); return m_channels.size();
} }
PVR_ERROR OctonetData::getChannels(ADDON_HANDLE handle, bool bRadio) PVR_ERROR OctonetData::getChannels(ADDON_HANDLE handle, bool bRadio)
{ {
for (unsigned int i = 0; i < channels.size(); i++) for (unsigned int i = 0; i < m_channels.size(); i++)
{ {
OctonetChannel &channel = channels.at(i); OctonetChannel& channel = m_channels.at(i);
if (channel.radio == bRadio) if (channel.radio == bRadio)
{ {
PVR_CHANNEL chan; PVR_CHANNEL chan;
memset(&chan, 0, sizeof(PVR_CHANNEL)); memset(&chan, 0, sizeof(PVR_CHANNEL));
chan.iUniqueId = channel.id; chan.iUniqueId = channel.id;
chan.bIsRadio = channel.radio; chan.bIsRadio = channel.radio;
chan.iChannelNumber = i; chan.iChannelNumber = i;
strncpy(chan.strChannelName, channel.name.c_str(), strlen(channel.name.c_str())); strncpy(chan.strChannelName, channel.name.c_str(), strlen(channel.name.c_str()));
strcpy(chan.strInputFormat, "video/x-mpegts"); strcpy(chan.strInputFormat, "video/x-mpegts");
chan.bIsHidden = false; chan.bIsHidden = false;
pvr->TransferChannelEntry(handle, &chan); pvr->TransferChannelEntry(handle, &chan);
} }
} }
return PVR_ERROR_NO_ERROR; return PVR_ERROR_NO_ERROR;
} }
PVR_ERROR OctonetData::getEPG(ADDON_HANDLE handle, int iChannelUid, time_t start, time_t end) PVR_ERROR OctonetData::getEPG(ADDON_HANDLE handle, int iChannelUid, time_t start, time_t end)
{ {
for (unsigned int i = 0; i < channels.size(); i++) for (unsigned int i = 0; i < m_channels.size(); i++)
{ {
OctonetChannel &chan = channels.at(i); OctonetChannel& chan = m_channels.at(i);
if (iChannelUid != chan.id) if (iChannelUid != chan.id)
continue; continue;
if(chan.epg.empty()) { if (chan.epg.empty())
loadEPG(); {
} LoadEPG();
}
// FIXME: Check if reload is needed!? // FIXME: Check if reload is needed!?
std::vector<OctonetEpgEntry>::iterator it; std::vector<OctonetEpgEntry>::iterator it;
time_t last_end = 0; time_t last_end = 0;
for (it = chan.epg.begin(); it != chan.epg.end(); ++it) { for (it = chan.epg.begin(); it != chan.epg.end(); ++it)
if (it->end > last_end) {
last_end = it->end; if (it->end > last_end)
last_end = it->end;
if (it->end < start || it->start > end) { if (it->end < start || it->start > end)
continue; {
} continue;
}
EPG_TAG entry; EPG_TAG entry;
memset(&entry, 0, sizeof(EPG_TAG)); memset(&entry, 0, sizeof(EPG_TAG));
entry.iSeriesNumber = EPG_TAG_INVALID_SERIES_EPISODE; entry.iSeriesNumber = EPG_TAG_INVALID_SERIES_EPISODE;
entry.iEpisodeNumber = EPG_TAG_INVALID_SERIES_EPISODE; entry.iEpisodeNumber = EPG_TAG_INVALID_SERIES_EPISODE;
entry.iEpisodePartNumber = EPG_TAG_INVALID_SERIES_EPISODE; entry.iEpisodePartNumber = EPG_TAG_INVALID_SERIES_EPISODE;
entry.iUniqueChannelId = chan.id; entry.iUniqueChannelId = chan.id;
entry.iUniqueBroadcastId = it->id; entry.iUniqueBroadcastId = it->id;
entry.strTitle = it->title.c_str(); entry.strTitle = it->title.c_str();
entry.strPlotOutline = it->subtitle.c_str(); entry.strPlotOutline = it->subtitle.c_str();
entry.startTime = it->start; entry.startTime = it->start;
entry.endTime = it->end; entry.endTime = it->end;
pvr->TransferEpgEntry(handle, &entry); pvr->TransferEpgEntry(handle, &entry);
} }
if (last_end < end) if (last_end < end)
loadEPG(); LoadEPG();
for (it = chan.epg.begin(); it != chan.epg.end(); ++it) { for (it = chan.epg.begin(); it != chan.epg.end(); ++it)
if (it->end < start || it->start > end) { {
continue; if (it->end < start || it->start > end)
} {
continue;
}
EPG_TAG entry; EPG_TAG entry;
memset(&entry, 0, sizeof(EPG_TAG)); memset(&entry, 0, sizeof(EPG_TAG));
entry.iSeriesNumber = EPG_TAG_INVALID_SERIES_EPISODE; entry.iSeriesNumber = EPG_TAG_INVALID_SERIES_EPISODE;
entry.iEpisodeNumber = EPG_TAG_INVALID_SERIES_EPISODE; entry.iEpisodeNumber = EPG_TAG_INVALID_SERIES_EPISODE;
entry.iEpisodePartNumber = EPG_TAG_INVALID_SERIES_EPISODE; entry.iEpisodePartNumber = EPG_TAG_INVALID_SERIES_EPISODE;
entry.iUniqueChannelId = chan.id; entry.iUniqueChannelId = chan.id;
entry.iUniqueBroadcastId = it->id; entry.iUniqueBroadcastId = it->id;
entry.strTitle = it->title.c_str(); entry.strTitle = it->title.c_str();
entry.strPlotOutline = it->subtitle.c_str(); entry.strPlotOutline = it->subtitle.c_str();
entry.startTime = it->start; entry.startTime = it->start;
entry.endTime = it->end; entry.endTime = it->end;
pvr->TransferEpgEntry(handle, &entry); pvr->TransferEpgEntry(handle, &entry);
} }
} }
return PVR_ERROR_NO_ERROR; return PVR_ERROR_NO_ERROR;
} }
const std::string& OctonetData::getUrl(int id) const { const std::string& OctonetData::GetUrl(int id) const
for(std::vector<OctonetChannel>::const_iterator iter = channels.begin(); iter != channels.end(); ++iter) { {
if(iter->id == id) { for (std::vector<OctonetChannel>::const_iterator iter = m_channels.begin(); iter != m_channels.end();
return iter->url; ++iter)
} {
} if (iter->id == id)
{
return iter->url;
}
}
return channels[0].url; return m_channels[0].url;
} }
const std::string& OctonetData::getName(int id) const { const std::string& OctonetData::GetName(int id) const
for(std::vector<OctonetChannel>::const_iterator iter = channels.begin(); iter != channels.end(); ++iter) { {
if(iter->id == id) { for (std::vector<OctonetChannel>::const_iterator iter = m_channels.begin(); iter != m_channels.end();
return iter->name; ++iter)
} {
} if (iter->id == id)
{
return iter->name;
}
}
return channels[0].name; return m_channels[0].name;
} }
int OctonetData::getGroupCount(void) int OctonetData::getGroupCount(void)
{ {
return groups.size(); return m_groups.size();
} }
PVR_ERROR OctonetData::getGroups(ADDON_HANDLE handle, bool bRadio) PVR_ERROR OctonetData::getGroups(ADDON_HANDLE handle, bool bRadio)
{ {
for (unsigned int i = 0; i < groups.size(); i++) for (unsigned int i = 0; i < m_groups.size(); i++)
{ {
OctonetGroup &group = groups.at(i); OctonetGroup& group = m_groups.at(i);
if (group.radio == bRadio) if (group.radio == bRadio)
{ {
PVR_CHANNEL_GROUP g; PVR_CHANNEL_GROUP g;
memset(&g, 0, sizeof(PVR_CHANNEL_GROUP)); memset(&g, 0, sizeof(PVR_CHANNEL_GROUP));
g.iPosition = 0; g.iPosition = 0;
g.bIsRadio = group.radio; g.bIsRadio = group.radio;
strncpy(g.strGroupName, group.name.c_str(), strlen(group.name.c_str())); strncpy(g.strGroupName, group.name.c_str(), strlen(group.name.c_str()));
pvr->TransferChannelGroup(handle, &g); pvr->TransferChannelGroup(handle, &g);
} }
} }
return PVR_ERROR_NO_ERROR; return PVR_ERROR_NO_ERROR;
} }
PVR_ERROR OctonetData::getGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group) PVR_ERROR OctonetData::getGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP& group)
{ {
OctonetGroup *g = findGroup(group.strGroupName); OctonetGroup* g = FindGroup(group.strGroupName);
if (g == NULL) if (g == NULL)
return PVR_ERROR_UNKNOWN; return PVR_ERROR_UNKNOWN;
for (unsigned int i = 0; i < g->members.size(); i++) for (unsigned int i = 0; i < g->members.size(); i++)
{ {
OctonetChannel &channel = channels.at(g->members[i]); OctonetChannel& channel = m_channels.at(g->members[i]);
PVR_CHANNEL_GROUP_MEMBER m; PVR_CHANNEL_GROUP_MEMBER m;
memset(&m, 0, sizeof(PVR_CHANNEL_GROUP_MEMBER)); memset(&m, 0, sizeof(PVR_CHANNEL_GROUP_MEMBER));
strncpy(m.strGroupName, group.strGroupName, strlen(group.strGroupName)); strncpy(m.strGroupName, group.strGroupName, strlen(group.strGroupName));
m.iChannelUniqueId = channel.id; m.iChannelUniqueId = channel.id;
m.iChannelNumber = channel.id; m.iChannelNumber = channel.id;
pvr->TransferChannelGroupMember(handle, &m); pvr->TransferChannelGroupMember(handle, &m);
} }
return PVR_ERROR_NO_ERROR; return PVR_ERROR_NO_ERROR;
} }
OctonetGroup* OctonetData::findGroup(const std::string &name) OctonetGroup* OctonetData::FindGroup(const std::string& name)
{ {
for (unsigned int i = 0; i < groups.size(); i++) for (unsigned int i = 0; i < m_groups.size(); i++)
{ {
if (groups.at(i).name == name) if (m_groups.at(i).name == name)
return &groups.at(i); return &m_groups.at(i);
} }
return NULL; return NULL;
} }

View File

@ -10,71 +10,71 @@
#pragma once #pragma once
#include <vector> #include "client.h"
#include "p8-platform/threads/threads.h" #include "p8-platform/threads/threads.h"
#include "client.h"
#include <vector>
struct OctonetEpgEntry struct OctonetEpgEntry
{ {
int64_t channelId; int64_t channelId;
time_t start; time_t start;
time_t end; time_t end;
int id; int id;
std::string title; std::string title;
std::string subtitle; std::string subtitle;
}; };
struct OctonetChannel struct OctonetChannel
{ {
int64_t nativeId; int64_t nativeId;
std::string name; std::string name;
std::string url; std::string url;
bool radio; bool radio;
int id; int id;
std::vector<OctonetEpgEntry> epg; std::vector<OctonetEpgEntry> epg;
}; };
struct OctonetGroup struct OctonetGroup
{ {
std::string name; std::string name;
bool radio; bool radio;
std::vector<int> members; std::vector<int> members;
}; };
class OctonetData : public P8PLATFORM::CThread class OctonetData : public P8PLATFORM::CThread
{ {
public: public:
OctonetData(void); OctonetData(void);
virtual ~OctonetData(void); virtual ~OctonetData(void);
virtual int getChannelCount(void); virtual int getChannelCount(void);
virtual PVR_ERROR getChannels(ADDON_HANDLE handle, bool bRadio); virtual PVR_ERROR getChannels(ADDON_HANDLE handle, bool bRadio);
virtual int getGroupCount(void); virtual int getGroupCount(void);
virtual PVR_ERROR getGroups(ADDON_HANDLE handle, bool bRadio); virtual PVR_ERROR getGroups(ADDON_HANDLE handle, bool bRadio);
virtual PVR_ERROR getGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group); virtual PVR_ERROR getGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP& group);
virtual PVR_ERROR getEPG(ADDON_HANDLE handle, int iChannelUid, time_t start, time_t end); virtual PVR_ERROR getEPG(ADDON_HANDLE handle, int iChannelUid, time_t start, time_t end);
const std::string& getUrl(int id) const; const std::string& GetUrl(int id) const;
const std::string& getName(int id) const; const std::string& GetName(int id) const;
protected: protected:
virtual bool loadChannelList(void); void* Process(void) override;
virtual bool loadEPG(void);
virtual OctonetGroup* findGroup(const std::string &name);
virtual void *Process(void); bool LoadChannelList(void);
bool LoadEPG(void);
OctonetGroup* FindGroup(const std::string& name);
OctonetChannel* FindChannel(int64_t nativeId);
time_t ParseDateTime(std::string date);
int64_t ParseID(std::string id);
OctonetChannel* findChannel(int64_t nativeId); private:
time_t parseDateTime(std::string date); std::string m_serverAddress;
int64_t parseID(std::string id); std::vector<OctonetChannel> m_channels;
std::vector<OctonetGroup> m_groups;
private: time_t m_lastEpgLoad;
std::string serverAddress;
std::vector<OctonetChannel> channels;
std::vector<OctonetGroup> groups;
time_t lastEpgLoad;
}; };

View File

@ -6,12 +6,15 @@
* See LICENSE.md for more information. * See LICENSE.md for more information.
*/ */
#include "kodi/libXBMC_addon.h"
#include <string>
#include "p8-platform/os.h"
#include "client.h"
#include "Socket.h" #include "Socket.h"
#include "client.h"
#include "kodi/libXBMC_addon.h"
#include "p8-platform/os.h"
#include <cstdio> #include <cstdio>
#include <string>
using namespace std; using namespace std;
using namespace ADDON; using namespace ADDON;
@ -22,28 +25,31 @@ namespace OCTO
/* Master defines for client control */ /* Master defines for client control */
#define RECEIVE_TIMEOUT 6 //sec #define RECEIVE_TIMEOUT 6 //sec
Socket::Socket(const enum SocketFamily family, const enum SocketDomain domain, const enum SocketType type, const enum SocketProtocol protocol) Socket::Socket(const enum SocketFamily family,
const enum SocketDomain domain,
const enum SocketType type,
const enum SocketProtocol protocol)
{ {
_sd = INVALID_SOCKET; m_sd = INVALID_SOCKET;
_family = family; m_family = family;
_domain = domain; m_domain = domain;
_type = type; m_type = type;
_protocol = protocol; m_protocol = protocol;
_port = 0; m_port = 0;
memset (&_sockaddr, 0, sizeof( _sockaddr ) ); memset(&m_sockaddr, 0, sizeof(m_sockaddr));
} }
Socket::Socket() Socket::Socket()
{ {
// Default constructor, default settings // Default constructor, default settings
_sd = INVALID_SOCKET; m_sd = INVALID_SOCKET;
_family = af_inet; m_family = af_inet;
_domain = pf_inet; m_domain = pf_inet;
_type = sock_stream; m_type = sock_stream;
_protocol = tcp; m_protocol = tcp;
_port = 0; m_port = 0;
memset (&_sockaddr, 0, sizeof( _sockaddr ) ); memset(&m_sockaddr, 0, sizeof(m_sockaddr));
} }
@ -55,7 +61,7 @@ Socket::~Socket()
bool Socket::setHostname(const std::string& host) bool Socket::setHostname(const std::string& host)
{ {
_hostname = host; m_hostname = host;
return true; return true;
} }
@ -63,9 +69,9 @@ bool Socket::close()
{ {
if (is_valid()) if (is_valid())
{ {
if (_sd != SOCKET_ERROR) if (m_sd != SOCKET_ERROR)
closesocket(_sd); closesocket(m_sd);
_sd = INVALID_SOCKET; m_sd = INVALID_SOCKET;
return true; return true;
} }
return false; return false;
@ -75,7 +81,7 @@ bool Socket::create()
{ {
close(); close();
if(!osInit()) if (!osInit())
{ {
return false; return false;
} }
@ -84,25 +90,25 @@ bool Socket::create()
} }
bool Socket::bind ( const unsigned short port ) bool Socket::bind(const unsigned short port)
{ {
if (is_valid()) if (is_valid())
{ {
close(); close();
} }
_sd = socket(_family, _type, _protocol); m_sd = socket(m_family, m_type, m_protocol);
_port = port; m_port = port;
_sockaddr.sin_family = (sa_family_t) _family; m_sockaddr.sin_family = (sa_family_t)m_family;
_sockaddr.sin_addr.s_addr = INADDR_ANY; //listen to all m_sockaddr.sin_addr.s_addr = INADDR_ANY; //listen to all
_sockaddr.sin_port = htons( _port ); m_sockaddr.sin_port = htons(m_port);
int bind_return = ::bind(_sd, (sockaddr*)(&_sockaddr), sizeof(_sockaddr)); int bind_return = ::bind(m_sd, (sockaddr*)(&m_sockaddr), sizeof(m_sockaddr));
if ( bind_return == -1 ) if (bind_return == -1)
{ {
errormessage( getLastError(), "Socket::bind" ); errormessage(getLastError(), "Socket::bind");
return false; return false;
} }
@ -118,13 +124,13 @@ bool Socket::listen() const
return false; return false;
} }
int listen_return = ::listen (_sd, SOMAXCONN); int listen_return = ::listen(m_sd, SOMAXCONN);
//This is defined as 5 in winsock.h, and 0x7FFFFFFF in winsock2.h. //This is defined as 5 in winsock.h, and 0x7FFFFFFF in winsock2.h.
//linux 128//MAXCONNECTIONS =1 //linux 128//MAXCONNECTIONS =1
if (listen_return == -1) if (listen_return == -1)
{ {
errormessage( getLastError(), "Socket::listen" ); errormessage(getLastError(), "Socket::listen");
return false; return false;
} }
@ -132,23 +138,23 @@ bool Socket::listen() const
} }
bool Socket::accept ( Socket& new_socket ) const bool Socket::accept(Socket& new_socket) const
{ {
if (!is_valid()) if (!is_valid())
{ {
return false; return false;
} }
socklen_t addr_length = sizeof( _sockaddr ); socklen_t addr_length = sizeof(m_sockaddr);
new_socket._sd = ::accept(_sd, const_cast<sockaddr*>( (const sockaddr*) &_sockaddr), &addr_length ); new_socket.m_sd = ::accept(m_sd, const_cast<sockaddr*>((const sockaddr*)&m_sockaddr), &addr_length);
#ifdef TARGET_WINDOWS #ifdef TARGET_WINDOWS
if (new_socket._sd == INVALID_SOCKET) if (new_socket.m_sd == INVALID_SOCKET)
#else #else
if (new_socket._sd <= 0) if (new_socket.m_sd <= 0)
#endif #endif
{ {
errormessage( getLastError(), "Socket::accept" ); errormessage(getLastError(), "Socket::accept");
return false; return false;
} }
@ -156,17 +162,17 @@ bool Socket::accept ( Socket& new_socket ) const
} }
int Socket::send ( const std::string& data ) int Socket::send(const std::string& data)
{ {
return Socket::send( (const char*) data.c_str(), (const unsigned int) data.size()); return Socket::send((const char*)data.c_str(), (const unsigned int)data.size());
} }
int Socket::send ( const char* data, const unsigned int len ) int Socket::send(const char* data, const unsigned int len)
{ {
fd_set set_w, set_e; fd_set set_w, set_e;
struct timeval tv; struct timeval tv;
int result; int result;
if (!is_valid()) if (!is_valid())
{ {
@ -174,15 +180,15 @@ int Socket::send ( const char* data, const unsigned int len )
} }
// fill with new data // fill with new data
tv.tv_sec = 0; tv.tv_sec = 0;
tv.tv_usec = 0; tv.tv_usec = 0;
FD_ZERO(&set_w); FD_ZERO(&set_w);
FD_ZERO(&set_e); FD_ZERO(&set_e);
FD_SET(_sd, &set_w); FD_SET(m_sd, &set_w);
FD_SET(_sd, &set_e); FD_SET(m_sd, &set_e);
result = select(FD_SETSIZE, &set_w, NULL, &set_e, &tv); result = select(FD_SETSIZE, &set_w, nullptr, &set_e, &tv);
if (result < 0) if (result < 0)
{ {
@ -190,18 +196,18 @@ int Socket::send ( const char* data, const unsigned int len )
close(); close();
return 0; return 0;
} }
if (FD_ISSET(_sd, &set_w)) if (FD_ISSET(m_sd, &set_w))
{ {
libKodi->Log(LOG_ERROR, "Socket::send - failed to send data"); libKodi->Log(LOG_ERROR, "Socket::send - failed to send data");
close(); close();
return 0; return 0;
} }
int status = ::send(_sd, data, len, 0 ); int status = ::send(m_sd, data, len, 0);
if (status == -1) if (status == -1)
{ {
errormessage( getLastError(), "Socket::send"); errormessage(getLastError(), "Socket::send");
libKodi->Log(LOG_ERROR, "Socket::send - failed to send data"); libKodi->Log(LOG_ERROR, "Socket::send - failed to send data");
close(); close();
return 0; return 0;
@ -210,31 +216,31 @@ int Socket::send ( const char* data, const unsigned int len )
} }
int Socket::sendto ( const char* data, unsigned int size, bool sendcompletebuffer) int Socket::sendto(const char* data, unsigned int size, bool sendcompletebuffer)
{ {
int sentbytes = 0; int sentbytes = 0;
int i; int i;
do do
{ {
i = ::sendto(_sd, data, size, 0, (const struct sockaddr*) &_sockaddr, sizeof( _sockaddr ) ); i = ::sendto(m_sd, data, size, 0, (const struct sockaddr*)&m_sockaddr, sizeof(m_sockaddr));
if (i <= 0) if (i <= 0)
{ {
errormessage( getLastError(), "Socket::sendto"); errormessage(getLastError(), "Socket::sendto");
osCleanup(); osCleanup();
return i; return i;
} }
sentbytes += i; sentbytes += i;
} while ( (sentbytes < (int) size) && (sendcompletebuffer == true)); } while ((sentbytes < (int)size) && (sendcompletebuffer == true));
return i; return i;
} }
int Socket::receive ( std::string& data, unsigned int minpacketsize ) const int Socket::receive(std::string& data, unsigned int minpacketsize) const
{ {
char * buf = NULL; char* buf = nullptr;
int status = 0; int status = 0;
if (!is_valid()) if (!is_valid())
@ -242,10 +248,10 @@ int Socket::receive ( std::string& data, unsigned int minpacketsize ) const
return 0; return 0;
} }
buf = new char [ minpacketsize + 1 ]; buf = new char[minpacketsize + 1];
memset ( buf, 0, minpacketsize + 1 ); memset(buf, 0, minpacketsize + 1);
status = receive( buf, minpacketsize, minpacketsize ); status = receive(buf, minpacketsize, minpacketsize);
data = buf; data = buf;
@ -255,12 +261,12 @@ int Socket::receive ( std::string& data, unsigned int minpacketsize ) const
//Receive until error or \n //Receive until error or \n
bool Socket::ReadLine (string& line) bool Socket::ReadLine(string& line)
{ {
fd_set set_r, set_e; fd_set set_r, set_e;
timeval timeout; timeval timeout;
int retries = 6; int retries = 6;
char buffer[2048]; char buffer[2048];
if (!is_valid()) if (!is_valid())
return false; return false;
@ -274,15 +280,15 @@ bool Socket::ReadLine (string& line)
return true; return true;
} }
timeout.tv_sec = RECEIVE_TIMEOUT; timeout.tv_sec = RECEIVE_TIMEOUT;
timeout.tv_usec = 0; timeout.tv_usec = 0;
// fill with new data // fill with new data
FD_ZERO(&set_r); FD_ZERO(&set_r);
FD_ZERO(&set_e); FD_ZERO(&set_e);
FD_SET(_sd, &set_r); FD_SET(m_sd, &set_r);
FD_SET(_sd, &set_e); FD_SET(m_sd, &set_e);
int result = select(FD_SETSIZE, &set_r, NULL, &set_e, &timeout); int result = select(FD_SETSIZE, &set_r, nullptr, &set_e, &timeout);
if (result < 0) if (result < 0)
{ {
@ -296,16 +302,20 @@ bool Socket::ReadLine (string& line)
{ {
if (retries != 0) if (retries != 0)
{ {
libKodi->Log(LOG_DEBUG, "%s: timeout waiting for response, retrying... (%i)", __FUNCTION__, retries); libKodi->Log(LOG_DEBUG, "%s: timeout waiting for response, retrying... (%i)", __FUNCTION__,
retries--; retries);
retries--;
continue; continue;
} else { }
libKodi->Log(LOG_DEBUG, "%s: timeout waiting for response. Aborting after 10 retries.", __FUNCTION__); else
return false; {
libKodi->Log(LOG_DEBUG, "%s: timeout waiting for response. Aborting after 10 retries.",
__FUNCTION__);
return false;
} }
} }
result = recv(_sd, buffer, sizeof(buffer) - 1, 0); result = recv(m_sd, buffer, sizeof(buffer) - 1, 0);
if (result < 0) if (result < 0)
{ {
libKodi->Log(LOG_DEBUG, "%s: recv failed", __FUNCTION__); libKodi->Log(LOG_DEBUG, "%s: recv failed", __FUNCTION__);
@ -322,39 +332,41 @@ bool Socket::ReadLine (string& line)
} }
int Socket::receive ( std::string& data) const int Socket::receive(std::string& data) const
{ {
char buf[MAXRECV + 1]; char buf[MAXRECV + 1];
int status = 0; int status = 0;
if ( !is_valid() ) if (!is_valid())
{ {
return 0; return 0;
} }
memset ( buf, 0, MAXRECV + 1 ); memset(buf, 0, MAXRECV + 1);
status = receive( buf, MAXRECV, 0 ); status = receive(buf, MAXRECV, 0);
data = buf; data = buf;
return status; return status;
} }
int Socket::receive ( char* data, const unsigned int buffersize, const unsigned int minpacketsize ) const int Socket::receive(char* data,
const unsigned int buffersize,
const unsigned int minpacketsize) const
{ {
unsigned int receivedsize = 0; unsigned int receivedsize = 0;
if ( !is_valid() ) if (!is_valid())
{ {
return 0; return 0;
} }
while ( (receivedsize <= minpacketsize) && (receivedsize < buffersize) ) while ((receivedsize <= minpacketsize) && (receivedsize < buffersize))
{ {
int status = ::recv(_sd, data+receivedsize, (buffersize - receivedsize), 0 ); int status = ::recv(m_sd, data + receivedsize, (buffersize - receivedsize), 0);
if ( status == SOCKET_ERROR ) if (status == SOCKET_ERROR)
{ {
errormessage( getLastError(), "Socket::receive" ); errormessage(getLastError(), "Socket::receive");
return status; return status;
} }
@ -365,35 +377,38 @@ int Socket::receive ( char* data, const unsigned int buffersize, const unsigned
} }
int Socket::recvfrom ( char* data, const int buffersize, struct sockaddr* from, socklen_t* fromlen) const int Socket::recvfrom(char* data,
const int buffersize,
struct sockaddr* from,
socklen_t* fromlen) const
{ {
int status = ::recvfrom(_sd, data, buffersize, 0, from, fromlen); int status = ::recvfrom(m_sd, data, buffersize, 0, from, fromlen);
return status; return status;
} }
bool Socket::connect ( const std::string& host, const unsigned short port ) bool Socket::connect(const std::string& host, const unsigned short port)
{ {
close(); close();
if ( !setHostname( host ) ) if (!setHostname(host))
{ {
libKodi->Log(LOG_ERROR, "Socket::setHostname(%s) failed.\n", host.c_str()); libKodi->Log(LOG_ERROR, "Socket::setHostname(%s) failed.\n", host.c_str());
return false; return false;
} }
_port = port; m_port = port;
char strPort[15]; char strPort[15];
snprintf(strPort, 15, "%hu", port); snprintf(strPort, 15, "%hu", port);
struct addrinfo hints; struct addrinfo hints;
struct addrinfo* result = NULL; struct addrinfo* result = nullptr;
struct addrinfo *address = NULL; struct addrinfo* address = nullptr;
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
hints.ai_family = _family; hints.ai_family = m_family;
hints.ai_socktype = _type; hints.ai_socktype = m_type;
hints.ai_protocol = _protocol; hints.ai_protocol = m_protocol;
int retval = getaddrinfo(host.c_str(), strPort, &hints, &result); int retval = getaddrinfo(host.c_str(), strPort, &hints, &result);
if (retval != 0) if (retval != 0)
@ -402,18 +417,18 @@ bool Socket::connect ( const std::string& host, const unsigned short port )
return false; return false;
} }
for (address = result; address != NULL; address = address->ai_next) for (address = result; address != nullptr; address = address->ai_next)
{ {
// Create the socket // Create the socket
_sd = socket(address->ai_family, address->ai_socktype, address->ai_protocol); m_sd = socket(address->ai_family, address->ai_socktype, address->ai_protocol);
if (_sd == INVALID_SOCKET) if (m_sd == INVALID_SOCKET)
{ {
errormessage(getLastError(), "Socket::create"); errormessage(getLastError(), "Socket::create");
continue; continue;
} }
int status = ::connect(_sd, address->ai_addr, address->ai_addrlen); int status = ::connect(m_sd, address->ai_addr, address->ai_addrlen);
if (status == SOCKET_ERROR) if (status == SOCKET_ERROR)
{ {
close(); close();
@ -426,7 +441,7 @@ bool Socket::connect ( const std::string& host, const unsigned short port )
freeaddrinfo(result); freeaddrinfo(result);
if (address == NULL) if (address == nullptr)
{ {
libKodi->Log(LOG_ERROR, "Socket::connect %s:%u\n", host.c_str(), port); libKodi->Log(LOG_ERROR, "Socket::connect %s:%u\n", host.c_str(), port);
errormessage(getLastError(), "Socket::connect"); errormessage(getLastError(), "Socket::connect");
@ -439,30 +454,30 @@ bool Socket::connect ( const std::string& host, const unsigned short port )
bool Socket::reconnect() bool Socket::reconnect()
{ {
if ( is_valid() ) if (is_valid())
{ {
return true; return true;
} }
return connect(_hostname, _port); return connect(m_hostname, m_port);
} }
bool Socket::is_valid() const bool Socket::is_valid() const
{ {
return (_sd != INVALID_SOCKET); return (m_sd != INVALID_SOCKET);
} }
#if defined(TARGET_WINDOWS) #if defined(TARGET_WINDOWS)
bool Socket::set_non_blocking ( const bool b ) bool Socket::set_non_blocking(const bool b)
{ {
u_long iMode; u_long iMode;
if ( b ) if (b)
iMode = 1; // enable non_blocking iMode = 1; // enable non_blocking
else else
iMode = 0; // disable non_blocking iMode = 0; // disable non_blocking
if (ioctlsocket(_sd, FIONBIO, &iMode) == -1) if (ioctlsocket(m_sd, FIONBIO, &iMode) == -1)
{ {
libKodi->Log(LOG_ERROR, "Socket::set_non_blocking - Can't set socket condition to: %i", iMode); libKodi->Log(LOG_ERROR, "Socket::set_non_blocking - Can't set socket condition to: %i", iMode);
return false; return false;
@ -471,89 +486,89 @@ bool Socket::set_non_blocking ( const bool b )
return true; return true;
} }
void Socket::errormessage( int errnum, const char* functionname) const void Socket::errormessage(int errnum, const char* functionname) const
{ {
const char* errmsg = NULL; const char* errmsg = nullptr;
switch (errnum) switch (errnum)
{ {
case WSANOTINITIALISED: case WSANOTINITIALISED:
errmsg = "A successful WSAStartup call must occur before using this function."; errmsg = "A successful WSAStartup call must occur before using this function.";
break; break;
case WSAENETDOWN: case WSAENETDOWN:
errmsg = "The network subsystem or the associated service provider has failed"; errmsg = "The network subsystem or the associated service provider has failed";
break; break;
case WSA_NOT_ENOUGH_MEMORY: case WSA_NOT_ENOUGH_MEMORY:
errmsg = "Insufficient memory available"; errmsg = "Insufficient memory available";
break; break;
case WSA_INVALID_PARAMETER: case WSA_INVALID_PARAMETER:
errmsg = "One or more parameters are invalid"; errmsg = "One or more parameters are invalid";
break; break;
case WSA_OPERATION_ABORTED: case WSA_OPERATION_ABORTED:
errmsg = "Overlapped operation aborted"; errmsg = "Overlapped operation aborted";
break; break;
case WSAEINTR: case WSAEINTR:
errmsg = "Interrupted function call"; errmsg = "Interrupted function call";
break; break;
case WSAEBADF: case WSAEBADF:
errmsg = "File handle is not valid"; errmsg = "File handle is not valid";
break; break;
case WSAEACCES: case WSAEACCES:
errmsg = "Permission denied"; errmsg = "Permission denied";
break; break;
case WSAEFAULT: case WSAEFAULT:
errmsg = "Bad address"; errmsg = "Bad address";
break; break;
case WSAEINVAL: case WSAEINVAL:
errmsg = "Invalid argument"; errmsg = "Invalid argument";
break; break;
case WSAENOTSOCK: case WSAENOTSOCK:
errmsg = "Socket operation on nonsocket"; errmsg = "Socket operation on nonsocket";
break; break;
case WSAEDESTADDRREQ: case WSAEDESTADDRREQ:
errmsg = "Destination address required"; errmsg = "Destination address required";
break; break;
case WSAEMSGSIZE: case WSAEMSGSIZE:
errmsg = "Message too long"; errmsg = "Message too long";
break; break;
case WSAEPROTOTYPE: case WSAEPROTOTYPE:
errmsg = "Protocol wrong type for socket"; errmsg = "Protocol wrong type for socket";
break; break;
case WSAENOPROTOOPT: case WSAENOPROTOOPT:
errmsg = "Bad protocol option"; errmsg = "Bad protocol option";
break; break;
case WSAEPFNOSUPPORT: case WSAEPFNOSUPPORT:
errmsg = "Protocol family not supported"; errmsg = "Protocol family not supported";
break; break;
case WSAEAFNOSUPPORT: case WSAEAFNOSUPPORT:
errmsg = "Address family not supported by protocol family"; errmsg = "Address family not supported by protocol family";
break; break;
case WSAEADDRINUSE: case WSAEADDRINUSE:
errmsg = "Address already in use"; errmsg = "Address already in use";
break; break;
case WSAECONNRESET: case WSAECONNRESET:
errmsg = "Connection reset by peer"; errmsg = "Connection reset by peer";
break; break;
case WSAHOST_NOT_FOUND: case WSAHOST_NOT_FOUND:
errmsg = "Authoritative answer host not found"; errmsg = "Authoritative answer host not found";
break; break;
case WSATRY_AGAIN: case WSATRY_AGAIN:
errmsg = "Nonauthoritative host not found, or server failure"; errmsg = "Nonauthoritative host not found, or server failure";
break; break;
case WSAEISCONN: case WSAEISCONN:
errmsg = "Socket is already connected"; errmsg = "Socket is already connected";
break; break;
case WSAETIMEDOUT: case WSAETIMEDOUT:
errmsg = "Connection timed out"; errmsg = "Connection timed out";
break; break;
case WSAECONNREFUSED: case WSAECONNREFUSED:
errmsg = "Connection refused"; errmsg = "Connection refused";
break; break;
case WSANO_DATA: case WSANO_DATA:
errmsg = "Valid name, no data record of requested type"; errmsg = "Valid name, no data record of requested type";
break; break;
default: default:
errmsg = "WSA Error"; errmsg = "WSA Error";
} }
libKodi->Log(LOG_ERROR, "%s: (Winsock error=%i) %s\n", functionname, errnum, errmsg); libKodi->Log(LOG_ERROR, "%s: (Winsock error=%i) %s\n", functionname, errnum, errmsg);
} }
@ -569,15 +584,15 @@ bool Socket::osInit()
{ {
win_usage_count++; win_usage_count++;
// initialize winsock: // initialize winsock:
if (WSAStartup(MAKEWORD(2,2),&_wsaData) != 0) if (WSAStartup(MAKEWORD(2, 2), &m_wsaData) != 0)
{ {
return false; return false;
} }
WORD wVersionRequested = MAKEWORD(2,2); WORD wVersionRequested = MAKEWORD(2, 2);
// check version // check version
if (_wsaData.wVersion != wVersionRequested) if (m_wsaData.wVersion != wVersionRequested)
{ {
return false; return false;
} }
@ -588,30 +603,30 @@ bool Socket::osInit()
void Socket::osCleanup() void Socket::osCleanup()
{ {
win_usage_count--; win_usage_count--;
if(win_usage_count == 0) if (win_usage_count == 0)
{ {
WSACleanup(); WSACleanup();
} }
} }
#elif defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD #elif defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD
bool Socket::set_non_blocking ( const bool b ) bool Socket::set_non_blocking(const bool b)
{ {
int opts; int opts;
opts = fcntl(_sd, F_GETFL); opts = fcntl(m_sd, F_GETFL);
if ( opts < 0 ) if (opts < 0)
{ {
return false; return false;
} }
if ( b ) if (b)
opts = ( opts | O_NONBLOCK ); opts = (opts | O_NONBLOCK);
else else
opts = ( opts & ~O_NONBLOCK ); opts = (opts & ~O_NONBLOCK);
if(fcntl (_sd , F_SETFL, opts) == -1) if (fcntl(m_sd, F_SETFL, opts) == -1)
{ {
libKodi->Log(LOG_ERROR, "Socket::set_non_blocking - Can't set socket flags to: %i", opts); libKodi->Log(LOG_ERROR, "Socket::set_non_blocking - Can't set socket flags to: %i", opts);
return false; return false;
@ -619,11 +634,11 @@ bool Socket::set_non_blocking ( const bool b )
return true; return true;
} }
void Socket::errormessage( int errnum, const char* functionname) const void Socket::errormessage(int errnum, const char* functionname) const
{ {
const char* errmsg = NULL; const char* errmsg = nullptr;
switch ( errnum ) switch (errnum)
{ {
case EAGAIN: //same as EWOULDBLOCK case EAGAIN: //same as EWOULDBLOCK
errmsg = "EAGAIN: The socket is marked non-blocking and the requested operation would block"; errmsg = "EAGAIN: The socket is marked non-blocking and the requested operation would block";
@ -650,7 +665,8 @@ void Socket::errormessage( int errnum, const char* functionname) const
errmsg = "ENOTSOCK: The argument is not a valid socket"; errmsg = "ENOTSOCK: The argument is not a valid socket";
break; break;
case EMSGSIZE: case EMSGSIZE:
errmsg = "EMSGSIZE: The socket requires that message be sent atomically, and the size of the message to be sent made this impossible"; errmsg = "EMSGSIZE: The socket requires that message be sent atomically, and the size of the "
"message to be sent made this impossible";
break; break;
case ENOBUFS: case ENOBUFS:
errmsg = "ENOBUFS: The output queue for a network interface was full"; errmsg = "ENOBUFS: The output queue for a network interface was full";
@ -662,7 +678,8 @@ void Socket::errormessage( int errnum, const char* functionname) const
errmsg = "EPIPE: The local end has been shut down on a connection oriented socket"; errmsg = "EPIPE: The local end has been shut down on a connection oriented socket";
break; break;
case EPROTONOSUPPORT: case EPROTONOSUPPORT:
errmsg = "EPROTONOSUPPORT: The protocol type or the specified protocol is not supported within this domain"; errmsg = "EPROTONOSUPPORT: The protocol type or the specified protocol is not supported "
"within this domain";
break; break;
case EAFNOSUPPORT: case EAFNOSUPPORT:
errmsg = "EAFNOSUPPORT: The implementation does not support the specified address family"; errmsg = "EAFNOSUPPORT: The implementation does not support the specified address family";
@ -674,13 +691,16 @@ void Socket::errormessage( int errnum, const char* functionname) const
errmsg = "EMFILE: Process file table overflow"; errmsg = "EMFILE: Process file table overflow";
break; break;
case EACCES: case EACCES:
errmsg = "EACCES: Permission to create a socket of the specified type and/or protocol is denied"; errmsg =
"EACCES: Permission to create a socket of the specified type and/or protocol is denied";
break; break;
case ECONNREFUSED: case ECONNREFUSED:
errmsg = "ECONNREFUSED: A remote host refused to allow the network connection (typically because it is not running the requested service)"; errmsg = "ECONNREFUSED: A remote host refused to allow the network connection (typically "
"because it is not running the requested service)";
break; break;
case ENOTCONN: case ENOTCONN:
errmsg = "ENOTCONN: The socket is associated with a connection-oriented protocol and has not been connected"; errmsg = "ENOTCONN: The socket is associated with a connection-oriented protocol and has not "
"been connected";
break; break;
//case E: //case E:
// errmsg = ""; // errmsg = "";

View File

@ -10,64 +10,64 @@
//Include platform specific datatypes, header files, defines and constants: //Include platform specific datatypes, header files, defines and constants:
#if defined TARGET_WINDOWS #if defined TARGET_WINDOWS
#define WIN32_LEAN_AND_MEAN // Enable LEAN_AND_MEAN support #define WIN32_LEAN_AND_MEAN // Enable LEAN_AND_MEAN support
#pragma warning(disable:4005) // Disable "warning C4005: '_WINSOCKAPI_' : macro redefinition" #pragma warning(disable : 4005) // Disable "warning C4005: '_WINSOCKAPI_' : macro redefinition"
#include <winsock2.h> #include <WS2tcpip.h>
#include <WS2tcpip.h> #include <winsock2.h>
#pragma warning(default:4005) #pragma warning(default : 4005)
#include <windows.h> #include <windows.h>
#ifndef NI_MAXHOST #ifndef NI_MAXHOST
#define NI_MAXHOST 1025 #define NI_MAXHOST 1025
#endif #endif
#ifndef socklen_t #ifndef socklen_t
typedef int socklen_t; typedef int socklen_t;
#endif #endif
#ifndef ipaddr_t #ifndef ipaddr_t
typedef unsigned long ipaddr_t; typedef unsigned long ipaddr_t;
#endif #endif
#ifndef port_t #ifndef port_t
typedef unsigned short port_t; typedef unsigned short port_t;
#endif #endif
#ifndef sa_family_t #ifndef sa_family_t
#define sa_family_t ADDRESS_FAMILY #define sa_family_t ADDRESS_FAMILY
#endif #endif
#elif defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD #elif defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD
#ifdef SOCKADDR_IN #ifdef SOCKADDR_IN
#undef SOCKADDR_IN #undef SOCKADDR_IN
#endif #endif
#include <sys/types.h> /* for socket,connect */ #include <arpa/inet.h> /* for inet_pton */
#include <sys/socket.h> /* for socket,connect */ #include <errno.h>
#include <sys/un.h> /* for Unix socket */ #include <fcntl.h>
#include <arpa/inet.h> /* for inet_pton */ #include <netdb.h> /* for gethostbyname */
#include <netdb.h> /* for gethostbyname */ #include <netinet/in.h> /* for htons */
#include <netinet/in.h> /* for htons */ #include <sys/socket.h> /* for socket,connect */
#include <unistd.h> /* for read, write, close */ #include <sys/types.h> /* for socket,connect */
#include <errno.h> #include <sys/un.h> /* for Unix socket */
#include <fcntl.h> #include <unistd.h> /* for read, write, close */
typedef int SOCKET; typedef int SOCKET;
typedef sockaddr SOCKADDR; typedef sockaddr SOCKADDR;
typedef sockaddr_in SOCKADDR_IN; typedef sockaddr_in SOCKADDR_IN;
#ifndef INVALID_SOCKET #ifndef INVALID_SOCKET
#define INVALID_SOCKET (-1) #define INVALID_SOCKET (-1)
#endif #endif
#define SOCKET_ERROR (-1) #define SOCKET_ERROR (-1)
#define closesocket(sd) ::close(sd) #define closesocket(sd) ::close(sd)
#else #else
#error Platform specific socket support is not yet available on this platform! #error Platform specific socket support is not yet available on this platform!
#endif #endif
#include <string>
#include <vector> #include <vector>
namespace OCTO namespace OCTO
{ {
#define MAXCONNECTIONS 1 ///< Maximum number of pending connections before "Connection refused" #define MAXCONNECTIONS 1 ///< Maximum number of pending connections before "Connection refused"
#define MAXRECV 1500 ///< Maximum packet size #define MAXRECV 1500 ///< Maximum packet size
enum SocketFamily enum SocketFamily
{ {
@ -78,10 +78,10 @@ enum SocketFamily
enum SocketDomain enum SocketDomain
{ {
#if defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD #if defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD
pf_unix = PF_UNIX, pf_unix = PF_UNIX,
pf_local = PF_LOCAL, pf_local = PF_LOCAL,
#endif #endif
pf_inet = PF_INET pf_inet = PF_INET
}; };
@ -99,197 +99,187 @@ enum SocketProtocol
class Socket class Socket
{ {
public: public:
/*!
* An unconnected socket may be created directly on the local
* machine. The socket type (SOCK_STREAM, SOCK_DGRAM) and
* protocol may also be specified.
* If the socket cannot be created, an exception is thrown.
*
* \param family Socket family (IPv4 or IPv6)
* \param domain The domain parameter specifies a communications domain within which communication will take place;
* this selects the protocol family which should be used.
* \param type base type and protocol family of the socket.
* \param protocol specific protocol to apply.
*/
Socket(const enum SocketFamily family,
const enum SocketDomain domain,
const enum SocketType type,
const enum SocketProtocol protocol = tcp);
Socket(void);
virtual ~Socket();
/*! //Socket settings
* An unconnected socket may be created directly on the local
* machine. The socket type (SOCK_STREAM, SOCK_DGRAM) and
* protocol may also be specified.
* If the socket cannot be created, an exception is thrown.
*
* \param family Socket family (IPv4 or IPv6)
* \param domain The domain parameter specifies a communications domain within which communication will take place;
* this selects the protocol family which should be used.
* \param type base type and protocol family of the socket.
* \param protocol specific protocol to apply.
*/
Socket(const enum SocketFamily family, const enum SocketDomain domain, const enum SocketType type, const enum SocketProtocol protocol = tcp);
Socket(void);
virtual ~Socket();
//Socket settings /*!
* Socket setFamily
* \param family Can be af_inet or af_inet6. Default: af_inet
*/
void setFamily(const enum SocketFamily family) { m_family = family; };
/*! /*!
* Socket setFamily * Socket setDomain
* \param family Can be af_inet or af_inet6. Default: af_inet * \param domain Can be pf_unix, pf_local, pf_inet or pf_inet6. Default: pf_inet
*/ */
void setFamily(const enum SocketFamily family) void setDomain(const enum SocketDomain domain) { m_domain = domain; };
{
_family = family;
};
/*! /*!
* Socket setDomain * Socket setType
* \param domain Can be pf_unix, pf_local, pf_inet or pf_inet6. Default: pf_inet * \param type Can be sock_stream or sock_dgram. Default: sock_stream.
*/ */
void setDomain(const enum SocketDomain domain) void setType(const enum SocketType type) { m_type = type; };
{
_domain = domain;
};
/*! /*!
* Socket setType * Socket setProtocol
* \param type Can be sock_stream or sock_dgram. Default: sock_stream. * \param protocol Can be tcp or udp. Default: tcp.
*/ */
void setType(const enum SocketType type) void setProtocol(const enum SocketProtocol protocol) { m_protocol = protocol; };
{
_type = type;
};
/*! /*!
* Socket setProtocol * Socket setPort
* \param protocol Can be tcp or udp. Default: tcp. * \param port port number for socket communication
*/ */
void setProtocol(const enum SocketProtocol protocol) void setPort(const unsigned short port) { m_sockaddr.sin_port = htons(port); };
{
_protocol = protocol;
};
/*! bool setHostname(const std::string& host);
* Socket setPort
* \param port port number for socket communication
*/
void setPort (const unsigned short port)
{
_sockaddr.sin_port = htons ( port );
};
bool setHostname ( const std::string& host ); // Server initialization
// Server initialization /*!
* Socket create
* Create a new socket
* \return True if succesful
*/
bool create();
/*! /*!
* Socket create * Socket close
* Create a new socket * Close the socket
* \return True if succesful * \return True if succesful
*/ */
bool create(); bool close();
/*! /*!
* Socket close * Socket bind
* Close the socket */
* \return True if succesful bool bind(const unsigned short port);
*/ bool listen() const;
bool close(); bool accept(Socket& socket) const;
/*! // Client initialization
* Socket bind bool connect(const std::string& host, const unsigned short port);
*/
bool bind ( const unsigned short port );
bool listen() const;
bool accept ( Socket& socket ) const;
// Client initialization bool reconnect();
bool connect ( const std::string& host, const unsigned short port );
bool reconnect(); // Data Transmission
// Data Transmission /*!
* Socket send function
*
* \param data Reference to a std::string with the data to transmit
* \return Number of bytes send or -1 in case of an error
*/
int send(const std::string& data);
/*! /*!
* Socket send function * Socket send function
* *
* \param data Reference to a std::string with the data to transmit * \param data Pointer to a character array of size 'size' with the data to transmit
* \return Number of bytes send or -1 in case of an error * \param size Length of the data to transmit
*/ * \return Number of bytes send or -1 in case of an error
int send ( const std::string& data ); */
int send(const char* data, const unsigned int size);
/*! /*!
* Socket send function * Socket sendto function
* *
* \param data Pointer to a character array of size 'size' with the data to transmit * \param data Reference to a std::string with the data to transmit
* \param size Length of the data to transmit * \param size Length of the data to transmit
* \return Number of bytes send or -1 in case of an error * \param sendcompletebuffer If 'true': do not return until the complete buffer is transmitted
*/ * \return Number of bytes send or -1 in case of an error
int send ( const char* data, const unsigned int size ); */
int sendto(const char* data, unsigned int size, bool sendcompletebuffer = false);
// Data Receive
/*! /*!
* Socket sendto function * Socket receive function
* *
* \param data Reference to a std::string with the data to transmit * \param data Reference to a std::string for storage of the received data.
* \param size Length of the data to transmit * \param minpacketsize The minimum number of bytes that should be received before returning from this function
* \param sendcompletebuffer If 'true': do not return until the complete buffer is transmitted * \return Number of bytes received or SOCKET_ERROR
* \return Number of bytes send or -1 in case of an error */
*/ int receive(std::string& data, unsigned int minpacketsize) const;
int sendto ( const char* data, unsigned int size, bool sendcompletebuffer = false);
// Data Receive
/*! /*!
* Socket receive function * Socket receive function
* *
* \param data Reference to a std::string for storage of the received data. * \param data Reference to a std::string for storage of the received data.
* \param minpacketsize The minimum number of bytes that should be received before returning from this function * \return Number of bytes received or SOCKET_ERROR
* \return Number of bytes received or SOCKET_ERROR */
*/ int receive(std::string& data) const;
int receive ( std::string& data, unsigned int minpacketsize ) const;
/*! /*!
* Socket receive function * Socket receive function
* *
* \param data Reference to a std::string for storage of the received data. * \param data Pointer to a character array of size buffersize. Used to store the received data.
* \return Number of bytes received or SOCKET_ERROR * \param buffersize Size of the 'data' buffer
*/ * \param minpacketsize Specifies the minimum number of bytes that need to be received before returning
int receive ( std::string& data ) const; * \return Number of bytes received or SOCKET_ERROR
*/
int receive(char* data, const unsigned int buffersize, const unsigned int minpacketsize) const;
/*! /*!
* Socket receive function * Socket recvfrom function
* *
* \param data Pointer to a character array of size buffersize. Used to store the received data. * \param data Pointer to a character array of size buffersize. Used to store the received data.
* \param buffersize Size of the 'data' buffer * \param buffersize Size of the 'data' buffer
* \param minpacketsize Specifies the minimum number of bytes that need to be received before returning * \param from Optional: pointer to a sockaddr struct that will get the address from which the data is received
* \return Number of bytes received or SOCKET_ERROR * \param fromlen Optional, only required if 'from' is given: length of from struct
*/ * \return Number of bytes received or SOCKET_ERROR
int receive ( char* data, const unsigned int buffersize, const unsigned int minpacketsize ) const; */
int recvfrom(char* data,
const int buffersize,
struct sockaddr* from = nullptr,
socklen_t* fromlen = nullptr) const;
/*! bool set_non_blocking(const bool);
* Socket recvfrom function
*
* \param data Pointer to a character array of size buffersize. Used to store the received data.
* \param buffersize Size of the 'data' buffer
* \param from Optional: pointer to a sockaddr struct that will get the address from which the data is received
* \param fromlen Optional, only required if 'from' is given: length of from struct
* \return Number of bytes received or SOCKET_ERROR
*/
int recvfrom ( char* data, const int buffersize, struct sockaddr* from = NULL, socklen_t* fromlen = NULL) const;
bool set_non_blocking ( const bool ); bool ReadLine(std::string& line);
bool ReadLine (std::string& line); bool is_valid() const;
bool is_valid() const; private:
SOCKET m_sd; ///< Socket Descriptor
SOCKADDR_IN m_sockaddr; ///< Socket Address
//struct addrinfo* m_addrinfo; ///< Socket address info
std::string m_hostname; ///< Hostname
unsigned short m_port; ///< Port number
private: enum SocketFamily m_family; ///< Socket Address Family
enum SocketProtocol m_protocol; ///< Socket Protocol
enum SocketType m_type; ///< Socket Type
enum SocketDomain m_domain; ///< Socket domain
SOCKET _sd; ///< Socket Descriptor #ifdef TARGET_WINDOWS
SOCKADDR_IN _sockaddr; ///< Socket Address WSADATA m_wsaData; ///< Windows Socket data
//struct addrinfo* _addrinfo; ///< Socket address info static int
std::string _hostname; ///< Hostname win_usage_count; ///< Internal Windows usage counter used to prevent a global WSACleanup when more than one Socket object is used
unsigned short _port; ///< Port number #endif
enum SocketFamily _family; ///< Socket Address Family void errormessage(int errornum, const char* functionname = nullptr) const;
enum SocketProtocol _protocol; ///< Socket Protocol int getLastError(void) const;
enum SocketType _type; ///< Socket Type bool osInit();
enum SocketDomain _domain; ///< Socket domain void osCleanup();
#ifdef TARGET_WINDOWS
WSADATA _wsaData; ///< Windows Socket data
static int win_usage_count; ///< Internal Windows usage counter used to prevent a global WSACleanup when more than one Socket object is used
#endif
void errormessage( int errornum, const char* functionname = NULL) const;
int getLastError(void) const;
bool osInit();
void osCleanup();
}; };
} //namespace OCTO } //namespace OCTO

View File

@ -9,13 +9,14 @@
*/ */
#include "client.h" #include "client.h"
#include <kodi/xbmc_pvr_dll.h>
#include <kodi/libXBMC_addon.h>
#include <p8-platform/util/util.h>
#include "OctonetData.h" #include "OctonetData.h"
#include "rtsp_client.hpp" #include "rtsp_client.hpp"
#include <kodi/libXBMC_addon.h>
#include <kodi/xbmc_pvr_dll.h>
#include <p8-platform/util/util.h>
using namespace ADDON; using namespace ADDON;
/* setting variables with defaults */ /* setting variables with defaults */
@ -23,72 +24,71 @@ std::string octonetAddress = "";
/* internal state variables */ /* internal state variables */
ADDON_STATUS addonStatus = ADDON_STATUS_UNKNOWN; ADDON_STATUS addonStatus = ADDON_STATUS_UNKNOWN;
CHelper_libXBMC_addon *libKodi = NULL; CHelper_libXBMC_addon* libKodi = nullptr;
CHelper_libXBMC_pvr *pvr = NULL; CHelper_libXBMC_pvr* pvr = nullptr;
OctonetData *data = NULL; OctonetData* data = nullptr;
/* KODI Core Addon functions /* KODI Core Addon functions
* see xbmc_addon_dll.h */ * see xbmc_addon_dll.h */
extern "C" { extern "C"
void ADDON_ReadSettings(void)
{ {
char buffer[2048];
if (libKodi->GetSetting("octonetAddress", &buffer))
octonetAddress = buffer;
}
ADDON_STATUS ADDON_Create(void* callbacks, const char* globalApiVersion, void* props) void ADDON_ReadSettings(void)
{ {
if (callbacks == NULL || props == NULL) char buffer[2048];
return ADDON_STATUS_UNKNOWN; if (libKodi->GetSetting("octonetAddress", &buffer))
octonetAddress = buffer;
}
AddonProperties_PVR *pvrprops = (AddonProperties_PVR*)props; ADDON_STATUS ADDON_Create(void* callbacks, const char* globalApiVersion, void* props)
libKodi = new CHelper_libXBMC_addon; {
if (!libKodi->RegisterMe(callbacks)) { if (callbacks == nullptr || props == nullptr)
libKodi->Log(LOG_ERROR, "%s: Failed to register octonet addon", __func__); return ADDON_STATUS_UNKNOWN;
SAFE_DELETE(libKodi);
return ADDON_STATUS_PERMANENT_FAILURE;
}
pvr = new CHelper_libXBMC_pvr; AddonProperties_PVR* pvrprops = (AddonProperties_PVR*)props;
if (!pvr->RegisterMe(callbacks)) { libKodi = new CHelper_libXBMC_addon;
libKodi->Log(LOG_ERROR, "%s: Failed to register octonet pvr addon", __func__); if (!libKodi->RegisterMe(callbacks))
SAFE_DELETE(pvr); {
SAFE_DELETE(libKodi); libKodi->Log(LOG_ERROR, "%s: Failed to register octonet addon", __func__);
return ADDON_STATUS_PERMANENT_FAILURE; SAFE_DELETE(libKodi);
} return ADDON_STATUS_PERMANENT_FAILURE;
}
libKodi->Log(LOG_DEBUG, "%s: Creating octonet pvr addon", __func__); pvr = new CHelper_libXBMC_pvr;
ADDON_ReadSettings(); if (!pvr->RegisterMe(callbacks))
{
libKodi->Log(LOG_ERROR, "%s: Failed to register octonet pvr addon", __func__);
SAFE_DELETE(pvr);
SAFE_DELETE(libKodi);
return ADDON_STATUS_PERMANENT_FAILURE;
}
data = new OctonetData; libKodi->Log(LOG_DEBUG, "%s: Creating octonet pvr addon", __func__);
ADDON_ReadSettings();
addonStatus = ADDON_STATUS_OK; data = new OctonetData;
return addonStatus;
}
void ADDON_Destroy() addonStatus = ADDON_STATUS_OK;
{ return addonStatus;
delete pvr; }
delete libKodi;
addonStatus = ADDON_STATUS_UNKNOWN;
}
ADDON_STATUS ADDON_GetStatus() void ADDON_Destroy()
{ {
return addonStatus; delete pvr;
} delete libKodi;
addonStatus = ADDON_STATUS_UNKNOWN;
}
ADDON_STATUS ADDON_SetSetting(const char *settingName, const void *settingValue) ADDON_STATUS ADDON_GetStatus() { return addonStatus; }
{
/* For simplicity do a full addon restart whenever settings are ADDON_STATUS ADDON_SetSetting(const char* settingName, const void* settingValue)
{
/* For simplicity do a full addon restart whenever settings are
* changed */ * changed */
return ADDON_STATUS_NEED_RESTART; return ADDON_STATUS_NEED_RESTART;
} }
} }
@ -97,178 +97,204 @@ ADDON_STATUS ADDON_SetSetting(const char *settingName, const void *settingValue)
extern "C" extern "C"
{ {
PVR_ERROR GetCapabilities(PVR_ADDON_CAPABILITIES *pCapabilities) PVR_ERROR GetCapabilities(PVR_ADDON_CAPABILITIES* pCapabilities)
{ {
pCapabilities->bSupportsTV = true; pCapabilities->bSupportsTV = true;
pCapabilities->bSupportsRadio = true; pCapabilities->bSupportsRadio = true;
pCapabilities->bSupportsChannelGroups = true; pCapabilities->bSupportsChannelGroups = true;
pCapabilities->bSupportsEPG = true; pCapabilities->bSupportsEPG = true;
pCapabilities->bSupportsRecordings = false; pCapabilities->bSupportsRecordings = false;
pCapabilities->bSupportsRecordingsRename = false; pCapabilities->bSupportsRecordingsRename = false;
pCapabilities->bSupportsRecordingsLifetimeChange = false; pCapabilities->bSupportsRecordingsLifetimeChange = false;
pCapabilities->bSupportsDescrambleInfo = false; pCapabilities->bSupportsDescrambleInfo = false;
return PVR_ERROR_NO_ERROR; return PVR_ERROR_NO_ERROR;
} }
const char* GetBackendName(void) const char* GetBackendName(void) { return "Digital Devices Octopus NET Client"; }
{
return "Digital Devices Octopus NET Client";
}
const char* GetBackendVersion(void) const char* GetBackendVersion(void) { return STR(OCTONET_VERSION); }
{
return STR(OCTONET_VERSION);
}
const char* GetConnectionString(void) const char* GetConnectionString(void)
{ {
return "connected"; // FIXME: translate? return "connected"; // FIXME: translate?
} }
PVR_ERROR GetDriveSpace(long long* iTotal, long long* iUsed) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR GetDriveSpace(long long* iTotal, long long* iUsed) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR CallMenuHook(const PVR_MENUHOOK& menuhook, const PVR_MENUHOOK_DATA &item) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR CallMenuHook(const PVR_MENUHOOK& menuhook, const PVR_MENUHOOK_DATA& item)
{
return PVR_ERROR_NOT_IMPLEMENTED;
}
void OnSystemSleep() { void OnSystemSleep()
libKodi->Log(LOG_INFO, "Received event: %s", __FUNCTION__); {
// FIXME: Disconnect? libKodi->Log(LOG_INFO, "Received event: %s", __FUNCTION__);
} // FIXME: Disconnect?
}
void OnSystemWake() { void OnSystemWake()
libKodi->Log(LOG_INFO, "Received event: %s", __FUNCTION__); {
// FIXME:Reconnect? libKodi->Log(LOG_INFO, "Received event: %s", __FUNCTION__);
} // FIXME:Reconnect?
}
void OnPowerSavingActivated() {} void OnPowerSavingActivated() {}
void OnPowerSavingDeactivated() {} void OnPowerSavingDeactivated() {}
/* EPG */ /* EPG */
PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, int iChannelUid, time_t iStart, time_t iEnd) PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, int iChannelUid, time_t iStart, time_t iEnd)
{ {
return data->getEPG(handle, iChannelUid, iStart, iEnd); return data->getEPG(handle, iChannelUid, iStart, iEnd);
} }
PVR_ERROR IsEPGTagRecordable(const EPG_TAG*, bool*) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR IsEPGTagRecordable(const EPG_TAG*, bool*) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR IsEPGTagPlayable(const EPG_TAG*, bool*) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR IsEPGTagPlayable(const EPG_TAG*, bool*) { return PVR_ERROR_NOT_IMPLEMENTED; }
/* Channel groups */ /* Channel groups */
int GetChannelGroupsAmount(void) int GetChannelGroupsAmount(void) { return data->getGroupCount(); }
{
return data->getGroupCount();
}
PVR_ERROR GetChannelGroups(ADDON_HANDLE handle, bool bRadio) PVR_ERROR GetChannelGroups(ADDON_HANDLE handle, bool bRadio)
{ {
return data->getGroups(handle, bRadio); return data->getGroups(handle, bRadio);
} }
PVR_ERROR GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP& group) PVR_ERROR GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP& group)
{ {
return data->getGroupMembers(handle, group); return data->getGroupMembers(handle, group);
} }
/* Channels */ /* Channels */
PVR_ERROR OpenDialogChannelScan(void) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR OpenDialogChannelScan(void) { return PVR_ERROR_NOT_IMPLEMENTED; }
int GetChannelsAmount(void) int GetChannelsAmount(void) { return data->getChannelCount(); }
{
return data->getChannelCount();
}
PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio) PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio)
{ {
return data->getChannels(handle, bRadio); return data->getChannels(handle, bRadio);
} }
PVR_ERROR DeleteChannel(const PVR_CHANNEL& channel) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR DeleteChannel(const PVR_CHANNEL& channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR RenameChannel(const PVR_CHANNEL& channel) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR RenameChannel(const PVR_CHANNEL& channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR OpenDialogChannelSettings(const PVR_CHANNEL& channel) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR OpenDialogChannelSettings(const PVR_CHANNEL& channel)
PVR_ERROR OpenDialogChannelAdd(const PVR_CHANNEL& channel) { return PVR_ERROR_NOT_IMPLEMENTED; } {
return PVR_ERROR_NOT_IMPLEMENTED;
}
PVR_ERROR OpenDialogChannelAdd(const PVR_CHANNEL& channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
/* Recordings */ /* Recordings */
int GetRecordingsAmount(bool deleted) { return PVR_ERROR_NOT_IMPLEMENTED; } int GetRecordingsAmount(bool deleted) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR GetRecordings(ADDON_HANDLE handle, bool deleted) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR GetRecordings(ADDON_HANDLE handle, bool deleted) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR DeleteRecording(const PVR_RECORDING& recording) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR DeleteRecording(const PVR_RECORDING& recording) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR UndeleteRecording(const PVR_RECORDING& recording) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR UndeleteRecording(const PVR_RECORDING& recording) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR DeleteAllRecordingsFromTrash() { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR DeleteAllRecordingsFromTrash() { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR RenameRecording(const PVR_RECORDING& recording) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR RenameRecording(const PVR_RECORDING& recording) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR SetRecordingLifetime(const PVR_RECORDING*) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR SetRecordingLifetime(const PVR_RECORDING*) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR SetRecordingPlayCount(const PVR_RECORDING& recording, int count) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR SetRecordingPlayCount(const PVR_RECORDING& recording, int count)
PVR_ERROR SetRecordingLastPlayedPosition(const PVR_RECORDING& recording, int lastplayedposition) { return PVR_ERROR_NOT_IMPLEMENTED; } {
int GetRecordingLastPlayedPosition(const PVR_RECORDING& recording) { return PVR_ERROR_NOT_IMPLEMENTED; } return PVR_ERROR_NOT_IMPLEMENTED;
PVR_ERROR GetRecordingEdl(const PVR_RECORDING&, PVR_EDL_ENTRY edl[], int *size) { return PVR_ERROR_NOT_IMPLEMENTED; } }
PVR_ERROR GetRecordingSize(const PVR_RECORDING* recording, int64_t* sizeInBytes) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR SetRecordingLastPlayedPosition(const PVR_RECORDING& recording, int lastplayedposition)
PVR_ERROR GetTimerTypes(PVR_TIMER_TYPE types[], int *size) { return PVR_ERROR_NOT_IMPLEMENTED; } {
int GetTimersAmount(void) { return PVR_ERROR_NOT_IMPLEMENTED; } return PVR_ERROR_NOT_IMPLEMENTED;
PVR_ERROR GetTimers(ADDON_HANDLE handle) { return PVR_ERROR_NOT_IMPLEMENTED; } }
PVR_ERROR AddTimer(const PVR_TIMER& timer) { return PVR_ERROR_NOT_IMPLEMENTED; } int GetRecordingLastPlayedPosition(const PVR_RECORDING& recording)
PVR_ERROR DeleteTimer(const PVR_TIMER& timer, bool bForceDelete) { return PVR_ERROR_NOT_IMPLEMENTED; } {
PVR_ERROR UpdateTimer(const PVR_TIMER& timer) { return PVR_ERROR_NOT_IMPLEMENTED; } return PVR_ERROR_NOT_IMPLEMENTED;
}
PVR_ERROR GetRecordingEdl(const PVR_RECORDING&, PVR_EDL_ENTRY edl[], int* size)
{
return PVR_ERROR_NOT_IMPLEMENTED;
}
PVR_ERROR GetRecordingSize(const PVR_RECORDING* recording, int64_t* sizeInBytes)
{
return PVR_ERROR_NOT_IMPLEMENTED;
}
PVR_ERROR GetTimerTypes(PVR_TIMER_TYPE types[], int* size) { return PVR_ERROR_NOT_IMPLEMENTED; }
int GetTimersAmount(void) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR GetTimers(ADDON_HANDLE handle) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR AddTimer(const PVR_TIMER& timer) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR DeleteTimer(const PVR_TIMER& timer, bool bForceDelete)
{
return PVR_ERROR_NOT_IMPLEMENTED;
}
PVR_ERROR UpdateTimer(const PVR_TIMER& timer) { return PVR_ERROR_NOT_IMPLEMENTED; }
/* PVR stream properties handling */ /* PVR stream properties handling */
PVR_ERROR GetStreamReadChunkSize(int* chunksize) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR GetStreamReadChunkSize(int* chunksize) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR GetChannelStreamProperties(const PVR_CHANNEL*, PVR_NAMED_VALUE*, unsigned int*) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR GetChannelStreamProperties(const PVR_CHANNEL*, PVR_NAMED_VALUE*, unsigned int*)
PVR_ERROR GetRecordingStreamProperties(const PVR_RECORDING*, PVR_NAMED_VALUE*, unsigned int*) { return PVR_ERROR_NOT_IMPLEMENTED; } {
PVR_ERROR GetEPGTagStreamProperties(const EPG_TAG*, PVR_NAMED_VALUE*, unsigned int*) { return PVR_ERROR_NOT_IMPLEMENTED; } return PVR_ERROR_NOT_IMPLEMENTED;
PVR_ERROR GetEPGTagEdl(const EPG_TAG* epgTag, PVR_EDL_ENTRY edl[], int *size) { return PVR_ERROR_NOT_IMPLEMENTED; } }
PVR_ERROR GetRecordingStreamProperties(const PVR_RECORDING*, PVR_NAMED_VALUE*, unsigned int*)
{
return PVR_ERROR_NOT_IMPLEMENTED;
}
PVR_ERROR GetEPGTagStreamProperties(const EPG_TAG*, PVR_NAMED_VALUE*, unsigned int*)
{
return PVR_ERROR_NOT_IMPLEMENTED;
}
PVR_ERROR GetEPGTagEdl(const EPG_TAG* epgTag, PVR_EDL_ENTRY edl[], int* size)
{
return PVR_ERROR_NOT_IMPLEMENTED;
}
/* PVR stream handling */ /* PVR stream handling */
/* entirely unused, as we use standard RTSP+TS mux, which can be handlded by /* entirely unused, as we use standard RTSP+TS mux, which can be handlded by
* Kodi core */ * Kodi core */
bool OpenLiveStream(const PVR_CHANNEL& channel) { bool OpenLiveStream(const PVR_CHANNEL& channel)
return rtsp_open(data->getName(channel.iUniqueId), data->getUrl(channel.iUniqueId)); {
} return rtsp_open(data->GetName(channel.iUniqueId), data->GetUrl(channel.iUniqueId));
}
int ReadLiveStream(unsigned char* pBuffer, unsigned int iBufferSize) {
return rtsp_read(pBuffer, iBufferSize); int ReadLiveStream(unsigned char* pBuffer, unsigned int iBufferSize)
} {
return rtsp_read(pBuffer, iBufferSize);
void CloseLiveStream(void) { }
rtsp_close();
} void CloseLiveStream(void) { rtsp_close(); }
long long SeekLiveStream(long long iPosition, int iWhence) { return -1; } long long SeekLiveStream(long long iPosition, int iWhence) { return -1; }
long long LengthLiveStream(void) { return -1; } long long LengthLiveStream(void) { return -1; }
bool IsRealTimeStream(void) { return true; } bool IsRealTimeStream(void) { return true; }
PVR_ERROR GetSignalStatus(int channelUid, PVR_SIGNAL_STATUS* signalStatus) { PVR_ERROR GetSignalStatus(int channelUid, PVR_SIGNAL_STATUS* signalStatus)
memset(signalStatus, 0, sizeof(PVR_SIGNAL_STATUS)); {
rtsp_fill_signal_status(signalStatus); memset(signalStatus, 0, sizeof(PVR_SIGNAL_STATUS));
return PVR_ERROR_NO_ERROR; rtsp_fill_signal_status(signalStatus);
} return PVR_ERROR_NO_ERROR;
}
PVR_ERROR GetStreamTimes(PVR_STREAM_TIMES *times) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* pProperties) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR GetStreamTimes(PVR_STREAM_TIMES* times) { return PVR_ERROR_NOT_IMPLEMENTED; }
PVR_ERROR GetDescrambleInfo(int, PVR_DESCRAMBLE_INFO*) { return PVR_ERROR_NOT_IMPLEMENTED; } PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* pProperties)
{
/* Recording stream handling */ return PVR_ERROR_NOT_IMPLEMENTED;
bool OpenRecordedStream(const PVR_RECORDING& recording) { return false; } }
void CloseRecordedStream(void) {} PVR_ERROR GetDescrambleInfo(int, PVR_DESCRAMBLE_INFO*) { return PVR_ERROR_NOT_IMPLEMENTED; }
int ReadRecordedStream(unsigned char* pBuffer, unsigned int iBufferSize) { return -1; }
long long SeekRecordedStream(long long iPosition, int iWhence) { return -1; } /* Recording stream handling */
long long LengthRecordedStream(void) { return -1; } bool OpenRecordedStream(const PVR_RECORDING& recording) { return false; }
void CloseRecordedStream(void) {}
/* PVR demuxer */ int ReadRecordedStream(unsigned char* pBuffer, unsigned int iBufferSize) { return -1; }
/* entirey unused, as we use TS */ long long SeekRecordedStream(long long iPosition, int iWhence) { return -1; }
void DemuxReset(void) {} long long LengthRecordedStream(void) { return -1; }
void DemuxAbort(void) {}
void DemuxFlush(void) {} /* PVR demuxer */
DemuxPacket* DemuxRead(void) { return NULL; } /* entirey unused, as we use TS */
void FillBuffer(bool mode) {} void DemuxReset(void) {}
void DemuxAbort(void) {}
/* Various helper functions */ void DemuxFlush(void) {}
bool CanPauseStream() { return false; } DemuxPacket* DemuxRead(void) { return nullptr; }
bool CanSeekStream() { return false; } void FillBuffer(bool mode) {}
/* Callbacks */ /* Various helper functions */
void PauseStream(bool bPaused) {} bool CanPauseStream() { return false; }
bool SeekTime(double time, bool backwards, double *startpts) { return false; } bool CanSeekStream() { return false; }
void SetSpeed(int speed) {}
PVR_ERROR SetEPGTimeFrame(int) { return PVR_ERROR_NOT_IMPLEMENTED; } /* Callbacks */
void PauseStream(bool bPaused) {}
const char* GetBackendHostname() bool SeekTime(double time, bool backwards, double* startpts) { return false; }
{ void SetSpeed(int speed) {}
return octonetAddress.c_str(); PVR_ERROR SetEPGTimeFrame(int) { return PVR_ERROR_NOT_IMPLEMENTED; }
}
const char* GetBackendHostname() { return octonetAddress.c_str(); }
} }

View File

@ -17,8 +17,8 @@
#define __func__ __FUNCTION__ #define __func__ __FUNCTION__
#endif #endif
extern ADDON::CHelper_libXBMC_addon *libKodi; extern ADDON::CHelper_libXBMC_addon* libKodi;
extern CHelper_libXBMC_pvr *pvr; extern CHelper_libXBMC_pvr* pvr;
/* IP or hostname of the octonet to be connected to */ /* IP or hostname of the octonet to be connected to */
extern std::string octonetAddress; extern std::string octonetAddress;

View File

@ -1,32 +1,44 @@
/*
* Copyright (C) 2005-2020 Team Kodi
* https://kodi.tv
*
* SPDX-License-Identifier: GPL-2.0-or-later
* See LICENSE.md for more information.
*/
#include "rtsp_client.hpp" #include "rtsp_client.hpp"
#include <algorithm>
#include <cctype>
#include <iterator>
#include "Socket.h" #include "Socket.h"
#include "client.h" #include "client.h"
#include <p8-platform/util/util.h>
#include <kodi/libXBMC_addon.h> #include <algorithm>
#include <cctype>
#include <cstring> #include <cstring>
#include <iterator>
#include <kodi/libXBMC_addon.h>
#include <p8-platform/util/util.h>
#include <sstream> #include <sstream>
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
#define strtok_r strtok_s #define strtok_r strtok_s
#define strncasecmp _strnicmp #define strncasecmp _strnicmp
int vasprintf(char **sptr, char *fmt, va_list argv) { int vasprintf(char** sptr, char* fmt, va_list argv)
int wanted = vsnprintf(*sptr = NULL, 0, fmt, argv); {
if((wanted < 0) || ((*sptr = (char *)malloc(1 + wanted)) == NULL)) int wanted = vsnprintf(*sptr = nullptr, 0, fmt, argv);
return -1; if ((wanted < 0) || ((*sptr = (char*)malloc(1 + wanted)) == nullptr))
return vsprintf(*sptr, fmt, argv); return -1;
return vsprintf(*sptr, fmt, argv);
} }
int asprintf(char **sptr, char *fmt, ...) { int asprintf(char** sptr, char* fmt, ...)
int retval; {
va_list argv; int retval;
va_start(argv, fmt); va_list argv;
retval = vasprintf(sptr, fmt, argv); va_start(argv, fmt);
va_end(argv); retval = vasprintf(sptr, fmt, argv);
return retval; va_end(argv);
return retval;
} }
#endif #endif
@ -43,438 +55,498 @@ using namespace std;
using namespace ADDON; using namespace ADDON;
using namespace OCTO; using namespace OCTO;
enum rtsp_state { enum rtsp_state
RTSP_IDLE, {
RTSP_DESCRIBE, RTSP_IDLE,
RTSP_SETUP, RTSP_DESCRIBE,
RTSP_PLAY, RTSP_SETUP,
RTSP_RUNNING RTSP_PLAY,
RTSP_RUNNING
}; };
enum rtsp_result { enum rtsp_result
RTSP_RESULT_OK = 200, {
RTSP_RESULT_OK = 200,
}; };
struct rtsp_client { struct rtsp_client
char *content_base; {
char *control; char* content_base;
char session_id[64]; char* control;
uint16_t stream_id; char session_id[64];
int keepalive_interval; uint16_t stream_id;
int keepalive_interval;
char udp_address[UDP_ADDRESS_LEN]; char udp_address[UDP_ADDRESS_LEN];
uint16_t udp_port; uint16_t udp_port;
Socket tcp_sock; Socket tcp_sock;
Socket udp_sock; Socket udp_sock;
Socket rtcp_sock; Socket rtcp_sock;
enum rtsp_state state; enum rtsp_state state;
int cseq; int cseq;
size_t fifo_size; size_t fifo_size;
uint16_t last_seq_nr; uint16_t last_seq_nr;
string name; string name;
int level; int level;
int quality; int quality;
}; };
struct url { struct url
string protocol; {
string host; string protocol;
int port; string host;
string path; int port;
string path;
}; };
struct rtcp_app { struct rtcp_app
uint8_t subtype; {
uint8_t pt; uint8_t subtype;
uint16_t len; uint8_t pt;
uint32_t ssrc; uint16_t len;
char name[4]; uint32_t ssrc;
uint16_t identifier; char name[4];
uint16_t string_len; uint16_t identifier;
uint16_t string_len;
}; };
static rtsp_client *rtsp = NULL; static rtsp_client* rtsp = nullptr;
static url parse_url(const std::string& str) { static url parse_url(const std::string& str)
static const string prot_end = "://"; {
static const string host_end = "/"; static const string prot_end = "://";
url result; static const string host_end = "/";
url result;
string::const_iterator begin = str.begin(); string::const_iterator begin = str.begin();
string::const_iterator end = search(begin, str.end(), prot_end.begin(), prot_end.end()); string::const_iterator end = search(begin, str.end(), prot_end.begin(), prot_end.end());
result.protocol.reserve(distance(begin, end)); result.protocol.reserve(distance(begin, end));
transform(begin, end, back_inserter(result.protocol), ::tolower); transform(begin, end, back_inserter(result.protocol), ::tolower);
advance(end, prot_end.size()); advance(end, prot_end.size());
begin = end; begin = end;
end = search(begin, str.end(), host_end.begin(), host_end.end()); end = search(begin, str.end(), host_end.begin(), host_end.end());
result.host.reserve(distance(begin, end)); result.host.reserve(distance(begin, end));
transform(begin, end, back_inserter(result.host), ::tolower); transform(begin, end, back_inserter(result.host), ::tolower);
advance(end, host_end.size()); advance(end, host_end.size());
begin = end; begin = end;
result.port = RTSP_DEFAULT_PORT; result.port = RTSP_DEFAULT_PORT;
result.path.reserve(distance(begin, str.end())); result.path.reserve(distance(begin, str.end()));
transform(begin, str.end(), back_inserter(result.path), ::tolower); transform(begin, str.end(), back_inserter(result.path), ::tolower);
return result; return result;
} }
void split_string(const string& s, char delim, vector<string>& elems) { void split_string(const string& s, char delim, vector<string>& elems)
stringstream ss; {
ss.str(s); stringstream ss;
ss.str(s);
string item; string item;
while(getline(ss, item, delim)) { while (getline(ss, item, delim))
elems.push_back(item); {
} elems.push_back(item);
}
} }
static int tcp_sock_read_line(string &line) { static int tcp_sock_read_line(string& line)
static string buf; {
static string buf;
while(true) { while (true)
string::size_type pos = buf.find("\r\n"); {
if(pos != string::npos) { string::size_type pos = buf.find("\r\n");
line = buf.substr(0, pos); if (pos != string::npos)
buf.erase(0, pos + 2); {
return 0; line = buf.substr(0, pos);
} buf.erase(0, pos + 2);
return 0;
}
char tmp_buf[2048]; char tmp_buf[2048];
int size = rtsp->tcp_sock.receive(tmp_buf, sizeof(tmp_buf), 1); int size = rtsp->tcp_sock.receive(tmp_buf, sizeof(tmp_buf), 1);
if(size <= 0) { if (size <= 0)
return 1; {
} return 1;
}
buf.append(&tmp_buf[0], &tmp_buf[size]); buf.append(&tmp_buf[0], &tmp_buf[size]);
} }
} }
static string compose_url(const url& u) static string compose_url(const url& u)
{ {
stringstream res; stringstream res;
res << u.protocol << "://" << u.host; res << u.protocol << "://" << u.host;
if (u.port > 0) if (u.port > 0)
res << ":" << u.port; res << ":" << u.port;
res << "/" << u.path; res << "/" << u.path;
return res.str(); return res.str();
} }
static void parse_session(char *request_line, char *session, unsigned max, int *timeout) { static void parse_session(char* request_line, char* session, unsigned max, int* timeout)
char *state;
char *tok;
tok = strtok_r(request_line, ";", &state);
if (tok == NULL)
return;
strncpy(session, tok, min(strlen(tok), (size_t)(max - 1)));
while ((tok = strtok_r(NULL, ";", &state)) != NULL) {
if (strncmp(tok, "timeout=", 8) == 0) {
*timeout = atoi(tok + 8);
if (*timeout > 5)
*timeout -= KEEPALIVE_MARGIN;
else if (*timeout > 0)
*timeout = 1;
}
}
}
static int parse_port(char *str, uint16_t *port)
{ {
int p = atoi(str); char* state;
if (p < 0 || p > UINT16_MAX) char* tok;
return -1;
*port = p; tok = strtok_r(request_line, ";", &state);
if (tok == nullptr)
return;
strncpy(session, tok, min(strlen(tok), (size_t)(max - 1)));
return 0; while ((tok = strtok_r(nullptr, ";", &state)) != nullptr)
{
if (strncmp(tok, "timeout=", 8) == 0)
{
*timeout = atoi(tok + 8);
if (*timeout > 5)
*timeout -= KEEPALIVE_MARGIN;
else if (*timeout > 0)
*timeout = 1;
}
}
} }
static int parse_transport(char *request_line) { static int parse_port(char* str, uint16_t* port)
char *state; {
char *tok; int p = atoi(str);
int err; if (p < 0 || p > UINT16_MAX)
return -1;
tok = strtok_r(request_line, ";", &state); *port = p;
if (tok == NULL || strncmp(tok, "RTP/AVP", 7) != 0)
return -1;
tok = strtok_r(NULL, ";", &state); return 0;
if (tok == NULL || strncmp(tok, "multicast", 9) != 0)
return 0;
while ((tok = strtok_r(NULL, ";", &state)) != NULL) {
if (strncmp(tok, "destination=", 12) == 0) {
strncpy(rtsp->udp_address, tok + 12, min(strlen(tok + 12), (size_t)(UDP_ADDRESS_LEN - 1)));
} else if (strncmp(tok, "port=", 5) == 0) {
char port[6];
char *end;
memset(port, 0x00, 6);
strncpy(port, tok + 5, min(strlen(tok + 5), (size_t)5));
if ((end = strstr(port, "-")) != NULL)
*end = '\0';
err = parse_port(port, &rtsp->udp_port);
if (err)
return err;
}
}
return 0;
} }
#define skip_whitespace(x) while(*x == ' ') x++ static int parse_transport(char* request_line)
static enum rtsp_result rtsp_handle() { {
uint8_t buffer[512]; char* state;
int rtsp_result = 0; char* tok;
bool have_header = false; int err;
size_t content_length = 0;
size_t read = 0;
char *in, *val;
string in_str;
/* Parse header */ tok = strtok_r(request_line, ";", &state);
while (!have_header) { if (tok == nullptr || strncmp(tok, "RTP/AVP", 7) != 0)
if (tcp_sock_read_line(in_str) < 0) return -1;
break;
in = const_cast<char *>(in_str.c_str());
if (strncmp(in, "RTSP/1.0 ", 9) == 0) { tok = strtok_r(nullptr, ";", &state);
rtsp_result = atoi(in + 9); if (tok == nullptr || strncmp(tok, "multicast", 9) != 0)
} else if (strncmp(in, "Content-Base:", 13) == 0) { return 0;
free(rtsp->content_base);
val = in + 13; while ((tok = strtok_r(nullptr, ";", &state)) != nullptr)
skip_whitespace(val); {
if (strncmp(tok, "destination=", 12) == 0)
{
strncpy(rtsp->udp_address, tok + 12, min(strlen(tok + 12), (size_t)(UDP_ADDRESS_LEN - 1)));
}
else if (strncmp(tok, "port=", 5) == 0)
{
char port[6];
char* end;
rtsp->content_base = strdup(val); memset(port, 0x00, 6);
} else if (strncmp(in, "Content-Length:", 15) == 0) { strncpy(port, tok + 5, min(strlen(tok + 5), (size_t)5));
val = in + 16; if ((end = strstr(port, "-")) != nullptr)
skip_whitespace(val); *end = '\0';
err = parse_port(port, &rtsp->udp_port);
if (err)
return err;
}
}
content_length = atoi(val); return 0;
} else if (strncmp("Session:", in, 8) == 0) { }
val = in + 8;
skip_whitespace(val);
parse_session(val, rtsp->session_id, 64, &rtsp->keepalive_interval); #define skip_whitespace(x) \
} else if (strncmp("Transport:", in, 10) == 0) { while (*x == ' ') \
val = in + 10; x++
skip_whitespace(val); static enum rtsp_result rtsp_handle()
{
uint8_t buffer[512];
int rtsp_result = 0;
bool have_header = false;
size_t content_length = 0;
size_t read = 0;
char *in, *val;
string in_str;
if (parse_transport(val) != 0) { /* Parse header */
rtsp_result = -1; while (!have_header)
break; {
} if (tcp_sock_read_line(in_str) < 0)
} else if (strncmp("com.ses.streamID:", in, 17) == 0) { break;
val = in + 17; in = const_cast<char*>(in_str.c_str());
skip_whitespace(val);
rtsp->stream_id = atoi(val); if (strncmp(in, "RTSP/1.0 ", 9) == 0)
} else if (in[0] == '\0') { {
have_header = true; rtsp_result = atoi(in + 9);
} }
} else if (strncmp(in, "Content-Base:", 13) == 0)
{
free(rtsp->content_base);
/* Discard further content */ val = in + 13;
while (content_length > 0 && skip_whitespace(val);
(read = rtsp->tcp_sock.receive((char*)buffer, sizeof(buffer), min(sizeof(buffer), content_length))))
content_length -= read;
return (enum rtsp_result)rtsp_result; rtsp->content_base = strdup(val);
}
else if (strncmp(in, "Content-Length:", 15) == 0)
{
val = in + 16;
skip_whitespace(val);
content_length = atoi(val);
}
else if (strncmp("Session:", in, 8) == 0)
{
val = in + 8;
skip_whitespace(val);
parse_session(val, rtsp->session_id, 64, &rtsp->keepalive_interval);
}
else if (strncmp("Transport:", in, 10) == 0)
{
val = in + 10;
skip_whitespace(val);
if (parse_transport(val) != 0)
{
rtsp_result = -1;
break;
}
}
else if (strncmp("com.ses.streamID:", in, 17) == 0)
{
val = in + 17;
skip_whitespace(val);
rtsp->stream_id = atoi(val);
}
else if (in[0] == '\0')
{
have_header = true;
}
}
/* Discard further content */
while (content_length > 0 && (read = rtsp->tcp_sock.receive((char*)buffer, sizeof(buffer),
min(sizeof(buffer), content_length))))
content_length -= read;
return (enum rtsp_result)rtsp_result;
} }
bool rtsp_open(const string& name, const string& url_str) bool rtsp_open(const string& name, const string& url_str)
{ {
string setup_url_str; string setup_url_str;
const char *psz_setup_url; const char* psz_setup_url;
stringstream setup_ss; stringstream setup_ss;
stringstream play_ss; stringstream play_ss;
url setup_url; url setup_url;
rtsp_close(); rtsp_close();
rtsp = new rtsp_client(); rtsp = new rtsp_client();
if (rtsp == NULL) if (rtsp == nullptr)
return false; return false;
rtsp->name = name; rtsp->name = name;
rtsp->level = 0; rtsp->level = 0;
rtsp->quality = 0; rtsp->quality = 0;
libKodi->Log(LOG_DEBUG, "try to open '%s'", url_str.c_str()); libKodi->Log(LOG_DEBUG, "try to open '%s'", url_str.c_str());
url dst = parse_url(url_str); url dst = parse_url(url_str);
libKodi->Log(LOG_DEBUG, "connect to host '%s'", dst.host.c_str()); libKodi->Log(LOG_DEBUG, "connect to host '%s'", dst.host.c_str());
if(!rtsp->tcp_sock.connect(dst.host, dst.port)) { if (!rtsp->tcp_sock.connect(dst.host, dst.port))
libKodi->Log(LOG_ERROR, "Failed to connect to RTSP server %s:%d", dst.host.c_str(), dst.port); {
goto error; libKodi->Log(LOG_ERROR, "Failed to connect to RTSP server %s:%d", dst.host.c_str(), dst.port);
} goto error;
}
// TODO: tcp keep alive? // TODO: tcp keep alive?
if (asprintf(&rtsp->content_base, "rtsp://%s:%d/", dst.host.c_str(), if (asprintf(&rtsp->content_base, "rtsp://%s:%d/", dst.host.c_str(), dst.port) < 0)
dst.port) < 0) { {
rtsp->content_base = NULL; rtsp->content_base = nullptr;
goto error; goto error;
} }
rtsp->last_seq_nr = 0; rtsp->last_seq_nr = 0;
rtsp->keepalive_interval = (KEEPALIVE_INTERVAL - KEEPALIVE_MARGIN); rtsp->keepalive_interval = (KEEPALIVE_INTERVAL - KEEPALIVE_MARGIN);
setup_url = dst; setup_url = dst;
// reverse the satip protocol trick, as SAT>IP believes to be RTSP // reverse the satip protocol trick, as SAT>IP believes to be RTSP
if (!strncasecmp(setup_url.protocol.c_str(), "satip", 5)) { if (!strncasecmp(setup_url.protocol.c_str(), "satip", 5))
setup_url.protocol = "rtsp"; {
} setup_url.protocol = "rtsp";
}
setup_url_str = compose_url(setup_url); setup_url_str = compose_url(setup_url);
psz_setup_url = setup_url_str.c_str(); psz_setup_url = setup_url_str.c_str();
// TODO: Find available port // TODO: Find available port
rtsp->udp_sock = Socket(af_inet, pf_inet, sock_dgram, udp); rtsp->udp_sock = Socket(af_inet, pf_inet, sock_dgram, udp);
rtsp->udp_port = 6785; rtsp->udp_port = 6785;
if(!rtsp->udp_sock.bind(rtsp->udp_port)) { if (!rtsp->udp_sock.bind(rtsp->udp_port))
goto error; {
} goto error;
}
setup_ss << "SETUP " << setup_url_str<< " RTSP/1.0\r\n"; setup_ss << "SETUP " << setup_url_str << " RTSP/1.0\r\n";
setup_ss << "CSeq: " << rtsp->cseq++ << "\r\n"; setup_ss << "CSeq: " << rtsp->cseq++ << "\r\n";
setup_ss << "Transport: RTP/AVP;unicast;client_port=" << rtsp->udp_port << "-" << (rtsp->udp_port + 1) << "\r\n\r\n"; setup_ss << "Transport: RTP/AVP;unicast;client_port=" << rtsp->udp_port << "-"
rtsp->tcp_sock.send(setup_ss.str()); << (rtsp->udp_port + 1) << "\r\n\r\n";
rtsp->tcp_sock.send(setup_ss.str());
if (rtsp_handle() != RTSP_RESULT_OK) { if (rtsp_handle() != RTSP_RESULT_OK)
libKodi->Log(LOG_ERROR, "Failed to setup RTSP session"); {
goto error; libKodi->Log(LOG_ERROR, "Failed to setup RTSP session");
} goto error;
}
if (asprintf(&rtsp->control, "%sstream=%d", rtsp->content_base, rtsp->stream_id) < 0) { if (asprintf(&rtsp->control, "%sstream=%d", rtsp->content_base, rtsp->stream_id) < 0)
rtsp->control = NULL; {
goto error; rtsp->control = nullptr;
} goto error;
}
play_ss << "PLAY " << rtsp->control << " RTSP/1.0\r\n"; play_ss << "PLAY " << rtsp->control << " RTSP/1.0\r\n";
play_ss << "CSeq: " << rtsp->cseq++ << "\r\n"; play_ss << "CSeq: " << rtsp->cseq++ << "\r\n";
play_ss << "Session: " << rtsp->session_id << "\r\n\r\n"; play_ss << "Session: " << rtsp->session_id << "\r\n\r\n";
rtsp->tcp_sock.send(play_ss.str()); rtsp->tcp_sock.send(play_ss.str());
if (rtsp_handle() != RTSP_RESULT_OK) { if (rtsp_handle() != RTSP_RESULT_OK)
libKodi->Log(LOG_ERROR, "Failed to play RTSP session"); {
goto error; libKodi->Log(LOG_ERROR, "Failed to play RTSP session");
} goto error;
}
rtsp->rtcp_sock = Socket(af_inet, pf_inet, sock_dgram, udp); rtsp->rtcp_sock = Socket(af_inet, pf_inet, sock_dgram, udp);
if(!rtsp->rtcp_sock.bind(rtsp->udp_port + 1)) { if (!rtsp->rtcp_sock.bind(rtsp->udp_port + 1))
goto error; {
} goto error;
if(!rtsp->rtcp_sock.set_non_blocking(true)) { }
goto error; if (!rtsp->rtcp_sock.set_non_blocking(true))
} {
goto error;
}
return true; return true;
error: error:
rtsp_close(); rtsp_close();
return false; return false;
} }
static void parse_rtcp(const char *buf, int size) { static void parse_rtcp(const char* buf, int size)
int offset = 0; {
while(size > 4) { int offset = 0;
const rtcp_app *app = reinterpret_cast<const rtcp_app *>(buf + offset); while (size > 4)
uint16_t len = 4 * (ntohs(app->len) + 1); {
const rtcp_app* app = reinterpret_cast<const rtcp_app*>(buf + offset);
uint16_t len = 4 * (ntohs(app->len) + 1);
if((app->pt != 204) || (memcmp(app->name, "SES1", 4) != 0)) { if ((app->pt != 204) || (memcmp(app->name, "SES1", 4) != 0))
size -= len; {
offset += len; size -= len;
continue; offset += len;
} continue;
}
uint16_t string_len = ntohs(app->string_len); uint16_t string_len = ntohs(app->string_len);
string app_data(&buf[offset + sizeof(rtcp_app)], string_len); string app_data(&buf[offset + sizeof(rtcp_app)], string_len);
vector<string> elems; vector<string> elems;
split_string(app_data, ';', elems); split_string(app_data, ';', elems);
if(elems.size() != 4) { if (elems.size() != 4)
return; {
} return;
}
vector<string> tuner; vector<string> tuner;
split_string(elems[2], ',', tuner); split_string(elems[2], ',', tuner);
if(tuner.size() < 4) { if (tuner.size() < 4)
return; {
} return;
}
rtsp->level = atoi(tuner[1].c_str()); rtsp->level = atoi(tuner[1].c_str());
rtsp->quality = atoi(tuner[3].c_str()); rtsp->quality = atoi(tuner[3].c_str());
return; return;
} }
} }
int rtsp_read(void *buf, unsigned buf_size) { int rtsp_read(void* buf, unsigned buf_size)
sockaddr addr; {
socklen_t addr_len = sizeof(addr); sockaddr addr;
int ret = rtsp->udp_sock.recvfrom((char *)buf, buf_size, (sockaddr *)&addr, &addr_len); socklen_t addr_len = sizeof(addr);
int ret = rtsp->udp_sock.recvfrom((char*)buf, buf_size, (sockaddr*)&addr, &addr_len);
char rtcp_buf[RTCP_BUFFER_SIZE]; char rtcp_buf[RTCP_BUFFER_SIZE];
int rtcp_len = rtsp->rtcp_sock.recvfrom(rtcp_buf, RTCP_BUFFER_SIZE, (sockaddr *)&addr, &addr_len); int rtcp_len = rtsp->rtcp_sock.recvfrom(rtcp_buf, RTCP_BUFFER_SIZE, (sockaddr*)&addr, &addr_len);
parse_rtcp(rtcp_buf, rtcp_len); parse_rtcp(rtcp_buf, rtcp_len);
// TODO: check ip // TODO: check ip
return ret; return ret;
} }
static void rtsp_teardown() { static void rtsp_teardown()
if(!rtsp->tcp_sock.is_valid()) { {
return; if (!rtsp->tcp_sock.is_valid())
} {
return;
}
if (rtsp->session_id[0] > 0) { if (rtsp->session_id[0] > 0)
char *msg; {
int len; char* msg;
stringstream ss; int len;
stringstream ss;
rtsp->udp_sock.close(); rtsp->udp_sock.close();
ss << "TEARDOWN " << rtsp->control << " RTSP/1.0\r\n"; ss << "TEARDOWN " << rtsp->control << " RTSP/1.0\r\n";
ss << "CSeq: " << rtsp->cseq++ << "\r\n"; ss << "CSeq: " << rtsp->cseq++ << "\r\n";
ss << "Session: " << rtsp->session_id << "\r\n\r\n"; ss << "Session: " << rtsp->session_id << "\r\n\r\n";
rtsp->tcp_sock.send(ss.str()); rtsp->tcp_sock.send(ss.str());
if (rtsp_handle() != RTSP_RESULT_OK) { if (rtsp_handle() != RTSP_RESULT_OK)
libKodi->Log(LOG_ERROR, "Failed to teardown RTSP session"); {
return; libKodi->Log(LOG_ERROR, "Failed to teardown RTSP session");
} return;
} }
}
} }
void rtsp_close() void rtsp_close()
{ {
if(rtsp) { if (rtsp)
rtsp_teardown(); {
rtsp->tcp_sock.close(); rtsp_teardown();
rtsp->udp_sock.close(); rtsp->tcp_sock.close();
rtsp->rtcp_sock.close(); rtsp->udp_sock.close();
delete rtsp; rtsp->rtcp_sock.close();
rtsp = NULL; delete rtsp;
} rtsp = nullptr;
}
} }
void rtsp_fill_signal_status(PVR_SIGNAL_STATUS* signal_status) { void rtsp_fill_signal_status(PVR_SIGNAL_STATUS* signal_status)
if(rtsp) { {
strncpy(signal_status->strServiceName, rtsp->name.c_str(), PVR_ADDON_NAME_STRING_LENGTH - 1); if (rtsp)
signal_status->iSNR = 0x1111 * rtsp->quality; {
signal_status->iSignal = 0x101 * rtsp->level; strncpy(signal_status->strServiceName, rtsp->name.c_str(), PVR_ADDON_NAME_STRING_LENGTH - 1);
} signal_status->iSNR = 0x1111 * rtsp->quality;
signal_status->iSignal = 0x101 * rtsp->level;
}
} }

View File

@ -1,12 +1,18 @@
#ifndef _RTSP_CLIENT_HPP_ /*
#define _RTSP_CLIENT_HPP_ * Copyright (C) 2005-2020 Team Kodi
* https://kodi.tv
*
* SPDX-License-Identifier: GPL-2.0-or-later
* See LICENSE.md for more information.
*/
#pragma once
#include <string>
#include <kodi/xbmc_pvr_types.h> #include <kodi/xbmc_pvr_types.h>
#include <string>
bool rtsp_open(const std::string& name, const std::string& url_str); bool rtsp_open(const std::string& name, const std::string& url_str);
void rtsp_close(); void rtsp_close();
int rtsp_read(void *buf, unsigned buf_size); int rtsp_read(void* buf, unsigned buf_size);
void rtsp_fill_signal_status(PVR_SIGNAL_STATUS* signal_status); void rtsp_fill_signal_status(PVR_SIGNAL_STATUS* signal_status);
#endif