diff --git a/language/English/strings.po b/language/English/strings.po
index 0e473bb9f3a09..795d596fc275e 100644
--- a/language/English/strings.po
+++ b/language/English/strings.po
@@ -12888,7 +12888,12 @@ msgctxt "#25009"
msgid "The menu of this Blu-ray is not supported"
msgstr ""
-#empty strings from id 25010 to 29799
+#: xbmc/video/dialogs/GUIDialogVideoBookmarks.cpp
+msgctxt "#25010"
+msgid "Chapter %u"
+msgstr ""
+
+#empty strings from id 25011 to 29799
#strings 29800 thru 29998 reserved strings used only in the default Project Mayhem III skin and not c++ code
#: skin.confluence
@@ -16312,7 +16317,19 @@ msgctxt "#37043"
msgid "Defines the time to wait for subsequent key presses before performing the skip. Only applies when using smart skipping (when using more than one skip step for a direction)."
msgstr ""
-#empty strings from id 37044 to 38009
+#. Setting #37044 Settings -> Video -> File lists -> Extract chapter thumbnails
+#: system/settings/settings.xml
+msgctxt "#37044"
+msgid "Extract chapter thumbnails"
+msgstr ""
+
+#. Description of setting #37044 "Video -> File lists -> Extract chapter thumbnails"
+#: system/settings/settings.xml
+msgctxt "#37045"
+msgid "Extract chapter thumbnails for presentation in the chapters/bookmarks dialog. This might increase CPU load."
+msgstr ""
+
+#empty strings from id 37046 to 38009
#: system/settings/rbp.xml
msgctxt "#38010"
diff --git a/lib/libdvd/libdvdnav/src/dvdnav.c b/lib/libdvd/libdvdnav/src/dvdnav.c
index 7b2ff892a4b67..b261b3e3590e8 100644
--- a/lib/libdvd/libdvdnav/src/dvdnav.c
+++ b/lib/libdvd/libdvdnav/src/dvdnav.c
@@ -1230,6 +1230,11 @@ int dvdnav_get_button_info(dvdnav_t* this, int alpha[2][4], int color[2][4])
return 0;
}
+void dvdnav_free(void* pdata)
+{
+ free(pdata);
+}
+
#undef printerr
#define printerr(str) strncpy(self->err_str, str, MAX_ERR_LEN);
diff --git a/lib/libdvd/libdvdnav/src/dvdnav/dvdnav.h b/lib/libdvd/libdvdnav/src/dvdnav/dvdnav.h
index bf6aae9d2a3ea..8b9d75ea2bdf7 100644
--- a/lib/libdvd/libdvdnav/src/dvdnav/dvdnav.h
+++ b/lib/libdvd/libdvdnav/src/dvdnav/dvdnav.h
@@ -697,6 +697,7 @@ int8_t dvdnav_is_domain_vtsm(dvdnav_t *self);
*/
int8_t dvdnav_is_domain_vts(dvdnav_t *self);
+void dvdnav_free(void* pdata);
#ifdef __cplusplus
}
diff --git a/lib/libdvd/patches/libdvdnav.diff b/lib/libdvd/patches/libdvdnav.diff
index cb702a4b38de2..42bad876b0fb7 100644
--- a/lib/libdvd/patches/libdvdnav.diff
+++ b/lib/libdvd/patches/libdvdnav.diff
@@ -149,6 +149,16 @@ diff -uwr ../libdvdnav-4.2.1/src/dvdnav/dvdnav.h lib/libdvd/libdvdnav/src/dvdnav
/*
* Stop playing the current position and start playback of the title
+ * from the specified timecode.
+ *
+@@ -695,6 +697,7 @@
+ */
+ int8_t dvdnav_is_domain_vts(dvdnav_t *self);
+
++void dvdnav_free(void* pdata);
+
+ #ifdef __cplusplus
+ }
diff -uwr ../libdvdnav-4.2.1/src/dvdnav.c lib/libdvd/libdvdnav/src/dvdnav.c
--- ../libdvdnav-4.2.1/src/dvdnav.c Thu Oct 3 23:39:38 2013
+++ lib/libdvd/libdvdnav/src/dvdnav.c Fri Feb 7 19:24:42 2014
@@ -284,6 +294,11 @@ diff -uwr ../libdvdnav-4.2.1/src/dvdnav.c lib/libdvd/libdvdnav/src/dvdnav.c
+ return 0;
+}
+
++void dvdnav_free(void* pdata)
++{
++ free(pdata);
++}
++
+#undef printerr
+#define printerr(str) strncpy(self->err_str, str, MAX_ERR_LEN);
+
diff --git a/system/settings/rbp.xml b/system/settings/rbp.xml
index f572eedbfae23..bd3e2ad51f283 100644
--- a/system/settings/rbp.xml
+++ b/system/settings/rbp.xml
@@ -16,6 +16,13 @@
+
+
+
+
+
diff --git a/system/settings/settings.xml b/system/settings/settings.xml
index 3ece367d0bee1..0fbb920ec0b57 100644
--- a/system/settings/settings.xml
+++ b/system/settings/settings.xml
@@ -784,6 +784,11 @@
true
+
1
true
diff --git a/xbmc/ApplicationPlayer.cpp b/xbmc/ApplicationPlayer.cpp
index 99d5f3f133501..ede084cbe1bc9 100644
--- a/xbmc/ApplicationPlayer.cpp
+++ b/xbmc/ApplicationPlayer.cpp
@@ -131,11 +131,21 @@ int CApplicationPlayer::GetChapterCount()
return 0;
}
-void CApplicationPlayer::GetChapterName(std::string& strChapterName)
+void CApplicationPlayer::GetChapterName(std::string& strChapterName,
+ int chapterIdx)
{
std::shared_ptr player = GetInternal();
if (player)
- player->GetChapterName(strChapterName);
+ player->GetChapterName(strChapterName, chapterIdx);
+}
+
+int64_t CApplicationPlayer::GetChapterPos(int chapterIdx)
+{
+ std::shared_ptr player = GetInternal();
+ if (player)
+ return player->GetChapterPos(chapterIdx);
+
+ return -1;
}
bool CApplicationPlayer::HasAudio() const
diff --git a/xbmc/ApplicationPlayer.h b/xbmc/ApplicationPlayer.h
index fdbd6cafa8ec4..36fce14bb670b 100644
--- a/xbmc/ApplicationPlayer.h
+++ b/xbmc/ApplicationPlayer.h
@@ -95,7 +95,8 @@ class CApplicationPlayer
float GetCachePercentage() const;
int GetChapterCount();
int GetChapter();
- void GetChapterName(std::string& strChapterName);
+ void GetChapterName(std::string& strChapterName, int chapterIdx=-1);
+ int64_t GetChapterPos(int chapterIdx=-1);
void GetDeinterlaceMethods(std::vector &deinterlaceMethods);
void GetDeinterlaceModes(std::vector &deinterlaceModes);
void GetGeneralInfo(std::string& strVideoInfo);
diff --git a/xbmc/cores/IPlayer.h b/xbmc/cores/IPlayer.h
index e8b3dcbac66d7..f6a7d71b51bc0 100644
--- a/xbmc/cores/IPlayer.h
+++ b/xbmc/cores/IPlayer.h
@@ -183,7 +183,8 @@ class IPlayer
virtual int GetChapterCount() { return 0; }
virtual int GetChapter() { return -1; }
- virtual void GetChapterName(std::string& strChapterName) { return; }
+ virtual void GetChapterName(std::string& strChapterName, int chapterIdx = -1) { return; }
+ virtual int64_t GetChapterPos(int chapterIdx=-1) { return 0; }
virtual int SeekChapter(int iChapter) { return -1; }
// virtual bool GetChapterInfo(int chapter, SChapterInfo &info) { return false; }
diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h
index d69991ea5b02f..fca164d60184d 100644
--- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h
+++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h
@@ -275,9 +275,17 @@ class CDVDDemux
virtual int GetChapter() { return 0; }
/*
- * Get the name of the current chapter
+ * Get the name of a chapter
+ * \param strChapterName[out] Name of chapter
+ * \param chapterIdx -1 for current chapter, else a chapter index
*/
- virtual void GetChapterName(std::string& strChapterName) {}
+ virtual void GetChapterName(std::string& strChapterName, int chapterIdx=-1) {}
+
+ /*
+ * Get the position of a chapter
+ * \param chapterIdx -1 for current chapter, else a chapter index
+ */
+ virtual int64_t GetChapterPos(int chapterIdx=-1) { return 0; }
/*
* Set the playspeed, if demuxer can handle different
diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
index 1315117125680..f58472f6b87a1 100644
--- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
+++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
@@ -1401,14 +1401,15 @@ int CDVDDemuxFFmpeg::GetChapter()
return 0;
}
-void CDVDDemuxFFmpeg::GetChapterName(std::string& strChapterName)
+void CDVDDemuxFFmpeg::GetChapterName(std::string& strChapterName, int chapterIdx)
{
+ if (chapterIdx <= 0 || chapterIdx > GetChapterCount())
+ chapterIdx = GetChapter();
CDVDInputStream::IChapter* ich = dynamic_cast(m_pInput);
if(ich)
- ich->GetChapterName(strChapterName);
+ ich->GetChapterName(strChapterName, chapterIdx);
else
{
- int chapterIdx = GetChapter();
if(chapterIdx <= 0)
return;
@@ -1419,6 +1420,20 @@ void CDVDDemuxFFmpeg::GetChapterName(std::string& strChapterName)
}
}
+int64_t CDVDDemuxFFmpeg::GetChapterPos(int chapterIdx)
+{
+ if (chapterIdx <= 0 || chapterIdx > GetChapterCount())
+ chapterIdx = GetChapter();
+ if(chapterIdx <= 0)
+ return 0;
+
+ CDVDInputStream::IChapter* ich = dynamic_cast(m_pInput);
+ if(ich)
+ return ich->GetChapterPos(chapterIdx);
+
+ return m_pFormatContext->chapters[chapterIdx-1]->start*av_q2d(m_pFormatContext->chapters[chapterIdx-1]->time_base);
+}
+
bool CDVDDemuxFFmpeg::SeekChapter(int chapter, double* startpts)
{
if(chapter < 1)
@@ -1432,7 +1447,9 @@ bool CDVDDemuxFFmpeg::SeekChapter(int chapter, double* startpts)
return false;
if(startpts)
- *startpts = DVD_NOPTS_VALUE;
+ {
+ *startpts = DVD_SEC_TO_TIME(ich->GetChapterPos(chapter));
+ }
Flush();
return true;
diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h
index bb64a2f2bfa6d..d180e40392e75 100644
--- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h
+++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h
@@ -110,7 +110,8 @@ class CDVDDemuxFFmpeg : public CDVDDemux
bool SeekChapter(int chapter, double* startpts = NULL);
int GetChapterCount();
int GetChapter();
- void GetChapterName(std::string& strChapterName);
+ void GetChapterName(std::string& strChapterName, int chapterIdx=-1);
+ int64_t GetChapterPos(int chapterIdx=-1);
virtual void GetStreamCodecName(int iStreamId, std::string &strName);
bool Aborted();
diff --git a/xbmc/cores/dvdplayer/DVDFileInfo.cpp b/xbmc/cores/dvdplayer/DVDFileInfo.cpp
index e05b6470fd2f0..1648d605f13b8 100644
--- a/xbmc/cores/dvdplayer/DVDFileInfo.cpp
+++ b/xbmc/cores/dvdplayer/DVDFileInfo.cpp
@@ -97,7 +97,7 @@ int DegreeToOrientation(int degrees)
bool CDVDFileInfo::ExtractThumb(const std::string &strPath,
CTextureDetails &details,
- CStreamDetails *pStreamDetails)
+ CStreamDetails *pStreamDetails, int pos)
{
std::string redactPath = CURL::GetRedacted(strPath);
unsigned int nTime = XbmcThreads::SystemClockMillis();
@@ -216,7 +216,7 @@ bool CDVDFileInfo::ExtractThumb(const std::string &strPath,
if (pVideoCodec)
{
int nTotalLen = pDemuxer->GetStreamLength();
- int nSeekTo = nTotalLen / 3;
+ int 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))
diff --git a/xbmc/cores/dvdplayer/DVDFileInfo.h b/xbmc/cores/dvdplayer/DVDFileInfo.h
index 63491ebe6a05e..1afecaf65ae46 100644
--- a/xbmc/cores/dvdplayer/DVDFileInfo.h
+++ b/xbmc/cores/dvdplayer/DVDFileInfo.h
@@ -36,7 +36,7 @@ class CDVDFileInfo
// Extract a thumbnail immage from the media at strPath, optionally populating a streamdetails class with the data
static bool ExtractThumb(const std::string &strPath,
CTextureDetails &details,
- CStreamDetails *pStreamDetails);
+ CStreamDetails *pStreamDetails, int pos=-1);
// Probe the files streams and store the info in the VideoInfoTag
static bool GetFileStreamDetails(CFileItem *pItem);
diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStream.h b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStream.h
index 51a3849631147..11df1b804b0fd 100644
--- a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStream.h
+++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStream.h
@@ -101,7 +101,8 @@ class CDVDInputStream
virtual ~IChapter() {};
virtual int GetChapter() = 0;
virtual int GetChapterCount() = 0;
- virtual void GetChapterName(std::string& name) = 0;
+ virtual void GetChapterName(std::string& name, int ch=-1) = 0;
+ virtual int64_t GetChapterPos(int ch=-1) = 0;
virtual bool SeekChapter(int ch) = 0;
};
diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamBluray.cpp b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamBluray.cpp
index 2ef5f95d7fa87..61667bd7ee7f1 100644
--- a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamBluray.cpp
+++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamBluray.cpp
@@ -935,6 +935,17 @@ bool CDVDInputStreamBluray::SeekChapter(int ch)
return true;
}
+int64_t CDVDInputStreamBluray::GetChapterPos(int ch)
+{
+ if (ch == -1 || ch > GetChapterCount())
+ ch = GetChapter();
+
+ if (m_title && m_title->chapters)
+ return m_title->chapters[ch - 1].start / 90000;
+ else
+ return 0;
+}
+
int64_t CDVDInputStreamBluray::Seek(int64_t offset, int whence)
{
#if LIBBLURAY_BYTESEEK
diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamBluray.h b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamBluray.h
index 1f5f9266520f2..1107ed2282db7 100644
--- a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamBluray.h
+++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamBluray.h
@@ -96,7 +96,8 @@ class CDVDInputStreamBluray
int GetChapter();
int GetChapterCount();
- void GetChapterName(std::string& name) {};
+ void GetChapterName(std::string& name, int ch=-1) {};
+ int64_t GetChapterPos(int ch);
bool SeekChapter(int ch);
int GetTotalTime();
diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamNavigator.cpp b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamNavigator.cpp
index 3fd9a0745816a..c7a6e4c159ff8 100644
--- a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamNavigator.cpp
+++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamNavigator.cpp
@@ -1455,3 +1455,26 @@ bool CDVDInputStreamNavigator::GetDVDSerialString(std::string& serialStr)
serialStr.assign(str);
return true;
}
+
+int64_t CDVDInputStreamNavigator::GetChapterPos(int ch)
+{
+ if (ch == -1 || ch > GetChapterCount())
+ ch = GetChapter();
+
+ if (ch <= 1)
+ return 0;
+
+ uint64_t* times = NULL;
+ uint64_t duration;
+
+ m_dll.dvdnav_describe_title_chapters(m_dvdnav, m_iTitle, ×, &duration);
+
+ int64_t result = 0;
+
+ if (times)
+ {
+ result = times[ch - 2] / 90000;
+ m_dll.dvdnav_free(times);
+ }
+ return result;
+}
diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamNavigator.h b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamNavigator.h
index f9a6d0f6ee84e..d7e967cf21907 100644
--- a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamNavigator.h
+++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamNavigator.h
@@ -127,7 +127,8 @@ class CDVDInputStreamNavigator
int GetChapter() { return m_iPart; } // the current part in the current title
int GetChapterCount() { return m_iPartCount; } // the number of parts in the current title
- void GetChapterName(std::string& name) {};
+ void GetChapterName(std::string& name, int idx=-1) {};
+ int64_t GetChapterPos(int ch=-1);
bool SeekChapter(int iChapter);
int GetTotalTime(); // the total time in milli seconds
diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DllDvdNav.h b/xbmc/cores/dvdplayer/DVDInputStreams/DllDvdNav.h
index ecacd978f344a..dcbf829cd840a 100644
--- a/xbmc/cores/dvdplayer/DVDInputStreams/DllDvdNav.h
+++ b/xbmc/cores/dvdplayer/DVDInputStreams/DllDvdNav.h
@@ -106,6 +106,8 @@ class DllDvdNavInterface
virtual dvdnav_status_t dvdnav_mouse_select(dvdnav_t *self, pci_t *pci, int32_t x, int32_t y)=0;
virtual dvdnav_status_t dvdnav_get_title_string(dvdnav_t *self, const char **title_str)=0;
virtual dvdnav_status_t dvdnav_get_serial_string(dvdnav_t *self, const char **serial_str)=0;
+ virtual uint32_t dvdnav_describe_title_chapters(dvdnav_t* self, uint32_t title, uint64_t** times, uint64_t* duration)=0;
+ virtual void dvdnav_free(void* pdata) = 0;
};
#if (defined USE_STATIC_LIBDVDNAV)
@@ -231,6 +233,10 @@ class DllDvdNav : public DllDynamic, DllDvdNavInterface
{ return ::dvdnav_get_title_string(self, title_str); }
virtual dvdnav_status_t dvdnav_get_serial_string(dvdnav_t *self, const char **serial_str)
{ return ::dvdnav_get_serial_string(self, serial_str); }
+ virtual uint32_t dvdnav_describe_title_chapters(dvdnav_t* self, uint32_t title, uint64_t** times, uint64_t* duration)
+ { return ::dvdnav_describe_title_chapters(self, title, times, duration); }
+ virtual void dvdnav_free(void* data)
+ { return ::dvdnav_free(data); }
// DLL faking.
virtual bool ResolveExports() { return true; }
@@ -303,6 +309,8 @@ class DllDvdNav : public DllDynamic, DllDvdNavInterface
DEFINE_METHOD4(dvdnav_status_t, dvdnav_mouse_select, (dvdnav_t *p1, pci_t *p2, int32_t p3, int32_t p4))
DEFINE_METHOD2(dvdnav_status_t, dvdnav_get_title_string, (dvdnav_t *p1, const char **p2))
DEFINE_METHOD2(dvdnav_status_t, dvdnav_get_serial_string, (dvdnav_t *p1, const char **p2))
+ DEFINE_METHOD4(uint32_t, dvdnav_describe_title_chapters, (dvdnav_t* p1, uint32_t p2, uint64_t** p3, uint64_t* p4))
+ DEFINE_METHOD1(void, dvdnav_free, (void *p1))
BEGIN_METHOD_RESOLVE()
RESOLVE_METHOD(dvdnav_open)
RESOLVE_METHOD(dvdnav_close)
@@ -363,6 +371,8 @@ class DllDvdNav : public DllDynamic, DllDvdNavInterface
RESOLVE_METHOD(dvdnav_mouse_select)
RESOLVE_METHOD(dvdnav_get_title_string)
RESOLVE_METHOD(dvdnav_get_serial_string)
+ RESOLVE_METHOD(dvdnav_describe_title_chapters)
+ RESOLVE_METHOD(dvdnav_free)
END_METHOD_RESOLVE()
};
diff --git a/xbmc/cores/dvdplayer/DVDPlayer.cpp b/xbmc/cores/dvdplayer/DVDPlayer.cpp
index 9a1e4979a16d9..5e16230fda725 100644
--- a/xbmc/cores/dvdplayer/DVDPlayer.cpp
+++ b/xbmc/cores/dvdplayer/DVDPlayer.cpp
@@ -4078,7 +4078,7 @@ bool CDVDPlayer::SetPlayerState(const std::string& state)
int CDVDPlayer::GetChapterCount()
{
CSingleLock lock(m_StateSection);
- return m_State.chapter_count;
+ return m_State.chapters.size();
}
int CDVDPlayer::GetChapter()
@@ -4087,10 +4087,13 @@ int CDVDPlayer::GetChapter()
return m_State.chapter;
}
-void CDVDPlayer::GetChapterName(std::string& strChapterName)
+void CDVDPlayer::GetChapterName(std::string& strChapterName, int chapterIdx)
{
CSingleLock lock(m_StateSection);
- strChapterName = m_State.chapter_name;
+ if (chapterIdx == -1 && m_State.chapter > 0 && m_State.chapter <= (int) m_State.chapters.size())
+ strChapterName = m_State.chapters[m_State.chapter - 1].first;
+ else if (chapterIdx > 0 && chapterIdx <= (int) m_State.chapters.size())
+ strChapterName = m_State.chapters[chapterIdx - 1].first;
}
int CDVDPlayer::SeekChapter(int iChapter)
@@ -4110,6 +4113,15 @@ int CDVDPlayer::SeekChapter(int iChapter)
return 0;
}
+int64_t CDVDPlayer::GetChapterPos(int chapterIdx)
+{
+ CSingleLock lock(m_StateSection);
+ if (chapterIdx > 0 && chapterIdx <= (int) m_StateInput.chapters.size())
+ return m_State.chapters[chapterIdx - 1].second;
+
+ return -1;
+}
+
int CDVDPlayer::AddSubtitle(const std::string& strSubPath)
{
return AddSubtitleFile(strSubPath);
@@ -4281,9 +4293,21 @@ void CDVDPlayer::UpdatePlayState(double timeout)
if(m_pDemuxer)
{
- state.chapter = m_pDemuxer->GetChapter();
- state.chapter_count = m_pDemuxer->GetChapterCount();
- m_pDemuxer->GetChapterName(state.chapter_name);
+ if (IsInMenu())
+ state.chapter = 0;
+ else
+ state.chapter = m_pDemuxer->GetChapter();
+
+ state.chapters.clear();
+ if (m_pDemuxer->GetChapterCount() > 0)
+ {
+ for (int i = 0; i < m_pDemuxer->GetChapterCount(); ++i)
+ {
+ std::string name;
+ m_pDemuxer->GetChapterName(name, i + 1);
+ state.chapters.push_back(make_pair(name, m_pDemuxer->GetChapterPos(i + 1)));
+ }
+ }
if(state.dts == DVD_NOPTS_VALUE)
state.time = 0;
diff --git a/xbmc/cores/dvdplayer/DVDPlayer.h b/xbmc/cores/dvdplayer/DVDPlayer.h
index 8bcda49f39eb4..24d85fb4a1bec 100644
--- a/xbmc/cores/dvdplayer/DVDPlayer.h
+++ b/xbmc/cores/dvdplayer/DVDPlayer.h
@@ -266,7 +266,8 @@ class CDVDPlayer : public IPlayer, public CThread, public IDVDPlayer
virtual int GetChapterCount();
virtual int GetChapter();
- virtual void GetChapterName(std::string& strChapterName);
+ virtual void GetChapterName(std::string& strChapterName, int chapterIdx=-1);
+ virtual int64_t GetChapterPos(int chapterIdx=-1);
virtual int SeekChapter(int iChapter);
virtual void SeekTime(int64_t iTime);
@@ -479,8 +480,7 @@ class CDVDPlayer : public IPlayer, public CThread, public IDVDPlayer
dts = DVD_NOPTS_VALUE;
player_state = "";
chapter = 0;
- chapter_name = "";
- chapter_count = 0;
+ chapters.clear();
canrecord = false;
recording = false;
canpause = false;
@@ -505,9 +505,8 @@ class CDVDPlayer : public IPlayer, public CThread, public IDVDPlayer
std::string player_state; // full player state
- int chapter; // current chapter
- std::string chapter_name; // name of current chapter
- int chapter_count;// number of chapter
+ int chapter; // current chapter
+ std::vector> chapters; // name and position for chapters
bool canrecord; // can input stream record
bool recording; // are we currently recording
diff --git a/xbmc/video/VideoThumbLoader.cpp b/xbmc/video/VideoThumbLoader.cpp
index 196846632e68b..e01609d89772d 100644
--- a/xbmc/video/VideoThumbLoader.cpp
+++ b/xbmc/video/VideoThumbLoader.cpp
@@ -50,12 +50,16 @@ using namespace VIDEO;
CThumbExtractor::CThumbExtractor(const CFileItem& item,
const std::string& listpath,
bool thumb,
- const std::string& target)
+ const std::string& target,
+ int64_t pos,
+ bool fillStreamDetails)
{
m_listpath = listpath;
m_target = target;
m_thumb = thumb;
m_item = item;
+ m_pos = pos;
+ m_fillStreamDetails = fillStreamDetails;
if (item.IsVideoDb() && item.HasVideoInfoTag())
m_item.SetPath(item.GetVideoInfoTag()->m_strFileNameAndPath);
@@ -73,7 +77,8 @@ bool CThumbExtractor::operator==(const CJob* job) const
if (strcmp(job->GetType(),GetType()) == 0)
{
const CThumbExtractor* jobExtract = dynamic_cast(job);
- if (jobExtract && jobExtract->m_listpath == m_listpath)
+ if (jobExtract && jobExtract->m_listpath == m_listpath
+ && jobExtract->m_target == m_target)
return true;
}
return false;
@@ -106,7 +111,7 @@ bool CThumbExtractor::DoWork()
// construct the thumb cache file
CTextureDetails details;
details.file = CTextureCache::GetCacheFile(m_target) + ".jpg";
- result = CDVDFileInfo::ExtractThumb(m_item.GetPath(), details, &m_item.GetVideoInfoTag()->m_streamDetails);
+ result = CDVDFileInfo::ExtractThumb(m_item.GetPath(), details, m_fillStreamDetails ? &m_item.GetVideoInfoTag()->m_streamDetails : NULL, (int) m_pos);
if(result)
{
CTextureCache::Get().AddCachedTexture(m_target, details);
diff --git a/xbmc/video/VideoThumbLoader.h b/xbmc/video/VideoThumbLoader.h
index 55560dad4eeb9..1ad97adfbd847 100644
--- a/xbmc/video/VideoThumbLoader.h
+++ b/xbmc/video/VideoThumbLoader.h
@@ -38,7 +38,7 @@ class CVideoDatabase;
class CThumbExtractor : public CJob
{
public:
- CThumbExtractor(const CFileItem& item, const std::string& listpath, bool thumb, const std::string& strTarget="");
+ CThumbExtractor(const CFileItem& item, const std::string& listpath, bool thumb, const std::string& strTarget="", int64_t pos = -1, bool fillStreamDetails = true);
virtual ~CThumbExtractor();
/*!
@@ -57,6 +57,8 @@ class CThumbExtractor : public CJob
std::string m_listpath; ///< path used in fileitem list
CFileItem m_item;
bool m_thumb; ///< extract thumb?
+ int64_t m_pos; ///< position to extract thumb from
+ bool m_fillStreamDetails; ///< fill in stream details?
};
class CVideoThumbLoader : public CThumbLoader, public CJobQueue
diff --git a/xbmc/video/dialogs/GUIDialogVideoBookmarks.cpp b/xbmc/video/dialogs/GUIDialogVideoBookmarks.cpp
index 00139c834b60a..328974bcbcf0f 100644
--- a/xbmc/video/dialogs/GUIDialogVideoBookmarks.cpp
+++ b/xbmc/video/dialogs/GUIDialogVideoBookmarks.cpp
@@ -48,6 +48,11 @@
#include "utils/Variant.h"
#include "Util.h"
#include "cores/IPlayer.h"
+#include "video/VideoThumbLoader.h"
+#include "filesystem/File.h"
+#include "TextureCache.h"
+#include "ApplicationMessenger.h"
+#include "settings/Settings.h"
using namespace std;
@@ -61,10 +66,12 @@ using namespace std;
#define CONTROL_THUMBS 11
CGUIDialogVideoBookmarks::CGUIDialogVideoBookmarks()
- : CGUIDialog(WINDOW_DIALOG_VIDEO_BOOKMARKS, "VideoOSDBookmarks.xml")
+ : CGUIDialog(WINDOW_DIALOG_VIDEO_BOOKMARKS, "VideoOSDBookmarks.xml"),
+ CJobQueue(false, 1, CJob::PRIORITY_NORMAL)
{
m_vecItems = new CFileItemList;
- m_loadType = KEEP_IN_MEMORY;
+ m_loadType = LOAD_EVERY_TIME;
+ m_jobsStarted = 0;
}
CGUIDialogVideoBookmarks::~CGUIDialogVideoBookmarks()
@@ -134,7 +141,17 @@ bool CGUIDialogVideoBookmarks::OnMessage(CGUIMessage& message)
break;
case GUI_MSG_REFRESH_LIST:
{
- OnRefreshList();
+ switch (message.GetParam1())
+ {
+ case 0:
+ OnRefreshList();
+ break;
+ case 1:
+ UpdateItem(message.GetParam2());
+ break;
+ default:
+ break;
+ }
}
break;
}
@@ -159,7 +176,7 @@ bool CGUIDialogVideoBookmarks::OnAction(const CAction &action)
void CGUIDialogVideoBookmarks::OnPopupMenu(int item)
{
- if (item < 0 || item >= m_vecItems->Size())
+ if (item < 0 || item >= (int) m_bookmarks.size())
return;
// highlight the item
@@ -199,25 +216,41 @@ void CGUIDialogVideoBookmarks::Delete(int item)
Update();
}
+void CGUIDialogVideoBookmarks::UpdateItem(unsigned int chapterIdx)
+{
+ CSingleLock lock(m_refreshSection);
+ int itemPos = ((chapterIdx - 1) + m_chapterOffset);
+ if (itemPos < m_vecItems->Size())
+ {
+ std::string time = StringUtils::Format("chapter://%s/%i", m_filePath.c_str(), chapterIdx);
+ std::string cachefile = CTextureCache::Get().GetCachedPath(CTextureCache::Get().GetCacheFile(time) + ".jpg");
+ if (XFILE::CFile::Exists(cachefile))
+ {
+ (*m_vecItems)[itemPos]->SetArt("thumb", cachefile);
+ }
+ }
+}
+
void CGUIDialogVideoBookmarks::OnRefreshList()
{
m_bookmarks.clear();
CBookmark resumemark;
// open the d/b and retrieve the bookmarks for the current movie
- std::string path = g_application.CurrentFile();
+ m_filePath = g_application.CurrentFile();
if (g_application.CurrentFileItem().HasProperty("original_listitem_url") &&
!URIUtils::IsVideoDb(g_application.CurrentFileItem().GetProperty("original_listitem_url").asString()))
- path = g_application.CurrentFileItem().GetProperty("original_listitem_url").asString();
+ m_filePath = g_application.CurrentFileItem().GetProperty("original_listitem_url").asString();
CVideoDatabase videoDatabase;
videoDatabase.Open();
- videoDatabase.GetBookMarksForFile(path, m_bookmarks);
- videoDatabase.GetBookMarksForFile(path, m_bookmarks, CBookmark::EPISODE, true);
+ videoDatabase.GetBookMarksForFile(m_filePath, m_bookmarks);
+ videoDatabase.GetBookMarksForFile(m_filePath, m_bookmarks, CBookmark::EPISODE, true);
/* push in the resume mark first */
- if( videoDatabase.GetResumeBookMark(path, resumemark) )
+ if (videoDatabase.GetResumeBookMark(m_filePath, resumemark))
m_bookmarks.push_back(resumemark);
videoDatabase.Close();
+ CSingleLock lock(m_refreshSection);
m_vecItems->Clear();
// cycle through each stored bookmark and add it to our list control
for (unsigned int i = 0; i < m_bookmarks.size(); ++i)
@@ -235,6 +268,34 @@ void CGUIDialogVideoBookmarks::OnRefreshList()
item->SetArt("thumb", m_bookmarks[i].thumbNailImage);
m_vecItems->Add(item);
}
+ // add chapters if around
+ m_chapterOffset = m_vecItems->Size();
+ for (int i=1;i<=g_application.m_pPlayer->GetChapterCount();++i)
+ {
+ std::string chapterName;
+ g_application.m_pPlayer->GetChapterName(chapterName, i);
+ if (chapterName.empty())
+ chapterName = StringUtils::Format(g_localizeStrings.Get(25010).c_str(), i);
+ int64_t pos = g_application.m_pPlayer->GetChapterPos(i);
+ std::string time =
+ StringUtils::SecondsToTimeString((long) pos, TIME_FORMAT_HH_MM_SS);
+ std::string name = StringUtils::Format("%s (%s)",
+ chapterName.c_str(), time.c_str());
+ CFileItemPtr item(new CFileItem(name));
+ time = StringUtils::Format("chapter://%s/%i", m_filePath.c_str(), i);
+ std::string cachefile = CTextureCache::Get().GetCachedPath(CTextureCache::Get().GetCacheFile(time)+".jpg");
+ if (XFILE::CFile::Exists(cachefile))
+ item->SetArt("thumb", cachefile);
+ else if (i > m_jobsStarted && CSettings::Get().GetBool("myvideos.extractchapterthumbs"))
+ {
+ CFileItem item(m_filePath, false);
+ CJob* job = new CThumbExtractor(item, m_filePath, true, time, pos * 1000, false);
+ AddJob(job);
+ m_mapJobsChapter[job] = i;
+ m_jobsStarted++;
+ }
+ m_vecItems->Add(item);
+ }
m_viewControl.SetItems(*m_vecItems);
}
@@ -284,11 +345,16 @@ void CGUIDialogVideoBookmarks::Clear()
void CGUIDialogVideoBookmarks::GotoBookmark(int item)
{
- if (item < 0 || item >= (int)m_bookmarks.size()) return;
+ if (item < 0 || item >= (int)m_bookmarks.size()+g_application.m_pPlayer->GetChapterCount()) return;
if (g_application.m_pPlayer->HasPlayer())
{
- g_application.m_pPlayer->SetPlayerState(m_bookmarks[item].playerState);
- g_application.SeekTime((double)m_bookmarks[item].timeInSeconds);
+ if (item < (int) m_bookmarks.size())
+ {
+ g_application.m_pPlayer->SetPlayerState(m_bookmarks[item].playerState);
+ g_application.SeekTime((double)m_bookmarks[item].timeInSeconds);
+ }
+ else
+ g_application.m_pPlayer->SeekChapter(item-m_bookmarks.size()+1);
}
}
@@ -387,10 +453,17 @@ 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();
}
@@ -474,4 +547,19 @@ 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);
+ CApplicationMessenger::Get().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 8d0e6213f2147..9bac80ff913ec 100644
--- a/xbmc/video/dialogs/GUIDialogVideoBookmarks.h
+++ b/xbmc/video/dialogs/GUIDialogVideoBookmarks.h
@@ -23,11 +23,14 @@
#include "guilib/GUIDialog.h"
#include "view/GUIViewControl.h"
#include "video/VideoDatabase.h"
+#include "utils/JobManager.h"
class CFileItemList;
-class CGUIDialogVideoBookmarks : public CGUIDialog
+class CGUIDialogVideoBookmarks : public CGUIDialog, public CJobQueue
{
+ typedef std::map MAPJOBSCHAPS;
+
public:
CGUIDialogVideoBookmarks(void);
virtual ~CGUIDialogVideoBookmarks(void);
@@ -69,7 +72,18 @@ class CGUIDialogVideoBookmarks : public CGUIDialog
void OnPopupMenu(int item);
CGUIControl *GetFirstFocusableControl(int id);
+ void OnJobComplete(unsigned int jobID, bool success, CJob* job);
+
CFileItemList* m_vecItems;
CGUIViewControl m_viewControl;
VECBOOKMARKS m_bookmarks;
+
+private:
+ void UpdateItem(unsigned int chapterIdx);
+
+ int m_chapterOffset;
+ int m_jobsStarted;
+ std::string m_filePath;
+ CCriticalSection m_refreshSection;
+ MAPJOBSCHAPS m_mapJobsChapter;
};