diff options
author | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-26 15:33:15 +0000 |
---|---|---|
committer | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-26 15:33:15 +0000 |
commit | 47379e92dad68b0bedd344be841248a6498f79e8 (patch) | |
tree | a43af9bf9735f1a38fcfcdbbfff933f4e87f5178 /remoting/base | |
parent | 376d1e164bdfde1ca048d1dac77028b7b8af531e (diff) | |
download | chromium_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.h | 23 | ||||
-rw-r--r-- | remoting/base/decoder_vp8.cc | 142 | ||||
-rw-r--r-- | remoting/base/decoder_vp8.h | 28 | ||||
-rw-r--r-- | remoting/base/encoder_vp8.cc | 13 | ||||
-rw-r--r-- | remoting/base/util.cc | 71 | ||||
-rw-r--r-- | remoting/base/util.h | 27 |
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_ |