MythTV  master
tvbrowsehelper.cpp
Go to the documentation of this file.
1 
2 #include "mythcorecontext.h"
3 
4 #include <QCoreApplication>
5 
6 #include "tvbrowsehelper.h"
7 #include "playercontext.h"
8 #include "remoteencoder.h"
9 #include "recordinginfo.h"
10 #include "channelutil.h"
11 #include "mythlogging.h"
12 #include "cardutil.h"
13 #include "tv_play.h"
14 
15 #define LOC QString("BH: ")
16 
17 #define GetPlayer(X,Y) GetPlayerHaveLock(X, Y, __FILE__ , __LINE__)
18 #define GetOSDLock(X) GetOSDL(X, __FILE__, __LINE__)
19 
20 static void format_time(int seconds, QString &tMin, QString &tHrsMin)
21 {
22  int minutes = seconds / 60;
23  int hours = minutes / 60;
24  int min = minutes % 60;
25 
26  tMin = TV::tr("%n minute(s)", "", minutes);
27  tHrsMin = QString("%1:%2").arg(hours).arg(min, 2, 10, QChar('0'));
28 }
29 
31  TV *tv,
32  uint browse_max_forward,
33  bool browse_all_tuners,
34  bool use_channel_groups,
35  const QString& db_channel_ordering) :
36  MThread("TVBrowseHelper"),
37  m_tv(tv),
38  m_dbBrowseMaxForward(browse_max_forward),
39  m_dbBrowseAllTuners(browse_all_tuners),
40  m_dbUseChannelGroups(use_channel_groups)
41 {
43  0, true, "channum, callsign");
45  m_dbAllChannels, db_channel_ordering, false);
46 
47  ChannelInfoList::const_iterator it = m_dbAllChannels.begin();
48  for (; it != m_dbAllChannels.end(); ++it)
49  {
50  m_dbChanidToChannum[(*it).m_chanid] = (*it).m_channum;
51  m_dbChanidToSourceid[(*it).m_chanid] = (*it).m_sourceid;
52  m_dbChannumToChanids.insert((*it).m_channum,(*it).m_chanid);
53  }
54 
56  0, true, "channum, callsign");
58  m_dbAllVisibleChannels, db_channel_ordering, false);
59 
60  start();
61 }
62 
63 
66 bool TVBrowseHelper::BrowseStart(PlayerContext *ctx, bool skip_browse)
67 {
68  if (!gCoreContext->IsUIThread())
69  return false;
70 
71  QMutexLocker locker(&m_lock);
72 
73  if (m_ctx)
74  return m_ctx == ctx;
75 
76  m_tv->ClearOSD(ctx);
77 
78  ctx->LockPlayingInfo(__FILE__, __LINE__);
79  if (ctx->m_playingInfo)
80  {
81  m_ctx = ctx;
85  ctx->UnlockPlayingInfo(__FILE__, __LINE__);
86 
87  if (!skip_browse)
88  {
90  locker.unlock();
91  BrowseDispInfo(ctx, bi);
92  }
93  return true;
94  }
95  ctx->UnlockPlayingInfo(__FILE__, __LINE__);
96  return false;
97 }
98 
105 void TVBrowseHelper::BrowseEnd(PlayerContext *ctx, bool change_channel)
106 {
107  if (!gCoreContext->IsUIThread())
108  return;
109 
110  QMutexLocker locker(&m_lock);
111 
112  if (ctx && m_ctx != ctx)
113  return;
114 
115  if (!m_ctx)
116  return;
117 
118  {
119  QMutexLocker locker2(&m_tv->m_timerIdLock);
120  if (m_tv->m_browseTimerId)
121  {
123  m_tv->m_browseTimerId = 0;
124  }
125  }
126 
127  m_list.clear();
128  m_wait.wakeAll();
129 
130  OSD *osd = m_tv->GetOSDLock(ctx);
131  if (osd)
132  osd->HideWindow("browse_info");
133  m_tv->ReturnOSDLock(ctx, osd);
134 
135  if (change_channel)
136  m_tv->ChangeChannel(ctx, 0, m_channum);
137 
138  m_ctx = nullptr;
139 }
140 
142 {
143  if (!gCoreContext->IsUIThread())
144  return;
145 
146  if (!BrowseStart(ctx, true))
147  return;
148 
149  {
150  QMutexLocker locker(&m_tv->m_timerIdLock);
151  if (m_tv->m_browseTimerId)
152  {
155  m_tv->StartTimer(TV::kBrowseTimeout, __LINE__);
156  }
157  }
158 
159  QMutexLocker locker(&m_lock);
160  if (BROWSE_SAME == bi.m_dir)
161  m_list.clear();
162  m_list.push_back(bi);
163  m_wait.wakeAll();
164 }
165 
166 void TVBrowseHelper::BrowseChannel(PlayerContext *ctx, const QString &channum)
167 {
168  if (!gCoreContext->IsUIThread())
169  return;
170 
172  {
173  BrowseInfo bi(channum, 0);
174  BrowseDispInfo(ctx, bi);
175  return;
176  }
177 
178  if (!ctx->m_recorder || !ctx->m_lastCardid)
179  return;
180 
181  uint inputid = ctx->m_lastCardid;
182  uint sourceid = CardUtil::GetSourceID(inputid);
183  if (sourceid)
184  {
185  BrowseInfo bi(channum, sourceid);
186  BrowseDispInfo(ctx, bi);
187  }
188 }
189 
191 {
192  QMutexLocker locker(&m_lock);
194  if (m_ctx != nullptr)
195  {
196  bi.m_channum = m_channum;
197  bi.m_chanid = m_chanid;
199  }
200  return bi;
201 }
202 
207 {
208  if (!gCoreContext->IsUIThread())
209  return true;
210 
211  return m_ctx != nullptr;
212 }
213 
222  const QString &channum, uint pref_cardid, uint pref_sourceid) const
223 {
224  if (pref_sourceid)
225  {
226  auto it = m_dbAllChannels.cbegin();
227  for (; it != m_dbAllChannels.cend(); ++it)
228  {
229  if ((*it).m_sourceid == pref_sourceid && (*it).m_channum == channum)
230  return (*it).m_chanid;
231  }
232  }
233 
234  if (pref_cardid)
235  {
236  auto it = m_dbAllChannels.cbegin();
237  for (; it != m_dbAllChannels.cend(); ++it)
238  {
239  if ((*it).GetInputIds().contains(pref_cardid) &&
240  (*it).m_channum == channum)
241  return (*it).m_chanid;
242  }
243  }
244 
246  {
247  auto it = m_dbAllChannels.cbegin();
248  for (; it != m_dbAllChannels.cend(); ++it)
249  {
250  if ((*it).m_channum == channum)
251  return (*it).m_chanid;
252  }
253  }
254 
255  return 0;
256 }
257 
264  BrowseDirection direction, InfoMap &infoMap) const
265 {
266  if (!m_ctx || !m_ctx->m_recorder)
267  return;
268 
269  QString title;
270  QString subtitle;
271  QString desc;
272  QString category;
273  QString endtime;
274  QString callsign;
275  QString iconpath;
276  QDateTime begts;
277  QDateTime endts;
278 
279  QString starttime = infoMap["dbstarttime"];
280  QString chanid = infoMap["chanid"];
281  QString channum = infoMap["channum"];
282  QString seriesid = infoMap["seriesid"];
283  QString programid = infoMap["programid"];
284 
286  direction,
287  title, subtitle, desc, category,
288  starttime, endtime, callsign, iconpath,
289  channum, chanid, seriesid, programid);
290 
291  if (!starttime.isEmpty())
292  begts = MythDate::fromString(starttime);
293  else
294  begts = MythDate::fromString(infoMap["dbstarttime"]);
295 
296  infoMap["starttime"] = MythDate::toString(begts, MythDate::kTime);
297  infoMap["startdate"] = MythDate::toString(
299 
300  infoMap["endtime"] = infoMap["enddate"] = "";
301  if (!endtime.isEmpty())
302  {
303  endts = MythDate::fromString(endtime);
304  infoMap["endtime"] = MythDate::toString(endts, MythDate::kTime);
305  infoMap["enddate"] = MythDate::toString(
307  }
308 
309  infoMap["lenmins"] = TV::tr("%n minute(s)", "", 0);
310  infoMap["lentime"] = "0:00";
311  if (begts.isValid() && endts.isValid())
312  {
313  QString lenM;
314  QString lenHM;
315  format_time(begts.secsTo(endts), lenM, lenHM);
316  infoMap["lenmins"] = lenM;
317  infoMap["lentime"] = lenHM;
318  }
319 
320  infoMap["dbstarttime"] = starttime;
321  infoMap["dbendtime"] = endtime;
322  infoMap["title"] = title;
323  infoMap["subtitle"] = subtitle;
324  infoMap["description"] = desc;
325  infoMap["category"] = category;
326  infoMap["callsign"] = callsign;
327  infoMap["channum"] = channum;
328  infoMap["chanid"] = chanid;
329  infoMap["iconpath"] = iconpath;
330  infoMap["seriesid"] = seriesid;
331  infoMap["programid"] = programid;
332 }
333 
335  BrowseDirection direction, InfoMap &infoMap) const
336 {
337  uint chanid = infoMap["chanid"].toUInt();
338  if (!chanid)
339  {
340  LOG(VB_GENERAL, LOG_ERR, LOC + "GetNextProgramDB() requires a chanid");
341  return;
342  }
343 
344  int chandir = -1;
345  switch (direction)
346  {
347  case BROWSE_UP: chandir = CHANNEL_DIRECTION_UP; break;
348  case BROWSE_DOWN: chandir = CHANNEL_DIRECTION_DOWN; break;
349  case BROWSE_FAVORITE: chandir = CHANNEL_DIRECTION_FAVORITE; break;
350  case BROWSE_SAME:
351  case BROWSE_LEFT:
352  case BROWSE_RIGHT:
353  case BROWSE_INVALID:
354  default: break; // quiet -Wswitch-enum
355  }
356  if (chandir != -1)
357  {
359  m_dbAllVisibleChannels, chanid, 0 /*mplexid_restriction*/,
360  0 /* chanid restriction */,
361  static_cast<ChannelChangeDirection>(chandir),
362  true /*skip non visible*/, true /*skip same callsign*/);
363  }
364 
365  infoMap["chanid"] = QString::number(chanid);
366  infoMap["channum"] = m_dbChanidToChannum[chanid];
367 
368  QDateTime nowtime = MythDate::current();
369  QDateTime latesttime = nowtime.addSecs(6*60*60);
370  QDateTime browsetime = MythDate::fromString(infoMap["dbstarttime"]);
371 
372  MSqlBindings bindings;
373  bindings[":CHANID"] = chanid;
374  bindings[":NOWTS"] = nowtime;
375  bindings[":LATESTTS"] = latesttime;
376  bindings[":BROWSETS"] = browsetime;
377  bindings[":BROWSETS2"] = browsetime;
378 
379  QString querystr = " WHERE program.chanid = :CHANID ";
380  switch (direction)
381  {
382  case BROWSE_LEFT:
383  querystr += " AND program.endtime <= :BROWSETS "
384  " AND program.endtime > :NOWTS ";
385  break;
386 
387  case BROWSE_RIGHT:
388  querystr += " AND program.starttime > :BROWSETS "
389  " AND program.starttime < :LATESTTS ";
390  break;
391 
392  default:
393  querystr += " AND program.starttime <= :BROWSETS "
394  " AND program.endtime > :BROWSETS2 ";
395  };
396 
397  ProgramList progList;
398  ProgramList dummySched;
399  LoadFromProgram(progList, querystr, bindings, dummySched);
400 
401  if (progList.empty())
402  {
403  infoMap["dbstarttime"] = "";
404  return;
405  }
406 
407  const ProgramInfo *prog = (direction == BROWSE_LEFT) ?
408  progList[progList.size() - 1] : progList[0];
409 
410  infoMap["dbstarttime"] = prog->GetScheduledStartTime(MythDate::ISODate);
411 }
412 
414 {
415  RunProlog();
416  QMutexLocker locker(&m_lock);
417  while (true)
418  {
419  while (m_list.empty() && m_run)
420  m_wait.wait(&m_lock);
421 
422  if (!m_run)
423  break;
424 
425  BrowseInfo bi = m_list.front();
426  m_list.pop_front();
427 
428  PlayerContext *ctx = m_ctx;
429 
430  vector<uint> chanids;
431  if (BROWSE_SAME == bi.m_dir)
432  {
433  if (!bi.m_chanid)
434  {
435  vector<uint> chanids_extra;
436  uint sourceid = m_dbChanidToSourceid[m_chanid];
437  QMultiMap<QString,uint>::iterator it;
438  it = m_dbChannumToChanids.lowerBound(bi.m_channum);
439  for ( ; (it != m_dbChannumToChanids.end()) &&
440  (it.key() == bi.m_channum); ++it)
441  {
442  if (m_dbChanidToSourceid[*it] == sourceid)
443  chanids.push_back(*it);
444  else
445  chanids_extra.push_back(*it);
446  }
447  chanids.insert(chanids.end(),
448  chanids_extra.begin(),
449  chanids_extra.end());
450  }
451  m_channum = bi.m_channum;
452  m_chanid = (chanids.empty()) ? bi.m_chanid : chanids[0];
454  }
455 
456  BrowseDirection direction = bi.m_dir;
457 
458  QDateTime lasttime = MythDate::fromString(m_starttime);
459  QDateTime curtime = MythDate::current();
460  if (lasttime < curtime)
461  m_starttime = curtime.toString(Qt::ISODate);
462 
463  QDateTime maxtime = curtime.addSecs(m_dbBrowseMaxForward);
464  if ((lasttime > maxtime) && (direction == BROWSE_RIGHT))
465  continue;
466 
467  m_lock.unlock();
468 
469  // if browsing channel groups is enabled or
470  // direction if BROWSE_FAVORITES
471  // Then pick the next channel in the channel group list to browse
472  // If channel group is ALL CHANNELS (-1), then bypass picking from
473  // the channel group list
474  if ((m_dbUseChannelGroups || (direction == BROWSE_FAVORITE)) &&
475  (direction != BROWSE_RIGHT) && (direction != BROWSE_LEFT) &&
476  (direction != BROWSE_SAME))
477  {
478  m_tv->m_channelGroupLock.lock();
479  if (m_tv->m_channelGroupId > -1)
480  {
482  if ((direction == BROWSE_UP) || (direction == BROWSE_FAVORITE))
483  dir = CHANNEL_DIRECTION_UP;
484  else if (direction == BROWSE_DOWN)
486 
489  direction = BROWSE_SAME;
490 
491  m_tv->m_channelGroupLock.unlock();
492 
493  m_lock.lock();
494  m_chanid = chanid;
495  m_channum.clear();
496  m_lock.unlock();
497  }
498  else
499  m_tv->m_channelGroupLock.unlock();
500  }
501 
502  if (direction == BROWSE_FAVORITE)
503  direction = BROWSE_UP;
504 
505  InfoMap infoMap;
506  infoMap["dbstarttime"] = m_starttime;
507  infoMap["channum"] = m_channum;
508  infoMap["chanid"] = QString::number(m_chanid);
509 
510  m_tv->GetPlayerReadLock(0,__FILE__,__LINE__);
511  bool still_there = false;
512  for (uint i = 0; i < m_tv->m_player.size() && !still_there; i++)
513  still_there |= (ctx == m_tv->m_player[i]);
514  if (still_there)
515  {
516  if (!m_dbBrowseAllTuners)
517  {
518  GetNextProgram(direction, infoMap);
519  }
520  else
521  {
522  if (!chanids.empty())
523  {
524  for (size_t i = 0; i < chanids.size(); i++)
525  {
526  if (TV::IsTunable(ctx, chanids[i]))
527  {
528  infoMap["chanid"] = QString::number(chanids[i]);
529  GetNextProgramDB(direction, infoMap);
530  break;
531  }
532  }
533  }
534  else
535  {
536  uint orig_chanid = infoMap["chanid"].toUInt();
537  GetNextProgramDB(direction, infoMap);
538  while (!TV::IsTunable(ctx, infoMap["chanid"].toUInt()) &&
539  (infoMap["chanid"].toUInt() != orig_chanid))
540  {
541  GetNextProgramDB(direction, infoMap);
542  }
543  }
544  }
545  }
546  m_tv->ReturnPlayerLock(ctx);
547 
548  m_lock.lock();
549  if (!m_ctx && !still_there)
550  continue;
551 
552  m_channum = infoMap["channum"];
553  m_chanid = infoMap["chanid"].toUInt();
554 
555  if (((direction == BROWSE_LEFT) || (direction == BROWSE_RIGHT)) &&
556  !infoMap["dbstarttime"].isEmpty())
557  {
558  m_starttime = infoMap["dbstarttime"];
559  }
560 
561  if (!m_list.empty())
562  {
563  // send partial info to UI for appearance of responsiveness
564  QCoreApplication::postEvent(
565  m_tv, new UpdateBrowseInfoEvent(infoMap));
566  continue;
567  }
568  m_lock.unlock();
569 
570  // pull in additional data from the DB...
572  infoMap["channelgroup"] = ChannelGroup::GetChannelGroupName(m_tv->m_channelGroupId);
573  else
574  infoMap["channelgroup"] = QObject::tr("All channels");
575 
576  QDateTime startts = MythDate::fromString(m_starttime);
577  RecordingInfo recinfo(m_chanid, startts, false);
578  recinfo.ToMap(infoMap);
579  infoMap["iconpath"] = ChannelUtil::GetIcon(recinfo.GetChanID());
580 
581  m_lock.lock();
582  if (m_ctx)
583  {
584  QCoreApplication::postEvent(
585  m_tv, new UpdateBrowseInfoEvent(infoMap));
586  }
587  }
588  RunEpilog();
589 }
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
QHash< uint, QString > m_dbChanidToChannum
void BrowseDispInfo(PlayerContext *ctx, BrowseInfo &bi)
QString m_channum
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
QHash< uint, uint > m_dbChanidToSourceid
Fetch browse information on current channel and time.
Definition: tv.h:40
void GetNextProgramDB(BrowseDirection direction, InfoMap &infoMap) const
virtual void ToMap(InfoMap &progMap, bool showrerecord=false, uint star_range=10) const
Converts ProgramInfo into QString QHash containing each field in ProgramInfo converted into localized...
ChannelChangeDirection
ChannelChangeDirection is an enumeration of possible channel changing directions.
Definition: tv.h:28
static bool IsTunable(uint chanid)
Definition: tv_play.cpp:8404
void GetNextProgram(int direction, QString &title, QString &subtitle, QString &desc, QString &category, QString &starttime, QString &endtime, QString &callsign, QString &iconpath, QString &channelname, QString &chanid, QString &seriesid, QString &programid)
Returns information about the program that would be seen if we changed the channel using ChangeChanne...
vector< PlayerContext * > m_player
Definition: tv_play.h:899
void ReturnPlayerLock(PlayerContext *&)
Definition: tv_play.cpp:13283
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
Fetch information on current channel in the past.
Definition: tv.h:43
BrowseDirection m_dir
void KillTimer(int id)
Definition: tv_play.cpp:3171
void UnlockPlayingInfo(const char *file, int line) const
void GetNextProgram(BrowseDirection direction, InfoMap &infoMap) const
Fetches information on the desired program from the backend.
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:34
PlayerContext * GetPlayerReadLock(int which, const char *file, int location)
Definition: tv_play.cpp:13220
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
size_t size(void) const
QMutex m_channelGroupLock
Lock necessary when modifying channel group variables.
Definition: tv_play.h:939
Do Today/Yesterday/Tomorrow transform.
Definition: mythdate.h:23
QString GetChanNum(void) const
This is the channel "number", in the form 1, 1_2, 1-2, 1#1, etc.
Definition: programinfo.h:368
static void SortChannels(ChannelInfoList &list, const QString &order, bool eliminate_duplicates=false)
void ReturnOSDLock(const PlayerContext *, OSD *&)
Definition: tv_play.cpp:13194
bool IsBrowsing(void) const
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:382
ChannelInfoList m_channelGroupChannelList
Definition: tv_play.h:941
ProgramInfo * m_playingInfo
Currently playing info.
Holds information on recordings and videos.
Definition: programinfo.h:66
QMap< QString, QVariant > MSqlBindings
typedef for a map of string -> string bindings for generic queries.
Definition: mythdbcon.h:98
QMultiMap< QString, uint > m_dbChannumToChanids
void HideWindow(const QString &Window)
Definition: osd.cpp:913
bool BrowseStart(PlayerContext *ctx, bool skip_browse=false)
Begins channel browsing.
BrowseInfo GetBrowsedInfo(void) const
bool empty(void) const
static void format_time(int seconds, QString &tMin, QString &tHrsMin)
static const uint kBrowseTimeout
Timeout for browse mode exit in msec.
Definition: tv_play.h:1067
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
Default local time.
Definition: mythdate.h:16
void LockPlayingInfo(const char *file, int line) const
QString m_starttime
static ChannelInfoList GetChannels(uint sourceid, bool visible_only, const QString &group_by=QString(), uint channel_groupid=0)
Definition: channelutil.h:237
int m_lastCardid
CardID of current/last recorder.
static QString GetIcon(uint chanid)
Control TV playback.
Definition: tv_play.h:279
unsigned int uint
Definition: compat.h:140
void BrowseChannel(PlayerContext *ctx, const QString &channum)
static QString GetChannelGroupName(int grpid)
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:101
QWaitCondition m_wait
ChannelInfoList m_dbAllVisibleChannels
RemoteEncoder * m_recorder
void BrowseEnd(PlayerContext *ctx, bool change_channel)
Ends channel browsing.
QMutex m_timerIdLock
Definition: tv_play.h:949
volatile int m_channelGroupId
Definition: tv_play.h:940
BrowseDirection
Used to request ProgramInfo for channel browsing.
Definition: tv.h:37
Fetch information on the next favorite channel.
Definition: tv.h:45
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
Fetch information on next channel.
Definition: tv.h:42
#define LOC
volatile int m_browseTimerId
Definition: tv_play.h:959
TVBrowseHelper(TV *tv, uint browse_max_forward, bool browse_all_tuners, bool use_channel_groups, const QString &db_channel_ordering)
bool LoadFromProgram(ProgramList &destination, const QString &where, const QString &groupBy, const QString &orderBy, const MSqlBindings &bindings, const ProgramList &schedList)
Fetch information on previous channel.
Definition: tv.h:41
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:364
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:202
ChannelInfoList m_dbAllChannels
Definition: osd.h:131
QList< BrowseInfo > m_list
uint GetChanId(const QString &channum, uint pref_cardid, uint pref_sourceid) const
Returns a chanid for the channum, or 0 if none is available.
static uint GetSourceID(uint inputid)
Definition: cardutil.cpp:1775
Default UTC.
Definition: mythdate.h:14
void ChangeChannel(const PlayerContext *, const ChannelInfoList &options)
Definition: tv_play.cpp:7786
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
int StartTimer(int interval, int line)
Definition: tv_play.cpp:3159
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:30
Fetch information on current channel in the future.
Definition: tv.h:44
bool ClearOSD(const PlayerContext *)
Definition: tv_play.cpp:7854
PlayerContext * m_ctx
Default local time.
Definition: mythdate.h:19
static uint GetNextChannel(const ChannelInfoList &sorted, uint old_chanid, uint mplexid_restriction, uint chanid_restriction, ChannelChangeDirection direction, bool skip_non_visible=true, bool skip_same_channum_and_callsign=false)