MythTV  master
remoteencoder.cpp
Go to the documentation of this file.
1 #include <unistd.h>
2 
3 #include <QStringList>
4 
5 #include "remoteencoder.h"
6 #include "programinfo.h"
7 #include "mythdate.h"
8 #include "mythcorecontext.h"
9 #include "signalmonitor.h"
10 #include "mythdb.h"
11 #include "mythsocket.h"
12 #include "mythlogging.h"
13 
14 using namespace std;
15 
16 #define LOC QString("RemoteEncoder(%1): ").arg(m_recordernum)
17 
18 #define MAX_SIZE_CHECK 500 // in ms
19 
21 {
22  if (m_controlSock)
23  {
24  m_controlSock->DecrRef();
25  m_controlSock = nullptr;
26  }
27 }
28 
30 {
31  if (!m_controlSock)
32  {
33  LOG(VB_NETWORK, LOG_DEBUG, "RemoteEncoder::Setup(): Connecting...");
34 
35  QString ann = QString("ANN Playback %1 %2")
36  .arg(gCoreContext->GetHostName()).arg(false);
37 
38  m_controlSock = gCoreContext->ConnectCommandSocket(
39  m_remotehost, m_remoteport, ann);
40 
41  if (m_controlSock)
42  {
43  LOG(VB_NETWORK, LOG_DEBUG, "RemoteEncoder::Setup(): Connected");
44  }
45  else
46  {
47  LOG(VB_GENERAL, LOG_ERR,
48  "RemoteEncoder::Setup(): Failed to connect to backend");
49  }
50  }
51  else
52  {
53  LOG(VB_NETWORK, LOG_DEBUG, "RemoteEncoder::Setup(): Already connected");
54  }
55  return m_controlSock;
56 }
57 
59 {
60  return (m_recordernum >= 0);
61 }
62 
64 {
65  return m_recordernum;
66 }
67 
69  QStringList &strlist, uint min_reply_length)
70 {
71  QMutexLocker locker(&m_lock);
72  if (!m_controlSock)
73  Setup();
74 
75  m_backendError = false;
76 
77  if (!m_controlSock)
78  {
79  LOG(VB_GENERAL, LOG_ERR, "RemoteEncoder::SendReceiveStringList(): "
80  "Failed to reconnect with backend.");
81  m_backendError = true;
82  return false;
83  }
84 
85  if (!m_controlSock->WriteStringList(strlist))
86  {
87  LOG(VB_GENERAL, LOG_ERR, "RemoteEncoder::SendReceiveStringList(): "
88  "Failed to write data.");
89  m_backendError = true;
90  }
91 
92  if (!m_backendError &&
93  !m_controlSock->ReadStringList(strlist, MythSocket::kShortTimeout))
94  {
95  LOG(VB_GENERAL, LOG_ERR,
96  "RemoteEncoder::SendReceiveStringList(): No response.");
97  m_backendError = true;
98  }
99 
100  if (!m_backendError &&
101  min_reply_length && ((uint)strlist.size() < min_reply_length))
102  {
103  LOG(VB_GENERAL, LOG_ERR,
104  "RemoteEncoder::SendReceiveStringList(): Response too short");
105  m_backendError = true;
106  }
107 
108  if (m_backendError)
109  {
110  m_controlSock->DecrRef();
111  m_controlSock = nullptr;
112  return false;
113  }
114 
115  return true;
116 }
117 
119 {
120  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
121  strlist << "IS_RECORDING";
122 
123  bool ret = SendReceiveStringList(strlist, 1);
124  if (!ret)
125  {
126  if (ok)
127  *ok = false;
128 
129  return false;
130  }
131 
132  if (ok)
133  *ok = true;
134 
135  return strlist[0].toInt() != 0;
136 }
137 
139 {
140  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
141  strlist << "GET_RECORDING";
142 
143  if (SendReceiveStringList(strlist))
144  {
145  auto *proginfo = new ProgramInfo(strlist);
146  if (proginfo->GetChanID())
147  return proginfo;
148  delete proginfo;
149  }
150 
151  return nullptr;
152 }
153 
161 {
162  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum));
163  strlist << "GET_FRAMERATE";
164 
165  bool ok = false;
166  float retval = 30.0F;
167 
168  if (SendReceiveStringList(strlist, 1))
169  {
170  retval = strlist[0].toFloat(&ok);
171 
172  if (!ok)
173  {
174  LOG(VB_GENERAL, LOG_ERR, LOC +
175  QString("GetFrameRate() failed to parse response '%1'")
176  .arg(strlist[0]));
177  }
178  }
179  else
180  {
181  LOG(VB_GENERAL, LOG_ERR, LOC +
182  "GetFrameRate(): SendReceiveStringList() failed");
183  }
184 
185  return (ok) ? retval : 30.0F;
186 }
187 
196 {
197  if (m_lastTimeCheck.isRunning() && m_lastTimeCheck.elapsed() < MAX_SIZE_CHECK)
198  {
199  return m_cachedFramesWritten;
200  }
201 
202  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum));
203  strlist << "GET_FRAMES_WRITTEN";
204 
205  if (!SendReceiveStringList(strlist, 1))
206  {
207  LOG(VB_GENERAL, LOG_ERR, LOC + "GetFramesWritten() -- network error");
208  }
209  else
210  {
211  m_cachedFramesWritten = strlist[0].toLongLong();
212  m_lastTimeCheck.restart();
213  }
214 
215  return m_cachedFramesWritten;
216 }
217 
225 {
226  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum));
227  strlist << "GET_FILE_POSITION";
228 
229  if (SendReceiveStringList(strlist, 1))
230  return strlist[0].toLongLong();
231 
232  return -1;
233 }
234 
240 {
241  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum));
242  strlist << "GET_MAX_BITRATE";
243 
244  if (SendReceiveStringList(strlist, 1))
245  return strlist[0].toLongLong();
246 
247  return 20200000LL; // Peek bit rate for HD-PVR
248 }
249 
257 int64_t RemoteEncoder::GetKeyframePosition(uint64_t desired)
258 {
259  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
260  strlist << "GET_KEYFRAME_POS";
261  strlist << QString::number(desired);
262 
263  if (SendReceiveStringList(strlist, 1))
264  return strlist[0].toLongLong();
265 
266  return -1;
267 }
268 
269 void RemoteEncoder::FillPositionMap(int64_t start, int64_t end,
270  frm_pos_map_t &positionMap)
271 {
272  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum));
273  strlist << "FILL_POSITION_MAP";
274  strlist << QString::number(start);
275  strlist << QString::number(end);
276 
277  if (!SendReceiveStringList(strlist))
278  return;
279 
280  QStringList::const_iterator it = strlist.begin();
281  for (; it != strlist.end(); ++it)
282  {
283  bool ok;
284  uint64_t index = (*it).toLongLong(&ok);
285  if (++it == strlist.end() || !ok)
286  break;
287 
288  uint64_t pos = (*it).toLongLong(&ok);
289  if (!ok)
290  break;
291 
292  positionMap[index] = pos;
293  }
294 }
295 
296 void RemoteEncoder::FillDurationMap(int64_t start, int64_t end,
297  frm_pos_map_t &durationMap)
298 {
299  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum));
300  strlist << "FILL_DURATION_MAP";
301  strlist << QString::number(start);
302  strlist << QString::number(end);
303 
304  if (!SendReceiveStringList(strlist))
305  return;
306 
307  QStringList::const_iterator it = strlist.begin();
308  for (; it != strlist.end(); ++it)
309  {
310  bool ok;
311  uint64_t index = (*it).toLongLong(&ok);
312  if (++it == strlist.end() || !ok)
313  break;
314 
315  uint64_t pos = (*it).toLongLong(&ok);
316  if (!ok)
317  break;
318 
319  durationMap[index] = pos;
320  }
321 }
322 
324 {
325  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum));
326  strlist << "CANCEL_NEXT_RECORDING";
327  strlist << QString::number((cancel) ? 1 : 0);
328 
329  SendReceiveStringList(strlist);
330 }
331 
333 {
334  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum));
335  strlist << "FRONTEND_READY";
336 
337  SendReceiveStringList(strlist);
338 }
339 
345 {
346  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
347  strlist << "STOP_PLAYING";
348 
349  SendReceiveStringList(strlist);
350 }
351 
357 void RemoteEncoder::SpawnLiveTV(const QString& chainId, bool pip, const QString& startchan)
358 {
359  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum));
360  strlist << "SPAWN_LIVETV";
361  strlist << chainId;
362  strlist << QString::number((int)pip);
363  strlist << startchan;
364 
365  SendReceiveStringList(strlist);
366 }
367 
374 {
375  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
376  strlist << "STOP_LIVETV";
377 
378  SendReceiveStringList(strlist);
379 }
380 
387 {
388  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
389  strlist << "PAUSE";
390 
391  if (SendReceiveStringList(strlist))
392  m_lastinput = "";
393 }
394 
396 {
397  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
398  strlist << "FINISH_RECORDING";
399 
400  SendReceiveStringList(strlist);
401 }
402 
404 {
405  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
406  strlist << "SET_LIVE_RECORDING";
407  strlist << QString::number(recording);
408 
409  SendReceiveStringList(strlist);
410 }
411 
413 {
414  if (!m_lastinput.isEmpty())
415  return m_lastinput;
416 
417  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
418  strlist << "GET_INPUT";
419 
420  if (SendReceiveStringList(strlist, 1))
421  {
422  m_lastinput = strlist[0];
423  return m_lastinput;
424  }
425 
426  return "Error";
427 }
428 
429 QString RemoteEncoder::SetInput(const QString& input)
430 {
431  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
432  strlist << "SET_INPUT";
433  strlist << input;
434 
435  if (SendReceiveStringList(strlist, 1))
436  {
437  m_lastchannel = "";
438  m_lastinput = "";
439  return strlist[0];
440  }
441 
442  return (m_lastinput.isEmpty()) ? "Error" : m_lastinput;
443 }
444 
445 void RemoteEncoder::ToggleChannelFavorite(const QString& changroupname)
446 {
447  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
448  strlist << "TOGGLE_CHANNEL_FAVORITE";
449  strlist << changroupname;
450 
451  SendReceiveStringList(strlist);
452 }
453 
454 void RemoteEncoder::ChangeChannel(int channeldirection)
455 {
456  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
457  strlist << "CHANGE_CHANNEL";
458  strlist << QString::number(channeldirection);
459 
460  if (!SendReceiveStringList(strlist))
461  return;
462 
463  m_lastchannel = "";
464  m_lastinput = "";
465 }
466 
467 void RemoteEncoder::SetChannel(const QString& channel)
468 {
469  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
470  strlist << "SET_CHANNEL";
471  strlist << channel;
472 
473  if (!SendReceiveStringList(strlist))
474  return;
475 
476  m_lastchannel = "";
477  m_lastinput = "";
478 }
479 
496 int RemoteEncoder::SetSignalMonitoringRate(int rate, bool notifyFrontend)
497 {
498  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
499  strlist << "SET_SIGNAL_MONITORING_RATE";
500  strlist << QString::number(rate);
501  strlist << QString::number((int)notifyFrontend);
502 
503  if (SendReceiveStringList(strlist, 1))
504  return strlist[0].toInt();
505 
506  return 0;
507 }
508 
510 {
511  QMutexLocker locker(&m_lock);
512 
513  QMap<QString,uint>::const_iterator it = m_cachedTimeout.find(input);
514  if (it != m_cachedTimeout.end())
515  return *it;
516 
517  uint cardid = m_recordernum;
518  uint timeout = 0xffffffff;
519  MSqlQuery query(MSqlQuery::InitCon());
520  query.prepare(
521  "SELECT channel_timeout, cardtype "
522  "FROM capturecard "
523  "WHERE capturecard.inputname = :INNAME AND "
524  " capturecard.cardid = :CARDID");
525  query.bindValue(":INNAME", input);
526  query.bindValue(":CARDID", cardid);
527  if (!query.exec() || !query.isActive())
528  MythDB::DBError("Getting timeout", query);
529  else if (query.next() &&
530  SignalMonitor::IsRequired(query.value(1).toString()))
531  timeout = max(query.value(0).toInt(), 500);
532 
533 #if 0
534  LOG(VB_PLAYBACK, LOG_DEBUG, "RemoteEncoder: " +
535  QString("GetSignalLockTimeout(%1): Set lock timeout to %2 ms")
536  .arg(cardid).arg(timeout));
537 #endif
538  m_cachedTimeout[input] = timeout;
539  return timeout;
540 }
541 
542 
544 {
545  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
546 
547  if (kPictureAttribute_Contrast == attr)
548  strlist << "GET_CONTRAST";
549  else if (kPictureAttribute_Brightness == attr)
550  strlist << "GET_BRIGHTNESS";
551  else if (kPictureAttribute_Colour == attr)
552  strlist << "GET_COLOUR";
553  else if (kPictureAttribute_Hue == attr)
554  strlist << "GET_HUE";
555  else
556  return -1;
557 
558  if (SendReceiveStringList(strlist, 1))
559  return strlist[0].toInt();
560 
561  return -1;
562 }
563 
572  PictureAdjustType type, PictureAttribute attr, bool up)
573 {
574  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
575 
576  if (kPictureAttribute_Contrast == attr)
577  strlist << "CHANGE_CONTRAST";
578  else if (kPictureAttribute_Brightness == attr)
579  strlist << "CHANGE_BRIGHTNESS";
580  else if (kPictureAttribute_Colour == attr)
581  strlist << "CHANGE_COLOUR";
582  else if (kPictureAttribute_Hue == attr)
583  strlist << "CHANGE_HUE";
584  else
585  return -1;
586 
587  strlist << QString::number(type);
588  strlist << QString::number((int)up);
589 
590  if (SendReceiveStringList(strlist, 1))
591  return strlist[0].toInt();
592 
593  return -1;
594 }
595 
597 {
598  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
599  strlist << "CHANGE_DEINTERLACER";
600  strlist << QString::number(deint_mode);
601 
602  SendReceiveStringList(strlist);
603 }
604 
614 bool RemoteEncoder::CheckChannel(const QString& channel)
615 {
616  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
617  strlist << "CHECK_CHANNEL";
618  strlist << channel;
619 
620  if (SendReceiveStringList(strlist, 1))
621  return strlist[0].toInt() != 0;
622 
623  return false;
624 }
625 
635 bool RemoteEncoder::ShouldSwitchToAnotherCard(const QString& channelid)
636 {
637  // this function returns true if the channelid is not a valid
638  // channel on the current recorder. It queries to server in order
639  // to determine this.
640  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
641  strlist << "SHOULD_SWITCH_CARD";
642  strlist << channelid;
643 
644  if (SendReceiveStringList(strlist, 1))
645  return strlist[0].toInt() != 0;
646 
647  return false;
648 }
649 
657  const QString &prefix,
658  uint &complete_valid_channel_on_rec,
659  bool &is_extra_char_useful,
660  QString &needed_spacer)
661 {
662  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
663  strlist << "CHECK_CHANNEL_PREFIX";
664  strlist << prefix;
665 
666  if (!SendReceiveStringList(strlist, 4))
667  return false;
668 
669  complete_valid_channel_on_rec = strlist[1].toInt();
670  is_extra_char_useful = (strlist[2].toInt() != 0);
671  needed_spacer = (strlist[3] == "X") ? "" : strlist[3];
672 
673  return strlist[0].toInt() != 0;
674 }
675 
676 static QString cleanup(const QString &str)
677 {
678  if (str == " ")
679  return "";
680  return str;
681 }
682 
683 static QString make_safe(const QString &str)
684 {
685  if (str.isEmpty())
686  return " ";
687  return str;
688 }
689 
696 void RemoteEncoder::GetNextProgram(int direction,
697  QString &title, QString &subtitle,
698  QString &desc, QString &category,
699  QString &starttime, QString &endtime,
700  QString &callsign, QString &iconpath,
701  QString &channelname, QString &chanid,
702  QString &seriesid, QString &programid)
703 {
704  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum) );
705  strlist << "GET_NEXT_PROGRAM_INFO";
706  strlist << channelname;
707  strlist << chanid;
708  strlist << QString::number(direction);
709  strlist << starttime;
710 
711  if (!SendReceiveStringList(strlist, 12))
712  return;
713 
714  title = cleanup(strlist[0]);
715  subtitle = cleanup(strlist[1]);
716  desc = cleanup(strlist[2]);
717  category = cleanup(strlist[3]);
718  starttime = cleanup(strlist[4]);
719  endtime = cleanup(strlist[5]);
720  callsign = cleanup(strlist[6]);
721  iconpath = cleanup(strlist[7]);
722  channelname = cleanup(strlist[8]);
723  chanid = cleanup(strlist[9]);
724  seriesid = cleanup(strlist[10]);
725  programid = cleanup(strlist[11]);
726 }
727 
729 {
730  QStringList strlist( QString("QUERY_RECORDER %1").arg(m_recordernum));
731  strlist << "GET_CHANNEL_INFO";
732  strlist << QString::number(chanid);
733 
734  if (!SendReceiveStringList(strlist, 6))
735  return;
736 
737  infoMap["chanid"] = cleanup(strlist[0]);
738  infoMap["sourceid"] = cleanup(strlist[1]);
739  infoMap["callsign"] = cleanup(strlist[2]);
740  infoMap["channum"] = cleanup(strlist[3]);
741  infoMap["channame"] = cleanup(strlist[4]);
742  infoMap["XMLTV"] = cleanup(strlist[5]);
743 
744  infoMap["oldchannum"] = infoMap["channum"];
745 }
746 
748 {
749  QStringList strlist( "SET_CHANNEL_INFO" );
750  strlist << make_safe(infoMap["chanid"]);
751  strlist << make_safe(infoMap["sourceid"]);
752  strlist << make_safe(infoMap["oldchannum"]);
753  strlist << make_safe(infoMap["callsign"]);
754  strlist << make_safe(infoMap["channum"]);
755  strlist << make_safe(infoMap["channame"]);
756  strlist << make_safe(infoMap["XMLTV"]);
757 
758  if (SendReceiveStringList(strlist, 1))
759  return strlist[0].toInt() != 0;
760 
761  return false;
762 }
void SetChannel(const QString &channel)
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:781
bool Setup(void)
void FrontendReady(void)
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:862
void GetChannelInfo(InfoMap &infoMap, uint chanid=0)
void CancelNextRecording(bool cancel)
void StopLiveTV(void)
Tells TVRec to stop a "Live TV" recorder.
void ChangeDeinterlacer(int deint_mode)
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...
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
long long GetFilePosition(void)
Returns total number of bytes written by TVRec's RingBuffer.
void ToggleChannelFavorite(const QString &)
int GetPictureAttribute(PictureAttribute attr)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
void FillDurationMap(int64_t start, int64_t end, frm_pos_map_t &durationMap)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QString SetInput(const QString &)
static QString make_safe(const QString &str)
long long GetFramesWritten(void)
Returns number of frames written to disk by TVRec's RecorderBase instance.
QMap< long long, long long > frm_pos_map_t
Frame # -> File offset map.
Definition: programtypes.h:46
QVariant value(int i) const
Definition: mythdbcon.h:198
bool IsValidRecorder(void) const
Holds information on recordings and videos.
Definition: programinfo.h:66
MythSocket * ConnectCommandSocket(const QString &hostname, int port, const QString &announcement, bool *proto_mismatch=nullptr, int maxConnTry=-1, int setup_timeout=-1)
bool ShouldSwitchToAnotherCard(const QString &channelid)
Checks if named channel exists on current tuner, or another tuner.
static bool IsRequired(const QString &cardtype)
Returns true iff the card type supports signal monitoring.
void FillPositionMap(int64_t start, int64_t end, frm_pos_map_t &positionMap)
bool IsRecording(bool *ok=nullptr)
bool CheckChannel(const QString &channel)
Checks if named channel exists on current tuner.
bool isActive(void) const
Definition: mythdbcon.h:204
float GetFrameRate(void)
Returns recordering frame rate set by nvr.
PictureAttribute
Definition: videoouttypes.h:89
unsigned int uint
Definition: compat.h:140
int ChangePictureAttribute(PictureAdjustType type, PictureAttribute attr, bool up)
Changes brightness/contrast/colour/hue of a recording.
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:534
int SetSignalMonitoringRate(int rate, bool notifyFrontend=true)
Sets the signal monitoring rate.
void PauseRecorder(void)
Tells TVRec to pause a recorder, used for channel and input changes.
bool CheckChannelPrefix(const QString &, uint &, bool &, QString &)
Checks a prefix against the channels in the DB.
int64_t GetKeyframePosition(uint64_t desired)
Returns byte position in RingBuffer of a keyframe.
void StopPlaying(void)
Tells TVRec to stop streaming a recording to the frontend.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:806
#define MAX_SIZE_CHECK
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
PictureAdjustType
Definition: tv.h:120
bool SetChannelInfo(const InfoMap &infoMap)
ProgramInfo * GetRecording(void)
static const uint kShortTimeout
Definition: mythsocket.h:71
bool SendReceiveStringList(QStringList &strlist, uint min_reply_length=0)
int GetRecorderNumber(void) const
void SetLiveRecording(bool recording)
uint GetSignalLockTimeout(const QString &input)
static QString cleanup(const QString &str)
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:602
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
long long GetMaxBitrate()
Returns the maximum bits per second this recorder can produce.
void FinishRecording(void)
QString GetHostName(void)
void SpawnLiveTV(const QString &chainid, bool pip, const QString &startchan)
Tells TVRec to Spawn a "Live TV" recorder.
QString GetInput(void)
void ChangeChannel(int channeldirection)
#define LOC