Index: libs/libmythtv/ThreadedFileWriter.cpp
===================================================================
--- libs/libmythtv/ThreadedFileWriter.cpp	(revision 19654)
+++ libs/libmythtv/ThreadedFileWriter.cpp	(working copy)
@@ -27,8 +27,10 @@
 #define LOC_ERR QString("TFW, Error: ")
 
 const uint ThreadedFileWriter::TFW_DEF_BUF_SIZE   = 2*1024*1024;
-const uint ThreadedFileWriter::TFW_MAX_WRITE_SIZE = TFW_DEF_BUF_SIZE / 4;
-const uint ThreadedFileWriter::TFW_MIN_WRITE_SIZE = TFW_DEF_BUF_SIZE / 32;
+const uint ThreadedFileWriter::TFW_MAX_WRITE_SIZE = TFW_DEF_BUF_SIZE / 
+                                                    (128 / (2<<(5-1)));
+const uint ThreadedFileWriter::TFW_MIN_WRITE_SIZE = TFW_DEF_BUF_SIZE /
+                                                    (1024 / (2<<(5-1)));
 
 /** \class ThreadedFileWriter
  *  \brief This class supports the writing of recordings to disk.
@@ -118,7 +120,8 @@
  *  \brief Creates a threaded file writer.
  */
 ThreadedFileWriter::ThreadedFileWriter(const QString &fname,
-                                       int pflags, mode_t pmode) :
+                                       int pflags, mode_t pmode,
+                                       uint io_mult) :
     // file stuff
     filename(fname),                     flags(pflags),
     mode(pmode),                         fd(-1),
@@ -130,6 +133,7 @@
     rpos(0),                             wpos(0),
     written(0),
     // buffer
+    io_multiplier(io_mult),
     buf(NULL),                           tfw_buf_size(0)
 {
     filename.detach();
@@ -163,7 +167,18 @@
         bzero(buf, TFW_DEF_BUF_SIZE + 64);
 
         tfw_buf_size = TFW_DEF_BUF_SIZE;
-        tfw_min_write_size = TFW_MIN_WRITE_SIZE;
+
+        tfw_min_write_size =
+            TFW_DEF_BUF_SIZE / (1024 / (2<<(io_multiplier - 1)));
+        tfw_max_write_size =
+            TFW_DEF_BUF_SIZE / (128 / (2<<(io_multiplier - 1)));
+
+        VERBOSE(VB_RECORD, LOC + QString(
+                    "Using io multiplier %1, min/max write = %2/%3")
+                .arg(io_multiplier)
+                .arg(tfw_min_write_size)
+                .arg(tfw_max_write_size));
+
         pthread_create(&writer, NULL, boot_writer, this);
         pthread_create(&syncer, NULL, boot_syncer, this);
         return true;
@@ -427,7 +442,7 @@
            buffer is valid, and we try to write all of it at once which
            takes a long time. During this time, the other thread fills up
            the 10% that was free... */
-        size = (size > TFW_MAX_WRITE_SIZE) ? TFW_MAX_WRITE_SIZE : size;
+        size = (size > tfw_max_write_size) ? tfw_max_write_size : size;
 
         bool write_ok;
         if (ignore_writes)
Index: libs/libmythtv/dbcheck.cpp
===================================================================
--- libs/libmythtv/dbcheck.cpp	(revision 19654)
+++ libs/libmythtv/dbcheck.cpp	(working copy)
@@ -18,7 +18,7 @@
 #define MINIMUM_DBMS_VERSION 5,0,15
 
 /// This is the DB schema version expected by the running MythTV instance.
-const QString currentDatabaseVersion = "1228";
+const QString currentDatabaseVersion = "1229";
 
 static bool UpdateDBVersionNumber(const QString &newnumber);
 static bool performActualUpdate(
@@ -4396,6 +4396,17 @@
             return false;
     }
 
+    if (dbver == "1228")
+    {
+        const char *updates[] = {
+"ALTER TABLE storagegroup ADD COLUMN io_multiplier TINYINT(1) "
+"  NOT NULL default '5';",
+NULL
+};
+        if (!performActualUpdate(updates, "1229", dbver))
+            return false;
+    }
+
     return true;
 }
 
Index: libs/libmythtv/RingBuffer.cpp
===================================================================
--- libs/libmythtv/RingBuffer.cpp	(revision 19654)
+++ libs/libmythtv/RingBuffer.cpp	(working copy)
@@ -43,9 +43,8 @@
 #endif
 
 const uint RingBuffer::kBufferSize = 3 * 1024 * 1024;
+const uint RingBuffer::kDefaultReadBlockSize = 8 * 4096;
 
