00001
00007
00008 #include <unistd.h>
00009 #include <sys/types.h>
00010 #include <sys/socket.h>
00011 #include <netinet/in.h>
00012 #include <arpa/inet.h>
00013 #include <netdb.h>
00014 #include <sys/time.h>
00015 #include <fcntl.h>
00016
00017
00018 #include <algorithm>
00019 using namespace std;
00020
00021
00022 #include "mythdbcon.h"
00023 #include "mythcontext.h"
00024 #include "hdhrchannel.h"
00025 #include "videosource.h"
00026 #include "channelutil.h"
00027
00028 #define DEBUG_PID_FILTERS
00029
00030 #define LOC QString("HDHRChan(%1): ").arg(GetDevice())
00031 #define LOC_ERR QString("HDHRChan(%1), Error: ").arg(GetDevice())
00032
00033 HDHRChannel::HDHRChannel(TVRec *parent, const QString &device, uint tuner)
00034 : DTVChannel(parent), _hdhomerun_device(NULL),
00035 _device_id(0), _device_ip(0),
00036 _tuner(tuner), _lock(true)
00037 {
00038 bool valid;
00039 _device_id = device.toUInt(&valid, 16);
00040
00041 if (valid && hdhomerun_discover_validate_device_id(_device_id))
00042 return;
00043
00044 _device_id = HDHOMERUN_DEVICE_ID_WILDCARD;
00045
00046
00047 struct in_addr address;
00048 if (inet_aton(device, &address))
00049 {
00050 _device_ip = ntohl(address.s_addr);
00051 return;
00052 }
00053
00054
00055 VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Invalid DeviceID '%1'")
00056 .arg(device));
00057
00058 }
00059
00060 HDHRChannel::~HDHRChannel(void)
00061 {
00062 Close();
00063 }
00064
00065 bool HDHRChannel::Open(void)
00066 {
00067 if (IsOpen())
00068 return true;
00069
00070 if (!InitializeInputs())
00071 return false;
00072
00073 return Connect();
00074 }
00075
00076 void HDHRChannel::Close(void)
00077 {
00078 if (_hdhomerun_device)
00079 {
00080 hdhomerun_device_destroy(_hdhomerun_device);
00081 _hdhomerun_device = NULL;
00082 }
00083 }
00084
00085 bool HDHRChannel::EnterPowerSavingMode(void)
00086 {
00087 return hdhomerun_device_set_tuner_channel(_hdhomerun_device, "none") > 0;
00088 }
00089
00090 bool HDHRChannel::Connect(void)
00091 {
00092 _hdhomerun_device = hdhomerun_device_create(
00093 _device_id, _device_ip, _tuner, NULL);
00094
00095 if (!_hdhomerun_device)
00096 {
00097 VERBOSE(VB_IMPORTANT,
00098 LOC_ERR + "Unable to create hdhomerun device object");
00099 return false;
00100 }
00101
00102 return true;
00103 }
00104
00105 QString HDHRChannel::DeviceGet(const QString &name, bool report_error_return)
00106 {
00107 QMutexLocker locker(&_lock);
00108
00109 if (!_hdhomerun_device)
00110 {
00111 VERBOSE(VB_IMPORTANT, LOC_ERR + "Get request failed (not connected)");
00112 return QString::null;
00113 }
00114
00115 char *value = NULL;
00116 char *error = NULL;
00117 if (hdhomerun_device_get_var(_hdhomerun_device, name, &value, &error) < 0)
00118 {
00119 VERBOSE(VB_IMPORTANT, LOC_ERR + "Get request failed" + ENO);
00120 return QString::null;
00121 }
00122
00123 if (report_error_return && error)
00124 {
00125 VERBOSE(VB_IMPORTANT, LOC_ERR +
00126 QString("DeviceGet(%1): %2").arg(name).arg(error));
00127
00128 return QString::null;
00129 }
00130
00131 return QString(value);
00132 }
00133
00134 QString HDHRChannel::DeviceSet(const QString &name, const QString &val,
00135 bool report_error_return)
00136 {
00137 QMutexLocker locker(&_lock);
00138
00139 if (!_hdhomerun_device)
00140 {
00141 VERBOSE(VB_IMPORTANT, LOC_ERR + "Set request failed (not connected)");
00142 return QString::null;
00143 }
00144
00145 char *value = NULL;
00146 char *error = NULL;
00147 if (hdhomerun_device_set_var(
00148 _hdhomerun_device, name, val, &value, &error) < 0)
00149 {
00150 VERBOSE(VB_IMPORTANT, LOC_ERR + "Set request failed" + ENO);
00151
00152 return QString::null;
00153 }
00154
00155 if (report_error_return && error)
00156 {
00157 VERBOSE(VB_IMPORTANT, LOC_ERR +
00158 QString("DeviceSet(%1 %2): %3").arg(name).arg(val).arg(error));
00159
00160 return QString::null;
00161 }
00162
00163 return QString(value);
00164 }
00165
00166 struct hdhomerun_device_t *HDHRChannel::GetHDHRDevice(void)
00167 {
00168 return _hdhomerun_device;
00169 }
00170
00171 QString HDHRChannel::TunerGet(const QString &name, bool report_error_return)
00172 {
00173 return DeviceGet(QString("/tuner%1/%2").arg(_tuner).arg(name),
00174 report_error_return);
00175 }
00176
00177 QString HDHRChannel::TunerSet(const QString &name, const QString &value,
00178 bool report_error_return)
00179 {
00180 return DeviceSet(QString("/tuner%1/%2").arg(_tuner).arg(name), value,
00181 report_error_return);
00182 }
00183
00184 bool HDHRChannel::DeviceSetTarget(unsigned short localPort)
00185 {
00186 if (localPort == 0)
00187 {
00188 return false;
00189 }
00190
00191 unsigned long localIP = hdhomerun_device_get_local_machine_addr(
00192 _hdhomerun_device);
00193 if (localIP == 0)
00194 {
00195 return false;
00196 }
00197
00198 QString configValue = QString("%1.%2.%3.%4:%5")
00199 .arg((localIP >> 24) & 0xFF).arg((localIP >> 16) & 0xFF)
00200 .arg((localIP >> 8) & 0xFF).arg((localIP >> 0) & 0xFF)
00201 .arg(localPort);
00202
00203 if (hdhomerun_device_set_tuner_target(_hdhomerun_device, configValue) <= 0)
00204 {
00205 return false;
00206 }
00207
00208 return true;
00209 }
00210
00211 bool HDHRChannel::DeviceClearTarget(void)
00212 {
00213 return hdhomerun_device_set_tuner_target(_hdhomerun_device, "none") > 0;
00214 }
00215
00216 bool HDHRChannel::SetChannelByString(const QString &channum)
00217 {
00218 QString loc = LOC + QString("SetChannelByString(%1)").arg(channum);
00219 QString loc_err = loc + ", Error: ";
00220 VERBOSE(VB_CHANNEL, loc);
00221
00222 if (!Open())
00223 {
00224 VERBOSE(VB_IMPORTANT, loc_err + "Channel object "
00225 "will not open, can not change channels.");
00226
00227 return false;
00228 }
00229
00230 QString inputName;
00231 if (!CheckChannel(channum, inputName))
00232 {
00233 VERBOSE(VB_IMPORTANT, loc_err +
00234 "CheckChannel failed.\n\t\t\tPlease verify the channel "
00235 "in the 'mythtv-setup' Channel Editor.");
00236
00237 return false;
00238 }
00239
00240
00241
00242
00243 if (!inputName.isEmpty())
00244 return SwitchToInput(inputName, channum);
00245
00246 ClearDTVInfo();
00247 _ignore_filters = false;
00248
00249 InputMap::const_iterator it = inputs.find(currentInputID);
00250 if (it == inputs.end())
00251 return false;
00252
00253 uint mplexid_restriction;
00254 if (!IsInputAvailable(currentInputID, mplexid_restriction))
00255 return false;
00256
00257
00258 QString tvformat, modulation, freqtable, freqid, si_std;
00259 int finetune;
00260 uint64_t frequency;
00261 int mpeg_prog_num;
00262 uint atsc_major, atsc_minor, mplexid, tsid, netid;
00263
00264 if (!ChannelUtil::GetChannelData(
00265 (*it)->sourceid, channum,
00266 tvformat, modulation, freqtable, freqid,
00267 finetune, frequency,
00268 si_std, mpeg_prog_num, atsc_major, atsc_minor, tsid, netid,
00269 mplexid, commfree))
00270 {
00271 return false;
00272 }
00273
00274 if (mplexid_restriction && (mplexid != mplexid_restriction))
00275 return false;
00276
00277
00278 bool ok = (frequency > 0);
00279 if (!ok)
00280 {
00281 frequency = (freqid.toInt(&ok) + finetune) * 1000;
00282 mplexid = 0;
00283 }
00284 bool isFrequency = ok && (frequency > 10000000);
00285
00286
00287 if ((*it)->externalChanger.isEmpty())
00288 {
00289 if (isFrequency)
00290 {
00291 if (!Tune(frequency, inputName, modulation, si_std))
00292 return false;
00293 }
00294 else
00295 {
00296 VERBOSE(VB_IMPORTANT, LOC_ERR +
00297 "dtv_multiplex data is required for tuning");
00298
00299 return false;
00300 }
00301 }
00302 else if (!ChangeExternalChannel(freqid))
00303 return false;
00304
00305
00306 curchannelname = QDeepCopy<QString>(channum);
00307
00308
00309 SetDTVInfo(atsc_major, atsc_minor, netid, tsid, mpeg_prog_num);
00310
00311
00312 inputs[currentInputID]->startChanNum = QDeepCopy<QString>(curchannelname);
00313
00314
00315
00316 if (mpeg_prog_num && (GetTuningMode() == "mpeg"))
00317 {
00318 QString pnum = QString::number(mpeg_prog_num);
00319 _ignore_filters = (hdhomerun_device_set_tuner_program(
00320 _hdhomerun_device, pnum) > 0);
00321 }
00322
00323 return true;
00324 }
00325
00326
00327 bool HDHRChannel::TuneMultiplex(uint mplexid, QString inputname)
00328 {
00329 VERBOSE(VB_CHANNEL, LOC + QString("TuneMultiplex(%1)").arg(mplexid));
00330
00331 QString modulation;
00332 QString si_std;
00333 uint64_t frequency;
00334 uint transportid;
00335 uint dvb_networkid;
00336
00337 if (!ChannelUtil::GetTuningParams(
00338 mplexid, modulation, frequency,
00339 transportid, dvb_networkid, si_std))
00340 {
00341 VERBOSE(VB_IMPORTANT, LOC_ERR + "TuneMultiplex(): " +
00342 QString("Could not find tuning parameters for multiplex %1.")
00343 .arg(mplexid));
00344
00345 return false;
00346 }
00347
00348 if (!Tune(frequency, inputname, modulation, si_std))
00349 return false;
00350
00351 return true;
00352 }
00353
00354 bool HDHRChannel::Tune(const DTVMultiplex &tuning, QString inputname)
00355 {
00356 return Tune(tuning.frequency, inputname,
00357 tuning.modulation.toString(), tuning.sistandard);
00358 }
00359
00360 bool HDHRChannel::Tune(uint frequency, QString ,
00361 QString modulation, QString si_std)
00362 {
00363 QString chan = modulation + ':' + QString::number(frequency);
00364
00365 VERBOSE(VB_CHANNEL, LOC + "Tune()ing to " + chan);
00366
00367 if (hdhomerun_device_set_tuner_channel(_hdhomerun_device, chan) > 0)
00368 {
00369 SetSIStandard(si_std);
00370 return true;
00371 }
00372
00373
00374
00375
00376 chan = "auto:" + QString::number(frequency);
00377
00378 VERBOSE(VB_CHANNEL, LOC + "Failed. Now trying " + chan);
00379
00380 if (hdhomerun_device_set_tuner_channel(_hdhomerun_device, chan) > 0)
00381 {
00382 SetSIStandard(si_std);
00383 return true;
00384 }
00385
00386
00387 return false;
00388 }
00389
00390 bool HDHRChannel::AddPID(uint pid, bool do_update)
00391 {
00392 QMutexLocker locker(&_lock);
00393
00394 vector<uint>::iterator it;
00395 it = lower_bound(_pids.begin(), _pids.end(), pid);
00396 if (it != _pids.end() && *it == pid)
00397 {
00398 #ifdef DEBUG_PID_FILTERS
00399 VERBOSE(VB_CHANNEL, "AddPID(0x"<<hex<<pid<<dec<<") NOOP");
00400 #endif // DEBUG_PID_FILTERS
00401 return true;
00402 }
00403
00404 _pids.insert(it, pid);
00405
00406 #ifdef DEBUG_PID_FILTERS
00407 VERBOSE(VB_CHANNEL, "AddPID(0x"<<hex<<pid<<dec<<")");
00408 #endif // DEBUG_PID_FILTERS
00409
00410 if (do_update)
00411 return UpdateFilters();
00412 return true;
00413 }
00414
00415 bool HDHRChannel::DelPID(uint pid, bool do_update)
00416 {
00417 QMutexLocker locker(&_lock);
00418
00419 vector<uint>::iterator it;
00420 it = lower_bound(_pids.begin(), _pids.end(), pid);
00421 if (it == _pids.end())
00422 {
00423 #ifdef DEBUG_PID_FILTERS
00424 VERBOSE(VB_CHANNEL, "DelPID(0x"<<hex<<pid<<dec<<") NOOP");
00425 #endif // DEBUG_PID_FILTERS
00426
00427 return true;
00428 }
00429
00430 if (*it == pid)
00431 {
00432 #ifdef DEBUG_PID_FILTERS
00433 VERBOSE(VB_CHANNEL, "DelPID(0x"<<hex<<pid<<dec<<") -- found");
00434 #endif // DEBUG_PID_FILTERS
00435 _pids.erase(it);
00436 }
00437 else
00438 {
00439 #ifdef DEBUG_PID_FILTERS
00440 VERBOSE(VB_CHANNEL, "DelPID(0x"<<hex<<pid<<dec<<") -- failed");
00441 #endif // DEBUG_PID_FILTERS
00442 }
00443
00444 if (do_update)
00445 return UpdateFilters();
00446 return true;
00447 }
00448
00449 bool HDHRChannel::DelAllPIDs(void)
00450 {
00451 QMutexLocker locker(&_lock);
00452
00453 #ifdef DEBUG_PID_FILTERS
00454 VERBOSE(VB_CHANNEL, "DelAllPID()");
00455 #endif // DEBUG_PID_FILTERS
00456
00457 _pids.clear();
00458
00459 return UpdateFilters();
00460 }
00461
00462 QString filt_str(uint pid)
00463 {
00464 uint pid0 = (pid / (16*16*16)) % 16;
00465 uint pid1 = (pid / (16*16)) % 16;
00466 uint pid2 = (pid / (16)) % 16;
00467 uint pid3 = pid % 16;
00468 return QString("0x%1%2%3%4")
00469 .arg(pid0,0,16).arg(pid1,0,16)
00470 .arg(pid2,0,16).arg(pid3,0,16);
00471 }
00472
00473 bool HDHRChannel::UpdateFilters(void)
00474 {
00475 QMutexLocker locker(&_lock);
00476
00477 QString filter = "";
00478
00479 vector<uint> range_min;
00480 vector<uint> range_max;
00481
00482 if (_ignore_filters)
00483 return true;
00484
00485 for (uint i = 0; i < _pids.size(); i++)
00486 {
00487 uint pid_min = _pids[i];
00488 uint pid_max = pid_min;
00489 for (uint j = i + 1; j < _pids.size(); j++)
00490 {
00491 if (pid_max + 1 != _pids[j])
00492 break;
00493 pid_max++;
00494 i++;
00495 }
00496 range_min.push_back(pid_min);
00497 range_max.push_back(pid_max);
00498 }
00499
00500 if (range_min.size() > 16)
00501 {
00502 range_min.resize(16);
00503 uint pid_max = range_max.back();
00504 range_max.resize(15);
00505 range_max.push_back(pid_max);
00506 }
00507
00508 for (uint i = 0; i < range_min.size(); i++)
00509 {
00510 filter += filt_str(range_min[i]);
00511 if (range_min[i] != range_max[i])
00512 filter += QString("-%1").arg(filt_str(range_max[i]));
00513 filter += " ";
00514 }
00515
00516 filter = filter.stripWhiteSpace();
00517
00518 QString new_filter = TunerSet("filter", filter);
00519
00520 #ifdef DEBUG_PID_FILTERS
00521 QString msg = QString("Filter: '%1'").arg(filter);
00522 if (filter != new_filter)
00523 msg += QString("\n\t\t\t\t'%2'").arg(new_filter);
00524
00525 VERBOSE(VB_CHANNEL, msg);
00526 #endif // DEBUG_PID_FILTERS
00527
00528 return filter == new_filter;
00529 }