MythTV  0.26-pre
DetectLetterbox.cpp
Go to the documentation of this file.
00001 // -*- Mode: c++ -*-
00002 
00003 // MythTV headers
00004 #include "DetectLetterbox.h"
00005 #include "mythplayer.h"
00006 #include "videoouttypes.h"
00007 #include "mythcorecontext.h"
00008 
00009 DetectLetterbox::DetectLetterbox(MythPlayer* const player)
00010 {
00011     int dbAdjustFill = gCoreContext->GetNumSetting("AdjustFill", 0);
00012     isDetectLetterbox = dbAdjustFill >= kAdjustFill_AutoDetect_DefaultOff;
00013     firstFrameChecked = 0;
00014     detectLetterboxDefaultMode = (AdjustFillMode) max((int) kAdjustFill_Off,
00015                                  dbAdjustFill - kAdjustFill_AutoDetect_DefaultOff);
00016     detectLetterboxSwitchFrame = -1;
00017     detectLetterboxPossibleHalfFrame = -1;
00018     detectLetterboxPossibleFullFrame = -1;
00019     detectLetterboxConsecutiveCounter = 0;
00020     detectLetterboxDetectedMode = player->GetAdjustFill();
00021     detectLetterboxLimit = gCoreContext->GetNumSetting("DetectLeterboxLimit", 75);
00022     m_player = player;
00023 }
00024 
00025 DetectLetterbox::~DetectLetterbox()
00026 {
00027 }
00028 
00035 void DetectLetterbox::Detect(VideoFrame *frame)
00036 {
00037     unsigned char *buf = frame->buf;
00038     int *pitches = frame->pitches;
00039     int *offsets = frame->offsets;
00040     const int width = frame->width;
00041     const int height = frame->height;
00042     const long long frameNumber = frame->frameNumber;
00043     const int NUMBER_OF_DETECTION_LINES = 3; // How many lines are we looking at
00044     const int THRESHOLD = 5; // Y component has to not vary more than this in the bars
00045     const int HORIZONTAL_THRESHOLD = 4; // How tolerant are we that the image has horizontal edges
00046 
00047     // If the black bars is larger than this limit we switch to Half or Full Mode
00048     //    const int fullLimit = (int) (((height - width * 9 / 16) / 2) * detectLetterboxLimit / 100);
00049     //    const int halfLimit = (int) (((height - width * 9 / 14) / 2) * detectLetterboxLimit / 100);
00050     // If the black bars is larger than this limit we switch to Half or Full Mode
00051     const int fullLimit = (int) ((height * (1 - m_player->GetVideoAspect() * 9 / 16) / 2) * detectLetterboxLimit / 100);
00052     const int halfLimit = (int) ((height * (1 - m_player->GetVideoAspect() * 9 / 14) / 2) * detectLetterboxLimit / 100);
00053 
00054     const int xPos[] = {width / 4, width / 2, width * 3 / 4};    // Lines to scan for black letterbox edge
00055     int topHits = 0, bottomHits = 0, minTop = 0, minBottom = 0, maxTop = 0, maxBottom = 0;
00056     int topHit[] = {0, 0, 0}, bottomHit[] = {0, 0, 0};
00057 
00058     if (!GetDetectLetterbox())
00059         return;
00060 
00061     if (!m_player->GetVideoOutput())
00062         return;
00063 
00064     switch (frame->codec) {
00065         case FMT_YV12:
00066             if (!firstFrameChecked)
00067             {
00068                 firstFrameChecked = frameNumber;
00069                 LOG(VB_PLAYBACK, LOG_INFO,
00070                     QString("Detect Letterbox: YV12 frame format detected"));
00071             }
00072             break;
00073         default:
00074             LOG(VB_PLAYBACK, LOG_INFO,
00075                 QString("Detect Letterbox: The source is not "
00076                         "a supported frame format (was %1)").arg(frame->codec));
00077             isDetectLetterbox = false;
00078             return;
00079     }
00080 
00081     if (frameNumber < 0)
00082     {
00083         LOG(VB_PLAYBACK, LOG_INFO,
00084              QString("Detect Letterbox: Strange frame number %1")
00085                  .arg(frameNumber));
00086         return;
00087     }
00088 
00089     if (m_player->GetVideoAspect() > 1.5)
00090     {
00091         if (detectLetterboxDetectedMode != detectLetterboxDefaultMode)
00092         {
00093             LOG(VB_PLAYBACK, LOG_INFO,
00094                 QString("Detect Letterbox: The source is "
00095                         "already in widescreen (aspect: %1)")
00096                     .arg(m_player->GetVideoAspect()));
00097             detectLetterboxLock.lock();
00098             detectLetterboxConsecutiveCounter = 0;
00099             detectLetterboxDetectedMode = detectLetterboxDefaultMode;
00100             detectLetterboxSwitchFrame = frameNumber;
00101             detectLetterboxLock.unlock();
00102         }
00103         else
00104         {
00105             detectLetterboxConsecutiveCounter++;
00106         }
00107         LOG(VB_PLAYBACK, LOG_INFO,
00108             QString("Detect Letterbox: The source is already "
00109                     "in widescreen (aspect: %1)")
00110                 .arg(m_player->GetVideoAspect()));
00111         isDetectLetterbox = false;
00112         return;
00113     }
00114 
00115     // Establish the level of light in the edge
00116     int averageY = 0;
00117     for (int detectionLine = 0;
00118          detectionLine < NUMBER_OF_DETECTION_LINES;
00119          detectionLine++)
00120     {
00121         averageY += buf[offsets[0] + 5 * pitches[0]            + xPos[detectionLine]];
00122         averageY += buf[offsets[0] + (height - 6) * pitches[0] + xPos[detectionLine]];
00123     }
00124     averageY /= NUMBER_OF_DETECTION_LINES * 2;
00125     if (averageY > 64) // To bright to be a letterbox border
00126         averageY = 0;
00127 
00128     // Scan the detection lines
00129     for (int y = 5; y < height / 4; y++) // skip first pixels incase of noise in the edge
00130     {
00131         for (int detectionLine = 0;
00132              detectionLine < NUMBER_OF_DETECTION_LINES;
00133              detectionLine++)
00134         {
00135             int Y = buf[offsets[0] +  y     * pitches[0] +  xPos[detectionLine]];
00136             int U = buf[offsets[1] + (y>>1) * pitches[1] + (xPos[detectionLine]>>1)];
00137             int V = buf[offsets[2] + (y>>1) * pitches[2] + (xPos[detectionLine]>>1)];
00138             if ((!topHit[detectionLine]) &&
00139                 ( Y > averageY + THRESHOLD || Y < averageY - THRESHOLD ||
00140                   U < 128 - 32 || U > 128 + 32 ||
00141                   V < 128 - 32 || V > 128 + 32 ))
00142             {
00143                 topHit[detectionLine] = y;
00144                 topHits++;
00145                 if (!minTop)
00146                     minTop = y;
00147                 maxTop = y;
00148             }
00149 
00150             Y = buf[offsets[0] + (height-y-1     ) * pitches[0] + xPos[detectionLine]];
00151             U = buf[offsets[1] + ((height-y-1) >> 1) * pitches[1] + (xPos[detectionLine]>>1)];
00152             V = buf[offsets[2] + ((height-y-1) >> 1) * pitches[2] + (xPos[detectionLine]>>1)];
00153             if ((!bottomHit[detectionLine]) &&
00154                 ( Y > averageY + THRESHOLD || Y < averageY - THRESHOLD ||
00155                   U < 128 - 32 || U > 128 + 32 ||
00156                   V < 128 - 32 || V > 128 + 32 ))
00157             {
00158                 bottomHit[detectionLine] = y;
00159                 bottomHits++;
00160                 if (!minBottom)
00161                     minBottom = y;
00162                 maxBottom = y;
00163             }
00164         }
00165 
00166         if (topHits == NUMBER_OF_DETECTION_LINES &&
00167             bottomHits == NUMBER_OF_DETECTION_LINES)
00168         {
00169             break;
00170         }
00171     }
00172     if (topHits != NUMBER_OF_DETECTION_LINES) maxTop = height / 4;
00173     if (!minTop) minTop = height / 4;
00174     if (bottomHits != NUMBER_OF_DETECTION_LINES) maxBottom = height / 4;
00175     if (!minBottom) minBottom = height / 4;
00176 
00177     bool horizontal = ((minTop && maxTop - minTop < HORIZONTAL_THRESHOLD) &&
00178                        (minBottom && maxBottom - minBottom < HORIZONTAL_THRESHOLD));
00179 
00180     if (detectLetterboxSwitchFrame > frameNumber) // user is reversing
00181     {
00182         detectLetterboxLock.lock();
00183         detectLetterboxDetectedMode = m_player->GetAdjustFill();
00184         detectLetterboxSwitchFrame = -1;
00185         detectLetterboxPossibleHalfFrame = -1;
00186         detectLetterboxPossibleFullFrame = -1;
00187         detectLetterboxLock.unlock();
00188     }
00189 
00190     if (minTop < halfLimit || minBottom < halfLimit)
00191         detectLetterboxPossibleHalfFrame = -1;
00192     if (minTop < fullLimit || minBottom < fullLimit)
00193         detectLetterboxPossibleFullFrame = -1;
00194 
00195     if (detectLetterboxDetectedMode != kAdjustFill_Full)
00196     {
00197         if (detectLetterboxPossibleHalfFrame == -1 &&
00198             minTop > halfLimit && minBottom > halfLimit) {
00199             detectLetterboxPossibleHalfFrame = frameNumber;
00200         }
00201     }
00202     else
00203     {
00204         if (detectLetterboxPossibleHalfFrame == -1 &&
00205             minTop < fullLimit && minBottom < fullLimit) {
00206             detectLetterboxPossibleHalfFrame = frameNumber;
00207         }
00208     }
00209     if (detectLetterboxPossibleFullFrame == -1 &&
00210         minTop > fullLimit && minBottom > fullLimit)
00211         detectLetterboxPossibleFullFrame = frameNumber;
00212 
00213     if ( maxTop < halfLimit || maxBottom < halfLimit) // Not to restrictive when switching to off
00214     {
00215         // No Letterbox
00216         if (detectLetterboxDetectedMode != detectLetterboxDefaultMode)
00217         {
00218             LOG(VB_PLAYBACK, LOG_INFO,
00219                 QString("Detect Letterbox: Non Letterbox "
00220                         "detected on line: %1 (limit: %2)")
00221                     .arg(min(maxTop, maxBottom)).arg(halfLimit));
00222             detectLetterboxLock.lock();
00223             detectLetterboxConsecutiveCounter = 0;
00224             detectLetterboxDetectedMode = detectLetterboxDefaultMode;
00225             detectLetterboxSwitchFrame = frameNumber;
00226             detectLetterboxLock.unlock();
00227         }
00228         else
00229         {
00230             detectLetterboxConsecutiveCounter++;
00231         }
00232     }
00233     else if (horizontal && minTop > halfLimit && minBottom > halfLimit &&
00234              maxTop < fullLimit && maxBottom < fullLimit)
00235     {
00236         // Letterbox (with narrow bars)
00237         if (detectLetterboxDetectedMode != kAdjustFill_Half)
00238         {
00239             LOG(VB_PLAYBACK, LOG_INFO,
00240                 QString("Detect Letterbox: Narrow Letterbox "
00241                         "detected on line: %1 (limit: %2) frame: %3")
00242                     .arg(minTop).arg(halfLimit)
00243                     .arg(detectLetterboxPossibleHalfFrame));
00244             detectLetterboxLock.lock();
00245             detectLetterboxConsecutiveCounter = 0;
00246             if (detectLetterboxDetectedMode == kAdjustFill_Full &&
00247                 detectLetterboxSwitchFrame != -1) {
00248                 // Do not change switch frame if switch to Full mode has not been executed yet
00249             }
00250             else
00251                 detectLetterboxSwitchFrame = detectLetterboxPossibleHalfFrame;
00252             detectLetterboxDetectedMode = kAdjustFill_Half;
00253             detectLetterboxLock.unlock();
00254         }
00255         else
00256         {
00257             detectLetterboxConsecutiveCounter++;
00258         }
00259     }
00260     else if (horizontal && minTop > fullLimit && minBottom > fullLimit)
00261     {
00262         // Letterbox
00263         detectLetterboxPossibleHalfFrame = -1;
00264         if (detectLetterboxDetectedMode != kAdjustFill_Full)
00265         {
00266             LOG(VB_PLAYBACK, LOG_INFO,
00267                 QString("Detect Letterbox: Detected Letterbox "
00268                         "on line: %1 (limit: %2) frame: %3").arg(minTop)
00269                     .arg(fullLimit).arg(detectLetterboxPossibleFullFrame));
00270             detectLetterboxLock.lock();
00271             detectLetterboxConsecutiveCounter = 0;
00272             detectLetterboxDetectedMode = kAdjustFill_Full;
00273             detectLetterboxSwitchFrame = detectLetterboxPossibleFullFrame;
00274             detectLetterboxLock.unlock();
00275         }
00276         else
00277         {
00278             detectLetterboxConsecutiveCounter++;
00279         }
00280     }
00281     else
00282     {
00283         if (detectLetterboxConsecutiveCounter <= 3)
00284             detectLetterboxConsecutiveCounter = 0;
00285     }
00286 }
00287 
00293 void DetectLetterbox::SwitchTo(VideoFrame *frame)
00294 {
00295     if (!GetDetectLetterbox())
00296         return;
00297 
00298     if (detectLetterboxSwitchFrame == -1)
00299         return;
00300 
00301     detectLetterboxLock.lock();
00302     if (detectLetterboxSwitchFrame <= frame->frameNumber &&
00303         detectLetterboxConsecutiveCounter > 3)
00304     {
00305         if (m_player->GetAdjustFill() != detectLetterboxDetectedMode)
00306         {
00307             LOG(VB_PLAYBACK, LOG_INFO,
00308                 QString("Detect Letterbox: Switched to %1 "
00309                         "on frame %2 (%3)").arg(detectLetterboxDetectedMode)
00310                     .arg(frame->frameNumber)
00311                     .arg(detectLetterboxSwitchFrame));
00312             m_player->GetVideoOutput()->
00313                       ToggleAdjustFill(detectLetterboxDetectedMode);
00314             m_player->ReinitOSD();
00315         }
00316         detectLetterboxSwitchFrame = -1;
00317     }
00318     else if (detectLetterboxSwitchFrame <= frame->frameNumber)
00319         LOG(VB_PLAYBACK, LOG_INFO,
00320             QString("Detect Letterbox: Not Switched to %1 on frame %2 "
00321                     "(%3) Not enough consecutive detections (%4)")
00322                 .arg(detectLetterboxDetectedMode)
00323                 .arg(frame->frameNumber).arg(detectLetterboxSwitchFrame)
00324                 .arg(detectLetterboxConsecutiveCounter));
00325 
00326     detectLetterboxLock.unlock();
00327 }
00328 
00329 void DetectLetterbox::SetDetectLetterbox(bool detect)
00330 {
00331     isDetectLetterbox = detect;
00332     detectLetterboxSwitchFrame = -1;
00333     detectLetterboxDetectedMode = m_player->GetAdjustFill();
00334     firstFrameChecked = 0;
00335 }
00336 
00337 bool DetectLetterbox::GetDetectLetterbox() const
00338 {
00339     return isDetectLetterbox;
00340 }
00341 
00342 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends