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

Side by Side Diff: webrtc/modules/desktop_capture/screen_capturer_mac.mm

Issue 1861893002: Fix screen capturers to initialize on the same thread on which Start() is called. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Created 4 years, 8 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
OLDNEW
1 /* 1 /*
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. 2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license 4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source 5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found 6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may 7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree. 8 * be found in the AUTHORS file in the root of the source tree.
9 */ 9 */
10 10
11 #include "webrtc/modules/desktop_capture/screen_capturer.h" 11 #include "webrtc/modules/desktop_capture/screen_capturer.h"
12 12
13 #include <stddef.h> 13 #include <stddef.h>
14 14
15 #include <memory> 15 #include <memory>
16 #include <set> 16 #include <set>
17 17
18 #include <ApplicationServices/ApplicationServices.h> 18 #include <ApplicationServices/ApplicationServices.h>
19 #include <Cocoa/Cocoa.h> 19 #include <Cocoa/Cocoa.h>
20 #include <dlfcn.h> 20 #include <dlfcn.h>
21 #include <IOKit/pwr_mgt/IOPMLib.h> 21 #include <IOKit/pwr_mgt/IOPMLib.h>
22 #include <OpenGL/CGLMacro.h> 22 #include <OpenGL/CGLMacro.h>
23 #include <OpenGL/OpenGL.h> 23 #include <OpenGL/OpenGL.h>
24 24
25 #include "webrtc/base/checks.h"
25 #include "webrtc/base/macutils.h" 26 #include "webrtc/base/macutils.h"
27 #include "webrtc/base/thread_checker.h"
26 #include "webrtc/modules/desktop_capture/desktop_capture_options.h" 28 #include "webrtc/modules/desktop_capture/desktop_capture_options.h"
27 #include "webrtc/modules/desktop_capture/desktop_frame.h" 29 #include "webrtc/modules/desktop_capture/desktop_frame.h"
28 #include "webrtc/modules/desktop_capture/desktop_geometry.h" 30 #include "webrtc/modules/desktop_capture/desktop_geometry.h"
29 #include "webrtc/modules/desktop_capture/desktop_region.h" 31 #include "webrtc/modules/desktop_capture/desktop_region.h"
30 #include "webrtc/modules/desktop_capture/mac/desktop_configuration.h" 32 #include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
31 #include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h" 33 #include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h"
32 #include "webrtc/modules/desktop_capture/mac/scoped_pixel_buffer_object.h" 34 #include "webrtc/modules/desktop_capture/mac/scoped_pixel_buffer_object.h"
33 #include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h" 35 #include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h"
34 #include "webrtc/modules/desktop_capture/screen_capturer_helper.h" 36 #include "webrtc/modules/desktop_capture/screen_capturer_helper.h"
35 #include "webrtc/system_wrappers/include/logging.h" 37 #include "webrtc/system_wrappers/include/logging.h"
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after
181 window_bounds, window_list, kCGWindowImageDefault); 183 window_bounds, window_list, kCGWindowImageDefault);
182 } 184 }
183 185
184 // A class to perform video frame capturing for mac. 186 // A class to perform video frame capturing for mac.
185 class ScreenCapturerMac : public ScreenCapturer { 187 class ScreenCapturerMac : public ScreenCapturer {
186 public: 188 public:
187 explicit ScreenCapturerMac( 189 explicit ScreenCapturerMac(
188 rtc::scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor); 190 rtc::scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor);
189 virtual ~ScreenCapturerMac(); 191 virtual ~ScreenCapturerMac();
190 192
191 bool Init();
192
193 // Overridden from ScreenCapturer: 193 // Overridden from ScreenCapturer:
194 void Start(Callback* callback) override; 194 void Start(Callback* callback) override;
195 void Capture(const DesktopRegion& region) override; 195 void Capture(const DesktopRegion& region) override;
196 void SetExcludedWindow(WindowId window) override; 196 void SetExcludedWindow(WindowId window) override;
197 bool GetScreenList(ScreenList* screens) override; 197 bool GetScreenList(ScreenList* screens) override;
198 bool SelectScreen(ScreenId id) override; 198 bool SelectScreen(ScreenId id) override;
199 199
200 private: 200 private:
201 bool Initialize();
202
201 void GlBlitFast(const DesktopFrame& frame, 203 void GlBlitFast(const DesktopFrame& frame,
202 const DesktopRegion& region); 204 const DesktopRegion& region);
203 void GlBlitSlow(const DesktopFrame& frame); 205 void GlBlitSlow(const DesktopFrame& frame);
204 void CgBlitPreLion(const DesktopFrame& frame, 206 void CgBlitPreLion(const DesktopFrame& frame,
205 const DesktopRegion& region); 207 const DesktopRegion& region);
206 // Returns false if the selected screen is no longer valid. 208 // Returns false if the selected screen is no longer valid.
207 bool CgBlitPostLion(const DesktopFrame& frame, 209 bool CgBlitPostLion(const DesktopFrame& frame,
208 const DesktopRegion& region); 210 const DesktopRegion& region);
209 211
210 // Called when the screen configuration is changed. 212 // Called when the screen configuration is changed.
(...skipping 10 matching lines...) Expand all
221 const CGRect *rect_array, 223 const CGRect *rect_array,
222 void *user_parameter); 224 void *user_parameter);
223 static void ScreenUpdateMoveCallback(CGScreenUpdateMoveDelta delta, 225 static void ScreenUpdateMoveCallback(CGScreenUpdateMoveDelta delta,
224 size_t count, 226 size_t count,
225 const CGRect *rect_array, 227 const CGRect *rect_array,
226 void *user_parameter); 228 void *user_parameter);
227 void ReleaseBuffers(); 229 void ReleaseBuffers();
228 230
229 DesktopFrame* CreateFrame(); 231 DesktopFrame* CreateFrame();
230 232
231 Callback* callback_; 233 rtc::ThreadChecker thread_checker_;
232 234
233 CGLContextObj cgl_context_; 235 Callback* callback_ = nullptr;
236
237 CGLContextObj cgl_context_ = nullptr;
234 ScopedPixelBufferObject pixel_buffer_object_; 238 ScopedPixelBufferObject pixel_buffer_object_;
235 239
236 // Queue of the frames buffers. 240 // Queue of the frames buffers.
237 ScreenCaptureFrameQueue queue_; 241 ScreenCaptureFrameQueue queue_;
238 242
239 // Current display configuration. 243 // Current display configuration.
240 MacDesktopConfiguration desktop_config_; 244 MacDesktopConfiguration desktop_config_;
241 245
242 // Currently selected display, or 0 if the full desktop is selected. On OS X 246 // Currently selected display, or 0 if the full desktop is selected. On OS X
243 // 10.6 and before, this is always 0. 247 // 10.6 and before, this is always 0.
244 CGDirectDisplayID current_display_; 248 CGDirectDisplayID current_display_ = 0;
245 249
246 // The physical pixel bounds of the current screen. 250 // The physical pixel bounds of the current screen.
247 DesktopRect screen_pixel_bounds_; 251 DesktopRect screen_pixel_bounds_;
248 252
249 // The dip to physical pixel scale of the current screen. 253 // The dip to physical pixel scale of the current screen.
250 float dip_to_pixel_scale_; 254 float dip_to_pixel_scale_ = 1.0f;
251 255
252 // A thread-safe list of invalid rectangles, and the size of the most 256 // A thread-safe list of invalid rectangles, and the size of the most
253 // recently captured screen. 257 // recently captured screen.
254 ScreenCapturerHelper helper_; 258 ScreenCapturerHelper helper_;
255 259
256 // Contains an invalid region from the previous capture. 260 // Contains an invalid region from the previous capture.
257 DesktopRegion last_invalid_region_; 261 DesktopRegion last_invalid_region_;
258 262
259 // Monitoring display reconfiguration. 263 // Monitoring display reconfiguration.
260 rtc::scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor_; 264 rtc::scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor_;
261 265
262 // Power management assertion to prevent the screen from sleeping. 266 // Power management assertion to prevent the screen from sleeping.
263 IOPMAssertionID power_assertion_id_display_; 267 IOPMAssertionID power_assertion_id_display_ = kIOPMNullAssertionID;
264 268
265 // Power management assertion to indicate that the user is active. 269 // Power management assertion to indicate that the user is active.
266 IOPMAssertionID power_assertion_id_user_; 270 IOPMAssertionID power_assertion_id_user_ = kIOPMNullAssertionID;
267 271
268 // Dynamically link to deprecated APIs for Mac OS X 10.6 support. 272 // Dynamically link to deprecated APIs for Mac OS X 10.6 support.
269 void* app_services_library_; 273 void* app_services_library_ = nullptr;
270 CGDisplayBaseAddressFunc cg_display_base_address_; 274 CGDisplayBaseAddressFunc cg_display_base_address_ = nullptr;
271 CGDisplayBytesPerRowFunc cg_display_bytes_per_row_; 275 CGDisplayBytesPerRowFunc cg_display_bytes_per_row_ = nullptr;
272 CGDisplayBitsPerPixelFunc cg_display_bits_per_pixel_; 276 CGDisplayBitsPerPixelFunc cg_display_bits_per_pixel_ = nullptr;
273 void* opengl_library_; 277 void* opengl_library_ = nullptr;
274 CGLSetFullScreenFunc cgl_set_full_screen_; 278 CGLSetFullScreenFunc cgl_set_full_screen_ = nullptr;
275 279
276 CGWindowID excluded_window_; 280 CGWindowID excluded_window_ = 0;
277 281
278 RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerMac); 282 RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerMac);
279 }; 283 };
280 284
281 // DesktopFrame wrapper that flips wrapped frame upside down by inverting 285 // DesktopFrame wrapper that flips wrapped frame upside down by inverting
282 // stride. 286 // stride.
283 class InvertedDesktopFrame : public DesktopFrame { 287 class InvertedDesktopFrame : public DesktopFrame {
284 public: 288 public:
285 // Takes ownership of |frame|. 289 // Takes ownership of |frame|.
286 InvertedDesktopFrame(DesktopFrame* frame) 290 InvertedDesktopFrame(DesktopFrame* frame)
287 : DesktopFrame( 291 : DesktopFrame(
288 frame->size(), -frame->stride(), 292 frame->size(), -frame->stride(),
289 frame->data() + (frame->size().height() - 1) * frame->stride(), 293 frame->data() + (frame->size().height() - 1) * frame->stride(),
290 frame->shared_memory()), 294 frame->shared_memory()),
291 original_frame_(frame) { 295 original_frame_(frame) {
292 set_dpi(frame->dpi()); 296 set_dpi(frame->dpi());
293 set_capture_time_ms(frame->capture_time_ms()); 297 set_capture_time_ms(frame->capture_time_ms());
294 mutable_updated_region()->Swap(frame->mutable_updated_region()); 298 mutable_updated_region()->Swap(frame->mutable_updated_region());
295 } 299 }
296 virtual ~InvertedDesktopFrame() {} 300 virtual ~InvertedDesktopFrame() {}
297 301
298 private: 302 private:
299 std::unique_ptr<DesktopFrame> original_frame_; 303 std::unique_ptr<DesktopFrame> original_frame_;
300 304
301 RTC_DISALLOW_COPY_AND_ASSIGN(InvertedDesktopFrame); 305 RTC_DISALLOW_COPY_AND_ASSIGN(InvertedDesktopFrame);
302 }; 306 };
303 307
304 ScreenCapturerMac::ScreenCapturerMac( 308 ScreenCapturerMac::ScreenCapturerMac(
305 rtc::scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor) 309 rtc::scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor)
306 : callback_(NULL), 310 : desktop_config_monitor_(desktop_config_monitor) {
307 cgl_context_(NULL), 311 // ScreenCapturer can be used on a thread different from the thread on which
308 current_display_(0), 312 // it's created.
309 dip_to_pixel_scale_(1.0f), 313 thread_checker_.DetachFromThread();
310 desktop_config_monitor_(desktop_config_monitor),
311 power_assertion_id_display_(kIOPMNullAssertionID),
312 power_assertion_id_user_(kIOPMNullAssertionID),
313 app_services_library_(NULL),
314 cg_display_base_address_(NULL),
315 cg_display_bytes_per_row_(NULL),
316 cg_display_bits_per_pixel_(NULL),
317 opengl_library_(NULL),
318 cgl_set_full_screen_(NULL),
319 excluded_window_(0) {
320 } 314 }
321 315
322 ScreenCapturerMac::~ScreenCapturerMac() { 316 ScreenCapturerMac::~ScreenCapturerMac() {
317 RTC_DCHECK(thread_checker_.CalledOnValidThread());
318
323 if (power_assertion_id_display_ != kIOPMNullAssertionID) { 319 if (power_assertion_id_display_ != kIOPMNullAssertionID) {
324 IOPMAssertionRelease(power_assertion_id_display_); 320 IOPMAssertionRelease(power_assertion_id_display_);
325 power_assertion_id_display_ = kIOPMNullAssertionID; 321 power_assertion_id_display_ = kIOPMNullAssertionID;
326 } 322 }
327 if (power_assertion_id_user_ != kIOPMNullAssertionID) { 323 if (power_assertion_id_user_ != kIOPMNullAssertionID) {
328 IOPMAssertionRelease(power_assertion_id_user_); 324 IOPMAssertionRelease(power_assertion_id_user_);
329 power_assertion_id_user_ = kIOPMNullAssertionID; 325 power_assertion_id_user_ = kIOPMNullAssertionID;
330 } 326 }
331 327
332 ReleaseBuffers(); 328 ReleaseBuffers();
333 UnregisterRefreshAndMoveHandlers(); 329 UnregisterRefreshAndMoveHandlers();
334 dlclose(app_services_library_); 330 dlclose(app_services_library_);
335 dlclose(opengl_library_); 331 dlclose(opengl_library_);
336 } 332 }
337 333
338 bool ScreenCapturerMac::Init() { 334 bool ScreenCapturerMac::Initialize() {
339 if (!RegisterRefreshAndMoveHandlers()) { 335 if (!RegisterRefreshAndMoveHandlers()) {
340 return false; 336 return false;
341 } 337 }
342 desktop_config_monitor_->Lock(); 338 desktop_config_monitor_->Lock();
343 desktop_config_ = desktop_config_monitor_->desktop_configuration(); 339 desktop_config_ = desktop_config_monitor_->desktop_configuration();
344 desktop_config_monitor_->Unlock(); 340 desktop_config_monitor_->Unlock();
345 ScreenConfigurationChanged(); 341 ScreenConfigurationChanged();
346 return true; 342 return true;
347 } 343 }
348 344
349 void ScreenCapturerMac::ReleaseBuffers() { 345 void ScreenCapturerMac::ReleaseBuffers() {
350 if (cgl_context_) { 346 if (cgl_context_) {
351 pixel_buffer_object_.Release(); 347 pixel_buffer_object_.Release();
352 CGLDestroyContext(cgl_context_); 348 CGLDestroyContext(cgl_context_);
353 cgl_context_ = NULL; 349 cgl_context_ = NULL;
354 } 350 }
355 // The buffers might be in use by the encoder, so don't delete them here. 351 // The buffers might be in use by the encoder, so don't delete them here.
356 // Instead, mark them as "needs update"; next time the buffers are used by 352 // Instead, mark them as "needs update"; next time the buffers are used by
357 // the capturer, they will be recreated if necessary. 353 // the capturer, they will be recreated if necessary.
358 queue_.Reset(); 354 queue_.Reset();
359 } 355 }
360 356
361 void ScreenCapturerMac::Start(Callback* callback) { 357 void ScreenCapturerMac::Start(Callback* callback) {
362 assert(!callback_); 358 RTC_DCHECK(thread_checker_.CalledOnValidThread());
363 assert(callback); 359 RTC_DCHECK(!callback_);
360 RTC_DCHECK(callback);
364 361
365 callback_ = callback; 362 callback_ = callback;
366 363
364 if (!Initialize()) {
365 callback_->OnInitializationFailed();
366 return;
367 }
368
367 // Create power management assertions to wake the display and prevent it from 369 // Create power management assertions to wake the display and prevent it from
368 // going to sleep on user idle. 370 // going to sleep on user idle.
369 // TODO(jamiewalch): Use IOPMAssertionDeclareUserActivity on 10.7.3 and above 371 // TODO(jamiewalch): Use IOPMAssertionDeclareUserActivity on 10.7.3 and above
370 // instead of the following two assertions. 372 // instead of the following two assertions.
371 IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, 373 IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
372 kIOPMAssertionLevelOn, 374 kIOPMAssertionLevelOn,
373 CFSTR("Chrome Remote Desktop connection active"), 375 CFSTR("Chrome Remote Desktop connection active"),
374 &power_assertion_id_display_); 376 &power_assertion_id_display_);
375 // This assertion ensures that the display is woken up if it already asleep 377 // This assertion ensures that the display is woken up if it already asleep
376 // (as used by Apple Remote Desktop). 378 // (as used by Apple Remote Desktop).
377 IOPMAssertionCreateWithName(CFSTR("UserIsActive"), 379 IOPMAssertionCreateWithName(CFSTR("UserIsActive"),
378 kIOPMAssertionLevelOn, 380 kIOPMAssertionLevelOn,
379 CFSTR("Chrome Remote Desktop connection active"), 381 CFSTR("Chrome Remote Desktop connection active"),
380 &power_assertion_id_user_); 382 &power_assertion_id_user_);
381 } 383 }
382 384
383 void ScreenCapturerMac::Capture(const DesktopRegion& region_to_capture) { 385 void ScreenCapturerMac::Capture(const DesktopRegion& region_to_capture) {
386 RTC_DCHECK(thread_checker_.CalledOnValidThread());
387
384 TickTime capture_start_time = TickTime::Now(); 388 TickTime capture_start_time = TickTime::Now();
385 389
386 queue_.MoveToNextFrame(); 390 queue_.MoveToNextFrame();
387 391
388 desktop_config_monitor_->Lock(); 392 desktop_config_monitor_->Lock();
389 MacDesktopConfiguration new_config = 393 MacDesktopConfiguration new_config =
390 desktop_config_monitor_->desktop_configuration(); 394 desktop_config_monitor_->desktop_configuration();
391 if (!desktop_config_.Equals(new_config)) { 395 if (!desktop_config_.Equals(new_config)) {
392 desktop_config_ = new_config; 396 desktop_config_ = new_config;
393 // If the display configuraiton has changed then refresh capturer data 397 // If the display configuraiton has changed then refresh capturer data
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
442 // Signal that we are done capturing data from the display framebuffer, 446 // Signal that we are done capturing data from the display framebuffer,
443 // and accessing display structures. 447 // and accessing display structures.
444 desktop_config_monitor_->Unlock(); 448 desktop_config_monitor_->Unlock();
445 449
446 new_frame->set_capture_time_ms( 450 new_frame->set_capture_time_ms(
447 (TickTime::Now() - capture_start_time).Milliseconds()); 451 (TickTime::Now() - capture_start_time).Milliseconds());
448 callback_->OnCaptureCompleted(new_frame); 452 callback_->OnCaptureCompleted(new_frame);
449 } 453 }
450 454
451 void ScreenCapturerMac::SetExcludedWindow(WindowId window) { 455 void ScreenCapturerMac::SetExcludedWindow(WindowId window) {
456 RTC_DCHECK(thread_checker_.CalledOnValidThread());
452 excluded_window_ = window; 457 excluded_window_ = window;
453 } 458 }
454 459
455 bool ScreenCapturerMac::GetScreenList(ScreenList* screens) { 460 bool ScreenCapturerMac::GetScreenList(ScreenList* screens) {
456 assert(screens->size() == 0); 461 RTC_DCHECK(thread_checker_.CalledOnValidThread());
462 RTC_DCHECK(screens->size() == 0);
463
457 if (rtc::GetOSVersionName() < rtc::kMacOSLion) { 464 if (rtc::GetOSVersionName() < rtc::kMacOSLion) {
458 // Single monitor cast is not supported on pre OS X 10.7. 465 // Single monitor cast is not supported on pre OS X 10.7.
459 Screen screen; 466 Screen screen;
460 screen.id = kFullDesktopScreenId; 467 screen.id = kFullDesktopScreenId;
461 screens->push_back(screen); 468 screens->push_back(screen);
462 return true; 469 return true;
463 } 470 }
464 471
465 for (MacDisplayConfigurations::iterator it = desktop_config_.displays.begin(); 472 for (MacDisplayConfigurations::iterator it = desktop_config_.displays.begin();
466 it != desktop_config_.displays.end(); ++it) { 473 it != desktop_config_.displays.end(); ++it) {
467 Screen screen; 474 Screen screen;
468 screen.id = static_cast<ScreenId>(it->id); 475 screen.id = static_cast<ScreenId>(it->id);
469 screens->push_back(screen); 476 screens->push_back(screen);
470 } 477 }
471 return true; 478 return true;
472 } 479 }
473 480
474 bool ScreenCapturerMac::SelectScreen(ScreenId id) { 481 bool ScreenCapturerMac::SelectScreen(ScreenId id) {
482 RTC_DCHECK(thread_checker_.CalledOnValidThread());
483
475 if (rtc::GetOSVersionName() < rtc::kMacOSLion) { 484 if (rtc::GetOSVersionName() < rtc::kMacOSLion) {
476 // Ignore the screen selection on unsupported OS. 485 // Ignore the screen selection on unsupported OS.
477 assert(!current_display_); 486 assert(!current_display_);
478 return id == kFullDesktopScreenId; 487 return id == kFullDesktopScreenId;
479 } 488 }
480 489
481 if (id == kFullDesktopScreenId) { 490 if (id == kFullDesktopScreenId) {
482 current_display_ = 0; 491 current_display_ = 0;
483 } else { 492 } else {
484 const MacDisplayConfiguration* config = 493 const MacDisplayConfiguration* config =
(...skipping 488 matching lines...) Expand 10 before | Expand all | Expand 10 after
973 return frame.release(); 982 return frame.release();
974 } 983 }
975 984
976 } // namespace 985 } // namespace
977 986
978 // static 987 // static
979 ScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) { 988 ScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) {
980 if (!options.configuration_monitor()) 989 if (!options.configuration_monitor())
981 return NULL; 990 return NULL;
982 991
983 std::unique_ptr<ScreenCapturerMac> capturer( 992 return new ScreenCapturerMac(options.configuration_monitor());
984 new ScreenCapturerMac(options.configuration_monitor()));
985 if (!capturer->Init())
986 capturer.reset();
987 return capturer.release();
988 } 993 }
989 994
990 } // namespace webrtc 995 } // namespace webrtc
OLDNEW
« no previous file with comments | « webrtc/modules/desktop_capture/desktop_capturer.h ('k') | webrtc/modules/desktop_capture/screen_capturer_x11.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698