summaryrefslogtreecommitdiffstats
path: root/remoting/client/rectangle_update_decoder.cc
diff options
context:
space:
mode:
authoralexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-24 23:05:56 +0000
committeralexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-24 23:05:56 +0000
commit55d3688e99e891b269cc93c1f695c65a00ea3b48 (patch)
tree31c34f0901866f698d241b7b45a71a2ba6d3cce7 /remoting/client/rectangle_update_decoder.cc
parent1fd0ff3f0814835c474e527b08c6a2f560246e94 (diff)
downloadchromium_src-55d3688e99e891b269cc93c1f695c65a00ea3b48.zip
chromium_src-55d3688e99e891b269cc93c1f695c65a00ea3b48.tar.gz
chromium_src-55d3688e99e891b269cc93c1f695c65a00ea3b48.tar.bz2
This CL makes several the following improvements to the Chromoting decoder pipeline:
1. Only the clipping area, not the full frame, is drawn. This reduces the risk of out of memory situation on high page zoom levels. Screen updates are also incremental including scrolling scenarios. 2. Decoders now write pixels directly to the Pepper API backing store making it one memcpy less. 3. Scaling and panning is fully controlled by the plugin. The decoder only supplies the pixels it was asked for by the plugin. BUG=109938 Review URL: http://codereview.chromium.org/9331003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@123573 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/client/rectangle_update_decoder.cc')
-rw-r--r--remoting/client/rectangle_update_decoder.cc206
1 files changed, 85 insertions, 121 deletions
diff --git a/remoting/client/rectangle_update_decoder.cc b/remoting/client/rectangle_update_decoder.cc
index 673c2a0..6f58564 100644
--- a/remoting/client/rectangle_update_decoder.cc
+++ b/remoting/client/rectangle_update_decoder.cc
@@ -10,6 +10,7 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop_proxy.h"
+#include "ppapi/cpp/image_data.h"
#include "remoting/base/decoder.h"
#include "remoting/base/decoder_row_based.h"
#include "remoting/base/decoder_vp8.h"
@@ -17,6 +18,7 @@
#include "remoting/client/frame_consumer.h"
#include "remoting/protocol/session_config.h"
+using base::Passed;
using remoting::protocol::ChannelConfig;
using remoting::protocol::SessionConfig;
@@ -26,9 +28,9 @@ RectangleUpdateDecoder::RectangleUpdateDecoder(
base::MessageLoopProxy* message_loop, FrameConsumer* consumer)
: message_loop_(message_loop),
consumer_(consumer),
- screen_size_(SkISize::Make(0, 0)),
- clip_rect_(SkIRect::MakeEmpty()),
- decoder_needs_reset_(false) {
+ source_size_(SkISize::Make(0, 0)),
+ view_size_(SkISize::Make(0, 0)),
+ clip_area_(SkIRect::MakeEmpty()) {
}
RectangleUpdateDecoder::~RectangleUpdateDecoder() {
@@ -56,67 +58,29 @@ void RectangleUpdateDecoder::DecodePacket(const VideoPacket* packet,
this, packet, done));
return;
}
- AllocateFrame(packet, done);
-}
-void RectangleUpdateDecoder::AllocateFrame(const VideoPacket* packet,
- const base::Closure& done) {
- if (!message_loop_->BelongsToCurrentThread()) {
- message_loop_->PostTask(
- FROM_HERE, base::Bind(&RectangleUpdateDecoder::AllocateFrame,
- this, packet, done));
- return;
- }
base::ScopedClosureRunner done_runner(done);
+ bool decoder_needs_reset = false;
// If the packet includes a screen size, store it.
if (packet->format().has_screen_width() &&
packet->format().has_screen_height()) {
- screen_size_.set(packet->format().screen_width(),
- packet->format().screen_height());
- }
-
- // If we've never seen a screen size, ignore the packet.
- if (screen_size_.isZero()) {
- return;
- }
-
- // Ensure the output frame is the right size.
- SkISize frame_size = SkISize::Make(0, 0);
- if (frame_)
- frame_size.set(frame_->width(), frame_->height());
-
- // Allocate a new frame, if necessary.
- if ((!frame_) || (screen_size_ != frame_size)) {
- if (frame_) {
- consumer_->ReleaseFrame(frame_);
- frame_ = NULL;
+ SkISize source_size = SkISize::Make(packet->format().screen_width(),
+ packet->format().screen_height());
+ if (source_size_ != source_size) {
+ source_size_ = source_size;
+ decoder_needs_reset = true;
}
-
- consumer_->AllocateFrame(
- media::VideoFrame::RGB32, screen_size_, &frame_,
- base::Bind(&RectangleUpdateDecoder::ProcessPacketData,
- this, packet, done_runner.Release()));
- decoder_needs_reset_ = true;
- return;
}
- ProcessPacketData(packet, done_runner.Release());
-}
-void RectangleUpdateDecoder::ProcessPacketData(
- const VideoPacket* packet, const base::Closure& done) {
- if (!message_loop_->BelongsToCurrentThread()) {
- message_loop_->PostTask(
- FROM_HERE, base::Bind(&RectangleUpdateDecoder::ProcessPacketData,
- this, packet, done));
+ // If we've never seen a screen size, ignore the packet.
+ if (source_size_.isZero()) {
return;
}
- base::ScopedClosureRunner done_runner(done);
- if (decoder_needs_reset_) {
- decoder_->Reset();
- decoder_->Initialize(frame_);
- decoder_needs_reset_ = false;
+ if (decoder_needs_reset) {
+ decoder_->Initialize(source_size_);
+ consumer_->SetSourceSize(source_size_);
}
if (!decoder_->IsReadyForData()) {
@@ -126,109 +90,109 @@ void RectangleUpdateDecoder::ProcessPacketData(
}
if (decoder_->DecodePacket(packet) == Decoder::DECODE_DONE)
- SubmitToConsumer();
+ DoPaint();
}
-void RectangleUpdateDecoder::SetOutputSize(const SkISize& size) {
- if (!message_loop_->BelongsToCurrentThread()) {
- message_loop_->PostTask(
- FROM_HERE, base::Bind(&RectangleUpdateDecoder::SetOutputSize,
- this, size));
+void RectangleUpdateDecoder::DoPaint() {
+ if (buffers_.empty())
return;
- }
- // TODO(wez): Refresh the frame only if the ratio has changed.
- if (frame_) {
- SkIRect frame_rect = SkIRect::MakeWH(frame_->width(), frame_->height());
- refresh_region_.op(frame_rect, SkRegion::kUnion_Op);
- }
+ // Draw the invalidated region to the buffer.
+ pp::ImageData* buffer = buffers_.front();
+ SkRegion output_region;
+ decoder_->RenderFrame(view_size_, clip_area_,
+ reinterpret_cast<uint8*>(buffer->data()),
+ buffer->stride(),
+ &output_region);
- // TODO(hclam): If the scale ratio has changed we should reallocate a
- // VideoFrame of different size. However if the scale ratio is always
- // smaller than 1.0 we can use the same video frame.
- if (decoder_.get()) {
- decoder_->SetOutputSize(size);
- RefreshFullFrame();
+ // Notify the consumer that painting is done.
+ if (!output_region.isEmpty()) {
+ buffers_.pop_front();
+ consumer_->ApplyBuffer(view_size_, clip_area_, buffer, output_region);
}
}
-void RectangleUpdateDecoder::UpdateClipRect(const SkIRect& new_clip_rect) {
+void RectangleUpdateDecoder::RequestReturnBuffers(const base::Closure& done) {
if (!message_loop_->BelongsToCurrentThread()) {
message_loop_->PostTask(
- FROM_HERE, base::Bind(&RectangleUpdateDecoder::UpdateClipRect,
- this, new_clip_rect));
+ FROM_HERE, base::Bind(&RectangleUpdateDecoder::RequestReturnBuffers,
+ this, done));
return;
}
- if (new_clip_rect == clip_rect_ || !decoder_.get())
- return;
-
- // TODO(wez): Only refresh newly-exposed portions of the frame.
- if (frame_) {
- SkIRect frame_rect = SkIRect::MakeWH(frame_->width(), frame_->height());
- refresh_region_.op(frame_rect, SkRegion::kUnion_Op);
+ while (!buffers_.empty()) {
+ consumer_->ReturnBuffer(buffers_.front());
+ buffers_.pop_front();
}
- clip_rect_ = new_clip_rect;
- decoder_->SetClipRect(new_clip_rect);
-
- // TODO(wez): Defer refresh so that multiple events can be batched.
- DoRefresh();
+ if (!done.is_null())
+ done.Run();
}
-void RectangleUpdateDecoder::RefreshFullFrame() {
+void RectangleUpdateDecoder::DrawBuffer(pp::ImageData* buffer) {
if (!message_loop_->BelongsToCurrentThread()) {
message_loop_->PostTask(
- FROM_HERE, base::Bind(&RectangleUpdateDecoder::RefreshFullFrame, this));
+ FROM_HERE, base::Bind(&RectangleUpdateDecoder::DrawBuffer,
+ this, buffer));
return;
}
- // If a video frame or the decoder is not allocated yet then don't
- // save the refresh rectangle to avoid wasted computation.
- if (!frame_ || !decoder_.get())
- return;
-
- SkIRect frame_rect = SkIRect::MakeWH(frame_->width(), frame_->height());
- refresh_region_.op(frame_rect, SkRegion::kUnion_Op);
-
- DoRefresh();
-}
-
-void RectangleUpdateDecoder::SubmitToConsumer() {
- // A frame is not allocated yet, we can reach here because of a refresh
- // request.
- if (!frame_)
- return;
-
- SkRegion* dirty_region = new SkRegion;
- decoder_->GetUpdatedRegion(dirty_region);
+ DCHECK(clip_area_.width() <= buffer->size().width() &&
+ clip_area_.height() <= buffer->size().height());
- consumer_->OnPartialFrameOutput(frame_, dirty_region, base::Bind(
- &RectangleUpdateDecoder::OnFrameConsumed, this, dirty_region));
+ buffers_.push_back(buffer);
+ DoPaint();
}
-void RectangleUpdateDecoder::DoRefresh() {
- DCHECK(message_loop_->BelongsToCurrentThread());
-
- if (refresh_region_.isEmpty())
+void RectangleUpdateDecoder::InvalidateRegion(const SkRegion& region) {
+ if (!message_loop_->BelongsToCurrentThread()) {
+ message_loop_->PostTask(
+ FROM_HERE, base::Bind(&RectangleUpdateDecoder::InvalidateRegion,
+ this, region));
return;
+ }
- decoder_->RefreshRegion(refresh_region_);
- refresh_region_.setEmpty();
- SubmitToConsumer();
+ if (decoder_.get()) {
+ decoder_->Invalidate(view_size_, region);
+ DoPaint();
+ }
}
-void RectangleUpdateDecoder::OnFrameConsumed(SkRegion* region) {
+void RectangleUpdateDecoder::SetOutputSizeAndClip(const SkISize& view_size,
+ const SkIRect& clip_area) {
if (!message_loop_->BelongsToCurrentThread()) {
message_loop_->PostTask(
- FROM_HERE, base::Bind(&RectangleUpdateDecoder::OnFrameConsumed,
- this, region));
+ FROM_HERE, base::Bind(&RectangleUpdateDecoder::SetOutputSizeAndClip,
+ this, view_size, clip_area));
return;
}
- delete region;
-
- DoRefresh();
+ // The whole frame needs to be repainted if the scaling factor has changed.
+ if (view_size_ != view_size && decoder_.get()) {
+ SkRegion region;
+ region.op(SkIRect::MakeSize(view_size), SkRegion::kUnion_Op);
+ decoder_->Invalidate(view_size, region);
+ }
+
+ if (view_size_ != view_size ||
+ clip_area_ != clip_area) {
+ view_size_ = view_size;
+ clip_area_ = clip_area;
+
+ // Return buffers that are smaller than needed to the consumer for
+ // reuse/reallocation.
+ std::list<pp::ImageData*>::iterator i = buffers_.begin();
+ while (i != buffers_.end()) {
+ pp::Size buffer_size = (*i)->size();
+ if (buffer_size.width() < clip_area_.width() ||
+ buffer_size.height() < clip_area_.height()) {
+ consumer_->ReturnBuffer(*i);
+ i = buffers_.erase(i);
+ } else {
+ ++i;
+ }
+ }
+ }
}
} // namespace remoting