00001
00002 #include <cstdio>
00003 #include <cstdlib>
00004 #include <cerrno>
00005
00006
00007 #include <unistd.h>
00008 #include <fcntl.h>
00009 #include <signal.h>
00010 #include <sys/stat.h>
00011 #include <sys/types.h>
00012
00013
00014 #include <iostream>
00015 #include <algorithm>
00016 using namespace std;
00017
00018
00019 #include "videodev_myth.h"
00020 #include "channel.h"
00021 #include "frequencies.h"
00022 #include "tv_rec.h"
00023 #include "mythcontext.h"
00024 #include "exitcodes.h"
00025 #include "mythdbcon.h"
00026 #include "cardutil.h"
00027 #include "channelutil.h"
00028 #include "remoteutil.h"
00029 #include "sourceutil.h"
00030 #include "cardutil.h"
00031 #include "compat.h"
00032
00033 #define LOC QString("ChannelBase(%1): ").arg(GetCardID())
00034 #define LOC_WARN QString("ChannelBase(%1) Warning: ").arg(GetCardID())
00035 #define LOC_ERR QString("ChannelBase(%1) Error: ").arg(GetCardID())
00036
00037 ChannelBase::ChannelBase(TVRec *parent)
00038 :
00039 pParent(parent), curchannelname(""),
00040 currentInputID(-1), commfree(false), cardid(0)
00041 {
00042 }
00043
00044 ChannelBase::~ChannelBase(void)
00045 {
00046 }
00047
00048 bool ChannelBase::Init(QString &inputname, QString &startchannel, bool setchan)
00049 {
00050 bool ok;
00051
00052 if (!setchan)
00053 ok = inputname.isEmpty() ? false : IsTunable(inputname, startchannel);
00054 else if (inputname.isEmpty())
00055 ok = SetChannelByString(startchannel);
00056 else
00057 ok = SwitchToInput(inputname, startchannel);
00058
00059 if (ok)
00060 return true;
00061
00062
00063 QString msg1 = QString("Setting start channel '%1' failed, ")
00064 .arg(startchannel);
00065 QString msg2 = "and we failed to find any suitible channels on any input.";
00066 bool msg_error = true;
00067
00068 QStringList inputs = GetConnectedInputs();
00069 QStringList::const_iterator start =
00070 find(inputs.begin(), inputs.end(), inputname);
00071 start = (start == inputs.end()) ? inputs.begin() : start;
00072
00073 if (start != inputs.end())
00074 {
00075 VERBOSE(VB_CHANNEL, LOC +
00076 QString("Looking for startchannel '%1' on input '%2'")
00077 .arg(startchannel).arg(*start));
00078 }
00079
00080
00081 QStringList::const_iterator it = start;
00082 while (it != inputs.end())
00083 {
00084 DBChanList channels = GetChannels(*it);
00085
00086 DBChanList::const_iterator cit = channels.begin();
00087 for (; cit != channels.end(); cit++)
00088 {
00089 if ((*cit).channum == startchannel &&
00090 IsTunable(*it, startchannel))
00091 {
00092 inputname = *it;
00093 VERBOSE(VB_CHANNEL, LOC +
00094 QString("Found startchannel '%1' on input '%2'")
00095 .arg(startchannel).arg(inputname));
00096 return true;
00097 }
00098 }
00099
00100 it++;
00101 it = (it == inputs.end()) ? inputs.begin() : it;
00102 if (it == start)
00103 break;
00104 }
00105
00106 it = start;
00107 while (it != inputs.end() && !ok)
00108 {
00109 uint mplexid_restriction = 0;
00110
00111 DBChanList channels = GetChannels(*it);
00112 if (channels.size() &&
00113 IsInputAvailable(GetInputByName(*it), mplexid_restriction))
00114 {
00115 uint chanid = ChannelUtil::GetNextChannel(
00116 channels, channels[0].chanid,
00117 mplexid_restriction, CHANNEL_DIRECTION_UP);
00118
00119 DBChanList::const_iterator cit =
00120 find(channels.begin(), channels.end(), chanid);
00121
00122 if (chanid && cit != channels.end())
00123 {
00124 if (!setchan)
00125 {
00126 ok = IsTunable(*it, (mplexid_restriction) ?
00127 (*cit).channum : startchannel);
00128 }
00129 else
00130 ok = SwitchToInput(*it, (*cit).channum);
00131
00132 if (ok)
00133 {
00134 inputname = *it;
00135 if (mplexid_restriction)
00136 startchannel = QDeepCopy<QString>((*cit).channum);
00137 msg2 = QString("selected to '%1' on input '%2' instead.")
00138 .arg(startchannel).arg(inputname);
00139 msg_error = false;
00140 }
00141 }
00142 }
00143
00144 it++;
00145 it = (it == inputs.end()) ? inputs.begin() : it;
00146 if (it == start)
00147 break;
00148 }
00149
00150 VERBOSE(((msg_error) ? VB_IMPORTANT : VB_GENERAL),
00151 ((msg_error) ? LOC_ERR : LOC_WARN) +
00152 msg1 + "\n\t\t\t" + msg2);
00153
00154 return ok;
00155 }
00156
00157 bool ChannelBase::IsTunable(const QString &input, const QString &channum) const
00158 {
00159 QString loc = LOC + QString("IsTunable(%1,%2)").arg(input).arg(channum);
00160
00161 int inputid = currentInputID;
00162 if (!input.isEmpty())
00163 inputid = GetInputByName(input);
00164
00165 InputMap::const_iterator it = inputs.find(inputid);
00166 if (it == inputs.end())
00167 {
00168 VERBOSE(VB_IMPORTANT, loc + " " + QString(
00169 "Requested non-existant input '%1':'%2' ")
00170 .arg(input).arg(inputid));
00171
00172 return false;
00173 }
00174
00175 uint mplexid_restriction;
00176 if (!IsInputAvailable(inputid, mplexid_restriction))
00177 {
00178 VERBOSE(VB_IMPORTANT, loc + " " + QString(
00179 "Requested channel is on input '%1' "
00180 "which is in a busy input group")
00181 .arg(inputid));
00182
00183 return false;
00184 }
00185
00186
00187 QString tvformat, modulation, freqtable, freqid, dtv_si_std;
00188 int finetune;
00189 uint64_t frequency;
00190 int mpeg_prog_num;
00191 uint atsc_major, atsc_minor, mplexid, tsid, netid;
00192 bool commfree;
00193
00194 if (!ChannelUtil::GetChannelData(
00195 (*it)->sourceid, channum,
00196 tvformat, modulation, freqtable, freqid,
00197 finetune, frequency,
00198 dtv_si_std, mpeg_prog_num, atsc_major, atsc_minor, tsid, netid,
00199 mplexid, commfree))
00200 {
00201 VERBOSE(VB_IMPORTANT, loc + " " + QString(
00202 "Failed to find channel in DB on input '%1' ")
00203 .arg(inputid));
00204
00205 return false;
00206 }
00207
00208 if (mplexid_restriction && (mplexid != mplexid_restriction))
00209 {
00210 VERBOSE(VB_IMPORTANT, loc + " " + QString(
00211 "Channel is valid, but tuner is busy "
00212 "on different multiplex (%1 != %2)")
00213 .arg(mplexid).arg(mplexid_restriction));
00214
00215 return false;
00216 }
00217
00218 return true;
00219 }
00220
00221 uint ChannelBase::GetNextChannel(uint chanid, int direction) const
00222 {
00223 if (!chanid)
00224 {
00225 InputMap::const_iterator it = inputs.find(currentInputID);
00226 if (it == inputs.end())
00227 return 0;
00228
00229 chanid = ChannelUtil::GetChanID((*it)->sourceid, curchannelname);
00230 }
00231
00232 uint mplexid_restriction = 0;
00233 IsInputAvailable(currentInputID, mplexid_restriction);
00234
00235 return ChannelUtil::GetNextChannel(
00236 allchannels, chanid, mplexid_restriction, direction);
00237 }
00238
00239 uint ChannelBase::GetNextChannel(const QString &channum, int direction) const
00240 {
00241 InputMap::const_iterator it = inputs.find(currentInputID);
00242 if (it == inputs.end())
00243 return 0;
00244
00245 uint chanid = ChannelUtil::GetChanID((*it)->sourceid, channum);
00246 return GetNextChannel(chanid, direction);
00247 }
00248
00249 int ChannelBase::GetNextInputNum(void) const
00250 {
00251
00252 if (!inputs.size())
00253 return -1;
00254
00255
00256 InputMap::const_iterator it;
00257 it = inputs.find(currentInputID);
00258
00259
00260
00261 bool skip_incr = false;
00262 if (it == inputs.end())
00263 {
00264 it = inputs.begin();
00265 skip_incr = true;
00266 }
00267
00268
00269 int i = 0;
00270 for (; i < 100; i++)
00271 {
00272 if (!skip_incr)
00273 {
00274 ++it;
00275 it = (it == inputs.end()) ? inputs.begin() : it;
00276 }
00277 skip_incr = false;
00278
00279 if ((*it)->sourceid)
00280 break;
00281 }
00282
00283
00284 return (i<100) ? (int)it.key() : -1;
00285 }
00286
00290 QStringList ChannelBase::GetConnectedInputs(void) const
00291 {
00292 QStringList list;
00293
00294 InputMap::const_iterator it = inputs.begin();
00295 for (; it != inputs.end(); ++it)
00296 if ((*it)->sourceid)
00297 list.push_back((*it)->name);
00298
00299 return list;
00300 }
00301
00305 QString ChannelBase::GetInputByNum(int capchannel) const
00306 {
00307 InputMap::const_iterator it = inputs.find(capchannel);
00308 if (it != inputs.end())
00309 return (*it)->name;
00310 return QString::null;
00311 }
00312
00316 int ChannelBase::GetInputByName(const QString &input) const
00317 {
00318 InputMap::const_iterator it = inputs.begin();
00319 for (; it != inputs.end(); ++it)
00320 {
00321 if ((*it)->name == input)
00322 return (int)it.key();
00323 }
00324 return -1;
00325 }
00326
00327 bool ChannelBase::SwitchToInput(const QString &inputname)
00328 {
00329 int input = GetInputByName(inputname);
00330
00331 if (input >= 0)
00332 return SwitchToInput(input, true);
00333 else
00334 VERBOSE(VB_IMPORTANT, QString("ChannelBase: Could not find input: "
00335 "%1 on card\n").arg(inputname));
00336 return false;
00337 }
00338
00339 bool ChannelBase::SwitchToInput(const QString &inputname, const QString &chan)
00340 {
00341 int input = GetInputByName(inputname);
00342
00343 bool ok = false;
00344 if (input >= 0)
00345 {
00346 ok = SwitchToInput(input, false);
00347 if (ok)
00348 ok = SetChannelByString(chan);
00349 }
00350 else
00351 {
00352 VERBOSE(VB_IMPORTANT,
00353 QString("ChannelBase: Could not find input: %1 on card when "
00354 "setting channel %2\n").arg(inputname).arg(chan));
00355 }
00356 return ok;
00357 }
00358
00359 bool ChannelBase::SwitchToInput(int newInputNum, bool setstarting)
00360 {
00361 InputMap::const_iterator it = inputs.find(newInputNum);
00362 if (it == inputs.end() || (*it)->startChanNum.isEmpty())
00363 return false;
00364
00365 uint mplexid_restriction;
00366 if (!IsInputAvailable(newInputNum, mplexid_restriction))
00367 return false;
00368
00369
00370
00371 if (setstarting)
00372 return SetChannelByString((*it)->startChanNum);
00373
00374 return true;
00375 }
00376
00377 static bool is_input_group_busy(
00378 uint inputid,
00379 uint groupid,
00380 const vector<uint> &excluded_cardids,
00381 QMap<uint,bool> &busygrp,
00382 QMap<uint,bool> &busyrec,
00383 QMap<uint,TunedInputInfo> &busyin,
00384 uint &mplexid_restriction)
00385 {
00386
00387 QMap<uint,bool>::const_iterator bit = busygrp.find(groupid);
00388 if ((bit != busygrp.end()) && !*bit)
00389 return false;
00390
00391 vector<TunedInputInfo> conflicts;
00392 vector<uint> cardids = CardUtil::GetGroupCardIDs(groupid);
00393 for (uint i = 0; i < cardids.size(); i++)
00394 {
00395 if (find(excluded_cardids.begin(),
00396 excluded_cardids.end(), cardids[i]) != excluded_cardids.end())
00397 {
00398 continue;
00399 }
00400
00401 TunedInputInfo info;
00402 QMap<uint,bool>::const_iterator it = busyrec.find(cardids[i]);
00403 if (it == busyrec.end())
00404 {
00405 busyrec[cardids[i]] = RemoteIsBusy(cardids[i], info);
00406 it = busyrec.find(cardids[i]);
00407 if (*it)
00408 busyin[cardids[i]] = info;
00409 }
00410
00411 if (*it)
00412 conflicts.push_back(busyin[cardids[i]]);
00413 }
00414
00415
00416 busygrp[groupid] = !conflicts.empty();
00417 if (conflicts.empty())
00418 return false;
00419
00420 InputInfo in;
00421 in.inputid = inputid;
00422 if (!CardUtil::GetInputInfo(in))
00423 return true;
00424
00425
00426 bool is_busy_input = false;
00427
00428 for (uint i = 0; i < conflicts.size() && !is_busy_input; i++)
00429 is_busy_input = (in.sourceid != conflicts[i].sourceid);
00430
00431 if (is_busy_input)
00432 return true;
00433
00434
00435 is_busy_input = !SourceUtil::HasDigitalChannel(in.sourceid);
00436 if (!is_busy_input && conflicts[0].chanid)
00437 {
00438 MSqlQuery query(MSqlQuery::InitCon());
00439 query.prepare(
00440 "SELECT mplexid "
00441 "FROM channel "
00442 "WHERE chanid = :CHANID");
00443 query.bindValue(":CHANID", conflicts[0].chanid);
00444 if (!query.exec())
00445 MythContext::DBError("is_input_group_busy", query);
00446 else if (query.next())
00447 {
00448 mplexid_restriction = query.value(0).toUInt();
00449 mplexid_restriction = (32767 == mplexid_restriction) ?
00450 0 : mplexid_restriction;
00451 }
00452 }
00453
00454 return is_busy_input;
00455 }
00456
00457 static bool is_input_busy(
00458 uint inputid,
00459 const vector<uint> &groupids,
00460 const vector<uint> &excluded_cardids,
00461 QMap<uint,bool> &busygrp,
00462 QMap<uint,bool> &busyrec,
00463 QMap<uint,TunedInputInfo> &busyin,
00464 uint &mplexid_restriction)
00465 {
00466 bool is_busy = false;
00467 for (uint i = 0; i < groupids.size() && !is_busy; i++)
00468 {
00469 is_busy |= is_input_group_busy(
00470 inputid, groupids[i], excluded_cardids,
00471 busygrp, busyrec, busyin, mplexid_restriction);
00472 }
00473 return is_busy;
00474 }
00475
00476 bool ChannelBase::IsInputAvailable(
00477 int inputid, uint &mplexid_restriction) const
00478 {
00479 if (inputid < 0)
00480 return false;
00481
00482
00483
00484 QMap<uint,bool> busygrp;
00485 QMap<uint,bool> busyrec;
00486 QMap<uint,TunedInputInfo> busyin;
00487
00488
00489 uint cid = GetCardID();
00490 TunedInputInfo info;
00491 busyrec[cid] = pParent->IsBusy(&info);
00492 if (busyrec[cid])
00493 {
00494 busyin[cid] = info;
00495 info.chanid = GetChanID();
00496 }
00497
00498 vector<uint> excluded_cardids;
00499 excluded_cardids.push_back(cid);
00500
00501 mplexid_restriction = 0;
00502
00503 vector<uint> groupids = CardUtil::GetInputGroups(inputid);
00504
00505 return !is_input_busy(inputid, groupids, excluded_cardids,
00506 busygrp, busyrec, busyin, mplexid_restriction);
00507 }
00508
00517 vector<InputInfo> ChannelBase::GetFreeInputs(
00518 const vector<uint> &excluded_cardids) const
00519 {
00520 vector<InputInfo> new_list;
00521
00522 QStringList list = GetConnectedInputs();
00523 if (list.empty())
00524 return new_list;
00525
00526
00527
00528 QMap<uint,bool> busygrp;
00529 QMap<uint,bool> busyrec;
00530 QMap<uint,TunedInputInfo> busyin;
00531
00532
00533 TunedInputInfo info;
00534 uint cid = GetCardID();
00535 busyrec[cid] = pParent->IsBusy(&info);
00536 if (busyrec[cid])
00537 {
00538 busyin[cid] = info;
00539 info.chanid = GetChanID();
00540 }
00541
00542
00543 if (busyrec[cid] &&
00544 (find(excluded_cardids.begin(), excluded_cardids.end(),
00545 cid) == excluded_cardids.end()))
00546 {
00547 return new_list;
00548 }
00549
00550 QStringList::const_iterator it;
00551 for (it = list.begin(); it != list.end(); ++it)
00552 {
00553 InputInfo info;
00554 vector<uint> groupids;
00555 info.inputid = GetInputByName(*it);
00556
00557 if (!CardUtil::GetInputInfo(info, &groupids))
00558 continue;
00559
00560 bool is_busy_grp = is_input_busy(
00561 info.inputid, groupids, excluded_cardids,
00562 busygrp, busyrec, busyin, info.mplexid);
00563
00564 if (!is_busy_grp)
00565 new_list.push_back(info);
00566 }
00567
00568 return new_list;
00569 }
00570
00571 uint ChannelBase::GetInputCardID(int inputNum) const
00572 {
00573 InputMap::const_iterator it = inputs.find(inputNum);
00574 if (it != inputs.end())
00575 return (*it)->cardid;
00576 return 0;
00577 }
00578
00579 DBChanList ChannelBase::GetChannels(int inputNum) const
00580 {
00581 int inputid = (inputNum > 0) ? inputNum : currentInputID;
00582
00583 DBChanList ret;
00584 InputMap::const_iterator it = inputs.find(inputid);
00585 if (it != inputs.end())
00586 ret = (*it)->channels;
00587
00588 return ret;
00589 }
00590
00591 DBChanList ChannelBase::GetChannels(const QString &inputname) const
00592 {
00593 int inputid = currentInputID;
00594 if (!inputname.isEmpty())
00595 {
00596 int tmp = GetInputByName(inputname);
00597 inputid = (tmp > 0) ? tmp : inputid;
00598 }
00599
00600 return GetChannels(inputid);
00601 }
00602
00603 bool ChannelBase::ChangeExternalChannel(const QString &channum)
00604 {
00605 #ifdef USING_MINGW
00606 VERBOSE(VB_IMPORTANT, LOC_WARN +
00607 QString("ChangeExternalChannel is not implemented in MinGW."));
00608 return false;
00609 #else
00610 InputMap::const_iterator it = inputs.find(currentInputID);
00611 QString changer = (*it)->externalChanger;
00612
00613 if (changer.isEmpty())
00614 return false;
00615
00616 QString command = QString("%1 %2").arg(changer).arg(channum);
00617
00618 VERBOSE(VB_CHANNEL, QString("External channel change: %1").arg(command));
00619 pid_t child = fork();
00620 if (child < 0)
00621 {
00622 VERBOSE(VB_IMPORTANT, LOC_ERR + "Fork error -- " + ENO);
00623 return false;
00624 }
00625 else if (child == 0)
00626 {
00627 for(int i = 3; i < sysconf(_SC_OPEN_MAX) - 1; ++i)
00628 close(i);
00629 int ret = execl("/bin/sh", "sh", "-c", command.ascii(), (char *)NULL);
00630 QString msg("ChannelBase: ");
00631 if (EACCES == ret) {
00632 msg.append(QString("Access denied to /bin/sh"
00633 " when executing %1\n").arg(command.ascii()));
00634 }
00635 msg.append(strerror(errno));
00636 VERBOSE(VB_IMPORTANT, msg);
00637 _exit(CHANNEL__EXIT__EXECL_ERROR);
00638 }
00639 else
00640 {
00641 int status = 0, pid = 0;
00642 VERBOSE(VB_CHANNEL, "Waiting for External Tuning program to exit");
00643
00644 bool timed_out = false;
00645 uint timeout = 30;
00646 time_t start_time = time(0);
00647 while (-1 != pid && !timed_out)
00648 {
00649 sleep(1);
00650 pid = waitpid(child, &status, WUNTRACED|WNOHANG);
00651 VERBOSE(VB_IMPORTANT, QString("ret_pid(%1) child(%2) status(0x%3)")
00652 .arg(pid).arg(child).arg(status,0,16));
00653 if (pid==child)
00654 break;
00655 else if (time(0) > (time_t)(start_time + timeout))
00656 timed_out = true;
00657 }
00658 if (timed_out)
00659 {
00660 VERBOSE(VB_IMPORTANT, "External Tuning program timed out, killing");
00661 kill(child, SIGTERM);
00662 usleep(500);
00663 kill(child, SIGKILL);
00664 return false;
00665 }
00666
00667 VERBOSE(VB_CHANNEL, "External Tuning program no longer running");
00668 if (WIFEXITED(status))
00669 {
00670 int ret = WEXITSTATUS(status);
00671 if (CHANNEL__EXIT__EXECL_ERROR == ret)
00672 {
00673 VERBOSE(VB_IMPORTANT, QString("ChannelBase: Could not execute "
00674 "external tuning program."));
00675 return false;
00676 }
00677 else if (ret)
00678 {
00679 VERBOSE(VB_IMPORTANT,
00680 QString("ChannelBase: external tuning program "
00681 "exited with error %1").arg(ret));
00682 return false;
00683 }
00684 VERBOSE(VB_IMPORTANT, "External Tuning program exited with no error");
00685 }
00686 else
00687 {
00688 QString msg = QString("ChannelBase: external tuning program "
00689 "encountered error %1 -- ").arg(errno);
00690 msg.append(strerror(errno));
00691 VERBOSE(VB_IMPORTANT, msg);
00692 return false;
00693 }
00694 }
00695
00696 return true;
00697 #endif // !USING_MINGW
00698 }
00699
00703 int ChannelBase::GetCardID(void) const
00704 {
00705 if (cardid > 0)
00706 return cardid;
00707
00708 if (pParent)
00709 return pParent->GetCaptureCardNum();
00710
00711 if (GetDevice().isEmpty())
00712 return -1;
00713
00714 uint tmpcardid = CardUtil::GetFirstCardID(GetDevice());
00715 return (tmpcardid <= 0) ? -1 : tmpcardid;
00716 }
00717
00718 int ChannelBase::GetChanID() const
00719 {
00720 InputMap::const_iterator it = inputs.find(currentInputID);
00721 if (it == inputs.end())
00722 return false;
00723
00724 MSqlQuery query(MSqlQuery::InitCon());
00725
00726 query.prepare("SELECT chanid FROM channel "
00727 "WHERE channum = :CHANNUM AND "
00728 " sourceid = :SOURCEID");
00729 query.bindValue(":CHANNUM", curchannelname);
00730 query.bindValue(":SOURCEID", (*it)->sourceid);
00731
00732 if (!query.exec() || !query.isActive())
00733 {
00734 MythContext::DBError("fetching chanid", query);
00735 return -1;
00736 }
00737
00738 if (query.size() <= 0)
00739 return -1;
00740
00741 query.next();
00742 return query.value(0).toInt();
00743 }
00744
00748 bool ChannelBase::InitializeInputs(void)
00749 {
00750 inputs.clear();
00751
00752 uint cardid = max(GetCardID(), 0);
00753 if (!cardid)
00754 {
00755 VERBOSE(VB_IMPORTANT, LOC_ERR + "InitializeInputs(): "
00756 "Programmer error, cardid invalid.");
00757 return false;
00758 }
00759
00760 MSqlQuery query(MSqlQuery::InitCon());
00761 query.prepare(
00762 "SELECT cardinputid, "
00763 " inputname, startchan, "
00764 " tunechan, externalcommand, "
00765 " sourceid "
00766 "FROM cardinput "
00767 "WHERE cardid = :CARDID");
00768 query.bindValue(":CARDID", cardid);
00769
00770 if (!query.exec() || !query.isActive())
00771 {
00772 MythContext::DBError("InitializeInputs", query);
00773 return false;
00774 }
00775 else if (!query.size())
00776 {
00777 VERBOSE(VB_IMPORTANT, LOC_ERR + "InitializeInputs(): "
00778 "\n\t\t\tCould not get inputs for the capturecard."
00779 "\n\t\t\tPerhaps you have forgotten to bind video"
00780 "\n\t\t\tsources to your card's inputs?");
00781 return false;
00782 }
00783
00784 allchannels.clear();
00785 QString order = gContext->GetSetting("ChannelOrdering", "channum");
00786 while (query.next())
00787 {
00788 uint sourceid = query.value(5).toUInt();
00789 DBChanList channels = ChannelUtil::GetChannels(sourceid, false);
00790
00791 ChannelUtil::SortChannels(channels, order);
00792
00793 inputs[query.value(0).toUInt()] = new ChannelInputInfo(
00794 query.value(1).toString(), query.value(2).toString(),
00795 query.value(3).toString(), query.value(4).toString(),
00796 sourceid, cardid,
00797 query.value(0).toUInt(), 0,
00798 channels);
00799
00800 allchannels.insert(allchannels.end(),
00801 channels.begin(), channels.end());
00802 }
00803 ChannelUtil::SortChannels(allchannels, order);
00804 ChannelUtil::EliminateDuplicateChanNum(allchannels);
00805
00806
00807 currentInputID = -1;
00808 currentInputID = GetNextInputNum();
00809
00810
00811 InputMap::const_iterator it;
00812 for (it = inputs.begin(); it != inputs.end(); ++it)
00813 {
00814 VERBOSE(VB_CHANNEL, LOC + QString("Input #%1: '%2' schan(%3) "
00815 "sourceid(%4) ccid(%5)")
00816 .arg(it.key()).arg((*it)->name).arg((*it)->startChanNum)
00817 .arg((*it)->sourceid).arg((*it)->cardid));
00818 }
00819 VERBOSE(VB_CHANNEL, LOC + QString("Current Input #%1: '%2'")
00820 .arg(GetCurrentInputNum()).arg(GetCurrentInput()));
00821
00822 return inputs.size();
00823 }
00824
00828 void ChannelBase::Renumber(uint sourceid,
00829 const QString &oldChanNum,
00830 const QString &newChanNum)
00831 {
00832 InputMap::iterator it = inputs.begin();
00833
00834 for (; it != inputs.end(); ++it)
00835 {
00836 bool skip = ((*it)->name.isEmpty() ||
00837 (*it)->startChanNum.isEmpty() ||
00838 (*it)->startChanNum != oldChanNum ||
00839 (*it)->sourceid != sourceid);
00840 if (!skip)
00841 (*it)->startChanNum = newChanNum;
00842 }
00843
00844 if (GetCurrentSourceID() == sourceid && oldChanNum == curchannelname)
00845 curchannelname = newChanNum;
00846
00847 StoreInputChannels(inputs);
00848 }
00849
00854 void ChannelBase::StoreInputChannels(const InputMap &inputs)
00855 {
00856 MSqlQuery query(MSqlQuery::InitCon());
00857 InputMap::const_iterator it = inputs.begin();
00858 for (; it != inputs.end(); ++it)
00859 {
00860 if ((*it)->name.isEmpty() || (*it)->startChanNum.isEmpty())
00861 continue;
00862
00863 query.prepare(
00864 "UPDATE cardinput "
00865 "SET startchan = :STARTCHAN "
00866 "WHERE cardinputid = :CARDINPUTID");
00867 query.bindValue(":STARTCHAN", (*it)->startChanNum);
00868 query.bindValue(":CARDINPUTID", it.key());
00869
00870 if (!query.exec() || !query.isActive())
00871 MythContext::DBError("StoreInputChannels", query);
00872 }
00873 }
00874
00880 void ChannelBase::StoreDefaultInput(uint cardid, const QString &input)
00881 {
00882 MSqlQuery query(MSqlQuery::InitCon());
00883 query.prepare(
00884 "UPDATE capturecard "
00885 "SET defaultinput = :DEFAULTINPUT "
00886 "WHERE cardid = :CARDID");
00887 query.bindValue(":DEFAULTINPUT", input);
00888 query.bindValue(":CARDID", cardid);
00889
00890 if (!query.exec() || !query.isActive())
00891 MythContext::DBError("StoreDefaultInput", query);
00892 }
00893
00894 bool ChannelBase::CheckChannel(const QString &channum,
00895 QString& inputName) const
00896 {
00897 inputName = "";
00898
00899 bool ret = false;
00900
00901 QString channelinput = GetCurrentInput();
00902
00903 MSqlQuery query(MSqlQuery::InitCon());
00904 if (!query.isConnected())
00905 return false;
00906
00907 query.prepare(
00908 "SELECT channel.chanid "
00909 "FROM channel, capturecard, cardinput "
00910 "WHERE channel.channum = :CHANNUM AND "
00911 " channel.sourceid = cardinput.sourceid AND "
00912 " cardinput.inputname = :INPUT AND "
00913 " cardinput.cardid = capturecard.cardid AND "
00914 " capturecard.cardid = :CARDID AND "
00915 " capturecard.hostname = :HOSTNAME");
00916 query.bindValue(":CHANNUM", channum);
00917 query.bindValue(":INPUT", channelinput);
00918 query.bindValue(":CARDID", GetCardID());
00919 query.bindValue(":HOSTNAME", gContext->GetHostName());
00920
00921 if (!query.exec() || !query.isActive())
00922 {
00923 MythContext::DBError("checkchannel", query);
00924 }
00925 else if (query.size() > 0)
00926 {
00927 return true;
00928 }
00929
00930 QString msg = QString(
00931 "Failed to find channel(%1) on current input (%2) of card (%3).")
00932 .arg(channum).arg(channelinput).arg(GetCardID());
00933 VERBOSE(VB_CHANNEL, LOC + msg);
00934
00935
00936 query.prepare(
00937 "SELECT channel.chanid, cardinput.inputname "
00938 "FROM channel, capturecard, cardinput "
00939 "WHERE channel.channum = :CHANNUM AND "
00940 " channel.sourceid = cardinput.sourceid AND "
00941 " cardinput.cardid = capturecard.cardid AND "
00942 " capturecard.cardid = :CARDID AND "
00943 " capturecard.hostname = :HOSTNAME");
00944 query.bindValue(":CHANNUM", channum);
00945 query.bindValue(":CARDID", GetCardID());
00946 query.bindValue(":HOSTNAME", gContext->GetHostName());
00947
00948 if (!query.exec() || !query.isActive())
00949 {
00950 MythContext::DBError("checkchannel", query);
00951 }
00952 else if (query.size() > 0)
00953 {
00954 query.next();
00955 QString test = query.value(1).toString();
00956 if (test != QString::null)
00957 inputName = QString::fromUtf8(test);
00958
00959 msg = QString("Found channel(%1) on another input (%2) of card (%3).")
00960 .arg(channum).arg(inputName).arg(GetCardID());
00961 VERBOSE(VB_CHANNEL, LOC + msg);
00962
00963 return true;
00964 }
00965
00966 msg = QString("Failed to find channel(%1) on any input of card (%2).")
00967 .arg(channum).arg(GetCardID());
00968 VERBOSE(VB_CHANNEL, LOC + msg);
00969
00970 query.prepare("SELECT NULL FROM channel");
00971
00972 if (query.exec() && query.size() == 0)
00973 ret = true;
00974
00975 return ret;
00976 }