MythTV  master
CommDetector2.cpp
Go to the documentation of this file.
1 // ANSI C headers
2 #include <cmath>
3 #include <cerrno>
4 #include <chrono> // for milliseconds
5 #include <thread> // for sleep_for
6 
7 // C++ headers
8 #include <algorithm>
9 using namespace std;
10 
11 // Qt headers
12 #include <QCoreApplication>
13 #include <QDir>
14 #include <QFileInfo>
15 
16 // MythTV headers
17 #include "compat.h"
18 #include "mythdb.h"
19 #include "mythlogging.h"
20 #include "mythmiscutil.h"
21 #include "mythplayer.h"
22 #include "programinfo.h"
23 #include "channelutil.h"
24 
25 // Commercial Flagging headers
26 #include "CommDetector2.h"
27 #include "CannyEdgeDetector.h"
28 #include "FrameAnalyzer.h"
29 #include "PGMConverter.h"
30 #include "BorderDetector.h"
31 #include "HistogramAnalyzer.h"
32 #include "BlankFrameDetector.h"
33 #include "SceneChangeDetector.h"
34 #include "TemplateFinder.h"
35 #include "TemplateMatcher.h"
36 
37 namespace {
38 
39 bool stopForBreath(bool isrecording, long long frameno)
40 {
41  return (isrecording && (frameno % 100) == 0) || (frameno % 500) == 0;
42 }
43 
44 bool needToReportState(bool showprogress, bool isrecording, long long frameno)
45 {
46  return ((showprogress || isrecording) && (frameno % 100) == 0) ||
47  (frameno % 500) == 0;
48 }
49 
50 void waitForBuffer(const struct timeval *framestart, int minlag, int flaglag,
51  float fps, bool fullspeed)
52 {
53  long usperframe = (long)(1000000.0F / fps);
54  struct timeval now {};
55  struct timeval elapsed {};
56 
57  (void)gettimeofday(&now, nullptr);
58  timersub(&now, framestart, &elapsed);
59 
60  // Sleep for one frame's worth of time.
61  long sleepus = usperframe - elapsed.tv_sec * 1000000 - elapsed.tv_usec;
62  if (sleepus <= 0)
63  return;
64 
65  if (flaglag > minlag)
66  {
67  // Try to catch up; reduce sleep time.
68  if (fullspeed)
69  return;
70  sleepus /= 4;
71  }
72  else if (flaglag < minlag)
73  {
74  // Slow down; increase sleep time
75  sleepus = sleepus * 3 / 2;
76  }
77  std::this_thread::sleep_for(std::chrono::microseconds(sleepus));
78 }
79 
80 bool MythPlayerInited(FrameAnalyzerItem &pass,
81  FrameAnalyzerItem &finishedAnalyzers,
82  FrameAnalyzerItem &deadAnalyzers,
83  MythPlayer *player,
84  long long nframes)
85 {
86  auto it = pass.begin();
87  while (it != pass.end())
88  {
90  (*it)->MythPlayerInited(player, nframes);
91 
92  if ((FrameAnalyzer::ANALYZE_OK == ares) ||
94  {
95  ++it;
96  }
97  else if (FrameAnalyzer::ANALYZE_FINISHED == ares)
98  {
99  finishedAnalyzers.push_back(*it);
100  it = pass.erase(it);
101  }
102  else if (FrameAnalyzer::ANALYZE_FATAL == ares)
103  {
104  deadAnalyzers.push_back(*it);
105  it = pass.erase(it);
106  }
107  else
108  {
109  LOG(VB_GENERAL, LOG_ERR,
110  QString("Unexpected return value from %1::MythPlayerInited: %2")
111  .arg((*it)->name()).arg(ares));
112  return false;
113  }
114  }
115 
116  return true;
117 }
118 
119 long long processFrame(FrameAnalyzerItem &pass,
120  FrameAnalyzerItem &finishedAnalyzers,
121  FrameAnalyzerItem &deadAnalyzers,
122  const VideoFrame *frame,
123  long long frameno)
124 {
125  long long nextFrame = 0;
126  long long minNextFrame = FrameAnalyzer::kAnyFrame;
127 
128  auto it = pass.begin();
129  while (it != pass.end())
130  {
132  (*it)->analyzeFrame(frame, frameno, &nextFrame);
133 
134  if ((FrameAnalyzer::ANALYZE_OK == ares) ||
136  {
137  minNextFrame = std::min(minNextFrame, nextFrame);
138  ++it;
139  }
140  else if (ares == FrameAnalyzer::ANALYZE_FINISHED)
141  {
142  finishedAnalyzers.push_back(*it);
143  it = pass.erase(it);
144  }
145  else
146  {
147  if (ares != FrameAnalyzer::ANALYZE_FATAL)
148  {
149  LOG(VB_GENERAL, LOG_ERR,
150  QString("Unexpected return value from %1::analyzeFrame: %2")
151  .arg((*it)->name()).arg(ares));
152  }
153 
154  deadAnalyzers.push_back(*it);
155  it = pass.erase(it);
156  }
157  }
158 
159  if (minNextFrame == FrameAnalyzer::kAnyFrame)
160  minNextFrame = FrameAnalyzer::kNextFrame;
161 
162  if (minNextFrame == FrameAnalyzer::kNextFrame)
163  minNextFrame = frameno + 1;
164 
165  return minNextFrame;
166 }
167 
168 int passFinished(FrameAnalyzerItem &pass, long long nframes, bool final)
169 {
170  for (auto & pas : pass)
171  (void)pas->finished(nframes, final);
172 
173  return 0;
174 }
175 
176 int passReportTime(const FrameAnalyzerItem &pass)
177 {
178  for (auto *pas : pass)
179  (void)pas->reportTime();
180 
181  return 0;
182 }
183 
184 bool searchingForLogo(TemplateFinder *tf, const FrameAnalyzerItem &pass)
185 {
186  if (!tf)
187  return false;
188 
189  auto it = std::find(pass.cbegin(), pass.cend(), tf);
190  return it != pass.end();
191 }
192 
193 }; // namespace
194 
195 namespace commDetector2 {
196 
197 QString debugDirectory(int chanid, const QDateTime& recstartts)
198 {
199  /*
200  * Should deleting a recording also delete its associated debug directory?
201  */
203  query.prepare("SELECT basename"
204  " FROM recorded"
205  " WHERE chanid = :CHANID"
206  " AND starttime = :STARTTIME"
207  ";");
208  query.bindValue(":CHANID", chanid);
209  query.bindValue(":STARTTIME", recstartts);
210  if (!query.exec() || !query.next())
211  {
212  MythDB::DBError("Error in CommDetector2::CommDetector2", query);
213  return "";
214  }
215 
216  ProgramInfo pginfo(chanid, recstartts);
217 
218  if (!pginfo.GetChanID())
219  return "";
220 
221  QString pburl = pginfo.GetPlaybackURL(true);
222  if (!pburl.startsWith("/"))
223  return "";
224 
225  QString basename(query.value(0).toString());
226  QString m_debugdir = pburl.section('/',0,-2) + "/" + basename + "-debug";
227 
228  return m_debugdir;
229 }
230 
231 void createDebugDirectory(const QString& dirname, const QString& comment)
232 {
233  QDir qdir(dirname);
234  if (qdir.exists())
235  {
236  LOG(VB_COMMFLAG, LOG_INFO, QString("%1 using debug directory \"%2\"")
237  .arg(comment).arg(dirname));
238  }
239  else
240  {
241  if (qdir.mkdir(dirname))
242  {
243  LOG(VB_COMMFLAG, LOG_INFO,
244  QString("%1 created debug directory \"%1\"")
245  .arg(comment).arg(dirname));
246  }
247  else
248  {
249  LOG(VB_COMMFLAG, LOG_INFO, QString("%1 failed to create \"%2\": %3")
250  .arg(comment).arg(dirname).arg(strerror(errno)));
251  }
252  }
253 }
254 
255 QString frameToTimestamp(long long frameno, float fps)
256 {
257  int ms = (int)roundf(frameno / fps * 1000);
258  if (ms % 1000 >= 500)
259  ms += 500; // Round up to next second
260  return MythFormatTimeMs(ms, "hh:mm:ss");
261 }
262 
263 QString frameToTimestampms(long long frameno, float fps)
264 {
265  int ms = (int)roundf(frameno / fps * 1000);
266  QString timestr = MythFormatTimeMs(ms, "mm:ss.zzz");
267  timestr.chop(1); // Chop 1 to return hundredths
268  return timestr;
269 }
270 
271 QString strftimeval(const struct timeval *tv)
272 {
273  return QString("%1.%2")
274  .arg(tv->tv_sec).arg(tv->tv_usec, 6, 10, QChar(QChar('0')));
275 }
276 
277 }; /* namespace */
278 
279 using namespace commDetector2;
280 
282  SkipType commDetectMethod_in,
283  bool showProgress_in,
284  bool fullSpeed_in,
285  MythPlayer *player_in,
286  int chanid,
287  QDateTime startts_in,
288  QDateTime endts_in,
289  QDateTime recstartts_in,
290  QDateTime recendts_in,
291  bool useDB) :
292  m_commDetectMethod((SkipType)(commDetectMethod_in & ~COMM_DETECT_2)),
293  m_showProgress(showProgress_in), m_fullSpeed(fullSpeed_in),
294  m_player(player_in),
295  m_startts(std::move(startts_in)), m_endts(std::move(endts_in)),
296  m_recstartts(std::move(recstartts_in)), m_recendts(std::move(recendts_in)),
297  m_isRecording(MythDate::current() < m_recendts),
298  m_debugdir("")
299 {
300  FrameAnalyzerItem pass0;
301  FrameAnalyzerItem pass1;
302 
303  if (useDB)
305 
306  std::shared_ptr<PGMConverter> pgmConverter =
307  std::make_shared<PGMConverter>();
308  std::shared_ptr<BorderDetector> borderDetector =
309  std::make_shared<BorderDetector>();
310  std::shared_ptr<HistogramAnalyzer> histogramAnalyzer =
311  std::make_shared<HistogramAnalyzer>(pgmConverter, borderDetector,
312  m_debugdir);
313 
314  /*
315  * Look for blank frames to use as delimiters between commercial and
316  * non-commercial segments.
317  */
319  {
321  {
322  m_blankFrameDetector = new BlankFrameDetector(histogramAnalyzer,
323  m_debugdir);
324  pass1.push_back(m_blankFrameDetector);
325  }
326  }
327 
328  /*
329  * Look for "scene changes" to use as delimiters between commercial and
330  * non-commercial segments.
331  */
333  {
335  {
336  m_sceneChangeDetector = new SceneChangeDetector(histogramAnalyzer,
337  m_debugdir);
338  pass1.push_back(m_sceneChangeDetector);
339  }
340  }
341 
342  /*
343  * Logo Detection requires two passes. The first pass looks for static
344  * areas of the screen to look for logos and generate a template. The
345  * second pass matches the template against the frame and computes the
346  * closeness of the match.
347  */
349  {
350  std::shared_ptr<CannyEdgeDetector> cannyEdgeDetector =
351  std::make_shared<CannyEdgeDetector>();
352 
353  if (!m_logoFinder)
354  {
355  m_logoFinder = new TemplateFinder(pgmConverter, borderDetector,
356  cannyEdgeDetector, m_player, m_recstartts.secsTo(m_recendts),
357  m_debugdir);
358  pass0.push_back(m_logoFinder);
359  }
360 
361  if (!m_logoMatcher)
362  {
363  m_logoMatcher = new TemplateMatcher(pgmConverter, cannyEdgeDetector,
365  pass1.push_back(m_logoMatcher);
366  }
367  }
368 
369  if (histogramAnalyzer && m_logoFinder)
370  histogramAnalyzer->setLogoState(m_logoFinder);
371 
372  /* Aggregate them all together. */
373  m_frameAnalyzers.push_back(pass0);
374  m_frameAnalyzers.push_back(pass1);
375 }
376 
377 void CommDetector2::reportState(int elapsedms, long long frameno,
378  long long nframes, unsigned int passno, unsigned int npasses)
379 {
380  float fps = elapsedms ? (float)frameno * 1000 / elapsedms : 0;
381  static int s_prevpercent = -1;
382 
383  /* Assume that 0-th pass is negligible in terms of computational cost. */
384  int percentage = (passno == 0 || npasses == 1 || nframes == 0) ? 0 :
385  (passno - 1) * 100 / (npasses - 1) +
386  min((long long)100, (frameno * 100 / nframes) / (npasses - 1));
387 
388  if (m_showProgress)
389  {
390  QString tmp = "";
391 
392  if (nframes)
393  tmp = QString("\r%1%/ %2fps").arg(percentage,3).arg(fps,6,'f',2);
394  else
395  tmp = QString("\r%1/ %2fps").arg(frameno,6).arg(fps,6,'f',2);
396 
397  QByteArray tmp2 = tmp.toLocal8Bit();
398  cerr << tmp2.constData() << " \r";
399  cerr.flush();
400  }
401 
402  if (nframes)
403  {
404  emit statusUpdate(QCoreApplication::translate("(mythcommflag)",
405  "%1% Completed @ %2 fps.").arg(percentage).arg(fps));
406  }
407  else
408  {
409  emit statusUpdate(QCoreApplication::translate("(mythcommflag)",
410  "%1 Frames Completed @ %2 fps.").arg(frameno).arg(fps));
411  }
412 
413  if (percentage % 10 == 0 && s_prevpercent != percentage)
414  {
415  s_prevpercent = percentage;
416  LOG(VB_GENERAL, LOG_INFO, QString("%1%% Completed @ %2 fps.")
417  .arg(percentage) .arg(fps));
418  }
419 }
420 
421 int CommDetector2::computeBreaks(long long nframes)
422 {
423  int trow = 0;
424  int tcol = 0;
425  int twidth = 0;
426  int theight = 0;
427 
428  m_breaks.clear();
429 
430  TemplateMatcher *matcher = nullptr;
431  if (m_logoFinder && m_logoFinder->getTemplate(&trow, &tcol, &twidth, &theight))
432  matcher = m_logoMatcher;
433 
434  if (matcher && m_blankFrameDetector)
435  {
436  int cmp = matcher->templateCoverage(nframes, m_finished);
437  if (cmp == 0)
438  {
439  if (matcher->adjustForBlanks(m_blankFrameDetector, nframes))
440  return -1;
441  if (matcher->computeBreaks(&m_breaks))
442  return -1;
443  }
444  else
445  {
446  if (cmp > 0 &&
448  return -1;
449  if (cmp < 0 &&
451  return -1;
452 
454  return -1;
455  }
456  }
457  else if (matcher)
458  {
459  if (matcher->computeBreaks(&m_breaks))
460  return -1;
461  }
462  else if (m_blankFrameDetector)
463  {
465  return -1;
466  }
467 
468  return 0;
469 }
470 
472 {
473  int minlag = 7; // seconds
474 
475  if (m_player->OpenFile() < 0)
476  return false;
477 
478  if (!m_player->InitVideo())
479  {
480  LOG(VB_GENERAL, LOG_ERR,
481  "NVP: Unable to initialize video for FlagCommercials.");
482  return false;
483  }
484 
485  m_player->EnableSubtitles(false);
486 
487  QElapsedTimer totalFlagTime;
488  totalFlagTime.start();
489 
490  /* If still recording, estimate the eventual total number of frames. */
491  long long nframes = m_isRecording ?
492  (long long)roundf((m_recstartts.secsTo(m_recendts) + 5) *
493  m_player->GetFrameRate()) :
495  bool postprocessing = !m_isRecording;
496 
497  if (m_showProgress)
498  {
499  if (nframes)
500  cerr << " 0%/ ";
501  else
502  cerr << " 0/ ";
503  cerr.flush();
504  }
505 
506  frm_dir_map_t lastBreakMap;
507  unsigned int passno = 0;
508  unsigned int npasses = m_frameAnalyzers.size();
509  for (m_currentPass = m_frameAnalyzers.begin();
511  ++m_currentPass, passno++)
512  {
513  FrameAnalyzerItem deadAnalyzers;
514 
515  LOG(VB_COMMFLAG, LOG_INFO,
516  QString("CommDetector2::go pass %1 of %2 (%3 frames, %4 fps)")
517  .arg(passno + 1).arg(npasses)
519  .arg(m_player->GetFrameRate(), 0, 'f', 2));
520 
521  if (!MythPlayerInited(
522  *m_currentPass, m_finishedAnalyzers, deadAnalyzers, m_player, nframes))
523  {
524  return false;
525  }
526 
528  long long nextFrame = -1;
530  long long lastLoggedFrame = m_currentFrameNumber;
531  QElapsedTimer passTime;
532  QElapsedTimer clock;
533  struct timeval getframetime {};
534 
536 
537  if (searchingForLogo(m_logoFinder, *m_currentPass))
538  emit statusUpdate(QCoreApplication::translate("(mythcommflag)",
539  "Performing Logo Identification"));
540 
541  clock.start();
542  passTime.start();
543  memset(&getframetime, 0, sizeof(getframetime));
544  while (!(*m_currentPass).empty() && m_player->GetEof() == kEofStateNone)
545  {
546  struct timeval start {};
547  struct timeval end {};
548  struct timeval elapsedtv {};
549 
550  (void)gettimeofday(&start, nullptr);
551  bool fetchNext = (nextFrame == m_currentFrameNumber + 1);
552  VideoFrame *currentFrame =
553  m_player->GetRawVideoFrame(fetchNext ? -1 : nextFrame);
554  long long lastFrameNumber = m_currentFrameNumber;
555  m_currentFrameNumber = currentFrame->frameNumber + 1;
556  (void)gettimeofday(&end, nullptr);
557  timersub(&end, &start, &elapsedtv);
558  timeradd(&getframetime, &elapsedtv, &getframetime);
559 
560  if (nextFrame != -1 && nextFrame == lastFrameNumber + 1 &&
561  m_currentFrameNumber != nextFrame)
562  {
563  /*
564  * Don't log "Jumped" when we know we're skipping frames (e.g.,
565  * logo detection).
566  */
567  LOG(VB_COMMFLAG, LOG_INFO,
568  QString("Jumped from frame %1 to frame %2")
569  .arg(lastFrameNumber).arg(m_currentFrameNumber));
570  }
571 
572  if (stopForBreath(m_isRecording, m_currentFrameNumber))
573  {
574  emit breathe();
575  if (m_bStop)
576  {
577  m_player->DiscardVideoFrame(currentFrame);
578  return false;
579  }
580  }
581 
582  while (m_bPaused)
583  {
584  emit breathe();
585  std::this_thread::sleep_for(std::chrono::seconds(1));
586  }
587 
588  if (!searchingForLogo(m_logoFinder, *m_currentPass) &&
589  needToReportState(m_showProgress, m_isRecording,
591  {
592  reportState(passTime.elapsed(), m_currentFrameNumber,
593  nframes, passno, npasses);
594  }
595 
596  nextFrame = processFrame(
598  deadAnalyzers, currentFrame, m_currentFrameNumber);
599 
600  if (((m_currentFrameNumber >= 1) && (nframes > 0) &&
601  (((nextFrame * 10) / nframes) !=
602  ((m_currentFrameNumber * 10) / nframes))) ||
603  (nextFrame >= nframes))
604  {
605  /* Log something every 10%. */
606  int elapsed = clock.restart();
607  LOG(VB_COMMFLAG, LOG_INFO,
608  QString("processFrame %1 of %2 (%3%) - %4 fps")
610  .arg(nframes)
611  .arg((nframes == 0) ? 0 :
612  (int)roundf(m_currentFrameNumber * 100.0 / nframes))
613  .arg((m_currentFrameNumber - lastLoggedFrame) * 1000 /
614  (elapsed ? elapsed : 1)));
615  lastLoggedFrame = m_currentFrameNumber;
616  }
617 
618  if (m_isRecording)
619  {
620  waitForBuffer(&start, minlag,
621  m_recstartts.secsTo(MythDate::current()) -
622  totalFlagTime.elapsed() / 1000, m_player->GetFrameRate(),
623  m_fullSpeed);
624  }
625 
626  // sleep a little so we don't use all cpu even if we're niced
627  if (!m_fullSpeed && !m_isRecording)
628  std::this_thread::sleep_for(std::chrono::milliseconds(10));
629 
631  !(m_currentFrameNumber % 500)))
632  {
633  frm_dir_map_t breakMap;
634 
635  GetCommercialBreakList(breakMap);
636 
637  auto ii = breakMap.cbegin();
638  auto jj = lastBreakMap.cbegin();
639  while (ii != breakMap.cend() && jj != lastBreakMap.cend())
640  {
641  if (ii.key() != jj.key())
642  break;
643  if (*ii != *jj)
644  break;
645  ++ii;
646  ++jj;
647  }
648  bool same = ii == breakMap.end() && jj == lastBreakMap.end();
649  lastBreakMap = breakMap;
650 
651  if (m_breakMapUpdateRequested || !same)
653 
655  }
656 
657  m_player->DiscardVideoFrame(currentFrame);
658  }
659 
660  // Save total duration only on the last pass, which hopefully does
661  // no skipping.
662  if (passno + 1 == npasses)
664 
665  m_currentPass->insert(m_currentPass->end(),
666  m_finishedAnalyzers.begin(),
667  m_finishedAnalyzers.end());
668  m_finishedAnalyzers.clear();
669 
670  if (postprocessing)
672  if (passFinished(*m_currentPass, m_currentFrameNumber + 1, true))
673  return false;
674 
675  LOG(VB_COMMFLAG, LOG_INFO, QString("NVP Time: GetRawVideoFrame=%1s")
676  .arg(strftimeval(&getframetime)));
677  if (passReportTime(*m_currentPass))
678  return false;
679  }
680 
681  if (m_showProgress)
682  {
683  if (nframes)
684  cerr << "\b\b\b\b\b\b \b\b\b\b\b\b";
685  else
686  cerr << "\b\b\b\b\b\b\b\b\b\b\b\b\b "
687  "\b\b\b\b\b\b\b\b\b\b\b\b\b";
688  cerr.flush();
689  }
690 
691  m_finished = true;
692  return true;
693 }
694 
696 {
697  if (!m_finished)
698  {
699  for (auto & analyzer : m_frameAnalyzers)
700  {
701  if (analyzer == *m_currentPass &&
702  passFinished(m_finishedAnalyzers, m_currentFrameNumber + 1, false))
703  {
704  return;
705  }
706 
707  if (passFinished(analyzer, m_currentFrameNumber + 1, false))
708  return;
709  }
710  }
711 
713  return;
714 
715  marks.clear();
716 
717  /* Create break list. */
718  long long breakframes = 0;
719  for (auto bb = m_breaks.begin(); bb != m_breaks.end(); ++bb)
720  {
721  long long segb = bb.key();
722  long long seglen = *bb;
723  long long sege = segb + seglen - 1;
724 
725  if (segb < sege)
726  {
727  marks[segb] = MARK_COMM_START;
728  marks[sege] = MARK_COMM_END;
729 
730  breakframes += seglen;
731  }
732  }
733 
734  /* Report results. */
735  const float fps = m_player->GetFrameRate();
736  for (frm_dir_map_t::const_iterator iimark = marks.begin();
737  iimark != marks.end();
738  ++iimark)
739  {
740  /* Display as 1-based frame numbers. */
741  long long markstart = iimark.key() + 1; /* MARK_COMM_BEGIN */
742  ++iimark; /* MARK_COMM_END */
743  if (iimark == marks.end())
744  break;
745  long long markend = iimark.key() + 1;
746 
747  LOG(VB_COMMFLAG, LOG_INFO, QString("Break: frame %1-%2 (%3-%4, %5)")
748  .arg(markstart, 6).arg(markend, 6)
749  .arg(frameToTimestamp(markstart, fps))
750  .arg(frameToTimestamp(markend, fps))
751  .arg(frameToTimestamp(markend - markstart + 1, fps)));
752  }
753 
754  const long long nframes = m_player->GetTotalFrameCount();
755  LOG(VB_COMMFLAG, LOG_INFO,
756  QString("Flagged %1 of %2 frames (%3 of %4), %5% commercials (%6)")
757  .arg(m_currentFrameNumber + 1).arg(nframes)
759  .arg(frameToTimestamp(nframes, fps))
760  .arg(breakframes * 100 / m_currentFrameNumber)
761  .arg(frameToTimestamp(breakframes, fps)));
762 }
763 
764 void CommDetector2::recordingFinished(long long totalFileSize)
765 {
767  m_isRecording = false;
768  LOG(VB_COMMFLAG, LOG_INFO,
769  QString("CommDetector2::recordingFinished: %1 bytes")
770  .arg(totalFileSize));
771 }
772 
774 {
775  if (searchingForLogo(m_logoFinder, *m_currentPass))
776  {
777  LOG(VB_COMMFLAG, LOG_INFO, "Ignoring request for commBreakMapUpdate; "
778  "still doing logo detection");
779  return;
780  }
781 
782  LOG(VB_COMMFLAG, LOG_INFO,
783  QString("commBreakMapUpdate requested at frame %1")
784  .arg(m_currentFrameNumber + 1));
785  m_sendBreakMapUpdates = true;
787 }
788 
789 static void PrintReportMap(ostream &out,
790  const FrameAnalyzer::FrameMap &frameMap)
791 {
792  FrameAnalyzer::FrameMap::const_iterator it = frameMap.begin();
793  for (; it != frameMap.end(); ++it)
794  {
795  /*
796  * QMap'd as 0-based index, but display as 1-based index to match "Edit
797  * Recording" OSD.
798  */
799 
800  long long bb = it.key() + 1;
801  long long ee = (*it) ? (bb + *it) : 1;
802  QString tmp = QString("%1: %2").arg(bb, 10).arg(ee - 1, 10);
803 
804  out << qPrintable(tmp) << "\n";
805  }
806  out << flush;
807 }
808 
810  ostream &out, const frm_dir_map_t */*comm_breaks*/, bool /*verbose*/) const
811 {
812  FrameAnalyzer::FrameMap logoMap;
813  FrameAnalyzer::FrameMap blankMap;
814  FrameAnalyzer::FrameMap blankBreakMap;
815  FrameAnalyzer::FrameMap sceneMap;
816  if (m_logoFinder)
817  logoMap = m_logoFinder->GetMap(0);
818 
820  {
821  blankBreakMap = m_blankFrameDetector->GetMap(0);
822  blankMap = m_blankFrameDetector->GetMap(1);
823  }
824 
826  sceneMap = m_sceneChangeDetector->GetMap(0);
827 
828  out << "Logo Break Map" << endl;
829  PrintReportMap(out, logoMap);
830  out << "Blank Break Map" << endl;
831  PrintReportMap(out, blankBreakMap);
832  out << "Blank Map" << endl;
833  PrintReportMap(out, blankMap);
834  out << "Scene Break Map" << endl;
835  PrintReportMap(out, sceneMap);
836 }
837 
838 /* vim: set expandtab tabstop=4 shiftwidth=4: */
MythFormatTimeMs
QString MythFormatTimeMs(int msecs, const QString &fmt)
Format a milliseconds time value.
Definition: mythmiscutil.cpp:1232
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:783
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:126
MythPlayer::EnableSubtitles
void EnableSubtitles(bool enable)
Definition: mythplayer.cpp:1417
TemplateMatcher.h
MARK_COMM_END
@ MARK_COMM_END
Definition: programtypes.h:60
MythPlayer::GetTotalFrameCount
uint64_t GetTotalFrameCount(void) const
Definition: mythplayer.h:236
CommDetectorBase::m_bPaused
bool m_bPaused
Definition: CommDetectorBase.h:54
SceneChangeDetector
Definition: SceneChangeDetector.h:18
commDetector2::strftimeval
QString strftimeval(const struct timeval *tv)
Definition: CommDetector2.cpp:271
mythdb.h
CommDetectorBase::breathe
void breathe()
kEofStateNone
@ kEofStateNone
Definition: decoderbase.h:68
CommDetector2::m_showProgress
bool m_showProgress
Definition: CommDetector2.h:61
CommDetector2::reportState
void reportState(int elapsedms, long long frameno, long long nframes, unsigned int passno, unsigned int npasses)
Definition: CommDetector2.cpp:377
FrameAnalyzer::ANALYZE_OK
@ ANALYZE_OK
Definition: FrameAnalyzer.h:37
CommDetector2.h
MythPlayer::OpenFile
virtual int OpenFile(int Retries=4)
Definition: mythplayer.cpp:782
CommDetector2::go
bool go(void) override
Definition: CommDetector2.cpp:471
CommDetector2::recordingFinished
void recordingFinished(long long totalFileSize) override
Definition: CommDetector2.cpp:764
CommDetectorBase::gotNewCommercialBreakList
void gotNewCommercialBreakList()
timeradd
#define timeradd(a, b, result)
Definition: compat.h:300
FrameAnalyzer::FrameMap
QMap< long long, long long > FrameMap
Definition: FrameAnalyzer.h:45
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:198
CommDetector2::m_player
MythPlayer * m_player
Definition: CommDetector2.h:63
arg
arg(title).arg(filename).arg(doDelete))
CommDetectorBase::statusUpdate
void statusUpdate(const QString &a)
frm_dir_map_t
QMap< uint64_t, MarkTypes > frm_dir_map_t
Frame # -> Mark map.
Definition: programtypes.h:82
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
CommDetector2::m_breaks
FrameAnalyzer::FrameMap m_breaks
Definition: CommDetector2.h:80
MythPlayer::GetFrameRate
float GetFrameRate(void) const
Definition: mythplayer.h:219
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
CommDetector2::PrintFullMap
void PrintFullMap(ostream &out, const frm_dir_map_t *comm_breaks, bool verbose) const override
Definition: CommDetector2.cpp:809
CommDetector2::requestCommBreakMapUpdate
void requestCommBreakMapUpdate(void) override
Definition: CommDetector2.cpp:773
mythplayer.h
CommDetector2::m_recstartts
QDateTime m_recstartts
Definition: CommDetector2.h:66
MythPlayer::GetEof
EofState GetEof(void) const
Definition: mythplayer.cpp:3222
MythPlayer
Definition: mythplayer.h:165
CommDetector2::m_finished
bool m_finished
Definition: CommDetector2.h:73
VideoFrame
Definition: mythframe.h:138
CommDetector2::m_finishedAnalyzers
FrameAnalyzerItem m_finishedAnalyzers
Definition: CommDetector2.h:78
FrameAnalyzer::kAnyFrame
static const long long kAnyFrame
Definition: FrameAnalyzer.h:58
commDetector2::createDebugDirectory
void createDebugDirectory(const QString &dirname, const QString &comment)
Definition: CommDetector2.cpp:231
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
commDetector2::debugDirectory
QString debugDirectory(int chanid, const QDateTime &recstartts)
Definition: CommDetector2.cpp:197
tmp
static guint32 * tmp
Definition: goom_core.cpp:30
CommDetectorBase::recordingFinished
virtual void recordingFinished(long long totalFileSize)
Definition: CommDetectorBase.h:40
COMM_DETECT_2_BLANK
@ COMM_DETECT_2_BLANK
Definition: programtypes.h:106
CommDetector2::computeBreaks
int computeBreaks(long long nframes)
Definition: CommDetector2.cpp:421
MythPlayer::DiscardVideoFrame
void DiscardVideoFrame(VideoFrame *buffer)
Places frame in the available frames queue.
Definition: mythplayer.cpp:996
BlankFrameDetector::computeForLogoSurplus
int computeForLogoSurplus(const TemplateMatcher *templateMatcher)
Definition: BlankFrameDetector.cpp:443
TemplateFinder
Definition: TemplateFinder.h:31
programinfo.h
mythlogging.h
SceneChangeDetector.h
PGMConverter.h
BlankFrameDetector::GetMap
FrameMap GetMap(unsigned int index) const override
Definition: BlankFrameDetector.h:30
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
compat.h
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
BlankFrameDetector.h
MythPlayer::InitVideo
bool InitVideo(void)
Definition: mythplayer.cpp:364
TemplateMatcher::templateCoverage
int templateCoverage(long long nframes, bool final) const
Definition: TemplateMatcher.cpp:590
CannyEdgeDetector.h
timersub
#define timersub(a, b, result)
Definition: compat.h:310
hardwareprofile.smolt.long
long
Definition: smolt.py:76
BlankFrameDetector::computeBreaks
int computeBreaks(FrameMap *breaks)
Definition: BlankFrameDetector.cpp:632
CommDetector2::GetCommercialBreakList
void GetCommercialBreakList(frm_dir_map_t &marks) override
Definition: CommDetector2.cpp:695
FrameAnalyzer::kNextFrame
static const long long kNextFrame
Definition: FrameAnalyzer.h:59
FrameAnalyzer::analyzeFrameResult
analyzeFrameResult
Definition: FrameAnalyzer.h:36
FrameAnalyzer.h
CommDetector2::m_currentFrameNumber
long long m_currentFrameNumber
Definition: CommDetector2.h:75
TemplateFinder::GetMap
FrameMap GetMap(unsigned int) const override
Definition: TemplateFinder.h:49
FrameAnalyzer::ANALYZE_ERROR
@ ANALYZE_ERROR
Definition: FrameAnalyzer.h:38
CommDetector2::m_frameAnalyzers
FrameAnalyzerList m_frameAnalyzers
Definition: CommDetector2.h:76
CommDetector2::m_sceneChangeDetector
SceneChangeDetector * m_sceneChangeDetector
Definition: CommDetector2.h:85
CommDetector2::m_sendBreakMapUpdates
bool m_sendBreakMapUpdates
Definition: CommDetector2.h:71
channelutil.h
CommDetectorBase::m_bStop
bool m_bStop
Definition: CommDetectorBase.h:55
BorderDetector.h
BlankFrameDetector
Definition: BlankFrameDetector.h:16
CommDetector2::m_logoMatcher
TemplateMatcher * m_logoMatcher
Definition: CommDetector2.h:83
ProgramInfo::GetChanID
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:367
CommDetector2::m_currentPass
FrameAnalyzerList::iterator m_currentPass
Definition: CommDetector2.h:77
marks
static struct mark marks[16]
CommDetector2::m_recendts
QDateTime m_recendts
Definition: CommDetector2.h:67
ProgramInfo
Holds information on recordings and videos.
Definition: programinfo.h:68
HistogramAnalyzer.h
mythmiscutil.h
CommDetector2::m_logoFinder
TemplateFinder * m_logoFinder
Definition: CommDetector2.h:82
CommDetector2::m_commDetectMethod
SkipType m_commDetectMethod
Definition: CommDetector2.h:60
MythDate
Definition: mythdate.cpp:8
ProgramInfo::GetPlaybackURL
QString GetPlaybackURL(bool checkMaster=false, bool forceCheckLocal=false)
Returns filename or URL to be used to play back this recording.
Definition: programinfo.cpp:2440
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:864
MythPlayer::GetRawVideoFrame
VideoFrame * GetRawVideoFrame(long long frameNumber=-1)
Returns a specific frame from the video.
Definition: mythplayer.cpp:4603
FrameAnalyzer::ANALYZE_FATAL
@ ANALYZE_FATAL
Definition: FrameAnalyzer.h:40
commDetector2
Definition: CommDetector2.cpp:195
MythPlayer::ResetTotalDuration
void ResetTotalDuration(void)
Definition: mythplayer.cpp:5560
SceneChangeDetector::GetMap
FrameMap GetMap(unsigned int) const override
Definition: SceneChangeDetector.h:32
VideoFrame::frameNumber
long long frameNumber
Definition: mythframe.h:148
FrameAnalyzer::ANALYZE_FINISHED
@ ANALYZE_FINISHED
Definition: FrameAnalyzer.h:39
SkipType
SkipType
This is used as a bitmask.
Definition: programtypes.h:92
CommDetector2::CommDetector2
CommDetector2(SkipType commDetectMethod, bool showProgress, bool fullSpeed, MythPlayer *player, int chanid, QDateTime startts, QDateTime endts, QDateTime recstartts, QDateTime recendts, bool useDB)
Definition: CommDetector2.cpp:281
TemplateMatcher::computeBreaks
int computeBreaks(FrameMap *breaks)
Definition: TemplateMatcher.cpp:814
MythPlayer::SaveTotalDuration
void SaveTotalDuration(void)
Definition: mythplayer.cpp:5552
COMM_DETECT_2_SCENE
@ COMM_DETECT_2_SCENE
Definition: programtypes.h:107
TemplateFinder.h
commDetector2::frameToTimestampms
QString frameToTimestampms(long long frameno, float fps)
Definition: CommDetector2.cpp:263
commDetector2::frameToTimestamp
QString frameToTimestamp(long long frameno, float fps)
Definition: CommDetector2.cpp:255
CommDetector2::m_fullSpeed
bool m_fullSpeed
Definition: CommDetector2.h:62
TemplateMatcher::adjustForBlanks
int adjustForBlanks(const BlankFrameDetector *blankFrameDetector, long long nframes)
Definition: TemplateMatcher.cpp:646
CommDetector2::m_blankFrameDetector
BlankFrameDetector * m_blankFrameDetector
Definition: CommDetector2.h:84
BlankFrameDetector::computeForLogoDeficit
static int computeForLogoDeficit(const TemplateMatcher *templateMatcher)
Definition: BlankFrameDetector.cpp:621
MARK_COMM_START
@ MARK_COMM_START
Definition: programtypes.h:59
TemplateFinder::getTemplate
const struct AVFrame * getTemplate(int *prow, int *pcol, int *pwidth, int *pheight) const
Definition: TemplateFinder.cpp:1049
FrameAnalyzerItem
vector< FrameAnalyzer * > FrameAnalyzerItem
Definition: CommDetector2.h:34
query
MSqlQuery query(MSqlQuery::InitCon())
CommDetector2::m_isRecording
bool m_isRecording
Definition: CommDetector2.h:70
TemplateMatcher
Definition: TemplateMatcher.h:32
PrintReportMap
static void PrintReportMap(ostream &out, const FrameAnalyzer::FrameMap &frameMap)
Definition: CommDetector2.cpp:789
COMM_DETECT_2
@ COMM_DETECT_2
Definition: programtypes.h:104
COMM_DETECT_2_LOGO
@ COMM_DETECT_2_LOGO
Definition: programtypes.h:105
find
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
Definition: dvbstreamhandler.cpp:356
CommDetector2::m_debugdir
QString m_debugdir
Definition: CommDetector2.h:87
CommDetector2::m_breakMapUpdateRequested
bool m_breakMapUpdateRequested
Definition: CommDetector2.h:72
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:808