29 #include <QTextStream>
33 using namespace std::chrono_literals;
43 setObjectName(
"Control");
83 std::this_thread::sleep_for(50us);
91 LOG(VB_RECORD, LOG_CRIT,
LOC +
"Terminated.");
97 LOG(VB_RECORD, LOG_CRIT,
LOC + msg);
114 const QString & serial,
131 #define LOC QString("%1").arg(m_parent->Desc())
204 int len =
write(2, status.toUtf8().constData(), status.size());
205 len +=
write(2,
"\n", 1);
207 if (len != status.size() + 1)
209 LOG(VB_RECORD, LOG_ERR,
LOC +
210 QString(
"%1: Only wrote %2 of %3 bytes of message '%4'.")
211 .arg(command).arg(len).arg(status.size()).arg(status));
215 LOG(VB_RECORD, LOG_INFO,
LOC + QString(
"Processing '%1' --> '%2'")
216 .arg(command, status));
223 const QString & status)
225 QString msg = QString(
"%1:%2").arg(serial, status);
227 int len =
write(2, msg.toUtf8().constData(), msg.size());
228 len +=
write(2,
"\n", 1);
230 if (len != msg.size() + 1)
232 LOG(VB_RECORD, LOG_ERR,
LOC +
233 QString(
"%1: Only wrote %2 of %3 bytes of message '%4'.")
234 .arg(command).arg(len).arg(msg.size()).arg(msg));
238 if (!command.isEmpty())
240 LOG(VB_RECORD, LOG_INFO,
LOC + QString(
"Processing '%1' --> '%2'")
245 LOG(VB_RECORD, LOG_INFO,
LOC + QString(
"%1").arg(msg));
254 LOG(VB_RECORD, LOG_DEBUG,
LOC + QString(
"Processing '%1'").arg(cmd));
258 if (cmd.startsWith(
"APIVersion?"))
267 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
268 QStringList tokens = cmd.split(
':', QString::SkipEmptyParts);
270 QStringList tokens = cmd.split(
':', Qt::SkipEmptyParts);
272 if (tokens.size() < 2)
275 QString(
"0:ERR:Version 2 API expects serial_no:msg format. "
276 "Saw '%1' instead").arg(cmd));
280 if (tokens[1].startsWith(
"APIVersion?"))
287 else if (tokens[1].startsWith(
"APIVersion"))
289 if (tokens.size() > 1)
295 SendStatus(cmd, tokens[0],
"ERR:Missing API Version number");
297 else if (tokens[1].startsWith(
"Version?"))
304 else if (tokens[1].startsWith(
"Description?"))
313 else if (tokens[1].startsWith(
"HasLock?"))
320 else if (tokens[1].startsWith(
"SignalStrengthPercent"))
327 else if (tokens[1].startsWith(
"LockTimeout?"))
331 else if (tokens[1].startsWith(
"HasTuner"))
335 else if (tokens[1].startsWith(
"HasPictureAttributes"))
339 else if (tokens[1].startsWith(
"SendBytes"))
342 SendStatus(cmd, tokens[0],
"ERR:Not supported");
344 else if (tokens[1].startsWith(
"XON"))
354 SendStatus(cmd, tokens[0],
"WARN:Not streaming");
356 else if (tokens[1].startsWith(
"XOFF"))
366 SendStatus(cmd, tokens[0],
"WARN:Not streaming");
368 else if (tokens[1].startsWith(
"TuneChannel"))
370 if (tokens.size() > 2)
373 SendStatus(cmd, tokens[0],
"ERR:Missing channum");
375 else if (tokens[1].startsWith(
"TuneStatus?"))
379 else if (tokens[1].startsWith(
"LoadChannels"))
383 else if (tokens[1].startsWith(
"FirstChannel"))
387 else if (tokens[1].startsWith(
"NextChannel"))
391 else if (tokens[1].startsWith(
"IsOpen?"))
399 SendStatus(cmd, tokens[0],
"WARN:Not Open yet");
401 else if (tokens[1].startsWith(
"CloseRecorder"))
409 else if (tokens[1].startsWith(
"FlowControl?"))
413 else if (tokens[1].startsWith(
"BlockSize"))
415 if (tokens.size() > 1)
418 SendStatus(cmd, tokens[0],
"ERR:Missing block size");
420 else if (tokens[1].startsWith(
"StartStreaming"))
424 else if (tokens[1].startsWith(
"StopStreaming"))
433 QString(
"ERR:Unrecognized command '%1'").arg(tokens[1]));
440 setObjectName(
"Commands");
444 std::array<struct pollfd,2> polls {};
447 polls[0].events = POLLIN | POLLPRI;
448 polls[0].revents = 0;
451 input.open(stdin, QIODevice::ReadOnly);
452 QTextStream qtin(&input);
454 LOG(VB_RECORD, LOG_INFO,
LOC +
"Command parser ready.");
460 int ret = poll(polls.data(), poll_cnt,
timeout);
462 if (polls[0].revents & POLLHUP)
464 LOG(VB_RECORD, LOG_ERR,
LOC +
"poll eof (POLLHUP)");
467 if (polls[0].revents & POLLNVAL)
473 if (polls[0].revents & POLLIN)
477 cmd = qtin.readLine();
483 if ((EOVERFLOW == errno))
485 LOG(VB_RECORD, LOG_ERR,
"command overflow");
489 if ((EAGAIN == errno) || (EINTR == errno))
491 LOG(VB_RECORD, LOG_ERR,
LOC +
"retry command read.");
495 LOG(VB_RECORD, LOG_ERR,
LOC +
"unknown error reading command.");
500 LOG(VB_RECORD, LOG_INFO,
LOC +
"Command parser: shutting down");
513 if (buffer.size() < 1)
516 static int s_dropped = 0;
517 static int s_droppedBytes = 0;
529 block_t blk(
reinterpret_cast<const uint8_t *
>(buffer.constData()),
530 reinterpret_cast<const uint8_t *
>(buffer.constData())
536 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
537 QString(
"Adding %1 bytes").arg(buffer.size()));
541 s_droppedBytes += buffer.size();
542 LOG(VB_RECORD, LOG_WARNING,
LOC +
543 QString(
"Packet queue overrun. Dropped %1 packets, %2 bytes.")
544 .arg(++s_dropped).arg(s_droppedBytes));
546 std::this_thread::sleep_for(250us);
559 setObjectName(
"Buffer");
561 bool is_empty =
false;
563 auto send_time = std::chrono::system_clock::now() + 5min;
564 uint64_t write_total = 0;
565 uint64_t written = 0;
566 uint64_t write_cnt = 0;
567 uint64_t empty_cnt = 0;
569 LOG(VB_RECORD, LOG_INFO,
LOC +
"Buffer: Ready for data.");
579 if (send_time < std::chrono::system_clock::now())
582 send_time = std::chrono::system_clock::now() + 5min;
583 write_total += written;
586 LOG(VB_RECORD, LOG_NOTICE,
LOC +
587 QString(
"Count: %1, Empty cnt %2, Written %3, Total %4")
588 .arg(write_cnt).arg(empty_cnt)
589 .arg(written).arg(write_total));
593 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"Not streaming.");
596 write_cnt = empty_cnt = written = 0;
609 is_empty =
m_data.empty();
615 uint sz =
write(1, pkt.data(), pkt.size());
619 if (sz != pkt.size())
621 LOG(VB_GENERAL, LOG_WARNING,
LOC +
622 QString(
"Only wrote %1 of %2 bytes to mythbackend")
623 .arg(sz).arg(pkt.size()));
651 LOG(VB_RECORD, LOG_INFO,
LOC +
"Buffer: shutting down");