Index: libs/libmythtv/mythiowrapper.cpp
===================================================================
--- libs/libmythtv/mythiowrapper.cpp	(revision 27380)
+++ libs/libmythtv/mythiowrapper.cpp	(working copy)
@@ -130,15 +130,17 @@
         else
         {
             if (flags & O_WRONLY)
-                rb = new RingBuffer(pathname, true, false, -1); // Writeable
+                rb = RingBuffer::Create(
+                    pathname, true, false,
+                    RingBuffer::kDefaultOpenTimeout, true); // Writeable
             else
-                rb = new RingBuffer(pathname, false, true, -1); // Read-Only
+                rb = RingBuffer::Create(
+                    pathname, false, true,
+                    RingBuffer::kDefaultOpenTimeout, true); // Read-Only
 
             if (!rb)
                 return -1;
 
-            rb->SetStreamOnly(true);
-            rb->OpenFile(pathname);
             rb->Start();
         }
 
Index: libs/libmythtv/NuppelVideoRecorder.cpp
===================================================================
--- libs/libmythtv/NuppelVideoRecorder.cpp	(revision 27380)
+++ libs/libmythtv/NuppelVideoRecorder.cpp	(working copy)
@@ -731,7 +731,7 @@
     if (!ringBuffer)
     {
         VERBOSE(VB_IMPORTANT, LOC + "Warning, old RingBuffer creation");
-        ringBuffer = new RingBuffer("output.nuv", true);
+        ringBuffer = RingBuffer::Create("output.nuv", true);
         weMadeBuffer = true;
         livetv = false;
         if (!ringBuffer->IsOpen())
Index: libs/libmythtv/importrecorder.cpp
===================================================================
--- libs/libmythtv/importrecorder.cpp	(revision 27380)
+++ libs/libmythtv/importrecorder.cpp	(working copy)
@@ -103,7 +103,7 @@
     if (_import_fd && _request_recording && !_error)
     {
         MythCommFlagPlayer *cfp = new MythCommFlagPlayer();
-        RingBuffer *rb = new RingBuffer(
+        RingBuffer *rb = RingBuffer::Create(
             ringBuffer->GetFilename(), false, true, 6000);
 
         PlayerContext *ctx = new PlayerContext(kImportRecorderInUseID);
Index: libs/libmythtv/avformatdecoder.cpp
===================================================================
--- libs/libmythtv/avformatdecoder.cpp	(revision 27380)
+++ libs/libmythtv/avformatdecoder.cpp	(working copy)
@@ -653,12 +653,8 @@
 void AvFormatDecoder::SeekReset(long long newKey, uint skipFrames,
                                 bool doflush, bool discardFrames)
 {
-    if (ringBuffer->isDVD())
-    {
-        if (ringBuffer->InDiscMenuOrStillFrame() ||
-            newKey == 0)
-            return;
-    }
+    if (ringBuffer->IsInDiscMenuOrStillFrame() || newKey == 0)
+        return;
 
     VERBOSE(VB_PLAYBACK, LOC +
             QString("SeekReset(%1, %2, %3 flush, %4 discard)")
@@ -718,7 +714,7 @@
 
         prevgoppos = 0;
         gopset = false;
-        if (!ringBuffer->isDVD())
+        if (!ringBuffer->IsDVD())
         {
             if (!no_dts_hack)
             {
@@ -760,7 +756,7 @@
 {
     DecoderBase::Reset();
 
-    if (ringBuffer->isDVD())
+    if (ringBuffer->IsDVD())
         SyncPositionMap();
 }
 
@@ -801,7 +797,8 @@
     int streamed = 0;
     int buffer_size = 32768;
 
-    if (ringBuffer->isDVD()) {
+    if (ringBuffer->IsDVD())
+    {
         streamed = 1;
         buffer_size = 2048;
     }
@@ -921,7 +918,7 @@
         QMutexLocker locker(avcodeclock);
         ret = av_find_stream_info(ic);
     }
-    if (ringBuffer->isDVD())
+    if (ringBuffer->IsDVD())
     {
         if (!ringBuffer->DVD()->StartFromBeginning())
             return -1;
@@ -936,13 +933,13 @@
         return -1;
     }
     ic->streams_changed = HandleStreamChange;
-    if (ringBuffer->isDVD())
+    if (ringBuffer->IsDVD())
         ic->streams_changed = HandleDVDStreamChange;
     ic->stream_change_data = this;
 
     fmt->flags &= ~AVFMT_NOFILE;
 
-    if (!ringBuffer->isDVD() && !ringBuffer->isBD() && !livetv)
+    if (!livetv && !ringBuffer->IsDisc())
         av_estimate_timings(ic, 0);
 
     // Scan for the initial A/V streams
@@ -1220,7 +1217,7 @@
             <<") type ("<<ff_codec_type_string(enc->codec_type)
             <<").");
 
-    if (ringBuffer && ringBuffer->isDVD())
+    if (ringBuffer && ringBuffer->IsDVD())
         directrendering = false;
 
     enc->opaque = (void *)this;
@@ -1722,7 +1719,7 @@
     map<int,uint> lang_aud_cnt;
     uint audioStreamCount = 0;
 
-    if (ringBuffer && ringBuffer->isDVD() &&
+    if (ringBuffer && ringBuffer->IsDVD() &&
         ringBuffer->DVD()->AudioStreamsChanged())
     {
         ringBuffer->DVD()->AudioStreamsChanged(false);
@@ -1818,12 +1815,12 @@
 #ifdef USING_XVMC
 
                     bool force_xv = no_hardware_decoders;
-                    if (ringBuffer && ringBuffer->isDVD())
+                    if (ringBuffer && ringBuffer->IsDVD())
                     {
                         if (dec.left(4) == "xvmc")
                             dvd_xvmc_enabled = true;
 
-                        if (ringBuffer->InDiscMenuOrStillFrame() &&
+                        if (ringBuffer->IsInDiscMenuOrStillFrame() &&
                             dvd_xvmc_enabled)
                         {
                             force_xv = true;
@@ -1846,7 +1843,7 @@
                         enc->codec_id = (CodecID)
                             myth2av_codecid(mcid, vcd, idct, mc, vdpau);
 
-                        if (ringBuffer && ringBuffer->isDVD() &&
+                        if (ringBuffer && ringBuffer->IsDVD() &&
                             (mcid == video_codec_id) &&
                             dvd_video_codec_changed)
                         {
@@ -2072,8 +2069,11 @@
         if (enc->codec_type == CODEC_TYPE_SUBTITLE)
         {
             int lang;
-            if (ringBuffer && ringBuffer->isBD())
-                lang = ringBuffer->BD()->GetSubtitleLanguage(subtitleStreamCount);
+            if (ringBuffer && ringBuffer->IsBD())
+            {
+                lang = ringBuffer->BD()->
+                    GetSubtitleLanguage(subtitleStreamCount);
+            }
             else
             {
                 AVMetadataTag *metatag = av_metadata_get(ic->streams[i]->metadata,
@@ -2097,16 +2097,22 @@
         if (enc->codec_type == CODEC_TYPE_AUDIO)
         {
             int lang;
-            if (ringBuffer && ringBuffer->isDVD())
+            if (ringBuffer && ringBuffer->IsDVD())
+            {
                 lang = ringBuffer->DVD()->GetAudioLanguage(
                     ringBuffer->DVD()->GetAudioTrackNum(ic->streams[i]->id));
-            else if (ringBuffer && ringBuffer->isBD())
+            }
+            else if (ringBuffer && ringBuffer->IsBD())
+            {
                 lang = ringBuffer->BD()->GetAudioLanguage(audioStreamCount);
+            }
             else
             {
-                AVMetadataTag *metatag = av_metadata_get(ic->streams[i]->metadata,
-                                                         "language", NULL, 0);
-                lang = metatag ? get_canonical_lang(metatag->value) : iso639_str3_to_key("und");
+                AVMetadataTag *metatag = av_metadata_get(
+                    ic->streams[i]->metadata,
+                    "language", NULL, 0);
+                lang = metatag ? get_canonical_lang(metatag->value) :
+                    iso639_str3_to_key("und");
             }
 
             int channels  = ic->streams[i]->codec->channels;
@@ -2124,8 +2130,9 @@
             else
             {
                 int logical_stream_id;
-                if (ringBuffer && ringBuffer->isDVD())
-                    logical_stream_id = ringBuffer->DVD()->GetAudioTrackNum(ic->streams[i]->id);
+                if (ringBuffer && ringBuffer->IsDVD())
+                    logical_stream_id =
+                        ringBuffer->DVD()->GetAudioTrackNum(ic->streams[i]->id);
                 else
                     logical_stream_id = ic->streams[i]->id;
 
@@ -2149,7 +2156,7 @@
             ringBuffer->UpdateRawBitrate(bitrate);
     }
 
-    if (ringBuffer && ringBuffer->isDVD())
+    if (ringBuffer && ringBuffer->IsDVD())
     {
         if (tracks[kTrackTypeAudio].size() > 1)
         {
@@ -2192,7 +2199,7 @@
                 m_parent->EnableSubtitles(false);
             }
             else if (trackNo >= 0 && trackNo < trackcount &&
-                    !ringBuffer->InDiscMenuOrStillFrame())
+                    !ringBuffer->IsInDiscMenuOrStillFrame())
             {
                     SetTrack(kTrackTypeSubtitle, trackNo);
                     m_parent->EnableSubtitles(true);
@@ -2209,7 +2216,7 @@
     {
         m_audio->SetAudioParams(FORMAT_NONE, -1, -1, CODEC_ID_NONE, -1, false);
         m_audio->ReinitAudio();
-        if (ringBuffer && ringBuffer->isDVD())
+        if (ringBuffer && ringBuffer->IsDVD())
             audioIn = AudioInfo();
     }
 
@@ -2755,7 +2762,7 @@
     {
         bufptr = ff_find_start_code(bufptr, bufend, &start_code_state);
 
-        if (ringBuffer->isDVD() && (start_code_state == SEQ_END_CODE))
+        if (ringBuffer->IsDVD() && (start_code_state == SEQ_END_CODE))
             ringBuffer->DVD()->NewSequence(true);
 
         if (start_code_state >= SLICE_MIN && start_code_state <= SLICE_MAX)
@@ -2999,7 +3006,7 @@
         context->reordered_opaque = pkt->pts;
         ret = avcodec_decode_video2(context, &mpa_pic, &gotpicture, pkt);
         // Reparse it to not drop the DVD still frame
-        if (ringBuffer->isDVD() && ringBuffer->DVD()->NeedsStillFrame())
+        if (ringBuffer->IsDVD() && ringBuffer->DVD()->NeedsStillFrame())
             ret = avcodec_decode_video2(context, &mpa_pic, &gotpicture, pkt);
     }
     avcodeclock->unlock();
@@ -3034,7 +3041,7 @@
     // the DTS timestamp is missing. Also use fixups for missing PTS instead of
     // DTS to avoid oscillating between PTS and DTS. Only select DTS if PTS is
     // more faulty or never detected.
-    if (ringBuffer->isDVD())
+    if (ringBuffer->IsDVD())
     {
         if (pkt->dts != (int64_t)AV_NOPTS_VALUE)
             pts = pkt->dts;
@@ -3136,7 +3143,7 @@
     // Validate the video pts against the last pts. If it's
     // a little bit smaller, equal or missing, compute
     // it from the last. Otherwise assume a wraparound.
-    if (!ringBuffer->isDVD() &&
+    if (!ringBuffer->IsDVD() &&
         temppts <= lastvpts &&
         (temppts + 10000 > lastvpts || temppts <= 0))
     {
@@ -3350,7 +3357,7 @@
     AVSubtitle subtitle;
     memset(&subtitle, 0, sizeof(AVSubtitle));
 
-    if (ringBuffer->isDVD())
+    if (ringBuffer->IsDVD())
     {
         if (ringBuffer->DVD()->NumMenuButtons() > 0)
         {
@@ -3461,7 +3468,7 @@
     int lang_key = tracks[type][trackNo].language;
     if (kTrackTypeAudio == type)
     {
-        if (ringBuffer->isDVD())
+        if (ringBuffer->IsDVD())
             lang_key = ringBuffer->DVD()->GetAudioLanguage(trackNo);
 
         QString msg = iso639_key_toName(lang_key);
@@ -3478,7 +3485,7 @@
             msg += QString(" %1").arg(s->codec->codec->name).toUpper();
 
         int channels = 0;
-        if (ringBuffer->isDVD())
+        if (ringBuffer->IsDVD())
             channels = ringBuffer->DVD()->GetNumAudioChannels(trackNo);
         else if (s->codec->channels)
             channels = tracks[kTrackTypeAudio][trackNo].orig_num_channels;
@@ -3494,7 +3501,7 @@
     }
     else if (kTrackTypeSubtitle == type)
     {
-        if (ringBuffer->isDVD())
+        if (ringBuffer->IsDVD())
             lang_key = ringBuffer->DVD()->GetSubtitleLanguage(trackNo);
 
         return QObject::tr("Subtitle") + QString(" %1: %2")
@@ -3562,7 +3569,7 @@
     if (kTrackTypeAudio == type)
         return AutoSelectAudioTrack();
 
-    if (ringBuffer->InDiscMenuOrStillFrame())
+    if (ringBuffer->IsInDiscMenuOrStillFrame())
         return -1;
 
     return DecoderBase::AutoSelectTrack(type);
@@ -4017,7 +4024,7 @@
 
         total_decoded_audio += data_size;
 
-        allowedquit |= ringBuffer->InDiscMenuOrStillFrame();
+        allowedquit |= ringBuffer->IsInDiscMenuOrStillFrame();
         // Audio can expand by a factor of 6 in audiooutputbase's audiobuffer
         allowedquit |= !(decodetype & kDecodeVideo) &&
                        ((ofill + total_decoded_audio * 6) > othresh);
@@ -4110,10 +4117,11 @@
                 allowedquit = true;
         }
 
-        if (ringBuffer->isDVD())
+        if (ringBuffer->IsDVD())
         {
             // Update the title length
-            if (m_parent->AtNormalSpeed() && ringBuffer->DVD()->PGCLengthChanged())
+            if (m_parent->AtNormalSpeed() &&
+                ringBuffer->DVD()->PGCLengthChanged())
             {
                 ResetPosMap();
                 SyncPositionMap();
@@ -4124,11 +4132,12 @@
             if (ringBuffer->DVD()->AudioStreamsChanged())
                 ScanStreams(true);
 
-            // always use the first video stream (must come after ScanStreams above)
+            // Always use the first video stream
+            // (must come after ScanStreams above)
             selectedTrack[kTrackTypeVideo].av_stream_index = 0;
         }
 
-        if (ringBuffer->isBD())
+        if (ringBuffer->IsBD())
         {
             // Update the title length
             if (m_parent->AtNormalSpeed() && ringBuffer->BD()->TitleChanged())
@@ -4138,7 +4147,8 @@
                 UpdateFramesPlayed();
             }
 
-            // always use the first video stream (must come after ScanStreams above)
+            // Always use the first video stream
+            // (must come after ScanStreams above)
             selectedTrack[kTrackTypeVideo].av_stream_index = 0;
         }
 
@@ -4154,7 +4164,7 @@
             else if (lowbuffers && ((decodetype & kDecodeAV) == kDecodeAV) &&
                      storedPackets.count() < max_video_queue_size &&
                      lastapts < lastvpts + 100 &&
-                     !ringBuffer->InDiscMenuOrStillFrame())
+                     !ringBuffer->IsInDiscMenuOrStillFrame())
             {
                 storevideoframes = true;
             }
@@ -4223,7 +4233,7 @@
             continue;
         }
 
-        if (ringBuffer->isDVD() &&
+        if (ringBuffer->IsDVD() &&
             curstream->codec->codec_type == CODEC_TYPE_VIDEO)
         {
 #ifdef USING_XVMC
@@ -4239,7 +4249,7 @@
                         dvd_xvmc_active = true;
                     }
 
-                    bool indiscmenu   = ringBuffer->InDiscMenuOrStillFrame();
+                    bool indiscmenu   = ringBuffer->IsInDiscMenuOrStillFrame();
                     if ((indiscmenu && dvd_xvmc_active) ||
                         ((!indiscmenu && !dvd_xvmc_active)))
                     {
@@ -4252,9 +4262,10 @@
 
                 if ((video_width > 0) && dvd_video_codec_changed)
                 {
-                    VERBOSE(VB_PLAYBACK, LOC + QString("DVD Stream/Codec Change "
-                                "video_width %1 current_width %2 "
-                                "dvd_video_codec_changed %3")
+                    VERBOSE(VB_PLAYBACK, LOC +
+                            QString("DVD Stream/Codec Change "
+                                    "video_width %1 current_width %2 "
+                                    "dvd_video_codec_changed %3")
                             .arg(video_width).arg(current_width)
                             .arg(dvd_video_codec_changed));
                     av_free_packet(pkt);
Index: libs/libmythtv/DVDRingBuffer.cpp
===================================================================
--- libs/libmythtv/DVDRingBuffer.cpp	(revision 27380)
+++ libs/libmythtv/DVDRingBuffer.cpp	(working copy)
@@ -33,40 +33,39 @@
     "Part",
 };
 
-DVDRingBufferPriv::DVDRingBufferPriv()
-    : m_dvdnav(NULL),     m_dvdBlockReadBuf(NULL),
-      m_dvdFilename(QString::null),
-      m_dvdBlockRPos(0),  m_dvdBlockWPos(0),
-      m_pgLength(0),      m_pgcLength(0),
-      m_cellStart(0),     m_cellChanged(false),
-      m_pgcLengthChanged(false), m_pgStart(0),
-      m_currentpos(0),
-      m_lastNav(NULL),    m_part(0), m_lastPart(0),
-      m_title(0),         m_lastTitle(0),   m_playerWait(false),
-      m_titleParts(0),    m_gotStop(false), m_currentAngle(0),
-      m_currentTitleAngleCount(0), m_newSequence(false),
-      m_still(0), m_lastStill(0),
-      m_audioStreamsChanged(false),
-      m_dvdWaiting(false),
-      m_titleLength(0),
+DVDRingBuffer::DVDRingBuffer(const QString &lfilename) :
+    m_dvdnav(NULL),     m_dvdBlockReadBuf(NULL),
+    m_dvdBlockRPos(0),  m_dvdBlockWPos(0),
+    m_pgLength(0),      m_pgcLength(0),
+    m_cellStart(0),     m_cellChanged(false),
+    m_pgcLengthChanged(false), m_pgStart(0),
+    m_currentpos(0),
+    m_lastNav(NULL),    m_part(0), m_lastPart(0),
+    m_title(0),         m_lastTitle(0),   m_playerWait(false),
+    m_titleParts(0),    m_gotStop(false), m_currentAngle(0),
+    m_currentTitleAngleCount(0), m_newSequence(false),
+    m_still(0), m_lastStill(0),
+    m_audioStreamsChanged(false),
+    m_dvdWaiting(false),
+    m_titleLength(0),
 
-      m_skipstillorwait(true),
-      m_cellstartPos(0), m_buttonSelected(false),
-      m_buttonExists(false), m_cellid(0),
-      m_lastcellid(0), m_vobid(0),
-      m_lastvobid(0), m_cellRepeated(false),
+    m_skipstillorwait(true),
+    m_cellstartPos(0), m_buttonSelected(false),
+    m_buttonExists(false), m_cellid(0),
+    m_lastcellid(0), m_vobid(0),
+    m_lastvobid(0), m_cellRepeated(false),
 
-      m_curAudioTrack(0),
-      m_curSubtitleTrack(0),
-      m_autoselectsubtitle(true),
-      m_dvdname(NULL), m_serialnumber(NULL),
-      m_seeking(false), m_seektime(0),
-      m_currentTime(0),
-      m_parent(NULL),
+    m_curAudioTrack(0),
+    m_curSubtitleTrack(0),
+    m_autoselectsubtitle(true),
+    m_dvdname(NULL), m_serialnumber(NULL),
+    m_seeking(false), m_seektime(0),
+    m_currentTime(0),
+    m_parent(NULL),
 
-      // Menu/buttons
-      m_inMenu(false), m_buttonVersion(1), m_buttonStreamID(0),
-      m_hl_button(0, 0, 0, 0), m_menuSpuPkt(0), m_menuBuflength(0)
+    // Menu/buttons
+    m_inMenu(false), m_buttonVersion(1), m_buttonStreamID(0),
+    m_hl_button(0, 0, 0, 0), m_menuSpuPkt(0), m_menuBuflength(0)
 {
     memset(&m_dvdMenuButton, 0, sizeof(AVSubtitle));
     memset(m_dvdBlockWriteBuf, 0, sizeof(char) * DVD_BLOCK_SIZE);
@@ -78,15 +77,17 @@
 
     for (uint i = 0; i < 8; i++)
         m_seekSpeedMap.insert(def[i], seekValues[i]);
+
+    OpenFile(filename);
 }
 
-DVDRingBufferPriv::~DVDRingBufferPriv()
+DVDRingBuffer::~DVDRingBuffer()
 {
     CloseDVD();
     ClearMenuSPUParameters();
 }
 
-void DVDRingBufferPriv::CloseDVD(void)
+void DVDRingBuffer::CloseDVD(void)
 {
     if (m_dvdnav)
     {
@@ -96,13 +97,90 @@
     }
 }
 
-long long DVDRingBufferPriv::NormalSeek(long long time)
+long long DVDRingBuffer::Seek(long long pos, int whence, bool has_lock)
 {
+    VERBOSE(VB_FILE, LOC + QString("Seek(%1,%2,%3)")
+            .arg(pos).arg((SEEK_SET==whence)?"SEEK_SET":
+                          ((SEEK_CUR==whence)?"SEEK_CUR":"SEEK_END"))
+            .arg(has_lock?"locked":"unlocked"));
+
+    long long ret = -1;
+
+    // lockForWrite takes priority over lockForRead, so this will
+    // take priority over the lockForRead in the read ahead thread.
+    if (!has_lock)
+        rwlock.lockForWrite();
+
+    poslock.lockForWrite();
+
+    // Optimize no-op seeks
+    if (readaheadrunning &&
+        ((whence == SEEK_SET && pos == readpos) ||
+         (whence == SEEK_CUR && pos == 0)))
+    {
+        ret = readpos;
+
+        poslock.unlock();
+        if (!has_lock)
+            rwlock.unlock();
+
+        return ret;
+    }
+
+    // only valid for SEEK_SET & SEEK_CUR
+    long long new_pos = (SEEK_SET==whence) ? pos : readpos + pos;
+
+    // Here we perform a normal seek. When successful we
+    // need to call ResetReadAhead(). A reset means we will
+    // need to refill the buffer, which takes some time.
+    if ((SEEK_END == whence) ||
+        ((SEEK_CUR == whence) && new_pos != 0))
+    {
+        errno = EINVAL;
+        ret = -1;
+    }
+    else
+    {
+        NormalSeek(new_pos);
+        ret = new_pos;
+    }
+
+    if (ret >= 0)
+    {
+        readpos = ret;
+        
+        ignorereadpos = -1;
+
+        if (readaheadrunning)
+            ResetReadAhead(readpos);
+
+        readAdjust = 0;
+    }
+    else
+    {
+        QString cmd = QString("Seek(%1, %2)").arg(pos)
+            .arg((SEEK_SET == whence) ? "SEEK_SET" :
+                 ((SEEK_CUR == whence) ?"SEEK_CUR" : "SEEK_END"));
+        VERBOSE(VB_IMPORTANT, LOC_ERR + cmd + " Failed" + ENO);
+    }
+
+    poslock.unlock();
+
+    generalWait.wakeAll();
+
+    if (!has_lock)
+        rwlock.unlock();
+
+    return ret;
+}
+
+long long DVDRingBuffer::NormalSeek(long long time)
+{
     QMutexLocker lock(&m_seekLock);
     return Seek(time);
 }
 
-long long DVDRingBufferPriv::Seek(long long time)
+long long DVDRingBuffer::Seek(long long time)
 {
     dvdnav_status_t dvdRet = DVDNAV_STATUS_OK;
 
@@ -148,7 +226,7 @@
     return m_currentpos;
 }
 
-void DVDRingBufferPriv::GetDescForPos(QString &desc)
+void DVDRingBuffer::GetDescForPos(QString &desc)
 {
     if (m_inMenu)
     {
@@ -163,78 +241,98 @@
     }
 }
 
-bool DVDRingBufferPriv::OpenFile(const QString &filename)
+bool DVDRingBuffer::OpenFile(const QString &lfilename, uint retry_ms)
 {
-    m_dvdFilename = filename;
-    m_dvdFilename.detach();
-    QByteArray fname = m_dvdFilename.toLocal8Bit();
+    rwlock.lockForWrite();
 
+    if (m_dvdnav)
+    {
+        dvdnav_close(m_dvdnav);
+        m_dvdnav = NULL;
+    }
+
+    filename = lfilename;
+    QByteArray fname = filename.toLocal8Bit();
+
     dvdnav_status_t res = dvdnav_open(&m_dvdnav, fname.constData());
     if (res == DVDNAV_STATUS_ERR)
     {
         VERBOSE(VB_IMPORTANT, QString("Failed to open DVD device at %1")
                 .arg(fname.constData()));
+        rwlock.unlock();
         return false;
     }
-    else
-    {
-        VERBOSE(VB_IMPORTANT, QString("Opened DVD device at %1")
-                .arg(fname.constData()));
-        dvdnav_set_readahead_flag(m_dvdnav, 0);
-        dvdnav_set_PGC_positioning_flag(m_dvdnav, 1);
 
-        int32_t num_titles = 0;
-        int32_t num_parts  = 0;
+    VERBOSE(VB_IMPORTANT, QString("Opened DVD device at %1")
+            .arg(fname.constData()));
 
+    dvdnav_set_readahead_flag(m_dvdnav, 0);
+    dvdnav_set_PGC_positioning_flag(m_dvdnav, 1);
+
+    int32_t num_titles = 0;
+    int32_t num_parts  = 0;
+
+    res = dvdnav_get_number_of_titles(m_dvdnav, &num_titles);
+    if (num_titles == 0 || res == DVDNAV_STATUS_ERR)
+    {
+        char buf[DVD_BLOCK_SIZE * 5];
+        VERBOSE(VB_IMPORTANT, QString("Reading %1 bytes from the drive")
+                .arg(DVD_BLOCK_SIZE * 5));
+        safe_read(buf, DVD_BLOCK_SIZE * 5);
         res = dvdnav_get_number_of_titles(m_dvdnav, &num_titles);
-        if (num_titles == 0 || res == DVDNAV_STATUS_ERR)
-        {
-            char buf[DVD_BLOCK_SIZE * 5];
-            VERBOSE(VB_IMPORTANT, QString("Reading %1 bytes from the drive")
-                    .arg(DVD_BLOCK_SIZE * 5));
-            safe_read(buf, DVD_BLOCK_SIZE * 5);
-            res = dvdnav_get_number_of_titles(m_dvdnav, &num_titles);
-        }
+    }
 
-        if (res == DVDNAV_STATUS_ERR)
+    if (res == DVDNAV_STATUS_ERR)
+    {
+        VERBOSE(VB_IMPORTANT,
+                QString("Failed to get the number of titles on the DVD" ));
+    }
+    else
+    {
+        VERBOSE(VB_IMPORTANT, QString("There are %1 titles on the disk")
+                .arg(num_titles));
+
+        for (int i = 1; i < num_titles + 1; i++)
         {
-            VERBOSE(VB_IMPORTANT,
-                    QString("Failed to get the number of titles on the DVD" ));
-        }
-        else
-        {
-            VERBOSE(VB_IMPORTANT, QString("There are %1 titles on the disk")
-                    .arg(num_titles));
-
-            for(int i = 1; i < num_titles + 1; i++)
+            res = dvdnav_get_number_of_parts(m_dvdnav, i, &num_parts);
+            if (res != DVDNAV_STATUS_ERR)
             {
-                res = dvdnav_get_number_of_parts(m_dvdnav, i, &num_parts);
-                if (res != DVDNAV_STATUS_ERR)
-                {
-                    VERBOSE(VB_IMPORTANT, LOC + QString("Title %1 has %2 parts.")
+                VERBOSE(VB_IMPORTANT, LOC + QString("Title %1 has %2 parts.")
                         .arg(i).arg(num_parts));
-                }
-                else
-                {
-                    VERBOSE(VB_IMPORTANT, LOC_ERR +
+            }
+            else
+            {
+                VERBOSE(VB_IMPORTANT, LOC_ERR +
                         QString("Failed to get number of parts for title %1")
                         .arg(i));
-                }
             }
         }
+    }
 
-        dvdnav_title_play(m_dvdnav, 1);
-        dvdnav_current_title_info(m_dvdnav, &m_title, &m_part);
-        dvdnav_get_title_string(m_dvdnav, &m_dvdname);
-        dvdnav_get_serial_string(m_dvdnav, &m_serialnumber);
-        dvdnav_get_angle_info(m_dvdnav, &m_currentAngle, &m_currentTitleAngleCount);
-        SetDVDSpeed();
-        VERBOSE(VB_PLAYBACK, QString("DVD Serial Number %1").arg(m_serialnumber));
-        return true;
-    }
+    dvdnav_title_play(m_dvdnav, 1);
+    dvdnav_current_title_info(m_dvdnav, &m_title, &m_part);
+    dvdnav_get_title_string(m_dvdnav, &m_dvdname);
+    dvdnav_get_serial_string(m_dvdnav, &m_serialnumber);
+    dvdnav_get_angle_info(m_dvdnav, &m_currentAngle, &m_currentTitleAngleCount);
+    SetDVDSpeed();
+
+    VERBOSE(VB_PLAYBACK, QString("DVD Serial Number %1").arg(m_serialnumber));
+
+    readblocksize   = DVD_BLOCK_SIZE * 62;
+    setswitchtonext = false;
+    ateof           = false;
+    commserror      = false;
+    numfailures     = 0;
+    rawbitrate      = 8000;
+
+    CalcReadAheadThresh();
+
+    rwlock.unlock();
+
+    return true;
 }
 
-bool DVDRingBufferPriv::StartFromBeginning(void)
+bool DVDRingBuffer::StartFromBeginning(void)
 {
     if (!m_dvdnav)
         return false;
@@ -249,7 +347,7 @@
                 "DVD errored after initial scan - trying again");
         CloseDVD();
         m_gotStop = false;
-        OpenFile(m_dvdFilename);
+        OpenFile(filename);
         if (!m_dvdnav)
         {
             VERBOSE(VB_IMPORTANT, LOC + "Failed to re-open DVD.");
@@ -266,7 +364,7 @@
 
 /** \brief returns current position in the PGC.
  */
-long long DVDRingBufferPriv::GetReadPosition(void)
+long long DVDRingBuffer::GetReadPosition(void) const
 {
     uint32_t pos = 0;
     uint32_t length = 1;
@@ -281,7 +379,7 @@
     return pos * DVD_BLOCK_SIZE;
 }
 
-void DVDRingBufferPriv::WaitForPlayer(void)
+void DVDRingBuffer::WaitForPlayer(void)
 {
     if (!m_skipstillorwait)
     {
@@ -299,7 +397,7 @@
     }
 }
 
-int DVDRingBufferPriv::safe_read(void *data, unsigned sz)
+int DVDRingBuffer::safe_read(void *data, uint sz)
 {
     dvdnav_status_t dvdStat;
     unsigned char  *blockBuf     = NULL;
@@ -313,6 +411,7 @@
     if (m_gotStop)
     {
         VERBOSE(VB_IMPORTANT, LOC + "safe_read: called after DVDNAV_STOP");
+        errno = EBADF;
         return -1;
     }
 
@@ -327,6 +426,7 @@
         {
             VERBOSE(VB_IMPORTANT, QString("Error reading block from DVD: %1")
                     .arg(dvdnav_err_to_string(m_dvdnav)));
+            errno = EIO;
             return -1;
         }
 
@@ -386,7 +486,8 @@
 
                 // debug
                 VERBOSE(VB_PLAYBACK, LOC +
-                    QString("---- DVDNAV_CELL_CHANGE - Cell #%1 Menu %2 Length %3")
+                    QString("---- DVDNAV_CELL_CHANGE - Cell "
+                            "#%1 Menu %2 Length %3")
                     .arg(cell_event->cellN).arg(m_inMenu ? "Yes" : "No")
                     .arg((float)cell_event->cell_length / 90000.0f, 0, 'f', 1));
                 QString still = m_still ? ((m_still < 0xff) ?
@@ -401,14 +502,16 @@
                 }
                 else
                 {
-                    VERBOSE(VB_PLAYBACK, LOC + QString("Title #%1: %2 Part %3 of %4")
+                    VERBOSE(VB_PLAYBACK, LOC +
+                            QString("Title #%1: %2 Part %3 of %4")
                         .arg(m_title).arg(still).arg(m_part).arg(m_titleParts));
                 }
 
                 // wait unless it is a transition from one normal video cell to
                 // another or the same menu id
                 if (((m_still != m_lastStill) || (m_title != m_lastTitle)) &&
-                    !((m_title == 0 && m_lastTitle == 0) && (m_part == m_lastPart)))
+                    !((m_title == 0 && m_lastTitle == 0) &&
+                      (m_part == m_lastPart)))
                 {
                     WaitForPlayer();
                 }
@@ -492,8 +595,8 @@
                         QString(LOC + "DVDNAV_SPU_STREAM_CHANGE: "
                                 "physicalwide %1, physicalletterbox %2, "
                                 "physicalpanscan %3, currenttrack %4")
-                                .arg(spu->physical_wide).arg(spu->physical_letterbox)
-                                .arg(spu->physical_pan_scan).arg(m_curSubtitleTrack));
+                        .arg(spu->physical_wide).arg(spu->physical_letterbox)
+                        .arg(spu->physical_pan_scan).arg(m_curSubtitleTrack));
 
                 // release buffer
                 if (blockBuf != m_dvdBlockWriteBuf)
@@ -557,7 +660,8 @@
                 if (m_seeking)
                 {
 
-                    int relativetime = (int)((m_seektime - m_currentTime)/ 90000);
+                    int relativetime =
+                        (int)((m_seektime - m_currentTime)/ 90000);
                     if (relativetime <= 1)
                     {
                         m_seeking = false;
@@ -737,7 +841,7 @@
     return tot;
 }
 
-bool DVDRingBufferPriv::nextTrack(void)
+bool DVDRingBuffer::nextTrack(void)
 {
     int newPart = m_part + 1;
 
@@ -751,7 +855,7 @@
     return false;
 }
 
-void DVDRingBufferPriv::prevTrack(void)
+void DVDRingBuffer::prevTrack(void)
 {
     int newPart = m_part - 1;
 
@@ -766,21 +870,21 @@
 /** \brief get the total time of the title in seconds
  * 90000 ticks = 1 sec
  */
-uint DVDRingBufferPriv::GetTotalTimeOfTitle(void)
+uint DVDRingBuffer::GetTotalTimeOfTitle(void)
 {
     return m_pgcLength / 90000;
 }
 
 /** \brief get the start of the cell in seconds
  */
-uint DVDRingBufferPriv::GetCellStart(void)
+uint DVDRingBuffer::GetCellStart(void)
 {
     return m_cellStart / 90000;
 }
 
 /** \brief check if dvd cell has changed
  */
-bool DVDRingBufferPriv::CellChanged(void)
+bool DVDRingBuffer::CellChanged(void)
 {
     bool ret = m_cellChanged;
     m_cellChanged = false;
@@ -789,21 +893,21 @@
 
 /** \brief check if pgc length has changed
  */
-bool DVDRingBufferPriv::PGCLengthChanged(void)
+bool DVDRingBuffer::PGCLengthChanged(void)
 {
     bool ret = m_pgcLengthChanged;
     m_pgcLengthChanged = false;
     return ret;
 }
 
-void DVDRingBufferPriv::SkipStillFrame(void)
+void DVDRingBuffer::SkipStillFrame(void)
 {
     QMutexLocker locker(&m_seekLock);
     VERBOSE(VB_PLAYBACK, LOC + "Skipping still frame.");
     dvdnav_still_skip(m_dvdnav);
 }
 
-void DVDRingBufferPriv::WaitSkip(void)
+void DVDRingBuffer::WaitSkip(void)
 {
     QMutexLocker locker(&m_seekLock);
     dvdnav_wait_skip(m_dvdnav);
@@ -813,7 +917,7 @@
 
 /** \brief jump to a dvd root or chapter menu
  */
-bool DVDRingBufferPriv::GoToMenu(const QString str)
+bool DVDRingBuffer::GoToMenu(const QString str)
 {
     DVDMenuID_t menuid;
     QMutexLocker locker(&m_seekLock);
@@ -839,21 +943,21 @@
     return false;
 }
 
-void DVDRingBufferPriv::GoToNextProgram(void)
+void DVDRingBuffer::GoToNextProgram(void)
 {
     QMutexLocker locker(&m_seekLock);
     if (!dvdnav_is_domain_vts(m_dvdnav))
         dvdnav_next_pg_search(m_dvdnav);
 }
 
-void DVDRingBufferPriv::GoToPreviousProgram(void)
+void DVDRingBuffer::GoToPreviousProgram(void)
 {
     QMutexLocker locker(&m_seekLock);
     if (!dvdnav_is_domain_vts(m_dvdnav))
         dvdnav_prev_pg_search(m_dvdnav);
 }
 
-void DVDRingBufferPriv::MoveButtonLeft(void)
+void DVDRingBuffer::MoveButtonLeft(void)
 {
     if (NumMenuButtons() > 1)
     {
@@ -862,7 +966,7 @@
     }
 }
 
-void DVDRingBufferPriv::MoveButtonRight(void)
+void DVDRingBuffer::MoveButtonRight(void)
 {
     if (NumMenuButtons() > 1)
     {
@@ -871,7 +975,7 @@
     }
 }
 
-void DVDRingBufferPriv::MoveButtonUp(void)
+void DVDRingBuffer::MoveButtonUp(void)
 {
     if (NumMenuButtons() > 1)
     {
@@ -880,7 +984,7 @@
     }
 }
 
-void DVDRingBufferPriv::MoveButtonDown(void)
+void DVDRingBuffer::MoveButtonDown(void)
 {
     if (NumMenuButtons() > 1)
     {
@@ -891,7 +995,7 @@
 
 /** \brief action taken when a dvd menu button is selected
  */
-void DVDRingBufferPriv::ActivateButton(void)
+void DVDRingBuffer::ActivateButton(void)
 {
     if (NumMenuButtons() > 0)
     {
@@ -903,7 +1007,7 @@
 
 /** \brief get SPU pkt from dvd menu subtitle stream
  */
-void DVDRingBufferPriv::GetMenuSPUPkt(uint8_t *buf, int buf_size, int stream_id)
+void DVDRingBuffer::GetMenuSPUPkt(uint8_t *buf, int buf_size, int stream_id)
 {
     if (buf_size < 4)
         return;
@@ -936,7 +1040,7 @@
 /** \brief returns dvd menu button information if available.
  * used by NVP::DisplayDVDButton
  */
-AVSubtitle *DVDRingBufferPriv::GetMenuSubtitle(uint &version)
+AVSubtitle *DVDRingBuffer::GetMenuSubtitle(uint &version)
 {
     // this is unlocked by ReleaseMenuButton
     m_menuBtnLock.lock();
@@ -953,14 +1057,14 @@
 }
 
 
-void DVDRingBufferPriv::ReleaseMenuButton(void)
+void DVDRingBuffer::ReleaseMenuButton(void)
 {
     m_menuBtnLock.unlock();
 }
 
 /** \brief get coordinates of highlighted button
  */
-QRect DVDRingBufferPriv::GetButtonCoords(void)
+QRect DVDRingBuffer::GetButtonCoords(void)
 {
     QRect rect(0,0,0,0);
     if (!m_buttonExists)
@@ -982,7 +1086,7 @@
 /** \brief generate dvd subtitle bitmap or dvd menu bitmap.
  * code obtained from ffmpeg project
  */
-bool DVDRingBufferPriv::DecodeSubtitles(AVSubtitle *sub, int *gotSubtitles,
+bool DVDRingBuffer::DecodeSubtitles(AVSubtitle *sub, int *gotSubtitles,
                                     const uint8_t *spu_pkt, int buf_size)
 {
     #define GETBE16(p) (((p)[0] << 8) | (p)[1])
@@ -1152,7 +1256,7 @@
 /** \brief update the dvd menu button parameters
  * when a user changes the dvd menu button position
  */
-bool DVDRingBufferPriv::DVDButtonUpdate(bool b_mode)
+bool DVDRingBuffer::DVDButtonUpdate(bool b_mode)
 {
     if (!m_parent)
         return false;
@@ -1189,7 +1293,7 @@
 
 /** \brief clears the dvd menu button structures
  */
-void DVDRingBufferPriv::ClearMenuButton(void)
+void DVDRingBuffer::ClearMenuButton(void)
 {
     if (m_buttonExists || m_dvdMenuButton.rects)
     {
@@ -1210,7 +1314,7 @@
 /** \brief clears the menu SPU pkt and parameters.
  * necessary action during dvd menu changes
  */
-void DVDRingBufferPriv::ClearMenuSPUParameters(void)
+void DVDRingBuffer::ClearMenuSPUParameters(void)
 {
     if (m_menuBuflength == 0)
         return;
@@ -1224,7 +1328,7 @@
     m_hl_button.setRect(0, 0, 0, 0);
 }
 
-int DVDRingBufferPriv::NumMenuButtons(void) const
+int DVDRingBuffer::NumMenuButtons(void) const
 {
     pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
     int numButtons = pci->hli.hl_gi.btn_ns;
@@ -1236,7 +1340,7 @@
 
 /** \brief get the audio language from the dvd
  */
-uint DVDRingBufferPriv::GetAudioLanguage(int id)
+uint DVDRingBuffer::GetAudioLanguage(int id)
 {
     uint16_t lang = dvdnav_audio_stream_to_lang(m_dvdnav, id);
     VERBOSE(VB_PLAYBACK, LOC + QString("StreamID: %1; lang: %2").arg(id).arg(lang));
@@ -1246,14 +1350,14 @@
 /** \brief get real dvd track audio number
   * \param key stream_id
 */
-int DVDRingBufferPriv::GetAudioTrackNum(uint stream_id)
+int DVDRingBuffer::GetAudioTrackNum(uint stream_id)
 {
     return dvdnav_get_audio_logical_stream(m_dvdnav, stream_id);
 }
 
 /** \brief get the subtitle language from the dvd
  */
-uint DVDRingBufferPriv::GetSubtitleLanguage(int id)
+uint DVDRingBuffer::GetSubtitleLanguage(int id)
 {
     uint16_t lang = dvdnav_spu_stream_to_lang(m_dvdnav, id);
     VERBOSE(VB_PLAYBACK, LOC + QString("StreamID: %1; lang: %2").arg(id).arg(lang));
@@ -1262,7 +1366,7 @@
 
 /** \brief converts the subtitle/audio lang code to iso639.
  */
-uint DVDRingBufferPriv::ConvertLangCode(uint16_t code)
+uint DVDRingBuffer::ConvertLangCode(uint16_t code)
 {
     if (code == 0)
         return 0;
@@ -1282,7 +1386,7 @@
 /** \brief determines the default dvd menu button to
  * show when you initially access the dvd menu.
  */
-void DVDRingBufferPriv::SelectDefaultButton(void)
+void DVDRingBuffer::SelectDefaultButton(void)
 {
     pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
     int32_t button = pci->hli.hl_gi.fosl_btnn;
@@ -1302,7 +1406,7 @@
  *  \param type    currently kTrackTypeSubtitle or kTrackTypeAudio
  *  \param trackNo if -1 then autoselect the track num from the dvd IFO
  */
-void DVDRingBufferPriv::SetTrack(uint type, int trackNo)
+void DVDRingBuffer::SetTrack(uint type, int trackNo)
 {
     if (type == kTrackTypeSubtitle)
     {
@@ -1320,11 +1424,11 @@
 }
 
 /** \brief get the track the dvd should be playing.
- * can either be set by the user using DVDRingBufferPriv::SetTrack
+ * can either be set by the user using DVDRingBuffer::SetTrack
  * or determined from the dvd IFO.
  * \param type: use either kTrackTypeSubtitle or kTrackTypeAudio
  */
-int DVDRingBufferPriv::GetTrack(uint type)
+int DVDRingBuffer::GetTrack(uint type)
 {
     if (type == kTrackTypeSubtitle)
         return m_curSubtitleTrack;
@@ -1334,7 +1438,7 @@
     return 0;
 }
 
-uint8_t DVDRingBufferPriv::GetNumAudioChannels(int id)
+uint8_t DVDRingBuffer::GetNumAudioChannels(int id)
 {
     unsigned char channels = dvdnav_audio_stream_channels(m_dvdnav, id);
     if (channels == 0xff)
@@ -1344,7 +1448,7 @@
 
 /** \brief Get the dvd title and serial num
  */
-bool DVDRingBufferPriv::GetNameAndSerialNum(QString& _name, QString& _serial)
+bool DVDRingBuffer::GetNameAndSerialNum(QString& _name, QString& _serial)
 {
     _name    = QString(m_dvdname);
     _serial    = QString(m_serialnumber);
@@ -1358,7 +1462,7 @@
  * FPS for a dvd is determined by AFD::normalized_fps
  * * dvdnav_get_video_format: 0 - NTSC, 1 - PAL
  */
-double DVDRingBufferPriv::GetFrameRate(void)
+double DVDRingBuffer::GetFrameRate(void)
 {
     double dvdfps = 0;
     int format = dvdnav_get_video_format(m_dvdnav);
@@ -1371,7 +1475,7 @@
 /** \brief set dvd speed. uses the DVDDriveSpeed Setting from the settings
  *  table
  */
-void DVDRingBufferPriv::SetDVDSpeed(void)
+void DVDRingBuffer::SetDVDSpeed(void)
 {
     QMutexLocker lock(&m_seekLock);
     int dvdDriveSpeed = gCoreContext->GetNumSetting("DVDDriveSpeed", 12);
@@ -1380,22 +1484,22 @@
 
 /** \brief set dvd speed.
  */
-void DVDRingBufferPriv::SetDVDSpeed(int speed)
+void DVDRingBuffer::SetDVDSpeed(int speed)
 {
-    if (m_dvdFilename.startsWith("/"))
-        MediaMonitor::SetCDSpeed(m_dvdFilename.toLocal8Bit().constData(), speed);
+    if (filename.startsWith("/"))
+        MediaMonitor::SetCDSpeed(filename.toLocal8Bit().constData(), speed);
 }
 
 /**\brief returns seconds left in the title
  */
-uint DVDRingBufferPriv::TitleTimeLeft(void)
+uint DVDRingBuffer::TitleTimeLeft(void)
 {
     return (GetTotalTimeOfTitle() - GetCurrentTime());
 }
 
 /** \brief converts palette values from YUV to RGB
  */
-void DVDRingBufferPriv::guess_palette(uint32_t *rgba_palette,uint8_t *palette,
+void DVDRingBuffer::guess_palette(uint32_t *rgba_palette,uint8_t *palette,
                                         uint8_t *alpha)
 {
     int i,r,g,b,y,cr,cb;
@@ -1425,7 +1529,7 @@
 /** \brief decodes the bitmap from the subtitle packet.
  *         copied from ffmpeg's dvdsubdec.c.
  */
-int DVDRingBufferPriv::decode_rle(uint8_t *bitmap, int linesize, int w, int h,
+int DVDRingBuffer::decode_rle(uint8_t *bitmap, int linesize, int w, int h,
                                   const uint8_t *buf, int nibble_offset, int buf_size)
 {
     unsigned int v;
@@ -1472,7 +1576,7 @@
 
 /** copied from ffmpeg's dvdsubdec.c
  */
-int DVDRingBufferPriv::get_nibble(const uint8_t *buf, int nibble_offset)
+int DVDRingBuffer::get_nibble(const uint8_t *buf, int nibble_offset)
 {
     return (buf[nibble_offset >> 1] >> ((1 - (nibble_offset & 1)) << 2)) & 0xf;
 }
@@ -1481,7 +1585,7 @@
  * \brief obtained from ffmpeg dvdsubdec.c
  * used to find smallest bounded rectangle
  */
-int DVDRingBufferPriv::is_transp(const uint8_t *buf, int pitch, int n,
+int DVDRingBuffer::is_transp(const uint8_t *buf, int pitch, int n,
                      const uint8_t *transp_color)
 {
     int i;
@@ -1499,7 +1603,7 @@
  * used to find smallest bounded rect.
  * helps prevent jerky picture during subtitle creation
  */
-int DVDRingBufferPriv::find_smallest_bounding_rectangle(AVSubtitle *s)
+int DVDRingBuffer::find_smallest_bounding_rectangle(AVSubtitle *s)
 {
     uint8_t transp_color[256];
     int y1, y2, x1, x2, y, w, h, i;
@@ -1579,7 +1683,7 @@
     return 1;
 }
 
-bool DVDRingBufferPriv::SwitchAngle(uint angle)
+bool DVDRingBuffer::SwitchAngle(uint angle)
 {
     if (!m_dvdnav)
         return false;
@@ -1595,7 +1699,7 @@
     return false;
 }
 
-bool DVDRingBufferPriv::NewSequence(bool new_sequence)
+bool DVDRingBuffer::NewSequence(bool new_sequence)
 {
     bool result = false;
     if (new_sequence)
Index: libs/libmythtv/libmythtv.pro
===================================================================
--- libs/libmythtv/libmythtv.pro	(revision 27380)
+++ libs/libmythtv/libmythtv.pro	(working copy)
@@ -141,8 +141,6 @@
 
 # Misc. needed by backend/frontend
 HEADERS += recordinginfo.h
-HEADERS += RingBuffer.h             avfringbuffer.h
-HEADERS += ThreadedFileWriter.h
 HEADERS += dbcheck.h
 HEADERS += tvremoteutil.h           tv.h
 HEADERS += jobqueue.h
@@ -163,10 +161,11 @@
 HEADERS += channelgroup.h           channelgroupsettings.h
 HEADERS += recordingrule.h          programdetail.h
 HEADERS += mythsystemevent.h
+HEADERS += avfringbuffer.h          ThreadedFileWriter.h
+HEADERS += RingBuffer.h             FileRingBuffer.h
+HEADERS += DVDRingBuffer.h          BDRingBuffer.h 
 
 SOURCES += recordinginfo.cpp
-SOURCES += RingBuffer.cpp           avfringbuffer.cpp
-SOURCES += ThreadedFileWriter.cpp
 SOURCES += dbcheck.cpp
 SOURCES += tvremoteutil.cpp         tv.cpp
 SOURCES += jobqueue.cpp
@@ -187,6 +186,9 @@
 SOURCES += myth_imgconvert.cpp
 SOURCES += recordingrule.cpp        programdetail.cpp
 SOURCES += mythsystemevent.cpp
+SOURCES += avfringbuffer.cpp        ThreadedFileWriter.cpp
+SOURCES += RingBuffer.cpp           FileRingBuffer.cpp
+SOURCES += DVDRingBuffer.cpp        BDRingBuffer.cpp
 
 # DiSEqC
 HEADERS += diseqc.h                 diseqcsettings.h
@@ -251,17 +253,17 @@
     # Video playback
     HEADERS += tv_play.h                mythplayer.h
     HEADERS += mythdvdplayer.h          audioplayer.h
-    HEADERS += DVDRingBuffer.h          playercontext.h
+    HEADERS += playercontext.h
     HEADERS += tv_play_win.h            deletemap.h
     HEADERS += mythcommflagplayer.h     commbreakmap.h
-    HEADERS += BDRingBuffer.h           mythbdplayer.h
+    HEADERS += mythbdplayer.h
     HEADERS += mythiowrapper.h          tvbrowsehelper.h
     SOURCES += tv_play.cpp              mythplayer.cpp
     SOURCES += mythdvdplayer.cpp        audioplayer.cpp
-    SOURCES += DVDRingBuffer.cpp        playercontext.cpp
+    SOURCES += playercontext.cpp
     SOURCES += tv_play_win.cpp          deletemap.cpp
     SOURCES += mythcommflagplayer.cpp   commbreakmap.cpp
-    SOURCES += BDRingBuffer.cpp         mythbdplayer.cpp
+    SOURCES += mythbdplayer.cpp
     SOURCES += mythiowrapper.cpp        tvbrowsehelper.cpp
 
     # Text subtitle parser
Index: libs/libmythtv/tv_play.h
===================================================================
--- libs/libmythtv/tv_play.h	(revision 27380)
+++ libs/libmythtv/tv_play.h	(working copy)
@@ -617,10 +617,7 @@
     // DVD methods
     void DVDJumpBack(PlayerContext*);
     void DVDJumpForward(PlayerContext*);
-    bool DiscMenuHandleAction(PlayerContext*,
-                             const QStringList &actions,
-                             bool isDVD, bool isDVDStill,
-                             bool isBD = false);
+    bool DiscMenuHandleAction(PlayerContext*, const QStringList &actions);
 
     // Program jumping stuff
     void SetLastProgram(const ProgramInfo *rcinfo);
Index: libs/libmythtv/BDRingBuffer.cpp
===================================================================
--- libs/libmythtv/BDRingBuffer.cpp	(revision 27380)
+++ libs/libmythtv/BDRingBuffer.cpp	(working copy)
@@ -15,27 +15,31 @@
 #include "mythdirs.h"
 #include "bluray.h"
 
-#define LOC     QString("BDRingBuffer: ")
+#define LOC      QString("BDRingBuf(%1): ").arg(GetFilename())
+#define LOC_WARN QString("BDRingBuf(%1) Warning: ").arg(GetFilename())
+#define LOC_ERR  QString("BDRingBuf(%1) Error: ").arg(GetFilename())
 
-static void HandleOverlayCallback(void *data, const bd_overlay_s * const overlay)
+static void HandleOverlayCallback(
+    void *data, const bd_overlay_s * const overlay)
 {
-    BDRingBufferPriv *bdpriv = (BDRingBufferPriv*) data;
+    BDRingBuffer *bdrb = (BDRingBuffer*) data;
 
-    if (!bdpriv)
+    if (!bdrb)
         return;
 
     if (!overlay || overlay->plane == 1)
-        bdpriv->m_inMenu = false;
+        bdrb->m_inMenu = false;
 
     if (!overlay || !overlay->img)
         return;
 
-    bdpriv->m_inMenu = true;
+    bdrb->m_inMenu = true;
 
     const BD_PG_RLE_ELEM *rlep = overlay->img;
     uint8_t *yuvimg = (uint8_t*)malloc(overlay->w * overlay->h);
     unsigned pixels = overlay->w * overlay->h;
 
+
     for (unsigned i = 0; i < pixels; i += rlep->len, rlep++)
     {
         memset(yuvimg + i, rlep->color, rlep->len);
@@ -49,27 +53,30 @@
         palette.push_back(origpalette[i]);
 
     qoverlay.setColorTable(palette);
-    qoverlay.save(QString("bluray.menuimg.%1.%2.png").arg(overlay->w).arg(overlay->h));
+    qoverlay.save(QString("bluray.menuimg.%1.%2.png")
+                  .arg(overlay->w).arg(overlay->h));
 
-    VERBOSE(VB_PLAYBACK|VB_EXTRA, LOC + QString("In Menu Callback, ready to draw "
-                        "an overlay of %1x%2 at %3,%4 (%5 pixels).")
-                        .arg(overlay->w).arg(overlay->h).arg(overlay->x)
-                        .arg(overlay->y).arg(pixels));
+    VERBOSE(VB_PLAYBACK|VB_EXTRA,
+            QString("BDRingBuf(%1): ").arg(bdrb->GetFilename()) +
+            QString("In Menu Callback, ready to draw "
+                    "an overlay of %1x%2 at %3,%4 (%5 pixels).")
+            .arg(overlay->w).arg(overlay->h).arg(overlay->x)
+            .arg(overlay->y).arg(pixels));
 }
 
-BDRingBufferPriv::BDRingBufferPriv()
-    : bdnav(NULL),
-      m_is_hdmv_navigation(false),
-      m_numTitles(0), m_titleChanged(false)
+BDRingBuffer::BDRingBuffer(const QString &lfilename) :
+    bdnav(NULL),
+    m_is_hdmv_navigation(false), m_numTitles(0), m_titleChanged(false)
 {
+    OpenFile(lfilename);
 }
 
-BDRingBufferPriv::~BDRingBufferPriv()
+BDRingBuffer::~BDRingBuffer()
 {
     close();
 }
 
-void BDRingBufferPriv::close(void)
+void BDRingBuffer::close(void)
 {
     if (bdnav)
     {
@@ -80,8 +87,85 @@
     }
 }
 
-uint64_t BDRingBufferPriv::Seek(uint64_t pos)
+long long BDRingBuffer::Seek(long long pos, int whence, bool has_lock)
 {
+    VERBOSE(VB_FILE, LOC + QString("Seek(%1,%2,%3)")
+            .arg(pos).arg((SEEK_SET==whence)?"SEEK_SET":
+                          ((SEEK_CUR==whence)?"SEEK_CUR":"SEEK_END"))
+            .arg(has_lock?"locked":"unlocked"));
+
+    long long ret = -1;
+
+    // lockForWrite takes priority over lockForRead, so this will
+    // take priority over the lockForRead in the read ahead thread.
+    if (!has_lock)
+        rwlock.lockForWrite();
+
+    poslock.lockForWrite();
+
+    // Optimize no-op seeks
+    if (readaheadrunning &&
+        ((whence == SEEK_SET && pos == readpos) ||
+         (whence == SEEK_CUR && pos == 0)))
+    {
+        ret = readpos;
+
+        poslock.unlock();
+        if (!has_lock)
+            rwlock.unlock();
+
+        return ret;
+    }
+
+    // only valid for SEEK_SET & SEEK_CUR
+    long long new_pos = (SEEK_SET==whence) ? pos : readpos + pos;
+
+    // Here we perform a normal seek. When successful we
+    // need to call ResetReadAhead(). A reset means we will
+    // need to refill the buffer, which takes some time.
+    if ((SEEK_END == whence) ||
+        ((SEEK_CUR == whence) && new_pos != 0))
+    {
+        errno = EINVAL;
+        ret = -1;
+    }
+    else
+    {
+        Seek(new_pos);
+        ret = new_pos;
+    }
+
+    if (ret >= 0)
+    {
+        readpos = ret;
+        
+        ignorereadpos = -1;
+
+        if (readaheadrunning)
+            ResetReadAhead(readpos);
+
+        readAdjust = 0;
+    }
+    else
+    {
+        QString cmd = QString("Seek(%1, %2)").arg(pos)
+            .arg((SEEK_SET == whence) ? "SEEK_SET" :
+                 ((SEEK_CUR == whence) ?"SEEK_CUR" : "SEEK_END"));
+        VERBOSE(VB_IMPORTANT, LOC_ERR + cmd + " Failed" + ENO);
+    }
+
+    poslock.unlock();
+
+    generalWait.wakeAll();
+
+    if (!has_lock)
+        rwlock.unlock();
+
+    return ret;
+}
+
+uint64_t BDRingBuffer::Seek(uint64_t pos)
+{
     VERBOSE(VB_PLAYBACK|VB_EXTRA, LOC + QString("Seeking to %1.")
                 .arg(pos));
 
@@ -90,65 +174,86 @@
     return GetReadPosition();
 }
 
-void BDRingBufferPriv::GetDescForPos(QString &desc) const
+void BDRingBuffer::GetDescForPos(QString &desc) const
 {
     desc = QObject::tr("Title %1 chapter %2")
                        .arg(m_currentTitleInfo->idx)
                        .arg(m_currentTitleInfo->chapters->idx);
 }
 
-bool BDRingBufferPriv::OpenFile(const QString &filename)
+bool BDRingBuffer::OpenFile(const QString &lfilename, uint retry_ms)
 {
     VERBOSE(VB_IMPORTANT, LOC + QString("Opened BDRingBuffer device at %1")
-            .arg(filename.toLatin1().data()));
+            .arg(lfilename.toLatin1().data()));
 
+    rwlock.lockForWrite();
+
+    if (bdnav)
+    {
+        if (m_currentTitleInfo)
+            bd_free_title_info(m_currentTitleInfo);
+        bd_close(bdnav);
+        bdnav = NULL;
+    }
+
+    filename = lfilename;
+
     QString keyfile = QString("%1/KEYDB.cfg").arg(GetConfDir());
     QByteArray keyarray = keyfile.toAscii();
     const char *keyfilepath = keyarray.data();
 
-    bdnav = bd_open(filename.toLatin1().data(), keyfilepath);
+    bdnav = bd_open(lfilename.toLatin1().data(), keyfilepath);
 
     if (!bdnav)
+    {
+        rwlock.unlock();
         return false;
+    }
 
     // Check disc to see encryption status, menu and navigation types.
     const BLURAY_DISC_INFO *discinfo = bd_get_disc_info(bdnav);
     if (discinfo)
-        VERBOSE(VB_PLAYBACK, QString("*** Blu-ray Disc Information ***\n"
-                                  "First Play Supported: %1\n"
-                                  "Top Menu Supported: %2\n"
-                                  "Number of HDMV Titles: %3\n"
-                                  "Number of BD-J Titles: %4\n"
-                                  "Number of Unsupported Titles: %5\n"
-                                  "AACS present on disc: %6\n"
-                                  "libaacs used: %7\n"
-                                  "AACS handled: %8\n"
-                                  "BD+ present on disc: %9\n"
-                                  "libbdplus used: %10\n"
-                                  "BD+ handled: %11")
-                                  .arg(discinfo->first_play_supported ? "yes" : "no")
-                                  .arg(discinfo->top_menu_supported ? "yes" : "no")
-                                  .arg(discinfo->num_hdmv_titles)
-                                  .arg(discinfo->num_bdj_titles)
-                                  .arg(discinfo->num_unsupported_titles)
-                                  .arg(discinfo->aacs_detected ? "yes" : "no")
-                                  .arg(discinfo->libaacs_detected ? "yes" : "no")
-                                  .arg(discinfo->aacs_handled ? "yes" : "no")
-                                  .arg(discinfo->bdplus_detected ? "yes" : "no")
-                                  .arg(discinfo->libbdplus_detected ? "yes" : "no")
-                                  .arg(discinfo->bdplus_handled ? "yes" : "no"));
+    {
+        VERBOSE(VB_PLAYBACK, QString(
+                    "*** Blu-ray Disc Information ***\n"
+                    "First Play Supported: %1\n"
+                    "Top Menu Supported: %2\n"
+                    "Number of HDMV Titles: %3\n"
+                    "Number of BD-J Titles: %4\n"
+                    "Number of Unsupported Titles: %5\n"
+                    "AACS present on disc: %6\n"
+                    "libaacs used: %7\n"
+                    "AACS handled: %8\n"
+                    "BD+ present on disc: %9\n"
+                    "libbdplus used: %10\n"
+                    "BD+ handled: %11")
+                .arg(discinfo->first_play_supported ? "yes" : "no")
+                .arg(discinfo->top_menu_supported ? "yes" : "no")
+                .arg(discinfo->num_hdmv_titles)
+                .arg(discinfo->num_bdj_titles)
+                .arg(discinfo->num_unsupported_titles)
+                .arg(discinfo->aacs_detected ? "yes" : "no")
+                .arg(discinfo->libaacs_detected ? "yes" : "no")
+                .arg(discinfo->aacs_handled ? "yes" : "no")
+                .arg(discinfo->bdplus_detected ? "yes" : "no")
+                .arg(discinfo->libbdplus_detected ? "yes" : "no")
+                .arg(discinfo->bdplus_handled ? "yes" : "no"));
+    }
 
-    // The following settings affect HDMV navigation (default audio track selection,
+    // The following settings affect HDMV navigation
+    // (default audio track selection,
     // parental controls, menu language, etc.  They are not yet used.
 
     // Set parental level "age" to 99 for now.  TODO: Add support for FE level
     bd_set_player_setting(bdnav, BLURAY_PLAYER_SETTING_PARENTAL, 99);
 
     // Set preferred language to FE guide language
-    const char *langpref = gCoreContext->GetSetting("ISO639Language0", "eng").toLatin1().data();
+    const char *langpref = gCoreContext->GetSetting(
+        "ISO639Language0", "eng").toLatin1().data();
     QString QScountry  = gCoreContext->GetLocale()->GetCountryCode().toLower();
     const char *country = QScountry.toLatin1().data();
-    bd_set_player_setting_str(bdnav, BLURAY_PLAYER_SETTING_AUDIO_LANG, langpref);
+    bd_set_player_setting_str(
+        bdnav, BLURAY_PLAYER_SETTING_AUDIO_LANG, langpref);
 
     // Set preferred presentation graphics language to the FE guide language
     bd_set_player_setting_str(bdnav, BLURAY_PLAYER_SETTING_PG_LANG, langpref);
@@ -157,7 +262,8 @@
     bd_set_player_setting_str(bdnav, BLURAY_PLAYER_SETTING_MENU_LANG, langpref);
 
     // Set player country code via MythLocale. (not a region setting)
-    bd_set_player_setting_str(bdnav, BLURAY_PLAYER_SETTING_COUNTRY_CODE, country);
+    bd_set_player_setting_str(
+        bdnav, BLURAY_PLAYER_SETTING_COUNTRY_CODE, country);
 
     int regioncode = 0;
     regioncode = gCoreContext->GetNumSetting("BlurayRegionCode");
@@ -232,24 +338,34 @@
     }
 #endif
 
+    readblocksize   = BD_BLOCK_SIZE * 62;
+    setswitchtonext = false;
+    ateof           = false;
+    commserror      = false;
+    numfailures     = 0;
+    rawbitrate      = 8000;
+    CalcReadAheadThresh();
+
+    rwlock.unlock();
+
     return true;
 }
 
-uint64_t BDRingBufferPriv::GetReadPosition(void)
+long long BDRingBuffer::GetReadPosition(void) const
 {
     if (bdnav)
         return bd_tell(bdnav);
     return 0;
 }
 
-uint32_t BDRingBufferPriv::GetNumChapters(void)
+uint32_t BDRingBuffer::GetNumChapters(void)
 {
     if (m_currentTitleInfo)
         return m_currentTitleInfo->chapter_count;
     return 0;
 }
 
-uint64_t BDRingBufferPriv::GetChapterStartTime(uint32_t chapter)
+uint64_t BDRingBuffer::GetChapterStartTime(uint32_t chapter)
 {
     if (chapter < 0 || chapter >= GetNumChapters())
         return 0;
@@ -257,7 +373,7 @@
                                    90000.0f);
 }
 
-uint64_t BDRingBufferPriv::GetChapterStartFrame(uint32_t chapter)
+uint64_t BDRingBuffer::GetChapterStartFrame(uint32_t chapter)
 {
     if (chapter < 0 || chapter >= GetNumChapters())
         return 0;
@@ -265,14 +381,14 @@
                                     GetFrameRate()) / 90000.0f);
 }
 
-int BDRingBufferPriv::GetCurrentTitle(void) const
+int BDRingBuffer::GetCurrentTitle(void) const
 {
     if (m_currentTitleInfo)
         return m_currentTitleInfo->idx;
     return -1;
 }
 
-int BDRingBufferPriv::GetTitleDuration(int title) const
+int BDRingBuffer::GetTitleDuration(int title) const
 {
     int numTitles = GetNumTitles();
 
@@ -288,7 +404,7 @@
     return duration;
 }
 
-bool BDRingBufferPriv::SwitchTitle(uint title)
+bool BDRingBuffer::SwitchTitle(uint title)
 {
     if (!bdnav)
         return false;
@@ -341,14 +457,14 @@
     return true;
 }
 
-bool BDRingBufferPriv::TitleChanged(void)
+bool BDRingBuffer::TitleChanged(void)
 {
     bool ret = m_titleChanged;
     m_titleChanged = false;
     return ret;
 }
 
-bool BDRingBufferPriv::SwitchAngle(uint angle)
+bool BDRingBuffer::SwitchAngle(uint angle)
 {
     if (!bdnav)
         return false;
@@ -359,14 +475,14 @@
     return true;
 }
 
-uint64_t BDRingBufferPriv::GetTotalReadPosition(void)
+uint64_t BDRingBuffer::GetTotalReadPosition(void)
 {
     if (bdnav)
         return bd_get_title_size(bdnav);
     return 0;
 }
 
-int BDRingBufferPriv::safe_read(void *data, unsigned sz)
+int BDRingBuffer::safe_read(void *data, uint sz)
 {
     if (m_is_hdmv_navigation)
     {
@@ -391,7 +507,7 @@
     return sz;
 }
 
-double BDRingBufferPriv::GetFrameRate(void)
+double BDRingBuffer::GetFrameRate(void)
 {
     if (bdnav && m_currentTitleInfo)
     {
@@ -424,7 +540,7 @@
     return 0;
 }
 
-int BDRingBufferPriv::GetAudioLanguage(uint streamID)
+int BDRingBuffer::GetAudioLanguage(uint streamID)
 {
     if (!m_currentTitleInfo ||
         streamID >= m_currentTitleInfo->clips->audio_stream_count)
@@ -439,7 +555,7 @@
     return code;
 }
 
-int BDRingBufferPriv::GetSubtitleLanguage(uint streamID)
+int BDRingBuffer::GetSubtitleLanguage(uint streamID)
 {
     if (!m_currentTitleInfo)
         return iso639_str3_to_key("und");
@@ -465,7 +581,7 @@
     return iso639_str3_to_key("und");
 }
 
-void BDRingBufferPriv::PressButton(int32_t key, int64_t pts)
+void BDRingBuffer::PressButton(int32_t key, int64_t pts)
 {
     if (!bdnav)
         return;
@@ -486,7 +602,7 @@
 
 /** \brief jump to a Blu-ray root or popup menu
  */
-bool BDRingBufferPriv::GoToMenu(const QString str)
+bool BDRingBuffer::GoToMenu(const QString str)
 {
     if (!m_is_hdmv_navigation)
         return false;
@@ -512,7 +628,7 @@
     return false;
 }
 
-bool BDRingBufferPriv::HandleBDEvents()
+bool BDRingBuffer::HandleBDEvents(void)
 {
     BD_EVENT ev;
     while (bd_get_event(bdnav, &ev))
@@ -527,7 +643,7 @@
     return true;
 }
 
-void BDRingBufferPriv::HandleBDEvent(BD_EVENT &ev)
+void BDRingBuffer::HandleBDEvent(BD_EVENT &ev)
 {
     switch (ev.event) {
         case BD_EVENT_NONE:
Index: libs/libmythtv/FileRingBuffer.cpp
===================================================================
--- libs/libmythtv/FileRingBuffer.cpp	(revision 0)
+++ libs/libmythtv/FileRingBuffer.cpp	(revision 0)
@@ -0,0 +1,781 @@
+#include <cstdlib>
+#include <cerrno>
+
+// POSIX C headers
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <QFileInfo>
+#include <QDir>
+
+#include "ThreadedFileWriter.h"
+#include "FileRingBuffer.h"
+#include "mythcontext.h"
+#include "remotefile.h"
+#include "util.h"
+
+#ifndef O_STREAMING
+#define O_STREAMING 0
+#endif
+
+#ifndef O_LARGEFILE
+#define O_LARGEFILE 0
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#define LOC      QString("FileRingBuf(%1): ").arg(filename)
+#define LOC_WARN QString("FileRingBuf(%1) Warning: ").arg(filename)
+#define LOC_ERR  QString("FileRingBuf(%1) Error: ").arg(filename)
+
+FileRingBuffer::FileRingBuffer(const QString &lfilename,
+                               bool write, bool readahead, int timeout_ms)
+{
+    startreadahead = readahead;
+
+    if (write)
+    {
+        filename = lfilename;
+        if (filename.startsWith("myth://"))
+        {
+            remotefile = new RemoteFile(filename, true);
+            if (!remotefile->isOpen())
+            {
+                VERBOSE(VB_IMPORTANT,
+                        QString("RingBuffer::RingBuffer(): Failed to open "
+                                "remote file (%1) for write").arg(filename));
+                delete remotefile;
+                remotefile = NULL;
+            }
+            else
+                writemode = true;
+        }
+        else
+        {
+            tfw = new ThreadedFileWriter(
+                filename, O_WRONLY|O_TRUNC|O_CREAT|O_LARGEFILE, 0644);
+
+            if (!tfw->Open())
+            {
+                delete tfw;
+                tfw = NULL;
+            }
+            else
+                writemode = true;
+        }
+    }
+    else if (timeout_ms >= 0)
+    {
+        OpenFile(filename, timeout_ms);
+    }
+}
+
+FileRingBuffer::~FileRingBuffer()
+{
+    rwlock.lockForWrite();
+
+    if (remotefile)
+    {
+        delete remotefile;
+        remotefile = NULL;
+    }
+
+    if (tfw)
+    {
+        delete tfw;
+        tfw = NULL;
+    }
+
+    if (fd2 >= 0)
+    {
+        close(fd2);
+        fd2 = -1;
+    }
+
+    rwlock.unlock();
+}
+
+/** \fn check_permissions(const QString&)
+ *  \brief Returns false iff file exists and has incorrect permissions.
+ *  \param filename File (including path) that we want to know about
+ */
+static bool check_permissions(const QString &filename)
+{
+    QFileInfo fileInfo(filename);
+    if (fileInfo.exists() && !fileInfo.isReadable())
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR +
+                "File exists but is not readable by MythTV!");
+        return false;
+    }
+    return true;
+}
+
+static bool is_subtitle_possible(const QString &extension)
+{
+    QMutexLocker locker(&RingBuffer::subExtLock);
+    bool no_subtitle = false;
+    for (uint i = 0; i < (uint)RingBuffer::subExtNoCheck.size(); i++)
+    {
+        if (extension.contains(RingBuffer::subExtNoCheck[i].right(3)))
+        {
+            no_subtitle = true;
+            break;
+        }
+    }
+    return !no_subtitle;
+}
+
+static QString local_sub_filename(QFileInfo &fileInfo)
+{
+    // Subtitle handling
+    QString vidFileName = fileInfo.fileName();
+    QString dirName = fileInfo.absolutePath();
+
+    QString baseName = vidFileName;
+    int suffixPos = vidFileName.lastIndexOf(QChar('.'));
+    if (suffixPos > 0)
+        baseName = vidFileName.left(suffixPos);
+
+    QStringList el;
+    {
+        // The dir listing does not work if the filename has the
+        // following chars "[]()" so we convert them to the wildcard '?'
+        const QString findBaseName = baseName
+            .replace("[", "?")
+            .replace("]", "?")
+            .replace("(", "?")
+            .replace(")", "?");
+
+        QMutexLocker locker(&RingBuffer::subExtLock);
+        QStringList::const_iterator eit = RingBuffer::subExt.begin();
+        for (; eit != RingBuffer::subExt.end(); eit++)
+            el += findBaseName + *eit;
+    }
+
+    // Some Qt versions do not accept paths in the search string of
+    // entryList() so we have to set the dir first
+    QDir dir;
+    dir.setPath(dirName);
+
+    const QStringList candidates = dir.entryList(el);
+
+    QStringList::const_iterator cit = candidates.begin();
+    for (; cit != candidates.end(); ++cit)
+    {
+        QFileInfo fi(dirName + "/" + *cit);
+        if (fi.exists() && (fi.size() >= RingBuffer::kReadTestSize))
+            return fi.absoluteFilePath();
+    }
+
+    return QString::null;
+}
+
+bool FileRingBuffer::OpenFile(const QString &lfilename, uint retry_ms)
+{
+    VERBOSE(VB_PLAYBACK, LOC + QString("OpenFile(%1, %2 ms)")
+            .arg(lfilename).arg(retry_ms));
+
+    rwlock.lockForWrite();
+
+    filename = lfilename;
+
+    if (remotefile)
+    {
+        delete remotefile;
+        remotefile = NULL;
+    }
+
+    if (fd2 >= 0)
+    {
+        close(fd2);
+        fd2 = -1;
+    }
+
+    bool is_local = 
+        (filename.left(4) != "/dev") &&
+        ((filename.left(1) == "/") || QFile::exists(filename));
+
+    if (is_local)
+    {
+        char buf[kReadTestSize];
+        int lasterror = 0;
+
+        MythTimer openTimer;
+        openTimer.start();
+
+        uint openAttempts = 0;
+        do
+        {
+            openAttempts++;
+            lasterror = 0;
+
+            fd2 = open(filename.toLocal8Bit().constData(),
+                       O_RDONLY|O_LARGEFILE|O_STREAMING|O_BINARY);
+
+            if (fd2 < 0)
+            {
+                if (!check_permissions(filename))
+                {
+                    lasterror = 3;
+                    break;
+                }
+
+                lasterror = 1;
+                usleep(10 * 1000);
+            }
+            else
+            {
+                int ret = read(fd2, buf, kReadTestSize);
+                if (ret != (int)kReadTestSize)
+                {
+                    lasterror = 2;
+                    close(fd2);
+                    fd2 = -1;
+                    if (oldfile)
+                        break; // if it's an old file it won't grow..
+                    usleep(10 * 1000);
+                }
+                else
+                {
+                    if (0 == lseek(fd2, 0, SEEK_SET))
+                    {
+                        posix_fadvise(fd2, 0, 0, POSIX_FADV_SEQUENTIAL);
+                        posix_fadvise(fd2, 0, 1*1024*1024, POSIX_FADV_WILLNEED);
+                        lasterror = 0;
+                        break;
+                    }
+                    lasterror = 4;
+                    close(fd2);
+                    fd2 = -1;
+                }
+            }
+        }
+        while ((uint)openTimer.elapsed() < retry_ms);
+
+        switch (lasterror)
+        {
+            case 0:
+            {
+                QFileInfo fi(filename);
+                oldfile = fi.lastModified()
+                    .secsTo(QDateTime::currentDateTime()) > 60;
+                QString extension = fi.completeSuffix().toLower();
+                if (is_subtitle_possible(extension))
+                    subtitlefilename = local_sub_filename(fi);
+                break;
+            }
+            case 1:
+                VERBOSE(VB_IMPORTANT, LOC_ERR +
+                        QString("OpenFile(): Could not open."));
+                break;
+            case 2:
+                VERBOSE(VB_IMPORTANT, LOC_ERR +
+                        QString("OpenFile(): File too small (%1B).")
+                        .arg(QFileInfo(filename).size()));
+                break;
+            case 3:
+                VERBOSE(VB_IMPORTANT, LOC_ERR +
+                        "OpenFile(): Improper permissions.");
+                break;
+            case 4:
+                VERBOSE(VB_IMPORTANT, LOC_ERR +
+                        "OpenFile(): Cannot seek in file.");
+                break;
+            default:
+                break;
+        }
+        VERBOSE(VB_FILE, LOC + QString("OpenFile() made %1 attempts in %2 ms")
+                .arg(openAttempts).arg(openTimer.elapsed()));
+
+    }
+    else
+    {
+        QString tmpSubName = filename;
+        QString dirName  = ".";
+
+        int dirPos = filename.lastIndexOf(QChar('/'));
+        if (dirPos > 0)
+        {
+            tmpSubName = filename.mid(dirPos + 1);
+            dirName = filename.left(dirPos);
+        }
+
+        QString baseName  = tmpSubName;
+        QString extension = tmpSubName;
+        QStringList auxFiles;
+
+        int suffixPos = tmpSubName.lastIndexOf(QChar('.'));
+        if (suffixPos > 0)
+        {
+            baseName = tmpSubName.left(suffixPos);
+            extension = tmpSubName.right(suffixPos-1);
+            if (is_subtitle_possible(extension))
+            {
+                QMutexLocker locker(&subExtLock);
+                QStringList::const_iterator eit = subExt.begin();
+                for (; eit != subExt.end(); eit++)
+                    auxFiles += baseName + *eit;
+            }
+        }
+
+        remotefile = new RemoteFile(filename, false, true,
+                                    retry_ms, &auxFiles);
+        if (!remotefile->isOpen())
+        {
+            VERBOSE(VB_IMPORTANT, LOC_ERR +
+                    QString("RingBuffer::RingBuffer(): Failed to open remote "
+                            "file (%1)").arg(filename));
+            delete remotefile;
+            remotefile = NULL;
+        }
+        else
+        {
+            QStringList aux = remotefile->GetAuxiliaryFiles();
+            if (aux.size())
+                subtitlefilename = dirName + "/" + aux[0];
+        }
+    }
+
+    setswitchtonext = false;
+    ateof = false;
+    commserror = false;
+    numfailures = 0;
+
+    rawbitrate = 8000;
+    CalcReadAheadThresh();
+
+    bool ok = fd2 >= 0 || remotefile;
+
+    rwlock.unlock();
+
+    return ok;
+}
+
+bool FileRingBuffer::IsOpen(void) const
+{
+    rwlock.lockForRead();
+    bool ret = tfw || (fd2 > -1) || remotefile;
+    rwlock.unlock();
+    return ret;
+}
+
+/** \fn FileRingBuffer::safe_read(int, void*, uint)
+ *  \brief Reads data from the file-descriptor.
+ *
+ *   This will re-read the file forever until the
+ *   end-of-file is reached or the buffer is full.
+ *
+ *  \param fd   File descriptor to read from
+ *  \param data Pointer to where data will be written
+ *  \param sz   Number of bytes to read
+ *  \return Returns number of bytes read
+ */
+int FileRingBuffer::safe_read(int fd, void *data, uint sz)
+{
+    int ret;
+    unsigned tot = 0;
+    unsigned errcnt = 0;
+    unsigned zerocnt = 0;
+
+    if (fd2 < 0)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR +
+                "Invalid file descriptor in 'safe_read()'");
+        return 0;
+    }
+
+    if (stopreads)
+        return 0;
+
+    while (tot < sz)
+    {
+        ret = read(fd2, (char *)data + tot, sz - tot);
+        if (ret < 0)
+        {
+            if (errno == EAGAIN)
+                continue;
+
+            VERBOSE(VB_IMPORTANT,
+                    LOC_ERR + "File I/O problem in 'safe_read()'" + ENO);
+
+            errcnt++;
+            numfailures++;
+            if (errcnt == 3)
+                break;
+        }
+        else if (ret > 0)
+        {
+            tot += ret;
+        }
+
+        if (oldfile)
+            break;
+
+        if (ret == 0) // EOF returns 0
+        {
+            if (tot > 0)
+                break;
+
+            zerocnt++;
+
+            // 0.36 second timeout for livetvchain with usleep(60000),
+            // or 2.4 seconds if it's a new file less than 30 minutes old.
+            if (zerocnt >= (livetvchain ? 6 : 40))
+            {
+                break;
+            }
+        }
+        if (stopreads)
+            break;
+        if (tot < sz)
+            usleep(60000);
+    }
+    return tot;
+}
+
+/** \fn FileRingBuffer::safe_read(RemoteFile*, void*, uint)
+ *  \brief Reads data from the RemoteFile.
+ *
+ *  \param rf   RemoteFile to read from
+ *  \param data Pointer to where data will be written
+ *  \param sz   Number of bytes to read
+ *  \return Returns number of bytes read
+ */
+int FileRingBuffer::safe_read(RemoteFile *rf, void *data, uint sz)
+{
+    int ret = rf->Read(data, sz);
+    if (ret < 0)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR +
+                "safe_read(RemoteFile* ...): read failed");
+            
+        poslock.lockForRead();
+        rf->Seek(internalreadpos - readAdjust, SEEK_SET);
+        poslock.unlock();
+        numfailures++;
+    }
+    else if (ret == 0)
+    {
+        VERBOSE(VB_FILE, LOC +
+                "safe_read(RemoteFile* ...): at EOF");
+    }
+
+    return ret;
+}
+
+long long FileRingBuffer::GetReadPosition(void) const
+{
+    poslock.lockForRead();
+    long long ret = readpos;
+    poslock.unlock();
+    return ret;
+}
+
+long long FileRingBuffer::GetRealFileSize(void) const
+{
+    rwlock.lockForRead();
+    long long ret = -1;
+    if (remotefile)
+        ret = remotefile->GetFileSize();
+    else
+        ret = QFileInfo(filename).size();
+    rwlock.unlock();
+    return ret;
+}
+
+long long FileRingBuffer::Seek(long long pos, int whence, bool has_lock)
+{
+    VERBOSE(VB_FILE, LOC + QString("Seek(%1,%2,%3)")
+            .arg(pos).arg((SEEK_SET==whence)?"SEEK_SET":
+                          ((SEEK_CUR==whence)?"SEEK_CUR":"SEEK_END"))
+            .arg(has_lock?"locked":"unlocked"));
+
+    long long ret = -1;
+
+    StopReads();
+
+    // lockForWrite takes priority over lockForRead, so this will
+    // take priority over the lockForRead in the read ahead thread.
+    if (!has_lock)
+        rwlock.lockForWrite();
+
+    StartReads();
+
+    if (writemode)
+    {
+        ret = WriterSeek(pos, whence, true);
+        if (!has_lock)
+            rwlock.unlock();
+        return ret;
+    }
+
+    poslock.lockForWrite();
+
+    // Optimize no-op seeks
+    if (readaheadrunning &&
+        ((whence == SEEK_SET && pos == readpos) ||
+         (whence == SEEK_CUR && pos == 0)))
+    {
+        ret = readpos;
+
+        poslock.unlock();
+        if (!has_lock)
+            rwlock.unlock();
+
+        return ret;
+    }
+
+    // only valid for SEEK_SET & SEEK_CUR
+    long long new_pos = (SEEK_SET==whence) ? pos : readpos + pos;
+
+#if 1
+    // Optimize short seeks where the data for
+    // them is in our ringbuffer already.
+    if (readaheadrunning &&
+        (SEEK_SET==whence || SEEK_CUR==whence))
+    {
+        rbrlock.lockForWrite();
+        rbwlock.lockForRead();
+        VERBOSE(VB_FILE, LOC +
+                QString("Seek(): rbrpos: %1 rbwpos: %2"
+                        "\n\t\t\treadpos: %3 internalreadpos: %4")
+                .arg(rbrpos).arg(rbwpos)
+                .arg(readpos).arg(internalreadpos));
+        bool used_opt = false;
+        if ((new_pos < readpos))
+        {
+            int min_safety = max(fill_min, readblocksize);
+            int free = ((rbwpos >= rbrpos) ?
+                        rbrpos + kBufferSize : rbrpos) - rbwpos;
+            int internal_backbuf =
+                (rbwpos >= rbrpos) ? rbrpos : rbrpos - rbwpos;
+            internal_backbuf = min(internal_backbuf, free - min_safety);
+            long long sba = readpos - new_pos;
+            VERBOSE(VB_FILE, LOC +
+                    QString("Seek(): internal_backbuf: %1 sba: %2")
+                    .arg(internal_backbuf).arg(sba));
+            if (internal_backbuf >= sba)
+            {
+                rbrpos = (rbrpos>=sba) ? rbrpos - sba :
+                    kBufferSize + rbrpos - sba;
+                used_opt = true;
+                VERBOSE(VB_FILE, LOC +
+                        QString("Seek(): OPT1 rbrpos: %1 rbwpos: %2"
+                                "\n\t\t\treadpos: %3 internalreadpos: %4")
+                        .arg(rbrpos).arg(rbwpos)
+                        .arg(new_pos).arg(internalreadpos));
+            }
+        }
+        else if ((new_pos >= readpos) && (new_pos <= internalreadpos))
+        {
+            rbrpos = (rbrpos + (new_pos - readpos)) % kBufferSize;
+            used_opt = true;
+            VERBOSE(VB_FILE, LOC +
+                    QString("Seek(): OPT2 rbrpos: %1 sba: %2")
+                    .arg(rbrpos).arg(readpos - new_pos));
+        }
+        rbwlock.unlock();
+        rbrlock.unlock();
+
+        if (used_opt)
+        {
+            if (ignorereadpos >= 0)
+            {
+                // seek should always succeed since we were at this position
+                int ret;
+                if (remotefile)
+                    ret = remotefile->Seek(internalreadpos, SEEK_SET);
+                else
+                {
+                    ret = lseek64(fd2, internalreadpos, SEEK_SET);
+                    posix_fadvise(fd2, 0,
+                                  internalreadpos, POSIX_FADV_DONTNEED);
+                    posix_fadvise(fd2, internalreadpos,
+                                  1*1024*1024, POSIX_FADV_WILLNEED);
+                }
+                VERBOSE(VB_FILE, LOC +
+                        QString("Seek to %1 from ignore pos %2 returned %3")
+                        .arg(internalreadpos).arg(ignorereadpos).arg(ret));
+                ignorereadpos = -1;
+            }
+            readpos = new_pos;
+            poslock.unlock();
+            generalWait.wakeAll();
+            ateof = false;
+            readsallowed = false;
+            if (!has_lock)
+                rwlock.unlock();
+            return new_pos;
+        }
+    }
+#endif
+
+#if 1
+    // This optimizes the seek end-250000, read, seek 0, read portion 
+    // of the pattern ffmpeg performs at the start of playback to
+    // determine the pts.
+    // If the seek is a SEEK_END or is a seek where the position
+    // changes over 100 MB we check the file size and if the
+    // destination point is within 300000 bytes of the end of
+    // the file we enter a special mode where the read ahead
+    // buffer stops reading data and all reads are made directly
+    // until another seek is performed. The point of all this is
+    // to avoid flushing out the buffer that still contains all
+    // the data the final seek 0, read will need just to read the
+    // last 250000 bytes. A further optimization would be to buffer
+    // the 250000 byte read, which is currently performed in 32KB
+    // blocks (inefficient with RemoteFile).
+    if ((remotefile || fd2 >= 0) && (ignorereadpos < 0))
+    {
+        long long off_end = 0xDEADBEEF;
+        if (SEEK_END == whence)
+        {
+            off_end = pos;
+            if (remotefile)
+            {
+                new_pos = remotefile->GetFileSize() - off_end;
+            }
+            else
+            {
+                QFileInfo fi(filename);
+                new_pos = fi.size() - off_end;
+            }
+        }
+        else
+        {
+            if (remotefile)
+            {
+                off_end = remotefile->GetFileSize() - new_pos;
+            }
+            else
+            {
+                QFileInfo fi(filename);
+                off_end = fi.size() - new_pos;
+            }
+        }
+
+        if (off_end != 0xDEADBEEF)
+        {
+            VERBOSE(VB_FILE, LOC +
+                    QString("Seek(): Offset from end: %1").arg(off_end));
+        }
+
+        if (off_end == 250000)
+        {
+            VERBOSE(VB_FILE, LOC +
+                    QString("Seek(): offset from end: %1").arg(off_end) +
+                    "\n\t\t\t -- ignoring read ahead thread until next seek.");
+
+            ignorereadpos = new_pos;
+            errno = EINVAL;
+            long long ret;
+            if (remotefile)
+                ret = remotefile->Seek(ignorereadpos, SEEK_SET);
+            else
+            {
+                ret = lseek64(fd2, ignorereadpos, SEEK_SET);
+                posix_fadvise(fd2, ignorereadpos, 250000, POSIX_FADV_WILLNEED);
+            }
+
+            if (ret < 0)
+            {
+                int tmp_eno = errno;
+                QString cmd = QString("Seek(%1, SEEK_SET) ign ")
+                    .arg(ignorereadpos);
+
+                ignorereadpos = -1;
+
+                VERBOSE(VB_IMPORTANT, LOC_ERR + cmd + " Failed" + ENO);
+
+                // try to return to former position..
+                if (remotefile)
+                    ret = remotefile->Seek(internalreadpos, SEEK_SET);
+                else
+                    ret = lseek64(fd2, internalreadpos, SEEK_SET);
+                if (ret < 0)
+                {
+                    QString cmd = QString("Seek(%1, SEEK_SET) int ")
+                        .arg(internalreadpos);
+                    VERBOSE(VB_IMPORTANT, LOC_ERR + cmd + " Failed" + ENO);
+                }
+                else
+                {
+                    QString cmd = QString("Seek(%1, %2) int ")
+                        .arg(internalreadpos)
+                        .arg((SEEK_SET == whence) ? "SEEK_SET" :
+                             ((SEEK_CUR == whence) ?"SEEK_CUR" : "SEEK_END"));
+                    VERBOSE(VB_IMPORTANT, LOC_ERR + cmd + " succeeded");
+                }
+                ret = -1;
+                errno = tmp_eno;
+            }
+            else
+            {
+                ateof = false;
+                readsallowed = false;
+            }
+
+            poslock.unlock();
+
+            generalWait.wakeAll();
+
+            if (!has_lock)
+                rwlock.unlock();
+
+            return ret;
+        }
+    }
+#endif
+
+    // Here we perform a normal seek. When successful we
+    // need to call ResetReadAhead(). A reset means we will
+    // need to refill the buffer, which takes some time.
+    if (remotefile)
+    {
+        ret = remotefile->Seek(pos, whence, readpos);
+        if (ret<0)
+            errno = EINVAL;
+    }
+    else
+    {
+        ret = lseek64(fd2, pos, whence);
+        if (ret >= 0)
+        {
+            posix_fadvise(fd2, 0,   ret,         POSIX_FADV_DONTNEED);
+            posix_fadvise(fd2, ret, 1*1024*1024, POSIX_FADV_WILLNEED);
+        }
+    }
+
+    if (ret >= 0)
+    {
+        readpos = ret;
+        
+        ignorereadpos = -1;
+
+        if (readaheadrunning)
+            ResetReadAhead(readpos);
+
+        readAdjust = 0;
+    }
+    else
+    {
+        QString cmd = QString("Seek(%1, %2)").arg(pos)
+            .arg((SEEK_SET == whence) ? "SEEK_SET" :
+                 ((SEEK_CUR == whence) ?"SEEK_CUR" : "SEEK_END"));
+        VERBOSE(VB_IMPORTANT, LOC_ERR + cmd + " Failed" + ENO);
+    }
+
+    poslock.unlock();
+
+    generalWait.wakeAll();
+
+    if (!has_lock)
+        rwlock.unlock();
+
+    return ret;
+}
Index: libs/libmythtv/mythdvdplayer.cpp
===================================================================
--- libs/libmythtv/mythdvdplayer.cpp	(revision 27380)
+++ libs/libmythtv/mythdvdplayer.cpp	(working copy)
@@ -11,7 +11,8 @@
 MythDVDPlayer::MythDVDPlayer(bool muted)
   : MythPlayer(muted), m_buttonVersion(0),
     dvd_stillframe_showing(false), need_change_dvd_track(0),
-    m_initial_title(-1), m_initial_audio_track(-1), m_initial_subtitle_track(-1),
+    m_initial_title(-1), m_initial_audio_track(-1),
+    m_initial_subtitle_track(-1),
     m_stillFrameLength(0)
 {
 }
@@ -25,19 +26,19 @@
                                           int64_t timecode, bool wrap)
 {
     MythPlayer::ReleaseNextVideoFrame(buffer, timecode,
-                        !player_ctx->buffer->InDiscMenuOrStillFrame());
+                        !player_ctx->buffer->IsInDiscMenuOrStillFrame());
 }
 
 void MythDVDPlayer::DisableCaptions(uint mode, bool osd_msg)
 {
-    if ((kDisplayAVSubtitle & mode) && player_ctx->buffer->isDVD())
+    if ((kDisplayAVSubtitle & mode) && player_ctx->buffer->IsDVD())
         player_ctx->buffer->DVD()->SetTrack(kTrackTypeSubtitle, -1);
     MythPlayer::DisableCaptions(mode, osd_msg);
 }
 
 void MythDVDPlayer::EnableCaptions(uint mode, bool osd_msg)
 {
-    if ((kDisplayAVSubtitle & mode) && player_ctx->buffer->isDVD())
+    if ((kDisplayAVSubtitle & mode) && player_ctx->buffer->IsDVD())
         player_ctx->buffer->DVD()->SetTrack(kTrackTypeSubtitle,
                                             GetTrack(kTrackTypeSubtitle));
     MythPlayer::EnableCaptions(mode, osd_msg);
@@ -45,9 +46,11 @@
 
 void MythDVDPlayer::DisplayPauseFrame(void)
 {
-    if (player_ctx->buffer->isDVD() &&
-        player_ctx->buffer->DVD()->InStillFrame())
+    if (player_ctx->buffer->IsDVD() &&
+        player_ctx->buffer->DVD()->IsInStillFrame())
+    {
         SetScanType(kScan_Progressive);
+    }
     DisplayDVDButton();
     MythPlayer::DisplayPauseFrame();
 }
@@ -60,7 +63,7 @@
 
 bool MythDVDPlayer::PrebufferEnoughFrames(bool pause_audio, int  min_buffers)
 {
-    bool instill = player_ctx->buffer->InDiscMenuOrStillFrame();
+    bool instill = player_ctx->buffer->IsInDiscMenuOrStillFrame();
     return MythPlayer::PrebufferEnoughFrames(!instill, 1);
 }
 
@@ -74,8 +77,8 @@
 bool MythDVDPlayer::DecoderGetFrameREW(void)
 {
     MythPlayer::DecoderGetFrameREW();
-    return (player_ctx->buffer->isDVD() &&
-           (player_ctx->buffer->DVD()->GetCurrentTime() < 2));
+    return (player_ctx->buffer->IsDVD() &&
+            (player_ctx->buffer->DVD()->GetCurrentTime() < 2));
 }
 
 void MythDVDPlayer::PreProcessNormalFrame(void)
@@ -85,7 +88,7 @@
 
 bool MythDVDPlayer::VideoLoop(void)
 {
-    if (!player_ctx->buffer->isDVD())
+    if (!player_ctx->buffer->IsDVD())
     {
         SetErrored("RingBuffer is not a DVD.");
         return !IsErrored();
@@ -104,7 +107,7 @@
     bool release_one = (nbframes > 1) && videoPaused && !allpaused &&
                        (!videoOutput->EnoughFreeFrames() ||
                         player_ctx->buffer->DVD()->IsWaiting() ||
-                        player_ctx->buffer->DVD()->InStillFrame());
+                        player_ctx->buffer->DVD()->IsInStillFrame());
     if (release_all || release_one)
     {
         if (nbframes < 5 && videoOutput)
@@ -123,7 +126,7 @@
         VERBOSE(VB_PLAYBACK, LOC + "Clearing Mythtv dvd wait state");
         player_ctx->buffer->DVD()->SkipDVDWaitingForPlayer();
         ClearAfterSeek(true);
-        if (!player_ctx->buffer->DVD()->InStillFrame() && videoPaused)
+        if (!player_ctx->buffer->DVD()->IsInStillFrame() && videoPaused)
             UnpauseVideo();
         return !IsErrored();
     }
@@ -136,14 +139,14 @@
         {
             VERBOSE(VB_PLAYBACK, LOC + "Clearing DVD wait state");
             player_ctx->buffer->DVD()->WaitSkip();
-            if (!player_ctx->buffer->DVD()->InStillFrame() && videoPaused)
+            if (!player_ctx->buffer->DVD()->IsInStillFrame() && videoPaused)
                 UnpauseVideo();
             return !IsErrored();
         }
 
         // we need a custom presentation method for still frame menus with audio
         if (player_ctx->buffer->DVD()->IsInMenu() &&
-           !player_ctx->buffer->DVD()->InStillFrame())
+            !player_ctx->buffer->DVD()->IsInStillFrame())
         {
             // ensure we refresh the pause frame
             if (!dvd_stillframe_showing)
@@ -155,7 +158,7 @@
         }
 
         // the still frame is treated as a pause frame
-        if (player_ctx->buffer->DVD()->InStillFrame())
+        if (player_ctx->buffer->DVD()->IsInStillFrame())
         {
             // ensure we refresh the pause frame
             if (!dvd_stillframe_showing)
@@ -318,19 +321,21 @@
 
 bool MythDVDPlayer::PrepareAudioSample(int64_t &timecode)
 {
-    if (!player_ctx->buffer->InDiscMenuOrStillFrame())
+    if (!player_ctx->buffer->IsInDiscMenuOrStillFrame())
         WrapTimecode(timecode, TC_AUDIO);
 
-    if (player_ctx->buffer->isDVD() &&
-        player_ctx->buffer->DVD()->InStillFrame())
+    if (player_ctx->buffer->IsDVD() &&
+        player_ctx->buffer->DVD()->IsInStillFrame())
         return true;
     return false;
 }
 
 void MythDVDPlayer::SetBookmark(void)
 {
-    if (player_ctx->buffer->InDiscMenuOrStillFrame())
+    if (player_ctx->buffer->IsInDiscMenuOrStillFrame())
+    {
         SetDVDBookmark(0);
+    }
     else
     {
         SetDVDBookmark(framesPlayed);
@@ -348,7 +353,7 @@
 
 uint64_t MythDVDPlayer::GetBookmark(void)
 {
-    if (gCoreContext->IsDatabaseIgnored() || !player_ctx->buffer->isDVD())
+    if (gCoreContext->IsDatabaseIgnored() || !player_ctx->buffer->IsDVD())
         return 0;
 
     QStringList dvdbookmark = QStringList();
@@ -387,9 +392,9 @@
     MythPlayer::ChangeSpeed();
     if (decoder)
         decoder->UpdateDVDFramesPlayed();
-    if (play_speed != normal_speed && player_ctx->buffer->isDVD())
+    if (play_speed != normal_speed && player_ctx->buffer->IsDVD())
         player_ctx->buffer->DVD()->SetDVDSpeed(-1);
-    else if (player_ctx->buffer->isDVD())
+    else if (player_ctx->buffer->IsDVD())
         player_ctx->buffer->DVD()->SetDVDSpeed();
 }
 
@@ -400,7 +405,7 @@
 
 long long MythDVDPlayer::CalcMaxFFTime(long long ff, bool setjump) const
 {
-    if ((totalFrames > 0) && player_ctx->buffer->isDVD() &&
+    if ((totalFrames > 0) && player_ctx->buffer->IsDVD() &&
         player_ctx->buffer->DVD()->TitleTimeLeft() < 5)
         return 0;
     return MythPlayer::CalcMaxFFTime(ff, setjump);
@@ -413,7 +418,7 @@
     info.values.insert("position",   0);
     info.values.insert("progbefore", 0);
     info.values.insert("progafter",  0);
-    if (!player_ctx->buffer->isDVD())
+    if (!player_ctx->buffer->IsDVD())
         return;
 
     int playbackLen = totalLength;
@@ -438,13 +443,15 @@
 void MythDVDPlayer::SeekForScreenGrab(uint64_t &number, uint64_t frameNum,
                                       bool absolute)
 {
-    if (!player_ctx->buffer->isDVD())
+    if (!player_ctx->buffer->IsDVD())
         return;
     if (GoToDVDMenu("menu"))
     {
         if (player_ctx->buffer->DVD()->IsInMenu() &&
-           !player_ctx->buffer->DVD()->InStillFrame())
+            !player_ctx->buffer->DVD()->IsInStillFrame())
+        {
             GoToDVDProgram(1);
+        }
     }
     else if (player_ctx->buffer->DVD()->GetTotalTimeOfTitle() < 60)
     {
@@ -471,12 +478,12 @@
 {
     if (decoder)
         decoder->ChangeDVDTrack(need_change_dvd_track > 0);
-    ClearAfterSeek(!player_ctx->buffer->InDiscMenuOrStillFrame());
+    ClearAfterSeek(!player_ctx->buffer->IsInDiscMenuOrStillFrame());
 }
 
 void MythDVDPlayer::DisplayDVDButton(void)
 {
-    if (!osd || !player_ctx->buffer->isDVD())
+    if (!osd || !player_ctx->buffer->IsDVD())
         return;
 
     uint buttonversion = 0;
@@ -484,7 +491,7 @@
     bool numbuttons    = player_ctx->buffer->DVD()->NumMenuButtons();
 
     // nothing to do
-    if (buttonversion == m_buttonVersion)
+    if (buttonversion == ((uint)m_buttonVersion))
     {
         player_ctx->buffer->DVD()->ReleaseMenuButton();
         return;
@@ -510,7 +517,7 @@
 
 bool MythDVDPlayer::GoToDVDMenu(QString str)
 {
-    if (!player_ctx->buffer->isDVD())
+    if (!player_ctx->buffer->IsDVD())
         return false;
     textDisplayMode = kDisplayNone;
     bool ret = player_ctx->buffer->DVD()->GoToMenu(str);
@@ -527,7 +534,7 @@
 
 void MythDVDPlayer::GoToDVDProgram(bool direction)
 {
-    if (!player_ctx->buffer->isDVD())
+    if (!player_ctx->buffer->IsDVD())
         return;
     if (direction == 0)
         player_ctx->buffer->DVD()->GoToPreviousProgram();
@@ -537,7 +544,7 @@
 
 void MythDVDPlayer::SetDVDBookmark(uint64_t frame)
 {
-    if (!player_ctx->buffer->isDVD())
+    if (!player_ctx->buffer->IsDVD())
         return;
 
     uint64_t framenum = frame;
@@ -556,8 +563,9 @@
         return;
     }
 
-    if (!player_ctx->buffer->InDiscMenuOrStillFrame() &&
-        player_ctx->buffer->DVD()->GetTotalTimeOfTitle() > 120 && frame > 0)
+    if (!player_ctx->buffer->IsInDiscMenuOrStillFrame() &&
+        player_ctx->buffer->DVD()->
+        GetTotalTimeOfTitle() > 120 && frame > 0)
     {
         audiotrack = GetTrack(kTrackTypeAudio);
         if (GetCaptionMode() == kDisplayAVSubtitle)
@@ -637,8 +645,8 @@
 
 void MythDVDPlayer::StillFrameCheck(void)
 {
-    if (player_ctx->buffer->isDVD() &&
-        player_ctx->buffer->DVD()->InStillFrame() &&
+    if (player_ctx->buffer->IsDVD() &&
+        player_ctx->buffer->DVD()->IsInStillFrame() &&
        (m_stillFrameLength > 0) && (m_stillFrameLength < 0xff))
     {
         m_stillFrameTimerLock.lock();
Index: libs/libmythtv/previewgenerator.cpp
===================================================================
--- libs/libmythtv/previewgenerator.cpp	(revision 27380)
+++ libs/libmythtv/previewgenerator.cpp	(working copy)
@@ -788,7 +788,7 @@
         }
     }
 
-    RingBuffer *rbuf = new RingBuffer(filename, false, false, 0);
+    RingBuffer *rbuf = RingBuffer::Create(filename, false, false, 0);
     if (!rbuf->IsOpen())
     {
         VERBOSE(VB_IMPORTANT, LOC_ERR + "Previewer could not open file: " +
Index: libs/libmythtv/FileRingBuffer.h
===================================================================
--- libs/libmythtv/FileRingBuffer.h	(revision 0)
+++ libs/libmythtv/FileRingBuffer.h	(revision 0)
@@ -0,0 +1,35 @@
+#include "RingBuffer.h"
+
+class MPUBLIC FileRingBuffer : public RingBuffer
+{
+    friend class RingBuffer;
+  public:
+    ~FileRingBuffer();
+
+    // Gets
+    virtual bool      IsOpen(void)          const;
+    virtual long long GetReadPosition(void) const;
+    virtual long long GetRealFileSize(void) const;
+
+    // General Commands
+    virtual bool OpenFile(const QString &lfilename,
+                          uint retry_ms = kDefaultOpenTimeout);
+    virtual long long Seek(long long pos, int whence, bool has_lock);
+
+  protected:
+    FileRingBuffer(const QString &lfilename,
+                   bool write, bool readahead, int timeout_ms);
+
+    virtual int safe_read(void *data, uint sz)
+    {
+        if (remotefile)
+            return safe_read(remotefile, data, sz);
+        else if (fd2 >= 0)
+            return safe_read(fd2, data, sz);
+
+        errno = EBADF;
+        return -1;
+    }
+    int safe_read(int fd, void *data, uint sz);
+    int safe_read(RemoteFile *rf, void *data, uint sz);
+};
Index: libs/libmythtv/RingBuffer.cpp
===================================================================
--- libs/libmythtv/RingBuffer.cpp	(revision 27380)
+++ libs/libmythtv/RingBuffer.cpp	(working copy)
@@ -9,40 +9,23 @@
 #include <sys/time.h>
 #include <unistd.h>
 #include <fcntl.h>
-#include <pthread.h>
 
 // Qt headers
 #include <QFile>
 #include <QDateTime>
-#include <QFileInfo>
-#include <QDir>
 
-using namespace std;
-
-#include "mythcontext.h" // for VERBOSE
-#include "mythconfig.h"
-#include "exitcodes.h"
-#include "RingBuffer.h"
-#include "remotefile.h"
 #include "ThreadedFileWriter.h"
-#include "livetvchain.h"
+#include "FileRingBuffer.h"
 #include "DVDRingBuffer.h"
 #include "BDRingBuffer.h"
-#include "util.h"
+#include "livetvchain.h"
+#include "mythcontext.h"
+#include "RingBuffer.h"
+#include "mythconfig.h"
+#include "remotefile.h"
 #include "compat.h"
+#include "util.h"
 
-#ifndef O_STREAMING
-#define O_STREAMING 0
-#endif
-
-#ifndef O_LARGEFILE
-#define O_LARGEFILE 0
-#endif
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
 #if HAVE_POSIX_FADVISE < 1
 static int posix_fadvise(int, off_t, off_t, int) { return 0; }
 #define POSIX_FADV_SEQUENTIAL 0
@@ -122,451 +105,130 @@
  *                      the file after the first failure in
  *                      milliseconds before giving up.
  */
-RingBuffer::RingBuffer(const QString &lfilename,
-                       bool write, bool readahead,
-                       int timeout_ms)
-    : readpos(0),               writepos(0),
-      internalreadpos(0),       ignorereadpos(-1),
-      rbrpos(0),                rbwpos(0),
-      stopreads(false),
-      filename(lfilename),      subtitlefilename(QString::null),
-      tfw(NULL),                fd2(-1),
-      writemode(false),         remotefile(NULL),
-      startreadahead(readahead),readAheadBuffer(NULL),
-      readaheadrunning(false),  reallyrunning(false),
-      request_pause(false),     paused(false),
-      ateof(false),             readsallowed(false),
-      setswitchtonext(false),   streamOnly(false),
-      rawbitrate(8000),         playspeed(1.0f),
-      fill_threshold(65536),    fill_min(-1),
-      readblocksize(CHUNK),     wanttoread(0),
-      numfailures(0),           commserror(false),
-      dvdPriv(NULL),            bdPriv(NULL),
-      oldfile(false),           livetvchain(NULL),
-      ignoreliveeof(false),     readAdjust(0)
+RingBuffer *RingBuffer::Create(
+    const QString &xfilename, bool write,
+    bool usereadahead, int timeout_ms, bool stream_only)
 {
-    filename.detach();
+    QString lfilename = xfilename;
 
-    {
-        QMutexLocker locker(&subExtLock);
-        if (!subExt.size())
-        {
-            // Possible subtitle file extensions '.srt', '.sub', '.txt'
-            subExt += ".srt";
-            subExt += ".sub";
-            subExt += ".txt";
-
-            // Extensions for which a subtitle file should not exist
-            subExtNoCheck = subExt;
-            subExtNoCheck += ".gif";
-            subExtNoCheck += ".png";
-        }
-    }
-
     if (write)
     {
-        if (filename.startsWith("myth://"))
-        {
-            remotefile = new RemoteFile(filename, true);
-            if (!remotefile->isOpen())
-            {
-                VERBOSE(VB_IMPORTANT,
-                        QString("RingBuffer::RingBuffer(): Failed to open "
-                                "remote file (%1) for write").arg(filename));
-                delete remotefile;
-                remotefile = NULL;
-            }
-            else
-                writemode = true;
-        }
-        else
-        {
-            tfw = new ThreadedFileWriter(
-                filename, O_WRONLY|O_TRUNC|O_CREAT|O_LARGEFILE, 0644);
-
-            if (!tfw->Open())
-            {
-                delete tfw;
-                tfw = NULL;
-            }
-            else
-                writemode = true;
-        }
-        return;
+        return new FileRingBuffer(
+            lfilename, write, usereadahead, timeout_ms);
     }
 
-    if (timeout_ms >= 0)
-        OpenFile(filename, timeout_ms);
-}
-
-/** \fn check_permissions(const QString&)
- *  \brief Returns false iff file exists and has incorrect permissions.
- *  \param filename File (including path) that we want to know about
- */
-static bool check_permissions(const QString &filename)
-{
-    QFileInfo fileInfo(filename);
-    if (fileInfo.exists() && !fileInfo.isReadable())
-    {
-        VERBOSE(VB_IMPORTANT, LOC_ERR +
-                "File exists but is not readable by MythTV!");
-        return false;
-    }
-    return true;
-}
-
-static bool is_subtitle_possible(const QString &extension)
-{
-    QMutexLocker locker(&RingBuffer::subExtLock);
-    bool no_subtitle = false;
-    for (uint i = 0; i < (uint)RingBuffer::subExtNoCheck.size(); i++)
-    {
-        if (extension.contains(RingBuffer::subExtNoCheck[i].right(3)))
-        {
-            no_subtitle = true;
-            break;
-        }
-    }
-    return !no_subtitle;
-}
-
-static QString local_sub_filename(QFileInfo &fileInfo)
-{
-    // Subtitle handling
-    QString vidFileName = fileInfo.fileName();
-    QString dirName = fileInfo.absolutePath();
-
-    QString baseName = vidFileName;
-    int suffixPos = vidFileName.lastIndexOf(QChar('.'));
-    if (suffixPos > 0)
-        baseName = vidFileName.left(suffixPos);
-
-    QStringList el;
-    {
-        // The dir listing does not work if the filename has the
-        // following chars "[]()" so we convert them to the wildcard '?'
-        const QString findBaseName = baseName
-            .replace("[", "?")
-            .replace("]", "?")
-            .replace("(", "?")
-            .replace(")", "?");
-
-        QMutexLocker locker(&RingBuffer::subExtLock);
-        QStringList::const_iterator eit = RingBuffer::subExt.begin();
-        for (; eit != RingBuffer::subExt.end(); eit++)
-            el += findBaseName + *eit;
-    }
-
-    // Some Qt versions do not accept paths in the search string of
-    // entryList() so we have to set the dir first
-    QDir dir;
-    dir.setPath(dirName);
-
-    const QStringList candidates = dir.entryList(el);
-
-    QStringList::const_iterator cit = candidates.begin();
-    for (; cit != candidates.end(); ++cit)
-    {
-        QFileInfo fi(dirName + "/" + *cit);
-        if (fi.exists() && (fi.size() >= RingBuffer::kReadTestSize))
-            return fi.absoluteFilePath();
-    }
-
-    return QString::null;
-}
-
-/** \brief Opens a file for reading.
- *
- *  \param lfilename  Name of file to read
- *  \param retry_ms   How many ms to retry reading the file
- *                    after the first try before giving up.
- */
-void RingBuffer::OpenFile(const QString &lfilename, uint retry_ms)
-{
-    VERBOSE(VB_PLAYBACK, LOC + QString("OpenFile(%1, %2 ms)")
-            .arg(lfilename).arg(retry_ms));
-
-    if (retry_ms >= 1 && retry_ms <= 20)
-    {
-        VERBOSE(VB_IMPORTANT, LOC + QString("OpenFile(%1, %2 ms)")
-                .arg(lfilename).arg(retry_ms) +
-                "Timeout small, assuming caller meant for this "
-                "to be retries." + "\n\t\t\t"
-                "WARNING: This workaround will be removed after the "
-                "0.24 release, and this client may stop working.");
-        retry_ms *= 150;
-        qWarning("Applying temporary OpenFile() hack");
-    }
-
-    rwlock.lockForWrite();
-
-    filename = lfilename;
-
-    if (remotefile)
-    {
-        delete remotefile;
-    }
-
-    if (fd2 >= 0)
-    {
-        close(fd2);
-        fd2 = -1;
-    }
-
-    bool is_local = false;
     bool is_dvd = false;
-    (void) is_dvd; // not used when frontend is disabled.
-    bool is_bd = false;
-    (void) is_bd;
+    bool is_bd  = false;
 
-    if (!streamOnly && filename.startsWith("myth://"))
+    if (!stream_only && lfilename.startsWith("myth://"))
     {
         struct stat fileInfo;
-        if ((RemoteFile::Exists(filename, &fileInfo)) &&
+        if ((RemoteFile::Exists(lfilename, &fileInfo)) &&
             (S_ISDIR(fileInfo.st_mode)))
         {
-            QString tmpFile = filename + "/VIDEO_TS";
+            QString tmpFile = lfilename + "/VIDEO_TS";
             if (RemoteFile::Exists(tmpFile))
             {
                 is_dvd = true;
             }
             else
             {
-                tmpFile = filename + "/BDMV";
+                tmpFile = lfilename + "/BDMV";
                 if (RemoteFile::Exists(tmpFile))
                     is_bd = true;
             }
         }
     }
 
-    if ((filename.left(1) == "/") ||
-        (QFile::exists(filename)))
-        is_local = true;
-
-#ifdef USING_FRONTEND
-    else if ((!streamOnly) &&
-             ((filename.startsWith("dvd:")) || is_dvd ||
-              ((filename.startsWith("myth://")) &&
-               ((filename.endsWith(".img")) ||
-                (filename.endsWith(".iso"))))))
+    if ((lfilename.left(1) == "/") || (QFile::exists(lfilename)))
     {
+    }
+    else if ((!stream_only) &&
+             ((lfilename.startsWith("dvd:")) || is_dvd ||
+              ((lfilename.startsWith("myth://")) &&
+               ((lfilename.endsWith(".img")) ||
+                (lfilename.endsWith(".iso"))))))
+    {
         is_dvd = true;
-        dvdPriv = new DVDRingBufferPriv();
-        startreadahead = false;
 
-        if (filename.left(6) == "dvd://")      // 'Play DVD' sends "dvd:/" + dev
-            filename.remove(0,5);              //             e.g. "dvd://dev/sda"
-        else if (filename.left(5) == "dvd:/")  // Less correct URI "dvd:" + path
-            filename.remove(0,4);              //             e.g. "dvd:/videos/ET"
+        if (lfilename.left(6) == "dvd://")     // 'Play DVD' sends "dvd:/" + dev
+            lfilename.remove(0,5);             // e.g. "dvd://dev/sda"
+        else if (lfilename.left(5) == "dvd:/") // Less correct URI "dvd:" + path
+            lfilename.remove(0,4);             // e.g. "dvd:/videos/ET"
 
-        if (QFile::exists(filename) || filename.startsWith("myth://"))
-            VERBOSE(VB_PLAYBACK, "OpenFile() trying DVD at " + filename);
-        else
+        if (QFile::exists(lfilename) || lfilename.startsWith("myth://"))
         {
-            filename = "/dev/dvd";
+            VERBOSE(VB_PLAYBACK, "Trying DVD at " + lfilename);
         }
-    }
-    else if ((!streamOnly) && (filename.left(3) == "bd:" || is_bd))
-    {
-        is_bd = true;
-        bdPriv = new BDRingBufferPriv();
-        startreadahead = false;
-
-        if (filename.left(5) == "bd://")      // 'Play DVD' sends "bd:/" + dev
-            filename.remove(0,4);             //             e.g. "bd://dev/sda"
-        else if (filename.left(4) == "bd:/")  // Less correct URI "bd:" + path
-            filename.remove(0,3);             //             e.g. "bd:/videos/ET"
-
-        if (QFile::exists(filename) || filename.startsWith("myth://"))
-            VERBOSE(VB_PLAYBACK, "OpenFile() trying BD at " + filename);
         else
         {
-            filename = "/dev/dvd";
+            lfilename = "/dev/dvd";
         }
-    }
-#endif // USING_FRONTEND
 
-    if (is_local)
+        return new DVDRingBuffer(lfilename);
+    }
+    else if ((!stream_only) && (lfilename.left(3) == "bd:" || is_bd))
     {
-        char buf[kReadTestSize];
-        int lasterror = 0;
+        is_bd = true;
 
-        MythTimer openTimer;
-        openTimer.start();
+        if (lfilename.left(5) == "bd://")      // 'Play DVD' sends "bd:/" + dev
+            lfilename.remove(0,4);             // e.g. "bd://dev/sda"
+        else if (lfilename.left(4) == "bd:/")  // Less correct URI "bd:" + path
+            lfilename.remove(0,3);             // e.g. "bd:/videos/ET"
 
-        uint openAttempts = 0;
-        do
+        if (QFile::exists(lfilename) || lfilename.startsWith("myth://"))
         {
-            openAttempts++;
-            lasterror = 0;
-            QByteArray fname = filename.toLocal8Bit();
-            fd2 = open(fname.constData(),
-                       O_RDONLY|O_LARGEFILE|O_STREAMING|O_BINARY);
-
-            if (fd2 < 0)
-            {
-                if (!check_permissions(filename))
-                {
-                    lasterror = 3;
-                    break;
-                }
-
-                lasterror = 1;
-                usleep(10 * 1000);
-            }
-            else
-            {
-                int ret = read(fd2, buf, kReadTestSize);
-                if (ret != (int)kReadTestSize)
-                {
-                    lasterror = 2;
-                    close(fd2);
-                    fd2 = -1;
-                    if (oldfile)
-                        break; // if it's an old file it won't grow..
-                    usleep(10 * 1000);
-                }
-                else
-                {
-                    if (0 == lseek(fd2, 0, SEEK_SET))
-                    {
-                        posix_fadvise(fd2, 0, 0, POSIX_FADV_SEQUENTIAL);
-                        posix_fadvise(fd2, 0, 1*1024*1024, POSIX_FADV_WILLNEED);
-                        lasterror = 0;
-                        break;
-                    }
-                    lasterror = 4;
-                    close(fd2);
-                    fd2 = -1;
-                }
-            }
+            VERBOSE(VB_PLAYBACK, "Trying BD at " + lfilename);
         }
-        while ((uint)openTimer.elapsed() < retry_ms);
-
-        switch (lasterror)
+        else
         {
-            case 0:
-            {
-                QFileInfo fi(filename);
-                oldfile = fi.lastModified()
-                    .secsTo(QDateTime::currentDateTime()) > 60;
-                QString extension = fi.completeSuffix().toLower();
-                if (is_subtitle_possible(extension))
-                    subtitlefilename = local_sub_filename(fi);
-                break;
-            }
-            case 1:
-                VERBOSE(VB_IMPORTANT, LOC_ERR +
-                        QString("OpenFile(): Could not open."));
-                break;
-            case 2:
-                VERBOSE(VB_IMPORTANT, LOC_ERR +
-                        QString("OpenFile(): File too small (%1B).")
-                        .arg(QFileInfo(filename).size()));
-                break;
-            case 3:
-                VERBOSE(VB_IMPORTANT, LOC_ERR +
-                        "OpenFile(): Improper permissions.");
-                break;
-            case 4:
-                VERBOSE(VB_IMPORTANT, LOC_ERR +
-                        "OpenFile(): Cannot seek in file.");
-                break;
-            default:
-                break;
+            lfilename = "/dev/dvd";
         }
-        VERBOSE(VB_FILE, LOC + QString("OpenFile() made %1 attempts in %2 ms")
-                .arg(openAttempts).arg(openTimer.elapsed()));
 
+        return new BDRingBuffer(lfilename);
     }
-#ifdef USING_FRONTEND
-    else if (is_dvd)
-    {
-        dvdPriv->OpenFile(filename);
-        readblocksize = DVD_BLOCK_SIZE * 62;
-    }
-    else if (is_bd)
-    {
-        bdPriv->OpenFile(filename);
-        readblocksize = BD_BLOCK_SIZE * 62;
-    }
-#endif // USING_FRONTEND
-    else
-    {
-        QString tmpSubName = filename;
-        QString dirName  = ".";
 
-        int dirPos = filename.lastIndexOf(QChar('/'));
-        if (dirPos > 0)
-        {
-            tmpSubName = filename.mid(dirPos + 1);
-            dirName = filename.left(dirPos);
-        }
+    return new FileRingBuffer(
+        lfilename, write, usereadahead, timeout_ms);
+}
 
-        QString baseName  = tmpSubName;
-        QString extension = tmpSubName;
-        QStringList auxFiles;
-
-        int suffixPos = tmpSubName.lastIndexOf(QChar('.'));
-        if (suffixPos > 0)
+RingBuffer::RingBuffer(void) :
+    readpos(0),               writepos(0),
+    internalreadpos(0),       ignorereadpos(-1),
+    rbrpos(0),                rbwpos(0),
+    stopreads(false),
+    filename(),               subtitlefilename(),
+    tfw(NULL),                fd2(-1),
+    writemode(false),         remotefile(NULL),
+    startreadahead(false),    readAheadBuffer(NULL),
+    readaheadrunning(false),  reallyrunning(false),
+    request_pause(false),     paused(false),
+    ateof(false),             readsallowed(false),
+    setswitchtonext(false),
+    rawbitrate(8000),         playspeed(1.0f),
+    fill_threshold(65536),    fill_min(-1),
+    readblocksize(CHUNK),     wanttoread(0),
+    numfailures(0),           commserror(false),
+    oldfile(false),           livetvchain(NULL),
+    ignoreliveeof(false),     readAdjust(0)
+{
+    {
+        QMutexLocker locker(&subExtLock);
+        if (subExt.empty())
         {
-            baseName = tmpSubName.left(suffixPos);
-            extension = tmpSubName.right(suffixPos-1);
-            if (is_subtitle_possible(extension))
-            {
-                QMutexLocker locker(&subExtLock);
-                QStringList::const_iterator eit = subExt.begin();
-                for (; eit != subExt.end(); eit++)
-                    auxFiles += baseName + *eit;
-            }
-        }
+            // Possible subtitle file extensions '.srt', '.sub', '.txt'
+            subExt += ".srt";
+            subExt += ".sub";
+            subExt += ".txt";
 
-        remotefile = new RemoteFile(filename, false, true,
-                                    retry_ms, &auxFiles);
-        if (!remotefile->isOpen())
-        {
-            VERBOSE(VB_IMPORTANT, LOC_ERR +
-                    QString("RingBuffer::RingBuffer(): Failed to open remote "
-                            "file (%1)").arg(filename));
-            delete remotefile;
-            remotefile = NULL;
+            // Extensions for which a subtitle file should not exist
+            subExtNoCheck = subExt;
+            subExtNoCheck += ".gif";
+            subExtNoCheck += ".png";
         }
-        else
-        {
-            QStringList aux = remotefile->GetAuxiliaryFiles();
-            if (aux.size())
-                subtitlefilename = dirName + "/" + aux[0];
-        }
     }
-
-    setswitchtonext = false;
-    ateof = false;
-    commserror = false;
-    numfailures = 0;
-
-    rawbitrate = 8000;
-    CalcReadAheadThresh();
-
-    rwlock.unlock();
 }
 
-/** \fn RingBuffer::IsOpen(void) const
- *  \brief Returns true if the file is open for either reading or writing.
- */
-bool RingBuffer::IsOpen(void) const
-{
-    rwlock.lockForRead();
-    bool ret;
-#ifdef USING_FRONTEND
-    ret = tfw || (fd2 > -1) || remotefile || (dvdPriv && dvdPriv->IsOpen()) ||
-           (bdPriv && bdPriv->IsOpen());
-#else // if !USING_FRONTEND
-    ret = tfw || (fd2 > -1) || remotefile;
-#endif // !USING_FRONTEND
-    rwlock.unlock();
-    return ret;
-}
-
 /** \fn RingBuffer::~RingBuffer(void)
  *  \brief Shuts down any threads and closes any files.
  */
@@ -574,37 +236,6 @@
 {
     KillReadAheadThread();
 
-    rwlock.lockForWrite();
-
-    if (remotefile)
-    {
-        delete remotefile;
-        remotefile = NULL;
-    }
-
-    if (tfw)
-    {
-        delete tfw;
-        tfw = NULL;
-    }
-
-    if (fd2 >= 0)
-    {
-        close(fd2);
-        fd2 = -1;
-    }
-
-#ifdef USING_FRONTEND
-    if (dvdPriv)
-    {
-        delete dvdPriv;
-    }
-    if (bdPriv)
-    {
-        delete bdPriv;
-    }
-#endif // USING_FRONTEND
-
     if (readAheadBuffer) // this only runs if thread is terminated
     {
         delete [] readAheadBuffer;
@@ -653,110 +284,6 @@
     rwlock.unlock();
 }
 
-/** \fn RingBuffer::safe_read(int, void*, uint)
- *  \brief Reads data from the file-descriptor.
- *
- *   This will re-read the file forever until the
- *   end-of-file is reached or the buffer is full.
- *
- *  \param fd   File descriptor to read from
- *  \param data Pointer to where data will be written
- *  \param sz   Number of bytes to read
- *  \return Returns number of bytes read
- */
-int RingBuffer::safe_read(int fd, void *data, uint sz)
-{
-    int ret;
-    unsigned tot = 0;
-    unsigned errcnt = 0;
-    unsigned zerocnt = 0;
-
-    if (fd2 < 0)
-    {
-        VERBOSE(VB_IMPORTANT, LOC_ERR +
-                "Invalid file descriptor in 'safe_read()'");
-        return 0;
-    }
-
-    if (stopreads)
-        return 0;
-
-    while (tot < sz)
-    {
-        ret = read(fd2, (char *)data + tot, sz - tot);
-        if (ret < 0)
-        {
-            if (errno == EAGAIN)
-                continue;
-
-            VERBOSE(VB_IMPORTANT,
-                    LOC_ERR + "File I/O problem in 'safe_read()'" + ENO);
-
-            errcnt++;
-            numfailures++;
-            if (errcnt == 3)
-                break;
-        }
-        else if (ret > 0)
-        {
-            tot += ret;
-        }
-
-        if (oldfile)
-            break;
-
-        if (ret == 0) // EOF returns 0
-        {
-            if (tot > 0)
-                break;
-
-            zerocnt++;
-
-            // 0.36 second timeout for livetvchain with usleep(60000),
-            // or 2.4 seconds if it's a new file less than 30 minutes old.
-            if (zerocnt >= (livetvchain ? 6 : 40))
-            {
-                break;
-            }
-        }
-        if (stopreads)
-            break;
-        if (tot < sz)
-            usleep(60000);
-    }
-    return tot;
-}
-
-/** \fn RingBuffer::safe_read(RemoteFile*, void*, uint)
- *  \brief Reads data from the RemoteFile.
- *
- *  \param rf   RemoteFile to read from
- *  \param data Pointer to where data will be written
- *  \param sz   Number of bytes to read
- *  \return Returns number of bytes read
- */
-int RingBuffer::safe_read(RemoteFile *rf, void *data, uint sz)
-{
-    int ret = rf->Read(data, sz);
-    if (ret < 0)
-    {
-        VERBOSE(VB_IMPORTANT, LOC_ERR +
-                "RingBuffer::safe_read(RemoteFile* ...): read failed");
-            
-        poslock.lockForRead();
-        rf->Seek(internalreadpos - readAdjust, SEEK_SET);
-        poslock.unlock();
-        numfailures++;
-    }
-    else if (ret == 0)
-    {
-        VERBOSE(VB_FILE, LOC +
-                "RingBuffer::safe_read(RemoteFile* ...): at EOF");
-    }
-
-    return ret;
-}
-
 /** \fn RingBuffer::UpdateRawBitrate(uint)
  *  \brief Set the raw bit rate, to allow RingBuffer adjust effective bitrate.
  *  \param raw_bitrate Streams average number of kilobits per second when
@@ -1228,35 +755,19 @@
                         "Reading enough data to start playback");
             }
 
+            if (remotefile && livetvchain && livetvchain->HasNext())
+                remotefile->SetTimeout(true);
+
             VERBOSE(VB_FILE|VB_EXTRA,
                     LOC + QString("safe_read(...@%1, %2) -- begin")
                     .arg(rbwpos).arg(totfree));
-            if (remotefile)
-            {
-                if (livetvchain && livetvchain->HasNext())
-                    remotefile->SetTimeout(true);
-                read_return = safe_read(
-                    remotefile, readAheadBuffer + rbwpos, totfree);
-            }
-#ifdef USING_FRONTEND
-            else if (dvdPriv)
-            {
-                read_return = dvdPriv->safe_read(
-                    readAheadBuffer + rbwpos, totfree);
-            }
-            else if (bdPriv)
-            {
-                read_return = bdPriv->safe_read(
-                    readAheadBuffer + rbwpos, totfree);
-            }
-#endif // USING_FRONTEND
-            else
-            {
-                read_return = safe_read(fd2, readAheadBuffer + rbwpos, totfree);
-            }
+
+            read_return = safe_read(readAheadBuffer + rbwpos, totfree);
+
             VERBOSE(VB_FILE|VB_EXTRA, LOC +
                     QString("safe_read(...@%1, %2) -> %3")
                     .arg(rbwpos).arg(totfree).arg(read_return));
+
             rbwlock.unlock();
         }
 
@@ -1485,22 +996,7 @@
         poslock.unlock();
     }
 
-    int ret;
-    if (remotefile)
-        ret = safe_read(remotefile, buf, count);
-#ifdef USING_FRONTEND
-    else if (dvdPriv)
-        ret = dvdPriv->safe_read(buf, count);
-    else if (bdPriv)
-        ret = bdPriv->safe_read(buf, count);
-#endif // USING_FRONTEND
-    else if (fd2 >= 0)
-        ret = safe_read(fd2, buf, count);
-    else
-    {
-        ret = -1;
-        errno = EBADF;
-    }
+    int ret = safe_read(buf, count);
 
     poslock.lockForWrite();
     if (ignorereadpos >= 0 && ret > 0)
@@ -1527,31 +1023,25 @@
 
     if (peek && ret > 0)
     {
-        if (!dvdPriv && !bdPriv)
+        if ((IsDVD() || IsBD()) && old_pos != 0)
         {
-            long long new_pos = Seek(old_pos, SEEK_SET, true);
-            if (new_pos != old_pos)
-            {
-                VERBOSE(VB_IMPORTANT, LOC_ERR +
-                        QString("Peek() Failed to return from new "
-                                "position %1 to old position %2, now "
-                                "at position %3")
-                        .arg(old_pos - ret).arg(old_pos).arg(new_pos));
-            }
-        }
-        else if (old_pos != 0)
-        {
             VERBOSE(VB_IMPORTANT, LOC_ERR +
                     "DVD and Blu-Ray do not support arbitrary "
                     "peeks except when read-ahead is enabled."
                     "\n\t\t\tWill seek to beginning of video.");
+            old_pos = 0;
         }
-#ifdef USING_FRONTEND
-        if (dvdPriv)
-            dvdPriv->NormalSeek(old_pos);
-        else if (bdPriv)
-            bdPriv->Seek(old_pos);
-#endif // USING_FRONTEND
+
+        long long new_pos = Seek(old_pos, SEEK_SET, true);
+
+        if (new_pos != old_pos)
+        {
+            VERBOSE(VB_IMPORTANT, LOC_ERR +
+                    QString("Peek() Failed to return from new "
+                            "position %1 to old position %2, now "
+                            "at position %3")
+                    .arg(old_pos - ret).arg(old_pos).arg(new_pos));
+        }
     }
 
     return ret;
@@ -1777,322 +1267,6 @@
     rwlock.unlock();
 }
 
-/** \brief Seeks to a particular position in the file.
- */
-long long RingBuffer::Seek(long long pos, int whence, bool has_lock)
-{
-    VERBOSE(VB_FILE, LOC + QString("Seek(%1,%2,%3)")
-            .arg(pos).arg((SEEK_SET==whence)?"SEEK_SET":
-                          ((SEEK_CUR==whence)?"SEEK_CUR":"SEEK_END"))
-            .arg(has_lock?"locked":"unlocked"));
-
-    long long ret = -1;
-
-    StopReads();
-
-    // lockForWrite takes priority over lockForRead, so this will
-    // take priority over the lockForRead in the read ahead thread.
-    if (!has_lock)
-        rwlock.lockForWrite();
-
-    StartReads();
-
-    if (writemode)
-    {
-        ret = WriterSeek(pos, whence, true);
-        if (!has_lock)
-            rwlock.unlock();
-        return ret;
-    }
-
-    poslock.lockForWrite();
-
-    // Optimize no-op seeks
-    if (readaheadrunning &&
-        ((whence == SEEK_SET && pos == readpos) ||
-         (whence == SEEK_CUR && pos == 0)))
-    {
-        ret = readpos;
-
-        poslock.unlock();
-        if (!has_lock)
-            rwlock.unlock();
-
-        return ret;
-    }
-
-    // only valid for SEEK_SET & SEEK_CUR
-    long long new_pos = (SEEK_SET==whence) ? pos : readpos + pos;
-
-#if 1
-    // Optimize short seeks where the data for
-    // them is in our ringbuffer already.
-    if (readaheadrunning &&
-        (SEEK_SET==whence || SEEK_CUR==whence))
-    {
-        rbrlock.lockForWrite();
-        rbwlock.lockForRead();
-        VERBOSE(VB_FILE, LOC +
-                QString("Seek(): rbrpos: %1 rbwpos: %2"
-                        "\n\t\t\treadpos: %3 internalreadpos: %4")
-                .arg(rbrpos).arg(rbwpos)
-                .arg(readpos).arg(internalreadpos));
-        bool used_opt = false;
-        if ((new_pos < readpos))
-        {
-            int min_safety = max(fill_min, readblocksize);
-            int free = ((rbwpos >= rbrpos) ?
-                        rbrpos + kBufferSize : rbrpos) - rbwpos;
-            int internal_backbuf =
-                (rbwpos >= rbrpos) ? rbrpos : rbrpos - rbwpos;
-            internal_backbuf = min(internal_backbuf, free - min_safety);
-            long long sba = readpos - new_pos;
-            VERBOSE(VB_FILE, LOC +
-                    QString("Seek(): internal_backbuf: %1 sba: %2")
-                    .arg(internal_backbuf).arg(sba));
-            if (internal_backbuf >= sba)
-            {
-                rbrpos = (rbrpos>=sba) ? rbrpos - sba :
-                    kBufferSize + rbrpos - sba;
-                used_opt = true;
-                VERBOSE(VB_FILE, LOC +
-                        QString("Seek(): OPT1 rbrpos: %1 rbwpos: %2"
-                                "\n\t\t\treadpos: %3 internalreadpos: %4")
-                        .arg(rbrpos).arg(rbwpos)
-                        .arg(new_pos).arg(internalreadpos));
-            }
-        }
-        else if ((new_pos >= readpos) && (new_pos <= internalreadpos))
-        {
-            rbrpos = (rbrpos + (new_pos - readpos)) % kBufferSize;
-            used_opt = true;
-            VERBOSE(VB_FILE, LOC +
-                    QString("Seek(): OPT2 rbrpos: %1 sba: %2")
-                    .arg(rbrpos).arg(readpos - new_pos));
-        }
-        rbwlock.unlock();
-        rbrlock.unlock();
-
-        if (used_opt)
-        {
-            if (ignorereadpos >= 0)
-            {
-                // seek should always succeed since we were at this position
-                int ret;
-                if (remotefile)
-                    ret = remotefile->Seek(internalreadpos, SEEK_SET);
-                else
-                {
-                    ret = lseek64(fd2, internalreadpos, SEEK_SET);
-                    posix_fadvise(fd2, 0,
-                                  internalreadpos, POSIX_FADV_DONTNEED);
-                    posix_fadvise(fd2, internalreadpos,
-                                  1*1024*1024, POSIX_FADV_WILLNEED);
-                }
-                VERBOSE(VB_FILE, LOC +
-                        QString("Seek to %1 from ignore pos %2 returned %3")
-                        .arg(internalreadpos).arg(ignorereadpos).arg(ret));
-                ignorereadpos = -1;
-            }
-            readpos = new_pos;
-            poslock.unlock();
-            generalWait.wakeAll();
-            ateof = false;
-            readsallowed = false;
-            if (!has_lock)
-                rwlock.unlock();
-            return new_pos;
-        }
-    }
-#endif
-
-#if 1
-    // This optimizes the seek end-250000, read, seek 0, read portion 
-    // of the pattern ffmpeg performs at the start of playback to
-    // determine the pts.
-    // If the seek is a SEEK_END or is a seek where the position
-    // changes over 100 MB we check the file size and if the
-    // destination point is within 300000 bytes of the end of
-    // the file we enter a special mode where the read ahead
-    // buffer stops reading data and all reads are made directly
-    // until another seek is performed. The point of all this is
-    // to avoid flushing out the buffer that still contains all
-    // the data the final seek 0, read will need just to read the
-    // last 250000 bytes. A further optimization would be to buffer
-    // the 250000 byte read, which is currently performed in 32KB
-    // blocks (inefficient with RemoteFile).
-    if ((remotefile || fd2 >= 0) && (ignorereadpos < 0))
-    {
-        long long off_end = 0xDEADBEEF;
-        if (SEEK_END == whence)
-        {
-            off_end = pos;
-            if (remotefile)
-            {
-                new_pos = remotefile->GetFileSize() - off_end;
-            }
-            else
-            {
-                QFileInfo fi(filename);
-                new_pos = fi.size() - off_end;
-            }
-        }
-        else
-        {
-            if (remotefile)
-            {
-                off_end = remotefile->GetFileSize() - new_pos;
-            }
-            else
-            {
-                QFileInfo fi(filename);
-                off_end = fi.size() - new_pos;
-            }
-        }
-
-        if (off_end != 0xDEADBEEF)
-        {
-            VERBOSE(VB_FILE, LOC +
-                    QString("Seek(): Offset from end: %1").arg(off_end));
-        }
-
-        // See #9150, mkv files and at least one mp4 file have trouble with
-        // this optimization for an unknown reason, but if we only do it on
-        // seeks to END-250000, then it is only used for MPEG-2 files where
-        // we know this can speed up seeking and no problems have been
-        // reported.
-        if (off_end == 250000)
-        {
-            VERBOSE(VB_FILE, LOC +
-                    QString("Seek(): offset from end: %1").arg(off_end) +
-                    "\n\t\t\t -- ignoring read ahead thread until next seek.");
-
-            ignorereadpos = new_pos;
-            errno = EINVAL;
-            long long ret;
-            if (remotefile)
-                ret = remotefile->Seek(ignorereadpos, SEEK_SET);
-            else
-            {
-                ret = lseek64(fd2, ignorereadpos, SEEK_SET);
-                posix_fadvise(fd2, ignorereadpos, 250000, POSIX_FADV_WILLNEED);
-            }
-
-            if (ret < 0)
-            {
-                int tmp_eno = errno;
-                QString cmd = QString("Seek(%1, SEEK_SET) ign ")
-                    .arg(ignorereadpos);
-
-                ignorereadpos = -1;
-
-                VERBOSE(VB_IMPORTANT, LOC_ERR + cmd + " Failed" + ENO);
-
-                // try to return to former position..
-                if (remotefile)
-                    ret = remotefile->Seek(internalreadpos, SEEK_SET);
-                else
-                    ret = lseek64(fd2, internalreadpos, SEEK_SET);
-                if (ret < 0)
-                {
-                    QString cmd = QString("Seek(%1, SEEK_SET) int ")
-                        .arg(internalreadpos);
-                    VERBOSE(VB_IMPORTANT, LOC_ERR + cmd + " Failed" + ENO);
-                }
-                else
-                {
-                    QString cmd = QString("Seek(%1, %2) int ")
-                        .arg(internalreadpos)
-                        .arg((SEEK_SET == whence) ? "SEEK_SET" :
-                             ((SEEK_CUR == whence) ?"SEEK_CUR" : "SEEK_END"));
-                    VERBOSE(VB_IMPORTANT, LOC_ERR + cmd + " succeeded");
-                }
-                ret = -1;
-                errno = tmp_eno;
-            }
-            else
-            {
-                ateof = false;
-                readsallowed = false;
-            }
-
-            poslock.unlock();
-
-            generalWait.wakeAll();
-
-            if (!has_lock)
-                rwlock.unlock();
-
-            return ret;
-        }
-    }
-#endif
-
-    // Here we perform a normal seek. When successful we
-    // need to call ResetReadAhead(). A reset means we will
-    // need to refill the buffer, which takes some time.
-    if (remotefile)
-    {
-        ret = remotefile->Seek(pos, whence, readpos);
-        if (ret<0)
-            errno = EINVAL;
-    }
-#ifdef USING_FRONTEND
-    else if ((dvdPriv || bdPriv) && (SEEK_END == whence))
-    {
-        errno = EINVAL;
-        ret = -1;
-    }
-    else if (dvdPriv)
-    {
-        dvdPriv->NormalSeek(new_pos);
-        ret = new_pos;
-    }
-    else if (bdPriv)
-    {
-        bdPriv->Seek(new_pos);
-        ret = new_pos;
-    }
-#endif // USING_FRONTEND
-    else
-    {
-        ret = lseek64(fd2, pos, whence);
-        if (ret >= 0)
-        {
-            posix_fadvise(fd2, 0,   ret,         POSIX_FADV_DONTNEED);
-            posix_fadvise(fd2, ret, 1*1024*1024, POSIX_FADV_WILLNEED);
-        }
-    }
-
-    if (ret >= 0)
-    {
-        readpos = ret;
-        
-        ignorereadpos = -1;
-
-        if (readaheadrunning)
-            ResetReadAhead(readpos);
-
-        readAdjust = 0;
-    }
-    else
-    {
-        QString cmd = QString("Seek(%1, %2)").arg(pos)
-            .arg((SEEK_SET == whence) ? "SEEK_SET" :
-                 ((SEEK_CUR == whence) ?"SEEK_CUR" : "SEEK_END"));
-        VERBOSE(VB_IMPORTANT, LOC_ERR + cmd + " Failed" + ENO);
-    }
-
-    poslock.unlock();
-
-    generalWait.wakeAll();
-
-    if (!has_lock)
-        rwlock.unlock();
-
-    return ret;
-}
-
 /** \brief Calls ThreadedFileWriter::Seek(long long,int).
  */
 long long RingBuffer::WriterSeek(long long pos, int whence, bool has_lock)
@@ -2177,23 +1351,11 @@
     rwlock.unlock();
 }
 
-// This appears to allow direct access to the DVD/BD device
-// when called with false (the default value), and enable
-// the ring buffer when called with true. But I'm not entirely
-// certain. -- dtk 2010-08-26
-void RingBuffer::SetStreamOnly(bool stream)
-{
-    rwlock.lockForWrite();
-    streamOnly = stream;
-    rwlock.unlock();
-}
-
 /// Returns name of file used by this RingBuffer
 QString RingBuffer::GetFilename(void) const
 {
     rwlock.lockForRead();
     QString tmp = filename;
-    tmp.detach();
     rwlock.unlock();
     return tmp;
 }
@@ -2202,30 +1364,10 @@
 {
     rwlock.lockForRead();
     QString tmp = subtitlefilename;
-    tmp.detach();
     rwlock.unlock();
     return tmp;
 }
 
-/** \fn RingBuffer::GetReadPosition(void) const
- *  \brief Returns how far into the file we have read.
- */
-long long RingBuffer::GetReadPosition(void) const
-{
-    rwlock.lockForRead();
-    poslock.lockForRead();
-    long long ret = readpos;
-#ifdef USING_FRONTEND
-    if (dvdPriv)
-        ret = dvdPriv->GetReadPosition();
-    else if (bdPriv)
-        ret = bdPriv->GetReadPosition();
-#endif // USING_FRONTEND
-    poslock.unlock();
-    rwlock.unlock();
-    return ret;
-}
-
 /** \fn RingBuffer::GetWritePosition(void) const
  *  \brief Returns how far into a ThreadedFileWriter file we have written.
  */
@@ -2237,22 +1379,6 @@
     return ret;
 }
 
-/** \fn RingBuffer::GetRealFileSize(void) const
- *  \brief Returns the size of the file we are reading/writing,
- *         or -1 if the query fails.
- */
-long long RingBuffer::GetRealFileSize(void) const
-{
-    rwlock.lockForRead();
-    long long ret = -1;
-    if (remotefile)
-        ret = remotefile->GetFileSize();
-    else
-        ret = QFileInfo(filename).size();
-    rwlock.unlock();
-    return ret;
-}
-
 /** \fn RingBuffer::LiveMode(void) const
  *  \brief Returns true if this RingBuffer has been assigned a LiveTVChain.
  *  \sa SetLiveMode(LiveTVChain*)
@@ -2284,53 +1410,24 @@
     rwlock.unlock();
 }
 
-/** \brief Returns true if this is a DVD backed RingBuffer.
- *
- * NOTE: This is not locked because ReadDirect calls
- * DVD safe_read which sleeps with a write lock on
- * rwlock in the DVDNAV_WAIT condition.
- *
- * Due to the lack of locking is only safe to call once OpenFile()
- * has completed.
- */
-bool RingBuffer::IsDVD(void) const
+const DVDRingBuffer *RingBuffer::DVD(void) const
 {
-    //rwlock.lockForRead();
-    bool ret = dvdPriv;
-    //rwlock.unlock();
-    return ret;
+    return dynamic_cast<const DVDRingBuffer*>(this);
 }
 
-/** \brief Returns true if this is a DVD backed RingBuffer.
- *
- * NOTE: This is not locked because ReadDirect calls
- * DVD safe_read which sleeps with a write lock on
- * rwlock in the DVDNAV_WAIT condition.
- *
- * Due to the lack of locking is only safe to call once OpenFile()
- * has completed.
- */
-bool RingBuffer::InDiscMenuOrStillFrame(void)
+const BDRingBuffer  *RingBuffer::BD(void) const
 {
-    //rwlock.lockForRead();
-    bool ret = false;
-#ifdef USING_FRONTEND
-    if (dvdPriv)
-        ret = (dvdPriv->IsInMenu() || dvdPriv->InStillFrame());
-    else if (bdPriv)
-        ret = (bdPriv->IsInMenu() || bdPriv->InStillFrame());
-#endif // USING_FRONTEND
-    //rwlock.unlock();
-    return ret;
+    return dynamic_cast<const BDRingBuffer*>(this);
 }
 
-/// Returns true if this is a Blu-ray backed RingBuffer.
-bool RingBuffer::IsBD(void) const
+DVDRingBuffer *RingBuffer::DVD(void)
 {
-    //rwlock.lockForRead();
-    bool ret = bdPriv;
-    //rwlock.unlock();
-    return ret;
+    return dynamic_cast<DVDRingBuffer*>(this);
 }
 
+BDRingBuffer  *RingBuffer::BD(void)
+{
+    return dynamic_cast<BDRingBuffer*>(this);
+}
+
 /* vim: set expandtab tabstop=4 shiftwidth=4: */
Index: libs/libmythtv/DVDRingBuffer.h
===================================================================
--- libs/libmythtv/DVDRingBuffer.h	(revision 27380)
+++ libs/libmythtv/DVDRingBuffer.h	(working copy)
@@ -10,7 +10,9 @@
 #include <QMutex>
 #include <QRect>
 
+#include "RingBuffer.h"
 #include "util.h"
+
 extern "C" {
 #include "libavcodec/avcodec.h"
 }
@@ -25,11 +27,11 @@
 
 class MythDVDPlayer;
 
-class MPUBLIC DVDRingBufferPriv
+class MPUBLIC DVDRingBuffer : public RingBuffer
 {
   public:
-    DVDRingBufferPriv();
-    virtual ~DVDRingBufferPriv();
+    DVDRingBuffer(const QString &lfilename);
+    virtual ~DVDRingBuffer();
 
     // gets
     int  GetTitle(void) const { return m_title;        }
@@ -38,7 +40,7 @@
     int  GetCurrentAngle(void) const { return m_currentAngle;           };
     int  GetNumAngles(void)          { return m_currentTitleAngleCount; };
     bool IsOpen(void)   const { return m_dvdnav;       }
-    long long GetReadPosition(void);
+    virtual long long GetReadPosition(void) const;
     long long GetTotalReadPosition(void) { return m_titleLength; }
     void GetDescForPos(QString &desc);
     void GetPartAndTitle(int &_part, int &_title) const
@@ -48,20 +50,22 @@
     uint GetCellStart(void);
     bool PGCLengthChanged(void);
     bool CellChanged(void);
-    bool InStillFrame(void) const { return m_still > 0; }
-    bool NeedsStillFrame(void) { return InStillFrame() || NewSequence(); }
+    bool IsInStillFrame(void) const { return m_still > 0; }
+    bool NeedsStillFrame(void) { return IsInStillFrame() || NewSequence(); }
     bool NewSequence(bool new_sequence = false);
     bool AudioStreamsChanged(void) const { return m_audioStreamsChanged; }
     bool IsWaiting(void) const { return m_dvdWaiting; }
     int  NumPartsInTitle(void) const { return m_titleParts; }
     void GetMenuSPUPkt(uint8_t *buf, int len, int stream_id);
+    virtual bool IsInDiscMenuOrStillFrame(void) const
+        { return IsInMenu() || IsInStillFrame(); } // RingBuffer
 
     // Public menu/button stuff
     AVSubtitle *GetMenuSubtitle(uint &version);
     int         NumMenuButtons(void) const;
     QRect       GetButtonCoords(void);
     void        ReleaseMenuButton(void);
-    bool        IsInMenu(void) { return m_inMenu; }
+    bool        IsInMenu(void) const { return m_inMenu; }
     void        ActivateButton(void);
     void        MoveButtonLeft(void);
     void        MoveButtonRight(void);
@@ -85,14 +89,16 @@
                                         (m_titleParts == 1)); }
 
     // commands
-    bool OpenFile(const QString &filename);
+    virtual bool OpenFile(const QString &lfilename,
+                          uint retry_ms = kDefaultOpenTimeout); // RingBuffer
     void PlayTitleAndPart(int _title, int _part)
         { dvdnav_part_play(m_dvdnav, _title, _part); }
     bool StartFromBeginning(void);
     void CloseDVD(void);
     bool nextTrack(void);
     void prevTrack(void);
-    int  safe_read(void *data, unsigned sz);
+    virtual int safe_read(void *data, uint sz); // RingBuffer
+    virtual long long Seek(long long pos, int whence, bool has_lock);
     long long NormalSeek(long long time);
     void SkipStillFrame(void);
     void WaitSkip(void);
@@ -118,7 +124,6 @@
     dvdnav_t      *m_dvdnav;
     unsigned char  m_dvdBlockWriteBuf[DVD_BLOCK_SIZE];
     unsigned char *m_dvdBlockReadBuf;
-    QString        m_dvdFilename;
     int            m_dvdBlockRPos;
     int            m_dvdBlockWPos;
     long long      m_pgLength;
Index: libs/libmythtv/tv_play.cpp
===================================================================
--- libs/libmythtv/tv_play.cpp	(revision 27380)
+++ libs/libmythtv/tv_play.cpp	(working copy)
@@ -2038,8 +2038,11 @@
                                           "cardtype(%2)")
                     .arg(playbackURL).arg(ctx->tvchain->GetCardType(-1)));
 
-            ctx->SetRingBuffer(new RingBuffer(playbackURL, false, true,
-                               opennow ? RingBuffer::kLiveTVOpenTimeout : -1));
+            ctx->SetRingBuffer(
+                RingBuffer::Create(
+                    playbackURL, false, true,
+                    opennow ? RingBuffer::kLiveTVOpenTimeout : -1));
+
             ctx->buffer->SetLiveMode(ctx->tvchain);
         }
 
@@ -2113,7 +2116,7 @@
         }
         ctx->UnlockPlayingInfo(__FILE__, __LINE__);
 
-        ctx->SetRingBuffer(new RingBuffer(playbackURL, false));
+        ctx->SetRingBuffer(RingBuffer::Create(playbackURL, false));
 
         if (ctx->buffer && ctx->buffer->IsOpen())
         {
@@ -2239,7 +2242,7 @@
             ITVRestart(ctx, false);
         }
 
-        if (ctx->buffer && ctx->buffer->isDVD())
+        if (ctx->buffer && ctx->buffer->IsDVD())
         {
             UpdateLCD();
         }
@@ -2367,7 +2370,7 @@
 
     SetActive(mctx, 0, false);
 
-    if (ctx->buffer && ctx->buffer->isDVD())
+    if (ctx->buffer && ctx->buffer->IsDVD())
     {
         VERBOSE(VB_PLAYBACK,LOC + " StopStuff() -- "
                 "get dvd player out of still frame or wait status");
@@ -3062,10 +3065,10 @@
         if (StateIsLiveTV(GetState(actx)))
             ShowLCDChannelInfo(actx);
 
-        if (actx->buffer && actx->buffer->isDVD())
+        if (actx->buffer && actx->buffer->IsDVD())
         {
             ShowLCDDVDInfo(actx);
-            showProgress = !actx->buffer->InDiscMenuOrStillFrame();
+            showProgress = !actx->buffer->IsInDiscMenuOrStillFrame();
         }
 
         if (showProgress)
@@ -3735,18 +3738,17 @@
 
     handled = false;
 
-    bool isDVD = actx->buffer && actx->buffer->isDVD();
-    bool isDVDStill = isDVD && actx->buffer->InDiscMenuOrStillFrame();
-    bool isBD = actx->buffer && actx->buffer->isBD();
-    bool isBDStill = isBD && actx->buffer->InDiscMenuOrStillFrame();
+    bool isDVD = actx->buffer && actx->buffer->IsDVD();
+    bool isMenuOrStill = actx->buffer->IsInDiscMenuOrStillFrame();
 
     handled = handled || BrowseHandleAction(actx, actions);
     handled = handled || ManualZoomHandleAction(actx, actions);
     handled = handled || PictureAttributeHandleAction(actx, actions);
     handled = handled || TimeStretchHandleAction(actx, actions);
     handled = handled || AudioSyncHandleAction(actx, actions);
-    handled = handled || DiscMenuHandleAction(actx, actions, isDVD, isDVDStill, isBD);
-    handled = handled || ActiveHandleAction(actx, actions, isDVD, isDVDStill);
+    handled = handled || DiscMenuHandleAction(actx, actions);
+    handled = handled || ActiveHandleAction(
+        actx, actions, isDVD, isMenuOrStill);
     handled = handled || ToggleHandleAction(actx, actions, isDVD);
     handled = handled || PxPHandleAction(actx, actions);
     handled = handled || FFRewHandleAction(actx, actions);
@@ -3972,16 +3974,15 @@
     return handled;
 }
 
-bool TV::DiscMenuHandleAction(PlayerContext *ctx,
-                             const QStringList &actions,
-                             bool isDVD, bool isDVDStill,
-                             bool isBD)
+bool TV::DiscMenuHandleAction(PlayerContext *ctx, const QStringList &actions)
 {
     bool handled = false;
+    DVDRingBuffer *dvdrb = ctx->buffer->DVD();
+    BDRingBuffer  *bdrb  = ctx->buffer->BD();
 
-    if (isDVD)
+    if (dvdrb)
     {
-        int nb_buttons = ctx->buffer->DVD()->NumMenuButtons();
+        int nb_buttons = dvdrb->NumMenuButtons();
         if (nb_buttons == 0)
             return false;
 
@@ -3989,33 +3990,33 @@
         if (has_action("UP", actions) ||
             has_action("CHANNELUP", actions))
         {
-            ctx->buffer->DVD()->MoveButtonUp();
+            dvdrb->MoveButtonUp();
         }
         else if (has_action("DOWN", actions) ||
                  has_action("CHANNELDOWN", actions))
         {
-            ctx->buffer->DVD()->MoveButtonDown();
+            dvdrb->MoveButtonDown();
         }
         else if (has_action("LEFT", actions) ||
                  has_action("SEEKRWND", actions))
         {
-            ctx->buffer->DVD()->MoveButtonLeft();
+            dvdrb->MoveButtonLeft();
         }
         else if (has_action("RIGHT", actions) ||
                  has_action("SEEKFFWD", actions))
         {
-            ctx->buffer->DVD()->MoveButtonRight();
+            dvdrb->MoveButtonRight();
         }
         else if (has_action("SELECT", actions))
         {
             ctx->LockDeletePlayer(__FILE__, __LINE__);
-            ctx->buffer->DVD()->ActivateButton();
+            dvdrb->ActivateButton();
             ctx->UnlockDeletePlayer(__FILE__, __LINE__);
         }
         else
             handled = false;
     }
-    if (isBD)
+    else if (bdrb)
     {
         int64_t pts = 0;
         VideoOutput *output = ctx->player->getVideoOutput();
@@ -4033,70 +4034,70 @@
         if (has_action("UP", actions) ||
             has_action("CHANNELUP", actions))
         {
-            ctx->buffer->BD()->PressButton(BD_VK_UP, pts);
+            bdrb->PressButton(BD_VK_UP, pts);
         }
         else if (has_action("DOWN", actions) ||
                  has_action("CHANNELDOWN", actions))
         {
-            ctx->buffer->BD()->PressButton(BD_VK_DOWN, pts);
+            bdrb->PressButton(BD_VK_DOWN, pts);
         }
         else if (has_action("LEFT", actions) ||
                  has_action("SEEKRWND", actions))
         {
-            ctx->buffer->BD()->PressButton(BD_VK_LEFT, pts);
+            bdrb->PressButton(BD_VK_LEFT, pts);
         }
         else if (has_action("RIGHT", actions) ||
                  has_action("SEEKFFWD", actions))
         {
-            ctx->buffer->BD()->PressButton(BD_VK_RIGHT, pts);
+            bdrb->PressButton(BD_VK_RIGHT, pts);
         }
         else if (has_action("MENUTEXT", actions))
         {
-            ctx->buffer->BD()->PressButton(BD_VK_POPUP, pts);
+            bdrb->PressButton(BD_VK_POPUP, pts);
         }
         else if (has_action("0", actions))
         {
-            ctx->buffer->BD()->PressButton(BD_VK_0, pts);
+            bdrb->PressButton(BD_VK_0, pts);
         }
         else if (has_action("1", actions))
         {
-            ctx->buffer->BD()->PressButton(BD_VK_1, pts);
+            bdrb->PressButton(BD_VK_1, pts);
         }
         else if (has_action("2", actions))
         {
-            ctx->buffer->BD()->PressButton(BD_VK_2, pts);
+            bdrb->PressButton(BD_VK_2, pts);
         }
         else if (has_action("3", actions))
         {
-            ctx->buffer->BD()->PressButton(BD_VK_3, pts);
+            bdrb->PressButton(BD_VK_3, pts);
         }
         else if (has_action("4", actions))
         {
-            ctx->buffer->BD()->PressButton(BD_VK_4, pts);
+            bdrb->PressButton(BD_VK_4, pts);
         }
         else if (has_action("5", actions))
         {
-            ctx->buffer->BD()->PressButton(BD_VK_5, pts);
+            bdrb->PressButton(BD_VK_5, pts);
         }
         else if (has_action("6", actions))
         {
-            ctx->buffer->BD()->PressButton(BD_VK_6, pts);
+            bdrb->PressButton(BD_VK_6, pts);
         }
         else if (has_action("7", actions))
         {
-            ctx->buffer->BD()->PressButton(BD_VK_7, pts);
+            bdrb->PressButton(BD_VK_7, pts);
         }
         else if (has_action("8", actions))
         {
-            ctx->buffer->BD()->PressButton(BD_VK_8, pts);
+            bdrb->PressButton(BD_VK_8, pts);
         }
         else if (has_action("9", actions))
         {
-            ctx->buffer->BD()->PressButton(BD_VK_9, pts);
+            bdrb->PressButton(BD_VK_9, pts);
         }
         else if (has_action("SELECT", actions))
         {
-            ctx->buffer->BD()->PressButton(BD_VK_ENTER, pts);
+            bdrb->PressButton(BD_VK_ENTER, pts);
         }
         else
             handled = false;
@@ -4814,7 +4815,7 @@
     }
     else if (tokens.size() >= 3 && tokens[1] == "SEEK" && ctx->HasPlayer())
     {
-        if (ctx->buffer && ctx->buffer->InDiscMenuOrStillFrame())
+        if (ctx->buffer && ctx->buffer->IsInDiscMenuOrStillFrame())
             return;
 
         ctx->LockDeletePlayer(__FILE__, __LINE__);
@@ -4929,7 +4930,7 @@
             }
             else
             {
-                if (ctx->buffer->isDVD())
+                if (ctx->buffer->IsDVD())
                     infoStr = "DVD";
                 else if (ctx->playingInfo->IsRecording())
                     infoStr = "Recorded";
@@ -5753,7 +5754,7 @@
     if (!ctx)
         return 0.0f;
 
-    if (ctx->buffer && ctx->buffer->InDiscMenuOrStillFrame())
+    if (ctx->buffer && ctx->buffer->IsInDiscMenuOrStillFrame())
         return 0.0f;
 
     ctx->ff_rew_speed = 0;
@@ -5792,7 +5793,7 @@
     if (!ctx || !ctx->HasPlayer())
         return;
 
-    if (ctx->buffer && ctx->buffer->InDiscMenuOrStillFrame())
+    if (ctx->buffer && ctx->buffer->IsInDiscMenuOrStillFrame())
         return;
 
     if (ctx->paused)
@@ -6585,8 +6586,10 @@
             ctx->LockPlayingInfo(__FILE__, __LINE__);
             QString playbackURL = ctx->playingInfo->GetPlaybackURL(true);
             bool opennow = (ctx->tvchain->GetCardType(-1) != "DUMMY");
-            ctx->SetRingBuffer(new RingBuffer(playbackURL, false, true,
-                               opennow ? RingBuffer::kLiveTVOpenTimeout : -1));
+            ctx->SetRingBuffer(
+                RingBuffer::Create(
+                    playbackURL, false, true,
+                    opennow ? RingBuffer::kLiveTVOpenTimeout : -1));
 
             ctx->tvchain->SetProgram(*ctx->playingInfo);
             ctx->buffer->SetLiveMode(ctx->tvchain);
@@ -7647,12 +7650,12 @@
 {
     class LCD * lcd = LCD::Get();
 
-    if (!lcd || !ctx->buffer || !ctx->buffer->isDVD())
+    if (!lcd || !ctx->buffer || !ctx->buffer->IsDVD())
     {
         return;
     }
 
-    DVDRingBufferPriv *dvd = ctx->buffer->DVD();
+    DVDRingBuffer *dvd = ctx->buffer->DVD();
     QString dvdName, dvdSerial;
     QString mainStatus, subStatus;
 
@@ -7660,10 +7663,15 @@
     {
         dvdName = tr("DVD");
     }
+
     if (dvd->IsInMenu())
+    {
         mainStatus = tr("Menu");
-    else if (dvd->InStillFrame())
+    }
+    else if (dvd->IsInStillFrame())
+    {
         mainStatus = tr("Still Frame");
+    }
     else
     {
         QString timeMins, timeHrsMin;
@@ -11480,14 +11488,15 @@
 */
 void TV::DVDJumpBack(PlayerContext *ctx)
 {
-    if (!ctx->HasPlayer() || !ctx->buffer || !ctx->buffer->isDVD())
+    DVDRingBuffer *dvdrb = dynamic_cast<DVDRingBuffer*>(ctx->buffer);
+    if (!ctx->HasPlayer() || !dvdrb)
         return;
 
-    if (ctx->buffer->InDiscMenuOrStillFrame())
+    if (ctx->buffer->IsInDiscMenuOrStillFrame())
     {
         UpdateOSDSeekMessage(ctx, tr("Skip Back Not Allowed"), kOSDTimeout_Med);
     }
-    else if (!ctx->buffer->DVD()->StartOfTitle())
+    else if (!dvdrb->StartOfTitle())
     {
         ctx->LockDeletePlayer(__FILE__, __LINE__);
         if (ctx->player)
@@ -11497,8 +11506,8 @@
     }
     else
     {
-        uint titleLength = ctx->buffer->DVD()->GetTotalTimeOfTitle();
-        uint chapterLength = ctx->buffer->DVD()->GetChapterLength();
+        uint titleLength = dvdrb->GetTotalTimeOfTitle();
+        uint chapterLength = dvdrb->GetChapterLength();
         if ((titleLength == chapterLength) && chapterLength > 300)
         {
             DoSeek(ctx, -ctx->jumptime * 60, tr("Jump Back"));
@@ -11520,17 +11529,18 @@
  */
 void TV::DVDJumpForward(PlayerContext *ctx)
 {
-    if (!ctx->HasPlayer() || !ctx->buffer || !ctx->buffer->isDVD())
+    DVDRingBuffer *dvdrb = dynamic_cast<DVDRingBuffer*>(ctx->buffer);
+    if (!ctx->HasPlayer() || !dvdrb)
         return;
 
-    bool in_still = ctx->buffer->DVD()->InStillFrame();
-    bool in_menu  = ctx->buffer->DVD()->IsInMenu();
-    if (in_still && !ctx->buffer->DVD()->NumMenuButtons())
+    bool in_still = dvdrb->IsInStillFrame();
+    bool in_menu  = dvdrb->IsInMenu();
+    if (in_still && !dvdrb->NumMenuButtons())
     {
-        ctx->buffer->DVD()->SkipStillFrame();
+        dvdrb->SkipStillFrame();
         UpdateOSDSeekMessage(ctx, tr("Skip Still Frame"), kOSDTimeout_Med);
     }
-    else if (!ctx->buffer->DVD()->EndOfTitle() && !in_still && !in_menu)
+    else if (!dvdrb->EndOfTitle() && !in_still && !in_menu)
     {
         ctx->LockDeletePlayer(__FILE__, __LINE__);
         if (ctx->player)
@@ -11541,9 +11551,9 @@
     }
     else if (!in_still && !in_menu)
     {
-        uint titleLength = ctx->buffer->DVD()->GetTotalTimeOfTitle();
-        uint chapterLength = ctx->buffer->DVD()->GetChapterLength();
-        uint currentTime = ctx->buffer->DVD()->GetCurrentTime();
+        uint titleLength = dvdrb->GetTotalTimeOfTitle();
+        uint chapterLength = dvdrb->GetChapterLength();
+        uint currentTime = dvdrb->GetCurrentTime();
         if ((titleLength == chapterLength) &&
              (currentTime < (chapterLength - (ctx->jumptime * 60))) &&
              chapterLength > 300)
@@ -11567,7 +11577,7 @@
  */
 bool TV::IsBookmarkAllowed(const PlayerContext *ctx) const
 {
-    bool isDVD = ctx->buffer && ctx->buffer->isDVD();
+    bool isDVD = ctx->buffer && ctx->buffer->IsDVD();
 
     ctx->LockPlayingInfo(__FILE__, __LINE__);
 
@@ -11624,7 +11634,7 @@
 
     if (StateIsLiveTV(GetState(ctx)))
         videotype = tr("Live TV");
-    else if (ctx->buffer->isDVD())
+    else if (ctx->buffer->IsDVD())
         videotype = tr("this DVD");
 
     ctx->LockPlayingInfo(__FILE__, __LINE__);
Index: libs/libmythtv/BDRingBuffer.h
===================================================================
--- libs/libmythtv/BDRingBuffer.h	(revision 27380)
+++ libs/libmythtv/BDRingBuffer.h	(working copy)
@@ -8,6 +8,7 @@
 #include "libmythbluray/bluray.h"
 #include "libmythbluray/keys.h"
 
+#include "RingBuffer.h"
 #include "util.h"
 
 /** \class BDRingBufferPriv
@@ -18,11 +19,11 @@
 
 class NuppelVideoPlayer;
 
-class MPUBLIC BDRingBufferPriv
+class MPUBLIC BDRingBuffer : public RingBuffer
 {
   public:
-    BDRingBufferPriv();
-    virtual ~BDRingBufferPriv();
+    BDRingBuffer(const QString &lfilename);
+    virtual ~BDRingBuffer();
 
     uint32_t GetNumTitles(void) const { return m_numTitles; }
     int      GetCurrentTitle(void) const;
@@ -33,7 +34,7 @@
     // Get The total duration of the current title in 90Khz ticks.
     uint64_t GetTotalTimeOfTitle(void) const { return (m_currentTitleLength / 90000); }
     uint64_t GetCurrentTime(void) { return (m_currentTime / 90000); }
-    uint64_t GetReadPosition(void);
+    virtual long long GetReadPosition(void) const; // RingBuffer
     uint64_t GetTotalReadPosition(void);
     uint32_t GetNumChapters(void);
     uint64_t GetNumAngles(void) { return m_currentTitleAngleCount; }
@@ -42,7 +43,9 @@
     bool IsOpen(void)        const { return bdnav; }
     bool IsHDMVNavigation(void) const { return m_is_hdmv_navigation; }
     bool IsInMenu(void) const { return m_inMenu; }
-    bool InStillFrame(void) { return m_still > 0; }
+    bool IsInStillFrame(void) const { return m_still > 0; }
+    virtual bool IsInDiscMenuOrStillFrame(void) const
+        { return IsInMenu() || IsInStillFrame(); } // RingBuffer
     bool TitleChanged(void);
 
     void GetDescForPos(QString &desc) const;
@@ -52,14 +55,16 @@
     int GetSubtitleLanguage(uint streamID);
 
     // commands
-    bool OpenFile(const QString &filename);
+    virtual bool OpenFile(const QString &filename,
+                          uint retry_ms = kDefaultOpenTimeout); // RingBuffer
     void close(void);
 
     bool GoToMenu(const QString str);
     bool SwitchTitle(uint title);
     bool SwitchAngle(uint angle);
 
-    int  safe_read(void *data, unsigned sz);
+    virtual int safe_read(void *data, uint sz); // RingBuffer
+    virtual long long Seek(long long pos, int whence, bool has_lock);
     uint64_t Seek(uint64_t pos);
 
     bool HandleBDEvents(void);
@@ -100,7 +105,7 @@
 
   public:
     uint8_t            m_still;
-    bool               m_inMenu;
+    volatile bool      m_inMenu;
 
 };
 #endif
Index: libs/libmythtv/RingBuffer.h
===================================================================
--- libs/libmythtv/RingBuffer.h	(revision 27380)
+++ libs/libmythtv/RingBuffer.h	(working copy)
@@ -17,25 +17,25 @@
 
 #include "mythexp.h"
 
-class RemoteFile;
 class ThreadedFileWriter;
-class DVDRingBufferPriv;
-class BDRingBufferPriv;
+class DVDRingBuffer;
+class BDRingBuffer;
 class LiveTVChain;
+class RemoteFile;
 
 class MPUBLIC RingBuffer : protected QThread
 {
   public:
-    RingBuffer(const QString &lfilename, bool write,
-               bool usereadahead = true,
-               int timeout_ms = kDefaultOpenTimeout);
-   ~RingBuffer();
+    static RingBuffer *Create(const QString &lfilename, bool write,
+                              bool usereadahead = true,
+                              int timeout_ms = kDefaultOpenTimeout,
+                              bool stream_only = false);
+    ~RingBuffer();
 
     // Sets
     void SetWriteBufferSize(int newSize);
     void SetWriteBufferMinWriteSize(int newMinSize);
     void SetOldFile(bool is_old);
-    void SetStreamOnly(bool stream);
     void UpdateRawBitrate(uint rawbitrate);
     void UpdatePlaySpeed(float playspeed);
 
@@ -46,15 +46,37 @@
     /// \sa StartReads(void), StopReads(void)
     bool      GetStopReads(void)     const { return stopreads; }
     bool      isPaused(void)         const;
-    long long GetReadPosition(void)  const;
+    /// \brief Returns how far into the file we have read.
+    virtual long long GetReadPosition(void)  const = 0;
     long long GetWritePosition(void) const;
-    long long GetRealFileSize(void)  const;
-    bool      IsOpen(void)           const;
+    /// \brief Returns the size of the file we are reading/writing,
+    ///        or -1 if the query fails.
+    virtual long long GetRealFileSize(void)  const { return -1; }
     bool      IsNearEnd(double fps, uint vvf) const;
+    /// \brief Returns true if open for either reading or writing.
+    virtual bool IsOpen(void) const = 0;
 
+    bool IsDisc(void) const { return IsDVD() || IsBD(); }
+    bool IsDVD(void)  const { return DVD() != NULL;     }
+    bool IsBD(void)   const { return BD()  != NULL;     }
+    const DVDRingBuffer *DVD(void) const;
+    const BDRingBuffer  *BD(void)  const;
+    DVDRingBuffer *DVD(void);
+    BDRingBuffer  *BD(void);
+
+    virtual bool IsInDiscMenuOrStillFrame(void) const { return false; }
+
     // General Commands
-    void OpenFile(const QString &lfilename,
-                  uint retry_ms = kDefaultOpenTimeout);
+
+    /** \brief Opens a file for reading.
+     *
+     *  \param lfilename  Name of file to read
+     *  \param retry_ms   How many ms to retry reading the file
+     *                    after the first try before giving up.
+     */
+    virtual bool OpenFile(const QString &lfilename,
+                          uint retry_ms = kDefaultOpenTimeout) = 0;
+
     int  Read(void *buf, int count);
     int  Peek(void *buf, int count); // only works with readahead
 
@@ -62,8 +84,9 @@
                bool toAdjust      = false,
                bool resetInternal = false);
 
-    // Seeks
-    long long Seek(long long pos, int whence, bool has_lock = false);
+    /// \brief Seeks to a particular position in the file.
+    virtual long long Seek(
+        long long pos, int whence, bool has_lock = false) = 0;
 
     // Pause commands
     void Pause(void);
@@ -87,47 +110,21 @@
     void Sync(void);
     long long WriterSeek(long long pos, int whence, bool has_lock = false);
 
-    // DVDRingBuffer proxies
-    bool IsDVD(void) const;
-
-    // BDRingBuffer proxies
-    bool IsBD(void) const;
-
-    // Universal still frame/menu check
-    bool InDiscMenuOrStillFrame(void);
-
     long long SetAdjustFilesize(void);
 
     /// Calls SetOldFile(), do not use
     void SetTimeout(bool is_old) MDEPRECATED { SetOldFile(is_old); }
-    /// Calls IsDVD(), do not use
-    bool isDVD(void) const MDEPRECATED { return IsDVD(); }
-    /// Calls IsBD(), do not use
-    bool isBD(void) const MDEPRECATED { return IsBD(); }
-    /// Illicitly manipulating privates is ill advised!
-    /// DO NOT USE
-    DVDRingBufferPriv *DVD() MDEPRECATED
-    {
-        return dvdPriv;
-    }
-    /// Illicitly manipulating privates is ill advised!
-    /// DO NOT USE
-    BDRingBufferPriv *BD() MDEPRECATED
-    {
-        return bdPriv;
-    }
 
     static const int kDefaultOpenTimeout;
     static const int kLiveTVOpenTimeout;
 
   protected:
+    RingBuffer();
+
     void run(void); // QThread
     void CalcReadAheadThresh(void);
     bool PauseAndWait(void);
-    int safe_read_bd(void *data, uint sz);
-    int safe_read_dvd(void *data, uint sz);
-    int safe_read(int fd, void *data, uint sz);
-    int safe_read(RemoteFile *rf, void *data, uint sz);
+    virtual int safe_read(void *data, uint sz) = 0;
 
     int ReadPriv(void *buf, int count, bool peek);
     int ReadDirect(void *buf, int count, bool peek);
@@ -140,7 +137,7 @@
     void ResetReadAhead(long long newinternal);
     void KillReadAheadThread(void);
 
-  private:
+  protected:
     mutable QReadWriteLock poslock;
     long long readpos;            // protected by poslock
     long long writepos;           // protected by poslock
@@ -176,7 +173,6 @@
     bool      ateof;              // protected by rwlock
     bool      readsallowed;       // protected by rwlock
     bool      setswitchtonext;    // protected by rwlock
-    bool      streamOnly;         // protected by rwlock
     bool      ignorereadahead;    // protected by rwlock
     uint      rawbitrate;         // protected by rwlock
     float     playspeed;          // protected by rwlock
@@ -187,11 +183,6 @@
     int       numfailures;        // protected by rwlock (see note 1)
     bool      commserror;         // protected by rwlock
 
-    // We should really subclass for these two sets of functionality..
-    // current implementation is not thread-safe.
-    DVDRingBufferPriv *dvdPriv; // NOT protected by a lock
-    BDRingBufferPriv  *bdPriv;  // NOT protected by a lock
-
     bool oldfile;                 // protected by rwlock
 
     LiveTVChain *livetvchain;     // protected by rwlock
Index: libs/libmythtv/decoderbase.cpp
===================================================================
--- libs/libmythtv/decoderbase.cpp	(revision 27380)
+++ libs/libmythtv/decoderbase.cpp	(working copy)
@@ -106,7 +106,7 @@
     // Overwrites current positionmap with entire contents of database
     frm_pos_map_t posMap;
 
-    if (ringBuffer->isDVD())
+    if (ringBuffer->IsDVD())
     {
         long long totframes;
         keyframedist = 15;
@@ -116,7 +116,7 @@
         totframes = (long long)(ringBuffer->DVD()->GetTotalTimeOfTitle() * fps);
         posMap[totframes] = ringBuffer->DVD()->GetTotalReadPosition();
     }
-    else if (ringBuffer->isBD())
+    else if (ringBuffer->IsBD())
     {
         long long totframes;
         keyframedist = 15;
@@ -182,8 +182,7 @@
         m_positionMap.push_back(e);
     }
 
-    if (!m_positionMap.empty() && !ringBuffer->isDVD() &&
-        !ringBuffer->isBD())
+    if (!m_positionMap.empty() && !ringBuffer->IsDisc())
         indexOffset = m_positionMap[0].index;
 
     if (!m_positionMap.empty())
@@ -234,8 +233,7 @@
         m_positionMap.push_back(e);
     }
 
-    if (!m_positionMap.empty() && !ringBuffer->isDVD() &&
-        !ringBuffer->isBD())
+    if (!m_positionMap.empty() && !ringBuffer->IsDisc())
         indexOffset = m_positionMap[0].index;
 
     if (!m_positionMap.empty())
@@ -335,13 +333,13 @@
         long long totframes = 0;
         int length = 0;
 
-        if (ringBuffer->isDVD())
+        if (ringBuffer->IsDVD())
         {
             length = ringBuffer->DVD()->GetTotalTimeOfTitle();
             QMutexLocker locker(&m_positionMapLock);
             totframes = m_positionMap.back().index;
         }
-        else if (ringBuffer->isBD())
+        else if (ringBuffer->IsBD())
         {
             length = ringBuffer->BD()->GetTotalTimeOfTitle();
             QMutexLocker locker(&m_positionMapLock);
@@ -509,7 +507,7 @@
     normalframes = max(normalframes, 0);
     SeekReset(lastKey, normalframes, true, discardFrames);
 
-    if (ringBuffer->isDVD() || ringBuffer->isBD() || discardFrames)
+    if (ringBuffer->IsDisc() || discardFrames)
     {
         // We need to tell the NVP and VideoOutput what frame we're on.
         m_parent->SetFramesPlayed(framesPlayed+1);
@@ -521,20 +519,20 @@
 
 long long DecoderBase::GetKey(const PosMapEntry &e) const
 {
-    long long kf  = (ringBuffer->isDVD() || ringBuffer->isBD()) ? 1LL : keyframedist;
+    long long kf = (ringBuffer->IsDisc()) ? 1LL : keyframedist;
     return (hasKeyFrameAdjustTable) ? e.adjFrame :(e.index - indexOffset) * kf;
 }
 
 bool DecoderBase::DoRewindSeek(long long desiredFrame)
 {
-    if (ringBuffer->isDVD())
+    if (ringBuffer->IsDVD())
     {
         long long pos = DVDFindPosition(desiredFrame);
         ringBuffer->Seek(pos, SEEK_SET);
         lastKey = desiredFrame + 1;
         return true;
     }
-    else if (ringBuffer->isBD())
+    else if (ringBuffer->IsBD())
     {
         long long pos = BDFindPosition(desiredFrame);
         ringBuffer->Seek(pos, SEEK_SET);
@@ -645,9 +643,9 @@
             .arg(desiredFrame).arg(framesPlayed)
             .arg((discardFrames) ? "do" : "don't"));
 
-    if (ringBuffer->isDVD() &&
-        ringBuffer->DVD()->TitleTimeLeft() < 5 &&
-        !ringBuffer->InDiscMenuOrStillFrame())
+    if (ringBuffer->IsDVD() &&
+        !ringBuffer->IsInDiscMenuOrStillFrame() &&
+        ringBuffer->DVD()->TitleTimeLeft() < 5)
     {
         return false;
     }
@@ -753,7 +751,7 @@
  */
 void DecoderBase::DoFastForwardSeek(long long desiredFrame, bool &needflush)
 {
-    if (ringBuffer->isDVD())
+    if (ringBuffer->IsDVD())
     {
         long long pos = DVDFindPosition(desiredFrame);
         ringBuffer->Seek(pos,SEEK_SET);
@@ -763,7 +761,7 @@
         framesRead   = lastKey;
         return;
     }
-    else if (ringBuffer->isBD())
+    else if (ringBuffer->IsBD())
     {
         long long pos = BDFindPosition(desiredFrame);
         ringBuffer->Seek(pos,SEEK_SET);
@@ -830,7 +828,7 @@
 
 void DecoderBase::ChangeDVDTrack(bool ffw)
 {
-    if (!ringBuffer->isDVD())
+    if (!ringBuffer->IsDVD())
         return;
 
     bool result = true;
@@ -853,7 +851,7 @@
 
 long long DecoderBase::DVDFindPosition(long long desiredFrame)
 {
-    if (!ringBuffer->isDVD())
+    if (!ringBuffer->IsDVD())
         return 0;
     int diffTime = 0;
     long long desiredTimePos;
@@ -884,7 +882,7 @@
 
 long long DecoderBase::BDFindPosition(long long desiredFrame)
 {
-    if (!ringBuffer->isBD())
+    if (!ringBuffer->IsBD())
         return 0;
     int diffTime = 0;
     long long desiredTimePos;
@@ -915,7 +913,7 @@
 
 void DecoderBase::UpdateDVDFramesPlayed(void)
 {
-    if (!ringBuffer->isDVD())
+    if (!ringBuffer->IsDVD())
         return;
     long long currentpos = (long long)(ringBuffer->DVD()->GetCurrentTime() * fps);
     framesPlayed = framesRead = currentpos ;
@@ -925,7 +923,7 @@
 
 void DecoderBase::UpdateBDFramesPlayed(void)
 {
-    if (!ringBuffer->isBD())
+    if (!ringBuffer->IsBD())
         return;
     long long currentpos = (long long)(ringBuffer->BD()->GetCurrentTime() * fps);
     framesPlayed = framesRead = currentpos ;
Index: libs/libmythtv/tv_rec.cpp
===================================================================
--- libs/libmythtv/tv_rec.cpp	(revision 27380)
+++ libs/libmythtv/tv_rec.cpp	(working copy)
@@ -4065,7 +4065,7 @@
         bool write = genOpt.cardtype != "IMPORT";
         VERBOSE(VB_IMPORTANT, LOC + QString("rec->GetPathname(): '%1'")
                 .arg(rec->GetPathname()));
-        SetRingBuffer(new RingBuffer(rec->GetPathname(), write));
+        SetRingBuffer(RingBuffer::Create(rec->GetPathname(), write));
         if (!ringBuffer->IsOpen() && write)
         {
             VERBOSE(VB_IMPORTANT, LOC_ERR +
@@ -4450,7 +4450,7 @@
 
     StartedRecording(prog);
 
-    *rb = new RingBuffer(prog->GetPathname(), true);
+    *rb = RingBuffer::Create(prog->GetPathname(), true);
     if (!(*rb)->IsOpen())
     {
         VERBOSE(VB_IMPORTANT, LOC_ERR +
Index: libs/libmythtv/textsubtitleparser.cpp
===================================================================
--- libs/libmythtv/textsubtitleparser.cpp	(revision 27380)
+++ libs/libmythtv/textsubtitleparser.cpp	(working copy)
@@ -119,7 +119,7 @@
 bool TextSubtitleParser::LoadSubtitles(QString fileName, TextSubtitles &target)
 {
     demux_sputext_t sub_data;
-    sub_data.rbuffer = new RingBuffer(fileName, 0, false);
+    sub_data.rbuffer = RingBuffer::Create(fileName, 0, false);
 
     if (!sub_data.rbuffer)
         return false;
Index: programs/mythfrontend/main.cpp
===================================================================
--- programs/mythfrontend/main.cpp	(revision 27380)
+++ programs/mythfrontend/main.cpp	(working copy)
@@ -750,7 +750,7 @@
 
     if (pginfo->IsVideoDVD())
     {
-        RingBuffer *tmprbuf = new RingBuffer(pginfo->GetPathname(), false);
+        RingBuffer *tmprbuf = RingBuffer::Create(pginfo->GetPathname(), false);
 
         if (!tmprbuf)
         {
@@ -759,8 +759,8 @@
         }
         QString name;
         QString serialid;
-        if (tmprbuf->isDVD() &&
-             tmprbuf->DVD()->GetNameAndSerialNum(name, serialid))
+        if (tmprbuf->IsDVD() &&
+            tmprbuf->DVD()->GetNameAndSerialNum(name, serialid))
         {
             QStringList fields = pginfo->QueryDVDBookmark(serialid, false);
             if (!fields.empty())
Index: programs/mythtranscode/transcode.cpp
===================================================================
--- programs/mythtranscode/transcode.cpp	(revision 27380)
+++ programs/mythtranscode/transcode.cpp	(working copy)
@@ -377,7 +377,7 @@
     nvr = new NuppelVideoRecorder(NULL, NULL);
 
     // Input setup
-    inRingBuffer = new RingBuffer(inputname, false, false);
+    inRingBuffer = RingBuffer::Create(inputname, false, false);
     player = new MythPlayer();
 
     player_ctx = new PlayerContext(kTranscoderInUseID);
@@ -684,7 +684,7 @@
         else if (vidsetting == "RTjpeg")
             nvr->SetupRTjpeg();
 
-        outRingBuffer = new RingBuffer(outputname, true, false);
+        outRingBuffer = RingBuffer::Create(outputname, true, false);
         nvr->SetRingBuffer(outRingBuffer);
         nvr->WriteHeader();
         nvr->StreamAllocate();
Index: programs/mythcommflag/main.cpp
===================================================================
--- programs/mythcommflag/main.cpp	(revision 27380)
+++ programs/mythcommflag/main.cpp	(working copy)
@@ -155,7 +155,7 @@
     else
         filename = get_filename(program_info);
 
-    RingBuffer *tmprbuf = new RingBuffer(filename, false);
+    RingBuffer *tmprbuf = RingBuffer::Create(filename, false);
     if (!tmprbuf)
     {
         VERBOSE(VB_IMPORTANT,
@@ -737,7 +737,7 @@
 
     QString filename = get_filename(program_info);
 
-    RingBuffer *tmprbuf = new RingBuffer(filename, false);
+    RingBuffer *tmprbuf = RingBuffer::Create(filename, false);
     if (!tmprbuf)
     {
         VERBOSE(VB_IMPORTANT,
Index: programs/mythbackend/filetransfer.cpp
===================================================================
--- programs/mythbackend/filetransfer.cpp	(revision 27380)
+++ programs/mythbackend/filetransfer.cpp	(working copy)
@@ -11,7 +11,7 @@
 FileTransfer::FileTransfer(QString &filename, MythSocket *remote,
                            bool usereadahead, int timeout_ms) :
     readthreadlive(true), readsLocked(false),
-    rbuffer(new RingBuffer(filename, false, usereadahead, timeout_ms)),
+    rbuffer(RingBuffer::Create(filename, false, usereadahead, timeout_ms)),
     sock(remote), ateof(false), lock(QMutex::NonRecursive),
     refLock(QMutex::NonRecursive), refCount(0), writemode(false)
 {
@@ -21,7 +21,7 @@
 
 FileTransfer::FileTransfer(QString &filename, MythSocket *remote, bool write) :
     readthreadlive(true), readsLocked(false),
-    rbuffer(new RingBuffer(filename, write)),
+    rbuffer(RingBuffer::Create(filename, write)),
     sock(remote), ateof(false), lock(QMutex::NonRecursive),
     refLock(QMutex::NonRecursive), refCount(0), writemode(write)
 {
@@ -234,7 +234,7 @@
     if (pginfo)
         pginfo->UpdateInUseMark();
 
-    rbuffer->SetTimeout(fast);
+    rbuffer->SetOldFile(fast);
 }
 
 /* vim: set expandtab tabstop=4 shiftwidth=4: */
