MythTV  master
DetectLetterbox.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
2 
3 // MythTV headers
4 #include "DetectLetterbox.h"
5 #include "mythplayer.h"
6 #include "videoouttypes.h"
7 #include "mythcorecontext.h"
8 
10 {
11  int dbAdjustFill = gCoreContext->GetNumSetting("AdjustFill", 0);
14  dbAdjustFill - kAdjustFill_AutoDetect_DefaultOff);
16  m_detectLetterboxLimit = gCoreContext->GetNumSetting("DetectLeterboxLimit", 75);
17  m_player = player;
18 }
19 
27 {
28  unsigned char *buf = frame->buf;
29  int *pitches = frame->pitches;
30  int *offsets = frame->offsets;
31  const int width = frame->width;
32  const int height = frame->height;
33  const long long frameNumber = frame->frameNumber;
34  const int NUMBER_OF_DETECTION_LINES = 3; // How many lines are we looking at
35  const int THRESHOLD = 5; // Y component has to not vary more than this in the bars
36  const int HORIZONTAL_THRESHOLD = 4; // How tolerant are we that the image has horizontal edges
37 
38  // If the black bars is larger than this limit we switch to Half or Full Mode
39  // const int fullLimit = (int) (((height - width * 9 / 16) / 2) * m_detectLetterboxLimit / 100);
40  // const int halfLimit = (int) (((height - width * 9 / 14) / 2) * m_detectLetterboxLimit / 100);
41  // If the black bars is larger than this limit we switch to Half or Full Mode
42  const int fullLimit = (int) ((height * (1 - m_player->GetVideoAspect() * 9 / 16) / 2) * m_detectLetterboxLimit / 100);
43  const int halfLimit = (int) ((height * (1 - m_player->GetVideoAspect() * 9 / 14) / 2) * m_detectLetterboxLimit / 100);
44 
45  const int xPos[] = {width / 4, width / 2, width * 3 / 4}; // Lines to scan for black letterbox edge
46  int topHits = 0, bottomHits = 0, minTop = 0, minBottom = 0, maxTop = 0, maxBottom = 0;
47  int topHit[] = {0, 0, 0}, bottomHit[] = {0, 0, 0};
48 
49  if (!GetDetectLetterbox())
50  return;
51 
52  if (!m_player->GetVideoOutput())
53  return;
54 
55  switch (frame->codec) {
56  case FMT_YV12:
58  {
59  m_firstFrameChecked = frameNumber;
60  LOG(VB_PLAYBACK, LOG_INFO,
61  QString("Detect Letterbox: YV12 frame format detected"));
62  }
63  break;
64  default:
65  LOG(VB_PLAYBACK, LOG_INFO,
66  QString("Detect Letterbox: The source is not "
67  "a supported frame format (was %1)").arg(frame->codec));
68  m_isDetectLetterbox = false;
69  return;
70  }
71 
72  if (frameNumber < 0)
73  {
74  LOG(VB_PLAYBACK, LOG_INFO,
75  QString("Detect Letterbox: Strange frame number %1")
76  .arg(frameNumber));
77  return;
78  }
79 
80  if (m_player->GetVideoAspect() > 1.5F)
81  {
83  {
84  LOG(VB_PLAYBACK, LOG_INFO,
85  QString("Detect Letterbox: The source is "
86  "already in widescreen (aspect: %1)")
87  .arg(m_player->GetVideoAspect()));
88  m_detectLetterboxLock.lock();
91  m_detectLetterboxSwitchFrame = frameNumber;
92  m_detectLetterboxLock.unlock();
93  }
94  else
95  {
97  }
98  LOG(VB_PLAYBACK, LOG_INFO,
99  QString("Detect Letterbox: The source is already "
100  "in widescreen (aspect: %1)")
101  .arg(m_player->GetVideoAspect()));
102  m_isDetectLetterbox = false;
103  return;
104  }
105 
106  // Establish the level of light in the edge
107  int averageY = 0;
108  for (int detectionLine = 0;
109  detectionLine < NUMBER_OF_DETECTION_LINES;
110  detectionLine++)
111  {
112  averageY += buf[offsets[0] + 5 * pitches[0] + xPos[detectionLine]];
113  averageY += buf[offsets[0] + (height - 6) * pitches[0] + xPos[detectionLine]];
114  }
115  averageY /= NUMBER_OF_DETECTION_LINES * 2;
116  if (averageY > 64) // To bright to be a letterbox border
117  averageY = 0;
118 
119  // Scan the detection lines
120  for (int y = 5; y < height / 4; y++) // skip first pixels incase of noise in the edge
121  {
122  for (int detectionLine = 0;
123  detectionLine < NUMBER_OF_DETECTION_LINES;
124  detectionLine++)
125  {
126  int Y = buf[offsets[0] + y * pitches[0] + xPos[detectionLine]];
127  int U = buf[offsets[1] + (y>>1) * pitches[1] + (xPos[detectionLine]>>1)];
128  int V = buf[offsets[2] + (y>>1) * pitches[2] + (xPos[detectionLine]>>1)];
129  if ((!topHit[detectionLine]) &&
130  ( Y > averageY + THRESHOLD || Y < averageY - THRESHOLD ||
131  U < 128 - 32 || U > 128 + 32 ||
132  V < 128 - 32 || V > 128 + 32 ))
133  {
134  topHit[detectionLine] = y;
135  topHits++;
136  if (!minTop)
137  minTop = y;
138  maxTop = y;
139  }
140 
141  Y = buf[offsets[0] + (height-y-1 ) * pitches[0] + xPos[detectionLine]];
142  U = buf[offsets[1] + ((height-y-1) >> 1) * pitches[1] + (xPos[detectionLine]>>1)];
143  V = buf[offsets[2] + ((height-y-1) >> 1) * pitches[2] + (xPos[detectionLine]>>1)];
144  if ((!bottomHit[detectionLine]) &&
145  ( Y > averageY + THRESHOLD || Y < averageY - THRESHOLD ||
146  U < 128 - 32 || U > 128 + 32 ||
147  V < 128 - 32 || V > 128 + 32 ))
148  {
149  bottomHit[detectionLine] = y;
150  bottomHits++;
151  if (!minBottom)
152  minBottom = y;
153  maxBottom = y;
154  }
155  }
156 
157  if (topHits == NUMBER_OF_DETECTION_LINES &&
158  bottomHits == NUMBER_OF_DETECTION_LINES)
159  {
160  break;
161  }
162  }
163  if (topHits != NUMBER_OF_DETECTION_LINES) maxTop = height / 4;
164  if (!minTop) minTop = height / 4;
165  if (bottomHits != NUMBER_OF_DETECTION_LINES) maxBottom = height / 4;
166  if (!minBottom) minBottom = height / 4;
167 
168  bool horizontal = ((minTop && maxTop - minTop < HORIZONTAL_THRESHOLD) &&
169  (minBottom && maxBottom - minBottom < HORIZONTAL_THRESHOLD));
170 
171  if (m_detectLetterboxSwitchFrame > frameNumber) // user is reversing
172  {
173  m_detectLetterboxLock.lock();
178  m_detectLetterboxLock.unlock();
179  }
180 
181  if (minTop < halfLimit || minBottom < halfLimit)
183  if (minTop < fullLimit || minBottom < fullLimit)
185 
187  {
189  minTop > halfLimit && minBottom > halfLimit) {
191  }
192  }
193  else
194  {
196  minTop < fullLimit && minBottom < fullLimit) {
198  }
199  }
201  minTop > fullLimit && minBottom > fullLimit)
203 
204  if ( maxTop < halfLimit || maxBottom < halfLimit) // Not to restrictive when switching to off
205  {
206  // No Letterbox
208  {
209  LOG(VB_PLAYBACK, LOG_INFO,
210  QString("Detect Letterbox: Non Letterbox "
211  "detected on line: %1 (limit: %2)")
212  .arg(min(maxTop, maxBottom)).arg(halfLimit));
213  m_detectLetterboxLock.lock();
216  m_detectLetterboxSwitchFrame = frameNumber;
217  m_detectLetterboxLock.unlock();
218  }
219  else
220  {
222  }
223  }
224  else if (horizontal && minTop > halfLimit && minBottom > halfLimit &&
225  maxTop < fullLimit && maxBottom < fullLimit)
226  {
227  // Letterbox (with narrow bars)
229  {
230  LOG(VB_PLAYBACK, LOG_INFO,
231  QString("Detect Letterbox: Narrow Letterbox "
232  "detected on line: %1 (limit: %2) frame: %3")
233  .arg(minTop).arg(halfLimit)
235  m_detectLetterboxLock.lock();
239  // Do not change switch frame if switch to Full mode has not been executed yet
240  }
241  else
244  m_detectLetterboxLock.unlock();
245  }
246  else
247  {
249  }
250  }
251  else if (horizontal && minTop > fullLimit && minBottom > fullLimit)
252  {
253  // Letterbox
256  {
257  LOG(VB_PLAYBACK, LOG_INFO,
258  QString("Detect Letterbox: Detected Letterbox "
259  "on line: %1 (limit: %2) frame: %3").arg(minTop)
260  .arg(fullLimit).arg(m_detectLetterboxPossibleFullFrame));
261  m_detectLetterboxLock.lock();
265  m_detectLetterboxLock.unlock();
266  }
267  else
268  {
270  }
271  }
272  else
273  {
276  }
277 }
278 
285 {
286  if (!GetDetectLetterbox())
287  return;
288 
290  return;
291 
292  m_detectLetterboxLock.lock();
293  if (m_detectLetterboxSwitchFrame <= frame->frameNumber &&
295  {
297  {
298  LOG(VB_PLAYBACK, LOG_INFO,
299  QString("Detect Letterbox: Switched to %1 "
300  "on frame %2 (%3)").arg(m_detectLetterboxDetectedMode)
301  .arg(frame->frameNumber)
304  ToggleAdjustFill(m_detectLetterboxDetectedMode);
305  m_player->ReinitOSD();
306  }
308  }
309  else if (m_detectLetterboxSwitchFrame <= frame->frameNumber)
310  LOG(VB_PLAYBACK, LOG_INFO,
311  QString("Detect Letterbox: Not Switched to %1 on frame %2 "
312  "(%3) Not enough consecutive detections (%4)")
314  .arg(frame->frameNumber).arg(m_detectLetterboxSwitchFrame)
316 
317  m_detectLetterboxLock.unlock();
318 }
319 
321 {
322  m_isDetectLetterbox = detect;
326 }
327 
329 {
330  return m_isDetectLetterbox;
331 }
332 
333 /* vim: set expandtab tabstop=4 shiftwidth=4: */
int pitches[3]
Y, U, & V pitches.
Definition: mythframe.h:63
float GetVideoAspect(void) const
Definition: mythplayer.h:175
VideoOutput * GetVideoOutput(void)
Definition: mythplayer.h:244
void Detect(VideoFrame *frame)
Detects if this frame is or is not letterboxed.
long long m_detectLetterboxSwitchFrame
On which frame was the mode switch detected.
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
bool GetDetectLetterbox() const
DetectLetterbox(MythPlayer *const player)
AdjustFillMode GetAdjustFill(void) const
QMutex m_detectLetterboxLock
AdjustFillMode m_detectLetterboxDefaultMode
int offsets[3]
Y, U, & V offsets.
Definition: mythframe.h:64
int height
Definition: mythframe.h:42
long long frameNumber
Definition: mythframe.h:48
AdjustFillMode
Definition: videoouttypes.h:57
int GetNumSetting(const QString &key, int defaultval=0)
MythPlayer * m_player
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void SwitchTo(VideoFrame *frame)
Switch to the mode detected by DetectLetterbox.
void ReinitOSD(void)
Definition: mythplayer.cpp:538
AdjustFillMode m_detectLetterboxDetectedMode
Which mode was last detected.
unsigned char * buf
Definition: mythframe.h:39
long long m_detectLetterboxPossibleHalfFrame
int m_detectLetterboxConsecutiveCounter
void SetDetectLetterbox(bool detect)
long long m_detectLetterboxPossibleFullFrame
VideoFrameType codec
Definition: mythframe.h:38