Index: libs/libmythtv/mpegrecorder.h
===================================================================
--- libs/libmythtv/mpegrecorder.h	(revision 18079)
+++ libs/libmythtv/mpegrecorder.h	(working copy)
@@ -6,6 +6,7 @@
 #include "dtvrecorder.h"
 #include "tspacket.h"
 #include "mpegstreamdata.h"
+#include "DeviceReadBuffer.h"
 
 struct AVFormatContext;
 struct AVPacket;
@@ -13,7 +14,8 @@
 class MpegRecorder : public DTVRecorder,
                      public MPEGSingleProgramStreamListener,
                      public TSPacketListener,
-                     public TSPacketListenerAV
+                     public TSPacketListenerAV,
+                     public ReaderPausedCB
 {
   public:
     MpegRecorder(TVRec*);
@@ -58,6 +60,9 @@
     void HandleSingleProgramPAT(ProgramAssociationTable *pat);
     void HandleSingleProgramPMT(ProgramMapTable *pmt);
 
+    // ReaderPausedCB
+    virtual void ReaderPaused(int fd) { paused = true; pauseWait.wakeAll(); }
+
   private:
     bool OpenMpegFileAsInput(void);
     bool OpenV4L2DeviceAsInput(void);
@@ -116,6 +121,9 @@
     static const char *aspectRatio[];
     static const unsigned int kBuildBufferMaxSize;
 
+    // Buffer device reads
+    DeviceReadBuffer *_device_read_buffer;
+
     // TS
     MPEGStreamData *_stream_data;
     unsigned char   _stream_id[0x1fff  + 1];
Index: libs/libmythtv/mpegrecorder.cpp
===================================================================
--- libs/libmythtv/mpegrecorder.cpp	(revision 18079)
+++ libs/libmythtv/mpegrecorder.cpp	(working copy)
@@ -95,8 +95,9 @@
     audvolume(80),            language(0),
     // Input file descriptors
     chanfd(-1),               readfd(-1),
+    _device_read_buffer(NULL),
     // TS packet handling
-    _stream_data(NULL)                                   
+    _stream_data(NULL)
 {
 }
 
@@ -107,6 +108,8 @@
 
 void MpegRecorder::TeardownAll(void)
 {
+    StopRecording();
+
     if (chanfd >= 0)
     {
         close(chanfd);
@@ -352,7 +355,21 @@
             .arg(usingv4l2).arg(has_v4l2_vbi).arg(has_buggy_vbi));
 
 
-    if ((driver != "hdpvr") && !SetFormat(chanfd))
+    if (driver == "hdpvr")
+    {
+        struct v4l2_format vfmt;
+        bzero(&vfmt, sizeof(vfmt));
+            
+        vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+            
+        if (ioctl(chanfd, VIDIOC_G_FMT, &vfmt) == 0)
+            VERBOSE(VB_RECORD, QString("HD-PVR video resolution %1 x %2")
+                    .arg(vfmt.fmt.pix.width).arg(vfmt.fmt.pix.height));
+        else
+            VERBOSE(VB_IMPORTANT,
+                    LOC_ERR + "HD-PVR driver did not return video resolution.");
+    }
+    else if (!SetFormat(chanfd))
         return false;
 
     if (driver != "hdpvr")
@@ -384,6 +401,33 @@
         return false;
     }
 
+    if (_device_read_buffer)
+    {
+        if (_device_read_buffer->IsRunning())
+            _device_read_buffer->Stop();
+        
+        delete _device_read_buffer;
+        _device_read_buffer = NULL;
+    }
+
+    _device_read_buffer = new DeviceReadBuffer(this);
+    
+    if (!_device_read_buffer)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to allocate DRB buffer");
+        _error = true;
+        return false;   
+    }
+
+    if (!_device_read_buffer->Setup(vdevice.constData(), readfd))
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to allocate DRB buffer");
+        _error = true;
+        return false;
+    }
+
+    VERBOSE(VB_RECORD, LOC + "DRB ready");
+
     return true;
 }
 
@@ -842,6 +886,13 @@
         return;
     }
 
+    bool has_select = true;
+    
+#if defined(__FreeBSD__)
+    // HACK. FreeBSD PVR150/500 driver doesn't currently support select()
+    has_select = false;
+#endif
+
     _start_code = 0xffffffff;
     _last_gop_seen = 0;
     _frames_written_count = 0;
@@ -877,57 +928,104 @@
 
     if (deviceIsMpegFile)
         elapsedTimer.start();
+    else if (_device_read_buffer)
+        _device_read_buffer->Start();
 
     QByteArray vdevice = videodevice.toAscii();
