Sophie

Sophie

distrib > Mageia > 7 > i586 > media > core-updates-src > by-pkgid > 363766992745b0b5168847be83af34b6 > files > 4

mythtv-30.0-20200418.1.mga7.src.rpm

diff --git a/mythplugins/configure b/mythplugins/configure
index bbeda10f4c..697e4df4bd 100755
--- a/mythplugins/configure
+++ b/mythplugins/configure
@@ -647,7 +647,11 @@ if ! check_lib libexif/exif-data.h exif_loader_new -lexif ; then
 fi
 
 if enabled game; then
-    if ! check_lib minizip/unzip.h unzGetCurrentFileInfo -lminizip ; then
+    if check_lib unzip.h unzGetCurrentFileInfo -lminizip ; then
+        MINIZIP_PREFIX=
+    elif check_lib minizip/unzip.h unzGetCurrentFileInfo -lminizip ; then
+        MINIZIP_PREFIX="minizip/"
+    else
         echo 'minizip required to compile mythgame'
         disable game
     fi
@@ -943,6 +947,21 @@ if enabled gallery ; then
     fi
 fi
 
+###########################################################
+#                                                         #
+#   MythGame related configuration options                #
+#                                                         #
+###########################################################
+
+if enabled game ; then
+
+    echo "/*" >  ./mythgame/mythgame/config.h
+    echo "    Automatically generated by configure - do not modify" >> ./mythgame/mythgame/config.h
+    echo "*/" >> ./mythgame/mythgame/config.h
+    echo "#define MINIZIP_UNZIP_H \"${MINIZIP_PREFIX}unzip.h\"" >> ./mythgame/mythgame/config.h
+
+fi
+
 ###########################################################
 #                                                         #
 #  MythMusic related configuration options                #
diff --git a/mythplugins/mytharchive/mytharchivehelper/main.cpp b/mythplugins/mytharchive/mytharchivehelper/main.cpp
index 13c7032e62..1960e0b925 100644
--- a/mythplugins/mytharchive/mytharchivehelper/main.cpp
+++ b/mythplugins/mytharchive/mytharchivehelper/main.cpp
@@ -524,7 +524,7 @@ int NativeArchive::exportRecording(QDomElement   &itemNode,
     }
 
     // add any rating
