MythTV  0.26-pre
channelbase.cpp
Go to the documentation of this file.
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 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends