Ticket #4242: 4242-v1.patch

File 4242-v1.patch, 32.3 KB (added by danielk, 17 years ago)

Reviewed version of patch with some substancial changes.

  • libs/libmythtv/ThreadedFileWriter.cpp

     
    2727#define LOC_ERR QString("TFW, Error: ")
    2828
    2929const uint ThreadedFileWriter::TFW_DEF_BUF_SIZE   = 2*1024*1024;
    30 const uint ThreadedFileWriter::TFW_MAX_WRITE_SIZE = TFW_DEF_BUF_SIZE / 4;
    31 const uint ThreadedFileWriter::TFW_MIN_WRITE_SIZE = TFW_DEF_BUF_SIZE / 32;
     30const uint ThreadedFileWriter::TFW_MAX_WRITE_SIZE = TFW_DEF_BUF_SIZE /
     31                                                    (128 / (2<<(5-1)));
     32const uint ThreadedFileWriter::TFW_MIN_WRITE_SIZE = TFW_DEF_BUF_SIZE /
     33                                                    (1024 / (2<<(5-1)));
    3234
    3335/** \class ThreadedFileWriter
    3436 *  \brief This class supports the writing of recordings to disk.
     
    118120 *  \brief Creates a threaded file writer.
    119121 */
    120122ThreadedFileWriter::ThreadedFileWriter(const QString &fname,
    121                                        int pflags, mode_t pmode) :
     123                                       int pflags, mode_t pmode,
     124                                       uint io_mult) :
    122125    // file stuff
    123126    filename(fname),                     flags(pflags),
    124127    mode(pmode),                         fd(-1),
     
    130133    rpos(0),                             wpos(0),
    131134    written(0),
    132135    // buffer
     136    io_multiplier(io_mult),
    133137    buf(NULL),                           tfw_buf_size(0)
    134138{
    135139    filename.detach();
     
    163167        bzero(buf, TFW_DEF_BUF_SIZE + 64);
    164168
    165169        tfw_buf_size = TFW_DEF_BUF_SIZE;
    166         tfw_min_write_size = TFW_MIN_WRITE_SIZE;
     170
     171        tfw_min_write_size =
     172            TFW_DEF_BUF_SIZE / (1024 / (2<<(io_multiplier - 1)));
     173        tfw_max_write_size =
     174            TFW_DEF_BUF_SIZE / (128 / (2<<(io_multiplier - 1)));
     175
     176        VERBOSE(VB_RECORD, LOC + QString(
     177                    "Using io multiplier %1, min/max write = %2/%3")
     178                .arg(io_multiplier)
     179                .arg(tfw_min_write_size)
     180                .arg(tfw_max_write_size));
     181
    167182        pthread_create(&writer, NULL, boot_writer, this);
    168183        pthread_create(&syncer, NULL, boot_syncer, this);
    169184        return true;
     
    427442           buffer is valid, and we try to write all of it at once which
    428443           takes a long time. During this time, the other thread fills up
    429444           the 10% that was free... */
    430         size = (size > TFW_MAX_WRITE_SIZE) ? TFW_MAX_WRITE_SIZE : size;
     445        size = (size > tfw_max_write_size) ? tfw_max_write_size : size;
    431446
    432447        bool write_ok;
    433448        if (ignore_writes)
  • libs/libmythtv/dbcheck.cpp

     
    1818#define MINIMUM_DBMS_VERSION 5,0,15
    1919
    2020/// This is the DB schema version expected by the running MythTV instance.
    21 const QString currentDatabaseVersion = "1228";
     21const QString currentDatabaseVersion = "1229";
    2222
    2323static bool UpdateDBVersionNumber(const QString &newnumber);
    2424static bool performActualUpdate(
     
    43964396            return false;
    43974397    }
    43984398
     4399    if (dbver == "1228")
     4400    {
     4401        const char *updates[] = {
     4402"ALTER TABLE storagegroup ADD COLUMN io_multiplier TINYINT(1) "
     4403"  NOT NULL default '5';",
     4404NULL
     4405};
     4406        if (!performActualUpdate(updates, "1229", dbver))
     4407            return false;
     4408    }
     4409
    43994410    return true;
    44004411}
    44014412
  • libs/libmythtv/RingBuffer.cpp

     
    4343#endif
    4444
    4545const uint RingBuffer::kBufferSize = 3 * 1024 * 1024;
     46const uint RingBuffer::kDefaultReadBlockSize = 8 * 4096;
    4647
    47 #define CHUNK 32768 /* readblocksize increments */
    48 
    4948#define PNG_MIN_SIZE   20 /* header plus one empty chunk */
    5049#define NUV_MIN_SIZE  204 /* header size? */
    5150#define MPEG_MIN_SIZE 376 /* 2 TS packets */
     
    106105 */
    107106RingBuffer::RingBuffer(const QString &lfilename,
    108107                       bool write, bool readahead,
    109                        uint read_retries)
     108                       uint read_retries, uint io_multiplier)
    110109    : filename(lfilename),
    111110      tfw(NULL),                fd2(-1),
    112111      writemode(false),
     
    121120      readsallowed(false),      wantseek(false), setswitchtonext(false),
    122121      rawbitrate(4000),         playspeed(1.0f),
    123122      fill_threshold(65536),    fill_min(-1),
    124       readblocksize(CHUNK),    wanttoread(0),
     123      readblocksize(kDefaultReadBlockSize), wanttoread(0),
    125124      numfailures(0),           commserror(false),
    126125      dvdPriv(NULL),            oldfile(false),
    127126      livetvchain(NULL),        ignoreliveeof(false),
     
    130129    filename.detach();
    131130    pthread_rwlock_init(&rwlock, NULL);
    132131
     132    if ((io_multiplier > StorageGroup::kMaximumIOMultiplier) ||
     133        (io_multiplier < StorageGroup::kMinimumIOMultiplier))
     134    {
     135        VERBOSE(VB_IMPORTANT, LOC +
     136                QString("Read/Write multiplier value of %1 is "
     137                        "out of range [%2,%3], "
     138                        "setting to the default value %4.")
     139                .arg(io_multiplier)
     140                .arg(StorageGroup::kMinimumIOMultiplier)
     141                .arg(StorageGroup::kMaximumIOMultiplier)
     142                .arg(StorageGroup::kDefaultIOMultiplier));
     143
     144        io_multiplier = StorageGroup::kDefaultIOMultiplier;
     145    }
     146
    133147    if (write)
    134148    {
    135149        tfw = new ThreadedFileWriter(
    136             filename, O_WRONLY|O_TRUNC|O_CREAT|O_LARGEFILE, 0644);
     150            filename, O_WRONLY|O_TRUNC|O_CREAT|O_LARGEFILE, 0644,
     151            io_multiplier);
    137152
    138153        if (!tfw->Open())
    139154        {
     
    144159        return;
    145160    }
    146161
     162    read_chunk_size = (2<<(io_multiplier - 1)) * 1024;
     163    max_read_size = 640 / (32 / (2<<(io_multiplier - 1))) * 1024;
     164
     165    VERBOSE(VB_GENERAL, LOC + QString(
     166                "Using io multiplier %1, chunk/max = %2/%3")
     167            .arg(io_multiplier).arg(read_chunk_size).arg(max_read_size));
     168
    147169    if (read_retries != (uint)-1)
    148170        OpenFile(filename, read_retries);
    149171}
     
    564586
    565587    wantseek       = false;
    566588    readsallowed   = false;
    567     readblocksize  = CHUNK;
     589    readblocksize  = read_chunk_size;
    568590
    569591    // loop without sleeping if the buffered data is less than this
    570     fill_threshold = CHUNK * 2;
     592    fill_threshold = read_chunk_size * 2;
    571593    fill_min       = 1;
    572594
    573595#ifdef USING_FRONTEND
     
    637659void RingBuffer::ResetReadAhead(long long newinternal)
    638660{
    639661    readAheadLock.lock();
    640     readblocksize = CHUNK;
     662    readblocksize = read_chunk_size;
    641663    rbrpos = 0;
    642664    rbwpos = 0;
    643665    internalreadpos = newinternal;
     
    762784
    763785    struct timeval lastread, now;
    764786    gettimeofday(&lastread, NULL);
    765     const int KB640 = 640*1024;
    766787    int readtimeavg = 300;
    767788    int readinterval;
    768789
    769790    pausereadthread = false;
    770791
    771     readAheadBuffer = new char[kBufferSize + KB640];
     792    readAheadBuffer = new char[kBufferSize + max_read_size];
    772793
    773794    pthread_rwlock_wrlock(&rwlock);
    774795    ResetReadAhead(0);
     
    822843
    823844            readtimeavg = (readtimeavg * 9 + readinterval) / 10;
    824845
    825             if (readtimeavg < 200 && readblocksize < KB640)
     846            if (readtimeavg < 200 && readblocksize < max_read_size)
    826847            {
    827                 readblocksize += CHUNK;
     848                readblocksize += read_chunk_size;
    828849                //VERBOSE(VB_PLAYBACK,
    829850                //    QString("Avg read interval was %1 msec. %2K block size")
    830851                //            .arg(readtimeavg).arg(readblocksize/1024));
    831852                readtimeavg = 300;
    832853            }
    833             else if (readtimeavg > 400 && readblocksize > CHUNK)
     854            else if (readtimeavg > 400 && readblocksize > read_chunk_size)
    834855            {
    835                 readblocksize -= CHUNK;
     856                readblocksize -= read_chunk_size;
    836857                //VERBOSE(VB_PLAYBACK,
    837858                //    QString("Avg read interval was %1 msec. %2K block size")
    838859                //            .arg(readtimeavg).arg(readblocksize/1024));
  • libs/libmythtv/tv_play.cpp

     
    5555#include "mythdialogbox.h"
    5656#include "mythmainwindow.h"
    5757#include "mythscreenstack.h"
     58#include "storagegroup.h"
    5859
    5960#ifndef HAVE_ROUND
    6061#define round(x) ((int) ((x) + 0.5))
     
    16761677        else
    16771678        {
    16781679            ctx->LockPlayingInfo(__FILE__, __LINE__);
    1679             QString playbackURL = ctx->playingInfo->GetPlaybackURL();
     1680            uint io_multiplier;
     1681            QString playbackURL = ctx->playingInfo->GetPlaybackURL(
     1682                false, false, &io_multiplier);
    16801683            ctx->UnlockPlayingInfo(__FILE__, __LINE__);
    16811684
    16821685            bool opennow = (ctx->tvchain->GetCardType(-1) != "DUMMY");
     
    16851688                                          "cardtype(%2)")
    16861689                    .arg(playbackURL).arg(ctx->tvchain->GetCardType(-1)));
    16871690
    1688             ctx->SetRingBuffer(new RingBuffer(playbackURL, false, true,
    1689                                       opennow ? 12 : (uint)-1));
     1691            ctx->SetRingBuffer(new RingBuffer(
     1692                                   playbackURL, false, true,
     1693                                   opennow ? 12 : (uint)-1, io_multiplier));
    16901694            ctx->buffer->SetLiveMode(ctx->tvchain);
    16911695        }
    16921696
     
    17391743             TRANSITION(kState_None, kState_WatchingRecording))
    17401744    {
    17411745        ctx->LockPlayingInfo(__FILE__, __LINE__);
     1746        uint io_multiplier = StorageGroup::kDefaultIOMultiplier;
    17421747        QString playbackURL;
    17431748        if ((ctx->playingInfo->pathname.left(4) == "dvd:") ||
    17441749            (ctx->playingInfo->isVideo))
     
    17471752            playbackURL.detach();
    17481753        }
    17491754        else
     1755        {
    17501756            playbackURL = ctx->playingInfo->GetPlaybackURL(
    1751                               desiredNextState != kState_WatchingRecording);
     1757                desiredNextState != kState_WatchingRecording,
     1758                false, &io_multiplier);
     1759        }
    17521760        ctx->UnlockPlayingInfo(__FILE__, __LINE__);
    17531761       
    1754         ctx->SetRingBuffer(new RingBuffer(playbackURL, false));
     1762        ctx->SetRingBuffer(new RingBuffer(
     1763                               playbackURL, false, true, 12, io_multiplier));
    17551764       
    17561765        if (ctx->buffer && ctx->buffer->IsOpen())
    17571766        {
     
    60316040        else
    60326041        {
    60336042            ctx->LockPlayingInfo(__FILE__, __LINE__);
    6034             QString playbackURL = ctx->playingInfo->GetPlaybackURL();
    6035             ctx->SetRingBuffer(new RingBuffer(playbackURL, false));
     6043            uint io_multiplier;
     6044            QString playbackURL = ctx->playingInfo->GetPlaybackURL(
     6045                false, false, &io_multiplier);
     6046            ctx->SetRingBuffer(
     6047                new RingBuffer(playbackURL, false, true, 12, io_multiplier));
    60366048            ctx->tvchain->SetProgram(*ctx->playingInfo);
    60376049            ctx->buffer->SetLiveMode(ctx->tvchain);
    60386050            ctx->UnlockPlayingInfo(__FILE__, __LINE__);
  • libs/libmythtv/programinfo.h

     
    239239    bool SetRecordBasename(QString basename);
    240240    QString GetRecordBasename(bool fromDB = false) const;
    241241    QString GetPlaybackURL(bool checkMaster = false,
    242                            bool forceCheckLocal = false);
     242                           bool forceCheckLocal = false,
     243                           uint *p_io_multiplier = NULL);
    243244    QString MakeUniqueKey(void) const;
    244245    int CalculateLength(void) const;
    245246    int SecsTillStart() const;
  • libs/libmythtv/RingBuffer.h

     
    1313}
    1414
    1515#include "mythexp.h"
     16#include "storagegroup.h"
    1617
    1718class RemoteFile;
    1819class RemoteEncoder;
     
    2425{
    2526  public:
    2627    RingBuffer(const QString &lfilename, bool write,
    27                bool usereadahead = true, uint read_retries = 12/*6*/);
     28               bool usereadahead = true, uint read_retries = 12/*6*/,
     29               uint io_multiplier = StorageGroup::kDefaultIOMultiplier);
    2830   ~RingBuffer();
    2931
    3032    // Sets
     
    126128    long long readpos;
    127129    long long writepos;
    128130
     131    uint read_chunk_size;
     132    uint max_read_size;
     133
    129134    bool stopreads;
    130135
    131136    mutable pthread_rwlock_t rwlock;
     
    154159    float          playspeed;
    155160    int            fill_threshold;
    156161    int            fill_min;
    157     int            readblocksize;
     162    uint           readblocksize;
    158163
    159164    QWaitCondition pauseWait;
    160165
     
    184189 
    185190    // constants
    186191    static const uint kBufferSize;
     192    static const uint kDefaultReadBlockSize;
    187193    static const uint kReadTestSize;
    188194};
    189195
  • libs/libmythtv/tv_rec.cpp

     
    1111using namespace std;
    1212
    1313// Qt headers
    14 #include <qapplication.h>
    15 #include <qsqldatabase.h>
     14#include <QFileInfo>
    1615
    1716// MythTV headers
    1817#include "mythconfig.h"
     
    3332#include "previewgenerator.h"
    3433#include "storagegroup.h"
    3534#include "remoteutil.h"
     35#include "storagegroup.h"
    3636
    3737#include "atscstreamdata.h"
    3838#include "dvbstreamdata.h"
     
    7676
    7777static bool is_dishnet_eit(int cardid);
    7878static QString load_profile(QString,void*,ProgramInfo*,RecordingProfile&);
     79static uint calc_io_mult(const ProgramInfo*);
    7980
    8081/** \class TVRec
    8182 *  \brief This is the coordinating class of the \ref recorder_subsystem.
     
    40034004
    40044005    if (lastTuningRequest.flags & kFlagRecording)
    40054006    {
    4006         SetRingBuffer(new RingBuffer(rec->GetFileName(), true));
     4007        uint iom = calc_io_mult(rec);
     4008        SetRingBuffer(new RingBuffer(rec->GetFileName(), true, true, 12, iom));
    40074009        if (!ringBuffer->IsOpen())
    40084010        {
    40094011            VERBOSE(VB_IMPORTANT, LOC_ERR +
     
    43864388
    43874389    StartedRecording(prog);
    43884390
    4389     *rb = new RingBuffer(prog->GetFileName(), true);
     4391    uint iom = calc_io_mult(prog);
     4392    *rb = new RingBuffer(prog->GetFileName(), true, true, 12, iom);
    43904393    if (!(*rb)->IsOpen())
    43914394    {
    43924395        VERBOSE(VB_IMPORTANT, LOC_ERR +
     
    45004503        .arg(TVRec::FlagToString(flags));
    45014504}
    45024505
     4506static uint calc_io_mult(const ProgramInfo *pginfo)
     4507{
     4508    StorageGroup sgroup(pginfo->storagegroup, gContext->GetHostName());
     4509    return sgroup.QueryIOMultiplier(
     4510        QFileInfo(pginfo->GetFileName()).absolutePath());
     4511}
     4512
    45034513/* vim: set expandtab tabstop=4 shiftwidth=4: */
    45044514
  • libs/libmythtv/programinfo.cpp

     
    16211621 *         If the file is accessible locally, the filename will be returned,
    16221622 *         otherwise a myth:// URL will be returned.
    16231623 */
    1624 QString ProgramInfo::GetPlaybackURL(bool checkMaster, bool forceCheckLocal)
     1624QString ProgramInfo::GetPlaybackURL(
     1625    bool checkMaster, bool forceCheckLocal, uint *p_io_multiplier)
    16251626{
    16261627    QString tmpURL;
    16271628    QString basename = GetRecordBasename(true);
    16281629
    16291630    bool alwaysStream = gContext->GetNumSetting("AlwaysStreamFiles", 0);
    16301631
     1632    if (p_io_multiplier)
     1633        *p_io_multiplier = StorageGroup::kDefaultIOMultiplier;
     1634
    16311635    if ((!alwaysStream) ||
    16321636        (forceCheckLocal) ||
    16331637        (hostname == gContext->GetHostName()))
    16341638    {
    16351639        // Check to see if the file exists locally
    16361640        StorageGroup sgroup(storagegroup);
    1637         tmpURL = sgroup.FindRecordingFile(basename);
     1641        QString dirname = sgroup.FindRecordingDir(basename);
    16381642
    1639         if (tmpURL != "")
     1643        if (!dirname.isEmpty())
    16401644        {
     1645            tmpURL = dirname + "/" + basename;
    16411646            VERBOSE(VB_FILE, LOC +
    16421647                    QString("GetPlaybackURL: File is local: '%1'").arg(tmpURL));
     1648
     1649            if (p_io_multiplier)
     1650                *p_io_multiplier = sgroup.QueryIOMultiplier(dirname);
     1651
    16431652            return tmpURL;
    16441653        }
    16451654        else if (hostname == gContext->GetHostName())
  • libs/libmythtv/ThreadedFileWriter.h

     
    77#include <qwaitcondition.h>
    88#include <qstring.h>
    99
     10#include "storagegroup.h"
     11
    1012class ThreadedFileWriter
    1113{
    1214  public:
    13     ThreadedFileWriter(const QString &fname, int flags, mode_t mode);
     15    ThreadedFileWriter(const QString &fname, int flags, mode_t mode,
     16                       uint io_mult = StorageGroup::kDefaultIOMultiplier);
    1417    ~ThreadedFileWriter();
    1518
    1619    bool Open(void);
     
    4346    int             flags;
    4447    mode_t          mode;
    4548    int             fd;
     49    uint            io_multiplier;
    4650
    4751    // state
    4852    bool            no_writes;
     
    5155    bool            in_dtor;
    5256    bool            ignore_writes;
    5357    long long       tfw_min_write_size;
     58    long long       tfw_max_write_size;
    5459
    5560    // buffer position state
    5661    uint            rpos;    ///< points to end of data written to disk
  • libs/libmyth/storagegroup.h

     
    22#define _STORAGEGROUP_H
    33
    44#include <QStringList>
     5#include <QMap>
    56
    67#include "libmyth/settings.h"
    78#include "libmyth/mythwidgets.h"
     
    3233
    3334    static QStringList getRecordingsGroups(void);
    3435
     36    uint QueryIOMultiplier(const QString &dirname);
     37
     38    static const uint kSentinelIOMultiplier = 1;
     39    static const uint kDefaultIOMultiplier  = 5;
     40    static const uint kMinimumIOMultiplier  = 2;
     41    static const uint kMaximumIOMultiplier  = 5;
     42
    3543  private:
    3644    QString      m_groupname;
    3745    QString      m_hostname;
    3846    QStringList  m_dirlist;
     47    uint         io_multiplier;
    3948};
    4049
     50class DirectoryPathSetting : public TransLineEditSetting
     51{
     52  public:
     53    DirectoryPathSetting(QString &dir) :
     54        TransLineEditSetting(true), m_dir(dir)
     55    {
     56        setLabel(QObject::tr("Directory Path"));
     57        QString help = QObject::tr(
     58            "Specify a path to add to this storage group.");
     59        setHelpText(help);
     60    }
     61
     62    virtual void Load(void)
     63    {
     64        setValue(m_dir);
     65    }
     66
     67    virtual void Save(void)
     68    {
     69        m_dir = getValue();
     70    }
     71
     72  private:
     73    QString &m_dir;
     74};
     75
     76class DirectoryConfig : public ConfigurationWizard
     77{
     78  public:
     79    DirectoryConfig(QString &dir, uint &io_multiplier);
     80};
     81
     82class IOMultiplierSetting : public TransSpinBoxSetting
     83{
     84  public:
     85    IOMultiplierSetting(uint &io_multiplier) :
     86        TransSpinBoxSetting(StorageGroup::kMinimumIOMultiplier,
     87                            StorageGroup::kMaximumIOMultiplier, 1),
     88        m_io_multiplier(io_multiplier)
     89    {
     90        setLabel(QObject::tr("Read/Write Buffer"));
     91        QString help = QObject::tr(
     92            "Specify the amount of buffering to "
     93            "use for this directory. Default %1. "
     94            "Use caution when adjusting this.")
     95            .arg(StorageGroup::kDefaultIOMultiplier);
     96        setHelpText(help);
     97    }
     98
     99    virtual void Load(void)
     100    {
     101        setValue(m_io_multiplier);
     102    }
     103
     104    virtual void Save(void)
     105    {
     106        m_io_multiplier = getValue().toUInt();
     107    }
     108
     109  private:
     110    uint &m_io_multiplier;
     111};
     112
    41113class MPUBLIC StorageGroupEditor :
    42114    public QObject, public ConfigurationDialog
    43115{
     
    56128    void doDelete(void);
    57129
    58130  protected:
     131    void editDirectory(QString &dir, uint &io_multiplier);
     132
    59133    QString         m_group;
    60134    ListBoxSetting *listbox;
    61135    QString         lastValue;
     136    QMap<QString, uint> multiplier_map;
    62137};
    63138
    64139class MPUBLIC StorageGroupListEditor :
  • libs/libmyth/storagegroup.cpp

     
    2020    << "DB Backups"
    2121    ;
    2222
     23DirectoryConfig::DirectoryConfig(QString &dir, uint &io_multiplier)
     24{
     25    ConfigurationGroup* group = new VerticalConfigurationGroup(false, false);
     26
     27    if (dir.isEmpty())
     28        group->setLabel(QObject::tr("Add new directory"));
     29    else
     30        group->setLabel(QObject::tr("Settings for directory: ") + dir);
     31
     32    group->addChild(new DirectoryPathSetting(dir));
     33    group->addChild(new IOMultiplierSetting(io_multiplier));
     34
     35    addChild(group);
     36}
     37
    2338/****************************************************************************/
    2439
    2540/** \brief StorageGroup constructor.
     
    327342    return groups;
    328343}
    329344
     345uint StorageGroup::QueryIOMultiplier(const QString &dirname)
     346{
     347    if (dirname.isEmpty() || m_groupname.isEmpty() || m_hostname.isEmpty())
     348        return StorageGroup::kDefaultIOMultiplier;
     349
     350    QString dirname_t = dirname;
     351
     352    if (dirname.endsWith("/"))
     353        dirname_t.chop(1);
     354    else
     355        dirname_t.append("/");
     356
     357    uint io_multiplier;
     358    MSqlQuery query(MSqlQuery::InitCon());
     359
     360    query.prepare("SELECT io_multiplier FROM storagegroup "
     361                  "WHERE groupname = :GROUP "
     362                  "AND hostname = :HOSTNAME "
     363                  "AND (dirname = :DIRNAME  "
     364                  "OR dirname = :DIRNAME_T);");
     365    query.bindValue(":GROUP", m_groupname);
     366    query.bindValue(":HOSTNAME", m_hostname);
     367    query.bindValue(":DIRNAME", dirname);
     368    query.bindValue(":DIRNAME_T", dirname_t);
     369
     370    if (!query.exec())
     371    {
     372        MythDB::DBError("StorageGroup::QueryIOMultiplier()", query);
     373
     374        return StorageGroup::kDefaultIOMultiplier;
     375    }
     376    else if (!query.next())
     377    {
     378        VERBOSE(VB_FILE, LOC + QString(
     379                    "Unable to find storage group '%1' on host %2, "
     380                    "using default io_multiplier of %3.")
     381                .arg(m_groupname).arg(m_hostname)
     382                .arg(StorageGroup::kDefaultIOMultiplier));
     383
     384        return StorageGroup::kDefaultIOMultiplier;
     385    }
     386
     387    io_multiplier = query.value(0).toUInt();
     388
     389    if ((io_multiplier > kMaximumIOMultiplier) ||
     390        (io_multiplier < kMinimumIOMultiplier))
     391    {
     392        VERBOSE(VB_IMPORTANT, LOC +
     393                QString(
     394                    "Read/Write multiplier value of %1 for storage group "
     395                    "'%2' on host %3 "
     396                    "\n\t\t\tis out of range [%4,%5], setting to the "
     397                    "default value %6.")
     398                .arg(io_multiplier).arg(m_groupname).arg(m_hostname)
     399                .arg(kMinimumIOMultiplier)
     400                .arg(kMaximumIOMultiplier)
     401                .arg(kDefaultIOMultiplier));
     402
     403        io_multiplier = StorageGroup::kDefaultIOMultiplier;
     404    }
     405
     406    return io_multiplier;
     407}
     408
    330409/****************************************************************************/
    331410typedef enum {
    332411    SGPopup_OK = 0,
     
    401480
    402481    if (name == "__CREATE_NEW_STORAGE_DIRECTORY__")
    403482    {
     483        uint m_io_multiplier = StorageGroup::kDefaultIOMultiplier;
    404484        name = "";
    405         SGPopupResult result = StorageGroupPopup::showPopup(
    406             gContext->GetMainWindow(),
    407             tr("Add Storage Group Directory"),
    408             tr("Enter directory name or press SELECT to enter text via the "
    409                "On Screen Keyboard"), name);
    410         if (result == SGPopup_CANCEL)
     485
     486        DirectoryConfig config(name, m_io_multiplier);
     487
     488        if (config.exec() != MythDialog::Accepted)
    411489            return;
    412490
    413491        if (name.isEmpty())
     
    417495            name.append("/");
    418496
    419497        MSqlQuery query(MSqlQuery::InitCon());
    420         query.prepare("INSERT INTO storagegroup (groupname, hostname, dirname) "
    421                       "VALUES (:NAME, :HOSTNAME, :DIRNAME);");
     498        query.prepare("INSERT INTO storagegroup (groupname, hostname, "
     499                      "dirname, io_multiplier) "
     500                      "VALUES (:NAME, :HOSTNAME, :DIRNAME, :IO_MULTIPLIER);");
    422501        query.bindValue(":NAME", m_group);
    423502        query.bindValue(":DIRNAME", name);
     503        query.bindValue(":IO_MULTIPLIER", m_io_multiplier);
    424504        query.bindValue(":HOSTNAME", gContext->GetHostName());
    425505        if (!query.exec())
    426506            MythDB::DBError("StorageGroupEditor::open", query);
    427507        else
    428508            lastValue = name;
    429     } else {
    430         SGPopupResult result = StorageGroupPopup::showPopup(
    431             gContext->GetMainWindow(),
    432             tr("Edit Storage Group Directory"),
    433             tr("Enter directory name or press SELECT to enter text via the "
    434                "On Screen Keyboard"), name);
    435         if (result == SGPopup_CANCEL)
     509    }
     510    else
     511    {
     512        uint m_io_multiplier = multiplier_map[name];
     513        if (!m_io_multiplier)
     514            m_io_multiplier = StorageGroup::kDefaultIOMultiplier;
     515
     516        DirectoryConfig config(name, m_io_multiplier);
     517
     518        if (config.exec() != MythDialog::Accepted)
    436519            return;
    437520
    438521        if (name.right(1) != "/")
     
    450533        if (!query.exec())
    451534            MythDB::DBError("StorageGroupEditor::open", query);
    452535
    453         query.prepare("INSERT INTO storagegroup (groupname, hostname, dirname) "
    454                       "VALUES (:NAME, :HOSTNAME, :DIRNAME);");
     536        query.prepare("INSERT INTO storagegroup (groupname, hostname, "
     537                      "dirname, io_multiplier) "
     538                      "VALUES (:NAME, :HOSTNAME, :DIRNAME, :IO_MULTIPLIER)");
    455539        query.bindValue(":NAME", m_group);
    456540        query.bindValue(":DIRNAME", name);
     541        query.bindValue(":IO_MULTIPLIER", m_io_multiplier);
    457542        query.bindValue(":HOSTNAME", gContext->GetHostName());
    458543        if (!query.exec())
    459544            MythDB::DBError("StorageGroupEditor::open", query);
     
    502587void StorageGroupEditor::Load(void)
    503588{
    504589    listbox->clearSelections();
     590    multiplier_map.clear();
    505591
    506592    MSqlQuery query(MSqlQuery::InitCon());
    507     query.prepare("SELECT dirname, id FROM storagegroup "
     593    query.prepare("SELECT dirname, io_multiplier, id FROM storagegroup "
    508594                  "WHERE groupname = :NAME AND hostname = :HOSTNAME "
    509595                  "ORDER BY id;");
    510596    query.bindValue(":NAME", m_group);
     
    522608                first = false;
    523609            }
    524610            listbox->addSelection(query.value(0).toString());
     611            multiplier_map[query.value(0).toString()] = query.value(1).toUInt();
    525612        }
    526613    }
    527614
  • programs/mythbackend/mainserver.h

     
    150150    FileTransfer *getFileTransferByID(int id);
    151151    FileTransfer *getFileTransferBySock(MythSocket *socket);
    152152
    153     QString LocalFilePath(const QUrl &url);
     153    QString LocalFilePath(const QUrl &url, uint &io_multiplier);
    154154
    155155    static void *SpawnDeleteThread(void *param);
    156156    void DoDeleteThread(const DeleteStruct *ds);
  • programs/mythbackend/filetransfer.cpp

     
    88#include "mythsocket.h"
    99
    1010FileTransfer::FileTransfer(QString &filename, MythSocket *remote,
    11                            bool usereadahead, int retries) :
     11                           bool usereadahead, int retries, uint io_mult) :
    1212    readthreadlive(true), readsLocked(false),
    13     rbuffer(new RingBuffer(filename, false, usereadahead, retries)),
     13    rbuffer(new RingBuffer(filename, false, usereadahead, retries, io_mult)),
    1414    sock(remote), ateof(false), lock(QMutex::NonRecursive),
    1515    refLock(QMutex::NonRecursive), refCount(0)
    1616{
    1717}
    1818
    19 FileTransfer::FileTransfer(QString &filename, MythSocket *remote) :
     19FileTransfer::FileTransfer(QString &filename, MythSocket *remote,
     20                           uint io_mult) :
    2021    readthreadlive(true), readsLocked(false),
    21     rbuffer(new RingBuffer(filename, false)),
     22    rbuffer(new RingBuffer(filename, false, true, 12, io_mult)),
    2223    sock(remote), ateof(false), lock(QMutex::NonRecursive),
    2324    refLock(QMutex::NonRecursive), refCount(0)
    2425{
  • programs/mythbackend/mainserver.cpp

     
    10541054        VERBOSE(VB_IMPORTANT, QString("adding: %1 as a remote file transfer")
    10551055                               .arg(commands[2]));
    10561056        QUrl qurl = slist[1];
    1057         QString filename = LocalFilePath(qurl);
     1057        uint io_multiplier;
     1058        QString filename = LocalFilePath(qurl, io_multiplier);
    10581059
    10591060        FileTransfer *ft = NULL;
    10601061        bool usereadahead = true;
     
    10661067        }
    10671068
    10681069        if (retries >= 0)
    1069             ft = new FileTransfer(filename, socket, usereadahead, retries);
     1070        {
     1071            ft = new FileTransfer(
     1072                filename, socket, usereadahead, retries, io_multiplier);
     1073        }
    10701074        else
    1071             ft = new FileTransfer(filename, socket);
     1075        {
     1076            ft = new FileTransfer(filename, socket, io_multiplier);
     1077        }
    10721078
    10731079        sockListLock.lock();
    10741080        fileTransferList.push_back(ft);
     
    43494355        QApplication::exit(m_exitCode);
    43504356}
    43514357
    4352 QString MainServer::LocalFilePath(const QUrl &url)
     4358QString MainServer::LocalFilePath(const QUrl &url, uint &io_multiplier)
    43534359{
    43544360    QString lpath = url.path();
    43554361
     4362    io_multiplier = StorageGroup::kDefaultIOMultiplier;
     4363
    43564364    if (lpath.section('/', -2, -2) == "channels")
    43574365    {
    43584366        // This must be an icon request. Check channel.icon to be safe.
     
    43864394        ProgramInfo *pginfo = ProgramInfo::GetProgramFromBasename(fpath);
    43874395        if (pginfo)
    43884396        {
    4389             QString pburl = GetPlaybackURL(pginfo);
     4397            QString pburl = pginfo->GetPlaybackURL(false, true, &io_multiplier);
    43904398            if (pburl.left(1) == "/")
    43914399            {
    43924400                lpath = pburl.section('/', 0, -2) + "/" + lpath;
     
    44104418            lpath = QFileInfo(lpath).fileName();
    44114419            StorageGroup sgroup;
    44124420            QString tmpFile = sgroup.FindRecordingFile(lpath);
     4421            QFileInfo fi(tmpFile);
     4422            io_multiplier = sgroup.QueryIOMultiplier(fi.absolutePath());
    44134423            if (!tmpFile.isEmpty())
    44144424            {
    44154425                lpath = tmpFile;
  • programs/mythbackend/backendutil.cpp

     
    355355QString GetPlaybackURL(ProgramInfo *pginfo, bool storePath)
    356356{
    357357    QString result = "";
     358
    358359    QMutexLocker locker(&recordingPathLock);
    359360    QString cacheKey = QString("%1:%2").arg(pginfo->chanid)
    360                                .arg(pginfo->recstartts.toString(Qt::ISODate));
     361        .arg(pginfo->recstartts.toString(Qt::ISODate));
     362
    361363    if ((recordingPathCache.contains(cacheKey)) &&
    362364        (QFile::exists(recordingPathCache[cacheKey])))
    363365    {
  • programs/mythbackend/filetransfer.h

     
    99using namespace std;
    1010
    1111// Qt headers
    12 #include <qstring.h>
    13 #include <qmutex.h>
    14 #include <qwaitcondition.h>
     12#include <QString>
     13#include <QMutex>
     14#include <QWaitCondition>
    1515
     16// MythTV headers
     17#include "storagegroup.h"
     18
    1619class RingBuffer;
    1720class MythSocket;
    1821
     
    2124    friend class QObject; // quiet OSX gcc warning
    2225
    2326  public:
    24     FileTransfer(QString &filename, MythSocket *remote);
    2527    FileTransfer(QString &filename, MythSocket *remote,
    26                  bool usereadahead, int retries);
     28                 uint io_mult = StorageGroup::kDefaultIOMultiplier);
     29    FileTransfer(QString &filename, MythSocket *remote,
     30                 bool usereadahead, int retries,
     31                 uint io_mult = StorageGroup::kDefaultIOMultiplier);
    2732
    2833    MythSocket *getSocket() { return sock; }
    2934