Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Inputstream] Add IChapter interface #16581

Merged
merged 1 commit into from Sep 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -4532,7 +4542,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 @@ -4553,6 +4563,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