From e7449d9537a650790c2e742a57277d46b6cf6b33 Mon Sep 17 00:00:00 2001 From: Alwin Esch Date: Sun, 7 Jun 2020 19:06:40 +0200 Subject: [PATCH] change to new C++ addon interface --- CMakeLists.txt | 41 ++- depends/common/p8-platform/p8-platform.txt | 1 - .../windowsstore/p8-platform/p8-platform.txt | 1 - src/OctonetData.cpp | 292 ++++++++++------- src/OctonetData.h | 54 +++- src/Socket.cpp | 45 ++- src/addon.cpp | 62 ++++ src/addon.h | 36 +++ src/client.cpp | 300 ------------------ src/client.h | 24 -- src/rtsp_client.cpp | 25 +- src/rtsp_client.hpp | 5 +- 12 files changed, 373 insertions(+), 513 deletions(-) delete mode 100644 depends/common/p8-platform/p8-platform.txt delete mode 100644 depends/windowsstore/p8-platform/p8-platform.txt create mode 100644 src/addon.cpp create mode 100644 src/addon.h delete mode 100644 src/client.cpp delete mode 100644 src/client.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 30c00b8..c3cdf78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,37 +4,34 @@ project(pvr.octonet) list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) find_package(Kodi REQUIRED) -find_package(p8-platform REQUIRED) find_package(JsonCpp REQUIRED) -include_directories( - ${p8-platform_INCLUDE_DIRS} - ${KODI_INCLUDE_DIR}/.. # Hack way with "/..", need bigger Kodi cmake rework to match right include ways - ${JSONCPP_INCLUDE_DIRS}) +include_directories(${KODI_INCLUDE_DIR}/.. # Hack way with "/..", need bigger Kodi cmake rework to match right include ways + ${JSONCPP_INCLUDE_DIRS}) -set(DEPLIBS - ${p8-platform_LIBRARIES} - ${JSONCPP_LIBRARIES}) +set(DEPLIBS ${JSONCPP_LIBRARIES}) -set(OCTONET_SOURCES - src/OctonetData.cpp - src/client.cpp - src/Socket.cpp - src/rtsp_client.cpp) +set(OCTONET_SOURCES src/addon.cpp + src/OctonetData.cpp + src/Socket.cpp + src/rtsp_client.cpp) -set(OCTONET_HEADERS - src/client.h - src/OctonetData.h - src/Socket.h) +set(OCTONET_HEADERS src/addon.h + src/OctonetData.h + src/Socket.h + src/rtsp_client.hpp) + +addon_version(pvr.octonet OCTONET) +add_definitions(-DOCTONET_VERSION=${OCTONET_VERSION}) build_addon(pvr.octonet OCTONET DEPLIBS) if(WIN32) - if(NOT CMAKE_SYSTEM_NAME STREQUAL WindowsStore) - target_link_libraries(pvr.octonet wsock32 ws2_32) - else() - target_link_libraries(pvr.octonet ws2_32) - endif() + if(NOT CMAKE_SYSTEM_NAME STREQUAL WindowsStore) + target_link_libraries(pvr.octonet wsock32 ws2_32) + else() + target_link_libraries(pvr.octonet ws2_32) + endif() endif() include(CPack) diff --git a/depends/common/p8-platform/p8-platform.txt b/depends/common/p8-platform/p8-platform.txt deleted file mode 100644 index 98ed58a..0000000 --- a/depends/common/p8-platform/p8-platform.txt +++ /dev/null @@ -1 +0,0 @@ -p8-platform https://github.com/xbmc/platform.git cee64e9dc0b69e8d286dc170a78effaabfa09c44 diff --git a/depends/windowsstore/p8-platform/p8-platform.txt b/depends/windowsstore/p8-platform/p8-platform.txt deleted file mode 100644 index db6f782..0000000 --- a/depends/windowsstore/p8-platform/p8-platform.txt +++ /dev/null @@ -1 +0,0 @@ -p8-platform https://github.com/afedchin/platform.git win10 diff --git a/src/OctonetData.cpp b/src/OctonetData.cpp index 6aa50aa..6de76ba 100644 --- a/src/OctonetData.cpp +++ b/src/OctonetData.cpp @@ -10,7 +10,11 @@ #include "OctonetData.h" +#include "rtsp_client.hpp" + #include +#include +#include #include #include @@ -18,9 +22,10 @@ #define timegm _mkgmtime #endif -using namespace ADDON; - -OctonetData::OctonetData() +OctonetData::OctonetData(const std::string& octonetAddress, + KODI_HANDLE instance, + const std::string& kodiVersion) + : kodi::addon::CInstancePVRClient(instance, kodiVersion) { m_serverAddress = octonetAddress; m_channels.clear(); @@ -28,13 +33,77 @@ OctonetData::OctonetData() m_lastEpgLoad = 0; if (!LoadChannelList()) - libKodi->QueueNotification(QUEUE_ERROR, libKodi->GetLocalizedString(30001), m_channels.size()); + kodi::QueueFormattedNotification(QUEUE_ERROR, kodi::GetLocalizedString(30001).c_str(), + m_channels.size()); + + /* + // Currently unused, as thread was already present before with + // p8platform, by remove of them was it added as C++11 thread way. + kodi::Log(ADDON_LOG_INFO, "%s Starting separate client update thread...", __func__); + m_running = true; + m_thread = std::thread([&] { Process(); }); + */ } OctonetData::~OctonetData(void) { - m_channels.clear(); - m_groups.clear(); + /* + m_running = false; + if (m_thread.joinable()) + m_thread.join(); + */ +} + +PVR_ERROR OctonetData::GetCapabilities(kodi::addon::PVRCapabilities& capabilities) +{ + capabilities.SetSupportsTV(true); + capabilities.SetSupportsRadio(true); + capabilities.SetSupportsChannelGroups(true); + capabilities.SetSupportsEPG(true); + capabilities.SetSupportsRecordings(false); + capabilities.SetSupportsRecordingsRename(false); + capabilities.SetSupportsRecordingsLifetimeChange(false); + capabilities.SetSupportsDescrambleInfo(false); + + return PVR_ERROR_NO_ERROR; +} + +PVR_ERROR OctonetData::GetBackendName(std::string& name) +{ + name = "Digital Devices Octopus NET Client"; + return PVR_ERROR_NO_ERROR; +} + +PVR_ERROR OctonetData::GetBackendVersion(std::string& version) +{ + version = STR(OCTONET_VERSION); + return PVR_ERROR_NO_ERROR; +} + +PVR_ERROR OctonetData::GetConnectionString(std::string& connection) +{ + connection = "connected"; // FIXME: translate? + return PVR_ERROR_NO_ERROR; +} + +PVR_ERROR OctonetData::GetBackendHostname(std::string& hostname) +{ + hostname = m_serverAddress; + return PVR_ERROR_NO_ERROR; +} + +PVR_ERROR OctonetData::OnSystemSleep() +{ + kodi::Log(ADDON_LOG_INFO, "Received event: %s", __func__); + // FIXME: Disconnect? + return PVR_ERROR_NO_ERROR; +} + +PVR_ERROR OctonetData::OnSystemWake() +{ + kodi::Log(ADDON_LOG_INFO, "Received event: %s", __func__); + // FIXME:Reconnect? + return PVR_ERROR_NO_ERROR; } int64_t OctonetData::ParseID(std::string id) @@ -48,16 +117,15 @@ int64_t OctonetData::ParseID(std::string id) bool OctonetData::LoadChannelList() { std::string jsonContent; - void* f = - libKodi->OpenFile(("http://" + m_serverAddress + "/channellist.lua?select=json").c_str(), 0); - if (!f) + kodi::vfs::CFile f; + if (!f.OpenFile("http://" + m_serverAddress + "/channellist.lua?select=json", 0)) return false; char buf[1024]; - while (int read = libKodi->ReadFile(f, buf, 1024)) + while (int read = f.Read(buf, 1024)) jsonContent.append(buf, read); - libKodi->CloseFile(f); + f.Close(); Json::Value root; Json::Reader reader; @@ -96,14 +164,13 @@ bool OctonetData::LoadChannelList() OctonetChannel* OctonetData::FindChannel(int64_t nativeId) { - std::vector::iterator it; - for (it = m_channels.begin(); it < m_channels.end(); ++it) + for (auto& channel : m_channels) { - if (it->nativeId == nativeId) - return &*it; + if (channel.nativeId == nativeId) + return &channel; } - return NULL; + return nullptr; } time_t OctonetData::ParseDateTime(std::string date) @@ -134,19 +201,19 @@ time_t OctonetData::ParseDateTime(std::string date) bool OctonetData::LoadEPG(void) { /* Reload at most every 30 seconds */ - if (m_lastEpgLoad + 30 > time(NULL)) + if (m_lastEpgLoad + 30 > time(nullptr)) return false; std::string jsonContent; - void* f = libKodi->OpenFile(("http://" + m_serverAddress + "/epg.lua?;#|encoding=gzip").c_str(), 0); - if (!f) + kodi::vfs::CFile f; + if (!f.OpenFile("http://" + m_serverAddress + "/epg.lua?;#|encoding=gzip", 0)) return false; char buf[1024]; - while (int read = libKodi->ReadFile(f, buf, 1024)) + while (int read = f.Read(buf, 1024)) jsonContent.append(buf, read); - libKodi->CloseFile(f); + f.Close(); Json::Value root; Json::Reader reader; @@ -155,7 +222,7 @@ bool OctonetData::LoadEPG(void) return false; const Json::Value eventList = root["EventList"]; - OctonetChannel* channel = NULL; + OctonetChannel* channel = nullptr; for (unsigned int i = 0; i < eventList.size(); i++) { const Json::Value event = eventList[i]; @@ -170,63 +237,66 @@ bool OctonetData::LoadEPG(void) channelId = channelId.substr(0, channelId.rfind(":")); entry.channelId = ParseID(channelId); - entry.id = atoi(epgId.c_str()); + entry.id = std::stoi(epgId); - if (channel == NULL || channel->nativeId != entry.channelId) + if (channel == nullptr || channel->nativeId != entry.channelId) channel = FindChannel(entry.channelId); - if (channel == NULL) + if (channel == nullptr) { - libKodi->Log(LOG_ERROR, "EPG for unknown channel."); + kodi::Log(ADDON_LOG_ERROR, "EPG for unknown channel."); continue; } channel->epg.push_back(entry); } - m_lastEpgLoad = time(NULL); + m_lastEpgLoad = time(nullptr); return true; } -void* OctonetData::Process(void) +void OctonetData::Process() { - return NULL; + return; } -int OctonetData::getChannelCount(void) +PVR_ERROR OctonetData::GetChannelsAmount(int& amount) { - return m_channels.size(); + amount = m_channels.size(); + return PVR_ERROR_NO_ERROR; } -PVR_ERROR OctonetData::getChannels(ADDON_HANDLE handle, bool bRadio) +PVR_ERROR OctonetData::GetChannels(bool radio, kodi::addon::PVRChannelsResultSet& results) { for (unsigned int i = 0; i < m_channels.size(); i++) { OctonetChannel& channel = m_channels.at(i); - if (channel.radio == bRadio) + if (channel.radio == radio) { - PVR_CHANNEL chan; - memset(&chan, 0, sizeof(PVR_CHANNEL)); + kodi::addon::PVRChannel chan; - 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.SetUniqueId(channel.id); + chan.SetIsRadio(channel.radio); + chan.SetChannelNumber(i); + chan.SetChannelName(channel.name); + chan.SetMimeType("video/x-mpegts"); + chan.SetIsHidden(false); - pvr->TransferChannelEntry(handle, &chan); + results.Add(chan); } } return PVR_ERROR_NO_ERROR; } -PVR_ERROR OctonetData::getEPG(ADDON_HANDLE handle, int iChannelUid, time_t start, time_t end) +PVR_ERROR OctonetData::GetEPGForChannel(int channelUid, + time_t start, + time_t end, + kodi::addon::PVREPGTagsResultSet& results) { for (unsigned int i = 0; i < m_channels.size(); i++) { OctonetChannel& chan = m_channels.at(i); - if (iChannelUid != chan.id) + if (channelUid != chan.id) continue; if (chan.epg.empty()) @@ -236,58 +306,49 @@ PVR_ERROR OctonetData::getEPG(ADDON_HANDLE handle, int iChannelUid, time_t start // 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) + for (const auto& epg : chan.epg) { - if (it->end > last_end) - last_end = it->end; + if (epg.end > last_end) + last_end = epg.end; - if (it->end < start || it->start > end) + if (epg.end < start || epg.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; + kodi::addon::PVREPGTag entry; - 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.SetUniqueChannelId(chan.id); + entry.SetUniqueBroadcastId(epg.id); + entry.SetTitle(epg.title); + entry.SetPlotOutline(epg.subtitle); + entry.SetStartTime(epg.start); + entry.SetEndTime(epg.end); - pvr->TransferEpgEntry(handle, &entry); + results.Add(entry); } if (last_end < end) LoadEPG(); - for (it = chan.epg.begin(); it != chan.epg.end(); ++it) + for (const auto& epg : chan.epg) { - if (it->end < start || it->start > end) + if (epg.end < start || epg.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; + kodi::addon::PVREPGTag entry; - 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.SetUniqueChannelId(chan.id); + entry.SetUniqueBroadcastId(epg.id); + entry.SetTitle(epg.title); + entry.SetPlotOutline(epg.subtitle); + entry.SetStartTime(epg.start); + entry.SetEndTime(epg.end); - pvr->TransferEpgEntry(handle, &entry); + results.Add(entry); } } @@ -296,12 +357,11 @@ PVR_ERROR OctonetData::getEPG(ADDON_HANDLE handle, int iChannelUid, time_t start const std::string& OctonetData::GetUrl(int id) const { - for (std::vector::const_iterator iter = m_channels.begin(); iter != m_channels.end(); - ++iter) + for (const auto& channel : m_channels) { - if (iter->id == id) + if (channel.id == id) { - return iter->url; + return channel.url; } } @@ -310,61 +370,59 @@ const std::string& OctonetData::GetUrl(int id) const const std::string& OctonetData::GetName(int id) const { - for (std::vector::const_iterator iter = m_channels.begin(); iter != m_channels.end(); - ++iter) + for (const auto& channel : m_channels) { - if (iter->id == id) + if (channel.id == id) { - return iter->name; + return channel.name; } } return m_channels[0].name; } -int OctonetData::getGroupCount(void) +PVR_ERROR OctonetData::GetChannelGroupsAmount(int& amount) { - return m_groups.size(); + amount = m_groups.size(); + return PVR_ERROR_NO_ERROR; } -PVR_ERROR OctonetData::getGroups(ADDON_HANDLE handle, bool bRadio) +PVR_ERROR OctonetData::GetChannelGroups(bool radio, kodi::addon::PVRChannelGroupsResultSet& results) { - for (unsigned int i = 0; i < m_groups.size(); i++) + for (const auto& group : m_groups) { - OctonetGroup& group = m_groups.at(i); - if (group.radio == bRadio) + if (group.radio == radio) { - PVR_CHANNEL_GROUP g; - memset(&g, 0, sizeof(PVR_CHANNEL_GROUP)); + kodi::addon::PVRChannelGroup g; - g.iPosition = 0; - g.bIsRadio = group.radio; - strncpy(g.strGroupName, group.name.c_str(), strlen(group.name.c_str())); + g.SetPosition(0); + g.SetIsRadio(group.radio); + g.SetGroupName(group.name); - pvr->TransferChannelGroup(handle, &g); + results.Add(g); } } return PVR_ERROR_NO_ERROR; } -PVR_ERROR OctonetData::getGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP& group) +PVR_ERROR OctonetData::GetChannelGroupMembers(const kodi::addon::PVRChannelGroup& group, + kodi::addon::PVRChannelGroupMembersResultSet& results) { - OctonetGroup* g = FindGroup(group.strGroupName); - if (g == NULL) + const OctonetGroup* g = FindGroup(group.GetGroupName()); + if (g == nullptr) return PVR_ERROR_UNKNOWN; 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)); + kodi::addon::PVRChannelGroupMember m; - strncpy(m.strGroupName, group.strGroupName, strlen(group.strGroupName)); - m.iChannelUniqueId = channel.id; - m.iChannelNumber = channel.id; + m.SetGroupName(group.GetGroupName()); + m.SetChannelUniqueId(channel.id); + m.SetChannelNumber(channel.id); - pvr->TransferChannelGroupMember(handle, &m); + results.Add(m); } return PVR_ERROR_NO_ERROR; @@ -372,11 +430,29 @@ PVR_ERROR OctonetData::getGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GR OctonetGroup* OctonetData::FindGroup(const std::string& name) { - for (unsigned int i = 0; i < m_groups.size(); i++) + for (auto& group : m_groups) { - if (m_groups.at(i).name == name) - return &m_groups.at(i); + if (group.name == name) + return &group; } - return NULL; + return nullptr; +} + +/* PVR stream handling */ +/* entirely unused, as we use standard RTSP+TS mux, which can be handlded by + * Kodi core */ +bool OctonetData::OpenLiveStream(const kodi::addon::PVRChannel& channelinfo) +{ + return rtsp_open(GetName(channelinfo.GetUniqueId()), GetUrl(channelinfo.GetUniqueId())); +} + +int OctonetData::ReadLiveStream(unsigned char* pBuffer, unsigned int iBufferSize) +{ + return rtsp_read(pBuffer, iBufferSize); +} + +void OctonetData::CloseLiveStream() +{ + rtsp_close(); } diff --git a/src/OctonetData.h b/src/OctonetData.h index b5af368..909f392 100644 --- a/src/OctonetData.h +++ b/src/OctonetData.h @@ -10,10 +10,9 @@ #pragma once -#include "client.h" - -#include "p8-platform/threads/threads.h" - +#include +#include +#include #include struct OctonetEpgEntry @@ -44,25 +43,45 @@ struct OctonetGroup std::vector members; }; -class OctonetData : public P8PLATFORM::CThread +class ATTRIBUTE_HIDDEN OctonetData : public kodi::addon::CInstancePVRClient { public: - OctonetData(void); - virtual ~OctonetData(void); + OctonetData(const std::string& octonetAddress, + KODI_HANDLE instance, + const std::string& kodiVersion); + ~OctonetData() override; - virtual int getChannelCount(void); - virtual PVR_ERROR getChannels(ADDON_HANDLE handle, bool bRadio); + PVR_ERROR GetCapabilities(kodi::addon::PVRCapabilities& capabilities) override; + PVR_ERROR GetBackendName(std::string& name) override; + PVR_ERROR GetBackendVersion(std::string& version) override; + PVR_ERROR GetConnectionString(std::string& connection) override; + PVR_ERROR GetBackendHostname(std::string& hostname) override; - 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); + PVR_ERROR OnSystemSleep() override; + PVR_ERROR OnSystemWake() override; - 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; + PVR_ERROR GetChannelsAmount(int& amount) override; + PVR_ERROR GetChannels(bool radio, kodi::addon::PVRChannelsResultSet& results) override; + + PVR_ERROR GetChannelGroupsAmount(int& amount) override; + PVR_ERROR GetChannelGroups(bool radio, kodi::addon::PVRChannelGroupsResultSet& results) override; + PVR_ERROR GetChannelGroupMembers(const kodi::addon::PVRChannelGroup& group, + kodi::addon::PVRChannelGroupMembersResultSet& results) override; + + PVR_ERROR GetEPGForChannel(int channelUid, + time_t start, + time_t end, + kodi::addon::PVREPGTagsResultSet& results) override; + + bool OpenLiveStream(const kodi::addon::PVRChannel& channelinfo) override; + int ReadLiveStream(unsigned char* buffer, unsigned int size) override; + void CloseLiveStream() override; protected: - void* Process(void) override; + void Process(); + + const std::string& GetUrl(int id) const; + const std::string& GetName(int id) const; bool LoadChannelList(void); bool LoadEPG(void); @@ -77,4 +96,7 @@ private: std::vector m_groups; time_t m_lastEpgLoad; + + std::atomic m_running = {false}; + std::thread m_thread; }; diff --git a/src/Socket.cpp b/src/Socket.cpp index 611af62..2ba9fb2 100644 --- a/src/Socket.cpp +++ b/src/Socket.cpp @@ -8,16 +8,11 @@ #include "Socket.h" -#include "client.h" - -#include "kodi/libXBMC_addon.h" -#include "p8-platform/os.h" - #include +#include #include using namespace std; -using namespace ADDON; namespace OCTO { @@ -146,7 +141,8 @@ bool Socket::accept(Socket& new_socket) const } socklen_t addr_length = sizeof(m_sockaddr); - new_socket.m_sd = ::accept(m_sd, const_cast((const sockaddr*)&m_sockaddr), &addr_length); + new_socket.m_sd = + ::accept(m_sd, const_cast((const sockaddr*)&m_sockaddr), &addr_length); #ifdef TARGET_WINDOWS if (new_socket.m_sd == INVALID_SOCKET) @@ -192,13 +188,13 @@ int Socket::send(const char* data, const unsigned int len) if (result < 0) { - libKodi->Log(LOG_ERROR, "Socket::send - select failed"); + kodi::Log(ADDON_LOG_ERROR, "Socket::send - select failed"); close(); return 0; } if (FD_ISSET(m_sd, &set_w)) { - libKodi->Log(LOG_ERROR, "Socket::send - failed to send data"); + kodi::Log(ADDON_LOG_ERROR, "Socket::send - failed to send data"); close(); return 0; } @@ -208,7 +204,7 @@ int Socket::send(const char* data, const unsigned int len) if (status == -1) { errormessage(getLastError(), "Socket::send"); - libKodi->Log(LOG_ERROR, "Socket::send - failed to send data"); + kodi::Log(ADDON_LOG_ERROR, "Socket::send - failed to send data"); close(); return 0; } @@ -292,8 +288,8 @@ bool Socket::ReadLine(string& line) if (result < 0) { - libKodi->Log(LOG_DEBUG, "%s: select failed", __FUNCTION__); - errormessage(getLastError(), __FUNCTION__); + kodi::Log(ADDON_LOG_DEBUG, "%s: select failed", __func__); + errormessage(getLastError(), __func__); close(); return false; } @@ -302,15 +298,15 @@ bool Socket::ReadLine(string& line) { if (retries != 0) { - libKodi->Log(LOG_DEBUG, "%s: timeout waiting for response, retrying... (%i)", __FUNCTION__, - retries); + kodi::Log(ADDON_LOG_DEBUG, "%s: timeout waiting for response, retrying... (%i)", __func__, + retries); retries--; continue; } else { - libKodi->Log(LOG_DEBUG, "%s: timeout waiting for response. Aborting after 10 retries.", - __FUNCTION__); + kodi::Log(ADDON_LOG_DEBUG, "%s: timeout waiting for response. Aborting after 10 retries.", + __func__); return false; } } @@ -318,8 +314,8 @@ bool Socket::ReadLine(string& line) result = recv(m_sd, buffer, sizeof(buffer) - 1, 0); if (result < 0) { - libKodi->Log(LOG_DEBUG, "%s: recv failed", __FUNCTION__); - errormessage(getLastError(), __FUNCTION__); + kodi::Log(ADDON_LOG_DEBUG, "%s: recv failed", __func__); + errormessage(getLastError(), __func__); close(); return false; } @@ -394,7 +390,7 @@ bool Socket::connect(const std::string& host, const unsigned short port) if (!setHostname(host)) { - libKodi->Log(LOG_ERROR, "Socket::setHostname(%s) failed.\n", host.c_str()); + kodi::Log(ADDON_LOG_ERROR, "Socket::setHostname(%s) failed.\n", host.c_str()); return false; } m_port = port; @@ -443,7 +439,7 @@ bool Socket::connect(const std::string& host, const unsigned short port) if (address == nullptr) { - libKodi->Log(LOG_ERROR, "Socket::connect %s:%u\n", host.c_str(), port); + kodi::Log(ADDON_LOG_ERROR, "Socket::connect %s:%u\n", host.c_str(), port); errormessage(getLastError(), "Socket::connect"); close(); return false; @@ -479,7 +475,8 @@ bool Socket::set_non_blocking(const bool b) if (ioctlsocket(m_sd, FIONBIO, &iMode) == -1) { - libKodi->Log(LOG_ERROR, "Socket::set_non_blocking - Can't set socket condition to: %i", iMode); + kodi::Log(ADDON_LOG_ERROR, "Socket::set_non_blocking - Can't set socket condition to: %i", + iMode); return false; } @@ -570,7 +567,7 @@ void Socket::errormessage(int errnum, const char* functionname) const default: errmsg = "WSA Error"; } - libKodi->Log(LOG_ERROR, "%s: (Winsock error=%i) %s\n", functionname, errnum, errmsg); + kodi::Log(ADDON_LOG_ERROR, "%s: (Winsock error=%i) %s\n", functionname, errnum, errmsg); } int Socket::getLastError() const @@ -628,7 +625,7 @@ bool Socket::set_non_blocking(const bool b) if (fcntl(m_sd, F_SETFL, opts) == -1) { - libKodi->Log(LOG_ERROR, "Socket::set_non_blocking - Can't set socket flags to: %i", opts); + kodi::Log(ADDON_LOG_ERROR, "Socket::set_non_blocking - Can't set socket flags to: %i", opts); return false; } return true; @@ -709,7 +706,7 @@ void Socket::errormessage(int errnum, const char* functionname) const break; } - libKodi->Log(LOG_ERROR, "%s: (errno=%i) %s\n", functionname, errnum, errmsg); + kodi::Log(ADDON_LOG_ERROR, "%s: (errno=%i) %s\n", functionname, errnum, errmsg); } int Socket::getLastError() const diff --git a/src/addon.cpp b/src/addon.cpp new file mode 100644 index 0000000..efd9afa --- /dev/null +++ b/src/addon.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Julian Scheel + * Copyright (C) 2015 jusst technologies GmbH + * Copyright (C) 2015 Digital Devices GmbH + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSE.md for more information. + * + */ + +#include "addon.h" + +#include "OctonetData.h" + +ADDON_STATUS COctonetAddon::SetSetting(const std::string& settingName, + const kodi::CSettingValue& settingValue) +{ + /* For simplicity do a full addon restart whenever settings are + * changed */ + return ADDON_STATUS_NEED_RESTART; +} + +ADDON_STATUS COctonetAddon::CreateInstance(int instanceType, + const std::string& instanceID, + KODI_HANDLE instance, + const std::string& version, + KODI_HANDLE& addonInstance) +{ + if (instanceType == ADDON_INSTANCE_PVR) + { + kodi::Log(ADDON_LOG_DEBUG, "%s: Creating octonet pvr instance", __func__); + + /* IP or hostname of the octonet to be connected to */ + std::string octonetAddress = kodi::GetSettingString("octonetAddress"); + + OctonetData* usedInstance = new OctonetData(octonetAddress, instance, version); + addonInstance = usedInstance; + + m_usedInstances.emplace(instanceID, usedInstance); + return ADDON_STATUS_OK; + } + + return ADDON_STATUS_UNKNOWN; +} + +void COctonetAddon::DestroyInstance(int instanceType, + const std::string& instanceID, + KODI_HANDLE addonInstance) +{ + if (instanceType == ADDON_INSTANCE_PVR) + { + kodi::Log(ADDON_LOG_DEBUG, "%s: Destoying octonet pvr instance", __func__); + + const auto& it = m_usedInstances.find(instanceID); + if (it != m_usedInstances.end()) + { + m_usedInstances.erase(it); + } + } +} + +ADDONCREATOR(COctonetAddon) diff --git a/src/addon.h b/src/addon.h new file mode 100644 index 0000000..6b340d0 --- /dev/null +++ b/src/addon.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015 Julian Scheel + * Copyright (C) 2015 jusst technologies GmbH + * Copyright (C) 2015 Digital Devices GmbH + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSE.md for more information. + * + */ + +#pragma once + +#include +#include + +class OctonetData; + +class ATTRIBUTE_HIDDEN COctonetAddon : public kodi::addon::CAddonBase +{ +public: + COctonetAddon() = default; + + ADDON_STATUS SetSetting(const std::string& settingName, + const kodi::CSettingValue& settingValue) override; + ADDON_STATUS CreateInstance(int instanceType, + const std::string& instanceID, + KODI_HANDLE instance, + const std::string& version, + KODI_HANDLE& addonInstance) override; + void DestroyInstance(int instanceType, + const std::string& instanceID, + KODI_HANDLE addonInstance) override; + +private: + std::unordered_map m_usedInstances; +}; diff --git a/src/client.cpp b/src/client.cpp deleted file mode 100644 index 379c801..0000000 --- a/src/client.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright (C) 2015 Julian Scheel - * Copyright (C) 2015 jusst technologies GmbH - * Copyright (C) 2015 Digital Devices GmbH - * - * SPDX-License-Identifier: GPL-2.0-or-later - * See LICENSE.md for more information. - * - */ - -#include "client.h" - -#include "OctonetData.h" -#include "rtsp_client.hpp" - -#include -#include -#include - -using namespace ADDON; - -/* setting variables with defaults */ -std::string octonetAddress = ""; - -/* internal state variables */ -ADDON_STATUS addonStatus = ADDON_STATUS_UNKNOWN; -CHelper_libXBMC_addon* libKodi = nullptr; -CHelper_libXBMC_pvr* pvr = nullptr; - -OctonetData* data = nullptr; - -/* KODI Core Addon functions - * see xbmc_addon_dll.h */ - -extern "C" -{ - - void ADDON_ReadSettings(void) - { - char buffer[2048]; - if (libKodi->GetSetting("octonetAddress", &buffer)) - octonetAddress = buffer; - } - - ADDON_STATUS ADDON_Create(void* callbacks, const char* globalApiVersion, void* props) - { - if (callbacks == nullptr || props == nullptr) - return ADDON_STATUS_UNKNOWN; - - 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; - } - - 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; - } - - libKodi->Log(LOG_DEBUG, "%s: Creating octonet pvr addon", __func__); - ADDON_ReadSettings(); - - data = new OctonetData; - - addonStatus = ADDON_STATUS_OK; - return addonStatus; - } - - void ADDON_Destroy() - { - delete pvr; - delete libKodi; - addonStatus = ADDON_STATUS_UNKNOWN; - } - - 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; - } -} - - -/* KODI PVR Addon functions - * see xbmc_pvr_dll.h */ -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; - - return PVR_ERROR_NO_ERROR; - } - - const char* GetBackendName(void) { return "Digital Devices Octopus NET Client"; } - - const char* GetBackendVersion(void) { return STR(OCTONET_VERSION); } - - 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; - } - - 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 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); - } - - 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(); } - - 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); - } - - /* Channels */ - PVR_ERROR OpenDialogChannelScan(void) { return PVR_ERROR_NOT_IMPLEMENTED; } - - int GetChannelsAmount(void) { return data->getChannelCount(); } - - 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; } - - /* 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 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 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 deleted file mode 100644 index 067e830..0000000 --- a/src/client.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2015 Julian Scheel - * Copyright (C) 2015 jusst technologies GmbH - * Copyright (C) 2015 Digital Devices GmbH - * - * SPDX-License-Identifier: GPL-2.0-or-later - * See LICENSE.md for more information. - * - */ - -#pragma once - -#include "kodi/libXBMC_addon.h" -#include "kodi/libXBMC_pvr.h" - -#ifndef __func__ -#define __func__ __FUNCTION__ -#endif - -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 06abc70..3a0ec5e 100644 --- a/src/rtsp_client.cpp +++ b/src/rtsp_client.cpp @@ -9,14 +9,11 @@ #include "rtsp_client.hpp" #include "Socket.h" -#include "client.h" #include #include #include #include -#include -#include #include #if defined(_WIN32) || defined(_WIN64) @@ -52,7 +49,6 @@ int asprintf(char** sptr, char* fmt, ...) #define RTCP_BUFFER_SIZE 1024 using namespace std; -using namespace ADDON; using namespace OCTO; enum rtsp_state @@ -359,14 +355,15 @@ bool rtsp_open(const string& name, const string& url_str) rtsp->level = 0; rtsp->quality = 0; - libKodi->Log(LOG_DEBUG, "try to open '%s'", url_str.c_str()); + kodi::Log(ADDON_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()); + kodi::Log(ADDON_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); + kodi::Log(ADDON_LOG_ERROR, "Failed to connect to RTSP server %s:%d", dst.host.c_str(), + dst.port); goto error; } @@ -408,7 +405,7 @@ bool rtsp_open(const string& name, const string& url_str) if (rtsp_handle() != RTSP_RESULT_OK) { - libKodi->Log(LOG_ERROR, "Failed to setup RTSP session"); + kodi::Log(ADDON_LOG_ERROR, "Failed to setup RTSP session"); goto error; } @@ -425,7 +422,7 @@ bool rtsp_open(const string& name, const string& url_str) if (rtsp_handle() != RTSP_RESULT_OK) { - libKodi->Log(LOG_ERROR, "Failed to play RTSP session"); + kodi::Log(ADDON_LOG_ERROR, "Failed to play RTSP session"); goto error; } @@ -522,7 +519,7 @@ static void rtsp_teardown() if (rtsp_handle() != RTSP_RESULT_OK) { - libKodi->Log(LOG_ERROR, "Failed to teardown RTSP session"); + kodi::Log(ADDON_LOG_ERROR, "Failed to teardown RTSP session"); return; } } @@ -541,12 +538,12 @@ void rtsp_close() } } -void rtsp_fill_signal_status(PVR_SIGNAL_STATUS* signal_status) +void rtsp_fill_signal_status(kodi::addon::PVRSignalStatus& 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; + signal_status.SetAdapterName(rtsp->name); + signal_status.SetSNR(0x1111 * rtsp->quality); + signal_status.SetSignal(0x101 * rtsp->level); } } diff --git a/src/rtsp_client.hpp b/src/rtsp_client.hpp index d6790f4..728e1da 100644 --- a/src/rtsp_client.hpp +++ b/src/rtsp_client.hpp @@ -8,11 +8,10 @@ #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); -void rtsp_fill_signal_status(PVR_SIGNAL_STATUS* signal_status); - +void rtsp_fill_signal_status(kodi::addon::PVRSignalStatus& signal_status);