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