Index: libs/libmythtv/channelscan/channelscanner.cpp
===================================================================
--- libs/libmythtv/channelscan/channelscanner.cpp	(revision 20746)
+++ libs/libmythtv/channelscan/channelscanner.cpp	(working copy)
@@ -332,8 +332,7 @@
 #ifdef USING_HDHOMERUN
     if ("HDHOMERUN" == card_type)
     {
-        uint tuner = CardUtil::GetHDHRTuner(cardid);
-        channel = new HDHRChannel(NULL, device, tuner);
+        channel = new HDHRChannel(NULL, device);
     }
 #endif // USING_HDHOMERUN
 
Index: libs/libmythtv/cardutil.h
===================================================================
--- libs/libmythtv/cardutil.h	(revision 20746)
+++ libs/libmythtv/cardutil.h	(working copy)
@@ -116,7 +116,7 @@
 
     static bool         IsTunerSharingCapable(const QString &rawtype)
     {
-        return (rawtype == "DVB");
+        return (rawtype == "DVB")   || (rawtype == "HDHOMERUN");
     }
 
     static bool         IsTunerShared(uint cardidA, uint cardidB);
Index: libs/libmythtv/videosource.h
===================================================================
--- libs/libmythtv/videosource.h	(revision 20746)
+++ libs/libmythtv/videosource.h	(working copy)
@@ -442,17 +442,27 @@
     static void fillSelections(SelectSetting* setting);
 };
 
+class HDHomeRunDeviceID;
+class HDHomeRunIP;
+class HDHomeRunTuner;
 class HDHomeRunConfigurationGroup : public VerticalConfigurationGroup
 {
     Q_OBJECT
 
+    friend class HDHomeRunExtra;
+
   public:
     HDHomeRunConfigurationGroup(CaptureCard &parent);
 
   public slots:
     void probeCard(const QString &device);
+    void HDHomeRunExtraPanel(void);
 
   private:
+    HDHomeRunDeviceID *deviceid;
+    HDHomeRunIP       *cardip;
+    HDHomeRunTuner    *cardtuner;
+
     CaptureCard       &parent;
     TransLabelSetting *desc;
 };
@@ -592,6 +602,7 @@
     CaptureCard(bool use_card_group = true);
 
     int  getCardID(void) const { return id->intValue(); }
+    QString GetRawCardType(void) const;
 
     void loadByID(int id);
 
@@ -623,7 +634,7 @@
             CaptureCardDBStorage(this, parent, "hostname") { }
     };
 
-private:
+  protected:
     ID       *id;
     uint      instance_count;
 };
Index: libs/libmythtv/libmythtv.pro
===================================================================
--- libs/libmythtv/libmythtv.pro	(revision 20746)
+++ libs/libmythtv/libmythtv.pro	(working copy)
@@ -524,10 +524,10 @@
     using_hdhomerun {
         # MythTV HDHomeRun glue
         HEADERS += hdhrsignalmonitor.h   hdhrchannel.h
-        HEADERS += hdhrrecorder.h
+        HEADERS += hdhrrecorder.h        hdhrstreamhandler.h
 
         SOURCES += hdhrsignalmonitor.cpp hdhrchannel.cpp
-        SOURCES += hdhrrecorder.cpp
+        SOURCES += hdhrrecorder.cpp      hdhrstreamhandler.cpp
 
         DEFINES += USING_HDHOMERUN
     }
