MythTV  master
DetectLetterbox.cpp
Go to the documentation of this file.
1 // MythTV
2 #include "mythcorecontext.h"
3 #include "DetectLetterbox.h"
4 
5 #define LOC QString("DetectLetterbox: ")
6 
7 #define NUMBER_OF_DETECTION_LINES 3 // How many lines are we looking at
8 #define THRESHOLD 5 // Y component has to not vary more than this in the bars
9 #define HORIZONTAL_THRESHOLD 4 // How tolerant are we that the image has horizontal edges
10 
12 {
13  int dbAdjustFill = gCoreContext->GetNumSetting("AdjustFill", 0);
16  static_cast<AdjustFillMode>(std::max(static_cast<int>(kAdjustFill_Off),
17  dbAdjustFill - kAdjustFill_AutoDetect_DefaultOff));
18  m_detectLetterboxLimit = gCoreContext->GetNumSetting("DetectLeterboxLimit", 75);
19 }
20 
27 bool DetectLetterbox::Detect(MythVideoFrame *Frame, float VideoAspect, AdjustFillMode& Current)
28 {
29  if (!Frame || !m_isDetectLetterbox)
30  return false;
31 
32  unsigned char *buf = Frame->m_buffer;
33  const FramePitches pitches = Frame->m_pitches;
34  const FrameOffsets offsets = Frame->m_offsets;
35  const int height = Frame->m_height;
36 
37  // If the black bars is larger than this limit we switch to Half or Full Mode
38  // const int fullLimit = static_cast<int>(((height - width * 9 / 16) / 2) * m_detectLetterboxLimit / 100);
39  // const int halfLimit = (static_cast<int>(((height - width * 9 / 14) / 2) * m_detectLetterboxLimit / 100);
40 
41  // If the black bars is larger than this limit we switch to Half or Full Mode
42  const int fullLimit = static_cast<int>((height * (1 - VideoAspect * 9 / 16) / 2) * m_detectLetterboxLimit / 100);
43  const int halfLimit = static_cast<int>((height * (1 - VideoAspect * 9 / 14) / 2) * m_detectLetterboxLimit / 100);
44 
45  // Lines to scan for black letterbox edge
46  const std::array<int,3> xPos { Frame->m_width / 4, Frame->m_width / 2, Frame->m_width * 3 / 4} ;
47  int topHits = 0;
48  int bottomHits = 0;
49  int minTop = 0;
50  int minBottom = 0;
51  int maxTop = 0;
52  int maxBottom = 0;
53  std::array<int,3> topHit = { 0, 0, 0 };
54  std::array<int,3> bottomHit = { 0, 0, 0 };
55 
56  switch (Frame->m_type)
57  {
58  case FMT_YV12:
59  case FMT_YUV420P9:
60  case FMT_YUV420P10:
61  case FMT_YUV420P12:
62  case FMT_YUV420P14:
63  case FMT_YUV420P16:
64  case FMT_NV12:
65  case FMT_P010:
66  case FMT_P016:
67  if (!m_firstFrameChecked || m_frameType != Frame->m_type)
68  {
69  m_firstFrameChecked = Frame->m_frameNumber;
70  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("'%1' frame format detected")
72  }
73  m_frameType = Frame->m_type;
74  break;
75  default:
76  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("'%1' frame format is not supported")
78  m_isDetectLetterbox = false;
79  return false;
80  }
81 
82  if (Frame->m_frameNumber < 0)
83  {
84  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Strange frame number %1").arg(Frame->m_frameNumber));
85  return false;
86  }
87 
88  if (VideoAspect > 1.5F)
89  {
91  {
92  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("The source is already in widescreen (aspect: %1)")
93  .arg(static_cast<double>(VideoAspect)));
96  m_detectLetterboxSwitchFrame = Frame->m_frameNumber;
97  }
98  else
99  {
101  }
102  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("The source is already in widescreen (aspect: %1)")
103  .arg(static_cast<double>(VideoAspect)));
104  m_isDetectLetterbox = false;
105  return false;
106  }
107 
108  // Establish the level of light in the edge
109  int averageY = 0;
110  for (int pos : xPos)
111  {
112  averageY += buf[offsets[0] + 5 * pitches[0] + pos];
113  averageY += buf[offsets[0] + (height - 6) * pitches[0] + pos];
114  }
115 
116  averageY /= NUMBER_OF_DETECTION_LINES * 2;
117  if (averageY > 64) // Too bright to be a letterbox border
118  averageY = 0;
119 
120  // Note - for 10/12 bit etc we only sample the most significant byte
121  bool triplanar = MythVideoFrame::FormatIs420(m_frameType);
123  int leftshift = depth > 8 ? 1 : 0;
124  int rightshift = depth > 8 ? 0 : 1;
125 
126  // Scan the detection lines
127  for (int y = 5; y < height / 4; y++) // skip first pixels incase of noise in the edge
128  {
129  for (int detectionLine = 0; detectionLine < NUMBER_OF_DETECTION_LINES; detectionLine++)
130  {
131  int Y = buf[offsets[0] + y * pitches[0] + (xPos[detectionLine] << leftshift)];
132  int U = 0;
133  int V = 0;
134  if (triplanar)
135  {
136  U = buf[offsets[1] + (y>>1) * pitches[1] + (xPos[detectionLine] >> rightshift)];
137  V = buf[offsets[2] + (y>>1) * pitches[2] + (xPos[detectionLine] >> rightshift)];
138  }
139  else
140  {
141  int offset = offsets[1] + ((y >> 1) * pitches[1]) + (xPos[detectionLine & ~0x1] << leftshift);
142  U = buf[offset];
143  V = buf[offset + (1 << leftshift)];
144  }
145 
146  if ((!topHit[detectionLine]) &&
147  ( Y > averageY + THRESHOLD || Y < averageY - THRESHOLD ||
148  U < 128 - 32 || U > 128 + 32 ||
149  V < 128 - 32 || V > 128 + 32 ))
150  {
151  topHit[detectionLine] = y;
152  topHits++;
153  if (!minTop)
154  minTop = y;
155  maxTop = y;
156  }
157 
158  Y = buf[offsets[0] + (height-y-1) * pitches[0] + (xPos[detectionLine] << leftshift)];
159  if (triplanar)
160  {
161  U = buf[offsets[1] + ((height-y-1) >> 1) * pitches[1] + (xPos[detectionLine] >> rightshift)];
162  V = buf[offsets[2] + ((height-y-1) >> 1) * pitches[2] + (xPos[detectionLine] >> rightshift)];
163  }
164  else
165  {
166  int offset = offsets[1] + (((height - y -1) >> 1) * pitches[1]) + (xPos[detectionLine & ~0x1] << leftshift);
167  U = buf[offset];
168  V = buf[offset + (1 << leftshift)];
169  }
170 
171  if ((!bottomHit[detectionLine]) &&
172  ( Y > averageY + THRESHOLD || Y < averageY - THRESHOLD ||
173  U < 128 - 32 || U > 128 + 32 ||
174  V < 128 - 32 || V > 128 + 32 ))
175  {
176  bottomHit[detectionLine] = y;
177  bottomHits++;
178  if (!minBottom)
179  minBottom = y;
180  maxBottom = y;
181  }
182  }
183 
184  if (topHits == NUMBER_OF_DETECTION_LINES && bottomHits == NUMBER_OF_DETECTION_LINES)
185  break;
186  }
187 
188  if (topHits != NUMBER_OF_DETECTION_LINES)
189  maxTop = height / 4;
190  if (!minTop)
191  minTop = height / 4;
192  if (bottomHits != NUMBER_OF_DETECTION_LINES)
193  maxBottom = height / 4;
194  if (!minBottom)
195  minBottom = height / 4;
196 
197  bool horizontal = (((minTop != 0) && (maxTop - minTop < HORIZONTAL_THRESHOLD)) &&
198  ((minBottom != 0) && (maxBottom - minBottom < HORIZONTAL_THRESHOLD)));
199 
200  if (m_detectLetterboxSwitchFrame > Frame->m_frameNumber) // user is reversing
201  {
206  }
207 
208  if (minTop < halfLimit || minBottom < halfLimit)
210  if (minTop < fullLimit || minBottom < fullLimit)
212 
214  {
215  if (m_detectLetterboxPossibleHalfFrame == -1 && minTop > halfLimit && minBottom > halfLimit)
216  m_detectLetterboxPossibleHalfFrame = Frame->m_frameNumber;
217  }
218  else
219  {
220  if (m_detectLetterboxPossibleHalfFrame == -1 && minTop < fullLimit && minBottom < fullLimit)
221  m_detectLetterboxPossibleHalfFrame = Frame->m_frameNumber;
222  }
223 
224  if (m_detectLetterboxPossibleFullFrame == -1 && minTop > fullLimit && minBottom > fullLimit)
225  m_detectLetterboxPossibleFullFrame = Frame->m_frameNumber;
226 
227  if (maxTop < halfLimit || maxBottom < halfLimit) // Not too restrictive when switching to off
228  {
229  // No Letterbox
231  {
232  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Non Letterbox detected on line: %1 (limit: %2)")
233  .arg(std::min(maxTop, maxBottom)).arg(halfLimit));
236  m_detectLetterboxSwitchFrame = Frame->m_frameNumber;
237  }
238  else
239  {
241  }
242  }
243  else if (horizontal && minTop > halfLimit && minBottom > halfLimit &&
244  maxTop < fullLimit && maxBottom < fullLimit)
245  {
246  // Letterbox (with narrow bars)
248  {
249  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Narrow Letterbox detected on line: %1 (limit: %2) frame: %3")
250  .arg(minTop).arg(halfLimit).arg(m_detectLetterboxPossibleHalfFrame));
253  {
254  // Do not change switch frame if switch to Full mode has not been executed yet
255  }
256  else
257  {
259  }
261  }
262  else
263  {
265  }
266  }
267  else if (horizontal && minTop > fullLimit && minBottom > fullLimit)
268  {
269  // Letterbox
272  {
273  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Detected Letterbox on line: %1 (limit: %2) frame: %3")
274  .arg(minTop).arg(fullLimit).arg(m_detectLetterboxPossibleFullFrame));
278  }
279  else
280  {
282  }
283  }
284  else
285  {
288  }
289 
291  return false;
292 
293  if (m_detectLetterboxSwitchFrame <= Frame->m_frameNumber && m_detectLetterboxConsecutiveCounter > 3)
294  {
295  if (Current != m_detectLetterboxDetectedMode)
296  {
297  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Switched to '%1' on frame %2 (%3)")
299  .arg(Frame->m_frameNumber).arg(m_detectLetterboxSwitchFrame));
301  return true;
302  }
304  }
305  else if (m_detectLetterboxSwitchFrame <= Frame->m_frameNumber)
306  {
307  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Not Switched to '%1' on frame %2 "
308  "(%3) Not enough consecutive detections (%4)")
310  .arg(Frame->m_frameNumber).arg(m_detectLetterboxSwitchFrame)
312  }
313 
314  return false;
315 }
316 
318 {
323 }
324 
326 {
327  return m_isDetectLetterbox;
328 }
DetectLetterbox::Detect
bool Detect(MythVideoFrame *Frame, float VideoAspect, AdjustFillMode &Current)
Detects if this frame is or is not letterboxed.
Definition: DetectLetterbox.cpp:27
FMT_YUV420P10
@ FMT_YUV420P10
Definition: mythframe.h:21
FMT_YUV420P16
@ FMT_YUV420P16
Definition: mythframe.h:24
LOC
#define LOC
Definition: DetectLetterbox.cpp:5
FMT_YUV420P14
@ FMT_YUV420P14
Definition: mythframe.h:23
DetectLetterbox::m_detectLetterboxDefaultMode
AdjustFillMode m_detectLetterboxDefaultMode
Definition: DetectLetterbox.h:24
kAdjustFill_Off
@ kAdjustFill_Off
Definition: videoouttypes.h:74
DetectLetterbox.h
Frame
Definition: zmdefines.h:94
FMT_P010
@ FMT_P010
Definition: mythframe.h:49
arg
arg(title).arg(filename).arg(doDelete))
FramePitches
std::array< int, 3 > FramePitches
Definition: mythframe.h:79
DetectLetterbox::m_detectLetterboxSwitchFrame
long long m_detectLetterboxSwitchFrame
Which mode was last detected.
Definition: DetectLetterbox.h:26
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
DetectLetterbox::m_detectLetterboxLimit
int m_detectLetterboxLimit
Definition: DetectLetterbox.h:30
FrameOffsets
std::array< int, 3 > FrameOffsets
Definition: mythframe.h:80
DetectLetterbox::m_isDetectLetterbox
bool m_isDetectLetterbox
Definition: DetectLetterbox.h:21
DetectLetterbox::m_detectLetterboxDetectedMode
AdjustFillMode m_detectLetterboxDetectedMode
Definition: DetectLetterbox.h:25
toString
QString toString(MarkTypes type)
Definition: programtypes.cpp:26
DetectLetterbox::m_frameType
VideoFrameType m_frameType
Definition: DetectLetterbox.h:23
HORIZONTAL_THRESHOLD
#define HORIZONTAL_THRESHOLD
Definition: DetectLetterbox.cpp:9
FMT_YV12
@ FMT_YV12
Definition: mythframe.h:19
DetectLetterbox::m_firstFrameChecked
long long m_firstFrameChecked
Definition: DetectLetterbox.h:22
NUMBER_OF_DETECTION_LINES
#define NUMBER_OF_DETECTION_LINES
Definition: DetectLetterbox.cpp:7
x1
static int x1
Definition: mythsocket.cpp:60
FMT_YUV420P12
@ FMT_YUV420P12
Definition: mythframe.h:22
DetectLetterbox::m_detectLetterboxPossibleFullFrame
long long m_detectLetterboxPossibleFullFrame
Definition: DetectLetterbox.h:28
MythVideoFrame::ColorDepth
static int ColorDepth(int Format)
Definition: mythframe.h:382
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:60
DetectLetterbox::GetDetectLetterbox
bool GetDetectLetterbox() const
Definition: DetectLetterbox.cpp:325
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:933
DetectLetterbox::SetDetectLetterbox
void SetDetectLetterbox(bool Detect, AdjustFillMode Mode)
Definition: DetectLetterbox.cpp:317
MythVideoFrame::FormatIs420
static bool FormatIs420(VideoFrameType Type)
Definition: mythframe.h:421
FMT_YUV420P9
@ FMT_YUV420P9
Definition: mythframe.h:20
MythVideoFrame::FormatDescription
static QString FormatDescription(VideoFrameType Type)
Definition: mythframe.cpp:363
mythcorecontext.h
AdjustFillMode
AdjustFillMode
Definition: videoouttypes.h:72
DetectLetterbox::m_detectLetterboxConsecutiveCounter
int m_detectLetterboxConsecutiveCounter
Definition: DetectLetterbox.h:29
THRESHOLD
#define THRESHOLD
Definition: DetectLetterbox.cpp:8
kAdjustFill_Half
@ kAdjustFill_Half
Definition: videoouttypes.h:75
MythVideoFrame
Definition: mythframe.h:83
FMT_NV12
@ FMT_NV12
Definition: mythframe.h:48
kAdjustFill_AutoDetect_DefaultOff
@ kAdjustFill_AutoDetect_DefaultOff
Definition: videoouttypes.h:82
FMT_P016
@ FMT_P016
Definition: mythframe.h:50
DetectLetterbox::m_detectLetterboxPossibleHalfFrame
long long m_detectLetterboxPossibleHalfFrame
On which frame was the mode switch detected.
Definition: DetectLetterbox.h:27
DetectLetterbox::DetectLetterbox
DetectLetterbox()
Definition: DetectLetterbox.cpp:11
kAdjustFill_Full
@ kAdjustFill_Full
Definition: videoouttypes.h:76