summaryrefslogtreecommitdiffstats
path: root/remoting/base
diff options
context:
space:
mode:
authorhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-26 15:33:15 +0000
committerhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-26 15:33:15 +0000
commit47379e92dad68b0bedd344be841248a6498f79e8 (patch)
treea43af9bf9735f1a38fcfcdbbfff933f4e87f5178 /remoting/base
parent376d1e164bdfde1ca048d1dac77028b7b8af531e (diff)
downloadchromium_src-47379e92dad68b0bedd344be841248a6498f79e8.zip
chromium_src-47379e92dad68b0bedd344be841248a6498f79e8.tar.gz
chromium_src-47379e92dad68b0bedd344be841248a6498f79e8.tar.bz2
2. Implement scale-to-fit and clipping in DecoderVp8
Perform color space conversion and scaling in DecoderVp8 using fast SIMD routines. Also move clipping to DecoderVp8 to save more CPU cycles. BUG=None TEST=None Review URL: http://codereview.chromium.org/7396005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@94099 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/base')
-rw-r--r--remoting/base/decoder.h23
-rw-r--r--remoting/base/decoder_vp8.cc142
-rw-r--r--remoting/base/decoder_vp8.h28
-rw-r--r--remoting/base/encoder_vp8.cc13
-rw-r--r--remoting/base/util.cc71
-rw-r--r--remoting/base/util.h27
6 files changed, 254 insertions, 50 deletions
diff --git a/remoting/base/decoder.h b/remoting/base/decoder.h
index c82f242..a0a9244 100644
--- a/remoting/base/decoder.h
+++ b/remoting/base/decoder.h
@@ -37,9 +37,7 @@ class Decoder {
Decoder() {}
virtual ~Decoder() {}
- // Initializes the decoder to draw into the given |frame|. The |clip|
- // specifies the region to draw into. The clip region must fit inside
- // the dimensions of frame. Failure to do so will CHECK fail.
+ // Initializes the decoder to draw into the given |frame|.
virtual void Initialize(scoped_refptr<media::VideoFrame> frame) = 0;
// Feeds more data into the decoder.
@@ -59,6 +57,25 @@ class Decoder {
virtual bool IsReadyForData() = 0;
virtual VideoPacketFormat::Encoding Encoding() = 0;
+
+ // Set the scale factors of the decoded output. If the decoder doesn't support
+ // scaling then this all is ignored.
+ // If both |horizontal_ratio| and |vertical_ratio| equal 1.0 then scaling is
+ // turned off.
+ virtual void SetScaleRatios(double horizontal_ratio, double vertical_ratio) {}
+
+ // Set the clipping rectangle to the decoder. Decoder should respect this and
+ // only output changes in this rectangle. The new clipping rectangle will be
+ // effective on the next decoded video frame.
+ //
+ // When scaling is enabled clipping rectangles are ignored.
+ virtual void SetClipRect(const gfx::Rect& clip_rect) {}
+
+ // Force decoder to output a video frame with content in |rects| using the
+ // last decoded video frame.
+ //
+ // Coordinates of rectangles supplied here are before scaling.
+ virtual void RefreshRects(const std::vector<gfx::Rect>& rects) {}
};
} // namespace remoting
diff --git a/remoting/base/decoder_vp8.cc b/remoting/base/decoder_vp8.cc
index c0c798a..234fc07 100644
--- a/remoting/base/decoder_vp8.cc
+++ b/remoting/base/decoder_vp8.cc
@@ -17,7 +17,10 @@ namespace remoting {
DecoderVp8::DecoderVp8()
: state_(kUninitialized),
- codec_(NULL) {
+ codec_(NULL),
+ last_image_(NULL),
+ horizontal_scale_ratio_(1.0),
+ vertical_scale_ratio_(1.0) {
}
DecoderVp8::~DecoderVp8() {
@@ -84,33 +87,21 @@ Decoder::DecodeResult DecoderVp8::DecodePacket(const VideoPacket* packet) {
LOG(INFO) << "No video frame decoded";
return DECODE_ERROR;
}
+ last_image_ = image;
- // Perform YUV conversion.
- uint8* data_start = frame_->data(media::VideoFrame::kRGBPlane);
- int stride = frame_->stride(media::VideoFrame::kRGBPlane);
-
- // Propogate updated rects.
- updated_rects_.clear();
+ std::vector<gfx::Rect> rects;
for (int i = 0; i < packet->dirty_rects_size(); ++i) {
gfx::Rect r = gfx::Rect(packet->dirty_rects(i).x(),
packet->dirty_rects(i).y(),
packet->dirty_rects(i).width(),
packet->dirty_rects(i).height());
-
- // Perform color space conversion only on the updated rectangle.
- ConvertYUVToRGB32WithRect(image->planes[0],
- image->planes[1],
- image->planes[2],
- data_start,
- r.x(),
- r.y(),
- r.width(),
- r.height(),
- image->stride[0],
- image->stride[1],
- stride);
- updated_rects_.push_back(r);
+ rects.push_back(r);
}
+
+ if (!DoScaling())
+ ConvertRects(rects, &updated_rects_);
+ else
+ ScaleAndConvertRects(rects, &updated_rects_);
return DECODE_DONE;
}
@@ -131,4 +122,113 @@ VideoPacketFormat::Encoding DecoderVp8::Encoding() {
return VideoPacketFormat::ENCODING_VP8;
}
+void DecoderVp8::SetScaleRatios(double horizontal_ratio,
+ double vertical_ratio) {
+ // TODO(hclam): Ratio greater than 1.0 is not supported. This is
+ // because we need to reallocate the backing video frame and this
+ // is not implemented yet.
+ if (horizontal_ratio > 1.0 || horizontal_ratio <= 0.0 ||
+ vertical_ratio > 1.0 || vertical_ratio <= 0.0) {
+ return;
+ }
+
+ horizontal_scale_ratio_ = horizontal_ratio;
+ vertical_scale_ratio_ = vertical_ratio;
+}
+
+void DecoderVp8::SetClipRect(const gfx::Rect& clip_rect) {
+ clip_rect_ = clip_rect;
+}
+
+void DecoderVp8::RefreshRects(const std::vector<gfx::Rect>& rects) {
+ if (!DoScaling())
+ ConvertRects(rects, &updated_rects_);
+ else
+ ScaleAndConvertRects(rects, &updated_rects_);
+}
+
+bool DecoderVp8::DoScaling() const {
+ return horizontal_scale_ratio_ != 1.0 || vertical_scale_ratio_ != 1.0;
+}
+
+void DecoderVp8::ConvertRects(const UpdatedRects& rects,
+ UpdatedRects* output_rects) {
+ if (!last_image_)
+ return;
+
+ uint8* data_start = frame_->data(media::VideoFrame::kRGBPlane);
+ const int stride = frame_->stride(media::VideoFrame::kRGBPlane);
+
+ output_rects->clear();
+ for (size_t i = 0; i < rects.size(); ++i) {
+ // Round down the image width and height.
+ int image_width = RoundToTwosMultiple(last_image_->d_w);
+ int image_height = RoundToTwosMultiple(last_image_->d_h);
+
+ // Clip by the clipping rectangle first.
+ gfx::Rect dest_rect = rects[i].Intersect(clip_rect_);
+
+ // Then clip by the rounded down dimension of the image for safety.
+ dest_rect = dest_rect.Intersect(
+ gfx::Rect(0, 0, image_width, image_height));
+
+ // Align the rectangle to avoid artifacts in color space conversion.
+ dest_rect = AlignRect(dest_rect);
+
+ if (dest_rect.IsEmpty())
+ continue;
+
+ ConvertYUVToRGB32WithRect(last_image_->planes[0],
+ last_image_->planes[1],
+ last_image_->planes[2],
+ data_start,
+ dest_rect,
+ last_image_->stride[0],
+ last_image_->stride[1],
+ stride);
+ output_rects->push_back(dest_rect);
+ }
+}
+
+void DecoderVp8::ScaleAndConvertRects(const UpdatedRects& rects,
+ UpdatedRects* output_rects) {
+ if (!last_image_)
+ return;
+
+ uint8* data_start = frame_->data(media::VideoFrame::kRGBPlane);
+ const int stride = frame_->stride(media::VideoFrame::kRGBPlane);
+
+ output_rects->clear();
+ for (size_t i = 0; i < rects.size(); ++i) {
+ // Round down the image width and height.
+ int image_width = RoundToTwosMultiple(last_image_->d_w);
+ int image_height = RoundToTwosMultiple(last_image_->d_h);
+
+ // Clip by the rounded down dimension of the image for safety.
+ gfx::Rect dest_rect =
+ rects[i].Intersect(gfx::Rect(0, 0, image_width, image_height));
+
+ // Align the rectangle to avoid artifacts in color space conversion.
+ dest_rect = AlignRect(dest_rect);
+
+ if (dest_rect.IsEmpty())
+ continue;
+
+ gfx::Rect scaled_rect = ScaleRect(dest_rect,
+ horizontal_scale_ratio_,
+ vertical_scale_ratio_);
+
+ ScaleYUVToRGB32WithRect(last_image_->planes[0],
+ last_image_->planes[1],
+ last_image_->planes[2],
+ data_start,
+ dest_rect,
+ scaled_rect,
+ last_image_->stride[0],
+ last_image_->stride[1],
+ stride);
+ output_rects->push_back(scaled_rect);
+ }
+}
+
} // namespace remoting
diff --git a/remoting/base/decoder_vp8.h b/remoting/base/decoder_vp8.h
index 08f60e2..7aab0fb 100644
--- a/remoting/base/decoder_vp8.h
+++ b/remoting/base/decoder_vp8.h
@@ -8,6 +8,7 @@
#include "remoting/base/decoder.h"
typedef struct vpx_codec_ctx vpx_codec_ctx_t;
+typedef struct vpx_image vpx_image_t;
namespace remoting {
@@ -23,6 +24,9 @@ class DecoderVp8 : public Decoder {
virtual bool IsReadyForData();
virtual void Reset();
virtual VideoPacketFormat::Encoding Encoding();
+ virtual void SetScaleRatios(double horizontal_ratio, double vertical_ratio);
+ virtual void SetClipRect(const gfx::Rect& clip_rect);
+ virtual void RefreshRects(const std::vector<gfx::Rect>& rects);
private:
enum State {
@@ -31,6 +35,20 @@ class DecoderVp8 : public Decoder {
kError,
};
+ // Return true if scaling is enabled
+ bool DoScaling() const;
+
+ // Perform color space conversion on the specified rectangles.
+ // Write the updated rectangles to |output_rects|.
+ void ConvertRects(const UpdatedRects& rects,
+ UpdatedRects* output_rects);
+
+ // Perform scaling and color space conversion on the specified
+ // rectangles.
+ // Write the updated rectangles to |output_rects|.
+ void ScaleAndConvertRects(const UpdatedRects& rects,
+ UpdatedRects* output_rects);
+
// The internal state of the decoder.
State state_;
@@ -39,9 +57,19 @@ class DecoderVp8 : public Decoder {
vpx_codec_ctx_t* codec_;
+ // Pointer to the last decoded image.
+ vpx_image_t* last_image_;
+
// Record the updated rects in the last decode.
UpdatedRects updated_rects_;
+ // Clipping rect for the output of the decoder.
+ gfx::Rect clip_rect_;
+
+ // Scale factors of the decoded output.
+ double horizontal_scale_ratio_;
+ double vertical_scale_ratio_;
+
DISALLOW_COPY_AND_ASSIGN(DecoderVp8);
};
diff --git a/remoting/base/encoder_vp8.cc b/remoting/base/encoder_vp8.cc
index aa2a3d7..447e5d5 100644
--- a/remoting/base/encoder_vp8.cc
+++ b/remoting/base/encoder_vp8.cc
@@ -133,19 +133,6 @@ bool EncoderVp8::Init(const gfx::Size& size) {
return true;
}
-static int RoundToTwosMultiple(int x) {
- return x & (~1);
-}
-
-// Align the sides of the rectangle to multiples of 2 (expanding outwards).
-static gfx::Rect AlignRect(const gfx::Rect& rect) {
- int x = RoundToTwosMultiple(rect.x());
- int y = RoundToTwosMultiple(rect.y());
- int right = RoundToTwosMultiple(rect.right() + 1);
- int bottom = RoundToTwosMultiple(rect.bottom() + 1);
- return gfx::Rect(x, y, right - x, bottom - y);
-}
-
// static
gfx::Rect EncoderVp8::AlignAndClipRect(const gfx::Rect& rect,
int width, int height) {
diff --git a/remoting/base/util.cc b/remoting/base/util.cc
index 9341050..788c602 100644
--- a/remoting/base/util.cc
+++ b/remoting/base/util.cc
@@ -44,29 +44,56 @@ void ConvertYUVToRGB32WithRect(const uint8* y_plane,
const uint8* u_plane,
const uint8* v_plane,
uint8* rgb_plane,
- int x,
- int y,
- int width,
- int height,
+ const gfx::Rect& rect,
int y_stride,
int uv_stride,
int rgb_stride) {
- int rgb_offset = CalculateRGBOffset(x, y, rgb_stride);
- int y_offset = CalculateYOffset(x, y, y_stride);
- int uv_offset = CalculateUVOffset(x, y, uv_stride);;
+ int rgb_offset = CalculateRGBOffset(rect.x(), rect.y(), rgb_stride);
+ int y_offset = CalculateYOffset(rect.x(), rect.y(), y_stride);
+ int uv_offset = CalculateUVOffset(rect.x(), rect.y(), uv_stride);
media::ConvertYUVToRGB32(y_plane + y_offset,
u_plane + uv_offset,
v_plane + uv_offset,
rgb_plane + rgb_offset,
- width,
- height,
+ rect.width(),
+ rect.height(),
y_stride,
uv_stride,
rgb_stride,
media::YV12);
}
+void ScaleYUVToRGB32WithRect(const uint8* y_plane,
+ const uint8* u_plane,
+ const uint8* v_plane,
+ uint8* rgb_plane,
+ const gfx::Rect& source_rect,
+ const gfx::Rect& dest_rect,
+ int y_stride,
+ int uv_stride,
+ int rgb_stride) {
+ int rgb_offset = CalculateRGBOffset(dest_rect.x(), dest_rect.y(), rgb_stride);
+ int y_offset = CalculateYOffset(source_rect.x(), source_rect.y(), y_stride);
+ int uv_offset = CalculateUVOffset(source_rect.x(),
+ source_rect.y(), uv_stride);
+
+ media::ScaleYUVToRGB32(y_plane + y_offset,
+ u_plane + uv_offset,
+ v_plane + uv_offset,
+ rgb_plane + rgb_offset,
+ source_rect.width(),
+ source_rect.height(),
+ dest_rect.width(),
+ dest_rect.height(),
+ y_stride,
+ uv_stride,
+ rgb_stride,
+ media::YV12,
+ media::ROTATE_0,
+ media::FILTER_NONE);
+}
+
void ConvertRGB32ToYUVWithRect(const uint8* rgb_plane,
uint8* y_plane,
uint8* u_plane,
@@ -93,4 +120,30 @@ void ConvertRGB32ToYUVWithRect(const uint8* rgb_plane,
uv_stride);
}
+int RoundToTwosMultiple(int x) {
+ return x & (~1);
+}
+
+gfx::Rect AlignRect(const gfx::Rect& rect) {
+ int x = RoundToTwosMultiple(rect.x());
+ int y = RoundToTwosMultiple(rect.y());
+ int right = RoundToTwosMultiple(rect.right() + 1);
+ int bottom = RoundToTwosMultiple(rect.bottom() + 1);
+ return gfx::Rect(x, y, right - x, bottom - y);
+}
+
+gfx::Rect ScaleRect(const gfx::Rect& rect,
+ double horizontal_ratio,
+ double vertical_ratio) {
+ gfx::Rect scaled_rect(rect.x() * horizontal_ratio,
+ rect.y() * vertical_ratio,
+ 0,
+ 0);
+ scaled_rect.set_width(
+ rect.right() * horizontal_ratio - scaled_rect.x());
+ scaled_rect.set_height(
+ rect.bottom() * vertical_ratio - scaled_rect.y());
+ return scaled_rect;
+}
+
} // namespace remoting
diff --git a/remoting/base/util.h b/remoting/base/util.h
index 51a8d10..920bb89 100644
--- a/remoting/base/util.h
+++ b/remoting/base/util.h
@@ -6,6 +6,7 @@
#define REMOTING_BASE_UTIL_H_
#include "media/base/video_frame.h"
+#include "ui/gfx/rect.h"
namespace remoting {
@@ -17,14 +18,21 @@ void ConvertYUVToRGB32WithRect(const uint8* y_plane,
const uint8* u_plane,
const uint8* v_plane,
uint8* rgb_plane,
- int x,
- int y,
- int width,
- int height,
+ const gfx::Rect& rect,
int y_stride,
int uv_stride,
int rgb_stride);
+void ScaleYUVToRGB32WithRect(const uint8* y_plane,
+ const uint8* u_plane,
+ const uint8* v_plane,
+ uint8* rgb_plane,
+ const gfx::Rect& source_rect,
+ const gfx::Rect& dest_rect,
+ int y_stride,
+ int uv_stride,
+ int rgb_stride);
+
void ConvertRGB32ToYUVWithRect(const uint8* rgb_plane,
uint8* y_plane,
uint8* u_plane,
@@ -37,6 +45,17 @@ void ConvertRGB32ToYUVWithRect(const uint8* rgb_plane,
int y_stride,
int uv_stride);
+int RoundToTwosMultiple(int x);
+
+// Align the sides of the rectangle to multiples of 2 (expanding outwards).
+gfx::Rect AlignRect(const gfx::Rect& rect);
+
+// Return a scaled rectangle using the horizontal and vertical scale
+// factors.
+gfx::Rect ScaleRect(const gfx::Rect& rect,
+ double horizontal_ratio,
+ double vertical_ratio);
+
} // namespace remoting
#endif // REMOTING_BASE_UTIL_H_