• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Classes
  • Files

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 <pthread.h>
00007 #include <sched.h> // for sched_yield
00008 
00009 // C++ headers
00010 #include <iostream>
00011 using namespace std;
00012 
00013 // Qt headers
00014 #include <qapplication.h>
00015 #include <qsqldatabase.h>
00016 #include <qsocket.h>
00017 
00018 // MythTV headers
00019 #include "mythconfig.h"
00020 #include "tv_rec.h"
00021 #include "osd.h"
00022 #include "mythcontext.h"
00023 #include "dialogbox.h"
00024 #include "recordingprofile.h"
00025 #include "util.h"
00026 #include "programinfo.h"
00027 #include "NuppelVideoPlayer.h"
00028 #include "dtvsignalmonitor.h"
00029 #include "mythdbcon.h"
00030 #include "jobqueue.h"
00031 #include "scheduledrecording.h"
00032 #include "eitscanner.h"
00033 #include "RingBuffer.h"
00034 #include "previewgenerator.h"
00035 #include "storagegroup.h"
00036 #include "remoteutil.h"
00037 
00038 #include "atscstreamdata.h"
00039 #include "dvbstreamdata.h"
00040 #include "atsctables.h"
00041 
00042 #include "livetvchain.h"
00043 
00044 #include "channelutil.h"
00045 #include "channelbase.h"
00046 #include "dummychannel.h"
00047 #include "dtvchannel.h"
00048 #include "dvbchannel.h"
00049 #include "dbox2channel.h"
00050 #include "hdhrchannel.h"
00051 #include "iptvchannel.h"
00052 #include "firewirechannel.h"
00053 
00054 #include "recorderbase.h"
00055 #include "NuppelVideoRecorder.h"
00056 #include "mpegrecorder.h"
00057 #include "dvbrecorder.h"
00058 #include "dbox2recorder.h"
00059 #include "hdhrrecorder.h"
00060 #include "iptvrecorder.h"
00061 #include "firewirerecorder.h"
00062 
00063 #ifdef USING_V4L
00064 #include "channel.h"
00065 #endif
00066 
00067 #define DEBUG_CHANNEL_PREFIX 0 
00069 #define LOC QString("TVRec(%1): ").arg(cardid)
00070 #define LOC_ERR QString("TVRec(%1) Error: ").arg(cardid)
00071 
00073 const uint TVRec::kSignalMonitoringRate = 50; /* msec */
00074 
00075 QMutex            TVRec::cardsLock;
00076 QMap<uint,TVRec*> TVRec::cards;
00077 
00078 static bool is_dishnet_eit(int cardid);
00079 static QString load_profile(QString,void*,ProgramInfo*,RecordingProfile&);
00080 
00105 TVRec::TVRec(int capturecardnum)
00106        // Various components TVRec coordinates
00107     : recorder(NULL), channel(NULL), signalMonitor(NULL),
00108       scanner(NULL),
00109       // Configuration variables from database
00110       eitIgnoresSource(false),      transcodeFirst(false),
00111       earlyCommFlag(false),         runJobOnHostOnly(false),
00112       eitCrawlIdleStart(60),        eitTransportTimeout(5*60),
00113       audioSampleRateDB(0),
00114       overRecordSecNrml(0),         overRecordSecCat(0),
00115       overRecordCategory(""),
00116       // Configuration variables from setup rutines
00117       cardid(capturecardnum), ispip(false),
00118       // State variables
00119       stateChangeLock(true),
00120       internalState(kState_None), desiredNextState(kState_None),
00121       changeState(false), pauseNotify(true),
00122       stateFlags(0), lastTuningRequest(0),
00123       m_switchingBuffer(false),
00124       // Current recording info
00125       curRecording(NULL), autoRunJobs(JOB_NONE),
00126       // Pseudo LiveTV recording
00127       pseudoLiveTVRecording(NULL),
00128       nextLiveTVDir(""),            nextLiveTVDirLock(false),
00129       // tvchain
00130       tvchain(NULL),
00131       // RingBuffer info
00132       ringBuffer(NULL), rbFileExt("mpg")
00133 {
00134     QMutexLocker locker(&cardsLock);
00135     cards[cardid] = this;
00136 }
00137 
00138 bool TVRec::CreateChannel(const QString &startchannel)
00139 {
00140     rbFileExt = "mpg";
00141     bool init_run = false;
00142     if (genOpt.cardtype == "DVB")
00143     {
00144 #ifdef USING_DVB
00145         channel = new DVBChannel(genOpt.videodev.toInt(), this);
00146         if (!channel->Open())
00147             return false;
00148         GetDVBChannel()->SetSlowTuning(dvbOpt.dvb_tuning_delay);
00149         InitChannel(genOpt.defaultinput, startchannel);
00150         CloseChannel(); // Close the channel if in dvb_on_demand mode
00151         init_run = true;
00152 #endif
00153     }
00154     else if (genOpt.cardtype == "FIREWIRE")
00155     {
00156 #ifdef USING_FIREWIRE
00157         channel = new FirewireChannel(this, genOpt.videodev, fwOpt);
00158         if (!channel->Open())
00159             return false;
00160         InitChannel(genOpt.defaultinput, startchannel);
00161         init_run = true;
00162 #endif
00163     }
00164     else if (genOpt.cardtype == "DBOX2")
00165     {
00166 #ifdef USING_DBOX2
00167         channel = new DBox2Channel(this, &dboxOpt, cardid);
00168         if (!channel->Open())
00169             return false;
00170         InitChannel(genOpt.defaultinput, startchannel);
00171         init_run = true;
00172 #endif
00173     }
00174     else if (genOpt.cardtype == "HDHOMERUN")
00175     {
00176 #ifdef USING_HDHOMERUN
00177         channel = new HDHRChannel(this, genOpt.videodev, dboxOpt.port);
00178         if (!channel->Open())
00179             return false;
00180         InitChannel(genOpt.defaultinput, startchannel);
00181         GetDTVChannel()->EnterPowerSavingMode();
00182         init_run = true;
00183 #endif
00184     }
00185     else if (genOpt.cardtype == "MPEG" &&
00186              genOpt.videodev.lower().left(5) == "file:")
00187     {
00188         channel = new DummyChannel(this);
00189         InitChannel(genOpt.defaultinput, startchannel);
00190         init_run = true;
00191     }
00192     else if (genOpt.cardtype == "FREEBOX")
00193     {
00194 #ifdef USING_IPTV
00195         channel = new IPTVChannel(this, genOpt.videodev);
00196         if (!channel->Open())
00197             return false;
00198         InitChannel(genOpt.defaultinput, startchannel);
00199         init_run = true;
00200 #endif
00201     }    
00202     else // "V4L" or "MPEG", ie, analog TV
00203     {
00204 #ifdef USING_V4L
00205         channel = new Channel(this, genOpt.videodev);
00206         if (!channel->Open())
00207             return false;
00208         InitChannel(genOpt.defaultinput, startchannel);
00209         CloseChannel();
00210         init_run = true;
00211 #endif
00212         if (genOpt.cardtype != "MPEG")
00213             rbFileExt = "nuv";
00214     }
00215 
00216     if (!init_run)
00217     {
00218         QString msg = QString(
00219             "%1 card configured on video device %2, \n"
00220             "but MythTV was not compiled with %2 support. \n"
00221             "\n"
00222             "Recompile MythTV with %3 support or remove the card \n"
00223             "from the configuration and restart MythTV.")
00224             .arg(genOpt.cardtype).arg(genOpt.videodev)
00225             .arg(genOpt.cardtype).arg(genOpt.cardtype);
00226         VERBOSE(VB_IMPORTANT, LOC_ERR + msg);
00227         SetFlags(kFlagErrored);
00228         return false;
00229     }
00230     return true;
00231 }
00232 
00238 bool TVRec::Init(void)
00239 {
00240     QMutexLocker lock(&stateChangeLock);
00241 
00242     if (!GetDevices(cardid, genOpt, dvbOpt, fwOpt, dboxOpt))
00243         return false;
00244 
00245     // configure the Channel instance
00246     QString startchannel = GetStartChannel(cardid, genOpt.defaultinput);
00247     if (!CreateChannel(startchannel))
00248         return false;
00249 
00250     eitIgnoresSource  = gContext->GetNumSetting("EITIgnoresSource", 0);
00251     transcodeFirst    =
00252         gContext->GetNumSetting("AutoTranscodeBeforeAutoCommflag", 0);
00253     earlyCommFlag     = gContext->GetNumSetting("AutoCommflagWhileRecording", 0);
00254     runJobOnHostOnly  = gContext->GetNumSetting("JobsRunOnRecordHost", 0);
00255     eitTransportTimeout=gContext->GetNumSetting("EITTransportTimeout", 5) * 60;
00256     eitCrawlIdleStart = gContext->GetNumSetting("EITCrawIdleStart", 60);
00257     audioSampleRateDB = gContext->GetNumSetting("AudioSampleRate");
00258     overRecordSecNrml = gContext->GetNumSetting("RecordOverTime");
00259     overRecordSecCat  = gContext->GetNumSetting("CategoryOverTime") * 60;
00260     overRecordCategory= gContext->GetSetting("OverTimeCategory");
00261 
00262     pthread_create(&event_thread, NULL, EventThread, this);
00263 
00264     WaitForEventThreadSleep();
00265 
00266     return true;
00267 }
00268 
00273 TVRec::~TVRec()
00274 {
00275     QMutexLocker locker(&cardsLock);
00276     cards.erase(cardid);
00277     TeardownAll();
00278 }
00279 
00280 void TVRec::deleteLater(void)
00281 {
00282     TeardownAll();
00283     QObject::deleteLater();
00284 }
00285 
00286 void TVRec::TeardownAll(void)
00287 {
00288     if (HasFlags(kFlagRunMainLoop))
00289     {
00290         ClearFlags(kFlagRunMainLoop);
00291         pthread_join(event_thread, NULL);
00292     }
00293 
00294     TeardownSignalMonitor();
00295 
00296     if (scanner)
00297     {
00298         delete scanner;
00299         scanner = NULL;
00300     }
00301 
00302     if (channel)
00303     {
00304         delete channel;
00305         channel = NULL;
00306     }
00307 
00308     TeardownRecorder(true);
00309 
00310     SetRingBuffer(NULL);
00311 }
00312 
00319 TVState TVRec::GetState(void) const
00320 {
00321     if (changeState)
00322         return kState_ChangingState;
00323     return internalState;
00324 }
00325 
00333 ProgramInfo *TVRec::GetRecording(void)
00334 {
00335     QMutexLocker lock(&stateChangeLock);
00336 
00337     ProgramInfo *tmppginfo = NULL;
00338 
00339     if (curRecording && !changeState)
00340     {
00341         tmppginfo = new ProgramInfo(*curRecording);
00342         tmppginfo->recstatus = rsRecording;
00343     }
00344     else
00345         tmppginfo = new ProgramInfo();
00346     tmppginfo->cardid = cardid;
00347 
00348     return tmppginfo;
00349 }
00350 
00365 void TVRec::RecordPending(const ProgramInfo *rcinfo, int secsleft,
00366                           bool hasLater)
00367 {
00368     QMutexLocker lock(&stateChangeLock);
00369 
00370     if (secsleft < 0)
00371     {
00372         VERBOSE(VB_RECORD, LOC + "Pending recording revoked on " +
00373                 QString("inputid %1").arg(rcinfo->inputid));
00374 
00375         PendingMap::iterator it = pendingRecordings.find(rcinfo->cardid);
00376         if (it != pendingRecordings.end())
00377         {
00378             (*it).ask = false;
00379             (*it).doNotAsk = (*it).canceled = true;
00380         }
00381         return;
00382     }
00383 
00384     VERBOSE(VB_RECORD, LOC +
00385             QString("RecordPending on inputid %1").arg(rcinfo->inputid));
00386 
00387     PendingInfo pending;
00388     pending.info            = new ProgramInfo(*rcinfo);
00389     pending.recordingStart  = QDateTime::currentDateTime().addSecs(secsleft);
00390     pending.hasLaterShowing = hasLater;
00391     pending.ask             = true;
00392     pending.doNotAsk        = false;
00393 
00394     pendingRecordings[rcinfo->cardid] = pending;
00395 
00396     // If this isn't a recording for this instance to make, we are done
00397     if (rcinfo->cardid != cardid)
00398         return;
00399 
00400     // We also need to check our input groups
00401     vector<uint> cardids = CardUtil::GetConflictingCards(
00402         rcinfo->inputid, cardid);
00403 
00404     pendingRecordings[rcinfo->cardid].possibleConflicts = cardids;
00405 
00406     stateChangeLock.unlock();
00407     for (uint i = 0; i < cardids.size(); i++)
00408         RemoteRecordPending(cardids[i], rcinfo, secsleft, hasLater);
00409     stateChangeLock.lock();
00410 }
00411 
00415 void TVRec::SetPseudoLiveTVRecording(ProgramInfo *pi)
00416 {
00417     ProgramInfo *old_rec = pseudoLiveTVRecording;
00418     pseudoLiveTVRecording = pi;
00419     if (old_rec)
00420         delete old_rec;
00421 }
00422 
00426 QDateTime TVRec::GetRecordEndTime(const ProgramInfo *pi) const
00427 {
00428     bool spcat = (pi->category == overRecordCategory);
00429     int secs = (spcat) ? overRecordSecCat : overRecordSecNrml;
00430     return pi->recendts.addSecs(secs);
00431 }
00432 
00438 void TVRec::CancelNextRecording(bool cancel)
00439 {
00440     VERBOSE(VB_RECORD, LOC + "CancelNextRecording("<<cancel<<") -- begin");
00441 
00442     PendingMap::iterator it = pendingRecordings.find(cardid);
00443     if (it == pendingRecordings.end())
00444     {
00445         VERBOSE(VB_RECORD, LOC + "CancelNextRecording("<<cancel<<") -- "
00446                 "error, unknown recording");
00447         return;
00448     }
00449     
00450     if (cancel)
00451     {
00452         vector<uint> &cardids = (*it).possibleConflicts;
00453         for (uint i = 0; i < cardids.size(); i++)
00454         {
00455             VERBOSE(VB_RECORD, LOC +
00456                     "CancelNextRecording -- cardid "<<cardids[i]);
00457 
00458             RemoteRecordPending(cardids[i], (*it).info, -1, false);
00459         }
00460 
00461         VERBOSE(VB_RECORD, LOC + "CancelNextRecording -- cardid "<<cardid);
00462 
00463         RecordPending((*it).info, -1, false);
00464     }
00465     else
00466     {
00467         (*it).canceled = false;
00468     }
00469 
00470     VERBOSE(VB_RECORD, LOC + "CancelNextRecording("<<cancel<<") -- end");
00471 }
00472 
00482 RecStatusType TVRec::StartRecording(const ProgramInfo *rcinfo)
00483 {
00484     VERBOSE(VB_RECORD, LOC + QString("StartRecording(%1)").arg(rcinfo->title));
00485 
00486     QMutexLocker lock(&stateChangeLock);
00487     QString msg("");
00488 
00489     RecStatusType retval = rsAborted;
00490 
00491     // Flush out any pending state changes
00492     WaitForEventThreadSleep();
00493 
00494     // We need to do this check early so we don't cancel an overrecord
00495     // that we're trying to extend.
00496     if (internalState != kState_WatchingLiveTV && 
00497         curRecording &&
00498         curRecording->title == rcinfo->title &&
00499         curRecording->chanid == rcinfo->chanid &&
00500         curRecording->startts == rcinfo->startts)
00501     {
00502         int post_roll_seconds  = curRecording->recendts.secsTo(recordEndTime);
00503         curRecording->rectype  = rcinfo->rectype;
00504         curRecording->recordid = rcinfo->recordid;
00505         curRecording->recendts = rcinfo->recendts;
00506         curRecording->UpdateRecordingEnd();
00507         MythEvent me("RECORDING_LIST_CHANGE");
00508         gContext->dispatch(me);
00509 
00510         recordEndTime = curRecording->recendts.addSecs(post_roll_seconds);
00511 
00512         msg = QString("updating recording: %1 %2 %3 %4")
00513             .arg(curRecording->title).arg(curRecording->chanid)
00514             .arg(curRecording->recstartts.toString())
00515             .arg(curRecording->recendts.toString());
00516         VERBOSE(VB_RECORD, LOC + msg);
00517 
00518         ClearFlags(kFlagCancelNextRecording);
00519 
00520         retval = rsRecording;
00521         return retval;
00522     }
00523 
00524     PendingMap::iterator it = pendingRecordings.find(cardid);
00525     bool cancelNext = false;
00526     if (it != pendingRecordings.end())
00527     {
00528         (*it).ask = (*it).doNotAsk = false;
00529         cancelNext = (*it).canceled;
00530     }
00531 
00532     // Flush out events...
00533     WaitForEventThreadSleep();
00534 
00535     // If the needed input is in a shared input group, and we are
00536     // not canceling the recording anyway, check other recorders
00537     if (!cancelNext &&
00538         (it != pendingRecordings.end()) && (*it).possibleConflicts.size())
00539     {
00540         VERBOSE(VB_RECORD, LOC + "Checking input group recorders - begin");
00541         vector<uint> &cardids = (*it).possibleConflicts;
00542 
00543         uint mplexid = 0, sourceid = 0;
00544         vector<uint> cardids2;
00545         vector<TVState> states;
00546 
00547         // Stop remote recordings if needed
00548         for (uint i = 0; i < cardids.size(); i++)
00549         {
00550             TunedInputInfo busy_input;
00551             bool is_busy = RemoteIsBusy(cardids[i], busy_input);
00552 
00553             // if the other recorder is busy, but the input is
00554             // not in a shared input group, then as far as we're
00555             // concerned here it isn't busy.
00556             if (is_busy)
00557             {
00558                 is_busy = (bool) igrp.GetSharedInputGroup(
00559                     busy_input.inputid, rcinfo->inputid);
00560             }
00561 
00562             if (is_busy && !sourceid)
00563             {
00564                 mplexid  = (*it).info->GetMplexID();
00565                 sourceid = (*it).info->sourceid;
00566             }
00567 
00568             if (is_busy &&
00569                 ((sourceid != busy_input.sourceid) ||
00570                  (mplexid  != busy_input.mplexid)))
00571             {
00572                 states.push_back((TVState) RemoteGetState(cardids[i]));
00573                 cardids2.push_back(cardids[i]);
00574             }
00575         }
00576 
00577         bool ok = true;
00578         for (uint i = 0; (i < cardids2.size()) && ok; i++)
00579         {
00580             VERBOSE(VB_RECORD, LOC +
00581                     QString("Attempting to stop card %1 in state %2")
00582                     .arg(cardids2[i]).arg(StateToString(states[i])));
00583 
00584             bool success = RemoteStopRecording(cardids2[i]);
00585             if (success)
00586             {
00587                 uint state = RemoteGetState(cardids2[i]);
00588                 VERBOSE(VB_IMPORTANT, LOC + QString("a %1: %2")
00589                         .arg(cardids2[i]).arg(StateToString((TVState)state)));
00590                 success = (kState_None == state);
00591             }
00592 
00593             // If we managed to stop LiveTV recording, restart playback..
00594             if (success && states[i] == kState_WatchingLiveTV)
00595             {
00596                 QString message = QString("QUIT_LIVETV %1").arg(cardids2[i]);
00597                 MythEvent me(message);
00598                 gContext->dispatch(me);
00599             }
00600 
00601             VERBOSE(VB_RECORD, LOC + QString(
00602                         "Stopping recording on %1, %2")
00603                     .arg(cardids2[i])
00604                     .arg(success ? "succeeded" : "failed"));
00605 
00606             ok &= success;
00607         }
00608 
00609         // If we failed to stop the remote recordings, don't record
00610         if (!ok)
00611         {
00612             CancelNextRecording(true);
00613             cancelNext = true;
00614         }
00615 
00616         cardids.clear();
00617 
00618         VERBOSE(VB_RECORD, LOC + "Checking input group recorders - done");
00619     }
00620 
00621     // If in post-roll, end recording
00622     if (!cancelNext && (GetState() == kState_RecordingOnly))
00623     {
00624         stateChangeLock.unlock();
00625         StopRecording();
00626         stateChangeLock.lock();
00627     }
00628 
00629     if (!cancelNext && (GetState() == kState_None))
00630     {
00631         if (tvchain)
00632         {
00633             QString message = QString("LIVETV_EXITED");
00634             MythEvent me(message, tvchain->GetID());
00635             gContext->dispatch(me);
00636             tvchain = NULL;
00637         }
00638 
00639         recordEndTime = GetRecordEndTime(rcinfo);
00640 
00641         // Tell event loop to begin recording.
00642         curRecording = new ProgramInfo(*rcinfo);
00643         curRecording->MarkAsInUse(true, "recorder");
00644         StartedRecording(curRecording);
00645 
00646         // Make sure scheduler is allowed to end this recording
00647         ClearFlags(kFlagCancelNextRecording);
00648 
00649         ChangeState(kState_RecordingOnly);
00650 
00651         retval = rsRecording;
00652     }
00653     else if (!cancelNext && (GetState() == kState_WatchingLiveTV))
00654     {
00655         SetPseudoLiveTVRecording(new ProgramInfo(*rcinfo));
00656         recordEndTime = GetRecordEndTime(rcinfo);
00657 
00658         // We want the frontend to to change channel for recording
00659         // and disable the UI for channel change, PiP, etc.
00660 
00661         QString message = QString("LIVETV_WATCH %1 1").arg(cardid);
00662         QStringList prog;
00663         rcinfo->ToStringList(prog);
00664         MythEvent me(message, prog);
00665         gContext->dispatch(me);
00666 
00667         retval = rsRecording;
00668     }
00669     else
00670     {
00671         msg = QString("Wanted to record: %1 %2 %3 %4\n\t\t\t")
00672             .arg(rcinfo->title).arg(rcinfo->chanid)
00673             .arg(rcinfo->recstartts.toString())
00674             .arg(rcinfo->recendts.toString());
00675 
00676         if (cancelNext)
00677         {
00678             msg += "But a user has canceled this recording";
00679             retval = rsCancelled;
00680         }
00681         else
00682         {
00683             msg += QString("But the current state is: %1")
00684                 .arg(StateToString(internalState));
00685             retval = rsTunerBusy;
00686         }
00687 
00688         if (curRecording && internalState == kState_RecordingOnly)
00689             msg += QString("\n\t\t\tCurrently recording: %1 %2 %3 %4")
00690                 .arg(curRecording->title).arg(curRecording->chanid)
00691                 .arg(curRecording->recstartts.toString())
00692                 .arg(curRecording->recendts.toString());
00693 
00694         VERBOSE(VB_IMPORTANT, LOC + msg);
00695     }
00696 
00697     for (uint i = 0; i < pendingRecordings.size(); i++)
00698         delete pendingRecordings[i].info;
00699     pendingRecordings.clear();
00700 
00701     WaitForEventThreadSleep();
00702 
00703     if ((curRecording) && (curRecording->recstatus == rsFailed) &&
00704         (retval == rsRecording))
00705         retval = rsFailed;
00706 
00707     return retval;
00708 }
00709 
00714 void TVRec::StopRecording(void)
00715 {
00716     if (StateIsRecording(GetState()))
00717     {
00718         QMutexLocker lock(&stateChangeLock);
00719         ChangeState(RemoveRecording(GetState()));
00720         // wait for state change to take effect
00721         WaitForEventThreadSleep();
00722         ClearFlags(kFlagCancelNextRecording);
00723     }
00724 }
00725 
00731 bool TVRec::StateIsRecording(TVState state)
00732 {
00733     return (state == kState_RecordingOnly ||
00734             state == kState_WatchingLiveTV);
00735 }
00736 
00741 bool TVRec::StateIsPlaying(TVState state)
00742 {
00743     return (state == kState_WatchingPreRecorded);
00744 }
00745 
00751 TVState TVRec::RemoveRecording(TVState state)
00752 {
00753     if (StateIsRecording(state))
00754         return kState_None;
00755 
00756     VERBOSE(VB_IMPORTANT, LOC_ERR +
00757             QString("Unknown state in RemoveRecording: %1")
00758             .arg(StateToString(state)));
00759     return kState_Error;
00760 }
00761 
00767 TVState TVRec::RemovePlaying(TVState state)
00768 {
00769     if (StateIsPlaying(state))
00770     {
00771         if (state == kState_WatchingPreRecorded)
00772             return kState_None;
00773         return kState_RecordingOnly;
00774     }
00775 
00776     QString msg = "Unknown state in RemovePlaying: %1";
00777     VERBOSE(VB_IMPORTANT, LOC_ERR + msg.arg(StateToString(state)));
00778 
00779     return kState_Error;
00780 }
00781 
00788 void TVRec::StartedRecording(ProgramInfo *curRec)
00789 {
00790     if (!curRec)
00791         return;
00792 
00793     curRec->StartedRecording(rbFileExt);
00794     VERBOSE(VB_RECORD, LOC + "StartedRecording("<<curRec<<") fn("
00795             <<curRec->GetFileName()<<")");
00796 
00797     if (curRec->chancommfree != 0)
00798         curRec->SetCommFlagged(COMM_FLAG_COMMFREE);
00799 
00800     MythEvent me("RECORDING_LIST_CHANGE");
00801     gContext->dispatch(me);
00802 }
00803 
00811 void TVRec::FinishedRecording(ProgramInfo *curRec)
00812 {
00813     if (!curRec)
00814         return;
00815 
00816     ProgramInfo *pi = NULL;
00817 
00818     QString pigrp = curRec->recgroup;
00819 
00820     pi = ProgramInfo::GetProgramFromRecorded(curRec->chanid,
00821                                              curRec->recstartts);
00822     if (pi)
00823     {
00824         pigrp = pi->recgroup;
00825         delete pi;
00826     }
00827     VERBOSE(VB_RECORD, LOC + QString("FinishedRecording(%1) in recgroup: %2")
00828                                      .arg(curRec->title).arg(pigrp));
00829 
00830     if (curRec->recstatus != rsFailed)
00831         curRec->recstatus = rsRecorded;
00832     curRec->recendts = mythCurrentDateTime();
00833 
00834     if (tvchain)
00835         tvchain->FinishedRecording(curRec);
00836 
00837     // Make sure really short recordings have positive run time.
00838     if (curRec->recendts <= curRec->recstartts)
00839         curRec->recendts = curRec->recstartts.addSecs(60);
00840 
00841     curRec->recendts.setTime(QTime(
00842         curRec->recendts.addSecs(30).time().hour(),
00843         curRec->recendts.addSecs(30).time().minute()));
00844 
00845     if (pigrp != "LiveTV")
00846     {
00847         MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
00848                      .arg(curRec->cardid)
00849                      .arg(curRec->chanid)
00850                      .arg(curRec->startts.toString(Qt::ISODate))
00851                      .arg(curRec->recstatus)
00852                      .arg(curRec->recendts.toString(Qt::ISODate)));
00853         gContext->dispatch(me);
00854     }
00855 
00856     curRec->FinishedRecording(curRec->recstatus != rsRecorded);
00857 }
00858 
00859 #define TRANSITION(ASTATE,BSTATE) \
00860    ((internalState == ASTATE) && (desiredNextState == BSTATE))
00861 #define SET_NEXT() do { nextState = desiredNextState; changed = true; } while(0)
00862 #define SET_LAST() do { nextState = internalState; changed = true; } while(0)
00863 
00871 void TVRec::HandleStateChange(void)
00872 {
00873     TVState nextState = internalState;
00874 
00875     bool changed = false;
00876 
00877     QString transMsg = QString(" %1 to %2")
00878         .arg(StateToString(nextState))
00879         .arg(StateToString(desiredNextState));
00880 
00881     if (desiredNextState == internalState)
00882     {
00883         VERBOSE(VB_IMPORTANT, LOC_ERR + "HandleStateChange(): "
00884                 "Null transition" + transMsg);
00885         changeState = false;
00886         return;
00887     }
00888 
00889     // Make sure EIT scan is stopped before any tuning,
00890     // to avoid race condition with it's tuning requests.
00891     if (HasFlags(kFlagEITScannerRunning))
00892     {
00893         scanner->StopActiveScan();
00894         ClearFlags(kFlagEITScannerRunning);
00895     }
00896 
00897     // Handle different state transitions
00898     if (TRANSITION(kState_None, kState_WatchingLiveTV))
00899     {
00900         tuningRequests.enqueue(TuningRequest(kFlagLiveTV));
00901         SET_NEXT();
00902     }
00903     else if (TRANSITION(kState_WatchingLiveTV, kState_None))
00904     {
00905         tuningRequests.enqueue(TuningRequest(kFlagKillRec|kFlagKillRingBuffer));
00906         SET_NEXT();
00907     }
00908     else if (TRANSITION(kState_WatchingLiveTV, kState_RecordingOnly))
00909     {
00910         SetPseudoLiveTVRecording(NULL);
00911 
00912         SET_NEXT();
00913     }
00914     else if (TRANSITION(kState_None, kState_RecordingOnly))
00915     {
00916         SetPseudoLiveTVRecording(NULL);
00917         tuningRequests.enqueue(TuningRequest(kFlagRecording, curRecording));
00918         SET_NEXT();
00919     }
00920     else if (TRANSITION(kState_RecordingOnly, kState_None))
00921     {
00922         tuningRequests.enqueue(
00923             TuningRequest(kFlagCloseRec|kFlagKillRingBuffer));
00924         SET_NEXT();
00925     }
00926 
00927     QString msg = (changed) ? "Changing from" : "Unknown state transition:";
00928     VERBOSE(VB_IMPORTANT, LOC + msg + transMsg);
00929  
00930     // update internal state variable
00931     internalState = nextState;
00932     changeState = false;
00933 
00934     eitScanStartTime = QDateTime::currentDateTime();    
00935     if ((internalState == kState_None) &&
00936         scanner)
00937         eitScanStartTime = eitScanStartTime.addSecs(eitCrawlIdleStart);
00938     else
00939         eitScanStartTime = eitScanStartTime.addYears(1);
00940 }
00941 #undef TRANSITION
00942 #undef SET_NEXT
00943 #undef SET_LAST
00944 
00948 void TVRec::ChangeState(TVState nextState)
00949 {
00950     QMutexLocker lock(&stateChangeLock);
00951 
00952     desiredNextState = nextState;
00953     changeState = true;
00954     triggerEventLoop.wakeAll();
00955 }
00956 
00970 bool TVRec::SetupRecorder(RecordingProfile &profile)
00971 {
00972     recorder = NULL;
00973     if (genOpt.cardtype == "MPEG")
00974     {
00975 #ifdef USING_IVTV
00976         recorder = new MpegRecorder(this);
00977 #endif // USING_IVTV
00978     }
00979     else if (genOpt.cardtype == "FIREWIRE")
00980     {
00981 #ifdef USING_FIREWIRE
00982         recorder = new FirewireRecorder(this, GetFirewireChannel());
00983 #endif // USING_FIREWIRE
00984     }
00985     else if (genOpt.cardtype == "DBOX2")
00986     {
00987 #ifdef USING_DBOX2
00988         recorder = new DBox2Recorder(this, GetDBox2Channel());
00989         recorder->SetOption("port",     dboxOpt.port);
00990         recorder->SetOption("host",     dboxOpt.host);
00991         recorder->SetOption("httpport", dboxOpt.httpport);
00992 #endif // USING_DBOX2
00993     }
00994     else if (genOpt.cardtype == "HDHOMERUN")
00995     {
00996 #ifdef USING_HDHOMERUN
00997         recorder = new HDHRRecorder(this, GetHDHRChannel());
00998         ringBuffer->SetWriteBufferSize(4*1024*1024);
00999         recorder->SetOption("wait_for_seqstart", genOpt.wait_for_seqstart);
01000 #endif // USING_HDHOMERUN
01001     }
01002     else if (genOpt.cardtype == "DVB")
01003     {
01004 #ifdef USING_DVB
01005         recorder = new DVBRecorder(this, GetDVBChannel());
01006         ringBuffer->SetWriteBufferSize(4*1024*1024);
01007         recorder->SetOption("wait_for_seqstart", genOpt.wait_for_seqstart);
01008         recorder->SetOption("dvb_on_demand",     dvbOpt.dvb_on_demand);
01009 #endif // USING_DVB
01010     }
01011     else if (genOpt.cardtype == "FREEBOX")
01012     {
01013 #ifdef USING_IPTV
01014         IPTVChannel *chan = dynamic_cast<IPTVChannel*>(channel);
01015         recorder = new IPTVRecorder(this, chan);
01016         ringBuffer->SetWriteBufferSize(4*1024*1024);
01017         recorder->SetOption("mrl", genOpt.videodev);
01018 #endif // USING_IPTV
01019     }
01020     else
01021     {
01022 #ifdef USING_V4L
01023         // V4L/MJPEG/GO7007 from here on
01024         recorder = new NuppelVideoRecorder(this, channel);
01025         recorder->SetOption("skipbtaudio", genOpt.skip_btaudio);
01026 #endif // USING_V4L
01027     }
01028 
01029     if (recorder)
01030     {
01031         recorder->SetOptionsFromProfile(
01032             &profile, genOpt.videodev, genOpt.audiodev, genOpt.vbidev);
01033         recorder->SetRingBuffer(ringBuffer);
01034         recorder->Initialize();
01035 
01036         if (recorder->IsErrored())
01037         {
01038             VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to initialize recorder!");
01039             delete recorder;
01040             recorder = NULL;
01041             return false;
01042         }
01043 
01044         return true;
01045     }
01046 
01047     QString msg = "Need %1 recorder, but compiled without %2 support!";
01048     msg = msg.arg(genOpt.cardtype).arg(genOpt.cardtype);
01049     VERBOSE(VB_IMPORTANT, LOC_ERR + msg);
01050 
01051     return false;
01052 }
01053 
01073 void TVRec::TeardownRecorder(bool killFile)
01074 {
01075     pauseNotify = false;
01076     ispip = false;
01077 
01078     if (recorder && HasFlags(kFlagRecorderRunning))
01079     {
01080         int secsSince = curRecording->recstartts
01081             .secsTo(QDateTime::currentDateTime());
01082         QString message = QString("DONE_RECORDING %1 %2 %3")
01083             .arg(cardid).arg(secsSince).arg(GetFramesWritten());
01084         MythEvent me(message);
01085         gContext->dispatch(me);
01086 
01087         recorder->StopRecording();
01088         pthread_join(recorder_thread, NULL);
01089     }
01090     ClearFlags(kFlagRecorderRunning);
01091 
01092     if (recorder)
01093     {
01094         if (GetV4LChannel())
01095             channel->SetFd(-1);
01096 
01097         delete recorder;
01098         recorder = NULL;
01099     }
01100 
01101     if (ringBuffer)
01102         ringBuffer->StopReads();
01103 
01104     if (curRecording)
01105     {
01106         if (!killFile)
01107         {
01108             (new PreviewGenerator(curRecording, true))->Start();
01109 
01110             if (!tvchain)
01111             {
01112                 int secsSince = curRecording->recstartts.secsTo(
01113                                                   QDateTime::currentDateTime());
01114                 if (secsSince < 120)
01115                 {
01116                     JobQueue::RemoveJobsFromMask(JOB_COMMFLAG, autoRunJobs);
01117                     JobQueue::RemoveJobsFromMask(JOB_TRANSCODE, autoRunJobs);
01118                 }
01119 
01120                 if (autoRunJobs)
01121                     JobQueue::QueueRecordingJobs(curRecording, autoRunJobs);
01122             }
01123         }
01124 
01125         FinishedRecording(curRecording);
01126 
01127         curRecording->MarkAsInUse(false);
01128         delete curRecording;
01129         curRecording = NULL;
01130     }
01131 
01132     MythEvent me("RECORDING_LIST_CHANGE");
01133     gContext->dispatch(me);
01134     pauseNotify = true;
01135 
01136     if (GetDTVChannel())
01137         GetDTVChannel()->EnterPowerSavingMode();
01138 }
01139 
01140 DVBRecorder *TVRec::GetDVBRecorder(void)
01141 {
01142 #ifdef USING_DVB
01143     return dynamic_cast<DVBRecorder*>(recorder);
01144 #else // if !USING_DVB
01145     return NULL;
01146 #endif // !USING_DVB
01147 }
01148 
01149 HDHRRecorder *TVRec::GetHDHRRecorder(void)
01150 {
01151 #ifdef USING_HDHOMERUN
01152     return dynamic_cast<HDHRRecorder*>(recorder);
01153 #else // if !USING_HDHOMERUN
01154     return NULL;
01155 #endif // !USING_HDHOMERUN
01156 }
01157 
01158 DTVRecorder *TVRec::GetDTVRecorder(void)
01159 {
01160     return dynamic_cast<DTVRecorder*>(recorder);
01161 }
01162 
01167 void TVRec::InitChannel(const QString &inputname, const QString &startchannel)
01168 {
01169     if (!channel)
01170         return;
01171 
01172     QString input   = inputname;
01173     QString channum = startchannel;
01174 
01175     channel->Init(input, channum, true);
01176 }
01177 
01178 void TVRec::CloseChannel(void)
01179 {
01180     if (!channel)
01181         return;
01182 
01183     if (GetDVBChannel() && !dvbOpt.dvb_on_demand)
01184         return;
01185 
01186     channel->Close();
01187 }
01188 
01189 DBox2Channel *TVRec::GetDBox2Channel(void)
01190 {
01191 #ifdef USING_DBOX2
01192     return dynamic_cast<DBox2Channel*>(channel);
01193 #else
01194     return NULL;
01195 #endif // USING_DBOX2
01196 }
01197 
01198 DTVChannel *TVRec::GetDTVChannel(void)
01199 {
01200     return dynamic_cast<DTVChannel*>(channel);
01201 }
01202 
01203 HDHRChannel *TVRec::GetHDHRChannel(void)
01204 {
01205 #ifdef USING_HDHOMERUN
01206     return dynamic_cast<HDHRChannel*>(channel);
01207 #else
01208     return NULL;
01209 #endif // USING_HDHOMERUN
01210 }
01211 
01212 DVBChannel *TVRec::GetDVBChannel(void)
01213 {
01214 #ifdef USING_DVB
01215     return dynamic_cast<DVBChannel*>(channel);
01216 #else
01217     return NULL;
01218 #endif // USING_DVB
01219 }
01220 
01221 FirewireChannel *TVRec::GetFirewireChannel(void)
01222 {
01223 #ifdef USING_FIREWIRE
01224     return dynamic_cast<FirewireChannel*>(channel);
01225 #else
01226     return NULL;
01227 #endif // USING_FIREWIRE
01228 }
01229 
01230 Channel *TVRec::GetV4LChannel(void)
01231 {
01232 #ifdef USING_V4L
01233     return dynamic_cast<Channel*>(channel);
01234 #else
01235     return NULL;
01236 #endif // USING_V4L
01237 }
01238 
01242 void *TVRec::EventThread(void *param)
01243 {
01244     TVRec *thetv = (TVRec *)param;
01245     thetv->RunTV();
01246     return NULL;
01247 }
01248 
01253 void *TVRec::RecorderThread(void *param)
01254 {
01255     RecorderBase *recorder = (RecorderBase *)param;
01256     recorder->StartRecording();
01257     return NULL;
01258 }
01259 
01260 bool get_use_eit(uint cardid)
01261 {
01262     MSqlQuery query(MSqlQuery::InitCon());
01263     query.prepare(
01264         "SELECT SUM(useeit) "
01265         "FROM videosource, cardinput "
01266         "WHERE videosource.sourceid = cardinput.sourceid AND"
01267         "      cardinput.cardid     = :CARDID");
01268     query.bindValue(":CARDID", cardid);
01269 
01270     if (!query.exec() || !query.isActive())
01271     {
01272         MythContext::DBError("get_use_eit", query);
01273         return false;
01274     }
01275     else if (query.next())
01276         return query.value(0).toBool();
01277     return false;
01278 }
01279 
01280 static bool is_dishnet_eit(int cardid)
01281 {
01282     MSqlQuery query(MSqlQuery::InitCon());
01283     query.prepare(
01284         "SELECT SUM(dishnet_eit) "
01285         "FROM videosource, cardinput "
01286         "WHERE videosource.sourceid = cardinput.sourceid AND"
01287         "      cardinput.cardid     = :CARDID");
01288     query.bindValue(":CARDID", cardid);
01289 
01290     if (!query.exec() || !query.isActive())
01291     {
01292         MythContext::DBError("is_dishnet_eit", query);
01293         return false;
01294     }
01295     else if (query.next())
01296         return query.value(0).toBool();
01297     return false;
01298 }
01299 
01303 void TVRec::RunTV(void)
01304 { 
01305     QMutexLocker lock(&stateChangeLock);
01306     SetFlags(kFlagRunMainLoop);
01307     ClearFlags(kFlagExitPlayer | kFlagFinishRecording);
01308 
01309     eitScanStartTime = QDateTime::currentDateTime();    
01310     // check whether we should use the EITScanner in this TVRec instance
01311     if (CardUtil::IsEITCapable(genOpt.cardtype) &&
01312         (!GetDVBChannel() || GetDVBChannel()->IsMaster()))
01313     {
01314         scanner = new EITScanner(cardid);
01315         // Wait at least 15 seconds between starting EIT scanning
01316         // on distinct cards
01317         uint timeout = eitCrawlIdleStart + cardid * 15;
01318         eitScanStartTime = eitScanStartTime.addSecs(timeout);
01319     }
01320     else
01321         eitScanStartTime = eitScanStartTime.addYears(1);
01322 
01323     while (HasFlags(kFlagRunMainLoop))
01324     {
01325         // If there is a state change queued up, do it...
01326         if (changeState)
01327         {
01328             HandleStateChange();
01329             ClearFlags(kFlagFrontendReady | kFlagCancelNextRecording);
01330         }
01331 
01332         // Quick exit on fatal errors.
01333         if (IsErrored())
01334         {
01335             VERBOSE(VB_IMPORTANT, LOC_ERR +
01336                     "RunTV encountered fatal error, exiting event thread.");
01337             ClearFlags(kFlagRunMainLoop);
01338             return;
01339         }
01340 
01341         // Handle any tuning events..
01342         HandleTuning();
01343 
01344         // Tell frontends about pending recordings
01345         HandlePendingRecordings();
01346 
01347         // If we are recording a program, check if the recording is
01348         // over or someone has asked us to finish the recording.
01349         if (GetState() == kState_RecordingOnly &&
01350             (QDateTime::currentDateTime() > recordEndTime ||
01351              HasFlags(kFlagFinishRecording)))
01352         {
01353             ChangeState(kState_None);
01354             ClearFlags(kFlagFinishRecording);
01355         }
01356 
01357         if (curRecording)
01358         {
01359             curRecording->UpdateInUseMark();
01360 
01361             if (recorder)
01362                 recorder->SavePositionMap();
01363         }
01364 
01365         // Check for the end of the current program..
01366         if (GetState() == kState_WatchingLiveTV)
01367         {
01368             QDateTime now   = QDateTime::currentDateTime();
01369             bool has_finish = HasFlags(kFlagFinishRecording);
01370             bool has_rec    = pseudoLiveTVRecording;
01371             bool rec_soon   =
01372                 pendingRecordings.find(cardid) != pendingRecordings.end();
01373             bool enable_ui  = true;
01374 
01375             if (has_rec && (has_finish || (now > recordEndTime)))
01376             {
01377                 if (pseudoLiveTVRecording && curRecording)
01378                 {
01379                     int secsSince = curRecording->recstartts.secsTo(
01380                                         QDateTime::currentDateTime());
01381                     if (secsSince < 120)
01382                     {
01383                         JobQueue::RemoveJobsFromMask(JOB_COMMFLAG,
01384                             autoRunJobs);
01385                         JobQueue::RemoveJobsFromMask(JOB_TRANSCODE,
01386                             autoRunJobs);
01387                     }
01388 
01389                     if (autoRunJobs)
01390                         JobQueue::QueueRecordingJobs(curRecording,
01391                             autoRunJobs);
01392                 }
01393 
01394                 SetPseudoLiveTVRecording(NULL);
01395             }
01396             else if (!has_rec && !rec_soon && curRecording &&
01397                      (now >= curRecording->endts))
01398             {
01399                 if (!m_switchingBuffer)
01400                 {
01401                     m_switchingBuffer = true;
01402 
01403                     SwitchLiveTVRingBuffer(false, true);
01404 
01405                     QDateTime starttime; starttime.setTime_t(0);
01406                     if (curRecording)
01407                         starttime = curRecording->recstartts;
01408 
01409                     VERBOSE(VB_RECORD, LOC 
01410                             <<"!has_rec("<<!has_rec<<") "
01411                             <<"!rec_soon("<<!rec_soon<<") "
01412                             <<"curRec("<<curRecording<<") "
01413                             <<"starttm("
01414                             <<starttime.toString(Qt::ISODate)<<")");
01415                 }
01416                 else
01417                 {
01418                     VERBOSE(VB_RECORD, "Waiting for ringbuffer switch");
01419                 }
01420             }
01421             else
01422                 enable_ui = false;
01423 
01424             if (enable_ui)
01425             {
01426                 VERBOSE(VB_RECORD, LOC + "Enabling Full LiveTV UI.");
01427                 QString message = QString("LIVETV_WATCH %1 0").arg(cardid);
01428                 MythEvent me(message);
01429                 gContext->dispatch(me);
01430             }   
01431         }
01432 
01433         // Check for ExitPlayer flag, and if set change to a non-watching
01434         // state (either kState_RecordingOnly or kState_None).
01435         if (HasFlags(kFlagExitPlayer))
01436         {
01437             if (internalState == kState_WatchingLiveTV)
01438                 ChangeState(kState_None);
01439             else if (StateIsPlaying(internalState))
01440                 ChangeState(RemovePlaying(internalState));
01441             ClearFlags(kFlagExitPlayer);
01442         }
01443 
01444         if (channel && scanner &&
01445             QDateTime::currentDateTime() > eitScanStartTime)
01446         {
01447             if (!dvbOpt.dvb_eitscan)
01448             {
01449                 VERBOSE(VB_EIT, LOC + "EIT scanning disabled for this card.");
01450                 eitScanStartTime = eitScanStartTime.addYears(1);
01451             }
01452             else if (!get_use_eit(GetCaptureCardNum()))
01453             {
01454                 VERBOSE(VB_EIT, LOC + "EIT scanning disabled "
01455                         "for all sources on this card.");
01456                 eitScanStartTime = eitScanStartTime.addYears(1);
01457             }
01458             else
01459             {
01460                 scanner->StartActiveScan(
01461                     this, eitTransportTimeout, eitIgnoresSource);
01462                 SetFlags(kFlagEITScannerRunning);
01463                 eitScanStartTime = QDateTime::currentDateTime().addYears(1);
01464             }
01465         }
01466 
01467         // We should be no more than a few thousand milliseconds,
01468         // as the end recording code does not have a trigger...
01469         // NOTE: If you change anything here, make sure that
01470         // WaitforEventThreadSleep() will still work...
01471         if (tuningRequests.empty() && !changeState)
01472         {
01473             triggerEventSleep.wakeAll();
01474             lock.mutex()->unlock();
01475             sched_yield();
01476             triggerEventSleep.wakeAll();
01477             triggerEventLoop.wait(1000 /* ms */);
01478             lock.mutex()->lock();
01479         }
01480     }
01481   
01482     if (GetState() != kState_None)
01483     {
01484         ChangeState(kState_None);
01485         HandleStateChange();
01486     }
01487 }
01488 
01489 bool TVRec::WaitForEventThreadSleep(bool wake, ulong time)
01490 {
01491     bool ok = false;
01492     MythTimer t;
01493     t.start();
01494     while (!ok && ((unsigned long) t.elapsed()) < time)
01495     {
01496         if (wake)
01497             triggerEventLoop.wakeAll();
01498 
01499         stateChangeLock.unlock();
01500         // It is possible for triggerEventSleep.wakeAll() to be sent
01501         // before we enter wait so we only wait 100 ms so we can try
01502         // again a few times before 15 second timeout on frontend...
01503         triggerEventSleep.wait(100);
01504         stateChangeLock.lock();
01505 
01506         // verify that we were triggered.
01507         ok = (tuningRequests.empty() && !changeState);
01508     }
01509     return ok;
01510 }
01511 
01512 void TVRec::HandlePendingRecordings(void)
01513 {
01514     if (pendingRecordings.empty())
01515         return;
01516 
01517     // If we have a pending recording and AskAllowRecording
01518     // or DoNotAskAllowRecording is set and the frontend is 
01519     // ready send an ASK_RECORDING query to frontend.
01520 
01521     PendingMap::iterator it, next;
01522 
01523     for (it = pendingRecordings.begin(); it != pendingRecordings.end();)
01524     {
01525         next = it; ++next;
01526         if (QDateTime::currentDateTime() > (*it).recordingStart.addSecs(30))
01527         {
01528             VERBOSE(VB_RECORD, LOC + "Deleting stale pending recording " +
01529                     QString("%1 '%2'")
01530                     .arg((*it).info->cardid)
01531                     .arg((*it).info->title));
01532 
01533             delete (*it).info;
01534             pendingRecordings.erase(it);
01535         }
01536         it = next;
01537     }
01538 
01539     bool has_rec = false;
01540     it = pendingRecordings.begin();
01541     if ((1 == pendingRecordings.size()) &&
01542         (*it).ask &&
01543         ((*it).info->cardid == cardid) &&
01544         (GetState() == kState_WatchingLiveTV))
01545     {
01546         CheckForRecGroupChange();
01547         has_rec = pseudoLiveTVRecording &&
01548             (pseudoLiveTVRecording->recendts > (*it).recordingStart);
01549     }
01550 
01551     for (it = pendingRecordings.begin(); it != pendingRecordings.end(); ++it)
01552     {
01553         if (!(*it).ask && !(*it).doNotAsk)
01554             continue;
01555 
01556         int timeuntil = ((*it).doNotAsk) ?
01557             -1: QDateTime::currentDateTime().secsTo((*it).recordingStart);
01558 
01559         if (has_rec)
01560             (*it).canceled = true;
01561 
01562         QString query = QString("ASK_RECORDING %1 %2 %3 %4")
01563             .arg(cardid)
01564             .arg(timeuntil)
01565             .arg(has_rec ? 1 : 0)
01566             .arg((*it).hasLaterShowing ? 1 : 0);
01567 
01568         VERBOSE(VB_IMPORTANT, LOC + query);
01569 
01570         QStringList msg;
01571         (*it).info->ToStringList(msg);
01572         MythEvent me(query, msg);
01573         gContext->dispatch(me);
01574 
01575         (*it).ask = (*it).doNotAsk = false;
01576     }
01577 }
01578 
01579 bool TVRec::GetDevices(int cardid,
01580                        GeneralDBOptions   &gen_opts,
01581                        DVBDBOptions       &dvb_opts,
01582                        FireWireDBOptions  &firewire_opts,
01583                        DBox2DBOptions     &dbox2_opts)
01584 {
01585     int testnum = 0;
01586     QString test;
01587 
01588     MSqlQuery query(MSqlQuery::InitCon());
01589     query.prepare(
01590         "SELECT videodevice,      vbidevice,           audiodevice,     "
01591         "       audioratelimit,   defaultinput,        cardtype,        "
01592         "       skipbtaudio,      signal_timeout,      channel_timeout, "
01593         "       dvb_wait_for_seqstart, "
01594         ""
01595         "       dvb_on_demand,    dvb_tuning_delay,    dvb_eitscan,"
01596         ""
01597         "       firewire_speed,   firewire_model,      firewire_connection, "
01598         ""
01599         "       dbox2_port,       dbox2_host,          dbox2_httpport   "
01600         ""
01601         "FROM capturecard "
01602         "WHERE cardid = :CARDID");
01603     query.bindValue(":CARDID", cardid);
01604 
01605     if (!query.exec() || !query.isActive())
01606     {
01607         MythContext::DBError("getdevices", query);
01608         return false;
01609     }
01610 
01611     if (!query.next())
01612         return false;
01613 
01614     // General options
01615     test = query.value(0).toString();
01616     if (test != QString::null)
01617         gen_opts.videodev = QString::fromUtf8(test);
01618 
01619     test = query.value(1).toString();
01620     if (test != QString::null)
01621         gen_opts.vbidev = QString::fromUtf8(test);
01622 
01623     test = query.value(2).toString();
01624     if (test != QString::null)
01625         gen_opts.audiodev = QString::fromUtf8(test);
01626 
01627     gen_opts.audiosamplerate = max(testnum, query.value(3).toInt());
01628 
01629     test = query.value(4).toString();
01630     if (test != QString::null)
01631         gen_opts.defaultinput = QString::fromUtf8(test);
01632 
01633     test = query.value(5).toString();
01634     if (test != QString::null)
01635         gen_opts.cardtype = QString::fromUtf8(test);
01636 
01637     gen_opts.skip_btaudio = query.value(6).toUInt();
01638 
01639     gen_opts.signal_timeout  = (uint) max(query.value(7).toInt(), 0);
01640     gen_opts.channel_timeout = (uint) max(query.value(8).toInt(), 0);
01641 
01642     // We should have at least 100 ms to acquire tables...
01643     int table_timeout = ((int)gen_opts.channel_timeout - 
01644                          (int)gen_opts.signal_timeout);
01645     if (table_timeout < 100)
01646         gen_opts.channel_timeout = gen_opts.signal_timeout + 2500;
01647 
01648     gen_opts.wait_for_seqstart = query.value(9).toUInt();
01649 
01650     // DVB options
01651     uint dvboff = 10;
01652     dvb_opts.dvb_on_demand    = query.value(dvboff + 0).toUInt();
01653     dvb_opts.dvb_tuning_delay = query.value(dvboff + 1).toUInt();
01654     dvb_opts.dvb_eitscan      = query.value(dvboff + 2).toUInt();
01655 
01656     // Firewire options
01657     uint fireoff = dvboff + 3;
01658     firewire_opts.speed       = query.value(fireoff + 0).toUInt();
01659 
01660     test = query.value(fireoff + 1).toString();
01661     if (test != QString::null)
01662         firewire_opts.model = QString::fromUtf8(test);
01663 
01664     firewire_opts.connection  = query.value(fireoff + 2).toUInt();
01665 
01666     // DBOX2/HDHomeRun options
01667     uint dbox2off = fireoff + 3;
01668     dbox2_opts.port = query.value(dbox2off + 0).toUInt();
01669 
01670     test = query.value(dbox2off + 1).toString();
01671     if (test != QString::null)
01672         dbox2_opts.host = QString::fromUtf8(test);
01673 
01674     dbox2_opts.httpport = query.value(dbox2off + 2).toUInt();
01675 
01676     return true;
01677 }
01678 
01679 QString TVRec::GetStartChannel(int cardid, const QString &defaultinput)
01680 {
01681     QString startchan = QString::null;
01682 
01683     // Get last tuned channel from database, to use as starting channel
01684     MSqlQuery query(MSqlQuery::InitCon());
01685     query.prepare(
01686         "SELECT startchan "
01687         "FROM cardinput "
01688         "WHERE cardinput.cardid   = :CARDID    AND "
01689         "      inputname          = :INPUTNAME");
01690     query.bindValue(":CARDID",    cardid);
01691     query.bindValue(":INPUTNAME", defaultinput);
01692 
01693     if (!query.exec() || !query.isActive())
01694     {
01695         MythContext::DBError("getstartchan", query);
01696     }
01697     else if (query.next())
01698     {
01699         startchan = QString::fromUtf8(query.value(0).toString());
01700         if (!startchan.isEmpty())
01701         {
01702             VERBOSE(VB_CHANNEL, LOC + QString("Start channel: %1.")
01703                     .arg(startchan));
01704             return startchan;
01705         }
01706     }
01707 
01708     // If we failed to get the last tuned channel,
01709     // get a valid channel on our current input.
01710     query.prepare(
01711         "SELECT channum "
01712         "FROM capturecard, cardinput, channel "
01713         "WHERE capturecard.cardid = cardinput.cardid   AND "
01714         "      channel.sourceid   = cardinput.sourceid AND "
01715         "      capturecard.cardid = :CARDID AND "
01716         "      inputname          = :INPUTNAME");
01717     query.bindValue(":CARDID",    cardid);
01718     query.bindValue(":INPUTNAME", defaultinput);
01719 
01720     if (!query.exec() || !query.isActive())
01721     {
01722         MythContext::DBError("getstartchan2", query);
01723     }
01724     while (query.next())
01725     {
01726         startchan = QString::fromUtf8(query.value(0).toString());
01727         if (!startchan.isEmpty())
01728         {
01729             VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Start channel from DB is "
01730                     "empty, setting to '%1' instead.").arg(startchan));
01731             return startchan;
01732         }
01733     }
01734 
01735     // If we failed to get a channel on our current input,
01736     // widen search to any input.
01737     query.prepare(
01738         "SELECT channum, inputname "
01739         "FROM capturecard, cardinput, channel "
01740         "WHERE capturecard.cardid = cardinput.cardid   AND "
01741         "      channel.sourceid   = cardinput.sourceid AND "
01742         "      capturecard.cardid = :CARDID");
01743     query.bindValue(":CARDID", cardid);
01744 
01745     if (!query.exec() || !query.isActive())
01746     {
01747         MythContext::DBError("getstartchan3", query);
01748     }
01749     while (query.next())
01750     {
01751         startchan = QString::fromUtf8(query.value(0).toString());
01752         if (!startchan.isEmpty())
01753         {
01754             VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Start channel invalid, "
01755                     "setting to '%1' on input %2 instead.").arg(startchan)
01756                     .arg(query.value(1).toString()));
01757             return startchan;
01758         }
01759     }
01760 
01761     // If there are no valid channels, just use a random channel
01762     startchan = "3";
01763     VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Problem finding starting channel, "
01764             "setting to default of '%1'.").arg(startchan));
01765     return startchan;
01766 }
01767 
01768 void GetPidsToCache(DTVSignalMonitor *dtvMon, pid_cache_t &pid_cache)
01769 {
01770     if (!dtvMon->GetATSCStreamData())
01771         return;
01772 
01773     const MasterGuideTable *mgt = dtvMon->GetATSCStreamData()->GetCachedMGT();
01774     if (!mgt)
01775         return;
01776 
01777     for (uint i = 0; i < mgt->TableCount(); ++i)
01778     {
01779         pid_cache_item_t item(mgt->TablePID(i), mgt->TableType(i));
01780         pid_cache.push_back(item);
01781     }
01782     dtvMon->GetATSCStreamData()->ReturnCachedTable(mgt);
01783 }
01784 
01785 bool ApplyCachedPids(DTVSignalMonitor *dtvMon, const DTVChannel* channel)
01786 {
01787     pid_cache_t pid_cache;
01788     channel->GetCachedPids(pid_cache);
01789     pid_cache_t::const_iterator it = pid_cache.begin();
01790     bool vctpid_cached = false;
01791     for (; it != pid_cache.end(); ++it)
01792     {
01793         if ((it->second == TableID::TVCT) ||
01794             (it->second == TableID::CVCT))
01795         {
01796             vctpid_cached = true;
01797             dtvMon->GetATSCStreamData()->AddListeningPID(it->first);
01798         }
01799     }
01800     return vctpid_cached;
01801 }
01802 
01815 bool TVRec::SetupDTVSignalMonitor(void)
01816 {
01817     VERBOSE(VB_RECORD, LOC + "Setting up table monitoring.");
01818 
01819     DTVSignalMonitor *sm = GetDTVSignalMonitor();
01820     DTVChannel *dtvchan = GetDTVChannel();
01821     if (!sm || !dtvchan)
01822     {
01823         VERBOSE(VB_IMPORTANT, LOC_ERR + "Setting up table monitoring.");
01824         return false;
01825     }
01826 
01827     MPEGStreamData *sd = NULL;
01828     if (GetDTVRecorder())
01829     {
01830         sd = GetDTVRecorder()->GetStreamData();
01831         sd->SetCaching(true);
01832     }
01833 
01834     QString recording_type = "all";
01835     ProgramInfo *rec = lastTuningRequest.program;
01836     RecordingProfile profile;
01837     load_profile(genOpt.cardtype, tvchain, rec, profile);
01838     const Setting *setting = profile.byName("recordingtype");
01839     if (setting)
01840         recording_type = setting->getValue();
01841 
01842     const QString tuningmode = dtvchan->GetTuningMode();
01843 
01844     // Check if this is an ATSC Channel
01845     int major = dtvchan->GetMajorChannel();
01846     int minor = dtvchan->GetMinorChannel();
01847     if ((minor > 0) && (tuningmode == "atsc"))
01848     {
01849         QString msg = QString("ATSC channel: %1_%2").arg(major).arg(minor);
01850         VERBOSE(VB_RECORD, LOC + msg);
01851 
01852         ATSCStreamData *asd = dynamic_cast<ATSCStreamData*>(sd);
01853         if (!asd)
01854         {
01855             sd = asd = new ATSCStreamData(major, minor);
01856             sd->SetCaching(true);
01857             if (GetDTVRecorder())
01858                 GetDTVRecorder()->SetStreamData(asd);
01859         }
01860 
01861         asd->Reset();
01862         sd->SetRecordingType(recording_type);
01863         sm->SetStreamData(sd);
01864         sm->SetChannel(major, minor);
01865 
01866         // Try to get pid of VCT from cache and
01867         // require MGT if we don't have VCT pid.
01868         if (!ApplyCachedPids(sm, dtvchan))
01869             sm->AddFlags(SignalMonitor::kDTVSigMon_WaitForMGT);
01870 
01871         VERBOSE(VB_RECORD, LOC + "Successfully set up ATSC table monitoring.");
01872         return true;
01873     }
01874 
01875     // Check if this is an DVB channel
01876     int progNum = dtvchan->GetProgramNumber();
01877 #ifdef USING_DVB
01878     if ((progNum >= 0) && (tuningmode == "dvb"))
01879     {
01880         int netid   = dtvchan->GetOriginalNetworkID();
01881         int tsid    = dtvchan->GetTransportID();
01882 
01883         DVBStreamData *dsd = dynamic_cast<DVBStreamData*>(sd);
01884         if (!dsd)
01885         {
01886             sd = dsd = new DVBStreamData(netid, tsid, progNum);
01887             sd->SetCaching(true);
01888             if (GetDTVRecorder())
01889                 GetDTVRecorder()->SetStreamData(dsd);
01890         }
01891 
01892         VERBOSE(VB_RECORD, LOC +
01893                 QString("DVB service_id %1 on net_id %2 tsid %3")
01894                 .arg(progNum).arg(netid).arg(tsid));
01895 
01896         // Some DVB devices munge the PMT and/or PAT so the CRC check fails.
01897         // We need to tell the stream data class to not check the CRC on 
01898         // these devices.
01899         if (GetDVBChannel())
01900             sd->SetIgnoreCRC(GetDVBChannel()->HasCRCBug());
01901 
01902         dsd->Reset();
01903         sd->SetRecordingType(recording_type);
01904         sm->SetStreamData(sd);
01905         sm->SetDVBService(netid, tsid, progNum);
01906 
01907         sm->AddFlags(SignalMonitor::kDTVSigMon_WaitForPMT |
01908                      SignalMonitor::kDTVSigMon_WaitForSDT |
01909                      SignalMonitor::kDVBSigMon_WaitForPos);
01910         sm->SetRotorTarget(1.0f);
01911 
01912         VERBOSE(VB_RECORD, LOC + "Successfully set up DVB table monitoring.");
01913         return true;
01914     }
01915 #endif // USING_DVB
01916 
01917     // Check if this is an MPEG channel
01918     if (progNum >= 0)
01919     {
01920         if (!sd)
01921         {
01922             sd = new MPEGStreamData(progNum, true);
01923             sd->SetCaching(true);
01924             if (GetDTVRecorder())
01925                 GetDTVRecorder()->SetStreamData(sd);
01926         }
01927 
01928         QString msg = QString("MPEG program number: %1").arg(progNum);
01929         VERBOSE(VB_RECORD, LOC + msg);
01930 
01931 #ifdef USING_DVB
01932         // Some DVB devices munge the PMT and/or PAT so the CRC check fails.
01933         // We need to tell the stream data class to not check the CRC on 
01934         // these devices.
01935         if (GetDVBChannel())
01936             sd->SetIgnoreCRC(GetDVBChannel()->HasCRCBug());
01937 #endif // USING_DVB
01938 
01939         sd->Reset();
01940         sd->SetRecordingType(recording_type);
01941         sm->SetStreamData(sd);
01942         sm->SetProgramNumber(progNum);
01943 
01944         sm->AddFlags(SignalMonitor::kDTVSigMon_WaitForPAT |
01945                      SignalMonitor::kDTVSigMon_WaitForPMT |
01946                      SignalMonitor::kDVBSigMon_WaitForPos);
01947         sm->SetRotorTarget(1.0f);
01948 
01949         VERBOSE(VB_RECORD, LOC + "Successfully set up MPEG table monitoring.");
01950         return true;
01951     }
01952 
01953     QString msg = "No valid DTV info, ATSC maj(%1) min(%2), MPEG pn(%3)";
01954     VERBOSE(VB_IMPORTANT, LOC_ERR + msg.arg(major).arg(minor).arg(progNum));
01955     return false;
01956 }
01957 
01970 bool TVRec::SetupSignalMonitor(bool tablemon, bool notify)
01971 {
01972     VERBOSE(VB_RECORD, LOC + "SetupSignalMonitor("
01973             <<tablemon<<", "<<notify<<")");
01974 
01975     // if it already exists, there no need to initialize it
01976     if (signalMonitor)
01977         return true;
01978 
01979     // if there is no channel object we can't monitor it
01980     if (!channel)
01981         return false;
01982 
01983     // make sure statics are initialized
01984     SignalMonitorValue::Init();
01985 
01986     if (SignalMonitor::IsSupported(genOpt.cardtype) && channel->Open())
01987         signalMonitor = SignalMonitor::Init(genOpt.cardtype, cardid, channel);
01988     
01989     if (signalMonitor)
01990     {
01991         VERBOSE(VB_RECORD, LOC + "Signal monitor successfully created");
01992         // If this is a monitor for Digital TV, initialize table monitors
01993         if (GetDTVSignalMonitor() && tablemon && !SetupDTVSignalMonitor())
01994         {
01995             VERBOSE(VB_IMPORTANT, LOC_ERR +
01996                     "Failed to setup digital signal monitoring");
01997 
01998             return false;
01999         }
02000 
02001         connect(signalMonitor, SIGNAL(AllGood(void)),
02002                 this, SLOT(SignalMonitorAllGood(void)));
02003 
02004         signalMonitor->SetUpdateRate(kSignalMonitoringRate);
02005         signalMonitor->SetNotifyFrontend(notify);
02006 
02007         // Start the monitoring thread
02008         signalMonitor->Start();
02009     }
02010 
02011     return true;
02012 }
02013 
02018 void TVRec::TeardownSignalMonitor()
02019 {
02020     if (!signalMonitor)
02021         return;
02022 
02023     VERBOSE(VB_RECORD, LOC + "TeardownSignalMonitor() -- begin");
02024 
02025     // If this is a DTV signal monitor, save any pids we know about.
02026     DTVSignalMonitor *dtvMon  = GetDTVSignalMonitor();
02027     DTVChannel       *dtvChan = GetDTVChannel();
02028     if (dtvMon && dtvChan)
02029     {
02030         pid_cache_t pid_cache;
02031         GetPidsToCache(dtvMon, pid_cache);
02032         if (pid_cache.size())
02033             dtvChan->SaveCachedPids(pid_cache);
02034     }
02035 
02036     if (signalMonitor)
02037     {
02038         signalMonitor->deleteLater();
02039         signalMonitor = NULL;
02040     }
02041 
02042     VERBOSE(VB_RECORD, LOC + "TeardownSignalMonitor() -- end");
02043 }
02044 
02056 int TVRec::SetSignalMonitoringRate(int rate, int notifyFrontend)
02057 {
02058     QString msg = "SetSignalMonitoringRate(%1, %2)";
02059     VERBOSE(VB_RECORD, LOC + msg.arg(rate).arg(notifyFrontend) + "-- start");
02060 
02061     QMutexLocker lock(&stateChangeLock);
02062 
02063     if (!SignalMonitor::IsSupported(genOpt.cardtype))
02064     {
02065         VERBOSE(VB_IMPORTANT, LOC + "Signal Monitoring is not"
02066                 "supported by your hardware.");
02067         return 0;
02068     }
02069 
02070     if (GetState() != kState_WatchingLiveTV)
02071     {
02072         VERBOSE(VB_IMPORTANT, LOC + "Signal can only "
02073                 "be monitored in LiveTV Mode.");
02074         return 0;
02075     }
02076 
02077     ClearFlags(kFlagRingBufferReady);
02078 
02079     TuningRequest req = (rate > 0) ?
02080         TuningRequest(kFlagAntennaAdjust, channel->GetCurrentName()) :
02081         TuningRequest(kFlagLiveTV);
02082 
02083     tuningRequests.enqueue(req);
02084 
02085     // Wait for RingBuffer reset
02086     while (!HasFlags(kFlagRingBufferReady))
02087         WaitForEventThreadSleep();
02088     VERBOSE(VB_RECORD, LOC + msg.arg(rate).arg(notifyFrontend) + " -- end");
02089     return 1;
02090 }
02091 
02092 DTVSignalMonitor *TVRec::GetDTVSignalMonitor(void)
02093 {
02094     return dynamic_cast<DTVSignalMonitor*>(signalMonitor);
02095 }
02096 
02108 bool TVRec::ShouldSwitchToAnotherCard(QString chanid)
02109 {
02110     QString msg("");
02111     MSqlQuery query(MSqlQuery::InitCon());
02112 
02113     if (!query.isConnected())
02114         return false;
02115 
02116     query.prepare("SELECT channel.channum, channel.callsign "
02117                   "FROM channel "
02118                   "WHERE channel.chanid = :CHANID");
02119     query.bindValue(":CHANID", chanid);
02120     if (!query.exec() || !query.isActive() || query.size() == 0)
02121     {
02122         MythContext::DBError("ShouldSwitchToAnotherCard", query);
02123         return false;
02124     }
02125 
02126     query.next();
02127     QString channelname = query.value(0).toString();
02128     QString callsign = query.value(1).toString();
02129 
02130     query.prepare(
02131         "SELECT channel.channum "
02132         "FROM channel,cardinput "
02133         "WHERE ( channel.chanid = :CHANID OR             "
02134         "        ( channel.channum  = :CHANNUM AND       "
02135         "          channel.callsign = :CALLSIGN    )     "
02136         "      )                                     AND "
02137         "      channel.sourceid = cardinput.sourceid AND "
02138         "      cardinput.cardid = :CARDID");
02139     query.bindValue(":CHANID", chanid);
02140     query.bindValue(":CHANNUM", channelname);
02141     query.bindValue(":CALLSIGN", callsign);
02142     query.bindValue(":CARDID", cardid);
02143 
02144     if (!query.exec() || !query.isActive())
02145     {
02146         MythContext::DBError("ShouldSwitchToAnotherCard", query);
02147     }
02148     else if (query.size() > 0)
02149     {
02150         msg = "Found channel (%1) on current card(%2).";
02151         VERBOSE(VB_RECORD, LOC + msg.arg(channelname).arg(cardid));
02152         return false;
02153     }
02154 
02155     // We didn't find it on the current card, so now we check other cards.
02156     query.prepare(
02157         "SELECT channel.channum, cardinput.cardid "
02158         "FROM channel,cardinput "
02159         "WHERE ( channel.chanid = :CHANID OR              "
02160         "        ( channel.channum  = :CHANNUM AND        "
02161         "          channel.callsign = :CALLSIGN    )      "
02162         "      )                                      AND "
02163         "      channel.sourceid  = cardinput.sourceid AND "
02164         "      cardinput.cardid != :CARDID");
02165     query.bindValue(":CHANID", chanid);
02166     query.bindValue(":CHANNUM", channelname);
02167     query.bindValue(":CALLSIGN", callsign);
02168     query.bindValue(":CARDID", cardid);
02169 
02170     if (!query.exec() || !query.isActive())
02171     {
02172         MythContext::DBError("ShouldSwitchToAnotherCard", query);
02173     }
02174     else if (query.next())
02175     {
02176         msg = QString("Found channel (%1) on different card(%2).")
02177             .arg(query.value(0).toString()).arg(query.value(1).toString());
02178         VERBOSE(VB_RECORD, LOC + msg);
02179         return true;
02180     }
02181 
02182     msg = QString("Did not find channel(%1) on any card.").arg(channelname);
02183     VERBOSE(VB_RECORD, LOC + msg);
02184     return false;
02185 }
02186 
02197 bool TVRec::CheckChannel(QString name) const
02198 {
02199     if (!channel)
02200         return false;
02201 
02202     QString dummyID;
02203     return channel->CheckChannel(name, dummyID);
02204 }
02205 
02209 static QString add_spacer(const QString &channel, const QString &spacer)
02210 {
02211     QString chan = QDeepCopy<QString>(channel);
02212     if ((chan.length() >= 2) && !spacer.isEmpty())
02213         return chan.left(chan.length()-1) + spacer + chan.right(1);
02214     return chan;
02215 }
02216 
02244 bool TVRec::CheckChannelPrefix(const QString &prefix,
02245                                uint          &is_complete_valid_channel_on_rec,
02246                                bool          &is_extra_char_useful,
02247                                QString       &needed_spacer)
02248 {
02249 #if DEBUG_CHANNEL_PREFIX
02250     VERBOSE(VB_IMPORTANT, QString("CheckChannelPrefix(%1)").arg(prefix));
02251 #endif
02252 
02253     static const uint kSpacerListSize = 5;
02254     static const char* spacers[kSpacerListSize] = { "", "_", "-", "#", "." };
02255 
02256     MSqlQuery query(MSqlQuery::InitCon());
02257     QString basequery = QString(
02258         "SELECT channel.chanid, channel.channum, cardinput.cardid "
02259         "FROM channel, capturecard, cardinput "
02260         "WHERE channel.channum LIKE '%1%'            AND "
02261         "      channel.sourceid = cardinput.sourceid AND "
02262         "      cardinput.cardid = capturecard.cardid");
02263 
02264     QString cardquery[2] =
02265     {
02266         QString(" AND capturecard.cardid  = '%1'").arg(cardid),
02267         QString(" AND capturecard.cardid != '%1'").arg(cardid),
02268     };
02269 
02270     vector<uint>    fchanid;
02271     vector<QString> fchannum;
02272     vector<uint>    fcardid;
02273     vector<QString> fspacer;
02274 
02275     for (uint i = 0; i < 2; i++)
02276     {
02277         for (uint j = 0; j < kSpacerListSize; j++)
02278         {
02279             QString qprefix = add_spacer(
02280                 prefix, (QString(spacers[j]) == "_") ? "\\_" : spacers[j]);
02281             query.prepare(basequery.arg(qprefix) + cardquery[i]);
02282 
02283             if (!query.exec() || !query.isActive())
02284             {
02285                 MythContext::DBError("checkchannel -- locate channum", query);
02286             }
02287             else if (query.size())
02288             {
02289                 while (query.next())
02290                 {
02291                     fchanid.push_back(query.value(0).toUInt());
02292                     fchannum.push_back(query.value(1).toString());
02293                     fcardid.push_back(query.value(2).toUInt());
02294                     fspacer.push_back(spacers[j]);
02295 #if DEBUG_CHANNEL_PREFIX
02296                     VERBOSE(VB_IMPORTANT, QString("(%1,%2) Adding %3 rec %4")
02297                             .arg(i).arg(j).arg(query.value(1).toString(),6)
02298                             .arg(query.value(2).toUInt()));
02299 #endif
02300                 }
02301             }
02302 
02303             if (prefix.length() < 2)
02304                 break;
02305         }
02306     }
02307 
02308     // Now process the lists for the info we need...
02309     is_extra_char_useful = false;
02310     is_complete_valid_channel_on_rec = 0;
02311     needed_spacer = "";
02312 
02313     if (fchanid.size() == 0)
02314         return false;
02315 
02316     if (fchanid.size() == 1) // Unique channel...
02317     {
02318         needed_spacer = QDeepCopy<QString>(fspacer[0]);
02319         bool nc       = (fchannum[0] != add_spacer(prefix, fspacer[0]));
02320 
02321         is_complete_valid_channel_on_rec = (nc) ? 0 : fcardid[0];
02322         is_extra_char_useful             = nc;
02323         return true;
02324     }
02325 
02326     // If we get this far there is more than one channel
02327     // sharing the prefix we were given.
02328 
02329     // Is an extra characher useful for disambiguation?
02330     is_extra_char_useful = false;
02331     for (uint i = 0; (i < fchannum.size()) && !is_extra_char_useful; i++)
02332     {
02333         is_extra_char_useful = (fchannum[i] != add_spacer(prefix, fspacer[i]));
02334 #if DEBUG_CHANNEL_PREFIX
02335         VERBOSE(VB_IMPORTANT, "is_extra_char_useful("
02336                 <<fchannum[i]<<"!="<<add_spacer(prefix, fspacer[i])
02337                 <<"): "<<is_extra_char_useful);
02338 #endif
02339     }
02340 
02341     // Are any of the channels complete w/o spacer?
02342     // If so set is_complete_valid_channel_on_rec,
02343     // with a preference for our cardid.
02344     for (uint i = 0; i < fchannum.size(); i++)
02345     {
02346         if (fchannum[i] == prefix)
02347         {
02348             is_complete_valid_channel_on_rec = fcardid[i];
02349             if (fcardid[i] == (uint)cardid)
02350                 break;
02351         }
02352     }
02353 
02354     if (is_complete_valid_channel_on_rec)
02355         return true;
02356 
02357     // Add a spacer, if one is needed to select a valid channel.
02358     bool spacer_needed = true;
02359     for (uint i = 0; (i < fspacer.size() && spacer_needed); i++)
02360         spacer_needed = !fspacer[i].isEmpty();
02361     if (spacer_needed)
02362         needed_spacer = QDeepCopy<QString>(fspacer[0]);
02363 
02364     // If it isn't useful to wait for more characters,
02365     // then try to commit to any true match immediately.
02366     for (uint i = 0; i < ((is_extra_char_useful) ? 0 : fchanid.size()); i++)
02367     {
02368         if (fchannum[i] == add_spacer(prefix, fspacer[i]))
02369         {
02370             needed_spacer = QDeepCopy<QString>(fspacer[i]);
02371             is_complete_valid_channel_on_rec = fcardid[i];
02372             return true;
02373         }
02374     }
02375 
02376     return true;
02377 }
02378 
02379 bool TVRec::SetVideoFiltersForChannel(uint  sourceid,
02380                                       const QString &channum)
02381 {
02382     if (!recorder)
02383         return false;
02384 
02385     QString videoFilters = ChannelUtil::GetVideoFilters(sourceid, channum);
02386     if (!videoFilters.isEmpty())
02387     {
02388         recorder->SetVideoFilters(videoFilters);
02389         return true;
02390     }
02391 
02392     return false;
02393 }
02394 
02399 bool TVRec::IsReallyRecording(void)
02400 {
02401     return ((recorder && recorder->IsRecording()) ||
02402             HasFlags(kFlagDummyRecorderRunning));
02403 }
02404 
02410 bool TVRec::IsBusy(TunedInputInfo *busy_input, int time_buffer) const
02411 {
02412     QMutexLocker lock(&stateChangeLock);
02413 
02414     TunedInputInfo dummy;
02415     if (!busy_input)
02416         busy_input = &dummy;
02417 
02418     busy_input->Clear();
02419 
02420     if (!channel)
02421         return false;
02422 
02423     QStringList list = channel->GetConnectedInputs();
02424     if (list.empty())
02425         return false;
02426 
02427     uint chanid = 0;
02428 
02429     if (GetState() != kState_None)
02430     {
02431         busy_input->inputid = channel->GetCurrentInputNum();
02432         chanid              = channel->GetChanID();
02433     }
02434 
02435     PendingMap::const_iterator it = pendingRecordings.find(cardid);
02436     if (!busy_input->inputid && (it != pendingRecordings.end()))
02437     {
02438         int timeLeft = QDateTime::currentDateTime()
02439             .secsTo((*it).recordingStart);
02440 
02441         if (timeLeft <= time_buffer)
02442         {
02443             QString channum = QString::null, input = QString::null;
02444             if ((*it).info->GetChannel(channum, input))
02445             {
02446                 busy_input->inputid = channel->GetInputByName(input);
02447                 chanid = (*it).info->chanid.toUInt();
02448             }
02449         }
02450     }
02451 
02452     if (busy_input->inputid)
02453     {
02454         CardUtil::GetInputInfo(*busy_input);
02455         busy_input->chanid  = chanid;
02456         busy_input->mplexid = ChannelUtil::GetMplexID(busy_input->chanid);
02457         busy_input->mplexid =
02458             (32767 == busy_input->mplexid) ? 0 : busy_input->mplexid;
02459     }
02460 
02461     return busy_input->inputid;
02462 }
02463 
02464 
02471 float TVRec::GetFramerate(void)
02472 {
02473     QMutexLocker lock(&stateChangeLock);
02474 
02475     if (recorder)
02476         return recorder->GetFrameRate();
02477     return -1.0f;
02478 }
02479 
02486 long long TVRec::GetFramesWritten(void)
02487 {
02488     QMutexLocker lock(&stateChangeLock);
02489 
02490     if (recorder)
02491         return recorder->GetFramesWritten();
02492     return -1;
02493 }
02494 
02501 long long TVRec::GetFilePosition(void)
02502 {
02503     QMutexLocker lock(&stateChangeLock);
02504 
02505     if (ringBuffer)
02506         return ringBuffer->GetWritePosition();
02507     return -1;
02508 }
02509 
02517 long long TVRec::GetKeyframePosition(long long desired)
02518 {
02519     QMutexLocker lock(&stateChangeLock);
02520 
02521     if (recorder)
02522         return recorder->GetKeyframePosition(desired);
02523     return -1;
02524 }
02525 
02531 long long TVRec::GetMaxBitrate(void)
02532 {
02533     long long bitrate;
02534     if (genOpt.cardtype == "MPEG")
02535         bitrate = 10080000LL; // use DVD max bit rate
02536     else if (genOpt.cardtype == "DBOX2")
02537         bitrate = 10080000LL; // use DVD max bit rate
02538     else if (!CardUtil::IsEncoder(genOpt.cardtype))
02539         bitrate = 19400000LL; // 1080i
02540     else // frame grabber
02541         bitrate = 10080000LL; // use DVD max bit rate, probably too big
02542 
02543     return bitrate;
02544 }
02545 
02551 void TVRec::SpawnLiveTV(LiveTVChain *newchain, bool pip, QString startchan)
02552 {
02553     QMutexLocker lock(&stateChangeLock);
02554 
02555     tvchain = newchain;
02556     tvchain->ReloadAll();
02557 
02558     QString hostprefix = QString("myth://%1:%2/")
02559                                 .arg(gContext->GetSetting("BackendServerIP"))
02560                                 .arg(gContext->GetSetting("BackendServerPort"));
02561 
02562     tvchain->SetHostPrefix(hostprefix);
02563     tvchain->SetCardType(genOpt.cardtype);
02564 
02565     ispip = pip;
02566     LiveTVStartChannel = startchan;
02567 
02568     // Change to WatchingLiveTV
02569     ChangeState(kState_WatchingLiveTV);
02570     // Wait for state change to take effect
02571     WaitForEventThreadSleep();
02572 
02573     // Make sure StartRecording can't steal our tuner
02574     SetFlags(kFlagCancelNextRecording);
02575 }
02576 
02580 QString TVRec::GetChainID(void)
02581 {
02582     if (tvchain)
02583         return tvchain->GetID();
02584     return "";
02585 }
02586 
02595 void TVRec::CheckForRecGroupChange(void)
02596 {
02597     QMutexLocker lock(&stateChangeLock);
02598 
02599     if (internalState == kState_None)
02600         return; // already stopped
02601 
02602     ProgramInfo *pi = NULL;
02603     if (curRecording)
02604     {
02605         pi = ProgramInfo::GetProgramFromRecorded(
02606             curRecording->chanid, curRecording->recstartts);
02607     }
02608     if (!pi)
02609         return;
02610 
02611     if (pi->recgroup != "LiveTV" && !pseudoLiveTVRecording)
02612     {
02613         // User wants this recording to continue
02614         SetPseudoLiveTVRecording(pi);
02615         return;
02616     }
02617     else if (pi->recgroup == "LiveTV" && pseudoLiveTVRecording)
02618     {
02619         // User wants to abandon scheduled recording
02620         SetPseudoLiveTVRecording(NULL);
02621     }
02622 
02623     delete pi;
02624 }
02625 
02626 static uint get_input_id(uint cardid, const QString &inputname)
02627 {
02628     MSqlQuery query(MSqlQuery::InitCon());
02629 
02630     query.prepare(
02631         "SELECT cardinputid "
02632         "FROM cardinput "
02633         "WHERE cardid    = :CARDID AND "
02634         "      inputname = :INNAME");
02635 
02636     query.bindValue(":CARDID", cardid);
02637     query.bindValue(":INNAME", inputname);
02638 
02639     if (!query.exec() || !query.isActive())
02640         MythContext::DBError("get_input_id", query);
02641     else if (query.next())
02642         return query.value(0).toUInt();
02643 
02644     return 0;
02645 }
02646 
02656 void TVRec::NotifySchedulerOfRecording(ProgramInfo *rec)
02657 {
02658     if (!channel)
02659         return;
02660 
02661     // Notify scheduler of the recording.
02662     // + set up recording so it can be resumed
02663     rec->cardid    = cardid;
02664     rec->inputid   = get_input_id(cardid, channel->GetCurrentInput());
02665 
02666     rec->rectype = rec->GetScheduledRecording()->getRecordingType();
02667 
02668     if (rec->rectype == kNotRecording)
02669     {
02670         rec->rectype = kSingleRecord;
02671         rec->GetScheduledRecording()->setRecordingType(kSingleRecord);
02672     }
02673 
02674     // + remove DefaultEndOffset which would mismatch the live session
02675     rec->GetScheduledRecording()->setEndOffset(0);
02676 
02677     // + save rsInactive recstatus to so that a reschedule call
02678     //   doesn't start recording this on another card before we
02679     //   send the SCHEDULER_ADD_RECORDING message to the scheduler.
02680     rec->recstatus = rsInactive;
02681     rec->AddHistory(false);
02682 
02683     // + save ScheduledRecording so that we get a recordid
02684     //   (don't allow signalChange(), avoiding unneeded reschedule)
02685     rec->GetScheduledRecording()->save(false);
02686 
02687     // + save recordid to recorded entry
02688     rec->ApplyRecordRecID();
02689 
02690     // + set proper recstatus (saved later)
02691     rec->recstatus = rsRecording;
02692 
02693     // + pass proginfo to scheduler and reschedule
02694     QStringList prog;
02695     rec->ToStringList(prog);
02696     MythEvent me("SCHEDULER_ADD_RECORDING", prog);
02697     gContext->dispatch(me);
02698 
02699     // Allow scheduler to end this recording before post-roll,
02700     // if it has another recording for this recorder.
02701     ClearFlags(kFlagCancelNextRecording);
02702 }
02703 
02715 void TVRec::SetLiveRecording(int recording)
02716 {
02717     VERBOSE(VB_IMPORTANT, LOC + "SetLiveRecording("<<recording<<")");
02718     QMutexLocker locker(&stateChangeLock);
02719 
02720     (void) recording;
02721 
02722     RecStatusType recstat = rsCancelled;
02723     bool was_rec = pseudoLiveTVRecording;
02724     CheckForRecGroupChange();
02725     if (was_rec && !pseudoLiveTVRecording)
02726     {
02727         VERBOSE(VB_IMPORTANT, LOC + "SetLiveRecording() -- cancel");
02728         // cancel -- 'recording' should be 0 or -1
02729         SetFlags(kFlagCancelNextRecording);
02730         curRecording->recgroup = "LiveTV";
02731     }
02732     else if (!was_rec && pseudoLiveTVRecording)
02733     {
02734         VERBOSE(VB_IMPORTANT, LOC + "SetLiveRecording() -- record");
02735         // record -- 'recording' should be 1 or -1
02736 
02737         // If the last recording was flagged for keeping
02738         // in the frontend, then add the recording rule
02739         // so that transcode, commfrag, etc can be run.
02740         recordEndTime = GetRecordEndTime(pseudoLiveTVRecording);
02741         NotifySchedulerOfRecording(curRecording);
02742         recstat = curRecording->recstatus;
02743         curRecording->recgroup = "Default";
02744     }
02745 
02746     MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
02747                  .arg(curRecording->cardid)
02748                  .arg(curRecording->chanid)
02749                  .arg(curRecording->startts.toString(Qt::ISODate))
02750                  .arg(recstat)
02751                  .arg(curRecording->recendts.toString(Qt::ISODate)));
02752 
02753     gContext->dispatch(me);
02754 }
02755 
02760 void TVRec::StopLiveTV(void)
02761 {
02762     QMutexLocker lock(&stateChangeLock);
02763     VERBOSE(VB_RECORD, LOC + "StopLiveTV(void) curRec: "<<curRecording
02764             <<" pseudoRec: "<<pseudoLiveTVRecording);
02765 
02766     if (internalState == kState_None)
02767         return; // already stopped
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         VERBOSE(VB_IMPORTANT, LOC + "PauseRecorder() "
02808                 "called with no recorder");
02809         return;
02810     }
02811 
02812     recorder->Pause();
02813 } 
02814 
02820 void TVRec::RecorderPaused(void)
02821 {
02822     if (pauseNotify)
02823     {
02824         QMutexLocker lock(&stateChangeLock);
02825         triggerEventLoop.wakeAll();
02826     }
02827 }
02828 
02832 void TVRec::ToggleChannelFavorite(void)
02833 {
02834     QMutexLocker lock(&stateChangeLock);
02835 
02836     if (!channel)
02837         return;
02838 
02839     // Get current channel id...
02840     uint    sourceid = channel->GetCurrentSourceID();
02841     QString channum  = channel->GetCurrentName();
02842     uint chanid = ChannelUtil::GetChanID(sourceid, channum);
02843 
02844     if (!chanid)
02845     {
02846         VERBOSE(VB_IMPORTANT, LOC_ERR + QString(
02847                 "Channel: \'%1\' was not found in the database.\n"
02848                 "\t\t\tMost likely, your DefaultTVChannel setting is wrong.\n"
02849                 "\t\t\tCould not toggle favorite.").arg(channum));
02850         return;
02851     }
02852 
02853     // Check if favorite exists for that chanid...
02854     MSqlQuery query(MSqlQuery::InitCon());
02855     query.prepare(
02856         "SELECT favorites.favid "
02857         "FROM favorites "
02858         "WHERE favorites.chanid = :CHANID "
02859         "LIMIT 1");
02860     query.bindValue(":CHANID", chanid);
02861 
02862     if (!query.exec() || !query.isActive())
02863     {
02864         MythContext::DBError("togglechannelfavorite", query);
02865     }
02866     else if (query.size() > 0)
02867     {
02868         // We have a favorites record...Remove it to toggle...
02869         query.next();
02870         QString favid = query.value(0).toString();
02871         query.prepare(
02872             QString("DELETE FROM favorites "
02873                     "WHERE favid = '%1'").arg(favid));
02874         query.exec();
02875         VERBOSE(VB_RECORD, LOC + "Removing Favorite.");
02876     }
02877     else
02878     {
02879         // We have no favorites record...Add one to toggle...
02880         query.prepare(
02881             QString("INSERT INTO favorites (chanid) "
02882                     "VALUES ('%1')").arg(chanid));
02883         query.exec();
02884         VERBOSE(VB_RECORD, LOC + "Adding Favorite.");
02885     }
02886 }
02887 
02893 int TVRec::GetPictureAttribute(PictureAttribute attr)
02894 {
02895     QMutexLocker lock(&stateChangeLock);
02896     if (!channel)
02897         return -1;
02898 
02899     int ret = channel->GetPictureAttribute(attr);
02900 
02901     return (ret < 0) ? -1 : ret / 655;
02902 }
02903 
02911 int TVRec::ChangePictureAttribute(PictureAdjustType type,
02912                                   PictureAttribute  attr,
02913                                   bool              direction)
02914 {
02915     QMutexLocker lock(&stateChangeLock);
02916     if (!channel)
02917         return -1;
02918 
02919     int ret = channel->ChangePictureAttribute(type, attr, direction);
02920 
02921     return (ret < 0) ? -1 : ret / 655;
02922 }
02923 
02932 vector<InputInfo> TVRec::GetFreeInputs(
02933     const vector<uint> &excluded_cardids) const
02934 {
02935     vector<InputInfo> list;
02936     if (channel)
02937         list = channel->GetFreeInputs(excluded_cardids);
02938     return list;
02939 }
02940 
02944 QString TVRec::GetInput(void) const
02945 {
02946     if (channel)
02947         return channel->GetCurrentInput();
02948     return QString::null;
02949 }
02950 
02959 QString TVRec::SetInput(QString input, uint requestType)
02960 {
02961     QMutexLocker lock(&stateChangeLock);
02962     QString origIn = input;
02963     VERBOSE(VB_RECORD, LOC + "SetInput("<<input<<") -- begin");
02964 
02965     if (!channel)
02966     {
02967         VERBOSE(VB_RECORD, LOC + "SetInput() -- end  no channel class");
02968         return QString::null;
02969     }
02970 
02971     input = (input == "SwitchToNextInput") ? channel->GetNextInput() : input;
02972 
02973     if (input == channel->GetCurrentInput())
02974     {
02975         VERBOSE(VB_RECORD, LOC + "SetInput("<<origIn<<":"<<input
02976                 <<") -- end  nothing to do");
02977         return input;
02978     }
02979 
02980     QString name = channel->GetNextInputStartChan();
02981     
02982     // Detect tuning request type if needed
02983     if (requestType & kFlagDetect)
02984     {
02985         WaitForEventThreadSleep();
02986         requestType = lastTuningRequest.flags & (kFlagRec | kFlagNoRec);
02987     }
02988 
02989     // Clear the RingBuffer reset flag, in case we wait for a reset below
02990     ClearFlags(kFlagRingBufferReady);
02991 
02992     // Actually add the tuning request to the queue, and
02993     // then wait for it to start tuning
02994     tuningRequests.enqueue(TuningRequest(requestType, name, input));
02995     WaitForEventThreadSleep();
02996 
02997     // If we are using a recorder, wait for a RingBuffer reset
02998     if (requestType & kFlagRec)
02999     {
03000         while (!HasFlags(kFlagRingBufferReady))
03001             WaitForEventThreadSleep();
03002     }
03003     VERBOSE(VB_RECORD, LOC + "SetInput("<<origIn<<":"<<input<<") -- end");
03004 
03005     return GetInput();
03006 }
03007 
03017 void TVRec::SetChannel(QString name, uint requestType)
03018 {
03019     QMutexLocker lock(&stateChangeLock);
03020     VERBOSE(VB_CHANNEL, LOC + QString("SetChannel(%1) -- begin").arg(name));
03021 
03022     // Detect tuning request type if needed
03023     if (requestType & kFlagDetect)
03024     {
03025         WaitForEventThreadSleep();
03026         requestType = lastTuningRequest.flags & (kFlagRec | kFlagNoRec);
03027     }
03028 
03029     // Clear the RingBuffer reset flag, in case we wait for a reset below
03030     ClearFlags(kFlagRingBufferReady);
03031 
03032     // Actually add the tuning request to the queue, and
03033     // then wait for it to start tuning
03034     tuningRequests.enqueue(TuningRequest(requestType, name));
03035     WaitForEventThreadSleep();
03036 
03037     // If we are using a recorder, wait for a RingBuffer reset
03038     if (requestType & kFlagRec)
03039     {
03040         while (!HasFlags(kFlagRingBufferReady))
03041             WaitForEventThreadSleep();
03042     }
03043     VERBOSE(VB_CHANNEL, LOC + QString("SetChannel(%1) -- end").arg(name));
03044 }
03045 
03052 void TVRec::GetNextProgram(int direction,
03053                            QString &title,       QString &subtitle,
03054                            QString &desc,        QString &category,
03055                            QString &starttime,   QString &endtime,
03056                            QString &callsign,    QString &iconpath,
03057                            QString &channum,     QString &chanidStr,
03058                            QString &seriesid,    QString &programid)
03059 {
03060     QString compare     = "<=";
03061     QString sortorder   = "desc";
03062     uint     chanid     = 0;
03063     uint sourceChanid   = 0;
03064 
03065     if (!chanidStr.isEmpty())
03066     {
03067         sourceChanid = chanidStr.toUInt();
03068         chanid = sourceChanid;
03069 
03070         if (BROWSE_UP == direction)
03071             chanid = channel->GetNextChannel(chanid, CHANNEL_DIRECTION_UP);
03072         else if (BROWSE_DOWN == direction)
03073             chanid = channel->GetNextChannel(chanid, CHANNEL_DIRECTION_DOWN);
03074         else if (BROWSE_FAVORITE == direction)
03075             chanid = channel->GetNextChannel(chanid, CHANNEL_DIRECTION_FAVORITE
03076 );
03077         else if (BROWSE_LEFT == direction)
03078         {
03079             compare = "<";
03080         }
03081         else if (BROWSE_RIGHT == direction)
03082         {
03083             compare = ">";
03084             sortorder = "asc";
03085         }
03086     }
03087 
03088     if (!chanid)
03089     {
03090         if (BROWSE_SAME == direction)
03091             chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_SAME);
03092         else if (BROWSE_UP == direction)
03093             chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_UP);
03094         else if (BROWSE_DOWN == direction)
03095             chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_DOWN);
03096         else if (BROWSE_FAVORITE == direction)
03097             chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_FAVORITE);
03098         else if (BROWSE_LEFT == direction)
03099         {
03100             chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_SAME);
03101             compare = "<";
03102         }
03103         else if (BROWSE_RIGHT == direction)
03104         {
03105                 chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_SAME);
03106             compare = ">";
03107             sortorder = "asc";
03108         }
03109     }
03110 
03111     QString querystr = QString(
03112         "SELECT title,     subtitle, description, category, "
03113         "       starttime, endtime,  callsign,    icon,     "
03114         "       channum,   seriesid, programid "
03115         "FROM program, channel "
03116         "WHERE program.chanid = channel.chanid AND "
03117         "      channel.chanid = :CHANID        AND "
03118         "      starttime %1 :STARTTIME "
03119         "ORDER BY starttime %2 "
03120         "LIMIT 1").arg(compare).arg(sortorder);
03121 
03122     MSqlQuery query(MSqlQuery::InitCon());
03123     query.prepare(querystr);
03124     query.bindValue(":CHANID",    chanid);
03125     query.bindValue(":STARTTIME", starttime);
03126 
03127     // Clear everything now in case either query fails.
03128     title     = subtitle  = desc      = category  = "";
03129     starttime = endtime   = callsign  = iconpath  = "";
03130     channum   = chanidStr = seriesid  = programid = "";
03131 
03132     // We already know the new chanid..
03133     chanidStr = QString::number(chanid);
03134 
03135     // Try to get the program info
03136     if (!query.exec() && !query.isActive())
03137     {
03138         MythContext::DBError("GetNextProgram -- get program info", query);
03139     }
03140     else if (query.next())
03141     {
03142         title     = QString::fromUtf8(query.value(0).toString());
03143         subtitle  = QString::fromUtf8(query.value(1).toString());
03144         desc      = QString::fromUtf8(query.value(2).toString());
03145         category  = QString::fromUtf8(query.value(3).toString());
03146         starttime = query.value(4).toString();
03147         endtime   = query.value(5).toString();
03148         callsign  = query.value(6).toString();
03149         iconpath  = query.value(7).toString();
03150         channum   = query.value(8).toString();
03151         seriesid  = query.value(9).toString();
03152         programid = query.value(10).toString();
03153 
03154         return;
03155     }
03156 
03157     // Couldn't get program info, so get the channel info instead
03158     query.prepare(
03159         "SELECT channum, callsign, icon "
03160         "FROM channel "
03161         "WHERE chanid = :CHANID");
03162     query.bindValue(":CHANID", chanid);
03163 
03164     if (!query.exec() || !query.isActive())
03165     {
03166         MythContext::DBError("GetNextProgram -- get channel info", query);
03167     }
03168     else if (query.next())
03169     {
03170         channum  = query.value(0).toString();
03171         callsign = query.value(1).toString();
03172         iconpath = query.value(2).toString();
03173     }
03174 }
03175 
03176 bool TVRec::GetChannelInfo(uint &chanid, uint &sourceid,
03177                            QString &callsign, QString &channum,
03178                            QString &channame, QString &xmltvid) const
03179 {
03180     callsign = "";
03181     channum  = "";
03182     channame = "";
03183     xmltvid  = "";
03184 
03185     if ((!chanid || !sourceid) && !channel)
03186         return false;
03187 
03188     if (!chanid)
03189         chanid = (uint) max(channel->GetChanID(), 0);
03190 
03191     if (!sourceid)
03192         sourceid = channel->GetCurrentSourceID();
03193 
03194     MSqlQuery query(MSqlQuery::InitCon());
03195     query.prepare(
03196         "SELECT callsign, channum, name, xmltvid "
03197         "FROM channel "
03198         "WHERE chanid = :CHANID");
03199     query.bindValue(":CHANID", chanid);
03200     if (!query.exec() || !query.isActive())
03201     {
03202         MythContext::DBError("GetChannelInfo", query);
03203         return false;
03204     }
03205 
03206     if (!query.next())
03207         return false;
03208 
03209     callsign = query.value(0).toString();
03210     channum  = query.value(1).toString();
03211     channame = query.value(2).toString();
03212     xmltvid  = query.value(3).toString();
03213 
03214     return true;
03215 }
03216 
03217 bool TVRec::SetChannelInfo(uint chanid, uint sourceid,
03218                            QString oldchannum,
03219                            QString callsign, QString channum,
03220                            QString channame, QString xmltvid)
03221 {
03222     if (!chanid || !sourceid || channum.isEmpty())
03223         return false;
03224 
03225     MSqlQuery query(MSqlQuery::InitCon());
03226     query.prepare(
03227         "UPDATE channel "
03228         "SET callsign = :CALLSIGN, "
03229         "    channum  = :CHANNUM,  "
03230         "    name     = :CHANNAME, "
03231         "    xmltvid  = :XMLTVID   "
03232         "WHERE chanid   = :CHANID AND "
03233         "      sourceid = :SOURCEID");
03234     query.bindValue(":CALLSIGN", callsign);
03235     query.bindValue(":CHANNUM",  channum);
03236     query.bindValue(":CHANNAME", channame);
03237     query.bindValue(":XMLTVID",  xmltvid);
03238     query.bindValue(":CHANID",   chanid);
03239     query.bindValue(":SOURCEID", sourceid);
03240 
03241     if (!query.exec())
03242     {
03243         MythContext::DBError("SetChannelInfo", query);
03244         return false;
03245     }
03246 
03247     if (channel)
03248         channel->Renumber(sourceid, oldchannum, channum);
03249 
03250     return true;
03251 }
03252 
03256 void TVRec::SetRingBuffer(RingBuffer *rb)
03257 {
03258     QMutexLocker lock(&stateChangeLock);
03259 
03260     RingBuffer *rb_old = ringBuffer;
03261     ringBuffer = rb;
03262 
03263     if (rb_old && (rb_old != rb))
03264     {
03265         if (HasFlags(kFlagDummyRecorderRunning))
03266             ClearFlags(kFlagDummyRecorderRunning);
03267         delete rb_old;
03268     }
03269 }
03270 
03271 void TVRec::RingBufferChanged(RingBuffer *rb, ProgramInfo *pginfo)
03272 {
03273     VERBOSE(VB_IMPORTANT, LOC + "RingBufferChanged()");
03274 
03275     SetRingBuffer(rb);
03276 
03277     if (pginfo)
03278     {
03279         if (curRecording)
03280         {
03281             FinishedRecording(curRecording);
03282             curRecording->MarkAsInUse(false);
03283             delete curRecording;
03284         }
03285         curRecording = new ProgramInfo(*pginfo);
03286         curRecording->MarkAsInUse(true, "recorder");
03287     }
03288 
03289     m_switchingBuffer = false;
03290 }
03291 
03292 QString TVRec::TuningGetChanNum(const TuningRequest &request,
03293                                 QString &input) const
03294 {
03295     QString channum = QString::null;
03296 
03297     if (request.program)
03298     {
03299         request.program->GetChannel(channum, input);
03300         return channum;
03301     }
03302 
03303     channum = request.channel;
03304     input   = request.input;
03305 
03306     // If this is Live TV startup, we need a channel...
03307     if (channum.isEmpty() && (request.flags & kFlagLiveTV))
03308     {
03309         if (!LiveTVStartChannel.isEmpty())
03310             channum = LiveTVStartChannel;
03311         else
03312         {
03313             input   = genOpt.defaultinput;
03314             channum = GetStartChannel(cardid, input);
03315         }
03316     }
03317     if (request.flags & kFlagLiveTV)
03318         channel->Init(input, channum, false);
03319 
03320     if (channel && !channum.isEmpty() && (channum.find("NextChannel") >= 0))
03321     {
03322         int dir     = channum.right(channum.length() - 12).toInt();
03323         uint chanid = channel->GetNextChannel(0, dir);
03324         channum     = ChannelUtil::GetChanNum(chanid);
03325     }
03326 
03327     return channum;
03328 }
03329 
03330 bool TVRec::TuningOnSameMultiplex(TuningRequest &request)
03331 {
03332     if ((request.flags & kFlagAntennaAdjust) || !GetDTVRecorder() ||
03333         signalMonitor || !channel || !channel->IsOpen())
03334     {
03335         return false;
03336     }
03337 
03338     if (!request.input.isEmpty())
03339         return false;
03340 
03341     uint    sourceid   = channel->GetCurrentSourceID();
03342     QString oldchannum = channel->GetCurrentName();
03343     QString newchannum = QDeepCopy<QString>(request.channel);
03344 
03345     if (ChannelUtil::IsOnSameMultiplex(sourceid, newchannum, oldchannum))
03346     {
03347         MPEGStreamData *mpeg = GetDTVRecorder()->GetStreamData();
03348         ATSCStreamData *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
03349 
03350         if (atsc)
03351         {
03352             uint major, minor = 0;
03353             ChannelUtil::GetATSCChannel(sourceid, newchannum, major, minor);
03354 
03355             if (minor && atsc->HasChannel(major, minor))
03356             {
03357                 request.majorChan = major;
03358                 request.minorChan = minor;
03359                 return true;
03360             }
03361         }
03362 
03363         if (mpeg)
03364         {
03365             uint progNum = ChannelUtil::GetProgramNumber(sourceid, newchannum);
03366             if (mpeg->HasProgram(progNum))
03367             {
03368                 request.progNum = progNum;
03369                 return true;
03370             }
03371         }
03372     }
03373 
03374     return false;
03375 }
03376 
03384 void TVRec::HandleTuning(void)
03385 {
03386     if (tuningRequests.size())
03387     {
03388         TuningRequest request = tuningRequests.front();
03389         VERBOSE(VB_RECORD, LOC + "Request: "<<request.toString());
03390 
03391         QString input;
03392         request.channel = TuningGetChanNum(request, input);
03393         request.input   = input;
03394 
03395         if (TuningOnSameMultiplex(request))
03396             VERBOSE(VB_PLAYBACK, LOC + "On same multiplex");
03397 
03398         TuningShutdowns(request);
03399 
03400         // The dequeue isn't safe to do until now because we
03401         // release the stateChangeLock to teardown a recorder
03402         tuningRequests.dequeue();
03403 
03404         // Now we start new stuff
03405         if (request.flags & (kFlagRecording|kFlagLiveTV|
03406                              kFlagEITScan|kFlagAntennaAdjust))
03407         {
03408             if (!recorder)
03409             {
03410                 VERBOSE(VB_RECORD, LOC +
03411                         "No recorder yet, calling TuningFrequency");
03412                 TuningFrequency(request);
03413             }
03414             else
03415             {
03416                 VERBOSE(VB_RECORD, LOC + "Waiting for recorder pause..");
03417                 SetFlags(kFlagWaitingForRecPause);
03418             }
03419         }
03420         lastTuningRequest = request;
03421     }
03422 
03423     if (HasFlags(kFlagWaitingForRecPause))
03424     {
03425         if (!recorder->IsPaused())
03426             return;
03427 
03428         ClearFlags(kFlagWaitingForRecPause);
03429 #ifdef USING_HDHOMERUN
03430         if (GetHDHRRecorder())
03431         {
03432             // We currently need to close the file descriptor for
03433             // HDHomeRun signal monitoring to work.
03434             GetHDHRRecorder()->Close();
03435             GetHDHRRecorder()->SetRingBuffer(NULL);
03436         }
03437 #endif // USING_HDHOMERUN
03438         VERBOSE(VB_RECORD, LOC + "Recorder paused, calling TuningFrequency");
03439         TuningFrequency(lastTuningRequest);
03440     }
03441 
03442     MPEGStreamData *streamData = NULL;
03443     if (HasFlags(kFlagWaitingForSignal) && !(streamData = TuningSignalCheck()))
03444         return;
03445 
03446     if (HasFlags(kFlagNeedToStartRecorder))
03447     { 
03448         if (recorder)
03449             TuningRestartRecorder();
03450         else
03451             TuningNewRecorder(streamData);
03452 
03453         // If we got this far it is safe to set a new starting channel...
03454         if (channel)
03455             channel->StoreInputChannels();
03456     }
03457 }
03458 
03462 uint TVRec::TuningCheckForHWChange(const TuningRequest &request,
03463                                    QString &channum,
03464                                    QString &inputname)
03465 {
03466     if (!channel)
03467         return 0;
03468 
03469     uint curCardID = 0, newCardID = 0;
03470     channum   = request.channel;
03471     inputname = request.input;
03472 
03473     if (request.program)
03474         request.program->GetChannel(channum, inputname);
03475 
03476     if (!channum.isEmpty() && inputname.isEmpty())
03477         channel->CheckChannel(channum, inputname);
03478 
03479     if (!inputname.isEmpty())
03480     {
03481         int current_input = channel->GetCurrentInputNum();
03482         int new_input     = channel->GetInputByName(inputname);
03483         curCardID = channel->GetInputCardID(current_input);
03484         newCardID = channel->GetInputCardID(new_input);
03485         VERBOSE(VB_IMPORTANT, LOC<<"HW Tuner: "<<curCardID<<"->"<<newCardID);
03486     }
03487 
03488     if (curCardID != newCardID)
03489     {    
03490         if (channum.isEmpty())
03491             channum = GetStartChannel(newCardID, inputname);
03492         return newCardID;
03493     }
03494 
03495     return 0;
03496 }
03497 
03502 void TVRec::TuningShutdowns(const TuningRequest &request)
03503 {
03504     QString channum, inputname;
03505     uint newCardID = TuningCheckForHWChange(request, channum, inputname);
03506 
03507     if (!(request.flags & kFlagEITScan) && HasFlags(kFlagEITScannerRunning))
03508     {
03509         scanner->StopActiveScan();
03510         ClearFlags(kFlagEITScannerRunning);
03511     }
03512 
03513     if (scanner && !request.IsOnSameMultiplex())
03514         scanner->StopPassiveScan();
03515 
03516     if (HasFlags(kFlagSignalMonitorRunning))
03517     {
03518         MPEGStreamData *sd = NULL;
03519         if (GetDTVSignalMonitor())
03520             sd = GetDTVSignalMonitor()->GetStreamData();
03521         TeardownSignalMonitor();
03522         ClearFlags(kFlagSignalMonitorRunning);
03523 
03524         // Delete StreamData if it is not in use by the recorder.
03525         MPEGStreamData *rec_sd = NULL;
03526         if (GetDTVRecorder())
03527             rec_sd = GetDTVRecorder()->GetStreamData();
03528         if (sd && (sd != rec_sd))
03529             delete sd;
03530     }
03531     if (HasFlags(kFlagWaitingForSignal))
03532         ClearFlags(kFlagWaitingForSignal);
03533 
03534     // At this point any waits are canceled.
03535 
03536     if (newCardID || (request.flags & kFlagNoRec))
03537     {
03538         if (HasFlags(kFlagDummyRecorderRunning))
03539         {
03540             ClearFlags(kFlagDummyRecorderRunning);
03541             FinishedRecording(curRecording);
03542             curRecording->MarkAsInUse(false);
03543         }
03544 
03545         if (request.flags & kFlagCloseRec)
03546             FinishedRecording(lastTuningRequest.program);
03547 
03548         if (HasFlags(kFlagRecorderRunning))
03549         {
03550             stateChangeLock.unlock();
03551             TeardownRecorder(request.flags & kFlagKillRec);
03552             stateChangeLock.lock();
03553             ClearFlags(kFlagRecorderRunning);
03554         }
03555         // At this point the recorders are shut down
03556 
03557         CloseChannel();
03558         // At this point the channel is shut down
03559     }
03560     
03561     // handle HW change for digital/analog cards
03562     if (newCardID)
03563     {
03564         VERBOSE(VB_IMPORTANT, "Recreating channel...");
03565         channel->Close();
03566         delete channel;
03567         channel = NULL;
03568 
03569         GetDevices(newCardID, genOpt, dvbOpt, fwOpt, dboxOpt);
03570         genOpt.defaultinput = inputname;
03571         CreateChannel(channum);
03572         if (!(request.flags & kFlagNoRec))
03573             channel->Open();
03574     }
03575 
03576     if (ringBuffer && (request.flags & kFlagKillRingBuffer))
03577     {
03578         VERBOSE(VB_RECORD, LOC + "Tearing down RingBuffer");
03579         SetRingBuffer(NULL);
03580         // At this point the ringbuffer is shut down
03581     }
03582 
03583     // Clear pending actions from last request
03584     ClearFlags(kFlagPendingActions);
03585 }
03586 
03604 void TVRec::TuningFrequency(const TuningRequest &request)
03605 {
03606     DTVChannel *dtvchan = GetDTVChannel();
03607     if (dtvchan)
03608     {
03609         MPEGStreamData *mpeg = NULL;
03610 
03611         if (GetDTVRecorder())
03612             mpeg = GetDTVRecorder()->GetStreamData();
03613 
03614         const QString tuningmode = (HasFlags(kFlagEITScannerRunning)) ?
03615             dtvchan->GetSIStandard() :
03616             dtvchan->GetSuggestedTuningMode(
03617                 kState_WatchingLiveTV == internalState);
03618 
03619         dtvchan->SetTuningMode(tuningmode);
03620 
03621         if (request.minorChan && (tuningmode == "atsc"))
03622         {
03623             channel->SetChannelByString(request.channel);
03624 
03625             ATSCStreamData *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
03626             if (atsc)
03627                 atsc->SetDesiredChannel(request.majorChan, request.minorChan);
03628         }
03629         else if (request.progNum >= 0)
03630         {
03631             channel->SetChannelByString(request.channel);
03632 
03633             if (mpeg)
03634                 mpeg->SetDesiredProgram(request.progNum);
03635         }
03636     }
03637 
03638     if (request.IsOnSameMultiplex())
03639     {
03640         QStringList slist;
03641         slist<<"message"<<QObject::tr("On known multiplex...");
03642         MythEvent me(QString("SIGNAL %1").arg(cardid), slist);
03643         gContext->dispatch(me);
03644 
03645         SetFlags(kFlagNeedToStartRecorder);
03646         return;
03647     }
03648 
03649     QString input   = request.input;
03650     QString channum = request.channel;
03651 
03652     bool ok = false;
03653     if (channel)
03654         channel->Open();
03655     else
03656         ok = true;
03657 
03658     if (channel && !channum.isEmpty())
03659     {
03660         if (!input.isEmpty())
03661             ok = channel->SwitchToInput(input, channum);
03662         else
03663             ok = channel->SetChannelByString(channum);
03664     }
03665 
03666     if (!ok)
03667     {
03668         if (!(request.flags & kFlagLiveTV) || !(request.flags & kFlagEITScan))
03669         {
03670             if (curRecording)
03671                 curRecording->recstatus = rsFailed;
03672 
03673             VERBOSE(VB_IMPORTANT, LOC_ERR +
03674                     QString("Failed to set channel to %1. "
03675                             "Reverting to kState_None")
03676                     .arg(channum));
03677             if (kState_None != internalState)
03678                 ChangeState(kState_None);
03679             else
03680                 tuningRequests.enqueue(TuningRequest(kFlagKillRec));
03681             return;
03682         }
03683         else
03684         {
03685             VERBOSE(VB_IMPORTANT, LOC_ERR +
03686                     QString("Failed to set channel to %1.").arg(channum));
03687         }
03688     }
03689 
03690     bool livetv = request.flags & kFlagLiveTV;
03691     bool antadj = request.flags & kFlagAntennaAdjust;
03692     bool use_sm = SignalMonitor::IsRequired(genOpt.cardtype);
03693     bool use_dr = use_sm && (livetv || antadj);
03694     bool has_dummy = false;
03695 
03696     if (use_dr)
03697     {
03698         // We need there to be a ringbuffer for these modes
03699         bool ok;
03700         ProgramInfo *tmp = pseudoLiveTVRecording;
03701         pseudoLiveTVRecording = NULL;
03702 
03703         tvchain->SetCardType("DUMMY");
03704 
03705         if (!ringBuffer)
03706             ok = CreateLiveTVRingBuffer();
03707         else
03708             ok = SwitchLiveTVRingBuffer(true, false);
03709         pseudoLiveTVRecording = tmp;
03710 
03711         tvchain->SetCardType(genOpt.cardtype);
03712 
03713         if (!ok)
03714         {
03715             VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to create RingBuffer 1");
03716             return;
03717         }
03718 
03719         has_dummy = true;
03720     }
03721 
03722     // Start signal monitoring for devices capable of monitoring
03723     if (use_sm)
03724     {
03725         VERBOSE(VB_RECORD, LOC + "Starting Signal Monitor");
03726         bool error = false;
03727         if (!SetupSignalMonitor(!antadj, livetv | antadj))
03728         {
03729             VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to setup signal monitor");
03730             if (signalMonitor)
03731             {
03732                 signalMonitor->deleteLater();
03733                 signalMonitor = NULL;
03734             }
03735 
03736             // pretend the signal monitor is running to prevent segfault
03737             SetFlags(kFlagSignalMonitorRunning);
03738             ClearFlags(kFlagWaitingForSignal);
03739             error = true;
03740         }
03741 
03742         if (signalMonitor)
03743         {
03744             if (request.flags & kFlagEITScan)
03745             {
03746                 GetDTVSignalMonitor()->GetStreamData()->
03747                     SetVideoStreamsRequired(0);
03748                 GetDTVSignalMonitor()->IgnoreEncrypted(true);
03749             }
03750 
03751             SetFlags(kFlagSignalMonitorRunning);
03752             ClearFlags(kFlagWaitingForSignal);
03753             if (!antadj)
03754                 SetFlags(kFlagWaitingForSignal);
03755         }
03756 
03757         if (has_dummy && ringBuffer)
03758         {
03759             // Make sure recorder doesn't point to bogus ringbuffer before
03760             // it is potentially restarted without a new ringbuffer, if
03761             // the next channel won't tune and the user exits LiveTV.
03762             if (recorder)
03763                 recorder->SetRingBuffer(NULL);
03764 
03765             SetFlags(kFlagDummyRecorderRunning);
03766             VERBOSE(VB_RECORD, "DummyDTVRecorder -- started");
03767             SetFlags(kFlagRingBufferReady);
03768         }
03769 
03770         // if we had problems starting the signal monitor,
03771         // we don't want to start the recorder...
03772         if (error)
03773             return;
03774     }
03775 
03776     // Request a recorder, if the command is a recording command
03777     ClearFlags(kFlagNeedToStartRecorder);
03778     if (request.flags & kFlagRec && !antadj)
03779         SetFlags(kFlagNeedToStartRecorder);
03780 }
03781 
03789 MPEGStreamData *TVRec::TuningSignalCheck(void)
03790 {
03791     if (!signalMonitor->IsAllGood())
03792         return NULL;
03793 
03794     VERBOSE(VB_RECORD, LOC + "Got good signal");
03795 
03796     // grab useful data from DTV signal monitor before we kill it...
03797     MPEGStreamData *streamData = NULL;
03798     if (GetDTVSignalMonitor())
03799         streamData = GetDTVSignalMonitor()->GetStreamData();
03800 
03801     if (!HasFlags(kFlagEITScannerRunning))
03802     {
03803         // shut down signal monitoring
03804         TeardownSignalMonitor();
03805         ClearFlags(kFlagSignalMonitorRunning);
03806     }
03807     ClearFlags(kFlagWaitingForSignal);
03808 
03809     if (streamData)
03810     {
03811         DVBStreamData *dsd = dynamic_cast<DVBStreamData*>(streamData);
03812         if (dsd)
03813             dsd->SetDishNetEIT(is_dishnet_eit(cardid));
03814         if (!get_use_eit(GetCaptureCardNum()))
03815         {
03816             VERBOSE(VB_EIT, LOC + "EIT scanning disabled "
03817                     "for all sources on this card.");
03818         }
03819         else if (scanner)
03820             scanner->StartPassiveScan(channel, streamData, eitIgnoresSource);
03821     }
03822 
03823     return streamData;
03824 }
03825 
03826 static int init_jobs(const ProgramInfo *rec, RecordingProfile &profile,
03827                       bool on_host, bool transcode_bfr_comm, bool on_line_comm)
03828 {
03829     if (!rec)
03830         return 0; // no jobs for Live TV recordings..
03831 
03832     int jobs = 0; // start with no jobs
03833 
03834     // grab standard jobs flags from program info
03835     JobQueue::AddJobsToMask(rec->GetAutoRunJobs(), jobs);
03836 
03837     // disable commercial flagging on PBS, BBC, etc.
03838     if (rec->chancommfree)
03839         JobQueue::RemoveJobsFromMask(JOB_COMMFLAG, jobs);
03840 
03841     // disable transcoding if the profile does not allow auto transcoding
03842     const Setting *autoTrans = profile.byName("autotranscode");
03843     if ((!autoTrans) || (autoTrans->getValue().toInt() == 0))
03844         JobQueue::RemoveJobsFromMask(JOB_TRANSCODE, jobs);
03845 
03846     // is commercial flagging enabled, and is on-line comm flagging enabled?
03847     bool rt = JobQueue::JobIsInMask(JOB_COMMFLAG, jobs) && on_line_comm;
03848     // also, we either need transcoding to be disabled or 
03849     // we need to be allowed to commercial flag before transcoding?
03850     rt &= JobQueue::JobIsNotInMask(JOB_TRANSCODE, jobs) ||
03851         !transcode_bfr_comm;
03852     if (rt)
03853     {
03854         // queue up real-time (i.e. on-line) commercial flagging.
03855         QString host = (on_host) ? gContext->GetHostName() : "";
03856         JobQueue::QueueJob(JOB_COMMFLAG,
03857                            rec->chanid, rec->recstartts, "", "",
03858                            host, JOB_LIVE_REC);
03859 
03860         // don't do regular comm flagging, we won't need it.
03861         JobQueue::RemoveJobsFromMask(JOB_COMMFLAG, jobs);
03862     }
03863 
03864     return jobs;
03865 }
03866 
03867 static QString load_profile(QString cardtype, void *tvchain,
03868                             ProgramInfo *rec, RecordingProfile &profile)
03869 {
03870     // Determine the correct recording profile.
03871     // In LiveTV mode use "Live TV" profile, otherwise use the
03872     // recording's specified profile. If the desired profile can't
03873     // be found, fall back to the "Default" profile for card type.
03874     QString profileName = "Live TV";
03875     if (!tvchain && rec)
03876         profileName = rec->GetScheduledRecording()->getProfileName();
03877 
03878     if (!profile.loadByType(profileName, cardtype))
03879     {
03880         profileName = "Default";
03881         profile.loadByType(profileName, cardtype);
03882     }
03883 
03884     VERBOSE(VB_RECORD, QString("Using profile '%1' to record")
03885             .arg(profileName));
03886 
03887     return profileName;
03888 }
03889 
03893 void TVRec::TuningNewRecorder(MPEGStreamData *streamData)
03894 {
03895     VERBOSE(VB_RECORD, LOC + "Starting Recorder");
03896 
03897     bool had_dummyrec = false;
03898     if (HasFlags(kFlagDummyRecorderRunning))
03899     {
03900         ClearFlags(kFlagDummyRecorderRunning);
03901         FinishedRecording(curRecording);
03902         curRecording->MarkAsInUse(false);
03903         had_dummyrec = true;
03904     }
03905 
03906     ProgramInfo *rec = lastTuningRequest.program;
03907 
03908     RecordingProfile profile;
03909     QString profileName = load_profile(genOpt.cardtype, tvchain, rec, profile);
03910 
03911     if (tvchain)
03912     {
03913         bool ok;
03914         if (!ringBuffer)
03915         {
03916             ok = CreateLiveTVRingBuffer();
03917             SetFlags(kFlagRingBufferReady);
03918         }
03919         else
03920             ok = SwitchLiveTVRingBuffer(true, !had_dummyrec && recorder);
03921         if (!ok)
03922         {
03923             VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to create RingBuffer 2");
03924             goto err_ret;
03925         }
03926         rec = tvchain->GetProgramAt(-1);
03927     }
03928 
03929     if (lastTuningRequest.flags & kFlagRecording)
03930     {
03931         SetRingBuffer(new RingBuffer(rec->GetFileName(), true));
03932         if (!ringBuffer->IsOpen())
03933         {
03934             VERBOSE(VB_IMPORTANT, LOC_ERR +
03935                     QString("RingBuffer '%1' not open...")
03936                     .arg(rec->GetFileName()));
03937             SetRingBuffer(NULL);
03938             ClearFlags(kFlagPendingActions);
03939             goto err_ret;
03940         }
03941     }
03942 
03943     if (!ringBuffer)
03944     {
03945         VERBOSE(VB_IMPORTANT, LOC_ERR + QString(
03946                     "Failed to start recorder!  ringBuffer is NULL\n"
03947                     "\t\t\t\t  Tuning request was %1\n")
03948                 .arg(lastTuningRequest.toString()));
03949 
03950         if (HasFlags(kFlagLiveTV))
03951         {
03952             QString message = QString("QUIT_LIVETV %1").arg(cardid);
03953             MythEvent me(message);
03954             gContext->dispatch(me);
03955         }
03956         goto err_ret;
03957     }
03958 
03959     if (channel && genOpt.cardtype == "MJPEG")
03960         channel->Close(); // Needed because of NVR::MJPEGInit()
03961 
03962     if (!SetupRecorder(profile))
03963     {
03964         VERBOSE(VB_IMPORTANT, LOC_ERR + QString(
03965                     "Failed to start recorder!\n"
03966                     "\t\t\t\t  Tuning request was %1\n")
03967                 .arg(lastTuningRequest.toString()));
03968 
03969         if (HasFlags(kFlagLiveTV))
03970         {
03971             QString message = QString("QUIT_LIVETV %1").arg(cardid);
03972             MythEvent me(message);
03973             gContext->dispatch(me);
03974         }
03975         TeardownRecorder(true);
03976         goto err_ret;
03977     }
03978 
03979     if (GetDTVRecorder() && streamData)
03980         GetDTVRecorder()->SetStreamData(streamData);
03981 
03982     if (channel && genOpt.cardtype == "MJPEG")
03983         channel->Open(); // Needed because of NVR::MJPEGInit()
03984 
03985     if (rec)
03986         recorder->SetRecording(rec);
03987 
03988     // Setup for framebuffer capture devices..
03989     if (channel)
03990     {
03991         SetVideoFiltersForChannel(channel->GetCurrentSourceID(),
03992                                   channel->GetCurrentName());
03993     }
03994 
03995 #ifdef USING_V4L 
03996     if (GetV4LChannel())
03997     {
03998         channel->InitPictureAttributes();
03999         CloseChannel();
04000     }
04001 #endif
04002 
04003     pthread_create(&recorder_thread, NULL, TVRec::RecorderThread, recorder);
04004 
04005     // Wait for recorder to start.
04006     stateChangeLock.unlock();
04007     while (!recorder->IsRecording() && !recorder->IsErrored())
04008         usleep(5 * 1000);
04009     stateChangeLock.lock();
04010 
04011     if (GetV4LChannel())
04012         channel->SetFd(recorder->GetVideoFd());
04013 
04014     SetFlags(kFlagRecorderRunning | kFlagRingBufferReady);
04015 
04016     if (!tvchain)
04017         autoRunJobs = init_jobs(rec, profile, runJobOnHostOnly,
04018                                 transcodeFirst, earlyCommFlag);
04019 
04020     ClearFlags(kFlagNeedToStartRecorder);
04021     if (tvchain)
04022         delete rec;
04023     return;
04024 
04025   err_ret:
04026     ChangeState(kState_None);
04027     if (tvchain)
04028         delete rec;
04029 }
04030 
04034 void TVRec::TuningRestartRecorder(void)
04035 {
04036     VERBOSE(VB_RECORD, LOC + "Restarting Recorder");
04037 
04038     bool had_dummyrec = false;
04039     if (HasFlags(kFlagDummyRecorderRunning))
04040     {
04041         ClearFlags(kFlagDummyRecorderRunning);
04042         had_dummyrec = true;
04043     }
04044 
04045     if (curRecording)
04046     {
04047         FinishedRecording(curRecording);
04048         curRecording->MarkAsInUse(false);
04049 
04050         if (pseudoLiveTVRecording)
04051         {
04052             int secsSince = curRecording->recstartts.secsTo(
04053                                               QDateTime::currentDateTime());
04054             if (secsSince < 120)
04055             {
04056                 JobQueue::RemoveJobsFromMask(JOB_COMMFLAG, autoRunJobs);
04057                 JobQueue::RemoveJobsFromMask(JOB_TRANSCODE, autoRunJobs);
04058             }
04059 
04060             if (autoRunJobs)
04061                 JobQueue::QueueRecordingJobs(curRecording, autoRunJobs);
04062         }
04063     }
04064 
04065     SwitchLiveTVRingBuffer(true, !had_dummyrec);
04066 
04067     if (had_dummyrec)
04068     {
04069         recorder->SetRingBuffer(ringBuffer);
04070         ProgramInfo *progInfo = tvchain->GetProgramAt(-1);
04071         recorder->SetRecording(progInfo);
04072         delete progInfo;
04073     }
04074     recorder->Reset();
04075 
04076 #ifdef USING_HDHOMERUN
04077     if (GetHDHRRecorder())
04078     {
04079         pauseNotify = false;
04080         GetHDHRRecorder()->Close();
04081         pauseNotify = true;
04082         GetHDHRRecorder()->Open();
04083         GetHDHRRecorder()->StartData();
04084     }
04085 #endif // USING_HDHOMERUN
04086 
04087     // Set file descriptor of channel from recorder for V4L
04088     channel->SetFd(recorder->GetVideoFd());
04089 
04090     // Some recorders unpause on Reset, others do not...
04091     recorder->Unpause();
04092 
04093     if (pseudoLiveTVRecording)
04094     {
04095         ProgramInfo *rcinfo1 = pseudoLiveTVRecording; 
04096         QString msg1 = QString("Recording: %1 %2 %3 %4")
04097             .arg(rcinfo1->title).arg(rcinfo1->chanid)
04098             .arg(rcinfo1->recstartts.toString())
04099             .arg(rcinfo1->recendts.toString());
04100         ProgramInfo *rcinfo2 = tvchain->GetProgramAt(-1);
04101         QString msg2 = QString("Recording: %1 %2 %3 %4")
04102             .arg(rcinfo2->title).arg(rcinfo2->chanid)
04103             .arg(rcinfo2->recstartts.toString())
04104             .arg(rcinfo2->recendts.toString());
04105         delete rcinfo2;
04106         VERBOSE(VB_RECORD, LOC + "Pseudo LiveTV recording starting." +
04107                 "\n\t\t\t" + msg1 + "\n\t\t\t" + msg2);
04108 
04109         curRecording->SetAutoExpire(
04110             curRecording->GetScheduledRecording()->GetAutoExpire());
04111         curRecording->ApplyRecordRecGroupChange(
04112             curRecording->GetScheduledRecording()->GetRecGroup());
04113 
04114         RecordingProfile profile;
04115         QString profileName = load_profile(genOpt.cardtype, NULL,
04116                                            curRecording, profile);
04117         autoRunJobs = init_jobs(curRecording, profile, runJobOnHostOnly,
04118                                 transcodeFirst, earlyCommFlag);
04119     }
04120 
04121     ClearFlags(kFlagNeedToStartRecorder);
04122 }
04123 
04124 void TVRec::SetFlags(uint f)
04125 {
04126     QMutexLocker lock(&stateChangeLock);
04127     stateFlags |= f;
04128     VERBOSE(VB_RECORD, LOC + QString("SetFlags(%1) -> %2")
04129             .arg(FlagToString(f)).arg(FlagToString(stateFlags)));
04130     triggerEventLoop.wakeAll();
04131 }
04132 
04133 void TVRec::ClearFlags(uint f)
04134 {
04135     QMutexLocker lock(&stateChangeLock);
04136     stateFlags &= ~f;
04137     VERBOSE(VB_RECORD, LOC + QString("ClearFlags(%1) -> %2")
04138             .arg(FlagToString(f)).arg(FlagToString(stateFlags)));
04139     triggerEventLoop.wakeAll();
04140 }
04141 
04142 QString TVRec::FlagToString(uint f)
04143 {
04144     QString msg("");
04145 
04146     // General flags
04147     if (kFlagFrontendReady & f)
04148         msg += "FrontendReady,";
04149     if (kFlagRunMainLoop & f)
04150         msg += "RunMainLoop,";
04151     if (kFlagExitPlayer & f)
04152         msg += "ExitPlayer,";
04153     if (kFlagFinishRecording & f)
04154         msg += "FinishRecording,";
04155     if (kFlagErrored & f)
04156         msg += "Errored,";
04157     if (kFlagCancelNextRecording & f)
04158         msg += "CancelNextRecording,";
04159 
04160     // Tuning flags
04161     if ((kFlagRec & f) == kFlagRec)
04162         msg += "REC,";
04163     else
04164     {
04165         if (kFlagLiveTV & f)
04166             msg += "LiveTV,";
04167         if (kFlagRecording & f)
04168             msg += "Recording,";
04169     }
04170     if ((kFlagNoRec & f) == kFlagNoRec)
04171         msg += "NOREC,";
04172     else
04173     {
04174         if (