From 0fb175f191f1d348b0fb2c5ae4301b7e26cd38b6 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Mon, 7 Apr 2014 18:19:32 +0100
Subject: [PATCH 01/88] [rbp/omxplayer] When opening a stream don't try to
 update gui so often

---
 xbmc/dialogs/GUIDialogBusy.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/xbmc/dialogs/GUIDialogBusy.cpp b/xbmc/dialogs/GUIDialogBusy.cpp
index e7cfcdd..20b99ad 100644
--- a/xbmc/dialogs/GUIDialogBusy.cpp
+++ b/xbmc/dialogs/GUIDialogBusy.cpp
@@ -68,7 +68,11 @@ bool CGUIDialogBusy::WaitOnEvent(CEvent &event, unsigned int displaytime /* = 10
     if (dialog)
     {
       dialog->Show();
+#ifdef TARGET_RASPBERRY_PI
+      while(!event.WaitMSec(100))
+#else
       while(!event.WaitMSec(1))
+#endif
       {
         g_windowManager.ProcessRenderLoop(false);
         if (allowCancel && dialog->IsCanceled())

From e2be18e9c1d63873a4b194e5888571d0f59bd1eb Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Tue, 29 Apr 2014 15:23:22 +0100
Subject: [PATCH 02/88] [ffmpeg] Speed up wtv index creation

The index creation is O(N^2) with number of entries (typically thousands).
On a Pi this can take more than 60 seconds to execute for a recording of a few hours.

By replacing with an O(N) loop, this takes virtually zero time
---
 tools/depends/target/ffmpeg/Makefile               |  3 +-
 .../ffmpeg_Speed_up_wtv_index_creation.patch       | 47 ++++++++++++++++++++++
 2 files changed, 49 insertions(+), 1 deletion(-)
 create mode 100644 tools/depends/target/ffmpeg/ffmpeg_Speed_up_wtv_index_creation.patch

diff --git a/tools/depends/target/ffmpeg/Makefile b/tools/depends/target/ffmpeg/Makefile
index 0e08dcf..7ce982c 100644
--- a/tools/depends/target/ffmpeg/Makefile
+++ b/tools/depends/target/ffmpeg/Makefile
@@ -1,6 +1,6 @@
 include ../../Makefile.include
 include FFMPEG-VERSION
-DEPS= ../../Makefile.include FFMPEG-VERSION Makefile
+DEPS= ../../Makefile.include FFMPEG-VERSION Makefile ffmpeg_Speed_up_wtv_index_creation.patch
 
 # set to "yes" to enable patching
 # we don't apply patches until we move to a vanilla ffmpeg tarball
@@ -63,6 +63,7 @@ $(PLATFORM): $(TARBALLS_LOCATION)/$(ARCHIVE) $(DEPS)
 	rm -rf $(PLATFORM); mkdir -p $(PLATFORM)
 	cd $(PLATFORM); $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE)
 	cd $(PLATFORM); sed -i".bak" -e "s%pkg_config_default=pkg-config%export PKG_CONFIG_LIBDIR=$(PREFIX)/lib/pkgconfig \&\& pkg_config_default=$(NATIVEPREFIX)/bin/pkg-config%" configure
+	cd $(PLATFORM); patch -p3 < ../ffmpeg_Speed_up_wtv_index_creation.patch
 	cd $(PLATFORM);\
 	CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" CPPFLAGS="$(CPPFLAGS)" LDFLAGS="$(LDFLAGS)" \
 	./configure $(ffmpg_config)
diff --git a/tools/depends/target/ffmpeg/ffmpeg_Speed_up_wtv_index_creation.patch b/tools/depends/target/ffmpeg/ffmpeg_Speed_up_wtv_index_creation.patch
new file mode 100644
index 0000000..4ac5636
--- /dev/null
+++ b/tools/depends/target/ffmpeg/ffmpeg_Speed_up_wtv_index_creation.patch
@@ -0,0 +1,47 @@
+commit 0e7427498cb1131671f6fe9d054245ae7e5a36f5
+Author: popcornmix <popcornmix@gmail.com>
+Date:   Tue Mar 25 19:43:07 2014 +0000
+
+    [ffmpeg] Speed up wtv index creation
+
+    The index creation is O(N^2) with number of entries (typically thousands).
+    On a Pi this can take more than 60 seconds to execute for a recording of a few hours.
+
+    By replacing with an O(N) loop, this takes virtually zero time
+
+diff --git a/lib/ffmpeg/libavformat/wtvdec.c b/lib/ffmpeg/libavformat/wtvdec.c
+index e423370..70898bd 100644
+--- a/lib/ffmpeg/libavformat/wtvdec.c
++++ b/lib/ffmpeg/libavformat/wtvdec.c
+@@ -980,21 +980,23 @@ static int read_header(AVFormatContext *s)
+                 pb = wtvfile_open(s, root, root_size, ff_timeline_table_0_entries_Events_le16);
+                 if (pb) {
+                     int i;
++                    AVIndexEntry *e = wtv->index_entries;
++                    AVIndexEntry *e_end = wtv->index_entries + wtv->nb_index_entries - 1;
++                    uint64_t last_position = 0;
+                     while (1) {
+                         uint64_t frame_nb = avio_rl64(pb);
+                         uint64_t position = avio_rl64(pb);
++                        while (frame_nb > e->size && e <= e_end) {
++                           e->pos = last_position;
++                           e++;
++                        }
+                         if (avio_feof(pb))
+                             break;
+-                        for (i = wtv->nb_index_entries - 1; i >= 0; i--) {
+-                            AVIndexEntry *e = wtv->index_entries + i;
+-                            if (frame_nb > e->size)
+-                                break;
+-                            if (position > e->pos)
+-                                e->pos = position;
+-                        }
++                        last_position = position;
+                     }
++                    e_end->pos = last_position;
+                     wtvfile_close(pb);
+-                    st->duration = wtv->index_entries[wtv->nb_index_entries - 1].timestamp;
++                    st->duration = e_end->timestamp;
+                 }
+             }
+         }

From 5f39c4a49b854f0edadf73a4f8f05ac3d48ed053 Mon Sep 17 00:00:00 2001
From: Jonathan Marshall <jmarshall@xbmc.org>
Date: Sat, 2 Nov 2013 23:49:17 +1300
Subject: [PATCH 03/88] adds GetTvShowSeasons

---
 xbmc/video/VideoDatabase.cpp | 30 ++++++++++++++++++++++++------
 xbmc/video/VideoDatabase.h   |  1 +
 2 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/xbmc/video/VideoDatabase.cpp b/xbmc/video/VideoDatabase.cpp
index 056e9eb..c21ece2 100644
--- a/xbmc/video/VideoDatabase.cpp
+++ b/xbmc/video/VideoDatabase.cpp
@@ -4211,7 +4211,7 @@ bool CVideoDatabase::RemoveArtForItem(int mediaId, const MediaType &mediaType, c
   return result;
 }
 
-bool CVideoDatabase::GetTvShowSeasonArt(int showId, map<int, map<string, string> > &seasonArt)
+bool CVideoDatabase::GetTvShowSeasons(int showId, map<int, int> &seasons)
 {
   try
   {
@@ -4222,19 +4222,37 @@ bool CVideoDatabase::GetTvShowSeasonArt(int showId, map<int, map<string, string>
     CStdString sql = PrepareSQL("select idSeason,season from seasons where idShow=%i", showId);
     m_pDS2->query(sql.c_str());
 
-    vector< pair<int, int> > seasons;
+    seasons.clear();
     while (!m_pDS2->eof())
     {
-      seasons.push_back(make_pair(m_pDS2->fv(0).get_asInt(), m_pDS2->fv(1).get_asInt()));
+      seasons.insert(make_pair(m_pDS2->fv(1).get_asInt(), m_pDS2->fv(0).get_asInt()));
       m_pDS2->next();
     }
     m_pDS2->close();
+    return true;
+  }
+  catch (...)
+  {
+    CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, showId);
+  }
+  return false;
+}
+
+bool CVideoDatabase::GetTvShowSeasonArt(int showId, map<int, map<string, string> > &seasonArt)
+{
+  try
+  {
+    if (NULL == m_pDB.get()) return false;
+    if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
+
+    map<int, int> seasons;
+    GetTvShowSeasons(showId, seasons);
 
-    for (vector< pair<int,int> >::const_iterator i = seasons.begin(); i != seasons.end(); ++i)
+    for (map<int, int>::const_iterator i = seasons.begin(); i != seasons.end(); ++i)
     {
       map<string, string> art;
-      GetArtForItem(i->first, MediaTypeSeason, art);
-      seasonArt.insert(make_pair(i->second,art));
+      GetArtForItem(i->second, MediaTypeSeason, art);
+      seasonArt.insert(make_pair(i->first,art));
     }
     return true;
   }
diff --git a/xbmc/video/VideoDatabase.h b/xbmc/video/VideoDatabase.h
index 78259ed..cbb26b7 100644
--- a/xbmc/video/VideoDatabase.h
+++ b/xbmc/video/VideoDatabase.h
@@ -746,6 +746,7 @@ class CVideoDatabase : public CDatabase
   std::string GetArtForItem(int mediaId, const MediaType &mediaType, const std::string &artType);
   bool RemoveArtForItem(int mediaId, const MediaType &mediaType, const std::string &artType);
   bool RemoveArtForItem(int mediaId, const MediaType &mediaType, const std::set<std::string> &artTypes);
+  bool GetTvShowSeasons(int showId, std::map<int, int> &seasons);
   bool GetTvShowSeasonArt(int mediaId, std::map<int, std::map<std::string, std::string> > &seasonArt);
   bool GetArtTypes(const MediaType &mediaType, std::vector<std::string> &artTypes);
 

From 6698d8aa78b855e60b16a176277354cb759676a5 Mon Sep 17 00:00:00 2001
From: Jonathan Marshall <jmarshall@xbmc.org>
Date: Sat, 2 Nov 2013 23:50:10 +1300
Subject: [PATCH 04/88] move AddSeason() public.

---
 xbmc/video/VideoDatabase.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/xbmc/video/VideoDatabase.h b/xbmc/video/VideoDatabase.h
index cbb26b7..1a79c00 100644
--- a/xbmc/video/VideoDatabase.h
+++ b/xbmc/video/VideoDatabase.h
@@ -757,6 +757,7 @@ class CVideoDatabase : public CDatabase
 
   virtual bool GetFilter(CDbUrl &videoUrl, Filter &filter, SortDescription &sorting);
 
+  int AddSeason(int showID, int season);
   int AddSet(const CStdString& strSet);
   void ClearMovieSet(int idMovie);
   void SetMovieSet(int idMovie, int idSet);
@@ -787,7 +788,6 @@ class CVideoDatabase : public CDatabase
 
   int AddTvShow();
   int AddMusicVideo(const CStdString& strFilenameAndPath);
-  int AddSeason(int showID, int season);
 
   /*! \brief Adds a path to the tvshow link table.
    \param idShow the id of the show.

From a437f73647103824d808572a8cb274a9d18dd402 Mon Sep 17 00:00:00 2001
From: Jonathan Marshall <jmarshall@xbmc.org>
Date: Sat, 2 Nov 2013 23:48:24 +1300
Subject: [PATCH 05/88] adds GetArt function to (video) scraper, allowing art
 to be fetched given the video identifier.

---
 xbmc/addons/Scraper.cpp            | 38 ++++++++++++++++++++++++++++++++++++++
 xbmc/addons/Scraper.h              |  3 +++
 xbmc/video/VideoInfoDownloader.cpp |  5 +++++
 xbmc/video/VideoInfoDownloader.h   |  7 +++++++
 4 files changed, 53 insertions(+)

diff --git a/xbmc/addons/Scraper.cpp b/xbmc/addons/Scraper.cpp
index 76d5248..f3f2930 100644
--- a/xbmc/addons/Scraper.cpp
+++ b/xbmc/addons/Scraper.cpp
@@ -924,6 +924,44 @@ EPISODELIST CScraper::GetEpisodeList(XFILE::CCurlFile &fcurl, const CScraperUrl
   return vcep;
 }
 
+// takes URL; returns true and populates art XML details on success, false otherwise
+bool CScraper::GetArt(XFILE::CCurlFile &fcurl, const std::string &id, CVideoInfoTag &video)
+{
+  CLog::Log(LOGDEBUG, "%s: Reading art for '%s' using %s scraper "
+            "(file: '%s', content: '%s', version: '%s')", __FUNCTION__, id.c_str(), Name().c_str(), Path().c_str(),
+            ADDON::TranslateContent(Content()).c_str(), Version().asString().c_str());
+
+  video.Reset();
+  vector<string> vcsIn;
+  CScraperUrl scurl;
+  vcsIn.push_back(id);
+  vector<string> vcsOut = RunNoThrow("GetArt", scurl, fcurl, &vcsIn);
+
+  // parse XML output
+  bool fRet(false);
+  for (vector<string>::const_iterator i = vcsOut.begin(); i != vcsOut.end(); ++i)
+  {
+    CXBMCTinyXML doc;
+    doc.Parse(*i, TIXML_ENCODING_UTF8);
+    if (!doc.RootElement())
+    {
+      CLog::Log(LOGERROR, "%s: Unable to parse XML", __FUNCTION__);
+      continue;
+    }
+
+    TiXmlHandle xhDoc(&doc);
+    TiXmlElement *pxeDetails = xhDoc.FirstChild("details").Element();
+    if (!pxeDetails)
+    {
+      CLog::Log(LOGERROR, "%s: Invalid XML file (want <details>)", __FUNCTION__);
+      continue;
+    }
+    video.Load(pxeDetails, true/*fChain*/);
+    fRet = true;  // but don't exit in case of chaining
+  }
+  return fRet;
+}
+
 // takes URL; returns true and populates video details on success, false otherwise
 bool CScraper::GetVideoDetails(XFILE::CCurlFile &fcurl, const CScraperUrl &scurl,
   bool fMovie/*else episode*/, CVideoInfoTag &video)
diff --git a/xbmc/addons/Scraper.h b/xbmc/addons/Scraper.h
index c7274f2..5df5296 100644
--- a/xbmc/addons/Scraper.h
+++ b/xbmc/addons/Scraper.h
@@ -18,6 +18,8 @@
  *  <http://www.gnu.org/licenses/>.
  *
  */
+
+#include <string>
 #include "addons/Addon.h"
 #include "XBDateTime.h"
 #include "utils/ScraperUrl.h"
@@ -146,6 +148,7 @@ class CScraper : public CAddon
     CAlbum &album);
   bool GetArtistDetails(XFILE::CCurlFile &fcurl, const CScraperUrl &scurl,
     const std::string &sSearch, CArtist &artist);
+  bool GetArt(XFILE::CCurlFile &fcurl, const std::string &id, CVideoInfoTag &video);
 
 private:
   CScraper(const CScraper &rhs);
diff --git a/xbmc/video/VideoInfoDownloader.cpp b/xbmc/video/VideoInfoDownloader.cpp
index f33ac8a..5d84734 100644
--- a/xbmc/video/VideoInfoDownloader.cpp
+++ b/xbmc/video/VideoInfoDownloader.cpp
@@ -191,6 +191,11 @@ bool CVideoInfoDownloader::GetDetails(const CScraperUrl &url,
     return m_info->GetVideoDetails(*m_http, url, true/*fMovie*/, movieDetails);
 }
 
+bool CVideoInfoDownloader::GetArt(const std::string &id, CVideoInfoTag &details)
+{
+  return m_info->GetArt(*m_http, id, details);
+}
+
 bool CVideoInfoDownloader::GetEpisodeDetails(const CScraperUrl &url,
                                              CVideoInfoTag &movieDetails,
                                              CGUIDialogProgress *pProgress /* = NULL */)
diff --git a/xbmc/video/VideoInfoDownloader.h b/xbmc/video/VideoInfoDownloader.h
index 22ac229..75bc341 100644
--- a/xbmc/video/VideoInfoDownloader.h
+++ b/xbmc/video/VideoInfoDownloader.h
@@ -59,6 +59,13 @@ class CVideoInfoDownloader : public CThread
 
   static void ShowErrorDialog(const ADDON::CScraperError &sce);
 
+  /*! \brief Grab art URLs for an item with the scraper
+   \param id the unique identifier used by the scraper to describe the item.
+   \param details [out] the video info tag structure to fill with art.
+   \return true on success, false on failure.
+   */
+  bool GetArt(const std::string &id, CVideoInfoTag &details);
+
 protected:
   enum LOOKUP_STATE { DO_NOTHING = 0,
                       FIND_MOVIE = 1,

From fc5835d26ec5a89a4870a4797e19047b1492f2f7 Mon Sep 17 00:00:00 2001
From: Jonathan Marshall <jmarshall@xbmc.org>
Date: Sat, 2 Nov 2013 23:53:14 +1300
Subject: [PATCH 06/88] refresh season art if a new season is found that isn't
 recorded in the database yet. Fixes #14339

---
 xbmc/video/VideoInfoScanner.cpp | 33 ++++++++++++++++++++++++++++++++-
 xbmc/video/VideoInfoScanner.h   |  2 ++
 2 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/xbmc/video/VideoInfoScanner.cpp b/xbmc/video/VideoInfoScanner.cpp
index 9bf4349..f14b978 100644
--- a/xbmc/video/VideoInfoScanner.cpp
+++ b/xbmc/video/VideoInfoScanner.cpp
@@ -1372,6 +1372,10 @@ namespace VIDEO
       pDlgProgress->Progress();
     }
 
+    bool updateSeasons = false;
+    map<int, int> seasons;
+    m_database.GetTvShowSeasons(showInfo.m_iDbId, seasons);
+
     EPISODELIST episodes;
     bool hasEpisodeGuide = false;
 
@@ -1420,6 +1424,8 @@ namespace VIDEO
         }
         if (AddVideo(&item, CONTENT_TVSHOWS, file->isFolder, true, &showInfo) < 0)
           return INFO_ERROR;
+        if (seasons.find(item.GetVideoInfoTag()->m_iSeason) == seasons.end())
+          updateSeasons = true;
         continue;
       }
 
@@ -1549,6 +1555,8 @@ namespace VIDEO
           
         if (AddVideo(&item, CONTENT_TVSHOWS, file->isFolder, useLocal, &showInfo) < 0)
           return INFO_ERROR;
+        if (seasons.find(item.GetVideoInfoTag()->m_iSeason) == seasons.end())
+          updateSeasons = true;
       }
       else
       {
@@ -1557,9 +1565,27 @@ namespace VIDEO
                   file->cDate.GetAsLocalizedDate().c_str(), file->strTitle.c_str());
       }
     }
+    if (updateSeasons)
+      UpdateSeasons(showInfo, scraper, useLocal);
     return INFO_ADDED;
   }
 
+  void CVideoInfoScanner::UpdateSeasons(const CVideoInfoTag &showInfo, const ADDON::ScraperPtr &scraper, bool useLocal)
+  {
+    map<int, map<string, string> > seasonArt;
+    m_database.GetTvShowSeasonArt(showInfo.m_iDbId, seasonArt);
+    CVideoInfoTag details;
+    CVideoInfoDownloader loader(scraper);
+    loader.GetArt(showInfo.m_strIMDBNumber, details);
+    details.m_strPath = showInfo.m_strPath;
+    GetSeasonThumbs(details, seasonArt, CVideoThumbLoader::GetArtTypes("season"), useLocal);
+    for (map<int, map<string, string> >::iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
+    {
+      int seasonID = m_database.AddSeason(showInfo.m_iDbId, i->first);
+      m_database.SetArtForItem(seasonID, "season", i->second);
+    }
+  }
+
   CStdString CVideoInfoScanner::GetnfoFile(CFileItem *item, bool bGrabAny) const
   {
     CStdString nfoFile;
@@ -1824,6 +1850,11 @@ namespace VIDEO
     }
     for (int season = -1; season <= maxSeasons; season++)
     {
+      // skip if we already have some art
+      map<int, map<string, string> >::const_iterator i = seasonArt.find(season);
+      if (i != seasonArt.end() && !i->second.empty())
+        continue;
+
       map<string, string> art;
       if (useLocal)
       {
@@ -1877,7 +1908,7 @@ namespace VIDEO
           art.insert(make_pair(artTypes.front(), image));
       }
 
-      seasonArt.insert(make_pair(season, art));
+      seasonArt[season] = art;
     }
   }
 
diff --git a/xbmc/video/VideoInfoScanner.h b/xbmc/video/VideoInfoScanner.h
index 92883e7..47d5ed4 100644
--- a/xbmc/video/VideoInfoScanner.h
+++ b/xbmc/video/VideoInfoScanner.h
@@ -231,6 +231,8 @@ namespace VIDEO
      */
     INFO_RET OnProcessSeriesFolder(EPISODELIST& files, const ADDON::ScraperPtr &scraper, bool useLocal, const CVideoInfoTag& showInfo, CGUIDialogProgress* pDlgProgress = NULL);
 
+    void UpdateSeasons(const CVideoInfoTag &showInfo, const ADDON::ScraperPtr &scraper, bool useLocal);
+
     bool EnumerateSeriesFolder(CFileItem* item, EPISODELIST& episodeList);
     bool ProcessItemByVideoInfoTag(const CFileItem *item, EPISODELIST &episodeList);
 

From 45467aa1dbcbce0e31bd9266480ef2e29c332d00 Mon Sep 17 00:00:00 2001
From: Jonathan Marshall <jmarshall@xbmc.org>
Date: Sat, 2 Nov 2013 23:53:34 +1300
Subject: [PATCH 07/88] REMOVEME: updated thetvdb.com scraper to support art
 updates

---
 addons/metadata.tvdb.com/tvdb.xml | 59 +++++++++++++++++++++++++--------------
 1 file changed, 38 insertions(+), 21 deletions(-)

diff --git a/addons/metadata.tvdb.com/tvdb.xml b/addons/metadata.tvdb.com/tvdb.xml
index f27e4fc..bdf329f 100644
--- a/addons/metadata.tvdb.com/tvdb.xml
+++ b/addons/metadata.tvdb.com/tvdb.xml
@@ -102,57 +102,74 @@
 			<RegExp input="$$5" output="&lt;actor&gt;&lt;name&gt;\2&lt;/name&gt;&lt;role&gt;\3&lt;/role&gt;&lt;/actor&gt;" dest="4+">
 				<expression repeat="yes" noclean="1,2,3">&lt;Actor&gt;.*?&lt;Image&gt;([^&lt;]*)&lt;/Image&gt;.*?&lt;Name&gt;([^&lt;]*)&lt;/Name&gt;.*?&lt;Role&gt;([^&lt;]*)</expression>
 			</RegExp>
-			<RegExp input="$$5" output="&lt;thumb aspect=&quot;banner&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
+			<RegExp input="$$2" output="&lt;chain function=&quot;GetArt&quot;&gt;\1&lt;/chain&gt;" dest="4+">
+				<expression/>
+			</RegExp>
+			<RegExp input="$$3" output="\1" dest="6">
+				<expression>.*/(.*).zip</expression>
+			</RegExp>
+			<RegExp input="$$3" output="&lt;episodeguide&gt;&lt;url cache=&quot;$$2-$$6.xml&quot;&gt;\1&lt;/url&gt;&lt;/episodeguide&gt;" dest="4+">
+				<expression/>
+			</RegExp>
+			<expression noclean="1"/>
+		</RegExp>
+	</GetDetails>
+
+	<GetArt dest="3">
+		<RegExp input="$$4" output="&lt;details&gt;\1&lt;/details&gt;" dest="3">
+			<RegExp input="$$1" output="&lt;url function=&quot;ParseArt&quot; cache=&quot;\1-banners.xml&quot;&gt;http://thetvdb.com/api/1D62F2F90030C444/series/\1/banners.xml&lt;/url&gt;" dest="4">
+				<expression/>
+			</RegExp>
+			<expression noclean="1"/>
+		</RegExp>
+	</GetArt>
+	<ParseArt dest="3">
+		<RegExp input="$$4" output="&lt;details&gt;\1&lt;/details&gt;" dest="3">
+			<RegExp input="$$1" output="&lt;thumb aspect=&quot;banner&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4">
 				<expression repeat="yes">&lt;BannerPath&gt;([^&lt;]*)&lt;/BannerPath&gt;[^&lt;]*&lt;BannerType&gt;series&lt;/BannerType&gt;[^&lt;]*&lt;BannerType2&gt;graphical&lt;/BannerType2&gt;[^&lt;]*&lt;Language&gt;$INFO[language]&lt;/Language&gt;</expression>
 			</RegExp>
-			<RegExp input="$$5" output="&lt;thumb aspect=&quot;banner&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
+			<RegExp input="$$1" output="&lt;thumb aspect=&quot;banner&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
 				<expression repeat="yes">&lt;BannerPath&gt;([^&lt;]*)&lt;/BannerPath&gt;[^&lt;]*&lt;BannerType&gt;series&lt;/BannerType&gt;[^&lt;]*&lt;BannerType2&gt;graphical&lt;/BannerType2&gt;[^&lt;]*&lt;Language&gt;((?!$INFO[language])[a-z])*&lt;/Language&gt;</expression>
 			</RegExp>
-			<RegExp input="$$5" output="&lt;thumb aspect=&quot;banner&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
+			<RegExp input="$$1" output="&lt;thumb aspect=&quot;banner&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
 				<expression repeat="yes">&lt;BannerPath&gt;([^&lt;]*)&lt;/BannerPath&gt;[^&lt;]*&lt;BannerType&gt;series&lt;/BannerType&gt;[^&lt;]*&lt;BannerType2&gt;text&lt;/BannerType2&gt;[^&lt;]*&lt;Language&gt;$INFO[language]&lt;/Language&gt;</expression>
 			</RegExp>
-			<RegExp input="$$5" output="&lt;thumb aspect=&quot;banner&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
+			<RegExp input="$$1" output="&lt;thumb aspect=&quot;banner&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
 				<expression repeat="yes">&lt;BannerPath&gt;([^&lt;]*)&lt;/BannerPath&gt;[^&lt;]*&lt;BannerType&gt;series&lt;/BannerType&gt;[^&lt;]*&lt;BannerType2&gt;text&lt;/BannerType2&gt;[^&lt;]*&lt;Language&gt;((?!$INFO[language])[a-z])*&lt;/Language&gt;</expression>
 			</RegExp>
-			<RegExp input="$$5" output="&lt;thumb aspect=&quot;banner&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
+			<RegExp input="$$1" output="&lt;thumb aspect=&quot;banner&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
 				<expression repeat="yes">&lt;BannerPath&gt;([^&lt;]*)&lt;/BannerPath&gt;[^&lt;]*&lt;BannerType&gt;series&lt;/BannerType&gt;[^&lt;]*&lt;BannerType2&gt;blank&lt;/BannerType2&gt;[^&lt;]*&lt;Language&gt;&lt;/Language&gt;</expression>
 			</RegExp>
-			<RegExp input="$$5" output="&lt;thumb aspect=&quot;poster&quot; type=&quot;season&quot; season=&quot;\2&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
+			<RegExp input="$$1" output="&lt;thumb aspect=&quot;poster&quot; type=&quot;season&quot; season=&quot;\2&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
 				<expression repeat="yes">&lt;BannerPath&gt;([^&lt;]*)&lt;/BannerPath&gt;[^&lt;]*&lt;BannerType&gt;season&lt;/BannerType&gt;[^&lt;]*&lt;BannerType2&gt;season&lt;/BannerType2&gt;[^&lt;]*&lt;Language&gt;$INFO[language]&lt;/Language&gt;[^&lt;]*[^S]*Season&gt;([0-9]+)&lt;/Season&gt;</expression>
 			</RegExp>
-			<RegExp input="$$5" output="&lt;thumb aspect=&quot;poster&quot; type=&quot;season&quot; season=&quot;\3&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
+			<RegExp input="$$1" output="&lt;thumb aspect=&quot;poster&quot; type=&quot;season&quot; season=&quot;\3&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
 				<expression repeat="yes">&lt;BannerPath&gt;([^&lt;]*)&lt;/BannerPath&gt;[^&lt;]*&lt;BannerType&gt;season&lt;/BannerType&gt;[^&lt;]*&lt;BannerType2&gt;season&lt;/BannerType2&gt;[^&lt;]*&lt;Language&gt;((?!$INFO[language])[a-z])*&lt;/Language&gt;[^&lt;]*[^S]*Season&gt;([0-9]+)&lt;/Season&gt;</expression>
 			</RegExp>
-			<RegExp input="$$5" output="&lt;thumb aspect=&quot;banner&quot; type=&quot;season&quot; season=&quot;\2&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
+			<RegExp input="$$1" output="&lt;thumb aspect=&quot;banner&quot; type=&quot;season&quot; season=&quot;\2&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
 				<expression repeat="yes">&lt;BannerPath&gt;([^&lt;]*)&lt;/BannerPath&gt;[^&lt;]*&lt;BannerType&gt;season&lt;/BannerType&gt;[^&lt;]*&lt;BannerType2&gt;seasonwide&lt;/BannerType2&gt;[^&lt;]*&lt;Language&gt;$INFO[language]&lt;/Language&gt;[^&lt;]*[^S]*Season&gt;([0-9]+)&lt;/Season&gt;</expression>
 			</RegExp>
-			<RegExp input="$$5" output="&lt;thumb aspect=&quot;banner&quot; type=&quot;season&quot; season=&quot;\3&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
+			<RegExp input="$$1" output="&lt;thumb aspect=&quot;banner&quot; type=&quot;season&quot; season=&quot;\3&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
 				<expression repeat="yes">&lt;BannerPath&gt;([^&lt;]*)&lt;/BannerPath&gt;[^&lt;]*&lt;BannerType&gt;season&lt;/BannerType&gt;[^&lt;]*&lt;BannerType2&gt;seasonwide&lt;/BannerType2&gt;[^&lt;]*&lt;Language&gt;((?!$INFO[language])[a-z])*&lt;/Language&gt;[^&lt;]*[^S]*Season&gt;([0-9]+)&lt;/Season&gt;</expression>
 			</RegExp>
-			<RegExp input="$$5" output="&lt;thumb aspect=&quot;poster&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
+			<RegExp input="$$1" output="&lt;thumb aspect=&quot;poster&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
 				<expression repeat="yes">&lt;BannerPath&gt;([^&lt;]*)&lt;/BannerPath&gt;[^&lt;]*&lt;BannerType&gt;poster&lt;/BannerType&gt;</expression>
 			</RegExp>
-			<RegExp input="$$5" output="&lt;thumb aspect=&quot;poster&quot; type=&quot;season&quot; season=&quot;-1&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
+			<RegExp input="$$1" output="&lt;thumb aspect=&quot;poster&quot; type=&quot;season&quot; season=&quot;-1&quot;&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
 				<expression repeat="yes">&lt;BannerPath&gt;([^&lt;]*)&lt;/BannerPath&gt;[^&lt;]*&lt;BannerType&gt;poster&lt;/BannerType&gt;</expression>
 			</RegExp>
-			<RegExp conditional="fanart" input="$$7" output="&lt;fanart url=&quot;http://thetvdb.com/banners/&quot;&gt;\1&lt;/fanart&gt;" dest="4+">
-				<RegExp input="$$5" output="&lt;thumb dim=&quot;\2&quot; colors=&quot;\3&quot; preview=&quot;_cache/\1&quot;&gt;\1&lt;/thumb&gt;" dest="7+">
+			<RegExp conditional="fanart" input="$$5" output="&lt;fanart url=&quot;http://thetvdb.com/banners/&quot;&gt;\1&lt;/fanart&gt;" dest="4+">
+				<RegExp input="$$1" output="&lt;thumb dim=&quot;\2&quot; colors=&quot;\3&quot; preview=&quot;_cache/\1&quot;&gt;\1&lt;/thumb&gt;" dest="5">
 					<expression repeat="yes">&lt;BannerPath&gt;([^&lt;]*)&lt;/BannerPath&gt;[^&lt;]*&lt;BannerType&gt;fanart&lt;/BannerType&gt;[^&lt;]*&lt;BannerType2&gt;([^&lt;]*)&lt;/BannerType2&gt;[^&lt;]*&lt;Colors&gt;([^&lt;]*)&lt;/Colors&gt;[^&lt;]*&lt;Language&gt;$INFO[language]&lt;/Language&gt;</expression>
 				</RegExp>
-				<RegExp input="$$5" output="&lt;thumb dim=&quot;\2&quot; colors=&quot;\3&quot; preview=&quot;_cache/\1&quot;&gt;\1&lt;/thumb&gt;" dest="7+">
+				<RegExp input="$$1" output="&lt;thumb dim=&quot;\2&quot; colors=&quot;\3&quot; preview=&quot;_cache/\1&quot;&gt;\1&lt;/thumb&gt;" dest="5+">
 					<expression repeat="yes">&lt;BannerPath&gt;([^&lt;]*)&lt;/BannerPath&gt;[^&lt;]*&lt;BannerType&gt;fanart&lt;/BannerType&gt;[^&lt;]*&lt;BannerType2&gt;([^&lt;]*)&lt;/BannerType2&gt;[^&lt;]*&lt;Colors&gt;([^&lt;]*)&lt;/Colors&gt;[^&lt;]*&lt;Language&gt;((?!$INFO[language])[a-z])*&lt;/Language&gt;</expression>
 				</RegExp>
 				<expression noclean="1"/>
 			</RegExp>
-			<RegExp input="$$3" output="\1" dest="6">
-				<expression>.*/(.*).zip</expression>
-			</RegExp>
-			<RegExp input="$$3" output="&lt;episodeguide&gt;&lt;url cache=&quot;$$2-$$6.xml&quot;&gt;\1&lt;/url&gt;&lt;/episodeguide&gt;" dest="4+">
-				<expression/>
-			</RegExp>
 			<expression noclean="1"/>
 		</RegExp>
-	</GetDetails>
+	</ParseArt>
 
 	<!-- input:	$1=html !-->
 	<!-- input:	$2=series url !-->

From e6e425270f311de3202cb4cd9a3684fe6dc59ca0 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sat, 2 Aug 2014 17:48:04 +0100
Subject: [PATCH 08/88] [omx] Report decoded image name

---
 xbmc/cores/omxplayer/OMXImage.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/xbmc/cores/omxplayer/OMXImage.cpp b/xbmc/cores/omxplayer/OMXImage.cpp
index 3fbea3b..57f69c5 100644
--- a/xbmc/cores/omxplayer/OMXImage.cpp
+++ b/xbmc/cores/omxplayer/OMXImage.cpp
@@ -325,6 +325,7 @@ bool COMXImage::DecodeJpegToTexture(COMXImageFile *file, unsigned int width, uns
   {
     ret = true;
     *userdata = tex;
+    CLog::Log(LOGDEBUG, "%s: decoded %s %dx%d", __func__, file->GetFilename(), width, height);
   }
   else
   {

From 76836bef384f9076800b4d28f236990f8207948e Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sat, 14 Dec 2013 16:55:05 +0000
Subject: [PATCH 09/88] logging: Add microsecond timer to log messages

---
 xbmc/utils/log.cpp | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/xbmc/utils/log.cpp b/xbmc/utils/log.cpp
index 4e51166..3c405ab 100644
--- a/xbmc/utils/log.cpp
+++ b/xbmc/utils/log.cpp
@@ -23,6 +23,7 @@
 #include "threads/SingleLock.h"
 #include "threads/Thread.h"
 #include "utils/StringUtils.h"
+#include "utils/TimeUtils.cpp"
 
 static const char* const levelNames[] =
 {"DEBUG", "INFO", "NOTICE", "WARNING", "ERROR", "SEVERE", "FATAL", "NONE"};
@@ -195,19 +196,31 @@ void CLog::PrintDebugString(const std::string& line)
 
 bool CLog::WriteLogString(int logLevel, const std::string& logString)
 {
+#if defined(TARGET_LINUX)
+  static const char* prefixFormat = "%02.2d:%02.2d:%02.2d %10.6f T:%" PRIu64" %7s: ";
+#else
   static const char* prefixFormat = "%02.2d:%02.2d:%02.2d T:%" PRIu64" %7s: ";
-
+#endif
   std::string strData(logString);
   /* fixup newline alignment, number of spaces should equal prefix length */
   StringUtils::Replace(strData, "\n", "\n                                            ");
 
   int hour, minute, second;
   s_globals.m_platform.GetCurrentLocalTime(hour, minute, second);
-  
+
+#if defined(TARGET_LINUX)
+  struct timespec now;
+  clock_gettime(CLOCK_MONOTONIC, &now);
+  float Now = now.tv_sec + now.tv_nsec * 1e-9;
+#endif
+
   strData = StringUtils::Format(prefixFormat,
                                   hour,
                                   minute,
                                   second,
+#if defined(TARGET_LINUX)
+                                  Now,
+#endif
                                   (uint64_t)CThread::GetCurrentThreadId(),
                                   levelNames[logLevel]) + strData;
 

From 75e494302ff1480f5aa2a0347e384116086a05be Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sat, 8 Mar 2014 15:36:06 +0000
Subject: [PATCH 12/88] [hifiberry] Hack: force it to be recognised as IEC958
 capable to enable passthrough options

---
 xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp
index a464b4b..7eba389 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp
@@ -1296,6 +1296,10 @@ void CAESinkALSA::EnumerateDevice(AEDeviceInfoList &list, const std::string &dev
     if (snd_card_get_name(cardNr, &cardName) == 0)
       info.m_displayName = cardName;
 
+    // hack: hifiberry digi doesn't correctly report as iec958 device. Needs fixing in kernel driver
+    if (info.m_displayName == "snd_rpi_hifiberry_digi")
+      info.m_deviceType = AE_DEVTYPE_IEC958;
+
     if (info.m_deviceType == AE_DEVTYPE_HDMI && info.m_displayName.size() > 5 &&
         info.m_displayName.substr(info.m_displayName.size()-5) == " HDMI")
     {

From a75bc8435a2d56c0e5e63b64f1f663b650e17768 Mon Sep 17 00:00:00 2001
From: Ben Avison <bavison@riscosopen.org>
Date: Wed, 11 Dec 2013 17:21:54 +0000
Subject: [PATCH 13/88] Move the reference-counting of Begin and End calls from
 DX and GL source files into GUIFontTTF.cpp.

---
 xbmc/guilib/GUIFontTTF.cpp   |  21 ++++++
 xbmc/guilib/GUIFontTTF.h     |   6 +-
 xbmc/guilib/GUIFontTTFDX.cpp |  79 ++++++++++------------
 xbmc/guilib/GUIFontTTFDX.h   |   4 +-
 xbmc/guilib/GUIFontTTFGL.cpp | 154 ++++++++++++++++++++-----------------------
 xbmc/guilib/GUIFontTTFGL.h   |   4 +-
 6 files changed, 135 insertions(+), 133 deletions(-)

diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp
index 4e6fb67..b1d7452 100644
--- a/xbmc/guilib/GUIFontTTF.cpp
+++ b/xbmc/guilib/GUIFontTTF.cpp
@@ -333,6 +333,27 @@ bool CGUIFontTTFBase::Load(const CStdString& strFilename, float height, float as
   return true;
 }
 
+void CGUIFontTTFBase::Begin()
+{
+  if (m_nestedBeginCount == 0 && m_texture != NULL && FirstBegin())
+  {
+    m_vertex_count = 0;
+  }
+  // Keep track of the nested begin/end calls.
+  m_nestedBeginCount++;
+}
+
+void CGUIFontTTFBase::End()
+{
+  if (m_nestedBeginCount == 0)
+    return;
+
+  if (--m_nestedBeginCount > 0)
+    return;
+
+  LastEnd();
+}
+
 void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors, const vecText &text, uint32_t alignment, float maxPixelWidth, bool scrolling)
 {
   Begin();
diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h
index 1714ed7..cd35d0f 100644
--- a/xbmc/guilib/GUIFontTTF.h
+++ b/xbmc/guilib/GUIFontTTF.h
@@ -79,8 +79,8 @@ class CGUIFontTTFBase
 
   bool Load(const CStdString& strFilename, float height = 20.0f, float aspect = 1.0f, float lineSpacing = 1.0f, bool border = false);
 
-  virtual void Begin() = 0;
-  virtual void End() = 0;
+  void Begin();
+  void End();
 
   const CStdString& GetFileName() const { return m_strFileName; };
 
@@ -171,6 +171,8 @@ class CGUIFontTTFBase
   XUTILS::auto_buffer m_fontFileInMemory; // used only in some cases, see CFreeTypeLibrary::GetFont()
 
 private:
+  virtual bool FirstBegin() = 0;
+  virtual void LastEnd() = 0;
   CGUIFontTTFBase(const CGUIFontTTFBase&);
   CGUIFontTTFBase& operator=(const CGUIFontTTFBase&);
   int m_referenceCount;
diff --git a/xbmc/guilib/GUIFontTTFDX.cpp b/xbmc/guilib/GUIFontTTFDX.cpp
index e3eba24..2f90668 100644
--- a/xbmc/guilib/GUIFontTTFDX.cpp
+++ b/xbmc/guilib/GUIFontTTFDX.cpp
@@ -51,65 +51,56 @@ CGUIFontTTFDX::~CGUIFontTTFDX(void)
   free(m_index);
 }
 
-void CGUIFontTTFDX::Begin()
+bool CGUIFontTTFDX::FirstBegin()
 {
   LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
 
   if (pD3DDevice == NULL)
+  {
     CLog::Log(LOGERROR, __FUNCTION__" - failed to get Direct3D device");
+    return false;
+  }
 
-  if (m_nestedBeginCount == 0 && pD3DDevice != NULL && m_texture != NULL)
+  int unit = 0;
+  // just have to blit from our texture.
+  m_texture->BindToUnit(unit);
+  pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP, D3DTOP_SELECTARG1 ); // only use diffuse
+  pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
+  pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
+  pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+  pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
+  unit++;
+
+  if(g_Windowing.UseLimitedColor())
   {
-    int unit = 0;
-    // just have to blit from our texture.
-    m_texture->BindToUnit(unit);
-    pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP, D3DTOP_SELECTARG1 ); // only use diffuse
-    pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
-    pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
-    pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
-    pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
+    pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP  , D3DTOP_ADD );
+    pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG1, D3DTA_CURRENT) ;
+    pD3DDevice->SetRenderState( D3DRS_TEXTUREFACTOR, D3DCOLOR_RGBA(16,16,16,0) );
+    pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG2, D3DTA_TFACTOR );
     unit++;
-
-    if(g_Windowing.UseLimitedColor())
-    {
-      pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP  , D3DTOP_ADD );
-      pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG1, D3DTA_CURRENT) ;
-      pD3DDevice->SetRenderState( D3DRS_TEXTUREFACTOR, D3DCOLOR_RGBA(16,16,16,0) );
-      pD3DDevice->SetTextureStageState( unit, D3DTSS_COLORARG2, D3DTA_TFACTOR );
-      unit++;
-    }
-
-    // no other texture stages needed
-    pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP, D3DTOP_DISABLE);
-    pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
-
-    pD3DDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
-    pD3DDevice->SetRenderState( D3DRS_FOGENABLE, FALSE );
-    pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
-    pD3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
-    pD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
-    pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
-    pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
-    pD3DDevice->SetRenderState( D3DRS_LIGHTING, FALSE);
-
-    pD3DDevice->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);
-    m_vertex_count = 0;
   }
 
-  // Keep track of the nested begin/end calls.
-  m_nestedBeginCount++;
+  // no other texture stages needed
+  pD3DDevice->SetTextureStageState( unit, D3DTSS_COLOROP, D3DTOP_DISABLE);
+  pD3DDevice->SetTextureStageState( unit, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
+
+  pD3DDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
+  pD3DDevice->SetRenderState( D3DRS_FOGENABLE, FALSE );
+  pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
+  pD3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
+  pD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
+  pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
+  pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
+  pD3DDevice->SetRenderState( D3DRS_LIGHTING, FALSE);
+
+  pD3DDevice->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);
+  return true;
 }
 
-void CGUIFontTTFDX::End()
+void CGUIFontTTFDX::LastEnd()
 {
   LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
 
-  if (m_nestedBeginCount == 0)
-    return;
-
-  if (--m_nestedBeginCount > 0)
-    return;
-
   if (m_vertex_count == 0)
     return;
 
diff --git a/xbmc/guilib/GUIFontTTFDX.h b/xbmc/guilib/GUIFontTTFDX.h
index 0431085..17dfefe 100644
--- a/xbmc/guilib/GUIFontTTFDX.h
+++ b/xbmc/guilib/GUIFontTTFDX.h
@@ -41,8 +41,8 @@ class CGUIFontTTFDX : public CGUIFontTTFBase
   CGUIFontTTFDX(const CStdString& strFileName);
   virtual ~CGUIFontTTFDX(void);
 
-  virtual void Begin();
-  virtual void End();
+  virtual bool FirstBegin();
+  virtual void LastEnd();
 
 protected:
   virtual CBaseTexture* ReallocTexture(unsigned int& newHeight);
diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp
index 6a8291b..97853fd 100644
--- a/xbmc/guilib/GUIFontTTFGL.cpp
+++ b/xbmc/guilib/GUIFontTTFGL.cpp
@@ -53,108 +53,96 @@ CGUIFontTTFGL::~CGUIFontTTFGL(void)
 {
 }
 
-void CGUIFontTTFGL::Begin()
+bool CGUIFontTTFGL::FirstBegin()
 {
-  if (m_nestedBeginCount == 0 && m_texture != NULL)
+  if (m_textureStatus == TEXTURE_REALLOCATED)
   {
-    if (m_textureStatus == TEXTURE_REALLOCATED)
-    {
-      if (glIsTexture(m_nTexture))
-        g_TextureManager.ReleaseHwTexture(m_nTexture);
-      m_textureStatus = TEXTURE_VOID;
-    }
-    
-    if (m_textureStatus == TEXTURE_VOID)
-    {
-      // Have OpenGL generate a texture object handle for us
-      glGenTextures(1, (GLuint*) &m_nTexture);
+    if (glIsTexture(m_nTexture))
+      g_TextureManager.ReleaseHwTexture(m_nTexture);
+    m_textureStatus = TEXTURE_VOID;
+  }
 
-      // Bind the texture object
-      glBindTexture(GL_TEXTURE_2D, m_nTexture);
+  if (m_textureStatus == TEXTURE_VOID)
+  {
+    // Have OpenGL generate a texture object handle for us
+    glGenTextures(1, (GLuint*) &m_nTexture);
+
+    // Bind the texture object
+    glBindTexture(GL_TEXTURE_2D, m_nTexture);
 #ifdef HAS_GL
-      glEnable(GL_TEXTURE_2D);
+    glEnable(GL_TEXTURE_2D);
 #endif
-      // Set the texture's stretching properties
-      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    // Set the texture's stretching properties
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 
-      // Set the texture image -- THIS WORKS, so the pixels must be wrong.
-      glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, m_texture->GetWidth(), m_texture->GetHeight(), 0,
-                   GL_ALPHA, GL_UNSIGNED_BYTE, 0);
-      
-      VerifyGLState();
-      m_textureStatus = TEXTURE_UPDATED;
-    }
+    // Set the texture image -- THIS WORKS, so the pixels must be wrong.
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, m_texture->GetWidth(), m_texture->GetHeight(), 0,
+        GL_ALPHA, GL_UNSIGNED_BYTE, 0);
 
-    if (m_textureStatus == TEXTURE_UPDATED)
-    {
-      glBindTexture(GL_TEXTURE_2D, m_nTexture);
-      glTexSubImage2D(GL_TEXTURE_2D, 0, 0, m_updateY1, m_texture->GetWidth(), m_updateY2 - m_updateY1, GL_ALPHA, GL_UNSIGNED_BYTE,
-                      m_texture->GetPixels() + m_updateY1 * m_texture->GetPitch());
-      glDisable(GL_TEXTURE_2D);
-        
-      m_updateY1 = m_updateY2 = 0;
-      m_textureStatus = TEXTURE_READY;
-    }
+    VerifyGLState();
+    m_textureStatus = TEXTURE_UPDATED;
+  }
 
-    // Turn Blending On
-    glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE);
-    glEnable(GL_BLEND);
+  if (m_textureStatus == TEXTURE_UPDATED)
+  {
+    glBindTexture(GL_TEXTURE_2D, m_nTexture);
+    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, m_updateY1, m_texture->GetWidth(), m_updateY2 - m_updateY1, GL_ALPHA, GL_UNSIGNED_BYTE,
+        m_texture->GetPixels() + m_updateY1 * m_texture->GetPitch());
+    glDisable(GL_TEXTURE_2D);
+
+    m_updateY1 = m_updateY2 = 0;
+    m_textureStatus = TEXTURE_READY;
+  }
+
+  // Turn Blending On
+  glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE);
+  glEnable(GL_BLEND);
 #ifdef HAS_GL
-    glEnable(GL_TEXTURE_2D);
+  glEnable(GL_TEXTURE_2D);
 #endif
-    glBindTexture(GL_TEXTURE_2D, m_nTexture);
+  glBindTexture(GL_TEXTURE_2D, m_nTexture);
 
 #ifdef HAS_GL
-    glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE);
-    glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_REPLACE);
-    glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR);
-    glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
-    glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
-    glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE0);
-    glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
-    glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR);
-    glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
-    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-    VerifyGLState();
+  glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE);
+  glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_REPLACE);
+  glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR);
+  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+  glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+  glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE0);
+  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+  glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR);
+  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+  VerifyGLState();
+
+  if(g_Windowing.UseLimitedColor())
+  {
+    glActiveTexture(GL_TEXTURE1);
+    glBindTexture(GL_TEXTURE_2D, m_nTexture); // dummy bind
+    glEnable(GL_TEXTURE_2D);
 
-    if(g_Windowing.UseLimitedColor())
-    {
-      glActiveTexture(GL_TEXTURE1);
-      glBindTexture(GL_TEXTURE_2D, m_nTexture); // dummy bind
-      glEnable(GL_TEXTURE_2D);
-
-      const GLfloat rgba[4] = {16.0f / 255.0f, 16.0f / 255.0f, 16.0f / 255.0f, 0.0f};
-      glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE , GL_COMBINE);
-      glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, rgba);
-      glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB      , GL_ADD);
-      glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_RGB      , GL_PREVIOUS);
-      glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_RGB      , GL_CONSTANT);
-      glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB     , GL_SRC_COLOR);
-      glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB     , GL_SRC_COLOR);
-      glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA    , GL_REPLACE);
-      glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA    , GL_PREVIOUS);
-      VerifyGLState();
-    }
+    const GLfloat rgba[4] = {16.0f / 255.0f, 16.0f / 255.0f, 16.0f / 255.0f, 0.0f};
+    glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE , GL_COMBINE);
+    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, rgba);
+    glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB      , GL_ADD);
+    glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_RGB      , GL_PREVIOUS);
+    glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_RGB      , GL_CONSTANT);
+    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB     , GL_SRC_COLOR);
+    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB     , GL_SRC_COLOR);
+    glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA    , GL_REPLACE);
+    glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA    , GL_PREVIOUS);
+    VerifyGLState();
+  }
 
 #else
-    g_Windowing.EnableGUIShader(SM_FONTS);
+  g_Windowing.EnableGUIShader(SM_FONTS);
 #endif
-
-    m_vertex_count = 0;
-  }
-  // Keep track of the nested begin/end calls.
-  m_nestedBeginCount++;
+  return true;
 }
 
-void CGUIFontTTFGL::End()
+void CGUIFontTTFGL::LastEnd()
 {
-  if (m_nestedBeginCount == 0)
-    return;
-
-  if (--m_nestedBeginCount > 0)
-    return;
-
 #ifdef HAS_GL
   glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
 
diff --git a/xbmc/guilib/GUIFontTTFGL.h b/xbmc/guilib/GUIFontTTFGL.h
index c0bb53a..735fb3a 100644
--- a/xbmc/guilib/GUIFontTTFGL.h
+++ b/xbmc/guilib/GUIFontTTFGL.h
@@ -41,8 +41,8 @@ class CGUIFontTTFGL : public CGUIFontTTFBase
   CGUIFontTTFGL(const CStdString& strFileName);
   virtual ~CGUIFontTTFGL(void);
 
-  virtual void Begin();
-  virtual void End();
+  virtual bool FirstBegin();
+  virtual void LastEnd();
 
 protected:
   virtual CBaseTexture* ReallocTexture(unsigned int& newHeight);

From acb4a6b7e5dfdfc67b81419d2568a48a61a08d2d Mon Sep 17 00:00:00 2001
From: Ben Avison <bavison@riscosopen.org>
Date: Wed, 11 Dec 2013 18:47:54 +0000
Subject: [PATCH 14/88] Convert CGUIFontTTFBase::m_vertex to be managed as a
 std::vector.

Also retired CGUIFontTTFBase::m_vertex_count and
CGUIFontTTFBase::m_vertex_size because these can be derived from vector
member functions.
---
 xbmc/guilib/GUIFontTTF.cpp   | 29 +++++------------------------
 xbmc/guilib/GUIFontTTF.h     |  4 +---
 xbmc/guilib/GUIFontTTFDX.cpp | 12 ++++++------
 xbmc/guilib/GUIFontTTFGL.cpp | 12 ++++++------
 4 files changed, 18 insertions(+), 39 deletions(-)

diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp
index b1d7452..686dddf 100644
--- a/xbmc/guilib/GUIFontTTF.cpp
+++ b/xbmc/guilib/GUIFontTTF.cpp
@@ -160,8 +160,7 @@ CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName)
   m_maxChars = 0;
   m_nestedBeginCount = 0;
 
-  m_vertex_size   = 4*1024;
-  m_vertex        = (SVertex*)malloc(m_vertex_size * sizeof(SVertex));
+  m_vertex.reserve(4*1024);
 
   m_face = NULL;
   m_stroker = NULL;
@@ -176,7 +175,6 @@ CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName)
   m_textureScaleX = m_textureScaleY = 0.0;
   m_ellipsesWidth = m_height = 0.0f;
   m_color = 0;
-  m_vertex_count = 0;
   m_nTexture = 0;
 }
 
@@ -237,9 +235,7 @@ void CGUIFontTTFBase::Clear()
     g_freeTypeLibrary.ReleaseStroker(m_stroker);
   m_stroker = NULL;
 
-  free(m_vertex);
-  m_vertex = NULL;
-  m_vertex_count = 0;
+  m_vertex.clear();
 
   m_strFileName.clear();
   m_fontFileInMemory.clear();
@@ -337,7 +333,7 @@ void CGUIFontTTFBase::Begin()
 {
   if (m_nestedBeginCount == 0 && m_texture != NULL && FirstBegin())
   {
-    m_vertex_count = 0;
+    m_vertex.clear();
   }
   // Keep track of the nested begin/end calls.
   m_nestedBeginCount++;
@@ -770,22 +766,9 @@ void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *c
   float tt = texture.y1 * m_textureScaleY;
   float tb = texture.y2 * m_textureScaleY;
 
-  // grow the vertex buffer if required
-  if(m_vertex_count >= m_vertex_size)
-  {
-    m_vertex_size *= 2;
-    void* old      = m_vertex;
-    m_vertex       = (SVertex*)realloc(m_vertex, m_vertex_size * sizeof(SVertex));
-    if (!m_vertex)
-    {
-      free(old);
-      CLog::Log(LOGSEVERE, "%s: can't allocate %" PRIdS" bytes for texture", __FUNCTION__ , m_vertex_size * sizeof(SVertex));
-      return;
-    }
-  }
-
+  m_vertex.resize(m_vertex.size() + 4);
+  SVertex* v = &m_vertex[m_vertex.size() - 4];
   m_color = color;
-  SVertex* v = m_vertex + m_vertex_count;
 
   unsigned char r = GET_R(color)
               , g = GET_G(color)
@@ -852,8 +835,6 @@ void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *c
   v[3].y = y[2];
   v[3].z = z[2];
 #endif
-
-  m_vertex_count+=4;
 }
 
 // Oblique code - original taken from freetype2 (ftsynth.c)
diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h
index cd35d0f..5675725 100644
--- a/xbmc/guilib/GUIFontTTF.h
+++ b/xbmc/guilib/GUIFontTTF.h
@@ -158,9 +158,7 @@ class CGUIFontTTFBase
 
   unsigned int m_nTexture;
 
-  SVertex* m_vertex;
-  int      m_vertex_count;
-  int      m_vertex_size;
+  std::vector<SVertex> m_vertex;
 
   float    m_textureScaleX;
   float    m_textureScaleY;
diff --git a/xbmc/guilib/GUIFontTTFDX.cpp b/xbmc/guilib/GUIFontTTFDX.cpp
index 2f90668..6ef8984 100644
--- a/xbmc/guilib/GUIFontTTFDX.cpp
+++ b/xbmc/guilib/GUIFontTTFDX.cpp
@@ -101,17 +101,17 @@ void CGUIFontTTFDX::LastEnd()
 {
   LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
 
-  if (m_vertex_count == 0)
+  if (m_vertex.size() == 0)
     return;
 
-  unsigned index_size = m_vertex_size * 6 / 4;
+  unsigned index_size = m_vertex.capacity() * 6 / 4;
   if(m_index_size < index_size)
   {
     uint16_t* id  = (uint16_t*)calloc(index_size, sizeof(uint16_t));
     if(id == NULL)
       return;
 
-    for(int i = 0, b = 0; i < m_vertex_size; i += 4, b += 6)
+    for(int i = 0, b = 0; i < m_vertex.capacity(); i += 4, b += 6)
     {
       id[b+0] = i + 0;
       id[b+1] = i + 1;
@@ -140,11 +140,11 @@ void CGUIFontTTFDX::LastEnd()
 
   pD3DDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST
                                     , 0
-                                    , m_vertex_count
-                                    , m_vertex_count / 2
+                                    , m_vertex.size()
+                                    , m_vertex.size() / 2
                                     , m_index
                                     , D3DFMT_INDEX16
-                                    , m_vertex
+                                    , &m_vertex[0]
                                     , sizeof(SVertex));
   pD3DDevice->SetTransform(D3DTS_WORLD, &orig);
 
diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp
index 97853fd..b76c6a5 100644
--- a/xbmc/guilib/GUIFontTTFGL.cpp
+++ b/xbmc/guilib/GUIFontTTFGL.cpp
@@ -146,13 +146,13 @@ void CGUIFontTTFGL::LastEnd()
 #ifdef HAS_GL
   glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
 
-  glColorPointer   (4, GL_UNSIGNED_BYTE, sizeof(SVertex), (char*)m_vertex + offsetof(SVertex, r));
-  glVertexPointer  (3, GL_FLOAT        , sizeof(SVertex), (char*)m_vertex + offsetof(SVertex, x));
-  glTexCoordPointer(2, GL_FLOAT        , sizeof(SVertex), (char*)m_vertex + offsetof(SVertex, u));
+  glColorPointer   (4, GL_UNSIGNED_BYTE, sizeof(SVertex), (char*)&m_vertex[0] + offsetof(SVertex, r));
+  glVertexPointer  (3, GL_FLOAT        , sizeof(SVertex), (char*)&m_vertex[0] + offsetof(SVertex, x));
+  glTexCoordPointer(2, GL_FLOAT        , sizeof(SVertex), (char*)&m_vertex[0] + offsetof(SVertex, u));
   glEnableClientState(GL_COLOR_ARRAY);
   glEnableClientState(GL_VERTEX_ARRAY);
   glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-  glDrawArrays(GL_QUADS, 0, m_vertex_count);
+  glDrawArrays(GL_QUADS, 0, m_vertex.size());
   glPopClientAttrib();
 
   glActiveTexture(GL_TEXTURE1);
@@ -168,10 +168,10 @@ void CGUIFontTTFGL::LastEnd()
   GLint tex0Loc = g_Windowing.GUIShaderGetCoord0();
 
   // stack object until VBOs will be used
-  std::vector<SVertex> vecVertices( 6 * (m_vertex_count / 4) );
+  std::vector<SVertex> vecVertices( 6 * (m_vertex.size() / 4) );
   SVertex *vertices = &vecVertices[0];
 
-  for (int i=0; i<m_vertex_count; i+=4)
+  for (size_t i=0; i<m_vertex.size(); i+=4)
   {
     *vertices++ = m_vertex[i];
     *vertices++ = m_vertex[i+1];

From ec7796db5c9145f195f7c75a8ada28bc9857a29b Mon Sep 17 00:00:00 2001
From: Ben Avison <bavison@riscosopen.org>
Date: Mon, 16 Dec 2013 18:58:12 +0000
Subject: [PATCH 15/88] CGUIFontTTFBase::RenderCharacter can now append to
 arbitrary vectors of vertices rather than only CGUIFontTTFBase::m_vertex

---
 xbmc/guilib/GUIFontTTF.cpp | 12 +++++++-----
 xbmc/guilib/GUIFontTTF.h   |  2 +-
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp
index 686dddf..e6eb3f2 100644
--- a/xbmc/guilib/GUIFontTTF.cpp
+++ b/xbmc/guilib/GUIFontTTF.cpp
@@ -354,6 +354,8 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
 {
   Begin();
 
+  std::vector<SVertex> &vertices = m_vertex;
+
   // save the origin, which is scaled separately
   m_originX = x;
   m_originY = y;
@@ -434,7 +436,7 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
 
         for (int i = 0; i < 3; i++)
         {
-          RenderCharacter(startX + cursorX, startY, period, color, !scrolling);
+          RenderCharacter(startX + cursorX, startY, period, color, !scrolling, vertices);
           cursorX += period->advance;
         }
         break;
@@ -443,7 +445,7 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
     else if (maxPixelWidth > 0 && cursorX > maxPixelWidth)
       break;  // exceeded max allowed width - stop rendering
 
-    RenderCharacter(startX + cursorX, startY, ch, color, !scrolling);
+    RenderCharacter(startX + cursorX, startY, ch, color, !scrolling, vertices);
     if ( alignment & XBFONT_JUSTIFIED )
     {
       if ((*pos & 0xffff) == L' ')
@@ -700,7 +702,7 @@ bool CGUIFontTTFBase::CacheCharacter(wchar_t letter, uint32_t style, Character *
   return true;
 }
 
-void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *ch, color_t color, bool roundX)
+void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *ch, color_t color, bool roundX, std::vector<SVertex> &vertices)
 {
   // actual image width isn't same as the character width as that is
   // just baseline width and height should include the descent
@@ -766,8 +768,8 @@ void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *c
   float tt = texture.y1 * m_textureScaleY;
   float tb = texture.y2 * m_textureScaleY;
 
-  m_vertex.resize(m_vertex.size() + 4);
-  SVertex* v = &m_vertex[m_vertex.size() - 4];
+  vertices.resize(vertices.size() + 4);
+  SVertex* v = &vertices[vertices.size() - 4];
   m_color = color;
 
   unsigned char r = GET_R(color)
diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h
index 5675725..a5d44f4 100644
--- a/xbmc/guilib/GUIFontTTF.h
+++ b/xbmc/guilib/GUIFontTTF.h
@@ -111,7 +111,7 @@ class CGUIFontTTFBase
   // Stuff for pre-rendering for speed
   inline Character *GetCharacter(character_t letter);
   bool CacheCharacter(wchar_t letter, uint32_t style, Character *ch);
-  void RenderCharacter(float posX, float posY, const Character *ch, color_t color, bool roundX);
+  void RenderCharacter(float posX, float posY, const Character *ch, color_t color, bool roundX, std::vector<SVertex> &vertices);
   void ClearCharacterCache();
 
   virtual CBaseTexture* ReallocTexture(unsigned int& newHeight) = 0;

From 3920823d71730917234d3c4391d1c018c11d09df Mon Sep 17 00:00:00 2001
From: Ben Avison <bavison@riscosopen.org>
Date: Wed, 15 Jan 2014 17:18:38 +0000
Subject: [PATCH 16/88] Add a cache of font glyph bounding box vertices.

This is implemented as a template because ultimately we will key on different
parameters and store values of different types, depending upon whether we
have a GLES or non-GLES backend, and for GLES, whether or not the currently
applicable transformation matrices permit the use of hardware clipping.
---
 XBMC.xcodeproj/project.pbxproj             |  10 ++
 project/VS2010Express/XBMC.vcxproj         |   2 +
 project/VS2010Express/XBMC.vcxproj.filters |   6 +
 xbmc/guilib/GUIFontCache.cpp               | 105 ++++++++++++++
 xbmc/guilib/GUIFontCache.h                 | 217 +++++++++++++++++++++++++++++
 xbmc/guilib/GUIFontTTF.cpp                 | 181 +++++++++++++-----------
 xbmc/guilib/GUIFontTTF.h                   |   5 +
 xbmc/guilib/GUIFontTTFGL.cpp               |   1 +
 xbmc/guilib/GraphicContext.h               |   1 +
 xbmc/guilib/Makefile.in                    |   1 +
 xbmc/guilib/TransformMatrix.h              |  11 ++
 11 files changed, 456 insertions(+), 84 deletions(-)
 create mode 100644 xbmc/guilib/GUIFontCache.cpp
 create mode 100644 xbmc/guilib/GUIFontCache.h

diff --git a/XBMC.xcodeproj/project.pbxproj b/XBMC.xcodeproj/project.pbxproj
index 415d73b..1a5dc91 100644
--- a/XBMC.xcodeproj/project.pbxproj
+++ b/XBMC.xcodeproj/project.pbxproj
@@ -168,6 +168,9 @@
 		1D638128161E211E003603ED /* PeripheralImon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D638126161E211E003603ED /* PeripheralImon.cpp */; };
 		1DAFDB7C16DFDCA7007F8C68 /* PeripheralBusCEC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DAFDB7A16DFDCA7007F8C68 /* PeripheralBusCEC.cpp */; };
 		1DE0443515828F4B005DDB4D /* Exception.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1DE0443315828F4B005DDB4D /* Exception.cpp */; };
+		2F4564D51970129A00396109 /* GUIFontCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2F4564D31970129A00396109 /* GUIFontCache.cpp */; };
+		2F4564D61970129A00396109 /* GUIFontCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2F4564D31970129A00396109 /* GUIFontCache.cpp */; };
+		2F4564D71970129A00396109 /* GUIFontCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2F4564D31970129A00396109 /* GUIFontCache.cpp */; };
 		32C631281423A90F00F18420 /* JpegIO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 32C631261423A90F00F18420 /* JpegIO.cpp */; };
 		36A9443D15821E2800727135 /* DatabaseUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A9443B15821E2800727135 /* DatabaseUtils.cpp */; };
 		36A9444115821E7C00727135 /* SortUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A9443F15821E7C00727135 /* SortUtils.cpp */; };
@@ -4017,6 +4020,8 @@
 		1DAFDB7B16DFDCA7007F8C68 /* PeripheralBusCEC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PeripheralBusCEC.h; sourceTree = "<group>"; };
 		1DE0443315828F4B005DDB4D /* Exception.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Exception.cpp; path = commons/Exception.cpp; sourceTree = "<group>"; };
 		1DE0443415828F4B005DDB4D /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Exception.h; path = commons/Exception.h; sourceTree = "<group>"; };
+		2F4564D31970129A00396109 /* GUIFontCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIFontCache.cpp; sourceTree = "<group>"; };
+		2F4564D41970129A00396109 /* GUIFontCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIFontCache.h; sourceTree = "<group>"; };
 		32C631261423A90F00F18420 /* JpegIO.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JpegIO.cpp; sourceTree = "<group>"; };
 		32C631271423A90F00F18420 /* JpegIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JpegIO.h; sourceTree = "<group>"; };
 		36A9443B15821E2800727135 /* DatabaseUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DatabaseUtils.cpp; sourceTree = "<group>"; };
@@ -6529,6 +6534,8 @@
 				18B7C7101294222D009E7A26 /* GUIFixedListContainer.h */,
 				18B7C76B1294222E009E7A26 /* GUIFont.cpp */,
 				18B7C7111294222D009E7A26 /* GUIFont.h */,
+				2F4564D31970129A00396109 /* GUIFontCache.cpp */,
+				2F4564D41970129A00396109 /* GUIFontCache.h */,
 				18B7C76C1294222E009E7A26 /* GUIFontManager.cpp */,
 				18B7C7121294222D009E7A26 /* GUIFontManager.h */,
 				18B7C76D1294222E009E7A26 /* GUIFontTTF.cpp */,
@@ -11024,6 +11031,7 @@
 				7C5608C70F1754930056433A /* ExternalPlayer.cpp in Sources */,
 				F584E12E0F257C5100DB26A5 /* HTTPDirectory.cpp in Sources */,
 				F54C51D20F1E783200D46E3C /* GUIDialogKaraokeSongSelector.cpp in Sources */,
+				2F4564D51970129A00396109 /* GUIFontCache.cpp in Sources */,
 				F54C51D50F1E784800D46E3C /* karaokelyricscdg.cpp in Sources */,
 				F54C51D80F1E785700D46E3C /* karaokelyrics.cpp in Sources */,
 				F54C51E50F1E787700D46E3C /* karaokelyricstextkar.cpp in Sources */,
@@ -12695,6 +12703,7 @@
 				DFF0F45B17528350002DA3A4 /* Control.cpp in Sources */,
 				DFF0F45C17528350002DA3A4 /* Dialog.cpp in Sources */,
 				DFF0F45D17528350002DA3A4 /* File.cpp in Sources */,
+				2F4564D71970129A00396109 /* GUIFontCache.cpp in Sources */,
 				DFF0F45E17528350002DA3A4 /* InfoTagMusic.cpp in Sources */,
 				DFF0F45F17528350002DA3A4 /* InfoTagVideo.cpp in Sources */,
 				DFF0F46017528350002DA3A4 /* Keyboard.cpp in Sources */,
@@ -13494,6 +13503,7 @@
 				E499131D174E5DAD00741B6D /* GUIVisualisationControl.cpp in Sources */,
 				E499131E174E5DAD00741B6D /* GUIWindow.cpp in Sources */,
 				E499131F174E5DAD00741B6D /* GUIWindowManager.cpp in Sources */,
+				2F4564D61970129A00396109 /* GUIFontCache.cpp in Sources */,
 				E4991320174E5DAD00741B6D /* GUIWrappingListContainer.cpp in Sources */,
 				E4991321174E5DAD00741B6D /* imagefactory.cpp in Sources */,
 				E4991322174E5DAD00741B6D /* IWindowManagerCallback.cpp in Sources */,
diff --git a/project/VS2010Express/XBMC.vcxproj b/project/VS2010Express/XBMC.vcxproj
index ed5ad2a..bc1db7e 100644
--- a/project/VS2010Express/XBMC.vcxproj
+++ b/project/VS2010Express/XBMC.vcxproj
@@ -427,6 +427,7 @@
     <ClCompile Include="..\..\xbmc\guilib\GUIFadeLabelControl.cpp" />
     <ClCompile Include="..\..\xbmc\guilib\GUIFixedListContainer.cpp" />
     <ClCompile Include="..\..\xbmc\guilib\GUIFont.cpp" />
+    <ClCompile Include="..\..\xbmc\guilib\GUIFontCache.cpp" />
     <ClCompile Include="..\..\xbmc\guilib\GUIFontManager.cpp" />
     <ClCompile Include="..\..\xbmc\guilib\GUIFontTTF.cpp" />
     <ClCompile Include="..\..\xbmc\guilib\GUIFontTTFDX.cpp" />
@@ -1752,6 +1753,7 @@
     <ClInclude Include="..\..\xbmc\guilib\GUIFadeLabelControl.h" />
     <ClInclude Include="..\..\xbmc\guilib\GUIFixedListContainer.h" />
     <ClInclude Include="..\..\xbmc\guilib\GUIFont.h" />
+    <ClInclude Include="..\..\xbmc\guilib\GUIFontCache.h" />
     <ClInclude Include="..\..\xbmc\guilib\GUIFontManager.h" />
     <ClInclude Include="..\..\xbmc\guilib\GUIFontTTF.h" />
     <ClInclude Include="..\..\xbmc\guilib\GUIFontTTFDX.h" />
diff --git a/project/VS2010Express/XBMC.vcxproj.filters b/project/VS2010Express/XBMC.vcxproj.filters
index ca16ec8..a79aec4 100644
--- a/project/VS2010Express/XBMC.vcxproj.filters
+++ b/project/VS2010Express/XBMC.vcxproj.filters
@@ -991,6 +991,9 @@
     <ClCompile Include="..\..\xbmc\guilib\GUIFont.cpp">
       <Filter>guilib</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\xbmc\guilib\GUIFontCache.cpp">
+      <Filter>guilib</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\xbmc\guilib\GUIFontManager.cpp">
       <Filter>guilib</Filter>
     </ClCompile>
@@ -3891,6 +3894,9 @@
     <ClInclude Include="..\..\xbmc\guilib\GUIFont.h">
       <Filter>guilib</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\xbmc\guilib\GUIFontCache.h">
+      <Filter>guilib</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\xbmc\guilib\GUIFontManager.h">
       <Filter>guilib</Filter>
     </ClInclude>
diff --git a/xbmc/guilib/GUIFontCache.cpp b/xbmc/guilib/GUIFontCache.cpp
new file mode 100644
index 0000000..2c72f9c
--- /dev/null
+++ b/xbmc/guilib/GUIFontCache.cpp
@@ -0,0 +1,105 @@
+/*
+ *      Copyright (C) 2005-2013 Team XBMC
+ *      http://xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <vector>
+#include "utils/StdString.h" // required by GUIFontTTF.h
+#include "GUIFontTTF.h"
+#include "GraphicContext.h"
+
+template<class Position, class Value>
+void CGUIFontCacheEntry<Position, Value>::Reassign::operator()(CGUIFontCacheEntry<Position, Value> &entry)
+{
+  entry.m_key.m_pos = m_key.m_pos;
+  entry.m_key.m_colors.assign(m_key.m_colors.begin(), m_key.m_colors.end());
+  entry.m_key.m_text.assign(m_key.m_text.begin(), m_key.m_text.end());
+  entry.m_key.m_alignment = m_key.m_alignment;
+  entry.m_key.m_maxPixelWidth = m_key.m_maxPixelWidth;
+  entry.m_key.m_scrolling = m_key.m_scrolling;
+  entry.m_matrix = m_key.m_matrix;
+  entry.m_key.m_scaleX = m_key.m_scaleX;
+  entry.m_key.m_scaleY = m_key.m_scaleY;
+
+  entry.m_lastUsedMillis = m_nowMillis;
+  entry.m_value.clear();
+}
+
+template<class Position, class Value>
+CGUIFontCacheEntry<Position, Value>::~CGUIFontCacheEntry()
+{
+  delete &m_key.m_colors;
+  delete &m_key.m_text;
+  m_value.clear();
+}
+
+template<class Position, class Value>
+Value &CGUIFontCache<Position, Value>::Lookup(Position &pos,
+                                              const vecColors &colors, const vecText &text,
+                                              uint32_t alignment, float maxPixelWidth,
+                                              bool scrolling,
+                                              unsigned int nowMillis, bool &dirtyCache)
+{
+  const CGUIFontCacheKey<Position> key(pos,
+                                       const_cast<vecColors &>(colors), const_cast<vecText &>(text),
+                                       alignment, maxPixelWidth,
+                                       scrolling, g_graphicsContext.GetGUIMatrix(),
+                                       g_graphicsContext.GetGUIScaleX(), g_graphicsContext.GetGUIScaleY());
+  EntryHashIterator i = m_list.template get<Hash>().find(key);
+  if (i == m_list.template get<Hash>().end())
+  {
+    /* Cache miss */
+    EntryAgeIterator oldest = m_list.template get<Age>().begin();
+    if (!m_list.template get<Age>().empty() && nowMillis - oldest->m_lastUsedMillis > FONT_CACHE_TIME_LIMIT)
+    {
+      /* The oldest existing entry is old enough to expire and reuse */
+      m_list.template get<Hash>().modify(m_list.template project<Hash>(oldest), typename CGUIFontCacheEntry<Position, Value>::Reassign(key, nowMillis));
+      m_list.template get<Age>().relocate(m_list.template get<Age>().end(), oldest);
+    }
+    else
+    {
+      /* We need a new entry instead */
+      /* Yes, this causes the creation an destruction of a temporary entry, but
+       * this code ought to only be used infrequently, when the cache needs to grow */
+      m_list.template get<Age>().push_back(CGUIFontCacheEntry<Position, Value>(*this, key, nowMillis));
+    }
+    dirtyCache = true;
+    return (--m_list.template get<Age>().end())->m_value;
+  }
+  else
+  {
+    /* Cache hit */
+    /* Update time in entry and move to the back of the list */
+    i->m_lastUsedMillis = nowMillis;
+    m_list.template get<Age>().relocate(m_list.template get<Age>().end(), m_list.template project<Age>(i));
+    dirtyCache = false;
+    return i->m_value;
+  }
+}
+
+template<class Position, class Value>
+void CGUIFontCache<Position, Value>::Flush()
+{
+  m_list.template get<Age>().clear();
+}
+
+template void CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::Reassign::operator()(CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue> &entry);
+template CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::~CGUIFontCacheEntry();
+template CGUIFontCacheStaticValue &CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::Lookup(CGUIFontCacheStaticPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &);
+template void CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::Flush();
diff --git a/xbmc/guilib/GUIFontCache.h b/xbmc/guilib/GUIFontCache.h
new file mode 100644
index 0000000..ef65845
--- /dev/null
+++ b/xbmc/guilib/GUIFontCache.h
@@ -0,0 +1,217 @@
+/*!
+\file GUIFontCache.h
+\brief
+*/
+
+#ifndef CGUILIB_GUIFONTCACHE_H
+#define CGUILIB_GUIFONTCACHE_H
+#pragma once
+
+/*
+ *      Copyright (C) 2005-2013 Team XBMC
+ *      http://xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <cstddef>
+#include <cstring>
+#include <stdint.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "boost/multi_index_container.hpp"
+#include "boost/multi_index/sequenced_index.hpp"
+#include "boost/multi_index/hashed_index.hpp"
+#include "boost/multi_index/member.hpp"
+
+#include "TransformMatrix.h"
+
+using namespace boost::multi_index;
+
+#define FONT_CACHE_TIME_LIMIT (1000)
+
+template<class Position, class Value> class CGUIFontCache;
+class CGUIFontTTFBase;
+
+template<class Position>
+struct CGUIFontCacheKey
+{
+  Position m_pos;
+  vecColors &m_colors;
+  vecText &m_text;
+  uint32_t m_alignment;
+  float m_maxPixelWidth;
+  bool m_scrolling;
+  const TransformMatrix &m_matrix;
+  float m_scaleX;
+  float m_scaleY;
+
+  CGUIFontCacheKey(Position pos,
+                   vecColors &colors, vecText &text,
+                   uint32_t alignment, float maxPixelWidth,
+                   bool scrolling, const TransformMatrix &matrix,
+                   float scaleX, float scaleY) :
+    m_pos(pos),
+    m_colors(colors), m_text(text),
+    m_alignment(alignment), m_maxPixelWidth(maxPixelWidth),
+    m_scrolling(scrolling), m_matrix(matrix),
+    m_scaleX(scaleX), m_scaleY(scaleY)
+  {}
+};
+
+template<class Position, class Value>
+struct CGUIFontCacheEntry
+{
+  const CGUIFontCache<Position, Value> &m_cache;
+  CGUIFontCacheKey<Position> m_key;
+  TransformMatrix m_matrix;
+
+  /* These need to be declared as mutable to get round the fact that only
+   * const iterators are available. These fields do not affect comparison or
+   * hash functors, so from the container's point of view, they are mutable. */
+  mutable unsigned int m_lastUsedMillis;
+  mutable Value m_value;
+
+  CGUIFontCacheEntry(const CGUIFontCache<Position, Value> &cache, const CGUIFontCacheKey<Position> &key, unsigned int nowMillis) :
+    m_cache(cache),
+    m_key(key.m_pos,
+          *new vecColors, *new vecText,
+          key.m_alignment, key.m_maxPixelWidth,
+          key.m_scrolling, m_matrix,
+          key.m_scaleX, key.m_scaleY),
+    m_lastUsedMillis(nowMillis)
+  {
+    m_key.m_colors.assign(key.m_colors.begin(), key.m_colors.end());
+    m_key.m_text.assign(key.m_text.begin(), key.m_text.end());
+    m_matrix = key.m_matrix;
+  }
+
+  CGUIFontCacheEntry(const CGUIFontCacheEntry &other) :
+    m_cache(other.m_cache),
+    m_key(other.m_key.m_pos,
+          *new vecColors, *new vecText,
+          other.m_key.m_alignment, other.m_key.m_maxPixelWidth,
+          other.m_key.m_scrolling, m_matrix,
+          other.m_key.m_scaleX, other.m_key.m_scaleY),
+    m_lastUsedMillis(other.m_lastUsedMillis),
+    m_value(other.m_value)
+  {
+    m_key.m_colors.assign(other.m_key.m_colors.begin(), other.m_key.m_colors.end());
+    m_key.m_text.assign(other.m_key.m_text.begin(), other.m_key.m_text.end());
+    m_matrix = other.m_key.m_matrix;
+  }
+
+  struct Reassign
+  {
+    Reassign(const CGUIFontCacheKey<Position> &key, unsigned int nowMillis) : m_key(key), m_nowMillis(nowMillis) {}
+    void operator()(CGUIFontCacheEntry &entry);
+  private:
+    const CGUIFontCacheKey<Position> &m_key;
+    unsigned int m_nowMillis;
+  };
+
+  ~CGUIFontCacheEntry();
+};
+
+template<class Position>
+struct CGUIFontCacheHash
+{
+  size_t operator()(const CGUIFontCacheKey<Position> &key) const
+  {
+    /* Not much effort has gone into choosing this hash function */
+    size_t hash = 0, i;
+    for (i = 0; i < 3 && i < key.m_text.size(); ++i)
+      hash += key.m_text[i];
+    if (key.m_colors.size())
+      hash += key.m_colors[0];
+    hash += MatrixHashContribution(key);
+    return hash;
+  }
+};
+
+template<class Position>
+struct CGUIFontCacheKeysMatch
+{
+  bool operator()(const CGUIFontCacheKey<Position> &a, const CGUIFontCacheKey<Position> &b) const
+  {
+    return a.m_text == b.m_text &&
+           a.m_colors == b.m_colors &&
+           a.m_alignment == b.m_alignment &&
+           a.m_scrolling == b.m_scrolling &&
+           a.m_maxPixelWidth == b.m_maxPixelWidth &&
+           Match(a.m_pos, a.m_matrix, b.m_pos, b.m_matrix, a.m_scrolling) &&
+           a.m_scaleX == b.m_scaleX &&
+           a.m_scaleY == b.m_scaleY;
+  }
+};
+
+template<class Position, class Value>
+class CGUIFontCache
+{
+  /* Empty structs used as tags to identify indexes */
+  struct Age {};
+  struct Hash {};
+
+  typedef multi_index_container<
+      CGUIFontCacheEntry<Position, Value>,
+      indexed_by<
+          sequenced<tag<Age> >,
+          hashed_unique<tag<Hash>, member<CGUIFontCacheEntry<Position, Value>, CGUIFontCacheKey<Position>, &CGUIFontCacheEntry<Position, Value>::m_key>, CGUIFontCacheHash<Position>, CGUIFontCacheKeysMatch<Position> >
+      >
+  > EntryList;
+
+  typedef typename EntryList::template index<Age>::type::iterator EntryAgeIterator;
+  typedef typename EntryList::template index<Hash>::type::iterator EntryHashIterator;
+
+  EntryList m_list;
+
+public:
+  const CGUIFontTTFBase &m_font;
+
+  CGUIFontCache(CGUIFontTTFBase &font) : m_font(font) {}
+  Value &Lookup(Position &pos,
+                const vecColors &colors, const vecText &text,
+                uint32_t alignment, float maxPixelWidth,
+                bool scrolling,
+                unsigned int nowMillis, bool &dirtyCache);
+  void Flush();
+};
+
+struct CGUIFontCacheStaticPosition
+{
+  float m_x;
+  float m_y;
+  CGUIFontCacheStaticPosition(float x, float y) : m_x(x), m_y(y) {}
+};
+
+typedef std::vector<SVertex> CGUIFontCacheStaticValue;
+
+inline bool Match(const CGUIFontCacheStaticPosition &a, const TransformMatrix &a_m,
+                  const CGUIFontCacheStaticPosition &b, const TransformMatrix &b_m,
+                  bool scrolling)
+{
+  return a.m_x == b.m_x && a.m_y == b.m_y && a_m == b_m;
+}
+
+inline float MatrixHashContribution(const CGUIFontCacheKey<CGUIFontCacheStaticPosition> &a)
+{
+  /* Ensure horizontally translated versions end up in different buckets */
+  return a.m_matrix.m[0][3];
+}
+
+#endif
diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp
index e6eb3f2..6a40e9c 100644
--- a/xbmc/guilib/GUIFontTTF.cpp
+++ b/xbmc/guilib/GUIFontTTF.cpp
@@ -29,6 +29,7 @@
 #include "windowing/WindowingFactory.h"
 #include "URL.h"
 #include "filesystem/File.h"
+#include "threads/SystemClock.h"
 
 #include <math.h>
 
@@ -153,7 +154,7 @@ class CFreeTypeLibrary
 XBMC_GLOBAL_REF(CFreeTypeLibrary, g_freeTypeLibrary); // our freetype library
 #define g_freeTypeLibrary XBMC_GLOBAL_USE(CFreeTypeLibrary)
 
-CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName)
+CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName) : m_staticCache(*this)
 {
   m_texture = NULL;
   m_char = NULL;
@@ -354,108 +355,120 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
 {
   Begin();
 
-  std::vector<SVertex> &vertices = m_vertex;
-
-  // save the origin, which is scaled separately
-  m_originX = x;
-  m_originY = y;
-
-  // Check if we will really need to truncate or justify the text
-  if ( alignment & XBFONT_TRUNCATED )
+  bool dirtyCache;
+  CGUIFontCacheStaticPosition staticPos(x, y);
+  std::vector<SVertex> &vertices = m_staticCache.Lookup(staticPos,
+                                                        colors, text,
+                                                        alignment, maxPixelWidth,
+                                                        scrolling,
+                                                        XbmcThreads::SystemClockMillis(),
+                                                        dirtyCache);
+  if (dirtyCache)
   {
-    if ( maxPixelWidth <= 0.0f || GetTextWidthInternal(text.begin(), text.end()) <= maxPixelWidth)
-      alignment &= ~XBFONT_TRUNCATED;
-  }
-  else if ( alignment & XBFONT_JUSTIFIED )
-  {
-    if ( maxPixelWidth <= 0.0f )
-      alignment &= ~XBFONT_JUSTIFIED;
-  }
+    // save the origin, which is scaled separately
+    m_originX = x;
+    m_originY = y;
 
-  // calculate sizing information
-  float startX = 0;
-  float startY = (alignment & XBFONT_CENTER_Y) ? -0.5f*m_cellHeight : 0;  // vertical centering
+    // Check if we will really need to truncate or justify the text
+    if ( alignment & XBFONT_TRUNCATED )
+    {
+      if ( maxPixelWidth <= 0.0f || GetTextWidthInternal(text.begin(), text.end()) <= maxPixelWidth)
+        alignment &= ~XBFONT_TRUNCATED;
+    }
+    else if ( alignment & XBFONT_JUSTIFIED )
+    {
+      if ( maxPixelWidth <= 0.0f )
+        alignment &= ~XBFONT_JUSTIFIED;
+    }
 
-  if ( alignment & (XBFONT_RIGHT | XBFONT_CENTER_X) )
-  {
-    // Get the extent of this line
-    float w = GetTextWidthInternal( text.begin(), text.end() );
+    // calculate sizing information
+    float startX = 0;
+    float startY = (alignment & XBFONT_CENTER_Y) ? -0.5f*m_cellHeight : 0;  // vertical centering
 
-    if ( alignment & XBFONT_TRUNCATED && w > maxPixelWidth + 0.5f ) // + 0.5f due to rounding issues
-      w = maxPixelWidth;
+    if ( alignment & (XBFONT_RIGHT | XBFONT_CENTER_X) )
+    {
+      // Get the extent of this line
+      float w = GetTextWidthInternal( text.begin(), text.end() );
 
-    if ( alignment & XBFONT_CENTER_X)
-      w *= 0.5f;
-    // Offset this line's starting position
-    startX -= w;
-  }
+      if ( alignment & XBFONT_TRUNCATED && w > maxPixelWidth + 0.5f ) // + 0.5f due to rounding issues
+        w = maxPixelWidth;
 
-  float spacePerLetter = 0; // for justification effects
-  if ( alignment & XBFONT_JUSTIFIED )
-  {
-    // first compute the size of the text to render in both characters and pixels
-    unsigned int lineChars = 0;
-    float linePixels = 0;
-    for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos)
+      if ( alignment & XBFONT_CENTER_X)
+        w *= 0.5f;
+      // Offset this line's starting position
+      startX -= w;
+    }
+
+    float spacePerLetter = 0; // for justification effects
+    if ( alignment & XBFONT_JUSTIFIED )
     {
-      Character *ch = GetCharacter(*pos);
-      if (ch)
-      { // spaces have multiple times the justification spacing of normal letters
-        lineChars += ((*pos & 0xffff) == L' ') ? justification_word_weight : 1;
-        linePixels += ch->advance;
+      // first compute the size of the text to render in both characters and pixels
+      unsigned int lineChars = 0;
+      float linePixels = 0;
+      for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos)
+      {
+        Character *ch = GetCharacter(*pos);
+        if (ch)
+        { // spaces have multiple times the justification spacing of normal letters
+          lineChars += ((*pos & 0xffff) == L' ') ? justification_word_weight : 1;
+          linePixels += ch->advance;
+        }
       }
+      if (lineChars > 1)
+        spacePerLetter = (maxPixelWidth - linePixels) / (lineChars - 1);
     }
-    if (lineChars > 1)
-      spacePerLetter = (maxPixelWidth - linePixels) / (lineChars - 1);
-  }
-  float cursorX = 0; // current position along the line
-
-  for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos)
-  {
-    // If starting text on a new line, determine justification effects
-    // Get the current letter in the CStdString
-    color_t color = (*pos & 0xff0000) >> 16;
-    if (color >= colors.size())
-      color = 0;
-    color = colors[color];
+    float cursorX = 0; // current position along the line
 
-    // grab the next character
-    Character *ch = GetCharacter(*pos);
-    if (!ch) continue;
-
-    if ( alignment & XBFONT_TRUNCATED )
+    for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos)
     {
-      // Check if we will be exceeded the max allowed width
-      if ( cursorX + ch->advance + 3 * m_ellipsesWidth > maxPixelWidth )
-      {
-        // Yup. Let's draw the ellipses, then bail
-        // Perhaps we should really bail to the next line in this case??
-        Character *period = GetCharacter(L'.');
-        if (!period)
-          break;
+      // If starting text on a new line, determine justification effects
+      // Get the current letter in the CStdString
+      color_t color = (*pos & 0xff0000) >> 16;
+      if (color >= colors.size())
+        color = 0;
+      color = colors[color];
+
+      // grab the next character
+      Character *ch = GetCharacter(*pos);
+      if (!ch) continue;
 
-        for (int i = 0; i < 3; i++)
+      if ( alignment & XBFONT_TRUNCATED )
+      {
+        // Check if we will be exceeded the max allowed width
+        if ( cursorX + ch->advance + 3 * m_ellipsesWidth > maxPixelWidth )
         {
-          RenderCharacter(startX + cursorX, startY, period, color, !scrolling, vertices);
-          cursorX += period->advance;
+          // Yup. Let's draw the ellipses, then bail
+          // Perhaps we should really bail to the next line in this case??
+          Character *period = GetCharacter(L'.');
+          if (!period)
+            break;
+
+          for (int i = 0; i < 3; i++)
+          {
+            RenderCharacter(startX + cursorX, startY, period, color, !scrolling, vertices);
+            cursorX += period->advance;
+          }
+          break;
         }
-        break;
       }
-    }
-    else if (maxPixelWidth > 0 && cursorX > maxPixelWidth)
-      break;  // exceeded max allowed width - stop rendering
+      else if (maxPixelWidth > 0 && cursorX > maxPixelWidth)
+        break;  // exceeded max allowed width - stop rendering
 
-    RenderCharacter(startX + cursorX, startY, ch, color, !scrolling, vertices);
-    if ( alignment & XBFONT_JUSTIFIED )
-    {
-      if ((*pos & 0xffff) == L' ')
-        cursorX += ch->advance + spacePerLetter * justification_word_weight;
+      RenderCharacter(startX + cursorX, startY, ch, color, !scrolling, vertices);
+      if ( alignment & XBFONT_JUSTIFIED )
+      {
+        if ((*pos & 0xffff) == L' ')
+          cursorX += ch->advance + spacePerLetter * justification_word_weight;
+        else
+          cursorX += ch->advance + spacePerLetter;
+      }
       else
-        cursorX += ch->advance + spacePerLetter;
+        cursorX += ch->advance;
     }
-    else
-      cursorX += ch->advance;
   }
+  /* Append the new vertices (from the cache or otherwise) to the set collected
+   * since the first Begin() call */
+  m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end());
 
   End();
 }
diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h
index a5d44f4..3b93672 100644
--- a/xbmc/guilib/GUIFontTTF.h
+++ b/xbmc/guilib/GUIFontTTF.h
@@ -66,6 +66,9 @@ struct SVertex
 };
 
 
+#include "GUIFontCache.h"
+
+
 class CGUIFontTTFBase
 {
   friend class CGUIFont;
@@ -168,6 +171,8 @@ class CGUIFontTTFBase
   CStdString m_strFileName;
   XUTILS::auto_buffer m_fontFileInMemory; // used only in some cases, see CFreeTypeLibrary::GetFont()
 
+  CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue> m_staticCache;
+
 private:
   virtual bool FirstBegin() = 0;
   virtual void LastEnd() = 0;
diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp
index b76c6a5..9935ea4 100644
--- a/xbmc/guilib/GUIFontTTFGL.cpp
+++ b/xbmc/guilib/GUIFontTTFGL.cpp
@@ -221,6 +221,7 @@ CBaseTexture* CGUIFontTTFGL::ReallocTexture(unsigned int& newHeight)
   m_textureScaleX = 1.0f / m_textureWidth;
   if (m_textureHeight < newHeight)
     CLog::Log(LOGWARNING, "%s: allocated new texture with height of %d, requested %d", __FUNCTION__, m_textureHeight, newHeight);
+  m_staticCache.Flush();
 
   memset(newTexture->GetPixels(), 0, m_textureHeight * newTexture->GetPitch());
   if (m_texture)
diff --git a/xbmc/guilib/GraphicContext.h b/xbmc/guilib/GraphicContext.h
index c2a4c16..a385eb4 100644
--- a/xbmc/guilib/GraphicContext.h
+++ b/xbmc/guilib/GraphicContext.h
@@ -146,6 +146,7 @@ class CGraphicContext : public CCriticalSection,
   inline void ScaleFinalCoords(float &x, float &y, float &z) const XBMC_FORCE_INLINE { m_finalTransform.matrix.TransformPosition(x, y, z); }
   bool RectIsAngled(float x1, float y1, float x2, float y2) const;
 
+  inline const TransformMatrix &GetGUIMatrix() const XBMC_FORCE_INLINE { return m_finalTransform.matrix; }
   inline float GetGUIScaleX() const XBMC_FORCE_INLINE { return m_finalTransform.scaleX; }
   inline float GetGUIScaleY() const XBMC_FORCE_INLINE { return m_finalTransform.scaleY; }
   inline color_t MergeAlpha(color_t color) const XBMC_FORCE_INLINE
diff --git a/xbmc/guilib/Makefile.in b/xbmc/guilib/Makefile.in
index 086fb0d..af82979 100644
--- a/xbmc/guilib/Makefile.in
+++ b/xbmc/guilib/Makefile.in
@@ -23,6 +23,7 @@ SRCS += GUIEditControl.cpp
 SRCS += GUIFadeLabelControl.cpp
 SRCS += GUIFixedListContainer.cpp
 SRCS += GUIFont.cpp
+SRCS += GUIFontCache.cpp
 SRCS += GUIFontManager.cpp
 SRCS += GUIFontTTF.cpp
 SRCS += GUIImage.cpp
diff --git a/xbmc/guilib/TransformMatrix.h b/xbmc/guilib/TransformMatrix.h
index f351c99..9036ba9 100644
--- a/xbmc/guilib/TransformMatrix.h
+++ b/xbmc/guilib/TransformMatrix.h
@@ -245,3 +245,14 @@ class TransformMatrix
   float alpha;
   bool identity;
 };
+
+inline bool operator==(const TransformMatrix &a, const TransformMatrix &b)
+{
+  return a.alpha == b.alpha && ((a.identity && b.identity) ||
+      (!a.identity && !b.identity && std::equal(&a.m[0][0], &a.m[0][0] + sizeof a.m / sizeof a.m[0][0], &b.m[0][0])));
+}
+
+inline bool operator!=(const TransformMatrix &a, const TransformMatrix &b)
+{
+  return !operator==(a, b);
+}

From a94b6585c65bdc8b06003fce9fc99accbb42a2cb Mon Sep 17 00:00:00 2001
From: Ben Avison <bavison@riscosopen.org>
Date: Thu, 23 Jan 2014 22:24:17 +0000
Subject: [PATCH 17/88] Lay the groundwork for hardware clipping.

For glScissor() to replace CGraphicContext::ClipRect, a necessary condition
is that no shear or rotation is introduced between the coordinate systems
they use; this depends upon the settings of the GUI matrix m_finalTransform
as well as the OpenGL model-view and projection matrices. These all remain
unchanged between paired calls of CGUIShader::OnEnabled and
CGUIShader::OnDisabled, so we scan the matrices in CGUIShader::OnEnabled to
see whether hardware clipping is possible.

Then, in CGUIFontTTFBase::RenderCharacter, we don't apply software clipping
in such cases. However, because vertices arising from multiple
CGUIFontTTFBase::DrawTextInternal calls (each of which often uses a different
clip rectangle) get lumped into the same vector, which only at the end is
passed to OpenGL for rendering, we need to wait a few commits before we can
actually apply hardware clipping. In the meantime, expect to see rendering
errors.
---
 xbmc/guilib/GUIFontTTF.cpp               |  3 +-
 xbmc/guilib/GUIShader.cpp                | 80 +++++++++++++++++++++++++++++++-
 xbmc/guilib/GUIShader.h                  | 11 +++++
 xbmc/guilib/GraphicContext.cpp           | 10 ++++
 xbmc/guilib/GraphicContext.h             |  1 +
 xbmc/rendering/RenderSystem.h            |  2 +
 xbmc/rendering/gles/RenderSystemGLES.cpp | 22 +++++++++
 xbmc/rendering/gles/RenderSystemGLES.h   |  2 +
 8 files changed, 128 insertions(+), 3 deletions(-)

diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp
index 6a40e9c..a8f5e67 100644
--- a/xbmc/guilib/GUIFontTTF.cpp
+++ b/xbmc/guilib/GUIFontTTF.cpp
@@ -734,7 +734,8 @@ void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *c
                (posY + ch->offsetY + height) * g_graphicsContext.GetGUIScaleY());
   vertex += CPoint(m_originX, m_originY);
   CRect texture(ch->left, ch->top, ch->right, ch->bottom);
-  g_graphicsContext.ClipRect(vertex, texture);
+  if (!g_Windowing.ScissorsCanEffectClipping())
+    g_graphicsContext.ClipRect(vertex, texture);
 
   // transform our positions - note, no scaling due to GUI calibration/resolution occurs
   float x[4], y[4], z[4];
diff --git a/xbmc/guilib/GUIShader.cpp b/xbmc/guilib/GUIShader.cpp
index 23cb84f..5d836cee 100644
--- a/xbmc/guilib/GUIShader.cpp
+++ b/xbmc/guilib/GUIShader.cpp
@@ -26,6 +26,8 @@
 #include "GUIShader.h"
 #include "MatrixGLES.h"
 #include "utils/log.h"
+#include "windowing/WindowingFactory.h"
+#include "guilib/GraphicContext.h"
 
 using namespace Shaders;
 
@@ -88,8 +90,82 @@ bool CGUIShader::OnEnabled()
 {
   // This is called after glUseProgram()
 
-  glUniformMatrix4fv(m_hProj,  1, GL_FALSE, g_matrices.GetMatrix(MM_PROJECTION));
-  glUniformMatrix4fv(m_hModel, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW));
+  GLfloat *projMatrix = g_matrices.GetMatrix(MM_PROJECTION);
+  GLfloat *modelMatrix = g_matrices.GetMatrix(MM_MODELVIEW);
+  glUniformMatrix4fv(m_hProj,  1, GL_FALSE, projMatrix);
+  glUniformMatrix4fv(m_hModel, 1, GL_FALSE, modelMatrix);
+
+  const TransformMatrix &guiMatrix = g_graphicsContext.GetGUIMatrix();
+  CRect viewPort; // absolute positions of corners
+  g_Windowing.GetViewPort(viewPort);
+
+  /* glScissor operates in window coordinates. In order that we can use it to
+   * perform clipping, we must ensure that there is an independent linear
+   * transformation from the coordinate system used by CGraphicContext::ClipRect
+   * to window coordinates, separately for X and Y (in other words, no
+   * rotation or shear is introduced at any stage). To do, this, we need to
+   * check that zeros are present in the following locations:
+   *
+   * GUI matrix:
+   * / * 0 * * \
+   * | 0 * * * |
+   * \ 0 0 * * /
+   *       ^ TransformMatrix::TransformX/Y/ZCoord are only ever called with
+   *         input z = 0, so this column doesn't matter
+   * Model-view matrix:
+   * / * 0 0 * \
+   * | 0 * 0 * |
+   * | 0 0 * * |
+   * \ * * * * /  <- eye w has no influence on window x/y (last column below
+   *                                                       is either 0 or ignored)
+   * Projection matrix:
+   * / * 0 0 0 \
+   * | 0 * 0 0 |
+   * | * * * * |  <- normalised device coordinate z has no influence on window x/y
+   * \ 0 0 * 0 /
+   *
+   * Some of these zeros are not strictly required to ensure this, but they tend
+   * to be zeroed in the common case, so by checking for zeros here, we simplify
+   * the calculation of the window x/y coordinates further down the line.
+   *
+   * (Minor detail: we don't quite deal in window coordinates as defined by
+   * OpenGL, because CRenderSystemGLES::SetScissors flips the Y axis. But all
+   * that's needed to handle that is an effective negation at the stage where
+   * Y is in normalised device coordinates.)
+   */
+  m_clipPossible = guiMatrix.m[0][1] == 0 &&
+      guiMatrix.m[1][0] == 0 &&
+      guiMatrix.m[2][0] == 0 &&
+      guiMatrix.m[2][1] == 0 &&
+      modelMatrix[0+1*4] == 0 &&
+      modelMatrix[0+2*4] == 0 &&
+      modelMatrix[1+0*4] == 0 &&
+      modelMatrix[1+2*4] == 0 &&
+      modelMatrix[2+0*4] == 0 &&
+      modelMatrix[2+1*4] == 0 &&
+      projMatrix[0+1*4] == 0 &&
+      projMatrix[0+2*4] == 0 &&
+      projMatrix[0+3*4] == 0 &&
+      projMatrix[1+0*4] == 0 &&
+      projMatrix[1+2*4] == 0 &&
+      projMatrix[1+3*4] == 0 &&
+      projMatrix[3+0*4] == 0 &&
+      projMatrix[3+1*4] == 0 &&
+      projMatrix[3+3*4] == 0;
+  if (m_clipPossible)
+  {
+    m_clipXFactor = guiMatrix.m[0][0] * modelMatrix[0+0*4] * projMatrix[0+0*4];
+    m_clipXOffset = (guiMatrix.m[0][3] * modelMatrix[0+0*4] + modelMatrix[0+3*4]) * projMatrix[0+0*4];
+    m_clipYFactor = guiMatrix.m[1][1] * modelMatrix[1+1*4] * projMatrix[1+1*4];
+    m_clipYOffset = (guiMatrix.m[1][3] * modelMatrix[1+1*4] + modelMatrix[1+3*4]) * projMatrix[1+1*4];
+    float clipW = (guiMatrix.m[2][3] * modelMatrix[2+2*4] + modelMatrix[2+3*4]) * projMatrix[3+2*4];
+    float xMult = (viewPort.x2 - viewPort.x1) / (2 * clipW);
+    float yMult = (viewPort.y1 - viewPort.y2) / (2 * clipW); // correct for inverted window coordinate scheme
+    m_clipXFactor = m_clipXFactor * xMult;
+    m_clipXOffset = m_clipXOffset * xMult + (viewPort.x2 + viewPort.x1) / 2;
+    m_clipYFactor = m_clipYFactor * yMult;
+    m_clipYOffset = m_clipYOffset * yMult + (viewPort.y2 + viewPort.y1) / 2;
+  }
 
   return true;
 }
diff --git a/xbmc/guilib/GUIShader.h b/xbmc/guilib/GUIShader.h
index f7b5d9a..fdf7452 100644
--- a/xbmc/guilib/GUIShader.h
+++ b/xbmc/guilib/GUIShader.h
@@ -39,6 +39,11 @@ class CGUIShader : public Shaders::CGLSLShaderProgram
   GLint GetCord1Loc() { return m_hCord1; }
   GLint GetUniColLoc() { return m_hUniCol; }
   GLint GetCoord0MatrixLoc() { return m_hCoord0Matrix; }
+  bool HardwareClipIsPossible() { return m_clipPossible; }
+  GLfloat GetClipXFactor() { return m_clipXFactor; }
+  GLfloat GetClipXOffset() { return m_clipXOffset; }
+  GLfloat GetClipYFactor() { return m_clipYFactor; }
+  GLfloat GetClipYOffset() { return m_clipYOffset; }
 
 protected:
   GLint m_hTex0;
@@ -54,6 +59,12 @@ class CGUIShader : public Shaders::CGLSLShaderProgram
 
   GLfloat *m_proj;
   GLfloat *m_model;
+
+  bool m_clipPossible;
+  GLfloat m_clipXFactor;
+  GLfloat m_clipXOffset;
+  GLfloat m_clipYFactor;
+  GLfloat m_clipYOffset;
 };
 
 #endif // GUI_SHADER_H
diff --git a/xbmc/guilib/GraphicContext.cpp b/xbmc/guilib/GraphicContext.cpp
index 991ae5b..282792c 100644
--- a/xbmc/guilib/GraphicContext.cpp
+++ b/xbmc/guilib/GraphicContext.cpp
@@ -167,6 +167,16 @@ void CGraphicContext::ClipRect(CRect &vertex, CRect &texture, CRect *texture2)
   }
 }
 
+CRect CGraphicContext::GetClipRegion()
+{
+  if (m_clipRegions.empty())
+    return CRect(0, 0, m_iScreenWidth, m_iScreenHeight);
+  CRect clipRegion(m_clipRegions.top());
+  if (!m_origins.empty())
+    clipRegion -= m_origins.top();
+  return clipRegion;
+}
+
 bool CGraphicContext::SetViewPort(float fx, float fy, float fwidth, float fheight, bool intersectPrevious /* = false */)
 {
   // transform coordinates - we may have a rotation which changes the positioning of the
diff --git a/xbmc/guilib/GraphicContext.h b/xbmc/guilib/GraphicContext.h
index a385eb4..2904c1b 100644
--- a/xbmc/guilib/GraphicContext.h
+++ b/xbmc/guilib/GraphicContext.h
@@ -199,6 +199,7 @@ class CGraphicContext : public CCriticalSection,
   void ApplyHardwareTransform();
   void RestoreHardwareTransform();
   void ClipRect(CRect &vertex, CRect &texture, CRect *diffuse = NULL);
+  CRect GetClipRegion();
   inline void AddGUITransform()
   {
     m_transforms.push(m_finalTransform);
diff --git a/xbmc/rendering/RenderSystem.h b/xbmc/rendering/RenderSystem.h
index fa64eba..c1dfb93 100644
--- a/xbmc/rendering/RenderSystem.h
+++ b/xbmc/rendering/RenderSystem.h
@@ -110,6 +110,8 @@ class CRenderSystemBase
   virtual void GetViewPort(CRect& viewPort) = 0;
   virtual void RestoreViewPort() {};
 
+  virtual bool ScissorsCanEffectClipping() { return false; }
+  virtual CRect ClipRectToScissorRect(const CRect &rect) { return CRect(); }
   virtual void SetScissors(const CRect &rect) = 0;
   virtual void ResetScissors() = 0;
 
diff --git a/xbmc/rendering/gles/RenderSystemGLES.cpp b/xbmc/rendering/gles/RenderSystemGLES.cpp
index 653c9ec..deb3afc 100644
--- a/xbmc/rendering/gles/RenderSystemGLES.cpp
+++ b/xbmc/rendering/gles/RenderSystemGLES.cpp
@@ -533,6 +533,28 @@ void CRenderSystemGLES::SetViewPort(CRect& viewPort)
   m_viewPort[3] = viewPort.Height();
 }
 
+bool CRenderSystemGLES::ScissorsCanEffectClipping()
+{
+  if (m_pGUIshader[m_method])
+    return m_pGUIshader[m_method]->HardwareClipIsPossible();
+
+  return false;
+}
+
+CRect CRenderSystemGLES::ClipRectToScissorRect(const CRect &rect)
+{
+  if (!m_pGUIshader[m_method])
+    return CRect();
+  float xFactor = m_pGUIshader[m_method]->GetClipXFactor();
+  float xOffset = m_pGUIshader[m_method]->GetClipXOffset();
+  float yFactor = m_pGUIshader[m_method]->GetClipYFactor();
+  float yOffset = m_pGUIshader[m_method]->GetClipYOffset();
+  return CRect(rect.x1 * xFactor + xOffset,
+               rect.y1 * yFactor + yOffset,
+               rect.x2 * xFactor + xOffset,
+               rect.y2 * yFactor + yOffset);
+}
+
 void CRenderSystemGLES::SetScissors(const CRect &rect)
 {
   if (!m_bRenderCreated)
diff --git a/xbmc/rendering/gles/RenderSystemGLES.h b/xbmc/rendering/gles/RenderSystemGLES.h
index 98e398a..81ee49e 100644
--- a/xbmc/rendering/gles/RenderSystemGLES.h
+++ b/xbmc/rendering/gles/RenderSystemGLES.h
@@ -63,6 +63,8 @@ class CRenderSystemGLES : public CRenderSystemBase
   virtual void SetViewPort(CRect& viewPort);
   virtual void GetViewPort(CRect& viewPort);
 
+  virtual bool ScissorsCanEffectClipping();
+  virtual CRect ClipRectToScissorRect(const CRect &rect);
   virtual void SetScissors(const CRect& rect);
   virtual void ResetScissors();
 

From 62be6ccfe7b9e51cc4e360cb1261ae5399aaaf93 Mon Sep 17 00:00:00 2001
From: Ben Avison <bavison@riscosopen.org>
Date: Thu, 23 Jan 2014 16:42:22 +0000
Subject: [PATCH 18/88] Increase font cache hit rate by keying on the
 fractional part of m_originX and m_originY *after* they have been through the
 graphics context's transformation matrix, plus the scale/rotation elements of
 the matrix, rather than the origin in the original frame of reference plus
 the complete transformation matrix. All vertices of individual glyph bounding
 boxes are a constant offset from this position, and when the fractional part
 of the translation is a match, the rounding of each vertex will be in the
 same direction; this permits us to calculate the desired vertices from the
 cached ones simply by adding the integer parts of the translations with no
 additional rounding steps.

Note that this requires that software clipping is *not* performed.
---
 xbmc/guilib/GUIFontCache.cpp |  8 +++++++
 xbmc/guilib/GUIFontCache.h   | 43 +++++++++++++++++++++++++++++++++++
 xbmc/guilib/GUIFontTTF.cpp   | 53 +++++++++++++++++++++++++++++++++++---------
 xbmc/guilib/GUIFontTTF.h     |  1 +
 4 files changed, 95 insertions(+), 10 deletions(-)

diff --git a/xbmc/guilib/GUIFontCache.cpp b/xbmc/guilib/GUIFontCache.cpp
index 2c72f9c..df466a5 100644
--- a/xbmc/guilib/GUIFontCache.cpp
+++ b/xbmc/guilib/GUIFontCache.cpp
@@ -85,6 +85,9 @@ Value &CGUIFontCache<Position, Value>::Lookup(Position &pos,
   else
   {
     /* Cache hit */
+    /* Update the translation arguments so that they hold the offset to apply
+     * to the cached values (but only in the dynamic case) */
+    pos.UpdateWithOffsets(i->m_key.m_pos, scrolling);
     /* Update time in entry and move to the back of the list */
     i->m_lastUsedMillis = nowMillis;
     m_list.template get<Age>().relocate(m_list.template get<Age>().end(), m_list.template project<Age>(i));
@@ -103,3 +106,8 @@ template void CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStati
 template CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::~CGUIFontCacheEntry();
 template CGUIFontCacheStaticValue &CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::Lookup(CGUIFontCacheStaticPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &);
 template void CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::Flush();
+
+template void CGUIFontCacheEntry<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::Reassign::operator()(CGUIFontCacheEntry<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue> &entry);
+template CGUIFontCacheEntry<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::~CGUIFontCacheEntry();
+template CGUIFontCacheDynamicValue &CGUIFontCache<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::Lookup(CGUIFontCacheDynamicPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &);
+template void CGUIFontCache<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::Flush();
diff --git a/xbmc/guilib/GUIFontCache.h b/xbmc/guilib/GUIFontCache.h
index ef65845..d913dee 100644
--- a/xbmc/guilib/GUIFontCache.h
+++ b/xbmc/guilib/GUIFontCache.h
@@ -44,6 +44,7 @@
 using namespace boost::multi_index;
 
 #define FONT_CACHE_TIME_LIMIT (1000)
+#define FONT_CACHE_DIST_LIMIT (0.01)
 
 template<class Position, class Value> class CGUIFontCache;
 class CGUIFontTTFBase;
@@ -197,6 +198,7 @@ struct CGUIFontCacheStaticPosition
   float m_x;
   float m_y;
   CGUIFontCacheStaticPosition(float x, float y) : m_x(x), m_y(y) {}
+  void UpdateWithOffsets(const CGUIFontCacheStaticPosition &cached, bool scrolling) {}
 };
 
 typedef std::vector<SVertex> CGUIFontCacheStaticValue;
@@ -214,4 +216,45 @@ inline float MatrixHashContribution(const CGUIFontCacheKey<CGUIFontCacheStaticPo
   return a.m_matrix.m[0][3];
 }
 
+struct CGUIFontCacheDynamicPosition
+{
+  float m_x;
+  float m_y;
+  float m_z;
+  CGUIFontCacheDynamicPosition() {}
+  CGUIFontCacheDynamicPosition(float x, float y, float z) : m_x(x), m_y(y), m_z(z) {}
+  void UpdateWithOffsets(const CGUIFontCacheDynamicPosition &cached, bool scrolling)
+  {
+    if (scrolling)
+      m_x = m_x - cached.m_x;
+    else
+      m_x = floorf(m_x - cached.m_x + FONT_CACHE_DIST_LIMIT);
+    m_y = floorf(m_y - cached.m_y + FONT_CACHE_DIST_LIMIT);
+    m_z = floorf(m_z - cached.m_z + FONT_CACHE_DIST_LIMIT);
+  }
+};
+
+typedef std::vector<SVertex> CGUIFontCacheDynamicValue;
+
+inline bool Match(const CGUIFontCacheDynamicPosition &a, const TransformMatrix &a_m,
+                  const CGUIFontCacheDynamicPosition &b, const TransformMatrix &b_m,
+                  bool scrolling)
+{
+  float diffX = a.m_x - b.m_x + FONT_CACHE_DIST_LIMIT;
+  float diffY = a.m_y - b.m_y + FONT_CACHE_DIST_LIMIT;
+  float diffZ = a.m_z - b.m_z + FONT_CACHE_DIST_LIMIT;
+  return (scrolling || diffX - floorf(diffX) < 2 * FONT_CACHE_DIST_LIMIT) &&
+          diffY - floorf(diffY) < 2 * FONT_CACHE_DIST_LIMIT &&
+          diffZ - floorf(diffZ) < 2 * FONT_CACHE_DIST_LIMIT &&
+          a_m.m[0][0] == b_m.m[0][0] &&
+          a_m.m[1][1] == b_m.m[1][1] &&
+          a_m.m[2][2] == b_m.m[2][2];
+          // We already know the first 3 columns of both matrices are diagonal, so no need to check the other elements
+}
+
+inline float MatrixHashContribution(const CGUIFontCacheKey<CGUIFontCacheDynamicPosition> &a)
+{
+  return 0;
+}
+
 #endif
diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp
index a8f5e67..91bf91b 100644
--- a/xbmc/guilib/GUIFontTTF.cpp
+++ b/xbmc/guilib/GUIFontTTF.cpp
@@ -154,7 +154,7 @@ class CFreeTypeLibrary
 XBMC_GLOBAL_REF(CFreeTypeLibrary, g_freeTypeLibrary); // our freetype library
 #define g_freeTypeLibrary XBMC_GLOBAL_USE(CFreeTypeLibrary)
 
-CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName) : m_staticCache(*this)
+CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName) : m_staticCache(*this), m_dynamicCache(*this)
 {
   m_texture = NULL;
   m_char = NULL;
@@ -356,13 +356,28 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
   Begin();
 
   bool dirtyCache;
+  bool hardwareClipping = g_Windowing.ScissorsCanEffectClipping();
   CGUIFontCacheStaticPosition staticPos(x, y);
-  std::vector<SVertex> &vertices = m_staticCache.Lookup(staticPos,
-                                                        colors, text,
-                                                        alignment, maxPixelWidth,
-                                                        scrolling,
-                                                        XbmcThreads::SystemClockMillis(),
-                                                        dirtyCache);
+  CGUIFontCacheDynamicPosition dynamicPos;
+  if (hardwareClipping)
+  {
+    dynamicPos = CGUIFontCacheDynamicPosition(g_graphicsContext.ScaleFinalXCoord(x, y),
+                                              g_graphicsContext.ScaleFinalYCoord(x, y),
+                                              g_graphicsContext.ScaleFinalZCoord(x, y));
+  }
+  std::vector<SVertex> &vertices = hardwareClipping ?
+      m_dynamicCache.Lookup(dynamicPos,
+                            colors, text,
+                            alignment, maxPixelWidth,
+                            scrolling,
+                            XbmcThreads::SystemClockMillis(),
+                            dirtyCache) :
+      m_staticCache.Lookup(staticPos,
+                           colors, text,
+                           alignment, maxPixelWidth,
+                           scrolling,
+                           XbmcThreads::SystemClockMillis(),
+                           dirtyCache);
   if (dirtyCache)
   {
     // save the origin, which is scaled separately
@@ -465,10 +480,28 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
       else
         cursorX += ch->advance;
     }
+    if (hardwareClipping)
+      /* Append the new vertices (which we have just constructed in the cache)
+       * to the set collected since the first Begin() call */
+      m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end());
+  }
+  else if (hardwareClipping)
+  {
+    /* Apply the translation offset to the vertices from the cache after
+     * appending them to the set collected since the first Begin() call */
+    m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end());
+    SVertex *v;
+    for (v = &*m_vertex.end() - vertices.size(); v != &*m_vertex.end(); v++)
+    {
+      v->x += dynamicPos.m_x;
+      v->y += dynamicPos.m_y;
+      v->z += dynamicPos.m_z;
+    }
   }
-  /* Append the new vertices (from the cache or otherwise) to the set collected
-   * since the first Begin() call */
-  m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end());
+  if (!hardwareClipping)
+    /* Append the new vertices (from the cache or otherwise) to the set collected
+     * since the first Begin() call */
+    m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end());
 
   End();
 }
diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h
index 3b93672..258dffa 100644
--- a/xbmc/guilib/GUIFontTTF.h
+++ b/xbmc/guilib/GUIFontTTF.h
@@ -172,6 +172,7 @@ class CGUIFontTTFBase
   XUTILS::auto_buffer m_fontFileInMemory; // used only in some cases, see CFreeTypeLibrary::GetFont()
 
   CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue> m_staticCache;
+  CGUIFontCache<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue> m_dynamicCache;
 
 private:
   virtual bool FirstBegin() = 0;

From 21a1cbbbf6ebb56ed1f037470165946dc57c1df1 Mon Sep 17 00:00:00 2001
From: Ben Avison <bavison@riscosopen.org>
Date: Wed, 8 Jan 2014 12:16:33 +0000
Subject: [PATCH 19/88] Rewrite of scrolling text code.

No longer shuffles the string round to minimise the number of characters
before the clipping rectangle; this doesn't save much on rendering time but
does harm the effectiveness of the cache. Now uses a pixel offset into the
string rather than a character + pixel offset, and plots the entire string
every time (execpt when the wrap point is visible, in which case the entire
string is plotted twice).

It also makes motion smoother, because (possibly unintentionally) the
previous code preferred to align the scroll offset with character boundaries.
This would lead to uneven changes of position, especially when the width of
the character currently being scrolled off the edge was only slightly more
than an integral multiple of the scroll increment.
---
 xbmc/guilib/GUIFadeLabelControl.cpp |   8 +--
 xbmc/guilib/GUIFont.cpp             | 123 +++++++++++++-----------------------
 xbmc/guilib/GUIFont.h               |  17 ++---
 xbmc/guilib/GUIRSSControl.cpp       |   6 +-
 xbmc/utils/RssReader.cpp            |   2 +-
 xbmc/utils/RssReader.h              |   2 +-
 6 files changed, 58 insertions(+), 100 deletions(-)

diff --git a/xbmc/guilib/GUIFadeLabelControl.cpp b/xbmc/guilib/GUIFadeLabelControl.cpp
index 844f960..5859d9f 100644
--- a/xbmc/guilib/GUIFadeLabelControl.cpp
+++ b/xbmc/guilib/GUIFadeLabelControl.cpp
@@ -109,18 +109,14 @@ void CGUIFadeLabelControl::Process(unsigned int currentTime, CDirtyRegionList &d
     bool moveToNextLabel = false;
     if (!m_scrollOut)
     {
-      vecText text;
-      m_textLayout.GetFirstText(text);
-      if (m_scrollInfo.characterPos && m_scrollInfo.characterPos < text.size())
-        text.erase(text.begin(), text.begin() + min((int)m_scrollInfo.characterPos - 1, (int)text.size()));
-      if (m_label.font->GetTextWidth(text) < m_width)
+      if (m_scrollInfo.pixelPos + m_width > m_scrollInfo.m_textWidth)
       {
         if (m_fadeAnim.GetProcess() != ANIM_PROCESS_NORMAL)
           m_fadeAnim.QueueAnimation(ANIM_PROCESS_NORMAL);
         moveToNextLabel = true;
       }
     }
-    else if (m_scrollInfo.characterPos > m_textLayout.GetTextLength())
+    else if (m_scrollInfo.pixelPos > m_scrollInfo.m_textWidth)
       moveToNextLabel = true;
     
     // apply the fading animation
diff --git a/xbmc/guilib/GUIFont.cpp b/xbmc/guilib/GUIFont.cpp
index a7ee668..eb8efdb 100644
--- a/xbmc/guilib/GUIFont.cpp
+++ b/xbmc/guilib/GUIFont.cpp
@@ -36,7 +36,12 @@ CScrollInfo::CScrollInfo(unsigned int wait /* = 50 */, float pos /* = 0 */,
     initialWait = wait;
     initialPos = pos;
     SetSpeed(speed ? speed : defaultSpeed);
-    g_charsetConverter.utf8ToW(scrollSuffix, suffix);
+    CStdStringW wsuffix;
+    g_charsetConverter.utf8ToW(scrollSuffix, wsuffix);
+    suffix.clear();
+    suffix.reserve(wsuffix.size());
+    for (vecText::size_type i = 0; i < wsuffix.size(); i++)
+      suffix.push_back(wsuffix[i]);
     Reset();
 }
 
@@ -115,11 +120,12 @@ bool CGUIFont::UpdateScrollInfo(const vecText &text, CScrollInfo &scrollInfo)
 {
   // draw at our scroll position
   // we handle the scrolling as follows:
-  //   We scroll on a per-pixel basis up until we have scrolled the first character outside
-  //   of our viewport, whereby we cycle the string around, and reset the scroll position.
-  //
-  //   pixelPos is the amount in pixels to move the string by.
-  //   characterPos is the amount in characters to rotate the string by.
+  //   We scroll on a per-pixel basis (eschewing the use of character indices
+  //   which were also in use previously). The complete string, including suffix,
+  //   is plotted to achieve the desired effect - normally just the one time, but
+  //   if there is a wrap point within the viewport then it will be plotted twice.
+  //   If the string is smaller than the viewport, then it may be plotted even
+  //   more times than that.
   //
   if (scrollInfo.waitTime)
   {
@@ -135,54 +141,19 @@ bool CGUIFont::UpdateScrollInfo(const vecText &text, CScrollInfo &scrollInfo)
   // move along by the appropriate scroll amount
   float scrollAmount = fabs(scrollInfo.GetPixelsPerFrame() * g_graphicsContext.GetGUIScaleX());
 
-  if (scrollInfo.pixelSpeed > 0)
+  if (!scrollInfo.m_widthValid)
   {
-    // we want to move scrollAmount, grab the next character
-    float charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text));
-    if (scrollInfo.pixelPos + scrollAmount < charWidth)
-      scrollInfo.pixelPos += scrollAmount;  // within the current character
-    else
-    { // past the current character, decrement scrollAmount by the charWidth and move to the next character
-      while (scrollInfo.pixelPos + scrollAmount >= charWidth)
-      {
-        scrollAmount -= (charWidth - scrollInfo.pixelPos);
-        scrollInfo.pixelPos = 0;
-        scrollInfo.characterPos++;
-        if (scrollInfo.characterPos >= text.size() + scrollInfo.suffix.size())
-        {
-          scrollInfo.Reset();
-          break;
-        }
-        charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text));
-      }
-    }
-  }
-  else if (scrollInfo.pixelSpeed < 0)
-  { // scrolling backwards
-    // we want to move scrollAmount, grab the next character
-    float charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text));
-    if (scrollInfo.pixelPos + scrollAmount < charWidth)
-      scrollInfo.pixelPos += scrollAmount;  // within the current character
-    else
-    { // past the current character, decrement scrollAmount by the charWidth and move to the next character
-      while (scrollInfo.pixelPos + scrollAmount >= charWidth)
-      {
-        scrollAmount -= (charWidth - scrollInfo.pixelPos);
-        scrollInfo.pixelPos = 0;
-        if (scrollInfo.characterPos == 0)
-        {
-          scrollInfo.Reset();
-          scrollInfo.characterPos = text.size() + scrollInfo.suffix.size() - 1;
-          break;
-        }
-        scrollInfo.characterPos--;
-        charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text));
-      }
-    }
+    /* Calculate the pixel width of the complete string */
+    scrollInfo.m_textWidth = GetTextWidth(text);
+    scrollInfo.m_totalWidth = scrollInfo.m_textWidth + GetTextWidth(scrollInfo.suffix);
+    scrollInfo.m_widthValid = true;
   }
+  scrollInfo.pixelPos += scrollAmount;
+  assert(scrollInfo.m_totalWidth != 0);
+  while (scrollInfo.pixelPos >= scrollInfo.m_totalWidth)
+    scrollInfo.pixelPos -= scrollInfo.m_totalWidth;
 
-  if(scrollInfo.characterPos != old.characterPos
-  || scrollInfo.pixelPos     != old.pixelPos)
+  if (scrollInfo.pixelPos != old.pixelPos)
     return true;
   else
     return false;
@@ -194,39 +165,27 @@ void CGUIFont::DrawScrollingText(float x, float y, const vecColors &colors, colo
   if (!m_font) return;
   if (!shadowColor) shadowColor = m_shadowColor;
 
-  float spaceWidth = GetCharWidth(L' ');
-  // max chars on screen + extra margin chars
-  vecText::size_type maxChars =
-    std::min<vecText::size_type>(
-      (text.size() + (vecText::size_type)scrollInfo.suffix.size()),
-      (vecText::size_type)((maxWidth * 1.05f) / spaceWidth));
-
   if (!text.size() || ClippedRegionIsEmpty(x, y, maxWidth, alignment))
     return; // nothing to render
 
-  maxWidth = ROUND((maxWidth + scrollInfo.pixelPos) / g_graphicsContext.GetGUIScaleX());
+  if (!scrollInfo.m_widthValid)
+  {
+    /* Calculate the pixel width of the complete string */
+    scrollInfo.m_textWidth = GetTextWidth(text);
+    scrollInfo.m_totalWidth = scrollInfo.m_textWidth + GetTextWidth(scrollInfo.suffix);
+    scrollInfo.m_widthValid = true;
+  }
+
+  assert(scrollInfo.m_totalWidth != 0);
+
+  float textPixelWidth = ROUND(scrollInfo.m_textWidth / g_graphicsContext.GetGUIScaleX());
+  float suffixPixelWidth = ROUND((scrollInfo.m_totalWidth - scrollInfo.m_textWidth) / g_graphicsContext.GetGUIScaleX());
 
-  float charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text));
   float offset;
   if(scrollInfo.pixelSpeed >= 0)
     offset = scrollInfo.pixelPos;
   else
-    offset = charWidth - scrollInfo.pixelPos;
-
-  // Now rotate our string as needed, only take a slightly larger then visible part of the text.
-  unsigned int pos = scrollInfo.characterPos;
-  vecText renderText;
-  renderText.reserve(maxChars);
-  for (vecText::size_type i = 0; i < maxChars; i++)
-  {
-    if (pos >= text.size() + scrollInfo.suffix.size())
-      pos = 0;
-    if (pos < text.size())
-      renderText.push_back(text[pos]);
-    else
-      renderText.push_back(scrollInfo.suffix[pos - text.size()]);
-    pos++;
-  }
+    offset = scrollInfo.m_totalWidth - scrollInfo.pixelPos;
 
   vecColors renderColors;
   for (unsigned int i = 0; i < colors.size(); i++)
@@ -239,9 +198,17 @@ void CGUIFont::DrawScrollingText(float x, float y, const vecColors &colors, colo
     vecColors shadowColors;
     for (unsigned int i = 0; i < renderColors.size(); i++)
       shadowColors.push_back((renderColors[i] & 0xff000000) != 0 ? shadowColor : 0);
-    m_font->DrawTextInternal(x - offset + 1, y + 1, shadowColors, renderText, alignment, maxWidth + m_font->GetLineHeight(2.0f), scroll);
+    for (float dx = -offset; dx < maxWidth; dx += scrollInfo.m_totalWidth)
+    {
+      m_font->DrawTextInternal(x + dx + 1, y + 1, shadowColors, text, alignment, textPixelWidth, scroll);
+      m_font->DrawTextInternal(x + dx + scrollInfo.m_textWidth + 1, y + 1, shadowColors, scrollInfo.suffix, alignment, suffixPixelWidth, scroll);
+    }
+  }
+  for (float dx = -offset; dx < maxWidth; dx += scrollInfo.m_totalWidth)
+  {
+    m_font->DrawTextInternal(x + dx, y, renderColors, text, alignment, textPixelWidth, scroll);
+    m_font->DrawTextInternal(x + dx + scrollInfo.m_textWidth, y, renderColors, scrollInfo.suffix, alignment, suffixPixelWidth, scroll);
   }
-  m_font->DrawTextInternal(x - offset, y, renderColors, renderText, alignment, maxWidth + m_font->GetLineHeight(2.0f), scroll);
 
   g_graphicsContext.RestoreClipRegion();
 }
diff --git a/xbmc/guilib/GUIFont.h b/xbmc/guilib/GUIFont.h
index c55db48..09cf9b3 100644
--- a/xbmc/guilib/GUIFont.h
+++ b/xbmc/guilib/GUIFont.h
@@ -64,7 +64,6 @@ class CScrollInfo
   void Reset()
   {
     waitTime = initialWait;
-    characterPos = 0;
     // pixelPos is where we start the current letter, so is measured
     // to the left of the text rendering's left edge.  Thus, a negative
     // value will mean the text starts to the right
@@ -72,25 +71,19 @@ class CScrollInfo
     // privates:
     m_averageFrameTime = 1000.f / abs(defaultSpeed);
     m_lastFrameTime = 0;
-  }
-  uint32_t GetCurrentChar(const vecText &text) const
-  {
-    assert(text.size());
-    if (characterPos < text.size())
-      return text[characterPos];
-    else if (characterPos < text.size() + suffix.size())
-      return suffix[characterPos - text.size()];
-    return text[0];
+    m_widthValid = false;
   }
   float GetPixelsPerFrame();
 
   float pixelPos;
   float pixelSpeed;
   unsigned int waitTime;
-  unsigned int characterPos;
   unsigned int initialWait;
   float initialPos;
-  CStdStringW suffix;
+  vecText suffix;
+  mutable float m_textWidth;
+  mutable float m_totalWidth;
+  mutable bool m_widthValid;
 
   static const int defaultSpeed = 60;
 private:
diff --git a/xbmc/guilib/GUIRSSControl.cpp b/xbmc/guilib/GUIRSSControl.cpp
index 8d985cf..a8e20fc 100644
--- a/xbmc/guilib/GUIRSSControl.cpp
+++ b/xbmc/guilib/GUIRSSControl.cpp
@@ -119,7 +119,9 @@ void CGUIRSSControl::Process(unsigned int currentTime, CDirtyRegionList &dirtyre
       dirty = true;
 
       if (CRssManager::Get().GetReader(GetID(), GetParentID(), this, m_pReader))
-        m_scrollInfo.characterPos = m_pReader->m_SavedScrollPos;
+      {
+        m_scrollInfo.pixelPos = m_pReader->m_savedScrollPixelPos;
+      }
       else
       {
         if (m_strRSSTags != "")
@@ -174,7 +176,7 @@ void CGUIRSSControl::Render()
     if (m_pReader)
     {
       m_pReader->CheckForUpdates();
-      m_pReader->m_SavedScrollPos = m_scrollInfo.characterPos;
+      m_pReader->m_savedScrollPixelPos = m_scrollInfo.pixelPos;
     }
   }
   CGUIControl::Render();
diff --git a/xbmc/utils/RssReader.cpp b/xbmc/utils/RssReader.cpp
index 5383156..41f9bc2 100644
--- a/xbmc/utils/RssReader.cpp
+++ b/xbmc/utils/RssReader.cpp
@@ -55,7 +55,7 @@ CRssReader::CRssReader() : CThread("RSSReader")
   m_pObserver = NULL;
   m_spacesBetweenFeeds = 0;
   m_bIsRunning = false;
-  m_SavedScrollPos = 0;
+  m_savedScrollPixelPos = 0;
   m_rtlText = false;
   m_requestRefresh = false;
 }
diff --git a/xbmc/utils/RssReader.h b/xbmc/utils/RssReader.h
index 2cda726..fbc579e 100644
--- a/xbmc/utils/RssReader.h
+++ b/xbmc/utils/RssReader.h
@@ -43,7 +43,7 @@ class CRssReader : public CThread
   void SetObserver(IRssObserver* observer);
   void CheckForUpdates();
   void requestRefresh();
-  unsigned int m_SavedScrollPos;
+  float m_savedScrollPixelPos;
 
 private:
   void Process();

From d424b7cc2480ff833644d3dfa274c7d4637d05e8 Mon Sep 17 00:00:00 2001
From: Ben Avison <bavison@riscosopen.org>
Date: Mon, 27 Jan 2014 23:21:10 +0000
Subject: [PATCH 20/88] Move the application of the translation offsets into
 the GLES code.

Still all pure software at this stage. Main change is in the data types at
the interface between CGUIFontTTFBase and CGUIFontTTFGL. The old way
(array of vertices in m_vertex) are retained in addition, for the sake`of
cases that need to use software clipping on GLES, as well as for DX and GL
support where the new scheme is not (yet?) used.
---
 xbmc/guilib/GUIFontTTF.cpp   | 19 +++---------
 xbmc/guilib/GUIFontTTF.h     | 17 +++++++++++
 xbmc/guilib/GUIFontTTFGL.cpp | 72 ++++++++++++++++++++++++++++++++------------
 3 files changed, 73 insertions(+), 35 deletions(-)

diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp
index 91bf91b..b1d00e5 100644
--- a/xbmc/guilib/GUIFontTTF.cpp
+++ b/xbmc/guilib/GUIFontTTF.cpp
@@ -236,6 +236,7 @@ void CGUIFontTTFBase::Clear()
     g_freeTypeLibrary.ReleaseStroker(m_stroker);
   m_stroker = NULL;
 
+  m_vertexTrans.clear();
   m_vertex.clear();
 
   m_strFileName.clear();
@@ -334,6 +335,7 @@ void CGUIFontTTFBase::Begin()
 {
   if (m_nestedBeginCount == 0 && m_texture != NULL && FirstBegin())
   {
+    m_vertexTrans.clear();
     m_vertex.clear();
   }
   // Keep track of the nested begin/end calls.
@@ -481,23 +483,10 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
         cursorX += ch->advance;
     }
     if (hardwareClipping)
-      /* Append the new vertices (which we have just constructed in the cache)
-       * to the set collected since the first Begin() call */
-      m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end());
+      m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertices));
   }
   else if (hardwareClipping)
-  {
-    /* Apply the translation offset to the vertices from the cache after
-     * appending them to the set collected since the first Begin() call */
-    m_vertex.insert(m_vertex.end(), vertices.begin(), vertices.end());
-    SVertex *v;
-    for (v = &*m_vertex.end() - vertices.size(); v != &*m_vertex.end(); v++)
-    {
-      v->x += dynamicPos.m_x;
-      v->y += dynamicPos.m_y;
-      v->z += dynamicPos.m_z;
-    }
-  }
+    m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertices));
   if (!hardwareClipping)
     /* Append the new vertices (from the cache or otherwise) to the set collected
      * since the first Begin() call */
diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h
index 258dffa..40bf430 100644
--- a/xbmc/guilib/GUIFontTTF.h
+++ b/xbmc/guilib/GUIFontTTF.h
@@ -63,6 +63,14 @@ struct SVertex
   unsigned char r, g, b, a;
 #endif
   float u, v;
+  struct SVertex Offset(float translate[3]) const
+  {
+    SVertex out = *this;
+    out.x += translate[0];
+    out.y += translate[1];
+    out.z += translate[2];
+    return out;
+  }
 };
 
 
@@ -161,6 +169,15 @@ class CGUIFontTTFBase
 
   unsigned int m_nTexture;
 
+  struct CTranslatedVertices
+  {
+    float translateX;
+    float translateY;
+    float translateZ;
+    const std::vector<SVertex> *vertexBuffer;
+    CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector<SVertex> *vertexBuffer) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer) {}
+  };
+  std::vector<CTranslatedVertices> m_vertexTrans;
   std::vector<SVertex> m_vertex;
 
   float    m_textureScaleX;
diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp
index 9935ea4..18c9358 100644
--- a/xbmc/guilib/GUIFontTTFGL.cpp
+++ b/xbmc/guilib/GUIFontTTFGL.cpp
@@ -167,34 +167,65 @@ void CGUIFontTTFGL::LastEnd()
   GLint colLoc  = g_Windowing.GUIShaderGetCol();
   GLint tex0Loc = g_Windowing.GUIShaderGetCoord0();
 
-  // stack object until VBOs will be used
-  std::vector<SVertex> vecVertices( 6 * (m_vertex.size() / 4) );
-  SVertex *vertices = &vecVertices[0];
+  // Enable the attributes used by this shader
+  glEnableVertexAttribArray(posLoc);
+  glEnableVertexAttribArray(colLoc);
+  glEnableVertexAttribArray(tex0Loc);
 
-  for (size_t i=0; i<m_vertex.size(); i+=4)
+  if (m_vertex.size() > 0)
   {
-    *vertices++ = m_vertex[i];
-    *vertices++ = m_vertex[i+1];
-    *vertices++ = m_vertex[i+2];
+    // Deal with vertices that had to use software clipping
+    std::vector<SVertex> vecVertices( 6 * (m_vertex.size() / 4) );
+    SVertex *vertices = &vecVertices[0];
 
-    *vertices++ = m_vertex[i+1];
-    *vertices++ = m_vertex[i+3];
-    *vertices++ = m_vertex[i+2];
-  }
+    for (size_t i=0; i<m_vertex.size(); i+=4)
+    {
+      *vertices++ = m_vertex[i];
+      *vertices++ = m_vertex[i+1];
+      *vertices++ = m_vertex[i+2];
 
-  vertices = &vecVertices[0];
+      *vertices++ = m_vertex[i+1];
+      *vertices++ = m_vertex[i+3];
+      *vertices++ = m_vertex[i+2];
+    }
 
-  glVertexAttribPointer(posLoc,  3, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x));
-  // Normalize color values. Does not affect Performance at all.
-  glVertexAttribPointer(colLoc,  4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r));
-  glVertexAttribPointer(tex0Loc, 2, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u));
+    vertices = &vecVertices[0];
 
-  glEnableVertexAttribArray(posLoc);
-  glEnableVertexAttribArray(colLoc);
-  glEnableVertexAttribArray(tex0Loc);
+    glVertexAttribPointer(posLoc,  3, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x));
+    // Normalize color values. Does not affect Performance at all.
+    glVertexAttribPointer(colLoc,  4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r));
+    glVertexAttribPointer(tex0Loc, 2, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u));
+
+    glDrawArrays(GL_TRIANGLES, 0, vecVertices.size());
+  }
+  if (m_vertexTrans.size() > 0)
+  {
+    // Deal with the vertices that can be hardware clipped and therefore translated
+    std::vector<SVertex> vecVertices;
+    for (size_t i = 0; i < m_vertexTrans.size(); i++)
+    {
+      float translate[3] = { m_vertexTrans[i].translateX, m_vertexTrans[i].translateY, m_vertexTrans[i].translateZ };
+      for (size_t j = 0; j < m_vertexTrans[i].vertexBuffer->size(); j += 4)
+      {
+        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j].Offset(translate));
+        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1].Offset(translate));
+        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2].Offset(translate));
+        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1].Offset(translate));
+        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+3].Offset(translate));
+        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2].Offset(translate));
+      }
+    }
+    SVertex *vertices = &vecVertices[0];
 
-  glDrawArrays(GL_TRIANGLES, 0, vecVertices.size());
+    glVertexAttribPointer(posLoc,  3, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x));
+    // Normalize color values. Does not affect Performance at all.
+    glVertexAttribPointer(colLoc,  4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r));
+    glVertexAttribPointer(tex0Loc, 2, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u));
+
+    glDrawArrays(GL_TRIANGLES, 0, vecVertices.size());
+  }
 
+  // Disable the attributes used by this shader
   glDisableVertexAttribArray(posLoc);
   glDisableVertexAttribArray(colLoc);
   glDisableVertexAttribArray(tex0Loc);
@@ -222,6 +253,7 @@ CBaseTexture* CGUIFontTTFGL::ReallocTexture(unsigned int& newHeight)
   if (m_textureHeight < newHeight)
     CLog::Log(LOGWARNING, "%s: allocated new texture with height of %d, requested %d", __FUNCTION__, m_textureHeight, newHeight);
   m_staticCache.Flush();
+  m_dynamicCache.Flush();
 
   memset(newTexture->GetPixels(), 0, m_textureHeight * newTexture->GetPitch());
   if (m_texture)

From fca2bf09eb092fc78703ffa3e06fe53b1c6670e9 Mon Sep 17 00:00:00 2001
From: Ben Avison <bavison@riscosopen.org>
Date: Wed, 15 Jan 2014 15:28:06 +0000
Subject: [PATCH 21/88] Rather than applying the translation offsets to the
 vertices, now applies them to the model view matrix from the top of the
 matrix stack and pushes it over to OpenGL. The vertices themselves are still
 all held client-side.

---
 xbmc/guilib/GUIFontTTF.h                 |  8 -------
 xbmc/guilib/GUIFontTTFGL.cpp             | 40 +++++++++++++++++++++-----------
 xbmc/guilib/GUIShader.h                  |  1 +
 xbmc/rendering/gles/RenderSystemGLES.cpp |  8 +++++++
 xbmc/rendering/gles/RenderSystemGLES.h   |  1 +
 5 files changed, 36 insertions(+), 22 deletions(-)

diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h
index 40bf430..49222a4 100644
--- a/xbmc/guilib/GUIFontTTF.h
+++ b/xbmc/guilib/GUIFontTTF.h
@@ -63,14 +63,6 @@ struct SVertex
   unsigned char r, g, b, a;
 #endif
   float u, v;
-  struct SVertex Offset(float translate[3]) const
-  {
-    SVertex out = *this;
-    out.x += translate[0];
-    out.y += translate[1];
-    out.z += translate[2];
-    return out;
-  }
 };
 
 
diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp
index 18c9358..ea08bf4 100644
--- a/xbmc/guilib/GUIFontTTFGL.cpp
+++ b/xbmc/guilib/GUIFontTTFGL.cpp
@@ -29,6 +29,7 @@
 #include "utils/log.h"
 #include "utils/GLUtils.h"
 #include "windowing/WindowingFactory.h"
+#include "guilib/MatrixGLES.h"
 
 // stuff for freetype
 #include <ft2build.h>
@@ -166,6 +167,7 @@ void CGUIFontTTFGL::LastEnd()
   GLint posLoc  = g_Windowing.GUIShaderGetPos();
   GLint colLoc  = g_Windowing.GUIShaderGetCol();
   GLint tex0Loc = g_Windowing.GUIShaderGetCoord0();
+  GLint modelLoc = g_Windowing.GUIShaderGetModel();
 
   // Enable the attributes used by this shader
   glEnableVertexAttribArray(posLoc);
@@ -204,25 +206,35 @@ void CGUIFontTTFGL::LastEnd()
     std::vector<SVertex> vecVertices;
     for (size_t i = 0; i < m_vertexTrans.size(); i++)
     {
-      float translate[3] = { m_vertexTrans[i].translateX, m_vertexTrans[i].translateY, m_vertexTrans[i].translateZ };
+      // Apply the translation to the currently active (top-of-stack) model view matrix
+      g_matrices.MatrixMode(MM_MODELVIEW);
+      g_matrices.PushMatrix();
+      g_matrices.Translatef(m_vertexTrans[i].translateX, m_vertexTrans[i].translateY, m_vertexTrans[i].translateZ);
+      glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW));
+
+      vecVertices.clear();
       for (size_t j = 0; j < m_vertexTrans[i].vertexBuffer->size(); j += 4)
       {
-        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j].Offset(translate));
-        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1].Offset(translate));
-        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2].Offset(translate));
-        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1].Offset(translate));
-        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+3].Offset(translate));
-        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2].Offset(translate));
+        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j]);
+        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1]);
+        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2]);
+        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1]);
+        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+3]);
+        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2]);
       }
-    }
-    SVertex *vertices = &vecVertices[0];
+      SVertex *vertices = &vecVertices[0];
 
-    glVertexAttribPointer(posLoc,  3, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x));
-    // Normalize color values. Does not affect Performance at all.
-    glVertexAttribPointer(colLoc,  4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r));
-    glVertexAttribPointer(tex0Loc, 2, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u));
+      glVertexAttribPointer(posLoc,  3, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x));
+      // Normalize color values. Does not affect Performance at all.
+      glVertexAttribPointer(colLoc,  4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r));
+      glVertexAttribPointer(tex0Loc, 2, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u));
 
-    glDrawArrays(GL_TRIANGLES, 0, vecVertices.size());
+      glDrawArrays(GL_TRIANGLES, 0, vecVertices.size());
+
+      g_matrices.PopMatrix();
+    }
+    // Restore the original model view matrix
+    glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW));
   }
 
   // Disable the attributes used by this shader
diff --git a/xbmc/guilib/GUIShader.h b/xbmc/guilib/GUIShader.h
index fdf7452..abbe21c 100644
--- a/xbmc/guilib/GUIShader.h
+++ b/xbmc/guilib/GUIShader.h
@@ -39,6 +39,7 @@ class CGUIShader : public Shaders::CGLSLShaderProgram
   GLint GetCord1Loc() { return m_hCord1; }
   GLint GetUniColLoc() { return m_hUniCol; }
   GLint GetCoord0MatrixLoc() { return m_hCoord0Matrix; }
+  GLint GetModelLoc() { return m_hModel; }
   bool HardwareClipIsPossible() { return m_clipPossible; }
   GLfloat GetClipXFactor() { return m_clipXFactor; }
   GLfloat GetClipXOffset() { return m_clipXOffset; }
diff --git a/xbmc/rendering/gles/RenderSystemGLES.cpp b/xbmc/rendering/gles/RenderSystemGLES.cpp
index deb3afc..0904d1f 100644
--- a/xbmc/rendering/gles/RenderSystemGLES.cpp
+++ b/xbmc/rendering/gles/RenderSystemGLES.cpp
@@ -691,4 +691,12 @@ bool CRenderSystemGLES::SupportsStereo(RENDER_STEREO_MODE mode)
   }
 }
 
+GLint CRenderSystemGLES::GUIShaderGetModel()
+{
+  if (m_pGUIshader[m_method])
+    return m_pGUIshader[m_method]->GetModelLoc();
+
+  return -1;
+}
+
 #endif
diff --git a/xbmc/rendering/gles/RenderSystemGLES.h b/xbmc/rendering/gles/RenderSystemGLES.h
index 81ee49e..d2f9cd1 100644
--- a/xbmc/rendering/gles/RenderSystemGLES.h
+++ b/xbmc/rendering/gles/RenderSystemGLES.h
@@ -91,6 +91,7 @@ class CRenderSystemGLES : public CRenderSystemBase
   GLint GUIShaderGetCoord1();
   GLint GUIShaderGetUniCol();
   GLint GUIShaderGetCoord0Matrix();
+  GLint GUIShaderGetModel();
 
 protected:
   virtual void SetVSyncImpl(bool enable) = 0;

From cb15fabacab40e581453f16aa48a0707f66fc7f7 Mon Sep 17 00:00:00 2001
From: Ben Avison <bavison@riscosopen.org>
Date: Wed, 29 Jan 2014 13:21:19 +0000
Subject: [PATCH 22/88] Enable hardware clipping.

---
 xbmc/guilib/GUIFontTTF.cpp   | 4 ++--
 xbmc/guilib/GUIFontTTF.h     | 4 +++-
 xbmc/guilib/GUIFontTTFGL.cpp | 6 ++++++
 3 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp
index b1d00e5..ff51c89 100644
--- a/xbmc/guilib/GUIFontTTF.cpp
+++ b/xbmc/guilib/GUIFontTTF.cpp
@@ -483,10 +483,10 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
         cursorX += ch->advance;
     }
     if (hardwareClipping)
-      m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertices));
+      m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertices, g_graphicsContext.GetClipRegion()));
   }
   else if (hardwareClipping)
-    m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertices));
+    m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertices, g_graphicsContext.GetClipRegion()));
   if (!hardwareClipping)
     /* Append the new vertices (from the cache or otherwise) to the set collected
      * since the first Begin() call */
diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h
index 49222a4..77b324f 100644
--- a/xbmc/guilib/GUIFontTTF.h
+++ b/xbmc/guilib/GUIFontTTF.h
@@ -28,6 +28,7 @@
  */
 
 #include "utils/auto_buffer.h"
+#include "Geometry.h"
 
 // forward definition
 class CBaseTexture;
@@ -167,7 +168,8 @@ class CGUIFontTTFBase
     float translateY;
     float translateZ;
     const std::vector<SVertex> *vertexBuffer;
-    CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector<SVertex> *vertexBuffer) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer) {}
+    CRect clip;
+    CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector<SVertex> *vertexBuffer, const CRect &clip) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer), clip(clip) {}
   };
   std::vector<CTranslatedVertices> m_vertexTrans;
   std::vector<SVertex> m_vertex;
diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp
index ea08bf4..b63e337 100644
--- a/xbmc/guilib/GUIFontTTFGL.cpp
+++ b/xbmc/guilib/GUIFontTTFGL.cpp
@@ -206,6 +206,10 @@ void CGUIFontTTFGL::LastEnd()
     std::vector<SVertex> vecVertices;
     for (size_t i = 0; i < m_vertexTrans.size(); i++)
     {
+      // Apply the clip rectangle
+      CRect clip = g_Windowing.ClipRectToScissorRect(m_vertexTrans[i].clip);
+      g_Windowing.SetScissors(clip);
+
       // Apply the translation to the currently active (top-of-stack) model view matrix
       g_matrices.MatrixMode(MM_MODELVIEW);
       g_matrices.PushMatrix();
@@ -233,6 +237,8 @@ void CGUIFontTTFGL::LastEnd()
 
       g_matrices.PopMatrix();
     }
+    // Restore the original scissor rectangle
+    g_Windowing.ResetScissors();
     // Restore the original model view matrix
     glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW));
   }

From 40d4f5bfb89977f8c10a403ce3fe247af5ad9069 Mon Sep 17 00:00:00 2001
From: Ben Avison <bavison@riscosopen.org>
Date: Wed, 15 Jan 2014 15:32:51 +0000
Subject: [PATCH 23/88] Move the vertex data across to a vertex buffer object
 just prior to drawing.

---
 xbmc/guilib/GUIFontTTFGL.cpp | 24 +++++++++++++++++++-----
 1 file changed, 19 insertions(+), 5 deletions(-)

diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp
index b63e337..b00055d 100644
--- a/xbmc/guilib/GUIFontTTFGL.cpp
+++ b/xbmc/guilib/GUIFontTTFGL.cpp
@@ -228,12 +228,24 @@ void CGUIFontTTFGL::LastEnd()
       }
       SVertex *vertices = &vecVertices[0];
 
-      glVertexAttribPointer(posLoc,  3, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x));
-      // Normalize color values. Does not affect Performance at all.
-      glVertexAttribPointer(colLoc,  4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r));
-      glVertexAttribPointer(tex0Loc, 2, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u));
-
+      // Generate a unique buffer object name and put it in vertexBuffer
+      GLuint vertexBuffer;
+      glGenBuffers(1, &vertexBuffer);
+      // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point
+      glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
+      // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER
+      // binding point (i.e. our buffer object) and initialise it from the
+      // specified client-side pointer
+      glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof *vertices, vertices, GL_STATIC_DRAW);
+      // Set up the offsets of the various vertex attributes within the buffer
+      // object bound to GL_ARRAY_BUFFER
+      glVertexAttribPointer(posLoc,  3, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, x));
+      glVertexAttribPointer(colLoc,  4, GL_UNSIGNED_BYTE, GL_TRUE,  sizeof(SVertex), (GLvoid *) offsetof(SVertex, r));
+      glVertexAttribPointer(tex0Loc, 2, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, u));
+      // Do the actual drawing operation, using the full set of vertices in the buffer
       glDrawArrays(GL_TRIANGLES, 0, vecVertices.size());
+      // Release the buffer name for reuse
+      glDeleteBuffers(1, &vertexBuffer);
 
       g_matrices.PopMatrix();
     }
@@ -241,6 +253,8 @@ void CGUIFontTTFGL::LastEnd()
     g_Windowing.ResetScissors();
     // Restore the original model view matrix
     glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW));
+    // Unbind GL_ARRAY_BUFFER
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
   }
 
   // Disable the attributes used by this shader

From a5cbcbcfa9441f572b50ca04a22a7197d591cb61 Mon Sep 17 00:00:00 2001
From: Ben Avison <bavison@riscosopen.org>
Date: Wed, 15 Jan 2014 16:04:04 +0000
Subject: [PATCH 24/88] Move vertex data into an OpenGL VBO when the font cache
 entry is populated.

The font cache now stores the "name" (handle) of the VBO, rather than a vector
of vertices.
---
 xbmc/guilib/GUIFontCache.cpp |  6 ++++
 xbmc/guilib/GUIFontCache.h   | 30 +++++++++++++++++-
 xbmc/guilib/GUIFontTTF.cpp   | 15 +++++++--
 xbmc/guilib/GUIFontTTF.h     |  7 +++--
 xbmc/guilib/GUIFontTTFGL.cpp | 74 ++++++++++++++++++++++++++++++--------------
 xbmc/guilib/GUIFontTTFGL.h   |  5 +++
 6 files changed, 107 insertions(+), 30 deletions(-)

diff --git a/xbmc/guilib/GUIFontCache.cpp b/xbmc/guilib/GUIFontCache.cpp
index df466a5..bd84b9a 100644
--- a/xbmc/guilib/GUIFontCache.cpp
+++ b/xbmc/guilib/GUIFontCache.cpp
@@ -111,3 +111,9 @@ template void CGUIFontCacheEntry<CGUIFontCacheDynamicPosition, CGUIFontCacheDyna
 template CGUIFontCacheEntry<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::~CGUIFontCacheEntry();
 template CGUIFontCacheDynamicValue &CGUIFontCache<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::Lookup(CGUIFontCacheDynamicPosition &, const vecColors &, const vecText &, uint32_t, float, bool, unsigned int, bool &);
 template void CGUIFontCache<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::Flush();
+
+void CVertexBuffer::clear()
+{
+  if (m_font != NULL)
+    m_font->DestroyVertexBuffer(*this);
+}
diff --git a/xbmc/guilib/GUIFontCache.h b/xbmc/guilib/GUIFontCache.h
index d913dee..ff766bf 100644
--- a/xbmc/guilib/GUIFontCache.h
+++ b/xbmc/guilib/GUIFontCache.h
@@ -234,7 +234,35 @@ struct CGUIFontCacheDynamicPosition
   }
 };
 
-typedef std::vector<SVertex> CGUIFontCacheDynamicValue;
+struct CVertexBuffer
+{
+  void *bufferHandle;
+  size_t size;
+  CVertexBuffer() : bufferHandle(NULL), size(0), m_font(NULL) {}
+  CVertexBuffer(void *bufferHandle, size_t size, const CGUIFontTTFBase *font) : bufferHandle(bufferHandle), size(size), m_font(font) {}
+  CVertexBuffer(const CVertexBuffer &other) : bufferHandle(other.bufferHandle), size(other.size), m_font(other.m_font)
+  {
+    /* In practice, the copy constructor is only called before a vertex buffer
+     * has been attached. If this should ever change, we'll need another support
+     * function in GUIFontTTFGL/DX to duplicate a buffer, given its handle. */
+    assert(other.bufferHandle == 0);
+  }
+  CVertexBuffer &operator=(CVertexBuffer &other)
+  {
+    /* This is used with move-assignment semantics for initialising the object in the font cache */
+    assert(bufferHandle == 0);
+    bufferHandle = other.bufferHandle;
+    other.bufferHandle = 0;
+    size = other.size;
+    m_font = other.m_font;
+    return *this;
+  }
+  void clear();
+private:
+  const CGUIFontTTFBase *m_font;
+};
+
+typedef CVertexBuffer CGUIFontCacheDynamicValue;
 
 inline bool Match(const CGUIFontCacheDynamicPosition &a, const TransformMatrix &a_m,
                   const CGUIFontCacheDynamicPosition &b, const TransformMatrix &b_m,
diff --git a/xbmc/guilib/GUIFontTTF.cpp b/xbmc/guilib/GUIFontTTF.cpp
index ff51c89..7d4fc88 100644
--- a/xbmc/guilib/GUIFontTTF.cpp
+++ b/xbmc/guilib/GUIFontTTF.cpp
@@ -367,13 +367,18 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
                                               g_graphicsContext.ScaleFinalYCoord(x, y),
                                               g_graphicsContext.ScaleFinalZCoord(x, y));
   }
-  std::vector<SVertex> &vertices = hardwareClipping ?
+  CVertexBuffer unusedVertexBuffer;
+  CVertexBuffer &vertexBuffer = hardwareClipping ?
       m_dynamicCache.Lookup(dynamicPos,
                             colors, text,
                             alignment, maxPixelWidth,
                             scrolling,
                             XbmcThreads::SystemClockMillis(),
                             dirtyCache) :
+      unusedVertexBuffer;
+  std::vector<SVertex> tempVertices;
+  std::vector<SVertex> &vertices = hardwareClipping ?
+      tempVertices :
       m_staticCache.Lookup(staticPos,
                            colors, text,
                            alignment, maxPixelWidth,
@@ -483,10 +488,14 @@ void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors
         cursorX += ch->advance;
     }
     if (hardwareClipping)
-      m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertices, g_graphicsContext.GetClipRegion()));
+    {
+      CVertexBuffer newVertexBuffer = CreateVertexBuffer(tempVertices);
+      vertexBuffer = newVertexBuffer;
+      m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertexBuffer, g_graphicsContext.GetClipRegion()));
+    }
   }
   else if (hardwareClipping)
-    m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertices, g_graphicsContext.GetClipRegion()));
+    m_vertexTrans.push_back(CTranslatedVertices(dynamicPos.m_x, dynamicPos.m_y, dynamicPos.m_z, &vertexBuffer, g_graphicsContext.GetClipRegion()));
   if (!hardwareClipping)
     /* Append the new vertices (from the cache or otherwise) to the set collected
      * since the first Begin() call */
diff --git a/xbmc/guilib/GUIFontTTF.h b/xbmc/guilib/GUIFontTTF.h
index 77b324f..caa1e62 100644
--- a/xbmc/guilib/GUIFontTTF.h
+++ b/xbmc/guilib/GUIFontTTF.h
@@ -85,6 +85,9 @@ class CGUIFontTTFBase
 
   void Begin();
   void End();
+  /* The next two should only be called if we've declared we can do hardware clipping */
+  virtual CVertexBuffer CreateVertexBuffer(const std::vector<SVertex> &vertices) const { assert(false); return CVertexBuffer(); }
+  virtual void DestroyVertexBuffer(CVertexBuffer &bufferHandle) const {}
 
   const CStdString& GetFileName() const { return m_strFileName; };
 
@@ -167,9 +170,9 @@ class CGUIFontTTFBase
     float translateX;
     float translateY;
     float translateZ;
-    const std::vector<SVertex> *vertexBuffer;
+    const CVertexBuffer *vertexBuffer;
     CRect clip;
-    CTranslatedVertices(float translateX, float translateY, float translateZ, const std::vector<SVertex> *vertexBuffer, const CRect &clip) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer), clip(clip) {}
+    CTranslatedVertices(float translateX, float translateY, float translateZ, const CVertexBuffer *vertexBuffer, const CRect &clip) : translateX(translateX), translateY(translateY), translateZ(translateZ), vertexBuffer(vertexBuffer), clip(clip) {}
   };
   std::vector<CTranslatedVertices> m_vertexTrans;
   std::vector<SVertex> m_vertex;
diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp
index b00055d..aabb9a6 100644
--- a/xbmc/guilib/GUIFontTTFGL.cpp
+++ b/xbmc/guilib/GUIFontTTFGL.cpp
@@ -52,6 +52,10 @@ CGUIFontTTFGL::CGUIFontTTFGL(const CStdString& strFileName)
 
 CGUIFontTTFGL::~CGUIFontTTFGL(void)
 {
+  // It's important that all the CGUIFontCacheEntry objects are
+  // destructed before the CGUIFontTTFGL goes out of scope, because
+  // our virtual methods won't be accessible after this point
+  m_dynamicCache.Flush();
 }
 
 bool CGUIFontTTFGL::FirstBegin()
@@ -203,7 +207,6 @@ void CGUIFontTTFGL::LastEnd()
   if (m_vertexTrans.size() > 0)
   {
     // Deal with the vertices that can be hardware clipped and therefore translated
-    std::vector<SVertex> vecVertices;
     for (size_t i = 0; i < m_vertexTrans.size(); i++)
     {
       // Apply the clip rectangle
@@ -216,36 +219,17 @@ void CGUIFontTTFGL::LastEnd()
       g_matrices.Translatef(m_vertexTrans[i].translateX, m_vertexTrans[i].translateY, m_vertexTrans[i].translateZ);
       glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW));
 
-      vecVertices.clear();
-      for (size_t j = 0; j < m_vertexTrans[i].vertexBuffer->size(); j += 4)
-      {
-        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j]);
-        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1]);
-        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2]);
-        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+1]);
-        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+3]);
-        vecVertices.push_back((*m_vertexTrans[i].vertexBuffer)[j+2]);
-      }
-      SVertex *vertices = &vecVertices[0];
-
-      // Generate a unique buffer object name and put it in vertexBuffer
-      GLuint vertexBuffer;
-      glGenBuffers(1, &vertexBuffer);
       // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point
-      glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
-      // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER
-      // binding point (i.e. our buffer object) and initialise it from the
-      // specified client-side pointer
-      glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof *vertices, vertices, GL_STATIC_DRAW);
+      glBindBuffer(GL_ARRAY_BUFFER, (GLuint) m_vertexTrans[i].vertexBuffer->bufferHandle);
+
       // Set up the offsets of the various vertex attributes within the buffer
       // object bound to GL_ARRAY_BUFFER
       glVertexAttribPointer(posLoc,  3, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, x));
       glVertexAttribPointer(colLoc,  4, GL_UNSIGNED_BYTE, GL_TRUE,  sizeof(SVertex), (GLvoid *) offsetof(SVertex, r));
       glVertexAttribPointer(tex0Loc, 2, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, u));
+
       // Do the actual drawing operation, using the full set of vertices in the buffer
-      glDrawArrays(GL_TRIANGLES, 0, vecVertices.size());
-      // Release the buffer name for reuse
-      glDeleteBuffers(1, &vertexBuffer);
+      glDrawArrays(GL_TRIANGLES, 0, 6 * m_vertexTrans[i].vertexBuffer->size);
 
       g_matrices.PopMatrix();
     }
@@ -266,6 +250,48 @@ void CGUIFontTTFGL::LastEnd()
 #endif
 }
 
+#if HAS_GLES
+CVertexBuffer CGUIFontTTFGL::CreateVertexBuffer(const std::vector<SVertex> &vertices) const
+{
+  // Rearrange the vertices to describe triangles
+  std::vector<SVertex> triangleVertices;
+  triangleVertices.reserve(vertices.size() * 6 / 4);
+  for (size_t i = 0; i < vertices.size(); i += 4)
+  {
+    triangleVertices.push_back(vertices[i]);
+    triangleVertices.push_back(vertices[i+1]);
+    triangleVertices.push_back(vertices[i+2]);
+    triangleVertices.push_back(vertices[i+1]);
+    triangleVertices.push_back(vertices[i+3]);
+    triangleVertices.push_back(vertices[i+2]);
+  }
+
+  // Generate a unique buffer object name and put it in bufferHandle
+  GLuint bufferHandle;
+  glGenBuffers(1, &bufferHandle);
+  // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point
+  glBindBuffer(GL_ARRAY_BUFFER, bufferHandle);
+  // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER
+  // binding point (i.e. our buffer object) and initialise it from the
+  // specified client-side pointer
+  glBufferData(GL_ARRAY_BUFFER, triangleVertices.size() * sizeof (SVertex), &triangleVertices[0], GL_STATIC_DRAW);
+  // Unbind GL_ARRAY_BUFFER
+  glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+  return CVertexBuffer((void *) bufferHandle, vertices.size() / 4, this);
+}
+
+void CGUIFontTTFGL::DestroyVertexBuffer(CVertexBuffer &buffer) const
+{
+  if (buffer.bufferHandle != 0)
+  {
+    // Release the buffer name for reuse
+    glDeleteBuffers(1, (GLuint *) &buffer.bufferHandle);
+    buffer.bufferHandle = 0;
+  }
+}
+#endif
+
 CBaseTexture* CGUIFontTTFGL::ReallocTexture(unsigned int& newHeight)
 {
   newHeight = CBaseTexture::PadPow2(newHeight);
diff --git a/xbmc/guilib/GUIFontTTFGL.h b/xbmc/guilib/GUIFontTTFGL.h
index 735fb3a..6102c90 100644
--- a/xbmc/guilib/GUIFontTTFGL.h
+++ b/xbmc/guilib/GUIFontTTFGL.h
@@ -29,6 +29,7 @@
 
 
 #include "GUIFontTTF.h"
+#include "system.h"
 
 
 /*!
@@ -43,6 +44,10 @@ class CGUIFontTTFGL : public CGUIFontTTFBase
 
   virtual bool FirstBegin();
   virtual void LastEnd();
+#if HAS_GLES
+  virtual CVertexBuffer CreateVertexBuffer(const std::vector<SVertex> &vertices) const;
+  virtual void DestroyVertexBuffer(CVertexBuffer &bufferHandle) const;
+#endif
 
 protected:
   virtual CBaseTexture* ReallocTexture(unsigned int& newHeight);

From a4a06da228796c5b244bcf74debe3fee530bf6eb Mon Sep 17 00:00:00 2001
From: Ben Avison <bavison@riscosopen.org>
Date: Thu, 16 Jan 2014 16:29:42 +0000
Subject: [PATCH 25/88] Switch from glDrawArrays() to glDrawElements().

This involves setting up a static VBO containing the indexes necessary to
convert from quads to triangles on the fly in the GPU.
---
 xbmc/guilib/GUIFontTTFGL.cpp        | 72 +++++++++++++++++++++++++------------
 xbmc/guilib/GUIFontTTFGL.h          | 11 +++++-
 xbmc/windowing/egl/WinSystemEGL.cpp | 17 +++++++++
 3 files changed, 77 insertions(+), 23 deletions(-)

diff --git a/xbmc/guilib/GUIFontTTFGL.cpp b/xbmc/guilib/GUIFontTTFGL.cpp
index aabb9a6..812662c 100644
--- a/xbmc/guilib/GUIFontTTFGL.cpp
+++ b/xbmc/guilib/GUIFontTTFGL.cpp
@@ -207,6 +207,10 @@ void CGUIFontTTFGL::LastEnd()
   if (m_vertexTrans.size() > 0)
   {
     // Deal with the vertices that can be hardware clipped and therefore translated
+
+    // Bind our pre-calculated array to GL_ELEMENT_ARRAY_BUFFER
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementArrayHandle);
+
     for (size_t i = 0; i < m_vertexTrans.size(); i++)
     {
       // Apply the clip rectangle
@@ -222,14 +226,21 @@ void CGUIFontTTFGL::LastEnd()
       // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point
       glBindBuffer(GL_ARRAY_BUFFER, (GLuint) m_vertexTrans[i].vertexBuffer->bufferHandle);
 
-      // Set up the offsets of the various vertex attributes within the buffer
-      // object bound to GL_ARRAY_BUFFER
-      glVertexAttribPointer(posLoc,  3, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, x));
-      glVertexAttribPointer(colLoc,  4, GL_UNSIGNED_BYTE, GL_TRUE,  sizeof(SVertex), (GLvoid *) offsetof(SVertex, r));
-      glVertexAttribPointer(tex0Loc, 2, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (GLvoid *) offsetof(SVertex, u));
+      // Do the actual drawing operation, split into groups of characters no
+      // larger than the pre-determined size of the element array
+      for (size_t character = 0; m_vertexTrans[i].vertexBuffer->size > character; character += ELEMENT_ARRAY_MAX_CHAR_INDEX)
+      {
+        size_t count = m_vertexTrans[i].vertexBuffer->size - character;
+        count = std::min<size_t>(count, ELEMENT_ARRAY_MAX_CHAR_INDEX);
+
+        // Set up the offsets of the various vertex attributes within the buffer
+        // object bound to GL_ARRAY_BUFFER
+        glVertexAttribPointer(posLoc,  3, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (GLvoid *) (character*sizeof(SVertex)*4 + offsetof(SVertex, x)));
+        glVertexAttribPointer(colLoc,  4, GL_UNSIGNED_BYTE, GL_TRUE,  sizeof(SVertex), (GLvoid *) (character*sizeof(SVertex)*4 + offsetof(SVertex, r)));
+        glVertexAttribPointer(tex0Loc, 2, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (GLvoid *) (character*sizeof(SVertex)*4 + offsetof(SVertex, u)));
 
-      // Do the actual drawing operation, using the full set of vertices in the buffer
-      glDrawArrays(GL_TRIANGLES, 0, 6 * m_vertexTrans[i].vertexBuffer->size);
+        glDrawElements(GL_TRIANGLES, 6 * count, GL_UNSIGNED_SHORT, 0);
+      }
 
       g_matrices.PopMatrix();
     }
@@ -237,8 +248,9 @@ void CGUIFontTTFGL::LastEnd()
     g_Windowing.ResetScissors();
     // Restore the original model view matrix
     glUniformMatrix4fv(modelLoc, 1, GL_FALSE, g_matrices.GetMatrix(MM_MODELVIEW));
-    // Unbind GL_ARRAY_BUFFER
+    // Unbind GL_ARRAY_BUFFER and GL_ELEMENT_ARRAY_BUFFER
     glBindBuffer(GL_ARRAY_BUFFER, 0);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
   }
 
   // Disable the attributes used by this shader
@@ -253,19 +265,6 @@ void CGUIFontTTFGL::LastEnd()
 #if HAS_GLES
 CVertexBuffer CGUIFontTTFGL::CreateVertexBuffer(const std::vector<SVertex> &vertices) const
 {
-  // Rearrange the vertices to describe triangles
-  std::vector<SVertex> triangleVertices;
-  triangleVertices.reserve(vertices.size() * 6 / 4);
-  for (size_t i = 0; i < vertices.size(); i += 4)
-  {
-    triangleVertices.push_back(vertices[i]);
-    triangleVertices.push_back(vertices[i+1]);
-    triangleVertices.push_back(vertices[i+2]);
-    triangleVertices.push_back(vertices[i+1]);
-    triangleVertices.push_back(vertices[i+3]);
-    triangleVertices.push_back(vertices[i+2]);
-  }
-
   // Generate a unique buffer object name and put it in bufferHandle
   GLuint bufferHandle;
   glGenBuffers(1, &bufferHandle);
@@ -274,7 +273,7 @@ CVertexBuffer CGUIFontTTFGL::CreateVertexBuffer(const std::vector<SVertex> &vert
   // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER
   // binding point (i.e. our buffer object) and initialise it from the
   // specified client-side pointer
-  glBufferData(GL_ARRAY_BUFFER, triangleVertices.size() * sizeof (SVertex), &triangleVertices[0], GL_STATIC_DRAW);
+  glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof (SVertex), &vertices[0], GL_STATIC_DRAW);
   // Unbind GL_ARRAY_BUFFER
   glBindBuffer(GL_ARRAY_BUFFER, 0);
 
@@ -393,4 +392,33 @@ void CGUIFontTTFGL::DeleteHardwareTexture()
   }
 }
 
+#if HAS_GLES
+void CGUIFontTTFGL::CreateStaticVertexBuffers(void)
+{
+  // Bind a new buffer to the OpenGL context's GL_ELEMENT_ARRAY_BUFFER binding point
+  glGenBuffers(1, &m_elementArrayHandle);
+  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementArrayHandle);
+  // Create an array holding the mesh indices to convert quads to triangles
+  GLushort index[ELEMENT_ARRAY_MAX_CHAR_INDEX][6];
+  for (size_t i = 0; i < ELEMENT_ARRAY_MAX_CHAR_INDEX; i++)
+  {
+    index[i][0] = 4*i;
+    index[i][1] = 4*i+1;
+    index[i][2] = 4*i+2;
+    index[i][3] = 4*i+1;
+    index[i][4] = 4*i+3;
+    index[i][5] = 4*i+2;
+  }
+  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof index, index, GL_STATIC_DRAW);
+  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+void CGUIFontTTFGL::DestroyStaticVertexBuffers(void)
+{
+  glDeleteBuffers(1, &m_elementArrayHandle);
+}
+
+GLuint CGUIFontTTFGL::m_elementArrayHandle;
+#endif
+
 #endif
diff --git a/xbmc/guilib/GUIFontTTFGL.h b/xbmc/guilib/GUIFontTTFGL.h
index 6102c90..dcf9ca1 100644
--- a/xbmc/guilib/GUIFontTTFGL.h
+++ b/xbmc/guilib/GUIFontTTFGL.h
@@ -30,6 +30,7 @@
 
 #include "GUIFontTTF.h"
 #include "system.h"
+#include "system_gl.h"
 
 
 /*!
@@ -47,13 +48,21 @@ class CGUIFontTTFGL : public CGUIFontTTFBase
 #if HAS_GLES
   virtual CVertexBuffer CreateVertexBuffer(const std::vector<SVertex> &vertices) const;
   virtual void DestroyVertexBuffer(CVertexBuffer &bufferHandle) const;
+  static void CreateStaticVertexBuffers(void);
+  static void DestroyStaticVertexBuffers(void);
 #endif
 
 protected:
   virtual CBaseTexture* ReallocTexture(unsigned int& newHeight);
   virtual bool CopyCharToTexture(FT_BitmapGlyph bitGlyph, unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2);
   virtual void DeleteHardwareTexture();
-    
+
+#if HAS_GLES
+#define ELEMENT_ARRAY_MAX_CHAR_INDEX (1000)
+
+  static GLuint m_elementArrayHandle;
+#endif
+
 private:
   unsigned int m_updateY1;
   unsigned int m_updateY2;
diff --git a/xbmc/windowing/egl/WinSystemEGL.cpp b/xbmc/windowing/egl/WinSystemEGL.cpp
index 6de3532..258a293 100644
--- a/xbmc/windowing/egl/WinSystemEGL.cpp
+++ b/xbmc/windowing/egl/WinSystemEGL.cpp
@@ -29,6 +29,7 @@
 #include "settings/AdvancedSettings.h"
 #include "settings/Settings.h"
 #include "settings/DisplaySettings.h"
+#include "guilib/GUIFontTTFGL.h"
 #include "utils/log.h"
 #include "EGLWrapper.h"
 #include "EGLQuirks.h"
@@ -193,6 +194,9 @@ bool CWinSystemEGL::CreateWindow(RESOLUTION_INFO &res)
     return false;
   }
 
+#if HAS_GLES
+  bool newContext = false;
+#endif
   if (m_context == EGL_NO_CONTEXT)
   {
     if (!m_egl->CreateContext(m_display, m_config, contextAttrs, &m_context))
@@ -200,6 +204,9 @@ bool CWinSystemEGL::CreateWindow(RESOLUTION_INFO &res)
       CLog::Log(LOGERROR, "%s: Could not create context",__FUNCTION__);
       return false;
     }
+#if HAS_GLES
+    newContext = true;
+#endif
   }
 
   if (!m_egl->BindContext(m_display, m_surface, m_context))
@@ -208,6 +215,11 @@ bool CWinSystemEGL::CreateWindow(RESOLUTION_INFO &res)
     return false;
   }
 
+#if HAS_GLES
+  if (newContext)
+    CGUIFontTTFGL::CreateStaticVertexBuffers();
+#endif
+
   // for the non-trivial dirty region modes, we need the EGL buffer to be preserved across updates
   if (g_advancedSettings.m_guiAlgorithmDirtyRegions == DIRTYREGION_SOLVER_COST_REDUCTION ||
       g_advancedSettings.m_guiAlgorithmDirtyRegions == DIRTYREGION_SOLVER_UNION)
@@ -229,7 +241,12 @@ bool CWinSystemEGL::DestroyWindowSystem()
   DestroyWindow();
 
   if (m_context != EGL_NO_CONTEXT)
+  {
+#if HAS_GLES
+    CGUIFontTTFGL::DestroyStaticVertexBuffers();
+#endif
     m_egl->DestroyContext(m_display, m_context);
+  }
   m_context = EGL_NO_CONTEXT;
 
   if (m_display != EGL_NO_DISPLAY)

From c71017ac871e463931aa15b98f9c85adb4e03bed Mon Sep 17 00:00:00 2001
From: Ben Avison <bavison@riscosopen.org>
Date: Thu, 1 May 2014 16:28:39 +0100
Subject: [PATCH 26/88] Improved file buffering in CArchive

Even though memcpy is typically inlined by the compiler into byte/word loads
and stores (at least for release builds), the frequency with which 1, 2 and 4
byte loads/stores are encountered in cases where the size is *not*
determinable at compile time is still high enough that it's worth handling
these specially. On the ARM1176JZF-S in the Raspberry Pi, this improves the
total time to open a library (in the case where it's fetched from a CArchive)
by around 4%.

It should be noted that this code uses 16-bit and 32-bit word loads and
stores that are not necessarily aligned to their respective widths. It is
possible that there are some architectures out there which do not support
this, although all ARMs since ARMv6 have supported it (and ARMs earlier than
that are probably not powerful enough to be good targets for XBMC).
---
 xbmc/utils/Archive.h | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/xbmc/utils/Archive.h b/xbmc/utils/Archive.h
index 6ed0f8f..8506d95 100644
--- a/xbmc/utils/Archive.h
+++ b/xbmc/utils/Archive.h
@@ -154,9 +154,17 @@ class CArchive
      * than waiting until we attempt to put more data into an already full buffer */
     if (m_BufferRemain > size)
     {
+      switch (size)
+      {
+      case 1: *m_BufferPos++ = *ptr; m_BufferRemain--; break;
+      case 2: *(uint16_t *) m_BufferPos = *(const uint16_t *) ptr; m_BufferPos += 2; m_BufferRemain -= 2; break;
+      case 4: *(uint32_t *) m_BufferPos = *(const uint32_t *) ptr; m_BufferPos += 4; m_BufferRemain -= 4; break;
+      default:
       memcpy(m_BufferPos, ptr, size);
       m_BufferPos += size;
       m_BufferRemain -= size;
+      break;
+      }
       return *this;
     }
     else
@@ -171,9 +179,17 @@ class CArchive
     /* Note, refilling the buffer is deferred until we know we need to read more from it */
     if (m_BufferRemain >= size)
     {
+      switch (size)
+      {
+      case 1: *ptr = *m_BufferPos++; m_BufferRemain--; break;
+      case 2: *(uint16_t *) ptr = *(const uint16_t *) m_BufferPos; m_BufferPos += 2; m_BufferRemain -= 2; break;
+      case 4: *(uint32_t *) ptr = *(const uint32_t *) m_BufferPos; m_BufferPos += 4; m_BufferRemain -= 4; break;
+      default:
       memcpy(ptr, m_BufferPos, size);
       m_BufferPos += size;
       m_BufferRemain -= size;
+      break;
+      }
       return *this;
     }
     else

From 80dccee914caa93facbbc614f3b4a5763a9f6685 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Wed, 10 Sep 2014 22:07:21 +0100
Subject: [PATCH 27/88] [mmal] Allow mmal codec for dvd stills

---
 xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp
index f139433..4183a2b 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp
+++ b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp
@@ -192,6 +192,10 @@ CDVDVideoCodec* CDVDFactoryCodec::CreateVideoCodec(CDVDStreamInfo &hint, unsigne
 #endif
   CLog::Log(LOGDEBUG, "CDVDFactoryCodec: compiled in hardware support: %s", hwSupport.c_str());
 
+#if defined(HAS_MMAL)
+  // mmal can handle dvd playback including stills
+  if (!CSettings::Get().GetBool("videoplayer.usemmal"))
+#endif
   if (hint.stills && (hint.codec == AV_CODEC_ID_MPEG2VIDEO || hint.codec == AV_CODEC_ID_MPEG1VIDEO))
   {
      // If dvd is an mpeg2 and hint.stills

From 0cf6e135b6f83dd8d3ba0352e1e118749eebe997 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sat, 2 Aug 2014 17:47:38 +0100
Subject: [PATCH 29/88] [VideoReferenceClock] Add OMX support

---
 xbmc/linux/RBP.cpp                 | 34 ++++++++++++++++++++++++
 xbmc/linux/RBP.h                   |  3 +++
 xbmc/video/VideoReferenceClock.cpp | 53 ++++++++++++++++++++++++++++++++++++++
 xbmc/video/VideoReferenceClock.h   |  6 +++++
 4 files changed, 96 insertions(+)

diff --git a/xbmc/linux/RBP.cpp b/xbmc/linux/RBP.cpp
index 73a42c4..11376fc 100644
--- a/xbmc/linux/RBP.cpp
+++ b/xbmc/linux/RBP.cpp
@@ -32,6 +32,7 @@ CRBP::CRBP()
   m_omx_initialized = false;
   m_DllBcmHost      = new DllBcmHost();
   m_OMX             = new COMXCore();
+  m_element = 0;
 }
 
 CRBP::~CRBP()
@@ -53,6 +54,9 @@ bool CRBP::Initialize()
 
   m_DllBcmHost->bcm_host_init();
 
+  uint32_t                    vc_image_ptr;
+  m_resource = vc_dispmanx_resource_create( VC_IMAGE_RGB565, 1, 1, &vc_image_ptr );
+
   m_omx_initialized = m_OMX->Initialize();
   if(!m_omx_initialized)
     return false;
@@ -156,6 +160,36 @@ unsigned char *CRBP::CaptureDisplay(int width, int height, int *pstride, bool sw
   return image;
 }
 
+void CRBP::WaitVsync(void)
+{
+  DISPMANX_DISPLAY_HANDLE_T display = vc_dispmanx_display_open( 0 /*screen*/ );
+  DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0);
+
+  VC_DISPMANX_ALPHA_T alpha = { (DISPMANX_FLAGS_ALPHA_T)(DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS), 120, /*alpha 0->255*/ 0 };
+  VC_RECT_T       src_rect;
+  VC_RECT_T       dst_rect;
+  vc_dispmanx_rect_set( &src_rect, 0, 0, 1 << 16, 1 << 16 );
+  vc_dispmanx_rect_set( &dst_rect, 0, 0, 1, 1 );
+
+  if (m_element)
+    vc_dispmanx_element_remove( update, m_element );
+
+  m_element = vc_dispmanx_element_add( update,
+                                            display,
+                                            2000,               // layer
+                                            &dst_rect,
+                                            m_resource,
+                                            &src_rect,
+                                            DISPMANX_PROTECTION_NONE,
+                                            &alpha,
+                                            NULL,             // clamp
+                                            (DISPMANX_TRANSFORM_T)0 );
+
+  vc_dispmanx_update_submit_sync(update);
+  vc_dispmanx_display_close( display );
+}
+
+
 void CRBP::Deinitialize()
 {
   if (m_omx_image_init)
diff --git a/xbmc/linux/RBP.h b/xbmc/linux/RBP.h
index e9a2d5a..4fd18f3 100644
--- a/xbmc/linux/RBP.h
+++ b/xbmc/linux/RBP.h
@@ -57,6 +57,7 @@ class CRBP
   // stride can be null for packed output
   unsigned char *CaptureDisplay(int width, int height, int *stride, bool swap_red_blue, bool video_only = true);
   DllOMX *GetDllOMX() { return m_OMX ? m_OMX->GetDll() : NULL; }
+  void WaitVsync();
 
 private:
   DllBcmHost *m_DllBcmHost;
@@ -69,6 +70,8 @@ class CRBP
   bool       m_codec_mpg2_enabled;
   bool       m_codec_wvc1_enabled;
   COMXCore   *m_OMX;
+  DISPMANX_RESOURCE_HANDLE_T m_resource;
+  DISPMANX_ELEMENT_HANDLE_T m_element;
   class DllLibOMXCore;
   CCriticalSection m_critSection;
 };
diff --git a/xbmc/video/VideoReferenceClock.cpp b/xbmc/video/VideoReferenceClock.cpp
index 6d84b60..916a15c 100644
--- a/xbmc/video/VideoReferenceClock.cpp
+++ b/xbmc/video/VideoReferenceClock.cpp
@@ -50,6 +50,9 @@
   #endif
   #include "windowing/WindowingFactory.h"
   #include "settings/AdvancedSettings.h"
+#elif defined(TARGET_RASPBERRY_PI)
+  #include "guilib/GraphicContext.h"
+  #include "linux/RBP.h"
 #endif
 
 using namespace std;
@@ -177,6 +180,8 @@ void CVideoReferenceClock::Process()
     CLog::Log(LOGDEBUG, "CVideoReferenceClock: compiled without RandR support");
 #elif defined(TARGET_WINDOWS)
     CLog::Log(LOGDEBUG, "CVideoReferenceClock: only available on directx build");
+#elif defined(TARGET_RASPBERRY_PI)
+    SetupSuccess = SetupOMX();
 #else
     CLog::Log(LOGDEBUG, "CVideoReferenceClock: no implementation available");
 #endif
@@ -205,6 +210,8 @@ void CVideoReferenceClock::Process()
       RunD3D();
 #elif defined(TARGET_DARWIN)
       RunCocoa();
+#elif defined(TARGET_RASPBERRY_PI)
+      RunOMX();
 #endif
 
     }
@@ -237,6 +244,8 @@ void CVideoReferenceClock::Process()
     CleanupD3D();
 #elif defined(TARGET_DARWIN)
     CleanupCocoa();
+#elif defined(TARGET_RASPBERRY_PI)
+    CleanupOMX();
 #endif
     if (!SetupSuccess) break;
   }
@@ -863,6 +872,50 @@ void CVideoReferenceClock::VblankHandler(int64_t nowtime, double fps)
   SendVblankSignal();
   UpdateRefreshrate();
 }
+#elif defined(TARGET_RASPBERRY_PI)
+bool CVideoReferenceClock::SetupOMX()
+{
+  CLog::Log(LOGDEBUG, "CVideoReferenceClock: setting up OMX");
+
+  //init the vblank timestamp
+  m_MissedVblanks = 0;
+  m_RefreshRate = g_graphicsContext.GetFPS();
+
+  UpdateRefreshrate(true);
+  return true;
+}
+
+void CVideoReferenceClock::RunOMX()
+{
+  CSingleLock SingleLock(m_CritSection);
+  SingleLock.Leave();
+
+  while(!m_bStop)
+  {
+    g_RBP.WaitVsync();
+
+    m_RefreshRate = g_graphicsContext.GetFPS();
+    CLog::Log(LOGDEBUG, "CVideoReferenceClock:%s fps:%.2f missed:%d Time:%.3f", __func__, m_RefreshRate, m_TotalMissedVblanks, m_CurrTime * 1e-6);
+    //update the vblank timestamp, update the clock and send a signal that we got a vblank
+    SingleLock.Enter();
+    m_VblankTime = CurrentHostCounter();
+    UpdateClock(1, true);
+    SingleLock.Leave();
+    SendVblankSignal();
+
+    if (UpdateRefreshrate())
+    {
+      //we have to measure the refreshrate again
+      CLog::Log(LOGDEBUG, "CVideoReferenceClock: Displaymode changed");
+      return;
+    }
+  }
+}
+
+void CVideoReferenceClock::CleanupOMX()
+{
+  CLog::Log(LOGDEBUG, "CVideoReferenceClock: cleaning up OMX");
+}
 #endif
 
 //this is called from the vblank run function and from CVideoReferenceClock::Wait in case of a late update
diff --git a/xbmc/video/VideoReferenceClock.h b/xbmc/video/VideoReferenceClock.h
index 6027031..2dabac1 100644
--- a/xbmc/video/VideoReferenceClock.h
+++ b/xbmc/video/VideoReferenceClock.h
@@ -150,6 +150,12 @@ class CVideoReferenceClock : public CThread
 
     int64_t m_LastVBlankTime;  //timestamp of the last vblank, used for calculating how many vblanks happened
                                //not the same as m_VblankTime
+#elif defined(TARGET_RASPBERRY_PI)
+    bool SetupOMX();
+    void RunOMX();
+    void CleanupOMX();
+    int64_t m_LastVBlankTime;  //timestamp of the last vblank, used for calculating how many vblanks happened
+                               //not the same as m_VblankTime
 #endif
 };
 

From 438669fbe7069d7237fbcad6bc72fd7a8d5889ab Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Tue, 12 Aug 2014 00:03:18 +0100
Subject: [PATCH 30/88] videoreferenceclock: Boost priority

---
 xbmc/video/VideoReferenceClock.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/xbmc/video/VideoReferenceClock.cpp b/xbmc/video/VideoReferenceClock.cpp
index 916a15c..c491d29 100644
--- a/xbmc/video/VideoReferenceClock.cpp
+++ b/xbmc/video/VideoReferenceClock.cpp
@@ -157,6 +157,9 @@ void CVideoReferenceClock::Process()
   bool SetupSuccess = false;
   int64_t Now;
 
+  /* This shouldn't be very busy and timing is important so increase priority */
+  SetPriority(GetPriority()+1);
+
 #if defined(TARGET_WINDOWS) && defined(HAS_DX)
   //register callback
   m_D3dCallback.Reset();

From 32863b916ebb3f59b555e8560e1763b09c1fe5b1 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Wed, 5 Feb 2014 11:46:33 +0000
Subject: [PATCH 31/88] [rbp/settings] Allow av sync type to be enabled

It works for dvdplayer
---
 system/settings/rbp.xml | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/system/settings/rbp.xml b/system/settings/rbp.xml
index 319d9e5..e80ad91 100644
--- a/system/settings/rbp.xml
+++ b/system/settings/rbp.xml
@@ -1,13 +1,6 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <settings>
   <section id="videos">
-    <category id="videoplayer">
-      <group id="2">
-        <setting id="videoplayer.synctype">
-          <visible>false</visible>
-        </setting>
-      </group>
-    </category>
     <category id="videoacceleration">
       <group id="1">
         <visible>false</visible>

From 8a3bef46ae9e92ae3e739ce8962b1c6580c3d760 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Wed, 16 Apr 2014 21:18:06 +0100
Subject: [PATCH 33/88] [omxplayer] Don't propagate 3d flags based on supported
 3d modes

---
 xbmc/cores/omxplayer/OMXPlayerVideo.cpp | 29 ++++-------------------------
 1 file changed, 4 insertions(+), 25 deletions(-)

diff --git a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
index 2c25fd9..c2bd788 100644
--- a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
+++ b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
@@ -759,36 +759,15 @@ void OMXPlayerVideo::ResolutionUpdateCallBack(uint32_t width, uint32_t height, f
   unsigned flags = 0;
   ERenderFormat format = RENDER_FMT_BYPASS;
 
+  /* figure out steremode expected based on user settings and hints */
+  unsigned int stereo_flags = GetStereoModeFlags(GetStereoMode());
+
   if(m_bAllowFullscreen)
   {
     flags |= CONF_FLAGS_FULLSCREEN;
     m_bAllowFullscreen = false; // only allow on first configure
   }
-
-  flags |= GetStereoModeFlags(GetStereoMode());
-
-  if(flags & CONF_FLAGS_STEREO_MODE_SBS)
-  {
-    if(g_Windowing.Support3D(video_width, video_height, D3DPRESENTFLAG_MODE3DSBS))
-      CLog::Log(LOGNOTICE, "3DSBS movie found");
-    else
-    {
-      flags &= ~CONF_FLAGS_STEREO_MODE_MASK(~0);
-      CLog::Log(LOGNOTICE, "3DSBS movie found but not supported");
-    }
-  }
-  else if(flags & CONF_FLAGS_STEREO_MODE_TAB)
-  {
-    if(g_Windowing.Support3D(video_width, video_height, D3DPRESENTFLAG_MODE3DTB))
-      CLog::Log(LOGNOTICE, "3DTB movie found");
-    else
-    {
-      flags &= ~CONF_FLAGS_STEREO_MODE_MASK(~0);
-      CLog::Log(LOGNOTICE, "3DTB movie found but not supported");
-    }
-  }
-  else
-    CLog::Log(LOGNOTICE, "not a 3D movie");
+  flags |= stereo_flags;
 
   unsigned int iDisplayWidth  = width;
   unsigned int iDisplayHeight = height;

From 461db3a5d935997a7bfcd504ef18fccba4ce52a1 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Thu, 17 Apr 2014 13:00:52 +0100
Subject: [PATCH 34/88] [graphics] Don't set stereo mode based on resolution

The resolution change should follow stereo mode
---
 xbmc/guilib/GraphicContext.cpp | 35 +++++++++++++++++++----------------
 1 file changed, 19 insertions(+), 16 deletions(-)

diff --git a/xbmc/guilib/GraphicContext.cpp b/xbmc/guilib/GraphicContext.cpp
index 282792c..f1926de 100644
--- a/xbmc/guilib/GraphicContext.cpp
+++ b/xbmc/guilib/GraphicContext.cpp
@@ -436,26 +436,29 @@ void CGraphicContext::SetVideoResolutionInternal(RESOLUTION res, bool forceUpdat
   RESOLUTION_INFO info_org  = CDisplaySettings::Get().GetResolutionInfo(res);
   RESOLUTION_INFO info_last = CDisplaySettings::Get().GetResolutionInfo(lastRes);
 
-  RENDER_STEREO_MODE stereo_mode = m_stereoMode;
-
   // if the new mode is an actual stereo mode, switch to that
   // if the old mode was an actual stereo mode, switch to no 3d mode
-  if (info_org.dwFlags & D3DPRESENTFLAG_MODE3DTB)
-    stereo_mode = RENDER_STEREO_MODE_SPLIT_HORIZONTAL;
-  else if (info_org.dwFlags & D3DPRESENTFLAG_MODE3DSBS)
-    stereo_mode = RENDER_STEREO_MODE_SPLIT_VERTICAL;
-  else if ((info_last.dwFlags & D3DPRESENTFLAG_MODE3DSBS) != 0
-        || (info_last.dwFlags & D3DPRESENTFLAG_MODE3DTB)  != 0)
-    stereo_mode = RENDER_STEREO_MODE_OFF;
-
-  if(stereo_mode != m_stereoMode)
+  // only do this if 3D flags have changed
+  if ((info_org.dwFlags ^ info_last.dwFlags) & (D3DPRESENTFLAG_MODE3DTB | D3DPRESENTFLAG_MODE3DSBS))
   {
-    m_stereoView     = RENDER_STEREO_VIEW_OFF;
-    m_stereoMode     = stereo_mode;
-    m_nextStereoMode = stereo_mode;
-    CSettings::Get().SetInt("videoscreen.stereoscopicmode", (int)m_stereoMode);
-  }
+    RENDER_STEREO_MODE stereo_mode = m_stereoMode;
+
+    if (info_org.dwFlags & D3DPRESENTFLAG_MODE3DTB)
+      stereo_mode = RENDER_STEREO_MODE_SPLIT_HORIZONTAL;
+    else if (info_org.dwFlags & D3DPRESENTFLAG_MODE3DSBS)
+      stereo_mode = RENDER_STEREO_MODE_SPLIT_VERTICAL;
+    else if ((info_last.dwFlags & D3DPRESENTFLAG_MODE3DSBS) != 0
+          || (info_last.dwFlags & D3DPRESENTFLAG_MODE3DTB)  != 0)
+      stereo_mode = RENDER_STEREO_MODE_OFF;
 
+    if(stereo_mode != m_stereoMode)
+    {
+      m_stereoView     = RENDER_STEREO_VIEW_OFF;
+      m_stereoMode     = stereo_mode;
+      m_nextStereoMode = stereo_mode;
+      CSettings::Get().SetInt("videoscreen.stereoscopicmode", (int)m_stereoMode);
+    }
+  }
   RESOLUTION_INFO info_mod = GetResInfo(res);
 
   m_iScreenWidth  = info_mod.iWidth;

From 846e9d7e07c12468ef86587f5bca16c70598bb2e Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Thu, 17 Apr 2014 13:01:51 +0100
Subject: [PATCH 35/88] [graphics] Allow switching to a more suitable 3D
 resolution

---
 xbmc/guilib/GraphicContext.cpp | 40 +++++++++++++++++++++++++++++++++++++++-
 xbmc/guilib/GraphicContext.h   |  1 +
 2 files changed, 40 insertions(+), 1 deletion(-)

diff --git a/xbmc/guilib/GraphicContext.cpp b/xbmc/guilib/GraphicContext.cpp
index f1926de..e7e8321 100644
--- a/xbmc/guilib/GraphicContext.cpp
+++ b/xbmc/guilib/GraphicContext.cpp
@@ -35,6 +35,7 @@
 #include "utils/JobManager.h"
 #include "video/VideoReferenceClock.h"
 #include "cores/IPlayer.h"
+#include <float.h>
 
 using namespace std;
 
@@ -500,6 +501,43 @@ RESOLUTION CGraphicContext::GetVideoResolution() const
   return m_Resolution;
 }
 
+RESOLUTION CGraphicContext::Get3DVideoResolution(RESOLUTION resolution, RENDER_STEREO_MODE mode) const
+{
+  RESOLUTION best = resolution;
+  RESOLUTION_INFO curr = CDisplaySettings::Get().GetResolutionInfo(best);
+  // Find closest refresh rate
+  for (size_t i = (int)RES_DESKTOP; i < CDisplaySettings::Get().ResolutionInfoSize(); i++)
+  {
+    const RESOLUTION_INFO info = CDisplaySettings::Get().GetResolutionInfo((RESOLUTION)i);
+
+    //discard resolutions that are not the same width and height (and interlaced/3D flags)
+    //or have a too low refreshrate
+    if (info.iScreenWidth  != curr.iScreenWidth
+    ||  info.iScreenHeight != curr.iScreenHeight
+    ||  info.iScreen       != curr.iScreen
+    ||  (info.dwFlags & D3DPRESENTFLAG_INTERLACED) != (curr.dwFlags & D3DPRESENTFLAG_INTERLACED)
+    ||  fabs(info.fRefreshRate - curr.fRefreshRate) >= FLT_EPSILON)
+      continue;
+
+    if (mode == RENDER_STEREO_MODE_SPLIT_VERTICAL && info.dwFlags & D3DPRESENTFLAG_MODE3DSBS)
+    {
+      best = (RESOLUTION)i;
+      break;
+    }
+    else if (mode == RENDER_STEREO_MODE_SPLIT_HORIZONTAL && info.dwFlags & D3DPRESENTFLAG_MODE3DTB)
+    {
+      best = (RESOLUTION)i;
+      break;
+    }
+    else if ((mode == RENDER_STEREO_MODE_OFF || mode == RENDER_STEREO_MODE_MONO) && !(info.dwFlags & (D3DPRESENTFLAG_MODE3DSBS|D3DPRESENTFLAG_MODE3DTB)))
+    {
+      best = (RESOLUTION)i;
+      break;
+    }
+  }
+  return best;
+}
+
 void CGraphicContext::ResetOverscan(RESOLUTION_INFO &res)
 {
   res.Overscan.left = 0;
@@ -1037,7 +1075,7 @@ void CGraphicContext::Flip(const CDirtyRegionList& dirty)
   if(m_stereoMode != m_nextStereoMode)
   {
     m_stereoMode = m_nextStereoMode;
-    SetVideoResolution(GetVideoResolution(), true);
+    SetVideoResolution(Get3DVideoResolution(m_Resolution, m_stereoMode), true);
     g_windowManager.SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_RENDERER_RESET);
   }
 }
diff --git a/xbmc/guilib/GraphicContext.h b/xbmc/guilib/GraphicContext.h
index 2904c1b..8d3774f 100644
--- a/xbmc/guilib/GraphicContext.h
+++ b/xbmc/guilib/GraphicContext.h
@@ -108,6 +108,7 @@ class CGraphicContext : public CCriticalSection,
   bool IsValidResolution(RESOLUTION res);
   void SetVideoResolution(RESOLUTION res, bool forceUpdate = false);
   RESOLUTION GetVideoResolution() const;
+  RESOLUTION Get3DVideoResolution(RESOLUTION resolution, RENDER_STEREO_MODE mode) const;
   void ResetOverscan(RESOLUTION res, OVERSCAN &overscan);
   void ResetOverscan(RESOLUTION_INFO &resinfo);
   void ResetScreenParameters(RESOLUTION res);

From 6761ff32dfb4a483c355d19c7e1e01bdd39dc818 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Thu, 17 Apr 2014 13:38:55 +0100
Subject: [PATCH 36/88] [3D] Support switching to 3D resolutions

Include matching 3D flags (SBS/TAB) in the score of a resolution to switch to, to enable switching to 3d modes.
Also remove the old code that treated 3D modes differently when assigning a score.
---
 xbmc/cores/VideoRenderers/BaseRenderer.cpp | 47 +++++++++++-------------------
 1 file changed, 17 insertions(+), 30 deletions(-)

diff --git a/xbmc/cores/VideoRenderers/BaseRenderer.cpp b/xbmc/cores/VideoRenderers/BaseRenderer.cpp
index 83c3adb..8076e76 100644
--- a/xbmc/cores/VideoRenderers/BaseRenderer.cpp
+++ b/xbmc/cores/VideoRenderers/BaseRenderer.cpp
@@ -222,10 +222,14 @@ void CBaseRenderer::FindResolutionFromFpsMatch(float fps, float& weight)
 RESOLUTION CBaseRenderer::FindClosestResolution(float fps, float multiplier, RESOLUTION current, float& weight)
 {
   RESOLUTION_INFO curr = g_graphicsContext.GetResInfo(current);
+  RENDER_STEREO_MODE stereo_mode = g_graphicsContext.GetStereoMode();
 
   float fRefreshRate = fps;
 
-  float last_diff = fRefreshRate;
+  int c_weight = MathUtils::round_int(RefreshWeight(curr.fRefreshRate, fRefreshRate * multiplier) * 1000.0);
+  if (!(stereo_mode == RENDER_STEREO_MODE_SPLIT_VERTICAL) != !(curr.dwFlags & D3DPRESENTFLAG_MODE3DSBS) ||
+      !(stereo_mode == RENDER_STEREO_MODE_SPLIT_HORIZONTAL) != !(curr.dwFlags & D3DPRESENTFLAG_MODE3DTB))
+    c_weight += 1000;
 
   // Find closest refresh rate
   for (size_t i = (int)RES_DESKTOP; i < CDisplaySettings::Get().ResolutionInfoSize(); i++)
@@ -241,40 +245,23 @@ RESOLUTION CBaseRenderer::FindClosestResolution(float fps, float multiplier, RES
     ||  info.fRefreshRate < (fRefreshRate * multiplier / 1.001) - 0.001)
       continue;
 
-    // For 3D choose the closest refresh rate 
-    if(CONF_FLAGS_STEREO_MODE_MASK(m_iFlags))
-    {
-      float diff = (info.fRefreshRate - fRefreshRate);
-      if(diff < 0)
-        diff *= -1.0f;
+    int i_weight = MathUtils::round_int(RefreshWeight(info.fRefreshRate, fRefreshRate * multiplier) * 1000.0);
 
-      if(diff < last_diff)
-      {
-        last_diff = diff;
-        current = (RESOLUTION)i;
-        curr = info;
-      }
-    }
-    else
-    {
-      int c_weight = MathUtils::round_int(RefreshWeight(curr.fRefreshRate, fRefreshRate * multiplier) * 1000.0);
-      int i_weight = MathUtils::round_int(RefreshWeight(info.fRefreshRate, fRefreshRate * multiplier) * 1000.0);
+    if (!(stereo_mode == RENDER_STEREO_MODE_SPLIT_VERTICAL) != !(info.dwFlags & D3DPRESENTFLAG_MODE3DSBS) ||
+        !(stereo_mode == RENDER_STEREO_MODE_SPLIT_HORIZONTAL) != !(info.dwFlags & D3DPRESENTFLAG_MODE3DTB))
+      i_weight += 1000;
 
-      // Closer the better, prefer higher refresh rate if the same
-      if ((i_weight <  c_weight)
-      ||  (i_weight == c_weight && info.fRefreshRate > curr.fRefreshRate))
-      {
-        current = (RESOLUTION)i;
-        curr    = info;
-      }
+    // Closer the better, prefer higher refresh rate if the same
+    if ((i_weight <  c_weight)
+    ||  (i_weight == c_weight && info.fRefreshRate > curr.fRefreshRate))
+    {
+      current  = (RESOLUTION)i;
+      curr     = info;
+      c_weight = i_weight;
     }
   }
 
-  // For 3D overwrite weight
-  if(CONF_FLAGS_STEREO_MODE_MASK(m_iFlags))
-    weight = 0;
-  else
-    weight = RefreshWeight(curr.fRefreshRate, fRefreshRate * multiplier);
+  weight = RefreshWeight(curr.fRefreshRate, fRefreshRate * multiplier);
 
   return current;
 }

From 175136d5d3871f90a0e298120cb8c6012159dbf9 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Wed, 23 Apr 2014 00:05:07 +0100
Subject: [PATCH 37/88] [graphics] Make pixel ratio for 3d modes consistent

Note: Use the stored stereo flags from lists of resolutions.
Use current stereo mode for current resolution.
---
 xbmc/cores/VideoRenderers/BaseRenderer.cpp      | 10 +++----
 xbmc/guilib/GraphicContext.cpp                  | 37 ++++++++++++-------------
 xbmc/guilib/GraphicContext.h                    | 12 ++++++--
 xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp |  8 ------
 4 files changed, 32 insertions(+), 35 deletions(-)

diff --git a/xbmc/cores/VideoRenderers/BaseRenderer.cpp b/xbmc/cores/VideoRenderers/BaseRenderer.cpp
index 8076e76..9118cb0 100644
--- a/xbmc/cores/VideoRenderers/BaseRenderer.cpp
+++ b/xbmc/cores/VideoRenderers/BaseRenderer.cpp
@@ -119,7 +119,7 @@ bool CBaseRenderer::FindResolutionFromOverride(float fps, float& weight, bool fa
 
     for (size_t j = (int)RES_DESKTOP; j < CDisplaySettings::Get().ResolutionInfoSize(); j++)
     {
-      RESOLUTION_INFO info = g_graphicsContext.GetResInfo((RESOLUTION)j);
+      RESOLUTION_INFO info = g_graphicsContext.GetResInfo((RESOLUTION)j, g_graphicsContext.GetStereoMode((RESOLUTION)j));
 
       if (info.iScreenWidth  == curr.iScreenWidth
        && info.iScreenHeight == curr.iScreenHeight
@@ -179,7 +179,7 @@ void CBaseRenderer::FindResolutionFromFpsMatch(float fps, float& weight)
       //get the resolution with the refreshrate closest to 60 hertz
       for (size_t i = (int)RES_DESKTOP; i < CDisplaySettings::Get().ResolutionInfoSize(); i++)
       {
-        RESOLUTION_INFO info = g_graphicsContext.GetResInfo((RESOLUTION)i);
+        RESOLUTION_INFO info = g_graphicsContext.GetResInfo((RESOLUTION)i, g_graphicsContext.GetStereoMode((RESOLUTION)i));
 
         if (MathUtils::round_int(info.fRefreshRate) == 60
          && info.iScreenWidth  == curr.iScreenWidth
@@ -200,7 +200,7 @@ void CBaseRenderer::FindResolutionFromFpsMatch(float fps, float& weight)
         CLog::Log(LOGDEBUG, "60 hertz refreshrate not available, choosing highest");
         for (size_t i = (int)RES_DESKTOP; i < CDisplaySettings::Get().ResolutionInfoSize(); i++)
         {
-          RESOLUTION_INFO info = g_graphicsContext.GetResInfo((RESOLUTION)i);
+          RESOLUTION_INFO info = g_graphicsContext.GetResInfo((RESOLUTION)i, g_graphicsContext.GetStereoMode((RESOLUTION)i));
 
           if (info.fRefreshRate  >  curr.fRefreshRate
            && info.iScreenWidth  == curr.iScreenWidth
@@ -234,14 +234,14 @@ RESOLUTION CBaseRenderer::FindClosestResolution(float fps, float multiplier, RES
   // Find closest refresh rate
   for (size_t i = (int)RES_DESKTOP; i < CDisplaySettings::Get().ResolutionInfoSize(); i++)
   {
-    const RESOLUTION_INFO info = g_graphicsContext.GetResInfo((RESOLUTION)i);
+    const RESOLUTION_INFO info = g_graphicsContext.GetResInfo((RESOLUTION)i, g_graphicsContext.GetStereoMode((RESOLUTION)i));
 
     //discard resolutions that are not the same width and height (and interlaced/3D flags)
     //or have a too low refreshrate
     if (info.iScreenWidth  != curr.iScreenWidth
     ||  info.iScreenHeight != curr.iScreenHeight
     ||  info.iScreen       != curr.iScreen
-    ||  (info.dwFlags & D3DPRESENTFLAG_MODEMASK) != (curr.dwFlags & D3DPRESENTFLAG_MODEMASK)
+    ||  (info.dwFlags & D3DPRESENTFLAG_INTERLACED) != (curr.dwFlags & D3DPRESENTFLAG_INTERLACED)
     ||  info.fRefreshRate < (fRefreshRate * multiplier / 1.001) - 0.001)
       continue;
 
diff --git a/xbmc/guilib/GraphicContext.cpp b/xbmc/guilib/GraphicContext.cpp
index e7e8321..d4f0346 100644
--- a/xbmc/guilib/GraphicContext.cpp
+++ b/xbmc/guilib/GraphicContext.cpp
@@ -747,32 +747,33 @@ void CGraphicContext::ApplyStateBlock()
   g_Windowing.ApplyStateBlock();
 }
 
-const RESOLUTION_INFO CGraphicContext::GetResInfo(RESOLUTION res) const
+RENDER_STEREO_MODE CGraphicContext::GetStereoMode(RESOLUTION res) const
 {
   RESOLUTION_INFO info = CDisplaySettings::Get().GetResolutionInfo(res);
+  return (info.dwFlags & D3DPRESENTFLAG_MODE3DTB)  ? RENDER_STEREO_MODE_SPLIT_HORIZONTAL :
+         (info.dwFlags & D3DPRESENTFLAG_MODE3DSBS) ? RENDER_STEREO_MODE_SPLIT_VERTICAL : RENDER_STEREO_MODE_OFF;
+}
 
-  if(m_stereoMode == RENDER_STEREO_MODE_SPLIT_HORIZONTAL)
+const RESOLUTION_INFO CGraphicContext::GetResInfo(RESOLUTION res, RENDER_STEREO_MODE stereoMode) const
+{
+  RESOLUTION_INFO info = CDisplaySettings::Get().GetResolutionInfo(res);
+
+  if(stereoMode == RENDER_STEREO_MODE_SPLIT_HORIZONTAL)
   {
-    if((info.dwFlags & D3DPRESENTFLAG_MODE3DTB) == 0)
-    {
-      info.fPixelRatio     /= 2;
-      info.iBlanking        = 0;
-      info.dwFlags         |= D3DPRESENTFLAG_MODE3DTB;
-    }
+    info.fPixelRatio     /= 2;
+    info.iBlanking        = 0;
+    info.dwFlags         |= D3DPRESENTFLAG_MODE3DTB;
     info.iHeight          = (info.iHeight         - info.iBlanking) / 2;
     info.Overscan.top    /= 2;
     info.Overscan.bottom  = (info.Overscan.bottom - info.iBlanking) / 2;
     info.iSubtitles       = (info.iSubtitles      - info.iBlanking) / 2;
   }
 
-  if(m_stereoMode == RENDER_STEREO_MODE_SPLIT_VERTICAL)
+  if(stereoMode == RENDER_STEREO_MODE_SPLIT_VERTICAL)
   {
-    if((info.dwFlags & D3DPRESENTFLAG_MODE3DSBS) == 0)
-    {
-      info.fPixelRatio     *= 2;
-      info.iBlanking        = 0;
-      info.dwFlags         |= D3DPRESENTFLAG_MODE3DSBS;
-    }
+    info.fPixelRatio     *= 2;
+    info.iBlanking        = 0;
+    info.dwFlags         |= D3DPRESENTFLAG_MODE3DSBS;
     info.iWidth           = (info.iWidth         - info.iBlanking) / 2;
     info.Overscan.left   /= 2;
     info.Overscan.right   = (info.Overscan.right - info.iBlanking) / 2;
@@ -790,16 +791,14 @@ void CGraphicContext::SetResInfo(RESOLUTION res, const RESOLUTION_INFO& info)
   if(info.dwFlags & D3DPRESENTFLAG_MODE3DSBS)
   {
     curr.Overscan.right  = info.Overscan.right  * 2 + info.iBlanking;
-    if((curr.dwFlags & D3DPRESENTFLAG_MODE3DSBS) == 0)
-      curr.fPixelRatio  /= 2.0;
+    curr.fPixelRatio  /= 2.0;
   }
 
   if(info.dwFlags & D3DPRESENTFLAG_MODE3DTB)
   {
     curr.Overscan.bottom = info.Overscan.bottom * 2 + info.iBlanking;
     curr.iSubtitles      = info.iSubtitles      * 2 + info.iBlanking;
-    if((curr.dwFlags & D3DPRESENTFLAG_MODE3DTB) == 0)
-      curr.fPixelRatio  *= 2.0;
+    curr.fPixelRatio  *= 2.0;
   }
 }
 
diff --git a/xbmc/guilib/GraphicContext.h b/xbmc/guilib/GraphicContext.h
index 8d3774f..f2e122d 100644
--- a/xbmc/guilib/GraphicContext.h
+++ b/xbmc/guilib/GraphicContext.h
@@ -120,11 +120,15 @@ class CGraphicContext : public CCriticalSection,
   void GetAllowedResolutions(std::vector<RESOLUTION> &res);
 
   // output scaling
+  const RESOLUTION_INFO GetResInfo(RESOLUTION res) const
+  {
+    return GetResInfo(res, GetStereoMode());
+  }
   const RESOLUTION_INFO GetResInfo() const
   {
     return GetResInfo(m_Resolution);
   }
-  const RESOLUTION_INFO GetResInfo(RESOLUTION res) const;
+  const RESOLUTION_INFO GetResInfo(RESOLUTION res, RENDER_STEREO_MODE stereo_mode) const;
   void SetResInfo(RESOLUTION res, const RESOLUTION_INFO& info);
 
   /* \brief Get UI scaling information from a given resolution to the screen resolution.
@@ -161,9 +165,11 @@ class CGraphicContext : public CCriticalSection,
   void RestoreOrigin();
   void SetCameraPosition(const CPoint &camera);
   void SetStereoView(RENDER_STEREO_VIEW view);
-  RENDER_STEREO_VIEW GetStereoView()  { return m_stereoView; }
+  RENDER_STEREO_VIEW GetStereoView() const { return m_stereoView; }
   void SetStereoMode(RENDER_STEREO_MODE mode) { m_nextStereoMode = mode; }
-  RENDER_STEREO_MODE GetStereoMode()  { return m_stereoMode; }
+  RENDER_STEREO_MODE GetStereoMode() const { return m_stereoMode; }
+  RENDER_STEREO_MODE GetStereoMode(RESOLUTION res) const;
+
   void RestoreCameraPosition();
   /*! \brief Set a region in which to clip all rendering
    Anything that is rendered after setting a clip region will be clipped so that no part renders
diff --git a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
index c58c28a..bf1e589 100644
--- a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
+++ b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
@@ -449,15 +449,9 @@ bool CEGLNativeTypeRaspberryPI::ProbeResolutions(std::vector<RESOLUTION_INFO> &r
       m_desktopRes.fPixelRatio  = get_display_aspect_ratio((HDMI_ASPECT_T)tv_state.display.hdmi.display_options.aspect) / ((float)m_desktopRes.iScreenWidth / (float)m_desktopRes.iScreenHeight);
       // Also add 3D flags
       if (tv_state.display.hdmi.format_3d == HDMI_3D_FORMAT_SBS_HALF)
-      {
         m_desktopRes.dwFlags |= D3DPRESENTFLAG_MODE3DSBS;
-        m_desktopRes.fPixelRatio *= 2.0;
-      }
       else if (tv_state.display.hdmi.format_3d == HDMI_3D_FORMAT_TB_HALF)
-      {
         m_desktopRes.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
-        m_desktopRes.fPixelRatio *= 0.5;
-      }
       HDMI_PROPERTY_PARAM_T property;
       property.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE;
       vc_tv_hdmi_get_property(&property);
@@ -600,7 +594,6 @@ void CEGLNativeTypeRaspberryPI::GetSupportedModes(HDMI_RES_GROUP_T group, std::v
         RESOLUTION_INFO res2 = res;
         res2.dwFlags |= D3DPRESENTFLAG_MODE3DSBS;
         res2.fPixelRatio    = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio) / ((float)res2.iScreenWidth / (float)res2.iScreenHeight);
-        res2.fPixelRatio   *= 2.0f;
         res2.iSubtitles    = (int)(0.965 * res2.iHeight);
 
         AddUniqueResolution(res2, resolutions);
@@ -616,7 +609,6 @@ void CEGLNativeTypeRaspberryPI::GetSupportedModes(HDMI_RES_GROUP_T group, std::v
         RESOLUTION_INFO res2 = res;
         res2.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
         res2.fPixelRatio    = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio) / ((float)res2.iScreenWidth / (float)res2.iScreenHeight);
-        res2.fPixelRatio   *= 0.5f;
         res2.iSubtitles    = (int)(0.965 * res2.iHeight);
 
         AddUniqueResolution(res2, resolutions);

From 58c8800edbc1a15169ff6de916dafa9fd254d2c3 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sun, 10 Aug 2014 16:45:16 +0100
Subject: [PATCH 38/88] filesystem: Make support of browsing into archives
 optional

The ability to browse, scan and play content in archives can cause problems on low powered/low memory devices.
It's quite common to see reports of a large rar file that causes xbmc to crash with an out-of-memory error when browsing or scanning.
It also can be slow as any archive in the directory is opened and extracted.

Add a settings option to enable this feature and default to disabled on Pi
---
 language/English/strings.po              |  9 +++++++++
 system/settings/rbp.xml                  | 10 ++++++++++
 system/settings/settings.xml             |  5 +++++
 xbmc/filesystem/FileDirectoryFactory.cpp |  4 ++++
 4 files changed, 28 insertions(+)

diff --git a/language/English/strings.po b/language/English/strings.po
index 1836b29..82b1279 100755
--- a/language/English/strings.po
+++ b/language/English/strings.po
@@ -15957,4 +15957,13 @@ msgstr ""
 #: system/settings/rbp.xml
 msgctxt "#38010"
 msgid "GPU accelerated"
+
+#: system/settings/settings.xml
+msgctxt "#38020"
+msgid "Support browsing into archives"
+msgstr ""
+
+#: system/settings/settings.xml
+msgctxt "#38021"
+msgid "Allow viewing and playing files in archives (e.g. zip, rar)"
 msgstr ""
diff --git a/system/settings/rbp.xml b/system/settings/rbp.xml
index e80ad91..13e30ad 100644
--- a/system/settings/rbp.xml
+++ b/system/settings/rbp.xml
@@ -1,5 +1,15 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <settings>
+  <section id="appearance">
+    <category id="filelists">
+      <group id="1">
+        <setting id="filelists.browsearchives">
+          <default>false</default>
+        </setting>
+      </group>
+    </category>
+  </section>
+
   <section id="videos">
     <category id="videoacceleration">
       <group id="1">
diff --git a/system/settings/settings.xml b/system/settings/settings.xml
index d8736c3..dc33d95 100644
--- a/system/settings/settings.xml
+++ b/system/settings/settings.xml
@@ -226,6 +226,11 @@
           <default>false</default>
           <control type="toggle" />
         </setting>
+        <setting id="filelists.browsearchives" type="boolean" label="38020" help="38021">
+          <level>1</level>
+          <default>true</default>
+          <control type="toggle" />
+        </setting>
       </group>
     </category>
     <category id="screensaver" label="360" help="36128">
diff --git a/xbmc/filesystem/FileDirectoryFactory.cpp b/xbmc/filesystem/FileDirectoryFactory.cpp
index 2fd8777..3b294cd 100644
--- a/xbmc/filesystem/FileDirectoryFactory.cpp
+++ b/xbmc/filesystem/FileDirectoryFactory.cpp
@@ -46,6 +46,7 @@
 #include "Directory.h"
 #include "File.h"
 #include "ZipManager.h"
+#include "settings/Settings.h"
 #include "settings/AdvancedSettings.h"
 #include "FileItem.h"
 #include "utils/StringUtils.h"
@@ -142,6 +143,8 @@ IFileDirectory* CFileDirectoryFactory::Create(const CURL& url, CFileItem* pItem,
     return NULL;
   }
 #endif
+  if (CSettings::Get().GetBool("filelists.browsearchives"))
+  {
   if (url.IsFileType("zip"))
   {
     CURL zipURL = URIUtils::CreateArchivePath("zip", url);
@@ -215,6 +218,7 @@ IFileDirectory* CFileDirectoryFactory::Create(const CURL& url, CFileItem* pItem,
     }
     return NULL;
   }
+  }
   if (url.IsFileType("xsp"))
   { // XBMC Smart playlist - just XML renamed to XSP
     // read the name of the playlist in

From d4e2e5498836c39f6765d52f28309392621f8ea5 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Mon, 11 Aug 2014 22:56:13 +0100
Subject: [PATCH 40/88] [omxplayer] Add acceleration option to choose
 omxplayer/dvdplayer automatically

---
 language/English/strings.po        | 10 +++++++
 system/settings/settings.xml       |  9 ++++++
 xbmc/cores/dvdplayer/DVDPlayer.cpp | 58 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 77 insertions(+)

diff --git a/language/English/strings.po b/language/English/strings.po
index 82b1279..aee9a3a 100755
--- a/language/English/strings.po
+++ b/language/English/strings.po
@@ -15967,3 +15967,13 @@ msgstr ""
 msgctxt "#38021"
 msgid "Allow viewing and playing files in archives (e.g. zip, rar)"
 msgstr ""
+
+#: system/settings/settings.xml
+msgctxt "#37033"
+msgid "Select omxplayer or dvdplayer automatically"
+msgstr ""
+
+#: system/settings/settings.xml
+msgctxt "#37034"
+msgid "Uses codec information and audio setting to choose dvdplayer or omxplayer as appropriate"
+msgstr ""
diff --git a/system/settings/settings.xml b/system/settings/settings.xml
index dc33d95..f4fcb7c 100644
--- a/system/settings/settings.xml
+++ b/system/settings/settings.xml
@@ -751,6 +751,15 @@
           <default>true</default>
           <control type="toggle" />
         </setting>
+        <setting id="videoplayer.autoomxplayer" type="boolean" label="37033" help="37033">
+          <requirement>HAS_OMXPLAYER</requirement>
+          <dependencies>
+            <dependency type="enable" setting="videoplayer.decodingmethod" operator="is">1</dependency>
+          </dependencies>
+          <level>2</level>
+          <default>true</default>
+          <control type="toggle" />
+        </setting>
         <setting id="videoplayer.useomxplayer" type="boolean" label="13458" help="13459">
           <requirement>HAS_OMXPLAYER</requirement>
           <dependencies>
diff --git a/xbmc/cores/dvdplayer/DVDPlayer.cpp b/xbmc/cores/dvdplayer/DVDPlayer.cpp
index 21d5f7c..713739b 100644
--- a/xbmc/cores/dvdplayer/DVDPlayer.cpp
+++ b/xbmc/cores/dvdplayer/DVDPlayer.cpp
@@ -490,6 +490,62 @@ void CSelectionStreams::Update(CDVDInputStream* input, CDVDDemux* demuxer, std::
 
 void CDVDPlayer::CreatePlayers()
 {
+#ifdef HAS_OMXPLAYER
+  bool omxplayer_mode = m_omxplayer_mode;
+  bool autoomx = CSettings::Get().GetBool("videoplayer.autoomxplayer");
+  // omxplayer only handles Pi sink
+  if (autoomx && m_omxplayer_mode &&
+      CSettings::Get().GetString("audiooutput.audiodevice") != "PI:Analogue" &&
+      CSettings::Get().GetString("audiooutput.audiodevice") != "PI:HDMI")
+  {
+    CLog::Log(LOGNOTICE, "COMXPlayer::%s OMXPlayer unsuitable due to audio sink", __func__);
+    m_omxplayer_mode = false;
+  }
+  if (autoomx && m_pDemuxer && m_omxplayer_mode)
+  {
+    // find video stream
+    int num_supported = 0, num_unsupported = 0;
+    AVCodecID codec = AV_CODEC_ID_NONE;
+    SelectionStreams streams = m_SelectionStreams.Get(STREAM_VIDEO, PredicateVideoPriority);
+    for(SelectionStreams::iterator it = streams.begin(); it != streams.end(); ++it)
+    {
+      int iStream = it->id;
+      CDemuxStream *stream = m_pDemuxer->GetStream(iStream);
+      if(!stream || stream->disabled)
+        continue;
+      CDVDStreamInfo hint(*stream, true);
+
+      bool supported = false;
+      if ((hint.codec == AV_CODEC_ID_MPEG1VIDEO || hint.codec == AV_CODEC_ID_MPEG2VIDEO) && g_RBP.GetCodecMpg2())
+        supported = true;
+      else if ((hint.codec == AV_CODEC_ID_VC1 || hint.codec == AV_CODEC_ID_WMV3) && g_RBP.GetCodecWvc1())
+        supported = true;
+      else if (hint.codec == AV_CODEC_ID_H264 || hint.codec == AV_CODEC_ID_MPEG4 || hint.codec == AV_CODEC_ID_H263 ||
+          hint.codec == AV_CODEC_ID_VP6 || hint.codec == AV_CODEC_ID_VP6F || hint.codec == AV_CODEC_ID_VP6A || hint.codec == AV_CODEC_ID_VP8 ||
+          hint.codec == AV_CODEC_ID_THEORA || hint.codec == AV_CODEC_ID_MJPEG || hint.codec == AV_CODEC_ID_MJPEGB)
+        supported = true;
+      codec = hint.codec;
+      if (supported)
+        num_supported++;
+      else
+        num_unsupported++;
+    }
+    if (num_unsupported > 0 && num_supported == 0)
+    {
+      CLog::Log(LOGNOTICE, "COMXPlayer::%s OMXPlayer unsuitable due to video codec (%x:%d/%d)", __func__, codec, num_supported, num_unsupported);
+      m_omxplayer_mode = false;
+    }
+  }
+  if (autoomx && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
+  {
+    CLog::Log(LOGNOTICE, "COMXPlayer::%s OMXPlayer unsuitable due to dvd menus", __func__);
+    m_omxplayer_mode = false;
+  }
+
+  if (m_omxplayer_mode != omxplayer_mode)
+    DestroyPlayers();
+#endif
+
   if (m_players_created)
     return;
 
@@ -1187,6 +1243,8 @@ void CDVDPlayer::Process()
     m_bAbortRequest = true;
     return;
   }
+  // give players a chance to reconsider now codecs are known
+  CreatePlayers();
 
   // allow renderer to switch to fullscreen if requested
   m_dvdPlayerVideo->EnableFullscreen(m_PlayerOptions.fullscreen);

From 7dc5ca6ed6eb20cf63964badd122773dbcfea03c Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Tue, 12 Aug 2014 16:51:18 +0100
Subject: [PATCH 41/88] AE: Add some logging for suspend/resume

---
 xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp | 5 +++++
 xbmc/cores/omxplayer/OMXAudio.cpp                    | 2 ++
 2 files changed, 7 insertions(+)

diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp
index 0e4d8da..64775c0 100644
--- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp
@@ -348,6 +348,7 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg)
         switch (signal)
         {
         case CActiveAEControlProtocol::INIT:
+          CLog::Log(LOGNOTICE, "CActiveAE::%s - AE_TOP_UNCONFIGURED/CActiveAEControlProtocol::INIT", __func__);
           m_extError = false;
           m_sink.EnumerateSinkList(false);
           LoadSettings();
@@ -433,6 +434,7 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg)
           m_extDeferData = true;
           return;
         case CActiveAEControlProtocol::SUSPEND:
+          CLog::Log(LOGNOTICE, "CActiveAE::%s - AE_TOP_CONFIGURED/CActiveAEControlProtocol::SUSPEND", __func__);
           UnconfigureSink();
           m_stats.SetSuspended(true);
           m_state = AE_TOP_CONFIGURED_SUSPEND;
@@ -658,6 +660,7 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg)
           CLog::Log(LOGDEBUG,"CActiveAE - display reset event");
           displayReset = true;
         case CActiveAEControlProtocol::INIT:
+          CLog::Log(LOGNOTICE, "CActiveAE::%s - AE_TOP_CONFIGURED_SUSPEND/CActiveAEControlProtocol::INIT", __func__);
           m_extError = false;
           if (!displayReset)
           {
@@ -2339,12 +2342,14 @@ void CActiveAE::Shutdown()
 
 bool CActiveAE::Suspend()
 {
+  CLog::Log(LOGNOTICE, "ActiveAE::%s", __FUNCTION__);
   return m_controlPort.SendOutMessage(CActiveAEControlProtocol::SUSPEND);
 }
 
 bool CActiveAE::Resume()
 {
   Message *reply;
+  CLog::Log(LOGNOTICE, "ActiveAE::%s", __FUNCTION__);
   if (m_controlPort.SendOutMessageSync(CActiveAEControlProtocol::INIT,
                                                  &reply,
                                                  5000))
diff --git a/xbmc/cores/omxplayer/OMXAudio.cpp b/xbmc/cores/omxplayer/OMXAudio.cpp
index 9e98207..4f85dc7 100644
--- a/xbmc/cores/omxplayer/OMXAudio.cpp
+++ b/xbmc/cores/omxplayer/OMXAudio.cpp
@@ -932,6 +932,7 @@ bool COMXAudio::Initialize(AEAudioFormat format, OMXClock *clock, CDVDStreamInfo
 //***********************************************************************************************
 bool COMXAudio::Deinitialize()
 {
+  CLog::Log(LOGNOTICE, "COMXAudio::%s start", __func__);
   CSingleLock lock (m_critSection);
 
   if ( m_omx_tunnel_clock_analog.IsInitialized() )
@@ -992,6 +993,7 @@ bool COMXAudio::Deinitialize()
   m_submitted     = 0.0f;
   m_maxLevel      = 0.0f;
 
+  CLog::Log(LOGNOTICE, "COMXAudio::%s end", __func__);
   return true;
 }
 

From 2766a736638a7ed216ec315f0f341b2a4684034d Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Wed, 9 Jul 2014 22:45:43 +0100
Subject: [PATCH 42/88] [rbp] Make cachemembuffersize default depend on memory
 size

---
 xbmc/linux/RBP.cpp                 | 5 +++++
 xbmc/settings/AdvancedSettings.cpp | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/xbmc/linux/RBP.cpp b/xbmc/linux/RBP.cpp
index 11376fc..b67fbb1 100644
--- a/xbmc/linux/RBP.cpp
+++ b/xbmc/linux/RBP.cpp
@@ -22,6 +22,7 @@
 #if defined(TARGET_RASPBERRY_PI)
 
 #include "settings/Settings.h"
+#include "settings/AdvancedSettings.h"
 #include "utils/log.h"
 
 #include "cores/omxplayer/OMXImage.h"
@@ -84,6 +85,9 @@ bool CRBP::Initialize()
   if (!m_gui_resolution_limit)
     m_gui_resolution_limit = m_gpu_mem < 128 ? 720:1080;
 
+  if (g_advancedSettings.m_cacheMemBufferSize == ~0)
+    g_advancedSettings.m_cacheMemBufferSize = m_arm_mem < 256 ? 1024 * 1024 * 2 : 1024 * 1024 * 20;
+
   g_OMXImage.Initialize();
   m_omx_image_init = true;
   return true;
@@ -96,6 +100,7 @@ void CRBP::LogFirmwareVerison()
   response[sizeof(response) - 1] = '\0';
   CLog::Log(LOGNOTICE, "Raspberry PI firmware version: %s", response);
   CLog::Log(LOGNOTICE, "ARM mem: %dMB GPU mem: %dMB MPG2:%d WVC1:%d", m_arm_mem, m_gpu_mem, m_codec_mpg2_enabled, m_codec_wvc1_enabled);
+  CLog::Log(LOGNOTICE, "cacheMemBufferSize: %dMB",  g_advancedSettings.m_cacheMemBufferSize >> 20);
   m_DllBcmHost->vc_gencmd(response, sizeof response, "get_config int");
   response[sizeof(response) - 1] = '\0';
   CLog::Log(LOGNOTICE, "Config:\n%s", response);
diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp
index 6109cec..2959ae8 100644
--- a/xbmc/settings/AdvancedSettings.cpp
+++ b/xbmc/settings/AdvancedSettings.cpp
@@ -364,7 +364,12 @@ void CAdvancedSettings::Initialize()
 
   m_measureRefreshrate = false;
 
+#ifdef TARGET_RASPBERRY_PI
+  // want default to be memory dependent, but interface to gpu not available yet, so set in RBP.cpp
+  m_cacheMemBufferSize = ~0;
+#else
   m_cacheMemBufferSize = 1024 * 1024 * 20;
+#endif
   m_networkBufferMode = 0; // Default (buffer all internet streams/filesystems)
   // the following setting determines the readRate of a player data
   // as multiply of the default data read rate

From 72392bd0153806e1e500d5e6d1ed1adefbb102c2 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Mon, 16 Jun 2014 19:06:00 +0100
Subject: [PATCH 44/88] [experimental] Disable quiet-noise generation

---
 xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp
index c6272cc..9111593 100644
--- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp
@@ -871,6 +871,7 @@ void CActiveAESink::SwapInit(CSampleBuffer* samples)
 
 void CActiveAESink::GenerateNoise()
 {
+#ifndef TARGET_RASPBERRY_PI
   int nb_floats = m_sampleOfSilence.pkt->max_nb_samples;
   nb_floats *= m_sampleOfSilence.pkt->config.channels;
 
@@ -909,6 +910,7 @@ void CActiveAESink::GenerateNoise()
 
   _aligned_free(noise);
   delete resampler;
+#endif
 }
 
 void CActiveAESink::SetSilenceTimer()

From 76de204c4a1eb8c0f4ec665042d6971cd7fcf975 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Fri, 30 May 2014 14:58:43 +0100
Subject: [PATCH 45/88] [settings] Experiment: Report DESKTOP resolution in
 video settings

---
 xbmc/settings/DisplaySettings.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/xbmc/settings/DisplaySettings.cpp b/xbmc/settings/DisplaySettings.cpp
index 6902f83..50c5f97 100644
--- a/xbmc/settings/DisplaySettings.cpp
+++ b/xbmc/settings/DisplaySettings.cpp
@@ -674,6 +674,9 @@ void CDisplaySettings::SettingOptionsResolutionsFiller(const CSetting *setting,
     vector<RESOLUTION_WHR> resolutions = g_Windowing.ScreenResolutions(info.iScreen, info.fRefreshRate);
     for (vector<RESOLUTION_WHR>::const_iterator resolution = resolutions.begin(); resolution != resolutions.end(); ++resolution)
     {
+if (resolution->ResInfo_Index == RES_DESKTOP)
+      list.push_back(make_pair(StringUtils::Format("DESKTOP"), resolution->ResInfo_Index));
+else
       list.push_back(make_pair(
         StringUtils::Format("%dx%d%s", resolution->width, resolution->height,
                             ModeFlagsToString(resolution->flags, false).c_str()),

From dd67fb4e0413eab25b5f3bc77bc2d772625f2506 Mon Sep 17 00:00:00 2001
From: macrule <macrule2001@yahoo.de>
Date: Thu, 11 Apr 2013 18:24:42 +0200
Subject: [PATCH 46/88] Added some vc_tv_* functions that were missing in
 DllBCM.

---
 xbmc/linux/DllBCM.h | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/xbmc/linux/DllBCM.h b/xbmc/linux/DllBCM.h
index b92fdb8..9c7e293 100644
--- a/xbmc/linux/DllBCM.h
+++ b/xbmc/linux/DllBCM.h
@@ -48,6 +48,9 @@ class DllBcmHostInterface
   virtual void bcm_host_init() = 0;
   virtual void bcm_host_deinit() = 0;
   virtual int32_t graphics_get_display_size( const uint16_t display_number, uint32_t *width, uint32_t *height) = 0;
+  virtual int vc_tv_power_off() = 0;
+  virtual int vc_tv_sdtv_power_on(SDTV_MODE_T mode, SDTV_OPTIONS_T *options) = 0;
+  virtual int vc_tv_hdmi_power_on_preferred() = 0;
   virtual int vc_tv_hdmi_power_on_best(uint32_t width, uint32_t height, uint32_t frame_rate,
                                        HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags) = 0;
   virtual int vc_tv_hdmi_power_on_best_3d(uint32_t width, uint32_t height, uint32_t frame_rate,
@@ -92,6 +95,12 @@ class DllBcmHost : public DllDynamic, DllBcmHostInterface
     { return ::bcm_host_deinit(); };
   virtual int32_t graphics_get_display_size( const uint16_t display_number, uint32_t *width, uint32_t *height)
     { return ::graphics_get_display_size(display_number, width, height); };
+  virtual int vc_tv_power_off()
+    { return ::vc_tv_power_off(); }
+  virtual int vc_tv_sdtv_power_on(SDTV_MODE_T mode, SDTV_OPTIONS_T *options)
+    { return ::vc_tv_sdtv_power_on(mode, options); }
+  virtual int vc_tv_hdmi_power_on_preferred()
+    { return ::vc_tv_hdmi_power_on_preferred(); }
   virtual int vc_tv_hdmi_power_on_best(uint32_t width, uint32_t height, uint32_t frame_rate,
                                        HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags)
     { return ::vc_tv_hdmi_power_on_best(width, height, frame_rate, scan_mode, match_flags); };

From b7d184caa2f8e10ff1a8574405bfa957867d7a2c Mon Sep 17 00:00:00 2001
From: macrule <macrule2001@yahoo.de>
Date: Thu, 11 Apr 2013 18:29:03 +0200
Subject: [PATCH 47/88] Added private utility function to map a float display
 aspect, to the respective SDTV_ASPECT_* enum value.

---
 xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
index bf1e589..518a87d 100644
--- a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
+++ b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
@@ -411,6 +411,25 @@ static void SetResolutionString(RESOLUTION_INFO &res)
     res.dwFlags & D3DPRESENTFLAG_MODE3DTB   ? " 3DTB" : "",
     res.dwFlags & D3DPRESENTFLAG_MODE3DSBS  ? " 3DSBS" : "");
 }
+
+static SDTV_ASPECT_T get_sdtv_aspect_from_display_aspect(float display_aspect)
+{
+  SDTV_ASPECT_T aspect;
+  const float delta = 1e-3;
+  if(fabs(get_display_aspect_ratio(SDTV_ASPECT_16_9) - display_aspect) < delta)
+  {
+    aspect = SDTV_ASPECT_16_9;
+  }
+  else if(fabs(get_display_aspect_ratio(SDTV_ASPECT_14_9) - display_aspect) < delta)
+  {
+    aspect = SDTV_ASPECT_14_9;
+  }
+  else
+  {
+    aspect = SDTV_ASPECT_4_3;
+  }
+  return aspect;
+}
 #endif
 
 bool CEGLNativeTypeRaspberryPI::ProbeResolutions(std::vector<RESOLUTION_INFO> &resolutions)

From 83f9763d93eb1189c353cf861d196bc4009f1c0f Mon Sep 17 00:00:00 2001
From: macrule <macrule2001@yahoo.de>
Date: Thu, 11 Apr 2013 19:50:58 +0200
Subject: [PATCH 48/88] Changed SDTV resolutions to be treated similarly to
 HDMI resolutions in SetNativeResolution. This means that the SDTV interface
 is powered up and set to the right mode.

---
 xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp | 36 ++++++++++++++++++++-----
 xbmc/windowing/egl/EGLNativeTypeRaspberryPI.h   |  1 -
 2 files changed, 29 insertions(+), 8 deletions(-)

diff --git a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
index 518a87d..9dc39d5 100644
--- a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
+++ b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
@@ -221,7 +221,7 @@ bool CEGLNativeTypeRaspberryPI::SetNativeResolution(const RESOLUTION_INFO &res)
 
   DestroyDispmaxWindow();
 
-  if(!m_fixedMode && GETFLAGS_GROUP(res.dwFlags) && GETFLAGS_MODE(res.dwFlags))
+  if(GETFLAGS_GROUP(res.dwFlags) && GETFLAGS_MODE(res.dwFlags))
   {
     sem_init(&m_tv_synced, 0, 0);
     m_DllBcmHost->vc_tv_register_callback(CallbackTvServiceCallback, this);
@@ -275,6 +275,33 @@ bool CEGLNativeTypeRaspberryPI::SetNativeResolution(const RESOLUTION_INFO &res)
 
     m_desktopRes = res;
   }
+  else if(!GETFLAGS_GROUP(res.dwFlags) && GETFLAGS_MODE(res.dwFlags))
+  {
+    sem_init(&m_tv_synced, 0, 0);
+    m_DllBcmHost->vc_tv_register_callback(CallbackTvServiceCallback, this);
+
+    SDTV_OPTIONS_T options;
+    options.aspect = get_sdtv_aspect_from_display_aspect((float)res.iScreenWidth / (float)res.iScreenHeight);
+
+    int success = m_DllBcmHost->vc_tv_sdtv_power_on((SDTV_MODE_T)GETFLAGS_MODE(res.dwFlags), &options);
+
+    if (success == 0)
+    {
+      CLog::Log(LOGDEBUG, "EGL set SDTV mode (%d,%d)=%d\n",
+                          GETFLAGS_GROUP(res.dwFlags), GETFLAGS_MODE(res.dwFlags), success);
+
+      sem_wait(&m_tv_synced);
+    }
+    else
+    {
+      CLog::Log(LOGERROR, "EGL failed to set SDTV mode (%d,%d)=%d\n",
+                          GETFLAGS_GROUP(res.dwFlags), GETFLAGS_MODE(res.dwFlags), success);
+    }
+    m_DllBcmHost->vc_tv_unregister_callback(CallbackTvServiceCallback);
+    sem_destroy(&m_tv_synced);
+
+    m_desktopRes = res;
+  }
 
   m_dispman_display = m_DllBcmHost->vc_dispmanx_display_open(0);
 
@@ -440,8 +467,6 @@ bool CEGLNativeTypeRaspberryPI::ProbeResolutions(std::vector<RESOLUTION_INFO> &r
   if(!m_DllBcmHost)
     return false;
 
-  m_fixedMode               = false;
-
   /* read initial desktop resolution before probe resolutions.
    * probing will replace the desktop resolution when it finds the same one.
    * we raplace it because probing will generate more detailed 
@@ -484,7 +509,7 @@ bool CEGLNativeTypeRaspberryPI::ProbeResolutions(std::vector<RESOLUTION_INFO> &r
       m_desktopRes.iHeight      = tv_state.display.sdtv.height;
       m_desktopRes.iScreenWidth = tv_state.display.sdtv.width;
       m_desktopRes.iScreenHeight= tv_state.display.sdtv.height;
-      m_desktopRes.dwFlags      = D3DPRESENTFLAG_INTERLACED;
+      m_desktopRes.dwFlags      = MAKEFLAGS(HDMI_RES_GROUP_INVALID, tv_state.display.sdtv.mode, 1);
       m_desktopRes.fRefreshRate = (float)tv_state.display.sdtv.frame_rate;
       m_desktopRes.fPixelRatio  = get_display_aspect_ratio((SDTV_ASPECT_T)tv_state.display.sdtv.display_options.aspect) / ((float)m_desktopRes.iScreenWidth / (float)m_desktopRes.iScreenHeight);
     }
@@ -507,9 +532,6 @@ bool CEGLNativeTypeRaspberryPI::ProbeResolutions(std::vector<RESOLUTION_INFO> &r
     CLog::Log(LOGDEBUG, "EGL probe resolution %s:%x\n", m_desktopRes.strMode.c_str(), m_desktopRes.dwFlags);
   }
 
-  if(resolutions.size() < 2)
-    m_fixedMode = true;
-
   DLOG("CEGLNativeTypeRaspberryPI::ProbeResolutions\n");
   return true;
 #else
diff --git a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.h b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.h
index 59401f5..a0acb1a 100644
--- a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.h
+++ b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.h
@@ -59,7 +59,6 @@ class CEGLNativeTypeRaspberryPI : public CEGLNativeType
   DISPMANX_ELEMENT_HANDLE_T     m_dispman_element;
   TV_GET_STATE_RESP_T           m_tv_state;
   sem_t                         m_tv_synced;
-  bool                          m_fixedMode;
   RESOLUTION_INFO               m_desktopRes;
   int                           m_width;
   int                           m_height;

From 5ff25c07d6213a3f8b722909e1d29cec34fc67b7 Mon Sep 17 00:00:00 2001
From: macrule <macrule2001@yahoo.de>
Date: Thu, 11 Apr 2013 19:54:59 +0200
Subject: [PATCH 49/88] Added methods SuspendVideoOutput() and
 ResumeVideoOutput() to CRBP class, which can be used to power down the
 Raspberry PI's video interface, and restore it at a later point.

---
 xbmc/linux/RBP.cpp                              | 15 +++++++++++++++
 xbmc/linux/RBP.h                                |  3 +++
 xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp |  3 +++
 3 files changed, 21 insertions(+)

diff --git a/xbmc/linux/RBP.cpp b/xbmc/linux/RBP.cpp
index b67fbb1..2ff6078 100644
--- a/xbmc/linux/RBP.cpp
+++ b/xbmc/linux/RBP.cpp
@@ -212,4 +212,19 @@ void CRBP::Deinitialize()
   m_initialized     = false;
   m_omx_initialized = false;
 }
+
+void CRBP::SuspendVideoOutput()
+{
+  CLog::Log(LOGDEBUG, "Raspberry PI suspending video output\n");
+  char response[80];
+  m_DllBcmHost->vc_gencmd(response, sizeof response, "display_power 0");
+}
+
+void CRBP::ResumeVideoOutput()
+{
+  char response[80];
+  m_DllBcmHost->vc_gencmd(response, sizeof response, "display_power 1");
+  CLog::Log(LOGDEBUG, "Raspberry PI resuming video output\n");
+}
+
 #endif
diff --git a/xbmc/linux/RBP.h b/xbmc/linux/RBP.h
index 4fd18f3..f947acc 100644
--- a/xbmc/linux/RBP.h
+++ b/xbmc/linux/RBP.h
@@ -59,6 +59,9 @@ class CRBP
   DllOMX *GetDllOMX() { return m_OMX ? m_OMX->GetDll() : NULL; }
   void WaitVsync();
 
+  void SuspendVideoOutput();
+  void ResumeVideoOutput();
+
 private:
   DllBcmHost *m_DllBcmHost;
   bool       m_initialized;
diff --git a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
index 9dc39d5..ca36082 100644
--- a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
+++ b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
@@ -59,7 +59,10 @@
 # define DLOG(fmt, args...)
 #endif
 
+#if defined(TARGET_RASPBERRY_PI)
 static void SetResolutionString(RESOLUTION_INFO &res);
+static SDTV_ASPECT_T get_sdtv_aspect_from_display_aspect(float display_aspect);
+#endif
 
 CEGLNativeTypeRaspberryPI::CEGLNativeTypeRaspberryPI()
 {

From aec31972c715b6946656ad6402258cb15f542bbe Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sun, 11 Aug 2013 15:03:36 +0100
Subject: [PATCH 50/88] PowerManager (and its IPowerSyscall instance) now gets
 called from CApplication::OnKey() and can process and suppress key presses.
 This is a requirement to implement a virtual sleep state.

---
 xbmc/Application.cpp                  | 7 +++++++
 xbmc/powermanagement/IPowerSyscall.h  | 7 +++++++
 xbmc/powermanagement/PowerManager.cpp | 6 ++++++
 xbmc/powermanagement/PowerManager.h   | 3 +++
 4 files changed, 23 insertions(+)

diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp
index 10903bf..8e63e25 100644
--- a/xbmc/Application.cpp
+++ b/xbmc/Application.cpp
@@ -2332,6 +2332,13 @@ bool CApplication::OnKey(const CKey& key)
   // special handling if the screensaver is active
   CAction action = CButtonTranslator::GetInstance().GetAction(iWin, key);
 
+  // give the PowerManager a chance to process a keypress, and
+  // suppress further processing. we need this for virtual sleep.
+  if (g_powerManager.ProcessAction(action))
+  {
+    return true;
+  }
+
   // a key has been pressed.
   // reset Idle Timer
   m_idleTimer.StartZero();
diff --git a/xbmc/powermanagement/IPowerSyscall.h b/xbmc/powermanagement/IPowerSyscall.h
index 7f9e7ed..711abea 100644
--- a/xbmc/powermanagement/IPowerSyscall.h
+++ b/xbmc/powermanagement/IPowerSyscall.h
@@ -20,6 +20,9 @@
  *
  */
 
+// forward declaration
+class CAction;
+
 class IPowerEventsCallback
 {
 public:
@@ -60,6 +63,10 @@ class IPowerSyscall
    \param callback the callback to signal to
    */
   virtual bool PumpPowerEvents(IPowerEventsCallback *callback) = 0;
+
+  // this is an optional part of the interface, so we provide a no-op implementation here.
+  // return true to suppress further processing of the CAction.
+  virtual bool ProcessAction(const CAction& action) { return false; }
 };
 
 class CPowerSyscallWithoutEvents : public IPowerSyscall
diff --git a/xbmc/powermanagement/PowerManager.cpp b/xbmc/powermanagement/PowerManager.cpp
index 5900f98..6e819d9 100644
--- a/xbmc/powermanagement/PowerManager.cpp
+++ b/xbmc/powermanagement/PowerManager.cpp
@@ -225,6 +225,12 @@ void CPowerManager::ProcessEvents()
   nesting--;
 }
 
+bool CPowerManager::ProcessAction(const CAction& action)
+{
+  return m_instance->ProcessAction(action);
+}
+
+
 void CPowerManager::OnSleep()
 {
   CAnnouncementManager::Get().Announce(System, "xbmc", "OnSleep");
diff --git a/xbmc/powermanagement/PowerManager.h b/xbmc/powermanagement/PowerManager.h
index 0b1f10a..e42b143 100644
--- a/xbmc/powermanagement/PowerManager.h
+++ b/xbmc/powermanagement/PowerManager.h
@@ -58,6 +58,8 @@ class CNullPowerSyscall : public IPowerSyscall
 
 
   virtual bool PumpPowerEvents(IPowerEventsCallback *callback) { return false; }
+
+  virtual bool ProcessAction(const CAction& action) { return false; }
 };
 
 // This class will wrap and handle PowerSyscalls.
@@ -87,6 +89,7 @@ class CPowerManager : public IPowerEventsCallback
 
   static void SettingOptionsShutdownStatesFiller(const CSetting *setting, std::vector< std::pair<std::string, int> > &list, int &current, void *data);
 
+  bool ProcessAction(const CAction& action);
 private:
   void OnSleep();
   void OnWake();

From c4d68d2b3060b755f616557839e8b7d3abd1f890 Mon Sep 17 00:00:00 2001
From: macrule <macrule2001@yahoo.de>
Date: Wed, 17 Apr 2013 13:23:01 +0200
Subject: [PATCH 51/88] Added CPowerSyscallVirtualSleep class, which acts as a
 base class for devices that have no native standby mode, and need to fake it
 in some way.

---
 xbmc/powermanagement/Makefile                     |  1 +
 xbmc/powermanagement/PowerSyscallVirtualSleep.cpp | 84 +++++++++++++++++++++++
 xbmc/powermanagement/PowerSyscallVirtualSleep.h   | 56 +++++++++++++++
 3 files changed, 141 insertions(+)
 create mode 100644 xbmc/powermanagement/PowerSyscallVirtualSleep.cpp
 create mode 100644 xbmc/powermanagement/PowerSyscallVirtualSleep.h

diff --git a/xbmc/powermanagement/Makefile b/xbmc/powermanagement/Makefile
index 0b4c029..16056af 100644
--- a/xbmc/powermanagement/Makefile
+++ b/xbmc/powermanagement/Makefile
@@ -1,5 +1,6 @@
 SRCS=DPMSSupport.cpp \
      PowerManager.cpp \
+     PowerSyscallVirtualSleep.cpp \
 
 LIB=powermanagement.a
 
diff --git a/xbmc/powermanagement/PowerSyscallVirtualSleep.cpp b/xbmc/powermanagement/PowerSyscallVirtualSleep.cpp
new file mode 100644
index 0000000..6a1e47b
--- /dev/null
+++ b/xbmc/powermanagement/PowerSyscallVirtualSleep.cpp
@@ -0,0 +1,84 @@
+/*
+ *      Copyright (C) 2013 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PowerSyscallVirtualSleep.h"
+#include "guilib/Key.h"
+#include "utils/log.h"
+#include "utils/StringUtils.h"
+
+bool CPowerSyscallVirtualSleep::Suspend()
+{
+  if (m_virtualSleepState == VIRTUAL_SLEEP_STATE_AWAKE)
+  {
+    if (VirtualSleep())
+    {
+      m_virtualSleepState = VIRTUAL_SLEEP_STATE_WILL_SLEEP;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool CPowerSyscallVirtualSleep::PumpPowerEvents(IPowerEventsCallback *callback)
+{
+  if (m_virtualSleepState == VIRTUAL_SLEEP_STATE_WILL_WAKE)
+  {
+    callback->OnWake();
+    m_virtualSleepState = VIRTUAL_SLEEP_STATE_AWAKE;
+    return true;
+  }
+  else if (m_virtualSleepState == VIRTUAL_SLEEP_STATE_WILL_SLEEP)
+  {
+    callback->OnSleep();
+    m_virtualSleepState = VIRTUAL_SLEEP_STATE_ASLEEP;
+    return true;
+  }
+
+  return false;
+}
+
+bool CPowerSyscallVirtualSleep::ProcessAction(const CAction& action)
+{
+  if (m_virtualSleepState != VIRTUAL_SLEEP_STATE_ASLEEP)
+    return false;
+
+  // device is in virtual sleep, only one of the power keys will
+  // wake it up again.
+  if (action.GetID() == ACTION_BUILT_IN_FUNCTION)
+  {
+    CStdString name = action.GetName();
+    StringUtils::ToLower(name);
+    if(name.Equals("xbmc.suspend()") ||
+       name.Equals("shutdown") ||
+       name.Equals("suspend")  ||
+       name.Equals("hibernate"))
+    {
+      if(VirtualWake())
+      {
+        m_virtualSleepState = VIRTUAL_SLEEP_STATE_WILL_WAKE;
+        return false;
+      }
+    }
+  }
+
+  // wasn't a power key, suppress this and stay asleep
+  return true;
+}
diff --git a/xbmc/powermanagement/PowerSyscallVirtualSleep.h b/xbmc/powermanagement/PowerSyscallVirtualSleep.h
new file mode 100644
index 0000000..ef6e682
--- /dev/null
+++ b/xbmc/powermanagement/PowerSyscallVirtualSleep.h
@@ -0,0 +1,56 @@
+/*
+ *      Copyright (C) 2013 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _POWER_SYSCALL_VIRTUAL_SLEEP_H_
+#define _POWER_SYSCALL_VIRTUAL_SLEEP_H_
+#include "IPowerSyscall.h"
+
+// Systems that have no native standby mode, can base their
+// IPowerSyscall implementation on this class, and need only
+// implement VirtualSleep()/VirtualWake().
+class CPowerSyscallVirtualSleep : public IPowerSyscall
+{
+public:
+  CPowerSyscallVirtualSleep() : m_virtualSleepState(VIRTUAL_SLEEP_STATE_AWAKE) {}
+  virtual ~CPowerSyscallVirtualSleep() {}
+
+  virtual bool CanSuspend() { return true; }
+  virtual bool Suspend();
+
+  virtual bool PumpPowerEvents(IPowerEventsCallback *callback);
+
+  virtual bool ProcessAction(const CAction& action);
+
+  virtual bool VirtualSleep() = 0;
+  virtual bool VirtualWake()  = 0;
+
+protected:
+  // keep track of virtual sleep state for devices that support it
+  typedef enum {
+    VIRTUAL_SLEEP_STATE_AWAKE = 0,
+    VIRTUAL_SLEEP_STATE_ASLEEP,
+    VIRTUAL_SLEEP_STATE_WILL_WAKE,
+    VIRTUAL_SLEEP_STATE_WILL_SLEEP,
+  } VirtualSleepState;
+
+  VirtualSleepState m_virtualSleepState;
+};
+
+#endif // _POWER_SYSCALL_VIRTUAL_SLEEP_H_

From 4f198e9cfec4a2e261c558226ae39bf531cbdb3d Mon Sep 17 00:00:00 2001
From: macrule <macrule2001@yahoo.de>
Date: Wed, 17 Apr 2013 13:24:22 +0200
Subject: [PATCH 52/88] Added power management support for the Raspberry Pi.
 Since it doesn't support true standby, we fake it by turning video on or off,
 and ignoring remote inputs during the standby phase.

---
 xbmc/powermanagement/PowerManager.cpp              |  4 ++
 xbmc/powermanagement/linux/Makefile                |  1 +
 .../linux/RaspberryPIPowerSyscall.cpp              | 38 +++++++++++++++++
 .../linux/RaspberryPIPowerSyscall.h                | 49 ++++++++++++++++++++++
 4 files changed, 92 insertions(+)
 create mode 100644 xbmc/powermanagement/linux/RaspberryPIPowerSyscall.cpp
 create mode 100644 xbmc/powermanagement/linux/RaspberryPIPowerSyscall.h

diff --git a/xbmc/powermanagement/PowerManager.cpp b/xbmc/powermanagement/PowerManager.cpp
index 6e819d9..2d8c750 100644
--- a/xbmc/powermanagement/PowerManager.cpp
+++ b/xbmc/powermanagement/PowerManager.cpp
@@ -38,6 +38,8 @@
 
 #if defined(TARGET_DARWIN)
 #include "osx/CocoaPowerSyscall.h"
+#elif defined(TARGET_RASPBERRY_PI)
+#include "linux/RaspberryPIPowerSyscall.h"
 #elif defined(TARGET_ANDROID)
 #include "android/AndroidPowerSyscall.h"
 #elif defined(TARGET_POSIX)
@@ -71,6 +73,8 @@ void CPowerManager::Initialize()
 {
 #if defined(TARGET_DARWIN)
   m_instance = new CCocoaPowerSyscall();
+#elif defined(TARGET_RASPBERRY_PI)
+  m_instance = new CRaspberryPIPowerSyscall();
 #elif defined(TARGET_ANDROID)
   m_instance = new CAndroidPowerSyscall();
 #elif defined(TARGET_POSIX)
diff --git a/xbmc/powermanagement/linux/Makefile b/xbmc/powermanagement/linux/Makefile
index e9d95fb..b130812 100644
--- a/xbmc/powermanagement/linux/Makefile
+++ b/xbmc/powermanagement/linux/Makefile
@@ -1,5 +1,6 @@
 SRCS=ConsoleDeviceKitPowerSyscall.cpp \
      ConsoleUPowerSyscall.cpp \
+     RaspberryPIPowerSyscall.cpp \
      UPowerSyscall.cpp \
      LogindUPowerSyscall.cpp
 
diff --git a/xbmc/powermanagement/linux/RaspberryPIPowerSyscall.cpp b/xbmc/powermanagement/linux/RaspberryPIPowerSyscall.cpp
new file mode 100644
index 0000000..10deeb8
--- /dev/null
+++ b/xbmc/powermanagement/linux/RaspberryPIPowerSyscall.cpp
@@ -0,0 +1,38 @@
+/*
+ *      Copyright (C) 2013 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if defined(TARGET_RASPBERRY_PI)
+
+#include "RaspberryPIPowerSyscall.h"
+#include "RBP.h"
+
+bool CRaspberryPIPowerSyscall::VirtualSleep()
+{
+  g_RBP.SuspendVideoOutput();
+  return true;
+}
+
+bool CRaspberryPIPowerSyscall::VirtualWake()
+{
+  g_RBP.ResumeVideoOutput();
+  return true;
+}
+
+#endif
diff --git a/xbmc/powermanagement/linux/RaspberryPIPowerSyscall.h b/xbmc/powermanagement/linux/RaspberryPIPowerSyscall.h
new file mode 100644
index 0000000..fd1d67c
--- /dev/null
+++ b/xbmc/powermanagement/linux/RaspberryPIPowerSyscall.h
@@ -0,0 +1,49 @@
+#pragma once
+/*
+ *      Copyright (C) 2013 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _RASPBERRY_PI_POWER_SYSCALL_H_
+#define _RASPBERRY_PI_POWER_SYSCALL_H_
+
+#if defined(TARGET_RASPBERRY_PI)
+#include "powermanagement/PowerSyscallVirtualSleep.h"
+
+class CRaspberryPIPowerSyscall : public CPowerSyscallVirtualSleep
+{
+public:
+  CRaspberryPIPowerSyscall() : CPowerSyscallVirtualSleep() {}
+  virtual ~CRaspberryPIPowerSyscall() {}
+
+  virtual bool Powerdown()    { return false; }
+  virtual bool Hibernate()    { return false; }
+  virtual bool Reboot()       { return false; }
+
+  virtual bool CanPowerdown() { return false; }
+  virtual bool CanHibernate() { return false; }
+  virtual bool CanReboot()    { return true; }
+
+  virtual int  BatteryLevel() { return 0; }
+
+  virtual bool VirtualSleep();
+  virtual bool VirtualWake();
+};
+#endif // TARGET_RASPBERRY_PI
+
+#endif // _RASPBERRY_PI_POWER_SYSCALL_H_

From b0b217f0878b0470d6b52a2b38044c5c78196342 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Mon, 3 Mar 2014 16:16:29 +0000
Subject: [PATCH 53/88] [power] hack - don't kill lirc or cec

---
 xbmc/peripherals/devices/PeripheralCecAdapter.cpp | 37 +++++++++++++++++++++++
 xbmc/powermanagement/PowerManager.cpp             |  4 +--
 2 files changed, 39 insertions(+), 2 deletions(-)

diff --git a/xbmc/peripherals/devices/PeripheralCecAdapter.cpp b/xbmc/peripherals/devices/PeripheralCecAdapter.cpp
index 9907ba2..dfc2b78 100644
--- a/xbmc/peripherals/devices/PeripheralCecAdapter.cpp
+++ b/xbmc/peripherals/devices/PeripheralCecAdapter.cpp
@@ -183,12 +183,49 @@ void CPeripheralCecAdapter::Announce(AnnouncementFlag flag, const char *sender,
   }
   else if (flag == System && !strcmp(sender, "xbmc") && !strcmp(message, "OnSleep"))
   {
+#if 1
+  bool bSendStandbyCommands(false);
+  {
+    CSingleLock lock(m_critSection);
+    bSendStandbyCommands = m_iExitCode != EXITCODE_REBOOT &&
+                           m_iExitCode != EXITCODE_RESTARTAPP &&
+                           !m_bDeviceRemoved &&
+                           (!m_bGoingToStandby || GetSettingBool("standby_tv_on_pc_standby")) &&
+                           GetSettingBool("enabled");
+
+    if (m_bGoingToStandby)
+      m_bActiveSourceBeforeStandby = m_cecAdapter->IsLibCECActiveSource();
+  }
+
+  if (bSendStandbyCommands)
+  {
+    if (m_cecAdapter->IsLibCECActiveSource())
+    {
+      if (!m_configuration.powerOffDevices.IsEmpty())
+      {
+        CLog::Log(LOGDEBUG, "%s - sending standby commands", __FUNCTION__);
+        m_standbySent = CDateTime::GetCurrentDateTime();
+        m_cecAdapter->StandbyDevices();
+      }
+      else if (m_configuration.bSendInactiveSource == 1)
+      {
+        CLog::Log(LOGDEBUG, "%s - sending inactive source commands", __FUNCTION__);
+        m_cecAdapter->SetInactiveView();
+      }
+    }
+    else
+    {
+      CLog::Log(LOGDEBUG, "%s - XBMC is not the active source, not sending any standby commands", __FUNCTION__);
+    }
+  }
+#else
     // this will also power off devices when we're the active source
     {
       CSingleLock lock(m_critSection);
       m_bGoingToStandby = true;
     }
     StopThread();
+#endif
   }
   else if (flag == System && !strcmp(sender, "xbmc") && !strcmp(message, "OnWake"))
   {
diff --git a/xbmc/powermanagement/PowerManager.cpp b/xbmc/powermanagement/PowerManager.cpp
index 2d8c750..901f449 100644
--- a/xbmc/powermanagement/PowerManager.cpp
+++ b/xbmc/powermanagement/PowerManager.cpp
@@ -241,7 +241,7 @@ void CPowerManager::OnSleep()
   CLog::Log(LOGNOTICE, "%s: Running sleep jobs", __FUNCTION__);
 
   // stop lirc
-#if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
+#if 0 //defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
   CLog::Log(LOGNOTICE, "%s: Stopping lirc", __FUNCTION__);
   CBuiltins::Execute("LIRC.Stop");
 #endif
@@ -277,7 +277,7 @@ void CPowerManager::OnWake()
 #endif
 
   // restart lirc
-#if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
+#if 0 // defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
   CLog::Log(LOGNOTICE, "%s: Restarting lirc", __FUNCTION__);
   CBuiltins::Execute("LIRC.Start");
 #endif

From c6da6fa95b75e8103141d1ae6460a91562d1b5b1 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Mon, 3 Mar 2014 16:47:54 +0000
Subject: [PATCH 54/88] [power] hack - wake on any action

---
 xbmc/powermanagement/PowerSyscallVirtualSleep.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/xbmc/powermanagement/PowerSyscallVirtualSleep.cpp b/xbmc/powermanagement/PowerSyscallVirtualSleep.cpp
index 6a1e47b..a717a09 100644
--- a/xbmc/powermanagement/PowerSyscallVirtualSleep.cpp
+++ b/xbmc/powermanagement/PowerSyscallVirtualSleep.cpp
@@ -62,14 +62,14 @@ bool CPowerSyscallVirtualSleep::ProcessAction(const CAction& action)
 
   // device is in virtual sleep, only one of the power keys will
   // wake it up again.
-  if (action.GetID() == ACTION_BUILT_IN_FUNCTION)
+  //if (action.GetID() == ACTION_BUILT_IN_FUNCTION)
   {
     CStdString name = action.GetName();
     StringUtils::ToLower(name);
-    if(name.Equals("xbmc.suspend()") ||
+    /*if(name.Equals("system.suspend") ||
        name.Equals("shutdown") ||
        name.Equals("suspend")  ||
-       name.Equals("hibernate"))
+       name.Equals("hibernate"))*/
     {
       if(VirtualWake())
       {

From 178e9a2a4515847c60c42e9a7faa31940924a6ae Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Mon, 3 Mar 2014 17:30:07 +0000
Subject: [PATCH 55/88] [power] hack - Make suspend toggle suspend state

---
 xbmc/powermanagement/PowerSyscallVirtualSleep.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/xbmc/powermanagement/PowerSyscallVirtualSleep.cpp b/xbmc/powermanagement/PowerSyscallVirtualSleep.cpp
index a717a09..d39c3ed 100644
--- a/xbmc/powermanagement/PowerSyscallVirtualSleep.cpp
+++ b/xbmc/powermanagement/PowerSyscallVirtualSleep.cpp
@@ -33,6 +33,11 @@ bool CPowerSyscallVirtualSleep::Suspend()
       return true;
     }
   }
+  else if (VirtualWake())
+  {
+    m_virtualSleepState = VIRTUAL_SLEEP_STATE_WILL_WAKE;
+    return false;
+  }
 
   return false;
 }

From 6f0f5289e4d100848caba3d5e9728a8257273368 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Tue, 4 Mar 2014 19:33:44 +0000
Subject: [PATCH 56/88] [power] Add back in powerdown and reboot

---
 .../linux/RaspberryPIPowerSyscall.cpp              | 34 ++++++++++++++++++++++
 .../linux/RaspberryPIPowerSyscall.h                |  6 ++--
 2 files changed, 37 insertions(+), 3 deletions(-)

diff --git a/xbmc/powermanagement/linux/RaspberryPIPowerSyscall.cpp b/xbmc/powermanagement/linux/RaspberryPIPowerSyscall.cpp
index 10deeb8..220bca9 100644
--- a/xbmc/powermanagement/linux/RaspberryPIPowerSyscall.cpp
+++ b/xbmc/powermanagement/linux/RaspberryPIPowerSyscall.cpp
@@ -20,7 +20,11 @@
 
 #if defined(TARGET_RASPBERRY_PI)
 
+#include "system.h"
 #include "RaspberryPIPowerSyscall.h"
+#if defined(HAS_DBUS)
+#include "LogindUPowerSyscall.h"
+#endif
 #include "RBP.h"
 
 bool CRaspberryPIPowerSyscall::VirtualSleep()
@@ -35,4 +39,34 @@ bool CRaspberryPIPowerSyscall::VirtualWake()
   return true;
 }
 
+bool CRaspberryPIPowerSyscall::Powerdown()
+{
+  int s = false;
+#if defined(HAS_DBUS)
+  if (CLogindUPowerSyscall::HasLogind())
+  {
+    IPowerSyscall *m_instance = new CLogindUPowerSyscall;
+    if (m_instance->CanPowerdown())
+      s = m_instance->Powerdown();
+    delete m_instance;
+  }
+#endif
+  return s;
+}
+
+bool CRaspberryPIPowerSyscall::Reboot()
+{
+  int s = false;
+#if defined(HAS_DBUS)
+  if (CLogindUPowerSyscall::HasLogind())
+  {
+    IPowerSyscall *m_instance = new CLogindUPowerSyscall;
+    if (m_instance->CanReboot())
+      s = m_instance->Reboot();
+    delete m_instance;
+  }
+#endif
+  return s;
+}
+
 #endif
diff --git a/xbmc/powermanagement/linux/RaspberryPIPowerSyscall.h b/xbmc/powermanagement/linux/RaspberryPIPowerSyscall.h
index fd1d67c..062132e 100644
--- a/xbmc/powermanagement/linux/RaspberryPIPowerSyscall.h
+++ b/xbmc/powermanagement/linux/RaspberryPIPowerSyscall.h
@@ -31,11 +31,11 @@ class CRaspberryPIPowerSyscall : public CPowerSyscallVirtualSleep
   CRaspberryPIPowerSyscall() : CPowerSyscallVirtualSleep() {}
   virtual ~CRaspberryPIPowerSyscall() {}
 
-  virtual bool Powerdown()    { return false; }
+  virtual bool Powerdown();
   virtual bool Hibernate()    { return false; }
-  virtual bool Reboot()       { return false; }
+  virtual bool Reboot();
 
-  virtual bool CanPowerdown() { return false; }
+  virtual bool CanPowerdown() { return true; }
   virtual bool CanHibernate() { return false; }
   virtual bool CanReboot()    { return true; }
 

From 23d1437daeadcca45975cc39c1b830e839a8309c Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sat, 26 Apr 2014 17:27:52 +0100
Subject: [PATCH 57/88] [cec] Don't suspend pi on tv switch off - it can't wake
 up

---
 system/peripherals.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/system/peripherals.xml b/system/peripherals.xml
index a906628..9b5271a 100644
--- a/system/peripherals.xml
+++ b/system/peripherals.xml
@@ -16,7 +16,7 @@
     <setting key="send_inactive_source" type="bool" value="1" label="36025" order="5" />
     <setting key="cec_standby_screensaver" type="bool" value="0" label="36009" order="6" />
     <setting key="cec_wake_screensaver" type="bool" value="1" label="36010" order="7" />
-    <setting key="standby_pc_on_tv_standby" type="enum" value="13011" label="36029" order="8" lvalues="36028|13005|13011" />
+    <setting key="standby_pc_on_tv_standby" type="enum" value="36028" label="36029" order="8" lvalues="36028|13005|13011" />
     <setting key="standby_tv_on_pc_standby" type="bool" value="1" label="36026" order="9" />
     <setting key="use_tv_menu_language" type="bool" value="1" label="36018" order="10" />
     <setting key="pause_playback_on_deactivate" type="bool" value="1" label="36033" order="11" />

From 9fe228d21a28daff0a7444a6ae36bf092931018b Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Mon, 18 Aug 2014 19:09:32 +0100
Subject: [PATCH 58/88] rbp: Use new dispmanx function for vsync callbacks

---
 xbmc/linux/RBP.cpp                              | 85 ++++++++++++++-----------
 xbmc/linux/RBP.h                                |  5 +-
 xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp |  4 +-
 3 files changed, 52 insertions(+), 42 deletions(-)

diff --git a/xbmc/linux/RBP.cpp b/xbmc/linux/RBP.cpp
index 2ff6078..360fc39 100644
--- a/xbmc/linux/RBP.cpp
+++ b/xbmc/linux/RBP.cpp
@@ -33,7 +33,7 @@ CRBP::CRBP()
   m_omx_initialized = false;
   m_DllBcmHost      = new DllBcmHost();
   m_OMX             = new COMXCore();
-  m_element = 0;
+  m_display = DISPMANX_NO_HANDLE;
 }
 
 CRBP::~CRBP()
@@ -55,8 +55,7 @@ bool CRBP::Initialize()
 
   m_DllBcmHost->bcm_host_init();
 
-  uint32_t                    vc_image_ptr;
-  m_resource = vc_dispmanx_resource_create( VC_IMAGE_RGB565, 1, 1, &vc_image_ptr );
+  //OpenDisplay(0 /*screen*/);
 
   m_omx_initialized = m_OMX->Initialize();
   if(!m_omx_initialized)
@@ -109,13 +108,24 @@ void CRBP::LogFirmwareVerison()
   CLog::Log(LOGNOTICE, "Config:\n%s", response);
 }
 
+DISPMANX_DISPLAY_HANDLE_T CRBP::OpenDisplay(uint32_t device)
+{
+  if (m_display == DISPMANX_NO_HANDLE)
+    m_display = vc_dispmanx_display_open( 0 /*screen*/ );
+  return m_display;
+}
+
+void CRBP::CloseDisplay(DISPMANX_DISPLAY_HANDLE_T display)
+{
+  assert(display == m_display);
+  vc_dispmanx_display_close(m_display);
+  m_display = DISPMANX_NO_HANDLE;
+}
+
 void CRBP::GetDisplaySize(int &width, int &height)
 {
-  DISPMANX_DISPLAY_HANDLE_T display;
   DISPMANX_MODEINFO_T info;
-
-  display = vc_dispmanx_display_open( 0 /*screen*/ );
-  if (vc_dispmanx_display_get_info(display, &info) == 0)
+  if (vc_dispmanx_display_get_info(m_display, &info) == 0)
   {
     width = info.width;
     height = info.height;
@@ -125,12 +135,10 @@ void CRBP::GetDisplaySize(int &width, int &height)
     width = 0;
     height = 0;
   }
-  vc_dispmanx_display_close(display );
 }
 
 unsigned char *CRBP::CaptureDisplay(int width, int height, int *pstride, bool swap_red_blue, bool video_only)
 {
-  DISPMANX_DISPLAY_HANDLE_T display;
   DISPMANX_RESOURCE_HANDLE_T resource;
   VC_RECT_T rect;
   unsigned char *image = NULL;
@@ -145,7 +153,6 @@ unsigned char *CRBP::CaptureDisplay(int width, int height, int *pstride, bool sw
   if (!pstride)
     flags |= DISPMANX_SNAPSHOT_PACK;
 
-  display = vc_dispmanx_display_open( 0 /*screen*/ );
   stride = ((width + 15) & ~15) * 4;
   image = new unsigned char [height * stride];
 
@@ -153,45 +160,44 @@ unsigned char *CRBP::CaptureDisplay(int width, int height, int *pstride, bool sw
   {
     resource = vc_dispmanx_resource_create( VC_IMAGE_RGBA32, width, height, &vc_image_ptr );
 
-    vc_dispmanx_snapshot(display, resource, (DISPMANX_TRANSFORM_T)flags);
+    assert(m_display != DISPMANX_NO_HANDLE);
+    vc_dispmanx_snapshot(m_display, resource, (DISPMANX_TRANSFORM_T)flags);
 
     vc_dispmanx_rect_set(&rect, 0, 0, width, height);
     vc_dispmanx_resource_read_data(resource, &rect, image, stride);
     vc_dispmanx_resource_delete( resource );
-    vc_dispmanx_display_close(display );
   }
   if (pstride)
     *pstride = stride;
   return image;
 }
 
-void CRBP::WaitVsync(void)
+
+static void vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *arg)
+{
+  CEvent *sync = (CEvent *)arg;
+  sync->Set();
+}
+
+void CRBP::WaitVsync()
 {
-  DISPMANX_DISPLAY_HANDLE_T display = vc_dispmanx_display_open( 0 /*screen*/ );
-  DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0);
-
-  VC_DISPMANX_ALPHA_T alpha = { (DISPMANX_FLAGS_ALPHA_T)(DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS), 120, /*alpha 0->255*/ 0 };
-  VC_RECT_T       src_rect;
-  VC_RECT_T       dst_rect;
-  vc_dispmanx_rect_set( &src_rect, 0, 0, 1 << 16, 1 << 16 );
-  vc_dispmanx_rect_set( &dst_rect, 0, 0, 1, 1 );
-
-  if (m_element)
-    vc_dispmanx_element_remove( update, m_element );
-
-  m_element = vc_dispmanx_element_add( update,
-                                            display,
-                                            2000,               // layer
-                                            &dst_rect,
-                                            m_resource,
-                                            &src_rect,
-                                            DISPMANX_PROTECTION_NONE,
-                                            &alpha,
-                                            NULL,             // clamp
-                                            (DISPMANX_TRANSFORM_T)0 );
-
-  vc_dispmanx_update_submit_sync(update);
-  vc_dispmanx_display_close( display );
+  int s;
+  CEvent sync;
+  DISPMANX_DISPLAY_HANDLE_T m_display = vc_dispmanx_display_open( 0 /*screen*/ );
+  if (m_display == DISPMANX_NO_HANDLE)
+  {
+    CLog::Log(LOGDEBUG, "CRBP::%s skipping while display closed", __func__);
+    return;
+  }
+  s = vc_dispmanx_vsync_callback(m_display, vsync_callback, (void *)&sync);
+  if (s == 0)
+  {
+    sync.Wait();
+  }
+  else assert(0);
+  s = vc_dispmanx_vsync_callback(m_display, NULL, NULL);
+  assert(s == 0);
+  vc_dispmanx_display_close( m_display );
 }
 
 
@@ -203,6 +209,9 @@ void CRBP::Deinitialize()
   if(m_omx_initialized)
     m_OMX->Deinitialize();
 
+  if (m_display)
+    CloseDisplay(m_display);
+
   m_DllBcmHost->bcm_host_deinit();
 
   if(m_initialized)
diff --git a/xbmc/linux/RBP.h b/xbmc/linux/RBP.h
index f947acc..ca80045 100644
--- a/xbmc/linux/RBP.h
+++ b/xbmc/linux/RBP.h
@@ -53,6 +53,8 @@ class CRBP
   bool GetCodecMpg2() { return m_codec_mpg2_enabled; }
   bool GetCodecWvc1() { return m_codec_wvc1_enabled; }
   void GetDisplaySize(int &width, int &height);
+  DISPMANX_DISPLAY_HANDLE_T OpenDisplay(uint32_t device);
+  void CloseDisplay(DISPMANX_DISPLAY_HANDLE_T display);
   int GetGUIResolutionLimit() { return m_gui_resolution_limit; }
   // stride can be null for packed output
   unsigned char *CaptureDisplay(int width, int height, int *stride, bool swap_red_blue, bool video_only = true);
@@ -73,10 +75,9 @@ class CRBP
   bool       m_codec_mpg2_enabled;
   bool       m_codec_wvc1_enabled;
   COMXCore   *m_OMX;
-  DISPMANX_RESOURCE_HANDLE_T m_resource;
-  DISPMANX_ELEMENT_HANDLE_T m_element;
   class DllLibOMXCore;
   CCriticalSection m_critSection;
+  DISPMANX_DISPLAY_HANDLE_T m_display;
 };
 
 extern CRBP g_RBP;
diff --git a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
index ca36082..4b74ea0 100644
--- a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
+++ b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
@@ -306,7 +306,7 @@ bool CEGLNativeTypeRaspberryPI::SetNativeResolution(const RESOLUTION_INFO &res)
     m_desktopRes = res;
   }
 
-  m_dispman_display = m_DllBcmHost->vc_dispmanx_display_open(0);
+  m_dispman_display = g_RBP.OpenDisplay(0);
 
   m_width   = res.iWidth;
   m_height  = res.iHeight;
@@ -571,7 +571,7 @@ void CEGLNativeTypeRaspberryPI::DestroyDispmaxWindow()
 
   if (m_dispman_display != DISPMANX_NO_HANDLE)
   {
-    m_DllBcmHost->vc_dispmanx_display_close(m_dispman_display);
+    g_RBP.CloseDisplay(m_dispman_display);
     m_dispman_display = DISPMANX_NO_HANDLE;
   }
   DLOG("CEGLNativeTypeRaspberryPI::DestroyDispmaxWindow\n");

From fe17b7692bd6141a191b89dfcb2c056da01e9181 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Tue, 19 Aug 2014 17:56:45 +0100
Subject: [PATCH 59/88] Revert "rbp: Use new dispmanx function for vsync
 callbacks"

This reverts commit afbf8fbceaa6649fb4a6bbd9a1cee6087590412b.
---
 xbmc/linux/RBP.cpp                              | 85 +++++++++++--------------
 xbmc/linux/RBP.h                                |  5 +-
 xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp |  4 +-
 3 files changed, 42 insertions(+), 52 deletions(-)

diff --git a/xbmc/linux/RBP.cpp b/xbmc/linux/RBP.cpp
index 360fc39..2ff6078 100644
--- a/xbmc/linux/RBP.cpp
+++ b/xbmc/linux/RBP.cpp
@@ -33,7 +33,7 @@ CRBP::CRBP()
   m_omx_initialized = false;
   m_DllBcmHost      = new DllBcmHost();
   m_OMX             = new COMXCore();
-  m_display = DISPMANX_NO_HANDLE;
+  m_element = 0;
 }
 
 CRBP::~CRBP()
@@ -55,7 +55,8 @@ bool CRBP::Initialize()
 
   m_DllBcmHost->bcm_host_init();
 
-  //OpenDisplay(0 /*screen*/);
+  uint32_t                    vc_image_ptr;
+  m_resource = vc_dispmanx_resource_create( VC_IMAGE_RGB565, 1, 1, &vc_image_ptr );
 
   m_omx_initialized = m_OMX->Initialize();
   if(!m_omx_initialized)
@@ -108,24 +109,13 @@ void CRBP::LogFirmwareVerison()
   CLog::Log(LOGNOTICE, "Config:\n%s", response);
 }
 
-DISPMANX_DISPLAY_HANDLE_T CRBP::OpenDisplay(uint32_t device)
-{
-  if (m_display == DISPMANX_NO_HANDLE)
-    m_display = vc_dispmanx_display_open( 0 /*screen*/ );
-  return m_display;
-}
-
-void CRBP::CloseDisplay(DISPMANX_DISPLAY_HANDLE_T display)
-{
-  assert(display == m_display);
-  vc_dispmanx_display_close(m_display);
-  m_display = DISPMANX_NO_HANDLE;
-}
-
 void CRBP::GetDisplaySize(int &width, int &height)
 {
+  DISPMANX_DISPLAY_HANDLE_T display;
   DISPMANX_MODEINFO_T info;
-  if (vc_dispmanx_display_get_info(m_display, &info) == 0)
+
+  display = vc_dispmanx_display_open( 0 /*screen*/ );
+  if (vc_dispmanx_display_get_info(display, &info) == 0)
   {
     width = info.width;
     height = info.height;
@@ -135,10 +125,12 @@ void CRBP::GetDisplaySize(int &width, int &height)
     width = 0;
     height = 0;
   }
+  vc_dispmanx_display_close(display );
 }
 
 unsigned char *CRBP::CaptureDisplay(int width, int height, int *pstride, bool swap_red_blue, bool video_only)
 {
+  DISPMANX_DISPLAY_HANDLE_T display;
   DISPMANX_RESOURCE_HANDLE_T resource;
   VC_RECT_T rect;
   unsigned char *image = NULL;
@@ -153,6 +145,7 @@ unsigned char *CRBP::CaptureDisplay(int width, int height, int *pstride, bool sw
   if (!pstride)
     flags |= DISPMANX_SNAPSHOT_PACK;
 
+  display = vc_dispmanx_display_open( 0 /*screen*/ );
   stride = ((width + 15) & ~15) * 4;
   image = new unsigned char [height * stride];
 
@@ -160,44 +153,45 @@ unsigned char *CRBP::CaptureDisplay(int width, int height, int *pstride, bool sw
   {
     resource = vc_dispmanx_resource_create( VC_IMAGE_RGBA32, width, height, &vc_image_ptr );
 
-    assert(m_display != DISPMANX_NO_HANDLE);
-    vc_dispmanx_snapshot(m_display, resource, (DISPMANX_TRANSFORM_T)flags);
+    vc_dispmanx_snapshot(display, resource, (DISPMANX_TRANSFORM_T)flags);
 
     vc_dispmanx_rect_set(&rect, 0, 0, width, height);
     vc_dispmanx_resource_read_data(resource, &rect, image, stride);
     vc_dispmanx_resource_delete( resource );
+    vc_dispmanx_display_close(display );
   }
   if (pstride)
     *pstride = stride;
   return image;
 }
 
-
-static void vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *arg)
-{
-  CEvent *sync = (CEvent *)arg;
-  sync->Set();
-}
-
-void CRBP::WaitVsync()
+void CRBP::WaitVsync(void)
 {
-  int s;
-  CEvent sync;
-  DISPMANX_DISPLAY_HANDLE_T m_display = vc_dispmanx_display_open( 0 /*screen*/ );
-  if (m_display == DISPMANX_NO_HANDLE)
-  {
-    CLog::Log(LOGDEBUG, "CRBP::%s skipping while display closed", __func__);
-    return;
-  }
-  s = vc_dispmanx_vsync_callback(m_display, vsync_callback, (void *)&sync);
-  if (s == 0)
-  {
-    sync.Wait();
-  }
-  else assert(0);
-  s = vc_dispmanx_vsync_callback(m_display, NULL, NULL);
-  assert(s == 0);
-  vc_dispmanx_display_close( m_display );
+  DISPMANX_DISPLAY_HANDLE_T display = vc_dispmanx_display_open( 0 /*screen*/ );
+  DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0);
+
+  VC_DISPMANX_ALPHA_T alpha = { (DISPMANX_FLAGS_ALPHA_T)(DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS), 120, /*alpha 0->255*/ 0 };
+  VC_RECT_T       src_rect;
+  VC_RECT_T       dst_rect;
+  vc_dispmanx_rect_set( &src_rect, 0, 0, 1 << 16, 1 << 16 );
+  vc_dispmanx_rect_set( &dst_rect, 0, 0, 1, 1 );
+
+  if (m_element)
+    vc_dispmanx_element_remove( update, m_element );
+
+  m_element = vc_dispmanx_element_add( update,
+                                            display,
+                                            2000,               // layer
+                                            &dst_rect,
+                                            m_resource,
+                                            &src_rect,
+                                            DISPMANX_PROTECTION_NONE,
+                                            &alpha,
+                                            NULL,             // clamp
+                                            (DISPMANX_TRANSFORM_T)0 );
+
+  vc_dispmanx_update_submit_sync(update);
+  vc_dispmanx_display_close( display );
 }
 
 
@@ -209,9 +203,6 @@ void CRBP::Deinitialize()
   if(m_omx_initialized)
     m_OMX->Deinitialize();
 
-  if (m_display)
-    CloseDisplay(m_display);
-
   m_DllBcmHost->bcm_host_deinit();
 
   if(m_initialized)
diff --git a/xbmc/linux/RBP.h b/xbmc/linux/RBP.h
index ca80045..f947acc 100644
--- a/xbmc/linux/RBP.h
+++ b/xbmc/linux/RBP.h
@@ -53,8 +53,6 @@ class CRBP
   bool GetCodecMpg2() { return m_codec_mpg2_enabled; }
   bool GetCodecWvc1() { return m_codec_wvc1_enabled; }
   void GetDisplaySize(int &width, int &height);
-  DISPMANX_DISPLAY_HANDLE_T OpenDisplay(uint32_t device);
-  void CloseDisplay(DISPMANX_DISPLAY_HANDLE_T display);
   int GetGUIResolutionLimit() { return m_gui_resolution_limit; }
   // stride can be null for packed output
   unsigned char *CaptureDisplay(int width, int height, int *stride, bool swap_red_blue, bool video_only = true);
@@ -75,9 +73,10 @@ class CRBP
   bool       m_codec_mpg2_enabled;
   bool       m_codec_wvc1_enabled;
   COMXCore   *m_OMX;
+  DISPMANX_RESOURCE_HANDLE_T m_resource;
+  DISPMANX_ELEMENT_HANDLE_T m_element;
   class DllLibOMXCore;
   CCriticalSection m_critSection;
-  DISPMANX_DISPLAY_HANDLE_T m_display;
 };
 
 extern CRBP g_RBP;
diff --git a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
index 4b74ea0..ca36082 100644
--- a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
+++ b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
@@ -306,7 +306,7 @@ bool CEGLNativeTypeRaspberryPI::SetNativeResolution(const RESOLUTION_INFO &res)
     m_desktopRes = res;
   }
 
-  m_dispman_display = g_RBP.OpenDisplay(0);
+  m_dispman_display = m_DllBcmHost->vc_dispmanx_display_open(0);
 
   m_width   = res.iWidth;
   m_height  = res.iHeight;
@@ -571,7 +571,7 @@ void CEGLNativeTypeRaspberryPI::DestroyDispmaxWindow()
 
   if (m_dispman_display != DISPMANX_NO_HANDLE)
   {
-    g_RBP.CloseDisplay(m_dispman_display);
+    m_DllBcmHost->vc_dispmanx_display_close(m_dispman_display);
     m_dispman_display = DISPMANX_NO_HANDLE;
   }
   DLOG("CEGLNativeTypeRaspberryPI::DestroyDispmaxWindow\n");

From 609deee8db8195bf7bfc5b242e5ab99f53782721 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Fri, 27 Jun 2014 00:01:05 +0100
Subject: [PATCH 60/88] [rbp] Resume video output on startup

---
 xbmc/linux/RBP.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/xbmc/linux/RBP.cpp b/xbmc/linux/RBP.cpp
index 2ff6078..34e0108 100644
--- a/xbmc/linux/RBP.cpp
+++ b/xbmc/linux/RBP.cpp
@@ -88,6 +88,9 @@ bool CRBP::Initialize()
   if (g_advancedSettings.m_cacheMemBufferSize == ~0)
     g_advancedSettings.m_cacheMemBufferSize = m_arm_mem < 256 ? 1024 * 1024 * 2 : 1024 * 1024 * 20;
 
+  // in case xbcm was restarted when suspended
+  ResumeVideoOutput();
+
   g_OMXImage.Initialize();
   m_omx_image_init = true;
   return true;

From 3e1a03218ec3f36542bd1309144ae6f525e73b24 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sat, 16 Aug 2014 21:01:42 +0100
Subject: [PATCH 61/88] omxrender: Hacks to reduce GUI rendering rate when
 playing video

---
 language/English/strings.po | 27 +++++++++++++++++++++++++++
 system/settings/rbp.xml     | 16 ++++++++++++++++
 xbmc/Application.cpp        | 17 +++++++++++++++++
 3 files changed, 60 insertions(+)

diff --git a/language/English/strings.po b/language/English/strings.po
index aee9a3a..996f682 100755
--- a/language/English/strings.po
+++ b/language/English/strings.po
@@ -15977,3 +15977,30 @@ msgstr ""
 msgctxt "#37034"
 msgid "Uses codec information and audio setting to choose dvdplayer or omxplayer as appropriate"
 msgstr ""
+
+#: system/settings/settings.xml
+msgctxt "#38000"
+msgid "Limit GUI updates when playing video"
+msgstr ""
+
+#: system/settings/settings.xml
+msgctxt "#38001"
+msgid "This can reduce CPU when playing video by updating the overlays less often"
+msgstr ""
+
+#: system/settings/settings.xml
+msgctxt "#38002"
+msgid "Off"
+msgstr ""
+
+msgctxt "#38003"
+msgid "5 fps"
+msgstr ""
+
+msgctxt "#38004"
+msgid "10 fps"
+msgstr ""
+
+msgctxt "#38005"
+msgid "24 fps"
+msgstr ""
diff --git a/system/settings/rbp.xml b/system/settings/rbp.xml
index 13e30ad..e721d31 100644
--- a/system/settings/rbp.xml
+++ b/system/settings/rbp.xml
@@ -25,6 +25,22 @@
           <control type="toggle" />
         </setting>
       </group>
+      <group id="3">
+        <setting id="videoplayer.limitguiupdate" type="integer" label="38000" help="38001">
+          <level>2</level>
+          <default>10</default>
+          <constraints>
+            <options>
+              <option label="38002">0</option>   <!-- off -->
+              <option label="38003">5</option>   <!-- 5 fps -->
+              <option label="38004">10</option>  <!-- 10 fps -->
+              <option label="38005">24</option>  <!-- 24 fps -->
+            </options>
+          </constraints>
+          <control type="spinner" format="string" />
+          <control type="edit" format="integer" />
+        </setting>
+      </group>
     </category>
   </section>
 
diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp
index 8e63e25..9c74cbd 100644
--- a/xbmc/Application.cpp
+++ b/xbmc/Application.cpp
@@ -2180,6 +2180,23 @@ void CApplication::Render()
   if (m_bStop)
     return;
 
+#ifdef TARGET_RASPBERRY_PI
+  if (g_graphicsContext.IsFullScreenVideo() && !m_pPlayer->IsPausedPlayback())
+  {
+    int fps = CSettings::Get().GetInt("videoplayer.limitguiupdate");
+    unsigned int now = XbmcThreads::SystemClockMillis();
+    unsigned int frameTime = now - m_lastFrameTime;
+    if (fps > 0 && frameTime * fps < 1000)
+    {
+      g_renderManager.FrameWait(100);
+      g_infoManager.UpdateFPS();
+      g_renderManager.FrameMove();
+      g_renderManager.FrameFinish();
+      return;
+    }
+  }
+#endif
+
   MEASURE_FUNCTION;
 
   int vsync_mode = CSettings::Get().GetInt("videoscreen.vsync");

From de90d423fe10286ae4eef2f3501f04cd2aa63cf4 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Tue, 9 Sep 2014 12:04:26 +0100
Subject: [PATCH 62/88] egl: Treat unknown display aspect ratio as square pixel

---
 xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
index ca36082..1529045 100644
--- a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
+++ b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
@@ -493,7 +493,7 @@ bool CEGLNativeTypeRaspberryPI::ProbeResolutions(std::vector<RESOLUTION_INFO> &r
       m_desktopRes.iScreenWidth = tv_state.display.hdmi.width;
       m_desktopRes.iScreenHeight= tv_state.display.hdmi.height;
       m_desktopRes.dwFlags      = MAKEFLAGS(tv_state.display.hdmi.group, tv_state.display.hdmi.mode, tv_state.display.hdmi.scan_mode);
-      m_desktopRes.fPixelRatio  = get_display_aspect_ratio((HDMI_ASPECT_T)tv_state.display.hdmi.display_options.aspect) / ((float)m_desktopRes.iScreenWidth / (float)m_desktopRes.iScreenHeight);
+      m_desktopRes.fPixelRatio  = tv_state.display.hdmi.display_options.aspect == 0 ? 1.0f : get_display_aspect_ratio((HDMI_ASPECT_T)tv_state.display.hdmi.display_options.aspect) / ((float)m_desktopRes.iScreenWidth / (float)m_desktopRes.iScreenHeight);
       // Also add 3D flags
       if (tv_state.display.hdmi.format_3d == HDMI_3D_FORMAT_SBS_HALF)
         m_desktopRes.dwFlags |= D3DPRESENTFLAG_MODE3DSBS;
@@ -514,7 +514,7 @@ bool CEGLNativeTypeRaspberryPI::ProbeResolutions(std::vector<RESOLUTION_INFO> &r
       m_desktopRes.iScreenHeight= tv_state.display.sdtv.height;
       m_desktopRes.dwFlags      = MAKEFLAGS(HDMI_RES_GROUP_INVALID, tv_state.display.sdtv.mode, 1);
       m_desktopRes.fRefreshRate = (float)tv_state.display.sdtv.frame_rate;
-      m_desktopRes.fPixelRatio  = get_display_aspect_ratio((SDTV_ASPECT_T)tv_state.display.sdtv.display_options.aspect) / ((float)m_desktopRes.iScreenWidth / (float)m_desktopRes.iScreenHeight);
+      m_desktopRes.fPixelRatio  = tv_state.display.hdmi.display_options.aspect == 0 ? 1.0f : get_display_aspect_ratio((SDTV_ASPECT_T)tv_state.display.sdtv.display_options.aspect) / ((float)m_desktopRes.iScreenWidth / (float)m_desktopRes.iScreenHeight);
     }
 
     SetResolutionString(m_desktopRes);

From 40f5a6ec1e609050700e235e106df623bcccaeb8 Mon Sep 17 00:00:00 2001
From: anaconda <anaconda@menakite.eu>
Date: Thu, 11 Sep 2014 21:30:43 +0200
Subject: [PATCH 63/88] Disable textbox autoscrolling while on screensaver.

---
 xbmc/guilib/GUITextBox.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/xbmc/guilib/GUITextBox.cpp b/xbmc/guilib/GUITextBox.cpp
index b7ef051..ba68fad 100644
--- a/xbmc/guilib/GUITextBox.cpp
+++ b/xbmc/guilib/GUITextBox.cpp
@@ -23,6 +23,7 @@
 #include "utils/XBMCTinyXML.h"
 #include "utils/MathUtils.h"
 #include "utils/StringUtils.h"
+#include "Application.h"
 
 using namespace std;
 
@@ -132,7 +133,7 @@ void CGUITextBox::Process(unsigned int currentTime, CDirtyRegionList &dirtyregio
   // update our auto-scrolling as necessary
   if (m_autoScrollTime && m_lines.size() > m_itemsPerPage)
   {
-    if (!m_autoScrollCondition || m_autoScrollCondition->Get())
+    if ((!m_autoScrollCondition || m_autoScrollCondition->Get()) && !g_application.IsInScreenSaver())
     {
       if (m_lastRenderTime)
         m_autoScrollDelayTime += currentTime - m_lastRenderTime;

From 12830030ed4f1d7864bb3bb43d98d44526b011e0 Mon Sep 17 00:00:00 2001
From: anaconda <anaconda@menakite.eu>
Date: Sat, 13 Sep 2014 19:49:01 +0200
Subject: [PATCH 64/88] SQUASH: only if dim or black

---
 xbmc/Application.cpp       | 7 +++++++
 xbmc/Application.h         | 2 ++
 xbmc/guilib/GUITextBox.cpp | 4 +++-
 3 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp
index 9c74cbd..b2d56cd 100644
--- a/xbmc/Application.cpp
+++ b/xbmc/Application.cpp
@@ -5813,3 +5813,10 @@ void CApplication::CloseNetworkShares()
   CSFTPSessionManager::DisconnectAllSessions();
 #endif
 }
+
+std::string CApplication::GetScreenSaverId()
+{
+  if (m_screenSaver)
+    return m_screenSaver->ID();
+  return "";
+}
diff --git a/xbmc/Application.h b/xbmc/Application.h
index 4157891..f807a98 100644
--- a/xbmc/Application.h
+++ b/xbmc/Application.h
@@ -373,6 +373,8 @@ class CApplication : public CXBApplicationEx, public IPlayerCallback, public IMs
 
   void SetLoggingIn(bool loggingIn) { m_loggingIn = loggingIn; }
 
+  std::string GetScreenSaverId();
+
 protected:
   virtual bool OnSettingsSaving() const;
 
diff --git a/xbmc/guilib/GUITextBox.cpp b/xbmc/guilib/GUITextBox.cpp
index ba68fad..e149418 100644
--- a/xbmc/guilib/GUITextBox.cpp
+++ b/xbmc/guilib/GUITextBox.cpp
@@ -133,7 +133,9 @@ void CGUITextBox::Process(unsigned int currentTime, CDirtyRegionList &dirtyregio
   // update our auto-scrolling as necessary
   if (m_autoScrollTime && m_lines.size() > m_itemsPerPage)
   {
-    if ((!m_autoScrollCondition || m_autoScrollCondition->Get()) && !g_application.IsInScreenSaver())
+    if ((!m_autoScrollCondition || m_autoScrollCondition->Get()) && !(g_application.IsInScreenSaver() &&
+         (g_application.GetScreenSaverId() == "screensaver.xbmc.builtin.black" ||
+          g_application.GetScreenSaverId() == "screensaver.xbmc.builtin.dim")))
     {
       if (m_lastRenderTime)
         m_autoScrollDelayTime += currentTime - m_lastRenderTime;

From a56767138ba6566ebdf52a76cfe1294e27d79795 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Tue, 16 Sep 2014 00:52:02 +0100
Subject: [PATCH 65/88] [PiSink] Initial dual audio support

---
 system/settings/rbp.xml                   |   5 -
 xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp | 219 ++++++++++++++++++++++++------
 xbmc/cores/AudioEngine/Sinks/AESinkPi.h   |   5 +
 xbmc/cores/omxplayer/OMXAudio.cpp         |  12 +-
 4 files changed, 186 insertions(+), 55 deletions(-)

diff --git a/system/settings/rbp.xml b/system/settings/rbp.xml
index e721d31..2996c29 100644
--- a/system/settings/rbp.xml
+++ b/system/settings/rbp.xml
@@ -80,11 +80,6 @@
     </category>
     <category id="audiooutput">
       <group id="1">
-        <setting id="audiooutput.dualaudio" type="boolean" label="37017" help="36542">
-          <level>2</level>
-          <default>false</default>
-          <control type="toggle" />
-        </setting>
         <setting id="audiooutput.processquality">
           <default>101</default> <!-- AE_QUALITY_GPU -->
         </setting>
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp
index 7af2078..89684e4 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp
@@ -43,7 +43,8 @@ CAEDeviceInfo CAESinkPi::m_info;
 CAESinkPi::CAESinkPi() :
     m_sinkbuffer_sec_per_byte(0),
     m_Initialized(false),
-    m_submitted(0)
+    m_submitted(0),
+    m_omx_output(NULL)
 {
 }
 
@@ -56,13 +57,26 @@ void CAESinkPi::SetAudioDest()
   OMX_ERRORTYPE omx_err   = OMX_ErrorNone;
   OMX_CONFIG_BRCMAUDIODESTINATIONTYPE audioDest;
   OMX_INIT_STRUCTURE(audioDest);
-  if (CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Analogue")
-    strncpy((char *)audioDest.sName, "local", strlen("local"));
-  else
-    strncpy((char *)audioDest.sName, "hdmi", strlen("hdmi"));
-  omx_err = m_omx_render.SetConfig(OMX_IndexConfigBrcmAudioDestination, &audioDest);
-  if (omx_err != OMX_ErrorNone)
-    CLog::Log(LOGERROR, "%s::%s - m_omx_render.SetConfig omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+  if ( m_omx_render.IsInitialized() )
+  {
+    if (CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Analogue")
+      strncpy((char *)audioDest.sName, "local", strlen("local"));
+    else
+      strncpy((char *)audioDest.sName, "hdmi", strlen("hdmi"));
+    omx_err = m_omx_render.SetConfig(OMX_IndexConfigBrcmAudioDestination, &audioDest);
+    if (omx_err != OMX_ErrorNone)
+      CLog::Log(LOGERROR, "%s::%s - m_omx_render.SetConfig omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+  }
+  if ( m_omx_render_slave.IsInitialized() )
+  {
+    if (CSettings::Get().GetString("audiooutput.audiodevice") != "PI:Analogue")
+      strncpy((char *)audioDest.sName, "local", strlen("local"));
+    else
+      strncpy((char *)audioDest.sName, "hdmi", strlen("hdmi"));
+    omx_err = m_omx_render_slave.SetConfig(OMX_IndexConfigBrcmAudioDestination, &audioDest);
+    if (omx_err != OMX_ErrorNone)
+      CLog::Log(LOGERROR, "%s::%s - m_omx_render_slave.SetConfig omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+  }
 }
 
 static void SetAudioProps(bool stream_channels, uint32_t channel_map)
@@ -176,8 +190,11 @@ bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device)
   m_initFormat = format;
 
   // analogue only supports stereo
-  if (CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Analogue")
+  if (CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Analogue" || CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Both")
+  {
     format.m_channelLayout = AE_CH_LAYOUT_2_0;
+    m_passthrough = false;
+  }
 
   // setup for a 50ms sink feed from SoftAE
   if (format.m_dataFormat != AE_FMT_FLOATP && format.m_dataFormat != AE_FMT_FLOAT &&
@@ -196,18 +213,29 @@ bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device)
   m_format = format;
   m_sinkbuffer_sec_per_byte = 1.0 / (double)(m_format.m_frameSize * m_format.m_sampleRate);
 
-  CLog::Log(LOGDEBUG, "%s:%s Format:%d Channels:%d Samplerate:%d framesize:%d bufsize:%d bytes/s=%.2f", CLASSNAME, __func__,
-                m_format.m_dataFormat, channels, m_format.m_sampleRate, m_format.m_frameSize, m_format.m_frameSize * m_format.m_frames, 1.0/m_sinkbuffer_sec_per_byte);
-
-  CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
+  CLog::Log(LOGDEBUG, "%s:%s Format:%d Channels:%d Samplerate:%d framesize:%d bufsize:%d bytes/s=%.2f dest=%s", CLASSNAME, __func__,
+                m_format.m_dataFormat, channels, m_format.m_sampleRate, m_format.m_frameSize, m_format.m_frameSize * m_format.m_frames, 1.0/m_sinkbuffer_sec_per_byte,
+                CSettings::Get().GetString("audiooutput.audiodevice").c_str());
 
   OMX_ERRORTYPE omx_err   = OMX_ErrorNone;
 
   if (!m_omx_render.Initialize("OMX.broadcom.audio_render", OMX_IndexParamAudioInit))
     CLog::Log(LOGERROR, "%s::%s - m_omx_render.Initialize omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
 
+  if (CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Both")
+  {
+    if (!m_omx_splitter.Initialize("OMX.broadcom.audio_splitter", OMX_IndexParamAudioInit))
+      CLog::Log(LOGERROR, "%s::%s - m_omx_splitter.Initialize omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+    if (!m_omx_render_slave.Initialize("OMX.broadcom.audio_render", OMX_IndexParamAudioInit))
+      CLog::Log(LOGERROR, "%s::%s - m_omx_render.Initialize omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+    m_omx_output = &m_omx_splitter;
+  }
+  else
+    m_omx_output = &m_omx_render;
+
+  SetAudioDest();
+
   OMX_INIT_STRUCTURE(m_pcm_input);
-  m_pcm_input.nPortIndex            = m_omx_render.GetInputPort();
   m_pcm_input.eNumData              = OMX_NumericalDataSigned;
   m_pcm_input.eEndian               = OMX_EndianLittle;
   m_pcm_input.bInterleaved          = OMX_TRUE;
@@ -222,37 +250,100 @@ bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device)
   m_pcm_input.nChannels             = channels;
   m_pcm_input.nSamplingRate         = m_format.m_sampleRate;
 
-  omx_err = m_omx_render.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input);
-  if (omx_err != OMX_ErrorNone)
-    CLog::Log(LOGERROR, "%s::%s - error m_omx_render SetParameter in omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+  if ( m_omx_splitter.IsInitialized() )
+  {
+    m_pcm_input.nPortIndex = m_omx_splitter.GetInputPort();
+    omx_err = m_omx_splitter.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input);
+    if (omx_err != OMX_ErrorNone)
+      CLog::Log(LOGERROR, "%s::%s - error m_omx_splitter SetParameter in omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+
+    m_pcm_input.nPortIndex = m_omx_splitter.GetOutputPort();
+    omx_err = m_omx_splitter.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input);
+    if(omx_err != OMX_ErrorNone)
+      CLog::Log(LOGERROR, "%s::%s - error m_omx_splitter SetParameter omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+
+    m_pcm_input.nPortIndex = m_omx_splitter.GetOutputPort() + 1;
+    omx_err = m_omx_splitter.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input);
+    if(omx_err != OMX_ErrorNone)
+      CLog::Log(LOGERROR, "%s::%s - error m_omx_splitter SetParameter omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+  }
 
-  m_omx_render.ResetEos();
+  if ( m_omx_render_slave.IsInitialized() )
+  {
+    m_pcm_input.nPortIndex = m_omx_render_slave.GetInputPort();
+    omx_err = m_omx_render_slave.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input);
+    if (omx_err != OMX_ErrorNone)
+      CLog::Log(LOGERROR, "%s::%s - error m_omx_render_slave SetParameter in omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+  }
 
-  SetAudioDest();
+  if ( m_omx_render.IsInitialized() )
+  {
+    m_pcm_input.nPortIndex = m_omx_render.GetInputPort();
+    omx_err = m_omx_render.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input);
+    if (omx_err != OMX_ErrorNone)
+      CLog::Log(LOGERROR, "%s::%s - error m_omx_render SetParameter in omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+  }
 
-  // set up the number/size of buffers for decoder input
-  OMX_PARAM_PORTDEFINITIONTYPE port_param;
-  OMX_INIT_STRUCTURE(port_param);
-  port_param.nPortIndex = m_omx_render.GetInputPort();
+  if ( m_omx_output->IsInitialized() )
+  {
+    // set up the number/size of buffers for decoder input
+    OMX_PARAM_PORTDEFINITIONTYPE port_param;
+    OMX_INIT_STRUCTURE(port_param);
+    port_param.nPortIndex = m_omx_output->GetInputPort();
 
-  omx_err = m_omx_render.GetParameter(OMX_IndexParamPortDefinition, &port_param);
-  if (omx_err != OMX_ErrorNone)
-    CLog::Log(LOGERROR, "%s:%s - error get OMX_IndexParamPortDefinition (input) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+    omx_err = m_omx_output->GetParameter(OMX_IndexParamPortDefinition, &port_param);
+    if (omx_err != OMX_ErrorNone)
+      CLog::Log(LOGERROR, "%s:%s - error get OMX_IndexParamPortDefinition (input) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
 
-  port_param.nBufferCountActual = std::max((unsigned int)port_param.nBufferCountMin, (unsigned int)NUM_OMX_BUFFERS);
-  port_param.nBufferSize = m_format.m_frameSize * m_format.m_frames;
+    port_param.nBufferCountActual = std::max((unsigned int)port_param.nBufferCountMin, (unsigned int)NUM_OMX_BUFFERS);
+    port_param.nBufferSize = ALIGN_UP(m_format.m_frameSize * m_format.m_frames, port_param.nBufferAlignment);
 
-  omx_err = m_omx_render.SetParameter(OMX_IndexParamPortDefinition, &port_param);
-  if (omx_err != OMX_ErrorNone)
-    CLog::Log(LOGERROR, "%s:%s - error set OMX_IndexParamPortDefinition (intput) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+    omx_err = m_omx_output->SetParameter(OMX_IndexParamPortDefinition, &port_param);
+    if (omx_err != OMX_ErrorNone)
+      CLog::Log(LOGERROR, "%s:%s - error set OMX_IndexParamPortDefinition (input) omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
 
-  omx_err = m_omx_render.AllocInputBuffers();
-  if (omx_err != OMX_ErrorNone)
-    CLog::Log(LOGERROR, "%s:%s - Error alloc buffers 0x%08x", CLASSNAME, __func__, omx_err);
+    omx_err = m_omx_output->AllocInputBuffers();
+    if (omx_err != OMX_ErrorNone)
+      CLog::Log(LOGERROR, "%s:%s - Error alloc buffers 0x%08x", CLASSNAME, __func__, omx_err);
+  }
 
-  omx_err = m_omx_render.SetStateForComponent(OMX_StateExecuting);
-  if (omx_err != OMX_ErrorNone)
-    CLog::Log(LOGERROR, "%s:%s - m_omx_render OMX_StateExecuting omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+  if ( m_omx_splitter.IsInitialized() )
+  {
+    m_omx_tunnel_splitter.Initialize(&m_omx_splitter, m_omx_splitter.GetOutputPort(), &m_omx_render, m_omx_render.GetInputPort());
+    omx_err = m_omx_tunnel_splitter.Establish();
+    if (omx_err != OMX_ErrorNone)
+    {
+      CLog::Log(LOGERROR, "COMXAudio::Initialize - Error m_omx_tunnel_splitter.Establish 0x%08x", omx_err);
+      return false;
+    }
+
+    m_omx_tunnel_splitter_slave.Initialize(&m_omx_splitter, m_omx_splitter.GetOutputPort() + 1, &m_omx_render_slave, m_omx_render_slave.GetInputPort());
+    omx_err = m_omx_tunnel_splitter_slave.Establish();
+    if (omx_err != OMX_ErrorNone)
+    {
+      CLog::Log(LOGERROR, "COMXAudio::Initialize - Error m_omx_tunnel_splitter_slave.Establish 0x%08x", omx_err);
+      return false;
+    }
+  }
+
+  if ( m_omx_splitter.IsInitialized() )
+  {
+    omx_err = m_omx_splitter.SetStateForComponent(OMX_StateExecuting);
+    if (omx_err != OMX_ErrorNone)
+      CLog::Log(LOGERROR, "%s:%s - m_omx_splitter OMX_StateExecuting omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+  }
+  if ( m_omx_render.IsInitialized() )
+  {
+    omx_err = m_omx_render.SetStateForComponent(OMX_StateExecuting);
+    if (omx_err != OMX_ErrorNone)
+      CLog::Log(LOGERROR, "%s:%s - m_omx_render OMX_StateExecuting omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+  }
+  if ( m_omx_render_slave.IsInitialized() )
+  {
+    omx_err = m_omx_render_slave.SetStateForComponent(OMX_StateExecuting);
+    if (omx_err != OMX_ErrorNone)
+      CLog::Log(LOGERROR, "%s:%s - m_omx_render_slave OMX_StateExecuting omx_err(0x%08x)", CLASSNAME, __func__, omx_err);
+  }
 
   m_Initialized = true;
   return true;
@@ -263,12 +354,32 @@ void CAESinkPi::Deinitialize()
 {
   CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__);
   SetAudioProps(false, 0);
-  if (m_Initialized)
-  {
+
+  if ( m_omx_render.IsInitialized() )
+    m_omx_render.IgnoreNextError(OMX_ErrorPortUnpopulated);
+  if ( m_omx_render_slave.IsInitialized() )
+    m_omx_render_slave.IgnoreNextError(OMX_ErrorPortUnpopulated);
+
+  if ( m_omx_tunnel_splitter.IsInitialized() )
+    m_omx_tunnel_splitter.Deestablish();
+  if ( m_omx_tunnel_splitter_slave.IsInitialized() )
+    m_omx_tunnel_splitter_slave.Deestablish();
+
+  if ( m_omx_splitter.IsInitialized() )
+    m_omx_splitter.FlushAll();
+  if ( m_omx_render.IsInitialized() )
     m_omx_render.FlushAll();
+  if ( m_omx_render_slave.IsInitialized() )
+    m_omx_render_slave.FlushAll();
+
+  if ( m_omx_splitter.IsInitialized() )
+    m_omx_splitter.Deinitialize();
+  if ( m_omx_render.IsInitialized() )
     m_omx_render.Deinitialize();
-    m_Initialized = false;
-  }
+  if ( m_omx_render_slave.IsInitialized() )
+    m_omx_render_slave.Deinitialize();
+
+  m_Initialized = false;
 }
 
 bool CAESinkPi::IsCompatible(const AEAudioFormat &format, const std::string &device)
@@ -314,7 +425,7 @@ double CAESinkPi::GetCacheTotal()
 
 unsigned int CAESinkPi::AddPackets(uint8_t **data, unsigned int frames, unsigned int offset)
 {
-  if (!m_Initialized || !frames)
+  if (!m_Initialized || !m_omx_output || !frames)
     return frames;
 
   OMX_ERRORTYPE omx_err   = OMX_ErrorNone;
@@ -332,7 +443,7 @@ unsigned int CAESinkPi::AddPackets(uint8_t **data, unsigned int frames, unsigned
   if (delay <= 0.0 && m_submitted)
     CLog::Log(LOGNOTICE, "%s:%s Underrun (delay:%.2f frames:%d)", CLASSNAME, __func__, delay, frames);
 
-  omx_buffer = m_omx_render.GetInputBuffer(1000);
+  omx_buffer = m_omx_output->GetInputBuffer(1000);
   if (omx_buffer == NULL)
   {
     CLog::Log(LOGERROR, "CAESinkPi::AddPackets timeout");
@@ -351,7 +462,7 @@ unsigned int CAESinkPi::AddPackets(uint8_t **data, unsigned int frames, unsigned
     for (int i=0; i < planes; i++)
       memcpy((uint8_t *)omx_buffer->pBuffer + i * planesize, data[i] + offset * pitch, planesize);
   }
-  omx_err = m_omx_render.EmptyThisBuffer(omx_buffer);
+  omx_err = m_omx_output->EmptyThisBuffer(omx_buffer);
   if (omx_err != OMX_ErrorNone)
     CLog::Log(LOGERROR, "%s:%s frames=%d err=%x", CLASSNAME, __func__, frames, omx_err);
   m_submitted++;
@@ -419,6 +530,26 @@ void CAESinkPi::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
   m_info.m_dataFormats.push_back(AE_FMT_S16NEP);
 
   list.push_back(m_info);
+
+  m_info.m_channels.Reset();
+  m_info.m_dataFormats.clear();
+  m_info.m_sampleRates.clear();
+
+  m_info.m_deviceType = AE_DEVTYPE_PCM;
+  m_info.m_deviceName = "Both";
+  m_info.m_displayName = "HDMI and Analogue";
+  m_info.m_displayNameExtra = "";
+  m_info.m_channels += AE_CH_FL;
+  m_info.m_channels += AE_CH_FR;
+  m_info.m_sampleRates.push_back(48000);
+  m_info.m_dataFormats.push_back(AE_FMT_FLOAT);
+  m_info.m_dataFormats.push_back(AE_FMT_S32LE);
+  m_info.m_dataFormats.push_back(AE_FMT_S16LE);
+  m_info.m_dataFormats.push_back(AE_FMT_FLOATP);
+  m_info.m_dataFormats.push_back(AE_FMT_S32NEP);
+  m_info.m_dataFormats.push_back(AE_FMT_S16NEP);
+
+  list.push_back(m_info);
 }
 
 #endif
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkPi.h b/xbmc/cores/AudioEngine/Sinks/AESinkPi.h
index 8ab1d08..5c57999 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkPi.h
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkPi.h
@@ -57,8 +57,13 @@ class CAESinkPi : public IAESink
   bool                 m_Initialized;
   uint32_t             m_submitted;
   OMX_AUDIO_PARAM_PCMMODETYPE m_pcm_input;
+  COMXCoreComponent   *m_omx_output;
+  COMXCoreComponent    m_omx_splitter;
   COMXCoreComponent    m_omx_render;
+  COMXCoreComponent    m_omx_render_slave;
   bool                 m_passthrough;
+  COMXCoreTunel        m_omx_tunnel_splitter;
+  COMXCoreTunel        m_omx_tunnel_splitter_slave;
 };
 
 #endif
diff --git a/xbmc/cores/omxplayer/OMXAudio.cpp b/xbmc/cores/omxplayer/OMXAudio.cpp
index 4f85dc7..e96713c 100644
--- a/xbmc/cores/omxplayer/OMXAudio.cpp
+++ b/xbmc/cores/omxplayer/OMXAudio.cpp
@@ -124,17 +124,17 @@ bool COMXAudio::PortSettingsChanged()
     if(!m_omx_mixer.Initialize("OMX.broadcom.audio_mixer", OMX_IndexParamAudioInit))
       return false;
   }
-  if(CSettings::Get().GetBool("audiooutput.dualaudio"))
+  if(CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Both")
   {
     if(!m_omx_splitter.Initialize("OMX.broadcom.audio_splitter", OMX_IndexParamAudioInit))
       return false;
   }
-  if (CSettings::Get().GetBool("audiooutput.dualaudio") || CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Analogue")
+  if (CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Both" || CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Analogue")
   {
     if(!m_omx_render_analog.Initialize("OMX.broadcom.audio_render", OMX_IndexParamAudioInit))
       return false;
   }
-  if (CSettings::Get().GetBool("audiooutput.dualaudio") || CSettings::Get().GetString("audiooutput.audiodevice") != "PI:Analogue")
+  if (CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Both" || CSettings::Get().GetString("audiooutput.audiodevice") == "PI:HDMI")
   {
     if(!m_omx_render_hdmi.Initialize("OMX.broadcom.audio_render", OMX_IndexParamAudioInit))
       return false;
@@ -252,7 +252,7 @@ bool COMXAudio::PortSettingsChanged()
   {
     // By default audio_render is the clock master, and if output samples don't fit the timestamps, it will speed up/slow down the clock.
     // This tends to be better for maintaining audio sync and avoiding audio glitches, but can affect video/display sync
-    if(CSettings::Get().GetBool("videoplayer.usedisplayasclock") || (CSettings::Get().GetBool("audiooutput.dualaudio") && CSettings::Get().GetString("audiooutput.audiodevice") != "PI:Analogue"))
+    if(CSettings::Get().GetBool("videoplayer.usedisplayasclock") || CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Both")
     {
       OMX_CONFIG_BOOLEANTYPE configBool;
       OMX_INIT_STRUCTURE(configBool);
@@ -278,7 +278,7 @@ bool COMXAudio::PortSettingsChanged()
   {
     // By default audio_render is the clock master, and if output samples don't fit the timestamps, it will speed up/slow down the clock.
     // This tends to be better for maintaining audio sync and avoiding audio glitches, but can affect video/display sync
-    if(CSettings::Get().GetBool("videoplayer.usedisplayasclock") || (CSettings::Get().GetBool("audiooutput.dualaudio") && CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Analogue"))
+    if(CSettings::Get().GetBool("videoplayer.usedisplayasclock"))
     {
       OMX_CONFIG_BOOLEANTYPE configBool;
       OMX_INIT_STRUCTURE(configBool);
@@ -601,7 +601,7 @@ bool COMXAudio::Initialize(AEAudioFormat format, OMXClock *clock, CDVDStreamInfo
     CAEChannelInfo stdLayout = (enum AEStdChLayout)CSettings::Get().GetInt("audiooutput.channels");
 
     // ignore layout setting for analogue
-    if (CSettings::Get().GetBool("audiooutput.dualaudio") || CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Analogue")
+    if (CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Both" || CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Analogue")
       stdLayout = AE_CH_LAYOUT_2_0;
 
     // force out layout to stereo if input is not multichannel - it gives the receiver a chance to upmix

From 4d78c4ddad578458e99e90eef4c746ef5567643d Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Thu, 18 Sep 2014 14:24:56 +0100
Subject: [PATCH 66/88] [omxplayer] Only enable audio clock master when A/V
 sync method is set to audio clock

---
 xbmc/cores/omxplayer/OMXAudio.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/xbmc/cores/omxplayer/OMXAudio.cpp b/xbmc/cores/omxplayer/OMXAudio.cpp
index e96713c..036552a 100644
--- a/xbmc/cores/omxplayer/OMXAudio.cpp
+++ b/xbmc/cores/omxplayer/OMXAudio.cpp
@@ -252,7 +252,8 @@ bool COMXAudio::PortSettingsChanged()
   {
     // By default audio_render is the clock master, and if output samples don't fit the timestamps, it will speed up/slow down the clock.
     // This tends to be better for maintaining audio sync and avoiding audio glitches, but can affect video/display sync
-    if(CSettings::Get().GetBool("videoplayer.usedisplayasclock") || CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Both")
+    if((CSettings::Get().GetBool("videoplayer.usedisplayasclock") && CSettings::Get().GetInt("videoplayer.synctype") == SYNC_DISCON) ||
+        CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Both")
     {
       OMX_CONFIG_BOOLEANTYPE configBool;
       OMX_INIT_STRUCTURE(configBool);
@@ -278,7 +279,7 @@ bool COMXAudio::PortSettingsChanged()
   {
     // By default audio_render is the clock master, and if output samples don't fit the timestamps, it will speed up/slow down the clock.
     // This tends to be better for maintaining audio sync and avoiding audio glitches, but can affect video/display sync
-    if(CSettings::Get().GetBool("videoplayer.usedisplayasclock"))
+    if(CSettings::Get().GetBool("videoplayer.usedisplayasclock") && CSettings::Get().GetInt("videoplayer.synctype") == SYNC_DISCON)
     {
       OMX_CONFIG_BOOLEANTYPE configBool;
       OMX_INIT_STRUCTURE(configBool);

From fa419f5d488a49d1a45f290213ee6204668482bd Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Mon, 18 Aug 2014 19:09:32 +0100
Subject: [PATCH 67/88] rbp: Use new dispmanx function for vsync callbacks

[rbp] Enable vsync handler all the time
---
 xbmc/linux/RBP.cpp                              | 96 ++++++++++++++-----------
 xbmc/linux/RBP.h                                |  7 +-
 xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp |  4 +-
 3 files changed, 61 insertions(+), 46 deletions(-)

diff --git a/xbmc/linux/RBP.cpp b/xbmc/linux/RBP.cpp
index 34e0108..8ce60cb 100644
--- a/xbmc/linux/RBP.cpp
+++ b/xbmc/linux/RBP.cpp
@@ -33,7 +33,7 @@ CRBP::CRBP()
   m_omx_initialized = false;
   m_DllBcmHost      = new DllBcmHost();
   m_OMX             = new COMXCore();
-  m_element = 0;
+  m_display = DISPMANX_NO_HANDLE;
 }
 
 CRBP::~CRBP()
@@ -55,8 +55,7 @@ bool CRBP::Initialize()
 
   m_DllBcmHost->bcm_host_init();
 
-  uint32_t                    vc_image_ptr;
-  m_resource = vc_dispmanx_resource_create( VC_IMAGE_RGB565, 1, 1, &vc_image_ptr );
+  //OpenDisplay(0 /*screen*/);
 
   m_omx_initialized = m_OMX->Initialize();
   if(!m_omx_initialized)
@@ -112,13 +111,55 @@ void CRBP::LogFirmwareVerison()
   CLog::Log(LOGNOTICE, "Config:\n%s", response);
 }
 
+static void vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *arg)
+{
+  CEvent *sync = (CEvent *)arg;
+  sync->Set();
+}
+
+void CRBP::WaitVsync()
+{
+  CSingleLock lock (m_critSection);
+  if (m_display == DISPMANX_NO_HANDLE)
+  {
+    // don't busy spin
+    Sleep(10);
+    return;
+  }
+  m_vsync.Reset();
+  m_vsync.Wait();
+}
+
+DISPMANX_DISPLAY_HANDLE_T CRBP::OpenDisplay(uint32_t device)
+{
+  CSingleLock lock (m_critSection);
+  if (m_display == DISPMANX_NO_HANDLE)
+    m_display = vc_dispmanx_display_open( 0 /*screen*/ );
+
+  if (m_display != DISPMANX_NO_HANDLE)
+  {
+    int s = vc_dispmanx_vsync_callback(m_display, vsync_callback, (void *)&m_vsync);
+    assert(s == 0);
+  }
+  return m_display;
+}
+
+void CRBP::CloseDisplay(DISPMANX_DISPLAY_HANDLE_T display)
+{
+  CSingleLock lock (m_critSection);
+  assert(display == m_display);
+
+  int s = vc_dispmanx_vsync_callback(m_display, NULL, NULL);
+  assert(s == 0);
+
+  vc_dispmanx_display_close(m_display);
+  m_display = DISPMANX_NO_HANDLE;
+}
+
 void CRBP::GetDisplaySize(int &width, int &height)
 {
-  DISPMANX_DISPLAY_HANDLE_T display;
   DISPMANX_MODEINFO_T info;
-
-  display = vc_dispmanx_display_open( 0 /*screen*/ );
-  if (vc_dispmanx_display_get_info(display, &info) == 0)
+  if (vc_dispmanx_display_get_info(m_display, &info) == 0)
   {
     width = info.width;
     height = info.height;
@@ -128,12 +169,10 @@ void CRBP::GetDisplaySize(int &width, int &height)
     width = 0;
     height = 0;
   }
-  vc_dispmanx_display_close(display );
 }
 
 unsigned char *CRBP::CaptureDisplay(int width, int height, int *pstride, bool swap_red_blue, bool video_only)
 {
-  DISPMANX_DISPLAY_HANDLE_T display;
   DISPMANX_RESOURCE_HANDLE_T resource;
   VC_RECT_T rect;
   unsigned char *image = NULL;
@@ -148,7 +187,6 @@ unsigned char *CRBP::CaptureDisplay(int width, int height, int *pstride, bool sw
   if (!pstride)
     flags |= DISPMANX_SNAPSHOT_PACK;
 
-  display = vc_dispmanx_display_open( 0 /*screen*/ );
   stride = ((width + 15) & ~15) * 4;
   image = new unsigned char [height * stride];
 
@@ -156,56 +194,30 @@ unsigned char *CRBP::CaptureDisplay(int width, int height, int *pstride, bool sw
   {
     resource = vc_dispmanx_resource_create( VC_IMAGE_RGBA32, width, height, &vc_image_ptr );
 
-    vc_dispmanx_snapshot(display, resource, (DISPMANX_TRANSFORM_T)flags);
+    assert(m_display != DISPMANX_NO_HANDLE);
+    vc_dispmanx_snapshot(m_display, resource, (DISPMANX_TRANSFORM_T)flags);
 
     vc_dispmanx_rect_set(&rect, 0, 0, width, height);
     vc_dispmanx_resource_read_data(resource, &rect, image, stride);
     vc_dispmanx_resource_delete( resource );
-    vc_dispmanx_display_close(display );
   }
   if (pstride)
     *pstride = stride;
   return image;
 }
 
-void CRBP::WaitVsync(void)
-{
-  DISPMANX_DISPLAY_HANDLE_T display = vc_dispmanx_display_open( 0 /*screen*/ );
-  DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0);
-
-  VC_DISPMANX_ALPHA_T alpha = { (DISPMANX_FLAGS_ALPHA_T)(DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS), 120, /*alpha 0->255*/ 0 };
-  VC_RECT_T       src_rect;
-  VC_RECT_T       dst_rect;
-  vc_dispmanx_rect_set( &src_rect, 0, 0, 1 << 16, 1 << 16 );
-  vc_dispmanx_rect_set( &dst_rect, 0, 0, 1, 1 );
-
-  if (m_element)
-    vc_dispmanx_element_remove( update, m_element );
-
-  m_element = vc_dispmanx_element_add( update,
-                                            display,
-                                            2000,               // layer
-                                            &dst_rect,
-                                            m_resource,
-                                            &src_rect,
-                                            DISPMANX_PROTECTION_NONE,
-                                            &alpha,
-                                            NULL,             // clamp
-                                            (DISPMANX_TRANSFORM_T)0 );
-
-  vc_dispmanx_update_submit_sync(update);
-  vc_dispmanx_display_close( display );
-}
-
-
 void CRBP::Deinitialize()
 {
+  CSingleLock lock (m_critSection);
   if (m_omx_image_init)
     g_OMXImage.Deinitialize();
 
   if(m_omx_initialized)
     m_OMX->Deinitialize();
 
+  if (m_display)
+    CloseDisplay(m_display);
+
   m_DllBcmHost->bcm_host_deinit();
 
   if(m_initialized)
diff --git a/xbmc/linux/RBP.h b/xbmc/linux/RBP.h
index f947acc..029d69c 100644
--- a/xbmc/linux/RBP.h
+++ b/xbmc/linux/RBP.h
@@ -38,6 +38,7 @@
 #include "DllBCM.h"
 #include "OMXCore.h"
 #include "threads/CriticalSection.h"
+#include "threads/Event.h"
 
 class CRBP
 {
@@ -53,6 +54,8 @@ class CRBP
   bool GetCodecMpg2() { return m_codec_mpg2_enabled; }
   bool GetCodecWvc1() { return m_codec_wvc1_enabled; }
   void GetDisplaySize(int &width, int &height);
+  DISPMANX_DISPLAY_HANDLE_T OpenDisplay(uint32_t device);
+  void CloseDisplay(DISPMANX_DISPLAY_HANDLE_T display);
   int GetGUIResolutionLimit() { return m_gui_resolution_limit; }
   // stride can be null for packed output
   unsigned char *CaptureDisplay(int width, int height, int *stride, bool swap_red_blue, bool video_only = true);
@@ -73,10 +76,10 @@ class CRBP
   bool       m_codec_mpg2_enabled;
   bool       m_codec_wvc1_enabled;
   COMXCore   *m_OMX;
-  DISPMANX_RESOURCE_HANDLE_T m_resource;
-  DISPMANX_ELEMENT_HANDLE_T m_element;
   class DllLibOMXCore;
   CCriticalSection m_critSection;
+  DISPMANX_DISPLAY_HANDLE_T m_display;
+  CEvent     m_vsync;
 };
 
 extern CRBP g_RBP;
diff --git a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
index 1529045..b6bf1fc 100644
--- a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
+++ b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
@@ -306,7 +306,7 @@ bool CEGLNativeTypeRaspberryPI::SetNativeResolution(const RESOLUTION_INFO &res)
     m_desktopRes = res;
   }
 
-  m_dispman_display = m_DllBcmHost->vc_dispmanx_display_open(0);
+  m_dispman_display = g_RBP.OpenDisplay(0);
 
   m_width   = res.iWidth;
   m_height  = res.iHeight;
@@ -571,7 +571,7 @@ void CEGLNativeTypeRaspberryPI::DestroyDispmaxWindow()
 
   if (m_dispman_display != DISPMANX_NO_HANDLE)
   {
-    m_DllBcmHost->vc_dispmanx_display_close(m_dispman_display);
+    g_RBP.CloseDisplay(m_dispman_display);
     m_dispman_display = DISPMANX_NO_HANDLE;
   }
   DLOG("CEGLNativeTypeRaspberryPI::DestroyDispmaxWindow\n");

From 38716528fd3836091adaf141c8b30cd455ad3ac2 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Thu, 18 Sep 2014 23:14:31 +0100
Subject: [PATCH 68/88] Revert "rbp: Use new dispmanx function for vsync
 callbacks"

This reverts commit e56163da23b6a844e7eee076e4583bce606f788b.
---
 xbmc/linux/RBP.cpp                              | 96 +++++++++++--------------
 xbmc/linux/RBP.h                                |  7 +-
 xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp |  4 +-
 3 files changed, 46 insertions(+), 61 deletions(-)

diff --git a/xbmc/linux/RBP.cpp b/xbmc/linux/RBP.cpp
index 8ce60cb..34e0108 100644
--- a/xbmc/linux/RBP.cpp
+++ b/xbmc/linux/RBP.cpp
@@ -33,7 +33,7 @@ CRBP::CRBP()
   m_omx_initialized = false;
   m_DllBcmHost      = new DllBcmHost();
   m_OMX             = new COMXCore();
-  m_display = DISPMANX_NO_HANDLE;
+  m_element = 0;
 }
 
 CRBP::~CRBP()
@@ -55,7 +55,8 @@ bool CRBP::Initialize()
 
   m_DllBcmHost->bcm_host_init();
 
-  //OpenDisplay(0 /*screen*/);
+  uint32_t                    vc_image_ptr;
+  m_resource = vc_dispmanx_resource_create( VC_IMAGE_RGB565, 1, 1, &vc_image_ptr );
 
   m_omx_initialized = m_OMX->Initialize();
   if(!m_omx_initialized)
@@ -111,55 +112,13 @@ void CRBP::LogFirmwareVerison()
   CLog::Log(LOGNOTICE, "Config:\n%s", response);
 }
 
-static void vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *arg)
-{
-  CEvent *sync = (CEvent *)arg;
-  sync->Set();
-}
-
-void CRBP::WaitVsync()
-{
-  CSingleLock lock (m_critSection);
-  if (m_display == DISPMANX_NO_HANDLE)
-  {
-    // don't busy spin
-    Sleep(10);
-    return;
-  }
-  m_vsync.Reset();
-  m_vsync.Wait();
-}
-
-DISPMANX_DISPLAY_HANDLE_T CRBP::OpenDisplay(uint32_t device)
-{
-  CSingleLock lock (m_critSection);
-  if (m_display == DISPMANX_NO_HANDLE)
-    m_display = vc_dispmanx_display_open( 0 /*screen*/ );
-
-  if (m_display != DISPMANX_NO_HANDLE)
-  {
-    int s = vc_dispmanx_vsync_callback(m_display, vsync_callback, (void *)&m_vsync);
-    assert(s == 0);
-  }
-  return m_display;
-}
-
-void CRBP::CloseDisplay(DISPMANX_DISPLAY_HANDLE_T display)
-{
-  CSingleLock lock (m_critSection);
-  assert(display == m_display);
-
-  int s = vc_dispmanx_vsync_callback(m_display, NULL, NULL);
-  assert(s == 0);
-
-  vc_dispmanx_display_close(m_display);
-  m_display = DISPMANX_NO_HANDLE;
-}
-
 void CRBP::GetDisplaySize(int &width, int &height)
 {
+  DISPMANX_DISPLAY_HANDLE_T display;
   DISPMANX_MODEINFO_T info;
-  if (vc_dispmanx_display_get_info(m_display, &info) == 0)
+
+  display = vc_dispmanx_display_open( 0 /*screen*/ );
+  if (vc_dispmanx_display_get_info(display, &info) == 0)
   {
     width = info.width;
     height = info.height;
@@ -169,10 +128,12 @@ void CRBP::GetDisplaySize(int &width, int &height)
     width = 0;
     height = 0;
   }
+  vc_dispmanx_display_close(display );
 }
 
 unsigned char *CRBP::CaptureDisplay(int width, int height, int *pstride, bool swap_red_blue, bool video_only)
 {
+  DISPMANX_DISPLAY_HANDLE_T display;
   DISPMANX_RESOURCE_HANDLE_T resource;
   VC_RECT_T rect;
   unsigned char *image = NULL;
@@ -187,6 +148,7 @@ unsigned char *CRBP::CaptureDisplay(int width, int height, int *pstride, bool sw
   if (!pstride)
     flags |= DISPMANX_SNAPSHOT_PACK;
 
+  display = vc_dispmanx_display_open( 0 /*screen*/ );
   stride = ((width + 15) & ~15) * 4;
   image = new unsigned char [height * stride];
 
@@ -194,30 +156,56 @@ unsigned char *CRBP::CaptureDisplay(int width, int height, int *pstride, bool sw
   {
     resource = vc_dispmanx_resource_create( VC_IMAGE_RGBA32, width, height, &vc_image_ptr );
 
-    assert(m_display != DISPMANX_NO_HANDLE);
-    vc_dispmanx_snapshot(m_display, resource, (DISPMANX_TRANSFORM_T)flags);
+    vc_dispmanx_snapshot(display, resource, (DISPMANX_TRANSFORM_T)flags);
 
     vc_dispmanx_rect_set(&rect, 0, 0, width, height);
     vc_dispmanx_resource_read_data(resource, &rect, image, stride);
     vc_dispmanx_resource_delete( resource );
+    vc_dispmanx_display_close(display );
   }
   if (pstride)
     *pstride = stride;
   return image;
 }
 
+void CRBP::WaitVsync(void)
+{
+  DISPMANX_DISPLAY_HANDLE_T display = vc_dispmanx_display_open( 0 /*screen*/ );
+  DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0);
+
+  VC_DISPMANX_ALPHA_T alpha = { (DISPMANX_FLAGS_ALPHA_T)(DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS), 120, /*alpha 0->255*/ 0 };
+  VC_RECT_T       src_rect;
+  VC_RECT_T       dst_rect;
+  vc_dispmanx_rect_set( &src_rect, 0, 0, 1 << 16, 1 << 16 );
+  vc_dispmanx_rect_set( &dst_rect, 0, 0, 1, 1 );
+
+  if (m_element)
+    vc_dispmanx_element_remove( update, m_element );
+
+  m_element = vc_dispmanx_element_add( update,
+                                            display,
+                                            2000,               // layer
+                                            &dst_rect,
+                                            m_resource,
+                                            &src_rect,
+                                            DISPMANX_PROTECTION_NONE,
+                                            &alpha,
+                                            NULL,             // clamp
+                                            (DISPMANX_TRANSFORM_T)0 );
+
+  vc_dispmanx_update_submit_sync(update);
+  vc_dispmanx_display_close( display );
+}
+
+
 void CRBP::Deinitialize()
 {
-  CSingleLock lock (m_critSection);
   if (m_omx_image_init)
     g_OMXImage.Deinitialize();
 
   if(m_omx_initialized)
     m_OMX->Deinitialize();
 
-  if (m_display)
-    CloseDisplay(m_display);
-
   m_DllBcmHost->bcm_host_deinit();
 
   if(m_initialized)
diff --git a/xbmc/linux/RBP.h b/xbmc/linux/RBP.h
index 029d69c..f947acc 100644
--- a/xbmc/linux/RBP.h
+++ b/xbmc/linux/RBP.h
@@ -38,7 +38,6 @@
 #include "DllBCM.h"
 #include "OMXCore.h"
 #include "threads/CriticalSection.h"
-#include "threads/Event.h"
 
 class CRBP
 {
@@ -54,8 +53,6 @@ class CRBP
   bool GetCodecMpg2() { return m_codec_mpg2_enabled; }
   bool GetCodecWvc1() { return m_codec_wvc1_enabled; }
   void GetDisplaySize(int &width, int &height);
-  DISPMANX_DISPLAY_HANDLE_T OpenDisplay(uint32_t device);
-  void CloseDisplay(DISPMANX_DISPLAY_HANDLE_T display);
   int GetGUIResolutionLimit() { return m_gui_resolution_limit; }
   // stride can be null for packed output
   unsigned char *CaptureDisplay(int width, int height, int *stride, bool swap_red_blue, bool video_only = true);
@@ -76,10 +73,10 @@ class CRBP
   bool       m_codec_mpg2_enabled;
   bool       m_codec_wvc1_enabled;
   COMXCore   *m_OMX;
+  DISPMANX_RESOURCE_HANDLE_T m_resource;
+  DISPMANX_ELEMENT_HANDLE_T m_element;
   class DllLibOMXCore;
   CCriticalSection m_critSection;
-  DISPMANX_DISPLAY_HANDLE_T m_display;
-  CEvent     m_vsync;
 };
 
 extern CRBP g_RBP;
diff --git a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
index b6bf1fc..1529045 100644
--- a/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
+++ b/xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
@@ -306,7 +306,7 @@ bool CEGLNativeTypeRaspberryPI::SetNativeResolution(const RESOLUTION_INFO &res)
     m_desktopRes = res;
   }
 
-  m_dispman_display = g_RBP.OpenDisplay(0);
+  m_dispman_display = m_DllBcmHost->vc_dispmanx_display_open(0);
 
   m_width   = res.iWidth;
   m_height  = res.iHeight;
@@ -571,7 +571,7 @@ void CEGLNativeTypeRaspberryPI::DestroyDispmaxWindow()
 
   if (m_dispman_display != DISPMANX_NO_HANDLE)
   {
-    g_RBP.CloseDisplay(m_dispman_display);
+    m_DllBcmHost->vc_dispmanx_display_close(m_dispman_display);
     m_dispman_display = DISPMANX_NO_HANDLE;
   }
   DLOG("CEGLNativeTypeRaspberryPI::DestroyDispmaxWindow\n");

From c82c26a3a043883ba32b20473ef2a424bedb18b2 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Fri, 19 Sep 2014 11:54:49 +0100
Subject: [PATCH 69/88] [dvdplayer/rbp] Add pi specific option to maintain
 vsync with pll adjustment

New A/V sync option in settings/video/playback to do "Adjust PLL".
This uses video clock (so perfect video syncing) but avoids having to resample
or drop/dupe audio packets which is normally required.

Needed updated firmware
---
 language/English/strings.po             |  5 +++++
 system/settings/rbp.xml                 | 20 ++++++++++++++++++
 xbmc/cores/AudioEngine/Utils/AEUtil.h   |  3 ++-
 xbmc/cores/dvdplayer/DVDPlayerAudio.cpp | 36 +++++++++++++++++++++++++++++++--
 xbmc/cores/dvdplayer/DVDPlayerAudio.h   |  1 +
 xbmc/linux/RBP.cpp                      | 13 ++++++++++++
 xbmc/linux/RBP.h                        |  1 +
 7 files changed, 76 insertions(+), 3 deletions(-)

diff --git a/language/English/strings.po b/language/English/strings.po
index 996f682..7a27549 100755
--- a/language/English/strings.po
+++ b/language/English/strings.po
@@ -16004,3 +16004,8 @@ msgstr ""
 msgctxt "#38005"
 msgid "24 fps"
 msgstr ""
+
+#: system/settings/settings.xml
+msgctxt "#38006"
+msgid "Adjust PLL"
+msgstr ""
diff --git a/system/settings/rbp.xml b/system/settings/rbp.xml
index 2996c29..79853a8 100644
--- a/system/settings/rbp.xml
+++ b/system/settings/rbp.xml
@@ -11,6 +11,26 @@
   </section>
 
   <section id="videos">
+    <category id="videoplayer" label="14086" help="36151">
+      <group id="2">
+        <setting id="videoplayer.synctype" type="integer" parent="videoplayer.usedisplayasclock" label="13500" help="36167">
+          <level>2</level>
+          <default>2</default> <!-- SYNC_RESAMPLE -->
+          <constraints>
+            <options>
+              <option label="13501">0</option> <!-- SYNC_DISCON -->
+              <option label="13502">1</option> <!-- SYNC_SKIPDUP -->
+              <option label="13503">2</option> <!-- SYNC_RESAMPLE -->
+              <option label="38006">3</option> <!-- SYNC_PLLADJUST -->
+            </options>
+          </constraints>
+          <dependencies>
+            <dependency type="enable" setting="videoplayer.usedisplayasclock" operator="is">true</dependency>
+          </dependencies>
+          <control type="spinner" format="string" />
+        </setting>
+      </group>
+    </category>
     <category id="videoacceleration">
       <group id="1">
         <visible>false</visible>
diff --git a/xbmc/cores/AudioEngine/Utils/AEUtil.h b/xbmc/cores/AudioEngine/Utils/AEUtil.h
index 782a9ba..3e0390c 100644
--- a/xbmc/cores/AudioEngine/Utils/AEUtil.h
+++ b/xbmc/cores/AudioEngine/Utils/AEUtil.h
@@ -57,7 +57,8 @@ enum AVSync
 {
   SYNC_DISCON   = 0,
   SYNC_SKIPDUP,
-  SYNC_RESAMPLE
+  SYNC_RESAMPLE,
+  SYNC_PLLADJUST
 };
 
 struct AEDelayStatus
diff --git a/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp b/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp
index 98da461..bb2c375 100644
--- a/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp
+++ b/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp
@@ -112,6 +112,7 @@ CDVDPlayerAudio::CDVDPlayerAudio(CDVDClock* pClock, CDVDMessageQueue& parent)
   m_started = false;
   m_silence = false;
   m_resampleratio = 1.0;
+  m_plladjust = 1.0f;
   m_synctype = SYNC_DISCON;
   m_setsynctype = SYNC_DISCON;
   m_prevsynctype = -1;
@@ -472,6 +473,8 @@ void CDVDPlayerAudio::UpdatePlayerInfo()
   //if the resample ratio is 0.5, then we're playing twice as fast
   if (m_synctype == SYNC_RESAMPLE)
     s << ", rr:" << fixed << setprecision(5) << 1.0 / m_resampleratio;
+  if (m_synctype == SYNC_PLLADJUST)
+    s << ", pll:" << fixed << setprecision(5) << 1.0 / m_plladjust;
 
   s << ", att:" << fixed << setprecision(1) << log(GetCurrentAttenuation()) * 20.0f << " dB";
 
@@ -622,8 +625,8 @@ void CDVDPlayerAudio::SetSyncType(bool passthrough)
 
   if (m_synctype != m_prevsynctype)
   {
-    const char *synctypes[] = {"clock feedback", "skip/duplicate", "resample", "invalid"};
-    int synctype = (m_synctype >= 0 && m_synctype <= 2) ? m_synctype : 3;
+    const char *synctypes[] = {"clock feedback", "skip/duplicate", "resample", "pll adjust", "invalid"};
+    int synctype = (m_synctype >= 0 && m_synctype <= 3) ? m_synctype : 4;
     CLog::Log(LOGDEBUG, "CDVDPlayerAudio:: synctype set to %i: %s", m_synctype, synctypes[synctype]);
     m_prevsynctype = m_synctype;
   }
@@ -697,6 +700,31 @@ void CDVDPlayerAudio::HandleSyncError(double duration)
       }
       m_resampleratio = 1.0 / m_pClock->GetClockSpeed() + proportional + m_integral;
     }
+    else if (m_synctype == SYNC_PLLADJUST)
+    {
+#if defined(TARGET_RASPBERRY_PI)
+      //reset the integral on big errors, failsafe
+      if (fabs(m_error) > DVD_TIME_BASE)
+        m_integral = 0;
+      else if (fabs(m_error) > DVD_MSEC_TO_TIME(5))
+        m_integral += m_error / DVD_TIME_BASE / INTEGRAL;
+
+      double proportional = 0.0;
+
+      //on big errors use more proportional
+      if (fabs(m_error / DVD_TIME_BASE) > 0.0)
+      {
+        double proportionaldiv = PROPORTIONAL * (PROPREF / fabs(m_error / DVD_TIME_BASE));
+        if (proportionaldiv < PROPDIVMIN) proportionaldiv = PROPDIVMIN;
+        else if (proportionaldiv > PROPDIVMAX) proportionaldiv = PROPDIVMAX;
+
+        proportional = m_error / DVD_TIME_BASE / proportionaldiv;
+      }
+      m_plladjust = 1.0 / m_pClock->GetClockSpeed() + proportional + m_integral;
+      double new_adjust = g_RBP.AdjustHDMIClock(m_plladjust);
+      CLog::Log(LOGDEBUG, "CDVDPlayerAudio::%s pll:%.4f (%.4f) proportional:%.4f integral:%.4f", __FUNCTION__, m_plladjust, new_adjust, proportional, m_integral);
+#endif
+    }
   }
 }
 
@@ -735,6 +763,10 @@ bool CDVDPlayerAudio::OutputPacket(DVDAudioFrame &audioframe)
     m_dvdAudio.SetResampleRatio(m_resampleratio);
     m_dvdAudio.AddPackets(audioframe);
   }
+  else if (m_synctype == SYNC_PLLADJUST)
+  {
+    m_dvdAudio.AddPackets(audioframe);
+  }
 
   return true;
 }
diff --git a/xbmc/cores/dvdplayer/DVDPlayerAudio.h b/xbmc/cores/dvdplayer/DVDPlayerAudio.h
index e8b2ab6..02c64a0 100644
--- a/xbmc/cores/dvdplayer/DVDPlayerAudio.h
+++ b/xbmc/cores/dvdplayer/DVDPlayerAudio.h
@@ -232,6 +232,7 @@ class CDVDPlayerAudio : public CThread, public IDVDStreamPlayerAudio
   bool   m_prevskipped;
   double m_maxspeedadjust;
   double m_resampleratio; //resample ratio when using SYNC_RESAMPLE, used for the codec info
+  double m_plladjust;    // for display using SYNC_PLLADJUST
 
   struct SInfo
   {
diff --git a/xbmc/linux/RBP.cpp b/xbmc/linux/RBP.cpp
index 34e0108..3cd8e18 100644
--- a/xbmc/linux/RBP.cpp
+++ b/xbmc/linux/RBP.cpp
@@ -230,4 +230,17 @@ void CRBP::ResumeVideoOutput()
   CLog::Log(LOGDEBUG, "Raspberry PI resuming video output\n");
 }
 
+double CRBP::AdjustHDMIClock(double adjust)
+{
+  char response[80];
+  vc_gencmd(response, sizeof response, "hdmi_adjust_clock %f", adjust);
+  float new_adjust = 1.0f;
+  char *p = strchr(response, '=');
+  if (p)
+    new_adjust = atof(p+1);
+  CLog::Log(LOGDEBUG, "CRBP::%s(%.4f) = %.4f", __func__, adjust, new_adjust);
+  return new_adjust;
+}
+
+
 #endif
diff --git a/xbmc/linux/RBP.h b/xbmc/linux/RBP.h
index f947acc..606c24f 100644
--- a/xbmc/linux/RBP.h
+++ b/xbmc/linux/RBP.h
@@ -58,6 +58,7 @@ class CRBP
   unsigned char *CaptureDisplay(int width, int height, int *stride, bool swap_red_blue, bool video_only = true);
   DllOMX *GetDllOMX() { return m_OMX ? m_OMX->GetDll() : NULL; }
   void WaitVsync();
+  double AdjustHDMIClock(double adjust);
 
   void SuspendVideoOutput();
   void ResumeVideoOutput();

From 4085c67b535627e1723ca8d7f82f3cdeaa67f54e Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sun, 21 Sep 2014 18:31:31 +0100
Subject: [PATCH 70/88] hack: revert squash: don't update originaldts when
 marked as invalid

---
 xbmc/cores/dvdplayer/DVDPlayer.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/xbmc/cores/dvdplayer/DVDPlayer.cpp b/xbmc/cores/dvdplayer/DVDPlayer.cpp
index 713739b..b88adb4 100644
--- a/xbmc/cores/dvdplayer/DVDPlayer.cpp
+++ b/xbmc/cores/dvdplayer/DVDPlayer.cpp
@@ -2106,6 +2106,7 @@ void CDVDPlayer::CheckContinuity(CCurrentStream& current, DemuxPacket* pPacket)
       // not sure yet - flags the packets as unknown until we get confirmation on another audio/video packet
       pPacket->dts = DVD_NOPTS_VALUE;
       pPacket->pts = DVD_NOPTS_VALUE;
+      current.originaldts = pPacket->dts;
     }
   }
   else

From 5a175022f112ed4f1d66598a3cc36831f0793b8e Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sat, 27 Sep 2014 15:27:04 +0100
Subject: [PATCH 71/88] [omxplayer] Don't sync up to passthrough audio packets
 - let GPU handle it

---
 xbmc/cores/omxplayer/OMXAudio.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/xbmc/cores/omxplayer/OMXAudio.cpp b/xbmc/cores/omxplayer/OMXAudio.cpp
index 036552a..ce35b07 100644
--- a/xbmc/cores/omxplayer/OMXAudio.cpp
+++ b/xbmc/cores/omxplayer/OMXAudio.cpp
@@ -1119,6 +1119,7 @@ unsigned int COMXAudio::AddPackets(const void* data, unsigned int len, double dt
     return len;
   }
 
+#if 0
   if(m_eEncoding == OMX_AUDIO_CodingDTS && m_LostSync && (m_Passthrough || m_HWDecode))
   {
     int skip = SyncDTS((uint8_t *)data, len);
@@ -1132,6 +1133,7 @@ unsigned int COMXAudio::AddPackets(const void* data, unsigned int len, double dt
     if(skip > 0)
       return len;
   }
+#endif
 
   unsigned pitch = (m_Passthrough || m_HWDecode) ? 1:(m_BitsPerSample >> 3) * m_InputChannels;
   unsigned int demuxer_samples = len / pitch;

From a266d4d3f5b8b20bd6ad2f18cadc6460b63edb74 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sat, 27 Sep 2014 15:32:37 +0100
Subject: [PATCH 72/88] [dvdplayer] exerimental: don't raise priority of audio
 thread

---
 xbmc/cores/dvdplayer/DVDPlayer.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/xbmc/cores/dvdplayer/DVDPlayer.cpp b/xbmc/cores/dvdplayer/DVDPlayer.cpp
index b88adb4..31dda29 100644
--- a/xbmc/cores/dvdplayer/DVDPlayer.cpp
+++ b/xbmc/cores/dvdplayer/DVDPlayer.cpp
@@ -3320,7 +3320,11 @@ bool CDVDPlayer::OpenAudioStream(CDVDStreamInfo& hint, bool reset)
   m_dvdPlayerAudio->SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1);
 
   /* audio normally won't consume full cpu, so let it have prio */
+#ifdef TARGET_RASPBERRY_PI
+  m_dvdPlayerAudio->SetPriority(GetPriority());
+#else
   m_dvdPlayerAudio->SetPriority(GetPriority()+1);
+#endif
   return true;
 }
 

From 8bf6e87fc6a72e35fbae5e0f076c67bf3be954e5 Mon Sep 17 00:00:00 2001
From: da-anda <da-anda@xbmc.org>
Date: Sun, 17 Aug 2014 21:09:59 +0200
Subject: [PATCH 73/88] handle stereoscopic mode of videos in mixed playlists

---
 language/English/strings.po          |  2 +-
 xbmc/guilib/StereoscopicsManager.cpp | 76 +++++++++++++++++++++++-------------
 2 files changed, 49 insertions(+), 29 deletions(-)

diff --git a/language/English/strings.po b/language/English/strings.po
index 7a27549..dd46ea5 100755
--- a/language/English/strings.po
+++ b/language/English/strings.po
@@ -15628,7 +15628,7 @@ msgstr ""
 #. Description of setting "Videos -> Playback -> Disable stereoscopic mode when playback is stopped" with label #36526
 #: system/settings/settings.xml
 msgctxt "#36538"
-msgid "[Enabled] Switch GUI (and some TVs) back to 2D mode when playback ended. [Disabled] GUI and TV will stay in stereoscopic 3D mode. For video playlists the stereoscopic 3D mode won't change between videos, not even for non stereoscopic ones."
+msgid "[Enabled] Switch GUI (and some TVs) back to 2D mode when playback ended - also between videos in a playlist if necessary. [Disabled] GUI and TV will stay in stereoscopic 3D mode. For video playlists the stereoscopic 3D mode won't change between videos, not even for non stereoscopic ones."
 msgstr ""
 
 #. Description of setting "System -> Video output -> Stereoscopic mode (current)" with label #36500
diff --git a/xbmc/guilib/StereoscopicsManager.cpp b/xbmc/guilib/StereoscopicsManager.cpp
index 35ba597..b013942 100644
--- a/xbmc/guilib/StereoscopicsManager.cpp
+++ b/xbmc/guilib/StereoscopicsManager.cpp
@@ -123,13 +123,23 @@ RENDER_STEREO_MODE CStereoscopicsManager::GetStereoMode(void)
 void CStereoscopicsManager::SetStereoMode(const RENDER_STEREO_MODE &mode)
 {
   RENDER_STEREO_MODE currentMode = GetStereoMode();
-  if (mode != currentMode && mode >= RENDER_STEREO_MODE_OFF)
+  RENDER_STEREO_MODE applyMode = mode;
+
+  // resolve automatic mode before applying
+  if (mode == RENDER_STEREO_MODE_AUTO)
   {
-    if(!g_Windowing.SupportsStereo(mode))
-      return;
+    if (g_infoManager.EvaluateBool("videoplayer.isstereoscopic"))
+      applyMode = GetStereoModeOfPlayingVideo();
+    else
+      applyMode = RENDER_STEREO_MODE_OFF;
+  }
 
+  if (applyMode != currentMode && applyMode >= RENDER_STEREO_MODE_OFF)
+  {
+    if (!g_Windowing.SupportsStereo(applyMode))
+      return;
     m_lastStereoMode = currentMode;
-    CSettings::Get().SetInt("videoscreen.stereoscopicmode", mode);
+    CSettings::Get().SetInt("videoscreen.stereoscopicmode", applyMode);
   }
 }
 
@@ -210,6 +220,12 @@ RENDER_STEREO_MODE CStereoscopicsManager::GetStereoModeByUserChoice(const CStdSt
       if (mode == selectableMode)
         pDlgSelect->SetSelected( label );
     }
+    // inject AUTO pseudo mode after OFF
+    if (i == RENDER_STEREO_MODE_OFF)
+    {
+      selectableModes.push_back(RENDER_STEREO_MODE_AUTO);
+      pDlgSelect->Add(GetLabelForStereoMode(RENDER_STEREO_MODE_AUTO));
+    }
   }
 
   pDlgSelect->DoModal();
@@ -248,20 +264,7 @@ CStdString CStereoscopicsManager::GetLabelForStereoMode(const RENDER_STEREO_MODE
 
 RENDER_STEREO_MODE CStereoscopicsManager::GetPreferredPlaybackMode(void)
 {
-  RENDER_STEREO_MODE playbackMode = m_lastStereoMode;
-  int preferredMode = CSettings::Get().GetInt("videoscreen.preferedstereoscopicmode");
-  if (preferredMode == RENDER_STEREO_MODE_AUTO) // automatic mode, detect by movie
-  {
-    if (g_infoManager.EvaluateBool("videoplayer.isstereoscopic"))
-      playbackMode = GetStereoModeOfPlayingVideo();
-    else if (playbackMode == RENDER_STEREO_MODE_OFF)
-      playbackMode = GetNextSupportedStereoMode(RENDER_STEREO_MODE_OFF);
-  }
-  else // predefined mode
-  {
-    playbackMode = (RENDER_STEREO_MODE) preferredMode;
-  }
-  return playbackMode;
+  return (RENDER_STEREO_MODE) CSettings::Get().GetInt("videoscreen.preferedstereoscopicmode");
 }
 
 int CStereoscopicsManager::ConvertVideoToGuiStereoMode(const std::string &mode)
@@ -450,9 +453,6 @@ void CStereoscopicsManager::ApplyStereoMode(const RENDER_STEREO_MODE &mode, bool
 
 void CStereoscopicsManager::OnPlaybackStarted(void)
 {
-  if (!g_infoManager.EvaluateBool("videoplayer.isstereoscopic"))
-    return;
-
   STEREOSCOPIC_PLAYBACK_MODE playbackMode = (STEREOSCOPIC_PLAYBACK_MODE) CSettings::Get().GetInt("videoplayer.stereoscopicplaybackmode");
   RENDER_STEREO_MODE mode = GetStereoMode();
 
@@ -460,8 +460,31 @@ void CStereoscopicsManager::OnPlaybackStarted(void)
   if (playbackMode == STEREOSCOPIC_PLAYBACK_MODE_IGNORE && mode == RENDER_STEREO_MODE_OFF)
     return;
 
-  if (mode != RENDER_STEREO_MODE_OFF)
+  if (!g_infoManager.EvaluateBool("videoplayer.isstereoscopic"))
+  {
+    // exit stereo mode if started item is not stereoscopic
+    // and if user prefers to stop 3D playback when movie is finished
+    if (mode != RENDER_STEREO_MODE_OFF && CSettings::Get().GetBool("videoplayer.quitstereomodeonstop"))
+      SetStereoMode(RENDER_STEREO_MODE_OFF);
     return;
+  }
+
+  RENDER_STEREO_MODE preferred = GetPreferredPlaybackMode();
+  RENDER_STEREO_MODE playing = GetStereoModeOfPlayingVideo();
+
+  if (mode != RENDER_STEREO_MODE_OFF)
+  {
+    // don't change mode if user selected to not exit stereomode on playback stop
+    // users selecting this option usually have to manually switch their TV into 3D mode
+    // and would be annoyed by having to switch TV modes when next movies comes up
+    // @todo probably add a new setting for just this behavior
+    if (CSettings::Get().GetBool("videoplayer.quitstereomodeonstop") == false)
+      return;
+
+    // only change to new stereo mode if not yet in preferred stereo mode
+    if (mode == preferred || (preferred == RENDER_STEREO_MODE_AUTO && mode == playing))
+      return;
+  }
 
   switch (playbackMode)
   {
@@ -473,9 +496,6 @@ void CStereoscopicsManager::OnPlaybackStarted(void)
       pDlgSelect->Reset();
       pDlgSelect->SetHeading(g_localizeStrings.Get(36527).c_str());
 
-      RENDER_STEREO_MODE preferred = GetPreferredPlaybackMode();
-      RENDER_STEREO_MODE playing   = GetStereoModeOfPlayingVideo();
-
       int idx_playing   = -1
         , idx_mono      = -1;
         
@@ -488,7 +508,7 @@ void CStereoscopicsManager::OnPlaybackStarted(void)
 
       idx_mono = pDlgSelect->Add(GetLabelForStereoMode(RENDER_STEREO_MODE_MONO)); // mono / 2d
 
-      if(playing != RENDER_STEREO_MODE_OFF && playing != preferred && g_Windowing.SupportsStereo(playing))
+      if (playing != RENDER_STEREO_MODE_OFF && playing != preferred && preferred != RENDER_STEREO_MODE_AUTO && g_Windowing.SupportsStereo(playing)) // same as movie
         idx_playing = pDlgSelect->Add((CStdString)g_localizeStrings.Get(36532)
                                     + " ("
                                     + GetLabelForStereoMode(playing)
@@ -503,7 +523,7 @@ void CStereoscopicsManager::OnPlaybackStarted(void)
         int iItem = pDlgSelect->GetSelectedLabel();
         if      (iItem == idx_preferred) mode = preferred;
         else if (iItem == idx_mono)      mode = RENDER_STEREO_MODE_MONO;
-        else if (iItem == idx_playing)   mode = playing;
+        else if (iItem == idx_playing)   mode = RENDER_STEREO_MODE_AUTO;
         else if (iItem == idx_select)    mode = GetStereoModeByUserChoice();
 
         SetStereoMode(mode);
@@ -513,7 +533,7 @@ void CStereoscopicsManager::OnPlaybackStarted(void)
     }
     break;
   case STEREOSCOPIC_PLAYBACK_MODE_PREFERRED: // Stereoscopic
-    SetStereoMode( GetPreferredPlaybackMode() );
+    SetStereoMode( preferred );
     break;
   case 2: // Mono
     SetStereoMode( RENDER_STEREO_MODE_MONO );

From a411c11e4cc4dd9a0fafe83f7886c88e4eb29c65 Mon Sep 17 00:00:00 2001
From: da-anda <da-anda@xbmc.org>
Date: Sat, 16 Aug 2014 11:20:54 +0200
Subject: [PATCH 74/88] remember user selected 3D modes between videos until
 playback ended

---
 xbmc/guilib/StereoscopicsManager.cpp | 79 ++++++++++++++++++++++++++----------
 xbmc/guilib/StereoscopicsManager.h   |  6 ++-
 xbmc/rendering/RenderSystem.h        |  1 +
 3 files changed, 63 insertions(+), 23 deletions(-)

diff --git a/xbmc/guilib/StereoscopicsManager.cpp b/xbmc/guilib/StereoscopicsManager.cpp
index b013942..3db8eaa 100644
--- a/xbmc/guilib/StereoscopicsManager.cpp
+++ b/xbmc/guilib/StereoscopicsManager.cpp
@@ -95,7 +95,8 @@ static const struct StereoModeMap StringToGuiModeMap[] =
 
 CStereoscopicsManager::CStereoscopicsManager(void)
 {
-  m_lastStereoMode = RENDER_STEREO_MODE_OFF;
+  m_stereoModeSetByUser = RENDER_STEREO_MODE_UNDEFINED;
+  m_lastStereoModeSetByUser = RENDER_STEREO_MODE_UNDEFINED;
 }
 
 CStereoscopicsManager::~CStereoscopicsManager(void)
@@ -110,7 +111,6 @@ CStereoscopicsManager& CStereoscopicsManager::Get(void)
 
 void CStereoscopicsManager::Initialize(void)
 {
-  m_lastStereoMode = GetStereoMode();
   // turn off stereo mode on XBMC startup
   SetStereoMode(RENDER_STEREO_MODE_OFF);
 }
@@ -120,6 +120,16 @@ RENDER_STEREO_MODE CStereoscopicsManager::GetStereoMode(void)
   return (RENDER_STEREO_MODE) CSettings::Get().GetInt("videoscreen.stereoscopicmode");
 }
 
+void CStereoscopicsManager::SetStereoModeByUser(const RENDER_STEREO_MODE &mode)
+{
+  // only update last user mode if desired mode is different from current
+  if (mode != m_stereoModeSetByUser)
+    m_lastStereoModeSetByUser = m_stereoModeSetByUser;
+
+  m_stereoModeSetByUser = mode;
+  SetStereoMode(mode);
+}
+
 void CStereoscopicsManager::SetStereoMode(const RENDER_STEREO_MODE &mode)
 {
   RENDER_STEREO_MODE currentMode = GetStereoMode();
@@ -138,7 +148,6 @@ void CStereoscopicsManager::SetStereoMode(const RENDER_STEREO_MODE &mode)
   {
     if (!g_Windowing.SupportsStereo(applyMode))
       return;
-    m_lastStereoMode = currentMode;
     CSettings::Get().SetInt("videoscreen.stereoscopicmode", applyMode);
   }
 }
@@ -384,46 +393,65 @@ bool CStereoscopicsManager::OnAction(const CAction &action)
 
   if (action.GetID() == ACTION_STEREOMODE_NEXT)
   {
-    SetStereoMode(GetNextSupportedStereoMode(mode));
+    SetStereoModeByUser(GetNextSupportedStereoMode(mode));
     return true;
   }
   else if (action.GetID() == ACTION_STEREOMODE_PREVIOUS)
   {
-    SetStereoMode(GetNextSupportedStereoMode(mode, RENDER_STEREO_MODE_COUNT - 1));
+    SetStereoModeByUser(GetNextSupportedStereoMode(mode, RENDER_STEREO_MODE_COUNT - 1));
     return true;
   }
   else if (action.GetID() == ACTION_STEREOMODE_TOGGLE)
   {
     if (mode == RENDER_STEREO_MODE_OFF)
     {
-      RENDER_STEREO_MODE targetMode = m_lastStereoMode;
-      if (targetMode == RENDER_STEREO_MODE_OFF)
-        targetMode = GetPreferredPlaybackMode();
-      SetStereoMode(targetMode);
+      RENDER_STEREO_MODE targetMode = GetPreferredPlaybackMode();
+
+      // if user selected a specific mode before, make sure to
+      // switch back into that mode on toggle.
+      if (m_stereoModeSetByUser != RENDER_STEREO_MODE_UNDEFINED)
+      {
+        // if user mode is set to OFF, he manually turned it off before. In this case use the last user applied mode
+        if (m_stereoModeSetByUser != RENDER_STEREO_MODE_OFF)
+          targetMode = m_stereoModeSetByUser;
+        else if (m_lastStereoModeSetByUser != RENDER_STEREO_MODE_UNDEFINED && m_lastStereoModeSetByUser != RENDER_STEREO_MODE_OFF)
+          targetMode = m_lastStereoModeSetByUser;
+      }
+
+      SetStereoModeByUser(targetMode);
     }
     else
     {
-      SetStereoMode(RENDER_STEREO_MODE_OFF);
+      SetStereoModeByUser(RENDER_STEREO_MODE_OFF);
     }
     return true;
   }
   else if (action.GetID() == ACTION_STEREOMODE_SELECT)
   {
-    SetStereoMode(GetStereoModeByUserChoice());
+    SetStereoModeByUser(GetStereoModeByUserChoice());
     return true;
   }
   else if (action.GetID() == ACTION_STEREOMODE_TOMONO)
   {
     if (mode == RENDER_STEREO_MODE_MONO)
     {
-      RENDER_STEREO_MODE targetMode = m_lastStereoMode;
-      if (targetMode == RENDER_STEREO_MODE_OFF)
-        targetMode = GetPreferredPlaybackMode();
-      SetStereoMode(targetMode);
+      RENDER_STEREO_MODE targetMode = GetPreferredPlaybackMode();
+
+      // if we have an old userdefined steremode, use that one as toggle target
+      if (m_stereoModeSetByUser != RENDER_STEREO_MODE_UNDEFINED)
+      {
+        // if user mode is set to OFF, he manually turned it off before. In this case use the last user applied mode
+        if (m_stereoModeSetByUser != RENDER_STEREO_MODE_OFF && m_stereoModeSetByUser != mode)
+          targetMode = m_stereoModeSetByUser;
+        else if (m_lastStereoModeSetByUser != RENDER_STEREO_MODE_UNDEFINED && m_lastStereoModeSetByUser != RENDER_STEREO_MODE_OFF && m_lastStereoModeSetByUser != mode)
+          targetMode = m_lastStereoModeSetByUser;
+      }
+
+      SetStereoModeByUser(targetMode);
     }
     else
     {
-      SetStereoMode(RENDER_STEREO_MODE_MONO);
+      SetStereoModeByUser(RENDER_STEREO_MODE_MONO);
     }
     return true;
   }
@@ -431,7 +459,7 @@ bool CStereoscopicsManager::OnAction(const CAction &action)
   {
     int stereoMode = ConvertStringToGuiStereoMode(action.GetName());
     if (stereoMode > -1)
-      SetStereoMode( (RENDER_STEREO_MODE) stereoMode);
+      SetStereoModeByUser( (RENDER_STEREO_MODE) stereoMode );
     return true;
   }
 
@@ -469,6 +497,13 @@ void CStereoscopicsManager::OnPlaybackStarted(void)
     return;
   }
 
+  // if we're not in stereomode yet, restore previously selected stereo mode in case it was user selected
+  if (m_stereoModeSetByUser != RENDER_STEREO_MODE_UNDEFINED)
+  {
+    SetStereoMode(m_stereoModeSetByUser);
+    return;
+  }
+
   RENDER_STEREO_MODE preferred = GetPreferredPlaybackMode();
   RENDER_STEREO_MODE playing = GetStereoModeOfPlayingVideo();
 
@@ -526,7 +561,7 @@ void CStereoscopicsManager::OnPlaybackStarted(void)
         else if (iItem == idx_playing)   mode = RENDER_STEREO_MODE_AUTO;
         else if (iItem == idx_select)    mode = GetStereoModeByUserChoice();
 
-        SetStereoMode(mode);
+        SetStereoModeByUser( mode );
       }
 
       CApplicationMessenger::Get().MediaUnPause();
@@ -546,8 +581,10 @@ void CStereoscopicsManager::OnPlaybackStarted(void)
 void CStereoscopicsManager::OnPlaybackStopped(void)
 {
   RENDER_STEREO_MODE mode = GetStereoMode();
-  if (CSettings::Get().GetBool("videoplayer.quitstereomodeonstop") == true && mode != RENDER_STEREO_MODE_OFF)
-  {
+  if (CSettings::Get().GetBool("videoplayer.quitstereomodeonstop") && mode != RENDER_STEREO_MODE_OFF)
     SetStereoMode(RENDER_STEREO_MODE_OFF);
-  }
+  // reset user modes on playback end to start over new on next playback and not end up in a probably unwanted mode
+  if (m_stereoModeSetByUser != RENDER_STEREO_MODE_OFF)
+    m_lastStereoModeSetByUser = m_stereoModeSetByUser;
+  m_stereoModeSetByUser = RENDER_STEREO_MODE_UNDEFINED;
 }
diff --git a/xbmc/guilib/StereoscopicsManager.h b/xbmc/guilib/StereoscopicsManager.h
index 422cba2..6e9c36d 100644
--- a/xbmc/guilib/StereoscopicsManager.h
+++ b/xbmc/guilib/StereoscopicsManager.h
@@ -54,8 +54,9 @@ class CStereoscopicsManager : public ISettingCallback,
   static CStereoscopicsManager& Get(void);
 
   void Initialize(void);
-  void SetStereoMode(const RENDER_STEREO_MODE &mode);
   RENDER_STEREO_MODE GetStereoMode(void);
+  void SetStereoModeByUser(const RENDER_STEREO_MODE &mode);
+  void SetStereoMode(const RENDER_STEREO_MODE &mode);
   RENDER_STEREO_MODE GetNextSupportedStereoMode(const RENDER_STEREO_MODE &currentMode, int step = 1);
   std::string DetectStereoModeByString(const std::string &needle);
   RENDER_STEREO_MODE GetStereoModeByUserChoice(const CStdString &heading = "");
@@ -92,5 +93,6 @@ class CStereoscopicsManager : public ISettingCallback,
   void OnPlaybackStarted(void);
   void OnPlaybackStopped(void);
 
-  RENDER_STEREO_MODE m_lastStereoMode;
+  RENDER_STEREO_MODE m_stereoModeSetByUser;
+  RENDER_STEREO_MODE m_lastStereoModeSetByUser;
 };
diff --git a/xbmc/rendering/RenderSystem.h b/xbmc/rendering/RenderSystem.h
index c1dfb93..cb54bd0 100644
--- a/xbmc/rendering/RenderSystem.h
+++ b/xbmc/rendering/RenderSystem.h
@@ -81,6 +81,7 @@ enum RENDER_STEREO_MODE
 
   // psuevdo modes
   RENDER_STEREO_MODE_AUTO = 100,
+  RENDER_STEREO_MODE_UNDEFINED = 999,
 };
 
 

From bae79caf7d0aabff2e681cab22830c8192657bb0 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sun, 28 Sep 2014 17:35:31 +0100
Subject: [PATCH 75/88] [renderer] Avoid warning for too few buffers with
 bypass renderer

Bypass renderer doesn't use a video render queue, so shouldn't generate a warning message
---
 xbmc/cores/VideoRenderers/RenderManager.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/xbmc/cores/VideoRenderers/RenderManager.cpp b/xbmc/cores/VideoRenderers/RenderManager.cpp
index 1ecdc16..cc3a76b 100644
--- a/xbmc/cores/VideoRenderers/RenderManager.cpp
+++ b/xbmc/cores/VideoRenderers/RenderManager.cpp
@@ -270,7 +270,8 @@ bool CXBMCRenderManager::Configure(unsigned int width, unsigned int height, unsi
     if(m_QueueSize < 2)
     {
       m_QueueSize = 2;
-      CLog::Log(LOGWARNING, "CXBMCRenderManager::Configure - queue size too small (%d, %d, %d)", m_QueueSize, renderbuffers, buffers);
+      if (m_format != RENDER_FMT_BYPASS)
+        CLog::Log(LOGWARNING, "CXBMCRenderManager::Configure - queue size too small (%d, %d, %d)", m_QueueSize, renderbuffers, buffers);
     }
 
     m_pRenderer->SetBufferSize(m_QueueSize);

From aaa268df687880936e1c0d9456b7e7670cf814e9 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sun, 28 Sep 2014 19:28:17 +0100
Subject: [PATCH 76/88] [mmalcodec] Introduce a preroll period to buffer up
 frames on startup

---
 xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp | 21 +++++++++++++--------
 xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.h   |  1 +
 2 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp
index 6a1f70a..c9aa390 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp
@@ -136,6 +136,7 @@ CMMALVideo::CMMALVideo()
   m_output_busy = 0;
   m_demux_queue_length = 0;
   m_es_format = mmal_format_alloc();
+  m_preroll = true;
 }
 
 CMMALVideo::~CMMALVideo()
@@ -699,6 +700,7 @@ bool CMMALVideo::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options, MMALVide
 
   m_drop_state = false;
   m_startframe = false;
+  m_preroll = !m_hints.stills;
 
   return true;
 }
@@ -910,24 +912,26 @@ int CMMALVideo::Decode(uint8_t* pData, int iSize, double dts, double pts)
       break;
   }
   int ret = 0;
-  if (!m_output_ready.empty())
+  if (mmal_queue_length(m_dec_input_pool->queue) > 0 && !m_demux_queue_length)
   {
     #if defined(MMAL_DEBUG_VERBOSE)
-    CLog::Log(LOGDEBUG, "%s::%s -  got output picture:%d", CLASSNAME, __func__, m_output_ready.size());
+    CLog::Log(LOGDEBUG, "%s::%s - got space for output: demux_queue(%d) space(%d)", CLASSNAME, __func__, m_demux_queue_length, mmal_queue_length(m_dec_input_pool->queue) * m_dec_input->buffer_size);
     #endif
-    ret |= VC_PICTURE;
+    ret |= VC_BUFFER;
   }
-  if (mmal_queue_length(m_dec_input_pool->queue) > 0 && !m_demux_queue_length)
+  else
+    m_preroll = false;
+  if (!m_output_ready.empty() && !m_preroll)
   {
     #if defined(MMAL_DEBUG_VERBOSE)
-    CLog::Log(LOGDEBUG, "%s::%s - got space for output: demux_queue(%d) space(%d)", CLASSNAME, __func__, m_demux_queue_length, mmal_queue_length(m_dec_input_pool->queue) * m_dec_input->buffer_size);
+    CLog::Log(LOGDEBUG, "%s::%s -  got output picture:%d", CLASSNAME, __func__, m_output_ready.size());
     #endif
-    ret |= VC_BUFFER;
+    ret |= VC_PICTURE;
   }
   if (!ret)
   {
-    CLog::Log(LOGDEBUG, "%s::%s - Nothing to do: dts_queue(%d) ready_queue(%d) busy_queue(%d) demux_queue(%d) space(%d)",
-        CLASSNAME, __func__, m_dts_queue.size(), m_output_ready.size(), m_output_busy, m_demux_queue_length, mmal_queue_length(m_dec_input_pool->queue) * m_dec_input->buffer_size);
+    CLog::Log(LOGDEBUG, "%s::%s - Nothing to do: dts_queue(%d) ready_queue(%d) busy_queue(%d) demux_queue(%d) space(%d) preroll(%d)",
+        CLASSNAME, __func__, m_dts_queue.size(), m_output_ready.size(), m_output_busy, m_demux_queue_length, mmal_queue_length(m_dec_input_pool->queue) * m_dec_input->buffer_size, m_preroll);
     Sleep(10); // otherwise we busy spin
   }
   return ret;
@@ -971,6 +975,7 @@ void CMMALVideo::Reset(void)
   m_decoderPts = DVD_NOPTS_VALUE;
   m_droppedPics = 0;
   m_decode_frame_number = 1;
+  m_preroll = !m_hints.stills;
 }
 
 
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.h
index b4aa571..a2da46b 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.h
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.h
@@ -140,6 +140,7 @@ class CMMALVideo
   unsigned int      m_decode_frame_number;
   double            m_decoderPts;
   unsigned int      m_droppedPics;
+  bool              m_preroll;
 
   MMAL_COMPONENT_T *m_dec;
   MMAL_PORT_T *m_dec_input;

From 0ac7bd7de554636f538192fb24158493022d9b11 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Fri, 11 Apr 2014 16:12:27 +0100
Subject: [PATCH 77/88] [omxplayer] Add ability to log more timestamp info in
 extra debug settings

---
 language/English/strings.po             |  5 +++++
 xbmc/commons/ilog.h                     |  1 +
 xbmc/cores/dvdplayer/DVDPlayer.cpp      | 10 ++++++----
 xbmc/cores/omxplayer/OMXPlayerAudio.cpp |  8 ++++----
 xbmc/cores/omxplayer/OMXPlayerVideo.cpp |  9 +++++----
 xbmc/settings/AdvancedSettings.cpp      |  3 +++
 6 files changed, 24 insertions(+), 12 deletions(-)

diff --git a/language/English/strings.po b/language/English/strings.po
index dd46ea5..d699647 100755
--- a/language/English/strings.po
+++ b/language/English/strings.po
@@ -2886,6 +2886,11 @@ msgctxt "#679"
 msgid "Verbose logging for CEC library"
 msgstr ""
 
+#: xbmc/settings/AdvancedSettings.cpp
+msgctxt "#697"
+msgid "Verbose logging for OMXPLAYER"
+msgstr ""
+
 #empty strings from id 680 to 699
 
 msgctxt "#700"
diff --git a/xbmc/commons/ilog.h b/xbmc/commons/ilog.h
index 92e2835..ad02d95 100644
--- a/xbmc/commons/ilog.h
+++ b/xbmc/commons/ilog.h
@@ -53,6 +53,7 @@
 #define LOGAIRTUNES (1 << (LOGMASKBIT + 8))
 #define LOGUPNP     (1 << (LOGMASKBIT + 9))
 #define LOGCEC      (1 << (LOGMASKBIT + 10))
+#define LOGOMXPLAYER  (1 << (LOGMASKBIT+11))
 
 #include "utils/params_check_macros.h"
 
diff --git a/xbmc/cores/dvdplayer/DVDPlayer.cpp b/xbmc/cores/dvdplayer/DVDPlayer.cpp
index 31dda29..afdf74a 100644
--- a/xbmc/cores/dvdplayer/DVDPlayer.cpp
+++ b/xbmc/cores/dvdplayer/DVDPlayer.cpp
@@ -1135,7 +1135,8 @@ void CDVDPlayer::OMXDoProcessing()
     m_OmxPlayerState.video_fifo = (int)(100.0*(m_dvdPlayerVideo->GetDecoderBufferSize()-m_dvdPlayerVideo->GetDecoderFreeSpace())/m_dvdPlayerVideo->GetDecoderBufferSize());
     m_OmxPlayerState.audio_fifo = (int)(100.0*audio_fifo/m_dvdPlayerAudio->GetCacheTotal());
 
-    #ifdef _DEBUG
+  if (g_advancedSettings.CanLogComponent(LOGOMXPLAYER))
+  {
     static unsigned count;
     if ((count++ & 7) == 0)
     {
@@ -1155,7 +1156,7 @@ void CDVDPlayer::OMXDoProcessing()
       vc_gencmd(response, sizeof response, "render_bar 7 audio_queue %d %d %d %d",
             m_dvdPlayerAudio->GetLevel(), 0, 0, 100);
     }
-    #endif
+  }
     if (audio_pts != DVD_NOPTS_VALUE)
     {
       audio_fifo_low = m_HasAudio && audio_fifo < threshold;
@@ -1171,13 +1172,14 @@ void CDVDPlayer::OMXDoProcessing()
     if (!m_HasVideo && m_HasAudio)
       video_fifo_high = true;
 
-    #ifdef _DEBUG
+  if (g_advancedSettings.CanLogComponent(LOGOMXPLAYER))
+  {
     CLog::Log(LOGDEBUG, "%s::%s M:%.6f-%.6f (A:%.6f V:%.6f) PEF:%d%d%d S:%.2f A:%.2f V:%.2f/T:%.2f (A:%d%d V:%d%d) A:%d%% V:%d%% (%.2f,%.2f)", "CDVDPlayer", __FUNCTION__,
       m_OmxPlayerState.stamp*1e-6, m_OmxPlayerState.av_clock.OMXClockAdjustment()*1e-6, audio_pts*1e-6, video_pts*1e-6, m_OmxPlayerState.av_clock.OMXIsPaused(), m_OmxPlayerState.bOmxSentEOFs, not_accepts_data, m_playSpeed * (1.0f/DVD_PLAYSPEED_NORMAL),
       audio_pts == DVD_NOPTS_VALUE ? 0.0:audio_fifo, video_pts == DVD_NOPTS_VALUE ? 0.0:video_fifo, m_OmxPlayerState.threshold,
       audio_fifo_low, audio_fifo_high, video_fifo_low, video_fifo_high,
       m_dvdPlayerAudio->GetLevel(), m_dvdPlayerVideo->GetLevel(), m_dvdPlayerAudio->GetDelay(), (float)m_dvdPlayerAudio->GetCacheTotal());
-    #endif
+  }
 
     if(!m_Pause && (m_OmxPlayerState.bOmxSentEOFs || not_accepts_data || (audio_fifo_high && video_fifo_high) || m_playSpeed != DVD_PLAYSPEED_NORMAL))
     {
diff --git a/xbmc/cores/omxplayer/OMXPlayerAudio.cpp b/xbmc/cores/omxplayer/OMXPlayerAudio.cpp
index f1208e6..f6ef16f 100644
--- a/xbmc/cores/omxplayer/OMXPlayerAudio.cpp
+++ b/xbmc/cores/omxplayer/OMXPlayerAudio.cpp
@@ -362,10 +362,10 @@ void OMXPlayerAudio::Process()
       DemuxPacket* pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket();
       bool bPacketDrop     = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop();
 
-      #ifdef _DEBUG
-      CLog::Log(LOGINFO, "Audio: dts:%.0f pts:%.0f size:%d (s:%d f:%d d:%d l:%d) s:%d %d/%d late:%d,%d", pPacket->dts, pPacket->pts,
-           (int)pPacket->iSize, m_started, m_flush, bPacketDrop, m_stalled, m_speed, 0, 0, (int)m_omxAudio.GetAudioRenderingLatency(), (int)m_hints_current.samplerate);
-      #endif
+      if (g_advancedSettings.CanLogComponent(LOGOMXPLAYER))
+        CLog::Log(LOGINFO, "Audio: dts:%.0f pts:%.0f size:%d (s:%d f:%d d:%d l:%d) s:%d %d/%d late:%d,%d", pPacket->dts, pPacket->pts,
+             (int)pPacket->iSize, m_started, m_flush, bPacketDrop, m_stalled, m_speed, 0, 0, (int)m_omxAudio.GetAudioRenderingLatency(), (int)m_hints_current.samplerate);
+
       if(Decode(pPacket, m_speed > DVD_PLAYSPEED_NORMAL || m_speed < 0 || bPacketDrop))
       {
         // we are not running until something is cached in output device
diff --git a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
index c2bd788..5c4a515 100644
--- a/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
+++ b/xbmc/cores/omxplayer/OMXPlayerVideo.cpp
@@ -42,6 +42,7 @@
 #include "DVDOverlayRenderer.h"
 #include "settings/DisplaySettings.h"
 #include "settings/Settings.h"
+#include "settings/AdvancedSettings.h"
 #include "settings/MediaSettings.h"
 #include "cores/VideoRenderers/RenderFormats.h"
 #include "cores/VideoRenderers/RenderFlags.h"
@@ -461,10 +462,10 @@ void OMXPlayerVideo::Process()
       DemuxPacket* pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket();
       bool bPacketDrop     = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop();
 
-      #ifdef _DEBUG
-      CLog::Log(LOGINFO, "Video: dts:%.0f pts:%.0f size:%d (s:%d f:%d d:%d l:%d) s:%d %d/%d late:%d\n", pPacket->dts, pPacket->pts, 
-          (int)pPacket->iSize, m_started, m_flush, bPacketDrop, m_stalled, m_speed, 0, 0, 0);
-      #endif
+      if (g_advancedSettings.CanLogComponent(LOGOMXPLAYER))
+        CLog::Log(LOGINFO, "Video: dts:%.0f pts:%.0f size:%d (s:%d f:%d d:%d l:%d) s:%d %d/%d late:%d\n", pPacket->dts, pPacket->pts,
+            (int)pPacket->iSize, m_started, m_flush, bPacketDrop, m_stalled, m_speed, 0, 0, 0);
+
       if (m_messageQueue.GetDataSize() == 0
       ||  m_speed < 0)
       {
diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp
index 2959ae8..c9f9c94 100644
--- a/xbmc/settings/AdvancedSettings.cpp
+++ b/xbmc/settings/AdvancedSettings.cpp
@@ -1388,6 +1388,9 @@ void CAdvancedSettings::SettingOptionsLoggingComponentsFiller(const CSetting *se
 #ifdef HAVE_LIBCEC
   list.push_back(std::make_pair(g_localizeStrings.Get(679), LOGCEC));
 #endif
+#ifdef TARGET_RASPBERRY_PI
+  list.push_back(std::make_pair(g_localizeStrings.Get(697), LOGOMXPLAYER));
+#endif
 }
 
 void CAdvancedSettings::setExtraLogLevel(const std::vector<CVariant> &components)

From 5dafcbd232750aa408bcec39e5f91b60acb23ad6 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Mon, 7 Apr 2014 23:13:55 +0100
Subject: [PATCH 78/88] [omxplayer] Add ability to dump out audio/video data
 for later debugging

---
 language/English/strings.po        | 10 ++++++++
 xbmc/commons/ilog.h                |  2 ++
 xbmc/cores/omxplayer/OMXAudio.cpp  | 49 ++++++++++++++++++++++++++++++++++++++
 xbmc/cores/omxplayer/OMXVideo.cpp  | 47 ++++++++++++++++++++++++++++++++++++
 xbmc/settings/AdvancedSettings.cpp |  4 ++++
 5 files changed, 112 insertions(+)

diff --git a/language/English/strings.po b/language/English/strings.po
index d699647..856d371 100755
--- a/language/English/strings.po
+++ b/language/English/strings.po
@@ -2891,6 +2891,16 @@ msgctxt "#697"
 msgid "Verbose logging for OMXPLAYER"
 msgstr ""
 
+#: xbmc/settings/AdvancedSettings.cpp
+msgctxt "#698"
+msgid "Dump video frames to debug file"
+msgstr ""
+
+#: xbmc/settings/AdvancedSettings.cpp
+msgctxt "#699"
+msgid "Dump audio frames to debug file"
+msgstr ""
+
 #empty strings from id 680 to 699
 
 msgctxt "#700"
diff --git a/xbmc/commons/ilog.h b/xbmc/commons/ilog.h
index ad02d95..be40370 100644
--- a/xbmc/commons/ilog.h
+++ b/xbmc/commons/ilog.h
@@ -54,6 +54,8 @@
 #define LOGUPNP     (1 << (LOGMASKBIT + 9))
 #define LOGCEC      (1 << (LOGMASKBIT + 10))
 #define LOGOMXPLAYER  (1 << (LOGMASKBIT+11))
+#define LOGDUMPVIDEO  (1 << (LOGMASKBIT+12))
+#define LOGDUMPAUDIO  (1 << (LOGMASKBIT+13))
 
 #include "utils/params_check_macros.h"
 
diff --git a/xbmc/cores/omxplayer/OMXAudio.cpp b/xbmc/cores/omxplayer/OMXAudio.cpp
index ce35b07..22ee423 100644
--- a/xbmc/cores/omxplayer/OMXAudio.cpp
+++ b/xbmc/cores/omxplayer/OMXAudio.cpp
@@ -60,6 +60,49 @@ static const uint16_t AC3FSCod   [] = {48000, 44100, 32000, 0};
 
 static const uint16_t DTSFSCod   [] = {0, 8000, 16000, 32000, 0, 0, 11025, 22050, 44100, 0, 0, 12000, 24000, 48000, 0, 0};
 
+//#define DEBUG_PLAYBACK
+static void dump_omx_buffer(OMX_BUFFERHEADERTYPE *omx_buffer)
+{
+  if (!(g_advancedSettings.CanLogComponent(LOGDUMPAUDIO)))
+    return;
+  static FILE *fp;
+  if (!omx_buffer)
+  {
+    if (fp)
+    {
+      fclose(fp);
+      fp = NULL;
+    }
+    return;
+  }
+  if (!fp)
+  {
+    char filename[1024];
+    strcpy(filename, g_advancedSettings.m_logFolder.c_str());
+    strcat(filename, "audio.dat");
+#ifdef DEBUG_PLAYBACK
+    fp = fopen(filename, "rb");
+#else
+    fp = fopen(filename, "wb");
+#endif
+    }
+  if (fp)
+  {
+#ifdef DEBUG_PLAYBACK
+    OMX_BUFFERHEADERTYPE omx = {0};
+    int s = fread(&omx, sizeof omx, 1, fp);
+    omx_buffer->nFilledLen = omx.nFilledLen;
+    omx_buffer->nFlags = omx.nFlags;
+    omx_buffer->nTimeStamp = omx.nTimeStamp;
+    if (s==1)
+      fread(omx_buffer->pBuffer, omx_buffer->nFilledLen, 1, fp);
+#else
+    if (fwrite(omx_buffer, sizeof *omx_buffer, 1, fp) == 1)
+      fwrite(omx_buffer->pBuffer, omx_buffer->nFilledLen, 1, fp);
+#endif
+  }
+}
+
 //////////////////////////////////////////////////////////////////////
 // Construction/Destruction
 //////////////////////////////////////////////////////////////////////
@@ -867,6 +910,7 @@ bool COMXAudio::Initialize(AEAudioFormat format, OMXClock *clock, CDVDStreamInfo
     memcpy((unsigned char *)omx_buffer->pBuffer, &m_wave_header, omx_buffer->nFilledLen);
     omx_buffer->nFlags = OMX_BUFFERFLAG_CODECCONFIG | OMX_BUFFERFLAG_ENDOFFRAME;
 
+    dump_omx_buffer(omx_buffer);
     omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer);
     if (omx_err != OMX_ErrorNone)
     {
@@ -899,6 +943,7 @@ bool COMXAudio::Initialize(AEAudioFormat format, OMXClock *clock, CDVDStreamInfo
       memcpy((unsigned char *)omx_buffer->pBuffer, m_extradata, omx_buffer->nFilledLen);
       omx_buffer->nFlags = OMX_BUFFERFLAG_CODECCONFIG | OMX_BUFFERFLAG_ENDOFFRAME;
   
+      dump_omx_buffer(omx_buffer);
       omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer);
       if (omx_err != OMX_ErrorNone)
       {
@@ -936,6 +981,8 @@ bool COMXAudio::Deinitialize()
   CLog::Log(LOGNOTICE, "COMXAudio::%s start", __func__);
   CSingleLock lock (m_critSection);
 
+  dump_omx_buffer(NULL);
+
   if ( m_omx_tunnel_clock_analog.IsInitialized() )
     m_omx_tunnel_clock_analog.Deestablish();
   if ( m_omx_tunnel_clock_hdmi.IsInitialized() )
@@ -1243,6 +1290,7 @@ unsigned int COMXAudio::AddPackets(const void* data, unsigned int len, double dt
     int nRetry = 0;
     while(true)
     {
+      dump_omx_buffer(omx_buffer);
       omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer);
       if (omx_err == OMX_ErrorNone)
       {
@@ -1490,6 +1538,7 @@ void COMXAudio::SubmitEOS()
 
   omx_buffer->nFlags = OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS | OMX_BUFFERFLAG_TIME_UNKNOWN;
 
+  dump_omx_buffer(omx_buffer);
   omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer);
   if (omx_err != OMX_ErrorNone)
   {
diff --git a/xbmc/cores/omxplayer/OMXVideo.cpp b/xbmc/cores/omxplayer/OMXVideo.cpp
index 0363aaa..6cfb148 100644
--- a/xbmc/cores/omxplayer/OMXVideo.cpp
+++ b/xbmc/cores/omxplayer/OMXVideo.cpp
@@ -63,6 +63,49 @@
 
 #define MAX_TEXT_LENGTH 1024
 
+//#define DEBUG_PLAYBACK
+static void dump_omx_buffer(OMX_BUFFERHEADERTYPE *omx_buffer)
+{
+  if (!(g_advancedSettings.CanLogComponent(LOGDUMPVIDEO)))
+    return;
+  static FILE *fp;
+  if (!omx_buffer)
+  {
+    if (fp)
+    {
+      fclose(fp);
+      fp = NULL;
+    }
+    return;
+  }
+  if (!fp)
+  {
+    char filename[1024];
+    strcpy(filename, g_advancedSettings.m_logFolder.c_str());
+    strcat(filename, "video.dat");
+#ifdef DEBUG_PLAYBACK
+    fp = fopen(filename, "rb");
+#else
+    fp = fopen(filename, "wb");
+#endif
+    }
+  if (fp)
+  {
+#ifdef DEBUG_PLAYBACK
+    OMX_BUFFERHEADERTYPE omx = {0};
+    int s = fread(&omx, sizeof omx, 1, fp);
+    omx_buffer->nFilledLen = omx.nFilledLen;
+    omx_buffer->nFlags = omx.nFlags;
+    omx_buffer->nTimeStamp = omx.nTimeStamp;
+    if (s==1)
+      fread(omx_buffer->pBuffer, omx_buffer->nFilledLen, 1, fp);
+#else
+    if (fwrite(omx_buffer, sizeof *omx_buffer, 1, fp) == 1)
+      fwrite(omx_buffer->pBuffer, omx_buffer->nFilledLen, 1, fp);
+#endif
+  }
+}
+
 COMXVideo::COMXVideo() : m_video_codec_name("")
 {
   m_is_open           = false;
@@ -118,6 +161,7 @@ bool COMXVideo::SendDecoderConfig()
     memcpy((unsigned char *)omx_buffer->pBuffer, m_extradata, omx_buffer->nFilledLen);
     omx_buffer->nFlags = OMX_BUFFERFLAG_CODECCONFIG | OMX_BUFFERFLAG_ENDOFFRAME;
   
+    dump_omx_buffer(omx_buffer);
     omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer);
     if (omx_err != OMX_ErrorNone)
     {
@@ -707,6 +751,7 @@ bool COMXVideo::Open(CDVDStreamInfo &hints, OMXClock *clock, EDEINTERLACEMODE de
 void COMXVideo::Close()
 {
   CSingleLock lock (m_critSection);
+  dump_omx_buffer(NULL);
   m_omx_tunnel_clock.Deestablish();
   m_omx_tunnel_decoder.Deestablish();
   if(m_deinterlace)
@@ -801,6 +846,7 @@ int COMXVideo::Decode(uint8_t *pData, int iSize, double pts)
       int nRetry = 0;
       while(true)
       {
+        dump_omx_buffer(omx_buffer);
         omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer);
         if (omx_err == OMX_ErrorNone)
         {
@@ -931,6 +977,7 @@ void COMXVideo::SubmitEOS()
 
   omx_buffer->nFlags = OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS | OMX_BUFFERFLAG_TIME_UNKNOWN;
   
+  dump_omx_buffer(omx_buffer);
   omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer);
   if (omx_err != OMX_ErrorNone)
   {
diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp
index c9f9c94..00d5e61 100644
--- a/xbmc/settings/AdvancedSettings.cpp
+++ b/xbmc/settings/AdvancedSettings.cpp
@@ -1391,6 +1391,10 @@ void CAdvancedSettings::SettingOptionsLoggingComponentsFiller(const CSetting *se
 #ifdef TARGET_RASPBERRY_PI
   list.push_back(std::make_pair(g_localizeStrings.Get(697), LOGOMXPLAYER));
 #endif
+#ifdef TARGET_RASPBERRY_PI
+  list.push_back(std::make_pair(g_localizeStrings.Get(698), LOGDUMPVIDEO));
+  list.push_back(std::make_pair(g_localizeStrings.Get(699), LOGDUMPAUDIO));
+#endif
 }
 
 void CAdvancedSettings::setExtraLogLevel(const std::vector<CVariant> &components)

From 83e540011c95472c0e41e9d3d9a69afb75a20472 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Wed, 1 Oct 2014 16:34:51 +0100
Subject: [PATCH 79/88] [mmalcodec] squash: Avoid preroll when using trickplay

---
 xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecMMAL.cpp |  5 +++++
 xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecMMAL.h   |  1 +
 xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp         | 12 +++++++++++-
 xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.h           |  2 ++
 4 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecMMAL.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecMMAL.cpp
index 55b9969..262283d 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecMMAL.cpp
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecMMAL.cpp
@@ -96,4 +96,9 @@ bool CDVDVideoCodecMMAL::GetCodecStats(double &pts, int &droppedPics)
   return m_decoder->GetCodecStats(pts, droppedPics);
 }
 
+void CDVDVideoCodecMMAL::SetSpeed(int iSpeed)
+{
+  m_decoder->SetSpeed(iSpeed);
+}
+
 #endif
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecMMAL.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecMMAL.h
index 67aa505..a768e70 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecMMAL.h
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecMMAL.h
@@ -42,6 +42,7 @@ class CDVDVideoCodecMMAL : public CDVDVideoCodec
   virtual void SetDropState(bool bDrop);
   virtual const char* GetName(void);
   virtual bool GetCodecStats(double &pts, int &droppedPics);
+  virtual void SetSpeed(int iSpeed);
 
 protected:
   MMALVideoPtr m_decoder;
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp
index c9aa390..cee1499 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp
@@ -137,6 +137,7 @@ CMMALVideo::CMMALVideo()
   m_demux_queue_length = 0;
   m_es_format = mmal_format_alloc();
   m_preroll = true;
+  m_speed = DVD_PLAYSPEED_NORMAL;
 }
 
 CMMALVideo::~CMMALVideo()
@@ -701,6 +702,7 @@ bool CMMALVideo::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options, MMALVide
   m_drop_state = false;
   m_startframe = false;
   m_preroll = !m_hints.stills;
+  m_speed = DVD_PLAYSPEED_NORMAL;
 
   return true;
 }
@@ -975,9 +977,17 @@ void CMMALVideo::Reset(void)
   m_decoderPts = DVD_NOPTS_VALUE;
   m_droppedPics = 0;
   m_decode_frame_number = 1;
-  m_preroll = !m_hints.stills;
+  m_preroll = !m_hints.stills && (m_speed == DVD_PLAYSPEED_NORMAL || m_speed == DVD_PLAYSPEED_PAUSE);
 }
 
+void CMMALVideo::SetSpeed(int iSpeed)
+{
+#if defined(MMAL_DEBUG_VERBOSE)
+  CLog::Log(LOGDEBUG, "%s::%s %d->%d", CLASSNAME, __func__, m_speed, iSpeed);
+#endif
+
+  m_speed = iSpeed;
+}
 
 void CMMALVideo::ReturnBuffer(CMMALVideoBuffer *buffer)
 {
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.h
index a2da46b..4f81bbd 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.h
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.h
@@ -90,6 +90,7 @@ class CMMALVideo
   virtual void SetDropState(bool bDrop);
   virtual const char* GetName(void) { return (const char*)m_pFormatName; }
   virtual bool GetCodecStats(double &pts, int &droppedPics);
+  virtual void SetSpeed(int iSpeed);
 
   // MMAL decoder callback routines.
   void ReleaseBuffer(CMMALVideoBuffer *buffer);
@@ -140,6 +141,7 @@ class CMMALVideo
   unsigned int      m_decode_frame_number;
   double            m_decoderPts;
   unsigned int      m_droppedPics;
+  int               m_speed;
   bool              m_preroll;
 
   MMAL_COMPONENT_T *m_dec;

From b1066eb16576a94e979243c89320e90246ca3b21 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Wed, 1 Oct 2014 22:45:09 +0100
Subject: [PATCH 80/88] [mmalrenderer] Add choice of 3 deinterlace schemes

---
 xbmc/cores/VideoRenderers/MMALRenderer.cpp         |  4 ++++
 xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp |  4 +++-
 xbmc/cores/dvdplayer/DVDPlayer.cpp                 |  5 ++++-
 xbmc/cores/dvdplayer/DVDPlayer.h                   |  1 +
 xbmc/cores/omxplayer/OMXVideo.cpp                  | 11 ++++++++---
 5 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/xbmc/cores/VideoRenderers/MMALRenderer.cpp b/xbmc/cores/VideoRenderers/MMALRenderer.cpp
index 5fb6dce..9cb4d60 100644
--- a/xbmc/cores/VideoRenderers/MMALRenderer.cpp
+++ b/xbmc/cores/VideoRenderers/MMALRenderer.cpp
@@ -502,6 +502,10 @@ bool CMMALRenderer::Supports(EINTERLACEMETHOD method)
 {
   if (method == VS_INTERLACEMETHOD_DEINTERLACE)
     return true;
+  if (method == VS_INTERLACEMETHOD_DEINTERLACE_HALF)
+    return true;
+  if (method == VS_INTERLACEMETHOD_RENDER_BOB)
+    return true;
 
   return false;
 }
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp
index cee1499..42c62db 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp
@@ -366,7 +366,9 @@ bool CMMALVideo::CreateDeinterlace(EINTERLACEMETHOD interlace_method)
     CLog::Log(LOGERROR, "%s::%s Failed to create deinterlace component (status=%x %s)", CLASSNAME, __func__, status, mmal_status_to_string(status));
     return false;
   }
-  MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {{MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param)}, MMAL_PARAM_IMAGEFX_DEINTERLACE_FAST, 1, {3}};
+  MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {{MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param)},
+        interlace_method == VS_INTERLACEMETHOD_DEINTERLACE ? MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV : MMAL_PARAM_IMAGEFX_DEINTERLACE_FAST,
+        3, {3, 0, interlace_method == VS_INTERLACEMETHOD_DEINTERLACE_HALF }};
   status = mmal_port_parameter_set(m_deint->output[0], &imfx_param.hdr);
   if (status != MMAL_SUCCESS)
   {
diff --git a/xbmc/cores/dvdplayer/DVDPlayer.cpp b/xbmc/cores/dvdplayer/DVDPlayer.cpp
index afdf74a..676b35270a 100644
--- a/xbmc/cores/dvdplayer/DVDPlayer.cpp
+++ b/xbmc/cores/dvdplayer/DVDPlayer.cpp
@@ -618,6 +618,7 @@ CDVDPlayer::CDVDPlayer(IPlayerCallback& callback)
   m_OmxPlayerState.bOmxSentEOFs        = false;
   m_OmxPlayerState.threshold           = 0.2f;
   m_OmxPlayerState.current_deinterlace = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
+  m_OmxPlayerState.interlace_method    = g_renderManager.AutoInterlaceMethod(CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod);
 #ifdef HAS_OMXPLAYER
   m_omxplayer_mode                     = CSettings::Get().GetBool("videoplayer.useomxplayer");
 #else
@@ -1123,13 +1124,15 @@ void CDVDPlayer::OMXDoProcessing()
     bool audio_fifo_low = false, video_fifo_low = false, audio_fifo_high = false, video_fifo_high = false;
 
     // if deinterlace setting has changed, we should close and open video
-    if (m_OmxPlayerState.current_deinterlace != CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode)
+    if (m_OmxPlayerState.current_deinterlace != CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode ||
+        m_OmxPlayerState.interlace_method    != g_renderManager.AutoInterlaceMethod(CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod))
     {
       CloseStream(m_CurrentVideo, false);
       OpenStream(m_CurrentVideo, m_CurrentVideo.id, m_CurrentVideo.source);
       if (m_State.canseek)
         m_messenger.Put(new CDVDMsgPlayerSeek(GetTime(), true, true, true, true, true));
       m_OmxPlayerState.current_deinterlace = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
+      m_OmxPlayerState.interlace_method    = g_renderManager.AutoInterlaceMethod(CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod);
     }
 
     m_OmxPlayerState.video_fifo = (int)(100.0*(m_dvdPlayerVideo->GetDecoderBufferSize()-m_dvdPlayerVideo->GetDecoderFreeSpace())/m_dvdPlayerVideo->GetDecoderBufferSize());
diff --git a/xbmc/cores/dvdplayer/DVDPlayer.h b/xbmc/cores/dvdplayer/DVDPlayer.h
index dac00e9..88f5cf8 100644
--- a/xbmc/cores/dvdplayer/DVDPlayer.h
+++ b/xbmc/cores/dvdplayer/DVDPlayer.h
@@ -541,6 +541,7 @@ class CDVDPlayer : public IPlayer, public CThread, public IDVDPlayer
   {
     OMXClock av_clock;              // openmax clock component
     EDEINTERLACEMODE current_deinterlace; // whether deinterlace is currently enabled
+    EINTERLACEMETHOD interlace_method; // current deinterlace method
     bool bOmxWaitVideo;             // whether we need to wait for video to play out on EOS
     bool bOmxWaitAudio;             // whether we need to wait for audio to play out on EOS
     bool bOmxSentEOFs;              // flag if we've send EOFs to audio/video players
diff --git a/xbmc/cores/omxplayer/OMXVideo.cpp b/xbmc/cores/omxplayer/OMXVideo.cpp
index 6cfb148..37765b1 100644
--- a/xbmc/cores/omxplayer/OMXVideo.cpp
+++ b/xbmc/cores/omxplayer/OMXVideo.cpp
@@ -30,6 +30,8 @@
 #include "linux/XMemUtils.h"
 #include "DVDDemuxers/DVDDemuxUtils.h"
 #include "settings/AdvancedSettings.h"
+#include "settings/MediaSettings.h"
+#include "cores/VideoRenderers/RenderManager.h"
 #include "xbmc/guilib/GraphicContext.h"
 #include "settings/Settings.h"
 #include "utils/BitstreamConverter.h"
@@ -298,8 +300,9 @@ bool COMXVideo::PortSettingsChanged()
 
   if(m_deinterlace)
   {
-    bool advanced_deinterlace = port_image.format.video.nFrameWidth * port_image.format.video.nFrameHeight <= 576 * 720;
-
+    EINTERLACEMETHOD interlace_method = g_renderManager.AutoInterlaceMethod(CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod);
+    bool advanced_deinterlace = interlace_method == VS_INTERLACEMETHOD_DEINTERLACE && port_image.format.video.nFrameWidth * port_image.format.video.nFrameHeight <= 576 * 720;
+    bool half_framerate = interlace_method == VS_INTERLACEMETHOD_DEINTERLACE_HALF;
     if (!advanced_deinterlace)
     {
       // Image_fx assumed 3 frames of context. simple deinterlace doesn't require this
@@ -319,8 +322,10 @@ bool COMXVideo::PortSettingsChanged()
     OMX_INIT_STRUCTURE(image_filter);
 
     image_filter.nPortIndex = m_omx_image_fx.GetOutputPort();
-    image_filter.nNumParams = 1;
+    image_filter.nNumParams = 3;
     image_filter.nParams[0] = 3;
+    image_filter.nParams[1] = 0;
+    image_filter.nParams[2] = half_framerate;
     if (!advanced_deinterlace)
       image_filter.eImageFilter = OMX_ImageFilterDeInterlaceFast;
     else

From ef73ce6916594207a6e910cb943d7f2973365df6 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Wed, 24 Sep 2014 23:13:52 +0100
Subject: [PATCH 81/88] [audio] Add settings option to boost centre channel
 when downmixing

This allows a dB volume increase to be added to centre channel.
This can help improve dialgue in the presence of background music/effects.
It can go up to 30dB for testing purposes, but value of 6 is probably more reasonable.
It is recommended to ensure "Normalise levels on downmix" is enabled when boosting by large values to avoid clipping.

Should work with Pi Sink (dvdplayer/paplayer) and omxplayer
---
 language/English/strings.po                                | 14 ++++++++++++++
 system/settings/settings.xml                               | 12 ++++++++++++
 .../Engines/ActiveAE/ActiveAEResampleFFMPEG.cpp            |  7 +++++++
 .../AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.cpp    |  6 ++++++
 xbmc/cores/omxplayer/OMXAudio.cpp                          |  6 ++++++
 5 files changed, 45 insertions(+)

diff --git a/language/English/strings.po b/language/English/strings.po
index 856d371..3da0a86 100755
--- a/language/English/strings.po
+++ b/language/English/strings.po
@@ -16024,3 +16024,17 @@ msgstr ""
 msgctxt "#38006"
 msgid "Adjust PLL"
 msgstr ""
+
+#: system/settings/settings.xml
+msgctxt "#38007"
+msgid "Boost centre channel when downmixing"
+msgstr ""
+
+#: system/settings/settings.xml
+msgctxt "#38008"
+msgid "Increase this value to make the dialogue louder compared to background sounds when downmixing multichannel audio"
+msgstr ""
+
+#: system/settings/settings.xml
+msgctxt "#38009"
+msgid "%i dB"
diff --git a/system/settings/settings.xml b/system/settings/settings.xml
index f4fcb7c..cd2bea6 100644
--- a/system/settings/settings.xml
+++ b/system/settings/settings.xml
@@ -2457,6 +2457,18 @@
           <default>true</default>
           <control type="toggle" />
         </setting>
+         <setting id="audiooutput.boostcenter" type="integer" label="38007" help="38008">
+          <level>2</level>
+          <default>0</default>
+          <constraints>
+            <minimum>0</minimum>
+            <step>1</step>
+            <maximum>30</maximum>
+          </constraints>
+          <control type="spinner" format="string">
+            <formatlabel>38009</formatlabel>
+          </control>
+        </setting>
         <setting id="audiooutput.processquality" type="integer" label="13505" help="36169">
           <requirement>HAS_AE_QUALITY_LEVELS</requirement>
           <level>2</level>
diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResampleFFMPEG.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResampleFFMPEG.cpp
index 26f0140..2e3ef91 100644
--- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResampleFFMPEG.cpp
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResampleFFMPEG.cpp
@@ -20,6 +20,7 @@
 
 #include "cores/AudioEngine/Utils/AEUtil.h"
 #include "ActiveAEResampleFFMPEG.h"
+#include "settings/Settings.h"
 #include "utils/log.h"
 
 extern "C" {
@@ -105,6 +106,12 @@ bool CActiveAEResampleFFMPEG::Init(uint64_t dst_chan_layout, int dst_channels, i
   {
      av_opt_set_double(m_pContext, "rematrix_maxval", 1.0, 0);
   }
+  int boost_center = CSettings::Get().GetInt("audiooutput.boostcenter");
+  if (boost_center)
+  {
+    float gain = pow(10.0f, ((float)(-3 + boost_center))/20.0f);
+    av_opt_set_double(m_pContext, "center_mix_level", gain, 0);
+  }
 
   if (remapLayout)
   {
diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.cpp
index 6565cdb..a50cd70 100644
--- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.cpp
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEResamplePi.cpp
@@ -160,6 +160,12 @@ bool CActiveAEResamplePi::Init(uint64_t dst_chan_layout, int dst_channels, int d
   {
     av_opt_set_double(m_pContext, "rematrix_maxval", 1.0, 0);
   }
+  int boost_center = CSettings::Get().GetInt("audiooutput.boostcenter");
+  if (boost_center)
+  {
+    float gain = pow(10.0f, ((float)(-3 + boost_center))/20.0f);
+    av_opt_set_double(m_pContext, "center_mix_level", gain, 0);
+  }
 
   if (remapLayout)
   {
diff --git a/xbmc/cores/omxplayer/OMXAudio.cpp b/xbmc/cores/omxplayer/OMXAudio.cpp
index 22ee423..db2968f 100644
--- a/xbmc/cores/omxplayer/OMXAudio.cpp
+++ b/xbmc/cores/omxplayer/OMXAudio.cpp
@@ -682,6 +682,12 @@ bool COMXAudio::Initialize(AEAudioFormat format, OMXClock *clock, CDVDStreamInfo
     {
        av_opt_set_double(m_pContext, "rematrix_maxval", 1.0, 0);
     }
+    int boost_center = CSettings::Get().GetInt("audiooutput.boostcenter");
+    if (boost_center)
+    {
+      float gain = pow(10.0f, ((float)(-3 + boost_center))/20.0f);
+      av_opt_set_double(m_pContext, "center_mix_level", gain, 0);
+    }
 
     // stereo upmix
     if (upmix && m_src_channels == 2 && m_dst_channels > 2)

From 923296874b60c37352c3605b7cd2b50530414a7a Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Fri, 3 Oct 2014 18:38:07 +0100
Subject: [PATCH 82/88] [omxcore] Skip out of flush on error

---
 xbmc/linux/OMXCore.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/xbmc/linux/OMXCore.cpp b/xbmc/linux/OMXCore.cpp
index 4ae29ba..4caa304 100644
--- a/xbmc/linux/OMXCore.cpp
+++ b/xbmc/linux/OMXCore.cpp
@@ -419,7 +419,7 @@ void COMXCoreComponent::FlushAll()
 
 void COMXCoreComponent::FlushInput()
 {
-  if(!m_handle)
+  if(!m_handle || m_resource_error)
     return;
 
   OMX_ERRORTYPE omx_err = OMX_ErrorNone;
@@ -436,7 +436,7 @@ void COMXCoreComponent::FlushInput()
 
 void COMXCoreComponent::FlushOutput()
 {
-  if(!m_handle)
+  if(!m_handle || m_resource_error)
     return;
 
   OMX_ERRORTYPE omx_err = OMX_ErrorNone;

From fc2f230c5a2bb3baef275f5d61e1a16cc49380e5 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Fri, 3 Oct 2014 18:40:06 +0100
Subject: [PATCH 83/88] [SinkPi] Handle multichannel layout more like OMXAudio

---
 xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp
index 89684e4..fd9a03d 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp
@@ -92,9 +92,9 @@ static void SetAudioProps(bool stream_channels, uint32_t channel_map)
   CLog::Log(LOGDEBUG, "%s:%s hdmi_stream_channels %d hdmi_channel_map %08x", CLASSNAME, __func__, stream_channels, channel_map);
 }
 
-static uint32_t GetChannelMap(AEAudioFormat &format, bool passthrough)
+static uint32_t GetChannelMap(const CAEChannelInfo &channelLayout, bool passthrough)
 {
-  unsigned int channels = format.m_channelLayout.Count();
+  unsigned int channels = channelLayout.Count();
   uint32_t channel_map = 0;
   if (passthrough)
     return 0;
@@ -133,12 +133,12 @@ static uint32_t GetChannelMap(AEAudioFormat &format, bool passthrough)
   // According to CEA-861-D only RL and RR are known. In case of a format having SL and SR channels
   // but no BR BL channels, we use the wide map in order to open only the num of channels really
   // needed.
-  if (format.m_channelLayout.HasChannel(AE_CH_BL) && !format.m_channelLayout.HasChannel(AE_CH_SL))
+  if (channelLayout.HasChannel(AE_CH_BL) && !channelLayout.HasChannel(AE_CH_SL))
     map = map_back;
 
   for (unsigned int i = 0; i < channels; ++i)
   {
-    AEChannel c = format.m_channelLayout[i];
+    AEChannel c = channelLayout[i];
     unsigned int chan = 0;
     if ((unsigned int)c < sizeof map_normal / sizeof *map_normal)
       chan = map[(unsigned int)c];
@@ -169,9 +169,9 @@ static uint32_t GetChannelMap(AEAudioFormat &format, bool passthrough)
     0xff, // 7
     0x13, // 7.1
   };
-  uint8_t cea = format.m_channelLayout.HasChannel(AE_CH_LFE) ? cea_map_lfe[channels] : cea_map[channels];
+  uint8_t cea = channelLayout.HasChannel(AE_CH_LFE) ? cea_map_lfe[channels] : cea_map[channels];
   if (cea == 0xff)
-    CLog::Log(LOGERROR, "%s::%s - Unexpected CEA mapping %d,%d", CLASSNAME, __func__, format.m_channelLayout.HasChannel(AE_CH_LFE), channels);
+    CLog::Log(LOGERROR, "%s::%s - Unexpected CEA mapping %d,%d", CLASSNAME, __func__, channelLayout.HasChannel(AE_CH_LFE), channels);
 
   channel_map |= cea << 24;
 
@@ -208,7 +208,7 @@ bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device)
   format.m_frames        = format.m_sampleRate * AUDIO_PLAYBUFFER / NUM_OMX_BUFFERS;
   format.m_frameSamples  = format.m_frames * channels;
 
-  SetAudioProps(m_passthrough, GetChannelMap(format, m_passthrough));
+  SetAudioProps(m_passthrough, GetChannelMap(format.m_channelLayout, m_passthrough));
 
   m_format = format;
   m_sinkbuffer_sec_per_byte = 1.0 / (double)(m_format.m_frameSize * m_format.m_sampleRate);

From d535a9edff8b08243aaf69ffb10a2b630a27aabd Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sun, 5 Oct 2014 14:05:25 +0100
Subject: [PATCH 84/88] [PiSink] Allow audio output latency to be set in
 settings

---
 language/English/strings.po               | 15 +++++++++++++++
 system/settings/rbp.xml                   | 12 ++++++++++++
 xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp | 13 ++++++++-----
 xbmc/cores/AudioEngine/Sinks/AESinkPi.h   |  1 +
 4 files changed, 36 insertions(+), 5 deletions(-)

diff --git a/language/English/strings.po b/language/English/strings.po
index 3da0a86..1bc647f 100755
--- a/language/English/strings.po
+++ b/language/English/strings.po
@@ -16038,3 +16038,18 @@ msgstr ""
 #: system/settings/settings.xml
 msgctxt "#38009"
 msgid "%i dB"
+
+#: system/settings/rbp.xml
+msgctxt "#38012"
+msgid "Audio output latency"
+msgstr ""
+
+#: system/settings/rbp.xml
+msgctxt "#38013"
+msgid "Increase this to protect against underruns, reduce to have lower latency gui sounds"
+msgstr ""
+
+#: system/settings/rbp.xml
+msgctxt "#38014"
+msgid "%i ms"
+msgstr ""
diff --git a/system/settings/rbp.xml b/system/settings/rbp.xml
index 79853a8..b8e6e59 100644
--- a/system/settings/rbp.xml
+++ b/system/settings/rbp.xml
@@ -103,6 +103,18 @@
         <setting id="audiooutput.processquality">
           <default>101</default> <!-- AE_QUALITY_GPU -->
         </setting>
+         <setting id="audiooutput.latency" type="integer" label="38012" help="38013">
+          <level>3</level>
+          <default>100</default>
+          <constraints>
+            <minimum>50</minimum>
+            <step>50</step>
+            <maximum>300</maximum>
+          </constraints>
+          <control type="spinner" format="string">
+            <formatlabel>38014</formatlabel>
+          </control>
+        </setting>
       </group>
       <group id="3">
         <setting id="audiooutput.ac3transcode" help="37024">
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp
index fd9a03d..484ce14 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp
@@ -34,7 +34,6 @@
 #define CLASSNAME "CAESinkPi"
 
 #define NUM_OMX_BUFFERS 2
-#define AUDIO_PLAYBUFFER (0.1) // 100ms
 
 static const unsigned int PassthroughSampleRates[] = { 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 88200, 96000, 176400, 192000 };
 
@@ -42,6 +41,7 @@ CAEDeviceInfo CAESinkPi::m_info;
 
 CAESinkPi::CAESinkPi() :
     m_sinkbuffer_sec_per_byte(0),
+    m_latency(0),
     m_Initialized(false),
     m_submitted(0),
     m_omx_output(NULL)
@@ -189,6 +189,9 @@ bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device)
   m_initDevice = device;
   m_initFormat = format;
 
+  m_latency = CSettings::Get().GetInt("audiooutput.latency") * 1e-3;
+  m_latency = std::max(m_latency, 50e-3);
+
   // analogue only supports stereo
   if (CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Analogue" || CSettings::Get().GetString("audiooutput.audiodevice") == "PI:Both")
   {
@@ -205,7 +208,7 @@ bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device)
   unsigned int sample_size = CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3;
   format.m_frameSize     = sample_size * channels;
   format.m_sampleRate    = std::max(8000U, std::min(192000U, format.m_sampleRate));
-  format.m_frames        = format.m_sampleRate * AUDIO_PLAYBUFFER / NUM_OMX_BUFFERS;
+  format.m_frames        = format.m_sampleRate * m_latency / NUM_OMX_BUFFERS;
   format.m_frameSamples  = format.m_frames * channels;
 
   SetAudioProps(m_passthrough, GetChannelMap(format.m_channelLayout, m_passthrough));
@@ -420,7 +423,7 @@ void CAESinkPi::GetDelay(AEDelayStatus& status)
 
 double CAESinkPi::GetCacheTotal()
 {
-  return AUDIO_PLAYBUFFER;
+  return m_latency;
 }
 
 unsigned int CAESinkPi::AddPackets(uint8_t **data, unsigned int frames, unsigned int offset)
@@ -468,8 +471,8 @@ unsigned int CAESinkPi::AddPackets(uint8_t **data, unsigned int frames, unsigned
   m_submitted++;
   GetDelay(status);
   delay = status.GetDelay();
-  if (delay > AUDIO_PLAYBUFFER)
-    Sleep((int)(1000.0f * (delay - AUDIO_PLAYBUFFER)));
+  if (delay > m_latency)
+    Sleep((int)(1000.0f * (delay - m_latency)));
   return frames;
 }
 
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkPi.h b/xbmc/cores/AudioEngine/Sinks/AESinkPi.h
index 5c57999..cac5051 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkPi.h
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkPi.h
@@ -53,6 +53,7 @@ class CAESinkPi : public IAESink
   AEAudioFormat        m_initFormat;
   AEAudioFormat        m_format;
   double               m_sinkbuffer_sec_per_byte;
+  double               m_latency;
   static CAEDeviceInfo m_info;
   bool                 m_Initialized;
   uint32_t             m_submitted;

From 65e9f504ac1fc8177430eacfb8af8edf117a315e Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Mon, 6 Oct 2014 21:29:41 +0100
Subject: [PATCH 85/88] [AE] Don't submit data to resampler when more than one
 frame is buffered

---
 xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp
index e40ac2e..9e42385 100644
--- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp
+++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp
@@ -258,7 +258,7 @@ bool CActiveAEBufferPoolResample::ResampleBuffers(int64_t timestamp)
 
     bool skipInput = false;
     // avoid that ffmpeg resample buffer grows too large
-    if (out_samples > free_samples * 2 && !m_empty)
+    if (out_samples > free_samples && !m_empty)
       skipInput = true;
 
     bool hasInput = !m_inputSamples.empty();

From 897693bca9cc30d2346ab65efe7677f18e52df24 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Mon, 13 Oct 2014 22:37:52 +0100
Subject: [PATCH 86/88] [rbp] Fix for orientation handling of Pi textures

There were a number of "off by one" errors in the orientations when using accelerated jpeg<->textures on the Pi.
This mostly worked out, but was the root cause of this issue:
http://forum.xbmc.org/showthread.php?tid=192380&pid=1805144#pid1805144
---
 xbmc/TextureCacheJob.cpp          |  2 +-
 xbmc/cores/omxplayer/OMXImage.cpp | 19 +++++++++++--------
 xbmc/cores/omxplayer/OMXImage.h   |  4 ++--
 xbmc/guilib/TexturePi.cpp         |  4 ++--
 4 files changed, 16 insertions(+), 13 deletions(-)

diff --git a/xbmc/TextureCacheJob.cpp b/xbmc/TextureCacheJob.cpp
index 2fdeef7..fe0904d 100644
--- a/xbmc/TextureCacheJob.cpp
+++ b/xbmc/TextureCacheJob.cpp
@@ -97,7 +97,7 @@ bool CTextureCacheJob::CacheTexture(CBaseTexture **out_texture)
     m_details.height = height;
     m_details.file = m_cachePath + ".jpg";
     if (out_texture)
-      *out_texture = LoadImage(CTextureCache::GetCachedPath(m_details.file), width, height, additional_info);
+      *out_texture = LoadImage(CTextureCache::GetCachedPath(m_details.file), width, height, "" /* already flipped */);
     CLog::Log(LOGDEBUG, "Fast %s image '%s' to '%s': %p", m_oldHash.empty() ? "Caching" : "Recaching", image.c_str(), m_details.file.c_str(), out_texture);
     return true;
   }
diff --git a/xbmc/cores/omxplayer/OMXImage.cpp b/xbmc/cores/omxplayer/OMXImage.cpp
index 57f69c5..22ec3f0 100644
--- a/xbmc/cores/omxplayer/OMXImage.cpp
+++ b/xbmc/cores/omxplayer/OMXImage.cpp
@@ -206,7 +206,8 @@ bool COMXImage::CreateThumb(const std::string& srcFile, unsigned int maxHeight,
   COMXImageReEnc reenc;
   void *pDestBuffer;
   unsigned int nDestSize;
-  if (URIUtils::HasExtension(srcFile, ".jpg|.tbn") && file.ReadFile(srcFile) && reenc.ReEncode(file, maxWidth, maxHeight, pDestBuffer, nDestSize))
+  int orientation = additional_info == "flipped" ? 1:0;
+  if (URIUtils::HasExtension(srcFile, ".jpg|.tbn") && file.ReadFile(srcFile, orientation) && reenc.ReEncode(file, maxWidth, maxHeight, pDestBuffer, nDestSize))
   {
     XFILE::CFile outfile;
     if (outfile.OpenForWrite(destFile, true))
@@ -592,7 +593,7 @@ static void inline SKIPN(uint8_t * &p, unsigned int n)
   p += n;
 }
 
-OMX_IMAGE_CODINGTYPE COMXImageFile::GetCodingType(unsigned int &width, unsigned int &height)
+OMX_IMAGE_CODINGTYPE COMXImageFile::GetCodingType(unsigned int &width, unsigned int &height, int orientation)
 {
   OMX_IMAGE_CODINGTYPE eCompressionFormat = OMX_IMAGE_CodingMax;
   bool progressive = false;
@@ -809,7 +810,7 @@ OMX_IMAGE_CODINGTYPE COMXImageFile::GetCodingType(unsigned int &width, unsigned
                 {
                   SKIPN(p, 1 * 7);
                   readBits += 7;
-                  m_orientation = READ8(p);
+                  m_orientation = READ8(p)-1;
                   readBits += 1;
                   SKIPN(p, 1 * 2);
                   readBits += 2;
@@ -818,7 +819,7 @@ OMX_IMAGE_CODINGTYPE COMXImageFile::GetCodingType(unsigned int &width, unsigned
                 {
                   SKIPN(p, 1 * 6);
                   readBits += 6;
-                  m_orientation = READ8(p);
+                  m_orientation = READ8(p)-1;
                   readBits += 1;
                   SKIPN(p, 1 * 3);
                   readBits += 3;
@@ -848,7 +849,9 @@ OMX_IMAGE_CODINGTYPE COMXImageFile::GetCodingType(unsigned int &width, unsigned
     }
   }
 
-  if(m_orientation > 8)
+  // apply input orientation
+  m_orientation = m_orientation ^ orientation;
+  if(m_orientation < 0 || m_orientation >= 8)
     m_orientation = 0;
 
   if(eCompressionFormat == OMX_IMAGE_CodingMax)
@@ -872,7 +875,7 @@ OMX_IMAGE_CODINGTYPE COMXImageFile::GetCodingType(unsigned int &width, unsigned
 }
 
 
-bool COMXImageFile::ReadFile(const std::string& inputFile)
+bool COMXImageFile::ReadFile(const std::string& inputFile, int orientation)
 {
   XFILE::CFile      m_pFile;
   m_filename = inputFile.c_str();
@@ -903,7 +906,7 @@ bool COMXImageFile::ReadFile(const std::string& inputFile)
   m_pFile.Read(m_image_buffer, m_image_size);
   m_pFile.Close();
 
-  OMX_IMAGE_CODINGTYPE eCompressionFormat = GetCodingType(m_width, m_height);
+  OMX_IMAGE_CODINGTYPE eCompressionFormat = GetCodingType(m_width, m_height, orientation);
   if(eCompressionFormat != OMX_IMAGE_CodingJPEG)
   {
     CLog::Log(LOGERROR, "%s::%s %s GetCodingType=0x%x\n", CLASSNAME, __func__, inputFile.c_str(), eCompressionFormat);
@@ -1659,7 +1662,7 @@ bool COMXImageReEnc::HandlePortSettingChange(unsigned int resize_width, unsigned
       item.metadata.eValueCharset = OMX_MetadataCharsetASCII;
       item.metadata.sLanguageCountry = 0;
       item.metadata.nValueMaxSize = sizeof(item.metadata_space);
-      sprintf((char *)item.metadata.nValue, "%d", orientation);
+      sprintf((char *)item.metadata.nValue, "%d", orientation + 1);
       item.metadata.nValueSizeUsed = strlen((char *)item.metadata.nValue);
 
       omx_err = m_omx_encoder.SetParameter(OMX_IndexConfigMetadataItem, &item);
diff --git a/xbmc/cores/omxplayer/OMXImage.h b/xbmc/cores/omxplayer/OMXImage.h
index 90e513a..a93aa82 100644
--- a/xbmc/cores/omxplayer/OMXImage.h
+++ b/xbmc/cores/omxplayer/OMXImage.h
@@ -94,7 +94,7 @@ class COMXImageFile
 public:
   COMXImageFile();
   virtual ~COMXImageFile();
-  bool ReadFile(const std::string& inputFile);
+  bool ReadFile(const std::string& inputFile, int orientation = 0);
   int  GetOrientation() { return m_orientation; };
   unsigned int GetWidth()  { return m_width; };
   unsigned int GetHeight() { return m_height; };
@@ -102,7 +102,7 @@ class COMXImageFile
   const uint8_t *GetImageBuffer() { return (const uint8_t *)m_image_buffer; };
   const char *GetFilename() { return m_filename; };
 protected:
-  OMX_IMAGE_CODINGTYPE GetCodingType(unsigned int &width, unsigned int &height);
+  OMX_IMAGE_CODINGTYPE GetCodingType(unsigned int &width, unsigned int &height, int orientation);
   uint8_t           *m_image_buffer;
   unsigned long     m_image_size;
   unsigned int      m_width;
diff --git a/xbmc/guilib/TexturePi.cpp b/xbmc/guilib/TexturePi.cpp
index 3950b3f..a17bfba 100644
--- a/xbmc/guilib/TexturePi.cpp
+++ b/xbmc/guilib/TexturePi.cpp
@@ -142,8 +142,8 @@ bool CPiTexture::LoadFromFileInternal(const CStdString& texturePath, unsigned in
       if (okay)
       {
         m_hasAlpha = false;
-        if (autoRotate && orientation)
-          m_orientation = orientation - 1;
+        if (autoRotate)
+          m_orientation = orientation;
         return true;
       }
     }

From 7ce67eff4bd7884f01df5018c6db57c32a74ee4f Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sat, 18 Oct 2014 14:41:39 +0100
Subject: [PATCH 87/88] [omxplayer] Restore flush/accurate seek flags to fix
 issue with edl skipping

---
 xbmc/cores/dvdplayer/DVDPlayer.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/xbmc/cores/dvdplayer/DVDPlayer.cpp b/xbmc/cores/dvdplayer/DVDPlayer.cpp
index 676b35270a..c3f9b6a 100644
--- a/xbmc/cores/dvdplayer/DVDPlayer.cpp
+++ b/xbmc/cores/dvdplayer/DVDPlayer.cpp
@@ -2176,7 +2176,7 @@ void CDVDPlayer::CheckAutoSceneSkip()
     /*
      * Seeking is NOT flushed so any content up to the demux point is retained when playing forwards.
      */
-    m_messenger.Put(new CDVDMsgPlayerSeek(seek, true, false, true, false, true));
+    m_messenger.Put(new CDVDMsgPlayerSeek(seek, true, m_omxplayer_mode, true, false, true));
     /*
      * Seek doesn't always work reliably. Last physical seek time is recorded to prevent looping
      * if there was an error with seeking and it landed somewhere unexpected, perhaps back in the
@@ -2194,7 +2194,7 @@ void CDVDPlayer::CheckAutoSceneSkip()
     /*
      * Seeking is NOT flushed so any content up to the demux point is retained when playing forwards.
      */
-    m_messenger.Put(new CDVDMsgPlayerSeek(cut.end + 1, true, false, true, false, true));
+    m_messenger.Put(new CDVDMsgPlayerSeek(cut.end + 1, true, m_omxplayer_mode, true, false, true));
     /*
      * Each commercial break is only skipped once so poorly detected commercial breaks can be
      * manually re-entered. Start and end are recorded to prevent looping and to allow seeking back
@@ -3479,7 +3479,7 @@ void CDVDPlayer::UpdateClockMaster()
 void CDVDPlayer::FlushBuffers(bool queued, double pts, bool accurate)
 {
   double startpts;
-  if(accurate)
+  if(accurate && !m_omxplayer_mode)
     startpts = pts;
   else
     startpts = DVD_NOPTS_VALUE;

From 469f6cd8487d2af728d76bacd8a20bca730d17b6 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sat, 18 Oct 2014 18:13:43 +0100
Subject: [PATCH 88/88] [mmal] Exit preroll when we have some decoded frames

---
 xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp
index 42c62db..0498ec7 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/MMALCodec.cpp
@@ -925,6 +925,10 @@ int CMMALVideo::Decode(uint8_t* pData, int iSize, double dts, double pts)
   }
   else
     m_preroll = false;
+
+  if (m_preroll && m_output_ready.size() >= GetAllowedReferences())
+    m_preroll = false;
+
   if (!m_output_ready.empty() && !m_preroll)
   {
     #if defined(MMAL_DEBUG_VERBOSE)
