25 isBlank(
unsigned char median,
float stddev,
unsigned char maxmedian,
28 return ((median < maxmedian) ||
29 ((median == maxmedian) && (stddev <= maxstddev)));
35 return *(
unsigned char*)aa - *(
unsigned char*)bb;
41 float faa = *(
float*)aa;
42 float fbb = *(
float*)bb;
52 unsigned char minval,
unsigned char maxval)
54 return medianval >= minval && medianval <= maxval;
59 const unsigned char *median,
const float *stddev,
60 const unsigned char *monochromatic)
66 const unsigned char MINBLANKMEDIAN = 1;
67 const unsigned char MAXBLANKMEDIAN = 96;
68 const float MEDIANPCTILE = 0.95;
69 const float STDDEVPCTILE = 0.85;
71 long long frameno = 1;
77 long long nblanks = 0;
78 for (frameno = 0; frameno < nframes; frameno++)
80 if (monochromatic[frameno] &&
pickmedian(median[frameno],
81 MINBLANKMEDIAN, MAXBLANKMEDIAN))
88 LOG(VB_COMMFLAG, LOG_INFO,
89 "BlankFrameDetector::computeBlankMap: No blank frames.");
95 auto *blankmedian =
new unsigned char[nblanks];
96 auto *blankstddev =
new float[nblanks];
97 long long blankno = 0;
98 for (frameno = 0; frameno < nframes; frameno++)
100 if (monochromatic[frameno] &&
pickmedian(median[frameno],
101 MINBLANKMEDIAN, MAXBLANKMEDIAN))
103 blankmedian[blankno] = median[frameno];
104 blankstddev[blankno] = stddev[frameno];
110 blankno = std::min(nblanks - 1, (
long long)roundf(nblanks * MEDIANPCTILE));
111 uchar maxmedian = blankmedian[blankno];
114 long long stddevno = std::min(nblanks - 1, (
long long)roundf(nblanks * STDDEVPCTILE));
115 float maxstddev = blankstddev[stddevno];
119 long long blankno1 = blankno;
120 long long blankno2 = blankno;
121 while (blankno1 > 0 && blankmedian[blankno1] == maxmedian)
123 if (blankmedian[blankno1] != maxmedian)
125 while (blankno2 < nblanks && blankmedian[blankno2] == maxmedian)
127 if (blankno2 == nblanks)
130 long long stddevno1 = stddevno;
131 long long stddevno2 = stddevno;
132 while (stddevno1 > 0 && blankstddev[stddevno1] == maxstddev)
134 if (blankstddev[stddevno1] != maxstddev)
136 while (stddevno2 < nblanks && blankstddev[stddevno2] == maxstddev)
138 if (stddevno2 == nblanks)
141 LOG(VB_COMMFLAG, LOG_INFO,
142 QString(
"Blanks selecting median<=%1 (%2-%3%), stddev<=%4 (%5-%6%)")
144 .arg(blankno1 * 100 / nblanks).arg(blankno2 * 100 / nblanks)
146 .arg(stddevno1 * 100 / nblanks).arg(stddevno2 * 100 / nblanks));
148 delete []blankmedian;
149 delete []blankstddev;
152 if (monochromatic[0] &&
isBlank(median[0], stddev[0], maxmedian, maxstddev))
160 blankMap->insert(0, 0);
164 for (frameno = 1; frameno < nframes; frameno++)
166 if (monochromatic[frameno] &&
isBlank(median[frameno], stddev[frameno],
167 maxmedian, maxstddev))
170 if (sege < frameno - 1)
182 else if (sege == frameno - 1)
185 long long seglen = frameno - segb;
186 blankMap->insert(segb, seglen);
189 if (sege == frameno - 1)
192 long long seglen = frameno - segb;
193 blankMap->insert(segb, seglen);
196 FrameAnalyzer::FrameMap::Iterator iiblank = blankMap->end();
198 if (iiblank.key() + *iiblank < nframes)
204 blankMap->insert(nframes - 1, 0);
219 std::chrono::seconds m_len;
220 std::chrono::seconds m_delta;
222 static constexpr std::array<const breakType,4> kBreakType {{
236 static const int kMinContentLen = (int)roundf(10 * fps);
239 for (FrameAnalyzer::FrameMap::const_iterator iiblank = blankMap->begin();
240 iiblank != blankMap->end();
243 long long brkb = iiblank.key();
244 long long iilen = *iiblank;
245 long long start = brkb + (iilen / 2);
247 for (
const auto&
type : kBreakType)
250 FrameAnalyzer::FrameMap::const_iterator jjblank = iiblank;
251 for (++jjblank; jjblank != blankMap->end(); ++jjblank)
253 long long brke = jjblank.key();
254 long long jjlen = *jjblank;
255 long long end = brke + (jjlen / 2);
257 auto testlen = std::chrono::seconds(lroundf((end - start) / fps));
258 if (testlen >
type.m_len +
type.m_delta)
261 std::chrono::seconds delta = testlen -
type.m_len;
262 delta = std::chrono::abs(delta);
264 if (delta >
type.m_delta)
268 bool inserted =
false;
269 for (
unsigned int jj = 0;; jj++)
271 long long newbrkb = brkb + jj;
274 LOG(VB_COMMFLAG, LOG_INFO,
275 QString(
"BF [%1,%2] ran out of slots")
276 .arg(brkb).arg(brke - 1));
279 if (breakMap->find(newbrkb) == breakMap->end())
281 breakMap->insert(newbrkb, brke - newbrkb);
295 LOG(VB_COMMFLAG, LOG_INFO,
296 "BF coalescing overlapping/nearby breaks ...");
305 bool coalesced =
false;
306 FrameAnalyzer::FrameMap::iterator iibreak = breakMap->begin();
307 while (iibreak != breakMap->end())
309 long long iib = iibreak.key();
310 long long iie = iib + *iibreak;
312 FrameAnalyzer::FrameMap::iterator jjbreak = iibreak;
314 if (jjbreak == breakMap->end())
317 long long jjb = jjbreak.key();
318 long long jje = jjb + *jjbreak;
327 if (iie + kMinContentLen < jjb)
337 breakMap->remove(iib);
338 breakMap->insert(iib, jje - iib);
340 breakMap->erase(jjbreak);
342 iibreak = breakMap->find(iib);
349 FrameAnalyzer::FrameMap::iterator iibreak = breakMap->begin();
350 while (iibreak != breakMap->end())
352 long long iib = iibreak.key();
353 long long iie = iib + *iibreak;
354 iibreak = breakMap->erase(iibreak);
357 auto iter = blankMap->find(iib);
358 if (iter == blankMap->end())
360 long long addb = *iter;
365 iter = blankMap->find(iib);
366 if (iter == blankMap->end())
368 long long adde = *iter;
370 long long sube = adde / 2;
373 breakMap->insert(iib, iie - iib);
380 const QString &debugdir)
381 : m_histogramAnalyzer(std::move(ha))
392 QString(
"BlankFrameDetector debugLevel %1").arg(
m_debugLevel));
405 LOG(VB_COMMFLAG, LOG_INFO,
406 QString(
"BlankFrameDetector::MythPlayerInited %1x%2")
407 .arg(video_disp_dim.width())
408 .arg(video_disp_dim.height()));
415 long long *pNextFrame)
423 LOG(VB_COMMFLAG, LOG_INFO,
424 QString(
"BlankFrameDetector::analyzeFrame error at frame %1")
435 LOG(VB_COMMFLAG, LOG_INFO, QString(
"BlankFrameDetector::finished(%1)")
460 const int MAXBLANKADJUSTMENT = (int)roundf(5 *
m_fps);
462 LOG(VB_COMMFLAG, LOG_INFO,
"BlankFrameDetector adjusting for logo surplus");
468 for (FrameAnalyzer::FrameMap::const_iterator ii =
469 logoBreakMap->constBegin();
470 ii != logoBreakMap->constEnd();
474 long long iikey = ii.key();
475 long long iibb = iikey - MAXBLANKADJUSTMENT;
476 long long iiee = iikey + MAXBLANKADJUSTMENT;
477 FrameAnalyzer::FrameMap::Iterator jjfound =
m_blankMap.end();
482 long long jjbb = jj.key();
483 long long jjee = jjbb + *jj;
505 long long jjee = jjfound.key() + *jjfound;
521 long long kkkey = ii.key() + *ii;
522 long long kkbb = kkkey - MAXBLANKADJUSTMENT;
523 long long kkee = kkkey + MAXBLANKADJUSTMENT;
524 FrameAnalyzer::FrameMap::Iterator mmfound =
m_blankMap.end();
529 long long mmbb = mm.key();
530 long long mmee = mmbb + *mm;
539 if (mmee < kkkey || mmfound ==
m_blankMap.end())
548 long long mmbb = mmfound.key();
574 for (FrameAnalyzer::FrameMap::const_iterator ii =
575 logoBreakMap->constBegin();
576 ii != logoBreakMap->constEnd();
579 long long iibb = ii.key();
580 long long iiee = iibb + *ii;
581 bool overlap =
false;
585 long long jjbb = jj.key();
586 long long jjee = jjbb + *jj;
598 if (iibb < jjbb && jjbb < iiee)
603 m_breakMap.insert(iibb, std::max(iiee, jjee) - iibb);
606 if (jjbb < iibb && iibb < jjee)
629 LOG(VB_COMMFLAG, LOG_INFO,
"BlankFrameDetector adjusting for "
630 "too little logo coverage (unimplemented)");
646 breaks->insert(bb.key(), *bb);