Index: libs/libmythtv/hdhrstreamhandler.cpp
===================================================================
--- libs/libmythtv/hdhrstreamhandler.cpp	(revision 0)
+++ libs/libmythtv/hdhrstreamhandler.cpp	(revision 0)
@@ -0,0 +1,799 @@
+// -*- Mode: c++ -*-
+
+// POSIX headers
+#include <pthread.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <sys/ioctl.h>
+
+// Qt headers
+#include <QString>
+
+// MythTV headers
+#include "hdhrstreamhandler.h"
+#include "hdhrchannel.h"
+#include "dtvsignalmonitor.h"
+#include "streamlisteners.h"
+#include "mpegstreamdata.h"
+#include "cardutil.h"
+
+#define LOC      QString("HDHRSH(%1): ").arg(_devicename)
+#define LOC_WARN QString("HDHRSH(%1) Warning: ").arg(_devicename)
+#define LOC_ERR  QString("HDHRSH(%1) Error: ").arg(_devicename)
+
+QMap<uint,bool> HDHRStreamHandler::_rec_supports_ts_monitoring;
+QMutex          HDHRStreamHandler::_rec_supports_ts_monitoring_lock;
+
+QMap<QString,HDHRStreamHandler*> HDHRStreamHandler::_handlers;
+QMap<QString,uint>               HDHRStreamHandler::_handlers_refcnt;
+QMutex                           HDHRStreamHandler::_handlers_lock;
+
+//#define DEBUG_PID_FILTERS
+
+HDHRStreamHandler *HDHRStreamHandler::Get(const QString &devname)
+{
+    QMutexLocker locker(&_handlers_lock);
+
+    QMap<QString,HDHRStreamHandler*>::iterator it =
+        _handlers.find(devname);
+
+    if (it == _handlers.end())
+    {
+        HDHRStreamHandler *newhandler = new HDHRStreamHandler(devname);
+        newhandler->Open();
+        _handlers[devname] = newhandler;
+        _handlers_refcnt[devname] = 1;
+        VERBOSE(VB_RECORD,
+                QString("HDHRSH: Creating new stream handler for %1")
+                .arg(devname));
+    }
+    else
+    {
+        _handlers_refcnt[devname]++;
+        uint rcount = _handlers_refcnt[devname];
+        VERBOSE(VB_RECORD,
+                QString("HDHRSH: Using existing stream handler for %1")
+                .arg(devname) + QString(" (%2 in use)").arg(rcount));
+    }
+
+    return _handlers[devname];
+}
+
+void HDHRStreamHandler::Return(HDHRStreamHandler * & ref)
+{
+    QMutexLocker locker(&_handlers_lock);
+
+    QString devname = ref->_devicename;
+
+    QMap<QString,uint>::iterator rit = _handlers_refcnt.find(devname);
+    if (rit == _handlers_refcnt.end())
+        return;
+
+    if (*rit > 1)
+    {
+        ref = NULL;
+        *rit--;
+        return;
+    }
+
+    QMap<QString,HDHRStreamHandler*>::iterator it = _handlers.find(devname);
+    if ((it != _handlers.end()) && (*it == ref))
+    {
+        VERBOSE(VB_RECORD,
+                QString("HDHRSH: Closing handler for %1")
+                .arg(devname));
+        ref->Close();
+        delete *it;
+        _handlers.erase(it);
+    }
+    else
+    {
+        VERBOSE(VB_IMPORTANT,
+                QString("HDHRSH Error: Couldn't find handler for %1")
+                .arg(devname));
+    }
+
+    _handlers_refcnt.erase(rit);
+    ref = NULL;
+}
+
+HDHRStreamHandler::HDHRStreamHandler(const QString &devicename) :
+    _control_socket(NULL),
+    _video_socket(NULL),
+    _devicename(devicename),
+
+    _start_stop_lock(QMutex::Recursive),
+    _running(false),
+
+    _pid_lock(QMutex::Recursive),
+    _listener_lock(QMutex::Recursive),
+    _hdhr_lock(QMutex::Recursive)
+{
+}
+
+HDHRStreamHandler::~HDHRStreamHandler()
+{
+    if (!_stream_data_list.empty())
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR + "dtor & _stream_data_list not empty");
+    }
+}
+
+void HDHRStreamHandler::AddListener(MPEGStreamData *data)
+{
+    VERBOSE(VB_RECORD, LOC + "AddListener("<<data<<") -- begin");
+    if (!data)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR +
+                "AddListener("<<data<<") -- null data");
+        return;
+    }
+
+    _listener_lock.lock();
+
+    VERBOSE(VB_RECORD, LOC + "AddListener("<<data<<") -- locked");
+
+    _stream_data_list.push_back(data);
+
+    _listener_lock.unlock();
+
+    Start();
+
+    VERBOSE(VB_RECORD, LOC + "AddListener("<<data<<") -- end");
+}
+
+void HDHRStreamHandler::RemoveListener(MPEGStreamData *data)
+{
+    VERBOSE(VB_RECORD, LOC + "RemoveListener("<<data<<") -- begin");
+    if (!data)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR +
+                "RemoveListener("<<data<<") -- null data");
+        return;
+    }
+
+    _listener_lock.lock();
+
+    VERBOSE(VB_RECORD, LOC + "RemoveListener("<<data<<") -- locked");
+
+    vector<MPEGStreamData*>::iterator it =
+        find(_stream_data_list.begin(), _stream_data_list.end(), data);
+
+    if (it != _stream_data_list.end())
+        _stream_data_list.erase(it);
+
+    if (_stream_data_list.empty())
+    {
+        _listener_lock.unlock();
+        Stop();
+    }
+    else
+    {
+        _listener_lock.unlock();
+    }
+
+    VERBOSE(VB_RECORD, LOC + "RemoveListener("<<data<<") -- end");
+}
+
+void *run_hdhr_stream_handler_thunk(void *param)
+{
+    HDHRStreamHandler *mon = (HDHRStreamHandler*) param;
+    mon->Run();
+    return NULL;
+}
+
+void HDHRStreamHandler::Start(void)
+{
+    QMutexLocker locker(&_start_stop_lock);
+
+    _eit_pids.clear(); 
+
+    if (!IsRunning())
+    {
+        QMutex is_running_lock;
+        int rval = pthread_create(&_reader_thread, NULL,
+                                  run_hdhr_stream_handler_thunk, this);
+
+        if (0 != rval)
+        {
+            VERBOSE(VB_IMPORTANT, LOC_ERR +
+                    "Start: Failed to create thread." + ENO);
+            return;
+        }
+
+        is_running_lock.lock();
+        while (!IsRunning())
+        {
+            _running_state_changed.wait(&is_running_lock, 100);
+        }
+    }
+}
+
+void HDHRStreamHandler::Stop(void)
+{
+    QMutexLocker locker(&_start_stop_lock);
+
+    if (IsRunning())
+    {
+        SetRunning(false);
+        pthread_join(_reader_thread, NULL);
+    }
+}
+
+void HDHRStreamHandler::Run(void)
+{
+    SetRunning(true);
+    RunTS();
+}
+
+/** \fn HDHRStreamHandler::RunTS(void)
+ *  \brief Uses TS filtering devices to read a DVB device for tables & data
+ *
+ *  This supports all types of MPEG based stream data, but is extreemely
+ *  slow with DVB over USB 1.0 devices which for efficiency reasons buffer 
+ *  a stream until a full block transfer buffer full of the requested 
+ *  tables is available. This takes a very long time when you are just
+ *  waiting for a PAT or PMT table, and the buffer is hundreds of packets
+ *  in size.
+ */
+void HDHRStreamHandler::RunTS(void)
+{
+    int remainder = 0;
+
+    /* Calculate buffer size */
+    uint buffersize = gContext->GetNumSetting(
+        "HDRingbufferSize", 50 * TSPacket::SIZE) * 1024;
+    buffersize /= VIDEO_DATA_PACKET_SIZE;
+    buffersize *= VIDEO_DATA_PACKET_SIZE;
+
+    // Buffer should be at least about 1MB..
+    buffersize = max(49 * TSPacket::SIZE * 128, buffersize);
+
+    /* Create TS socket. */
+    _video_socket = hdhomerun_video_create(0, buffersize);
+    if (!_video_socket)
+    {
+        VERBOSE(VB_IMPORTANT, LOC + "Open() failed to open socket");
+        return;
+    }
+
+    uint localPort = hdhomerun_video_get_local_port(_video_socket);
+    if (!DeviceSetTarget(localPort))
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR +
+                "Starting recording (set target failed). Aborting.");
+        return;
+    }
+    hdhomerun_video_flush(_video_socket);
+
+    bool _error = false;
+
+    VERBOSE(VB_RECORD, LOC + "RunTS(): begin");
+
+    while (IsRunning() && !_error)
+    {
+        UpdateFiltersFromStreamData();
+
+        size_t read_size = 64 * 1024; // read about 64KB
+        read_size /= VIDEO_DATA_PACKET_SIZE;
+        read_size *= VIDEO_DATA_PACKET_SIZE;
+
+        size_t data_length;
+        unsigned char *data_buffer =
+            hdhomerun_video_recv(_video_socket, read_size, &data_length);
+
+        if (!data_buffer)
+        {
+            usleep(5000);
+            continue;
+        }
+
+        // Assume data_length is a multiple of 188 (packet size)
+
+        _listener_lock.lock();
+
+        if (_stream_data_list.empty())
+        {
+            _listener_lock.unlock();
+            continue;
+        }
+
+        for (uint i = 0; i < _stream_data_list.size(); i++)
+        {
+            remainder = _stream_data_list[i]->ProcessData(
+                data_buffer, data_length);
+        }
+       
+        _listener_lock.unlock();
+        if (remainder != 0)
+        {
+            VERBOSE(VB_IMPORTANT, LOC +
+                    QString("RunTS(): data_length = %1 remainder = %2")
+                    .arg(data_length).arg(remainder));
+        }
+    }
+    VERBOSE(VB_RECORD, LOC + "RunTS(): " + "shutdown");
+
+    RemoveAllPIDFilters();
+
+    DeviceClearTarget();
+    VERBOSE(VB_RECORD, LOC + "RunTS(): " + "end");
+
+    hdhomerun_video_sock_t* tmp_video_socket;
+    {
+        QMutexLocker locker(&_hdhr_lock);
+        tmp_video_socket = _video_socket;
+        _video_socket=NULL;
+    }
+     
+    hdhomerun_video_destroy(tmp_video_socket);
+
+    VERBOSE(VB_RECORD, LOC + "RunTS(): " + "end");
+
+    SetRunning(false);
+}
+
+bool HDHRStreamHandler::AddPIDFilter(uint pid, bool do_update)
+{
+#ifdef DEBUG_PID_FILTERS
+    VERBOSE(VB_RECORD, LOC + QString("AddPIDFilter(0x%1)")
+            .arg(pid, 0, 16));
+#endif // DEBUG_PID_FILTERS
+
+    QMutexLocker writing_locker(&_pid_lock);
+
+    vector<uint>::iterator it;
+    it = lower_bound(_pid_info.begin(), _pid_info.end(), pid);
+    if (it != _pid_info.end() && *it == pid)
+        return true;
+
+    _pid_info.insert(it, pid);
+
+    if (do_update)
+        return UpdateFilters();
+
+    return true;
+}
+
+bool HDHRStreamHandler::RemovePIDFilter(uint pid, bool do_update)
+{
+#ifdef DEBUG_PID_FILTERS
+    VERBOSE(VB_RECORD, LOC +
+            QString("RemovePIDFilter(0x%1)").arg(pid, 0, 16));
+#endif // DEBUG_PID_FILTERS
+
+    QMutexLocker write_locker(&_pid_lock);
+
+    vector<uint>::iterator it;
+    it = lower_bound(_pid_info.begin(), _pid_info.end(), pid);
+    if ((it == _pid_info.end()) || (*it != pid))
+       return false;
+
+    _pid_info.erase(it);
+
+    if (do_update)
+        return UpdateFilters();
+
+    return true;
+}
+
+bool HDHRStreamHandler::RemoveAllPIDFilters(void)
+{
+    QMutexLocker write_locker(&_pid_lock);
+
+#ifdef DEBUG_PID_FILTERS
+    VERBOSE(VB_RECORD, LOC + "RemoveAllPIDFilters()");
+#endif // DEBUG_PID_FILTERS
+
+    _pid_info.clear();
+
+    return UpdateFilters();
+}
+
+static QString filt_str(uint pid)
+{
+    uint pid0 = (pid / (16*16*16)) % 16;
+    uint pid1 = (pid / (16*16))    % 16;
+    uint pid2 = (pid / (16))        % 16;
+    uint pid3 = pid % 16;
+    return QString("0x%1%2%3%4")
+        .arg(pid0,0,16).arg(pid1,0,16)
+        .arg(pid2,0,16).arg(pid3,0,16);
+}
+
+void HDHRStreamHandler::UpdateListeningForEIT(void)
+{
+    vector<uint> add_eit, del_eit;
+
+    QMutexLocker read_locker(&_listener_lock);
+
+    for (uint i = 0; i < _stream_data_list.size(); i++)
+    {
+        MPEGStreamData *sd = _stream_data_list[i];
+        if (sd->HasEITPIDChanges(_eit_pids) &&
+            sd->GetEITPIDChanges(_eit_pids, add_eit, del_eit))
+        {
+            for (uint i = 0; i < del_eit.size(); i++)
+            {
+                uint_vec_t::iterator it;
+                it = find(_eit_pids.begin(), _eit_pids.end(), del_eit[i]);
+                if (it != _eit_pids.end())
+                    _eit_pids.erase(it);
+                sd->RemoveListeningPID(del_eit[i]);
+            }
+
+            for (uint i = 0; i < add_eit.size(); i++)
+            {
+                _eit_pids.push_back(add_eit[i]);
+                sd->AddListeningPID(add_eit[i]);
+            }
+        }
+    }
+}
+
+bool HDHRStreamHandler::UpdateFiltersFromStreamData(void)
+{
+    UpdateListeningForEIT();
+
+    pid_map_t pids;
+
+    {
+        QMutexLocker read_locker(&_listener_lock);
+
+        for (uint i = 0; i < _stream_data_list.size(); i++)
+            _stream_data_list[i]->GetPIDs(pids);
+    }
+
+    uint_vec_t           add_pids;
+    vector<uint>         del_pids;
+
+    {
+        QMutexLocker read_locker(&_pid_lock);
+
+        // PIDs that need to be added..
+        pid_map_t::const_iterator lit = pids.constBegin();
+        for (; lit != pids.constEnd(); ++lit)
+        {
+            vector<uint>::iterator it;
+            it = lower_bound(_pid_info.begin(), _pid_info.end(), lit.key());
+            if (it == _pid_info.end() || *it != lit.key())
+                add_pids.push_back(lit.key());
+        }
+
+        // PIDs that need to be removed..
+        vector<uint>::const_iterator fit = _pid_info.begin();
+        for (; fit != _pid_info.end(); ++fit)
+        {
+            bool in_pids = pids.find(*fit) != pids.end();
+            if (!in_pids)
+                del_pids.push_back(*fit);
+        }
+    }
+
+    bool need_update = false;
+
+    // Remove PIDs
+    bool ok = true;
+    vector<uint>::iterator dit = del_pids.begin();
+    for (; dit != del_pids.end(); ++dit)
+    {
+        need_update = true;
+        ok &= RemovePIDFilter(*dit, false);
+    }
+
+    // Add PIDs
+    vector<uint>::iterator ait = add_pids.begin();
+    for (; ait != add_pids.end(); ++ait)
+    {
+        need_update = true;
+        ok &= AddPIDFilter(*ait, false);
+    }
+
+    if (need_update)
+        return UpdateFilters() && ok;
+
+    return ok;
+}
+
+bool HDHRStreamHandler::UpdateFilters(void)
+{
+#ifdef DEBUG_PID_FILTERS
+    VERBOSE(VB_RECORD, LOC + "UpdateFilters()");
+#endif // DEBUG_PID_FILTERS
+    QMutexLocker locker(&_pid_lock);
+
+    QString filter = "";
+
+    vector<uint> range_min;
+    vector<uint> range_max;
+
+// FIXME
+//    if (_ignore_filters)
+ //       return true;
+
+    for (uint i = 0; i < _pid_info.size(); i++)
+    {
+        uint pid_min = _pid_info[i];
+        uint pid_max  = pid_min;
+        for (uint j = i + 1; j < _pid_info.size(); j++)
+        {
+            if (pid_max + 1 != _pid_info[j])
+                break;
+            pid_max++;
+            i++;
+        }
+        range_min.push_back(pid_min);
+        range_max.push_back(pid_max);
+    }
+    if (range_min.size() > 16)
+    {
+        range_min.resize(16);
+        uint pid_max = range_max.back();
+        range_max.resize(15);
+        range_max.push_back(pid_max);
+    }
+
+    for (uint i = 0; i < range_min.size(); i++)
+    {
+        filter += filt_str(range_min[i]);
+        if (range_min[i] != range_max[i])
+            filter += QString("-%1").arg(filt_str(range_max[i]));
+        filter += " ";
+    }
+
+    filter = filter.trimmed();
+
+    QString new_filter = TunerSet("filter", filter);
+
+#ifdef DEBUG_PID_FILTERS
+    QString msg = QString("Filter: '%1'").arg(filter);
+    if (filter != new_filter)
+        msg += QString("\n\t\t\t\t'%2'").arg(new_filter);
+
+    VERBOSE(VB_RECORD, LOC + msg);
+#endif // DEBUG_PID_FILTERS
+
+    return filter == new_filter;
+}
+
+void HDHRStreamHandler::SetRunning(bool is_running)
+{
+    _running = is_running;
+    _running_state_changed.wakeAll();
+}
+
+PIDPriority HDHRStreamHandler::GetPIDPriority(uint pid) const
+{
+    QMutexLocker reading_locker(&_listener_lock);
+
+    PIDPriority tmp = kPIDPriorityNone;
+
+    for (uint i = 0; i < _stream_data_list.size(); i++)
+        tmp = max(tmp, _stream_data_list[i]->GetPIDPriority(pid));
+
+    return tmp;
+}
+
+bool HDHRStreamHandler::Open(void)
+{
+    if (!FindDevice())
+        return false;
+
+    return Connect();
+}
+
+void HDHRStreamHandler::Close(void)
+{
+    if (_control_socket)
+    {
+        TuneChannel("none");
+        hdhomerun_control_destroy(_control_socket);
+        _control_socket=NULL;
+    }
+}
+
+bool HDHRStreamHandler::Connect(void)
+{
+    _control_socket = hdhomerun_control_create(_device_id, _device_ip);
+
+    if (!_control_socket)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR + "Unable to create control socket");
+        return false;
+    }
+
+    if (hdhomerun_control_get_local_addr(_control_socket) == 0)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR + "Unable to connect to device");
+        return false;
+    }
+
+    VERBOSE(VB_RECORD, LOC + "Successfully connected to device");
+    return true;
+}
+
+bool HDHRStreamHandler::FindDevice(void)
+{
+    hdhomerun_device_t* thisdevice = hdhomerun_device_create_from_str(
+        _devicename.toLocal8Bit().constData());
+
+    if (thisdevice)
+    {
+        _device_id = hdhomerun_device_get_device_id(thisdevice);
+        _device_ip = hdhomerun_device_get_device_ip(thisdevice);
+        _tuner     = hdhomerun_device_get_tuner(thisdevice);
+        hdhomerun_device_destroy(thisdevice);
+
+        VERBOSE(VB_IMPORTANT, LOC +
+                QString("device %5 found at address %1.%2.%3.%4 tuner %6")
+                .arg((_device_ip>>24) & 0xFF).arg((_device_ip>>16) & 0xFF)
+                .arg((_device_ip>> 8) & 0xFF).arg((_device_ip>> 0) & 0xFF)
+                .arg(_devicename).arg(_tuner));
+
+        return true;
+    }
+    return false;
+}
+
+
+bool HDHRStreamHandler::EnterPowerSavingMode(void)
+{
+    if (_video_socket)
+    {
+        VERBOSE(VB_RECORD, LOC + "Ignoring request - video streaming active");
+        return false;
+    }
+    else
+    {
+        return TuneChannel("none");
+    }
+}
+
+QString HDHRStreamHandler::DeviceGet(
+    const QString &name, bool report_error_return) const
+{
+    QMutexLocker locker(&_hdhr_lock);
+
+    if (!_control_socket)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR + "Get request failed (not connected)");
+        return QString::null;
+    }
+
+    char *value = NULL;
+    char *error = NULL;
+    if (hdhomerun_control_get(
+            _control_socket, name.toLocal8Bit().constData(),
+            &value, &error) < 0)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR + "Get request failed" + ENO);
+        return QString::null;
+    }
+
+    if (report_error_return && error)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR +
+                QString("DeviceGet(%1): %2").arg(name).arg(error));
+
+        return QString::null;
+    }
+
+    return QString(value);
+}
+
+QString HDHRStreamHandler::DeviceSet(
+    const QString &name, const QString &val, bool report_error_return)
+{
+    QMutexLocker locker(&_hdhr_lock);
+
+    if (!_control_socket)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR + "Set request failed (not connected)");
+        return QString::null;
+    }
+
+    char *value = NULL;
+    char *error = NULL;
+    if (hdhomerun_control_set(
+            _control_socket, name.toLocal8Bit().constData(),
+            val.toLocal8Bit().constData(), &value, &error) < 0)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR + "Set request failed" + ENO);
+
+        return QString::null;
+    }
+
+    if (report_error_return && error)
+    {
+        VERBOSE(VB_IMPORTANT, LOC_ERR +
+                QString("DeviceSet(%1 %2): %3").arg(name).arg(val).arg(error));
+
+        return QString::null;
+    }
+
+    return QString(value);
+}
+
+QString HDHRStreamHandler::TunerGet(
+    const QString &name, bool report_error_return) const
+{
+    return DeviceGet(QString("/tuner%1/%2").arg(_tuner).arg(name),
+                     report_error_return);
+}
+
+QString HDHRStreamHandler::TunerSet(
+    const QString &name, const QString &value, bool report_error_return)
+{
+    QString valname = QString("/tuner%1/%2").arg(_tuner).arg(name);
+    return DeviceSet(valname, value, report_error_return);
+}
+
+bool HDHRStreamHandler::DeviceSetTarget(unsigned short localPort)
+{
+    if (localPort == 0)
+    {
+        return false;
+    }
+
+    unsigned long localIP = hdhomerun_control_get_local_addr(_control_socket);
+    if (localIP == 0)
+    {
+        return false;
+    }
+
+    QString rtpValue = QString("rtp://%1.%2.%3.%4:%5")
+        .arg((localIP >> 24) & 0xFF).arg((localIP >> 16) & 0xFF)
+        .arg((localIP >>  8) & 0xFF).arg((localIP >>  0) & 0xFF)
+        .arg(localPort);
+
+    QString err = TunerSet("target", rtpValue, true);
+
+    if (err.isEmpty())
+    {
+        QString udpValue = QString("udp://%1.%2.%3.%4:%5")
+            .arg((localIP >> 24) & 0xFF).arg((localIP >> 16) & 0xFF)
+            .arg((localIP >>  8) & 0xFF).arg((localIP >>  0) & 0xFF)
+            .arg(localPort);
+        return !TunerSet("target", udpValue).isEmpty();
+    }
+
+    return true;
+}
+
+bool HDHRStreamHandler::DeviceClearTarget(void)
+{
+    return !TunerSet("target", "0.0.0.0:0").isEmpty();
+}
+
+QString HDHRStreamHandler::GetTunerStatus(void) const
+{
+    return TunerGet("status");
+}
+
+bool HDHRStreamHandler::IsConnected(void) const
+{
+    return (_control_socket != NULL);
+}
+
+bool HDHRStreamHandler::TuneChannel(const QString &chn)
+{
+    QString current = TunerGet("channel");
+
+    if (current == chn)
+    {
+        VERBOSE(VB_RECORD, QString(LOC + "Not Re-Tuning channel %1").arg(chn));
+        return true;
+    }
+
+    VERBOSE(VB_RECORD, QString(LOC + "Tuning channel %1 (was %2)")
+            .arg(chn).arg(current));
+    return !TunerSet("channel", chn).isEmpty();
+}
+
+bool HDHRStreamHandler::TuneProgram(uint mpeg_prog_num)
+{
+    VERBOSE(VB_RECORD, QString(LOC + "Tuning program %1").arg(mpeg_prog_num));
+    return !TunerSet(
+        "program", QString::number(mpeg_prog_num), false).isEmpty();
+}
Index: libs/libmythtv/hdhrsignalmonitor.h
===================================================================
--- libs/libmythtv/hdhrsignalmonitor.h	(revision 20746)
+++ libs/libmythtv/hdhrsignalmonitor.h	(working copy)
@@ -7,6 +7,7 @@
 #include "qstringlist.h"
 
 class HDHRChannel;
+class HDHRStreamHandler;
 
 typedef QMap<uint,int> FilterMap;
 
