|
MythTV
0.26-pre
|
00001 // Std C headers 00002 #include <cstdlib> 00003 #include <cerrno> 00004 00005 // POSIX headers 00006 #include <unistd.h> 00007 #include <fcntl.h> 00008 #include <sys/stat.h> 00009 #include <sys/types.h> 00010 00011 // C++ headers 00012 #include <iostream> 00013 #include <algorithm> 00014 using namespace std; 00015 00016 // Qt headers 00017 #include <QCoreApplication> 00018 00019 // MythTV headers 00020 #ifdef USING_OSX_FIREWIRE 00021 #include "darwinfirewiredevice.h" 00022 #endif 00023 #ifdef USING_LINUX_FIREWIRE 00024 #include "linuxfirewiredevice.h" 00025 #endif 00026 #include "firewirechannel.h" 00027 #include "mythcorecontext.h" 00028 #include "cetonchannel.h" 00029 #include "dummychannel.h" 00030 #include "tvremoteutil.h" 00031 #include "channelutil.h" 00032 #include "channelbase.h" 00033 #include "channelutil.h" 00034 #include "frequencies.h" 00035 #include "hdhrchannel.h" 00036 #include "iptvchannel.h" 00037 #include "mythlogging.h" 00038 #include "asichannel.h" 00039 #include "dtvchannel.h" 00040 #include "dvbchannel.h" 00041 #include "v4lchannel.h" 00042 #include "sourceutil.h" 00043 #include "exitcodes.h" 00044 #include "cardutil.h" 00045 #include "compat.h" 00046 00047 #define LOC QString("ChannelBase(%1): ").arg(GetCardID()) 00048 00049 ChannelBase::ChannelBase(TVRec *parent) : 00050 m_pParent(parent), m_curchannelname(""), 00051 m_currentInputID(-1), m_commfree(false), m_cardid(0), 00052 m_system(NULL), m_system_status(0) 00053 { 00054 } 00055 00056 ChannelBase::~ChannelBase(void) 00057 { 00058 ClearInputMap(); 00059 00060 QMutexLocker locker(&m_system_lock); 00061 if (m_system) 00062 KillScript(); 00063 } 00064 00065 bool ChannelBase::Init(QString &inputname, QString &startchannel, bool setchan) 00066 { 00067 bool ok; 00068 00069 if (!setchan) 00070 ok = inputname.isEmpty() ? false : IsTunable(inputname, startchannel); 00071 else if (inputname.isEmpty()) 00072 ok = SetChannelByString(startchannel); 00073 else 00074 ok = SwitchToInput(inputname, startchannel); 00075 00076 if (ok) 00077 return true; 00078 00079 // try to find a valid channel if given start channel fails. 00080 QString msg1 = QString("Setting start channel '%1' failed, ") 00081 .arg(startchannel); 00082 QString msg2 = "and we failed to find any suitible channels on any input."; 00083 bool msg_error = true; 00084 00085 QStringList inputs = GetConnectedInputs(); 00086 // Note we use qFind rather than std::find() for ulibc compat (#4507) 00087 QStringList::const_iterator start = 00088 qFind(inputs.begin(), inputs.end(), inputname); 00089 start = (start == inputs.end()) ? inputs.begin() : start; 00090 00091 if (start != inputs.end()) 00092 { 00093 LOG(VB_CHANNEL, LOG_INFO, LOC + 00094 QString("Looking for startchannel '%1' on input '%2'") 00095 .arg(startchannel).arg(*start)); 00096 } 00097 00098 // Attempt to find an input for the requested startchannel 00099 QStringList::const_iterator it = start; 00100 while (it != inputs.end()) 00101 { 00102 DBChanList channels = GetChannels(*it); 00103 00104 DBChanList::const_iterator cit = channels.begin(); 00105 for (; cit != channels.end(); ++cit) 00106 { 00107 if ((*cit).channum == startchannel && 00108 IsTunable(*it, startchannel)) 00109 { 00110 inputname = *it; 00111 LOG(VB_CHANNEL, LOG_INFO, LOC + 00112 QString("Found startchannel '%1' on input '%2'") 00113 .arg(startchannel).arg(inputname)); 00114 return true; 00115 } 00116 } 00117 00118 ++it; 00119 it = (it == inputs.end()) ? inputs.begin() : it; 00120 if (it == start) 00121 break; 00122 } 00123 00124 it = start; 00125 while (it != inputs.end() && !ok) 00126 { 00127 uint mplexid_restriction = 0; 00128 00129 DBChanList channels = GetChannels(*it); 00130 if (channels.size() && 00131 IsInputAvailable(GetInputByName(*it), mplexid_restriction)) 00132 { 00133 uint chanid = ChannelUtil::GetNextChannel( 00134 channels, channels[0].chanid, 00135 mplexid_restriction, CHANNEL_DIRECTION_UP); 00136 00137 DBChanList::const_iterator cit = 00138 find(channels.begin(), channels.end(), chanid); 00139 00140 if (chanid && cit != channels.end()) 00141 { 00142 if (!setchan) 00143 { 00144 ok = IsTunable(*it, (mplexid_restriction) ? 00145 (*cit).channum : startchannel); 00146 } 00147 else 00148 ok = SwitchToInput(*it, (*cit).channum); 00149 00150 if (ok) 00151 { 00152 inputname = *it; 00153 if (mplexid_restriction) 00154 { 00155 startchannel = (*cit).channum; 00156 startchannel.detach(); 00157 } 00158 msg2 = QString("selected to '%1' on input '%2' instead.") 00159 .arg(startchannel).arg(inputname); 00160 msg_error = false; 00161 } 00162 } 00163 } 00164 00165 ++it; 00166 it = (it == inputs.end()) ? inputs.begin() : it; 00167 if (it == start) 00168 break; 00169 } 00170 00171 LOG(VB_GENERAL, ((msg_error) ? LOG_ERR : LOG_WARNING), LOC + 00172 msg1 + "\n\t\t\t" + msg2); 00173 00174 return ok; 00175 } 00176 00177 bool ChannelBase::IsTunable(const QString &input, const QString &channum) const 00178 { 00179 QString loc = LOC + QString("IsTunable(%1,%2)").arg(input).arg(channum); 00180 00181 int inputid = m_currentInputID; 00182 if (!input.isEmpty()) 00183 inputid = GetInputByName(input); 00184 00185 InputMap::const_iterator it = m_inputs.find(inputid); 00186 if (it == m_inputs.end()) 00187 { 00188 LOG(VB_GENERAL, LOG_ERR, loc + " " + 00189 QString("Requested non-existant input '%1':'%2' ") 00190 .arg(input).arg(inputid)); 00191 00192 return false; 00193 } 00194 00195 uint mplexid_restriction; 00196 if (!IsInputAvailable(inputid, mplexid_restriction)) 00197 { 00198 LOG(VB_GENERAL, LOG_ERR, loc + " " + 00199 QString("Requested channel is on input '%1' " 00200 "which is in a busy input group") 00201 .arg(inputid)); 00202 00203 return false; 00204 } 00205 00206 // Fetch tuning data from the database. 00207 QString tvformat, modulation, freqtable, freqid, dtv_si_std; 00208 int finetune; 00209 uint64_t frequency; 00210 int mpeg_prog_num; 00211 uint atsc_major, atsc_minor, mplexid, tsid, netid; 00212 bool commfree; 00213 00214 if (!ChannelUtil::GetChannelData((*it)->sourceid, channum, 00215 tvformat, modulation, freqtable, freqid, 00216 finetune, frequency, dtv_si_std, 00217 mpeg_prog_num, atsc_major, atsc_minor, 00218 tsid, netid, mplexid, commfree)) 00219 { 00220 LOG(VB_GENERAL, LOG_ERR, loc + " " + 00221 QString("Failed to find channel in DB on input '%1' ") 00222 .arg(inputid)); 00223 00224 return false; 00225 } 00226 00227 if (mplexid_restriction && (mplexid != mplexid_restriction)) 00228 { 00229 LOG(VB_GENERAL, LOG_ERR, loc + " " + 00230 QString("Channel is valid, but tuner is busy " 00231 "on different multiplex (%1 != %2)") 00232 .arg(mplexid).arg(mplexid_restriction)); 00233 00234 return false; 00235 } 00236 00237 return true; 00238 } 00239 00240 uint ChannelBase::GetNextChannel(uint chanid, int direction) const 00241 { 00242 if (!chanid) 00243 { 00244 InputMap::const_iterator it = m_inputs.find(m_currentInputID); 00245 if (it == m_inputs.end()) 00246 return 0; 00247 00248 chanid = ChannelUtil::GetChanID((*it)->sourceid, m_curchannelname); 00249 } 00250 00251 uint mplexid_restriction = 0; 00252 IsInputAvailable(m_currentInputID, mplexid_restriction); 00253 00254 return ChannelUtil::GetNextChannel( 00255 m_allchannels, chanid, mplexid_restriction, direction); 00256 } 00257 00258 uint ChannelBase::GetNextChannel(const QString &channum, int direction) const 00259 { 00260 InputMap::const_iterator it = m_inputs.find(m_currentInputID); 00261 if (it == m_inputs.end()) 00262 return 0; 00263 00264 uint chanid = ChannelUtil::GetChanID((*it)->sourceid, channum); 00265 return GetNextChannel(chanid, direction); 00266 } 00267 00268 int ChannelBase::GetNextInputNum(void) const 00269 { 00270 // Exit early if inputs don't exist.. 00271 if (!m_inputs.size()) 00272 return -1; 00273 00274 // Find current input 00275 InputMap::const_iterator it; 00276 it = m_inputs.find(m_currentInputID); 00277 00278 // If we can't find the current input, start at 00279 // the beginning and don't increment initially. 00280 bool skip_incr = false; 00281 if (it == m_inputs.end()) 00282 { 00283 it = m_inputs.begin(); 00284 skip_incr = true; 00285 } 00286 00287 // Find the next _connected_ input. 00288 int i = 0; 00289 for (; i < 100; i++) 00290 { 00291 if (!skip_incr) 00292 { 00293 ++it; 00294 it = (it == m_inputs.end()) ? m_inputs.begin() : it; 00295 } 00296 skip_incr = false; 00297 00298 if ((*it)->sourceid) 00299 break; 00300 } 00301 00302 // if we found anything, including current cap channel return it 00303 return (i<100) ? (int)it.key() : -1; 00304 } 00305 00309 QStringList ChannelBase::GetConnectedInputs(void) const 00310 { 00311 QStringList list; 00312 00313 InputMap::const_iterator it = m_inputs.begin(); 00314 for (; it != m_inputs.end(); ++it) 00315 if ((*it)->sourceid) 00316 list.push_back((*it)->name); 00317 00318 return list; 00319 } 00320 00324 QString ChannelBase::GetInputByNum(int capchannel) const 00325 { 00326 InputMap::const_iterator it = m_inputs.find(capchannel); 00327 if (it != m_inputs.end()) 00328 return (*it)->name; 00329 return QString::null; 00330 } 00331 00335 int ChannelBase::GetInputByName(const QString &input) const 00336 { 00337 InputMap::const_iterator it = m_inputs.begin(); 00338 for (; it != m_inputs.end(); ++it) 00339 { 00340 if ((*it)->name == input) 00341 return (int)it.key(); 00342 } 00343 return -1; 00344 } 00345 00346 bool ChannelBase::SwitchToInput(const QString &inputname) 00347 { 00348 int input = GetInputByName(inputname); 00349 00350 if (input >= 0) 00351 return SwitchToInput(input, true); 00352 else 00353 { 00354 LOG(VB_GENERAL, LOG_ERR, LOC + 00355 QString("Could not find input: %1 on card").arg(inputname)); 00356 return false; 00357 } 00358 } 00359 00360 bool ChannelBase::SwitchToInput(const QString &inputname, const QString &chan) 00361 { 00362 LOG(VB_CHANNEL, LOG_DEBUG, LOC + QString("SwitchToInput(%1,%2)") 00363 .arg(inputname).arg(chan)); 00364 00365 int input = GetInputByName(inputname); 00366 00367 bool ok = false; 00368 if (input >= 0) 00369 { 00370 ok = SwitchToInput(input, false); 00371 if (ok) 00372 ok = SetChannelByString(chan); 00373 } 00374 else 00375 { 00376 LOG(VB_GENERAL, LOG_ERR, LOC + 00377 QString("Could not find input: %1 on card when setting channel %2") 00378 .arg(inputname).arg(chan)); 00379 } 00380 return ok; 00381 } 00382 00383 bool ChannelBase::SwitchToInput(int newInputNum, bool setstarting) 00384 { 00385 InputMap::const_iterator it = m_inputs.find(newInputNum); 00386 if (it == m_inputs.end() || (*it)->startChanNum.isEmpty()) 00387 return false; 00388 00389 uint mplexid_restriction; 00390 if (!IsInputAvailable(newInputNum, mplexid_restriction)) 00391 return false; 00392 00393 // input switching code would go here 00394 00395 if (setstarting) 00396 return SetChannelByString((*it)->startChanNum); 00397 00398 return true; 00399 } 00400 00401 static bool is_input_group_busy( 00402 uint inputid, 00403 uint groupid, 00404 const vector<uint> &excluded_cardids, 00405 QMap<uint,bool> &busygrp, 00406 QMap<uint,bool> &busyrec, 00407 QMap<uint,TunedInputInfo> &busyin, 00408 uint &mplexid_restriction) 00409 { 00410 static QMutex igrpLock; 00411 static InputGroupMap igrp; 00412 00413 // If none are busy, we don't need to check further 00414 QMap<uint,bool>::const_iterator bit = busygrp.find(groupid); 00415 if ((bit != busygrp.end()) && !*bit) 00416 return false; 00417 00418 vector<TunedInputInfo> conflicts; 00419 vector<uint> cardids = CardUtil::GetGroupCardIDs(groupid); 00420 for (uint i = 0; i < cardids.size(); i++) 00421 { 00422 if (find(excluded_cardids.begin(), 00423 excluded_cardids.end(), cardids[i]) != excluded_cardids.end()) 00424 { 00425 continue; 00426 } 00427 00428 TunedInputInfo info; 00429 QMap<uint,bool>::const_iterator it = busyrec.find(cardids[i]); 00430 if (it == busyrec.end()) 00431 { 00432 busyrec[cardids[i]] = RemoteIsBusy(cardids[i], info); 00433 it = busyrec.find(cardids[i]); 00434 if (*it) 00435 busyin[cardids[i]] = info; 00436 } 00437 00438 if (*it) 00439 { 00440 QMutexLocker locker(&igrpLock); 00441 if (igrp.GetSharedInputGroup(busyin[cardids[i]].inputid, inputid)) 00442 conflicts.push_back(busyin[cardids[i]]); 00443 } 00444 } 00445 00446 // If none are busy, we don't need to check further 00447 busygrp[groupid] = !conflicts.empty(); 00448 if (conflicts.empty()) 00449 return false; 00450 00451 InputInfo in; 00452 in.inputid = inputid; 00453 if (!CardUtil::GetInputInfo(in)) 00454 return true; 00455 00456 // If they aren't using the same source they are definately busy 00457 bool is_busy_input = false; 00458 00459 for (uint i = 0; i < conflicts.size() && !is_busy_input; i++) 00460 is_busy_input = (in.sourceid != conflicts[i].sourceid); 00461 00462 if (is_busy_input) 00463 return true; 00464 00465 // If the source's channels aren't digitally tuned then there is a conflict 00466 is_busy_input = !SourceUtil::HasDigitalChannel(in.sourceid); 00467 if (!is_busy_input && conflicts[0].chanid) 00468 { 00469 MSqlQuery query(MSqlQuery::InitCon()); 00470 query.prepare( 00471 "SELECT mplexid " 00472 "FROM channel " 00473 "WHERE chanid = :CHANID"); 00474 query.bindValue(":CHANID", conflicts[0].chanid); 00475 if (!query.exec()) 00476 MythDB::DBError("is_input_group_busy", query); 00477 else if (query.next()) 00478 { 00479 mplexid_restriction = query.value(0).toUInt(); 00480 mplexid_restriction = (32767 == mplexid_restriction) ? 00481 0 : mplexid_restriction; 00482 } 00483 } 00484 00485 return is_busy_input; 00486 } 00487 00488 static bool is_input_busy( 00489 uint inputid, 00490 const vector<uint> &groupids, 00491 const vector<uint> &excluded_cardids, 00492 QMap<uint,bool> &busygrp, 00493 QMap<uint,bool> &busyrec, 00494 QMap<uint,TunedInputInfo> &busyin, 00495 uint &mplexid_restriction) 00496 { 00497 bool is_busy = false; 00498 for (uint i = 0; i < groupids.size() && !is_busy; i++) 00499 { 00500 is_busy |= is_input_group_busy( 00501 inputid, groupids[i], excluded_cardids, 00502 busygrp, busyrec, busyin, mplexid_restriction); 00503 } 00504 return is_busy; 00505 } 00506 00507 bool ChannelBase::IsInputAvailable( 00508 int inputid, uint &mplexid_restriction) const 00509 { 00510 if (inputid < 0) 00511 return false; 00512 00513 // Check each input to make sure it doesn't belong to an 00514 // input group which is attached to a busy recorder. 00515 QMap<uint,bool> busygrp; 00516 QMap<uint,bool> busyrec; 00517 QMap<uint,TunedInputInfo> busyin; 00518 00519 // Cache our busy input if applicable 00520 uint cid = GetCardID(); 00521 TunedInputInfo info; 00522 busyrec[cid] = m_pParent->IsBusy(&info); 00523 if (busyrec[cid]) 00524 { 00525 busyin[cid] = info; 00526 info.chanid = GetChanID(); 00527 } 00528 00529 vector<uint> excluded_cardids; 00530 excluded_cardids.push_back(cid); 00531 00532 mplexid_restriction = 0; 00533 00534 vector<uint> groupids = CardUtil::GetInputGroups(inputid); 00535 00536 return !is_input_busy(inputid, groupids, excluded_cardids, 00537 busygrp, busyrec, busyin, mplexid_restriction); 00538 } 00539 00548 vector<InputInfo> ChannelBase::GetFreeInputs( 00549 const vector<uint> &excluded_cardids) const 00550 { 00551 vector<InputInfo> new_list; 00552 00553 QStringList list = GetConnectedInputs(); 00554 if (list.empty()) 00555 return new_list; 00556 00557 // Check each input to make sure it doesn't belong to an 00558 // input group which is attached to a busy recorder. 00559 QMap<uint,bool> busygrp; 00560 QMap<uint,bool> busyrec; 00561 QMap<uint,TunedInputInfo> busyin; 00562 00563 // Cache our busy input if applicable 00564 TunedInputInfo info; 00565 uint cid = GetCardID(); 00566 busyrec[cid] = m_pParent->IsBusy(&info); 00567 if (busyrec[cid]) 00568 { 00569 busyin[cid] = info; 00570 info.chanid = GetChanID(); 00571 } 00572 00573 // If we're busy and not excluded, all inputs are busy 00574 if (busyrec[cid] && 00575 (find(excluded_cardids.begin(), excluded_cardids.end(), 00576 cid) == excluded_cardids.end())) 00577 { 00578 return new_list; 00579 } 00580 00581 QStringList::const_iterator it; 00582 for (it = list.begin(); it != list.end(); ++it) 00583 { 00584 InputInfo info; 00585 vector<uint> groupids; 00586 info.inputid = GetInputByName(*it); 00587 00588 if (!CardUtil::GetInputInfo(info, &groupids)) 00589 continue; 00590 00591 bool is_busy_grp = is_input_busy( 00592 info.inputid, groupids, excluded_cardids, 00593 busygrp, busyrec, busyin, info.mplexid); 00594 00595 if (!is_busy_grp && info.livetvorder) 00596 new_list.push_back(info); 00597 } 00598 00599 return new_list; 00600 } 00601 00602 uint ChannelBase::GetInputCardID(int inputNum) const 00603 { 00604 InputMap::const_iterator it = m_inputs.find(inputNum); 00605 if (it != m_inputs.end()) 00606 return (*it)->cardid; 00607 return 0; 00608 } 00609 00610 DBChanList ChannelBase::GetChannels(int inputNum) const 00611 { 00612 int inputid = (inputNum > 0) ? inputNum : m_currentInputID; 00613 00614 DBChanList ret; 00615 InputMap::const_iterator it = m_inputs.find(inputid); 00616 if (it != m_inputs.end()) 00617 ret = (*it)->channels; 00618 00619 return ret; 00620 } 00621 00622 DBChanList ChannelBase::GetChannels(const QString &inputname) const 00623 { 00624 int inputid = m_currentInputID; 00625 if (!inputname.isEmpty()) 00626 { 00627 int tmp = GetInputByName(inputname); 00628 inputid = (tmp > 0) ? tmp : inputid; 00629 } 00630 00631 return GetChannels(inputid); 00632 } 00633 00635 bool ChannelBase::KillScript(void) 00636 { 00637 if (!m_system) 00638 return true; 00639 00640 m_system->Term(true); 00641 00642 delete m_system; 00643 m_system = NULL; 00644 return true; 00645 } 00646 00648 void ChannelBase::HandleScript(const QString &freqid) 00649 { 00650 QMutexLocker locker(&m_system_lock); 00651 00652 bool ok = true; 00653 m_system_status = 0; // unknown 00654 00655 InputMap::const_iterator it = m_inputs.find(m_currentInputID); 00656 if (it == m_inputs.end()) 00657 { 00658 m_system_status = 2; // failed 00659 HandleScriptEnd(true); 00660 return; 00661 } 00662 00663 if ((*it)->externalChanger.isEmpty()) 00664 { 00665 m_system_status = 3; // success 00666 HandleScriptEnd(true); 00667 return; 00668 } 00669 00670 if (freqid.isEmpty()) 00671 { 00672 LOG(VB_GENERAL, LOG_WARNING, LOC + 00673 "A channel changer is set, but the freqid field is empty." 00674 "\n\t\t\tWe will return success to ease setup pains, " 00675 "but no script is will actually run."); 00676 m_system_status = 3; // success 00677 HandleScriptEnd(true); 00678 return; 00679 } 00680 00681 // It's possible we simply never reaped the process, check status first. 00682 if (m_system) 00683 GetScriptStatus(true); 00684 00685 // If it's still running, try killing it 00686 if (m_system) 00687 ok = KillScript(); 00688 00689 // The GetScriptStatus() call above can reset m_system_status with 00690 // the exit status of the last channel change script invocation, so 00691 // we must set it to pending here. 00692 m_system_status = 1; // pending 00693 00694 if (!ok) 00695 { 00696 LOG(VB_GENERAL, LOG_ERR, LOC + 00697 "Can not execute channel changer, previous call to script " 00698 "is still running."); 00699 m_system_status = 2; // failed 00700 HandleScriptEnd(ok); 00701 } 00702 else 00703 { 00704 if ((*it)->externalChanger.toLower() == "internal") 00705 { 00706 ok = ChangeInternalChannel(freqid, (*it)->inputid); 00707 if (!ok) 00708 { 00709 LOG(VB_GENERAL, LOG_ERR, LOC + "Can not execute internal channel " 00710 "changer."); 00711 m_system_status = 2; // failed 00712 } 00713 else 00714 m_system_status = 3; // success 00715 00716 HandleScriptEnd(ok); 00717 } 00718 else 00719 { 00720 ok = ChangeExternalChannel((*it)->externalChanger, freqid); 00721 if (!ok) 00722 { 00723 LOG(VB_GENERAL, LOG_ERR, LOC + "Can not execute channel changer."); 00724 m_system_status = 2; // failed 00725 HandleScriptEnd(ok); 00726 } 00727 } 00728 } 00729 } 00730 00731 bool ChannelBase::ChangeInternalChannel(const QString &freqid, 00732 uint inputid) 00733 { 00734 #ifdef USING_FIREWIRE 00735 FirewireDevice *device = NULL; 00736 QString fwnode = CardUtil::GetFirewireChangerNode(inputid); 00737 uint64_t guid = string_to_guid(fwnode); 00738 QString fwmodel = CardUtil::GetFirewireChangerModel(inputid); 00739 00740 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Internal channel change to %1 " 00741 "on inputid %2, GUID %3 (%4)").arg(freqid).arg(inputid) 00742 .arg(fwnode).arg(fwmodel)); 00743 00744 #ifdef USING_LINUX_FIREWIRE 00745 device = new LinuxFirewireDevice( 00746 guid, 0, 100, 1); 00747 #endif // USING_LINUX_FIREWIRE 00748 00749 #ifdef USING_OSX_FIREWIRE 00750 device = new DarwinFirewireDevice(guid, 0, 100); 00751 #endif // USING_OSX_FIREWIRE 00752 00753 if (!device) 00754 return false; 00755 00756 if (!device->OpenPort()) 00757 return false; 00758 00759 if (!device->SetChannel(fwmodel, 0, freqid.toUInt())) 00760 { 00761 device->ClosePort(); 00762 delete device; 00763 device = NULL; 00764 return false; 00765 } 00766 00767 device->ClosePort(); 00768 delete device; 00769 device = NULL; 00770 return true; 00771 #else 00772 return false; 00773 #endif 00774 } 00775 00777 bool ChannelBase::ChangeExternalChannel(const QString &changer, 00778 const QString &freqid) 00779 { 00780 if (m_system) 00781 return false; 00782 00783 if (changer.isEmpty() || freqid.isEmpty()) 00784 return false; 00785 00786 QString command = QString("%1 %2").arg(changer).arg(freqid); 00787 LOG(VB_CHANNEL, LOG_INFO, LOC + 00788 QString("Running command: %1").arg(command)); 00789 00790 m_system = new MythSystem(command, kMSRunShell | kMSRunBackground); 00791 m_system->Run(); 00792 00793 return true; 00794 } 00795 00796 uint ChannelBase::GetScriptStatus(bool holding_lock) 00797 { 00798 if (!m_system) 00799 return m_system_status; 00800 00801 if (!holding_lock) 00802 m_system_lock.lock(); 00803 00804 m_system_status = m_system->Wait(); 00805 if (m_system_status != GENERIC_EXIT_RUNNING && 00806 m_system_status != GENERIC_EXIT_START) 00807 { 00808 delete m_system; 00809 m_system = NULL; 00810 00811 HandleScriptEnd(m_system_status == GENERIC_EXIT_OK); 00812 } 00813 00814 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("GetScriptStatus() %1") 00815 .arg(m_system_status)); 00816 00817 uint ret; 00818 switch(m_system_status) 00819 { 00820 case GENERIC_EXIT_OK: 00821 ret = 3; // success 00822 break; 00823 case GENERIC_EXIT_RUNNING: 00824 case GENERIC_EXIT_START: 00825 ret = 1; // pending 00826 break; 00827 default: 00828 ret = 2; // fail 00829 break; 00830 } 00831 00832 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("GetScriptStatus() %1 -> %2") 00833 .arg(m_system_status). arg(ret)); 00834 00835 m_system_status = ret; 00836 00837 if (!holding_lock) 00838 m_system_lock.unlock(); 00839 00840 return ret; 00841 } 00842 00844 void ChannelBase::HandleScriptEnd(bool ok) 00845 { 00846 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Channel change script %1") 00847 .arg((ok) ? "succeeded" : "failed")); 00848 00849 if (ok) 00850 { 00851 InputMap::const_iterator it = m_inputs.find(m_currentInputID); 00852 if (it != m_inputs.end()) 00853 { 00854 // Set this as the future start channel for this source 00855 (*it)->startChanNum = m_curchannelname; 00856 } 00857 } 00858 } 00859 00863 int ChannelBase::GetCardID(void) const 00864 { 00865 if (m_cardid > 0) 00866 return m_cardid; 00867 00868 if (m_pParent) 00869 return m_pParent->GetCaptureCardNum(); 00870 00871 if (GetDevice().isEmpty()) 00872 return -1; 00873 00874 uint tmpcardid = CardUtil::GetFirstCardID(GetDevice()); 00875 return (tmpcardid <= 0) ? -1 : tmpcardid; 00876 } 00877 00878 int ChannelBase::GetChanID() const 00879 { 00880 InputMap::const_iterator it = m_inputs.find(m_currentInputID); 00881 if (it == m_inputs.end()) 00882 return false; 00883 00884 MSqlQuery query(MSqlQuery::InitCon()); 00885 00886 query.prepare("SELECT chanid FROM channel " 00887 "WHERE channum = :CHANNUM AND " 00888 " sourceid = :SOURCEID"); 00889 query.bindValue(":CHANNUM", m_curchannelname); 00890 query.bindValue(":SOURCEID", (*it)->sourceid); 00891 00892 if (!query.exec() || !query.isActive()) 00893 { 00894 MythDB::DBError("fetching chanid", query); 00895 return -1; 00896 } 00897 00898 if (query.size() <= 0) 00899 return -1; 00900 00901 query.next(); 00902 return query.value(0).toInt(); 00903 } 00904 00908 bool ChannelBase::InitializeInputs(void) 00909 { 00910 ClearInputMap(); 00911 00912 uint cardid = max(GetCardID(), 0); 00913 if (!cardid) 00914 { 00915 LOG(VB_GENERAL, LOG_ERR, 00916 "InitializeInputs(): Programmer error, cardid invalid."); 00917 return false; 00918 } 00919 00920 MSqlQuery query(MSqlQuery::InitCon()); 00921 query.prepare( 00922 "SELECT cardinputid, " 00923 " inputname, startchan, " 00924 " tunechan, externalcommand, " 00925 " sourceid, livetvorder " 00926 "FROM cardinput " 00927 "WHERE cardid = :CARDID"); 00928 query.bindValue(":CARDID", cardid); 00929 00930 if (!query.exec() || !query.isActive()) 00931 { 00932 MythDB::DBError("InitializeInputs", query); 00933 return false; 00934 } 00935 else if (!query.size()) 00936 { 00937 LOG(VB_GENERAL, LOG_ERR, "InitializeInputs(): " 00938 "\n\t\t\tCould not get inputs for the capturecard." 00939 "\n\t\t\tPerhaps you have forgotten to bind video" 00940 "\n\t\t\tsources to your card's inputs?"); 00941 return false; 00942 } 00943 00944 m_allchannels.clear(); 00945 QString order = gCoreContext->GetSetting("ChannelOrdering", "channum"); 00946 while (query.next()) 00947 { 00948 uint sourceid = query.value(5).toUInt(); 00949 DBChanList channels = ChannelUtil::GetChannels(sourceid, false); 00950 00951 ChannelUtil::SortChannels(channels, order); 00952 00953 m_inputs[query.value(0).toUInt()] = new ChannelInputInfo( 00954 query.value(1).toString(), query.value(2).toString(), 00955 query.value(3).toString(), query.value(4).toString(), 00956 sourceid, cardid, 00957 query.value(0).toUInt(), query.value(5).toUInt(), 00958 0, channels); 00959 00960 if (!IsExternalChannelChangeSupported() && 00961 !m_inputs[query.value(0).toUInt()]->externalChanger.isEmpty()) 00962 { 00963 LOG(VB_GENERAL, LOG_WARNING, LOC + "External Channel changer is " 00964 "set, but this device does not support it."); 00965 m_inputs[query.value(0).toUInt()]->externalChanger.clear(); 00966 } 00967 00968 m_allchannels.insert(m_allchannels.end(), 00969 channels.begin(), channels.end()); 00970 } 00971 ChannelUtil::SortChannels(m_allchannels, order, true); 00972 00973 m_currentInputID = GetStartInput(cardid); 00974 00975 // In case that initial input is not set 00976 if (m_currentInputID == -1) 00977 m_currentInputID = GetNextInputNum(); 00978 00979 // print em 00980 InputMap::const_iterator it; 00981 for (it = m_inputs.begin(); it != m_inputs.end(); ++it) 00982 { 00983 LOG(VB_CHANNEL, LOG_INFO, LOC + 00984 QString("Input #%1: '%2' schan(%3) sourceid(%4) ccid(%5)") 00985 .arg(it.key()).arg((*it)->name).arg((*it)->startChanNum) 00986 .arg((*it)->sourceid).arg((*it)->cardid)); 00987 } 00988 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Current Input #%1: '%2'") 00989 .arg(GetCurrentInputNum()).arg(GetCurrentInput())); 00990 00991 return m_inputs.size(); 00992 } 00993 00997 void ChannelBase::Renumber(uint sourceid, 00998 const QString &oldChanNum, 00999 const QString &newChanNum) 01000 { 01001 InputMap::iterator it = m_inputs.begin(); 01002 01003 for (; it != m_inputs.end(); ++it) 01004 { 01005 bool skip = ((*it)->name.isEmpty() || 01006 (*it)->startChanNum.isEmpty() || 01007 (*it)->startChanNum != oldChanNum || 01008 (*it)->sourceid != sourceid); 01009 if (!skip) 01010 (*it)->startChanNum = newChanNum; 01011 } 01012 01013 if (GetCurrentSourceID() == sourceid && oldChanNum == m_curchannelname) 01014 m_curchannelname = newChanNum; 01015 01016 StoreInputChannels(m_inputs); 01017 } 01018 01023 void ChannelBase::StoreInputChannels(const InputMap &inputs) 01024 { 01025 MSqlQuery query(MSqlQuery::InitCon()); 01026 InputMap::const_iterator it = inputs.begin(); 01027 for (; it != inputs.end(); ++it) 01028 { 01029 if ((*it)->name.isEmpty() || (*it)->startChanNum.isEmpty()) 01030 continue; 01031 01032 query.prepare( 01033 "UPDATE cardinput " 01034 "SET startchan = :STARTCHAN " 01035 "WHERE cardinputid = :CARDINPUTID"); 01036 query.bindValue(":STARTCHAN", (*it)->startChanNum); 01037 query.bindValue(":CARDINPUTID", it.key()); 01038 01039 if (!query.exec() || !query.isActive()) 01040 MythDB::DBError("StoreInputChannels", query); 01041 } 01042 } 01043 01048 int ChannelBase::GetStartInput(uint cardid) 01049 { 01050 return GetInputByName(CardUtil::GetStartInput(cardid)); 01051 } 01052 01053 bool ChannelBase::CheckChannel(const QString &channum, 01054 QString& inputName) const 01055 { 01056 inputName = ""; 01057 01058 bool ret = false; 01059 01060 QString channelinput = GetCurrentInput(); 01061 01062 MSqlQuery query(MSqlQuery::InitCon()); 01063 if (!query.isConnected()) 01064 return false; 01065 01066 query.prepare( 01067 "SELECT channel.chanid " 01068 "FROM channel, capturecard, cardinput " 01069 "WHERE channel.channum = :CHANNUM AND " 01070 " channel.sourceid = cardinput.sourceid AND " 01071 " cardinput.inputname = :INPUT AND " 01072 " cardinput.cardid = capturecard.cardid AND " 01073 " capturecard.cardid = :CARDID AND " 01074 " capturecard.hostname = :HOSTNAME"); 01075 query.bindValue(":CHANNUM", channum); 01076 query.bindValue(":INPUT", channelinput); 01077 query.bindValue(":CARDID", GetCardID()); 01078 query.bindValue(":HOSTNAME", gCoreContext->GetHostName()); 01079 01080 if (!query.exec() || !query.isActive()) 01081 { 01082 MythDB::DBError("checkchannel", query); 01083 } 01084 else if (query.size() > 0) 01085 { 01086 return true; 01087 } 01088 01089 QString msg = QString( 01090 "Failed to find channel(%1) on current input (%2) of card (%3).") 01091 .arg(channum).arg(channelinput).arg(GetCardID()); 01092 LOG(VB_CHANNEL, LOG_ERR, LOC + msg); 01093 01094 // We didn't find it on the current input let's widen the search 01095 query.prepare( 01096 "SELECT channel.chanid, cardinput.inputname " 01097 "FROM channel, capturecard, cardinput " 01098 "WHERE channel.channum = :CHANNUM AND " 01099 " channel.sourceid = cardinput.sourceid AND " 01100 " cardinput.cardid = capturecard.cardid AND " 01101 " capturecard.cardid = :CARDID AND " 01102 " capturecard.hostname = :HOSTNAME"); 01103 query.bindValue(":CHANNUM", channum); 01104 query.bindValue(":CARDID", GetCardID()); 01105 query.bindValue(":HOSTNAME", gCoreContext->GetHostName()); 01106 01107 if (!query.exec() || !query.isActive()) 01108 { 01109 MythDB::DBError("checkchannel", query); 01110 } 01111 else if (query.size() > 0) 01112 { 01113 query.next(); 01114 QString test = query.value(1).toString(); 01115 if (test != QString::null) 01116 inputName = test; 01117 01118 msg = QString("Found channel(%1) on another input (%2) of card (%3).") 01119 .arg(channum).arg(inputName).arg(GetCardID()); 01120 LOG(VB_CHANNEL, LOG_INFO, LOC + msg); 01121 01122 return true; 01123 } 01124 01125 msg = QString("Failed to find channel(%1) on any input of card (%2).") 01126 .arg(channum).arg(GetCardID()); 01127 LOG(VB_CHANNEL, LOG_ERR, LOC + msg); 01128 01129 query.prepare("SELECT NULL FROM channel"); 01130 01131 if (query.exec() && query.size() == 0) 01132 ret = true; 01133 01134 return ret; 01135 } 01136 01137 void ChannelBase::ClearInputMap(void) 01138 { 01139 InputMap::iterator it = m_inputs.begin(); 01140 for (; it != m_inputs.end(); ++it) 01141 delete *it; 01142 m_inputs.clear(); 01143 } 01144 01145 ChannelBase *ChannelBase::CreateChannel( 01146 TVRec *tvrec, 01147 const GeneralDBOptions &genOpt, 01148 const DVBDBOptions &dvbOpt, 01149 const FireWireDBOptions &fwOpt, 01150 const QString &startchannel, 01151 bool enter_power_save_mode, 01152 QString &rbFileExt) 01153 { 01154 rbFileExt = "mpg"; 01155 01156 ChannelBase *channel = NULL; 01157 if (genOpt.cardtype == "DVB") 01158 { 01159 #ifdef USING_DVB 01160 channel = new DVBChannel(genOpt.videodev, tvrec); 01161 dynamic_cast<DVBChannel*>(channel)->SetSlowTuning( 01162 dvbOpt.dvb_tuning_delay); 01163 #endif 01164 } 01165 else if (genOpt.cardtype == "FIREWIRE") 01166 { 01167 #ifdef USING_FIREWIRE 01168 channel = new FirewireChannel(tvrec, genOpt.videodev, fwOpt); 01169 #endif 01170 } 01171 else if (genOpt.cardtype == "HDHOMERUN") 01172 { 01173 #ifdef USING_HDHOMERUN 01174 channel = new HDHRChannel(tvrec, genOpt.videodev); 01175 #endif 01176 } 01177 else if ((genOpt.cardtype == "IMPORT") || 01178 (genOpt.cardtype == "DEMO") || 01179 (genOpt.cardtype == "MPEG" && 01180 genOpt.videodev.toLower().left(5) == "file:")) 01181 { 01182 channel = new DummyChannel(tvrec); 01183 } 01184 else if (genOpt.cardtype == "FREEBOX") 01185 { 01186 #ifdef USING_IPTV 01187 channel = new IPTVChannel(tvrec, genOpt.videodev); 01188 #endif 01189 } 01190 else if (genOpt.cardtype == "ASI") 01191 { 01192 #ifdef USING_ASI 01193 channel = new ASIChannel(tvrec, genOpt.videodev); 01194 #endif 01195 } 01196 else if (genOpt.cardtype == "CETON") 01197 { 01198 #ifdef USING_CETON 01199 channel = new CetonChannel(tvrec, genOpt.videodev); 01200 #endif 01201 } 01202 else if (CardUtil::IsV4L(genOpt.cardtype)) 01203 { 01204 #ifdef USING_V4L2 01205 channel = new V4LChannel(tvrec, genOpt.videodev); 01206 #endif 01207 if ((genOpt.cardtype != "MPEG") && (genOpt.cardtype != "HDPVR")) 01208 rbFileExt = "nuv"; 01209 } 01210 01211 if (!channel) 01212 { 01213 QString msg = QString( 01214 "%1 card configured on video device %2, \n" 01215 "but MythTV was not compiled with %3 support. \n" 01216 "\n" 01217 "Recompile MythTV with %4 support or remove the card \n" 01218 "from the configuration and restart MythTV.") 01219 .arg(genOpt.cardtype).arg(genOpt.videodev) 01220 .arg(genOpt.cardtype).arg(genOpt.cardtype); 01221 LOG(VB_GENERAL, LOG_ERR, "ChannelBase: CreateChannel() Error: \n" + 01222 msg + "\n"); 01223 return NULL; 01224 } 01225 01226 if (!channel->Open()) 01227 { 01228 LOG(VB_GENERAL, LOG_ERR, "ChannelBase: CreateChannel() Error: " + 01229 QString("Failed to open device %1").arg(genOpt.videodev)); 01230 delete channel; 01231 return NULL; 01232 } 01233 01234 QString input = CardUtil::GetStartInput(tvrec->GetCaptureCardNum()); 01235 QString channum = startchannel; 01236 channel->Init(input, channum, true); 01237 01238 if (enter_power_save_mode) 01239 { 01240 if (channel && 01241 ((genOpt.cardtype == "DVB" && dvbOpt.dvb_on_demand) || 01242 CardUtil::IsV4L(genOpt.cardtype))) 01243 { 01244 channel->Close(); 01245 } 01246 else 01247 { 01248 DTVChannel *dtvchannel = dynamic_cast<DTVChannel*>(channel); 01249 if (dtvchannel) 01250 dtvchannel->EnterPowerSavingMode(); 01251 } 01252 } 01253 01254 return channel; 01255 } 01256
1.7.6.1