Ticket #9737: tv_rec.cpp

File tv_rec.cpp, 139.2 KB (added by luigi.zanderighi@…, 15 years ago)

Patched tv_rec.cpp

Line 
1// C headers
2#include <cstdio>
3#include <cstdlib>
4#include <cstring>
5#include <unistd.h>
6#include <sched.h> // for sched_yield
7
8// C++ headers
9#include <iostream>
10using namespace std;
11
12// MythTV headers
13
14#include "compat.h"
15#include "previewgeneratorqueue.h"
16#include "mythconfig.h"
17#include "tv_rec.h"
18#include "osd.h"
19#include "mythcorecontext.h"
20#include "dialogbox.h"
21#include "recordingprofile.h"
22#include "util.h"
23#include "programinfo.h"
24#include "dtvsignalmonitor.h"
25#include "mythdb.h"
26#include "jobqueue.h"
27#include "recordingrule.h"
28#include "eitscanner.h"
29#include "ringbuffer.h"
30#include "storagegroup.h"
31#include "remoteutil.h"
32#include "tvremoteutil.h"
33#include "mythsystemevent.h"
34
35#include "atscstreamdata.h"
36#include "dvbstreamdata.h"
37#include "atsctables.h"
38
39#include "livetvchain.h"
40
41#include "channelutil.h"
42#include "channelbase.h"
43#include "dummychannel.h"
44#include "dtvchannel.h"
45#include "dvbchannel.h"
46#include "hdhrchannel.h"
47#include "iptvchannel.h"
48#include "firewirechannel.h"
49
50#include "recorderbase.h"
51#include "NuppelVideoRecorder.h"
52#include "mpegrecorder.h"
53#include "dvbrecorder.h"
54#include "hdhrrecorder.h"
55#include "iptvrecorder.h"
56#include "firewirerecorder.h"
57#include "importrecorder.h"
58
59#include "channelgroup.h"
60
61#ifdef USING_V4L
62#include "v4lchannel.h"
63#endif
64
65#define DEBUG_CHANNEL_PREFIX 0 /**< set to 1 to channel prefixing */
66
67#define LOC QString("TVRec(%1): ").arg(cardid)
68#define LOC_ERR QString("TVRec(%1) Error: ").arg(cardid)
69
70/// How many milliseconds the signal monitor should wait between checks
71const uint TVRec::kSignalMonitoringRate = 50; /* msec */
72
73QMutex TVRec::cardsLock;
74QMap<uint,TVRec*> TVRec::cards;
75
76static bool is_dishnet_eit(uint cardid);
77static QString load_profile(QString,void*,RecordingInfo*,RecordingProfile&);
78static int init_jobs(const RecordingInfo *rec, RecordingProfile &profile,
79 bool on_host, bool transcode_bfr_comm, bool on_line_comm);
80
81/** \class TVRec
82 * \brief This is the coordinating class of the \ref recorder_subsystem.
83 *
84 * TVRec is used by EncoderLink, which in turn is used by RemoteEncoder
85 * which allows the TV class on the frontend to communicate with TVRec
86 * and is used by MainServer to implement portions of the
87 * \ref myth_network_protocol on the backend.
88 *
89 * TVRec contains an instance of RecorderBase, which actually handles the
90 * recording of a program. It also contains an instance of RingBuffer, which
91 * in this case is used to either stream an existing recording to the
92 * frontend, or to save a stream from the RecorderBase to disk. Finally,
93 * if there is a tuner on the hardware RecorderBase is implementing then
94 * TVRec contains a channel instance for that hardware, and possibly a
95 * SignalMonitor instance which monitors the signal quality on a tuners
96 * current input.
97 */
98
99/** \fn TVRec::TVRec(int)
100 * \brief Performs instance initialiation not requiring access to database.
101 *
102 * \sa Init()
103 * \param capturecardnum Capture card number
104 */
105TVRec::TVRec(int capturecardnum)
106 // Various components TVRec coordinates
107 : recorder(NULL), channel(NULL), signalMonitor(NULL),
108 scanner(NULL),
109 // Configuration variables from database
110 transcodeFirst(false),
111 earlyCommFlag(false), runJobOnHostOnly(false),
112 eitCrawlIdleStart(60), eitTransportTimeout(5*60),
113 audioSampleRateDB(0),
114 overRecordSecNrml(0), overRecordSecCat(0),
115 overRecordCategory(""),
116 // Configuration variables from setup rutines
117 cardid(capturecardnum), ispip(false),
118 // State variables
119 stateChangeLock(QMutex::Recursive),
120 pendingRecLock(QMutex::Recursive),
121 internalState(kState_None), desiredNextState(kState_None),
122 changeState(false), pauseNotify(true),
123 stateFlags(0), lastTuningRequest(0),
124 triggerEventLoopLock(QMutex::NonRecursive),
125 triggerEventLoopSignal(false),
126 triggerEventSleepLock(QMutex::NonRecursive),
127 triggerEventSleepSignal(false),
128 m_switchingBuffer(false),
129 m_recStatus(rsUnknown),
130 // Current recording info
131 curRecording(NULL), autoRunJobs(JOB_NONE),
132 overrecordseconds(0),
133 // Pseudo LiveTV recording
134 pseudoLiveTVRecording(NULL),
135 nextLiveTVDir(""), nextLiveTVDirLock(),
136 // tvchain
137 tvchain(NULL),
138 // RingBuffer info
139 ringBuffer(NULL), rbFileExt("mpg")
140{
141 QMutexLocker locker(&cardsLock);
142 cards[cardid] = this;
143}
144
145bool TVRec::CreateChannel(const QString &startchannel)
146{
147 rbFileExt = "mpg";
148 bool init_run = false;
149
150 if (genOpt.cardtype == "DVB")
151 {
152#ifdef USING_DVB
153 channel = new DVBChannel(genOpt.videodev, this);
154 if (!channel->Open())
155 return false;
156 GetDVBChannel()->SetSlowTuning(dvbOpt.dvb_tuning_delay);
157 InitChannel(genOpt.defaultinput, startchannel);
158 CloseChannel(); // Close the channel if in dvb_on_demand mode
159 init_run = true;
160#endif
161 }
162 else if (genOpt.cardtype == "FIREWIRE")
163 {
164#ifdef USING_FIREWIRE
165 channel = new FirewireChannel(this, genOpt.videodev, fwOpt);
166 if (!channel->Open())
167 return false;
168 InitChannel(genOpt.defaultinput, startchannel);
169 init_run = true;
170#endif
171 }
172 else if (genOpt.cardtype == "HDHOMERUN")
173 {
174#ifdef USING_HDHOMERUN
175 channel = new HDHRChannel(this, genOpt.videodev);
176 if (!channel->Open())
177 return false;
178 InitChannel(genOpt.defaultinput, startchannel);
179 GetDTVChannel()->EnterPowerSavingMode();
180 init_run = true;
181#endif
182 }
183 else if ((genOpt.cardtype == "IMPORT") ||
184 (genOpt.cardtype == "DEMO") ||
185 (genOpt.cardtype == "MPEG" &&
186 genOpt.videodev.toLower().left(5) == "file:"))
187 {
188 channel = new DummyChannel(this);
189 if (!channel->Open())
190 return false;
191 InitChannel(genOpt.defaultinput, startchannel);
192 init_run = true;
193 }
194 else if (genOpt.cardtype == "FREEBOX")
195 {
196#ifdef USING_IPTV
197 channel = new IPTVChannel(this, genOpt.videodev);
198 if (!channel->Open())
199 return false;
200 InitChannel(genOpt.defaultinput, startchannel);
201 init_run = true;
202#endif
203 }
204 else // "V4L" or "MPEG", ie, analog TV
205 {
206#ifdef USING_V4L
207 channel = new V4LChannel(this, genOpt.videodev);
208 if (!channel->Open())
209 return false;
210 InitChannel(genOpt.defaultinput, startchannel);
211 CloseChannel();
212 init_run = true;
213#endif
214 if ((genOpt.cardtype != "MPEG") && (genOpt.cardtype != "HDPVR"))
215 rbFileExt = "nuv";
216 }
217
218 if (!init_run)
219 {
220 QString msg = QString(
221 "%1 card configured on video device %2, \n"
222 "but MythTV was not compiled with %3 support. \n"
223 "\n"
224 "Recompile MythTV with %4 support or remove the card \n"
225 "from the configuration and restart MythTV.")
226 .arg(genOpt.cardtype).arg(genOpt.videodev)
227 .arg(genOpt.cardtype).arg(genOpt.cardtype);
228 VERBOSE(VB_IMPORTANT, LOC_ERR + "\n" + msg + "\n");
229 SetFlags(kFlagErrored);
230 return false;
231 }
232 return true;
233}
234
235/** \fn TVRec::Init(void)
236 * \brief Performs instance initialization, returns true on success.
237 *
238 * \return Returns true on success, false on failure.
239 */
240bool TVRec::Init(void)
241{
242 QMutexLocker lock(&stateChangeLock);
243
244 if (!GetDevices(cardid, genOpt, dvbOpt, fwOpt))
245 return false;
246
247 pendingRecLock.lock();
248 m_recStatus = rsUnknown;
249 pendingRecLock.unlock();
250
251 // configure the Channel instance
252 QString startchannel = GetStartChannel(cardid, genOpt.defaultinput);
253 if (!CreateChannel(startchannel))
254 return false;
255
256 transcodeFirst =
257 gCoreContext->GetNumSetting("AutoTranscodeBeforeAutoCommflag", 0);
258 earlyCommFlag = gCoreContext->GetNumSetting("AutoCommflagWhileRecording", 0);
259 runJobOnHostOnly = gCoreContext->GetNumSetting("JobsRunOnRecordHost", 0);
260 eitTransportTimeout=gCoreContext->GetNumSetting("EITTransportTimeout", 5) * 60;
261 eitCrawlIdleStart = gCoreContext->GetNumSetting("EITCrawIdleStart", 60);
262 audioSampleRateDB = gCoreContext->GetNumSetting("AudioSampleRate");
263 overRecordSecNrml = gCoreContext->GetNumSetting("RecordOverTime");
264 overRecordSecCat = gCoreContext->GetNumSetting("CategoryOverTime") * 60;
265 overRecordCategory= gCoreContext->GetSetting("OverTimeCategory");
266
267 EventThread.SetParent(this);
268 EventThread.start();
269
270 WaitForEventThreadSleep();
271
272 return true;
273}
274
275/** \fn TVRec::~TVRec()
276 * \brief Stops the event and scanning threads and deletes any ChannelBase,
277 * RingBuffer, and RecorderBase instances.
278 */
279TVRec::~TVRec()
280{
281 QMutexLocker locker(&cardsLock);
282 cards.remove(cardid);
283 TeardownAll();
284}
285
286void TVRec::TeardownAll(void)
287{
288 if (HasFlags(kFlagRunMainLoop))
289 {
290 ClearFlags(kFlagRunMainLoop);
291 EventThread.wait();
292 }
293
294 TeardownSignalMonitor();
295
296 if (scanner)
297 {
298 delete scanner;
299 scanner = NULL;
300 }
301
302 if (channel)
303 {
304 delete channel;
305 channel = NULL;
306 }
307
308 TeardownRecorder(true);
309
310 SetRingBuffer(NULL);
311}
312
313void TVRec::WakeEventLoop(void)
314{
315 QMutexLocker locker(&triggerEventLoopLock);
316 triggerEventLoopSignal = true;
317 triggerEventLoopWait.wakeAll();
318}
319
320/** \fn TVRec::GetState() const
321 * \brief Returns the TVState of the recorder.
322 *
323 * If there is a pending state change kState_ChangingState is returned.
324 * \sa EncoderLink::GetState(), \ref recorder_subsystem
325 */
326TVState TVRec::GetState(void) const
327{
328 if (changeState)
329 return kState_ChangingState;
330 return internalState;
331}
332
333/** \fn TVRec::GetRecording(void)
334 * \brief Allocates and returns a ProgramInfo for the current recording.
335 *
336 * Note: The user of this function must free the %ProgramInfo this returns.
337 * \return %ProgramInfo for the current recording, if it exists, blank
338 * %ProgramInfo otherwise.
339 */
340ProgramInfo *TVRec::GetRecording(void)
341{
342 QMutexLocker lock(&stateChangeLock);
343
344 ProgramInfo *tmppginfo = NULL;
345
346 if (curRecording && !changeState)
347 {
348 tmppginfo = new ProgramInfo(*curRecording);
349 tmppginfo->SetRecordingStatus(rsRecording);
350 }
351 else
352 tmppginfo = new ProgramInfo();
353 tmppginfo->SetCardID(cardid);
354
355 return tmppginfo;
356}
357
358/** \fn TVRec::RecordPending(const ProgramInfo*, int, bool)
359 * \brief Tells TVRec "rcinfo" is the next pending recording.
360 *
361 * When there is a pending recording and the frontend is in "Live TV"
362 * mode the TVRec event loop will send a "ASK_RECORDING" message to
363 * it. Depending on what that query returns, the recording will be
364 * started or not started.
365 *
366 * \sa TV::AskAllowRecording(const QStringList&, int, bool)
367 * \param rcinfo ProgramInfo on pending program.
368 * \param secsleft Seconds left until pending recording begins.
369 * Set to -1 to revoke the current pending recording.
370 * \param hasLater If true, a later non-conflicting showing is available.
371 */
372void TVRec::RecordPending(const ProgramInfo *rcinfo, int secsleft,
373 bool hasLater)
374{
375 QMutexLocker statelock(&stateChangeLock);
376 QMutexLocker pendlock(&pendingRecLock);
377
378 if (secsleft < 0)
379 {
380 VERBOSE(VB_RECORD, LOC + "Pending recording revoked on " +
381 QString("inputid %1").arg(rcinfo->GetInputID()));
382
383 PendingMap::iterator it = pendingRecordings.find(rcinfo->GetCardID());
384 if (it != pendingRecordings.end())
385 {
386 (*it).ask = false;
387 (*it).doNotAsk = (*it).canceled = true;
388 }
389 return;
390 }
391
392 VERBOSE(VB_RECORD, LOC +
393 QString("RecordPending on inputid %1").arg(rcinfo->GetInputID()));
394
395 PendingInfo pending;
396 pending.info = new ProgramInfo(*rcinfo);
397 pending.recordingStart = QDateTime::currentDateTime().addSecs(secsleft);
398 pending.hasLaterShowing = hasLater;
399 pending.ask = true;
400 pending.doNotAsk = false;
401
402 pendingRecordings[rcinfo->GetCardID()] = pending;
403
404 // If this isn't a recording for this instance to make, we are done
405 if (rcinfo->GetCardID() != cardid)
406 return;
407
408 // We also need to check our input groups
409 vector<uint> cardids = CardUtil::GetConflictingCards(
410 rcinfo->GetInputID(), cardid);
411
412 pendingRecordings[rcinfo->GetCardID()].possibleConflicts = cardids;
413
414 pendlock.unlock();
415 statelock.unlock();
416 for (uint i = 0; i < cardids.size(); i++)
417 RemoteRecordPending(cardids[i], rcinfo, secsleft, hasLater);
418 statelock.relock();
419 pendlock.relock();
420}
421
422/** \fn TVRec::SetPseudoLiveTVRecording(ProgramInfo*)
423 * \brief Sets the pseudo LiveTV ProgramInfo
424 */
425void TVRec::SetPseudoLiveTVRecording(ProgramInfo *pi)
426{
427 ProgramInfo *old_rec = pseudoLiveTVRecording;
428 pseudoLiveTVRecording = pi;
429 if (old_rec)
430 delete old_rec;
431}
432
433/** \fn TVRec::GetRecordEndTime(const ProgramInfo*) const
434 * \brief Returns recording end time with proper post-roll
435 */
436QDateTime TVRec::GetRecordEndTime(const ProgramInfo *pi) const
437{
438 bool spcat = (!overRecordCategory.isEmpty() &&
439 pi->GetCategory() == overRecordCategory);
440 int secs = (spcat) ? overRecordSecCat : overRecordSecNrml;
441 return pi->GetRecordingEndTime().addSecs(secs);
442}
443
444/** \fn TVRec::CancelNextRecording(bool)
445 * \brief Tells TVRec to cancel the upcoming recording.
446 * \sa RecordPending(const ProgramInfo*, int, bool),
447 * TV::AskAllowRecording(const QStringList&, int, bool)
448 */
449void TVRec::CancelNextRecording(bool cancel)
450{
451 QMutexLocker pendlock(&pendingRecLock);
452 VERBOSE(VB_RECORD, LOC + "CancelNextRecording("<<cancel<<") -- begin");
453
454 PendingMap::iterator it = pendingRecordings.find(cardid);
455 if (it == pendingRecordings.end())
456 {
457 VERBOSE(VB_RECORD, LOC + "CancelNextRecording("<<cancel<<") -- "
458 "error, unknown recording");
459 return;
460 }
461
462 if (cancel)
463 {
464 vector<uint> &cardids = (*it).possibleConflicts;
465 for (uint i = 0; i < cardids.size(); i++)
466 {
467 VERBOSE(VB_RECORD, LOC +
468 "CancelNextRecording -- cardid "<<cardids[i]);
469
470 pendlock.unlock();
471 RemoteRecordPending(cardids[i], (*it).info, -1, false);
472 pendlock.relock();
473 }
474
475 VERBOSE(VB_RECORD, LOC + "CancelNextRecording -- cardid "<<cardid);
476
477 RecordPending((*it).info, -1, false);
478 }
479 else
480 {
481 (*it).canceled = false;
482 }
483
484 VERBOSE(VB_RECORD, LOC + "CancelNextRecording("<<cancel<<") -- end");
485}
486
487/** \fn TVRec::StartRecording(const ProgramInfo*)
488 * \brief Tells TVRec to Start recording the program "rcinfo"
489 * as soon as possible.
490 *
491 * \return +1 if the recording started successfully,
492 * -1 if TVRec is busy doing something else, 0 otherwise.
493 * \sa EncoderLink::StartRecording(const ProgramInfo*)
494 * RecordPending(const ProgramInfo*, int, bool), StopRecording()
495 */
496RecStatusType TVRec::StartRecording(const ProgramInfo *rcinfo)
497{
498 VERBOSE(VB_RECORD, LOC + QString("StartRecording(%1)")
499 .arg(rcinfo->toString(ProgramInfo::kTitleSubtitle)));
500
501 QMutexLocker lock(&stateChangeLock);
502 QString msg("");
503
504 pendingRecLock.lock();
505 m_recStatus = rsAborted;
506 pendingRecLock.unlock();
507
508 // Flush out any pending state changes
509 WaitForEventThreadSleep();
510
511 // We need to do this check early so we don't cancel an overrecord
512 // that we're trying to extend.
513 if (internalState != kState_WatchingLiveTV &&
514 curRecording && curRecording->IsSameProgramWeakCheck(*rcinfo))
515 {
516 int post_roll_seconds = curRecording->GetRecordingEndTime()
517 .secsTo(recordEndTime);
518
519 curRecording->SetRecordingRuleType(rcinfo->GetRecordingRuleType());
520 curRecording->SetRecordingRuleID(rcinfo->GetRecordingRuleID());
521 curRecording->SetRecordingEndTime(rcinfo->GetRecordingEndTime());
522 curRecording->UpdateRecordingEnd();
523
524 recordEndTime = curRecording->GetRecordingEndTime()
525 .addSecs(post_roll_seconds);
526
527 msg = QString("updating recording: %1 %2 %3 %4")
528 .arg(curRecording->GetTitle()).arg(curRecording->GetChanID())
529 .arg(curRecording->GetRecordingStartTime(ISODate))
530 .arg(curRecording->GetRecordingEndTime(ISODate));
531 VERBOSE(VB_RECORD, LOC + msg);
532
533 ClearFlags(kFlagCancelNextRecording);
534
535 pendingRecLock.lock();
536 m_recStatus = rsRecording;
537 pendingRecLock.unlock();
538 return rsRecording;
539 }
540
541 bool cancelNext = false;
542 PendingInfo pendinfo;
543 PendingMap::iterator it;
544 bool has_pending;
545
546 pendingRecLock.lock();
547 if ((it = pendingRecordings.find(cardid)) != pendingRecordings.end())
548 {
549 (*it).ask = (*it).doNotAsk = false;
550 cancelNext = (*it).canceled;
551 }
552 pendingRecLock.unlock();
553
554 // Flush out events...
555 WaitForEventThreadSleep();
556
557 // Rescan pending recordings since the event loop may have deleted
558 // a stale entry. If this happens the info pointer will not be valid
559 // since the HandlePendingRecordings loop will have deleted it.
560 pendingRecLock.lock();
561 it = pendingRecordings.find(cardid);
562 has_pending = (it != pendingRecordings.end());
563 if (has_pending)
564 pendinfo = *it;
565 pendingRecLock.unlock();
566
567 // If the needed input is in a shared input group, and we are
568 // not canceling the recording anyway, check other recorders
569 if (!cancelNext && has_pending && pendinfo.possibleConflicts.size())
570 {
571 VERBOSE(VB_RECORD, LOC + "Checking input group recorders - begin");
572 vector<uint> &cardids = pendinfo.possibleConflicts;
573
574 uint mplexid = 0, sourceid = 0;
575 vector<uint> cardids2;
576 vector<TVState> states;
577
578 // Stop remote recordings if needed
579 for (uint i = 0; i < cardids.size(); i++)
580 {
581 TunedInputInfo busy_input;
582 bool is_busy = RemoteIsBusy(cardids[i], busy_input);
583
584 // if the other recorder is busy, but the input is
585 // not in a shared input group, then as far as we're
586 // concerned here it isn't busy.
587 if (is_busy)
588 {
589 is_busy = (bool) igrp.GetSharedInputGroup(
590 busy_input.inputid, rcinfo->GetInputID());
591 }
592
593 if (is_busy && !sourceid)
594 {
595 mplexid = pendinfo.info->QueryMplexID();
596 sourceid = pendinfo.info->GetSourceID();
597 }
598
599 if (is_busy &&
600 ((sourceid != busy_input.sourceid) ||
601 (mplexid != busy_input.mplexid)))
602 {
603 states.push_back((TVState) RemoteGetState(cardids[i]));
604 cardids2.push_back(cardids[i]);
605 }
606 }
607
608 bool ok = true;
609 for (uint i = 0; (i < cardids2.size()) && ok; i++)
610 {
611 VERBOSE(VB_RECORD, LOC +
612 QString("Attempting to stop card %1 in state %2")
613 .arg(cardids2[i]).arg(StateToString(states[i])));
614
615 bool success = RemoteStopRecording(cardids2[i]);
616 if (success)
617 {
618 uint state = RemoteGetState(cardids2[i]);
619 VERBOSE(VB_IMPORTANT, LOC + QString("a %1: %2")
620 .arg(cardids2[i]).arg(StateToString((TVState)state)));
621 success = (kState_None == state);
622 }
623
624 // If we managed to stop LiveTV recording, restart playback..
625 if (success && states[i] == kState_WatchingLiveTV)
626 {
627 QString message = QString("QUIT_LIVETV %1").arg(cardids2[i]);
628 MythEvent me(message);
629 gCoreContext->dispatch(me);
630 }
631
632 VERBOSE(VB_RECORD, LOC + QString(
633 "Stopping recording on %1, %2")
634 .arg(cardids2[i])
635 .arg(success ? "succeeded" : "failed"));
636
637 ok &= success;
638 }
639
640 // If we failed to stop the remote recordings, don't record
641 if (!ok)
642 {
643 CancelNextRecording(true);
644 cancelNext = true;
645 }
646
647 cardids.clear();
648
649 VERBOSE(VB_RECORD, LOC + "Checking input group recorders - done");
650 }
651
652 // If in post-roll, end recording
653 if (!cancelNext && (GetState() == kState_RecordingOnly))
654 {
655 stateChangeLock.unlock();
656 StopRecording();
657 stateChangeLock.lock();
658 }
659
660 if (!cancelNext && (GetState() == kState_None))
661 {
662 if (tvchain)
663 {
664 QString message = QString("LIVETV_EXITED");
665 MythEvent me(message, tvchain->GetID());
666 gCoreContext->dispatch(me);
667 tvchain = NULL;
668 }
669
670 recordEndTime = GetRecordEndTime(rcinfo);
671
672 // Tell event loop to begin recording.
673 curRecording = new RecordingInfo(*rcinfo);
674 curRecording->MarkAsInUse(true, kRecorderInUseID);
675 StartedRecording(curRecording);
676
677 // Make sure scheduler is allowed to end this recording
678 ClearFlags(kFlagCancelNextRecording);
679
680 pendingRecLock.lock();
681 m_recStatus = rsTuning;
682 pendingRecLock.unlock();
683 ChangeState(kState_RecordingOnly);
684 }
685 else if (!cancelNext && (GetState() == kState_WatchingLiveTV))
686 {
687 SetPseudoLiveTVRecording(new ProgramInfo(*rcinfo));
688 recordEndTime = GetRecordEndTime(rcinfo);
689 pendingRecLock.lock();
690 m_recStatus = rsRecording;
691 pendingRecLock.unlock();
692
693 // We want the frontend to change channel for recording
694 // and disable the UI for channel change, PiP, etc.
695
696 QString message = QString("LIVETV_WATCH %1 1").arg(cardid);
697 QStringList prog;
698 rcinfo->ToStringList(prog);
699 MythEvent me(message, prog);
700 gCoreContext->dispatch(me);
701 }
702 else
703 {
704 msg = QString("Wanted to record: %1 %2 %3 %4\n\t\t\t")
705 .arg(rcinfo->GetTitle()).arg(rcinfo->GetChanID())
706 .arg(rcinfo->GetRecordingStartTime(ISODate))
707 .arg(rcinfo->GetRecordingEndTime(ISODate));
708
709 if (cancelNext)
710 {
711 msg += "But a user has canceled this recording";
712 pendingRecLock.lock();
713 m_recStatus = rsCancelled;
714 pendingRecLock.unlock();
715 }
716 else
717 {
718 msg += QString("But the current state is: %1")
719 .arg(StateToString(internalState));
720 pendingRecLock.lock();
721 m_recStatus = rsTunerBusy;
722 pendingRecLock.unlock();
723 }
724
725 if (curRecording && internalState == kState_RecordingOnly)
726 msg += QString("\n\t\t\tCurrently recording: %1 %2 %3 %4")
727 .arg(curRecording->GetTitle()).arg(curRecording->GetChanID())
728 .arg(curRecording->GetRecordingStartTime(ISODate))
729 .arg(curRecording->GetRecordingEndTime(ISODate));
730
731 VERBOSE(VB_IMPORTANT, LOC + msg);
732 }
733
734 for (int i = 0; i < pendingRecordings.size(); i++)
735 delete pendingRecordings[i].info;
736 pendingRecordings.clear();
737
738 WaitForEventThreadSleep();
739
740 RecStatusType status;
741
742 pendingRecLock.lock();
743 if ((curRecording) && (curRecording->GetRecordingStatus() == rsFailed) &&
744 (m_recStatus == rsRecording || m_recStatus == rsTuning))
745 m_recStatus = rsFailed;
746 status = m_recStatus;
747 pendingRecLock.unlock();
748
749 return status;
750}
751
752RecStatusType TVRec::GetRecordingStatus(void) const
753{
754 QMutexLocker pendlock(&pendingRecLock);
755 return m_recStatus;
756}
757
758
759/** \fn TVRec::StopRecording(bool killFile)
760 * \brief Changes from a recording state to kState_None.
761 * \sa StartRecording(const ProgramInfo *rec), FinishRecording()
762 */
763void TVRec::StopRecording(bool killFile)
764{
765 if (StateIsRecording(GetState()))
766 {
767 QMutexLocker lock(&stateChangeLock);
768 if (killFile)
769 SetFlags(kFlagKillRec);
770 ChangeState(RemoveRecording(GetState()));
771 // wait for state change to take effect
772 WaitForEventThreadSleep();
773 ClearFlags(kFlagCancelNextRecording|kFlagKillRec);
774
775 pendingRecLock.lock();
776 m_recStatus = rsUnknown;
777 pendingRecLock.unlock();
778 }
779}
780
781/** \fn TVRec::StateIsRecording(TVState)
782 * \brief Returns true if "state" is kState_RecordingOnly,
783 * or kState_WatchingLiveTV.
784 * \param state TVState to check.
785 */
786bool TVRec::StateIsRecording(TVState state)
787{
788 return (state == kState_RecordingOnly ||
789 state == kState_WatchingLiveTV);
790}
791
792/** \fn TVRec::StateIsPlaying(TVState)
793 * \brief Returns true if we are in any state associated with a player.
794 * \param state TVState to check.
795 */
796bool TVRec::StateIsPlaying(TVState state)
797{
798 return (state == kState_WatchingPreRecorded);
799}
800
801/** \fn TVRec::RemoveRecording(TVState)
802 * \brief If "state" is kState_RecordingOnly or kState_WatchingLiveTV,
803 * returns a kState_None, otherwise returns kState_Error.
804 * \param state TVState to check.
805 */
806TVState TVRec::RemoveRecording(TVState state)
807{
808 if (StateIsRecording(state))
809 return kState_None;
810
811 VERBOSE(VB_IMPORTANT, LOC_ERR +
812 QString("Unknown state in RemoveRecording: %1")
813 .arg(StateToString(state)));
814 return kState_Error;
815}
816
817/** \fn TVRec::RemovePlaying(TVState)
818 * \brief Returns TVState that would remove the playing, but potentially
819 * keep recording if we are watching an in progress recording.
820 * \param state TVState to check.
821 */
822TVState TVRec::RemovePlaying(TVState state)
823{
824 if (StateIsPlaying(state))
825 {
826 if (state == kState_WatchingPreRecorded)
827 return kState_None;
828 return kState_RecordingOnly;
829 }
830
831 QString msg = "Unknown state in RemovePlaying: %1";
832 VERBOSE(VB_IMPORTANT, LOC_ERR + msg.arg(StateToString(state)));
833
834 return kState_Error;
835}
836
837/** \fn TVRec::StartedRecording(RecordingInfo *curRec)
838 * \brief Inserts a "curRec" into the database
839 * \param curRec Recording to add to database.
840 * \sa ProgramInfo::StartedRecording(const QString&)
841 */
842void TVRec::StartedRecording(RecordingInfo *curRec)
843{
844 if (!curRec)
845 return;
846
847 curRec->StartedRecording(rbFileExt);
848 VERBOSE(VB_RECORD, LOC + "StartedRecording("<<curRec<<") fn("
849 <<curRec->GetPathname()<<")");
850
851 if (curRec->IsCommercialFree())
852 curRec->SaveCommFlagged(COMM_FLAG_COMMFREE);
853
854 SendMythSystemRecEvent("REC_STARTED", curRec);
855}
856
857/** \fn TVRec::FinishedRecording(RecordingInfo *curRec)
858 * \brief If not a premature stop, adds program to history of recorded
859 * programs. If the recording type is kFindOneRecord this find
860 * is removed.
861 * \sa ProgramInfo::FinishedRecording(bool prematurestop)
862 * \param curRec ProgramInfo or recording to mark as done
863 */
864void TVRec::FinishedRecording(RecordingInfo *curRec)
865{
866 if (!curRec)
867 return;
868
869 const QString recgrp = curRec->QueryRecordingGroup();
870 curRec->SetRecordingGroup(recgrp);
871
872 VERBOSE(VB_RECORD, LOC + QString("FinishedRecording(%1) in recgroup: %2")
873 .arg(curRec->GetTitle()).arg(recgrp));
874
875 if (curRec->GetRecordingStatus() == rsRecording)
876 curRec->SetRecordingStatus(rsRecorded);
877 else if (curRec->GetRecordingStatus() != rsRecorded)
878 curRec->SetRecordingStatus(rsFailed);
879 curRec->SetRecordingEndTime(mythCurrentDateTime());
880
881 if (tvchain)
882 tvchain->FinishedRecording(curRec);
883
884 // Make sure really short recordings have positive run time.
885 if (curRec->GetRecordingEndTime() <= curRec->GetRecordingStartTime())
886 {
887 curRec->SetRecordingEndTime(
888 curRec->GetRecordingStartTime().addSecs(60));
889 }
890
891 // Round up recording end time (probably should be done in the UI only)
892 QDateTime recendts = curRec->GetRecordingEndTime();
893 recendts.setTime(QTime(recendts.addSecs(30).time().hour(),
894 recendts.addSecs(30).time().minute()));
895 curRec->SetRecordingEndTime(recendts);
896
897 if (recgrp != "LiveTV")
898 {
899 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
900 .arg(curRec->GetCardID())
901 .arg(curRec->GetChanID())
902 .arg(curRec->GetScheduledStartTime(ISODate))
903 .arg(curRec->GetRecordingStatus())
904 .arg(curRec->GetRecordingEndTime(ISODate)));
905 gCoreContext->dispatch(me);
906 }
907
908 curRec->FinishedRecording(curRec->GetRecordingStatus() != rsRecorded);
909}
910
911#define TRANSITION(ASTATE,BSTATE) \
912 ((internalState == ASTATE) && (desiredNextState == BSTATE))
913#define SET_NEXT() do { nextState = desiredNextState; changed = true; } while(0)
914#define SET_LAST() do { nextState = internalState; changed = true; } while(0)
915
916/** \fn TVRec::HandleStateChange(void)
917 * \brief Changes the internalState to the desiredNextState if possible.
918 *
919 * Note: There must exist a state transition from any state we can enter
920 * to the kState_None state, as this is used to shutdown TV in RunTV.
921 *
922 */
923void TVRec::HandleStateChange(void)
924{
925 TVState nextState = internalState;
926
927 bool changed = false;
928
929 QString transMsg = QString(" %1 to %2")
930 .arg(StateToString(nextState))
931 .arg(StateToString(desiredNextState));
932
933 if (desiredNextState == internalState)
934 {
935 VERBOSE(VB_IMPORTANT, LOC_ERR + "HandleStateChange(): "
936 "Null transition" + transMsg);
937 changeState = false;
938 return;
939 }
940
941 // Make sure EIT scan is stopped before any tuning,
942 // to avoid race condition with it's tuning requests.
943 if (HasFlags(kFlagEITScannerRunning))
944 {
945 scanner->StopActiveScan();
946 ClearFlags(kFlagEITScannerRunning);
947 }
948
949 // Handle different state transitions
950 if (TRANSITION(kState_None, kState_WatchingLiveTV))
951 {
952 tuningRequests.enqueue(TuningRequest(kFlagLiveTV));
953 SET_NEXT();
954 }
955 else if (TRANSITION(kState_WatchingLiveTV, kState_None))
956 {
957 tuningRequests.enqueue(TuningRequest(kFlagKillRec|kFlagKillRingBuffer));
958 SET_NEXT();
959 }
960 else if (TRANSITION(kState_WatchingLiveTV, kState_RecordingOnly))
961 {
962 SetPseudoLiveTVRecording(NULL);
963
964 SET_NEXT();
965 }
966 else if (TRANSITION(kState_None, kState_RecordingOnly))
967 {
968 SetPseudoLiveTVRecording(NULL);
969 tuningRequests.enqueue(TuningRequest(kFlagRecording, curRecording));
970 SET_NEXT();
971 }
972 else if (TRANSITION(kState_RecordingOnly, kState_None))
973 {
974 tuningRequests.enqueue(
975 TuningRequest(kFlagCloseRec|kFlagKillRingBuffer|
976 (GetFlags()&kFlagKillRec)));
977 SET_NEXT();
978 }
979
980 QString msg = (changed) ? "Changing from" : "Unknown state transition:";
981 VERBOSE(VB_IMPORTANT, LOC + msg + transMsg);
982
983 // update internal state variable
984 internalState = nextState;
985 changeState = false;
986
987 eitScanStartTime = QDateTime::currentDateTime();
988 if ((internalState == kState_None) &&
989 scanner)
990 eitScanStartTime = eitScanStartTime.addSecs(eitCrawlIdleStart);
991 else
992 eitScanStartTime = eitScanStartTime.addYears(1);
993}
994#undef TRANSITION
995#undef SET_NEXT
996#undef SET_LAST
997
998/** \fn TVRec::ChangeState(TVState)
999 * \brief Puts a state change on the nextState queue.
1000 */
1001void TVRec::ChangeState(TVState nextState)
1002{
1003 QMutexLocker lock(&stateChangeLock);
1004 desiredNextState = nextState;
1005 changeState = true;
1006 WakeEventLoop();
1007}
1008
1009/** \fn TVRec::SetupRecorder(RecordingProfile&)
1010 * \brief Allocates and initializes the RecorderBase instance.
1011 *
1012 * Based on the card type, one of the possible recorders are started.
1013 * If the card type is "MPEG" or "HDPVR" a MpegRecorder is started,
1014 * if the card type is "HDHOMERUN" a HDHRRecorder is started,
1015 * if the card type is "FIREWIRE" a FirewireRecorder is started,
1016 * if the card type is "DVB" a DVBRecorder is started,
1017 * otherwise a NuppelVideoRecorder is started.
1018 *
1019 * If there is any this will return false.
1020 * \sa IsErrored()
1021 */
1022bool TVRec::SetupRecorder(RecordingProfile &profile)
1023{
1024 recorder = NULL;
1025 if (genOpt.cardtype == "MPEG")
1026 {
1027#ifdef USING_IVTV
1028 recorder = new MpegRecorder(this);
1029#endif // USING_IVTV
1030 }
1031 else if (genOpt.cardtype == "HDPVR")
1032 {
1033#ifdef USING_HDPVR
1034 recorder = new MpegRecorder(this);
1035#endif // USING_HDPVR
1036 }
1037 else if (genOpt.cardtype == "FIREWIRE")
1038 {
1039#ifdef USING_FIREWIRE
1040 recorder = new FirewireRecorder(this, GetFirewireChannel());
1041#endif // USING_FIREWIRE
1042 }
1043 else if (genOpt.cardtype == "HDHOMERUN")
1044 {
1045#ifdef USING_HDHOMERUN
1046 recorder = new HDHRRecorder(this, GetHDHRChannel());
1047 ringBuffer->SetWriteBufferSize(4*1024*1024);
1048 recorder->SetOption("wait_for_seqstart", genOpt.wait_for_seqstart);
1049#endif // USING_HDHOMERUN
1050 }
1051 else if (genOpt.cardtype == "DVB")
1052 {
1053#ifdef USING_DVB
1054 recorder = new DVBRecorder(this, GetDVBChannel());
1055 ringBuffer->SetWriteBufferSize(4*1024*1024);
1056 recorder->SetOption("wait_for_seqstart", genOpt.wait_for_seqstart);
1057 recorder->SetOption("dvb_on_demand", dvbOpt.dvb_on_demand);
1058#endif // USING_DVB
1059 }
1060 else if (genOpt.cardtype == "FREEBOX")
1061 {
1062#ifdef USING_IPTV
1063 IPTVChannel *chan = dynamic_cast<IPTVChannel*>(channel);
1064 recorder = new IPTVRecorder(this, chan);
1065 ringBuffer->SetWriteBufferSize(4*1024*1024);
1066 recorder->SetOption("mrl", genOpt.videodev);
1067#endif // USING_IPTV
1068 }
1069 else if (genOpt.cardtype == "IMPORT")
1070 {
1071 recorder = new ImportRecorder(this);
1072 }
1073 else if (genOpt.cardtype == "DEMO")
1074 {
1075#ifdef USING_IVTV
1076 recorder = new MpegRecorder(this);
1077#else
1078 recorder = new ImportRecorder(this);
1079#endif
1080 }
1081 else
1082 {
1083#ifdef USING_V4L
1084 // V4L/MJPEG/GO7007 from here on
1085 recorder = new NuppelVideoRecorder(this, channel);
1086 recorder->SetOption("skipbtaudio", genOpt.skip_btaudio);
1087#endif // USING_V4L
1088 }
1089
1090 if (recorder)
1091 {
1092 recorder->SetOptionsFromProfile(
1093 &profile, genOpt.videodev, genOpt.audiodev, genOpt.vbidev);
1094 // Override the samplerate defined in the profile if this card
1095 // was configured with a fixed rate.
1096 if (genOpt.audiosamplerate)
1097 recorder->SetOption("samplerate", genOpt.audiosamplerate);
1098 recorder->SetRingBuffer(ringBuffer);
1099 recorder->Initialize();
1100
1101 if (recorder->IsErrored())
1102 {
1103 VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to initialize recorder!");
1104 delete recorder;
1105 recorder = NULL;
1106 return false;
1107 }
1108
1109 return true;
1110 }
1111
1112 QString msg = "Need %1 recorder, but compiled without %2 support!";
1113 msg = msg.arg(genOpt.cardtype).arg(genOpt.cardtype);
1114 VERBOSE(VB_IMPORTANT, LOC_ERR + msg);
1115
1116 return false;
1117}
1118
1119/** \fn TVRec::TeardownRecorder(bool)
1120 * \brief Tears down the recorder.
1121 *
1122 * If a "recorder" exists, RecorderBase::StopRecording() is called.
1123 * We then wait for "recorder_thread" to exit, and finally we delete
1124 * "recorder".
1125 *
1126 * If a RingBuffer instance exists, RingBuffer::StopReads() is called,
1127 * and then we delete the RingBuffer instance.
1128 *
1129 * If killfile is true, the recording is deleted.
1130 *
1131 * Finally, if there was a recording and it was not deleted,
1132 * schedule any post-processing jobs.
1133 *
1134 * \param killFile if true the recorded file is deleted.
1135 */
1136void TVRec::TeardownRecorder(bool killFile)
1137{
1138 pauseNotify = false;
1139 ispip = false;
1140
1141 if (recorder && HasFlags(kFlagRecorderRunning))
1142 {
1143 // Get the width and set the videoprops
1144 uint avg_height = curRecording->QueryAverageHeight();
1145 curRecording->SaveResolutionProperty(
1146 (avg_height > 1000) ? VID_1080 :
1147 ((avg_height > 700) ? VID_720 : VID_UNKNOWN));
1148
1149 int secsSince = curRecording->GetRecordingStartTime()
1150 .secsTo(QDateTime::currentDateTime());
1151 QString message = QString("DONE_RECORDING %1 %2 %3")
1152 .arg(cardid).arg(secsSince).arg(GetFramesWritten());
1153 MythEvent me(message);
1154 gCoreContext->dispatch(me);
1155
1156 recorder->StopRecording();
1157 RecorderThread.wait();
1158 }
1159 ClearFlags(kFlagRecorderRunning);
1160
1161 if (recorder)
1162 {
1163 if (GetV4LChannel())
1164 channel->SetFd(-1);
1165
1166 delete recorder;
1167 recorder = NULL;
1168 }
1169
1170 if (ringBuffer)
1171 ringBuffer->StopReads();
1172
1173 if (curRecording)
1174 {
1175 if (!killFile)
1176 {
1177 if (curRecording->IsLocal())
1178 PreviewGeneratorQueue::GetPreviewImage(*curRecording, "");
1179
1180 if (!tvchain)
1181 {
1182 int secsSince = curRecording->GetRecordingStartTime()
1183 .secsTo(QDateTime::currentDateTime());
1184 if (secsSince < 120)
1185 {
1186 JobQueue::RemoveJobsFromMask(JOB_COMMFLAG, autoRunJobs);
1187 JobQueue::RemoveJobsFromMask(JOB_TRANSCODE, autoRunJobs);
1188 }
1189
1190 if (autoRunJobs)
1191 JobQueue::QueueRecordingJobs(*curRecording, autoRunJobs);
1192 }
1193 }
1194
1195 FinishedRecording(curRecording);
1196
1197 SendMythSystemRecEvent("REC_FINISHED", curRecording);
1198
1199 curRecording->MarkAsInUse(false, kRecorderInUseID);
1200 delete curRecording;
1201 curRecording = NULL;
1202 }
1203
1204 pauseNotify = true;
1205
1206 if (GetDTVChannel())
1207 GetDTVChannel()->EnterPowerSavingMode();
1208}
1209
1210DVBRecorder *TVRec::GetDVBRecorder(void)
1211{
1212#ifdef USING_DVB
1213 return dynamic_cast<DVBRecorder*>(recorder);
1214#else // if !USING_DVB
1215 return NULL;
1216#endif // !USING_DVB
1217}
1218
1219HDHRRecorder *TVRec::GetHDHRRecorder(void)
1220{
1221#ifdef USING_HDHOMERUN
1222 return dynamic_cast<HDHRRecorder*>(recorder);
1223#else // if !USING_HDHOMERUN
1224 return NULL;
1225#endif // !USING_HDHOMERUN
1226}
1227
1228DTVRecorder *TVRec::GetDTVRecorder(void)
1229{
1230 return dynamic_cast<DTVRecorder*>(recorder);
1231}
1232
1233/** \fn TVRec::InitChannel(const QString&, const QString&)
1234 * \brief Performs ChannelBase instance init from database and
1235 * tuner hardware (requires that channel be open).
1236 */
1237void TVRec::InitChannel(const QString &inputname, const QString &startchannel)
1238{
1239 if (!channel)
1240 return;
1241
1242 QString input = inputname;
1243 QString channum = startchannel;
1244
1245 channel->Init(input, channum, true);
1246}
1247
1248void TVRec::CloseChannel(void)
1249{
1250 if (!channel)
1251 return;
1252
1253 if (GetDVBChannel() && !dvbOpt.dvb_on_demand)
1254 return;
1255
1256 channel->Close();
1257}
1258
1259DTVChannel *TVRec::GetDTVChannel(void)
1260{
1261 return dynamic_cast<DTVChannel*>(channel);
1262}
1263
1264HDHRChannel *TVRec::GetHDHRChannel(void)
1265{
1266#ifdef USING_HDHOMERUN
1267 return dynamic_cast<HDHRChannel*>(channel);
1268#else
1269 return NULL;
1270#endif // USING_HDHOMERUN
1271}
1272
1273DVBChannel *TVRec::GetDVBChannel(void)
1274{
1275#ifdef USING_DVB
1276 return dynamic_cast<DVBChannel*>(channel);
1277#else
1278 return NULL;
1279#endif // USING_DVB
1280}
1281
1282FirewireChannel *TVRec::GetFirewireChannel(void)
1283{
1284#ifdef USING_FIREWIRE
1285 return dynamic_cast<FirewireChannel*>(channel);
1286#else
1287 return NULL;
1288#endif // USING_FIREWIRE
1289}
1290
1291V4LChannel *TVRec::GetV4LChannel(void)
1292{
1293#ifdef USING_V4L
1294 return dynamic_cast<V4LChannel*>(channel);
1295#else
1296 return NULL;
1297#endif // USING_V4L
1298}
1299
1300/** \fn TVReEventThread::run(void)
1301 * \brief Thunk that allows event thread to call RunTV().
1302 */
1303void TVRecEventThread::run(void)
1304{
1305 if (!m_parent)
1306 return;
1307
1308 m_parent->RunTV();
1309}
1310
1311/** \fn TVReRecordThread::run(void)
1312 * \brief Thunk that allows recorder thread to
1313 * call RecorderBase::StartRecording().
1314 */
1315void TVRecRecordThread::run(void)
1316{
1317 if (!m_parent || !m_parent->recorder)
1318 return;
1319
1320 m_parent->recorder->StartRecording();
1321}
1322
1323static bool get_use_eit(uint cardid)
1324{
1325 MSqlQuery query(MSqlQuery::InitCon());
1326 query.prepare(
1327 "SELECT SUM(useeit) "
1328 "FROM videosource, cardinput "
1329 "WHERE videosource.sourceid = cardinput.sourceid AND"
1330 " cardinput.cardid = :CARDID");
1331 query.bindValue(":CARDID", cardid);
1332
1333 if (!query.exec() || !query.isActive())
1334 {
1335 MythDB::DBError("get_use_eit", query);
1336 return false;
1337 }
1338 else if (query.next())
1339 return query.value(0).toBool();
1340 return false;
1341}
1342
1343static bool is_dishnet_eit(uint cardid)
1344{
1345 MSqlQuery query(MSqlQuery::InitCon());
1346 query.prepare(
1347 "SELECT SUM(dishnet_eit) "
1348 "FROM videosource, cardinput "
1349 "WHERE videosource.sourceid = cardinput.sourceid AND"
1350 " cardinput.cardid = :CARDID");
1351 query.bindValue(":CARDID", cardid);
1352
1353 if (!query.exec() || !query.isActive())
1354 {
1355 MythDB::DBError("is_dishnet_eit", query);
1356 return false;
1357 }
1358 else if (query.next())
1359 return query.value(0).toBool();
1360 return false;
1361}
1362
1363static int no_capturecards(uint cardid)
1364{
1365 MSqlQuery query(MSqlQuery::InitCon());
1366
1367 QString str =
1368 "SELECT COUNT(cardid) "
1369 "FROM capturecard ";
1370
1371 if (cardid)
1372 str += "WHERE cardid < :CARDID";
1373
1374 query.prepare(str);
1375
1376 if (cardid)
1377 query.bindValue(":CARDID", cardid);
1378
1379 if (!query.exec() || !query.isActive())
1380 {
1381 MythDB::DBError("no_capturecards", query);
1382 return -1;
1383 }
1384 else if (query.next())
1385 return query.value(0).toInt();
1386 return -1;
1387}
1388
1389/** \fn TVRec::RunTV(void)
1390 * \brief Event handling method, contains event loop.
1391 */
1392void TVRec::RunTV(void)
1393{
1394 QMutexLocker lock(&stateChangeLock);
1395 SetFlags(kFlagRunMainLoop);
1396 ClearFlags(kFlagExitPlayer | kFlagFinishRecording);
1397
1398 eitScanStartTime = QDateTime::currentDateTime();
1399 // check whether we should use the EITScanner in this TVRec instance
1400 if (CardUtil::IsEITCapable(genOpt.cardtype) &&
1401 (!GetDVBChannel() || GetDVBChannel()->IsMaster()))
1402 {
1403 scanner = new EITScanner(cardid);
1404 uint timeout = eitCrawlIdleStart;
1405 // get the number of capture cards and the position of the current card
1406 // to distribute the the scan start evenly over eitTransportTimeout
1407 int card_pos = no_capturecards(cardid);
1408 int no_cards = no_capturecards(0);
1409 if (no_cards > 0 && card_pos >= 0)
1410 timeout += eitTransportTimeout * card_pos / no_cards;
1411 else
1412 timeout += random() % eitTransportTimeout;
1413
1414 eitScanStartTime = eitScanStartTime.addSecs(timeout);
1415 }
1416 else
1417 eitScanStartTime = eitScanStartTime.addYears(1);
1418
1419 while (HasFlags(kFlagRunMainLoop))
1420 {
1421 // If there is a state change queued up, do it...
1422 if (changeState)
1423 {
1424 HandleStateChange();
1425 ClearFlags(kFlagFrontendReady | kFlagCancelNextRecording);
1426 }
1427
1428 // Quick exit on fatal errors.
1429 if (IsErrored())
1430 {
1431 VERBOSE(VB_IMPORTANT, LOC_ERR +
1432 "RunTV encountered fatal error, exiting event thread.");
1433 ClearFlags(kFlagRunMainLoop);
1434 return;
1435 }
1436
1437 // Handle any tuning events..
1438 HandleTuning();
1439
1440 // Tell frontends about pending recordings
1441 HandlePendingRecordings();
1442
1443 // If we are recording a program, check if the recording is
1444 // over or someone has asked us to finish the recording.
1445 if (GetState() == kState_RecordingOnly &&
1446 (QDateTime::currentDateTime() > recordEndTime ||
1447 HasFlags(kFlagFinishRecording)))
1448 {
1449 ChangeState(kState_None);
1450 ClearFlags(kFlagFinishRecording);
1451 }
1452
1453 if (curRecording)
1454 {
1455 curRecording->UpdateInUseMark();
1456
1457 if (recorder)
1458 {
1459 recorder->SavePositionMap();
1460
1461 // Check for recorder errors
1462 if (recorder->IsErrored())
1463 {
1464 curRecording->SetRecordingStatus(rsFailed);
1465
1466 if (GetState() == kState_WatchingLiveTV)
1467 {
1468 QString message = QString("QUIT_LIVETV %1").arg(cardid);
1469 MythEvent me(message);
1470 gCoreContext->dispatch(me);
1471 }
1472 else
1473 ChangeState(kState_None);
1474 }
1475 }
1476 }
1477
1478 // Check for the end of the current program..
1479 if (GetState() == kState_WatchingLiveTV)
1480 {
1481 QDateTime now = QDateTime::currentDateTime();
1482 bool has_finish = HasFlags(kFlagFinishRecording);
1483 bool has_rec = pseudoLiveTVRecording;
1484 bool enable_ui = true;
1485
1486 pendingRecLock.lock();
1487 bool rec_soon =
1488 pendingRecordings.find(cardid) != pendingRecordings.end();
1489 pendingRecLock.unlock();
1490
1491 if (has_rec && (has_finish || (now > recordEndTime)))
1492 {
1493 if (pseudoLiveTVRecording && curRecording)
1494 {
1495 int secsSince = curRecording->GetRecordingStartTime()
1496 .secsTo(QDateTime::currentDateTime());
1497 if (secsSince < 120)
1498 {
1499 JobQueue::RemoveJobsFromMask(JOB_COMMFLAG,
1500 autoRunJobs);
1501 JobQueue::RemoveJobsFromMask(JOB_TRANSCODE,
1502 autoRunJobs);
1503 }
1504
1505 if (autoRunJobs)
1506 {
1507 JobQueue::QueueRecordingJobs(
1508 *curRecording, autoRunJobs);
1509 }
1510 }
1511
1512 SetPseudoLiveTVRecording(NULL);
1513 }
1514 else if (!has_rec && !rec_soon && curRecording &&
1515 (now >= curRecording->GetScheduledEndTime()))
1516 {
1517 if (!m_switchingBuffer)
1518 {
1519 m_switchingBuffer = true;
1520
1521 SwitchLiveTVRingBuffer(channel->GetCurrentName(),
1522 false, true);
1523
1524 QDateTime starttime; starttime.setTime_t(0);
1525 if (curRecording)
1526 starttime = curRecording->GetRecordingStartTime();
1527
1528 VERBOSE(VB_RECORD, LOC
1529 <<"!has_rec("<<!has_rec<<") "
1530 <<"!rec_soon("<<!rec_soon<<") "
1531 <<"curRec("<<curRecording<<") "
1532 <<"starttm("
1533 <<starttime.toString(Qt::ISODate)<<")");
1534 }
1535 else
1536 {
1537 VERBOSE(VB_RECORD, "Waiting for ringbuffer switch");
1538 }
1539 }
1540 else
1541 enable_ui = false;
1542
1543 if (enable_ui)
1544 {
1545 VERBOSE(VB_RECORD, LOC + "Enabling Full LiveTV UI.");
1546 QString message = QString("LIVETV_WATCH %1 0").arg(cardid);
1547 MythEvent me(message);
1548 gCoreContext->dispatch(me);
1549 }
1550 }
1551
1552 // Check for ExitPlayer flag, and if set change to a non-watching
1553 // state (either kState_RecordingOnly or kState_None).
1554 if (HasFlags(kFlagExitPlayer))
1555 {
1556 if (internalState == kState_WatchingLiveTV)
1557 ChangeState(kState_None);
1558 else if (StateIsPlaying(internalState))
1559 ChangeState(RemovePlaying(internalState));
1560 ClearFlags(kFlagExitPlayer);
1561 }
1562
1563 if (channel && scanner &&
1564 QDateTime::currentDateTime() > eitScanStartTime)
1565 {
1566 if (!dvbOpt.dvb_eitscan)
1567 {
1568 VERBOSE(VB_EIT, LOC + "EIT scanning disabled for this card.");
1569 eitScanStartTime = eitScanStartTime.addYears(1);
1570 }
1571 else if (!get_use_eit(GetCaptureCardNum()))
1572 {
1573 VERBOSE(VB_EIT, LOC + "EIT scanning disabled "
1574 "for all sources on this card.");
1575 eitScanStartTime = eitScanStartTime.addYears(1);
1576 }
1577 else
1578 {
1579 scanner->StartActiveScan(this, eitTransportTimeout);
1580 SetFlags(kFlagEITScannerRunning);
1581 eitScanStartTime = QDateTime::currentDateTime().addYears(1);
1582 }
1583 }
1584
1585 // We should be no more than a few thousand milliseconds,
1586 // as the end recording code does not have a trigger...
1587 // NOTE: If you change anything here, make sure that
1588 // WaitforEventThreadSleep() will still work...
1589 if (tuningRequests.empty() && !changeState)
1590 {
1591 lock.unlock(); // stateChangeLock
1592
1593 {
1594 QMutexLocker locker(&triggerEventSleepLock);
1595 triggerEventSleepSignal = true;
1596 triggerEventSleepWait.wakeAll();
1597 }
1598
1599 sched_yield();
1600
1601 {
1602 QMutexLocker locker(&triggerEventLoopLock);
1603 // We check triggerEventLoopSignal because it is possible
1604 // that WakeEventLoop() was called since we
1605 // unlocked the stateChangeLock
1606 if (!triggerEventLoopSignal)
1607 {
1608 triggerEventLoopWait.wait(
1609 &triggerEventLoopLock, 1000 /* ms */);
1610 }
1611 triggerEventLoopSignal = false;
1612 }
1613
1614 lock.relock(); // stateChangeLock
1615 }
1616 }
1617
1618 if (GetState() != kState_None)
1619 {
1620 ChangeState(kState_None);
1621 HandleStateChange();
1622 }
1623}
1624
1625/** \fn TVRec::WaitForEventThreadSleep(bool wake, ulong time)
1626 *
1627 * You MUST HAVE the stateChange-lock locked when you call this method!
1628 */
1629
1630bool TVRec::WaitForEventThreadSleep(bool wake, ulong time)
1631{
1632 bool ok = false;
1633 MythTimer t;
1634 t.start();
1635
1636 while (!ok && ((unsigned long) t.elapsed()) < time)
1637 {
1638 if (wake)
1639 WakeEventLoop();
1640
1641 stateChangeLock.unlock();
1642
1643 sched_yield();
1644
1645 {
1646 QMutexLocker locker(&triggerEventSleepLock);
1647 if (!triggerEventSleepSignal)
1648 triggerEventSleepWait.wait(&triggerEventSleepLock);
1649 triggerEventSleepSignal = false;
1650 }
1651
1652 stateChangeLock.lock();
1653
1654 // verify that we were triggered.
1655 ok = (tuningRequests.empty() && !changeState);
1656 }
1657 return ok;
1658}
1659
1660void TVRec::HandlePendingRecordings(void)
1661{
1662 QMutexLocker pendlock(&pendingRecLock);
1663
1664 if (pendingRecordings.empty())
1665 return;
1666
1667 // If we have a pending recording and AskAllowRecording
1668 // or DoNotAskAllowRecording is set and the frontend is
1669 // ready send an ASK_RECORDING query to frontend.
1670
1671 PendingMap::iterator it, next;
1672
1673 for (it = pendingRecordings.begin(); it != pendingRecordings.end();)
1674 {
1675 next = it; ++next;
1676 if (QDateTime::currentDateTime() > (*it).recordingStart.addSecs(30))
1677 {
1678 VERBOSE(VB_RECORD, LOC + "Deleting stale pending recording " +
1679 QString("%1 '%2'")
1680 .arg((*it).info->GetCardID())
1681 .arg((*it).info->GetTitle()));
1682
1683 delete (*it).info;
1684 pendingRecordings.erase(it);
1685 }
1686 it = next;
1687 }
1688
1689 bool has_rec = false;
1690 it = pendingRecordings.begin();
1691 if ((1 == pendingRecordings.size()) &&
1692 (*it).ask &&
1693 ((*it).info->GetCardID() == cardid) &&
1694 (GetState() == kState_WatchingLiveTV))
1695 {
1696 CheckForRecGroupChange();
1697 has_rec = pseudoLiveTVRecording &&
1698 (pseudoLiveTVRecording->GetRecordingEndTime() >
1699 (*it).recordingStart);
1700 }
1701
1702 for (it = pendingRecordings.begin(); it != pendingRecordings.end(); ++it)
1703 {
1704 if (!(*it).ask && !(*it).doNotAsk)
1705 continue;
1706
1707 int timeuntil = ((*it).doNotAsk) ?
1708 -1: QDateTime::currentDateTime().secsTo((*it).recordingStart);
1709
1710 if (has_rec)
1711 (*it).canceled = true;
1712
1713 QString query = QString("ASK_RECORDING %1 %2 %3 %4")
1714 .arg(cardid)
1715 .arg(timeuntil)
1716 .arg(has_rec ? 1 : 0)
1717 .arg((*it).hasLaterShowing ? 1 : 0);
1718
1719 VERBOSE(VB_IMPORTANT, LOC + query);
1720
1721 QStringList msg;
1722 (*it).info->ToStringList(msg);
1723 MythEvent me(query, msg);
1724 gCoreContext->dispatch(me);
1725
1726 (*it).ask = (*it).doNotAsk = false;
1727 }
1728}
1729
1730bool TVRec::GetDevices(uint cardid,
1731 GeneralDBOptions &gen_opts,
1732 DVBDBOptions &dvb_opts,
1733 FireWireDBOptions &firewire_opts)
1734{
1735 int testnum = 0;
1736 QString test;
1737
1738 MSqlQuery query(MSqlQuery::InitCon());
1739 query.prepare(
1740 "SELECT videodevice, vbidevice, audiodevice, "
1741 " audioratelimit, defaultinput, cardtype, "
1742 " skipbtaudio, signal_timeout, channel_timeout, "
1743 " dvb_wait_for_seqstart, "
1744 ""
1745 " dvb_on_demand, dvb_tuning_delay, dvb_eitscan,"
1746 ""
1747 " firewire_speed, firewire_model, firewire_connection "
1748 ""
1749 "FROM capturecard "
1750 "WHERE cardid = :CARDID");
1751 query.bindValue(":CARDID", cardid);
1752
1753 if (!query.exec() || !query.isActive())
1754 {
1755 MythDB::DBError("getdevices", query);
1756 return false;
1757 }
1758
1759 if (!query.next())
1760 return false;
1761
1762 // General options
1763 test = query.value(0).toString();
1764 if (test != QString::null)
1765 gen_opts.videodev = test;
1766
1767 test = query.value(1).toString();
1768 if (test != QString::null)
1769 gen_opts.vbidev = test;
1770
1771 test = query.value(2).toString();
1772 if (test != QString::null)
1773 gen_opts.audiodev = test;
1774
1775 gen_opts.audiosamplerate = max(testnum, query.value(3).toInt());
1776
1777 test = query.value(4).toString();
1778 if (test != QString::null)
1779 gen_opts.defaultinput = test;
1780
1781 test = query.value(5).toString();
1782 if (test != QString::null)
1783 gen_opts.cardtype = test;
1784
1785 gen_opts.skip_btaudio = query.value(6).toUInt();
1786
1787 gen_opts.signal_timeout = (uint) max(query.value(7).toInt(), 0);
1788 gen_opts.channel_timeout = (uint) max(query.value(8).toInt(), 0);
1789
1790 // We should have at least 100 ms to acquire tables...
1791 int table_timeout = ((int)gen_opts.channel_timeout -
1792 (int)gen_opts.signal_timeout);
1793 if (table_timeout < 100)
1794 gen_opts.channel_timeout = gen_opts.signal_timeout + 2500;
1795
1796 gen_opts.wait_for_seqstart = query.value(9).toUInt();
1797
1798 // DVB options
1799 uint dvboff = 10;
1800 dvb_opts.dvb_on_demand = query.value(dvboff + 0).toUInt();
1801 dvb_opts.dvb_tuning_delay = query.value(dvboff + 1).toUInt();
1802 dvb_opts.dvb_eitscan = query.value(dvboff + 2).toUInt();
1803
1804 // Firewire options
1805 uint fireoff = dvboff + 3;
1806 firewire_opts.speed = query.value(fireoff + 0).toUInt();
1807
1808 test = query.value(fireoff + 1).toString();
1809 if (test != QString::null)
1810 firewire_opts.model = test;
1811
1812 firewire_opts.connection = query.value(fireoff + 2).toUInt();
1813
1814 return true;
1815}
1816
1817QString TVRec::GetStartChannel(uint cardid, const QString &defaultinput)
1818{
1819 QString startchan = QString::null;
1820
1821 // Get last tuned channel from database, to use as starting channel
1822 MSqlQuery query(MSqlQuery::InitCon());
1823 query.prepare(
1824 "SELECT startchan "
1825 "FROM cardinput "
1826 "WHERE cardinput.cardid = :CARDID AND "
1827 " inputname = :INPUTNAME");
1828 query.bindValue(":CARDID", cardid);
1829 query.bindValue(":INPUTNAME", defaultinput);
1830
1831 if (!query.exec() || !query.isActive())
1832 {
1833 MythDB::DBError("getstartchan", query);
1834 }
1835 else if (query.next())
1836 {
1837 startchan = query.value(0).toString();
1838 if (!startchan.isEmpty())
1839 {
1840 VERBOSE(VB_CHANNEL, LOC + QString("Start channel: %1.")
1841 .arg(startchan));
1842 return startchan;
1843 }
1844 }
1845
1846 // If we failed to get the last tuned channel,
1847 // get a valid channel on our current input.
1848 query.prepare(
1849 "SELECT channum "
1850 "FROM capturecard, cardinput, channel "
1851 "WHERE capturecard.cardid = cardinput.cardid AND "
1852 " channel.sourceid = cardinput.sourceid AND "
1853 " capturecard.cardid = :CARDID AND "
1854 " inputname = :INPUTNAME");
1855 query.bindValue(":CARDID", cardid);
1856 query.bindValue(":INPUTNAME", defaultinput);
1857
1858 if (!query.exec() || !query.isActive())
1859 {
1860 MythDB::DBError("getstartchan2", query);
1861 }
1862 while (query.next())
1863 {
1864 startchan = query.value(0).toString();
1865 if (!startchan.isEmpty())
1866 {
1867 VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Start channel from DB is "
1868 "empty, setting to '%1' instead.").arg(startchan));
1869 return startchan;
1870 }
1871 }
1872
1873 // If we failed to get a channel on our current input,
1874 // widen search to any input.
1875 query.prepare(
1876 "SELECT channum, inputname "
1877 "FROM capturecard, cardinput, channel "
1878 "WHERE capturecard.cardid = cardinput.cardid AND "
1879 " channel.sourceid = cardinput.sourceid AND "
1880 " capturecard.cardid = :CARDID");
1881 query.bindValue(":CARDID", cardid);
1882
1883 if (!query.exec() || !query.isActive())
1884 {
1885 MythDB::DBError("getstartchan3", query);
1886 }
1887 while (query.next())
1888 {
1889 startchan = query.value(0).toString();
1890 if (!startchan.isEmpty())
1891 {
1892 VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Start channel invalid, "
1893 "setting to '%1' on input %2 instead.").arg(startchan)
1894 .arg(query.value(1).toString()));
1895 return startchan;
1896 }
1897 }
1898
1899 // If there are no valid channels, just use a random channel
1900 startchan = "3";
1901 VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Problem finding starting channel, "
1902 "setting to default of '%1'.").arg(startchan));
1903 return startchan;
1904}
1905
1906static void GetPidsToCache(DTVSignalMonitor *dtvMon, pid_cache_t &pid_cache)
1907{
1908 if (!dtvMon->GetATSCStreamData())
1909 return;
1910
1911 const MasterGuideTable *mgt = dtvMon->GetATSCStreamData()->GetCachedMGT();
1912 if (!mgt)
1913 return;
1914
1915 for (uint i = 0; i < mgt->TableCount(); ++i)
1916 {
1917 pid_cache_item_t item(mgt->TablePID(i), mgt->TableType(i));
1918 pid_cache.push_back(item);
1919 }
1920 dtvMon->GetATSCStreamData()->ReturnCachedTable(mgt);
1921}
1922
1923static bool ApplyCachedPids(DTVSignalMonitor *dtvMon, const DTVChannel* channel)
1924{
1925 pid_cache_t pid_cache;
1926 channel->GetCachedPids(pid_cache);
1927 pid_cache_t::const_iterator it = pid_cache.begin();
1928 bool vctpid_cached = false;
1929 for (; it != pid_cache.end(); ++it)
1930 {
1931 if ((it->second == TableID::TVCT) ||
1932 (it->second == TableID::CVCT))
1933 {
1934 vctpid_cached = true;
1935 dtvMon->GetATSCStreamData()->AddListeningPID(it->first);
1936 }
1937 }
1938 return vctpid_cached;
1939}
1940
1941/** \fn bool TVRec::SetupDTVSignalMonitor(void)
1942 * \brief Tells DTVSignalMonitor what channel to look for.
1943 *
1944 * If the major and minor channels are set we tell the signal
1945 * monitor to look for those in the VCT.
1946 *
1947 * Otherwise, we tell the signal monitor to look for the MPEG
1948 * program number in the PAT.
1949 *
1950 * This method also grabs the ATSCStreamData() from the recorder
1951 * if possible, or creates one if needed.
1952 */
1953bool TVRec::SetupDTVSignalMonitor(bool EITscan)
1954{
1955 VERBOSE(VB_RECORD, LOC + "Setting up table monitoring.");
1956
1957 DTVSignalMonitor *sm = GetDTVSignalMonitor();
1958 DTVChannel *dtvchan = GetDTVChannel();
1959 if (!sm || !dtvchan)
1960 {
1961 VERBOSE(VB_IMPORTANT, LOC_ERR + "Setting up table monitoring.");
1962 return false;
1963 }
1964
1965 MPEGStreamData *sd = NULL;
1966 if (GetDTVRecorder())
1967 {
1968 sd = GetDTVRecorder()->GetStreamData();
1969 sd->SetCaching(true);
1970 }
1971
1972 QString recording_type = "all";
1973 RecordingInfo *rec = lastTuningRequest.program;
1974 RecordingProfile profile;
1975 load_profile(genOpt.cardtype, tvchain, rec, profile);
1976 const Setting *setting = profile.byName("recordingtype");
1977 if (setting)
1978 recording_type = setting->getValue();
1979
1980 const QString tuningmode = dtvchan->GetTuningMode();
1981
1982 // Check if this is an ATSC Channel
1983 int major = dtvchan->GetMajorChannel();
1984 int minor = dtvchan->GetMinorChannel();
1985 if ((minor > 0) && (tuningmode == "atsc"))
1986 {
1987 QString msg = QString("ATSC channel: %1_%2").arg(major).arg(minor);
1988 VERBOSE(VB_RECORD, LOC + msg);
1989
1990 ATSCStreamData *asd = dynamic_cast<ATSCStreamData*>(sd);
1991 if (!asd)
1992 {
1993 sd = asd = new ATSCStreamData(major, minor);
1994 sd->SetCaching(true);
1995 if (GetDTVRecorder())
1996 GetDTVRecorder()->SetStreamData(asd);
1997 }
1998
1999 asd->Reset();
2000 sm->SetStreamData(sd);
2001 sm->SetChannel(major, minor);
2002 sd->SetRecordingType(recording_type);
2003
2004 // Try to get pid of VCT from cache and
2005 // require MGT if we don't have VCT pid.
2006 if (!ApplyCachedPids(sm, dtvchan))
2007 sm->AddFlags(SignalMonitor::kDTVSigMon_WaitForMGT);
2008
2009 VERBOSE(VB_RECORD, LOC + "Successfully set up ATSC table monitoring.");
2010 return true;
2011 }
2012
2013 // Check if this is an DVB channel
2014 int progNum = dtvchan->GetProgramNumber();
2015#ifdef USING_DVB
2016 if ((progNum >= 0) && (tuningmode == "dvb"))
2017 {
2018 int netid = dtvchan->GetOriginalNetworkID();
2019 int tsid = dtvchan->GetTransportID();
2020
2021 DVBStreamData *dsd = dynamic_cast<DVBStreamData*>(sd);
2022 if (!dsd)
2023 {
2024 sd = dsd = new DVBStreamData(netid, tsid, progNum);
2025 sd->SetCaching(true);
2026 if (GetDTVRecorder())
2027 GetDTVRecorder()->SetStreamData(dsd);
2028 }
2029
2030 VERBOSE(VB_RECORD, LOC +
2031 QString("DVB service_id %1 on net_id %2 tsid %3")
2032 .arg(progNum).arg(netid).arg(tsid));
2033
2034 // Some DVB devices munge the PMT and/or PAT so the CRC check fails.
2035 // We need to tell the stream data class to not check the CRC on
2036 // these devices.
2037 if (GetDVBChannel())
2038 sd->SetIgnoreCRC(GetDVBChannel()->HasCRCBug());
2039
2040 dsd->Reset();
2041 sm->SetStreamData(sd);
2042 sm->SetDVBService(netid, tsid, progNum);
2043 sd->SetRecordingType(recording_type);
2044
2045 sm->AddFlags(SignalMonitor::kDTVSigMon_WaitForPMT |
2046 SignalMonitor::kDTVSigMon_WaitForSDT |
2047 SignalMonitor::kDVBSigMon_WaitForPos);
2048 sm->SetRotorTarget(1.0f);
2049
2050 VERBOSE(VB_RECORD, LOC + "Successfully set up DVB table monitoring.");
2051 return true;
2052 }
2053#endif // USING_DVB
2054
2055 // Check if this is an MPEG channel
2056 if (progNum >= 0)
2057 {
2058 if (!sd)
2059 {
2060 sd = new MPEGStreamData(progNum, true);
2061 sd->SetCaching(true);
2062 if (GetDTVRecorder())
2063 GetDTVRecorder()->SetStreamData(sd);
2064 }
2065
2066 QString msg = QString("MPEG program number: %1").arg(progNum);
2067 VERBOSE(VB_RECORD, LOC + msg);
2068
2069#ifdef USING_DVB
2070 // Some DVB devices munge the PMT and/or PAT so the CRC check fails.
2071 // We need to tell the stream data class to not check the CRC on
2072 // these devices.
2073 if (GetDVBChannel())
2074 sd->SetIgnoreCRC(GetDVBChannel()->HasCRCBug());
2075#endif // USING_DVB
2076
2077 sd->Reset();
2078 sm->SetStreamData(sd);
2079 sm->SetProgramNumber(progNum);
2080 sd->SetRecordingType(recording_type);
2081
2082 sm->AddFlags(SignalMonitor::kDTVSigMon_WaitForPAT |
2083 SignalMonitor::kDTVSigMon_WaitForPMT |
2084 SignalMonitor::kDVBSigMon_WaitForPos);
2085 sm->SetRotorTarget(1.0f);
2086
2087 if (EITscan)
2088 {
2089 sm->GetStreamData()->SetVideoStreamsRequired(0);
2090 sm->IgnoreEncrypted(true);
2091 }
2092
2093 VERBOSE(VB_RECORD, LOC + "Successfully set up MPEG table monitoring.");
2094 return true;
2095 }
2096
2097 QString msg = "No valid DTV info, ATSC maj(%1) min(%2), MPEG pn(%3)";
2098 VERBOSE(VB_IMPORTANT, LOC_ERR + msg.arg(major).arg(minor).arg(progNum));
2099 return false;
2100}
2101
2102/** \fn TVRec::SetupSignalMonitor(bool,bool)
2103 * \brief This creates a SignalMonitor instance and
2104 * begins signal monitoring.
2105 *
2106 * If the channel exists a SignalMonitor instance is created and
2107 * SignalMonitor::Start() is called to start the signal monitoring thread.
2108 *
2109 * \param tablemon If set we enable table monitoring
2110 * \param notify If set we notify the frontend of the signal values
2111 * \return true on success, false on failure
2112 */
2113bool TVRec::SetupSignalMonitor(bool tablemon, bool EITscan, bool notify)
2114{
2115 VERBOSE(VB_RECORD, LOC + "SetupSignalMonitor("
2116 <<tablemon<<", "<<notify<<")");
2117
2118 // if it already exists, there no need to initialize it
2119 if (signalMonitor)
2120 return true;
2121
2122 // if there is no channel object we can't monitor it
2123 if (!channel)
2124 return false;
2125
2126 // nothing to monitor here either (DummyChannel)
2127 if (genOpt.cardtype == "IMPORT" || genOpt.cardtype == "DEMO")
2128 return true;
2129
2130 // make sure statics are initialized
2131 SignalMonitorValue::Init();
2132
2133 if (channel->Open())
2134 signalMonitor = SignalMonitor::Init(genOpt.cardtype, cardid,
2135 channel);
2136
2137 if (signalMonitor)
2138 {
2139 VERBOSE(VB_RECORD, LOC + "Signal monitor successfully created");
2140
2141 signalMonitor->SetMonitoring(this, EITscan,
2142 GetDTVSignalMonitor() && tablemon);
2143 signalMonitor->AddListener(this);
2144 signalMonitor->SetUpdateRate(kSignalMonitoringRate);
2145 signalMonitor->SetNotifyFrontend(notify);
2146
2147 // Start the monitoring thread
2148 signalMonitor->Start(true);
2149 }
2150
2151 return true;
2152}
2153
2154/** \fn TVRec::TeardownSignalMonitor()
2155 * \brief If a SignalMonitor instance exists, the monitoring thread is
2156 * stopped and the instance is deleted.
2157 */
2158void TVRec::TeardownSignalMonitor()
2159{
2160 if (!signalMonitor)
2161 return;
2162
2163 VERBOSE(VB_RECORD, LOC + "TeardownSignalMonitor() -- begin");
2164
2165 // If this is a DTV signal monitor, save any pids we know about.
2166 DTVSignalMonitor *dtvMon = GetDTVSignalMonitor();
2167 DTVChannel *dtvChan = GetDTVChannel();
2168 if (dtvMon && dtvChan)
2169 {
2170 pid_cache_t pid_cache;
2171 GetPidsToCache(dtvMon, pid_cache);
2172 if (pid_cache.size())
2173 dtvChan->SaveCachedPids(pid_cache);
2174 }
2175
2176 if (signalMonitor)
2177 {
2178 delete signalMonitor;
2179 signalMonitor = NULL;
2180 }
2181
2182 VERBOSE(VB_RECORD, LOC + "TeardownSignalMonitor() -- end");
2183}
2184
2185/** \fn TVRec::SetSignalMonitoringRate(int,int)
2186 * \brief Sets the signal monitoring rate.
2187 *
2188 * \sa EncoderLink::SetSignalMonitoringRate(int,int),
2189 * RemoteEncoder::SetSignalMonitoringRate(int,int)
2190 * \param rate The update rate to use in milliseconds,
2191 * use 0 to disable signal monitoring.
2192 * \param notifyFrontend If 1, SIGNAL messages will be sent to
2193 * the frontend using this recorder.
2194 * \return 1 if it signal monitoring is turned on, 0 otherwise.
2195 */
2196int TVRec::SetSignalMonitoringRate(int rate, int notifyFrontend)
2197{
2198 QString msg = "SetSignalMonitoringRate(%1, %2)";
2199 VERBOSE(VB_RECORD, LOC + msg.arg(rate).arg(notifyFrontend) + "-- start");
2200
2201 QMutexLocker lock(&stateChangeLock);
2202
2203 if (!SignalMonitor::IsSupported(genOpt.cardtype))
2204 {
2205 VERBOSE(VB_IMPORTANT, LOC + "Signal Monitoring is not"
2206 "supported by your hardware.");
2207 return 0;
2208 }
2209
2210 if (GetState() != kState_WatchingLiveTV)
2211 {
2212 VERBOSE(VB_IMPORTANT, LOC + "Signal can only "
2213 "be monitored in LiveTV Mode.");
2214 return 0;
2215 }
2216
2217 ClearFlags(kFlagRingBufferReady);
2218
2219 TuningRequest req = (rate > 0) ?
2220 TuningRequest(kFlagAntennaAdjust, channel->GetCurrentName()) :
2221 TuningRequest(kFlagLiveTV);
2222
2223 tuningRequests.enqueue(req);
2224
2225 // Wait for RingBuffer reset
2226 while (!HasFlags(kFlagRingBufferReady))
2227 WaitForEventThreadSleep();
2228 VERBOSE(VB_RECORD, LOC + msg.arg(rate).arg(notifyFrontend) + " -- end");
2229 return 1;
2230}
2231
2232DTVSignalMonitor *TVRec::GetDTVSignalMonitor(void)
2233{
2234 return dynamic_cast<DTVSignalMonitor*>(signalMonitor);
2235}
2236
2237/** \fn TVRec::ShouldSwitchToAnotherCard(QString)
2238 * \brief Checks if named channel exists on current tuner, or
2239 * another tuner.
2240 *
2241 * \param chanid channel to verify against tuners.
2242 * \return true if the channel on another tuner and not current tuner,
2243 * false otherwise.
2244 * \sa EncoderLink::ShouldSwitchToAnotherCard(const QString&),
2245 * RemoteEncoder::ShouldSwitchToAnotherCard(QString),
2246 * CheckChannel(QString)
2247 */
2248bool TVRec::ShouldSwitchToAnotherCard(QString chanid)
2249{
2250 QString msg("");
2251 MSqlQuery query(MSqlQuery::InitCon());
2252
2253 if (!query.isConnected())
2254 return false;
2255
2256 query.prepare("SELECT channel.channum, channel.callsign "
2257 "FROM channel "
2258 "WHERE channel.chanid = :CHANID");
2259 query.bindValue(":CHANID", chanid);
2260 if (!query.exec() || !query.isActive() || query.size() == 0)
2261 {
2262 MythDB::DBError("ShouldSwitchToAnotherCard", query);
2263 return false;
2264 }
2265
2266 query.next();
2267 QString channelname = query.value(0).toString();
2268 QString callsign = query.value(1).toString();
2269
2270 query.prepare(
2271 "SELECT channel.channum "
2272 "FROM channel,cardinput "
2273 "WHERE ( channel.chanid = :CHANID OR "
2274 " ( channel.channum = :CHANNUM AND "
2275 " channel.callsign = :CALLSIGN ) "
2276 " ) AND "
2277 " channel.sourceid = cardinput.sourceid AND "
2278 " cardinput.cardid = :CARDID");
2279 query.bindValue(":CHANID", chanid);
2280 query.bindValue(":CHANNUM", channelname);
2281 query.bindValue(":CALLSIGN", callsign);
2282 query.bindValue(":CARDID", cardid);
2283
2284 if (!query.exec() || !query.isActive())
2285 {
2286 MythDB::DBError("ShouldSwitchToAnotherCard", query);
2287 }
2288 else if (query.size() > 0)
2289 {
2290 msg = "Found channel (%1) on current card(%2).";
2291 VERBOSE(VB_RECORD, LOC + msg.arg(channelname).arg(cardid));
2292 return false;
2293 }
2294
2295 // We didn't find it on the current card, so now we check other cards.
2296 query.prepare(
2297 "SELECT channel.channum, cardinput.cardid "
2298 "FROM channel,cardinput "
2299 "WHERE ( channel.chanid = :CHANID OR "
2300 " ( channel.channum = :CHANNUM AND "
2301 " channel.callsign = :CALLSIGN ) "
2302 " ) AND "
2303 " channel.sourceid = cardinput.sourceid AND "
2304 " cardinput.cardid != :CARDID");
2305 query.bindValue(":CHANID", chanid);
2306 query.bindValue(":CHANNUM", channelname);
2307 query.bindValue(":CALLSIGN", callsign);
2308 query.bindValue(":CARDID", cardid);
2309
2310 if (!query.exec() || !query.isActive())
2311 {
2312 MythDB::DBError("ShouldSwitchToAnotherCard", query);
2313 }
2314 else if (query.next())
2315 {
2316 msg = QString("Found channel (%1) on different card(%2).")
2317 .arg(query.value(0).toString()).arg(query.value(1).toString());
2318 VERBOSE(VB_RECORD, LOC + msg);
2319 return true;
2320 }
2321
2322 msg = QString("Did not find channel(%1) on any card.").arg(channelname);
2323 VERBOSE(VB_RECORD, LOC + msg);
2324 return false;
2325}
2326
2327/** \fn TVRec::CheckChannel(QString) const
2328 * \brief Checks if named channel exists on current tuner.
2329 *
2330 * \param name channel to verify against current tuner.
2331 * \return true if it succeeds, false otherwise.
2332 * \sa EncoderLink::CheckChannel(const QString&),
2333 * RemoteEncoder::CheckChannel(QString),
2334 * CheckChannel(ChannelBase*,const QString&,QString&),
2335 * ShouldSwitchToAnotherCard(QString)
2336 */
2337bool TVRec::CheckChannel(QString name) const
2338{
2339 if (!channel)
2340 return false;
2341
2342 QString dummyID;
2343 return channel->CheckChannel(name, dummyID);
2344}
2345
2346/** \fn QString add_spacer(const QString&, const QString&)
2347 * \brief Adds the spacer before the last character in chan.
2348 */
2349static QString add_spacer(const QString &channel, const QString &spacer)
2350{
2351 QString chan = channel;
2352 chan.detach();
2353 if ((chan.length() >= 2) && !spacer.isEmpty())
2354 return chan.left(chan.length()-1) + spacer + chan.right(1);
2355 return chan;
2356}
2357
2358/** \fn TVRec::CheckChannelPrefix(const QString&,uint&,bool&,QString&)
2359 * \brief Checks a prefix against the channels in the DB.
2360 *
2361 * If the prefix matches a channel on any recorder this function returns
2362 * true, otherwise it returns false.
2363 *
2364 * If the prefix matches any channel entirely (i.e. prefix == channum),
2365 * then the cardid of the recorder it matches is returned in
2366 * 'is_complete_valid_channel_on_rec'; if it matches multiple recorders,
2367 * and one of them is this recorder, this recorder is returned in
2368 * 'is_complete_valid_channel_on_rec'; if it isn't complete for any channel
2369 * on any recorder 'is_complete_valid_channel_on_rec' is set to zero.
2370 *
2371 * If adding another character could reduce the number of channels the
2372 * prefix matches 'is_extra_char_useful' is set to true, otherwise it
2373 * is set to false.
2374 *
2375 * Finally, if in order for the prefix to match a channel, a spacer needs
2376 * to be added, the first matching spacer is returned in needed_spacer.
2377 * If there is more than one spacer that might be employed and one of them
2378 * is used for the current recorder, and others are used for other
2379 * recorders, then the one for the current recorder is returned. The
2380 * spacer must be inserted before the last character of the prefix for
2381 * anything else returned from the function to be valid.
2382 *
2383 * \return true if this is a valid prefix for a channel, false otherwise
2384 */
2385bool TVRec::CheckChannelPrefix(const QString &prefix,
2386 uint &is_complete_valid_channel_on_rec,
2387 bool &is_extra_char_useful,
2388 QString &needed_spacer)
2389{
2390#if DEBUG_CHANNEL_PREFIX
2391 VERBOSE(VB_IMPORTANT, QString("CheckChannelPrefix(%1)").arg(prefix));
2392#endif
2393
2394 static const uint kSpacerListSize = 5;
2395 static const char* spacers[kSpacerListSize] = { "", "_", "-", "#", "." };
2396
2397 MSqlQuery query(MSqlQuery::InitCon());
2398 QString basequery = QString(
2399 "SELECT channel.chanid, channel.channum, cardinput.cardid "
2400 "FROM channel, capturecard, cardinput "
2401 "WHERE channel.channum LIKE '%1%' AND "
2402 " channel.sourceid = cardinput.sourceid AND "
2403 " cardinput.cardid = capturecard.cardid");
2404
2405 QString cardquery[2] =
2406 {
2407 QString(" AND capturecard.cardid = '%1'").arg(cardid),
2408 QString(" AND capturecard.cardid != '%1'").arg(cardid),
2409 };
2410
2411 vector<uint> fchanid;
2412 vector<QString> fchannum;
2413 vector<uint> fcardid;
2414 vector<QString> fspacer;
2415
2416 for (uint i = 0; i < 2; i++)
2417 {
2418 for (uint j = 0; j < kSpacerListSize; j++)
2419 {
2420 QString qprefix = add_spacer(
2421 prefix, (QString(spacers[j]) == "_") ? "\\_" : spacers[j]);
2422 query.prepare(basequery.arg(qprefix) + cardquery[i]);
2423
2424 if (!query.exec() || !query.isActive())
2425 {
2426 MythDB::DBError("checkchannel -- locate channum", query);
2427 }
2428 else if (query.size())
2429 {
2430 while (query.next())
2431 {
2432 fchanid.push_back(query.value(0).toUInt());
2433 fchannum.push_back(query.value(1).toString());
2434 fcardid.push_back(query.value(2).toUInt());
2435 fspacer.push_back(spacers[j]);
2436#if DEBUG_CHANNEL_PREFIX
2437 VERBOSE(VB_IMPORTANT, QString("(%1,%2) Adding %3 rec %4")
2438 .arg(i).arg(j).arg(query.value(1).toString(),6)
2439 .arg(query.value(2).toUInt()));
2440#endif
2441 }
2442 }
2443
2444 if (prefix.length() < 2)
2445 break;
2446 }
2447 }
2448
2449 // Now process the lists for the info we need...
2450 is_extra_char_useful = false;
2451 is_complete_valid_channel_on_rec = 0;
2452 needed_spacer = "";
2453
2454 if (fchanid.size() == 0)
2455 return false;
2456
2457 if (fchanid.size() == 1) // Unique channel...
2458 {
2459 needed_spacer = fspacer[0];
2460 bool nc = (fchannum[0] != add_spacer(prefix, fspacer[0]));
2461
2462 is_complete_valid_channel_on_rec = (nc) ? 0 : fcardid[0];
2463 is_extra_char_useful = nc;
2464 return true;
2465 }
2466
2467 // If we get this far there is more than one channel
2468 // sharing the prefix we were given.
2469
2470 // Is an extra characher useful for disambiguation?
2471 is_extra_char_useful = false;
2472 for (uint i = 0; (i < fchannum.size()) && !is_extra_char_useful; i++)
2473 {
2474 is_extra_char_useful = (fchannum[i] != add_spacer(prefix, fspacer[i]));
2475#if DEBUG_CHANNEL_PREFIX
2476 VERBOSE(VB_IMPORTANT, "is_extra_char_useful("
2477 <<fchannum[i]<<"!="<<add_spacer(prefix, fspacer[i])
2478 <<"): "<<is_extra_char_useful);
2479#endif
2480 }
2481
2482 // Are any of the channels complete w/o spacer?
2483 // If so set is_complete_valid_channel_on_rec,
2484 // with a preference for our cardid.
2485 for (uint i = 0; i < fchannum.size(); i++)
2486 {
2487 if (fchannum[i] == prefix)
2488 {
2489 is_complete_valid_channel_on_rec = fcardid[i];
2490 if (fcardid[i] == (uint)cardid)
2491 break;
2492 }
2493 }
2494
2495 if (is_complete_valid_channel_on_rec)
2496 return true;
2497
2498 // Add a spacer, if one is needed to select a valid channel.
2499 bool spacer_needed = true;
2500 for (uint i = 0; (i < fspacer.size() && spacer_needed); i++)
2501 spacer_needed = !fspacer[i].isEmpty();
2502 if (spacer_needed)
2503 needed_spacer = fspacer[0];
2504
2505 // If it isn't useful to wait for more characters,
2506 // then try to commit to any true match immediately.
2507 for (uint i = 0; i < ((is_extra_char_useful) ? 0 : fchanid.size()); i++)
2508 {
2509 if (fchannum[i] == add_spacer(prefix, fspacer[i]))
2510 {
2511 needed_spacer = fspacer[i];
2512 is_complete_valid_channel_on_rec = fcardid[i];
2513 return true;
2514 }
2515 }
2516
2517 return true;
2518}
2519
2520bool TVRec::SetVideoFiltersForChannel(uint sourceid,
2521 const QString &channum)
2522{
2523 if (!recorder)
2524 return false;
2525
2526 QString videoFilters = ChannelUtil::GetVideoFilters(sourceid, channum);
2527 if (!videoFilters.isEmpty())
2528 {
2529 recorder->SetVideoFilters(videoFilters);
2530 return true;
2531 }
2532
2533 return false;
2534}
2535
2536/** \fn TVRec::IsReallyRecording()
2537 * \brief Returns true if frontend can consider the recorder started.
2538 * \sa IsRecording()
2539 */
2540bool TVRec::IsReallyRecording(void)
2541{
2542 return ((recorder && recorder->IsRecording()) ||
2543 HasFlags(kFlagDummyRecorderRunning));
2544}
2545
2546/** \fn TVRec::IsBusy(TunedInputInfo*,int) const
2547 * \brief Returns true if the recorder is busy, or will be within
2548 * the next time_buffer seconds.
2549 * \sa EncoderLink::IsBusy(TunedInputInfo*, int time_buffer)
2550 */
2551bool TVRec::IsBusy(TunedInputInfo *busy_input, int time_buffer) const
2552{
2553 TunedInputInfo dummy;
2554 if (!busy_input)
2555 busy_input = &dummy;
2556
2557 busy_input->Clear();
2558
2559 if (!channel)
2560 return false;
2561
2562 QStringList list = channel->GetConnectedInputs();
2563 if (list.empty())
2564 return false;
2565
2566 uint chanid = 0;
2567
2568 if (GetState() != kState_None)
2569 {
2570 busy_input->inputid = channel->GetCurrentInputNum();
2571 chanid = channel->GetChanID();
2572 }
2573
2574 PendingInfo pendinfo;
2575 bool has_pending;
2576 {
2577 pendingRecLock.lock();
2578 PendingMap::const_iterator it = pendingRecordings.find(cardid);
2579 has_pending = (it != pendingRecordings.end());
2580 if (has_pending)
2581 pendinfo = *it;
2582 pendingRecLock.unlock();
2583 }
2584
2585 if (!busy_input->inputid && has_pending)
2586 {
2587 int timeLeft = QDateTime::currentDateTime()
2588 .secsTo(pendinfo.recordingStart);
2589
2590 if (timeLeft <= time_buffer)
2591 {
2592 QString channum = QString::null, input = QString::null;
2593 if (pendinfo.info->QueryTuningInfo(channum, input))
2594 {
2595 busy_input->inputid = channel->GetInputByName(input);
2596 chanid = pendinfo.info->GetChanID();
2597 }
2598 }
2599 }
2600
2601 if (busy_input->inputid)
2602 {
2603 CardUtil::GetInputInfo(*busy_input);
2604 busy_input->chanid = chanid;
2605 busy_input->mplexid = ChannelUtil::GetMplexID(busy_input->chanid);
2606 busy_input->mplexid =
2607 (32767 == busy_input->mplexid) ? 0 : busy_input->mplexid;
2608 }
2609
2610 return busy_input->inputid;
2611}
2612
2613
2614/** \fn TVRec::GetFramerate()
2615 * \brief Returns recordering frame rate from the recorder.
2616 * \sa RemoteEncoder::GetFrameRate(), EncoderLink::GetFramerate(void),
2617 * RecorderBase::GetFrameRate()
2618 * \return Frames per second if query succeeds -1 otherwise.
2619 */
2620float TVRec::GetFramerate(void)
2621{
2622 QMutexLocker lock(&stateChangeLock);
2623
2624 if (recorder)
2625 return recorder->GetFrameRate();
2626 return -1.0f;
2627}
2628
2629/** \fn TVRec::GetFramesWritten()
2630 * \brief Returns number of frames written to disk by recorder.
2631 *
2632 * \sa EncoderLink::GetFramesWritten(), RemoteEncoder::GetFramesWritten()
2633 * \return Number of frames if query succeeds, -1 otherwise.
2634 */
2635long long TVRec::GetFramesWritten(void)
2636{
2637 QMutexLocker lock(&stateChangeLock);
2638
2639 if (recorder)
2640 return recorder->GetFramesWritten();
2641 return -1;
2642}
2643
2644/** \fn TVRec::GetFilePosition()
2645 * \brief Returns total number of bytes written by RingBuffer.
2646 *
2647 * \sa EncoderLink::GetFilePosition(), RemoteEncoder::GetFilePosition()
2648 * \return Bytes written if query succeeds, -1 otherwise.
2649 */
2650long long TVRec::GetFilePosition(void)
2651{
2652 QMutexLocker lock(&stateChangeLock);
2653
2654 if (ringBuffer)
2655 return ringBuffer->GetWritePosition();
2656 return -1;
2657}
2658
2659/** \brief Returns byte position in RingBuffer
2660 * of a keyframe according to recorder.
2661 *
2662 * \sa EncoderLink::GetKeyframePosition(uint64_t),
2663 * RemoteEncoder::GetKeyframePosition(uint64_t)
2664 * \return Byte position of keyframe if query succeeds, -1 otherwise.
2665 */
2666int64_t TVRec::GetKeyframePosition(uint64_t desired) const
2667{
2668 QMutexLocker lock(&stateChangeLock);
2669
2670 if (recorder)
2671 return recorder->GetKeyframePosition(desired);
2672 return -1;
2673}
2674
2675/**
2676 * \brief Returns byte position in RingBuffer of a keyframes
2677 * according to recorder.
2678 *
2679 * \sa EncoderLink::GetKeyframePositions(int64_t, int64_t, frm_pos_map_t&),
2680 * RemoteEncoder::GetKeyframePositions(int64_t, int64_t, frm_pos_map_t&)
2681 * \return Byte position of keyframe if query succeeds, -1 otherwise.
2682 */
2683bool TVRec::GetKeyframePositions(
2684 int64_t start, int64_t end, frm_pos_map_t &map) const
2685{
2686 QMutexLocker lock(&stateChangeLock);
2687
2688 if (recorder)
2689 return recorder->GetKeyframePositions(start, end, map);
2690
2691 return false;
2692}
2693
2694/** \fn TVRec::GetMaxBitrate(void) const
2695 * \brief Returns the maximum bits per second this recorder can produce.
2696 *
2697 * \sa EncoderLink::GetMaxBitrate(void), RemoteEncoder::GetMaxBitrate(void)
2698 */
2699long long TVRec::GetMaxBitrate(void) const
2700{
2701 long long bitrate;
2702 if (genOpt.cardtype == "MPEG")
2703 bitrate = 10080000LL; // use DVD max bit rate
2704 if (genOpt.cardtype == "HDPVR")
2705 bitrate = 20200000LL; // Peek bit rate for HD-PVR
2706 else if (!CardUtil::IsEncoder(genOpt.cardtype))
2707 bitrate = 22200000LL; // 1080i
2708 else // frame grabber
2709 bitrate = 10080000LL; // use DVD max bit rate, probably too big
2710
2711 return bitrate;
2712}
2713
2714/** \fn TVRec::SpawnLiveTV(LiveTVChain*,bool,QString)
2715 * \brief Tells TVRec to spawn a "Live TV" recorder.
2716 * \sa EncoderLink::SpawnLiveTV(LiveTVChain*,bool,QString),
2717 * RemoteEncoder::SpawnLiveTV(QString,bool,QSting)
2718 */
2719void TVRec::SpawnLiveTV(LiveTVChain *newchain, bool pip, QString startchan)
2720{
2721 QMutexLocker lock(&stateChangeLock);
2722
2723 tvchain = newchain;
2724 tvchain->ReloadAll();
2725
2726 QString hostprefix = QString("myth://%1:%2/")
2727 .arg(gCoreContext->GetSetting("BackendServerIP"))
2728 .arg(gCoreContext->GetSetting("BackendServerPort"));
2729
2730 tvchain->SetHostPrefix(hostprefix);
2731 tvchain->SetCardType(genOpt.cardtype);
2732
2733 ispip = pip;
2734 LiveTVStartChannel = startchan;
2735
2736 // Change to WatchingLiveTV
2737 ChangeState(kState_WatchingLiveTV);
2738 // Wait for state change to take effect
2739 WaitForEventThreadSleep();
2740
2741 // Make sure StartRecording can't steal our tuner
2742 SetFlags(kFlagCancelNextRecording);
2743}
2744
2745/** \fn TVRec::GetChainID()
2746 * \brief Get the chainid of the livetv instance
2747 */
2748QString TVRec::GetChainID(void)
2749{
2750 if (tvchain)
2751 return tvchain->GetID();
2752 return "";
2753}
2754
2755/** \fn TVRec::CheckForRecGroupChange(void)
2756 * \brief Check if frontend changed the recording group.
2757 *
2758 * This is needed because the frontend may toggle whether something
2759 * should be kept as a recording in the frontend, but this class may
2760 * not find out about it in time unless we check the DB when this
2761 * information is important.
2762 */
2763void TVRec::CheckForRecGroupChange(void)
2764{
2765 QMutexLocker lock(&stateChangeLock);
2766
2767 if (internalState == kState_None)
2768 return; // already stopped
2769
2770 if (!curRecording)
2771 return;
2772
2773 const QString recgrp = curRecording->QueryRecordingGroup();
2774 curRecording->SetRecordingGroup(recgrp);
2775
2776 if (recgrp != "LiveTV" && !pseudoLiveTVRecording)
2777 {
2778 // User wants this recording to continue
2779 SetPseudoLiveTVRecording(new ProgramInfo(*curRecording));
2780 }
2781 else if (recgrp == "LiveTV" && pseudoLiveTVRecording)
2782 {
2783 // User wants to abandon scheduled recording
2784 SetPseudoLiveTVRecording(NULL);
2785 }
2786}
2787
2788static uint get_input_id(uint cardid, const QString &inputname)
2789{
2790 MSqlQuery query(MSqlQuery::InitCon());
2791
2792 query.prepare(
2793 "SELECT cardinputid "
2794 "FROM cardinput "
2795 "WHERE cardid = :CARDID AND "
2796 " inputname = :INNAME");
2797
2798 query.bindValue(":CARDID", cardid);
2799 query.bindValue(":INNAME", inputname);
2800
2801 if (!query.exec() || !query.isActive())
2802 MythDB::DBError("get_input_id", query);
2803 else if (query.next())
2804 return query.value(0).toUInt();
2805
2806 return 0;
2807}
2808
2809/** \fn TVRec::NotifySchedulerOfRecording(RecordingInfo*)
2810 * \brief Tell scheduler about the recording.
2811 *
2812 * This is needed if the frontend has marked the LiveTV
2813 * buffer for recording after we exit LiveTV. In this case
2814 * the scheduler needs to know about the recording so it
2815 * can properly take overrecord into account, and to properly
2816 * reschedule other recordings around to avoid this recording.
2817 */
2818void TVRec::NotifySchedulerOfRecording(RecordingInfo *rec)
2819{
2820 if (!channel)
2821 return;
2822
2823 // Notify scheduler of the recording.
2824 // + set up recording so it can be resumed
2825 rec->SetCardID(cardid);
2826 rec->SetInputID(get_input_id(cardid, channel->GetCurrentInput()));
2827 rec->SetRecordingRuleType(rec->GetRecordingRule()->m_type);
2828
2829 if (rec->GetRecordingRuleType() == kNotRecording)
2830 {
2831 rec->SetRecordingRuleType(kSingleRecord);
2832 rec->GetRecordingRule()->m_type = kSingleRecord;
2833 }
2834
2835 // + remove DefaultEndOffset which would mismatch the live session
2836 rec->GetRecordingRule()->m_endOffset = 0;
2837
2838 // + save rsInactive recstatus to so that a reschedule call
2839 // doesn't start recording this on another card before we
2840 // send the SCHEDULER_ADD_RECORDING message to the scheduler.
2841 rec->SetRecordingStatus(rsInactive);
2842 rec->AddHistory(false);
2843
2844 // + save RecordingRule so that we get a recordid
2845 // (don't allow signalChange(), avoiding unneeded reschedule)
2846 rec->GetRecordingRule()->Save(false);
2847
2848 // + save recordid to recorded entry
2849 rec->ApplyRecordRecID();
2850
2851 // + set proper recstatus (saved later)
2852 rec->SetRecordingStatus(rsRecording);
2853
2854 // + pass proginfo to scheduler and reschedule
2855 QStringList prog;
2856 rec->ToStringList(prog);
2857 MythEvent me("SCHEDULER_ADD_RECORDING", prog);
2858 gCoreContext->dispatch(me);
2859
2860 // Allow scheduler to end this recording before post-roll,
2861 // if it has another recording for this recorder.
2862 ClearFlags(kFlagCancelNextRecording);
2863}
2864
2865/** \fn TVRec::SetLiveRecording(int)
2866 * \brief Tells the Scheduler about changes to the recording status
2867 * of the LiveTV recording.
2868 *
2869 * NOTE: Currently the 'recording' parameter is ignored and decisions
2870 * are based on the recording group alone.
2871 *
2872 * \param recording Set to 1 to mark as rsRecording, set to 0 to mark as
2873 * rsCancelled, and set to -1 to base the decision of the recording
2874 * group.
2875 */
2876void TVRec::SetLiveRecording(int recording)
2877{
2878 VERBOSE(VB_IMPORTANT, LOC + "SetLiveRecording("<<recording<<")");
2879 QMutexLocker locker(&stateChangeLock);
2880
2881 (void) recording;
2882
2883 RecStatusType recstat = rsCancelled;
2884 bool was_rec = pseudoLiveTVRecording;
2885 CheckForRecGroupChange();
2886 if (was_rec && !pseudoLiveTVRecording)
2887 {
2888 VERBOSE(VB_IMPORTANT, LOC + "SetLiveRecording() -- cancel");
2889 // cancel -- 'recording' should be 0 or -1
2890 SetFlags(kFlagCancelNextRecording);
2891 curRecording->SetRecordingGroup("LiveTV");
2892 autoRunJobs = JOB_NONE;
2893 }
2894 else if (!was_rec && pseudoLiveTVRecording)
2895 {
2896 VERBOSE(VB_IMPORTANT, LOC + "SetLiveRecording() -- record");
2897 // record -- 'recording' should be 1 or -1
2898
2899 // If the last recording was flagged for keeping
2900 // in the frontend, then add the recording rule
2901 // so that transcode, commfrag, etc can be run.
2902 recordEndTime = GetRecordEndTime(pseudoLiveTVRecording);
2903 NotifySchedulerOfRecording(curRecording);
2904 recstat = curRecording->GetRecordingStatus();
2905 curRecording->SetRecordingGroup("Default");
2906
2907 RecordingProfile profile;
2908 load_profile(genOpt.cardtype, NULL, curRecording, profile);
2909 autoRunJobs = init_jobs(curRecording, profile, runJobOnHostOnly,
2910 transcodeFirst, earlyCommFlag);
2911 }
2912
2913 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
2914 .arg(curRecording->GetCardID())
2915 .arg(curRecording->GetChanID())
2916 .arg(curRecording->GetScheduledStartTime(ISODate))
2917 .arg(recstat)
2918 .arg(curRecording->GetRecordingEndTime(ISODate)));
2919
2920 gCoreContext->dispatch(me);
2921}
2922
2923/** \fn TVRec::StopLiveTV(void)
2924 * \brief Tells TVRec to stop a "Live TV" recorder.
2925 * \sa EncoderLink::StopLiveTV(), RemoteEncoder::StopLiveTV()
2926 */
2927void TVRec::StopLiveTV(void)
2928{
2929 QMutexLocker lock(&stateChangeLock);
2930 VERBOSE(VB_RECORD, LOC + "StopLiveTV(void) curRec: "<<curRecording
2931 <<" pseudoRec: "<<pseudoLiveTVRecording);
2932
2933 if (internalState != kState_WatchingLiveTV)
2934 return;
2935
2936 bool hadPseudoLiveTVRec = pseudoLiveTVRecording;
2937 CheckForRecGroupChange();
2938
2939 if (!hadPseudoLiveTVRec && pseudoLiveTVRecording)
2940 NotifySchedulerOfRecording(curRecording);
2941
2942 // Figure out next state and if needed recording end time.
2943 TVState next_state = kState_None;
2944 if (pseudoLiveTVRecording)
2945 {
2946 recordEndTime = GetRecordEndTime(pseudoLiveTVRecording);
2947 next_state = kState_RecordingOnly;
2948 }
2949
2950 // Change to the appropriate state
2951 ChangeState(next_state);
2952
2953 // Wait for state change to take effect...
2954 WaitForEventThreadSleep();
2955
2956 // We are done with the tvchain...
2957 tvchain = NULL;
2958}
2959
2960/** \fn TVRec::PauseRecorder(void)
2961 * \brief Tells "recorder" to pause, used for channel and input changes.
2962 *
2963 * When the RecorderBase instance has paused it calls RecorderPaused(void)
2964 *
2965 * \sa EncoderLink::PauseRecorder(void), RemoteEncoder::PauseRecorder(void),
2966 * RecorderBase::Pause(void)
2967 */
2968void TVRec::PauseRecorder(void)
2969{
2970 QMutexLocker lock(&stateChangeLock);
2971
2972 if (!recorder)
2973 {
2974 VERBOSE(VB_IMPORTANT, LOC + "PauseRecorder() "
2975 "called with no recorder");
2976 return;
2977 }
2978
2979 recorder->Pause();
2980}
2981
2982/** \fn TVRec::RecorderPaused(void)
2983 * \brief This is a callback, called by the "recorder" instance when
2984 * it has actually paused.
2985 * \sa PauseRecorder(void)
2986 */
2987void TVRec::RecorderPaused(void)
2988{
2989 if (pauseNotify)
2990 WakeEventLoop();
2991}
2992
2993/**
2994 * \brief Toggles whether the current channel should be on our favorites list.
2995 */
2996void TVRec::ToggleChannelFavorite(QString changroupname)
2997{
2998 QMutexLocker lock(&stateChangeLock);
2999
3000 if (!channel)
3001 return;
3002
3003 // Get current channel id...
3004 uint sourceid = channel->GetCurrentSourceID();
3005 QString channum = channel->GetCurrentName();
3006 uint chanid = ChannelUtil::GetChanID(sourceid, channum);
3007
3008 if (!chanid)
3009 {
3010 VERBOSE(VB_IMPORTANT, LOC_ERR + QString(
3011 "Channel: \'%1\' was not found in the database.\n"
3012 "\t\t\tMost likely, your DefaultTVChannel setting is wrong.\n"
3013 "\t\t\tCould not toggle favorite.").arg(channum));
3014 return;
3015 }
3016
3017 int changrpid;
3018 bool result;
3019
3020 changrpid = ChannelGroup::GetChannelGroupId(changroupname);
3021
3022 if (changrpid <1)
3023 {
3024 VERBOSE(VB_RECORD, LOC + QString("ToggleChannelFavorite: Invalid "
3025 "channel group name %1, ").arg(changroupname));
3026 }
3027 else
3028 {
3029 result = ChannelGroup::ToggleChannel(chanid, changrpid, true);
3030
3031 if (!result)
3032 VERBOSE(VB_RECORD, LOC + "Unable to toggle channel favorite.");
3033 else
3034 VERBOSE(VB_RECORD, LOC + QString("Toggled channel favorite."
3035 "channum %1, chan group %2").arg(channum).arg(changroupname));
3036 }
3037}
3038
3039/** \fn TVRec::ChangePictureAttribute(PictureAdjustType,PictureAttribute,bool)
3040 * \brief Returns current value [0,100] if it succeeds, -1 otherwise.
3041 *
3042 * Note: In practice this only works with frame grabbing recorders.
3043 */
3044int TVRec::GetPictureAttribute(PictureAttribute attr)
3045{
3046 QMutexLocker lock(&stateChangeLock);
3047 if (!channel)
3048 return -1;
3049
3050 int ret = channel->GetPictureAttribute(attr);
3051
3052 return (ret < 0) ? -1 : ret / 655;
3053}
3054
3055/** \fn TVRec::ChangePictureAttribute(PictureAdjustType,PictureAttribute,bool)
3056 * \brief Changes brightness/contrast/colour/hue of a recording.
3057 *
3058 * Note: In practice this only works with frame grabbing recorders.
3059 *
3060 * \return current value [0,100] if it succeeds, -1 otherwise.
3061 */
3062int TVRec::ChangePictureAttribute(PictureAdjustType type,
3063 PictureAttribute attr,
3064 bool direction)
3065{
3066 QMutexLocker lock(&stateChangeLock);
3067 if (!channel)
3068 return -1;
3069
3070 int ret = channel->ChangePictureAttribute(type, attr, direction);
3071
3072 return (ret < 0) ? -1 : ret / 655;
3073}
3074
3075/** \fn TVRec::GetFreeInputs(const vector<uint>&) const
3076 * \brief Returns the recorder's available inputs.
3077 *
3078 * This filters out the connected inputs that belong to an input
3079 * group which is busy. Recorders in the excluded cardids will
3080 * not be considered busy for the sake of determining free inputs.
3081 *
3082 */
3083vector<InputInfo> TVRec::GetFreeInputs(
3084 const vector<uint> &excluded_cardids) const
3085{
3086 vector<InputInfo> list;
3087 if (channel)
3088 list = channel->GetFreeInputs(excluded_cardids);
3089 return list;
3090}
3091
3092/** \fn TVRec::GetInput(void) const
3093 * \brief Returns current input.
3094 */
3095QString TVRec::GetInput(void) const
3096{
3097 if (channel)
3098 return channel->GetCurrentInput();
3099 return QString::null;
3100}
3101
3102/** \fn TVRec::SetInput(QString, uint)
3103 * \brief Changes to the specified input.
3104 *
3105 * You must call PauseRecorder(void) before calling this.
3106 *
3107 * \param input Input to switch to, or "SwitchToNextInput".
3108 * \return input we have switched to
3109 */
3110QString TVRec::SetInput(QString input, uint requestType)
3111{
3112 QMutexLocker lock(&stateChangeLock);
3113 QString origIn = input;
3114 VERBOSE(VB_RECORD, LOC + "SetInput("<<input<<") -- begin");
3115
3116 if (!channel)
3117 {
3118 VERBOSE(VB_RECORD, LOC + "SetInput() -- end no channel class");
3119 return QString::null;
3120 }
3121
3122 input = (input == "SwitchToNextInput") ? channel->GetNextInput() : input;
3123
3124 if (input == channel->GetCurrentInput())
3125 {
3126 VERBOSE(VB_RECORD, LOC + "SetInput("<<origIn<<":"<<input
3127 <<") -- end nothing to do");
3128 return input;
3129 }
3130
3131 QString name = channel->GetNextInputStartChan();
3132
3133 // Detect tuning request type if needed
3134 if (requestType & kFlagDetect)
3135 {
3136 WaitForEventThreadSleep();
3137 requestType = lastTuningRequest.flags & (kFlagRec | kFlagNoRec);
3138 }
3139
3140 // Clear the RingBuffer reset flag, in case we wait for a reset below
3141 ClearFlags(kFlagRingBufferReady);
3142
3143 // Actually add the tuning request to the queue, and
3144 // then wait for it to start tuning
3145 tuningRequests.enqueue(TuningRequest(requestType, name, input));
3146 WaitForEventThreadSleep();
3147
3148 // If we are using a recorder, wait for a RingBuffer reset
3149 if (requestType & kFlagRec)
3150 {
3151 while (!HasFlags(kFlagRingBufferReady))
3152 WaitForEventThreadSleep();
3153 }
3154 VERBOSE(VB_RECORD, LOC + "SetInput("<<origIn<<":"<<input<<") -- end");
3155
3156 return GetInput();
3157}
3158
3159/** \fn TVRec::SetChannel(QString,uint)
3160 * \brief Changes to a named channel on the current tuner.
3161 *
3162 * You must call PauseRecorder() before calling this.
3163 *
3164 * \param name channum of channel to change to
3165 * \param requestType tells us what kind of request to actually send to
3166 * the tuning thread, kFlagDetect is usually sufficient
3167 */
3168void TVRec::SetChannel(QString name, uint requestType)
3169{
3170 QMutexLocker lock(&stateChangeLock);
3171 VERBOSE(VB_CHANNEL, LOC + QString("SetChannel(%1) -- begin").arg(name));
3172
3173 // Detect tuning request type if needed
3174 if (requestType & kFlagDetect)
3175 {
3176 WaitForEventThreadSleep();
3177 requestType = lastTuningRequest.flags & (kFlagRec | kFlagNoRec);
3178 }
3179
3180 // Clear the RingBuffer reset flag, in case we wait for a reset below
3181 ClearFlags(kFlagRingBufferReady);
3182
3183 // Actually add the tuning request to the queue, and
3184 // then wait for it to start tuning
3185 tuningRequests.enqueue(TuningRequest(requestType, name));
3186 WaitForEventThreadSleep();
3187
3188 // If we are using a recorder, wait for a RingBuffer reset
3189 if (requestType & kFlagRec)
3190 {
3191 while (!HasFlags(kFlagRingBufferReady))
3192 WaitForEventThreadSleep();
3193 }
3194 VERBOSE(VB_CHANNEL, LOC + QString("SetChannel(%1) -- end").arg(name));
3195}
3196
3197void TVRec::GetNextProgram(BrowseDirection direction,
3198 QString &title, QString &subtitle,
3199 QString &desc, QString &category,
3200 QString &starttime, QString &endtime,
3201 QString &callsign, QString &iconpath,
3202 QString &channum, uint &sourceChanid,
3203 QString &seriesid, QString &programid)
3204{
3205 QString compare = "<=";
3206 QString sortorder = "desc";
3207 uint chanid = 0;
3208
3209 if (sourceChanid)
3210 {
3211 chanid = sourceChanid;
3212
3213 if (BROWSE_UP == direction)
3214 chanid = channel->GetNextChannel(chanid, CHANNEL_DIRECTION_UP);
3215 else if (BROWSE_DOWN == direction)
3216 chanid = channel->GetNextChannel(chanid, CHANNEL_DIRECTION_DOWN);
3217 else if (BROWSE_FAVORITE == direction)
3218 chanid = channel->GetNextChannel(
3219 chanid, CHANNEL_DIRECTION_FAVORITE);
3220
3221 else if (BROWSE_LEFT == direction)
3222 {
3223 compare = "<";
3224 }
3225 else if (BROWSE_RIGHT == direction)
3226 {
3227 compare = ">";
3228 sortorder = "asc";
3229 }
3230 }
3231
3232 if (!chanid)
3233 {
3234 if (BROWSE_SAME == direction)
3235 chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_SAME);
3236 else if (BROWSE_UP == direction)
3237 chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_UP);
3238 else if (BROWSE_DOWN == direction)
3239 chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_DOWN);
3240 else if (BROWSE_FAVORITE == direction)
3241 chanid = channel->GetNextChannel(channum,
3242 CHANNEL_DIRECTION_FAVORITE);
3243 else if (BROWSE_LEFT == direction)
3244 {
3245 chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_SAME);
3246 compare = "<";
3247 }
3248 else if (BROWSE_RIGHT == direction)
3249 {
3250 chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_SAME);
3251 compare = ">";
3252 sortorder = "asc";
3253 }
3254 }
3255
3256 QString querystr = QString(
3257 "SELECT title, subtitle, description, category, "
3258 " starttime, endtime, callsign, icon, "
3259 " channum, seriesid, programid "
3260 "FROM program, channel "
3261 "WHERE program.chanid = channel.chanid AND "
3262 " channel.chanid = :CHANID AND "
3263 " starttime %1 :STARTTIME "
3264 "ORDER BY starttime %2 "
3265 "LIMIT 1").arg(compare).arg(sortorder);
3266
3267 MSqlQuery query(MSqlQuery::InitCon());
3268 query.prepare(querystr);
3269 query.bindValue(":CHANID", chanid);
3270 query.bindValue(":STARTTIME", starttime);
3271
3272 // Clear everything now in case either query fails.
3273 title = subtitle = desc = category = "";
3274 starttime = endtime = callsign = iconpath = "";
3275 channum = seriesid = programid = "";
3276 sourceChanid = 0;
3277
3278 // Try to get the program info
3279 if (!query.exec() && !query.isActive())
3280 {
3281 MythDB::DBError("GetNextProgram -- get program info", query);
3282 }
3283 else if (query.next())
3284 {
3285 title = query.value(0).toString();
3286 subtitle = query.value(1).toString();
3287 desc = query.value(2).toString();
3288 category = query.value(3).toString();
3289 starttime = query.value(4).toString();
3290 endtime = query.value(5).toString();
3291 callsign = query.value(6).toString();
3292 iconpath = query.value(7).toString();
3293 channum = query.value(8).toString();
3294 seriesid = query.value(9).toString();
3295 programid = query.value(10).toString();
3296 sourceChanid = chanid;
3297 return;
3298 }
3299
3300 // Couldn't get program info, so get the channel info instead
3301 query.prepare(
3302 "SELECT channum, callsign, icon "
3303 "FROM channel "
3304 "WHERE chanid = :CHANID");
3305 query.bindValue(":CHANID", chanid);
3306
3307 if (!query.exec() || !query.isActive())
3308 {
3309 MythDB::DBError("GetNextProgram -- get channel info", query);
3310 }
3311 else if (query.next())
3312 {
3313 sourceChanid = chanid;
3314 channum = query.value(0).toString();
3315 callsign = query.value(1).toString();
3316 iconpath = query.value(2).toString();
3317 }
3318}
3319
3320bool TVRec::GetChannelInfo(uint &chanid, uint &sourceid,
3321 QString &callsign, QString &channum,
3322 QString &channame, QString &xmltvid) const
3323{
3324 callsign = "";
3325 channum = "";
3326 channame = "";
3327 xmltvid = "";
3328
3329 if ((!chanid || !sourceid) && !channel)
3330 return false;
3331
3332 if (!chanid)
3333 chanid = (uint) max(channel->GetChanID(), 0);
3334
3335 if (!sourceid)
3336 sourceid = channel->GetCurrentSourceID();
3337
3338 MSqlQuery query(MSqlQuery::InitCon());
3339 query.prepare(
3340 "SELECT callsign, channum, name, xmltvid "
3341 "FROM channel "
3342 "WHERE chanid = :CHANID");
3343 query.bindValue(":CHANID", chanid);
3344 if (!query.exec() || !query.isActive())
3345 {
3346 MythDB::DBError("GetChannelInfo", query);
3347 return false;
3348 }
3349
3350 if (!query.next())
3351 return false;
3352
3353 callsign = query.value(0).toString();
3354 channum = query.value(1).toString();
3355 channame = query.value(2).toString();
3356 xmltvid = query.value(3).toString();
3357
3358 return true;
3359}
3360
3361bool TVRec::SetChannelInfo(uint chanid, uint sourceid,
3362 QString oldchannum,
3363 QString callsign, QString channum,
3364 QString channame, QString xmltvid)
3365{
3366 if (!chanid || !sourceid || channum.isEmpty())
3367 return false;
3368//LZ: 'swap' channels not only update
3369 MSqlQuery swapquery(MSqlQuery::InitCon());
3370 query.prepare(
3371 "UPDATE channel "
3372 "SET channum = :OLDCHANNUM, "
3373 "WHERE channum = :CHANNUM AND "
3374 " sourceid = :SOURCEID");
3375 swapquery.bindValue(":CHANNUM", channum);
3376 swapquery.bindValue(":SOURCEID", channame);
3377 swapquery.bindValue(":OLDCHANNUM", xmltvid);
3378
3379
3380 MSqlQuery query(MSqlQuery::InitCon());
3381 query.prepare(
3382 "UPDATE channel "
3383 "SET callsign = :CALLSIGN, "
3384 " channum = :CHANNUM, "
3385 " name = :CHANNAME, "
3386 " xmltvid = :XMLTVID "
3387 "WHERE chanid = :CHANID AND "
3388 " sourceid = :SOURCEID");
3389 query.bindValue(":CALLSIGN", callsign);
3390 query.bindValue(":CHANNUM", channum);
3391 query.bindValue(":CHANNAME", channame);
3392 query.bindValue(":XMLTVID", xmltvid);
3393 query.bindValue(":CHANID", chanid);
3394 query.bindValue(":SOURCEID", sourceid);
3395
3396 if (!query.exec() || !swapquery.exec())
3397 {
3398 MythDB::DBError("SetChannelInfo", query);
3399 return false;
3400 }
3401
3402 if (channel)
3403 channel->Renumber(sourceid, oldchannum, channum);
3404
3405 return true;
3406}
3407
3408/** \fn TVRec::SetRingBuffer(RingBuffer*)
3409 * \brief Sets "ringBuffer", deleting any existing RingBuffer.
3410 */
3411void TVRec::SetRingBuffer(RingBuffer *rb)
3412{
3413 QMutexLocker lock(&stateChangeLock);
3414
3415 RingBuffer *rb_old = ringBuffer;
3416 ringBuffer = rb;
3417
3418 if (rb_old && (rb_old != rb))
3419 {
3420 if (HasFlags(kFlagDummyRecorderRunning))
3421 ClearFlags(kFlagDummyRecorderRunning);
3422 delete rb_old;
3423 }
3424
3425 m_switchingBuffer = false;
3426}
3427
3428void TVRec::RingBufferChanged(RingBuffer *rb, ProgramInfo *pginfo)
3429{
3430 VERBOSE(VB_IMPORTANT, LOC + "RingBufferChanged()");
3431
3432 if (pginfo)
3433 {
3434 if (curRecording)
3435 {
3436 FinishedRecording(curRecording);
3437 curRecording->MarkAsInUse(false, kRecorderInUseID);
3438 delete curRecording;
3439 }
3440 curRecording = new RecordingInfo(*pginfo);
3441 curRecording->MarkAsInUse(true, kRecorderInUseID);
3442 }
3443
3444 SetRingBuffer(rb);
3445}
3446
3447QString TVRec::TuningGetChanNum(const TuningRequest &request,
3448 QString &input) const
3449{
3450 QString channum = QString::null;
3451
3452 if (request.program)
3453 {
3454 request.program->QueryTuningInfo(channum, input);
3455 return channum;
3456 }
3457
3458 channum = request.channel;
3459 input = request.input;
3460
3461 // If this is Live TV startup, we need a channel...
3462 if (channum.isEmpty() && (request.flags & kFlagLiveTV))
3463 {
3464 if (!LiveTVStartChannel.isEmpty())
3465 channum = LiveTVStartChannel;
3466 else
3467 {
3468 input = genOpt.defaultinput;
3469 channum = GetStartChannel(cardid, input);
3470 }
3471 }
3472 if (request.flags & kFlagLiveTV)
3473 channel->Init(input, channum, false);
3474
3475 if (channel && !channum.isEmpty() && (channum.indexOf("NextChannel") >= 0))
3476 {
3477 int dir = channum.right(channum.length() - 12).toInt();
3478 uint chanid = channel->GetNextChannel(0, dir);
3479 channum = ChannelUtil::GetChanNum(chanid);
3480 }
3481
3482 return channum;
3483}
3484
3485bool TVRec::TuningOnSameMultiplex(TuningRequest &request)
3486{
3487 if ((request.flags & kFlagAntennaAdjust) || request.input.isEmpty() ||
3488 !GetDTVRecorder() || signalMonitor || !channel || !channel->IsOpen())
3489 {
3490 return false;
3491 }
3492
3493 uint sourceid = channel->GetCurrentSourceID();
3494 QString oldchannum = channel->GetCurrentName();
3495 QString newchannum = request.channel;
3496
3497 if (ChannelUtil::IsOnSameMultiplex(sourceid, newchannum, oldchannum))
3498 {
3499 MPEGStreamData *mpeg = GetDTVRecorder()->GetStreamData();
3500 ATSCStreamData *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
3501
3502 if (atsc)
3503 {
3504 uint major, minor = 0;
3505 ChannelUtil::GetATSCChannel(sourceid, newchannum, major, minor);
3506
3507 if (minor && atsc->HasChannel(major, minor))
3508 {
3509 request.majorChan = major;
3510 request.minorChan = minor;
3511 return true;
3512 }
3513 }
3514
3515 if (mpeg)
3516 {
3517 uint progNum = ChannelUtil::GetProgramNumber(sourceid, newchannum);
3518 if (mpeg->HasProgram(progNum))
3519 {
3520 request.progNum = progNum;
3521 return true;
3522 }
3523 }
3524 }
3525
3526 return false;
3527}
3528
3529/** \fn TVRec::HandleTuning(void)
3530 * \brief Handles all tuning events.
3531 *
3532 * This method pops tuning events off the tuningState queue
3533 * and does what needs to be done, mostly by calling one of
3534 * the Tuning... methods.
3535 */
3536void TVRec::HandleTuning(void)
3537{
3538 if (tuningRequests.size())
3539 {
3540 TuningRequest request = tuningRequests.front();
3541 VERBOSE(VB_RECORD, LOC + "HandleTuning Request: "<<request.toString());
3542
3543 QString input;
3544 request.channel = TuningGetChanNum(request, input);
3545 request.input = input;
3546
3547 if (TuningOnSameMultiplex(request))
3548 VERBOSE(VB_PLAYBACK, LOC + "On same multiplex");
3549
3550 TuningShutdowns(request);
3551
3552 // The dequeue isn't safe to do until now because we
3553 // release the stateChangeLock to teardown a recorder
3554 tuningRequests.dequeue();
3555
3556 // Now we start new stuff
3557 if (request.flags & (kFlagRecording|kFlagLiveTV|
3558 kFlagEITScan|kFlagAntennaAdjust))
3559 {
3560 if (!recorder)
3561 {
3562 VERBOSE(VB_RECORD, LOC +
3563 "No recorder yet, calling TuningFrequency");
3564 TuningFrequency(request);
3565 }
3566 else
3567 {
3568 VERBOSE(VB_RECORD, LOC + "Waiting for recorder pause..");
3569 SetFlags(kFlagWaitingForRecPause);
3570 }
3571 }
3572 lastTuningRequest = request;
3573 }
3574
3575 if (HasFlags(kFlagWaitingForRecPause))
3576 {
3577 if (!recorder->IsPaused())
3578 return;
3579
3580 ClearFlags(kFlagWaitingForRecPause);
3581 VERBOSE(VB_RECORD, LOC + "Recorder paused, calling TuningFrequency");
3582 TuningFrequency(lastTuningRequest);
3583 }
3584
3585 MPEGStreamData *streamData = NULL;
3586 if (HasFlags(kFlagWaitingForSignal) && !(streamData = TuningSignalCheck()))
3587 return;
3588
3589 if (HasFlags(kFlagNeedToStartRecorder))
3590 {
3591 if (recorder)
3592 TuningRestartRecorder();
3593 else
3594 TuningNewRecorder(streamData);
3595
3596 // If we got this far it is safe to set a new starting channel...
3597 if (channel)
3598 channel->StoreInputChannels();
3599 }
3600}
3601
3602/** \fn TVRec::TuningCheckForHWChange(const TuningRequest&,QString&,QString&)
3603 * \brief Returns cardid for device info row in capturecard if it changes.
3604 */
3605uint TVRec::TuningCheckForHWChange(const TuningRequest &request,
3606 QString &channum,
3607 QString &inputname)
3608{
3609 if (!channel)
3610 return 0;
3611
3612 uint curCardID = 0, newCardID = 0;
3613 channum = request.channel;
3614 inputname = request.input;
3615
3616 if (request.program)
3617 request.program->QueryTuningInfo(channum, inputname);
3618
3619 if (!channum.isEmpty() && inputname.isEmpty())
3620 channel->CheckChannel(channum, inputname);
3621
3622 if (!inputname.isEmpty())
3623 {
3624 int current_input = channel->GetCurrentInputNum();
3625 int new_input = channel->GetInputByName(inputname);
3626 curCardID = channel->GetInputCardID(current_input);
3627 newCardID = channel->GetInputCardID(new_input);
3628 VERBOSE(VB_IMPORTANT, LOC<<"HW Tuner: "<<curCardID<<"->"<<newCardID);
3629 }
3630
3631 if (curCardID != newCardID)
3632 {
3633 if (channum.isEmpty())
3634 channum = GetStartChannel(newCardID, inputname);
3635 return newCardID;
3636 }
3637
3638 return 0;
3639}
3640
3641/** \fn TVRec::TuningShutdowns(const TuningRequest&)
3642 * \brief This shuts down anything that needs to be shut down
3643 * before handling the passed in tuning request.
3644 */
3645void TVRec::TuningShutdowns(const TuningRequest &request)
3646{
3647 QString channum, inputname;
3648 uint newCardID = TuningCheckForHWChange(request, channum, inputname);
3649
3650 if (!(request.flags & kFlagEITScan) && HasFlags(kFlagEITScannerRunning))
3651 {
3652 scanner->StopActiveScan();
3653 ClearFlags(kFlagEITScannerRunning);
3654 }
3655
3656 if (scanner && !request.IsOnSameMultiplex())
3657 scanner->StopPassiveScan();
3658
3659 if (HasFlags(kFlagSignalMonitorRunning))
3660 {
3661 MPEGStreamData *sd = NULL;
3662 if (GetDTVSignalMonitor())
3663 sd = GetDTVSignalMonitor()->GetStreamData();
3664 TeardownSignalMonitor();
3665 ClearFlags(kFlagSignalMonitorRunning);
3666
3667 // Delete StreamData if it is not in use by the recorder.
3668 MPEGStreamData *rec_sd = NULL;
3669 if (GetDTVRecorder())
3670 rec_sd = GetDTVRecorder()->GetStreamData();
3671 if (sd && (sd != rec_sd))
3672 delete sd;
3673 }
3674 if (HasFlags(kFlagWaitingForSignal))
3675 ClearFlags(kFlagWaitingForSignal);
3676
3677 // At this point any waits are canceled.
3678
3679 if (newCardID || (request.flags & kFlagNoRec))
3680 {
3681 if (HasFlags(kFlagDummyRecorderRunning))
3682 {
3683 ClearFlags(kFlagDummyRecorderRunning);
3684 FinishedRecording(curRecording);
3685 curRecording->MarkAsInUse(false, kRecorderInUseID);
3686 }
3687
3688 if (request.flags & kFlagCloseRec)
3689 FinishedRecording(lastTuningRequest.program);
3690
3691 if (HasFlags(kFlagRecorderRunning) ||
3692 (curRecording && curRecording->GetRecordingStatus() == rsFailed))
3693 {
3694 stateChangeLock.unlock();
3695 TeardownRecorder(request.flags & kFlagKillRec);
3696 stateChangeLock.lock();
3697 ClearFlags(kFlagRecorderRunning);
3698 }
3699 // At this point the recorders are shut down
3700
3701 CloseChannel();
3702 // At this point the channel is shut down
3703 }
3704
3705 // handle HW change for digital/analog cards
3706 if (newCardID)
3707 {
3708 VERBOSE(VB_IMPORTANT, "Recreating channel...");
3709 channel->Close();
3710 delete channel;
3711 channel = NULL;
3712
3713 GetDevices(newCardID, genOpt, dvbOpt, fwOpt);
3714 genOpt.defaultinput = inputname;
3715 CreateChannel(channum);
3716 if (!(request.flags & kFlagNoRec))
3717 channel->Open();
3718 }
3719
3720 if (ringBuffer && (request.flags & kFlagKillRingBuffer))
3721 {
3722 VERBOSE(VB_RECORD, LOC + "Tearing down RingBuffer");
3723 SetRingBuffer(NULL);
3724 // At this point the ringbuffer is shut down
3725 }
3726
3727 // Clear pending actions from last request
3728 ClearFlags(kFlagPendingActions);
3729}
3730
3731/** \fn TVRec::TuningFrequency(const TuningRequest&)
3732 * \brief Performs initial tuning required for any tuning event.
3733 *
3734 * This figures out the channel name, and possibly the
3735 * input name we need to pass to "channel" and then calls
3736 * channel appropriately.
3737 *
3738 * Then it adds any filters and sets any video capture attributes
3739 * that need to be set.
3740 *
3741 * The signal monitoring is started if possible. If it is started
3742 * the kFlagWaitForSignal flag is set.
3743 *
3744 * The kFlagNeedToStartRecorder flag is ald set if this isn't
3745 * an EIT scan so that the recorder is started or restarted a
3746 * appropriate.
3747 */
3748void TVRec::TuningFrequency(const TuningRequest &request)
3749{
3750 if (!channel)
3751 {
3752 VERBOSE(VB_IMPORTANT, LOC_ERR +
3753 "TuningFrequency called without a valid channel object");
3754 return;
3755 }
3756
3757 DTVChannel *dtvchan = GetDTVChannel();
3758 bool livetv = request.flags & kFlagLiveTV;
3759 bool antadj = request.flags & kFlagAntennaAdjust;
3760 bool has_dummy = false;
3761 bool ok = true;
3762
3763 if (dtvchan)
3764 {
3765 MPEGStreamData *mpeg = NULL;
3766
3767 if (GetDTVRecorder())
3768 mpeg = GetDTVRecorder()->GetStreamData();
3769
3770 const QString tuningmode = (HasFlags(kFlagEITScannerRunning)) ?
3771 dtvchan->GetSIStandard() :
3772 dtvchan->GetSuggestedTuningMode
3773 (kState_WatchingLiveTV == internalState);
3774
3775 dtvchan->SetTuningMode(tuningmode);
3776
3777 if (request.minorChan && (tuningmode == "atsc"))
3778 {
3779 channel->SelectChannel(request.channel, false);
3780 ATSCStreamData *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
3781 if (atsc)
3782 atsc->SetDesiredChannel(request.majorChan, request.minorChan);
3783 }
3784 else if (request.progNum >= 0)
3785 {
3786 channel->SelectChannel(request.channel, false);
3787 if (mpeg)
3788 mpeg->SetDesiredProgram(request.progNum);
3789 }
3790 }
3791
3792 if (request.IsOnSameMultiplex())
3793 {
3794 QStringList slist;
3795 slist<<"message"<<QObject::tr("On known multiplex...");
3796 MythEvent me(QString("SIGNAL %1").arg(cardid), slist);
3797 gCoreContext->dispatch(me);
3798
3799 SetFlags(kFlagNeedToStartRecorder);
3800 return;
3801 }
3802
3803 QString input = request.input;
3804 QString channum = request.channel;
3805
3806 channel->Open();
3807
3808 if (livetv || antadj)
3809 {
3810 // We need there to be a ringbuffer for these modes
3811 ProgramInfo *tmp = pseudoLiveTVRecording;
3812 pseudoLiveTVRecording = NULL;
3813
3814 tvchain->SetCardType("DUMMY");
3815
3816 if (!ringBuffer)
3817 ok = CreateLiveTVRingBuffer(channum);
3818 else
3819 ok = SwitchLiveTVRingBuffer(channum, true, false);
3820 pseudoLiveTVRecording = tmp;
3821
3822 tvchain->SetCardType(genOpt.cardtype);
3823
3824 if (!ok)
3825 {
3826 VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to create RingBuffer 1");
3827 return;
3828 }
3829
3830 has_dummy = true;
3831 }
3832
3833 if (!channum.isEmpty())
3834 {
3835 if (!input.isEmpty())
3836 channel->SelectInput(input, channum, true);
3837 else
3838 channel->SelectChannel(channum, true);
3839 }
3840
3841 // Start signal (or channel change) monitoring
3842 VERBOSE(VB_RECORD, LOC + "Starting Signal Monitor");
3843 bool error = false;
3844 if (!SetupSignalMonitor(!antadj, request.flags & kFlagEITScan,
3845 livetv | antadj))
3846 {
3847 VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to setup signal monitor");
3848 if (signalMonitor)
3849 {
3850 delete signalMonitor;
3851 signalMonitor = NULL;
3852 }
3853
3854 // pretend the signal monitor is running to prevent segfault
3855 SetFlags(kFlagSignalMonitorRunning);
3856 ClearFlags(kFlagWaitingForSignal);
3857 error = true;
3858 }
3859 else if (signalMonitor)
3860 {
3861 SetFlags(kFlagSignalMonitorRunning);
3862 ClearFlags(kFlagWaitingForSignal);
3863 if (!antadj)
3864 SetFlags(kFlagWaitingForSignal);
3865 }
3866
3867 if (has_dummy && ringBuffer)
3868 {
3869 // Make sure recorder doesn't point to bogus ringbuffer before
3870 // it is potentially restarted without a new ringbuffer, if
3871 // the next channel won't tune and the user exits LiveTV.
3872 if (recorder)
3873 recorder->SetRingBuffer(NULL);
3874
3875 SetFlags(kFlagDummyRecorderRunning);
3876 VERBOSE(VB_RECORD, "DummyDTVRecorder -- started");
3877 SetFlags(kFlagRingBufferReady);
3878 }
3879
3880 // if we had problems starting the signal monitor,
3881 // we don't want to start the recorder...
3882 if (error)
3883 return;
3884
3885 // Request a recorder, if the command is a recording command
3886 ClearFlags(kFlagNeedToStartRecorder);
3887 if (request.flags & kFlagRec && !antadj)
3888 SetFlags(kFlagNeedToStartRecorder);
3889}
3890
3891/** \fn TVRec::TuningSignalCheck(void)
3892 * \brief This checks if we have a channel lock.
3893 *
3894 * If we have a channel lock this shuts down the signal monitoring.
3895 *
3896 * \return MPEGStreamData pointer if we have a complete lock, NULL otherwise
3897 */
3898MPEGStreamData *TVRec::TuningSignalCheck(void)
3899{
3900 if (signalMonitor->IsAllGood())
3901 {
3902 VERBOSE(VB_RECORD, LOC + "Got good signal");
3903
3904 pendingRecLock.lock();
3905 m_recStatus = rsRecording;
3906 pendingRecLock.unlock();
3907 if (curRecording)
3908 curRecording->SetRecordingStatus(rsRecording);
3909 }
3910 else if (signalMonitor->IsErrored())
3911 {
3912 VERBOSE(VB_RECORD, LOC_ERR + "SignalMonitor failed");
3913 ClearFlags(kFlagNeedToStartRecorder);
3914
3915 pendingRecLock.lock();
3916 m_recStatus = rsFailed;
3917 pendingRecLock.unlock();
3918 if (curRecording)
3919 curRecording->SetRecordingStatus(rsFailed);
3920
3921 if (HasFlags(kFlagEITScannerRunning))
3922 {
3923 scanner->StopActiveScan();
3924 ClearFlags(kFlagEITScannerRunning);
3925 }
3926 }
3927 else
3928 return NULL;
3929
3930 if (curRecording)
3931 {
3932 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
3933 .arg(curRecording->GetCardID())
3934 .arg(curRecording->GetChanID())
3935 .arg(curRecording->GetScheduledStartTime(ISODate))
3936 .arg(curRecording->GetRecordingStatus())
3937 .arg(curRecording->GetRecordingEndTime(ISODate)));
3938 gCoreContext->dispatch(me);
3939 }
3940
3941 // grab useful data from DTV signal monitor before we kill it...
3942 MPEGStreamData *streamData = NULL;
3943 if (GetDTVSignalMonitor())
3944 streamData = GetDTVSignalMonitor()->GetStreamData();
3945
3946 if (!HasFlags(kFlagEITScannerRunning))
3947 {
3948 // shut down signal monitoring
3949 TeardownSignalMonitor();
3950 ClearFlags(kFlagSignalMonitorRunning);
3951 }
3952 ClearFlags(kFlagWaitingForSignal);
3953
3954 if (streamData)
3955 {
3956 DVBStreamData *dsd = dynamic_cast<DVBStreamData*>(streamData);
3957 if (dsd)
3958 dsd->SetDishNetEIT(is_dishnet_eit(cardid));
3959 if (!get_use_eit(GetCaptureCardNum()))
3960 {
3961 VERBOSE(VB_EIT, LOC + "EIT scanning disabled "
3962 "for all sources on this card.");
3963 }
3964 else if (scanner)
3965 scanner->StartPassiveScan(channel, streamData);
3966 }
3967
3968 return streamData;
3969}
3970
3971static int init_jobs(const RecordingInfo *rec, RecordingProfile &profile,
3972 bool on_host, bool transcode_bfr_comm, bool on_line_comm)
3973{
3974 if (!rec)
3975 return 0; // no jobs for Live TV recordings..
3976
3977 int jobs = 0; // start with no jobs
3978
3979 // grab standard jobs flags from program info
3980 JobQueue::AddJobsToMask(rec->GetAutoRunJobs(), jobs);
3981
3982 // disable commercial flagging on PBS, BBC, etc.
3983 if (rec->IsCommercialFree())
3984 JobQueue::RemoveJobsFromMask(JOB_COMMFLAG, jobs);
3985
3986 // disable transcoding if the profile does not allow auto transcoding
3987 const Setting *autoTrans = profile.byName("autotranscode");
3988 if ((!autoTrans) || (autoTrans->getValue().toInt() == 0))
3989 JobQueue::RemoveJobsFromMask(JOB_TRANSCODE, jobs);
3990
3991 // is commercial flagging enabled, and is on-line comm flagging enabled?
3992 bool rt = JobQueue::JobIsInMask(JOB_COMMFLAG, jobs) && on_line_comm;
3993 // also, we either need transcoding to be disabled or
3994 // we need to be allowed to commercial flag before transcoding?
3995 rt &= JobQueue::JobIsNotInMask(JOB_TRANSCODE, jobs) ||
3996 !transcode_bfr_comm;
3997 if (rt)
3998 {
3999 // queue up real-time (i.e. on-line) commercial flagging.
4000 QString host = (on_host) ? gCoreContext->GetHostName() : "";
4001 JobQueue::QueueJob(JOB_COMMFLAG,
4002 rec->GetChanID(),
4003 rec->GetRecordingStartTime(), "", "",
4004 host, JOB_LIVE_REC);
4005
4006 // don't do regular comm flagging, we won't need it.
4007 JobQueue::RemoveJobsFromMask(JOB_COMMFLAG, jobs);
4008 }
4009
4010 return jobs;
4011}
4012
4013static QString load_profile(QString cardtype, void *tvchain,
4014 RecordingInfo *rec, RecordingProfile &profile)
4015{
4016 // Determine the correct recording profile.
4017 // In LiveTV mode use "Live TV" profile, otherwise use the
4018 // recording's specified profile. If the desired profile can't
4019 // be found, fall back to the "Default" profile for card type.
4020 QString profileName = "Live TV";
4021 if (!tvchain && rec)
4022 profileName = rec->GetRecordingRule()->m_recProfile;
4023
4024 if (!profile.loadByType(profileName, cardtype))
4025 {
4026 profileName = "Default";
4027 profile.loadByType(profileName, cardtype);
4028 }
4029
4030 VERBOSE(VB_RECORD, QString("Using profile '%1' to record")
4031 .arg(profileName));
4032
4033 return profileName;
4034}
4035
4036/** \fn TVRec::TuningNewRecorder(MPEGStreamData*)
4037 * \brief Creates a recorder instance.
4038 */
4039void TVRec::TuningNewRecorder(MPEGStreamData *streamData)
4040{
4041 VERBOSE(VB_RECORD, LOC + "Starting Recorder");
4042
4043 bool had_dummyrec = false;
4044 if (HasFlags(kFlagDummyRecorderRunning))
4045 {
4046 ClearFlags(kFlagDummyRecorderRunning);
4047 FinishedRecording(curRecording);
4048 curRecording->MarkAsInUse(false, kRecorderInUseID);
4049 had_dummyrec = true;
4050 }
4051
4052 RecordingInfo *rec = lastTuningRequest.program;
4053
4054 RecordingProfile profile;
4055 QString profileName = load_profile(genOpt.cardtype, tvchain, rec, profile);
4056
4057 if (tvchain)
4058 {
4059 bool ok;
4060 if (!ringBuffer)
4061 {
4062 ok = CreateLiveTVRingBuffer(channel->GetCurrentName());
4063 SetFlags(kFlagRingBufferReady);
4064 }
4065 else
4066 ok = SwitchLiveTVRingBuffer(channel->GetCurrentName(),
4067 true, !had_dummyrec && recorder);
4068 if (!ok)
4069 {
4070 VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to create RingBuffer 2");
4071 goto err_ret;
4072 }
4073 rec = curRecording; // new'd in Create/SwitchLiveTVRingBuffer()
4074 }
4075
4076 if (lastTuningRequest.flags & kFlagRecording)
4077 {
4078 bool write = genOpt.cardtype != "IMPORT";
4079 VERBOSE(VB_IMPORTANT, LOC + QString("rec->GetPathname(): '%1'")
4080 .arg(rec->GetPathname()));
4081 SetRingBuffer(RingBuffer::Create(rec->GetPathname(), write));
4082 if (!ringBuffer->IsOpen() && write)
4083 {
4084 VERBOSE(VB_IMPORTANT, LOC_ERR +
4085 QString("RingBuffer '%1' not open...")
4086 .arg(rec->GetPathname()));
4087 SetRingBuffer(NULL);
4088 ClearFlags(kFlagPendingActions);
4089 goto err_ret;
4090 }
4091 }
4092
4093 if (!ringBuffer)
4094 {
4095 VERBOSE(VB_IMPORTANT, LOC_ERR + QString(
4096 "Failed to start recorder! ringBuffer is NULL\n"
4097 "\t\t\t\t Tuning request was %1\n")
4098 .arg(lastTuningRequest.toString()));
4099
4100 if (HasFlags(kFlagLiveTV))
4101 {
4102 QString message = QString("QUIT_LIVETV %1").arg(cardid);
4103 MythEvent me(message);
4104 gCoreContext->dispatch(me);
4105 }
4106 goto err_ret;
4107 }
4108
4109 if (channel && genOpt.cardtype == "MJPEG")
4110 channel->Close(); // Needed because of NVR::MJPEGInit()
4111
4112 if (!SetupRecorder(profile))
4113 {
4114 VERBOSE(VB_IMPORTANT, LOC_ERR + QString(
4115 "Failed to start recorder!\n"
4116 "\t\t\t\t Tuning request was %1\n")
4117 .arg(lastTuningRequest.toString()));
4118
4119 if (HasFlags(kFlagLiveTV))
4120 {
4121 QString message = QString("QUIT_LIVETV %1").arg(cardid);
4122 MythEvent me(message);
4123 gCoreContext->dispatch(me);
4124 }
4125 TeardownRecorder(true);
4126 goto err_ret;
4127 }
4128
4129 if (GetDTVRecorder() && streamData)
4130 {
4131 const Setting *setting = profile.byName("recordingtype");
4132 if (setting)
4133 streamData->SetRecordingType(setting->getValue());
4134 GetDTVRecorder()->SetStreamData(streamData);
4135 }
4136
4137 if (channel && genOpt.cardtype == "MJPEG")
4138 channel->Open(); // Needed because of NVR::MJPEGInit()
4139
4140 if (rec)
4141 recorder->SetRecording(rec);
4142
4143 // Setup for framebuffer capture devices..
4144 if (channel)
4145 {
4146 SetVideoFiltersForChannel(channel->GetCurrentSourceID(),
4147 channel->GetCurrentName());
4148 }
4149
4150#ifdef USING_V4L
4151 if (GetV4LChannel())
4152 {
4153 channel->InitPictureAttributes();
4154 CloseChannel();
4155 }
4156#endif
4157
4158 RecorderThread.SetParent(this);
4159 RecorderThread.start();
4160
4161 // Wait for recorder to start.
4162 stateChangeLock.unlock();
4163 while (!recorder->IsRecording() && !recorder->IsErrored())
4164 usleep(5 * 1000);
4165 stateChangeLock.lock();
4166
4167 if (GetV4LChannel())
4168 channel->SetFd(recorder->GetVideoFd());
4169
4170 SetFlags(kFlagRecorderRunning | kFlagRingBufferReady);
4171
4172 if (!tvchain)
4173 autoRunJobs = init_jobs(rec, profile, runJobOnHostOnly,
4174 transcodeFirst, earlyCommFlag);
4175
4176 ClearFlags(kFlagNeedToStartRecorder);
4177 return;
4178
4179 err_ret:
4180 ChangeState(kState_None);
4181 if (tvchain)
4182 delete rec;
4183}
4184
4185/** \fn TVRec::TuningRestartRecorder(void)
4186 * \brief Restarts a stopped recorder or unpauses a paused recorder.
4187 */
4188void TVRec::TuningRestartRecorder(void)
4189{
4190 VERBOSE(VB_RECORD, LOC + "Restarting Recorder");
4191
4192 bool had_dummyrec = false;
4193 if (HasFlags(kFlagDummyRecorderRunning))
4194 {
4195 ClearFlags(kFlagDummyRecorderRunning);
4196 had_dummyrec = true;
4197 }
4198
4199 if (curRecording)
4200 {
4201 FinishedRecording(curRecording);
4202 curRecording->MarkAsInUse(false, kRecorderInUseID);
4203
4204 if (pseudoLiveTVRecording)
4205 {
4206 int secsSince = curRecording->GetRecordingStartTime()
4207 .secsTo(QDateTime::currentDateTime());
4208 if (secsSince < 120)
4209 {
4210 JobQueue::RemoveJobsFromMask(JOB_COMMFLAG, autoRunJobs);
4211 JobQueue::RemoveJobsFromMask(JOB_TRANSCODE, autoRunJobs);
4212 }
4213
4214 if (autoRunJobs)
4215 JobQueue::QueueRecordingJobs(*curRecording, autoRunJobs);
4216 }
4217 }
4218
4219 SwitchLiveTVRingBuffer(channel->GetCurrentName(), true, !had_dummyrec);
4220
4221 if (had_dummyrec)
4222 {
4223 recorder->SetRingBuffer(ringBuffer);
4224 ProgramInfo *progInfo = tvchain->GetProgramAt(-1);
4225 recorder->SetRecording(progInfo);
4226 delete progInfo;
4227 }
4228 recorder->Reset();
4229
4230 // Set file descriptor of channel from recorder for V4L
4231 channel->SetFd(recorder->GetVideoFd());
4232
4233 // Some recorders unpause on Reset, others do not...
4234 recorder->Unpause();
4235
4236 if (pseudoLiveTVRecording)
4237 {
4238 ProgramInfo *rcinfo1 = pseudoLiveTVRecording;
4239 QString msg1 = QString("Recording: %1 %2 %3 %4")
4240 .arg(rcinfo1->GetTitle()).arg(rcinfo1->GetChanID())
4241 .arg(rcinfo1->GetRecordingStartTime(ISODate))
4242 .arg(rcinfo1->GetRecordingEndTime(ISODate));
4243 ProgramInfo *rcinfo2 = tvchain->GetProgramAt(-1);
4244 QString msg2 = QString("Recording: %1 %2 %3 %4")
4245 .arg(rcinfo2->GetTitle()).arg(rcinfo2->GetChanID())
4246 .arg(rcinfo2->GetRecordingStartTime(ISODate))
4247 .arg(rcinfo2->GetRecordingEndTime(ISODate));
4248 delete rcinfo2;
4249 VERBOSE(VB_RECORD, LOC + "Pseudo LiveTV recording starting." +
4250 "\n\t\t\t" + msg1 + "\n\t\t\t" + msg2);
4251
4252 curRecording->SaveAutoExpire(
4253 curRecording->GetRecordingRule()->GetAutoExpire());
4254
4255 curRecording->ApplyRecordRecGroupChange(
4256 curRecording->GetRecordingRule()->m_recGroup);
4257
4258 RecordingProfile profile;
4259 QString profileName = load_profile(genOpt.cardtype, NULL,
4260 curRecording, profile);
4261 autoRunJobs = init_jobs(curRecording, profile, runJobOnHostOnly,
4262 transcodeFirst, earlyCommFlag);
4263 }
4264
4265 ClearFlags(kFlagNeedToStartRecorder);
4266}
4267
4268void TVRec::SetFlags(uint f)
4269{
4270 QMutexLocker lock(&stateChangeLock);
4271 stateFlags |= f;
4272 VERBOSE(VB_RECORD, LOC + QString("SetFlags(%1) -> %2")
4273 .arg(FlagToString(f)).arg(FlagToString(stateFlags)));
4274 WakeEventLoop();
4275}
4276
4277void TVRec::ClearFlags(uint f)
4278{
4279 QMutexLocker lock(&stateChangeLock);
4280 stateFlags &= ~f;
4281 VERBOSE(VB_RECORD, LOC + QString("ClearFlags(%1) -> %2")
4282 .arg(FlagToString(f)).arg(FlagToString(stateFlags)));
4283 WakeEventLoop();
4284}
4285
4286QString TVRec::FlagToString(uint f)
4287{
4288 QString msg("");
4289
4290 // General flags
4291 if (kFlagFrontendReady & f)
4292 msg += "FrontendReady,";
4293 if (kFlagRunMainLoop & f)
4294 msg += "RunMainLoop,";
4295 if (kFlagExitPlayer & f)
4296 msg += "ExitPlayer,";
4297 if (kFlagFinishRecording & f)
4298 msg += "FinishRecording,";
4299 if (kFlagErrored & f)
4300 msg += "Errored,";
4301 if (kFlagCancelNextRecording & f)
4302 msg += "CancelNextRecording,";
4303
4304 // Tuning flags
4305 if ((kFlagRec & f) == kFlagRec)
4306 msg += "REC,";
4307 else
4308 {
4309 if (kFlagLiveTV & f)
4310 msg += "LiveTV,";
4311 if (kFlagRecording & f)
4312 msg += "Recording,";
4313 }
4314 if ((kFlagNoRec & f) == kFlagNoRec)
4315 msg += "NOREC,";
4316 else
4317 {
4318 if (kFlagEITScan & f)
4319 msg += "EITScan,";
4320 if (kFlagCloseRec & f)
4321 msg += "CloseRec,";
4322 if (kFlagKillRec & f)
4323 msg += "KillRec,";
4324 if (kFlagAntennaAdjust & f)
4325 msg += "AntennaAdjust,";
4326 }
4327 if ((kFlagPendingActions & f) == kFlagPendingActions)
4328 msg += "PENDINGACTIONS,";
4329 else
4330 {
4331 if (kFlagWaitingForRecPause & f)
4332 msg += "WaitingForRecPause,";
4333 if (kFlagWaitingForSignal & f)
4334 msg += "WaitingForSignal,";
4335 if (kFlagNeedToStartRecorder & f)
4336 msg += "NeedToStartRecorder,";
4337 if (kFlagKillRingBuffer & f)
4338 msg += "KillRingBuffer,";
4339 }
4340 if ((kFlagAnyRunning & f) == kFlagAnyRunning)
4341 msg += "ANYRUNNING,";
4342 else
4343 {
4344 if (kFlagSignalMonitorRunning & f)
4345 msg += "SignalMonitorRunning,";
4346 if (kFlagEITScannerRunning & f)
4347 msg += "EITScannerRunning,";
4348 if ((kFlagAnyRecRunning & f) == kFlagAnyRecRunning)
4349 msg += "ANYRECRUNNING,";
4350 else
4351 {
4352 if (kFlagDummyRecorderRunning & f)
4353 msg += "DummyRecorderRunning,";
4354 if (kFlagRecorderRunning & f)
4355 msg += "RecorderRunning,";
4356 }
4357 }
4358 if (kFlagRingBufferReady & f)
4359 msg += "RingBufferReady,";
4360
4361 if (msg.isEmpty())
4362 msg = QString("0x%1").arg(f,0,16);
4363
4364 return msg;
4365}
4366
4367bool TVRec::WaitForNextLiveTVDir(void)
4368{
4369 QMutexLocker lock(&nextLiveTVDirLock);
4370
4371 bool found = !nextLiveTVDir.isEmpty();
4372 if (!found && triggerLiveTVDir.wait(&nextLiveTVDirLock, 500))
4373 {
4374 found = !nextLiveTVDir.isEmpty();
4375 }
4376
4377 return found;
4378}
4379
4380void TVRec::SetNextLiveTVDir(QString dir)
4381{
4382 QMutexLocker lock(&nextLiveTVDirLock);
4383
4384 nextLiveTVDir = dir;
4385 triggerLiveTVDir.wakeAll();
4386}
4387
4388bool TVRec::GetProgramRingBufferForLiveTV(RecordingInfo **pginfo,
4389 RingBuffer **rb,
4390 const QString & channum,
4391 int inputID)
4392{
4393 VERBOSE(VB_RECORD, LOC + "GetProgramRingBufferForLiveTV()");
4394 if (!channel || !tvchain || !pginfo || !rb)
4395 return false;
4396
4397 nextLiveTVDirLock.lock();
4398 nextLiveTVDir.clear();
4399 nextLiveTVDirLock.unlock();
4400
4401 // Dispatch this early, the response can take a while.
4402 MythEvent me(QString("QUERY_NEXT_LIVETV_DIR %1").arg(cardid));
4403 gCoreContext->dispatch(me);
4404
4405 uint sourceid = channel->GetSourceID(inputID);
4406 int chanid = ChannelUtil::GetChanID(sourceid, channum);
4407
4408 if (chanid < 0)
4409 {
4410 // Test setups might have zero channels
4411 if (genOpt.cardtype == "IMPORT" || genOpt.cardtype == "DEMO")
4412 chanid = 9999;
4413 else
4414 {
4415 VERBOSE(VB_IMPORTANT, (LOC_ERR +
4416 "Channel: \'%1\' was not found in the database.\n"
4417 "\t\t\tMost likely, your DefaultTVChannel setting is wrong.\n"
4418 "\t\t\tCould not start livetv.").arg(channum));
4419 return false;
4420 }
4421 }
4422
4423 int hoursMax = gCoreContext->GetNumSetting("MaxHoursPerLiveTVRecording", 8);
4424 if (hoursMax <= 0)
4425 hoursMax = 8;
4426
4427 RecordingInfo *prog = NULL;
4428 if (pseudoLiveTVRecording)
4429 prog = new RecordingInfo(*pseudoLiveTVRecording);
4430 else
4431 {
4432 prog = new RecordingInfo(
4433 chanid, mythCurrentDateTime(), true, hoursMax);
4434 }
4435
4436 prog->SetCardID(cardid);
4437
4438 if (prog->GetRecordingStartTime() == prog->GetRecordingEndTime())
4439 {
4440 VERBOSE(VB_IMPORTANT, LOC_ERR + "GetProgramRingBufferForLiveTV()"
4441 "\n\t\t\tProgramInfo is invalid."
4442 "\n" + prog->toString());
4443 prog->SetScheduledEndTime(prog->GetRecordingStartTime().addSecs(3600));
4444 prog->SetRecordingEndTime(prog->GetScheduledEndTime());
4445
4446 prog->SetChanID(chanid);
4447 }
4448
4449 if (!pseudoLiveTVRecording)
4450 prog->SetRecordingStartTime(mythCurrentDateTime());
4451
4452 prog->SetStorageGroup("LiveTV");
4453
4454 if (WaitForNextLiveTVDir())
4455 {
4456 QMutexLocker lock(&nextLiveTVDirLock);
4457 prog->SetPathname(nextLiveTVDir);
4458 }
4459 else
4460 {
4461 StorageGroup sgroup("LiveTV", gCoreContext->GetHostName());
4462 prog->SetPathname(sgroup.FindNextDirMostFree());
4463 }
4464
4465 StartedRecording(prog);
4466
4467 *rb = RingBuffer::Create(prog->GetPathname(), true);
4468 if (!(*rb)->IsOpen())
4469 {
4470 VERBOSE(VB_IMPORTANT, LOC_ERR +
4471 QString("RingBuffer '%1' not open...")
4472 .arg(prog->GetPathname()));
4473
4474 delete *rb;
4475 delete prog;
4476
4477 return false;
4478 }
4479
4480 *pginfo = prog;
4481 return true;
4482}
4483
4484bool TVRec::CreateLiveTVRingBuffer(const QString & channum)
4485{
4486 VERBOSE(VB_RECORD, LOC + QString("CreateLiveTVRingBuffer(%1)")
4487 .arg(channum));
4488
4489 RecordingInfo *pginfo = NULL;
4490 RingBuffer *rb = NULL;
4491 QString inputName;
4492 int inputID = -1;
4493
4494 if (!channel->CheckChannel(channum, inputName))
4495 {
4496 ChangeState(kState_None);
4497 return false;
4498 }
4499
4500 inputID = inputName.isEmpty() ?
4501 channel->GetCurrentInputNum() : channel->GetInputByName(inputName);
4502
4503 if (!GetProgramRingBufferForLiveTV(&pginfo, &rb, channum, inputID))
4504 {
4505 ClearFlags(kFlagPendingActions);
4506 ChangeState(kState_None);
4507 VERBOSE(VB_IMPORTANT, LOC_ERR +
4508 QString("CreateLiveTVRingBuffer(%1) failed").arg(channum));
4509 return false;
4510 }
4511
4512 SetRingBuffer(rb);
4513
4514 pginfo->SaveAutoExpire(kLiveTVAutoExpire);
4515 pginfo->ApplyRecordRecGroupChange("LiveTV");
4516
4517 bool discont = (tvchain->TotalSize() > 0);
4518 tvchain->AppendNewProgram(pginfo, channel->GetCurrentName(),
4519 channel->GetCurrentInput(), discont);
4520
4521 if (curRecording)
4522 {
4523 curRecording->MarkAsInUse(false, kRecorderInUseID);
4524 delete curRecording;
4525 }
4526
4527 curRecording = pginfo;
4528 curRecording->MarkAsInUse(true, kRecorderInUseID);
4529
4530 return true;
4531}
4532
4533bool TVRec::SwitchLiveTVRingBuffer(const QString & channum,
4534 bool discont, bool set_rec)
4535{
4536 VERBOSE(VB_RECORD, LOC + "SwitchLiveTVRingBuffer(discont "
4537 <<discont<<", set_rec "<<set_rec<<")");
4538
4539 RecordingInfo *pginfo = NULL;
4540 RingBuffer *rb = NULL;
4541 QString inputName;
4542 int inputID = -1;
4543
4544 if (!channel->CheckChannel(channum, inputName))
4545 {
4546 ChangeState(kState_None);
4547 return false;
4548 }
4549
4550 inputID = inputName.isEmpty() ?
4551 channel->GetCurrentInputNum() : channel->GetInputByName(inputName);
4552
4553 if (!GetProgramRingBufferForLiveTV(&pginfo, &rb, channum, inputID))
4554 {
4555 ChangeState(kState_None);
4556 return false;
4557 }
4558
4559 ProgramInfo *pi = tvchain->GetProgramAt(-1);
4560 if (pi)
4561 {
4562 RecordingInfo *oldinfo = new RecordingInfo(*pi);
4563 delete pi;
4564 FinishedRecording(oldinfo);
4565 if (tvchain->GetCardType(-1) != "DUMMY")
4566 {
4567 if (!oldinfo->IsLocal())
4568 oldinfo->SetPathname(oldinfo->GetPlaybackURL(false,true));
4569 if (oldinfo->IsLocal())
4570 PreviewGeneratorQueue::GetPreviewImage(*oldinfo, "");
4571 }
4572 delete oldinfo;
4573 }
4574
4575 pginfo->MarkAsInUse(true, kRecorderInUseID);
4576 pginfo->SaveAutoExpire(kLiveTVAutoExpire);
4577 pginfo->ApplyRecordRecGroupChange("LiveTV");
4578 tvchain->AppendNewProgram(pginfo, channel->GetCurrentName(),
4579 channel->GetCurrentInput(), discont);
4580
4581 if (set_rec && recorder)
4582 {
4583 recorder->SetNextRecording(pginfo, rb);
4584 if (discont)
4585 recorder->CheckForRingBufferSwitch();
4586 delete pginfo;
4587 SetFlags(kFlagRingBufferReady);
4588 }
4589 else if (!set_rec)
4590 {
4591 if (curRecording)
4592 {
4593 curRecording->MarkAsInUse(false, kRecorderInUseID);
4594 delete curRecording;
4595 }
4596 curRecording = pginfo;
4597 SetRingBuffer(rb);
4598 }
4599
4600 return true;
4601}
4602
4603TVRec* TVRec::GetTVRec(uint cardid)
4604{
4605 QMutexLocker locker(&cardsLock);
4606 QMap<uint,TVRec*>::const_iterator it = cards.find(cardid);
4607 if (it == cards.end())
4608 return NULL;
4609 return *it;
4610}
4611
4612QString TuningRequest::toString(void) const
4613{
4614 return QString("Program(%1) channel(%2) input(%3) flags(%4)")
4615 .arg((program != 0) ? "yes" : "no").arg(channel).arg(input)
4616 .arg(TVRec::FlagToString(flags));
4617}
4618
4619/* vim: set expandtab tabstop=4 shiftwidth=4: */
4620