00001
00002 #include <cstdlib>
00003 #include <cerrno>
00004
00005
00006 #include <unistd.h>
00007 #include <fcntl.h>
00008 #include <sys/stat.h>
00009 #include <sys/types.h>
00010
00011
00012 #include <iostream>
00013 #include <algorithm>
00014 using namespace std;
00015
00016
00017 #include <QCoreApplication>
00018
00019
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
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
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
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
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
00271 if (!m_inputs.size())
00272 return -1;
00273
00274
00275 InputMap::const_iterator it;
00276 it = m_inputs.find(m_currentInputID);
00277
00278
00279
00280 bool skip_incr = false;
00281 if (it == m_inputs.end())
00282 {
00283 it = m_inputs.begin();
00284 skip_incr = true;
00285 }
00286
00287
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
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
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
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
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
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
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
00514
00515 QMap<uint,bool> busygrp;
00516 QMap<uint,bool> busyrec;
00517 QMap<uint,TunedInputInfo> busyin;
00518
00519
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
00558
00559 QMap<uint,bool> busygrp;
00560 QMap<uint,bool> busyrec;
00561 QMap<uint,TunedInputInfo> busyin;
00562
00563
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
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;
00654
00655 InputMap::const_iterator it = m_inputs.find(m_currentInputID);
00656 if (it == m_inputs.end())
00657 {
00658 m_system_status = 2;
00659 HandleScriptEnd(true);
00660 return;
00661 }
00662
00663 if ((*it)->externalChanger.isEmpty())
00664 {
00665 m_system_status = 3;
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;
00677 HandleScriptEnd(true);
00678 return;
00679 }
00680
00681
00682 if (m_system)
00683 GetScriptStatus(true);
00684
00685
00686 if (m_system)
00687 ok = KillScript();
00688
00689
00690
00691
00692 m_system_status = 1;
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;
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;
00712 }
00713 else
00714 m_system_status = 3;
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;
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;
00822 break;
00823 case GENERIC_EXIT_RUNNING:
00824 case GENERIC_EXIT_START:
00825 ret = 1;
00826 break;
00827 default:
00828 ret = 2;
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
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
00976 if (m_currentInputID == -1)
00977 m_currentInputID = GetNextInputNum();
00978
00979
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
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