Home | History | Annotate | Download | only in base
      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