Skip to content

Commit

Permalink
Merge pull request #16581 from peak3d/chapter
Browse files Browse the repository at this point in the history
[Inputstream] Add IChapter interface
  • Loading branch information
peak3d committed Sep 23, 2019
2 parents f126ec7 + c38104d commit a34bfcd
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 20 deletions.
Expand Up @@ -32,7 +32,7 @@ class CInstanceInputStream;
} // namespace kodi

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

extern "C"
{
Expand Down Expand Up @@ -61,6 +61,9 @@ extern "C"

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

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

/// set of supported capabilities
Expand Down Expand Up @@ -365,6 +368,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 @@ -384,14 +394,14 @@ namespace addon
class CInstanceInputStream : public IAddonInstance
{
public:
explicit CInstanceInputStream(KODI_HANDLE instance)
explicit CInstanceInputStream(KODI_HANDLE instance, const std::string& kodiVersion)
: 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);
SetAddonStruct(instance, kodiVersion);
}

~CInstanceInputStream() override = default;
Expand Down Expand Up @@ -527,6 +537,35 @@ class CInstanceInputStream : public IAddonInstance
*/
virtual bool PosTime(int ms) { return false; }

/*!
* 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 char* GetChapterName(int ch) { return nullptr; };

/*!
* 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 if the backend support pausing the currently playing stream
Expand Down Expand Up @@ -617,11 +656,21 @@ class CInstanceInputStream : public IAddonInstance
}

private:
void SetAddonStruct(KODI_HANDLE instance)
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;
}

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!");
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 All @@ -645,7 +694,6 @@ class CInstanceInputStream : public IAddonInstance
m_instanceData->toAddon.get_time = ADDON_GetTime;

m_instanceData->toAddon.get_times = ADDON_GetTimes;

m_instanceData->toAddon.pos_time = ADDON_PosTime;

m_instanceData->toAddon.can_pause_stream = ADDON_CanPauseStream;
Expand All @@ -657,6 +705,16 @@ class CInstanceInputStream : public IAddonInstance
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 @@ -765,6 +823,31 @@ class CInstanceInputStream : public IAddonInstance
return instance->toAddon.addonInstance->PosTime(ms);
}

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);
}

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);
}

// Seekable (mandatory)
inline static bool ADDON_CanPauseStream(const AddonInstance_InputStream* instance)
{
Expand Down
2 changes: 1 addition & 1 deletion xbmc/addons/kodi-addon-dev-kit/include/kodi/versions.h
Expand Up @@ -88,7 +88,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.9"
#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 @@ -185,6 +185,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
57 changes: 55 additions & 2 deletions xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.cpp
Expand Up @@ -386,8 +386,8 @@ CDemuxStream* CInputStreamAddon::GetStream(int streamId) const
if (stream.m_contentLightMetadata)
{
videoStream->contentLightMetaData = std::shared_ptr<AVContentLightMetadata>(new AVContentLightMetadata);
videoStream->contentLightMetaData->MaxCLL = stream.m_contentLightMetadata->max_cll;
videoStream->contentLightMetaData->MaxFALL = stream.m_contentLightMetadata->max_fall;
videoStream->contentLightMetaData->MaxCLL = static_cast<unsigned>(stream.m_contentLightMetadata->max_cll);
videoStream->contentLightMetaData->MaxFALL = static_cast<unsigned>(stream.m_contentLightMetadata->max_fall);
}
}
demuxStream = videoStream;
Expand Down Expand Up @@ -512,6 +512,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
35 changes: 33 additions & 2 deletions xbmc/cores/VideoPlayer/VideoPlayer.cpp
Expand Up @@ -2627,7 +2627,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 @@ -4531,7 +4541,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 @@ -4552,6 +4562,27 @@ 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)));
}
}
CServiceBroker::GetDataCacheCore().SetChapters(state.chapters);
}

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

Expand Down

0 comments on commit a34bfcd

Please sign in to comment.