|
MythTV
0.26-pre
|
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 }
1.7.6.1