00001
00002 #include <cstdio>
00003 #include <cstdlib>
00004 #include <cstring>
00005 #include <unistd.h>
00006 #include <pthread.h>
00007 #include <sched.h>
00008
00009
00010 #include <iostream>
00011 using namespace std;
00012
00013
00014 #include <qapplication.h>
00015 #include <qsqldatabase.h>
00016 #include <qsocket.h>
00017
00018
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;
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
00107 : recorder(NULL), channel(NULL), signalMonitor(NULL),
00108 scanner(NULL),
00109
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
00117 cardid(capturecardnum), ispip(false),
00118
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
00125 curRecording(NULL), autoRunJobs(JOB_NONE),
00126
00127 pseudoLiveTVRecording(NULL),
00128 nextLiveTVDir(""), nextLiveTVDirLock(false),
00129
00130 tvchain(NULL),
00131
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();
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
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
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
00397 if (rcinfo->cardid != cardid)
00398 return;
00399
00400
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
00492 WaitForEventThreadSleep();
00493
00494
00495
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
00533 WaitForEventThreadSleep();
00534
00535
00536
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
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
00554
00555
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
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
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
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
00642 curRecording = new ProgramInfo(*rcinfo);
00643 curRecording->MarkAsInUse(true, "recorder");
00644 StartedRecording(curRecording);
00645
00646
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
00659
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
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
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
00890
00891 if (HasFlags(kFlagEITScannerRunning))
00892 {
00893 scanner->StopActiveScan();
00894 ClearFlags(kFlagEITScannerRunning);
00895 }
00896
00897
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
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
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
01311 if (CardUtil::IsEITCapable(genOpt.cardtype) &&
01312 (!GetDVBChannel() || GetDVBChannel()->IsMaster()))
01313 {
01314 scanner = new EITScanner(cardid);
01315
01316
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
01326 if (changeState)
01327 {
01328 HandleStateChange();
01329 ClearFlags(kFlagFrontendReady | kFlagCancelNextRecording);
01330 }
01331
01332
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
01342 HandleTuning();
01343
01344
01345 HandlePendingRecordings();
01346
01347
01348
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
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
01434
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
01468
01469
01470
01471 if (tuningRequests.empty() && !changeState)
01472 {
01473 triggerEventSleep.wakeAll();
01474 lock.mutex()->unlock();
01475 sched_yield();
01476 triggerEventSleep.wakeAll();
01477 triggerEventLoop.wait(1000 );
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
01501
01502
01503 triggerEventSleep.wait(100);
01504 stateChangeLock.lock();
01505
01506
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
01518
01519
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
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
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
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
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
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
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
01709
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
01736
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
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
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
01867
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
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
01897
01898
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
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
01933
01934
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
01976 if (signalMonitor)
01977 return true;
01978
01979
01980 if (!channel)
01981 return false;
01982
01983
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
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
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
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
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
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
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)
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
02327
02328
02329
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
02342
02343
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
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
02365
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;
02536 else if (genOpt.cardtype == "DBOX2")
02537 bitrate = 10080000LL;
02538 else if (!CardUtil::IsEncoder(genOpt.cardtype))
02539 bitrate = 19400000LL;
02540 else
02541 bitrate = 10080000LL;
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
02569 ChangeState(kState_WatchingLiveTV);
02570
02571 WaitForEventThreadSleep();
02572
02573
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;
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
02614 SetPseudoLiveTVRecording(pi);
02615 return;
02616 }
02617 else if (pi->recgroup == "LiveTV" && pseudoLiveTVRecording)
02618 {
02619
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
02662
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
02675 rec->GetScheduledRecording()->setEndOffset(0);
02676
02677
02678
02679
02680 rec->recstatus = rsInactive;
02681 rec->AddHistory(false);
02682
02683
02684
02685 rec->GetScheduledRecording()->save(false);
02686
02687
02688 rec->ApplyRecordRecID();
02689
02690
02691 rec->recstatus = rsRecording;
02692
02693
02694 QStringList prog;
02695 rec->ToStringList(prog);
02696 MythEvent me("SCHEDULER_ADD_RECORDING", prog);
02697 gContext->dispatch(me);
02698
02699
02700
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
02729 SetFlags(kFlagCancelNextRecording);
02730 curRecording->recgroup = "LiveTV";
02731 }
02732 else if (!was_rec && pseudoLiveTVRecording)
02733 {
02734 VERBOSE(VB_IMPORTANT, LOC + "SetLiveRecording() -- record");
02735
02736
02737
02738
02739
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;
02768
02769 bool hadPseudoLiveTVRec = pseudoLiveTVRecording;
02770 CheckForRecGroupChange();
02771
02772 if (!hadPseudoLiveTVRec && pseudoLiveTVRecording)
02773 NotifySchedulerOfRecording(curRecording);
02774
02775
02776 TVState next_state = kState_None;
02777 if (pseudoLiveTVRecording)
02778 {
02779 recordEndTime = GetRecordEndTime(pseudoLiveTVRecording);
02780 next_state = kState_RecordingOnly;
02781 }
02782
02783
02784 ChangeState(next_state);
02785
02786
02787 WaitForEventThreadSleep();
02788
02789
02790 tvchain = NULL;
02791 }
02792
02801 void TVRec::PauseRecorder(void)
02802 {
02803 QMutexLocker lock(&stateChangeLock);
02804
02805 if (!recorder)
02806 {
02807 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
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
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
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
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
02983 if (requestType & kFlagDetect)
02984 {
02985 WaitForEventThreadSleep();
02986 requestType = lastTuningRequest.flags & (kFlagRec | kFlagNoRec);
02987 }
02988
02989
02990 ClearFlags(kFlagRingBufferReady);
02991
02992
02993
02994 tuningRequests.enqueue(TuningRequest(requestType, name, input));
02995 WaitForEventThreadSleep();
02996
02997
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
03023 if (requestType & kFlagDetect)
03024 {
03025 WaitForEventThreadSleep();
03026 requestType = lastTuningRequest.flags & (kFlagRec | kFlagNoRec);
03027 }
03028
03029
03030 ClearFlags(kFlagRingBufferReady);
03031
03032
03033
03034 tuningRequests.enqueue(TuningRequest(requestType, name));
03035 WaitForEventThreadSleep();
03036
03037
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
03128 title = subtitle = desc = category = "";
03129 starttime = endtime = callsign = iconpath = "";
03130 channum = chanidStr = seriesid = programid = "";
03131
03132
03133 chanidStr = QString::number(chanid);
03134
03135
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
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
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
03401
03402 tuningRequests.dequeue();
03403
03404
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
03433
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
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
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
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
03556
03557 CloseChannel();
03558
03559 }
03560
03561
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
03581 }
03582
03583
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
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
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
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
03760
03761
03762 if (recorder)
03763 recorder->SetRingBuffer(NULL);
03764
03765 SetFlags(kFlagDummyRecorderRunning);
03766 VERBOSE(VB_RECORD, "DummyDTVRecorder -- started");
03767 SetFlags(kFlagRingBufferReady);
03768 }
03769
03770
03771
03772 if (error)
03773 return;
03774 }
03775
03776
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
03797 MPEGStreamData *streamData = NULL;
03798 if (GetDTVSignalMonitor())
03799 streamData = GetDTVSignalMonitor()->GetStreamData();
03800
03801 if (!HasFlags(kFlagEITScannerRunning))
03802 {
03803
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;
03831
03832 int jobs = 0;
03833
03834
03835 JobQueue::AddJobsToMask(rec->GetAutoRunJobs(), jobs);
03836
03837
03838 if (rec->chancommfree)
03839 JobQueue::RemoveJobsFromMask(JOB_COMMFLAG, jobs);
03840
03841
03842 const Setting *autoTrans = profile.byName("autotranscode");
03843 if ((!autoTrans) || (autoTrans->getValue().toInt() == 0))
03844 JobQueue::RemoveJobsFromMask(JOB_TRANSCODE, jobs);
03845
03846
03847 bool rt = JobQueue::JobIsInMask(JOB_COMMFLAG, jobs) && on_line_comm;
03848
03849
03850 rt &= JobQueue::JobIsNotInMask(JOB_TRANSCODE, jobs) ||
03851 !transcode_bfr_comm;
03852 if (rt)
03853 {
03854
03855 QString host = (on_host) ? gContext->GetHostName() : "";
03856 JobQueue::QueueJob(JOB_COMMFLAG,
03857 rec->chanid, rec->recstartts, "", "",
03858 host, JOB_LIVE_REC);
03859
03860
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
03871
03872
03873
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();
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();
03984
03985 if (rec)
03986 recorder->SetRecording(rec);
03987
03988
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
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
04088 channel->SetFd(recorder->GetVideoFd());
04089
04090
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
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
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 (