diff --git a/mythtv/programs/mythfilerecorder/commandlineparser.cpp b/mythtv/programs/mythfilerecorder/commandlineparser.cpp
index d9caeab..e8d4ed5 100644
a
|
b
|
MythFileRecorderCommandLineParser::MythFileRecorderCommandLineParser() : |
12 | 12 | |
13 | 13 | QString MythFileRecorderCommandLineParser::GetHelpHeader(void) const |
14 | 14 | { |
15 | | return "MythFilelRecorder's allow a go-between app to interface " |
16 | | "with a recording device before the data is processed by mythbackend."; |
| 15 | return "MythFileRecorder is a go-between app which interfaces " |
| 16 | "between a recording device and mythbackend."; |
17 | 17 | } |
18 | 18 | |
19 | 19 | void MythFileRecorderCommandLineParser::LoadArguments(void) |
… |
… |
void MythFileRecorderCommandLineParser::LoadArguments(void) |
26 | 26 | |
27 | 27 | addInFile(); |
28 | 28 | |
| 29 | add(QStringList(QStringList() << "--exec"), |
| 30 | "exec", false, |
| 31 | "Execute infile as a program, rather than opening it. Recording data is expected to be on stdout. --noloop is ignored.", ""); |
| 32 | |
29 | 33 | add(QStringList(QStringList() << "--noloop"), |
30 | 34 | "noloop", false, |
31 | 35 | "Don't loop input back to beginning on EOF.", ""); |
… |
… |
void MythFileRecorderCommandLineParser::LoadArguments(void) |
33 | 37 | add(QStringList(QStringList() << "--data-rate"), |
34 | 38 | "data_rate", 188*50000, |
35 | 39 | "Rate at which to read data from the file.", ""); |
| 40 | |
| 41 | add(QStringList(QStringList() << "--tuner"), |
| 42 | "tuner", "", |
| 43 | "Path of another program which will tune the channel requested by MythTV. Must accept the channel number as the last parameter.", ""); |
36 | 44 | } |
diff --git a/mythtv/programs/mythfilerecorder/mythfilerecorder.cpp b/mythtv/programs/mythfilerecorder/mythfilerecorder.cpp
index 6ea4295..521d5f8 100644
a
|
b
|
using namespace std; |
12 | 12 | #include <QCoreApplication> |
13 | 13 | #include <QTime> |
14 | 14 | #include <QThread> |
| 15 | #include <QProcess> |
15 | 16 | |
16 | 17 | #include "commandlineparser.h" |
17 | 18 | #include "mythfilerecorder.h" |
… |
… |
using namespace std; |
23 | 24 | |
24 | 25 | |
25 | 26 | #define VERSION "1.0.0" |
26 | | #define LOC QString("File(%1): ").arg(m_fileName) |
| 27 | #define LOC (m_exec_file ? QString("Child program: ") : QString("File(%1): ").arg(m_fileName)) |
27 | 28 | |
28 | 29 | Streamer::Streamer(Commands *parent, const QString &fname, |
29 | | int data_rate, bool loopinput) : |
30 | | m_parent(parent), m_fileName(fname), m_file(NULL), m_loop(loopinput), |
| 30 | bool exec_file, int data_rate, bool loopinput) : |
| 31 | m_parent(parent), m_fileName(fname), m_file(NULL), |
| 32 | m_exec_file(exec_file), m_loop(loopinput), |
31 | 33 | m_bufferMax(188 * 100000), m_blockSize(m_bufferMax / 4), |
32 | | m_data_rate(data_rate), m_data_read(0) |
| 34 | m_data_rate(data_rate), m_data_read(0), m_proc(NULL) |
33 | 35 | { |
34 | 36 | setObjectName("Streamer"); |
35 | | OpenFile(); |
| 37 | if (!m_exec_file) OpenFile(); |
36 | 38 | LOG(VB_RECORD, LOG_INFO, LOC + QString("Data Rate: %1").arg(m_data_rate)); |
37 | 39 | } |
38 | 40 | |
… |
… |
Streamer::~Streamer(void) |
45 | 47 | |
46 | 48 | void Streamer::OpenFile(void) |
47 | 49 | { |
48 | | m_file = new QFile(m_fileName); |
49 | | if (!m_file || !m_file->open(QIODevice::ReadOnly)) |
| 50 | if (m_exec_file) |
50 | 51 | { |
51 | | LOG(VB_RECORD, LOG_ERR, LOC + QString("Failed to open '%1' - ") |
52 | | .arg(m_fileName) + ENO); |
| 52 | m_proc = new QProcess(this); |
| 53 | m_proc->setProcessChannelMode(QProcess::ForwardedOutputChannel); |
| 54 | m_proc->start(m_fileName, QIODevice::ReadOnly|QProcess::Unbuffered); |
| 55 | if (!m_proc->waitForStarted()) { |
| 56 | LOG(VB_RECORD, LOG_ERR, LOC + QString("Failed to execute '%1' - ") |
| 57 | .arg(m_fileName) + ENO); |
| 58 | } |
| 59 | else { |
| 60 | LOG(VB_RECORD, LOG_INFO, LOC + QString("Executed '%1' - pid: %2") |
| 61 | .arg(m_fileName).arg(m_proc->processId())); |
| 62 | } |
| 63 | } |
| 64 | else { |
| 65 | m_file = new QFile(m_fileName); |
| 66 | if (!m_file || !m_file->open(QIODevice::ReadOnly)) |
| 67 | { |
| 68 | LOG(VB_RECORD, LOG_ERR, LOC + QString("Failed to open '%1' - ") |
| 69 | .arg(m_fileName) + ENO); |
| 70 | } |
53 | 71 | } |
54 | 72 | } |
55 | 73 | |
… |
… |
void Streamer::CloseFile(void) |
62 | 80 | delete m_file; |
63 | 81 | m_file = NULL; |
64 | 82 | } |
| 83 | if (m_proc) |
| 84 | { |
| 85 | m_proc->close(); |
| 86 | delete m_proc; |
| 87 | m_proc = NULL; |
| 88 | } |
65 | 89 | |
66 | 90 | LOG(VB_RECORD, LOG_INFO, LOC + "Streamer::Close -- end"); |
67 | 91 | } |
… |
… |
void Streamer::SendBytes(void) |
72 | 96 | |
73 | 97 | LOG(VB_RECORD, LOG_DEBUG, LOC + "SendBytes -- start"); |
74 | 98 | |
| 99 | if (m_exec_file) |
| 100 | { |
| 101 | if (!m_proc) |
| 102 | { |
| 103 | OpenFile(); |
| 104 | } |
| 105 | return; |
| 106 | } |
| 107 | |
75 | 108 | if (!m_file) |
76 | 109 | { |
77 | 110 | LOG(VB_GENERAL, LOG_ERR, LOC + "SendBytes -- file not open"); |
… |
… |
bool Commands::process_command(QString & cmd) |
199 | 232 | } |
200 | 233 | if (cmd.startsWith("HasLock?")) |
201 | 234 | { |
202 | | send_status(m_streamer->IsOpen() ? "OK:Yes" : "OK:No"); |
| 235 | send_status(m_exec_file || m_streamer->IsOpen() ? "OK:Yes" : "OK:No"); |
203 | 236 | return true; |
204 | 237 | } |
205 | 238 | if (cmd.startsWith("SignalStrengthPercent")) |
206 | 239 | { |
207 | | send_status(m_streamer->IsOpen() ? "OK:100" : "OK:0"); |
| 240 | send_status(m_exec_file || m_streamer->IsOpen() ? "OK:100" : "OK:0"); |
208 | 241 | return true; |
209 | 242 | } |
210 | 243 | if (cmd.startsWith("LockTimeout")) |
211 | 244 | { |
212 | | send_status("OK:1000"); |
| 245 | send_status("OK:5000"); |
213 | 246 | return true; |
214 | 247 | } |
215 | 248 | if (cmd.startsWith("HasTuner?")) |
216 | 249 | { |
217 | | send_status("OK:No"); |
| 250 | send_status(m_tuner.isEmpty() ? "OK:No" : "OK:Yes"); |
218 | 251 | return true; |
219 | 252 | } |
220 | 253 | if (cmd.startsWith("HasPictureAttributes?")) |
… |
… |
bool Commands::process_command(QString & cmd) |
233 | 266 | |
234 | 267 | if (cmd.startsWith("SendBytes")) |
235 | 268 | { |
236 | | if (!m_streamer->IsOpen()) |
| 269 | if (!m_exec_file && !m_streamer->IsOpen()) |
237 | 270 | { |
238 | 271 | send_status("ERR:file not open"); |
239 | 272 | LOG(VB_RECORD, LOG_ERR, LOC + "SendBytes - file not open."); |
… |
… |
bool Commands::process_command(QString & cmd) |
241 | 274 | else |
242 | 275 | { |
243 | 276 | #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) |
244 | | if (m_eof.loadAcquire() != 0) |
| 277 | if (m_eof.loadAcquire() != 0) |
245 | 278 | #else |
246 | | if (m_eof != 0) |
| 279 | if (m_eof != 0) |
247 | 280 | #endif |
248 | 281 | send_status("ERR:End of file"); |
249 | 282 | else |
… |
… |
bool Commands::process_command(QString & cmd) |
253 | 286 | } |
254 | 287 | } |
255 | 288 | } |
256 | | #if 0 |
257 | 289 | else if (cmd.startsWith("XON")) |
258 | 290 | { |
259 | 291 | // Used when FlowControl is XON/XOFF |
| 292 | send_status("OK"); |
260 | 293 | } |
261 | 294 | else if (cmd.startsWith("XOFF")) |
262 | 295 | { |
263 | 296 | // Used when FlowControl is XON/XOFF |
| 297 | send_status("OK"); |
264 | 298 | } |
265 | | else if (cmd.startsWith("TuneChannel")) |
| 299 | else if (cmd.startsWith("TuneChannel:")) |
266 | 300 | { |
267 | | // Used if we announce that we have a 'tuner' |
268 | | |
269 | | /** |
270 | | * TODO: extend to allow to 'tune' to different files. |
271 | | */ |
| 301 | if (!m_tuner.isEmpty()) |
| 302 | { |
| 303 | QProcess *p = new QProcess(this); |
| 304 | p->start(QString("%1 %2").arg(m_tuner).arg(cmd.mid(12))); |
| 305 | p->waitForFinished(); |
| 306 | delete p; |
| 307 | p = NULL; |
| 308 | send_status("OK"); |
| 309 | } |
| 310 | else |
| 311 | send_status(QString("ERR:No tuner has been set")); |
272 | 312 | } |
273 | | #endif |
274 | 313 | else if (cmd.startsWith("IsOpen?")) |
275 | 314 | { |
276 | | if (m_streamer->IsOpen()) |
| 315 | if (m_exec_file || m_streamer->IsOpen()) |
277 | 316 | send_status("OK:Open"); |
278 | 317 | else |
279 | | send_status(QString("ERR:Not Open: '%2'") |
| 318 | send_status(QString("ERR:Not Open: '%1'") |
280 | 319 | .arg(m_streamer->ErrorString())); |
281 | 320 | } |
282 | 321 | else if (cmd.startsWith("CloseRecorder")) |
… |
… |
bool Commands::process_command(QString & cmd) |
304 | 343 | * this 'recording' ExternalChannel::EnterPowerSavingMode() |
305 | 344 | * will be called, which invokes CloseRecorder() */ |
306 | 345 | send_status("OK:Stopped"); |
| 346 | emit CloseFile(); |
307 | 347 | } |
308 | 348 | else |
309 | 349 | { |
… |
… |
bool Commands::process_command(QString & cmd) |
315 | 355 | return true; |
316 | 356 | } |
317 | 357 | |
318 | | bool Commands::Run(const QString & filename, int data_rate, bool loopinput) |
| 358 | bool Commands::Run(const QString & filename, bool exec_file, int data_rate, bool loopinput, const QString & tuner) |
319 | 359 | { |
320 | 360 | QString cmd; |
321 | 361 | |
… |
… |
bool Commands::Run(const QString & filename, int data_rate, bool loopinput) |
329 | 369 | polls[0].revents = 0; |
330 | 370 | |
331 | 371 | m_fileName = filename; |
| 372 | m_exec_file = exec_file; |
| 373 | m_tuner = tuner; |
332 | 374 | |
333 | | m_streamer = new Streamer(this, m_fileName, data_rate, loopinput); |
| 375 | m_streamer = new Streamer(this, m_fileName, m_exec_file, data_rate, loopinput); |
334 | 376 | QThread *streamThread = new QThread(this); |
335 | 377 | |
336 | 378 | m_streamer->moveToThread(streamThread); |
… |
… |
bool Commands::Run(const QString & filename, int data_rate, bool loopinput) |
339 | 381 | |
340 | 382 | connect(this, SIGNAL(SendBytes(void)), |
341 | 383 | m_streamer, SLOT(SendBytes(void))); |
| 384 | connect(this, SIGNAL(CloseFile(void)), |
| 385 | m_streamer, SLOT(CloseFile(void))); |
342 | 386 | |
343 | 387 | streamThread->start(); |
344 | 388 | |
… |
… |
int main(int argc, char *argv[]) |
424 | 468 | } |
425 | 469 | |
426 | 470 | bool loopinput = !cmdline.toBool("noloop"); |
| 471 | bool exec_file = cmdline.toBool("exec"); |
427 | 472 | int data_rate = cmdline.toInt("data_rate"); |
428 | 473 | |
429 | 474 | QCoreApplication a(argc, argv); |
… |
… |
int main(int argc, char *argv[]) |
439 | 484 | else if (cmdline.GetArgs().size() >= 1) |
440 | 485 | filename = cmdline.GetArgs()[0]; |
441 | 486 | |
| 487 | QString tuner = ""; |
| 488 | if (!cmdline.toString("tuner").isEmpty()) |
| 489 | tuner = cmdline.toString("tuner"); |
| 490 | |
442 | 491 | Commands recorder; |
443 | | recorder.Run(filename, data_rate, loopinput); |
| 492 | recorder.Run(filename, exec_file, data_rate, loopinput, tuner); |
444 | 493 | |
445 | 494 | return GENERIC_EXIT_OK; |
446 | 495 | } |
diff --git a/mythtv/programs/mythfilerecorder/mythfilerecorder.h b/mythtv/programs/mythfilerecorder/mythfilerecorder.h
index 0396bd9..f3d9aa6 100644
a
|
b
|
|
4 | 4 | #include <QObject> |
5 | 5 | #include <QFile> |
6 | 6 | #include <QString> |
| 7 | #include <QProcess> |
7 | 8 | |
8 | 9 | #include <sys/types.h> |
9 | 10 | #include <unistd.h> |
… |
… |
class Streamer : public QObject |
22 | 23 | void SendBytes(void); |
23 | 24 | |
24 | 25 | public: |
25 | | Streamer(Commands *parent, const QString &filename, int data_rate, |
26 | | bool loopinput); |
| 26 | Streamer(Commands *parent, const QString &filename, bool exec_file, |
| 27 | int data_rate, bool loopinput); |
27 | 28 | virtual ~Streamer(void); |
28 | 29 | void BlockSize(int val) { m_blockSize = val; } |
29 | | bool IsOpen(void) const { return m_file; } |
| 30 | bool IsOpen(void) const { return m_exec_file ? (bool) m_proc : (bool) m_file; } |
30 | 31 | QString ErrorString(void) const { return m_error; } |
31 | 32 | |
32 | 33 | protected: |
… |
… |
class Streamer : public QObject |
36 | 37 | Commands *m_parent; |
37 | 38 | QString m_fileName; |
38 | 39 | QFile *m_file; |
| 40 | bool m_exec_file; |
39 | 41 | bool m_loop; |
40 | 42 | |
41 | | QString m_error; |
| 43 | QString m_error; |
42 | 44 | |
43 | | QByteArray m_buffer; |
44 | | int m_bufferMax; |
45 | | QAtomicInt m_blockSize; |
| 45 | QByteArray m_buffer; |
| 46 | int m_bufferMax; |
| 47 | QAtomicInt m_blockSize; |
46 | 48 | |
47 | 49 | // Regulate data rate |
48 | 50 | uint m_data_rate; // bytes per second |
49 | 51 | QDateTime m_start_time; // When the first packet was processed |
50 | 52 | quint64 m_data_read; // How many bytes have been sent |
| 53 | |
| 54 | QProcess *m_proc; |
51 | 55 | }; |
52 | 56 | |
53 | 57 | class Commands : public QObject |
… |
… |
class Commands : public QObject |
61 | 65 | public: |
62 | 66 | Commands(void); |
63 | 67 | virtual ~Commands(void); |
64 | | bool Run(const QString & filename, int data_rate, bool loopinput); |
| 68 | bool Run(const QString & filename, bool exec_file, int data_rate, bool loopinput, const QString &tuner); |
65 | 69 | void setEoF(void) { m_eof = true; } |
66 | 70 | |
67 | 71 | protected: |
… |
… |
class Commands : public QObject |
69 | 73 | bool process_command(QString & cmd); |
70 | 74 | |
71 | 75 | private: |
72 | | QString m_fileName; |
| 76 | QString m_fileName, m_tuner; |
73 | 77 | Streamer *m_streamer; |
74 | 78 | int m_timeout; |
75 | 79 | bool m_run; |
| 80 | bool m_exec_file; |
76 | 81 | QAtomicInt m_eof; |
77 | 82 | }; |
78 | 83 | |