MythTV  0.26-pre
tvbrowsehelper.cpp
Go to the documentation of this file.
00001 #include <algorithm>
00002 using namespace std;
00003 
00004 #include "mythcorecontext.h"
00005 
00006 #include <QCoreApplication>
00007 
00008 #include "tvbrowsehelper.h"
00009 #include "playercontext.h"
00010 #include "remoteencoder.h"
00011 #include "recordinginfo.h"
00012 #include "mythplayer.h"
00013 #include "cardutil.h"
00014 #include "tv_play.h"
00015 #include "mythlogging.h"
00016 
00017 #define LOC      QString("BH: ")
00018 
00019 #define GetPlayer(X,Y) GetPlayerHaveLock(X, Y, __FILE__ , __LINE__)
00020 #define GetOSDLock(X) GetOSDL(X, __FILE__, __LINE__)
00021 
00022 static void format_time(int seconds, QString &tMin, QString &tHrsMin)
00023 {
00024     int minutes     = seconds / 60;
00025     int hours       = minutes / 60;
00026     int min         = minutes % 60;
00027 
00028     tMin = TV::tr("%n minute(s)", "", minutes);
00029     tHrsMin.sprintf("%d:%02d", hours, min);
00030 }
00031 
00032 TVBrowseHelper::TVBrowseHelper(
00033     TV      *tv,
00034     QString  time_format,
00035     QString  short_date_format,
00036     uint     browse_max_forward,
00037     bool     browse_all_tuners,
00038     bool     use_channel_groups,
00039     QString  db_channel_ordering) :
00040     MThread("TVBrowseHelper"),
00041     m_tv(tv),
00042     db_time_format(time_format),
00043     db_short_date_format(short_date_format),
00044     db_browse_max_forward(browse_max_forward),
00045     db_browse_all_tuners(browse_all_tuners),
00046     db_use_channel_groups(use_channel_groups),
00047     m_ctx(NULL),
00048     m_chanid(0),
00049     m_run(true)
00050 {
00051     db_all_channels = ChannelUtil::GetChannels(
00052         0, true, "channum, callsign");
00053     ChannelUtil::SortChannels(
00054         db_all_channels, db_channel_ordering, false);
00055 
00056     DBChanList::const_iterator it = db_all_channels.begin();
00057     for (; it != db_all_channels.end(); ++it)
00058     {
00059         db_chanid_to_channum[(*it).chanid] = (*it).channum;
00060         db_chanid_to_sourceid[(*it).chanid] = (*it).sourceid;
00061         db_channum_to_chanids.insert((*it).channum,(*it).chanid);
00062     }
00063 
00064     db_all_visible_channels = ChannelUtil::GetChannels(
00065         0, true, "channum, callsign");
00066     ChannelUtil::SortChannels(
00067         db_all_visible_channels, db_channel_ordering, false);
00068 
00069     start();
00070 }
00071 
00072 
00075 bool TVBrowseHelper::BrowseStart(PlayerContext *ctx, bool skip_browse)
00076 {
00077     if (!gCoreContext->IsUIThread())
00078         return false;
00079 
00080     QMutexLocker locker(&m_lock);
00081 
00082     if (m_ctx)
00083         return m_ctx == ctx;
00084 
00085     m_tv->ClearOSD(ctx);
00086 
00087     ctx->LockPlayingInfo(__FILE__, __LINE__);
00088     if (ctx->playingInfo)
00089     {
00090         m_ctx       = ctx;
00091         m_channum   = ctx->playingInfo->GetChanNum();
00092         m_chanid    = ctx->playingInfo->GetChanID();
00093         m_starttime = ctx->playingInfo->GetScheduledStartTime(ISODate);
00094         ctx->UnlockPlayingInfo(__FILE__, __LINE__);
00095 
00096         if (!skip_browse)
00097         {
00098             BrowseInfo bi(BROWSE_SAME, m_channum, m_chanid, m_starttime);
00099             locker.unlock();
00100             BrowseDispInfo(ctx, bi);
00101         }
00102         return true;
00103     }
00104     else
00105     {
00106         ctx->UnlockPlayingInfo(__FILE__, __LINE__);
00107         return false;
00108     }
00109 }
00110 
00117 void TVBrowseHelper::BrowseEnd(PlayerContext *ctx, bool change_channel)
00118 {
00119     if (!gCoreContext->IsUIThread())
00120         return;
00121 
00122     QMutexLocker locker(&m_lock);
00123 
00124     if (ctx && m_ctx != ctx)
00125         return;
00126 
00127     if (!m_ctx)
00128         return;
00129 
00130     {
00131         QMutexLocker locker(&m_tv->timerIdLock);
00132         if (m_tv->browseTimerId)
00133         {
00134             m_tv->KillTimer(m_tv->browseTimerId);
00135             m_tv->browseTimerId = 0;
00136         }
00137     }
00138 
00139     m_list.clear();
00140     m_wait.wakeAll();
00141 
00142     OSD *osd = m_tv->GetOSDLock(ctx);
00143     if (osd)
00144         osd->HideWindow("browse_info");
00145     m_tv->ReturnOSDLock(ctx, osd);
00146 
00147     if (change_channel)
00148         m_tv->ChangeChannel(ctx, 0, m_channum);
00149 
00150     m_ctx = NULL;
00151 }
00152 
00153 void TVBrowseHelper::BrowseDispInfo(PlayerContext *ctx, BrowseInfo &bi)
00154 {
00155     if (!gCoreContext->IsUIThread())
00156         return;
00157 
00158     if (!BrowseStart(ctx, true))
00159         return;
00160 
00161     {
00162         QMutexLocker locker(&m_tv->timerIdLock);
00163         if (m_tv->browseTimerId)
00164         {
00165             m_tv->KillTimer(m_tv->browseTimerId);
00166             m_tv->browseTimerId =
00167                 m_tv->StartTimer(TV::kBrowseTimeout, __LINE__);
00168         }
00169     }
00170 
00171     QMutexLocker locker(&m_lock);
00172     if (BROWSE_SAME == bi.m_dir)
00173         m_list.clear();
00174     m_list.push_back(bi);
00175     m_wait.wakeAll();
00176 }
00177 
00178 void TVBrowseHelper::BrowseChannel(PlayerContext *ctx, const QString &channum)
00179 {
00180     if (!gCoreContext->IsUIThread())
00181         return;
00182 
00183     if (db_browse_all_tuners)
00184     {
00185         BrowseInfo bi(channum, 0);
00186         BrowseDispInfo(ctx, bi);
00187         return;
00188     }
00189 
00190     if (!ctx->recorder || !ctx->last_cardid)
00191         return;
00192 
00193     QString inputname = ctx->recorder->GetInput();
00194     uint    inputid   = CardUtil::GetInputID(ctx->last_cardid, inputname);
00195     uint    sourceid  = CardUtil::GetSourceID(inputid);
00196     if (sourceid)
00197     {
00198         BrowseInfo bi(channum, sourceid);
00199         BrowseDispInfo(ctx, bi);
00200     }
00201 }
00202 
00203 BrowseInfo TVBrowseHelper::GetBrowsedInfo(void) const
00204 {
00205     QMutexLocker locker(&m_lock);
00206     BrowseInfo bi(BROWSE_SAME);
00207     if (m_ctx != NULL)
00208     {
00209         bi.m_channum   = m_channum;
00210         bi.m_chanid    = m_chanid;
00211         bi.m_starttime = m_starttime;
00212     }
00213     return bi;
00214 }
00215 
00219 bool TVBrowseHelper::IsBrowsing(void) const
00220 {
00221     if (!gCoreContext->IsUIThread())
00222         return true;
00223 
00224     return m_ctx != NULL;
00225 }
00226 
00234 uint TVBrowseHelper::GetChanId(
00235     const QString &channum, uint pref_cardid, uint pref_sourceid) const
00236 {
00237     if (pref_sourceid)
00238     {
00239         DBChanList::const_iterator it = db_all_channels.begin();
00240         for (; it != db_all_channels.end(); ++it)
00241         {
00242             if ((*it).sourceid == pref_sourceid && (*it).channum == channum)
00243                 return (*it).chanid;
00244         }
00245     }
00246 
00247     if (pref_cardid)
00248     {
00249         DBChanList::const_iterator it = db_all_channels.begin();
00250         for (; it != db_all_channels.end(); ++it)
00251         {
00252             if ((*it).cardid == pref_cardid && (*it).channum == channum)
00253                 return (*it).chanid;
00254         }
00255     }
00256 
00257     if (db_browse_all_tuners)
00258     {
00259         DBChanList::const_iterator it = db_all_channels.begin();
00260         for (; it != db_all_channels.end(); ++it)
00261         {
00262             if ((*it).channum == channum)
00263                 return (*it).chanid;
00264         }
00265     }
00266 
00267     return 0;
00268 }
00269 
00276 void TVBrowseHelper::GetNextProgram(
00277     BrowseDirection direction, InfoMap &infoMap) const
00278 {
00279     if (!m_ctx->recorder)
00280         return;
00281 
00282     QString title, subtitle, desc, category, endtime, callsign, iconpath;
00283     QDateTime begts, endts;
00284 
00285     QString starttime = infoMap["dbstarttime"];
00286     QString chanid    = infoMap["chanid"];
00287     QString channum   = infoMap["channum"];
00288     QString seriesid  = infoMap["seriesid"];
00289     QString programid = infoMap["programid"];
00290 
00291     m_ctx->recorder->GetNextProgram(
00292         direction,
00293         title,     subtitle,  desc,      category,
00294         starttime, endtime,   callsign,  iconpath,
00295         channum,   chanid,    seriesid,  programid);
00296 
00297     if (!starttime.isEmpty())
00298         begts = QDateTime::fromString(starttime, Qt::ISODate);
00299     else
00300         begts = QDateTime::fromString(infoMap["dbstarttime"], Qt::ISODate);
00301 
00302     infoMap["starttime"] = begts.toString(db_time_format);
00303     infoMap["startdate"] = begts.toString(db_short_date_format);
00304 
00305     infoMap["endtime"] = infoMap["enddate"] = "";
00306     if (!endtime.isEmpty())
00307     {
00308         endts = QDateTime::fromString(endtime, Qt::ISODate);
00309         infoMap["endtime"] = endts.toString(db_time_format);
00310         infoMap["enddate"] = endts.toString(db_short_date_format);
00311     }
00312 
00313     infoMap["lenmins"] = TV::tr("%n minute(s)", "", 0);
00314     infoMap["lentime"] = "0:00";
00315     if (begts.isValid() && endts.isValid())
00316     {
00317         QString lenM, lenHM;
00318         format_time(begts.secsTo(endts), lenM, lenHM);
00319         infoMap["lenmins"] = lenM;
00320         infoMap["lentime"] = lenHM;
00321     }
00322 
00323     infoMap["dbstarttime"] = starttime;
00324     infoMap["dbendtime"]   = endtime;
00325     infoMap["title"]       = title;
00326     infoMap["subtitle"]    = subtitle;
00327     infoMap["description"] = desc;
00328     infoMap["category"]    = category;
00329     infoMap["callsign"]    = callsign;
00330     infoMap["channum"]     = channum;
00331     infoMap["chanid"]      = chanid;
00332     infoMap["iconpath"]    = iconpath;
00333     infoMap["seriesid"]    = seriesid;
00334     infoMap["programid"]   = programid;
00335 }
00336 
00337 void TVBrowseHelper::GetNextProgramDB(
00338     BrowseDirection direction, InfoMap &infoMap) const
00339 {
00340     uint chanid = infoMap["chanid"].toUInt();
00341     if (!chanid)
00342     {
00343         LOG(VB_GENERAL, LOG_ERR, LOC + "GetNextProgramDB() requires a chanid");
00344         return;
00345     }
00346 
00347     int chandir = -1;
00348     switch (direction)
00349     {
00350         case BROWSE_UP:       chandir = CHANNEL_DIRECTION_UP;       break;
00351         case BROWSE_DOWN:     chandir = CHANNEL_DIRECTION_DOWN;     break;
00352         case BROWSE_FAVORITE: chandir = CHANNEL_DIRECTION_FAVORITE; break;
00353     }
00354     if (chandir != -1)
00355     {
00356         chanid = ChannelUtil::GetNextChannel(
00357             db_all_visible_channels, chanid, 0 /*mplexid_restriction*/,
00358             chandir, true /*skip non visible*/, true /*skip same callsign*/);
00359     }
00360 
00361     infoMap["chanid"]  = QString::number(chanid);
00362     infoMap["channum"] = db_chanid_to_channum[chanid];
00363 
00364     QDateTime nowtime = QDateTime::currentDateTime();
00365     QDateTime latesttime = nowtime.addSecs(6*60*60);
00366     QDateTime browsetime = QDateTime::fromString(
00367         infoMap["dbstarttime"], Qt::ISODate);
00368 
00369     MSqlBindings bindings;
00370     bindings[":CHANID"] = chanid;
00371     bindings[":NOWTS"] = nowtime;
00372     bindings[":LATESTTS"] = latesttime;
00373     bindings[":BROWSETS"] = browsetime;
00374     bindings[":BROWSETS2"] = browsetime;
00375 
00376     QString querystr = " WHERE program.chanid = :CHANID ";
00377     switch (direction)
00378     {
00379         case BROWSE_LEFT:
00380             querystr += " AND program.endtime <= :BROWSETS "
00381                 " AND program.endtime > :NOWTS ";
00382             break;
00383 
00384         case BROWSE_RIGHT:
00385             querystr += " AND program.starttime > :BROWSETS "
00386                 " AND program.starttime < :LATESTTS ";
00387             break;
00388 
00389         default:
00390             querystr += " AND program.starttime <= :BROWSETS "
00391                 " AND program.endtime > :BROWSETS2 ";
00392     };
00393 
00394     ProgramList progList;
00395     ProgramList dummySched;
00396     LoadFromProgram(progList, querystr, bindings, dummySched);
00397 
00398     if (progList.empty())
00399     {
00400         infoMap["dbstarttime"] = "";
00401         return;
00402     }
00403 
00404     const ProgramInfo *prog = (direction == BROWSE_LEFT) ?
00405         progList[progList.size() - 1] : progList[0];
00406 
00407     infoMap["dbstarttime"] = prog->GetScheduledStartTime(ISODate);
00408 }
00409 
00410 inline static QString toString(const InfoMap &infoMap, const QString sep="\n")
00411 {
00412     QString str("");
00413     InfoMap::const_iterator it = infoMap.begin();
00414     for (; it != infoMap.end() ; ++it)
00415         str += QString("[%1]:%2%3").arg(it.key()).arg(*it).arg(sep);
00416     return str;
00417 }
00418 
00419 void TVBrowseHelper::run()
00420 {
00421     RunProlog();
00422     QMutexLocker locker(&m_lock);
00423     while (true)
00424     {
00425         while (m_list.empty() && m_run)
00426             m_wait.wait(&m_lock);
00427 
00428         if (!m_run)
00429             break;
00430 
00431         BrowseInfo bi = m_list.front();
00432         m_list.pop_front();
00433 
00434         PlayerContext *ctx = m_ctx;
00435 
00436         vector<uint> chanids;
00437         if (BROWSE_SAME == bi.m_dir)
00438         {
00439             if (!bi.m_chanid)
00440             {
00441                 vector<uint> chanids_extra;
00442                 uint sourceid = db_chanid_to_sourceid[m_chanid];
00443                 QMultiMap<QString,uint>::iterator it;
00444                 it = db_channum_to_chanids.lowerBound(bi.m_channum);
00445                 for ( ; (it != db_channum_to_chanids.end()) &&
00446                           (it.key() == bi.m_channum); ++it)
00447                 {
00448                     if (db_chanid_to_sourceid[*it] == sourceid)
00449                         chanids.push_back(*it);
00450                     else
00451                         chanids_extra.push_back(*it);
00452                 }
00453                 chanids.insert(chanids.end(),
00454                                chanids_extra.begin(),
00455                                chanids_extra.end());
00456             }
00457             m_channum   = bi.m_channum;
00458             m_chanid    = (chanids.empty()) ? bi.m_chanid : chanids[0];
00459             m_starttime = bi.m_starttime;
00460         }
00461 
00462         BrowseDirection direction = bi.m_dir;
00463 
00464         QDateTime lasttime = QDateTime::fromString(
00465             m_starttime, Qt::ISODate);
00466         QDateTime curtime  = QDateTime::currentDateTime();
00467         if (lasttime < curtime)
00468             m_starttime = curtime.toString(Qt::ISODate);
00469 
00470         QDateTime maxtime  = curtime.addSecs(db_browse_max_forward);
00471         if ((lasttime > maxtime) && (direction == BROWSE_RIGHT))
00472             continue;
00473 
00474         m_lock.unlock();
00475 
00476         // if browsing channel groups is enabled or
00477         // direction if BROWSE_FAVORITES
00478         // Then pick the next channel in the channel group list to browse
00479         // If channel group is ALL CHANNELS (-1), then bypass picking from
00480         // the channel group list
00481         if ((db_use_channel_groups || (direction == BROWSE_FAVORITE)) &&
00482             (direction != BROWSE_RIGHT) && (direction != BROWSE_LEFT) &&
00483             (direction != BROWSE_SAME))
00484         {
00485             m_tv->channelGroupLock.lock();
00486             if (m_tv->channelGroupId > -1)
00487             {
00488                 int dir = direction;
00489                 if ((direction == BROWSE_UP) || (direction == BROWSE_FAVORITE))
00490                     dir = CHANNEL_DIRECTION_UP;
00491                 else if (direction == BROWSE_DOWN)
00492                     dir = CHANNEL_DIRECTION_DOWN;
00493 
00494                 uint chanid = ChannelUtil::GetNextChannel(
00495                     m_tv->channelGroupChannelList, m_chanid, 0, dir);
00496                 direction = BROWSE_SAME;
00497 
00498                 m_tv->channelGroupLock.unlock();
00499 
00500                 m_lock.lock();
00501                 m_chanid  = chanid;
00502                 m_channum = QString::null;
00503                 m_lock.unlock();
00504             }
00505             else
00506                 m_tv->channelGroupLock.unlock();
00507         }
00508 
00509         if (direction == BROWSE_FAVORITE)
00510             direction = BROWSE_UP;
00511 
00512         InfoMap infoMap;
00513         infoMap["dbstarttime"] = m_starttime;
00514         infoMap["channum"]     = m_channum;
00515         infoMap["chanid"]      = QString::number(m_chanid);
00516 
00517         m_tv->GetPlayerReadLock(0,__FILE__,__LINE__);
00518         bool still_there = false;
00519         for (uint i = 0; i < m_tv->player.size() && !still_there; i++)
00520             still_there |= (ctx == m_tv->player[i]);
00521         if (still_there)
00522         {
00523             if (!db_browse_all_tuners)
00524             {
00525                 GetNextProgram(direction, infoMap);
00526             }
00527             else
00528             {
00529                 if (!chanids.empty())
00530                 {
00531                     for (uint i = 0; i < chanids.size(); i++)
00532                     {
00533                         if (m_tv->IsTunable(ctx, chanids[i]))
00534                         {
00535                             infoMap["chanid"] = QString::number(chanids[i]);
00536                             GetNextProgramDB(direction, infoMap);
00537                             break;
00538                         }
00539                     }
00540                 }
00541                 else
00542                 {
00543                     uint orig_chanid = infoMap["chanid"].toUInt();
00544                     GetNextProgramDB(direction, infoMap);
00545                     while (!m_tv->IsTunable(ctx, infoMap["chanid"].toUInt()) &&
00546                            (infoMap["chanid"].toUInt() != orig_chanid))
00547                     {
00548                         GetNextProgramDB(direction, infoMap);
00549                     }
00550                 }
00551             }
00552         }
00553         m_tv->ReturnPlayerLock(ctx);
00554 
00555         m_lock.lock();
00556         if (!m_ctx && !still_there)
00557             continue;
00558 
00559         m_channum = infoMap["channum"];
00560         m_chanid  = infoMap["chanid"].toUInt();
00561 
00562         if (((direction == BROWSE_LEFT) || (direction == BROWSE_RIGHT)) &&
00563             !infoMap["dbstarttime"].isEmpty())
00564         {
00565             m_starttime = infoMap["dbstarttime"];
00566         }
00567 
00568         if (!m_list.empty())
00569         {
00570             // send partial info to UI for appearance of responsiveness
00571             QCoreApplication::postEvent(
00572                 m_tv, new UpdateBrowseInfoEvent(infoMap));
00573             continue;
00574         }
00575         m_lock.unlock();
00576 
00577         // pull in additional data from the DB...
00578         QDateTime startts = QDateTime::fromString(
00579             m_starttime, Qt::ISODate);
00580         RecordingInfo recinfo(m_chanid, startts, false);
00581         recinfo.ToMap(infoMap);
00582         infoMap["iconpath"] = ChannelUtil::GetIcon(recinfo.GetChanID());
00583 
00584         m_lock.lock();
00585         if (m_ctx)
00586         {
00587             QCoreApplication::postEvent(
00588                 m_tv, new UpdateBrowseInfoEvent(infoMap));
00589         }
00590     }
00591     RunEpilog();
00592 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends