Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "remoting/client/rectangle_update_decoder.h" | 5 #include "remoting/client/rectangle_update_decoder.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
| 9 #include "base/callback.h" | 9 #include "base/callback.h" |
| 10 #include "base/location.h" | 10 #include "base/location.h" |
| 11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/message_loop_proxy.h" | 12 #include "base/message_loop_proxy.h" |
| 13 #include "ppapi/cpp/image_data.h" | |
| 13 #include "remoting/base/decoder.h" | 14 #include "remoting/base/decoder.h" |
| 14 #include "remoting/base/decoder_row_based.h" | 15 #include "remoting/base/decoder_row_based.h" |
| 15 #include "remoting/base/decoder_vp8.h" | 16 #include "remoting/base/decoder_vp8.h" |
| 16 #include "remoting/base/util.h" | 17 #include "remoting/base/util.h" |
| 17 #include "remoting/client/frame_consumer.h" | 18 #include "remoting/client/frame_consumer.h" |
| 18 #include "remoting/protocol/session_config.h" | 19 #include "remoting/protocol/session_config.h" |
| 19 | 20 |
| 20 using remoting::protocol::ChannelConfig; | 21 using remoting::protocol::ChannelConfig; |
| 21 using remoting::protocol::SessionConfig; | 22 using remoting::protocol::SessionConfig; |
| 22 | 23 |
| 23 namespace remoting { | 24 namespace remoting { |
| 24 | 25 |
| 25 RectangleUpdateDecoder::RectangleUpdateDecoder( | 26 RectangleUpdateDecoder::RectangleUpdateDecoder( |
| 26 base::MessageLoopProxy* message_loop, FrameConsumer* consumer) | 27 base::MessageLoopProxy* message_loop, FrameConsumer* consumer) |
| 27 : message_loop_(message_loop), | 28 : message_loop_(message_loop), |
| 28 consumer_(consumer), | 29 consumer_(consumer), |
| 29 screen_size_(SkISize::Make(0, 0)), | 30 screen_size_(SkISize::Make(0, 0)), |
| 30 clip_rect_(SkIRect::MakeEmpty()), | 31 update_pending_(false), |
| 31 decoder_needs_reset_(false) { | 32 view_size_(SkISize::Make(0, 0)), |
| 33 clip_area_(SkIRect::MakeEmpty()) { | |
| 32 } | 34 } |
| 33 | 35 |
| 34 RectangleUpdateDecoder::~RectangleUpdateDecoder() { | 36 RectangleUpdateDecoder::~RectangleUpdateDecoder() { |
| 35 } | 37 } |
| 36 | 38 |
| 37 void RectangleUpdateDecoder::Initialize(const SessionConfig& config) { | 39 void RectangleUpdateDecoder::Initialize(const SessionConfig& config) { |
| 38 // Initialize decoder based on the selected codec. | 40 // Initialize decoder based on the selected codec. |
| 39 ChannelConfig::Codec codec = config.video_config().codec; | 41 ChannelConfig::Codec codec = config.video_config().codec; |
| 40 if (codec == ChannelConfig::CODEC_VERBATIM) { | 42 if (codec == ChannelConfig::CODEC_VERBATIM) { |
| 41 decoder_.reset(DecoderRowBased::CreateVerbatimDecoder()); | 43 decoder_.reset(DecoderRowBased::CreateVerbatimDecoder()); |
| 42 } else if (codec == ChannelConfig::CODEC_ZIP) { | 44 } else if (codec == ChannelConfig::CODEC_ZIP) { |
| 43 decoder_.reset(DecoderRowBased::CreateZlibDecoder()); | 45 decoder_.reset(DecoderRowBased::CreateZlibDecoder()); |
| 44 } else if (codec == ChannelConfig::CODEC_VP8) { | 46 } else if (codec == ChannelConfig::CODEC_VP8) { |
| 45 decoder_.reset(new DecoderVp8()); | 47 decoder_.reset(new DecoderVp8()); |
| 46 } else { | 48 } else { |
| 47 NOTREACHED() << "Invalid Encoding found: " << codec; | 49 NOTREACHED() << "Invalid Encoding found: " << codec; |
| 48 } | 50 } |
| 49 } | 51 } |
| 50 | 52 |
| 51 void RectangleUpdateDecoder::DecodePacket(const VideoPacket* packet, | 53 void RectangleUpdateDecoder::DecodePacket(const VideoPacket* packet, |
| 52 const base::Closure& done) { | 54 const base::Closure& done) { |
| 53 if (!message_loop_->BelongsToCurrentThread()) { | 55 if (!message_loop_->BelongsToCurrentThread()) { |
| 54 message_loop_->PostTask( | 56 message_loop_->PostTask( |
| 55 FROM_HERE, base::Bind(&RectangleUpdateDecoder::DecodePacket, | 57 FROM_HERE, base::Bind(&RectangleUpdateDecoder::DecodePacket, |
| 56 this, packet, done)); | 58 this, packet, done)); |
| 57 return; | 59 return; |
| 58 } | 60 } |
| 59 AllocateFrame(packet, done); | |
| 60 } | |
| 61 | 61 |
| 62 void RectangleUpdateDecoder::AllocateFrame(const VideoPacket* packet, | |
| 63 const base::Closure& done) { | |
| 64 if (!message_loop_->BelongsToCurrentThread()) { | |
| 65 message_loop_->PostTask( | |
| 66 FROM_HERE, base::Bind(&RectangleUpdateDecoder::AllocateFrame, | |
| 67 this, packet, done)); | |
| 68 return; | |
| 69 } | |
| 70 base::ScopedClosureRunner done_runner(done); | 62 base::ScopedClosureRunner done_runner(done); |
| 63 bool decoder_needs_reset = false; | |
| 71 | 64 |
| 72 // If the packet includes a screen size, store it. | 65 // If the packet includes a screen size, store it. |
| 73 if (packet->format().has_screen_width() && | 66 if (packet->format().has_screen_width() && |
| 74 packet->format().has_screen_height()) { | 67 packet->format().has_screen_height()) { |
| 75 screen_size_.set(packet->format().screen_width(), | 68 SkISize screen_size = SkISize::Make(packet->format().screen_width(), |
| 76 packet->format().screen_height()); | 69 packet->format().screen_height()); |
| 70 if (screen_size_ != screen_size) { | |
| 71 screen_size_ = screen_size; | |
| 72 decoder_needs_reset = true; | |
|
Wez
2012/02/07 01:56:31
So we really need to reset the decoder on host res
alexeypa (please no reviews)
2012/02/15 23:06:22
I don't know but I'd like to avoid adding more cha
| |
| 73 } | |
| 77 } | 74 } |
| 78 | 75 |
| 79 // If we've never seen a screen size, ignore the packet. | 76 // If we've never seen a screen size, ignore the packet. |
| 80 if (screen_size_.isZero()) { | 77 if (screen_size_.isZero()) { |
| 81 return; | 78 return; |
| 82 } | 79 } |
| 83 | 80 |
| 84 // Ensure the output frame is the right size. | 81 if (decoder_needs_reset) { |
| 85 SkISize frame_size = SkISize::Make(0, 0); | |
| 86 if (frame_) | |
| 87 frame_size.set(frame_->width(), frame_->height()); | |
| 88 | |
| 89 // Allocate a new frame, if necessary. | |
| 90 if ((!frame_) || (screen_size_ != frame_size)) { | |
| 91 if (frame_) { | |
| 92 consumer_->ReleaseFrame(frame_); | |
| 93 frame_ = NULL; | |
| 94 } | |
| 95 | |
| 96 consumer_->AllocateFrame( | |
| 97 media::VideoFrame::RGB32, screen_size_, &frame_, | |
| 98 base::Bind(&RectangleUpdateDecoder::ProcessPacketData, | |
| 99 this, packet, done_runner.Release())); | |
| 100 decoder_needs_reset_ = true; | |
| 101 return; | |
| 102 } | |
| 103 ProcessPacketData(packet, done_runner.Release()); | |
| 104 } | |
| 105 | |
| 106 void RectangleUpdateDecoder::ProcessPacketData( | |
| 107 const VideoPacket* packet, const base::Closure& done) { | |
| 108 if (!message_loop_->BelongsToCurrentThread()) { | |
| 109 message_loop_->PostTask( | |
| 110 FROM_HERE, base::Bind(&RectangleUpdateDecoder::ProcessPacketData, | |
| 111 this, packet, done)); | |
| 112 return; | |
| 113 } | |
| 114 base::ScopedClosureRunner done_runner(done); | |
| 115 | |
| 116 if (decoder_needs_reset_) { | |
| 117 decoder_->Reset(); | 82 decoder_->Reset(); |
| 118 decoder_->Initialize(frame_); | 83 decoder_->Initialize(screen_size_); |
| 119 decoder_needs_reset_ = false; | |
| 120 } | 84 } |
| 121 | 85 |
| 122 if (!decoder_->IsReadyForData()) { | 86 if (!decoder_->IsReadyForData()) { |
| 123 // TODO(ajwong): This whole thing should move into an invalid state. | 87 // TODO(ajwong): This whole thing should move into an invalid state. |
| 124 LOG(ERROR) << "Decoder is unable to process data. Dropping packet."; | 88 LOG(ERROR) << "Decoder is unable to process data. Dropping packet."; |
| 125 return; | 89 return; |
| 126 } | 90 } |
| 127 | 91 |
| 128 if (decoder_->DecodePacket(packet) == Decoder::DECODE_DONE) | 92 if (decoder_->DecodePacket(packet) == Decoder::DECODE_DONE) |
| 129 SubmitToConsumer(); | 93 SubmitToConsumer(); |
| 130 } | 94 } |
| 131 | 95 |
| 132 void RectangleUpdateDecoder::SetOutputSize(const SkISize& size) { | |
| 133 if (!message_loop_->BelongsToCurrentThread()) { | |
| 134 message_loop_->PostTask( | |
| 135 FROM_HERE, base::Bind(&RectangleUpdateDecoder::SetOutputSize, | |
| 136 this, size)); | |
| 137 return; | |
| 138 } | |
| 139 | |
| 140 // TODO(wez): Refresh the frame only if the ratio has changed. | |
| 141 if (frame_) { | |
| 142 SkIRect frame_rect = SkIRect::MakeWH(frame_->width(), frame_->height()); | |
| 143 refresh_region_.op(frame_rect, SkRegion::kUnion_Op); | |
| 144 } | |
| 145 | |
| 146 // TODO(hclam): If the scale ratio has changed we should reallocate a | |
| 147 // VideoFrame of different size. However if the scale ratio is always | |
| 148 // smaller than 1.0 we can use the same video frame. | |
| 149 if (decoder_.get()) { | |
| 150 decoder_->SetOutputSize(size); | |
| 151 RefreshFullFrame(); | |
| 152 } | |
| 153 } | |
| 154 | |
| 155 void RectangleUpdateDecoder::UpdateClipRect(const SkIRect& new_clip_rect) { | |
| 156 if (!message_loop_->BelongsToCurrentThread()) { | |
| 157 message_loop_->PostTask( | |
| 158 FROM_HERE, base::Bind(&RectangleUpdateDecoder::UpdateClipRect, | |
| 159 this, new_clip_rect)); | |
| 160 return; | |
| 161 } | |
| 162 | |
| 163 if (new_clip_rect == clip_rect_ || !decoder_.get()) | |
| 164 return; | |
| 165 | |
| 166 // TODO(wez): Only refresh newly-exposed portions of the frame. | |
| 167 if (frame_) { | |
| 168 SkIRect frame_rect = SkIRect::MakeWH(frame_->width(), frame_->height()); | |
| 169 refresh_region_.op(frame_rect, SkRegion::kUnion_Op); | |
| 170 } | |
| 171 | |
| 172 clip_rect_ = new_clip_rect; | |
| 173 decoder_->SetClipRect(new_clip_rect); | |
| 174 | |
| 175 // TODO(wez): Defer refresh so that multiple events can be batched. | |
| 176 DoRefresh(); | |
| 177 } | |
| 178 | |
| 179 void RectangleUpdateDecoder::RefreshFullFrame() { | 96 void RectangleUpdateDecoder::RefreshFullFrame() { |
| 180 if (!message_loop_->BelongsToCurrentThread()) { | 97 if (!message_loop_->BelongsToCurrentThread()) { |
| 181 message_loop_->PostTask( | 98 message_loop_->PostTask( |
| 182 FROM_HERE, base::Bind(&RectangleUpdateDecoder::RefreshFullFrame, this)); | 99 FROM_HERE, base::Bind(&RectangleUpdateDecoder::RefreshFullFrame, this)); |
| 183 return; | 100 return; |
| 184 } | 101 } |
| 185 | 102 |
| 186 // If a video frame or the decoder is not allocated yet then don't | 103 if (decoder_.get()) { |
| 187 // save the refresh rectangle to avoid wasted computation. | 104 SkRegion region; |
| 188 if (!frame_ || !decoder_.get()) | 105 region.op(SkIRect::MakeSize(screen_size_), SkRegion::kUnion_Op); |
| 189 return; | 106 decoder_->UpdateRegion(region); |
| 190 | 107 SubmitToConsumer(); |
| 191 SkIRect frame_rect = SkIRect::MakeWH(frame_->width(), frame_->height()); | 108 } |
| 192 refresh_region_.op(frame_rect, SkRegion::kUnion_Op); | |
| 193 | |
| 194 DoRefresh(); | |
| 195 } | 109 } |
| 196 | 110 |
| 197 void RectangleUpdateDecoder::SubmitToConsumer() { | 111 void RectangleUpdateDecoder::SubmitToConsumer() { |
| 198 // A frame is not allocated yet, we can reach here because of a refresh | 112 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 199 // request. | |
| 200 if (!frame_) | |
| 201 return; | |
| 202 | 113 |
| 203 SkRegion* dirty_region = new SkRegion; | 114 if (!update_pending_) { |
| 204 decoder_->GetUpdatedRegion(dirty_region); | 115 // Make sure OnFrameReady() will not be called again until OnPaintFrame() |
| 205 | 116 // is invoked. |
| 206 consumer_->OnPartialFrameOutput(frame_, dirty_region, base::Bind( | 117 update_pending_ = true; |
| 207 &RectangleUpdateDecoder::OnFrameConsumed, this, dirty_region)); | 118 consumer_->OnFrameReady( |
| 119 screen_size_, | |
| 120 &view_size_, &clip_area_, &backing_store_, | |
| 121 base::Bind(&RectangleUpdateDecoder::OnPaintFrame, this)); | |
| 122 } | |
| 208 } | 123 } |
| 209 | 124 |
| 210 void RectangleUpdateDecoder::DoRefresh() { | 125 void RectangleUpdateDecoder::OnPaintFrame() { |
| 211 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 212 | |
| 213 if (refresh_region_.isEmpty()) | |
| 214 return; | |
| 215 | |
| 216 decoder_->RefreshRegion(refresh_region_); | |
| 217 refresh_region_.setEmpty(); | |
| 218 SubmitToConsumer(); | |
| 219 } | |
| 220 | |
| 221 void RectangleUpdateDecoder::OnFrameConsumed(SkRegion* region) { | |
| 222 if (!message_loop_->BelongsToCurrentThread()) { | 126 if (!message_loop_->BelongsToCurrentThread()) { |
| 223 message_loop_->PostTask( | 127 message_loop_->PostTask( |
| 224 FROM_HERE, base::Bind(&RectangleUpdateDecoder::OnFrameConsumed, | 128 FROM_HERE, base::Bind(&RectangleUpdateDecoder::OnPaintFrame, |
| 225 this, region)); | 129 this)); |
| 226 return; | 130 return; |
| 227 } | 131 } |
| 228 | 132 |
| 229 delete region; | 133 update_pending_ = false; |
| 230 | 134 |
| 231 DoRefresh(); | 135 // Skip painting if the backing store is not ready yet. |
| 136 if (backing_store_.get() && !backing_store_->is_null()) { | |
| 137 // Draw the updated region to the backing store. | |
| 138 scoped_ptr<SkRegion> output_region(new SkRegion()); | |
| 139 decoder_->Draw(view_size_, clip_area_, | |
| 140 reinterpret_cast<uint8*>(backing_store_->data()), | |
| 141 backing_store_->stride(), | |
| 142 output_region.get()); | |
| 143 | |
| 144 // Notify the consumer that painting is done. | |
| 145 consumer_->OnPaintDone(backing_store_.Pass(), output_region.Pass()); | |
| 146 } else { | |
| 147 LOG(ERROR) << "Backing store is not available."; | |
| 148 } | |
| 232 } | 149 } |
| 233 | 150 |
| 234 } // namespace remoting | 151 } // namespace remoting |
| OLD | NEW |