diff options
-rw-r--r-- | remoting/host/capturer.h | 10 | ||||
-rw-r--r-- | remoting/host/client_connection.cc | 2 | ||||
-rw-r--r-- | remoting/host/client_connection.h | 2 | ||||
-rw-r--r-- | remoting/host/client_connection_unittest.cc | 6 | ||||
-rw-r--r-- | remoting/host/encoder.h | 28 | ||||
-rw-r--r-- | remoting/host/encoder_verbatim.cc | 40 | ||||
-rw-r--r-- | remoting/host/encoder_verbatim.h | 13 | ||||
-rw-r--r-- | remoting/host/mock_objects.h | 17 | ||||
-rw-r--r-- | remoting/host/session_manager.cc | 92 | ||||
-rw-r--r-- | remoting/host/session_manager.h | 55 | ||||
-rw-r--r-- | remoting/host/session_manager_unittest.cc | 46 |
11 files changed, 166 insertions, 145 deletions
diff --git a/remoting/host/capturer.h b/remoting/host/capturer.h index 6007b350..77a8aad 100644 --- a/remoting/host/capturer.h +++ b/remoting/host/capturer.h @@ -31,10 +31,10 @@ class Capturer { // Capture the full screen. When the action is completed |done_task| // is called. // - // It is OK to call this methods while another thread is reading + // It is OK to call this method while another thread is reading // data of the last capture. // There can be at most one concurrent read going on when this - // methods is called. + // method is called. virtual void CaptureFullScreen(Task* done_task) = 0; // Capture the updated regions since last capture. If the last @@ -45,7 +45,7 @@ class Capturer { // It is OK to call this method while another thread is reading // data of the last capture. // There can be at most one concurrent read going on when this - // methods is called. + // method is called. virtual void CaptureDirtyRects(Task* done_task) = 0; // Capture the specified screen rect and call |done_task| when complete. @@ -55,14 +55,14 @@ class Capturer { // It is OK to call this method while another thread is reading // data of the last capture. // There can be at most one concurrent read going on when this - // methods is called. + // method is called. virtual void CaptureRect(const gfx::Rect& rect, Task* done_task) = 0; // Get the image data of the last capture. The pointers to data is // written to |planes|. |planes| should be an array of 3 elements. virtual void GetData(const uint8* planes[]) const = 0; - // Get the image data stride of the last capture. This size of strides + // Get the image data stride of the last capture. The size of strides // is written to |strides|. |strides| should be array of 3 elements. virtual void GetDataStride(int strides[]) const = 0; diff --git a/remoting/host/client_connection.cc b/remoting/host/client_connection.cc index 5bf962c..23b8b80 100644 --- a/remoting/host/client_connection.cc +++ b/remoting/host/client_connection.cc @@ -62,7 +62,7 @@ void ClientConnection::SendBeginUpdateStreamMessage() { } void ClientConnection::SendUpdateStreamPacketMessage( - UpdateStreamPacketHeader* header, + const UpdateStreamPacketHeader* header, scoped_refptr<DataBuffer> data) { DCHECK_EQ(loop_, MessageLoop::current()); DCHECK(channel_.get()); diff --git a/remoting/host/client_connection.h b/remoting/host/client_connection.h index 4b46ee3..810eb61 100644 --- a/remoting/host/client_connection.h +++ b/remoting/host/client_connection.h @@ -78,7 +78,7 @@ class ClientConnection : public base::RefCountedThreadSafe<ClientConnection>, // Send encoded update stream data to the viewer. The viewer // should not take ownership of the data. virtual void SendUpdateStreamPacketMessage( - UpdateStreamPacketHeader* header, + const UpdateStreamPacketHeader* header, scoped_refptr<media::DataBuffer> data); // Notifies the viewer the update stream has ended. diff --git a/remoting/host/client_connection_unittest.cc b/remoting/host/client_connection_unittest.cc index d7be888..f82c887 100644 --- a/remoting/host/client_connection_unittest.cc +++ b/remoting/host/client_connection_unittest.cc @@ -51,14 +51,14 @@ TEST_F(ClientConnectionTest, SendUpdateStream) { // Then send the actual data. EXPECT_CALL(*channel_, Write(_)); - UpdateStreamPacketHeader* header = new UpdateStreamPacketHeader(); + scoped_ptr<UpdateStreamPacketHeader> header(new UpdateStreamPacketHeader); header->set_x(0); header->set_y(0); header->set_width(640); header->set_height(480); + scoped_refptr<media::DataBuffer> data = new media::DataBuffer(10); - viewer_->SendUpdateStreamPacketMessage(header, data); - delete header; + viewer_->SendUpdateStreamPacketMessage(header.get(), data); // Send the end of update message. EXPECT_CALL(*channel_, Write(_)); diff --git a/remoting/host/encoder.h b/remoting/host/encoder.h index 53ddb63..808b34f 100644 --- a/remoting/host/encoder.h +++ b/remoting/host/encoder.h @@ -6,7 +6,8 @@ #define REMOTING_HOST_ENCODER_H_ #include "base/basictypes.h" -#include "base/task.h" +#include "base/callback.h" +#include "media/base/data_buffer.h" #include "remoting/base/protocol/chromotocol.pb.h" #include "remoting/host/capturer.h" @@ -23,6 +24,24 @@ namespace remoting { // This class operates asynchronously to enable maximum throughput. class Encoder { public: + + // EncodingState is a bitfield that tracks the state of the encoding. + // An encoding that consists of a single block could concievably be starting + // inprogress and ended at the same time. + enum { + EncodingStarting = 1 << 0, + EncodingInProgress = 1 << 1, + EncodingEnded = 1 << 2 + }; + typedef int EncodingState; + + // DataAvailableCallback is called as blocks of data are made available + // from the encoder. The callback takes ownership of header and is + // responsible for deleting it. + typedef Callback3<const UpdateStreamPacketHeader*, + const scoped_refptr<media::DataBuffer>&, + EncodingState>::Type DataAvailableCallback; + virtual ~Encoder() {} // Encode an image stored in |input_data|. |dirty_rects| contains @@ -41,13 +60,10 @@ class Encoder { // Implementation has to ensure that when |data_available_task| is called // output parameters are stable. virtual void Encode(const DirtyRects& dirty_rects, - const uint8** input_data, + const uint8* const* input_data, const int* strides, bool key_frame, - UpdateStreamPacketHeader* header, - scoped_refptr<media::DataBuffer>* output_data, - bool* encode_done, - Task* data_available_task) = 0; + DataAvailableCallback* data_available_callback) = 0; // Set the dimension of the incoming images. Need to call this before // calling Encode(). diff --git a/remoting/host/encoder_verbatim.cc b/remoting/host/encoder_verbatim.cc index 58525f1..f054ae8 100644 --- a/remoting/host/encoder_verbatim.cc +++ b/remoting/host/encoder_verbatim.cc @@ -13,22 +13,35 @@ namespace remoting { using media::DataBuffer; void EncoderVerbatim::Encode(const DirtyRects& dirty_rects, - const uint8** input_data, + const uint8* const* input_data, const int* strides, bool key_frame, - UpdateStreamPacketHeader* header, - scoped_refptr<DataBuffer>* output_data, - bool* encode_done, - Task* data_available_task) { + DataAvailableCallback* data_available_callback) { int num_rects = dirty_rects.size(); for (int i = 0; i < num_rects; i++) { - if (EncodeRect(dirty_rects[i], input_data, strides, header, output_data)) { - *encode_done = (i == num_rects - 1); // Set for last rect. - data_available_task->Run(); + scoped_refptr<DataBuffer> data; + gfx::Rect dirty_rect = dirty_rects[i]; + PixelFormat pixel_format; + UpdateStreamEncoding encoding; + scoped_ptr<UpdateStreamPacketHeader> header(new UpdateStreamPacketHeader); + if (EncodeRect(dirty_rect, + input_data, + strides, + header.get(), + &data)) { + EncodingState state = EncodingInProgress; + if (i == 0) { + state |= EncodingStarting; + } else if (i == num_rects - 1) { + state |= EncodingEnded; + } + data_available_callback->Run(header.release(), + data, + state); } } - delete data_available_task; + delete data_available_callback; } void EncoderVerbatim::SetSize(int width, int height) { @@ -54,9 +67,9 @@ void EncoderVerbatim::SetPixelFormat(PixelFormat pixel_format) { } bool EncoderVerbatim::EncodeRect(const gfx::Rect& dirty, - const uint8** input_data, + const uint8* const* input_data, const int* strides, - UpdateStreamPacketHeader* header, + UpdateStreamPacketHeader *header, scoped_refptr<DataBuffer>* output_data) { const int kPlanes = 3; @@ -74,10 +87,9 @@ bool EncoderVerbatim::EncodeRect(const gfx::Rect& dirty, header->set_encoding(EncodingNone); header->set_pixel_format(pixel_format_); - *output_data = new DataBuffer(output_size); - (*output_data)->SetDataSize(output_size); - + *output_data = new DataBuffer(new uint8[output_size], output_size); uint8* out = (*output_data)->GetWritableData(); + for (int i = 0; i < kPlanes; ++i) { const uint8* in = input_data[i]; // Skip over planes that don't have data. diff --git a/remoting/host/encoder_verbatim.h b/remoting/host/encoder_verbatim.h index d7a1760..6d371ea 100644 --- a/remoting/host/encoder_verbatim.h +++ b/remoting/host/encoder_verbatim.h @@ -18,13 +18,10 @@ class EncoderVerbatim : public Encoder { virtual ~EncoderVerbatim() {} virtual void Encode(const DirtyRects& dirty_rects, - const uint8** input_data, + const uint8* const* input_data, const int* strides, bool key_frame, - UpdateStreamPacketHeader* header, - scoped_refptr<media::DataBuffer>* output_data, - bool* encode_done, - Task* data_available_task); + DataAvailableCallback* data_available_callback); virtual void SetSize(int width, int height); virtual void SetPixelFormat(PixelFormat pixel_format); @@ -32,10 +29,10 @@ class EncoderVerbatim : public Encoder { // Encode a single dirty rect. Called by Encode(). // Returns false if there is an error. bool EncodeRect(const gfx::Rect& dirty, - const uint8** input_data, + const uint8* const* input_data, const int* strides, - UpdateStreamPacketHeader* header, - scoped_refptr<media::DataBuffer>* output_data); + UpdateStreamPacketHeader *header, + scoped_refptr<media::DataBuffer> *output_data); int width_; int height_; diff --git a/remoting/host/mock_objects.h b/remoting/host/mock_objects.h index d492376..597c25e 100644 --- a/remoting/host/mock_objects.h +++ b/remoting/host/mock_objects.h @@ -37,15 +37,12 @@ class MockEncoder : public Encoder { public: MockEncoder() {} - MOCK_METHOD8(Encode, void( - const DirtyRects& dirty_rects, - const uint8** planes, - const int* strides, - bool key_frame, - UpdateStreamPacketHeader* output_data_header, - scoped_refptr<media::DataBuffer>* output_data, - bool* encode_done, - Task* data_available_task)); + MOCK_METHOD5(Encode, void( + const DirtyRects& dirty_rects, + const uint8* const* input_data, + const int* strides, + bool key_frame, + DataAvailableCallback* data_available_callback)); MOCK_METHOD2(SetSize, void(int width, int height)); MOCK_METHOD1(SetPixelFormat, void(PixelFormat pixel_format)); @@ -70,7 +67,7 @@ class MockClientConnection : public ClientConnection { MOCK_METHOD2(SendInitClientMessage, void(int width, int height)); MOCK_METHOD0(SendBeginUpdateStreamMessage, void()); MOCK_METHOD2(SendUpdateStreamPacketMessage, - void(UpdateStreamPacketHeader* header, + void(const UpdateStreamPacketHeader* header, scoped_refptr<media::DataBuffer> data)); MOCK_METHOD0(SendEndUpdateStreamMessage, void()); MOCK_METHOD0(GetPendingUpdateStreamMessages, int()); diff --git a/remoting/host/session_manager.cc b/remoting/host/session_manager.cc index 0160993..c434742 100644 --- a/remoting/host/session_manager.cc +++ b/remoting/host/session_manager.cc @@ -7,6 +7,7 @@ #include <algorithm> #include "base/logging.h" +#include "base/scoped_ptr.h" #include "base/stl_util-inl.h" #include "media/base/data_buffer.h" #include "remoting/base/protocol_decoder.h" @@ -46,12 +47,7 @@ SessionManager::SessionManager( max_rate_(kDefaultCaptureRate), started_(false), recordings_(0), - rate_control_started_(false), - capture_width_(0), - capture_height_(0), - capture_pixel_format_(PixelFormatInvalid), - encode_stream_started_(false), - encode_done_(false) { + rate_control_started_(false) { DCHECK(capture_loop_); DCHECK(encode_loop_); DCHECK(network_loop_); @@ -188,47 +184,45 @@ void SessionManager::DoFinishEncode() { DoCapture(); } -void SessionManager::DoEncode() { - DCHECK_EQ(encode_loop_, MessageLoop::current()); +void SessionManager::DoEncode(const CaptureData *capture_data) { + // Take ownership of capture_data. + scoped_ptr<const CaptureData> capture_data_owner(capture_data); - // Reset states about the encode stream. - encode_done_ = false; - encode_stream_started_ = false; + DCHECK_EQ(encode_loop_, MessageLoop::current()); - DCHECK(!encoded_data_.get()); DCHECK(encoder_.get()); // TODO(hclam): Enable |force_refresh| if a new client was // added. - encoder_->SetSize(capture_width_, capture_height_); - encoder_->SetPixelFormat(capture_pixel_format_); + encoder_->SetSize(capture_data->width_, capture_data->height_); + encoder_->SetPixelFormat(capture_data->pixel_format_); encoder_->Encode( - capture_dirty_rects_, - capture_data_, - capture_data_strides_, + capture_data->dirty_rects_, + capture_data->data_, + capture_data->data_strides_, false, - &encoded_data_header_, - &encoded_data_, - &encode_done_, - NewRunnableMethod(this, &SessionManager::EncodeDataAvailableTask)); + NewCallback(this, &SessionManager::EncodeDataAvailableTask)); } -void SessionManager::DoSendUpdate( - UpdateStreamPacketHeader* header, - scoped_refptr<media::DataBuffer> encoded_data, - bool begin_update, bool end_update) { +void SessionManager::DoSendUpdate(const UpdateStreamPacketHeader* header, + const scoped_refptr<media::DataBuffer> data, + Encoder::EncodingState state) { + // Take ownership of header. + scoped_ptr<const UpdateStreamPacketHeader> header_owner(header); DCHECK_EQ(network_loop_, MessageLoop::current()); for (size_t i = 0; i < clients_.size(); ++i) { - if (begin_update) + if (state & Encoder::EncodingStarting) { clients_[i]->SendBeginUpdateStreamMessage(); + } - // This will pass the ownership of the DataBuffer to the ClientConnection. - clients_[i]->SendUpdateStreamPacketMessage(header, encoded_data); + clients_[i]->SendUpdateStreamPacketMessage(header, data); - if (end_update) + if (state & Encoder::EncodingEnded) { clients_[i]->SendEndUpdateStreamMessage(); + } } + delete header; } void SessionManager::DoSendInit(scoped_refptr<ClientConnection> client, @@ -358,20 +352,25 @@ void SessionManager::ScheduleNextRateControl() { void SessionManager::CaptureDoneTask() { DCHECK_EQ(capture_loop_, MessageLoop::current()); + scoped_ptr<CaptureData> data(new CaptureData); + // Save results of the capture. - capturer_->GetData(capture_data_); - capturer_->GetDataStride(capture_data_strides_); - capture_dirty_rects_.clear(); - capturer_->GetDirtyRects(&capture_dirty_rects_); - capture_pixel_format_ = capturer_->GetPixelFormat(); - capture_width_ = capturer_->GetWidth(); - capture_height_ = capturer_->GetHeight(); + capturer_->GetData(data->data_); + capturer_->GetDataStride(data->data_strides_); + capturer_->GetDirtyRects(&data->dirty_rects_); + data->pixel_format_ = capturer_->GetPixelFormat(); + data->width_ = capturer_->GetWidth(); + data->height_ = capturer_->GetHeight(); encode_loop_->PostTask( - FROM_HERE, NewRunnableMethod(this, &SessionManager::DoEncode)); + FROM_HERE, + NewRunnableMethod(this, &SessionManager::DoEncode, data.release())); } -void SessionManager::EncodeDataAvailableTask() { +void SessionManager::EncodeDataAvailableTask( + const UpdateStreamPacketHeader *header, + const scoped_refptr<media::DataBuffer>& data, + Encoder::EncodingState state) { DCHECK_EQ(encode_loop_, MessageLoop::current()); // Before a new encode task starts, notify clients a new update @@ -382,20 +381,11 @@ void SessionManager::EncodeDataAvailableTask() { FROM_HERE, NewRunnableMethod(this, &SessionManager::DoSendUpdate, - &encoded_data_header_, - encoded_data_, - !encode_stream_started_, - encode_done_)); - - // Since we have received data from the Encoder, mark the encode - // stream has started. - encode_stream_started_ = true; - - // Give up the ownership of DataBuffer since it is passed to - // the ClientConnections. - encoded_data_ = NULL; + header, + data, + state)); - if (encode_done_) { + if (state == Encoder::EncodingEnded) { capture_loop_->PostTask( FROM_HERE, NewRunnableMethod(this, &SessionManager::DoFinishEncode)); } diff --git a/remoting/host/session_manager.h b/remoting/host/session_manager.h index 677e3bd..54aae70 100644 --- a/remoting/host/session_manager.h +++ b/remoting/host/session_manager.h @@ -14,6 +14,7 @@ #include "base/time.h" #include "remoting/base/protocol/chromotocol.pb.h" #include "remoting/host/capturer.h" +#include "remoting/host/encoder.h" namespace media { @@ -95,6 +96,18 @@ class SessionManager : public base::RefCountedThreadSafe<SessionManager> { void RemoveClient(scoped_refptr<ClientConnection> client); private: + + // Stores the data and information of a capture to pass off to the + // encoding thread. + struct CaptureData { + DirtyRects dirty_rects_; + const uint8* data_[3]; + int data_strides_[3]; + int width_; + int height_; + PixelFormat pixel_format_; + }; + void DoStart(); void DoPause(); void DoStartRateControl(); @@ -102,12 +115,14 @@ class SessionManager : public base::RefCountedThreadSafe<SessionManager> { void DoCapture(); void DoFinishEncode(); - void DoEncode(); - void DoSendUpdate( - UpdateStreamPacketHeader* header, - scoped_refptr<media::DataBuffer> encoded_data, - bool begin_update, - bool end_update); + + // DoEncode takes ownership of capture_data and is responsible for deleting + // it. + void DoEncode(const CaptureData *capture_data); + // DoSendUpdate takes ownership of header and is responsible for deleting it. + void DoSendUpdate(const UpdateStreamPacketHeader* header, + const scoped_refptr<media::DataBuffer> data, + Encoder::EncodingState state); void DoSendInit(scoped_refptr<ClientConnection> client, int width, int height); void DoGetInitInfo(scoped_refptr<ClientConnection> client); @@ -124,7 +139,11 @@ class SessionManager : public base::RefCountedThreadSafe<SessionManager> { void ScheduleNextRateControl(); void CaptureDoneTask(); - void EncodeDataAvailableTask(); + // EncodeDataAvailableTask takes ownership of header and is responsible for + // deleting it. + void EncodeDataAvailableTask(const UpdateStreamPacketHeader *header, + const scoped_refptr<media::DataBuffer>& data, + Encoder::EncodingState state); // Message loops used by this class. MessageLoop* capture_loop_; @@ -160,28 +179,6 @@ class SessionManager : public base::RefCountedThreadSafe<SessionManager> { // The following member is accessed on the network thread. bool rate_control_started_; - // Stores the data and information of the last capture done. - // These members are written on capture thread and read on encode thread. - // It is guranteed the read happens after the write. - DirtyRects capture_dirty_rects_; - const uint8* capture_data_[3]; - int capture_data_strides_[3]; - int capture_width_; - int capture_height_; - PixelFormat capture_pixel_format_; - - // The following members are accessed on the encode thread. - // Output parameter written by Encoder to carry encoded data. - UpdateStreamPacketHeader encoded_data_header_; - scoped_refptr<media::DataBuffer> encoded_data_; - - // True if we have started receiving encoded data from the Encoder. - bool encode_stream_started_; - - // Output parameter written by Encoder to notify the end of encoded data - // stream. - bool encode_done_; - DISALLOW_COPY_AND_ASSIGN(SessionManager); }; diff --git a/remoting/host/session_manager_unittest.cc b/remoting/host/session_manager_unittest.cc index 008d063..a4134a8 100644 --- a/remoting/host/session_manager_unittest.cc +++ b/remoting/host/session_manager_unittest.cc @@ -28,8 +28,8 @@ static uint8* kData[3] = { reinterpret_cast<uint8*>(0x02), reinterpret_cast<uint8*>(0x03), }; -static const PixelFormat kFormat = - PixelFormatRgb32; +static const PixelFormat kFormat = PixelFormatRgb32; +static const UpdateStreamEncoding kEncoding = EncodingNone; class SessionManagerTest : public testing::Test { public: @@ -67,12 +67,18 @@ ACTION(RunSimpleTask) { delete arg0; } -ACTION_P2(FinishDecode, header, data) { - *arg4 = header; - *arg5 = data; - *arg6 = true; - arg7->Run(); - delete arg7; +ACTION_P3(FinishDecode, rects, buffer, header) { + gfx::Rect& rect = (*rects)[0]; + Encoder::EncodingState state = (Encoder::EncodingStarting | + Encoder::EncodingInProgress | + Encoder::EncodingEnded); + header->set_x(rect.x()); + header->set_y(rect.y()); + header->set_width(rect.width()); + header->set_height(rect.height()); + header->set_encoding(kEncoding); + header->set_pixel_format(kFormat); + arg4->Run(header, *buffer, state); } ACTION_P(AssignCaptureData, data) { @@ -81,6 +87,10 @@ ACTION_P(AssignCaptureData, data) { arg0[2] = data[2]; } +ACTION_P(AssignDirtyRect, rects) { + *arg0 = *rects; +} + TEST_F(SessionManagerTest, OneRecordCycle) { Init(); @@ -96,8 +106,12 @@ TEST_F(SessionManagerTest, OneRecordCycle) { // First the capturer is called. EXPECT_CALL(*capturer_, CaptureDirtyRects(NotNull())) .WillOnce(RunSimpleTask()); - // TODO(hclam): Return DirtyRects for verification. - EXPECT_CALL(*capturer_, GetDirtyRects(NotNull())); + + // Create a dirty rect that can be verified. + DirtyRects rects; + rects.push_back(gfx::Rect(0, 0, 10, 10)); + EXPECT_CALL(*capturer_, GetDirtyRects(NotNull())) + .WillOnce(AssignDirtyRect(&rects)); EXPECT_CALL(*capturer_, GetData(NotNull())) .WillOnce(AssignCaptureData(kData)); EXPECT_CALL(*capturer_, GetDataStride(NotNull())) @@ -106,19 +120,17 @@ TEST_F(SessionManagerTest, OneRecordCycle) { .WillOnce(Return(kFormat)); // Expect the encoder be called. - UpdateStreamPacketHeader header; scoped_refptr<media::DataBuffer> buffer = new media::DataBuffer(0); EXPECT_CALL(*encoder_, SetSize(kWidth, kHeight)); EXPECT_CALL(*encoder_, SetPixelFormat(kFormat)); - // TODO(hclam): Expect the content of the dirty rects. - EXPECT_CALL(*encoder_, - Encode(_, NotNull(), NotNull(), false, NotNull(), - NotNull(), NotNull(), NotNull())) - .WillOnce(FinishDecode(header, buffer)); + UpdateStreamPacketHeader *header = new UpdateStreamPacketHeader; + EXPECT_CALL(*encoder_, Encode(rects, NotNull(), NotNull(), false, NotNull())) + .WillOnce(FinishDecode(&rects, &buffer, header)); // Expect the client be notified. EXPECT_CALL(*client_, SendBeginUpdateStreamMessage()); - EXPECT_CALL(*client_, SendUpdateStreamPacketMessage(NotNull(), buffer)); + + EXPECT_CALL(*client_, SendUpdateStreamPacketMessage(header ,buffer)); EXPECT_CALL(*client_, SendEndUpdateStreamMessage()); EXPECT_CALL(*client_, GetPendingUpdateStreamMessages()) .Times(AtLeast(0)) |