summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--media/base/yuv_convert.cc5
-rw-r--r--remoting/base/decoder_vp8.cc102
-rw-r--r--remoting/base/decoder_vp8.h13
-rw-r--r--remoting/base/util.cc175
-rw-r--r--remoting/base/util.h43
-rw-r--r--remoting/base/util_unittest.cc187
-rw-r--r--remoting/remoting.gyp1
7 files changed, 399 insertions, 127 deletions
diff --git a/media/base/yuv_convert.cc b/media/base/yuv_convert.cc
index 8d5073b..ed9bed4 100644
--- a/media/base/yuv_convert.cc
+++ b/media/base/yuv_convert.cc
@@ -335,8 +335,9 @@ void ScaleYUVToRGB32WithRect(const uint8* y_buf,
// Determine the parts of the Y, U and V buffers to interpolate.
int source_y_left = source_left >> kFractionBits;
- int source_y_right = (source_right >> kFractionBits) + 2;
- DCHECK(source_y_right <= source_width);
+ int source_y_right = std::min(
+ (source_right >> kFractionBits) + 2,
+ source_width + 1);
int source_uv_left = source_y_left / 2;
int source_uv_right = std::min(
diff --git a/remoting/base/decoder_vp8.cc b/remoting/base/decoder_vp8.cc
index 40746913..fc6670f 100644
--- a/remoting/base/decoder_vp8.cc
+++ b/remoting/base/decoder_vp8.cc
@@ -142,71 +142,17 @@ void DecoderVp8::RefreshRegion(const SkRegion& region) {
if (output_size_.height() > static_cast<int>(frame_->height()))
output_size_.set(output_size_.width(), frame_->height());
- if (!DoScaling()) {
- ConvertRegion(region, &updated_region_);
- } else {
- ScaleAndConvertRegion(region, &updated_region_);
- }
-}
-
-bool DecoderVp8::DoScaling() const {
- DCHECK(last_image_);
- return !output_size_.equals(last_image_->d_w, last_image_->d_h);
-}
-
-void DecoderVp8::ConvertRegion(const SkRegion& input_region,
- SkRegion* output_region) {
if (!last_image_)
return;
- output_region->setEmpty();
+ updated_region_.setEmpty();
// Clip based on both the output dimensions and Pepper clip rect.
- // ConvertYUVToRGB32WithRect() requires even X and Y coordinates, so we align
- // |clip_rect| to prevent clipping from breaking alignment. We then clamp it
- // to the image dimensions, which may lead to odd width & height, which we
- // can cope with.
+ // ConvertAndScaleYUVToRGB32Rect() requires even X and Y coordinates, so we
+ // align |clip_rect| to prevent clipping from breaking alignment. We then
+ // clamp it to the image dimensions, which may lead to odd width & height,
+ // which we can cope with.
SkIRect clip_rect = AlignRect(clip_rect_);
- if (!clip_rect.intersect(SkIRect::MakeWH(last_image_->d_w, last_image_->d_h)))
- return;
-
- uint8* output_rgb_buf = frame_->data(media::VideoFrame::kRGBPlane);
- const int output_stride = frame_->stride(media::VideoFrame::kRGBPlane);
-
- for (SkRegion::Iterator i(input_region); !i.done(); i.next()) {
- // Align the rectangle so the top-left coordinates are even, for
- // ConvertYUVToRGB32WithRect().
- SkIRect dest_rect(AlignRect(i.rect()));
-
- // Clip the rectangle, preserving alignment since |clip_rect| is aligned.
- if (!dest_rect.intersect(clip_rect))
- continue;
-
- ConvertYUVToRGB32WithRect(last_image_->planes[0],
- last_image_->planes[1],
- last_image_->planes[2],
- output_rgb_buf,
- dest_rect,
- last_image_->stride[0],
- last_image_->stride[1],
- output_stride);
-
- output_region->op(dest_rect, SkRegion::kUnion_Op);
- }
-}
-
-void DecoderVp8::ScaleAndConvertRegion(const SkRegion& input_region,
- SkRegion* output_region) {
- if (!last_image_)
- return;
-
- DCHECK(output_size_.width() <= static_cast<int>(frame_->width()));
- DCHECK(output_size_.height() <= static_cast<int>(frame_->height()));
-
- output_region->setEmpty();
-
- // Clip based on both the output dimensions and Pepper clip rect.
- SkIRect clip_rect = clip_rect_;
if (!clip_rect.intersect(SkIRect::MakeSize(output_size_)))
return;
@@ -214,30 +160,30 @@ void DecoderVp8::ScaleAndConvertRegion(const SkRegion& input_region,
uint8* output_rgb_buf = frame_->data(media::VideoFrame::kRGBPlane);
const int output_stride = frame_->stride(media::VideoFrame::kRGBPlane);
- for (SkRegion::Iterator i(input_region); !i.done(); i.next()) {
+ for (SkRegion::Iterator i(region); !i.done(); i.next()) {
// Determine the scaled area affected by this rectangle changing.
- SkIRect output_rect = ScaleRect(i.rect(), image_size, output_size_);
+ // Align the rectangle so the top-left coordinates are even, for
+ // ConvertAndScaleYUVToRGB32Rect().
+ SkIRect output_rect = ScaleRect(AlignRect(i.rect()),
+ image_size, output_size_);
if (!output_rect.intersect(clip_rect))
continue;
// The scaler will not to read outside the input dimensions.
- media::ScaleYUVToRGB32WithRect(last_image_->planes[0],
- last_image_->planes[1],
- last_image_->planes[2],
- output_rgb_buf,
- image_size.width(),
- image_size.height(),
- output_size_.width(),
- output_size_.height(),
- output_rect.x(),
- output_rect.y(),
- output_rect.right(),
- output_rect.bottom(),
- last_image_->stride[0],
- last_image_->stride[1],
- output_stride);
-
- output_region->op(output_rect, SkRegion::kUnion_Op);
+ ConvertAndScaleYUVToRGB32Rect(last_image_->planes[0],
+ last_image_->planes[1],
+ last_image_->planes[2],
+ last_image_->stride[0],
+ last_image_->stride[1],
+ image_size,
+ SkIRect::MakeSize(image_size),
+ output_rgb_buf,
+ output_stride,
+ output_size_,
+ SkIRect::MakeSize(output_size_),
+ output_rect);
+
+ updated_region_.op(output_rect, SkRegion::kUnion_Op);
}
}
diff --git a/remoting/base/decoder_vp8.h b/remoting/base/decoder_vp8.h
index 76fd547..0549f03 100644
--- a/remoting/base/decoder_vp8.h
+++ b/remoting/base/decoder_vp8.h
@@ -35,19 +35,6 @@ class DecoderVp8 : public Decoder {
kError,
};
- // Return true if scaling is enabled
- bool DoScaling() const;
-
- // Perform color space conversion on the specified region.
- // Writes the updated region to |output_region|.
- void ConvertRegion(const SkRegion& region,
- SkRegion* output_region);
-
- // Perform scaling and color space conversion on the specified
- // region. Writes the updated rectangles to |output_region|.
- void ScaleAndConvertRegion(const SkRegion& region,
- SkRegion* output_region);
-
// The internal state of the decoder.
State state_;
diff --git a/remoting/base/util.cc b/remoting/base/util.cc
index 486b6a3..30691ea 100644
--- a/remoting/base/util.cc
+++ b/remoting/base/util.cc
@@ -11,6 +11,7 @@
#include "base/time.h"
#include "media/base/video_frame.h"
#include "media/base/yuv_convert.h"
+#include "third_party/skia/include/core/SkRegion.h"
using media::VideoFrame;
@@ -48,38 +49,15 @@ static int CalculateRGBOffset(int x, int y, int stride) {
}
static int CalculateYOffset(int x, int y, int stride) {
+ DCHECK(((x & 1) == 0) && ((y & 1) == 0));
return stride * y + x;
}
static int CalculateUVOffset(int x, int y, int stride) {
+ DCHECK(((x & 1) == 0) && ((y & 1) == 0));
return stride * y / 2 + x / 2;
}
-void ConvertYUVToRGB32WithRect(const uint8* y_plane,
- const uint8* u_plane,
- const uint8* v_plane,
- uint8* rgb_plane,
- const SkIRect& rect,
- int y_stride,
- int uv_stride,
- int rgb_stride) {
- DCHECK((rect.x() & 1) == 0 && (rect.y() & 1) == 0);
- int rgb_offset = CalculateRGBOffset(rect.left(), rect.top(), rgb_stride);
- int y_offset = CalculateYOffset(rect.left(), rect.top(), y_stride);
- int uv_offset = CalculateUVOffset(rect.left(), rect.top(), uv_stride);
-
- media::ConvertYUVToRGB32(y_plane + y_offset,
- u_plane + uv_offset,
- v_plane + uv_offset,
- rgb_plane + rgb_offset,
- rect.width(),
- rect.height(),
- y_stride,
- uv_stride,
- rgb_stride,
- media::YV12);
-}
-
void ConvertRGB32ToYUVWithRect(const uint8* rgb_plane,
uint8* y_plane,
uint8* u_plane,
@@ -106,6 +84,126 @@ void ConvertRGB32ToYUVWithRect(const uint8* rgb_plane,
uv_stride);
}
+void ConvertAndScaleYUVToRGB32Rect(const uint8* source_yplane,
+ const uint8* source_uplane,
+ const uint8* source_vplane,
+ int source_ystride,
+ int source_uvstride,
+ const SkISize& source_size,
+ const SkIRect& source_buffer_rect,
+ uint8* dest_buffer,
+ int dest_stride,
+ const SkISize& dest_size,
+ const SkIRect& dest_buffer_rect,
+ const SkIRect& dest_rect) {
+ // N.B. It is caller's responsibility to check if strides are large enough. We
+ // cannot do it here anyway.
+ DCHECK(SkIRect::MakeSize(source_size).contains(source_buffer_rect));
+ DCHECK(SkIRect::MakeSize(dest_size).contains(dest_buffer_rect));
+ DCHECK(dest_buffer_rect.contains(dest_rect));
+ DCHECK(ScaleRect(source_buffer_rect, source_size, dest_size).
+ contains(dest_rect));
+
+ // If the source and/or destination buffers don't start at (0, 0)
+ // offset the pointers to pretend we have complete buffers.
+ int y_offset = - CalculateYOffset(source_buffer_rect.x(),
+ source_buffer_rect.y(),
+ source_ystride);
+ int uv_offset = - CalculateUVOffset(source_buffer_rect.x(),
+ source_buffer_rect.y(),
+ source_uvstride);
+ int rgb_offset = - CalculateRGBOffset(dest_buffer_rect.x(),
+ dest_buffer_rect.y(),
+ dest_stride);
+
+ // See if scaling is needed.
+ if (source_size == dest_size) {
+ // Calculate the inner rectangle that can be copied by the optimized
+ // ConvertYUVToRGB32().
+ SkIRect inner_rect =
+ SkIRect::MakeLTRB(RoundToTwosMultiple(dest_rect.left() + 1),
+ RoundToTwosMultiple(dest_rect.top() + 1),
+ dest_rect.right(),
+ dest_rect.bottom());
+
+ // Offset pointers to point to the top left corner of the inner rectangle.
+ y_offset += CalculateYOffset(inner_rect.x(), inner_rect.y(),
+ source_ystride);
+ uv_offset += CalculateUVOffset(inner_rect.x(), inner_rect.y(),
+ source_uvstride);
+ rgb_offset += CalculateRGBOffset(inner_rect.x(), inner_rect.y(),
+ dest_stride);
+
+ media::ConvertYUVToRGB32(source_yplane + y_offset,
+ source_uplane + uv_offset,
+ source_vplane + uv_offset,
+ dest_buffer + rgb_offset,
+ inner_rect.width(),
+ inner_rect.height(),
+ source_ystride,
+ source_uvstride,
+ dest_stride,
+ media::YV12);
+
+ // Now see if some pixels weren't copied due to alignment.
+ if (dest_rect != inner_rect) {
+ SkIRect outer_rect =
+ SkIRect::MakeLTRB(RoundToTwosMultiple(dest_rect.left()),
+ RoundToTwosMultiple(dest_rect.top()),
+ dest_rect.right(),
+ dest_rect.bottom());
+
+ SkIPoint offset = SkIPoint::Make(outer_rect.x() - inner_rect.x(),
+ outer_rect.y() - inner_rect.y());
+
+ // Offset the pointers to point to the top left corner of the outer
+ // rectangle.
+ y_offset += CalculateYOffset(offset.x(), offset.y(), source_ystride);
+ uv_offset += CalculateUVOffset(offset.x(), offset.y(), source_uvstride);
+ rgb_offset += CalculateRGBOffset(offset.x(), offset.y(), dest_stride);
+
+ // Draw unaligned edges.
+ SkRegion edges(dest_rect);
+ edges.op(inner_rect, SkRegion::kDifference_Op);
+ for (SkRegion::Iterator i(edges); !i.done(); i.next()) {
+ SkIRect rect(i.rect());
+ rect.offset(- outer_rect.left(), - outer_rect.top());
+ media::ScaleYUVToRGB32WithRect(source_yplane + y_offset,
+ source_uplane + uv_offset,
+ source_vplane + uv_offset,
+ dest_buffer + rgb_offset,
+ source_size.width(),
+ source_size.height(),
+ dest_size.width(),
+ dest_size.height(),
+ rect.left(),
+ rect.top(),
+ rect.right(),
+ rect.bottom(),
+ source_ystride,
+ source_uvstride,
+ dest_stride);
+ }
+ }
+ } else {
+ media::ScaleYUVToRGB32WithRect(source_yplane + y_offset,
+ source_uplane + uv_offset,
+ source_vplane + uv_offset,
+ dest_buffer + rgb_offset,
+ source_size.width(),
+ source_size.height(),
+ dest_size.width(),
+ dest_size.height(),
+ dest_rect.left(),
+ dest_rect.top(),
+ dest_rect.right(),
+ dest_rect.bottom(),
+ source_ystride,
+ source_uvstride,
+ dest_stride);
+ }
+}
+
int RoundToTwosMultiple(int x) {
return x & (~1);
}
@@ -153,4 +251,31 @@ void CopyRect(const uint8* src_plane,
}
}
+void CopyRGB32Rect(const uint8* source_buffer,
+ int source_stride,
+ const SkIRect& source_buffer_rect,
+ uint8* dest_buffer,
+ int dest_stride,
+ const SkIRect& dest_buffer_rect,
+ const SkIRect& dest_rect) {
+ DCHECK(dest_buffer_rect.contains(dest_rect));
+ DCHECK(source_buffer_rect.contains(dest_rect));
+
+ // Get the address of the starting point.
+ int source_offset = CalculateRGBOffset(dest_rect.x() - source_buffer_rect.x(),
+ dest_rect.y() - source_buffer_rect.y(),
+ source_stride);
+ int dest_offset = CalculateRGBOffset(dest_rect.x() - dest_buffer_rect.x(),
+ dest_rect.y() - dest_buffer_rect.y(),
+ source_stride);
+
+ // Copy bits.
+ CopyRect(source_buffer + source_offset,
+ source_stride,
+ dest_buffer + dest_offset,
+ dest_stride,
+ GetBytesPerPixel(media::VideoFrame::RGB32),
+ SkIRect::MakeWH(dest_rect.width(), dest_rect.height()));
+}
+
} // namespace remoting
diff --git a/remoting/base/util.h b/remoting/base/util.h
index c05ef2f..c4621cc 100644
--- a/remoting/base/util.h
+++ b/remoting/base/util.h
@@ -18,16 +18,33 @@ std::string GetTimestampString();
// TODO(sergeyu): Move these methods to media.
int GetBytesPerPixel(media::VideoFrame::Format format);
-// Convert YUV to RGB32 on a specific rectangle.
-void ConvertYUVToRGB32WithRect(const uint8* y_plane,
- const uint8* u_plane,
- const uint8* v_plane,
- uint8* rgb_plane,
- const SkIRect& rect,
- int y_stride,
- int uv_stride,
- int rgb_stride);
+// Convert and scale YUV to RGB32 on a specific rectangle. The source and
+// destination buffers are assumed to contain only |source_buffer_rect| and
+// |dest_buffer_rect| areas correspondingly. The scaling factor is determined
+// as ratio between |dest_size| and |source_size|. The target rectangle
+// |dect_rect| is specified in the destination coordinates.
+//
+// |source_buffer_rect| and |dest_buffer_rect| must fall entirely within
+// the source and destination dimensions, respectively. |dest_rect| must be
+// completely contained within the source and destinations buffers boundaries
+// including the case when scaling is requested.
+//
+// N.B. The top left corner coordinates of YUV buffer should have even X and Y
+// coordinates.
+void ConvertAndScaleYUVToRGB32Rect(const uint8* source_yplane,
+ const uint8* source_uplane,
+ const uint8* source_vplane,
+ int source_ystride,
+ int source_uvstride,
+ const SkISize& source_size,
+ const SkIRect& source_buffer_rect,
+ uint8* dest_buffer,
+ int dest_stride,
+ const SkISize& dest_size,
+ const SkIRect& dest_buffer_rect,
+ const SkIRect& dest_rect);
+// Convert RGB32 to YUV on a specific rectangle.
void ConvertRGB32ToYUVWithRect(const uint8* rgb_plane,
uint8* y_plane,
uint8* u_plane,
@@ -60,6 +77,14 @@ void CopyRect(const uint8* src_plane,
int bytes_per_pixel,
const SkIRect& rect);
+void CopyRGB32Rect(const uint8* source_buffer,
+ int source_stride,
+ const SkIRect& source_buffer_rect,
+ uint8* dest_buffer,
+ int dest_stride,
+ const SkIRect& dest_buffer_rect,
+ const SkIRect& dest_rect);
+
} // namespace remoting
#endif // REMOTING_BASE_UTIL_H_
diff --git a/remoting/base/util_unittest.cc b/remoting/base/util_unittest.cc
new file mode 100644
index 0000000..24e628d
--- /dev/null
+++ b/remoting/base/util_unittest.cc
@@ -0,0 +1,187 @@
+// 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 <algorithm>
+
+#include "remoting/base/util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "third_party/skia/include/core/SkSize.h"
+
+static const int kWidth = 32 ;
+static const int kHeight = 24 ;
+static const int kBytesPerPixel = 4;
+static const int kYStride = kWidth;
+static const int kUvStride = kWidth / 2;
+static const int kRgbStride = kWidth * kBytesPerPixel;
+static const uint32 kFillColor = 0xffffff;
+
+namespace remoting {
+
+class YuvToRgbTester {
+ public:
+ YuvToRgbTester() {
+ yuv_buffer_size_ = (kYStride + kUvStride) * kHeight;
+ yuv_buffer_.reset(new uint8[yuv_buffer_size_]);
+ yplane_ = yuv_buffer_.get();
+ uplane_ = yplane_ + (kYStride * kHeight);
+ vplane_ = uplane_ + (kUvStride * kHeight / 2);
+
+ rgb_buffer_size_ = kWidth * kHeight * kBytesPerPixel;
+ rgb_buffer_.reset(new uint8[rgb_buffer_size_]);
+
+ ResetYuvBuffer();
+ ResetRgbBuffer();
+ }
+
+ ~YuvToRgbTester() {}
+
+ void ResetYuvBuffer() {
+ memset(yuv_buffer_.get(), 0, yuv_buffer_size_);
+ }
+
+ void ResetRgbBuffer() {
+ memset(rgb_buffer_.get(), 0, rgb_buffer_size_);
+ }
+
+ void FillRgbBuffer(const SkIRect& rect) {
+ uint32* ptr = reinterpret_cast<uint32*>(
+ rgb_buffer_.get() + (rect.top() * kRgbStride) +
+ (rect.left() * kBytesPerPixel));
+ int width = rect.width();
+ for (int height = rect.height(); height > 0; --height) {
+ std::fill(ptr, ptr + width, kFillColor);
+ ptr += kRgbStride / kBytesPerPixel;
+ }
+ }
+
+ // Check the the desination buffer is filled within expected bounds.
+ void CheckRgbBuffer(const SkIRect& rect) {
+ uint32* ptr = reinterpret_cast<uint32*>(rgb_buffer_.get());
+ for (int y = 0; y < kHeight; ++y) {
+ if (y < rect.top() || rect.bottom() <= y) {
+ // The whole line should be intact.
+ EXPECT_EQ((ptrdiff_t)kWidth,
+ std::count(ptr, ptr + kWidth, 0u));
+ } else {
+ // The space before the painted rectangle should be intact.
+ EXPECT_EQ((ptrdiff_t)rect.left(),
+ std::count(ptr, ptr + rect.left(), 0u));
+
+ // All pixels of the target rectangle should be touched.
+ EXPECT_EQ(ptr + rect.right(),
+ std::find(ptr + rect.left(), ptr + rect.right(), 0u));
+
+ // The space after the painted rectangle should be intact.
+ EXPECT_EQ((ptrdiff_t)kWidth - rect.right(),
+ std::count(ptr + rect.right(), ptr + kWidth, 0u));
+ }
+ ptr += kRgbStride / kBytesPerPixel;
+ }
+ }
+
+ void RunTest(const SkISize dest_size, const SkIRect& rect) {
+ ASSERT_TRUE(SkIRect::MakeSize(dest_size).contains(rect));
+
+ // Reset buffers.
+ ResetYuvBuffer();
+ ResetRgbBuffer();
+ FillRgbBuffer(rect);
+
+ // RGB -> YUV
+ ConvertRGB32ToYUVWithRect(rgb_buffer_.get(),
+ yplane_,
+ uplane_,
+ vplane_,
+ 0,
+ 0,
+ kWidth,
+ kHeight,
+ kRgbStride,
+ kYStride,
+ kUvStride);
+
+ // Reset RGB buffer and do opposite conversion.
+ ResetRgbBuffer();
+ ConvertAndScaleYUVToRGB32Rect(yplane_,
+ uplane_,
+ vplane_,
+ kYStride,
+ kUvStride,
+ SkISize::Make(kWidth, kHeight),
+ SkIRect::MakeWH(kWidth, kHeight),
+ rgb_buffer_.get(),
+ kRgbStride,
+ dest_size,
+ SkIRect::MakeSize(dest_size),
+ rect);
+
+ // Check if it worked out.
+ CheckRgbBuffer(rect);
+ }
+
+ void TestBasicConversion() {
+ // Whole buffer.
+ RunTest(SkISize::Make(kWidth, kHeight), SkIRect::MakeWH(kWidth, kHeight));
+ }
+
+ private:
+ size_t yuv_buffer_size_;
+ scoped_array<uint8> yuv_buffer_;
+ uint8* yplane_;
+ uint8* uplane_;
+ uint8* vplane_;
+
+ size_t rgb_buffer_size_;
+ scoped_array<uint8> rgb_buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(YuvToRgbTester);
+};
+
+TEST(YuvToRgbTest, BasicConversion) {
+ YuvToRgbTester tester;
+ tester.TestBasicConversion();
+}
+
+TEST(YuvToRgbTest, Clipping) {
+ YuvToRgbTester tester;
+
+ SkISize dest_size = SkISize::Make(kWidth, kHeight);
+ SkIRect rect = SkIRect::MakeLTRB(0, 0, kWidth - 1, kHeight - 1);
+ for (int i = 0; i < 16; ++i) {
+ SkIRect dest_rect = rect;
+ if ((i & 1) != 0)
+ dest_rect.fLeft += 1;
+ if ((i & 2) != 0)
+ dest_rect.fTop += 1;
+ if ((i & 4) != 0)
+ dest_rect.fRight += 1;
+ if ((i & 8) != 0)
+ dest_rect.fBottom += 1;
+
+ tester.RunTest(dest_size, dest_rect);
+ }
+}
+
+TEST(YuvToRgbTest, ClippingAndScaling) {
+ YuvToRgbTester tester;
+
+ SkISize dest_size = SkISize::Make(kWidth - 10, kHeight - 10);
+ SkIRect rect = SkIRect::MakeLTRB(5, 5, kWidth - 11, kHeight - 11);
+ for (int i = 0; i < 16; ++i) {
+ SkIRect dest_rect = rect;
+ if ((i & 1) != 0)
+ dest_rect.fLeft += 1;
+ if ((i & 2) != 0)
+ dest_rect.fTop += 1;
+ if ((i & 4) != 0)
+ dest_rect.fRight += 1;
+ if ((i & 8) != 0)
+ dest_rect.fBottom += 1;
+
+ tester.RunTest(dest_size, dest_rect);
+ }
+}
+
+} // namespace remoting
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index 4b86ba2..5c69bc2 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -922,6 +922,7 @@
'base/encoder_row_based_unittest.cc',
'base/base_mock_objects.cc',
'base/base_mock_objects.h',
+ 'base/util_unittest.cc',
'client/mouse_input_filter_unittest.cc',
'host/capturer_linux_unittest.cc',
'host/capturer_mac_unittest.cc',