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 |
| 21 using base::Passed; |
20 using remoting::protocol::ChannelConfig; | 22 using remoting::protocol::ChannelConfig; |
21 using remoting::protocol::SessionConfig; | 23 using remoting::protocol::SessionConfig; |
22 | 24 |
23 namespace remoting { | 25 namespace remoting { |
24 | 26 |
25 RectangleUpdateDecoder::RectangleUpdateDecoder( | 27 RectangleUpdateDecoder::RectangleUpdateDecoder( |
26 base::MessageLoopProxy* message_loop, FrameConsumer* consumer) | 28 base::MessageLoopProxy* message_loop, FrameConsumer* consumer) |
27 : message_loop_(message_loop), | 29 : message_loop_(message_loop), |
28 consumer_(consumer), | 30 consumer_(consumer), |
29 screen_size_(SkISize::Make(0, 0)), | 31 source_size_(SkISize::Make(0, 0)), |
30 clip_rect_(SkIRect::MakeEmpty()), | 32 view_size_(SkISize::Make(0, 0)), |
31 decoder_needs_reset_(false) { | 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 source_size = SkISize::Make(packet->format().screen_width(), |
76 packet->format().screen_height()); | 69 packet->format().screen_height()); |
| 70 if (source_size_ != source_size) { |
| 71 source_size_ = source_size; |
| 72 decoder_needs_reset = true; |
| 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 (source_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); | 82 decoder_->Initialize(source_size_); |
86 if (frame_) | 83 consumer_->SetSourceSize(source_size_); |
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(); | |
118 decoder_->Initialize(frame_); | |
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 DoPaint(); |
130 } | 94 } |
131 | 95 |
132 void RectangleUpdateDecoder::SetOutputSize(const SkISize& size) { | 96 void RectangleUpdateDecoder::DoPaint() { |
| 97 if (buffers_.empty()) |
| 98 return; |
| 99 |
| 100 // Draw the invalidated region to the buffer. |
| 101 pp::ImageData* buffer = buffers_.front(); |
| 102 SkRegion output_region; |
| 103 decoder_->RenderFrame(view_size_, clip_area_, |
| 104 reinterpret_cast<uint8*>(buffer->data()), |
| 105 buffer->stride(), |
| 106 &output_region); |
| 107 |
| 108 // Notify the consumer that painting is done. |
| 109 if (!output_region.isEmpty()) { |
| 110 buffers_.pop_front(); |
| 111 consumer_->ApplyBuffer(view_size_, clip_area_, buffer, output_region); |
| 112 } |
| 113 } |
| 114 |
| 115 void RectangleUpdateDecoder::RequestReturnBuffers(const base::Closure& done) { |
133 if (!message_loop_->BelongsToCurrentThread()) { | 116 if (!message_loop_->BelongsToCurrentThread()) { |
134 message_loop_->PostTask( | 117 message_loop_->PostTask( |
135 FROM_HERE, base::Bind(&RectangleUpdateDecoder::SetOutputSize, | 118 FROM_HERE, base::Bind(&RectangleUpdateDecoder::RequestReturnBuffers, |
136 this, size)); | 119 this, done)); |
137 return; | 120 return; |
138 } | 121 } |
139 | 122 |
140 // TODO(wez): Refresh the frame only if the ratio has changed. | 123 while (!buffers_.empty()) { |
141 if (frame_) { | 124 consumer_->ReturnBuffer(buffers_.front()); |
142 SkIRect frame_rect = SkIRect::MakeWH(frame_->width(), frame_->height()); | 125 buffers_.pop_front(); |
143 refresh_region_.op(frame_rect, SkRegion::kUnion_Op); | |
144 } | 126 } |
145 | 127 |
146 // TODO(hclam): If the scale ratio has changed we should reallocate a | 128 if (!done.is_null()) |
147 // VideoFrame of different size. However if the scale ratio is always | 129 done.Run(); |
148 // smaller than 1.0 we can use the same video frame. | |
149 if (decoder_.get()) { | |
150 decoder_->SetOutputSize(size); | |
151 RefreshFullFrame(); | |
152 } | |
153 } | 130 } |
154 | 131 |
155 void RectangleUpdateDecoder::UpdateClipRect(const SkIRect& new_clip_rect) { | 132 void RectangleUpdateDecoder::DrawBuffer(pp::ImageData* buffer) { |
156 if (!message_loop_->BelongsToCurrentThread()) { | 133 if (!message_loop_->BelongsToCurrentThread()) { |
157 message_loop_->PostTask( | 134 message_loop_->PostTask( |
158 FROM_HERE, base::Bind(&RectangleUpdateDecoder::UpdateClipRect, | 135 FROM_HERE, base::Bind(&RectangleUpdateDecoder::DrawBuffer, |
159 this, new_clip_rect)); | 136 this, buffer)); |
160 return; | 137 return; |
161 } | 138 } |
162 | 139 |
163 if (new_clip_rect == clip_rect_ || !decoder_.get()) | 140 DCHECK(clip_area_.width() <= buffer->size().width() && |
164 return; | 141 clip_area_.height() <= buffer->size().height()); |
165 | 142 |
166 // TODO(wez): Only refresh newly-exposed portions of the frame. | 143 buffers_.push_back(buffer); |
167 if (frame_) { | 144 DoPaint(); |
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 } | 145 } |
178 | 146 |
179 void RectangleUpdateDecoder::RefreshFullFrame() { | 147 void RectangleUpdateDecoder::InvalidateRegion(const SkRegion& region) { |
180 if (!message_loop_->BelongsToCurrentThread()) { | 148 if (!message_loop_->BelongsToCurrentThread()) { |
181 message_loop_->PostTask( | 149 message_loop_->PostTask( |
182 FROM_HERE, base::Bind(&RectangleUpdateDecoder::RefreshFullFrame, this)); | 150 FROM_HERE, base::Bind(&RectangleUpdateDecoder::InvalidateRegion, |
183 return; | |
184 } | |
185 | |
186 // If a video frame or the decoder is not allocated yet then don't | |
187 // save the refresh rectangle to avoid wasted computation. | |
188 if (!frame_ || !decoder_.get()) | |
189 return; | |
190 | |
191 SkIRect frame_rect = SkIRect::MakeWH(frame_->width(), frame_->height()); | |
192 refresh_region_.op(frame_rect, SkRegion::kUnion_Op); | |
193 | |
194 DoRefresh(); | |
195 } | |
196 | |
197 void RectangleUpdateDecoder::SubmitToConsumer() { | |
198 // A frame is not allocated yet, we can reach here because of a refresh | |
199 // request. | |
200 if (!frame_) | |
201 return; | |
202 | |
203 SkRegion* dirty_region = new SkRegion; | |
204 decoder_->GetUpdatedRegion(dirty_region); | |
205 | |
206 consumer_->OnPartialFrameOutput(frame_, dirty_region, base::Bind( | |
207 &RectangleUpdateDecoder::OnFrameConsumed, this, dirty_region)); | |
208 } | |
209 | |
210 void RectangleUpdateDecoder::DoRefresh() { | |
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()) { | |
223 message_loop_->PostTask( | |
224 FROM_HERE, base::Bind(&RectangleUpdateDecoder::OnFrameConsumed, | |
225 this, region)); | 151 this, region)); |
226 return; | 152 return; |
227 } | 153 } |
228 | 154 |
229 delete region; | 155 if (decoder_.get()) { |
| 156 decoder_->Invalidate(view_size_, region); |
| 157 DoPaint(); |
| 158 } |
| 159 } |
230 | 160 |
231 DoRefresh(); | 161 void RectangleUpdateDecoder::SetOutputSizeAndClip(const SkISize& view_size, |
| 162 const SkIRect& clip_area) { |
| 163 if (!message_loop_->BelongsToCurrentThread()) { |
| 164 message_loop_->PostTask( |
| 165 FROM_HERE, base::Bind(&RectangleUpdateDecoder::SetOutputSizeAndClip, |
| 166 this, view_size, clip_area)); |
| 167 return; |
| 168 } |
| 169 |
| 170 // The whole frame needs to be repainted if the scaling factor has changed. |
| 171 if (view_size_ != view_size && decoder_.get()) { |
| 172 SkRegion region; |
| 173 region.op(SkIRect::MakeSize(view_size), SkRegion::kUnion_Op); |
| 174 decoder_->Invalidate(view_size, region); |
| 175 } |
| 176 |
| 177 if (view_size_ != view_size || |
| 178 clip_area_ != clip_area) { |
| 179 view_size_ = view_size; |
| 180 clip_area_ = clip_area; |
| 181 |
| 182 // Return buffers that are smaller than needed to the consumer for |
| 183 // reuse/reallocation. |
| 184 std::list<pp::ImageData*>::iterator i = buffers_.begin(); |
| 185 while (i != buffers_.end()) { |
| 186 pp::Size buffer_size = (*i)->size(); |
| 187 if (buffer_size.width() < clip_area_.width() || |
| 188 buffer_size.height() < clip_area_.height()) { |
| 189 consumer_->ReturnBuffer(*i); |
| 190 i = buffers_.erase(i); |
| 191 } else { |
| 192 ++i; |
| 193 } |
| 194 } |
| 195 } |
232 } | 196 } |
233 | 197 |
234 } // namespace remoting | 198 } // namespace remoting |
OLD | NEW |