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