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