summaryrefslogtreecommitdiffstats
path: root/remoting/client
diff options
context:
space:
mode:
authorgarykac@google.com <garykac@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-24 17:51:57 +0000
committergarykac@google.com <garykac@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-24 17:51:57 +0000
commite4f323a219824c30f130d2f34105bcda84c3a35f (patch)
tree81221a138ee85c805f07169d72fd637ad1f791cd /remoting/client
parent339b38a6e23e085e5d1faebb736c0414b5df694d (diff)
downloadchromium_src-e4f323a219824c30f130d2f34105bcda84c3a35f.zip
chromium_src-e4f323a219824c30f130d2f34105bcda84c3a35f.tar.gz
chromium_src-e4f323a219824c30f130d2f34105bcda84c3a35f.tar.bz2
Move common ChromotingView functionality into base class so that the code can
be shared between PepperView and X11View. Have View create appropriate decoder based on encoding of the update stream. Add state to decoder to keep track of whether Begin/End has been called in the correct order. Add unittests for new routines. BUG=none TEST=remoting unit tests Review URL: http://codereview.chromium.org/3124005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@57206 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/client')
-rw-r--r--remoting/client/chromoting_view.cc103
-rw-r--r--remoting/client/chromoting_view.h54
-rw-r--r--remoting/client/chromoting_view_unittest.cc558
-rw-r--r--remoting/client/plugin/pepper_view.cc43
-rw-r--r--remoting/client/plugin/pepper_view.h11
-rw-r--r--remoting/client/x11_view.cc65
-rw-r--r--remoting/client/x11_view.h15
7 files changed, 764 insertions, 85 deletions
diff --git a/remoting/client/chromoting_view.cc b/remoting/client/chromoting_view.cc
new file mode 100644
index 0000000..9bc1a8a
--- /dev/null
+++ b/remoting/client/chromoting_view.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2010 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/client/chromoting_view.h"
+
+#include "remoting/base/decoder_verbatim.h"
+#include "remoting/base/decoder_zlib.h"
+
+namespace remoting {
+
+ChromotingView::ChromotingView()
+ : frame_width_(0),
+ frame_height_(0) {
+}
+
+bool ChromotingView::SetupDecoder(UpdateStreamEncoding encoding) {
+ if (encoding == EncodingInvalid) {
+ LOG(ERROR) << "Cannot create encoder for EncodingInvalid";
+ return false;
+ }
+
+ // If we're in the middle of decoding a stream, then we need to make sure
+ // that that all packets in that stream match the encoding of the first
+ // packet.
+ //
+ // If we decide to relax this constraint in the future, we'll need to
+ // update this to keep a set of decoders around.
+ if (decoder_.get() && decoder_->IsStarted()) {
+ // Verify that the encoding matches the decoder. Once we've started
+ // decoding, we can't switch to another decoder.
+ if (decoder_->Encoding() != encoding) {
+ LOG(ERROR) << "Encoding mismatch: Set up to handle "
+ << "UpdateStreamEncoding=" << decoder_->Encoding()
+ << " but received request for "
+ << encoding;
+ return false;
+ }
+ return true;
+ }
+
+ // Lazily initialize a new decoder.
+ // We create a new decoder if we don't currently have a decoder or if the
+ // decoder doesn't match the desired encoding.
+ if (!decoder_.get() || decoder_->Encoding() != encoding) {
+ // Initialize a new decoder based on this message encoding.
+ if (encoding == EncodingNone) {
+ decoder_.reset(new DecoderVerbatim());
+ } else if (encoding == EncodingZlib) {
+ decoder_.reset(new DecoderZlib());
+ }
+ // Make sure we successfully allocated a decoder of the correct type.
+ DCHECK(decoder_.get());
+ DCHECK(decoder_->Encoding() == encoding);
+ }
+
+ return true;
+}
+
+bool ChromotingView::BeginDecoding(Task* partial_decode_done,
+ Task* decode_done) {
+ if (decoder_->IsStarted()) {
+ LOG(ERROR) << "BeginDecoding called without ending previous decode.";
+ return false;
+ }
+
+ decoder_->BeginDecode(frame_, &update_rects_,
+ partial_decode_done, decode_done);
+
+ if (!decoder_->IsStarted()) {
+ LOG(ERROR) << "Unable to start decoding.";
+ return false;
+ }
+
+ return true;
+}
+
+bool ChromotingView::Decode(HostMessage* msg) {
+ if (!decoder_->IsStarted()) {
+ LOG(ERROR) << "Attempt to decode payload before calling BeginDecode.";
+ return false;
+ }
+
+ return decoder_->PartialDecode(msg);
+}
+
+bool ChromotingView::EndDecoding() {
+ if (!decoder_->IsStarted()) {
+ LOG(ERROR) << "Attempt to end decode when none has been started.";
+ return false;
+ }
+
+ decoder_->EndDecode();
+
+ if (decoder_->IsStarted()) {
+ LOG(ERROR) << "Unable to properly end decoding.\n";
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace remoting
diff --git a/remoting/client/chromoting_view.h b/remoting/client/chromoting_view.h
index a50b407..051fd7b 100644
--- a/remoting/client/chromoting_view.h
+++ b/remoting/client/chromoting_view.h
@@ -6,16 +6,16 @@
#define REMOTING_CLIENT_CHROMOTING_VIEW_H_
#include "base/ref_counted.h"
+#include "remoting/base/decoder.h"
namespace remoting {
-class HostMessage;
-
// ChromotingView defines the behavior of an object that draws a view of the
// remote desktop. Its main function is to choose the right decoder and render
// the update stream onto the screen.
class ChromotingView {
public:
+ ChromotingView();
virtual ~ChromotingView() {}
// Initialize the common structures for the view.
@@ -47,13 +47,63 @@ class ChromotingView {
virtual void SetHostScreenSize(int width, int height) = 0;
// Handle the BeginUpdateStream message.
+ // This method should perform the following tasks:
+ // (1) Perform any platform-specific tasks for start of update stream.
+ // (2) Make sure the |frame_| has been initialized.
+ // (3) Delete the HostMessage.
virtual void HandleBeginUpdateStream(HostMessage* msg) = 0;
// Handle the UpdateStreamPacket message.
+ // This method should perform the following tasks:
+ // (1) Extract the decoding from the update packet message.
+ // (2) Call SetupDecoder with the encoding to lazily initialize the decoder.
+ // We don't do this in BeginUpdateStream because the begin message
+ // doesn't contain the encoding.
+ // (3) Call BeginDecoding if this is the first packet of the stream.
+ // (4) Call the decoder's PartialDecode() method to decode the packet.
+ // This call will delete the HostMessage.
+ // Note:
+ // * For a given begin/end update stream, the encodings specified in the
+ // update packets must all match. We may revisit this constraint at a
+ // later date.
virtual void HandleUpdateStreamPacket(HostMessage* msg) = 0;
// Handle the EndUpdateStream message.
+ // This method should perform the following tasks:
+ // (1) Call EndDecoding().
+ // (2) Perform any platform-specific tasks for end of update stream.
+ // (3) Delete the HostMessage.
virtual void HandleEndUpdateStream(HostMessage* msg) = 0;
+
+ protected:
+ // Setup the decoder based on the given encoding.
+ // Returns true if a new decoder has already been started (with a call to
+ // BeginDecoding).
+ bool SetupDecoder(UpdateStreamEncoding encoding);
+
+ // Prepare the decoder to start decoding a chunk of data.
+ // This needs to be called if SetupDecoder() returns false.
+ bool BeginDecoding(Task* partial_decode_done, Task* decode_done);
+
+ // Decode the given message.
+ // BeginDecoding() must be called before any calls to Decode().
+ bool Decode(HostMessage* msg);
+
+ // Finish decoding and send notifications to update the view.
+ bool EndDecoding();
+
+ // Decoder used to decode the video frames (or frame fragements).
+ scoped_ptr<Decoder> decoder_;
+
+ // Framebuffer for the decoder.
+ scoped_refptr<media::VideoFrame> frame_;
+
+ // Dimensions of |frame_| bitmap.
+ int frame_width_;
+ int frame_height_;
+
+ UpdatedRects update_rects_;
+ UpdatedRects all_update_rects_;
};
} // namespace remoting
diff --git a/remoting/client/chromoting_view_unittest.cc b/remoting/client/chromoting_view_unittest.cc
new file mode 100644
index 0000000..96d97ed
--- /dev/null
+++ b/remoting/client/chromoting_view_unittest.cc
@@ -0,0 +1,558 @@
+// Copyright (c) 2010 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 "base/scoped_ptr.h"
+#include "remoting/base/protocol/chromotocol.pb.h"
+#include "remoting/client/chromoting_view.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Return;
+
+namespace remoting {
+
+class MockDecoder : public Decoder {
+ public:
+ MockDecoder() {}
+
+ MOCK_METHOD4(BeginDecode, bool(scoped_refptr<media::VideoFrame> frame,
+ UpdatedRects* updated_rects,
+ Task* partial_decode_done,
+ Task* decode_done));
+ MOCK_METHOD1(PartialDecode, bool(HostMessage* message));
+ MOCK_METHOD0(EndDecode, void());
+
+ MOCK_METHOD0(Encoding, UpdateStreamEncoding());
+ MOCK_METHOD0(IsStarted, bool());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockDecoder);
+};
+
+// Fake ChromotingView that provides stub implementations for all pure virtual
+// methods. This is sufficient since we're only interested in testing the
+// base class methods in this file.
+class FakeView : public ChromotingView {
+ bool Initialize() { return false; }
+ void TearDown() {}
+ void Paint() {}
+ void SetSolidFill(uint32 color) {}
+ void UnsetSolidFill() {}
+ void SetViewport(int x, int y, int width, int height) {
+ frame_width_ = width;
+ frame_height_ = height;
+ }
+ void SetHostScreenSize(int width, int height) {}
+ void HandleBeginUpdateStream(HostMessage* msg) {}
+ void HandleUpdateStreamPacket(HostMessage* msg) {}
+ void HandleEndUpdateStream(HostMessage* msg) {}
+
+ public:
+ // Testing accessors.
+ // These provide access to private/protected members of ChromotingView so
+ // that they can be tested/verified.
+ Decoder* get_decoder() {
+ return decoder_.get();
+ }
+ void set_decoder(Decoder* decoder) {
+ decoder_.reset(decoder);
+ }
+
+ // Testing wrappers for private setup/startup decoder routines.
+ bool setup_decoder(UpdateStreamEncoding encoding) {
+ return SetupDecoder(encoding);
+ }
+ bool begin_decoding(Task* partial_decode_done, Task* decode_done) {
+ return BeginDecoding(partial_decode_done, decode_done);
+ }
+ bool decode(HostMessage* msg) {
+ return Decode(msg);
+ }
+ bool end_decoding() {
+ return EndDecoding();
+ }
+
+ // Testing setup.
+ void set_test_viewport() {
+ SetViewport(0, 0, 640, 480);
+ }
+};
+
+// Verify the initial state.
+TEST(ChromotingViewTest, InitialState) {
+ scoped_ptr<FakeView> view(new FakeView());
+ EXPECT_TRUE(view->get_decoder() == NULL);
+}
+
+// Test a simple decoder sequence:
+// HandleBeginUpdateStream:
+// HandleUpdateStreamPacket:
+// SetupDecoder - return false
+// BeginDecoding
+// Decode
+// HandleEndUpdateStream:
+// EndDecoding
+TEST(ChromotingViewTest, DecodeSimple) {
+ scoped_ptr<FakeView> view(new FakeView());
+ view->set_test_viewport();
+
+ // HandleBeginUpdateStream
+
+ // HandleUpdateStreamPacket
+
+ ASSERT_TRUE(view->setup_decoder(EncodingZlib));
+ Decoder* decoder = view->get_decoder();
+ ASSERT_TRUE(decoder != NULL);
+ EXPECT_EQ(EncodingZlib, decoder->Encoding());
+ EXPECT_EQ(false, decoder->IsStarted());
+
+ // Overwrite |decoder_| with MockDecoder.
+ MockDecoder* mock_decoder = new MockDecoder();
+ view->set_decoder(mock_decoder);
+ EXPECT_CALL(*mock_decoder, Encoding())
+ .WillRepeatedly(Return(EncodingZlib));
+ {
+ InSequence s;
+
+ // BeginDecoding
+ EXPECT_CALL(*mock_decoder, IsStarted())
+ .WillOnce(Return(false));
+ EXPECT_CALL(*mock_decoder, BeginDecode(_, _, _, _))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_decoder, IsStarted())
+ .WillOnce(Return(true));
+
+ // Decode
+ EXPECT_CALL(*mock_decoder, IsStarted())
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_decoder, PartialDecode(_))
+ .WillOnce(Return(true));
+
+ // EndDecoding
+ EXPECT_CALL(*mock_decoder, IsStarted())
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_decoder, EndDecode())
+ .Times(1);
+ EXPECT_CALL(*mock_decoder, IsStarted())
+ .WillOnce(Return(false));
+ }
+
+ // decoder_->IsStarted() is false, so we call begin_decoding().
+ ASSERT_TRUE(view->begin_decoding(NULL, NULL));
+
+ ASSERT_TRUE(view->decode(NULL));
+
+ // HandleEndUpdateStream
+
+ ASSERT_TRUE(view->end_decoding());
+}
+
+// Test a three-packet decoder sequence:
+// HandleBeginUpdateStream:
+// HandleUpdateStreamPacket: (1)
+// SetupDecoder - return false
+// BeginDecoding
+// Decode
+// HandleUpdateStreamPacket: (2)
+// SetupDecoder - return true
+// Decode
+// HandleUpdateStreamPacket: (3)
+// SetupDecoder - return true
+// Decode
+// HandleEndUpdateStream:
+// EndDecoding
+TEST(ChromotingViewTest, DecodeThreePackets) {
+ scoped_ptr<FakeView> view(new FakeView());
+ view->set_test_viewport();
+
+ // HandleBeginUpdateStream
+
+ // HandleUpdateStreamPacket (1)
+
+ ASSERT_TRUE(view->setup_decoder(EncodingZlib));
+ Decoder* decoder = view->get_decoder();
+ ASSERT_TRUE(decoder != NULL);
+ EXPECT_EQ(EncodingZlib, decoder->Encoding());
+ EXPECT_EQ(false, decoder->IsStarted());
+
+ // Overwrite |decoder_| with MockDecoder.
+ MockDecoder* mock_decoder = new MockDecoder();
+ view->set_decoder(mock_decoder);
+ EXPECT_CALL(*mock_decoder, Encoding())
+ .WillRepeatedly(Return(EncodingZlib));
+ EXPECT_CALL(*mock_decoder, BeginDecode(_, _, _, _))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_decoder, PartialDecode(_))
+ .Times(3)
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_decoder, EndDecode())
+ .Times(1);
+ {
+ InSequence s;
+
+ // BeginDecoding
+ EXPECT_CALL(*mock_decoder, IsStarted())
+ .WillOnce(Return(false))
+ .RetiresOnSaturation();
+ // BeginDecoding (1)
+ // Decode (1)
+ // SetupDecoder (1)
+ // Decode (1)
+ // SetupDecoder (1)
+ // Decode (1)
+ // EndDecoding (1)
+ // Total = 7 calls
+ EXPECT_CALL(*mock_decoder, IsStarted())
+ .Times(7)
+ .WillRepeatedly(Return(true))
+ .RetiresOnSaturation();
+ // EndDecoding
+ EXPECT_CALL(*mock_decoder, IsStarted())
+ .WillOnce(Return(false))
+ .RetiresOnSaturation();
+ }
+
+ // decoder_->IsStarted() is false, so we call begin_decoding().
+ ASSERT_TRUE(view->begin_decoding(NULL, NULL));
+
+ ASSERT_TRUE(view->decode(NULL));
+
+ // HandleUpdateStreamPacket (2)
+
+ ASSERT_TRUE(view->setup_decoder(EncodingZlib));
+ // Don't call BeginDecoding() because it is already started.
+ ASSERT_TRUE(view->decode(NULL));
+
+ // HandleUpdateStreamPacket (3)
+
+ ASSERT_TRUE(view->setup_decoder(EncodingZlib));
+ // Don't call BeginDecoding() because it is already started.
+ ASSERT_TRUE(view->decode(NULL));
+
+ // HandleEndUpdateStream
+
+ ASSERT_TRUE(view->end_decoding());
+}
+
+// Test two update streams: (same packet encoding)
+// HandleBeginUpdateStream:
+// HandleUpdateStreamPacket:
+// SetupDecoder - return false
+// BeginDecoding
+// Decode
+// HandleEndUpdateStream:
+// EndDecoding
+//
+// HandleBeginUpdateStream:
+// HandleUpdateStreamPacket:
+// SetupDecoder - return false
+// BeginDecoding
+// Decode
+// HandleEndUpdateStream:
+// EndDecoding
+TEST(ChromotingViewTest, DecodeTwoStreams) {
+ scoped_ptr<FakeView> view(new FakeView());
+ view->set_test_viewport();
+
+ // HandleBeginUpdateStream (update stream 1)
+
+ // HandleUpdateStreamPacket
+
+ ASSERT_TRUE(view->setup_decoder(EncodingZlib));
+ Decoder* decoder = view->get_decoder();
+ ASSERT_TRUE(decoder != NULL);
+ EXPECT_EQ(EncodingZlib, decoder->Encoding());
+ EXPECT_EQ(false, decoder->IsStarted());
+
+ // Overwrite |decoder_| with MockDecoder.
+ MockDecoder* mock_decoder = new MockDecoder();
+ view->set_decoder(mock_decoder);
+ EXPECT_CALL(*mock_decoder, Encoding())
+ .WillRepeatedly(Return(EncodingZlib));
+ EXPECT_CALL(*mock_decoder, BeginDecode(_, _, _, _))
+ .Times(2)
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_decoder, PartialDecode(_))
+ .Times(2)
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_decoder, EndDecode())
+ .Times(2);
+ {
+ InSequence s;
+ // BeginDecoding
+ EXPECT_CALL(*mock_decoder, IsStarted())
+ .WillOnce(Return(false))
+ .RetiresOnSaturation();
+ // BeginDecoding (1)
+ // Decode (1)
+ // EndDecoding (1)
+ // Total = 3 calls
+ EXPECT_CALL(*mock_decoder, IsStarted())
+ .Times(3)
+ .WillRepeatedly(Return(true))
+ .RetiresOnSaturation();
+ // EndDecoding (1)
+ // SetupDecoder (1)
+ // BeginDecoding (1)
+ // Total = 3 calls
+ EXPECT_CALL(*mock_decoder, IsStarted())
+ .Times(3)
+ .WillRepeatedly(Return(false))
+ .RetiresOnSaturation();
+ // BeginDecoding (1)
+ // Decode (1)
+ // EndDecoding (1)
+ // Total = 3 calls
+ EXPECT_CALL(*mock_decoder, IsStarted())
+ .Times(3)
+ .WillRepeatedly(Return(true))
+ .RetiresOnSaturation();
+ // EndDecoding
+ EXPECT_CALL(*mock_decoder, IsStarted())
+ .WillOnce(Return(false))
+ .RetiresOnSaturation();
+ }
+
+ // |started| is false, so we call begin_decoding().
+ ASSERT_TRUE(view->begin_decoding(NULL, NULL));
+
+ ASSERT_TRUE(view->decode(NULL));
+
+ // HandleEndUpdateStream
+
+ ASSERT_TRUE(view->end_decoding());
+
+ // HandleBeginUpdateStream (update stream 2)
+
+ // HandleUpdateStreamPacket
+
+ Decoder* old_decoder = view->get_decoder();
+ view->setup_decoder(EncodingZlib);
+ // Verify we're using the same decoder since the encoding matches.
+ Decoder* new_decoder = view->get_decoder();
+ ASSERT_TRUE(new_decoder == old_decoder);
+
+ // |started| is false, so we call begin_decoding().
+ ASSERT_TRUE(view->begin_decoding(NULL, NULL));
+
+ ASSERT_TRUE(view->decode(NULL));
+
+ // HandleEndUpdateStream
+
+ ASSERT_TRUE(view->end_decoding());
+}
+
+// Test two update streams with different encodings:
+// HandleBeginUpdateStream:
+// HandleUpdateStreamPacket: ('Zlib' encoded)
+// SetupDecoder
+// BeginDecoding
+// Decode
+// HandleEndUpdateStream:
+// EndDecoding
+//
+// HandleBeginUpdateStream:
+// HandleUpdateStreamPacket: ('None' encoded)
+// SetupDecoder
+// BeginDecoding
+// Decode
+// HandleEndUpdateStream:
+// EndDecoding
+TEST(ChromotingViewTest, DecodeTwoStreamsDifferentEncodings) {
+ scoped_ptr<FakeView> view(new FakeView());
+ view->set_test_viewport();
+
+ // HandleBeginUpdateStream (update stream 1)
+
+ // HandleUpdateStreamPacket
+
+ ASSERT_TRUE(view->setup_decoder(EncodingZlib));
+ Decoder* decoder = view->get_decoder();
+ ASSERT_TRUE(decoder != NULL);
+ EXPECT_EQ(EncodingZlib, decoder->Encoding());
+ EXPECT_EQ(false, decoder->IsStarted());
+
+ // Overwrite |decoder_| with MockDecoder.
+ MockDecoder* mock_decoder1 = new MockDecoder();
+ view->set_decoder(mock_decoder1);
+ EXPECT_CALL(*mock_decoder1, Encoding())
+ .WillRepeatedly(Return(EncodingZlib));
+ EXPECT_CALL(*mock_decoder1, BeginDecode(_, _, _, _))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_decoder1, PartialDecode(_))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_decoder1, EndDecode());
+ {
+ InSequence s1;
+ // BeginDecoding
+ EXPECT_CALL(*mock_decoder1, IsStarted())
+ .WillOnce(Return(false))
+ .RetiresOnSaturation();
+ // BeginDecoding (1)
+ // Decode (1)
+ // EndDecoding (1)
+ // Total = 3 calls
+ EXPECT_CALL(*mock_decoder1, IsStarted())
+ .Times(3)
+ .WillRepeatedly(Return(true))
+ .RetiresOnSaturation();
+ // EndDecoding (1)
+ // SetupDecoder (1)
+ // Total = 2 calls
+ EXPECT_CALL(*mock_decoder1, IsStarted())
+ .Times(2)
+ .WillRepeatedly(Return(false))
+ .RetiresOnSaturation();
+ }
+
+ // |started| is false, so we call begin_decoding().
+ ASSERT_TRUE(view->begin_decoding(NULL, NULL));
+
+ ASSERT_TRUE(view->decode(NULL));
+
+ // HandleEndUpdateStream
+
+ ASSERT_TRUE(view->end_decoding());
+
+ // HandleBeginUpdateStream (update stream 2)
+
+ // HandleUpdateStreamPacket
+
+ // Encoding for second stream is different from first, so this will
+ // create a new decoder.
+ ASSERT_TRUE(view->setup_decoder(EncodingNone));
+ // The decoder should be new.
+ EXPECT_NE(mock_decoder1, view->get_decoder());
+
+ // Overwrite |decoder_| with MockDecoder.
+ MockDecoder* mock_decoder2 = new MockDecoder();
+ view->set_decoder(mock_decoder2);
+ EXPECT_CALL(*mock_decoder2, Encoding())
+ .WillRepeatedly(Return(EncodingNone));
+ EXPECT_CALL(*mock_decoder2, BeginDecode(_, _, _, _))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_decoder2, PartialDecode(_))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_decoder2, EndDecode());
+ {
+ InSequence s2;
+ // BeginDecoding
+ EXPECT_CALL(*mock_decoder2, IsStarted())
+ .WillOnce(Return(false))
+ .RetiresOnSaturation();
+ // BeginDecoding (1)
+ // Decode (1)
+ // EndDecoding (1)
+ // Total = 3 calls
+ EXPECT_CALL(*mock_decoder2, IsStarted())
+ .Times(3)
+ .WillRepeatedly(Return(true))
+ .RetiresOnSaturation();
+ // EndDecoding
+ EXPECT_CALL(*mock_decoder2, IsStarted())
+ .WillOnce(Return(false))
+ .RetiresOnSaturation();
+ }
+
+ // |started| is false, so we call begin_decoding().
+ ASSERT_TRUE(view->begin_decoding(NULL, NULL));
+
+ ASSERT_TRUE(view->decode(NULL));
+
+ // HandleEndUpdateStream
+
+ ASSERT_TRUE(view->end_decoding());
+}
+
+// Test failure when packets in a stream have mismatched encodings.
+// HandleBeginUpdateStream:
+// HandleUpdateStreamPacket: (1)
+// SetupDecoder - encoding = Zlib
+// BeginDecoding
+// Decode
+// HandleUpdateStreamPacket: (2)
+// SetupDecoder - encoding = none
+// DEATH
+TEST(ChromotingViewTest, MismatchedEncodings) {
+ scoped_ptr<FakeView> view(new FakeView());
+ view->set_test_viewport();
+
+ // HandleBeginUpdateStream
+
+ // HandleUpdateStreamPacket (1)
+ // encoding = Zlib
+
+ ASSERT_TRUE(view->setup_decoder(EncodingZlib));
+ Decoder* decoder = view->get_decoder();
+ ASSERT_TRUE(decoder != NULL);
+ EXPECT_EQ(EncodingZlib, decoder->Encoding());
+ EXPECT_EQ(false, decoder->IsStarted());
+
+ // Overwrite |decoder_| with MockDecoder.
+ MockDecoder* mock_decoder = new MockDecoder();
+ view->set_decoder(mock_decoder);
+ EXPECT_CALL(*mock_decoder, Encoding())
+ .WillRepeatedly(Return(EncodingZlib));
+ {
+ InSequence s;
+
+ // BeginDecoding().
+ EXPECT_CALL(*mock_decoder, IsStarted())
+ .WillOnce(Return(false));
+ EXPECT_CALL(*mock_decoder, BeginDecode(_, _, _, _))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_decoder, IsStarted())
+ .WillOnce(Return(true));
+
+ // Decode().
+ EXPECT_CALL(*mock_decoder, IsStarted())
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock_decoder, PartialDecode(_))
+ .WillOnce(Return(true));
+
+ // SetupDecoder().
+ EXPECT_CALL(*mock_decoder, IsStarted())
+ .WillOnce(Return(true));
+ }
+
+ // |started| is false, so we call begin_decoding().
+ ASSERT_TRUE(view->begin_decoding(NULL, NULL));
+
+ ASSERT_TRUE(view->decode(NULL));
+
+ // HandleUpdateStreamPacket (2)
+ // encoding = None
+
+ // Doesn't match previous packet encoding, so returns failure.
+ ASSERT_FALSE(view->setup_decoder(EncodingNone));
+}
+
+// Verify we fail when Decode() is called without first calling
+// BeginDecoding().
+TEST(ChromotingViewTest, MissingBegin) {
+ scoped_ptr<FakeView> view(new FakeView());
+ view->set_test_viewport();
+
+ ASSERT_TRUE(view->setup_decoder(EncodingZlib));
+ ASSERT_TRUE(view->get_decoder() != NULL);
+
+ // Overwrite |decoder_| with MockDecoder.
+ MockDecoder* mock_decoder = new MockDecoder();
+ view->set_decoder(mock_decoder);
+ EXPECT_CALL(*mock_decoder, IsStarted())
+ .WillOnce(Return(false));
+
+ // Call decode() without calling begin_decoding().
+ EXPECT_FALSE(view->decode(NULL));
+}
+
+// Test requesting a decoder for an invalid encoding.
+TEST(ChromotingViewTest, InvalidEncoding) {
+ scoped_ptr<FakeView> view(new FakeView());
+ EXPECT_FALSE(view->setup_decoder(EncodingInvalid));
+}
+
+} // namespace remoting
diff --git a/remoting/client/plugin/pepper_view.cc b/remoting/client/plugin/pepper_view.cc
index 6715888..86b70fc 100644
--- a/remoting/client/plugin/pepper_view.cc
+++ b/remoting/client/plugin/pepper_view.cc
@@ -5,7 +5,6 @@
#include "remoting/client/plugin/pepper_view.h"
#include "base/message_loop.h"
-#include "remoting/base/decoder_zlib.h"
#include "remoting/client/plugin/chromoting_instance.h"
#include "remoting/client/plugin/pepper_util.h"
#include "third_party/ppapi/cpp/device_context_2d.h"
@@ -17,8 +16,6 @@ namespace remoting {
PepperView::PepperView(ChromotingInstance* instance)
: instance_(instance),
- backing_store_width_(0),
- backing_store_height_(0),
viewport_x_(0),
viewport_y_(0),
viewport_width_(0),
@@ -63,13 +60,13 @@ void PepperView::Paint() {
} else if (frame_) {
uint32_t* frame_data =
reinterpret_cast<uint32_t*>(frame_->data(media::VideoFrame::kRGBPlane));
- int max_height = std::min(backing_store_height_, image.size().height());
- int max_width = std::min(backing_store_width_, image.size().width());
+ int max_height = std::min(frame_height_, image.size().height());
+ int max_width = std::min(frame_width_, image.size().width());
for (int y = 0; y < max_height; y++) {
for (int x = 0; x < max_width; x++) {
// Force alpha to be set to 255.
*image.GetAddr32(pp::Point(x, y)) =
- frame_data[y*backing_store_width_ + x] | 0xFF000000;
+ frame_data[y*frame_width_ + x] | 0xFF000000;
}
}
} else {
@@ -135,8 +132,11 @@ void PepperView::SetHostScreenSize(int width, int height) {
return;
}
- backing_store_width_ = width;
- backing_store_height_ = height;
+ frame_width_ = width;
+ frame_height_ = height;
+
+ // Reset |frame_| - it will be recreated by the next update stream.
+ frame_ = NULL;
}
void PepperView::HandleBeginUpdateStream(HostMessage* msg) {
@@ -149,24 +149,14 @@ void PepperView::HandleBeginUpdateStream(HostMessage* msg) {
scoped_ptr<HostMessage> deleter(msg);
- // TODO(hclam): Use the information from the message to create the decoder.
- // We lazily construct the decoder.
- if (!decoder_.get()) {
- decoder_.reset(new DecoderZlib());
- }
-
+ // Make sure the |frame_| is initialized.
if (!frame_) {
media::VideoFrame::CreateFrame(media::VideoFrame::RGB32,
- backing_store_width_,
- backing_store_height_,
+ frame_width_, frame_height_,
base::TimeDelta(), base::TimeDelta(),
&frame_);
+ CHECK(frame_);
}
-
- // Tell the decoder to do start decoding.
- decoder_->BeginDecode(frame_, &update_rects_,
- NewRunnableMethod(this, &PepperView::OnPartialDecodeDone),
- NewRunnableMethod(this, &PepperView::OnDecodeDone));
}
void PepperView::HandleUpdateStreamPacket(HostMessage* msg) {
@@ -177,7 +167,14 @@ void PepperView::HandleUpdateStreamPacket(HostMessage* msg) {
return;
}
- decoder_->PartialDecode(msg);
+ // Lazily initialize the decoder.
+ SetupDecoder(msg->update_stream_packet().begin_rect().encoding());
+ if (!decoder_->IsStarted()) {
+ BeginDecoding(NewRunnableMethod(this, &PepperView::OnPartialDecodeDone),
+ NewRunnableMethod(this, &PepperView::OnDecodeDone));
+ }
+
+ Decode(msg);
}
void PepperView::HandleEndUpdateStream(HostMessage* msg) {
@@ -189,7 +186,7 @@ void PepperView::HandleEndUpdateStream(HostMessage* msg) {
}
scoped_ptr<HostMessage> deleter(msg);
- decoder_->EndDecode();
+ EndDecoding();
}
void PepperView::OnPaintDone() {
diff --git a/remoting/client/plugin/pepper_view.h b/remoting/client/plugin/pepper_view.h
index bb4557b..d134207 100644
--- a/remoting/client/plugin/pepper_view.h
+++ b/remoting/client/plugin/pepper_view.h
@@ -17,14 +17,12 @@
#include "base/scoped_ptr.h"
#include "base/task.h"
#include "media/base/video_frame.h"
-#include "remoting/base/decoder.h"
#include "remoting/client/chromoting_view.h"
#include "third_party/ppapi/cpp/device_context_2d.h"
namespace remoting {
class ChromotingInstance;
-class Decoder;
class PepperView : public ChromotingView {
public:
@@ -60,9 +58,6 @@ class PepperView : public ChromotingView {
pp::DeviceContext2D device_context_;
- int backing_store_width_;
- int backing_store_height_;
-
int viewport_x_;
int viewport_y_;
int viewport_width_;
@@ -71,12 +66,6 @@ class PepperView : public ChromotingView {
bool is_static_fill_;
uint32 static_fill_color_;
- scoped_refptr<media::VideoFrame> frame_;
- UpdatedRects update_rects_;
- UpdatedRects all_update_rects_;
-
- scoped_ptr<Decoder> decoder_;
-
DISALLOW_COPY_AND_ASSIGN(PepperView);
};
diff --git a/remoting/client/x11_view.cc b/remoting/client/x11_view.cc
index b733d93..267ae336 100644
--- a/remoting/client/x11_view.cc
+++ b/remoting/client/x11_view.cc
@@ -10,7 +10,6 @@
#include <X11/extensions/Xcomposite.h>
#include "base/logging.h"
-#include "remoting/base/decoder_verbatim.h"
#include "remoting/base/decoder_zlib.h"
namespace remoting {
@@ -18,22 +17,9 @@ namespace remoting {
X11View::X11View()
: display_(NULL),
window_(0),
- width_(0),
- height_(0),
picture_(0) {
}
-X11View::X11View(Display* display, XID window, int width, int height)
- : display_(display),
- window_(window),
- width_(width),
- height_(height),
- picture_(0) {
- media::VideoFrame::CreateFrame(media::VideoFrame::RGB32, width_, height_,
- base::TimeDelta(), base::TimeDelta(), &frame_);
- DCHECK(frame_);
-}
-
X11View::~X11View() {
DCHECK(!display_);
DCHECK(!window_);
@@ -74,7 +60,7 @@ void X11View::TearDown() {
void X11View::Paint() {
// Don't bother attempting to paint if the display hasn't been set up.
- if (!display_ || !window_ || !height_ || !width_ || !frame_) {
+ if (!display_ || !window_ || !frame_height_ || !frame_width_ || !frame_) {
return;
}
@@ -91,8 +77,8 @@ void X11View::Paint() {
// Creates a XImage.
XImage image;
memset(&image, 0, sizeof(image));
- image.width = width_;
- image.height = height_;
+ image.width = frame_width_;
+ image.height = frame_height_;
image.depth = 32;
image.bits_per_pixel = 32;
image.format = ZPixmap;
@@ -107,10 +93,12 @@ void X11View::Paint() {
frame_->data(media::VideoFrame::kRGBPlane));
// Creates a pixmap and uploads from the XImage.
- unsigned long pixmap = XCreatePixmap(display_, window_, width_, height_, 32);
+ unsigned long pixmap = XCreatePixmap(display_, window_,
+ frame_width_, frame_height_, 32);
GC gc = XCreateGC(display_, pixmap, 0, NULL);
- XPutImage(display_, pixmap, gc, &image, 0, 0, 0, 0, width_, height_);
+ XPutImage(display_, pixmap, gc, &image, 0, 0, 0, 0,
+ frame_width_, frame_height_);
XFreeGC(display_, gc);
// Creates the picture representing the pixmap.
@@ -122,7 +110,7 @@ void X11View::Paint() {
// Composite the picture over the picture representing the window.
XRenderComposite(display_, PictOpSrc, picture, 0,
picture_, 0, 0, 0, 0, 0, 0,
- width_, height_);
+ frame_width_, frame_height_);
XRenderFreePicture(display_, picture);
XFreePixmap(display_, pixmap);
@@ -144,11 +132,11 @@ void X11View::SetViewport(int x, int y, int width, int height) {
}
void X11View::SetHostScreenSize(int width, int height) {
- width_ = width;
- height_ = height;
- XResizeWindow(display_, window_, width_, height_);
- media::VideoFrame::CreateFrame(media::VideoFrame::RGB32, width_, height_,
- base::TimeDelta(), base::TimeDelta(), &frame_);
+ frame_width_ = width;
+ frame_height_ = height;
+ frame_ = NULL;
+
+ XResizeWindow(display_, window_, frame_width_, frame_height_);
}
void X11View::InitPaintTarget() {
@@ -172,25 +160,30 @@ void X11View::InitPaintTarget() {
void X11View::HandleBeginUpdateStream(HostMessage* msg) {
scoped_ptr<HostMessage> deleter(msg);
- // TODO(hclam): Use the information from the message to create the decoder.
- // We lazily construct the decoder.
- if (!decoder_.get()) {
- decoder_.reset(new DecoderZlib());
+ // Make sure the |frame_| is initialized.
+ if (!frame_) {
+ media::VideoFrame::CreateFrame(media::VideoFrame::RGB32,
+ frame_width_, frame_height_,
+ base::TimeDelta(), base::TimeDelta(),
+ &frame_);
+ CHECK(frame_);
}
-
- // Tell the decoder to do start decoding.
- decoder_->BeginDecode(frame_, &update_rects_,
- NewRunnableMethod(this, &X11View::OnPartialDecodeDone),
- NewRunnableMethod(this, &X11View::OnDecodeDone));
}
void X11View::HandleUpdateStreamPacket(HostMessage* msg) {
- decoder_->PartialDecode(msg);
+ // Lazily initialize the decoder.
+ SetupDecoder(msg->update_stream_packet().begin_rect().encoding());
+ if (!decoder_->IsStarted()) {
+ BeginDecoding(NewRunnableMethod(this, &X11View::OnPartialDecodeDone),
+ NewRunnableMethod(this, &X11View::OnDecodeDone));
+ }
+
+ Decode(msg);
}
void X11View::HandleEndUpdateStream(HostMessage* msg) {
scoped_ptr<HostMessage> deleter(msg);
- decoder_->EndDecode();
+ EndDecoding();
}
void X11View::OnPartialDecodeDone() {
diff --git a/remoting/client/x11_view.h b/remoting/client/x11_view.h
index 2825575..d2a52d5 100644
--- a/remoting/client/x11_view.h
+++ b/remoting/client/x11_view.h
@@ -8,7 +8,6 @@
#include "base/basictypes.h"
#include "base/scoped_ptr.h"
#include "media/base/video_frame.h"
-#include "remoting/base/decoder.h"
#include "remoting/client/chromoting_view.h"
typedef unsigned long XID;
@@ -19,9 +18,7 @@ namespace remoting {
// A ChromotingView implemented using X11 and XRender.
class X11View : public ChromotingView {
public:
- X11View(); // Delete this.
- X11View(Display* display, XID window, int width, int height);
-
+ X11View();
virtual ~X11View();
// ChromotingView implementations.
@@ -34,7 +31,7 @@ class X11View : public ChromotingView {
virtual void SetHostScreenSize(int width, int height);
virtual void HandleBeginUpdateStream(HostMessage* msg);
virtual void HandleUpdateStreamPacket(HostMessage* msg);
- virtual void HandleEndUpdateStream(HostMessage* msg) ;
+ virtual void HandleEndUpdateStream(HostMessage* msg);
Display* display() { return display_; }
@@ -45,19 +42,11 @@ class X11View : public ChromotingView {
Display* display_;
XID window_;
- int width_;
- int height_;
// A picture created in the X server that represents drawing area of the
// window.
XID picture_;
- scoped_refptr<media::VideoFrame> frame_;
- UpdatedRects update_rects_;
- UpdatedRects all_update_rects_;
-
- scoped_ptr<Decoder> decoder_;
-
DISALLOW_COPY_AND_ASSIGN(X11View);
};