MythTV  0.26-pre
hdhrstreamhandler.cpp
Go to the documentation of this file.
00001 // -*- Mode: c++ -*-
00002 
00003 // POSIX headers
00004 #include <fcntl.h>
00005 #include <unistd.h>
00006 #ifndef USING_MINGW
00007 #include <sys/select.h>
00008 #include <sys/ioctl.h>
00009 #endif
00010 
00011 // MythTV headers
00012 #include "hdhrstreamhandler.h"
00013 #include "hdhrchannel.h"
00014 #include "dtvsignalmonitor.h"
00015 #include "streamlisteners.h"
00016 #include "mpegstreamdata.h"
00017 #include "cardutil.h"
00018 #include "mythlogging.h"
00019 
00020 #define LOC      QString("HDHRSH(%1): ").arg(_device)
00021 
00022 QMap<QString,HDHRStreamHandler*> HDHRStreamHandler::_handlers;
00023 QMap<QString,uint>               HDHRStreamHandler::_handlers_refcnt;
00024 QMutex                           HDHRStreamHandler::_handlers_lock;
00025 
00026 HDHRStreamHandler *HDHRStreamHandler::Get(const QString &devname)
00027 {
00028     QMutexLocker locker(&_handlers_lock);
00029 
00030     QString devkey = devname.toUpper();
00031 
00032     QMap<QString,HDHRStreamHandler*>::iterator it = _handlers.find(devkey);
00033 
00034     if (it == _handlers.end())
00035     {
00036         HDHRStreamHandler *newhandler = new HDHRStreamHandler(devkey);
00037         newhandler->Open();
00038         _handlers[devkey] = newhandler;
00039         _handlers_refcnt[devkey] = 1;
00040 
00041         LOG(VB_RECORD, LOG_INFO,
00042             QString("HDHRSH: Creating new stream handler %1 for %2")
00043                 .arg(devkey).arg(devname));
00044     }
00045     else
00046     {
00047         _handlers_refcnt[devkey]++;
00048         uint rcount = _handlers_refcnt[devkey];
00049         LOG(VB_RECORD, LOG_INFO,
00050             QString("HDHRSH: Using existing stream handler %1 for %2")
00051                 .arg(devkey)
00052                 .arg(devname) + QString(" (%1 in use)").arg(rcount));
00053     }
00054 
00055     return _handlers[devkey];
00056 }
00057 
00058 void HDHRStreamHandler::Return(HDHRStreamHandler * & ref)
00059 {
00060     QMutexLocker locker(&_handlers_lock);
00061 
00062     QString devname = ref->_device;
00063 
00064     QMap<QString,uint>::iterator rit = _handlers_refcnt.find(devname);
00065     if (rit == _handlers_refcnt.end())
00066         return;
00067 
00068     if (*rit > 1)
00069     {
00070         ref = NULL;
00071         (*rit)--;
00072         return;
00073     }
00074 
00075     QMap<QString,HDHRStreamHandler*>::iterator it = _handlers.find(devname);
00076     if ((it != _handlers.end()) && (*it == ref))
00077     {
00078         LOG(VB_RECORD, LOG_INFO, QString("HDHRSH: Closing handler for %1")
00079                            .arg(devname));
00080         ref->Close();
00081         delete *it;
00082         _handlers.erase(it);
00083     }
00084     else
00085     {
00086         LOG(VB_GENERAL, LOG_ERR,
00087             QString("HDHRSH Error: Couldn't find handler for %1")
00088                 .arg(devname));
00089     }
00090 
00091     _handlers_refcnt.erase(rit);
00092     ref = NULL;
00093 }
00094 
00095 HDHRStreamHandler::HDHRStreamHandler(const QString &device) :
00096     StreamHandler(device),
00097     _hdhomerun_device(NULL),
00098     _tuner(-1),
00099 
00100     _hdhr_lock(QMutex::Recursive)
00101 {
00102     setObjectName("HDHRStreamHandler");
00103 }
00104 
00108 void HDHRStreamHandler::run(void)
00109 {
00110     RunProlog();
00111     /* Create TS socket. */
00112     if (!hdhomerun_device_stream_start(_hdhomerun_device))
00113     {
00114         LOG(VB_GENERAL, LOG_ERR, LOC +
00115             "Starting recording (set target failed). Aborting.");
00116         _error = true;
00117         RunEpilog();
00118         return;
00119     }
00120     hdhomerun_device_stream_flush(_hdhomerun_device);
00121 
00122     SetRunning(true, false, false);
00123 
00124     /* Calculate buffer size */
00125     uint buffersize = 40000 * TSPacket::kSize;
00126     buffersize /= VIDEO_DATA_PACKET_SIZE;
00127     buffersize *= VIDEO_DATA_PACKET_SIZE;
00128 
00129     LOG(VB_RECORD, LOG_INFO, LOC + "RunTS(): begin");
00130 
00131     int remainder = 0;
00132     QTime last_update;
00133     while (_running_desired && !_error)
00134     {
00135         int elapsed = !last_update.isValid() ? -1 : last_update.elapsed();
00136         elapsed = (elapsed < 0) ? 1000 : elapsed;
00137         if (elapsed > 100)
00138         {
00139             UpdateFiltersFromStreamData();
00140             if (_tune_mode != hdhrTuneModeVChannel)
00141                 UpdateFilters();
00142             last_update.restart();
00143         }
00144 
00145         size_t read_size = 64 * 1024; // read about 64KB
00146         read_size /= VIDEO_DATA_PACKET_SIZE;
00147         read_size *= VIDEO_DATA_PACKET_SIZE;
00148 
00149         size_t data_length;
00150         unsigned char *data_buffer = hdhomerun_device_stream_recv(
00151             _hdhomerun_device, read_size, &data_length);
00152 
00153         if (!data_buffer)
00154         {
00155             usleep(20000);
00156             continue;
00157         }
00158 
00159         // Assume data_length is a multiple of 188 (packet size)
00160 
00161         _listener_lock.lock();
00162 
00163         if (_stream_data_list.empty())
00164         {
00165             _listener_lock.unlock();
00166             continue;
00167         }
00168 
00169         StreamDataList::const_iterator sit = _stream_data_list.begin();
00170         for (; sit != _stream_data_list.end(); ++sit)
00171             remainder = sit.key()->ProcessData(data_buffer, data_length);
00172 
00173         _listener_lock.unlock();
00174         if (remainder != 0)
00175         {
00176             LOG(VB_RECORD, LOG_INFO, LOC +
00177                 QString("RunTS(): data_length = %1 remainder = %2")
00178                     .arg(data_length).arg(remainder));
00179         }
00180     }
00181     LOG(VB_RECORD, LOG_INFO, LOC + "RunTS(): " + "shutdown");
00182 
00183     RemoveAllPIDFilters();
00184 
00185     hdhomerun_device_stream_stop(_hdhomerun_device);
00186     LOG(VB_RECORD, LOG_INFO, LOC + "RunTS(): " + "end");
00187 
00188     SetRunning(false, false, false);
00189     RunEpilog();
00190 }
00191 
00192 static QString filt_str(uint pid)
00193 {
00194     uint pid0 = (pid / (16*16*16)) % 16;
00195     uint pid1 = (pid / (16*16))    % 16;
00196     uint pid2 = (pid / (16))        % 16;
00197     uint pid3 = pid % 16;
00198     return QString("0x%1%2%3%4")
00199         .arg(pid0,0,16).arg(pid1,0,16)
00200         .arg(pid2,0,16).arg(pid3,0,16);
00201 }
00202 
00203 bool HDHRStreamHandler::UpdateFilters(void)
00204 {
00205     if (_tune_mode == hdhrTuneModeFrequency)
00206         _tune_mode = hdhrTuneModeFrequencyPid;
00207 
00208     if (_tune_mode != hdhrTuneModeFrequencyPid)
00209     {
00210         LOG(VB_GENERAL, LOG_ERR, LOC +
00211             "UpdateFilters called in wrong tune mode");
00212         return false;
00213     }
00214 
00215 #ifdef DEBUG_PID_FILTERS
00216     LOG(VB_RECORD, LOG_INFO, LOC + "UpdateFilters()");
00217 #endif // DEBUG_PID_FILTERS
00218     QMutexLocker locker(&_pid_lock);
00219 
00220     QString filter = "";
00221 
00222     vector<uint> range_min;
00223     vector<uint> range_max;
00224 
00225     PIDInfoMap::const_iterator it = _pid_info.begin();
00226     for (; it != _pid_info.end(); ++it)
00227     {
00228         range_min.push_back(it.key());
00229         PIDInfoMap::const_iterator eit = it;
00230         for (++eit;
00231              (eit != _pid_info.end()) && (it.key() + 1 == eit.key());
00232              ++it, ++eit);
00233         range_max.push_back(it.key());
00234     }
00235     if (range_min.size() > 16)
00236     {
00237         range_min.resize(16);
00238         uint pid_max = range_max.back();
00239         range_max.resize(15);
00240         range_max.push_back(pid_max);
00241     }
00242 
00243     for (uint i = 0; i < range_min.size(); i++)
00244     {
00245         filter += filt_str(range_min[i]);
00246         if (range_min[i] != range_max[i])
00247             filter += QString("-%1").arg(filt_str(range_max[i]));
00248         filter += " ";
00249     }
00250 
00251     filter = filter.trimmed();
00252 
00253     QString new_filter = TunerSet("filter", filter);
00254 
00255 #ifdef DEBUG_PID_FILTERS
00256     QString msg = QString("Filter: '%1'").arg(filter);
00257     if (filter != new_filter)
00258         msg += QString("\n\t\t\t\t'%2'").arg(new_filter);
00259 
00260     LOG(VB_RECORD, LOG_INFO, LOC + msg);
00261 #endif // DEBUG_PID_FILTERS
00262 
00263     return filter == new_filter;
00264 }
00265 
00266 bool HDHRStreamHandler::Open(void)
00267 {
00268     if (Connect())
00269     {
00270         const char *model = hdhomerun_device_get_model_str(_hdhomerun_device);
00271         _tuner_types.clear();
00272         if (QString(model).toLower().contains("cablecard"))
00273         {
00274             _tuner_types.push_back(DTVTunerType::kTunerTypeOCUR);
00275         }
00276         else if (QString(model).toLower().contains("dvb"))
00277         {
00278             _tuner_types.push_back(DTVTunerType::kTunerTypeDVBT);
00279             _tuner_types.push_back(DTVTunerType::kTunerTypeDVBC);
00280         }
00281         else
00282         {
00283             _tuner_types.push_back(DTVTunerType::kTunerTypeATSC);
00284         }
00285 
00286         return true;
00287     }
00288     return false;
00289 }
00290 
00291 void HDHRStreamHandler::Close(void)
00292 {
00293     if (_hdhomerun_device)
00294     {
00295         TuneChannel("none");
00296         hdhomerun_device_destroy(_hdhomerun_device);
00297         _hdhomerun_device = NULL;
00298     }
00299 }
00300 
00301 bool HDHRStreamHandler::Connect(void)
00302 {
00303     _hdhomerun_device = hdhomerun_device_create_from_str(
00304         _device.toLocal8Bit().constData(), NULL);
00305 
00306     if (!_hdhomerun_device)
00307     {
00308         LOG(VB_GENERAL, LOG_ERR, LOC + "Unable to create hdhomerun object");
00309         return false;
00310     }
00311 
00312     _tuner = hdhomerun_device_get_tuner(_hdhomerun_device);
00313 
00314     if (hdhomerun_device_get_local_machine_addr(_hdhomerun_device) == 0)
00315     {
00316         LOG(VB_GENERAL, LOG_ERR, LOC + "Unable to connect to device");
00317         return false;
00318     }
00319 
00320     LOG(VB_RECORD, LOG_INFO, LOC + "Successfully connected to device");
00321     return true;
00322 }
00323 
00324 bool HDHRStreamHandler::EnterPowerSavingMode(void)
00325 {
00326     QMutexLocker locker(&_listener_lock);
00327 
00328     if (!_stream_data_list.empty())
00329     {
00330         LOG(VB_RECORD, LOG_INFO, LOC +
00331             "Ignoring request - video streaming active");
00332         return false;
00333     }
00334     else
00335     {
00336         locker.unlock(); // _listener_lock
00337         return TuneChannel("none");
00338     }
00339 }
00340 
00341 QString HDHRStreamHandler::TunerGet(
00342     const QString &name, bool report_error_return, bool print_error) const
00343 {
00344     QMutexLocker locker(&_hdhr_lock);
00345 
00346     if (!_hdhomerun_device)
00347     {
00348         LOG(VB_GENERAL, LOG_ERR, LOC + "Get request failed (not connected)");
00349         return QString::null;
00350     }
00351 
00352     QString valname = QString("/tuner%1/%2").arg(_tuner).arg(name);
00353     char *value = NULL;
00354     char *error = NULL;
00355     if (hdhomerun_device_get_var(
00356             _hdhomerun_device, valname.toLocal8Bit().constData(),
00357             &value, &error) < 0)
00358     {
00359         LOG(VB_GENERAL, LOG_ERR, LOC + "Get request failed" + ENO);
00360         return QString::null;
00361     }
00362 
00363     if (report_error_return && error)
00364     {
00365         if (print_error)
00366         {
00367             LOG(VB_GENERAL, LOG_ERR, LOC + QString("DeviceGet(%1): %2")
00368                     .arg(name).arg(error));
00369         }
00370 
00371         return QString::null;
00372     }
00373 
00374     return QString(value);
00375 }
00376 
00377 QString HDHRStreamHandler::TunerSet(
00378     const QString &name, const QString &val,
00379     bool report_error_return, bool print_error)
00380 {
00381     QMutexLocker locker(&_hdhr_lock);
00382 
00383     if (!_hdhomerun_device)
00384     {
00385         LOG(VB_GENERAL, LOG_ERR, LOC + "Set request failed (not connected)");
00386         return QString::null;
00387     }
00388 
00389 
00390     QString valname = QString("/tuner%1/%2").arg(_tuner).arg(name);
00391     char *value = NULL;
00392     char *error = NULL;
00393 
00394     if (hdhomerun_device_set_var(
00395             _hdhomerun_device, valname.toLocal8Bit().constData(),
00396             val.toLocal8Bit().constData(), &value, &error) < 0)
00397     {
00398         LOG(VB_GENERAL, LOG_ERR, LOC + "Set request failed" + ENO);
00399 
00400         return QString::null;
00401     }
00402 
00403     if (report_error_return && error)
00404     {
00405         if (print_error)
00406         {
00407             LOG(VB_GENERAL, LOG_ERR, LOC + QString("DeviceSet(%1 %2): %3")
00408                     .arg(name).arg(val).arg(error));
00409         }
00410 
00411         return QString::null;
00412     }
00413 
00414     return QString(value);
00415 }
00416 
00417 void HDHRStreamHandler::GetTunerStatus(struct hdhomerun_tuner_status_t *status)
00418 {
00419     hdhomerun_device_get_tuner_status(_hdhomerun_device, NULL, status);
00420 }
00421 
00422 bool HDHRStreamHandler::IsConnected(void) const
00423 {
00424     return (_hdhomerun_device != NULL);
00425 }
00426 
00427 bool HDHRStreamHandler::TuneChannel(const QString &chn)
00428 {
00429     _tune_mode = hdhrTuneModeFrequency;
00430 
00431     QString current = TunerGet("channel");
00432     if (current == chn)
00433     {
00434         LOG(VB_RECORD, LOG_INFO, LOC + QString("Not Re-Tuning channel %1")
00435                 .arg(chn));
00436         return true;
00437     }
00438 
00439     LOG(VB_RECORD, LOG_INFO, LOC + QString("Tuning channel %1 (was %2)")
00440             .arg(chn).arg(current));
00441     return !TunerSet("channel", chn).isEmpty();
00442 }
00443 
00444 bool HDHRStreamHandler::TuneProgram(uint mpeg_prog_num)
00445 {
00446     if (_tune_mode == hdhrTuneModeFrequency)
00447         _tune_mode = hdhrTuneModeFrequencyProgram;
00448 
00449     if (_tune_mode != hdhrTuneModeFrequencyProgram)
00450     {
00451         LOG(VB_GENERAL, LOG_ERR, LOC + "TuneProgram called in wrong tune mode");
00452         return false;
00453     }
00454 
00455     LOG(VB_RECORD, LOG_INFO, LOC + QString("Tuning program %1")
00456             .arg(mpeg_prog_num));
00457     return !TunerSet(
00458         "program", QString::number(mpeg_prog_num), false).isEmpty();
00459 }
00460 
00461 bool HDHRStreamHandler::TuneVChannel(const QString &vchn)
00462 {
00463     _tune_mode = hdhrTuneModeVChannel;
00464 
00465     LOG(VB_RECORD, LOG_INFO, LOC + QString("Tuning vchannel %1").arg(vchn));
00466     return !TunerSet(
00467         "vchannel", vchn).isEmpty();
00468 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends