diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..8ef33f0 --- /dev/null +++ b/.clang-format @@ -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 +... diff --git a/src/OctonetData.cpp b/src/OctonetData.cpp index cc5a3a6..6aa50aa 100644 --- a/src/OctonetData.cpp +++ b/src/OctonetData.cpp @@ -8,12 +8,11 @@ * */ -#include -#include +#include "OctonetData.h" #include - -#include "OctonetData.h" +#include +#include #ifdef __WINDOWS__ #define timegm _mkgmtime @@ -23,341 +22,361 @@ using namespace ADDON; OctonetData::OctonetData() { - serverAddress = octonetAddress; - channels.clear(); - groups.clear(); - lastEpgLoad = 0; + m_serverAddress = octonetAddress; + m_channels.clear(); + m_groups.clear(); + m_lastEpgLoad = 0; - if (!loadChannelList()) - libKodi->QueueNotification(QUEUE_ERROR, libKodi->GetLocalizedString(30001), channels.size()); + if (!LoadChannelList()) + libKodi->QueueNotification(QUEUE_ERROR, libKodi->GetLocalizedString(30001), m_channels.size()); } OctonetData::~OctonetData(void) { - channels.clear(); - groups.clear(); + m_channels.clear(); + m_groups.clear(); } -int64_t OctonetData::parseID(std::string id) +int64_t OctonetData::ParseID(std::string id) { - std::hash hash_fn; - int64_t nativeId = hash_fn(id); + std::hash hash_fn; + int64_t nativeId = hash_fn(id); - return nativeId; + return nativeId; } -bool OctonetData::loadChannelList() +bool OctonetData::LoadChannelList() { - std::string jsonContent; - void *f = libKodi->OpenFile(("http://" + serverAddress + "/channellist.lua?select=json").c_str(), 0); - if (!f) - return false; + std::string jsonContent; + void* f = + libKodi->OpenFile(("http://" + m_serverAddress + "/channellist.lua?select=json").c_str(), 0); + if (!f) + return false; - char buf[1024]; - while (int read = libKodi->ReadFile(f, buf, 1024)) - jsonContent.append(buf, read); + char buf[1024]; + while (int read = libKodi->ReadFile(f, buf, 1024)) + jsonContent.append(buf, read); - libKodi->CloseFile(f); + libKodi->CloseFile(f); - Json::Value root; - Json::Reader reader; + Json::Value root; + Json::Reader reader; - if (!reader.parse(jsonContent, root, false)) - return false; + if (!reader.parse(jsonContent, root, false)) + return false; - const Json::Value groupList = root["GroupList"]; - for (unsigned int i = 0; i < groupList.size(); i++) { - const Json::Value channelList = groupList[i]["ChannelList"]; - OctonetGroup group; + const Json::Value groupList = root["GroupList"]; + for (unsigned int i = 0; i < groupList.size(); i++) + { + const Json::Value channelList = groupList[i]["ChannelList"]; + OctonetGroup group; - group.name = groupList[i]["Title"].asString(); - group.radio = group.name.compare(0, 5, "Radio") ? false : true; + group.name = groupList[i]["Title"].asString(); + group.radio = group.name.compare(0, 5, "Radio") ? false : true; - for (unsigned int j = 0; j < channelList.size(); j++) { - const Json::Value channel = channelList[j]; - OctonetChannel chan; + for (unsigned int j = 0; j < channelList.size(); j++) + { + const Json::Value channel = channelList[j]; + OctonetChannel chan; - chan.name = channel["Title"].asString(); - chan.url = "rtsp://" + serverAddress + "/" + channel["Request"].asString(); - chan.radio = group.radio; - chan.nativeId = parseID(channel["ID"].asString()); + chan.name = channel["Title"].asString(); + chan.url = "rtsp://" + m_serverAddress + "/" + channel["Request"].asString(); + chan.radio = group.radio; + chan.nativeId = ParseID(channel["ID"].asString()); - chan.id = 1000 + channels.size(); - group.members.push_back(channels.size()); - channels.push_back(chan); - } - groups.push_back(group); - } + chan.id = 1000 + m_channels.size(); + group.members.push_back(m_channels.size()); + m_channels.push_back(chan); + } + m_groups.push_back(group); + } - return true; + return true; } -OctonetChannel* OctonetData::findChannel(int64_t nativeId) +OctonetChannel* OctonetData::FindChannel(int64_t nativeId) { - std::vector::iterator it; - for (it = channels.begin(); it < channels.end(); ++it) { - if (it->nativeId == nativeId) - return &*it; - } + std::vector::iterator it; + for (it = m_channels.begin(); it < m_channels.end(); ++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) { - sscanf(date.c_str(), "%04d-%02d-%02dT%02d:%02d:%02dZ", - &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday, - &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec); - timeinfo.tm_mon -= 1; - timeinfo.tm_year -= 1900; - } else { - sscanf(date.c_str(), "%02d:%02d:%02d", - &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec); - timeinfo.tm_year = 70; // unix timestamps start 1970 - timeinfo.tm_mday = 1; - } + if (date.length() > 8) + { + sscanf(date.c_str(), "%04d-%02d-%02dT%02d:%02d:%02dZ", &timeinfo.tm_year, &timeinfo.tm_mon, + &timeinfo.tm_mday, &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec); + timeinfo.tm_mon -= 1; + timeinfo.tm_year -= 1900; + } + else + { + sscanf(date.c_str(), "%02d:%02d:%02d", &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec); + 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 */ - if (lastEpgLoad + 30 > time(NULL)) - return false; + /* Reload at most every 30 seconds */ + if (m_lastEpgLoad + 30 > time(NULL)) + return false; - std::string jsonContent; - void *f = libKodi->OpenFile(("http://" + serverAddress + "/epg.lua?;#|encoding=gzip").c_str(), 0); - if (!f) - return false; + std::string jsonContent; + void* f = libKodi->OpenFile(("http://" + m_serverAddress + "/epg.lua?;#|encoding=gzip").c_str(), 0); + if (!f) + return false; - char buf[1024]; - while (int read = libKodi->ReadFile(f, buf, 1024)) - jsonContent.append(buf, read); + char buf[1024]; + while (int read = libKodi->ReadFile(f, buf, 1024)) + jsonContent.append(buf, read); - libKodi->CloseFile(f); + libKodi->CloseFile(f); - Json::Value root; - Json::Reader reader; + Json::Value root; + Json::Reader reader; - if (!reader.parse(jsonContent, root, false)) - return false; + if (!reader.parse(jsonContent, root, false)) + return false; - const Json::Value eventList = root["EventList"]; - OctonetChannel *channel = NULL; - for (unsigned int i = 0; i < eventList.size(); i++) { - const Json::Value event = eventList[i]; - OctonetEpgEntry entry; + const Json::Value eventList = root["EventList"]; + OctonetChannel* channel = NULL; + for (unsigned int i = 0; i < eventList.size(); i++) + { + const Json::Value event = eventList[i]; + OctonetEpgEntry entry; - entry.start = parseDateTime(event["Time"].asString()); - entry.end = entry.start + parseDateTime(event["Duration"].asString()); - entry.title = event["Name"].asString(); - entry.subtitle = event["Text"].asString(); - std::string channelId = event["ID"].asString(); - std::string epgId = channelId.substr(channelId.rfind(":") + 1); - channelId = channelId.substr(0, channelId.rfind(":")); + entry.start = ParseDateTime(event["Time"].asString()); + entry.end = entry.start + ParseDateTime(event["Duration"].asString()); + entry.title = event["Name"].asString(); + entry.subtitle = event["Text"].asString(); + std::string channelId = event["ID"].asString(); + std::string epgId = channelId.substr(channelId.rfind(":") + 1); + channelId = channelId.substr(0, channelId.rfind(":")); - entry.channelId = parseID(channelId); - entry.id = atoi(epgId.c_str()); + entry.channelId = ParseID(channelId); + entry.id = atoi(epgId.c_str()); - if (channel == NULL || channel->nativeId != entry.channelId) - channel = findChannel(entry.channelId); + if (channel == NULL || channel->nativeId != entry.channelId) + channel = FindChannel(entry.channelId); - if (channel == NULL) { - libKodi->Log(LOG_ERROR, "EPG for unknown channel."); - continue; - } + if (channel == NULL) + { + libKodi->Log(LOG_ERROR, "EPG for unknown channel."); + continue; + } - channel->epg.push_back(entry); - } + channel->epg.push_back(entry); + } - lastEpgLoad = time(NULL); - return true; + m_lastEpgLoad = time(NULL); + return true; } -void *OctonetData::Process(void) +void* OctonetData::Process(void) { - return NULL; + return NULL; } int OctonetData::getChannelCount(void) { - return channels.size(); + return m_channels.size(); } PVR_ERROR OctonetData::getChannels(ADDON_HANDLE handle, bool bRadio) { - for (unsigned int i = 0; i < channels.size(); i++) - { - OctonetChannel &channel = channels.at(i); - if (channel.radio == bRadio) - { - PVR_CHANNEL chan; - memset(&chan, 0, sizeof(PVR_CHANNEL)); + for (unsigned int i = 0; i < m_channels.size(); i++) + { + OctonetChannel& channel = m_channels.at(i); + if (channel.radio == bRadio) + { + PVR_CHANNEL chan; + memset(&chan, 0, sizeof(PVR_CHANNEL)); - chan.iUniqueId = channel.id; - chan.bIsRadio = channel.radio; - chan.iChannelNumber = i; - strncpy(chan.strChannelName, channel.name.c_str(), strlen(channel.name.c_str())); - strcpy(chan.strInputFormat, "video/x-mpegts"); - chan.bIsHidden = false; + chan.iUniqueId = channel.id; + chan.bIsRadio = channel.radio; + chan.iChannelNumber = i; + strncpy(chan.strChannelName, channel.name.c_str(), strlen(channel.name.c_str())); + strcpy(chan.strInputFormat, "video/x-mpegts"); + chan.bIsHidden = false; - pvr->TransferChannelEntry(handle, &chan); - } - } - return PVR_ERROR_NO_ERROR; + pvr->TransferChannelEntry(handle, &chan); + } + } + return PVR_ERROR_NO_ERROR; } PVR_ERROR OctonetData::getEPG(ADDON_HANDLE handle, int iChannelUid, time_t start, time_t end) { - for (unsigned int i = 0; i < channels.size(); i++) - { - OctonetChannel &chan = channels.at(i); - if (iChannelUid != chan.id) - continue; + for (unsigned int i = 0; i < m_channels.size(); i++) + { + OctonetChannel& chan = m_channels.at(i); + if (iChannelUid != chan.id) + continue; - if(chan.epg.empty()) { - loadEPG(); - } + if (chan.epg.empty()) + { + LoadEPG(); + } - // FIXME: Check if reload is needed!? + // FIXME: Check if reload is needed!? - std::vector::iterator it; - time_t last_end = 0; - for (it = chan.epg.begin(); it != chan.epg.end(); ++it) { - if (it->end > last_end) - last_end = it->end; + std::vector::iterator it; + time_t last_end = 0; + for (it = chan.epg.begin(); it != chan.epg.end(); ++it) + { + if (it->end > last_end) + last_end = it->end; - if (it->end < start || it->start > end) { - continue; - } + if (it->end < start || it->start > end) + { + continue; + } - EPG_TAG entry; - memset(&entry, 0, sizeof(EPG_TAG)); - entry.iSeriesNumber = EPG_TAG_INVALID_SERIES_EPISODE; - entry.iEpisodeNumber = EPG_TAG_INVALID_SERIES_EPISODE; - entry.iEpisodePartNumber = EPG_TAG_INVALID_SERIES_EPISODE; + EPG_TAG entry; + memset(&entry, 0, sizeof(EPG_TAG)); + entry.iSeriesNumber = EPG_TAG_INVALID_SERIES_EPISODE; + entry.iEpisodeNumber = EPG_TAG_INVALID_SERIES_EPISODE; + entry.iEpisodePartNumber = EPG_TAG_INVALID_SERIES_EPISODE; - entry.iUniqueChannelId = chan.id; - entry.iUniqueBroadcastId = it->id; - entry.strTitle = it->title.c_str(); - entry.strPlotOutline = it->subtitle.c_str(); - entry.startTime = it->start; - entry.endTime = it->end; + entry.iUniqueChannelId = chan.id; + entry.iUniqueBroadcastId = it->id; + entry.strTitle = it->title.c_str(); + entry.strPlotOutline = it->subtitle.c_str(); + entry.startTime = it->start; + entry.endTime = it->end; - pvr->TransferEpgEntry(handle, &entry); - } + pvr->TransferEpgEntry(handle, &entry); + } - if (last_end < end) - loadEPG(); + if (last_end < end) + LoadEPG(); - for (it = chan.epg.begin(); it != chan.epg.end(); ++it) { - if (it->end < start || it->start > end) { - continue; - } + for (it = chan.epg.begin(); it != chan.epg.end(); ++it) + { + if (it->end < start || it->start > end) + { + continue; + } - EPG_TAG entry; - memset(&entry, 0, sizeof(EPG_TAG)); - entry.iSeriesNumber = EPG_TAG_INVALID_SERIES_EPISODE; - entry.iEpisodeNumber = EPG_TAG_INVALID_SERIES_EPISODE; - entry.iEpisodePartNumber = EPG_TAG_INVALID_SERIES_EPISODE; + EPG_TAG entry; + memset(&entry, 0, sizeof(EPG_TAG)); + entry.iSeriesNumber = EPG_TAG_INVALID_SERIES_EPISODE; + entry.iEpisodeNumber = EPG_TAG_INVALID_SERIES_EPISODE; + entry.iEpisodePartNumber = EPG_TAG_INVALID_SERIES_EPISODE; - entry.iUniqueChannelId = chan.id; - entry.iUniqueBroadcastId = it->id; - entry.strTitle = it->title.c_str(); - entry.strPlotOutline = it->subtitle.c_str(); - entry.startTime = it->start; - entry.endTime = it->end; + entry.iUniqueChannelId = chan.id; + entry.iUniqueBroadcastId = it->id; + entry.strTitle = it->title.c_str(); + entry.strPlotOutline = it->subtitle.c_str(); + entry.startTime = it->start; + 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 { - for(std::vector::const_iterator iter = channels.begin(); iter != channels.end(); ++iter) { - if(iter->id == id) { - return iter->url; - } - } +const std::string& OctonetData::GetUrl(int id) const +{ + for (std::vector::const_iterator iter = m_channels.begin(); iter != m_channels.end(); + ++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 { - for(std::vector::const_iterator iter = channels.begin(); iter != channels.end(); ++iter) { - if(iter->id == id) { - return iter->name; - } - } +const std::string& OctonetData::GetName(int id) const +{ + for (std::vector::const_iterator iter = m_channels.begin(); iter != m_channels.end(); + ++iter) + { + if (iter->id == id) + { + return iter->name; + } + } - return channels[0].name; + return m_channels[0].name; } int OctonetData::getGroupCount(void) { - return groups.size(); + return m_groups.size(); } PVR_ERROR OctonetData::getGroups(ADDON_HANDLE handle, bool bRadio) { - for (unsigned int i = 0; i < groups.size(); i++) - { - OctonetGroup &group = groups.at(i); - if (group.radio == bRadio) - { - PVR_CHANNEL_GROUP g; - memset(&g, 0, sizeof(PVR_CHANNEL_GROUP)); + for (unsigned int i = 0; i < m_groups.size(); i++) + { + OctonetGroup& group = m_groups.at(i); + if (group.radio == bRadio) + { + PVR_CHANNEL_GROUP g; + memset(&g, 0, sizeof(PVR_CHANNEL_GROUP)); - g.iPosition = 0; - g.bIsRadio = group.radio; - strncpy(g.strGroupName, group.name.c_str(), strlen(group.name.c_str())); + g.iPosition = 0; + g.bIsRadio = group.radio; + 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); - if (g == NULL) - return PVR_ERROR_UNKNOWN; + OctonetGroup* g = FindGroup(group.strGroupName); + if (g == NULL) + return PVR_ERROR_UNKNOWN; - for (unsigned int i = 0; i < g->members.size(); i++) - { - OctonetChannel &channel = channels.at(g->members[i]); - PVR_CHANNEL_GROUP_MEMBER m; - memset(&m, 0, sizeof(PVR_CHANNEL_GROUP_MEMBER)); + for (unsigned int i = 0; i < g->members.size(); i++) + { + OctonetChannel& channel = m_channels.at(g->members[i]); + PVR_CHANNEL_GROUP_MEMBER m; + memset(&m, 0, sizeof(PVR_CHANNEL_GROUP_MEMBER)); - strncpy(m.strGroupName, group.strGroupName, strlen(group.strGroupName)); - m.iChannelUniqueId = channel.id; - m.iChannelNumber = channel.id; + strncpy(m.strGroupName, group.strGroupName, strlen(group.strGroupName)); + m.iChannelUniqueId = 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++) - { - if (groups.at(i).name == name) - return &groups.at(i); - } + for (unsigned int i = 0; i < m_groups.size(); i++) + { + if (m_groups.at(i).name == name) + return &m_groups.at(i); + } - return NULL; + return NULL; } diff --git a/src/OctonetData.h b/src/OctonetData.h index 0c8d271..b5af368 100644 --- a/src/OctonetData.h +++ b/src/OctonetData.h @@ -10,71 +10,71 @@ #pragma once -#include +#include "client.h" #include "p8-platform/threads/threads.h" -#include "client.h" + +#include struct OctonetEpgEntry { - int64_t channelId; - time_t start; - time_t end; - int id; - std::string title; - std::string subtitle; + int64_t channelId; + time_t start; + time_t end; + int id; + std::string title; + std::string subtitle; }; struct OctonetChannel { - int64_t nativeId; - std::string name; - std::string url; - bool radio; - int id; + int64_t nativeId; + std::string name; + std::string url; + bool radio; + int id; - std::vector epg; + std::vector epg; }; struct OctonetGroup { - std::string name; - bool radio; - std::vector members; + std::string name; + bool radio; + std::vector members; }; class OctonetData : public P8PLATFORM::CThread { - public: - OctonetData(void); - virtual ~OctonetData(void); +public: + OctonetData(void); + virtual ~OctonetData(void); - virtual int getChannelCount(void); - virtual PVR_ERROR getChannels(ADDON_HANDLE handle, bool bRadio); + virtual int getChannelCount(void); + virtual PVR_ERROR getChannels(ADDON_HANDLE handle, bool bRadio); - virtual int getGroupCount(void); - virtual PVR_ERROR getGroups(ADDON_HANDLE handle, bool bRadio); - virtual PVR_ERROR getGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group); + virtual int getGroupCount(void); + virtual PVR_ERROR getGroups(ADDON_HANDLE handle, bool bRadio); + 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); - const std::string& getUrl(int id) const; - const std::string& getName(int id) const; + 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& GetName(int id) const; - protected: - virtual bool loadChannelList(void); - virtual bool loadEPG(void); - virtual OctonetGroup* findGroup(const std::string &name); +protected: + void* Process(void) override; - 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); - time_t parseDateTime(std::string date); - int64_t parseID(std::string id); +private: + std::string m_serverAddress; + std::vector m_channels; + std::vector m_groups; - private: - std::string serverAddress; - std::vector channels; - std::vector groups; - - time_t lastEpgLoad; + time_t m_lastEpgLoad; }; diff --git a/src/Socket.cpp b/src/Socket.cpp index 2d29678..611af62 100644 --- a/src/Socket.cpp +++ b/src/Socket.cpp @@ -6,12 +6,15 @@ * See LICENSE.md for more information. */ -#include "kodi/libXBMC_addon.h" -#include -#include "p8-platform/os.h" -#include "client.h" #include "Socket.h" + +#include "client.h" + +#include "kodi/libXBMC_addon.h" +#include "p8-platform/os.h" + #include +#include using namespace std; using namespace ADDON; @@ -22,28 +25,31 @@ namespace OCTO /* Master defines for client control */ #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; - _family = family; - _domain = domain; - _type = type; - _protocol = protocol; - _port = 0; - memset (&_sockaddr, 0, sizeof( _sockaddr ) ); + m_sd = INVALID_SOCKET; + m_family = family; + m_domain = domain; + m_type = type; + m_protocol = protocol; + m_port = 0; + memset(&m_sockaddr, 0, sizeof(m_sockaddr)); } Socket::Socket() { // Default constructor, default settings - _sd = INVALID_SOCKET; - _family = af_inet; - _domain = pf_inet; - _type = sock_stream; - _protocol = tcp; - _port = 0; - memset (&_sockaddr, 0, sizeof( _sockaddr ) ); + m_sd = INVALID_SOCKET; + m_family = af_inet; + m_domain = pf_inet; + m_type = sock_stream; + m_protocol = tcp; + m_port = 0; + memset(&m_sockaddr, 0, sizeof(m_sockaddr)); } @@ -55,7 +61,7 @@ Socket::~Socket() bool Socket::setHostname(const std::string& host) { - _hostname = host; + m_hostname = host; return true; } @@ -63,9 +69,9 @@ bool Socket::close() { if (is_valid()) { - if (_sd != SOCKET_ERROR) - closesocket(_sd); - _sd = INVALID_SOCKET; + if (m_sd != SOCKET_ERROR) + closesocket(m_sd); + m_sd = INVALID_SOCKET; return true; } return false; @@ -75,7 +81,7 @@ bool Socket::create() { close(); - if(!osInit()) + if (!osInit()) { 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()) { - close(); + close(); } - _sd = socket(_family, _type, _protocol); - _port = port; - _sockaddr.sin_family = (sa_family_t) _family; - _sockaddr.sin_addr.s_addr = INADDR_ANY; //listen to all - _sockaddr.sin_port = htons( _port ); + m_sd = socket(m_family, m_type, m_protocol); + m_port = port; + m_sockaddr.sin_family = (sa_family_t)m_family; + m_sockaddr.sin_addr.s_addr = INADDR_ANY; //listen to all + 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; } @@ -118,13 +124,13 @@ bool Socket::listen() const 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. //linux 128//MAXCONNECTIONS =1 if (listen_return == -1) { - errormessage( getLastError(), "Socket::listen" ); + errormessage(getLastError(), "Socket::listen"); 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()) { return false; } - socklen_t addr_length = sizeof( _sockaddr ); - new_socket._sd = ::accept(_sd, const_cast( (const sockaddr*) &_sockaddr), &addr_length ); + socklen_t addr_length = sizeof(m_sockaddr); + new_socket.m_sd = ::accept(m_sd, const_cast((const sockaddr*)&m_sockaddr), &addr_length); #ifdef TARGET_WINDOWS - if (new_socket._sd == INVALID_SOCKET) + if (new_socket.m_sd == INVALID_SOCKET) #else - if (new_socket._sd <= 0) + if (new_socket.m_sd <= 0) #endif { - errormessage( getLastError(), "Socket::accept" ); + errormessage(getLastError(), "Socket::accept"); 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; struct timeval tv; - int result; + int result; if (!is_valid()) { @@ -174,15 +180,15 @@ int Socket::send ( const char* data, const unsigned int len ) } // fill with new data - tv.tv_sec = 0; + tv.tv_sec = 0; tv.tv_usec = 0; FD_ZERO(&set_w); FD_ZERO(&set_e); - FD_SET(_sd, &set_w); - FD_SET(_sd, &set_e); + FD_SET(m_sd, &set_w); + 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) { @@ -190,18 +196,18 @@ int Socket::send ( const char* data, const unsigned int len ) close(); 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"); close(); return 0; } - int status = ::send(_sd, data, len, 0 ); + int status = ::send(m_sd, data, len, 0); if (status == -1) { - errormessage( getLastError(), "Socket::send"); + errormessage(getLastError(), "Socket::send"); libKodi->Log(LOG_ERROR, "Socket::send - failed to send data"); close(); 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 i; 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) { - errormessage( getLastError(), "Socket::sendto"); + errormessage(getLastError(), "Socket::sendto"); osCleanup(); return i; } sentbytes += i; - } while ( (sentbytes < (int) size) && (sendcompletebuffer == true)); + } while ((sentbytes < (int)size) && (sendcompletebuffer == true)); 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; if (!is_valid()) @@ -242,10 +248,10 @@ int Socket::receive ( std::string& data, unsigned int minpacketsize ) const return 0; } - buf = new char [ minpacketsize + 1 ]; - memset ( buf, 0, minpacketsize + 1 ); + buf = new char[minpacketsize + 1]; + memset(buf, 0, minpacketsize + 1); - status = receive( buf, minpacketsize, minpacketsize ); + status = receive(buf, minpacketsize, minpacketsize); data = buf; @@ -255,12 +261,12 @@ int Socket::receive ( std::string& data, unsigned int minpacketsize ) const //Receive until error or \n -bool Socket::ReadLine (string& line) +bool Socket::ReadLine(string& line) { - fd_set set_r, set_e; - timeval timeout; - int retries = 6; - char buffer[2048]; + fd_set set_r, set_e; + timeval timeout; + int retries = 6; + char buffer[2048]; if (!is_valid()) return false; @@ -274,15 +280,15 @@ bool Socket::ReadLine (string& line) return true; } - timeout.tv_sec = RECEIVE_TIMEOUT; + timeout.tv_sec = RECEIVE_TIMEOUT; timeout.tv_usec = 0; // fill with new data FD_ZERO(&set_r); FD_ZERO(&set_e); - FD_SET(_sd, &set_r); - FD_SET(_sd, &set_e); - int result = select(FD_SETSIZE, &set_r, NULL, &set_e, &timeout); + FD_SET(m_sd, &set_r); + FD_SET(m_sd, &set_e); + int result = select(FD_SETSIZE, &set_r, nullptr, &set_e, &timeout); if (result < 0) { @@ -296,16 +302,20 @@ bool Socket::ReadLine (string& line) { if (retries != 0) { - libKodi->Log(LOG_DEBUG, "%s: timeout waiting for response, retrying... (%i)", __FUNCTION__, retries); - retries--; + libKodi->Log(LOG_DEBUG, "%s: timeout waiting for response, retrying... (%i)", __FUNCTION__, + retries); + retries--; continue; - } else { - libKodi->Log(LOG_DEBUG, "%s: timeout waiting for response. Aborting after 10 retries.", __FUNCTION__); - return false; + } + else + { + 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) { 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]; int status = 0; - if ( !is_valid() ) + if (!is_valid()) { return 0; } - memset ( buf, 0, MAXRECV + 1 ); - status = receive( buf, MAXRECV, 0 ); + memset(buf, 0, MAXRECV + 1); + status = receive(buf, MAXRECV, 0); data = buf; 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; - if ( !is_valid() ) + if (!is_valid()) { 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; } @@ -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; } -bool Socket::connect ( const std::string& host, const unsigned short port ) +bool Socket::connect(const std::string& host, const unsigned short port) { close(); - if ( !setHostname( host ) ) + if (!setHostname(host)) { libKodi->Log(LOG_ERROR, "Socket::setHostname(%s) failed.\n", host.c_str()); return false; } - _port = port; + m_port = port; char strPort[15]; snprintf(strPort, 15, "%hu", port); struct addrinfo hints; - struct addrinfo* result = NULL; - struct addrinfo *address = NULL; + struct addrinfo* result = nullptr; + struct addrinfo* address = nullptr; memset(&hints, 0, sizeof(hints)); - hints.ai_family = _family; - hints.ai_socktype = _type; - hints.ai_protocol = _protocol; + hints.ai_family = m_family; + hints.ai_socktype = m_type; + hints.ai_protocol = m_protocol; int retval = getaddrinfo(host.c_str(), strPort, &hints, &result); if (retval != 0) @@ -402,18 +417,18 @@ bool Socket::connect ( const std::string& host, const unsigned short port ) return false; } - for (address = result; address != NULL; address = address->ai_next) + for (address = result; address != nullptr; address = address->ai_next) { // 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"); 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) { close(); @@ -426,7 +441,7 @@ bool Socket::connect ( const std::string& host, const unsigned short port ) freeaddrinfo(result); - if (address == NULL) + if (address == nullptr) { libKodi->Log(LOG_ERROR, "Socket::connect %s:%u\n", host.c_str(), port); errormessage(getLastError(), "Socket::connect"); @@ -439,30 +454,30 @@ bool Socket::connect ( const std::string& host, const unsigned short port ) bool Socket::reconnect() { - if ( is_valid() ) + if (is_valid()) { return true; } - return connect(_hostname, _port); + return connect(m_hostname, m_port); } bool Socket::is_valid() const { - return (_sd != INVALID_SOCKET); + return (m_sd != INVALID_SOCKET); } #if defined(TARGET_WINDOWS) -bool Socket::set_non_blocking ( const bool b ) +bool Socket::set_non_blocking(const bool b) { u_long iMode; - if ( b ) - iMode = 1; // enable non_blocking + if (b) + iMode = 1; // enable non_blocking 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); return false; @@ -471,89 +486,89 @@ bool Socket::set_non_blocking ( const bool b ) 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) { - case WSANOTINITIALISED: - errmsg = "A successful WSAStartup call must occur before using this function."; - break; - case WSAENETDOWN: - errmsg = "The network subsystem or the associated service provider has failed"; - break; - case WSA_NOT_ENOUGH_MEMORY: - errmsg = "Insufficient memory available"; - break; - case WSA_INVALID_PARAMETER: - errmsg = "One or more parameters are invalid"; - break; - case WSA_OPERATION_ABORTED: - errmsg = "Overlapped operation aborted"; - break; - case WSAEINTR: - errmsg = "Interrupted function call"; - break; - case WSAEBADF: - errmsg = "File handle is not valid"; - break; - case WSAEACCES: - errmsg = "Permission denied"; - break; - case WSAEFAULT: - errmsg = "Bad address"; - break; - case WSAEINVAL: - errmsg = "Invalid argument"; - break; - case WSAENOTSOCK: - errmsg = "Socket operation on nonsocket"; - break; - case WSAEDESTADDRREQ: - errmsg = "Destination address required"; - break; - case WSAEMSGSIZE: - errmsg = "Message too long"; - break; - case WSAEPROTOTYPE: - errmsg = "Protocol wrong type for socket"; - break; - case WSAENOPROTOOPT: - errmsg = "Bad protocol option"; - break; - case WSAEPFNOSUPPORT: - errmsg = "Protocol family not supported"; - break; - case WSAEAFNOSUPPORT: - errmsg = "Address family not supported by protocol family"; - break; - case WSAEADDRINUSE: - errmsg = "Address already in use"; - break; - case WSAECONNRESET: - errmsg = "Connection reset by peer"; - break; - case WSAHOST_NOT_FOUND: - errmsg = "Authoritative answer host not found"; - break; - case WSATRY_AGAIN: - errmsg = "Nonauthoritative host not found, or server failure"; - break; - case WSAEISCONN: - errmsg = "Socket is already connected"; - break; - case WSAETIMEDOUT: - errmsg = "Connection timed out"; - break; - case WSAECONNREFUSED: - errmsg = "Connection refused"; - break; - case WSANO_DATA: - errmsg = "Valid name, no data record of requested type"; - break; - default: - errmsg = "WSA Error"; + case WSANOTINITIALISED: + errmsg = "A successful WSAStartup call must occur before using this function."; + break; + case WSAENETDOWN: + errmsg = "The network subsystem or the associated service provider has failed"; + break; + case WSA_NOT_ENOUGH_MEMORY: + errmsg = "Insufficient memory available"; + break; + case WSA_INVALID_PARAMETER: + errmsg = "One or more parameters are invalid"; + break; + case WSA_OPERATION_ABORTED: + errmsg = "Overlapped operation aborted"; + break; + case WSAEINTR: + errmsg = "Interrupted function call"; + break; + case WSAEBADF: + errmsg = "File handle is not valid"; + break; + case WSAEACCES: + errmsg = "Permission denied"; + break; + case WSAEFAULT: + errmsg = "Bad address"; + break; + case WSAEINVAL: + errmsg = "Invalid argument"; + break; + case WSAENOTSOCK: + errmsg = "Socket operation on nonsocket"; + break; + case WSAEDESTADDRREQ: + errmsg = "Destination address required"; + break; + case WSAEMSGSIZE: + errmsg = "Message too long"; + break; + case WSAEPROTOTYPE: + errmsg = "Protocol wrong type for socket"; + break; + case WSAENOPROTOOPT: + errmsg = "Bad protocol option"; + break; + case WSAEPFNOSUPPORT: + errmsg = "Protocol family not supported"; + break; + case WSAEAFNOSUPPORT: + errmsg = "Address family not supported by protocol family"; + break; + case WSAEADDRINUSE: + errmsg = "Address already in use"; + break; + case WSAECONNRESET: + errmsg = "Connection reset by peer"; + break; + case WSAHOST_NOT_FOUND: + errmsg = "Authoritative answer host not found"; + break; + case WSATRY_AGAIN: + errmsg = "Nonauthoritative host not found, or server failure"; + break; + case WSAEISCONN: + errmsg = "Socket is already connected"; + break; + case WSAETIMEDOUT: + errmsg = "Connection timed out"; + break; + case WSAECONNREFUSED: + errmsg = "Connection refused"; + break; + case WSANO_DATA: + errmsg = "Valid name, no data record of requested type"; + break; + default: + errmsg = "WSA Error"; } libKodi->Log(LOG_ERROR, "%s: (Winsock error=%i) %s\n", functionname, errnum, errmsg); } @@ -569,15 +584,15 @@ bool Socket::osInit() { win_usage_count++; // initialize winsock: - if (WSAStartup(MAKEWORD(2,2),&_wsaData) != 0) + if (WSAStartup(MAKEWORD(2, 2), &m_wsaData) != 0) { return false; } - WORD wVersionRequested = MAKEWORD(2,2); + WORD wVersionRequested = MAKEWORD(2, 2); // check version - if (_wsaData.wVersion != wVersionRequested) + if (m_wsaData.wVersion != wVersionRequested) { return false; } @@ -588,30 +603,30 @@ bool Socket::osInit() void Socket::osCleanup() { win_usage_count--; - if(win_usage_count == 0) + if (win_usage_count == 0) { WSACleanup(); } } #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; - opts = fcntl(_sd, F_GETFL); + opts = fcntl(m_sd, F_GETFL); - if ( opts < 0 ) + if (opts < 0) { return false; } - if ( b ) - opts = ( opts | O_NONBLOCK ); + if (b) + opts = (opts | O_NONBLOCK); 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); return false; @@ -619,11 +634,11 @@ bool Socket::set_non_blocking ( const bool b ) 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 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"; break; 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; case ENOBUFS: 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"; break; 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; case EAFNOSUPPORT: 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"; break; 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; 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; 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; //case E: // errmsg = ""; diff --git a/src/Socket.h b/src/Socket.h index 49f91fb..9a537e2 100644 --- a/src/Socket.h +++ b/src/Socket.h @@ -10,64 +10,64 @@ //Include platform specific datatypes, header files, defines and constants: #if defined TARGET_WINDOWS - #define WIN32_LEAN_AND_MEAN // Enable LEAN_AND_MEAN support - #pragma warning(disable:4005) // Disable "warning C4005: '_WINSOCKAPI_' : macro redefinition" - #include - #include - #pragma warning(default:4005) - #include +#define WIN32_LEAN_AND_MEAN // Enable LEAN_AND_MEAN support +#pragma warning(disable : 4005) // Disable "warning C4005: '_WINSOCKAPI_' : macro redefinition" +#include +#include +#pragma warning(default : 4005) +#include - #ifndef NI_MAXHOST - #define NI_MAXHOST 1025 - #endif +#ifndef NI_MAXHOST +#define NI_MAXHOST 1025 +#endif - #ifndef socklen_t - typedef int socklen_t; - #endif - #ifndef ipaddr_t - typedef unsigned long ipaddr_t; - #endif - #ifndef port_t - typedef unsigned short port_t; - #endif - #ifndef sa_family_t - #define sa_family_t ADDRESS_FAMILY - #endif +#ifndef socklen_t +typedef int socklen_t; +#endif +#ifndef ipaddr_t +typedef unsigned long ipaddr_t; +#endif +#ifndef port_t +typedef unsigned short port_t; +#endif +#ifndef sa_family_t +#define sa_family_t ADDRESS_FAMILY +#endif #elif defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD #ifdef SOCKADDR_IN #undef SOCKADDR_IN #endif - #include /* for socket,connect */ - #include /* for socket,connect */ - #include /* for Unix socket */ - #include /* for inet_pton */ - #include /* for gethostbyname */ - #include /* for htons */ - #include /* for read, write, close */ - #include - #include +#include /* for inet_pton */ +#include +#include +#include /* for gethostbyname */ +#include /* for htons */ +#include /* for socket,connect */ +#include /* for socket,connect */ +#include /* for Unix socket */ +#include /* for read, write, close */ - typedef int SOCKET; - typedef sockaddr SOCKADDR; - typedef sockaddr_in SOCKADDR_IN; - #ifndef INVALID_SOCKET - #define INVALID_SOCKET (-1) - #endif - #define SOCKET_ERROR (-1) +typedef int SOCKET; +typedef sockaddr SOCKADDR; +typedef sockaddr_in SOCKADDR_IN; +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (-1) +#endif +#define SOCKET_ERROR (-1) - #define closesocket(sd) ::close(sd) +#define closesocket(sd) ::close(sd) #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 - +#include #include namespace OCTO { -#define MAXCONNECTIONS 1 ///< Maximum number of pending connections before "Connection refused" -#define MAXRECV 1500 ///< Maximum packet size +#define MAXCONNECTIONS 1 ///< Maximum number of pending connections before "Connection refused" +#define MAXRECV 1500 ///< Maximum packet size enum SocketFamily { @@ -78,10 +78,10 @@ enum SocketFamily enum SocketDomain { - #if defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD - pf_unix = PF_UNIX, - pf_local = PF_LOCAL, - #endif +#if defined TARGET_LINUX || defined TARGET_DARWIN || defined TARGET_FREEBSD + pf_unix = PF_UNIX, + pf_local = PF_LOCAL, +#endif pf_inet = PF_INET }; @@ -99,197 +99,187 @@ enum SocketProtocol 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(); - /*! - * 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 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 - * \param family Can be af_inet or af_inet6. Default: af_inet - */ - void setFamily(const enum SocketFamily family) - { - _family = family; - }; + /*! + * Socket setDomain + * \param domain Can be pf_unix, pf_local, pf_inet or pf_inet6. Default: pf_inet + */ + void setDomain(const enum SocketDomain domain) { m_domain = domain; }; - /*! - * Socket setDomain - * \param domain Can be pf_unix, pf_local, pf_inet or pf_inet6. Default: pf_inet - */ - void setDomain(const enum SocketDomain domain) - { - _domain = domain; - }; + /*! + * Socket setType + * \param type Can be sock_stream or sock_dgram. Default: sock_stream. + */ + void setType(const enum SocketType type) { m_type = type; }; - /*! - * Socket setType - * \param type Can be sock_stream or sock_dgram. Default: sock_stream. - */ - void setType(const enum SocketType type) - { - _type = type; - }; + /*! + * Socket setProtocol + * \param protocol Can be tcp or udp. Default: tcp. + */ + void setProtocol(const enum SocketProtocol protocol) { m_protocol = protocol; }; - /*! - * Socket setProtocol - * \param protocol Can be tcp or udp. Default: tcp. - */ - void setProtocol(const enum SocketProtocol protocol) - { - _protocol = protocol; - }; + /*! + * Socket setPort + * \param port port number for socket communication + */ + void setPort(const unsigned short port) { m_sockaddr.sin_port = htons(port); }; - /*! - * 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); - bool setHostname ( const std::string& host ); + // Server initialization - // Server initialization + /*! + * Socket create + * Create a new socket + * \return True if succesful + */ + bool create(); - /*! - * Socket create - * Create a new socket - * \return True if succesful - */ - bool create(); + /*! + * Socket close + * Close the socket + * \return True if succesful + */ + bool close(); - /*! - * Socket close - * Close the socket - * \return True if succesful - */ - bool close(); + /*! + * Socket bind + */ + bool bind(const unsigned short port); + bool listen() const; + bool accept(Socket& socket) const; - /*! - * Socket bind - */ - bool bind ( const unsigned short port ); - bool listen() const; - bool accept ( Socket& socket ) const; + // Client initialization + bool connect(const std::string& host, const unsigned short port); - // Client initialization - bool connect ( const std::string& host, const unsigned short port ); + bool reconnect(); - 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 - * - * \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 + * + * \param data Pointer to a character array of size 'size' with the data to transmit + * \param size Length of the data to transmit + * \return Number of bytes send or -1 in case of an error + */ + int send(const char* data, const unsigned int size); - /*! - * Socket send function - * - * \param data Pointer to a character array of size 'size' with the data to transmit - * \param size Length of the data to transmit - * \return Number of bytes send or -1 in case of an error - */ - int send ( const char* data, const unsigned int size ); + /*! + * Socket sendto function + * + * \param data Reference to a std::string with the data to transmit + * \param size Length of the data to transmit + * \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 sendto(const char* data, unsigned int size, bool sendcompletebuffer = false); + // Data Receive - /*! - * Socket sendto function - * - * \param data Reference to a std::string with the data to transmit - * \param size Length of the data to transmit - * \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 sendto ( const char* data, unsigned int size, bool sendcompletebuffer = false); - // Data Receive + /*! + * Socket receive function + * + * \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 + */ + int receive(std::string& data, unsigned int minpacketsize) const; - /*! - * Socket receive function - * - * \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 - */ - int receive ( std::string& data, unsigned int minpacketsize ) const; + /*! + * Socket receive function + * + * \param data Reference to a std::string for storage of the received data. + * \return Number of bytes received or SOCKET_ERROR + */ + int receive(std::string& data) const; - /*! - * Socket receive function - * - * \param data Reference to a std::string for storage of the received data. - * \return Number of bytes received or SOCKET_ERROR - */ - int receive ( std::string& data ) const; + /*! + * Socket receive 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 minpacketsize Specifies the minimum number of bytes that need to be received before returning + * \return Number of bytes received or SOCKET_ERROR + */ + int receive(char* data, const unsigned int buffersize, const unsigned int minpacketsize) const; - /*! - * Socket receive 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 minpacketsize Specifies the minimum number of bytes that need to be received before returning - * \return Number of bytes received or SOCKET_ERROR - */ - int receive ( char* data, const unsigned int buffersize, const unsigned int minpacketsize ) const; + /*! + * 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 = nullptr, + socklen_t* fromlen = nullptr) const; - /*! - * 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 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 - SOCKADDR_IN _sockaddr; ///< Socket Address - //struct addrinfo* _addrinfo; ///< Socket address info - std::string _hostname; ///< Hostname - unsigned short _port; ///< Port number +#ifdef TARGET_WINDOWS + WSADATA m_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 - enum SocketFamily _family; ///< Socket Address Family - enum SocketProtocol _protocol; ///< Socket Protocol - enum SocketType _type; ///< Socket Type - enum SocketDomain _domain; ///< Socket domain - - #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(); + void errormessage(int errornum, const char* functionname = nullptr) const; + int getLastError(void) const; + bool osInit(); + void osCleanup(); }; } //namespace OCTO diff --git a/src/client.cpp b/src/client.cpp index a805c82..379c801 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -9,13 +9,14 @@ */ #include "client.h" -#include -#include -#include #include "OctonetData.h" #include "rtsp_client.hpp" +#include +#include +#include + using namespace ADDON; /* setting variables with defaults */ @@ -23,72 +24,71 @@ std::string octonetAddress = ""; /* internal state variables */ ADDON_STATUS addonStatus = ADDON_STATUS_UNKNOWN; -CHelper_libXBMC_addon *libKodi = NULL; -CHelper_libXBMC_pvr *pvr = NULL; +CHelper_libXBMC_addon* libKodi = nullptr; +CHelper_libXBMC_pvr* pvr = nullptr; -OctonetData *data = NULL; +OctonetData* data = nullptr; /* KODI Core Addon functions * see xbmc_addon_dll.h */ -extern "C" { - -void ADDON_ReadSettings(void) +extern "C" { - char buffer[2048]; - if (libKodi->GetSetting("octonetAddress", &buffer)) - octonetAddress = buffer; -} -ADDON_STATUS ADDON_Create(void* callbacks, const char* globalApiVersion, void* props) -{ - if (callbacks == NULL || props == NULL) - return ADDON_STATUS_UNKNOWN; + void ADDON_ReadSettings(void) + { + char buffer[2048]; + if (libKodi->GetSetting("octonetAddress", &buffer)) + octonetAddress = buffer; + } - AddonProperties_PVR *pvrprops = (AddonProperties_PVR*)props; - libKodi = new CHelper_libXBMC_addon; - if (!libKodi->RegisterMe(callbacks)) { - libKodi->Log(LOG_ERROR, "%s: Failed to register octonet addon", __func__); - SAFE_DELETE(libKodi); - return ADDON_STATUS_PERMANENT_FAILURE; - } + ADDON_STATUS ADDON_Create(void* callbacks, const char* globalApiVersion, void* props) + { + if (callbacks == nullptr || props == nullptr) + return ADDON_STATUS_UNKNOWN; - pvr = new CHelper_libXBMC_pvr; - 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; - } + AddonProperties_PVR* pvrprops = (AddonProperties_PVR*)props; + libKodi = new CHelper_libXBMC_addon; + if (!libKodi->RegisterMe(callbacks)) + { + libKodi->Log(LOG_ERROR, "%s: Failed to register octonet addon", __func__); + SAFE_DELETE(libKodi); + return ADDON_STATUS_PERMANENT_FAILURE; + } - libKodi->Log(LOG_DEBUG, "%s: Creating octonet pvr addon", __func__); - ADDON_ReadSettings(); + pvr = new CHelper_libXBMC_pvr; + 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; - return addonStatus; -} + data = new OctonetData; -void ADDON_Destroy() -{ - delete pvr; - delete libKodi; - addonStatus = ADDON_STATUS_UNKNOWN; -} + addonStatus = ADDON_STATUS_OK; + return addonStatus; + } -ADDON_STATUS ADDON_GetStatus() -{ - return addonStatus; -} + void ADDON_Destroy() + { + delete pvr; + delete libKodi; + addonStatus = ADDON_STATUS_UNKNOWN; + } -ADDON_STATUS ADDON_SetSetting(const char *settingName, const void *settingValue) -{ - /* For simplicity do a full addon restart whenever settings are + ADDON_STATUS ADDON_GetStatus() { return addonStatus; } + + ADDON_STATUS ADDON_SetSetting(const char* settingName, const void* settingValue) + { + /* For simplicity do a full addon restart whenever settings are * 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" { -PVR_ERROR GetCapabilities(PVR_ADDON_CAPABILITIES *pCapabilities) -{ - pCapabilities->bSupportsTV = true; - pCapabilities->bSupportsRadio = true; - pCapabilities->bSupportsChannelGroups = true; - pCapabilities->bSupportsEPG = true; - pCapabilities->bSupportsRecordings = false; - pCapabilities->bSupportsRecordingsRename = false; - pCapabilities->bSupportsRecordingsLifetimeChange = false; - pCapabilities->bSupportsDescrambleInfo = false; + PVR_ERROR GetCapabilities(PVR_ADDON_CAPABILITIES* pCapabilities) + { + pCapabilities->bSupportsTV = true; + pCapabilities->bSupportsRadio = true; + pCapabilities->bSupportsChannelGroups = true; + pCapabilities->bSupportsEPG = true; + pCapabilities->bSupportsRecordings = false; + pCapabilities->bSupportsRecordingsRename = false; + pCapabilities->bSupportsRecordingsLifetimeChange = false; + pCapabilities->bSupportsDescrambleInfo = false; - return PVR_ERROR_NO_ERROR; -} + return PVR_ERROR_NO_ERROR; + } -const char* GetBackendName(void) -{ - return "Digital Devices Octopus NET Client"; -} + const char* GetBackendName(void) { return "Digital Devices Octopus NET Client"; } -const char* GetBackendVersion(void) -{ - return STR(OCTONET_VERSION); -} + const char* GetBackendVersion(void) { return STR(OCTONET_VERSION); } -const char* GetConnectionString(void) -{ - return "connected"; // FIXME: translate? -} + const char* GetConnectionString(void) + { + return "connected"; // FIXME: translate? + } -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 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; + } -void OnSystemSleep() { - libKodi->Log(LOG_INFO, "Received event: %s", __FUNCTION__); - // FIXME: Disconnect? -} + void OnSystemSleep() + { + libKodi->Log(LOG_INFO, "Received event: %s", __FUNCTION__); + // FIXME: Disconnect? + } -void OnSystemWake() { - libKodi->Log(LOG_INFO, "Received event: %s", __FUNCTION__); - // FIXME:Reconnect? -} + void OnSystemWake() + { + libKodi->Log(LOG_INFO, "Received event: %s", __FUNCTION__); + // FIXME:Reconnect? + } -void OnPowerSavingActivated() {} -void OnPowerSavingDeactivated() {} + void OnPowerSavingActivated() {} + void OnPowerSavingDeactivated() {} -/* EPG */ -PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, int iChannelUid, time_t iStart, time_t iEnd) -{ - return data->getEPG(handle, iChannelUid, iStart, iEnd); -} + /* EPG */ + PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, int iChannelUid, time_t iStart, time_t iEnd) + { + return data->getEPG(handle, iChannelUid, iStart, iEnd); + } -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 IsEPGTagRecordable(const EPG_TAG*, bool*) { return PVR_ERROR_NOT_IMPLEMENTED; } + PVR_ERROR IsEPGTagPlayable(const EPG_TAG*, bool*) { return PVR_ERROR_NOT_IMPLEMENTED; } -/* Channel groups */ -int GetChannelGroupsAmount(void) -{ - return data->getGroupCount(); -} + /* Channel groups */ + int GetChannelGroupsAmount(void) { return data->getGroupCount(); } -PVR_ERROR GetChannelGroups(ADDON_HANDLE handle, bool bRadio) -{ - return data->getGroups(handle, bRadio); -} + PVR_ERROR GetChannelGroups(ADDON_HANDLE handle, bool bRadio) + { + return data->getGroups(handle, bRadio); + } -PVR_ERROR GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP& group) -{ - return data->getGroupMembers(handle, group); -} + PVR_ERROR GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP& group) + { + return data->getGroupMembers(handle, group); + } -/* Channels */ -PVR_ERROR OpenDialogChannelScan(void) { return PVR_ERROR_NOT_IMPLEMENTED; } + /* Channels */ + PVR_ERROR OpenDialogChannelScan(void) { return PVR_ERROR_NOT_IMPLEMENTED; } -int GetChannelsAmount(void) -{ - return data->getChannelCount(); -} + int GetChannelsAmount(void) { return data->getChannelCount(); } -PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio) -{ - return data->getChannels(handle, bRadio); -} + PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio) + { + return data->getChannels(handle, bRadio); + } -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 OpenDialogChannelSettings(const PVR_CHANNEL& channel) { return PVR_ERROR_NOT_IMPLEMENTED; } -PVR_ERROR OpenDialogChannelAdd(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 OpenDialogChannelSettings(const PVR_CHANNEL& channel) + { + return PVR_ERROR_NOT_IMPLEMENTED; + } + PVR_ERROR OpenDialogChannelAdd(const PVR_CHANNEL& channel) { return PVR_ERROR_NOT_IMPLEMENTED; } -/* Recordings */ -int GetRecordingsAmount(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 UndeleteRecording(const PVR_RECORDING& recording) { 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 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 SetRecordingLastPlayedPosition(const PVR_RECORDING& recording, int lastplayedposition) { return PVR_ERROR_NOT_IMPLEMENTED; } -int GetRecordingLastPlayedPosition(const PVR_RECORDING& recording) { 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; } + /* Recordings */ + int GetRecordingsAmount(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 UndeleteRecording(const PVR_RECORDING& recording) { 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 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 SetRecordingLastPlayedPosition(const PVR_RECORDING& recording, int lastplayedposition) + { + return PVR_ERROR_NOT_IMPLEMENTED; + } + int GetRecordingLastPlayedPosition(const PVR_RECORDING& recording) + { + 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_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 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 properties handling */ + 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 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 */ -/* entirely unused, as we use standard RTSP+TS mux, which can be handlded by + /* PVR stream handling */ + /* entirely unused, as we use standard RTSP+TS mux, which can be handlded by * Kodi core */ -bool OpenLiveStream(const PVR_CHANNEL& channel) { - return rtsp_open(data->getName(channel.iUniqueId), data->getUrl(channel.iUniqueId)); -} - -int ReadLiveStream(unsigned char* pBuffer, unsigned int iBufferSize) { - return rtsp_read(pBuffer, iBufferSize); -} - -void CloseLiveStream(void) { - rtsp_close(); -} - -long long SeekLiveStream(long long iPosition, int iWhence) { return -1; } -long long LengthLiveStream(void) { return -1; } -bool IsRealTimeStream(void) { return true; } - -PVR_ERROR GetSignalStatus(int channelUid, PVR_SIGNAL_STATUS* signalStatus) { - memset(signalStatus, 0, sizeof(PVR_SIGNAL_STATUS)); - 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 GetDescrambleInfo(int, PVR_DESCRAMBLE_INFO*) { return PVR_ERROR_NOT_IMPLEMENTED; } - -/* Recording stream handling */ -bool OpenRecordedStream(const PVR_RECORDING& recording) { return false; } -void CloseRecordedStream(void) {} -int ReadRecordedStream(unsigned char* pBuffer, unsigned int iBufferSize) { return -1; } -long long SeekRecordedStream(long long iPosition, int iWhence) { return -1; } -long long LengthRecordedStream(void) { return -1; } - -/* PVR demuxer */ -/* entirey unused, as we use TS */ -void DemuxReset(void) {} -void DemuxAbort(void) {} -void DemuxFlush(void) {} -DemuxPacket* DemuxRead(void) { return NULL; } -void FillBuffer(bool mode) {} - -/* Various helper functions */ -bool CanPauseStream() { return false; } -bool CanSeekStream() { return false; } - -/* Callbacks */ -void PauseStream(bool bPaused) {} -bool SeekTime(double time, bool backwards, double *startpts) { return false; } -void SetSpeed(int speed) {} -PVR_ERROR SetEPGTimeFrame(int) { return PVR_ERROR_NOT_IMPLEMENTED; } - -const char* GetBackendHostname() -{ - return octonetAddress.c_str(); -} - + bool OpenLiveStream(const PVR_CHANNEL& channel) + { + return rtsp_open(data->GetName(channel.iUniqueId), data->GetUrl(channel.iUniqueId)); + } + + int ReadLiveStream(unsigned char* pBuffer, unsigned int iBufferSize) + { + return rtsp_read(pBuffer, iBufferSize); + } + + void CloseLiveStream(void) { rtsp_close(); } + + long long SeekLiveStream(long long iPosition, int iWhence) { return -1; } + long long LengthLiveStream(void) { return -1; } + bool IsRealTimeStream(void) { return true; } + + PVR_ERROR GetSignalStatus(int channelUid, PVR_SIGNAL_STATUS* signalStatus) + { + memset(signalStatus, 0, sizeof(PVR_SIGNAL_STATUS)); + 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 GetDescrambleInfo(int, PVR_DESCRAMBLE_INFO*) { return PVR_ERROR_NOT_IMPLEMENTED; } + + /* Recording stream handling */ + bool OpenRecordedStream(const PVR_RECORDING& recording) { return false; } + void CloseRecordedStream(void) {} + int ReadRecordedStream(unsigned char* pBuffer, unsigned int iBufferSize) { return -1; } + long long SeekRecordedStream(long long iPosition, int iWhence) { return -1; } + long long LengthRecordedStream(void) { return -1; } + + /* PVR demuxer */ + /* entirey unused, as we use TS */ + void DemuxReset(void) {} + void DemuxAbort(void) {} + void DemuxFlush(void) {} + DemuxPacket* DemuxRead(void) { return nullptr; } + void FillBuffer(bool mode) {} + + /* Various helper functions */ + bool CanPauseStream() { return false; } + bool CanSeekStream() { return false; } + + /* Callbacks */ + void PauseStream(bool bPaused) {} + bool SeekTime(double time, bool backwards, double* startpts) { return false; } + void SetSpeed(int speed) {} + PVR_ERROR SetEPGTimeFrame(int) { return PVR_ERROR_NOT_IMPLEMENTED; } + + const char* GetBackendHostname() { return octonetAddress.c_str(); } } diff --git a/src/client.h b/src/client.h index 6c18f0b..067e830 100644 --- a/src/client.h +++ b/src/client.h @@ -17,8 +17,8 @@ #define __func__ __FUNCTION__ #endif -extern ADDON::CHelper_libXBMC_addon *libKodi; -extern CHelper_libXBMC_pvr *pvr; +extern ADDON::CHelper_libXBMC_addon* libKodi; +extern CHelper_libXBMC_pvr* pvr; /* IP or hostname of the octonet to be connected to */ extern std::string octonetAddress; diff --git a/src/rtsp_client.cpp b/src/rtsp_client.cpp index 68ad800..06abc70 100644 --- a/src/rtsp_client.cpp +++ b/src/rtsp_client.cpp @@ -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 -#include -#include + #include "Socket.h" #include "client.h" -#include -#include + +#include +#include #include +#include +#include +#include #include #if defined(_WIN32) || defined(_WIN64) #define strtok_r strtok_s #define strncasecmp _strnicmp -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)) - return -1; - return vsprintf(*sptr, fmt, argv); +int vasprintf(char** sptr, char* fmt, va_list argv) +{ + int wanted = vsnprintf(*sptr = nullptr, 0, fmt, argv); + if ((wanted < 0) || ((*sptr = (char*)malloc(1 + wanted)) == nullptr)) + return -1; + return vsprintf(*sptr, fmt, argv); } -int asprintf(char **sptr, char *fmt, ...) { - int retval; - va_list argv; - va_start(argv, fmt); - retval = vasprintf(sptr, fmt, argv); - va_end(argv); - return retval; +int asprintf(char** sptr, char* fmt, ...) +{ + int retval; + va_list argv; + va_start(argv, fmt); + retval = vasprintf(sptr, fmt, argv); + va_end(argv); + return retval; } #endif @@ -43,438 +55,498 @@ using namespace std; using namespace ADDON; using namespace OCTO; -enum rtsp_state { - RTSP_IDLE, - RTSP_DESCRIBE, - RTSP_SETUP, - RTSP_PLAY, - RTSP_RUNNING +enum rtsp_state +{ + RTSP_IDLE, + RTSP_DESCRIBE, + RTSP_SETUP, + RTSP_PLAY, + RTSP_RUNNING }; -enum rtsp_result { - RTSP_RESULT_OK = 200, +enum rtsp_result +{ + RTSP_RESULT_OK = 200, }; -struct rtsp_client { - char *content_base; - char *control; - char session_id[64]; - uint16_t stream_id; - int keepalive_interval; +struct rtsp_client +{ + char* content_base; + char* control; + char session_id[64]; + uint16_t stream_id; + int keepalive_interval; - char udp_address[UDP_ADDRESS_LEN]; - uint16_t udp_port; + char udp_address[UDP_ADDRESS_LEN]; + uint16_t udp_port; - Socket tcp_sock; - Socket udp_sock; - Socket rtcp_sock; + Socket tcp_sock; + Socket udp_sock; + Socket rtcp_sock; - enum rtsp_state state; - int cseq; + enum rtsp_state state; + int cseq; - size_t fifo_size; - uint16_t last_seq_nr; + size_t fifo_size; + uint16_t last_seq_nr; - string name; - int level; - int quality; + string name; + int level; + int quality; }; -struct url { - string protocol; - string host; - int port; - string path; +struct url +{ + string protocol; + string host; + int port; + string path; }; -struct rtcp_app { - uint8_t subtype; - uint8_t pt; - uint16_t len; - uint32_t ssrc; - char name[4]; - uint16_t identifier; - uint16_t string_len; +struct rtcp_app +{ + uint8_t subtype; + uint8_t pt; + uint16_t len; + uint32_t ssrc; + char name[4]; + 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 const string prot_end = "://"; - static const string host_end = "/"; - url result; +static url parse_url(const std::string& str) +{ + static const string prot_end = "://"; + static const string host_end = "/"; + url result; - string::const_iterator begin = str.begin(); - string::const_iterator end = search(begin, str.end(), prot_end.begin(), prot_end.end()); - result.protocol.reserve(distance(begin, end)); - transform(begin, end, back_inserter(result.protocol), ::tolower); - advance(end, prot_end.size()); - begin = end; + string::const_iterator begin = str.begin(); + string::const_iterator end = search(begin, str.end(), prot_end.begin(), prot_end.end()); + result.protocol.reserve(distance(begin, end)); + transform(begin, end, back_inserter(result.protocol), ::tolower); + advance(end, prot_end.size()); + begin = end; - end = search(begin, str.end(), host_end.begin(), host_end.end()); - result.host.reserve(distance(begin, end)); - transform(begin, end, back_inserter(result.host), ::tolower); - advance(end, host_end.size()); - begin = end; + end = search(begin, str.end(), host_end.begin(), host_end.end()); + result.host.reserve(distance(begin, end)); + transform(begin, end, back_inserter(result.host), ::tolower); + advance(end, host_end.size()); + begin = end; - result.port = RTSP_DEFAULT_PORT; + result.port = RTSP_DEFAULT_PORT; - result.path.reserve(distance(begin, str.end())); - transform(begin, str.end(), back_inserter(result.path), ::tolower); + result.path.reserve(distance(begin, str.end())); + transform(begin, str.end(), back_inserter(result.path), ::tolower); - return result; + return result; } -void split_string(const string& s, char delim, vector& elems) { - stringstream ss; - ss.str(s); +void split_string(const string& s, char delim, vector& elems) +{ + stringstream ss; + ss.str(s); - string item; - while(getline(ss, item, delim)) { - elems.push_back(item); - } + string item; + while (getline(ss, item, delim)) + { + elems.push_back(item); + } } -static int tcp_sock_read_line(string &line) { - static string buf; +static int tcp_sock_read_line(string& line) +{ + static string buf; - while(true) { - string::size_type pos = buf.find("\r\n"); - if(pos != string::npos) { - line = buf.substr(0, pos); - buf.erase(0, pos + 2); - return 0; - } + while (true) + { + string::size_type pos = buf.find("\r\n"); + if (pos != string::npos) + { + line = buf.substr(0, pos); + buf.erase(0, pos + 2); + return 0; + } - char tmp_buf[2048]; - int size = rtsp->tcp_sock.receive(tmp_buf, sizeof(tmp_buf), 1); - if(size <= 0) { - return 1; - } + char tmp_buf[2048]; + int size = rtsp->tcp_sock.receive(tmp_buf, sizeof(tmp_buf), 1); + if (size <= 0) + { + 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) { - stringstream res; - res << u.protocol << "://" << u.host; - if (u.port > 0) - res << ":" << u.port; - res << "/" << u.path; + stringstream res; + res << u.protocol << "://" << u.host; + if (u.port > 0) + res << ":" << u.port; + res << "/" << u.path; - return res.str(); + return res.str(); } -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) +static void parse_session(char* request_line, char* session, unsigned max, int* timeout) { - int p = atoi(str); - if (p < 0 || p > UINT16_MAX) - return -1; + char* state; + char* tok; - *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) { - char *state; - char *tok; - int err; +static int parse_port(char* str, uint16_t* port) +{ + int p = atoi(str); + if (p < 0 || p > UINT16_MAX) + return -1; - tok = strtok_r(request_line, ";", &state); - if (tok == NULL || strncmp(tok, "RTP/AVP", 7) != 0) - return -1; + *port = p; - tok = strtok_r(NULL, ";", &state); - 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; + return 0; } -#define skip_whitespace(x) while(*x == ' ') x++ -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; +static int parse_transport(char* request_line) +{ + char* state; + char* tok; + int err; - /* Parse header */ - while (!have_header) { - if (tcp_sock_read_line(in_str) < 0) - break; - in = const_cast(in_str.c_str()); + tok = strtok_r(request_line, ";", &state); + if (tok == nullptr || strncmp(tok, "RTP/AVP", 7) != 0) + return -1; - if (strncmp(in, "RTSP/1.0 ", 9) == 0) { - rtsp_result = atoi(in + 9); - } else if (strncmp(in, "Content-Base:", 13) == 0) { - free(rtsp->content_base); + tok = strtok_r(nullptr, ";", &state); + if (tok == nullptr || strncmp(tok, "multicast", 9) != 0) + return 0; - val = in + 13; - skip_whitespace(val); + while ((tok = strtok_r(nullptr, ";", &state)) != nullptr) + { + 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); - } else if (strncmp(in, "Content-Length:", 15) == 0) { - val = in + 16; - skip_whitespace(val); + memset(port, 0x00, 6); + strncpy(port, tok + 5, min(strlen(tok + 5), (size_t)5)); + if ((end = strstr(port, "-")) != nullptr) + *end = '\0'; + err = parse_port(port, &rtsp->udp_port); + if (err) + return err; + } + } - content_length = atoi(val); - } else if (strncmp("Session:", in, 8) == 0) { - val = in + 8; - skip_whitespace(val); + return 0; +} - parse_session(val, rtsp->session_id, 64, &rtsp->keepalive_interval); - } else if (strncmp("Transport:", in, 10) == 0) { - val = in + 10; - skip_whitespace(val); +#define skip_whitespace(x) \ + while (*x == ' ') \ + x++ +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) { - rtsp_result = -1; - break; - } - } else if (strncmp("com.ses.streamID:", in, 17) == 0) { - val = in + 17; - skip_whitespace(val); + /* Parse header */ + while (!have_header) + { + if (tcp_sock_read_line(in_str) < 0) + break; + in = const_cast(in_str.c_str()); - rtsp->stream_id = atoi(val); - } else if (in[0] == '\0') { - have_header = true; - } - } + if (strncmp(in, "RTSP/1.0 ", 9) == 0) + { + rtsp_result = atoi(in + 9); + } + else if (strncmp(in, "Content-Base:", 13) == 0) + { + free(rtsp->content_base); - /* Discard further content */ - while (content_length > 0 && - (read = rtsp->tcp_sock.receive((char*)buffer, sizeof(buffer), min(sizeof(buffer), content_length)))) - content_length -= read; + val = in + 13; + skip_whitespace(val); - 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) { - string setup_url_str; - const char *psz_setup_url; - stringstream setup_ss; - stringstream play_ss; - url setup_url; + string setup_url_str; + const char* psz_setup_url; + stringstream setup_ss; + stringstream play_ss; + url setup_url; - rtsp_close(); - rtsp = new rtsp_client(); - if (rtsp == NULL) - return false; + rtsp_close(); + rtsp = new rtsp_client(); + if (rtsp == nullptr) + return false; - rtsp->name = name; - rtsp->level = 0; - rtsp->quality = 0; + rtsp->name = name; + rtsp->level = 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); - libKodi->Log(LOG_DEBUG, "connect to host '%s'", dst.host.c_str()); + url dst = parse_url(url_str); + libKodi->Log(LOG_DEBUG, "connect to host '%s'", dst.host.c_str()); - 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; - } + 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; + } - // TODO: tcp keep alive? + // TODO: tcp keep alive? - if (asprintf(&rtsp->content_base, "rtsp://%s:%d/", dst.host.c_str(), - dst.port) < 0) { - rtsp->content_base = NULL; - goto error; - } + if (asprintf(&rtsp->content_base, "rtsp://%s:%d/", dst.host.c_str(), dst.port) < 0) + { + rtsp->content_base = nullptr; + goto error; + } - rtsp->last_seq_nr = 0; - rtsp->keepalive_interval = (KEEPALIVE_INTERVAL - KEEPALIVE_MARGIN); + rtsp->last_seq_nr = 0; + 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 - if (!strncasecmp(setup_url.protocol.c_str(), "satip", 5)) { - setup_url.protocol = "rtsp"; - } + // reverse the satip protocol trick, as SAT>IP believes to be RTSP + if (!strncasecmp(setup_url.protocol.c_str(), "satip", 5)) + { + setup_url.protocol = "rtsp"; + } - setup_url_str = compose_url(setup_url); - psz_setup_url = setup_url_str.c_str(); + setup_url_str = compose_url(setup_url); + psz_setup_url = setup_url_str.c_str(); - // TODO: Find available port - rtsp->udp_sock = Socket(af_inet, pf_inet, sock_dgram, udp); - rtsp->udp_port = 6785; - if(!rtsp->udp_sock.bind(rtsp->udp_port)) { - goto error; - } + // TODO: Find available port + rtsp->udp_sock = Socket(af_inet, pf_inet, sock_dgram, udp); + rtsp->udp_port = 6785; + if (!rtsp->udp_sock.bind(rtsp->udp_port)) + { + goto error; + } - setup_ss << "SETUP " << setup_url_str<< " RTSP/1.0\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"; - rtsp->tcp_sock.send(setup_ss.str()); + setup_ss << "SETUP " << setup_url_str << " RTSP/1.0\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"; + rtsp->tcp_sock.send(setup_ss.str()); - if (rtsp_handle() != RTSP_RESULT_OK) { - libKodi->Log(LOG_ERROR, "Failed to setup RTSP session"); - goto error; - } + if (rtsp_handle() != RTSP_RESULT_OK) + { + libKodi->Log(LOG_ERROR, "Failed to setup RTSP session"); + goto error; + } - if (asprintf(&rtsp->control, "%sstream=%d", rtsp->content_base, rtsp->stream_id) < 0) { - rtsp->control = NULL; - goto error; - } + if (asprintf(&rtsp->control, "%sstream=%d", rtsp->content_base, rtsp->stream_id) < 0) + { + rtsp->control = nullptr; + goto error; + } - play_ss << "PLAY " << rtsp->control << " RTSP/1.0\r\n"; - play_ss << "CSeq: " << rtsp->cseq++ << "\r\n"; - play_ss << "Session: " << rtsp->session_id << "\r\n\r\n"; - rtsp->tcp_sock.send(play_ss.str()); + play_ss << "PLAY " << rtsp->control << " RTSP/1.0\r\n"; + play_ss << "CSeq: " << rtsp->cseq++ << "\r\n"; + play_ss << "Session: " << rtsp->session_id << "\r\n\r\n"; + rtsp->tcp_sock.send(play_ss.str()); - if (rtsp_handle() != RTSP_RESULT_OK) { - libKodi->Log(LOG_ERROR, "Failed to play RTSP session"); - goto error; - } + if (rtsp_handle() != RTSP_RESULT_OK) + { + libKodi->Log(LOG_ERROR, "Failed to play RTSP session"); + goto error; + } - rtsp->rtcp_sock = Socket(af_inet, pf_inet, sock_dgram, udp); - if(!rtsp->rtcp_sock.bind(rtsp->udp_port + 1)) { - goto error; - } - if(!rtsp->rtcp_sock.set_non_blocking(true)) { - goto error; - } + rtsp->rtcp_sock = Socket(af_inet, pf_inet, sock_dgram, udp); + if (!rtsp->rtcp_sock.bind(rtsp->udp_port + 1)) + { + goto error; + } + if (!rtsp->rtcp_sock.set_non_blocking(true)) + { + goto error; + } - return true; + return true; error: - rtsp_close(); - return false; + rtsp_close(); + return false; } -static void parse_rtcp(const char *buf, int size) { - int offset = 0; - while(size > 4) { - const rtcp_app *app = reinterpret_cast(buf + offset); - uint16_t len = 4 * (ntohs(app->len) + 1); +static void parse_rtcp(const char* buf, int size) +{ + int offset = 0; + while (size > 4) + { + const rtcp_app* app = reinterpret_cast(buf + offset); + uint16_t len = 4 * (ntohs(app->len) + 1); - if((app->pt != 204) || (memcmp(app->name, "SES1", 4) != 0)) { - size -= len; - offset += len; - continue; - } + if ((app->pt != 204) || (memcmp(app->name, "SES1", 4) != 0)) + { + size -= len; + offset += len; + continue; + } - uint16_t string_len = ntohs(app->string_len); - string app_data(&buf[offset + sizeof(rtcp_app)], string_len); + uint16_t string_len = ntohs(app->string_len); + string app_data(&buf[offset + sizeof(rtcp_app)], string_len); - vector elems; - split_string(app_data, ';', elems); - if(elems.size() != 4) { - return; - } + vector elems; + split_string(app_data, ';', elems); + if (elems.size() != 4) + { + return; + } - vector tuner; - split_string(elems[2], ',', tuner); - if(tuner.size() < 4) { - return; - } + vector tuner; + split_string(elems[2], ',', tuner); + if (tuner.size() < 4) + { + return; + } - rtsp->level = atoi(tuner[1].c_str()); - rtsp->quality = atoi(tuner[3].c_str()); + rtsp->level = atoi(tuner[1].c_str()); + rtsp->quality = atoi(tuner[3].c_str()); - return; - } + return; + } } -int rtsp_read(void *buf, unsigned buf_size) { - sockaddr addr; - socklen_t addr_len = sizeof(addr); - int ret = rtsp->udp_sock.recvfrom((char *)buf, buf_size, (sockaddr *)&addr, &addr_len); +int rtsp_read(void* buf, unsigned buf_size) +{ + sockaddr addr; + 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]; - int rtcp_len = rtsp->rtcp_sock.recvfrom(rtcp_buf, RTCP_BUFFER_SIZE, (sockaddr *)&addr, &addr_len); - parse_rtcp(rtcp_buf, rtcp_len); + char rtcp_buf[RTCP_BUFFER_SIZE]; + int rtcp_len = rtsp->rtcp_sock.recvfrom(rtcp_buf, RTCP_BUFFER_SIZE, (sockaddr*)&addr, &addr_len); + parse_rtcp(rtcp_buf, rtcp_len); - // TODO: check ip + // TODO: check ip - return ret; + return ret; } -static void rtsp_teardown() { - if(!rtsp->tcp_sock.is_valid()) { - return; - } +static void rtsp_teardown() +{ + if (!rtsp->tcp_sock.is_valid()) + { + return; + } - if (rtsp->session_id[0] > 0) { - char *msg; - int len; - stringstream ss; + if (rtsp->session_id[0] > 0) + { + char* msg; + int len; + stringstream ss; - rtsp->udp_sock.close(); + rtsp->udp_sock.close(); - ss << "TEARDOWN " << rtsp->control << " RTSP/1.0\r\n"; - ss << "CSeq: " << rtsp->cseq++ << "\r\n"; - ss << "Session: " << rtsp->session_id << "\r\n\r\n"; - rtsp->tcp_sock.send(ss.str()); + ss << "TEARDOWN " << rtsp->control << " RTSP/1.0\r\n"; + ss << "CSeq: " << rtsp->cseq++ << "\r\n"; + ss << "Session: " << rtsp->session_id << "\r\n\r\n"; + rtsp->tcp_sock.send(ss.str()); - if (rtsp_handle() != RTSP_RESULT_OK) { - libKodi->Log(LOG_ERROR, "Failed to teardown RTSP session"); - return; - } - } + if (rtsp_handle() != RTSP_RESULT_OK) + { + libKodi->Log(LOG_ERROR, "Failed to teardown RTSP session"); + return; + } + } } void rtsp_close() { - if(rtsp) { - rtsp_teardown(); - rtsp->tcp_sock.close(); - rtsp->udp_sock.close(); - rtsp->rtcp_sock.close(); - delete rtsp; - rtsp = NULL; - } + if (rtsp) + { + rtsp_teardown(); + rtsp->tcp_sock.close(); + rtsp->udp_sock.close(); + rtsp->rtcp_sock.close(); + delete rtsp; + rtsp = nullptr; + } } -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); - signal_status->iSNR = 0x1111 * rtsp->quality; - signal_status->iSignal = 0x101 * rtsp->level; - } +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); + signal_status->iSNR = 0x1111 * rtsp->quality; + signal_status->iSignal = 0x101 * rtsp->level; + } } diff --git a/src/rtsp_client.hpp b/src/rtsp_client.hpp index bcd893b..d6790f4 100644 --- a/src/rtsp_client.hpp +++ b/src/rtsp_client.hpp @@ -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 #include +#include bool rtsp_open(const std::string& name, const std::string& url_str); 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); -#endif