window_capturer_mac.mm revision eef29ec6cf4389e0ce1c8b0336387bf1afb4a076
1b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org/*
2b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org *
4b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org *  Use of this source code is governed by a BSD-style license
5b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org *  that can be found in the LICENSE file in the root of the source
6b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org *  tree. An additional intellectual property rights grant can be found
7b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org *  in the file PATENTS.  All contributing project authors may
8b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org *  be found in the AUTHORS file in the root of the source tree.
9b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org */
10b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
11b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org#include "webrtc/modules/desktop_capture/window_capturer.h"
12b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
1312dc1a38ca54a000e4fecfbc6d41138b895c9ca5pbos@webrtc.org#include <assert.h>
14eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org#include <ApplicationServices/ApplicationServices.h>
15eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org#include <CoreFoundation/CoreFoundation.h>
16b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
17b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org#include "webrtc/modules/desktop_capture/desktop_frame.h"
18eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org#include "webrtc/system_wrappers/interface/logging.h"
19b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
20b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.orgnamespace webrtc {
21b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
22b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.orgnamespace {
23b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
24eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.orgbool CFStringRefToUtf8(const CFStringRef string, std::string* str_utf8) {
25eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  assert(string);
26eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  assert(str_utf8);
27eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CFIndex length = CFStringGetLength(string);
28eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  size_t max_length_utf8 =
29eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
30eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  str_utf8->resize(max_length_utf8);
31eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CFIndex used_bytes;
32eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  int result = CFStringGetBytes(
33eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      string, CFRangeMake(0, length), kCFStringEncodingUTF8, 0, false,
34eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      reinterpret_cast<UInt8*>(&*str_utf8->begin()), max_length_utf8,
35eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      &used_bytes);
36eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  if (result != length) {
37eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    str_utf8->clear();
38eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    return false;
39eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  }
40eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  str_utf8->resize(used_bytes);
41eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  return true;
42eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org}
43eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org
44eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org// DesktopFrame that stores data in CFData.
45eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.orgclass CFDataDesktopFrame : public DesktopFrame {
46eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org public:
47eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  // Consumes |cf_data| reference.
48eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  //
49eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  // TODO(sergeyu): Here we const_cast<> the buffer used in CFDataRef. CFDataRef
50eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  // buffer is immutable, but DesktopFrame is always mutable. This shouldn't be
51eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  // a problem because frames generated by WindowCapturers are normally not
52eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  // mutated. To avoid this hack consider making DesktopFrame immutable and add
53eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  // MutableDesktopFrame.
54eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CFDataDesktopFrame(DesktopSize size, int stride, CFDataRef cf_data)
55eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      : DesktopFrame(size, stride,
56eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org                     const_cast<uint8_t*>(CFDataGetBytePtr(cf_data)), NULL),
57eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org        cf_data_(cf_data) {
58eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  }
59eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  virtual ~CFDataDesktopFrame() {
60eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    CFRelease(cf_data_);
61eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  }
62eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org
63eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org private:
64eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CFDataRef cf_data_;
65eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org};
66eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org
67b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.orgclass WindowCapturerMac : public WindowCapturer {
68b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org public:
69b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  WindowCapturerMac();
70b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  virtual ~WindowCapturerMac();
71b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
72b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  // WindowCapturer interface.
73b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  virtual bool GetWindowList(WindowList* windows) OVERRIDE;
74b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  virtual bool SelectWindow(WindowId id) OVERRIDE;
75b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
76b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  // DesktopCapturer interface.
77b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  virtual void Start(Callback* callback) OVERRIDE;
78b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  virtual void Capture(const DesktopRegion& region) OVERRIDE;
79b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
80b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org private:
81b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  Callback* callback_;
82eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CGWindowID window_id_;
83b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
84b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  DISALLOW_COPY_AND_ASSIGN(WindowCapturerMac);
85b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org};
86b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
87b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.orgWindowCapturerMac::WindowCapturerMac()
88eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    : callback_(NULL),
89eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      window_id_(0) {
90b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org}
91b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
92b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.orgWindowCapturerMac::~WindowCapturerMac() {
93b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org}
94b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
95b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.orgbool WindowCapturerMac::GetWindowList(WindowList* windows) {
96eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  // Only get on screen, non-desktop windows.
97eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CFArrayRef window_array = CGWindowListCopyWindowInfo(
98eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
99eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      kCGNullWindowID);
100eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  if (!window_array)
101eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    return false;
102eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org
103eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  // Check windows to make sure they have an id, title, and use window layer
104eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  // other than 0.
105eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CFIndex count = CFArrayGetCount(window_array);
106eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  for (CFIndex i = 0; i < count; ++i) {
107eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
108eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org        CFArrayGetValueAtIndex(window_array, i));
109eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    CFStringRef window_title = reinterpret_cast<CFStringRef>(
110eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org        CFDictionaryGetValue(window, kCGWindowName));
111eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    CFNumberRef window_id = reinterpret_cast<CFNumberRef>(
112eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org        CFDictionaryGetValue(window, kCGWindowNumber));
113eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    CFNumberRef window_layer = reinterpret_cast<CFNumberRef>(
114eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org        CFDictionaryGetValue(window, kCGWindowLayer));
115eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    if (window_title && window_id && window_layer) {
116eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      // Skip windows with layer=0 (menu, dock).
117eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      int layer;
118eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      CFNumberGetValue(window_layer, kCFNumberIntType, &layer);
119eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      if (layer != 0)
120eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org        continue;
121eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org
122eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      int id;
123eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      CFNumberGetValue(window_id, kCFNumberIntType, &id);
124eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      WindowCapturer::Window window;
125eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      window.id = id;
126eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      if (!CFStringRefToUtf8(window_title, &(window.title)) ||
127eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org          window.title.empty()) {
128eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org        continue;
129eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      }
130eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      windows->push_back(window);
131eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    }
132eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  }
133eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org
134eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CFRelease(window_array);
135eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  return true;
136b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org}
137b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
138b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.orgbool WindowCapturerMac::SelectWindow(WindowId id) {
139eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  // Request description for the specified window to make sure |id| is valid.
140eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CGWindowID ids[1];
141eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  ids[0] = id;
142eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CFArrayRef window_id_array =
143eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      CFArrayCreate(NULL, reinterpret_cast<const void **>(&ids), 1, NULL);
144eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CFArrayRef window_array =
145eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      CGWindowListCreateDescriptionFromArray(window_id_array);
146eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  int results_count = window_array ? CFArrayGetCount(window_array) : 0;
147eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CFRelease(window_id_array);
148eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CFRelease(window_array);
149eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org
150eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  if (results_count == 0) {
151eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    // Could not find the window. It might have been closed.
152eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    return false;
153eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  }
154eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org
155eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  window_id_ = id;
156eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  return true;
157b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org}
158b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
159b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.orgvoid WindowCapturerMac::Start(Callback* callback) {
160b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  assert(!callback_);
161b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  assert(callback);
162b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
163b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  callback_ = callback;
164b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org}
165b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
166b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.orgvoid WindowCapturerMac::Capture(const DesktopRegion& region) {
167eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CGImageRef window_image = CGWindowListCreateImage(
168eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      CGRectNull, kCGWindowListOptionIncludingWindow,
169eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      window_id_, kCGWindowImageBoundsIgnoreFraming);
170eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org
171eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  if (!window_image) {
172eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    CFRelease(window_image);
173eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    callback_->OnCaptureCompleted(NULL);
174eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    return;
175eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  }
176eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org
177eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  int bits_per_pixel = CGImageGetBitsPerPixel(window_image);
178eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  if (bits_per_pixel != 32) {
179eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    LOG(LS_ERROR) << "Unsupported window image depth: " << bits_per_pixel;
180eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    CFRelease(window_image);
181eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    callback_->OnCaptureCompleted(NULL);
182eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org    return;
183eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  }
184eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org
185eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  int width = CGImageGetWidth(window_image);
186eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  int height = CGImageGetHeight(window_image);
187eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CGDataProviderRef provider = CGImageGetDataProvider(window_image);
188eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  DesktopFrame* frame = new CFDataDesktopFrame(
189eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      DesktopSize(width, height), CGImageGetBytesPerRow(window_image),
190eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org      CGDataProviderCopyData(provider));
191eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  CFRelease(window_image);
192eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org
193eef29ec6cf4389e0ce1c8b0336387bf1afb4a076sergeyu@chromium.org  callback_->OnCaptureCompleted(frame);
194b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org}
195b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
196b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org}  // namespace
197b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
198b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org// static
199b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.orgWindowCapturer* WindowCapturer::Create() {
200b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org  return new WindowCapturerMac();
201b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org}
202b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org
203b10ccbec02db00fc17397afb72a115072590d391sergeyu@chromium.org}  // namespace webrtc
204