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 screen_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 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; | |
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); | 82 decoder_->Initialize(screen_size_); |
86 if (frame_) | 83 consumer_->SetScreenSize(screen_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(); | |
111 consumer_->PaintBuffer(view_size_, clip_area_, buffer, output_region); | |
112 } | |
113 } | |
114 | |
115 void RectangleUpdateDecoder::DrainQueue(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::DrainQueue, this, done)); |
136 this, size)); | |
137 return; | 119 return; |
138 } | 120 } |
139 | 121 |
140 // TODO(wez): Refresh the frame only if the ratio has changed. | 122 while (!buffers_.empty()) { |
141 if (frame_) { | 123 consumer_->ReturnBuffer(buffers_.front()); |
142 SkIRect frame_rect = SkIRect::MakeWH(frame_->width(), frame_->height()); | 124 buffers_.pop(); |
143 refresh_region_.op(frame_rect, SkRegion::kUnion_Op); | |
144 } | 125 } |
145 | 126 |
146 // TODO(hclam): If the scale ratio has changed we should reallocate a | 127 if (!done.is_null()) |
147 // VideoFrame of different size. However if the scale ratio is always | 128 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 } | 129 } |
154 | 130 |
155 void RectangleUpdateDecoder::UpdateClipRect(const SkIRect& new_clip_rect) { | 131 void RectangleUpdateDecoder::EnqueueBuffer(pp::ImageData* buffer) { |
156 if (!message_loop_->BelongsToCurrentThread()) { | 132 if (!message_loop_->BelongsToCurrentThread()) { |
157 message_loop_->PostTask( | 133 message_loop_->PostTask( |
158 FROM_HERE, base::Bind(&RectangleUpdateDecoder::UpdateClipRect, | 134 FROM_HERE, base::Bind(&RectangleUpdateDecoder::EnqueueBuffer, |
159 this, new_clip_rect)); | 135 this, buffer)); |
160 return; | 136 return; |
161 } | 137 } |
162 | 138 |
163 if (new_clip_rect == clip_rect_ || !decoder_.get()) | 139 DCHECK(clip_area_.width() <= buffer->size().width() && |
164 return; | 140 clip_area_.height() <= buffer->size().height()); |
165 | 141 |
166 // TODO(wez): Only refresh newly-exposed portions of the frame. | 142 buffers_.push(buffer); |
167 if (frame_) { | 143 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 } | 144 } |
178 | 145 |
179 void RectangleUpdateDecoder::RefreshFullFrame() { | 146 void RectangleUpdateDecoder::Invalidate(const SkRegion& region) { |
180 if (!message_loop_->BelongsToCurrentThread()) { | 147 if (!message_loop_->BelongsToCurrentThread()) { |
181 message_loop_->PostTask( | 148 message_loop_->PostTask( |
182 FROM_HERE, base::Bind(&RectangleUpdateDecoder::RefreshFullFrame, this)); | 149 FROM_HERE, base::Bind(&RectangleUpdateDecoder::Invalidate, |
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)); | 150 this, region)); |
226 return; | 151 return; |
227 } | 152 } |
228 | 153 |
229 delete region; | 154 if (decoder_.get()) { |
155 decoder_->Invalidate(view_size_, region); | |
156 DoPaint(); | |
157 } | |
158 } | |
230 | 159 |
231 DoRefresh(); | 160 void RectangleUpdateDecoder::SetView(const SkISize& view_size, |
161 const SkIRect& clip_area) { | |
162 if (!message_loop_->BelongsToCurrentThread()) { | |
163 message_loop_->PostTask( | |
164 FROM_HERE, base::Bind(&RectangleUpdateDecoder::SetView, | |
165 this, view_size, clip_area)); | |
166 return; | |
167 } | |
168 | |
169 // The whole frame needs to be repainted if the scaling factor has changed. | |
170 // Otherwise the newly exposed parts of the frame will be automatically | |
171 // updated because they haven't been validated by RenderFrame() yet. | |
Wez
2012/02/17 23:42:17
This sentence sounds the wrong way around?
alexeypa (please no reviews)
2012/02/21 23:00:44
Yeah. I was trying to say that if the scaling is t
| |
172 if (view_size_ != view_size && decoder_.get()) { | |
173 SkRegion region; | |
174 region.op(SkIRect::MakeSize(view_size), SkRegion::kUnion_Op); | |
175 decoder_->Invalidate(view_size, region); | |
176 } | |
177 | |
178 if (view_size_ != view_size || | |
179 clip_area_ != clip_area) { | |
180 view_size_ = view_size; | |
181 clip_area_ = clip_area; | |
182 | |
183 // Return buffers to the consumer for reuse/reallocation. | |
184 while (!buffers_.empty()) { | |
185 consumer_->ReturnBuffer(buffers_.front()); | |
186 buffers_.pop(); | |
187 } | |
188 } | |
232 } | 189 } |
233 | 190 |
234 } // namespace remoting | 191 } // namespace remoting |
OLD | NEW |