|
MythTV
0.26-pre
|
00001 #include <algorithm> 00002 using namespace std; 00003 00004 #include "recordingquality.h" 00005 #include "mythcorecontext.h" 00006 #include "mythlogging.h" 00007 #include "programinfo.h" 00008 #include "mythmiscutil.h" 00009 00010 static void merge_overlapping(RecordingGaps &gaps); 00011 static double score_gaps(const ProgramInfo*, const RecordingGaps&); 00012 00013 RecordingQuality::RecordingQuality( 00014 const ProgramInfo *pi, const RecordingGaps &rg, 00015 const QDateTime &first, const QDateTime &latest) : 00016 m_continuity_error_count(0), m_packet_count(0), 00017 m_overall_score(1.0), m_recording_gaps(rg) 00018 { 00019 if (!pi) 00020 return; 00021 00022 m_program_key = pi->MakeUniqueKey(); 00023 00024 // trim start 00025 QDateTime start = pi->GetScheduledStartTime(); 00026 while (!m_recording_gaps.empty() && 00027 m_recording_gaps.first().GetStart() < start) 00028 { 00029 RecordingGap &first = m_recording_gaps.first(); 00030 if (start < first.GetEnd()) 00031 first = RecordingGap(start, first.GetEnd()); 00032 else 00033 m_recording_gaps.pop_front(); 00034 } 00035 00036 // trim end 00037 QDateTime end = pi->GetScheduledEndTime(); 00038 while (!m_recording_gaps.empty() && 00039 m_recording_gaps.back().GetEnd() > end) 00040 { 00041 RecordingGap &back = m_recording_gaps.back(); 00042 if (back.GetStart() < end) 00043 back = RecordingGap(back.GetStart(), end); 00044 else 00045 m_recording_gaps.pop_back(); 00046 } 00047 00048 // account for late start 00049 int start_gap = (first.isValid()) ? start.secsTo(first) : 0; 00050 if (start_gap > 15) 00051 m_recording_gaps.push_front(RecordingGap(start, first)); 00052 00053 // account for missing end 00054 int end_gap = (latest.isValid()) ? latest.secsTo(end) : 0; 00055 if (end_gap > 15) 00056 m_recording_gaps.push_back(RecordingGap(latest, end)); 00057 00058 stable_sort(m_recording_gaps.begin(), m_recording_gaps.end()); 00059 merge_overlapping(m_recording_gaps); 00060 00061 m_overall_score = score_gaps(pi, m_recording_gaps); 00062 } 00063 00064 void RecordingQuality::AddTSStatistics( 00065 int continuity_error_count, int packet_count) 00066 { 00067 m_continuity_error_count = continuity_error_count; 00068 m_packet_count = packet_count; 00069 if (!m_packet_count) 00070 return; 00071 00072 double er = double(m_continuity_error_count) / double(m_packet_count); 00073 if (er >= 0.01) 00074 m_overall_score = max(m_overall_score * 0.60, 0.0); 00075 else if (er >= 0.001) 00076 m_overall_score = max(m_overall_score * 0.80, 0.0); 00077 00078 if (er >= 0.01) 00079 m_overall_score = min(m_overall_score, 0.5); 00080 } 00081 00082 bool RecordingQuality::IsDamaged(void) const 00083 { 00084 return (m_overall_score * 100) < 00085 gCoreContext->GetNumSetting("MinimumRecordingQuality", 95); 00086 } 00087 00088 QString RecordingQuality::toStringXML(void) const 00089 { 00090 QString str = 00091 QString("<RecordingQuality overall_score=\"%1\" key=\"%2\"") 00092 .arg(m_overall_score).arg(m_program_key); 00093 00094 if (m_packet_count) 00095 { 00096 str += QString(" countinuity_error_count=\"%1\" packet_count=\"%2\"") 00097 .arg(m_continuity_error_count).arg(m_packet_count); 00098 } 00099 00100 if (m_recording_gaps.empty()) 00101 return str + " />"; 00102 00103 str += ">\n"; 00104 00105 RecordingGaps::const_iterator it = m_recording_gaps.begin(); 00106 for (; it != m_recording_gaps.end(); ++it) 00107 { 00108 str += xml_indent(1) + 00109 QString("<Gap start=\"%1\" end=\"%2\" duration=\"%3\" />\n") 00110 .arg((*it).GetStart().toString(Qt::ISODate)) 00111 .arg((*it).GetEnd().toString(Qt::ISODate)) 00112 .arg((*it).GetStart().secsTo((*it).GetEnd())); 00113 } 00114 00115 return str + "</RecordingQuality>"; 00116 } 00117 00118 static void merge_overlapping(RecordingGaps &gaps) 00119 { 00120 if (gaps.empty()) 00121 return; 00122 00123 RecordingGaps::iterator it = gaps.begin(); 00124 RecordingGaps::iterator next = it; ++next; 00125 while (next != gaps.end()) 00126 { 00127 if ((*it).GetEnd() >= (*next).GetStart()) 00128 { 00129 (*it) = RecordingGap((*it).GetStart(), (*next).GetEnd()); 00130 next = gaps.erase(next); 00131 } 00132 else 00133 { 00134 it = next; 00135 ++next; 00136 } 00137 } 00138 } 00139 00140 static double score_gaps(const ProgramInfo *pi, const RecordingGaps &gaps) 00141 { 00142 RecordingGaps::const_iterator it = gaps.begin(); 00143 if (it == gaps.end()) 00144 return 1.0; 00145 00146 QDateTime start = pi->GetScheduledStartTime(); 00147 00148 double program_length = start.secsTo(pi->GetScheduledEndTime()); 00149 if (program_length < 1.0) 00150 return 0.0; 00151 00152 double score = 1.0; 00153 for (; it != gaps.end(); ++it) 00154 { 00155 double gap_start = start.secsTo((*it).GetStart()); 00156 double gap_end = start.secsTo((*it).GetEnd()); 00157 double gap_length = gap_end - gap_start; 00158 double rel_start = gap_start / program_length; 00159 double rel_end = gap_end / program_length; 00160 double rel_center = (rel_start + rel_end) * 0.5; 00161 double rel_length = rel_end - rel_start; 00162 00163 /* 00164 LOG(VB_GENERAL, LOG_INFO, 00165 QString("%1 gap(%2,%3,%4) rel(%5,%6,%7)") 00166 .arg((*it).toString()) 00167 .arg(gap_start).arg(gap_end).arg(gap_length) 00168 .arg(rel_start).arg(rel_end).arg(rel_length)); 00169 */ 00170 00171 if (rel_center >= 0.9 || rel_end >= 0.95) 00172 rel_length *= 4; 00173 00174 if (rel_center < 0.1) 00175 rel_length *= 2; 00176 00177 if (gap_length > 5) // 5 secs 00178 rel_length *= 1.5; 00179 00180 if (gap_length > 120) // 2 minutes 00181 rel_length *= 5; 00182 00183 // NOTE: many more scoring adjustments could be made here 00184 // and we may want to tune this differently depending on 00185 // program length. 00186 00187 score -= rel_length; 00188 } 00189 00190 return (score > 0.0) ? score : 0.0; 00191 } 00192
1.7.6.1