From c38104dab43feceb939e8863eef9877d207d19fd Mon Sep 17 00:00:00 2001 From: peak3d Date: Sun, 8 Sep 2019 17:02:46 +0200 Subject: [PATCH] [Inlutstream] Add IChapter interface --- .../include/kodi/addon-instance/Inputstream.h | 93 ++++++++++++++++++- .../include/kodi/versions.h | 2 +- xbmc/cores/VideoPlayer/DVDFileInfo.cpp | 8 +- .../DVDInputStreams/DVDInputStream.h | 1 + .../DVDInputStreams/InputStreamAddon.cpp | 57 +++++++++++- .../DVDInputStreams/InputStreamAddon.h | 21 +++-- xbmc/cores/VideoPlayer/VideoPlayer.cpp | 35 ++++++- 7 files changed, 197 insertions(+), 20 deletions(-) diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/addon-instance/Inputstream.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/addon-instance/Inputstream.h index a6719a65adc05..6667c8457f3ea 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/addon-instance/Inputstream.h +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/addon-instance/Inputstream.h @@ -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" { @@ -61,6 +61,9 @@ extern "C" /// supports interface ITime SUPPORTS_ITIME = (1 << 5), + + /// supports interface IChapter + SUPPORTS_ICHAPTER = (1 << 6), }; /// set of supported capabilities @@ -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 */ @@ -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; @@ -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 @@ -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(instance); m_instanceData->toAddon.addonInstance = this; @@ -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; @@ -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) @@ -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) { diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/versions.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/versions.h index d95a74729789f..0983af735a3ae 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/versions.h +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/versions.h @@ -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" diff --git a/xbmc/cores/VideoPlayer/DVDFileInfo.cpp b/xbmc/cores/VideoPlayer/DVDFileInfo.cpp index 6fcba20e5fb68..16ddea9f4efa2 100644 --- a/xbmc/cores/VideoPlayer/DVDFileInfo.cpp +++ b/xbmc/cores/VideoPlayer/DVDFileInfo.cpp @@ -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(nSeekTo), true)) { CDVDVideoCodec::VCReturn iDecoderState = CDVDVideoCodec::VC_NONE; VideoPicture picture = {}; @@ -377,7 +377,7 @@ bool CDVDFileInfo::DemuxerToStreamDetails(std::shared_ptr pInpu CDemuxStreamVideo* vstream = static_cast(stream); p->m_iWidth = vstream->iWidth; p->m_iHeight = vstream->iHeight; - p->m_fAspect = vstream->fAspect; + p->m_fAspect = static_cast(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); diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStream.h b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStream.h index d00b593b665ae..f220d138c9fd8 100644 --- a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStream.h +++ b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStream.h @@ -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); } diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.cpp b/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.cpp index fa09e3bea85f4..67c7c20544ab3 100644 --- a/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.cpp +++ b/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.cpp @@ -386,8 +386,8 @@ CDemuxStream* CInputStreamAddon::GetStream(int streamId) const if (stream.m_contentLightMetadata) { videoStream->contentLightMetaData = std::shared_ptr(new AVContentLightMetadata); - videoStream->contentLightMetaData->MaxCLL = stream.m_contentLightMetadata->max_cll; - videoStream->contentLightMetaData->MaxFALL = stream.m_contentLightMetadata->max_fall; + videoStream->contentLightMetaData->MaxCLL = static_cast(stream.m_contentLightMetadata->max_cll); + videoStream->contentLightMetaData->MaxFALL = static_cast(stream.m_contentLightMetadata->max_fall); } } demuxStream = videoStream; @@ -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) diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.h b/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.h index e11108db48bce..eea0f2e3abdf6 100644 --- a/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.h +++ b/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.h @@ -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); @@ -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); diff --git a/xbmc/cores/VideoPlayer/VideoPlayer.cpp b/xbmc/cores/VideoPlayer/VideoPlayer.cpp index 65d813a2c41ba..cda6afa47cff1 100644 --- a/xbmc/cores/VideoPlayer/VideoPlayer.cpp +++ b/xbmc/cores/VideoPlayer/VideoPlayer.cpp @@ -2627,7 +2627,17 @@ void CVideoPlayer::HandleMessages() offset = DVD_TIME_TO_MSEC(start) - static_cast(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(beforeSeek); + m_callback.OnPlayBackSeekChapter(msg.GetChapter()); + } + } CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetPlayerInfoProvider().SetDisplayAfterSeek(2500, offset); } else if (pMsg->IsType(CDVDMsg::DEMUXER_RESET)) @@ -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); @@ -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();