@@ -19,24 +20,16 @@
 
     void Stop(void);
 
-    bool UpdateFiltersFromStreamData(void);
-
   protected:
     HDHRSignalMonitor(void);
     HDHRSignalMonitor(const HDHRSignalMonitor&);
 
     virtual void UpdateValues(void);
+    HDHRChannel *GetHDHRChannel(void); 
 
-    static void *TableMonitorThread(void *param);
-    void RunTableMonitor(void);
-
-    bool SupportsTSMonitoring(void);
-
   protected:
-    bool               dtvMonitorRunning;
-    pthread_t          table_monitor_thread;
-
-    FilterMap          filters; ///< PID filters for table monitoring
+    bool               streamHandlerStarted;
+    HDHRStreamHandler *streamHandler;
 };
 
 #endif // HDHRSIGNALMONITOR_H
Index: libs/libmythtv/hdhrrecorder.h
===================================================================
--- libs/libmythtv/hdhrrecorder.h	(revision 20746)
+++ libs/libmythtv/hdhrrecorder.h	(working copy)
@@ -7,13 +7,17 @@
 #ifndef HDHOMERUNRECORDER_H_
 #define HDHOMERUNRECORDER_H_
 
-#include <qmutex.h>
+// Qt includes
+#include <QMutex>
+
 #include "dtvrecorder.h"
 #include "streamlisteners.h"
 #include "eitscanner.h"
 
 class HDHRChannel;
 class ProgramMapTable;
+class MPEGStreamData;
+class HDHRStreamHandler;
 
 typedef vector<uint>        uint_vec_t;
 
@@ -21,7 +25,9 @@
                      public DVBMainStreamListener,
                      public ATSCMainStreamListener,
                      public MPEGStreamListener,
-                     public MPEGSingleProgramStreamListener
+                     public MPEGSingleProgramStreamListener,
+                     public TSPacketListener,
+                     public TSPacketListenerAV
 {
   public:
     HDHRRecorder(TVRec *rec, HDHRChannel *channel);
@@ -32,16 +38,14 @@
                                const QString &audiodev,
                                const QString &vbidev);
 
+    void StartRecording(void);
+    void ResetForNewFile(void);
+    void StopRecording(void);
+
     bool Open(void);
-    bool StartData(void);
+    bool IsOpen(void) const { return _stream_handler; }
     void Close(void);
 
-    void StartRecording(void);
-
-    void SetStreamData(MPEGStreamData*);
-    MPEGStreamData *GetStreamData(void) { return _stream_data; }
-    ATSCStreamData *GetATSCStreamData(void);
-
     // MPEG Stream Listener
     void HandlePAT(const ProgramAssociationTable*);
     void HandleCAT(const ConditionalAccessTable*) {}
@@ -52,34 +56,60 @@
     void HandleSingleProgramPAT(ProgramAssociationTable *pat);
     void HandleSingleProgramPMT(ProgramMapTable *pmt);
 
-    // ATSC
+    // ATSC Main
     void HandleSTT(const SystemTimeTable*) {}
-    void HandleMGT(const MasterGuideTable *) {};
-    void HandleVCT(uint, const VirtualChannelTable*) {}
+    void HandleVCT(uint /*tsid*/, const VirtualChannelTable*) {}
+    void HandleMGT(const MasterGuideTable*) {}
 
-    // DVB
+    // DVBMainStreamListener
     void HandleTDT(const TimeDateTable*) {}
     void HandleNIT(const NetworkInformationTable*) {}
     void HandleSDT(uint /*tsid*/, const ServiceDescriptionTable*) {}
 
+    // TSPacketListener
+    bool ProcessTSPacket(const TSPacket &tspacket);
+
+    // TSPacketListenerAV
+    bool ProcessVideoTSPacket(const TSPacket& tspacket);
+    bool ProcessAudioTSPacket(const TSPacket& tspacket);
+
+    // Common audio/visual processing
+    bool ProcessAVTSPacket(const TSPacket &tspacket);
+
+    void SetStreamData(MPEGStreamData*);
+    MPEGStreamData *GetStreamData(void) { return _stream_data; }
+    ATSCStreamData *GetATSCStreamData(void);
+
+    void BufferedWrite(const TSPacket &tspacket);
+
   private:
-    bool AdjustFilters(void);
-    bool AdjustEITPIDs(void);
+    void TeardownAll(void);
 
-    void ProcessTSData(const unsigned char *buffer, int len);
-    bool ProcessTSPacket(const TSPacket& tspacket);
-    void TeardownAll(void);
-    
+    void ReaderPaused(int fd);
+    bool PauseAndWait(int timeout = 100);
+
   private:
-    HDHRChannel                   *_channel;
-    struct hdhomerun_video_sock_t *_video_socket;
-    MPEGStreamData                *_stream_data;
+    HDHRChannel              *_channel;
+    HDHRStreamHandler        *_stream_handler;
 
-    ProgramAssociationTable       *_input_pat;
-    ProgramMapTable               *_input_pmt;
-    bool                           _reset_pid_filters;
-    uint_vec_t                     _eit_pids;
-    mutable QMutex                 _pid_lock;
+    // general recorder stuff
+    MPEGStreamData          *_stream_data;
+    mutable QMutex           _pid_lock;
+    ProgramAssociationTable *_input_pat; ///< PAT on input side
+    ProgramMapTable         *_input_pmt; ///< PMT on input side
+    bool                     _has_no_av;
+
+    // TS recorder stuff
+    unsigned char _stream_id[0x1fff + 1];
+    unsigned char _pid_status[0x1fff + 1];
+    unsigned char _continuity_counter[0x1fff + 1];
+
+    // Constants
+    static const int TSPACKETS_BETWEEN_PSIP_SYNC;
+    static const int POLL_INTERVAL;
+    static const int POLL_WARNING_TIMEOUT;
+
+    static const unsigned char kPayloadStartSeen = 0x2;
 };
 
 #endif
Index: libs/libmythtv/dbcheck.cpp
===================================================================
--- libs/libmythtv/dbcheck.cpp	(revision 20746)
+++ libs/libmythtv/dbcheck.cpp	(working copy)
@@ -14,11 +14,16 @@
 #include "mythdb.h"
 #include "mythverbose.h"
 
+// HDHomeRun headers
+#ifdef USING_HDHOMERUN
+#include "hdhomerun.h"
+#endif
 
+
 #define MINIMUM_DBMS_VERSION 5,0,15
 
 /// This is the DB schema version expected by the running MythTV instance.
-const QString currentDatabaseVersion = "1235";
+const QString currentDatabaseVersion = "1236";
 
 static bool UpdateDBVersionNumber(const QString &newnumber);
 static bool performActualUpdate(
@@ -4543,7 +4548,189 @@
             return false;
     }
 
+    if (dbver == "1235")
+    {
+/*
+#ifdef USING_HDHOMERUN
+        VERBOSE(VB_IMPORTANT, "In 1235 upg (HDhomerun tunerid change)");
 
+        // First discover all HDhomreun devices on the network
+        // This will be cached and used to match up with the database query
+
+        uint32_t  target_ip   = 0; // WILDCARD
+        uint32_t  device_type = HDHOMERUN_DEVICE_TYPE_TUNER;
+        uint32_t  device_id   = HDHOMERUN_DEVICE_ID_WILDCARD;
+        const int max_count   = 50;
+        hdhomerun_discover_device_t hdhr_device_list[max_count];
+
+        int hdhr_device_count = hdhomerun_discover_find_devices_custom(
+                                  target_ip,
+                                  device_type,
+                                  device_id,
+                                  hdhr_device_list,
+                                  max_count);
+
+        if (hdhr_device_count == -1)
+        {
+            // Can only check for existing devices
+            VERBOSE(VB_IMPORTANT, "Error finding HDHomerun devices");
+            VERBOSE(VB_IMPORTANT, "All configured HDHomerun devices must be accessible");
+            return false;
+        }
+
+        MSqlQuery query(MSqlQuery::InitCon());
+        query.prepare("SELECT cardid, videodevice, dbox2_port "
+                      "FROM capturecard "
+                      "WHERE cardtype = 'HDHOMERUN' "
+                      "ORDER BY cardid");
+        bool ok = query.exec();
+
+        MSqlQuery query2(MSqlQuery::InitCon());
+        QRegExp newstyle = QRegExp("[0-9A-Z]{8}-[0-9]", Qt::CaseInsensitive);
+        QRegExp newwildcard = QRegExp("F{8}-[0-9]", Qt::CaseInsensitive); // "FFFFFFFF-n"
+
+        while (ok && query.next())
+        {
+            uint    cardid  = query.value(0).toUInt();
+            QString device  = query.value(1).toString();
+            uint    tunerid = query.value(2).toUInt();
+
+            // First check if this is the new style already
+            if (device.contains(newstyle))
+            {
+                QString new_device = "";
+                if (device.contains(newwildcard)) // FFFFFFFF-1
+                {
+                    // Must convert to an actual HDHR
+                    // Use the first HDHR found.
+
+                    QString new_device_id = QString("%1").arg(hdhr_device_list[0].device_id, 8, 16);
+                    new_device = device;
+                    new_device.replace("ffffffff", new_device_id, Qt::CaseInsensitive);
+                } else if (device.toUpper() == device)
+                    VERBOSE(VB_GENERAL, QString("Retaining card %1: HDhomerun %2")
+                                               .arg(cardid).arg(device));
+                else
+                {
+                    // Convert to upper case
+                    new_device = device;
+                }
+
+                if (new_device.length() > 0)
+                {
+                    QString updatequery = QString("UPDATE capturecard SET videodevice = '%1' "
+                                                  "WHERE cardid = %2;")
+                                                 .arg(new_device.toUpper())
+                                                 .arg(cardid);
+                    VERBOSE(VB_GENERAL, QString("Converting card %1: HDhomerun %2 to %3")
+                                               .arg(cardid).arg(device).arg(new_device.toUpper()));
+
+                    if (!query2.exec(updatequery))
+                    {
+                        MythDB::DBError(
+                            "Could not perform update for '1236'", query2);
+                        ok = false;
+                    }
+                }
+            }
+            else
+            {
+
+                // change from device, tuner to device-tuner
+                // i.e.:  AABBCCDD, 1 -> AABBCCDD-1
+                // If device is xxxxxxxx then use it directly
+                // If device is FFFFFFFF then try to discover the HDHR to get the actual value
+                // If device is x.x.x.x (ip address) then try to discover the HDHR to get the actual value
+
+                bool    valid;
+                uint    device_id = device.toUInt(&valid, 16);
+
+                QString new_device = "";
+                if (valid && device_id != HDHOMERUN_DEVICE_ID_WILDCARD
+                          && hdhomerun_discover_validate_device_id(device_id))
+                {
+                    // Valid, non-wildcard device id
+                    // Update it to "xxxxxxxx-#"
+                    new_device = QString("%1-%2").arg(device).arg(tunerid);
+
+                }
+                else if (valid && device_id == HDHOMERUN_DEVICE_ID_WILDCARD)
+                {
+                    // Use the first HDHR found.  It should be the only one if this
+                    // worked before.
+
+                    new_device = QString("%1-%2")
+                                         .arg(hdhr_device_list[0].device_id, 8, 16)
+                                         .arg(tunerid);
+                }
+                else
+                {
+
+                    // Check for IP address;
+
+                    struct in_addr address;
+                    uint tmp_device_ip;
+                    if (inet_aton(device.toUtf8(), &address))
+                    {
+                        tmp_device_ip = ntohl(address.s_addr);
+                        int i;
+                        for (i = 0; i < hdhr_device_count; i++)
+                        {
+                            if (tmp_device_ip == hdhr_device_list[i].ip_addr)
+                            {
+                                new_device = QString("%1-%2")
+                                                     .arg(hdhr_device_list[1].device_id, 8, 16)
+                                                     .arg(tunerid);
+                                break;
+                            }
+                        }
+                    }
+                }
+
+                // If we identified the HDhomerun device, update it.
+                // Otherwise delete it
+
+                QString updatequery;
+                if (new_device.length() > 0)
+                {
+                    updatequery = QString("UPDATE capturecard SET videodevice = '%1', dbox2_port = 31338 "
+                                          "WHERE cardid = %2;")
+                                         .arg(new_device.toUpper())
+                                         .arg(cardid);
+                    VERBOSE(VB_GENERAL, QString("Converting card %1: HDhomerun %2 tuner %3 to %4")
+                                               .arg(cardid).arg(device)
+                                               .arg(tunerid).arg(new_device.toUpper()));
+                }
+                else
+                {
+                    updatequery = QString("DELETE FROM capturecard "
+                                          "WHERE cardid = %1;"
+                                          "AND dbox2_port = %2;")
+                                         .arg(cardid);
+                    VERBOSE(VB_IMPORTANT, QString("Couldn't find card %1: HDHomerun %2 tuner %3 - deleting")
+                                                 .arg(cardid).arg(device).arg(tunerid));
+                }
+    
+                if (!query2.exec(updatequery))
+                {
+                    MythDB::DBError(
+                        "Could not perform update for '1236'", query2);
+                    ok = false;
+                }
+            }
+        }
+
+        if (!ok)
+            return false;
+#endif // USING_HDHOMERUN
+*/
+        const char * updates[] = { NULL };
+
+        if (!performActualUpdate(updates, "1236", dbver))
+            return false;
+        VERBOSE(VB_IMPORTANT, "DBCheck: done with HDhomerun upgrade");
+    }
+
     return true;
 }
 
Index: libs/libmythtv/hdhrsignalmonitor.cpp
===================================================================
--- libs/libmythtv/hdhrsignalmonitor.cpp	(revision 20746)
+++ libs/libmythtv/hdhrsignalmonitor.cpp	(working copy)
@@ -18,6 +18,7 @@
 
 #include "hdhrchannel.h"
 #include "hdhrrecorder.h"
+#include "hdhrstreamhandler.h"
 
 #define LOC QString("HDHRSM(%1): ").arg(channel->GetDevice())
 #define LOC_ERR QString("HDHRSM(%1), Error: ").arg(channel->GetDevice())
@@ -39,15 +40,15 @@
 HDHRSignalMonitor::HDHRSignalMonitor(
     int db_cardnum, HDHRChannel* _channel, uint64_t _flags) :
     DTVSignalMonitor(db_cardnum, _channel, _flags),
