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 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 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 53 void CopyYPlane(const uint8* source, int stride, int rows, VideoFrame* frame) { 54 CopyPlane(VideoFrame::kYPlane, source, stride, rows, frame); 55 } 56 57 void CopyUPlane(const uint8* source, int stride, int rows, VideoFrame* frame) { 58 CopyPlane(VideoFrame::kUPlane, source, stride, rows, frame); 59 } 60 61 void CopyVPlane(const uint8* source, int stride, int rows, VideoFrame* frame) { 62 CopyPlane(VideoFrame::kVPlane, source, stride, rows, frame); 63 } 64 65 void CopyAPlane(const uint8* source, int stride, int rows, VideoFrame* frame) { 66 CopyPlane(VideoFrame::kAPlane, source, stride, rows, frame); 67 } 68 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 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 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 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 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 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 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 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