MythTV  master
recordingquality.cpp
Go to the documentation of this file.
1 #include <algorithm>
2 #include <utility>
3 
4 using namespace std;
5 
6 #include "recordingquality.h"
7 #include "mythcorecontext.h"
8 #include "recordinginfo.h"
9 #include "mythmiscutil.h"
10 #include "mythlogging.h"
11 
12 static void merge_overlapping(RecordingGaps &gaps);
13 static double score_gaps(const RecordingInfo& /*ri*/, const RecordingGaps& /*gaps*/);
14 static QDateTime get_start(const RecordingInfo& /*ri*/);
15 static QDateTime get_end(const RecordingInfo& /*ri*/);
16 
18  RecordingGaps rg)
19  : m_recording_gaps(std::move(rg))
20 {
21  if (!ri)
22  return;
23 
24  stable_sort(m_recording_gaps.begin(), m_recording_gaps.end());
26 
28 
29  LOG(VB_RECORD, LOG_INFO,
30  QString("RecordingQuality() score(%3)").arg(m_overall_score));
31 }
32 
34  const RecordingInfo *ri, RecordingGaps rg,
35  const QDateTime &first, const QDateTime &latest) :
36  m_recording_gaps(std::move(rg))
37 {
38  if (!ri)
39  return;
40 
42 
43  // trim start
44  QDateTime start = get_start(*ri);
45  while (!m_recording_gaps.empty() &&
46  m_recording_gaps.first().GetStart() < start)
47  {
48  RecordingGap &firstGap = m_recording_gaps.first();
49  if (start < firstGap.GetEnd())
50  firstGap = RecordingGap(start, firstGap.GetEnd());
51  else
52  m_recording_gaps.pop_front();
53  }
54 
55  // trim end
56  QDateTime end = get_end(*ri);
57  while (!m_recording_gaps.empty() &&
58  m_recording_gaps.back().GetEnd() > end)
59  {
61  if (back.GetStart() < end)
62  back = RecordingGap(back.GetStart(), end);
63  else
64  m_recording_gaps.pop_back();
65  }
66 
67  // account for late start
68  int start_gap = (first.isValid()) ? start.secsTo(first) : 0;
69  if (start_gap > 15)
70  m_recording_gaps.push_front(RecordingGap(start, first));
71 
72  // account for missing end
73  int end_gap = (latest.isValid()) ? latest.secsTo(end) : 0;
74  if (end_gap > 15)
75  m_recording_gaps.push_back(RecordingGap(latest, end));
76 
77  stable_sort(m_recording_gaps.begin(), m_recording_gaps.end());
79 
81 
82  LOG(VB_RECORD, LOG_INFO,
83  QString("RecordingQuality() start(%1) end(%2) score(%3)")
86  .arg(m_overall_score));
87 }
88 
90  int continuity_error_count, int packet_count)
91 {
92  m_continuity_error_count = continuity_error_count;
93  m_packet_count = packet_count;
94  if (!m_packet_count)
95  return;
96 
97  double er = double(m_continuity_error_count) / double(m_packet_count);
98  if (er >= 0.01)
99  m_overall_score = max(m_overall_score * 0.60, 0.0);
100  else if (er >= 0.001)
101  m_overall_score = max(m_overall_score * 0.80, 0.0);
102  else if (er >= 0.0001)
103  m_overall_score = max(m_overall_score * 0.90, 0.0);
104 
105  if (er >= 0.01)
106  m_overall_score = min(m_overall_score, 0.5);
107 }
108 
110 {
111  return (m_overall_score * 100) <
112  gCoreContext->GetNumSetting("MinimumRecordingQuality", 95);
113 }
114 
115 QString RecordingQuality::toStringXML(void) const
116 {
117  QString str =
118  QString("<RecordingQuality overall_score=\"%1\" key=\"%2\"")
119  .arg(m_overall_score).arg(m_program_key);
120 
121  if (m_packet_count)
122  {
123  str += QString(" continuity_error_count=\"%1\" packet_count=\"%2\"")
125  }
126 
127  if (m_recording_gaps.empty())
128  return str + " />";
129 
130  str += ">\n";
131 
132  RecordingGaps::const_iterator it = m_recording_gaps.begin();
133  for (; it != m_recording_gaps.end(); ++it)
134  {
135  str += xml_indent(1) +
136  QString("<Gap start=\"%1\" end=\"%2\" duration=\"%3\" />\n")
137  .arg((*it).GetStart().toString(Qt::ISODate))
138  .arg((*it).GetEnd().toString(Qt::ISODate))
139  .arg((*it).GetStart().secsTo((*it).GetEnd()));
140  }
141 
142  return str + "</RecordingQuality>";
143 }
144 
146 {
147  if (gaps.empty())
148  return;
149 
150  RecordingGaps::iterator it = gaps.begin();
151  RecordingGaps::iterator next = it; ++next;
152  while (next != gaps.end())
153  {
154  if ((*it).GetEnd() >= (*next).GetStart())
155  {
156  (*it) = RecordingGap((*it).GetStart(), (*next).GetEnd());
157  next = gaps.erase(next);
158  }
159  else
160  {
161  it = next;
162  ++next;
163  }
164  }
165 }
166 
167 static double score_gaps(const RecordingInfo &ri, const RecordingGaps &gaps)
168 {
169  RecordingGaps::const_iterator it = gaps.begin();
170  if (it == gaps.end())
171  return 1.0;
172 
173  QDateTime start = get_start(ri);
174 
175  double program_length = start.secsTo(get_end(ri));
176  if (program_length < 1.0)
177  return 0.0;
178 
179  double score = 1.0;
180  for (; it != gaps.end(); ++it)
181  {
182  double gap_start = start.secsTo((*it).GetStart());
183  double gap_end = start.secsTo((*it).GetEnd());
184  double gap_length = gap_end - gap_start;
185  double rel_start = gap_start / program_length;
186  double rel_end = gap_end / program_length;
187  double rel_center = (rel_start + rel_end) * 0.5;
188  double rel_length = rel_end - rel_start;
189 
190  /*
191  LOG(VB_GENERAL, LOG_INFO,
192  QString("%1 gap(%2,%3,%4) rel(%5,%6,%7)")
193  .arg((*it).toString())
194  .arg(gap_start).arg(gap_end).arg(gap_length)
195  .arg(rel_start).arg(rel_end).arg(rel_length));
196  */
197 
198  if (rel_center >= 0.9 || rel_end >= 0.95)
199  rel_length *= 4;
200 
201  if (rel_center < 0.1)
202  rel_length *= 2;
203 
204  if (gap_length > 5) // 5 secs
205  rel_length *= 1.5;
206 
207  if (gap_length > 120) // 2 minutes
208  rel_length *= 5;
209 
210  // NOTE: many more scoring adjustments could be made here
211  // and we may want to tune this differently depending on
212  // program length.
213 
214  score -= rel_length;
215  }
216 
217  return (score > 0.0) ? score : 0.0;
218 }
219 
220 static QDateTime get_start(const RecordingInfo &ri)
221 {
222  if (ri.GetDesiredStartTime().isValid())
223  {
224  return (ri.GetScheduledStartTime() > ri.GetDesiredStartTime()) ?
226  }
227  return ri.GetScheduledStartTime();
228 }
229 
230 static QDateTime get_end(const RecordingInfo &ri)
231 {
232  if (ri.GetDesiredEndTime().isValid())
233  {
234  return (ri.GetScheduledEndTime() < ri.GetDesiredEndTime()) ?
236  }
237  return ri.GetScheduledEndTime();
238 }
QDateTime GetDesiredEndTime(void) const
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:34
RecordingGaps m_recording_gaps
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QList< RecordingGap > RecordingGaps
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:382
QString MakeUniqueKey(void) const
Creates a unique string that can be used to identify an existing recording.
Definition: programinfo.h:331
static QDateTime get_start(const RecordingInfo &)
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:389
static double score_gaps(const RecordingInfo &, const RecordingGaps &)
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:101
RecordingQuality(const RecordingInfo *ri, RecordingGaps rg)
PictureAttribute next(PictureAttributeSupported supported, PictureAttribute attribute)
int GetNumSetting(const QString &key, int defaultval=0)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static void merge_overlapping(RecordingGaps &gaps)
QDateTime GetEnd(void) const
QString toStringXML(void) const
Default UTC.
Definition: mythdate.h:14
static guint32 * back
Definition: goom_core.c:34
bool IsDamaged(void) const
static QDateTime get_end(const RecordingInfo &)
QString xml_indent(uint level)
QDateTime GetDesiredStartTime(void) const
void AddTSStatistics(int continuity_error_count, int packet_count)