-    dtvMonitorRunning(false)
+    streamHandlerStarted(false), streamHandler(NULL)
 {
     VERBOSE(VB_CHANNEL, LOC + "ctor");
 
-    _channel->DelAllPIDs();
-
     signalStrength.SetThreshold(45);
 
     AddFlags(kSigMon_WaitForSig);
+
+    streamHandler = HDHRStreamHandler::Get(_channel->GetDevice());
 }
 
 /** \fn HDHRSignalMonitor::~HDHRSignalMonitor()
@@ -57,6 +58,7 @@
 {
     VERBOSE(VB_CHANNEL, LOC + "dtor");
     Stop();
+    HDHRStreamHandler::Return(streamHandler);
 }
 
 /** \fn HDHRSignalMonitor::Stop(void)
@@ -66,133 +68,21 @@
 {
     VERBOSE(VB_CHANNEL, LOC + "Stop() -- begin");
     SignalMonitor::Stop();
-    if (dtvMonitorRunning)
-    {
-        dtvMonitorRunning = false;
-        pthread_join(table_monitor_thread, NULL);
-    }
+    if (GetStreamData())
+        streamHandler->RemoveListener(GetStreamData());
+    streamHandlerStarted = false;
+
     VERBOSE(VB_CHANNEL, LOC + "Stop() -- end");
 }
 
-void *HDHRSignalMonitor::TableMonitorThread(void *param)
+HDHRChannel *HDHRSignalMonitor::GetHDHRChannel(void)
 {
-    HDHRSignalMonitor *mon = (HDHRSignalMonitor*) param;
-    mon->RunTableMonitor();
-    return NULL;
+    return dynamic_cast<HDHRChannel*>(channel);
 }
 
-bool HDHRSignalMonitor::UpdateFiltersFromStreamData(void)
-{
-    vector<int> add_pids;
-    vector<int> del_pids;
-
-    if (!GetStreamData())
-        return false;
-
-    UpdateListeningForEIT();
-
-    const pid_map_t &listening = GetStreamData()->ListeningPIDs();
-
-    // PIDs that need to be added..
-    pid_map_t::const_iterator lit = listening.constBegin();
-    for (; lit != listening.constEnd(); ++lit)
-        if (*lit && (filters.find(lit.key()) == filters.end()))
-            add_pids.push_back(lit.key());
-
-    // PIDs that need to be removed..
-    FilterMap::const_iterator fit = filters.constBegin();
-    for (; fit != filters.constEnd(); ++fit)
-        if (listening.find(fit.key()) == listening.end())
-            del_pids.push_back(fit.key());
-
-    HDHRChannel *hdhr = dynamic_cast<HDHRChannel*>(channel);
-    if (!hdhr)
-        return false;
-
-    // Remove PIDs
-    bool ok = true;
-    vector<int>::iterator dit = del_pids.begin();
-    for (; dit != del_pids.end(); ++dit)
-    {
-        ok &= hdhr->DelPID(*dit);
-        filters.erase(filters.find(*dit));
-    }
-
-    // Add PIDs
-    vector<int>::iterator ait = add_pids.begin();
-    for (; ait != add_pids.end(); ++ait)
-    {
-        ok &= hdhr->AddPID(*ait);
-        filters[*ait] = 1;
-    }
-
-    return ok;
-}
-
-void HDHRSignalMonitor::RunTableMonitor(void)
-{
-    dtvMonitorRunning = true;
-
-    struct hdhomerun_video_sock_t *_video_socket;
-    _video_socket = hdhomerun_video_create(0, VIDEO_DATA_BUFFER_SIZE_1S);
-    if (!_video_socket)
-    {
-        VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to get video socket");
-        return;
-    }
-
-    HDHRChannel *hdrc = dynamic_cast<HDHRChannel*>(channel);
-    if (!hdrc)
-        return;
-
-    uint localPort = hdhomerun_video_get_local_port(_video_socket);
-    if (!hdrc->DeviceSetTarget(localPort))
-    {
-        hdhomerun_video_destroy(_video_socket);
-        VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to set target");
-        return;
-    }
-
-    VERBOSE(VB_CHANNEL, LOC + "RunTableMonitor(): " +
-            QString("begin (# of pids %1)")
-            .arg(GetStreamData()->ListeningPIDs().size()));
-
-    while (dtvMonitorRunning && GetStreamData())
-    {
-        UpdateFiltersFromStreamData();
-
-        size_t data_length;
-        unsigned char *data_buffer =
-            hdhomerun_video_recv(_video_socket,
-                                         VIDEO_DATA_BUFFER_SIZE_1S / 5,
-                                         &data_length);
-
-        if (data_buffer)
-        {
-            GetStreamData()->ProcessData(data_buffer, data_length);
-            continue;
-        }
-
-        usleep(2500);
-    }
-
-    hdrc->DeviceClearTarget();
-    hdhomerun_video_destroy(_video_socket);
-
-    VERBOSE(VB_CHANNEL, LOC + "RunTableMonitor(): -- shutdown");
-
-    // TODO teardown PID filters here
-
-    VERBOSE(VB_CHANNEL, LOC + "RunTableMonitor(): -- end");
-}
-
-/** \fn HDHRSignalMonitor::UpdateValues()
+/** \fn HDHRSignalMonitor::UpdateValues(void)
  *  \brief Fills in frontend stats and emits status Qt signals.
  *
- *   This function uses five ioctl's FE_READ_SNR, FE_READ_SIGNAL_STRENGTH
- *   FE_READ_BER, FE_READ_UNCORRECTED_BLOCKS, and FE_READ_STATUS to obtain
- *   statistics from the frontend.
- *
  *   This is automatically called by MonitorLoop(), after Start()
  *   has been used to start the signal monitoring thread.
  */
@@ -201,18 +91,19 @@
     if (!running || exit)
         return;
 
-    if (dtvMonitorRunning)
+    if (streamHandlerStarted)
     {
         EmitStatus();
         if (IsAllGood())
             SendMessageAllGood();
+
         // TODO dtv signals...
 
         update_done = true;
         return;
     }
 
-    QString msg = ((HDHRChannel*)channel)->TunerGet("status");
+    QString msg = streamHandler->GetTunerStatus();
     //ss  = signal strength,        [0,100]
     //snq = signal to noise quality [0,100]
     //seq = signal error quality    [0,100]
@@ -250,17 +141,8 @@
                    kDTVSigMon_WaitForMGT | kDTVSigMon_WaitForVCT |
                    kDTVSigMon_WaitForNIT | kDTVSigMon_WaitForSDT))
     {
-        pthread_create(&table_monitor_thread, NULL,
-                       TableMonitorThread, this);
-
-        VERBOSE(VB_CHANNEL, LOC + "UpdateValues() -- "
-                "Waiting for table monitor to start");
-
-        while (!dtvMonitorRunning)
-            usleep(50);
-
-        VERBOSE(VB_CHANNEL, LOC + "UpdateValues() -- "
-                "Table monitor started");
+        streamHandler->AddListener(GetStreamData());
+        streamHandlerStarted = true;
     }
 
     update_done = true;
Index: libs/libmythtv/hdhrrecorder.cpp
===================================================================
--- libs/libmythtv/hdhrrecorder.cpp	(revision 20746)
+++ libs/libmythtv/hdhrrecorder.cpp	(working copy)
@@ -5,8 +5,9 @@
  *  Distributed as part of MythTV under GPL v2 and later.
  */
 
-// C includes
+// POSIX includes
 #include <pthread.h>
+#include <fcntl.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -14,7 +15,6 @@
 #include <arpa/inet.h>
 #include <netdb.h>
 #include <sys/time.h>
-#include <fcntl.h>
 
 // C++ includes
 #include <iostream>
@@ -23,24 +23,30 @@
 
 // MythTV includes
 #include "RingBuffer.h"
-#include "hdhrchannel.h"
-#include "hdhrrecorder.h"
 #include "atsctables.h"
 #include "atscstreamdata.h"
 #include "dvbstreamdata.h"
 #include "eithelper.h"
 #include "tv_rec.h"
 
+// MythTV HDHR includes
+#include "hdhrchannel.h"
+#include "hdhrrecorder.h"
+#include "hdhrstreamhandler.h"
+
 #define LOC QString("HDHRRec(%1): ").arg(tvrec->GetCaptureCardNum())
+#define LOC_WARN QString("HDHRRec(%1), Warning: ") \
+                     .arg(tvrec->GetCaptureCardNum())
 #define LOC_ERR QString("HDHRRec(%1), Error: ") \
                     .arg(tvrec->GetCaptureCardNum())
 
 HDHRRecorder::HDHRRecorder(TVRec *rec, HDHRChannel *channel)
     : DTVRecorder(rec),
-      _channel(channel),        _video_socket(NULL),
+      _channel(channel),        _stream_handler(NULL),
       _stream_data(NULL),
+      _pid_lock(QMutex::Recursive),
       _input_pat(NULL),         _input_pmt(NULL),
-      _reset_pid_filters(false),_pid_lock(QMutex::Recursive)
+      _has_no_av(false)
 {
 }
 
@@ -91,109 +97,79 @@
     // HACK -- end
 }
 
-bool HDHRRecorder::Open(void)
+void HDHRRecorder::HandleSingleProgramPAT(ProgramAssociationTable *pat)
 {
-    VERBOSE(VB_RECORD, LOC + "Open()");
-    if (_video_socket)
+    if (!pat)
     {
-        VERBOSE(VB_RECORD, LOC + "Card already open (recorder)");
-        return true;
+        VERBOSE(VB_RECORD, LOC + "HandleSingleProgramPAT(NULL)");
+        return;
     }
 
-    /* Calculate buffer size */
-    uint buffersize = gContext->GetNumSetting(
-        "HDRingbufferSize", 50 * TSPacket::SIZE) * 1024;
-    buffersize /= VIDEO_DATA_PACKET_SIZE;
-    buffersize *= VIDEO_DATA_PACKET_SIZE;
+    if (!ringBuffer)
+        return;
 
-    // Buffer should be at least about 1MB..
-    buffersize = max(49 * TSPacket::SIZE * 128, buffersize);
+    uint posA[2] = { ringBuffer->GetWritePosition(), _payload_buffer.size() };
 
-    /* Create TS socket. */
-    _video_socket = hdhomerun_video_create(0, buffersize);
-    if (!_video_socket)
+    if (pat)
     {
-        VERBOSE(VB_IMPORTANT, LOC + "Open() failed to open socket");
-        return false;
+        uint next_cc = (pat->tsheader()->ContinuityCounter()+1)&0xf;
+        pat->tsheader()->SetContinuityCounter(next_cc);
+        DTVRecorder::BufferedWrite(*(reinterpret_cast<TSPacket*>(pat->tsheader())));
     }
 
-    /* Success. */
-    return true;
-}
+    uint posB[2] = { ringBuffer->GetWritePosition(), _payload_buffer.size() };
 
-/** \fn HDHRRecorder::StartData(void)
- *  \brief Configure device to send video.
- */
-bool HDHRRecorder::StartData(void)
-{
-    VERBOSE(VB_RECORD, LOC + "StartData()");
-    uint localPort = hdhomerun_video_get_local_port(_video_socket);
-    return _channel->DeviceSetTarget(localPort);
+    if (posB[0] + posB[1] * TSPacket::SIZE > 
+        posA[0] + posA[1] * TSPacket::SIZE)
+    {
+        VERBOSE(VB_RECORD, LOC + "Wrote PAT @"
+                << posA[0] << " + " << (posA[1] * TSPacket::SIZE));
+    }
+    else
+    {
+        VERBOSE(VB_RECORD, LOC + "Saw PAT but did not write to disk yet");
+    }
 }
 
-void HDHRRecorder::Close(void)
+void HDHRRecorder::HandleSingleProgramPMT(ProgramMapTable *pmt)
 {
-    VERBOSE(VB_RECORD, LOC + "Close()");
-    if (_video_socket)
+    if (!pmt)
     {
-        hdhomerun_video_destroy(_video_socket);
-        _video_socket = NULL;
+        VERBOSE(VB_RECORD, LOC + "HandleSingleProgramPMT(NULL)");
+        return;
     }
-}
 
-void HDHRRecorder::ProcessTSData(const uint8_t *buffer, int len)
-{
-    QMutexLocker locker(&_pid_lock);
-    const uint8_t *data = buffer;
-    const uint8_t *end = buffer + len;
+    // collect stream types for H.264 (MPEG-4 AVC) keyframe detection
+    for (uint i = 0; i < pmt->StreamCount(); i++)
+        _stream_id[pmt->StreamPID(i)] = pmt->StreamType(i);
 
-    while (data + 188 <= end)
-    {
-        if (data[0] != 0x47)
-        {
-            return;
-        }
+    if (!ringBuffer)
+        return;
 
-        const TSPacket *tspacket = reinterpret_cast<const TSPacket*>(data);
-        ProcessTSPacket(*tspacket);
+    unsigned char buf[8 * 1024];
+    uint next_cc = (pmt->tsheader()->ContinuityCounter()+1)&0xf;
+    pmt->tsheader()->SetContinuityCounter(next_cc);
+    uint size = pmt->WriteAsTSPackets(buf, next_cc);
 
-        data += 188;
-    }
-}
+    uint posA[2] = { ringBuffer->GetWritePosition(), _payload_buffer.size() };
 
-void HDHRRecorder::SetStreamData(MPEGStreamData *data)
-{
-    if (data == _stream_data)
-        return;
+    for (uint i = 0; i < size ; i += TSPacket::SIZE)
+        DTVRecorder::BufferedWrite(*(reinterpret_cast<TSPacket*>(&buf[i])));
 
-    MPEGStreamData *old_data = _stream_data;
-    _stream_data = data;
-    if (old_data)
-        delete old_data;
+    uint posB[2] = { ringBuffer->GetWritePosition(), _payload_buffer.size() };
 
-    if (data)
+    if (posB[0] + posB[1] * TSPacket::SIZE >
+        posA[0] + posA[1] * TSPacket::SIZE)
     {
-        data->AddMPEGSPListener(this);
-        data->AddMPEGListener(this);
-
-        ATSCStreamData *atsc = dynamic_cast<ATSCStreamData*>(data);
-        DVBStreamData  *dvb  = dynamic_cast<DVBStreamData*>(data);
-
-        if (atsc && atsc->DesiredMinorChannel())
-            atsc->SetDesiredChannel(atsc->DesiredMajorChannel(),
-                                    atsc->DesiredMinorChannel());
-        else if (dvb)
-            dvb->AddDVBMainListener(this);
-        else if (data->DesiredProgram() >= 0)
-            data->SetDesiredProgram(data->DesiredProgram());
+        VERBOSE(VB_RECORD, LOC + "Wrote PMT @"
+                << posA[0] << " + " << (posA[1] * TSPacket::SIZE));
     }
+    else
+    {
+        VERBOSE(VB_RECORD, LOC + "Saw PMT but did not write to disk yet");
+    }
 }
 
