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
FillYUVA(VideoFrame * frame,uint8 y,uint8 u,uint8 v,uint8 a)99 void FillYUVA(VideoFrame* frame, uint8 y, uint8 u, uint8 v, uint8 a) {
100 // Fill Y, U and V planes.
101 FillYUV(frame, y, u, v);
102
103 // Fill the A plane.
104 uint8* a_plane = frame->data(VideoFrame::kAPlane);
105 int a_rows = frame->rows(VideoFrame::kAPlane);
106 int a_row_bytes = frame->row_bytes(VideoFrame::kAPlane);
107 for (int i = 0; i < a_rows; ++i) {
108 memset(a_plane, a, a_row_bytes);
109 a_plane += frame->stride(VideoFrame::kAPlane);
110 }
111 }
112
LetterboxPlane(VideoFrame * frame,int plane,const gfx::Rect & view_area,uint8 fill_byte)113 static void LetterboxPlane(VideoFrame* frame,
114 int plane,
115 const gfx::Rect& view_area,
116 uint8 fill_byte) {
117 uint8* ptr = frame->data(plane);
118 const int rows = frame->rows(plane);
119 const int row_bytes = frame->row_bytes(plane);
120 const int stride = frame->stride(plane);
121
122 CHECK_GE(stride, row_bytes);
123 CHECK_GE(view_area.x(), 0);
124 CHECK_GE(view_area.y(), 0);
125 CHECK_LE(view_area.right(), row_bytes);
126 CHECK_LE(view_area.bottom(), rows);
127
128 int y = 0;
129 for (; y < view_area.y(); y++) {
130 memset(ptr, fill_byte, row_bytes);
131 ptr += stride;
132 }
133 if (view_area.width() < row_bytes) {
134 for (; y < view_area.bottom(); y++) {
135 if (view_area.x() > 0) {
136 memset(ptr, fill_byte, view_area.x());
137 }
138 if (view_area.right() < row_bytes) {
139 memset(ptr + view_area.right(),
140 fill_byte,
141 row_bytes - view_area.right());
142 }
143 ptr += stride;
144 }
145 } else {
146 y += view_area.height();
147 ptr += stride * view_area.height();
148 }
149 for (; y < rows; y++) {
150 memset(ptr, fill_byte, row_bytes);
151 ptr += stride;
152 }
153 }
154
LetterboxYUV(VideoFrame * frame,const gfx::Rect & view_area)155 void LetterboxYUV(VideoFrame* frame, const gfx::Rect& view_area) {
156 DCHECK(!(view_area.x() & 1));
157 DCHECK(!(view_area.y() & 1));
158 DCHECK(!(view_area.width() & 1));
159 DCHECK(!(view_area.height() & 1));
160 DCHECK(frame->format() == VideoFrame::YV12 ||
161 frame->format() == VideoFrame::YV12J ||
162 frame->format() == VideoFrame::I420);
163 LetterboxPlane(frame, VideoFrame::kYPlane, view_area, 0x00);
164 gfx::Rect half_view_area(view_area.x() / 2,
165 view_area.y() / 2,
166 view_area.width() / 2,
167 view_area.height() / 2);
168 LetterboxPlane(frame, VideoFrame::kUPlane, half_view_area, 0x80);
169 LetterboxPlane(frame, VideoFrame::kVPlane, half_view_area, 0x80);
170 }
171
RotatePlaneByPixels(const uint8 * src,uint8 * dest,int width,int height,int rotation,bool flip_vert,bool flip_horiz)172 void RotatePlaneByPixels(
173 const uint8* src,
174 uint8* dest,
175 int width,
176 int height,
177 int rotation, // Clockwise.
178 bool flip_vert,
179 bool flip_horiz) {
180 DCHECK((width > 0) && (height > 0) &&
181 ((width & 1) == 0) && ((height & 1) == 0) &&
182 (rotation >= 0) && (rotation < 360) && (rotation % 90 == 0));
183
184 // Consolidate cases. Only 0 and 90 are left.
185 if (rotation == 180 || rotation == 270) {
186 rotation -= 180;
187 flip_vert = !flip_vert;
188 flip_horiz = !flip_horiz;
189 }
190
191 int num_rows = height;
192 int num_cols = width;
193 int src_stride = width;
194 // During pixel copying, the corresponding incremental of dest pointer
195 // when src pointer moves to next row.
196 int dest_row_step = width;
197 // During pixel copying, the corresponding incremental of dest pointer
198 // when src pointer moves to next column.
199 int dest_col_step = 1;
200
201 if (rotation == 0) {
202 if (flip_horiz) {
203 // Use pixel copying.
204 dest_col_step = -1;
205 if (flip_vert) {
206 // Rotation 180.
207 dest_row_step = -width;
208 dest += height * width - 1;
209 } else {
210 dest += width - 1;
211 }
212 } else {
213 if (flip_vert) {
214 // Fast copy by rows.
215 dest += width * (height - 1);
216 for (int row = 0; row < height; ++row) {
217 memcpy(dest, src, width);
218 src += width;
219 dest -= width;
220 }
221 } else {
222 memcpy(dest, src, width * height);
223 }
224 return;
225 }
226 } else if (rotation == 90) {
227 int offset;
228 if (width > height) {
229 offset = (width - height) / 2;
230 src += offset;
231 num_rows = num_cols = height;
232 } else {
233 offset = (height - width) / 2;
234 src += width * offset;
235 num_rows = num_cols = width;
236 }
237
238 dest_col_step = (flip_vert ? -width : width);
239 dest_row_step = (flip_horiz ? 1 : -1);
240 if (flip_horiz) {
241 if (flip_vert) {
242 dest += (width > height ? width * (height - 1) + offset :
243 width * (height - offset - 1));
244 } else {
245 dest += (width > height ? offset : width * offset);
246 }
247 } else {
248 if (flip_vert) {
249 dest += (width > height ? width * height - offset - 1 :
250 width * (height - offset) - 1);
251 } else {
252 dest += (width > height ? width - offset - 1 :
253 width * (offset + 1) - 1);
254 }
255 }
256 } else {
257 NOTREACHED();
258 }
259
260 // Copy pixels.
261 for (int row = 0; row < num_rows; ++row) {
262 const uint8* src_ptr = src;
263 uint8* dest_ptr = dest;
264 for (int col = 0; col < num_cols; ++col) {
265 *dest_ptr = *src_ptr++;
266 dest_ptr += dest_col_step;
267 }
268 src += src_stride;
269 dest += dest_row_step;
270 }
271 }
272
ComputeLetterboxRegion(const gfx::Rect & bounds,const gfx::Size & content)273 gfx::Rect ComputeLetterboxRegion(const gfx::Rect& bounds,
274 const gfx::Size& content) {
275 // If |content| has an undefined aspect ratio, let's not try to divide by
276 // zero.
277 if (content.IsEmpty())
278 return gfx::Rect();
279
280 int64 x = static_cast<int64>(content.width()) * bounds.height();
281 int64 y = static_cast<int64>(content.height()) * bounds.width();
282
283 gfx::Size letterbox(bounds.width(), bounds.height());
284 if (y < x)
285 letterbox.set_height(static_cast<int>(y / content.width()));
286 else
287 letterbox.set_width(static_cast<int>(x / content.height()));
288 gfx::Rect result = bounds;
289 result.ClampToCenteredSize(letterbox);
290 return result;
291 }
292
CopyRGBToVideoFrame(const uint8 * source,int stride,const gfx::Rect & region_in_frame,VideoFrame * frame)293 void CopyRGBToVideoFrame(const uint8* source,
294 int stride,
295 const gfx::Rect& region_in_frame,
296 VideoFrame* frame) {
297 const int kY = VideoFrame::kYPlane;
298 const int kU = VideoFrame::kUPlane;
299 const int kV = VideoFrame::kVPlane;
300 CHECK_EQ(frame->stride(kU), frame->stride(kV));
301 const int uv_stride = frame->stride(kU);
302
303 if (region_in_frame != gfx::Rect(frame->coded_size())) {
304 LetterboxYUV(frame, region_in_frame);
305 }
306
307 const int y_offset = region_in_frame.x()
308 + (region_in_frame.y() * frame->stride(kY));
309 const int uv_offset = region_in_frame.x() / 2
310 + (region_in_frame.y() / 2 * uv_stride);
311
312 ConvertRGB32ToYUV(source,
313 frame->data(kY) + y_offset,
314 frame->data(kU) + uv_offset,
315 frame->data(kV) + uv_offset,
316 region_in_frame.width(),
317 region_in_frame.height(),
318 stride,
319 frame->stride(kY),
320 uv_stride);
321 }
322
323 } // namespace media
324