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  auto iter = blankMap->find(iib);
353  if (iter == blankMap->end())
354  break;
355  long long addb = *iter;
356  addb = addb / 2;
357  if (addb > MAX_BLANK_FRAMES)
358  addb = MAX_BLANK_FRAMES;
359  iib += addb;
360  /* Add trailing blanks to commercial break. */
361  iter = blankMap->find(iib);
362  if (iter == blankMap->end())
363  break;
364  long long adde = *iter;
365  iie += adde;
366  long long sube = adde / 2;
367  if (sube > MAX_BLANK_FRAMES)
368  sube = MAX_BLANK_FRAMES;
369  iie -= sube;
370  breakMap->insert(iib, iie - iib);
371  }
372 }
373 
374 }; /* namespace */
375 
376 BlankFrameDetector::BlankFrameDetector(std::shared_ptr<HistogramAnalyzer>ha,
377  const QString &debugdir)
378  : m_histogramAnalyzer(std::move(ha))
379 {
380  /*
381  * debugLevel:
382  * 0: no debugging
383  * 2: extra verbosity [O(nframes)]
384  */
385  m_debugLevel = gCoreContext->GetNumSetting("BlankFrameDetectorDebugLevel", 0);
386 
387  if (m_debugLevel >= 1)
388  createDebugDirectory(debugdir,
389  QString("BlankFrameDetector debugLevel %1").arg(m_debugLevel));
390 }
391 
394 {
396  m_histogramAnalyzer->MythPlayerInited(player, nframes);
397 
398  m_fps = player->GetFrameRate();
399 
400  QSize video_disp_dim = player->GetVideoSize();
401 
402  LOG(VB_COMMFLAG, LOG_INFO,
403  QString("BlankFrameDetector::MythPlayerInited %1x%2")
404  .arg(video_disp_dim.width())
405  .arg(video_disp_dim.height()));
406 
407  return ares;
408 }
409 
411 BlankFrameDetector::analyzeFrame(const MythVideoFrame *frame, long long frameno,
412  long long *pNextFrame)
413 {
414  *pNextFrame = kNextFrame;
415 
416  if (m_histogramAnalyzer->analyzeFrame(frame, frameno) ==
418  return ANALYZE_OK;
419 
420  LOG(VB_COMMFLAG, LOG_INFO,
421  QString("BlankFrameDetector::analyzeFrame error at frame %1")
422  .arg(frameno));
423  return ANALYZE_ERROR;
424 }
425 
426 int
427 BlankFrameDetector::finished(long long nframes, bool final)
428 {
429  if (m_histogramAnalyzer->finished(nframes, final))
430  return -1;
431 
432  LOG(VB_COMMFLAG, LOG_INFO, QString("BlankFrameDetector::finished(%1)")
433  .arg(nframes));
434 
435  /* Identify all sequences of blank frames (blankMap). */
436  computeBlankMap(&m_blankMap, nframes,
437  m_histogramAnalyzer->getMedians(), m_histogramAnalyzer->getStdDevs(),
438  m_histogramAnalyzer->getMonochromatics());
439  if (m_debugLevel >= 2)
441 
442  return 0;
443 }
444 
445 int
447  const TemplateMatcher *templateMatcher)
448 {
449  /*
450  * See TemplateMatcher::templateCoverage; some commercial breaks have
451  * logos. Conversely, any logo breaks are probably really breaks, so prefer
452  * those over blank-frame-calculated breaks.
453  */
454  const FrameAnalyzer::FrameMap *logoBreakMap = templateMatcher->getBreaks();
455 
456  /* TUNABLE: see TemplateMatcher::adjustForBlanks */
457  const int MAXBLANKADJUSTMENT = (int)roundf(5 * m_fps); /* frames */
458 
459  LOG(VB_COMMFLAG, LOG_INFO, "BlankFrameDetector adjusting for logo surplus");
460 
461  /*
462  * For each logo break, find the blank frames closest to its beginning and
463  * end. This helps properly support CommSkipAllBlanks.
464  */
465  for (FrameAnalyzer::FrameMap::const_iterator ii =
466  logoBreakMap->constBegin();
467  ii != logoBreakMap->constEnd();
468  ++ii)
469  {
470  /* Get bounds of beginning of logo break. */
471  long long iikey = ii.key();
472  long long iibb = iikey - MAXBLANKADJUSTMENT;
473  long long iiee = iikey + MAXBLANKADJUSTMENT;
474  FrameAnalyzer::FrameMap::Iterator jjfound = m_blankMap.end();
475 
476  /* Look for a blank frame near beginning of logo break. */
477  for (auto jj = m_blankMap.begin(); jj != m_blankMap.end(); ++jj)
478  {
479  long long jjbb = jj.key();
480  long long jjee = jjbb + *jj;
481 
482  if (iiee < jjbb)
483  break; /* No nearby blank frames. */
484 
485  if (jjee < iibb)
486  continue; /* Too early; keep looking. */
487 
488  jjfound = jj;
489  if (iikey <= jjbb)
490  {
491  /*
492  * Prefer the first blank frame beginning after the logo break
493  * begins.
494  */
495  break;
496  }
497  }
498 
499  /* Adjust blank frame to begin with logo break beginning. */
500  if (jjfound != m_blankMap.end())
501  {
502  long long jjee = jjfound.key() + *jjfound;
503  m_blankMap.erase(jjfound);
504  if (jjee <= iikey)
505  {
506  /* Move blank frame to beginning of logo break. */
507  m_blankMap.remove(iikey);
508  m_blankMap.insert(iikey, 1);
509  }
510  else
511  {
512  /* Adjust blank frame to begin with logo break. */
513  m_blankMap.insert(iikey, jjee - iikey);
514  }
515  }
516 
517  /* Get bounds of end of logo break. */
518  long long kkkey = ii.key() + *ii;
519  long long kkbb = kkkey - MAXBLANKADJUSTMENT;
520  long long kkee = kkkey + MAXBLANKADJUSTMENT;
521  FrameAnalyzer::FrameMap::Iterator mmfound = m_blankMap.end();
522 
523  /* Look for a blank frame near end of logo break. */
524  for (auto mm = m_blankMap.begin(); mm != m_blankMap.end(); ++mm)
525  {
526  long long mmbb = mm.key();
527  long long mmee = mmbb + *mm;
528 
529  if (kkee < mmbb)
530  break; /* No nearby blank frames. */
531 
532  if (mmee < kkbb)
533  continue; /* Too early; keep looking. */
534 
535  /* Prefer the last blank frame ending before the logo break ends. */
536  if (mmee < kkkey || mmfound == m_blankMap.end())
537  mmfound = mm;
538  if (mmee >= kkkey)
539  break;
540  }
541 
542  /* Adjust blank frame to end with logo break end. */
543  if (mmfound != m_blankMap.end())
544  {
545  long long mmbb = mmfound.key();
546  if (mmbb < kkkey)
547  {
548  /* Adjust blank frame to end with logo break. */
549  m_blankMap.remove(mmbb);
550  m_blankMap.insert(mmbb, kkkey - mmbb);
551  }
552  else
553  {
554  /* Move blank frame to end of logo break. */
555  m_blankMap.erase(mmfound);
556  m_blankMap.remove(kkkey - 1);
557  m_blankMap.insert(kkkey - 1, 1);
558  }
559  }
560  }
561 
562  /*
563  * Compute breaks (breakMap).
564  */
565  computeBreakMap(&m_breakMap, &m_blankMap, m_fps, m_debugLevel);
566 
567  /*
568  * Expand blank-frame breaks to fully include overlapping logo breaks.
569  * Fully include logo breaks that don't include any blank-frame breaks.
570  */
571  for (FrameAnalyzer::FrameMap::const_iterator ii =
572  logoBreakMap->constBegin();
573  ii != logoBreakMap->constEnd();
574  ++ii)
575  {
576  long long iibb = ii.key();
577  long long iiee = iibb + *ii;
578  bool overlap = false;
579 
580  for (auto jj = m_breakMap.begin(); jj != m_breakMap.end(); )
581  {
582  long long jjbb = jj.key();
583  long long jjee = jjbb + *jj;
584 
585  if (iiee < jjbb)
586  {
587  if (!overlap)
588  {
589  /* Fully incorporate logo break */
590  m_breakMap.insert(iibb, iiee - iibb);
591  }
592  break;
593  }
594 
595  if (iibb < jjbb && jjbb < iiee)
596  {
597  /* End of logo break includes beginning of blank-frame break. */
598  overlap = true;
599  jj = m_breakMap.erase(jj);
600  m_breakMap.insert(iibb, std::max(iiee, jjee) - iibb);
601  continue;
602  }
603  if (jjbb < iibb && iibb < jjee)
604  {
605  /* End of blank-frame break includes beginning of logo break. */
606  overlap = true;
607  if (jjee < iiee)
608  {
609  m_breakMap.remove(jjbb);
610  m_breakMap.insert(jjbb, iiee - jjbb);
611  }
612  }
613 
614  jj++;
615  }
616  }
617 
618  frameAnalyzerReportMap(&m_breakMap, m_fps, "BF Break");
619  return 0;
620 }
621 
622 int
624  [[maybe_unused]] const TemplateMatcher *templateMatcher)
625 {
626  LOG(VB_COMMFLAG, LOG_INFO, "BlankFrameDetector adjusting for "
627  "too little logo coverage (unimplemented)");
628  return 0;
629 }
630 
631 int
633 {
634  if (m_breakMap.empty())
635  {
636  /* Compute breaks (m_breakMap). */
637  computeBreakMap(&m_breakMap, &m_blankMap, m_fps, m_debugLevel);
638  frameAnalyzerReportMap(&m_breakMap, m_fps, "BF Break");
639  }
640 
641  breaks->clear();
642  for (auto bb = m_breakMap.begin(); bb != m_breakMap.end(); ++bb)
643  breaks->insert(bb.key(), *bb);
644 
645  return 0;
646 }
647 
648 int
650 {
651  return m_histogramAnalyzer->reportTime();
652 }
653 
654 /* 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:135
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:649
BlankFrameDetector::analyzeFrame
enum analyzeFrameResult analyzeFrame(const MythVideoFrame *frame, long long frameno, long long *pNextFrame) override
Definition: BlankFrameDetector.cpp:411
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:446
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:376
BlankFrameDetector.h
BlankFrameDetector::computeBreaks
int computeBreaks(FrameMap *breaks)
Definition: BlankFrameDetector.cpp:632
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:55
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:911
BlankFrameDetector::finished
int finished(long long nframes, bool final) override
Definition: BlankFrameDetector.cpp:427
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:133
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:393
BlankFrameDetector::computeForLogoDeficit
static int computeForLogoDeficit(const TemplateMatcher *templateMatcher)
Definition: BlankFrameDetector.cpp:623
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