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 int max_start = gCoreContext->GetNumSetting("MaxStartGap", 15);
42 int max_end = gCoreContext->GetNumSetting("MaxEndGap", 15);
43
44 // trim start
45 QDateTime start = get_start(*ri);
46 while (!m_recordingGaps.empty() &&
47 m_recordingGaps.first().GetStart() < start)
48 {
49 RecordingGap &firstGap = m_recordingGaps.first();
50 if (start < firstGap.GetEnd())
51 firstGap = RecordingGap(start, firstGap.GetEnd());
52 else
53 m_recordingGaps.pop_front();
54 }
55
56 // trim end
57 QDateTime end = get_end(*ri).addSecs(-max_end);
58 while (!m_recordingGaps.empty() &&
59 m_recordingGaps.back().GetEnd() > end)
60 {
62 if (back.GetStart() < end)
63 back = RecordingGap(back.GetStart(), end);
64 else
65 m_recordingGaps.pop_back();
66 }
67
68 // account for late start
69 int start_gap = (first.isValid()) ? start.secsTo(first) : 0;
70 if (start_gap > max_start)
71 m_recordingGaps.push_front(RecordingGap(start, first));
72
73 // account for missing end
74 int end_gap = (latest.isValid()) ? latest.secsTo(end) : 0;
75 if (end_gap > max_end)
76 m_recordingGaps.push_back(RecordingGap(latest, end));
77
78 std::stable_sort(m_recordingGaps.begin(), m_recordingGaps.end());
80
82
83 LOG(VB_RECORD, LOG_INFO,
84 QString("RecordingQuality() start(%1) end(%2) score(%3)")
87 QString::number(m_overallScore)));
88}
89
91 int continuity_error_count, int packet_count)
92{
93 m_continuityErrorCount = continuity_error_count;
94 m_packetCount = packet_count;
95 if (!m_packetCount)
96 return;
97
98 double er = double(m_continuityErrorCount) / double(m_packetCount);
99 if (er >= 0.01)
100 m_overallScore = std::max(m_overallScore * 0.60, 0.0);
101 else if (er >= 0.001)
102 m_overallScore = std::max(m_overallScore * 0.80, 0.0);
103 else if (er >= 0.0001)
104 m_overallScore = std::max(m_overallScore * 0.90, 0.0);
105
106 if (er >= 0.01)
107 m_overallScore = std::min(m_overallScore, 0.5);
108}
109
111{
112 return (m_overallScore * 100) <
113 gCoreContext->GetNumSetting("MinimumRecordingQuality", 95);
114}
115
117{
118 QString str =
119 QString(R"(<RecordingQuality overall_score="%1" key="%2")")
120 .arg(m_overallScore).arg(m_programKey);
121
122 if (m_packetCount)
123 {
124 str += QString(R"( continuity_error_count="%1" packet_count="%2")")
126 }
127
128 if (m_recordingGaps.empty())
129 return str + " />";
130
131 str += ">\n";
132
133 auto add_gap = [](const QString& s, const auto & gap) {
134 return s + StringUtil::indentSpaces(1) +
135 QString("<Gap start=\"%1\" end=\"%2\" duration=\"%3\" />\n")
136 .arg(gap.GetStart().toString(Qt::ISODate))
137 .arg(gap.GetEnd().toString(Qt::ISODate))
138 .arg(gap.GetStart().secsTo(gap.GetEnd()));
139 };
140 str = std::accumulate(m_recordingGaps.cbegin(),m_recordingGaps.cend(),
141 str, add_gap);
142
143 return str + "</RecordingQuality>";
144}
145
147{
148 if (gaps.empty())
149 return;
150
151 RecordingGaps::iterator it = gaps.begin();
152 RecordingGaps::iterator next = it; ++next;
153 while (next != gaps.end())
154 {
155 if ((*it).GetEnd() >= (*next).GetStart())
156 {
157 (*it) = RecordingGap((*it).GetStart(), (*next).GetEnd());
158 next = gaps.erase(next);
159 }
160 else
161 {
162 it = next;
163 ++next;
164 }
165 }
166}
167
168static double score_gaps(const RecordingInfo &ri, const RecordingGaps &gaps)
169{
170 RecordingGaps::const_iterator it = gaps.begin();
171 if (it == gaps.end())
172 return 1.0;
173
174 QDateTime start = get_start(ri);
175
176 double program_length = start.secsTo(get_end(ri));
177 if (program_length < 1.0)
178 return 0.0;
179
180 double score = 1.0;
181 for (; it != gaps.end(); ++it)
182 {
183 double gap_start = start.secsTo((*it).GetStart());
184 double gap_end = start.secsTo((*it).GetEnd());
185 double gap_length = gap_end - gap_start;
186 double rel_start = gap_start / program_length;
187 double rel_end = gap_end / program_length;
188 double rel_center = (rel_start + rel_end) * 0.5;
189 double rel_length = rel_end - rel_start;
190
191 /*
192 LOG(VB_GENERAL, LOG_INFO,
193 QString("%1 gap(%2,%3,%4) rel(%5,%6,%7)")
194 .arg((*it).toString())
195 .arg(gap_start).arg(gap_end).arg(gap_length)
196 .arg(rel_start).arg(rel_end).arg(rel_length));
197 */
198
199 if (rel_center >= 0.9 || rel_end >= 0.95)
200 rel_length *= 4;
201
202 if (rel_center < 0.1)
203 rel_length *= 2;
204
205 if (gap_length > 5) // 5 secs
206 rel_length *= 1.5;
207
208 if (gap_length > 120) // 2 minutes
209 rel_length *= 5;
210
211 // NOTE: many more scoring adjustments could be made here
212 // and we may want to tune this differently depending on
213 // program length.
214
215 score -= rel_length;
216 }
217
218 return (score > 0.0) ? score : 0.0;
219}
220
221static QDateTime get_start(const RecordingInfo &ri)
222{
223 if (ri.GetDesiredStartTime().isValid())
224 {
225 return (ri.GetScheduledStartTime() > ri.GetDesiredStartTime()) ?
227 }
228 return ri.GetScheduledStartTime();
229}
230
231static QDateTime get_end(const RecordingInfo &ri)
232{
233 if (ri.GetDesiredEndTime().isValid())
234 {
235 return (ri.GetScheduledEndTime() < ri.GetDesiredEndTime()) ?
237 }
238 return ri.GetScheduledEndTime();
239}
int GetNumSetting(const QString &key, int defaultval=0)
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:405
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:398
QString MakeUniqueKey(void) const
Creates a unique string that can be used to identify an existing recording.
Definition: programinfo.h:346
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 std::vector< uint32_t > back
Definition: goom_core.cpp:27
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