Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / media / base / video_util.cc
blobcddbb9c3df24307237157d17979fbaccabc38baf
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"
7 #include <cmath>
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"
15 namespace media {
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)
23 return gfx::Size();
25 double aspect_ratio = aspect_ratio_numerator /
26 static_cast<double>(aspect_ratio_denominator);
28 return gfx::Size(round(visible_size.width() * aspect_ratio),
29 visible_size.height());
32 void CopyPlane(size_t plane, const uint8* source, int stride, int rows,
33 VideoFrame* frame) {
34 uint8* dest = frame->data(plane);
35 int dest_stride = frame->stride(plane);
37 // Clamp in case source frame has smaller stride.
38 int bytes_to_copy_per_row = std::min(frame->row_bytes(plane), stride);
40 // Clamp in case source frame has smaller height.
41 int rows_to_copy = std::min(frame->rows(plane), rows);
43 // Copy!
44 for (int row = 0; row < rows_to_copy; ++row) {
45 memcpy(dest, source, bytes_to_copy_per_row);
46 source += stride;
47 dest += dest_stride;
51 void CopyYPlane(const uint8* source, int stride, int rows, VideoFrame* frame) {
52 CopyPlane(VideoFrame::kYPlane, source, stride, rows, frame);
55 void CopyUPlane(const uint8* source, int stride, int rows, VideoFrame* frame) {
56 CopyPlane(VideoFrame::kUPlane, source, stride, rows, frame);
59 void CopyVPlane(const uint8* source, int stride, int rows, VideoFrame* frame) {
60 CopyPlane(VideoFrame::kVPlane, source, stride, rows, frame);
63 void CopyAPlane(const uint8* source, int stride, int rows, VideoFrame* frame) {
64 CopyPlane(VideoFrame::kAPlane, source, stride, rows, frame);
67 void MakeOpaqueAPlane(int stride, int rows, VideoFrame* frame) {
68 int rows_to_clear = std::min(frame->rows(VideoFrame::kAPlane), rows);
69 memset(frame->data(VideoFrame::kAPlane), 255,
70 frame->stride(VideoFrame::kAPlane) * rows_to_clear);
73 void FillYUV(VideoFrame* frame, uint8 y, uint8 u, uint8 v) {
74 // Fill the Y plane.
75 uint8* y_plane = frame->data(VideoFrame::kYPlane);
76 int y_rows = frame->rows(VideoFrame::kYPlane);
77 int y_row_bytes = frame->row_bytes(VideoFrame::kYPlane);
78 for (int i = 0; i < y_rows; ++i) {
79 memset(y_plane, y, y_row_bytes);
80 y_plane += frame->stride(VideoFrame::kYPlane);
83 // Fill the U and V planes.
84 uint8* u_plane = frame->data(VideoFrame::kUPlane);
85 uint8* v_plane = frame->data(VideoFrame::kVPlane);
86 int uv_rows = frame->rows(VideoFrame::kUPlane);
87 int u_row_bytes = frame->row_bytes(VideoFrame::kUPlane);
88 int v_row_bytes = frame->row_bytes(VideoFrame::kVPlane);
89 for (int i = 0; i < uv_rows; ++i) {
90 memset(u_plane, u, u_row_bytes);
91 memset(v_plane, v, v_row_bytes);
92 u_plane += frame->stride(VideoFrame::kUPlane);
93 v_plane += frame->stride(VideoFrame::kVPlane);
97 void FillYUVA(VideoFrame* frame, uint8 y, uint8 u, uint8 v, uint8 a) {
98 // Fill Y, U and V planes.
99 FillYUV(frame, y, u, v);
101 // Fill the A plane.
102 uint8* a_plane = frame->data(VideoFrame::kAPlane);
103 int a_rows = frame->rows(VideoFrame::kAPlane);
104 int a_row_bytes = frame->row_bytes(VideoFrame::kAPlane);
105 for (int i = 0; i < a_rows; ++i) {
106 memset(a_plane, a, a_row_bytes);
107 a_plane += frame->stride(VideoFrame::kAPlane);
111 static void LetterboxPlane(VideoFrame* frame,
112 int plane,
113 const gfx::Rect& view_area,
114 uint8 fill_byte) {
115 uint8* ptr = frame->data(plane);
116 const int rows = frame->rows(plane);
117 const int row_bytes = frame->row_bytes(plane);
118 const int stride = frame->stride(plane);
120 CHECK_GE(stride, row_bytes);
121 CHECK_GE(view_area.x(), 0);
122 CHECK_GE(view_area.y(), 0);
123 CHECK_LE(view_area.right(), row_bytes);
124 CHECK_LE(view_area.bottom(), rows);
126 int y = 0;
127 for (; y < view_area.y(); y++) {
128 memset(ptr, fill_byte, row_bytes);
129 ptr += stride;
131 if (view_area.width() < row_bytes) {
132 for (; y < view_area.bottom(); y++) {
133 if (view_area.x() > 0) {
134 memset(ptr, fill_byte, view_area.x());
136 if (view_area.right() < row_bytes) {
137 memset(ptr + view_area.right(),
138 fill_byte,
139 row_bytes - view_area.right());
141 ptr += stride;
143 } else {
144 y += view_area.height();
145 ptr += stride * view_area.height();
147 for (; y < rows; y++) {
148 memset(ptr, fill_byte, row_bytes);
149 ptr += stride;
153 void LetterboxYUV(VideoFrame* frame, const gfx::Rect& view_area) {
154 DCHECK(!(view_area.x() & 1));
155 DCHECK(!(view_area.y() & 1));
156 DCHECK(!(view_area.width() & 1));
157 DCHECK(!(view_area.height() & 1));
158 DCHECK(frame->format() == PIXEL_FORMAT_YV12 ||
159 frame->format() == PIXEL_FORMAT_I420);
160 LetterboxPlane(frame, VideoFrame::kYPlane, view_area, 0x00);
161 gfx::Rect half_view_area(view_area.x() / 2,
162 view_area.y() / 2,
163 view_area.width() / 2,
164 view_area.height() / 2);
165 LetterboxPlane(frame, VideoFrame::kUPlane, half_view_area, 0x80);
166 LetterboxPlane(frame, VideoFrame::kVPlane, half_view_area, 0x80);
169 void RotatePlaneByPixels(
170 const uint8* src,
171 uint8* dest,
172 int width,
173 int height,
174 int rotation, // Clockwise.
175 bool flip_vert,
176 bool flip_horiz) {
177 DCHECK((width > 0) && (height > 0) &&
178 ((width & 1) == 0) && ((height & 1) == 0) &&
179 (rotation >= 0) && (rotation < 360) && (rotation % 90 == 0));
181 // Consolidate cases. Only 0 and 90 are left.
182 if (rotation == 180 || rotation == 270) {
183 rotation -= 180;
184 flip_vert = !flip_vert;
185 flip_horiz = !flip_horiz;
188 int num_rows = height;
189 int num_cols = width;
190 int src_stride = width;
191 // During pixel copying, the corresponding incremental of dest pointer
192 // when src pointer moves to next row.
193 int dest_row_step = width;
194 // During pixel copying, the corresponding incremental of dest pointer
195 // when src pointer moves to next column.
196 int dest_col_step = 1;
198 if (rotation == 0) {
199 if (flip_horiz) {
200 // Use pixel copying.
201 dest_col_step = -1;
202 if (flip_vert) {
203 // Rotation 180.
204 dest_row_step = -width;
205 dest += height * width - 1;
206 } else {
207 dest += width - 1;
209 } else {
210 if (flip_vert) {
211 // Fast copy by rows.
212 dest += width * (height - 1);
213 for (int row = 0; row < height; ++row) {
214 memcpy(dest, src, width);
215 src += width;
216 dest -= width;
218 } else {
219 memcpy(dest, src, width * height);
221 return;
223 } else if (rotation == 90) {
224 int offset;
225 if (width > height) {
226 offset = (width - height) / 2;
227 src += offset;
228 num_rows = num_cols = height;
229 } else {
230 offset = (height - width) / 2;
231 src += width * offset;
232 num_rows = num_cols = width;
235 dest_col_step = (flip_vert ? -width : width);
236 dest_row_step = (flip_horiz ? 1 : -1);
237 if (flip_horiz) {
238 if (flip_vert) {
239 dest += (width > height ? width * (height - 1) + offset :
240 width * (height - offset - 1));
241 } else {
242 dest += (width > height ? offset : width * offset);
244 } else {
245 if (flip_vert) {
246 dest += (width > height ? width * height - offset - 1 :
247 width * (height - offset) - 1);
248 } else {
249 dest += (width > height ? width - offset - 1 :
250 width * (offset + 1) - 1);
253 } else {
254 NOTREACHED();
257 // Copy pixels.
258 for (int row = 0; row < num_rows; ++row) {
259 const uint8* src_ptr = src;
260 uint8* dest_ptr = dest;
261 for (int col = 0; col < num_cols; ++col) {
262 *dest_ptr = *src_ptr++;
263 dest_ptr += dest_col_step;
265 src += src_stride;
266 dest += dest_row_step;
270 // Helper function to return |a| divided by |b|, rounded to the nearest integer.
271 static int RoundedDivision(int64 a, int b) {
272 DCHECK_GE(a, 0);
273 DCHECK_GT(b, 0);
274 base::CheckedNumeric<uint64> result(a);
275 result += b / 2;
276 result /= b;
277 return base::checked_cast<int>(result.ValueOrDie());
280 // Common logic for the letterboxing and scale-within/scale-encompassing
281 // functions. Scales |size| to either fit within or encompass |target|,
282 // depending on whether |fit_within_target| is true.
283 static gfx::Size ScaleSizeToTarget(const gfx::Size& size,
284 const gfx::Size& target,
285 bool fit_within_target) {
286 if (size.IsEmpty())
287 return gfx::Size(); // Corner case: Aspect ratio is undefined.
289 const int64 x = static_cast<int64>(size.width()) * target.height();
290 const int64 y = static_cast<int64>(size.height()) * target.width();
291 const bool use_target_width = fit_within_target ? (y < x) : (x < y);
292 return use_target_width ?
293 gfx::Size(target.width(), RoundedDivision(y, size.width())) :
294 gfx::Size(RoundedDivision(x, size.height()), target.height());
297 gfx::Rect ComputeLetterboxRegion(const gfx::Rect& bounds,
298 const gfx::Size& content) {
299 // If |content| has an undefined aspect ratio, let's not try to divide by
300 // zero.
301 if (content.IsEmpty())
302 return gfx::Rect();
304 gfx::Rect result = bounds;
305 result.ClampToCenteredSize(ScaleSizeToTarget(content, bounds.size(), true));
306 return result;
309 gfx::Size ScaleSizeToFitWithinTarget(const gfx::Size& size,
310 const gfx::Size& target) {
311 return ScaleSizeToTarget(size, target, true);
314 gfx::Size ScaleSizeToEncompassTarget(const gfx::Size& size,
315 const gfx::Size& target) {
316 return ScaleSizeToTarget(size, target, false);
319 gfx::Size PadToMatchAspectRatio(const gfx::Size& size,
320 const gfx::Size& target) {
321 if (target.IsEmpty())
322 return gfx::Size(); // Aspect ratio is undefined.
324 const int64 x = static_cast<int64>(size.width()) * target.height();
325 const int64 y = static_cast<int64>(size.height()) * target.width();
326 if (x < y)
327 return gfx::Size(RoundedDivision(y, target.height()), size.height());
328 return gfx::Size(size.width(), RoundedDivision(x, target.width()));
331 void CopyRGBToVideoFrame(const uint8* source,
332 int stride,
333 const gfx::Rect& region_in_frame,
334 VideoFrame* frame) {
335 const int kY = VideoFrame::kYPlane;
336 const int kU = VideoFrame::kUPlane;
337 const int kV = VideoFrame::kVPlane;
338 CHECK_EQ(frame->stride(kU), frame->stride(kV));
339 const int uv_stride = frame->stride(kU);
341 if (region_in_frame != gfx::Rect(frame->coded_size())) {
342 LetterboxYUV(frame, region_in_frame);
345 const int y_offset = region_in_frame.x()
346 + (region_in_frame.y() * frame->stride(kY));
347 const int uv_offset = region_in_frame.x() / 2
348 + (region_in_frame.y() / 2 * uv_stride);
350 ConvertRGB32ToYUV(source,
351 frame->data(kY) + y_offset,
352 frame->data(kU) + uv_offset,
353 frame->data(kV) + uv_offset,
354 region_in_frame.width(),
355 region_in_frame.height(),
356 stride,
357 frame->stride(kY),
358 uv_stride);
361 } // namespace media