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
11static void merge_overlapping(RecordingGaps &gaps);
12static double score_gaps(const RecordingInfo& /*ri*/, const RecordingGaps& /*gaps*/);
13static QDateTime get_start(const RecordingInfo& /*ri*/);
14static QDateTime get_end(const RecordingInfo& /*ri*/);
15
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
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
166static 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
219static 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
229static 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}
int GetNumSetting(const QString &key, int defaultval=0)
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:398
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:391
QString MakeUniqueKey(void) const
Creates a unique string that can be used to identify an existing recording.
Definition: programinfo.h:340
QDateTime GetEnd(void) const
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:36
QDateTime GetDesiredEndTime(void) const
QDateTime GetDesiredStartTime(void) const
RecordingGaps m_recordingGaps
bool IsDamaged(void) const
void AddTSStatistics(int continuity_error_count, int packet_count)
QString toStringXML(void) const
RecordingQuality(const RecordingInfo *ri, RecordingGaps rg)
static guint32 * back
Definition: goom_core.cpp:25
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
@ ISODate
Default UTC.
Definition: mythdate.h:17
QString indentSpaces(unsigned int level, unsigned int size=4)
Definition: stringutil.h:36
STL namespace.
static QDateTime get_start(const RecordingInfo &)
static double score_gaps(const RecordingInfo &, const RecordingGaps &)
static void merge_overlapping(RecordingGaps &gaps)
static QDateTime get_end(const RecordingInfo &)
QList< RecordingGap > RecordingGaps