Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(757)

Side by Side Diff: remoting/host/video_frame_capturer_win.cc

Issue 10696134: remoting/host: Rename Capturer to VideoFrameCapturer. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "remoting/host/video_frame_capturer.h"
6
7 #include <windows.h>
8
9 #include "base/file_path.h"
10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/scoped_native_library.h"
13 #include "base/utf_string_conversions.h"
14 #include "base/win/scoped_gdi_object.h"
15 #include "remoting/base/capture_data.h"
16 #include "remoting/host/video_frame_capturer_helper.h"
Wez 2012/07/12 15:59:22 nit: Move below differ.h
17 #include "remoting/host/desktop_win.h"
18 #include "remoting/host/differ.h"
19 #include "remoting/host/scoped_thread_desktop_win.h"
20 #include "remoting/proto/control.pb.h"
21
22 namespace remoting {
23
24 namespace {
25
26 // Constants from dwmapi.h.
27 const UINT DWM_EC_DISABLECOMPOSITION = 0;
28 const UINT DWM_EC_ENABLECOMPOSITION = 1;
29
30 typedef HRESULT (WINAPI * DwmEnableCompositionFunc)(UINT);
31
32 const char kDwmapiLibraryName[] = "dwmapi";
33
34 // Pixel colors used when generating cursor outlines.
35 const uint32 kPixelBgraBlack = 0xff000000;
36 const uint32 kPixelBgraWhite = 0xffffffff;
37 const uint32 kPixelBgraTransparent = 0x00000000;
38
39 // VideoFrameCapturerWin captures 32bit RGB using GDI.
40 //
41 // VideoFrameCapturerWin is double-buffered as required by VideoFrameCapturer.
42 // See remoting/host/video_frame_capturer.h.
43 class VideoFrameCapturerWin : public VideoFrameCapturer {
44 public:
45 VideoFrameCapturerWin();
46 virtual ~VideoFrameCapturerWin();
47
48 // Overridden from VideoFrameCapturer:
49 virtual void Start(const CursorShapeChangedCallback& callback) OVERRIDE;
50 virtual void Stop() OVERRIDE;
51 virtual void ScreenConfigurationChanged() OVERRIDE;
52 virtual media::VideoFrame::Format pixel_format() const OVERRIDE;
53 virtual void ClearInvalidRegion() OVERRIDE;
54 virtual void InvalidateRegion(const SkRegion& invalid_region) OVERRIDE;
55 virtual void InvalidateScreen(const SkISize& size) OVERRIDE;
56 virtual void InvalidateFullScreen() OVERRIDE;
57 virtual void CaptureInvalidRegion(
58 const CaptureCompletedCallback& callback) OVERRIDE;
59 virtual const SkISize& size_most_recent() const OVERRIDE;
60
61 private:
62 struct VideoFrameBuffer {
63 VideoFrameBuffer(void* data, const SkISize& size, int bytes_per_pixel,
64 int bytes_per_row)
65 : data(data), size(size), bytes_per_pixel(bytes_per_pixel),
66 bytes_per_row(bytes_per_row) {
67 }
68 VideoFrameBuffer() {
69 data = 0;
70 size = SkISize::Make(0, 0);
71 bytes_per_pixel = 0;
72 bytes_per_row = 0;
73 }
74 void* data;
75 SkISize size;
76 int bytes_per_pixel;
77 int bytes_per_row;
78 };
79
80 // Make sure that the current buffer has the same size as the screen.
81 void UpdateBufferCapture(const SkISize& size);
82
83 // Allocate memory for a buffer of a given size, freeing any memory previously
84 // allocated for that buffer.
85 void ReallocateBuffer(int buffer_index, const SkISize& size);
86
87 void CalculateInvalidRegion();
88 void CaptureRegion(const SkRegion& region,
89 const CaptureCompletedCallback& callback);
90
91 void ReleaseBuffers();
92 // Generates an image in the current buffer.
93 void CaptureImage();
94
95 // Expand the cursor shape to add a white outline for visibility against
96 // dark backgrounds.
97 void AddCursorOutline(int width, int height, uint32* dst);
98
99 // Capture the current cursor shape.
100 void CaptureCursor();
101
102 // Gets the screen size.
103 SkISize GetScreenSize();
104
105 // A thread-safe list of invalid rectangles, and the size of the most
106 // recently captured screen.
107 VideoFrameCapturerHelper helper_;
108
109 // Callback notified whenever the cursor shape is changed.
110 CursorShapeChangedCallback cursor_shape_changed_callback_;
111
112 // Snapshot of the last cursor bitmap we sent to the client. This is used
113 // to diff against the current cursor so we only send a cursor-change
114 // message when the shape has changed.
115 scoped_array<uint8> last_cursor_;
116 SkISize last_cursor_size_;
117
118 // There are two buffers for the screen images, as required by Capturer.
119 static const int kNumBuffers = 2;
120 VideoFrameBuffer buffers_[kNumBuffers];
121
122 ScopedThreadDesktopWin desktop_;
123
124 // Gdi specific information about screen.
125 HWND desktop_window_;
126 HDC desktop_dc_;
127 HDC memory_dc_;
128 HBITMAP target_bitmap_[kNumBuffers];
129
130 // The screen size attached to the device contexts through which the screen
131 // is captured.
132 SkISize dc_size_;
133
134 // The current buffer with valid data for reading.
135 int current_buffer_;
136
137 // Format of pixels returned in buffer.
138 media::VideoFrame::Format pixel_format_;
139
140 // Class to calculate the difference between two screen bitmaps.
141 scoped_ptr<Differ> differ_;
142
143 base::ScopedNativeLibrary dwmapi_library_;
144 DwmEnableCompositionFunc composition_func_;
145
146 DISALLOW_COPY_AND_ASSIGN(VideoFrameCapturerWin);
147 };
148
149 // 3780 pixels per meter is equivalent to 96 DPI, typical on desktop monitors.
150 static const int kPixelsPerMeter = 3780;
151 // 32 bit RGBA is 4 bytes per pixel.
152 static const int kBytesPerPixel = 4;
153
154 VideoFrameCapturerWin::VideoFrameCapturerWin()
155 : last_cursor_size_(SkISize::Make(0, 0)),
156 desktop_window_(NULL),
157 desktop_dc_(NULL),
158 memory_dc_(NULL),
159 dc_size_(SkISize::Make(0, 0)),
160 current_buffer_(0),
161 pixel_format_(media::VideoFrame::RGB32),
162 composition_func_(NULL) {
163 memset(target_bitmap_, 0, sizeof(target_bitmap_));
164 memset(buffers_, 0, sizeof(buffers_));
165 ScreenConfigurationChanged();
166 }
167
168 VideoFrameCapturerWin::~VideoFrameCapturerWin() {
169 ReleaseBuffers();
170 }
171
172 media::VideoFrame::Format VideoFrameCapturerWin::pixel_format() const {
173 return pixel_format_;
174 }
175
176 void VideoFrameCapturerWin::ClearInvalidRegion() {
177 helper_.ClearInvalidRegion();
178 }
179
180 void VideoFrameCapturerWin::InvalidateRegion(const SkRegion& invalid_region) {
181 helper_.InvalidateRegion(invalid_region);
182 }
183
184 void VideoFrameCapturerWin::InvalidateScreen(const SkISize& size) {
185 helper_.InvalidateScreen(size);
186 }
187
188 void VideoFrameCapturerWin::InvalidateFullScreen() {
189 helper_.InvalidateFullScreen();
190 }
191
192 void VideoFrameCapturerWin::CaptureInvalidRegion(
193 const CaptureCompletedCallback& callback) {
194 // Force the system to power-up display hardware, if it has been suspended.
195 SetThreadExecutionState(ES_DISPLAY_REQUIRED);
196
197 // Perform the capture.
198 CalculateInvalidRegion();
199 SkRegion invalid_region;
200 helper_.SwapInvalidRegion(&invalid_region);
201 CaptureRegion(invalid_region, callback);
202
203 // Check for cursor shape update.
204 CaptureCursor();
205 }
206
207 const SkISize& VideoFrameCapturerWin::size_most_recent() const {
208 return helper_.size_most_recent();
209 }
210
211 void VideoFrameCapturerWin::ReleaseBuffers() {
212 for (int i = kNumBuffers - 1; i >= 0; i--) {
213 if (target_bitmap_[i]) {
214 DeleteObject(target_bitmap_[i]);
215 target_bitmap_[i] = NULL;
216 }
217 if (buffers_[i].data) {
218 DeleteObject(buffers_[i].data);
219 buffers_[i].data = NULL;
220 }
221 }
222
223 if (desktop_dc_) {
224 ReleaseDC(desktop_window_, desktop_dc_);
225 desktop_window_ = NULL;
226 desktop_dc_ = NULL;
227 }
228
229 if (memory_dc_) {
230 DeleteDC(memory_dc_);
231 memory_dc_ = NULL;
232 }
233 }
234
235 void VideoFrameCapturerWin::Start(
236 const CursorShapeChangedCallback& callback) {
237 cursor_shape_changed_callback_ = callback;
238
239 // Load dwmapi.dll dynamically since it is not available on XP.
240 if (!dwmapi_library_.is_valid()) {
241 FilePath path(base::GetNativeLibraryName(UTF8ToUTF16(kDwmapiLibraryName)));
242 dwmapi_library_.Reset(base::LoadNativeLibrary(path, NULL));
243 }
244
245 if (dwmapi_library_.is_valid() && composition_func_ == NULL) {
246 composition_func_ = static_cast<DwmEnableCompositionFunc>(
247 dwmapi_library_.GetFunctionPointer("DwmEnableComposition"));
248 }
249
250 // Vote to disable Aero composited desktop effects while capturing. Windows
251 // will restore Aero automatically if the process exits. This has no effect
252 // under Windows 8 or higher. See crbug.com/124018.
253 if (composition_func_ != NULL) {
254 (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
255 }
256 }
257
258 void VideoFrameCapturerWin::Stop() {
259 // Restore Aero.
260 if (composition_func_ != NULL) {
261 (*composition_func_)(DWM_EC_ENABLECOMPOSITION);
262 }
263 }
264
265 void VideoFrameCapturerWin::ScreenConfigurationChanged() {
266 // We poll for screen configuration changes, so ignore notifications.
267 }
268
269 void VideoFrameCapturerWin::UpdateBufferCapture(const SkISize& size) {
270 // Switch to the desktop receiving user input if different from the current
271 // one.
272 scoped_ptr<DesktopWin> input_desktop = DesktopWin::GetInputDesktop();
273 if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) {
274 // Release GDI resources otherwise SetThreadDesktop will fail.
275 if (desktop_dc_) {
276 ReleaseDC(desktop_window_, desktop_dc_);
277 desktop_window_ = NULL;
278 desktop_dc_ = NULL;
279 }
280
281 if (memory_dc_) {
282 DeleteDC(memory_dc_);
283 memory_dc_ = NULL;
284 }
285
286 ReleaseBuffers();
287
288 // If SetThreadDesktop() fails, the thread is still assigned a desktop.
289 // So we can continue capture screen bits, just from a diffented desktop.
290 desktop_.SetThreadDesktop(input_desktop.Pass());
291 }
292
293 // Make sure the DCs have the correct dimensions.
294 if (size != dc_size_) {
295 // TODO(simonmorris): screen dimensions changing isn't equivalent to needing
296 // a new DC, but it's good enough for now.
297 if (desktop_dc_) {
298 ReleaseDC(desktop_window_, desktop_dc_);
299 desktop_window_ = NULL;
300 desktop_dc_ = NULL;
301 }
302
303 if (memory_dc_) {
304 DeleteDC(memory_dc_);
305 memory_dc_ = NULL;
306 }
307 }
308
309 if (desktop_dc_ == NULL) {
310 DCHECK(desktop_window_ == NULL);
311 DCHECK(memory_dc_ == NULL);
312
313 desktop_window_ = GetDesktopWindow();
314 desktop_dc_ = GetDC(desktop_window_);
315 memory_dc_ = CreateCompatibleDC(desktop_dc_);
316 dc_size_ = size;
317 }
318
319 // Make sure the current bitmap has the correct dimensions.
320 if (buffers_[current_buffer_].data == NULL ||
321 size != buffers_[current_buffer_].size) {
322 ReallocateBuffer(current_buffer_, size);
323 InvalidateFullScreen();
324 }
325 }
326
327 void VideoFrameCapturerWin::ReallocateBuffer(int buffer_index,
328 const SkISize& size) {
329 // Delete any previously constructed bitmap.
330 if (target_bitmap_[buffer_index]) {
331 DeleteObject(target_bitmap_[buffer_index]);
332 target_bitmap_[buffer_index] = NULL;
333 }
334 if (buffers_[buffer_index].data) {
335 DeleteObject(buffers_[buffer_index].data);
336 buffers_[buffer_index].data = NULL;
337 }
338
339 // Create a bitmap to keep the desktop image.
340 int rounded_width = (size.width() + 3) & (~3);
341
342 // Dimensions of screen.
343 pixel_format_ = media::VideoFrame::RGB32;
344 int bytes_per_row = rounded_width * kBytesPerPixel;
345
346 // Create a device independent bitmap (DIB) that is the same size.
347 BITMAPINFO bmi;
348 memset(&bmi, 0, sizeof(bmi));
349 bmi.bmiHeader.biHeight = -size.height();
350 bmi.bmiHeader.biWidth = rounded_width;
351 bmi.bmiHeader.biPlanes = 1;
352 bmi.bmiHeader.biBitCount = kBytesPerPixel * 8;
353 bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
354 bmi.bmiHeader.biSizeImage = bytes_per_row * size.height();
355 bmi.bmiHeader.biXPelsPerMeter = kPixelsPerMeter;
356 bmi.bmiHeader.biYPelsPerMeter = kPixelsPerMeter;
357
358 // Create memory for the buffers.
359 target_bitmap_[buffer_index] =
360 CreateDIBSection(desktop_dc_, &bmi, DIB_RGB_COLORS,
361 static_cast<void**>(&buffers_[buffer_index].data),
362 NULL, 0);
363 buffers_[buffer_index].size = SkISize::Make(bmi.bmiHeader.biWidth,
364 std::abs(bmi.bmiHeader.biHeight));
365 buffers_[buffer_index].bytes_per_pixel = bmi.bmiHeader.biBitCount / 8;
366 buffers_[buffer_index].bytes_per_row =
367 bmi.bmiHeader.biSizeImage / std::abs(bmi.bmiHeader.biHeight);
368 }
369
370 void VideoFrameCapturerWin::CalculateInvalidRegion() {
371 CaptureImage();
372
373 const VideoFrameBuffer& current = buffers_[current_buffer_];
374
375 // Find the previous and current screens.
376 int prev_buffer_id = current_buffer_ - 1;
377 if (prev_buffer_id < 0) {
378 prev_buffer_id = kNumBuffers - 1;
379 }
380 const VideoFrameBuffer& prev = buffers_[prev_buffer_id];
381
382 // Maybe the previous and current screens can't be differenced.
383 if ((current.size != prev.size) ||
384 (current.bytes_per_pixel != prev.bytes_per_pixel) ||
385 (current.bytes_per_row != prev.bytes_per_row)) {
386 InvalidateScreen(current.size);
387 return;
388 }
389
390 // Make sure the differencer is set up correctly for these previous and
391 // current screens.
392 if (!differ_.get() ||
393 (differ_->width() != current.size.width()) ||
394 (differ_->height() != current.size.height()) ||
395 (differ_->bytes_per_pixel() != current.bytes_per_pixel) ||
396 (differ_->bytes_per_row() != current.bytes_per_row)) {
397 differ_.reset(new Differ(current.size.width(), current.size.height(),
398 current.bytes_per_pixel, current.bytes_per_row));
399 }
400
401 SkRegion region;
402 differ_->CalcDirtyRegion(prev.data, current.data, &region);
403
404 InvalidateRegion(region);
405 }
406
407 void VideoFrameCapturerWin::CaptureRegion(
408 const SkRegion& region,
409 const CaptureCompletedCallback& callback) {
410 const VideoFrameBuffer& buffer = buffers_[current_buffer_];
411 current_buffer_ = (current_buffer_ + 1) % kNumBuffers;
412
413 DataPlanes planes;
414 planes.data[0] = static_cast<uint8*>(buffer.data);
415 planes.strides[0] = buffer.bytes_per_row;
416
417 scoped_refptr<CaptureData> data(new CaptureData(planes,
418 buffer.size,
419 pixel_format_));
420 data->mutable_dirty_region() = region;
421
422 helper_.set_size_most_recent(data->size());
423
424 callback.Run(data);
425 }
426
427 void VideoFrameCapturerWin::CaptureImage() {
428 // Make sure the structures we use to capture the image have the correct size.
429 UpdateBufferCapture(GetScreenSize());
430
431 // Select the target bitmap into the memory dc.
432 SelectObject(memory_dc_, target_bitmap_[current_buffer_]);
433
434 // And then copy the rect from desktop to memory.
435 BitBlt(memory_dc_, 0, 0, buffers_[current_buffer_].size.width(),
436 buffers_[current_buffer_].size.height(), desktop_dc_, 0, 0,
437 SRCCOPY | CAPTUREBLT);
438 }
439
440 void VideoFrameCapturerWin::AddCursorOutline(int width,
441 int height,
442 uint32* dst) {
443 for (int y = 0; y < height; y++) {
444 for (int x = 0; x < width; x++) {
445 // If this is a transparent pixel (bgr == 0 and alpha = 0), check the
446 // neighbor pixels to see if this should be changed to an outline pixel.
447 if (*dst == kPixelBgraTransparent) {
448 // Change to white pixel if any neighbors (top, bottom, left, right)
449 // are black.
450 if ((y > 0 && dst[-width] == kPixelBgraBlack) ||
451 (y < height - 1 && dst[width] == kPixelBgraBlack) ||
452 (x > 0 && dst[-1] == kPixelBgraBlack) ||
453 (x < width - 1 && dst[1] == kPixelBgraBlack)) {
454 *dst = kPixelBgraWhite;
455 }
456 }
457 dst++;
458 }
459 }
460 }
461
462 void VideoFrameCapturerWin::CaptureCursor() {
463 CURSORINFO cursor_info;
464 cursor_info.cbSize = sizeof(CURSORINFO);
465 if (!GetCursorInfo(&cursor_info)) {
466 VLOG(3) << "Unable to get cursor info. Error = " << GetLastError();
467 return;
468 }
469
470 // Note that this does not need to be freed.
471 HCURSOR hcursor = cursor_info.hCursor;
472 ICONINFO iinfo;
473 if (!GetIconInfo(hcursor, &iinfo)) {
474 VLOG(3) << "Unable to get cursor icon info. Error = " << GetLastError();
475 return;
476 }
477 int hotspot_x = iinfo.xHotspot;
478 int hotspot_y = iinfo.yHotspot;
479
480 // Get the cursor bitmap.
481 base::win::ScopedBitmap hbitmap;
482 BITMAP bitmap;
483 bool color_bitmap;
484 if (iinfo.hbmColor) {
485 // Color cursor bitmap.
486 color_bitmap = true;
487 hbitmap.Set((HBITMAP)CopyImage(iinfo.hbmColor, IMAGE_BITMAP, 0, 0,
488 LR_CREATEDIBSECTION));
489 if (!hbitmap.Get()) {
490 VLOG(3) << "Unable to copy color cursor image. Error = "
491 << GetLastError();
492 return;
493 }
494
495 // Free the color and mask bitmaps since we only need our copy.
496 DeleteObject(iinfo.hbmColor);
497 DeleteObject(iinfo.hbmMask);
498 } else {
499 // Black and white (xor) cursor.
500 color_bitmap = false;
501 hbitmap.Set(iinfo.hbmMask);
502 }
503
504 if (!GetObject(hbitmap.Get(), sizeof(BITMAP), &bitmap)) {
505 VLOG(3) << "Unable to get cursor bitmap. Error = " << GetLastError();
506 return;
507 }
508
509 int width = bitmap.bmWidth;
510 int height = bitmap.bmHeight;
511 // For non-color cursors, the mask contains both an AND and an XOR mask and
512 // the height includes both. Thus, the width is correct, but we need to
513 // divide by 2 to get the correct mask height.
514 if (!color_bitmap) {
515 height /= 2;
516 }
517 int data_size = height * width * kBytesPerPixel;
518
519 scoped_ptr<protocol::CursorShapeInfo> cursor_proto(
520 new protocol::CursorShapeInfo());
521 cursor_proto->mutable_data()->resize(data_size);
522 uint8* cursor_dst_data = const_cast<uint8*>(reinterpret_cast<const uint8*>(
523 cursor_proto->mutable_data()->data()));
524
525 // Copy/convert cursor bitmap into format needed by chromotocol.
526 int row_bytes = bitmap.bmWidthBytes;
527 if (color_bitmap) {
528 if (bitmap.bmPlanes != 1 || bitmap.bmBitsPixel != 32) {
529 VLOG(3) << "Unsupported color cursor format. Error = " << GetLastError();
530 return;
531 }
532
533 // Cursor bitmap is stored upside-down on Windows. Flip the rows and store
534 // it in the proto.
535 uint8* cursor_src_data = reinterpret_cast<uint8*>(bitmap.bmBits);
536 uint8* src = cursor_src_data + ((height - 1) * row_bytes);
537 uint8* dst = cursor_dst_data;
538 for (int row = 0; row < height; row++) {
539 memcpy(dst, src, row_bytes);
540 dst += width * kBytesPerPixel;
541 src -= row_bytes;
542 }
543 } else {
544 if (bitmap.bmPlanes != 1 || bitmap.bmBitsPixel != 1) {
545 VLOG(3) << "Unsupported cursor mask format. Error = " << GetLastError();
546 return;
547 }
548
549 // x2 because there are 2 masks in the bitmap: AND and XOR.
550 int mask_bytes = height * row_bytes * 2;
551 scoped_array<uint8> mask(new uint8[mask_bytes]);
552 if (!GetBitmapBits(hbitmap.Get(), mask_bytes, mask.get())) {
553 VLOG(3) << "Unable to get cursor mask bits. Error = " << GetLastError();
554 return;
555 }
556 uint8* and_mask = mask.get();
557 uint8* xor_mask = mask.get() + height * row_bytes;
558 uint8* dst = cursor_dst_data;
559 bool add_outline = false;
560 for (int y = 0; y < height; y++) {
561 for (int x = 0; x < width; x++) {
562 int byte = y * row_bytes + x / 8;
563 int bit = 7 - x % 8;
564 int and = and_mask[byte] & (1 << bit);
565 int xor = xor_mask[byte] & (1 << bit);
566
567 // The two cursor masks combine as follows:
568 // AND XOR Windows Result Our result RGB Alpha
569 // 0 0 Black Black 00 ff
570 // 0 1 White White ff ff
571 // 1 0 Screen Transparent 00 00
572 // 1 1 Reverse-screen Black 00 ff
573 // Since we don't support XOR cursors, we replace the "Reverse Screen"
574 // with black. In this case, we also add an outline around the cursor
575 // so that it is visible against a dark background.
576 int rgb = (!and && xor) ? 0xff : 0x00;
577 int alpha = (and && !xor) ? 0x00 : 0xff;
578 *dst++ = rgb;
579 *dst++ = rgb;
580 *dst++ = rgb;
581 *dst++ = alpha;
582 if (and && xor) {
583 add_outline = true;
584 }
585 }
586 }
587 if (add_outline) {
588 AddCursorOutline(width, height,
589 reinterpret_cast<uint32*>(cursor_dst_data));
590 }
591 }
592
593 // Compare the current cursor with the last one we sent to the client. If
594 // they're the same, then don't bother sending the cursor again.
595 if (last_cursor_size_.equals(width, height) &&
596 memcmp(last_cursor_.get(), cursor_dst_data, data_size) == 0) {
597 return;
598 }
599
600 VLOG(3) << "Sending updated cursor: " << width << "x" << height;
601
602 cursor_proto->set_width(width);
603 cursor_proto->set_height(height);
604 cursor_proto->set_hotspot_x(hotspot_x);
605 cursor_proto->set_hotspot_y(hotspot_y);
606
607 // Record the last cursor image that we sent to the client.
608 last_cursor_.reset(new uint8[data_size]);
609 memcpy(last_cursor_.get(), cursor_dst_data, data_size);
610 last_cursor_size_ = SkISize::Make(width, height);
611
612 cursor_shape_changed_callback_.Run(cursor_proto.Pass());
613 }
614
615 SkISize VideoFrameCapturerWin::GetScreenSize() {
616 return SkISize::Make(GetSystemMetrics(SM_CXSCREEN),
617 GetSystemMetrics(SM_CYSCREEN));
618 }
619
620 } // namespace
621
622 // static
623 VideoFrameCapturer* VideoFrameCapturer::Create() {
624 return new VideoFrameCapturerWin();
625 }
626
627 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698