MythTV  0.26-pre
CommDetector2.cpp
Go to the documentation of this file.
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: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends