| | 1 | // POSIX headers |
| | 2 | #include <unistd.h> |
| | 3 | #include <sys/time.h> // for gettimeofday |
| | 4 | |
| | 5 | // ANSI C headers |
| | 6 | #include <cmath> |
| | 7 | |
| | 8 | // C++ headers |
| | 9 | #include <algorithm> // for min/max |
| | 10 | #include <iostream> // for cerr |
| | 11 | using namespace std; |
| | 12 | |
| | 13 | // Qt headers |
| | 14 | #include <QString> |
| | 15 | |
| | 16 | // MythTV headers |
| | 17 | #include "mythcontext.h" |
| | 18 | #include "programinfo.h" |
| | 19 | #include "mythplayer.h" |
| | 20 | |
| | 21 | #define USE_SUBTITLES |
| | 22 | |
| | 23 | // Commercial Flagging headers |
| | 24 | #include "NextgenCommDetector.h" |
| | 25 | #include "AudioChangeDetector.h" |
| | 26 | #ifdef USE_SUBTITLES |
| | 27 | #include "SubtitleChangeDetector.h" |
| | 28 | #endif |
| | 29 | #include "NextgenLogoDetector.h" |
| | 30 | #include "NextgenSceneChangeDetector.h" |
| | 31 | #include "AudioBuffer.h" |
| | 32 | |
| | 33 | enum frameAspects { |
| | 34 | COMM_ASPECT_NORMAL = 0, |
| | 35 | COMM_ASPECT_WIDE |
| | 36 | }; |
| | 37 | |
| | 38 | enum frameFormats { |
| | 39 | COMM_FORMAT_NORMAL = 0, |
| | 40 | COMM_FORMAT_LETTERBOX, |
| | 41 | COMM_FORMAT_PILLARBOX, |
| | 42 | COMM_FORMAT_MAX |
| | 43 | }; |
| | 44 | |
| | 45 | static QString toStringFrameMaskValues(int mask, bool verbose) |
| | 46 | { |
| | 47 | QString msg; |
| | 48 | |
| | 49 | if (verbose) |
| | 50 | { |
| | 51 | if (COMM_FRAME_SKIPPED & mask) |
| | 52 | msg += "skipped,"; |
| | 53 | if (COMM_FRAME_BLANK & mask) |
| | 54 | msg += "blank,"; |
| | 55 | if (COMM_FRAME_SCENE_CHANGE & mask) |
| | 56 | msg += "scene,"; |
| | 57 | if (COMM_FRAME_LOGO_PRESENT & mask) |
| | 58 | msg += "logo,"; |
| | 59 | if (COMM_FRAME_ASPECT_CHANGE & mask) |
| | 60 | msg += "aspect,"; |
| | 61 | if (COMM_FRAME_RATING_SYMBOL & mask) |
| | 62 | msg += "rating,"; |
| | 63 | if (COMM_FRAME_NO_AUDIO & mask) |
| | 64 | msg += "silent,"; |
| | 65 | if (COMM_FRAME_AUDIO_CHANGE & mask) |
| | 66 | msg += "audchg,"; |
| | 67 | if (COMM_FRAME_INVALID_AUDIO & mask) |
| | 68 | msg += "noaud,"; |
| | 69 | if (COMM_FRAME_SUBTITLE_PRESENT & mask) |
| | 70 | msg += "sub,"; |
| | 71 | |
| | 72 | if (msg.length()) |
| | 73 | msg = msg.left(msg.length() - 1); |
| | 74 | else |
| | 75 | msg = "noflags"; |
| | 76 | } |
| | 77 | else |
| | 78 | { |
| | 79 | msg += (COMM_FRAME_SKIPPED & mask) ? "s" : " "; |
| | 80 | msg += (COMM_FRAME_BLANK & mask) ? "B" : " "; |
| | 81 | msg += (COMM_FRAME_SCENE_CHANGE & mask) ? "S" : " "; |
| | 82 | msg += (COMM_FRAME_LOGO_PRESENT & mask) ? "L" : " "; |
| | 83 | msg += (COMM_FRAME_ASPECT_CHANGE & mask) ? "A" : " "; |
| | 84 | msg += (COMM_FRAME_RATING_SYMBOL & mask) ? "R" : " "; |
| | 85 | msg += (COMM_FRAME_NO_AUDIO & mask) ? "Q" : " "; |
| | 86 | msg += (COMM_FRAME_AUDIO_CHANGE & mask) ? "C" : " "; |
| | 87 | msg += (COMM_FRAME_INVALID_AUDIO & mask) ? "I" : " "; |
| | 88 | msg += (COMM_FRAME_SUBTITLE_PRESENT & mask) ? "c" : " "; |
| | 89 | } |
| | 90 | |
| | 91 | return msg; |
| | 92 | } |
| | 93 | |
| | 94 | static QString toStringFrameAspects(int aspect, bool verbose) |
| | 95 | { |
| | 96 | if (verbose) |
| | 97 | return (COMM_ASPECT_NORMAL == aspect) ? "normal" : " wide "; |
| | 98 | else |
| | 99 | return (COMM_ASPECT_NORMAL == aspect) ? "n" : "w"; |
| | 100 | } |
| | 101 | |
| | 102 | static QString toStringFrameFormats(int format, bool verbose) |
| | 103 | { |
| | 104 | switch (format) |
| | 105 | { |
| | 106 | case COMM_FORMAT_NORMAL: |
| | 107 | return (verbose) ? "normal" : "N"; |
| | 108 | case COMM_FORMAT_LETTERBOX: |
| | 109 | return (verbose) ? "letter" : "L"; |
| | 110 | case COMM_FORMAT_PILLARBOX: |
| | 111 | return (verbose) ? "pillar" : "P"; |
| | 112 | case COMM_FORMAT_MAX: |
| | 113 | return (verbose) ? " max " : "M"; |
| | 114 | } |
| | 115 | |
| | 116 | return (verbose) ? " null " : "n"; |
| | 117 | } |
| | 118 | |
| | 119 | QString NGFrameInfoEntry::GetHeader(void) |
| | 120 | { |
| | 121 | return QString(" frame min/max/avg scene aspect format apwr achan flags"); |
| | 122 | } |
| | 123 | |
| | 124 | QString NGFrameInfoEntry::toString(uint64_t frame, bool verbose) const |
| | 125 | { |
| | 126 | return QString( |
| | 127 | "%1: %2/%3/%4 %5% %6 %7 %8 %9 %10") |
| | 128 | .arg(frame,10) |
| | 129 | .arg(minBrightness,3) |
| | 130 | .arg(maxBrightness,3) |
| | 131 | .arg(avgBrightness,3) |
| | 132 | .arg(sceneChangePercent,3) |
| | 133 | .arg(toStringFrameAspects(aspect, verbose)) |
| | 134 | .arg(toStringFrameFormats(format, verbose)) |
| | 135 | .arg(audioPower,8) |
| | 136 | .arg(audioMode,6) |
| | 137 | .arg(toStringFrameMaskValues(flagMask, verbose)); |
| | 138 | } |
| | 139 | |
| | 140 | NextgenCommDetector::NextgenCommDetector(SkipType commDetectMethod_in, |
| | 141 | bool showProgress_in, |
| | 142 | bool fullSpeed_in, |
| | 143 | MythPlayer* player_in, |
| | 144 | int chanid, |
| | 145 | const QDateTime& startedAt_in, |
| | 146 | const QDateTime& stopsAt_in, |
| | 147 | const QDateTime& recordingStartedAt_in, |
| | 148 | const QDateTime& recordingStopsAt_in) : |
| | 149 | |
| | 150 | |
| | 151 | commDetectMethod(commDetectMethod_in), |
| | 152 | commBreakMapUpdateRequested(false), sendCommBreakMapUpdates(false), |
| | 153 | verboseDebugging(false), |
| | 154 | lastFrameNumber(0), curFrameNumber(0), |
| | 155 | width(0), height(0), |
| | 156 | horizSpacing(0), vertSpacing(0), |
| | 157 | fpm(0.0), blankFramesOnly(false), |
| | 158 | blankFrameCount(0), currentAspect(0), |
| | 159 | silentFrameCount(0), |
| | 160 | totalMinBrightness(0), detectBlankFrames(false), |
| | 161 | detectSceneChanges(false), detectStationLogo(false), |
| | 162 | detectSilentFrames(false), |
| | 163 | subtitleInfoAvailable(false), subtitleFrameCount(0), |
| | 164 | logoInfoAvailable(false), logoDetector(0), |
| | 165 | framePtr(0), frameIsBlank(false), |
| | 166 | sceneHasChanged(false), stationLogoPresent(false), |
| | 167 | lastFrameWasBlank(false), lastFrameWasSceneChange(false), |
| | 168 | decoderFoundAspectChanges(false), sceneChangeDetector(0), |
| | 169 | audioChangeDetector(0), |
| | 170 | #ifdef USE_SUBTITLES |
| | 171 | subtitleChangeDetector(0), |
| | 172 | #endif |
| | 173 | player(player_in), |
| | 174 | startedAt(startedAt_in), stopsAt(stopsAt_in), |
| | 175 | recordingStartedAt(recordingStartedAt_in), |
| | 176 | recordingStopsAt(recordingStopsAt_in), aggressiveDetection(false), |
| | 177 | stillRecording(recordingStopsAt > QDateTime::currentDateTime()), |
| | 178 | fullSpeed(fullSpeed_in), showProgress(showProgress_in), |
| | 179 | fps(0.0), framesProcessed(0), |
| | 180 | preRoll(0), postRoll(0) |
| | 181 | { |
| | 182 | commDetectBorder = |
| | 183 | gCoreContext->GetNumSetting("CommDetectBorder", 20); |
| | 184 | commDetectBlankFrameMaxDiff = |
| | 185 | gCoreContext->GetNumSetting("CommDetectBlankFrameMaxDiff", 25); |
| | 186 | commDetectDarkBrightness = |
| | 187 | gCoreContext->GetNumSetting("CommDetectDarkBrightness", 80); |
| | 188 | commDetectDimBrightness = |
| | 189 | gCoreContext->GetNumSetting("CommDetectDimBrightness", 120); |
| | 190 | commDetectBoxBrightness = |
| | 191 | gCoreContext->GetNumSetting("CommDetectBoxBrightness", 30); |
| | 192 | commDetectDimAverage = |
| | 193 | gCoreContext->GetNumSetting("CommDetectDimAverage", 35); |
| | 194 | commDetectMaxCommBreakLength = |
| | 195 | gCoreContext->GetNumSetting("CommDetectMaxCommBreakLength", 395); |
| | 196 | commDetectMinCommBreakLength = |
| | 197 | gCoreContext->GetNumSetting("CommDetectMinCommBreakLength", 60); |
| | 198 | commDetectMinShowLength = |
| | 199 | gCoreContext->GetNumSetting("CommDetectMinShowLength", 65); |
| | 200 | commDetectMaxCommLength = |
| | 201 | gCoreContext->GetNumSetting("CommDetectMaxCommLength", 125); |
| | 202 | commDetectLargeSceneChangeThreshold = |
| | 203 | gCoreContext->GetNumSetting("CommDetectLargeSceneChangeThreshold", 60); |
| | 204 | QStringList commDetectCommLengthsString = |
| | 205 | gCoreContext->GetSetting("CommDetectCommLengths", "12,15,20,30,40,45,60").split(',', QString::SkipEmptyParts); |
| | 206 | for (QStringList::iterator it = commDetectCommLengthsString.begin(); it != commDetectCommLengthsString.end(); ++it) |
| | 207 | commDetectCommLengths.append((*it).toFloat()); |
| | 208 | |
| | 209 | commDetectBlankCanHaveLogo = |
| | 210 | !!gCoreContext->GetNumSetting("CommDetectBlankCanHaveLogo", 1); |
| | 211 | |
| | 212 | AudioSettings settings("", ""); |
| | 213 | AudioOutput *audioOutput = new AudioBuffer(settings); |
| | 214 | audioBuffer = ((AudioBuffer*)audioOutput); |
| | 215 | |
| | 216 | } |
| | 217 | |
| | 218 | void NextgenCommDetector::Init() |
| | 219 | { |
| | 220 | QSize video_disp_dim = player->GetVideoSize(); |
| | 221 | width = video_disp_dim.width(); |
| | 222 | height = video_disp_dim.height(); |
| | 223 | fps = player->GetFrameRate(); |
| | 224 | |
| | 225 | preRoll = (long long)(max(0,recordingStartedAt.secsTo(startedAt)) * fps); |
| | 226 | postRoll = (long long)(max(0,stopsAt.secsTo(recordingStopsAt)) * fps); |
| | 227 | |
| | 228 | #ifdef SHOW_DEBUG_WIN |
| | 229 | comm_debug_init(width, height); |
| | 230 | #endif |
| | 231 | |
| | 232 | currentAspect = COMM_ASPECT_WIDE; |
| | 233 | |
| | 234 | lastFrameNumber = -2; |
| | 235 | curFrameNumber = -1; |
| | 236 | |
| | 237 | if (getenv("DEBUGCOMMFLAG")) |
| | 238 | verboseDebugging = true; |
| | 239 | else |
| | 240 | verboseDebugging = false; |
| | 241 | |
| | 242 | LOG(VB_COMMFLAG, LOG_INFO, |
| | 243 | QString("Commercial Detection initialized: " |
| | 244 | "width = %1, height = %2, fps = %3, method = %4") |
| | 245 | .arg(width).arg(height) |
| | 246 | .arg(player->GetFrameRate()).arg(commDetectMethod)); |
| | 247 | |
| | 248 | if ((width * height) > 1000000) |
| | 249 | { |
| | 250 | horizSpacing = 10; |
| | 251 | vertSpacing = 10; |
| | 252 | } |
| | 253 | else if ((width * height) > 800000) |
| | 254 | { |
| | 255 | horizSpacing = 8; |
| | 256 | vertSpacing = 8; |
| | 257 | } |
| | 258 | else if ((width * height) > 400000) |
| | 259 | { |
| | 260 | horizSpacing = 6; |
| | 261 | vertSpacing = 6; |
| | 262 | } |
| | 263 | else if ((width * height) > 300000) |
| | 264 | { |
| | 265 | horizSpacing = 6; |
| | 266 | vertSpacing = 4; |
| | 267 | } |
| | 268 | else |
| | 269 | { |
| | 270 | horizSpacing = 4; |
| | 271 | vertSpacing = 4; |
| | 272 | } |
| | 273 | |
| | 274 | LOG(VB_COMMFLAG, LOG_INFO, |
| | 275 | QString("Using Sample Spacing of %1 horizontal & %2 vertical pixels.") |
| | 276 | .arg(horizSpacing).arg(vertSpacing)); |
| | 277 | |
| | 278 | framesProcessed = 0; |
| | 279 | totalMinBrightness = 0; |
| | 280 | blankFrameCount = 0; |
| | 281 | |
| | 282 | aggressiveDetection = true; |
| | 283 | currentAspect = COMM_ASPECT_WIDE; |
| | 284 | decoderFoundAspectChanges = false; |
| | 285 | |
| | 286 | lastSentCommBreakMap.clear(); |
| | 287 | |
| | 288 | // Check if close to 4:3 |
| | 289 | if (fabs(((width*1.0)/height) - 1.333333) < 0.1) |
| | 290 | currentAspect = COMM_ASPECT_NORMAL; |
| | 291 | |
| | 292 | sceneChangeDetector = new NextgenSceneChangeDetector(width, height, |
| | 293 | commDetectBorder, horizSpacing, vertSpacing); |
| | 294 | connect( |
| | 295 | sceneChangeDetector, |
| | 296 | SIGNAL(haveNewInformation(unsigned int,bool,float)), |
| | 297 | this, |
| | 298 | SLOT(sceneChangeDetectorHasNewInformation(unsigned int,bool,float)) |
| | 299 | ); |
| | 300 | |
| | 301 | silentFrameCount = 0; |
| | 302 | audioChangeDetector = new AudioChangeDetector(); |
| | 303 | connect( |
| | 304 | audioChangeDetector, |
| | 305 | SIGNAL(haveNewInformation(unsigned int,bool,float,float)), |
| | 306 | this, |
| | 307 | SLOT(audioDetectorHasNewInformation(unsigned int,bool,float,float)) |
| | 308 | ); |
| | 309 | |
| | 310 | #ifdef USE_SUBTITLES |
| | 311 | subtitleChangeDetector = new SubtitleChangeDetector(player); |
| | 312 | connect( |
| | 313 | subtitleChangeDetector, |
| | 314 | SIGNAL(haveNewInformation(unsigned int,bool,float)), |
| | 315 | this, |
| | 316 | SLOT(subtitleDetectorHasNewInformation(unsigned int,bool,float)) |
| | 317 | ); |
| | 318 | #endif |
| | 319 | |
| | 320 | frameIsBlank = false; |
| | 321 | stationLogoPresent = false; |
| | 322 | |
| | 323 | framePtr = NULL; |
| | 324 | |
| | 325 | logoInfoAvailable = false; |
| | 326 | |
| | 327 | ClearAllMaps(); |
| | 328 | |
| | 329 | if (verboseDebugging) |
| | 330 | { |
| | 331 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 332 | " Fr # Min Max Avg Scn F A Mask"); |
| | 333 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 334 | " ------ --- --- --- --- - - ----"); |
| | 335 | } |
| | 336 | } |
| | 337 | |
| | 338 | void NextgenCommDetector::deleteLater(void) |
| | 339 | { |
| | 340 | if (audioBuffer) |
| | 341 | { |
| | 342 | if (player && player->GetAudio()) |
| | 343 | player->GetAudio()->SetAudioOutput(NULL); |
| | 344 | delete audioBuffer; |
| | 345 | audioBuffer = NULL; |
| | 346 | } |
| | 347 | if (audioChangeDetector) |
| | 348 | audioChangeDetector->deleteLater(); |
| | 349 | |
| | 350 | if (sceneChangeDetector) |
| | 351 | sceneChangeDetector->deleteLater(); |
| | 352 | |
| | 353 | if (logoDetector) |
| | 354 | logoDetector->deleteLater(); |
| | 355 | |
| | 356 | CommDetectorBase::deleteLater(); |
| | 357 | } |
| | 358 | |
| | 359 | bool NextgenCommDetector::go() |
| | 360 | { |
| | 361 | player->GetAudio()->ReinitAudio(); |
| | 362 | int secsSince = 0; |
| | 363 | int requiredBuffer = 30; |
| | 364 | int requiredHeadStart = requiredBuffer; |
| | 365 | bool wereRecording = stillRecording; |
| | 366 | |
| | 367 | emit statusUpdate(QObject::tr("Building Head Start Buffer")); |
| | 368 | secsSince = recordingStartedAt.secsTo(QDateTime::currentDateTime()); |
| | 369 | while (stillRecording && (secsSince < requiredHeadStart)) |
| | 370 | { |
| | 371 | emit breathe(); |
| | 372 | if (m_bStop) |
| | 373 | return false; |
| | 374 | |
| | 375 | sleep(2); |
| | 376 | secsSince = recordingStartedAt.secsTo(QDateTime::currentDateTime()); |
| | 377 | } |
| | 378 | |
| | 379 | if (player->OpenFile() < 0) |
| | 380 | return false; |
| | 381 | |
| | 382 | Init(); |
| | 383 | |
| | 384 | if (commDetectMethod & COMM_DETECT_LOGO) |
| | 385 | { |
| | 386 | logoDetector = new NextgenLogoDetector(this, width, height, |
| | 387 | commDetectBorder, horizSpacing, vertSpacing); |
| | 388 | |
| | 389 | requiredHeadStart += max(0,recordingStartedAt.secsTo(startedAt)); |
| | 390 | requiredHeadStart += logoDetector->getRequiredAvailableBufferForSearch(); |
| | 391 | |
| | 392 | emit statusUpdate(QObject::tr("Building Logo Detection Buffer")); |
| | 393 | secsSince = recordingStartedAt.secsTo(QDateTime::currentDateTime()); |
| | 394 | while (stillRecording && (secsSince < requiredHeadStart)) |
| | 395 | { |
| | 396 | emit breathe(); |
| | 397 | if (m_bStop) |
| | 398 | return false; |
| | 399 | |
| | 400 | sleep(2); |
| | 401 | secsSince = recordingStartedAt.secsTo(QDateTime::currentDateTime()); |
| | 402 | } |
| | 403 | } |
| | 404 | |
| | 405 | // Don't bother flagging short ~realtime recordings |
| | 406 | if ((wereRecording) && (!stillRecording) && (secsSince < requiredHeadStart)) |
| | 407 | return false; |
| | 408 | |
| | 409 | aggressiveDetection = |
| | 410 | gCoreContext->GetNumSetting("AggressiveCommDetect", 1); |
| | 411 | |
| | 412 | if (!player->InitVideo()) |
| | 413 | { |
| | 414 | LOG(VB_GENERAL, LOG_ERR, |
| | 415 | "NVP: Unable to initialize video for FlagCommercials."); |
| | 416 | return false; |
| | 417 | } |
| | 418 | #ifdef USE_SUBTITLES |
| | 419 | //player->EnableSubtitles(false); |
| | 420 | #endif |
| | 421 | |
| | 422 | if (commDetectMethod & COMM_DETECT_LOGO) |
| | 423 | { |
| | 424 | emit statusUpdate(QObject::tr("Searching for Logo")); |
| | 425 | |
| | 426 | if (showProgress) |
| | 427 | { |
| | 428 | cerr << "Finding Logo"; |
| | 429 | cerr.flush(); |
| | 430 | } |
| | 431 | LOG(VB_GENERAL, LOG_INFO, "Finding Logo"); |
| | 432 | |
| | 433 | logoInfoAvailable = logoDetector->searchForLogo(player); |
| | 434 | |
| | 435 | if (showProgress) |
| | 436 | { |
| | 437 | cerr << "\b\b\b\b\b\b\b\b\b\b\b\b " |
| | 438 | "\b\b\b\b\b\b\b\b\b\b\b\b"; |
| | 439 | cerr.flush(); |
| | 440 | } |
| | 441 | } |
| | 442 | |
| | 443 | emit breathe(); |
| | 444 | if (m_bStop) |
| | 445 | return false; |
| | 446 | |
| | 447 | QTime flagTime; |
| | 448 | flagTime.start(); |
| | 449 | |
| | 450 | long long myTotalFrames; |
| | 451 | if (recordingStopsAt < QDateTime::currentDateTime() ) |
| | 452 | myTotalFrames = player->GetTotalFrameCount(); |
| | 453 | else |
| | 454 | myTotalFrames = (long long)(player->GetFrameRate() * |
| | 455 | (recordingStartedAt.secsTo(recordingStopsAt))); |
| | 456 | |
| | 457 | if (showProgress) |
| | 458 | { |
| | 459 | if (myTotalFrames) |
| | 460 | cerr << "\r 0%/ \r" << flush; |
| | 461 | else |
| | 462 | cerr << "\r 0/ \r" << flush; |
| | 463 | } |
| | 464 | |
| | 465 | |
| | 466 | float flagFPS; |
| | 467 | long long currentFrameNumber; |
| | 468 | float aspect = player->GetVideoAspect(); |
| | 469 | float newAspect = aspect; |
| | 470 | int prevpercent = -1; |
| | 471 | |
| | 472 | SetVideoParams(aspect); |
| | 473 | |
| | 474 | emit breathe(); |
| | 475 | |
| | 476 | audioBuffer->Enable(); |
| | 477 | |
| | 478 | // make sure we get a key frame before us otherwise reset doesnt happen correctly. |
| | 479 | player->DiscardVideoFrame(player->GetRawVideoFrame(60)); |
| | 480 | |
| | 481 | emit breathe(); |
| | 482 | |
| | 483 | player->GetAudio()->SetAudioOutput(audioBuffer); |
| | 484 | player->GetAudio()->ReinitAudio(); |
| | 485 | |
| | 486 | player->DiscardVideoFrame(player->GetRawVideoFrame(0)); |
| | 487 | player->ResetTotalDuration(); |
| | 488 | |
| | 489 | #ifdef USE_SUBTITLES |
| | 490 | player->GetSubReader()->EnableAVSubtitles(true); |
| | 491 | player->GetSubReader()->EnableTextSubtitles(true); |
| | 492 | player->GetSubReader()->EnableRawTextSubtitles(true); |
| | 493 | //player->GetTeletextReader()->SetEnabled(true); |
| | 494 | //player->EnableCaptions(kDisplayTeletextCaptions, false); |
| | 495 | player->GetCC608Reader()->SetEnabled(true); |
| | 496 | player->GetCC708Reader()->SetEnabled(true); |
| | 497 | player->GetDecoder()->SetDecodeAllSubtitles(true); |
| | 498 | LOG(VB_GENERAL, LOG_DEBUG, "enabling subtitles."); |
| | 499 | #endif |
| | 500 | |
| | 501 | while (!player->GetEof()) |
| | 502 | { |
| | 503 | struct timeval startTime; |
| | 504 | if (stillRecording) |
| | 505 | gettimeofday(&startTime, NULL); |
| | 506 | |
| | 507 | VideoFrame* currentFrame = player->GetRawVideoFrame(); |
| | 508 | if (!vfQueue.isEmpty()) |
| | 509 | { |
| | 510 | if (currentFrame) |
| | 511 | vfQueue.enqueue(currentFrame); |
| | 512 | currentFrame = vfQueue.dequeue(); |
| | 513 | } |
| | 514 | currentFrameNumber = currentFrame->frameNumber; |
| | 515 | |
| | 516 | //Lucas: maybe we should make the nuppelvideoplayer send out a signal |
| | 517 | //when the aspect ratio changes. |
| | 518 | //In order to not change too many things at a time, I"m using basic |
| | 519 | //polling for now. |
| | 520 | newAspect = currentFrame->aspect; |
| | 521 | if (newAspect != aspect) |
| | 522 | { |
| | 523 | SetVideoParams(aspect); |
| | 524 | aspect = newAspect; |
| | 525 | } |
| | 526 | |
| | 527 | if (((currentFrameNumber % 500) == 0) || |
| | 528 | (((currentFrameNumber % 100) == 0) && |
| | 529 | (stillRecording))) |
| | 530 | { |
| | 531 | emit breathe(); |
| | 532 | if (m_bStop) |
| | 533 | { |
| | 534 | player->DiscardVideoFrame(currentFrame); |
| | 535 | return false; |
| | 536 | } |
| | 537 | } |
| | 538 | |
| | 539 | if ((sendCommBreakMapUpdates) && |
| | 540 | ((commBreakMapUpdateRequested) || |
| | 541 | ((currentFrameNumber % 500) == 0))) |
| | 542 | { |
| | 543 | frm_dir_map_t commBreakMap; |
| | 544 | frm_dir_map_t::iterator it; |
| | 545 | frm_dir_map_t::iterator lastIt; |
| | 546 | bool mapsAreIdentical = false; |
| | 547 | |
| | 548 | GetCommercialBreakList(commBreakMap); |
| | 549 | |
| | 550 | if ((commBreakMap.size() == 0) && |
| | 551 | (lastSentCommBreakMap.size() == 0)) |
| | 552 | { |
| | 553 | mapsAreIdentical = true; |
| | 554 | } |
| | 555 | else if (commBreakMap.size() == lastSentCommBreakMap.size()) |
| | 556 | { |
| | 557 | // assume true for now and set false if we find a difference |
| | 558 | mapsAreIdentical = true; |
| | 559 | for (it = commBreakMap.begin(); |
| | 560 | it != commBreakMap.end() && mapsAreIdentical; ++it) |
| | 561 | { |
| | 562 | lastIt = lastSentCommBreakMap.find(it.key()); |
| | 563 | if ((lastIt == lastSentCommBreakMap.end()) || |
| | 564 | (*lastIt != *it)) |
| | 565 | mapsAreIdentical = false; |
| | 566 | } |
| | 567 | } |
| | 568 | |
| | 569 | if (commBreakMapUpdateRequested || !mapsAreIdentical) |
| | 570 | { |
| | 571 | emit gotNewCommercialBreakList(); |
| | 572 | lastSentCommBreakMap = commBreakMap; |
| | 573 | } |
| | 574 | |
| | 575 | if (commBreakMapUpdateRequested) |
| | 576 | commBreakMapUpdateRequested = false; |
| | 577 | } |
| | 578 | |
| | 579 | while (m_bPaused) |
| | 580 | { |
| | 581 | emit breathe(); |
| | 582 | sleep(1); |
| | 583 | } |
| | 584 | |
| | 585 | // sleep a little so we don't use all cpu even if we're niced |
| | 586 | if (!fullSpeed && !stillRecording) |
| | 587 | usleep(10000); |
| | 588 | |
| | 589 | if (((currentFrameNumber % 500) == 0) || |
| | 590 | ((showProgress || stillRecording) && |
| | 591 | ((currentFrameNumber % 100) == 0))) |
| | 592 | { |
| | 593 | float elapsed = flagTime.elapsed() / 1000.0; |
| | 594 | |
| | 595 | if (elapsed) |
| | 596 | flagFPS = currentFrameNumber / elapsed; |
| | 597 | else |
| | 598 | flagFPS = 0.0; |
| | 599 | |
| | 600 | int percentage; |
| | 601 | if (myTotalFrames) |
| | 602 | percentage = currentFrameNumber * 100 / myTotalFrames; |
| | 603 | else |
| | 604 | percentage = 0; |
| | 605 | |
| | 606 | if (percentage > 100) |
| | 607 | percentage = 100; |
| | 608 | |
| | 609 | if (showProgress) |
| | 610 | { |
| | 611 | if (myTotalFrames) |
| | 612 | { |
| | 613 | QString tmp = QString("\r%1%/%2fps \r") |
| | 614 | .arg(percentage, 3).arg((int)flagFPS, 4); |
| | 615 | cerr << qPrintable(tmp) << flush; |
| | 616 | } |
| | 617 | else |
| | 618 | { |
| | 619 | QString tmp = QString("\r%1/%2fps \r") |
| | 620 | .arg(currentFrameNumber, 6).arg((int)flagFPS, 4); |
| | 621 | cerr << qPrintable(tmp) << flush; |
| | 622 | } |
| | 623 | } |
| | 624 | |
| | 625 | if (myTotalFrames) |
| | 626 | emit statusUpdate(QObject::tr("%1% Completed @ %2 fps.") |
| | 627 | .arg(percentage).arg(flagFPS)); |
| | 628 | else |
| | 629 | emit statusUpdate(QObject::tr("%1 Frames Completed @ %2 fps.") |
| | 630 | .arg(currentFrameNumber).arg(flagFPS)); |
| | 631 | |
| | 632 | if (percentage % 10 == 0 && prevpercent != percentage) |
| | 633 | { |
| | 634 | prevpercent = percentage; |
| | 635 | LOG(VB_GENERAL, LOG_INFO, QString("%1%% Completed @ %2 fps.") |
| | 636 | .arg(percentage) .arg(flagFPS)); |
| | 637 | } |
| | 638 | } |
| | 639 | |
| | 640 | ProcessFrame(currentFrame, currentFrameNumber); |
| | 641 | |
| | 642 | if (stillRecording) |
| | 643 | { |
| | 644 | int secondsRecorded = |
| | 645 | recordingStartedAt.secsTo(QDateTime::currentDateTime()); |
| | 646 | int secondsFlagged = (int)(framesProcessed / fps); |
| | 647 | int secondsBehind = secondsRecorded - secondsFlagged; |
| | 648 | long usecPerFrame = (long)(1.0 / player->GetFrameRate() * 1000000); |
| | 649 | |
| | 650 | struct timeval endTime; |
| | 651 | gettimeofday(&endTime, NULL); |
| | 652 | |
| | 653 | long long usecSleep = |
| | 654 | usecPerFrame - |
| | 655 | (((endTime.tv_sec - startTime.tv_sec) * 1000000) + |
| | 656 | (endTime.tv_usec - startTime.tv_usec)); |
| | 657 | |
| | 658 | if (secondsBehind > requiredBuffer) |
| | 659 | { |
| | 660 | if (fullSpeed) |
| | 661 | usecSleep = 0; |
| | 662 | else |
| | 663 | usecSleep = (long)(usecSleep * 0.25); |
| | 664 | } |
| | 665 | else if (secondsBehind < requiredBuffer) |
| | 666 | usecSleep = (long)(usecPerFrame * 1.5); |
| | 667 | |
| | 668 | if (usecSleep > 0) |
| | 669 | usleep(usecSleep); |
| | 670 | } |
| | 671 | |
| | 672 | player->DiscardVideoFrame(currentFrame); |
| | 673 | } |
| | 674 | |
| | 675 | if (showProgress) |
| | 676 | { |
| | 677 | float elapsed = flagTime.elapsed() / 1000.0; |
| | 678 | |
| | 679 | if (elapsed) |
| | 680 | flagFPS = currentFrameNumber / elapsed; |
| | 681 | else |
| | 682 | flagFPS = 0.0; |
| | 683 | |
| | 684 | if (myTotalFrames) |
| | 685 | cerr << "\b\b\b\b\b\b \b\b\b\b\b\b"; |
| | 686 | else |
| | 687 | cerr << "\b\b\b\b\b\b\b\b\b\b\b\b\b " |
| | 688 | "\b\b\b\b\b\b\b\b\b\b\b\b\b"; |
| | 689 | cerr.flush(); |
| | 690 | } |
| | 691 | |
| | 692 | return true; |
| | 693 | } |
| | 694 | |
| | 695 | void NextgenCommDetector::sceneChangeDetectorHasNewInformation( |
| | 696 | unsigned int framenum,bool isSceneChange,float debugValue) |
| | 697 | { |
| | 698 | if (isSceneChange) |
| | 699 | { |
| | 700 | frameInfo[framenum].flagMask |= COMM_FRAME_SCENE_CHANGE; |
| | 701 | sceneMap[framenum] = MARK_SCENE_CHANGE; |
| | 702 | } |
| | 703 | else |
| | 704 | { |
| | 705 | frameInfo[framenum].flagMask &= ~COMM_FRAME_SCENE_CHANGE; |
| | 706 | sceneMap.remove(framenum); |
| | 707 | } |
| | 708 | |
| | 709 | frameInfo[framenum].sceneChangePercent = (int) (debugValue*100); |
| | 710 | |
| | 711 | if (verboseDebugging) |
| | 712 | { |
| | 713 | //LOG(VB_COMMFLAG, LOG_DEBUG, QString("Scene Change @%1 : %2 %3").arg(framenum).arg(isSceneChange).arg((int)(debugValue*100))); |
| | 714 | } |
| | 715 | } |
| | 716 | |
| | 717 | void NextgenCommDetector::audioDetectorHasNewInformation( |
| | 718 | unsigned int framenum, bool hasChanged, float amplitude, float debugValue) |
| | 719 | { |
| | 720 | if (hasChanged) |
| | 721 | frameInfo[framenum].flagMask |= COMM_FRAME_AUDIO_CHANGE; |
| | 722 | else |
| | 723 | frameInfo[framenum].flagMask &= ~COMM_FRAME_AUDIO_CHANGE; |
| | 724 | frameInfo[framenum].audioMode = (int) (debugValue); |
| | 725 | } |
| | 726 | |
| | 727 | void NextgenCommDetector::subtitleDetectorHasNewInformation( |
| | 728 | unsigned int framenum, bool hasChanged, float debugValue) |
| | 729 | { |
| | 730 | if (hasChanged) |
| | 731 | { |
| | 732 | frameInfo[framenum].flagMask |= COMM_FRAME_SUBTITLE_PRESENT; |
| | 733 | subtitleInfoAvailable = true; |
| | 734 | subtitleFrameCount++; |
| | 735 | } |
| | 736 | else |
| | 737 | frameInfo[framenum].flagMask &= ~COMM_FRAME_SUBTITLE_PRESENT; |
| | 738 | frameInfo[framenum].subtitleMode = (int) (debugValue); |
| | 739 | } |
| | 740 | |
| | 741 | void NextgenCommDetector::GetCommercialBreakList(frm_dir_map_t &marks) |
| | 742 | { |
| | 743 | |
| | 744 | LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetCommBreakMap()"); |
| | 745 | |
| | 746 | marks.clear(); |
| | 747 | |
| | 748 | CleanupFrameInfo(); |
| | 749 | |
| | 750 | bool blank = COMM_DETECT_BLANK & commDetectMethod; |
| | 751 | bool scene = COMM_DETECT_SCENE & commDetectMethod; |
| | 752 | bool logo = COMM_DETECT_LOGO & commDetectMethod; |
| | 753 | bool audio = COMM_DETECT_AUDIO & commDetectMethod; |
| | 754 | |
| | 755 | if (COMM_DETECT_OFF == commDetectMethod) |
| | 756 | return; |
| | 757 | |
| | 758 | if (!blank && !scene && !logo && !audio) |
| | 759 | { |
| | 760 | LOG(VB_COMMFLAG, LOG_ERR, QString("Unexpected commDetectMethod: 0x%1") |
| | 761 | .arg(commDetectMethod,0,16)); |
| | 762 | return; |
| | 763 | } |
| | 764 | |
| | 765 | if (blank && scene && logo) |
| | 766 | { |
| | 767 | BuildAllMethodsCommList(); |
| | 768 | marks = commBreakMap; |
| | 769 | LOG(VB_COMMFLAG, LOG_INFO, "Final Commercial Break Map"); |
| | 770 | return; |
| | 771 | } |
| | 772 | |
| | 773 | if (blank) |
| | 774 | { |
| | 775 | BuildBlankFrameCommList(); |
| | 776 | marks = blankCommBreakMap; |
| | 777 | } |
| | 778 | |
| | 779 | if (scene) |
| | 780 | { |
| | 781 | BuildSceneChangeCommList(); |
| | 782 | marks = sceneCommBreakMap; |
| | 783 | } |
| | 784 | |
| | 785 | if (logo) |
| | 786 | { |
| | 787 | BuildLogoCommList(); |
| | 788 | marks = logoCommBreakMap; |
| | 789 | } |
| | 790 | |
| | 791 | if (audio) |
| | 792 | { |
| | 793 | BuildAudioFrameCommList(); |
| | 794 | marks = audioCommBreakMap; |
| | 795 | } |
| | 796 | |
| | 797 | int cnt = ((blank) ? 1 : 0) + ((scene) ? 1 : 0) + ((logo) ? 1 : 0) + ((audio) ? 1 : 0); |
| | 798 | if (cnt >= 2) |
| | 799 | { |
| | 800 | marks.clear(); |
| | 801 | if (blank) |
| | 802 | { |
| | 803 | marks = Combine2Maps(blankCommBreakMap, marks); |
| | 804 | } |
| | 805 | if (scene) |
| | 806 | { |
| | 807 | marks = Combine2Maps(sceneCommBreakMap, marks); |
| | 808 | } |
| | 809 | if (logo) |
| | 810 | { |
| | 811 | marks = Combine2Maps(logoCommBreakMap, marks); |
| | 812 | } |
| | 813 | if (audio) |
| | 814 | { |
| | 815 | marks = Combine2Maps(audioCommBreakMap, marks); |
| | 816 | } |
| | 817 | commBreakMap = marks; |
| | 818 | } |
| | 819 | |
| | 820 | LOG(VB_COMMFLAG, LOG_INFO, "Final Commercial Break Map"); |
| | 821 | } |
| | 822 | |
| | 823 | void NextgenCommDetector::recordingFinished(long long totalFileSize) |
| | 824 | { |
| | 825 | (void)totalFileSize; |
| | 826 | |
| | 827 | stillRecording = false; |
| | 828 | } |
| | 829 | |
| | 830 | void NextgenCommDetector::requestCommBreakMapUpdate(void) |
| | 831 | { |
| | 832 | commBreakMapUpdateRequested = true; |
| | 833 | sendCommBreakMapUpdates = true; |
| | 834 | } |
| | 835 | |
| | 836 | void NextgenCommDetector::SetVideoParams(float aspect) |
| | 837 | { |
| | 838 | int newAspect = COMM_ASPECT_WIDE; |
| | 839 | |
| | 840 | LOG(VB_COMMFLAG, LOG_INFO, |
| | 841 | QString("CommDetect::SetVideoParams called with aspect = %1") |
| | 842 | .arg(aspect)); |
| | 843 | // Default to Widescreen but use the same check as VideoOutput::MoveResize() |
| | 844 | // to determine if is normal 4:3 aspect |
| | 845 | if (fabs(aspect - 1.333333) < 0.1) |
| | 846 | newAspect = COMM_ASPECT_NORMAL; |
| | 847 | |
| | 848 | if (newAspect != currentAspect) |
| | 849 | { |
| | 850 | LOG(VB_COMMFLAG, LOG_INFO, |
| | 851 | QString("Aspect Ratio changed from %1 to %2 at frame %3") |
| | 852 | .arg(currentAspect).arg(newAspect) |
| | 853 | .arg(curFrameNumber)); |
| | 854 | |
| | 855 | if (frameInfo.contains(curFrameNumber)) |
| | 856 | { |
| | 857 | // pretend that this frame is blank so that we can create test |
| | 858 | // blocks on real aspect ratio change boundaries. |
| | 859 | frameInfo[curFrameNumber].flagMask |= COMM_FRAME_BLANK; |
| | 860 | frameInfo[curFrameNumber].flagMask |= COMM_FRAME_ASPECT_CHANGE; |
| | 861 | decoderFoundAspectChanges = true; |
| | 862 | } |
| | 863 | else if (curFrameNumber != -1) |
| | 864 | { |
| | 865 | LOG(VB_COMMFLAG, LOG_ERR, |
| | 866 | QString("Unable to keep track of Aspect ratio change because " |
| | 867 | "frameInfo for frame number %1 does not exist.") |
| | 868 | .arg(curFrameNumber)); |
| | 869 | } |
| | 870 | currentAspect = newAspect; |
| | 871 | } |
| | 872 | } |
| | 873 | |
| | 874 | void NextgenCommDetector::ProcessFrame(VideoFrame *frame, |
| | 875 | long long frame_number) |
| | 876 | { |
| | 877 | int max = 0; |
| | 878 | int min = 255; |
| | 879 | int avg = 0; |
| | 880 | unsigned char pixel; |
| | 881 | int blankPixelsChecked = 0; |
| | 882 | long long totBrightness = 0; |
| | 883 | unsigned char *rowMax = new unsigned char[height]; |
| | 884 | unsigned char *colMax = new unsigned char[width]; |
| | 885 | memset(rowMax, 0, sizeof(*rowMax)*height); |
| | 886 | memset(colMax, 0, sizeof(*colMax)*width); |
| | 887 | int topDarkRow = commDetectBorder; |
| | 888 | int bottomDarkRow = height - commDetectBorder - 1; |
| | 889 | int leftDarkCol = commDetectBorder; |
| | 890 | int rightDarkCol = width - commDetectBorder - 1; |
| | 891 | NGFrameInfoEntry fInfo; |
| | 892 | |
| | 893 | if (!frame || !(frame->buf) || frame_number == -1 || |
| | 894 | frame->codec != FMT_YV12) |
| | 895 | { |
| | 896 | LOG(VB_COMMFLAG, LOG_ERR, "CommDetect: Invalid video frame or codec, " |
| | 897 | "unable to process frame."); |
| | 898 | delete[] rowMax; |
| | 899 | delete[] colMax; |
| | 900 | return; |
| | 901 | } |
| | 902 | |
| | 903 | if (!width || !height) |
| | 904 | { |
| | 905 | LOG(VB_COMMFLAG, LOG_ERR, "CommDetect: Width or Height is 0, " |
| | 906 | "unable to process frame."); |
| | 907 | delete[] rowMax; |
| | 908 | delete[] colMax; |
| | 909 | return; |
| | 910 | } |
| | 911 | |
| | 912 | curFrameNumber = frame_number; |
| | 913 | framePtr = frame->buf; |
| | 914 | |
| | 915 | fInfo.minBrightness = -1; |
| | 916 | fInfo.maxBrightness = -1; |
| | 917 | fInfo.avgBrightness = -1; |
| | 918 | fInfo.sceneChangePercent = -1; |
| | 919 | fInfo.aspect = currentAspect; |
| | 920 | fInfo.format = COMM_FORMAT_NORMAL; |
| | 921 | fInfo.audioPower = 0; |
| | 922 | fInfo.audioMode = 0; |
| | 923 | fInfo.flagMask = 0; |
| | 924 | |
| | 925 | int& flagMask = frameInfo[curFrameNumber].flagMask; |
| | 926 | |
| | 927 | // Fill in dummy info records for skipped frames. |
| | 928 | if (lastFrameNumber != (curFrameNumber - 1)) |
| | 929 | { |
| | 930 | if (lastFrameNumber > 0) |
| | 931 | { |
| | 932 | fInfo.aspect = frameInfo[lastFrameNumber].aspect; |
| | 933 | fInfo.format = frameInfo[lastFrameNumber].format; |
| | 934 | } |
| | 935 | fInfo.flagMask = COMM_FRAME_SKIPPED; |
| | 936 | |
| | 937 | lastFrameNumber++; |
| | 938 | while(lastFrameNumber < curFrameNumber) |
| | 939 | frameInfo[lastFrameNumber++] = fInfo; |
| | 940 | |
| | 941 | fInfo.flagMask = 0; |
| | 942 | } |
| | 943 | lastFrameNumber = curFrameNumber; |
| | 944 | |
| | 945 | frameInfo[curFrameNumber] = fInfo; |
| | 946 | |
| | 947 | if (commDetectMethod & COMM_DETECT_BLANKS) |
| | 948 | frameIsBlank = false; |
| | 949 | |
| | 950 | if (commDetectMethod & COMM_DETECT_SCENE) |
| | 951 | { |
| | 952 | sceneChangeDetector->processFrame(framePtr, curFrameNumber); |
| | 953 | } |
| | 954 | |
| | 955 | #ifdef USE_SUBTITLES |
| | 956 | subtitleChangeDetector->processFrame(player, frame); |
| | 957 | #endif |
| | 958 | |
| | 959 | stationLogoPresent = false; |
| | 960 | |
| | 961 | for(int y = commDetectBorder; y < (height - commDetectBorder); |
| | 962 | y += vertSpacing) |
| | 963 | { |
| | 964 | for(int x = commDetectBorder; x < (width - commDetectBorder); |
| | 965 | x += horizSpacing) |
| | 966 | { |
| | 967 | pixel = framePtr[y * width + x]; |
| | 968 | |
| | 969 | if (commDetectMethod & COMM_DETECT_BLANKS) |
| | 970 | { |
| | 971 | bool checkPixel = false; |
| | 972 | if (!commDetectBlankCanHaveLogo) |
| | 973 | checkPixel = true; |
| | 974 | |
| | 975 | if (!logoInfoAvailable) |
| | 976 | checkPixel = true; |
| | 977 | else if (!logoDetector->pixelInsideLogo(x,y)) |
| | 978 | checkPixel=true; |
| | 979 | |
| | 980 | if (checkPixel) |
| | 981 | { |
| | 982 | blankPixelsChecked++; |
| | 983 | totBrightness += pixel; |
| | 984 | |
| | 985 | if (pixel < min) |
| | 986 | min = pixel; |
| | 987 | |
| | 988 | if (pixel > max) |
| | 989 | max = pixel; |
| | 990 | |
| | 991 | if (pixel > rowMax[y]) |
| | 992 | rowMax[y] = pixel; |
| | 993 | |
| | 994 | if (pixel > colMax[x]) |
| | 995 | colMax[x] = pixel; |
| | 996 | } |
| | 997 | } |
| | 998 | } |
| | 999 | } |
| | 1000 | |
| | 1001 | if (commDetectMethod & COMM_DETECT_BLANKS) |
| | 1002 | { |
| | 1003 | for(int y = commDetectBorder; y < (height - commDetectBorder); |
| | 1004 | y += vertSpacing) |
| | 1005 | { |
| | 1006 | if (rowMax[y] > commDetectBoxBrightness) |
| | 1007 | break; |
| | 1008 | else |
| | 1009 | topDarkRow = y; |
| | 1010 | } |
| | 1011 | |
| | 1012 | for(int y = commDetectBorder; y < (height - commDetectBorder); |
| | 1013 | y += vertSpacing) |
| | 1014 | if (rowMax[y] >= commDetectBoxBrightness) |
| | 1015 | bottomDarkRow = y; |
| | 1016 | |
| | 1017 | delete[] rowMax; |
| | 1018 | rowMax = 0; |
| | 1019 | |
| | 1020 | for(int x = commDetectBorder; x < (width - commDetectBorder); |
| | 1021 | x += horizSpacing) |
| | 1022 | { |
| | 1023 | if (colMax[x] > commDetectBoxBrightness) |
| | 1024 | break; |
| | 1025 | else |
| | 1026 | leftDarkCol = x; |
| | 1027 | } |
| | 1028 | |
| | 1029 | for(int x = commDetectBorder; x < (width - commDetectBorder); |
| | 1030 | x += horizSpacing) |
| | 1031 | if (colMax[x] >= commDetectBoxBrightness) |
| | 1032 | rightDarkCol = x; |
| | 1033 | |
| | 1034 | delete[] colMax; |
| | 1035 | colMax = 0; |
| | 1036 | |
| | 1037 | if ((topDarkRow > commDetectBorder) && |
| | 1038 | (topDarkRow < (height * .20)) && |
| | 1039 | (bottomDarkRow < (height - commDetectBorder)) && |
| | 1040 | (bottomDarkRow > (height * .80))) |
| | 1041 | { |
| | 1042 | frameInfo[curFrameNumber].format = COMM_FORMAT_LETTERBOX; |
| | 1043 | } |
| | 1044 | else if ((leftDarkCol > commDetectBorder) && |
| | 1045 | (leftDarkCol < (width * .20)) && |
| | 1046 | (rightDarkCol < (width - commDetectBorder)) && |
| | 1047 | (rightDarkCol > (width * .80))) |
| | 1048 | { |
| | 1049 | frameInfo[curFrameNumber].format = COMM_FORMAT_PILLARBOX; |
| | 1050 | } |
| | 1051 | else |
| | 1052 | { |
| | 1053 | frameInfo[curFrameNumber].format = COMM_FORMAT_NORMAL; |
| | 1054 | } |
| | 1055 | |
| | 1056 | avg = totBrightness / blankPixelsChecked; |
| | 1057 | |
| | 1058 | frameInfo[curFrameNumber].minBrightness = min; |
| | 1059 | frameInfo[curFrameNumber].maxBrightness = max; |
| | 1060 | frameInfo[curFrameNumber].avgBrightness = avg; |
| | 1061 | |
| | 1062 | totalMinBrightness += min; |
| | 1063 | commDetectDimAverage = min + 10; |
| | 1064 | |
| | 1065 | // Is the frame really dark |
| | 1066 | if (((max - min) <= commDetectBlankFrameMaxDiff) && |
| | 1067 | (max < commDetectDimBrightness)) |
| | 1068 | frameIsBlank = true; |
| | 1069 | |
| | 1070 | // Are we non-strict and the frame is blank |
| | 1071 | if ((!aggressiveDetection) && |
| | 1072 | ((max - min) <= commDetectBlankFrameMaxDiff)) |
| | 1073 | frameIsBlank = true; |
| | 1074 | |
| | 1075 | // Are we non-strict and the frame is dark |
| | 1076 | // OR the frame is dim and has a low avg brightness |
| | 1077 | if ((!aggressiveDetection) && |
| | 1078 | ((max < commDetectDarkBrightness) || |
| | 1079 | ((max < commDetectDimBrightness) && (avg < commDetectDimAverage)))) |
| | 1080 | frameIsBlank = true; |
| | 1081 | } |
| | 1082 | |
| | 1083 | if ((logoInfoAvailable) && (commDetectMethod & COMM_DETECT_LOGO)) |
| | 1084 | { |
| | 1085 | stationLogoPresent = |
| | 1086 | logoDetector->doesThisFrameContainTheFoundLogo(framePtr); |
| | 1087 | } |
| | 1088 | |
| | 1089 | #if 0 |
| | 1090 | if ((commDetectMethod == COMM_DETECT_ALL) && |
| | 1091 | (CheckRatingSymbol())) |
| | 1092 | { |
| | 1093 | flagMask |= COMM_FRAME_RATING_SYMBOL; |
| | 1094 | } |
| | 1095 | #endif |
| | 1096 | |
| | 1097 | if (frameIsBlank) |
| | 1098 | { |
| | 1099 | blankFrameMap[curFrameNumber] = MARK_BLANK_FRAME; |
| | 1100 | flagMask |= COMM_FRAME_BLANK; |
| | 1101 | blankFrameCount++; |
| | 1102 | } |
| | 1103 | |
| | 1104 | if (stationLogoPresent) |
| | 1105 | flagMask |= COMM_FRAME_LOGO_PRESENT; |
| | 1106 | |
| | 1107 | if (commDetectMethod & COMM_DETECT_AUDIO) |
| | 1108 | { |
| | 1109 | if (audioBuffer) |
| | 1110 | { |
| | 1111 | static int max_loops = 100; |
| | 1112 | // process a number of frames worth of audio |
| | 1113 | PAudioSample audSample = audioBuffer->GetSample(frame->timecode); |
| | 1114 | int loops = max_loops; |
| | 1115 | //#define USE_VFQUEUES |
| | 1116 | #ifdef USE_VFQUEUES |
| | 1117 | int queued = 0; |
| | 1118 | int not_queued = 0; |
| | 1119 | #endif |
| | 1120 | while (!audSample) |
| | 1121 | { |
| | 1122 | #ifdef USE_VFQUEUES |
| | 1123 | if (vfQueue.count() < 5) |
| | 1124 | { |
| | 1125 | VideoFrame* newFrame = player->GetRawVideoFrame(); |
| | 1126 | if (newFrame) |
| | 1127 | { |
| | 1128 | vfQueue.enqueue(newFrame); |
| | 1129 | queued++; |
| | 1130 | } |
| | 1131 | else |
| | 1132 | not_queued++; |
| | 1133 | |
| | 1134 | usleep(10000); |
| | 1135 | } |
| | 1136 | else |
| | 1137 | { |
| | 1138 | usleep(1000); |
| | 1139 | } |
| | 1140 | #else |
| | 1141 | usleep(1000); |
| | 1142 | #endif |
| | 1143 | emit breathe(); |
| | 1144 | |
| | 1145 | audSample = audioBuffer->GetSample(frame->timecode); |
| | 1146 | if (--loops == 0) |
| | 1147 | break; |
| | 1148 | } |
| | 1149 | if (loops < max_loops) |
| | 1150 | { |
| | 1151 | if (verboseDebugging) |
| | 1152 | { |
| | 1153 | #ifdef USE_VFQUEUES |
| | 1154 | if (loops == 0) |
| | 1155 | LOG(VB_COMMFLAG, LOG_DEBUG, QString(" get audio sample failed after max loops (%1,%2)").arg(queued).arg(not_queued)); |
| | 1156 | else |
| | 1157 | LOG(VB_COMMFLAG, LOG_DEBUG, QString(" get audio sample needed loops %1 (%2,%3)").arg(max_loops-loops).arg(queued).arg(not_queued)); |
| | 1158 | #else |
| | 1159 | if (loops == 0) |
| | 1160 | LOG(VB_COMMFLAG, LOG_DEBUG, QString(" get audio sample failed after max loops")); |
| | 1161 | else |
| | 1162 | LOG(VB_COMMFLAG, LOG_DEBUG, QString(" get audio sample needed loops %1").arg(max_loops-loops)); |
| | 1163 | #endif |
| | 1164 | } |
| | 1165 | } |
| | 1166 | |
| | 1167 | if (audSample && (audSample->power >= 0)) |
| | 1168 | { |
| | 1169 | frameInfo[curFrameNumber].audioPower = audSample->power; |
| | 1170 | frameInfo[curFrameNumber].audioMode = audSample->channels; |
| | 1171 | if (audSample->changed) |
| | 1172 | flagMask |= COMM_FRAME_AUDIO_CHANGE; |
| | 1173 | if (audSample->power < 1e-6) |
| | 1174 | { |
| | 1175 | flagMask |= COMM_FRAME_NO_AUDIO; |
| | 1176 | silentFrameCount++; |
| | 1177 | } |
| | 1178 | #ifdef AUDIODEBUGGING |
| | 1179 | if (verboseDebugging) |
| | 1180 | { |
| | 1181 | LOG(VB_COMMFLAG, LOG_DEBUG, QString(" video %1 audio %3") |
| | 1182 | .arg(frame->timecode) |
| | 1183 | .arg(audSample->toString())); |
| | 1184 | } |
| | 1185 | #endif |
| | 1186 | } |
| | 1187 | else |
| | 1188 | { |
| | 1189 | #ifdef AUDIODEBUGGING |
| | 1190 | if (verboseDebugging) |
| | 1191 | { |
| | 1192 | LOG(VB_COMMFLAG, LOG_DEBUG, QString(" video %1") |
| | 1193 | .arg(frame->timecode)); |
| | 1194 | } |
| | 1195 | #endif |
| | 1196 | frameInfo[curFrameNumber].audioPower = 0; |
| | 1197 | flagMask |= COMM_FRAME_INVALID_AUDIO; |
| | 1198 | } |
| | 1199 | } |
| | 1200 | } |
| | 1201 | |
| | 1202 | //TODO: move this debugging code out of the perframe loop, and do it after |
| | 1203 | // we've processed all frames. this is because a scenechangedetector can |
| | 1204 | // now use a few frames to determine whether the frame a few frames ago was |
| | 1205 | // a scene change or not.. due to this lookahead possibility the values |
| | 1206 | // that are currently in the frameInfo array, might be changed a few frames |
| | 1207 | // from now. The NextgenSceneChangeDetector doesn't use this though. future |
| | 1208 | // scenechangedetectors might. |
| | 1209 | |
| | 1210 | if (verboseDebugging) |
| | 1211 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1212 | QString().sprintf("Frame: %6ld -> %3d %3d %3d %3d %1d %1d %9.6f %1d %04x", |
| | 1213 | (long)curFrameNumber, |
| | 1214 | frameInfo[curFrameNumber].minBrightness, |
| | 1215 | frameInfo[curFrameNumber].maxBrightness, |
| | 1216 | frameInfo[curFrameNumber].avgBrightness, |
| | 1217 | frameInfo[curFrameNumber].sceneChangePercent, |
| | 1218 | frameInfo[curFrameNumber].format, |
| | 1219 | frameInfo[curFrameNumber].aspect, |
| | 1220 | frameInfo[curFrameNumber].audioPower, |
| | 1221 | frameInfo[curFrameNumber].audioMode, |
| | 1222 | frameInfo[curFrameNumber].flagMask )); |
| | 1223 | |
| | 1224 | #ifdef SHOW_DEBUG_WIN |
| | 1225 | comm_debug_show(frame->buf); |
| | 1226 | getchar(); |
| | 1227 | #endif |
| | 1228 | |
| | 1229 | framesProcessed++; |
| | 1230 | delete[] rowMax; |
| | 1231 | delete[] colMax; |
| | 1232 | } |
| | 1233 | |
| | 1234 | void NextgenCommDetector::ClearAllMaps(void) |
| | 1235 | { |
| | 1236 | LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::ClearAllMaps()"); |
| | 1237 | |
| | 1238 | frameInfo.clear(); |
| | 1239 | blankFrameMap.clear(); |
| | 1240 | blankCommMap.clear(); |
| | 1241 | blankCommBreakMap.clear(); |
| | 1242 | sceneMap.clear(); |
| | 1243 | sceneCommBreakMap.clear(); |
| | 1244 | audioCommBreakMap.clear(); |
| | 1245 | commBreakMap.clear(); |
| | 1246 | } |
| | 1247 | |
| | 1248 | void NextgenCommDetector::GetBlankCommMap(frm_dir_map_t &comms) |
| | 1249 | { |
| | 1250 | LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetBlankCommMap()"); |
| | 1251 | |
| | 1252 | if (blankCommMap.isEmpty()) |
| | 1253 | BuildBlankFrameCommList(); |
| | 1254 | |
| | 1255 | comms = blankCommMap; |
| | 1256 | } |
| | 1257 | |
| | 1258 | void NextgenCommDetector::GetBlankCommBreakMap(frm_dir_map_t &comms) |
| | 1259 | { |
| | 1260 | LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetBlankCommBreakMap()"); |
| | 1261 | |
| | 1262 | if (blankCommBreakMap.isEmpty()) |
| | 1263 | BuildBlankFrameCommList(); |
| | 1264 | |
| | 1265 | comms = blankCommBreakMap; |
| | 1266 | } |
| | 1267 | |
| | 1268 | void NextgenCommDetector::GetAudioCommBreakMap(frm_dir_map_t &comms) |
| | 1269 | { |
| | 1270 | LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetAudioCommBreakMap()"); |
| | 1271 | |
| | 1272 | if (audioCommBreakMap.isEmpty()) |
| | 1273 | BuildAudioFrameCommList(); |
| | 1274 | |
| | 1275 | comms = audioCommBreakMap; |
| | 1276 | } |
| | 1277 | |
| | 1278 | void NextgenCommDetector::GetSceneChangeMap(frm_dir_map_t &scenes, |
| | 1279 | int64_t start_frame) |
| | 1280 | { |
| | 1281 | LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetSceneChangeMap()"); |
| | 1282 | |
| | 1283 | frm_dir_map_t::iterator it; |
| | 1284 | |
| | 1285 | if (start_frame == -1) |
| | 1286 | scenes.clear(); |
| | 1287 | |
| | 1288 | for (it = sceneMap.begin(); it != sceneMap.end(); ++it) |
| | 1289 | if ((start_frame == -1) || ((int64_t)it.key() >= start_frame)) |
| | 1290 | scenes[it.key()] = *it; |
| | 1291 | } |
| | 1292 | |
| | 1293 | frm_dir_map_t NextgenCommDetector::Combine2Maps(const frm_dir_map_t &a, |
| | 1294 | const frm_dir_map_t &b) const |
| | 1295 | { |
| | 1296 | LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::BuildMasterCommList()"); |
| | 1297 | |
| | 1298 | frm_dir_map_t newMap; |
| | 1299 | |
| | 1300 | if (a.size()) |
| | 1301 | { |
| | 1302 | frm_dir_map_t::const_iterator it = a.begin(); |
| | 1303 | for (; it != a.end(); ++it) |
| | 1304 | newMap[it.key()] = *it; |
| | 1305 | } |
| | 1306 | |
| | 1307 | if ((a.size() > 1) && |
| | 1308 | (b.size() > 1)) |
| | 1309 | { |
| | 1310 | // see if beginning of the recording looks like a commercial |
| | 1311 | frm_dir_map_t::const_iterator it_a; |
| | 1312 | frm_dir_map_t::const_iterator it_b; |
| | 1313 | |
| | 1314 | it_a = a.begin(); |
| | 1315 | it_b = b.begin(); |
| | 1316 | |
| | 1317 | if ((it_b.key() < 2) && (it_a.key() > 2)) |
| | 1318 | { |
| | 1319 | newMap.remove(it_a.key()); |
| | 1320 | newMap[0] = MARK_COMM_START; |
| | 1321 | } |
| | 1322 | |
| | 1323 | |
| | 1324 | // see if ending of recording looks like a commercial |
| | 1325 | frm_dir_map_t::const_iterator it; |
| | 1326 | uint64_t max_a = 0; |
| | 1327 | uint64_t max_b = 0; |
| | 1328 | |
| | 1329 | it = a.begin(); |
| | 1330 | for (; it != a.end(); ++it) |
| | 1331 | { |
| | 1332 | if ((*it == MARK_COMM_END) && (it.key() > max_a)) |
| | 1333 | max_a = it.key(); |
| | 1334 | } |
| | 1335 | |
| | 1336 | it = b.begin(); |
| | 1337 | for (; it != b.end(); ++it) |
| | 1338 | { |
| | 1339 | if ((*it == MARK_COMM_END) && (it.key() > max_b)) |
| | 1340 | max_b = it.key(); |
| | 1341 | } |
| | 1342 | |
| | 1343 | if ((max_a < (framesProcessed - 2)) && |
| | 1344 | (max_b > (framesProcessed - 2))) |
| | 1345 | { |
| | 1346 | newMap.remove(max_a); |
| | 1347 | newMap[framesProcessed] = MARK_COMM_END; |
| | 1348 | } |
| | 1349 | } |
| | 1350 | |
| | 1351 | if ((a.size() > 3) && |
| | 1352 | (b.size() > 1)) |
| | 1353 | { |
| | 1354 | frm_dir_map_t::const_iterator it_a; |
| | 1355 | frm_dir_map_t::const_iterator it_b; |
| | 1356 | |
| | 1357 | it_a = a.begin(); |
| | 1358 | ++it_a; |
| | 1359 | it_b = it_a; |
| | 1360 | ++it_b; |
| | 1361 | while (it_b != a.end()) |
| | 1362 | { |
| | 1363 | uint64_t fdiff = it_b.key() - it_a.key(); |
| | 1364 | bool allTrue = false; |
| | 1365 | |
| | 1366 | if (fdiff < (62 * fps)) |
| | 1367 | { |
| | 1368 | uint64_t f = it_a.key() + 1; |
| | 1369 | |
| | 1370 | allTrue = true; |
| | 1371 | |
| | 1372 | while ((f <= framesProcessed) && (f < it_b.key()) && (allTrue)) |
| | 1373 | allTrue = FrameIsInBreakMap(f++, b); |
| | 1374 | } |
| | 1375 | |
| | 1376 | if (allTrue) |
| | 1377 | { |
| | 1378 | newMap.remove(it_a.key()); |
| | 1379 | newMap.remove(it_b.key()); |
| | 1380 | } |
| | 1381 | |
| | 1382 | ++it_a; ++it_a; |
| | 1383 | ++it_b; |
| | 1384 | if (it_b != a.end()) |
| | 1385 | ++it_b; |
| | 1386 | } |
| | 1387 | } |
| | 1388 | |
| | 1389 | return newMap; |
| | 1390 | } |
| | 1391 | |
| | 1392 | void NextgenCommDetector::UpdateFrameBlock(PFrameBlock fbp, |
| | 1393 | NGFrameInfoEntry& finfo, |
| | 1394 | int format, int aspect) |
| | 1395 | { |
| | 1396 | int value = 0; |
| | 1397 | |
| | 1398 | value = finfo.flagMask; |
| | 1399 | |
| | 1400 | if (value & COMM_FRAME_BLANK) |
| | 1401 | fbp->bfCount++; |
| | 1402 | |
| | 1403 | if (value & COMM_FRAME_LOGO_PRESENT) |
| | 1404 | fbp->logoCount++; |
| | 1405 | |
| | 1406 | if (value & COMM_FRAME_RATING_SYMBOL) |
| | 1407 | fbp->ratingCount++; |
| | 1408 | |
| | 1409 | if (value & COMM_FRAME_SCENE_CHANGE) |
| | 1410 | fbp->scCount++; |
| | 1411 | |
| | 1412 | if (value & COMM_FRAME_NO_AUDIO) |
| | 1413 | fbp->saCount++; |
| | 1414 | |
| | 1415 | if (value & COMM_FRAME_AUDIO_CHANGE) |
| | 1416 | fbp->acCount++; |
| | 1417 | |
| | 1418 | if (value & COMM_FRAME_SUBTITLE_PRESENT) |
| | 1419 | fbp->subCount++; |
| | 1420 | |
| | 1421 | fbp->aPowerAvg += finfo.audioPower; |
| | 1422 | |
| | 1423 | if (finfo.format == format) |
| | 1424 | fbp->formatMatch++; |
| | 1425 | |
| | 1426 | if (finfo.aspect == aspect) |
| | 1427 | fbp->aspectMatch++; |
| | 1428 | } |
| | 1429 | |
| | 1430 | NextgenCommDetector::FrameBlock::FrameBlock() |
| | 1431 | { |
| | 1432 | start = 0; |
| | 1433 | end = 0; |
| | 1434 | frames = 0; |
| | 1435 | length = 0; |
| | 1436 | bfCount = 0; |
| | 1437 | saCount = 0; |
| | 1438 | aPowerAvg = 0; |
| | 1439 | acCount = 0; |
| | 1440 | logoCount = 0; |
| | 1441 | subCount = 0; |
| | 1442 | ratingCount = 0; |
| | 1443 | scCount = 0; |
| | 1444 | scRate = 0.0; |
| | 1445 | formatMatch = 0; |
| | 1446 | aspectMatch = 0; |
| | 1447 | segmentScore = 0; |
| | 1448 | transitionScore = 0; |
| | 1449 | } |
| | 1450 | |
| | 1451 | void NextgenCommDetector::FrameBlock::Merge(FrameBlock& o) |
| | 1452 | { |
| | 1453 | if (start <= o.start) |
| | 1454 | { |
| | 1455 | end = o.end; |
| | 1456 | subBlock.insert(subBlock.end(), o.subBlock.begin(), o.subBlock.end()); |
| | 1457 | } |
| | 1458 | else |
| | 1459 | { |
| | 1460 | start = o.start; |
| | 1461 | subBlock.insert(subBlock.begin(), o.subBlock.begin(), o.subBlock.end()); |
| | 1462 | } |
| | 1463 | |
| | 1464 | bfCount += o.bfCount; |
| | 1465 | saCount += o.saCount; |
| | 1466 | acCount += o.acCount; |
| | 1467 | logoCount += o.logoCount; |
| | 1468 | subCount += o.subCount; |
| | 1469 | ratingCount += o.ratingCount; |
| | 1470 | scCount += o.scCount; |
| | 1471 | formatMatch += o.formatMatch; |
| | 1472 | aspectMatch += o.aspectMatch; |
| | 1473 | segmentScore += o.segmentScore; |
| | 1474 | transitionScore += o.transitionScore; |
| | 1475 | |
| | 1476 | aPowerAvg = aPowerAvg*frames + o.aPowerAvg*o.frames; |
| | 1477 | scRate = scRate*frames + o.scRate*o.frames; |
| | 1478 | |
| | 1479 | frames += o.frames; |
| | 1480 | length += o.length; |
| | 1481 | |
| | 1482 | aPowerAvg /= frames; |
| | 1483 | scRate /= frames; |
| | 1484 | |
| | 1485 | } |
| | 1486 | |
| | 1487 | const char * NextgenCommDetector::FrameBlockHeader1() const |
| | 1488 | { |
| | 1489 | return "Block StTime StFrm EndFrm Frames Secs " |
| | 1490 | "Bf Lg Cnt ST Cnt RT Cnt SC Cnt SC Rt FmtMch AspMch SA Cnt AC Cnt AudPower TScore SScore"; |
| | 1491 | } |
| | 1492 | |
| | 1493 | const char * NextgenCommDetector::FrameBlockHeader2() const |
| | 1494 | { |
| | 1495 | return "----- ------ ------ ------ ------ ------- " |
| | 1496 | "--- ------ ------ ------ ------ ----- ------ ------ ------ ------ -------- ------ ------"; |
| | 1497 | } |
| | 1498 | |
| | 1499 | const QString NextgenCommDetector::FrameBlockStr(int curBlock, PFrameBlock fbp, int *score) const |
| | 1500 | { |
| | 1501 | #define STATS_PRINT_SPEC " %3d:%02d %6ld %6ld %6ld %7.2f %3d %6d %6d %6d %6d " \ |
| | 1502 | "%5.2f %6d %6d %6d %6d %8f %6d %6d" |
| | 1503 | #define STATS_PRINT_PARAMS \ |
| | 1504 | (int)(fbp->start / fps) / 60, \ |
| | 1505 | (int)((fbp->start / fps )) % 60, \ |
| | 1506 | fbp->start, fbp->end, fbp->frames, fbp->length, \ |
| | 1507 | fbp->bfCount, fbp->logoCount, fbp->subCount, fbp->ratingCount, \ |
| | 1508 | fbp->scCount, fbp->scRate, fbp->formatMatch, \ |
| | 1509 | fbp->aspectMatch, \ |
| | 1510 | fbp->saCount, fbp->acCount, fbp->aPowerAvg, \ |
| | 1511 | fbp->transitionScore, score?*score:fbp->segmentScore |
| | 1512 | |
| | 1513 | QString msg; |
| | 1514 | if (curBlock < 0) |
| | 1515 | { |
| | 1516 | msg.sprintf(" NOW" STATS_PRINT_SPEC , |
| | 1517 | STATS_PRINT_PARAMS ); |
| | 1518 | } |
| | 1519 | else |
| | 1520 | { |
| | 1521 | msg.sprintf("%5d" STATS_PRINT_SPEC , |
| | 1522 | curBlock, STATS_PRINT_PARAMS ); |
| | 1523 | } |
| | 1524 | return msg; |
| | 1525 | #undef STATS_PRINT_SPEC |
| | 1526 | #undef STATS_PRINT_PARAMS |
| | 1527 | } |
| | 1528 | |
| | 1529 | void NextgenCommDetector::BuildAllMethodsCommList(void) |
| | 1530 | { |
| | 1531 | LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::BuildAllMethodsCommList()"); |
| | 1532 | |
| | 1533 | vector< PFrameBlock > cfblock; |
| | 1534 | vector< PFrameBlock > fblock; |
| | 1535 | PFrameBlock fbp; |
| | 1536 | PFrameBlock cfbp; |
| | 1537 | |
| | 1538 | int value = 0; |
| | 1539 | int curBlock = 0; |
| | 1540 | int maxBlock = 0; |
| | 1541 | int lastScore = 0; |
| | 1542 | int thisScore = 0; |
| | 1543 | int nextScore = 0; |
| | 1544 | uint64_t curFrame = 0; |
| | 1545 | int64_t breakStart = 0; |
| | 1546 | uint64_t lastStart = 0; |
| | 1547 | uint64_t lastEnd = 0; |
| | 1548 | int64_t firstLogoFrame = -1; |
| | 1549 | bool lastFrameWasBlank = false; |
| | 1550 | bool lastFrameWasSilent = false; |
| | 1551 | uint64_t formatFrames = 0; |
| | 1552 | int format = COMM_FORMAT_NORMAL; |
| | 1553 | uint64_t aspectFrames = 0; |
| | 1554 | int aspect = COMM_ASPECT_NORMAL; |
| | 1555 | QString msg; |
| | 1556 | uint64_t formatCounts[COMM_FORMAT_MAX]; |
| | 1557 | frm_dir_map_t tmpCommMap; |
| | 1558 | frm_dir_map_t::iterator it; |
| | 1559 | |
| | 1560 | bool audioMethod = (commDetectMethod & COMM_DETECT_AUDIO); |
| | 1561 | |
| | 1562 | commBreakMap.clear(); |
| | 1563 | |
| | 1564 | curBlock = 0; |
| | 1565 | curFrame = 0; |
| | 1566 | |
| | 1567 | #if 0 |
| | 1568 | cfbp = PFrameBlock(new FrameBlock); |
| | 1569 | cfblock.push_back(cfbp); |
| | 1570 | fbp = PFrameBlock(new FrameBlock); |
| | 1571 | fbp->transitionScore = 5; // first one is always a transition |
| | 1572 | cfbp->subBlock.push_back(fbp); |
| | 1573 | fblock.push_back(fbp); |
| | 1574 | #endif |
| | 1575 | |
| | 1576 | value = frameInfo[curFrame].flagMask; |
| | 1577 | lastFrameWasSilent = !(value & COMM_FRAME_NO_AUDIO); |
| | 1578 | lastFrameWasBlank = !(value & COMM_FRAME_BLANK); |
| | 1579 | |
| | 1580 | if (decoderFoundAspectChanges) |
| | 1581 | { |
| | 1582 | for (int64_t i = preRoll; |
| | 1583 | i < ((int64_t)framesProcessed - (int64_t)postRoll); i++) |
| | 1584 | { |
| | 1585 | if ((frameInfo.contains(i)) && |
| | 1586 | (frameInfo[i].aspect == COMM_ASPECT_NORMAL)) |
| | 1587 | aspectFrames++; |
| | 1588 | } |
| | 1589 | |
| | 1590 | if (aspectFrames < ((framesProcessed - preRoll - postRoll) / 2)) |
| | 1591 | { |
| | 1592 | aspect = COMM_ASPECT_WIDE; |
| | 1593 | aspectFrames = framesProcessed - preRoll - postRoll - aspectFrames; |
| | 1594 | } |
| | 1595 | } |
| | 1596 | else |
| | 1597 | { |
| | 1598 | memset(&formatCounts, 0, sizeof(formatCounts)); |
| | 1599 | |
| | 1600 | for(int64_t i = preRoll; |
| | 1601 | i < ((int64_t)framesProcessed - (int64_t)postRoll); i++ ) |
| | 1602 | if ((frameInfo.contains(i)) && |
| | 1603 | (frameInfo[i].format >= 0) && |
| | 1604 | (frameInfo[i].format < COMM_FORMAT_MAX)) |
| | 1605 | formatCounts[frameInfo[i].format]++; |
| | 1606 | |
| | 1607 | for(int i = 0; i < COMM_FORMAT_MAX; i++) |
| | 1608 | { |
| | 1609 | if (formatCounts[i] > formatFrames) |
| | 1610 | { |
| | 1611 | format = i; |
| | 1612 | formatFrames = formatCounts[i]; |
| | 1613 | } |
| | 1614 | } |
| | 1615 | } |
| | 1616 | |
| | 1617 | while (curFrame <= framesProcessed) |
| | 1618 | { |
| | 1619 | value = frameInfo[curFrame].flagMask; |
| | 1620 | |
| | 1621 | int newBlockMask = 0; |
| | 1622 | bool lastSilentFrameState = lastFrameWasSilent; |
| | 1623 | lastFrameWasSilent = value & COMM_FRAME_NO_AUDIO; |
| | 1624 | |
| | 1625 | bool lastBlankFrameState = lastFrameWasBlank; |
| | 1626 | lastFrameWasBlank = value & COMM_FRAME_BLANK; |
| | 1627 | if (audioMethod) |
| | 1628 | { |
| | 1629 | // transition only when both are set |
| | 1630 | newBlockMask = ((lastSilentFrameState & lastBlankFrameState) != (lastFrameWasSilent & lastFrameWasBlank))?4:0; |
| | 1631 | |
| | 1632 | if ((frameInfo[curFrame].sceneChangePercent < 50) && lastFrameWasSilent && !lastFrameWasBlank) |
| | 1633 | { |
| | 1634 | newBlockMask |= 2; |
| | 1635 | } |
| | 1636 | } |
| | 1637 | else |
| | 1638 | { |
| | 1639 | newBlockMask |= (lastBlankFrameState != lastFrameWasBlank); |
| | 1640 | } |
| | 1641 | |
| | 1642 | if (newBlockMask) |
| | 1643 | { |
| | 1644 | //LOG(VB_COMMFLAG, LOG_INFO, QString("nbm %1 %2 %3 %4").arg(curFrame).arg(lastSilentFrameState).arg(lastFrameWasBlank).arg(newBlockMask)); |
| | 1645 | if (fbp) |
| | 1646 | { |
| | 1647 | fbp->end = curFrame - 1; |
| | 1648 | fbp->frames = fbp->end - fbp->start + 1; |
| | 1649 | fbp->length = fbp->frames / fps; |
| | 1650 | |
| | 1651 | fbp->aPowerAvg /= fbp->frames; |
| | 1652 | |
| | 1653 | if ((fbp->scCount) && (fbp->length > 1.05)) |
| | 1654 | fbp->scRate = fbp->scCount / fbp->length; |
| | 1655 | } |
| | 1656 | |
| | 1657 | curBlock++; |
| | 1658 | |
| | 1659 | fbp = PFrameBlock(new FrameBlock); |
| | 1660 | fblock.push_back(fbp); |
| | 1661 | if (((newBlockMask & 4) && (lastFrameWasSilent && lastFrameWasBlank)) || !cfbp) |
| | 1662 | { |
| | 1663 | if (cfbp) |
| | 1664 | { |
| | 1665 | cfbp->end = curFrame - 1; |
| | 1666 | cfbp->frames = cfbp->end - cfbp->start + 1; |
| | 1667 | cfbp->length = cfbp->frames / fps; |
| | 1668 | cfbp->aPowerAvg /= cfbp->frames; |
| | 1669 | |
| | 1670 | if ((cfbp->scCount) && (cfbp->length > 1.05)) |
| | 1671 | cfbp->scRate = cfbp->scCount / cfbp->length; |
| | 1672 | } |
| | 1673 | |
| | 1674 | cfbp = PFrameBlock(new FrameBlock); |
| | 1675 | cfblock.push_back(cfbp); |
| | 1676 | |
| | 1677 | //fbp->transitionScore = 10; |
| | 1678 | cfbp->start = curFrame; |
| | 1679 | } |
| | 1680 | cfbp->subBlock.push_back(fbp); |
| | 1681 | #if 0 |
| | 1682 | cfbp->frames += fbp->frames; |
| | 1683 | cfbp->aPowerAvg += fbp->aPowerAvg; |
| | 1684 | cfbp->bfCount += fbp->bfCount; |
| | 1685 | cfbp->scCount += fbp->scCount; |
| | 1686 | cfbp->saCount += fbp->saCount; |
| | 1687 | cfbp->acCount += fbp->acCount; |
| | 1688 | cfbp->logoCount += fbp->logoCount; |
| | 1689 | cfbp->ratingCount += fbp->ratingCount; |
| | 1690 | cfbp->formatMatch += fbp->formatMatch; |
| | 1691 | cfbp->aspectMatch += fbp->aspectMatch; |
| | 1692 | #endif |
| | 1693 | |
| | 1694 | fbp->start = curFrame; |
| | 1695 | } |
| | 1696 | UpdateFrameBlock(fbp, frameInfo[curFrame], format, aspect); |
| | 1697 | UpdateFrameBlock(cfbp, frameInfo[curFrame], format, aspect); |
| | 1698 | |
| | 1699 | if ((value & COMM_FRAME_LOGO_PRESENT) && |
| | 1700 | (firstLogoFrame == -1)) |
| | 1701 | firstLogoFrame = curFrame; |
| | 1702 | |
| | 1703 | curFrame++; |
| | 1704 | } |
| | 1705 | |
| | 1706 | fbp->end = curFrame - 1; |
| | 1707 | fbp->frames = fbp->end - fbp->start + 1; |
| | 1708 | fbp->length = fbp->frames / fps; |
| | 1709 | |
| | 1710 | fbp->aPowerAvg /= fbp->frames; |
| | 1711 | |
| | 1712 | if ((fbp->scCount) && (fbp->length > 1.05)) |
| | 1713 | fbp->scRate = fbp->scCount / fbp->length; |
| | 1714 | |
| | 1715 | cfbp->end = curFrame - 1; |
| | 1716 | cfbp->frames = cfbp->end - cfbp->start + 1; |
| | 1717 | cfbp->length = cfbp->frames / fps; |
| | 1718 | cfbp->aPowerAvg /= cfbp->frames; |
| | 1719 | |
| | 1720 | if ((cfbp->scCount) && (cfbp->length > 1.05)) |
| | 1721 | cfbp->scRate = cfbp->scCount / cfbp->length; |
| | 1722 | |
| | 1723 | maxBlock = curBlock; |
| | 1724 | curBlock = 0; |
| | 1725 | lastScore = 0; |
| | 1726 | |
| | 1727 | LOG(VB_COMMFLAG, LOG_INFO, "Initial Block pass"); |
| | 1728 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1()); |
| | 1729 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2()); |
| | 1730 | while (curBlock < maxBlock) |
| | 1731 | { |
| | 1732 | fbp = fblock[curBlock]; |
| | 1733 | |
| | 1734 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, fbp)); |
| | 1735 | |
| | 1736 | if ((fbp->bfCount == fbp->saCount) && (fbp->bfCount == fbp->frames) && (fbp->frames > 1)) |
| | 1737 | { |
| | 1738 | fbp->transitionScore += 10; |
| | 1739 | goto endInitialPass; |
| | 1740 | } |
| | 1741 | |
| | 1742 | if (fbp->frames > fps) |
| | 1743 | { |
| | 1744 | if (verboseDebugging) |
| | 1745 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1746 | QString(" FRAMES > %1").arg(fps)); |
| | 1747 | |
| | 1748 | if (fbp->length > commDetectMaxCommLength) |
| | 1749 | { |
| | 1750 | if (verboseDebugging) |
| | 1751 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1752 | " length > max comm length, +20"); |
| | 1753 | fbp->segmentScore += 20; |
| | 1754 | } |
| | 1755 | |
| | 1756 | if (fbp->length > commDetectMaxCommBreakLength) |
| | 1757 | { |
| | 1758 | if (verboseDebugging) |
| | 1759 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1760 | " length > max comm break length, +20"); |
| | 1761 | fbp->segmentScore += 20; |
| | 1762 | } |
| | 1763 | |
| | 1764 | if ((fbp->length > 4) && |
| | 1765 | (fbp->logoCount > (fbp->frames * 0.60)) && |
| | 1766 | (fbp->bfCount < (fbp->frames * 0.10))) |
| | 1767 | { |
| | 1768 | if (verboseDebugging) |
| | 1769 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1770 | " length > 4 && logoCount > frames * 0.60 && " |
| | 1771 | "bfCount < frames * .10"); |
| | 1772 | if (fbp->length > commDetectMaxCommBreakLength) |
| | 1773 | { |
| | 1774 | if (verboseDebugging) |
| | 1775 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1776 | " length > max comm break length, +20"); |
| | 1777 | fbp->segmentScore += 20; |
| | 1778 | } |
| | 1779 | else |
| | 1780 | { |
| | 1781 | if (verboseDebugging) |
| | 1782 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1783 | " length <= max comm break length, +10"); |
| | 1784 | fbp->segmentScore += 10; |
| | 1785 | } |
| | 1786 | } |
| | 1787 | |
| | 1788 | if ((logoInfoAvailable) && |
| | 1789 | (fbp->logoCount < (fbp->frames * 0.50)) && |
| | 1790 | (fbp->bfCount < (fbp->frames * 0.10))) |
| | 1791 | { |
| | 1792 | if (verboseDebugging) |
| | 1793 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1794 | " logoInfoAvailable && logoCount < frames * .50 && blanks < frames * 0.1, " |
| | 1795 | "-10"); |
| | 1796 | fbp->segmentScore -= 10; |
| | 1797 | } |
| | 1798 | |
| | 1799 | if (fbp->ratingCount > (fbp->frames * 0.05)) |
| | 1800 | { |
| | 1801 | if (verboseDebugging) |
| | 1802 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1803 | " rating symbol present > 5% of time, +20"); |
| | 1804 | fbp->segmentScore += 20; |
| | 1805 | } |
| | 1806 | |
| | 1807 | if ((fbp->scRate > 1.0) && |
| | 1808 | (fbp->bfCount < (fbp->frames * 0.50)) && |
| | 1809 | (fbp->logoCount < (fbp->frames * .90))) |
| | 1810 | { |
| | 1811 | if (verboseDebugging) |
| | 1812 | LOG(VB_COMMFLAG, LOG_DEBUG, " scRate > 1.0, -10"); |
| | 1813 | fbp->segmentScore -= 10; |
| | 1814 | |
| | 1815 | if (fbp->scRate > 2.0) |
| | 1816 | { |
| | 1817 | if (verboseDebugging) |
| | 1818 | LOG(VB_COMMFLAG, LOG_DEBUG, " scRate > 2.0, -10"); |
| | 1819 | fbp->segmentScore -= 10; |
| | 1820 | } |
| | 1821 | } |
| | 1822 | |
| | 1823 | if ((!decoderFoundAspectChanges) && |
| | 1824 | (fbp->formatMatch < (fbp->frames * .10))) |
| | 1825 | { |
| | 1826 | if (verboseDebugging) |
| | 1827 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1828 | " < 10% of frames match show letter/pillar-box " |
| | 1829 | "format, -20"); |
| | 1830 | fbp->segmentScore -= 20; |
| | 1831 | } |
| | 1832 | |
| | 1833 | // check for common comm break lengths |
| | 1834 | for(QList<float>::iterator lit = commDetectCommLengths.begin(); lit != commDetectCommLengths.end(); ++lit) |
| | 1835 | { |
| | 1836 | if (abs(fbp->frames - ((*lit * fps) - fps/2 + 1)) <= (fps/2)) |
| | 1837 | { |
| | 1838 | if (verboseDebugging) |
| | 1839 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1840 | " block appears to be standard comm length, -10"); |
| | 1841 | fbp->segmentScore -= 10; |
| | 1842 | break; |
| | 1843 | } |
| | 1844 | } |
| | 1845 | } |
| | 1846 | else |
| | 1847 | { |
| | 1848 | if (verboseDebugging) |
| | 1849 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1850 | QString(" FRAMES <= %1").arg(fps)); |
| | 1851 | |
| | 1852 | if ((logoInfoAvailable) && |
| | 1853 | (fbp->start >= firstLogoFrame) && |
| | 1854 | (fbp->logoCount == 0)) |
| | 1855 | { |
| | 1856 | if (verboseDebugging) |
| | 1857 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1858 | " logoInfoAvailable && logoCount == 0, -10"); |
| | 1859 | fbp->segmentScore -= 10; |
| | 1860 | } |
| | 1861 | |
| | 1862 | if ((!decoderFoundAspectChanges) && |
| | 1863 | (fbp->formatMatch < (fbp->frames * .10))) |
| | 1864 | { |
| | 1865 | if (verboseDebugging) |
| | 1866 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1867 | " < 10% of frames match show letter/pillar-box " |
| | 1868 | "format, -10"); |
| | 1869 | fbp->segmentScore -= 10; |
| | 1870 | } |
| | 1871 | |
| | 1872 | if (fbp->ratingCount > (fbp->frames * 0.25)) |
| | 1873 | { |
| | 1874 | if (verboseDebugging) |
| | 1875 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1876 | " rating symbol present > 25% of time, +10"); |
| | 1877 | fbp->segmentScore += 10; |
| | 1878 | } |
| | 1879 | } |
| | 1880 | |
| | 1881 | if ((decoderFoundAspectChanges) && |
| | 1882 | (fbp->aspectMatch < (fbp->frames * .10))) |
| | 1883 | { |
| | 1884 | if (verboseDebugging) |
| | 1885 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1886 | " < 10% of frames match show aspect, -20"); |
| | 1887 | fbp->segmentScore -= 20; |
| | 1888 | } |
| | 1889 | |
| | 1890 | endInitialPass: |
| | 1891 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(-1, fbp)); |
| | 1892 | |
| | 1893 | lastScore = fbp->segmentScore; |
| | 1894 | curBlock++; |
| | 1895 | } |
| | 1896 | |
| | 1897 | LOG(VB_COMMFLAG, LOG_DEBUG, "============================================"); |
| | 1898 | LOG(VB_COMMFLAG, LOG_INFO, "Initial Comm Block pass"); |
| | 1899 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1()); |
| | 1900 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2()); |
| | 1901 | for(curBlock = 0; curBlock < (int)cfblock.size(); curBlock++) |
| | 1902 | { |
| | 1903 | cfbp = cfblock[curBlock]; |
| | 1904 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, cfbp)); |
| | 1905 | |
| | 1906 | if (cfbp->length >= commDetectMaxCommBreakLength) |
| | 1907 | { |
| | 1908 | // definitely program |
| | 1909 | cfbp->segmentScore += 100; |
| | 1910 | } |
| | 1911 | else if (cfbp->length >= (commDetectCommLengths[0] - 1)) |
| | 1912 | { |
| | 1913 | // may be commercial. check subBlocks |
| | 1914 | vector<PFrameBlock>::iterator sbi; |
| | 1915 | for(sbi=cfbp->subBlock.begin(); sbi != cfbp->subBlock.end(); ++sbi) |
| | 1916 | { |
| | 1917 | int frames = (*sbi)->frames; |
| | 1918 | // check for common comm break lengths |
| | 1919 | for(QList<float>::iterator lit = commDetectCommLengths.begin(); lit != commDetectCommLengths.end(); ++lit) |
| | 1920 | { |
| | 1921 | if (abs(frames - ((*lit * fps) - fps/2 + 1)) <= (fps/2)) |
| | 1922 | { |
| | 1923 | if (verboseDebugging) |
| | 1924 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1925 | " subblock appears to be standard comm length, -10"); |
| | 1926 | cfbp->segmentScore -= 10; |
| | 1927 | break; |
| | 1928 | } |
| | 1929 | } |
| | 1930 | } |
| | 1931 | } |
| | 1932 | else |
| | 1933 | { |
| | 1934 | // too short so try to coalesce |
| | 1935 | // probably silent credits (black with writing and no music) |
| | 1936 | cfbp->transitionScore += -10; // mark for merge |
| | 1937 | } |
| | 1938 | if (cfbp->subCount > 0) |
| | 1939 | { |
| | 1940 | if (verboseDebugging) |
| | 1941 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1942 | " subblock has subtitles, +5"); |
| | 1943 | cfbp->segmentScore += 5; |
| | 1944 | } |
| | 1945 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(-1, cfbp)); |
| | 1946 | } |
| | 1947 | |
| | 1948 | LOG(VB_COMMFLAG, LOG_DEBUG, "============================================"); |
| | 1949 | LOG(VB_COMMFLAG, LOG_INFO, "Merge same Comm Block pass"); |
| | 1950 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1()); |
| | 1951 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2()); |
| | 1952 | for(curBlock = 0; curBlock < (int)cfblock.size(); curBlock++) |
| | 1953 | { |
| | 1954 | cfbp = cfblock[curBlock]; |
| | 1955 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, cfbp)); |
| | 1956 | |
| | 1957 | // next one exists |
| | 1958 | if ((curBlock + 1) < (int)cfblock.size()) |
| | 1959 | { |
| | 1960 | PFrameBlock cfbp2 = cfblock[curBlock+1]; |
| | 1961 | if (curBlock == 0 && cfbp->segmentScore == 0 && cfbp2->segmentScore != 0) |
| | 1962 | { |
| | 1963 | cfbp->segmentScore += 5; |
| | 1964 | if (verboseDebugging) |
| | 1965 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1966 | " unlabelled first segment, set to opposite of 2nd for indexing"); |
| | 1967 | } |
| | 1968 | if ((cfbp->segmentScore < 0 && cfbp2->segmentScore == 0 && |
| | 1969 | (cfbp->length + cfbp2->length) >= commDetectMaxCommBreakLength)) |
| | 1970 | { |
| | 1971 | // cant be a comm as the length is over so mark next as program |
| | 1972 | cfbp2->segmentScore += 10; |
| | 1973 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1974 | " comm length + next length > commDetectMaxCommBreakLength, next +10"); |
| | 1975 | } |
| | 1976 | if ((cfbp->segmentScore < 0 && cfbp2->segmentScore < 0) || |
| | 1977 | (cfbp->segmentScore > 0 && cfbp2->segmentScore > 0) || |
| | 1978 | (cfbp->segmentScore == 0 && cfbp2->segmentScore == 0 && |
| | 1979 | cfbp->transitionScore < 0 && cfbp2->transitionScore < 0) || |
| | 1980 | (cfbp->segmentScore != 0 && cfbp2->segmentScore == 0 && |
| | 1981 | cfbp2->transitionScore < 0 && cfbp2->length <= 1) || |
| | 1982 | (cfbp->segmentScore == 0 && cfbp2->segmentScore != 0 && |
| | 1983 | cfbp->transitionScore < 0 && cfbp->length <= 1) |
| | 1984 | ) |
| | 1985 | { |
| | 1986 | if (verboseDebugging) |
| | 1987 | { |
| | 1988 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock+1, cfbp2)); |
| | 1989 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 1990 | " merge with next"); |
| | 1991 | } |
| | 1992 | cfbp->Merge(*cfbp2); |
| | 1993 | cfblock.erase(cfblock.begin()+curBlock+1); |
| | 1994 | // reprocess |
| | 1995 | curBlock--; |
| | 1996 | } |
| | 1997 | } |
| | 1998 | if (curBlock == (cfblock.size()-1) && curBlock > 0) |
| | 1999 | { |
| | 2000 | // last block has change of sign from previous block to fora a seek point |
| | 2001 | if (cfbp->segmentScore == 0 && cfblock[curBlock-1]->segmentScore != 0) |
| | 2002 | cfbp->segmentScore = - cfblock[curBlock-1]->segmentScore; |
| | 2003 | } |
| | 2004 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(-1, cfbp)); |
| | 2005 | } |
| | 2006 | |
| | 2007 | LOG(VB_COMMFLAG, LOG_DEBUG, "============================================"); |
| | 2008 | LOG(VB_COMMFLAG, LOG_INFO, "Merge Comm Block pass 2"); |
| | 2009 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1()); |
| | 2010 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2()); |
| | 2011 | for(curBlock = 0; curBlock < (int)cfblock.size(); curBlock++) |
| | 2012 | { |
| | 2013 | cfbp = cfblock[curBlock]; |
| | 2014 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, cfbp)); |
| | 2015 | |
| | 2016 | // next one exists |
| | 2017 | if ((curBlock + 1) < (int)cfblock.size()) |
| | 2018 | { |
| | 2019 | } |
| | 2020 | |
| | 2021 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(-1, cfbp)); |
| | 2022 | } |
| | 2023 | |
| | 2024 | curBlock = 0; |
| | 2025 | lastScore = 0; |
| | 2026 | |
| | 2027 | LOG(VB_COMMFLAG, LOG_DEBUG, "============================================"); |
| | 2028 | LOG(VB_COMMFLAG, LOG_INFO, "Second Block pass"); |
| | 2029 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1()); |
| | 2030 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2()); |
| | 2031 | while (curBlock < maxBlock) |
| | 2032 | { |
| | 2033 | fbp = fblock[curBlock]; |
| | 2034 | |
| | 2035 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, fbp)); |
| | 2036 | |
| | 2037 | if ((curBlock >= 0) && (curBlock < (maxBlock-1))) |
| | 2038 | { |
| | 2039 | nextScore = fblock[curBlock + 1]->segmentScore; |
| | 2040 | |
| | 2041 | if ((lastScore < 0) && (nextScore < 0) && (fbp->length < 35)) |
| | 2042 | { |
| | 2043 | if (verboseDebugging) |
| | 2044 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 2045 | " lastScore < 0 && nextScore < 0 " |
| | 2046 | "&& length < 35, setting -10"); |
| | 2047 | fbp->segmentScore -= 10; |
| | 2048 | } |
| | 2049 | |
| | 2050 | if ((fbp->bfCount > (fbp->frames * 0.95)) && |
| | 2051 | (fbp->frames < (2*fps)) && |
| | 2052 | (lastScore < 0 && nextScore < 0)) |
| | 2053 | { |
| | 2054 | if (verboseDebugging) |
| | 2055 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 2056 | " blanks > frames * 0.95 && frames < 2*fps && " |
| | 2057 | "lastScore < 0 && nextScore < 0, setting -10"); |
| | 2058 | fbp->segmentScore -= 10; |
| | 2059 | } |
| | 2060 | if ((fbp->saCount > (fbp->frames * 0.95)) && |
| | 2061 | (fbp->frames < (2*fps)) && |
| | 2062 | (lastScore < 0 && nextScore < 0)) |
| | 2063 | { |
| | 2064 | if (verboseDebugging) |
| | 2065 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 2066 | " silent > frames * 0.95 && frames < 2*fps && " |
| | 2067 | "lastScore < 0 && nextScore < 0, setting -10"); |
| | 2068 | fbp->segmentScore -= 10; |
| | 2069 | } |
| | 2070 | |
| | 2071 | if ((fbp->frames < (120*fps)) && |
| | 2072 | (lastScore < 0) && |
| | 2073 | (fbp->segmentScore > 0) && |
| | 2074 | (fbp->segmentScore < 20) && |
| | 2075 | (nextScore < 0)) |
| | 2076 | { |
| | 2077 | if (verboseDebugging) |
| | 2078 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 2079 | " frames < 120 * fps && (-20 < lastScore < 0) && " |
| | 2080 | "thisScore > 0 && nextScore < 0, setting score = -10"); |
| | 2081 | fbp->segmentScore = -10; |
| | 2082 | } |
| | 2083 | |
| | 2084 | if ((fbp->frames < (30*fps)) && |
| | 2085 | (lastScore > 0) && |
| | 2086 | (fbp->segmentScore < 0) && |
| | 2087 | (fbp->segmentScore > -20) && |
| | 2088 | (nextScore > 0)) |
| | 2089 | { |
| | 2090 | if (verboseDebugging) |
| | 2091 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 2092 | " frames < 30 * fps && (0 < lastScore < 20) && " |
| | 2093 | "thisScore < 0 && nextScore > 0, setting score = 10"); |
| | 2094 | fbp->segmentScore = 10; |
| | 2095 | } |
| | 2096 | } |
| | 2097 | |
| | 2098 | if ((fbp->segmentScore == 0) && (lastScore > 30)) |
| | 2099 | { |
| | 2100 | int offset = 1; |
| | 2101 | while(((curBlock + offset) < maxBlock) && |
| | 2102 | (fblock[curBlock + offset]->frames < (2 * fps)) && |
| | 2103 | (fblock[curBlock + offset]->segmentScore == 0)) |
| | 2104 | offset++; |
| | 2105 | |
| | 2106 | if ((curBlock + offset) < maxBlock) |
| | 2107 | { |
| | 2108 | offset--; |
| | 2109 | if (fblock[curBlock + offset + 1]->segmentScore > 0) |
| | 2110 | { |
| | 2111 | for (; offset >= 0; offset--) |
| | 2112 | { |
| | 2113 | fblock[curBlock + offset]->segmentScore += 10; |
| | 2114 | if (verboseDebugging) |
| | 2115 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 2116 | QString(" Setting block %1 score +10") |
| | 2117 | .arg(curBlock+offset)); |
| | 2118 | } |
| | 2119 | } |
| | 2120 | else if (fblock[curBlock + offset + 1]->segmentScore < 0) |
| | 2121 | { |
| | 2122 | for (; offset >= 0; offset--) |
| | 2123 | { |
| | 2124 | fblock[curBlock + offset]->segmentScore -= 10; |
| | 2125 | if (verboseDebugging) |
| | 2126 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 2127 | QString(" Setting block %1 score -10") |
| | 2128 | .arg(curBlock+offset)); |
| | 2129 | } |
| | 2130 | } |
| | 2131 | } |
| | 2132 | } |
| | 2133 | |
| | 2134 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(-1, fbp)); |
| | 2135 | |
| | 2136 | lastScore = fbp->segmentScore; |
| | 2137 | curBlock++; |
| | 2138 | } |
| | 2139 | |
| | 2140 | LOG(VB_COMMFLAG, LOG_DEBUG, "============================================"); |
| | 2141 | LOG(VB_COMMFLAG, LOG_INFO, "FINAL Block stats"); |
| | 2142 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1()); |
| | 2143 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2()); |
| | 2144 | curBlock = 0; |
| | 2145 | lastScore = 0; |
| | 2146 | breakStart = -1; |
| | 2147 | while (curBlock < maxBlock) |
| | 2148 | { |
| | 2149 | fbp = fblock[curBlock]; |
| | 2150 | thisScore = fbp->segmentScore; |
| | 2151 | |
| | 2152 | if ((breakStart >= 0) && |
| | 2153 | ((fbp->end - breakStart) > (commDetectMaxCommBreakLength * fps))) |
| | 2154 | { |
| | 2155 | if (((fbp->start - breakStart) > |
| | 2156 | (commDetectMinCommBreakLength * fps)) || |
| | 2157 | (breakStart == 0)) |
| | 2158 | { |
| | 2159 | if (verboseDebugging) |
| | 2160 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 2161 | QString("Closing commercial block at start of " |
| | 2162 | "frame block %1 with length %2, frame " |
| | 2163 | "block length of %3 frames would put comm " |
| | 2164 | "block length over max of %4 seconds.") |
| | 2165 | .arg(curBlock).arg(fbp->start - breakStart) |
| | 2166 | .arg(fbp->frames) |
| | 2167 | .arg(commDetectMaxCommBreakLength)); |
| | 2168 | |
| | 2169 | commBreakMap[breakStart] = MARK_COMM_START; |
| | 2170 | commBreakMap[fbp->start] = MARK_COMM_END; |
| | 2171 | lastStart = breakStart; |
| | 2172 | lastEnd = fbp->start; |
| | 2173 | breakStart = -1; |
| | 2174 | } |
| | 2175 | else |
| | 2176 | { |
| | 2177 | if (verboseDebugging) |
| | 2178 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 2179 | QString("Ignoring what appears to be commercial" |
| | 2180 | " block at frame %1 with length %2, " |
| | 2181 | "length of %3 frames would put comm " |
| | 2182 | "block length under min of %4 seconds.") |
| | 2183 | .arg(breakStart) |
| | 2184 | .arg(fbp->start - breakStart) |
| | 2185 | .arg(fbp->frames) |
| | 2186 | .arg(commDetectMinCommBreakLength)); |
| | 2187 | breakStart = -1; |
| | 2188 | } |
| | 2189 | } |
| | 2190 | if (thisScore == 0) |
| | 2191 | { |
| | 2192 | thisScore = lastScore; |
| | 2193 | } |
| | 2194 | else if (thisScore < 0) |
| | 2195 | { |
| | 2196 | if ((lastScore > 0) || (curBlock == 0)) |
| | 2197 | { |
| | 2198 | if ((fbp->start - lastEnd) < (commDetectMinShowLength * fps)) |
| | 2199 | { |
| | 2200 | commBreakMap.remove(lastStart); |
| | 2201 | commBreakMap.remove(lastEnd); |
| | 2202 | breakStart = lastStart; |
| | 2203 | |
| | 2204 | if (verboseDebugging) |
| | 2205 | { |
| | 2206 | if (breakStart) |
| | 2207 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 2208 | QString("ReOpening commercial block at " |
| | 2209 | "frame %1 because show less than " |
| | 2210 | "%2 seconds") |
| | 2211 | .arg(breakStart) |
| | 2212 | .arg(commDetectMinShowLength)); |
| | 2213 | else |
| | 2214 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 2215 | "Opening initial commercial block " |
| | 2216 | "at start of recording, block 0."); |
| | 2217 | } |
| | 2218 | } |
| | 2219 | else |
| | 2220 | { |
| | 2221 | breakStart = fbp->start; |
| | 2222 | |
| | 2223 | if (verboseDebugging) |
| | 2224 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 2225 | QString("Starting new commercial block at " |
| | 2226 | "frame %1 from start of frame block %2") |
| | 2227 | .arg(fbp->start).arg(curBlock)); |
| | 2228 | } |
| | 2229 | } |
| | 2230 | else if (curBlock == (maxBlock-1)) |
| | 2231 | { |
| | 2232 | if ((fbp->end - breakStart) > |
| | 2233 | (commDetectMinCommBreakLength * fps)) |
| | 2234 | { |
| | 2235 | if (fbp->end <= |
| | 2236 | ((int64_t)framesProcessed - (int64_t)(2 * fps) - 2)) |
| | 2237 | { |
| | 2238 | if (verboseDebugging) |
| | 2239 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 2240 | QString("Closing final commercial block at " |
| | 2241 | "frame %1").arg(fbp->end)); |
| | 2242 | |
| | 2243 | commBreakMap[breakStart] = MARK_COMM_START; |
| | 2244 | commBreakMap[fbp->end] = MARK_COMM_END; |
| | 2245 | lastStart = breakStart; |
| | 2246 | lastEnd = fbp->end; |
| | 2247 | breakStart = -1; |
| | 2248 | } |
| | 2249 | } |
| | 2250 | else |
| | 2251 | { |
| | 2252 | if (verboseDebugging) |
| | 2253 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 2254 | QString("Ignoring what appears to be commercial" |
| | 2255 | " block at frame %1 with length %2, " |
| | 2256 | "length of %3 frames would put comm " |
| | 2257 | "block length under min of %4 seconds.") |
| | 2258 | .arg(breakStart) |
| | 2259 | .arg(fbp->start - breakStart) |
| | 2260 | .arg(fbp->frames) |
| | 2261 | .arg(commDetectMinCommBreakLength)); |
| | 2262 | breakStart = -1; |
| | 2263 | } |
| | 2264 | } |
| | 2265 | } |
| | 2266 | else if ((thisScore > 0) && |
| | 2267 | (lastScore < 0) && |
| | 2268 | (breakStart != -1)) |
| | 2269 | { |
| | 2270 | if (((fbp->start - breakStart) > |
| | 2271 | (commDetectMinCommBreakLength * fps)) || |
| | 2272 | (breakStart == 0)) |
| | 2273 | { |
| | 2274 | commBreakMap[breakStart] = MARK_COMM_START; |
| | 2275 | commBreakMap[fbp->start] = MARK_COMM_END; |
| | 2276 | lastStart = breakStart; |
| | 2277 | lastEnd = fbp->start; |
| | 2278 | |
| | 2279 | if (verboseDebugging) |
| | 2280 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 2281 | QString("Closing commercial block at frame %1") |
| | 2282 | .arg(fbp->start)); |
| | 2283 | } |
| | 2284 | else |
| | 2285 | { |
| | 2286 | if (verboseDebugging) |
| | 2287 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 2288 | QString("Ignoring what appears to be commercial " |
| | 2289 | "block at frame %1 with length %2, " |
| | 2290 | "length of %3 frames would put comm block " |
| | 2291 | "length under min of %4 seconds.") |
| | 2292 | .arg(breakStart) |
| | 2293 | .arg(fbp->start - breakStart) |
| | 2294 | .arg(fbp->frames) |
| | 2295 | .arg(commDetectMinCommBreakLength)); |
| | 2296 | } |
| | 2297 | breakStart = -1; |
| | 2298 | } |
| | 2299 | |
| | 2300 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, fbp, &thisScore)); |
| | 2301 | |
| | 2302 | lastScore = thisScore; |
| | 2303 | curBlock++; |
| | 2304 | } |
| | 2305 | |
| | 2306 | if ((breakStart != -1) && |
| | 2307 | (breakStart <= ((int64_t)framesProcessed - (int64_t)(2 * fps) - 2))) |
| | 2308 | { |
| | 2309 | if (verboseDebugging) |
| | 2310 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 2311 | QString("Closing final commercial block started at " |
| | 2312 | "block %1 and going to end of program. length " |
| | 2313 | "is %2 frames") |
| | 2314 | .arg(curBlock) |
| | 2315 | .arg((framesProcessed - breakStart - 1))); |
| | 2316 | |
| | 2317 | commBreakMap[breakStart] = MARK_COMM_START; |
| | 2318 | // Create what is essentially an open-ended final skip region |
| | 2319 | // by setting the end point 10 seconds past the end of the |
| | 2320 | // recording. |
| | 2321 | commBreakMap[framesProcessed + (10 * fps)] = MARK_COMM_END; |
| | 2322 | } |
| | 2323 | |
| | 2324 | // include/exclude blanks from comm breaks |
| | 2325 | tmpCommMap = commBreakMap; |
| | 2326 | commBreakMap.clear(); |
| | 2327 | |
| | 2328 | if (verboseDebugging) |
| | 2329 | LOG(VB_COMMFLAG, LOG_DEBUG, |
| | 2330 | "Adjusting start/end marks according to blanks."); |
| | 2331 | for (it = tmpCommMap.begin(); it != tmpCommMap.end(); ++it) |
| | 2332 | { |
| | 2333 | if (*it == MARK_COMM_START) |
| | 2334 | { |
| | 2335 | uint64_t lastStartLower = it.key(); |
| | 2336 | uint64_t lastStartUpper = it.key(); |
| | 2337 | while ((lastStartLower > 0) && |
| | 2338 | (frameInfo[lastStartLower - 1].flagMask & COMM_FRAME_BLANK)) |
| | 2339 | lastStartLower--; |
| | 2340 | while ((lastStartUpper < (framesProcessed - (2 * fps))) && |
| | 2341 | (frameInfo[lastStartUpper + 1].flagMask & COMM_FRAME_BLANK)) |
| | 2342 | lastStartUpper++; |
| | 2343 | uint64_t adj = (lastStartUpper - lastStartLower) / 2; |
| | 2344 | if (adj > MAX_BLANK_FRAMES) |
| | 2345 | adj = MAX_BLANK_FRAMES; |
| | 2346 | lastStart = lastStartLower + adj; |
| | 2347 | |
| | 2348 | if (verboseDebugging) |
| | 2349 | LOG(VB_COMMFLAG, LOG_DEBUG, QString("Start Mark: %1 -> %2") |
| | 2350 | .arg(it.key()).arg(lastStart)); |
| | 2351 | |
| | 2352 | commBreakMap[lastStart] = MARK_COMM_START; |
| | 2353 | } |
| | 2354 | else |
| | 2355 | { |
| | 2356 | uint64_t lastEndLower = it.key(); |
| | 2357 | uint64_t lastEndUpper = it.key(); |
| | 2358 | while ((lastEndUpper < (framesProcessed - (2 * fps))) && |
| | 2359 | (frameInfo[lastEndUpper + 1].flagMask & COMM_FRAME_BLANK)) |
| | 2360 | lastEndUpper++; |
| | 2361 | while ((lastEndLower > 0) && |
| | 2362 | (frameInfo[lastEndLower - 1].flagMask & COMM_FRAME_BLANK)) |
| | 2363 | lastEndLower--; |
| | 2364 | uint64_t adj = (lastEndUpper - lastEndLower) / 2; |
| | 2365 | if (adj > MAX_BLANK_FRAMES) |
| | 2366 | adj = MAX_BLANK_FRAMES; |
| | 2367 | lastEnd = lastEndUpper - adj; |
| | 2368 | |
| | 2369 | if (verboseDebugging) |
| | 2370 | LOG(VB_COMMFLAG, LOG_DEBUG, QString("End Mark : %1 -> %2") |
| | 2371 | .arg(it.key()).arg(lastEnd)); |
| | 2372 | |
| | 2373 | commBreakMap[lastEnd] = MARK_COMM_END; |
| | 2374 | } |
| | 2375 | } |
| | 2376 | |
| | 2377 | // new one |
| | 2378 | if ((commDetectMethod & COMM_DETECT_NG_OLD) == 0) |
| | 2379 | commBreakMap.clear(); |
| | 2380 | lastScore = 0; |
| | 2381 | LOG(VB_COMMFLAG, LOG_DEBUG, "============================================"); |
| | 2382 | LOG(VB_COMMFLAG, LOG_INFO, "Generate Comm Breaks pass"); |
| | 2383 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader1()); |
| | 2384 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockHeader2()); |
| | 2385 | cfbp = cfblock[0]; |
| | 2386 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(0, cfbp)); |
| | 2387 | // is first one a comm |
| | 2388 | if ((lastScore = cfbp->segmentScore) < 0) |
| | 2389 | { |
| | 2390 | if ((commDetectMethod & COMM_DETECT_NG_OLD) == 0) |
| | 2391 | commBreakMap[0] = MARK_COMM_START; |
| | 2392 | if (verboseDebugging) |
| | 2393 | LOG(VB_COMMFLAG, LOG_DEBUG, QString("Start Mark : %1") |
| | 2394 | .arg(0)); |
| | 2395 | } |
| | 2396 | for(curBlock = 1; curBlock < (int)cfblock.size(); curBlock++) |
| | 2397 | { |
| | 2398 | cfbp = cfblock[curBlock]; |
| | 2399 | LOG(VB_COMMFLAG, LOG_DEBUG, FrameBlockStr(curBlock, cfbp)); |
| | 2400 | if (cfbp->segmentScore != 0) |
| | 2401 | { |
| | 2402 | if (lastScore < 0 && cfbp->segmentScore > 0) |
| | 2403 | { |
| | 2404 | if ((commDetectMethod & COMM_DETECT_NG_OLD) == 0) |
| | 2405 | commBreakMap[cfbp->start + cfbp->subBlock[0]->frames/2] = MARK_COMM_END; |
| | 2406 | if (verboseDebugging) |
| | 2407 | LOG(VB_COMMFLAG, LOG_DEBUG, QString("End Mark : %1") |
| | 2408 | .arg(cfbp->start + cfbp->subBlock[0]->frames/2)); |
| | 2409 | } |
| | 2410 | if (lastScore > 0 && cfbp->segmentScore < 0) |
| | 2411 | { |
| | 2412 | if ((commDetectMethod & COMM_DETECT_NG_OLD) == 0) |
| | 2413 | commBreakMap[cfbp->start + cfbp->subBlock[0]->frames/2] = MARK_COMM_START; |
| | 2414 | if (verboseDebugging) |
| | 2415 | LOG(VB_COMMFLAG, LOG_DEBUG, QString("Start Mark : %1") |
| | 2416 | .arg(cfbp->start + cfbp->subBlock[0]->frames/2)); |
| | 2417 | } |
| | 2418 | lastScore = cfbp->segmentScore; |
| | 2419 | } |
| | 2420 | } |
| | 2421 | if (lastScore < 0) |
| | 2422 | { |
| | 2423 | if ((commDetectMethod & COMM_DETECT_NG_OLD) == 0) |
| | 2424 | commBreakMap[cfbp->end + fps * 2 + 1] = MARK_COMM_END; |
| | 2425 | if (verboseDebugging) |
| | 2426 | LOG(VB_COMMFLAG, LOG_DEBUG, QString("End Mark : %1") |
| | 2427 | .arg(cfbp->end + fps * 2 + 1)); |
| | 2428 | } |
| | 2429 | } |
| | 2430 | |
| | 2431 | |
| | 2432 | void NextgenCommDetector::BuildBlankFrameCommList(void) |
| | 2433 | { |
| | 2434 | LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::BuildBlankFrameCommList()"); |
| | 2435 | |
| | 2436 | long long *bframes = new long long[blankFrameMap.count()*2]; |
| | 2437 | long long *c_start = new long long[blankFrameMap.count()]; |
| | 2438 | long long *c_end = new long long[blankFrameMap.count()]; |
| | 2439 | int frames = 0; |
| | 2440 | int commercials = 0; |
| | 2441 | int i, x; |
| | 2442 | frm_dir_map_t::iterator it; |
| | 2443 | |
| | 2444 | blankCommMap.clear(); |
| | 2445 | |
| | 2446 | for (it = blankFrameMap.begin(); it != blankFrameMap.end(); ++it) |
| | 2447 | bframes[frames++] = it.key(); |
| | 2448 | |
| | 2449 | if (frames == 0) |
| | 2450 | { |
| | 2451 | delete[] c_start; |
| | 2452 | delete[] c_end; |
| | 2453 | delete[] bframes; |
| | 2454 | return; |
| | 2455 | } |
| | 2456 | |
| | 2457 | // detect individual commercials from blank frames |
| | 2458 | // commercial end is set to frame right before ending blank frame to |
| | 2459 | // account for instances with only a single blank frame between comms. |
| | 2460 | for(i = 0; i < frames; i++ ) |
| | 2461 | { |
| | 2462 | for(x=i+1; x < frames; x++ ) |
| | 2463 | { |
| | 2464 | // check for various length spots since some channels don't |
| | 2465 | // have blanks inbetween commercials just at the beginning and |
| | 2466 | // end of breaks |
| | 2467 | int gap_length = bframes[x] - bframes[i]; |
| | 2468 | if (((aggressiveDetection) && |
| | 2469 | ((abs((int)(gap_length - (5 * fps))) < 5 ) || |
| | 2470 | (abs((int)(gap_length - (10 * fps))) < 7 ) || |
| | 2471 | (abs((int)(gap_length - (15 * fps))) < 10 ) || |
| | 2472 | (abs((int)(gap_length - (20 * fps))) < 11 ) || |
| | 2473 | (abs((int)(gap_length - (30 * fps))) < 12 ) || |
| | 2474 | (abs((int)(gap_length - (40 * fps))) < 1 ) || |
| | 2475 | (abs((int)(gap_length - (45 * fps))) < 1 ) || |
| | 2476 | (abs((int)(gap_length - (60 * fps))) < 15 ) || |
| | 2477 | (abs((int)(gap_length - (90 * fps))) < 10 ) || |
| | 2478 | (abs((int)(gap_length - (120 * fps))) < 10 ))) || |
| | 2479 | ((!aggressiveDetection) && |
| | 2480 | ((abs((int)(gap_length - (5 * fps))) < 11 ) || |
| | 2481 | (abs((int)(gap_length - (10 * fps))) < 13 ) || |
| | 2482 | (abs((int)(gap_length - (15 * fps))) < 16 ) || |
| | 2483 | (abs((int)(gap_length - (20 * fps))) < 17 ) || |
| | 2484 | (abs((int)(gap_length - (30 * fps))) < 18 ) || |
| | 2485 | (abs((int)(gap_length - (40 * fps))) < 3 ) || |
| | 2486 | (abs((int)(gap_length - (45 * fps))) < 3 ) || |
| | 2487 | (abs((int)(gap_length - (60 * fps))) < 20 ) || |
| | 2488 | (abs((int)(gap_length - (90 * fps))) < 20 ) || |
| | 2489 | (abs((int)(gap_length - (120 * fps))) < 20 )))) |
| | 2490 | { |
| | 2491 | c_start[commercials] = bframes[i]; |
| | 2492 | c_end[commercials] = bframes[x] - 1; |
| | 2493 | commercials++; |
| | 2494 | i = x-1; |
| | 2495 | x = frames; |
| | 2496 | } |
| | 2497 | |
| | 2498 | if ((!aggressiveDetection) && |
| | 2499 | ((abs((int)(gap_length - (30 * fps))) < (int)(fps * 0.85)) || |
| | 2500 | (abs((int)(gap_length - (60 * fps))) < (int)(fps * 0.95)) || |
| | 2501 | (abs((int)(gap_length - (90 * fps))) < (int)(fps * 1.05)) || |
| | 2502 | (abs((int)(gap_length - (120 * fps))) < (int)(fps * 1.15))) && |
| | 2503 | ((x + 2) < frames) && |
| | 2504 | ((i + 2) < frames) && |
| | 2505 | ((bframes[i] + 1) == bframes[i+1]) && |
| | 2506 | ((bframes[x] + 1) == bframes[x+1])) |
| | 2507 | { |
| | 2508 | c_start[commercials] = bframes[i]; |
| | 2509 | c_end[commercials] = bframes[x]; |
| | 2510 | commercials++; |
| | 2511 | i = x; |
| | 2512 | x = frames; |
| | 2513 | } |
| | 2514 | } |
| | 2515 | } |
| | 2516 | |
| | 2517 | i = 0; |
| | 2518 | |
| | 2519 | // don't allow single commercial at head |
| | 2520 | // of show unless followed by another |
| | 2521 | if ((commercials > 1) && |
| | 2522 | (c_end[0] < (33 * fps)) && |
| | 2523 | (c_start[1] > (c_end[0] + 40 * fps))) |
| | 2524 | i = 1; |
| | 2525 | |
| | 2526 | // eliminate any blank frames at end of commercials |
| | 2527 | bool first_comm = true; |
| | 2528 | for(; i < (commercials-1); i++) |
| | 2529 | { |
| | 2530 | long long r = c_start[i]; |
| | 2531 | long long adjustment = 0; |
| | 2532 | |
| | 2533 | if ((r < (30 * fps)) && |
| | 2534 | (first_comm)) |
| | 2535 | r = 1; |
| | 2536 | |
| | 2537 | blankCommMap[r] = MARK_COMM_START; |
| | 2538 | |
| | 2539 | r = c_end[i]; |
| | 2540 | if ( i < (commercials-1)) |
| | 2541 | { |
| | 2542 | for(x = 0; x < (frames-1); x++) |
| | 2543 | if (bframes[x] == r) |
| | 2544 | break; |
| | 2545 | while((x < (frames-1)) && |
| | 2546 | ((bframes[x] + 1 ) == bframes[x+1]) && |
| | 2547 | (bframes[x+1] < c_start[i+1])) |
| | 2548 | { |
| | 2549 | r++; |
| | 2550 | x++; |
| | 2551 | } |
| | 2552 | |
| | 2553 | while((blankFrameMap.contains(r+1)) && |
| | 2554 | (c_start[i+1] != (r+1))) |
| | 2555 | { |
| | 2556 | r++; |
| | 2557 | adjustment++; |
| | 2558 | } |
| | 2559 | } |
| | 2560 | else |
| | 2561 | { |
| | 2562 | while(blankFrameMap.contains(r+1)) |
| | 2563 | { |
| | 2564 | r++; |
| | 2565 | adjustment++; |
| | 2566 | } |
| | 2567 | } |
| | 2568 | |
| | 2569 | adjustment /= 2; |
| | 2570 | if (adjustment > MAX_BLANK_FRAMES) |
| | 2571 | adjustment = MAX_BLANK_FRAMES; |
| | 2572 | r -= adjustment; |
| | 2573 | blankCommMap[r] = MARK_COMM_END; |
| | 2574 | first_comm = false; |
| | 2575 | } |
| | 2576 | |
| | 2577 | blankCommMap[c_start[i]] = MARK_COMM_START; |
| | 2578 | blankCommMap[c_end[i]] = MARK_COMM_END; |
| | 2579 | |
| | 2580 | delete[] c_start; |
| | 2581 | delete[] c_end; |
| | 2582 | delete[] bframes; |
| | 2583 | |
| | 2584 | LOG(VB_COMMFLAG, LOG_INFO, "Blank-Frame Commercial Map" ); |
| | 2585 | for(it = blankCommMap.begin(); it != blankCommMap.end(); ++it) |
| | 2586 | LOG(VB_COMMFLAG, LOG_INFO, QString(" %1:%2") |
| | 2587 | .arg(it.key()).arg(*it)); |
| | 2588 | |
| | 2589 | MergeBlankCommList(); |
| | 2590 | |
| | 2591 | LOG(VB_COMMFLAG, LOG_INFO, "Merged Blank-Frame Commercial Break Map" ); |
| | 2592 | for(it = blankCommBreakMap.begin(); it != blankCommBreakMap.end(); ++it) |
| | 2593 | LOG(VB_COMMFLAG, LOG_INFO, QString(" %1:%2") |
| | 2594 | .arg(it.key()).arg(*it)); |
| | 2595 | } |
| | 2596 | |
| | 2597 | void NextgenCommDetector::BuildAudioFrameCommList(void) |
| | 2598 | { |
| | 2599 | LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::BuildBlankFrameCommList()"); |
| | 2600 | |
| | 2601 | audioCommBreakMap.clear(); |
| | 2602 | // TODO |
| | 2603 | } |
| | 2604 | |
| | 2605 | void NextgenCommDetector::BuildSceneChangeCommList(void) |
| | 2606 | { |
| | 2607 | int section_start = -1; |
| | 2608 | int seconds = (int)(framesProcessed / fps); |
| | 2609 | int *sc_histogram = new int[seconds+1]; |
| | 2610 | |
| | 2611 | sceneCommBreakMap.clear(); |
| | 2612 | |
| | 2613 | memset(sc_histogram, 0, (seconds+1)*sizeof(int)); |
| | 2614 | for (uint64_t f = 1; f <= framesProcessed; f++) |
| | 2615 | { |
| | 2616 | if (sceneMap.contains(f)) |
| | 2617 | sc_histogram[(uint64_t)(f / fps)]++; |
| | 2618 | } |
| | 2619 | |
| | 2620 | for(long long s = 0; s < (seconds + 1); s++) |
| | 2621 | { |
| | 2622 | if (sc_histogram[s] > 2) |
| | 2623 | { |
| | 2624 | if (section_start == -1) |
| | 2625 | { |
| | 2626 | long long f = (long long)(s * fps); |
| | 2627 | for(int i = 0; i < fps; i++, f++) |
| | 2628 | { |
| | 2629 | if (sceneMap.contains(f)) |
| | 2630 | { |
| | 2631 | sceneCommBreakMap[f] = MARK_COMM_START; |
| | 2632 | i = (int)(fps) + 1; |
| | 2633 | } |
| | 2634 | } |
| | 2635 | } |
| | 2636 | |
| | 2637 | section_start = s; |
| | 2638 | } |
| | 2639 | |
| | 2640 | if ((section_start >= 0) && |
| | 2641 | (s > (section_start + 32))) |
| | 2642 | { |
| | 2643 | long long f = (long long)(section_start * fps); |
| | 2644 | bool found_end = false; |
| | 2645 | |
| | 2646 | for(int i = 0; i < fps; i++, f++) |
| | 2647 | { |
| | 2648 | if (sceneMap.contains(f)) |
| | 2649 | { |
| | 2650 | frm_dir_map_t::iterator dit = sceneCommBreakMap.find(f); |
| | 2651 | if (dit != sceneCommBreakMap.end()) |
| | 2652 | sceneCommBreakMap.erase(dit); |
| | 2653 | else |
| | 2654 | sceneCommBreakMap[f] = MARK_COMM_END; |
| | 2655 | i = (int)(fps) + 1; |
| | 2656 | found_end = true; |
| | 2657 | } |
| | 2658 | } |
| | 2659 | section_start = -1; |
| | 2660 | |
| | 2661 | if (!found_end) |
| | 2662 | { |
| | 2663 | f = (long long)(section_start * fps); |
| | 2664 | sceneCommBreakMap[f] = MARK_COMM_END; |
| | 2665 | } |
| | 2666 | } |
| | 2667 | } |
| | 2668 | delete[] sc_histogram; |
| | 2669 | |
| | 2670 | if (section_start >= 0) |
| | 2671 | sceneCommBreakMap[framesProcessed] = MARK_COMM_END; |
| | 2672 | |
| | 2673 | frm_dir_map_t deleteMap; |
| | 2674 | frm_dir_map_t::iterator it = sceneCommBreakMap.begin(); |
| | 2675 | frm_dir_map_t::iterator prev = it; |
| | 2676 | if (it != sceneCommBreakMap.end()) |
| | 2677 | { |
| | 2678 | ++it; |
| | 2679 | while (it != sceneCommBreakMap.end()) |
| | 2680 | { |
| | 2681 | if ((*it == MARK_COMM_END) && |
| | 2682 | (it.key() - prev.key()) < (30 * fps)) |
| | 2683 | { |
| | 2684 | deleteMap[it.key()] = MARK_CUT_START; |
| | 2685 | deleteMap[prev.key()] = MARK_CUT_START; |
| | 2686 | } |
| | 2687 | ++prev; |
| | 2688 | if (it != sceneCommBreakMap.end()) |
| | 2689 | ++it; |
| | 2690 | } |
| | 2691 | |
| | 2692 | frm_dir_map_t::iterator dit; |
| | 2693 | for (dit = deleteMap.begin(); dit != deleteMap.end(); ++dit) |
| | 2694 | sceneCommBreakMap.remove(dit.key()); |
| | 2695 | } |
| | 2696 | |
| | 2697 | LOG(VB_COMMFLAG, LOG_INFO, "Scene-Change Commercial Break Map" ); |
| | 2698 | for (it = sceneCommBreakMap.begin(); it != sceneCommBreakMap.end(); ++it) |
| | 2699 | { |
| | 2700 | LOG(VB_COMMFLAG, LOG_INFO, QString(" %1:%2") |
| | 2701 | .arg(it.key()).arg(*it)); |
| | 2702 | } |
| | 2703 | } |
| | 2704 | |
| | 2705 | |
| | 2706 | void NextgenCommDetector::BuildLogoCommList() |
| | 2707 | { |
| | 2708 | show_map_t showmap; |
| | 2709 | GetLogoCommBreakMap(showmap); |
| | 2710 | CondenseMarkMap(showmap, (int)(25 * fps), (int)(30 * fps)); |
| | 2711 | ConvertShowMapToCommMap(logoCommBreakMap, showmap); |
| | 2712 | |
| | 2713 | frm_dir_map_t::iterator it; |
| | 2714 | LOG(VB_COMMFLAG, LOG_INFO, "Logo Commercial Break Map" ); |
| | 2715 | for(it = logoCommBreakMap.begin(); it != logoCommBreakMap.end(); ++it) |
| | 2716 | LOG(VB_COMMFLAG, LOG_INFO, QString(" %1:%2") |
| | 2717 | .arg(it.key()).arg(*it)); |
| | 2718 | } |
| | 2719 | |
| | 2720 | void NextgenCommDetector::MergeBlankCommList(void) |
| | 2721 | { |
| | 2722 | frm_dir_map_t::iterator it; |
| | 2723 | frm_dir_map_t::iterator prev; |
| | 2724 | QMap<long long, long long> tmpMap; |
| | 2725 | QMap<long long, long long>::Iterator tmpMap_it; |
| | 2726 | QMap<long long, long long>::Iterator tmpMap_prev; |
| | 2727 | |
| | 2728 | blankCommBreakMap.clear(); |
| | 2729 | |
| | 2730 | if (blankCommMap.isEmpty()) |
| | 2731 | return; |
| | 2732 | |
| | 2733 | for (it = blankCommMap.begin(); it != blankCommMap.end(); ++it) |
| | 2734 | blankCommBreakMap[it.key()] = *it; |
| | 2735 | |
| | 2736 | if (blankCommBreakMap.isEmpty()) |
| | 2737 | return; |
| | 2738 | |
| | 2739 | it = blankCommMap.begin(); |
| | 2740 | prev = it; |
| | 2741 | ++it; |
| | 2742 | for(; it != blankCommMap.end(); ++it, ++prev) |
| | 2743 | { |
| | 2744 | // if next commercial starts less than 15*fps frames away then merge |
| | 2745 | if ((((prev.key() + 1) == it.key()) || |
| | 2746 | ((prev.key() + (15 * fps)) > it.key())) && |
| | 2747 | (*prev == MARK_COMM_END) && |
| | 2748 | (*it == MARK_COMM_START)) |
| | 2749 | { |
| | 2750 | blankCommBreakMap.remove(prev.key()); |
| | 2751 | blankCommBreakMap.remove(it.key()); |
| | 2752 | } |
| | 2753 | } |
| | 2754 | |
| | 2755 | |
| | 2756 | // make temp copy of commercial break list |
| | 2757 | it = blankCommBreakMap.begin(); |
| | 2758 | prev = it; |
| | 2759 | ++it; |
| | 2760 | tmpMap[prev.key()] = it.key(); |
| | 2761 | for(; it != blankCommBreakMap.end(); ++it, ++prev) |
| | 2762 | { |
| | 2763 | if ((*prev == MARK_COMM_START) && |
| | 2764 | (*it == MARK_COMM_END)) |
| | 2765 | tmpMap[prev.key()] = it.key(); |
| | 2766 | } |
| | 2767 | |
| | 2768 | tmpMap_it = tmpMap.begin(); |
| | 2769 | tmpMap_prev = tmpMap_it; |
| | 2770 | tmpMap_it++; |
| | 2771 | for(; tmpMap_it != tmpMap.end(); ++tmpMap_it, ++tmpMap_prev) |
| | 2772 | { |
| | 2773 | // if we find any segments less than 35 seconds between commercial |
| | 2774 | // breaks include those segments in the commercial break. |
| | 2775 | if (((*tmpMap_prev + (35 * fps)) > tmpMap_it.key()) && |
| | 2776 | ((*tmpMap_prev - tmpMap_prev.key()) > (35 * fps)) && |
| | 2777 | ((*tmpMap_it - tmpMap_it.key()) > (35 * fps))) |
| | 2778 | { |
| | 2779 | blankCommBreakMap.remove(*tmpMap_prev); |
| | 2780 | blankCommBreakMap.remove(tmpMap_it.key()); |
| | 2781 | } |
| | 2782 | } |
| | 2783 | } |
| | 2784 | |
| | 2785 | bool NextgenCommDetector::FrameIsInBreakMap( |
| | 2786 | uint64_t f, const frm_dir_map_t &breakMap) const |
| | 2787 | { |
| | 2788 | for (uint64_t i = f; i < framesProcessed; i++) |
| | 2789 | { |
| | 2790 | if (breakMap.contains(i)) |
| | 2791 | { |
| | 2792 | int type = breakMap[i]; |
| | 2793 | if ((type == MARK_COMM_END) || (i == f)) |
| | 2794 | return true; |
| | 2795 | if (type == MARK_COMM_START) |
| | 2796 | return false; |
| | 2797 | } |
| | 2798 | } |
| | 2799 | |
| | 2800 | // We want from f down to 0, but without wrapping the counter to negative |
| | 2801 | // on an unsigned counter. |
| | 2802 | for (uint64_t i = (f + 1); i-- > 0; ) |
| | 2803 | { |
| | 2804 | if (breakMap.contains(i)) |
| | 2805 | { |
| | 2806 | int type = breakMap[i]; |
| | 2807 | if ((type == MARK_COMM_START) || (i == f)) |
| | 2808 | return true; |
| | 2809 | if (type == MARK_COMM_END) |
| | 2810 | return false; |
| | 2811 | } |
| | 2812 | } |
| | 2813 | |
| | 2814 | return false; |
| | 2815 | } |
| | 2816 | |
| | 2817 | void NextgenCommDetector::DumpMap(frm_dir_map_t &map) |
| | 2818 | { |
| | 2819 | frm_dir_map_t::iterator it; |
| | 2820 | QString msg; |
| | 2821 | |
| | 2822 | LOG(VB_COMMFLAG, LOG_INFO, |
| | 2823 | "---------------------------------------------------"); |
| | 2824 | for (it = map.begin(); it != map.end(); ++it) |
| | 2825 | { |
| | 2826 | long long frame = it.key(); |
| | 2827 | int flag = *it; |
| | 2828 | int my_fps = (int)ceil(fps); |
| | 2829 | int hour = (frame / my_fps) / 60 / 60; |
| | 2830 | int min = (frame / my_fps) / 60 - (hour * 60); |
| | 2831 | int sec = (frame / my_fps) - (min * 60) - (hour * 60 * 60); |
| | 2832 | int frm = frame - ((sec * my_fps) + (min * 60 * my_fps) + |
| | 2833 | (hour * 60 * 60 * my_fps)); |
| | 2834 | int my_sec = (int)(frame / my_fps); |
| | 2835 | msg.sprintf("%7ld : %d (%02d:%02d:%02d.%02d) (%d)", |
| | 2836 | (long)frame, flag, hour, min, sec, frm, my_sec); |
| | 2837 | LOG(VB_COMMFLAG, LOG_INFO, msg); |
| | 2838 | } |
| | 2839 | LOG(VB_COMMFLAG, LOG_INFO, |
| | 2840 | "---------------------------------------------------"); |
| | 2841 | } |
| | 2842 | |
| | 2843 | void NextgenCommDetector::CondenseMarkMap(show_map_t &map, int spacing, |
| | 2844 | int length) |
| | 2845 | { |
| | 2846 | show_map_t::iterator it; |
| | 2847 | show_map_t::iterator prev; |
| | 2848 | show_map_t tmpMap; |
| | 2849 | |
| | 2850 | if (map.size() <= 2) |
| | 2851 | return; |
| | 2852 | |
| | 2853 | // merge any segments less than 'spacing' frames apart from each other |
| | 2854 | LOG(VB_COMMFLAG, LOG_INFO, "Commercial Map Before condense:" ); |
| | 2855 | for (it = map.begin(); it != map.end(); ++it) |
| | 2856 | { |
| | 2857 | LOG(VB_COMMFLAG, LOG_INFO, QString(" %1:%2") |
| | 2858 | .arg(it.key()).arg(*it)); |
| | 2859 | tmpMap[it.key()] = *it; |
| | 2860 | } |
| | 2861 | |
| | 2862 | prev = tmpMap.begin(); |
| | 2863 | it = prev; |
| | 2864 | ++it; |
| | 2865 | while (it != tmpMap.end()) |
| | 2866 | { |
| | 2867 | if ((*it == MARK_START) && |
| | 2868 | (*prev == MARK_END) && |
| | 2869 | ((it.key() - prev.key()) < (uint64_t)spacing)) |
| | 2870 | { |
| | 2871 | map.remove(prev.key()); |
| | 2872 | map.remove(it.key()); |
| | 2873 | } |
| | 2874 | ++prev; |
| | 2875 | ++it; |
| | 2876 | } |
| | 2877 | |
| | 2878 | if (map.size() == 0) |
| | 2879 | return; |
| | 2880 | |
| | 2881 | // delete any segments less than 'length' frames in length |
| | 2882 | tmpMap.clear(); |
| | 2883 | for (it = map.begin(); it != map.end(); ++it) |
| | 2884 | tmpMap[it.key()] = *it; |
| | 2885 | |
| | 2886 | prev = tmpMap.begin(); |
| | 2887 | it = prev; |
| | 2888 | ++it; |
| | 2889 | while (it != tmpMap.end()) |
| | 2890 | { |
| | 2891 | if ((*prev == MARK_START) && |
| | 2892 | (*it == MARK_END) && |
| | 2893 | ((it.key() - prev.key()) < (uint64_t)length)) |
| | 2894 | { |
| | 2895 | map.remove(prev.key()); |
| | 2896 | map.remove(it.key()); |
| | 2897 | } |
| | 2898 | ++prev; |
| | 2899 | ++it; |
| | 2900 | } |
| | 2901 | |
| | 2902 | LOG(VB_COMMFLAG, LOG_INFO, "Commercial Map After condense:" ); |
| | 2903 | for (it = map.begin(); it != map.end(); ++it) |
| | 2904 | LOG(VB_COMMFLAG, LOG_INFO, QString(" %1:%2") |
| | 2905 | .arg(it.key()).arg(*it)); |
| | 2906 | } |
| | 2907 | |
| | 2908 | void NextgenCommDetector::ConvertShowMapToCommMap( |
| | 2909 | frm_dir_map_t &out, const show_map_t &in) |
| | 2910 | { |
| | 2911 | out.clear(); |
| | 2912 | if (in.empty()) |
| | 2913 | return; |
| | 2914 | |
| | 2915 | show_map_t::const_iterator sit; |
| | 2916 | for (sit = in.begin(); sit != in.end(); ++sit) |
| | 2917 | { |
| | 2918 | if (*sit == MARK_START) |
| | 2919 | out[sit.key()] = MARK_COMM_END; |
| | 2920 | else |
| | 2921 | out[sit.key()] = MARK_COMM_START; |
| | 2922 | } |
| | 2923 | |
| | 2924 | frm_dir_map_t::iterator it = out.begin(); |
| | 2925 | if (it == out.end()) |
| | 2926 | return; |
| | 2927 | |
| | 2928 | switch (out[it.key()]) |
| | 2929 | { |
| | 2930 | case MARK_COMM_END: |
| | 2931 | if (it.key() == 0) |
| | 2932 | out.remove(0); |
| | 2933 | else |
| | 2934 | out[0] = MARK_COMM_START; |
| | 2935 | break; |
| | 2936 | case MARK_COMM_START: |
| | 2937 | break; |
| | 2938 | default: |
| | 2939 | out.remove(0); |
| | 2940 | break; |
| | 2941 | } |
| | 2942 | } |
| | 2943 | |
| | 2944 | |
| | 2945 | /* ideas for this method ported back from comskip.c mods by Jere Jones |
| | 2946 | * which are partially mods based on Myth's original commercial skip |
| | 2947 | * code written by Chris Pinkham. */ |
| | 2948 | |
| | 2949 | void NextgenCommDetector::CleanupFrameInfo(void) |
| | 2950 | { |
| | 2951 | LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::CleanupFrameInfo()"); |
| | 2952 | |
| | 2953 | int value; |
| | 2954 | int before, after; |
| | 2955 | |
| | 2956 | // try to account for noisy signal causing blank frames to be undetected |
| | 2957 | if ((framesProcessed > (fps * 60)) && |
| | 2958 | (blankFrameCount < (framesProcessed * 0.0004))) |
| | 2959 | { |
| | 2960 | int avgHistogram[256]; |
| | 2961 | int minAvg = -1; |
| | 2962 | int newThreshold = -1; |
| | 2963 | |
| | 2964 | LOG(VB_COMMFLAG, LOG_INFO, |
| | 2965 | QString("NextgenCommDetect: Only found %1 blank frames but " |
| | 2966 | "wanted at least %2, rechecking data using higher " |
| | 2967 | "threshold.") |
| | 2968 | .arg(blankFrameCount) |
| | 2969 | .arg((int)(framesProcessed * 0.0004))); |
| | 2970 | blankFrameMap.clear(); |
| | 2971 | blankFrameCount = 0; |
| | 2972 | |
| | 2973 | memset(avgHistogram, 0, sizeof(avgHistogram)); |
| | 2974 | |
| | 2975 | for (uint64_t i = 1; i <= framesProcessed; i++) |
| | 2976 | avgHistogram[clamp(frameInfo[i].avgBrightness, 0, 255)] += 1; |
| | 2977 | |
| | 2978 | for (int i = 1; i <= 255 && minAvg == -1; i++) |
| | 2979 | if (avgHistogram[i] > (framesProcessed * 0.0004)) |
| | 2980 | minAvg = i; |
| | 2981 | |
| | 2982 | newThreshold = minAvg + 3; |
| | 2983 | LOG(VB_COMMFLAG, LOG_INFO, |
| | 2984 | QString("Minimum Average Brightness on a frame " |
| | 2985 | "was %1, will use %2 as new threshold") |
| | 2986 | .arg(minAvg).arg(newThreshold)); |
| | 2987 | |
| | 2988 | for (uint64_t i = 1; i <= framesProcessed; i++) |
| | 2989 | { |
| | 2990 | value = frameInfo[i].flagMask; |
| | 2991 | frameInfo[i].flagMask = value & ~COMM_FRAME_BLANK; |
| | 2992 | |
| | 2993 | if (( !(frameInfo[i].flagMask & COMM_FRAME_BLANK)) && |
| | 2994 | (frameInfo[i].avgBrightness < newThreshold)) |
| | 2995 | { |
| | 2996 | frameInfo[i].flagMask = value | COMM_FRAME_BLANK; |
| | 2997 | blankFrameMap[i] = MARK_BLANK_FRAME; |
| | 2998 | blankFrameCount++; |
| | 2999 | } |
| | 3000 | } |
| | 3001 | |
| | 3002 | LOG(VB_COMMFLAG, LOG_INFO, |
| | 3003 | QString("Found %1 blank frames using new value") |
| | 3004 | .arg(blankFrameCount)); |
| | 3005 | } |
| | 3006 | |
| | 3007 | // try to account for fuzzy logo detection |
| | 3008 | for (uint64_t i = 1; i <= framesProcessed; i++) |
| | 3009 | { |
| | 3010 | if ((i < 10) || ((i+10) > framesProcessed)) |
| | 3011 | continue; |
| | 3012 | |
| | 3013 | before = 0; |
| | 3014 | for (int offset = 1; offset <= 10; offset++) |
| | 3015 | if (frameInfo[i - offset].flagMask & COMM_FRAME_LOGO_PRESENT) |
| | 3016 | before++; |
| | 3017 | |
| | 3018 | after = 0; |
| | 3019 | for (int offset = 1; offset <= 10; offset++) |
| | 3020 | if (frameInfo[i + offset].flagMask & COMM_FRAME_LOGO_PRESENT) |
| | 3021 | after++; |
| | 3022 | |
| | 3023 | value = frameInfo[i].flagMask; |
| | 3024 | if (value == -1) |
| | 3025 | frameInfo[i].flagMask = 0; |
| | 3026 | |
| | 3027 | if (value & COMM_FRAME_LOGO_PRESENT) |
| | 3028 | { |
| | 3029 | if ((before < 4) && (after < 4)) |
| | 3030 | frameInfo[i].flagMask = value & ~COMM_FRAME_LOGO_PRESENT; |
| | 3031 | } |
| | 3032 | else |
| | 3033 | { |
| | 3034 | if ((before > 6) && (after > 6)) |
| | 3035 | frameInfo[i].flagMask = value | COMM_FRAME_LOGO_PRESENT; |
| | 3036 | } |
| | 3037 | } |
| | 3038 | } |
| | 3039 | |
| | 3040 | void NextgenCommDetector::GetLogoCommBreakMap(show_map_t &map) |
| | 3041 | { |
| | 3042 | LOG(VB_COMMFLAG, LOG_INFO, "CommDetect::GetLogoCommBreakMap()"); |
| | 3043 | |
| | 3044 | map.clear(); |
| | 3045 | |
| | 3046 | bool PrevFrameLogo = false; |
| | 3047 | |
| | 3048 | for (uint64_t curFrame = 1 ; curFrame <= framesProcessed; curFrame++) |
| | 3049 | { |
| | 3050 | bool CurrentFrameLogo = |
| | 3051 | (frameInfo[curFrame].flagMask & COMM_FRAME_LOGO_PRESENT); |
| | 3052 | |
| | 3053 | if (!PrevFrameLogo && CurrentFrameLogo) |
| | 3054 | map[curFrame] = MARK_START; |
| | 3055 | else if (PrevFrameLogo && !CurrentFrameLogo) |
| | 3056 | map[curFrame] = MARK_END; |
| | 3057 | |
| | 3058 | PrevFrameLogo = CurrentFrameLogo; |
| | 3059 | } |
| | 3060 | } |
| | 3061 | |
| | 3062 | void NextgenCommDetector::logoDetectorBreathe() |
| | 3063 | { |
| | 3064 | emit breathe(); |
| | 3065 | } |
| | 3066 | |
| | 3067 | void NextgenCommDetector::PrintFullMap( |
| | 3068 | ostream &out, const frm_dir_map_t *comm_breaks, bool verbose) const |
| | 3069 | { |
| | 3070 | if (verbose) |
| | 3071 | { |
| | 3072 | QByteArray tmp = NGFrameInfoEntry::GetHeader().toAscii(); |
| | 3073 | out << tmp.constData() << " mark" << endl; |
| | 3074 | } |
| | 3075 | |
| | 3076 | for (long long i = 1; i < curFrameNumber; i++) |
| | 3077 | { |
| | 3078 | QMap<long long, NGFrameInfoEntry>::const_iterator it = frameInfo.find(i); |
| | 3079 | if (it == frameInfo.end()) |
| | 3080 | continue; |
| | 3081 | |
| | 3082 | QByteArray atmp = (*it).toString(i, verbose).toAscii(); |
| | 3083 | out << atmp.constData() << " "; |
| | 3084 | if (comm_breaks) |
| | 3085 | { |
| | 3086 | frm_dir_map_t::const_iterator mit = comm_breaks->find(i); |
| | 3087 | if (mit != comm_breaks->end()) |
| | 3088 | { |
| | 3089 | QString tmp = (verbose) ? |
| | 3090 | toString((MarkTypes)*mit) : QString::number(*mit); |
| | 3091 | atmp = tmp.toAscii(); |
| | 3092 | |
| | 3093 | out << atmp.constData(); |
| | 3094 | } |
| | 3095 | } |
| | 3096 | out << "\n"; |
| | 3097 | } |
| | 3098 | |
| | 3099 | out << flush; |
| | 3100 | } |
| | 3101 | |
| | 3102 | /* vim: set expandtab tabstop=4 shiftwidth=4: */ |