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
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
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
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
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
00165
00166
00167
00168
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)
00178 rel_length *= 1.5;
00179
00180 if (gap_length > 120)
00181 rel_length *= 5;
00182
00183
00184
00185
00186
00187 score -= rel_length;
00188 }
00189
00190 return (score > 0.0) ? score : 0.0;
00191 }
00192