• 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 
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