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::I420); 148 LetterboxPlane(frame, VideoFrame::kYPlane, view_area, 0x00); 149 gfx::Rect half_view_area(view_area.x() / 2, 150 view_area.y() / 2, 151 view_area.width() / 2, 152 view_area.height() / 2); 153 LetterboxPlane(frame, VideoFrame::kUPlane, half_view_area, 0x80); 154 LetterboxPlane(frame, VideoFrame::kVPlane, half_view_area, 0x80); 155 } 156 157 void RotatePlaneByPixels( 158 const uint8* src, 159 uint8* dest, 160 int width, 161 int height, 162 int rotation, // Clockwise. 163 bool flip_vert, 164 bool flip_horiz) { 165 DCHECK((width > 0) && (height > 0) && 166 ((width & 1) == 0) && ((height & 1) == 0) && 167 (rotation >= 0) && (rotation < 360) && (rotation % 90 == 0)); 168 169 // Consolidate cases. Only 0 and 90 are left. 170 if (rotation == 180 || rotation == 270) { 171 rotation -= 180; 172 flip_vert = !flip_vert; 173 flip_horiz = !flip_horiz; 174 } 175 176 int num_rows = height; 177 int num_cols = width; 178 int src_stride = width; 179 // During pixel copying, the corresponding incremental of dest pointer 180 // when src pointer moves to next row. 181 int dest_row_step = width; 182 // During pixel copying, the corresponding incremental of dest pointer 183 // when src pointer moves to next column. 184 int dest_col_step = 1; 185 186 if (rotation == 0) { 187 if (flip_horiz) { 188 // Use pixel copying. 189 dest_col_step = -1; 190 if (flip_vert) { 191 // Rotation 180. 192 dest_row_step = -width; 193 dest += height * width - 1; 194 } else { 195 dest += width - 1; 196 } 197 } else { 198 if (flip_vert) { 199 // Fast copy by rows. 200 dest += width * (height - 1); 201 for (int row = 0; row < height; ++row) { 202 memcpy(dest, src, width); 203 src += width; 204 dest -= width; 205 } 206 } else { 207 memcpy(dest, src, width * height); 208 } 209 return; 210 } 211 } else if (rotation == 90) { 212 int offset; 213 if (width > height) { 214 offset = (width - height) / 2; 215 src += offset; 216 num_rows = num_cols = height; 217 } else { 218 offset = (height - width) / 2; 219 src += width * offset; 220 num_rows = num_cols = width; 221 } 222 223 dest_col_step = (flip_vert ? -width : width); 224 dest_row_step = (flip_horiz ? 1 : -1); 225 if (flip_horiz) { 226 if (flip_vert) { 227 dest += (width > height ? width * (height - 1) + offset : 228 width * (height - offset - 1)); 229 } else { 230 dest += (width > height ? offset : width * offset); 231 } 232 } else { 233 if (flip_vert) { 234 dest += (width > height ? width * height - offset - 1 : 235 width * (height - offset) - 1); 236 } else { 237 dest += (width > height ? width - offset - 1 : 238 width * (offset + 1) - 1); 239 } 240 } 241 } else { 242 NOTREACHED(); 243 } 244 245 // Copy pixels. 246 for (int row = 0; row < num_rows; ++row) { 247 const uint8* src_ptr = src; 248 uint8* dest_ptr = dest; 249 for (int col = 0; col < num_cols; ++col) { 250 *dest_ptr = *src_ptr++; 251 dest_ptr += dest_col_step; 252 } 253 src += src_stride; 254 dest += dest_row_step; 255 } 256 } 257 258 gfx::Rect ComputeLetterboxRegion(const gfx::Rect& bounds, 259 const gfx::Size& content) { 260 // If |content| has an undefined aspect ratio, let's not try to divide by 261 // zero. 262 if (content.IsEmpty()) 263 return gfx::Rect(); 264 265 int64 x = static_cast<int64>(content.width()) * bounds.height(); 266 int64 y = static_cast<int64>(content.height()) * bounds.width(); 267 268 gfx::Size letterbox(bounds.width(), bounds.height()); 269 if (y < x) 270 letterbox.set_height(static_cast<int>(y / content.width())); 271 else 272 letterbox.set_width(static_cast<int>(x / content.height())); 273 gfx::Rect result = bounds; 274 result.ClampToCenteredSize(letterbox); 275 return result; 276 } 277 278 void CopyRGBToVideoFrame(const uint8* source, 279 int stride, 280 const gfx::Rect& region_in_frame, 281 VideoFrame* frame) { 282 const int kY = VideoFrame::kYPlane; 283 const int kU = VideoFrame::kUPlane; 284 const int kV = VideoFrame::kVPlane; 285 CHECK_EQ(frame->stride(kU), frame->stride(kV)); 286 const int uv_stride = frame->stride(kU); 287 288 if (region_in_frame != gfx::Rect(frame->coded_size())) { 289 LetterboxYUV(frame, region_in_frame); 290 } 291 292 const int y_offset = region_in_frame.x() 293 + (region_in_frame.y() * frame->stride(kY)); 294 const int uv_offset = region_in_frame.x() / 2 295 + (region_in_frame.y() / 2 * uv_stride); 296 297 ConvertRGB32ToYUV(source, 298 frame->data(kY) + y_offset, 299 frame->data(kU) + uv_offset, 300 frame->data(kV) + uv_offset, 301 region_in_frame.width(), 302 region_in_frame.height(), 303 stride, 304 frame->stride(kY), 305 uv_stride); 306 } 307 308 } // namespace media 309