MythTV  master
DetectLetterbox.cpp
Go to the documentation of this file.
1 // MythTV
3 #include "DetectLetterbox.h"
4 
5 #define LOC QString("DetectLetterbox: ")
6 
7 static constexpr int8_t NUMBER_OF_DETECTION_LINES { 3 }; // How many lines are we looking at
8 static constexpr int8_t THRESHOLD { 5 }; // Y component has to not vary more than this in the bars
9 static constexpr int8_t 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  if (!buf)
34  return false;
35 
36  const FramePitches pitches = Frame->m_pitches;
37  const FrameOffsets offsets = Frame->m_offsets;
38  const int height = Frame->m_height;
39 
40  // If the black bars is larger than this limit we switch to Half or Full Mode
41  // const int fullLimit = static_cast<int>(((height - width * 9 / 16) / 2) * m_detectLetterboxLimit / 100);
42  // const int halfLimit = (static_cast<int>(((height - width * 9 / 14) / 2) * m_detectLetterboxLimit / 100);
43 
44  // If the black bars is larger than this limit we switch to Half or Full Mode
45  const int fullLimit = static_cast<int>((height * (1 - VideoAspect * 9 / 16) / 2) * m_detectLetterboxLimit / 100);
46  const int halfLimit = static_cast<int>((height * (1 - VideoAspect * 9 / 14) / 2) * m_detectLetterboxLimit / 100);
47 
48  // Lines to scan for black letterbox edge
49  const std::array<int,3> xPos { Frame->m_width / 4, Frame->m_width / 2, Frame->m_width * 3 / 4} ;
50  int topHits = 0;
51  int bottomHits = 0;
52  int minTop = 0;
53  int minBottom = 0;
54  int maxTop = 0;
55  int maxBottom = 0;
56  std::array<int,3> topHit = { 0, 0, 0 };
57  std::array<int,3> bottomHit = { 0, 0, 0 };
58 
59  switch (Frame->m_type)
60  {
61  case FMT_YV12:
62  case FMT_YUV420P9:
63  case FMT_YUV420P10:
64  case FMT_YUV420P12:
65  case FMT_YUV420P14:
66  case FMT_YUV420P16:
67  case FMT_NV12:
68  case FMT_P010:
69  case FMT_P016:
70  if (!m_firstFrameChecked || m_frameType != Frame->m_type)
71  {
72  m_firstFrameChecked = Frame->m_frameNumber;
73  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("'%1' frame format detected")
75  }
76  m_frameType = Frame->m_type;
77  break;
78  default:
79  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("'%1' frame format is not supported")
81  m_isDetectLetterbox = false;
82  return false;
83  }
84 
85  if (Frame->m_frameNumber < 0)
86  {
87  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Strange frame number %1").arg(Frame->m_frameNumber));
88  return false;
89  }
90 
91  if (VideoAspect > 1.5F)
92  {
94  {
95  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("The source is already in widescreen (aspect: %1)")
96  .arg(static_cast<double>(VideoAspect)));
99  m_detectLetterboxSwitchFrame = Frame->m_frameNumber;
100  }
101  else
102  {
104  }
105  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("The source is already in widescreen (aspect: %1)")
106  .arg(static_cast<double>(VideoAspect)));
107  m_isDetectLetterbox = false;
108  return false;
109  }
110 
111  // Establish the level of light in the edge
112  int averageY = 0;
113  for (int pos : xPos)
114  {
115  averageY += buf[offsets[0] + 5 * pitches[0] + pos];
116  averageY += buf[offsets[0] + (height - 6) * pitches[0] + pos];
117  }
118 
119  averageY /= NUMBER_OF_DETECTION_LINES * 2;
120  if (averageY > 64) // Too bright to be a letterbox border
121  averageY = 0;
122 
123  // Note - for 10/12 bit etc we only sample the most significant byte
124  bool triplanar = MythVideoFrame::FormatIs420(m_frameType);
126  int leftshift = depth > 8 ? 1 : 0;
127  int rightshift = depth > 8 ? 0 : 1;
128 
129  // Scan the detection lines
130  for (int y = 5; y < height / 4; y++) // skip first pixels incase of noise in the edge
131  {
132  for (int detectionLine = 0; detectionLine < NUMBER_OF_DETECTION_LINES; detectionLine++)
133  {
134  int Y = buf[offsets[0] + y * pitches[0] + (xPos[detectionLine] << leftshift)];
135  int U = 0;
136  int V = 0;
137  if (triplanar)
138  {
139  U = buf[offsets[1] + (y>>1) * pitches[1] + (xPos[detectionLine] >> rightshift)];
140  V = buf[offsets[2] + (y>>1) * pitches[2] + (xPos[detectionLine] >> rightshift)];
141  }
142  else
143  {
144  int offset = offsets[1] + ((y >> 1) * pitches[1]) + (xPos[detectionLine & ~0x1] << leftshift);
145  U = buf[offset];
146  V = buf[offset + (1 << leftshift)];
147  }
148 
149  if ((!topHit[detectionLine]) &&
150  ( Y > averageY + THRESHOLD || Y < averageY - THRESHOLD ||
151  U < 128 - 32 || U > 128 + 32 ||
152  V < 128 - 32 || V > 128 + 32 ))
153  {
154  topHit[detectionLine] = y;
155  topHits++;
156  if (!minTop)
157  minTop = y;
158  maxTop = y;
159  }
160 
161  Y = buf[offsets[0] + (height-y-1) * pitches[0] + (xPos[detectionLine] << leftshift)];
162  if (triplanar)
163  {
164  U = buf[offsets[1] + ((height-y-1) >> 1) * pitches[1] + (xPos[detectionLine] >> rightshift)];
165  V = buf[offsets[2] + ((height-y-1) >> 1) * pitches[2] + (xPos[detectionLine] >> rightshift)];
166  }
167  else
168  {
169  int offset = offsets[1] + (((height - y -1) >> 1) * pitches[1]) + (xPos[detectionLine & ~0x1] << leftshift);
170  U = buf[offset];
171  V = buf[offset + (1 << leftshift)];
172  }
173 
174  if ((!bottomHit[detectionLine]) &&
175  ( Y > averageY + THRESHOLD || Y < averageY - THRESHOLD ||
176  U < 128 - 32 || U > 128 + 32 ||
177  V < 128 - 32 || V > 128 + 32 ))
178  {
179  bottomHit[detectionLine] = y;
180  bottomHits++;
181  if (!minBottom)
182  minBottom = y;
183  maxBottom = y;
184  }
185  }
186 
187  if (topHits == NUMBER_OF_DETECTION_LINES && bottomHits == NUMBER_OF_DETECTION_LINES)
188  break;
189  }
190 
191  if (topHits != NUMBER_OF_DETECTION_LINES)
192  maxTop = height / 4;
193  if (!minTop)
194  minTop = height / 4;
195  if (bottomHits != NUMBER_OF_DETECTION_LINES)
196  maxBottom = height / 4;
197  if (!minBottom)
198  minBottom = height / 4;
199 
200  bool horizontal = (((minTop != 0) && (maxTop - minTop < HORIZONTAL_THRESHOLD)) &&
201  ((minBottom != 0) && (maxBottom - minBottom < HORIZONTAL_THRESHOLD)));
202 
203  if (m_detectLetterboxSwitchFrame > Frame->m_frameNumber) // user is reversing
204  {
209  }
210 
211  if (minTop < halfLimit || minBottom < halfLimit)
213  if (minTop < fullLimit || minBottom < fullLimit)
215 
217  {
218  if (m_detectLetterboxPossibleHalfFrame == -1 && minTop > halfLimit && minBottom > halfLimit)
219  m_detectLetterboxPossibleHalfFrame = Frame->m_frameNumber;
220  }
221  else
222  {
223  if (m_detectLetterboxPossibleHalfFrame == -1 && minTop < fullLimit && minBottom < fullLimit)
224  m_detectLetterboxPossibleHalfFrame = Frame->m_frameNumber;
225  }
226 
227  if (m_detectLetterboxPossibleFullFrame == -1 && minTop > fullLimit && minBottom > fullLimit)
228  m_detectLetterboxPossibleFullFrame = Frame->m_frameNumber;
229 
230  if (maxTop < halfLimit || maxBottom < halfLimit) // Not too restrictive when switching to off
231  {
232  // No Letterbox
234  {
235  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Non Letterbox detected on line: %1 (limit: %2)")
236  .arg(std::min(maxTop, maxBottom)).arg(halfLimit));
239  m_detectLetterboxSwitchFrame = Frame->m_frameNumber;
240  }
241  else
242  {
244  }
245  }
246  else if (horizontal && minTop > halfLimit && minBottom > halfLimit &&
247  maxTop < fullLimit && maxBottom < fullLimit)
248  {
249  // Letterbox (with narrow bars)
251  {
252  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Narrow Letterbox detected on line: %1 (limit: %2) frame: %3")
253  .arg(minTop).arg(halfLimit).arg(m_detectLetterboxPossibleHalfFrame));
256  {
257  // Do not change switch frame if switch to Full mode has not been executed yet
258  }
259  else
260  {
262  }
264  }
265  else
266  {
268  }
269  }
270  else if (horizontal && minTop > fullLimit && minBottom > fullLimit)
271  {
272  // Letterbox
275  {
276  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Detected Letterbox on line: %1 (limit: %2) frame: %3")
277  .arg(minTop).arg(fullLimit).arg(m_detectLetterboxPossibleFullFrame));
281  }
282  else
283  {
285  }
286  }
287  else
288  {
291  }
292 
294  return false;
295 
296  if (m_detectLetterboxSwitchFrame <= Frame->m_frameNumber && m_detectLetterboxConsecutiveCounter > 3)
297  {
298  if (Current != m_detectLetterboxDetectedMode)
299  {
300  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Switched to '%1' on frame %2 (%3)")
302  .arg(Frame->m_frameNumber).arg(m_detectLetterboxSwitchFrame));
304  return true;
305  }
307  }
308  else if (m_detectLetterboxSwitchFrame <= Frame->m_frameNumber)
309  {
310  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Not Switched to '%1' on frame %2 "
311  "(%3) Not enough consecutive detections (%4)")
313  .arg(Frame->m_frameNumber).arg(m_detectLetterboxSwitchFrame)
315  }
316 
317  return false;
318 }
319 
321 {
326 }
327 
329 {
330  return m_isDetectLetterbox;
331 }
DetectLetterbox::Detect
bool Detect(MythVideoFrame *Frame, float VideoAspect, AdjustFillMode &Current)
Detects if this frame is or is not letterboxed.
Definition: DetectLetterbox.cpp:27
MythDate::toString
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:84
FMT_YUV420P10
@ FMT_YUV420P10
Definition: mythframe.h:26
FMT_YUV420P16
@ FMT_YUV420P16
Definition: mythframe.h:29
LOC
#define LOC
Definition: DetectLetterbox.cpp:5
FMT_YUV420P14
@ FMT_YUV420P14
Definition: mythframe.h:28
Mode
Mode
Definition: synaesthesia.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:93
HORIZONTAL_THRESHOLD
static constexpr int8_t HORIZONTAL_THRESHOLD
Definition: DetectLetterbox.cpp:9
FMT_P010
@ FMT_P010
Definition: mythframe.h:54
FramePitches
std::array< int, 3 > FramePitches
Definition: mythframe.h:84
THRESHOLD
static constexpr int8_t THRESHOLD
Definition: DetectLetterbox.cpp:8
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:39
DetectLetterbox::m_detectLetterboxLimit
int m_detectLetterboxLimit
Definition: DetectLetterbox.h:30
FrameOffsets
std::array< int, 3 > FrameOffsets
Definition: mythframe.h:85
DetectLetterbox::m_isDetectLetterbox
bool m_isDetectLetterbox
Definition: DetectLetterbox.h:21
DetectLetterbox::m_detectLetterboxDetectedMode
AdjustFillMode m_detectLetterboxDetectedMode
Definition: DetectLetterbox.h:25
DetectLetterbox::m_frameType
VideoFrameType m_frameType
Definition: DetectLetterbox.h:23
FMT_YV12
@ FMT_YV12
Definition: mythframe.h:24
DetectLetterbox::m_firstFrameChecked
long long m_firstFrameChecked
Definition: DetectLetterbox.h:22
x1
static int x1
Definition: mythsocket.cpp:50
FMT_YUV420P12
@ FMT_YUV420P12
Definition: mythframe.h:27
DetectLetterbox::m_detectLetterboxPossibleFullFrame
long long m_detectLetterboxPossibleFullFrame
Definition: DetectLetterbox.h:28
MythVideoFrame::ColorDepth
static int ColorDepth(int Format)
Definition: mythframe.h:399
NUMBER_OF_DETECTION_LINES
static constexpr int8_t NUMBER_OF_DETECTION_LINES
Definition: DetectLetterbox.cpp:7
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:55
DetectLetterbox::GetDetectLetterbox
bool GetDetectLetterbox() const
Definition: DetectLetterbox.cpp:328
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:912
DetectLetterbox::SetDetectLetterbox
void SetDetectLetterbox(bool Detect, AdjustFillMode Mode)
Definition: DetectLetterbox.cpp:320
MythVideoFrame::FormatIs420
static bool FormatIs420(VideoFrameType Type)
Definition: mythframe.h:438
FMT_YUV420P9
@ FMT_YUV420P9
Definition: mythframe.h:25
MythVideoFrame::FormatDescription
static QString FormatDescription(VideoFrameType Type)
Definition: mythframe.cpp:368
mythcorecontext.h
AdjustFillMode
AdjustFillMode
Definition: videoouttypes.h:71
DetectLetterbox::m_detectLetterboxConsecutiveCounter
int m_detectLetterboxConsecutiveCounter
Definition: DetectLetterbox.h:29
kAdjustFill_Half
@ kAdjustFill_Half
Definition: videoouttypes.h:75
MythVideoFrame
Definition: mythframe.h:88
FMT_NV12
@ FMT_NV12
Definition: mythframe.h:53
kAdjustFill_AutoDetect_DefaultOff
@ kAdjustFill_AutoDetect_DefaultOff
Definition: videoouttypes.h:82
FMT_P016
@ FMT_P016
Definition: mythframe.h:55
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