-#define CHUNK 32768 /* readblocksize increments */
-
 #define PNG_MIN_SIZE   20 /* header plus one empty chunk */
 #define NUV_MIN_SIZE  204 /* header size? */
 #define MPEG_MIN_SIZE 376 /* 2 TS packets */
@@ -106,7 +105,7 @@
  */
 RingBuffer::RingBuffer(const QString &lfilename,
                        bool write, bool readahead,
-                       uint read_retries)
+                       uint read_retries, uint io_multiplier)
     : filename(lfilename),
       tfw(NULL),                fd2(-1),
       writemode(false),
@@ -121,7 +120,7 @@
       readsallowed(false),      wantseek(false), setswitchtonext(false),
       rawbitrate(4000),         playspeed(1.0f),
       fill_threshold(65536),    fill_min(-1),
-      readblocksize(CHUNK),     wanttoread(0),
+      readblocksize(kDefaultReadBlockSize), wanttoread(0),
       numfailures(0),           commserror(false),
       dvdPriv(NULL),            oldfile(false),
       livetvchain(NULL),        ignoreliveeof(false),
@@ -130,10 +129,26 @@
     filename.detach();
     pthread_rwlock_init(&rwlock, NULL);
 
+    if ((io_multiplier > StorageGroup::kMaximumIOMultiplier) ||
+        (io_multiplier < StorageGroup::kMinimumIOMultiplier))
+    {
+        VERBOSE(VB_IMPORTANT, LOC + 
+                QString("Read/Write multiplier value of %1 is "
+                        "out of range [%2,%3], "
+                        "setting to the default value %4.")
+                .arg(io_multiplier)
+                .arg(StorageGroup::kMinimumIOMultiplier)
+                .arg(StorageGroup::kMaximumIOMultiplier)
+                .arg(StorageGroup::kDefaultIOMultiplier));
+
+        io_multiplier = StorageGroup::kDefaultIOMultiplier;
+    }
+
     if (write)
     {
         tfw = new ThreadedFileWriter(
-            filename, O_WRONLY|O_TRUNC|O_CREAT|O_LARGEFILE, 0644);
+            filename, O_WRONLY|O_TRUNC|O_CREAT|O_LARGEFILE, 0644,
+            io_multiplier);
 
         if (!tfw->Open())
         {
@@ -144,6 +159,13 @@
         return;
     }
 
+    read_chunk_size = (2<<(io_multiplier - 1)) * 1024;
+    max_read_size = 640 / (32 / (2<<(io_multiplier - 1))) * 1024;
+
+    VERBOSE(VB_GENERAL, LOC + QString(
+                "Using io multiplier %1, chunk/max = %2/%3")
+            .arg(io_multiplier).arg(read_chunk_size).arg(max_read_size));
+
     if (read_retries != (uint)-1)
         OpenFile(filename, read_retries);
 }
@@ -564,10 +586,10 @@
 
     wantseek       = false;
     readsallowed   = false;
-    readblocksize  = CHUNK;
+    readblocksize  = read_chunk_size;
 
     // loop without sleeping if the buffered data is less than this
-    fill_threshold = CHUNK * 2;
+    fill_threshold = read_chunk_size * 2;
     fill_min       = 1;
 
 #ifdef USING_FRONTEND
