|
MythTV
0.26-pre
|
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 }
1.7.6.1