MythTV  0.26-pre
livetvchain.cpp
Go to the documentation of this file.
00001 #include "livetvchain.h"
00002 #include "mythcontext.h"
00003 #include "mythdb.h"
00004 #include "mythlogging.h"
00005 #include "programinfo.h"
00006 #include "mythsocket.h"
00007 #include "cardutil.h"
00008 
00009 #define LOC QString("LiveTVChain(%1): ").arg(m_id)
00010 
00011 static inline void clear(LiveTVChainEntry &entry)
00012 {
00013     entry.chanid = 0;
00014     entry.starttime.setTime_t(0);
00015     entry.endtime = QDateTime();
00016     entry.discontinuity = true;
00017     entry.hostprefix = QString();
00018     entry.cardtype = QString();
00019     entry.channum = QString();
00020     entry.inputname = QString();
00021 }
00022 
00026 LiveTVChain::LiveTVChain() :
00027     m_id(""), m_maxpos(0), m_lock(QMutex::Recursive),
00028     m_curpos(0), m_cur_chanid(0),
00029     m_switchid(-1), m_jumppos(0)
00030 {
00031     clear(m_switchentry);
00032 }
00033 
00034 LiveTVChain::~LiveTVChain()
00035 {
00036 }
00037 
00038 QString LiveTVChain::InitializeNewChain(const QString &seed)
00039 {
00040     QDateTime curdt = QDateTime::currentDateTime();
00041     m_id = QString("live-%1-%2").arg(seed).arg(curdt.toString(Qt::ISODate));
00042     return m_id;
00043 }
00044 
00045 void LiveTVChain::SetHostPrefix(const QString &prefix)
00046 {
00047     m_hostprefix = prefix;
00048 }
00049 
00050 void LiveTVChain::SetCardType(const QString &type)
00051 {
00052     m_cardtype = type;
00053 }
00054 
00055 void LiveTVChain::LoadFromExistingChain(const QString &id)
00056 {
00057     m_id = id;
00058     ReloadAll();
00059 }
00060 
00061 void LiveTVChain::AppendNewProgram(ProgramInfo *pginfo, QString channum,
00062                                    QString inputname, bool discont)
00063 {
00064     QMutexLocker lock(&m_lock);
00065 
00066     QTime tmptime = pginfo->GetRecordingStartTime().time();
00067 
00068     LiveTVChainEntry newent;
00069     newent.chanid = pginfo->GetChanID();
00070     newent.starttime = pginfo->GetRecordingStartTime();
00071     newent.starttime.setTime(QTime(tmptime.hour(), tmptime.minute(),
00072                                    tmptime.second()));
00073     newent.discontinuity = discont;
00074     newent.hostprefix = m_hostprefix;
00075     newent.cardtype = m_cardtype;
00076     newent.channum = channum;
00077     newent.inputname = inputname;
00078 
00079     m_chain.append(newent);
00080 
00081     MSqlQuery query(MSqlQuery::InitCon());
00082     query.prepare("INSERT INTO tvchain (chanid, starttime, endtime, chainid,"
00083                   " chainpos, discontinuity, watching, hostprefix, cardtype, "
00084                   " channame, input) "
00085                   "VALUES(:CHANID, :START, :END, :CHAINID, :CHAINPOS, "
00086                   " :DISCONT, :WATCHING, :PREFIX, :CARDTYPE, :CHANNAME, "
00087                   " :INPUT );");
00088     query.bindValue(":CHANID", pginfo->GetChanID());
00089     query.bindValue(":START", pginfo->GetRecordingStartTime());
00090     query.bindValue(":END", pginfo->GetRecordingEndTime());
00091     query.bindValue(":CHAINID", m_id);
00092     query.bindValue(":CHAINPOS", m_maxpos);
00093     query.bindValue(":DISCONT", discont);
00094     query.bindValue(":WATCHING", 0);
00095     query.bindValue(":PREFIX", m_hostprefix);
00096     query.bindValue(":CARDTYPE", m_cardtype);
00097     query.bindValue(":CHANNAME", channum);
00098     query.bindValue(":INPUT", inputname);
00099 
00100     if (!query.exec() || !query.isActive())
00101         MythDB::DBError("Chain: AppendNewProgram", query);
00102     else
00103         LOG(VB_RECORD, LOG_INFO, QString("Chain: Appended@%3 '%1_%2'")
00104                 .arg(newent.chanid)
00105                 .arg(newent.starttime.toString("yyyyMMddhhmmss"))
00106                 .arg(m_maxpos));
00107 
00108     m_maxpos++;
00109     BroadcastUpdate();
00110 }
00111 
00112 void LiveTVChain::FinishedRecording(ProgramInfo *pginfo)
00113 {
00114     QMutexLocker lock(&m_lock);
00115 
00116     MSqlQuery query(MSqlQuery::InitCon());
00117     query.prepare("UPDATE tvchain SET endtime = :END "
00118                   "WHERE chanid = :CHANID AND starttime = :START ;");
00119     query.bindValue(":END", pginfo->GetRecordingEndTime());
00120     query.bindValue(":CHANID", pginfo->GetChanID());
00121     query.bindValue(":START", pginfo->GetRecordingStartTime());
00122 
00123     if (!query.exec() || !query.isActive())
00124         MythDB::DBError("Chain: FinishedRecording", query);
00125     else
00126         LOG(VB_RECORD, LOG_INFO,
00127             QString("Chain: Updated endtime for '%1_%2' to %3")
00128                 .arg(pginfo->GetChanID())
00129                 .arg(pginfo->GetRecordingStartTime(MythDate))
00130                 .arg(pginfo->GetRecordingEndTime(MythDate)));
00131 
00132     QList<LiveTVChainEntry>::iterator it;
00133     for (it = m_chain.begin(); it != m_chain.end(); ++it)
00134     {
00135         if ((*it).chanid    == pginfo->GetChanID() &&
00136             (*it).starttime == pginfo->GetRecordingStartTime())
00137         {
00138             (*it).endtime = pginfo->GetRecordingEndTime();
00139         }
00140     }
00141     BroadcastUpdate();
00142 }
00143 
00144 void LiveTVChain::DeleteProgram(ProgramInfo *pginfo)
00145 {
00146     QMutexLocker lock(&m_lock);
00147 
00148     QList<LiveTVChainEntry>::iterator it, del;
00149     for (it = m_chain.begin(); it != m_chain.end(); ++it)
00150     {
00151         if ((*it).chanid    == pginfo->GetChanID() &&
00152             (*it).starttime == pginfo->GetRecordingStartTime())
00153         {
00154             del = it;
00155             ++it;
00156 
00157             MSqlQuery query(MSqlQuery::InitCon());
00158             if (it != m_chain.end())
00159             {
00160                 (*it).discontinuity = true;
00161                 query.prepare("UPDATE tvchain SET discontinuity = :DISCONT "
00162                               "WHERE chanid = :CHANID AND starttime = :START "
00163                               "AND chainid = :CHAINID ;");
00164                 query.bindValue(":CHANID", (*it).chanid);
00165                 query.bindValue(":START", (*it).starttime);
00166                 query.bindValue(":CHAINID", m_id);
00167                 query.bindValue(":DISCONT", true);
00168                 if (!query.exec())
00169                     MythDB::DBError("LiveTVChain::DeleteProgram -- "
00170                                     "discontinuity", query);
00171             }
00172 
00173             query.prepare("DELETE FROM tvchain WHERE chanid = :CHANID "
00174                           "AND starttime = :START AND chainid = :CHAINID ;");
00175             query.bindValue(":CHANID", (*del).chanid);
00176             query.bindValue(":START", (*del).starttime);
00177             query.bindValue(":CHAINID", m_id);
00178             if (!query.exec())
00179                 MythDB::DBError("LiveTVChain::DeleteProgram -- delete", query);
00180 
00181             m_chain.erase(del);
00182 
00183             BroadcastUpdate();
00184             break;
00185         }
00186     }
00187 }
00188 
00189 void LiveTVChain::BroadcastUpdate(void)
00190 {
00191     QString message = QString("LIVETV_CHAIN UPDATE %1").arg(m_id);
00192     MythEvent me(message);
00193     gCoreContext->dispatch(me);
00194 }
00195 
00196 void LiveTVChain::DestroyChain(void)
00197 {
00198     QMutexLocker lock(&m_lock);
00199 
00200     m_chain.clear();
00201 
00202     MSqlQuery query(MSqlQuery::InitCon());
00203     query.prepare("DELETE FROM tvchain WHERE chainid = :CHAINID ;");
00204     query.bindValue(":CHAINID", m_id);
00205 
00206     if (!query.exec())
00207         MythDB::DBError("LiveTVChain::DestroyChain", query);
00208 }
00209 
00210 void LiveTVChain::ReloadAll(void)
00211 {
00212     QMutexLocker lock(&m_lock);
00213 
00214     int prev_size = m_chain.size();
00215     m_chain.clear();
00216 
00217     MSqlQuery query(MSqlQuery::InitCon());
00218     query.prepare("SELECT chanid, starttime, endtime, discontinuity, "
00219                   "chainpos, hostprefix, cardtype, channame, input "
00220                   "FROM tvchain "
00221                   "WHERE chainid = :CHAINID ORDER BY chainpos;");
00222     query.bindValue(":CHAINID", m_id);
00223 
00224     if (query.exec() && query.isActive() && query.size() > 0)
00225     {
00226         while (query.next())
00227         {
00228 
00229             LiveTVChainEntry entry;
00230             entry.chanid = query.value(0).toUInt();
00231             entry.starttime = query.value(1).toDateTime();
00232             entry.endtime = query.value(2).toDateTime();
00233             entry.discontinuity = query.value(3).toInt();
00234             entry.hostprefix = query.value(5).toString();
00235             entry.cardtype = query.value(6).toString();
00236             entry.channum = query.value(7).toString();
00237             entry.inputname = query.value(8).toString();
00238 
00239             m_maxpos = query.value(4).toInt() + 1;
00240 
00241             m_chain.append(entry);
00242         }
00243     }
00244 
00245     m_curpos = ProgramIsAt(m_cur_chanid, m_cur_startts);
00246     if (m_curpos < 0)
00247         m_curpos = 0;
00248 
00249     if (m_switchid >= 0)
00250         m_switchid = ProgramIsAt(m_switchentry.chanid,m_switchentry.starttime);
00251 
00252     if (prev_size!=m_chain.size())
00253     {
00254         LOG(VB_PLAYBACK, LOG_INFO, LOC + "ReloadAll(): Added new recording");
00255         LOG(VB_PLAYBACK, LOG_INFO, LOC + toString());
00256     }
00257 }
00258 
00259 void LiveTVChain::GetEntryAt(int at, LiveTVChainEntry &entry) const
00260 {
00261     QMutexLocker lock(&m_lock);
00262 
00263     int size = m_chain.count();
00264     int new_at = (size && (at < 0 || at >= size)) ? size - 1 : at;
00265 
00266     if (size && new_at >= 0 && new_at < size)
00267         entry = m_chain[new_at];
00268     else
00269     {
00270         LOG(VB_GENERAL, LOG_ERR, QString("GetEntryAt(%1) failed.").arg(at));
00271         if (at == -1)
00272             LOG(VB_GENERAL, LOG_ERR, "It appears that your backend may "
00273                 "be misconfigured.  Check your backend logs to determine "
00274                 "whether your capture cards, lineups, channels, or storage "
00275                 "configuration are reporting errors.  This issue is commonly "
00276                 "caused by failing to complete all setup steps properly.  You "
00277                 "may wish to review the documentation for mythtv-setup.");
00278         clear(entry);
00279     }
00280 }
00281 
00282 ProgramInfo *LiveTVChain::EntryToProgram(const LiveTVChainEntry &entry)
00283 {
00284     ProgramInfo *pginfo = new ProgramInfo(entry.chanid, entry.starttime);
00285 
00286     if (pginfo->GetChanID())
00287     {
00288         pginfo->SetPathname(entry.hostprefix + pginfo->GetBasename());
00289         return pginfo;
00290     }
00291 
00292     LOG(VB_GENERAL, LOG_ERR,
00293         QString("EntryToProgram(%1@%2) failed to get pginfo")
00294             .arg(entry.chanid).arg(entry.starttime.toString()));
00295     delete pginfo;
00296     return NULL;
00297 }
00298 
00306 ProgramInfo *LiveTVChain::GetProgramAt(int at) const
00307 {
00308     LiveTVChainEntry entry;
00309     GetEntryAt(at, entry);
00310 
00311     return EntryToProgram(entry);
00312 }
00313 
00317 int LiveTVChain::ProgramIsAt(uint chanid, const QDateTime &starttime) const
00318 {
00319     QMutexLocker lock(&m_lock);
00320 
00321     int count = 0;
00322     QList<LiveTVChainEntry>::const_iterator it;
00323     for (it = m_chain.begin(); it != m_chain.end(); ++it, ++count)
00324     {
00325         if ((*it).chanid == chanid &&
00326             (*it).starttime == starttime)
00327         {
00328             return count;
00329         }
00330     }
00331 
00332     return -1;
00333 }
00334 
00338 int LiveTVChain::ProgramIsAt(const ProgramInfo &pginfo) const
00339 {
00340     return ProgramIsAt(pginfo.GetChanID(), pginfo.GetRecordingStartTime());
00341 }
00342 
00346 int LiveTVChain::GetLengthAtCurPos(void)
00347 {
00348     QMutexLocker lock(&m_lock);
00349     LiveTVChainEntry entry;
00350 
00351     entry = m_chain[m_curpos];
00352     if (m_curpos == ((int)m_chain.count() - 1))
00353         return entry.starttime.secsTo(QDateTime::currentDateTime());
00354     else
00355         return entry.starttime.secsTo(entry.endtime);
00356 }
00357 
00358 int LiveTVChain::TotalSize(void) const
00359 {
00360     return m_chain.count();
00361 }
00362 
00363 void LiveTVChain::SetProgram(const ProgramInfo &pginfo)
00364 {
00365     QMutexLocker lock(&m_lock);
00366 
00367     m_cur_chanid  = pginfo.GetChanID();
00368     m_cur_startts = pginfo.GetRecordingStartTime();
00369 
00370     m_curpos = ProgramIsAt(pginfo);
00371     m_switchid = -1;
00372 }
00373 
00374 bool LiveTVChain::HasNext(void) const
00375 {
00376     return ((int)m_chain.count() - 1 > m_curpos);
00377 }
00378 
00379 void LiveTVChain::ClearSwitch(void)
00380 {
00381     QMutexLocker lock(&m_lock);
00382 
00383     m_switchid = -1;
00384     m_jumppos = 0;
00385 }
00386 
00397 ProgramInfo *LiveTVChain::GetSwitchProgram(bool &discont, bool &newtype,
00398                                            int &newid)
00399 {
00400     ReloadAll();
00401     QMutexLocker lock(&m_lock);
00402 
00403     if (m_switchid < 0 || m_curpos == m_switchid)
00404     {
00405         ClearSwitch();
00406         return NULL;
00407     }
00408 
00409     LiveTVChainEntry oldentry, entry;
00410     GetEntryAt(m_curpos, oldentry);
00411 
00412     ProgramInfo *pginfo = NULL;
00413     while (!pginfo && m_switchid < (int)m_chain.count() && m_switchid >= 0)
00414     {
00415         GetEntryAt(m_switchid, entry);
00416 
00417         bool at_last_entry = 
00418             ((m_switchid > m_curpos) &&
00419              (m_switchid == (int)(m_chain.count()-1))) ||
00420             ((m_switchid <= m_curpos) && (m_switchid == 0));
00421 
00422         // Skip dummy recordings, if possible.
00423         if (at_last_entry || (entry.cardtype != "DUMMY"))
00424             pginfo = EntryToProgram(entry);
00425 
00426         // Skip empty recordings, if possible
00427         if (pginfo && (0 == pginfo->GetFilesize()) &&
00428             m_switchid < (int)(m_chain.count()-1))
00429         {
00430             LOG(VB_GENERAL, LOG_WARNING,
00431                 QString("Skipping empty program %1")
00432                 .arg(pginfo->MakeUniqueKey()));
00433             delete pginfo;
00434             pginfo = NULL;
00435         }
00436 
00437         if (!pginfo)
00438         {
00439             if (m_switchid > m_curpos)
00440                 m_switchid++;
00441             else
00442                 m_switchid--;
00443         }
00444     }
00445 
00446     if (!pginfo)
00447     {
00448         ClearSwitch();
00449         return NULL;
00450     }
00451 
00452     discont = true;
00453     if (m_curpos == m_switchid - 1)
00454         discont = entry.discontinuity;
00455 
00456     newtype = (oldentry.cardtype != entry.cardtype);
00457 
00458     // Some cards can change their streams dramatically on a channel change...
00459     if (discont)
00460         newtype |= CardUtil::IsChannelChangeDiscontinuous(entry.cardtype);
00461 
00462     newid = m_switchid;
00463 
00464     ClearSwitch();
00465 
00466     return pginfo;
00467 }
00468 
00473 void LiveTVChain::SwitchTo(int num)
00474 {
00475     QMutexLocker lock(&m_lock);
00476 
00477     LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SwitchTo(%1)").arg(num));
00478 
00479     int size = m_chain.count();
00480     if ((num < 0) || (num >= size))
00481         num = size - 1;
00482 
00483     if (m_curpos != num)
00484     {
00485         m_switchid = num;
00486         GetEntryAt(num, m_switchentry);
00487     }
00488     else
00489         LOG(VB_GENERAL, LOG_ERR, LOC + "SwitchTo() not switching to current");
00490 
00491     if (VERBOSE_LEVEL_CHECK(VB_PLAYBACK, LOG_DEBUG))
00492     {
00493         LiveTVChainEntry e;
00494         GetEntryAt(num, e);
00495         QString msg = QString("%1_%2")
00496             .arg(e.chanid).arg(e.starttime.toString("yyyyMMddhhmmss"));
00497         LOG(VB_PLAYBACK, LOG_DEBUG,
00498             LOC + QString("Entry@%1: '%2')").arg(num).arg(msg));
00499     }
00500 }
00501 
00507 void LiveTVChain::SwitchToNext(bool up)
00508 {
00509 #if 0
00510     LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "SwitchToNext("<<(up?"up":"down")<<")");
00511 #endif
00512     if (up && HasNext())
00513         SwitchTo(m_curpos + 1);
00514     else if (!up && HasPrev())
00515         SwitchTo(m_curpos - 1);
00516 }
00517 
00518 void LiveTVChain::JumpTo(int num, int pos)
00519 {
00520     m_jumppos = pos;
00521     SwitchTo(num);
00522 }
00523 
00524 void LiveTVChain::JumpToNext(bool up, int pos)
00525 {
00526     m_jumppos = pos;
00527     SwitchToNext(up);
00528 }
00529 
00533 int LiveTVChain::GetJumpPos(void)
00534 {
00535     int ret = m_jumppos;
00536     m_jumppos = 0;
00537     return ret;
00538 }
00539 
00540 QString LiveTVChain::GetChannelName(int pos) const
00541 {
00542     LiveTVChainEntry entry;
00543     GetEntryAt(pos, entry);
00544 
00545     return entry.channum;
00546 }
00547 
00548 QString LiveTVChain::GetInputName(int pos) const
00549 {
00550     LiveTVChainEntry entry;
00551     GetEntryAt(pos, entry);
00552 
00553     return entry.inputname;
00554 }
00555 
00556 QString LiveTVChain::GetCardType(int pos) const
00557 {
00558     LiveTVChainEntry entry;
00559     GetEntryAt(pos, entry);
00560 
00561     return entry.cardtype;
00562 }
00563 
00564 void LiveTVChain::SetHostSocket(MythSocket *sock)
00565 {
00566     QMutexLocker lock(&m_sockLock);
00567 
00568     if (!m_inUseSocks.contains(sock))
00569         m_inUseSocks.append(sock);
00570 }
00571 
00572 bool LiveTVChain::IsHostSocket(const MythSocket *sock) const
00573 {
00574     QMutexLocker lock(&m_sockLock);
00575     return m_inUseSocks.contains(const_cast<MythSocket*>(sock));
00576 }
00577 
00578 uint LiveTVChain::HostSocketCount(void) const
00579 {
00580     QMutexLocker lock(&m_sockLock);
00581     return m_inUseSocks.count();
00582 }
00583 
00584 void LiveTVChain::DelHostSocket(MythSocket *sock)
00585 {
00586     QMutexLocker lock(&m_sockLock);
00587     m_inUseSocks.removeAll(sock);
00588 }
00589 
00590 static QString toString(const LiveTVChainEntry &v)
00591 {
00592     return QString("%1: %2 (%3 to %4)%5")
00593         .arg(v.cardtype,6).arg(v.chanid,4)
00594         .arg(v.starttime.time().toString())
00595         .arg(v.endtime.time().toString())
00596         .arg(v.discontinuity?" discontinuous":"");
00597 }
00598 
00599 QString LiveTVChain::toString() const
00600 {
00601     QMutexLocker lock(&m_lock);
00602     QString ret = QString("LiveTVChain has %1 entries\n").arg(m_chain.size());
00603     for (uint i = 0; i < (uint)m_chain.size(); i++)
00604     {
00605         ret += (QString((i==(uint)m_curpos) ? "* " : "  ") +
00606                 ::toString(m_chain[i]) + "\n");
00607     }
00608     return ret;
00609 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends