|
MythTV
0.26-pre
|
00001 // POSIX headers 00002 #include <unistd.h> 00003 00004 // ANSI C headers 00005 #include <cmath> 00006 #include <cerrno> 00007 00008 // C++ headers 00009 #include <algorithm> 00010 using namespace std; 00011 00012 // Qt headers 00013 #include <QDir> 00014 #include <QFileInfo> 00015 00016 // MythTV headers 00017 #include "compat.h" 00018 #include "mythdb.h" 00019 #include "mythlogging.h" 00020 #include "mythplayer.h" 00021 #include "programinfo.h" 00022 #include "channelutil.h" 00023 00024 // Commercial Flagging headers 00025 #include "CommDetector2.h" 00026 #include "CannyEdgeDetector.h" 00027 #include "FrameAnalyzer.h" 00028 #include "PGMConverter.h" 00029 #include "BorderDetector.h" 00030 #include "HistogramAnalyzer.h" 00031 #include "BlankFrameDetector.h" 00032 #include "SceneChangeDetector.h" 00033 #include "TemplateFinder.h" 00034 #include "TemplateMatcher.h" 00035 00036 namespace { 00037 00038 bool stopForBreath(bool isrecording, long long frameno) 00039 { 00040 return (isrecording && (frameno % 100) == 0) || (frameno % 500) == 0; 00041 } 00042 00043 bool needToReportState(bool showprogress, bool isrecording, long long frameno) 00044 { 00045 return ((showprogress || isrecording) && (frameno % 100) == 0) || 00046 (frameno % 500) == 0; 00047 } 00048 00049 void waitForBuffer(const struct timeval *framestart, int minlag, int flaglag, 00050 float fps, bool fullspeed) 00051 { 00052 long usperframe = (long)(1000000.0 / fps); 00053 struct timeval now, elapsed; 00054 long sleepus; 00055 00056 (void)gettimeofday(&now, NULL); 00057 timersub(&now, framestart, &elapsed); 00058 00059 // Sleep for one frame's worth of time. 00060 sleepus = usperframe - elapsed.tv_sec * 1000000 - elapsed.tv_usec; 00061 if (sleepus <= 0) 00062 return; 00063 00064 if (flaglag > minlag) 00065 { 00066 // Try to catch up; reduce sleep time. 00067 if (fullspeed) 00068 return; 00069 sleepus /= 4; 00070 } 00071 else if (flaglag < minlag) 00072 { 00073 // Slow down; increase sleep time 00074 sleepus = sleepus * 3 / 2; 00075 } 00076 usleep(sleepus); 00077 } 00078 00079 bool MythPlayerInited(FrameAnalyzerItem &pass, 00080 FrameAnalyzerItem &finishedAnalyzers, 00081 FrameAnalyzerItem &deadAnalyzers, 00082 MythPlayer *player, 00083 long long nframes) 00084 { 00085 FrameAnalyzerItem::iterator it = pass.begin(); 00086 while (it != pass.end()) 00087 { 00088 FrameAnalyzer::analyzeFrameResult ares = 00089 (*it)->MythPlayerInited(player, nframes); 00090 00091 if ((FrameAnalyzer::ANALYZE_OK == ares) || 00092 (FrameAnalyzer::ANALYZE_ERROR == ares)) 00093 { 00094 ++it; 00095 } 00096 else if (FrameAnalyzer::ANALYZE_FINISHED == ares) 00097 { 00098 finishedAnalyzers.push_back(*it); 00099 it = pass.erase(it); 00100 } 00101 else if (FrameAnalyzer::ANALYZE_FATAL == ares) 00102 { 00103 deadAnalyzers.push_back(*it); 00104 it = pass.erase(it); 00105 } 00106 else 00107 { 00108 LOG(VB_GENERAL, LOG_ERR, 00109 QString("Unexpected return value from %1::MythPlayerInited: %2") 00110 .arg((*it)->name()).arg(ares)); 00111 return false; 00112 } 00113 } 00114 00115 return true; 00116 } 00117 00118 long long processFrame(FrameAnalyzerItem &pass, 00119 FrameAnalyzerItem &finishedAnalyzers, 00120 FrameAnalyzerItem &deadAnalyzers, 00121 const VideoFrame *frame, 00122 long long frameno) 00123 { 00124 long long nextFrame; 00125 long long minNextFrame = FrameAnalyzer::ANYFRAME; 00126 00127 FrameAnalyzerItem::iterator it = pass.begin(); 00128 while (it != pass.end()) 00129 { 00130 FrameAnalyzer::analyzeFrameResult ares = 00131 (*it)->analyzeFrame(frame, frameno, &nextFrame); 00132 00133 if ((FrameAnalyzer::ANALYZE_OK == ares) || 00134 (FrameAnalyzer::ANALYZE_ERROR == ares)) 00135 { 00136 minNextFrame = std::min(minNextFrame, nextFrame); 00137 ++it; 00138 } 00139 else if (ares == FrameAnalyzer::ANALYZE_FINISHED) 00140 { 00141 finishedAnalyzers.push_back(*it); 00142 it = pass.erase(it); 00143 } 00144 else 00145 { 00146 if (ares != FrameAnalyzer::ANALYZE_FATAL) 00147 { 00148 LOG(VB_GENERAL, LOG_ERR, 00149 QString("Unexpected return value from %1::analyzeFrame: %2") 00150 .arg((*it)->name()).arg(ares)); 00151 } 00152 00153 deadAnalyzers.push_back(*it); 00154 it = pass.erase(it); 00155 } 00156 } 00157 00158 if (minNextFrame == FrameAnalyzer::ANYFRAME) 00159 minNextFrame = FrameAnalyzer::NEXTFRAME; 00160 00161 if (minNextFrame == FrameAnalyzer::NEXTFRAME) 00162 minNextFrame = frameno + 1; 00163 00164 return minNextFrame; 00165 } 00166 00167 int passFinished(FrameAnalyzerItem &pass, long long nframes, bool final) 00168 { 00169 FrameAnalyzerItem::iterator it = pass.begin(); 00170 for (; it != pass.end(); ++it) 00171 (void)(*it)->finished(nframes, final); 00172 00173 return 0; 00174 } 00175 00176 int passReportTime(const FrameAnalyzerItem &pass) 00177 { 00178 FrameAnalyzerItem::const_iterator it = pass.begin(); 00179 for (; it != pass.end(); ++it) 00180 (void)(*it)->reportTime(); 00181 00182 return 0; 00183 } 00184 00185 bool searchingForLogo(TemplateFinder *tf, const FrameAnalyzerItem &pass) 00186 { 00187 if (!tf) 00188 return false; 00189 00190 FrameAnalyzerItem::const_iterator it = 00191 std::find(pass.begin(), pass.end(), tf); 00192 00193 return it != pass.end(); 00194 } 00195 00196 }; // namespace 00197 00198 namespace commDetector2 { 00199 00200 QString debugDirectory(int chanid, const QDateTime& recstartts) 00201 { 00202 /* 00203 * Should deleting a recording also delete its associated debug directory? 00204 */ 00205 MSqlQuery query(MSqlQuery::InitCon()); 00206 query.prepare("SELECT basename" 00207 " FROM recorded" 00208 " WHERE chanid = :CHANID" 00209 " AND starttime = :STARTTIME" 00210 ";"); 00211 query.bindValue(":CHANID", chanid); 00212 query.bindValue(":STARTTIME", recstartts); 00213 if (!query.exec() || !query.next()) 00214 { 00215 MythDB::DBError("Error in CommDetector2::CommDetector2", query); 00216 return ""; 00217 } 00218 00219 const ProgramInfo pginfo(chanid, recstartts); 00220 00221 if (!pginfo.GetChanID()) 00222 return ""; 00223 00224 QString pburl = pginfo.GetPlaybackURL(true); 00225 if (pburl.left(1) != "/") 00226 return ""; 00227 00228 QString basename(query.value(0).toString()); 00229 QString debugdir = pburl.section('/',0,-2) + "/" + basename + "-debug"; 00230 00231 return debugdir; 00232 } 00233 00234 void createDebugDirectory(QString dirname, QString comment) 00235 { 00236 QDir qdir(dirname); 00237 if (qdir.exists()) 00238 { 00239 LOG(VB_COMMFLAG, LOG_INFO, QString("%1 using debug directory \"%2\"") 00240 .arg(comment).arg(dirname)); 00241 } 00242 else 00243 { 00244 if (qdir.mkdir(dirname)) 00245 { 00246 LOG(VB_COMMFLAG, LOG_INFO, 00247 QString("%1 created debug directory \"%1\"") 00248 .arg(comment).arg(dirname)); 00249 } 00250 else 00251 { 00252 LOG(VB_COMMFLAG, LOG_INFO, QString("%1 failed to create \"%2\": %3") 00253 .arg(comment).arg(dirname).arg(strerror(errno))); 00254 } 00255 } 00256 } 00257 00258 QString frameToTimestamp(long long frameno, float fps) 00259 { 00260 int ms, ss, mm, hh; 00261 QString ts; 00262 00263 ms = (int)roundf(frameno / fps * 1000); 00264 00265 ss = ms / 1000; 00266 ms %= 1000; 00267 if (ms >= 500) 00268 ss++; 00269 00270 mm = ss / 60; 00271 ss %= 60; 00272 00273 hh = mm / 60; 00274 mm %= 60; 00275 00276 return ts.sprintf("%d:%02d:%02d", hh, mm, ss); 00277 } 00278 00279 QString frameToTimestampms(long long frameno, float fps) 00280 { 00281 int ms, ss, mm; 00282 QString ts; 00283 00284 ms = (int)roundf(frameno / fps * 1000); 00285 00286 ss = ms / 1000; 00287 ms %= 1000; 00288 00289 mm = ss / 60; 00290 ss %= 60; 00291 00292 return ts.sprintf("%d:%02d.%03d", mm, ss, ms); 00293 } 00294 00295 QString strftimeval(const struct timeval *tv) 00296 { 00297 QString str; 00298 return str.sprintf("%ld.%06ld", tv->tv_sec, tv->tv_usec); 00299 } 00300 00301 }; /* namespace */ 00302 00303 using namespace commDetector2; 00304 00305 CommDetector2::CommDetector2( 00306 enum SkipTypes commDetectMethod_in, 00307 bool showProgress_in, 00308 bool fullSpeed_in, 00309 MythPlayer *player_in, 00310 int chanid, 00311 const QDateTime &startts_in, 00312 const QDateTime &endts_in, 00313 const QDateTime &recstartts_in, 00314 const QDateTime &recendts_in, 00315 bool useDB) : 00316 commDetectMethod((enum SkipTypes)(commDetectMethod_in & ~COMM_DETECT_2)), 00317 showProgress(showProgress_in), fullSpeed(fullSpeed_in), 00318 player(player_in), 00319 startts(startts_in), endts(endts_in), 00320 recstartts(recstartts_in), recendts(recendts_in), 00321 isRecording(QDateTime::currentDateTime() < recendts), 00322 sendBreakMapUpdates(false), breakMapUpdateRequested(false), 00323 finished(false), currentFrameNumber(0), 00324 logoFinder(NULL), logoMatcher(NULL), 00325 blankFrameDetector(NULL), sceneChangeDetector(NULL), 00326 debugdir("") 00327 { 00328 FrameAnalyzerItem pass0, pass1; 00329 PGMConverter *pgmConverter = NULL; 00330 BorderDetector *borderDetector = NULL; 00331 HistogramAnalyzer *histogramAnalyzer = NULL; 00332 00333 if (useDB) 00334 debugdir = debugDirectory(chanid, recstartts); 00335 00336 /* 00337 * Look for blank frames to use as delimiters between commercial and 00338 * non-commercial segments. 00339 */ 00340 if ((commDetectMethod & COMM_DETECT_2_BLANK)) 00341 { 00342 if (!pgmConverter) 00343 pgmConverter = new PGMConverter(); 00344 00345 if (!borderDetector) 00346 borderDetector = new BorderDetector(); 00347 00348 if (!histogramAnalyzer) 00349 { 00350 histogramAnalyzer = new HistogramAnalyzer(pgmConverter, 00351 borderDetector, debugdir); 00352 } 00353 00354 if (!blankFrameDetector) 00355 { 00356 blankFrameDetector = new BlankFrameDetector(histogramAnalyzer, 00357 debugdir); 00358 pass1.push_back(blankFrameDetector); 00359 } 00360 } 00361 00362 /* 00363 * Look for "scene changes" to use as delimiters between commercial and 00364 * non-commercial segments. 00365 */ 00366 if ((commDetectMethod & COMM_DETECT_2_SCENE)) 00367 { 00368 if (!pgmConverter) 00369 pgmConverter = new PGMConverter(); 00370 00371 if (!borderDetector) 00372 borderDetector = new BorderDetector(); 00373 00374 if (!histogramAnalyzer) 00375 { 00376 histogramAnalyzer = new HistogramAnalyzer(pgmConverter, 00377 borderDetector, debugdir); 00378 } 00379 00380 if (!sceneChangeDetector) 00381 { 00382 sceneChangeDetector = new SceneChangeDetector(histogramAnalyzer, 00383 debugdir); 00384 pass1.push_back(sceneChangeDetector); 00385 } 00386 } 00387 00388 /* 00389 * Logo Detection requires two passes. The first pass looks for static 00390 * areas of the screen to look for logos and generate a template. The 00391 * second pass matches the template against the frame and computes the 00392 * closeness of the match. 00393 */ 00394 if ((commDetectMethod & COMM_DETECT_2_LOGO)) 00395 { 00396 CannyEdgeDetector *cannyEdgeDetector = NULL; 00397 00398 if (!pgmConverter) 00399 pgmConverter = new PGMConverter(); 00400 00401 if (!borderDetector) 00402 borderDetector = new BorderDetector(); 00403 00404 if (!cannyEdgeDetector) 00405 cannyEdgeDetector = new CannyEdgeDetector(); 00406 00407 if (!logoFinder) 00408 { 00409 logoFinder = new TemplateFinder(pgmConverter, borderDetector, 00410 cannyEdgeDetector, player, recstartts.secsTo(recendts), 00411 debugdir); 00412 pass0.push_back(logoFinder); 00413 } 00414 00415 if (!logoMatcher) 00416 { 00417 logoMatcher = new TemplateMatcher(pgmConverter, cannyEdgeDetector, 00418 logoFinder, debugdir); 00419 pass1.push_back(logoMatcher); 00420 } 00421 } 00422 00423 if (histogramAnalyzer && logoFinder) 00424 histogramAnalyzer->setLogoState(logoFinder); 00425 00426 /* Aggregate them all together. */ 00427 frameAnalyzers.push_back(pass0); 00428 frameAnalyzers.push_back(pass1); 00429 } 00430 00431 void CommDetector2::reportState(int elapsedms, long long frameno, 00432 long long nframes, unsigned int passno, unsigned int npasses) 00433 { 00434 float fps = elapsedms ? (float)frameno * 1000 / elapsedms : 0; 00435 int prevpercent = -1; 00436 00437 /* Assume that 0-th pass is negligible in terms of computational cost. */ 00438 int percentage = passno == 0 ? 0 : 00439 (passno - 1) * 100 / (npasses - 1) + 00440 min((long long)100, (frameno * 100 / nframes) / (npasses - 1)); 00441 00442 if (showProgress) 00443 { 00444 QString tmp = ""; 00445 00446 if (nframes) 00447 tmp = QString("\r%1%/ %2fps").arg(percentage,3).arg(fps,6,'f',2); 00448 else 00449 tmp = QString("\r%1/ %2fps").arg(frameno,6).arg(fps,6,'f',2); 00450 00451 QByteArray tmp2 = tmp.toLocal8Bit(); 00452 cerr << tmp2.constData() << " \r"; 00453 cerr.flush(); 00454 } 00455 00456 if (nframes) 00457 { 00458 emit statusUpdate(QObject::tr("%1% Completed @ %2 fps.") 00459 .arg(percentage).arg(fps)); 00460 } 00461 else 00462 { 00463 emit statusUpdate(QObject::tr("%1 Frames Completed @ %2 fps.") 00464 .arg(frameno).arg(fps)); 00465 } 00466 00467 if (percentage % 10 == 0 && prevpercent != percentage) 00468 { 00469 prevpercent = percentage; 00470 LOG(VB_GENERAL, LOG_INFO, QString("%1%% Completed @ %2 fps.") 00471 .arg(percentage) .arg(fps)); 00472 } 00473 } 00474 00475 int CommDetector2::computeBreaks(long long nframes) 00476 { 00477 int trow, tcol, twidth, theight; 00478 TemplateMatcher *matcher; 00479 00480 breaks.clear(); 00481 00482 matcher = logoFinder && 00483 logoFinder->getTemplate(&trow, &tcol, &twidth, &theight) ? logoMatcher : 00484 NULL; 00485 00486 if (matcher && blankFrameDetector) 00487 { 00488 int cmp; 00489 if (!(cmp = matcher->templateCoverage(nframes, finished))) 00490 { 00491 if (matcher->adjustForBlanks(blankFrameDetector, nframes)) 00492 return -1; 00493 if (matcher->computeBreaks(&breaks)) 00494 return -1; 00495 } 00496 else 00497 { 00498 if (cmp > 0 && 00499 blankFrameDetector->computeForLogoSurplus(matcher)) 00500 return -1; 00501 else if (cmp < 0 && 00502 blankFrameDetector->computeForLogoDeficit(matcher)) 00503 return -1; 00504 00505 if (blankFrameDetector->computeBreaks(&breaks)) 00506 return -1; 00507 } 00508 } 00509 else if (matcher) 00510 { 00511 if (matcher->computeBreaks(&breaks)) 00512 return -1; 00513 } 00514 else if (blankFrameDetector) 00515 { 00516 if (blankFrameDetector->computeBreaks(&breaks)) 00517 return -1; 00518 } 00519 00520 return 0; 00521 } 00522 00523 bool CommDetector2::go(void) 00524 { 00525 int minlag = 7; // seconds 00526 00527 if (player->OpenFile() < 0) 00528 return false; 00529 00530 if (!player->InitVideo()) 00531 { 00532 LOG(VB_GENERAL, LOG_ERR, 00533 "NVP: Unable to initialize video for FlagCommercials."); 00534 return false; 00535 } 00536 00537 player->EnableSubtitles(false); 00538 00539 QTime totalFlagTime; 00540 totalFlagTime.start(); 00541 00542 /* If still recording, estimate the eventual total number of frames. */ 00543 long long nframes = isRecording ? 00544 (long long)roundf((recstartts.secsTo(recendts) + 5) * 00545 player->GetFrameRate()) : 00546 player->GetTotalFrameCount(); 00547 bool postprocessing = !isRecording; 00548 00549 if (showProgress) 00550 { 00551 if (nframes) 00552 cerr << " 0%/ "; 00553 else 00554 cerr << " 0/ "; 00555 cerr.flush(); 00556 } 00557 00558 frm_dir_map_t lastBreakMap; 00559 unsigned int passno = 0; 00560 unsigned int npasses = frameAnalyzers.size(); 00561 for (currentPass = frameAnalyzers.begin(); 00562 currentPass != frameAnalyzers.end(); 00563 ++currentPass, passno++) 00564 { 00565 FrameAnalyzerItem deadAnalyzers; 00566 00567 LOG(VB_COMMFLAG, LOG_INFO, 00568 QString("CommDetector2::go pass %1 of %2 (%3 frames, %4 fps)") 00569 .arg(passno + 1).arg(npasses) 00570 .arg(player->GetTotalFrameCount()) 00571 .arg(player->GetFrameRate(), 0, 'f', 2)); 00572 00573 if (!MythPlayerInited( 00574 *currentPass, finishedAnalyzers, deadAnalyzers, player, nframes)) 00575 { 00576 return false; 00577 } 00578 00579 player->DiscardVideoFrame(player->GetRawVideoFrame(0)); 00580 long long nextFrame = -1; 00581 currentFrameNumber = 0; 00582 long long lastLoggedFrame = currentFrameNumber; 00583 QTime passTime, clock; 00584 struct timeval getframetime; 00585 00586 player->ResetTotalDuration(); 00587 00588 if (searchingForLogo(logoFinder, *currentPass)) 00589 emit statusUpdate(QObject::tr("Performing Logo Identification")); 00590 00591 clock.start(); 00592 passTime.start(); 00593 memset(&getframetime, 0, sizeof(getframetime)); 00594 while (!(*currentPass).empty() && !player->GetEof()) 00595 { 00596 struct timeval start, end, elapsedtv; 00597 00598 (void)gettimeofday(&start, NULL); 00599 VideoFrame *currentFrame = player->GetRawVideoFrame(nextFrame); 00600 long long lastFrameNumber = currentFrameNumber; 00601 currentFrameNumber = currentFrame->frameNumber; 00602 (void)gettimeofday(&end, NULL); 00603 timersub(&end, &start, &elapsedtv); 00604 timeradd(&getframetime, &elapsedtv, &getframetime); 00605 00606 if (nextFrame != -1 && nextFrame == lastFrameNumber + 1 && 00607 currentFrameNumber != nextFrame) 00608 { 00609 /* 00610 * Don't log "Jumped" when we know we're skipping frames (e.g., 00611 * logo detection). 00612 */ 00613 LOG(VB_COMMFLAG, LOG_INFO, 00614 QString("Jumped from frame %1 to frame %2") 00615 .arg(lastFrameNumber).arg(currentFrameNumber)); 00616 } 00617 00618 if (stopForBreath(isRecording, currentFrameNumber)) 00619 { 00620 emit breathe(); 00621 if (m_bStop) 00622 { 00623 player->DiscardVideoFrame(currentFrame); 00624 return false; 00625 } 00626 } 00627 00628 while (m_bPaused) 00629 { 00630 emit breathe(); 00631 sleep(1); 00632 } 00633 00634 if (!searchingForLogo(logoFinder, *currentPass) && 00635 needToReportState(showProgress, isRecording, 00636 currentFrameNumber)) 00637 { 00638 reportState(passTime.elapsed(), currentFrameNumber, 00639 nframes, passno, npasses); 00640 } 00641 00642 nextFrame = processFrame( 00643 *currentPass, finishedAnalyzers, 00644 deadAnalyzers, currentFrame, currentFrameNumber); 00645 00646 if (((currentFrameNumber >= 1) && 00647 (((nextFrame * 10) / nframes) != 00648 ((currentFrameNumber * 10) / nframes))) || 00649 (nextFrame >= nframes)) 00650 { 00651 /* Log something every 10%. */ 00652 int elapsed = clock.restart(); 00653 LOG(VB_COMMFLAG, LOG_INFO, 00654 QString("processFrame %1 of %2 (%3%) - %4 fps") 00655 .arg(currentFrameNumber) 00656 .arg(nframes) 00657 .arg((int)roundf(currentFrameNumber * 100.0 / nframes)) 00658 .arg((currentFrameNumber - lastLoggedFrame) * 1000 / 00659 elapsed)); 00660 lastLoggedFrame = currentFrameNumber; 00661 } 00662 00663 if (isRecording) 00664 { 00665 waitForBuffer(&start, minlag, 00666 recstartts.secsTo(QDateTime::currentDateTime()) - 00667 totalFlagTime.elapsed() / 1000, player->GetFrameRate(), 00668 fullSpeed); 00669 } 00670 00671 // sleep a little so we don't use all cpu even if we're niced 00672 if (!fullSpeed && !isRecording) 00673 usleep(10000); // 10ms 00674 00675 if (sendBreakMapUpdates && (breakMapUpdateRequested || 00676 !(currentFrameNumber % 500))) 00677 { 00678 frm_dir_map_t breakMap; 00679 00680 GetCommercialBreakList(breakMap); 00681 00682 frm_dir_map_t::const_iterator ii, jj; 00683 ii = breakMap.begin(); 00684 jj = lastBreakMap.begin(); 00685 while (ii != breakMap.end() && jj != lastBreakMap.end()) 00686 { 00687 if (ii.key() != jj.key()) 00688 break; 00689 if (*ii != *jj) 00690 break; 00691 ++ii; 00692 ++jj; 00693 } 00694 bool same = ii == breakMap.end() && jj == lastBreakMap.end(); 00695 lastBreakMap = breakMap; 00696 00697 if (breakMapUpdateRequested || !same) 00698 emit gotNewCommercialBreakList(); 00699 00700 breakMapUpdateRequested = false; 00701 } 00702 00703 player->DiscardVideoFrame(currentFrame); 00704 } 00705 00706 // Save total duration only on the last pass, which hopefully does 00707 // no skipping. 00708 if (passno + 1 == npasses) 00709 player->SaveTotalDuration(); 00710 00711 currentPass->insert(currentPass->end(), 00712 finishedAnalyzers.begin(), 00713 finishedAnalyzers.end()); 00714 finishedAnalyzers.clear(); 00715 00716 if (postprocessing) 00717 currentFrameNumber = player->GetTotalFrameCount() - 1; 00718 if (passFinished(*currentPass, currentFrameNumber + 1, true)) 00719 return false; 00720 00721 LOG(VB_COMMFLAG, LOG_INFO, QString("NVP Time: GetRawVideoFrame=%1s") 00722 .arg(strftimeval(&getframetime))); 00723 if (passReportTime(*currentPass)) 00724 return false; 00725 } 00726 00727 if (showProgress) 00728 { 00729 if (nframes) 00730 cerr << "\b\b\b\b\b\b \b\b\b\b\b\b"; 00731 else 00732 cerr << "\b\b\b\b\b\b\b\b\b\b\b\b\b " 00733 "\b\b\b\b\b\b\b\b\b\b\b\b\b"; 00734 cerr.flush(); 00735 } 00736 00737 finished = true; 00738 return true; 00739 } 00740 00741 void CommDetector2::GetCommercialBreakList(frm_dir_map_t &marks) 00742 { 00743 if (!finished) 00744 { 00745 for (FrameAnalyzerList::iterator pass = frameAnalyzers.begin(); 00746 pass != frameAnalyzers.end(); ++pass) 00747 { 00748 if (*pass == *currentPass && 00749 passFinished(finishedAnalyzers, currentFrameNumber + 1, false)) 00750 { 00751 return; 00752 } 00753 00754 if (passFinished(*pass, currentFrameNumber + 1, false)) 00755 return; 00756 } 00757 } 00758 00759 if (computeBreaks(currentFrameNumber + 1)) 00760 return; 00761 00762 marks.clear(); 00763 00764 /* Create break list. */ 00765 long long breakframes = 0; 00766 for (FrameAnalyzer::FrameMap::Iterator bb = breaks.begin(); 00767 bb != breaks.end(); 00768 ++bb) 00769 { 00770 long long segb = bb.key(); 00771 long long seglen = *bb; 00772 long long sege = segb + seglen - 1; 00773 00774 if (segb < sege) 00775 { 00776 marks[segb] = MARK_COMM_START; 00777 marks[sege] = MARK_COMM_END; 00778 00779 breakframes += seglen; 00780 } 00781 } 00782 00783 /* Report results. */ 00784 const float fps = player->GetFrameRate(); 00785 for (frm_dir_map_t::const_iterator iimark = marks.begin(); 00786 iimark != marks.end(); 00787 ++iimark) 00788 { 00789 long long markstart, markend; 00790 00791 /* Display as 1-based frame numbers. */ 00792 markstart = iimark.key() + 1; /* MARK_COMM_BEGIN */ 00793 ++iimark; /* MARK_COMM_END */ 00794 if (iimark == marks.end()) 00795 break; 00796 markend = iimark.key() + 1; 00797 00798 LOG(VB_COMMFLAG, LOG_INFO, QString("Break: frame %1-%2 (%3-%4, %5)") 00799 .arg(markstart, 6).arg(markend, 6) 00800 .arg(frameToTimestamp(markstart, fps)) 00801 .arg(frameToTimestamp(markend, fps)) 00802 .arg(frameToTimestamp(markend - markstart + 1, fps))); 00803 } 00804 00805 const long long nframes = player->GetTotalFrameCount(); 00806 LOG(VB_COMMFLAG, LOG_INFO, 00807 QString("Flagged %1 of %2 frames (%3 of %4), %5% commercials (%6)") 00808 .arg(currentFrameNumber + 1).arg(nframes) 00809 .arg(frameToTimestamp(currentFrameNumber + 1, fps)) 00810 .arg(frameToTimestamp(nframes, fps)) 00811 .arg(breakframes * 100 / currentFrameNumber) 00812 .arg(frameToTimestamp(breakframes, fps))); 00813 } 00814 00815 void CommDetector2::recordingFinished(long long totalFileSize) 00816 { 00817 CommDetectorBase::recordingFinished(totalFileSize); 00818 isRecording = false; 00819 LOG(VB_COMMFLAG, LOG_INFO, 00820 QString("CommDetector2::recordingFinished: %1 bytes") 00821 .arg(totalFileSize)); 00822 } 00823 00824 void CommDetector2::requestCommBreakMapUpdate(void) 00825 { 00826 if (searchingForLogo(logoFinder, *currentPass)) 00827 { 00828 LOG(VB_COMMFLAG, LOG_INFO, "Ignoring request for commBreakMapUpdate; " 00829 "still doing logo detection"); 00830 return; 00831 } 00832 00833 LOG(VB_COMMFLAG, LOG_INFO, 00834 QString("commBreakMapUpdate requested at frame %1") 00835 .arg(currentFrameNumber + 1)); 00836 sendBreakMapUpdates = true; 00837 breakMapUpdateRequested = true; 00838 } 00839 00840 static void PrintReportMap(ostream &out, 00841 const FrameAnalyzer::FrameMap &frameMap) 00842 { 00843 FrameAnalyzer::FrameMap::const_iterator it = frameMap.begin(); 00844 for (; it != frameMap.end(); ++it) 00845 { 00846 /* 00847 * QMap'd as 0-based index, but display as 1-based index to match "Edit 00848 * Recording" OSD. 00849 */ 00850 00851 long long bb = it.key() + 1; 00852 long long ee = (*it) ? (bb + *it) : 1; 00853 QString tmp = QString("%1: %2").arg(bb, 10).arg(ee - 1, 10); 00854 00855 out << qPrintable(tmp) << "\n"; 00856 } 00857 out << flush; 00858 } 00859 00860 void CommDetector2::PrintFullMap( 00861 ostream &out, const frm_dir_map_t *comm_breaks, bool verbose) const 00862 { 00863 FrameAnalyzer::FrameMap logoMap, blankMap, blankBreakMap, sceneMap; 00864 if (logoFinder) 00865 logoMap = logoFinder->GetMap(0); 00866 00867 if (blankFrameDetector) 00868 { 00869 blankBreakMap = blankFrameDetector->GetMap(0); 00870 blankMap = blankFrameDetector->GetMap(1); 00871 } 00872 00873 if (sceneChangeDetector) 00874 sceneMap = sceneChangeDetector->GetMap(0); 00875 00876 out << "Logo Break Map" << endl; 00877 PrintReportMap(out, logoMap); 00878 out << "Blank Break Map" << endl; 00879 PrintReportMap(out, blankBreakMap); 00880 out << "Blank Map" << endl; 00881 PrintReportMap(out, blankMap); 00882 out << "Scene Break Map" << endl; 00883 PrintReportMap(out, sceneMap); 00884 } 00885 00886 /* vim: set expandtab tabstop=4 shiftwidth=4: */
1.7.6.1