@@ -637,7 +659,7 @@
 void RingBuffer::ResetReadAhead(long long newinternal)
 {
     readAheadLock.lock();
-    readblocksize = CHUNK;
+    readblocksize = read_chunk_size;
     rbrpos = 0;
     rbwpos = 0;
     internalreadpos = newinternal;
@@ -762,13 +784,12 @@
 
     struct timeval lastread, now;
     gettimeofday(&lastread, NULL);
-    const int KB640 = 640*1024;
     int readtimeavg = 300;
     int readinterval;
 
     pausereadthread = false;
 
-    readAheadBuffer = new char[kBufferSize + KB640];
+    readAheadBuffer = new char[kBufferSize + max_read_size];
 
     pthread_rwlock_wrlock(&rwlock);
     ResetReadAhead(0);
@@ -822,17 +843,17 @@
 
             readtimeavg = (readtimeavg * 9 + readinterval) / 10;
 
-            if (readtimeavg < 200 && readblocksize < KB640)
+            if (readtimeavg < 200 && readblocksize < max_read_size)
             {
-                readblocksize += CHUNK;
+                readblocksize += read_chunk_size;
                 //VERBOSE(VB_PLAYBACK,
                 //    QString("Avg read interval was %1 msec. %2K block size")
                 //            .arg(readtimeavg).arg(readblocksize/1024));
                 readtimeavg = 300;
             }
-            else if (readtimeavg > 400 && readblocksize > CHUNK)
+            else if (readtimeavg > 400 && readblocksize > read_chunk_size)
             {
-                readblocksize -= CHUNK;
+                readblocksize -= read_chunk_size;
                 //VERBOSE(VB_PLAYBACK,
                 //    QString("Avg read interval was %1 msec. %2K block size")
                 //            .arg(readtimeavg).arg(readblocksize/1024));
Index: libs/libmythtv/tv_play.cpp
===================================================================
--- libs/libmythtv/tv_play.cpp	(revision 19654)
+++ libs/libmythtv/tv_play.cpp	(working copy)
@@ -55,6 +55,7 @@
 #include "mythdialogbox.h"
 #include "mythmainwindow.h"
 #include "mythscreenstack.h"
+#include "storagegroup.h"
 
 #ifndef HAVE_ROUND
 #define round(x) ((int) ((x) + 0.5))
@@ -1676,7 +1677,9 @@
         else
         {
             ctx->LockPlayingInfo(__FILE__, __LINE__);
-            QString playbackURL = ctx->playingInfo->GetPlaybackURL();
+            uint io_multiplier;
+            QString playbackURL = ctx->playingInfo->GetPlaybackURL(
+                false, false, &io_multiplier);
             ctx->UnlockPlayingInfo(__FILE__, __LINE__);
 
             bool opennow = (ctx->tvchain->GetCardType(-1) != "DUMMY");
@@ -1685,8 +1688,9 @@
                                           "cardtype(%2)")
                     .arg(playbackURL).arg(ctx->tvchain->GetCardType(-1)));
 
-            ctx->SetRingBuffer(new RingBuffer(playbackURL, false, true,
-                                      opennow ? 12 : (uint)-1));
+            ctx->SetRingBuffer(new RingBuffer(
+                                   playbackURL, false, true,
+                                   opennow ? 12 : (uint)-1, io_multiplier));
             ctx->buffer->SetLiveMode(ctx->tvchain);
         }
 
@@ -1739,6 +1743,7 @@
              TRANSITION(kState_None, kState_WatchingRecording))
     {
         ctx->LockPlayingInfo(__FILE__, __LINE__);
+        uint io_multiplier = StorageGroup::kDefaultIOMultiplier;
         QString playbackURL;
         if ((ctx->playingInfo->pathname.left(4) == "dvd:") ||
             (ctx->playingInfo->isVideo))
@@ -1747,11 +1752,15 @@
             playbackURL.detach();
         }
         else
+        {
             playbackURL = ctx->playingInfo->GetPlaybackURL(
-                              desiredNextState != kState_WatchingRecording);
+                desiredNextState != kState_WatchingRecording,
+                false, &io_multiplier);
+        }
         ctx->UnlockPlayingInfo(__FILE__, __LINE__);
         
-        ctx->SetRingBuffer(new RingBuffer(playbackURL, false));
+        ctx->SetRingBuffer(new RingBuffer(
+                               playbackURL, false, true, 12, io_multiplier));
         
         if (ctx->buffer && ctx->buffer->IsOpen())
         {
@@ -6031,8 +6040,11 @@
         else
         {
             ctx->LockPlayingInfo(__FILE__, __LINE__);
-            QString playbackURL = ctx->playingInfo->GetPlaybackURL();
-            ctx->SetRingBuffer(new RingBuffer(playbackURL, false));
+            uint io_multiplier;
+            QString playbackURL = ctx->playingInfo->GetPlaybackURL(
+                false, false, &io_multiplier);
+            ctx->SetRingBuffer(
+                new RingBuffer(playbackURL, false, true, 12, io_multiplier));
             ctx->tvchain->SetProgram(*ctx->playingInfo);
             ctx->buffer->SetLiveMode(ctx->tvchain);
             ctx->UnlockPlayingInfo(__FILE__, __LINE__);
Index: libs/libmythtv/programinfo.h
===================================================================
--- libs/libmythtv/programinfo.h	(revision 19654)
+++ libs/libmythtv/programinfo.h	(working copy)
@@ -239,7 +239,8 @@
     bool SetRecordBasename(QString basename);
     QString GetRecordBasename(bool fromDB = false) const;
     QString GetPlaybackURL(bool checkMaster = false,
-                           bool forceCheckLocal = false);
+                           bool forceCheckLocal = false,
+                           uint *p_io_multiplier = NULL);
     QString MakeUniqueKey(void) const;
     int CalculateLength(void) const;
     int SecsTillStart() const;
Index: libs/libmythtv/RingBuffer.h
===================================================================
--- libs/libmythtv/RingBuffer.h	(revision 19654)
+++ libs/libmythtv/RingBuffer.h	(working copy)
@@ -13,6 +13,7 @@
 }
 
 #include "mythexp.h"
