mirror of
https://github.com/DigitalDevices/pvr.octonet.git
synced 2023-10-10 13:36:57 +02:00
change to new C++ addon interface
This commit is contained in:
parent
fc5b149f2c
commit
e7449d9537
@ -4,37 +4,34 @@ project(pvr.octonet)
|
|||||||
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR})
|
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR})
|
||||||
|
|
||||||
find_package(Kodi REQUIRED)
|
find_package(Kodi REQUIRED)
|
||||||
find_package(p8-platform REQUIRED)
|
|
||||||
find_package(JsonCpp REQUIRED)
|
find_package(JsonCpp REQUIRED)
|
||||||
|
|
||||||
include_directories(
|
include_directories(${KODI_INCLUDE_DIR}/.. # Hack way with "/..", need bigger Kodi cmake rework to match right include ways
|
||||||
${p8-platform_INCLUDE_DIRS}
|
${JSONCPP_INCLUDE_DIRS})
|
||||||
${KODI_INCLUDE_DIR}/.. # Hack way with "/..", need bigger Kodi cmake rework to match right include ways
|
|
||||||
${JSONCPP_INCLUDE_DIRS})
|
|
||||||
|
|
||||||
set(DEPLIBS
|
set(DEPLIBS ${JSONCPP_LIBRARIES})
|
||||||
${p8-platform_LIBRARIES}
|
|
||||||
${JSONCPP_LIBRARIES})
|
|
||||||
|
|
||||||
set(OCTONET_SOURCES
|
set(OCTONET_SOURCES src/addon.cpp
|
||||||
src/OctonetData.cpp
|
src/OctonetData.cpp
|
||||||
src/client.cpp
|
src/Socket.cpp
|
||||||
src/Socket.cpp
|
src/rtsp_client.cpp)
|
||||||
src/rtsp_client.cpp)
|
|
||||||
|
|
||||||
set(OCTONET_HEADERS
|
set(OCTONET_HEADERS src/addon.h
|
||||||
src/client.h
|
src/OctonetData.h
|
||||||
src/OctonetData.h
|
src/Socket.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)
|
build_addon(pvr.octonet OCTONET DEPLIBS)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
if(NOT CMAKE_SYSTEM_NAME STREQUAL WindowsStore)
|
if(NOT CMAKE_SYSTEM_NAME STREQUAL WindowsStore)
|
||||||
target_link_libraries(pvr.octonet wsock32 ws2_32)
|
target_link_libraries(pvr.octonet wsock32 ws2_32)
|
||||||
else()
|
else()
|
||||||
target_link_libraries(pvr.octonet ws2_32)
|
target_link_libraries(pvr.octonet ws2_32)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
include(CPack)
|
include(CPack)
|
||||||
|
@ -1 +0,0 @@
|
|||||||
p8-platform https://github.com/xbmc/platform.git cee64e9dc0b69e8d286dc170a78effaabfa09c44
|
|
@ -1 +0,0 @@
|
|||||||
p8-platform https://github.com/afedchin/platform.git win10
|
|
@ -10,7 +10,11 @@
|
|||||||
|
|
||||||
#include "OctonetData.h"
|
#include "OctonetData.h"
|
||||||
|
|
||||||
|
#include "rtsp_client.hpp"
|
||||||
|
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
#include <kodi/Filesystem.h>
|
||||||
|
#include <kodi/General.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@ -18,9 +22,10 @@
|
|||||||
#define timegm _mkgmtime
|
#define timegm _mkgmtime
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using namespace ADDON;
|
OctonetData::OctonetData(const std::string& octonetAddress,
|
||||||
|
KODI_HANDLE instance,
|
||||||
OctonetData::OctonetData()
|
const std::string& kodiVersion)
|
||||||
|
: kodi::addon::CInstancePVRClient(instance, kodiVersion)
|
||||||
{
|
{
|
||||||
m_serverAddress = octonetAddress;
|
m_serverAddress = octonetAddress;
|
||||||
m_channels.clear();
|
m_channels.clear();
|
||||||
@ -28,13 +33,77 @@ OctonetData::OctonetData()
|
|||||||
m_lastEpgLoad = 0;
|
m_lastEpgLoad = 0;
|
||||||
|
|
||||||
if (!LoadChannelList())
|
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)
|
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)
|
int64_t OctonetData::ParseID(std::string id)
|
||||||
@ -48,16 +117,15 @@ int64_t OctonetData::ParseID(std::string id)
|
|||||||
bool OctonetData::LoadChannelList()
|
bool OctonetData::LoadChannelList()
|
||||||
{
|
{
|
||||||
std::string jsonContent;
|
std::string jsonContent;
|
||||||
void* f =
|
kodi::vfs::CFile f;
|
||||||
libKodi->OpenFile(("http://" + m_serverAddress + "/channellist.lua?select=json").c_str(), 0);
|
if (!f.OpenFile("http://" + m_serverAddress + "/channellist.lua?select=json", 0))
|
||||||
if (!f)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
while (int read = libKodi->ReadFile(f, buf, 1024))
|
while (int read = f.Read(buf, 1024))
|
||||||
jsonContent.append(buf, read);
|
jsonContent.append(buf, read);
|
||||||
|
|
||||||
libKodi->CloseFile(f);
|
f.Close();
|
||||||
|
|
||||||
Json::Value root;
|
Json::Value root;
|
||||||
Json::Reader reader;
|
Json::Reader reader;
|
||||||
@ -96,14 +164,13 @@ bool OctonetData::LoadChannelList()
|
|||||||
|
|
||||||
OctonetChannel* OctonetData::FindChannel(int64_t nativeId)
|
OctonetChannel* OctonetData::FindChannel(int64_t nativeId)
|
||||||
{
|
{
|
||||||
std::vector<OctonetChannel>::iterator it;
|
for (auto& channel : m_channels)
|
||||||
for (it = m_channels.begin(); it < m_channels.end(); ++it)
|
|
||||||
{
|
{
|
||||||
if (it->nativeId == nativeId)
|
if (channel.nativeId == nativeId)
|
||||||
return &*it;
|
return &channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
time_t OctonetData::ParseDateTime(std::string date)
|
time_t OctonetData::ParseDateTime(std::string date)
|
||||||
@ -134,19 +201,19 @@ time_t OctonetData::ParseDateTime(std::string date)
|
|||||||
bool OctonetData::LoadEPG(void)
|
bool OctonetData::LoadEPG(void)
|
||||||
{
|
{
|
||||||
/* Reload at most every 30 seconds */
|
/* Reload at most every 30 seconds */
|
||||||
if (m_lastEpgLoad + 30 > time(NULL))
|
if (m_lastEpgLoad + 30 > time(nullptr))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
std::string jsonContent;
|
std::string jsonContent;
|
||||||
void* f = libKodi->OpenFile(("http://" + m_serverAddress + "/epg.lua?;#|encoding=gzip").c_str(), 0);
|
kodi::vfs::CFile f;
|
||||||
if (!f)
|
if (!f.OpenFile("http://" + m_serverAddress + "/epg.lua?;#|encoding=gzip", 0))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
while (int read = libKodi->ReadFile(f, buf, 1024))
|
while (int read = f.Read(buf, 1024))
|
||||||
jsonContent.append(buf, read);
|
jsonContent.append(buf, read);
|
||||||
|
|
||||||
libKodi->CloseFile(f);
|
f.Close();
|
||||||
|
|
||||||
Json::Value root;
|
Json::Value root;
|
||||||
Json::Reader reader;
|
Json::Reader reader;
|
||||||
@ -155,7 +222,7 @@ bool OctonetData::LoadEPG(void)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
const Json::Value eventList = root["EventList"];
|
const Json::Value eventList = root["EventList"];
|
||||||
OctonetChannel* channel = NULL;
|
OctonetChannel* channel = nullptr;
|
||||||
for (unsigned int i = 0; i < eventList.size(); i++)
|
for (unsigned int i = 0; i < eventList.size(); i++)
|
||||||
{
|
{
|
||||||
const Json::Value event = eventList[i];
|
const Json::Value event = eventList[i];
|
||||||
@ -170,63 +237,66 @@ bool OctonetData::LoadEPG(void)
|
|||||||
channelId = channelId.substr(0, channelId.rfind(":"));
|
channelId = channelId.substr(0, channelId.rfind(":"));
|
||||||
|
|
||||||
entry.channelId = ParseID(channelId);
|
entry.channelId = ParseID(channelId);
|
||||||
entry.id = atoi(epgId.c_str());
|
entry.id = std::stoi(epgId);
|
||||||
|
|
||||||
if (channel == NULL || channel->nativeId != entry.channelId)
|
if (channel == nullptr || channel->nativeId != entry.channelId)
|
||||||
channel = FindChannel(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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
channel->epg.push_back(entry);
|
channel->epg.push_back(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_lastEpgLoad = time(NULL);
|
m_lastEpgLoad = time(nullptr);
|
||||||
return true;
|
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++)
|
for (unsigned int i = 0; i < m_channels.size(); i++)
|
||||||
{
|
{
|
||||||
OctonetChannel& channel = m_channels.at(i);
|
OctonetChannel& channel = m_channels.at(i);
|
||||||
if (channel.radio == bRadio)
|
if (channel.radio == radio)
|
||||||
{
|
{
|
||||||
PVR_CHANNEL chan;
|
kodi::addon::PVRChannel chan;
|
||||||
memset(&chan, 0, sizeof(PVR_CHANNEL));
|
|
||||||
|
|
||||||
chan.iUniqueId = channel.id;
|
chan.SetUniqueId(channel.id);
|
||||||
chan.bIsRadio = channel.radio;
|
chan.SetIsRadio(channel.radio);
|
||||||
chan.iChannelNumber = i;
|
chan.SetChannelNumber(i);
|
||||||
strncpy(chan.strChannelName, channel.name.c_str(), strlen(channel.name.c_str()));
|
chan.SetChannelName(channel.name);
|
||||||
strcpy(chan.strInputFormat, "video/x-mpegts");
|
chan.SetMimeType("video/x-mpegts");
|
||||||
chan.bIsHidden = false;
|
chan.SetIsHidden(false);
|
||||||
|
|
||||||
pvr->TransferChannelEntry(handle, &chan);
|
results.Add(chan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return PVR_ERROR_NO_ERROR;
|
return PVR_ERROR_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
PVR_ERROR OctonetData::getEPG(ADDON_HANDLE handle, int iChannelUid, time_t start, time_t end)
|
PVR_ERROR OctonetData::GetEPGForChannel(int channelUid,
|
||||||
|
time_t start,
|
||||||
|
time_t end,
|
||||||
|
kodi::addon::PVREPGTagsResultSet& results)
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < m_channels.size(); i++)
|
for (unsigned int i = 0; i < m_channels.size(); i++)
|
||||||
{
|
{
|
||||||
OctonetChannel& chan = m_channels.at(i);
|
OctonetChannel& chan = m_channels.at(i);
|
||||||
if (iChannelUid != chan.id)
|
if (channelUid != chan.id)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (chan.epg.empty())
|
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!?
|
// FIXME: Check if reload is needed!?
|
||||||
|
|
||||||
std::vector<OctonetEpgEntry>::iterator it;
|
|
||||||
time_t last_end = 0;
|
time_t last_end = 0;
|
||||||
for (it = chan.epg.begin(); it != chan.epg.end(); ++it)
|
for (const auto& epg : chan.epg)
|
||||||
{
|
{
|
||||||
if (it->end > last_end)
|
if (epg.end > last_end)
|
||||||
last_end = it->end;
|
last_end = epg.end;
|
||||||
|
|
||||||
if (it->end < start || it->start > end)
|
if (epg.end < start || epg.start > end)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
EPG_TAG entry;
|
kodi::addon::PVREPGTag 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.SetUniqueChannelId(chan.id);
|
||||||
entry.iUniqueBroadcastId = it->id;
|
entry.SetUniqueBroadcastId(epg.id);
|
||||||
entry.strTitle = it->title.c_str();
|
entry.SetTitle(epg.title);
|
||||||
entry.strPlotOutline = it->subtitle.c_str();
|
entry.SetPlotOutline(epg.subtitle);
|
||||||
entry.startTime = it->start;
|
entry.SetStartTime(epg.start);
|
||||||
entry.endTime = it->end;
|
entry.SetEndTime(epg.end);
|
||||||
|
|
||||||
pvr->TransferEpgEntry(handle, &entry);
|
results.Add(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last_end < end)
|
if (last_end < end)
|
||||||
LoadEPG();
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
EPG_TAG entry;
|
kodi::addon::PVREPGTag 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.SetUniqueChannelId(chan.id);
|
||||||
entry.iUniqueBroadcastId = it->id;
|
entry.SetUniqueBroadcastId(epg.id);
|
||||||
entry.strTitle = it->title.c_str();
|
entry.SetTitle(epg.title);
|
||||||
entry.strPlotOutline = it->subtitle.c_str();
|
entry.SetPlotOutline(epg.subtitle);
|
||||||
entry.startTime = it->start;
|
entry.SetStartTime(epg.start);
|
||||||
entry.endTime = it->end;
|
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
|
const std::string& OctonetData::GetUrl(int id) const
|
||||||
{
|
{
|
||||||
for (std::vector<OctonetChannel>::const_iterator iter = m_channels.begin(); iter != m_channels.end();
|
for (const auto& channel : m_channels)
|
||||||
++iter)
|
|
||||||
{
|
{
|
||||||
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
|
const std::string& OctonetData::GetName(int id) const
|
||||||
{
|
{
|
||||||
for (std::vector<OctonetChannel>::const_iterator iter = m_channels.begin(); iter != m_channels.end();
|
for (const auto& channel : m_channels)
|
||||||
++iter)
|
|
||||||
{
|
{
|
||||||
if (iter->id == id)
|
if (channel.id == id)
|
||||||
{
|
{
|
||||||
return iter->name;
|
return channel.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_channels[0].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 == radio)
|
||||||
if (group.radio == bRadio)
|
|
||||||
{
|
{
|
||||||
PVR_CHANNEL_GROUP g;
|
kodi::addon::PVRChannelGroup g;
|
||||||
memset(&g, 0, sizeof(PVR_CHANNEL_GROUP));
|
|
||||||
|
|
||||||
g.iPosition = 0;
|
g.SetPosition(0);
|
||||||
g.bIsRadio = group.radio;
|
g.SetIsRadio(group.radio);
|
||||||
strncpy(g.strGroupName, group.name.c_str(), strlen(group.name.c_str()));
|
g.SetGroupName(group.name);
|
||||||
|
|
||||||
pvr->TransferChannelGroup(handle, &g);
|
results.Add(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::GetChannelGroupMembers(const kodi::addon::PVRChannelGroup& group,
|
||||||
|
kodi::addon::PVRChannelGroupMembersResultSet& results)
|
||||||
{
|
{
|
||||||
OctonetGroup* g = FindGroup(group.strGroupName);
|
const OctonetGroup* g = FindGroup(group.GetGroupName());
|
||||||
if (g == NULL)
|
if (g == nullptr)
|
||||||
return PVR_ERROR_UNKNOWN;
|
return PVR_ERROR_UNKNOWN;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < g->members.size(); i++)
|
for (unsigned int i = 0; i < g->members.size(); i++)
|
||||||
{
|
{
|
||||||
OctonetChannel& channel = m_channels.at(g->members[i]);
|
OctonetChannel& channel = m_channels.at(g->members[i]);
|
||||||
PVR_CHANNEL_GROUP_MEMBER m;
|
kodi::addon::PVRChannelGroupMember m;
|
||||||
memset(&m, 0, sizeof(PVR_CHANNEL_GROUP_MEMBER));
|
|
||||||
|
|
||||||
strncpy(m.strGroupName, group.strGroupName, strlen(group.strGroupName));
|
m.SetGroupName(group.GetGroupName());
|
||||||
m.iChannelUniqueId = channel.id;
|
m.SetChannelUniqueId(channel.id);
|
||||||
m.iChannelNumber = channel.id;
|
m.SetChannelNumber(channel.id);
|
||||||
|
|
||||||
pvr->TransferChannelGroupMember(handle, &m);
|
results.Add(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PVR_ERROR_NO_ERROR;
|
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)
|
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)
|
if (group.name == name)
|
||||||
return &m_groups.at(i);
|
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();
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,9 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "client.h"
|
#include <atomic>
|
||||||
|
#include <kodi/addon-instance/PVR.h>
|
||||||
#include "p8-platform/threads/threads.h"
|
#include <thread>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
struct OctonetEpgEntry
|
struct OctonetEpgEntry
|
||||||
@ -44,25 +43,45 @@ struct OctonetGroup
|
|||||||
std::vector<int> members;
|
std::vector<int> members;
|
||||||
};
|
};
|
||||||
|
|
||||||
class OctonetData : public P8PLATFORM::CThread
|
class ATTRIBUTE_HIDDEN OctonetData : public kodi::addon::CInstancePVRClient
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OctonetData(void);
|
OctonetData(const std::string& octonetAddress,
|
||||||
virtual ~OctonetData(void);
|
KODI_HANDLE instance,
|
||||||
|
const std::string& kodiVersion);
|
||||||
|
~OctonetData() override;
|
||||||
|
|
||||||
virtual int getChannelCount(void);
|
PVR_ERROR GetCapabilities(kodi::addon::PVRCapabilities& capabilities) override;
|
||||||
virtual PVR_ERROR getChannels(ADDON_HANDLE handle, bool bRadio);
|
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);
|
PVR_ERROR OnSystemSleep() override;
|
||||||
virtual PVR_ERROR getGroups(ADDON_HANDLE handle, bool bRadio);
|
PVR_ERROR OnSystemWake() override;
|
||||||
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);
|
PVR_ERROR GetChannelsAmount(int& amount) override;
|
||||||
const std::string& GetUrl(int id) const;
|
PVR_ERROR GetChannels(bool radio, kodi::addon::PVRChannelsResultSet& results) override;
|
||||||
const std::string& GetName(int id) const;
|
|
||||||
|
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:
|
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 LoadChannelList(void);
|
||||||
bool LoadEPG(void);
|
bool LoadEPG(void);
|
||||||
@ -77,4 +96,7 @@ private:
|
|||||||
std::vector<OctonetGroup> m_groups;
|
std::vector<OctonetGroup> m_groups;
|
||||||
|
|
||||||
time_t m_lastEpgLoad;
|
time_t m_lastEpgLoad;
|
||||||
|
|
||||||
|
std::atomic<bool> m_running = {false};
|
||||||
|
std::thread m_thread;
|
||||||
};
|
};
|
||||||
|
@ -8,16 +8,11 @@
|
|||||||
|
|
||||||
#include "Socket.h"
|
#include "Socket.h"
|
||||||
|
|
||||||
#include "client.h"
|
|
||||||
|
|
||||||
#include "kodi/libXBMC_addon.h"
|
|
||||||
#include "p8-platform/os.h"
|
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <kodi/General.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace ADDON;
|
|
||||||
|
|
||||||
namespace OCTO
|
namespace OCTO
|
||||||
{
|
{
|
||||||
@ -146,7 +141,8 @@ bool Socket::accept(Socket& new_socket) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
socklen_t addr_length = sizeof(m_sockaddr);
|
socklen_t addr_length = sizeof(m_sockaddr);
|
||||||
new_socket.m_sd = ::accept(m_sd, const_cast<sockaddr*>((const sockaddr*)&m_sockaddr), &addr_length);
|
new_socket.m_sd =
|
||||||
|
::accept(m_sd, const_cast<sockaddr*>((const sockaddr*)&m_sockaddr), &addr_length);
|
||||||
|
|
||||||
#ifdef TARGET_WINDOWS
|
#ifdef TARGET_WINDOWS
|
||||||
if (new_socket.m_sd == INVALID_SOCKET)
|
if (new_socket.m_sd == INVALID_SOCKET)
|
||||||
@ -192,13 +188,13 @@ int Socket::send(const char* data, const unsigned int len)
|
|||||||
|
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
{
|
{
|
||||||
libKodi->Log(LOG_ERROR, "Socket::send - select failed");
|
kodi::Log(ADDON_LOG_ERROR, "Socket::send - select failed");
|
||||||
close();
|
close();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (FD_ISSET(m_sd, &set_w))
|
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();
|
close();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -208,7 +204,7 @@ int Socket::send(const char* data, const unsigned int len)
|
|||||||
if (status == -1)
|
if (status == -1)
|
||||||
{
|
{
|
||||||
errormessage(getLastError(), "Socket::send");
|
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();
|
close();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -292,8 +288,8 @@ bool Socket::ReadLine(string& line)
|
|||||||
|
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
{
|
{
|
||||||
libKodi->Log(LOG_DEBUG, "%s: select failed", __FUNCTION__);
|
kodi::Log(ADDON_LOG_DEBUG, "%s: select failed", __func__);
|
||||||
errormessage(getLastError(), __FUNCTION__);
|
errormessage(getLastError(), __func__);
|
||||||
close();
|
close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -302,15 +298,15 @@ bool Socket::ReadLine(string& line)
|
|||||||
{
|
{
|
||||||
if (retries != 0)
|
if (retries != 0)
|
||||||
{
|
{
|
||||||
libKodi->Log(LOG_DEBUG, "%s: timeout waiting for response, retrying... (%i)", __FUNCTION__,
|
kodi::Log(ADDON_LOG_DEBUG, "%s: timeout waiting for response, retrying... (%i)", __func__,
|
||||||
retries);
|
retries);
|
||||||
retries--;
|
retries--;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
libKodi->Log(LOG_DEBUG, "%s: timeout waiting for response. Aborting after 10 retries.",
|
kodi::Log(ADDON_LOG_DEBUG, "%s: timeout waiting for response. Aborting after 10 retries.",
|
||||||
__FUNCTION__);
|
__func__);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -318,8 +314,8 @@ bool Socket::ReadLine(string& line)
|
|||||||
result = recv(m_sd, buffer, sizeof(buffer) - 1, 0);
|
result = recv(m_sd, buffer, sizeof(buffer) - 1, 0);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
{
|
{
|
||||||
libKodi->Log(LOG_DEBUG, "%s: recv failed", __FUNCTION__);
|
kodi::Log(ADDON_LOG_DEBUG, "%s: recv failed", __func__);
|
||||||
errormessage(getLastError(), __FUNCTION__);
|
errormessage(getLastError(), __func__);
|
||||||
close();
|
close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -394,7 +390,7 @@ bool Socket::connect(const std::string& host, const unsigned short port)
|
|||||||
|
|
||||||
if (!setHostname(host))
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
m_port = port;
|
m_port = port;
|
||||||
@ -443,7 +439,7 @@ bool Socket::connect(const std::string& host, const unsigned short port)
|
|||||||
|
|
||||||
if (address == nullptr)
|
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");
|
errormessage(getLastError(), "Socket::connect");
|
||||||
close();
|
close();
|
||||||
return false;
|
return false;
|
||||||
@ -479,7 +475,8 @@ bool Socket::set_non_blocking(const bool b)
|
|||||||
|
|
||||||
if (ioctlsocket(m_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);
|
kodi::Log(ADDON_LOG_ERROR, "Socket::set_non_blocking - Can't set socket condition to: %i",
|
||||||
|
iMode);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -570,7 +567,7 @@ void Socket::errormessage(int errnum, const char* functionname) const
|
|||||||
default:
|
default:
|
||||||
errmsg = "WSA Error";
|
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
|
int Socket::getLastError() const
|
||||||
@ -628,7 +625,7 @@ bool Socket::set_non_blocking(const bool b)
|
|||||||
|
|
||||||
if (fcntl(m_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);
|
kodi::Log(ADDON_LOG_ERROR, "Socket::set_non_blocking - Can't set socket flags to: %i", opts);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -709,7 +706,7 @@ void Socket::errormessage(int errnum, const char* functionname) const
|
|||||||
break;
|
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
|
int Socket::getLastError() const
|
||||||
|
62
src/addon.cpp
Normal file
62
src/addon.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Julian Scheel <julian@jusst.de>
|
||||||
|
* 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)
|
36
src/addon.h
Normal file
36
src/addon.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Julian Scheel <julian@jusst.de>
|
||||||
|
* 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/AddonBase.h>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
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<std::string, OctonetData*> m_usedInstances;
|
||||||
|
};
|
300
src/client.cpp
300
src/client.cpp
@ -1,300 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015 Julian Scheel <julian@jusst.de>
|
|
||||||
* 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 <kodi/libXBMC_addon.h>
|
|
||||||
#include <kodi/xbmc_pvr_dll.h>
|
|
||||||
#include <p8-platform/util/util.h>
|
|
||||||
|
|
||||||
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(); }
|
|
||||||
}
|
|
24
src/client.h
24
src/client.h
@ -1,24 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015 Julian Scheel <julian@jusst.de>
|
|
||||||
* 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;
|
|
@ -9,14 +9,11 @@
|
|||||||
#include "rtsp_client.hpp"
|
#include "rtsp_client.hpp"
|
||||||
|
|
||||||
#include "Socket.h"
|
#include "Socket.h"
|
||||||
#include "client.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <kodi/libXBMC_addon.h>
|
|
||||||
#include <p8-platform/util/util.h>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
@ -52,7 +49,6 @@ int asprintf(char** sptr, char* fmt, ...)
|
|||||||
#define RTCP_BUFFER_SIZE 1024
|
#define RTCP_BUFFER_SIZE 1024
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace ADDON;
|
|
||||||
using namespace OCTO;
|
using namespace OCTO;
|
||||||
|
|
||||||
enum rtsp_state
|
enum rtsp_state
|
||||||
@ -359,14 +355,15 @@ bool rtsp_open(const string& name, const string& url_str)
|
|||||||
rtsp->level = 0;
|
rtsp->level = 0;
|
||||||
rtsp->quality = 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);
|
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))
|
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;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,7 +405,7 @@ bool rtsp_open(const string& name, const string& url_str)
|
|||||||
|
|
||||||
if (rtsp_handle() != RTSP_RESULT_OK)
|
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;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,7 +422,7 @@ bool rtsp_open(const string& name, const string& url_str)
|
|||||||
|
|
||||||
if (rtsp_handle() != RTSP_RESULT_OK)
|
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;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -522,7 +519,7 @@ static void rtsp_teardown()
|
|||||||
|
|
||||||
if (rtsp_handle() != RTSP_RESULT_OK)
|
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;
|
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)
|
if (rtsp)
|
||||||
{
|
{
|
||||||
strncpy(signal_status->strServiceName, rtsp->name.c_str(), PVR_ADDON_NAME_STRING_LENGTH - 1);
|
signal_status.SetAdapterName(rtsp->name);
|
||||||
signal_status->iSNR = 0x1111 * rtsp->quality;
|
signal_status.SetSNR(0x1111 * rtsp->quality);
|
||||||
signal_status->iSignal = 0x101 * rtsp->level;
|
signal_status.SetSignal(0x101 * rtsp->level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <kodi/xbmc_pvr_types.h>
|
#include <kodi/addon-instance/pvr/Channels.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
bool rtsp_open(const std::string& name, const std::string& url_str);
|
bool rtsp_open(const std::string& name, const std::string& url_str);
|
||||||
void rtsp_close();
|
void rtsp_close();
|
||||||
int rtsp_read(void* buf, unsigned buf_size);
|
int rtsp_read(void* buf, unsigned buf_size);
|
||||||
void rtsp_fill_signal_status(PVR_SIGNAL_STATUS* signal_status);
|
void rtsp_fill_signal_status(kodi::addon::PVRSignalStatus& signal_status);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user