-    query.prepare("SELECT system, rating FROM recordedrating "
+    query.prepare("SELECT `system`, rating FROM recordedrating "
             "WHERE chanid = :CHANID AND starttime = :STARTTIME;");
     query.bindValue(":CHANID", chanID);
     query.bindValue(":STARTTIME", startTime);
diff --git a/mythplugins/mythgame/mythgame/dbcheck.cpp b/mythplugins/mythgame/mythgame/dbcheck.cpp
index a787feb821..6b9952a8e3 100644
--- a/mythplugins/mythgame/mythgame/dbcheck.cpp
+++ b/mythplugins/mythgame/mythgame/dbcheck.cpp
@@ -70,7 +70,7 @@ static bool InitializeDatabase(void)
 
     const QString updates[] = {
 "CREATE TABLE gamemetadata ("
-"  system varchar(128) NOT NULL default '',"
+"  `system` varchar(128) NOT NULL default '',"
 "  romname varchar(128) NOT NULL default '',"
 "  gamename varchar(128) NOT NULL default '',"
 "  genre varchar(128) NOT NULL default '',"
@@ -84,7 +84,7 @@ static bool InitializeDatabase(void)
 "  crc_value varchar(64) NOT NULL default '',"
 "  display tinyint(1) NOT NULL default '1',"
 "  version varchar(64) NOT NULL default '',"
-"  KEY system (system),"
+"  KEY `system` (`system`),"
 "  KEY year (year),"
 "  KEY romname (romname),"
 "  KEY gamename (gamename),"
@@ -298,7 +298,7 @@ bool UpgradeGameDatabaseSchema(void)
 QString("ALTER DATABASE %1 DEFAULT CHARACTER SET latin1;")
         .arg(gContext->GetDatabaseParams().dbName),
 "ALTER TABLE gamemetadata"
-"  MODIFY system varbinary(128) NOT NULL default '',"
+"  MODIFY `system` varbinary(128) NOT NULL default '',"
 "  MODIFY romname varbinary(128) NOT NULL default '',"
 "  MODIFY gamename varbinary(128) NOT NULL default '',"
 "  MODIFY genre varbinary(128) NOT NULL default '',"
@@ -345,7 +345,7 @@ QString("ALTER DATABASE %1 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;")
         .arg(gContext->GetDatabaseParams().dbName),
 "ALTER TABLE gamemetadata"
 "  DEFAULT CHARACTER SET default,"
-"  MODIFY system varchar(128) CHARACTER SET utf8 NOT NULL default '',"
+"  MODIFY `system` varchar(128) CHARACTER SET utf8 NOT NULL default '',"
 "  MODIFY romname varchar(128) CHARACTER SET utf8 NOT NULL default '',"
 "  MODIFY gamename varchar(128) CHARACTER SET utf8 NOT NULL default '',"
 "  MODIFY genre varchar(128) CHARACTER SET utf8 NOT NULL default '',"
diff --git a/mythplugins/mythgame/mythgame/gamehandler.cpp b/mythplugins/mythgame/mythgame/gamehandler.cpp
index 9d5539e3f1..565662970f 100644
--- a/mythplugins/mythgame/mythgame/gamehandler.cpp
+++ b/mythplugins/mythgame/mythgame/gamehandler.cpp
@@ -243,7 +243,7 @@ static void updateDisplayRom(QString romname, int display, QString Systemname)
 {
     MSqlQuery query(MSqlQuery::InitCon());
     query.prepare("UPDATE gamemetadata SET display = :DISPLAY "
-                  "WHERE romname = :ROMNAME AND system = :SYSTEM");
+                  "WHERE romname = :ROMNAME AND `system` = :SYSTEM");
 
     query.bindValue(":DISPLAY", display);
     query.bindValue(":ROMNAME", romname);
@@ -273,7 +273,7 @@ static void updateGameName(QString romname, QString GameName, QString Systemname
 {
     MSqlQuery query(MSqlQuery::InitCon());
     query.prepare("UPDATE gamemetadata SET GameName = :GAMENAME "
-                  "WHERE romname = :ROMNAME AND system = :SYSTEM ");
+                  "WHERE romname = :ROMNAME AND `system` = :SYSTEM ");
 
     query.bindValue(":GAMENAME", GameName);
     query.bindValue(":ROMNAME", romname);
@@ -300,10 +300,10 @@ static void UpdateGameCounts(QStringList updatelist)
         LOG(VB_GENERAL, LOG_NOTICE,
             LOC + QString("Update gametype %1").arg(GameType));
 
-        query.prepare("SELECT romname,system,spandisks,gamename FROM "
+        query.prepare("SELECT romname,`system`,spandisks,gamename FROM "
               "gamemetadata,gameplayers WHERE "
               "gamemetadata.gametype = :GAMETYPE AND "
-              "playername = system ORDER BY romname");
+              "playername = `system` ORDER BY romname");
 
         query.bindValue(":GAMETYPE",GameType);
 
@@ -450,7 +450,7 @@ void GameHandler::UpdateGameDB(GameHandler *handler)
 #endif
 
             query.prepare("INSERT INTO gamemetadata "
-                          "(system, romname, gamename, genre, year, gametype, "
+                          "(`system`, romname, gamename, genre, year, gametype, "
                           "rompath, country, crc_value, diskcount, display, plot, "
                           "publisher, version, fanart, boxart, screenshot) "
                           "VALUES (:SYSTEM, :ROMNAME, :GAMENAME, :GENRE, :YEAR, "
@@ -501,7 +501,7 @@ void GameHandler::VerifyGameDB(GameHandler *handler)
 
     MSqlQuery query(MSqlQuery::InitCon());
     query.prepare("SELECT romname,rompath,gamename FROM gamemetadata "
-                  "WHERE system = :SYSTEM");
+                  "WHERE `system` = :SYSTEM");
 
     query.bindValue(":SYSTEM",handler->SystemName());
 
diff --git a/mythplugins/mythgame/mythgame/rom_metadata.cpp b/mythplugins/mythgame/mythgame/rom_metadata.cpp
index b9838769c3..9bc8ed61ee 100644
--- a/mythplugins/mythgame/mythgame/rom_metadata.cpp
+++ b/mythplugins/mythgame/mythgame/rom_metadata.cpp
@@ -1,10 +1,12 @@
+#include "config.h"
 #include "rom_metadata.h"
 
 #include <QFile>
 
 #include <mythcontext.h>
 
-#include <minizip/unzip.h>
+#include "zlib.h"
+#include MINIZIP_UNZIP_H
 #undef Z_NULL
 #define Z_NULL nullptr
 
diff --git a/mythplugins/mythgame/mythgame/rominfo.cpp b/mythplugins/mythgame/mythgame/rominfo.cpp
index 53490b0054..0743c88fad 100644
--- a/mythplugins/mythgame/mythgame/rominfo.cpp
+++ b/mythplugins/mythgame/mythgame/rominfo.cpp
@@ -31,7 +31,7 @@ void RomInfo::SaveToDatabase()
             .arg(Romname()));
 
         query.prepare("INSERT INTO gamemetadata "
-                      "(system, romname, gamename, genre, year, gametype, "
+                      "(`system`, romname, gamename, genre, year, gametype, "
                       "rompath, country, crc_value, diskcount, display, plot, "
                       "publisher, version, fanart, boxart, screenshot) "
                       "VALUES (:SYSTEM, :ROMNAME, :GAMENAME, :GENRE, :YEAR, "
@@ -257,10 +257,10 @@ void RomInfo::fillData()
 
     QString systemtype;
     if (system != "") {
-        systemtype  += " AND system = :SYSTEM ";
+        systemtype  += " AND `system` = :SYSTEM ";
     }
 
-    QString thequery = "SELECT system,gamename,genre,year,romname,favorite,"
+    QString thequery = "SELECT `system`,gamename,genre,year,romname,favorite,"
                        "rompath,country,crc_value,diskcount,gametype,plot,publisher,"
                        "version,screenshot,fanart,boxart,inetref,intid FROM gamemetadata "
                        "WHERE gamename = :GAMENAME "
@@ -299,7 +299,7 @@ void RomInfo::fillData()
     // systems available to play it.
     if (RomCount() > 1)
     {
-        query.prepare("SELECT DISTINCT system FROM gamemetadata "
+        query.prepare("SELECT DISTINCT `system` FROM gamemetadata "
                       "WHERE romname = :ROMNAME");
         query.bindValue(":ROMNAME", Romname());
         if (!query.exec())
@@ -325,7 +325,7 @@ QList<RomInfo*> RomInfo::GetAllRomInfo()
 
     MSqlQuery query(MSqlQuery::InitCon());
 
-    QString querystr = "SELECT intid,system,romname,gamename,genre,year,publisher,"
+    QString querystr = "SELECT intid,`system`,romname,gamename,genre,year,publisher,"
                        "favorite,rompath,screenshot,fanart,plot,boxart,"
                        "gametype,diskcount,country,crc_value,inetref,display,"
                        "version FROM gamemetadata ORDER BY diskcount DESC";
@@ -373,7 +373,7 @@ RomInfo *RomInfo::GetRomInfoById(int id)
 
     MSqlQuery query(MSqlQuery::InitCon());
 
-    QString querystr = "SELECT intid,system,romname,gamename,genre,year,publisher,"
+    QString querystr = "SELECT intid,`system`,romname,gamename,genre,year,publisher,"
                        "favorite,rompath,screenshot,fanart,plot,boxart,"
                        "gametype,diskcount,country,crc_value,inetref,display,"
                        "version FROM gamemetadata WHERE intid = :INTID";
diff --git a/mythplugins/mythweather/mythweather/scripts/no_yrno/yrnoxml.pl b/mythplugins/mythweather/mythweather/scripts/no_yrno/yrnoxml.pl
index b66376c080..d17c8e0cc0 100755
--- a/mythplugins/mythweather/mythweather/scripts/no_yrno/yrnoxml.pl
+++ b/mythplugins/mythweather/mythweather/scripts/no_yrno/yrnoxml.pl
@@ -5,7 +5,7 @@ use strict;
 use warnings;
 
 use utf8;
-use encoding 'utf8';
+
 use LWP::UserAgent;
 use Getopt::Std;
 use URI::Escape;
diff --git a/mythplugins/mythweather/mythweather/scripts/uk_metoffice/MetOffCommon.pm b/mythplugins/mythweather/mythweather/scripts/uk_metoffice/MetOffCommon.pm
index 43eb2a9909..099a4e6bb9 100644
--- a/mythplugins/mythweather/mythweather/scripts/uk_metoffice/MetOffCommon.pm
+++ b/mythplugins/mythweather/mythweather/scripts/uk_metoffice/MetOffCommon.pm
@@ -7,7 +7,7 @@ use warnings;
 require Exporter;
 
 use utf8;
-use encoding 'utf8';
+
 use LWP::UserAgent;
 use LWP::Simple;
 use XML::Simple;
diff --git a/mythplugins/mythweather/mythweather/scripts/uk_metoffice/metoffice_fivedayapi.pl b/mythplugins/mythweather/mythweather/scripts/uk_metoffice/metoffice_fivedayapi.pl
index 853d900e1c..ee4ef9ec2b 100755
--- a/mythplugins/mythweather/mythweather/scripts/uk_metoffice/metoffice_fivedayapi.pl
+++ b/mythplugins/mythweather/mythweather/scripts/uk_metoffice/metoffice_fivedayapi.pl
@@ -5,7 +5,7 @@ use strict;
 use warnings;
 
 use utf8;
-use encoding 'utf8';
+
 use English;
 
 use File::Basename;
diff --git a/mythplugins/mythweather/mythweather/scripts/uk_metoffice/metoffice_threehourapi.pl b/mythplugins/mythweather/mythweather/scripts/uk_metoffice/metoffice_threehourapi.pl
index 9ec77f8964..6c566207c2 100755
--- a/mythplugins/mythweather/mythweather/scripts/uk_metoffice/metoffice_threehourapi.pl
+++ b/mythplugins/mythweather/mythweather/scripts/uk_metoffice/metoffice_threehourapi.pl
@@ -5,7 +5,7 @@ use strict;
 use warnings;
 
 use utf8;
-use encoding 'utf8';
+
 use English;
 
 use File::Basename;
diff --git a/mythplugins/mythweather/mythweather/scripts/wunderground/wunderground-animaps.pl b/mythplugins/mythweather/mythweather/scripts/wunderground/wunderground-animaps.pl
index 382dda730b..5cb74644fc 100755
--- a/mythplugins/mythweather/mythweather/scripts/wunderground/wunderground-animaps.pl
+++ b/mythplugins/mythweather/mythweather/scripts/wunderground/wunderground-animaps.pl
@@ -13,7 +13,7 @@ use lib dirname(abs_path($0 or $PROGRAM_NAME)),
         '/usr/local/share/mythtv/mythweather/scripts/wunderground';
 
 use utf8;
-use encoding 'utf8';
+
 use LWP::UserAgent;
 use Getopt::Std;
 use URI::Escape;
diff --git a/mythplugins/mythweather/mythweather/scripts/wunderground/wunderground-maps.pl b/mythplugins/mythweather/mythweather/scripts/wunderground/wunderground-maps.pl
index b7797b5e49..9fb3561620 100755
--- a/mythplugins/mythweather/mythweather/scripts/wunderground/wunderground-maps.pl
+++ b/mythplugins/mythweather/mythweather/scripts/wunderground/wunderground-maps.pl
@@ -13,7 +13,7 @@ use lib dirname(abs_path($0 or $PROGRAM_NAME)),
         '/usr/local/share/mythtv/mythweather/scripts/wunderground';
 
 use utf8;
-use encoding 'utf8';
+
 use LWP::UserAgent;
 use Getopt::Std;
 use URI::Escape;
diff --git a/mythplugins/mythweather/mythweather/scripts/wunderground/wunderground.pl b/mythplugins/mythweather/mythweather/scripts/wunderground/wunderground.pl
index 2ce69c250e..c9a7896894 100755
--- a/mythplugins/mythweather/mythweather/scripts/wunderground/wunderground.pl
+++ b/mythplugins/mythweather/mythweather/scripts/wunderground/wunderground.pl
@@ -5,7 +5,7 @@ use strict;
 use warnings;
 
 use utf8;
-use encoding 'utf8';
+
 use LWP::UserAgent;
 use Getopt::Std;
 use URI::Escape;
diff --git a/mythplugins/mythzoneminder/mythzmserver/zmserver.cpp b/mythplugins/mythzoneminder/mythzmserver/zmserver.cpp
index eefe9fbbbe..3c21697c5f 100644
--- a/mythplugins/mythzoneminder/mythzmserver/zmserver.cpp
+++ b/mythplugins/mythzoneminder/mythzmserver/zmserver.cpp
@@ -175,6 +175,12 @@ void loadZMConfig(const string &configfile)
     fclose(cfg);
 }
 
+#if !defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 80000
+using reconnect_t = int;
+#else
+using reconnect_t = my_bool;
+#endif
+
 void connectToDatabase(void)
 {
     if (!mysql_init(&g_dbConn))
@@ -183,7 +189,7 @@ void connectToDatabase(void)
         exit(mysql_errno(&g_dbConn));
     }
 
-    my_bool reconnect = 1;
+    reconnect_t reconnect = 1;
     mysql_options(&g_dbConn, MYSQL_OPT_RECONNECT, &reconnect);
 
     if (!mysql_real_connect(&g_dbConn, g_server.c_str(), g_user.c_str(),
@@ -241,6 +247,9 @@ void MONITOR::initMonitor(bool debug, const string &mmapPath, int shmKey)
     int shared_data_size;
     int frame_size = width * height * bytes_per_pixel;
 
+    if (!enabled)
+        return;
+
     if (checkVersion(1, 26, 0))
     {
         shared_data_size = sizeof(SharedData26) +
@@ -956,7 +965,7 @@ void ZMServer::handleGetMonitorStatus(void)
             string id = row[0];
             string type = row[2];
             string device = row[3];
-            string host = row[4];
+            string host = row[4] ? row[4] : "";
             string channel = row[5];
             string function = row[6];
             string enabled = row[7];
@@ -1675,7 +1684,7 @@ void ZMServer::getMonitorList(void)
             m->function = row[8];
             m->enabled = atoi(row[9]);
             m->device = row[10];
-            m->host = row[11];
+            m->host = row[11] ? row[11] : "";
             m->controllable = atoi(row[12]);
             m->trackMotion = atoi(row[13]);
 
diff --git a/mythplugins/mythzoneminder/mythzoneminder/zmclient.cpp b/mythplugins/mythzoneminder/mythzoneminder/zmclient.cpp
index 0a99c93ac9..068a2dc0c9 100644
--- a/mythplugins/mythzoneminder/mythzoneminder/zmclient.cpp
+++ b/mythplugins/mythzoneminder/mythzoneminder/zmclient.cpp
@@ -26,6 +26,7 @@
 ZMClient::ZMClient()
     : QObject(nullptr),
       m_listLock(QMutex::Recursive),
+      m_commandLock(QMutex::Recursive),
       m_socket(nullptr),
       m_socketLock(QMutex::Recursive),
       m_hostname("localhost"),
@@ -135,8 +136,6 @@ bool ZMClient::connectToHost(const QString &lhostname, unsigned int lport)
 
 bool ZMClient::sendReceiveStringList(QStringList &strList)
 {
-    QMutexLocker locker(&m_socketLock);
-
     QStringList origStrList = strList;
 
     bool ok = false;
@@ -196,6 +195,8 @@ bool ZMClient::sendReceiveStringList(QStringList &strList)
 
 bool ZMClient::checkProtoVersion(void)
 {
+    QMutexLocker locker(&m_commandLock);
+
     QStringList strList("HELLO");
     if (!sendReceiveStringList(strList))
     {
@@ -272,6 +273,8 @@ ZMClient::~ZMClient()
 
 void ZMClient::getServerStatus(QString &status, QString &cpuStat, QString &diskStat)
 {
+    QMutexLocker locker(&m_commandLock);
+
     QStringList strList("GET_SERVER_STATUS");
     if (!sendReceiveStringList(strList))
         return;
@@ -290,6 +293,8 @@ void ZMClient::getServerStatus(QString &status, QString &cpuStat, QString &diskS
 
 void ZMClient::updateMonitorStatus(void)
 {
+    QMutexLocker clocker(&m_commandLock);
+
     QStringList strList("GET_MONITOR_STATUS");
     if (!sendReceiveStringList(strList))
         return;
@@ -359,6 +364,8 @@ static QString stateToString(State state)
 
 bool ZMClient::updateAlarmStates(void)
 {
+    QMutexLocker clocker(&m_commandLock);
+
     QStringList strList("GET_ALARM_STATES");
     if (!sendReceiveStringList(strList))
         return false;
@@ -410,6 +417,8 @@ void ZMClient::getEventList(const QString &monitorName, bool oldestFirst,
                             const QString &date, bool includeContinuous,
                             vector<Event*> *eventList)
 {
+    QMutexLocker locker(&m_commandLock);
+
     eventList->clear();
 
     QStringList strList("GET_EVENT_LIST");
@@ -462,6 +471,8 @@ void ZMClient::getEventList(const QString &monitorName, bool oldestFirst,
 void ZMClient::getEventDates(const QString &monitorName, bool oldestFirst,
                             QStringList &dateList)
 {
+    QMutexLocker locker(&m_commandLock);
+
     dateList.clear();
 
     QStringList strList("GET_EVENT_DATES");
@@ -505,6 +516,8 @@ void ZMClient::getEventDates(const QString &monitorName, bool oldestFirst,
 
 void ZMClient::getFrameList(int eventID, vector<Frame*> *frameList)
 {
+    QMutexLocker locker(&m_commandLock);
+
     frameList->clear();
 
     QStringList strList("GET_FRAME_LIST");
@@ -549,6 +562,8 @@ void ZMClient::getFrameList(int eventID, vector<Frame*> *frameList)
 
 void ZMClient::deleteEvent(int eventID)
 {
+    QMutexLocker locker(&m_commandLock);
+
     QStringList strList("DELETE_EVENT");
     strList << QString::number(eventID);
     sendReceiveStringList(strList);
@@ -556,6 +571,8 @@ void ZMClient::deleteEvent(int eventID)
 
 void ZMClient::deleteEventList(vector<Event*> *eventList)
 {
+    QMutexLocker locker(&m_commandLock);
+
     // delete events in 100 event chunks
     QStringList strList("DELETE_EVENT_LIST");
     int count = 0;
@@ -641,6 +658,8 @@ bool ZMClient::readData(unsigned char *data, int dataSize)
 
 void ZMClient::getEventFrame(Event *event, int frameNo, MythImage **image)
 {
+    QMutexLocker locker(&m_commandLock);
+
     if (*image)
     {
         (*image)->DecrRef();
@@ -690,6 +709,8 @@ void ZMClient::getEventFrame(Event *event, int frameNo, MythImage **image)
 
 void ZMClient::getAnalyseFrame(Event *event, int frameNo, QImage &image)
 {
+    QMutexLocker locker(&m_commandLock);
+
     QStringList strList("GET_ANALYSE_FRAME");
     strList << QString::number(event->monitorID());
     strList << QString::number(event->eventID());
@@ -735,6 +756,8 @@ void ZMClient::getAnalyseFrame(Event *event, int frameNo, QImage &image)
 
 int ZMClient::getLiveFrame(int monitorID, QString &status, unsigned char* buffer, int bufferSize)
 {
+    QMutexLocker locker(&m_commandLock);
+
     QStringList strList("GET_LIVE_FRAME");
     strList << QString::number(monitorID);
     if (!sendReceiveStringList(strList))
@@ -794,6 +817,8 @@ int ZMClient::getLiveFrame(int monitorID, QString &status, unsigned char* buffer
 
 void ZMClient::getCameraList(QStringList &cameraList)
 {
+    QMutexLocker locker(&m_commandLock);
+
     cameraList.clear();
 
     QStringList strList("GET_CAMERA_LIST");
@@ -860,6 +885,7 @@ Monitor* ZMClient::getMonitorByID(int monID)
 
 void ZMClient::doGetMonitorList(void)
 {
+    QMutexLocker clocker(&m_commandLock);
     QMutexLocker locker(&m_listLock);
 
     for (int x = 0; x < m_monitorList.count(); x++)
@@ -926,6 +952,8 @@ void ZMClient::doGetMonitorList(void)
 
 void ZMClient::setMonitorFunction(const int monitorID, const QString &function, const int enabled)
 {
+    QMutexLocker locker(&m_commandLock);
+
     QStringList strList("SET_MONITOR_FUNCTION");
     strList << QString::number(monitorID);
     strList << function;
@@ -942,10 +970,13 @@ void ZMClient::saveNotificationMonitors(void)
     for (int x = 0; x < m_monitorList.count(); x++)
     {
         Monitor *mon = m_monitorList.at(x);
-        if (!s.isEmpty())
-            s += QString(",%1").arg(mon->id);
-        else
-            s = QString("%1").arg(mon->id);
+        if (mon->showNotifications)
+        {
+            if (!s.isEmpty())
+                s += QString(",%1").arg(mon->id);
+            else
+                s = QString("%1").arg(mon->id);
+        }
     }
 
     gCoreContext->SaveSetting("ZoneMinderNotificationMonitors", s);
diff --git a/mythplugins/mythzoneminder/mythzoneminder/zmclient.h b/mythplugins/mythzoneminder/mythzoneminder/zmclient.h
index 3dd5c20faa..75369f0f92 100644
--- a/mythplugins/mythzoneminder/mythzoneminder/zmclient.h
+++ b/mythplugins/mythzoneminder/mythzoneminder/zmclient.h
@@ -76,6 +76,8 @@ class MPUBLIC ZMClient : public QObject
     bool sendReceiveStringList(QStringList &strList);
 
     QMutex              m_listLock;
+    QMutex              m_commandLock;
+
     QList<Monitor*>     m_monitorList;
     QMap<int, Monitor*> m_monitorMap;
 
diff --git a/mythtv/FAQ b/mythtv/FAQ
index 593d3b9fea..e4f6c45109 100644
--- a/mythtv/FAQ
+++ b/mythtv/FAQ
@@ -1,5 +1,5 @@
                               MythTV FAQ
 
 The FAQ is available on the MythTV wiki at
-http://www.mythtv.org/wiki/Frequently_Asked_Questions
+https://www.mythtv.org/wiki/Frequently_Asked_Questions
 
diff --git a/mythtv/bindings/php/MythBackend.php b/mythtv/bindings/php/MythBackend.php
index 54472e9489..a703620982 100644
--- a/mythtv/bindings/php/MythBackend.php
+++ b/mythtv/bindings/php/MythBackend.php
@@ -191,7 +191,7 @@ class MythBackend {
     // Parse the records, starting at the offset point
         $row = 0;
         $col = 0;
-        $count = count($records);
+        $count = (is_array($records) ? count($records) : 0);
         for($i = $offset; $i < $count; $i++) {
             $rows[$row][$col] = $records[$i];
         // Every $NUMPROGRAMLINES fields (0 through ($NUMPROGRAMLINES-1)) means
diff --git a/mythtv/bindings/python/MythTV/services_api/send.py b/mythtv/bindings/python/MythTV/services_api/send.py
index 0d5946a8c0..4c30aec8ca 100644
--- a/mythtv/bindings/python/MythTV/services_api/send.py
+++ b/mythtv/bindings/python/MythTV/services_api/send.py
@@ -397,8 +397,6 @@ class Send(object):
 
         # TODO: Problem with the BE not accepting postdata in the initial
         # authorized query, Send a GET first as a workaround.
-        #
-        # Looks like a bug, Myth/version works for the backend.
 
         try:
             if self.opts['user'] and self.opts['pass']:
@@ -407,7 +405,11 @@ class Send(object):
                 if self.postdata:
                     saved_endpoint = self.endpoint
                     saved_postdata = self.postdata
-                    self.send(endpoint='Myth/version', opts=self.opts)
+                    # Need to adjust this if a service other than Frontend is
+                    # added.
+                    self.send(endpoint='{}/version'.format(
+                        'Myth' if self.endpoint[:8] != 'Frontend'
+                        else 'Frontend'), opts=self.opts)
                     self.endpoint = saved_endpoint
                     self.postdata = saved_postdata
         except KeyError:
diff --git a/mythtv/configure b/mythtv/configure
index a80828efc7..4f526864a5 100755
--- a/mythtv/configure
+++ b/mythtv/configure
@@ -6651,6 +6651,14 @@ if enabled x11; then
 			# This is hopefully temporary.
 			disable xnvctrl_external
 			;;
+                    *gentoo*)
+                        if [ -e "/usr/bin/nvidia-settings" ]
+                        then
+                            require XNVctrl "X11/Xlib.h NVCtrl/NVCtrl.h NVCtrl/NVCtrlLib.h" XNVCTRLIsNvScreen -lXNVCtrl || disable xnvctrl
+                        else
+                            disable xnvctrl_external
+                        fi
+                        ;;
 		    *)
                         require XNVctrl "X11/Xlib.h NVCtrl/NVCtrl.h NVCtrl/NVCtrlLib.h" XNVCTRLIsNvScreen -lXNVCtrl || disable xnvctrl
                         ;;
diff --git a/mythtv/database/mc.sql b/mythtv/database/mc.sql
index 58fcd36a1d..a583cb9525 100644
--- a/mythtv/database/mc.sql
+++ b/mythtv/database/mc.sql
@@ -1,6 +1,7 @@
 CREATE DATABASE IF NOT EXISTS mythconverg;
-GRANT ALL ON mythconverg.* TO mythtv@localhost IDENTIFIED BY "mythtv";
+CREATE USER IF NOT EXISTS 'mythtv'@'localhost' IDENTIFIED BY 'mythtv';
+GRANT ALL ON mythconverg.* TO mythtv@localhost;
 FLUSH PRIVILEGES;
-GRANT CREATE TEMPORARY TABLES ON mythconverg.* TO mythtv@localhost IDENTIFIED BY "mythtv";
+GRANT CREATE TEMPORARY TABLES ON mythconverg.* TO mythtv@localhost;
 FLUSH PRIVILEGES;
 ALTER DATABASE mythconverg DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
diff --git a/mythtv/html/tv/programsearch.qsp b/mythtv/html/tv/programsearch.qsp
index cfce0300ca..6441e769a8 100644
--- a/mythtv/html/tv/programsearch.qsp
+++ b/mythtv/html/tv/programsearch.qsp
@@ -76,7 +76,7 @@ import "/tv/js/tvutil.qjs"
     var Count = 30;
     var Details = 1;
     var progLoadTimeStart = new Date();
-    var Channels = channel.GetChannelInfoList(0,0,0,1).ChannelInfos;
+    var Channels = channel.GetChannelInfoList(0,0,0,0,1).ChannelInfos;
     var programList = Array();
     var programs = Array();
 
diff --git a/mythtv/libs/libmythbase/logging.cpp b/mythtv/libs/libmythbase/logging.cpp
index 92de0eb539..e127f40ae4 100644
--- a/mythtv/libs/libmythbase/logging.cpp
+++ b/mythtv/libs/libmythbase/logging.cpp
@@ -428,6 +428,7 @@ bool LoggerThread::logConsole(LoggingItem *item)
 
     item->IncrRef();
 
+#ifndef Q_OS_ANDROID
     if (item->m_type & kStandardIO)
         snprintf( line, MAX_STRING_LENGTH, "%s", item->m_message );
     else
@@ -462,11 +463,42 @@ bool LoggerThread::logConsole(LoggingItem *item)
 #endif
     }
 
-#ifdef Q_OS_ANDROID
-    __android_log_print(ANDROID_LOG_INFO, "mfe", line);
-#else
     int result = write( 1, line, strlen(line) );
     (void)result;
+
+#else // Q_OS_ANDROID
+
+    android_LogPriority aprio;
+    switch (item->m_level)
+    {
+    case LOG_EMERG:
+        aprio = ANDROID_LOG_FATAL;
+    case LOG_ALERT:
+    case LOG_CRIT:
+    case LOG_ERR:
+        aprio = ANDROID_LOG_ERROR;
+        break;
+    case LOG_WARNING:
+        aprio = ANDROID_LOG_WARN;
+        break;
+    case LOG_NOTICE:
+    case LOG_INFO:
+        aprio = ANDROID_LOG_INFO;
+        break;
+    case LOG_DEBUG:
+        aprio = ANDROID_LOG_DEBUG;
+        break;
+    case LOG_UNKNOWN:
+    default:
+        aprio = ANDROID_LOG_UNKNOWN;
+        break;
+    }
+#if CONFIG_DEBUGTYPE
+    __android_log_print(aprio, "mfe", "%s:%d:%s  %s", item->m_file,
+                        item->m_line, item->m_function, item->m_message);
+#else
+    __android_log_print(aprio, "mfe", "%s", item->m_message);
+#endif
 #endif
 
     item->DecrRef();
diff --git a/mythtv/libs/libmythbase/mythsocket.cpp b/mythtv/libs/libmythbase/mythsocket.cpp
index 69cba58448..61fbba0f66 100644
--- a/mythtv/libs/libmythbase/mythsocket.cpp
+++ b/mythtv/libs/libmythbase/mythsocket.cpp
@@ -490,7 +490,7 @@ bool MythSocket::Announce(const QStringList &new_announce)
     WriteStringList(new_announce);
 
     QStringList tmplist;
-    if (!ReadStringList(tmplist, true))
+    if (!ReadStringList(tmplist, MythSocket::kShortTimeout))
     {
         LOG(VB_GENERAL, LOG_ERR, LOC +
             QString("\n\t\t\tCould not read string list from server %1:%2")
diff --git a/mythtv/libs/libmythbase/remotefile.cpp b/mythtv/libs/libmythbase/remotefile.cpp
index 3bbfcff034..d3ee79895c 100644
--- a/mythtv/libs/libmythbase/remotefile.cpp
+++ b/mythtv/libs/libmythbase/remotefile.cpp
@@ -1073,7 +1073,19 @@ int RemoteFile::Read(void *data, int size)
 
     if (error || sent != recv)
     {
+        LOG(VB_GENERAL, LOG_WARNING,
+            QString("RemoteFile::Read(): sent %1 != recv %2")
+            .arg(sent).arg(recv));
         recv = -1;
+
+        // The TCP socket is dropped if there's a timeout, so we reconnect
+        if (!Resume())
+        {
+            LOG(VB_GENERAL, LOG_WARNING, "RemoteFile::Read(): Resume failed.");
+            sent = -1;
+        }
+        else
+            LOG(VB_GENERAL, LOG_NOTICE, "RemoteFile::Read(): Resume success.");
     }
     else
     {
diff --git a/mythtv/libs/libmythbase/storagegroup.cpp b/mythtv/libs/libmythbase/storagegroup.cpp
index 1349f00f7f..15ff64c468 100644
--- a/mythtv/libs/libmythbase/storagegroup.cpp
+++ b/mythtv/libs/libmythbase/storagegroup.cpp
@@ -615,7 +615,7 @@ QString StorageGroup::FindFile(const QString &filename)
     if (!recDir.isEmpty())
     {
         result = recDir + "/" + filename;
-        LOG(VB_FILE, LOG_DEBUG, LOC +
+        LOG(VB_FILE, LOG_INFO, LOC +
             QString("FindFile: Found '%1'") .arg(result));
     }
     else
diff --git a/mythtv/libs/libmythbase/test/test_mythsystem/test_mythsystem.h b/mythtv/libs/libmythbase/test/test_mythsystem/test_mythsystem.h
index 37de6be88c..0dd793042c 100644
--- a/mythtv/libs/libmythbase/test/test_mythsystem/test_mythsystem.h
+++ b/mythtv/libs/libmythbase/test/test_mythsystem/test_mythsystem.h
@@ -83,7 +83,8 @@ class TestMythSystem: public QObject
     {
         QScopedPointer<MythSystem> cmd(
             MythSystem::Create(
-                QString("echo %1").arg(__FUNCTION__), kMSStdOut));
+                QString("echo %1; sleep 1").arg(__FUNCTION__),
+                kMSStdOut | kMSRunShell));
         cmd->Wait();
         QVERIFY(QString(cmd->GetStandardOutputStream()->readAll())
                 .contains(__FUNCTION__));
@@ -128,8 +129,8 @@ class TestMythSystem: public QObject
     void stdout_works(void)
     {
         QScopedPointer<MythSystem> cmd(
-            MythSystem::Create(QString("echo %1").arg(__FUNCTION__),
-                               kMSStdOut));
+            MythSystem::Create(QString("echo %1; sleep 1").arg(__FUNCTION__),
+                               kMSStdOut | kMSRunShell));
         cmd->Wait();
         QVERIFY(cmd->GetExitCode() == 0);
         QVERIFY(cmd->GetStandardOutputStream());
@@ -142,7 +143,7 @@ class TestMythSystem: public QObject
     void stderr_works(void)
     {
         QScopedPointer<MythSystem> cmd(
-            MythSystem::Create(QString("echo %1 >&2").arg(__FUNCTION__),
+            MythSystem::Create(QString("echo %1 >&2; sleep 1").arg(__FUNCTION__),
                                kMSRunShell | kMSStdErr));
         cmd->Wait();
         QVERIFY(cmd->GetExitCode() == 0);
@@ -156,7 +157,7 @@ class TestMythSystem: public QObject
     void shell_used_when_requested(void)
     {
         QScopedPointer<MythSystem> cmd(
-            MythSystem::Create("if [ x != y ] ; then echo X ; else echo Y ; fi",
+            MythSystem::Create("if [ x != y ] ; then echo X ; else echo Y ; fi; sleep 1",
                                kMSRunShell | kMSStdOut));
         cmd->Wait();
         QVERIFY(QString(cmd->GetStandardOutputStream()->readAll())
@@ -166,7 +167,7 @@ class TestMythSystem: public QObject
     void shell_not_used_when_not_requested(void)
     {
         QScopedPointer<MythSystem> cmd(
-            MythSystem::Create("if [ x != y ] ; then echo X ; else echo Y ; fi",
+            MythSystem::Create("if [ x != y ] ; then echo X ; else echo Y ; fi; sleep 1",
                                kMSStdOut));
         cmd->Wait();
         QVERIFY(!QString(cmd->GetStandardOutputStream()->readAll())
diff --git a/mythtv/libs/libmythbase/test/test_mythsystemlegacy/test_mythsystemlegacy.h b/mythtv/libs/libmythbase/test/test_mythsystemlegacy/test_mythsystemlegacy.h
index 53a4601989..51e669eb8b 100644
--- a/mythtv/libs/libmythbase/test/test_mythsystemlegacy/test_mythsystemlegacy.h
+++ b/mythtv/libs/libmythbase/test/test_mythsystemlegacy/test_mythsystemlegacy.h
@@ -88,7 +88,8 @@ class TestMythSystemLegacy: public QObject
 
     void constructed_command_is_run(void)
     {
-        MythSystemLegacy cmd(QString("echo %1").arg(__FUNCTION__), kMSStdOut);
+        MythSystemLegacy cmd(QString("echo %1; sleep 1").arg(__FUNCTION__),
+                             kMSStdOut | kMSRunShell);
         Go(cmd);
         QVERIFY(QString(cmd.ReadAll()).contains(__FUNCTION__));
     }
@@ -139,7 +140,8 @@ class TestMythSystemLegacy: public QObject
     // kMSStdOut             -- allow access to stdout
     void stdout_works(void)
     {
-        MythSystemLegacy cmd(QString("echo %1").arg(__FUNCTION__), kMSStdOut);
+        MythSystemLegacy cmd(QString("echo %1; sleep 1").arg(__FUNCTION__),
+                             kMSStdOut | kMSRunShell);
         Go(cmd);
         QVERIFY(cmd.GetStatus() == 0);
         QVERIFY(QString(cmd.ReadAll()).contains(__FUNCTION__));
@@ -148,7 +150,7 @@ class TestMythSystemLegacy: public QObject
     // kMSStdErr             -- allow access to stderr
     void stderr_works(void)
     {
-        MythSystemLegacy cmd(QString("echo %1 >&2").arg(__FUNCTION__),
+        MythSystemLegacy cmd(QString("echo %1 >&2; sleep 1").arg(__FUNCTION__),
                        kMSRunShell | kMSStdErr);
         Go(cmd);
         QVERIFY(cmd.GetStatus() == 0);
@@ -158,7 +160,7 @@ class TestMythSystemLegacy: public QObject
     // kMSRunShell           -- run process through shell
     void shell_used_when_requested(void)
     {
-        MythSystemLegacy cmd("if [ x != y ] ; then echo X ; else echo Y ; fi",
+        MythSystemLegacy cmd("if [ x != y ] ; then echo X ; else echo Y ; fi; sleep 1",
                        kMSRunShell | kMSStdOut);
         Go(cmd);
         QVERIFY(QString(cmd.ReadAll()).contains("X"));
@@ -166,7 +168,7 @@ class TestMythSystemLegacy: public QObject
 
     void shell_not_used_when_not_requested(void)
     {
-        MythSystemLegacy cmd("if [ x != y ] ; then echo X ; else echo Y ; fi",
+        MythSystemLegacy cmd("if [ x != y ] ; then echo X ; else echo Y ; fi; sleep 1",
                        kMSStdOut);
         Go(cmd);
         QVERIFY(!QString(cmd.ReadAll()).contains("X"));
diff --git a/mythtv/libs/libmythbase/test/test_mythtimer/test_mythtimer.h b/mythtv/libs/libmythbase/test/test_mythtimer/test_mythtimer.h
index 2c4dddfeca..6d0b8705c7 100644
--- a/mythtv/libs/libmythbase/test/test_mythtimer/test_mythtimer.h
+++ b/mythtv/libs/libmythbase/test/test_mythtimer/test_mythtimer.h
@@ -47,76 +47,76 @@ class TestMythTimer: public QObject
     {
         MythTimer t;
         t.start();
-        std::this_thread::sleep_for(std::chrono::milliseconds(52));
-        QVERIFY(t.elapsed() > 50);
+        std::this_thread::sleep_for(std::chrono::milliseconds(520));
+        QVERIFY(t.elapsed() > 500);
     }
 
     void TimeElapsesAfterRestart(void)
     {
         MythTimer t;
         t.restart();
-        std::this_thread::sleep_for(std::chrono::milliseconds(52));
-        QVERIFY(t.elapsed() > 50);
+        std::this_thread::sleep_for(std::chrono::milliseconds(520));
+        QVERIFY(t.elapsed() > 500);
     }
 
     void TimeDoesNotElapseImmediatelyAfterConstructionByDefault(void)
     {
         MythTimer t;
-        std::this_thread::sleep_for(std::chrono::milliseconds(52));
+        std::this_thread::sleep_for(std::chrono::milliseconds(520));
         QVERIFY(t.elapsed() == 0);
     }
 
     void TimeDoesNotElapsesImmediatelyAfterContructionIfIntended(void)
     {
         MythTimer t(MythTimer::kStartRunning);
-        std::this_thread::sleep_for(std::chrono::milliseconds(52));
-        QVERIFY(t.elapsed() > 50);
+        std::this_thread::sleep_for(std::chrono::milliseconds(520));
+        QVERIFY(t.elapsed() > 500);
     }
 
     void TimeElapsesContinually(void)
     {
         MythTimer t;
         t.start();
-        std::this_thread::sleep_for(std::chrono::milliseconds(52));
-        QVERIFY(t.elapsed() > 50);
-        std::this_thread::sleep_for(std::chrono::milliseconds(50));
-        QVERIFY(t.elapsed() > 100);
+        std::this_thread::sleep_for(std::chrono::milliseconds(520));
+        QVERIFY(t.elapsed() > 500);
+        std::this_thread::sleep_for(std::chrono::milliseconds(500));
+        QVERIFY(t.elapsed() > 1000);
     }
 
     void TimeResetsOnRestart(void)
     {
         MythTimer t;
         t.start();
-        std::this_thread::sleep_for(std::chrono::milliseconds(52));
-        QVERIFY(t.restart() > 50);
-        std::this_thread::sleep_for(std::chrono::milliseconds(52));
-        QVERIFY(t.elapsed() > 50 && t.elapsed() < 75);
+        std::this_thread::sleep_for(std::chrono::milliseconds(520));
+        QVERIFY(t.restart() > 500);
+        std::this_thread::sleep_for(std::chrono::milliseconds(520));
+        QVERIFY(t.elapsed() > 500 && t.elapsed() < 750);
     }
 
     void AddMSecsWorks(void)
     {
         MythTimer t;
         t.start();
-        t.addMSecs(-25);
-        std::this_thread::sleep_for(std::chrono::milliseconds(52));
-        QVERIFY(t.elapsed() > 25 && t.elapsed() < 50);
+        t.addMSecs(-250);
+        std::this_thread::sleep_for(std::chrono::milliseconds(520));
+        QVERIFY(t.elapsed() > 250 && t.elapsed() < 500);
     }
 
     void AddMSecsIsResetOnStart(void)
     {
         MythTimer t;
-        t.addMSecs(-25);
+        t.addMSecs(-250);
         t.start();
-        std::this_thread::sleep_for(std::chrono::milliseconds(52));
-        QVERIFY(t.elapsed() > 50);
+        std::this_thread::sleep_for(std::chrono::milliseconds(520));
+        QVERIFY(t.elapsed() > 500);
     }
 
     void AddMSecsIsResetOnRestart(void)
     {
         MythTimer t;
-        t.addMSecs(-25);
+        t.addMSecs(-250);
         t.restart();
-        std::this_thread::sleep_for(std::chrono::milliseconds(52));
-        QVERIFY(t.elapsed() > 50);
+        std::this_thread::sleep_for(std::chrono::milliseconds(520));
+        QVERIFY(t.elapsed() > 500);
     }
 };
diff --git a/mythtv/libs/libmythmetadata/imagemetadata.cpp b/mythtv/libs/libmythmetadata/imagemetadata.cpp
index f1a57d4f8e..b7c71d2ed8 100644
--- a/mythtv/libs/libmythmetadata/imagemetadata.cpp
+++ b/mythtv/libs/libmythmetadata/imagemetadata.cpp
@@ -7,14 +7,7 @@
 #include "exitcodes.h"        // for ffprobe
 
 // libexiv2 for Exif metadata
-//#include <exiv2/exiv2.hpp>
-// Note: Older versions of Exiv2 don't have the exiv2.hpp include
-// file.  Using image.hpp instead seems to work.
-#ifdef _MSC_VER
-#include <exiv2/src/image.hpp>
-#else
-#include <exiv2/image.hpp>
-#endif
+#include <exiv2/exiv2.hpp>
 
 // To read FFMPEG Metadata
 extern "C" {
diff --git a/mythtv/libs/libmythmetadata/metaioflacvorbis.cpp b/mythtv/libs/libmythmetadata/metaioflacvorbis.cpp
index 3ac353dbc4..8452ffe71f 100644
--- a/mythtv/libs/libmythmetadata/metaioflacvorbis.cpp
+++ b/mythtv/libs/libmythmetadata/metaioflacvorbis.cpp
@@ -198,10 +198,9 @@ TagLib::FLAC::Picture *MetaIOFLACVorbis::getPictureFromFile(
         // From what I can tell, FLAC::File maintains ownership of the Picture pointers, so no need to delete
         const TagLib::List<Picture *>& picList = flacfile->pictureList();
 
-        for (TagLib::List<Picture *>::ConstIterator it = picList.begin();
-                it != picList.end(); it++)
+        for (auto it = picList.begin(); it != picList.end(); it++)
         {
-            if (pic->type() == artType)
+            if ((*it)->type() == artType)
             {
                 //found the type we were looking for
                 pic = *it;
diff --git a/mythtv/libs/libmythprotoserver/requesthandler/fileserverutil.cpp b/mythtv/libs/libmythprotoserver/requesthandler/fileserverutil.cpp
index 3e990f019d..bb79ae3e32 100644
--- a/mythtv/libs/libmythprotoserver/requesthandler/fileserverutil.cpp
+++ b/mythtv/libs/libmythprotoserver/requesthandler/fileserverutil.cpp
@@ -39,11 +39,11 @@ void DeleteHandler::Close(void)
     m_fd = -1;
 }
 
-QMap <QString, QString> recordingPathCache;
+static QMap <QString, QString> recordingPathCache;
+static QMutex recordingPathLock;
 
 QString GetPlaybackURL(ProgramInfo *pginfo, bool storePath)
 {
-    static QMutex recordingPathLock;
     QString result = "";
     QMutexLocker locker(&recordingPathLock);
     QString cacheKey = QString("%1:%2").arg(pginfo->GetChanID())
@@ -57,7 +57,9 @@ QString GetPlaybackURL(ProgramInfo *pginfo, bool storePath)
     }
     else
     {
+        locker.unlock();
         result = pginfo->GetPlaybackURL(false, true);
+        locker.relock();
         if (storePath && result.startsWith("/"))
             recordingPathCache[cacheKey] = result;
     }
diff --git a/mythtv/libs/libmythservicecontracts/service.h b/mythtv/libs/libmythservicecontracts/service.h
index b87995d94a..fed7eb3047 100644
--- a/mythtv/libs/libmythservicecontracts/service.h
+++ b/mythtv/libs/libmythservicecontracts/service.h
@@ -71,7 +71,10 @@ class SERVICE_PUBLIC Service : public QObject
 //
 //////////////////////////////////////////////////////////////////////////////
 
-inline Service::Service(QObject *parent) : QObject(parent) {}
+inline Service::Service(QObject *parent) : QObject(parent)
+{
+     qRegisterMetaType< QFileInfo >();
+}
 
 //////////////////////////////////////////////////////////////////////////////
 //
diff --git a/mythtv/libs/libmythservicecontracts/services/mythServices.h b/mythtv/libs/libmythservicecontracts/services/mythServices.h
index 9c2f7f505e..b4fc7834d4 100644
--- a/mythtv/libs/libmythservicecontracts/services/mythServices.h
+++ b/mythtv/libs/libmythservicecontracts/services/mythServices.h
@@ -54,6 +54,7 @@ class SERVICE_PUBLIC MythServices : public Service  //, public QScriptable ???
     Q_CLASSINFO( "SendNotification_Method",      "POST" )
     Q_CLASSINFO( "BackupDatabase_Method",        "POST" )
     Q_CLASSINFO( "CheckDatabase_Method",         "POST" )
+    Q_CLASSINFO( "DelayShutdown_Method",         "POST" )
     Q_CLASSINFO( "ProfileSubmit_Method",         "POST" )
     Q_CLASSINFO( "ProfileDelete_Method",         "POST" )
     Q_CLASSINFO( "ManageDigestUser_Method",      "POST" )
@@ -164,6 +165,8 @@ class SERVICE_PUBLIC MythServices : public Service  //, public QScriptable ???
 
         virtual bool                CheckDatabase       ( bool Repair ) = 0;
 
+        virtual bool                DelayShutdown       ( void ) = 0;
+
         virtual bool                ProfileSubmit       ( void ) = 0;
 
         virtual bool                ProfileDelete       ( void ) = 0;
diff --git a/mythtv/libs/libmythtv/Bluray/bdringbuffer.cpp b/mythtv/libs/libmythtv/Bluray/bdringbuffer.cpp
index 06258070e1..d7e4707d40 100644
--- a/mythtv/libs/libmythtv/Bluray/bdringbuffer.cpp
+++ b/mythtv/libs/libmythtv/Bluray/bdringbuffer.cpp
@@ -142,7 +142,7 @@ static int _img_read(void *handle, void *buf, int lba, int num_blocks)
 {
     int result = -1;
 
-    if (mythfile_seek(*((int*)handle), lba * 2048, SEEK_SET) != -1)
+    if (mythfile_seek(*((int*)handle), lba * 2048LL, SEEK_SET) != -1)
         result = mythfile_read(*((int*)handle), buf, num_blocks * 2048) / 2048;
 
     return result;
diff --git a/mythtv/libs/libmythtv/HLS/httplivestreambuffer.cpp b/mythtv/libs/libmythtv/HLS/httplivestreambuffer.cpp
index 6067814cce..43a0e9d219 100644
--- a/mythtv/libs/libmythtv/HLS/httplivestreambuffer.cpp
+++ b/mythtv/libs/libmythtv/HLS/httplivestreambuffer.cpp
@@ -342,6 +342,7 @@ public:
         {
             LOG(VB_PLAYBACK, LOG_ERR, LOC +
                 QString("bad padding character (0x%1)").arg(pad, 0, 16, QLatin1Char('0')));
+            delete[] decrypted_data;
             return RET_ERROR;
         }
         aeslen = m_data.size() - pad;
diff --git a/mythtv/libs/libmythtv/avformatdecoder.cpp b/mythtv/libs/libmythtv/avformatdecoder.cpp
index b25516c795..b97ff0e4be 100644
--- a/mythtv/libs/libmythtv/avformatdecoder.cpp
+++ b/mythtv/libs/libmythtv/avformatdecoder.cpp
@@ -1459,7 +1459,6 @@ float AvFormatDecoder::normalized_fps(AVStream *stream, AVCodecContext *enc)
             QString("Selected FPS is %1 (avg %2 codec %3 "
                     "container %4 estimated %5)").arg(fps).arg(avg_fps)
                 .arg(codec_fps).arg(container_fps).arg(estimated_fps));
-        m_fps = fps;
     }
 
     return fps;
@@ -1685,6 +1684,7 @@ void AvFormatDecoder::InitVideoCodec(AVStream *stream, AVCodecContext *enc,
     {
         enc->get_buffer2     = get_avf_buffer_vaapi2;
         enc->get_format      = get_format_vaapi2;
+        enc->slice_flags     = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
     }
     else
 #endif
diff --git a/mythtv/libs/libmythtv/cc708decoder.cpp b/mythtv/libs/libmythtv/cc708decoder.cpp
index 718bdbeef7..f8638a7830 100644
--- a/mythtv/libs/libmythtv/cc708decoder.cpp
+++ b/mythtv/libs/libmythtv/cc708decoder.cpp
@@ -15,7 +15,8 @@
 #define DEBUG_CC_SERVICE_2     0
 #define DEBUG_CC_RAWPACKET     0
 #define DEBUG_CC_VALIDPACKET   0
-#define DEBUG_CC_SERVICE_BLOCK 0
+#define DEBUG_CC_DECODE        0
+#define DEBUG_CC_PARSE         0
 
 typedef enum
 {
@@ -40,7 +41,7 @@ void CC708Decoder::decode_cc_data(uint cc_type, uint data1, uint data2)
 {
     if (DTVCC_PACKET_START == cc_type)
     {
-#if 0
+#if DEBUG_CC_DECODE
         LOG(VB_VBI, LOG_DEBUG, LOC + QString("CC ST data(0x%1 0x%2)")
                 .arg(data1,0,16).arg(data2,0,16));
 #endif
@@ -54,7 +55,7 @@ void CC708Decoder::decode_cc_data(uint cc_type, uint data1, uint data2)
     }
     else if (DTVCC_PACKET_DATA == cc_type)
     {
-#if 0
+#if DEBUG_CC_DECODE
         LOG(VB_VBI, LOG_DEBUG, LOC + QString("CC Ex data(0x%1 0x%2)")
                 .arg(data1,0,16).arg(data2,0,16));
 #endif
@@ -197,10 +198,10 @@ static void parse_cc_service_stream(CC708Reader* cc, uint service_num)
         dlc_loc = blk_size - 1;
     }
 
-#if 0
+#if DEBUG_CC_PARSE
     LOG(VB_VBI, LOG_ERR,
         QString("cc_ss delayed(%1) blk_start(%2) blk_size(%3)")
-            .arg(cc->delayed) .arg(blk_start) .arg(blk_size));
+            .arg(cc->delayed[service_num]) .arg(blk_start) .arg(blk_size));
 #endif
 
     for (i = (cc->delayed[service_num]) ? blk_size : blk_start;
@@ -492,7 +493,7 @@ static int handle_cc_c1(CC708Reader* cc, uint service_num, int i)
     else
     {
         LOG(VB_VBI, LOG_ERR, QString("handle_cc_c1: (NOT HANDLED) "
-                "code(0x%1) i(%2) blk_size(%3)").arg(code, 2, 16, '0')
+                "code(0x%1) i(%2) blk_size(%3)").arg(code, 2, 16, QLatin1Char('0'))
                 .arg(i).arg(blk_size));
     }
 #endif
@@ -555,18 +556,25 @@ static int handle_cc_c3(CC708Reader* cc, uint service_num, int i)
     return i;
 }
 
-static void rightsize_buf(CC708Reader* cc, uint service_num, uint block_size)
+static bool rightsize_buf(CC708Reader* cc, uint service_num, uint block_size)
 {
-    uint min_new_size = block_size + cc->buf_size[service_num];
+    size_t min_new_size = block_size + cc->buf_size[service_num];
+    bool ret = true;
     if (min_new_size >= cc->buf_alloc[service_num])
     {
-        uint new_alloc    = cc->buf_alloc[service_num];
+        size_t new_alloc = cc->buf_alloc[service_num];
         for (uint i = 0; (i < 32) && (new_alloc <= min_new_size); i++)
             new_alloc *= 2;
-
-        cc->buf[service_num] =
-            (unsigned char*) realloc(cc->buf[service_num], new_alloc);
-        cc->buf_alloc[service_num] = (cc->buf[service_num]) ? new_alloc : 0;
+        void *new_buf = realloc(cc->buf[service_num], new_alloc);
+        if (new_buf)
+        {
+            cc->buf[service_num] = (uchar *)new_buf;
+            cc->buf_alloc[service_num] = new_alloc;
+        }
+        else
+        {
+            ret = false;
+        }
 
 #if DEBUG_CC_SERVICE_2
         LOG(VB_VBI, LOG_DEBUG, QString("rightsize_buf: srv %1 to %1 bytes")
@@ -579,12 +587,17 @@ static void rightsize_buf(CC708Reader* cc, uint service_num, uint block_size)
             .arg(min_new_size)
             .arg(service_num)
             .arg(cc->buf_alloc[service_num]));
+    return ret;
 }
 
 static void append_cc(CC708Reader* cc, uint service_num,
                       const unsigned char* blk_buf, int block_size)
 {
-    rightsize_buf(cc, service_num, block_size);
+    if (!rightsize_buf(cc, service_num, block_size))
+    {
+        // The buffer resize failed. Drop the new data.
+        return;
+    }
 
     memcpy(cc->buf[service_num] + cc->buf_size[service_num],
            blk_buf, block_size);
diff --git a/mythtv/libs/libmythtv/channelscan/channelscanner.cpp b/mythtv/libs/libmythtv/channelscan/channelscanner.cpp
index d4532cd226..a6a55340d9 100644
--- a/mythtv/libs/libmythtv/channelscan/channelscanner.cpp
+++ b/mythtv/libs/libmythtv/channelscan/channelscanner.cpp
@@ -39,7 +39,9 @@ using namespace std;
 #include "hdhrchannel.h"
 #include "scanmonitor.h"
 #include "asichannel.h"
+#ifdef USING_DVB        // for bug in gcc 8.3
 #include "dvbchannel.h"
+#endif
 #include "v4lchannel.h"
 #include "iptvchannel.h"
 #include "ExternalChannel.h"
diff --git a/mythtv/libs/libmythtv/channelutil.cpp b/mythtv/libs/libmythtv/channelutil.cpp
index 1c35d7ca4f..bec67d0236 100644
--- a/mythtv/libs/libmythtv/channelutil.cpp
+++ b/mythtv/libs/libmythtv/channelutil.cpp
@@ -1875,7 +1875,8 @@ bool ChannelUtil::GetChannelData(
         "FROM channel, videosource "
         "WHERE videosource.sourceid = channel.sourceid AND "
         "      channum              = :CHANNUM         AND "
-        "      channel.sourceid     = :SOURCEID");
+        "      channel.sourceid     = :SOURCEID "
+        "ORDER BY channel.visible DESC, channel.chanid ");
     query.bindValue(":CHANNUM",  channum);
     query.bindValue(":SOURCEID", sourceid);
 
@@ -1885,35 +1886,38 @@ bool ChannelUtil::GetChannelData(
         return false;
     }
 
-    while (query.next())
+    if (query.next())
     {
+        finetune      = query.value(0).toInt();
+        freqid        = query.value(1).toString();
+        tvformat      = query.value(2).toString();
+        freqtable     = query.value(3).toString();
+        commfree      = (query.value(4).toInt() == -2);
+        mplexid       = query.value(5).toUInt();
+        atsc_major    = query.value(6).toUInt();
+        atsc_minor    = query.value(7).toUInt();
+        mpeg_prog_num = (query.value(8).isNull()) ? -1
+                        : query.value(8).toInt();
+        chanid        = query.value(9).toUInt();
+
         found += query.value(10).toInt();
-        if (found)
-        {
-            finetune      = query.value(0).toInt();
-            freqid        = query.value(1).toString();
-            tvformat      = query.value(2).toString();
-            freqtable     = query.value(3).toString();
-            commfree      = (query.value(4).toInt() == -2);
-            mplexid       = query.value(5).toUInt();
-            atsc_major    = query.value(6).toUInt();
-            atsc_minor    = query.value(7).toUInt();
-            mpeg_prog_num = (query.value(8).isNull()) ? -1
-                            : query.value(8).toInt();
-            chanid        = query.value(9).toUInt();
-        }
     }
 
-    if (!found)
+    while (query.next())
+        found += query.value(10).toInt();
+
+    if (found == 0 && chanid)
     {
-        LOG(VB_GENERAL, LOG_INFO,
-            QString("No visible channels for %1").arg(channum));
+        LOG(VB_GENERAL, LOG_WARNING,
+            QString("No visible channels for %1, using invisble chanid %2")
+            .arg(channum).arg(chanid));
     }
 
     if (found > 1)
     {
         LOG(VB_GENERAL, LOG_WARNING,
-            QString("Found multiple visible channels for %1").arg(channum));
+            QString("Found multiple visible channels for %1, using chanid %2")
+            .arg(channum).arg(chanid));
     }
 
     if (!chanid)
@@ -2409,7 +2413,7 @@ ChannelInfoList ChannelUtil::LoadChannels(uint startIndex, uint count,
         cond << "channel.sourceid = :SOURCEID ";
 
     if (liveTVOnly)
-        cond << "channel.livetvorder > 0 ";
+        cond << "capturecard.livetvorder > 0 ";
 
     if (!cond.isEmpty())
         sql += QString("WHERE %1").arg(cond.join("AND "));
@@ -2429,7 +2433,7 @@ ChannelInfoList ChannelUtil::LoadChannels(uint startIndex, uint count,
     }
     else // kChanOrderByLiveTV
     {
-        sql += "ORDER BY callsign = :CALLSIGN1 AND channum = :CHANNUM, "
+        sql += "ORDER BY callsign = :CALLSIGN1 AND channum = :CHANNUM DESC, "
                "         callsign = :CALLSIGN2 DESC, "
                "         livetvorder, "
                "         channel.recpriority DESC, "
diff --git a/mythtv/libs/libmythtv/datadirect.cpp b/mythtv/libs/libmythtv/datadirect.cpp
index e6477fdf21..9cd7178786 100644
--- a/mythtv/libs/libmythtv/datadirect.cpp
+++ b/mythtv/libs/libmythtv/datadirect.cpp
@@ -881,7 +881,7 @@ void DataDirectProcessor::DataDirectProgramUpdate(void)
 #endif
 
     if (!query.exec("INSERT IGNORE INTO programrating (chanid, starttime, "
-                    "system, rating) SELECT dd_v_program.chanid, "
+                    "`system`, rating) SELECT dd_v_program.chanid, "
                     "DATE_ADD(starttime, INTERVAL channel.tmoffset MINUTE), "
                     " 'MPAA', "
                     "mpaarating FROM dd_v_program, channel WHERE "
@@ -890,7 +890,7 @@ void DataDirectProcessor::DataDirectProgramUpdate(void)
         MythDB::DBError("Inserting into programrating table", query);
 
     if (!query.exec("INSERT IGNORE INTO programrating (chanid, starttime, "
-                    "system, rating) SELECT dd_v_program.chanid, "
+                    "`system`, rating) SELECT dd_v_program.chanid, "
                     "DATE_ADD(starttime, INTERVAL channel.tmoffset MINUTE), "
                     "'VCHIP', "
                     "tvrating FROM dd_v_program, channel WHERE tvrating != ''"
diff --git a/mythtv/libs/libmythtv/dbcheck.cpp b/mythtv/libs/libmythtv/dbcheck.cpp
index f3485aca9a..c55f1b207c 100644
--- a/mythtv/libs/libmythtv/dbcheck.cpp
+++ b/mythtv/libs/libmythtv/dbcheck.cpp
@@ -2636,9 +2636,9 @@ nullptr
                 "UNIQUE KEY recgroup ( recgroup )"
                 ") ENGINE=MyISAM DEFAULT CHARSET=utf8;",
             // Create the built-in, 'special', groups
-            "INSERT INTO recgroups ( recgroup, special ) VALUES ( 'Default', '1' );",
-            "INSERT INTO recgroups ( recgroup, special ) VALUES ( 'LiveTV', '1' );",
-            "INSERT INTO recgroups ( recgroup, special ) VALUES ( 'Deleted', '1' );",
+            "INSERT INTO recgroups ( recgroupid, recgroup, special ) VALUES ( 1, 'Default', '1' );",
+            "INSERT INTO recgroups ( recgroupid, recgroup, special ) VALUES ( 2, 'LiveTV', '1' );",
+            "INSERT INTO recgroups ( recgroupid, recgroup, special ) VALUES ( 3, 'Deleted', '1' );",
             // Copy in the passwords for the built-in groups
             "DELETE FROM recgrouppassword WHERE password = '';",
             "UPDATE recgroups r, recgrouppassword p SET r.password = p.password WHERE r.recgroup = p.recgroup;",
@@ -3442,7 +3442,7 @@ nullptr
     if (dbver == "1349")
     {
         const char *updates[] = {
-            "DELETE FROM settings WHERE value='AltClearSavedPosition';",
+            // Incorrect DB update removed
             nullptr
         };
         if (!performActualUpdate(updates, "1350", dbver))
@@ -4125,10 +4125,10 @@ bool InitializeMythSchema(void)
 "CREATE TABLE programrating ("
 "  chanid int(10) unsigned NOT NULL DEFAULT '0',"
 "  starttime datetime NOT NULL DEFAULT '0000-00-00 00:00:00',"
-"  system varchar(8) DEFAULT NULL,"
+"  `system` varchar(8) DEFAULT NULL,"
 "  rating varchar(16) DEFAULT NULL,"
-"  UNIQUE KEY chanid (chanid,starttime,system,rating),"
-"  KEY starttime (starttime,system)"
+"  UNIQUE KEY chanid (chanid,starttime,`system`,rating),"
+"  KEY starttime (starttime,`system`)"
 ") ENGINE=MyISAM DEFAULT CHARSET=utf8;",
 "CREATE TABLE recgrouppassword ("
 "  recgroup varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',"
@@ -4335,10 +4335,10 @@ bool InitializeMythSchema(void)
 "CREATE TABLE recordedrating ("
 "  chanid int(10) unsigned NOT NULL DEFAULT '0',"
 "  starttime datetime NOT NULL DEFAULT '0000-00-00 00:00:00',"
-"  system varchar(8) DEFAULT NULL,"
+"  `system` varchar(8) DEFAULT NULL,"
 "  rating varchar(16) DEFAULT NULL,"
-"  UNIQUE KEY chanid (chanid,starttime,system,rating),"
-"  KEY starttime (starttime,system)"
+"  UNIQUE KEY chanid (chanid,starttime,`system`,rating),"
+"  KEY starttime (starttime,`system`)"
 ") ENGINE=MyISAM DEFAULT CHARSET=utf8;",
 "CREATE TABLE recordedseek ("
 "  chanid int(10) unsigned NOT NULL DEFAULT '0',"
diff --git a/mythtv/libs/libmythtv/decoderbase.h b/mythtv/libs/libmythtv/decoderbase.h
index 22bbd9fbae..4ee216d02c 100644
--- a/mythtv/libs/libmythtv/decoderbase.h
+++ b/mythtv/libs/libmythtv/decoderbase.h
@@ -208,6 +208,7 @@ class DecoderBase
     virtual void UpdateFramesPlayed(void);
     long long GetFramesRead(void) const { return framesRead; }
     long long GetFramesPlayed(void) const { return framesPlayed; }
+    void SetFramesPlayed(long long newValue) {framesPlayed = newValue;}
 
     virtual QString GetCodecDecoderName(void) const = 0;
     virtual QString GetRawEncodingType(void) { return QString(); }
diff --git a/mythtv/libs/libmythtv/dsmccobjcarousel.cpp b/mythtv/libs/libmythtv/dsmccobjcarousel.cpp
index 0ace9e6a29..4109a52cc5 100644
--- a/mythtv/libs/libmythtv/dsmccobjcarousel.cpp
+++ b/mythtv/libs/libmythtv/dsmccobjcarousel.cpp
@@ -105,6 +105,13 @@ unsigned char *DSMCCCacheModuleData::AddModuleData(DsmccDb *ddb,
     for (uint i = 0; i < m_blocks.size(); i++)
     {
         QByteArray *block = m_blocks[i];
+        if (block == nullptr)
+        {
+            LOG(VB_DSMCC, LOG_INFO,
+                QString("[dsmcc] Null data found, aborting reconstruction"));
+            free(tmp_data);
+            return nullptr;
+        }
         m_blocks[i] = nullptr;
         uint size = block->size();
         memcpy(tmp_data + curp, block->data(), size);
diff --git a/mythtv/libs/libmythtv/eitcache.cpp b/mythtv/libs/libmythtv/eitcache.cpp
index c1ef93d608..9bbb724382 100644
--- a/mythtv/libs/libmythtv/eitcache.cpp
+++ b/mythtv/libs/libmythtv/eitcache.cpp
@@ -388,9 +388,7 @@ bool EITCache::IsNewEIT(uint chanid,  uint tableid,   uint version,
             tblChgCnt++;
         }
         else if ((extract_table_id(*it) == tableid) &&
-                 ((extract_version(*it) < version) ||
-                  ((extract_version(*it) == kVersionMax) &&
-                   version < kVersionMax)))
+                 (extract_version(*it) != version))
         {
             // EIT updated version on current table
             verChgCnt++;
diff --git a/mythtv/libs/libmythtv/eitfixup.cpp b/mythtv/libs/libmythtv/eitfixup.cpp
index 7807484ae5..0fda92942e 100644
--- a/mythtv/libs/libmythtv/eitfixup.cpp
+++ b/mythtv/libs/libmythtv/eitfixup.cpp
@@ -144,6 +144,8 @@ EITFixUp::EITFixUp()
       m_RTLEpisodeNo2("^(\\d{1,2}\\/[IVX]+)\\.*\\s*"),
       m_fiRerun("\\ ?Uusinta[a-zA-Z\\ ]*\\.?"),
       m_fiRerun2("\\([Uu]\\)"),
+      m_fiAgeLimit("\\(((1?[0-9]?)|[ST])\\)$"),
+      m_fiFilm("^(Film|Elokuva): "),
       m_dePremiereLength("\\s?[0-9]+\\sMin\\."),
       m_dePremiereAirdate("\\s?([^\\s^\\.]+)\\s((?:1|2)[0-9]{3})\\."),
       m_dePremiereCredits("\\sVon\\s([^,]+)(?:,|\\su\\.\\sa\\.)\\smit\\s([^\\.]*)\\."),
@@ -198,24 +200,28 @@ EITFixUp::EITFixUp()
       m_HTML("</?EM>", Qt::CaseInsensitive),
       m_grReplay("\\([ΕE]\\)"),
       m_grDescriptionFinale("\\s*Τελευταίο\\sΕπεισόδιο\\.\\s*"),
-      m_grActors("(?:[Ππ]α[ιί]ζουν:|[Μμ]ε τους:|Πρωταγωνιστο[υύ]ν:|Πρωταγωνιστε[ιί]:?)(?:\\s+στο ρόλο(?: του| της)?\\s(?:\\w+\\s[οη]\\s))?([-\\w\\s']+(?:,[-\\w\\s']+)*)(?:κ\\.[αά])?(?:\\W?)"),
+      m_grActors("(?:[Ππ]α[ιί]ζουν:|[ΜMμ]ε τους:|Πρωταγωνιστο[υύ]ν:|Πρωταγωνιστε[ιί]:?)(?:\\s+στο ρόλο(?: του| της)?\\s(?:\\w+\\s[οη]\\s))?([-\\w\\s']+(?:,[-\\w\\s']+)*)(?:κ\\.[αά])?(?:\\W?)"),
       // cap(1) actors, just names
       m_grFixnofullstopActors("(\\w\\s(Παίζουν:|Πρωταγων))"),
       m_grFixnofullstopDirectors("((\\w\\s(Σκηνοθ[εέ]))"),
-      m_grPeopleSeparator("(,\\s+)"),
-      m_grDirector("(?:Σκηνοθεσία: |Σκηνοθέτης: )(\\w+\\s\\w+\\s?)(?:\\W?)"),
-      m_grPres("(?:Παρουσ[ιί]αση:(?:\\b)*|Παρουσι[αά]ζ(?:ουν|ει)(?::|\\sο|\\sη)|Με τ(?:ον |ην )(?:[\\s|:|ο|η])*(?:\\b)*)([-\\w\\s]+(?:,[-\\w\\s]+)*)(?:\\W?)"),
+      m_grPeopleSeparator("([,-]\\s+)"),
+      m_grDirector("(?:Σκηνοθεσία: |Σκηνοθέτης: |Σκηνοθέτης - Επιμέλεια: )(\\w+\\s\\w+\\s?)(?:\\W?)"),
+      m_grPres("(?:Παρουσ[ιί]αση:(?:\\b)*|Παρουσι[αά]ζ(?:ουν|ει)(?::|\\sο|\\sη)|Παρουσι[αά]στ(?:[ηή]ς|ρια|ριες|[εέ]ς)(?::|\\sο|\\sη)|Με τ(?:ον |ην )(?:[\\s|:|ο|η])*(?:\\b)*)([-\\w\\s]+(?:,[-\\w\\s]+)*)(?:\\W?)"),
       m_grYear("(?:\\W?)(?:\\s?παραγωγ[ηή]ς|\\s?-|,)\\s*([1-2]{1}[0-9]{3})(?:-\\d{1,4})?",Qt::CaseInsensitive),
       m_grCountry("(?:\\W|\\b)(?:(ελλην|τουρκ|αμερικ[αά]ν|γαλλ|αγγλ|βρεττ?αν|γερμαν|ρωσσ?|ιταλ|ελβετ|σουηδ|ισπαν|πορτογαλ|μεξικ[αά]ν|κιν[εέ]ζικ|ιαπων|καναδ|βραζιλι[αά]ν)(ικ[ηή][ςσ]))",Qt::CaseInsensitive),
       m_grlongEp("\\b(?:Επ.|επεισ[οό]διο:?)\\s*(\\d+)(?:\\W?)",Qt::CaseInsensitive),
-      m_grSeason("(?:-\\s)?\\b((\\D{1,2})(?:')?|(\\d{1,2})(?:ος|ου)?)(?:\\sκ[υύ]κλο(?:[σς]|υ)){1}\\s?",Qt::CaseInsensitive),
-      m_grRealTitleinDescription("(?:^\\()([\\w\\s\\d\\D-]+)(?:\\))(?:\\s*)"),
+      m_grSeason_as_RomanNumerals(",\\s*([MDCLXVIΙΧ]+)$",Qt::CaseInsensitive),
+      m_grSeason("(?:\\W-?)*(?:\\(-\\s*)?\\b(([Α-Ω]{1,2})(?:'|΄)?|(\\d{1,2})(?:ος|ου|oς|os)?)(?:\\s*κ[υύ]κλο(?:[σς]|υ)){1}\\s?",Qt::CaseInsensitive),
+      m_grRealTitleinDescription("(?:^\\()([A-Za-z\\s\\d-]+)(?:\\))(?:\\s*)"),
       // cap1 = real title
       // cap0 = real title in parentheses.
-      m_grRealTitleinTitle("(?:\\()([\\w\\s\\d\\D-]+)(?:\\))(?:\\s*$)*"),
+      m_grRealTitleinTitle("(?:\\()([A-Za-z\\s\\d-]+)(?:\\))(?:\\s*$)*"),
       // cap1 = real title
       // cap0 = real title in parentheses.
-      m_grNotPreviouslyShown("(?:\\b[Α1]['η]?\\s*(?:τηλεοπτικ[ηή]\\s*)?(?:μετ[αά]δοση|προβολ[ηή]))(?:\\W?)"),
+      m_grCommentsinTitle("(?:\\()([Α-Ωα-ω\\s\\d-]+)(?:\\))(?:\\s*$)*"),
+      // cap1 = real title
+      // cap0 = real title in parentheses.
+      m_grNotPreviouslyShown("(?:\\W?)(?:-\\s*)*(?:\\b[Α1]['΄η]?\\s*(?:τηλεοπτικ[ηή]\\s*)?(?:μετ[αά]δοση|προβολ[ηή]))(?:\\W?)",Qt::CaseInsensitive),
       // Try to exctract Greek categories from keywords in description.
       m_grEpisodeAsSubtitle("(?:^Επεισ[οό]διο:\\s?)([\\w\\s-,']+)\\.(?:\\s)?"),
       m_grCategFood("(?:\\W)?(?:εκπομπ[ηή]\\W)?(Γαστρονομ[ιί]α[σς]?|μαγειρικ[ηή][σς]?|chef|συνταγ[εέηή]|διατροφ|wine|μ[αά]γειρα[σς]?)(?:\\W)?",Qt::CaseInsensitive),
@@ -1924,6 +1930,21 @@ void EITFixUp::FixFI(DBEventEIT &event) const
         event.audioProps |= AUD_STEREO;
         event.description = event.description.replace(m_Stereo, "");
     }
+
+    // Remove age limit in parenthesis at end of title
+    position = event.title.indexOf(m_fiAgeLimit);
+    if (position != -1)
+    {
+        event.title = event.title.replace(m_fiAgeLimit, "");
+    }
+
+    // Remove Film or Elokuva at start of title
+    position = event.title.indexOf(m_fiFilm);
+    if (position != -1)
+    {
+        event.title = event.title.replace(m_fiFilm, "");
+    }
+
 }
 
 /** \fn EITFixUp::FixPremiere(DBEventEIT&) const
@@ -2552,6 +2573,8 @@ void EITFixUp::FixGreekSubtitle(DBEventEIT &event) const
 
 void EITFixUp::FixGreekEIT(DBEventEIT &event) const
 {
+    int position1;
+    int position2;
     //Live show
     int position;
     QRegExp tmpRegEx;
@@ -2567,7 +2590,7 @@ void EITFixUp::FixGreekEIT(DBEventEIT &event) const
     if (position != -1)
     {
         event.previouslyshown = false;
-        event.title = event.title.replace(m_grNotPreviouslyShown.cap(0), "");
+        event.title = event.title.replace(m_grNotPreviouslyShown, "");
     }
 
     // Greek Replay (Ε)
@@ -2580,6 +2603,23 @@ void EITFixUp::FixGreekEIT(DBEventEIT &event) const
         event.title = event.title.replace(tmpRegEx, "");
     }
 
+    // Check for (HD) in the decription
+    position = event.description.indexOf("(HD)");
+    if (position != -1)
+    {
+        event.description = event.description.replace("(HD)", "");
+        event.videoProps |= VID_HDTV;
+    }
+
+    // Check for (Full HD) in the decription
+    position = event.description.indexOf("(Full HD)");
+    if (position != -1)
+    {
+        event.description = event.description.replace("(Full HD)", "");
+        event.videoProps |= VID_HDTV;
+    }
+
+
     tmpRegEx = m_grFixnofullstopActors;
     position = event.description.indexOf(tmpRegEx);
     if (position != -1)
@@ -2639,37 +2679,6 @@ void EITFixUp::FixGreekEIT(DBEventEIT &event) const
         event.description.replace(tmpRegEx.cap(0), "");
     }
 
-/*
-
-    tmpRegEx = m_grDirector;
-    position = event.description.indexOf(tmpRegEx);
-    if (position != -1)
-    {
-        // director is captured in cap(1).Due to sometimes miswritten data in
-        // eit, e.g no fullstop present(!)
-        QString tmpDirectorsString = tmpRegEx.cap(1);
-        // look for actors in director string (lack of fullstop, etc)
-        tmpRegEx = m_grActors;
-        int actposition = tmpDirectorsString.indexOf(tmpRegEx);
-        if (actposition != -1)
-        {
-            tmpDirectorsString = tmpDirectorsString.replace(tmpRegEx,"");
-        }
-        const QStringList directors =
-            tmpDirectorsString.split(m_grPeopleSeparator, QString::SkipEmptyParts);
-        QStringList::const_iterator it = directors.begin();
-        for (; it != directors.end(); ++it)
-        {
-            tmpDirectorsString = it->split(":").last().trimmed().
-                    remove(QRegExp("\\.$"));
-            if (tmpDirectorsString != "")
-                event.AddPerson(DBPerson::kDirector, tmpDirectorsString);
-
-        }
-        event.description.replace(tmpRegEx.cap(0), "");
-       // directorPresent = true;
-    }
-*/
     //Try to find presenter
     tmpRegEx = m_grPres;
     position = event.description.indexOf(tmpRegEx);
@@ -2709,6 +2718,8 @@ void EITFixUp::FixGreekEIT(DBEventEIT &event) const
     event.description = event.description.trimmed();
     event.title       = event.title.trimmed();
     event.subtitle    = event.subtitle.trimmed();
+    // Remove " ."
+    event.description = event.description.replace(" .",".").trimmed();
 
     //find country of origin and remove it from description.
     tmpRegEx = m_grCountry;
@@ -2724,9 +2735,8 @@ void EITFixUp::FixGreekEIT(DBEventEIT &event) const
     QRegExp tmpSeries = m_grSeason;
     // cap(2) is the season for ΑΒΓΔ
     // cap(3) is the season for 1234
-    int position1 = tmpSeries.indexIn(event.title);
-    int position2 = tmpSeries.indexIn(event.description);
-    if ((position1 != -1) || (position2 != -1))
+    if ((position1 = tmpSeries.indexIn(event.title)) != -1
+          || (position2 = tmpSeries.indexIn(event.description)) != -1)
     {
         if (!tmpSeries.cap(2).isEmpty()) // we found a letter representing a number
         {
@@ -2756,6 +2766,73 @@ void EITFixUp::FixGreekEIT(DBEventEIT &event) const
         if (position2 != -1)
             event.description.replace(tmpSeries.cap(0),"");
     }
+    // If Season is in Roman Numerals (I,II,etc)
+    tmpSeries = m_grSeason_as_RomanNumerals;
+    if ((position1 = tmpSeries.indexIn(event.title)) != -1
+          || (position2 = tmpSeries.indexIn(event.description)) != -1)
+    {
+        if (!tmpSeries.isEmpty()) //number
+        {
+            // make sure I replace greek Ι with english I
+            QString romanSeries = tmpSeries.cap(1).replace("Ι","I").toUpper();
+            if (romanSeries == "I")
+                event.season = 1;
+            else if (romanSeries == "II")
+                event.season = 2;
+            else if (romanSeries== "III")
+                event.season = 3;
+            else if (romanSeries == "IV")
+                event.season = 4;
+            else if (romanSeries == "V")
+                event.season = 5;
+            else if (romanSeries== "VI")
+                event.season = 6;
+            else if (romanSeries == "VII")
+                event.season = 7;
+            else if (romanSeries == "VIII")
+                event.season = 8;
+            else if (romanSeries == "IX")
+                event.season = 9;
+            else if (romanSeries == "X")
+                event.season = 10;
+            else if (romanSeries == "XI")
+                event.season = 11;
+            else if (romanSeries == "XII")
+                event.season = 12;
+            else if (romanSeries == "XII")
+                event.season = 13;
+            else if (romanSeries == "XIV")
+                event.season = 14;
+            else if (romanSeries == "XV")
+                event.season = 15;
+            else if (romanSeries == "XVI")
+                event.season = 16;
+            else if (romanSeries == "XVII")
+                event.season = 17;
+            else if (romanSeries == "XIII")
+                event.season = 18;
+            else if (romanSeries == "XIX")
+                event.season = 19;
+            else if (romanSeries == "XX")
+                event.season = 20;
+        }
+        series = true;
+        if (position1 != -1)
+        {
+            event.title.replace(tmpSeries.cap(0),"");
+            event.title = event.title.trimmed();
+            if (event.title.right(1) == ",")
+               event.title.chop(1);
+        }
+        if (position2 != -1)
+        {
+            event.description.replace(tmpSeries.cap(0),"");
+            event.description = event.description.trimmed();
+            if (event.description.right(1) == ",")
+               event.description.chop(1);
+        }
+    }
+
 
     QRegExp tmpEpisode = m_grlongEp;
     //tmpEpisode.setMinimal(true);
@@ -2776,6 +2853,17 @@ void EITFixUp::FixGreekEIT(DBEventEIT &event) const
                 event.season = 1;
         }
     }
+    // Sometimes, especially on greek national tv, they include comments in the
+    // title, e.g "connection to ert1", "ert archives".
+    // Because they obscure the real title, I'll isolate and remove them.
+
+    QRegExp tmpComment = m_grCommentsinTitle;
+    tmpComment.setMinimal(true);
+    position = event.title.indexOf(tmpComment);
+    if (position != -1)
+    {
+        event.title.replace(tmpComment.cap(0),"");
+    }
 
     // Sometimes the real (mostly English) title of a movie or series is
     // enclosed in parentheses in the event title, subtitle or description.
diff --git a/mythtv/libs/libmythtv/eitfixup.h b/mythtv/libs/libmythtv/eitfixup.h
index 67cbcd0846..35ed11753a 100644
--- a/mythtv/libs/libmythtv/eitfixup.h
+++ b/mythtv/libs/libmythtv/eitfixup.h
@@ -212,6 +212,8 @@ class EITFixUp
     const QRegExp m_RTLEpisodeNo2;
     const QRegExp m_fiRerun;
     const QRegExp m_fiRerun2;
+    const QRegExp m_fiAgeLimit;
+    const QRegExp m_fiFilm;
     const QRegExp m_dePremiereLength;
     const QRegExp m_dePremiereAirdate;
     const QRegExp m_dePremiereCredits;
@@ -272,10 +274,12 @@ class EITFixUp
     const QRegExp m_grYear; // Greek release year.
     const QRegExp m_grCountry; // Greek event country of origin.
     const QRegExp m_grlongEp; // Greek Episode
+    const QRegExp m_grSeason_as_RomanNumerals; // Greek Episode in Roman numerals
     const QRegExp m_grSeason; // Greek Season
     const QRegExp m_grSeries;
     const QRegExp m_grRealTitleinDescription; // The original title is often in the descr in parenthesis.
     const QRegExp m_grRealTitleinTitle; // The original title is often in the title in parenthesis.
+    const QRegExp m_grCommentsinTitle; // Sometimes esp. national stations include comments in the title eg "(ert arxeio)"
     const QRegExp m_grNotPreviouslyShown; // Not previously shown on TV
     const QRegExp m_grEpisodeAsSubtitle; // Description field: "^Episode: Lion in the cage. (Description follows)"
     const QRegExp m_grCategFood; // Greek category food
diff --git a/mythtv/libs/libmythtv/eithelper.cpp b/mythtv/libs/libmythtv/eithelper.cpp
index 1caa158d9f..7a839d1c42 100644
--- a/mythtv/libs/libmythtv/eithelper.cpp
+++ b/mythtv/libs/libmythtv/eithelper.cpp
@@ -89,13 +89,12 @@ uint EITHelper::ProcessEvents(void)
     if (!insertCount)
         return 0;
 
-    if (incomplete_events.size() || unmatched_etts.size())
+    if (!incomplete_events.empty())
     {
         LOG(VB_EIT, LOG_INFO,
-            LOC + QString("Added %1 events -- complete(%2) "
-                          "incomplete(%3) unmatched(%4)")
+            LOC + QString("Added %1 events -- complete: %2 incomplete: %3")
                 .arg(insertCount).arg(db_events.size())
-                .arg(incomplete_events.size()).arg(unmatched_etts.size()));
+                .arg(incomplete_events.size()));
     }
     else
     {
@@ -147,7 +146,6 @@ void EITHelper::AddEIT(uint atsc_major, uint atsc_minor,
 {
     uint atsc_key = (atsc_major << 16) | atsc_minor;
     EventIDToATSCEvent &events = incomplete_events[atsc_key];
-    EventIDToETT &etts = unmatched_etts[atsc_key];
 
     for (uint i = 0; i < eit->EventCount(); i++)
     {
@@ -156,29 +154,24 @@ void EITHelper::AddEIT(uint atsc_major, uint atsc_minor,
                      eit->title(i).GetBestMatch(languagePreferences),
                      eit->Descriptors(i), eit->DescriptorsLength(i));
 
-        // Look to see if there has been a recent ett message with the same event id.
-        EventIDToETT::iterator it = etts.find(eit->EventID(i));
-        QString ett_text;
-        bool found_matching_ett = false;
-        if (it != etts.end())
+        // Create an event immediately if the ETM_location specifies
+        // that there is no ETT event coming; otherwise save the EIT
+        // event in the incomplete_events for this channel.
+        if (!ev.etm)
         {
-            // Don't use an ett description if it was scanned long in the past.
-            if (!it->IsStale()) {
-              ett_text = it->ett_text;
-              found_matching_ett = true;
-            }
-            etts.erase(it);
-        }
-
-        // Create an event immediately if a matching ett description was found,
-        // or if item is false, indicating that no ett description should be
-        // expected.
-        if (found_matching_ett || !ev.etm)
-        {
-            CompleteEvent(atsc_major, atsc_minor, ev, ett_text);
+            CompleteEvent(atsc_major, atsc_minor, ev, "");
         }
         else
         {
+            // If there is an existing EIT event with this event_id then
+            // delete the descriptor to avoid a memory leak.
+            EventIDToATSCEvent::iterator it = events.find(eit->EventID(i));
+            if (it != events.end())
+            {
+                delete [] (*it).desc;
+            }
+
+            // Save the EIT event in the incomplete_events for this channel.
             unsigned char *tmp = new unsigned char[ev.desc_length];
             memcpy(tmp, eit->Descriptors(i), ev.desc_length);
             ev.desc = tmp;
@@ -190,52 +183,27 @@ void EITHelper::AddEIT(uint atsc_major, uint atsc_minor,
 void EITHelper::AddETT(uint atsc_major, uint atsc_minor,
                        const ExtendedTextTable *ett)
 {
+    // Find the matching incomplete EIT event for this ETT
+    // If we have no EIT event then just discard the ETT.
     uint atsc_key = (atsc_major << 16) | atsc_minor;
-    // Try to match up the ett with an eit event.
     ATSCSRCToEvents::iterator eits_it = incomplete_events.find(atsc_key);
     if (eits_it != incomplete_events.end())
     {
         EventIDToATSCEvent::iterator it = (*eits_it).find(ett->EventID());
         if (it != (*eits_it).end())
         {
-            bool completed_event = false;
-            // Only consider eit events from the recent past.
+            // Only consider EIT events from the very recent past.
             if (!it->IsStale()) {
-              completed_event = true;
               CompleteEvent(
                   atsc_major, atsc_minor, *it,
                   ett->ExtendedTextMessage().GetBestMatch(languagePreferences));
             }
 
-            if ((*it).desc)
-                delete [] (*it).desc;
-
+            // Remove EIT event from the incomplete_event list.
+            delete [] (*it).desc;
             (*eits_it).erase(it);
-
-            if (completed_event) return;
         }
     }
-
-    // Report if an unmatched ett was previously noted and overwrite it.
-    // See also https://code.mythtv.org/trac/ticket/11739
-    EventIDToETT &elist = unmatched_etts[atsc_key];
-    EventIDToETT::iterator existing_unmatched_ett_it =
-        elist.find(ett->EventID());
-    const QString next_ett_text = ett->ExtendedTextMessage()
-        .GetBestMatch(languagePreferences);
-    if (existing_unmatched_ett_it != elist.end() &&
-        existing_unmatched_ett_it->ett_text != next_ett_text)
-    {
-       LOG(VB_EIT, LOG_DEBUG, LOC +
-           QString("Overwriting previously unmatched ett. stale: %1 major: %2 "
-                   "minor: %3 old ett: %4  new ett: %5")
-               .arg(existing_unmatched_ett_it->IsStale())
-               .arg(atsc_major)
-               .arg(atsc_minor)
-               .arg(existing_unmatched_ett_it->ett_text)
-               .arg(next_ett_text));
-    }
-    elist.insert(ett->EventID(), ATSCEtt(next_ett_text));
 }
 
 static void parse_dvb_event_descriptors(desc_list_t list, FixupValue fix,
@@ -1421,6 +1389,7 @@ static void init_fixup(FixupMap &fix)
     fix[  100LL << 32 |  8492LL << 16 ] = // Ant1,Alpha,Art,10E
     fix[  102LL << 32 |  8492LL << 16 ] = // Mega,Star,SKAI,M.tv
     fix[  320LL << 32 |  8492LL << 16 ] = // Astra,Thessalia,TRT,TV10,ZEYS
+        EITFixUp::kEFixForceISO8859_7 |
         EITFixUp::kFixGreekEIT |
         EITFixUp::kFixGreekCategories;
     fix[    2LL << 32 |  8492LL << 16 ] = // N1,Nplus,NHD,Vouli
@@ -1429,6 +1398,16 @@ static void init_fixup(FixupMap &fix)
         EITFixUp::kFixGreekEIT |              // cut in db. Will move to descr.
         EITFixUp::kFixGreekCategories;
 
+    //DVB-S & S2 Greek Nova Provider
+    // Hotbird 11823H DVB-S
+    fix[ 5500LL << 32 |  318LL << 16 ] = EITFixUp::kEFixForceISO8859_7;
+    // Hotbird 11938H DVB-S
+    fix[ 6100LL << 32 |  318LL << 16 ] = EITFixUp::kEFixForceISO8859_7;
+    // Hotbird 12130H DVB-S2
+    fix[ 7100LL << 32 |  318LL << 16 ] = EITFixUp::kEFixForceISO8859_7;
+    // Hotbird 12169H DVB-S2
+    fix[ 7300LL << 32 |  318LL << 16 ] = EITFixUp::kEFixForceISO8859_7;
+
     // DVB-S Star One C2 70W (Brazil)
     // it has original_network_id = 1 like Astra on 19.2E, but transport_id does
     // not collide at the moment
diff --git a/mythtv/libs/libmythtv/eithelper.h b/mythtv/libs/libmythtv/eithelper.h
index fc2cff469a..5c06615fac 100644
--- a/mythtv/libs/libmythtv/eithelper.h
+++ b/mythtv/libs/libmythtv/eithelper.h
@@ -67,11 +67,9 @@ class ATSCEtt
     time_t scan_time;
 };
 
-
 typedef QMap<uint,ATSCEvent>               EventIDToATSCEvent;
 typedef QMap<uint,ATSCEtt>                 EventIDToETT;
 typedef QMap<uint,EventIDToATSCEvent>      ATSCSRCToEvents;
-typedef QMap<uint,EventIDToETT>            ATSCSRCToETTs;
 typedef QMap<unsigned long long,uint>      ServiceToChanID;
 
 typedef uint64_t                           FixupKey;
@@ -153,7 +151,6 @@ class EITHelper
 
     FixupMap fixup;
     ATSCSRCToEvents         incomplete_events;
-    ATSCSRCToETTs           unmatched_etts;
 
     MythDeque<DBEventEIT*>     db_events;
 
diff --git a/mythtv/libs/libmythtv/mythplayer.cpp b/mythtv/libs/libmythtv/mythplayer.cpp
index 5b80c452a3..9b766d5852 100644
--- a/mythtv/libs/libmythtv/mythplayer.cpp
+++ b/mythtv/libs/libmythtv/mythplayer.cpp
@@ -223,6 +223,9 @@ MythPlayer::MythPlayer(PlayerFlags flags)
       rtcbase(0),
       maxtcval(0), maxtcframes(0),
       numdroppedframes(0),
+      prior_audiotimecode(0),
+      prior_videotimecode(0),
+      m_timeOffsetBase(0),
       // LiveTVChain stuff
       m_tv(nullptr),                isDummy(false),
       // Counter for buffering messages
@@ -415,6 +418,8 @@ bool MythPlayer::Play(float speed, bool normal, bool unpauseaudio)
         return false;
     }
     rtcbase = 0;
+    prior_audiotimecode = 0;
+    prior_videotimecode = 0;
     SetEof(kEofStateNone);
     UnpauseBuffer();
     UnpauseDecoder();
@@ -912,15 +917,17 @@ void MythPlayer::OpenDummy(void)
 
 void MythPlayer::CreateDecoder(char *testbuf, int testreadsize)
 {
-    if (NuppelDecoder::CanHandle(testbuf, testreadsize))
-        SetDecoder(new NuppelDecoder(this, *player_ctx->playingInfo));
-    else if (AvFormatDecoder::CanHandle(testbuf,
+    if (AvFormatDecoder::CanHandle(testbuf,
                                         player_ctx->buffer->GetFilename(),
                                         testreadsize))
     {
         SetDecoder(new AvFormatDecoder(this, *player_ctx->playingInfo,
                                        playerFlags));
     }
+    else if (NuppelDecoder::CanHandle(testbuf, testreadsize))
+    {
+        SetDecoder(new NuppelDecoder(this, *player_ctx->playingInfo));
+    }
 }
 
 int MythPlayer::OpenFile(uint retries)
@@ -1806,6 +1813,8 @@ void MythPlayer::ResetAVSync(void)
     prevtc = 0;
     avsync_next = avsync_interval;      // Frames till next sync check
     rtcbase = 0;
+    prior_audiotimecode = 0;
+    prior_videotimecode = 0;
     LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC + "A/V sync reset");
 }
 
@@ -1822,6 +1831,8 @@ void MythPlayer::InitAVSync(void)
     // Number of frames over which to average time divergence
     avsync_averaging=4;
     rtcbase = 0;
+    prior_audiotimecode = 0;
+    prior_videotimecode = 0;
 
     // Special averaging default of 60 for OpenMAX passthru
     QString device = gCoreContext->GetSetting("AudioOutputDevice","");
@@ -2170,7 +2181,7 @@ void wait_for_time(int64_t framedue)
         QThread::usleep(delay);
 }
 
-#define AVSYNC_MAX_LATE 1000000
+#define AVSYNC_MAX_LATE 10000000
 void MythPlayer::AVSync2(VideoFrame *buffer)
 {
     if (videoOutput->IsErrored())
@@ -2180,7 +2191,6 @@ void MythPlayer::AVSync2(VideoFrame *buffer)
         SetErrored(tr("Failed to initialize A/V Sync"));
         return;
     }
-    int64_t audiotimecode = audio.GetAudioTime();
     int64_t videotimecode = 0;
 
     bool dropframe = false;
@@ -2191,10 +2201,10 @@ void MythPlayer::AVSync2(VideoFrame *buffer)
     int64_t unow = 0;
     int64_t lateness = 0;
     int64_t playspeed1000 = (float)1000 / play_speed;
+    bool reset = false;
 
     while (framedue == 0)
     {
-        bool reset = false;
         if (buffer)
         {
             videotimecode = buffer->timecode & 0x0000ffffffffffff;
@@ -2205,12 +2215,18 @@ void MythPlayer::AVSync2(VideoFrame *buffer)
 
         now = QDateTime::currentDateTimeUtc();
         unow = now.toMSecsSinceEpoch() * 1000;
+
+        if (!normal_speed || FlagIsSet(kMusicChoice))
+        {
+            framedue = unow + frame_interval;
+            break;
+        }
         // first time or after a seek - setup of rtcbase
         if (rtcbase == 0)
         {
             // cater for DVB radio
             if (videotimecode == 0)
-                videotimecode = audiotimecode;
+                videotimecode = audio.GetAudioTime();;
             // On first frame we get nothing, so exit out.
             if (videotimecode == 0)
                 return;
@@ -2218,6 +2234,7 @@ void MythPlayer::AVSync2(VideoFrame *buffer)
             maxtcval = 0;
             maxtcframes = 0;
             numdroppedframes = 0;
+            m_timeOffsetBase = TranslatePositionFrameToMs(framesPlayed, false) - videotimecode;
         }
 
         if (videotimecode == 0)
@@ -2239,29 +2256,44 @@ void MythPlayer::AVSync2(VideoFrame *buffer)
         else
             framedue = unow + frame_interval / 2;
 
+        // recalculate framesPlayed to conform to actual time code.
+        framesPlayed = TranslatePositionMsToFrame(videotimecode + m_timeOffsetBase, false);
+        decoder->SetFramesPlayed(framesPlayed);
+
         lateness = unow - framedue;
         dropframe = false;
         if (lateness > 30000)
-            dropframe = !FlagIsSet(kMusicChoice) && numdroppedframes < 10;
+            dropframe = numdroppedframes < 10;
 
-        if (lateness <= 30000 && audiotimecode > 0 && normal_speed && !FlagIsSet(kMusicChoice))
+        if (lateness <= 30000 && prior_audiotimecode > 0
+            && prior_videotimecode > 0)
         {
             // Get video in sync with audio
-            audio_adjustment = audiotimecode - videotimecode;
-            int sign = audio_adjustment < 0 ? -1 : 1;
-            if (audio_adjustment * sign > 40)
+            audio_adjustment = prior_audiotimecode - prior_videotimecode;
+            // If there is excess audio - throw it away.
+            if (audio_adjustment < -200)
             {
-                // adjust by AVSyncIncrementMS milliseconds at a time (range 1-40)
-                rtcbase -= (int64_t)1000000 * avsync2adjustms * sign / playspeed1000;
+                audio.Reset();
+                audio_adjustment = 0;
+            }
+            int sign = audio_adjustment < 0 ? -1 : 1;
+            int64_t fix_amount = audio_adjustment * sign;
+            if (fix_amount > avsync2adjustms)
+                fix_amount = avsync2adjustms;
+            // Faster catch-up when off by more than 200 ms
+            if (audio_adjustment * sign > 200)
+                // fix the sync within 15 - 20 frames
+                fix_amount = audio_adjustment * sign / 15;
+            int64_t speedup1000 = (float)1000 * play_speed;
+            rtcbase -= (int64_t)1000000 * fix_amount * sign / speedup1000;
+            if (audio_adjustment * sign > 20 || (framesPlayed % 400 == 0))
                 LOG(VB_PLAYBACK, LOG_INFO, LOC +
                     QString("AV Sync, audio ahead by %1 ms").arg(audio_adjustment));
-            }
-            if (audio_adjustment > 1000)
+            if (audio_adjustment > 200)
                 pause_audio = true;
         }
         // sanity check - reset rtcbase if time codes have gone crazy.
-        if ((lateness > AVSYNC_MAX_LATE || lateness < - AVSYNC_MAX_LATE)
-            && !FlagIsSet(kMusicChoice))
+        if ((lateness > AVSYNC_MAX_LATE || lateness < - AVSYNC_MAX_LATE))
         {
             framedue = 0;
             rtcbase = 0;
@@ -2277,7 +2309,7 @@ void MythPlayer::AVSync2(VideoFrame *buffer)
             reset = true;
         }
     }
-
+    prior_videotimecode = videotimecode;
     disp_timecode = videotimecode;
 
     output_jmeter && output_jmeter->RecordCycleTime();
@@ -2307,17 +2339,6 @@ void MythPlayer::AVSync2(VideoFrame *buffer)
         audio.Pause(true);
     }
 
-    LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC +
-        QString("A/V timecodes audio=%1 video=%2 frameinterval=%3 "
-                "audioadj=%4 tcoffset=%5 unow=%6 udue=%7")
-            .arg(audiotimecode)
-            .arg(videotimecode)
-            .arg(frame_interval)
-            .arg(audio_adjustment)
-            .arg(tc_wrap[TC_AUDIO])
-            .arg(unow)
-            .arg(framedue)
-                );
 
     if (dropframe)
         numdroppedframes++;
@@ -2338,6 +2359,8 @@ void MythPlayer::AVSync2(VideoFrame *buffer)
         // the primary PBP will become out of sync
         if (!player_ctx->IsPBP() || player_ctx->IsPrimaryPBP())
             wait_for_time(framedue);
+        // get time codes for calculating difference next time
+        prior_audiotimecode = audio.GetAudioTime();
         videoOutput->Show(ps);
         if (videoOutput->IsErrored())
         {
@@ -2373,6 +2396,19 @@ void MythPlayer::AVSync2(VideoFrame *buffer)
     }
     else
         wait_for_time(framedue);
+
+    LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC +
+        QString("A/V timecodes audio=%1 video=%2 frameinterval=%3 "
+                "audioadj=%4 tcoffset=%5 unow=%6 udue=%7")
+            .arg(prior_audiotimecode)
+            .arg(prior_videotimecode)
+            .arg(frame_interval)
+            .arg(audio_adjustment)
+            .arg(tc_wrap[TC_AUDIO])
+            .arg(unow)
+            .arg(framedue)
+                );
+
 }
 
 void MythPlayer::RefreshPauseFrame(void)
@@ -2451,7 +2487,6 @@ bool MythPlayer::PrebufferEnoughFrames(int min_buffers)
     if (!videoOutput)
         return false;
 
-    bool paused_now = false;
     if (!(min_buffers ? (videoOutput->ValidVideoFrames() >= min_buffers) :
                         (GetEof() != kEofStateNone) ||
                         (videoOutput->hasHWAcceleration() ?
@@ -2467,19 +2502,20 @@ bool MythPlayer::PrebufferEnoughFrames(int min_buffers)
         // for the jerking is detected.
 
         bool watchingTV = IsWatchingInprogress();
-        if (!paused_now && (livetv || watchingTV) && !FlagIsSet(kMusicChoice))
+        if ( (livetv || watchingTV) && !FlagIsSet(kMusicChoice))
         {
             uint64_t frameCount = GetCurrentFrameCount();
             uint64_t framesLeft = frameCount - framesPlayed;
             uint64_t margin = (uint64_t) (video_frame_rate * 3);
             if (framesLeft < margin)
             {
-                LOG(VB_PLAYBACK, LOG_NOTICE, LOC +
-                    QString("Pause to allow live tv catch up. Position in sec. Current: %2, Total: %3")
-                    .arg(framesPlayed).arg(frameCount));
+                if (rtcbase)
+                    LOG(VB_PLAYBACK, LOG_NOTICE, LOC +
+                        QString("Pause to allow live tv catch up. Position in sec. Current: %2, Total: %3")
+                        .arg(framesPlayed).arg(frameCount));
                 audio.Pause(true);
                 avsync_audiopaused = true;
-                paused_now = true;
+                rtcbase = 0;
             }
         }
         usleep(frame_interval >> 3);
@@ -2499,7 +2535,7 @@ bool MythPlayer::PrebufferEnoughFrames(int min_buffers)
                     QString("Waited %1ms for video buffers %2")
                         .arg(waited_for).arg(videoOutput->GetFrameStatus()));
             buffering_last_msg = QTime::currentTime();
-            if (audio.GetAudioBufferedTime() > 2000 && framesPlayed < 5
+            if (audio.IsBufferAlmostFull() && framesPlayed < 5
                 && gCoreContext->GetBoolSetting("MusicChoiceEnabled", false))
             {
                 playerFlags = (PlayerFlags)(playerFlags | kMusicChoice);
@@ -3143,8 +3179,8 @@ void MythPlayer::JumpToProgram(void)
         pginfo->GetPlaybackURL(), RingBuffer::kLiveTVOpenTimeout);
     QString subfn = player_ctx->buffer->GetSubtitleFilename();
     TVState desiredState = player_ctx->GetState();
-    bool isInProgress =
-        desiredState == kState_WatchingRecording || kState_WatchingLiveTV;
+    bool isInProgress = (desiredState == kState_WatchingRecording ||
+                         desiredState == kState_WatchingLiveTV);
     if (GetSubReader())
         GetSubReader()->LoadExternalSubtitles(subfn, isInProgress &&
                                               !subfn.isEmpty());
diff --git a/mythtv/libs/libmythtv/mythplayer.h b/mythtv/libs/libmythtv/mythplayer.h
index 7358d617a9..088da8a04e 100644
--- a/mythtv/libs/libmythtv/mythplayer.h
+++ b/mythtv/libs/libmythtv/mythplayer.h
@@ -856,6 +856,9 @@ class MTV_PUBLIC MythPlayer
     int       maxtcframes;    // number of frames seen since max to date tc
     int64_t   avsync2adjustms; // number of milliseconds to adjust for av sync errors
     int       numdroppedframes; // number of consecutive dropped frames.
+    int64_t   prior_audiotimecode;    // time code from prior frame
+    int64_t   prior_videotimecode;    // time code from prior frame
+    int64_t   m_timeOffsetBase;
 
     // LiveTV
     TV *m_tv;
diff --git a/mythtv/libs/libmythtv/openglvideo.cpp b/mythtv/libs/libmythtv/openglvideo.cpp
index 8b572b8d76..484c3f9a66 100644
--- a/mythtv/libs/libmythtv/openglvideo.cpp
+++ b/mythtv/libs/libmythtv/openglvideo.cpp
@@ -297,6 +297,7 @@ void OpenGLVideo::CheckResize(bool deinterlacing, bool allow)
     {
         RemoveFilter(kGLFilterResize);
         AddFilter(kGLFilterBicubic);
+        SetFiltering();
         return;
     }
 
@@ -304,6 +305,7 @@ void OpenGLVideo::CheckResize(bool deinterlacing, bool allow)
     {
         RemoveFilter(kGLFilterBicubic);
         AddFilter(kGLFilterResize);
+        SetFiltering();
         return;
     }
 
diff --git a/mythtv/libs/libmythtv/playercontext.cpp b/mythtv/libs/libmythtv/playercontext.cpp
index a373d690f8..8e113c1e81 100644
--- a/mythtv/libs/libmythtv/playercontext.cpp
+++ b/mythtv/libs/libmythtv/playercontext.cpp
@@ -421,8 +421,8 @@ bool PlayerContext::CreatePlayer(TV *tv, QWidget *widget,
     else
     {
         QString subfn = buffer->GetSubtitleFilename();
-        bool isInProgress =
-            desiredState == kState_WatchingRecording || kState_WatchingLiveTV;
+        bool isInProgress = (desiredState == kState_WatchingRecording ||
+                             desiredState == kState_WatchingLiveTV);
         if (!subfn.isEmpty() && player->GetSubReader())
             player->GetSubReader()->LoadExternalSubtitles(subfn, isInProgress);
     }
diff --git a/mythtv/libs/libmythtv/programdata.cpp b/mythtv/libs/libmythtv/programdata.cpp
index b46a15d64b..f8b9791375 100644
--- a/mythtv/libs/libmythtv/programdata.cpp
+++ b/mythtv/libs/libmythtv/programdata.cpp
@@ -759,7 +759,7 @@ uint DBEvent::UpdateDB(
     {
         query.prepare(
             "INSERT IGNORE INTO programrating "
-            "       ( chanid, starttime, system, rating) "
+            "       ( chanid, starttime, `system`, rating) "
             "VALUES (:CHANID, :START,    :SYS,  :RATING)");
         query.bindValue(":CHANID", chanid);
         query.bindValue(":START",  starttime);
@@ -1062,7 +1062,7 @@ uint DBEvent::InsertDB(MSqlQuery &query, uint chanid) const
     {
         query.prepare(
             "INSERT IGNORE INTO programrating "
-            "       ( chanid, starttime, system, rating) "
+            "       ( chanid, starttime, `system`, rating) "
             "VALUES (:CHANID, :START,    :SYS,  :RATING)");
         query.bindValue(":CHANID", chanid);
         query.bindValue(":START",  starttime);
@@ -1231,7 +1231,7 @@ uint ProgInfo::InsertDB(MSqlQuery &query, uint chanid) const
     {
         query.prepare(
             "INSERT IGNORE INTO programrating "
-            "       ( chanid, starttime, system, rating) "
+            "       ( chanid, starttime, `system`, rating) "
             "VALUES (:CHANID, :START,    :SYS,  :RATING)");
         query.bindValue(":CHANID", chanid);
         query.bindValue(":START",  starttime);
diff --git a/mythtv/libs/libmythtv/recorders/ExternalStreamHandler.cpp b/mythtv/libs/libmythtv/recorders/ExternalStreamHandler.cpp
index 4f541d30e5..74aea1419d 100644
--- a/mythtv/libs/libmythtv/recorders/ExternalStreamHandler.cpp
+++ b/mythtv/libs/libmythtv/recorders/ExternalStreamHandler.cpp
@@ -719,8 +719,6 @@ void ExternalStreamHandler::run(void)
 
         if (read_len == 0)
         {
-            std::this_thread::sleep_for(std::chrono::microseconds(50));
-
             if (!nodata_timer.isRunning())
                 nodata_timer.start();
             else
@@ -731,15 +729,20 @@ void ExternalStreamHandler::run(void)
                         "No data for 50 seconds, Restarting stream.");
                     if (!RestartStream())
                     {
-                        LOG(VB_RECORD, LOG_ERR, LOC + "Failed to restart stream.");
+                        LOG(VB_RECORD, LOG_ERR, LOC +
+                            "Failed to restart stream.");
                         _error = true;
                     }
                     nodata_timer.stop();
                     continue;
                 }
-
-                std::this_thread::sleep_for(std::chrono::milliseconds(50));
             }
+
+            std::this_thread::sleep_for(std::chrono::milliseconds(50));
+
+            // HLS type streams may only produce data every ~10 seconds
+            if (nodata_timer.elapsed() < 12000 && buffer.size() < TS_PACKET_SIZE)
+                continue;
         }
         else
         {
@@ -1138,7 +1141,7 @@ bool ExternalStreamHandler::StartStreaming(void)
 
     if (StreamingCount() == 0)
     {
-        if (!ProcessCommand("StartStreaming", result, 10000))
+        if (!ProcessCommand("StartStreaming", result, 15000))
         {
             LogLevel_t level = LOG_ERR;
             if (result.toLower().startsWith("warn"))
@@ -1168,8 +1171,6 @@ bool ExternalStreamHandler::StartStreaming(void)
 
 bool ExternalStreamHandler::StopStreaming(void)
 {
-    QString result;
-
     QMutexLocker locker(&m_stream_lock);
 
     LOG(VB_RECORD, LOG_INFO, LOC +
@@ -1206,7 +1207,8 @@ bool ExternalStreamHandler::StopStreaming(void)
         return false;
     }
 
-    if (!ProcessCommand("StopStreaming", result, 6000))
+    QString result;
+    if (!ProcessCommand("StopStreaming", result, 10000))
     {
         LogLevel_t level = LOG_ERR;
         if (result.toLower().startsWith("warn"))
diff --git a/mythtv/libs/libmythtv/recorders/dvbchannel.cpp b/mythtv/libs/libmythtv/recorders/dvbchannel.cpp
index a21d35df3f..70a354c67a 100644
--- a/mythtv/libs/libmythtv/recorders/dvbchannel.cpp
+++ b/mythtv/libs/libmythtv/recorders/dvbchannel.cpp
@@ -122,7 +122,11 @@ DVBChannel::~DVBChannel()
         if (!master_map[key].empty())
             new_master = dynamic_cast<DVBChannel*>(master_map[key].front());
         if (new_master)
+        {
+            QMutexLocker master_locker(&(master->hw_lock));
+            QMutexLocker new_master_locker(&(new_master->hw_lock));
             new_master->is_open = master->is_open;
+        }
     }
     else
     {
@@ -147,14 +151,14 @@ void DVBChannel::Close(DVBChannel *who)
 {
     LOG(VB_CHANNEL, LOG_INFO, LOC + "Closing DVB channel");
 
+    QMutexLocker locker(&hw_lock);
+
     IsOpenMap::iterator it = is_open.find(who);
     if (it == is_open.end())
         return; // this caller didn't have it open in the first place..
 
     is_open.erase(it);
 
-    QMutexLocker locker(&hw_lock);
-
     DVBChannel *master = GetMasterLock();
     if (master != nullptr && master != this)
     {
@@ -371,6 +375,8 @@ bool DVBChannel::Open(DVBChannel *who)
 
 bool DVBChannel::IsOpen(void) const
 {
+    //Have to acquire the hw lock to prevent is_open being modified whilst we're searching it
+    QMutexLocker locker(&hw_lock);
     IsOpenMap::const_iterator it = is_open.find(this);
     return it != is_open.end();
 }
diff --git a/mythtv/libs/libmythtv/recorders/hdhrstreamhandler.cpp b/mythtv/libs/libmythtv/recorders/hdhrstreamhandler.cpp
index 9eb32c9c78..88ad4be5c3 100644
--- a/mythtv/libs/libmythtv/recorders/hdhrstreamhandler.cpp
+++ b/mythtv/libs/libmythtv/recorders/hdhrstreamhandler.cpp
@@ -117,16 +117,20 @@ void HDHRStreamHandler::run(void)
 {
     RunProlog();
 
-    /* Create TS socket. */
-    if (!hdhomerun_device_stream_start(_hdhomerun_device))
     {
-        LOG(VB_GENERAL, LOG_ERR, LOC +
-            "Starting recording (set target failed). Aborting.");
-        _error = true;
-        RunEpilog();
-        return;
+        QMutexLocker locker(&_hdhr_lock);
+
+        /* Create TS socket. */
+        if (!hdhomerun_device_stream_start(_hdhomerun_device))
+        {
+            LOG(VB_GENERAL, LOG_ERR, LOC +
+                "Starting recording (set target failed). Aborting.");
+            _error = true;
+            RunEpilog();
+            return;
+        }
+        hdhomerun_device_stream_flush(_hdhomerun_device);
     }
-    hdhomerun_device_stream_flush(_hdhomerun_device);
 
     SetRunning(true, false, false);
 
@@ -188,7 +192,10 @@ void HDHRStreamHandler::run(void)
 
     RemoveAllPIDFilters();
 
-    hdhomerun_device_stream_stop(_hdhomerun_device);
+    {
+        QMutexLocker locker(&_hdhr_lock);
+        hdhomerun_device_stream_stop(_hdhomerun_device);
+    }
 
     if (VERBOSE_LEVEL_CHECK(VB_RECORD, LOG_INFO))
     {
@@ -476,6 +483,8 @@ QString HDHRStreamHandler::TunerSet(
 
 void HDHRStreamHandler::GetTunerStatus(struct hdhomerun_tuner_status_t *status)
 {
+    QMutexLocker locker(&_hdhr_lock);
+
     hdhomerun_device_get_tuner_status(_hdhomerun_device, nullptr, status);
 }
 
diff --git a/mythtv/libs/libmythtv/recorders/hlsstreamhandler.cpp b/mythtv/libs/libmythtv/recorders/hlsstreamhandler.cpp
index 12e9a09ab0..880cadeb29 100644
--- a/mythtv/libs/libmythtv/recorders/hlsstreamhandler.cpp
+++ b/mythtv/libs/libmythtv/recorders/hlsstreamhandler.cpp
@@ -181,7 +181,7 @@ void HLSStreamHandler::run(void)
         {
             LOG(VB_RECORD, LOG_INFO, LOC +
                 QString("Packet not starting with SYNC Byte (got 0x%1)")
-                .arg((char)m_readbuffer[0], 2, QLatin1Char('0')));
+                .arg((char)m_readbuffer[0], 2, 16, QLatin1Char('0')));
             continue;
         }
 
diff --git a/mythtv/libs/libmythtv/recorders/recorderbase.cpp b/mythtv/libs/libmythtv/recorders/recorderbase.cpp
index 24a5ff4630..024d81efbc 100644
--- a/mythtv/libs/libmythtv/recorders/recorderbase.cpp
+++ b/mythtv/libs/libmythtv/recorders/recorderbase.cpp
@@ -391,7 +391,14 @@ void RecorderBase::CheckForRingBufferSwitch(void)
     nextRingBufferLock.unlock();
 
     if (recq && tvrec)
+    {
+        // This call will free recq.
         tvrec->RingBufferChanged(ringBuffer, curRecording, recq);
+    }
+    else
+    {
+        delete recq;
+    }
 
     ringBufferCheckTimer.restart();
 }
diff --git a/mythtv/libs/libmythtv/recordinginfo.cpp b/mythtv/libs/libmythtv/recordinginfo.cpp
index f93da0bbea..143b37f431 100644
--- a/mythtv/libs/libmythtv/recordinginfo.cpp
+++ b/mythtv/libs/libmythtv/recordinginfo.cpp
@@ -1305,11 +1305,12 @@ void RecordingInfo::AddHistory(bool resched, bool forcedup, bool future)
     result.prepare("REPLACE INTO oldrecorded (chanid,starttime,"
                    "endtime,title,subtitle,description,season,episode,"
                    "category,seriesid,programid,inetref,findid,recordid,"
-                   "station,rectype,recstatus,duplicate,reactivate,future) "
+                   "station,rectype,recstatus,duplicate,reactivate,generic,"
+                   "future) "
                    "VALUES(:CHANID,:START,:END,:TITLE,:SUBTITLE,:DESC,:SEASON,"
                    ":EPISODE,:CATEGORY,:SERIESID,:PROGRAMID,:INETREF,"
                    ":FINDID,:RECORDID,:STATION,:RECTYPE,:RECSTATUS,:DUPLICATE,"
-                   ":REACTIVATE,:FUTURE);");
+                   ":REACTIVATE,:GENERIC,:FUTURE);");
     result.bindValue(":CHANID", chanid);
     result.bindValue(":START", startts);
     result.bindValue(":END", endts);
@@ -1329,6 +1330,7 @@ void RecordingInfo::AddHistory(bool resched, bool forcedup, bool future)
     result.bindValue(":RECSTATUS", rs);
     result.bindValue(":DUPLICATE", dup);
     result.bindValue(":REACTIVATE", 0);
+    result.bindValue(":GENERIC", IsGeneric());
     result.bindValue(":FUTURE", future);
 
     if (!result.exec())
diff --git a/mythtv/libs/libmythtv/ringbuffer.cpp b/mythtv/libs/libmythtv/ringbuffer.cpp
index bd8aa4f18e..36ad31099b 100644
--- a/mythtv/libs/libmythtv/ringbuffer.cpp
+++ b/mythtv/libs/libmythtv/ringbuffer.cpp
@@ -1085,7 +1085,7 @@ void RingBuffer::run(void)
             uint64_t bps = !sr_elapsed ? 1000000001 :
                            (uint64_t)(((double)read_return * 8000.0) /
                                       (double)sr_elapsed);
-            LOG(VB_FILE, LOG_INFO, LOC +
+            LOG(VB_FILE, LOG_DEBUG, LOC +
                 QString("safe_read(...@%1, %2) -> %3, took %4 ms %5 avg %6 ms")
                     .arg(rbwposcopy).arg(totfree).arg(read_return)
                     .arg(sr_elapsed)
@@ -1271,6 +1271,8 @@ void RingBuffer::run(void)
     rbrlock.unlock();
     rwlock.unlock();
 
+    LOG(VB_FILE, LOG_INFO, LOC + QString("Exiting readahead thread"));
+
     RunEpilog();
 }
 
diff --git a/mythtv/libs/libmythtv/subtitlescreen.cpp b/mythtv/libs/libmythtv/subtitlescreen.cpp
index c210b28efd..f0322fe1ff 100644
--- a/mythtv/libs/libmythtv/subtitlescreen.cpp
+++ b/mythtv/libs/libmythtv/subtitlescreen.cpp
@@ -2335,9 +2335,6 @@ void SubtitleScreen::AddScaledImage(QImage &img, QRect &pos)
 #ifdef USING_LIBASS
 static void myth_libass_log(int level, const char *fmt, va_list vl, void */*ctx*/)
 {
-    static QString full_line("libass:");
-    static const int msg_len = 255;
-    static QMutex string_lock;
     uint64_t verbose_mask = VB_GENERAL;
     LogLevel_t verbose_level = LOG_INFO;
 
@@ -2366,25 +2363,21 @@ static void myth_libass_log(int level, const char *fmt, va_list vl, void */*ctx*
     if (!VERBOSE_LEVEL_CHECK(verbose_mask, verbose_level))
         return;
 
+    static QMutex string_lock;
     string_lock.lock();
 
-    char str[msg_len+1];
-    int bytes = vsnprintf(str, msg_len+1, fmt, vl);
+    char str[1024];
+    int bytes = vsnprintf(str, sizeof str, fmt, vl);
     // check for truncated messages and fix them
-    if (bytes > msg_len)
+    int truncated = bytes - ((sizeof str)-1);
+    if (truncated > 0)
     {
         LOG(VB_GENERAL, LOG_ERR,
             QString("libASS log output truncated %1 of %2 bytes written")
-                .arg(msg_len).arg(bytes));
-        str[msg_len-1] = '\n';
+            .arg(truncated).arg(bytes));
     }
 
-    full_line += QString(str);
-    if (full_line.endsWith("\n"))
-    {
-        LOG(verbose_mask, verbose_level, full_line.trimmed());
-        full_line.truncate(0);
-    }
+    LOG(verbose_mask, verbose_level, QString("libass: %s").arg(str));
     string_lock.unlock();
 }
 
@@ -2412,7 +2405,17 @@ bool SubtitleScreen::InitialiseAssLibrary(void)
         if (!m_assRenderer)
             return false;
 
-        ass_set_fonts(m_assRenderer, nullptr, "sans-serif", 1, nullptr, 1);
+#ifdef Q_OS_ANDROID
+        // fontconfig doesn't yet work for us on Android.  For the
+        // time being, more explicitly set a font we know should
+        // exist.  This was adapted from VLC master as of 2019-01-21.
+        const char *psz_font = "/system/fonts/DroidSans.ttf";
+        const char *psz_font_family = "Droid Sans";
+#else
+        const char *psz_font = nullptr;
+        const char *psz_font_family = "sans-serif";
+#endif
+        ass_set_fonts(m_assRenderer, psz_font, psz_font_family, 1, nullptr, 1);
         ass_set_hinting(m_assRenderer, ASS_HINTING_LIGHT);
         LOG(VB_PLAYBACK, LOG_INFO, LOC + "Initialised libass renderer.");
     }
diff --git a/mythtv/libs/libmythtv/tv_play.cpp b/mythtv/libs/libmythtv/tv_play.cpp
index ea0aa806d5..7a58929fed 100644
--- a/mythtv/libs/libmythtv/tv_play.cpp
+++ b/mythtv/libs/libmythtv/tv_play.cpp
@@ -605,10 +605,37 @@ void TV::InitKeys(void)
             "Change Group View"), "");
     REG_KEY("TV Frontend", ACTION_LISTRECORDEDEPISODES, QT_TRANSLATE_NOOP("MythControls",
             "List recorded episodes"), "");
+    /*
+     * TODO DB update needs to perform the necessary conversion and delete
+     * the following upgrade code and replace bkmKeys and togBkmKeys  with "" in the
+     * REG_KEY for ACTION_SETBOOKMARK and ACTION_TOGGLEBOOKMARK.
+     */
+    // Bookmarks - Instead of SELECT to add or toggle,
+    // Use separate bookmark actions. This code is to convert users
+    // who may already be using SELECT. If they are not already using
+    // this frontend then nothing will be assigned to bookmark actions.
+    QString bkmKeys;
+    QString togBkmKeys;
+    // Check if this is a new frontend - if PAUSE returns
+    // "?" then frontend is new, never used before, so we will not assign
+    // any default bookmark keys
+    QString testKey = GetMythMainWindow()->GetKey("TV Playback", ACTION_PAUSE);
+    if (testKey != "?")
+    {
+        int alternate = gCoreContext->GetNumSetting("AltClearSavedPosition",0);
+        QString selectKeys = GetMythMainWindow()->GetKey("Global", ACTION_SELECT);
+        if (selectKeys != "?")
+        {
+            if (alternate)
+                togBkmKeys = selectKeys;
+            else
+                bkmKeys = selectKeys;
+        }
+    }
     REG_KEY("TV Playback", ACTION_SETBOOKMARK, QT_TRANSLATE_NOOP("MythControls",
-            "Add Bookmark"), "");
+            "Add Bookmark"), bkmKeys);
     REG_KEY("TV Playback", ACTION_TOGGLEBOOKMARK, QT_TRANSLATE_NOOP("MythControls",
-            "Toggle Bookmark"), "");
+            "Toggle Bookmark"), togBkmKeys);
     REG_KEY("TV Playback", "BACK", QT_TRANSLATE_NOOP("MythControls",
             "Exit or return to DVD menu"), "Esc");
     REG_KEY("TV Playback", ACTION_MENUCOMPACT, QT_TRANSLATE_NOOP("MythControls",
diff --git a/mythtv/libs/libmythtv/tv_rec.cpp b/mythtv/libs/libmythtv/tv_rec.cpp
index 421aff6823..00c7968a35 100644
--- a/mythtv/libs/libmythtv/tv_rec.cpp
+++ b/mythtv/libs/libmythtv/tv_rec.cpp
@@ -1198,7 +1198,9 @@ void TVRec::CloseChannel(void)
 {
     if (channel &&
         ((genOpt.inputtype == "DVB" && dvbOpt.dvb_on_demand) ||
-         genOpt.inputtype == "FREEBOX" || genOpt.inputtype == "VBOX" ||
+         genOpt.inputtype == "FREEBOX" ||
+         genOpt.inputtype == "VBOX" ||
+         genOpt.inputtype == "HDHOMERUN" ||
          CardUtil::IsV4L(genOpt.inputtype)))
     {
         channel->Close();
diff --git a/mythtv/libs/libmythtv/videodev2.h b/mythtv/libs/libmythtv/videodev2.h
index c64b5e5a30..6c2e9bb41c 100644
--- a/mythtv/libs/libmythtv/videodev2.h
+++ b/mythtv/libs/libmythtv/videodev2.h
@@ -59,15 +59,14 @@
 #include <sys/time.h>
 
 #ifdef __FreeBSD__
-#include <linux/input.h>	// For __[us][0-9]+ types
-#define __le64 __u64
-#define __le32 __u32
-#define __le16 __u16
-#define __le8  __u8
-#define __be64 __u64
-#define __be32 __u32
-#define __be16 __u16
-#define __be8  __u8
+typedef uint64_t __u64;
+typedef uint32_t __u32;
+typedef uint16_t __u16;
+typedef uint8_t __u8;
+typedef int64_t __s64;
+typedef int32_t __s32;
+typedef int16_t __s16;
+typedef int8_t __s8;
 #else
 #include <linux/ioctl.h>
 #include <linux/types.h>
@@ -1906,7 +1905,7 @@ struct v4l2_mpeg_vbi_itv0_line {
 } __attribute__ ((packed));
 
 struct v4l2_mpeg_vbi_itv0 {
-	__le32 linemask[2]; /* Bitmasks of VBI service lines present */
+	__u32 linemask[2]; /* Bitmasks of VBI service lines present */
 	struct v4l2_mpeg_vbi_itv0_line line[35];
 } __attribute__ ((packed));
 
diff --git a/mythtv/libs/libmythtv/videooutwindow.cpp b/mythtv/libs/libmythtv/videooutwindow.cpp
index a38a2786e5..2d7b2a3408 100644
--- a/mythtv/libs/libmythtv/videooutwindow.cpp
+++ b/mythtv/libs/libmythtv/videooutwindow.cpp
@@ -952,7 +952,9 @@ void VideoOutWindow::ToggleMoveBottomLine(void)
 {
     if (bottomline)
     {
+        mz_move.setX(0);
         mz_move.setY(0);
+        mz_scale_h = 1.0;
         mz_scale_v = 1.0;
         bottomline = false;
     }
@@ -960,11 +962,21 @@ void VideoOutWindow::ToggleMoveBottomLine(void)
     {
         const float zf = 0.02;
 
-        int y = gCoreContext->GetNumSetting("OSDMoveBottomLine", 4);
+        int x = gCoreContext->GetNumSetting("OSDMoveXBottomLine", 0);
+        mz_move.setX(x);
+
+        int y = gCoreContext->GetNumSetting("OSDMoveYBottomLine", 5);
         mz_move.setY(y);
-        double z = static_cast<double>
-                   (gCoreContext->GetNumSetting("OSDZoomBottomLine", 112)) / 100.0;
-        mz_scale_v = snap(z, 1.0f, zf / 2);
+
+        double h = static_cast<double>
+                   (gCoreContext->GetNumSetting("OSDScaleHBottomLine", 100)) /
+                   100.0;
+        mz_scale_h = snap(h, 1.0f, zf / 2);
+
+        double v = static_cast<double>
+                   (gCoreContext->GetNumSetting("OSDScaleVBottomLine", 112)) /
+                   100.0;
+        mz_scale_v = snap(v, 1.0f, zf / 2);
 
         bottomline = true;
     }
@@ -974,8 +986,11 @@ void VideoOutWindow::ToggleMoveBottomLine(void)
 
 void VideoOutWindow::SaveBottomLine(void)
 {
-    gCoreContext->SaveSetting("OSDMoveBottomLine", GetMzMove().y());
-    gCoreContext->SaveSetting("OSDZoomBottomLine", GetMzScaleV() * 100.0f);
+    gCoreContext->SaveSetting("OSDMoveXBottomLine", GetMzMove().x());
+    gCoreContext->SaveSetting("OSDMoveYBottomLine", GetMzMove().y());
+
+    gCoreContext->SaveSetting("OSDScaleHBottomLine", GetMzScaleH() * 100.0f);
+    gCoreContext->SaveSetting("OSDScaleVBottomLine", GetMzScaleV() * 100.0f);
 }
 
 QString VideoOutWindow::GetZoomString(void) const
diff --git a/mythtv/libs/libmythtv/videosource.cpp b/mythtv/libs/libmythtv/videosource.cpp
index f5be42d142..f206478089 100644
--- a/mythtv/libs/libmythtv/videosource.cpp
+++ b/mythtv/libs/libmythtv/videosource.cpp
@@ -159,7 +159,7 @@ class SchedGroup : public MythUICheckBoxSetting
         MythUICheckBoxSetting(new CardInputDBStorage(this, parent, "schedgroup"))
     {
         setLabel(QObject::tr("Schedule as group"));
-        setValue(false);
+        setValue(true);
         setHelpText(
             QObject::tr(
                 "Schedule all virtual inputs on this device as a group.  "
@@ -3192,9 +3192,6 @@ CardInput::CardInput(const QString & cardtype, const QString & device,
             this,     SLOT(  SetSourceID (const QString&)));
     connect(ingrpbtn, SIGNAL(clicked()),
             this,     SLOT(  CreateNewInputGroup()));
-    if (schedgroup)
-        connect(instancecount, SIGNAL(valueChanged(const QString &)),
-                this,     SLOT(UpdateSchedGroup(const QString &)));
 }
 
 CardInput::~CardInput()
@@ -3216,14 +3213,6 @@ void CardInput::SetSourceID(const QString &sourceid)
     srcfetch->setEnabled(enable);
 }
 
-void CardInput::UpdateSchedGroup(const QString &val)
-{
-    int value = val.toInt();
-    if (value <= 1)
-        schedgroup->setValue(false);
-    schedgroup->setEnabled(value > 1);
-}
-
 QString CardInput::getSourceName(void) const
 {
     return sourceid->getValueLabel();
diff --git a/mythtv/libs/libmythtv/videosource.h b/mythtv/libs/libmythtv/videosource.h
index 51e15e1dec..bb17f61e82 100644
--- a/mythtv/libs/libmythtv/videosource.h
+++ b/mythtv/libs/libmythtv/videosource.h
@@ -863,7 +863,6 @@ class CardInput : public GroupSetting
     void channelScanner();
     void sourceFetch();
     void SetSourceID(const QString &sourceid);
-    void UpdateSchedGroup(const QString &value);
     void CreateNewInputGroupSlot(const QString &name);
 
   private:
diff --git a/mythtv/libs/libmythui/mythfontproperties.cpp b/mythtv/libs/libmythui/mythfontproperties.cpp
index 4e7fa3516a..5354f9229f 100644
--- a/mythtv/libs/libmythui/mythfontproperties.cpp
+++ b/mythtv/libs/libmythui/mythfontproperties.cpp
@@ -9,6 +9,7 @@
 #include <QFontInfo>
 #include <QFontDatabase>
 #include <QRect>
+#include <QRegularExpression>
 
 #include "mythlogging.h"
 #include "mythdb.h"
@@ -432,7 +433,9 @@ MythFontProperties *MythFontProperties::ParseFromXml(
     newFont->Unfreeze();
 
     QFontInfo fi(newFont->m_face);
-    if (newFont->m_face.family() != fi.family())
+    QString fi_family =
+        fi.family().remove(QRegularExpression("\\[.*]")).trimmed();
+    if (newFont->m_face.family() != fi_family)
     {
         VERBOSE_XML(VB_GENERAL, LOG_ERR, filename, element,
                     QString("Failed to load '%1', got '%2' instead")
diff --git a/mythtv/libs/libmythui/mythrender_opengl2es.cpp b/mythtv/libs/libmythui/mythrender_opengl2es.cpp
index e27ec76f0f..65e2fcc168 100644
--- a/mythtv/libs/libmythui/mythrender_opengl2es.cpp
+++ b/mythtv/libs/libmythui/mythrender_opengl2es.cpp
@@ -20,6 +20,8 @@ void MythRenderOpenGL2ES::InitProcs()
     // GLSL ES precision qualifiers
     m_qualifiers = "#ifdef GL_FRAGMENT_PRECISION_HIGH\n"
                    "precision highp float;\n"
+                   "#else\n"
+                   "precision mediump float;\n"
                    "#endif\n";
 
     // Default OpenGL ES 2.0
diff --git a/mythtv/libs/libmythui/mythuiguidegrid.cpp b/mythtv/libs/libmythui/mythuiguidegrid.cpp
index 4fbd523e4a..994401adb4 100644
--- a/mythtv/libs/libmythui/mythuiguidegrid.cpp
+++ b/mythtv/libs/libmythui/mythuiguidegrid.cpp
@@ -329,10 +329,13 @@ void MythUIGuideGrid::DrawSelf(MythPainter *p, int xoffset, int yoffset,
                 drawBox(p, xoffset, yoffset, data, m_recordingColor, alphaMod);
             else
                 drawBox(p, xoffset, yoffset, data, m_conflictingColor, alphaMod);
+            drawText(p, xoffset, yoffset, data, alphaMod);
         }
     }
 
     drawCurrent(p, xoffset, yoffset, &m_selectedItem, alphaMod);
+    // Redraw the current selection's text in case it was clobbered by the above call.
+    drawText(p, xoffset, yoffset, &m_selectedItem, alphaMod);
 
     for (int i = 0; i < m_rowCount; i++)
     {
@@ -341,10 +344,7 @@ void MythUIGuideGrid::DrawSelf(MythPainter *p, int xoffset, int yoffset,
         for (; it != m_allData[i].end(); ++it)
         {
             UIGTCon *data = *it;
-            drawText(p, xoffset, yoffset, data, alphaMod);
-
-            if (data->m_recType != 0 || data->m_arrow != GridTimeNormal)
-                drawRecDecoration(p, xoffset, yoffset, data, alphaMod);
+            drawRecDecoration(p, xoffset, yoffset, data, alphaMod);
         }
     }
 }
@@ -747,12 +747,14 @@ bool MythUIGuideGrid::parseDefaultCategoryColors(QMap<QString, QString> &catColo
             break;
     }
 
+#ifndef Q_OS_ANDROID // Android does not get a file handle for assets file system
     if (f.handle() == -1)
     {
         LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unable to open '%1'")
             .arg(f.fileName()));
         return false;
     }
+#endif
 
     QDomDocument doc;
     QString errorMsg;
diff --git a/mythtv/libs/libmythui/mythuiwebbrowser.cpp b/mythtv/libs/libmythui/mythuiwebbrowser.cpp
index 0ebcb89daa..8b5fbad19c 100644
--- a/mythtv/libs/libmythui/mythuiwebbrowser.cpp
+++ b/mythtv/libs/libmythui/mythuiwebbrowser.cpp
@@ -861,6 +861,10 @@ void MythUIWebBrowser::Finalize(void)
  */
 void MythUIWebBrowser::Init(void)
 {
+    // only do the initialisation for widgets not being stored in the global object store
+    if (parent() == GetGlobalObjectStore())
+        return;
+
     if (m_initialized)
         return;
 
@@ -1116,6 +1120,9 @@ void MythUIWebBrowser::SetBackgroundColor(QColor color)
  */
 void MythUIWebBrowser::SetActive(bool active)
 {
+    if (!m_browser)
+        return;
+
     if (m_active == active)
         return;
 
@@ -1318,6 +1325,9 @@ QVariant MythUIWebBrowser::evaluateJavaScript(const QString &scriptSource)
 
 void MythUIWebBrowser::Scroll(int dx, int dy)
 {
+    if (!m_browser)
+        return;
+
     QPoint startPos = m_browser->page()->currentFrame()->scrollPosition();
     QPoint endPos = startPos + QPoint(dx, dy);
 
@@ -1427,6 +1437,9 @@ bool MythUIWebBrowser::IsOnTopScreen(void)
 
 void MythUIWebBrowser::UpdateScrollBars(void)
 {
+    if (!m_browser)
+        return;
+
     QPoint position = m_browser->page()->currentFrame()->scrollPosition();
     if (m_verticalScrollbar)
     {
@@ -1453,7 +1466,7 @@ void MythUIWebBrowser::UpdateBuffer(void)
 {
     UpdateScrollBars();
 
-    if (!m_image)
+    if (!m_image || !m_browser)
         return;
 
     if (!m_active || (m_active && !m_browser->hasFocus()))
@@ -1472,6 +1485,9 @@ void MythUIWebBrowser::UpdateBuffer(void)
  */
 void MythUIWebBrowser::Pulse(void)
 {
+    if (!m_browser)
+        return;
+
     if (m_scrollAnimation.IsActive() &&
         m_destinationScrollPos !=
         m_browser->page()->currentFrame()->scrollPosition())
@@ -1514,6 +1530,9 @@ void MythUIWebBrowser::DrawSelf(MythPainter *p, int xoffset, int yoffset,
  */
 bool MythUIWebBrowser::keyPressEvent(QKeyEvent *event)
 {
+    if (!m_browser)
+        return false;
+
     QStringList actions;
     bool handled = false;
     handled = GetMythMainWindow()->TranslateKeyPress("Browser", event, actions);
diff --git a/mythtv/libs/libmythupnp/serializers/serializer.cpp b/mythtv/libs/libmythupnp/serializers/serializer.cpp
index e86d406ee5..28a84fab89 100644
--- a/mythtv/libs/libmythupnp/serializers/serializer.cpp
+++ b/mythtv/libs/libmythupnp/serializers/serializer.cpp
@@ -71,7 +71,8 @@ void Serializer::Serialize( const QVariant &vValue, const QString &_sName )
     if ((sName.length() > 0) && sName.at(0) == 'Q')
         sName = sName.mid( 1 );
 
-    m_hash.reset();
+    if ( !vValue.isNull() )
+        m_hash.addData( vValue.toString().toUtf8() );
 
     BeginSerialize( sName );
 
diff --git a/mythtv/programs/mythbackend/autoexpire.cpp b/mythtv/programs/mythbackend/autoexpire.cpp
index e05ab7cbbe..b6e16f8e8e 100644
--- a/mythtv/programs/mythbackend/autoexpire.cpp
+++ b/mythtv/programs/mythbackend/autoexpire.cpp
@@ -36,7 +36,7 @@ using namespace std;
 #include "remoteutil.h"
 #include "remoteencoder.h"
 #include "encoderlink.h"
-#include "backendutil.h"
+#include "requesthandler/fileserverutil.h"
 #include "mainserver.h"
 #include "compat.h"
 #include "mythlogging.h"
@@ -141,7 +141,16 @@ void AutoExpire::CalcParams()
 
     instance_lock.lock();
     if (main_server)
-        main_server->GetFilesystemInfos(fsInfos);
+    {
+        // The scheduler relies on something forcing the mainserver
+        // fsinfos cache to get updated periodically.  Currently, that
+        // is done here.  Don't remove or change this invocation
+        // without handling that issue too.  It is done this way
+        // because the scheduler thread can't afford to be blocked by
+        // an unresponsive, remote filesystem and the autoexpirer
+        // thrad can.
+        main_server->GetFilesystemInfos(fsInfos, false);
+    }
     instance_lock.unlock();
 
     if (fsInfos.empty())
@@ -415,7 +424,7 @@ void AutoExpire::ExpireRecordings(void)
     LOG(VB_FILE, LOG_INFO, LOC + "ExpireRecordings()");
 
     if (main_server)
-        main_server->GetFilesystemInfos(fsInfos);
+        main_server->GetFilesystemInfos(fsInfos, true);
 
     if (fsInfos.empty())
     {
diff --git a/mythtv/programs/mythbackend/backendutil.cpp b/mythtv/programs/mythbackend/backendutil.cpp
deleted file mode 100644
index 7ad22dae16..0000000000
--- a/mythtv/programs/mythbackend/backendutil.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-
-#include <cstdlib> // for llabs
-
-#include "mythconfig.h"
-#if CONFIG_DARWIN || defined(__FreeBSD__)
-#include <sys/param.h>
-#include <sys/mount.h>
-#elif __linux__
-#include <sys/vfs.h>
-#endif
-
-#include <QMutex>
-#include <QFile>
-#include <QMap>
-
-#include "backendutil.h"
-#include "programinfo.h"
-
-QMutex recordingPathLock;
-QMap <QString, QString> recordingPathCache;
-
-QString GetPlaybackURL(ProgramInfo *pginfo, bool storePath)
-{
-    QString result = "";
-    QMutexLocker locker(&recordingPathLock);
-    QString cacheKey = QString("%1:%2").arg(pginfo->GetChanID())
-        .arg(pginfo->GetRecordingStartTime(MythDate::ISODate));
-    if ((recordingPathCache.contains(cacheKey)) &&
-        (QFile::exists(recordingPathCache[cacheKey])))
-    {
-        result = recordingPathCache[cacheKey];
-        if (!storePath)
-            recordingPathCache.remove(cacheKey);
-    }
-    else
-    {
-        locker.unlock();
-        result = pginfo->GetPlaybackURL(false, true);
-        locker.relock();
-        if (storePath && result.startsWith("/"))
-            recordingPathCache[cacheKey] = result;
-    }
-
-    return result;
-}
-
-/* vim: set expandtab tabstop=4 shiftwidth=4: */
diff --git a/mythtv/programs/mythbackend/backendutil.h b/mythtv/programs/mythbackend/backendutil.h
deleted file mode 100644
index c43f13953c..0000000000
--- a/mythtv/programs/mythbackend/backendutil.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef _BACKENDUTIL_H
-#define _BACKENDUTIL_H
-
-#include <vector>
-using namespace std;
-
-#include <QMap>
-
-#include "remoteutil.h"
-
-class QStringList;
-class EncoderLink;
-
-void BackendQueryDiskSpace(QStringList &strlist,
-                           QMap <int, EncoderLink *> *encoderList,
-                           bool consolidated = false, bool allHosts = false);
-
-QString GetPlaybackURL(ProgramInfo *pginfo, bool storePath = true);
-
-#endif
-
-/* vim: set expandtab tabstop=4 shiftwidth=4: */
diff --git a/mythtv/programs/mythbackend/encoderlink.cpp b/mythtv/programs/mythbackend/encoderlink.cpp
index d76301dcbc..422ce90f35 100644
--- a/mythtv/programs/mythbackend/encoderlink.cpp
+++ b/mythtv/programs/mythbackend/encoderlink.cpp
@@ -11,7 +11,7 @@
 #include "playbacksock.h"
 #include "tv_rec.h"
 #include "mythdate.h"
-#include "backendutil.h"
+#include "requesthandler/fileserverutil.h"
 #include "compat.h"
 #include "referencecounter.h"
 #include "inputinfo.h"                  // for InputInfo
diff --git a/mythtv/programs/mythbackend/filetransfer.cpp b/mythtv/programs/mythbackend/filetransfer.cpp
index 29b226f611..4a879f2234 100644
--- a/mythtv/programs/mythbackend/filetransfer.cpp
+++ b/mythtv/programs/mythbackend/filetransfer.cpp
@@ -19,7 +19,7 @@ FileTransfer::FileTransfer(QString &filename, MythSocket *remote,
 {
     pginfo = new ProgramInfo(filename);
     pginfo->MarkAsInUse(true, kFileTransferInUseID);
-    if (rbuffer)
+    if (rbuffer && rbuffer->IsOpen())
         rbuffer->Start();
 }
 
diff --git a/mythtv/programs/mythbackend/httpconfig.cpp b/mythtv/programs/mythbackend/httpconfig.cpp
index 48ccff8eb7..24ea02274e 100644
--- a/mythtv/programs/mythbackend/httpconfig.cpp
+++ b/mythtv/programs/mythbackend/httpconfig.cpp
@@ -7,7 +7,6 @@
 
 // MythTV headers
 #include "httpconfig.h"
-#include "backendutil.h"
 #include "mythcontext.h"
 #include "mythdb.h"
 #include "mythdirs.h"
diff --git a/mythtv/programs/mythbackend/httpstatus.cpp b/mythtv/programs/mythbackend/httpstatus.cpp
index 10cbbf2c30..24224ef0c6 100644
--- a/mythtv/programs/mythbackend/httpstatus.cpp
+++ b/mythtv/programs/mythbackend/httpstatus.cpp
@@ -19,7 +19,6 @@
 // Qt headers
 #include <QTextStream>
 #include <QRegExp>
-#include <QLocale>
 
 // MythTV headers
 #include "httpstatus.h"
@@ -1266,38 +1265,38 @@ int HttpStatus::PrintMachineInfo( QTextStream &os, QDomElement info )
 
                 os << "        <li>Total Disk Space:\r\n"
                 << "          <ul>\r\n";
-                QLocale c(QLocale::C);
 
                 os << "            <li>Total Space: ";
-                sRep = c.toString(nTotal) + " MB";
+                sRep = QString("%L1").arg(nTotal) + " MB";
                 os << sRep << "</li>\r\n";
 
                 os << "            <li>Space Used: ";
-                sRep = c.toString(nUsed) + " MB";
+                sRep = QString("%L1").arg(nUsed) + " MB";
                 os << sRep << "</li>\r\n";
 
                 os << "            <li>Space Free: ";
-                sRep = c.toString(nFree) + " MB";
+                sRep = QString("%L1").arg(nFree) + " MB";
                 os << sRep << "</li>\r\n";
 
                 if ((nLiveTV + nDeleted + nExpirable) > 0)
                 {
                     os << "            <li>Space Available "
                           "After Auto-expire: ";
-                    sRep = c.toString(nFree + nLiveTV +
+                    sRep = QString("%L1").arg(nUsed) + " MB";
+                    sRep = QString("%L1").arg(nFree + nLiveTV +
                                       nDeleted + nExpirable) + " MB";
                     os << sRep << "\r\n";
                     os << "              <ul>\r\n";
                     os << "                <li>Space Used by LiveTV: ";
-                    sRep = c.toString(nLiveTV) + " MB";
+                    sRep = QString("%L1").arg(nLiveTV) + " MB";
                     os << sRep << "</li>\r\n";
                     os << "                <li>Space Used by "
                           "Deleted Recordings: ";
-                    sRep = c.toString(nDeleted) + " MB";
+                    sRep = QString("%L1").arg(nDeleted) + " MB";
                     os << sRep << "</li>\r\n";
                     os << "                <li>Space Used by "
                           "Auto-expirable Recordings: ";
-                    sRep = c.toString(nExpirable) + " MB";
+                    sRep = QString("%L1").arg(nExpirable) + " MB";
                     os << sRep << "</li>\r\n";
                     os << "              </ul>\r\n";
                     os << "            </li>\r\n";
@@ -1350,18 +1349,16 @@ int HttpStatus::PrintMachineInfo( QTextStream &os, QDomElement info )
 
                 os << nDir << "</li>\r\n";
 
-                QLocale c(QLocale::C);
-
                 os << "            <li>Total Space: ";
-                sRep = c.toString(nTotal) + " MB";
+                sRep = QString("%L1").arg(nTotal) + " MB";
                 os << sRep << "</li>\r\n";
 
                 os << "            <li>Space Used: ";
-                sRep = c.toString(nUsed) + " MB";
+                sRep = QString("%L1").arg(nUsed) + " MB";
                 os << sRep << "</li>\r\n";
 
                 os << "            <li>Space Free: ";
-                sRep = c.toString(nFree) + " MB";
+                sRep = QString("%L1").arg(nFree) + " MB";
                 os << sRep << "</li>\r\n";
 
                 os << "          </ul>\r\n"
diff --git a/mythtv/programs/mythbackend/mainserver.cpp b/mythtv/programs/mythbackend/mainserver.cpp
index b6ba9bb9c9..794ef06901 100644
--- a/mythtv/programs/mythbackend/mainserver.cpp
+++ b/mythtv/programs/mythbackend/mainserver.cpp
@@ -54,7 +54,7 @@ using namespace std;
 #include "server.h"
 #include "mthread.h"
 #include "scheduler.h"
-#include "backendutil.h"
+#include "requesthandler/fileserverutil.h"
 #include "programinfo.h"
 #include "mythtimezone.h"
 #include "recordinginfo.h"
@@ -323,7 +323,13 @@ MainServer::MainServer(bool master, int port,
     deferredDeleteTimer->start(30 * 1000);
 
     if (sched)
+    {
+        // Make sure we have a good, fsinfo cache before setting
+        // mainServer in the scheduler.
+        QList<FileSystemInfo> fsInfos;
+        GetFilesystemInfos(fsInfos, false);
         sched->SetMainServer(this);
+    }
     if (expirer)
         expirer->SetMainServer(this);
 
@@ -2039,7 +2045,9 @@ void MainServer::HandleAnnounce(QStringList &slist, QStringList commands,
             QString("adding: %1(%2) as a file transfer")
                                       .arg(commands[2])
                                       .arg(quintptr(socket),0,16));
+        sockListLock.lockForWrite();
         fileTransferList.push_back(ft);
+        sockListLock.unlock();
 
         retlist << QString::number(socket->GetSocketDescriptor());
         retlist << QString::number(ft->GetFileSize());
@@ -5341,8 +5349,17 @@ void MainServer::BackendQueryDiskSpace(QStringList &strlist, bool consolidated,
     }
 }
 
-void MainServer::GetFilesystemInfos(QList<FileSystemInfo> &fsInfos)
+void MainServer::GetFilesystemInfos(QList<FileSystemInfo> &fsInfos,
+                                    bool useCache)
 {
+    // Return cached information if requested.
+    if (useCache)
+    {
+        QMutexLocker locker(&fsInfosCacheLock);
+        fsInfos = fsInfosCache;
+        return;
+    }
+
     QStringList strlist;
     FileSystemInfo fsInfo;
 
@@ -5411,6 +5428,10 @@ void MainServer::GetFilesystemInfos(QList<FileSystemInfo> &fsInfos)
         LOG(VB_FILE | VB_SCHEDULE, LOG_INFO, LOC +
             "--- GetFilesystemInfos directory list end ---");
     }
+
+    // Save these results to the cache.
+    QMutexLocker locker(&fsInfosCacheLock);
+    fsInfosCache = fsInfos;
 }
 
 void MainServer::HandleMoveFile(PlaybackSock *pbs, const QString &storagegroup,
diff --git a/mythtv/programs/mythbackend/mainserver.h b/mythtv/programs/mythbackend/mainserver.h
index 4c6bc0b4b5..f32fe5fa6a 100644
--- a/mythtv/programs/mythbackend/mainserver.h
+++ b/mythtv/programs/mythbackend/mainserver.h
@@ -143,7 +143,8 @@ class MainServer : public QObject, public MythSocketCBs
     size_t GetCurrentMaxBitrate(void);
     void BackendQueryDiskSpace(QStringList &strlist, bool consolidated,
                                bool allHosts);
-    void GetFilesystemInfos(QList<FileSystemInfo> &fsInfos);
+    void GetFilesystemInfos(QList<FileSystemInfo> &fsInfos,
+                            bool useCache=true);
 
     int GetExitCode() const { return m_exitCode; }
 
@@ -352,6 +353,8 @@ class MainServer : public QObject, public MythSocketCBs
 
     QMap<QString, int> fsIDcache;
     QMutex fsIDcacheLock;
+    QList<FileSystemInfo> fsInfosCache;
+    QMutex fsInfosCacheLock;
 
     QMutex                     m_downloadURLsLock;
     QMap<QString, QString>     m_downloadURLs;
diff --git a/mythtv/programs/mythbackend/mythbackend.pro b/mythtv/programs/mythbackend/mythbackend.pro
index 6d6e1083c4..9dde76944d 100644
--- a/mythtv/programs/mythbackend/mythbackend.pro
+++ b/mythtv/programs/mythbackend/mythbackend.pro
@@ -27,7 +27,6 @@ QMAKE_CLEAN += $(TARGET)
 # Input
 HEADERS += autoexpire.h encoderlink.h filetransfer.h httpstatus.h mainserver.h
 HEADERS += playbacksock.h scheduler.h server.h backendhousekeeper.h
-HEADERS += backendutil.h
 HEADERS += upnpcdstv.h upnpcdsmusic.h upnpcdsvideo.h mediaserver.h
 HEADERS += internetContent.h main_helpers.h backendcontext.h
 HEADERS += httpconfig.h mythsettings.h commandlineparser.h
@@ -45,7 +44,7 @@ HEADERS += services/capture.h services/image.h services/music.h
 
 SOURCES += autoexpire.cpp encoderlink.cpp filetransfer.cpp httpstatus.cpp
 SOURCES += main.cpp mainserver.cpp playbacksock.cpp scheduler.cpp server.cpp
-SOURCES += backendhousekeeper.cpp backendutil.cpp
+SOURCES += backendhousekeeper.cpp
 SOURCES += upnpcdstv.cpp upnpcdsmusic.cpp upnpcdsvideo.cpp mediaserver.cpp
 SOURCES += internetContent.cpp main_helpers.cpp backendcontext.cpp
 SOURCES += httpconfig.cpp mythsettings.cpp commandlineparser.cpp
diff --git a/mythtv/programs/mythbackend/scheduler.cpp b/mythtv/programs/mythbackend/scheduler.cpp
index aaacb87205..838d8e8b75 100644
--- a/mythtv/programs/mythbackend/scheduler.cpp
+++ b/mythtv/programs/mythbackend/scheduler.cpp
@@ -33,7 +33,6 @@ using namespace std;
 #include "encoderlink.h"
 #include "mainserver.h"
 #include "remoteutil.h"
-#include "backendutil.h"
 #include "mythdate.h"
 #include "exitcodes.h"
 #include "mythcontext.h"
@@ -96,8 +95,6 @@ Scheduler::Scheduler(bool runthread, QMap<int, EncoderLink *> *tvList,
 
     InitInputInfoMap();
 
-    fsInfoCacheFillTime = MythDate::current().addSecs(-1000);
-
     if (doRun)
     {
         ProgramInfo::CheckProgramIDAuthorities();
@@ -2474,8 +2471,6 @@ bool Scheduler::HandleReschedule(void)
                 static_cast<double>(placeTime));
     LOG(VB_GENERAL, LOG_INFO, msg);
 
-    fsInfoCacheFillTime = MythDate::current().addSecs(-1000);
-
     // Write changed entries to oldrecorded.
     RecIter it = reclist.begin();
     for ( ; it != reclist.end(); ++it)
@@ -2673,6 +2668,7 @@ bool Scheduler::HandleRecording(
 
     QDateTime curtime     = MythDate::current();
     QDateTime nextrectime = ri.GetRecordingStartTime();
+    int origprerollseconds = prerollseconds;
 
     if (ri.GetRecordingStatus() != RecStatus::WillRecord &&
         ri.GetRecordingStatus() != RecStatus::Pending)
@@ -2844,21 +2840,18 @@ bool Scheduler::HandleRecording(
 
     if (ri.GetRecordingStatus() != RecStatus::Pending)
     {
-        if (sinputinfomap[ri.GetInputID()].schedgroup)
+        if (!AssignGroupInput(tempri, origprerollseconds))
         {
-            if (!AssignGroupInput(tempri))
-            {
-                // We failed to assign an input.  Keep asking the main
-                // server to add one until we get one.
-                MythEvent me(QString("ADD_CHILD_INPUT %1")
-                             .arg(tempri.GetInputID()));
-                gCoreContext->dispatch(me);
-                nextWakeTime = min(nextWakeTime, curtime.addSecs(1));
-                return reclist_changed;
-            }
-            ri.SetInputID(tempri.GetInputID());
-            nexttv = (*m_tvList)[ri.GetInputID()];
+            // We failed to assign an input.  Keep asking the main
+            // server to add one until we get one.
+            MythEvent me(QString("ADD_CHILD_INPUT %1")
+                         .arg(tempri.GetInputID()));
+            gCoreContext->dispatch(me);
+            nextWakeTime = min(nextWakeTime, curtime.addSecs(1));
+            return reclist_changed;
         }
+        ri.SetInputID(tempri.GetInputID());
+        nexttv = (*m_tvList)[ri.GetInputID()];
 
         ri.SetRecordingStatus(RecStatus::Pending);
         tempri.SetRecordingStatus(RecStatus::Pending);
@@ -2953,7 +2946,8 @@ void Scheduler::HandleRecordingStatusChange(
     }
 }
 
-bool Scheduler::AssignGroupInput(RecordingInfo &ri)
+bool Scheduler::AssignGroupInput(RecordingInfo &ri,
+                                 int prerollseconds)
 {
     if (!sinputinfomap[ri.GetInputID()].schedgroup)
         return true;
@@ -2982,7 +2976,8 @@ bool Scheduler::AssignGroupInput(RecordingInfo &ri)
         for ( ; j != reclist.end(); ++j)
         {
             RecordingInfo *p = (*j);
-            if (now.secsTo(p->GetRecordingStartTime()) > 300)
+            if (now.secsTo(p->GetRecordingStartTime()) >
+                prerollseconds + 60)
                 break;
             if (p->GetInputID() != inputid)
                 continue;
@@ -3089,6 +3084,13 @@ bool Scheduler::AssignGroupInput(RecordingInfo &ri)
     return bestid;
 }
 
+// Called to delay shutdown for 5 minutes
+void Scheduler::DelayShutdown()
+{
+    m_delayShutdownTime = QDateTime::currentMSecsSinceEpoch() + 5*60*1000;
+}
+
+
 void Scheduler::HandleIdleShutdown(
     bool &blockShutdown, QDateTime &idleSince,
     int prerollseconds, int idleTimeoutSecs, int idleWaitForRecordingTime,
@@ -3111,7 +3113,12 @@ void Scheduler::HandleIdleShutdown(
     // the frontend may have connected then gone idle between scheduler runs
     if (blockShutdown)
     {
-        if (m_mainServer->isClientConnected())
+        schedLock.unlock();
+        bool b = m_mainServer->isClientConnected();
+        schedLock.lock();
+        if (reclist_changed)
+            return;
+        if (b)
         {
             LOG(VB_GENERAL, LOG_NOTICE, "Client is connected, removing startup block on shutdown");
             blockShutdown = false;
@@ -3119,10 +3126,14 @@ void Scheduler::HandleIdleShutdown(
     }
     else
     {
+        // Check for delay shutdown request
+        bool delay = (m_delayShutdownTime > QDateTime::currentMSecsSinceEpoch());
+
         QDateTime curtime = MythDate::current();
 
         // find out, if we are currently recording (or LiveTV)
         bool recording = false;
+        schedLock.unlock();
         TVRec::inputsLock.lockForRead();
         QMap<int, EncoderLink *>::Iterator it;
         for (it = m_tvList->begin(); (it != m_tvList->end()) &&
@@ -3135,7 +3146,10 @@ void Scheduler::HandleIdleShutdown(
 
         // If there are BLOCKING clients, then we're not idle
         bool blocking = m_mainServer->isClientConnected(true);
-        if (!blocking && !recording)
+        schedLock.lock();
+        if (reclist_changed)
+            return;
+        if (!blocking && !recording && !delay)
         {
             // have we received a RESET_IDLETIME message?
             resetIdleTime_lock.lock();
@@ -3277,6 +3291,10 @@ void Scheduler::HandleIdleShutdown(
                 LOG(logmask, LOG_NOTICE, "Blocking shutdown because "
                                          "of a connected client");
 
+            if (delay)
+                LOG(logmask, LOG_NOTICE, "Blocking shutdown because "
+                                         "of delay request from external application");
+
             // not idle, make the time invalid
             if (idleSince.isValid())
             {
@@ -4477,8 +4495,9 @@ void Scheduler::AddNewRecords(void)
         "    capturecard.hostname, recordmatch.oldrecstatus, NULL, "//43-45
         "    oldrecstatus.future, capturecard.schedorder, " //46-47
         "    p.syndicatedepisodenumber, p.partnumber, p.parttotal, " //48-50
-        "    c.mplexid, capturecard.displayname, ") +      //51-52
-        pwrpri + QString(                                  //53
+        "    c.mplexid, capturecard.displayname,         "//51-52
+        "    p.season, p.episode, p.totalepisodes, ") +   //53-55
+        pwrpri + QString(                                  //56
         "FROM recordmatch "
         "INNER JOIN RECTABLE ON (recordmatch.recordid = RECTABLE.recordid) "
         "INNER JOIN program AS p "
@@ -4552,9 +4571,9 @@ void Scheduler::AddNewRecords(void)
             result.value(5).toString(),//subtitle
             QString(),//sortsubtitle
             result.value(6).toString(),//description
-            0, // season
-            0, // episode
-            0, // total episodes
+            result.value(53).toInt(), // season
+            result.value(54).toInt(), // episode
+            result.value(55).toInt(), // total episodes
             result.value(48).toString(),//synidcatedepisode
             result.value(11).toString(),//category
 
@@ -4623,7 +4642,7 @@ void Scheduler::AddNewRecords(void)
             p->SetRecordingStatus(p->oldrecstatus);
         }
 
-        p->SetRecordingPriority2(result.value(53).toInt());
+        p->SetRecordingPriority2(result.value(56).toInt());
 
         // Check to see if the program is currently recording and if
         // the end time was changed.  Ideally, checking for a new end
@@ -5587,18 +5606,14 @@ int Scheduler::FillRecordingDir(
     return fsID;
 }
 
-void Scheduler::FillDirectoryInfoCache(bool force)
+void Scheduler::FillDirectoryInfoCache(void)
 {
-    if ((!force) &&
-        (fsInfoCacheFillTime > MythDate::current().addSecs(-180)))
-        return;
-
     QList<FileSystemInfo> fsInfos;
 
     fsInfoCache.clear();
 
     if (m_mainServer)
-        m_mainServer->GetFilesystemInfos(fsInfos);
+        m_mainServer->GetFilesystemInfos(fsInfos, true);
 
     QMap <int, bool> fsMap;
     QList<FileSystemInfo>::iterator it1;
@@ -5611,8 +5626,6 @@ void Scheduler::FillDirectoryInfoCache(bool force)
     LOG(VB_FILE, LOG_INFO, LOC +
         QString("FillDirectoryInfoCache: found %1 unique filesystems")
             .arg(fsMap.size()));
-
-    fsInfoCacheFillTime = MythDate::current();
 }
 
 void Scheduler::SchedLiveTV(void)
diff --git a/mythtv/programs/mythbackend/scheduler.h b/mythtv/programs/mythbackend/scheduler.h
index b583161483..062c0c4edb 100644
--- a/mythtv/programs/mythbackend/scheduler.h
+++ b/mythtv/programs/mythbackend/scheduler.h
@@ -122,6 +122,7 @@ class Scheduler : public MThread, public MythScheduler
     int GetError(void) const { return error; }
 
     void AddChildInput(uint parentid, uint childid);
+    void DelayShutdown();
 
   protected:
     void run(void) override; // MThread
@@ -201,7 +202,7 @@ class Scheduler : public MThread, public MythScheduler
                          uint cardid,
                          QString &recording_dir,
                          const RecList &reclist);
-    void FillDirectoryInfoCache(bool force = false);
+    void FillDirectoryInfoCache(void);
 
     void OldRecordedFixups(void);
     void ResetDuplicates(uint recordid, uint findid, const QString &title,
@@ -216,7 +217,7 @@ class Scheduler : public MThread, public MythScheduler
                          int prerollseconds);
     void HandleRecordingStatusChange(
         RecordingInfo &ri, RecStatus::Type recStatus, const QString &details);
-    bool AssignGroupInput(RecordingInfo &ri);
+    bool AssignGroupInput(RecordingInfo &ri, int prerollseconds);
     void HandleIdleShutdown(
         bool &blockShutdown, QDateTime &idleSince, int prerollseconds,
         int idleTimeoutSecs, int idleWaitForRecordingTime,
@@ -273,7 +274,6 @@ class Scheduler : public MThread, public MythScheduler
     bool m_isShuttingDown;
     MSqlQueryInfo dbConn;
 
-    QDateTime fsInfoCacheFillTime;
     QMap<QString, FileSystemInfo> fsInfoCache;
 
     int error;
@@ -284,6 +284,8 @@ class Scheduler : public MThread, public MythScheduler
     QDateTime livetvTime;
 
     QDateTime lastPrepareTime;
+    // Delay shutdown util this time (ms since epoch);
+    int64_t m_delayShutdownTime        {0};
 
     OpenEndType m_openEnd;
 
diff --git a/mythtv/programs/mythbackend/services/content.cpp b/mythtv/programs/mythbackend/services/content.cpp
index 3fddaed467..cb3afc320c 100644
--- a/mythtv/programs/mythbackend/services/content.cpp
+++ b/mythtv/programs/mythbackend/services/content.cpp
@@ -36,7 +36,7 @@
 #include "storagegroup.h"
 #include "programinfo.h"
 #include "previewgenerator.h"
-#include "backendutil.h"
+#include "requesthandler/fileserverutil.h"
 #include "httprequest.h"
 #include "serviceUtil.h"
 #include "mythdate.h"
diff --git a/mythtv/programs/mythbackend/services/dvr.cpp b/mythtv/programs/mythbackend/services/dvr.cpp
index 6866f9aead..7f0094671a 100644
--- a/mythtv/programs/mythbackend/services/dvr.cpp
+++ b/mythtv/programs/mythbackend/services/dvr.cpp
@@ -473,13 +473,21 @@ long Dvr::GetSavedBookmark( int RecordedId,
     uint64_t offset;
     bool isend=true;
     uint64_t position = ri.QueryBookmark();
+    // if no bookmark return 0
+    if (position == 0)
+        return 0;
     if (offsettype.toLower() == "position"){
-        ri.QueryKeyFramePosition(&offset, position, isend);
-        return offset;
+        // if bookmark cannot be converted to a keyframe we will
+        // just return the actual frame saved as the bookmark
+        if (ri.QueryKeyFramePosition(&offset, position, isend))
+            return offset;
     }
-    else if (offsettype.toLower() == "duration"){
-        ri.QueryKeyFrameDuration(&offset, position, isend);
-        return offset;
+    if (offsettype.toLower() == "duration"){
+        if (ri.QueryKeyFrameDuration(&offset, position, isend))
+            return offset;
+        else
+            // If bookmark cannot be converted to a duration return -1
+            return -1;
     }
     else
         return position;
diff --git a/mythtv/programs/mythbackend/services/myth.cpp b/mythtv/programs/mythbackend/services/myth.cpp
index 733db2ceb6..093849a20e 100644
--- a/mythtv/programs/mythbackend/services/myth.cpp
+++ b/mythtv/programs/mythbackend/services/myth.cpp
@@ -46,6 +46,7 @@
 #include "mythdate.h"
 #include "mythversion.h"
 #include "serviceUtil.h"
+#include "scheduler.h"
 
 /////////////////////////////////////////////////////////////////////////////
 //
@@ -941,6 +942,14 @@ bool Myth::CheckDatabase( bool repair )
     return bResult;
 }
 
+bool Myth::DelayShutdown( void )
+{
+    Scheduler *scheduler = dynamic_cast<Scheduler*>(gCoreContext->GetScheduler());
+    scheduler->DelayShutdown();
+    LOG(VB_GENERAL, LOG_NOTICE, "Shutdown delayed 5 minutes for external application.");
+    return true;
+}
+
 /////////////////////////////////////////////////////////////////////////////
 //
 /////////////////////////////////////////////////////////////////////////////
diff --git a/mythtv/programs/mythbackend/services/myth.h b/mythtv/programs/mythbackend/services/myth.h
index e22b14bfb9..7e704ade5b 100644
--- a/mythtv/programs/mythbackend/services/myth.h
+++ b/mythtv/programs/mythbackend/services/myth.h
@@ -129,6 +129,8 @@ class Myth : public MythServices
 
         bool                CheckDatabase       ( bool Repair ) override; // MythServices
 
+        bool                DelayShutdown       ( void ) override; // MythServices
+
         bool                ProfileSubmit       ( void ) override; // MythServices
 
         bool                ProfileDelete       ( void ) override; // MythServices
@@ -363,6 +365,13 @@ class ScriptableMyth : public QObject
             )
         }
 
+        bool DelayShutdown( void )
+        {
+            SCRIPT_CATCH_EXCEPTION( false,
+                return m_obj.DelayShutdown();
+            )
+        }
+
         bool ProfileSubmit( void )
         {
             SCRIPT_CATCH_EXCEPTION( false,
diff --git a/mythtv/programs/mythexternrecorder/MythExternRecApp.cpp b/mythtv/programs/mythexternrecorder/MythExternRecApp.cpp
index bc7a67cbb9..f4970d62ba 100644
--- a/mythtv/programs/mythexternrecorder/MythExternRecApp.cpp
+++ b/mythtv/programs/mythexternrecorder/MythExternRecApp.cpp
@@ -31,7 +31,8 @@
 
 MythExternRecApp::MythExternRecApp(const QString & command,
                                    const QString & conf_file,
-                                   const QString & log_file)
+                                   const QString & log_file,
+                                   const QString & logging)
     : QObject()
     , m_fatal(false)
     , m_run(true)
@@ -43,6 +44,7 @@ MythExternRecApp::MythExternRecApp(const QString & command,
     , m_lock_timeout(0)
     , m_scan_timeout(120000)
     , m_log_file(log_file)
+    , m_logging(logging)
     , m_config_ini(conf_file)
     , m_tuned(false)
     , m_chan_settings(nullptr)
@@ -146,7 +148,8 @@ bool MythExternRecApp::Open(void)
 
     qRegisterMetaType<QProcess::ExitStatus>("QProcess::ExitStatus");
     QObject::connect(&m_proc,
-                     static_cast<void (QProcess::*)(int,QProcess::ExitStatus exitStatus)>
+                     static_cast<void (QProcess::*)
+                     (int,QProcess::ExitStatus exitStatus)>
                      (&QProcess::finished),
                      this, &MythExternRecApp::ProcFinished);
 
@@ -391,7 +394,24 @@ Q_SLOT void MythExternRecApp::TuneChannel(const QString & serial,
     {
         m_command.replace("%URL%", url);
         LOG(VB_CHANNEL, LOG_DEBUG, LOC +
-            QString(": '%URL%' replaced in cmd: '%1'").arg(m_command));
+            QString(": '%URL%' replaced with '%1' in cmd: '%2'")
+            .arg(url).arg(m_command));
+    }
+
+    if (!m_log_file.isEmpty() && m_command.indexOf("%LOGFILE%") >= 0)
+    {
+        m_command.replace("%LOGFILE%", m_log_file);
+        LOG(VB_RECORD, LOG_DEBUG, LOC +
+            QString(": '%LOGFILE%' replaced with '%1' in cmd: '%2'")
+            .arg(m_log_file).arg(m_command));
+    }
+
+    if (!m_logging.isEmpty() && m_command.indexOf("%LOGGING%") >= 0)
+    {
+        m_command.replace("%LOGGING%", m_logging);
+        LOG(VB_RECORD, LOG_DEBUG, LOC +
+            QString(": '%LOGGING%' replaced with '%1' in cmd: '%2'")
+            .arg(m_logging).arg(m_command));
     }
 
     m_desc = m_rec_desc;
@@ -498,7 +518,7 @@ Q_SLOT void MythExternRecApp::StopStreaming(const QString & serial, bool silent)
     if (m_proc.state() == QProcess::Running)
     {
         m_proc.terminate();
-        m_proc.waitForFinished();
+        m_proc.waitForFinished(3000);
         m_proc.kill();
 
         LOG(VB_RECORD, LOG_INFO, LOC + ": External application terminated.");
diff --git a/mythtv/programs/mythexternrecorder/MythExternRecApp.h b/mythtv/programs/mythexternrecorder/MythExternRecApp.h
index 175a30b933..e1115122fd 100644
--- a/mythtv/programs/mythexternrecorder/MythExternRecApp.h
+++ b/mythtv/programs/mythexternrecorder/MythExternRecApp.h
@@ -35,7 +35,7 @@ class MythExternRecApp : public QObject
 
   public:
     MythExternRecApp(const QString & command, const QString & conf_file,
-                     const QString & log_file);
+                     const QString & log_file, const QString & logging);
     ~MythExternRecApp(void);
 
     bool Open(void);
@@ -108,6 +108,7 @@ class MythExternRecApp : public QObject
     uint      m_scan_timeout;
 
     QString   m_log_file;
+    QString   m_logging;
     QString   m_config_ini;
     QString   m_desc;
 
diff --git a/mythtv/programs/mythexternrecorder/external-ffmpeg.conf b/mythtv/programs/mythexternrecorder/external-ffmpeg.conf
index 934cc4459a..3804c1301b 100644
--- a/mythtv/programs/mythexternrecorder/external-ffmpeg.conf
+++ b/mythtv/programs/mythexternrecorder/external-ffmpeg.conf
@@ -2,6 +2,9 @@
 # The recorder command to execute.  %URL% is optional, and
 # will be replaced with the channel's "URL" as defined in the
 # [TUNER/channels] (channel conf) configuration file
+#
+# %LOGGING% will be replaced with mythtv logging params. For example:
+# --verbose general,channel,record --logpath /var/log/mythtv --loglevel info --quiet
 command=/opt/ffmpeg/bin/ffmpeg -hide_banner -nostats -loglevel panic -re -i \"%URL%\" -c:v copy -c:a copy -f mpegts -
 
 # Used in logging events
diff --git a/mythtv/programs/mythexternrecorder/external-twitch.conf b/mythtv/programs/mythexternrecorder/external-twitch.conf
index ed251fa6f9..d35bd188c0 100644
--- a/mythtv/programs/mythexternrecorder/external-twitch.conf
+++ b/mythtv/programs/mythexternrecorder/external-twitch.conf
@@ -2,6 +2,9 @@
 # The recorder command to execute.  %URL% is optional, and
 # will be replaced with the channel's "URL" as defined in the
 # [TUNER/channels] (channel conf) configuration file
+#
+# %LOGGING% will be replaced with mythtv logging params. For example:
+# --verbose general,channel,record --logpath /var/log/mythtv --loglevel info --quiet
 
 command="/usr/bin/youtube-dl --hls-use-mpegts --ffmpeg-location /opt/ffmpeg/bin --external-downloader-args \"-hide_banner -nostats -loglevel panic -re\" -o - \"%URL%\""
 
diff --git a/mythtv/programs/mythexternrecorder/external-vlc.conf b/mythtv/programs/mythexternrecorder/external-vlc.conf
index 843f848585..73ad30db02 100644
--- a/mythtv/programs/mythexternrecorder/external-vlc.conf
+++ b/mythtv/programs/mythexternrecorder/external-vlc.conf
@@ -2,6 +2,9 @@
 # The recorder command to execute.  %URL% is optional, and
 # will be replaced with the channel's "URL" as defined in the
 # [TUNER/channels] (channel conf) configuration file
+#
+# %LOGGING% will be replaced with mythtv logging params. For example:
+# --verbose general,channel,record --logpath /var/log/mythtv --loglevel info --quiet
 command="cvlc \"%URL%\" --sout \"#std{mux=ts,access=file,dst=-}\""
 
 # Used in logging events, %ARG% are replaced from the channel info
diff --git a/mythtv/programs/mythexternrecorder/main.cpp b/mythtv/programs/mythexternrecorder/main.cpp
index 77ba6fc16e..11a97d1dda 100644
--- a/mythtv/programs/mythexternrecorder/main.cpp
+++ b/mythtv/programs/mythexternrecorder/main.cpp
@@ -63,6 +63,7 @@ int main(int argc, char *argv[])
     if ((retval = cmdline.ConfigureLogging()) != GENERIC_EXIT_OK)
         return retval;
     QString logfile = cmdline.GetLogFilePath();
+    QString logging = logPropagateArgs;
 
     MythExternControl *control = new MythExternControl();
     MythExternRecApp  *process = nullptr;
@@ -70,12 +71,12 @@ int main(int argc, char *argv[])
     QString conf_file = cmdline.toString("conf");
     if (!conf_file.isEmpty())
     {
-        process = new MythExternRecApp("", conf_file, logfile);
+        process = new MythExternRecApp("", conf_file, logfile, logging);
     }
     else if (!cmdline.toString("exec").isEmpty())
     {
         QString command = cmdline.toString("exec");
-        process = new MythExternRecApp(command, "", logfile);
+        process = new MythExternRecApp(command, "", logfile, logging);
     }
     else if (!cmdline.toString("infile").isEmpty())
     {
@@ -83,7 +84,7 @@ int main(int argc, char *argv[])
         QString command = QString("ffmpeg -re -i \"%1\" "
                                   "-c:v copy -c:a copy -f mpegts -")
                           .arg(filename);
-        process = new MythExternRecApp(command, "", logfile);
+        process = new MythExternRecApp(command, "", logfile, logging);
     }
 
     QObject::connect(process, &MythExternRecApp::Opened,
diff --git a/mythtv/programs/mythfrontend/prevreclist.cpp b/mythtv/programs/mythfrontend/prevreclist.cpp
index d084efff92..33288df9af 100644
--- a/mythtv/programs/mythfrontend/prevreclist.cpp
+++ b/mythtv/programs/mythfrontend/prevreclist.cpp
@@ -245,7 +245,9 @@ bool PrevRecordedList::LoadTitles(void)
 
 bool PrevRecordedList::LoadDates(void)
 {
-    QString querystr = "SELECT DISTINCT YEAR(starttime), MONTH(starttime) "
+    QString querystr = "SELECT DISTINCT "
+            "YEAR(CONVERT_TZ(starttime,'UTC','SYSTEM')), "
+            "MONTH(CONVERT_TZ(starttime,'UTC','SYSTEM')) "
         "FROM oldrecorded "
         "WHERE oldrecorded.future = 0 " + m_where;
 
@@ -412,8 +414,11 @@ void PrevRecordedList::LoadShowsByTitle(void)
 {
     MSqlBindings bindings;
     QString sql = " AND oldrecorded.title = :TITLE " + m_where;
-    int selected = m_titleList->GetCurrentPos();
-    bindings[":TITLE"] = m_titleData[selected]->GetTitle();
+    uint selected = m_titleList->GetCurrentPos();
+    if (selected < m_titleData.size())
+        bindings[":TITLE"] = m_titleData[selected]->GetTitle();
+    else
+        bindings[":TITLE"] = "";
     if (!m_title.isEmpty())
         bindings[":MTITLE"] = m_title;
     m_showData.clear();
diff --git a/mythtv/programs/mythfrontend/progdetails.cpp b/mythtv/programs/mythfrontend/progdetails.cpp
index 9265292c35..4a9673017c 100644
--- a/mythtv/programs/mythfrontend/progdetails.cpp
+++ b/mythtv/programs/mythfrontend/progdetails.cpp
@@ -46,7 +46,7 @@ QString ProgDetails::getRatings(bool recorded, uint chanid, QDateTime startts)
 {
     QString table = (recorded) ? "recordedrating" : "programrating";
     QString sel = QString(
-        "SELECT system, rating FROM %1 "
+        "SELECT `system`, rating FROM %1 "
         "WHERE chanid  = :CHANID "
         "AND starttime = :STARTTIME").arg(table);
 
diff --git a/mythtv/programs/mythwelcome/main.cpp b/mythtv/programs/mythwelcome/main.cpp
index 8459b04a8a..4c1dbe9061 100644
--- a/mythtv/programs/mythwelcome/main.cpp
+++ b/mythtv/programs/mythwelcome/main.cpp
@@ -88,6 +88,8 @@ int main(int argc, char **argv)
 #endif
 
     gContext = new MythContext(MYTH_BINARY_VERSION, true);
+
+    cmdline.ApplySettingsOverride();
     if (!gContext->Init())
     {
         LOG(VB_GENERAL, LOG_ERR,
@@ -96,6 +98,8 @@ int main(int argc, char **argv)
         return GENERIC_EXIT_NO_MYTHCONTEXT;
     }
 
+    cmdline.ApplySettingsOverride();
+
     if (!MSqlQuery::testDBConnection())
     {
         LOG(VB_GENERAL, LOG_ERR,
diff --git a/mythtv/programs/scripts/hardwareprofile/smolt.py b/mythtv/programs/scripts/hardwareprofile/smolt.py
index d867772099..40a421e2b1 100644
--- a/mythtv/programs/scripts/hardwareprofile/smolt.py
+++ b/mythtv/programs/scripts/hardwareprofile/smolt.py
@@ -1176,6 +1176,8 @@ def read_cpuinfo():
 def read_memory():
     un = os.uname()
     kernel = un[2]
+    if kernel[:2] == "5.":
+        return read_memory_2_6()
     if kernel[:2] == "4.":
         return read_memory_2_6()
     if kernel[:2] == "3.":
diff --git a/mythtv/programs/scripts/hardwareprofile/software.py b/mythtv/programs/scripts/hardwareprofile/software.py
index b90384943c..b89ad58a1c 100644
--- a/mythtv/programs/scripts/hardwareprofile/software.py
+++ b/mythtv/programs/scripts/hardwareprofile/software.py
@@ -33,7 +33,7 @@ def read_lsb_release():
 initdefault_re = re.compile(r':(\d+):initdefault:')
 
 def read_runlevel():
-    defaultRunlevel = 'Unknown'
+    defaultRunlevel = '9'
     try:
         inittab = file('/etc/inittab').read()
         match = initdefault_re.search(inittab)
diff --git a/mythtv/programs/scripts/metadata/Movie/tmdb3.py b/mythtv/programs/scripts/metadata/Movie/tmdb3.py
index 3dd893d8b7..9739ecf320 100755
--- a/mythtv/programs/scripts/metadata/Movie/tmdb3.py
+++ b/mythtv/programs/scripts/metadata/Movie/tmdb3.py
@@ -29,6 +29,10 @@ __version__ = "0.3.7"
 
 from optparse import OptionParser
 import sys
+import signal
+
+def timeouthandler(signal, frame):
+    raise RuntimeError("Timed out")
 
 def buildSingle(inetref, opts):
     from MythTV.tmdb3.tmdb_exceptions import TMDBRequestInvalid
@@ -275,6 +279,9 @@ def main():
 
     opts, args = parser.parse_args()
 
+    signal.signal(signal.SIGALRM, timeouthandler)
+    signal.alarm(30)
+
     if opts.version:
         buildVersion()
 
@@ -312,14 +319,18 @@ def main():
         sys.stdout.write('ERROR: tmdb3.py requires exactly one non-empty argument')
         sys.exit(1)
 
-    if opts.movielist:
-        buildList(args[0], opts)
+    try:
+        if opts.movielist:
+            buildList(args[0], opts)
 
-    if opts.moviedata:
-        buildSingle(args[0], opts)
+        if opts.moviedata:
+            buildSingle(args[0], opts)
 
-    if opts.collectiondata:
-        buildCollection(args[0], opts)
+        if opts.collectiondata:
+            buildCollection(args[0], opts)
+    except RuntimeError, exc:
+        sys.stdout.write('ERROR: ' + str(exc) + ' exception')
+        sys.exit(1)
 
 if __name__ == '__main__':
     main()
diff --git a/mythtv/version.sh b/mythtv/version.sh
index fd2c0be875..d412cc0505 100755
--- a/mythtv/version.sh
+++ b/mythtv/version.sh
@@ -21,44 +21,64 @@ GITREPOPATH="exported"
 
 cd ${GITTREEDIR}
 
-git status > /dev/null 2>&1
-SOURCE_VERSION=$(git describe --dirty || git describe || echo Unknown)
+# if we have a mythtv/DESCRIBE file use that to get the branch and version
+if test -e $GITTREEDIR/DESCRIBE ; then
+    echo "Using $GITTREEDIR/DESCRIBE"
+    . $GITTREEDIR/DESCRIBE
+    echo "BRANCH: $BRANCH"
+    echo "SOURCE_VERSION: $SOURCE_VERSION"
+else
+    # get the branch and version from git or fall back to EXPORTED_VERSION then VERSION as last resort
+    git status > /dev/null 2>&1
+    SOURCE_VERSION=$(git describe --dirty || git describe || echo Unknown)
+    echo "SOURCE_VERSION: $SOURCE_VERSION"
 
-case "${SOURCE_VERSION}" in
-    exported|Unknown)
-        if ! grep -q Format $GITTREEDIR/EXPORTED_VERSION; then
-            . $GITTREEDIR/EXPORTED_VERSION
-            # This file has SOURCE_VERSION and BRANCH
-            # example SOURCE_VERSION="30d8a96"
-            # BRANCH examples from github
-            # BRANCH=" (HEAD -> master)"
-            # BRANCH=" (fixes/0.28)"
-            # BRANCH=" (tag: v0.28.1)"
-            # From a checkout they can be as follows:
-            # " (origin/fixes/0.28, fixes/0.28)"
-            # " (HEAD -> master, origin/master, origin/HEAD)"
-            # " (tag: v0.28.1)"
-            hash="$SOURCE_VERSION"
-            # This extracts after the last comma inside the parens:
-            BRANCH=$(echo "${BRANCH}" | sed -e 's/ (\(.*, \)\{0,1\}\(.*\))/\2/' -e 's,origin/,,')
-            # Create a suitable version (hash is no good)
-            SOURCE_VERSION="$BRANCH"
-            SOURCE_VERSION=`echo "$SOURCE_VERSION" | sed "s/tag: *//"`
-            if ! echo "$SOURCE_VERSION" | grep "^v[0-9]" ; then
+    case "${SOURCE_VERSION}" in
+        exported|Unknown)
+            if ! grep -q Format $GITTREEDIR/EXPORTED_VERSION; then
+                . $GITTREEDIR/EXPORTED_VERSION
+                echo "Using $GITTREEDIR/EXPORTED_VERSION"
+                echo "BRANCH: $BRANCH"
+                echo "SOURCE_VERSION: $SOURCE_VERSION"
+                # This file has SOURCE_VERSION and BRANCH
+                # example SOURCE_VERSION="30d8a96"
+                # BRANCH examples from github
+                # BRANCH=" (HEAD -> master)"
+                # BRANCH=" (fixes/0.28)"
+                # BRANCH=" (tag: v0.28.1)"
+                # From a checkout they can be as follows:
+                # " (origin/fixes/0.28, fixes/0.28)"
+                # " (HEAD -> master, origin/master, origin/HEAD)"
+                # " (tag: v0.28.1)"
+                hash="$SOURCE_VERSION"
+                # This extracts after the last comma inside the parens:
+                BRANCH=$(echo "${BRANCH}" | sed -e 's/ (\(.*, \)\{0,1\}\(.*\))/\2/' -e 's,origin/,,')
+                # Create a suitable version (hash is no good)
+                SOURCE_VERSION="$BRANCH"
+                SOURCE_VERSION=`echo "$SOURCE_VERSION" | sed "s/tag: *//"`
+                if ! echo "$SOURCE_VERSION" | grep "^v[0-9]" ; then
+                    . $GITTREEDIR/VERSION
+                fi
+                SOURCE_VERSION="${SOURCE_VERSION}-${hash}"
+                echo "Source Version created as $SOURCE_VERSION"
+                echo "Branch created as $BRANCH"
+            elif test -e $GITTREEDIR/VERSION ; then
+                echo "Using $GITTREEDIR/VERSION"
                 . $GITTREEDIR/VERSION
+                echo "BRANCH: $BRANCH"
+                echo "SOURCE_VERSION: $SOURCE_VERSION"
+            fi
+        ;;
+        *)
+            if [ -z "${BRANCH}" ]; then
+                BRANCH=$(git branch --no-color | sed -e '/^[^\*]/d' -e 's/^\* //' -e 's/(no branch)/exported/')
+                echo "Using git to get branch and version"
+                echo "BRANCH: $BRANCH"
+                echo "SOURCE_VERSION: $SOURCE_VERSION"
             fi
-            SOURCE_VERSION="${SOURCE_VERSION}-${hash}"
-            echo "Source Version created as $SOURCE_VERSION"
-        elif test -e $GITTREEDIR/VERSION ; then
-            . $GITTREEDIR/VERSION
-        fi
-    ;;
-    *)
-        if [ -z "${BRANCH}" ]; then
-            BRANCH=$(git branch --no-color | sed -e '/^[^\*]/d' -e 's/^\* //' -e 's/(no branch)/exported/')
-        fi
-    ;;
-esac
+        ;;
+    esac
+fi
 
 if ! echo "${SOURCE_VERSION}" | egrep -i "v[0-9]+.*"   ; then
     # Invalid version - use VERSION file