+#include "storagegroup.h"
 
 class RemoteFile;
 class RemoteEncoder;
@@ -24,7 +25,8 @@
 {
   public:
     RingBuffer(const QString &lfilename, bool write,
-               bool usereadahead = true, uint read_retries = 12/*6*/);
+               bool usereadahead = true, uint read_retries = 12/*6*/,
+               uint io_multiplier = StorageGroup::kDefaultIOMultiplier);
    ~RingBuffer();
 
     // Sets
@@ -126,6 +128,9 @@
     long long readpos;
     long long writepos;
 
+    uint read_chunk_size;
+    uint max_read_size;
+
     bool stopreads;
 
     mutable pthread_rwlock_t rwlock;
@@ -154,7 +159,7 @@
     float          playspeed;
     int            fill_threshold;
     int            fill_min;
-    int            readblocksize;
+    uint           readblocksize;
 
     QWaitCondition pauseWait;
 
@@ -184,6 +189,7 @@
  
     // constants
     static const uint kBufferSize;
+    static const uint kDefaultReadBlockSize;
     static const uint kReadTestSize;
 };
 
Index: libs/libmythtv/tv_rec.cpp
===================================================================
--- libs/libmythtv/tv_rec.cpp	(revision 19654)
+++ libs/libmythtv/tv_rec.cpp	(working copy)
@@ -11,8 +11,7 @@
 using namespace std;
 
 // Qt headers
-#include <qapplication.h>
-#include <qsqldatabase.h>
+#include <QFileInfo>
 
 // MythTV headers
 #include "mythconfig.h"
@@ -33,6 +32,7 @@
 #include "previewgenerator.h"
 #include "storagegroup.h"
 #include "remoteutil.h"
+#include "storagegroup.h"
 
 #include "atscstreamdata.h"
 #include "dvbstreamdata.h"
@@ -76,6 +76,7 @@
 
 static bool is_dishnet_eit(int cardid);
 static QString load_profile(QString,void*,ProgramInfo*,RecordingProfile&);
+static uint calc_io_mult(const ProgramInfo*);
 
 /** \class TVRec
  *  \brief This is the coordinating class of the \ref recorder_subsystem.
@@ -4003,7 +4004,8 @@
 
     if (lastTuningRequest.flags & kFlagRecording)
     {
-        SetRingBuffer(new RingBuffer(rec->GetFileName(), true));
+        uint iom = calc_io_mult(rec);
+        SetRingBuffer(new RingBuffer(rec->GetFileName(), true, true, 12, iom));
         if (!ringBuffer->IsOpen())
         {
             VERBOSE(VB_IMPORTANT, LOC_ERR +
@@ -4386,7 +4388,8 @@
 
     StartedRecording(prog);
 
-    *rb = new RingBuffer(prog->GetFileName(), true);
+    uint iom = calc_io_mult(prog);
+    *rb = new RingBuffer(prog->GetFileName(), true, true, 12, iom);
     if (!(*rb)->IsOpen())
     {
         VERBOSE(VB_IMPORTANT, LOC_ERR +
@@ -4500,5 +4503,12 @@
         .arg(TVRec::FlagToString(flags));
 }
 
+static uint calc_io_mult(const ProgramInfo *pginfo)
+{
+    StorageGroup sgroup(pginfo->storagegroup, gContext->GetHostName());
+    return sgroup.QueryIOMultiplier(
+        QFileInfo(pginfo->GetFileName()).absolutePath());
+}
+
 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 
Index: libs/libmythtv/programinfo.cpp
===================================================================
--- libs/libmythtv/programinfo.cpp	(revision 19654)
+++ libs/libmythtv/programinfo.cpp	(working copy)
@@ -1621,25 +1621,34 @@
  *         If the file is accessible locally, the filename will be returned,
  *         otherwise a myth:// URL will be returned.
  */
