MythTV  0.26-pre
recordingquality.cpp
Go to the documentation of this file.
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 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends