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/plugin/pepper_view.h" | 5 #include "remoting/client/plugin/pepper_view.h" |
6 | 6 |
| 7 #include <functional> |
| 8 |
7 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
8 #include "base/string_util.h" | 10 #include "base/string_util.h" |
| 11 #include "base/synchronization/waitable_event.h" |
9 #include "ppapi/cpp/completion_callback.h" | 12 #include "ppapi/cpp/completion_callback.h" |
10 #include "ppapi/cpp/graphics_2d.h" | 13 #include "ppapi/cpp/graphics_2d.h" |
11 #include "ppapi/cpp/image_data.h" | 14 #include "ppapi/cpp/image_data.h" |
12 #include "ppapi/cpp/point.h" | 15 #include "ppapi/cpp/point.h" |
13 #include "ppapi/cpp/rect.h" | 16 #include "ppapi/cpp/rect.h" |
14 #include "ppapi/cpp/size.h" | 17 #include "ppapi/cpp/size.h" |
15 #include "remoting/base/util.h" | 18 #include "remoting/base/util.h" |
16 #include "remoting/client/chromoting_stats.h" | 19 #include "remoting/client/chromoting_stats.h" |
17 #include "remoting/client/client_context.h" | 20 #include "remoting/client/client_context.h" |
| 21 #include "remoting/client/frame_producer.h" |
18 #include "remoting/client/plugin/chromoting_instance.h" | 22 #include "remoting/client/plugin/chromoting_instance.h" |
19 #include "remoting/client/plugin/pepper_util.h" | 23 #include "remoting/client/plugin/pepper_util.h" |
20 | 24 |
| 25 using base::Passed; |
| 26 |
21 namespace remoting { | 27 namespace remoting { |
22 | 28 |
23 namespace { | 29 namespace { |
24 | 30 |
| 31 // The maximum number of image buffers to be allocated at any point of time. |
| 32 const size_t kMaxPendingBuffersCount = 2; |
| 33 |
25 ChromotingInstance::ConnectionError ConvertConnectionError( | 34 ChromotingInstance::ConnectionError ConvertConnectionError( |
26 protocol::ConnectionToHost::Error error) { | 35 protocol::ConnectionToHost::Error error) { |
27 switch (error) { | 36 switch (error) { |
28 case protocol::ConnectionToHost::OK: | 37 case protocol::ConnectionToHost::OK: |
29 return ChromotingInstance::ERROR_NONE; | 38 return ChromotingInstance::ERROR_NONE; |
30 case protocol::ConnectionToHost::HOST_IS_OFFLINE: | 39 case protocol::ConnectionToHost::HOST_IS_OFFLINE: |
31 return ChromotingInstance::ERROR_HOST_IS_OFFLINE; | 40 return ChromotingInstance::ERROR_HOST_IS_OFFLINE; |
32 case protocol::ConnectionToHost::SESSION_REJECTED: | 41 case protocol::ConnectionToHost::SESSION_REJECTED: |
33 return ChromotingInstance::ERROR_SESSION_REJECTED; | 42 return ChromotingInstance::ERROR_SESSION_REJECTED; |
34 case protocol::ConnectionToHost::INCOMPATIBLE_PROTOCOL: | 43 case protocol::ConnectionToHost::INCOMPATIBLE_PROTOCOL: |
35 return ChromotingInstance::ERROR_INCOMPATIBLE_PROTOCOL; | 44 return ChromotingInstance::ERROR_INCOMPATIBLE_PROTOCOL; |
36 case protocol::ConnectionToHost::NETWORK_FAILURE: | 45 case protocol::ConnectionToHost::NETWORK_FAILURE: |
37 return ChromotingInstance::ERROR_NETWORK_FAILURE; | 46 return ChromotingInstance::ERROR_NETWORK_FAILURE; |
38 } | 47 } |
39 DLOG(FATAL) << "Unknown error code" << error; | 48 DLOG(FATAL) << "Unknown error code" << error; |
40 return ChromotingInstance::ERROR_NONE; | 49 return ChromotingInstance::ERROR_NONE; |
41 } | 50 } |
42 | 51 |
43 } // namespace | 52 } // namespace |
44 | 53 |
45 PepperView::PepperView(ChromotingInstance* instance, ClientContext* context) | 54 PepperView::PepperView(ChromotingInstance* instance, |
| 55 ClientContext* context, |
| 56 FrameProducer* producer) |
46 : instance_(instance), | 57 : instance_(instance), |
47 context_(context), | 58 context_(context), |
48 flush_blocked_(false), | 59 producer_(producer), |
49 is_static_fill_(false), | 60 merge_buffer_(NULL), |
50 static_fill_color_(0), | 61 merge_clip_area_(SkIRect::MakeEmpty()), |
| 62 view_size_(SkISize::Make(0, 0)), |
| 63 clip_area_(SkIRect::MakeEmpty()), |
| 64 source_size_(SkISize::Make(0, 0)), |
| 65 flush_pending_(false), |
| 66 in_teardown_(false), |
51 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { | 67 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { |
52 } | 68 } |
53 | 69 |
54 PepperView::~PepperView() { | 70 PepperView::~PepperView() { |
| 71 DCHECK(merge_buffer_ == NULL); |
| 72 DCHECK(buffers_.empty()); |
55 } | 73 } |
56 | 74 |
57 bool PepperView::Initialize() { | 75 bool PepperView::Initialize() { |
58 return true; | 76 return true; |
59 } | 77 } |
60 | 78 |
61 void PepperView::TearDown() { | 79 void PepperView::TearDown() { |
62 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | 80 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); |
63 | 81 |
| 82 // The producer should now return any pending buffers. At this point, however, |
| 83 // ReturnBuffer() tasks scheduled by the producer will not be delivered, |
| 84 // so we free all the buffers once the producer's queue is empty. |
| 85 in_teardown_ = true; |
| 86 base::WaitableEvent done_event(true, false); |
| 87 producer_->RequestReturnBuffers( |
| 88 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done_event))); |
| 89 done_event.Wait(); |
| 90 |
| 91 merge_buffer_ = NULL; |
| 92 while (!buffers_.empty()) { |
| 93 FreeBuffer(buffers_.front()); |
| 94 } |
| 95 |
64 weak_factory_.InvalidateWeakPtrs(); | 96 weak_factory_.InvalidateWeakPtrs(); |
65 } | 97 } |
66 | 98 |
67 void PepperView::Paint() { | 99 void PepperView::SetConnectionState(protocol::ConnectionToHost::State state, |
68 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | 100 protocol::ConnectionToHost::Error error) { |
69 | 101 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); |
70 if (is_static_fill_) { | 102 |
71 VLOG(1) << "Static filling " << static_fill_color_; | 103 switch (state) { |
72 pp::ImageData image(instance_, pp::ImageData::GetNativeImageDataFormat(), | 104 case protocol::ConnectionToHost::CONNECTING: |
73 pp::Size(graphics2d_.size().width(), | 105 instance_->SetConnectionState( |
74 graphics2d_.size().height()), | 106 ChromotingInstance::STATE_CONNECTING, |
75 false); | 107 ConvertConnectionError(error)); |
76 if (image.is_null()) { | 108 break; |
77 LOG(ERROR) << "Unable to allocate image of size: " | 109 |
78 << graphics2d_.size().width() << " x " | 110 case protocol::ConnectionToHost::CONNECTED: |
79 << graphics2d_.size().height(); | 111 instance_->SetConnectionState( |
80 return; | 112 ChromotingInstance::STATE_CONNECTED, |
| 113 ConvertConnectionError(error)); |
| 114 break; |
| 115 |
| 116 case protocol::ConnectionToHost::CLOSED: |
| 117 instance_->SetConnectionState( |
| 118 ChromotingInstance::STATE_CLOSED, |
| 119 ConvertConnectionError(error)); |
| 120 break; |
| 121 |
| 122 case protocol::ConnectionToHost::FAILED: |
| 123 instance_->SetConnectionState( |
| 124 ChromotingInstance::STATE_FAILED, |
| 125 ConvertConnectionError(error)); |
| 126 break; |
| 127 } |
| 128 } |
| 129 |
| 130 void PepperView::SetView(const SkISize& view_size, |
| 131 const SkIRect& clip_area) { |
| 132 bool view_changed = false; |
| 133 |
| 134 // TODO(alexeypa): Prevent upscaling because the YUV-to-RGB conversion code |
| 135 // currently does not support upscaling. Once it does, this code be removed. |
| 136 SkISize size = SkISize::Make( |
| 137 std::min(view_size.width(), source_size_.width()), |
| 138 std::min(view_size.height(), source_size_.height())); |
| 139 |
| 140 if (view_size_ != size) { |
| 141 view_changed = true; |
| 142 view_size_ = size; |
| 143 |
| 144 pp::Size pp_size = pp::Size(view_size_.width(), view_size_.height()); |
| 145 graphics2d_ = pp::Graphics2D(instance_, pp_size, true); |
| 146 bool result = instance_->BindGraphics(graphics2d_); |
| 147 |
| 148 // There is no good way to handle this error currently. |
| 149 DCHECK(result) << "Couldn't bind the device context."; |
| 150 } |
| 151 |
| 152 if (clip_area_ != clip_area) { |
| 153 view_changed = true; |
| 154 |
| 155 // YUV to RGB conversion may require even X and Y coordinates for |
| 156 // the top left corner of the clipping area. |
| 157 clip_area_ = AlignRect(clip_area); |
| 158 clip_area_.intersect(SkIRect::MakeSize(view_size_)); |
| 159 } |
| 160 |
| 161 if (view_changed) { |
| 162 producer_->SetOutputSizeAndClip(view_size_, clip_area_); |
| 163 InitiateDrawing(); |
| 164 } |
| 165 } |
| 166 |
| 167 void PepperView::ApplyBuffer(const SkISize& view_size, |
| 168 const SkIRect& clip_area, |
| 169 pp::ImageData* buffer, |
| 170 const SkRegion& region) { |
| 171 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); |
| 172 |
| 173 // Currently we cannot use the data in the buffer is scale factor has changed |
| 174 // already. |
| 175 // TODO(alexeypa): We could rescale and draw it (or even draw it without |
| 176 // rescaling) to reduce the perceived lag while we are waiting for |
| 177 // the properly scaled data. |
| 178 if (view_size_ != view_size) { |
| 179 FreeBuffer(buffer); |
| 180 InitiateDrawing(); |
| 181 } else { |
| 182 FlushBuffer(clip_area, buffer, region); |
| 183 } |
| 184 } |
| 185 |
| 186 void PepperView::ReturnBuffer(pp::ImageData* buffer) { |
| 187 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); |
| 188 |
| 189 // Free the buffer if there is nothing to paint. |
| 190 if (in_teardown_) { |
| 191 FreeBuffer(buffer); |
| 192 return; |
| 193 } |
| 194 |
| 195 // Reuse the buffer if it is large enough, otherwise drop it on the floor |
| 196 // and allocate a new one. |
| 197 if (buffer->size().width() >= clip_area_.width() && |
| 198 buffer->size().height() >= clip_area_.height()) { |
| 199 producer_->DrawBuffer(buffer); |
| 200 } else { |
| 201 FreeBuffer(buffer); |
| 202 InitiateDrawing(); |
| 203 } |
| 204 } |
| 205 |
| 206 void PepperView::SetSourceSize(const SkISize& source_size) { |
| 207 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); |
| 208 |
| 209 if (source_size_ == source_size) |
| 210 return; |
| 211 |
| 212 source_size_ = source_size; |
| 213 |
| 214 // Notify JavaScript of the change in source size. |
| 215 instance_->SetDesktopSize(source_size.width(), source_size.height()); |
| 216 } |
| 217 |
| 218 pp::ImageData* PepperView::AllocateBuffer() { |
| 219 pp::ImageData* buffer = NULL; |
| 220 if (buffers_.size() < kMaxPendingBuffersCount) { |
| 221 pp::Size pp_size = pp::Size(clip_area_.width(), clip_area_.height()); |
| 222 buffer = new pp::ImageData(instance_, |
| 223 PP_IMAGEDATAFORMAT_BGRA_PREMUL, |
| 224 pp_size, |
| 225 false); |
| 226 if (buffer->is_null()) { |
| 227 LOG(WARNING) << "Not enough memory for frame buffers."; |
| 228 delete buffer; |
| 229 buffer = NULL; |
| 230 } else { |
| 231 buffers_.push_back(buffer); |
81 } | 232 } |
82 | 233 } |
83 for (int y = 0; y < image.size().height(); y++) { | 234 |
84 for (int x = 0; x < image.size().width(); x++) { | 235 return buffer; |
85 *image.GetAddr32(pp::Point(x, y)) = static_fill_color_; | 236 } |
86 } | 237 |
| 238 void PepperView::FreeBuffer(pp::ImageData* buffer) { |
| 239 DCHECK(std::find(buffers_.begin(), buffers_.end(), buffer) != buffers_.end()); |
| 240 |
| 241 buffers_.remove(buffer); |
| 242 delete buffer; |
| 243 } |
| 244 |
| 245 void PepperView::InitiateDrawing() { |
| 246 // Do not schedule drawing if there is nothing to paint. |
| 247 if (in_teardown_) |
| 248 return; |
| 249 |
| 250 pp::ImageData* buffer = AllocateBuffer(); |
| 251 while (buffer) { |
| 252 producer_->DrawBuffer(buffer); |
| 253 buffer = AllocateBuffer(); |
| 254 } |
| 255 } |
| 256 |
| 257 void PepperView::FlushBuffer(const SkIRect& clip_area, |
| 258 pp::ImageData* buffer, |
| 259 const SkRegion& region) { |
| 260 |
| 261 // Defer drawing if the flush is already in progress. |
| 262 if (flush_pending_) { |
| 263 // |merge_buffer_| is guaranteed to be free here because we allocate only |
| 264 // two buffers simultaneously. If more buffers are allowed this code should |
| 265 // apply all pending changes to the screen. |
| 266 DCHECK(merge_buffer_ == NULL); |
| 267 |
| 268 merge_clip_area_ = clip_area; |
| 269 merge_buffer_ = buffer; |
| 270 merge_region_ = region; |
| 271 return; |
| 272 } |
| 273 |
| 274 // Notify Pepper API about the updated areas and flush pixels to the screen. |
| 275 base::Time start_time = base::Time::Now(); |
| 276 |
| 277 for (SkRegion::Iterator i(region); !i.done(); i.next()) { |
| 278 SkIRect rect = i.rect(); |
| 279 |
| 280 // Re-clip |region| with the current clipping area |clip_area_| because |
| 281 // the latter could change from the time the buffer was drawn. |
| 282 if (!rect.intersect(clip_area_)) |
| 283 continue; |
| 284 |
| 285 // Specify the rectangle coordinates relative to the clipping area. |
| 286 rect.offset(-clip_area.left(), -clip_area.top()); |
| 287 |
| 288 // Pepper Graphics 2D has a strange and badly documented API that the |
| 289 // point here is the offset from the source rect. Why? |
| 290 graphics2d_.PaintImageData( |
| 291 *buffer, |
| 292 pp::Point(clip_area.left(), clip_area.top()), |
| 293 pp::Rect(rect.left(), rect.top(), rect.width(), rect.height())); |
| 294 } |
| 295 |
| 296 // Notify the producer that some parts of the region weren't painted because |
| 297 // the clipping area has changed already. |
| 298 if (clip_area != clip_area_) { |
| 299 SkRegion not_painted = region; |
| 300 not_painted.op(clip_area_, SkRegion::kDifference_Op); |
| 301 if (!not_painted.isEmpty()) { |
| 302 producer_->InvalidateRegion(not_painted); |
87 } | 303 } |
88 | 304 } |
89 // For ReplaceContents, make sure the image size matches the device context | 305 |
90 // size! Otherwise, this will just silently do nothing. | 306 // Flush the updated areas to the screen. |
91 graphics2d_.ReplaceContents(&image); | |
92 FlushGraphics(base::Time::Now()); | |
93 } else { | |
94 // TODO(ajwong): We need to keep a backing store image of the host screen | |
95 // that has the data here which can be redrawn. | |
96 return; | |
97 } | |
98 } | |
99 | |
100 void PepperView::SetHostSize(const SkISize& host_size) { | |
101 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | |
102 | |
103 if (host_size_ == host_size) | |
104 return; | |
105 | |
106 host_size_ = host_size; | |
107 | |
108 // Submit an update of desktop size to Javascript. | |
109 instance_->SetDesktopSize(host_size.width(), host_size.height()); | |
110 } | |
111 | |
112 void PepperView::PaintFrame(media::VideoFrame* frame, const SkRegion& region) { | |
113 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | |
114 | |
115 SetHostSize(SkISize::Make(frame->width(), frame->height())); | |
116 | |
117 if (!backing_store_.get() || backing_store_->is_null()) { | |
118 LOG(ERROR) << "Backing store is not available."; | |
119 return; | |
120 } | |
121 | |
122 base::Time start_time = base::Time::Now(); | |
123 | |
124 // Copy updated regions to the backing store and then paint the regions. | |
125 bool changes_made = false; | |
126 for (SkRegion::Iterator i(region); !i.done(); i.next()) | |
127 changes_made |= PaintRect(frame, i.rect()); | |
128 | |
129 if (changes_made) | |
130 FlushGraphics(start_time); | |
131 } | |
132 | |
133 bool PepperView::PaintRect(media::VideoFrame* frame, const SkIRect& r) { | |
134 const uint8* frame_data = frame->data(media::VideoFrame::kRGBPlane); | |
135 const int kFrameStride = frame->stride(media::VideoFrame::kRGBPlane); | |
136 const int kBytesPerPixel = GetBytesPerPixel(media::VideoFrame::RGB32); | |
137 | |
138 pp::Size backing_store_size = backing_store_->size(); | |
139 SkIRect rect(r); | |
140 if (!rect.intersect(SkIRect::MakeWH(backing_store_size.width(), | |
141 backing_store_size.height()))) { | |
142 return false; | |
143 } | |
144 | |
145 const uint8* in = | |
146 frame_data + | |
147 kFrameStride * rect.fTop + // Y offset. | |
148 kBytesPerPixel * rect.fLeft; // X offset. | |
149 uint8* out = | |
150 reinterpret_cast<uint8*>(backing_store_->data()) + | |
151 backing_store_->stride() * rect.fTop + // Y offset. | |
152 kBytesPerPixel * rect.fLeft; // X offset. | |
153 | |
154 // TODO(hclam): We really should eliminate this memory copy. | |
155 for (int j = 0; j < rect.height(); ++j) { | |
156 memcpy(out, in, rect.width() * kBytesPerPixel); | |
157 in += kFrameStride; | |
158 out += backing_store_->stride(); | |
159 } | |
160 | |
161 // Pepper Graphics 2D has a strange and badly documented API that the | |
162 // point here is the offset from the source rect. Why? | |
163 graphics2d_.PaintImageData( | |
164 *backing_store_.get(), | |
165 pp::Point(0, 0), | |
166 pp::Rect(rect.fLeft, rect.fTop, rect.width(), rect.height())); | |
167 return true; | |
168 } | |
169 | |
170 void PepperView::BlankRect(pp::ImageData& image_data, const pp::Rect& rect) { | |
171 const int kBytesPerPixel = GetBytesPerPixel(media::VideoFrame::RGB32); | |
172 for (int y = rect.y(); y < rect.bottom(); y++) { | |
173 uint8* to = reinterpret_cast<uint8*>(image_data.data()) + | |
174 (y * image_data.stride()) + (rect.x() * kBytesPerPixel); | |
175 memset(to, 0xff, rect.width() * kBytesPerPixel); | |
176 } | |
177 } | |
178 | |
179 void PepperView::FlushGraphics(base::Time paint_start) { | |
180 scoped_ptr<base::Closure> task( | 307 scoped_ptr<base::Closure> task( |
181 new base::Closure( | 308 new base::Closure( |
182 base::Bind(&PepperView::OnPaintDone, weak_factory_.GetWeakPtr(), | 309 base::Bind(&PepperView::OnFlushDone, weak_factory_.GetWeakPtr(), |
183 paint_start))); | 310 start_time, buffer))); |
184 | 311 |
185 // Flag needs to be set here in order to get a proper error code for Flush(). | 312 // Flag needs to be set here in order to get a proper error code for Flush(). |
186 // Otherwise Flush() will always return PP_OK_COMPLETIONPENDING and the error | 313 // Otherwise Flush() will always return PP_OK_COMPLETIONPENDING and the error |
187 // would be hidden. | 314 // would be hidden. |
188 // | 315 // |
189 // Note that we can also handle this by providing an actual callback which | 316 // Note that we can also handle this by providing an actual callback which |
190 // takes the result code. Right now everything goes to the task that doesn't | 317 // takes the result code. Right now everything goes to the task that doesn't |
191 // result value. | 318 // result value. |
192 pp::CompletionCallback pp_callback(&CompletionCallbackClosureAdapter, | 319 pp::CompletionCallback pp_callback(&CompletionCallbackClosureAdapter, |
193 task.get(), | 320 task.get(), |
194 PP_COMPLETIONCALLBACK_FLAG_OPTIONAL); | 321 PP_COMPLETIONCALLBACK_FLAG_OPTIONAL); |
195 int error = graphics2d_.Flush(pp_callback); | 322 int error = graphics2d_.Flush(pp_callback); |
196 | 323 |
197 // There is already a flush in progress so set this flag to true so that we | |
198 // can flush again later. | |
199 // |paint_start| is then discarded but this is fine because we're not aiming | |
200 // for precise measurement of timing, otherwise we need to keep a list of | |
201 // queued start time(s). | |
202 if (error == PP_ERROR_INPROGRESS) | |
203 flush_blocked_ = true; | |
204 else | |
205 flush_blocked_ = false; | |
206 | |
207 // If Flush() returns asynchronously then release the task. | 324 // If Flush() returns asynchronously then release the task. |
208 if (error == PP_OK_COMPLETIONPENDING) | 325 flush_pending_ = (error == PP_OK_COMPLETIONPENDING); |
| 326 if (flush_pending_) { |
209 ignore_result(task.release()); | 327 ignore_result(task.release()); |
210 } | 328 } else { |
211 | 329 instance_->GetStats()->video_paint_ms()->Record( |
212 void PepperView::SetSolidFill(uint32 color) { | 330 (base::Time::Now() - start_time).InMilliseconds()); |
213 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | 331 |
214 | 332 ReturnBuffer(buffer); |
215 is_static_fill_ = true; | 333 |
216 static_fill_color_ = color; | 334 // Resume painting for the buffer that was previoulsy postponed because of |
217 | 335 // pending flush. |
218 Paint(); | 336 if (merge_buffer_ != NULL) { |
219 } | 337 buffer = merge_buffer_; |
220 | 338 merge_buffer_ = NULL; |
221 void PepperView::UnsetSolidFill() { | 339 FlushBuffer(merge_clip_area_, buffer, merge_region_); |
222 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | 340 } |
223 | 341 } |
224 is_static_fill_ = false; | 342 } |
225 } | 343 |
226 | 344 void PepperView::OnFlushDone(base::Time paint_start, |
227 void PepperView::SetConnectionState(protocol::ConnectionToHost::State state, | 345 pp::ImageData* buffer) { |
228 protocol::ConnectionToHost::Error error) { | 346 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); |
229 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | 347 DCHECK(flush_pending_); |
230 | 348 |
231 switch (state) { | |
232 case protocol::ConnectionToHost::CONNECTING: | |
233 SetSolidFill(kCreatedColor); | |
234 instance_->SetConnectionState( | |
235 ChromotingInstance::STATE_CONNECTING, | |
236 ConvertConnectionError(error)); | |
237 break; | |
238 | |
239 case protocol::ConnectionToHost::CONNECTED: | |
240 UnsetSolidFill(); | |
241 instance_->SetConnectionState( | |
242 ChromotingInstance::STATE_CONNECTED, | |
243 ConvertConnectionError(error)); | |
244 break; | |
245 | |
246 case protocol::ConnectionToHost::CLOSED: | |
247 SetSolidFill(kDisconnectedColor); | |
248 instance_->SetConnectionState( | |
249 ChromotingInstance::STATE_CLOSED, | |
250 ConvertConnectionError(error)); | |
251 break; | |
252 | |
253 case protocol::ConnectionToHost::FAILED: | |
254 SetSolidFill(kFailedColor); | |
255 instance_->SetConnectionState( | |
256 ChromotingInstance::STATE_FAILED, | |
257 ConvertConnectionError(error)); | |
258 break; | |
259 } | |
260 } | |
261 | |
262 bool PepperView::SetViewSize(const SkISize& view_size) { | |
263 if (view_size_ == view_size) | |
264 return false; | |
265 view_size_ = view_size; | |
266 | |
267 pp::Size pp_size = pp::Size(view_size.width(), view_size.height()); | |
268 | |
269 graphics2d_ = pp::Graphics2D(instance_, pp_size, true); | |
270 if (!instance_->BindGraphics(graphics2d_)) { | |
271 LOG(ERROR) << "Couldn't bind the device context."; | |
272 return false; | |
273 } | |
274 | |
275 if (view_size.isEmpty()) | |
276 return false; | |
277 | |
278 // Allocate the backing store to save the desktop image. | |
279 if ((backing_store_.get() == NULL) || | |
280 (backing_store_->size() != pp_size)) { | |
281 VLOG(1) << "Allocate backing store: " | |
282 << view_size.width() << " x " << view_size.height(); | |
283 backing_store_.reset( | |
284 new pp::ImageData(instance_, pp::ImageData::GetNativeImageDataFormat(), | |
285 pp_size, false)); | |
286 DCHECK(backing_store_.get() && !backing_store_->is_null()) | |
287 << "Not enough memory for backing store."; | |
288 } | |
289 return true; | |
290 } | |
291 | |
292 void PepperView::AllocateFrame(media::VideoFrame::Format format, | |
293 const SkISize& size, | |
294 scoped_refptr<media::VideoFrame>* frame_out, | |
295 const base::Closure& done) { | |
296 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | |
297 | |
298 *frame_out = media::VideoFrame::CreateFrame( | |
299 media::VideoFrame::RGB32, size.width(), size.height(), | |
300 base::TimeDelta(), base::TimeDelta()); | |
301 (*frame_out)->AddRef(); | |
302 done.Run(); | |
303 } | |
304 | |
305 void PepperView::ReleaseFrame(media::VideoFrame* frame) { | |
306 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | |
307 | |
308 if (frame) | |
309 frame->Release(); | |
310 } | |
311 | |
312 void PepperView::OnPartialFrameOutput(media::VideoFrame* frame, | |
313 SkRegion* region, | |
314 const base::Closure& done) { | |
315 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | |
316 | |
317 // TODO(ajwong): Clean up this API to be async so we don't need to use a | |
318 // member variable as a hack. | |
319 PaintFrame(frame, *region); | |
320 done.Run(); | |
321 } | |
322 | |
323 void PepperView::OnPaintDone(base::Time paint_start) { | |
324 DCHECK(context_->main_message_loop()->BelongsToCurrentThread()); | |
325 instance_->GetStats()->video_paint_ms()->Record( | 349 instance_->GetStats()->video_paint_ms()->Record( |
326 (base::Time::Now() - paint_start).InMilliseconds()); | 350 (base::Time::Now() - paint_start).InMilliseconds()); |
327 | 351 |
328 // If the last flush failed because there was already another one in progress | 352 flush_pending_ = false; |
329 // then we perform the flush now. | 353 ReturnBuffer(buffer); |
330 if (flush_blocked_) | 354 |
331 FlushGraphics(base::Time::Now()); | 355 // Resume painting for the buffer that was previoulsy postponed because of |
332 return; | 356 // pending flush. |
| 357 if (merge_buffer_ != NULL) { |
| 358 buffer = merge_buffer_; |
| 359 merge_buffer_ = NULL; |
| 360 FlushBuffer(merge_clip_area_, buffer, merge_region_); |
| 361 } |
333 } | 362 } |
334 | 363 |
335 } // namespace remoting | 364 } // namespace remoting |
OLD | NEW |