00001
00002 #include <cstdio>
00003 #include <cstdlib>
00004 #include <cstring>
00005 #include <unistd.h>
00006 #include <sched.h>
00007
00008
00009
00010 #include "compat.h"
00011 #include "previewgeneratorqueue.h"
00012 #include "dtvsignalmonitor.h"
00013 #include "recordingprofile.h"
00014 #include "mythcorecontext.h"
00015 #include "mythsystemevent.h"
00016 #include "atscstreamdata.h"
00017 #include "dvbstreamdata.h"
00018 #include "recordingrule.h"
00019 #include "channelgroup.h"
00020 #include "storagegroup.h"
00021 #include "tvremoteutil.h"
00022 #include "dtvrecorder.h"
00023 #include "livetvchain.h"
00024 #include "programinfo.h"
00025 #include "atsctables.h"
00026 #include "dtvchannel.h"
00027 #include "eitscanner.h"
00028 #include "mythconfig.h"
00029 #include "remoteutil.h"
00030 #include "ringbuffer.h"
00031 #include "mythlogging.h"
00032 #include "v4lchannel.h"
00033 #include "dialogbox.h"
00034 #include "jobqueue.h"
00035 #include "mythdb.h"
00036 #include "tv_rec.h"
00037 #include "mythmiscutil.h"
00038 #include "osd.h"
00039
00040 #define DEBUG_CHANNEL_PREFIX 0
00042 #define LOC QString("TVRec(%1): ").arg(cardid)
00043
00045 const uint TVRec::kSignalMonitoringRate = 50;
00046
00047 QMutex TVRec::cardsLock;
00048 QMap<uint,TVRec*> TVRec::cards;
00049
00050 static bool is_dishnet_eit(uint cardid);
00051 static QString load_profile(QString,void*,RecordingInfo*,RecordingProfile&);
00052 static int init_jobs(const RecordingInfo *rec, RecordingProfile &profile,
00053 bool on_host, bool transcode_bfr_comm, bool on_line_comm);
00054 static void apply_broken_dvb_driver_crc_hack(ChannelBase*, MPEGStreamData*);
00055
00056
00081 TVRec::TVRec(int capturecardnum)
00082
00083 : recorder(NULL), channel(NULL), signalMonitor(NULL),
00084 scanner(NULL),
00085
00086 eventThread(new MThread("TVRecEvent", this)),
00087 recorderThread(NULL),
00088
00089 transcodeFirst(false),
00090 earlyCommFlag(false), runJobOnHostOnly(false),
00091 eitCrawlIdleStart(60), eitTransportTimeout(5*60),
00092 audioSampleRateDB(0),
00093 overRecordSecNrml(0), overRecordSecCat(0),
00094 overRecordCategory(""),
00095
00096 cardid(capturecardnum), ispip(false),
00097
00098 stateChangeLock(QMutex::Recursive),
00099 pendingRecLock(QMutex::Recursive),
00100 internalState(kState_None), desiredNextState(kState_None),
00101 changeState(false), pauseNotify(true),
00102 stateFlags(0), lastTuningRequest(0),
00103 triggerEventLoopLock(QMutex::NonRecursive),
00104 triggerEventLoopSignal(false),
00105 triggerEventSleepLock(QMutex::NonRecursive),
00106 triggerEventSleepSignal(false),
00107 switchingBuffer(false),
00108 m_recStatus(rsUnknown),
00109
00110 curRecording(NULL), autoRunJobs(JOB_NONE),
00111 overrecordseconds(0),
00112
00113 pseudoLiveTVRecording(NULL),
00114 nextLiveTVDir(""), nextLiveTVDirLock(),
00115
00116 tvchain(NULL),
00117
00118 ringBuffer(NULL), rbFileExt("mpg")
00119 {
00120 QMutexLocker locker(&cardsLock);
00121 cards[cardid] = this;
00122 }
00123
00124 bool TVRec::CreateChannel(const QString &startchannel,
00125 bool enter_power_save_mode)
00126 {
00127 channel = ChannelBase::CreateChannel(
00128 this, genOpt, dvbOpt, fwOpt,
00129 startchannel, enter_power_save_mode, rbFileExt);
00130
00131 if (!channel)
00132 {
00133 SetFlags(kFlagErrored);
00134 return false;
00135 }
00136
00137 return true;
00138 }
00139
00145 bool TVRec::Init(void)
00146 {
00147 QMutexLocker lock(&stateChangeLock);
00148
00149 if (!GetDevices(cardid, genOpt, dvbOpt, fwOpt))
00150 return false;
00151
00152 SetRecordingStatus(rsUnknown, __LINE__);
00153
00154
00155 QString startchannel = GetStartChannel(cardid,
00156 CardUtil::GetStartInput(cardid));
00157 if (!CreateChannel(startchannel, true))
00158 return false;
00159
00160 transcodeFirst =
00161 gCoreContext->GetNumSetting("AutoTranscodeBeforeAutoCommflag", 0);
00162 earlyCommFlag = gCoreContext->GetNumSetting("AutoCommflagWhileRecording", 0);
00163 runJobOnHostOnly = gCoreContext->GetNumSetting("JobsRunOnRecordHost", 0);
00164 eitTransportTimeout=gCoreContext->GetNumSetting("EITTransportTimeout", 5) * 60;
00165 eitCrawlIdleStart = gCoreContext->GetNumSetting("EITCrawIdleStart", 60);
00166 audioSampleRateDB = gCoreContext->GetNumSetting("AudioSampleRate");
00167 overRecordSecNrml = gCoreContext->GetNumSetting("RecordOverTime");
00168 overRecordSecCat = gCoreContext->GetNumSetting("CategoryOverTime") * 60;
00169 overRecordCategory= gCoreContext->GetSetting("OverTimeCategory");
00170
00171 eventThread->start();
00172
00173 WaitForEventThreadSleep();
00174
00175 return true;
00176 }
00177
00182 TVRec::~TVRec()
00183 {
00184 QMutexLocker locker(&cardsLock);
00185 cards.remove(cardid);
00186 TeardownAll();
00187 }
00188
00189 void TVRec::TeardownAll(void)
00190 {
00191 if (HasFlags(kFlagRunMainLoop))
00192 {
00193 ClearFlags(kFlagRunMainLoop);
00194 eventThread->wait();
00195 delete eventThread;
00196 eventThread = NULL;
00197 }
00198
00199 TeardownSignalMonitor();
00200
00201 if (scanner)
00202 {
00203 delete scanner;
00204 scanner = NULL;
00205 }
00206
00207 if (channel)
00208 {
00209 delete channel;
00210 channel = NULL;
00211 }
00212
00213 TeardownRecorder(kFlagKillRec);
00214
00215 SetRingBuffer(NULL);
00216 }
00217
00218 void TVRec::WakeEventLoop(void)
00219 {
00220 QMutexLocker locker(&triggerEventLoopLock);
00221 triggerEventLoopSignal = true;
00222 triggerEventLoopWait.wakeAll();
00223 }
00224
00231 TVState TVRec::GetState(void) const
00232 {
00233 if (changeState)
00234 return kState_ChangingState;
00235 return internalState;
00236 }
00237
00245 ProgramInfo *TVRec::GetRecording(void)
00246 {
00247 QMutexLocker lock(&stateChangeLock);
00248
00249 ProgramInfo *tmppginfo = NULL;
00250
00251 if (curRecording && !changeState)
00252 {
00253 tmppginfo = new ProgramInfo(*curRecording);
00254 tmppginfo->SetRecordingStatus(rsRecording);
00255 }
00256 else
00257 tmppginfo = new ProgramInfo();
00258 tmppginfo->SetCardID(cardid);
00259
00260 return tmppginfo;
00261 }
00262
00277 void TVRec::RecordPending(const ProgramInfo *rcinfo, int secsleft,
00278 bool hasLater)
00279 {
00280 QMutexLocker statelock(&stateChangeLock);
00281 QMutexLocker pendlock(&pendingRecLock);
00282
00283 if (secsleft < 0)
00284 {
00285 LOG(VB_RECORD, LOG_INFO, LOC + "Pending recording revoked on " +
00286 QString("inputid %1").arg(rcinfo->GetInputID()));
00287
00288 PendingMap::iterator it = pendingRecordings.find(rcinfo->GetCardID());
00289 if (it != pendingRecordings.end())
00290 {
00291 (*it).ask = false;
00292 (*it).doNotAsk = (*it).canceled = true;
00293 }
00294 return;
00295 }
00296
00297 LOG(VB_RECORD, LOG_INFO, LOC +
00298 QString("RecordPending on inputid %1").arg(rcinfo->GetInputID()));
00299
00300 PendingInfo pending;
00301 pending.info = new ProgramInfo(*rcinfo);
00302 pending.recordingStart = QDateTime::currentDateTime().addSecs(secsleft);
00303 pending.hasLaterShowing = hasLater;
00304 pending.ask = true;
00305 pending.doNotAsk = false;
00306
00307 pendingRecordings[rcinfo->GetCardID()] = pending;
00308
00309
00310 if (rcinfo->GetCardID() != cardid)
00311 return;
00312
00313
00314 vector<uint> cardids = CardUtil::GetConflictingCards(
00315 rcinfo->GetInputID(), cardid);
00316
00317 pendingRecordings[rcinfo->GetCardID()].possibleConflicts = cardids;
00318
00319 pendlock.unlock();
00320 statelock.unlock();
00321 for (uint i = 0; i < cardids.size(); i++)
00322 RemoteRecordPending(cardids[i], rcinfo, secsleft, hasLater);
00323 statelock.relock();
00324 pendlock.relock();
00325 }
00326
00330 void TVRec::SetPseudoLiveTVRecording(ProgramInfo *pi)
00331 {
00332 ProgramInfo *old_rec = pseudoLiveTVRecording;
00333 pseudoLiveTVRecording = pi;
00334 if (old_rec)
00335 delete old_rec;
00336 }
00337
00341 QDateTime TVRec::GetRecordEndTime(const ProgramInfo *pi) const
00342 {
00343 bool spcat = (!overRecordCategory.isEmpty() &&
00344 pi->GetCategory() == overRecordCategory);
00345 int secs = (spcat) ? overRecordSecCat : overRecordSecNrml;
00346 return pi->GetRecordingEndTime().addSecs(secs);
00347 }
00348
00354 void TVRec::CancelNextRecording(bool cancel)
00355 {
00356 QMutexLocker pendlock(&pendingRecLock);
00357 LOG(VB_RECORD, LOG_INFO, LOC +
00358 QString("CancelNextRecording(%1) -- begin").arg(cancel));
00359
00360 PendingMap::iterator it = pendingRecordings.find(cardid);
00361 if (it == pendingRecordings.end())
00362 {
00363 LOG(VB_RECORD, LOG_INFO, LOC + QString("CancelNextRecording(%1) -- "
00364 "error, unknown recording").arg(cancel));
00365 return;
00366 }
00367
00368 if (cancel)
00369 {
00370 vector<uint> &cardids = (*it).possibleConflicts;
00371 for (uint i = 0; i < cardids.size(); i++)
00372 {
00373 LOG(VB_RECORD, LOG_INFO, LOC +
00374 QString("CancelNextRecording -- cardid 0x%1")
00375 .arg((uint64_t)cardids[i],0,16));
00376
00377 pendlock.unlock();
00378 RemoteRecordPending(cardids[i], (*it).info, -1, false);
00379 pendlock.relock();
00380 }
00381
00382 LOG(VB_RECORD, LOG_INFO, LOC +
00383 QString("CancelNextRecording -- cardid %1")
00384 .arg(cardid));
00385
00386 RecordPending((*it).info, -1, false);
00387 }
00388 else
00389 {
00390 (*it).canceled = false;
00391 }
00392
00393 LOG(VB_RECORD, LOG_INFO, LOC +
00394 QString("CancelNextRecording(%1) -- end").arg(cancel));
00395 }
00396
00404 RecStatusType TVRec::StartRecording(const ProgramInfo *rcinfo)
00405 {
00406 LOG(VB_RECORD, LOG_INFO, LOC + QString("StartRecording(%1)")
00407 .arg(rcinfo->toString(ProgramInfo::kTitleSubtitle)));
00408
00409 QMutexLocker lock(&stateChangeLock);
00410 QString msg("");
00411
00412 SetRecordingStatus(rsAborted, __LINE__);
00413
00414
00415 WaitForEventThreadSleep();
00416
00417
00418
00419 if (internalState != kState_WatchingLiveTV &&
00420 curRecording && curRecording->IsSameProgramWeakCheck(*rcinfo))
00421 {
00422 int post_roll_seconds = curRecording->GetRecordingEndTime()
00423 .secsTo(recordEndTime);
00424
00425 curRecording->SetRecordingRuleType(rcinfo->GetRecordingRuleType());
00426 curRecording->SetRecordingRuleID(rcinfo->GetRecordingRuleID());
00427 curRecording->SetRecordingEndTime(rcinfo->GetRecordingEndTime());
00428 curRecording->UpdateRecordingEnd();
00429
00430 recordEndTime = curRecording->GetRecordingEndTime()
00431 .addSecs(post_roll_seconds);
00432
00433 msg = QString("updating recording: %1 %2 %3 %4")
00434 .arg(curRecording->GetTitle()).arg(curRecording->GetChanID())
00435 .arg(curRecording->GetRecordingStartTime(ISODate))
00436 .arg(curRecording->GetRecordingEndTime(ISODate));
00437 LOG(VB_RECORD, LOG_INFO, LOC + msg);
00438
00439 ClearFlags(kFlagCancelNextRecording);
00440
00441 SetRecordingStatus(rsRecording, __LINE__);
00442 return rsRecording;
00443 }
00444
00445 bool cancelNext = false;
00446 PendingInfo pendinfo;
00447 PendingMap::iterator it;
00448 bool has_pending;
00449
00450 pendingRecLock.lock();
00451 if ((it = pendingRecordings.find(cardid)) != pendingRecordings.end())
00452 {
00453 (*it).ask = (*it).doNotAsk = false;
00454 cancelNext = (*it).canceled;
00455 }
00456 pendingRecLock.unlock();
00457
00458
00459 WaitForEventThreadSleep();
00460
00461
00462
00463
00464 pendingRecLock.lock();
00465 it = pendingRecordings.find(cardid);
00466 has_pending = (it != pendingRecordings.end());
00467 if (has_pending)
00468 pendinfo = *it;
00469 pendingRecLock.unlock();
00470
00471
00472
00473 if (!cancelNext && has_pending && !pendinfo.possibleConflicts.empty())
00474 {
00475 LOG(VB_RECORD, LOG_INFO, LOC +
00476 "Checking input group recorders - begin");
00477 vector<uint> &cardids = pendinfo.possibleConflicts;
00478
00479 uint mplexid = 0, sourceid = 0;
00480 vector<uint> cardids2;
00481 vector<TVState> states;
00482
00483
00484 for (uint i = 0; i < cardids.size(); i++)
00485 {
00486 TunedInputInfo busy_input;
00487 bool is_busy = RemoteIsBusy(cardids[i], busy_input);
00488
00489
00490
00491
00492 if (is_busy)
00493 {
00494 is_busy = (bool) igrp.GetSharedInputGroup(
00495 busy_input.inputid, rcinfo->GetInputID());
00496 }
00497
00498 if (is_busy && !sourceid)
00499 {
00500 mplexid = pendinfo.info->QueryMplexID();
00501 sourceid = pendinfo.info->GetSourceID();
00502 }
00503
00504 if (is_busy &&
00505 ((sourceid != busy_input.sourceid) ||
00506 (mplexid != busy_input.mplexid)))
00507 {
00508 states.push_back((TVState) RemoteGetState(cardids[i]));
00509 cardids2.push_back(cardids[i]);
00510 }
00511 }
00512
00513 bool ok = true;
00514 for (uint i = 0; (i < cardids2.size()) && ok; i++)
00515 {
00516 LOG(VB_RECORD, LOG_INFO, LOC +
00517 QString("Attempting to stop card %1 in state %2")
00518 .arg(cardids2[i]).arg(StateToString(states[i])));
00519
00520 bool success = RemoteStopRecording(cardids2[i]);
00521 if (success)
00522 {
00523 uint state = RemoteGetState(cardids2[i]);
00524 LOG(VB_GENERAL, LOG_INFO, LOC + QString("a %1: %2")
00525 .arg(cardids2[i]).arg(StateToString((TVState)state)));
00526 success = (kState_None == state);
00527 }
00528
00529
00530 if (success && states[i] == kState_WatchingLiveTV)
00531 {
00532 QString message = QString("QUIT_LIVETV %1").arg(cardids2[i]);
00533 MythEvent me(message);
00534 gCoreContext->dispatch(me);
00535 }
00536
00537 LOG(VB_RECORD, LOG_INFO, LOC +
00538 QString("Stopping recording on %1, %2") .arg(cardids2[i])
00539 .arg(success ? "succeeded" : "failed"));
00540
00541 ok &= success;
00542 }
00543
00544
00545 if (!ok)
00546 {
00547 CancelNextRecording(true);
00548 cancelNext = true;
00549 }
00550
00551 cardids.clear();
00552
00553 LOG(VB_RECORD, LOG_INFO, LOC + "Checking input group recorders - done");
00554 }
00555
00556 bool did_switch = false;
00557 if (!cancelNext && (GetState() == kState_RecordingOnly))
00558 {
00559 RecordingInfo *ri = SwitchRecordingRingBuffer(*rcinfo);
00560 did_switch = (NULL != ri);
00561 if (did_switch)
00562 {
00563
00564 ClearFlags(kFlagCancelNextRecording);
00565
00566 SetRecordingStatus(rsRecording, __LINE__);
00567 }
00568 else
00569 {
00570
00571 stateChangeLock.unlock();
00572 StopRecording();
00573 stateChangeLock.lock();
00574 }
00575 }
00576
00577 if (!cancelNext && (GetState() == kState_None))
00578 {
00579 if (tvchain)
00580 {
00581 QString message = QString("LIVETV_EXITED");
00582 MythEvent me(message, tvchain->GetID());
00583 gCoreContext->dispatch(me);
00584 tvchain = NULL;
00585 }
00586
00587 recordEndTime = GetRecordEndTime(rcinfo);
00588
00589
00590 curRecording = new RecordingInfo(*rcinfo);
00591 curRecording->MarkAsInUse(true, kRecorderInUseID);
00592 StartedRecording(curRecording);
00593
00594
00595 ClearFlags(kFlagCancelNextRecording);
00596
00597 SetRecordingStatus(rsTuning, __LINE__);
00598 ChangeState(kState_RecordingOnly);
00599 }
00600 else if (!cancelNext && (GetState() == kState_WatchingLiveTV))
00601 {
00602 SetPseudoLiveTVRecording(new ProgramInfo(*rcinfo));
00603 recordEndTime = GetRecordEndTime(rcinfo);
00604 SetRecordingStatus(rsRecording, __LINE__);
00605
00606
00607
00608
00609 QString message = QString("LIVETV_WATCH %1 1").arg(cardid);
00610 QStringList prog;
00611 rcinfo->ToStringList(prog);
00612 MythEvent me(message, prog);
00613 gCoreContext->dispatch(me);
00614 }
00615 else if (!did_switch)
00616 {
00617 msg = QString("Wanted to record: %1 %2 %3 %4\n\t\t\t")
00618 .arg(rcinfo->GetTitle()).arg(rcinfo->GetChanID())
00619 .arg(rcinfo->GetRecordingStartTime(ISODate))
00620 .arg(rcinfo->GetRecordingEndTime(ISODate));
00621
00622 if (cancelNext)
00623 {
00624 msg += "But a user has canceled this recording";
00625 SetRecordingStatus(rsCancelled, __LINE__);
00626 }
00627 else
00628 {
00629 msg += QString("But the current state is: %1")
00630 .arg(StateToString(internalState));
00631 SetRecordingStatus(rsTunerBusy, __LINE__);
00632 }
00633
00634 if (curRecording && internalState == kState_RecordingOnly)
00635 msg += QString("\n\t\t\tCurrently recording: %1 %2 %3 %4")
00636 .arg(curRecording->GetTitle()).arg(curRecording->GetChanID())
00637 .arg(curRecording->GetRecordingStartTime(ISODate))
00638 .arg(curRecording->GetRecordingEndTime(ISODate));
00639
00640 LOG(VB_GENERAL, LOG_INFO, LOC + msg);
00641 }
00642
00643 for (int i = 0; i < pendingRecordings.size(); i++)
00644 delete pendingRecordings[i].info;
00645 pendingRecordings.clear();
00646
00647 if (!did_switch)
00648 {
00649 WaitForEventThreadSleep();
00650
00651 QMutexLocker locker(&pendingRecLock);
00652 if ((curRecording) &&
00653 (curRecording->GetRecordingStatus() == rsFailed) &&
00654 (m_recStatus == rsRecording || m_recStatus == rsTuning))
00655 {
00656 SetRecordingStatus(rsFailed, __LINE__, true);
00657 }
00658 return m_recStatus;
00659 }
00660
00661 return GetRecordingStatus();
00662 }
00663
00664 RecStatusType TVRec::GetRecordingStatus(void) const
00665 {
00666 QMutexLocker pendlock(&pendingRecLock);
00667 return m_recStatus;
00668 }
00669
00670 void TVRec::SetRecordingStatus(
00671 RecStatusType new_status, int line, bool have_lock)
00672 {
00673 RecStatusType old_status;
00674 if (have_lock)
00675 {
00676 old_status = m_recStatus;
00677 m_recStatus = new_status;
00678 }
00679 else
00680 {
00681 pendingRecLock.lock();
00682 old_status = m_recStatus;
00683 m_recStatus = new_status;
00684 pendingRecLock.unlock();
00685 }
00686
00687 LOG(VB_RECORD, LOG_DEBUG, LOC +
00688 QString("SetRecordingStatus(%1->%2) on line %3")
00689 .arg(toString(old_status, kSingleRecord))
00690 .arg(toString(new_status, kSingleRecord))
00691 .arg(line));
00692 }
00693
00698 void TVRec::StopRecording(bool killFile)
00699 {
00700 if (StateIsRecording(GetState()))
00701 {
00702 QMutexLocker lock(&stateChangeLock);
00703 if (killFile)
00704 SetFlags(kFlagKillRec);
00705 ChangeState(RemoveRecording(GetState()));
00706
00707 WaitForEventThreadSleep();
00708 ClearFlags(kFlagCancelNextRecording|kFlagKillRec);
00709
00710 SetRecordingStatus(rsUnknown, __LINE__);
00711 }
00712 }
00713
00719 bool TVRec::StateIsRecording(TVState state)
00720 {
00721 return (state == kState_RecordingOnly ||
00722 state == kState_WatchingLiveTV);
00723 }
00724
00729 bool TVRec::StateIsPlaying(TVState state)
00730 {
00731 return (state == kState_WatchingPreRecorded);
00732 }
00733
00739 TVState TVRec::RemoveRecording(TVState state)
00740 {
00741 if (StateIsRecording(state))
00742 return kState_None;
00743
00744 LOG(VB_GENERAL, LOG_ERR, LOC +
00745 QString("Unknown state in RemoveRecording: %1")
00746 .arg(StateToString(state)));
00747 return kState_Error;
00748 }
00749
00755 TVState TVRec::RemovePlaying(TVState state)
00756 {
00757 if (StateIsPlaying(state))
00758 {
00759 if (state == kState_WatchingPreRecorded)
00760 return kState_None;
00761 return kState_RecordingOnly;
00762 }
00763
00764 QString msg = "Unknown state in RemovePlaying: %1";
00765 LOG(VB_GENERAL, LOG_ERR, LOC + msg.arg(StateToString(state)));
00766
00767 return kState_Error;
00768 }
00769
00775 void TVRec::StartedRecording(RecordingInfo *curRec)
00776 {
00777 if (!curRec)
00778 return;
00779
00780 curRec->StartedRecording(rbFileExt);
00781 LOG(VB_RECORD, LOG_INFO, LOC + QString("StartedRecording(%1) fn(%2)")
00782 .arg(curRec->MakeUniqueKey()).arg(curRec->GetPathname()));
00783
00784 if (curRec->IsCommercialFree())
00785 curRec->SaveCommFlagged(COMM_FLAG_COMMFREE);
00786
00787 SendMythSystemRecEvent("REC_STARTED", curRec);
00788 }
00789
00796 void TVRec::FinishedRecording(RecordingInfo *curRec, RecordingQuality *recq)
00797 {
00798 if (!curRec)
00799 return;
00800
00801
00802 const QString recgrp = curRec->QueryRecordingGroup();
00803 curRec->SetRecordingGroup(recgrp);
00804
00805 bool is_good = true;
00806 if (recq)
00807 {
00808 LOG((recq->IsDamaged()) ? VB_GENERAL : VB_RECORD, LOG_INFO,
00809 LOC + QString("FinishedRecording(%1) %2 recq:%3\n")
00810 .arg(curRec->MakeUniqueKey())
00811 .arg((recq->IsDamaged()) ? "damaged" : "good")
00812 .arg(recq->toStringXML()));
00813 is_good = !recq->IsDamaged();
00814 delete recq;
00815 recq = NULL;
00816 }
00817
00818 RecStatusTypes ors = curRec->GetRecordingStatus();
00819
00820 if (curRec->GetRecordingStatus() == rsRecording)
00821 curRec->SetRecordingStatus(rsRecorded);
00822 else if (curRec->GetRecordingStatus() != rsRecorded)
00823 curRec->SetRecordingStatus(rsFailed);
00824 curRec->SetRecordingEndTime(mythCurrentDateTime());
00825 is_good &= (curRec->GetRecordingStatus() == rsRecorded);
00826
00827
00828 bool was_finished = false;
00829 static QMutex finRecLock;
00830 static QHash<QString,QDateTime> finRecMap;
00831 {
00832 QMutexLocker locker(&finRecLock);
00833 QDateTime now = QDateTime::currentDateTime();
00834 QDateTime expired = now.addSecs(-60*5);
00835 QHash<QString,QDateTime>::iterator it = finRecMap.begin();
00836 while (it != finRecMap.end())
00837 {
00838 if ((*it) < expired)
00839 it = finRecMap.erase(it);
00840 else
00841 ++it;
00842 }
00843 QString key = curRec->MakeUniqueKey();
00844 it = finRecMap.find(key);
00845 if (it != finRecMap.end())
00846 was_finished = true;
00847 else
00848 finRecMap[key] = now;
00849 }
00850
00851
00852 LOG(VB_RECORD, LOG_INFO, LOC +
00853 QString("FinishedRecording(%1)"
00854 "\n\t\t\ttitle: %2\n\t\t\t"
00855 "in recgroup: %3 status: %4:%5 %6 %7")
00856 .arg(curRec->MakeUniqueKey())
00857 .arg(curRec->GetTitle())
00858 .arg(recgrp)
00859 .arg(toString(ors, kSingleRecord))
00860 .arg(toString(curRec->GetRecordingStatus(), kSingleRecord))
00861 .arg(HasFlags(kFlagDummyRecorderRunning)?"is_dummy":"not_dummy")
00862 .arg(was_finished?"already_finished":"finished_now"));
00863
00864
00865 if (was_finished)
00866 return;
00867
00868
00869 if (tvchain)
00870 tvchain->FinishedRecording(curRec);
00871
00872
00873 if (HasFlags(kFlagDummyRecorderRunning))
00874 {
00875 curRec->FinishedRecording(true);
00876 SendMythSystemRecEvent("REC_FINISHED", curRecording);
00877 return;
00878 }
00879
00880
00881 uint avg_height = curRec->QueryAverageHeight();
00882 curRec->SaveVideoProperties(
00883 VID_1080 | VID_720 | VID_DAMAGED,
00884 ((avg_height > 1000) ? VID_1080 : ((avg_height > 700) ? VID_720 : 0)) |
00885 ((is_good) ? 0 : VID_DAMAGED));
00886
00887
00888 if (curRec->GetRecordingEndTime() <= curRec->GetRecordingStartTime())
00889 {
00890 curRec->SetRecordingEndTime(
00891 curRec->GetRecordingStartTime().addSecs(60));
00892 }
00893
00894
00895 uint64_t fsize = (curRec->GetFilesize() < 1000) ?
00896 curRec->QueryFilesize() : curRec->GetFilesize();
00897 if (curRec->IsLocal() && (fsize >= 1000) &&
00898 (curRec->GetRecordingStatus() == rsRecorded))
00899 {
00900 PreviewGeneratorQueue::GetPreviewImage(*curRec, "");
00901 }
00902
00903
00904 if (recgrp != "LiveTV")
00905 {
00906 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
00907 .arg(curRec->GetCardID())
00908 .arg(curRec->GetChanID())
00909 .arg(curRec->GetScheduledStartTime(ISODate))
00910 .arg(curRec->GetRecordingStatus())
00911 .arg(curRec->GetRecordingEndTime(ISODate)));
00912 gCoreContext->dispatch(me);
00913 }
00914
00915
00916 curRec->FinishedRecording(!is_good || (recgrp == "LiveTV"));
00917
00918
00919 SendMythSystemRecEvent("REC_FINISHED", curRecording);
00920
00921
00922 int secsSince = curRec->GetRecordingStartTime()
00923 .secsTo(QDateTime::currentDateTime());
00924 QString message = QString("DONE_RECORDING %1 %2 %3")
00925 .arg(cardid).arg(secsSince).arg(GetFramesWritten());
00926 MythEvent me(message);
00927 gCoreContext->dispatch(me);
00928
00929
00930 if ((recgrp == "LiveTV") || (fsize < 1000) ||
00931 (curRec->GetRecordingStatus() != rsRecorded) ||
00932 (curRec->GetRecordingStartTime().secsTo(
00933 QDateTime::currentDateTime()) < 120))
00934 {
00935 JobQueue::RemoveJobsFromMask(JOB_COMMFLAG, autoRunJobs);
00936 JobQueue::RemoveJobsFromMask(JOB_TRANSCODE, autoRunJobs);
00937 }
00938 if (autoRunJobs)
00939 {
00940 JobQueue::QueueRecordingJobs(*curRec, autoRunJobs);
00941 }
00942 }
00943
00944 #define TRANSITION(ASTATE,BSTATE) \
00945 ((internalState == ASTATE) && (desiredNextState == BSTATE))
00946 #define SET_NEXT() do { nextState = desiredNextState; changed = true; } while(0)
00947 #define SET_LAST() do { nextState = internalState; changed = true; } while(0)
00948
00956 void TVRec::HandleStateChange(void)
00957 {
00958 TVState nextState = internalState;
00959
00960 bool changed = false;
00961
00962 QString transMsg = QString(" %1 to %2")
00963 .arg(StateToString(nextState))
00964 .arg(StateToString(desiredNextState));
00965
00966 if (desiredNextState == internalState)
00967 {
00968 LOG(VB_GENERAL, LOG_ERR, LOC +
00969 "HandleStateChange(): Null transition" + transMsg);
00970 changeState = false;
00971 return;
00972 }
00973
00974
00975
00976 if (scanner && HasFlags(kFlagEITScannerRunning))
00977 {
00978 scanner->StopActiveScan();
00979 ClearFlags(kFlagEITScannerRunning);
00980 }
00981
00982
00983 if (TRANSITION(kState_None, kState_WatchingLiveTV))
00984 {
00985 tuningRequests.enqueue(TuningRequest(kFlagLiveTV));
00986 SET_NEXT();
00987 }
00988 else if (TRANSITION(kState_WatchingLiveTV, kState_None))
00989 {
00990 tuningRequests.enqueue(TuningRequest(kFlagKillRec|kFlagKillRingBuffer));
00991 SET_NEXT();
00992 }
00993 else if (TRANSITION(kState_WatchingLiveTV, kState_RecordingOnly))
00994 {
00995 SetPseudoLiveTVRecording(NULL);
00996
00997 SET_NEXT();
00998 }
00999 else if (TRANSITION(kState_None, kState_RecordingOnly))
01000 {
01001 SetPseudoLiveTVRecording(NULL);
01002 tuningRequests.enqueue(TuningRequest(kFlagRecording, curRecording));
01003 SET_NEXT();
01004 }
01005 else if (TRANSITION(kState_RecordingOnly, kState_None))
01006 {
01007 tuningRequests.enqueue(
01008 TuningRequest(kFlagCloseRec|kFlagKillRingBuffer|
01009 (GetFlags()&kFlagKillRec)));
01010 SET_NEXT();
01011 }
01012
01013 QString msg = (changed) ? "Changing from" : "Unknown state transition:";
01014 LOG(VB_GENERAL, LOG_INFO, LOC + msg + transMsg);
01015
01016
01017 internalState = nextState;
01018 changeState = false;
01019
01020 eitScanStartTime = QDateTime::currentDateTime();
01021 if (scanner && (internalState == kState_None))
01022 eitScanStartTime = eitScanStartTime.addSecs(eitCrawlIdleStart);
01023 else
01024 eitScanStartTime = eitScanStartTime.addYears(1);
01025 }
01026 #undef TRANSITION
01027 #undef SET_NEXT
01028 #undef SET_LAST
01029
01033 void TVRec::ChangeState(TVState nextState)
01034 {
01035 QMutexLocker lock(&stateChangeLock);
01036 desiredNextState = nextState;
01037 changeState = true;
01038 WakeEventLoop();
01039 }
01040
01055 void TVRec::TeardownRecorder(uint request_flags)
01056 {
01057 pauseNotify = false;
01058 ispip = false;
01059
01060 if (recorder && HasFlags(kFlagRecorderRunning))
01061 {
01062 recorder->StopRecording();
01063 recorderThread->wait();
01064 delete recorderThread;
01065 recorderThread = NULL;
01066 }
01067 ClearFlags(kFlagRecorderRunning);
01068
01069 RecordingQuality *recq = NULL;
01070 if (recorder)
01071 {
01072 if (GetV4LChannel())
01073 channel->SetFd(-1);
01074
01075 recq = recorder->GetRecordingQuality();
01076
01077 QMutexLocker locker(&stateChangeLock);
01078 delete recorder;
01079 recorder = NULL;
01080 }
01081
01082 if (ringBuffer)
01083 {
01084 LOG(VB_FILE, LOG_INFO, LOC + "calling StopReads()");
01085 ringBuffer->StopReads();
01086 }
01087
01088 if (curRecording)
01089 {
01090 if (!!(request_flags & kFlagKillRec))
01091 curRecording->SetRecordingStatus(rsFailed);
01092
01093 FinishedRecording(curRecording, recq);
01094
01095 curRecording->MarkAsInUse(false, kRecorderInUseID);
01096 delete curRecording;
01097 curRecording = NULL;
01098 }
01099
01100 pauseNotify = true;
01101
01102 if (GetDTVChannel())
01103 GetDTVChannel()->EnterPowerSavingMode();
01104 }
01105
01106 DTVRecorder *TVRec::GetDTVRecorder(void)
01107 {
01108 return dynamic_cast<DTVRecorder*>(recorder);
01109 }
01110
01111 void TVRec::CloseChannel(void)
01112 {
01113 if (channel &&
01114 ((genOpt.cardtype == "DVB" && dvbOpt.dvb_on_demand) ||
01115 CardUtil::IsV4L(genOpt.cardtype)))
01116 {
01117 channel->Close();
01118 }
01119 }
01120
01121 DTVChannel *TVRec::GetDTVChannel(void)
01122 {
01123 return dynamic_cast<DTVChannel*>(channel);
01124 }
01125
01126 V4LChannel *TVRec::GetV4LChannel(void)
01127 {
01128 #ifdef USING_V4L2
01129 return dynamic_cast<V4LChannel*>(channel);
01130 #else
01131 return NULL;
01132 #endif // USING_V4L2
01133 }
01134
01135 static bool get_use_eit(uint cardid)
01136 {
01137 MSqlQuery query(MSqlQuery::InitCon());
01138 query.prepare(
01139 "SELECT SUM(useeit) "
01140 "FROM videosource, cardinput "
01141 "WHERE videosource.sourceid = cardinput.sourceid AND"
01142 " cardinput.cardid = :CARDID");
01143 query.bindValue(":CARDID", cardid);
01144
01145 if (!query.exec() || !query.isActive())
01146 {
01147 MythDB::DBError("get_use_eit", query);
01148 return false;
01149 }
01150 else if (query.next())
01151 return query.value(0).toBool();
01152 return false;
01153 }
01154
01155 static bool is_dishnet_eit(uint cardid)
01156 {
01157 MSqlQuery query(MSqlQuery::InitCon());
01158 query.prepare(
01159 "SELECT SUM(dishnet_eit) "
01160 "FROM videosource, cardinput "
01161 "WHERE videosource.sourceid = cardinput.sourceid AND"
01162 " cardinput.cardid = :CARDID");
01163 query.bindValue(":CARDID", cardid);
01164
01165 if (!query.exec() || !query.isActive())
01166 {
01167 MythDB::DBError("is_dishnet_eit", query);
01168 return false;
01169 }
01170 else if (query.next())
01171 return query.value(0).toBool();
01172 return false;
01173 }
01174
01175 static int no_capturecards(uint cardid)
01176 {
01177 MSqlQuery query(MSqlQuery::InitCon());
01178
01179 QString str =
01180 "SELECT COUNT(cardid) "
01181 "FROM capturecard ";
01182
01183 if (cardid)
01184 str += "WHERE cardid < :CARDID";
01185
01186 query.prepare(str);
01187
01188 if (cardid)
01189 query.bindValue(":CARDID", cardid);
01190
01191 if (!query.exec() || !query.isActive())
01192 {
01193 MythDB::DBError("no_capturecards", query);
01194 return -1;
01195 }
01196 else if (query.next())
01197 return query.value(0).toInt();
01198 return -1;
01199 }
01200
01202 void TVRec::run(void)
01203 {
01204 QMutexLocker lock(&stateChangeLock);
01205 SetFlags(kFlagRunMainLoop);
01206 ClearFlags(kFlagExitPlayer | kFlagFinishRecording);
01207
01208 eitScanStartTime = QDateTime::currentDateTime();
01209
01210 if (CardUtil::IsEITCapable(genOpt.cardtype) &&
01211 (!GetDTVChannel() || GetDTVChannel()->IsMaster()) &&
01212 (dvbOpt.dvb_eitscan || get_use_eit(cardid)))
01213 {
01214 scanner = new EITScanner(cardid);
01215 uint timeout = eitCrawlIdleStart;
01216
01217
01218 int card_pos = no_capturecards(cardid);
01219 int no_cards = no_capturecards(0);
01220 if (no_cards > 0 && card_pos >= 0)
01221 timeout += eitTransportTimeout * card_pos / no_cards;
01222 else
01223 timeout += random() % eitTransportTimeout;
01224
01225 eitScanStartTime = eitScanStartTime.addSecs(timeout);
01226 }
01227 else
01228 eitScanStartTime = eitScanStartTime.addYears(1);
01229
01230 while (HasFlags(kFlagRunMainLoop))
01231 {
01232
01233 if (changeState)
01234 {
01235 HandleStateChange();
01236 ClearFlags(kFlagFrontendReady | kFlagCancelNextRecording);
01237 }
01238
01239
01240 if (IsErrored())
01241 {
01242 LOG(VB_GENERAL, LOG_ERR, LOC +
01243 "RunTV encountered fatal error, exiting event thread.");
01244 ClearFlags(kFlagRunMainLoop);
01245 return;
01246 }
01247
01248
01249 HandleTuning();
01250
01251
01252 HandlePendingRecordings();
01253
01254
01255
01256
01257
01258 QDateTime recEnd = (!pendingRecordings.empty()) ?
01259 recordEndTime.addSecs(60) : recordEndTime;
01260 if ((GetState() == kState_RecordingOnly) &&
01261 (QDateTime::currentDateTime() > recEnd ||
01262 HasFlags(kFlagFinishRecording)))
01263 {
01264 ChangeState(kState_None);
01265 ClearFlags(kFlagFinishRecording);
01266 }
01267
01268 if (curRecording)
01269 {
01270 curRecording->UpdateInUseMark();
01271
01272 if (recorder)
01273 {
01274 recorder->SavePositionMap();
01275
01276
01277 if (recorder->IsErrored())
01278 {
01279 curRecording->SetRecordingStatus(rsFailed);
01280
01281 if (GetState() == kState_WatchingLiveTV)
01282 {
01283 QString message = QString("QUIT_LIVETV %1").arg(cardid);
01284 MythEvent me(message);
01285 gCoreContext->dispatch(me);
01286 }
01287 else
01288 ChangeState(kState_None);
01289 }
01290 }
01291 }
01292
01293
01294 if (GetState() == kState_WatchingLiveTV)
01295 {
01296 QDateTime now = QDateTime::currentDateTime();
01297 bool has_finish = HasFlags(kFlagFinishRecording);
01298 bool has_rec = pseudoLiveTVRecording;
01299 bool enable_ui = true;
01300
01301 pendingRecLock.lock();
01302 bool rec_soon =
01303 pendingRecordings.find(cardid) != pendingRecordings.end();
01304 pendingRecLock.unlock();
01305
01306 if (has_rec && (has_finish || (now > recordEndTime)))
01307 {
01308 SetPseudoLiveTVRecording(NULL);
01309 }
01310 else if (!has_rec && !rec_soon && curRecording &&
01311 (now >= curRecording->GetScheduledEndTime()))
01312 {
01313 if (!switchingBuffer)
01314 {
01315 LOG(VB_RECORD, LOG_INFO, LOC +
01316 "Switching Buffer (" +
01317 QString("!has_rec(%1) && ").arg(has_rec) +
01318 QString("!rec_soon(%1) && (").arg(rec_soon) +
01319 now.toString(Qt::ISODate) + " >= " +
01320 curRecording->GetScheduledEndTime(ISODate) +
01321 QString("(%1) ))")
01322 .arg(now >= curRecording->GetScheduledEndTime()));
01323
01324 switchingBuffer = true;
01325
01326 SwitchLiveTVRingBuffer(channel->GetCurrentName(),
01327 false, true);
01328 }
01329 else
01330 {
01331 LOG(VB_RECORD, LOG_INFO, "Waiting for ringbuffer switch");
01332 }
01333 }
01334 else
01335 enable_ui = false;
01336
01337 if (enable_ui)
01338 {
01339 LOG(VB_RECORD, LOG_INFO, LOC + "Enabling Full LiveTV UI.");
01340 QString message = QString("LIVETV_WATCH %1 0").arg(cardid);
01341 MythEvent me(message);
01342 gCoreContext->dispatch(me);
01343 }
01344 }
01345
01346
01347
01348 if (HasFlags(kFlagExitPlayer))
01349 {
01350 if (internalState == kState_WatchingLiveTV)
01351 ChangeState(kState_None);
01352 else if (StateIsPlaying(internalState))
01353 ChangeState(RemovePlaying(internalState));
01354 ClearFlags(kFlagExitPlayer);
01355 }
01356
01357 if (scanner && channel &&
01358 QDateTime::currentDateTime() > eitScanStartTime)
01359 {
01360 if (!dvbOpt.dvb_eitscan)
01361 {
01362 LOG(VB_EIT, LOG_INFO, LOC +
01363 "EIT scanning disabled for this card.");
01364 eitScanStartTime = eitScanStartTime.addYears(1);
01365 }
01366 else if (!get_use_eit(GetCaptureCardNum()))
01367 {
01368 LOG(VB_EIT, LOG_INFO, LOC +
01369 "EIT scanning disabled for all sources on this card.");
01370 eitScanStartTime = eitScanStartTime.addYears(1);
01371 }
01372 else
01373 {
01374 scanner->StartActiveScan(this, eitTransportTimeout);
01375 SetFlags(kFlagEITScannerRunning);
01376 eitScanStartTime = QDateTime::currentDateTime().addYears(1);
01377 }
01378 }
01379
01380
01381
01382
01383
01384 if (tuningRequests.empty() && !changeState)
01385 {
01386 lock.unlock();
01387
01388 {
01389 QMutexLocker locker(&triggerEventSleepLock);
01390 triggerEventSleepSignal = true;
01391 triggerEventSleepWait.wakeAll();
01392 }
01393
01394 sched_yield();
01395
01396 {
01397 QMutexLocker locker(&triggerEventLoopLock);
01398
01399
01400
01401 if (!triggerEventLoopSignal)
01402 {
01403 triggerEventLoopWait.wait(
01404 &triggerEventLoopLock, 1000 );
01405 }
01406 triggerEventLoopSignal = false;
01407 }
01408
01409 lock.relock();
01410 }
01411 }
01412
01413 if (GetState() != kState_None)
01414 {
01415 ChangeState(kState_None);
01416 HandleStateChange();
01417 }
01418 }
01419
01425 bool TVRec::WaitForEventThreadSleep(bool wake, ulong time)
01426 {
01427 bool ok = false;
01428 MythTimer t;
01429 t.start();
01430
01431 while (!ok && ((unsigned long) t.elapsed()) < time)
01432 {
01433 MythTimer t2;
01434 t2.start();
01435
01436 if (wake)
01437 WakeEventLoop();
01438
01439 stateChangeLock.unlock();
01440
01441 sched_yield();
01442
01443 {
01444 QMutexLocker locker(&triggerEventSleepLock);
01445 if (!triggerEventSleepSignal)
01446 triggerEventSleepWait.wait(&triggerEventSleepLock);
01447 triggerEventSleepSignal = false;
01448 }
01449
01450 stateChangeLock.lock();
01451
01452
01453 ok = (tuningRequests.empty() && !changeState);
01454
01455 int te = t2.elapsed();
01456 if (!ok && te < 10)
01457 usleep((10-te) * 1000);
01458 }
01459 return ok;
01460 }
01461
01462 void TVRec::HandlePendingRecordings(void)
01463 {
01464 QMutexLocker pendlock(&pendingRecLock);
01465
01466 if (pendingRecordings.empty())
01467 return;
01468
01469
01470
01471
01472
01473 PendingMap::iterator it, next;
01474
01475 for (it = pendingRecordings.begin(); it != pendingRecordings.end();)
01476 {
01477 next = it; ++next;
01478 if (QDateTime::currentDateTime() > (*it).recordingStart.addSecs(30))
01479 {
01480 LOG(VB_RECORD, LOG_INFO, LOC + "Deleting stale pending recording " +
01481 QString("%1 '%2'")
01482 .arg((*it).info->GetCardID())
01483 .arg((*it).info->GetTitle()));
01484
01485 delete (*it).info;
01486 pendingRecordings.erase(it);
01487 }
01488 it = next;
01489 }
01490
01491 bool has_rec = false;
01492 it = pendingRecordings.begin();
01493 if ((1 == pendingRecordings.size()) &&
01494 (*it).ask &&
01495 ((*it).info->GetCardID() == cardid) &&
01496 (GetState() == kState_WatchingLiveTV))
01497 {
01498 CheckForRecGroupChange();
01499 has_rec = pseudoLiveTVRecording &&
01500 (pseudoLiveTVRecording->GetRecordingEndTime() >
01501 (*it).recordingStart);
01502 }
01503
01504 for (it = pendingRecordings.begin(); it != pendingRecordings.end(); ++it)
01505 {
01506 if (!(*it).ask && !(*it).doNotAsk)
01507 continue;
01508
01509 int timeuntil = ((*it).doNotAsk) ?
01510 -1: QDateTime::currentDateTime().secsTo((*it).recordingStart);
01511
01512 if (has_rec)
01513 (*it).canceled = true;
01514
01515 QString query = QString("ASK_RECORDING %1 %2 %3 %4")
01516 .arg(cardid)
01517 .arg(timeuntil)
01518 .arg(has_rec ? 1 : 0)
01519 .arg((*it).hasLaterShowing ? 1 : 0);
01520
01521 LOG(VB_GENERAL, LOG_INFO, LOC + query);
01522
01523 QStringList msg;
01524 (*it).info->ToStringList(msg);
01525 MythEvent me(query, msg);
01526 gCoreContext->dispatch(me);
01527
01528 (*it).ask = (*it).doNotAsk = false;
01529 }
01530 }
01531
01532 bool TVRec::GetDevices(uint cardid,
01533 GeneralDBOptions &gen_opts,
01534 DVBDBOptions &dvb_opts,
01535 FireWireDBOptions &firewire_opts)
01536 {
01537 int testnum = 0;
01538 QString test;
01539
01540 MSqlQuery query(MSqlQuery::InitCon());
01541 query.prepare(
01542 "SELECT videodevice, vbidevice, audiodevice, "
01543 " audioratelimit, cardtype, "
01544 " skipbtaudio, signal_timeout, channel_timeout, "
01545 " dvb_wait_for_seqstart, "
01546 ""
01547 " dvb_on_demand, dvb_tuning_delay, dvb_eitscan,"
01548 ""
01549 " firewire_speed, firewire_model, firewire_connection "
01550 ""
01551 "FROM capturecard "
01552 "WHERE cardid = :CARDID");
01553 query.bindValue(":CARDID", cardid);
01554
01555 if (!query.exec() || !query.isActive())
01556 {
01557 MythDB::DBError("getdevices", query);
01558 return false;
01559 }
01560
01561 if (!query.next())
01562 return false;
01563
01564
01565 test = query.value(0).toString();
01566 if (test != QString::null)
01567 gen_opts.videodev = test;
01568
01569 test = query.value(1).toString();
01570 if (test != QString::null)
01571 gen_opts.vbidev = test;
01572
01573 test = query.value(2).toString();
01574 if (test != QString::null)
01575 gen_opts.audiodev = test;
01576
01577 gen_opts.audiosamplerate = max(testnum, query.value(3).toInt());
01578
01579 test = query.value(4).toString();
01580 if (test != QString::null)
01581 gen_opts.cardtype = test;
01582
01583 gen_opts.skip_btaudio = query.value(5).toUInt();
01584
01585 gen_opts.signal_timeout = (uint) max(query.value(6).toInt(), 0);
01586 gen_opts.channel_timeout = (uint) max(query.value(7).toInt(), 0);
01587
01588
01589 int table_timeout = ((int)gen_opts.channel_timeout -
01590 (int)gen_opts.signal_timeout);
01591 if (table_timeout < 100)
01592 gen_opts.channel_timeout = gen_opts.signal_timeout + 2500;
01593
01594 gen_opts.wait_for_seqstart = query.value(8).toUInt();
01595
01596
01597 uint dvboff = 9;
01598 dvb_opts.dvb_on_demand = query.value(dvboff + 0).toUInt();
01599 dvb_opts.dvb_tuning_delay = query.value(dvboff + 1).toUInt();
01600 dvb_opts.dvb_eitscan = query.value(dvboff + 2).toUInt();
01601
01602
01603 uint fireoff = dvboff + 3;
01604 firewire_opts.speed = query.value(fireoff + 0).toUInt();
01605
01606 test = query.value(fireoff + 1).toString();
01607 if (test != QString::null)
01608 firewire_opts.model = test;
01609
01610 firewire_opts.connection = query.value(fireoff + 2).toUInt();
01611
01612 return true;
01613 }
01614
01615 QString TVRec::GetStartChannel(uint cardid, const QString &startinput)
01616 {
01617 QString startchan = QString::null;
01618
01619
01620 MSqlQuery query(MSqlQuery::InitCon());
01621 query.prepare(
01622 "SELECT startchan "
01623 "FROM cardinput "
01624 "WHERE cardinput.cardid = :CARDID AND "
01625 " inputname = :INPUTNAME");
01626 query.bindValue(":CARDID", cardid);
01627 query.bindValue(":INPUTNAME", startinput);
01628
01629 if (!query.exec() || !query.isActive())
01630 {
01631 MythDB::DBError("getstartchan", query);
01632 }
01633 else if (query.next())
01634 {
01635 startchan = query.value(0).toString();
01636 if (!startchan.isEmpty())
01637 {
01638 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Start channel: %1.")
01639 .arg(startchan));
01640 return startchan;
01641 }
01642 }
01643
01644
01645
01646 query.prepare(
01647 "SELECT channum "
01648 "FROM capturecard, cardinput, channel "
01649 "WHERE capturecard.cardid = cardinput.cardid AND "
01650 " channel.sourceid = cardinput.sourceid AND "
01651 " capturecard.cardid = :CARDID AND "
01652 " inputname = :INPUTNAME");
01653 query.bindValue(":CARDID", cardid);
01654 query.bindValue(":INPUTNAME", startinput);
01655
01656 if (!query.exec() || !query.isActive())
01657 {
01658 MythDB::DBError("getstartchan2", query);
01659 }
01660 while (query.next())
01661 {
01662 startchan = query.value(0).toString();
01663 if (!startchan.isEmpty())
01664 {
01665 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Start channel from DB is "
01666 "empty, setting to '%1' instead.").arg(startchan));
01667 return startchan;
01668 }
01669 }
01670
01671
01672
01673 query.prepare(
01674 "SELECT channum, inputname "
01675 "FROM capturecard, cardinput, channel "
01676 "WHERE capturecard.cardid = cardinput.cardid AND "
01677 " channel.sourceid = cardinput.sourceid AND "
01678 " capturecard.cardid = :CARDID");
01679 query.bindValue(":CARDID", cardid);
01680
01681 if (!query.exec() || !query.isActive())
01682 {
01683 MythDB::DBError("getstartchan3", query);
01684 }
01685 while (query.next())
01686 {
01687 startchan = query.value(0).toString();
01688 if (!startchan.isEmpty())
01689 {
01690 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Start channel invalid, "
01691 "setting to '%1' on input %2 instead.").arg(startchan)
01692 .arg(query.value(1).toString()));
01693 return startchan;
01694 }
01695 }
01696
01697
01698 startchan = "3";
01699 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Problem finding starting channel, "
01700 "setting to default of '%1'.").arg(startchan));
01701 return startchan;
01702 }
01703
01704 static void GetPidsToCache(DTVSignalMonitor *dtvMon, pid_cache_t &pid_cache)
01705 {
01706 if (!dtvMon->GetATSCStreamData())
01707 return;
01708
01709 const MasterGuideTable *mgt = dtvMon->GetATSCStreamData()->GetCachedMGT();
01710 if (!mgt)
01711 return;
01712
01713 for (uint i = 0; i < mgt->TableCount(); ++i)
01714 {
01715 pid_cache_item_t item(mgt->TablePID(i), mgt->TableType(i));
01716 pid_cache.push_back(item);
01717 }
01718 dtvMon->GetATSCStreamData()->ReturnCachedTable(mgt);
01719 }
01720
01721 static bool ApplyCachedPids(DTVSignalMonitor *dtvMon, const DTVChannel* channel)
01722 {
01723 pid_cache_t pid_cache;
01724 channel->GetCachedPids(pid_cache);
01725 pid_cache_t::const_iterator it = pid_cache.begin();
01726 bool vctpid_cached = false;
01727 for (; it != pid_cache.end(); ++it)
01728 {
01729 if ((it->GetTableID() == TableID::TVCT) ||
01730 (it->GetTableID() == TableID::CVCT))
01731 {
01732 vctpid_cached = true;
01733 dtvMon->GetATSCStreamData()->AddListeningPID(it->GetPID());
01734 }
01735 }
01736 return vctpid_cached;
01737 }
01738
01754 bool TVRec::SetupDTVSignalMonitor(bool EITscan)
01755 {
01756 LOG(VB_RECORD, LOG_INFO, LOC + "Setting up table monitoring.");
01757
01758 DTVSignalMonitor *sm = GetDTVSignalMonitor();
01759 DTVChannel *dtvchan = GetDTVChannel();
01760 if (!sm || !dtvchan)
01761 {
01762 LOG(VB_GENERAL, LOG_ERR, LOC + "Setting up table monitoring.");
01763 return false;
01764 }
01765
01766 MPEGStreamData *sd = NULL;
01767 if (GetDTVRecorder())
01768 {
01769 sd = GetDTVRecorder()->GetStreamData();
01770 sd->SetCaching(true);
01771 }
01772
01773 QString recording_type = "all";
01774 RecordingInfo *rec = lastTuningRequest.program;
01775 RecordingProfile profile;
01776 load_profile(genOpt.cardtype, tvchain, rec, profile);
01777 const Setting *setting = profile.byName("recordingtype");
01778 if (setting)
01779 recording_type = setting->getValue();
01780
01781 const QString tuningmode = dtvchan->GetTuningMode();
01782
01783
01784 int major = dtvchan->GetMajorChannel();
01785 int minor = dtvchan->GetMinorChannel();
01786 if ((minor > 0) && (tuningmode == "atsc"))
01787 {
01788 QString msg = QString("ATSC channel: %1_%2").arg(major).arg(minor);
01789 LOG(VB_RECORD, LOG_INFO, LOC + msg);
01790
01791 ATSCStreamData *asd = dynamic_cast<ATSCStreamData*>(sd);
01792 if (!asd)
01793 {
01794 sd = asd = new ATSCStreamData(major, minor);
01795 sd->SetCaching(true);
01796 if (GetDTVRecorder())
01797 GetDTVRecorder()->SetStreamData(asd);
01798 }
01799
01800 asd->Reset();
01801 sm->SetStreamData(sd);
01802 sm->SetChannel(major, minor);
01803 sd->SetRecordingType(recording_type);
01804
01805
01806
01807 if (!ApplyCachedPids(sm, dtvchan))
01808 sm->AddFlags(SignalMonitor::kDTVSigMon_WaitForMGT);
01809
01810 LOG(VB_RECORD, LOG_INFO, LOC +
01811 "Successfully set up ATSC table monitoring.");
01812 return true;
01813 }
01814
01815
01816 int progNum = dtvchan->GetProgramNumber();
01817 if ((progNum >= 0) && (tuningmode == "dvb"))
01818 {
01819 int netid = dtvchan->GetOriginalNetworkID();
01820 int tsid = dtvchan->GetTransportID();
01821
01822 DVBStreamData *dsd = dynamic_cast<DVBStreamData*>(sd);
01823 if (!dsd)
01824 {
01825 sd = dsd = new DVBStreamData(netid, tsid, progNum);
01826 sd->SetCaching(true);
01827 if (GetDTVRecorder())
01828 GetDTVRecorder()->SetStreamData(dsd);
01829 }
01830
01831 LOG(VB_RECORD, LOG_INFO, LOC +
01832 QString("DVB service_id %1 on net_id %2 tsid %3")
01833 .arg(progNum).arg(netid).arg(tsid));
01834
01835 apply_broken_dvb_driver_crc_hack(channel, sd);
01836
01837 dsd->Reset();
01838 sm->SetStreamData(sd);
01839 sm->SetDVBService(netid, tsid, progNum);
01840 sd->SetRecordingType(recording_type);
01841
01842 sm->AddFlags(SignalMonitor::kDTVSigMon_WaitForPMT |
01843 SignalMonitor::kDTVSigMon_WaitForSDT |
01844 SignalMonitor::kDVBSigMon_WaitForPos);
01845 sm->SetRotorTarget(1.0f);
01846
01847 if (EITscan)
01848 {
01849 sm->GetStreamData()->SetVideoStreamsRequired(0);
01850 sm->IgnoreEncrypted(true);
01851 }
01852
01853 LOG(VB_RECORD, LOG_INFO, LOC +
01854 "Successfully set up DVB table monitoring.");
01855 return true;
01856 }
01857
01858
01859 if (progNum >= 0)
01860 {
01861 if (!sd)
01862 {
01863 sd = new MPEGStreamData(progNum, true);
01864 sd->SetCaching(true);
01865 if (GetDTVRecorder())
01866 GetDTVRecorder()->SetStreamData(sd);
01867 }
01868
01869 QString msg = QString("MPEG program number: %1").arg(progNum);
01870 LOG(VB_RECORD, LOG_INFO, LOC + msg);
01871
01872 apply_broken_dvb_driver_crc_hack(channel, sd);
01873
01874 sd->Reset();
01875 sm->SetStreamData(sd);
01876 sm->SetProgramNumber(progNum);
01877 sd->SetRecordingType(recording_type);
01878
01879 sm->AddFlags(SignalMonitor::kDTVSigMon_WaitForPAT |
01880 SignalMonitor::kDTVSigMon_WaitForPMT |
01881 SignalMonitor::kDVBSigMon_WaitForPos);
01882 sm->SetRotorTarget(1.0f);
01883
01884 if (EITscan)
01885 {
01886 sm->GetStreamData()->SetVideoStreamsRequired(0);
01887 sm->IgnoreEncrypted(true);
01888 }
01889
01890 LOG(VB_RECORD, LOG_INFO, LOC +
01891 "Successfully set up MPEG table monitoring.");
01892 return true;
01893 }
01894
01895
01896
01897 bool ok = false;
01898 if (GetDTVChannel())
01899 {
01900 pid_cache_t pid_cache;
01901 GetDTVChannel()->GetCachedPids(pid_cache);
01902 pid_cache_t::const_iterator it = pid_cache.begin();
01903 for (; !ok && it != pid_cache.end(); ++it)
01904 ok |= it->IsPermanent();
01905 }
01906
01907 if (!ok)
01908 {
01909 QString msg = "No valid DTV info, ATSC maj(%1) min(%2), MPEG pn(%3)";
01910 LOG(VB_GENERAL, LOG_ERR, LOC + msg.arg(major).arg(minor).arg(progNum));
01911 }
01912 else
01913 {
01914 LOG(VB_RECORD, LOG_INFO, LOC +
01915 "Successfully set up raw pid monitoring.");
01916 }
01917
01918 return ok;
01919 }
01920
01933 bool TVRec::SetupSignalMonitor(bool tablemon, bool EITscan, bool notify)
01934 {
01935 LOG(VB_RECORD, LOG_INFO, LOC + QString("SetupSignalMonitor(%1, %2)")
01936 .arg(tablemon).arg(notify));
01937
01938
01939 if (signalMonitor)
01940 return true;
01941
01942
01943 if (!channel)
01944 return false;
01945
01946
01947 if (genOpt.cardtype == "IMPORT" || genOpt.cardtype == "DEMO")
01948 return true;
01949
01950
01951 SignalMonitorValue::Init();
01952
01953 if (SignalMonitor::IsSupported(genOpt.cardtype) && channel->Open())
01954 signalMonitor = SignalMonitor::Init(genOpt.cardtype, cardid, channel);
01955
01956 if (signalMonitor)
01957 {
01958 LOG(VB_RECORD, LOG_INFO, LOC + "Signal monitor successfully created");
01959
01960 if (GetDTVSignalMonitor() && tablemon &&
01961 !SetupDTVSignalMonitor(EITscan))
01962 {
01963 LOG(VB_GENERAL, LOG_ERR, LOC +
01964 "Failed to setup digital signal monitoring");
01965
01966 return false;
01967 }
01968
01969 signalMonitor->AddListener(this);
01970 signalMonitor->SetUpdateRate(signalMonitor->HasExtraSlowTuning() ?
01971 kSignalMonitoringRate * 5 :
01972 kSignalMonitoringRate);
01973 signalMonitor->SetNotifyFrontend(notify);
01974
01975
01976 signalMonitor->Start();
01977 }
01978
01979 return true;
01980 }
01981
01986 void TVRec::TeardownSignalMonitor()
01987 {
01988 if (!signalMonitor)
01989 return;
01990
01991 LOG(VB_RECORD, LOG_INFO, LOC + "TeardownSignalMonitor() -- begin");
01992
01993
01994 DTVSignalMonitor *dtvMon = GetDTVSignalMonitor();
01995 DTVChannel *dtvChan = GetDTVChannel();
01996 if (dtvMon && dtvChan)
01997 {
01998 pid_cache_t pid_cache;
01999 GetPidsToCache(dtvMon, pid_cache);
02000 if (!pid_cache.empty())
02001 dtvChan->SaveCachedPids(pid_cache);
02002 }
02003
02004 if (signalMonitor)
02005 {
02006 delete signalMonitor;
02007 signalMonitor = NULL;
02008 }
02009
02010 LOG(VB_RECORD, LOG_INFO, LOC + "TeardownSignalMonitor() -- end");
02011 }
02012
02024 int TVRec::SetSignalMonitoringRate(int rate, int notifyFrontend)
02025 {
02026 QString msg = "SetSignalMonitoringRate(%1, %2)";
02027 LOG(VB_RECORD, LOG_INFO, LOC +
02028 msg.arg(rate).arg(notifyFrontend) + "-- start");
02029
02030 QMutexLocker lock(&stateChangeLock);
02031
02032 if (!SignalMonitor::IsSupported(genOpt.cardtype))
02033 {
02034 LOG(VB_GENERAL, LOG_ERR, LOC +
02035 "Signal Monitoring is notsupported by your hardware.");
02036 return 0;
02037 }
02038
02039 if (GetState() != kState_WatchingLiveTV)
02040 {
02041 LOG(VB_GENERAL, LOG_ERR, LOC +
02042 "Signal can only be monitored in LiveTV Mode.");
02043 return 0;
02044 }
02045
02046 ClearFlags(kFlagRingBufferReady);
02047
02048 TuningRequest req = (rate > 0) ?
02049 TuningRequest(kFlagAntennaAdjust, channel->GetCurrentName()) :
02050 TuningRequest(kFlagLiveTV);
02051
02052 tuningRequests.enqueue(req);
02053
02054
02055 while (!HasFlags(kFlagRingBufferReady))
02056 WaitForEventThreadSleep();
02057 LOG(VB_RECORD, LOG_INFO, LOC +
02058 msg.arg(rate).arg(notifyFrontend) + " -- end");
02059 return 1;
02060 }
02061
02062 DTVSignalMonitor *TVRec::GetDTVSignalMonitor(void)
02063 {
02064 return dynamic_cast<DTVSignalMonitor*>(signalMonitor);
02065 }
02066
02078 bool TVRec::ShouldSwitchToAnotherCard(QString chanid)
02079 {
02080 QString msg("");
02081 MSqlQuery query(MSqlQuery::InitCon());
02082
02083 if (!query.isConnected())
02084 return false;
02085
02086 query.prepare("SELECT channel.channum, channel.callsign "
02087 "FROM channel "
02088 "WHERE channel.chanid = :CHANID");
02089 query.bindValue(":CHANID", chanid);
02090 if (!query.exec() || !query.next())
02091 {
02092 MythDB::DBError("ShouldSwitchToAnotherCard", query);
02093 return false;
02094 }
02095
02096 QString channelname = query.value(0).toString();
02097 QString callsign = query.value(1).toString();
02098
02099 query.prepare(
02100 "SELECT channel.channum "
02101 "FROM channel,cardinput "
02102 "WHERE ( channel.chanid = :CHANID OR "
02103 " ( channel.channum = :CHANNUM AND "
02104 " channel.callsign = :CALLSIGN ) "
02105 " ) AND "
02106 " channel.sourceid = cardinput.sourceid AND "
02107 " cardinput.cardid = :CARDID");
02108 query.bindValue(":CHANID", chanid);
02109 query.bindValue(":CHANNUM", channelname);
02110 query.bindValue(":CALLSIGN", callsign);
02111 query.bindValue(":CARDID", cardid);
02112
02113 if (!query.exec() || !query.isActive())
02114 {
02115 MythDB::DBError("ShouldSwitchToAnotherCard", query);
02116 }
02117 else if (query.size() > 0)
02118 {
02119 msg = "Found channel (%1) on current card(%2).";
02120 LOG(VB_RECORD, LOG_INFO, LOC + msg.arg(channelname).arg(cardid));
02121 return false;
02122 }
02123
02124
02125 query.prepare(
02126 "SELECT channel.channum, cardinput.cardid "
02127 "FROM channel,cardinput "
02128 "WHERE ( channel.chanid = :CHANID OR "
02129 " ( channel.channum = :CHANNUM AND "
02130 " channel.callsign = :CALLSIGN ) "
02131 " ) AND "
02132 " channel.sourceid = cardinput.sourceid AND "
02133 " cardinput.cardid != :CARDID");
02134 query.bindValue(":CHANID", chanid);
02135 query.bindValue(":CHANNUM", channelname);
02136 query.bindValue(":CALLSIGN", callsign);
02137 query.bindValue(":CARDID", cardid);
02138
02139 if (!query.exec() || !query.isActive())
02140 {
02141 MythDB::DBError("ShouldSwitchToAnotherCard", query);
02142 }
02143 else if (query.next())
02144 {
02145 msg = QString("Found channel (%1) on different card(%2).")
02146 .arg(query.value(0).toString()).arg(query.value(1).toString());
02147 LOG(VB_RECORD, LOG_INFO, LOC + msg);
02148 return true;
02149 }
02150
02151 msg = QString("Did not find channel(%1) on any card.").arg(channelname);
02152 LOG(VB_RECORD, LOG_ERR, LOC + msg);
02153 return false;
02154 }
02155
02166 bool TVRec::CheckChannel(QString name) const
02167 {
02168 if (!channel)
02169 return false;
02170
02171 QString dummyID;
02172 return channel->CheckChannel(name, dummyID);
02173 }
02174
02178 static QString add_spacer(const QString &channel, const QString &spacer)
02179 {
02180 QString chan = channel;
02181 chan.detach();
02182 if ((chan.length() >= 2) && !spacer.isEmpty())
02183 return chan.left(chan.length()-1) + spacer + chan.right(1);
02184 return chan;
02185 }
02186
02214 bool TVRec::CheckChannelPrefix(const QString &prefix,
02215 uint &is_complete_valid_channel_on_rec,
02216 bool &is_extra_char_useful,
02217 QString &needed_spacer)
02218 {
02219 #if DEBUG_CHANNEL_PREFIX
02220 LOG(VB_GENERAL, LOG_DEBUG, QString("CheckChannelPrefix(%1)").arg(prefix));
02221 #endif
02222
02223 static const uint kSpacerListSize = 5;
02224 static const char* spacers[kSpacerListSize] = { "", "_", "-", "#", "." };
02225
02226 MSqlQuery query(MSqlQuery::InitCon());
02227 QString basequery = QString(
02228 "SELECT channel.chanid, channel.channum, cardinput.cardid "
02229 "FROM channel, capturecard, cardinput "
02230 "WHERE channel.channum LIKE '%1%' AND "
02231 " channel.sourceid = cardinput.sourceid AND "
02232 " cardinput.cardid = capturecard.cardid");
02233
02234 QString cardquery[2] =
02235 {
02236 QString(" AND capturecard.cardid = '%1'").arg(cardid),
02237 QString(" AND capturecard.cardid != '%1'").arg(cardid),
02238 };
02239
02240 vector<uint> fchanid;
02241 vector<QString> fchannum;
02242 vector<uint> fcardid;
02243 vector<QString> fspacer;
02244
02245 for (uint i = 0; i < 2; i++)
02246 {
02247 for (uint j = 0; j < kSpacerListSize; j++)
02248 {
02249 QString qprefix = add_spacer(
02250 prefix, (QString(spacers[j]) == "_") ? "\\_" : spacers[j]);
02251 query.prepare(basequery.arg(qprefix) + cardquery[i]);
02252
02253 if (!query.exec() || !query.isActive())
02254 {
02255 MythDB::DBError("checkchannel -- locate channum", query);
02256 }
02257 else if (query.size())
02258 {
02259 while (query.next())
02260 {
02261 fchanid.push_back(query.value(0).toUInt());
02262 fchannum.push_back(query.value(1).toString());
02263 fcardid.push_back(query.value(2).toUInt());
02264 fspacer.push_back(spacers[j]);
02265 #if DEBUG_CHANNEL_PREFIX
02266 LOG(VB_GENERAL, LOG_DEBUG,
02267 QString("(%1,%2) Adding %3 rec %4")
02268 .arg(i).arg(j).arg(query.value(1).toString(),6)
02269 .arg(query.value(2).toUInt()));
02270 #endif
02271 }
02272 }
02273
02274 if (prefix.length() < 2)
02275 break;
02276 }
02277 }
02278
02279
02280 is_extra_char_useful = false;
02281 is_complete_valid_channel_on_rec = 0;
02282 needed_spacer.clear();
02283
02284 if (fchanid.empty())
02285 return false;
02286
02287 if (fchanid.size() == 1)
02288 {
02289 needed_spacer = fspacer[0];
02290 bool nc = (fchannum[0] != add_spacer(prefix, fspacer[0]));
02291
02292 is_complete_valid_channel_on_rec = (nc) ? 0 : fcardid[0];
02293 is_extra_char_useful = nc;
02294 return true;
02295 }
02296
02297
02298
02299
02300
02301 is_extra_char_useful = false;
02302 for (uint i = 0; (i < fchannum.size()) && !is_extra_char_useful; i++)
02303 {
02304 is_extra_char_useful = (fchannum[i] != add_spacer(prefix, fspacer[i]));
02305 #if DEBUG_CHANNEL_PREFIX
02306 LOG(VB_GENERAL, LOG_DEBUG, QString("is_extra_char_useful(%1!=%2): %3")
02307 .arg(fchannum[i]).arg(add_spacer(prefix, fspacer[i]))
02308 .arg(is_extra_char_useful));
02309 #endif
02310 }
02311
02312
02313
02314
02315 for (uint i = 0; i < fchannum.size(); i++)
02316 {
02317 if (fchannum[i] == prefix)
02318 {
02319 is_complete_valid_channel_on_rec = fcardid[i];
02320 if (fcardid[i] == (uint)cardid)
02321 break;
02322 }
02323 }
02324
02325 if (is_complete_valid_channel_on_rec)
02326 return true;
02327
02328
02329 bool spacer_needed = true;
02330 for (uint i = 0; (i < fspacer.size() && spacer_needed); i++)
02331 spacer_needed = !fspacer[i].isEmpty();
02332 if (spacer_needed)
02333 needed_spacer = fspacer[0];
02334
02335
02336
02337 for (uint i = 0; i < ((is_extra_char_useful) ? 0 : fchanid.size()); i++)
02338 {
02339 if (fchannum[i] == add_spacer(prefix, fspacer[i]))
02340 {
02341 needed_spacer = fspacer[i];
02342 is_complete_valid_channel_on_rec = fcardid[i];
02343 return true;
02344 }
02345 }
02346
02347 return true;
02348 }
02349
02350 bool TVRec::SetVideoFiltersForChannel(uint sourceid,
02351 const QString &channum)
02352 {
02353 if (!recorder)
02354 return false;
02355
02356 QString videoFilters = ChannelUtil::GetVideoFilters(sourceid, channum);
02357 if (!videoFilters.isEmpty())
02358 {
02359 recorder->SetVideoFilters(videoFilters);
02360 return true;
02361 }
02362
02363 return false;
02364 }
02365
02370 bool TVRec::IsReallyRecording(void)
02371 {
02372 return ((recorder && recorder->IsRecording()) ||
02373 HasFlags(kFlagDummyRecorderRunning));
02374 }
02375
02381 bool TVRec::IsBusy(TunedInputInfo *busy_input, int time_buffer) const
02382 {
02383 TunedInputInfo dummy;
02384 if (!busy_input)
02385 busy_input = &dummy;
02386
02387 busy_input->Clear();
02388
02389 if (!channel)
02390 return false;
02391
02392 QStringList list = channel->GetConnectedInputs();
02393 if (list.empty())
02394 return false;
02395
02396 uint chanid = 0;
02397
02398 if (GetState() != kState_None)
02399 {
02400 busy_input->inputid = channel->GetCurrentInputNum();
02401 chanid = channel->GetChanID();
02402 }
02403
02404 PendingInfo pendinfo;
02405 bool has_pending;
02406 {
02407 pendingRecLock.lock();
02408 PendingMap::const_iterator it = pendingRecordings.find(cardid);
02409 has_pending = (it != pendingRecordings.end());
02410 if (has_pending)
02411 pendinfo = *it;
02412 pendingRecLock.unlock();
02413 }
02414
02415 if (!busy_input->inputid && has_pending)
02416 {
02417 int timeLeft = QDateTime::currentDateTime()
02418 .secsTo(pendinfo.recordingStart);
02419
02420 if (timeLeft <= time_buffer)
02421 {
02422 QString channum = QString::null, input = QString::null;
02423 if (pendinfo.info->QueryTuningInfo(channum, input))
02424 {
02425 busy_input->inputid = channel->GetInputByName(input);
02426 chanid = pendinfo.info->GetChanID();
02427 }
02428 }
02429 }
02430
02431 if (busy_input->inputid)
02432 {
02433 CardUtil::GetInputInfo(*busy_input);
02434 busy_input->chanid = chanid;
02435 busy_input->mplexid = ChannelUtil::GetMplexID(busy_input->chanid);
02436 busy_input->mplexid =
02437 (32767 == busy_input->mplexid) ? 0 : busy_input->mplexid;
02438 }
02439
02440 return busy_input->inputid;
02441 }
02442
02443
02450 float TVRec::GetFramerate(void)
02451 {
02452 QMutexLocker lock(&stateChangeLock);
02453
02454 if (recorder)
02455 return recorder->GetFrameRate();
02456 return -1.0f;
02457 }
02458
02465 long long TVRec::GetFramesWritten(void)
02466 {
02467 QMutexLocker lock(&stateChangeLock);
02468
02469 if (recorder)
02470 return recorder->GetFramesWritten();
02471 return -1;
02472 }
02473
02480 long long TVRec::GetFilePosition(void)
02481 {
02482 QMutexLocker lock(&stateChangeLock);
02483
02484 if (ringBuffer)
02485 return ringBuffer->GetWritePosition();
02486 return -1;
02487 }
02488
02496 int64_t TVRec::GetKeyframePosition(uint64_t desired) const
02497 {
02498 QMutexLocker lock(&stateChangeLock);
02499
02500 if (recorder)
02501 return recorder->GetKeyframePosition(desired);
02502 return -1;
02503 }
02504
02513 bool TVRec::GetKeyframePositions(
02514 int64_t start, int64_t end, frm_pos_map_t &map) const
02515 {
02516 QMutexLocker lock(&stateChangeLock);
02517
02518 if (recorder)
02519 return recorder->GetKeyframePositions(start, end, map);
02520
02521 return false;
02522 }
02523
02529 long long TVRec::GetMaxBitrate(void) const
02530 {
02531 long long bitrate;
02532 if (genOpt.cardtype == "MPEG")
02533 bitrate = 10080000LL;
02534 if (genOpt.cardtype == "HDPVR")
02535 bitrate = 20200000LL;
02536 else if (!CardUtil::IsEncoder(genOpt.cardtype))
02537 bitrate = 22200000LL;
02538 else
02539 bitrate = 10080000LL;
02540
02541 return bitrate;
02542 }
02543
02549 void TVRec::SpawnLiveTV(LiveTVChain *newchain, bool pip, QString startchan)
02550 {
02551 QMutexLocker lock(&stateChangeLock);
02552
02553 tvchain = newchain;
02554 tvchain->ReloadAll();
02555
02556 QString hostprefix = gCoreContext->GenMythURL(
02557 gCoreContext->GetBackendServerIP(),
02558 gCoreContext->GetSetting("BackendServerPort").toInt());
02559
02560 tvchain->SetHostPrefix(hostprefix);
02561 tvchain->SetCardType(genOpt.cardtype);
02562
02563 ispip = pip;
02564 LiveTVStartChannel = startchan;
02565
02566
02567 ChangeState(kState_WatchingLiveTV);
02568
02569 WaitForEventThreadSleep();
02570
02571
02572 SetFlags(kFlagCancelNextRecording);
02573 }
02574
02578 QString TVRec::GetChainID(void)
02579 {
02580 if (tvchain)
02581 return tvchain->GetID();
02582 return "";
02583 }
02584
02593 void TVRec::CheckForRecGroupChange(void)
02594 {
02595 QMutexLocker lock(&stateChangeLock);
02596
02597 if (internalState == kState_None)
02598 return;
02599
02600 if (!curRecording)
02601 return;
02602
02603 const QString recgrp = curRecording->QueryRecordingGroup();
02604 curRecording->SetRecordingGroup(recgrp);
02605
02606 if (recgrp != "LiveTV" && !pseudoLiveTVRecording)
02607 {
02608
02609 SetPseudoLiveTVRecording(new ProgramInfo(*curRecording));
02610 }
02611 else if (recgrp == "LiveTV" && pseudoLiveTVRecording)
02612 {
02613
02614 SetPseudoLiveTVRecording(NULL);
02615 }
02616 }
02617
02618 static uint get_input_id(uint cardid, const QString &inputname)
02619 {
02620 MSqlQuery query(MSqlQuery::InitCon());
02621
02622 query.prepare(
02623 "SELECT cardinputid "
02624 "FROM cardinput "
02625 "WHERE cardid = :CARDID AND "
02626 " inputname = :INNAME");
02627
02628 query.bindValue(":CARDID", cardid);
02629 query.bindValue(":INNAME", inputname);
02630
02631 if (!query.exec() || !query.isActive())
02632 MythDB::DBError("get_input_id", query);
02633 else if (query.next())
02634 return query.value(0).toUInt();
02635
02636 return 0;
02637 }
02638
02648 void TVRec::NotifySchedulerOfRecording(RecordingInfo *rec)
02649 {
02650 if (!channel)
02651 return;
02652
02653
02654
02655 rec->SetCardID(cardid);
02656 rec->SetInputID(get_input_id(cardid, channel->GetCurrentInput()));
02657 rec->SetRecordingRuleType(rec->GetRecordingRule()->m_type);
02658
02659 if (rec->GetRecordingRuleType() == kNotRecording)
02660 {
02661 rec->SetRecordingRuleType(kSingleRecord);
02662 rec->GetRecordingRule()->m_type = kSingleRecord;
02663 }
02664
02665
02666 rec->GetRecordingRule()->m_endOffset = 0;
02667
02668
02669
02670
02671 rec->SetRecordingStatus(rsInactive);
02672 rec->AddHistory(false);
02673
02674
02675
02676 rec->GetRecordingRule()->Save(false);
02677
02678
02679 rec->ApplyRecordRecID();
02680
02681
02682 rec->SetRecordingStatus(rsRecording);
02683
02684
02685 QStringList prog;
02686 rec->ToStringList(prog);
02687 MythEvent me("SCHEDULER_ADD_RECORDING", prog);
02688 gCoreContext->dispatch(me);
02689
02690
02691
02692 ClearFlags(kFlagCancelNextRecording);
02693 }
02694
02706 void TVRec::SetLiveRecording(int recording)
02707 {
02708 LOG(VB_GENERAL, LOG_INFO, LOC +
02709 QString("SetLiveRecording(%1)").arg(recording));
02710 QMutexLocker locker(&stateChangeLock);
02711
02712 (void) recording;
02713
02714 RecStatusType recstat = rsCancelled;
02715 bool was_rec = pseudoLiveTVRecording;
02716 CheckForRecGroupChange();
02717 if (was_rec && !pseudoLiveTVRecording)
02718 {
02719 LOG(VB_GENERAL, LOG_INFO, LOC + "SetLiveRecording() -- cancel");
02720
02721 SetFlags(kFlagCancelNextRecording);
02722 curRecording->SetRecordingGroup("LiveTV");
02723 autoRunJobs = JOB_NONE;
02724 }
02725 else if (!was_rec && pseudoLiveTVRecording)
02726 {
02727 LOG(VB_GENERAL, LOG_INFO, LOC + "SetLiveRecording() -- record");
02728
02729
02730
02731
02732
02733 recordEndTime = GetRecordEndTime(pseudoLiveTVRecording);
02734 NotifySchedulerOfRecording(curRecording);
02735 recstat = curRecording->GetRecordingStatus();
02736 curRecording->SetRecordingGroup("Default");
02737
02738 RecordingProfile profile;
02739 load_profile(genOpt.cardtype, NULL, curRecording, profile);
02740 autoRunJobs = init_jobs(curRecording, profile, runJobOnHostOnly,
02741 transcodeFirst, earlyCommFlag);
02742 }
02743
02744 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
02745 .arg(curRecording->GetCardID())
02746 .arg(curRecording->GetChanID())
02747 .arg(curRecording->GetScheduledStartTime(ISODate))
02748 .arg(recstat)
02749 .arg(curRecording->GetRecordingEndTime(ISODate)));
02750
02751 gCoreContext->dispatch(me);
02752 }
02753
02758 void TVRec::StopLiveTV(void)
02759 {
02760 QMutexLocker lock(&stateChangeLock);
02761 LOG(VB_RECORD, LOG_INFO, LOC +
02762 QString("StopLiveTV(void) curRec: 0x%1 pseudoRec: 0x%2")
02763 .arg((uint64_t)curRecording,0,16)
02764 .arg((uint64_t)pseudoLiveTVRecording,0,16));
02765
02766 if (internalState != kState_WatchingLiveTV)
02767 return;
02768
02769 bool hadPseudoLiveTVRec = pseudoLiveTVRecording;
02770 CheckForRecGroupChange();
02771
02772 if (!hadPseudoLiveTVRec && pseudoLiveTVRecording)
02773 NotifySchedulerOfRecording(curRecording);
02774
02775
02776 TVState next_state = kState_None;
02777 if (pseudoLiveTVRecording)
02778 {
02779 recordEndTime = GetRecordEndTime(pseudoLiveTVRecording);
02780 next_state = kState_RecordingOnly;
02781 }
02782
02783
02784 ChangeState(next_state);
02785
02786
02787 WaitForEventThreadSleep();
02788
02789
02790 tvchain = NULL;
02791 }
02792
02801 void TVRec::PauseRecorder(void)
02802 {
02803 QMutexLocker lock(&stateChangeLock);
02804
02805 if (!recorder)
02806 {
02807 LOG(VB_GENERAL, LOG_ERR, LOC +
02808 "PauseRecorder() called with no recorder");
02809 return;
02810 }
02811
02812 recorder->Pause();
02813 }
02814
02820 void TVRec::RecorderPaused(void)
02821 {
02822 if (pauseNotify)
02823 WakeEventLoop();
02824 }
02825
02829 void TVRec::ToggleChannelFavorite(QString changroupname)
02830 {
02831 QMutexLocker lock(&stateChangeLock);
02832
02833 if (!channel)
02834 return;
02835
02836
02837 uint sourceid = channel->GetCurrentSourceID();
02838 QString channum = channel->GetCurrentName();
02839 uint chanid = ChannelUtil::GetChanID(sourceid, channum);
02840
02841 if (!chanid)
02842 {
02843 LOG(VB_GENERAL, LOG_ERR, LOC +
02844 QString("Channel: \'%1\' was not found in the database.\n"
02845 "\t\tMost likely, your DefaultTVChannel setting is wrong.\n"
02846 "\t\tCould not toggle favorite.").arg(channum));
02847 return;
02848 }
02849
02850 int changrpid;
02851 bool result;
02852
02853 changrpid = ChannelGroup::GetChannelGroupId(changroupname);
02854
02855 if (changrpid <1)
02856 {
02857 LOG(VB_RECORD, LOG_ERR, LOC +
02858 QString("ToggleChannelFavorite: Invalid channel group name %1,")
02859 .arg(changroupname));
02860 }
02861 else
02862 {
02863 result = ChannelGroup::ToggleChannel(chanid, changrpid, true);
02864
02865 if (!result)
02866 LOG(VB_RECORD, LOG_ERR, LOC + "Unable to toggle channel favorite.");
02867 else
02868 LOG(VB_RECORD, LOG_INFO, LOC +
02869 QString("Toggled channel favorite.channum %1, chan group %2")
02870 .arg(channum).arg(changroupname));
02871 }
02872 }
02873
02879 int TVRec::GetPictureAttribute(PictureAttribute attr)
02880 {
02881 QMutexLocker lock(&stateChangeLock);
02882 if (!channel)
02883 return -1;
02884
02885 int ret = channel->GetPictureAttribute(attr);
02886
02887 return (ret < 0) ? -1 : ret / 655;
02888 }
02889
02897 int TVRec::ChangePictureAttribute(PictureAdjustType type,
02898 PictureAttribute attr,
02899 bool direction)
02900 {
02901 QMutexLocker lock(&stateChangeLock);
02902 if (!channel)
02903 return -1;
02904
02905 int ret = channel->ChangePictureAttribute(type, attr, direction);
02906
02907 return (ret < 0) ? -1 : ret / 655;
02908 }
02909
02918 vector<InputInfo> TVRec::GetFreeInputs(
02919 const vector<uint> &excluded_cardids) const
02920 {
02921 vector<InputInfo> list;
02922 if (channel)
02923 list = channel->GetFreeInputs(excluded_cardids);
02924 return list;
02925 }
02926
02930 QString TVRec::GetInput(void) const
02931 {
02932 if (channel)
02933 return channel->GetCurrentInput();
02934 return QString::null;
02935 }
02936
02945 QString TVRec::SetInput(QString input, uint requestType)
02946 {
02947 QMutexLocker lock(&stateChangeLock);
02948 QString origIn = input;
02949 LOG(VB_RECORD, LOG_INFO, LOC + "SetInput(" + input + ") -- begin");
02950
02951 if (!channel)
02952 {
02953 LOG(VB_RECORD, LOG_INFO, LOC + "SetInput() -- end no channel class");
02954 return QString::null;
02955 }
02956
02957 input = (input == "SwitchToNextInput") ? channel->GetNextInput() : input;
02958
02959 if (input == channel->GetCurrentInput())
02960 {
02961 LOG(VB_RECORD, LOG_INFO, LOC + "SetInput(" + origIn + ":" + input +
02962 ") -- end nothing to do");
02963 return input;
02964 }
02965
02966 QString name = channel->GetNextInputStartChan();
02967
02968
02969 if (requestType & kFlagDetect)
02970 {
02971 WaitForEventThreadSleep();
02972 requestType = lastTuningRequest.flags & (kFlagRec | kFlagNoRec);
02973 }
02974
02975
02976 ClearFlags(kFlagRingBufferReady);
02977
02978
02979
02980 tuningRequests.enqueue(TuningRequest(requestType, name, input));
02981 WaitForEventThreadSleep();
02982
02983
02984 if (requestType & kFlagRec)
02985 {
02986 while (!HasFlags(kFlagRingBufferReady))
02987 WaitForEventThreadSleep();
02988 }
02989 LOG(VB_RECORD, LOG_INFO, LOC +
02990 "SetInput(" + origIn + ":" + input + ") -- end");
02991
02992 return GetInput();
02993 }
02994
03004 void TVRec::SetChannel(QString name, uint requestType)
03005 {
03006 if (TVRec::kFlagEITScan == requestType)
03007 {
03008 if (!stateChangeLock.tryLock())
03009 {
03010 LOG(VB_CHANNEL, LOG_INFO, LOC +
03011 QString("SetChannel(%1, kFlagEITScan) -- "
03012 "couldn't get lock aborting").arg(name));
03013 return;
03014 }
03015 }
03016 else
03017 {
03018 stateChangeLock.lock();
03019 }
03020
03021 LOG(VB_CHANNEL, LOG_INFO, LOC +
03022 QString("SetChannel(%1) -- begin").arg(name));
03023
03024
03025 if (requestType & kFlagDetect)
03026 {
03027 WaitForEventThreadSleep();
03028 requestType = lastTuningRequest.flags & (kFlagRec | kFlagNoRec);
03029 }
03030
03031
03032 ClearFlags(kFlagRingBufferReady);
03033
03034
03035
03036 tuningRequests.enqueue(TuningRequest(requestType, name));
03037 WaitForEventThreadSleep();
03038
03039
03040 if (requestType & kFlagRec)
03041 {
03042 while (!HasFlags(kFlagRingBufferReady))
03043 WaitForEventThreadSleep();
03044 }
03045 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("SetChannel(%1) -- end").arg(name));
03046 stateChangeLock.unlock();
03047 }
03048
03049 void TVRec::GetNextProgram(BrowseDirection direction,
03050 QString &title, QString &subtitle,
03051 QString &desc, QString &category,
03052 QString &starttime, QString &endtime,
03053 QString &callsign, QString &iconpath,
03054 QString &channum, uint &sourceChanid,
03055 QString &seriesid, QString &programid)
03056 {
03057 QString compare = "<=";
03058 QString sortorder = "desc";
03059 uint chanid = 0;
03060
03061 if (sourceChanid)
03062 {
03063 chanid = sourceChanid;
03064
03065 if (BROWSE_UP == direction)
03066 chanid = channel->GetNextChannel(chanid, CHANNEL_DIRECTION_UP);
03067 else if (BROWSE_DOWN == direction)
03068 chanid = channel->GetNextChannel(chanid, CHANNEL_DIRECTION_DOWN);
03069 else if (BROWSE_FAVORITE == direction)
03070 chanid = channel->GetNextChannel(
03071 chanid, CHANNEL_DIRECTION_FAVORITE);
03072
03073 else if (BROWSE_LEFT == direction)
03074 {
03075 compare = "<";
03076 }
03077 else if (BROWSE_RIGHT == direction)
03078 {
03079 compare = ">";
03080 sortorder = "asc";
03081 }
03082 }
03083
03084 if (!chanid)
03085 {
03086 if (BROWSE_SAME == direction)
03087 chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_SAME);
03088 else if (BROWSE_UP == direction)
03089 chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_UP);
03090 else if (BROWSE_DOWN == direction)
03091 chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_DOWN);
03092 else if (BROWSE_FAVORITE == direction)
03093 chanid = channel->GetNextChannel(channum,
03094 CHANNEL_DIRECTION_FAVORITE);
03095 else if (BROWSE_LEFT == direction)
03096 {
03097 chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_SAME);
03098 compare = "<";
03099 }
03100 else if (BROWSE_RIGHT == direction)
03101 {
03102 chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_SAME);
03103 compare = ">";
03104 sortorder = "asc";
03105 }
03106 }
03107
03108 QString querystr = QString(
03109 "SELECT title, subtitle, description, category, "
03110 " starttime, endtime, callsign, icon, "
03111 " channum, seriesid, programid "
03112 "FROM program, channel "
03113 "WHERE program.chanid = channel.chanid AND "
03114 " channel.chanid = :CHANID AND "
03115 " starttime %1 :STARTTIME "
03116 "ORDER BY starttime %2 "
03117 "LIMIT 1").arg(compare).arg(sortorder);
03118
03119 MSqlQuery query(MSqlQuery::InitCon());
03120 query.prepare(querystr);
03121 query.bindValue(":CHANID", chanid);
03122 query.bindValue(":STARTTIME", starttime);
03123
03124
03125 title = subtitle = desc = category = "";
03126 starttime = endtime = callsign = iconpath = "";
03127 channum = seriesid = programid = "";
03128 sourceChanid = 0;
03129
03130
03131 if (!query.exec() && !query.isActive())
03132 {
03133 MythDB::DBError("GetNextProgram -- get program info", query);
03134 }
03135 else if (query.next())
03136 {
03137 title = query.value(0).toString();
03138 subtitle = query.value(1).toString();
03139 desc = query.value(2).toString();
03140 category = query.value(3).toString();
03141 starttime = query.value(4).toString();
03142 endtime = query.value(5).toString();
03143 callsign = query.value(6).toString();
03144 iconpath = query.value(7).toString();
03145 channum = query.value(8).toString();
03146 seriesid = query.value(9).toString();
03147 programid = query.value(10).toString();
03148 sourceChanid = chanid;
03149 return;
03150 }
03151
03152
03153 query.prepare(
03154 "SELECT channum, callsign, icon "
03155 "FROM channel "
03156 "WHERE chanid = :CHANID");
03157 query.bindValue(":CHANID", chanid);
03158
03159 if (!query.exec() || !query.isActive())
03160 {
03161 MythDB::DBError("GetNextProgram -- get channel info", query);
03162 }
03163 else if (query.next())
03164 {
03165 sourceChanid = chanid;
03166 channum = query.value(0).toString();
03167 callsign = query.value(1).toString();
03168 iconpath = query.value(2).toString();
03169 }
03170 }
03171
03172 bool TVRec::GetChannelInfo(uint &chanid, uint &sourceid,
03173 QString &callsign, QString &channum,
03174 QString &channame, QString &xmltvid) const
03175 {
03176 callsign.clear();
03177 channum.clear();
03178 channame.clear();
03179 xmltvid.clear();
03180
03181 if ((!chanid || !sourceid) && !channel)
03182 return false;
03183
03184 if (!chanid)
03185 chanid = (uint) max(channel->GetChanID(), 0);
03186
03187 if (!sourceid)
03188 sourceid = channel->GetCurrentSourceID();
03189
03190 MSqlQuery query(MSqlQuery::InitCon());
03191 query.prepare(
03192 "SELECT callsign, channum, name, xmltvid "
03193 "FROM channel "
03194 "WHERE chanid = :CHANID");
03195 query.bindValue(":CHANID", chanid);
03196 if (!query.exec() || !query.isActive())
03197 {
03198 MythDB::DBError("GetChannelInfo", query);
03199 return false;
03200 }
03201
03202 if (!query.next())
03203 return false;
03204
03205 callsign = query.value(0).toString();
03206 channum = query.value(1).toString();
03207 channame = query.value(2).toString();
03208 xmltvid = query.value(3).toString();
03209
03210 return true;
03211 }
03212
03213 bool TVRec::SetChannelInfo(uint chanid, uint sourceid,
03214 QString oldchannum,
03215 QString callsign, QString channum,
03216 QString channame, QString xmltvid)
03217 {
03218 if (!chanid || !sourceid || channum.isEmpty())
03219 return false;
03220
03221 MSqlQuery query(MSqlQuery::InitCon());
03222 query.prepare(
03223 "UPDATE channel "
03224 "SET callsign = :CALLSIGN, "
03225 " channum = :CHANNUM, "
03226 " name = :CHANNAME, "
03227 " xmltvid = :XMLTVID "
03228 "WHERE chanid = :CHANID AND "
03229 " sourceid = :SOURCEID");
03230 query.bindValue(":CALLSIGN", callsign);
03231 query.bindValue(":CHANNUM", channum);
03232 query.bindValue(":CHANNAME", channame);
03233 query.bindValue(":XMLTVID", xmltvid);
03234 query.bindValue(":CHANID", chanid);
03235 query.bindValue(":SOURCEID", sourceid);
03236
03237 if (!query.exec())
03238 {
03239 MythDB::DBError("SetChannelInfo", query);
03240 return false;
03241 }
03242
03243 if (channel)
03244 channel->Renumber(sourceid, oldchannum, channum);
03245
03246 return true;
03247 }
03248
03252 void TVRec::SetRingBuffer(RingBuffer *rb)
03253 {
03254 QMutexLocker lock(&stateChangeLock);
03255
03256 RingBuffer *rb_old = ringBuffer;
03257 ringBuffer = rb;
03258
03259 if (rb_old && (rb_old != rb))
03260 {
03261 if (HasFlags(kFlagDummyRecorderRunning))
03262 ClearFlags(kFlagDummyRecorderRunning);
03263 delete rb_old;
03264 }
03265
03266 switchingBuffer = false;
03267 }
03268
03269 void TVRec::RingBufferChanged(
03270 RingBuffer *rb, ProgramInfo *pginfo, RecordingQuality *recq)
03271 {
03272 LOG(VB_GENERAL, LOG_INFO, LOC + "RingBufferChanged()");
03273
03274 if (pginfo)
03275 {
03276 if (curRecording)
03277 {
03278 FinishedRecording(curRecording, recq);
03279 curRecording->MarkAsInUse(false, kRecorderInUseID);
03280 delete curRecording;
03281 }
03282 recordEndTime = GetRecordEndTime(pginfo);
03283 curRecording = new RecordingInfo(*pginfo);
03284 curRecording->MarkAsInUse(true, kRecorderInUseID);
03285 curRecording->SetRecordingStatus(rsRecording);
03286 }
03287
03288 SetRingBuffer(rb);
03289 }
03290
03291 QString TVRec::TuningGetChanNum(const TuningRequest &request,
03292 QString &input) const
03293 {
03294 QString channum = QString::null;
03295
03296 if (request.program)
03297 {
03298 request.program->QueryTuningInfo(channum, input);
03299 return channum;
03300 }
03301
03302 channum = request.channel;
03303 input = request.input;
03304
03305
03306 if (channum.isEmpty() && (request.flags & kFlagLiveTV))
03307 {
03308 if (!LiveTVStartChannel.isEmpty())
03309 channum = LiveTVStartChannel;
03310 else
03311 {
03312 input = CardUtil::GetStartInput(cardid);
03313 channum = GetStartChannel(cardid, input);
03314 }
03315 }
03316 if (request.flags & kFlagLiveTV)
03317 channel->Init(input, channum, false);
03318
03319 if (channel && !channum.isEmpty() && (channum.indexOf("NextChannel") >= 0))
03320 {
03321 int dir = channum.right(channum.length() - 12).toInt();
03322 uint chanid = channel->GetNextChannel(0, dir);
03323 channum = ChannelUtil::GetChanNum(chanid);
03324 }
03325
03326 return channum;
03327 }
03328
03329 bool TVRec::TuningOnSameMultiplex(TuningRequest &request)
03330 {
03331 if ((request.flags & kFlagAntennaAdjust) || request.input.isEmpty() ||
03332 !GetDTVRecorder() || signalMonitor || !channel || !channel->IsOpen())
03333 {
03334 return false;
03335 }
03336
03337 uint sourceid = channel->GetCurrentSourceID();
03338 QString oldchannum = channel->GetCurrentName();
03339 QString newchannum = request.channel;
03340
03341 if (ChannelUtil::IsOnSameMultiplex(sourceid, newchannum, oldchannum))
03342 {
03343 MPEGStreamData *mpeg = GetDTVRecorder()->GetStreamData();
03344 ATSCStreamData *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
03345
03346 if (atsc)
03347 {
03348 uint major, minor = 0;
03349 ChannelUtil::GetATSCChannel(sourceid, newchannum, major, minor);
03350
03351 if (minor && atsc->HasChannel(major, minor))
03352 {
03353 request.majorChan = major;
03354 request.minorChan = minor;
03355 return true;
03356 }
03357 }
03358
03359 if (mpeg)
03360 {
03361 uint progNum = ChannelUtil::GetProgramNumber(sourceid, newchannum);
03362 if (mpeg->HasProgram(progNum))
03363 {
03364 request.progNum = progNum;
03365 return true;
03366 }
03367 }
03368 }
03369
03370 return false;
03371 }
03372
03380 void TVRec::HandleTuning(void)
03381 {
03382 if (tuningRequests.size())
03383 {
03384 TuningRequest request = tuningRequests.front();
03385 LOG(VB_RECORD, LOG_INFO, LOC +
03386 "HandleTuning Request: " + request.toString());
03387
03388 QString input;
03389 request.channel = TuningGetChanNum(request, input);
03390 request.input = input;
03391
03392 if (TuningOnSameMultiplex(request))
03393 LOG(VB_PLAYBACK, LOG_INFO, LOC + "On same multiplex");
03394
03395 TuningShutdowns(request);
03396
03397
03398
03399 tuningRequests.dequeue();
03400
03401
03402 if (request.flags & (kFlagRecording|kFlagLiveTV|
03403 kFlagEITScan|kFlagAntennaAdjust))
03404 {
03405 if (!recorder)
03406 {
03407 LOG(VB_RECORD, LOG_INFO, LOC +
03408 "No recorder yet, calling TuningFrequency");
03409 TuningFrequency(request);
03410 }
03411 else
03412 {
03413 LOG(VB_RECORD, LOG_INFO, LOC + "Waiting for recorder pause..");
03414 SetFlags(kFlagWaitingForRecPause);
03415 }
03416 }
03417 lastTuningRequest = request;
03418 }
03419
03420 if (HasFlags(kFlagWaitingForRecPause))
03421 {
03422 if (!recorder->IsPaused())
03423 return;
03424
03425 ClearFlags(kFlagWaitingForRecPause);
03426 LOG(VB_RECORD, LOG_INFO, LOC +
03427 "Recorder paused, calling TuningFrequency");
03428 TuningFrequency(lastTuningRequest);
03429 }
03430
03431 MPEGStreamData *streamData = NULL;
03432 if (HasFlags(kFlagWaitingForSignal) && !(streamData = TuningSignalCheck()))
03433 return;
03434
03435 if (HasFlags(kFlagNeedToStartRecorder))
03436 {
03437 if (recorder)
03438 TuningRestartRecorder();
03439 else
03440 TuningNewRecorder(streamData);
03441
03442
03443 if (channel)
03444 channel->StoreInputChannels();
03445 }
03446 }
03447
03451 uint TVRec::TuningCheckForHWChange(const TuningRequest &request,
03452 QString &channum,
03453 QString &inputname)
03454 {
03455 if (!channel)
03456 return 0;
03457
03458 uint curCardID = 0, newCardID = 0;
03459 channum = request.channel;
03460 inputname = request.input;
03461
03462 if (request.program)
03463 request.program->QueryTuningInfo(channum, inputname);
03464
03465 if (!channum.isEmpty() && inputname.isEmpty())
03466 channel->CheckChannel(channum, inputname);
03467
03468 if (!inputname.isEmpty())
03469 {
03470 int current_input = channel->GetCurrentInputNum();
03471 int new_input = channel->GetInputByName(inputname);
03472 curCardID = channel->GetInputCardID(current_input);
03473 newCardID = channel->GetInputCardID(new_input);
03474 LOG(VB_GENERAL, LOG_INFO, LOC + QString("HW Tuner: %1->%2")
03475 .arg(curCardID).arg(newCardID));
03476 }
03477
03478 if (curCardID != newCardID)
03479 {
03480 if (channum.isEmpty())
03481 channum = GetStartChannel(newCardID, inputname);
03482 return newCardID;
03483 }
03484
03485 return 0;
03486 }
03487
03492 void TVRec::TuningShutdowns(const TuningRequest &request)
03493 {
03494 QString channum, inputname;
03495 uint newCardID = TuningCheckForHWChange(request, channum, inputname);
03496
03497 if (scanner && !(request.flags & kFlagEITScan) &&
03498 HasFlags(kFlagEITScannerRunning))
03499 {
03500 scanner->StopActiveScan();
03501 ClearFlags(kFlagEITScannerRunning);
03502 }
03503
03504 if (scanner && !request.IsOnSameMultiplex())
03505 scanner->StopPassiveScan();
03506
03507 if (HasFlags(kFlagSignalMonitorRunning))
03508 {
03509 MPEGStreamData *sd = NULL;
03510 if (GetDTVSignalMonitor())
03511 sd = GetDTVSignalMonitor()->GetStreamData();
03512 TeardownSignalMonitor();
03513 ClearFlags(kFlagSignalMonitorRunning);
03514
03515
03516 MPEGStreamData *rec_sd = NULL;
03517 if (GetDTVRecorder())
03518 rec_sd = GetDTVRecorder()->GetStreamData();
03519 if (sd && (sd != rec_sd))
03520 delete sd;
03521 }
03522 if (HasFlags(kFlagWaitingForSignal))
03523 ClearFlags(kFlagWaitingForSignal);
03524
03525
03526
03527 if (newCardID || (request.flags & kFlagNoRec))
03528 {
03529 if (HasFlags(kFlagDummyRecorderRunning))
03530 {
03531 FinishedRecording(curRecording, NULL);
03532 ClearFlags(kFlagDummyRecorderRunning);
03533 curRecording->MarkAsInUse(false, kRecorderInUseID);
03534 }
03535
03536 if (HasFlags(kFlagRecorderRunning) ||
03537 (curRecording && curRecording->GetRecordingStatus() == rsFailed))
03538 {
03539 stateChangeLock.unlock();
03540 TeardownRecorder(request.flags);
03541 stateChangeLock.lock();
03542 ClearFlags(kFlagRecorderRunning);
03543 }
03544
03545
03546 CloseChannel();
03547
03548 }
03549
03550
03551 if (newCardID)
03552 {
03553 LOG(VB_GENERAL, LOG_INFO, "Recreating channel...");
03554 channel->Close();
03555 delete channel;
03556 channel = NULL;
03557
03558 GetDevices(newCardID, genOpt, dvbOpt, fwOpt);
03559 CreateChannel(channum, false);
03560 }
03561
03562 if (ringBuffer && (request.flags & kFlagKillRingBuffer))
03563 {
03564 LOG(VB_RECORD, LOG_INFO, LOC + "Tearing down RingBuffer");
03565 SetRingBuffer(NULL);
03566
03567 }
03568
03569
03570 ClearFlags(kFlagPendingActions);
03571 }
03572
03590 void TVRec::TuningFrequency(const TuningRequest &request)
03591 {
03592 DTVChannel *dtvchan = GetDTVChannel();
03593 if (dtvchan)
03594 {
03595 MPEGStreamData *mpeg = NULL;
03596
03597 if (GetDTVRecorder())
03598 mpeg = GetDTVRecorder()->GetStreamData();
03599
03600 const QString tuningmode = (HasFlags(kFlagEITScannerRunning)) ?
03601 dtvchan->GetSIStandard() :
03602 dtvchan->GetSuggestedTuningMode(
03603 kState_WatchingLiveTV == internalState);
03604
03605 dtvchan->SetTuningMode(tuningmode);
03606
03607 if (request.minorChan && (tuningmode == "atsc"))
03608 {
03609 channel->SetChannelByString(request.channel);
03610
03611 ATSCStreamData *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
03612 if (atsc)
03613 atsc->SetDesiredChannel(request.majorChan, request.minorChan);
03614 }
03615 else if (request.progNum >= 0)
03616 {
03617 channel->SetChannelByString(request.channel);
03618
03619 if (mpeg)
03620 mpeg->SetDesiredProgram(request.progNum);
03621 }
03622 }
03623
03624 if (request.IsOnSameMultiplex())
03625 {
03626 QStringList slist;
03627 slist<<"message"<<QObject::tr("On known multiplex...");
03628 MythEvent me(QString("SIGNAL %1").arg(cardid), slist);
03629 gCoreContext->dispatch(me);
03630
03631 SetFlags(kFlagNeedToStartRecorder);
03632 return;
03633 }
03634
03635 QString input = request.input;
03636 QString channum = request.channel;
03637
03638 bool ok = false;
03639 if (channel)
03640 channel->Open();
03641 else
03642 ok = true;
03643
03644 if (channel && !channum.isEmpty())
03645 {
03646 if (!input.isEmpty())
03647 ok = channel->SwitchToInput(input, channum);
03648 else
03649 ok = channel->SetChannelByString(channum);
03650 }
03651
03652 if (!ok)
03653 {
03654 if (!(request.flags & kFlagLiveTV) || !(request.flags & kFlagEITScan))
03655 {
03656 if (curRecording)
03657 curRecording->SetRecordingStatus(rsFailed);
03658
03659 LOG(VB_GENERAL, LOG_ERR, LOC +
03660 QString("Failed to set channel to %1. Reverting to kState_None")
03661 .arg(channum));
03662 if (kState_None != internalState)
03663 ChangeState(kState_None);
03664 else
03665 tuningRequests.enqueue(TuningRequest(kFlagKillRec));
03666 return;
03667 }
03668 else
03669 {
03670 LOG(VB_GENERAL, LOG_ERR, LOC +
03671 QString("Failed to set channel to %1.").arg(channum));
03672 }
03673 }
03674
03675 bool livetv = request.flags & kFlagLiveTV;
03676 bool antadj = request.flags & kFlagAntennaAdjust;
03677 bool use_sm = SignalMonitor::IsRequired(genOpt.cardtype);
03678 bool use_dr = use_sm && (livetv || antadj);
03679 bool has_dummy = false;
03680
03681 if (use_dr)
03682 {
03683
03684 bool ok;
03685 ProgramInfo *tmp = pseudoLiveTVRecording;
03686 pseudoLiveTVRecording = NULL;
03687
03688 tvchain->SetCardType("DUMMY");
03689
03690 if (!ringBuffer)
03691 ok = CreateLiveTVRingBuffer(channum);
03692 else
03693 ok = SwitchLiveTVRingBuffer(channum, true, false);
03694 pseudoLiveTVRecording = tmp;
03695
03696 tvchain->SetCardType(genOpt.cardtype);
03697
03698 if (!ok)
03699 {
03700 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create RingBuffer 1");
03701 return;
03702 }
03703
03704 has_dummy = true;
03705 }
03706
03707
03708 if (use_sm)
03709 {
03710 LOG(VB_RECORD, LOG_INFO, LOC + "Starting Signal Monitor");
03711 bool error = false;
03712 if (!SetupSignalMonitor(
03713 !antadj, request.flags & kFlagEITScan, livetv | antadj))
03714 {
03715 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to setup signal monitor");
03716 if (signalMonitor)
03717 {
03718 delete signalMonitor;
03719 signalMonitor = NULL;
03720 }
03721
03722
03723 SetFlags(kFlagSignalMonitorRunning);
03724 ClearFlags(kFlagWaitingForSignal);
03725 error = true;
03726 }
03727
03728 if (signalMonitor)
03729 {
03730 if (request.flags & kFlagEITScan)
03731 {
03732 GetDTVSignalMonitor()->GetStreamData()->
03733 SetVideoStreamsRequired(0);
03734 GetDTVSignalMonitor()->IgnoreEncrypted(true);
03735 }
03736
03737 SetFlags(kFlagSignalMonitorRunning);
03738 ClearFlags(kFlagWaitingForSignal);
03739 if (!antadj)
03740 SetFlags(kFlagWaitingForSignal);
03741 }
03742
03743 if (has_dummy && ringBuffer)
03744 {
03745
03746
03747
03748 if (recorder)
03749 recorder->SetRingBuffer(NULL);
03750
03751 SetFlags(kFlagDummyRecorderRunning);
03752 LOG(VB_RECORD, LOG_INFO, "DummyDTVRecorder -- started");
03753 SetFlags(kFlagRingBufferReady);
03754 }
03755
03756
03757
03758 if (error)
03759 return;
03760 }
03761
03762
03763 ClearFlags(kFlagNeedToStartRecorder);
03764 if (request.flags & kFlagRec && !antadj)
03765 SetFlags(kFlagNeedToStartRecorder);
03766 }
03767
03775 MPEGStreamData *TVRec::TuningSignalCheck(void)
03776 {
03777 RecStatusType newRecStatus = rsRecording;
03778 if (signalMonitor->IsAllGood())
03779 {
03780 LOG(VB_RECORD, LOG_INFO, LOC + "Got good signal");
03781 }
03782 else if (signalMonitor->IsErrored())
03783 {
03784 LOG(VB_RECORD, LOG_ERR, LOC + "SignalMonitor failed");
03785 ClearFlags(kFlagNeedToStartRecorder);
03786
03787 newRecStatus = rsFailed;
03788
03789 if (scanner && HasFlags(kFlagEITScannerRunning))
03790 {
03791 scanner->StopActiveScan();
03792 ClearFlags(kFlagEITScannerRunning);
03793 }
03794 }
03795 else
03796 {
03797 return NULL;
03798 }
03799
03800 SetRecordingStatus(newRecStatus, __LINE__);
03801
03802 if (curRecording)
03803 {
03804 curRecording->SetRecordingStatus(newRecStatus);
03805 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
03806 .arg(curRecording->GetCardID())
03807 .arg(curRecording->GetChanID())
03808 .arg(curRecording->GetScheduledStartTime(ISODate))
03809 .arg(newRecStatus)
03810 .arg(curRecording->GetRecordingEndTime(ISODate)));
03811 gCoreContext->dispatch(me);
03812 }
03813
03814
03815 MPEGStreamData *streamData = NULL;
03816 if (GetDTVSignalMonitor())
03817 streamData = GetDTVSignalMonitor()->GetStreamData();
03818
03819 if (!HasFlags(kFlagEITScannerRunning))
03820 {
03821
03822 TeardownSignalMonitor();
03823 ClearFlags(kFlagSignalMonitorRunning);
03824 }
03825 ClearFlags(kFlagWaitingForSignal);
03826
03827 if (streamData)
03828 {
03829 DVBStreamData *dsd = dynamic_cast<DVBStreamData*>(streamData);
03830 if (dsd)
03831 dsd->SetDishNetEIT(is_dishnet_eit(cardid));
03832 if (!get_use_eit(GetCaptureCardNum()))
03833 {
03834 LOG(VB_EIT, LOG_INFO, LOC +
03835 "EIT scanning disabled for all sources on this card.");
03836 }
03837 else if (scanner)
03838 scanner->StartPassiveScan(channel, streamData);
03839 }
03840
03841 return streamData;
03842 }
03843
03844 static int init_jobs(const RecordingInfo *rec, RecordingProfile &profile,
03845 bool on_host, bool transcode_bfr_comm, bool on_line_comm)
03846 {
03847 if (!rec)
03848 return 0;
03849
03850 int jobs = 0;
03851
03852
03853 JobQueue::AddJobsToMask(rec->GetAutoRunJobs(), jobs);
03854
03855
03856 if (rec->IsCommercialFree())
03857 JobQueue::RemoveJobsFromMask(JOB_COMMFLAG, jobs);
03858
03859
03860 const Setting *autoTrans = profile.byName("autotranscode");
03861 if ((!autoTrans) || (autoTrans->getValue().toInt() == 0))
03862 JobQueue::RemoveJobsFromMask(JOB_TRANSCODE, jobs);
03863
03864 bool ml = JobQueue::JobIsInMask(JOB_METADATA, jobs);
03865 if (ml)
03866 {
03867
03868
03869
03870 QString host = (on_host) ? gCoreContext->GetHostName() : "";
03871 JobQueue::QueueJob(JOB_METADATA,
03872 rec->GetChanID(),
03873 rec->GetRecordingStartTime(), "", "",
03874 host, JOB_LIVE_REC);
03875
03876
03877 JobQueue::RemoveJobsFromMask(JOB_METADATA, jobs);
03878 }
03879
03880
03881 bool rt = JobQueue::JobIsInMask(JOB_COMMFLAG, jobs) && on_line_comm;
03882
03883
03884 rt &= JobQueue::JobIsNotInMask(JOB_TRANSCODE, jobs) ||
03885 !transcode_bfr_comm;
03886 if (rt)
03887 {
03888
03889 QString host = (on_host) ? gCoreContext->GetHostName() : "";
03890 JobQueue::QueueJob(JOB_COMMFLAG,
03891 rec->GetChanID(),
03892 rec->GetRecordingStartTime(), "", "",
03893 host, JOB_LIVE_REC);
03894
03895
03896 JobQueue::RemoveJobsFromMask(JOB_COMMFLAG, jobs);
03897 }
03898
03899 return jobs;
03900 }
03901
03902 static QString load_profile(QString cardtype, void *tvchain,
03903 RecordingInfo *rec, RecordingProfile &profile)
03904 {
03905
03906
03907
03908
03909 QString profileName = "Live TV";
03910 if (!tvchain && rec)
03911 profileName = rec->GetRecordingRule()->m_recProfile;
03912
03913 if (!profile.loadByType(profileName, cardtype))
03914 {
03915 profileName = "Default";
03916 profile.loadByType(profileName, cardtype);
03917 }
03918
03919 LOG(VB_RECORD, LOG_INFO, QString("Using profile '%1' to record")
03920 .arg(profileName));
03921
03922 return profileName;
03923 }
03924
03928 void TVRec::TuningNewRecorder(MPEGStreamData *streamData)
03929 {
03930 LOG(VB_RECORD, LOG_INFO, LOC + "Starting Recorder");
03931
03932 bool had_dummyrec = false;
03933 if (HasFlags(kFlagDummyRecorderRunning))
03934 {
03935 FinishedRecording(curRecording, NULL);
03936 ClearFlags(kFlagDummyRecorderRunning);
03937 curRecording->MarkAsInUse(false, kRecorderInUseID);
03938 had_dummyrec = true;
03939 }
03940
03941 RecordingInfo *rec = lastTuningRequest.program;
03942
03943 RecordingProfile profile;
03944 QString profileName = load_profile(genOpt.cardtype, tvchain, rec, profile);
03945
03946 if (tvchain)
03947 {
03948 bool ok;
03949 if (!ringBuffer)
03950 {
03951 ok = CreateLiveTVRingBuffer(channel->GetCurrentName());
03952 SetFlags(kFlagRingBufferReady);
03953 }
03954 else
03955 ok = SwitchLiveTVRingBuffer(channel->GetCurrentName(),
03956 true, !had_dummyrec && recorder);
03957 if (!ok)
03958 {
03959 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create RingBuffer 2");
03960 goto err_ret;
03961 }
03962 rec = curRecording;
03963 }
03964
03965 if (lastTuningRequest.flags & kFlagRecording)
03966 {
03967 bool write = genOpt.cardtype != "IMPORT";
03968 LOG(VB_GENERAL, LOG_INFO, LOC + QString("rec->GetPathname(): '%1'")
03969 .arg(rec->GetPathname()));
03970 SetRingBuffer(RingBuffer::Create(rec->GetPathname(), write));
03971 if (!ringBuffer->IsOpen() && write)
03972 {
03973 LOG(VB_GENERAL, LOG_ERR, LOC +
03974 QString("RingBuffer '%1' not open...")
03975 .arg(rec->GetPathname()));
03976 SetRingBuffer(NULL);
03977 ClearFlags(kFlagPendingActions);
03978 goto err_ret;
03979 }
03980 }
03981
03982 if (!ringBuffer)
03983 {
03984 LOG(VB_GENERAL, LOG_ERR, LOC +
03985 QString("Failed to start recorder! ringBuffer is NULL\n"
03986 "\t\t\t\t Tuning request was %1\n")
03987 .arg(lastTuningRequest.toString()));
03988
03989 if (HasFlags(kFlagLiveTV))
03990 {
03991 QString message = QString("QUIT_LIVETV %1").arg(cardid);
03992 MythEvent me(message);
03993 gCoreContext->dispatch(me);
03994 }
03995 goto err_ret;
03996 }
03997
03998 if (channel && genOpt.cardtype == "MJPEG")
03999 channel->Close();
04000
04001 recorder = RecorderBase::CreateRecorder(
04002 this, channel, profile, genOpt, dvbOpt);
04003
04004 if (recorder)
04005 {
04006 recorder->SetRingBuffer(ringBuffer);
04007 recorder->Initialize();
04008 if (recorder->IsErrored())
04009 {
04010 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialize recorder!");
04011 delete recorder;
04012 recorder = NULL;
04013 }
04014 }
04015
04016 if (!recorder)
04017 {
04018 LOG(VB_GENERAL, LOG_ERR, LOC +
04019 QString("Failed to start recorder!\n"
04020 "\t\t\t\t Tuning request was %1\n")
04021 .arg(lastTuningRequest.toString()));
04022
04023 if (HasFlags(kFlagLiveTV))
04024 {
04025 QString message = QString("QUIT_LIVETV %1").arg(cardid);
04026 MythEvent me(message);
04027 gCoreContext->dispatch(me);
04028 }
04029 TeardownRecorder(kFlagKillRec);
04030 goto err_ret;
04031 }
04032
04033 if (GetDTVRecorder() && streamData)
04034 {
04035 const Setting *setting = profile.byName("recordingtype");
04036 if (setting)
04037 streamData->SetRecordingType(setting->getValue());
04038 GetDTVRecorder()->SetStreamData(streamData);
04039 }
04040
04041 if (channel && genOpt.cardtype == "MJPEG")
04042 channel->Open();
04043
04044 if (rec)
04045 recorder->SetRecording(rec);
04046
04047
04048 if (channel)
04049 {
04050 SetVideoFiltersForChannel(channel->GetCurrentSourceID(),
04051 channel->GetCurrentName());
04052 }
04053
04054 #ifdef USING_V4L2
04055 if (GetV4LChannel())
04056 {
04057 channel->InitPictureAttributes();
04058 CloseChannel();
04059 }
04060 #endif
04061
04062 recorderThread = new MThread("RecThread", recorder);
04063 recorderThread->start();
04064
04065
04066 stateChangeLock.unlock();
04067 while (!recorder->IsRecording() && !recorder->IsErrored())
04068 usleep(5 * 1000);
04069 stateChangeLock.lock();
04070
04071 if (GetV4LChannel())
04072 channel->SetFd(recorder->GetVideoFd());
04073
04074 SetFlags(kFlagRecorderRunning | kFlagRingBufferReady);
04075
04076 if (!tvchain)
04077 autoRunJobs = init_jobs(rec, profile, runJobOnHostOnly,
04078 transcodeFirst, earlyCommFlag);
04079
04080 ClearFlags(kFlagNeedToStartRecorder);
04081 return;
04082
04083 err_ret:
04084 ChangeState(kState_None);
04085 if (tvchain)
04086 delete rec;
04087 }
04088
04092 void TVRec::TuningRestartRecorder(void)
04093 {
04094 LOG(VB_RECORD, LOG_INFO, LOC + "Restarting Recorder");
04095
04096 bool had_dummyrec = false;
04097
04098 if (curRecording)
04099 {
04100 FinishedRecording(curRecording, NULL);
04101 curRecording->MarkAsInUse(false, kRecorderInUseID);
04102 }
04103
04104 if (HasFlags(kFlagDummyRecorderRunning))
04105 {
04106 ClearFlags(kFlagDummyRecorderRunning);
04107 had_dummyrec = true;
04108 }
04109
04110 SwitchLiveTVRingBuffer(channel->GetCurrentName(), true, !had_dummyrec);
04111
04112 if (had_dummyrec)
04113 {
04114 recorder->SetRingBuffer(ringBuffer);
04115 ProgramInfo *progInfo = tvchain->GetProgramAt(-1);
04116 recorder->SetRecording(progInfo);
04117 delete progInfo;
04118 }
04119 recorder->Reset();
04120
04121
04122 if (GetV4LChannel())
04123 channel->SetFd(recorder->GetVideoFd());
04124
04125
04126 recorder->Unpause();
04127
04128 if (pseudoLiveTVRecording)
04129 {
04130 ProgramInfo *rcinfo1 = pseudoLiveTVRecording;
04131 QString msg1 = QString("Recording: %1 %2 %3 %4")
04132 .arg(rcinfo1->GetTitle()).arg(rcinfo1->GetChanID())
04133 .arg(rcinfo1->GetRecordingStartTime(ISODate))
04134 .arg(rcinfo1->GetRecordingEndTime(ISODate));
04135 ProgramInfo *rcinfo2 = tvchain->GetProgramAt(-1);
04136 QString msg2 = QString("Recording: %1 %2 %3 %4")
04137 .arg(rcinfo2->GetTitle()).arg(rcinfo2->GetChanID())
04138 .arg(rcinfo2->GetRecordingStartTime(ISODate))
04139 .arg(rcinfo2->GetRecordingEndTime(ISODate));
04140 delete rcinfo2;
04141 LOG(VB_RECORD, LOG_INFO, LOC + "Pseudo LiveTV recording starting." +
04142 "\n\t\t\t" + msg1 + "\n\t\t\t" + msg2);
04143
04144 curRecording->SaveAutoExpire(
04145 curRecording->GetRecordingRule()->GetAutoExpire());
04146
04147 curRecording->ApplyRecordRecGroupChange(
04148 curRecording->GetRecordingRule()->m_recGroup);
04149
04150 RecordingProfile profile;
04151 QString profileName = load_profile(genOpt.cardtype, NULL,
04152 curRecording, profile);
04153 autoRunJobs = init_jobs(curRecording, profile, runJobOnHostOnly,
04154 transcodeFirst, earlyCommFlag);
04155 }
04156
04157 ClearFlags(kFlagNeedToStartRecorder);
04158 }
04159
04160 void TVRec::SetFlags(uint f)
04161 {
04162 QMutexLocker lock(&stateChangeLock);
04163 stateFlags |= f;
04164 LOG(VB_RECORD, LOG_INFO, LOC + QString("SetFlags(%1) -> %2")
04165 .arg(FlagToString(f)).arg(FlagToString(stateFlags)));
04166 WakeEventLoop();
04167 }
04168
04169 void TVRec::ClearFlags(uint f)
04170 {
04171 QMutexLocker lock(&stateChangeLock);
04172 stateFlags &= ~f;
04173 LOG(VB_RECORD, LOG_INFO, LOC + QString("ClearFlags(%1) -> %2")
04174 .arg(FlagToString(f)).arg(FlagToString(stateFlags)));
04175 WakeEventLoop();
04176 }
04177
04178 QString TVRec::FlagToString(uint f)
04179 {
04180 QString msg("");
04181
04182
04183 if (kFlagFrontendReady & f)
04184 msg += "FrontendReady,";
04185 if (kFlagRunMainLoop & f)
04186 msg += "RunMainLoop,";
04187 if (kFlagExitPlayer & f)
04188 msg += "ExitPlayer,";
04189 if (kFlagFinishRecording & f)
04190 msg += "FinishRecording,";
04191 if (kFlagErrored & f)
04192 msg += "Errored,";
04193 if (kFlagCancelNextRecording & f)
04194 msg += "CancelNextRecording,";
04195
04196
04197 if ((kFlagRec & f) == kFlagRec)
04198 msg += "REC,";
04199 else
04200 {
04201 if (kFlagLiveTV & f)
04202 msg += "LiveTV,";
04203 if (kFlagRecording & f)
04204 msg += "Recording,";
04205 }
04206 if ((kFlagNoRec & f) == kFlagNoRec)
04207 msg += "NOREC,";
04208 else
04209 {
04210 if (kFlagEITScan & f)
04211 msg += "EITScan,";
04212 if (kFlagCloseRec & f)
04213 msg += "CloseRec,";
04214 if (kFlagKillRec & f)
04215 msg += "KillRec,";
04216 if (kFlagAntennaAdjust & f)
04217 msg += "AntennaAdjust,";
04218 }
04219 if ((kFlagPendingActions & f) == kFlagPendingActions)
04220 msg += "PENDINGACTIONS,";
04221 else
04222 {
04223 if (kFlagWaitingForRecPause & f)
04224 msg += "WaitingForRecPause,";
04225 if (kFlagWaitingForSignal & f)
04226 msg += "WaitingForSignal,";
04227 if (kFlagNeedToStartRecorder & f)
04228 msg += "NeedToStartRecorder,";
04229 if (kFlagKillRingBuffer & f)
04230 msg += "KillRingBuffer,";
04231 }
04232 if ((kFlagAnyRunning & f) == kFlagAnyRunning)
04233 msg += "ANYRUNNING,";
04234 else
04235 {
04236 if (kFlagSignalMonitorRunning & f)
04237 msg += "SignalMonitorRunning,";
04238 if (kFlagEITScannerRunning & f)
04239 msg += "EITScannerRunning,";
04240 if ((kFlagAnyRecRunning & f) == kFlagAnyRecRunning)
04241 msg += "ANYRECRUNNING,";
04242 else
04243 {
04244 if (kFlagDummyRecorderRunning & f)
04245 msg += "DummyRecorderRunning,";
04246 if (kFlagRecorderRunning & f)
04247 msg += "RecorderRunning,";
04248 }
04249 }
04250 if (kFlagRingBufferReady & f)
04251 msg += "RingBufferReady,";
04252
04253 if (msg.isEmpty())
04254 msg = QString("0x%1").arg(f,0,16);
04255
04256 return msg;
04257 }
04258
04259 bool TVRec::WaitForNextLiveTVDir(void)
04260 {
04261 QMutexLocker lock(&nextLiveTVDirLock);
04262
04263 bool found = !nextLiveTVDir.isEmpty();
04264 if (!found && triggerLiveTVDir.wait(&nextLiveTVDirLock, 500))
04265 {
04266 found = !nextLiveTVDir.isEmpty();
04267 }
04268
04269 return found;
04270 }
04271
04272 void TVRec::SetNextLiveTVDir(QString dir)
04273 {
04274 QMutexLocker lock(&nextLiveTVDirLock);
04275
04276 nextLiveTVDir = dir;
04277 triggerLiveTVDir.wakeAll();
04278 }
04279
04280 bool TVRec::GetProgramRingBufferForLiveTV(RecordingInfo **pginfo,
04281 RingBuffer **rb,
04282 const QString & channum,
04283 int inputID)
04284 {
04285 LOG(VB_RECORD, LOG_INFO, LOC + "GetProgramRingBufferForLiveTV()");
04286 if (!channel || !tvchain || !pginfo || !rb)
04287 return false;
04288
04289 nextLiveTVDirLock.lock();
04290 nextLiveTVDir.clear();
04291 nextLiveTVDirLock.unlock();
04292
04293
04294 MythEvent me(QString("QUERY_NEXT_LIVETV_DIR %1").arg(cardid));
04295 gCoreContext->dispatch(me);
04296
04297 uint sourceid = channel->GetSourceID(inputID);
04298 int chanid = ChannelUtil::GetChanID(sourceid, channum);
04299
04300 if (chanid < 0)
04301 {
04302
04303 if (genOpt.cardtype == "IMPORT" || genOpt.cardtype == "DEMO")
04304 chanid = 9999;
04305 else
04306 {
04307 LOG(VB_GENERAL, LOG_ERR, LOC +
04308 QString("Channel: \'%1\' was not found in the database.\n"
04309 "\t\tMost likely, your DefaultTVChannel setting is "
04310 "wrong.\n"
04311 "\t\tCould not start livetv.").arg(channum));
04312 return false;
04313 }
04314 }
04315
04316 int hoursMax = gCoreContext->GetNumSetting("MaxHoursPerLiveTVRecording", 8);
04317 if (hoursMax <= 0)
04318 hoursMax = 8;
04319
04320 RecordingInfo *prog = NULL;
04321 if (pseudoLiveTVRecording)
04322 prog = new RecordingInfo(*pseudoLiveTVRecording);
04323 else
04324 {
04325 prog = new RecordingInfo(
04326 chanid, mythCurrentDateTime(), true, hoursMax);
04327 }
04328
04329 prog->SetCardID(cardid);
04330
04331 if (prog->GetRecordingStartTime() == prog->GetRecordingEndTime())
04332 {
04333 LOG(VB_GENERAL, LOG_ERR, LOC + "GetProgramRingBufferForLiveTV()"
04334 "\n\t\t\tProgramInfo is invalid."
04335 "\n" + prog->toString());
04336 prog->SetScheduledEndTime(prog->GetRecordingStartTime().addSecs(3600));
04337 prog->SetRecordingEndTime(prog->GetScheduledEndTime());
04338
04339 prog->SetChanID(chanid);
04340 }
04341
04342 if (!pseudoLiveTVRecording)
04343 prog->SetRecordingStartTime(mythCurrentDateTime());
04344
04345 prog->SetStorageGroup("LiveTV");
04346
04347 if (WaitForNextLiveTVDir())
04348 {
04349 QMutexLocker lock(&nextLiveTVDirLock);
04350 prog->SetPathname(nextLiveTVDir);
04351 }
04352 else
04353 {
04354 StorageGroup sgroup("LiveTV", gCoreContext->GetHostName());
04355 prog->SetPathname(sgroup.FindNextDirMostFree());
04356 }
04357
04358 StartedRecording(prog);
04359
04360 *rb = RingBuffer::Create(prog->GetPathname(), true);
04361 if (!(*rb)->IsOpen())
04362 {
04363 LOG(VB_GENERAL, LOG_ERR, LOC + QString("RingBuffer '%1' not open...")
04364 .arg(prog->GetPathname()));
04365
04366 delete *rb;
04367 delete prog;
04368
04369 return false;
04370 }
04371
04372 *pginfo = prog;
04373 return true;
04374 }
04375
04376 bool TVRec::CreateLiveTVRingBuffer(const QString & channum)
04377 {
04378 LOG(VB_RECORD, LOG_INFO, LOC + QString("CreateLiveTVRingBuffer(%1)")
04379 .arg(channum));
04380
04381 RecordingInfo *pginfo = NULL;
04382 RingBuffer *rb = NULL;
04383 QString inputName;
04384 int inputID = -1;
04385
04386 if (!channel->CheckChannel(channum, inputName))
04387 {
04388 ChangeState(kState_None);
04389 return false;
04390 }
04391
04392 inputID = inputName.isEmpty() ?
04393 channel->GetCurrentInputNum() : channel->GetInputByName(inputName);
04394
04395 if (!GetProgramRingBufferForLiveTV(&pginfo, &rb, channum, inputID))
04396 {
04397 ClearFlags(kFlagPendingActions);
04398 ChangeState(kState_None);
04399 LOG(VB_GENERAL, LOG_ERR, LOC +
04400 QString("CreateLiveTVRingBuffer(%1) failed").arg(channum));
04401 return false;
04402 }
04403
04404 SetRingBuffer(rb);
04405
04406 pginfo->SaveAutoExpire(kLiveTVAutoExpire);
04407 pginfo->ApplyRecordRecGroupChange("LiveTV");
04408
04409 bool discont = (tvchain->TotalSize() > 0);
04410 tvchain->AppendNewProgram(pginfo, channel->GetCurrentName(),
04411 channel->GetCurrentInput(), discont);
04412
04413 if (curRecording)
04414 {
04415 curRecording->MarkAsInUse(false, kRecorderInUseID);
04416 delete curRecording;
04417 }
04418
04419 curRecording = pginfo;
04420 curRecording->MarkAsInUse(true, kRecorderInUseID);
04421
04422 return true;
04423 }
04424
04425 bool TVRec::SwitchLiveTVRingBuffer(const QString & channum,
04426 bool discont, bool set_rec)
04427 {
04428 QString msg;
04429 if (curRecording)
04430 {
04431 msg = QString(" curRec(%1) curRec.size(%2)")
04432 .arg(curRecording->MakeUniqueKey())
04433 .arg(curRecording->GetFilesize());
04434 }
04435 LOG(VB_RECORD, LOG_INFO, LOC +
04436 QString("SwitchLiveTVRingBuffer(discont %1, set_next_rec %2)")
04437 .arg(discont).arg(set_rec) + msg);
04438
04439 RecordingInfo *pginfo = NULL;
04440 RingBuffer *rb = NULL;
04441 QString inputName;
04442 int inputID = -1;
04443
04444 if (!channel->CheckChannel(channum, inputName))
04445 {
04446 ChangeState(kState_None);
04447 return false;
04448 }
04449
04450 inputID = inputName.isEmpty() ?
04451 channel->GetCurrentInputNum() : channel->GetInputByName(inputName);
04452
04453 if (!GetProgramRingBufferForLiveTV(&pginfo, &rb, channum, inputID))
04454 {
04455 ChangeState(kState_None);
04456 return false;
04457 }
04458
04459 QString oldcardtype = tvchain->GetCardType(-1);
04460
04461 pginfo->MarkAsInUse(true, kRecorderInUseID);
04462 pginfo->SaveAutoExpire(kLiveTVAutoExpire);
04463 pginfo->ApplyRecordRecGroupChange("LiveTV");
04464 tvchain->AppendNewProgram(pginfo, channel->GetCurrentName(),
04465 channel->GetCurrentInput(), discont);
04466
04467 if (set_rec && recorder)
04468 {
04469 recorder->SetNextRecording(pginfo, rb);
04470 if (discont)
04471 recorder->CheckForRingBufferSwitch();
04472 delete pginfo;
04473 SetFlags(kFlagRingBufferReady);
04474 }
04475 else if (!set_rec)
04476 {
04477
04478
04479 if (curRecording && oldcardtype != "DUMMY")
04480 {
04481 FinishedRecording(curRecording, NULL);
04482 curRecording->MarkAsInUse(false, kRecorderInUseID);
04483 delete curRecording;
04484 }
04485 curRecording = pginfo;
04486 SetRingBuffer(rb);
04487 }
04488
04489 return true;
04490 }
04491
04492 RecordingInfo *TVRec::SwitchRecordingRingBuffer(const RecordingInfo &rcinfo)
04493 {
04494 LOG(VB_RECORD, LOG_INFO, LOC + "SwitchRecordingRingBuffer()");
04495
04496 if (switchingBuffer || !recorder || !curRecording ||
04497 (rcinfo.GetChanID() != curRecording->GetChanID()))
04498 {
04499 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer() -> false 1");
04500 return NULL;
04501 }
04502
04503 PreviewGeneratorQueue::GetPreviewImage(*curRecording, "");
04504
04505 RecordingInfo *ri = new RecordingInfo(rcinfo);
04506 ri->MarkAsInUse(true, kRecorderInUseID);
04507 StartedRecording(ri);
04508
04509 bool write = genOpt.cardtype != "IMPORT";
04510 RingBuffer *rb = RingBuffer::Create(ri->GetPathname(), write);
04511 if (!rb->IsOpen())
04512 {
04513 ri->SetRecordingStatus(rsFailed);
04514 FinishedRecording(ri, NULL);
04515 ri->MarkAsInUse(false, kRecorderInUseID);
04516 delete ri;
04517 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer() -> false 2");
04518 return NULL;
04519 }
04520 else
04521 {
04522 recorder->SetNextRecording(ri, rb);
04523 SetFlags(kFlagRingBufferReady);
04524 recordEndTime = GetRecordEndTime(ri);
04525 switchingBuffer = true;
04526 ri->SetRecordingStatus(rsRecording);
04527 LOG(VB_RECORD, LOG_INFO, LOC + "SwitchRecordingRingBuffer() -> true");
04528 return ri;
04529 }
04530 }
04531
04532 TVRec* TVRec::GetTVRec(uint cardid)
04533 {
04534 QMutexLocker locker(&cardsLock);
04535 QMap<uint,TVRec*>::const_iterator it = cards.find(cardid);
04536 if (it == cards.end())
04537 return NULL;
04538 return *it;
04539 }
04540
04541 QString TuningRequest::toString(void) const
04542 {
04543 return QString("Program(%1) channel(%2) input(%3) flags(%4)")
04544 .arg((program == NULL) ? QString("NULL") : program->toString())
04545 .arg(channel).arg(input)
04546 .arg(TVRec::FlagToString(flags));
04547 }
04548
04549 #ifdef USING_DVB
04550 #include "dvbchannel.h"
04551 static void apply_broken_dvb_driver_crc_hack(ChannelBase *c, MPEGStreamData *s)
04552 {
04553
04554
04555
04556 if (dynamic_cast<DVBChannel*>(c))
04557 s->SetIgnoreCRC(dynamic_cast<DVBChannel*>(c)->HasCRCBug());
04558 }
04559 #else
04560 static void apply_broken_dvb_driver_crc_hack(ChannelBase*, MPEGStreamData*) {}
04561 #endif // USING_DVB
04562
04563
04564