|
MythTV
0.26-pre
|
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: */
1.7.6.1