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