Skip to content

Commit

Permalink
[Backport][Inputstream] Add IChapter interface xbmc#16581
Browse files Browse the repository at this point in the history
  • Loading branch information
peak3d committed Dec 13, 2019
1 parent 37f51f6 commit da9b5f1
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 23 deletions.
Expand Up @@ -26,6 +26,8 @@
namespace kodi { namespace addon { class CInstanceInputStream; }}

extern "C" {
//Increment this level always if you add features which can lead to compile failures in the addon
#define INPUTSTREAM_VERSION_LEVEL 2

/*!
* @brief InputStream add-on capabilities. All capabilities are set to "false" as default.
Expand All @@ -50,7 +52,10 @@ extern "C" {
SUPPORTS_PAUSE = (1 << 4),

/// supports interface ITime
SUPPORTS_ITIME = (1 << 5)
SUPPORTS_ITIME = (1 << 5),

/// supports interface IChapter
SUPPORTS_ICHAPTER = (1 << 6),
};

/// set of supported capabilities
Expand Down Expand Up @@ -249,6 +254,13 @@ extern "C" {
int64_t (__cdecl* length_stream)(const AddonInstance_InputStream* instance);
void (__cdecl* pause_stream)(const AddonInstance_InputStream* instance, double time);
bool (__cdecl* is_real_time_stream)(const AddonInstance_InputStream* instance);

// IChapter
int(__cdecl* get_chapter)(const AddonInstance_InputStream* instance);
int(__cdecl* get_chapter_count)(const AddonInstance_InputStream* instance);
const char*(__cdecl* get_chapter_name)(const AddonInstance_InputStream* instance, int ch);
int64_t(__cdecl* get_chapter_pos)(const AddonInstance_InputStream* instance, int ch);
bool(__cdecl* seek_chapter)(const AddonInstance_InputStream* instance, int ch);
} KodiToAddonFuncTable_InputStream;

typedef struct AddonInstance_InputStream /* internal */
Expand All @@ -268,13 +280,13 @@ namespace addon
class CInstanceInputStream : public IAddonInstance
{
public:
explicit CInstanceInputStream(KODI_HANDLE instance)
explicit CInstanceInputStream(KODI_HANDLE instance, const std::string& kodiVersion = "0.0.0")
: IAddonInstance(ADDON_INSTANCE_INPUTSTREAM)
{
if (CAddonBase::m_interface->globalSingleInstance != nullptr)
throw std::logic_error("kodi::addon::CInstanceInputStream: Creation of multiple together with single instance way is not allowed!");

SetAddonStruct(instance);
throw std::logic_error("kodi::addon::CInstanceInputStream: Creation of multiple together "
"with single instance way is not allowed!");
SetAddonStruct(instance, kodiVersion);
}

~CInstanceInputStream() override = default;
Expand Down Expand Up @@ -410,7 +422,6 @@ namespace addon
*/
virtual bool PosTime(int ms) { return false; }


/*!
* Check if the backend support pausing the currently playing stream
* This will enable/disable the pause button in Kodi based on the return value
Expand Down Expand Up @@ -461,6 +472,35 @@ namespace addon
*/
virtual void PauseStream(double time) { }

/*!
* Return currently selected chapter
* @remarks
*/
virtual int GetChapter() { return -1; };

/*!
* Return number of available chapters
* @remarks
*/
virtual int GetChapterCount() { return 0; };

/*!
* Return name of chapter # ch
* @remarks
*/
virtual const std::string GetChapterName(int ch) { return std::string(); };

/*!
* Return position if chapter # ch in milliseconds
* @remarks
*/
virtual int64_t GetChapterPos(int ch) { return 0; };

/*!
* Seek to the beginning of chapter # ch
* @remarks
*/
virtual bool SeekChapter(int ch) { return false; };

/*!
* Check for real-time streaming
Expand Down Expand Up @@ -498,10 +538,13 @@ namespace addon
}

private:
void SetAddonStruct(KODI_HANDLE instance)
void SetAddonStruct(KODI_HANDLE instance, const std::string& kodiVersion)
{
if (instance == nullptr)
throw std::logic_error("kodi::addon::CInstanceInputStream: Creation with empty addon structure not allowed, table must be given from Kodi!");
throw std::logic_error("kodi::addon::CInstanceInputStream: Creation with empty addon "
"structure not allowed, table must be given from Kodi!");
int api[3] = { 0, 0, 0 };
sscanf(kodiVersion.c_str(), "%d.%d.%d", &api[0], &api[1], &api[2]);

m_instanceData = static_cast<AddonInstance_InputStream*>(instance);
m_instanceData->toAddon.addonInstance = this;
Expand Down Expand Up @@ -537,6 +580,16 @@ namespace addon
m_instanceData->toAddon.length_stream = ADDON_LengthStream;
m_instanceData->toAddon.pause_stream = ADDON_PauseStream;
m_instanceData->toAddon.is_real_time_stream = ADDON_IsRealTimeStream;

int minChapterVersion[3] = { 2, 0, 10 };
if (compareVersion(api, minChapterVersion) >= 0)
{
m_instanceData->toAddon.get_chapter = ADDON_GetChapter;
m_instanceData->toAddon.get_chapter_count = ADDON_GetChapterCount;
m_instanceData->toAddon.get_chapter_name = ADDON_GetChapterName;
m_instanceData->toAddon.get_chapter_pos = ADDON_GetChapterPos;
m_instanceData->toAddon.seek_chapter = ADDON_SeekChapter;
}
}

inline static bool ADDON_Open(const AddonInstance_InputStream* instance, INPUTSTREAM* props)
Expand Down Expand Up @@ -611,6 +664,14 @@ namespace addon
instance->toAddon.addonInstance->SetVideoResolution(width, height);
}

private:
static int compareVersion(const int v1[3], const int v2[3])
{
for (unsigned i(0); i < 3; ++i)
if (v1[i] != v2[i])
return v1[i] - v2[i];
return 0;
}

// IDisplayTime
inline static int ADDON_GetTotalTime(const AddonInstance_InputStream* instance)
Expand Down Expand Up @@ -646,7 +707,6 @@ namespace addon
return instance->toAddon.addonInstance->CanSeekStream();
}


inline static int ADDON_ReadStream(const AddonInstance_InputStream* instance, uint8_t* buffer, unsigned int bufferSize)
{
return instance->toAddon.addonInstance->ReadStream(buffer, bufferSize);
Expand Down Expand Up @@ -677,8 +737,32 @@ namespace addon
return instance->toAddon.addonInstance->IsRealTimeStream();
}

inline static int ADDON_GetChapter(const AddonInstance_InputStream* instance)
{
return instance->toAddon.addonInstance->GetChapter();
}

inline static int ADDON_GetChapterCount(const AddonInstance_InputStream* instance)
{
return instance->toAddon.addonInstance->GetChapterCount();
}

inline static const char* ADDON_GetChapterName(const AddonInstance_InputStream* instance, int ch)
{
return instance->toAddon.addonInstance->GetChapterName(ch).c_str();
}

inline static int64_t ADDON_GetChapterPos(const AddonInstance_InputStream* instance, int ch)
{
return instance->toAddon.addonInstance->GetChapterPos(ch);
}

inline static bool ADDON_SeekChapter(const AddonInstance_InputStream* instance, int ch)
{
return instance->toAddon.addonInstance->SeekChapter(ch);
}

AddonInstance_InputStream* m_instanceData;
};

} /* namespace addon */
} /* namespace kodi */
2 changes: 1 addition & 1 deletion xbmc/addons/kodi-addon-dev-kit/include/kodi/versions.h
Expand Up @@ -87,7 +87,7 @@
#define ADDON_INSTANCE_VERSION_IMAGEDECODER_XML_ID "kodi.binary.instance.imagedecoder"
#define ADDON_INSTANCE_VERSION_IMAGEDECODER_DEPENDS "addon-instance/ImageDecoder.h"

#define ADDON_INSTANCE_VERSION_INPUTSTREAM "2.0.8"
#define ADDON_INSTANCE_VERSION_INPUTSTREAM "2.0.10"
#define ADDON_INSTANCE_VERSION_INPUTSTREAM_MIN "2.0.7"
#define ADDON_INSTANCE_VERSION_INPUTSTREAM_XML_ID "kodi.binary.instance.inputstream"
#define ADDON_INSTANCE_VERSION_INPUTSTREAM_DEPENDS "addon-instance/Inputstream.h"
Expand Down
8 changes: 4 additions & 4 deletions xbmc/cores/VideoPlayer/DVDFileInfo.cpp
Expand Up @@ -200,10 +200,10 @@ bool CDVDFileInfo::ExtractThumb(const CFileItem& fileItem,
if (pVideoCodec)
{
int nTotalLen = pDemuxer->GetStreamLength();
int nSeekTo = (pos == -1) ? nTotalLen / 3 : pos;
int64_t nSeekTo = (pos == -1) ? nTotalLen / 3 : pos;

CLog::Log(LOGDEBUG,"%s - seeking to pos %dms (total: %dms) in %s", __FUNCTION__, nSeekTo, nTotalLen, redactPath.c_str());
if (pDemuxer->SeekTime(nSeekTo, true))
CLog::Log(LOGDEBUG, "%s - seeking to pos %lldms (total: %dms) in %s", __FUNCTION__, nSeekTo, nTotalLen, redactPath.c_str());
if (pDemuxer->SeekTime(static_cast<double>(nSeekTo), true))
{
CDVDVideoCodec::VCReturn iDecoderState = CDVDVideoCodec::VC_NONE;
VideoPicture picture = {};
Expand Down Expand Up @@ -377,7 +377,7 @@ bool CDVDFileInfo::DemuxerToStreamDetails(std::shared_ptr<CDVDInputStream> pInpu
CDemuxStreamVideo* vstream = static_cast<CDemuxStreamVideo*>(stream);
p->m_iWidth = vstream->iWidth;
p->m_iHeight = vstream->iHeight;
p->m_fAspect = vstream->fAspect;
p->m_fAspect = static_cast<float>(vstream->fAspect);
if (p->m_fAspect == 0.0f)
p->m_fAspect = (float)p->m_iWidth / p->m_iHeight;
p->m_strCodec = pDemux->GetStreamCodecName(stream->demuxerId, stream->uniqueId);
Expand Down
1 change: 1 addition & 0 deletions xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStream.h
Expand Up @@ -184,6 +184,7 @@ class CDVDInputStream
virtual IPosTime* GetIPosTime() { return nullptr; }
virtual IDisplayTime* GetIDisplayTime() { return nullptr; }
virtual ITimes* GetITimes() { return nullptr; }
virtual IChapter* GetIChapter() { return nullptr; }

const CVariant &GetProperty(const std::string key){ return m_item.GetProperty(key); }

Expand Down
54 changes: 54 additions & 0 deletions xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.cpp
Expand Up @@ -95,6 +95,7 @@ bool CInputStreamAddon::Supports(BinaryAddonBasePtr& addonBase, const CFileItem
for (auto& value : extensionsList)
{
StringUtils::Trim(value);

if (value == filetype)
return true;
}
Expand Down Expand Up @@ -477,6 +478,59 @@ bool CInputStreamAddon::IsRealtime()
return false;
}


// IChapter
CDVDInputStream::IChapter* CInputStreamAddon::GetIChapter()
{
if ((m_caps.m_mask & INPUTSTREAM_CAPABILITIES::SUPPORTS_ICHAPTER) == 0)
return nullptr;

return this;
}

int CInputStreamAddon::GetChapter()
{
if (m_struct.toAddon.get_chapter)
return m_struct.toAddon.get_chapter(&m_struct);

return -1;
}

int CInputStreamAddon::GetChapterCount()
{
if (m_struct.toAddon.get_chapter_count)
return m_struct.toAddon.get_chapter_count(&m_struct);

return 0;
}

void CInputStreamAddon::GetChapterName(std::string& name, int ch)
{
name.clear();
if (m_struct.toAddon.get_chapter_name)
{
const char* res = m_struct.toAddon.get_chapter_name(&m_struct, ch);
if (res)
name = res;
}
}

int64_t CInputStreamAddon::GetChapterPos(int ch)
{
if (m_struct.toAddon.get_chapter_pos)
return m_struct.toAddon.get_chapter_pos(&m_struct, ch);

return 0;
}

bool CInputStreamAddon::SeekChapter(int ch)
{
if (m_struct.toAddon.seek_chapter)
return m_struct.toAddon.seek_chapter(&m_struct, ch);

return false;
}

int CInputStreamAddon::ConvertVideoCodecProfile(STREAMCODEC_PROFILE profile)
{
switch (profile)
Expand Down
21 changes: 15 additions & 6 deletions xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.h
Expand Up @@ -32,12 +32,13 @@ class CInputStreamProvider

//! \brief Input stream class
class CInputStreamAddon
: public ADDON::IAddonInstanceHandler,
public CDVDInputStream,
public CDVDInputStream::IDisplayTime,
public CDVDInputStream::ITimes,
public CDVDInputStream::IPosTime,
public CDVDInputStream::IDemux
: public ADDON::IAddonInstanceHandler
, public CDVDInputStream
, public CDVDInputStream::IDisplayTime
, public CDVDInputStream::ITimes
, public CDVDInputStream::IPosTime
, public CDVDInputStream::IDemux
, public CDVDInputStream::IChapter
{
public:
CInputStreamAddon(ADDON::BinaryAddonBasePtr& addonBase, IVideoPlayer* player, const CFileItem& fileitem);
Expand Down Expand Up @@ -86,6 +87,14 @@ class CInputStreamAddon
void SetVideoResolution(int width, int height) override;
bool IsRealtime() override;

// IChapter
CDVDInputStream::IChapter* GetIChapter() override;
int GetChapter() override;
int GetChapterCount() override;
void GetChapterName(std::string& name, int ch = -1) override;
int64_t GetChapterPos(int ch = -1) override;
bool SeekChapter(int ch) override;

protected:
static int ConvertVideoCodecProfile(STREAMCODEC_PROFILE profile);

Expand Down
34 changes: 32 additions & 2 deletions xbmc/cores/VideoPlayer/VideoPlayer.cpp
Expand Up @@ -2730,7 +2730,17 @@ void CVideoPlayer::HandleMessages()
offset = DVD_TIME_TO_MSEC(start) - static_cast<int>(beforeSeek);
m_callback.OnPlayBackSeekChapter(msg.GetChapter());
}

else if (m_pInputStream)
{
CDVDInputStream::IChapter* pChapter = m_pInputStream->GetIChapter();
if (pChapter && pChapter->SeekChapter(msg.GetChapter()))
{
FlushBuffers(start, true, true);
int64_t beforeSeek = GetTime();
offset = DVD_TIME_TO_MSEC(start) - static_cast<int>(beforeSeek);
m_callback.OnPlayBackSeekChapter(msg.GetChapter());
}
}
CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetPlayerInfoProvider().SetDisplayAfterSeek(2500, offset);
}
else if (pMsg->IsType(CDVDMsg::DEMUXER_RESET))
Expand Down Expand Up @@ -4702,7 +4712,7 @@ void CVideoPlayer::UpdatePlayState(double timeout)
state.chapters.clear();
if (m_pDemuxer->GetChapterCount() > 0)
{
for (int i = 0; i < m_pDemuxer->GetChapterCount(); ++i)
for (int i = 0, ie = m_pDemuxer->GetChapterCount(); i < ie; ++i)
{
std::string name;
m_pDemuxer->GetChapterName(name, i + 1);
Expand All @@ -4722,6 +4732,26 @@ void CVideoPlayer::UpdatePlayState(double timeout)

if (m_pInputStream)
{
CDVDInputStream::IChapter* pChapter = m_pInputStream->GetIChapter();
if (pChapter)
{
if (IsInMenuInternal())
state.chapter = 0;
else
state.chapter = pChapter->GetChapter();

state.chapters.clear();
if (pChapter->GetChapterCount() > 0)
{
for (int i = 0, ie = pChapter->GetChapterCount(); i < ie; ++i)
{
std::string name;
pChapter->GetChapterName(name, i + 1);
state.chapters.push_back(make_pair(name, pChapter->GetChapterPos(i + 1)));
}
}
}

CDVDInputStream::ITimes* pTimes = m_pInputStream->GetITimes();
CDVDInputStream::IDisplayTime* pDisplayTime = m_pInputStream->GetIDisplayTime();

Expand Down

0 comments on commit da9b5f1

Please sign in to comment.