MythTV  0.26-pre
tv_rec.cpp
Go to the documentation of this file.
00001 // C headers
00002 #include <cstdio>
00003 #include <cstdlib>
00004 #include <cstring>
00005 #include <unistd.h>
00006 #include <sched.h> // for sched_yield
00007 
00008 // MythTV headers
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; /* msec */
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        // Various components TVRec coordinates
00083     : recorder(NULL), channel(NULL), signalMonitor(NULL),
00084       scanner(NULL),
00085       // Various threads
00086       eventThread(new MThread("TVRecEvent", this)),
00087       recorderThread(NULL),
00088       // Configuration variables from database
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       // Configuration variables from setup rutines
00096       cardid(capturecardnum), ispip(false),
00097       // State variables
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       // Current recording info
00110       curRecording(NULL), autoRunJobs(JOB_NONE),
00111       overrecordseconds(0),
00112       // Pseudo LiveTV recording
00113       pseudoLiveTVRecording(NULL),
00114       nextLiveTVDir(""),            nextLiveTVDirLock(),
00115       // tvchain
00116       tvchain(NULL),
00117       // RingBuffer info
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     // configure the Channel instance
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     // If this isn't a recording for this instance to make, we are done
00310     if (rcinfo->GetCardID() != cardid)
00311         return;
00312 
00313     // We also need to check our input groups
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     // Flush out any pending state changes
00415     WaitForEventThreadSleep();
00416 
00417     // We need to do this check early so we don't cancel an overrecord
00418     // that we're trying to extend.
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     // Flush out events...
00459     WaitForEventThreadSleep();
00460 
00461     // Rescan pending recordings since the event loop may have deleted
00462     // a stale entry.  If this happens the info pointer will not be valid
00463     // since the HandlePendingRecordings loop will have deleted it.
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     // If the needed input is in a shared input group, and we are
00472     // not canceling the recording anyway, check other recorders
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         // Stop remote recordings if needed
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             // if the other recorder is busy, but the input is
00490             // not in a shared input group, then as far as we're
00491             // concerned here it isn't busy.
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             // If we managed to stop LiveTV recording, restart playback..
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         // If we failed to stop the remote recordings, don't record
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             // Make sure scheduler is allowed to end this recording
00564             ClearFlags(kFlagCancelNextRecording);
00565 
00566             SetRecordingStatus(rsRecording, __LINE__);
00567         }
00568         else
00569         {
00570             // If in post-roll, end recording
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         // Tell event loop to begin recording.
00590         curRecording = new RecordingInfo(*rcinfo);
00591         curRecording->MarkAsInUse(true, kRecorderInUseID);
00592         StartedRecording(curRecording);
00593 
00594         // Make sure scheduler is allowed to end this recording
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         // We want the frontend to change channel for recording
00607         // and disable the UI for channel change, PiP, etc.
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         // wait for state change to take effect
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     // Make sure the recording group is up to date
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     // Set the final recording status
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     // Figure out if this was already done for this recording
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     // Print something informative to the log
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     // This has already been called on this recording..
00865     if (was_finished)
00866         return;
00867 
00868     // Notify the frontend watching live tv that this file is final
00869     if (tvchain)
00870         tvchain->FinishedRecording(curRec);
00871 
00872     // if this is a dummy recorder, do no more..
00873     if (HasFlags(kFlagDummyRecorderRunning))
00874     {
00875         curRec->FinishedRecording(true); // so end time is updated
00876         SendMythSystemRecEvent("REC_FINISHED", curRecording);
00877         return;
00878     }
00879 
00880     // Get the width and set the videoprops
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     // Make sure really short recordings have positive run time.
00888     if (curRec->GetRecordingEndTime() <= curRec->GetRecordingStartTime())
00889     {
00890         curRec->SetRecordingEndTime(
00891             curRec->GetRecordingStartTime().addSecs(60));
00892     }
00893 
00894     // Generate a preview
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     // send out UPDATE_RECORDING_STATUS message
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     // store recording in recorded table
00916     curRec->FinishedRecording(!is_good || (recgrp == "LiveTV"));
00917 
00918     // send out REC_FINISHED message
00919     SendMythSystemRecEvent("REC_FINISHED", curRecording);
00920 
00921     // send out DONE_RECORDING message
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     // Handle JobQueue
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     // Make sure EIT scan is stopped before any tuning,
00975     // to avoid race condition with it's tuning requests.
00976     if (scanner && HasFlags(kFlagEITScannerRunning))
00977     {
00978         scanner->StopActiveScan();
00979         ClearFlags(kFlagEITScannerRunning);
00980     }
00981 
00982     // Handle different state transitions
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     // update internal state variable
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     // check whether we should use the EITScanner in this TVRec instance
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         // get the number of capture cards and the position of the current card
01217         // to distribute the the scan start evenly over eitTransportTimeout
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         // If there is a state change queued up, do it...
01233         if (changeState)
01234         {
01235             HandleStateChange();
01236             ClearFlags(kFlagFrontendReady | kFlagCancelNextRecording);
01237         }
01238 
01239         // Quick exit on fatal errors.
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         // Handle any tuning events..
01249         HandleTuning();
01250 
01251         // Tell frontends about pending recordings
01252         HandlePendingRecordings();
01253 
01254         // If we are recording a program, check if the recording is
01255         // over or someone has asked us to finish the recording.
01256         // Add an extra 60 seconds to the recording end time if we
01257         // might want a back to back recording.
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                 // Check for recorder errors
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         // Check for the end of the current program..
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         // Check for ExitPlayer flag, and if set change to a non-watching
01347         // state (either kState_RecordingOnly or kState_None).
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         // We should be no more than a few thousand milliseconds,
01381         // as the end recording code does not have a trigger...
01382         // NOTE: If you change anything here, make sure that
01383         // WaitforEventThreadSleep() will still work...
01384         if (tuningRequests.empty() && !changeState)
01385         {
01386             lock.unlock(); // stateChangeLock
01387 
01388             {
01389                 QMutexLocker locker(&triggerEventSleepLock);
01390                 triggerEventSleepSignal = true;
01391                 triggerEventSleepWait.wakeAll();
01392             }
01393 
01394             sched_yield();
01395 
01396             {
01397                 QMutexLocker locker(&triggerEventLoopLock);
01398                 // We check triggerEventLoopSignal because it is possible
01399                 // that WakeEventLoop() was called since we
01400                 // unlocked the stateChangeLock
01401                 if (!triggerEventLoopSignal)
01402                 {
01403                     triggerEventLoopWait.wait(
01404                         &triggerEventLoopLock, 1000 /* ms */);
01405                 }
01406                 triggerEventLoopSignal = false;
01407             }
01408 
01409             lock.relock(); // stateChangeLock
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         // verify that we were triggered.
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     // If we have a pending recording and AskAllowRecording
01470     // or DoNotAskAllowRecording is set and the frontend is
01471     // ready send an ASK_RECORDING query to frontend.
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     // General options
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     // We should have at least 100 ms to acquire tables...
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     // DVB options
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     // Firewire options
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     // Get last tuned channel from database, to use as starting channel
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     // If we failed to get the last tuned channel,
01645     // get a valid channel on our current input.
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     // If we failed to get a channel on our current input,
01672     // widen search to any input.
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     // If there are no valid channels, just use a random channel
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     // Check if this is an ATSC Channel
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         // Try to get pid of VCT from cache and
01806         // require MGT if we don't have VCT pid.
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     // Check if this is an DVB channel
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     // Check if this is an MPEG channel
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     // If this is not an ATSC, DVB or MPEG channel then check to make sure
01896     // that we have permanent pidcache entries.
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     // if it already exists, there no need to initialize it
01939     if (signalMonitor)
01940         return true;
01941 
01942     // if there is no channel object we can't monitor it
01943     if (!channel)
01944         return false;
01945 
01946     // nothing to monitor here either (DummyChannel)
01947     if (genOpt.cardtype == "IMPORT" || genOpt.cardtype == "DEMO")
01948         return true;
01949 
01950     // make sure statics are initialized
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         // If this is a monitor for Digital TV, initialize table monitors
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         // Start the monitoring thread
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     // If this is a DTV signal monitor, save any pids we know about.
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     // Wait for RingBuffer reset
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     // We didn't find it on the current card, so now we check other cards.
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     // Now process the lists for the info we need...
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) // Unique channel...
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     // If we get this far there is more than one channel
02298     // sharing the prefix we were given.
02299 
02300     // Is an extra characher useful for disambiguation?
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     // Are any of the channels complete w/o spacer?
02313     // If so set is_complete_valid_channel_on_rec,
02314     // with a preference for our cardid.
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     // Add a spacer, if one is needed to select a valid channel.
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     // If it isn't useful to wait for more characters,
02336     // then try to commit to any true match immediately.
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; // use DVD max bit rate
02534     if (genOpt.cardtype == "HDPVR")
02535         bitrate = 20200000LL; // Peek bit rate for HD-PVR
02536     else if (!CardUtil::IsEncoder(genOpt.cardtype))
02537         bitrate = 22200000LL; // 1080i
02538     else // frame grabber
02539         bitrate = 10080000LL; // use DVD max bit rate, probably too big
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     // Change to WatchingLiveTV
02567     ChangeState(kState_WatchingLiveTV);
02568     // Wait for state change to take effect
02569     WaitForEventThreadSleep();
02570 
02571     // Make sure StartRecording can't steal our tuner
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; // already stopped
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         // User wants this recording to continue
02609         SetPseudoLiveTVRecording(new ProgramInfo(*curRecording));
02610     }
02611     else if (recgrp == "LiveTV" && pseudoLiveTVRecording)
02612     {
02613         // User wants to abandon scheduled recording
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     // Notify scheduler of the recording.
02654     // + set up recording so it can be resumed
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     // + remove any end offset which would mismatch the live session
02666     rec->GetRecordingRule()->m_endOffset = 0;
02667 
02668     // + save rsInactive recstatus to so that a reschedule call
02669     //   doesn't start recording this on another card before we
02670     //   send the SCHEDULER_ADD_RECORDING message to the scheduler.
02671     rec->SetRecordingStatus(rsInactive);
02672     rec->AddHistory(false);
02673 
02674     // + save RecordingRule so that we get a recordid
02675     //   (don't allow RescheduleMatch(), avoiding unneeded reschedule)
02676     rec->GetRecordingRule()->Save(false);
02677 
02678     // + save recordid to recorded entry
02679     rec->ApplyRecordRecID();
02680 
02681     // + set proper recstatus (saved later)
02682     rec->SetRecordingStatus(rsRecording);
02683 
02684     // + pass proginfo to scheduler and reschedule
02685     QStringList prog;
02686     rec->ToStringList(prog);
02687     MythEvent me("SCHEDULER_ADD_RECORDING", prog);
02688     gCoreContext->dispatch(me);
02689 
02690     // Allow scheduler to end this recording before post-roll,
02691     // if it has another recording for this recorder.
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         // cancel -- 'recording' should be 0 or -1
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         // record -- 'recording' should be 1 or -1
02729 
02730         // If the last recording was flagged for keeping
02731         // in the frontend, then add the recording rule
02732         // so that transcode, commfrag, etc can be run.
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     // Figure out next state and if needed recording end time.
02776     TVState next_state = kState_None;
02777     if (pseudoLiveTVRecording)
02778     {
02779         recordEndTime = GetRecordEndTime(pseudoLiveTVRecording);
02780         next_state = kState_RecordingOnly;
02781     }
02782 
02783     // Change to the appropriate state
02784     ChangeState(next_state);
02785 
02786     // Wait for state change to take effect...
02787     WaitForEventThreadSleep();
02788 
02789     // We are done with the tvchain...
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     // Get current channel id...
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     // Detect tuning request type if needed
02969     if (requestType & kFlagDetect)
02970     {
02971         WaitForEventThreadSleep();
02972         requestType = lastTuningRequest.flags & (kFlagRec | kFlagNoRec);
02973     }
02974 
02975     // Clear the RingBuffer reset flag, in case we wait for a reset below
02976     ClearFlags(kFlagRingBufferReady);
02977 
02978     // Actually add the tuning request to the queue, and
02979     // then wait for it to start tuning
02980     tuningRequests.enqueue(TuningRequest(requestType, name, input));
02981     WaitForEventThreadSleep();
02982 
02983     // If we are using a recorder, wait for a RingBuffer reset
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     // Detect tuning request type if needed
03025     if (requestType & kFlagDetect)
03026     {
03027         WaitForEventThreadSleep();
03028         requestType = lastTuningRequest.flags & (kFlagRec | kFlagNoRec);
03029     }
03030 
03031     // Clear the RingBuffer reset flag, in case we wait for a reset below
03032     ClearFlags(kFlagRingBufferReady);
03033 
03034     // Actually add the tuning request to the queue, and
03035     // then wait for it to start tuning
03036     tuningRequests.enqueue(TuningRequest(requestType, name));
03037     WaitForEventThreadSleep();
03038 
03039     // If we are using a recorder, wait for a RingBuffer reset
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     // Clear everything now in case either query fails.
03125     title     = subtitle  = desc      = category  = "";
03126     starttime = endtime   = callsign  = iconpath  = "";
03127     channum   = seriesid  = programid = "";
03128     sourceChanid = 0;
03129 
03130     // Try to get the program info
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     // Couldn't get program info, so get the channel info instead
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     // If this is Live TV startup, we need a channel...
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         // The dequeue isn't safe to do until now because we
03398         // release the stateChangeLock to teardown a recorder
03399         tuningRequests.dequeue();
03400 
03401         // Now we start new stuff
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         // If we got this far it is safe to set a new starting channel...
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         // Delete StreamData if it is not in use by the recorder.
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     // At this point any waits are canceled.
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         // At this point the recorders are shut down
03545 
03546         CloseChannel();
03547         // At this point the channel is shut down
03548     }
03549 
03550     // handle HW change for digital/analog cards
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         // At this point the ringbuffer is shut down
03567     }
03568 
03569     // Clear pending actions from last request
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         // We need there to be a ringbuffer for these modes
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     // Start signal monitoring for devices capable of monitoring
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             // pretend the signal monitor is running to prevent segfault
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             // Make sure recorder doesn't point to bogus ringbuffer before
03746             // it is potentially restarted without a new ringbuffer, if
03747             // the next channel won't tune and the user exits LiveTV.
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         // if we had problems starting the signal monitor,
03757         // we don't want to start the recorder...
03758         if (error)
03759             return;
03760     }
03761 
03762     // Request a recorder, if the command is a recording command
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     // grab useful data from DTV signal monitor before we kill it...
03815     MPEGStreamData *streamData = NULL;
03816     if (GetDTVSignalMonitor())
03817         streamData = GetDTVSignalMonitor()->GetStreamData();
03818 
03819     if (!HasFlags(kFlagEITScannerRunning))
03820     {
03821         // shut down signal monitoring
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; // no jobs for Live TV recordings..
03849 
03850     int jobs = 0; // start with no jobs
03851 
03852     // grab standard jobs flags from program info
03853     JobQueue::AddJobsToMask(rec->GetAutoRunJobs(), jobs);
03854 
03855     // disable commercial flagging on PBS, BBC, etc.
03856     if (rec->IsCommercialFree())
03857         JobQueue::RemoveJobsFromMask(JOB_COMMFLAG, jobs);
03858 
03859     // disable transcoding if the profile does not allow auto transcoding
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         // When allowed, metadata lookup should occur at the
03868         // start of a recording to make the additional info
03869         // available immediately (and for use in future jobs).
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         // don't do regular metadata lookup, we won't need it.
03877         JobQueue::RemoveJobsFromMask(JOB_METADATA, jobs);
03878     }
03879 
03880     // is commercial flagging enabled, and is on-line comm flagging enabled?
03881     bool rt = JobQueue::JobIsInMask(JOB_COMMFLAG, jobs) && on_line_comm;
03882     // also, we either need transcoding to be disabled or
03883     // we need to be allowed to commercial flag before transcoding?
03884     rt &= JobQueue::JobIsNotInMask(JOB_TRANSCODE, jobs) ||
03885         !transcode_bfr_comm;
03886     if (rt)
03887     {
03888         // queue up real-time (i.e. on-line) commercial flagging.
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         // don't do regular comm flagging, we won't need it.
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     // Determine the correct recording profile.
03906     // In LiveTV mode use "Live TV" profile, otherwise use the
03907     // recording's specified profile. If the desired profile can't
03908     // be found, fall back to the "Default" profile for card type.
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;  // new'd in Create/SwitchLiveTVRingBuffer()
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(); // Needed because of NVR::MJPEGInit()
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(); // Needed because of NVR::MJPEGInit()
04043 
04044     if (rec)
04045         recorder->SetRecording(rec);
04046 
04047     // Setup for framebuffer capture devices..
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     // Wait for recorder to start.
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     // Set file descriptor of channel from recorder for V4L
04122     if (GetV4LChannel())
04123         channel->SetFd(recorder->GetVideoFd());
04124 
04125     // Some recorders unpause on Reset, others do not...
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     // General flags
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     // Tuning flags
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     // Dispatch this early, the response can take a while.
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         // Test setups might have zero channels
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         // dummy recordings are finished before this
04478         // is called and other recordings must be finished..
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     // Some DVB devices munge the PMT and/or PAT so the CRC check fails.
04554     // We need to tell the stream data class to not check the CRC on
04555     // these devices. This can cause segfaults.
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 /* vim: set expandtab tabstop=4 shiftwidth=4: */
04564 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends