int PVRClientMythTV::GetCurrentClientChannel() { if (g_bExtraDebug) XBMC->Log(LOG_DEBUG, "%s", __FUNCTION__); CLockObject lock(m_lock); if (m_rec.IsNull()) return -1; MythProgramInfo currentProgram = m_rec.GetCurrentProgram(); return currentProgram.ChannelID(); }
bool PVRClientMythTV::IsRecordingVisible(MythProgramInfo &recording) { // Filter out recordings of special storage groups (like LiveTV or Deleted) // When deleting a recording, it might not be deleted immediately but marked as 'pending delete'. // Depending on the protocol version the recording is moved to the group Deleted or // the 'delete pending' flag is set if (recording.RecordingGroup() == "LiveTV" || recording.RecordingGroup() == "Deleted" || recording.IsDeletePending()) { XBMC->Log(LOG_DEBUG, "%s: Ignoring recording %s", __FUNCTION__, recording.Path().c_str()); return false; } return true; }
bool MythProgramInfo::operator ==(const MythProgramInfo &other) { if (!this->IsNull() && !other.IsNull()) { if (this->m_proginfo->channel.chanId == other.m_proginfo->channel.chanId && this->m_proginfo->recording.startTs == other.m_proginfo->recording.startTs) return true; } return false; }
ProgramInfoMap MythConnection::GetScheduledPrograms() { Lock(); ProgramInfoMap retval; cmyth_proglist_t proglist = NULL; CMYTH_CONN_CALL_REF(proglist, proglist == NULL, cmyth_proglist_get_all_scheduled(*m_conn_t)); int len = cmyth_proglist_get_count(proglist); for (int i = 0; i < len; i++) { MythProgramInfo prog = cmyth_proglist_get_item(proglist, i); if (!prog.IsNull()) { retval.insert(std::pair<CStdString, MythProgramInfo>(prog.UID().c_str(), prog)); } } ref_release(proglist); Unlock(); return retval; }
void MythEventHandler::MythEventHandlerPrivate::HandleAskRecording(const CStdString &databuf, MythProgramInfo &programInfo) { // ASK_RECORDING <card id> <time until> <has rec> <has later>[]:[]<program info> // Example: ASK_RECORDING 9 29 0 1[]:[]<program> // The scheduled recording will hang in MythTV if ASK_RECORDING is just ignored. // - Stop recorder (and blocked for time until seconds) // - Skip the recording by sending CANCEL_NEXT_RECORDING(true) unsigned int cardid; int timeuntil, hasrec, haslater; if (sscanf(databuf.c_str(), "%d %d %d %d", &cardid, &timeuntil, &hasrec, &haslater) == 4) XBMC->Log(LOG_NOTICE, "%s: Event ASK_RECORDING: rec=%d timeuntil=%d hasrec=%d haslater=%d", __FUNCTION__, cardid, timeuntil, hasrec, haslater); else XBMC->Log(LOG_ERROR, "%s: Incorrect ASK_RECORDING event: rec=%d timeuntil=%d hasrec=%d haslater=%d", __FUNCTION__, cardid, timeuntil, hasrec, haslater); CStdString title; if (!programInfo.IsNull()) title = programInfo.Title(); XBMC->Log(LOG_NOTICE, "%s: Event ASK_RECORDING: title=%s", __FUNCTION__, title.c_str()); if (timeuntil >= 0 && !m_recorder.IsNull() && m_recorder.ID() == cardid) { if (g_iLiveTVConflictStrategy == LIVETV_CONFLICT_STRATEGY_CANCELREC || (g_iLiveTVConflictStrategy == LIVETV_CONFLICT_STRATEGY_HASLATER && haslater)) { XBMC->QueueNotification(QUEUE_WARNING, XBMC->GetLocalizedString(30307), title.c_str()); // Canceling conflicting recording: %s m_recorder.CancelNextRecording(true); } else // LIVETV_CONFLICT_STRATEGY_STOPTV { XBMC->QueueNotification(QUEUE_WARNING, XBMC->GetLocalizedString(30308), title.c_str()); // Stopping Live TV due to conflicting recording: %s if (m_observer) m_observer->CloseLiveStream(); } } }
float PVRClientMythTV::GetRecordingFrameRate(MythProgramInfo &recording) { // MythTV uses frame offsets whereas XBMC expects a time offset. // This function can be used to convert the frame offsets to time offsets and back. // The average frameRate is calculated by: frameRate = frameCount / duration. float frameRate = 0.0f; if (g_bExtraDebug) { XBMC->Log(LOG_DEBUG, "%s - Getting Framerate for: %s)", __FUNCTION__, recording.Title(false).c_str()); } // cmyth_get_bookmark_mark returns the appropriate frame offset for the given byte offset (recordedseek table) // This can be used to determine the frame count (by querying the max byte offset) long long frameCount = m_db.GetBookmarkMark(recording, LLONG_MAX, 0); if (frameCount > 0) { if (g_bExtraDebug) { XBMC->Log(LOG_DEBUG, "%s - FrameCount: %lld)", __FUNCTION__, frameCount); XBMC->Log(LOG_DEBUG, "%s - Duration: %d)", __FUNCTION__, recording.Duration()); } if (recording.Duration() > 0) { // Calculate frameRate frameRate = (float)frameCount / (float)recording.Duration(); if (g_bExtraDebug) { XBMC->Log(LOG_DEBUG, "%s - FrameRate: %f)", __FUNCTION__, frameRate); } } } return frameRate; }
void *MythEventHandler::MythEventHandlerPrivate::Process() { const char *events[] = { "CMYTH_EVENT_UNKNOWN", "CMYTH_EVENT_CLOSE", "CMYTH_EVENT_RECORDING_LIST_CHANGE", "CMYTH_EVENT_RECORDING_LIST_CHANGE_ADD", "CMYTH_EVENT_RECORDING_LIST_CHANGE_UPDATE", "CMYTH_EVENT_RECORDING_LIST_CHANGE_DELETE", "CMYTH_EVENT_SCHEDULE_CHANGE", "CMYTH_EVENT_DONE_RECORDING", "CMYTH_EVENT_QUIT_LIVETV", "CMYTH_EVENT_LIVETV_WATCH", "CMYTH_EVENT_LIVETV_CHAIN_UPDATE", "CMYTH_EVENT_SIGNAL", "CMYTH_EVENT_ASK_RECORDING", "CMYTH_EVENT_SYSTEM_EVENT", "CMYTH_EVENT_UPDATE_FILE_SIZE", "CMYTH_EVENT_GENERATED_PIXMAP", "CMYTH_EVENT_CLEAR_SETTINGS_CACHE" }; cmyth_event_t myth_event; char databuf[2049]; databuf[0] = 0; bool triggerRecordingUpdate = false; unsigned int recordingChangeCount = 0; timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 100000; while (!IsStopped()) { bool recordingChange = false; int select = 0; m_conn_t->Lock(); select = cmyth_event_select(*m_conn_t, &timeout); m_conn_t->Unlock(); if (select > 0) { cmyth_proginfo_t proginfo = NULL; m_conn_t->Lock(); myth_event = cmyth_event_get_message(*m_conn_t, databuf, 2048, &proginfo); m_conn_t->Unlock(); if (g_bExtraDebug) XBMC->Log(LOG_DEBUG, "%s - EVENT ID: %s, EVENT databuf: %s", __FUNCTION__, events[myth_event], databuf); if (myth_event == CMYTH_EVENT_UPDATE_FILE_SIZE) { if (g_bExtraDebug) XBMC->Log(LOG_NOTICE,"%s - Event file size update: %s", __FUNCTION__, databuf); HandleUpdateFileSize(databuf); } else if (myth_event == CMYTH_EVENT_LIVETV_CHAIN_UPDATE) { Lock(); if (!m_recorder.IsNull()) { bool retval = m_recorder.LiveTVChainUpdate(databuf); if (g_bExtraDebug) XBMC->Log(LOG_NOTICE, "%s - Event chain update: %s", __FUNCTION__, (retval ? "true" : "false")); } else if (g_bExtraDebug) XBMC->Log(LOG_NOTICE, "%s - Event chain update: No recorder", __FUNCTION__); Unlock(); } else if (myth_event == CMYTH_EVENT_LIVETV_WATCH) { if (g_bExtraDebug) XBMC->Log(LOG_NOTICE,"%s: Event LIVETV_WATCH: recoder %s", __FUNCTION__, databuf); Lock(); if (!m_recorder.IsNull()) { bool retval = m_recorder.LiveTVWatch(databuf); if (g_bExtraDebug) XBMC->Log(LOG_NOTICE, "%s: Event LIVETV_WATCH: %s", __FUNCTION__, (retval) ? "true " : "false"); } else if (g_bExtraDebug) XBMC->Log(LOG_NOTICE, "%s: Event LIVETV_WATCH: No recorder", __FUNCTION__); Unlock(); } else if(myth_event == CMYTH_EVENT_DONE_RECORDING) { if (g_bExtraDebug) XBMC->Log(LOG_NOTICE, "%s: Event DONE_RECORDING: recorder %s", __FUNCTION__, databuf); Lock(); if (!m_recorder.IsNull()) { bool retval = m_recorder.LiveTVDoneRecording(databuf); if (g_bExtraDebug) XBMC->Log(LOG_NOTICE, "%s: Event DONE_RECORDING: %s", __FUNCTION__, (retval) ? "true" : "false"); } else if (g_bExtraDebug) XBMC->Log(LOG_NOTICE, "%s: Event DONE_RECORDING: No recorder", __FUNCTION__); Unlock(); } else if (myth_event == CMYTH_EVENT_ASK_RECORDING) { MythProgramInfo prog(proginfo); HandleAskRecording(databuf, prog); } else if (myth_event == CMYTH_EVENT_SIGNAL) { HandleUpdateSignal(databuf); } if (myth_event == CMYTH_EVENT_SCHEDULE_CHANGE) { if (g_bExtraDebug) XBMC->Log(LOG_NOTICE, "%s - Event schedule change", __FUNCTION__); PVR->TriggerTimerUpdate(); } else if (myth_event == CMYTH_EVENT_RECORDING_LIST_CHANGE_ADD) { //Event data: "4121 2010-03-06T01:06:43[]:[]empty" unsigned int chanid; char recstartts[20]; if (strlen(databuf)>=24 && sscanf(databuf, "%u %19s", &chanid, recstartts) == 2) { Lock(); m_recordingChangeEventList.push_back(RecordingChangeEvent(CHANGE_ADD, chanid, recstartts)); Unlock(); if (g_bExtraDebug) XBMC->Log(LOG_DEBUG,"%s - Event recording list add: CHANID=%u TS=%s", __FUNCTION__, chanid, recstartts); recordingChange = true; } } else if (myth_event == CMYTH_EVENT_RECORDING_LIST_CHANGE_UPDATE) { //Event data: Updated 'proginfo' is returned MythProgramInfo prog = MythProgramInfo(proginfo); if (!prog.IsNull()) { Lock(); m_recordingChangeEventList.push_back(RecordingChangeEvent(CHANGE_UPDATE, prog)); Unlock(); if (g_bExtraDebug) XBMC->Log(LOG_DEBUG,"%s - Event recording list update: UID=%s", __FUNCTION__, prog.UID().c_str()); recordingChange = true; } } else if (myth_event == CMYTH_EVENT_RECORDING_LIST_CHANGE_DELETE) { //Event data: "4121 2010-03-06T01:06:43[]:[]empty" unsigned int chanid; char recstartts[20]; if (strlen(databuf)>=24 && sscanf(databuf, "%u %19s", &chanid, recstartts) == 2) { Lock(); m_recordingChangeEventList.push_back(RecordingChangeEvent(CHANGE_DELETE, chanid, recstartts)); Unlock(); if (g_bExtraDebug) XBMC->Log(LOG_DEBUG,"%s - Event recording list delete: CHANID=%u TS=%s", __FUNCTION__, chanid, recstartts); recordingChange = true; } } else if (myth_event == CMYTH_EVENT_RECORDING_LIST_CHANGE) { if (g_bExtraDebug) XBMC->Log(LOG_NOTICE, "%s - Event recording list change", __FUNCTION__); RecordingListChange(); } else if (myth_event == CMYTH_EVENT_UNKNOWN) { XBMC->Log(LOG_NOTICE, "%s - Event unknown, databuf: %s", __FUNCTION__, databuf); } else if (myth_event == CMYTH_EVENT_CLOSE) { XBMC->Log(LOG_NOTICE, "%s - Event client connection closed", __FUNCTION__); RetryConnect(); } databuf[0] = 0; } else if (select < 0) { XBMC->Log(LOG_ERROR, "%s Event client connection error", __FUNCTION__); RetryConnect(); } else if (cmyth_conn_hung(*m_conn_t)) { XBMC->Log(LOG_NOTICE, "%s - Connection hung - reconnect event client connection", __FUNCTION__); if (!m_conn_t || !TryReconnect()) RetryConnect(); } //Accumulate recording change events before triggering PVR event //First timeout is 0.5 sec and next 2 secs if (recordingChange) { if (recordingChangeCount == 0) { timeout.tv_sec = 0; timeout.tv_usec = 500000; } else { timeout.tv_sec = 2; timeout.tv_usec = 0; } recordingChangeCount++; triggerRecordingUpdate = true; } else { //Restore timeout timeout.tv_sec = 0; timeout.tv_usec = 100000; //Need PVR update ? if (triggerRecordingUpdate) { XBMC->Log(LOG_DEBUG, "%s - Trigger PVR recording update: %lu recording(s)", __FUNCTION__, recordingChangeCount); PVR->TriggerRecordingUpdate(); triggerRecordingUpdate = false; } //Reset counter recordingChangeCount = 0; } } // Free recording change event m_recordingChangeEventList.clear(); return NULL; }
bool MythScheduleHelper85::FillTimerEntryWithUpcoming(MythTimerEntry& entry, const MythProgramInfo& recording) const { //Only include timers which have an inactive status if the user has requested it (flag m_showNotRecording) switch (recording.Status()) { //Upcoming recordings which are disabled due to being lower priority duplicates or already recorded case Myth::RS_EARLIER_RECORDING: //will record earlier case Myth::RS_LATER_SHOWING: //will record later case Myth::RS_CURRENT_RECORDING: //Already in the current library case Myth::RS_PREVIOUS_RECORDING: //Previoulsy recorded but no longer in the library if (!m_manager->ShowNotRecording()) { XBMC->Log(LOG_DEBUG, "85::%s: Skipping %s:%s on %s because status %d", __FUNCTION__, recording.Title().c_str(), recording.Subtitle().c_str(), recording.ChannelName().c_str(), recording.Status()); return false; } default: break; } MythRecordingRuleNodePtr node = m_manager->FindRuleById(recording.RecordID()); if (node) { MythRecordingRule rule = node->GetRule(); // Relate the main rule as parent entry.parentIndex = MythScheduleManager::MakeIndex(node->GetMainRule()); switch (rule.Type()) { case Myth::RT_SingleRecord: return false; // Discard upcoming. We show only main rule. case Myth::RT_DontRecord: entry.recordingStatus = recording.Status(); entry.timerType = TIMER_TYPE_DONT_RECORD; entry.isInactive = rule.Inactive(); break; case Myth::RT_OverrideRecord: entry.recordingStatus = recording.Status(); entry.timerType = TIMER_TYPE_OVERRIDE; entry.isInactive = rule.Inactive(); break; default: entry.recordingStatus = recording.Status(); if (node->GetMainRule().SearchType() == Myth::ST_ManualSearch) entry.timerType = TIMER_TYPE_UPCOMING_MANUAL; else entry.timerType = TIMER_TYPE_UPCOMING; } entry.startOffset = rule.StartOffset(); entry.endOffset = rule.EndOffset(); entry.priority = rule.Priority(); entry.expiration = GetRuleExpirationId(RuleExpiration(rule.AutoExpire(), 0, false)); } else entry.timerType = TIMER_TYPE_ZOMBIE; switch (entry.timerType) { case TIMER_TYPE_UPCOMING: case TIMER_TYPE_OVERRIDE: case TIMER_TYPE_UPCOMING_MANUAL: entry.epgCheck = true; break; default: entry.epgCheck = false; } entry.description = ""; entry.chanid = recording.ChannelID(); entry.callsign = recording.Callsign(); entry.startTime = recording.StartTime(); entry.endTime = recording.EndTime(); entry.title.assign(recording.Title()); if (!recording.Subtitle().empty()) entry.title.append(" (").append(recording.Subtitle()).append(")"); if (recording.Season() || recording.Episode()) entry.title.append(" - ").append(Myth::IntToString(recording.Season())).append(".").append(Myth::IntToString(recording.Episode())); entry.recordingGroup = GetRuleRecordingGroupId(recording.RecordingGroup()); entry.entryIndex = MythScheduleManager::MakeIndex(recording); // upcoming index return true; }
MythRecordingRule MythScheduleHelper75::MakeOverride(const MythRecordingRule& rule, const MythProgramInfo& recording) { MythRecordingRule modifier = rule.DuplicateRecordingRule(); // Do the same as backend even we know the modifier will be rejected for manual rule: // Don't know if this behavior is a bug issue or desired: cf libmythtv/recordingrule.cpp if (modifier.SearchType() != Myth::ST_ManualSearch) modifier.SetSearchType(Myth::ST_NoSearch); modifier.SetType(Myth::RT_OverrideRecord); modifier.SetParentID(modifier.RecordID()); modifier.SetRecordID(0); modifier.SetInactive(false); // Assign recording info modifier.SetTitle(recording.Title()); modifier.SetSubtitle(recording.Subtitle()); modifier.SetDescription(recording.Description()); modifier.SetChannelID(recording.ChannelID()); modifier.SetCallsign(recording.Callsign()); modifier.SetStartTime(recording.StartTime()); modifier.SetEndTime(recording.EndTime()); modifier.SetSeriesID(recording.SerieID()); modifier.SetProgramID(recording.ProgramID()); modifier.SetCategory(recording.Category()); if (rule.InetRef().empty()) { modifier.SetInerRef(recording.Inetref()); modifier.SetSeason(recording.Season()); modifier.SetEpisode(recording.Episode()); } return modifier; }