-ATSCStreamData *HDHRRecorder::GetATSCStreamData(void)
-{
-    return dynamic_cast<ATSCStreamData*>(_stream_data);
-}
-
 void HDHRRecorder::HandlePAT(const ProgramAssociationTable *_pat)
 {
     if (!_pat)
@@ -221,7 +197,13 @@
     _input_pat = new ProgramAssociationTable(*_pat);
     delete oldpat;
 
-    _reset_pid_filters = true;
+    // Listen for the other PMTs for faster channel switching
+    for (uint i = 0; _input_pat && (i < _input_pat->ProgramCount()); i++)
+    {
+        uint pmt_pid = _input_pat->ProgramPID(i);
+        if (!_stream_data->IsListeningPID(pmt_pid))
+            _stream_data->AddListeningPID(pmt_pid);
+    }
 }
 
 void HDHRRecorder::HandlePMT(uint progNum, const ProgramMapTable *_pmt)
@@ -233,36 +215,21 @@
         VERBOSE(VB_RECORD, LOC + "SetPMT("<<progNum<<")");
         ProgramMapTable *oldpmt = _input_pmt;
         _input_pmt = new ProgramMapTable(*_pmt);
-        delete oldpmt;
 
-        _reset_pid_filters = true;
-    }
-}
+        QString sistandard = _channel->GetSIStandard();
 
-void HDHRRecorder::HandleSingleProgramPAT(ProgramAssociationTable *pat)
-{
-    if (!pat)
-        return;
+        bool has_no_av = true;
+        for (uint i = 0; i < _input_pmt->StreamCount() && has_no_av; i++)
+        {
+            has_no_av &= !_input_pmt->IsVideo(i, sistandard);
+            has_no_av &= !_input_pmt->IsAudio(i, sistandard);
+        }
+        _has_no_av = has_no_av;
 
-    int next = (pat->tsheader()->ContinuityCounter()+1)&0xf;
-    pat->tsheader()->SetContinuityCounter(next);
-    BufferedWrite(*(reinterpret_cast<TSPacket*>(pat->tsheader())));
+        delete oldpmt;
+    }
 }
 
-void HDHRRecorder::HandleSingleProgramPMT(ProgramMapTable *pmt)
-{
-    if (!pmt)
-        return;
-
-    unsigned char buf[8 * 1024];
-    uint next_cc = (pmt->tsheader()->ContinuityCounter()+1)&0xf;
-    pmt->tsheader()->SetContinuityCounter(next_cc);
-    uint size = pmt->WriteAsTSPackets(buf, next_cc);
-
-    for (uint i = 0; i < size ; i += TSPacket::SIZE)
-        DTVRecorder::BufferedWrite(*(reinterpret_cast<TSPacket*>(&buf[i])));
-}
-
 /** \fn HDHRRecorder::HandleMGT(const MasterGuideTable*)
  *  \brief Processes Master Guide Table, by enabling the 
  *         scanning of all PIDs listed.
@@ -280,48 +247,69 @@
 }
 */
 
-bool HDHRRecorder::ProcessTSPacket(const TSPacket& tspacket)
+bool HDHRRecorder::Open(void)
 {
-    bool ok = !tspacket.TransportError();
-    if (ok && !tspacket.ScramplingControl())
+    if (IsOpen())
     {
-        if (tspacket.HasAdaptationField())
-            GetStreamData()->HandleAdaptationFieldControl(&tspacket);
-        if (tspacket.HasPayload())
-        {
-            const unsigned int lpid = tspacket.PID();
+        VERBOSE(VB_GENERAL, LOC_WARN + "Card already open");
+        return true;
+    }
 
-            if ((GetStreamData()->VideoPIDSingleProgram() > 0x1fff) &&
-                _wait_for_keyframe_option)
-            {
-                _wait_for_keyframe_option = false;
-            }
+    memset(_stream_id,  0, sizeof(_stream_id));
+    memset(_pid_status, 0, sizeof(_pid_status));
+    memset(_continuity_counter, 0xff, sizeof(_continuity_counter));
 
-            // Pass or reject frames based on PID, and parse info from them
-            if (lpid == GetStreamData()->VideoPIDSingleProgram())
-            {
-                //cerr<<"v";
-                _buffer_packets = !FindMPEG2Keyframes(&tspacket);
-                BufferedWrite(tspacket);
-            }
-            else if (GetStreamData()->IsAudioPID(lpid))
-            {
-                //cerr<<"a";
-                _buffer_packets = !FindAudioKeyframes(&tspacket);
-                BufferedWrite(tspacket);
-            }
-            else if (GetStreamData()->IsListeningPID(lpid))
-            {
-                //cerr<<"t";
-                GetStreamData()->HandleTSTables(&tspacket);
-            }
-            else if (GetStreamData()->IsWritingPID(lpid))
-                BufferedWrite(tspacket);
-        }
+    _stream_handler = HDHRStreamHandler::Get(_channel->GetDevice());
+
+    VERBOSE(VB_RECORD, LOC + "HDHR opened successfully");
+
+    return true;
+}
+
+void HDHRRecorder::Close(void)
+{
+    VERBOSE(VB_RECORD, LOC + "Close() -- begin");
+
+    if (IsOpen())
+        HDHRStreamHandler::Return(_stream_handler); 
+
+    VERBOSE(VB_RECORD, LOC + "Close() -- end");
+}
+
+void HDHRRecorder::SetStreamData(MPEGStreamData *data)
+{
+    if (data == _stream_data)
+        return;
+
+    MPEGStreamData *old_data = _stream_data;
+    _stream_data = data;
+    if (old_data)
+        delete old_data;
+
+    if (data)
+    {
+        data->AddMPEGSPListener(this);
+        data->AddMPEGListener(this);
+
+        DVBStreamData *dvb = dynamic_cast<DVBStreamData*>(data);
+        if (dvb)
+            dvb->AddDVBMainListener(this);
+
+        ATSCStreamData *atsc = dynamic_cast<ATSCStreamData*>(data);
+
+        if (atsc && atsc->DesiredMinorChannel())
+            atsc->SetDesiredChannel(atsc->DesiredMajorChannel(),
+                                    atsc->DesiredMinorChannel());
+        else if (data->DesiredProgram() >= 0)
+            data->SetDesiredProgram(data->DesiredProgram());
     }
-    return ok;
 }
 
+ATSCStreamData *HDHRRecorder::GetATSCStreamData(void)
+{
+    return dynamic_cast<ATSCStreamData*>(_stream_data);
+}
+
 void HDHRRecorder::StartRecording(void)
 {
     VERBOSE(VB_RECORD, LOC + "StartRecording -- begin");
@@ -337,160 +325,202 @@
     _request_recording = true;
     _recording = true;
 
-    if (!StartData())
-    {
-        VERBOSE(VB_IMPORTANT, LOC_ERR + "Starting recording "
-                "(set target failed). Aborting.");
-        Close();
-        _error = true;
-        VERBOSE(VB_RECORD, LOC + "StartRecording -- end 2");
-        return;
-    }
+    // Make sure the first things in the file are a PAT & PMT
+    bool tmp = _wait_for_keyframe_option;
+    _wait_for_keyframe_option = false;
+    HandleSingleProgramPAT(_stream_data->PATSingleProgram());
+    HandleSingleProgramPMT(_stream_data->PMTSingleProgram());
+    _wait_for_keyframe_option = tmp;
 
-    hdhomerun_video_flush(_video_socket);
+    _stream_data->AddAVListener(this);
+    _stream_data->AddWritingListener(this);
+    _stream_handler->AddListener(_stream_data);
+    
     while (_request_recording && !_error)
     {
+        usleep(50000);
+
         if (PauseAndWait())
             continue;
 
-        if (_stream_data)
+        if (!_input_pmt)
         {
-            QMutexLocker read_lock(&_pid_lock);
-            _reset_pid_filters |= _stream_data->HasEITPIDChanges(_eit_pids);
+            VERBOSE(VB_GENERAL, LOC_WARN +
+                    "Recording will not commence until a PMT is set.");
+            usleep(5000);
+            continue;
         }
 
-        if (_reset_pid_filters)
+        if (!_stream_handler->IsRunning())
         {
-            _reset_pid_filters = false;
-            VERBOSE(VB_RECORD, LOC + "Resetting Demux Filters");
-            AdjustFilters();
+            _error = true;
+
+            VERBOSE(VB_IMPORTANT, LOC_ERR +
+                    "Stream handler died unexpectedly.");
         }
-
-        size_t read_size = 64 * 1024; // read about 64KB
-        read_size /= VIDEO_DATA_PACKET_SIZE;
-        read_size *= VIDEO_DATA_PACKET_SIZE;
-
-        size_t data_length;
-        unsigned char *data_buffer =
-            hdhomerun_video_recv(_video_socket, read_size, &data_length);
-        if (!data_buffer)
-        {
-            usleep(5000);
-	    continue;
-	}
-
-        ProcessTSData(data_buffer, data_length);
     }
 
     VERBOSE(VB_RECORD, LOC + "StartRecording -- ending...");
 
-    _channel->DeviceClearTarget();
+    _stream_handler->RemoveListener(_stream_data);
+    _stream_data->RemoveWritingListener(this);
+    _stream_data->RemoveAVListener(this);
+
     Close();
 
     FinishRecording();
+
     _recording = false;
 
     VERBOSE(VB_RECORD, LOC + "StartRecording -- end");
 }
 
-bool HDHRRecorder::AdjustFilters(void)
+void HDHRRecorder::ResetForNewFile(void)
 {
-    QMutexLocker change_lock(&_pid_lock);
+    DTVRecorder::ResetForNewFile();
 
-    if (!_channel)
+    memset(_stream_id,  0, sizeof(_stream_id));
+    memset(_pid_status, 0, sizeof(_pid_status));
+    memset(_continuity_counter, 0xff, sizeof(_continuity_counter));
+}
+
+void HDHRRecorder::StopRecording(void)
+{
+    _request_recording = false;
+    while (_recording)
+        usleep(2000);
+}
+
+bool HDHRRecorder::PauseAndWait(int timeout)
+{
+    if (request_pause)
     {
-        VERBOSE(VB_IMPORTANT, LOC_ERR + "AdjustFilters() no channel");
-        return false;
+        QMutex waitlock;
+        if (!paused)
+        {
+            assert(_stream_handler);
+            assert(_stream_data);
+
+            _stream_handler->RemoveListener(_stream_data);
+
+            paused = true;
+            pauseWait.wakeAll();
+            if (tvrec)
+                tvrec->RecorderPaused();
+        }
+        waitlock.lock();
+        unpauseWait.wait(&waitlock, timeout);
     }
 
-    if (!_input_pat || !_input_pmt)
+    if (!request_pause && paused)
     {
-        VERBOSE(VB_IMPORTANT, LOC + "AdjustFilters() no pmt or no pat");
-        return false;
+        paused = false;
+
+        assert(_stream_handler);
+        assert(_stream_data);
+
+        _stream_handler->AddListener(_stream_data);
     }
 
-    uint_vec_t add_pid;
+    return paused;
+}
 
