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