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