• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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