00001
00002
00003
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
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
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
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;
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
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();
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 }