1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/base/video_util.h"
9 #include "base/logging.h"
10 #include "base/numerics/safe_conversions.h"
11 #include "base/numerics/safe_math.h"
12 #include "media/base/video_frame.h"
13 #include "media/base/yuv_convert.h"
17 gfx::Size
GetNaturalSize(const gfx::Size
& visible_size
,
18 int aspect_ratio_numerator
,
19 int aspect_ratio_denominator
) {
20 if (aspect_ratio_denominator
== 0 ||
21 aspect_ratio_numerator
< 0 ||
22 aspect_ratio_denominator
< 0)
25 double aspect_ratio
= aspect_ratio_numerator
/
26 static_cast<double>(aspect_ratio_denominator
);
28 int width
= floor(visible_size
.width() * aspect_ratio
+ 0.5);
29 int height
= visible_size
.height();
31 // An even width makes things easier for YV12 and appears to be the behavior
32 // expected by WebKit layout tests.
33 return gfx::Size(width
& ~1, height
);
36 void CopyPlane(size_t plane
, const uint8
* source
, int stride
, int rows
,
38 uint8
* dest
= frame
->data(plane
);
39 int dest_stride
= frame
->stride(plane
);
41 // Clamp in case source frame has smaller stride.
42 int bytes_to_copy_per_row
= std::min(frame
->row_bytes(plane
), stride
);
44 // Clamp in case source frame has smaller height.
45 int rows_to_copy
= std::min(frame
->rows(plane
), rows
);
48 for (int row
= 0; row
< rows_to_copy
; ++row
) {
49 memcpy(dest
, source
, bytes_to_copy_per_row
);
55 void CopyYPlane(const uint8
* source
, int stride
, int rows
, VideoFrame
* frame
) {
56 CopyPlane(VideoFrame::kYPlane
, source
, stride
, rows
, frame
);
59 void CopyUPlane(const uint8
* source
, int stride
, int rows
, VideoFrame
* frame
) {
60 CopyPlane(VideoFrame::kUPlane
, source
, stride
, rows
, frame
);
63 void CopyVPlane(const uint8
* source
, int stride
, int rows
, VideoFrame
* frame
) {
64 CopyPlane(VideoFrame::kVPlane
, source
, stride
, rows
, frame
);
67 void CopyAPlane(const uint8
* source
, int stride
, int rows
, VideoFrame
* frame
) {
68 CopyPlane(VideoFrame::kAPlane
, source
, stride
, rows
, frame
);
71 void MakeOpaqueAPlane(int stride
, int rows
, VideoFrame
* frame
) {
72 int rows_to_clear
= std::min(frame
->rows(VideoFrame::kAPlane
), rows
);
73 memset(frame
->data(VideoFrame::kAPlane
), 255,
74 frame
->stride(VideoFrame::kAPlane
) * rows_to_clear
);
77 void FillYUV(VideoFrame
* frame
, uint8 y
, uint8 u
, uint8 v
) {
79 uint8
* y_plane
= frame
->data(VideoFrame::kYPlane
);
80 int y_rows
= frame
->rows(VideoFrame::kYPlane
);
81 int y_row_bytes
= frame
->row_bytes(VideoFrame::kYPlane
);
82 for (int i
= 0; i
< y_rows
; ++i
) {
83 memset(y_plane
, y
, y_row_bytes
);
84 y_plane
+= frame
->stride(VideoFrame::kYPlane
);
87 // Fill the U and V planes.
88 uint8
* u_plane
= frame
->data(VideoFrame::kUPlane
);
89 uint8
* v_plane
= frame
->data(VideoFrame::kVPlane
);
90 int uv_rows
= frame
->rows(VideoFrame::kUPlane
);
91 int u_row_bytes
= frame
->row_bytes(VideoFrame::kUPlane
);
92 int v_row_bytes
= frame
->row_bytes(VideoFrame::kVPlane
);
93 for (int i
= 0; i
< uv_rows
; ++i
) {
94 memset(u_plane
, u
, u_row_bytes
);
95 memset(v_plane
, v
, v_row_bytes
);
96 u_plane
+= frame
->stride(VideoFrame::kUPlane
);
97 v_plane
+= frame
->stride(VideoFrame::kVPlane
);
101 void FillYUVA(VideoFrame
* frame
, uint8 y
, uint8 u
, uint8 v
, uint8 a
) {
102 // Fill Y, U and V planes.
103 FillYUV(frame
, y
, u
, v
);
106 uint8
* a_plane
= frame
->data(VideoFrame::kAPlane
);
107 int a_rows
= frame
->rows(VideoFrame::kAPlane
);
108 int a_row_bytes
= frame
->row_bytes(VideoFrame::kAPlane
);
109 for (int i
= 0; i
< a_rows
; ++i
) {
110 memset(a_plane
, a
, a_row_bytes
);
111 a_plane
+= frame
->stride(VideoFrame::kAPlane
);
115 static void LetterboxPlane(VideoFrame
* frame
,
117 const gfx::Rect
& view_area
,
119 uint8
* ptr
= frame
->data(plane
);
120 const int rows
= frame
->rows(plane
);
121 const int row_bytes
= frame
->row_bytes(plane
);
122 const int stride
= frame
->stride(plane
);
124 CHECK_GE(stride
, row_bytes
);
125 CHECK_GE(view_area
.x(), 0);
126 CHECK_GE(view_area
.y(), 0);
127 CHECK_LE(view_area
.right(), row_bytes
);
128 CHECK_LE(view_area
.bottom(), rows
);
131 for (; y
< view_area
.y(); y
++) {
132 memset(ptr
, fill_byte
, row_bytes
);
135 if (view_area
.width() < row_bytes
) {
136 for (; y
< view_area
.bottom(); y
++) {
137 if (view_area
.x() > 0) {
138 memset(ptr
, fill_byte
, view_area
.x());
140 if (view_area
.right() < row_bytes
) {
141 memset(ptr
+ view_area
.right(),
143 row_bytes
- view_area
.right());
148 y
+= view_area
.height();
149 ptr
+= stride
* view_area
.height();
151 for (; y
< rows
; y
++) {
152 memset(ptr
, fill_byte
, row_bytes
);
157 void LetterboxYUV(VideoFrame
* frame
, const gfx::Rect
& view_area
) {
158 DCHECK(!(view_area
.x() & 1));
159 DCHECK(!(view_area
.y() & 1));
160 DCHECK(!(view_area
.width() & 1));
161 DCHECK(!(view_area
.height() & 1));
162 DCHECK(frame
->format() == PIXEL_FORMAT_YV12
||
163 frame
->format() == PIXEL_FORMAT_I420
);
164 LetterboxPlane(frame
, VideoFrame::kYPlane
, view_area
, 0x00);
165 gfx::Rect
half_view_area(view_area
.x() / 2,
167 view_area
.width() / 2,
168 view_area
.height() / 2);
169 LetterboxPlane(frame
, VideoFrame::kUPlane
, half_view_area
, 0x80);
170 LetterboxPlane(frame
, VideoFrame::kVPlane
, half_view_area
, 0x80);
173 void RotatePlaneByPixels(
178 int rotation
, // Clockwise.
181 DCHECK((width
> 0) && (height
> 0) &&
182 ((width
& 1) == 0) && ((height
& 1) == 0) &&
183 (rotation
>= 0) && (rotation
< 360) && (rotation
% 90 == 0));
185 // Consolidate cases. Only 0 and 90 are left.
186 if (rotation
== 180 || rotation
== 270) {
188 flip_vert
= !flip_vert
;
189 flip_horiz
= !flip_horiz
;
192 int num_rows
= height
;
193 int num_cols
= width
;
194 int src_stride
= width
;
195 // During pixel copying, the corresponding incremental of dest pointer
196 // when src pointer moves to next row.
197 int dest_row_step
= width
;
198 // During pixel copying, the corresponding incremental of dest pointer
199 // when src pointer moves to next column.
200 int dest_col_step
= 1;
204 // Use pixel copying.
208 dest_row_step
= -width
;
209 dest
+= height
* width
- 1;
215 // Fast copy by rows.
216 dest
+= width
* (height
- 1);
217 for (int row
= 0; row
< height
; ++row
) {
218 memcpy(dest
, src
, width
);
223 memcpy(dest
, src
, width
* height
);
227 } else if (rotation
== 90) {
229 if (width
> height
) {
230 offset
= (width
- height
) / 2;
232 num_rows
= num_cols
= height
;
234 offset
= (height
- width
) / 2;
235 src
+= width
* offset
;
236 num_rows
= num_cols
= width
;
239 dest_col_step
= (flip_vert
? -width
: width
);
240 dest_row_step
= (flip_horiz
? 1 : -1);
243 dest
+= (width
> height
? width
* (height
- 1) + offset
:
244 width
* (height
- offset
- 1));
246 dest
+= (width
> height
? offset
: width
* offset
);
250 dest
+= (width
> height
? width
* height
- offset
- 1 :
251 width
* (height
- offset
) - 1);
253 dest
+= (width
> height
? width
- offset
- 1 :
254 width
* (offset
+ 1) - 1);
262 for (int row
= 0; row
< num_rows
; ++row
) {
263 const uint8
* src_ptr
= src
;
264 uint8
* dest_ptr
= dest
;
265 for (int col
= 0; col
< num_cols
; ++col
) {
266 *dest_ptr
= *src_ptr
++;
267 dest_ptr
+= dest_col_step
;
270 dest
+= dest_row_step
;
274 // Helper function to return |a| divided by |b|, rounded to the nearest integer.
275 static int RoundedDivision(int64 a
, int b
) {
278 base::CheckedNumeric
<uint64
> result(a
);
281 return base::checked_cast
<int>(result
.ValueOrDie());
284 // Common logic for the letterboxing and scale-within/scale-encompassing
285 // functions. Scales |size| to either fit within or encompass |target|,
286 // depending on whether |fit_within_target| is true.
287 static gfx::Size
ScaleSizeToTarget(const gfx::Size
& size
,
288 const gfx::Size
& target
,
289 bool fit_within_target
) {
291 return gfx::Size(); // Corner case: Aspect ratio is undefined.
293 const int64 x
= static_cast<int64
>(size
.width()) * target
.height();
294 const int64 y
= static_cast<int64
>(size
.height()) * target
.width();
295 const bool use_target_width
= fit_within_target
? (y
< x
) : (x
< y
);
296 return use_target_width
?
297 gfx::Size(target
.width(), RoundedDivision(y
, size
.width())) :
298 gfx::Size(RoundedDivision(x
, size
.height()), target
.height());
301 gfx::Rect
ComputeLetterboxRegion(const gfx::Rect
& bounds
,
302 const gfx::Size
& content
) {
303 // If |content| has an undefined aspect ratio, let's not try to divide by
305 if (content
.IsEmpty())
308 gfx::Rect result
= bounds
;
309 result
.ClampToCenteredSize(ScaleSizeToTarget(content
, bounds
.size(), true));
313 gfx::Size
ScaleSizeToFitWithinTarget(const gfx::Size
& size
,
314 const gfx::Size
& target
) {
315 return ScaleSizeToTarget(size
, target
, true);
318 gfx::Size
ScaleSizeToEncompassTarget(const gfx::Size
& size
,
319 const gfx::Size
& target
) {
320 return ScaleSizeToTarget(size
, target
, false);
323 gfx::Size
PadToMatchAspectRatio(const gfx::Size
& size
,
324 const gfx::Size
& target
) {
325 if (target
.IsEmpty())
326 return gfx::Size(); // Aspect ratio is undefined.
328 const int64 x
= static_cast<int64
>(size
.width()) * target
.height();
329 const int64 y
= static_cast<int64
>(size
.height()) * target
.width();
331 return gfx::Size(RoundedDivision(y
, target
.height()), size
.height());
332 return gfx::Size(size
.width(), RoundedDivision(x
, target
.width()));
335 void CopyRGBToVideoFrame(const uint8
* source
,
337 const gfx::Rect
& region_in_frame
,
339 const int kY
= VideoFrame::kYPlane
;
340 const int kU
= VideoFrame::kUPlane
;
341 const int kV
= VideoFrame::kVPlane
;
342 CHECK_EQ(frame
->stride(kU
), frame
->stride(kV
));
343 const int uv_stride
= frame
->stride(kU
);
345 if (region_in_frame
!= gfx::Rect(frame
->coded_size())) {
346 LetterboxYUV(frame
, region_in_frame
);
349 const int y_offset
= region_in_frame
.x()
350 + (region_in_frame
.y() * frame
->stride(kY
));
351 const int uv_offset
= region_in_frame
.x() / 2
352 + (region_in_frame
.y() / 2 * uv_stride
);
354 ConvertRGB32ToYUV(source
,
355 frame
->data(kY
) + y_offset
,
356 frame
->data(kU
) + uv_offset
,
357 frame
->data(kV
) + uv_offset
,
358 region_in_frame
.width(),
359 region_in_frame
.height(),