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
13namespace media {
14
15gfx::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
34void 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
53void CopyYPlane(const uint8* source, int stride, int rows, VideoFrame* frame) {
54  CopyPlane(VideoFrame::kYPlane, source, stride, rows, frame);
55}
56
57void CopyUPlane(const uint8* source, int stride, int rows, VideoFrame* frame) {
58  CopyPlane(VideoFrame::kUPlane, source, stride, rows, frame);
59}
60
61void CopyVPlane(const uint8* source, int stride, int rows, VideoFrame* frame) {
62  CopyPlane(VideoFrame::kVPlane, source, stride, rows, frame);
63}
64
65void CopyAPlane(const uint8* source, int stride, int rows, VideoFrame* frame) {
66  CopyPlane(VideoFrame::kAPlane, source, stride, rows, frame);
67}
68
69void 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
75void 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
99static 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
141void 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
158void 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
259gfx::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
279void 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