MythTV  master
BlankFrameDetector.cpp
Go to the documentation of this file.
1 // C++ headers
2 #include <cmath>
3 #include <cstdlib>
4 #include <utility>
5 
6 // MythTV headers
7 #include "libmythbase/mythcorecontext.h" /* gContext */
8 #include "libmythtv/mythplayer.h"
9 
10 // Commercial Flagging headers
11 #include "BlankFrameDetector.h"
12 #include "CommDetector2.h"
13 #include "FrameAnalyzer.h"
14 #include "HistogramAnalyzer.h"
15 #include "TemplateMatcher.h"
16 #include "quickselect.h"
17 
18 using namespace commDetector2;
19 using namespace frameAnalyzer;
20 
21 namespace {
22 
23 bool
24 isBlank(unsigned char median, float stddev, unsigned char maxmedian,
25  float maxstddev)
26 {
27  return ((median < maxmedian) ||
28  ((median == maxmedian) && (stddev <= maxstddev)));
29 }
30 
31 int
32 sort_ascending_uchar(const void *aa, const void *bb)
33 {
34  return *(unsigned char*)aa - *(unsigned char*)bb;
35 }
36 
37 int
38 sort_ascending_float(const void *aa, const void *bb)
39 {
40  float faa = *(float*)aa;
41  float fbb = *(float*)bb;
42  return faa < fbb ? -1 : faa == fbb ? 0 : 1;
43 }
44 
45 bool
46 pickmedian(const unsigned char medianval,
47  unsigned char minval, unsigned char maxval)
48 {
49  return medianval >= minval && medianval <= maxval;
50 }
51 
52 void
53 computeBlankMap(FrameAnalyzer::FrameMap *blankMap, long long nframes,
54  const unsigned char *median, const float *stddev,
55  const unsigned char *monochromatic)
56 {
57  /*
58  * Select a "black" value based on a curve, to deal with varying "black"
59  * levels.
60  */
61  const unsigned char MINBLANKMEDIAN = 1;
62  const unsigned char MAXBLANKMEDIAN = 96;
63  const float MEDIANPCTILE = 0.95;
64  const float STDDEVPCTILE = 0.85;
65 
66  long long frameno = 1;
67  long long segb = 0;
68  long long sege = 0;
69 
70  /* Count and select for monochromatic frames. */
71 
72  long long nblanks = 0;
73  for (frameno = 0; frameno < nframes; frameno++)
74  {
75  if (monochromatic[frameno] && pickmedian(median[frameno],
76  MINBLANKMEDIAN, MAXBLANKMEDIAN))
77  nblanks++;
78  }
79 
80  if (!nblanks)
81  {
82  /* No monochromatic frames. */
83  LOG(VB_COMMFLAG, LOG_INFO,
84  "BlankFrameDetector::computeBlankMap: No blank frames.");
85  return;
86  }
87 
88  /* Select percentile values from monochromatic frames. */
89 
90  auto *blankmedian = new unsigned char[nblanks];
91  auto *blankstddev = new float[nblanks];
92  long long blankno = 0;
93  for (frameno = 0; frameno < nframes; frameno++)
94  {
95  if (monochromatic[frameno] && pickmedian(median[frameno],
96  MINBLANKMEDIAN, MAXBLANKMEDIAN))
97  {
98  blankmedian[blankno] = median[frameno];
99  blankstddev[blankno] = stddev[frameno];
100  blankno++;
101  }
102  }
103 
104  qsort(blankmedian, nblanks, sizeof(*blankmedian), sort_ascending_uchar);
105  blankno = std::min(nblanks - 1, (long long)roundf(nblanks * MEDIANPCTILE));
106  uchar maxmedian = blankmedian[blankno];
107 
108  qsort(blankstddev, nblanks, sizeof(*blankstddev), sort_ascending_float);
109  long long stddevno = std::min(nblanks - 1, (long long)roundf(nblanks * STDDEVPCTILE));
110  float maxstddev = blankstddev[stddevno];
111 
112  /* Determine effective percentile ranges (for debugging). */
113 
114  long long blankno1 = blankno;
115  long long blankno2 = blankno;
116  while (blankno1 > 0 && blankmedian[blankno1] == maxmedian)
117  blankno1--;
118  if (blankmedian[blankno1] != maxmedian)
119  blankno1++;
120  while (blankno2 < nblanks && blankmedian[blankno2] == maxmedian)
121  blankno2++;
122  if (blankno2 == nblanks)
123  blankno2--;
124 
125  long long stddevno1 = stddevno;
126  long long stddevno2 = stddevno;
127  while (stddevno1 > 0 && blankstddev[stddevno1] == maxstddev)
128  stddevno1--;
129  if (blankstddev[stddevno1] != maxstddev)
130  stddevno1++;
131  while (stddevno2 < nblanks && blankstddev[stddevno2] == maxstddev)
132  stddevno2++;
133  if (stddevno2 == nblanks)
134  stddevno2--;
135 
136  LOG(VB_COMMFLAG, LOG_INFO,
137  QString("Blanks selecting median<=%1 (%2-%3%), stddev<=%4 (%5-%6%)")
138  .arg(maxmedian)
139  .arg(blankno1 * 100 / nblanks).arg(blankno2 * 100 / nblanks)
140  .arg(maxstddev)
141  .arg(stddevno1 * 100 / nblanks).arg(stddevno2 * 100 / nblanks));
142 
143  delete []blankmedian;
144  delete []blankstddev;
145 
146  blankMap->clear();
147  if (monochromatic[0] && isBlank(median[0], stddev[0], maxmedian, maxstddev))
148  {
149  segb = 0;
150  sege = 0;
151  }
152  else
153  {
154  /* Fake up a dummy blank frame for interval calculations. */
155  blankMap->insert(0, 0);
156  segb = -1;
157  sege = -1;
158  }
159  for (frameno = 1; frameno < nframes; frameno++)
160  {
161  if (monochromatic[frameno] && isBlank(median[frameno], stddev[frameno],
162  maxmedian, maxstddev))
163  {
164  /* Blank frame. */
165  if (sege < frameno - 1)
166  {
167  /* Start counting. */
168  segb = frameno;
169  sege = frameno;
170  }
171  else
172  {
173  /* Continue counting. */
174  sege = frameno;
175  }
176  }
177  else if (sege == frameno - 1)
178  {
179  /* Transition to non-blank frame. */
180  long long seglen = frameno - segb;
181  blankMap->insert(segb, seglen);
182  }
183  }
184  if (sege == frameno - 1)
185  {
186  /* Possibly ending on blank frames. */
187  long long seglen = frameno - segb;
188  blankMap->insert(segb, seglen);
189  }
190 
191  FrameAnalyzer::FrameMap::Iterator iiblank = blankMap->end();
192  --iiblank;
193  if (iiblank.key() + *iiblank < nframes)
194  {
195  /*
196  * Didn't end on blank frames, so add a dummy blank frame at the
197  * end.
198  */
199  blankMap->insert(nframes - 1, 0);
200  }
201 }
202 
203 void
204 computeBreakMap(FrameAnalyzer::FrameMap *breakMap,
205  const FrameAnalyzer::FrameMap *blankMap, float fps,
206  int debugLevel)
207 {
208  /*
209  * TUNABLE:
210  *
211  * Common commercial-break lengths.
212  */
213  struct breakType {
214  std::chrono::seconds m_len;
215  std::chrono::seconds m_delta;
216  };
217  static constexpr std::array<const breakType,4> kBreakType {{
218  /* Sort by "len". */
219  { 15s, 2s },
220  { 20s, 2s },
221  { 30s, 5s },
222  { 60s, 5s },
223  }};
224 
225  /*
226  * TUNABLE:
227  *
228  * Shortest non-commercial length, used to coalesce consecutive commercial
229  * breaks that are usually identified due to in-commercial cuts.
230  */
231  static const int kMinContentLen = (int)roundf(10 * fps);
232 
233  breakMap->clear();
234  for (FrameAnalyzer::FrameMap::const_iterator iiblank = blankMap->begin();
235  iiblank != blankMap->end();
236  ++iiblank)
237  {
238  long long brkb = iiblank.key();
239  long long iilen = *iiblank;
240  long long start = brkb + iilen / 2;
241 
242  for (const auto& type : kBreakType)
243  {
244  /* Look for next blank frame that is an acceptable distance away. */
245  FrameAnalyzer::FrameMap::const_iterator jjblank = iiblank;
246  for (++jjblank; jjblank != blankMap->end(); ++jjblank)
247  {
248  long long brke = jjblank.key();
249  long long jjlen = *jjblank;
250  long long end = brke + jjlen / 2;
251 
252  auto testlen = std::chrono::seconds(lroundf((end - start) / fps));
253  if (testlen > type.m_len + type.m_delta)
254  break; /* Too far ahead; break to next break length. */
255 
256  std::chrono::seconds delta = testlen - type.m_len;
257  delta = std::chrono::abs(delta);
258 
259  if (delta > type.m_delta)
260  continue; /* Outside delta range; try next end-blank. */
261 
262  /* Mark this commercial break. */
263  bool inserted = false;
264  for (unsigned int jj = 0;; jj++)
265  {
266  long long newbrkb = brkb + jj;
267  if (newbrkb >= brke)
268  {
269  LOG(VB_COMMFLAG, LOG_INFO,
270  QString("BF [%1,%2] ran out of slots")
271  .arg(brkb).arg(brke - 1));
272  break;
273  }
274  if (breakMap->find(newbrkb) == breakMap->end())
275  {
276  breakMap->insert(newbrkb, brke - newbrkb);
277  inserted = true;
278  break;
279  }
280  }
281  if (inserted)
282  break; /* next break type */
283  }
284  }
285  }
286 
287  if (debugLevel >= 1)
288  {
289  frameAnalyzerReportMap(breakMap, fps, "BF Break");
290  LOG(VB_COMMFLAG, LOG_INFO,
291  "BF coalescing overlapping/nearby breaks ...");
292  }
293 
294  /*
295  * Coalesce overlapping or very-nearby breaks (handles cut-scenes within a
296  * commercial).
297  */
298  for (;;)
299  {
300  bool coalesced = false;
301  FrameAnalyzer::FrameMap::iterator iibreak = breakMap->begin();
302  while (iibreak != breakMap->end())
303  {
304  long long iib = iibreak.key();
305  long long iie = iib + *iibreak;
306 
307  FrameAnalyzer::FrameMap::iterator jjbreak = iibreak;
308  ++jjbreak;
309  if (jjbreak == breakMap->end())
310  break;
311 
312  long long jjb = jjbreak.key();
313  long long jje = jjb + *jjbreak;
314 
315  if (jjb < iib)
316  {
317  /* (jjb,jje) is behind (iib,iie). */
318  ++iibreak;
319  continue;
320  }
321 
322  if (iie + kMinContentLen < jjb)
323  {
324  /* (jjb,jje) is too far ahead. */
325  ++iibreak;
326  continue;
327  }
328 
329  /* Coalesce. */
330  if (jje > iie)
331  {
332  breakMap->remove(iib); /* overlap */
333  breakMap->insert(iib, jje - iib); /* overlap */
334  }
335  breakMap->erase(jjbreak);
336  coalesced = true;
337  iibreak = breakMap->find(iib);
338  }
339  if (!coalesced)
340  break;
341  }
342 
343  /* Adjust for blank intervals. */
344  FrameAnalyzer::FrameMap::iterator iibreak = breakMap->begin();
345  while (iibreak != breakMap->end())
346  {
347  long long iib = iibreak.key();
348  long long iie = iib + *iibreak;
349  iibreak = breakMap->erase(iibreak);
350 
351  /* Trim leading blanks from commercial break. */
352  long long addb = *blankMap->find(iib);
353  addb = addb / 2;
354  if (addb > MAX_BLANK_FRAMES)
355  addb = MAX_BLANK_FRAMES;
356  iib += addb;
357  /* Add trailing blanks to commercial break. */
358  long long adde = *blankMap->find(iie);
359  iie += adde;
360  long long sube = adde / 2;
361  if (sube > MAX_BLANK_FRAMES)
362  sube = MAX_BLANK_FRAMES;
363  iie -= sube;
364  breakMap->insert(iib, iie - iib);
365  }
366 }
367 
368 }; /* namespace */
369 
370 BlankFrameDetector::BlankFrameDetector(std::shared_ptr<HistogramAnalyzer>ha,
371  const QString &debugdir)
372  : m_histogramAnalyzer(std::move(ha))
373 {
374  /*
375  * debugLevel:
376  * 0: no debugging
377  * 2: extra verbosity [O(nframes)]
378  */
379  m_debugLevel = gCoreContext->GetNumSetting("BlankFrameDetectorDebugLevel", 0);
380 
381  if (m_debugLevel >= 1)
382  createDebugDirectory(debugdir,
383  QString("BlankFrameDetector debugLevel %1").arg(m_debugLevel));
384 }
385 
388 {
390  m_histogramAnalyzer->MythPlayerInited(player, nframes);
391 
392  m_fps = player->GetFrameRate();
393 
394  QSize video_disp_dim = player->GetVideoSize();
395 
396  LOG(VB_COMMFLAG, LOG_INFO,
397  QString("BlankFrameDetector::MythPlayerInited %1x%2")
398  .arg(video_disp_dim.width())
399  .arg(video_disp_dim.height()));
400 
401  return ares;
402 }
403 
405 BlankFrameDetector::analyzeFrame(const MythVideoFrame *frame, long long frameno,
406  long long *pNextFrame)
407 {
408  *pNextFrame = kNextFrame;
409 
410  if (m_histogramAnalyzer->analyzeFrame(frame, frameno) ==
412  return ANALYZE_OK;
413 
414  LOG(VB_COMMFLAG, LOG_INFO,
415  QString("BlankFrameDetector::analyzeFrame error at frame %1")
416  .arg(frameno));
417  return ANALYZE_ERROR;
418 }
419 
420 int
421 BlankFrameDetector::finished(long long nframes, bool final)
422 {
423  if (m_histogramAnalyzer->finished(nframes, final))
424  return -1;
425 
426  LOG(VB_COMMFLAG, LOG_INFO, QString("BlankFrameDetector::finished(%1)")
427  .arg(nframes));
428 
429  /* Identify all sequences of blank frames (blankMap). */
430  computeBlankMap(&m_blankMap, nframes,
431  m_histogramAnalyzer->getMedians(), m_histogramAnalyzer->getStdDevs(),
432  m_histogramAnalyzer->getMonochromatics());
433  if (m_debugLevel >= 2)
435 
436  return 0;
437 }
438 
439 int
441  const TemplateMatcher *templateMatcher)
442 {
443  /*
444  * See TemplateMatcher::templateCoverage; some commercial breaks have
445  * logos. Conversely, any logo breaks are probably really breaks, so prefer
446  * those over blank-frame-calculated breaks.
447  */
448  const FrameAnalyzer::FrameMap *logoBreakMap = templateMatcher->getBreaks();
449 
450  /* TUNABLE: see TemplateMatcher::adjustForBlanks */
451  const int MAXBLANKADJUSTMENT = (int)roundf(5 * m_fps); /* frames */
452 
453  LOG(VB_COMMFLAG, LOG_INFO, "BlankFrameDetector adjusting for logo surplus");
454 
455  /*
456  * For each logo break, find the blank frames closest to its beginning and
457  * end. This helps properly support CommSkipAllBlanks.
458  */
459  for (FrameAnalyzer::FrameMap::const_iterator ii =
460  logoBreakMap->constBegin();
461  ii != logoBreakMap->constEnd();
462  ++ii)
463  {
464  /* Get bounds of beginning of logo break. */
465  long long iikey = ii.key();
466  long long iibb = iikey - MAXBLANKADJUSTMENT;
467  long long iiee = iikey + MAXBLANKADJUSTMENT;
468  FrameAnalyzer::FrameMap::Iterator jjfound = m_blankMap.end();
469 
470  /* Look for a blank frame near beginning of logo break. */
471  for (auto jj = m_blankMap.begin(); jj != m_blankMap.end(); ++jj)
472  {
473  long long jjbb = jj.key();
474  long long jjee = jjbb + *jj;
475 
476  if (iiee < jjbb)
477  break; /* No nearby blank frames. */
478 
479  if (jjee < iibb)
480  continue; /* Too early; keep looking. */
481 
482  jjfound = jj;
483  if (iikey <= jjbb)
484  {
485  /*
486  * Prefer the first blank frame beginning after the logo break
487  * begins.
488  */
489  break;
490  }
491  }
492 
493  /* Adjust blank frame to begin with logo break beginning. */
494  if (jjfound != m_blankMap.end())
495  {
496  long long jjee = jjfound.key() + *jjfound;
497  m_blankMap.erase(jjfound);
498  if (jjee <= iikey)
499  {
500  /* Move blank frame to beginning of logo break. */
501  m_blankMap.remove(iikey);
502  m_blankMap.insert(iikey, 1);
503  }
504  else
505  {
506  /* Adjust blank frame to begin with logo break. */
507  m_blankMap.insert(iikey, jjee - iikey);
508  }
509  }
510 
511  /* Get bounds of end of logo break. */
512  long long kkkey = ii.key() + *ii;
513  long long kkbb = kkkey - MAXBLANKADJUSTMENT;
514  long long kkee = kkkey + MAXBLANKADJUSTMENT;
515  FrameAnalyzer::FrameMap::Iterator mmfound = m_blankMap.end();
516 
517  /* Look for a blank frame near end of logo break. */
518  for (auto mm = m_blankMap.begin(); mm != m_blankMap.end(); ++mm)
519  {
520  long long mmbb = mm.key();
521  long long mmee = mmbb + *mm;
522 
523  if (kkee < mmbb)
524  break; /* No nearby blank frames. */
525 
526  if (mmee < kkbb)
527  continue; /* Too early; keep looking. */
528 
529  /* Prefer the last blank frame ending before the logo break ends. */
530  if (mmee < kkkey || mmfound == m_blankMap.end())
531  mmfound = mm;
532  if (mmee >= kkkey)
533  break;
534  }
535 
536  /* Adjust blank frame to end with logo break end. */
537  if (mmfound != m_blankMap.end())
538  {
539  long long mmbb = mmfound.key();
540  if (mmbb < kkkey)
541  {
542  /* Adjust blank frame to end with logo break. */
543  m_blankMap.remove(mmbb);
544  m_blankMap.insert(mmbb, kkkey - mmbb);
545  }
546  else
547  {
548  /* Move blank frame to end of logo break. */
549  m_blankMap.erase(mmfound);
550  m_blankMap.remove(kkkey - 1);
551  m_blankMap.insert(kkkey - 1, 1);
552  }
553  }
554  }
555 
556  /*
557  * Compute breaks (breakMap).
558  */
559  computeBreakMap(&m_breakMap, &m_blankMap, m_fps, m_debugLevel);
560 
561  /*
562  * Expand blank-frame breaks to fully include overlapping logo breaks.
563  * Fully include logo breaks that don't include any blank-frame breaks.
564  */
565  for (FrameAnalyzer::FrameMap::const_iterator ii =
566  logoBreakMap->constBegin();
567  ii != logoBreakMap->constEnd();
568  ++ii)
569  {
570  long long iibb = ii.key();
571  long long iiee = iibb + *ii;
572  bool overlap = false;
573 
574  for (auto jj = m_breakMap.begin(); jj != m_breakMap.end(); )
575  {
576  long long jjbb = jj.key();
577  long long jjee = jjbb + *jj;
578 
579  if (iiee < jjbb)
580  {
581  if (!overlap)
582  {
583  /* Fully incorporate logo break */
584  m_breakMap.insert(iibb, iiee - iibb);
585  }
586  break;
587  }
588 
589  if (iibb < jjbb && jjbb < iiee)
590  {
591  /* End of logo break includes beginning of blank-frame break. */
592  overlap = true;
593  jj = m_breakMap.erase(jj);
594  m_breakMap.insert(iibb, std::max(iiee, jjee) - iibb);
595  continue;
596  }
597  if (jjbb < iibb && iibb < jjee)
598  {
599  /* End of blank-frame break includes beginning of logo break. */
600  overlap = true;
601  if (jjee < iiee)
602  {
603  m_breakMap.remove(jjbb);
604  m_breakMap.insert(jjbb, iiee - jjbb);
605  }
606  }
607 
608  jj++;
609  }
610  }
611 
612  frameAnalyzerReportMap(&m_breakMap, m_fps, "BF Break");
613  return 0;
614 }
615 
616 int
618  const TemplateMatcher *templateMatcher)
619 {
620  (void)templateMatcher; /* gcc */
621 
622  LOG(VB_COMMFLAG, LOG_INFO, "BlankFrameDetector adjusting for "
623  "too little logo coverage (unimplemented)");
624  return 0;
625 }
626 
627 int
629 {
630  if (m_breakMap.empty())
631  {
632  /* Compute breaks (m_breakMap). */
633  computeBreakMap(&m_breakMap, &m_blankMap, m_fps, m_debugLevel);
634  frameAnalyzerReportMap(&m_breakMap, m_fps, "BF Break");
635  }
636 
637  breaks->clear();
638  for (auto bb = m_breakMap.begin(); bb != m_breakMap.end(); ++bb)
639  breaks->insert(bb.key(), *bb);
640 
641  return 0;
642 }
643 
644 int
646 {
647  return m_histogramAnalyzer->reportTime();
648 }
649 
650 /* vim: set expandtab tabstop=4 shiftwidth=4: */
TemplateMatcher.h
TemplateMatcher::getBreaks
const FrameAnalyzer::FrameMap * getBreaks(void) const
Definition: TemplateMatcher.h:54
FrameAnalyzer::ANALYZE_OK
@ ANALYZE_OK
Definition: FrameAnalyzer.h:37
CommDetector2.h
FrameAnalyzer::FrameMap
QMap< long long, long long > FrameMap
Definition: FrameAnalyzer.h:45
quickselect.h
MythPlayer::GetFrameRate
float GetFrameRate(void) const
Definition: mythplayer.h:134
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
mythplayer.h
MythPlayer
Definition: mythplayer.h:84
BlankFrameDetector::reportTime
int reportTime(void) const override
Definition: BlankFrameDetector.cpp:645
BlankFrameDetector::analyzeFrame
enum analyzeFrameResult analyzeFrame(const MythVideoFrame *frame, long long frameno, long long *pNextFrame) override
Definition: BlankFrameDetector.cpp:405
commDetector2::createDebugDirectory
void createDebugDirectory(const QString &dirname, const QString &comment)
Definition: CommDetector2.cpp:225
BlankFrameDetector::m_histogramAnalyzer
std::shared_ptr< HistogramAnalyzer > m_histogramAnalyzer
Definition: BlankFrameDetector.h:40
BlankFrameDetector::computeForLogoSurplus
int computeForLogoSurplus(const TemplateMatcher *templateMatcher)
Definition: BlankFrameDetector.cpp:440
BlankFrameDetector::m_blankMap
FrameAnalyzer::FrameMap m_blankMap
Definition: BlankFrameDetector.h:43
BlankFrameDetector::BlankFrameDetector
BlankFrameDetector(std::shared_ptr< HistogramAnalyzer > ha, const QString &debugdir)
Definition: BlankFrameDetector.cpp:370
BlankFrameDetector.h
BlankFrameDetector::computeBreaks
int computeBreaks(FrameMap *breaks)
Definition: BlankFrameDetector.cpp:628
FrameAnalyzer::kNextFrame
static const long long kNextFrame
Definition: FrameAnalyzer.h:59
FrameAnalyzer::analyzeFrameResult
analyzeFrameResult
Definition: FrameAnalyzer.h:36
FrameAnalyzer.h
FrameAnalyzer::ANALYZE_ERROR
@ ANALYZE_ERROR
Definition: FrameAnalyzer.h:38
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:54
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:910
BlankFrameDetector::finished
int finished(long long nframes, bool final) override
Definition: BlankFrameDetector.cpp:421
HistogramAnalyzer.h
frameAnalyzer::frameAnalyzerReportMap
void frameAnalyzerReportMap(const FrameAnalyzer::FrameMap *frameMap, float fps, const char *comment)
Definition: FrameAnalyzer.cpp:17
BlankFrameDetector::m_fps
float m_fps
Definition: BlankFrameDetector.h:41
mythcorecontext.h
std
Definition: mythchrono.h:23
MythPlayer::GetVideoSize
QSize GetVideoSize(void) const
Definition: mythplayer.h:132
commDetector2
Definition: CommDetector2.cpp:189
BlankFrameDetector::m_breakMap
FrameAnalyzer::FrameMap m_breakMap
Definition: BlankFrameDetector.h:44
frameAnalyzer
Definition: FrameAnalyzer.cpp:7
MAX_BLANK_FRAMES
static constexpr int64_t MAX_BLANK_FRAMES
Definition: CommDetectorBase.h:11
MythVideoFrame
Definition: mythframe.h:88
BlankFrameDetector::MythPlayerInited
enum analyzeFrameResult MythPlayerInited(MythPlayer *player, long long nframes) override
Definition: BlankFrameDetector.cpp:387
BlankFrameDetector::computeForLogoDeficit
static int computeForLogoDeficit(const TemplateMatcher *templateMatcher)
Definition: BlankFrameDetector.cpp:617
TemplateMatcher
Definition: TemplateMatcher.h:31
BlankFrameDetector::m_debugLevel
int m_debugLevel
Definition: BlankFrameDetector.h:47
frameAnalyzer::frameAnalyzerReportMapms
void frameAnalyzerReportMapms(const FrameAnalyzer::FrameMap *frameMap, float fps, const char *comment)
Definition: FrameAnalyzer.cpp:53