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 "media/base/video_frame.h"
11 #include "media/base/yuv_convert.h"
15 gfx::Size
GetNaturalSize(const gfx::Size
& visible_size
,
16 int aspect_ratio_numerator
,
17 int aspect_ratio_denominator
) {
18 if (aspect_ratio_denominator
== 0 ||
19 aspect_ratio_numerator
< 0 ||
20 aspect_ratio_denominator
< 0)
23 double aspect_ratio
= aspect_ratio_numerator
/
24 static_cast<double>(aspect_ratio_denominator
);
26 int width
= floor(visible_size
.width() * aspect_ratio
+ 0.5);
27 int height
= visible_size
.height();
29 // An even width makes things easier for YV12 and appears to be the behavior
30 // expected by WebKit layout tests.
31 return gfx::Size(width
& ~1, height
);
34 void CopyPlane(size_t plane
, const uint8
* source
, int stride
, int rows
,
36 uint8
* dest
= frame
->data(plane
);
37 int dest_stride
= frame
->stride(plane
);
39 // Clamp in case source frame has smaller stride.
40 int bytes_to_copy_per_row
= std::min(frame
->row_bytes(plane
), stride
);
42 // Clamp in case source frame has smaller height.
43 int rows_to_copy
= std::min(frame
->rows(plane
), rows
);
46 for (int row
= 0; row
< rows_to_copy
; ++row
) {
47 memcpy(dest
, source
, bytes_to_copy_per_row
);
53 void CopyYPlane(const uint8
* source
, int stride
, int rows
, VideoFrame
* frame
) {
54 CopyPlane(VideoFrame::kYPlane
, source
, stride
, rows
, frame
);
57 void CopyUPlane(const uint8
* source
, int stride
, int rows
, VideoFrame
* frame
) {
58 CopyPlane(VideoFrame::kUPlane
, source
, stride
, rows
, frame
);
61 void CopyVPlane(const uint8
* source
, int stride
, int rows
, VideoFrame
* frame
) {
62 CopyPlane(VideoFrame::kVPlane
, source
, stride
, rows
, frame
);
65 void CopyAPlane(const uint8
* source
, int stride
, int rows
, VideoFrame
* frame
) {
66 CopyPlane(VideoFrame::kAPlane
, source
, stride
, rows
, frame
);
69 void MakeOpaqueAPlane(int stride
, int rows
, VideoFrame
* frame
) {
70 int rows_to_clear
= std::min(frame
->rows(VideoFrame::kAPlane
), rows
);
71 memset(frame
->data(VideoFrame::kAPlane
), 255,
72 frame
->stride(VideoFrame::kAPlane
) * rows_to_clear
);
75 void FillYUV(VideoFrame
* frame
, uint8 y
, uint8 u
, uint8 v
) {
77 uint8
* y_plane
= frame
->data(VideoFrame::kYPlane
);
78 int y_rows
= frame
->rows(VideoFrame::kYPlane
);
79 int y_row_bytes
= frame
->row_bytes(VideoFrame::kYPlane
);
80 for (int i
= 0; i
< y_rows
; ++i
) {
81 memset(y_plane
, y
, y_row_bytes
);
82 y_plane
+= frame
->stride(VideoFrame::kYPlane
);
85 // Fill the U and V planes.
86 uint8
* u_plane
= frame
->data(VideoFrame::kUPlane
);
87 uint8
* v_plane
= frame
->data(VideoFrame::kVPlane
);
88 int uv_rows
= frame
->rows(VideoFrame::kUPlane
);
89 int u_row_bytes
= frame
->row_bytes(VideoFrame::kUPlane
);
90 int v_row_bytes
= frame
->row_bytes(VideoFrame::kVPlane
);
91 for (int i
= 0; i
< uv_rows
; ++i
) {
92 memset(u_plane
, u
, u_row_bytes
);
93 memset(v_plane
, v
, v_row_bytes
);
94 u_plane
+= frame
->stride(VideoFrame::kUPlane
);
95 v_plane
+= frame
->stride(VideoFrame::kVPlane
);
99 static void LetterboxPlane(VideoFrame
* frame
,
101 const gfx::Rect
& view_area
,
103 uint8
* ptr
= frame
->data(plane
);
104 const int rows
= frame
->rows(plane
);
105 const int row_bytes
= frame
->row_bytes(plane
);
106 const int stride
= frame
->stride(plane
);
108 CHECK_GE(stride
, row_bytes
);
109 CHECK_GE(view_area
.x(), 0);
110 CHECK_GE(view_area
.y(), 0);
111 CHECK_LE(view_area
.right(), row_bytes
);
112 CHECK_LE(view_area
.bottom(), rows
);
115 for (; y
< view_area
.y(); y
++) {
116 memset(ptr
, fill_byte
, row_bytes
);
119 if (view_area
.width() < row_bytes
) {
120 for (; y
< view_area
.bottom(); y
++) {
121 if (view_area
.x() > 0) {
122 memset(ptr
, fill_byte
, view_area
.x());
124 if (view_area
.right() < row_bytes
) {
125 memset(ptr
+ view_area
.right(),
127 row_bytes
- view_area
.right());
132 y
+= view_area
.height();
133 ptr
+= stride
* view_area
.height();
135 for (; y
< rows
; y
++) {
136 memset(ptr
, fill_byte
, row_bytes
);
141 void LetterboxYUV(VideoFrame
* frame
, const gfx::Rect
& view_area
) {
142 DCHECK(!(view_area
.x() & 1));
143 DCHECK(!(view_area
.y() & 1));
144 DCHECK(!(view_area
.width() & 1));
145 DCHECK(!(view_area
.height() & 1));
146 DCHECK(frame
->format() == VideoFrame::YV12
||
147 frame
->format() == VideoFrame::YV12J
||
148 frame
->format() == VideoFrame::I420
);
149 LetterboxPlane(frame
, VideoFrame::kYPlane
, view_area
, 0x00);
150 gfx::Rect
half_view_area(view_area
.x() / 2,
152 view_area
.width() / 2,
153 view_area
.height() / 2);
154 LetterboxPlane(frame
, VideoFrame::kUPlane
, half_view_area
, 0x80);
155 LetterboxPlane(frame
, VideoFrame::kVPlane
, half_view_area
, 0x80);
158 void RotatePlaneByPixels(
163 int rotation
, // Clockwise.
166 DCHECK((width
> 0) && (height
> 0) &&
167 ((width
& 1) == 0) && ((height
& 1) == 0) &&
168 (rotation
>= 0) && (rotation
< 360) && (rotation
% 90 == 0));
170 // Consolidate cases. Only 0 and 90 are left.
171 if (rotation
== 180 || rotation
== 270) {
173 flip_vert
= !flip_vert
;
174 flip_horiz
= !flip_horiz
;
177 int num_rows
= height
;
178 int num_cols
= width
;
179 int src_stride
= width
;
180 // During pixel copying, the corresponding incremental of dest pointer
181 // when src pointer moves to next row.
182 int dest_row_step
= width
;
183 // During pixel copying, the corresponding incremental of dest pointer
184 // when src pointer moves to next column.
185 int dest_col_step
= 1;
189 // Use pixel copying.
193 dest_row_step
= -width
;
194 dest
+= height
* width
- 1;
200 // Fast copy by rows.
201 dest
+= width
* (height
- 1);
202 for (int row
= 0; row
< height
; ++row
) {
203 memcpy(dest
, src
, width
);
208 memcpy(dest
, src
, width
* height
);
212 } else if (rotation
== 90) {
214 if (width
> height
) {
215 offset
= (width
- height
) / 2;
217 num_rows
= num_cols
= height
;
219 offset
= (height
- width
) / 2;
220 src
+= width
* offset
;
221 num_rows
= num_cols
= width
;
224 dest_col_step
= (flip_vert
? -width
: width
);
225 dest_row_step
= (flip_horiz
? 1 : -1);
228 dest
+= (width
> height
? width
* (height
- 1) + offset
:
229 width
* (height
- offset
- 1));
231 dest
+= (width
> height
? offset
: width
* offset
);
235 dest
+= (width
> height
? width
* height
- offset
- 1 :
236 width
* (height
- offset
) - 1);
238 dest
+= (width
> height
? width
- offset
- 1 :
239 width
* (offset
+ 1) - 1);
247 for (int row
= 0; row
< num_rows
; ++row
) {
248 const uint8
* src_ptr
= src
;
249 uint8
* dest_ptr
= dest
;
250 for (int col
= 0; col
< num_cols
; ++col
) {
251 *dest_ptr
= *src_ptr
++;
252 dest_ptr
+= dest_col_step
;
255 dest
+= dest_row_step
;
259 gfx::Rect
ComputeLetterboxRegion(const gfx::Rect
& bounds
,
260 const gfx::Size
& content
) {
261 // If |content| has an undefined aspect ratio, let's not try to divide by
263 if (content
.IsEmpty())
266 int64 x
= static_cast<int64
>(content
.width()) * bounds
.height();
267 int64 y
= static_cast<int64
>(content
.height()) * bounds
.width();
269 gfx::Size
letterbox(bounds
.width(), bounds
.height());
271 letterbox
.set_height(static_cast<int>(y
/ content
.width()));
273 letterbox
.set_width(static_cast<int>(x
/ content
.height()));
274 gfx::Rect result
= bounds
;
275 result
.ClampToCenteredSize(letterbox
);
279 void CopyRGBToVideoFrame(const uint8
* source
,
281 const gfx::Rect
& region_in_frame
,
283 const int kY
= VideoFrame::kYPlane
;
284 const int kU
= VideoFrame::kUPlane
;
285 const int kV
= VideoFrame::kVPlane
;
286 CHECK_EQ(frame
->stride(kU
), frame
->stride(kV
));
287 const int uv_stride
= frame
->stride(kU
);
289 if (region_in_frame
!= gfx::Rect(frame
->coded_size())) {
290 LetterboxYUV(frame
, region_in_frame
);
293 const int y_offset
= region_in_frame
.x()
294 + (region_in_frame
.y() * frame
->stride(kY
));
295 const int uv_offset
= region_in_frame
.x() / 2
296 + (region_in_frame
.y() / 2 * uv_stride
);
298 ConvertRGB32ToYUV(source
,
299 frame
->data(kY
) + y_offset
,
300 frame
->data(kU
) + uv_offset
,
301 frame
->data(kV
) + uv_offset
,
302 region_in_frame
.width(),
303 region_in_frame
.height(),