Ticket #6138: hdhomerun-multirec-v3.patch

File hdhomerun-multirec-v3.patch, 97.1 KB (added by danielk, 11 years ago)

Reviewed version of multirec with rtp->udp fallback.

  • libs/libmythtv/channelscan/channelscanner.cpp

     
    332332#ifdef USING_HDHOMERUN
    333333    if ("HDHOMERUN" == card_type)
    334334    {
    335         uint tuner = CardUtil::GetHDHRTuner(cardid);
    336         channel = new HDHRChannel(NULL, device, tuner);
     335        channel = new HDHRChannel(NULL, device);
    337336    }
    338337#endif // USING_HDHOMERUN
    339338
  • libs/libmythtv/cardutil.h

     
    116116
    117117    static bool         IsTunerSharingCapable(const QString &rawtype)
    118118    {
    119         return (rawtype == "DVB");
     119        return (rawtype == "DVB")   || (rawtype == "HDHOMERUN");
    120120    }
    121121
    122122    static bool         IsTunerShared(uint cardidA, uint cardidB);
  • libs/libmythtv/videosource.h

     
    442442    static void fillSelections(SelectSetting* setting);
    443443};
    444444
     445class HDHomeRunDeviceID;
     446class HDHomeRunIP;
     447class HDHomeRunTuner;
    445448class HDHomeRunConfigurationGroup : public VerticalConfigurationGroup
    446449{
    447450    Q_OBJECT
    448451
     452    friend class HDHomeRunExtra;
     453
    449454  public:
    450455    HDHomeRunConfigurationGroup(CaptureCard &parent);
    451456
    452457  public slots:
    453458    void probeCard(const QString &device);
     459    void HDHomeRunExtraPanel(void);
    454460
    455461  private:
     462    HDHomeRunDeviceID *deviceid;
     463    HDHomeRunIP       *cardip;
     464    HDHomeRunTuner    *cardtuner;
     465
    456466    CaptureCard       &parent;
    457467    TransLabelSetting *desc;
    458468};
     
    592602    CaptureCard(bool use_card_group = true);
    593603
    594604    int  getCardID(void) const { return id->intValue(); }
     605    QString GetRawCardType(void) const;
    595606
    596607    void loadByID(int id);
    597608
     
    623634            CaptureCardDBStorage(this, parent, "hostname") { }
    624635    };
    625636
    626 private:
     637  protected:
    627638    ID       *id;
    628639    uint      instance_count;
    629640};
  • libs/libmythtv/libmythtv.pro

     
    524524    using_hdhomerun {
    525525        # MythTV HDHomeRun glue
    526526        HEADERS += hdhrsignalmonitor.h   hdhrchannel.h
    527         HEADERS += hdhrrecorder.h
     527        HEADERS += hdhrrecorder.h        hdhrstreamhandler.h
    528528
    529529        SOURCES += hdhrsignalmonitor.cpp hdhrchannel.cpp
    530         SOURCES += hdhrrecorder.cpp
     530        SOURCES += hdhrrecorder.cpp      hdhrstreamhandler.cpp
    531531
    532532        DEFINES += USING_HDHOMERUN
    533533    }
  • libs/libmythtv/hdhrstreamhandler.cpp

     
     1// -*- Mode: c++ -*-
     2
     3// POSIX headers
     4#include <pthread.h>
     5#include <fcntl.h>
     6#include <unistd.h>
     7#include <sys/select.h>
     8#include <sys/ioctl.h>
     9
     10// Qt headers
     11#include <QString>
     12
     13// MythTV headers
     14#include "hdhrstreamhandler.h"
     15#include "hdhrchannel.h"
     16#include "dtvsignalmonitor.h"
     17#include "streamlisteners.h"
     18#include "mpegstreamdata.h"
     19#include "cardutil.h"
     20
     21#define LOC      QString("HDHRSH(%1): ").arg(_devicename)
     22#define LOC_WARN QString("HDHRSH(%1) Warning: ").arg(_devicename)
     23#define LOC_ERR  QString("HDHRSH(%1) Error: ").arg(_devicename)
     24
     25QMap<uint,bool> HDHRStreamHandler::_rec_supports_ts_monitoring;
     26QMutex          HDHRStreamHandler::_rec_supports_ts_monitoring_lock;
     27
     28QMap<QString,HDHRStreamHandler*> HDHRStreamHandler::_handlers;
     29QMap<QString,uint>               HDHRStreamHandler::_handlers_refcnt;
     30QMutex                           HDHRStreamHandler::_handlers_lock;
     31
     32//#define DEBUG_PID_FILTERS
     33
     34HDHRStreamHandler *HDHRStreamHandler::Get(const QString &devname)
     35{
     36    QMutexLocker locker(&_handlers_lock);
     37
     38    QMap<QString,HDHRStreamHandler*>::iterator it =
     39        _handlers.find(devname);
     40
     41    if (it == _handlers.end())
     42    {
     43        HDHRStreamHandler *newhandler = new HDHRStreamHandler(devname);
     44        newhandler->Open();
     45        _handlers[devname] = newhandler;
     46        _handlers_refcnt[devname] = 1;
     47        VERBOSE(VB_RECORD,
     48                QString("HDHRSH: Creating new stream handler for %1")
     49                .arg(devname));
     50    }
     51    else
     52    {
     53        _handlers_refcnt[devname]++;
     54        uint rcount = _handlers_refcnt[devname];
     55        VERBOSE(VB_RECORD,
     56                QString("HDHRSH: Using existing stream handler for %1")
     57                .arg(devname) + QString(" (%2 in use)").arg(rcount));
     58    }
     59
     60    return _handlers[devname];
     61}
     62
     63void HDHRStreamHandler::Return(HDHRStreamHandler * & ref)
     64{
     65    QMutexLocker locker(&_handlers_lock);
     66
     67    QString devname = ref->_devicename;
     68
     69    QMap<QString,uint>::iterator rit = _handlers_refcnt.find(devname);
     70    if (rit == _handlers_refcnt.end())
     71        return;
     72
     73    if (*rit > 1)
     74    {
     75        ref = NULL;
     76        *rit--;
     77        return;
     78    }
     79
     80    QMap<QString,HDHRStreamHandler*>::iterator it = _handlers.find(devname);
     81    if ((it != _handlers.end()) && (*it == ref))
     82    {
     83        VERBOSE(VB_RECORD,
     84                QString("HDHRSH: Closing handler for %1")
     85                .arg(devname));
     86        ref->Close();
     87        delete *it;
     88        _handlers.erase(it);
     89    }
     90    else
     91    {
     92        VERBOSE(VB_IMPORTANT,
     93                QString("HDHRSH Error: Couldn't find handler for %1")
     94                .arg(devname));
     95    }
     96
     97    _handlers_refcnt.erase(rit);
     98    ref = NULL;
     99}
     100
     101HDHRStreamHandler::HDHRStreamHandler(const QString &devicename) :
     102    _control_socket(NULL),
     103    _video_socket(NULL),
     104    _devicename(devicename),
     105
     106    _start_stop_lock(QMutex::Recursive),
     107    _running(false),
     108
     109    _pid_lock(QMutex::Recursive),
     110    _listener_lock(QMutex::Recursive),
     111    _hdhr_lock(QMutex::Recursive)
     112{
     113}
     114
     115HDHRStreamHandler::~HDHRStreamHandler()
     116{
     117    if (!_stream_data_list.empty())
     118    {
     119        VERBOSE(VB_IMPORTANT, LOC_ERR + "dtor & _stream_data_list not empty");
     120    }
     121}
     122
     123void HDHRStreamHandler::AddListener(MPEGStreamData *data)
     124{
     125    VERBOSE(VB_RECORD, LOC + "AddListener("<<data<<") -- begin");
     126    if (!data)
     127    {
     128        VERBOSE(VB_IMPORTANT, LOC_ERR +
     129                "AddListener("<<data<<") -- null data");
     130        return;
     131    }
     132
     133    _listener_lock.lock();
     134
     135    VERBOSE(VB_RECORD, LOC + "AddListener("<<data<<") -- locked");
     136
     137    _stream_data_list.push_back(data);
     138
     139    _listener_lock.unlock();
     140
     141    Start();
     142
     143    VERBOSE(VB_RECORD, LOC + "AddListener("<<data<<") -- end");
     144}
     145
     146void HDHRStreamHandler::RemoveListener(MPEGStreamData *data)
     147{
     148    VERBOSE(VB_RECORD, LOC + "RemoveListener("<<data<<") -- begin");
     149    if (!data)
     150    {
     151        VERBOSE(VB_IMPORTANT, LOC_ERR +
     152                "RemoveListener("<<data<<") -- null data");
     153        return;
     154    }
     155
     156    _listener_lock.lock();
     157
     158    VERBOSE(VB_RECORD, LOC + "RemoveListener("<<data<<") -- locked");
     159
     160    vector<MPEGStreamData*>::iterator it =
     161        find(_stream_data_list.begin(), _stream_data_list.end(), data);
     162
     163    if (it != _stream_data_list.end())
     164        _stream_data_list.erase(it);
     165
     166    if (_stream_data_list.empty())
     167    {
     168        _listener_lock.unlock();
     169        Stop();
     170    }
     171    else
     172    {
     173        _listener_lock.unlock();
     174    }
     175
     176    VERBOSE(VB_RECORD, LOC + "RemoveListener("<<data<<") -- end");
     177}
     178
     179void *run_hdhr_stream_handler_thunk(void *param)
     180{
     181    HDHRStreamHandler *mon = (HDHRStreamHandler*) param;
     182    mon->Run();
     183    return NULL;
     184}
     185
     186void HDHRStreamHandler::Start(void)
     187{
     188    QMutexLocker locker(&_start_stop_lock);
     189
     190    _eit_pids.clear();
     191
     192    if (!IsRunning())
     193    {
     194        QMutex is_running_lock;
     195        int rval = pthread_create(&_reader_thread, NULL,
     196                                  run_hdhr_stream_handler_thunk, this);
     197
     198        if (0 != rval)
     199        {
     200            VERBOSE(VB_IMPORTANT, LOC_ERR +
     201                    "Start: Failed to create thread." + ENO);
     202            return;
     203        }
     204
     205        is_running_lock.lock();
     206        while (!IsRunning())
     207        {
     208            _running_state_changed.wait(&is_running_lock, 100);
     209        }
     210    }
     211}
     212
     213void HDHRStreamHandler::Stop(void)
     214{
     215    QMutexLocker locker(&_start_stop_lock);
     216
     217    if (IsRunning())
     218    {
     219        SetRunning(false);
     220        pthread_join(_reader_thread, NULL);
     221    }
     222}
     223
     224void HDHRStreamHandler::Run(void)
     225{
     226    SetRunning(true);
     227    RunTS();
     228}
     229
     230/** \fn HDHRStreamHandler::RunTS(void)
     231 *  \brief Uses TS filtering devices to read a DVB device for tables & data
     232 *
     233 *  This supports all types of MPEG based stream data, but is extreemely
     234 *  slow with DVB over USB 1.0 devices which for efficiency reasons buffer
     235 *  a stream until a full block transfer buffer full of the requested
     236 *  tables is available. This takes a very long time when you are just
     237 *  waiting for a PAT or PMT table, and the buffer is hundreds of packets
     238 *  in size.
     239 */
     240void HDHRStreamHandler::RunTS(void)
     241{
     242    int remainder = 0;
     243
     244    /* Calculate buffer size */
     245    uint buffersize = gContext->GetNumSetting(
     246        "HDRingbufferSize", 50 * TSPacket::SIZE) * 1024;
     247    buffersize /= VIDEO_DATA_PACKET_SIZE;
     248    buffersize *= VIDEO_DATA_PACKET_SIZE;
     249
     250    // Buffer should be at least about 1MB..
     251    buffersize = max(49 * TSPacket::SIZE * 128, buffersize);
     252
     253    /* Create TS socket. */
     254    _video_socket = hdhomerun_video_create(0, buffersize);
     255    if (!_video_socket)
     256    {
     257        VERBOSE(VB_IMPORTANT, LOC + "Open() failed to open socket");
     258        return;
     259    }
     260
     261    uint localPort = hdhomerun_video_get_local_port(_video_socket);
     262    if (!DeviceSetTarget(localPort))
     263    {
     264        VERBOSE(VB_IMPORTANT, LOC_ERR +
     265                "Starting recording (set target failed). Aborting.");
     266        return;
     267    }
     268    hdhomerun_video_flush(_video_socket);
     269
     270    bool _error = false;
     271
     272    VERBOSE(VB_RECORD, LOC + "RunTS(): begin");
     273
     274    while (IsRunning() && !_error)
     275    {
     276        UpdateFiltersFromStreamData();
     277
     278        size_t read_size = 64 * 1024; // read about 64KB
     279        read_size /= VIDEO_DATA_PACKET_SIZE;
     280        read_size *= VIDEO_DATA_PACKET_SIZE;
     281
     282        size_t data_length;
     283        unsigned char *data_buffer =
     284            hdhomerun_video_recv(_video_socket, read_size, &data_length);
     285
     286        if (!data_buffer)
     287        {
     288            usleep(5000);
     289            continue;
     290        }
     291
     292        // Assume data_length is a multiple of 188 (packet size)
     293
     294        _listener_lock.lock();
     295
     296        if (_stream_data_list.empty())
     297        {
     298            _listener_lock.unlock();
     299            continue;
     300        }
     301
     302        for (uint i = 0; i < _stream_data_list.size(); i++)
     303        {
     304            remainder = _stream_data_list[i]->ProcessData(
     305                data_buffer, data_length);
     306        }
     307       
     308        _listener_lock.unlock();
     309        if (remainder != 0)
     310        {
     311            VERBOSE(VB_IMPORTANT, LOC +
     312                    QString("RunTS(): data_length = %1 remainder = %2")
     313                    .arg(data_length).arg(remainder));
     314        }
     315    }
     316    VERBOSE(VB_RECORD, LOC + "RunTS(): " + "shutdown");
     317
     318    RemoveAllPIDFilters();
     319
     320    DeviceClearTarget();
     321    VERBOSE(VB_RECORD, LOC + "RunTS(): " + "end");
     322
     323    hdhomerun_video_sock_t* tmp_video_socket;
     324    {
     325        QMutexLocker locker(&_hdhr_lock);
     326        tmp_video_socket = _video_socket;
     327        _video_socket=NULL;
     328    }
     329     
     330    hdhomerun_video_destroy(tmp_video_socket);
     331
     332    VERBOSE(VB_RECORD, LOC + "RunTS(): " + "end");
     333
     334    SetRunning(false);
     335}
     336
     337bool HDHRStreamHandler::AddPIDFilter(uint pid, bool do_update)
     338{
     339#ifdef DEBUG_PID_FILTERS
     340    VERBOSE(VB_RECORD, LOC + QString("AddPIDFilter(0x%1)")
     341            .arg(pid, 0, 16));
     342#endif // DEBUG_PID_FILTERS
     343
     344    QMutexLocker writing_locker(&_pid_lock);
     345
     346    vector<uint>::iterator it;
     347    it = lower_bound(_pid_info.begin(), _pid_info.end(), pid);
     348    if (it != _pid_info.end() && *it == pid)
     349        return true;
     350
     351    _pid_info.insert(it, pid);
     352
     353    if (do_update)
     354        return UpdateFilters();
     355
     356    return true;
     357}
     358
     359bool HDHRStreamHandler::RemovePIDFilter(uint pid, bool do_update)
     360{
     361#ifdef DEBUG_PID_FILTERS
     362    VERBOSE(VB_RECORD, LOC +
     363            QString("RemovePIDFilter(0x%1)").arg(pid, 0, 16));
     364#endif // DEBUG_PID_FILTERS
     365
     366    QMutexLocker write_locker(&_pid_lock);
     367
     368    vector<uint>::iterator it;
     369    it = lower_bound(_pid_info.begin(), _pid_info.end(), pid);
     370    if ((it == _pid_info.end()) || (*it != pid))
     371       return false;
     372
     373    _pid_info.erase(it);
     374
     375    if (do_update)
     376        return UpdateFilters();
     377
     378    return true;
     379}
     380
     381bool HDHRStreamHandler::RemoveAllPIDFilters(void)
     382{
     383    QMutexLocker write_locker(&_pid_lock);
     384
     385#ifdef DEBUG_PID_FILTERS
     386    VERBOSE(VB_RECORD, LOC + "RemoveAllPIDFilters()");
     387#endif // DEBUG_PID_FILTERS
     388
     389    _pid_info.clear();
     390
     391    return UpdateFilters();
     392}
     393
     394static QString filt_str(uint pid)
     395{
     396    uint pid0 = (pid / (16*16*16)) % 16;
     397    uint pid1 = (pid / (16*16))    % 16;
     398    uint pid2 = (pid / (16))        % 16;
     399    uint pid3 = pid % 16;
     400    return QString("0x%1%2%3%4")
     401        .arg(pid0,0,16).arg(pid1,0,16)
     402        .arg(pid2,0,16).arg(pid3,0,16);
     403}
     404
     405void HDHRStreamHandler::UpdateListeningForEIT(void)
     406{
     407    vector<uint> add_eit, del_eit;
     408
     409    QMutexLocker read_locker(&_listener_lock);
     410
     411    for (uint i = 0; i < _stream_data_list.size(); i++)
     412    {
     413        MPEGStreamData *sd = _stream_data_list[i];
     414        if (sd->HasEITPIDChanges(_eit_pids) &&
     415            sd->GetEITPIDChanges(_eit_pids, add_eit, del_eit))
     416        {
     417            for (uint i = 0; i < del_eit.size(); i++)
     418            {
     419                uint_vec_t::iterator it;
     420                it = find(_eit_pids.begin(), _eit_pids.end(), del_eit[i]);
     421                if (it != _eit_pids.end())
     422                    _eit_pids.erase(it);
     423                sd->RemoveListeningPID(del_eit[i]);
     424            }
     425
     426            for (uint i = 0; i < add_eit.size(); i++)
     427            {
     428                _eit_pids.push_back(add_eit[i]);
     429                sd->AddListeningPID(add_eit[i]);
     430            }
     431        }
     432    }
     433}
     434
     435bool HDHRStreamHandler::UpdateFiltersFromStreamData(void)
     436{
     437    UpdateListeningForEIT();
     438
     439    pid_map_t pids;
     440
     441    {
     442        QMutexLocker read_locker(&_listener_lock);
     443
     444        for (uint i = 0; i < _stream_data_list.size(); i++)
     445            _stream_data_list[i]->GetPIDs(pids);
     446    }
     447
     448    uint_vec_t           add_pids;
     449    vector<uint>         del_pids;
     450
     451    {
     452        QMutexLocker read_locker(&_pid_lock);
     453
     454        // PIDs that need to be added..
     455        pid_map_t::const_iterator lit = pids.constBegin();
     456        for (; lit != pids.constEnd(); ++lit)
     457        {
     458            vector<uint>::iterator it;
     459            it = lower_bound(_pid_info.begin(), _pid_info.end(), lit.key());
     460            if (it == _pid_info.end() || *it != lit.key())
     461                add_pids.push_back(lit.key());
     462        }
     463
     464        // PIDs that need to be removed..
     465        vector<uint>::const_iterator fit = _pid_info.begin();
     466        for (; fit != _pid_info.end(); ++fit)
     467        {
     468            bool in_pids = pids.find(*fit) != pids.end();
     469            if (!in_pids)
     470                del_pids.push_back(*fit);
     471        }
     472    }
     473
     474    bool need_update = false;
     475
     476    // Remove PIDs
     477    bool ok = true;
     478    vector<uint>::iterator dit = del_pids.begin();
     479    for (; dit != del_pids.end(); ++dit)
     480    {
     481        need_update = true;
     482        ok &= RemovePIDFilter(*dit, false);
     483    }
     484
     485    // Add PIDs
     486    vector<uint>::iterator ait = add_pids.begin();
     487    for (; ait != add_pids.end(); ++ait)
     488    {
     489        need_update = true;
     490        ok &= AddPIDFilter(*ait, false);
     491    }
     492
     493    if (need_update)
     494        return UpdateFilters() && ok;
     495
     496    return ok;
     497}
     498
     499bool HDHRStreamHandler::UpdateFilters(void)
     500{
     501#ifdef DEBUG_PID_FILTERS
     502    VERBOSE(VB_RECORD, LOC + "UpdateFilters()");
     503#endif // DEBUG_PID_FILTERS
     504    QMutexLocker locker(&_pid_lock);
     505
     506    QString filter = "";
     507
     508    vector<uint> range_min;
     509    vector<uint> range_max;
     510
     511// FIXME
     512//    if (_ignore_filters)
     513 //       return true;
     514
     515    for (uint i = 0; i < _pid_info.size(); i++)
     516    {
     517        uint pid_min = _pid_info[i];
     518        uint pid_max  = pid_min;
     519        for (uint j = i + 1; j < _pid_info.size(); j++)
     520        {
     521            if (pid_max + 1 != _pid_info[j])
     522                break;
     523            pid_max++;
     524            i++;
     525        }
     526        range_min.push_back(pid_min);
     527        range_max.push_back(pid_max);
     528    }
     529    if (range_min.size() > 16)
     530    {
     531        range_min.resize(16);
     532        uint pid_max = range_max.back();
     533        range_max.resize(15);
     534        range_max.push_back(pid_max);
     535    }
     536
     537    for (uint i = 0; i < range_min.size(); i++)
     538    {
     539        filter += filt_str(range_min[i]);
     540        if (range_min[i] != range_max[i])
     541            filter += QString("-%1").arg(filt_str(range_max[i]));
     542        filter += " ";
     543    }
     544
     545    filter = filter.trimmed();
     546
     547    QString new_filter = TunerSet("filter", filter);
     548
     549#ifdef DEBUG_PID_FILTERS
     550    QString msg = QString("Filter: '%1'").arg(filter);
     551    if (filter != new_filter)
     552        msg += QString("\n\t\t\t\t'%2'").arg(new_filter);
     553
     554    VERBOSE(VB_RECORD, LOC + msg);
     555#endif // DEBUG_PID_FILTERS
     556
     557    return filter == new_filter;
     558}
     559
     560void HDHRStreamHandler::SetRunning(bool is_running)
     561{
     562    _running = is_running;
     563    _running_state_changed.wakeAll();
     564}
     565
     566PIDPriority HDHRStreamHandler::GetPIDPriority(uint pid) const
     567{
     568    QMutexLocker reading_locker(&_listener_lock);
     569
     570    PIDPriority tmp = kPIDPriorityNone;
     571
     572    for (uint i = 0; i < _stream_data_list.size(); i++)
     573        tmp = max(tmp, _stream_data_list[i]->GetPIDPriority(pid));
     574
     575    return tmp;
     576}
     577
     578bool HDHRStreamHandler::Open(void)
     579{
     580    if (!FindDevice())
     581        return false;
     582
     583    return Connect();
     584}
     585
     586void HDHRStreamHandler::Close(void)
     587{
     588    if (_control_socket)
     589    {
     590        TuneChannel("none");
     591        hdhomerun_control_destroy(_control_socket);
     592        _control_socket=NULL;
     593    }
     594}
     595
     596bool HDHRStreamHandler::Connect(void)
     597{
     598    _control_socket = hdhomerun_control_create(_device_id, _device_ip);
     599
     600    if (!_control_socket)
     601    {
     602        VERBOSE(VB_IMPORTANT, LOC_ERR + "Unable to create control socket");
     603        return false;
     604    }
     605
     606    if (hdhomerun_control_get_local_addr(_control_socket) == 0)
     607    {
     608        VERBOSE(VB_IMPORTANT, LOC_ERR + "Unable to connect to device");
     609        return false;
     610    }
     611
     612    VERBOSE(VB_RECORD, LOC + "Successfully connected to device");
     613    return true;
     614}
     615
     616bool HDHRStreamHandler::FindDevice(void)
     617{
     618    hdhomerun_device_t* thisdevice = hdhomerun_device_create_from_str(
     619        _devicename.toLocal8Bit().constData());
     620
     621    if (thisdevice)
     622    {
     623        _device_id = hdhomerun_device_get_device_id(thisdevice);
     624        _device_ip = hdhomerun_device_get_device_ip(thisdevice);
     625        _tuner     = hdhomerun_device_get_tuner(thisdevice);
     626        hdhomerun_device_destroy(thisdevice);
     627
     628        VERBOSE(VB_IMPORTANT, LOC +
     629                QString("device %5 found at address %1.%2.%3.%4 tuner %6")
     630                .arg((_device_ip>>24) & 0xFF).arg((_device_ip>>16) & 0xFF)
     631                .arg((_device_ip>> 8) & 0xFF).arg((_device_ip>> 0) & 0xFF)
     632                .arg(_devicename).arg(_tuner));
     633
     634        return true;
     635    }
     636    return false;
     637}
     638
     639
     640bool HDHRStreamHandler::EnterPowerSavingMode(void)
     641{
     642    if (_video_socket)
     643    {
     644        VERBOSE(VB_RECORD, LOC + "Ignoring request - video streaming active");
     645        return false;
     646    }
     647    else
     648    {
     649        return TuneChannel("none");
     650    }
     651}
     652
     653QString HDHRStreamHandler::DeviceGet(
     654    const QString &name, bool report_error_return) const
     655{
     656    QMutexLocker locker(&_hdhr_lock);
     657
     658    if (!_control_socket)
     659    {
     660        VERBOSE(VB_IMPORTANT, LOC_ERR + "Get request failed (not connected)");
     661        return QString::null;
     662    }
     663
     664    char *value = NULL;
     665    char *error = NULL;
     666    if (hdhomerun_control_get(
     667            _control_socket, name.toLocal8Bit().constData(),
     668            &value, &error) < 0)
     669    {
     670        VERBOSE(VB_IMPORTANT, LOC_ERR + "Get request failed" + ENO);
     671        return QString::null;
     672    }
     673
     674    if (report_error_return && error)
     675    {
     676        VERBOSE(VB_IMPORTANT, LOC_ERR +
     677                QString("DeviceGet(%1): %2").arg(name).arg(error));
     678
     679        return QString::null;
     680    }
     681
     682    return QString(value);
     683}
     684
     685QString HDHRStreamHandler::DeviceSet(
     686    const QString &name, const QString &val, bool report_error_return)
     687{
     688    QMutexLocker locker(&_hdhr_lock);
     689
     690    if (!_control_socket)
     691    {
     692        VERBOSE(VB_IMPORTANT, LOC_ERR + "Set request failed (not connected)");
     693        return QString::null;
     694    }
     695
     696    char *value = NULL;
     697    char *error = NULL;
     698    if (hdhomerun_control_set(
     699            _control_socket, name.toLocal8Bit().constData(),
     700            val.toLocal8Bit().constData(), &value, &error) < 0)
     701    {
     702        VERBOSE(VB_IMPORTANT, LOC_ERR + "Set request failed" + ENO);
     703
     704        return QString::null;
     705    }
     706
     707    if (report_error_return && error)
     708    {
     709        VERBOSE(VB_IMPORTANT, LOC_ERR +
     710                QString("DeviceSet(%1 %2): %3").arg(name).arg(val).arg(error));
     711
     712        return QString::null;
     713    }
     714
     715    return QString(value);
     716}
     717
     718QString HDHRStreamHandler::TunerGet(
     719    const QString &name, bool report_error_return) const
     720{
     721    return DeviceGet(QString("/tuner%1/%2").arg(_tuner).arg(name),
     722                     report_error_return);
     723}
     724
     725QString HDHRStreamHandler::TunerSet(
     726    const QString &name, const QString &value, bool report_error_return)
     727{
     728    QString valname = QString("/tuner%1/%2").arg(_tuner).arg(name);
     729    return DeviceSet(valname, value, report_error_return);
     730}
     731
     732bool HDHRStreamHandler::DeviceSetTarget(unsigned short localPort)
     733{
     734    if (localPort == 0)
     735    {
     736        return false;
     737    }
     738
     739    unsigned long localIP = hdhomerun_control_get_local_addr(_control_socket);
     740    if (localIP == 0)
     741    {
     742        return false;
     743    }
     744
     745    QString rtpValue = QString("rtp://%1.%2.%3.%4:%5")
     746        .arg((localIP >> 24) & 0xFF).arg((localIP >> 16) & 0xFF)
     747        .arg((localIP >>  8) & 0xFF).arg((localIP >>  0) & 0xFF)
     748        .arg(localPort);
     749
     750    QString err = TunerSet("target", rtpValue, true);
     751
     752    if (err.isEmpty())
     753    {
     754        QString udpValue = QString("udp://%1.%2.%3.%4:%5")
     755            .arg((localIP >> 24) & 0xFF).arg((localIP >> 16) & 0xFF)
     756            .arg((localIP >>  8) & 0xFF).arg((localIP >>  0) & 0xFF)
     757            .arg(localPort);
     758        return !TunerSet("target", udpValue).isEmpty();
     759    }
     760
     761    return true;
     762}
     763
     764bool HDHRStreamHandler::DeviceClearTarget(void)
     765{
     766    return !TunerSet("target", "0.0.0.0:0").isEmpty();
     767}
     768
     769QString HDHRStreamHandler::GetTunerStatus(void) const
     770{
     771    return TunerGet("status");
     772}
     773
     774bool HDHRStreamHandler::IsConnected(void) const
     775{
     776    return (_control_socket != NULL);
     777}
     778
     779bool HDHRStreamHandler::TuneChannel(const QString &chn)
     780{
     781    QString current = TunerGet("channel");
     782
     783    if (current == chn)
     784    {
     785        VERBOSE(VB_RECORD, QString(LOC + "Not Re-Tuning channel %1").arg(chn));
     786        return true;
     787    }
     788
     789    VERBOSE(VB_RECORD, QString(LOC + "Tuning channel %1 (was %2)")
     790            .arg(chn).arg(current));
     791    return !TunerSet("channel", chn).isEmpty();
     792}
     793
     794bool HDHRStreamHandler::TuneProgram(uint mpeg_prog_num)
     795{
     796    VERBOSE(VB_RECORD, QString(LOC + "Tuning program %1").arg(mpeg_prog_num));
     797    return !TunerSet(
     798        "program", QString::number(mpeg_prog_num), false).isEmpty();
     799}
  • libs/libmythtv/hdhrsignalmonitor.h

     
    77#include "qstringlist.h"
    88
    99class HDHRChannel;
     10class HDHRStreamHandler;
    1011
    1112typedef QMap<uint,int> FilterMap;
    1213
     
    1920
    2021    void Stop(void);
    2122
    22     bool UpdateFiltersFromStreamData(void);
    23 
    2423  protected:
    2524    HDHRSignalMonitor(void);
    2625    HDHRSignalMonitor(const HDHRSignalMonitor&);
    2726
    2827    virtual void UpdateValues(void);
     28    HDHRChannel *GetHDHRChannel(void);
    2929
    30     static void *TableMonitorThread(void *param);
    31     void RunTableMonitor(void);
    32 
    33     bool SupportsTSMonitoring(void);
    34 
    3530  protected:
    36     bool               dtvMonitorRunning;
    37     pthread_t          table_monitor_thread;
    38 
    39     FilterMap          filters; ///< PID filters for table monitoring
     31    bool               streamHandlerStarted;
     32    HDHRStreamHandler *streamHandler;
    4033};
    4134
    4235#endif // HDHRSIGNALMONITOR_H
  • libs/libmythtv/hdhrrecorder.h

     
    77#ifndef HDHOMERUNRECORDER_H_
    88#define HDHOMERUNRECORDER_H_
    99
    10 #include <qmutex.h>
     10// Qt includes
     11#include <QMutex>
     12
    1113#include "dtvrecorder.h"
    1214#include "streamlisteners.h"
    1315#include "eitscanner.h"
    1416
    1517class HDHRChannel;
    1618class ProgramMapTable;
     19class MPEGStreamData;
     20class HDHRStreamHandler;
    1721
    1822typedef vector<uint>        uint_vec_t;
    1923
     
    2125                     public DVBMainStreamListener,
    2226                     public ATSCMainStreamListener,
    2327                     public MPEGStreamListener,
    24                      public MPEGSingleProgramStreamListener
     28                     public MPEGSingleProgramStreamListener,
     29                     public TSPacketListener,
     30                     public TSPacketListenerAV
    2531{
    2632  public:
    2733    HDHRRecorder(TVRec *rec, HDHRChannel *channel);
     
    3238                               const QString &audiodev,
    3339                               const QString &vbidev);
    3440
     41    void StartRecording(void);
     42    void ResetForNewFile(void);
     43    void StopRecording(void);
     44
    3545    bool Open(void);
    36     bool StartData(void);
     46    bool IsOpen(void) const { return _stream_handler; }
    3747    void Close(void);
    3848
    39     void StartRecording(void);
    40 
    41     void SetStreamData(MPEGStreamData*);
    42     MPEGStreamData *GetStreamData(void) { return _stream_data; }
    43     ATSCStreamData *GetATSCStreamData(void);
    44 
    4549    // MPEG Stream Listener
    4650    void HandlePAT(const ProgramAssociationTable*);
    4751    void HandleCAT(const ConditionalAccessTable*) {}
     
    5256    void HandleSingleProgramPAT(ProgramAssociationTable *pat);
    5357    void HandleSingleProgramPMT(ProgramMapTable *pmt);
    5458
    55     // ATSC
     59    // ATSC Main
    5660    void HandleSTT(const SystemTimeTable*) {}
    57     void HandleMGT(const MasterGuideTable *) {};
    58     void HandleVCT(uint, const VirtualChannelTable*) {}
     61    void HandleVCT(uint /*tsid*/, const VirtualChannelTable*) {}
     62    void HandleMGT(const MasterGuideTable*) {}
    5963
    60     // DVB
     64    // DVBMainStreamListener
    6165    void HandleTDT(const TimeDateTable*) {}
    6266    void HandleNIT(const NetworkInformationTable*) {}
    6367    void HandleSDT(uint /*tsid*/, const ServiceDescriptionTable*) {}
    6468
     69    // TSPacketListener
     70    bool ProcessTSPacket(const TSPacket &tspacket);
     71
     72    // TSPacketListenerAV
     73    bool ProcessVideoTSPacket(const TSPacket& tspacket);
     74    bool ProcessAudioTSPacket(const TSPacket& tspacket);
     75
     76    // Common audio/visual processing
     77    bool ProcessAVTSPacket(const TSPacket &tspacket);
     78
     79    void SetStreamData(MPEGStreamData*);
     80    MPEGStreamData *GetStreamData(void) { return _stream_data; }
     81    ATSCStreamData *GetATSCStreamData(void);
     82
     83    void BufferedWrite(const TSPacket &tspacket);
     84
    6585  private:
    66     bool AdjustFilters(void);
    67     bool AdjustEITPIDs(void);
     86    void TeardownAll(void);
    6887
    69     void ProcessTSData(const unsigned char *buffer, int len);
    70     bool ProcessTSPacket(const TSPacket& tspacket);
    71     void TeardownAll(void);
    72    
     88    void ReaderPaused(int fd);
     89    bool PauseAndWait(int timeout = 100);
     90
    7391  private:
    74     HDHRChannel                   *_channel;
    75     struct hdhomerun_video_sock_t *_video_socket;
    76     MPEGStreamData                *_stream_data;
     92    HDHRChannel              *_channel;
     93    HDHRStreamHandler        *_stream_handler;
    7794
    78     ProgramAssociationTable       *_input_pat;
    79     ProgramMapTable               *_input_pmt;
    80     bool                           _reset_pid_filters;
    81     uint_vec_t                     _eit_pids;
    82     mutable QMutex                 _pid_lock;
     95    // general recorder stuff
     96    MPEGStreamData          *_stream_data;
     97    mutable QMutex           _pid_lock;
     98    ProgramAssociationTable *_input_pat; ///< PAT on input side
     99    ProgramMapTable         *_input_pmt; ///< PMT on input side
     100    bool                     _has_no_av;
     101
     102    // TS recorder stuff
     103    unsigned char _stream_id[0x1fff + 1];
     104    unsigned char _pid_status[0x1fff + 1];
     105    unsigned char _continuity_counter[0x1fff + 1];
     106
     107    // Constants
     108    static const int TSPACKETS_BETWEEN_PSIP_SYNC;
     109    static const int POLL_INTERVAL;
     110    static const int POLL_WARNING_TIMEOUT;
     111
     112    static const unsigned char kPayloadStartSeen = 0x2;
    83113};
    84114
    85115#endif
  • libs/libmythtv/dbcheck.cpp

     
    1414#include "mythdb.h"
    1515#include "mythverbose.h"
    1616
     17// HDHomeRun headers
     18#ifdef USING_HDHOMERUN
     19#include "hdhomerun.h"
     20#endif
    1721
     22
    1823#define MINIMUM_DBMS_VERSION 5,0,15
    1924
    2025/// This is the DB schema version expected by the running MythTV instance.
    21 const QString currentDatabaseVersion = "1235";
     26const QString currentDatabaseVersion = "1236";
    2227
    2328static bool UpdateDBVersionNumber(const QString &newnumber);
    2429static bool performActualUpdate(
     
    45434548            return false;
    45444549    }
    45454550
     4551    if (dbver == "1235")
     4552    {
     4553/*
     4554#ifdef USING_HDHOMERUN
     4555        VERBOSE(VB_IMPORTANT, "In 1235 upg (HDhomerun tunerid change)");
    45464556
     4557        // First discover all HDhomreun devices on the network
     4558        // This will be cached and used to match up with the database query
     4559
     4560        uint32_t  target_ip   = 0; // WILDCARD
     4561        uint32_t  device_type = HDHOMERUN_DEVICE_TYPE_TUNER;
     4562        uint32_t  device_id   = HDHOMERUN_DEVICE_ID_WILDCARD;
     4563        const int max_count   = 50;
     4564        hdhomerun_discover_device_t hdhr_device_list[max_count];
     4565
     4566        int hdhr_device_count = hdhomerun_discover_find_devices_custom(
     4567                                  target_ip,
     4568                                  device_type,
     4569                                  device_id,
     4570                                  hdhr_device_list,
     4571                                  max_count);
     4572
     4573        if (hdhr_device_count == -1)
     4574        {
     4575            // Can only check for existing devices
     4576            VERBOSE(VB_IMPORTANT, "Error finding HDHomerun devices");
     4577            VERBOSE(VB_IMPORTANT, "All configured HDHomerun devices must be accessible");
     4578            return false;
     4579        }
     4580
     4581        MSqlQuery query(MSqlQuery::InitCon());
     4582        query.prepare("SELECT cardid, videodevice, dbox2_port "
     4583                      "FROM capturecard "
     4584                      "WHERE cardtype = 'HDHOMERUN' "
     4585                      "ORDER BY cardid");
     4586        bool ok = query.exec();
     4587
     4588        MSqlQuery query2(MSqlQuery::InitCon());
     4589        QRegExp newstyle = QRegExp("[0-9A-Z]{8}-[0-9]", Qt::CaseInsensitive);
     4590        QRegExp newwildcard = QRegExp("F{8}-[0-9]", Qt::CaseInsensitive); // "FFFFFFFF-n"
     4591
     4592        while (ok && query.next())
     4593        {
     4594            uint    cardid  = query.value(0).toUInt();
     4595            QString device  = query.value(1).toString();
     4596            uint    tunerid = query.value(2).toUInt();
     4597
     4598            // First check if this is the new style already
     4599            if (device.contains(newstyle))
     4600            {
     4601                QString new_device = "";
     4602                if (device.contains(newwildcard)) // FFFFFFFF-1
     4603                {
     4604                    // Must convert to an actual HDHR
     4605                    // Use the first HDHR found.
     4606
     4607                    QString new_device_id = QString("%1").arg(hdhr_device_list[0].device_id, 8, 16);
     4608                    new_device = device;
     4609                    new_device.replace("ffffffff", new_device_id, Qt::CaseInsensitive);
     4610                } else if (device.toUpper() == device)
     4611                    VERBOSE(VB_GENERAL, QString("Retaining card %1: HDhomerun %2")
     4612                                               .arg(cardid).arg(device));
     4613                else
     4614                {
     4615                    // Convert to upper case
     4616                    new_device = device;
     4617                }
     4618
     4619                if (new_device.length() > 0)
     4620                {
     4621                    QString updatequery = QString("UPDATE capturecard SET videodevice = '%1' "
     4622                                                  "WHERE cardid = %2;")
     4623                                                 .arg(new_device.toUpper())
     4624                                                 .arg(cardid);
     4625                    VERBOSE(VB_GENERAL, QString("Converting card %1: HDhomerun %2 to %3")
     4626                                               .arg(cardid).arg(device).arg(new_device.toUpper()));
     4627
     4628                    if (!query2.exec(updatequery))
     4629                    {
     4630                        MythDB::DBError(
     4631                            "Could not perform update for '1236'", query2);
     4632                        ok = false;
     4633                    }
     4634                }
     4635            }
     4636            else
     4637            {
     4638
     4639                // change from device, tuner to device-tuner
     4640                // i.e.:  AABBCCDD, 1 -> AABBCCDD-1
     4641                // If device is xxxxxxxx then use it directly
     4642                // If device is FFFFFFFF then try to discover the HDHR to get the actual value
     4643                // If device is x.x.x.x (ip address) then try to discover the HDHR to get the actual value
     4644
     4645                bool    valid;
     4646                uint    device_id = device.toUInt(&valid, 16);
     4647
     4648                QString new_device = "";
     4649                if (valid && device_id != HDHOMERUN_DEVICE_ID_WILDCARD
     4650                          && hdhomerun_discover_validate_device_id(device_id))
     4651                {
     4652                    // Valid, non-wildcard device id
     4653                    // Update it to "xxxxxxxx-#"
     4654                    new_device = QString("%1-%2").arg(device).arg(tunerid);
     4655
     4656                }
     4657                else if (valid && device_id == HDHOMERUN_DEVICE_ID_WILDCARD)
     4658                {
     4659                    // Use the first HDHR found.  It should be the only one if this
     4660                    // worked before.
     4661
     4662                    new_device = QString("%1-%2")
     4663                                         .arg(hdhr_device_list[0].device_id, 8, 16)
     4664                                         .arg(tunerid);
     4665                }
     4666                else
     4667                {
     4668
     4669                    // Check for IP address;
     4670
     4671                    struct in_addr address;
     4672                    uint tmp_device_ip;
     4673                    if (inet_aton(device.toUtf8(), &address))
     4674                    {
     4675                        tmp_device_ip = ntohl(address.s_addr);
     4676                        int i;
     4677                        for (i = 0; i < hdhr_device_count; i++)
     4678                        {
     4679                            if (tmp_device_ip == hdhr_device_list[i].ip_addr)
     4680                            {
     4681                                new_device = QString("%1-%2")
     4682                                                     .arg(hdhr_device_list[1].device_id, 8, 16)
     4683                                                     .arg(tunerid);
     4684                                break;
     4685                            }
     4686                        }
     4687                    }
     4688                }
     4689
     4690                // If we identified the HDhomerun device, update it.
     4691                // Otherwise delete it
     4692
     4693                QString updatequery;
     4694                if (new_device.length() > 0)
     4695                {
     4696                    updatequery = QString("UPDATE capturecard SET videodevice = '%1', dbox2_port = 31338 "
     4697                                          "WHERE cardid = %2;")
     4698                                         .arg(new_device.toUpper())
     4699                                         .arg(cardid);
     4700                    VERBOSE(VB_GENERAL, QString("Converting card %1: HDhomerun %2 tuner %3 to %4")
     4701                                               .arg(cardid).arg(device)
     4702                                               .arg(tunerid).arg(new_device.toUpper()));
     4703                }
     4704                else
     4705                {
     4706                    updatequery = QString("DELETE FROM capturecard "
     4707                                          "WHERE cardid = %1;"
     4708                                          "AND dbox2_port = %2;")
     4709                                         .arg(cardid);
     4710                    VERBOSE(VB_IMPORTANT, QString("Couldn't find card %1: HDHomerun %2 tuner %3 - deleting")
     4711                                                 .arg(cardid).arg(device).arg(tunerid));
     4712                }
     4713   
     4714                if (!query2.exec(updatequery))
     4715                {
     4716                    MythDB::DBError(
     4717                        "Could not perform update for '1236'", query2);
     4718                    ok = false;
     4719                }
     4720            }
     4721        }
     4722
     4723        if (!ok)
     4724            return false;
     4725#endif // USING_HDHOMERUN
     4726*/
     4727        const char * updates[] = { NULL };
     4728
     4729        if (!performActualUpdate(updates, "1236", dbver))
     4730            return false;
     4731        VERBOSE(VB_IMPORTANT, "DBCheck: done with HDhomerun upgrade");
     4732    }
     4733
    45474734    return true;
    45484735}
    45494736
  • libs/libmythtv/hdhrsignalmonitor.cpp

     
    1818
    1919#include "hdhrchannel.h"
    2020#include "hdhrrecorder.h"
     21#include "hdhrstreamhandler.h"
    2122
    2223#define LOC QString("HDHRSM(%1): ").arg(channel->GetDevice())
    2324#define LOC_ERR QString("HDHRSM(%1), Error: ").arg(channel->GetDevice())
     
    3940HDHRSignalMonitor::HDHRSignalMonitor(
    4041    int db_cardnum, HDHRChannel* _channel, uint64_t _flags) :
    4142    DTVSignalMonitor(db_cardnum, _channel, _flags),
    42     dtvMonitorRunning(false)
     43    streamHandlerStarted(false), streamHandler(NULL)
    4344{
    4445    VERBOSE(VB_CHANNEL, LOC + "ctor");
    4546
    46     _channel->DelAllPIDs();
    47 
    4847    signalStrength.SetThreshold(45);
    4948
    5049    AddFlags(kSigMon_WaitForSig);
     50
     51    streamHandler = HDHRStreamHandler::Get(_channel->GetDevice());
    5152}
    5253
    5354/** \fn HDHRSignalMonitor::~HDHRSignalMonitor()
     
    5758{
    5859    VERBOSE(VB_CHANNEL, LOC + "dtor");
    5960    Stop();
     61    HDHRStreamHandler::Return(streamHandler);
    6062}
    6163
    6264/** \fn HDHRSignalMonitor::Stop(void)
     
    6668{
    6769    VERBOSE(VB_CHANNEL, LOC + "Stop() -- begin");
    6870    SignalMonitor::Stop();
    69     if (dtvMonitorRunning)
    70     {
    71         dtvMonitorRunning = false;
    72         pthread_join(table_monitor_thread, NULL);
    73     }
     71    if (GetStreamData())
     72        streamHandler->RemoveListener(GetStreamData());
     73    streamHandlerStarted = false;
     74
    7475    VERBOSE(VB_CHANNEL, LOC + "Stop() -- end");
    7576}
    7677
    77 void *HDHRSignalMonitor::TableMonitorThread(void *param)
     78HDHRChannel *HDHRSignalMonitor::GetHDHRChannel(void)
    7879{
    79     HDHRSignalMonitor *mon = (HDHRSignalMonitor*) param;
    80     mon->RunTableMonitor();
    81     return NULL;
     80    return dynamic_cast<HDHRChannel*>(channel);
    8281}
    8382
    84 bool HDHRSignalMonitor::UpdateFiltersFromStreamData(void)
    85 {
    86     vector<int> add_pids;
    87     vector<int> del_pids;
    88 
    89     if (!GetStreamData())
    90         return false;
    91 
    92     UpdateListeningForEIT();
    93 
    94     const pid_map_t &listening = GetStreamData()->ListeningPIDs();
    95 
    96     // PIDs that need to be added..
    97     pid_map_t::const_iterator lit = listening.constBegin();
    98     for (; lit != listening.constEnd(); ++lit)
    99         if (*lit && (filters.find(lit.key()) == filters.end()))
    100             add_pids.push_back(lit.key());
    101 
    102     // PIDs that need to be removed..
    103     FilterMap::const_iterator fit = filters.constBegin();
    104     for (; fit != filters.constEnd(); ++fit)
    105         if (listening.find(fit.key()) == listening.end())
    106             del_pids.push_back(fit.key());
    107 
    108     HDHRChannel *hdhr = dynamic_cast<HDHRChannel*>(channel);
    109     if (!hdhr)
    110         return false;
    111 
    112     // Remove PIDs
    113     bool ok = true;
    114     vector<int>::iterator dit = del_pids.begin();
    115     for (; dit != del_pids.end(); ++dit)
    116     {
    117         ok &= hdhr->DelPID(*dit);
    118         filters.erase(filters.find(*dit));
    119     }
    120 
    121     // Add PIDs
    122     vector<int>::iterator ait = add_pids.begin();
    123     for (; ait != add_pids.end(); ++ait)
    124     {
    125         ok &= hdhr->AddPID(*ait);
    126         filters[*ait] = 1;
    127     }
    128 
    129     return ok;
    130 }
    131 
    132 void HDHRSignalMonitor::RunTableMonitor(void)
    133 {
    134     dtvMonitorRunning = true;
    135 
    136     struct hdhomerun_video_sock_t *_video_socket;
    137     _video_socket = hdhomerun_video_create(0, VIDEO_DATA_BUFFER_SIZE_1S);
    138     if (!_video_socket)
    139     {
    140         VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to get video socket");
    141         return;
    142     }
    143 
    144     HDHRChannel *hdrc = dynamic_cast<HDHRChannel*>(channel);
    145     if (!hdrc)
    146         return;
    147 
    148     uint localPort = hdhomerun_video_get_local_port(_video_socket);
    149     if (!hdrc->DeviceSetTarget(localPort))
    150     {
    151         hdhomerun_video_destroy(_video_socket);
    152         VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to set target");
    153         return;
    154     }
    155 
    156     VERBOSE(VB_CHANNEL, LOC + "RunTableMonitor(): " +
    157             QString("begin (# of pids %1)")
    158             .arg(GetStreamData()->ListeningPIDs().size()));
    159 
    160     while (dtvMonitorRunning && GetStreamData())
    161     {
    162         UpdateFiltersFromStreamData();
    163 
    164         size_t data_length;
    165         unsigned char *data_buffer =
    166             hdhomerun_video_recv(_video_socket,
    167                                          VIDEO_DATA_BUFFER_SIZE_1S / 5,
    168                                          &data_length);
    169 
    170         if (data_buffer)
    171         {
    172             GetStreamData()->ProcessData(data_buffer, data_length);
    173             continue;
    174         }
    175 
    176         usleep(2500);
    177     }
    178 
    179     hdrc->DeviceClearTarget();
    180     hdhomerun_video_destroy(_video_socket);
    181 
    182     VERBOSE(VB_CHANNEL, LOC + "RunTableMonitor(): -- shutdown");
    183 
    184     // TODO teardown PID filters here
    185 
    186     VERBOSE(VB_CHANNEL, LOC + "RunTableMonitor(): -- end");
    187 }
    188 
    189 /** \fn HDHRSignalMonitor::UpdateValues()
     83/** \fn HDHRSignalMonitor::UpdateValues(void)
    19084 *  \brief Fills in frontend stats and emits status Qt signals.
    19185 *
    192  *   This function uses five ioctl's FE_READ_SNR, FE_READ_SIGNAL_STRENGTH
    193  *   FE_READ_BER, FE_READ_UNCORRECTED_BLOCKS, and FE_READ_STATUS to obtain
    194  *   statistics from the frontend.
    195  *
    19686 *   This is automatically called by MonitorLoop(), after Start()
    19787 *   has been used to start the signal monitoring thread.
    19888 */
     
    20191    if (!running || exit)
    20292        return;
    20393
    204     if (dtvMonitorRunning)
     94    if (streamHandlerStarted)
    20595    {
    20696        EmitStatus();
    20797        if (IsAllGood())
    20898            SendMessageAllGood();
     99
    209100        // TODO dtv signals...
    210101
    211102        update_done = true;
    212103        return;
    213104    }
    214105
    215     QString msg = ((HDHRChannel*)channel)->TunerGet("status");
     106    QString msg = streamHandler->GetTunerStatus();
    216107    //ss  = signal strength,        [0,100]
    217108    //snq = signal to noise quality [0,100]
    218109    //seq = signal error quality    [0,100]
     
    250141                   kDTVSigMon_WaitForMGT | kDTVSigMon_WaitForVCT |
    251142                   kDTVSigMon_WaitForNIT | kDTVSigMon_WaitForSDT))
    252143    {
    253         pthread_create(&table_monitor_thread, NULL,
    254                        TableMonitorThread, this);
    255 
    256         VERBOSE(VB_CHANNEL, LOC + "UpdateValues() -- "
    257                 "Waiting for table monitor to start");
    258 
    259         while (!dtvMonitorRunning)
    260             usleep(50);
    261 
    262         VERBOSE(VB_CHANNEL, LOC + "UpdateValues() -- "
    263                 "Table monitor started");
     144        streamHandler->AddListener(GetStreamData());
     145        streamHandlerStarted = true;
    264146    }
    265147
    266148    update_done = true;
  • libs/libmythtv/hdhrrecorder.cpp

     
    55 *  Distributed as part of MythTV under GPL v2 and later.
    66 */
    77
    8 // C includes
     8// POSIX includes
    99#include <pthread.h>
     10#include <fcntl.h>
    1011#include <unistd.h>
    1112#include <sys/types.h>
    1213#include <sys/socket.h>
     
    1415#include <arpa/inet.h>
    1516#include <netdb.h>
    1617#include <sys/time.h>
    17 #include <fcntl.h>
    1818
    1919// C++ includes
    2020#include <iostream>
     
    2323
    2424// MythTV includes
    2525#include "RingBuffer.h"
    26 #include "hdhrchannel.h"
    27 #include "hdhrrecorder.h"
    2826#include "atsctables.h"
    2927#include "atscstreamdata.h"
    3028#include "dvbstreamdata.h"
    3129#include "eithelper.h"
    3230#include "tv_rec.h"
    3331
     32// MythTV HDHR includes
     33#include "hdhrchannel.h"
     34#include "hdhrrecorder.h"
     35#include "hdhrstreamhandler.h"
     36
    3437#define LOC QString("HDHRRec(%1): ").arg(tvrec->GetCaptureCardNum())
     38#define LOC_WARN QString("HDHRRec(%1), Warning: ") \
     39                     .arg(tvrec->GetCaptureCardNum())
    3540#define LOC_ERR QString("HDHRRec(%1), Error: ") \
    3641                    .arg(tvrec->GetCaptureCardNum())
    3742
    3843HDHRRecorder::HDHRRecorder(TVRec *rec, HDHRChannel *channel)
    3944    : DTVRecorder(rec),
    40       _channel(channel),        _video_socket(NULL),
     45      _channel(channel),        _stream_handler(NULL),
    4146      _stream_data(NULL),
     47      _pid_lock(QMutex::Recursive),
    4248      _input_pat(NULL),         _input_pmt(NULL),
    43       _reset_pid_filters(false),_pid_lock(QMutex::Recursive)
     49      _has_no_av(false)
    4450{
    4551}
    4652
     
    9197    // HACK -- end
    9298}
    9399
    94 bool HDHRRecorder::Open(void)
     100void HDHRRecorder::HandleSingleProgramPAT(ProgramAssociationTable *pat)
    95101{
    96     VERBOSE(VB_RECORD, LOC + "Open()");
    97     if (_video_socket)
     102    if (!pat)
    98103    {
    99         VERBOSE(VB_RECORD, LOC + "Card already open (recorder)");
    100         return true;
     104        VERBOSE(VB_RECORD, LOC + "HandleSingleProgramPAT(NULL)");
     105        return;
    101106    }
    102107
    103     /* Calculate buffer size */
    104     uint buffersize = gContext->GetNumSetting(
    105         "HDRingbufferSize", 50 * TSPacket::SIZE) * 1024;
    106     buffersize /= VIDEO_DATA_PACKET_SIZE;
    107     buffersize *= VIDEO_DATA_PACKET_SIZE;
     108    if (!ringBuffer)
     109        return;
    108110
    109     // Buffer should be at least about 1MB..
    110     buffersize = max(49 * TSPacket::SIZE * 128, buffersize);
     111    uint posA[2] = { ringBuffer->GetWritePosition(), _payload_buffer.size() };
    111112
    112     /* Create TS socket. */
    113     _video_socket = hdhomerun_video_create(0, buffersize);
    114     if (!_video_socket)
     113    if (pat)
    115114    {
    116         VERBOSE(VB_IMPORTANT, LOC + "Open() failed to open socket");
    117         return false;
     115        uint next_cc = (pat->tsheader()->ContinuityCounter()+1)&0xf;
     116        pat->tsheader()->SetContinuityCounter(next_cc);
     117        DTVRecorder::BufferedWrite(*(reinterpret_cast<TSPacket*>(pat->tsheader())));
    118118    }
    119119
    120     /* Success. */
    121     return true;
    122 }
     120    uint posB[2] = { ringBuffer->GetWritePosition(), _payload_buffer.size() };
    123121
    124 /** \fn HDHRRecorder::StartData(void)
    125  *  \brief Configure device to send video.
    126  */
    127 bool HDHRRecorder::StartData(void)
    128 {
    129     VERBOSE(VB_RECORD, LOC + "StartData()");
    130     uint localPort = hdhomerun_video_get_local_port(_video_socket);
    131     return _channel->DeviceSetTarget(localPort);
     122    if (posB[0] + posB[1] * TSPacket::SIZE >
     123        posA[0] + posA[1] * TSPacket::SIZE)
     124    {
     125        VERBOSE(VB_RECORD, LOC + "Wrote PAT @"
     126                << posA[0] << " + " << (posA[1] * TSPacket::SIZE));
     127    }
     128    else
     129    {
     130        VERBOSE(VB_RECORD, LOC + "Saw PAT but did not write to disk yet");
     131    }
    132132}
    133133
    134 void HDHRRecorder::Close(void)
     134void HDHRRecorder::HandleSingleProgramPMT(ProgramMapTable *pmt)
    135135{
    136     VERBOSE(VB_RECORD, LOC + "Close()");
    137     if (_video_socket)
     136    if (!pmt)
    138137    {
    139         hdhomerun_video_destroy(_video_socket);
    140         _video_socket = NULL;
     138        VERBOSE(VB_RECORD, LOC + "HandleSingleProgramPMT(NULL)");
     139        return;
    141140    }
    142 }
    143141
    144 void HDHRRecorder::ProcessTSData(const uint8_t *buffer, int len)
    145 {
    146     QMutexLocker locker(&_pid_lock);
    147     const uint8_t *data = buffer;
    148     const uint8_t *end = buffer + len;
     142    // collect stream types for H.264 (MPEG-4 AVC) keyframe detection
     143    for (uint i = 0; i < pmt->StreamCount(); i++)
     144        _stream_id[pmt->StreamPID(i)] = pmt->StreamType(i);
    149145
    150     while (data + 188 <= end)
    151     {
    152         if (data[0] != 0x47)
    153         {
    154             return;
    155         }
     146    if (!ringBuffer)
     147        return;
    156148
    157         const TSPacket *tspacket = reinterpret_cast<const TSPacket*>(data);
    158         ProcessTSPacket(*tspacket);
     149    unsigned char buf[8 * 1024];
     150    uint next_cc = (pmt->tsheader()->ContinuityCounter()+1)&0xf;
     151    pmt->tsheader()->SetContinuityCounter(next_cc);
     152    uint size = pmt->WriteAsTSPackets(buf, next_cc);
    159153
    160         data += 188;
    161     }
    162 }
     154    uint posA[2] = { ringBuffer->GetWritePosition(), _payload_buffer.size() };
    163155
    164 void HDHRRecorder::SetStreamData(MPEGStreamData *data)
    165 {
    166     if (data == _stream_data)
    167         return;
     156    for (uint i = 0; i < size ; i += TSPacket::SIZE)
     157        DTVRecorder::BufferedWrite(*(reinterpret_cast<TSPacket*>(&buf[i])));
    168158
    169     MPEGStreamData *old_data = _stream_data;
    170     _stream_data = data;
    171     if (old_data)
    172         delete old_data;
     159    uint posB[2] = { ringBuffer->GetWritePosition(), _payload_buffer.size() };
    173160
    174     if (data)
     161    if (posB[0] + posB[1] * TSPacket::SIZE >
     162        posA[0] + posA[1] * TSPacket::SIZE)
    175163    {
    176         data->AddMPEGSPListener(this);
    177         data->AddMPEGListener(this);
    178 
    179         ATSCStreamData *atsc = dynamic_cast<ATSCStreamData*>(data);
    180         DVBStreamData  *dvb  = dynamic_cast<DVBStreamData*>(data);
    181 
    182         if (atsc && atsc->DesiredMinorChannel())
    183             atsc->SetDesiredChannel(atsc->DesiredMajorChannel(),
    184                                     atsc->DesiredMinorChannel());
    185         else if (dvb)
    186             dvb->AddDVBMainListener(this);
    187         else if (data->DesiredProgram() >= 0)
    188             data->SetDesiredProgram(data->DesiredProgram());
     164        VERBOSE(VB_RECORD, LOC + "Wrote PMT @"
     165                << posA[0] << " + " << (posA[1] * TSPacket::SIZE));
    189166    }
     167    else
     168    {
     169        VERBOSE(VB_RECORD, LOC + "Saw PMT but did not write to disk yet");
     170    }
    190171}
    191172
    192 ATSCStreamData *HDHRRecorder::GetATSCStreamData(void)
    193 {
    194     return dynamic_cast<ATSCStreamData*>(_stream_data);
    195 }
    196 
    197173void HDHRRecorder::HandlePAT(const ProgramAssociationTable *_pat)
    198174{
    199175    if (!_pat)
     
    221197    _input_pat = new ProgramAssociationTable(*_pat);
    222198    delete oldpat;
    223199
    224     _reset_pid_filters = true;
     200    // Listen for the other PMTs for faster channel switching
     201    for (uint i = 0; _input_pat && (i < _input_pat->ProgramCount()); i++)
     202    {
     203        uint pmt_pid = _input_pat->ProgramPID(i);
     204        if (!_stream_data->IsListeningPID(pmt_pid))
     205            _stream_data->AddListeningPID(pmt_pid);
     206    }
    225207}
    226208
    227209void HDHRRecorder::HandlePMT(uint progNum, const ProgramMapTable *_pmt)
     
    233215        VERBOSE(VB_RECORD, LOC + "SetPMT("<<progNum<<")");
    234216        ProgramMapTable *oldpmt = _input_pmt;
    235217        _input_pmt = new ProgramMapTable(*_pmt);
    236         delete oldpmt;
    237218
    238         _reset_pid_filters = true;
    239     }
    240 }
     219        QString sistandard = _channel->GetSIStandard();
    241220
    242 void HDHRRecorder::HandleSingleProgramPAT(ProgramAssociationTable *pat)
    243 {
    244     if (!pat)
    245         return;
     221        bool has_no_av = true;
     222        for (uint i = 0; i < _input_pmt->StreamCount() && has_no_av; i++)
     223        {
     224            has_no_av &= !_input_pmt->IsVideo(i, sistandard);
     225            has_no_av &= !_input_pmt->IsAudio(i, sistandard);
     226        }
     227        _has_no_av = has_no_av;
    246228
    247     int next = (pat->tsheader()->ContinuityCounter()+1)&0xf;
    248     pat->tsheader()->SetContinuityCounter(next);
    249     BufferedWrite(*(reinterpret_cast<TSPacket*>(pat->tsheader())));
     229        delete oldpmt;
     230    }
    250231}
    251232
    252 void HDHRRecorder::HandleSingleProgramPMT(ProgramMapTable *pmt)
    253 {
    254     if (!pmt)
    255         return;
    256 
    257     unsigned char buf[8 * 1024];
    258     uint next_cc = (pmt->tsheader()->ContinuityCounter()+1)&0xf;
    259     pmt->tsheader()->SetContinuityCounter(next_cc);
    260     uint size = pmt->WriteAsTSPackets(buf, next_cc);
    261 
    262     for (uint i = 0; i < size ; i += TSPacket::SIZE)
    263         DTVRecorder::BufferedWrite(*(reinterpret_cast<TSPacket*>(&buf[i])));
    264 }
    265 
    266233/** \fn HDHRRecorder::HandleMGT(const MasterGuideTable*)
    267234 *  \brief Processes Master Guide Table, by enabling the
    268235 *         scanning of all PIDs listed.
     
    280247}
    281248*/
    282249
    283 bool HDHRRecorder::ProcessTSPacket(const TSPacket& tspacket)
     250bool HDHRRecorder::Open(void)
    284251{
    285     bool ok = !tspacket.TransportError();
    286     if (ok && !tspacket.ScramplingControl())
     252    if (IsOpen())
    287253    {
    288         if (tspacket.HasAdaptationField())
    289             GetStreamData()->HandleAdaptationFieldControl(&tspacket);
    290         if (tspacket.HasPayload())
    291         {
    292             const unsigned int lpid = tspacket.PID();
     254        VERBOSE(VB_GENERAL, LOC_WARN + "Card already open");
     255        return true;
     256    }
    293257
    294             if ((GetStreamData()->VideoPIDSingleProgram() > 0x1fff) &&
    295                 _wait_for_keyframe_option)
    296             {
    297                 _wait_for_keyframe_option = false;
    298             }
     258    memset(_stream_id,  0, sizeof(_stream_id));
     259    memset(_pid_status, 0, sizeof(_pid_status));
     260    memset(_continuity_counter, 0xff, sizeof(_continuity_counter));
    299261
    300             // Pass or reject frames based on PID, and parse info from them
    301             if (lpid == GetStreamData()->VideoPIDSingleProgram())
    302             {
    303                 //cerr<<"v";
    304                 _buffer_packets = !FindMPEG2Keyframes(&tspacket);
    305                 BufferedWrite(tspacket);
    306             }
    307             else if (GetStreamData()->IsAudioPID(lpid))
    308             {
    309                 //cerr<<"a";
    310                 _buffer_packets = !FindAudioKeyframes(&tspacket);
    311                 BufferedWrite(tspacket);
    312             }
    313             else if (GetStreamData()->IsListeningPID(lpid))
    314             {
    315                 //cerr<<"t";
    316                 GetStreamData()->HandleTSTables(&tspacket);
    317             }
    318             else if (GetStreamData()->IsWritingPID(lpid))
    319                 BufferedWrite(tspacket);
    320         }
     262    _stream_handler = HDHRStreamHandler::Get(_channel->GetDevice());
     263
     264    VERBOSE(VB_RECORD, LOC + "HDHR opened successfully");
     265
     266    return true;
     267}
     268
     269void HDHRRecorder::Close(void)
     270{
     271    VERBOSE(VB_RECORD, LOC + "Close() -- begin");
     272
     273    if (IsOpen())
     274        HDHRStreamHandler::Return(_stream_handler);
     275
     276    VERBOSE(VB_RECORD, LOC + "Close() -- end");
     277}
     278
     279void HDHRRecorder::SetStreamData(MPEGStreamData *data)
     280{
     281    if (data == _stream_data)
     282        return;
     283
     284    MPEGStreamData *old_data = _stream_data;
     285    _stream_data = data;
     286    if (old_data)
     287        delete old_data;
     288
     289    if (data)
     290    {
     291        data->AddMPEGSPListener(this);
     292        data->AddMPEGListener(this);
     293
     294        DVBStreamData *dvb = dynamic_cast<DVBStreamData*>(data);
     295        if (dvb)
     296            dvb->AddDVBMainListener(this);
     297
     298        ATSCStreamData *atsc = dynamic_cast<ATSCStreamData*>(data);
     299
     300        if (atsc && atsc->DesiredMinorChannel())
     301            atsc->SetDesiredChannel(atsc->DesiredMajorChannel(),
     302                                    atsc->DesiredMinorChannel());
     303        else if (data->DesiredProgram() >= 0)
     304            data->SetDesiredProgram(data->DesiredProgram());
    321305    }
    322     return ok;
    323306}
    324307
     308ATSCStreamData *HDHRRecorder::GetATSCStreamData(void)
     309{
     310    return dynamic_cast<ATSCStreamData*>(_stream_data);
     311}
     312
    325313void HDHRRecorder::StartRecording(void)
    326314{
    327315    VERBOSE(VB_RECORD, LOC + "StartRecording -- begin");
     
    337325    _request_recording = true;
    338326    _recording = true;
    339327
    340     if (!StartData())
    341     {
    342         VERBOSE(VB_IMPORTANT, LOC_ERR + "Starting recording "
    343                 "(set target failed). Aborting.");
    344         Close();
    345         _error = true;
    346         VERBOSE(VB_RECORD, LOC + "StartRecording -- end 2");
    347         return;
    348     }
     328    // Make sure the first things in the file are a PAT & PMT
     329    bool tmp = _wait_for_keyframe_option;
     330    _wait_for_keyframe_option = false;
     331    HandleSingleProgramPAT(_stream_data->PATSingleProgram());
     332    HandleSingleProgramPMT(_stream_data->PMTSingleProgram());
     333    _wait_for_keyframe_option = tmp;
    349334
    350     hdhomerun_video_flush(_video_socket);
     335    _stream_data->AddAVListener(this);
     336    _stream_data->AddWritingListener(this);
     337    _stream_handler->AddListener(_stream_data);
     338   
    351339    while (_request_recording && !_error)
    352340    {
     341        usleep(50000);
     342
    353343        if (PauseAndWait())
    354344            continue;
    355345
    356         if (_stream_data)
     346        if (!_input_pmt)
    357347        {
    358             QMutexLocker read_lock(&_pid_lock);
    359             _reset_pid_filters |= _stream_data->HasEITPIDChanges(_eit_pids);
     348            VERBOSE(VB_GENERAL, LOC_WARN +
     349                    "Recording will not commence until a PMT is set.");
     350            usleep(5000);
     351            continue;
    360352        }
    361353
    362         if (_reset_pid_filters)
     354        if (!_stream_handler->IsRunning())
    363355        {
    364             _reset_pid_filters = false;
    365             VERBOSE(VB_RECORD, LOC + "Resetting Demux Filters");
    366             AdjustFilters();
     356            _error = true;
     357
     358            VERBOSE(VB_IMPORTANT, LOC_ERR +
     359                    "Stream handler died unexpectedly.");
    367360        }
    368 
    369         size_t read_size = 64 * 1024; // read about 64KB
    370         read_size /= VIDEO_DATA_PACKET_SIZE;
    371         read_size *= VIDEO_DATA_PACKET_SIZE;
    372 
    373         size_t data_length;
    374         unsigned char *data_buffer =
    375             hdhomerun_video_recv(_video_socket, read_size, &data_length);
    376         if (!data_buffer)
    377         {
    378             usleep(5000);
    379             continue;
    380         }
    381 
    382         ProcessTSData(data_buffer, data_length);
    383361    }
    384362
    385363    VERBOSE(VB_RECORD, LOC + "StartRecording -- ending...");
    386364
    387     _channel->DeviceClearTarget();
     365    _stream_handler->RemoveListener(_stream_data);
     366    _stream_data->RemoveWritingListener(this);
     367    _stream_data->RemoveAVListener(this);
     368
    388369    Close();
    389370
    390371    FinishRecording();
     372
    391373    _recording = false;
    392374
    393375    VERBOSE(VB_RECORD, LOC + "StartRecording -- end");
    394376}
    395377
    396 bool HDHRRecorder::AdjustFilters(void)
     378void HDHRRecorder::ResetForNewFile(void)
    397379{
    398     QMutexLocker change_lock(&_pid_lock);
     380    DTVRecorder::ResetForNewFile();
    399381
    400     if (!_channel)
     382    memset(_stream_id,  0, sizeof(_stream_id));
     383    memset(_pid_status, 0, sizeof(_pid_status));
     384    memset(_continuity_counter, 0xff, sizeof(_continuity_counter));
     385}
     386
     387void HDHRRecorder::StopRecording(void)
     388{
     389    _request_recording = false;
     390    while (_recording)
     391        usleep(2000);
     392}
     393
     394bool HDHRRecorder::PauseAndWait(int timeout)
     395{
     396    if (request_pause)
    401397    {
    402         VERBOSE(VB_IMPORTANT, LOC_ERR + "AdjustFilters() no channel");
    403         return false;
     398        QMutex waitlock;
     399        if (!paused)
     400        {
     401            assert(_stream_handler);
     402            assert(_stream_data);
     403
     404            _stream_handler->RemoveListener(_stream_data);
     405
     406            paused = true;
     407            pauseWait.wakeAll();
     408            if (tvrec)
     409                tvrec->RecorderPaused();
     410        }
     411        waitlock.lock();
     412        unpauseWait.wait(&waitlock, timeout);
    404413    }
    405414
    406     if (!_input_pat || !_input_pmt)
     415    if (!request_pause && paused)
    407416    {
    408         VERBOSE(VB_IMPORTANT, LOC + "AdjustFilters() no pmt or no pat");
    409         return false;
     417        paused = false;
     418
     419        assert(_stream_handler);
     420        assert(_stream_data);
     421
     422        _stream_handler->AddListener(_stream_data);
    410423    }
    411424
    412     uint_vec_t add_pid;
     425    return paused;
     426}
    413427
    414     add_pid.push_back(MPEG_PAT_PID);
    415     _stream_data->AddListeningPID(MPEG_PAT_PID);
     428bool HDHRRecorder::ProcessVideoTSPacket(const TSPacket &tspacket)
     429{
     430    uint streamType = _stream_id[tspacket.PID()];
    416431
    417     for (uint i = 0; i < _input_pat->ProgramCount(); i++)
     432    // Check for keyframes and count frames
     433    if (streamType == StreamID::H264Video)
    418434    {
    419         add_pid.push_back(_input_pat->ProgramPID(i));
    420         _stream_data->AddListeningPID(_input_pat->ProgramPID(i));
     435        _buffer_packets = !FindH264Keyframes(&tspacket);
     436        if (!_seen_sps)
     437            return true;
    421438    }
    422 
    423     // Record the streams in the PMT...
    424     bool need_pcr_pid = true;
    425     for (uint i = 0; i < _input_pmt->StreamCount(); i++)
     439    else
    426440    {
    427         add_pid.push_back(_input_pmt->StreamPID(i));
    428         need_pcr_pid &= (_input_pmt->StreamPID(i) != _input_pmt->PCRPID());
    429         _stream_data->AddWritingPID(_input_pmt->StreamPID(i));
     441        _buffer_packets = !FindMPEG2Keyframes(&tspacket);
    430442    }
    431443
    432     if (need_pcr_pid && (_input_pmt->PCRPID()))
     444    return ProcessAVTSPacket(tspacket);
     445}
     446
     447bool HDHRRecorder::ProcessAudioTSPacket(const TSPacket &tspacket)
     448{
     449    _buffer_packets = !FindAudioKeyframes(&tspacket);
     450    return ProcessAVTSPacket(tspacket);
     451}
     452
     453/// Common code for processing either audio or video packets
     454bool HDHRRecorder::ProcessAVTSPacket(const TSPacket &tspacket)
     455{
     456    const uint pid = tspacket.PID();
     457    // Sync recording start to first keyframe
     458    if (_wait_for_keyframe_option && _first_keyframe < 0)
     459        return true;
     460
     461    // Sync streams to the first Payload Unit Start Indicator
     462    // _after_ first keyframe iff _wait_for_keyframe_option is true
     463    if (!(_pid_status[pid] & kPayloadStartSeen) && tspacket.HasPayload())
    433464    {
    434         add_pid.push_back(_input_pmt->PCRPID());
    435         _stream_data->AddWritingPID(_input_pmt->PCRPID());
     465        if (!tspacket.PayloadStart())
     466            return true; // not payload start - drop packet
     467
     468        VERBOSE(VB_RECORD,
     469                QString("PID 0x%1 Found Payload Start").arg(pid,0,16));
     470
     471        _pid_status[pid] |= kPayloadStartSeen;
    436472    }
    437473
    438     // Adjust for EIT
    439     AdjustEITPIDs();
    440     for (uint i = 0; i < _eit_pids.size(); i++)
     474    BufferedWrite(tspacket);
     475
     476    return true;
     477}
     478
     479bool HDHRRecorder::ProcessTSPacket(const TSPacket &tspacket)
     480{
     481    // Only create fake keyframe[s] if there are no audio/video streams
     482    if (_input_pmt && _has_no_av)
    441483    {
    442         add_pid.push_back(_eit_pids[i]);
    443         _stream_data->AddListeningPID(_eit_pids[i]);
     484        _buffer_packets = !FindOtherKeyframes(&tspacket);
    444485    }
     486    else
     487    {
     488        // There are audio/video streams. Only write the packet
     489        // if audio/video key-frames have been found
     490        if (_wait_for_keyframe_option && _first_keyframe < 0)
     491            return true;
    445492
    446     // Delete filters for pids we no longer wish to monitor
    447     vector<uint>::const_iterator it;
    448     vector<uint> pids = _channel->GetPIDs();
    449     for (it = pids.begin(); it != pids.end(); ++it)
    450     {
    451         if (find(add_pid.begin(), add_pid.end(), *it) == add_pid.end())
    452         {
    453             _stream_data->RemoveListeningPID(*it);
    454             _stream_data->RemoveWritingPID(*it);
    455             _channel->DelPID(*it, false);
    456         }
     493        _buffer_packets = true;
    457494    }
    458495
    459     for (it = add_pid.begin(); it != add_pid.end(); ++it)
    460         _channel->AddPID(*it, false);
     496    BufferedWrite(tspacket);
    461497
    462     _channel->UpdateFilters();
    463 
    464     return add_pid.size();
     498    return true;
    465499}
    466500
    467 /** \fn HDHRRecorder::AdjustEITPIDs(void)
    468  *  \brief Adjusts EIT PID monitoring to monitor the right number of EIT PIDs.
    469  */
    470 bool HDHRRecorder::AdjustEITPIDs(void)
     501void HDHRRecorder::BufferedWrite(const TSPacket &tspacket)
    471502{
    472     bool changes = false;
    473     uint_vec_t add, del;
     503    // Care must be taken to make sure that the packet actually gets written
     504    // as the decision to actually write it has already been made
    474505
    475     QMutexLocker change_lock(&_pid_lock);
     506    // Do we have to buffer the packet for exact keyframe detection?
     507    if (_buffer_packets)
     508    {
     509        int idx = _payload_buffer.size();
     510        _payload_buffer.resize(idx + TSPacket::SIZE);
     511        memcpy(&_payload_buffer[idx], tspacket.data(), TSPacket::SIZE);
     512        return;
     513    }
    476514
    477     if (GetStreamData()->HasEITPIDChanges(_eit_pids))
    478         changes = GetStreamData()->GetEITPIDChanges(_eit_pids, add, del);
    479 
    480     if (!changes)
    481         return false;
    482 
    483     for (uint i = 0; i < del.size(); i++)
     515    // We are free to write the packet, but if we have buffered packet[s]
     516    // we have to write them first...
     517    if (!_payload_buffer.empty())
    484518    {
    485         uint_vec_t::iterator it;
    486         it = find(_eit_pids.begin(), _eit_pids.end(), del[i]);
    487         if (it != _eit_pids.end())
    488             _eit_pids.erase(it);
     519        if (ringBuffer)
     520            ringBuffer->Write(&_payload_buffer[0], _payload_buffer.size());
     521        _payload_buffer.clear();
    489522    }
    490523
    491     for (uint i = 0; i < add.size(); i++)
    492         _eit_pids.push_back(add[i]);
    493 
    494     return true;
     524    if (ringBuffer)
     525        ringBuffer->Write(tspacket.data(), TSPacket::SIZE);
    495526}
    496 
  • libs/libmythtv/hdhrchannel.cpp

     
    2525#include "hdhrchannel.h"
    2626#include "videosource.h"
    2727#include "channelutil.h"
     28#include "hdhrstreamhandler.h"
    2829
     30// HDHomeRun header
     31#include "hdhomerun.h"
     32
    2933#define DEBUG_PID_FILTERS
    3034
    3135#define LOC     QString("HDHRChan(%1): ").arg(GetDevice())
    3236#define LOC_ERR QString("HDHRChan(%1), Error: ").arg(GetDevice())
    3337
    34 HDHRChannel::HDHRChannel(TVRec *parent, const QString &device, uint tuner)
    35     : DTVChannel(parent),       _control_socket(NULL),
    36       _device_id(0),            _device_ip(0),
    37       _tuner(tuner),            _lock(QMutex::Recursive)
     38HDHRChannel::HDHRChannel(TVRec *parent, const QString &device)
     39    : DTVChannel(parent),       _stream_handler(NULL),
     40      _device_id(device),       _lock(QMutex::Recursive),
     41      tune_lock(QMutex::Recursive),
     42      hw_lock(QMutex::Recursive)
    3843{
    39     bool valid;
    40     _device_id = device.toUInt(&valid, 16);
    41 
    42     if (valid && hdhomerun_discover_validate_device_id(_device_id))
    43         return;
    44 
    45     /* Otherwise, is it a valid IP address? */
    46     struct in_addr address;
    47     if (inet_aton(device.toLatin1().constData(), &address))
    48     {
    49         _device_ip = ntohl(address.s_addr);
    50         return;
    51     }
    52 
    53     /* Invalid, use wildcard device ID. */
    54     VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Invalid DeviceID '%1'")
    55             .arg(device));
    56 
    57     _device_id = HDHOMERUN_DEVICE_ID_WILDCARD;
    5844}
    5945
    6046HDHRChannel::~HDHRChannel(void)
     
    6450
    6551bool HDHRChannel::Open(void)
    6652{
     53    VERBOSE(VB_CHANNEL, LOC + "Opening HDHR channel");
     54
     55    QMutexLocker locker(&hw_lock);
     56   
    6757    if (IsOpen())
    6858        return true;
    6959
    70     if (!FindDevice())
    71         return false;
     60    _stream_handler = HDHRStreamHandler::Get(_device_id);
    7261
    7362    if (!InitializeInputs())
    74         return false;
    75 
    76     return (_device_ip != 0) && Connect();
    77 }
    78 
    79 void HDHRChannel::Close(void)
    80 {
    81     if (_control_socket)
    8263    {
    83         hdhomerun_control_destroy(_control_socket);
    84         _control_socket = NULL;
    85     }
    86 }
    87 
    88 bool HDHRChannel::EnterPowerSavingMode(void)
    89 {
    90     return QString::null != TunerSet("channel", "none", false);
    91 }
    92 
    93 bool HDHRChannel::FindDevice(void)
    94 {
    95     if (!_device_id)
    96         return _device_ip;
    97 
    98     _device_ip = 0;
    99 
    100     /* Discover. */
    101     struct hdhomerun_discover_device_t result;
    102     int ret = hdhomerun_discover_find_devices_custom(
    103                   0, HDHOMERUN_DEVICE_TYPE_WILDCARD, _device_id, &result, 1);
    104     if (ret < 0)
    105     {
    106         VERBOSE(VB_IMPORTANT,
    107                 LOC_ERR + "Unable to send discovery request" + ENO);
     64        Close();
    10865        return false;
    10966    }
    110     if (ret == 0)
    111     {
    112         VERBOSE(VB_IMPORTANT, LOC_ERR + QString("device not found"));
    113         return false;
    114     }
    11567
    116     /* Found. */
    117     _device_ip = result.ip_addr;
    118 
    119     VERBOSE(VB_IMPORTANT, LOC +
    120             QString("device found at address %1.%2.%3.%4")
    121             .arg((_device_ip>>24) & 0xFF).arg((_device_ip>>16) & 0xFF)
    122             .arg((_device_ip>> 8) & 0xFF).arg((_device_ip>> 0) & 0xFF));
    123 
    124     return true;
     68    return _stream_handler->IsConnected();
    12569}
    12670
    127 bool HDHRChannel::Connect(void)
     71void HDHRChannel::Close(void)
    12872{
    129     _control_socket = hdhomerun_control_create(_device_id, _device_ip);
    130     if (!_control_socket)
    131     {
    132         VERBOSE(VB_IMPORTANT, LOC_ERR + "Unable to create control socket");
    133         return false;
    134     }
     73    VERBOSE(VB_CHANNEL, LOC + "Closing HDHR channel");
    13574
    136     if (hdhomerun_control_get_local_addr(_control_socket) == 0)
    137     {
    138         VERBOSE(VB_IMPORTANT, LOC_ERR + "Unable to connect to device");
    139         return false;
    140     }
     75        if (!IsOpen())
     76        return; // this caller didn't have it open in the first place..
    14177
    142     VERBOSE(VB_CHANNEL, LOC + "Successfully connected to device");
    143     return true;
     78    HDHRStreamHandler::Return(_stream_handler);
    14479}
    14580
    146 QString HDHRChannel::DeviceGet(const QString &name, bool report_error_return)
     81bool HDHRChannel::EnterPowerSavingMode(void)
    14782{
    148     QMutexLocker locker(&_lock);
    149 
    150     if (!_control_socket)
    151     {
    152         VERBOSE(VB_IMPORTANT, LOC_ERR + "Get request failed (not connected)");
    153         return QString::null;
    154     }
    155 
    156     char *value = NULL;
    157     char *error = NULL;
    158     if (hdhomerun_control_get(_control_socket, name.toLatin1().constData(),
    159                               &value, &error) < 0)
    160     {
    161         VERBOSE(VB_IMPORTANT, LOC_ERR + "Get request failed" + ENO);
    162         return QString::null;
    163     }
    164 
    165     if (report_error_return && error)
    166     {
    167         VERBOSE(VB_IMPORTANT, LOC_ERR +
    168                 QString("DeviceGet(%1): %2").arg(name).arg(error));
    169 
    170         return QString::null;
    171     }
    172 
    173     return QString(value);
     83    if (IsOpen())
     84        return _stream_handler->EnterPowerSavingMode();
     85    else
     86        return true;
    17487}
    17588
    176 QString HDHRChannel::DeviceSet(const QString &name, const QString &val,
    177                                bool report_error_return)
     89bool HDHRChannel::IsOpen(void) const
    17890{
    179     QMutexLocker locker(&_lock);
    180 
    181     if (!_control_socket)
    182     {
    183         VERBOSE(VB_IMPORTANT, LOC_ERR + "Set request failed (not connected)");
    184         return QString::null;
    185     }
    186 
    187     char *value = NULL;
    188     char *error = NULL;
    189     if (hdhomerun_control_set(_control_socket, name.toLatin1().constData(),
    190                               val.toAscii().constData(), &value, &error) < 0)
    191     {
    192         VERBOSE(VB_IMPORTANT, LOC_ERR + "Set request failed" + ENO);
    193 
    194         return QString::null;
    195     }
    196 
    197     if (report_error_return && error)
    198     {
    199         VERBOSE(VB_IMPORTANT, LOC_ERR +
    200                 QString("DeviceSet(%1 %2): %3").arg(name).arg(val).arg(error));
    201 
    202         return QString::null;
    203     }
    204 
    205     return QString(value);
     91      return _stream_handler;
    20692}
    20793
    208 QString HDHRChannel::TunerGet(const QString &name, bool report_error_return)
     94bool HDHRChannel::Init(
     95    QString &inputname, QString &startchannel, bool setchan)
    20996{
    210     return DeviceGet(QString("/tuner%1/%2").arg(_tuner).arg(name),
    211                      report_error_return);
    212 }
     97    if (setchan && !IsOpen())
     98        Open();
    21399
    214 QString HDHRChannel::TunerSet(const QString &name, const QString &value,
    215                               bool report_error_return)
    216 {
    217     return DeviceSet(QString("/tuner%1/%2").arg(_tuner).arg(name), value,
    218                      report_error_return);
     100    return ChannelBase::Init(inputname, startchannel, setchan);
    219101}
    220102
    221 bool HDHRChannel::DeviceSetTarget(unsigned short localPort)
    222 {
    223     if (localPort == 0)
    224     {
    225         return false;
    226     }
    227 
    228     unsigned long localIP = hdhomerun_control_get_local_addr(_control_socket);
    229     if (localIP == 0)
    230     {
    231         return false;
    232     }
    233 
    234     QString configValue = QString("%1.%2.%3.%4:%5")
    235         .arg((localIP >> 24) & 0xFF).arg((localIP >> 16) & 0xFF)
    236         .arg((localIP >>  8) & 0xFF).arg((localIP >>  0) & 0xFF)
    237         .arg(localPort);
    238 
    239     if (TunerSet("target", configValue).isEmpty())
    240     {
    241         return false;
    242     }
    243 
    244     return true;
    245 }
    246 
    247 bool HDHRChannel::DeviceClearTarget()
    248 {
    249     return !TunerSet("target", "0.0.0.0:0").isEmpty();
    250 }
    251 
    252103bool HDHRChannel::SetChannelByString(const QString &channum)
    253104{
    254105    QString loc = LOC + QString("SetChannelByString(%1)").arg(channum);
     
    280131        return SwitchToInput(inputName, channum);
    281132
    282133    ClearDTVInfo();
    283     _ignore_filters = false;
    284134
    285135    InputMap::const_iterator it = inputs.find(currentInputID);
    286136    if (it == inputs.end())
     
    349199    QString tmpX = curchannelname; tmpX.detach();
    350200    inputs[currentInputID]->startChanNum = tmpX;
    351201
    352     // Turn on the HDHomeRun program filtering if it is supported
    353     // and we are tuning to an MPEG program number.
    354202    if (mpeg_prog_num && (GetTuningMode() == "mpeg"))
    355     {
    356         QString pnum = QString::number(mpeg_prog_num);
    357         _ignore_filters = QString::null != TunerSet("program", pnum, false);
    358     }
     203        _stream_handler->TuneProgram(mpeg_prog_num);
    359204
    360205    return true;
    361206}
     
    409254
    410255    QString chan = modulation + ':' + QString::number(frequency);
    411256
    412     VERBOSE(VB_CHANNEL, LOC + "Tune()ing to " + chan);
     257    VERBOSE(VB_CHANNEL, LOC + "Tuning to " + chan);
    413258
    414     if (TunerSet("channel", chan).length())
     259    if (_stream_handler->TuneChannel(chan))
    415260    {
    416261        SetSIStandard(si_std);
    417262        return true;
     
    419264
    420265    return false;
    421266}
    422 
    423 bool HDHRChannel::AddPID(uint pid, bool do_update)
    424 {
    425     QMutexLocker locker(&_lock);
    426 
    427     vector<uint>::iterator it;
    428     it = lower_bound(_pids.begin(), _pids.end(), pid);
    429     if (it != _pids.end() && *it == pid)
    430     {
    431 #ifdef DEBUG_PID_FILTERS
    432         VERBOSE(VB_CHANNEL, "AddPID(0x"<<hex<<pid<<dec<<") NOOP");
    433 #endif // DEBUG_PID_FILTERS
    434         return true;
    435     }
    436 
    437     _pids.insert(it, pid);
    438 
    439 #ifdef DEBUG_PID_FILTERS
    440     VERBOSE(VB_CHANNEL, "AddPID(0x"<<hex<<pid<<dec<<")");
    441 #endif // DEBUG_PID_FILTERS
    442 
    443     if (do_update)
    444         return UpdateFilters();
    445     return true;
    446 }
    447 
    448 bool HDHRChannel::DelPID(uint pid, bool do_update)
    449 {
    450     QMutexLocker locker(&_lock);
    451 
    452     vector<uint>::iterator it;
    453     it = lower_bound(_pids.begin(), _pids.end(), pid);
    454     if (it == _pids.end())
    455     {
    456 #ifdef DEBUG_PID_FILTERS
    457         VERBOSE(VB_CHANNEL, "DelPID(0x"<<hex<<pid<<dec<<") NOOP");
    458 #endif // DEBUG_PID_FILTERS
    459 
    460        return true;
    461     }
    462 
    463     if (*it == pid)
    464     {
    465 #ifdef DEBUG_PID_FILTERS
    466         VERBOSE(VB_CHANNEL, "DelPID(0x"<<hex<<pid<<dec<<") -- found");
    467 #endif // DEBUG_PID_FILTERS
    468         _pids.erase(it);
    469     }
    470     else
    471     {
    472 #ifdef DEBUG_PID_FILTERS
    473         VERBOSE(VB_CHANNEL, "DelPID(0x"<<hex<<pid<<dec<<") -- failed");
    474 #endif // DEBUG_PID_FILTERS
    475     }
    476 
    477     if (do_update)
    478         return UpdateFilters();
    479     return true;
    480 }
    481 
    482 bool HDHRChannel::DelAllPIDs(void)
    483 {
    484     QMutexLocker locker(&_lock);
    485 
    486 #ifdef DEBUG_PID_FILTERS
    487     VERBOSE(VB_CHANNEL, "DelAllPID()");
    488 #endif // DEBUG_PID_FILTERS
    489 
    490     _pids.clear();
    491 
    492     return UpdateFilters();
    493 }
    494 
    495 QString filt_str(uint pid)
    496 {
    497     uint pid0 = (pid / (16*16*16)) % 16;
    498     uint pid1 = (pid / (16*16))    % 16;
    499     uint pid2 = (pid / (16))        % 16;
    500     uint pid3 = pid % 16;
    501     return QString("0x%1%2%3%4")
    502         .arg(pid0,0,16).arg(pid1,0,16)
    503         .arg(pid2,0,16).arg(pid3,0,16);
    504 }
    505 
    506 bool HDHRChannel::UpdateFilters(void)
    507 {
    508     QMutexLocker locker(&_lock);
    509 
    510     QString filter = "";
    511 
    512     vector<uint> range_min;
    513     vector<uint> range_max;
    514 
    515     if (_ignore_filters)
    516         return true;
    517 
    518     for (uint i = 0; i < _pids.size(); i++)
    519     {
    520         uint pid_min = _pids[i];
    521         uint pid_max  = pid_min;
    522         for (uint j = i + 1; j < _pids.size(); j++)
    523         {
    524             if (pid_max + 1 != _pids[j])
    525                 break;
    526             pid_max++;
    527             i++;
    528         }
    529         range_min.push_back(pid_min);
    530         range_max.push_back(pid_max);
    531     }
    532 
    533     if (range_min.size() > 16)
    534     {
    535         range_min.resize(16);
    536         uint pid_max = range_max.back();
    537         range_max.resize(15);
    538         range_max.push_back(pid_max);
    539     }
    540 
    541     for (uint i = 0; i < range_min.size(); i++)
    542     {
    543         filter += filt_str(range_min[i]);
    544         if (range_min[i] != range_max[i])
    545             filter += QString("-%1").arg(filt_str(range_max[i]));
    546         filter += " ";
    547     }
    548 
    549     filter = filter.trimmed();
    550 
    551     QString new_filter = TunerSet("filter", filter);
    552 
    553 #ifdef DEBUG_PID_FILTERS
    554     QString msg = QString("Filter: '%1'").arg(filter);
    555     if (filter != new_filter)
    556         msg += QString("\n\t\t\t\t'%2'").arg(new_filter);
    557 
    558     VERBOSE(VB_CHANNEL, msg);
    559 #endif // DEBUG_PID_FILTERS
    560 
    561     return filter == new_filter;
    562 }
  • libs/libmythtv/hdhrstreamhandler.h

     
     1// -*- Mode: c++ -*-
     2
     3#ifndef _HDHRSTREAMHANDLER_H_
     4#define _HDHRSTREAMHANDLER_H_
     5
     6#include <vector>
     7using namespace std;
     8
     9#include <QMap>
     10#include <QMutex>
     11
     12#include "util.h"
     13#include "DeviceReadBuffer.h"
     14#include "mpegstreamdata.h"
     15
     16class QString;
     17class HDHRStreamHandler;
     18class DTVSignalMonitor;
     19class HDHRChannel;
     20class DeviceReadBuffer;
     21
     22// HDHomeRun headers
     23#ifdef USING_HDHOMERUN
     24#include "hdhomerun.h"
     25#else
     26struct hdhomerun_control_sock_t { int dummy; };
     27#endif
     28
     29typedef QMap<uint,int> FilterMap;
     30
     31//#define RETUNE_TIMEOUT 5000
     32
     33class HDHRStreamHandler : public ReaderPausedCB
     34{
     35    friend void *run_hdhr_stream_handler_thunk(void *param);
     36
     37  public:
     38    static HDHRStreamHandler *Get(const QString &devicename);
     39    static void Return(HDHRStreamHandler * & ref);
     40
     41    void AddListener(MPEGStreamData *data);
     42    void RemoveListener(MPEGStreamData *data);
     43
     44    bool IsRunning(void) const { return _running; }
     45    QString GetTunerStatus(void) const;
     46    bool IsConnected(void) const;
     47
     48    // Commands
     49    bool TuneChannel(const QString &chanid);
     50    bool TuneProgram(uint mpeg_prog_num);
     51    bool EnterPowerSavingMode(void);
     52
     53
     54    // ReaderPausedCB
     55    virtual void ReaderPaused(int fd) { (void) fd; }
     56
     57  private:
     58    HDHRStreamHandler(const QString &);
     59    ~HDHRStreamHandler();
     60
     61    bool FindDevice(void);
     62    bool Connect(void);
     63
     64    QString DeviceGet(const QString &name,
     65                      bool report_error_return = true) const;
     66    QString DeviceSet(const QString &name, const QString &value,
     67                      bool report_error_return = true);
     68
     69    QString TunerGet(const QString &name,
     70                     bool report_error_return = true) const;
     71    QString TunerSet(const QString &name, const QString &value,
     72                     bool report_error_return = true);
     73
     74    bool DeviceSetTarget(short unsigned int);
     75    bool DeviceClearTarget(void);
     76
     77    bool Open(void);
     78    void Close(void);
     79
     80    void Start(void);
     81    void Stop(void);
     82
     83    void Run(void);
     84    void RunTS(void);
     85
     86    void UpdateListeningForEIT(void);
     87    bool UpdateFiltersFromStreamData(void);
     88    bool AddPIDFilter(uint pid, bool do_update = true);
     89    bool RemovePIDFilter(uint pid, bool do_update = true);
     90    bool RemoveAllPIDFilters(void);
     91    bool UpdateFilters(void);
     92
     93    void SetRunning(bool);
     94
     95    PIDPriority GetPIDPriority(uint pid) const;
     96
     97  private:
     98    hdhomerun_control_sock_t  *_control_socket;
     99    hdhomerun_video_sock_t    *_video_socket;
     100    uint                       _device_id;
     101    uint                       _device_ip;
     102    uint                       _tuner;
     103    QString                    _devicename;
     104
     105    mutable QMutex    _start_stop_lock;
     106    bool              _running;
     107    QWaitCondition    _running_state_changed;
     108    pthread_t         _reader_thread;
     109
     110    mutable QMutex    _pid_lock;
     111    vector<uint>      _eit_pids;
     112    vector<uint>      _pid_info; // kept sorted
     113    uint              _open_pid_filters;
     114    MythTimer         _cycle_timer;
     115
     116    mutable QMutex          _listener_lock;
     117    vector<MPEGStreamData*> _stream_data_list;
     118
     119    mutable QMutex          _hdhr_lock;
     120
     121    // for caching TS monitoring supported value.
     122    static QMutex          _rec_supports_ts_monitoring_lock;
     123    static QMap<uint,bool> _rec_supports_ts_monitoring;
     124
     125    // for implementing Get & Return
     126    static QMutex                            _handlers_lock;
     127    static QMap<QString, HDHRStreamHandler*> _handlers;
     128    static QMap<QString, uint>               _handlers_refcnt;
     129};
     130
     131#endif // _HDHRSTREAMHANDLER_H_
  • libs/libmythtv/cardutil.cpp

     
    214214                devs.push_back(subit->filePath());
    215215        }
    216216    }
     217#ifdef USING_HDHOMERUN
     218    else if (rawtype.toUpper() == "HDHOMERUN")
     219    {
     220        uint32_t  target_ip   = 0;
     221        uint32_t  device_type = HDHOMERUN_DEVICE_TYPE_TUNER;
     222        uint32_t  device_id   = HDHOMERUN_DEVICE_ID_WILDCARD;
     223        const int max_count   = 50;
     224        hdhomerun_discover_device_t result_list[max_count];
     225
     226        int result = hdhomerun_discover_find_devices_custom(
     227            target_ip, device_type, device_id, result_list, max_count);
     228
     229        if (result == -1)
     230        {
     231            VERBOSE(VB_IMPORTANT, "CardUtil::ProbeVideoDevices: "
     232                    "Error finding HDHomerun devices");
     233            return devs;
     234        }
     235
     236        if (result == 20)
     237        {
     238            VERBOSE(VB_IMPORTANT, "CardUtil::ProbeVideoDevices: "
     239                    "Warning: may be > 20 HDHomerun devices");
     240        }
     241
     242        // TODO FIXME -- figure out some way to return ip address as well
     243        for (int i = 0; i < result; i++)
     244        {
     245            QString did = QString("%1").arg(result_list[i].device_id, 0, 16);
     246            did = did.toUpper();
     247
     248            devs.push_back(did + "-0");
     249            devs.push_back(did + "-1");
     250        }
     251    }
     252#endif // USING_HDHOMERUN
    217253    else
    218254    {
    219255        VERBOSE(VB_IMPORTANT, QString("CardUtil::ProbeVideoDevices: ") +
     
    223259    return devs;
    224260}
    225261
     262#include <cassert>
    226263QString CardUtil::ProbeDVBType(const QString &device)
    227264{
    228265    QString ret = "ERROR_UNKNOWN";
    229     (void) device;
    230266
     267    if (device.isEmpty())
     268        return ret;
     269
    231270#ifdef USING_DVB
    232271    QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_FRONTEND, device);
     272    assert(dvbdev != device);
    233273    QByteArray dev = dvbdev.toAscii();
    234274    int fd_frontend = open(dev.constData(), O_RDONLY | O_NONBLOCK);
    235275    if (fd_frontend < 0)
    236276    {
    237         VERBOSE(VB_IMPORTANT, "Can't open DVB frontend (" + dvbdev + ").");
     277        VERBOSE(VB_IMPORTANT, QString("Can't open DVB frontend (%1) for %2.")
     278                .arg(dvbdev).arg(device));
    238279        return ret;
    239280    }
    240281
     
    331372    if (device.isEmpty())
    332373        return "ERROR_OPEN";
    333374
    334     return ProbeDVBType(device);
     375    QString subtype = ProbeDVBType(device);
     376    VERBOSE(VB_IMPORTANT,
     377            QString("Raw card type was '%1' subtype is '%2'")
     378            .arg(type).arg(subtype));
     379    return subtype;
    335380}
    336381
    337382/** \fn CardUtil::IsDVBCardType(const QString)
     
    11501195        uint id = 0;
    11511196        for (uint i = 0; !id && (i < 100); i++)
    11521197        {
    1153             name = QString("DVB%1").arg(dev.toUInt());
     1198            bool ok;
     1199            name = QString("DVB%1").arg(dev.toUInt(&ok));
     1200            if (!ok)
     1201                name = QString("HDHR_%1").arg(dev);
    11541202            name += (i) ? QString(":%1").arg(i) : QString("");
    11551203            id = CardUtil::CreateInputGroup(name);
    11561204        }
     
    17531801    }
    17541802    else if (cardtype == "HDHOMERUN")
    17551803    {
    1756         MSqlQuery query(MSqlQuery::InitCon());
    1757         query.prepare(
    1758             "SELECT dbox2_port "
    1759             "FROM capturecard "
    1760             "WHERE cardid = :CARDID");
    1761         query.bindValue(":CARDID", cardid);
    1762 
    1763         if (!query.exec() || !query.isActive() || !query.next())
    1764             label = "[ DB ERROR ]";
    1765         else
    1766             label = QString("[ HDHomeRun : ID %1 Port %2 ]")
    1767                 .arg(videodevice).arg(query.value(0).toString());
     1804        label = QString("[ HDHomeRun : %1 ]").arg(videodevice);
    17681805    }
    17691806    else
    17701807    {
  • libs/libmythtv/videosource.cpp

     
    4848#include "videodev_myth.h"
    4949#endif
    5050
     51#ifdef USING_HDHOMERUN
     52#include "hdhomerun.h"
     53#endif
     54
    5155QMutex XMLTVFindGrabbers::list_lock;
    5256
    5357VideoSourceSelector::VideoSourceSelector(uint           _initial_sourceid,
     
    14071411    CaptureCard &parent;
    14081412 };
    14091413
    1410 class HDHomeRunDeviceID : public LineEditSetting, public CaptureCardDBStorage
     1414class HDHomeRunIP : public TransLabelSetting
    14111415{
    14121416  public:
     1417    HDHomeRunIP()
     1418    {
     1419        setLabel(QObject::tr("IP Address"));
     1420    };
     1421};
     1422
     1423class HDHomeRunTuner : public TransLabelSetting
     1424{
     1425  public:
     1426    HDHomeRunTuner()
     1427    {
     1428        setLabel(QObject::tr("Tuner"));
     1429    };
     1430};
     1431
     1432class HDHomeRunDeviceID : public ComboBoxSetting, public CaptureCardDBStorage
     1433{
     1434  public:
    14131435    HDHomeRunDeviceID(const CaptureCard &parent) :
    1414         LineEditSetting(this),
     1436        ComboBoxSetting(this),
    14151437        CaptureCardDBStorage(this, parent, "videodevice")
    14161438    {
    1417         setValue("FFFFFFFF");
    14181439        setLabel(QObject::tr("Device ID"));
    1419         setHelpText(QObject::tr("IP address or Device ID from the bottom of "
    1420                                 "the HDHomeRun.  You may use "
    1421                                 "'FFFFFFFF' if there is only one unit "
    1422                                 "on your your network."));
     1440        setHelpText(
     1441            QObject::tr(
     1442                "DevicedID and Tuner Number of available HDHomeRun devices."));
     1443        fillSelections("");
     1444    };
     1445
     1446    /// \brief Adds all available device-tuner combinations to list
     1447    /// If current is >= 0 it will be considered available even
     1448    /// if no device exists for it on the network
     1449    void fillSelections(QString current)
     1450    {
     1451        clearSelections();
     1452
     1453        // Get network visible devices
     1454        vector<QString> devs = CardUtil::ProbeVideoDevices("HDHOMERUN");
     1455
     1456        // Add current if needed
     1457        if ((current != "") &&
     1458            (find(devs.begin(), devs.end(), current) == devs.end()))
     1459        {
     1460            devs.push_back(current);
     1461            stable_sort(devs.begin(), devs.end());
     1462        }
     1463
     1464        vector<QString> db = CardUtil::GetVideoDevices("HDHOMERUN");
     1465
     1466        QMap<QString, bool> in_use;
     1467        QString sel = current;
     1468        for (uint i = 0; i < devs.size(); i++)
     1469        {
     1470            const QString dev = devs[i];
     1471            in_use[devs[i]] = find(db.begin(), db.end(), dev) != db.end();
     1472            if (sel == "" && !in_use[devs[i]])
     1473                sel = dev;
     1474        }
     1475
     1476        if (sel == "" && devs.size())
     1477            sel = devs[0];
     1478 
     1479        QString usestr = QString(" -- ");
     1480        usestr += QObject::tr("Warning: already in use");
     1481
     1482        for (uint i = 0; i < devs.size(); i++)
     1483        {
     1484            const QString dev = devs[i];
     1485            QString desc = dev + (in_use[devs[i]] ? usestr : "");
     1486            desc = (current == devs[i]) ? dev : desc;
     1487            addSelection(desc, dev, dev == sel);
     1488        }
    14231489    }
     1490
     1491    virtual void Load(void)
     1492    {
     1493        clearSelections();
     1494        addSelection("");
     1495
     1496        CaptureCardDBStorage::Load();
     1497
     1498        fillSelections(getValue());
     1499    }
    14241500};
    14251501
    14261502class IPTVHost : public LineEditSetting, public CaptureCardDBStorage
     
    14531529    CaptureCard &parent;
    14541530};
    14551531
    1456 class HDHomeRunTunerIndex : public ComboBoxSetting, public CaptureCardDBStorage
     1532class HDHomeRunExtra : public ConfigurationWizard
    14571533{
    14581534  public:
    1459     HDHomeRunTunerIndex(const CaptureCard &parent) :
    1460         ComboBoxSetting(this),
    1461         CaptureCardDBStorage(this, parent, "dbox2_port")
     1535    HDHomeRunExtra(HDHomeRunConfigurationGroup &parent);
     1536    uint GetInstanceCount(void) const
    14621537    {
    1463         setLabel(QObject::tr("Tuner"));
    1464         addSelection("0");
    1465         addSelection("1");
     1538        return (uint) count->intValue();
    14661539    }
     1540
     1541  private:
     1542    InstanceCount *count;
    14671543};
    14681544
     1545HDHomeRunExtra::HDHomeRunExtra(HDHomeRunConfigurationGroup &parent)
     1546    : count(new InstanceCount(parent.parent))
     1547{
     1548    VerticalConfigurationGroup* rec = new VerticalConfigurationGroup(false);
     1549    rec->setLabel(QObject::tr("Recorder Options"));
     1550    rec->setUseLabel(false);
     1551
     1552    rec->addChild(count);
     1553
     1554    addChild(rec);
     1555}
     1556
     1557
    14691558HDHomeRunConfigurationGroup::HDHomeRunConfigurationGroup
    14701559        (CaptureCard& a_parent) :
    14711560    VerticalConfigurationGroup(false, true, false, false),
    14721561    parent(a_parent)
    14731562{
    1474     HDHomeRunDeviceID *device = new HDHomeRunDeviceID(parent);
     1563    setUseLabel(false);
     1564    deviceid = new HDHomeRunDeviceID(parent);
     1565    addChild(deviceid);
     1566    cardip = new HDHomeRunIP();
     1567    cardtuner = new HDHomeRunTuner();
    14751568
    1476     desc = new TransLabelSetting();
     1569    addChild(cardip);
     1570    addChild(cardtuner);
    14771571
    1478     setUseLabel(false);
    1479     addChild(device);
    1480     addChild(desc);
    1481     addChild(new HDHomeRunTunerIndex(parent));
    14821572    addChild(new SignalTimeout(parent, 1000, 250));
    14831573    addChild(new ChannelTimeout(parent, 3000, 1750));
    14841574    addChild(new SingleCardInput(parent));
    14851575
     1576    TransButtonSetting *buttonRecOpt = new TransButtonSetting();
     1577    buttonRecOpt->setLabel(tr("Recording Options"));
     1578    addChild(buttonRecOpt);
    14861579
    14871580    // Wish we could use something like editingFinished() here...
    1488     connect(device, SIGNAL(valueChanged(const QString&)),
    1489             this,   SLOT(  probeCard(   const QString&)));
     1581    connect(deviceid,     SIGNAL(valueChanged(const QString&)),
     1582            this,         SLOT(  probeCard   (const QString&)));
     1583    connect(buttonRecOpt, SIGNAL(pressed()),
     1584            this,         SLOT(  HDHomeRunExtraPanel()));
     1585
     1586//    addChild(desc);
    14901587};
    14911588
    1492 void HDHomeRunConfigurationGroup::probeCard(const QString &device)
     1589void HDHomeRunConfigurationGroup::probeCard(const QString &deviceid)
    14931590{
    1494     if (device.contains('.') || device.contains(QRegExp("^[0-9a-fA-F]{8}$")))
    1495         desc->setValue(CardUtil::GetHDHRdesc(device));
     1591#ifdef USING_HDHOMERUN
     1592    hdhomerun_device_t *thisdevice =
     1593        hdhomerun_device_create_from_str(deviceid.toLocal8Bit().constData());
     1594
     1595    if (thisdevice)
     1596    {
     1597        uint device_ip = hdhomerun_device_get_device_ip(thisdevice);
     1598        uint tuner     = hdhomerun_device_get_tuner(thisdevice);
     1599        hdhomerun_device_destroy(thisdevice);
     1600
     1601        QString ip = QString("%1.%2.%3.%4")
     1602                .arg((device_ip>>24) & 0xFF).arg((device_ip>>16) & 0xFF)
     1603                .arg((device_ip>> 8) & 0xFF).arg((device_ip>> 0) & 0xFF);
     1604
     1605        cardip->setValue(ip);
     1606        cardtuner->setValue(QString("%1").arg(tuner));
     1607    }
    14961608    else
    1497         desc->setValue(tr("Badly formatted Device ID"));
     1609    {
     1610        cardip->setValue("Unknown");
     1611        cardtuner->setValue("Unknown");
     1612    }
     1613#endif // USING_HDHOMERUN
    14981614}
    14991615
     1616void HDHomeRunConfigurationGroup::HDHomeRunExtraPanel(void)
     1617{
     1618    parent.reload(); // ensure card id is valid
     1619
     1620    HDHomeRunExtra acw(*this);
     1621    acw.exec();
     1622    parent.SetInstanceCount(acw.GetInstanceCount());
     1623}
     1624
    15001625V4LConfigurationGroup::V4LConfigurationGroup(CaptureCard& a_parent) :
    15011626    VerticalConfigurationGroup(false, true, false, false),
    15021627    parent(a_parent),
     
    16821807    addChild(new Hostname(*this));
    16831808}
    16841809
     1810QString CaptureCard::GetRawCardType(void) const
     1811{
     1812    int cardid = getCardID();
     1813    if (cardid <= 0)
     1814        return QString::null;
     1815    return CardUtil::GetRawCardType(cardid);
     1816}
     1817
    16851818void CaptureCard::fillSelections(SelectSetting *setting)
    16861819{
    16871820    MSqlQuery query(MSqlQuery::InitCon());
     
    17101843        if ((cardtype.toLower() == "dvb") && (1 != ++device_refs[videodevice]))
    17111844            continue;
    17121845
     1846        if ((cardtype.toLower() == "hdhomerun") && (1 != ++device_refs[videodevice]))
     1847            continue;
     1848
    17131849        QString label = CardUtil::GetDeviceLabel(
    17141850            cardid, cardtype, videodevice);
    17151851
     
    28532989        if ((cardtype.toLower() == "dvb") && (1 != ++device_refs[videodevice]))
    28542990            continue;
    28552991
     2992        if ((cardtype.toLower() == "hdhomerun") && (1 != ++device_refs[videodevice]))
     2993            continue;
     2994
    28562995        QStringList        inputLabels;
    28572996        vector<CardInput*> cardInputs;
    28582997
     
    29133052
    29143053void DVBConfigurationGroup::probeCard(const QString &videodevice)
    29153054{
    2916     (void) videodevice;
     3055    if (videodevice.isEmpty())
     3056    {
     3057        cardname->setValue("");
     3058        cardtype->setValue("");
     3059        return;
     3060    }
    29173061
     3062    if (parent.getCardID() && parent.GetRawCardType() != "DVB")
     3063    {
     3064        cardname->setValue("");
     3065        cardtype->setValue("");
     3066        return;
     3067    }
     3068
    29183069#ifdef USING_DVB
    29193070    QString frontend_name = CardUtil::ProbeDVBFrontendName(videodevice);
    2920     QString subtype       = CardUtil::ProbeDVBType(videodevice);
     3071    QString subtype = CardUtil::ProbeDVBType(videodevice);
    29213072
    29223073    QString err_open  = tr("Could not open card %1").arg(videodevice);
    29233074    QString err_other = tr("Could not get card info for card %1").arg(videodevice);
  • libs/libmythtv/tv_rec.cpp

     
    181181    else if (genOpt.cardtype == "HDHOMERUN")
    182182    {
    183183#ifdef USING_HDHOMERUN
    184         channel = new HDHRChannel(this, genOpt.videodev, dboxOpt.port);
     184        channel = new HDHRChannel(this, genOpt.videodev);
    185185        if (!channel->Open())
    186186            return false;
    187187        InitChannel(genOpt.defaultinput, startchannel);
     
    35013501            return;
    35023502
    35033503        ClearFlags(kFlagWaitingForRecPause);
    3504 #ifdef USING_HDHOMERUN
    3505         if (GetHDHRRecorder())
    3506         {
    3507             // We currently need to close the file descriptor for
    3508             // HDHomeRun signal monitoring to work.
    3509             GetHDHRRecorder()->Close();
    3510             GetHDHRRecorder()->SetRingBuffer(NULL);
    3511         }
    3512 #endif // USING_HDHOMERUN
    35133504        VERBOSE(VB_RECORD, LOC + "Recorder paused, calling TuningFrequency");
    35143505        TuningFrequency(lastTuningRequest);
    35153506    }
     
    41484139    }
    41494140    recorder->Reset();
    41504141
    4151 #ifdef USING_HDHOMERUN
    4152     if (GetHDHRRecorder())
    4153     {
    4154         pauseNotify = false;
    4155         GetHDHRRecorder()->Close();
    4156         pauseNotify = true;
    4157         GetHDHRRecorder()->Open();
    4158         GetHDHRRecorder()->StartData();
    4159     }
    4160 #endif // USING_HDHOMERUN
    4161 
    41624142    // Set file descriptor of channel from recorder for V4L
    41634143    channel->SetFd(recorder->GetVideoFd());
    41644144
  • libs/libmythtv/hdhrchannel.h

     
    88#define HDHOMERUNCHANNEL_H
    99
    1010// Qt headers
    11 #include <qstring.h>
     11#include <QString>
    1212
    1313// MythTV headers
    1414#include "dtvchannel.h"
    1515
    16 // HDHomeRun headers
    17 #ifdef USING_HDHOMERUN
    18 #include "hdhomerun.h"
    19 #else
    20 struct hdhomerun_control_sock_t { int dummy; };
    21 #endif
     16class HDHRChannel;
     17class HDHRStreamHandler;
     18class ProgramMapTable;
    2219
    23 typedef struct hdhomerun_control_sock_t hdhr_socket_t;
    24 
    2520class HDHRChannel : public DTVChannel
    2621{
    2722    friend class HDHRSignalMonitor;
    2823    friend class HDHRRecorder;
    2924
    3025  public:
    31     HDHRChannel(TVRec *parent, const QString &device, uint tuner);
     26    HDHRChannel(TVRec *parent, const QString &device);
    3227    ~HDHRChannel(void);
    3328
    3429    bool Open(void);
    3530    void Close(void);
    3631    bool EnterPowerSavingMode(void);
    3732
     33    bool Init(QString &inputname, QString &startchannel, bool setchan);
     34
    3835    // Sets
    3936    bool SetChannelByString(const QString &chan);
    4037
    4138    // Gets
    42     bool IsOpen(void) const { return (_control_socket != NULL); }
    43     QString GetDevice(void) const
    44         { return QString("%1/%2").arg(_device_id, 8, 16).arg(_tuner); }
    45     vector<uint> GetPIDs(void) const
    46         { QMutexLocker locker(&_lock); return _pids; }
    47     QString GetSIStandard(void) const { return "atsc"; }
     39    bool IsOpen(void) const;
     40    QString GetDevice(void) const { return _device_id; }
    4841
    49     // Commands
    50     bool AddPID(uint pid, bool do_update = true);
    51     bool DelPID(uint pid, bool do_update = true);
    52     bool DelAllPIDs(void);
    53     bool UpdateFilters(void);
    54 
    5542    // ATSC/DVB scanning/tuning stuff
    5643    bool TuneMultiplex(uint mplexid, QString inputname);
    5744    bool Tune(const DTVMultiplex &tuning, QString inputname);
    5845
    5946  private:
    60     bool FindDevice(void);
    61     bool Connect(void);
    6247    bool Tune(uint frequency, QString inputname,
    6348              QString modulation, QString si_std);
    6449
    65     bool DeviceSetTarget(unsigned short localPort);
    66     bool DeviceClearTarget(void);
     50  private:
     51    HDHRStreamHandler *_stream_handler;
    6752
    68     QString DeviceGet(const QString &name, bool report_error_return = true);
    69     QString DeviceSet(const QString &name, const QString &value,
    70                       bool report_error_return = true);
     53    QString         _device_id;
    7154
    72     QString TunerGet(const QString &name, bool report_error_return = true);
    73     QString TunerSet(const QString &name, const QString &value,
    74                      bool report_error_return = true);
    75 
    76   private:
    77     hdhr_socket_t  *_control_socket;
    78     uint            _device_id;
    79     uint            _device_ip;
    80     uint            _tuner;
    81     bool            _ignore_filters;
    82     vector<uint>    _pids;
    8355    mutable QMutex  _lock;
     56    mutable QMutex  tune_lock;
     57    mutable QMutex  hw_lock;
    8458};
    8559
    8660#endif
  • libs/libmythhdhomerun/hdhomerun.h

     
     1#ifndef __HDHOMERUN_INCLUDES__
     2#define __HDHOMERUN_INCLUDES__
    13/*
    24 * hdhomerun_device.h
    35 *
     
    2729#include "hdhomerun_channels.h"
    2830#include "hdhomerun_channelscan.h"
    2931#include "hdhomerun_device.h"
     32
     33#endif /* __HDHOMERUN_INCLUDES__ */
     34