-    while (encoding)
+    while (encoding && !_error)
     {
         if (PauseAndWait(100))
             continue;
 
-        if ((deviceIsMpegFile) && (GetFramesWritten()))
+        if (deviceIsMpegFile)
         {
-            elapsed = (elapsedTimer.elapsed() / 1000.0) + 1;
-            while ((GetFramesWritten() / elapsed) > 30)
+            if (GetFramesWritten())
             {
-                usleep(50000);
                 elapsed = (elapsedTimer.elapsed() / 1000.0) + 1;
+                while ((GetFramesWritten() / elapsed) > 30)
+                {
+                    usleep(50000);
+                    elapsed = (elapsedTimer.elapsed() / 1000.0) + 1;
+                }
             }
         }
+        else
+        {
+            if (readfd < 0)
+            {
+                if (!Open())
+                {
+                    _error = true;
+                    return;
+                }
 
-        if (readfd < 0)
-            readfd = open(vdevice.constData(), O_RDWR);
+                if (readfd < 0)
+                {
+                    VERBOSE(VB_IMPORTANT, LOC_ERR + 
+                            QString("Failed to open device '%1'")
+                            .arg(videodevice));
+                    continue;
+                }
+            }
+        }
 
-        if (readfd < 0)
+
+        if (_device_read_buffer)
         {
-            VERBOSE(VB_IMPORTANT, LOC_ERR + 
-                    QString("Failed to open device '%1'").arg(videodevice));
-            continue;
-        }
+            len = _device_read_buffer->Read(
+                    &(buffer[remainder]), bufferSize - remainder);
 
-        bool has_select = true;
+            // Check for DRB errors
+            if (_device_read_buffer->IsErrored())
+            {
+                VERBOSE(VB_IMPORTANT, LOC_ERR + "Device error detected");
 
-#if defined(__FreeBSD__)
-        // HACK. FreeBSD PVR150/500 driver doesn't currently support select()
-        has_select = false;
-#endif
-
-        if (has_select)
+                _device_read_buffer->Stop();
+                StopEncoding(readfd);
+                usleep(1000);
+                if (StartEncoding(readfd))
+                {
+                    _device_read_buffer->Start();
+                    // Make sure the next things in the file are a PAT & PMT
+                    _wait_for_keyframe_option = false;
+                    HandleSingleProgramPAT(_stream_data->PATSingleProgram());
+                    HandleSingleProgramPMT(_stream_data->PMTSingleProgram());
+                    _wait_for_keyframe_option = true;
+                }
+                else
+                {
+                    if (close(readfd) != 0)
+                        VERBOSE(VB_IMPORTANT, LOC_ERR + "Close error" + ENO);
+                    
+                    // Force card to be reopened on next iteration..
+                    readfd = -1;
+                }
+            }
+            else if (_device_read_buffer->IsEOF())
+            {
+                VERBOSE(VB_IMPORTANT, LOC_ERR + "Device EOF detected");
+                _error = true;
+            }
+        }
+        else
         {
-            tv.tv_sec = 5;
-            tv.tv_usec = 0;
-            FD_ZERO(&rdset);
-            FD_SET(readfd, &rdset);
+            if (has_select)
+            {
+                tv.tv_sec = 5;
+                tv.tv_usec = 0;
+                FD_ZERO(&rdset);
+                FD_SET(readfd, &rdset);
 
-            switch (select(readfd + 1, &rdset, NULL, NULL, &tv))
-            {
-                case -1:
+                switch (select(readfd + 1, &rdset, NULL, NULL, &tv))
+                {
+                  case -1:
                     if (errno == EINTR)
                         continue;
 
                     VERBOSE(VB_IMPORTANT, LOC_ERR + "Select error" + ENO);
                     continue;
 
-                case 0:
+                  case 0:
                     VERBOSE(VB_IMPORTANT, LOC_ERR + "select timeout - "
                             "driver has stopped responding");
 
@@ -941,46 +1039,48 @@
 
                     continue;
                 
-                default: break;
+                  default: break;
+                }
             }
-        }
 
-        len = read(readfd, &(buffer[remainder]), bufferSize - remainder);
+            len = read(readfd, &(buffer[remainder]), bufferSize - remainder);
 
-        if (len < 0 && !has_select)
-        {
-            usleep(25 * 1000);
-            continue;
-        }
-
-        if ((len == 0) && (deviceIsMpegFile))
-        {
-            close(readfd);
-            readfd = open(vdevice.constData(), O_RDONLY);
-
-            if (readfd >= 0)
+            if (len < 0 && !has_select)
             {
-                len = read(readfd,
-                           &(buffer[remainder]), bufferSize - remainder);
+                usleep(25 * 1000);
+                continue;
             }
-
-            if (len <= 0)
+            
+            if ((len == 0) && (deviceIsMpegFile))
             {
-                encoding = false;
+                close(readfd);
+                readfd = open(vdevice.constData(), O_RDONLY);
+                
+                if (readfd >= 0)
+                {
+                    len = read(readfd,
+                               &(buffer[remainder]), bufferSize - remainder);
+                }
+                
+                if (len <= 0)
+                {
+                    encoding = false;
+                    continue;
+                }
+            }
+            else if (len < 0 && errno != EAGAIN)
+            {
+                VERBOSE(VB_IMPORTANT,
+                        LOC_ERR + QString("error reading from: %1")
+                        .arg(videodevice) + ENO);
                 continue;
             }
         }
-        else if (len < 0 && errno != EAGAIN)
-        {
-            VERBOSE(VB_IMPORTANT, LOC_ERR + QString("error reading from: %1")
-                    .arg(videodevice) + ENO);
 
-            continue;
-        }
-        else if (len > 0)
+        if (len > 0)
         {
             len += remainder;
-
+                
             if (driver == "hdpvr") {
                 remainder = _stream_data->ProcessData(buffer, len);
                 int start_remain = len - remainder;
@@ -996,6 +1096,15 @@
         }
     }
 
+    if (_device_read_buffer)
+    {
+        if (_device_read_buffer->IsRunning())
+            _device_read_buffer->Stop();
+
+        delete _device_read_buffer;
+        _device_read_buffer = NULL;
+    }
+
     FinishRecording();
 
     delete[] buffer;
@@ -1095,6 +1204,9 @@
 void MpegRecorder::StopRecording(void)
 {
     encoding = false;
+    if (_device_read_buffer)
+        _device_read_buffer->Stop();
+    StopEncoding(readfd);
 }
 
 void MpegRecorder::ResetForNewFile(void)
@@ -1133,19 +1245,34 @@
     if (request_pause)
     {
         QMutex waitlock;
+        waitlock.lock();
+
         if (!paused)
         {
+            if (_device_read_buffer)
+            {
+                QMutex drb_lock;
+                drb_lock.lock();
+
+                _device_read_buffer->SetRequestPause(true);
+
+                pauseWait.wait(&drb_lock, timeout);
+            }
+            else
+            {
+                paused = true;
+                pauseWait.wakeAll();
+            }
+
             // Some drivers require streaming to be disabled before
             // an input switch and other channel format setting.
             if (requires_special_pause)
                 StopEncoding(readfd);
 
-            paused = true;
-            pauseWait.wakeAll();
             if (tvrec)
                 tvrec->RecorderPaused();
         }
-        waitlock.lock();
+
         unpauseWait.wait(&waitlock, timeout);
     }
     if (!request_pause)
@@ -1157,6 +1284,9 @@
             if (requires_special_pause)
                 StartEncoding(readfd);
 
+            if (_device_read_buffer)
+                _device_read_buffer->SetRequestPause(false);
+
             if (_stream_data)
                 _stream_data->Reset(_stream_data->DesiredProgram());
         }