-QString ProgramInfo::GetPlaybackURL(bool checkMaster, bool forceCheckLocal)
+QString ProgramInfo::GetPlaybackURL(
+    bool checkMaster, bool forceCheckLocal, uint *p_io_multiplier)
 {
     QString tmpURL;
     QString basename = GetRecordBasename(true);
 
     bool alwaysStream = gContext->GetNumSetting("AlwaysStreamFiles", 0);
 
+    if (p_io_multiplier)
+        *p_io_multiplier = StorageGroup::kDefaultIOMultiplier;
+
     if ((!alwaysStream) ||
         (forceCheckLocal) ||
         (hostname == gContext->GetHostName()))
     {
         // Check to see if the file exists locally
         StorageGroup sgroup(storagegroup);
-        tmpURL = sgroup.FindRecordingFile(basename);
+        QString dirname = sgroup.FindRecordingDir(basename);
 
-        if (tmpURL != "")
+        if (!dirname.isEmpty())
         {
+            tmpURL = dirname + "/" + basename;
             VERBOSE(VB_FILE, LOC +
                     QString("GetPlaybackURL: File is local: '%1'").arg(tmpURL));
+
+            if (p_io_multiplier)
+                *p_io_multiplier = sgroup.QueryIOMultiplier(dirname);
+
             return tmpURL;
         }
         else if (hostname == gContext->GetHostName())
Index: libs/libmythtv/ThreadedFileWriter.h
===================================================================
--- libs/libmythtv/ThreadedFileWriter.h	(revision 19654)
+++ libs/libmythtv/ThreadedFileWriter.h	(working copy)
@@ -7,10 +7,13 @@
 #include <qwaitcondition.h>
 #include <qstring.h>
 
+#include "storagegroup.h"
+
 class ThreadedFileWriter
 {
   public:
-    ThreadedFileWriter(const QString &fname, int flags, mode_t mode);
+    ThreadedFileWriter(const QString &fname, int flags, mode_t mode,
+                       uint io_mult = StorageGroup::kDefaultIOMultiplier);
     ~ThreadedFileWriter();
 
     bool Open(void);
@@ -43,6 +46,7 @@
     int             flags;
     mode_t          mode;
     int             fd;
+    uint            io_multiplier;
 
     // state
     bool            no_writes;
@@ -51,6 +55,7 @@
     bool            in_dtor;
     bool            ignore_writes;
     long long       tfw_min_write_size;
+    long long       tfw_max_write_size;
 
     // buffer position state
     uint            rpos;    ///< points to end of data written to disk
Index: libs/libmyth/storagegroup.h
===================================================================
--- libs/libmyth/storagegroup.h	(revision 19654)
+++ libs/libmyth/storagegroup.h	(working copy)
@@ -2,6 +2,7 @@
 #define _STORAGEGROUP_H
 
 #include <QStringList>
+#include <QMap>
 
 #include "libmyth/settings.h"
 #include "libmyth/mythwidgets.h"
@@ -32,12 +33,83 @@
 
     static QStringList getRecordingsGroups(void);
 
+    uint QueryIOMultiplier(const QString &dirname);
+
+    static const uint kSentinelIOMultiplier = 1;
+    static const uint kDefaultIOMultiplier  = 5;
+    static const uint kMinimumIOMultiplier  = 2;
+    static const uint kMaximumIOMultiplier  = 5;
+
   private:
     QString      m_groupname;
     QString      m_hostname;
     QStringList  m_dirlist;
+    uint         io_multiplier;
 };
 
+class DirectoryPathSetting : public TransLineEditSetting
+{
+  public:
+    DirectoryPathSetting(QString &dir) :
+        TransLineEditSetting(true), m_dir(dir)
+    {
+        setLabel(QObject::tr("Directory Path"));
+        QString help = QObject::tr(
+            "Specify a path to add to this storage group.");
+        setHelpText(help);
+    }
+
+    virtual void Load(void)
+    {
+        setValue(m_dir);
+    }
+
+    virtual void Save(void)
+    {
+        m_dir = getValue();
+    }
+
+  private:
+    QString &m_dir;
+};
+
+class DirectoryConfig : public ConfigurationWizard
+{
+  public:
+    DirectoryConfig(QString &dir, uint &io_multiplier);
+};
+
+class IOMultiplierSetting : public TransSpinBoxSetting
+{
+  public:
+    IOMultiplierSetting(uint &io_multiplier) :
+        TransSpinBoxSetting(StorageGroup::kMinimumIOMultiplier,
+                            StorageGroup::kMaximumIOMultiplier, 1),
+        m_io_multiplier(io_multiplier)
+    {
+        setLabel(QObject::tr("Read/Write Buffer"));
+        QString help = QObject::tr(
+            "Specify the amount of buffering to "
+            "use for this directory. Default %1. "
+            "Use caution when adjusting this.")
+            .arg(StorageGroup::kDefaultIOMultiplier);
+        setHelpText(help);
+    }
+
+    virtual void Load(void)
+    {
+        setValue(m_io_multiplier);
+    }
+
+    virtual void Save(void)
+    {
+        m_io_multiplier = getValue().toUInt();
+    }
+
+  private:
+    uint &m_io_multiplier;
+};
+
 class MPUBLIC StorageGroupEditor :
     public QObject, public ConfigurationDialog
 {
@@ -56,9 +128,12 @@
     void doDelete(void);
 
   protected:
+    void editDirectory(QString &dir, uint &io_multiplier);
+
     QString         m_group;
     ListBoxSetting *listbox;
     QString         lastValue;
+    QMap<QString, uint> multiplier_map;
 };
 
 class MPUBLIC StorageGroupListEditor :
Index: libs/libmyth/storagegroup.cpp
===================================================================
--- libs/libmyth/storagegroup.cpp	(revision 19654)
+++ libs/libmyth/storagegroup.cpp	(working copy)
@@ -20,6 +20,21 @@
     << "DB Backups"
     ;
 
+DirectoryConfig::DirectoryConfig(QString &dir, uint &io_multiplier)
+{
+    ConfigurationGroup* group = new VerticalConfigurationGroup(false, false);
+
+    if (dir.isEmpty())
+        group->setLabel(QObject::tr("Add new directory"));
+    else
+        group->setLabel(QObject::tr("Settings for directory: ") + dir);
+
+    group->addChild(new DirectoryPathSetting(dir));
+    group->addChild(new IOMultiplierSetting(io_multiplier));
+
+    addChild(group);
+}
+
 /****************************************************************************/
 
 /** \brief StorageGroup constructor.
@@ -327,6 +342,70 @@
     return groups;
 }
 
+uint StorageGroup::QueryIOMultiplier(const QString &dirname)
+{
+    if (dirname.isEmpty() || m_groupname.isEmpty() || m_hostname.isEmpty())
+        return StorageGroup::kDefaultIOMultiplier;
+
+    QString dirname_t = dirname;
+
+    if (dirname.endsWith("/"))
+        dirname_t.chop(1);
+    else
+        dirname_t.append("/");
+
+    uint io_multiplier;
+    MSqlQuery query(MSqlQuery::InitCon());
+
+    query.prepare("SELECT io_multiplier FROM storagegroup "
+                  "WHERE groupname = :GROUP "
+                  "AND hostname = :HOSTNAME "
+                  "AND (dirname = :DIRNAME  "
+                  "OR dirname = :DIRNAME_T);");
+    query.bindValue(":GROUP", m_groupname);
+    query.bindValue(":HOSTNAME", m_hostname);
+    query.bindValue(":DIRNAME", dirname);
+    query.bindValue(":DIRNAME_T", dirname_t);
+
+    if (!query.exec())
+    {
+        MythDB::DBError("StorageGroup::QueryIOMultiplier()", query);
+
+        return StorageGroup::kDefaultIOMultiplier;
+    }
+    else if (!query.next())
+    {
+        VERBOSE(VB_FILE, LOC + QString(
+                    "Unable to find storage group '%1' on host %2, "
+                    "using default io_multiplier of %3.")
+                .arg(m_groupname).arg(m_hostname)
+                .arg(StorageGroup::kDefaultIOMultiplier));
+
+        return StorageGroup::kDefaultIOMultiplier;
+    }
+
+    io_multiplier = query.value(0).toUInt();
+
+    if ((io_multiplier > kMaximumIOMultiplier) ||
+        (io_multiplier < kMinimumIOMultiplier))
+    {
+        VERBOSE(VB_IMPORTANT, LOC +
+                QString(
+                    "Read/Write multiplier value of %1 for storage group "
+                    "'%2' on host %3 "
+                    "\n\t\t\tis out of range [%4,%5], setting to the "
+                    "default value %6.")
+                .arg(io_multiplier).arg(m_groupname).arg(m_hostname)
+                .arg(kMinimumIOMultiplier)
+                .arg(kMaximumIOMultiplier)
+                .arg(kDefaultIOMultiplier));
+
+        io_multiplier = StorageGroup::kDefaultIOMultiplier;
+    }
+
+    return io_multiplier;
+}
+
 /****************************************************************************/
 typedef enum {
     SGPopup_OK = 0,
@@ -401,13 +480,12 @@
 
     if (name == "__CREATE_NEW_STORAGE_DIRECTORY__")
     {
+        uint m_io_multiplier = StorageGroup::kDefaultIOMultiplier;
         name = "";
-        SGPopupResult result = StorageGroupPopup::showPopup(
-            gContext->GetMainWindow(), 
-            tr("Add Storage Group Directory"),
-            tr("Enter directory name or press SELECT to enter text via the "
-               "On Screen Keyboard"), name);
-        if (result == SGPopup_CANCEL)
+
+        DirectoryConfig config(name, m_io_multiplier);
+
+        if (config.exec() != MythDialog::Accepted)
             return;
 
         if (name.isEmpty())
@@ -417,22 +495,27 @@
             name.append("/");
 
         MSqlQuery query(MSqlQuery::InitCon());
-        query.prepare("INSERT INTO storagegroup (groupname, hostname, dirname) "
-                      "VALUES (:NAME, :HOSTNAME, :DIRNAME);");
+        query.prepare("INSERT INTO storagegroup (groupname, hostname, "
+                      "dirname, io_multiplier) "
+                      "VALUES (:NAME, :HOSTNAME, :DIRNAME, :IO_MULTIPLIER);");
         query.bindValue(":NAME", m_group);
         query.bindValue(":DIRNAME", name);
+        query.bindValue(":IO_MULTIPLIER", m_io_multiplier);
         query.bindValue(":HOSTNAME", gContext->GetHostName());
         if (!query.exec())
             MythDB::DBError("StorageGroupEditor::open", query);
         else
             lastValue = name;
-    } else {
-        SGPopupResult result = StorageGroupPopup::showPopup(
-            gContext->GetMainWindow(), 
-            tr("Edit Storage Group Directory"),
-            tr("Enter directory name or press SELECT to enter text via the "
-               "On Screen Keyboard"), name);
-        if (result == SGPopup_CANCEL)
+    }
+    else
+    {
+        uint m_io_multiplier = multiplier_map[name];
+        if (!m_io_multiplier)
+            m_io_multiplier = StorageGroup::kDefaultIOMultiplier;
+
+        DirectoryConfig config(name, m_io_multiplier);
+
+        if (config.exec() != MythDialog::Accepted)
             return;
 
         if (name.right(1) != "/")
@@ -450,10 +533,12 @@
         if (!query.exec())
             MythDB::DBError("StorageGroupEditor::open", query);
 
-        query.prepare("INSERT INTO storagegroup (groupname, hostname, dirname) "
-                      "VALUES (:NAME, :HOSTNAME, :DIRNAME);");
+        query.prepare("INSERT INTO storagegroup (groupname, hostname, "
+                      "dirname, io_multiplier) "
+                      "VALUES (:NAME, :HOSTNAME, :DIRNAME, :IO_MULTIPLIER)");
         query.bindValue(":NAME", m_group);
         query.bindValue(":DIRNAME", name);
+        query.bindValue(":IO_MULTIPLIER", m_io_multiplier);
         query.bindValue(":HOSTNAME", gContext->GetHostName());
         if (!query.exec())
             MythDB::DBError("StorageGroupEditor::open", query);
@@ -502,9 +587,10 @@
 void StorageGroupEditor::Load(void)
 {
     listbox->clearSelections();
+    multiplier_map.clear();
 
     MSqlQuery query(MSqlQuery::InitCon());
-    query.prepare("SELECT dirname, id FROM storagegroup "
+    query.prepare("SELECT dirname, io_multiplier, id FROM storagegroup "
                   "WHERE groupname = :NAME AND hostname = :HOSTNAME "
                   "ORDER BY id;");
     query.bindValue(":NAME", m_group);
@@ -522,6 +608,7 @@
                 first = false;
             }
             listbox->addSelection(query.value(0).toString());
+            multiplier_map[query.value(0).toString()] = query.value(1).toUInt();
         }
     }
 
Index: programs/mythbackend/mainserver.h
===================================================================
--- programs/mythbackend/mainserver.h	(revision 19654)
+++ programs/mythbackend/mainserver.h	(working copy)
@@ -150,7 +150,7 @@
     FileTransfer *getFileTransferByID(int id);
     FileTransfer *getFileTransferBySock(MythSocket *socket);
 
-    QString LocalFilePath(const QUrl &url);
+    QString LocalFilePath(const QUrl &url, uint &io_multiplier);
 
     static void *SpawnDeleteThread(void *param);
     void DoDeleteThread(const DeleteStruct *ds);
Index: programs/mythbackend/filetransfer.cpp
===================================================================
--- programs/mythbackend/filetransfer.cpp	(revision 19654)
+++ programs/mythbackend/filetransfer.cpp	(working copy)
@@ -8,17 +8,18 @@
 #include "mythsocket.h"
 
 FileTransfer::FileTransfer(QString &filename, MythSocket *remote,
-                           bool usereadahead, int retries) :
+                           bool usereadahead, int retries, uint io_mult) :
     readthreadlive(true), readsLocked(false),
-    rbuffer(new RingBuffer(filename, false, usereadahead, retries)),
+    rbuffer(new RingBuffer(filename, false, usereadahead, retries, io_mult)),
     sock(remote), ateof(false), lock(QMutex::NonRecursive),
     refLock(QMutex::NonRecursive), refCount(0)
 {
 }
 
-FileTransfer::FileTransfer(QString &filename, MythSocket *remote) :
+FileTransfer::FileTransfer(QString &filename, MythSocket *remote,
+                           uint io_mult) :
     readthreadlive(true), readsLocked(false),
-    rbuffer(new RingBuffer(filename, false)),
+    rbuffer(new RingBuffer(filename, false, true, 12, io_mult)),
     sock(remote), ateof(false), lock(QMutex::NonRecursive),
     refLock(QMutex::NonRecursive), refCount(0)
 {
Index: programs/mythbackend/mainserver.cpp
===================================================================
--- programs/mythbackend/mainserver.cpp	(revision 19654)
+++ programs/mythbackend/mainserver.cpp	(working copy)
@@ -1054,7 +1054,8 @@
         VERBOSE(VB_IMPORTANT, QString("adding: %1 as a remote file transfer")
                                .arg(commands[2]));
         QUrl qurl = slist[1];
-        QString filename = LocalFilePath(qurl);
+        uint io_multiplier;
+        QString filename = LocalFilePath(qurl, io_multiplier);
 
         FileTransfer *ft = NULL;
         bool usereadahead = true;
@@ -1066,9 +1067,14 @@
         }
 
         if (retries >= 0)
-            ft = new FileTransfer(filename, socket, usereadahead, retries);
+        {
+            ft = new FileTransfer(
+                filename, socket, usereadahead, retries, io_multiplier);
+        }
         else
-            ft = new FileTransfer(filename, socket);
+        {
+            ft = new FileTransfer(filename, socket, io_multiplier);
+        }
 
         sockListLock.lock();
         fileTransferList.push_back(ft);
@@ -4349,10 +4355,12 @@
         QApplication::exit(m_exitCode);
 }
 
-QString MainServer::LocalFilePath(const QUrl &url)
+QString MainServer::LocalFilePath(const QUrl &url, uint &io_multiplier)
 {
     QString lpath = url.path();
 
+    io_multiplier = StorageGroup::kDefaultIOMultiplier;
+
     if (lpath.section('/', -2, -2) == "channels")
     {
         // This must be an icon request. Check channel.icon to be safe.
@@ -4386,7 +4394,7 @@
         ProgramInfo *pginfo = ProgramInfo::GetProgramFromBasename(fpath);
         if (pginfo)
         {
-            QString pburl = GetPlaybackURL(pginfo);
+            QString pburl = pginfo->GetPlaybackURL(false, true, &io_multiplier);
             if (pburl.left(1) == "/")
             {
                 lpath = pburl.section('/', 0, -2) + "/" + lpath;
@@ -4410,6 +4418,8 @@
             lpath = QFileInfo(lpath).fileName();
             StorageGroup sgroup;
             QString tmpFile = sgroup.FindRecordingFile(lpath);
+            QFileInfo fi(tmpFile);
+            io_multiplier = sgroup.QueryIOMultiplier(fi.absolutePath());
             if (!tmpFile.isEmpty())
             {
                 lpath = tmpFile;
Index: programs/mythbackend/backendutil.cpp
===================================================================
--- programs/mythbackend/backendutil.cpp	(revision 19654)
+++ programs/mythbackend/backendutil.cpp	(working copy)
@@ -355,9 +355,11 @@
 QString GetPlaybackURL(ProgramInfo *pginfo, bool storePath)
 {
     QString result = "";
+
     QMutexLocker locker(&recordingPathLock);
     QString cacheKey = QString("%1:%2").arg(pginfo->chanid)
-                               .arg(pginfo->recstartts.toString(Qt::ISODate));
+        .arg(pginfo->recstartts.toString(Qt::ISODate));
+
     if ((recordingPathCache.contains(cacheKey)) &&
         (QFile::exists(recordingPathCache[cacheKey])))
     {
Index: programs/mythbackend/filetransfer.h
===================================================================
--- programs/mythbackend/filetransfer.h	(revision 19654)
+++ programs/mythbackend/filetransfer.h	(working copy)
@@ -9,10 +9,13 @@
 using namespace std;
 
 // Qt headers
-#include <qstring.h>
-#include <qmutex.h>
-#include <qwaitcondition.h>
+#include <QString>
+#include <QMutex>
+#include <QWaitCondition>
 
+// MythTV headers
+#include "storagegroup.h"
+
 class RingBuffer;
 class MythSocket;
 
@@ -21,9 +24,11 @@
     friend class QObject; // quiet OSX gcc warning
 
   public:
-    FileTransfer(QString &filename, MythSocket *remote);
     FileTransfer(QString &filename, MythSocket *remote,
-                 bool usereadahead, int retries);
+                 uint io_mult = StorageGroup::kDefaultIOMultiplier);
+    FileTransfer(QString &filename, MythSocket *remote,
+                 bool usereadahead, int retries,
+                 uint io_mult = StorageGroup::kDefaultIOMultiplier);
 
     MythSocket *getSocket() { return sock; }
 
