00001
00002 #include <unistd.h>
00003 #include <sys/time.h>
00004
00005
00006 #include <cmath>
00007
00008
00009 #include <algorithm>
00010 #include <iostream>
00011 using namespace std;
00012
00013
00014 #include <QString>
00015
00016
00017 #include "mythcontext.h"
00018 #include "programinfo.h"
00019 #include "mythplayer.h"
00020
00021
00022 #include "ClassicCommDetector.h"
00023 #include "ClassicLogoDetector.h"
00024 #include "ClassicSceneChangeDetector.h"
00025
00026 enum frameAspects {
00027 COMM_ASPECT_NORMAL = 0,
00028 COMM_ASPECT_WIDE
00029 } FrameAspects;
00030
00031 enum frameFormats {
00032 COMM_FORMAT_NORMAL = 0,
00033 COMM_FORMAT_LETTERBOX,
00034 COMM_FORMAT_PILLARBOX,
00035 COMM_FORMAT_MAX
00036 } FrameFormats;
00037
00038 static QString toStringFrameMaskValues(int mask, bool verbose)
00039 {
00040 QString msg;
00041
00042 if (verbose)
00043 {
00044 if (COMM_FRAME_SKIPPED & mask)
00045 msg += "skipped,";
00046 if (COMM_FRAME_BLANK & mask)
00047 msg += "blank,";
00048 if (COMM_FRAME_SCENE_CHANGE & mask)
00049 msg += "scene,";
00050 if (COMM_FRAME_LOGO_PRESENT & mask)
00051 msg += "logo,";
00052 if (COMM_FRAME_ASPECT_CHANGE & mask)
00053 msg += "aspect,";
00054 if (COMM_FRAME_RATING_SYMBOL & mask)
00055 msg += "rating,";
00056
00057 if (msg.length())
00058 msg = msg.left(msg.length() - 1);
00059 else
00060 msg = "noflags";
00061 }
00062 else
00063 {
00064 msg += (COMM_FRAME_SKIPPED & mask) ? "s" : " ";
00065 msg += (COMM_FRAME_BLANK & mask) ? "B" : " ";
00066 msg += (COMM_FRAME_SCENE_CHANGE & mask) ? "S" : " ";
00067 msg += (COMM_FRAME_LOGO_PRESENT & mask) ? "L" : " ";
00068 msg += (COMM_FRAME_ASPECT_CHANGE & mask) ? "A" : " ";
00069 msg += (COMM_FRAME_RATING_SYMBOL & mask) ? "R" : " ";
00070 }
00071
00072 return msg;
00073 }
00074
00075 static QString toStringFrameAspects(int aspect, bool verbose)
00076 {
00077 if (verbose)
00078 return (COMM_ASPECT_NORMAL == aspect) ? "normal" : " wide ";
00079 else
00080 return (COMM_ASPECT_NORMAL == aspect) ? "n" : "w";
00081 }
00082
00083 static QString toStringFrameFormats(int format, bool verbose)
00084 {
00085 switch (format)
00086 {
00087 case COMM_FORMAT_NORMAL:
00088 return (verbose) ? "normal" : "N";
00089 case COMM_FORMAT_LETTERBOX:
00090 return (verbose) ? "letter" : "L";
00091 case COMM_FORMAT_PILLARBOX:
00092 return (verbose) ? "pillar" : "P";
00093 case COMM_FORMAT_MAX:
00094 return (verbose) ? " max " : "M";
00095 }
00096
00097 return (verbose) ? " null " : "n";
00098 }
00099
00100 QString FrameInfoEntry::GetHeader(void)
00101 {
00102 return QString(" frame min/max/avg scene aspect format flags");
00103 }
00104
00105 QString FrameInfoEntry::toString(uint64_t frame, bool verbose) const
00106 {
00107 return QString(
00108 "%1: %2/%3/%4 %5% %6 %7 %8")
00109 .arg(frame,10)
00110 .arg(minBrightness,3)
00111 .arg(maxBrightness,3)
00112 .arg(avgBrightness,3)
00113 .arg(sceneChangePercent,3)
00114 .arg(toStringFrameAspects(aspect, verbose))
00115 .arg(toStringFrameFormats(format, verbose))
00116 .arg(toStringFrameMaskValues(flagMask, verbose));
00117 }
00118
00119 ClassicCommDetector::ClassicCommDetector(SkipType commDetectMethod_in,
00120 bool showProgress_in,
00121 bool fullSpeed_in,
00122 MythPlayer* player_in,
00123 const QDateTime& startedAt_in,
00124 const QDateTime& stopsAt_in,
00125 const QDateTime& recordingStartedAt_in,
00126 const QDateTime& recordingStopsAt_in) :
00127
00128
00129 commDetectMethod(commDetectMethod_in),
00130 commBreakMapUpdateRequested(false), sendCommBreakMapUpdates(false),
00131 verboseDebugging(false),
00132 lastFrameNumber(0), curFrameNumber(0),
00133 width(0), height(0),
00134 horizSpacing(0), vertSpacing(0),
00135 fpm(0.0), blankFramesOnly(false),
00136 blankFrameCount(0), currentAspect(0),
00137 totalMinBrightness(0), detectBlankFrames(false),
00138 detectSceneChanges(false), detectStationLogo(false),
00139 logoInfoAvailable(false), logoDetector(0),
00140 framePtr(0), frameIsBlank(false),
00141 sceneHasChanged(false), stationLogoPresent(false),
00142 lastFrameWasBlank(false), lastFrameWasSceneChange(false),
00143 decoderFoundAspectChanges(false), sceneChangeDetector(0),
00144 player(player_in),
00145 startedAt(startedAt_in), stopsAt(stopsAt_in),
00146 recordingStartedAt(recordingStartedAt_in),
00147 recordingStopsAt(recordingStopsAt_in), aggressiveDetection(false),
00148 stillRecording(recordingStopsAt > QDateTime::currentDateTime()),
00149 fullSpeed(fullSpeed_in), showProgress(showProgress_in),
00150 fps(0.0), framesProcessed(0),
00151 preRoll(0), postRoll(0)
00152 {
00153 commDetectBorder =
00154 gCoreContext->GetNumSetting("CommDetectBorder", 20);
00155 commDetectBlankFrameMaxDiff =
00156 gCoreContext->GetNumSetting("CommDetectBlankFrameMaxDiff", 25);
00157 commDetectDarkBrightness =
00158 gCoreContext->GetNumSetting("CommDetectDarkBrightness", 80);
00159 commDetectDimBrightness =
00160 gCoreContext->GetNumSetting("CommDetectDimBrightness", 120);
00161 commDetectBoxBrightness =
00162 gCoreContext->GetNumSetting("CommDetectBoxBrightness", 30);
00163 commDetectDimAverage =
00164 gCoreContext->GetNumSetting("CommDetectDimAverage", 35);
00165 commDetectMaxCommBreakLength =
00166 gCoreContext->GetNumSetting("CommDetectMaxCommBreakLength", 395);
00167 commDetectMinCommBreakLength =
00168 gCoreContext->GetNumSetting("CommDetectMinCommBreakLength", 60);
00169 commDetectMinShowLength =
00170 gCoreContext->GetNumSetting("CommDetectMinShowLength", 65);
00171 commDetectMaxCommLength =
00172 gCoreContext->GetNumSetting("CommDetectMaxCommLength", 125);
00173
00174 commDetectBlankCanHaveLogo =
00175 !!gCoreContext->GetNumSetting("CommDetectBlankCanHaveLogo", 1);
00176 }
00177
00178 void ClassicCommDetector::Init()
00179 {
00180 QSize video_disp_dim = player->GetVideoSize();
00181 width = video_disp_dim.width();
00182 height = video_disp_dim.height();
00183 fps = player->GetFrameRate();
00184
00185 preRoll = (long long)(max(0,recordingStartedAt.secsTo(startedAt)) * fps);
00186 postRoll = (long long)(max(0,stopsAt.secsTo(recordingStopsAt)) * fps);
00187
00188 #ifdef SHOW_DEBUG_WIN
00189 comm_debug_init(width, height);
00190 #endif
00191
00192 currentAspect = COMM_ASPECT_WIDE;
00193
00194 lastFrameNumber = -2;
00195 curFrameNumber = -1;
00196
00197 if (getenv("DEBUGCOMMFLAG"))
00198 verboseDebugging = true;
00199 else
00200 verboseDebugging = false;
00201
00202 LOG(VB_COMMFLAG, LOG_INFO,
00203 QString("Commercial Detection initialized: "
00204 "width = %1, height = %2, fps = %3, method = %4")
00205 .arg(width).arg(height)
00206 .arg(player->GetFrameRate()).arg(commDetectMethod));
00207
00208 if ((width * height) > 1000000)
00209 {
00210 horizSpacing = 10;
00211 vertSpacing = 10;
00212 }
00213 else if ((width * height) > 800000)
00214 {
00215 horizSpacing = 8;
00216 vertSpacing = 8;
00217 }
00218 else if ((width * height) > 400000)
00219 {
00220 horizSpacing = 6;
00221 vertSpacing = 6;
00222 }
00223 else if ((width * height) > 300000)
00224 {
00225 horizSpacing = 6;
00226 vertSpacing = 4;
00227 }
00228 else
00229 {
00230 horizSpacing = 4;
00231 vertSpacing = 4;
00232 }
00233
00234 LOG(VB_COMMFLAG, LOG_INFO,
00235 QString("Using Sample Spacing of %1 horizontal & %2 vertical pixels.")
00236 .arg(horizSpacing).arg(vertSpacing));
00237
00238 framesProcessed = 0;
00239 totalMinBrightness = 0;
00240 blankFrameCount = 0;
00241
00242 aggressiveDetection = true;
00243 currentAspect = COMM_ASPECT_WIDE;
00244 decoderFoundAspectChanges = false;
00245
00246 lastSentCommBreakMap.clear();
00247
00248
00249 if (fabs(((width*1.0)/height) - 1.333333) < 0.1)
00250 currentAspect = COMM_ASPECT_NORMAL;
00251
00252 sceneChangeDetector = new ClassicSceneChangeDetector(width, height,
00253 commDetectBorder, horizSpacing, vertSpacing);
00254 connect(
00255 sceneChangeDetector,
00256 SIGNAL(haveNewInformation(unsigned int,bool,float)),
00257 this,
00258 SLOT(sceneChangeDetectorHasNewInformation(unsigned int,bool,float))
00259 );
00260
00261 frameIsBlank = false;
00262 stationLogoPresent = false;
00263
00264 framePtr = NULL;
00265
00266 logoInfoAvailable = false;
00267
00268 ClearAllMaps();
00269
00270 if (verboseDebugging)
00271 {
00272 LOG(VB_COMMFLAG, LOG_DEBUG,
00273 " Fr # Min Max Avg Scn F A Mask");
00274 LOG(VB_COMMFLAG, LOG_DEBUG,
00275 " ------ --- --- --- --- - - ----");
00276 }
00277 }
00278
00279 void ClassicCommDetector::deleteLater(void)
00280 {
00281 if (sceneChangeDetector)
00282 sceneChangeDetector->deleteLater();
00283
00284 if (logoDetector)
00285 logoDetector->deleteLater();
00286
00287 CommDetectorBase::deleteLater();
00288 }
00289
00290 bool ClassicCommDetector::go()
00291 {
00292 int secsSince = 0;
00293 int requiredBuffer = 30;
00294 int requiredHeadStart = requiredBuffer;
00295 bool wereRecording = stillRecording;
00296
00297 emit statusUpdate(QObject::tr("Building Head Start Buffer"));
00298 secsSince = recordingStartedAt.secsTo(QDateTime::currentDateTime());
00299 while (stillRecording && (secsSince < requiredHeadStart))
00300 {
00301 emit breathe();
00302 if (m_bStop)
00303 return false;
00304
00305 sleep(2);
00306 secsSince = recordingStartedAt.secsTo(QDateTime::currentDateTime());
00307 }
00308
00309 if (player->OpenFile() < 0)
00310 return false;
00311
00312 Init();
00313
00314 if (commDetectMethod & COMM_DETECT_LOGO)
00315 {
00316 logoDetector = new ClassicLogoDetector(this, width, height,
00317 commDetectBorder, horizSpacing, vertSpacing);
00318
00319 requiredHeadStart += max(0,recordingStartedAt.secsTo(startedAt));
00320 requiredHeadStart += logoDetector->getRequiredAvailableBufferForSearch();
00321
00322 emit statusUpdate(QObject::tr("Building Logo Detection Buffer"));
00323 secsSince = recordingStartedAt.secsTo(QDateTime::currentDateTime());
00324 while (stillRecording && (secsSince < requiredHeadStart))
00325 {
00326 emit breathe();
00327 if (m_bStop)
00328 return false;
00329
00330 sleep(2);
00331 secsSince = recordingStartedAt.secsTo(QDateTime::currentDateTime());
00332 }
00333 }
00334
00335
00336 if ((wereRecording) && (!stillRecording) && (secsSince < requiredHeadStart))
00337 return false;
00338
00339 aggressiveDetection =
00340 gCoreContext->GetNumSetting("AggressiveCommDetect", 1);
00341
00342 if (!player->InitVideo())
00343 {
00344 LOG(VB_GENERAL, LOG_ERR,
00345 "NVP: Unable to initialize video for FlagCommercials.");
00346 return false;
00347 }
00348 player->EnableSubtitles(false);
00349
00350 if (commDetectMethod & COMM_DETECT_LOGO)
00351 {
00352 emit statusUpdate(QObject::tr("Searching for Logo"));
00353
00354 if (showProgress)
00355 {
00356 cerr << "Finding Logo";
00357 cerr.flush();
00358 }
00359 LOG(VB_GENERAL, LOG_INFO, "Finding Logo");
00360
00361 logoInfoAvailable = logoDetector->searchForLogo(player);
00362
00363 if (showProgress)
00364 {
00365 cerr << "\b\b\b\b\b\b\b\b\b\b\b\b "
00366 "\b\b\b\b\b\b\b\b\b\b\b\b";
00367 cerr.flush();
00368 }
00369 }
00370
00371 emit breathe();
00372 if (m_bStop)
00373 return false;
00374
00375 QTime flagTime;
00376 flagTime.start();
00377
00378 long long myTotalFrames;
00379 if (recordingStopsAt < QDateTime::currentDateTime() )
00380 myTotalFrames = player->GetTotalFrameCount();
00381 else
00382 myTotalFrames = (long long)(player->GetFrameRate() *
00383 (recordingStartedAt.secsTo(recordingStopsAt)));
00384
00385 if (showProgress)
00386 {
00387 if (myTotalFrames)
00388 cerr << "\r 0%/ \r" << flush;
00389 else
00390 cerr << "\r 0/ \r" << flush;
00391 }
00392
00393
00394 float flagFPS;
00395 long long currentFrameNumber;
00396 float aspect = player->GetVideoAspect();
00397 float newAspect = aspect;
00398 int prevpercent = -1;
00399
00400 SetVideoParams(aspect);
00401
00402 emit breathe();
00403
00404 player->ResetTotalDuration();
00405
00406 while (!player->GetEof())
00407 {
00408 struct timeval startTime;
00409 if (stillRecording)
00410 gettimeofday(&startTime, NULL);
00411
00412 VideoFrame* currentFrame = player->GetRawVideoFrame();
00413 currentFrameNumber = currentFrame->frameNumber;
00414
00415
00416
00417
00418
00419 newAspect = currentFrame->aspect;
00420 if (newAspect != aspect)
00421 {
00422 SetVideoParams(aspect);
00423 aspect = newAspect;
00424 }
00425
00426 if (((currentFrameNumber % 500) == 0) ||
00427 (((currentFrameNumber % 100) == 0) &&
00428 (stillRecording)))
00429 {
00430 emit breathe();
00431 if (m_bStop)
00432 {
00433 player->DiscardVideoFrame(currentFrame);
00434 return false;
00435 }
00436 }
00437
00438 if ((sendCommBreakMapUpdates) &&
00439 ((commBreakMapUpdateRequested) ||
00440 ((currentFrameNumber % 500) == 0)))
00441 {
00442 frm_dir_map_t commBreakMap;
00443 frm_dir_map_t::iterator it;
00444 frm_dir_map_t::iterator lastIt;
00445 bool mapsAreIdentical = false;
00446
00447 GetCommercialBreakList(commBreakMap);
00448
00449 if ((commBreakMap.size() == 0) &&
00450 (lastSentCommBreakMap.size() == 0))
00451 {
00452 mapsAreIdentical = true;
00453 }
00454 else if (commBreakMap.size() == lastSentCommBreakMap.size())
00455 {
00456
00457 mapsAreIdentical = true;
00458 for (it = commBreakMap.begin();
00459 it != commBreakMap.end() && mapsAreIdentical; ++it)
00460 {
00461 lastIt = lastSentCommBreakMap.find(it.key());
00462 if ((lastIt == lastSentCommBreakMap.end()) ||
00463 (*lastIt != *it))
00464 mapsAreIdentical = false;
00465 }
00466 }
00467
00468 if (commBreakMapUpdateRequested || !mapsAreIdentical)
00469 {
00470 emit gotNewCommercialBreakList();
00471 lastSentCommBreakMap = commBreakMap;
00472 }
00473
00474 if (commBreakMapUpdateRequested)
00475 commBreakMapUpdateRequested = false;
00476 }
00477
00478 while (m_bPaused)
00479 {
00480 emit breathe();
00481 sleep(1);
00482 }
00483
00484
00485 if (!fullSpeed && !stillRecording)
00486 usleep(10000);
00487
00488 if (((currentFrameNumber % 500) == 0) ||
00489 ((showProgress || stillRecording) &&
00490 ((currentFrameNumber % 100) == 0)))
00491 {
00492 float elapsed = flagTime.elapsed() / 1000.0;
00493
00494 if (elapsed)
00495 flagFPS = currentFrameNumber / elapsed;
00496 else
00497 flagFPS = 0.0;
00498
00499 int percentage;
00500 if (myTotalFrames)
00501 percentage = currentFrameNumber * 100 / myTotalFrames;
00502 else
00503 percentage = 0;
00504
00505 if (percentage > 100)
00506 percentage = 100;
00507
00508 if (showProgress)
00509 {
00510 if (myTotalFrames)
00511 {
00512 QString tmp = QString("\r%1%/%2fps \r")
00513 .arg(percentage, 3).arg((int)flagFPS, 4);
00514 cerr << qPrintable(tmp) << flush;
00515 }
00516 else
00517 {
00518 QString tmp = QString("\r%1/%2fps \r")
00519 .arg(currentFrameNumber, 6).arg((int)flagFPS, 4);
00520 cerr << qPrintable(tmp) << flush;
00521 }
00522 }
00523
00524 if (myTotalFrames)
00525 emit statusUpdate(QObject::tr("%1% Completed @ %2 fps.")
00526 .arg(percentage).arg(flagFPS));
00527 else
00528 emit statusUpdate(QObject::tr("%1 Frames Completed @ %2 fps.")
00529 .arg(currentFrameNumber).arg(flagFPS));
00530
00531 if (percentage % 10 == 0 && prevpercent != percentage)
00532 {
00533 prevpercent = percentage;
00534 LOG(VB_GENERAL, LOG_INFO, QString("%1%% Completed @ %2 fps.")
00535 .arg(percentage) .arg(flagFPS));
00536 }
00537 }
00538
00539 ProcessFrame(currentFrame, currentFrameNumber);
00540
00541 if (stillRecording)
00542 {
00543 int secondsRecorded =
00544 recordingStartedAt.secsTo(QDateTime::currentDateTime());
00545 int secondsFlagged = (int)(framesProcessed / fps);
00546 int secondsBehind = secondsRecorded - secondsFlagged;
00547 long usecPerFrame = (long)(1.0 / player->GetFrameRate() * 1000000);
00548
00549 struct timeval endTime;
00550 gettimeofday(&endTime, NULL);
00551
00552 long long usecSleep =
00553 usecPerFrame -
00554 (((endTime.tv_sec - startTime.tv_sec) * 1000000) +
00555 (endTime.tv_usec - startTime.tv_usec));
00556
00557 if (secondsBehind > requiredBuffer)
00558 {
00559 if (fullSpeed)
00560 usecSleep = 0;
00561 else
00562 usecSleep = (long)(usecSleep * 0.25);
00563 }
00564 else if (secondsBehind < requiredBuffer)
00565 usecSleep = (long)(usecPerFrame * 1.5);
00566
00567 if (usecSleep > 0)
00568 usleep(usecSleep);
00569 }
00570
00571 player->DiscardVideoFrame(currentFrame);
00572 }
00573
00574 if (showProgress)
00575 {
00576 float elapsed = flagTime.elapsed() / 1000.0;
00577
00578 if (elapsed)
00579 flagFPS = currentFrameNumber / elapsed;
00580 else
00581 flagFPS = 0.0;
00582
00583 if (myTotalFrames)
00584 cerr << "\b\b\b\b\b\b \b\b\b\b\b\b";
00585 else
00586 cerr << "\b\b\b\b\b\b\b\b\b\b\b\b\b "
00587 "\b\b\b\b\b\b\b\b\b\b\b\b\b";
00588 cerr.flush();
00589 }
00590
00591 return true;
00592 }
00593
00594 void ClassicCommDetector::sceneChangeDetectorHasNewInformation(
00595 unsigned int framenum,bool isSceneChange,float debugValue)
00596 {
00597 if (isSceneChange)
00598 {
00599 frameInfo[framenum].flagMask |= COMM_FRAME_SCENE_CHANGE;
00600 sceneMap[framenum] = MARK_SCENE_CHANGE;
00601 }
00602 else
00603 {
00604 frameInfo[framenum].flagMask &= ~COMM_FRAME_SCENE_CHANGE;
00605 sceneMap.remove(framenum);
00606 }
00607
00608 frameInfo[framenum].sceneChangePercent = (int) (debugValue*100);
00609 }
00610
00611 void ClassicCommDetector::GetCommercialBreakList(frm_dir_map_t &marks)
00612 {
00613
00614 LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetCommBreakMap()");
00615
00616 marks.clear();
00617
00618 CleanupFrameInfo();
00619
00620 bool blank = COMM_DETECT_BLANK & commDetectMethod;
00621 bool scene = COMM_DETECT_SCENE & commDetectMethod;
00622 bool logo = COMM_DETECT_LOGO & commDetectMethod;
00623
00624 if (COMM_DETECT_OFF == commDetectMethod)
00625 return;
00626
00627 if (!blank && !scene && !logo)
00628 {
00629 LOG(VB_COMMFLAG, LOG_ERR, QString("Unexpected commDetectMethod: 0x%1")
00630 .arg(commDetectMethod,0,16));
00631 return;
00632 }
00633
00634 if (blank && scene && logo)
00635 {
00636 BuildAllMethodsCommList();
00637 marks = commBreakMap;
00638 LOG(VB_COMMFLAG, LOG_INFO, "Final Commercial Break Map");
00639 return;
00640 }
00641
00642 if (blank)
00643 {
00644 BuildBlankFrameCommList();
00645 marks = blankCommBreakMap;
00646 }
00647
00648 if (scene)
00649 {
00650 BuildSceneChangeCommList();
00651 marks = sceneCommBreakMap;
00652 }
00653
00654 if (logo)
00655 {
00656 BuildLogoCommList();
00657 marks = logoCommBreakMap;
00658 }
00659
00660 int cnt = ((blank) ? 1 : 0) + ((scene) ? 1 : 0) + ((logo) ? 1 : 0);
00661 if (cnt == 2)
00662 {
00663 if (blank && scene)
00664 {
00665 marks = commBreakMap = Combine2Maps(
00666 blankCommBreakMap, sceneCommBreakMap);
00667 }
00668 else if (blank && logo)
00669 {
00670 marks = commBreakMap = Combine2Maps(
00671 blankCommBreakMap, logoCommBreakMap);
00672 }
00673 else if (scene && logo)
00674 {
00675 marks = commBreakMap = Combine2Maps(
00676 sceneCommBreakMap, logoCommBreakMap);
00677 }
00678 }
00679
00680 LOG(VB_COMMFLAG, LOG_INFO, "Final Commercial Break Map");
00681 }
00682
00683 void ClassicCommDetector::recordingFinished(long long totalFileSize)
00684 {
00685 (void)totalFileSize;
00686
00687 stillRecording = false;
00688 }
00689
00690 void ClassicCommDetector::requestCommBreakMapUpdate(void)
00691 {
00692 commBreakMapUpdateRequested = true;
00693 sendCommBreakMapUpdates = true;
00694 }
00695
00696 void ClassicCommDetector::SetVideoParams(float aspect)
00697 {
00698 int newAspect = COMM_ASPECT_WIDE;
00699
00700 LOG(VB_COMMFLAG, LOG_INFO,
00701 QString("CommDetect::SetVideoParams called with aspect = %1")
00702 .arg(aspect));
00703
00704
00705 if (fabs(aspect - 1.333333) < 0.1)
00706 newAspect = COMM_ASPECT_NORMAL;
00707
00708 if (newAspect != currentAspect)
00709 {
00710 LOG(VB_COMMFLAG, LOG_INFO,
00711 QString("Aspect Ratio changed from %1 to %2 at frame %3")
00712 .arg(currentAspect).arg(newAspect)
00713 .arg(curFrameNumber));
00714
00715 if (frameInfo.contains(curFrameNumber))
00716 {
00717
00718
00719 frameInfo[curFrameNumber].flagMask |= COMM_FRAME_BLANK;
00720 frameInfo[curFrameNumber].flagMask |= COMM_FRAME_ASPECT_CHANGE;
00721 decoderFoundAspectChanges = true;
00722 }
00723 else if (curFrameNumber != -1)
00724 {
00725 LOG(VB_COMMFLAG, LOG_ERR,
00726 QString("Unable to keep track of Aspect ratio change because "
00727 "frameInfo for frame number %1 does not exist.")
00728 .arg(curFrameNumber));
00729 }
00730 currentAspect = newAspect;
00731 }
00732 }
00733
00734 void ClassicCommDetector::ProcessFrame(VideoFrame *frame,
00735 long long frame_number)
00736 {
00737 int max = 0;
00738 int min = 255;
00739 int avg = 0;
00740 unsigned char pixel;
00741 int blankPixelsChecked = 0;
00742 long long totBrightness = 0;
00743 unsigned char *rowMax = new unsigned char[height];
00744 unsigned char *colMax = new unsigned char[width];
00745 memset(rowMax, 0, sizeof(*rowMax)*height);
00746 memset(colMax, 0, sizeof(*colMax)*width);
00747 int topDarkRow = commDetectBorder;
00748 int bottomDarkRow = height - commDetectBorder - 1;
00749 int leftDarkCol = commDetectBorder;
00750 int rightDarkCol = width - commDetectBorder - 1;
00751 FrameInfoEntry fInfo;
00752
00753 if (!frame || !(frame->buf) || frame_number == -1 ||
00754 frame->codec != FMT_YV12)
00755 {
00756 LOG(VB_COMMFLAG, LOG_ERR, "CommDetect: Invalid video frame or codec, "
00757 "unable to process frame.");
00758 delete[] rowMax;
00759 delete[] colMax;
00760 return;
00761 }
00762
00763 if (!width || !height)
00764 {
00765 LOG(VB_COMMFLAG, LOG_ERR, "CommDetect: Width or Height is 0, "
00766 "unable to process frame.");
00767 delete[] rowMax;
00768 delete[] colMax;
00769 return;
00770 }
00771
00772 curFrameNumber = frame_number;
00773 framePtr = frame->buf;
00774
00775 fInfo.minBrightness = -1;
00776 fInfo.maxBrightness = -1;
00777 fInfo.avgBrightness = -1;
00778 fInfo.sceneChangePercent = -1;
00779 fInfo.aspect = currentAspect;
00780 fInfo.format = COMM_FORMAT_NORMAL;
00781 fInfo.flagMask = 0;
00782
00783 int& flagMask = frameInfo[curFrameNumber].flagMask;
00784
00785
00786 if (lastFrameNumber != (curFrameNumber - 1))
00787 {
00788 if (lastFrameNumber > 0)
00789 {
00790 fInfo.aspect = frameInfo[lastFrameNumber].aspect;
00791 fInfo.format = frameInfo[lastFrameNumber].format;
00792 }
00793 fInfo.flagMask = COMM_FRAME_SKIPPED;
00794
00795 lastFrameNumber++;
00796 while(lastFrameNumber < curFrameNumber)
00797 frameInfo[lastFrameNumber++] = fInfo;
00798
00799 fInfo.flagMask = 0;
00800 }
00801 lastFrameNumber = curFrameNumber;
00802
00803 frameInfo[curFrameNumber] = fInfo;
00804
00805 if (commDetectMethod & COMM_DETECT_BLANKS)
00806 frameIsBlank = false;
00807
00808 if (commDetectMethod & COMM_DETECT_SCENE)
00809 {
00810 sceneChangeDetector->processFrame(framePtr);
00811 }
00812
00813 stationLogoPresent = false;
00814
00815 for(int y = commDetectBorder; y < (height - commDetectBorder);
00816 y += vertSpacing)
00817 {
00818 for(int x = commDetectBorder; x < (width - commDetectBorder);
00819 x += horizSpacing)
00820 {
00821 pixel = framePtr[y * width + x];
00822
00823 if (commDetectMethod & COMM_DETECT_BLANKS)
00824 {
00825 bool checkPixel = false;
00826 if (!commDetectBlankCanHaveLogo)
00827 checkPixel = true;
00828
00829 if (!logoInfoAvailable)
00830 checkPixel = true;
00831 else if (!logoDetector->pixelInsideLogo(x,y))
00832 checkPixel=true;
00833
00834 if (checkPixel)
00835 {
00836 blankPixelsChecked++;
00837 totBrightness += pixel;
00838
00839 if (pixel < min)
00840 min = pixel;
00841
00842 if (pixel > max)
00843 max = pixel;
00844
00845 if (pixel > rowMax[y])
00846 rowMax[y] = pixel;
00847
00848 if (pixel > colMax[x])
00849 colMax[x] = pixel;
00850 }
00851 }
00852 }
00853 }
00854
00855 if (commDetectMethod & COMM_DETECT_BLANKS)
00856 {
00857 for(int y = commDetectBorder; y < (height - commDetectBorder);
00858 y += vertSpacing)
00859 {
00860 if (rowMax[y] > commDetectBoxBrightness)
00861 break;
00862 else
00863 topDarkRow = y;
00864 }
00865
00866 for(int y = commDetectBorder; y < (height - commDetectBorder);
00867 y += vertSpacing)
00868 if (rowMax[y] >= commDetectBoxBrightness)
00869 bottomDarkRow = y;
00870
00871 delete[] rowMax;
00872 rowMax = 0;
00873
00874 for(int x = commDetectBorder; x < (width - commDetectBorder);
00875 x += horizSpacing)
00876 {
00877 if (colMax[x] > commDetectBoxBrightness)
00878 break;
00879 else
00880 leftDarkCol = x;
00881 }
00882
00883 for(int x = commDetectBorder; x < (width - commDetectBorder);
00884 x += horizSpacing)
00885 if (colMax[x] >= commDetectBoxBrightness)
00886 rightDarkCol = x;
00887
00888 delete[] colMax;
00889 colMax = 0;
00890
00891 if ((topDarkRow > commDetectBorder) &&
00892 (topDarkRow < (height * .20)) &&
00893 (bottomDarkRow < (height - commDetectBorder)) &&
00894 (bottomDarkRow > (height * .80)))
00895 {
00896 frameInfo[curFrameNumber].format = COMM_FORMAT_LETTERBOX;
00897 }
00898 else if ((leftDarkCol > commDetectBorder) &&
00899 (leftDarkCol < (width * .20)) &&
00900 (rightDarkCol < (width - commDetectBorder)) &&
00901 (rightDarkCol > (width * .80)))
00902 {
00903 frameInfo[curFrameNumber].format = COMM_FORMAT_PILLARBOX;
00904 }
00905 else
00906 {
00907 frameInfo[curFrameNumber].format = COMM_FORMAT_NORMAL;
00908 }
00909
00910 avg = totBrightness / blankPixelsChecked;
00911
00912 frameInfo[curFrameNumber].minBrightness = min;
00913 frameInfo[curFrameNumber].maxBrightness = max;
00914 frameInfo[curFrameNumber].avgBrightness = avg;
00915
00916 totalMinBrightness += min;
00917 commDetectDimAverage = min + 10;
00918
00919
00920 if (((max - min) <= commDetectBlankFrameMaxDiff) &&
00921 (max < commDetectDimBrightness))
00922 frameIsBlank = true;
00923
00924
00925 if ((!aggressiveDetection) &&
00926 ((max - min) <= commDetectBlankFrameMaxDiff))
00927 frameIsBlank = true;
00928
00929
00930
00931 if ((!aggressiveDetection) &&
00932 ((max < commDetectDarkBrightness) ||
00933 ((max < commDetectDimBrightness) && (avg < commDetectDimAverage))))
00934 frameIsBlank = true;
00935 }
00936
00937 if ((logoInfoAvailable) && (commDetectMethod & COMM_DETECT_LOGO))
00938 {
00939 stationLogoPresent =
00940 logoDetector->doesThisFrameContainTheFoundLogo(framePtr);
00941 }
00942
00943 #if 0
00944 if ((commDetectMethod == COMM_DETECT_ALL) &&
00945 (CheckRatingSymbol()))
00946 {
00947 flagMask |= COMM_FRAME_RATING_SYMBOL;
00948 }
00949 #endif
00950
00951 if (frameIsBlank)
00952 {
00953 blankFrameMap[curFrameNumber] = MARK_BLANK_FRAME;
00954 flagMask |= COMM_FRAME_BLANK;
00955 blankFrameCount++;
00956 }
00957
00958 if (stationLogoPresent)
00959 flagMask |= COMM_FRAME_LOGO_PRESENT;
00960
00961
00962
00963
00964
00965
00966
00967
00968
00969 if (verboseDebugging)
00970 LOG(VB_COMMFLAG, LOG_DEBUG,
00971 QString().sprintf("Frame: %6ld -> %3d %3d %3d %3d %1d %1d %04x",
00972 (long)curFrameNumber,
00973 frameInfo[curFrameNumber].minBrightness,
00974 frameInfo[curFrameNumber].maxBrightness,
00975 frameInfo[curFrameNumber].avgBrightness,
00976 frameInfo[curFrameNumber].sceneChangePercent,
00977 frameInfo[curFrameNumber].format,
00978 frameInfo[curFrameNumber].aspect,
00979 frameInfo[curFrameNumber].flagMask ));
00980
00981 #ifdef SHOW_DEBUG_WIN
00982 comm_debug_show(frame->buf);
00983 getchar();
00984 #endif
00985
00986 framesProcessed++;
00987 delete[] rowMax;
00988 delete[] colMax;
00989 }
00990
00991 void ClassicCommDetector::ClearAllMaps(void)
00992 {
00993 LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::ClearAllMaps()");
00994
00995 frameInfo.clear();
00996 blankFrameMap.clear();
00997 blankCommMap.clear();
00998 blankCommBreakMap.clear();
00999 sceneMap.clear();
01000 sceneCommBreakMap.clear();
01001 commBreakMap.clear();
01002 }
01003
01004 void ClassicCommDetector::GetBlankCommMap(frm_dir_map_t &comms)
01005 {
01006 LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetBlankCommMap()");
01007
01008 if (blankCommMap.isEmpty())
01009 BuildBlankFrameCommList();
01010
01011 comms = blankCommMap;
01012 }
01013
01014 void ClassicCommDetector::GetBlankCommBreakMap(frm_dir_map_t &comms)
01015 {
01016 LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetBlankCommBreakMap()");
01017
01018 if (blankCommBreakMap.isEmpty())
01019 BuildBlankFrameCommList();
01020
01021 comms = blankCommBreakMap;
01022 }
01023
01024 void ClassicCommDetector::GetSceneChangeMap(frm_dir_map_t &scenes,
01025 int64_t start_frame)
01026 {
01027 LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetSceneChangeMap()");
01028
01029 frm_dir_map_t::iterator it;
01030
01031 if (start_frame == -1)
01032 scenes.clear();
01033
01034 for (it = sceneMap.begin(); it != sceneMap.end(); ++it)
01035 if ((start_frame == -1) || ((int64_t)it.key() >= start_frame))
01036 scenes[it.key()] = *it;
01037 }
01038
01039 frm_dir_map_t ClassicCommDetector::Combine2Maps(const frm_dir_map_t &a,
01040 const frm_dir_map_t &b) const
01041 {
01042 LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::BuildMasterCommList()");
01043
01044 frm_dir_map_t newMap;
01045
01046 if (a.size())
01047 {
01048 frm_dir_map_t::const_iterator it = a.begin();
01049 for (; it != a.end(); ++it)
01050 newMap[it.key()] = *it;
01051 }
01052
01053 if ((a.size() > 1) &&
01054 (b.size() > 1))
01055 {
01056
01057 frm_dir_map_t::const_iterator it_a;
01058 frm_dir_map_t::const_iterator it_b;
01059
01060 it_a = a.begin();
01061 it_b = b.begin();
01062
01063 if ((it_b.key() < 2) && (it_a.key() > 2))
01064 {
01065 newMap.remove(it_a.key());
01066 newMap[0] = MARK_COMM_START;
01067 }
01068
01069
01070
01071 frm_dir_map_t::const_iterator it;
01072 uint64_t max_a = 0;
01073 uint64_t max_b = 0;
01074
01075 it = a.begin();
01076 for (; it != a.end(); ++it)
01077 {
01078 if ((*it == MARK_COMM_END) && (it.key() > max_a))
01079 max_a = it.key();
01080 }
01081
01082 it = b.begin();
01083 for (; it != b.end(); ++it)
01084 {
01085 if ((*it == MARK_COMM_END) && (it.key() > max_b))
01086 max_b = it.key();
01087 }
01088
01089 if ((max_a < (framesProcessed - 2)) &&
01090 (max_b > (framesProcessed - 2)))
01091 {
01092 newMap.remove(max_a);
01093 newMap[framesProcessed] = MARK_COMM_END;
01094 }
01095 }
01096
01097 if ((a.size() > 3) &&
01098 (b.size() > 1))
01099 {
01100 frm_dir_map_t::const_iterator it_a;
01101 frm_dir_map_t::const_iterator it_b;
01102
01103 it_a = a.begin();
01104 ++it_a;
01105 it_b = it_a;
01106 ++it_b;
01107 while (it_b != a.end())
01108 {
01109 uint64_t fdiff = it_b.key() - it_a.key();
01110 bool allTrue = false;
01111
01112 if (fdiff < (62 * fps))
01113 {
01114 uint64_t f = it_a.key() + 1;
01115
01116 allTrue = true;
01117
01118 while ((f <= framesProcessed) && (f < it_b.key()) && (allTrue))
01119 allTrue = FrameIsInBreakMap(f++, b);
01120 }
01121
01122 if (allTrue)
01123 {
01124 newMap.remove(it_a.key());
01125 newMap.remove(it_b.key());
01126 }
01127
01128 ++it_a; ++it_a;
01129 ++it_b;
01130 if (it_b != a.end())
01131 ++it_b;
01132 }
01133 }
01134
01135 return newMap;
01136 }
01137
01138 void ClassicCommDetector::UpdateFrameBlock(FrameBlock *fbp,
01139 FrameInfoEntry finfo,
01140 int format, int aspect)
01141 {
01142 int value = 0;
01143
01144 value = finfo.flagMask;
01145
01146 if (value & COMM_FRAME_LOGO_PRESENT)
01147 fbp->logoCount++;
01148
01149 if (value & COMM_FRAME_RATING_SYMBOL)
01150 fbp->ratingCount++;
01151
01152 if (value & COMM_FRAME_SCENE_CHANGE)
01153 fbp->scCount++;
01154
01155 if (finfo.format == format)
01156 fbp->formatMatch++;
01157
01158 if (finfo.aspect == aspect)
01159 fbp->aspectMatch++;
01160 }
01161
01162
01163 void ClassicCommDetector::BuildAllMethodsCommList(void)
01164 {
01165 LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::BuildAllMethodsCommList()");
01166
01167 FrameBlock *fblock;
01168 FrameBlock *fbp;
01169 int value = 0;
01170 int curBlock = 0;
01171 int maxBlock = 0;
01172 int lastScore = 0;
01173 int thisScore = 0;
01174 int nextScore = 0;
01175 uint64_t curFrame = 0;
01176 int64_t breakStart = 0;
01177 uint64_t lastStart = 0;
01178 uint64_t lastEnd = 0;
01179 int64_t firstLogoFrame = -1;
01180 bool nextFrameIsBlank = false;
01181 bool lastFrameWasBlank = false;
01182 uint64_t formatFrames = 0;
01183 int format = COMM_FORMAT_NORMAL;
01184 uint64_t aspectFrames = 0;
01185 int aspect = COMM_ASPECT_NORMAL;
01186 QString msg;
01187 uint64_t formatCounts[COMM_FORMAT_MAX];
01188 frm_dir_map_t tmpCommMap;
01189 frm_dir_map_t::iterator it;
01190
01191 commBreakMap.clear();
01192
01193 fblock = new FrameBlock[blankFrameCount + 2];
01194
01195 curBlock = 0;
01196 curFrame = 1;
01197
01198 fbp = &fblock[curBlock];
01199 fbp->start = 0;
01200 fbp->bfCount = 0;
01201 fbp->logoCount = 0;
01202 fbp->ratingCount = 0;
01203 fbp->scCount = 0;
01204 fbp->scRate = 0.0;
01205 fbp->formatMatch = 0;
01206 fbp->aspectMatch = 0;
01207 fbp->score = 0;
01208
01209 lastFrameWasBlank = true;
01210
01211 if (decoderFoundAspectChanges)
01212 {
01213 for (int64_t i = preRoll;
01214 i < ((int64_t)framesProcessed - (int64_t)postRoll); i++)
01215 {
01216 if ((frameInfo.contains(i)) &&
01217 (frameInfo[i].aspect == COMM_ASPECT_NORMAL))
01218 aspectFrames++;
01219 }
01220
01221 if (aspectFrames < ((framesProcessed - preRoll - postRoll) / 2))
01222 {
01223 aspect = COMM_ASPECT_WIDE;
01224 aspectFrames = framesProcessed - preRoll - postRoll - aspectFrames;
01225 }
01226 }
01227 else
01228 {
01229 memset(&formatCounts, 0, sizeof(formatCounts));
01230
01231 for(int64_t i = preRoll;
01232 i < ((int64_t)framesProcessed - (int64_t)postRoll); i++ )
01233 if ((frameInfo.contains(i)) &&
01234 (frameInfo[i].format >= 0) &&
01235 (frameInfo[i].format < COMM_FORMAT_MAX))
01236 formatCounts[frameInfo[i].format]++;
01237
01238 for(int i = 0; i < COMM_FORMAT_MAX; i++)
01239 {
01240 if (formatCounts[i] > formatFrames)
01241 {
01242 format = i;
01243 formatFrames = formatCounts[i];
01244 }
01245 }
01246 }
01247
01248 while (curFrame <= framesProcessed)
01249 {
01250 value = frameInfo[curFrame].flagMask;
01251
01252 if (((curFrame + 1) <= framesProcessed) &&
01253 (frameInfo[curFrame + 1].flagMask & COMM_FRAME_BLANK))
01254 nextFrameIsBlank = true;
01255 else
01256 nextFrameIsBlank = false;
01257
01258 if (value & COMM_FRAME_BLANK)
01259 {
01260 fbp->bfCount++;
01261
01262 if (!nextFrameIsBlank || !lastFrameWasBlank)
01263 {
01264 UpdateFrameBlock(fbp, frameInfo[curFrame], format, aspect);
01265
01266 fbp->end = curFrame;
01267 fbp->frames = fbp->end - fbp->start + 1;
01268 fbp->length = fbp->frames / fps;
01269
01270 if ((fbp->scCount) && (fbp->length > 1.05))
01271 fbp->scRate = fbp->scCount / fbp->length;
01272
01273 curBlock++;
01274
01275 fbp = &fblock[curBlock];
01276 fbp->bfCount = 1;
01277 fbp->logoCount = 0;
01278 fbp->ratingCount = 0;
01279 fbp->scCount = 0;
01280 fbp->scRate = 0.0;
01281 fbp->score = 0;
01282 fbp->formatMatch = 0;
01283 fbp->aspectMatch = 0;
01284 fbp->start = curFrame;
01285 }
01286
01287 lastFrameWasBlank = true;
01288 }
01289 else
01290 {
01291 lastFrameWasBlank = false;
01292 }
01293
01294 UpdateFrameBlock(fbp, frameInfo[curFrame], format, aspect);
01295
01296 if ((value & COMM_FRAME_LOGO_PRESENT) &&
01297 (firstLogoFrame == -1))
01298 firstLogoFrame = curFrame;
01299
01300 curFrame++;
01301 }
01302
01303 fbp->end = curFrame;
01304 fbp->frames = fbp->end - fbp->start + 1;
01305 fbp->length = fbp->frames / fps;
01306
01307 if ((fbp->scCount) && (fbp->length > 1.05))
01308 fbp->scRate = fbp->scCount / fbp->length;
01309
01310 maxBlock = curBlock;
01311 curBlock = 0;
01312 lastScore = 0;
01313
01314 LOG(VB_COMMFLAG, LOG_INFO, "Initial Block pass");
01315 LOG(VB_COMMFLAG, LOG_DEBUG,
01316 "Block StTime StFrm EndFrm Frames Secs "
01317 "Bf Lg Cnt RT Cnt SC Cnt SC Rt FmtMch AspMch Score");
01318 LOG(VB_COMMFLAG, LOG_INFO,
01319 "----- ------ ------ ------ ------ ------- "
01320 "--- ------ ------ ------ ----- ------ ------ -----");
01321 while (curBlock <= maxBlock)
01322 {
01323 fbp = &fblock[curBlock];
01324
01325 msg.sprintf("%5d %3d:%02d %6ld %6ld %6ld %7.2f %3d %6d %6d %6d "
01326 "%5.2f %6d %6d %5d",
01327 curBlock, (int)(fbp->start / fps) / 60,
01328 (int)((fbp->start / fps )) % 60,
01329 fbp->start, fbp->end, fbp->frames, fbp->length,
01330 fbp->bfCount, fbp->logoCount, fbp->ratingCount,
01331 fbp->scCount, fbp->scRate, fbp->formatMatch,
01332 fbp->aspectMatch, fbp->score);
01333 LOG(VB_COMMFLAG, LOG_DEBUG, msg);
01334
01335 if (fbp->frames > fps)
01336 {
01337 if (verboseDebugging)
01338 LOG(VB_COMMFLAG, LOG_DEBUG,
01339 QString(" FRAMES > %1").arg(fps));
01340
01341 if (fbp->length > commDetectMaxCommLength)
01342 {
01343 if (verboseDebugging)
01344 LOG(VB_COMMFLAG, LOG_DEBUG,
01345 " length > max comm length, +20");
01346 fbp->score += 20;
01347 }
01348
01349 if (fbp->length > commDetectMaxCommBreakLength)
01350 {
01351 if (verboseDebugging)
01352 LOG(VB_COMMFLAG, LOG_DEBUG,
01353 " length > max comm break length, +20");
01354 fbp->score += 20;
01355 }
01356
01357 if ((fbp->length > 4) &&
01358 (fbp->logoCount > (fbp->frames * 0.60)) &&
01359 (fbp->bfCount < (fbp->frames * 0.10)))
01360 {
01361 if (verboseDebugging)
01362 LOG(VB_COMMFLAG, LOG_DEBUG,
01363 " length > 4 && logoCount > frames * 0.60 && "
01364 "bfCount < frames * .10");
01365 if (fbp->length > commDetectMaxCommBreakLength)
01366 {
01367 if (verboseDebugging)
01368 LOG(VB_COMMFLAG, LOG_DEBUG,
01369 " length > max comm break length, +20");
01370 fbp->score += 20;
01371 }
01372 else
01373 {
01374 if (verboseDebugging)
01375 LOG(VB_COMMFLAG, LOG_DEBUG,
01376 " length <= max comm break length, +10");
01377 fbp->score += 10;
01378 }
01379 }
01380
01381 if ((logoInfoAvailable) &&
01382 (fbp->logoCount < (fbp->frames * 0.50)))
01383 {
01384 if (verboseDebugging)
01385 LOG(VB_COMMFLAG, LOG_DEBUG,
01386 " logoInfoAvailable && logoCount < frames * .50, "
01387 "-10");
01388 fbp->score -= 10;
01389 }
01390
01391 if (fbp->ratingCount > (fbp->frames * 0.05))
01392 {
01393 if (verboseDebugging)
01394 LOG(VB_COMMFLAG, LOG_DEBUG,
01395 " rating symbol present > 5% of time, +20");
01396 fbp->score += 20;
01397 }
01398
01399 if ((fbp->scRate > 1.0) &&
01400 (fbp->logoCount < (fbp->frames * .90)))
01401 {
01402 if (verboseDebugging)
01403 LOG(VB_COMMFLAG, LOG_DEBUG, " scRate > 1.0, -10");
01404 fbp->score -= 10;
01405
01406 if (fbp->scRate > 2.0)
01407 {
01408 if (verboseDebugging)
01409 LOG(VB_COMMFLAG, LOG_DEBUG, " scRate > 2.0, -10");
01410 fbp->score -= 10;
01411 }
01412 }
01413
01414 if ((!decoderFoundAspectChanges) &&
01415 (fbp->formatMatch < (fbp->frames * .10)))
01416 {
01417 if (verboseDebugging)
01418 LOG(VB_COMMFLAG, LOG_DEBUG,
01419 " < 10% of frames match show letter/pillar-box "
01420 "format, -20");
01421 fbp->score -= 20;
01422 }
01423
01424 if ((abs((int)(fbp->frames - (15 * fps))) < 5 ) ||
01425 (abs((int)(fbp->frames - (30 * fps))) < 6 ) ||
01426 (abs((int)(fbp->frames - (60 * fps))) < 8 ))
01427 {
01428 if (verboseDebugging)
01429 LOG(VB_COMMFLAG, LOG_DEBUG,
01430 " block appears to be standard comm length, -10");
01431 fbp->score -= 10;
01432 }
01433 }
01434 else
01435 {
01436 if (verboseDebugging)
01437 LOG(VB_COMMFLAG, LOG_DEBUG,
01438 QString(" FRAMES <= %1").arg(fps));
01439
01440 if ((logoInfoAvailable) &&
01441 (fbp->start >= firstLogoFrame) &&
01442 (fbp->logoCount == 0))
01443 {
01444 if (verboseDebugging)
01445 LOG(VB_COMMFLAG, LOG_DEBUG,
01446 " logoInfoAvailable && logoCount == 0, -10");
01447 fbp->score -= 10;
01448 }
01449
01450 if ((!decoderFoundAspectChanges) &&
01451 (fbp->formatMatch < (fbp->frames * .10)))
01452 {
01453 if (verboseDebugging)
01454 LOG(VB_COMMFLAG, LOG_DEBUG,
01455 " < 10% of frames match show letter/pillar-box "
01456 "format, -10");
01457 fbp->score -= 10;
01458 }
01459
01460 if (fbp->ratingCount > (fbp->frames * 0.25))
01461 {
01462 if (verboseDebugging)
01463 LOG(VB_COMMFLAG, LOG_DEBUG,
01464 " rating symbol present > 25% of time, +10");
01465 fbp->score += 10;
01466 }
01467 }
01468
01469 if ((decoderFoundAspectChanges) &&
01470 (fbp->aspectMatch < (fbp->frames * .10)))
01471 {
01472 if (verboseDebugging)
01473 LOG(VB_COMMFLAG, LOG_DEBUG,
01474 " < 10% of frames match show aspect, -20");
01475 fbp->score -= 20;
01476 }
01477
01478 msg.sprintf(" NOW %3d:%02d %6ld %6ld %6ld %7.2f %3d %6d %6d %6d "
01479 "%5.2f %6d %6d %5d",
01480 (int)(fbp->start / fps) / 60,
01481 (int)((fbp->start / fps )) % 60,
01482 fbp->start, fbp->end, fbp->frames, fbp->length,
01483 fbp->bfCount, fbp->logoCount, fbp->ratingCount,
01484 fbp->scCount, fbp->scRate, fbp->formatMatch,
01485 fbp->aspectMatch, fbp->score);
01486 LOG(VB_COMMFLAG, LOG_DEBUG, msg);
01487
01488 lastScore = fbp->score;
01489 curBlock++;
01490 }
01491
01492 curBlock = 0;
01493 lastScore = 0;
01494
01495 LOG(VB_COMMFLAG, LOG_DEBUG, "============================================");
01496 LOG(VB_COMMFLAG, LOG_INFO, "Second Block pass");
01497 LOG(VB_COMMFLAG, LOG_DEBUG,
01498 "Block StTime StFrm EndFrm Frames Secs "
01499 "Bf Lg Cnt RT Cnt SC Cnt SC Rt FmtMch AspMch Score");
01500 LOG(VB_COMMFLAG, LOG_DEBUG,
01501 "----- ------ ------ ------ ------ ------- "
01502 "--- ------ ------ ------ ----- ------ ------ -----");
01503 while (curBlock <= maxBlock)
01504 {
01505 fbp = &fblock[curBlock];
01506
01507 msg.sprintf("%5d %3d:%02d %6ld %6ld %6ld %7.2f %3d %6d %6d %6d "
01508 "%5.2f %6d %6d %5d",
01509 curBlock, (int)(fbp->start / fps) / 60,
01510 (int)((fbp->start / fps )) % 60,
01511 fbp->start, fbp->end, fbp->frames, fbp->length,
01512 fbp->bfCount, fbp->logoCount, fbp->ratingCount,
01513 fbp->scCount, fbp->scRate, fbp->formatMatch,
01514 fbp->aspectMatch, fbp->score);
01515 LOG(VB_COMMFLAG, LOG_DEBUG, msg);
01516
01517 if ((curBlock > 0) && (curBlock < maxBlock))
01518 {
01519 nextScore = fblock[curBlock + 1].score;
01520
01521 if ((lastScore < 0) && (nextScore < 0) && (fbp->length < 35))
01522 {
01523 if (verboseDebugging)
01524 LOG(VB_COMMFLAG, LOG_DEBUG,
01525 " lastScore < 0 && nextScore < 0 "
01526 "&& length < 35, setting -10");
01527 fbp->score -= 10;
01528 }
01529
01530 if ((fbp->bfCount > (fbp->frames * 0.95)) &&
01531 (fbp->frames < (2*fps)) &&
01532 (lastScore < 0 && nextScore < 0))
01533 {
01534 if (verboseDebugging)
01535 LOG(VB_COMMFLAG, LOG_DEBUG,
01536 " blanks > frames * 0.95 && frames < 2*fps && "
01537 "lastScore < 0 && nextScore < 0, setting -10");
01538 fbp->score -= 10;
01539 }
01540
01541 if ((fbp->frames < (120*fps)) &&
01542 (lastScore < 0) &&
01543 (fbp->score > 0) &&
01544 (fbp->score < 20) &&
01545 (nextScore < 0))
01546 {
01547 if (verboseDebugging)
01548 LOG(VB_COMMFLAG, LOG_DEBUG,
01549 " frames < 120 * fps && (-20 < lastScore < 0) && "
01550 "thisScore > 0 && nextScore < 0, setting score = -10");
01551 fbp->score = -10;
01552 }
01553
01554 if ((fbp->frames < (30*fps)) &&
01555 (lastScore > 0) &&
01556 (fbp->score < 0) &&
01557 (fbp->score > -20) &&
01558 (nextScore > 0))
01559 {
01560 if (verboseDebugging)
01561 LOG(VB_COMMFLAG, LOG_DEBUG,
01562 " frames < 30 * fps && (0 < lastScore < 20) && "
01563 "thisScore < 0 && nextScore > 0, setting score = 10");
01564 fbp->score = 10;
01565 }
01566 }
01567
01568 if ((fbp->score == 0) && (lastScore > 30))
01569 {
01570 int offset = 1;
01571 while(((curBlock + offset) <= maxBlock) &&
01572 (fblock[curBlock + offset].frames < (2 * fps)) &&
01573 (fblock[curBlock + offset].score == 0))
01574 offset++;
01575
01576 if ((curBlock + offset) <= maxBlock)
01577 {
01578 offset--;
01579 if (fblock[curBlock + offset + 1].score > 0)
01580 {
01581 for (; offset >= 0; offset--)
01582 {
01583 fblock[curBlock + offset].score += 10;
01584 if (verboseDebugging)
01585 LOG(VB_COMMFLAG, LOG_DEBUG,
01586 QString(" Setting block %1 score +10")
01587 .arg(curBlock+offset));
01588 }
01589 }
01590 else if (fblock[curBlock + offset + 1].score < 0)
01591 {
01592 for (; offset >= 0; offset--)
01593 {
01594 fblock[curBlock + offset].score -= 10;
01595 if (verboseDebugging)
01596 LOG(VB_COMMFLAG, LOG_DEBUG,
01597 QString(" Setting block %1 score -10")
01598 .arg(curBlock+offset));
01599 }
01600 }
01601 }
01602 }
01603
01604 msg.sprintf(" NOW %3d:%02d %6ld %6ld %6ld %7.2f %3d %6d %6d %6d "
01605 "%5.2f %6d %6d %5d",
01606 (int)(fbp->start / fps) / 60,
01607 (int)((fbp->start / fps )) % 60,
01608 fbp->start, fbp->end, fbp->frames, fbp->length,
01609 fbp->bfCount, fbp->logoCount, fbp->ratingCount,
01610 fbp->scCount, fbp->scRate, fbp->formatMatch,
01611 fbp->aspectMatch, fbp->score);
01612 LOG(VB_COMMFLAG, LOG_DEBUG, msg);
01613
01614 lastScore = fbp->score;
01615 curBlock++;
01616 }
01617
01618 LOG(VB_COMMFLAG, LOG_DEBUG, "============================================");
01619 LOG(VB_COMMFLAG, LOG_INFO, "FINAL Block stats");
01620 LOG(VB_COMMFLAG, LOG_DEBUG,
01621 "Block StTime StFrm EndFrm Frames Secs "
01622 "Bf Lg Cnt RT Cnt SC Cnt SC Rt FmtMch AspMch Score");
01623 LOG(VB_COMMFLAG, LOG_DEBUG,
01624 "----- ------ ------ ------ ------ ------- "
01625 "--- ------ ------ ------ ----- ------ ------ -----");
01626 curBlock = 0;
01627 lastScore = 0;
01628 breakStart = -1;
01629 while (curBlock <= maxBlock)
01630 {
01631 fbp = &fblock[curBlock];
01632 thisScore = fbp->score;
01633
01634 if ((breakStart >= 0) &&
01635 ((fbp->end - breakStart) > (commDetectMaxCommBreakLength * fps)))
01636 {
01637 if (((fbp->start - breakStart) >
01638 (commDetectMinCommBreakLength * fps)) ||
01639 (breakStart == 0))
01640 {
01641 if (verboseDebugging)
01642 LOG(VB_COMMFLAG, LOG_DEBUG,
01643 QString("Closing commercial block at start of "
01644 "frame block %1 with length %2, frame "
01645 "block length of %3 frames would put comm "
01646 "block length over max of %4 seconds.")
01647 .arg(curBlock).arg(fbp->start - breakStart)
01648 .arg(fbp->frames)
01649 .arg(commDetectMaxCommBreakLength));
01650
01651 commBreakMap[breakStart] = MARK_COMM_START;
01652 commBreakMap[fbp->start] = MARK_COMM_END;
01653 lastStart = breakStart;
01654 lastEnd = fbp->start;
01655 breakStart = -1;
01656 }
01657 else
01658 {
01659 if (verboseDebugging)
01660 LOG(VB_COMMFLAG, LOG_DEBUG,
01661 QString("Ignoring what appears to be commercial"
01662 " block at frame %1 with length %2, "
01663 "length of %3 frames would put comm "
01664 "block length under min of %4 seconds.")
01665 .arg(breakStart)
01666 .arg(fbp->start - breakStart)
01667 .arg(fbp->frames)
01668 .arg(commDetectMinCommBreakLength));
01669 breakStart = -1;
01670 }
01671 }
01672 if (thisScore == 0)
01673 {
01674 thisScore = lastScore;
01675 }
01676 else if (thisScore < 0)
01677 {
01678 if ((lastScore > 0) || (curBlock == 0))
01679 {
01680 if ((fbp->start - lastEnd) < (commDetectMinShowLength * fps))
01681 {
01682 commBreakMap.remove(lastStart);
01683 commBreakMap.remove(lastEnd);
01684 breakStart = lastStart;
01685
01686 if (verboseDebugging)
01687 {
01688 if (breakStart)
01689 LOG(VB_COMMFLAG, LOG_DEBUG,
01690 QString("ReOpening commercial block at "
01691 "frame %1 because show less than "
01692 "%2 seconds")
01693 .arg(breakStart)
01694 .arg(commDetectMinShowLength));
01695 else
01696 LOG(VB_COMMFLAG, LOG_DEBUG,
01697 "Opening initial commercial block "
01698 "at start of recording, block 0.");
01699 }
01700 }
01701 else
01702 {
01703 breakStart = fbp->start;
01704
01705 if (verboseDebugging)
01706 LOG(VB_COMMFLAG, LOG_DEBUG,
01707 QString("Starting new commercial block at "
01708 "frame %1 from start of frame block %2")
01709 .arg(fbp->start).arg(curBlock));
01710 }
01711 }
01712 else if (curBlock == maxBlock)
01713 {
01714 if ((fbp->end - breakStart) >
01715 (commDetectMinCommBreakLength * fps))
01716 {
01717 if (fbp->end <=
01718 ((int64_t)framesProcessed - (int64_t)(2 * fps) - 2))
01719 {
01720 if (verboseDebugging)
01721 LOG(VB_COMMFLAG, LOG_DEBUG,
01722 QString("Closing final commercial block at "
01723 "frame %1").arg(fbp->end));
01724
01725 commBreakMap[breakStart] = MARK_COMM_START;
01726 commBreakMap[fbp->end] = MARK_COMM_END;
01727 lastStart = breakStart;
01728 lastEnd = fbp->end;
01729 breakStart = -1;
01730 }
01731 }
01732 else
01733 {
01734 if (verboseDebugging)
01735 LOG(VB_COMMFLAG, LOG_DEBUG,
01736 QString("Ignoring what appears to be commercial"
01737 " block at frame %1 with length %2, "
01738 "length of %3 frames would put comm "
01739 "block length under min of %4 seconds.")
01740 .arg(breakStart)
01741 .arg(fbp->start - breakStart)
01742 .arg(fbp->frames)
01743 .arg(commDetectMinCommBreakLength));
01744 breakStart = -1;
01745 }
01746 }
01747 }
01748 else if ((thisScore > 0) &&
01749 (lastScore < 0) &&
01750 (breakStart != -1))
01751 {
01752 if (((fbp->start - breakStart) >
01753 (commDetectMinCommBreakLength * fps)) ||
01754 (breakStart == 0))
01755 {
01756 commBreakMap[breakStart] = MARK_COMM_START;
01757 commBreakMap[fbp->start] = MARK_COMM_END;
01758 lastStart = breakStart;
01759 lastEnd = fbp->start;
01760
01761 if (verboseDebugging)
01762 LOG(VB_COMMFLAG, LOG_DEBUG,
01763 QString("Closing commercial block at frame %1")
01764 .arg(fbp->start));
01765 }
01766 else
01767 {
01768 if (verboseDebugging)
01769 LOG(VB_COMMFLAG, LOG_DEBUG,
01770 QString("Ignoring what appears to be commercial "
01771 "block at frame %1 with length %2, "
01772 "length of %3 frames would put comm block "
01773 "length under min of %4 seconds.")
01774 .arg(breakStart)
01775 .arg(fbp->start - breakStart)
01776 .arg(fbp->frames)
01777 .arg(commDetectMinCommBreakLength));
01778 }
01779 breakStart = -1;
01780 }
01781
01782 msg.sprintf("%5d %3d:%02d %6ld %6ld %6ld %7.2f %3d %6d %6d %6d "
01783 "%5.2f %6d %6d %5d",
01784 curBlock, (int)(fbp->start / fps) / 60,
01785 (int)((fbp->start / fps )) % 60,
01786 fbp->start, fbp->end, fbp->frames, fbp->length,
01787 fbp->bfCount, fbp->logoCount, fbp->ratingCount,
01788 fbp->scCount, fbp->scRate, fbp->formatMatch,
01789 fbp->aspectMatch, thisScore);
01790 LOG(VB_COMMFLAG, LOG_DEBUG, msg);
01791
01792 lastScore = thisScore;
01793 curBlock++;
01794 }
01795
01796 if ((breakStart != -1) &&
01797 (breakStart <= ((int64_t)framesProcessed - (int64_t)(2 * fps) - 2)))
01798 {
01799 if (verboseDebugging)
01800 LOG(VB_COMMFLAG, LOG_DEBUG,
01801 QString("Closing final commercial block started at "
01802 "block %1 and going to end of program. length "
01803 "is %2 frames")
01804 .arg(curBlock)
01805 .arg((framesProcessed - breakStart - 1)));
01806
01807 commBreakMap[breakStart] = MARK_COMM_START;
01808
01809
01810
01811 commBreakMap[framesProcessed + (10 * fps)] = MARK_COMM_END;
01812 }
01813
01814
01815 tmpCommMap = commBreakMap;
01816 commBreakMap.clear();
01817
01818 if (verboseDebugging)
01819 LOG(VB_COMMFLAG, LOG_DEBUG,
01820 "Adjusting start/end marks according to blanks.");
01821 for (it = tmpCommMap.begin(); it != tmpCommMap.end(); ++it)
01822 {
01823 if (*it == MARK_COMM_START)
01824 {
01825 uint64_t lastStartLower = it.key();
01826 uint64_t lastStartUpper = it.key();
01827 while ((lastStartLower > 0) &&
01828 (frameInfo[lastStartLower - 1].flagMask & COMM_FRAME_BLANK))
01829 lastStartLower--;
01830 while ((lastStartUpper < (framesProcessed - (2 * fps))) &&
01831 (frameInfo[lastStartUpper + 1].flagMask & COMM_FRAME_BLANK))
01832 lastStartUpper++;
01833 uint64_t adj = (lastStartUpper - lastStartLower) / 2;
01834 if (adj > MAX_BLANK_FRAMES)
01835 adj = MAX_BLANK_FRAMES;
01836 lastStart = lastStartLower + adj;
01837
01838 if (verboseDebugging)
01839 LOG(VB_COMMFLAG, LOG_DEBUG, QString("Start Mark: %1 -> %2")
01840 .arg(it.key()).arg(lastStart));
01841
01842 commBreakMap[lastStart] = MARK_COMM_START;
01843 }
01844 else
01845 {
01846 uint64_t lastEndLower = it.key();
01847 uint64_t lastEndUpper = it.key();
01848 while ((lastEndUpper < (framesProcessed - (2 * fps))) &&
01849 (frameInfo[lastEndUpper + 1].flagMask & COMM_FRAME_BLANK))
01850 lastEndUpper++;
01851 while ((lastEndLower > 0) &&
01852 (frameInfo[lastEndLower - 1].flagMask & COMM_FRAME_BLANK))
01853 lastEndLower--;
01854 uint64_t adj = (lastEndUpper - lastEndLower) / 2;
01855 if (adj > MAX_BLANK_FRAMES)
01856 adj = MAX_BLANK_FRAMES;
01857 lastEnd = lastEndUpper - adj;
01858
01859 if (verboseDebugging)
01860 LOG(VB_COMMFLAG, LOG_DEBUG, QString("End Mark : %1 -> %2")
01861 .arg(it.key()).arg(lastEnd));
01862
01863 commBreakMap[lastEnd] = MARK_COMM_END;
01864 }
01865 }
01866
01867 delete [] fblock;
01868 }
01869
01870
01871 void ClassicCommDetector::BuildBlankFrameCommList(void)
01872 {
01873 LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::BuildBlankFrameCommList()");
01874
01875 long long *bframes = new long long[blankFrameMap.count()*2];
01876 long long *c_start = new long long[blankFrameMap.count()];
01877 long long *c_end = new long long[blankFrameMap.count()];
01878 int frames = 0;
01879 int commercials = 0;
01880 int i, x;
01881 frm_dir_map_t::iterator it;
01882
01883 blankCommMap.clear();
01884
01885 for (it = blankFrameMap.begin(); it != blankFrameMap.end(); ++it)
01886 bframes[frames++] = it.key();
01887
01888 if (frames == 0)
01889 {
01890 delete[] c_start;
01891 delete[] c_end;
01892 delete[] bframes;
01893 return;
01894 }
01895
01896
01897
01898
01899 for(i = 0; i < frames; i++ )
01900 {
01901 for(x=i+1; x < frames; x++ )
01902 {
01903
01904
01905
01906 int gap_length = bframes[x] - bframes[i];
01907 if (((aggressiveDetection) &&
01908 ((abs((int)(gap_length - (5 * fps))) < 5 ) ||
01909 (abs((int)(gap_length - (10 * fps))) < 7 ) ||
01910 (abs((int)(gap_length - (15 * fps))) < 10 ) ||
01911 (abs((int)(gap_length - (20 * fps))) < 11 ) ||
01912 (abs((int)(gap_length - (30 * fps))) < 12 ) ||
01913 (abs((int)(gap_length - (40 * fps))) < 1 ) ||
01914 (abs((int)(gap_length - (45 * fps))) < 1 ) ||
01915 (abs((int)(gap_length - (60 * fps))) < 15 ) ||
01916 (abs((int)(gap_length - (90 * fps))) < 10 ) ||
01917 (abs((int)(gap_length - (120 * fps))) < 10 ))) ||
01918 ((!aggressiveDetection) &&
01919 ((abs((int)(gap_length - (5 * fps))) < 11 ) ||
01920 (abs((int)(gap_length - (10 * fps))) < 13 ) ||
01921 (abs((int)(gap_length - (15 * fps))) < 16 ) ||
01922 (abs((int)(gap_length - (20 * fps))) < 17 ) ||
01923 (abs((int)(gap_length - (30 * fps))) < 18 ) ||
01924 (abs((int)(gap_length - (40 * fps))) < 3 ) ||
01925 (abs((int)(gap_length - (45 * fps))) < 3 ) ||
01926 (abs((int)(gap_length - (60 * fps))) < 20 ) ||
01927 (abs((int)(gap_length - (90 * fps))) < 20 ) ||
01928 (abs((int)(gap_length - (120 * fps))) < 20 ))))
01929 {
01930 c_start[commercials] = bframes[i];
01931 c_end[commercials] = bframes[x] - 1;
01932 commercials++;
01933 i = x-1;
01934 x = frames;
01935 }
01936
01937 if ((!aggressiveDetection) &&
01938 ((abs((int)(gap_length - (30 * fps))) < (int)(fps * 0.85)) ||
01939 (abs((int)(gap_length - (60 * fps))) < (int)(fps * 0.95)) ||
01940 (abs((int)(gap_length - (90 * fps))) < (int)(fps * 1.05)) ||
01941 (abs((int)(gap_length - (120 * fps))) < (int)(fps * 1.15))) &&
01942 ((x + 2) < frames) &&
01943 ((i + 2) < frames) &&
01944 ((bframes[i] + 1) == bframes[i+1]) &&
01945 ((bframes[x] + 1) == bframes[x+1]))
01946 {
01947 c_start[commercials] = bframes[i];
01948 c_end[commercials] = bframes[x];
01949 commercials++;
01950 i = x;
01951 x = frames;
01952 }
01953 }
01954 }
01955
01956 i = 0;
01957
01958
01959
01960 if ((commercials > 1) &&
01961 (c_end[0] < (33 * fps)) &&
01962 (c_start[1] > (c_end[0] + 40 * fps)))
01963 i = 1;
01964
01965
01966 bool first_comm = true;
01967 for(; i < (commercials-1); i++)
01968 {
01969 long long r = c_start[i];
01970 long long adjustment = 0;
01971
01972 if ((r < (30 * fps)) &&
01973 (first_comm))
01974 r = 1;
01975
01976 blankCommMap[r] = MARK_COMM_START;
01977
01978 r = c_end[i];
01979 if ( i < (commercials-1))
01980 {
01981 for(x = 0; x < (frames-1); x++)
01982 if (bframes[x] == r)
01983 break;
01984 while((x < (frames-1)) &&
01985 ((bframes[x] + 1 ) == bframes[x+1]) &&
01986 (bframes[x+1] < c_start[i+1]))
01987 {
01988 r++;
01989 x++;
01990 }
01991
01992 while((blankFrameMap.contains(r+1)) &&
01993 (c_start[i+1] != (r+1)))
01994 {
01995 r++;
01996 adjustment++;
01997 }
01998 }
01999 else
02000 {
02001 while(blankFrameMap.contains(r+1))
02002 {
02003 r++;
02004 adjustment++;
02005 }
02006 }
02007
02008 adjustment /= 2;
02009 if (adjustment > MAX_BLANK_FRAMES)
02010 adjustment = MAX_BLANK_FRAMES;
02011 r -= adjustment;
02012 blankCommMap[r] = MARK_COMM_END;
02013 first_comm = false;
02014 }
02015
02016 blankCommMap[c_start[i]] = MARK_COMM_START;
02017 blankCommMap[c_end[i]] = MARK_COMM_END;
02018
02019 delete[] c_start;
02020 delete[] c_end;
02021 delete[] bframes;
02022
02023 LOG(VB_COMMFLAG, LOG_INFO, "Blank-Frame Commercial Map" );
02024 for(it = blankCommMap.begin(); it != blankCommMap.end(); ++it)
02025 LOG(VB_COMMFLAG, LOG_INFO, QString(" %1:%2")
02026 .arg(it.key()).arg(*it));
02027
02028 MergeBlankCommList();
02029
02030 LOG(VB_COMMFLAG, LOG_INFO, "Merged Blank-Frame Commercial Break Map" );
02031 for(it = blankCommBreakMap.begin(); it != blankCommBreakMap.end(); ++it)
02032 LOG(VB_COMMFLAG, LOG_INFO, QString(" %1:%2")
02033 .arg(it.key()).arg(*it));
02034 }
02035
02036
02037 void ClassicCommDetector::BuildSceneChangeCommList(void)
02038 {
02039 int section_start = -1;
02040 int seconds = (int)(framesProcessed / fps);
02041 int *sc_histogram = new int[seconds+1];
02042
02043 sceneCommBreakMap.clear();
02044
02045 memset(sc_histogram, 0, (seconds+1)*sizeof(int));
02046 for (uint64_t f = 1; f <= framesProcessed; f++)
02047 {
02048 if (sceneMap.contains(f))
02049 sc_histogram[(uint64_t)(f / fps)]++;
02050 }
02051
02052 for(long long s = 0; s < (seconds + 1); s++)
02053 {
02054 if (sc_histogram[s] > 2)
02055 {
02056 if (section_start == -1)
02057 {
02058 long long f = (long long)(s * fps);
02059 for(int i = 0; i < fps; i++, f++)
02060 {
02061 if (sceneMap.contains(f))
02062 {
02063 sceneCommBreakMap[f] = MARK_COMM_START;
02064 i = (int)(fps) + 1;
02065 }
02066 }
02067 }
02068
02069 section_start = s;
02070 }
02071
02072 if ((section_start >= 0) &&
02073 (s > (section_start + 32)))
02074 {
02075 long long f = (long long)(section_start * fps);
02076 bool found_end = false;
02077
02078 for(int i = 0; i < fps; i++, f++)
02079 {
02080 if (sceneMap.contains(f))
02081 {
02082 frm_dir_map_t::iterator dit = sceneCommBreakMap.find(f);
02083 if (dit != sceneCommBreakMap.end())
02084 sceneCommBreakMap.erase(dit);
02085 else
02086 sceneCommBreakMap[f] = MARK_COMM_END;
02087 i = (int)(fps) + 1;
02088 found_end = true;
02089 }
02090 }
02091 section_start = -1;
02092
02093 if (!found_end)
02094 {
02095 f = (long long)(section_start * fps);
02096 sceneCommBreakMap[f] = MARK_COMM_END;
02097 }
02098 }
02099 }
02100 delete[] sc_histogram;
02101
02102 if (section_start >= 0)
02103 sceneCommBreakMap[framesProcessed] = MARK_COMM_END;
02104
02105 frm_dir_map_t deleteMap;
02106 frm_dir_map_t::iterator it = sceneCommBreakMap.begin();
02107 frm_dir_map_t::iterator prev = it;
02108 if (it != sceneCommBreakMap.end())
02109 {
02110 ++it;
02111 while (it != sceneCommBreakMap.end())
02112 {
02113 if ((*it == MARK_COMM_END) &&
02114 (it.key() - prev.key()) < (30 * fps))
02115 {
02116 deleteMap[it.key()] = MARK_CUT_START;
02117 deleteMap[prev.key()] = MARK_CUT_START;
02118 }
02119 ++prev;
02120 if (it != sceneCommBreakMap.end())
02121 ++it;
02122 }
02123
02124 frm_dir_map_t::iterator dit;
02125 for (dit = deleteMap.begin(); dit != deleteMap.end(); ++dit)
02126 sceneCommBreakMap.remove(dit.key());
02127 }
02128
02129 LOG(VB_COMMFLAG, LOG_INFO, "Scene-Change Commercial Break Map" );
02130 for (it = sceneCommBreakMap.begin(); it != sceneCommBreakMap.end(); ++it)
02131 {
02132 LOG(VB_COMMFLAG, LOG_INFO, QString(" %1:%2")
02133 .arg(it.key()).arg(*it));
02134 }
02135 }
02136
02137
02138 void ClassicCommDetector::BuildLogoCommList()
02139 {
02140 show_map_t showmap;
02141 GetLogoCommBreakMap(showmap);
02142 CondenseMarkMap(showmap, (int)(25 * fps), (int)(30 * fps));
02143 ConvertShowMapToCommMap(logoCommBreakMap, showmap);
02144
02145 frm_dir_map_t::iterator it;
02146 LOG(VB_COMMFLAG, LOG_INFO, "Logo Commercial Break Map" );
02147 for(it = logoCommBreakMap.begin(); it != logoCommBreakMap.end(); ++it)
02148 LOG(VB_COMMFLAG, LOG_INFO, QString(" %1:%2")
02149 .arg(it.key()).arg(*it));
02150 }
02151
02152 void ClassicCommDetector::MergeBlankCommList(void)
02153 {
02154 frm_dir_map_t::iterator it;
02155 frm_dir_map_t::iterator prev;
02156 QMap<long long, long long> tmpMap;
02157 QMap<long long, long long>::Iterator tmpMap_it;
02158 QMap<long long, long long>::Iterator tmpMap_prev;
02159
02160 blankCommBreakMap.clear();
02161
02162 if (blankCommMap.isEmpty())
02163 return;
02164
02165 for (it = blankCommMap.begin(); it != blankCommMap.end(); ++it)
02166 blankCommBreakMap[it.key()] = *it;
02167
02168 if (blankCommBreakMap.isEmpty())
02169 return;
02170
02171 it = blankCommMap.begin();
02172 prev = it;
02173 ++it;
02174 for(; it != blankCommMap.end(); ++it, ++prev)
02175 {
02176
02177 if ((((prev.key() + 1) == it.key()) ||
02178 ((prev.key() + (15 * fps)) > it.key())) &&
02179 (*prev == MARK_COMM_END) &&
02180 (*it == MARK_COMM_START))
02181 {
02182 blankCommBreakMap.remove(prev.key());
02183 blankCommBreakMap.remove(it.key());
02184 }
02185 }
02186
02187
02188
02189 it = blankCommBreakMap.begin();
02190 prev = it;
02191 ++it;
02192 tmpMap[prev.key()] = it.key();
02193 for(; it != blankCommBreakMap.end(); ++it, ++prev)
02194 {
02195 if ((*prev == MARK_COMM_START) &&
02196 (*it == MARK_COMM_END))
02197 tmpMap[prev.key()] = it.key();
02198 }
02199
02200 tmpMap_it = tmpMap.begin();
02201 tmpMap_prev = tmpMap_it;
02202 tmpMap_it++;
02203 for(; tmpMap_it != tmpMap.end(); ++tmpMap_it, ++tmpMap_prev)
02204 {
02205
02206
02207 if (((*tmpMap_prev + (35 * fps)) > tmpMap_it.key()) &&
02208 ((*tmpMap_prev - tmpMap_prev.key()) > (35 * fps)) &&
02209 ((*tmpMap_it - tmpMap_it.key()) > (35 * fps)))
02210 {
02211 blankCommBreakMap.remove(*tmpMap_prev);
02212 blankCommBreakMap.remove(tmpMap_it.key());
02213 }
02214 }
02215 }
02216
02217 bool ClassicCommDetector::FrameIsInBreakMap(
02218 uint64_t f, const frm_dir_map_t &breakMap) const
02219 {
02220 for (uint64_t i = f; i < framesProcessed; i++)
02221 {
02222 if (breakMap.contains(i))
02223 {
02224 int type = breakMap[i];
02225 if ((type == MARK_COMM_END) || (i == f))
02226 return true;
02227 if (type == MARK_COMM_START)
02228 return false;
02229 }
02230 }
02231
02232
02233
02234 for (uint64_t i = (f + 1); i-- > 0; )
02235 {
02236 if (breakMap.contains(i))
02237 {
02238 int type = breakMap[i];
02239 if ((type == MARK_COMM_START) || (i == f))
02240 return true;
02241 if (type == MARK_COMM_END)
02242 return false;
02243 }
02244 }
02245
02246 return false;
02247 }
02248
02249 void ClassicCommDetector::DumpMap(frm_dir_map_t &map)
02250 {
02251 frm_dir_map_t::iterator it;
02252 QString msg;
02253
02254 LOG(VB_COMMFLAG, LOG_INFO,
02255 "---------------------------------------------------");
02256 for (it = map.begin(); it != map.end(); ++it)
02257 {
02258 long long frame = it.key();
02259 int flag = *it;
02260 int my_fps = (int)ceil(fps);
02261 int hour = (frame / my_fps) / 60 / 60;
02262 int min = (frame / my_fps) / 60 - (hour * 60);
02263 int sec = (frame / my_fps) - (min * 60) - (hour * 60 * 60);
02264 int frm = frame - ((sec * my_fps) + (min * 60 * my_fps) +
02265 (hour * 60 * 60 * my_fps));
02266 int my_sec = (int)(frame / my_fps);
02267 msg.sprintf("%7ld : %d (%02d:%02d:%02d.%02d) (%d)",
02268 (long)frame, flag, hour, min, sec, frm, my_sec);
02269 LOG(VB_COMMFLAG, LOG_INFO, msg);
02270 }
02271 LOG(VB_COMMFLAG, LOG_INFO,
02272 "---------------------------------------------------");
02273 }
02274
02275 void ClassicCommDetector::CondenseMarkMap(show_map_t &map, int spacing,
02276 int length)
02277 {
02278 show_map_t::iterator it;
02279 show_map_t::iterator prev;
02280 show_map_t tmpMap;
02281
02282 if (map.size() <= 2)
02283 return;
02284
02285
02286 LOG(VB_COMMFLAG, LOG_INFO, "Commercial Map Before condense:" );
02287 for (it = map.begin(); it != map.end(); ++it)
02288 {
02289 LOG(VB_COMMFLAG, LOG_INFO, QString(" %1:%2")
02290 .arg(it.key()).arg(*it));
02291 tmpMap[it.key()] = *it;
02292 }
02293
02294 prev = tmpMap.begin();
02295 it = prev;
02296 ++it;
02297 while (it != tmpMap.end())
02298 {
02299 if ((*it == MARK_START) &&
02300 (*prev == MARK_END) &&
02301 ((it.key() - prev.key()) < (uint64_t)spacing))
02302 {
02303 map.remove(prev.key());
02304 map.remove(it.key());
02305 }
02306 ++prev;
02307 ++it;
02308 }
02309
02310 if (map.size() == 0)
02311 return;
02312
02313
02314 tmpMap.clear();
02315 for (it = map.begin(); it != map.end(); ++it)
02316 tmpMap[it.key()] = *it;
02317
02318 prev = tmpMap.begin();
02319 it = prev;
02320 ++it;
02321 while (it != tmpMap.end())
02322 {
02323 if ((*prev == MARK_START) &&
02324 (*it == MARK_END) &&
02325 ((it.key() - prev.key()) < (uint64_t)length))
02326 {
02327 map.remove(prev.key());
02328 map.remove(it.key());
02329 }
02330 ++prev;
02331 ++it;
02332 }
02333
02334 LOG(VB_COMMFLAG, LOG_INFO, "Commercial Map After condense:" );
02335 for (it = map.begin(); it != map.end(); ++it)
02336 LOG(VB_COMMFLAG, LOG_INFO, QString(" %1:%2")
02337 .arg(it.key()).arg(*it));
02338 }
02339
02340 void ClassicCommDetector::ConvertShowMapToCommMap(
02341 frm_dir_map_t &out, const show_map_t &in)
02342 {
02343 out.clear();
02344 if (in.empty())
02345 return;
02346
02347 show_map_t::const_iterator sit;
02348 for (sit = in.begin(); sit != in.end(); ++sit)
02349 {
02350 if (*sit == MARK_START)
02351 out[sit.key()] = MARK_COMM_END;
02352 else
02353 out[sit.key()] = MARK_COMM_START;
02354 }
02355
02356 frm_dir_map_t::iterator it = out.begin();
02357 if (it == out.end())
02358 return;
02359
02360 switch (out[it.key()])
02361 {
02362 case MARK_COMM_END:
02363 if (it.key() == 0)
02364 out.remove(0);
02365 else
02366 out[0] = MARK_COMM_START;
02367 break;
02368 case MARK_COMM_START:
02369 break;
02370 default:
02371 out.remove(0);
02372 break;
02373 }
02374 }
02375
02376
02377
02378
02379
02380
02381 void ClassicCommDetector::CleanupFrameInfo(void)
02382 {
02383 LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::CleanupFrameInfo()");
02384
02385 int value;
02386 int before, after;
02387
02388
02389 if ((framesProcessed > (fps * 60)) &&
02390 (blankFrameCount < (framesProcessed * 0.0004)))
02391 {
02392 int avgHistogram[256];
02393 int minAvg = -1;
02394 int newThreshold = -1;
02395
02396 LOG(VB_COMMFLAG, LOG_INFO,
02397 QString("ClassicCommDetect: Only found %1 blank frames but "
02398 "wanted at least %2, rechecking data using higher "
02399 "threshold.")
02400 .arg(blankFrameCount)
02401 .arg((int)(framesProcessed * 0.0004)));
02402 blankFrameMap.clear();
02403 blankFrameCount = 0;
02404
02405 memset(avgHistogram, 0, sizeof(avgHistogram));
02406
02407 for (uint64_t i = 1; i <= framesProcessed; i++)
02408 avgHistogram[clamp(frameInfo[i].avgBrightness, 0, 255)] += 1;
02409
02410 for (int i = 1; i <= 255 && minAvg == -1; i++)
02411 if (avgHistogram[i] > (framesProcessed * 0.0004))
02412 minAvg = i;
02413
02414 newThreshold = minAvg + 3;
02415 LOG(VB_COMMFLAG, LOG_INFO,
02416 QString("Minimum Average Brightness on a frame "
02417 "was %1, will use %2 as new threshold")
02418 .arg(minAvg).arg(newThreshold));
02419
02420 for (uint64_t i = 1; i <= framesProcessed; i++)
02421 {
02422 value = frameInfo[i].flagMask;
02423 frameInfo[i].flagMask = value & ~COMM_FRAME_BLANK;
02424
02425 if (( !(frameInfo[i].flagMask & COMM_FRAME_BLANK)) &&
02426 (frameInfo[i].avgBrightness < newThreshold))
02427 {
02428 frameInfo[i].flagMask = value | COMM_FRAME_BLANK;
02429 blankFrameMap[i] = MARK_BLANK_FRAME;
02430 blankFrameCount++;
02431 }
02432 }
02433
02434 LOG(VB_COMMFLAG, LOG_INFO,
02435 QString("Found %1 blank frames using new value")
02436 .arg(blankFrameCount));
02437 }
02438
02439
02440 for (uint64_t i = 1; i <= framesProcessed; i++)
02441 {
02442 if ((i < 10) || ((i+10) > framesProcessed))
02443 continue;
02444
02445 before = 0;
02446 for (int offset = 1; offset <= 10; offset++)
02447 if (frameInfo[i - offset].flagMask & COMM_FRAME_LOGO_PRESENT)
02448 before++;
02449
02450 after = 0;
02451 for (int offset = 1; offset <= 10; offset++)
02452 if (frameInfo[i + offset].flagMask & COMM_FRAME_LOGO_PRESENT)
02453 after++;
02454
02455 value = frameInfo[i].flagMask;
02456 if (value == -1)
02457 frameInfo[i].flagMask = 0;
02458
02459 if (value & COMM_FRAME_LOGO_PRESENT)
02460 {
02461 if ((before < 4) && (after < 4))
02462 frameInfo[i].flagMask = value & ~COMM_FRAME_LOGO_PRESENT;
02463 }
02464 else
02465 {
02466 if ((before > 6) && (after > 6))
02467 frameInfo[i].flagMask = value | COMM_FRAME_LOGO_PRESENT;
02468 }
02469 }
02470 }
02471
02472 void ClassicCommDetector::GetLogoCommBreakMap(show_map_t &map)
02473 {
02474 LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetLogoCommBreakMap()");
02475
02476 map.clear();
02477
02478 bool PrevFrameLogo = false;
02479
02480 for (uint64_t curFrame = 1 ; curFrame <= framesProcessed; curFrame++)
02481 {
02482 bool CurrentFrameLogo =
02483 (frameInfo[curFrame].flagMask & COMM_FRAME_LOGO_PRESENT);
02484
02485 if (!PrevFrameLogo && CurrentFrameLogo)
02486 map[curFrame] = MARK_START;
02487 else if (PrevFrameLogo && !CurrentFrameLogo)
02488 map[curFrame] = MARK_END;
02489
02490 PrevFrameLogo = CurrentFrameLogo;
02491 }
02492 }
02493
02494 void ClassicCommDetector::logoDetectorBreathe()
02495 {
02496 emit breathe();
02497 }
02498
02499 void ClassicCommDetector::PrintFullMap(
02500 ostream &out, const frm_dir_map_t *comm_breaks, bool verbose) const
02501 {
02502 if (verbose)
02503 {
02504 QByteArray tmp = FrameInfoEntry::GetHeader().toAscii();
02505 out << tmp.constData() << " mark" << endl;
02506 }
02507
02508 for (long long i = 1; i < curFrameNumber; i++)
02509 {
02510 QMap<long long, FrameInfoEntry>::const_iterator it = frameInfo.find(i);
02511 if (it == frameInfo.end())
02512 continue;
02513
02514 QByteArray atmp = (*it).toString(i, verbose).toAscii();
02515 out << atmp.constData() << " ";
02516 if (comm_breaks)
02517 {
02518 frm_dir_map_t::const_iterator mit = comm_breaks->find(i);
02519 if (mit != comm_breaks->end())
02520 {
02521 QString tmp = (verbose) ?
02522 toString((MarkTypes)*mit) : QString::number(*mit);
02523 atmp = tmp.toAscii();
02524
02525 out << atmp.constData();
02526 }
02527 }
02528 out << "\n";
02529 }
02530
02531 out << flush;
02532 }
02533
02534