aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Rector <rmrector@gmail.com>2023-09-19 18:08:11 -0600
committerGitHub <noreply@github.com>2023-09-19 18:08:11 -0600
commitc57bca141aeb7cf21da0182dc540816aba199394 (patch)
tree99eec943e78186a60ede5e50009966c9288e809c
parent86baa0ad30bfd5402b539a008360ecd0bcca3b39 (diff)
parent0bfa18e486e8db6a28435393fdf2ec84d2050e1c (diff)
Merge pull request #23639 from rmrector/chapter-thumb-gen
[guilib][vfs][imagecache] Load video chapter images into texture cache when viewed, like standard images
-rw-r--r--xbmc/TextureCache.cpp2
-rw-r--r--xbmc/TextureCacheJob.cpp6
-rw-r--r--xbmc/cores/VideoPlayer/DVDFileInfo.cpp281
-rw-r--r--xbmc/cores/VideoPlayer/DVDFileInfo.h6
-rw-r--r--xbmc/imagefiles/SpecialImageLoaderFactory.cpp2
-rw-r--r--xbmc/imagefiles/SpecialImageLoaderFactory.h2
-rw-r--r--xbmc/video/CMakeLists.txt6
-rw-r--r--xbmc/video/VideoChapterImageFileLoader.cpp54
-rw-r--r--xbmc/video/VideoChapterImageFileLoader.h31
-rw-r--r--xbmc/video/VideoThumbLoader.cpp50
-rw-r--r--xbmc/video/VideoThumbLoader.h36
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoBookmarks.cpp69
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoBookmarks.h11
13 files changed, 149 insertions, 407 deletions
diff --git a/xbmc/TextureCache.cpp b/xbmc/TextureCache.cpp
index e0a5e2d906..ab5fff51e7 100644
--- a/xbmc/TextureCache.cpp
+++ b/xbmc/TextureCache.cpp
@@ -296,7 +296,7 @@ void CTextureCache::OnCachingComplete(bool success, CTextureCacheJob *job)
{
if (success)
{
- if (job->m_oldHash == job->m_details.hash)
+ if (job->m_details.id != -1 && job->m_oldHash == job->m_details.hash)
SetCachedTextureValid(job->m_url, job->m_details.updateable);
else
AddCachedTexture(job->m_url, job->m_details);
diff --git a/xbmc/TextureCacheJob.cpp b/xbmc/TextureCacheJob.cpp
index 0139993f3a..66edb39202 100644
--- a/xbmc/TextureCacheJob.cpp
+++ b/xbmc/TextureCacheJob.cpp
@@ -212,6 +212,12 @@ std::string CTextureCacheJob::DecodeImageURL(const std::string &url, unsigned in
scalingAlgorithm = CPictureScalingAlgorithm::FromString(thumbURL.GetOption("scaling_algorithm"));
}
+ if (StringUtils::StartsWith(url, "chapter://"))
+ {
+ // workaround for chapter thumbnail paths, which don't yet conform to the image:// path.
+ additional_info = "videochapter";
+ }
+
// Handle special case about audiodecoder addon music files, e.g. SACD
if (StringUtils::EndsWith(URIUtils::GetExtension(image), KODI_ADDON_AUDIODECODER_TRACK_EXT))
{
diff --git a/xbmc/cores/VideoPlayer/DVDFileInfo.cpp b/xbmc/cores/VideoPlayer/DVDFileInfo.cpp
index 73e5729e72..eb62cf24ea 100644
--- a/xbmc/cores/VideoPlayer/DVDFileInfo.cpp
+++ b/xbmc/cores/VideoPlayer/DVDFileInfo.cpp
@@ -87,190 +87,8 @@ int DegreeToOrientation(int degrees)
}
}
-bool CDVDFileInfo::ExtractThumb(const CFileItem& fileItem, CTextureDetails& details, int64_t pos)
-{
- const std::string redactPath = CURL::GetRedacted(fileItem.GetPath());
- auto start = std::chrono::steady_clock::now();
-
- CFileItem item(fileItem);
- item.SetMimeTypeForInternetFile();
- auto pInputStream = CDVDFactoryInputStream::CreateInputStream(NULL, item);
- if (!pInputStream)
- {
- CLog::Log(LOGERROR, "InputStream: Error creating stream for {}", redactPath);
- return false;
- }
-
- if (!pInputStream->Open())
- {
- CLog::Log(LOGERROR, "InputStream: Error opening, {}", redactPath);
- return false;
- }
-
- CDVDDemux *pDemuxer = NULL;
-
- try
- {
- pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(pInputStream, true);
- if(!pDemuxer)
- {
- CLog::Log(LOGERROR, "{} - Error creating demuxer", __FUNCTION__);
- return false;
- }
- }
- catch(...)
- {
- CLog::Log(LOGERROR, "{} - Exception thrown when opening demuxer", __FUNCTION__);
- if (pDemuxer)
- delete pDemuxer;
-
- return false;
- }
-
- int nVideoStream = -1;
- int64_t demuxerId = -1;
- for (CDemuxStream* pStream : pDemuxer->GetStreams())
- {
- if (pStream)
- {
- // ignore if it's a picture attachment (e.g. jpeg artwork)
- if (pStream->type == STREAM_VIDEO && !(pStream->flags & AV_DISPOSITION_ATTACHED_PIC))
- {
- nVideoStream = pStream->uniqueId;
- demuxerId = pStream->demuxerId;
- }
- else
- pDemuxer->EnableStream(pStream->demuxerId, pStream->uniqueId, false);
- }
- }
-
- bool bOk = false;
- int packetsTried = 0;
-
- if (nVideoStream != -1)
- {
- std::unique_ptr<CProcessInfo> pProcessInfo(CProcessInfo::CreateInstance());
- std::vector<AVPixelFormat> pixFmts;
- pixFmts.push_back(AV_PIX_FMT_YUV420P);
- pProcessInfo->SetPixFormats(pixFmts);
-
- CDVDStreamInfo hint(*pDemuxer->GetStream(demuxerId, nVideoStream), true);
- hint.codecOptions = CODEC_FORCE_SOFTWARE;
-
- std::unique_ptr<CDVDVideoCodec> pVideoCodec =
- CDVDFactoryCodec::CreateVideoCodec(hint, *pProcessInfo);
-
- if (pVideoCodec)
- {
- int nTotalLen = pDemuxer->GetStreamLength();
- int64_t nSeekTo = (pos == -1) ? nTotalLen / 3 : pos;
-
- CLog::Log(LOGDEBUG, "{} - seeking to pos {}ms (total: {}ms) in {}", __FUNCTION__, nSeekTo,
- nTotalLen, redactPath);
-
- if (pDemuxer->SeekTime(static_cast<double>(nSeekTo), true))
- {
- CDVDVideoCodec::VCReturn iDecoderState = CDVDVideoCodec::VC_NONE;
- VideoPicture picture = {};
-
- // num streams * 160 frames, should get a valid frame, if not abort.
- int abort_index = pDemuxer->GetNrOfStreams() * 160;
- do
- {
- DemuxPacket* pPacket = pDemuxer->Read();
- packetsTried++;
-
- if (!pPacket)
- break;
-
- if (pPacket->iStreamId != nVideoStream)
- {
- CDVDDemuxUtils::FreeDemuxPacket(pPacket);
- continue;
- }
-
- pVideoCodec->AddData(*pPacket);
- CDVDDemuxUtils::FreeDemuxPacket(pPacket);
-
- iDecoderState = CDVDVideoCodec::VC_NONE;
- while (iDecoderState == CDVDVideoCodec::VC_NONE)
- {
- iDecoderState = pVideoCodec->GetPicture(&picture);
- }
-
- if (iDecoderState == CDVDVideoCodec::VC_PICTURE)
- {
- if(!(picture.iFlags & DVP_FLAG_DROPPED))
- break;
- }
-
- } while (abort_index--);
-
- if (iDecoderState == CDVDVideoCodec::VC_PICTURE && !(picture.iFlags & DVP_FLAG_DROPPED))
- {
- {
- unsigned int nWidth = std::min(picture.iDisplayWidth, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_imageRes);
- double aspect = (double)picture.iDisplayWidth / (double)picture.iDisplayHeight;
- if(hint.forced_aspect && hint.aspect != 0)
- aspect = hint.aspect;
- unsigned int nHeight = (unsigned int)((double)nWidth / aspect);
-
- // We pass the buffers to sws_scale uses 16 aligned widths when using intrinsics
- int sizeNeeded = FFALIGN(nWidth, 16) * nHeight * 4;
- uint8_t *pOutBuf = static_cast<uint8_t*>(av_malloc(sizeNeeded));
- struct SwsContext *context = sws_getContext(picture.iWidth, picture.iHeight,
- AV_PIX_FMT_YUV420P, nWidth, nHeight, AV_PIX_FMT_BGRA, SWS_FAST_BILINEAR, NULL, NULL, NULL);
-
- if (context)
- {
- uint8_t *planes[YuvImage::MAX_PLANES];
- int stride[YuvImage::MAX_PLANES];
- picture.videoBuffer->GetPlanes(planes);
- picture.videoBuffer->GetStrides(stride);
- uint8_t *src[4]= { planes[0], planes[1], planes[2], 0 };
- int srcStride[] = { stride[0], stride[1], stride[2], 0 };
- uint8_t *dst[] = { pOutBuf, 0, 0, 0 };
- int dstStride[] = { (int)nWidth*4, 0, 0, 0 };
- int orientation = DegreeToOrientation(hint.orientation);
- sws_scale(context, src, srcStride, 0, picture.iHeight, dst, dstStride);
- sws_freeContext(context);
-
- details.width = nWidth;
- details.height = nHeight;
- CPicture::CacheTexture(pOutBuf, nWidth, nHeight, nWidth * 4, orientation, nWidth, nHeight, CTextureCache::GetCachedPath(details.file));
- bOk = true;
- }
- av_free(pOutBuf);
- }
- }
- else
- {
- CLog::Log(LOGDEBUG, "{} - decode failed in {} after {} packets.", __FUNCTION__,
- redactPath, packetsTried);
- }
- }
- }
- }
-
- if (pDemuxer)
- delete pDemuxer;
-
- if(!bOk)
- {
- XFILE::CFile file;
- if(file.OpenForWrite(CTextureCache::GetCachedPath(details.file)))
- file.Close();
- }
-
- auto end = std::chrono::steady_clock::now();
- auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
- CLog::Log(LOGDEBUG, "{} - measured {} ms to extract thumb from file <{}> in {} packets. ",
- __FUNCTION__, duration.count(), redactPath, packetsTried);
-
- return bOk;
-}
-
-std::unique_ptr<CTexture> CDVDFileInfo::ExtractThumbToTexture(const CFileItem& fileItem)
+std::unique_ptr<CTexture> CDVDFileInfo::ExtractThumbToTexture(const CFileItem& fileItem,
+ int chapterNumber)
{
if (!CanExtract(fileItem))
return {};
@@ -293,29 +111,16 @@ std::unique_ptr<CTexture> CDVDFileInfo::ExtractThumbToTexture(const CFileItem& f
return {};
}
- CDVDDemux* pDemuxer = NULL;
-
- try
- {
- pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(pInputStream, true);
- if (!pDemuxer)
- {
- CLog::LogF(LOGERROR, "Error creating demuxer");
- return {};
- }
- }
- catch (...)
+ std::unique_ptr<CDVDDemux> demuxer{CDVDFactoryDemuxer::CreateDemuxer(pInputStream, true)};
+ if (!demuxer)
{
- CLog::LogF(LOGERROR, "Exception thrown when opening demuxer");
- if (pDemuxer)
- delete pDemuxer;
-
+ CLog::LogF(LOGERROR, "Error creating demuxer");
return {};
}
int nVideoStream = -1;
int64_t demuxerId = -1;
- for (CDemuxStream* pStream : pDemuxer->GetStreams())
+ for (CDemuxStream* pStream : demuxer->GetStreams())
{
if (pStream)
{
@@ -326,7 +131,7 @@ std::unique_ptr<CTexture> CDVDFileInfo::ExtractThumbToTexture(const CFileItem& f
demuxerId = pStream->demuxerId;
}
else
- pDemuxer->EnableStream(pStream->demuxerId, pStream->uniqueId, false);
+ demuxer->EnableStream(pStream->demuxerId, pStream->uniqueId, false);
}
}
@@ -340,7 +145,7 @@ std::unique_ptr<CTexture> CDVDFileInfo::ExtractThumbToTexture(const CFileItem& f
pixFmts.push_back(AV_PIX_FMT_YUV420P);
pProcessInfo->SetPixFormats(pixFmts);
- CDVDStreamInfo hint(*pDemuxer->GetStream(demuxerId, nVideoStream), true);
+ CDVDStreamInfo hint(*demuxer->GetStream(demuxerId, nVideoStream), true);
hint.codecOptions = CODEC_FORCE_SOFTWARE;
std::unique_ptr<CDVDVideoCodec> pVideoCodec =
@@ -348,22 +153,25 @@ std::unique_ptr<CTexture> CDVDFileInfo::ExtractThumbToTexture(const CFileItem& f
if (pVideoCodec)
{
- int nTotalLen = pDemuxer->GetStreamLength();
- int64_t nSeekTo = nTotalLen / 3;
+ int nTotalLen = demuxer->GetStreamLength();
+
+ bool seekToChapter = chapterNumber > 0 && demuxer->GetChapterCount() > 0;
+ int64_t nSeekTo =
+ seekToChapter ? demuxer->GetChapterPos(chapterNumber) * 1000 : nTotalLen / 3;
CLog::LogF(LOGDEBUG, "seeking to pos {}ms (total: {}ms) in {}", nSeekTo, nTotalLen,
redactPath);
- if (pDemuxer->SeekTime(static_cast<double>(nSeekTo), true))
+ if (demuxer->SeekTime(static_cast<double>(nSeekTo), true))
{
CDVDVideoCodec::VCReturn iDecoderState = CDVDVideoCodec::VC_NONE;
VideoPicture picture = {};
// num streams * 160 frames, should get a valid frame, if not abort.
- int abort_index = pDemuxer->GetNrOfStreams() * 160;
+ int abort_index = demuxer->GetNrOfStreams() * 160;
do
{
- DemuxPacket* pPacket = pDemuxer->Read();
+ DemuxPacket* pPacket = demuxer->Read();
packetsTried++;
if (!pPacket)
@@ -394,35 +202,33 @@ std::unique_ptr<CTexture> CDVDFileInfo::ExtractThumbToTexture(const CFileItem& f
if (iDecoderState == CDVDVideoCodec::VC_PICTURE && !(picture.iFlags & DVP_FLAG_DROPPED))
{
+ unsigned int nWidth =
+ std::min(picture.iDisplayWidth,
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_imageRes);
+ double aspect = (double)picture.iDisplayWidth / (double)picture.iDisplayHeight;
+ if (hint.forced_aspect && hint.aspect != 0)
+ aspect = hint.aspect;
+ unsigned int nHeight = (unsigned int)((double)nWidth / aspect);
+
+ result = CTexture::CreateTexture(nWidth, nHeight);
+ result->SetAlpha(false);
+ struct SwsContext* context =
+ sws_getContext(picture.iWidth, picture.iHeight, AV_PIX_FMT_YUV420P, nWidth, nHeight,
+ AV_PIX_FMT_BGRA, SWS_FAST_BILINEAR, NULL, NULL, NULL);
+
+ if (context)
{
- unsigned int nWidth =
- std::min(picture.iDisplayWidth,
- CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_imageRes);
- double aspect = (double)picture.iDisplayWidth / (double)picture.iDisplayHeight;
- if (hint.forced_aspect && hint.aspect != 0)
- aspect = hint.aspect;
- unsigned int nHeight = (unsigned int)((double)nWidth / aspect);
-
- result = CTexture::CreateTexture(nWidth, nHeight);
- result->SetAlpha(false);
- struct SwsContext* context =
- sws_getContext(picture.iWidth, picture.iHeight, AV_PIX_FMT_YUV420P, nWidth, nHeight,
- AV_PIX_FMT_BGRA, SWS_FAST_BILINEAR, NULL, NULL, NULL);
-
- if (context)
- {
- uint8_t* planes[YuvImage::MAX_PLANES];
- int stride[YuvImage::MAX_PLANES];
- picture.videoBuffer->GetPlanes(planes);
- picture.videoBuffer->GetStrides(stride);
- uint8_t* src[4] = {planes[0], planes[1], planes[2], 0};
- int srcStride[] = {stride[0], stride[1], stride[2], 0};
- uint8_t* dst[] = {result->GetPixels(), 0, 0, 0};
- int dstStride[] = {static_cast<int>(result->GetPitch()), 0, 0, 0};
- result->SetOrientation(DegreeToOrientation(hint.orientation));
- sws_scale(context, src, srcStride, 0, picture.iHeight, dst, dstStride);
- sws_freeContext(context);
- }
+ uint8_t* planes[YuvImage::MAX_PLANES];
+ int stride[YuvImage::MAX_PLANES];
+ picture.videoBuffer->GetPlanes(planes);
+ picture.videoBuffer->GetStrides(stride);
+ uint8_t* src[4] = {planes[0], planes[1], planes[2], 0};
+ int srcStride[] = {stride[0], stride[1], stride[2], 0};
+ uint8_t* dst[] = {result->GetPixels(), 0, 0, 0};
+ int dstStride[] = {static_cast<int>(result->GetPitch()), 0, 0, 0};
+ result->SetOrientation(DegreeToOrientation(hint.orientation));
+ sws_scale(context, src, srcStride, 0, picture.iHeight, dst, dstStride);
+ sws_freeContext(context);
}
}
else
@@ -433,9 +239,6 @@ std::unique_ptr<CTexture> CDVDFileInfo::ExtractThumbToTexture(const CFileItem& f
}
}
- if (pDemuxer)
- delete pDemuxer;
-
auto end = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
CLog::LogF(LOGDEBUG, "measured {} ms to extract thumb from file <{}> in {} packets. ",
diff --git a/xbmc/cores/VideoPlayer/DVDFileInfo.h b/xbmc/cores/VideoPlayer/DVDFileInfo.h
index bbb9776326..0129d6ac4c 100644
--- a/xbmc/cores/VideoPlayer/DVDFileInfo.h
+++ b/xbmc/cores/VideoPlayer/DVDFileInfo.h
@@ -23,10 +23,8 @@ class CTextureDetails;
class CDVDFileInfo
{
public:
- // Extract a thumbnail image from the media referenced by fileItem
- static bool ExtractThumb(const CFileItem& fileItem, CTextureDetails& details, int64_t pos);
-
- static std::unique_ptr<CTexture> ExtractThumbToTexture(const CFileItem& fileItem);
+ static std::unique_ptr<CTexture> ExtractThumbToTexture(const CFileItem& fileItem,
+ int chapterNumber = 0);
/*!
* @brief Can a thumbnail image and file stream details be extracted from this file item?
diff --git a/xbmc/imagefiles/SpecialImageLoaderFactory.cpp b/xbmc/imagefiles/SpecialImageLoaderFactory.cpp
index 0fae961884..c143ad1ef4 100644
--- a/xbmc/imagefiles/SpecialImageLoaderFactory.cpp
+++ b/xbmc/imagefiles/SpecialImageLoaderFactory.cpp
@@ -11,6 +11,7 @@
#include "guilib/Texture.h"
#include "music/MusicEmbeddedImageFileLoader.h"
#include "pictures/PictureFolderImageFileLoader.h"
+#include "video/VideoChapterImageFileLoader.h"
#include "video/VideoEmbeddedImageFileLoader.h"
#include "video/VideoGeneratedImageFileLoader.h"
@@ -22,6 +23,7 @@ CSpecialImageLoaderFactory::CSpecialImageLoaderFactory()
m_specialImageLoaders[1] = std::make_unique<MUSIC_INFO::CMusicEmbeddedImageFileLoader>();
m_specialImageLoaders[2] = std::make_unique<VIDEO::CVideoGeneratedImageFileLoader>();
m_specialImageLoaders[3] = std::make_unique<CPictureFolderImageFileLoader>();
+ m_specialImageLoaders[4] = std::make_unique<VIDEO::CVideoChapterImageFileLoader>();
}
std::unique_ptr<CTexture> CSpecialImageLoaderFactory::Load(const std::string& specialType,
diff --git a/xbmc/imagefiles/SpecialImageLoaderFactory.h b/xbmc/imagefiles/SpecialImageLoaderFactory.h
index d633c617f1..e5bb7b6259 100644
--- a/xbmc/imagefiles/SpecialImageLoaderFactory.h
+++ b/xbmc/imagefiles/SpecialImageLoaderFactory.h
@@ -29,6 +29,6 @@ public:
unsigned int preferredHeight) const;
private:
- std::array<std::unique_ptr<ISpecialImageFileLoader>, 4> m_specialImageLoaders{};
+ std::array<std::unique_ptr<ISpecialImageFileLoader>, 5> m_specialImageLoaders{};
};
} // namespace IMAGE_FILES
diff --git a/xbmc/video/CMakeLists.txt b/xbmc/video/CMakeLists.txt
index b5068599b7..06e559c938 100644
--- a/xbmc/video/CMakeLists.txt
+++ b/xbmc/video/CMakeLists.txt
@@ -3,7 +3,7 @@ set(SOURCES Bookmark.cpp
GUIViewStateVideo.cpp
PlayerController.cpp
Teletext.cpp
- VideoItemArtworkHandler.cpp
+ VideoChapterImageFileLoader.cpp
VideoDatabase.cpp
VideoDbUrl.cpp
VideoEmbeddedImageFileLoader.cpp
@@ -11,6 +11,7 @@ set(SOURCES Bookmark.cpp
VideoInfoDownloader.cpp
VideoInfoScanner.cpp
VideoInfoTag.cpp
+ VideoItemArtworkHandler.cpp
VideoLibraryQueue.cpp
VideoThumbLoader.cpp
VideoUtils.cpp
@@ -23,7 +24,7 @@ set(HEADERS Bookmark.h
PlayerController.h
Teletext.h
TeletextDefines.h
- VideoItemArtworkHandler.h
+ VideoChapterImageFileLoader.h
VideoDatabase.h
VideoDbUrl.h
VideoEmbeddedImageFileLoader.h
@@ -31,6 +32,7 @@ set(HEADERS Bookmark.h
VideoInfoDownloader.h
VideoInfoScanner.h
VideoInfoTag.h
+ VideoItemArtworkHandler.h
VideoLibraryQueue.h
VideoThumbLoader.h
VideoUtils.h
diff --git a/xbmc/video/VideoChapterImageFileLoader.cpp b/xbmc/video/VideoChapterImageFileLoader.cpp
new file mode 100644
index 0000000000..5cb0704ff4
--- /dev/null
+++ b/xbmc/video/VideoChapterImageFileLoader.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "VideoChapterImageFileLoader.h"
+
+#include "DVDFileInfo.h"
+#include "FileItem.h"
+#include "ServiceBroker.h"
+#include "guilib/Texture.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/log.h"
+
+bool VIDEO::CVideoChapterImageFileLoader::CanLoad(const std::string& specialType) const
+{
+ return specialType == "videochapter";
+}
+
+std::unique_ptr<CTexture> VIDEO::CVideoChapterImageFileLoader::Load(
+ const std::string& specialType,
+ const std::string& goofyChapterPath,
+ unsigned int,
+ unsigned int) const
+{
+ // "goofy" chapter path because these paths don't yet conform to 'image://' path standard
+
+ // 10 = length of "chapter://" string prefix from GUIDialogVideoBookmarks
+ size_t lastSlashPos = goofyChapterPath.rfind("/");
+ std::string cleanname = goofyChapterPath.substr(10, lastSlashPos - 10);
+
+ int chapterNum = 0;
+ try
+ {
+ chapterNum = std::stoi(goofyChapterPath.substr(lastSlashPos + 1));
+ }
+ catch (...)
+ {
+ // invalid_argument because these paths can come from anywhere
+ // out_of_range mostly for the same reason - 32k+ seems high for a chapter count
+ return {};
+ }
+ if (chapterNum < 1)
+ {
+ return {};
+ }
+
+ CFileItem item{cleanname, false};
+ return CDVDFileInfo::ExtractThumbToTexture(item, chapterNum);
+}
diff --git a/xbmc/video/VideoChapterImageFileLoader.h b/xbmc/video/VideoChapterImageFileLoader.h
new file mode 100644
index 0000000000..9cd2eab496
--- /dev/null
+++ b/xbmc/video/VideoChapterImageFileLoader.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "imagefiles/SpecialImageFileLoader.h"
+
+namespace VIDEO
+{
+/*!
+ * @brief Generates a texture for a thumbnail of a video chapter.
+*/
+class CVideoChapterImageFileLoader : public IMAGE_FILES::ISpecialImageFileLoader
+{
+public:
+ CVideoChapterImageFileLoader() = default;
+ ~CVideoChapterImageFileLoader() override = default;
+
+ bool CanLoad(const std::string& specialType) const override;
+ std::unique_ptr<CTexture> Load(const std::string& specialType,
+ const std::string& goofyChapterPath,
+ unsigned int preferredWidth,
+ unsigned int preferredHeight) const override;
+};
+
+} // namespace VIDEO
diff --git a/xbmc/video/VideoThumbLoader.cpp b/xbmc/video/VideoThumbLoader.cpp
index 7bce9f300e..91d1e7a436 100644
--- a/xbmc/video/VideoThumbLoader.cpp
+++ b/xbmc/video/VideoThumbLoader.cpp
@@ -37,56 +37,6 @@
using namespace XFILE;
using namespace VIDEO;
-CChapterThumbExtractor::CChapterThumbExtractor(const CFileItem& item,
- const std::string& listpath,
- const std::string& target,
- int64_t pos)
- : m_target(target), m_listpath(listpath), m_item(item)
-{
- m_pos = pos;
-
- if (item.IsVideoDb() && item.HasVideoInfoTag())
- m_item.SetPath(item.GetVideoInfoTag()->m_strFileNameAndPath);
-
- if (m_item.IsStack())
- m_item.SetPath(CStackDirectory::GetFirstStackedFile(m_item.GetPath()));
-}
-
-CChapterThumbExtractor::~CChapterThumbExtractor() = default;
-
-bool CChapterThumbExtractor::operator==(const CJob* job) const
-{
- if (strcmp(job->GetType(),GetType()) == 0)
- {
- const CChapterThumbExtractor* jobExtract = dynamic_cast<const CChapterThumbExtractor*>(job);
- if (jobExtract && jobExtract->m_listpath == m_listpath
- && jobExtract->m_target == m_target)
- return true;
- }
- return false;
-}
-
-bool CChapterThumbExtractor::DoWork()
-{
- if (!CDVDFileInfo::CanExtract(m_item))
- return false;
-
- bool result=false;
- CLog::LogF(LOGDEBUG, "trying to extract thumb from video file {}",
- CURL::GetRedacted(m_item.GetPath()));
- // construct the thumb cache file
- CTextureDetails details;
- details.file = CTextureCache::GetCacheFile(m_target) + ".jpg";
- result = CDVDFileInfo::ExtractThumb(m_item, details, m_pos);
- if (!result)
- return false;
-
- CServiceBroker::GetTextureCache()->AddCachedTexture(m_target, details);
- m_item.SetArt("thumb", m_target);
-
- return true;
-}
-
CVideoThumbLoader::CVideoThumbLoader() : CThumbLoader()
{
m_videoDatabase = new CVideoDatabase();
diff --git a/xbmc/video/VideoThumbLoader.h b/xbmc/video/VideoThumbLoader.h
index 9cf0a9482f..f5505fe860 100644
--- a/xbmc/video/VideoThumbLoader.h
+++ b/xbmc/video/VideoThumbLoader.h
@@ -10,7 +10,6 @@
#include "FileItem.h"
#include "ThumbLoader.h"
-#include "utils/JobManager.h"
#include <map>
#include <vector>
@@ -22,41 +21,6 @@ class EmbeddedArt;
using ArtMap = std::map<std::string, std::string>;
using ArtCache = std::map<std::pair<MediaType, int>, ArtMap>;
-/*!
- \ingroup thumbs,jobs
- \brief Thumb extractor job class
-
- Used by the "chapter browser" GUI window to generate chapter thumbs.
-
- \sa CVideoThumbLoader and CJob
- */
-class CChapterThumbExtractor : public CJob
-{
-public:
- CChapterThumbExtractor(const CFileItem& item,
- const std::string& listpath,
- const std::string& strTarget = "",
- int64_t pos = -1);
- ~CChapterThumbExtractor() override;
-
- /*!
- \brief Work function that extracts thumb.
- */
- bool DoWork() override;
-
- const char* GetType() const override
- {
- return kJobTypeMediaFlags;
- }
-
- bool operator==(const CJob* job) const override;
-
- std::string m_target; ///< thumbpath
- std::string m_listpath; ///< path used in fileitem list
- CFileItem m_item;
- int64_t m_pos; ///< position to extract thumb from
-};
-
class CVideoThumbLoader : public CThumbLoader
{
public:
diff --git a/xbmc/video/dialogs/GUIDialogVideoBookmarks.cpp b/xbmc/video/dialogs/GUIDialogVideoBookmarks.cpp
index c4428817ae..425344219e 100644
--- a/xbmc/video/dialogs/GUIDialogVideoBookmarks.cpp
+++ b/xbmc/video/dialogs/GUIDialogVideoBookmarks.cpp
@@ -35,7 +35,6 @@
#include "utils/Variant.h"
#include "utils/log.h"
#include "video/VideoDatabase.h"
-#include "video/VideoThumbLoader.h"
#include "view/ViewState.h"
#include <mutex>
@@ -51,12 +50,10 @@
#define CONTROL_THUMBS 11
CGUIDialogVideoBookmarks::CGUIDialogVideoBookmarks()
- : CGUIDialog(WINDOW_DIALOG_VIDEO_BOOKMARKS, "VideoOSDBookmarks.xml"),
- CJobQueue(false, 1, CJob::PRIORITY_NORMAL)
+ : CGUIDialog(WINDOW_DIALOG_VIDEO_BOOKMARKS, "VideoOSDBookmarks.xml")
{
m_vecItems = new CFileItemList;
m_loadType = LOAD_EVERY_TIME;
- m_jobsStarted = 0;
}
CGUIDialogVideoBookmarks::~CGUIDialogVideoBookmarks()
@@ -137,9 +134,6 @@ bool CGUIDialogVideoBookmarks::OnMessage(CGUIMessage& message)
case 0:
OnRefreshList();
break;
- case 1:
- UpdateItem(message.GetParam2());
- break;
default:
break;
}
@@ -202,30 +196,6 @@ void CGUIDialogVideoBookmarks::Delete(int item)
Update();
}
-void CGUIDialogVideoBookmarks::UpdateItem(unsigned int chapterIdx)
-{
- std::unique_lock<CCriticalSection> lock(m_refreshSection);
-
- int itemPos = 0;
- for (const auto& item : *m_vecItems)
- {
- if (chapterIdx == item->GetProperty("chapter").asInteger())
- break;
- itemPos++;
- }
-
- if (itemPos < m_vecItems->Size())
- {
- std::string time = StringUtils::Format("chapter://{}/{}", m_filePath, chapterIdx);
- std::string cachefile = CServiceBroker::GetTextureCache()->GetCachedPath(
- CServiceBroker::GetTextureCache()->GetCacheFile(time) + ".jpg");
- if (CFileUtils::Exists(cachefile))
- {
- (*m_vecItems)[itemPos]->SetArt("thumb", cachefile);
- }
- }
-}
-
void CGUIDialogVideoBookmarks::OnRefreshList()
{
m_bookmarks.clear();
@@ -285,18 +255,11 @@ void CGUIDialogVideoBookmarks::OnRefreshList()
CFileItemPtr item(new CFileItem(chapterName));
item->SetLabel2(time);
- std::string chapterPath = StringUtils::Format("chapter://{}/{}", m_filePath, i);
- std::string cachefile = CServiceBroker::GetTextureCache()->GetCachedPath(
- CServiceBroker::GetTextureCache()->GetCacheFile(chapterPath) + ".jpg");
- if (CFileUtils::Exists(cachefile))
- item->SetArt("thumb", cachefile);
- else if (i > m_jobsStarted && CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MYVIDEOS_EXTRACTCHAPTERTHUMBS))
+ if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
+ CSettings::SETTING_MYVIDEOS_EXTRACTCHAPTERTHUMBS))
{
- CFileItem item(m_filePath, false);
- CJob* job = new CChapterThumbExtractor(item, m_filePath, chapterPath, pos * 1000);
- AddJob(job);
- m_mapJobsChapter[job] = i;
- m_jobsStarted++;
+ std::string chapterPath = StringUtils::Format("chapter://{}/{}", m_filePath, i);
+ item->SetArt("thumb", chapterPath);
}
item->SetProperty("chapter", i);
@@ -478,16 +441,11 @@ void CGUIDialogVideoBookmarks::OnWindowLoaded()
m_viewControl.Reset();
m_viewControl.SetParentWindow(GetID());
m_viewControl.AddView(GetControl(CONTROL_THUMBS));
- m_jobsStarted = 0;
- m_mapJobsChapter.clear();
m_vecItems->Clear();
}
void CGUIDialogVideoBookmarks::OnWindowUnload()
{
- //stop running thumb extraction jobs
- CancelJobs();
- m_mapJobsChapter.clear();
m_vecItems->Clear();
CGUIDialog::OnWindowUnload();
m_viewControl.Reset();
@@ -571,20 +529,3 @@ bool CGUIDialogVideoBookmarks::OnAddEpisodeBookmark()
}
return bReturn;
}
-
-void CGUIDialogVideoBookmarks::OnJobComplete(unsigned int jobID,
- bool success, CJob* job)
-{
- if (success && IsActive())
- {
- MAPJOBSCHAPS::iterator iter = m_mapJobsChapter.find(job);
- if (iter != m_mapJobsChapter.end())
- {
- unsigned int chapterIdx = (*iter).second;
- CGUIMessage m(GUI_MSG_REFRESH_LIST, GetID(), 0, 1, chapterIdx);
- CServiceBroker::GetAppMessenger()->SendGUIMessage(m);
- m_mapJobsChapter.erase(iter);
- }
- }
- CJobQueue::OnJobComplete(jobID, success, job);
-}
diff --git a/xbmc/video/dialogs/GUIDialogVideoBookmarks.h b/xbmc/video/dialogs/GUIDialogVideoBookmarks.h
index 801afcdd37..09fa5e3e98 100644
--- a/xbmc/video/dialogs/GUIDialogVideoBookmarks.h
+++ b/xbmc/video/dialogs/GUIDialogVideoBookmarks.h
@@ -9,16 +9,13 @@
#pragma once
#include "guilib/GUIDialog.h"
-#include "utils/JobManager.h"
#include "video/VideoDatabase.h"
#include "view/GUIViewControl.h"
class CFileItemList;
-class CGUIDialogVideoBookmarks : public CGUIDialog, public CJobQueue
+class CGUIDialogVideoBookmarks : public CGUIDialog
{
- typedef std::map<CJob*, unsigned int> MAPJOBSCHAPS;
-
public:
CGUIDialogVideoBookmarks(void);
~CGUIDialogVideoBookmarks(void) override;
@@ -60,17 +57,11 @@ protected:
void OnPopupMenu(int item);
CGUIControl *GetFirstFocusableControl(int id) override;
- void OnJobComplete(unsigned int jobID, bool success, CJob* job) override;
-
CFileItemList* m_vecItems;
CGUIViewControl m_viewControl;
VECBOOKMARKS m_bookmarks;
private:
- void UpdateItem(unsigned int chapterIdx);
-
- int m_jobsStarted;
std::string m_filePath;
CCriticalSection m_refreshSection;
- MAPJOBSCHAPS m_mapJobsChapter;
};