diff options
author | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-16 10:45:24 +0000 |
---|---|---|
committer | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-16 10:45:24 +0000 |
commit | b9ed58f046141b4610c1bdc966d962d5fb95ac6b (patch) | |
tree | b7dc10e49ea765b6ebc99985d3538a2e1363942e /remoting | |
parent | c1c88cf23606dd1ca2bfd9f57496aeec847e713f (diff) | |
download | chromium_src-b9ed58f046141b4610c1bdc966d962d5fb95ac6b.zip chromium_src-b9ed58f046141b4610c1bdc966d962d5fb95ac6b.tar.gz chromium_src-b9ed58f046141b4610c1bdc966d962d5fb95ac6b.tar.bz2 |
Use webrtc::DesktopCapturer for screen capturer implementation.
Screen capturers are being moved from media/video/capture/screen to
third_party/webrtc. This CL is an intermediate step in that process.
Depends on https://webrtc-codereview.appspot.com/1322007/
TBR=brettw@chromium.org (third_party/webrtc dependency)
Review URL: https://chromiumcodereview.appspot.com/13983010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@200504 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
34 files changed, 863 insertions, 643 deletions
diff --git a/remoting/codec/DEPS b/remoting/codec/DEPS index 61e5c26..913ea7d 100644 --- a/remoting/codec/DEPS +++ b/remoting/codec/DEPS @@ -5,4 +5,5 @@ include_rules = [ "+google/protobuf", "+third_party/opus", "+third_party/speex", + "+third_party/webrtc", ] diff --git a/remoting/codec/codec_test.cc b/remoting/codec/codec_test.cc index af8b11b..efcc872 100644 --- a/remoting/codec/codec_test.cc +++ b/remoting/codec/codec_test.cc @@ -14,27 +14,33 @@ #include "remoting/codec/video_encoder.h" #include "remoting/base/util.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" + +using webrtc::DesktopRect; +using webrtc::DesktopSize; namespace { const int kBytesPerPixel = 4; // Some sample rects for testing. -std::vector<std::vector<SkIRect> > MakeTestRectLists(const SkISize& size) { - std::vector<std::vector<SkIRect> > rect_lists; - std::vector<SkIRect> rects; - rects.push_back(SkIRect::MakeXYWH(0, 0, size.width(), size.height())); +std::vector<std::vector<DesktopRect> > MakeTestRectLists(DesktopSize size) { + std::vector<std::vector<DesktopRect> > rect_lists; + std::vector<DesktopRect> rects; + rects.push_back(DesktopRect::MakeXYWH(0, 0, size.width(), size.height())); rect_lists.push_back(rects); rects.clear(); - rects.push_back(SkIRect::MakeXYWH(0, 0, size.width() / 2, size.height() / 2)); + rects.push_back(DesktopRect::MakeXYWH( + 0, 0, size.width() / 2, size.height() / 2)); rect_lists.push_back(rects); rects.clear(); - rects.push_back(SkIRect::MakeXYWH(size.width() / 2, size.height() / 2, - size.width() / 2, size.height() / 2)); + rects.push_back(DesktopRect::MakeXYWH( + size.width() / 2, size.height() / 2, + size.width() / 2, size.height() / 2)); rect_lists.push_back(rects); rects.clear(); - rects.push_back(SkIRect::MakeXYWH(16, 16, 16, 16)); - rects.push_back(SkIRect::MakeXYWH(128, 64, 32, 32)); + rects.push_back(DesktopRect::MakeXYWH(16, 16, 16, 16)); + rects.push_back(DesktopRect::MakeXYWH(128, 64, 32, 32)); rect_lists.push_back(rects); return rect_lists; } @@ -72,10 +78,10 @@ class VideoEncoderMessageTester { ++begin_rect_; if (strict_) { - SkIRect rect = rects_.front(); + DesktopRect rect = rects_.front(); rects_.pop_front(); - EXPECT_EQ(rect.fLeft, packet->format().x()); - EXPECT_EQ(rect.fTop, packet->format().y()); + EXPECT_EQ(rect.left(), packet->format().x()); + EXPECT_EQ(rect.top(), packet->format().y()); EXPECT_EQ(rect.width(), packet->format().width()); EXPECT_EQ(rect.height(), packet->format().height()); } @@ -107,7 +113,7 @@ class VideoEncoderMessageTester { strict_ = strict; } - void AddRects(const SkIRect* rects, int count) { + void AddRects(const DesktopRect* rects, int count) { rects_.insert(rects_.begin() + rects_.size(), rects, rects + count); added_rects_ += count; } @@ -125,15 +131,16 @@ class VideoEncoderMessageTester { State state_; bool strict_; - std::deque<SkIRect> rects_; + std::deque<DesktopRect> rects_; DISALLOW_COPY_AND_ASSIGN(VideoEncoderMessageTester); }; class VideoDecoderTester { public: - VideoDecoderTester(VideoDecoder* decoder, const SkISize& screen_size, - const SkISize& view_size) + VideoDecoderTester(VideoDecoder* decoder, + const DesktopSize& screen_size, + const DesktopSize& view_size) : screen_size_(screen_size), view_size_(view_size), strict_(false), @@ -141,11 +148,12 @@ class VideoDecoderTester { image_data_.reset(new uint8[ view_size_.width() * view_size_.height() * kBytesPerPixel]); EXPECT_TRUE(image_data_.get()); - decoder_->Initialize(screen_size_); + decoder_->Initialize( + SkISize::Make(screen_size_.width(), screen_size_.height())); } void Reset() { - expected_region_.setEmpty(); + expected_region_.Clear(); update_region_.setEmpty(); } @@ -165,11 +173,12 @@ class VideoDecoderTester { } void RenderFrame() { - decoder_->RenderFrame(view_size_, - SkIRect::MakeSize(view_size_), - image_data_.get(), - view_size_.width() * kBytesPerPixel, - &update_region_); + decoder_->RenderFrame( + SkISize::Make(view_size_.width(), view_size_.height()), + SkIRect::MakeWH(view_size_.width(), view_size_.height()), + image_data_.get(), + view_size_.width() * kBytesPerPixel, + &update_region_); } void ReceivedScopedPacket(scoped_ptr<VideoPacket> packet) { @@ -180,34 +189,46 @@ class VideoDecoderTester { strict_ = strict; } - void set_capture_data(scoped_refptr<media::ScreenCaptureData> data) { - capture_data_ = data; + void set_frame(webrtc::DesktopFrame* frame) { + frame_ = frame; } - void AddRects(const SkIRect* rects, int count) { - SkRegion new_rects; - new_rects.setRects(rects, count); - AddRegion(new_rects); + void AddRects(const DesktopRect* rects, int count) { + for (int i = 0; i < count; ++i) { + expected_region_.AddRect(rects[i]); + } } - void AddRegion(const SkRegion& region) { - expected_region_.op(region, SkRegion::kUnion_Op); + void AddRegion(const webrtc::DesktopRegion& region) { + expected_region_.AddRegion(region); } void VerifyResults() { if (!strict_) return; - ASSERT_TRUE(capture_data_.get()); + ASSERT_TRUE(frame_); // Test the content of the update region. - EXPECT_EQ(expected_region_, update_region_); + // + // TODO(sergeyu): Change this to use DesktopRegion when it's capable of + // merging the rectangles. + SkRegion expected_region; + for (webrtc::DesktopRegion::Iterator it(expected_region_); + !it.IsAtEnd(); it.Advance()) { + expected_region.op( + SkIRect::MakeXYWH(it.rect().top(), it.rect().left(), + it.rect().width(), it.rect().height()), + SkRegion::kUnion_Op); + } + EXPECT_EQ(expected_region, update_region_); + for (SkRegion::Iterator i(update_region_); !i.done(); i.next()) { const int stride = view_size_.width() * kBytesPerPixel; - EXPECT_EQ(stride, capture_data_->stride()); + EXPECT_EQ(stride, frame_->stride()); const int offset = stride * i.rect().top() + kBytesPerPixel * i.rect().left(); - const uint8* original = capture_data_->data() + offset; + const uint8* original = frame_->data() + offset; const uint8* decoded = image_data_.get() + offset; const int row_size = kBytesPerPixel * i.rect().width(); for (int y = 0; y < i.rect().height(); ++y) { @@ -265,14 +286,14 @@ class VideoDecoderTester { } private: - SkISize screen_size_; - SkISize view_size_; + DesktopSize screen_size_; + DesktopSize view_size_; bool strict_; - SkRegion expected_region_; + webrtc::DesktopRegion expected_region_; SkRegion update_region_; VideoDecoder* decoder_; scoped_ptr<uint8[]> image_data_; - scoped_refptr<media::ScreenCaptureData> capture_data_; + webrtc::DesktopFrame* frame_; DISALLOW_COPY_AND_ASSIGN(VideoDecoderTester); }; @@ -301,7 +322,7 @@ class VideoEncoderTester { } } - void AddRects(const SkIRect* rects, int count) { + void AddRects(const DesktopRect* rects, int count) { message_tester_->AddRects(rects, count); } @@ -317,34 +338,30 @@ class VideoEncoderTester { DISALLOW_COPY_AND_ASSIGN(VideoEncoderTester); }; -scoped_refptr<media::ScreenCaptureData> PrepareEncodeData( - const SkISize& size, - scoped_ptr<uint8[]>* memory) { - int memory_size = size.width() * size.height() * kBytesPerPixel; - - memory->reset(new uint8[memory_size]); +scoped_ptr<webrtc::DesktopFrame> PrepareFrame(const DesktopSize& size) { + scoped_ptr<webrtc::DesktopFrame> frame(new webrtc::BasicDesktopFrame(size)); srand(0); + int memory_size = size.width() * size.height() * kBytesPerPixel; for (int i = 0; i < memory_size; ++i) { - (*memory)[i] = rand() % 256; + frame->data()[i] = rand() % 256; } - scoped_refptr<media::ScreenCaptureData> data = new media::ScreenCaptureData( - memory->get(), size.width() * kBytesPerPixel, size); - return data; + return frame.Pass(); } static void TestEncodingRects(VideoEncoder* encoder, VideoEncoderTester* tester, - scoped_refptr<media::ScreenCaptureData> data, - const SkIRect* rects, int count) { - data->mutable_dirty_region().setEmpty(); + webrtc::DesktopFrame* frame, + const DesktopRect* rects, + int count) { + frame->mutable_updated_region()->Clear(); for (int i = 0; i < count; ++i) { - data->mutable_dirty_region().op(rects[i], SkRegion::kUnion_Op); + frame->mutable_updated_region()->AddRect(rects[i]); } tester->AddRects(rects, count); - encoder->Encode(data, true, base::Bind( + encoder->Encode(frame, base::Bind( &VideoEncoderTester::DataAvailable, base::Unretained(tester))); } @@ -356,18 +373,15 @@ void TestVideoEncoder(VideoEncoder* encoder, bool strict) { VideoEncoderTester tester(&message_tester); - scoped_ptr<uint8[]> memory; - for (size_t xi = 0; xi < arraysize(kSizes); ++xi) { for (size_t yi = 0; yi < arraysize(kSizes); ++yi) { - SkISize size = SkISize::Make(kSizes[xi], kSizes[yi]); - scoped_refptr<media::ScreenCaptureData> data = - PrepareEncodeData(size, &memory); - std::vector<std::vector<SkIRect> > test_rect_lists = + DesktopSize size = DesktopSize(kSizes[xi], kSizes[yi]); + scoped_ptr<webrtc::DesktopFrame> frame = PrepareFrame(size); + std::vector<std::vector<DesktopRect> > test_rect_lists = MakeTestRectLists(size); for (size_t i = 0; i < test_rect_lists.size(); ++i) { - const std::vector<SkIRect>& test_rects = test_rect_lists[i]; - TestEncodingRects(encoder, &tester, data, + const std::vector<DesktopRect>& test_rects = test_rect_lists[i]; + TestEncodingRects(encoder, &tester, frame.get(), &test_rects[0], test_rects.size()); } } @@ -377,68 +391,69 @@ void TestVideoEncoder(VideoEncoder* encoder, bool strict) { static void TestEncodeDecodeRects(VideoEncoder* encoder, VideoEncoderTester* encoder_tester, VideoDecoderTester* decoder_tester, - scoped_refptr<media::ScreenCaptureData> data, - const SkIRect* rects, int count) { - data->mutable_dirty_region().setRects(rects, count); + webrtc::DesktopFrame* frame, + const DesktopRect* rects, int count) { + frame->mutable_updated_region()->Clear(); + for (int i = 0; i < count; ++i) { + frame->mutable_updated_region()->AddRect(rects[i]); + } encoder_tester->AddRects(rects, count); decoder_tester->AddRects(rects, count); // Generate random data for the updated region. srand(0); for (int i = 0; i < count; ++i) { - const int bytes_per_pixel = 4; // Because of RGB32 on previous line. - const int row_size = bytes_per_pixel * rects[i].width(); - uint8* memory = data->data() + - data->stride() * rects[i].top() + - bytes_per_pixel * rects[i].left(); + const int row_size = + webrtc::DesktopFrame::kBytesPerPixel * rects[i].width(); + uint8* memory = frame->data() + + frame->stride() * rects[i].top() + + webrtc::DesktopFrame::kBytesPerPixel * rects[i].left(); for (int y = 0; y < rects[i].height(); ++y) { for (int x = 0; x < row_size; ++x) memory[x] = rand() % 256; - memory += data->stride(); + memory += frame->stride(); } } - encoder->Encode(data, true, base::Bind(&VideoEncoderTester::DataAvailable, - base::Unretained(encoder_tester))); + encoder->Encode(frame, base::Bind(&VideoEncoderTester::DataAvailable, + base::Unretained(encoder_tester))); decoder_tester->VerifyResults(); decoder_tester->Reset(); } void TestVideoEncoderDecoder( VideoEncoder* encoder, VideoDecoder* decoder, bool strict) { - SkISize kSize = SkISize::Make(320, 240); + DesktopSize kSize = DesktopSize(320, 240); VideoEncoderMessageTester message_tester; message_tester.set_strict(strict); VideoEncoderTester encoder_tester(&message_tester); - scoped_ptr<uint8[]> memory; - scoped_refptr<media::ScreenCaptureData> data = - PrepareEncodeData(kSize, &memory); + scoped_ptr<webrtc::DesktopFrame> frame = PrepareFrame(kSize); VideoDecoderTester decoder_tester(decoder, kSize, kSize); decoder_tester.set_strict(strict); - decoder_tester.set_capture_data(data); + decoder_tester.set_frame(frame.get()); encoder_tester.set_decoder_tester(&decoder_tester); - std::vector<std::vector<SkIRect> > test_rect_lists = MakeTestRectLists(kSize); + std::vector<std::vector<DesktopRect> > test_rect_lists = + MakeTestRectLists(kSize); for (size_t i = 0; i < test_rect_lists.size(); ++i) { - const std::vector<SkIRect> test_rects = test_rect_lists[i]; - TestEncodeDecodeRects(encoder, &encoder_tester, &decoder_tester, data, - &test_rects[0], test_rects.size()); + const std::vector<DesktopRect> test_rects = test_rect_lists[i]; + TestEncodeDecodeRects(encoder, &encoder_tester, &decoder_tester, + frame.get(), &test_rects[0], test_rects.size()); } } -static void FillWithGradient(uint8* memory, const SkISize& frame_size, - const SkIRect& rect) { - for (int j = rect.top(); j < rect.bottom(); ++j) { - uint8* p = memory + ((j * frame_size.width()) + rect.left()) * 4; - for (int i = rect.left(); i < rect.right(); ++i) { - *p++ = static_cast<uint8>((255.0 * i) / frame_size.width()); - *p++ = static_cast<uint8>((164.0 * j) / frame_size.height()); - *p++ = static_cast<uint8>((82.0 * (i + j)) / - (frame_size.width() + frame_size.height())); +static void FillWithGradient(webrtc::DesktopFrame* frame) { + for (int j = 0; j < frame->size().height(); ++j) { + uint8* p = frame->data() + j * frame->stride(); + for (int i = 0; i < frame->size().width(); ++i) { + *p++ = (255.0 * i) / frame->size().width(); + *p++ = (164.0 * j) / frame->size().height(); + *p++ = (82.0 * (i + j)) / + (frame->size().width() + frame->size().height()); *p++ = 0; } } @@ -446,42 +461,38 @@ static void FillWithGradient(uint8* memory, const SkISize& frame_size, void TestVideoEncoderDecoderGradient(VideoEncoder* encoder, VideoDecoder* decoder, - const SkISize& screen_size, - const SkISize& view_size, + const DesktopSize& screen_size, + const DesktopSize& view_size, double max_error_limit, double mean_error_limit) { - SkIRect screen_rect = SkIRect::MakeSize(screen_size); - scoped_ptr<uint8[]> screen_data(new uint8[ - screen_size.width() * screen_size.height() * kBytesPerPixel]); - FillWithGradient(screen_data.get(), screen_size, screen_rect); - - SkIRect view_rect = SkIRect::MakeSize(view_size); - scoped_ptr<uint8[]> expected_view_data(new uint8[ - view_size.width() * view_size.height() * kBytesPerPixel]); - FillWithGradient(expected_view_data.get(), view_size, view_rect); + scoped_ptr<webrtc::BasicDesktopFrame> frame( + new webrtc::BasicDesktopFrame(screen_size)); + FillWithGradient(frame.get()); + frame->mutable_updated_region()->SetRect(DesktopRect::MakeSize(screen_size)); - scoped_refptr<media::ScreenCaptureData> capture_data = - new media::ScreenCaptureData( - screen_data.get(), screen_size.width() * kBytesPerPixel, screen_size); - capture_data->mutable_dirty_region().op(screen_rect, SkRegion::kUnion_Op); + scoped_ptr<webrtc::BasicDesktopFrame> expected_result( + new webrtc::BasicDesktopFrame(view_size)); + FillWithGradient(expected_result.get()); VideoDecoderTester decoder_tester(decoder, screen_size, view_size); - decoder_tester.set_capture_data(capture_data); - decoder_tester.AddRegion(capture_data->dirty_region()); + decoder_tester.set_frame(frame.get()); + decoder_tester.AddRegion(frame->updated_region()); - encoder->Encode(capture_data, true, + encoder->Encode(frame.get(), base::Bind(&VideoDecoderTester::ReceivedScopedPacket, base::Unretained(&decoder_tester))); - decoder_tester.VerifyResultsApprox(expected_view_data.get(), + decoder_tester.VerifyResultsApprox(expected_result->data(), max_error_limit, mean_error_limit); // Check that the decoder correctly re-renders the frame if its client // invalidates the frame. decoder_tester.ResetRenderedData(); - decoder->Invalidate(view_size, SkRegion(view_rect)); + decoder->Invalidate( + SkISize::Make(view_size.width(), view_size.height()), + SkRegion(SkIRect::MakeWH(view_size.width(), view_size.height()))); decoder_tester.RenderFrame(); - decoder_tester.VerifyResultsApprox(expected_view_data.get(), + decoder_tester.VerifyResultsApprox(expected_result->data(), max_error_limit, mean_error_limit); } diff --git a/remoting/codec/codec_test.h b/remoting/codec/codec_test.h index d2bda83..e7e055f 100644 --- a/remoting/codec/codec_test.h +++ b/remoting/codec/codec_test.h @@ -6,8 +6,10 @@ #define REMOTING_CODEC_CODEC_TEST_H_ #include "base/memory/ref_counted.h" -#include "media/base/video_frame.h" -#include "media/video/capture/screen/screen_capture_data.h" + +namespace webrtc { +class DesktopSize; +} namespace remoting { @@ -33,8 +35,8 @@ void TestVideoEncoderDecoder(VideoEncoder* encoder, // pair. void TestVideoEncoderDecoderGradient(VideoEncoder* encoder, VideoDecoder* decoder, - const SkISize& screen_size, - const SkISize& view_size, + const webrtc::DesktopSize& screen_size, + const webrtc::DesktopSize& view_size, double max_error_limit, double mean_error_limit); diff --git a/remoting/codec/video_decoder_vp8_unittest.cc b/remoting/codec/video_decoder_vp8_unittest.cc index d8d36f0..64d30df 100644 --- a/remoting/codec/video_decoder_vp8_unittest.cc +++ b/remoting/codec/video_decoder_vp8_unittest.cc @@ -8,6 +8,7 @@ #include "remoting/codec/codec_test.h" #include "remoting/codec/video_encoder_vp8.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" namespace remoting { @@ -19,10 +20,11 @@ class VideoDecoderVp8Test : public testing::Test { void TestGradient(int screen_width, int screen_height, int view_width, int view_height, double max_error_limit, double mean_error_limit) { - TestVideoEncoderDecoderGradient(&encoder_, &decoder_, - SkISize::Make(screen_width, screen_height), - SkISize::Make(view_width, view_height), - max_error_limit, mean_error_limit); + TestVideoEncoderDecoderGradient( + &encoder_, &decoder_, + webrtc::DesktopSize(screen_width, screen_height), + webrtc::DesktopSize(view_width, view_height), + max_error_limit, mean_error_limit); } }; diff --git a/remoting/codec/video_encoder.h b/remoting/codec/video_encoder.h index 81179c4..a89e1e7 100644 --- a/remoting/codec/video_encoder.h +++ b/remoting/codec/video_encoder.h @@ -7,19 +7,19 @@ #include "base/basictypes.h" #include "base/callback.h" -#include "media/base/data_buffer.h" -namespace media { -class ScreenCaptureData; -} // namespace media +class SkRegion; + +namespace webrtc { +class DesktopFrame; +} // namespace webrtc namespace remoting { class VideoPacket; -// A class to perform the task of encoding a continous stream of -// images. -// This class operates asynchronously to enable maximum throughput. +// A class to perform the task of encoding a continuous stream of images. The +// interface is asynchronous to enable maximum throughput. class VideoEncoder { public: @@ -29,15 +29,10 @@ class VideoEncoder { virtual ~VideoEncoder() {} - // Encode an image stored in |capture_data|. - // - // If |key_frame| is true, the encoder should not reference - // previous encode and encode the full frame. - // - // When encoded data is available, partial or full |data_available_callback| - // is called. - virtual void Encode(scoped_refptr<media::ScreenCaptureData> capture_data, - bool key_frame, + // Encode an image stored in |frame|. Doesn't take ownership of |frame|. When + // encoded data is available, partial or full |data_available_callback| is + // called. + virtual void Encode(const webrtc::DesktopFrame* frame, const DataAvailableCallback& data_available_callback) = 0; }; diff --git a/remoting/codec/video_encoder_verbatim.cc b/remoting/codec/video_encoder_verbatim.cc index 2e7f6d0..4680362 100644 --- a/remoting/codec/video_encoder_verbatim.cc +++ b/remoting/codec/video_encoder_verbatim.cc @@ -5,17 +5,17 @@ #include "remoting/codec/video_encoder_verbatim.h" #include "base/logging.h" -#include "media/video/capture/screen/screen_capture_data.h" +#include "base/stl_util.h" #include "remoting/base/util.h" #include "remoting/proto/video.pb.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" namespace remoting { static const int kPacketSize = 1024 * 1024; VideoEncoderVerbatim::VideoEncoderVerbatim() - : screen_size_(SkISize::Make(0,0)), - max_packet_size_(kPacketSize) { + : max_packet_size_(kPacketSize) { } void VideoEncoderVerbatim::SetMaxPacketSize(int size) { @@ -26,35 +26,33 @@ VideoEncoderVerbatim::~VideoEncoderVerbatim() { } void VideoEncoderVerbatim::Encode( - scoped_refptr<media::ScreenCaptureData> capture_data, - bool key_frame, + const webrtc::DesktopFrame* frame, const DataAvailableCallback& data_available_callback) { - capture_data_ = capture_data; callback_ = data_available_callback; encode_start_time_ = base::Time::Now(); - const SkRegion& region = capture_data->dirty_region(); - SkRegion::Iterator iter(region); - while (!iter.done()) { - SkIRect rect = iter.rect(); - iter.next(); - EncodeRect(rect, iter.done()); + webrtc::DesktopRegion::Iterator iter(frame->updated_region()); + while (!iter.IsAtEnd()) { + const webrtc::DesktopRect& rect = iter.rect(); + iter.Advance(); + EncodeRect(frame, rect, iter.IsAtEnd()); } - capture_data_ = NULL; callback_.Reset(); } -void VideoEncoderVerbatim::EncodeRect(const SkIRect& rect, bool last) { - CHECK(capture_data_->data()); - const int stride = capture_data_->stride(); +void VideoEncoderVerbatim::EncodeRect(const webrtc::DesktopFrame* frame, + const webrtc::DesktopRect& rect, + bool last) { + CHECK(frame->data()); + const int stride = frame->stride(); const int bytes_per_pixel = 4; const int row_size = bytes_per_pixel * rect.width(); scoped_ptr<VideoPacket> packet(new VideoPacket()); - PrepareUpdateStart(rect, packet.get()); - const uint8* in = capture_data_->data() + - rect.fTop * stride + rect.fLeft * bytes_per_pixel; + PrepareUpdateStart(frame, rect, packet.get()); + const uint8* in = frame->data() + + rect.top() * stride + rect.left() * bytes_per_pixel; // TODO(hclam): Fill in the sequence number. uint8* out = GetOutputBuffer(packet.get(), max_packet_size_); int filled = 0; @@ -88,16 +86,14 @@ void VideoEncoderVerbatim::EncodeRect(const SkIRect& rect, bool last) { packet->mutable_data()->resize(filled); packet->set_flags(packet->flags() | VideoPacket::LAST_PACKET); - packet->set_capture_time_ms(capture_data_->capture_time_ms()); + + packet->set_capture_time_ms(frame->capture_time_ms()); packet->set_encode_time_ms( (base::Time::Now() - encode_start_time_).InMillisecondsRoundedUp()); - packet->set_client_sequence_number( - capture_data_->client_sequence_number()); - SkIPoint dpi(capture_data_->dpi()); - if (dpi.x()) - packet->mutable_format()->set_x_dpi(dpi.x()); - if (dpi.y()) - packet->mutable_format()->set_y_dpi(dpi.y()); + if (!frame->dpi().is_zero()) { + packet->mutable_format()->set_x_dpi(frame->dpi().x()); + packet->mutable_format()->set_y_dpi(frame->dpi().y()); + } if (last) packet->set_flags(packet->flags() | VideoPacket::LAST_PARTITION); } @@ -110,18 +106,19 @@ void VideoEncoderVerbatim::EncodeRect(const SkIRect& rect, bool last) { } } -void VideoEncoderVerbatim::PrepareUpdateStart(const SkIRect& rect, +void VideoEncoderVerbatim::PrepareUpdateStart(const webrtc::DesktopFrame* frame, + const webrtc::DesktopRect& rect, VideoPacket* packet) { packet->set_flags(packet->flags() | VideoPacket::FIRST_PACKET); VideoPacketFormat* format = packet->mutable_format(); - format->set_x(rect.fLeft); - format->set_y(rect.fTop); + format->set_x(rect.left()); + format->set_y(rect.top()); format->set_width(rect.width()); format->set_height(rect.height()); format->set_encoding(VideoPacketFormat::ENCODING_VERBATIM); - if (capture_data_->size() != screen_size_) { - screen_size_ = capture_data_->size(); + if (frame->size().equals(screen_size_)) { + screen_size_ = frame->size(); format->set_screen_width(screen_size_.width()); format->set_screen_height(screen_size_.height()); } @@ -129,9 +126,7 @@ void VideoEncoderVerbatim::PrepareUpdateStart(const SkIRect& rect, uint8* VideoEncoderVerbatim::GetOutputBuffer(VideoPacket* packet, size_t size) { packet->mutable_data()->resize(size); - // TODO(ajwong): Is there a better way to do this at all??? - return const_cast<uint8*>(reinterpret_cast<const uint8*>( - packet->mutable_data()->data())); + return reinterpret_cast<uint8*>(string_as_array(packet->mutable_data())); } } // namespace remoting diff --git a/remoting/codec/video_encoder_verbatim.h b/remoting/codec/video_encoder_verbatim.h index 2b6250b..bf8e901 100644 --- a/remoting/codec/video_encoder_verbatim.h +++ b/remoting/codec/video_encoder_verbatim.h @@ -8,7 +8,7 @@ #include "base/time.h" #include "remoting/codec/video_encoder.h" #include "remoting/proto/video.pb.h" -#include "third_party/skia/include/core/SkRect.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" namespace remoting { @@ -24,17 +24,20 @@ class VideoEncoderVerbatim : public VideoEncoder { // VideoEncoder interface. virtual void Encode( - scoped_refptr<media::ScreenCaptureData> capture_data, - bool key_frame, + const webrtc::DesktopFrame* frame, const DataAvailableCallback& data_available_callback) OVERRIDE; private: // Encode a single dirty |rect|. - void EncodeRect(const SkIRect& rect, bool last); + void EncodeRect(const webrtc::DesktopFrame* frame, + const webrtc::DesktopRect& rect, + bool last); // Initializes first packet in a sequence of video packets to update screen // rectangle |rect|. - void PrepareUpdateStart(const SkIRect& rect, VideoPacket* packet); + void PrepareUpdateStart(const webrtc::DesktopFrame* frame, + const webrtc::DesktopRect& rect, + VideoPacket* packet); // Allocates a buffer of the specified |size| inside |packet| and returns the // pointer to it. @@ -43,12 +46,11 @@ class VideoEncoderVerbatim : public VideoEncoder { // Submit |packet| to |callback_|. void SubmitMessage(VideoPacket* packet, size_t rect_index); - scoped_refptr<media::ScreenCaptureData> capture_data_; DataAvailableCallback callback_; base::Time encode_start_time_; // The most recent screen size. - SkISize screen_size_; + webrtc::DesktopSize screen_size_; int max_packet_size_; }; diff --git a/remoting/codec/video_encoder_vp8.cc b/remoting/codec/video_encoder_vp8.cc index a4e4d1a..7824065 100644 --- a/remoting/codec/video_encoder_vp8.cc +++ b/remoting/codec/video_encoder_vp8.cc @@ -8,9 +8,10 @@ #include "base/sys_info.h" #include "base/time.h" #include "media/base/yuv_convert.h" -#include "media/video/capture/screen/screen_capture_data.h" #include "remoting/base/util.h" #include "remoting/proto/video.pb.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" extern "C" { #define VPX_CODEC_DISABLE_COMPAT 1 @@ -49,7 +50,7 @@ void VideoEncoderVp8::Destroy() { } } -bool VideoEncoderVp8::Init(const SkISize& size) { +bool VideoEncoderVp8::Init(const webrtc::DesktopSize& size) { Destroy(); codec_.reset(new vpx_codec_ctx_t()); image_.reset(new vpx_image_t()); @@ -146,11 +147,9 @@ bool VideoEncoderVp8::Init(const SkISize& size) { return true; } -void VideoEncoderVp8::PrepareImage( - scoped_refptr<media::ScreenCaptureData> capture_data, - SkRegion* updated_region) { - const SkRegion& region = capture_data->dirty_region(); - if (region.isEmpty()) { +void VideoEncoderVp8::PrepareImage(const webrtc::DesktopFrame* frame, + SkRegion* updated_region) { + if (frame->updated_region().is_empty()) { updated_region->setEmpty(); return; } @@ -159,8 +158,11 @@ void VideoEncoderVp8::PrepareImage( // This also ensures that all rectangles have even-aligned top-left, which // is required for ConvertRGBToYUVWithRect() to work. std::vector<SkIRect> aligned_rects; - for (SkRegion::Iterator r(region); !r.done(); r.next()) { - aligned_rects.push_back(AlignRect(r.rect())); + for (webrtc::DesktopRegion::Iterator r(frame->updated_region()); + !r.IsAtEnd(); r.Advance()) { + const webrtc::DesktopRect& rect = r.rect(); + aligned_rects.push_back(AlignRect( + SkIRect::MakeLTRB(rect.left(), rect.top(), rect.right(), rect.bottom()))); } DCHECK(!aligned_rects.empty()); updated_region->setRects(&aligned_rects[0], aligned_rects.size()); @@ -172,8 +174,8 @@ void VideoEncoderVp8::PrepareImage( SkRegion::kIntersect_Op); // Convert the updated region to YUV ready for encoding. - const uint8* rgb_data = capture_data->data(); - const int rgb_stride = capture_data->stride(); + const uint8* rgb_data = frame->data(); + const int rgb_stride = frame->stride(); const int y_stride = image_->stride[0]; DCHECK_EQ(image_->stride[1], image_->stride[2]); const int uv_stride = image_->stride[1]; @@ -213,17 +215,16 @@ void VideoEncoderVp8::PrepareActiveMap(const SkRegion& updated_region) { } void VideoEncoderVp8::Encode( - scoped_refptr<media::ScreenCaptureData> capture_data, - bool key_frame, + const webrtc::DesktopFrame* frame, const DataAvailableCallback& data_available_callback) { - DCHECK_LE(32, capture_data->size().width()); - DCHECK_LE(32, capture_data->size().height()); + DCHECK_LE(32, frame->size().width()); + DCHECK_LE(32, frame->size().height()); base::Time encode_start_time = base::Time::Now(); if (!initialized_ || - (capture_data->size() != SkISize::Make(image_->w, image_->h))) { - bool ret = Init(capture_data->size()); + !frame->size().equals(webrtc::DesktopSize(image_->w, image_->h))) { + bool ret = Init(frame->size()); // TODO(hclam): Handle error better. CHECK(ret) << "Initialization of encoder failed"; initialized_ = ret; @@ -231,7 +232,7 @@ void VideoEncoderVp8::Encode( // Convert the updated capture data ready for encode. SkRegion updated_region; - PrepareImage(capture_data, &updated_region); + PrepareImage(frame, &updated_region); // Update active map based on updated region. PrepareActiveMap(updated_region); @@ -286,17 +287,15 @@ void VideoEncoderVp8::Encode( packet->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8); packet->set_flags(VideoPacket::FIRST_PACKET | VideoPacket::LAST_PACKET | VideoPacket::LAST_PARTITION); - packet->mutable_format()->set_screen_width(capture_data->size().width()); - packet->mutable_format()->set_screen_height(capture_data->size().height()); - packet->set_capture_time_ms(capture_data->capture_time_ms()); + packet->mutable_format()->set_screen_width(frame->size().width()); + packet->mutable_format()->set_screen_height(frame->size().height()); + packet->set_capture_time_ms(frame->capture_time_ms()); packet->set_encode_time_ms( (base::Time::Now() - encode_start_time).InMillisecondsRoundedUp()); - packet->set_client_sequence_number(capture_data->client_sequence_number()); - SkIPoint dpi(capture_data->dpi()); - if (dpi.x()) - packet->mutable_format()->set_x_dpi(dpi.x()); - if (dpi.y()) - packet->mutable_format()->set_y_dpi(dpi.y()); + if (!frame->dpi().is_zero()) { + packet->mutable_format()->set_x_dpi(frame->dpi().x()); + packet->mutable_format()->set_y_dpi(frame->dpi().y()); + } for (SkRegion::Iterator r(updated_region); !r.done(); r.next()) { Rect* rect = packet->add_dirty_rects(); rect->set_x(r.rect().x()); diff --git a/remoting/codec/video_encoder_vp8.h b/remoting/codec/video_encoder_vp8.h index f26fad3..912c845 100644 --- a/remoting/codec/video_encoder_vp8.h +++ b/remoting/codec/video_encoder_vp8.h @@ -12,6 +12,10 @@ typedef struct vpx_codec_ctx vpx_codec_ctx_t; typedef struct vpx_image vpx_image_t; +namespace webrtc { +class DesktopSize; +} // namespace webrtc + namespace remoting { // A class that uses VP8 to perform encoding. @@ -20,23 +24,25 @@ class VideoEncoderVp8 : public VideoEncoder { VideoEncoderVp8(); virtual ~VideoEncoderVp8(); + // VideoEncoder interface. virtual void Encode( - scoped_refptr<media::ScreenCaptureData> capture_data, - bool key_frame, + const webrtc::DesktopFrame* frame, const DataAvailableCallback& data_available_callback) OVERRIDE; private: FRIEND_TEST_ALL_PREFIXES(VideoEncoderVp8Test, AlignAndClipRect); // Initialize the encoder. Returns true if successful. - bool Init(const SkISize& size); + bool Init(const webrtc::DesktopSize& size); // Destroy the encoder. void Destroy(); // Prepare |image_| for encoding. Write updated rectangles into // |updated_region|. - void PrepareImage(scoped_refptr<media::ScreenCaptureData> capture_data, + // + // TODO(sergeyu): Update this code to use webrtc::DesktopRegion. + void PrepareImage(const webrtc::DesktopFrame* frame, SkRegion* updated_region); // Update the active map according to |updated_region|. Active map is then diff --git a/remoting/codec/video_encoder_vp8_unittest.cc b/remoting/codec/video_encoder_vp8_unittest.cc index 9a32fe2..684910d 100644 --- a/remoting/codec/video_encoder_vp8_unittest.cc +++ b/remoting/codec/video_encoder_vp8_unittest.cc @@ -10,10 +10,10 @@ #include "base/bind.h" #include "base/callback.h" #include "base/memory/scoped_ptr.h" -#include "media/video/capture/screen/screen_capture_data.h" #include "remoting/codec/codec_test.h" #include "remoting/proto/video.pb.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" namespace { @@ -39,25 +39,21 @@ class VideoEncoderCallback { TEST(VideoEncoderVp8Test, TestSizeChangeNoLeak) { int height = 1000; int width = 1000; - const int kBytesPerPixel = 4; VideoEncoderVp8 encoder; VideoEncoderCallback callback; - std::vector<uint8> buffer(width * height * kBytesPerPixel); - scoped_refptr<media::ScreenCaptureData> capture_data( - new media::ScreenCaptureData(&buffer.front(), width * kBytesPerPixel, - SkISize::Make(width, height))); - encoder.Encode(capture_data, false, - base::Bind(&VideoEncoderCallback::DataAvailable, - base::Unretained(&callback))); + scoped_ptr<webrtc::DesktopFrame> frame(new webrtc::BasicDesktopFrame( + webrtc::DesktopSize(width, height))); + + encoder.Encode(frame.get(), base::Bind(&VideoEncoderCallback::DataAvailable, + base::Unretained(&callback))); height /= 2; - capture_data = new media::ScreenCaptureData( - &buffer.front(), width * kBytesPerPixel, SkISize::Make(width, height)); - encoder.Encode(capture_data, false, - base::Bind(&VideoEncoderCallback::DataAvailable, - base::Unretained(&callback))); + frame.reset(new webrtc::BasicDesktopFrame( + webrtc::DesktopSize(width, height))); + encoder.Encode(frame.get(), base::Bind(&VideoEncoderCallback::DataAvailable, + base::Unretained(&callback))); } class VideoEncoderDpiCallback { @@ -73,17 +69,14 @@ class VideoEncoderDpiCallback { TEST(VideoEncoderVp8Test, TestDpiPropagation) { int height = 32; int width = 32; - const int kBytesPerPixel = 4; VideoEncoderVp8 encoder; VideoEncoderDpiCallback callback; - std::vector<uint8> buffer(width * height * kBytesPerPixel); - scoped_refptr<media::ScreenCaptureData> capture_data( - new media::ScreenCaptureData(&buffer.front(), width * kBytesPerPixel, - SkISize::Make(width, height))); - capture_data->set_dpi(SkIPoint::Make(96, 97)); - encoder.Encode(capture_data, false, + scoped_ptr<webrtc::DesktopFrame> frame(new webrtc::BasicDesktopFrame( + webrtc::DesktopSize(width, height))); + frame->set_dpi(webrtc::DesktopVector(96, 97)); + encoder.Encode(frame.get(), base::Bind(&VideoEncoderDpiCallback::DataAvailable, base::Unretained(&callback))); } diff --git a/remoting/host/DEPS b/remoting/host/DEPS index dbdf6ac..cadbcd3 100644 --- a/remoting/host/DEPS +++ b/remoting/host/DEPS @@ -7,5 +7,6 @@ include_rules = [ "+third_party/jsoncpp", "+third_party/modp_b64", "+third_party/npapi", + "+third_party/webrtc", "+ui", ] diff --git a/remoting/host/chromoting_messages.h b/remoting/host/chromoting_messages.h index 0c640ea..7a032d0 100644 --- a/remoting/host/chromoting_messages.h +++ b/remoting/host/chromoting_messages.h @@ -8,11 +8,10 @@ #include "ipc/ipc_platform_file.h" #include "media/video/capture/screen/mouse_cursor_shape.h" #include "net/base/ip_endpoint.h" +#include "remoting/host/chromoting_param_traits.h" #include "remoting/host/screen_resolution.h" #include "remoting/protocol/transport.h" -#include "third_party/skia/include/core/SkPoint.h" -#include "third_party/skia/include/core/SkRect.h" -#include "third_party/skia/include/core/SkSize.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" #endif // REMOTING_HOST_CHROMOTING_MESSAGES_H_ @@ -64,11 +63,6 @@ IPC_MESSAGE_CONTROL3(ChromotingDaemonNetworkMsg_DesktopAttached, // console session. IPC_MESSAGE_CONTROL0(ChromotingNetworkDaemonMsg_SendSasToConsole) -IPC_STRUCT_TRAITS_BEGIN(remoting::ScreenResolution) - IPC_STRUCT_TRAITS_MEMBER(dimensions_) - IPC_STRUCT_TRAITS_MEMBER(dpi_) -IPC_STRUCT_TRAITS_END() - // Connects the terminal |terminal_id| (i.e. a remote client) to a desktop // session. IPC_MESSAGE_CONTROL3(ChromotingNetworkHostMsg_ConnectTerminal, @@ -138,9 +132,7 @@ IPC_MESSAGE_CONTROL0(ChromotingDesktopDaemonMsg_InjectSas) //----------------------------------------------------------------------------- // Chromoting messages sent from the desktop to the network process. -// Notifies the network process that a shared buffer has been created. Receipt -// of this message must be confirmed by replying with -// ChromotingNetworkDesktopMsg_SharedBufferCreated message. +// Notifies the network process that a shared buffer has been created. IPC_MESSAGE_CONTROL3(ChromotingDesktopNetworkMsg_CreateSharedBuffer, int /* id */, IPC::PlatformFileForTransit /* handle */, @@ -150,31 +142,14 @@ IPC_MESSAGE_CONTROL3(ChromotingDesktopNetworkMsg_CreateSharedBuffer, IPC_MESSAGE_CONTROL1(ChromotingDesktopNetworkMsg_ReleaseSharedBuffer, int /* id */) -IPC_STRUCT_TRAITS_BEGIN(SkIPoint) - IPC_STRUCT_TRAITS_MEMBER(fX) - IPC_STRUCT_TRAITS_MEMBER(fY) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(SkIRect) - IPC_STRUCT_TRAITS_MEMBER(fLeft) - IPC_STRUCT_TRAITS_MEMBER(fTop) - IPC_STRUCT_TRAITS_MEMBER(fRight) - IPC_STRUCT_TRAITS_MEMBER(fBottom) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(SkISize) - IPC_STRUCT_TRAITS_MEMBER(fWidth) - IPC_STRUCT_TRAITS_MEMBER(fHeight) -IPC_STRUCT_TRAITS_END() - IPC_STRUCT_TRAITS_BEGIN(media::MouseCursorShape) IPC_STRUCT_TRAITS_MEMBER(size) IPC_STRUCT_TRAITS_MEMBER(hotspot) IPC_STRUCT_TRAITS_MEMBER(data) IPC_STRUCT_TRAITS_END() -// Serialized media::ScreenCaptureData structure. -IPC_STRUCT_BEGIN(SerializedCapturedData) +// Serialized webrtc::DesktopFrame. +IPC_STRUCT_BEGIN(SerializedDesktopFrame) // ID of the shared memory buffer containing the pixels. IPC_STRUCT_MEMBER(int, shared_buffer_id) @@ -182,10 +157,10 @@ IPC_STRUCT_BEGIN(SerializedCapturedData) IPC_STRUCT_MEMBER(int, bytes_per_row) // Captured region. - IPC_STRUCT_MEMBER(std::vector<SkIRect>, dirty_region) + IPC_STRUCT_MEMBER(std::vector<webrtc::DesktopRect>, dirty_region) // Dimensions of the buffer in pixels. - IPC_STRUCT_MEMBER(SkISize, dimensions) + IPC_STRUCT_MEMBER(webrtc::DesktopSize, dimensions) // Time spent in capture. Unit is in milliseconds. IPC_STRUCT_MEMBER(int, capture_time_ms) @@ -194,12 +169,12 @@ IPC_STRUCT_BEGIN(SerializedCapturedData) IPC_STRUCT_MEMBER(int64, client_sequence_number) // DPI for this frame. - IPC_STRUCT_MEMBER(SkIPoint, dpi) + IPC_STRUCT_MEMBER(webrtc::DesktopVector, dpi) IPC_STRUCT_END() // Notifies the network process that a shared buffer has been created. IPC_MESSAGE_CONTROL1(ChromotingDesktopNetworkMsg_CaptureCompleted, - SerializedCapturedData /* capture_data */ ) + SerializedDesktopFrame /* frame */ ) // Carries a cursor share update from the desktop session agent to the client. IPC_MESSAGE_CONTROL1(ChromotingDesktopNetworkMsg_CursorShapeChanged, @@ -228,12 +203,6 @@ IPC_MESSAGE_CONTROL3(ChromotingNetworkDesktopMsg_StartSessionAgent, remoting::ScreenResolution /* resolution */, bool /* virtual_terminal */) -// Notifies the desktop process that the shared memory buffer has been mapped to -// the memory of the network process and so it can be safely dropped by -// the network process at any time. -IPC_MESSAGE_CONTROL1(ChromotingNetworkDesktopMsg_SharedBufferCreated, - int /* id */) - IPC_MESSAGE_CONTROL0(ChromotingNetworkDesktopMsg_CaptureFrame) // Carries a clipboard event from the client to the desktop session agent. diff --git a/remoting/host/chromoting_param_traits.cc b/remoting/host/chromoting_param_traits.cc new file mode 100644 index 0000000..d1c794c --- /dev/null +++ b/remoting/host/chromoting_param_traits.cc @@ -0,0 +1,127 @@ +// Copyright 2013 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 "remoting/host/chromoting_param_traits.h" + +#include "base/stringprintf.h" + +namespace IPC { + +// static +void ParamTraits<webrtc::DesktopVector>::Write(Message* m, + const webrtc::DesktopVector& p) { + m->WriteInt(p.x()); + m->WriteInt(p.y()); +} + +// static +bool ParamTraits<webrtc::DesktopVector>::Read(const Message* m, + PickleIterator* iter, + webrtc::DesktopVector* r) { + int x, y; + if (!m->ReadInt(iter, &x) || !m->ReadInt(iter, &y)) + return false; + *r = webrtc::DesktopVector(x, y); + return true; +} + +// static +void ParamTraits<webrtc::DesktopVector>::Log(const webrtc::DesktopVector& p, + std::string* l) { + l->append(base::StringPrintf("webrtc::DesktopVector(%d, %d)", + p.x(), p.y())); +} + +// static +void ParamTraits<webrtc::DesktopSize>::Write(Message* m, + const webrtc::DesktopSize& p) { + m->WriteInt(p.width()); + m->WriteInt(p.height()); +} + +// static +bool ParamTraits<webrtc::DesktopSize>::Read(const Message* m, + PickleIterator* iter, + webrtc::DesktopSize* r) { + int width, height; + if (!m->ReadInt(iter, &width) || !m->ReadInt(iter, &height)) + return false; + *r = webrtc::DesktopSize(width, height); + return true; +} + +// static +void ParamTraits<webrtc::DesktopSize>::Log(const webrtc::DesktopSize& p, + std::string* l) { + l->append(base::StringPrintf("webrtc::DesktopSize(%d, %d)", + p.width(), p.height())); +} + +// static +void ParamTraits<webrtc::DesktopRect>::Write(Message* m, + const webrtc::DesktopRect& p) { + m->WriteInt(p.left()); + m->WriteInt(p.top()); + m->WriteInt(p.right()); + m->WriteInt(p.bottom()); +} + +// static +bool ParamTraits<webrtc::DesktopRect>::Read(const Message* m, + PickleIterator* iter, + webrtc::DesktopRect* r) { + int left, right, top, bottom; + if (!m->ReadInt(iter, &left) || !m->ReadInt(iter, &top) || + !m->ReadInt(iter, &right) || !m->ReadInt(iter, &bottom)) { + return false; + } + *r = webrtc::DesktopRect::MakeLTRB(left, top, right, bottom); + return true; +} + +// static +void ParamTraits<webrtc::DesktopRect>::Log(const webrtc::DesktopRect& p, + std::string* l) { + l->append(base::StringPrintf("webrtc::DesktopRect(%d, %d, %d, %d)", + p.left(), p.top(), p.right(), p.bottom())); +} + +// static +void ParamTraits<remoting::ScreenResolution>::Write( + Message* m, + const remoting::ScreenResolution& p) { + ParamTraits<webrtc::DesktopSize>::Write(m, p.dimensions()); + ParamTraits<webrtc::DesktopVector>::Write(m, p.dpi()); +} + +// static +bool ParamTraits<remoting::ScreenResolution>::Read( + const Message* m, + PickleIterator* iter, + remoting::ScreenResolution* r) { + webrtc::DesktopSize size; + webrtc::DesktopVector dpi; + if (!ParamTraits<webrtc::DesktopSize>::Read(m, iter, &size) || + !ParamTraits<webrtc::DesktopVector>::Read(m, iter, &dpi)) { + return false; + } + if (size.width() < 0 || size.height() < 0 || + dpi.x() < 0 || dpi.y() < 0) { + return false; + } + *r = remoting::ScreenResolution(size, dpi); + return true; +} + +// static +void ParamTraits<remoting::ScreenResolution>::Log( + const remoting::ScreenResolution& p, + std::string* l) { + l->append(base::StringPrintf("webrtc::ScreenResolution(%d, %d, %d, %d)", + p.dimensions().width(), p.dimensions().height(), + p.dpi().x(), p.dpi().y())); +} + +} // namespace IPC + diff --git a/remoting/host/chromoting_param_traits.h b/remoting/host/chromoting_param_traits.h new file mode 100644 index 0000000..7925918 --- /dev/null +++ b/remoting/host/chromoting_param_traits.h @@ -0,0 +1,49 @@ +// Copyright 2013 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. + +#ifndef REMOTING_HOST_CHROMOTING_PARAM_TRAITS_H_ +#define REMOTING_HOST_CHROMOTING_PARAM_TRAITS_H_ + +#include "ipc/ipc_message.h" +#include "ipc/ipc_param_traits.h" +#include "remoting/host/screen_resolution.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" + +namespace IPC { + +template <> +struct ParamTraits<webrtc::DesktopVector> { + typedef webrtc::DesktopVector param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, PickleIterator* iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> +struct ParamTraits<webrtc::DesktopSize> { + typedef webrtc::DesktopSize param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, PickleIterator* iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> +struct ParamTraits<webrtc::DesktopRect> { + typedef webrtc::DesktopRect param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, PickleIterator* iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> +struct ParamTraits<remoting::ScreenResolution> { + typedef remoting::ScreenResolution param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, PickleIterator* iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +} // namespace IPC + +#endif // REMOTING_HOST_CHROMOTING_PARAM_TRAITS_H_ diff --git a/remoting/host/client_session.cc b/remoting/host/client_session.cc index 8636b7a..38ec520 100644 --- a/remoting/host/client_session.cc +++ b/remoting/host/client_session.cc @@ -99,8 +99,13 @@ void ClientSession::NotifyClientResolution( const protocol::ClientResolution& resolution) { DCHECK(CalledOnValidThread()); - if (!resolution.has_dips_width() || !resolution.has_dips_height()) + // TODO(sergeyu): Move these checks to protocol layer. + if (!resolution.has_dips_width() || !resolution.has_dips_height() || + resolution.dips_width() < 0 || resolution.dips_height() < 0 || + resolution.width() <= 0 || resolution.height() <= 0) { + LOG(ERROR) << "Received invalid ClientResolution message."; return; + } VLOG(1) << "Received ClientResolution (dips_width=" << resolution.dips_width() << ", dips_height=" @@ -110,12 +115,11 @@ void ClientSession::NotifyClientResolution( return; ScreenResolution client_resolution( - SkISize::Make(resolution.dips_width(), resolution.dips_height()), - SkIPoint::Make(kDefaultDPI, kDefaultDPI)); + webrtc::DesktopSize(resolution.dips_width(), resolution.dips_height()), + webrtc::DesktopVector(kDefaultDPI, kDefaultDPI)); // Try to match the client's resolution. - if (client_resolution.IsValid()) - screen_controls_->SetScreenResolution(client_resolution); + screen_controls_->SetScreenResolution(client_resolution); } void ClientSession::ControlVideo(const protocol::VideoControl& video_control) { diff --git a/remoting/host/client_session_unittest.cc b/remoting/host/client_session_unittest.cc index 3d92da7e..314c716 100644 --- a/remoting/host/client_session_unittest.cc +++ b/remoting/host/client_session_unittest.cc @@ -13,6 +13,7 @@ #include "remoting/host/host_mock_objects.h" #include "remoting/protocol/protocol_mock_objects.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_region.h" namespace remoting { diff --git a/remoting/host/daemon_process.cc b/remoting/host/daemon_process.cc index faf5e60..b7a0336 100644 --- a/remoting/host/daemon_process.cc +++ b/remoting/host/daemon_process.cc @@ -29,9 +29,9 @@ namespace { const char kApplicationName[] = "chromoting"; std::ostream& operator<<(std::ostream& os, const ScreenResolution& resolution) { - return os << resolution.dimensions_.width() << "x" - << resolution.dimensions_.height() << " at " - << resolution.dpi_.x() << "x" << resolution.dpi_.y() << " DPI"; + return os << resolution.dimensions().width() << "x" + << resolution.dimensions().height() << " at " + << resolution.dpi().x() << "x" << resolution.dpi().y() << " DPI"; } } // namespace @@ -188,13 +188,6 @@ void DaemonProcess::CreateDesktopSession(int terminal_id, // Terminal IDs cannot be reused. Update the expected next terminal ID. next_terminal_id_ = std::max(next_terminal_id_, terminal_id + 1); - // Validate |resolution| and restart the sender if it is not valid. - if (!resolution.IsValid()) { - LOG(ERROR) << "Invalid resolution specified: " << resolution; - CrashNetworkProcess(FROM_HERE); - return; - } - // Create the desktop session. scoped_ptr<DesktopSession> session = DoCreateDesktopSession( terminal_id, resolution, virtual_terminal); @@ -223,7 +216,7 @@ void DaemonProcess::SetScreenResolution(int terminal_id, } // Validate |resolution| and restart the sender if it is not valid. - if (!resolution.IsValid()) { + if (resolution.IsEmpty()) { LOG(ERROR) << "Invalid resolution specified: " << resolution; CrashNetworkProcess(FROM_HERE); return; diff --git a/remoting/host/desktop_session_agent.cc b/remoting/host/desktop_session_agent.cc index 6dd811c..025f6e9 100644 --- a/remoting/host/desktop_session_agent.cc +++ b/remoting/host/desktop_session_agent.cc @@ -6,10 +6,10 @@ #include "base/file_util.h" #include "base/logging.h" +#include "base/memory/shared_memory.h" #include "ipc/ipc_channel_proxy.h" #include "ipc/ipc_message.h" #include "ipc/ipc_message_macros.h" -#include "media/video/capture/screen/screen_capture_data.h" #include "remoting/base/auto_thread_task_runner.h" #include "remoting/base/constants.h" #include "remoting/host/audio_capturer.h" @@ -24,7 +24,8 @@ #include "remoting/proto/event.pb.h" #include "remoting/protocol/clipboard_stub.h" #include "remoting/protocol/input_event_tracker.h" -#include "third_party/skia/include/core/SkRegion.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" +#include "third_party/webrtc/modules/desktop_capture/shared_memory.h" namespace remoting { @@ -62,6 +63,46 @@ void DesktopSesssionClipboardStub::InjectClipboardEvent( } // namespace +// webrtc::SharedMemory implementation that notifies creating +// DesktopSessionAgent when it's deleted. +class DesktopSessionAgent::SharedBuffer : public webrtc::SharedMemory { + public: + static scoped_ptr<SharedBuffer> Create(DesktopSessionAgent* agent, + size_t size, + int id) { + scoped_ptr<base::SharedMemory> memory(new base::SharedMemory()); + if (!memory->CreateAndMapAnonymous(size)) + return scoped_ptr<SharedBuffer>(); + return scoped_ptr<SharedBuffer>( + new SharedBuffer(agent, memory.Pass(), size, id)); + } + + virtual ~SharedBuffer() { + agent_->OnSharedBufferDeleted(id()); + } + + private: + SharedBuffer(DesktopSessionAgent* agent, + scoped_ptr<base::SharedMemory> memory, + size_t size, + int id) + : SharedMemory(memory->memory(), size, +#if defined(OS_WIN) + memory->handle(), +#else + memory->handle().fd, +#endif + id), + agent_(agent), + shared_memory_(memory.Pass()) { + } + + DesktopSessionAgent* agent_; + scoped_ptr<base::SharedMemory> shared_memory_; + + DISALLOW_COPY_AND_ASSIGN(SharedBuffer); +}; + DesktopSessionAgent::Delegate::~Delegate() { } @@ -83,8 +124,6 @@ bool DesktopSessionAgent::OnMessageReceived(const IPC::Message& message) { IPC_BEGIN_MESSAGE_MAP(DesktopSessionAgent, message) IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_CaptureFrame, OnCaptureFrame) - IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_SharedBufferCreated, - OnSharedBufferCreated) IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectClipboardEvent, OnInjectClipboardEvent) IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectKeyEvent, @@ -127,14 +166,13 @@ void DesktopSessionAgent::OnChannelError() { delegate_->OnNetworkProcessDisconnected(); } -scoped_refptr<media::SharedBuffer> DesktopSessionAgent::CreateSharedBuffer( - uint32 size) { +webrtc::SharedMemory* DesktopSessionAgent::CreateSharedMemory(size_t size) { DCHECK(video_capture_task_runner()->BelongsToCurrentThread()); - scoped_refptr<media::SharedBuffer> buffer = new media::SharedBuffer(size); - if (buffer->ptr() != NULL) { - buffer->set_id(next_shared_buffer_id_); - shared_buffers_.push_back(buffer); + scoped_ptr<SharedBuffer> buffer = + SharedBuffer::Create(this, size, next_shared_buffer_id_); + if (buffer) { + shared_buffers_++; // |next_shared_buffer_id_| starts from 1 and incrementing it by 2 makes // sure it is always odd and therefore zero is never used as a valid buffer @@ -146,20 +184,17 @@ scoped_refptr<media::SharedBuffer> DesktopSessionAgent::CreateSharedBuffer( // speaking it never happens. next_shared_buffer_id_ += 2; + IPC::PlatformFileForTransit handle; +#if defined(OS_WIN) + handle = buffer->handle(); +#else + handle = base::FileDescriptor(buffer->handle(), false); +#endif SendToNetwork(new ChromotingDesktopNetworkMsg_CreateSharedBuffer( - buffer->id(), buffer->handle(), buffer->size())); + buffer->id(), handle, buffer->size())); } - return buffer; -} - -void DesktopSessionAgent::ReleaseSharedBuffer( - scoped_refptr<media::SharedBuffer> buffer) { - DCHECK(video_capture_task_runner()->BelongsToCurrentThread()); - DCHECK(buffer->id() != 0); - - SendToNetwork( - new ChromotingDesktopNetworkMsg_ReleaseSharedBuffer(buffer->id())); + return buffer.release(); } const std::string& DesktopSessionAgent::client_jid() const { @@ -241,26 +276,27 @@ void DesktopSessionAgent::OnStartSessionAgent( FROM_HERE, base::Bind(&DesktopSessionAgent::StartVideoCapturer, this)); } -void DesktopSessionAgent::OnCaptureCompleted( - scoped_refptr<media::ScreenCaptureData> capture_data) { +void DesktopSessionAgent::OnCaptureCompleted(webrtc::DesktopFrame* frame) { DCHECK(video_capture_task_runner()->BelongsToCurrentThread()); - current_size_ = capture_data->size(); + last_frame_.reset(frame); + + current_size_ = frame->size(); - // Serialize media::ScreenCaptureData - SerializedCapturedData serialized_data; - serialized_data.shared_buffer_id = capture_data->shared_buffer()->id(); - serialized_data.bytes_per_row = capture_data->stride(); - serialized_data.dimensions = capture_data->size(); - serialized_data.capture_time_ms = capture_data->capture_time_ms(); - serialized_data.client_sequence_number = - capture_data->client_sequence_number(); - serialized_data.dpi = capture_data->dpi(); - for (SkRegion::Iterator i(capture_data->dirty_region()); !i.done(); i.next()) - serialized_data.dirty_region.push_back(i.rect()); + // Serialize webrtc::DesktopFrame. + SerializedDesktopFrame serialized_frame; + serialized_frame.shared_buffer_id = frame->shared_memory()->id(); + serialized_frame.bytes_per_row = frame->stride(); + serialized_frame.dimensions = frame->size(); + serialized_frame.capture_time_ms = frame->capture_time_ms(); + serialized_frame.dpi = frame->dpi(); + for (webrtc::DesktopRegion::Iterator i(frame->updated_region()); + !i.IsAtEnd(); i.Advance()) { + serialized_frame.dirty_region.push_back(i.rect()); + } SendToNetwork( - new ChromotingDesktopNetworkMsg_CaptureCompleted(serialized_data)); + new ChromotingDesktopNetworkMsg_CaptureCompleted(serialized_frame)); } void DesktopSessionAgent::OnCursorShapeChanged( @@ -359,25 +395,7 @@ void DesktopSessionAgent::OnCaptureFrame() { // runner. If the client issues more requests, pixel data in captured frames // will likely be corrupted but stability of media::ScreenCapturer will not be // affected. - video_capturer_->CaptureFrame(); -} - -void DesktopSessionAgent::OnSharedBufferCreated(int id) { - if (!video_capture_task_runner()->BelongsToCurrentThread()) { - video_capture_task_runner()->PostTask( - FROM_HERE, - base::Bind(&DesktopSessionAgent::OnSharedBufferCreated, this, id)); - return; - } - - // Drop the cached reference to the buffer. - SharedBuffers::iterator i = shared_buffers_.begin(); - for (; i != shared_buffers_.end(); ++i) { - if ((*i)->id() == id) { - shared_buffers_.erase(i); - break; - } - } + video_capturer_->Capture(webrtc::DesktopRegion()); } void DesktopSessionAgent::OnInjectClipboardEvent( @@ -434,7 +452,7 @@ void DesktopSessionAgent::SetScreenResolution( const ScreenResolution& resolution) { DCHECK(caller_task_runner()->BelongsToCurrentThread()); - if (screen_controls_ && resolution.IsValid()) + if (screen_controls_ && resolution.IsEmpty()) screen_controls_->SetScreenResolution(resolution); } @@ -471,17 +489,20 @@ void DesktopSessionAgent::StopAudioCapturer() { void DesktopSessionAgent::StartVideoCapturer() { DCHECK(video_capture_task_runner()->BelongsToCurrentThread()); - if (video_capturer_) + if (video_capturer_) { + video_capturer_->SetMouseShapeObserver(this); video_capturer_->Start(this); + } } void DesktopSessionAgent::StopVideoCapturer() { DCHECK(video_capture_task_runner()->BelongsToCurrentThread()); video_capturer_.reset(); + last_frame_.reset(); - // Free any shared buffers left. - shared_buffers_.clear(); + // Video capturer must delete all buffers. + DCHECK_EQ(shared_buffers_, 0); } DesktopSessionAgent::DesktopSessionAgent( @@ -497,12 +518,21 @@ DesktopSessionAgent::DesktopSessionAgent( video_capture_task_runner_(video_capture_task_runner), control_factory_(this), desktop_pipe_(IPC::InvalidPlatformFileForTransit()), - current_size_(SkISize::Make(0, 0)), next_shared_buffer_id_(1), + shared_buffers_(0), started_(false) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); } +void DesktopSessionAgent::OnSharedBufferDeleted(int id) { + DCHECK(video_capture_task_runner()->BelongsToCurrentThread()); + DCHECK(id != 0); + + shared_buffers_--; + DCHECK_GE(shared_buffers_, 0); + SendToNetwork(new ChromotingDesktopNetworkMsg_ReleaseSharedBuffer(id)); +} + void DesktopSessionAgent::CloseDesktopPipeHandle() { if (!(desktop_pipe_ == IPC::InvalidPlatformFileForTransit())) { #if defined(OS_WIN) diff --git a/remoting/host/desktop_session_agent.h b/remoting/host/desktop_session_agent.h index a993520..91320ab 100644 --- a/remoting/host/desktop_session_agent.h +++ b/remoting/host/desktop_session_agent.h @@ -5,7 +5,7 @@ #ifndef REMOTING_HOST_DESKTOP_SESSION_AGENT_H_ #define REMOTING_HOST_DESKTOP_SESSION_AGENT_H_ -#include <list> +#include <map> #include "base/basictypes.h" #include "base/callback.h" @@ -16,11 +16,9 @@ #include "ipc/ipc_listener.h" #include "ipc/ipc_platform_file.h" #include "media/video/capture/screen/screen_capturer.h" -#include "media/video/capture/screen/shared_buffer.h" #include "remoting/host/client_session_control.h" #include "remoting/protocol/clipboard_stub.h" -#include "third_party/skia/include/core/SkRect.h" -#include "third_party/skia/include/core/SkSize.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" namespace IPC { class ChannelProxy; @@ -48,7 +46,8 @@ class InputEventTracker; class DesktopSessionAgent : public base::RefCountedThreadSafe<DesktopSessionAgent>, public IPC::Listener, - public media::ScreenCapturer::Delegate, + public webrtc::DesktopCapturer::Callback, + public media::ScreenCapturer::MouseShapeObserver, public ClientSessionControl { public: class Delegate { @@ -75,13 +74,11 @@ class DesktopSessionAgent virtual void OnChannelConnected(int32 peer_pid) OVERRIDE; virtual void OnChannelError() OVERRIDE; - // media::ScreenCapturer::Delegate implementation. - virtual scoped_refptr<media::SharedBuffer> CreateSharedBuffer( - uint32 size) OVERRIDE; - virtual void ReleaseSharedBuffer( - scoped_refptr<media::SharedBuffer> buffer) OVERRIDE; - virtual void OnCaptureCompleted( - scoped_refptr<media::ScreenCaptureData> capture_data) OVERRIDE; + // webrtc::DesktopCapturer::Callback implementation. + virtual webrtc::SharedMemory* CreateSharedMemory(size_t size) OVERRIDE; + virtual void OnCaptureCompleted(webrtc::DesktopFrame* frame) OVERRIDE; + + // media::ScreenCapturer::MouseShapeObserver implementation. virtual void OnCursorShapeChanged( scoped_ptr<media::MouseCursorShape> cursor_shape) OVERRIDE; @@ -102,14 +99,14 @@ class DesktopSessionAgent void Stop(); protected: + friend class base::RefCountedThreadSafe<DesktopSessionAgent>; + DesktopSessionAgent( scoped_refptr<AutoThreadTaskRunner> audio_capture_task_runner, scoped_refptr<AutoThreadTaskRunner> caller_task_runner, scoped_refptr<AutoThreadTaskRunner> input_task_runner, scoped_refptr<AutoThreadTaskRunner> io_task_runner, scoped_refptr<AutoThreadTaskRunner> video_capture_task_runner); - - friend class base::RefCountedThreadSafe<DesktopSessionAgent>; virtual ~DesktopSessionAgent(); // ClientSessionControl interface. @@ -186,6 +183,12 @@ class DesktopSessionAgent } private: + class SharedBuffer; + friend class SharedBuffer; + + // Called by SharedBuffer when it's destroyed. + void OnSharedBufferDeleted(int id); + // Closes |desktop_pipe_| if it is open. void CloseDesktopPipeHandle(); @@ -237,14 +240,13 @@ class DesktopSessionAgent IPC::PlatformFileForTransit desktop_pipe_; // Size of the most recent captured video frame. - SkISize current_size_; + webrtc::DesktopSize current_size_; // Next shared buffer ID to be used. int next_shared_buffer_id_; - // List of the shared buffers. - typedef std::list<scoped_refptr<media::SharedBuffer> > SharedBuffers; - SharedBuffers shared_buffers_; + // The number of currently allocated shared buffers. + int shared_buffers_; // True if the desktop session agent has been started. bool started_; @@ -252,6 +254,10 @@ class DesktopSessionAgent // Captures the screen. scoped_ptr<media::ScreenCapturer> video_capturer_; + // Keep reference to the last frame sent to make sure shared buffer is alive + // before it's received. + scoped_ptr<webrtc::DesktopFrame> last_frame_; + DISALLOW_COPY_AND_ASSIGN(DesktopSessionAgent); }; diff --git a/remoting/host/desktop_session_proxy.cc b/remoting/host/desktop_session_proxy.cc index 63fcbd0..27e6c41 100644 --- a/remoting/host/desktop_session_proxy.cc +++ b/remoting/host/desktop_session_proxy.cc @@ -8,10 +8,10 @@ #include "base/logging.h" #include "base/platform_file.h" #include "base/process_util.h" +#include "base/memory/shared_memory.h" #include "base/single_thread_task_runner.h" #include "ipc/ipc_channel_proxy.h" #include "ipc/ipc_message_macros.h" -#include "media/video/capture/screen/screen_capture_data.h" #include "remoting/base/capabilities.h" #include "remoting/host/chromoting_messages.h" #include "remoting/host/client_session.h" @@ -24,15 +24,80 @@ #include "remoting/proto/audio.pb.h" #include "remoting/proto/control.pb.h" #include "remoting/proto/event.pb.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" +#include "third_party/webrtc/modules/desktop_capture/shared_memory.h" #if defined(OS_WIN) #include "base/win/scoped_handle.h" #endif // defined(OS_WIN) +const bool kReadOnly = true; const char kSendInitialResolution[] = "sendInitialResolution"; namespace remoting { +class DesktopSessionProxy::IpcSharedBufferCore + : public base::RefCountedThreadSafe<IpcSharedBufferCore> { + public: + IpcSharedBufferCore(int id, + base::SharedMemoryHandle handle, + base::ProcessHandle process, + size_t size) + : id_(id), +#if defined(OS_WIN) + shared_memory_(handle, kReadOnly, process), +#else // !defined(OS_WIN) + shared_memory_(handle, kReadOnly), +#endif // !defined(OS_WIN) + size_(size) { + if (!shared_memory_.Map(size)) { + LOG(ERROR) << "Failed to map a shared buffer: id=" << id +#if defined(OS_WIN) + << ", handle=" << handle +#else + << ", handle.fd=" << handle.fd +#endif + << ", size=" << size; + } + } + + int id() { return id_; } + size_t size() { return size_; } + void* memory() { return shared_memory_.memory(); } + webrtc::SharedMemory::Handle handle() { +#if defined(OS_WIN) + return shared_memory_.handle(); +#else + return shared_memory_.handle().fd; +#endif + } + + private: + virtual ~IpcSharedBufferCore() {} + friend class base::RefCountedThreadSafe<IpcSharedBufferCore>; + + int id_; + base::SharedMemory shared_memory_; + size_t size_; + + DISALLOW_COPY_AND_ASSIGN(IpcSharedBufferCore); +}; + +class DesktopSessionProxy::IpcSharedBuffer : public webrtc::SharedMemory { + public: + IpcSharedBuffer(scoped_refptr<IpcSharedBufferCore> core) + : SharedMemory(core->memory(), core->size(), + core->handle(), core->id()), + core_(core) { + } + + private: + scoped_refptr<IpcSharedBufferCore> core_; + + DISALLOW_COPY_AND_ASSIGN(IpcSharedBuffer); +}; + DesktopSessionProxy::DesktopSessionProxy( scoped_refptr<base::SingleThreadTaskRunner> audio_capture_task_runner, scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner, @@ -215,7 +280,7 @@ void DesktopSessionProxy::DetachFromDesktop() { // Generate fake responses to keep the video capturer in sync. while (pending_capture_frame_requests_) { --pending_capture_frame_requests_; - PostCaptureCompleted(scoped_refptr<media::ScreenCaptureData>()); + PostCaptureCompleted(scoped_ptr<webrtc::DesktopFrame>()); } } @@ -237,7 +302,7 @@ void DesktopSessionProxy::CaptureFrame() { ++pending_capture_frame_requests_; SendToDesktop(new ChromotingNetworkDesktopMsg_CaptureFrame()); } else { - PostCaptureCompleted(scoped_refptr<media::ScreenCaptureData>()); + PostCaptureCompleted(scoped_ptr<webrtc::DesktopFrame>()); } } @@ -307,7 +372,7 @@ void DesktopSessionProxy::SetScreenResolution( const ScreenResolution& resolution) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); - if (!resolution.IsValid()) + if (!resolution.IsEmpty()) return; screen_resolution_ = resolution; @@ -344,8 +409,8 @@ DesktopSessionProxy::~DesktopSessionProxy() { } } -scoped_refptr<media::SharedBuffer> DesktopSessionProxy::GetSharedBuffer( - int id) { +scoped_refptr<DesktopSessionProxy::IpcSharedBufferCore> +DesktopSessionProxy::GetSharedBufferCore(int id) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); SharedBuffers::const_iterator i = shared_buffers_.find(id); @@ -353,7 +418,7 @@ scoped_refptr<media::SharedBuffer> DesktopSessionProxy::GetSharedBuffer( return i->second; } else { LOG(ERROR) << "Failed to find the shared buffer " << id; - return scoped_refptr<media::SharedBuffer>(); + return NULL; } } @@ -380,38 +445,13 @@ void DesktopSessionProxy::OnCreateSharedBuffer( uint32 size) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); - scoped_refptr<media::SharedBuffer> shared_buffer; + scoped_refptr<IpcSharedBufferCore> shared_buffer = + new IpcSharedBufferCore(id, handle, desktop_process_, size); -#if defined(OS_WIN) - shared_buffer = new media::SharedBuffer(id, handle, desktop_process_, size); -#elif defined(OS_POSIX) - shared_buffer = new media::SharedBuffer(id, handle, size); -#else -#error Unsupported platform. -#endif - - // Check if the buffer has been successfully mapped. - bool mapped = shared_buffer->ptr() != NULL; - if (!mapped) { -#if defined(OS_WIN) - LOG(ERROR) << "Failed to map a shared buffer: id=" << id - << ", handle=" << handle - << ", size=" << size; -#elif defined(OS_POSIX) - LOG(ERROR) << "Failed to map a shared buffer: id=" << id - << ", handle.fd=" << handle.fd - << ", size=" << size; -#endif - } - - if (mapped && + if (shared_buffer->memory() != NULL && !shared_buffers_.insert(std::make_pair(id, shared_buffer)).second) { LOG(ERROR) << "Duplicate shared buffer id " << id << " encountered"; } - - // Notify the desktop process that the buffer has been seen and can now be - // safely deleted if needed. - SendToDesktop(new ChromotingNetworkDesktopMsg_SharedBufferCreated(id)); } void DesktopSessionProxy::OnReleaseSharedBuffer(int id) { @@ -422,34 +462,28 @@ void DesktopSessionProxy::OnReleaseSharedBuffer(int id) { } void DesktopSessionProxy::OnCaptureCompleted( - const SerializedCapturedData& serialized_data) { + const SerializedDesktopFrame& serialized_frame) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); - // Assume that |serialized_data| is well formed because it was received from + // Assume that |serialized_frame| is well-formed because it was received from // a more privileged process. - scoped_refptr<media::ScreenCaptureData> capture_data; - scoped_refptr<media::SharedBuffer> shared_buffer = - GetSharedBuffer(serialized_data.shared_buffer_id); - CHECK(shared_buffer); - - capture_data = new media::ScreenCaptureData( - reinterpret_cast<uint8*>(shared_buffer->ptr()), - serialized_data.bytes_per_row, - serialized_data.dimensions); - capture_data->set_capture_time_ms(serialized_data.capture_time_ms); - capture_data->set_client_sequence_number( - serialized_data.client_sequence_number); - capture_data->set_dpi(serialized_data.dpi); - capture_data->set_shared_buffer(shared_buffer); - - if (!serialized_data.dirty_region.empty()) { - capture_data->mutable_dirty_region().setRects( - &serialized_data.dirty_region[0], - serialized_data.dirty_region.size()); + scoped_refptr<IpcSharedBufferCore> shared_buffer_core = + GetSharedBufferCore(serialized_frame.shared_buffer_id); + CHECK(shared_buffer_core); + + scoped_ptr<webrtc::DesktopFrame> frame( + new webrtc::SharedMemoryDesktopFrame( + serialized_frame.dimensions, serialized_frame.bytes_per_row, + new IpcSharedBuffer(shared_buffer_core))); + frame->set_capture_time_ms(serialized_frame.capture_time_ms); + frame->set_dpi(serialized_frame.dpi); + + for (size_t i = 0; i < serialized_frame.dirty_region.size(); ++i) { + frame->mutable_updated_region()->AddRect(serialized_frame.dirty_region[i]); } --pending_capture_frame_requests_; - PostCaptureCompleted(capture_data); + PostCaptureCompleted(frame.Pass()); } void DesktopSessionProxy::OnCursorShapeChanged( @@ -475,13 +509,13 @@ void DesktopSessionProxy::OnInjectClipboardEvent( } void DesktopSessionProxy::PostCaptureCompleted( - scoped_refptr<media::ScreenCaptureData> capture_data) { + scoped_ptr<webrtc::DesktopFrame> frame) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); video_capture_task_runner_->PostTask( FROM_HERE, base::Bind(&IpcVideoFrameCapturer::OnCaptureCompleted, video_capturer_, - capture_data)); + base::Passed(&frame))); } void DesktopSessionProxy::PostCursorShape( diff --git a/remoting/host/desktop_session_proxy.h b/remoting/host/desktop_session_proxy.h index 34eac55..198bd6f 100644 --- a/remoting/host/desktop_session_proxy.h +++ b/remoting/host/desktop_session_proxy.h @@ -16,7 +16,6 @@ #include "ipc/ipc_listener.h" #include "ipc/ipc_platform_file.h" #include "media/video/capture/screen/screen_capturer.h" -#include "media/video/capture/screen/shared_buffer.h" #include "remoting/host/audio_capturer.h" #include "remoting/host/desktop_environment.h" #include "remoting/host/screen_resolution.h" @@ -33,7 +32,7 @@ class ChannelProxy; class Message; } // namespace IPC -struct SerializedCapturedData; +struct SerializedDesktopFrame; namespace remoting { @@ -124,10 +123,15 @@ class DesktopSessionProxy private: friend class base::DeleteHelper<DesktopSessionProxy>; friend struct DesktopSessionProxyTraits; + + class IpcSharedBufferCore; + class IpcSharedBuffer; + typedef std::map<int, scoped_refptr<IpcSharedBufferCore> > SharedBuffers; + virtual ~DesktopSessionProxy(); // Returns a shared buffer from the list of known buffers. - scoped_refptr<media::SharedBuffer> GetSharedBuffer(int id); + scoped_refptr<IpcSharedBufferCore> GetSharedBufferCore(int id); // Handles AudioPacket notification from the desktop session agent. void OnAudioPacket(const std::string& serialized_packet); @@ -141,7 +145,7 @@ class DesktopSessionProxy void OnReleaseSharedBuffer(int id); // Handles CaptureCompleted notification from the desktop session agent. - void OnCaptureCompleted(const SerializedCapturedData& serialized_data); + void OnCaptureCompleted(const SerializedDesktopFrame& serialized_frame); // Handles CursorShapeChanged notification from the desktop session agent. void OnCursorShapeChanged(const media::MouseCursorShape& cursor_shape); @@ -150,9 +154,8 @@ class DesktopSessionProxy void OnInjectClipboardEvent(const std::string& serialized_event); // Posts OnCaptureCompleted() to |video_capturer_| on the video thread, - // passing |capture_data|. - void PostCaptureCompleted( - scoped_refptr<media::ScreenCaptureData> capture_data); + // passing |frame|. + void PostCaptureCompleted(scoped_ptr<webrtc::DesktopFrame> frame); // Posts OnCursorShapeChanged() to |video_capturer_| on the video thread, // passing |cursor_shape|. @@ -197,7 +200,8 @@ class DesktopSessionProxy int pending_capture_frame_requests_; - typedef std::map<int, scoped_refptr<media::SharedBuffer> > SharedBuffers; + // Shared memory buffers by Id. Each buffer is owned by the corresponding + // frame. SharedBuffers shared_buffers_; // Keeps the desired screen resolution so it can be passed to a newly attached diff --git a/remoting/host/desktop_session_win.cc b/remoting/host/desktop_session_win.cc index f263e82..36d7fb1 100644 --- a/remoting/host/desktop_session_win.cc +++ b/remoting/host/desktop_session_win.cc @@ -226,26 +226,22 @@ bool RdpSession::Initialize(const ScreenResolution& resolution) { return false; } - // DaemonProcess::CreateDesktopSession() verifies that the resolution is - // valid. - DCHECK(resolution.IsValid()); - ScreenResolution local_resolution = resolution; // If the screen resolution is not specified, use the default screen // resolution. if (local_resolution.IsEmpty()) { - local_resolution.dimensions_.set(kDefaultRdpScreenWidth, - kDefaultRdpScreenHeight); - local_resolution.dpi_.set(kDefaultRdpDpi, kDefaultRdpDpi); + local_resolution = ScreenResolution( + webrtc::DesktopSize(kDefaultRdpScreenWidth, kDefaultRdpScreenHeight), + webrtc::DesktopVector(kDefaultRdpDpi, kDefaultRdpDpi)); } // Get the screen dimensions assuming the default DPI. - SkISize host_size = local_resolution.ScaleDimensionsToDpi( - SkIPoint::Make(kDefaultRdpDpi, kDefaultRdpDpi)); + webrtc::DesktopSize host_size = local_resolution.ScaleDimensionsToDpi( + webrtc::DesktopVector(kDefaultRdpDpi, kDefaultRdpDpi)); // Make sure that the host resolution is within the limits supported by RDP. - host_size = SkISize::Make( + host_size = webrtc::DesktopSize( std::min(kMaxRdpScreenWidth, std::max(kMinRdpScreenWidth, host_size.width())), std::min(kMaxRdpScreenHeight, diff --git a/remoting/host/ipc_desktop_environment_unittest.cc b/remoting/host/ipc_desktop_environment_unittest.cc index 1318091..4e54b1d 100644 --- a/remoting/host/ipc_desktop_environment_unittest.cc +++ b/remoting/host/ipc_desktop_environment_unittest.cc @@ -32,6 +32,8 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkRegion.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_region.h" using testing::_; using testing::AnyNumber; @@ -199,7 +201,7 @@ class IpcDesktopEnvironmentTest : public testing::Test { // The last |terminal_id| passed to ConnectTermina(); int terminal_id_; - media::MockScreenCapturerDelegate screen_capturer_delegate_; + media::MockScreenCapturerCallback screen_capturer_callback_; MockClientSessionControl client_session_control_; base::WeakPtrFactory<ClientSessionControl> client_session_control_factory_; @@ -440,7 +442,7 @@ TEST_F(IpcDesktopEnvironmentTest, CaptureFrame) { // Start the input injector and screen capturer. input_injector_->Start(clipboard_stub.PassAs<protocol::ClipboardStub>()); - video_capturer_->Start(&screen_capturer_delegate_); + video_capturer_->Start(&screen_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); @@ -454,12 +456,12 @@ TEST_F(IpcDesktopEnvironmentTest, CaptureFrame) { .Times(0); // Stop the test when the first frame is captured. - EXPECT_CALL(screen_capturer_delegate_, OnCaptureCompleted(_)) + EXPECT_CALL(screen_capturer_callback_, OnCaptureCompleted(_)) .WillOnce(InvokeWithoutArgs( this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment)); // Capture a single frame. - video_capturer_->CaptureFrame(); + video_capturer_->Capture(webrtc::DesktopRegion()); task_runner_ = NULL; io_task_runner_ = NULL; @@ -475,7 +477,7 @@ TEST_F(IpcDesktopEnvironmentTest, Reattach) { // Start the input injector and screen capturer. input_injector_->Start(clipboard_stub.PassAs<protocol::ClipboardStub>()); - video_capturer_->Start(&screen_capturer_delegate_); + video_capturer_->Start(&screen_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); @@ -516,7 +518,7 @@ TEST_F(IpcDesktopEnvironmentTest, InjectClipboardEvent) { // Start the input injector and screen capturer. input_injector_->Start(clipboard_stub.PassAs<protocol::ClipboardStub>()); - video_capturer_->Start(&screen_capturer_delegate_); + video_capturer_->Start(&screen_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); @@ -551,7 +553,7 @@ TEST_F(IpcDesktopEnvironmentTest, InjectKeyEvent) { // Start the input injector and screen capturer. input_injector_->Start(clipboard_stub.PassAs<protocol::ClipboardStub>()); - video_capturer_->Start(&screen_capturer_delegate_); + video_capturer_->Start(&screen_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); @@ -586,7 +588,7 @@ TEST_F(IpcDesktopEnvironmentTest, InjectMouseEvent) { // Start the input injector and screen capturer. input_injector_->Start(clipboard_stub.PassAs<protocol::ClipboardStub>()); - video_capturer_->Start(&screen_capturer_delegate_); + video_capturer_->Start(&screen_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); diff --git a/remoting/host/ipc_video_frame_capturer.cc b/remoting/host/ipc_video_frame_capturer.cc index ccf72fb..695bf3c 100644 --- a/remoting/host/ipc_video_frame_capturer.cc +++ b/remoting/host/ipc_video_frame_capturer.cc @@ -5,40 +5,54 @@ #include "remoting/host/ipc_video_frame_capturer.h" #include "media/video/capture/screen/mouse_cursor_shape.h" -#include "media/video/capture/screen/screen_capture_data.h" #include "remoting/host/desktop_session_proxy.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" namespace remoting { IpcVideoFrameCapturer::IpcVideoFrameCapturer( scoped_refptr<DesktopSessionProxy> desktop_session_proxy) - : delegate_(NULL), + : callback_(NULL), + mouse_shape_observer_(NULL), desktop_session_proxy_(desktop_session_proxy), + capture_pending_(false), weak_factory_(this) { } IpcVideoFrameCapturer::~IpcVideoFrameCapturer() { } -void IpcVideoFrameCapturer::Start(Delegate* delegate) { - delegate_ = delegate; +void IpcVideoFrameCapturer::Start(Callback* callback) { + DCHECK(!callback_); + DCHECK(callback); + callback_ = callback; desktop_session_proxy_->SetVideoCapturer(weak_factory_.GetWeakPtr()); } -void IpcVideoFrameCapturer::CaptureFrame() { +void IpcVideoFrameCapturer::SetMouseShapeObserver( + MouseShapeObserver* mouse_shape_observer) { + DCHECK(!mouse_shape_observer_); + DCHECK(mouse_shape_observer); + mouse_shape_observer_ = mouse_shape_observer; +} + +void IpcVideoFrameCapturer::Capture(const webrtc::DesktopRegion& region) { + DCHECK(!capture_pending_); + capture_pending_ = true; desktop_session_proxy_->CaptureFrame(); } void IpcVideoFrameCapturer::OnCaptureCompleted( - scoped_refptr<media::ScreenCaptureData> capture_data) { - if (delegate_) - delegate_->OnCaptureCompleted(capture_data); + scoped_ptr<webrtc::DesktopFrame> frame) { + DCHECK(capture_pending_); + capture_pending_ = false; + callback_->OnCaptureCompleted(frame.release()); } void IpcVideoFrameCapturer::OnCursorShapeChanged( scoped_ptr<media::MouseCursorShape> cursor_shape) { - if (delegate_) - delegate_->OnCursorShapeChanged(cursor_shape.Pass()); + if (mouse_shape_observer_) + mouse_shape_observer_->OnCursorShapeChanged(cursor_shape.Pass()); } } // namespace remoting diff --git a/remoting/host/ipc_video_frame_capturer.h b/remoting/host/ipc_video_frame_capturer.h index 8d6b90a..48ed750 100644 --- a/remoting/host/ipc_video_frame_capturer.h +++ b/remoting/host/ipc_video_frame_capturer.h @@ -29,24 +29,32 @@ class IpcVideoFrameCapturer : public media::ScreenCapturer { scoped_refptr<DesktopSessionProxy> desktop_session_proxy); virtual ~IpcVideoFrameCapturer(); + // webrtc::DesktopCapturer interface. + virtual void Start(Callback* callback) OVERRIDE; + virtual void Capture(const webrtc::DesktopRegion& region) OVERRIDE; + // media::ScreenCapturer interface. - virtual void Start(Delegate* delegate) OVERRIDE; - virtual void CaptureFrame() OVERRIDE; + virtual void SetMouseShapeObserver( + MouseShapeObserver* mouse_shape_observer) OVERRIDE; - // Called when a video frame has been captured. |capture_data| describes - // a captured frame. - void OnCaptureCompleted(scoped_refptr<media::ScreenCaptureData> capture_data); + // Called when a video |frame| has been captured. + void OnCaptureCompleted(scoped_ptr<webrtc::DesktopFrame> frame); // Called when the cursor shape has changed. void OnCursorShapeChanged(scoped_ptr<media::MouseCursorShape> cursor_shape); private: - // Points to the delegate passed to media::ScreenCapturer::Start(). - media::ScreenCapturer::Delegate* delegate_; + // Points to the callback passed to media::ScreenCapturer::Start(). + media::ScreenCapturer::Callback* callback_; + + MouseShapeObserver* mouse_shape_observer_; // Wraps the IPC channel to the desktop session agent. scoped_refptr<DesktopSessionProxy> desktop_session_proxy_; + // Set to true when a frame is being captured. + bool capture_pending_; + // Used to cancel tasks pending on the capturer when it is stopped. base::WeakPtrFactory<IpcVideoFrameCapturer> weak_factory_; diff --git a/remoting/host/resizing_host_observer.cc b/remoting/host/resizing_host_observer.cc index 658a39b..ccb141f 100644 --- a/remoting/host/resizing_host_observer.cc +++ b/remoting/host/resizing_host_observer.cc @@ -116,15 +116,15 @@ void ResizingHostObserver::SetScreenResolution( // If the implementation returns any sizes, pick the best one according to // the algorithm described in CandidateSize::IsBetterThen. - std::list<SkISize> sizes = - desktop_resizer_->GetSupportedSizes(resolution.dimensions_); - if (sizes.empty()) { + SkISize dimentions = SkISize::Make( + resolution.dimensions().width(), resolution.dimensions().height()); + std::list<SkISize> sizes = desktop_resizer_->GetSupportedSizes(dimentions); + if (sizes.empty()) return; - } - CandidateSize best_size(sizes.front(), resolution.dimensions_); + CandidateSize best_size(sizes.front(), dimentions); for (std::list<SkISize>::const_iterator i = ++sizes.begin(); i != sizes.end(); ++i) { - CandidateSize candidate_size(*i, resolution.dimensions_); + CandidateSize candidate_size(*i, dimentions); if (candidate_size.IsBetterThan(best_size)) { best_size = candidate_size; } diff --git a/remoting/host/resizing_host_observer_unittest.cc b/remoting/host/resizing_host_observer_unittest.cc index cc4019a..2d61e5e 100644 --- a/remoting/host/resizing_host_observer_unittest.cc +++ b/remoting/host/resizing_host_observer_unittest.cc @@ -11,6 +11,7 @@ #include "remoting/host/screen_resolution.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkSize.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" std::ostream& operator<<(std::ostream& os, const SkISize& size) { return os << size.width() << "x" << size.height(); @@ -83,7 +84,8 @@ class ResizingHostObserverTest : public testing::Test { SkISize GetBestSize(const SkISize& client_size) { resizing_host_observer_->SetScreenResolution(ScreenResolution( - client_size, SkIPoint::Make(kDefaultDPI, kDefaultDPI))); + webrtc::DesktopSize(client_size.width(), client_size.height()), + webrtc::DesktopVector(kDefaultDPI, kDefaultDPI))); return desktop_resizer_->GetCurrentSize(); } diff --git a/remoting/host/screen_resolution.cc b/remoting/host/screen_resolution.cc index a8a7e68..b792cef 100644 --- a/remoting/host/screen_resolution.cc +++ b/remoting/host/screen_resolution.cc @@ -7,20 +7,27 @@ #include <algorithm> #include <limits> +#include "base/logging.h" + namespace remoting { ScreenResolution::ScreenResolution() - : dimensions_(SkISize::Make(0, 0)), - dpi_(SkIPoint::Make(0, 0)) { + : dimensions_(webrtc::DesktopSize(0, 0)), + dpi_(webrtc::DesktopVector(0, 0)) { } -ScreenResolution::ScreenResolution(const SkISize& dimensions, - const SkIPoint& dpi) +ScreenResolution::ScreenResolution(const webrtc::DesktopSize& dimensions, + const webrtc::DesktopVector& dpi) : dimensions_(dimensions), dpi_(dpi) { + // Check that dimensions are not negative. + DCHECK(!dimensions.is_empty() || dimensions.equals(webrtc::DesktopSize())); + DCHECK_GE(dpi.x(), 0); + DCHECK_GE(dpi.y(), 0); } -SkISize ScreenResolution::ScaleDimensionsToDpi(const SkIPoint& new_dpi) const { +webrtc::DesktopSize ScreenResolution::ScaleDimensionsToDpi( + const webrtc::DesktopVector& new_dpi) const { int64 width = dimensions_.width(); int64 height = dimensions_.height(); @@ -29,15 +36,11 @@ SkISize ScreenResolution::ScaleDimensionsToDpi(const SkIPoint& new_dpi) const { static_cast<int64>(std::numeric_limits<int32>::max())); height = std::min(height * new_dpi.y() / dpi_.y(), static_cast<int64>(std::numeric_limits<int32>::max())); - return SkISize::Make(static_cast<int32>(width), static_cast<int32>(height)); + return webrtc::DesktopSize(width, height); } bool ScreenResolution::IsEmpty() const { - return dimensions_.isEmpty() || dpi_.x() <= 0 || dpi_.y() <= 0; -} - -bool ScreenResolution::IsValid() const { - return !IsEmpty() || dimensions_.isZero(); + return dimensions_.is_empty() || dpi_.is_zero(); } } // namespace remoting diff --git a/remoting/host/screen_resolution.h b/remoting/host/screen_resolution.h index d09eb64..81690db 100644 --- a/remoting/host/screen_resolution.h +++ b/remoting/host/screen_resolution.h @@ -7,8 +7,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" -#include "third_party/skia/include/core/SkPoint.h" -#include "third_party/skia/include/core/SkSize.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" namespace remoting { @@ -16,25 +15,26 @@ namespace remoting { class ScreenResolution { public: ScreenResolution(); - - ScreenResolution(const SkISize& dimensions, const SkIPoint& dpi); + ScreenResolution(const webrtc::DesktopSize& dimensions, + const webrtc::DesktopVector& dpi); // Returns the screen dimensions scaled according to the passed |new_dpi|. - SkISize ScaleDimensionsToDpi(const SkIPoint& new_dpi) const; + webrtc::DesktopSize ScaleDimensionsToDpi( + const webrtc::DesktopVector& new_dpi) const; + + // Dimensions of the screen in pixels. + const webrtc::DesktopSize& dimensions() const { return dimensions_; } + + // The vertical and horizontal DPI of the screen. + const webrtc::DesktopVector& dpi() const { return dpi_; } // Returns true if |dimensions_| specifies an empty rectangle or when // IsValid() returns false. bool IsEmpty() const; - // Returns true if both |dimensions_| and |dpi_| are valid. |dimensions_| - // specifying an empty rectangle is considered to be valid. - bool IsValid() const; - - // Dimensions of the screen in pixels. - SkISize dimensions_; - - // The vertical and horizontal DPI of the screen. - SkIPoint dpi_; + private: + webrtc::DesktopSize dimensions_; + webrtc::DesktopVector dpi_; }; } // namespace remoting diff --git a/remoting/host/screen_resolution_unittest.cc b/remoting/host/screen_resolution_unittest.cc index dce7bc0..01de134 100644 --- a/remoting/host/screen_resolution_unittest.cc +++ b/remoting/host/screen_resolution_unittest.cc @@ -12,69 +12,38 @@ namespace remoting { TEST(ScreenResolutionTest, Empty) { - ScreenResolution resolution; - EXPECT_TRUE(resolution.IsEmpty()); + ScreenResolution resolution1( + webrtc::DesktopSize(100, 100), webrtc::DesktopVector(10, 10)); + EXPECT_FALSE(resolution1.IsEmpty()); - resolution.dimensions_.set(-1, 0); - EXPECT_TRUE(resolution.IsEmpty()); + ScreenResolution resolution2( + webrtc::DesktopSize(), webrtc::DesktopVector(10, 10)); + EXPECT_TRUE(resolution2.IsEmpty()); - resolution.dimensions_.set(0, -1); - EXPECT_TRUE(resolution.IsEmpty()); - - resolution.dimensions_.set(-1, -1); - EXPECT_TRUE(resolution.IsEmpty()); - - resolution.dpi_.set(-1, 0); - EXPECT_TRUE(resolution.IsEmpty()); - - resolution.dpi_.set(0, -1); - EXPECT_TRUE(resolution.IsEmpty()); - - resolution.dpi_.set(-1, -1); - EXPECT_TRUE(resolution.IsEmpty()); -} - -TEST(ScreenResolutionTest, Invalid) { - ScreenResolution resolution; - EXPECT_TRUE(resolution.IsValid()); - - resolution.dimensions_.set(-1, 0); - EXPECT_FALSE(resolution.IsValid()); - - resolution.dimensions_.set(0, -1); - EXPECT_FALSE(resolution.IsValid()); - - resolution.dimensions_.set(-1, -1); - EXPECT_FALSE(resolution.IsValid()); - - resolution.dpi_.set(-1, 0); - EXPECT_FALSE(resolution.IsValid()); - - resolution.dpi_.set(0, -1); - EXPECT_FALSE(resolution.IsValid()); - - resolution.dpi_.set(-1, -1); - EXPECT_FALSE(resolution.IsValid()); + ScreenResolution resolution3( + webrtc::DesktopSize(1, 1), webrtc::DesktopVector(0, 0)); + EXPECT_TRUE(resolution3.IsEmpty()); } TEST(ScreenResolutionTest, Scaling) { ScreenResolution resolution( - SkISize::Make(100, 100), SkIPoint::Make(10, 10)); + webrtc::DesktopSize(100, 100), webrtc::DesktopVector(10, 10)); - EXPECT_EQ(resolution.ScaleDimensionsToDpi(SkIPoint::Make(5, 5)), - SkISize::Make(50, 50)); + EXPECT_TRUE(webrtc::DesktopSize(50, 50).equals( + resolution.ScaleDimensionsToDpi(webrtc::DesktopVector(5, 5)))); - EXPECT_EQ(resolution.ScaleDimensionsToDpi(SkIPoint::Make(20, 20)), - SkISize::Make(200, 200)); + EXPECT_TRUE(webrtc::DesktopSize(200, 200).equals( + resolution.ScaleDimensionsToDpi(webrtc::DesktopVector(20, 20)))); } TEST(ScreenResolutionTest, ScalingSaturation) { ScreenResolution resolution( - SkISize::Make(10000000, 1000000), SkIPoint::Make(1, 1)); + webrtc::DesktopSize(10000000, 1000000), webrtc::DesktopVector(1, 1)); - EXPECT_EQ(resolution.ScaleDimensionsToDpi(SkIPoint::Make(1000000, 1000000)), - SkISize::Make(std::numeric_limits<int32>::max(), - std::numeric_limits<int32>::max())); + int32 max_int = std::numeric_limits<int32>::max(); + EXPECT_TRUE(webrtc::DesktopSize(max_int, max_int).equals( + resolution.ScaleDimensionsToDpi( + webrtc::DesktopVector(1000000, 1000000)))); } } // namespace remoting diff --git a/remoting/host/video_scheduler.cc b/remoting/host/video_scheduler.cc index 41c3dcc..f7db4c2 100644 --- a/remoting/host/video_scheduler.cc +++ b/remoting/host/video_scheduler.cc @@ -15,7 +15,6 @@ #include "base/sys_info.h" #include "base/time.h" #include "media/video/capture/screen/mouse_cursor_shape.h" -#include "media/video/capture/screen/screen_capture_data.h" #include "media/video/capture/screen/screen_capturer.h" #include "remoting/proto/control.pb.h" #include "remoting/proto/internal.pb.h" @@ -24,12 +23,13 @@ #include "remoting/protocol/message_decoder.h" #include "remoting/protocol/video_stub.h" #include "remoting/protocol/util.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" namespace remoting { // Maximum number of frames that can be processed simultaneously. // TODO(hclam): Move this value to CaptureScheduler. -static const int kMaxPendingCaptures = 2; +static const int kMaxPendingFrames = 2; VideoScheduler::VideoScheduler( scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner, @@ -46,7 +46,8 @@ VideoScheduler::VideoScheduler( encoder_(encoder.Pass()), cursor_stub_(cursor_stub), video_stub_(video_stub), - pending_captures_(0), + pending_frames_(0), + capture_pending_(false), did_skip_frame_(false), is_paused_(false), sequence_number_(0) { @@ -59,28 +60,31 @@ VideoScheduler::VideoScheduler( // Public methods -------------------------------------------------------------- -void VideoScheduler::OnCaptureCompleted( - scoped_refptr<media::ScreenCaptureData> capture_data) { +webrtc::SharedMemory* VideoScheduler::CreateSharedMemory(size_t size) { + return NULL; +} + +void VideoScheduler::OnCaptureCompleted(webrtc::DesktopFrame* frame) { DCHECK(capture_task_runner_->BelongsToCurrentThread()); - // Do nothing if the scheduler is being stopped. - if (!capturer_) - return; + capture_pending_ = false; - if (capture_data) { + scoped_ptr<webrtc::DesktopFrame> owned_frame(frame); + + if (frame) { scheduler_.RecordCaptureTime( - base::TimeDelta::FromMilliseconds(capture_data->capture_time_ms())); - - // The best way to get this value is by binding the sequence number to - // the callback when calling CaptureInvalidRects(). However the callback - // system doesn't allow this. Reading from the member variable is - // accurate as long as capture is synchronous as the following statement - // will obtain the most recent sequence number received. - capture_data->set_client_sequence_number(sequence_number_); + base::TimeDelta::FromMilliseconds(frame->capture_time_ms())); } encode_task_runner_->PostTask( - FROM_HERE, base::Bind(&VideoScheduler::EncodeFrame, this, capture_data)); + FROM_HERE, base::Bind(&VideoScheduler::EncodeFrame, this, + base::Passed(&owned_frame), sequence_number_)); + + // If a frame was skipped, try to capture it again. + if (did_skip_frame_) { + capture_task_runner_->PostTask( + FROM_HERE, base::Bind(&VideoScheduler::CaptureNextFrame, this)); + } } void VideoScheduler::OnCursorShapeChanged( @@ -163,6 +167,7 @@ void VideoScheduler::StartOnCaptureThread() { DCHECK(!capture_timer_); // Start the capturer and let it notify us if cursor shape changes. + capturer_->SetMouseShapeObserver(this); capturer_->Start(this); capture_timer_.reset(new base::OneShotTimer<VideoScheduler>()); @@ -174,17 +179,12 @@ void VideoScheduler::StartOnCaptureThread() { void VideoScheduler::StopOnCaptureThread() { DCHECK(capture_task_runner_->BelongsToCurrentThread()); + // This doesn't deleted already captured frames, so encoder can keep using the + // frames that were captured previously. + capturer_.reset(); + // |capture_timer_| must be destroyed on the thread on which it is used. capture_timer_.reset(); - - // Schedule deletion of |capturer_| once the encode thread is no longer - // processing capture data. See http://crbug.com/163641. This also clears - // |capturer_| pointer to prevent pending tasks from using it. - // TODO(wez): Make it safe to tear down capturer while buffers remain, and - // remove this work-around. - encode_task_runner_->PostTask( - FROM_HERE, base::Bind(&VideoScheduler::StopOnEncodeThread, this, - base::Passed(&capturer_))); } void VideoScheduler::ScheduleNextCapture() { @@ -203,10 +203,10 @@ void VideoScheduler::CaptureNextFrame() { if (!capturer_ || is_paused_) return; - // Make sure we have at most two oustanding recordings. We can simply return + // Make sure we have at most two outstanding recordings. We can simply return // if we can't make a capture now, the next capture will be started by the // end of an encode operation. - if (pending_captures_ >= kMaxPendingCaptures) { + if (pending_frames_ >= kMaxPendingFrames || capture_pending_) { did_skip_frame_ = true; return; } @@ -214,22 +214,24 @@ void VideoScheduler::CaptureNextFrame() { did_skip_frame_ = false; // At this point we are going to perform one capture so save the current time. - pending_captures_++; - DCHECK_LE(pending_captures_, kMaxPendingCaptures); + pending_frames_++; + DCHECK_LE(pending_frames_, kMaxPendingFrames); // Before doing a capture schedule for the next one. ScheduleNextCapture(); + capture_pending_ = true; + // And finally perform one capture. - capturer_->CaptureFrame(); + capturer_->Capture(webrtc::DesktopRegion()); } void VideoScheduler::FrameCaptureCompleted() { DCHECK(capture_task_runner_->BelongsToCurrentThread()); // Decrement the pending capture count. - pending_captures_--; - DCHECK_GE(pending_captures_, 0); + pending_frames_--; + DCHECK_GE(pending_frames_, 0); // If we've skipped a frame capture because too we had too many captures // pending then schedule one now. @@ -275,28 +277,35 @@ void VideoScheduler::SendCursorShape( // Encoder thread -------------------------------------------------------------- void VideoScheduler::EncodeFrame( - scoped_refptr<media::ScreenCaptureData> capture_data) { + scoped_ptr<webrtc::DesktopFrame> frame, + int sequence_number) { DCHECK(encode_task_runner_->BelongsToCurrentThread()); // If there is nothing to encode then send an empty keep-alive packet. - if (!capture_data || capture_data->dirty_region().isEmpty()) { + if (!frame || frame->updated_region().is_empty()) { scoped_ptr<VideoPacket> packet(new VideoPacket()); packet->set_flags(VideoPacket::LAST_PARTITION); + packet->set_sequence_number(sequence_number); network_task_runner_->PostTask( FROM_HERE, base::Bind(&VideoScheduler::SendVideoPacket, this, base::Passed(&packet))); + capture_task_runner_->DeleteSoon(FROM_HERE, frame.release()); return; } encoder_->Encode( - capture_data, false, - base::Bind(&VideoScheduler::EncodedDataAvailableCallback, this)); + frame.get(), base::Bind(&VideoScheduler::EncodedDataAvailableCallback, + this, sequence_number)); + capture_task_runner_->DeleteSoon(FROM_HERE, frame.release()); } void VideoScheduler::EncodedDataAvailableCallback( + int sequence_number, scoped_ptr<VideoPacket> packet) { DCHECK(encode_task_runner_->BelongsToCurrentThread()); + packet->set_sequence_number(sequence_number); + bool last = (packet->flags() & VideoPacket::LAST_PACKET) != 0; if (last) { scheduler_.RecordEncodeTime( @@ -308,14 +317,4 @@ void VideoScheduler::EncodedDataAvailableCallback( base::Passed(&packet))); } -void VideoScheduler::StopOnEncodeThread( - scoped_ptr<media::ScreenCapturer> capturer) { - DCHECK(encode_task_runner_->BelongsToCurrentThread()); - - // This is posted by StopOnCaptureThread, so we know that by the time we - // process it there are no more encode tasks queued. Pass |capturer| for - // deletion on the capture thread. - capture_task_runner_->DeleteSoon(FROM_HERE, capturer.release()); -} - } // namespace remoting diff --git a/remoting/host/video_scheduler.h b/remoting/host/video_scheduler.h index d26c394..16f6247 100644 --- a/remoting/host/video_scheduler.h +++ b/remoting/host/video_scheduler.h @@ -23,7 +23,6 @@ class SingleThreadTaskRunner; } // namespace base namespace media { -class ScreenCaptureData; class ScreenCapturer; } // namespace media @@ -74,7 +73,8 @@ class VideoStub; // too much CPU, or hogging the host's graphics subsystem. class VideoScheduler : public base::RefCountedThreadSafe<VideoScheduler>, - public media::ScreenCapturer::Delegate { + public webrtc::DesktopCapturer::Callback, + public media::ScreenCapturer::MouseShapeObserver { public: // Creates a VideoScheduler running capture, encode and network tasks on the // supplied TaskRunners. Video and cursor shape updates will be pumped to @@ -89,9 +89,11 @@ class VideoScheduler : public base::RefCountedThreadSafe<VideoScheduler>, protocol::CursorShapeStub* cursor_stub, protocol::VideoStub* video_stub); - // media::ScreenCapturer::Delegate implementation. - virtual void OnCaptureCompleted( - scoped_refptr<media::ScreenCaptureData> capture_data) OVERRIDE; + // webrtc::DesktopCapturer::Callback implementation. + virtual webrtc::SharedMemory* CreateSharedMemory(size_t size) OVERRIDE; + virtual void OnCaptureCompleted(webrtc::DesktopFrame* frame) OVERRIDE; + + // media::ScreenCapturer::MouseShapeObserver implementation. virtual void OnCursorShapeChanged( scoped_ptr<media::MouseCursorShape> cursor_shape) OVERRIDE; @@ -119,8 +121,7 @@ class VideoScheduler : public base::RefCountedThreadSafe<VideoScheduler>, // Starts the capturer on the capture thread. void StartOnCaptureThread(); - // Stops scheduling frame captures on the capture thread, and posts - // StopOnEncodeThread() to the network thread when done. + // Stops scheduling frame captures on the capture thread. void StopOnCaptureThread(); // Schedules the next call to CaptureNextFrame. @@ -147,13 +148,11 @@ class VideoScheduler : public base::RefCountedThreadSafe<VideoScheduler>, // Encoder thread ----------------------------------------------------------- // Encode a frame, passing generated VideoPackets to SendVideoPacket(). - void EncodeFrame(scoped_refptr<media::ScreenCaptureData> capture_data); - - void EncodedDataAvailableCallback(scoped_ptr<VideoPacket> packet); + void EncodeFrame(scoped_ptr<webrtc::DesktopFrame> frame, + int sequence_number); - // Used to synchronize capture and encode thread teardown, notifying the - // network thread when done. - void StopOnEncodeThread(scoped_ptr<media::ScreenCapturer> capturer); + void EncodedDataAvailableCallback(int sequence_number, + scoped_ptr<VideoPacket> packet); // Task runners used by this class. scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner_; @@ -174,8 +173,13 @@ class VideoScheduler : public base::RefCountedThreadSafe<VideoScheduler>, // Timer used to schedule CaptureNextFrame(). scoped_ptr<base::OneShotTimer<VideoScheduler> > capture_timer_; - // Count the number of recordings (i.e. capture or encode) happening. - int pending_captures_; + // The number of frames being processed, i.e. frames that we are currently + // capturing, encoding or sending. The value is capped at 2 to minimize + // latency. + int pending_frames_; + + // Set when the capturer is capturing a frame. + bool capture_pending_; // True if the previous scheduled capture was skipped. bool did_skip_frame_; diff --git a/remoting/host/video_scheduler_unittest.cc b/remoting/host/video_scheduler_unittest.cc index 44010af..7bf17fc 100644 --- a/remoting/host/video_scheduler_unittest.cc +++ b/remoting/host/video_scheduler_unittest.cc @@ -7,7 +7,6 @@ #include "base/bind.h" #include "base/message_loop.h" #include "base/run_loop.h" -#include "media/video/capture/screen/screen_capture_data.h" #include "media/video/capture/screen/screen_capturer_mock_objects.h" #include "remoting/base/auto_thread_task_runner.h" #include "remoting/codec/video_encoder.h" @@ -15,6 +14,7 @@ #include "remoting/protocol/protocol_mock_objects.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" using ::remoting::protocol::MockClientStub; using ::remoting::protocol::MockVideoStub; @@ -38,7 +38,7 @@ namespace { ACTION(FinishEncode) { scoped_ptr<VideoPacket> packet(new VideoPacket()); packet->set_flags(VideoPacket::LAST_PACKET | VideoPacket::LAST_PARTITION); - arg2.Run(packet.Pass()); + arg1.Run(packet.Pass()); } ACTION(FinishSend) { @@ -55,9 +55,8 @@ class MockVideoEncoder : public VideoEncoder { MockVideoEncoder(); virtual ~MockVideoEncoder(); - MOCK_METHOD3(Encode, void( - scoped_refptr<media::ScreenCaptureData> capture_data, - bool key_frame, + MOCK_METHOD2(Encode, void( + const webrtc::DesktopFrame* frame, const DataAvailableCallback& data_available_callback)); private: @@ -78,9 +77,8 @@ class VideoSchedulerTest : public testing::Test { void StopVideoScheduler(); // media::ScreenCapturer mocks. - void OnCapturerStart(media::ScreenCapturer::Delegate* delegate); - void OnCapturerStop(); - void OnCaptureFrame(); + void OnCapturerStart(media::ScreenCapturer::Callback* callback); + void OnCaptureFrame(const webrtc::DesktopRegion& region); protected: base::MessageLoop message_loop_; @@ -94,10 +92,10 @@ class VideoSchedulerTest : public testing::Test { // The following mock objects are owned by VideoScheduler. MockVideoEncoder* encoder_; - scoped_refptr<media::ScreenCaptureData> data_; + scoped_ptr<webrtc::DesktopFrame> frame_; - // Points to the delegate passed to media::ScreenCapturer::Start(). - media::ScreenCapturer::Delegate* capturer_delegate_; + // Points to the callback passed to media::ScreenCapturer::Start(). + media::ScreenCapturer::Callback* capturer_callback_; private: DISALLOW_COPY_AND_ASSIGN(VideoSchedulerTest); @@ -105,7 +103,7 @@ class VideoSchedulerTest : public testing::Test { VideoSchedulerTest::VideoSchedulerTest() : encoder_(NULL), - capturer_delegate_(NULL) { + capturer_callback_(NULL) { } void VideoSchedulerTest::SetUp() { @@ -134,22 +132,17 @@ void VideoSchedulerTest::StopVideoScheduler() { } void VideoSchedulerTest::OnCapturerStart( - media::ScreenCapturer::Delegate* delegate) { - EXPECT_FALSE(capturer_delegate_); - EXPECT_TRUE(delegate); + media::ScreenCapturer::Callback* callback) { + EXPECT_FALSE(capturer_callback_); + EXPECT_TRUE(callback); - capturer_delegate_ = delegate; + capturer_callback_ = callback; } -void VideoSchedulerTest::OnCapturerStop() { - capturer_delegate_ = NULL; -} - -void VideoSchedulerTest::OnCaptureFrame() { - SkRegion update_region(SkIRect::MakeXYWH(0, 0, 10, 10)); - data_->mutable_dirty_region().op(update_region, SkRegion::kUnion_Op); - - capturer_delegate_->OnCaptureCompleted(data_); +void VideoSchedulerTest::OnCaptureFrame(const webrtc::DesktopRegion& region) { + frame_->mutable_updated_region()->SetRect( + webrtc::DesktopRect::MakeXYWH(0, 0, 10, 10)); + capturer_callback_->OnCaptureCompleted(frame_.release()); } // This test mocks capturer, encoder and network layer to simulate one capture @@ -163,17 +156,17 @@ TEST_F(VideoSchedulerTest, StartAndStop) { EXPECT_CALL(*capturer, Start(_)) .WillOnce(Invoke(this, &VideoSchedulerTest::OnCapturerStart)); - data_ = new media::ScreenCaptureData( - NULL, kWidth * media::ScreenCaptureData::kBytesPerPixel, - SkISize::Make(kWidth, kHeight)); + frame_.reset(new webrtc::BasicDesktopFrame( + webrtc::DesktopSize(kWidth, kHeight))); + webrtc::DesktopFrame* frame_ptr = frame_.get(); // First the capturer is called. - Expectation capturer_capture = EXPECT_CALL(*capturer, CaptureFrame()) + Expectation capturer_capture = EXPECT_CALL(*capturer, Capture(_)) .After(capturer_start) .WillRepeatedly(Invoke(this, &VideoSchedulerTest::OnCaptureFrame)); // Expect the encoder be called. - EXPECT_CALL(*encoder_, Encode(data_, false, _)) + EXPECT_CALL(*encoder_, Encode(frame_ptr, _)) .WillRepeatedly(FinishEncode()); // By default delete the arguments when ProcessVideoPacket is received. diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index 1c4a1d0..b185270 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -300,6 +300,7 @@ '../google_apis/google_apis.gyp:google_apis', '../media/media.gyp:media', '../ipc/ipc.gyp:ipc', + '../third_party/webrtc/modules/modules.gyp:desktop_capture', ], 'defines': [ 'VERSION=<(version_full)', @@ -326,6 +327,8 @@ 'host/chromoting_host_context.h', 'host/chromoting_messages.cc', 'host/chromoting_messages.h', + 'host/chromoting_param_traits.cc', + 'host/chromoting_param_traits.h', 'host/client_session.cc', 'host/client_session.h', 'host/client_session_control.h', @@ -358,7 +361,7 @@ 'host/dns_blackhole_checker.h', 'host/heartbeat_sender.cc', 'host/heartbeat_sender.h', - 'host/host_change_notification_listener.cc', + 'host/host_change_notification_listener.cc', 'host/host_change_notification_listener.h', 'host/host_config.cc', 'host/host_config.h', @@ -1429,6 +1432,7 @@ 'remoting_me2me_host_static', 'remoting_protocol', 'remoting_version_resources', + '../third_party/webrtc/modules/modules.gyp:desktop_capture', ], 'sources': [ '<(SHARED_INTERMEDIATE_DIR)/remoting/host/chromoting_lib.rc', @@ -2263,6 +2267,7 @@ 'remoting_jingle_glue', 'remoting_resources', 'proto/chromotocol.gyp:chromotocol_proto_lib', + '../third_party/webrtc/modules/modules.gyp:desktop_capture', ], 'export_dependent_settings': [ '../base/base.gyp:base', @@ -2558,6 +2563,7 @@ 'remoting_jingle_glue', 'remoting_protocol', 'remoting_resources', + '../third_party/webrtc/modules/modules.gyp:desktop_capture', ], 'defines': [ 'VERSION=<(version_full)', |