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.
4
5 #include "media/base/video_util.h"
6
7 #include <cmath>
8
9 #include "base/logging.h"
10 #include "media/base/video_frame.h"
11 #include "media/base/yuv_convert.h"
12
13 namespace media {
14
GetNaturalSize(const gfx::Size & visible_size,int aspect_ratio_numerator,int aspect_ratio_denominator)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)
21 return gfx::Size();
22
23 double aspect_ratio = aspect_ratio_numerator /
24 static_cast<double>(aspect_ratio_denominator);
25
26 int width = floor(visible_size.width() * aspect_ratio + 0.5);
27 int height = visible_size.height();
28
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);
32 }
33
CopyPlane(size_t plane,const uint8 * source,int stride,int rows,VideoFrame * frame)34 void CopyPlane(size_t plane, const uint8* source, int stride, int rows,
35 VideoFrame* frame) {
36 uint8* dest = frame->data(plane);
37 int dest_stride = frame->stride(plane);
38
39 // Clamp in case source frame has smaller stride.
40 int bytes_to_copy_per_row = std::min(frame->row_bytes(plane), stride);
41
42 // Clamp in case source frame has smaller height.
43 int rows_to_copy = std::min(frame->rows(plane), rows);
44
45 // Copy!
46 for (int row = 0; row < rows_to_copy; ++row) {
47 memcpy(dest, source, bytes_to_copy_per_row);
48 source += stride;
49 dest += dest_stride;
50 }
51 }
52
CopyYPlane(const uint8 * source,int stride,int rows,VideoFrame * frame)53 void CopyYPlane(const uint8* source, int stride, int rows, VideoFrame* frame) {
54 CopyPlane(VideoFrame::kYPlane, source, stride, rows, frame);
55 }
56
CopyUPlane(const uint8 * source,int stride,int rows,VideoFrame * frame)57 void CopyUPlane(const uint8* source, int stride, int rows, VideoFrame* frame) {
58 CopyPlane(VideoFrame::kUPlane, source, stride, rows, frame);
59 }
60
CopyVPlane(const uint8 * source,int stride,int rows,VideoFrame * frame)61 void CopyVPlane(const uint8* source, int stride, int rows, VideoFrame* frame) {
62 CopyPlane(VideoFrame::kVPlane, source, stride, rows, frame);
63 }
64
CopyAPlane(const uint8 * source,int stride,int rows,VideoFrame * frame)65 void CopyAPlane(const uint8* source, int stride, int rows, VideoFrame* frame) {
66 CopyPlane(VideoFrame::kAPlane, source, stride, rows, frame);
67 }
68
MakeOpaqueAPlane(int stride,int rows,VideoFrame * 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);
73 }
74
FillYUV(VideoFrame * frame,uint8 y,uint8 u,uint8 v)75 void FillYUV(VideoFrame* frame, uint8 y, uint8 u, uint8 v) {
76 // Fill the Y plane.
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);
83 }
84
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);
96 }
97 }
98
LetterboxPlane(VideoFrame * frame,int plane,const gfx::Rect & view_area,uint8 fill_byte)99 static void LetterboxPlane(VideoFrame* frame,
100 int plane,
101 const gfx::Rect& view_area,
102 uint8 fill_byte) {
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);
107
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);
113
114 int y = 0;
115 for (; y < view_area.y(); y++) {
116 memset(ptr, fill_byte, row_bytes);
117 ptr += stride;
118 }
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());
123 }
124 if (view_area.right() < row_bytes) {
125 memset(ptr + view_area.right(),
126 fill_byte,
127 row_bytes - view_area.right());
128 }
129 ptr += stride;
130 }
131 } else {
132 y += view_area.height();
133 ptr += stride * view_area.height();
134 }
135 for (; y < rows; y++) {
136 memset(ptr, fill_byte, row_bytes);
137 ptr += stride;
138 }
139 }
140
LetterboxYUV(VideoFrame * frame,const gfx::Rect & view_area)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,
151 view_area.y() / 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);
156 }
157
RotatePlaneByPixels(const uint8 * src,uint8 * dest,int width,int height,int rotation,bool flip_vert,bool flip_horiz)158 void RotatePlaneByPixels(
159 const uint8* src,
160 uint8* dest,
161 int width,
162 int height,
163 int rotation, // Clockwise.
164 bool flip_vert,
165 bool flip_horiz) {
166 DCHECK((width > 0) && (height > 0) &&
167 ((width & 1) == 0) && ((height & 1) == 0) &&
168 (rotation >= 0) && (rotation < 360) && (rotation % 90 == 0));
169
170 // Consolidate cases. Only 0 and 90 are left.
171 if (rotation == 180 || rotation == 270) {
172 rotation -= 180;
173 flip_vert = !flip_vert;
174 flip_horiz = !flip_horiz;
175 }
176
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;
186
187 if (rotation == 0) {
188 if (flip_horiz) {
189 // Use pixel copying.
190 dest_col_step = -1;
191 if (flip_vert) {
192 // Rotation 180.
193 dest_row_step = -width;
194 dest += height * width - 1;
195 } else {
196 dest += width - 1;
197 }
198 } else {
199 if (flip_vert) {
200 // Fast copy by rows.
201 dest += width * (height - 1);
202 for (int row = 0; row < height; ++row) {
203 memcpy(dest, src, width);
204 src += width;
205 dest -= width;
206 }
207 } else {
208 memcpy(dest, src, width * height);
209 }
210 return;
211 }
212 } else if (rotation == 90) {
213 int offset;
214 if (width > height) {
215 offset = (width - height) / 2;
216 src += offset;
217 num_rows = num_cols = height;
218 } else {
219 offset = (height - width) / 2;
220 src += width * offset;
221 num_rows = num_cols = width;
222 }
223
224 dest_col_step = (flip_vert ? -width : width);
225 dest_row_step = (flip_horiz ? 1 : -1);
226 if (flip_horiz) {
227 if (flip_vert) {
228 dest += (width > height ? width * (height - 1) + offset :
229 width * (height - offset - 1));
230 } else {
231 dest += (width > height ? offset : width * offset);
232 }
233 } else {
234 if (flip_vert) {
235 dest += (width > height ? width * height - offset - 1 :
236 width * (height - offset) - 1);
237 } else {
238 dest += (width > height ? width - offset - 1 :
239 width * (offset + 1) - 1);
240 }
241 }
242 } else {
243 NOTREACHED();
244 }
245
246 // Copy pixels.
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;
253 }
254 src += src_stride;
255 dest += dest_row_step;
256 }
257 }
258
ComputeLetterboxRegion(const gfx::Rect & bounds,const gfx::Size & content)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
262 // zero.
263 if (content.IsEmpty())
264 return gfx::Rect();
265
266 int64 x = static_cast<int64>(content.width()) * bounds.height();
267 int64 y = static_cast<int64>(content.height()) * bounds.width();
268
269 gfx::Size letterbox(bounds.width(), bounds.height());
270 if (y < x)
271 letterbox.set_height(static_cast<int>(y / content.width()));
272 else
273 letterbox.set_width(static_cast<int>(x / content.height()));
274 gfx::Rect result = bounds;
275 result.ClampToCenteredSize(letterbox);
276 return result;
277 }
278
CopyRGBToVideoFrame(const uint8 * source,int stride,const gfx::Rect & region_in_frame,VideoFrame * frame)279 void CopyRGBToVideoFrame(const uint8* source,
280 int stride,
281 const gfx::Rect& region_in_frame,
282 VideoFrame* 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);
288
289 if (region_in_frame != gfx::Rect(frame->coded_size())) {
290 LetterboxYUV(frame, region_in_frame);
291 }
292
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);
297
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(),
304 stride,
305 frame->stride(kY),
306 uv_stride);
307 }
308
309 } // namespace media
310