-    add_pid.push_back(MPEG_PAT_PID);
-    _stream_data->AddListeningPID(MPEG_PAT_PID);
+bool HDHRRecorder::ProcessVideoTSPacket(const TSPacket &tspacket)
+{
+    uint streamType = _stream_id[tspacket.PID()];
 
-    for (uint i = 0; i < _input_pat->ProgramCount(); i++)
+    // Check for keyframes and count frames
+    if (streamType == StreamID::H264Video)
     {
-        add_pid.push_back(_input_pat->ProgramPID(i));
-        _stream_data->AddListeningPID(_input_pat->ProgramPID(i));
+        _buffer_packets = !FindH264Keyframes(&tspacket);
+        if (!_seen_sps)
+            return true;
     }
-
-    // Record the streams in the PMT...
-    bool need_pcr_pid = true;
-    for (uint i = 0; i < _input_pmt->StreamCount(); i++)
+    else
     {
-        add_pid.push_back(_input_pmt->StreamPID(i));
-        need_pcr_pid &= (_input_pmt->StreamPID(i) != _input_pmt->PCRPID());
-        _stream_data->AddWritingPID(_input_pmt->StreamPID(i));
+        _buffer_packets = !FindMPEG2Keyframes(&tspacket);
     }
 
-    if (need_pcr_pid && (_input_pmt->PCRPID()))
+    return ProcessAVTSPacket(tspacket);
+}
+
+bool HDHRRecorder::ProcessAudioTSPacket(const TSPacket &tspacket)
+{
+    _buffer_packets = !FindAudioKeyframes(&tspacket);
+    return ProcessAVTSPacket(tspacket);
+}
+
+/// Common code for processing either audio or video packets
+bool HDHRRecorder::ProcessAVTSPacket(const TSPacket &tspacket)
+{
+    const uint pid = tspacket.PID();
+    // Sync recording start to first keyframe
+    if (_wait_for_keyframe_option && _first_keyframe < 0)
+        return true;
+
+    // Sync streams to the first Payload Unit Start Indicator
+    // _after_ first keyframe iff _wait_for_keyframe_option is true
+    if (!(_pid_status[pid] & kPayloadStartSeen) && tspacket.HasPayload())
     {
-        add_pid.push_back(_input_pmt->PCRPID());
-        _stream_data->AddWritingPID(_input_pmt->PCRPID());
+        if (!tspacket.PayloadStart())
+            return true; // not payload start - drop packet
+
+        VERBOSE(VB_RECORD,
+                QString("PID 0x%1 Found Payload Start").arg(pid,0,16));
+
+        _pid_status[pid] |= kPayloadStartSeen;
     }
 
-    // Adjust for EIT
-    AdjustEITPIDs();
-    for (uint i = 0; i < _eit_pids.size(); i++)
+    BufferedWrite(tspacket);
+
+    return true;
+}
+
+bool HDHRRecorder::ProcessTSPacket(const TSPacket &tspacket)
+{
+    // Only create fake keyframe[s] if there are no audio/video streams
+    if (_input_pmt && _has_no_av)
     {
-        add_pid.push_back(_eit_pids[i]);
-        _stream_data->AddListeningPID(_eit_pids[i]);
+        _buffer_packets = !FindOtherKeyframes(&tspacket);
     }
+    else
+    {
+        // There are audio/video streams. Only write the packet
+        // if audio/video key-frames have been found
+        if (_wait_for_keyframe_option && _first_keyframe < 0)
+            return true;
 
-    // Delete filters for pids we no longer wish to monitor
-    vector<uint>::const_iterator it;
-    vector<uint> pids = _channel->GetPIDs();
-    for (it = pids.begin(); it != pids.end(); ++it)
-    {
-        if (find(add_pid.begin(), add_pid.end(), *it) == add_pid.end())
-        {
-            _stream_data->RemoveListeningPID(*it);
-            _stream_data->RemoveWritingPID(*it);
-            _channel->DelPID(*it, false);
-        }
+        _buffer_packets = true;
     }
 
-    for (it = add_pid.begin(); it != add_pid.end(); ++it)
-        _channel->AddPID(*it, false);
+    BufferedWrite(tspacket);
 
-    _channel->UpdateFilters();
-
-    return add_pid.size();
+    return true;
 }
 
-/** \fn HDHRRecorder::AdjustEITPIDs(void)
- *  \brief Adjusts EIT PID monitoring to monitor the right number of EIT PIDs.
- */
-bool HDHRRecorder::AdjustEITPIDs(void)
+void HDHRRecorder::BufferedWrite(const TSPacket &tspacket)
 {
-    bool changes = false;
-    uint_vec_t add, del;
+    // Care must be taken to make sure that the packet actually gets written
+    // as the decision to actually write it has already been made
 
-    QMutexLocker change_lock(&_pid_lock);
+    // Do we have to buffer the packet for exact keyframe detection?
+    if (_buffer_packets)
+    {
+        int idx = _payload_buffer.size();
+        _payload_buffer.resize(idx + TSPacket::SIZE);
+        memcpy(&_payload_buffer[idx], tspacket.data(), TSPacket::SIZE);
+        return;
+    }
 
-    if (GetStreamData()->HasEITPIDChanges(_eit_pids))
-        changes = GetStreamData()->GetEITPIDChanges(_eit_pids, add, del);
-
-    if (!changes)
-        return false;
-
-    for (uint i = 0; i < del.size(); i++)
+    // We are free to write the packet, but if we have buffered packet[s]
+    // we have to write them first...
+    if (!_payload_buffer.empty())
     {
-        uint_vec_t::iterator it;
-        it = find(_eit_pids.begin(), _eit_pids.end(), del[i]);
-        if (it != _eit_pids.end())
-            _eit_pids.erase(it);
+        if (ringBuffer)
+            ringBuffer->Write(&_payload_buffer[0], _payload_buffer.size());
+        _payload_buffer.clear();
     }
 
-    for (uint i = 0; i < add.size(); i++)
-        _eit_pids.push_back(add[i]);
-
-    return true;
+    if (ringBuffer)
+        ringBuffer->Write(tspacket.data(), TSPacket::SIZE);
 }
-
Index: libs/libmythtv/hdhrchannel.cpp
===================================================================
--- libs/libmythtv/hdhrchannel.cpp	(revision 20746)
+++ libs/libmythtv/hdhrchannel.cpp	(working copy)
@@ -25,36 +25,22 @@
 #include "hdhrchannel.h"
 #include "videosource.h"
 #include "channelutil.h"
+#include "hdhrstreamhandler.h"
 
+// HDHomeRun header
+#include "hdhomerun.h"
+
 #define DEBUG_PID_FILTERS
 
 #define LOC     QString("HDHRChan(%1): ").arg(GetDevice())
 #define LOC_ERR QString("HDHRChan(%1), Error: ").arg(GetDevice())
 
-HDHRChannel::HDHRChannel(TVRec *parent, const QString &device, uint tuner)
-    : DTVChannel(parent),       _control_socket(NULL),
-      _device_id(0),            _device_ip(0),
-      _tuner(tuner),            _lock(QMutex::Recursive)
+HDHRChannel::HDHRChannel(TVRec *parent, const QString &device)
+    : DTVChannel(parent),       _stream_handler(NULL),
+      _device_id(device),       _lock(QMutex::Recursive),
+      tune_lock(QMutex::Recursive),
+      hw_lock(QMutex::Recursive)
 {
-    bool valid;
-    _device_id = device.toUInt(&valid, 16);
-
-    if (valid && hdhomerun_discover_validate_device_id(_device_id))
-	return;
-
-    /* Otherwise, is it a valid IP address? */
-    struct in_addr address;
-    if (inet_aton(device.toLatin1().constData(), &address))
-    {
-	_device_ip = ntohl(address.s_addr);
-	return;
-    }
-
-    /* Invalid, use wildcard device ID. */
-    VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Invalid DeviceID '%1'")
-	    .arg(device));
-
-    _device_id = HDHOMERUN_DEVICE_ID_WILDCARD;
 }
 
 HDHRChannel::~HDHRChannel(void)
@@ -64,191 +50,56 @@
 
 bool HDHRChannel::Open(void)
 {
+    VERBOSE(VB_CHANNEL, LOC + "Opening HDHR channel");
+
+    QMutexLocker locker(&hw_lock);
+    
     if (IsOpen())
         return true;
 
-    if (!FindDevice())
-        return false;
+    _stream_handler = HDHRStreamHandler::Get(_device_id);
 
     if (!InitializeInputs())
-        return false;
-
-    return (_device_ip != 0) && Connect();
-}
-
-void HDHRChannel::Close(void)
-{
-    if (_control_socket)
     {
-        hdhomerun_control_destroy(_control_socket);
-        _control_socket = NULL;
-    }
-}
-
-bool HDHRChannel::EnterPowerSavingMode(void)
-{
-    return QString::null != TunerSet("channel", "none", false);
-}
-
-bool HDHRChannel::FindDevice(void)
-{
-    if (!_device_id)
-        return _device_ip;
-
-    _device_ip = 0;
-
-    /* Discover. */
-    struct hdhomerun_discover_device_t result;
-    int ret = hdhomerun_discover_find_devices_custom(
-                  0, HDHOMERUN_DEVICE_TYPE_WILDCARD, _device_id, &result, 1);
-    if (ret < 0)
-    {
-        VERBOSE(VB_IMPORTANT,
-                LOC_ERR + "Unable to send discovery request" + ENO);
+        Close();
         return false;
     }
-    if (ret == 0)
-    {
-        VERBOSE(VB_IMPORTANT, LOC_ERR + QString("device not found"));
-        return false;
-    }
 
-    /* Found. */
-    _device_ip = result.ip_addr;
-
-    VERBOSE(VB_IMPORTANT, LOC +
-            QString("device found at address %1.%2.%3.%4")
-            .arg((_device_ip>>24) & 0xFF).arg((_device_ip>>16) & 0xFF)
-            .arg((_device_ip>> 8) & 0xFF).arg((_device_ip>> 0) & 0xFF));
-
-    return true;
+    return _stream_handler->IsConnected();
 }
 
-bool HDHRChannel::Connect(void)
+void HDHRChannel::Close(void)
 {
-    _control_socket = hdhomerun_control_create(_device_id, _device_ip);
-    if (!_control_socket)
-    {
-        VERBOSE(VB_IMPORTANT, LOC_ERR + "Unable to create control socket");
-        return false;
-    }
+    VERBOSE(VB_CHANNEL, LOC + "Closing HDHR channel");
 
-    if (hdhomerun_control_get_local_addr(_control_socket) == 0)
-    {
-        VERBOSE(VB_IMPORTANT, LOC_ERR + "Unable to connect to device");
-        return false;
-    }
+        if (!IsOpen())
+        return; // this caller didn't have it open in the first place..
 
-    VERBOSE(VB_CHANNEL, LOC + "Successfully connected to device");
-    return true;
+    HDHRStreamHandler::Return(_stream_handler);
 }
 
-QString HDHRChannel::DeviceGet(const QString &name, bool report_error_return)
+bool HDHRChannel::EnterPowerSavingMode(void)
 {
-    QMutexLocker locker(&_lock);
-
-    if (!_control_socket)
-    {
-        VERBOSE(VB_IMPORTANT, LOC_ERR + "Get request failed (not connected)");
-        return QString::null;
-    }
-
-    char *value = NULL;
-    char *error = NULL;
-    if (hdhomerun_control_get(_control_socket, name.toLatin1().constData(),
-                              &value, &error) < 0)
-    {
-        VERBOSE(VB_IMPORTANT, LOC_ERR + "Get request failed" + ENO);
-        return QString::null;
-    }
-
-    if (report_error_return && error)
-    {
-        VERBOSE(VB_IMPORTANT, LOC_ERR +
-                QString("DeviceGet(%1): %2").arg(name).arg(error));
-
-        return QString::null;
-    }
-
-    return QString(value);
+    if (IsOpen())
+        return _stream_handler->EnterPowerSavingMode();
+    else
+        return true;
 }
 
-QString HDHRChannel::DeviceSet(const QString &name, const QString &val,
-                               bool report_error_return)
+bool HDHRChannel::IsOpen(void) const
 {
-    QMutexLocker locker(&_lock);
-
-    if (!_control_socket)
-    {
-        VERBOSE(VB_IMPORTANT, LOC_ERR + "Set request failed (not connected)");
-        return QString::null;
-    }
-
-    char *value = NULL;
-    char *error = NULL;
-    if (hdhomerun_control_set(_control_socket, name.toLatin1().constData(),
-                              val.toAscii().constData(), &value, &error) < 0)
-    {
-        VERBOSE(VB_IMPORTANT, LOC_ERR + "Set request failed" + ENO);
-
-        return QString::null;
-    }
-
-    if (report_error_return && error)
-    {
-        VERBOSE(VB_IMPORTANT, LOC_ERR +
-                QString("DeviceSet(%1 %2): %3").arg(name).arg(val).arg(error));
-
-        return QString::null;
-    }
-
-    return QString(value);
+      return _stream_handler;
 }
 
-QString HDHRChannel::TunerGet(const QString &name, bool report_error_return)
+bool HDHRChannel::Init(
+    QString &inputname, QString &startchannel, bool setchan)
 {
-    return DeviceGet(QString("/tuner%1/%2").arg(_tuner).arg(name),
-                     report_error_return);
-}
+    if (setchan && !IsOpen())
+        Open();
 
-QString HDHRChannel::TunerSet(const QString &name, const QString &value,
-                              bool report_error_return)
-{
-    return DeviceSet(QString("/tuner%1/%2").arg(_tuner).arg(name), value,
-                     report_error_return);
+    return ChannelBase::Init(inputname, startchannel, setchan);
 }
 
-bool HDHRChannel::DeviceSetTarget(unsigned short localPort)
-{
-    if (localPort == 0)
-    {
-        return false;
-    }
-
-    unsigned long localIP = hdhomerun_control_get_local_addr(_control_socket);
-    if (localIP == 0)
-    {
-        return false;
-    }
-
-    QString configValue = QString("%1.%2.%3.%4:%5")
-        .arg((localIP >> 24) & 0xFF).arg((localIP >> 16) & 0xFF)
-        .arg((localIP >>  8) & 0xFF).arg((localIP >>  0) & 0xFF)
-        .arg(localPort);
-
-    if (TunerSet("target", configValue).isEmpty())
-    {
-        return false;
-    }
-
-    return true;
-}
-
-bool HDHRChannel::DeviceClearTarget()
-{
-    return !TunerSet("target", "0.0.0.0:0").isEmpty();
-}
-
 bool HDHRChannel::SetChannelByString(const QString &channum)
 {
     QString loc = LOC + QString("SetChannelByString(%1)").arg(channum);
@@ -280,7 +131,6 @@
         return SwitchToInput(inputName, channum);
 
     ClearDTVInfo();
-    _ignore_filters = false;
 
     InputMap::const_iterator it = inputs.find(currentInputID);
     if (it == inputs.end())
@@ -349,13 +199,8 @@
     QString tmpX = curchannelname; tmpX.detach();
     inputs[currentInputID]->startChanNum = tmpX;
 
-    // Turn on the HDHomeRun program filtering if it is supported
-    // and we are tuning to an MPEG program number.
     if (mpeg_prog_num && (GetTuningMode() == "mpeg"))
-    {
-        QString pnum = QString::number(mpeg_prog_num);
-        _ignore_filters = QString::null != TunerSet("program", pnum, false);
-    }
+        _stream_handler->TuneProgram(mpeg_prog_num);
 
     return true;
 }
@@ -409,9 +254,9 @@
 
     QString chan = modulation + ':' + QString::number(frequency);
 
-    VERBOSE(VB_CHANNEL, LOC + "Tune()ing to " + chan);
+    VERBOSE(VB_CHANNEL, LOC + "Tuning to " + chan);
 
-    if (TunerSet("channel", chan).length())
+    if (_stream_handler->TuneChannel(chan))
     {
         SetSIStandard(si_std);
         return true;
@@ -419,144 +264,3 @@
 
     return false;
 }
