From da9b5f1f67684cfa4a0dbba9fa66ba753b860f16 Mon Sep 17 00:00:00 2001 From: peak3d Date: Fri, 13 Dec 2019 09:19:19 +0100 Subject: [PATCH] [Backport][Inputstream] Add IChapter interface #16581 --- .../include/kodi/addon-instance/Inputstream.h | 104 ++++++++++++++++-- .../include/kodi/versions.h | 2 +- xbmc/cores/VideoPlayer/DVDFileInfo.cpp | 8 +- .../DVDInputStreams/DVDInputStream.h | 1 + .../DVDInputStreams/InputStreamAddon.cpp | 54 +++++++++ .../DVDInputStreams/InputStreamAddon.h | 21 +++- xbmc/cores/VideoPlayer/VideoPlayer.cpp | 34 +++++- 7 files changed, 201 insertions(+), 23 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 252edbf74e51f..a5377dec4400c 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 @@ -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. @@ -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 @@ -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 */ @@ -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; @@ -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 @@ -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 @@ -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(instance); m_instanceData->toAddon.addonInstance = this; @@ -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) @@ -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) @@ -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); @@ -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 */ 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 06d75e02c5ff1..12782ec86bfde 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/versions.h +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/versions.h @@ -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" diff --git a/xbmc/cores/VideoPlayer/DVDFileInfo.cpp b/xbmc/cores/VideoPlayer/DVDFileInfo.cpp index d12f18e8fca2e..2b9e2d414397e 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 8dc39442c959b..40f5f3d3031c6 100644 --- a/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStream.h +++ b/xbmc/cores/VideoPlayer/DVDInputStreams/DVDInputStream.h @@ -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); } diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.cpp b/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.cpp index 8829572f757f9..cc42c3a6efeed 100644 --- a/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.cpp +++ b/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.cpp @@ -95,6 +95,7 @@ bool CInputStreamAddon::Supports(BinaryAddonBasePtr& addonBase, const CFileItem for (auto& value : extensionsList) { StringUtils::Trim(value); + if (value == filetype) return true; } @@ -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) diff --git a/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.h b/xbmc/cores/VideoPlayer/DVDInputStreams/InputStreamAddon.h index e215953ab23b4..559ad8d5f077a 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 459d2aacc0fab..68ec48710dd71 100644 --- a/xbmc/cores/VideoPlayer/VideoPlayer.cpp +++ b/xbmc/cores/VideoPlayer/VideoPlayer.cpp @@ -2730,7 +2730,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)) @@ -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); @@ -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();