Ticket #6138: hdhomerun-multirec-v3.patch
File hdhomerun-multirec-v3.patch, 97.1 KB (added by , 15 years ago) |
---|
-
libs/libmythtv/channelscan/channelscanner.cpp
332 332 #ifdef USING_HDHOMERUN 333 333 if ("HDHOMERUN" == card_type) 334 334 { 335 uint tuner = CardUtil::GetHDHRTuner(cardid); 336 channel = new HDHRChannel(NULL, device, tuner); 335 channel = new HDHRChannel(NULL, device); 337 336 } 338 337 #endif // USING_HDHOMERUN 339 338 -
libs/libmythtv/cardutil.h
116 116 117 117 static bool IsTunerSharingCapable(const QString &rawtype) 118 118 { 119 return (rawtype == "DVB") ;119 return (rawtype == "DVB") || (rawtype == "HDHOMERUN"); 120 120 } 121 121 122 122 static bool IsTunerShared(uint cardidA, uint cardidB); -
libs/libmythtv/videosource.h
442 442 static void fillSelections(SelectSetting* setting); 443 443 }; 444 444 445 class HDHomeRunDeviceID; 446 class HDHomeRunIP; 447 class HDHomeRunTuner; 445 448 class HDHomeRunConfigurationGroup : public VerticalConfigurationGroup 446 449 { 447 450 Q_OBJECT 448 451 452 friend class HDHomeRunExtra; 453 449 454 public: 450 455 HDHomeRunConfigurationGroup(CaptureCard &parent); 451 456 452 457 public slots: 453 458 void probeCard(const QString &device); 459 void HDHomeRunExtraPanel(void); 454 460 455 461 private: 462 HDHomeRunDeviceID *deviceid; 463 HDHomeRunIP *cardip; 464 HDHomeRunTuner *cardtuner; 465 456 466 CaptureCard &parent; 457 467 TransLabelSetting *desc; 458 468 }; … … 592 602 CaptureCard(bool use_card_group = true); 593 603 594 604 int getCardID(void) const { return id->intValue(); } 605 QString GetRawCardType(void) const; 595 606 596 607 void loadByID(int id); 597 608 … … 623 634 CaptureCardDBStorage(this, parent, "hostname") { } 624 635 }; 625 636 626 private:637 protected: 627 638 ID *id; 628 639 uint instance_count; 629 640 }; -
libs/libmythtv/libmythtv.pro
524 524 using_hdhomerun { 525 525 # MythTV HDHomeRun glue 526 526 HEADERS += hdhrsignalmonitor.h hdhrchannel.h 527 HEADERS += hdhrrecorder.h 527 HEADERS += hdhrrecorder.h hdhrstreamhandler.h 528 528 529 529 SOURCES += hdhrsignalmonitor.cpp hdhrchannel.cpp 530 SOURCES += hdhrrecorder.cpp 530 SOURCES += hdhrrecorder.cpp hdhrstreamhandler.cpp 531 531 532 532 DEFINES += USING_HDHOMERUN 533 533 } -
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 25 QMap<uint,bool> HDHRStreamHandler::_rec_supports_ts_monitoring; 26 QMutex HDHRStreamHandler::_rec_supports_ts_monitoring_lock; 27 28 QMap<QString,HDHRStreamHandler*> HDHRStreamHandler::_handlers; 29 QMap<QString,uint> HDHRStreamHandler::_handlers_refcnt; 30 QMutex HDHRStreamHandler::_handlers_lock; 31 32 //#define DEBUG_PID_FILTERS 33 34 HDHRStreamHandler *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 63 void 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 101 HDHRStreamHandler::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 115 HDHRStreamHandler::~HDHRStreamHandler() 116 { 117 if (!_stream_data_list.empty()) 118 { 119 VERBOSE(VB_IMPORTANT, LOC_ERR + "dtor & _stream_data_list not empty"); 120 } 121 } 122 123 void 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 146 void 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 179 void *run_hdhr_stream_handler_thunk(void *param) 180 { 181 HDHRStreamHandler *mon = (HDHRStreamHandler*) param; 182 mon->Run(); 183 return NULL; 184 } 185 186 void 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 213 void 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 224 void 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 */ 240 void 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 337 bool 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 359 bool 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 381 bool 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 394 static 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 405 void 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 435 bool 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 499 bool 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 560 void HDHRStreamHandler::SetRunning(bool is_running) 561 { 562 _running = is_running; 563 _running_state_changed.wakeAll(); 564 } 565 566 PIDPriority 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 578 bool HDHRStreamHandler::Open(void) 579 { 580 if (!FindDevice()) 581 return false; 582 583 return Connect(); 584 } 585 586 void 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 596 bool 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 616 bool 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 640 bool 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 653 QString 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 685 QString 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 718 QString 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 725 QString 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 732 bool 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 764 bool HDHRStreamHandler::DeviceClearTarget(void) 765 { 766 return !TunerSet("target", "0.0.0.0:0").isEmpty(); 767 } 768 769 QString HDHRStreamHandler::GetTunerStatus(void) const 770 { 771 return TunerGet("status"); 772 } 773 774 bool HDHRStreamHandler::IsConnected(void) const 775 { 776 return (_control_socket != NULL); 777 } 778 779 bool 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 794 bool 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
7 7 #include "qstringlist.h" 8 8 9 9 class HDHRChannel; 10 class HDHRStreamHandler; 10 11 11 12 typedef QMap<uint,int> FilterMap; 12 13 … … 19 20 20 21 void Stop(void); 21 22 22 bool UpdateFiltersFromStreamData(void);23 24 23 protected: 25 24 HDHRSignalMonitor(void); 26 25 HDHRSignalMonitor(const HDHRSignalMonitor&); 27 26 28 27 virtual void UpdateValues(void); 28 HDHRChannel *GetHDHRChannel(void); 29 29 30 static void *TableMonitorThread(void *param);31 void RunTableMonitor(void);32 33 bool SupportsTSMonitoring(void);34 35 30 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; 40 33 }; 41 34 42 35 #endif // HDHRSIGNALMONITOR_H -
libs/libmythtv/hdhrrecorder.h
7 7 #ifndef HDHOMERUNRECORDER_H_ 8 8 #define HDHOMERUNRECORDER_H_ 9 9 10 #include <qmutex.h> 10 // Qt includes 11 #include <QMutex> 12 11 13 #include "dtvrecorder.h" 12 14 #include "streamlisteners.h" 13 15 #include "eitscanner.h" 14 16 15 17 class HDHRChannel; 16 18 class ProgramMapTable; 19 class MPEGStreamData; 20 class HDHRStreamHandler; 17 21 18 22 typedef vector<uint> uint_vec_t; 19 23 … … 21 25 public DVBMainStreamListener, 22 26 public ATSCMainStreamListener, 23 27 public MPEGStreamListener, 24 public MPEGSingleProgramStreamListener 28 public MPEGSingleProgramStreamListener, 29 public TSPacketListener, 30 public TSPacketListenerAV 25 31 { 26 32 public: 27 33 HDHRRecorder(TVRec *rec, HDHRChannel *channel); … … 32 38 const QString &audiodev, 33 39 const QString &vbidev); 34 40 41 void StartRecording(void); 42 void ResetForNewFile(void); 43 void StopRecording(void); 44 35 45 bool Open(void); 36 bool StartData(void);46 bool IsOpen(void) const { return _stream_handler; } 37 47 void Close(void); 38 48 39 void StartRecording(void);40 41 void SetStreamData(MPEGStreamData*);42 MPEGStreamData *GetStreamData(void) { return _stream_data; }43 ATSCStreamData *GetATSCStreamData(void);44 45 49 // MPEG Stream Listener 46 50 void HandlePAT(const ProgramAssociationTable*); 47 51 void HandleCAT(const ConditionalAccessTable*) {} … … 52 56 void HandleSingleProgramPAT(ProgramAssociationTable *pat); 53 57 void HandleSingleProgramPMT(ProgramMapTable *pmt); 54 58 55 // ATSC 59 // ATSC Main 56 60 void HandleSTT(const SystemTimeTable*) {} 57 void Handle MGT(const MasterGuideTable *) {};58 void Handle VCT(uint, const VirtualChannelTable*) {}61 void HandleVCT(uint /*tsid*/, const VirtualChannelTable*) {} 62 void HandleMGT(const MasterGuideTable*) {} 59 63 60 // DVB 64 // DVBMainStreamListener 61 65 void HandleTDT(const TimeDateTable*) {} 62 66 void HandleNIT(const NetworkInformationTable*) {} 63 67 void HandleSDT(uint /*tsid*/, const ServiceDescriptionTable*) {} 64 68 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 65 85 private: 66 bool AdjustFilters(void); 67 bool AdjustEITPIDs(void); 86 void TeardownAll(void); 68 87 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 73 91 private: 74 HDHRChannel *_channel; 75 struct hdhomerun_video_sock_t *_video_socket; 76 MPEGStreamData *_stream_data; 92 HDHRChannel *_channel; 93 HDHRStreamHandler *_stream_handler; 77 94 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; 83 113 }; 84 114 85 115 #endif -
libs/libmythtv/dbcheck.cpp
14 14 #include "mythdb.h" 15 15 #include "mythverbose.h" 16 16 17 // HDHomeRun headers 18 #ifdef USING_HDHOMERUN 19 #include "hdhomerun.h" 20 #endif 17 21 22 18 23 #define MINIMUM_DBMS_VERSION 5,0,15 19 24 20 25 /// This is the DB schema version expected by the running MythTV instance. 21 const QString currentDatabaseVersion = "123 5";26 const QString currentDatabaseVersion = "1236"; 22 27 23 28 static bool UpdateDBVersionNumber(const QString &newnumber); 24 29 static bool performActualUpdate( … … 4543 4548 return false; 4544 4549 } 4545 4550 4551 if (dbver == "1235") 4552 { 4553 /* 4554 #ifdef USING_HDHOMERUN 4555 VERBOSE(VB_IMPORTANT, "In 1235 upg (HDhomerun tunerid change)"); 4546 4556 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 4547 4734 return true; 4548 4735 } 4549 4736 -
libs/libmythtv/hdhrsignalmonitor.cpp
18 18 19 19 #include "hdhrchannel.h" 20 20 #include "hdhrrecorder.h" 21 #include "hdhrstreamhandler.h" 21 22 22 23 #define LOC QString("HDHRSM(%1): ").arg(channel->GetDevice()) 23 24 #define LOC_ERR QString("HDHRSM(%1), Error: ").arg(channel->GetDevice()) … … 39 40 HDHRSignalMonitor::HDHRSignalMonitor( 40 41 int db_cardnum, HDHRChannel* _channel, uint64_t _flags) : 41 42 DTVSignalMonitor(db_cardnum, _channel, _flags), 42 dtvMonitorRunning(false)43 streamHandlerStarted(false), streamHandler(NULL) 43 44 { 44 45 VERBOSE(VB_CHANNEL, LOC + "ctor"); 45 46 46 _channel->DelAllPIDs();47 48 47 signalStrength.SetThreshold(45); 49 48 50 49 AddFlags(kSigMon_WaitForSig); 50 51 streamHandler = HDHRStreamHandler::Get(_channel->GetDevice()); 51 52 } 52 53 53 54 /** \fn HDHRSignalMonitor::~HDHRSignalMonitor() … … 57 58 { 58 59 VERBOSE(VB_CHANNEL, LOC + "dtor"); 59 60 Stop(); 61 HDHRStreamHandler::Return(streamHandler); 60 62 } 61 63 62 64 /** \fn HDHRSignalMonitor::Stop(void) … … 66 68 { 67 69 VERBOSE(VB_CHANNEL, LOC + "Stop() -- begin"); 68 70 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 74 75 VERBOSE(VB_CHANNEL, LOC + "Stop() -- end"); 75 76 } 76 77 77 void *HDHRSignalMonitor::TableMonitorThread(void *param)78 HDHRChannel *HDHRSignalMonitor::GetHDHRChannel(void) 78 79 { 79 HDHRSignalMonitor *mon = (HDHRSignalMonitor*) param; 80 mon->RunTableMonitor(); 81 return NULL; 80 return dynamic_cast<HDHRChannel*>(channel); 82 81 } 83 82 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) 190 84 * \brief Fills in frontend stats and emits status Qt signals. 191 85 * 192 * This function uses five ioctl's FE_READ_SNR, FE_READ_SIGNAL_STRENGTH193 * FE_READ_BER, FE_READ_UNCORRECTED_BLOCKS, and FE_READ_STATUS to obtain194 * statistics from the frontend.195 *196 86 * This is automatically called by MonitorLoop(), after Start() 197 87 * has been used to start the signal monitoring thread. 198 88 */ … … 201 91 if (!running || exit) 202 92 return; 203 93 204 if ( dtvMonitorRunning)94 if (streamHandlerStarted) 205 95 { 206 96 EmitStatus(); 207 97 if (IsAllGood()) 208 98 SendMessageAllGood(); 99 209 100 // TODO dtv signals... 210 101 211 102 update_done = true; 212 103 return; 213 104 } 214 105 215 QString msg = ((HDHRChannel*)channel)->TunerGet("status");106 QString msg = streamHandler->GetTunerStatus(); 216 107 //ss = signal strength, [0,100] 217 108 //snq = signal to noise quality [0,100] 218 109 //seq = signal error quality [0,100] … … 250 141 kDTVSigMon_WaitForMGT | kDTVSigMon_WaitForVCT | 251 142 kDTVSigMon_WaitForNIT | kDTVSigMon_WaitForSDT)) 252 143 { 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; 264 146 } 265 147 266 148 update_done = true; -
libs/libmythtv/hdhrrecorder.cpp
5 5 * Distributed as part of MythTV under GPL v2 and later. 6 6 */ 7 7 8 // Cincludes8 // POSIX includes 9 9 #include <pthread.h> 10 #include <fcntl.h> 10 11 #include <unistd.h> 11 12 #include <sys/types.h> 12 13 #include <sys/socket.h> … … 14 15 #include <arpa/inet.h> 15 16 #include <netdb.h> 16 17 #include <sys/time.h> 17 #include <fcntl.h>18 18 19 19 // C++ includes 20 20 #include <iostream> … … 23 23 24 24 // MythTV includes 25 25 #include "RingBuffer.h" 26 #include "hdhrchannel.h"27 #include "hdhrrecorder.h"28 26 #include "atsctables.h" 29 27 #include "atscstreamdata.h" 30 28 #include "dvbstreamdata.h" 31 29 #include "eithelper.h" 32 30 #include "tv_rec.h" 33 31 32 // MythTV HDHR includes 33 #include "hdhrchannel.h" 34 #include "hdhrrecorder.h" 35 #include "hdhrstreamhandler.h" 36 34 37 #define LOC QString("HDHRRec(%1): ").arg(tvrec->GetCaptureCardNum()) 38 #define LOC_WARN QString("HDHRRec(%1), Warning: ") \ 39 .arg(tvrec->GetCaptureCardNum()) 35 40 #define LOC_ERR QString("HDHRRec(%1), Error: ") \ 36 41 .arg(tvrec->GetCaptureCardNum()) 37 42 38 43 HDHRRecorder::HDHRRecorder(TVRec *rec, HDHRChannel *channel) 39 44 : DTVRecorder(rec), 40 _channel(channel), _ video_socket(NULL),45 _channel(channel), _stream_handler(NULL), 41 46 _stream_data(NULL), 47 _pid_lock(QMutex::Recursive), 42 48 _input_pat(NULL), _input_pmt(NULL), 43 _ reset_pid_filters(false),_pid_lock(QMutex::Recursive)49 _has_no_av(false) 44 50 { 45 51 } 46 52 … … 91 97 // HACK -- end 92 98 } 93 99 94 bool HDHRRecorder::Open(void)100 void HDHRRecorder::HandleSingleProgramPAT(ProgramAssociationTable *pat) 95 101 { 96 VERBOSE(VB_RECORD, LOC + "Open()"); 97 if (_video_socket) 102 if (!pat) 98 103 { 99 VERBOSE(VB_RECORD, LOC + " Card already open (recorder)");100 return true;104 VERBOSE(VB_RECORD, LOC + "HandleSingleProgramPAT(NULL)"); 105 return; 101 106 } 102 107 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; 108 110 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() }; 111 112 112 /* Create TS socket. */ 113 _video_socket = hdhomerun_video_create(0, buffersize); 114 if (!_video_socket) 113 if (pat) 115 114 { 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()))); 118 118 } 119 119 120 /* Success. */ 121 return true; 122 } 120 uint posB[2] = { ringBuffer->GetWritePosition(), _payload_buffer.size() }; 123 121 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 } 132 132 } 133 133 134 void HDHRRecorder:: Close(void)134 void HDHRRecorder::HandleSingleProgramPMT(ProgramMapTable *pmt) 135 135 { 136 VERBOSE(VB_RECORD, LOC + "Close()"); 137 if (_video_socket) 136 if (!pmt) 138 137 { 139 hdhomerun_video_destroy(_video_socket);140 _video_socket = NULL;138 VERBOSE(VB_RECORD, LOC + "HandleSingleProgramPMT(NULL)"); 139 return; 141 140 } 142 }143 141 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); 149 145 150 while (data + 188 <= end) 151 { 152 if (data[0] != 0x47) 153 { 154 return; 155 } 146 if (!ringBuffer) 147 return; 156 148 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); 159 153 160 data += 188; 161 } 162 } 154 uint posA[2] = { ringBuffer->GetWritePosition(), _payload_buffer.size() }; 163 155 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]))); 168 158 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() }; 173 160 174 if (data) 161 if (posB[0] + posB[1] * TSPacket::SIZE > 162 posA[0] + posA[1] * TSPacket::SIZE) 175 163 { 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)); 189 166 } 167 else 168 { 169 VERBOSE(VB_RECORD, LOC + "Saw PMT but did not write to disk yet"); 170 } 190 171 } 191 172 192 ATSCStreamData *HDHRRecorder::GetATSCStreamData(void)193 {194 return dynamic_cast<ATSCStreamData*>(_stream_data);195 }196 197 173 void HDHRRecorder::HandlePAT(const ProgramAssociationTable *_pat) 198 174 { 199 175 if (!_pat) … … 221 197 _input_pat = new ProgramAssociationTable(*_pat); 222 198 delete oldpat; 223 199 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 } 225 207 } 226 208 227 209 void HDHRRecorder::HandlePMT(uint progNum, const ProgramMapTable *_pmt) … … 233 215 VERBOSE(VB_RECORD, LOC + "SetPMT("<<progNum<<")"); 234 216 ProgramMapTable *oldpmt = _input_pmt; 235 217 _input_pmt = new ProgramMapTable(*_pmt); 236 delete oldpmt;237 218 238 _reset_pid_filters = true; 239 } 240 } 219 QString sistandard = _channel->GetSIStandard(); 241 220 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; 246 228 247 int next = (pat->tsheader()->ContinuityCounter()+1)&0xf; 248 pat->tsheader()->SetContinuityCounter(next); 249 BufferedWrite(*(reinterpret_cast<TSPacket*>(pat->tsheader()))); 229 delete oldpmt; 230 } 250 231 } 251 232 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 266 233 /** \fn HDHRRecorder::HandleMGT(const MasterGuideTable*) 267 234 * \brief Processes Master Guide Table, by enabling the 268 235 * scanning of all PIDs listed. … … 280 247 } 281 248 */ 282 249 283 bool HDHRRecorder:: ProcessTSPacket(const TSPacket& tspacket)250 bool HDHRRecorder::Open(void) 284 251 { 285 bool ok = !tspacket.TransportError(); 286 if (ok && !tspacket.ScramplingControl()) 252 if (IsOpen()) 287 253 { 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 } 293 257 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)); 299 261 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 269 void 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 279 void 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()); 321 305 } 322 return ok;323 306 } 324 307 308 ATSCStreamData *HDHRRecorder::GetATSCStreamData(void) 309 { 310 return dynamic_cast<ATSCStreamData*>(_stream_data); 311 } 312 325 313 void HDHRRecorder::StartRecording(void) 326 314 { 327 315 VERBOSE(VB_RECORD, LOC + "StartRecording -- begin"); … … 337 325 _request_recording = true; 338 326 _recording = true; 339 327 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; 349 334 350 hdhomerun_video_flush(_video_socket); 335 _stream_data->AddAVListener(this); 336 _stream_data->AddWritingListener(this); 337 _stream_handler->AddListener(_stream_data); 338 351 339 while (_request_recording && !_error) 352 340 { 341 usleep(50000); 342 353 343 if (PauseAndWait()) 354 344 continue; 355 345 356 if ( _stream_data)346 if (!_input_pmt) 357 347 { 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; 360 352 } 361 353 362 if ( _reset_pid_filters)354 if (!_stream_handler->IsRunning()) 363 355 { 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."); 367 360 } 368 369 size_t read_size = 64 * 1024; // read about 64KB370 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);383 361 } 384 362 385 363 VERBOSE(VB_RECORD, LOC + "StartRecording -- ending..."); 386 364 387 _channel->DeviceClearTarget(); 365 _stream_handler->RemoveListener(_stream_data); 366 _stream_data->RemoveWritingListener(this); 367 _stream_data->RemoveAVListener(this); 368 388 369 Close(); 389 370 390 371 FinishRecording(); 372 391 373 _recording = false; 392 374 393 375 VERBOSE(VB_RECORD, LOC + "StartRecording -- end"); 394 376 } 395 377 396 bool HDHRRecorder::AdjustFilters(void)378 void HDHRRecorder::ResetForNewFile(void) 397 379 { 398 QMutexLocker change_lock(&_pid_lock);380 DTVRecorder::ResetForNewFile(); 399 381 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 387 void HDHRRecorder::StopRecording(void) 388 { 389 _request_recording = false; 390 while (_recording) 391 usleep(2000); 392 } 393 394 bool HDHRRecorder::PauseAndWait(int timeout) 395 { 396 if (request_pause) 401 397 { 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); 404 413 } 405 414 406 if (! _input_pat || !_input_pmt)415 if (!request_pause && paused) 407 416 { 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); 410 423 } 411 424 412 uint_vec_t add_pid; 425 return paused; 426 } 413 427 414 add_pid.push_back(MPEG_PAT_PID); 415 _stream_data->AddListeningPID(MPEG_PAT_PID); 428 bool HDHRRecorder::ProcessVideoTSPacket(const TSPacket &tspacket) 429 { 430 uint streamType = _stream_id[tspacket.PID()]; 416 431 417 for (uint i = 0; i < _input_pat->ProgramCount(); i++) 432 // Check for keyframes and count frames 433 if (streamType == StreamID::H264Video) 418 434 { 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; 421 438 } 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 426 440 { 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); 430 442 } 431 443 432 if (need_pcr_pid && (_input_pmt->PCRPID())) 444 return ProcessAVTSPacket(tspacket); 445 } 446 447 bool 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 454 bool 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()) 433 464 { 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; 436 472 } 437 473 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 479 bool 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) 441 483 { 442 add_pid.push_back(_eit_pids[i]); 443 _stream_data->AddListeningPID(_eit_pids[i]); 484 _buffer_packets = !FindOtherKeyframes(&tspacket); 444 485 } 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; 445 492 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; 457 494 } 458 495 459 for (it = add_pid.begin(); it != add_pid.end(); ++it) 460 _channel->AddPID(*it, false); 496 BufferedWrite(tspacket); 461 497 462 _channel->UpdateFilters(); 463 464 return add_pid.size(); 498 return true; 465 499 } 466 500 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) 501 void HDHRRecorder::BufferedWrite(const TSPacket &tspacket) 471 502 { 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 474 505 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 } 476 514 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()) 484 518 { 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(); 489 522 } 490 523 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); 495 526 } 496 -
libs/libmythtv/hdhrchannel.cpp
25 25 #include "hdhrchannel.h" 26 26 #include "videosource.h" 27 27 #include "channelutil.h" 28 #include "hdhrstreamhandler.h" 28 29 30 // HDHomeRun header 31 #include "hdhomerun.h" 32 29 33 #define DEBUG_PID_FILTERS 30 34 31 35 #define LOC QString("HDHRChan(%1): ").arg(GetDevice()) 32 36 #define LOC_ERR QString("HDHRChan(%1), Error: ").arg(GetDevice()) 33 37 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) 38 HDHRChannel::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) 38 43 { 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;58 44 } 59 45 60 46 HDHRChannel::~HDHRChannel(void) … … 64 50 65 51 bool HDHRChannel::Open(void) 66 52 { 53 VERBOSE(VB_CHANNEL, LOC + "Opening HDHR channel"); 54 55 QMutexLocker locker(&hw_lock); 56 67 57 if (IsOpen()) 68 58 return true; 69 59 70 if (!FindDevice()) 71 return false; 60 _stream_handler = HDHRStreamHandler::Get(_device_id); 72 61 73 62 if (!InitializeInputs()) 74 return false;75 76 return (_device_ip != 0) && Connect();77 }78 79 void HDHRChannel::Close(void)80 {81 if (_control_socket)82 63 { 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(); 108 65 return false; 109 66 } 110 if (ret == 0)111 {112 VERBOSE(VB_IMPORTANT, LOC_ERR + QString("device not found"));113 return false;114 }115 67 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(); 125 69 } 126 70 127 bool HDHRChannel::Connect(void)71 void HDHRChannel::Close(void) 128 72 { 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"); 135 74 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.. 141 77 142 VERBOSE(VB_CHANNEL, LOC + "Successfully connected to device"); 143 return true; 78 HDHRStreamHandler::Return(_stream_handler); 144 79 } 145 80 146 QString HDHRChannel::DeviceGet(const QString &name, bool report_error_return)81 bool HDHRChannel::EnterPowerSavingMode(void) 147 82 { 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; 174 87 } 175 88 176 QString HDHRChannel::DeviceSet(const QString &name, const QString &val, 177 bool report_error_return) 89 bool HDHRChannel::IsOpen(void) const 178 90 { 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; 206 92 } 207 93 208 QString HDHRChannel::TunerGet(const QString &name, bool report_error_return) 94 bool HDHRChannel::Init( 95 QString &inputname, QString &startchannel, bool setchan) 209 96 { 210 return DeviceGet(QString("/tuner%1/%2").arg(_tuner).arg(name), 211 report_error_return); 212 } 97 if (setchan && !IsOpen()) 98 Open(); 213 99 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); 219 101 } 220 102 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 252 103 bool HDHRChannel::SetChannelByString(const QString &channum) 253 104 { 254 105 QString loc = LOC + QString("SetChannelByString(%1)").arg(channum); … … 280 131 return SwitchToInput(inputName, channum); 281 132 282 133 ClearDTVInfo(); 283 _ignore_filters = false;284 134 285 135 InputMap::const_iterator it = inputs.find(currentInputID); 286 136 if (it == inputs.end()) … … 349 199 QString tmpX = curchannelname; tmpX.detach(); 350 200 inputs[currentInputID]->startChanNum = tmpX; 351 201 352 // Turn on the HDHomeRun program filtering if it is supported353 // and we are tuning to an MPEG program number.354 202 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); 359 204 360 205 return true; 361 206 } … … 409 254 410 255 QString chan = modulation + ':' + QString::number(frequency); 411 256 412 VERBOSE(VB_CHANNEL, LOC + "Tun e()ing to " + chan);257 VERBOSE(VB_CHANNEL, LOC + "Tuning to " + chan); 413 258 414 if ( TunerSet("channel", chan).length())259 if (_stream_handler->TuneChannel(chan)) 415 260 { 416 261 SetSIStandard(si_std); 417 262 return true; … … 419 264 420 265 return false; 421 266 } 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_FILTERS432 VERBOSE(VB_CHANNEL, "AddPID(0x"<<hex<<pid<<dec<<") NOOP");433 #endif // DEBUG_PID_FILTERS434 return true;435 }436 437 _pids.insert(it, pid);438 439 #ifdef DEBUG_PID_FILTERS440 VERBOSE(VB_CHANNEL, "AddPID(0x"<<hex<<pid<<dec<<")");441 #endif // DEBUG_PID_FILTERS442 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_FILTERS457 VERBOSE(VB_CHANNEL, "DelPID(0x"<<hex<<pid<<dec<<") NOOP");458 #endif // DEBUG_PID_FILTERS459 460 return true;461 }462 463 if (*it == pid)464 {465 #ifdef DEBUG_PID_FILTERS466 VERBOSE(VB_CHANNEL, "DelPID(0x"<<hex<<pid<<dec<<") -- found");467 #endif // DEBUG_PID_FILTERS468 _pids.erase(it);469 }470 else471 {472 #ifdef DEBUG_PID_FILTERS473 VERBOSE(VB_CHANNEL, "DelPID(0x"<<hex<<pid<<dec<<") -- failed");474 #endif // DEBUG_PID_FILTERS475 }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_FILTERS487 VERBOSE(VB_CHANNEL, "DelAllPID()");488 #endif // DEBUG_PID_FILTERS489 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_FILTERS554 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_FILTERS560 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> 7 using namespace std; 8 9 #include <QMap> 10 #include <QMutex> 11 12 #include "util.h" 13 #include "DeviceReadBuffer.h" 14 #include "mpegstreamdata.h" 15 16 class QString; 17 class HDHRStreamHandler; 18 class DTVSignalMonitor; 19 class HDHRChannel; 20 class DeviceReadBuffer; 21 22 // HDHomeRun headers 23 #ifdef USING_HDHOMERUN 24 #include "hdhomerun.h" 25 #else 26 struct hdhomerun_control_sock_t { int dummy; }; 27 #endif 28 29 typedef QMap<uint,int> FilterMap; 30 31 //#define RETUNE_TIMEOUT 5000 32 33 class 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
214 214 devs.push_back(subit->filePath()); 215 215 } 216 216 } 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 217 253 else 218 254 { 219 255 VERBOSE(VB_IMPORTANT, QString("CardUtil::ProbeVideoDevices: ") + … … 223 259 return devs; 224 260 } 225 261 262 #include <cassert> 226 263 QString CardUtil::ProbeDVBType(const QString &device) 227 264 { 228 265 QString ret = "ERROR_UNKNOWN"; 229 (void) device;230 266 267 if (device.isEmpty()) 268 return ret; 269 231 270 #ifdef USING_DVB 232 271 QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_FRONTEND, device); 272 assert(dvbdev != device); 233 273 QByteArray dev = dvbdev.toAscii(); 234 274 int fd_frontend = open(dev.constData(), O_RDONLY | O_NONBLOCK); 235 275 if (fd_frontend < 0) 236 276 { 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)); 238 279 return ret; 239 280 } 240 281 … … 331 372 if (device.isEmpty()) 332 373 return "ERROR_OPEN"; 333 374 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; 335 380 } 336 381 337 382 /** \fn CardUtil::IsDVBCardType(const QString) … … 1150 1195 uint id = 0; 1151 1196 for (uint i = 0; !id && (i < 100); i++) 1152 1197 { 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); 1154 1202 name += (i) ? QString(":%1").arg(i) : QString(""); 1155 1203 id = CardUtil::CreateInputGroup(name); 1156 1204 } … … 1753 1801 } 1754 1802 else if (cardtype == "HDHOMERUN") 1755 1803 { 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); 1768 1805 } 1769 1806 else 1770 1807 { -
libs/libmythtv/videosource.cpp
48 48 #include "videodev_myth.h" 49 49 #endif 50 50 51 #ifdef USING_HDHOMERUN 52 #include "hdhomerun.h" 53 #endif 54 51 55 QMutex XMLTVFindGrabbers::list_lock; 52 56 53 57 VideoSourceSelector::VideoSourceSelector(uint _initial_sourceid, … … 1407 1411 CaptureCard &parent; 1408 1412 }; 1409 1413 1410 class HDHomeRun DeviceID : public LineEditSetting, public CaptureCardDBStorage1414 class HDHomeRunIP : public TransLabelSetting 1411 1415 { 1412 1416 public: 1417 HDHomeRunIP() 1418 { 1419 setLabel(QObject::tr("IP Address")); 1420 }; 1421 }; 1422 1423 class HDHomeRunTuner : public TransLabelSetting 1424 { 1425 public: 1426 HDHomeRunTuner() 1427 { 1428 setLabel(QObject::tr("Tuner")); 1429 }; 1430 }; 1431 1432 class HDHomeRunDeviceID : public ComboBoxSetting, public CaptureCardDBStorage 1433 { 1434 public: 1413 1435 HDHomeRunDeviceID(const CaptureCard &parent) : 1414 LineEditSetting(this),1436 ComboBoxSetting(this), 1415 1437 CaptureCardDBStorage(this, parent, "videodevice") 1416 1438 { 1417 setValue("FFFFFFFF");1418 1439 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 } 1423 1489 } 1490 1491 virtual void Load(void) 1492 { 1493 clearSelections(); 1494 addSelection(""); 1495 1496 CaptureCardDBStorage::Load(); 1497 1498 fillSelections(getValue()); 1499 } 1424 1500 }; 1425 1501 1426 1502 class IPTVHost : public LineEditSetting, public CaptureCardDBStorage … … 1453 1529 CaptureCard &parent; 1454 1530 }; 1455 1531 1456 class HDHomeRun TunerIndex : public ComboBoxSetting, public CaptureCardDBStorage1532 class HDHomeRunExtra : public ConfigurationWizard 1457 1533 { 1458 1534 public: 1459 HDHomeRunTunerIndex(const CaptureCard &parent) : 1460 ComboBoxSetting(this), 1461 CaptureCardDBStorage(this, parent, "dbox2_port") 1535 HDHomeRunExtra(HDHomeRunConfigurationGroup &parent); 1536 uint GetInstanceCount(void) const 1462 1537 { 1463 setLabel(QObject::tr("Tuner")); 1464 addSelection("0"); 1465 addSelection("1"); 1538 return (uint) count->intValue(); 1466 1539 } 1540 1541 private: 1542 InstanceCount *count; 1467 1543 }; 1468 1544 1545 HDHomeRunExtra::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 1469 1558 HDHomeRunConfigurationGroup::HDHomeRunConfigurationGroup 1470 1559 (CaptureCard& a_parent) : 1471 1560 VerticalConfigurationGroup(false, true, false, false), 1472 1561 parent(a_parent) 1473 1562 { 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(); 1475 1568 1476 desc = new TransLabelSetting(); 1569 addChild(cardip); 1570 addChild(cardtuner); 1477 1571 1478 setUseLabel(false);1479 addChild(device);1480 addChild(desc);1481 addChild(new HDHomeRunTunerIndex(parent));1482 1572 addChild(new SignalTimeout(parent, 1000, 250)); 1483 1573 addChild(new ChannelTimeout(parent, 3000, 1750)); 1484 1574 addChild(new SingleCardInput(parent)); 1485 1575 1576 TransButtonSetting *buttonRecOpt = new TransButtonSetting(); 1577 buttonRecOpt->setLabel(tr("Recording Options")); 1578 addChild(buttonRecOpt); 1486 1579 1487 1580 // 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); 1490 1587 }; 1491 1588 1492 void HDHomeRunConfigurationGroup::probeCard(const QString &device )1589 void HDHomeRunConfigurationGroup::probeCard(const QString &deviceid) 1493 1590 { 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 } 1496 1608 else 1497 desc->setValue(tr("Badly formatted Device ID")); 1609 { 1610 cardip->setValue("Unknown"); 1611 cardtuner->setValue("Unknown"); 1612 } 1613 #endif // USING_HDHOMERUN 1498 1614 } 1499 1615 1616 void 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 1500 1625 V4LConfigurationGroup::V4LConfigurationGroup(CaptureCard& a_parent) : 1501 1626 VerticalConfigurationGroup(false, true, false, false), 1502 1627 parent(a_parent), … … 1682 1807 addChild(new Hostname(*this)); 1683 1808 } 1684 1809 1810 QString CaptureCard::GetRawCardType(void) const 1811 { 1812 int cardid = getCardID(); 1813 if (cardid <= 0) 1814 return QString::null; 1815 return CardUtil::GetRawCardType(cardid); 1816 } 1817 1685 1818 void CaptureCard::fillSelections(SelectSetting *setting) 1686 1819 { 1687 1820 MSqlQuery query(MSqlQuery::InitCon()); … … 1710 1843 if ((cardtype.toLower() == "dvb") && (1 != ++device_refs[videodevice])) 1711 1844 continue; 1712 1845 1846 if ((cardtype.toLower() == "hdhomerun") && (1 != ++device_refs[videodevice])) 1847 continue; 1848 1713 1849 QString label = CardUtil::GetDeviceLabel( 1714 1850 cardid, cardtype, videodevice); 1715 1851 … … 2853 2989 if ((cardtype.toLower() == "dvb") && (1 != ++device_refs[videodevice])) 2854 2990 continue; 2855 2991 2992 if ((cardtype.toLower() == "hdhomerun") && (1 != ++device_refs[videodevice])) 2993 continue; 2994 2856 2995 QStringList inputLabels; 2857 2996 vector<CardInput*> cardInputs; 2858 2997 … … 2913 3052 2914 3053 void DVBConfigurationGroup::probeCard(const QString &videodevice) 2915 3054 { 2916 (void) videodevice; 3055 if (videodevice.isEmpty()) 3056 { 3057 cardname->setValue(""); 3058 cardtype->setValue(""); 3059 return; 3060 } 2917 3061 3062 if (parent.getCardID() && parent.GetRawCardType() != "DVB") 3063 { 3064 cardname->setValue(""); 3065 cardtype->setValue(""); 3066 return; 3067 } 3068 2918 3069 #ifdef USING_DVB 2919 3070 QString frontend_name = CardUtil::ProbeDVBFrontendName(videodevice); 2920 QString subtype 3071 QString subtype = CardUtil::ProbeDVBType(videodevice); 2921 3072 2922 3073 QString err_open = tr("Could not open card %1").arg(videodevice); 2923 3074 QString err_other = tr("Could not get card info for card %1").arg(videodevice); -
libs/libmythtv/tv_rec.cpp
181 181 else if (genOpt.cardtype == "HDHOMERUN") 182 182 { 183 183 #ifdef USING_HDHOMERUN 184 channel = new HDHRChannel(this, genOpt.videodev , dboxOpt.port);184 channel = new HDHRChannel(this, genOpt.videodev); 185 185 if (!channel->Open()) 186 186 return false; 187 187 InitChannel(genOpt.defaultinput, startchannel); … … 3501 3501 return; 3502 3502 3503 3503 ClearFlags(kFlagWaitingForRecPause); 3504 #ifdef USING_HDHOMERUN3505 if (GetHDHRRecorder())3506 {3507 // We currently need to close the file descriptor for3508 // HDHomeRun signal monitoring to work.3509 GetHDHRRecorder()->Close();3510 GetHDHRRecorder()->SetRingBuffer(NULL);3511 }3512 #endif // USING_HDHOMERUN3513 3504 VERBOSE(VB_RECORD, LOC + "Recorder paused, calling TuningFrequency"); 3514 3505 TuningFrequency(lastTuningRequest); 3515 3506 } … … 4148 4139 } 4149 4140 recorder->Reset(); 4150 4141 4151 #ifdef USING_HDHOMERUN4152 if (GetHDHRRecorder())4153 {4154 pauseNotify = false;4155 GetHDHRRecorder()->Close();4156 pauseNotify = true;4157 GetHDHRRecorder()->Open();4158 GetHDHRRecorder()->StartData();4159 }4160 #endif // USING_HDHOMERUN4161 4162 4142 // Set file descriptor of channel from recorder for V4L 4163 4143 channel->SetFd(recorder->GetVideoFd()); 4164 4144 -
libs/libmythtv/hdhrchannel.h
8 8 #define HDHOMERUNCHANNEL_H 9 9 10 10 // Qt headers 11 #include < qstring.h>11 #include <QString> 12 12 13 13 // MythTV headers 14 14 #include "dtvchannel.h" 15 15 16 // HDHomeRun headers 17 #ifdef USING_HDHOMERUN 18 #include "hdhomerun.h" 19 #else 20 struct hdhomerun_control_sock_t { int dummy; }; 21 #endif 16 class HDHRChannel; 17 class HDHRStreamHandler; 18 class ProgramMapTable; 22 19 23 typedef struct hdhomerun_control_sock_t hdhr_socket_t;24 25 20 class HDHRChannel : public DTVChannel 26 21 { 27 22 friend class HDHRSignalMonitor; 28 23 friend class HDHRRecorder; 29 24 30 25 public: 31 HDHRChannel(TVRec *parent, const QString &device , uint tuner);26 HDHRChannel(TVRec *parent, const QString &device); 32 27 ~HDHRChannel(void); 33 28 34 29 bool Open(void); 35 30 void Close(void); 36 31 bool EnterPowerSavingMode(void); 37 32 33 bool Init(QString &inputname, QString &startchannel, bool setchan); 34 38 35 // Sets 39 36 bool SetChannelByString(const QString &chan); 40 37 41 38 // 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; } 48 41 49 // Commands50 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 55 42 // ATSC/DVB scanning/tuning stuff 56 43 bool TuneMultiplex(uint mplexid, QString inputname); 57 44 bool Tune(const DTVMultiplex &tuning, QString inputname); 58 45 59 46 private: 60 bool FindDevice(void);61 bool Connect(void);62 47 bool Tune(uint frequency, QString inputname, 63 48 QString modulation, QString si_std); 64 49 65 bool DeviceSetTarget(unsigned short localPort);66 bool DeviceClearTarget(void);50 private: 51 HDHRStreamHandler *_stream_handler; 67 52 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; 71 54 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;83 55 mutable QMutex _lock; 56 mutable QMutex tune_lock; 57 mutable QMutex hw_lock; 84 58 }; 85 59 86 60 #endif -
libs/libmythhdhomerun/hdhomerun.h
1 #ifndef __HDHOMERUN_INCLUDES__ 2 #define __HDHOMERUN_INCLUDES__ 1 3 /* 2 4 * hdhomerun_device.h 3 5 * … … 27 29 #include "hdhomerun_channels.h" 28 30 #include "hdhomerun_channelscan.h" 29 31 #include "hdhomerun_device.h" 32 33 #endif /* __HDHOMERUN_INCLUDES__ */ 34