MythTV master
tvremoteutil.cpp
Go to the documentation of this file.
1#include <algorithm>
2#include <chrono> // for milliseconds
3#include <thread> // for sleep_for
4
7
8#include "cardutil.h"
9#include "inputinfo.h"
10#include "programinfo.h"
11#include "remoteencoder.h"
12#include "tv_rec.h"
13#include "tvremoteutil.h"
14
16{
18 {
19 const TVRec *rec = TVRec::GetTVRec(inputid);
20 if (rec)
21 return rec->GetFlags();
22 return 0;
23 }
24
25 QStringList strlist(QString("QUERY_REMOTEENCODER %1").arg(inputid));
26 strlist << "GET_FLAGS";
27 if (!gCoreContext->SendReceiveStringList(strlist) || strlist.empty())
28 return 0;
29
30 return strlist[0].toInt();
31}
32
34{
36 {
37 const TVRec *rec = TVRec::GetTVRec(inputid);
38 if (rec)
39 return rec->GetState();
41 }
42
43 QStringList strlist(QString("QUERY_REMOTEENCODER %1").arg(inputid));
44 strlist << "GET_STATE";
45 if (!gCoreContext->SendReceiveStringList(strlist) || strlist.empty())
47
48 return strlist[0].toInt();
49}
50
51
52bool RemoteRecordPending(uint inputid, const ProgramInfo *pginfo,
53 std::chrono::seconds secsleft, bool hasLater)
54{
56 {
57 TVRec *rec = TVRec::GetTVRec(inputid);
58 if (rec)
59 {
60 rec->RecordPending(pginfo, secsleft, hasLater);
61 return true;
62 }
63 return false;
64 }
65
66 QStringList strlist(QString("QUERY_REMOTEENCODER %1").arg(inputid));
67 strlist << "RECORD_PENDING";
68 strlist << QString::number(secsleft.count());
69 strlist << QString::number(static_cast<int>(hasLater));
70 pginfo->ToStringList(strlist);
71
72 if (!gCoreContext->SendReceiveStringList(strlist) || strlist.empty())
73 return false;
74
75 return strlist[0].toUpper() == "OK";
76}
77
79{
81 {
82 TVRec *rec = TVRec::GetTVRec(inputid);
83 if (rec)
84 {
85 rec->StopLiveTV();
86 return true;
87 }
88 return false;
89 }
90
91 QStringList strlist(QString("QUERY_REMOTEENCODER %1").arg(inputid));
92 strlist << "STOP_LIVETV";
93
94 if (!gCoreContext->SendReceiveStringList(strlist) || strlist.empty())
95 return false;
96
97 return strlist[0].toUpper() == "OK";
98}
99
101{
102 if (gCoreContext->IsBackend())
103 {
104 TVRec *rec = TVRec::GetTVRec(inputid);
105 if (rec)
106 {
107 rec->StopRecording();
108 return true;
109 }
110 return false;
111 }
112
113 QStringList strlist(QString("QUERY_REMOTEENCODER %1").arg(inputid));
114 strlist << "STOP_RECORDING";
115
116 if (!gCoreContext->SendReceiveStringList(strlist) || strlist.empty())
117 return false;
118
119 return strlist[0].toUpper() == "OK";
120}
121
123{
124 QStringList strlist(QString("STOP_RECORDING"));
125 pginfo->ToStringList(strlist);
126
128}
129
130void RemoteCancelNextRecording(uint inputid, bool cancel)
131{
132 QStringList strlist(QString("QUERY_RECORDER %1").arg(inputid));
133 strlist << "CANCEL_NEXT_RECORDING";
134 strlist << QString::number((cancel) ? 1 : 0);
135
137}
138
139std::vector<InputInfo> RemoteRequestFreeInputInfo(uint excluded_input)
140{
141 LOG(VB_CHANNEL, LOG_INFO,
142 QString("RemoteRequestFreeInputInfo excluding input %1")
143 .arg(excluded_input));
144
145 std::vector<InputInfo> inputs;
146
147 QStringList strlist(QString("GET_FREE_INPUT_INFO %1")
148 .arg(excluded_input));
149 if (!gCoreContext->SendReceiveStringList(strlist))
150 return inputs;
151
152 QStringList::const_iterator it = strlist.cbegin();
153 while (it != strlist.cend())
154 {
156 if (!info.FromStringList(it, strlist.cend()))
157 break;
158 inputs.push_back(info);
159 LOG(VB_CHANNEL, LOG_INFO,
160 QString("RemoteRequestFreeInputInfo got input %1 (%2/%3)")
161 .arg(info.m_inputId).arg(info.m_chanId).arg(info.m_mplexId));
162 }
163
164 LOG(VB_CHANNEL, LOG_INFO,
165 QString("RemoteRequestFreeInputInfo got %1 inputs")
166 .arg(inputs.size()));
167 return inputs;
168}
169
171{
172 LOG(VB_CHANNEL, LOG_INFO, QString("RemoteGetFreeRecorderCount"));
173
174 std::vector<InputInfo> inputs =
176
177 LOG(VB_CHANNEL, LOG_INFO, QString("RemoteGetFreeRecorderCount got %1")
178 .arg(inputs.size()));
179 return inputs.size();
180}
181
183{
184 LOG(VB_CHANNEL, LOG_INFO,
185 QString("RemoteRequestNextFreeRecorder after input %1)")
186 .arg(inputid));
187
188 std::vector<InputInfo> inputs =
190
191 if (inputs.empty())
192 {
193 LOG(VB_CHANNEL, LOG_INFO,
194 QString("RemoteRequestNextFreeRecorder got no input (after input %1)")
195 .arg(inputid));
196 return nullptr;
197 }
198
199 size_t i = 0;
200 for ( ; i < inputs.size(); ++i)
201 if (inputs[i].m_inputId == (uint)inputid)
202 break;
203
204 if (i >= inputs.size())
205 {
206 // We should always find the referenced input. If we don't,
207 // just return the first one.
208 i = 0;
209 }
210 else
211 {
212 // Try to find the next input with a different name. If one
213 // doesn't exist, just return the current one.
214 size_t j = i;
215 i = (i + 1) % inputs.size();
216 while (i != j && inputs[i].m_displayName == inputs[j].m_displayName)
217 {
218 i = (i + 1) % inputs.size();
219 }
220 }
221
222 LOG(VB_CHANNEL, LOG_INFO,
223 QString("RemoteRequestNextFreeRecorder got input %1")
224 .arg(inputs[i].m_inputId));
225
226 return RemoteGetExistingRecorder(inputs[i].m_inputId);
227}
228
229std::vector<uint> RemoteRequestFreeRecorderList(uint excluded_input)
230{
231 LOG(VB_CHANNEL, LOG_INFO,
232 QString("RemoteRequestFreeRecorderList excluding input %1")
233 .arg(excluded_input));
234
235 std::vector<InputInfo> inputs =
236 RemoteRequestFreeInputInfo(excluded_input);
237
238 std::vector<uint> inputids;
239 std::ranges::transform(inputs, std::back_inserter(inputids),
240 [](const auto & input){ return input.m_inputId; } );
241
242 LOG(VB_CHANNEL, LOG_INFO,
243 QString("RemoteRequestFreeRecorderList got inputs"));
244 return inputids;
245}
246
247std::vector<uint> RemoteRequestFreeInputList(uint excluded_input)
248{
249 LOG(VB_CHANNEL, LOG_INFO,
250 QString("RemoteRequestFreeInputList excluding input %1")
251 .arg(excluded_input));
252
253 std::vector<InputInfo> inputs =
254 RemoteRequestFreeInputInfo(excluded_input);
255
256 std::vector<uint> inputids;
257 std::ranges::transform(inputs, std::back_inserter(inputids),
258 [](const auto & input){ return input.m_inputId; } );
259
260 LOG(VB_CHANNEL, LOG_INFO,
261 QString("RemoteRequestFreeInputList got inputs"));
262 return inputids;
263}
264
266(const QStringList &qualifiedRecorders, uint excluded_input)
267{
268 LOG(VB_CHANNEL, LOG_INFO,
269 QString("RemoteRequestFreeRecorderFromList excluding input %1")
270 .arg(excluded_input));
271
272 std::vector<InputInfo> inputs =
273 RemoteRequestFreeInputInfo(excluded_input);
274
275 for (const auto & recorder : std::as_const(qualifiedRecorders))
276 {
277 uint inputid = recorder.toUInt();
278#ifdef __cpp_lib_ranges_contains
279 if (std::ranges::contains(inputs, inputid, &InputInfo::m_inputId))
280#else
281 auto sameinput = [inputid](const auto & input){ return input.m_inputId == inputid; };
282 if (std::ranges::any_of(inputs, sameinput))
283#endif
284 {
285 LOG(VB_CHANNEL, LOG_INFO,
286 QString("RemoteRequestFreeRecorderFromList got input %1")
287 .arg(inputid));
288 return RemoteGetExistingRecorder(inputid);
289 }
290 }
291
292 LOG(VB_CHANNEL, LOG_INFO,
293 QString("RemoteRequestFreeRecorderFromList got no input"));
294 return nullptr;
295}
296
298{
299 LOG(VB_CHANNEL, LOG_INFO,
300 QString("RemoteRequestRecorder entered"));
301
302 std::vector<InputInfo> inputs =
304
305 if (inputs.empty())
306 {
307 LOG(VB_CHANNEL, LOG_INFO,
308 QString("RemoteRequestRecorder got no input"));
309 return nullptr;
310 }
311
312 LOG(VB_CHANNEL, LOG_INFO,
313 QString("RemoteRequestRecorder got input %1")
314 .arg(inputs[0].m_inputId));
315 return RemoteGetExistingRecorder(inputs[0].m_inputId);
316}
317
319{
320 LOG(VB_CHANNEL, LOG_INFO,
321 QString("RemoteGetExistingRecorder program %1")
322 .arg(pginfo->GetTitle()));
323
324 QStringList strlist( "GET_RECORDER_NUM" );
325 pginfo->ToStringList(strlist);
326
327 if (!gCoreContext->SendReceiveStringList(strlist))
328 {
329 LOG(VB_CHANNEL, LOG_INFO,
330 QString("RemoteGetExistingRecorder got no input"));
331 return nullptr;
332 }
333
334 int num = strlist[0].toInt();
335 QString hostname = strlist[1];
336 int port = strlist[2].toInt();
337
338 LOG(VB_CHANNEL, LOG_INFO,
339 QString("RemoteGetExistingRecorder got input %1").arg(num));
340 return new RemoteEncoder(num, hostname, port);
341}
342
344{
345 LOG(VB_CHANNEL, LOG_INFO,
346 QString("RemoteGetExistingRecorder input %1")
347 .arg(recordernum));
348
349 QStringList strlist( "GET_RECORDER_FROM_NUM" );
350 strlist << QString("%1").arg(recordernum);
351
352 if (!gCoreContext->SendReceiveStringList(strlist))
353 {
354 LOG(VB_CHANNEL, LOG_INFO,
355 QString("RemoteGetExistingRecorder got no input"));
356 return nullptr;
357 }
358
359 QString hostname = strlist[0];
360 int port = strlist[1].toInt();
361
362 LOG(VB_CHANNEL, LOG_INFO,
363 QString("RemoteGetExistingRecorder got input %1")
364 .arg(recordernum));
365 return new RemoteEncoder(recordernum, hostname, port);
366}
367
368bool RemoteIsBusy(uint inputid, InputInfo &busy_input)
369{
370#if 0
371 LOG(VB_GENERAL, LOG_DEBUG, QString("RemoteIsBusy(%1) %2")
372 .arg(inputid).arg(gCoreContext->IsBackend() ? "be" : "fe"));
373#endif
374
375 busy_input.Clear();
376
377 if (gCoreContext->IsBackend())
378 {
379 const TVRec *rec = TVRec::GetTVRec(inputid);
380 if (rec)
381 return rec->IsBusy(&busy_input);
382
383 // Note this value is intentionally different than the
384 // non-backend, error value below. There is a small
385 // window when adding an input where an input can exist in
386 // the database, but not yet have a TVRec. In such cases,
387 // we don't want it to be considered busy and block other
388 // actions.
389 return false;
390 }
391
392 QStringList strlist(QString("QUERY_REMOTEENCODER %1").arg(inputid));
393 strlist << "IS_BUSY";
394 if (!gCoreContext->SendReceiveStringList(strlist) || strlist.empty())
395 return true;
396
397 QStringList::const_iterator it = strlist.cbegin();
398 bool state = (*it).toInt() != 0;
399 ++it;
400 if (!busy_input.FromStringList(it, strlist.cend()))
401 state = true; // if there was an error pretend that the input is busy.
402
403 return state;
404}
405
407 std::vector<TunerStatus> *tunerList, bool list_inactive)
408{
409 bool isRecording = false;
410 std::vector<uint> inputlist = CardUtil::GetInputList();
411
412 if (tunerList)
413 tunerList->clear();
414
415 for (uint inputid : inputlist)
416 {
417 int state = kState_ChangingState;
418 QString channelName = "";
419 QString title = "";
420 QString subtitle = "";
421 QDateTime dtStart = QDateTime();
422 QDateTime dtEnd = QDateTime();
423 QStringList strlist;
424
425 QString cmd = QString("QUERY_REMOTEENCODER %1").arg(inputid);
426
427 while (state == kState_ChangingState)
428 {
429 strlist = QStringList(cmd);
430 strlist << "GET_STATE";
432
433 if (strlist.empty())
434 break;
435
436 state = strlist[0].toInt();
437 if (kState_ChangingState == state)
438 std::this_thread::sleep_for(5ms);
439 }
440
441 if (kState_RecordingOnly == state || kState_WatchingRecording == state)
442 {
443 isRecording = true;
444
445 if (!tunerList)
446 break;
447
448 strlist = QStringList(QString("QUERY_RECORDER %1").arg(inputid));
449 strlist << "GET_RECORDING";
451
452 ProgramInfo progInfo(strlist);
453
454 title = progInfo.GetTitle();
455 subtitle = progInfo.GetSubtitle();
456 channelName = progInfo.GetChannelName();
457 dtStart = progInfo.GetScheduledStartTime();
458 dtEnd = progInfo.GetScheduledEndTime();
459 }
460 else if (!list_inactive)
461 {
462 continue;
463 }
464
465 if (tunerList)
466 {
467 TunerStatus tuner;
468 tuner.id = inputid;
469 tuner.isRecording = ((kState_RecordingOnly == state) ||
470 (kState_WatchingRecording == state));
471 tuner.channame = channelName;
472 tuner.title = (kState_ChangingState == state) ?
473 QObject::tr("Error querying recorder state") : title;
474 tuner.subtitle = subtitle;
475 tuner.startTime = dtStart;
476 tuner.endTime = dtEnd;
477 tunerList->push_back(tuner);
478 }
479 }
480
481 return isRecording;
482}
483
485{
486 int mask = 0;
487
488 QString cmd = "QUERY_ISRECORDING";
489
490 QStringList strlist( cmd );
491
492 if (!gCoreContext->SendReceiveStringList(strlist) || strlist.isEmpty())
493 return mask;
494
495 int recCount = strlist[0].toInt();
496
497 for (int i = 0, j = 0; j < recCount; i++)
498 {
499 cmd = QString("QUERY_RECORDER %1").arg(i + 1);
500
501 strlist = QStringList( cmd );
502 strlist << "IS_RECORDING";
503
504 if (gCoreContext->SendReceiveStringList(strlist) && !strlist.isEmpty())
505 {
506 if (strlist[0].toInt())
507 {
508 mask |= 1<<i;
509 j++; // count active recorder
510 }
511 }
512 else
513 {
514 break;
515 }
516 }
517
518 return mask;
519}
520
521/* vim: set expandtab tabstop=4 shiftwidth=4: */
static std::vector< uint > GetInputList(void)
Definition: cardutil.cpp:2908
virtual bool FromStringList(QStringList::const_iterator &it, const QStringList::const_iterator &end)
Definition: inputinfo.cpp:14
uint m_inputId
unique key in DB for this input
Definition: inputinfo.h:49
virtual void Clear(void)
Definition: inputinfo.cpp:6
bool IsBackend(void) const
is this process a backend process
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
Holds information on recordings and videos.
Definition: programinfo.h:74
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:405
QString GetChannelName(void) const
This is the channel name in the local market, i.e.
Definition: programinfo.h:394
QString GetTitle(void) const
Definition: programinfo.h:368
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:398
void ToStringList(QStringList &list) const
Serializes ProgramInfo into a QStringList which can be passed over a socket.
QString GetSubtitle(void) const
Definition: programinfo.h:370
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:142
uint GetFlags(void) const
Definition: tv_rec.h:244
void RecordPending(const ProgramInfo *rcinfo, std::chrono::seconds secsleft, bool hasLater)
Tells TVRec "rcinfo" is the next pending recording.
Definition: tv_rec.cpp:311
void StopLiveTV(void)
Tells TVRec to stop a "Live TV" recorder.
Definition: tv_rec.cpp:2929
bool IsBusy(InputInfo *busy_input=nullptr, std::chrono::seconds time_buffer=5s) const
Returns true if the recorder is busy, or will be within the next time_buffer seconds.
Definition: tv_rec.cpp:2536
static TVRec * GetTVRec(uint inputid)
Definition: tv_rec.cpp:4912
TVState GetState(void) const
Returns the TVState of the recorder.
Definition: tv_rec.cpp:263
void StopRecording(bool killFile=false)
Changes from a recording state to kState_None.
Definition: tv_rec.cpp:747
recording status stuff
Definition: tvremoteutil.h:17
bool isRecording
Definition: tvremoteutil.h:20
QDateTime endTime
Definition: tvremoteutil.h:25
QString subtitle
Definition: tvremoteutil.h:23
QString channame
Definition: tvremoteutil.h:21
QString title
Definition: tvremoteutil.h:22
QDateTime startTime
Definition: tvremoteutil.h:24
unsigned int uint
Definition: compat.h:60
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
static bool isRecording()
dictionary info
Definition: azlyrics.py:7
string hostname
Definition: caa.py:17
@ kState_RecordingOnly
Recording Only is a TVRec only state for when we are recording a program, but there is no one current...
Definition: tv.h:87
@ kState_WatchingRecording
Watching Recording is the state for when we are watching an in progress recording,...
Definition: tv.h:83
@ kState_ChangingState
This is a placeholder state which we never actually enter, but is returned by GetState() when we are ...
Definition: tv.h:92
bool RemoteStopLiveTV(uint inputid)
RemoteEncoder * RemoteRequestRecorder(void)
std::vector< uint > RemoteRequestFreeInputList(uint excluded_input)
bool RemoteStopRecording(uint inputid)
bool RemoteIsBusy(uint inputid, InputInfo &busy_input)
bool RemoteRecordPending(uint inputid, const ProgramInfo *pginfo, std::chrono::seconds secsleft, bool hasLater)
RemoteEncoder * RemoteGetExistingRecorder(const ProgramInfo *pginfo)
RemoteEncoder * RemoteRequestNextFreeRecorder(int inputid)
std::vector< InputInfo > RemoteRequestFreeInputInfo(uint excluded_input)
int RemoteGetFreeRecorderCount(void)
bool RemoteGetRecordingStatus(std::vector< TunerStatus > *tunerList, bool list_inactive)
uint RemoteGetState(uint inputid)
uint RemoteGetFlags(uint inputid)
int RemoteGetRecordingMask(void)
std::vector< uint > RemoteRequestFreeRecorderList(uint excluded_input)
RemoteEncoder * RemoteRequestFreeRecorderFromList(const QStringList &qualifiedRecorders, uint excluded_input)
void RemoteCancelNextRecording(uint inputid, bool cancel)