@@ -1167,14 +1297,50 @@
 
 bool MpegRecorder::StartEncoding(int fd)
 {
+    int idx;
+
+    VERBOSE(VB_RECORD, LOC + "StartEncoding");
+
+    if (driver == "hdpvr")
+    {
+        VERBOSE(VB_RECORD, LOC + "Waiting for HD-PVR");
+        // Make sure it is ready to stream
+        struct v4l2_format vfmt;
+        
+        for (idx = 0; idx < 40; ++idx)
+        {
+            bzero(&vfmt, sizeof(vfmt));
+            
+            vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+            
+            if (ioctl(chanfd, VIDIOC_G_FMT, &vfmt) == 0)
+                break;
+            
+            usleep(250 * 1000);
+        }
+        
+        if (idx == 400)
+        {
+            VERBOSE(VB_IMPORTANT, LOC_ERR +
+                    "StartEncoding - HD-PVR not ready, giving up" + ENO);
+            return false;
+        }
+
+        VERBOSE(VB_RECORD, QString("HD-PVR video resolution %1 x %2")
+                .arg(vfmt.fmt.pix.width).arg(vfmt.fmt.pix.height));
+    }
+
     struct v4l2_encoder_cmd command;
     memset(&command, 0, sizeof(struct v4l2_encoder_cmd));
     command.cmd = V4L2_ENC_CMD_START;
 
-    for (int idx = 0; idx < 10; ++idx)
+    for (idx = 0; idx < 40; ++idx)
     {
         if (ioctl(fd, VIDIOC_ENCODER_CMD, &command) == 0)
+        {
+            VERBOSE(VB_RECORD, LOC + "Encoding started");
             return true;
+        }
 
         if (errno != EAGAIN)
         {
@@ -1195,11 +1361,16 @@
     memset(&command, 0, sizeof(struct v4l2_encoder_cmd));
     command.cmd = V4L2_ENC_CMD_STOP;
 
+    VERBOSE(VB_RECORD, LOC + "StopEncoding");
+
     for (int idx = 0; idx < 10; ++idx)
     {
 
         if (ioctl(fd, VIDIOC_ENCODER_CMD, &command) == 0)
+        {
+            VERBOSE(VB_RECORD, LOC + "Encoding stopped");
             return true;
+        }
 
         if (errno != EAGAIN)
         {