-
-bool HDHRChannel::AddPID(uint pid, bool do_update)
-{
-    QMutexLocker locker(&_lock);
-
-    vector<uint>::iterator it;
-    it = lower_bound(_pids.begin(), _pids.end(), pid);
-    if (it != _pids.end() && *it == pid)
-    {
-#ifdef DEBUG_PID_FILTERS
-        VERBOSE(VB_CHANNEL, "AddPID(0x"<<hex<<pid<<dec<<") NOOP");
-#endif // DEBUG_PID_FILTERS
-        return true;
-    }
-
-    _pids.insert(it, pid);
-
-#ifdef DEBUG_PID_FILTERS
-    VERBOSE(VB_CHANNEL, "AddPID(0x"<<hex<<pid<<dec<<")");
-#endif // DEBUG_PID_FILTERS
-
-    if (do_update)
-        return UpdateFilters();
-    return true;
-}
-
-bool HDHRChannel::DelPID(uint pid, bool do_update)
-{
-    QMutexLocker locker(&_lock);
-
-    vector<uint>::iterator it;
-    it = lower_bound(_pids.begin(), _pids.end(), pid);
-    if (it == _pids.end())
-    {
-#ifdef DEBUG_PID_FILTERS
-        VERBOSE(VB_CHANNEL, "DelPID(0x"<<hex<<pid<<dec<<") NOOP");
-#endif // DEBUG_PID_FILTERS
-
-       return true;
-    }
-
-    if (*it == pid)
-    {
-#ifdef DEBUG_PID_FILTERS
-        VERBOSE(VB_CHANNEL, "DelPID(0x"<<hex<<pid<<dec<<") -- found");
-#endif // DEBUG_PID_FILTERS
-        _pids.erase(it);
-    }
-    else
-    {
-#ifdef DEBUG_PID_FILTERS
-        VERBOSE(VB_CHANNEL, "DelPID(0x"<<hex<<pid<<dec<<") -- failed");
-#endif // DEBUG_PID_FILTERS
-    }
-
-    if (do_update)
-        return UpdateFilters();
-    return true;
-}
-
-bool HDHRChannel::DelAllPIDs(void)
-{
-    QMutexLocker locker(&_lock);
-
-#ifdef DEBUG_PID_FILTERS
-    VERBOSE(VB_CHANNEL, "DelAllPID()");
-#endif // DEBUG_PID_FILTERS
-
-    _pids.clear();
-
-    return UpdateFilters();
-}
-
-QString filt_str(uint pid)
-{
-    uint pid0 = (pid / (16*16*16)) % 16;
-    uint pid1 = (pid / (16*16))    % 16;
-    uint pid2 = (pid / (16))        % 16;
-    uint pid3 = pid % 16;
-    return QString("0x%1%2%3%4")
-        .arg(pid0,0,16).arg(pid1,0,16)
-        .arg(pid2,0,16).arg(pid3,0,16);
-}
-
-bool HDHRChannel::UpdateFilters(void)
-{
-    QMutexLocker locker(&_lock);
-
-    QString filter = "";
-
-    vector<uint> range_min;
-    vector<uint> range_max;
-
-    if (_ignore_filters)
-        return true;
-
-    for (uint i = 0; i < _pids.size(); i++)
-    {
-        uint pid_min = _pids[i];
-        uint pid_max  = pid_min;
-        for (uint j = i + 1; j < _pids.size(); j++)
-        {
-            if (pid_max + 1 != _pids[j])
-                break;
-            pid_max++;
-            i++;
-        }
-        range_min.push_back(pid_min);
-        range_max.push_back(pid_max);
-    }
-
-    if (range_min.size() > 16)
-    {
-        range_min.resize(16);
-        uint pid_max = range_max.back();
-        range_max.resize(15);
-        range_max.push_back(pid_max);
-    }
-
-    for (uint i = 0; i < range_min.size(); i++)
-    {
-        filter += filt_str(range_min[i]);
-        if (range_min[i] != range_max[i])
-            filter += QString("-%1").arg(filt_str(range_max[i]));
-        filter += " ";
-    }
-
-    filter = filter.trimmed();
-
-    QString new_filter = TunerSet("filter", filter);
-
-#ifdef DEBUG_PID_FILTERS
-    QString msg = QString("Filter: '%1'").arg(filter);
-    if (filter != new_filter)
-        msg += QString("\n\t\t\t\t'%2'").arg(new_filter);
-
-    VERBOSE(VB_CHANNEL, msg);
-#endif // DEBUG_PID_FILTERS
-
-    return filter == new_filter;
-}
Index: libs/libmythtv/hdhrstreamhandler.h
===================================================================
--- libs/libmythtv/hdhrstreamhandler.h	(revision 0)
+++ libs/libmythtv/hdhrstreamhandler.h	(revision 0)
@@ -0,0 +1,131 @@
+// -*- Mode: c++ -*-
+
+#ifndef _HDHRSTREAMHANDLER_H_
+#define _HDHRSTREAMHANDLER_H_
+
+#include <vector>
+using namespace std;
+
+#include <QMap>
+#include <QMutex>
+
+#include "util.h"
+#include "DeviceReadBuffer.h"
+#include "mpegstreamdata.h"
+
+class QString;
+class HDHRStreamHandler;
+class DTVSignalMonitor;
+class HDHRChannel;
+class DeviceReadBuffer;
+
+// HDHomeRun headers
+#ifdef USING_HDHOMERUN
+#include "hdhomerun.h"
+#else
+struct hdhomerun_control_sock_t { int dummy; };
+#endif
+
+typedef QMap<uint,int> FilterMap;
+
+//#define RETUNE_TIMEOUT 5000
+
+class HDHRStreamHandler : public ReaderPausedCB
+{
+    friend void *run_hdhr_stream_handler_thunk(void *param);
+
+  public:
+    static HDHRStreamHandler *Get(const QString &devicename);
+    static void Return(HDHRStreamHandler * & ref);
+
+    void AddListener(MPEGStreamData *data);
+    void RemoveListener(MPEGStreamData *data);
+
+    bool IsRunning(void) const { return _running; }
+    QString GetTunerStatus(void) const;
+    bool IsConnected(void) const;
+
+    // Commands
+    bool TuneChannel(const QString &chanid);
+    bool TuneProgram(uint mpeg_prog_num);
+    bool EnterPowerSavingMode(void);
+
+
+    // ReaderPausedCB
+    virtual void ReaderPaused(int fd) { (void) fd; }
+
+  private:
+    HDHRStreamHandler(const QString &);
+    ~HDHRStreamHandler();
+
+    bool FindDevice(void);
+    bool Connect(void);
+
+    QString DeviceGet(const QString &name,
+                      bool report_error_return = true) const;
+    QString DeviceSet(const QString &name, const QString &value,
+                      bool report_error_return = true);
+
+    QString TunerGet(const QString &name,
+                     bool report_error_return = true) const;
+    QString TunerSet(const QString &name, const QString &value,
+                     bool report_error_return = true);
+
+    bool DeviceSetTarget(short unsigned int);
+    bool DeviceClearTarget(void);
+
+    bool Open(void);
+    void Close(void);
+
+    void Start(void);
+    void Stop(void);
+
+    void Run(void);
+    void RunTS(void);
+
+    void UpdateListeningForEIT(void);
+    bool UpdateFiltersFromStreamData(void);
+    bool AddPIDFilter(uint pid, bool do_update = true);
+    bool RemovePIDFilter(uint pid, bool do_update = true);
+    bool RemoveAllPIDFilters(void);
+    bool UpdateFilters(void);
+
+    void SetRunning(bool);
+
+    PIDPriority GetPIDPriority(uint pid) const;
+
+  private:
+    hdhomerun_control_sock_t  *_control_socket;
+    hdhomerun_video_sock_t    *_video_socket;
+    uint                       _device_id;
+    uint                       _device_ip;
+    uint                       _tuner;
+    QString                    _devicename;
+
+    mutable QMutex    _start_stop_lock;
+    bool              _running;
+    QWaitCondition    _running_state_changed;
+    pthread_t         _reader_thread;
+
+    mutable QMutex    _pid_lock;
+    vector<uint>      _eit_pids;
+    vector<uint>      _pid_info; // kept sorted
+    uint              _open_pid_filters;
+    MythTimer         _cycle_timer;
+
+    mutable QMutex          _listener_lock;
+    vector<MPEGStreamData*> _stream_data_list;
+
+    mutable QMutex          _hdhr_lock;
+
+    // for caching TS monitoring supported value.
+    static QMutex          _rec_supports_ts_monitoring_lock;
+    static QMap<uint,bool> _rec_supports_ts_monitoring;
+
+    // for implementing Get & Return
+    static QMutex                            _handlers_lock;
+    static QMap<QString, HDHRStreamHandler*> _handlers;
+    static QMap<QString, uint>               _handlers_refcnt;
+};
+
+#endif // _HDHRSTREAMHANDLER_H_
Index: libs/libmythtv/cardutil.cpp
===================================================================
--- libs/libmythtv/cardutil.cpp	(revision 20746)
+++ libs/libmythtv/cardutil.cpp	(working copy)
@@ -214,6 +214,42 @@
                 devs.push_back(subit->filePath());
         }
     }
