00001 #include "livetvchain.h"
00002 #include "mythcontext.h"
00003 #include "mythdbcon.h"
00004 #include "programinfo.h"
00005 #include "mythsocket.h"
00006
00007 #define LOC QString("LiveTVChain(%1): ").arg(m_id)
00008
00012 LiveTVChain::LiveTVChain()
00013 : m_id(""), m_maxpos(0), m_lock(true), m_curpos(0), m_cur_chanid(""),
00014 m_switchid(-1), m_jumppos(0)
00015 {
00016 }
00017
00018 LiveTVChain::~LiveTVChain()
00019 {
00020 }
00021
00022 QString LiveTVChain::InitializeNewChain(const QString &seed)
00023 {
00024 QDateTime curdt = QDateTime::currentDateTime();
00025 m_id = QString("live-%1-%2").arg(seed).arg(curdt.toString(Qt::ISODate));
00026 return m_id;
00027 }
00028
00029 void LiveTVChain::SetHostPrefix(const QString &prefix)
00030 {
00031 m_hostprefix = prefix;
00032 }
00033
00034 void LiveTVChain::SetCardType(const QString &type)
00035 {
00036 m_cardtype = type;
00037 }
00038
00039 void LiveTVChain::LoadFromExistingChain(const QString &id)
00040 {
00041 m_id = id;
00042 ReloadAll();
00043 }
00044
00045 void LiveTVChain::AppendNewProgram(ProgramInfo *pginfo, QString channum,
00046 QString inputname, bool discont)
00047 {
00048 QMutexLocker lock(&m_lock);
00049
00050 QTime tmptime = pginfo->recstartts.time();
00051
00052 LiveTVChainEntry newent;
00053 newent.chanid = pginfo->chanid;
00054 newent.starttime = pginfo->recstartts;
00055 newent.starttime.setTime(QTime(tmptime.hour(), tmptime.minute(),
00056 tmptime.second()));
00057 newent.discontinuity = discont;
00058 newent.hostprefix = m_hostprefix;
00059 newent.cardtype = m_cardtype;
00060 newent.channum = channum;
00061 newent.inputname = inputname;
00062
00063 m_chain.append(newent);
00064
00065 MSqlQuery query(MSqlQuery::InitCon());
00066 query.prepare("INSERT INTO tvchain (chanid, starttime, endtime, chainid,"
00067 " chainpos, discontinuity, watching, hostprefix, cardtype, "
00068 " channame, input) "
00069 "VALUES(:CHANID, :START, :END, :CHAINID, :CHAINPOS, "
00070 " :DISCONT, :WATCHING, :PREFIX, :CARDTYPE, :CHANNAME, "
00071 " :INPUT );");
00072 query.bindValue(":CHANID", pginfo->chanid);
00073 query.bindValue(":START", pginfo->recstartts);
00074 query.bindValue(":END", pginfo->recendts);
00075 query.bindValue(":CHAINID", m_id);
00076 query.bindValue(":CHAINPOS", m_maxpos);
00077 query.bindValue(":DISCONT", discont);
00078 query.bindValue(":WATCHING", 0);
00079 query.bindValue(":PREFIX", m_hostprefix);
00080 query.bindValue(":CARDTYPE", m_cardtype);
00081 query.bindValue(":CHANNAME", channum.utf8());
00082 query.bindValue(":INPUT", inputname.utf8());
00083
00084 if (!query.exec() || !query.isActive())
00085 MythContext::DBError("Chain: AppendNewProgram", query);
00086 else
00087 VERBOSE(VB_RECORD, QString("Chain: Appended@%3 '%1_%2'")
00088 .arg(newent.chanid)
00089 .arg(newent.starttime.toString("yyyyMMddhhmmss"))
00090 .arg(m_maxpos));
00091
00092 m_maxpos++;
00093 BroadcastUpdate();
00094 }
00095
00096 void LiveTVChain::FinishedRecording(ProgramInfo *pginfo)
00097 {
00098 QMutexLocker lock(&m_lock);
00099
00100 MSqlQuery query(MSqlQuery::InitCon());
00101 query.prepare("UPDATE tvchain SET endtime = :END "
00102 "WHERE chanid = :CHANID AND starttime = :START ;");
00103 query.bindValue(":END", pginfo->recendts);
00104 query.bindValue(":CHANID", pginfo->chanid);
00105 query.bindValue(":START", pginfo->recstartts);
00106
00107 if (!query.exec() || !query.isActive())
00108 MythContext::DBError("Chain: FinishedRecording", query);
00109 else
00110 VERBOSE(VB_RECORD, QString("Chain: Updated endtime for '%1_%2' to %3")
00111 .arg(pginfo->chanid)
00112 .arg(pginfo->recstartts.toString("yyyyMMddhhmmss"))
00113 .arg(pginfo->recendts.toString("yyyyMMddhhmmss")));
00114
00115 QValueList<LiveTVChainEntry>::iterator it;
00116 for (it = m_chain.begin(); it != m_chain.end(); ++it)
00117 {
00118 if ((*it).chanid == pginfo->chanid &&
00119 (*it).starttime == pginfo->recstartts)
00120 {
00121 (*it).endtime = pginfo->recendts;
00122 }
00123 }
00124 BroadcastUpdate();
00125 }
00126
00127 void LiveTVChain::DeleteProgram(ProgramInfo *pginfo)
00128 {
00129 QMutexLocker lock(&m_lock);
00130
00131 QValueList<LiveTVChainEntry>::iterator it, del;
00132 for (it = m_chain.begin(); it != m_chain.end(); ++it)
00133 {
00134 if ((*it).chanid == pginfo->chanid &&
00135 (*it).starttime == pginfo->recstartts)
00136 {
00137 del = it;
00138 ++it;
00139
00140 MSqlQuery query(MSqlQuery::InitCon());
00141 if (it != m_chain.end())
00142 {
00143 (*it).discontinuity = true;
00144 query.prepare("UPDATE tvchain SET discontinuity = :DISCONT "
00145 "WHERE chanid = :CHANID AND starttime = :START "
00146 "AND chainid = :CHAINID ;");
00147 query.bindValue(":CHANID", (*it).chanid);
00148 query.bindValue(":START", (*it).starttime);
00149 query.bindValue(":CHAINID", m_id);
00150 query.bindValue(":DISCONT", true);
00151 query.exec();
00152 }
00153
00154 query.prepare("DELETE FROM tvchain WHERE chanid = :CHANID "
00155 "AND starttime = :START AND chainid = :CHAINID ;");
00156 query.bindValue(":CHANID", (*del).chanid);
00157 query.bindValue(":START", (*del).starttime);
00158 query.bindValue(":CHAINID", m_id);
00159 query.exec();
00160
00161 m_chain.remove(del);
00162
00163 BroadcastUpdate();
00164 break;
00165 }
00166 }
00167 }
00168
00169 void LiveTVChain::BroadcastUpdate(void)
00170 {
00171 QString message = QString("LIVETV_CHAIN UPDATE %1").arg(m_id);
00172 MythEvent me(message);
00173 gContext->dispatch(me);
00174 }
00175
00176 void LiveTVChain::DestroyChain(void)
00177 {
00178 QMutexLocker lock(&m_lock);
00179
00180 m_chain.clear();
00181
00182 MSqlQuery query(MSqlQuery::InitCon());
00183 query.prepare("DELETE FROM tvchain WHERE chainid = :CHAINID ;");
00184 query.bindValue(":CHAINID", m_id);
00185
00186 query.exec();
00187 }
00188
00189 void LiveTVChain::ReloadAll(void)
00190 {
00191 QMutexLocker lock(&m_lock);
00192
00193 uint prev_size = m_chain.size();
00194 m_chain.clear();
00195
00196 MSqlQuery query(MSqlQuery::InitCon());
00197 query.prepare("SELECT chanid, starttime, endtime, discontinuity, "
00198 "chainpos, hostprefix, cardtype, channame, input "
00199 "FROM tvchain "
00200 "WHERE chainid = :CHAINID ORDER BY chainpos;");
00201 query.bindValue(":CHAINID", m_id);
00202
00203 if (query.exec() && query.isActive() && query.size() > 0)
00204 {
00205 while (query.next())
00206 {
00207
00208 LiveTVChainEntry entry;
00209 entry.chanid = query.value(0).toString();
00210 entry.starttime = query.value(1).toDateTime();
00211 entry.endtime = query.value(2).toDateTime();
00212 entry.discontinuity = query.value(3).toInt();
00213 entry.hostprefix = query.value(5).toString();
00214 entry.cardtype = query.value(6).toString();
00215 entry.channum = QString::fromUtf8(query.value(7).toString());
00216 entry.inputname = QString::fromUtf8(query.value(8).toString());
00217
00218 m_maxpos = query.value(4).toInt() + 1;
00219
00220 m_chain.append(entry);
00221 }
00222 }
00223
00224 m_curpos = ProgramIsAt(m_cur_chanid, m_cur_startts);
00225 if (m_curpos < 0)
00226 m_curpos = 0;
00227
00228 if (m_switchid >= 0)
00229 m_switchid = ProgramIsAt(m_switchentry.chanid,m_switchentry.starttime);
00230
00231 if (prev_size!=m_chain.size())
00232 {
00233 VERBOSE(VB_PLAYBACK, LOC +
00234 "ReloadAll(): Added new recording");
00235 }
00236 }
00237
00238 void LiveTVChain::GetEntryAt(int at, LiveTVChainEntry &entry) const
00239 {
00240 QMutexLocker lock(&m_lock);
00241
00242 int size = m_chain.count();
00243 int new_at = (at < 0 || at >= size) ? size - 1 : at;
00244
00245 if (new_at >= 0 && new_at <= size)
00246 entry = m_chain[new_at];
00247 else
00248 {
00249 VERBOSE(VB_IMPORTANT, QString("GetEntryAt(%1) failed.").arg(at));
00250 entry.chanid = "0";
00251 entry.starttime.setTime_t(0);
00252 }
00253 }
00254
00255 ProgramInfo *LiveTVChain::EntryToProgram(const LiveTVChainEntry &entry)
00256 {
00257 ProgramInfo *pginfo;
00258 pginfo = ProgramInfo::GetProgramFromRecorded(entry.chanid,
00259 entry.starttime);
00260 if (pginfo)
00261 pginfo->pathname = entry.hostprefix + pginfo->pathname;
00262 else
00263 {
00264 VERBOSE(VB_IMPORTANT,
00265 QString("EntryToProgram(%1@%2) failed to get pginfo")
00266 .arg(entry.chanid).arg(entry.starttime.toString()));
00267 }
00268
00269 return pginfo;
00270 }
00271
00279 ProgramInfo *LiveTVChain::GetProgramAt(int at) const
00280 {
00281 LiveTVChainEntry entry;
00282 GetEntryAt(at, entry);
00283
00284 return EntryToProgram(entry);
00285 }
00286
00290 int LiveTVChain::ProgramIsAt(const QString &chanid,
00291 const QDateTime &starttime) const
00292 {
00293 QMutexLocker lock(&m_lock);
00294
00295 int count = 0;
00296 QValueList<LiveTVChainEntry>::const_iterator it;
00297 for (it = m_chain.begin(); it != m_chain.end(); ++it, ++count)
00298 {
00299 if ((*it).chanid == chanid &&
00300 (*it).starttime == starttime)
00301 {
00302 return count;
00303 }
00304 }
00305
00306 return -1;
00307 }
00308
00312 int LiveTVChain::ProgramIsAt(const ProgramInfo *pginfo) const
00313 {
00314 return ProgramIsAt(pginfo->chanid, pginfo->recstartts);
00315 }
00316
00320 int LiveTVChain::GetLengthAtCurPos(void)
00321 {
00322 QMutexLocker lock(&m_lock);
00323 LiveTVChainEntry entry;
00324
00325 entry = m_chain[m_curpos];
00326 if (m_curpos == ((int)m_chain.count() - 1))
00327 return entry.starttime.secsTo(QDateTime::currentDateTime());
00328 else
00329 return entry.starttime.secsTo(entry.endtime);
00330 }
00331
00332 int LiveTVChain::TotalSize(void) const
00333 {
00334 return m_chain.count();
00335 }
00336
00337 void LiveTVChain::SetProgram(ProgramInfo *pginfo)
00338 {
00339 if (!pginfo)
00340 return;
00341
00342 QMutexLocker lock(&m_lock);
00343
00344 m_cur_chanid = pginfo->chanid;
00345 m_cur_startts = pginfo->recstartts;
00346
00347 m_curpos = ProgramIsAt(pginfo);
00348 m_switchid = -1;
00349 }
00350
00351 bool LiveTVChain::HasNext(void) const
00352 {
00353 return ((int)m_chain.count() - 1 > m_curpos);
00354 }
00355
00356 void LiveTVChain::ClearSwitch(void)
00357 {
00358 QMutexLocker lock(&m_lock);
00359
00360 m_switchid = -1;
00361 m_jumppos = 0;
00362 }
00363
00374 ProgramInfo *LiveTVChain::GetSwitchProgram(bool &discont, bool &newtype,
00375 int &newid)
00376 {
00377 QMutexLocker lock(&m_lock);
00378
00379 if (m_switchid < 0 || m_curpos == m_switchid)
00380 return NULL;
00381
00382 LiveTVChainEntry oldentry, entry;
00383 GetEntryAt(m_curpos, oldentry);
00384
00385 ProgramInfo *pginfo = NULL;
00386 while (!pginfo && m_switchid < (int)m_chain.count() && m_switchid >= 0)
00387 {
00388 GetEntryAt(m_switchid, entry);
00389 pginfo = EntryToProgram(entry);
00390
00391 if (!pginfo)
00392 {
00393 if (m_switchid > m_curpos)
00394 m_switchid++;
00395 else
00396 m_switchid--;
00397 }
00398 }
00399
00400 if (!pginfo)
00401 return NULL;
00402
00403
00404 if (entry.cardtype == "DUMMY")
00405 {
00406 if (m_switchid > m_curpos && m_switchid + 1 < (int)m_chain.count())
00407 m_switchid++;
00408 else if (m_switchid < m_curpos && m_switchid > 0)
00409 m_switchid--;
00410
00411 GetEntryAt(m_switchid, entry);
00412 pginfo = EntryToProgram(entry);
00413 }
00414
00415 if (!pginfo)
00416 return NULL;
00417
00418 discont = true;
00419 if (m_curpos == m_switchid - 1)
00420 discont = entry.discontinuity;
00421
00422 newtype = (oldentry.cardtype != entry.cardtype);
00423
00424 if (discont)
00425 {
00426
00427
00428 newtype |= entry.cardtype == "DVB";
00429 newtype |= entry.cardtype == "HDTV";
00430 newtype |= entry.cardtype == "FIREWIRE";
00431 newtype |= entry.cardtype == "DBOX2";
00432 newtype |= entry.cardtype == "HDHOMERUN";
00433 }
00434
00435 newid = m_switchid;
00436 m_switchid = -1;
00437
00438 return pginfo;
00439 }
00440
00445 void LiveTVChain::SwitchTo(int num)
00446 {
00447 QMutexLocker lock(&m_lock);
00448
00449 VERBOSE(VB_PLAYBACK, LOC + "SwitchTo("<<num<<")");
00450
00451 int size = m_chain.count();
00452 if ((num < 0) || (num >= size))
00453 num = size - 1;
00454
00455 if (m_curpos != num)
00456 {
00457 m_switchid = num;
00458 GetEntryAt(num, m_switchentry);
00459 }
00460 else
00461 VERBOSE(VB_IMPORTANT, LOC + "SwitchTo() not switching to current");
00462
00463 if (print_verbose_messages & VB_PLAYBACK)
00464 {
00465 LiveTVChainEntry e;
00466 GetEntryAt(num, e);
00467 QString msg = QString("%1_%2")
00468 .arg(e.chanid).arg(e.starttime.toString("yyyyMMddhhmmss"));
00469 VERBOSE(VB_PLAYBACK, LOC + "Entry@"<<num<<": '"<<msg<<"'");
00470 }
00471 }
00472
00478 void LiveTVChain::SwitchToNext(bool up)
00479 {
00480
00481 if (up && HasNext())
00482 SwitchTo(m_curpos + 1);
00483 else if (!up && HasPrev())
00484 SwitchTo(m_curpos - 1);
00485 }
00486
00487 void LiveTVChain::JumpTo(int num, int pos)
00488 {
00489 m_jumppos = pos;
00490 SwitchTo(num);
00491 }
00492
00493 void LiveTVChain::JumpToNext(bool up, int pos)
00494 {
00495 m_jumppos = pos;
00496 SwitchToNext(up);
00497 }
00498
00502 int LiveTVChain::GetJumpPos(void)
00503 {
00504 int ret = m_jumppos;
00505 m_jumppos = 0;
00506 return ret;
00507 }
00508
00509 QString LiveTVChain::GetChannelName(int pos) const
00510 {
00511 LiveTVChainEntry entry;
00512 GetEntryAt(pos, entry);
00513
00514 return entry.channum;
00515 }
00516
00517 QString LiveTVChain::GetInputName(int pos) const
00518 {
00519 LiveTVChainEntry entry;
00520 GetEntryAt(pos, entry);
00521
00522 return entry.inputname;
00523 }
00524
00525 QString LiveTVChain::GetCardType(int pos) const
00526 {
00527 LiveTVChainEntry entry;
00528 GetEntryAt(pos, entry);
00529
00530 return entry.cardtype;
00531 }
00532
00533 void LiveTVChain::SetHostSocket(MythSocket *sock)
00534 {
00535 QMutexLocker lock(&m_sockLock);
00536
00537 if (!m_inUseSocks.containsRef(sock))
00538 m_inUseSocks.append(sock);
00539 }
00540
00541 bool LiveTVChain::IsHostSocket(MythSocket *sock)
00542 {
00543 QMutexLocker lock(&m_sockLock);
00544 return m_inUseSocks.containsRef(sock);
00545 }
00546
00547 int LiveTVChain::HostSocketCount(void)
00548 {
00549 return m_inUseSocks.count();
00550 }
00551
00552 void LiveTVChain::DelHostSocket(MythSocket *sock)
00553 {
00554 QMutexLocker lock(&m_sockLock);
00555 m_inUseSocks.removeRef(sock);
00556 }
00557