普通文本  |  310行  |  9.57 KB

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/base/video_util.h"

#include <cmath>

#include "base/logging.h"
#include "media/base/video_frame.h"
#include "media/base/yuv_convert.h"

namespace media {

gfx::Size GetNaturalSize(const gfx::Size& visible_size,
                         int aspect_ratio_numerator,
                         int aspect_ratio_denominator) {
  if (aspect_ratio_denominator == 0 ||
      aspect_ratio_numerator < 0 ||
      aspect_ratio_denominator < 0)
    return gfx::Size();

  double aspect_ratio = aspect_ratio_numerator /
      static_cast<double>(aspect_ratio_denominator);

  int width = floor(visible_size.width() * aspect_ratio + 0.5);
  int height = visible_size.height();

  // An even width makes things easier for YV12 and appears to be the behavior
  // expected by WebKit layout tests.
  return gfx::Size(width & ~1, height);
}

void CopyPlane(size_t plane, const uint8* source, int stride, int rows,
               VideoFrame* frame) {
  uint8* dest = frame->data(plane);
  int dest_stride = frame->stride(plane);

  // Clamp in case source frame has smaller stride.
  int bytes_to_copy_per_row = std::min(frame->row_bytes(plane), stride);

  // Clamp in case source frame has smaller height.
  int rows_to_copy = std::min(frame->rows(plane), rows);

  // Copy!
  for (int row = 0; row < rows_to_copy; ++row) {
    memcpy(dest, source, bytes_to_copy_per_row);
    source += stride;
    dest += dest_stride;
  }
}

void CopyYPlane(const uint8* source, int stride, int rows, VideoFrame* frame) {
  CopyPlane(VideoFrame::kYPlane, source, stride, rows, frame);
}

void CopyUPlane(const uint8* source, int stride, int rows, VideoFrame* frame) {
  CopyPlane(VideoFrame::kUPlane, source, stride, rows, frame);
}

void CopyVPlane(const uint8* source, int stride, int rows, VideoFrame* frame) {
  CopyPlane(VideoFrame::kVPlane, source, stride, rows, frame);
}

void CopyAPlane(const uint8* source, int stride, int rows, VideoFrame* frame) {
  CopyPlane(VideoFrame::kAPlane, source, stride, rows, frame);
}

void MakeOpaqueAPlane(int stride, int rows, VideoFrame* frame) {
  int rows_to_clear = std::min(frame->rows(VideoFrame::kAPlane), rows);
  memset(frame->data(VideoFrame::kAPlane), 255,
         frame->stride(VideoFrame::kAPlane) * rows_to_clear);
}

void FillYUV(VideoFrame* frame, uint8 y, uint8 u, uint8 v) {
  // Fill the Y plane.
  uint8* y_plane = frame->data(VideoFrame::kYPlane);
  int y_rows = frame->rows(VideoFrame::kYPlane);
  int y_row_bytes = frame->row_bytes(VideoFrame::kYPlane);
  for (int i = 0; i < y_rows; ++i) {
    memset(y_plane, y, y_row_bytes);
    y_plane += frame->stride(VideoFrame::kYPlane);
  }

  // Fill the U and V planes.
  uint8* u_plane = frame->data(VideoFrame::kUPlane);
  uint8* v_plane = frame->data(VideoFrame::kVPlane);
  int uv_rows = frame->rows(VideoFrame::kUPlane);
  int u_row_bytes = frame->row_bytes(VideoFrame::kUPlane);
  int v_row_bytes = frame->row_bytes(VideoFrame::kVPlane);
  for (int i = 0; i < uv_rows; ++i) {
    memset(u_plane, u, u_row_bytes);
    memset(v_plane, v, v_row_bytes);
    u_plane += frame->stride(VideoFrame::kUPlane);
    v_plane += frame->stride(VideoFrame::kVPlane);
  }
}

static void LetterboxPlane(VideoFrame* frame,
                           int plane,
                           const gfx::Rect& view_area,
                           uint8 fill_byte) {
  uint8* ptr = frame->data(plane);
  const int rows = frame->rows(plane);
  const int row_bytes = frame->row_bytes(plane);
  const int stride = frame->stride(plane);

  CHECK_GE(stride, row_bytes);
  CHECK_GE(view_area.x(), 0);
  CHECK_GE(view_area.y(), 0);
  CHECK_LE(view_area.right(), row_bytes);
  CHECK_LE(view_area.bottom(), rows);

  int y = 0;
  for (; y < view_area.y(); y++) {
    memset(ptr, fill_byte, row_bytes);
    ptr += stride;
  }
  if (view_area.width() < row_bytes) {
    for (; y < view_area.bottom(); y++) {
      if (view_area.x() > 0) {
        memset(ptr, fill_byte, view_area.x());
      }
      if (view_area.right() < row_bytes) {
        memset(ptr + view_area.right(),
               fill_byte,
               row_bytes - view_area.right());
      }
      ptr += stride;
    }
  } else {
    y += view_area.height();
    ptr += stride * view_area.height();
  }
  for (; y < rows; y++) {
    memset(ptr, fill_byte, row_bytes);
    ptr += stride;
  }
}

void LetterboxYUV(VideoFrame* frame, const gfx::Rect& view_area) {
  DCHECK(!(view_area.x() & 1));
  DCHECK(!(view_area.y() & 1));
  DCHECK(!(view_area.width() & 1));
  DCHECK(!(view_area.height() & 1));
  DCHECK(frame->format() == VideoFrame::YV12 ||
         frame->format() == VideoFrame::YV12J ||
         frame->format() == VideoFrame::I420);
  LetterboxPlane(frame, VideoFrame::kYPlane, view_area, 0x00);
  gfx::Rect half_view_area(view_area.x() / 2,
                           view_area.y() / 2,
                           view_area.width() / 2,
                           view_area.height() / 2);
  LetterboxPlane(frame, VideoFrame::kUPlane, half_view_area, 0x80);
  LetterboxPlane(frame, VideoFrame::kVPlane, half_view_area, 0x80);
}

void RotatePlaneByPixels(
    const uint8* src,
    uint8* dest,
    int width,
    int height,
    int rotation,  // Clockwise.
    bool flip_vert,
    bool flip_horiz) {
  DCHECK((width > 0) && (height > 0) &&
         ((width & 1) == 0) && ((height & 1) == 0) &&
         (rotation >= 0) && (rotation < 360) && (rotation % 90 == 0));

  // Consolidate cases. Only 0 and 90 are left.
  if (rotation == 180 || rotation == 270) {
    rotation -= 180;
    flip_vert = !flip_vert;
    flip_horiz = !flip_horiz;
  }

  int num_rows = height;
  int num_cols = width;
  int src_stride = width;
  // During pixel copying, the corresponding incremental of dest pointer
  // when src pointer moves to next row.
  int dest_row_step = width;
  // During pixel copying, the corresponding incremental of dest pointer
  // when src pointer moves to next column.
  int dest_col_step = 1;

  if (rotation == 0) {
    if (flip_horiz) {
      // Use pixel copying.
      dest_col_step = -1;
      if (flip_vert) {
        // Rotation 180.
        dest_row_step = -width;
        dest += height * width - 1;
      } else {
        dest += width - 1;
      }
    } else {
      if (flip_vert) {
        // Fast copy by rows.
        dest += width * (height - 1);
        for (int row = 0; row < height; ++row) {
          memcpy(dest, src, width);
          src += width;
          dest -= width;
        }
      } else {
        memcpy(dest, src, width * height);
      }
      return;
    }
  } else if (rotation == 90) {
    int offset;
    if (width > height) {
      offset = (width - height) / 2;
      src += offset;
      num_rows = num_cols = height;
    } else {
      offset = (height - width) / 2;
      src += width * offset;
      num_rows = num_cols = width;
    }

    dest_col_step = (flip_vert ? -width : width);
    dest_row_step = (flip_horiz ? 1 : -1);
    if (flip_horiz) {
      if (flip_vert) {
        dest += (width > height ? width * (height - 1) + offset :
                                  width * (height - offset - 1));
      } else {
        dest += (width > height ? offset : width * offset);
      }
    } else {
      if (flip_vert) {
        dest += (width > height ?  width * height - offset - 1 :
                                   width * (height - offset) - 1);
      } else {
        dest += (width > height ? width - offset - 1 :
                                  width * (offset + 1) - 1);
      }
    }
  } else {
    NOTREACHED();
  }

  // Copy pixels.
  for (int row = 0; row < num_rows; ++row) {
    const uint8* src_ptr = src;
    uint8* dest_ptr = dest;
    for (int col = 0; col < num_cols; ++col) {
      *dest_ptr = *src_ptr++;
      dest_ptr += dest_col_step;
    }
    src += src_stride;
    dest += dest_row_step;
  }
}

gfx::Rect ComputeLetterboxRegion(const gfx::Rect& bounds,
                                 const gfx::Size& content) {
  // If |content| has an undefined aspect ratio, let's not try to divide by
  // zero.
  if (content.IsEmpty())
    return gfx::Rect();

  int64 x = static_cast<int64>(content.width()) * bounds.height();
  int64 y = static_cast<int64>(content.height()) * bounds.width();

  gfx::Size letterbox(bounds.width(), bounds.height());
  if (y < x)
    letterbox.set_height(static_cast<int>(y / content.width()));
  else
    letterbox.set_width(static_cast<int>(x / content.height()));
  gfx::Rect result = bounds;
  result.ClampToCenteredSize(letterbox);
  return result;
}

void CopyRGBToVideoFrame(const uint8* source,
                         int stride,
                         const gfx::Rect& region_in_frame,
                         VideoFrame* frame) {
  const int kY = VideoFrame::kYPlane;
  const int kU = VideoFrame::kUPlane;
  const int kV = VideoFrame::kVPlane;
  CHECK_EQ(frame->stride(kU), frame->stride(kV));
  const int uv_stride = frame->stride(kU);

  if (region_in_frame != gfx::Rect(frame->coded_size())) {
    LetterboxYUV(frame, region_in_frame);
  }

  const int y_offset = region_in_frame.x()
                     + (region_in_frame.y() * frame->stride(kY));
  const int uv_offset = region_in_frame.x() / 2
                      + (region_in_frame.y() / 2 * uv_stride);

  ConvertRGB32ToYUV(source,
                    frame->data(kY) + y_offset,
                    frame->data(kU) + uv_offset,
                    frame->data(kV) + uv_offset,
                    region_in_frame.width(),
                    region_in_frame.height(),
                    stride,
                    frame->stride(kY),
                    uv_stride);
}

}  // namespace media