+#ifdef USING_HDHOMERUN
+    else if (rawtype.toUpper() == "HDHOMERUN")
+    {
+        uint32_t  target_ip   = 0;
+        uint32_t  device_type = HDHOMERUN_DEVICE_TYPE_TUNER;
+        uint32_t  device_id   = HDHOMERUN_DEVICE_ID_WILDCARD;
+        const int max_count   = 50;
+        hdhomerun_discover_device_t result_list[max_count];
+
+        int result = hdhomerun_discover_find_devices_custom(
+            target_ip, device_type, device_id, result_list, max_count);
+
+        if (result == -1)
+        {
+            VERBOSE(VB_IMPORTANT, "CardUtil::ProbeVideoDevices: "
+                    "Error finding HDHomerun devices");
+            return devs;
+        }
+
+        if (result == 20)
+        {
+            VERBOSE(VB_IMPORTANT, "CardUtil::ProbeVideoDevices: "
+                    "Warning: may be > 20 HDHomerun devices");
+        }
+
+        // TODO FIXME -- figure out some way to return ip address as well
+        for (int i = 0; i < result; i++)
+        {
+            QString did = QString("%1").arg(result_list[i].device_id, 0, 16);
+            did = did.toUpper();
+
+            devs.push_back(did + "-0");
+            devs.push_back(did + "-1");
+        }
+    }
+#endif // USING_HDHOMERUN
     else
     {
         VERBOSE(VB_IMPORTANT, QString("CardUtil::ProbeVideoDevices: ") +
@@ -223,18 +259,23 @@
     return devs;
 }
 
+#include <cassert>
 QString CardUtil::ProbeDVBType(const QString &device)
 {
     QString ret = "ERROR_UNKNOWN";
-    (void) device;
 
+    if (device.isEmpty())
+        return ret;
+
 #ifdef USING_DVB
     QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_FRONTEND, device);
+    assert(dvbdev != device);
     QByteArray dev = dvbdev.toAscii();
     int fd_frontend = open(dev.constData(), O_RDONLY | O_NONBLOCK);
     if (fd_frontend < 0)
     {
-        VERBOSE(VB_IMPORTANT, "Can't open DVB frontend (" + dvbdev + ").");
+        VERBOSE(VB_IMPORTANT, QString("Can't open DVB frontend (%1) for %2.")
+                .arg(dvbdev).arg(device));
         return ret;
     }
 
@@ -331,7 +372,11 @@
     if (device.isEmpty())
         return "ERROR_OPEN";
 
-    return ProbeDVBType(device);
+    QString subtype = ProbeDVBType(device);
+    VERBOSE(VB_IMPORTANT,
+            QString("Raw card type was '%1' subtype is '%2'")
+            .arg(type).arg(subtype));
+    return subtype;
 }
 
 /** \fn CardUtil::IsDVBCardType(const QString)
@@ -1150,7 +1195,10 @@
         uint id = 0;
         for (uint i = 0; !id && (i < 100); i++)
         {
-            name = QString("DVB%1").arg(dev.toUInt());
+            bool ok;
+            name = QString("DVB%1").arg(dev.toUInt(&ok));
+            if (!ok)
+                name = QString("HDHR_%1").arg(dev);
             name += (i) ? QString(":%1").arg(i) : QString("");
             id = CardUtil::CreateInputGroup(name);
         }
@@ -1753,18 +1801,7 @@
     }
     else if (cardtype == "HDHOMERUN")
     {
-        MSqlQuery query(MSqlQuery::InitCon());
-        query.prepare(
-            "SELECT dbox2_port "
-            "FROM capturecard "
-            "WHERE cardid = :CARDID");
-        query.bindValue(":CARDID", cardid);
-
-        if (!query.exec() || !query.isActive() || !query.next())
-            label = "[ DB ERROR ]";
-        else
-            label = QString("[ HDHomeRun : ID %1 Port %2 ]")
-                .arg(videodevice).arg(query.value(0).toString());
+        label = QString("[ HDHomeRun : %1 ]").arg(videodevice);
     }
     else
     {
Index: libs/libmythtv/videosource.cpp
===================================================================
--- libs/libmythtv/videosource.cpp	(revision 20746)
+++ libs/libmythtv/videosource.cpp	(working copy)
@@ -48,6 +48,10 @@
 #include "videodev_myth.h"
 #endif
 
+#ifdef USING_HDHOMERUN
+#include "hdhomerun.h"
+#endif
+
 QMutex XMLTVFindGrabbers::list_lock;
 
 VideoSourceSelector::VideoSourceSelector(uint           _initial_sourceid,
@@ -1407,20 +1411,92 @@
     CaptureCard &parent;
  };
 
-class HDHomeRunDeviceID : public LineEditSetting, public CaptureCardDBStorage
+class HDHomeRunIP : public TransLabelSetting
 {
   public:
+    HDHomeRunIP()
+    {
+        setLabel(QObject::tr("IP Address"));
+    };
+};
+
+class HDHomeRunTuner : public TransLabelSetting
+{
+  public:
+    HDHomeRunTuner()
+    {
+        setLabel(QObject::tr("Tuner"));
+    };
+};
+
+class HDHomeRunDeviceID : public ComboBoxSetting, public CaptureCardDBStorage
+{
+  public:
     HDHomeRunDeviceID(const CaptureCard &parent) :
-        LineEditSetting(this),
+        ComboBoxSetting(this),
         CaptureCardDBStorage(this, parent, "videodevice")
     {
-        setValue("FFFFFFFF");
         setLabel(QObject::tr("Device ID"));
-        setHelpText(QObject::tr("IP address or Device ID from the bottom of "
-                                "the HDHomeRun.  You may use "
-                                "'FFFFFFFF' if there is only one unit "
-                                "on your your network."));
+        setHelpText(
+            QObject::tr(
+                "DevicedID and Tuner Number of available HDHomeRun devices."));
+        fillSelections("");
+    };
+
+    /// \brief Adds all available device-tuner combinations to list
+    /// If current is >= 0 it will be considered available even
+    /// if no device exists for it on the network
+    void fillSelections(QString current)
+    {
+        clearSelections();
+
+        // Get network visible devices
+        vector<QString> devs = CardUtil::ProbeVideoDevices("HDHOMERUN");
+
+        // Add current if needed
+        if ((current != "") &&
+            (find(devs.begin(), devs.end(), current) == devs.end()))
+        {
+            devs.push_back(current);
+            stable_sort(devs.begin(), devs.end());
+        }
+
+        vector<QString> db = CardUtil::GetVideoDevices("HDHOMERUN");
+
+        QMap<QString, bool> in_use;
+        QString sel = current;
+        for (uint i = 0; i < devs.size(); i++)
+        {
+            const QString dev = devs[i];
+            in_use[devs[i]] = find(db.begin(), db.end(), dev) != db.end();
+            if (sel == "" && !in_use[devs[i]])
+                sel = dev;
+        }
+
+        if (sel == "" && devs.size())
+            sel = devs[0];
+ 
+        QString usestr = QString(" -- ");
+        usestr += QObject::tr("Warning: already in use");
+
+        for (uint i = 0; i < devs.size(); i++)
+        {
+            const QString dev = devs[i];
+            QString desc = dev + (in_use[devs[i]] ? usestr : "");
+            desc = (current == devs[i]) ? dev : desc;
+            addSelection(desc, dev, dev == sel);
+        }
     }
+
+    virtual void Load(void)
+    {
+        clearSelections();
+        addSelection("");
+
+        CaptureCardDBStorage::Load();
+
+        fillSelections(getValue());
+    }
 };
 
 class IPTVHost : public LineEditSetting, public CaptureCardDBStorage
@@ -1453,50 +1529,99 @@
     CaptureCard &parent;
 };
 
-class HDHomeRunTunerIndex : public ComboBoxSetting, public CaptureCardDBStorage
+class HDHomeRunExtra : public ConfigurationWizard
 {
   public:
-    HDHomeRunTunerIndex(const CaptureCard &parent) :
-        ComboBoxSetting(this),
-        CaptureCardDBStorage(this, parent, "dbox2_port")
+    HDHomeRunExtra(HDHomeRunConfigurationGroup &parent);
+    uint GetInstanceCount(void) const
     {
-        setLabel(QObject::tr("Tuner"));
-        addSelection("0");
-        addSelection("1");
+        return (uint) count->intValue();
     }
+
+  private:
+    InstanceCount *count;
 };
 
+HDHomeRunExtra::HDHomeRunExtra(HDHomeRunConfigurationGroup &parent)
+    : count(new InstanceCount(parent.parent))
+{
+    VerticalConfigurationGroup* rec = new VerticalConfigurationGroup(false);
+    rec->setLabel(QObject::tr("Recorder Options"));
+    rec->setUseLabel(false);
+
+    rec->addChild(count);
+
+    addChild(rec);
+}
+
+
 HDHomeRunConfigurationGroup::HDHomeRunConfigurationGroup
         (CaptureCard& a_parent) :
     VerticalConfigurationGroup(false, true, false, false),
     parent(a_parent)
 {
-    HDHomeRunDeviceID *device = new HDHomeRunDeviceID(parent);
+    setUseLabel(false);
+    deviceid = new HDHomeRunDeviceID(parent);
+    addChild(deviceid);
+    cardip = new HDHomeRunIP();
+    cardtuner = new HDHomeRunTuner();
 
-    desc = new TransLabelSetting();
+    addChild(cardip);
+    addChild(cardtuner);
 
-    setUseLabel(false);
-    addChild(device);
-    addChild(desc);
-    addChild(new HDHomeRunTunerIndex(parent));
     addChild(new SignalTimeout(parent, 1000, 250));
     addChild(new ChannelTimeout(parent, 3000, 1750));
     addChild(new SingleCardInput(parent));
 
+    TransButtonSetting *buttonRecOpt = new TransButtonSetting();
+    buttonRecOpt->setLabel(tr("Recording Options"));
+    addChild(buttonRecOpt);
 
     // Wish we could use something like editingFinished() here...
-    connect(device, SIGNAL(valueChanged(const QString&)),
-            this,   SLOT(  probeCard(   const QString&)));
+    connect(deviceid,     SIGNAL(valueChanged(const QString&)),
+            this,         SLOT(  probeCard   (const QString&)));
+    connect(buttonRecOpt, SIGNAL(pressed()),
+            this,         SLOT(  HDHomeRunExtraPanel()));
+
+//    addChild(desc);
 };
 
-void HDHomeRunConfigurationGroup::probeCard(const QString &device)
+void HDHomeRunConfigurationGroup::probeCard(const QString &deviceid)
 {
-    if (device.contains('.') || device.contains(QRegExp("^[0-9a-fA-F]{8}$")))
-        desc->setValue(CardUtil::GetHDHRdesc(device));
+#ifdef USING_HDHOMERUN
+    hdhomerun_device_t *thisdevice =
+        hdhomerun_device_create_from_str(deviceid.toLocal8Bit().constData());
+
+    if (thisdevice)
+    {
+        uint device_ip = hdhomerun_device_get_device_ip(thisdevice);
+        uint tuner     = hdhomerun_device_get_tuner(thisdevice);
+        hdhomerun_device_destroy(thisdevice);
+
+        QString ip = QString("%1.%2.%3.%4")
+                .arg((device_ip>>24) & 0xFF).arg((device_ip>>16) & 0xFF)
+                .arg((device_ip>> 8) & 0xFF).arg((device_ip>> 0) & 0xFF);
+
+        cardip->setValue(ip);
+        cardtuner->setValue(QString("%1").arg(tuner));
+    }
     else
-        desc->setValue(tr("Badly formatted Device ID"));
+    {
+        cardip->setValue("Unknown");
+        cardtuner->setValue("Unknown");
+    }
+#endif // USING_HDHOMERUN
 }
 
+void HDHomeRunConfigurationGroup::HDHomeRunExtraPanel(void)
+{
+    parent.reload(); // ensure card id is valid
+
+    HDHomeRunExtra acw(*this);
+    acw.exec();
+    parent.SetInstanceCount(acw.GetInstanceCount());
+}
+
 V4LConfigurationGroup::V4LConfigurationGroup(CaptureCard& a_parent) :
     VerticalConfigurationGroup(false, true, false, false),
     parent(a_parent),
@@ -1682,6 +1807,14 @@
     addChild(new Hostname(*this));
 }
 
+QString CaptureCard::GetRawCardType(void) const
+{
+    int cardid = getCardID();
+    if (cardid <= 0)
+        return QString::null;
+    return CardUtil::GetRawCardType(cardid);
+}
+
 void CaptureCard::fillSelections(SelectSetting *setting)
 {
     MSqlQuery query(MSqlQuery::InitCon());
@@ -1710,6 +1843,9 @@
         if ((cardtype.toLower() == "dvb") && (1 != ++device_refs[videodevice]))
             continue;
 
+        if ((cardtype.toLower() == "hdhomerun") && (1 != ++device_refs[videodevice]))
+            continue;
+
         QString label = CardUtil::GetDeviceLabel(
             cardid, cardtype, videodevice);
 
@@ -2853,6 +2989,9 @@
         if ((cardtype.toLower() == "dvb") && (1 != ++device_refs[videodevice]))
             continue;
 
+        if ((cardtype.toLower() == "hdhomerun") && (1 != ++device_refs[videodevice]))
+            continue;
+
         QStringList        inputLabels;
         vector<CardInput*> cardInputs;
 
@@ -2913,11 +3052,23 @@
 
 void DVBConfigurationGroup::probeCard(const QString &videodevice)
 {
-    (void) videodevice;
+    if (videodevice.isEmpty())
+    {
+        cardname->setValue("");
+        cardtype->setValue("");
+        return;
+    }
 
+    if (parent.getCardID() && parent.GetRawCardType() != "DVB")
+    {
+        cardname->setValue("");
+        cardtype->setValue("");
+        return;
+    }
+
 #ifdef USING_DVB
     QString frontend_name = CardUtil::ProbeDVBFrontendName(videodevice);
-    QString subtype       = CardUtil::ProbeDVBType(videodevice);
+    QString subtype = CardUtil::ProbeDVBType(videodevice);
 
     QString err_open  = tr("Could not open card %1").arg(videodevice);
     QString err_other = tr("Could not get card info for card %1").arg(videodevice);
Index: libs/libmythtv/tv_rec.cpp
===================================================================
--- libs/libmythtv/tv_rec.cpp	(revision 20746)
+++ libs/libmythtv/tv_rec.cpp	(working copy)
@@ -181,7 +181,7 @@
     else if (genOpt.cardtype == "HDHOMERUN")
     {
 #ifdef USING_HDHOMERUN
-        channel = new HDHRChannel(this, genOpt.videodev, dboxOpt.port);
+        channel = new HDHRChannel(this, genOpt.videodev);
         if (!channel->Open())
             return false;
         InitChannel(genOpt.defaultinput, startchannel);
@@ -3501,15 +3501,6 @@
             return;
 
         ClearFlags(kFlagWaitingForRecPause);
-#ifdef USING_HDHOMERUN
-        if (GetHDHRRecorder())
-        {
-            // We currently need to close the file descriptor for
-            // HDHomeRun signal monitoring to work.
-            GetHDHRRecorder()->Close();
-            GetHDHRRecorder()->SetRingBuffer(NULL);
-        }
-#endif // USING_HDHOMERUN
         VERBOSE(VB_RECORD, LOC + "Recorder paused, calling TuningFrequency");
         TuningFrequency(lastTuningRequest);
     }
@@ -4148,17 +4139,6 @@
     }
     recorder->Reset();
 
-#ifdef USING_HDHOMERUN
-    if (GetHDHRRecorder())
-    {
-        pauseNotify = false;
-        GetHDHRRecorder()->Close();
-        pauseNotify = true;
-        GetHDHRRecorder()->Open();
-        GetHDHRRecorder()->StartData();
-    }
-#endif // USING_HDHOMERUN
-
     // Set file descriptor of channel from recorder for V4L
     channel->SetFd(recorder->GetVideoFd());
 
Index: libs/libmythtv/hdhrchannel.h
===================================================================
--- libs/libmythtv/hdhrchannel.h	(revision 20746)
+++ libs/libmythtv/hdhrchannel.h	(working copy)
@@ -8,79 +8,53 @@
 #define HDHOMERUNCHANNEL_H
 
 // Qt headers
-#include <qstring.h>
+#include <QString>
 
 // MythTV headers
 #include "dtvchannel.h"
 
-// HDHomeRun headers
-#ifdef USING_HDHOMERUN
-#include "hdhomerun.h"
-#else
-struct hdhomerun_control_sock_t { int dummy; };
-#endif
+class HDHRChannel;
+class HDHRStreamHandler;
+class ProgramMapTable;
 
-typedef struct hdhomerun_control_sock_t hdhr_socket_t;
-
 class HDHRChannel : public DTVChannel
 {
     friend class HDHRSignalMonitor;
     friend class HDHRRecorder;
 
   public:
-    HDHRChannel(TVRec *parent, const QString &device, uint tuner);
+    HDHRChannel(TVRec *parent, const QString &device);
     ~HDHRChannel(void);
 
     bool Open(void);
     void Close(void);
     bool EnterPowerSavingMode(void);
 
+    bool Init(QString &inputname, QString &startchannel, bool setchan);
+
     // Sets
     bool SetChannelByString(const QString &chan);
 
     // Gets
-    bool IsOpen(void) const { return (_control_socket != NULL); }
-    QString GetDevice(void) const
-        { return QString("%1/%2").arg(_device_id, 8, 16).arg(_tuner); }
-    vector<uint> GetPIDs(void) const
-        { QMutexLocker locker(&_lock); return _pids; }
-    QString GetSIStandard(void) const { return "atsc"; }
+    bool IsOpen(void) const;
+    QString GetDevice(void) const { return _device_id; }
 
-    // Commands
-    bool AddPID(uint pid, bool do_update = true);
-    bool DelPID(uint pid, bool do_update = true);
-    bool DelAllPIDs(void);
-    bool UpdateFilters(void);
-
     // ATSC/DVB scanning/tuning stuff
     bool TuneMultiplex(uint mplexid, QString inputname);
     bool Tune(const DTVMultiplex &tuning, QString inputname);
 
   private:
-    bool FindDevice(void);
-    bool Connect(void);
     bool Tune(uint frequency, QString inputname,
               QString modulation, QString si_std);
 
-    bool DeviceSetTarget(unsigned short localPort);
-    bool DeviceClearTarget(void);
+  private:
+    HDHRStreamHandler *_stream_handler;
 
-    QString DeviceGet(const QString &name, bool report_error_return = true);
-    QString DeviceSet(const QString &name, const QString &value,
-                      bool report_error_return = true);
+    QString         _device_id;
 
-    QString TunerGet(const QString &name, bool report_error_return = true);
-    QString TunerSet(const QString &name, const QString &value,
-                     bool report_error_return = true);
-
-  private:
-    hdhr_socket_t  *_control_socket;
-    uint            _device_id;
-    uint            _device_ip;
-    uint            _tuner;
-    bool            _ignore_filters;
-    vector<uint>    _pids;
     mutable QMutex  _lock;
+    mutable QMutex  tune_lock;
+    mutable QMutex  hw_lock;
 };
 
 #endif
Index: libs/libmythhdhomerun/hdhomerun.h
===================================================================
--- libs/libmythhdhomerun/hdhomerun.h	(revision 20746)
+++ libs/libmythhdhomerun/hdhomerun.h	(working copy)
@@ -1,3 +1,5 @@
+#ifndef __HDHOMERUN_INCLUDES__
+#define __HDHOMERUN_INCLUDES__
 /*
  * hdhomerun_device.h
  *
@@ -27,3 +29,6 @@
 #include "hdhomerun_channels.h"
 #include "hdhomerun_channelscan.h"
 #include "hdhomerun_device.h"
+
+#endif /* __HDHOMERUN_INCLUDES__ */
+
